test_conv3d_op.py 32.0 KB
Newer Older
1
#   Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserved.
D
dzhwinter 已提交
2
#
D
dzhwinter 已提交
3 4 5
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
D
dzhwinter 已提交
6
#
D
dzhwinter 已提交
7
#     http://www.apache.org/licenses/LICENSE-2.0
D
dzhwinter 已提交
8
#
D
dzhwinter 已提交
9 10 11 12 13 14
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

C
chengduoZH 已提交
15
import unittest
16

17
import numpy as np
18
from op_test import OpTest
19

H
hong 已提交
20
import paddle
21 22
import paddle.fluid as fluid
import paddle.fluid.core as core
C
chengduoZH 已提交
23 24


25 26 27 28 29 30 31 32
def conv3d_forward_naive(
    input,
    filter,
    group,
    conv_param,
    padding_algorithm='EXPLICIT',
    data_format="NCDHW",
):
L
liym27 已提交
33 34

    if padding_algorithm not in ["SAME", "VALID", "EXPLICIT"]:
35 36 37 38
        raise ValueError(
            "Unknown Attr(padding_algorithm): '%s'. "
            "It can only be 'SAME' or 'VALID'." % str(padding_algorithm)
        )
L
liym27 已提交
39 40

    if data_format not in ["NCDHW", "NDHWC"]:
41 42 43 44
        raise ValueError(
            "Unknown Attr(data_format): '%s' ."
            "It can only be 'NCDHW' or 'NDHWC'." % str(data_format)
        )
L
liym27 已提交
45

46
    channel_last = data_format == "NDHWC"
L
liym27 已提交
47 48 49
    if channel_last:
        input = np.transpose(input, [0, 4, 1, 2, 3])

50
    in_n, in_c, in_d, in_h, in_w = input.shape
L
liym27 已提交
51 52 53 54

    f_n, f_c, f_d, f_h, f_w = filter.shape
    out_n = in_n
    out_c = f_n
55 56
    assert f_c * group == in_c
    assert np.mod(out_c, group) == 0
M
minqiyang 已提交
57
    sub_out_c = out_c // group
L
liym27 已提交
58
    sub_f_n = f_n // group
59

60 61 62 63 64
    stride, pad, dilation = (
        conv_param['stride'],
        conv_param['pad'],
        conv_param['dilations'],
    )
C
chengduoZH 已提交
65

L
liym27 已提交
66 67 68
    # update pad and dilation
    def _get_padding_with_SAME(input_shape, pool_size, pool_stride):
        padding = []
69 70 71
        for input_size, filter_size, stride_size in zip(
            input_shape, pool_size, pool_stride
        ):
L
liym27 已提交
72
            out_size = int((input_size + stride_size - 1) / stride_size)
73
            pad_sum = np.max(
74 75
                ((out_size - 1) * stride_size + filter_size - input_size, 0)
            )
L
liym27 已提交
76 77 78 79 80 81 82 83 84 85 86
            pad_0 = int(pad_sum / 2)
            pad_1 = int(pad_sum - pad_0)
            padding.append(pad_0)
            padding.append(pad_1)
        return padding

    ksize = filter.shape[2:5]
    if padding_algorithm == "VALID":
        pad = [0, 0, 0, 0, 0, 0]
    elif padding_algorithm == "SAME":
        dilation = [1, 1, 1]
87
        input_data_shape = input.shape[2:5]
L
liym27 已提交
88 89 90 91 92 93 94 95 96 97
        pad = _get_padding_with_SAME(input_data_shape, ksize, stride)

    pad_d_0, pad_d_1 = pad[0], pad[0]
    pad_h_0, pad_h_1 = pad[1], pad[1]
    pad_w_0, pad_w_1 = pad[2], pad[2]
    if len(pad) == 6:
        pad_d_0, pad_d_1 = pad[0], pad[1]
        pad_h_0, pad_h_1 = pad[2], pad[3]
        pad_w_0, pad_w_1 = pad[4], pad[5]

98 99 100 101 102 103 104 105 106 107 108 109 110 111 112
    out_d = (
        1
        + (in_d + pad_d_0 + pad_d_1 - (dilation[0] * (f_d - 1) + 1))
        // stride[0]
    )
    out_h = (
        1
        + (in_h + pad_h_0 + pad_h_1 - (dilation[1] * (f_h - 1) + 1))
        // stride[1]
    )
    out_w = (
        1
        + (in_w + pad_w_0 + pad_w_1 - (dilation[2] * (f_w - 1) + 1))
        // stride[2]
    )
C
chengduoZH 已提交
113

114 115
    out = np.zeros((in_n, out_c, out_d, out_h, out_w))

116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131
    d_bolck_d = dilation[0] * (f_d - 1) + 1
    d_bolck_h = dilation[1] * (f_h - 1) + 1
    d_bolck_w = dilation[2] * (f_w - 1) + 1

    input_pad = np.pad(
        input,
        (
            (0, 0),
            (0, 0),
            (pad_d_0, pad_d_1),
            (pad_h_0, pad_h_1),
            (pad_w_0, pad_w_1),
        ),
        mode='constant',
        constant_values=0,
    )
C
chengduoZH 已提交
132

L
liym27 已提交
133
    filter_dilation = np.zeros((f_n, f_c, d_bolck_d, d_bolck_h, d_bolck_w))
134 135 136 137 138 139 140
    filter_dilation[
        :,
        :,
        0 : d_bolck_d : dilation[0],
        0 : d_bolck_h : dilation[1],
        0 : d_bolck_w : dilation[2],
    ] = filter
C
chengduoZH 已提交
141

142 143 144 145
    for d in range(out_d):
        for i in range(out_h):
            for j in range(out_w):
                for g in range(group):
146 147 148 149 150 151 152 153 154 155 156
                    input_pad_masked = input_pad[
                        :,
                        g * f_c : (g + 1) * f_c,
                        d * stride[0] : d * stride[0] + d_bolck_d,
                        i * stride[1] : i * stride[1] + d_bolck_h,
                        j * stride[2] : j * stride[2] + d_bolck_w,
                    ]

                    f_sub = filter_dilation[
                        g * sub_f_n : (g + 1) * sub_f_n, :, :, :, :
                    ]
157
                    for k in range(sub_out_c):
158 159 160 161
                        out[:, g * sub_out_c + k, d, i, j] = np.sum(
                            input_pad_masked * f_sub[k, :, :, :, :],
                            axis=(1, 2, 3, 4),
                        )
L
liym27 已提交
162 163
    if channel_last:
        out = np.transpose(out, [0, 2, 3, 4, 1])
164 165 166
    return out


L
liym27 已提交
167
def create_test_cudnn_class(parent):
168 169 170
    @unittest.skipIf(
        not core.is_compiled_with_cuda(), "core is not compiled with CUDA"
    )
L
liym27 已提交
171 172 173
    class TestCUDNNCase(parent):
        def init_kernel_type(self):
            self.use_cudnn = True
174 175 176
            self.dtype = (
                np.float32 if core.is_compiled_with_rocm() else np.float64
            )
L
liym27 已提交
177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205

    cls_name = "{0}_{1}".format(parent.__name__, "CUDNN")
    TestCUDNNCase.__name__ = cls_name
    globals()[cls_name] = TestCUDNNCase


def create_test_padding_SAME_class(parent):
    class TestPaddingSMAECase(parent):
        def init_paddings(self):
            self.pad = [0, 0, 0]
            self.padding_algorithm = "SAME"

    cls_name = "{0}_{1}".format(parent.__name__, "PaddingSAMEOp")
    TestPaddingSMAECase.__name__ = cls_name
    globals()[cls_name] = TestPaddingSMAECase


def create_test_padding_VALID_class(parent):
    class TestPaddingVALIDCase(parent):
        def init_paddings(self):
            self.pad = [1, 1, 1]
            self.padding_algorithm = "VALID"

    cls_name = "{0}_{1}".format(parent.__name__, "PaddingVALIDOp")
    TestPaddingVALIDCase.__name__ = cls_name
    globals()[cls_name] = TestPaddingVALIDCase


def create_test_cudnn_padding_SAME_class(parent):
206 207 208
    @unittest.skipIf(
        not core.is_compiled_with_cuda(), "core is not compiled with CUDA"
    )
L
liym27 已提交
209 210 211
    class TestCUDNNPaddingSMAECase(parent):
        def init_kernel_type(self):
            self.use_cudnn = True
212 213 214
            self.dtype = (
                np.float32 if core.is_compiled_with_rocm() else np.float64
            )
L
liym27 已提交
215 216 217 218 219 220 221 222 223 224 225

        def init_paddings(self):
            self.pad = [1, 1, 1]
            self.padding_algorithm = "SAME"

    cls_name = "{0}_{1}".format(parent.__name__, "CudnnPaddingSAMEOp")
    TestCUDNNPaddingSMAECase.__name__ = cls_name
    globals()[cls_name] = TestCUDNNPaddingSMAECase


def create_test_cudnn_padding_VALID_class(parent):
226 227 228
    @unittest.skipIf(
        not core.is_compiled_with_cuda(), "core is not compiled with CUDA"
    )
L
liym27 已提交
229 230 231
    class TestCUDNNPaddingVALIDCase(parent):
        def init_kernel_type(self):
            self.use_cudnn = True
232 233 234
            self.dtype = (
                np.float32 if core.is_compiled_with_rocm() else np.float64
            )
L
liym27 已提交
235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259

        def init_paddings(self):
            self.pad = [1, 1, 1]
            self.padding_algorithm = "VALID"

    cls_name = "{0}_{1}".format(parent.__name__, "CudnnPaddingVALIDOp")
    TestCUDNNPaddingVALIDCase.__name__ = cls_name
    globals()[cls_name] = TestCUDNNPaddingVALIDCase


def create_test_channel_last_class(parent):
    class TestChannelLastCase(parent):
        def init_data_format(self):
            self.data_format = "NDHWC"

        def init_test_case_2(self):
            N, C, D, H, W = self.input_size
            self.input_size = [N, D, H, W, C]

    cls_name = "{0}_{1}".format(parent.__name__, "ChannelLast")
    TestChannelLastCase.__name__ = cls_name
    globals()[cls_name] = TestChannelLastCase


def create_test_cudnn_channel_last_class(parent):
260 261 262
    @unittest.skipIf(
        not core.is_compiled_with_cuda(), "core is not compiled with CUDA"
    )
L
liym27 已提交
263 264 265
    class TestCudnnChannelLastCase(parent):
        def init_kernel_type(self):
            self.use_cudnn = True
266 267 268
            self.dtype = (
                np.float32 if core.is_compiled_with_rocm() else np.float64
            )
L
liym27 已提交
269 270 271 272 273 274 275 276 277 278 279 280 281

        def init_data_format(self):
            self.data_format = "NDHWC"

        def init_test_case_2(self):
            N, C, D, H, W = self.input_size
            self.input_size = [N, D, H, W, C]

    cls_name = "{0}_{1}".format(parent.__name__, "CudnnChannelLast")
    TestCudnnChannelLastCase.__name__ = cls_name
    globals()[cls_name] = TestCudnnChannelLastCase


C
cnn 已提交
282
class TestConv3DOp(OpTest):
C
chengduoZH 已提交
283
    def setUp(self):
K
Kexin Zhao 已提交
284
        self.op_type = "conv3d"
285
        self.use_cudnn = False
286 287
        self.use_mkldnn = False
        self.data_format = "AnyLayout"
288
        self.dtype = np.float64
K
Kexin Zhao 已提交
289
        self.init_kernel_type()
290
        self.init_group()
C
chengduoZH 已提交
291
        self.init_dilation()
292 293
        self.init_test_case()

C
chengduoZH 已提交
294 295 296
        conv3d_param = {
            'stride': self.stride,
            'pad': self.pad,
297
            'dilations': self.dilations,
C
chengduoZH 已提交
298
        }
K
Kexin Zhao 已提交
299 300 301

        input = np.random.random(self.input_size).astype(self.dtype)
        filter = np.random.random(self.filter_size).astype(self.dtype)
L
liym27 已提交
302 303 304 305
        output = conv3d_forward_naive(
            input,
            filter,
            self.groups,
306 307
            conv3d_param,
        ).astype(self.dtype)
C
chengduoZH 已提交
308

K
Kexin Zhao 已提交
309 310
        self.inputs = {
            'Input': OpTest.np_dtype_to_fluid_dtype(input),
311
            'Filter': OpTest.np_dtype_to_fluid_dtype(filter),
K
Kexin Zhao 已提交
312
        }
C
chengduoZH 已提交
313
        self.attrs = {
314 315
            'strides': self.stride,
            'paddings': self.pad,
C
chengduoZH 已提交
316
            'groups': self.groups,
K
Kexin Zhao 已提交
317
            'dilations': self.dilations,
318 319
            'use_cudnn': self.use_cudnn,
            'use_mkldnn': self.use_mkldnn,
320
            'data_format': self.data_format,
C
chengduoZH 已提交
321 322 323
        }
        self.outputs = {'Output': output}

324
    def has_cudnn(self):
325 326
        return core.is_compiled_with_cuda() and self.use_cudnn

C
chengduoZH 已提交
327
    def test_check_output(self):
328
        # TODO(wangzhongpu): support mkldnn op in dygraph mode
329
        place = core.CUDAPlace(0) if self.has_cudnn() else core.CPUPlace()
330
        self.check_output_with_place(
331
            place, atol=1e-5, check_dygraph=(not self.use_mkldnn)
332
        )
C
chengduoZH 已提交
333 334

    def test_check_grad(self):
K
Kexin Zhao 已提交
335 336
        if self.dtype == np.float16:
            return
337
        place = core.CUDAPlace(0) if self.has_cudnn() else core.CPUPlace()
338
        # TODO(wangzhongpu): support mkldnn op in dygraph mode
339 340 341 342 343
        self.check_grad_with_place(
            place,
            {'Input', 'Filter'},
            'Output',
            max_relative_error=0.03,
344
            check_dygraph=(not self.use_mkldnn),
345
        )
C
chengduoZH 已提交
346

C
chengduoZH 已提交
347
    def test_check_grad_no_filter(self):
K
Kexin Zhao 已提交
348 349
        if self.dtype == np.float16:
            return
350
        place = core.CUDAPlace(0) if self.has_cudnn() else core.CPUPlace()
351
        # TODO(wangzhongpu): support mkldnn op in dygraph mode
352 353 354 355 356 357
        self.check_grad_with_place(
            place,
            ['Input'],
            'Output',
            max_relative_error=0.03,
            no_grad_set=set(['Filter']),
358
            check_dygraph=(not self.use_mkldnn),
359
        )
C
chengduoZH 已提交
360 361

    def test_check_grad_no_input(self):
K
Kexin Zhao 已提交
362 363
        if self.dtype == np.float16:
            return
364
        place = core.CUDAPlace(0) if self.has_cudnn() else core.CPUPlace()
365
        # TODO(wangzhongpu): support mkldnn op in dygraph mode
366 367 368 369 370 371
        self.check_grad_with_place(
            place,
            ['Filter'],
            'Output',
            max_relative_error=0.03,
            no_grad_set=set(['Input']),
372
            check_dygraph=(not self.use_mkldnn),
373
        )
C
chengduoZH 已提交
374

375 376 377
    def init_test_case(self):
        self.pad = [0, 0, 0]
        self.stride = [1, 1, 1]
C
chengduoZH 已提交
378
        self.input_size = [2, 3, 4, 4, 4]  # NCDHW
379
        assert np.mod(self.input_size[1], self.groups) == 0
M
minqiyang 已提交
380
        f_c = self.input_size[1] // self.groups
381 382
        self.filter_size = [6, f_c, 3, 3, 3]

L
liym27 已提交
383 384 385
    def init_test_case_2(self):
        pass

C
chengduoZH 已提交
386 387 388
    def init_dilation(self):
        self.dilations = [1, 1, 1]

389
    def init_group(self):
C
chengduoZH 已提交
390 391
        self.groups = 1

K
Kexin Zhao 已提交
392 393
    def init_kernel_type(self):
        pass
394

C
chengduoZH 已提交
395

C
cnn 已提交
396
class TestCase1(TestConv3DOp):
C
chengduoZH 已提交
397 398 399
    def init_test_case(self):
        self.pad = [1, 1, 1]
        self.stride = [1, 1, 1]
C
chengduoZH 已提交
400
        self.input_size = [2, 3, 4, 4, 4]  # NCDHW
C
chengduoZH 已提交
401
        assert np.mod(self.input_size[1], self.groups) == 0
M
minqiyang 已提交
402
        f_c = self.input_size[1] // self.groups
C
chengduoZH 已提交
403 404 405
        self.filter_size = [6, f_c, 3, 3, 3]


C
cnn 已提交
406
class TestWithGroup1(TestConv3DOp):
C
chengduoZH 已提交
407 408
    def init_group(self):
        self.groups = 3
C
chengduoZH 已提交
409 410


C
chengduoZH 已提交
411
class TestWithGroup2(TestCase1):
412
    def init_group(self):
C
chengduoZH 已提交
413 414
        self.groups = 3

415

C
cnn 已提交
416
class TestWith1x1(TestConv3DOp):
C
chengduoZH 已提交
417 418 419
    def init_test_case(self):
        self.pad = [0, 0, 0]
        self.stride = [1, 1, 1]
L
liym27 已提交
420
        self.input_size = [2, 3, 4, 4, 4]
C
chengduoZH 已提交
421
        assert np.mod(self.input_size[1], self.groups) == 0
M
minqiyang 已提交
422
        f_c = self.input_size[1] // self.groups
Z
zhupengyang 已提交
423
        self.filter_size = [120, f_c, 1, 1, 1]
C
chengduoZH 已提交
424 425 426

    def init_dilation(self):
        self.dilations = [1, 1, 1]
C
chengduoZH 已提交
427

C
chengduoZH 已提交
428 429 430
    def init_group(self):
        self.groups = 3

C
chengduoZH 已提交
431

C
cnn 已提交
432
class TestWithInput1x1Filter1x1(TestConv3DOp):
433 434 435
    def init_test_case(self):
        self.pad = [0, 0, 0]
        self.stride = [1, 1, 1]
Z
zhupengyang 已提交
436
        self.input_size = [40, 3, 1, 1, 1]
437
        assert np.mod(self.input_size[1], self.groups) == 0
M
minqiyang 已提交
438
        f_c = self.input_size[1] // self.groups
Z
zhupengyang 已提交
439
        self.filter_size = [120, f_c, 1, 1, 1]
440 441 442 443 444 445 446 447

    def init_dilation(self):
        self.dilations = [1, 1, 1]

    def init_group(self):
        self.groups = 3


C
cnn 已提交
448
class TestWithDilation(TestConv3DOp):
C
chengduoZH 已提交
449 450 451
    def init_test_case(self):
        self.pad = [0, 0, 0]
        self.stride = [1, 1, 1]
L
liym27 已提交
452
        self.input_size = [2, 3, 6, 6, 6]
C
chengduoZH 已提交
453
        assert np.mod(self.input_size[1], self.groups) == 0
M
minqiyang 已提交
454
        f_c = self.input_size[1] // self.groups
Z
zhupengyang 已提交
455
        self.filter_size = [24, f_c, 2, 2, 2]
C
chengduoZH 已提交
456 457 458 459 460 461

    def init_dilation(self):
        self.dilations = [2, 2, 2]

    def init_group(self):
        self.groups = 3
C
chengduoZH 已提交
462

C
chengduoZH 已提交
463

464
# ---------------- Conv3DCUDNN ----------------
L
liym27 已提交
465 466


467 468 469
@unittest.skipIf(
    not core.is_compiled_with_cuda(), "core is not compiled with CUDA"
)
C
cnn 已提交
470
class TestCUDNN(TestConv3DOp):
K
Kexin Zhao 已提交
471
    def init_kernel_type(self):
472
        self.use_cudnn = True
473
        self.dtype = np.float32 if core.is_compiled_with_rocm() else np.float64
K
Kexin Zhao 已提交
474 475


476 477 478
@unittest.skipIf(
    not core.is_compiled_with_cuda(), "core is not compiled with CUDA"
)
C
cnn 已提交
479
class TestFP16CUDNN(TestConv3DOp):
K
Kexin Zhao 已提交
480 481 482 483 484 485 486 487 488
    def init_kernel_type(self):
        self.use_cudnn = True
        self.dtype = np.float16

    def test_check_output(self):
        if core.is_compiled_with_cuda():
            place = core.CUDAPlace(0)
            if core.is_float16_supported(place):
                self.check_output_with_place(place, atol=2e-2)
武毅 已提交
489 490


491 492 493
@unittest.skipIf(
    not core.is_compiled_with_cuda(), "core is not compiled with CUDA"
)
494
class TestWithGroup1CUDNN(TestWithGroup1):
K
Kexin Zhao 已提交
495
    def init_kernel_type(self):
496
        self.use_cudnn = True
497
        self.dtype = np.float32 if core.is_compiled_with_rocm() else np.float64
K
Kexin Zhao 已提交
498 499


500 501 502
@unittest.skipIf(
    not core.is_compiled_with_cuda(), "core is not compiled with CUDA"
)
K
Kexin Zhao 已提交
503 504 505 506 507 508 509 510 511 512
class TestFP16WithGroup1CUDNN(TestWithGroup1):
    def init_kernel_type(self):
        self.use_cudnn = True
        self.dtype = np.float16

    def test_check_output(self):
        if core.is_compiled_with_cuda():
            place = core.CUDAPlace(0)
            if core.is_float16_supported(place):
                self.check_output_with_place(place, atol=2e-2)
武毅 已提交
513 514


515 516 517
@unittest.skipIf(
    not core.is_compiled_with_cuda(), "core is not compiled with CUDA"
)
518
class TestWithGroup2CUDNN(TestWithGroup2):
K
Kexin Zhao 已提交
519
    def init_kernel_type(self):
520
        self.use_cudnn = True
521
        self.dtype = np.float32 if core.is_compiled_with_rocm() else np.float64
K
Kexin Zhao 已提交
522 523


524 525 526
@unittest.skipIf(
    not core.is_compiled_with_cuda(), "core is not compiled with CUDA"
)
K
Kexin Zhao 已提交
527 528 529 530 531 532 533 534 535 536
class TestFP16WithGroup2CUDNN(TestWithGroup2):
    def init_kernel_type(self):
        self.use_cudnn = True
        self.dtype = np.float16

    def test_check_output(self):
        if core.is_compiled_with_cuda():
            place = core.CUDAPlace(0)
            if core.is_float16_supported(place):
                self.check_output_with_place(place, atol=2e-2)
武毅 已提交
537 538


539 540 541
@unittest.skipIf(
    not core.is_compiled_with_cuda(), "core is not compiled with CUDA"
)
542
class TestWith1x1CUDNN(TestWith1x1):
K
Kexin Zhao 已提交
543
    def init_kernel_type(self):
544
        self.use_cudnn = True
545
        self.dtype = np.float32 if core.is_compiled_with_rocm() else np.float64
K
Kexin Zhao 已提交
546 547


548 549 550
@unittest.skipIf(
    not core.is_compiled_with_cuda(), "core is not compiled with CUDA"
)
K
Kexin Zhao 已提交
551 552 553 554 555 556 557 558 559 560
class TestFP16With1x1CUDNN(TestWith1x1):
    def init_kernel_type(self):
        self.use_cudnn = True
        self.dtype = np.float16

    def test_check_output(self):
        if core.is_compiled_with_cuda():
            place = core.CUDAPlace(0)
            if core.is_float16_supported(place):
                self.check_output_with_place(place, atol=2e-2)
武毅 已提交
561 562


563 564 565
@unittest.skipIf(
    not core.is_compiled_with_cuda(), "core is not compiled with CUDA"
)
566
class TestWithInput1x1Filter1x1CUDNN(TestWithInput1x1Filter1x1):
K
Kexin Zhao 已提交
567
    def init_kernel_type(self):
568
        self.use_cudnn = True
569
        self.dtype = np.float32 if core.is_compiled_with_rocm() else np.float64
K
Kexin Zhao 已提交
570 571


572 573 574
@unittest.skipIf(
    not core.is_compiled_with_cuda(), "core is not compiled with CUDA"
)
K
Kexin Zhao 已提交
575 576 577 578 579 580 581 582 583 584
class TestFP16WithInput1x1Filter1x1CUDNN(TestWithInput1x1Filter1x1):
    def init_kernel_type(self):
        self.use_cudnn = True
        self.dtype = np.float16

    def test_check_output(self):
        if core.is_compiled_with_cuda():
            place = core.CUDAPlace(0)
            if core.is_float16_supported(place):
                self.check_output_with_place(place, atol=2e-2)
585 586


587 588 589 590
class TestCUDNNExhaustiveSearch(TestCUDNN):
    def init_kernel_type(self):
        self.use_cudnn = True
        self.exhaustive_search = True
591
        self.dtype = np.float32 if core.is_compiled_with_rocm() else np.float64
592 593


L
liym27 已提交
594 595 596
# ---- test asymmetric padding ----


C
cnn 已提交
597
class TestConv3DOp_2(OpTest):
L
liym27 已提交
598 599 600 601 602
    def setUp(self):
        self.op_type = "conv3d"
        self.use_cudnn = False
        self.use_mkldnn = False
        self.data_format = "NCDHW"
603
        self.dtype = np.float64
L
liym27 已提交
604 605 606 607 608 609 610 611 612 613 614 615
        self.init_kernel_type()
        self.init_group()
        self.init_dilation()
        self.init_data_format()
        self.init_test_case()
        self.init_paddings()

        self.init_test_case_2()

        conv3d_param = {
            'stride': self.stride,
            'pad': self.pad,
616
            'dilations': self.dilations,
L
liym27 已提交
617 618 619 620
        }

        input = np.random.random(self.input_size).astype(self.dtype)
        filter = np.random.random(self.filter_size).astype(self.dtype)
621 622 623 624 625 626 627 628
        output = conv3d_forward_naive(
            input,
            filter,
            self.groups,
            conv3d_param,
            self.padding_algorithm,
            self.data_format,
        ).astype(self.dtype)
L
liym27 已提交
629 630 631

        self.inputs = {
            'Input': OpTest.np_dtype_to_fluid_dtype(input),
632
            'Filter': OpTest.np_dtype_to_fluid_dtype(filter),
L
liym27 已提交
633 634 635 636 637 638 639 640 641
        }
        self.attrs = {
            'strides': self.stride,
            'paddings': self.pad,
            'padding_algorithm': self.padding_algorithm,
            'groups': self.groups,
            'dilations': self.dilations,
            'use_cudnn': self.use_cudnn,
            'use_mkldnn': self.use_mkldnn,
642
            'data_format': self.data_format,
L
liym27 已提交
643 644 645 646 647 648 649 650 651 652 653 654 655 656
        }
        self.outputs = {'Output': output}

    def has_cudnn(self):
        return core.is_compiled_with_cuda() and self.use_cudnn

    def test_check_output(self):
        place = core.CUDAPlace(0) if self.has_cudnn() else core.CPUPlace()
        self.check_output_with_place(place, atol=1e-5)

    def test_check_grad(self):
        if self.dtype == np.float16:
            return
        place = core.CUDAPlace(0) if self.has_cudnn() else core.CPUPlace()
657 658 659
        self.check_grad_with_place(
            place, {'Input', 'Filter'}, 'Output', max_relative_error=0.03
        )
L
liym27 已提交
660 661 662 663 664

    def test_check_grad_no_filter(self):
        if self.dtype == np.float16:
            return
        place = core.CUDAPlace(0) if self.has_cudnn() else core.CPUPlace()
665 666 667 668 669 670 671
        self.check_grad_with_place(
            place,
            ['Input'],
            'Output',
            max_relative_error=0.03,
            no_grad_set=set(['Filter']),
        )
L
liym27 已提交
672 673 674 675 676

    def test_check_grad_no_input(self):
        if self.dtype == np.float16:
            return
        place = core.CUDAPlace(0) if self.has_cudnn() else core.CPUPlace()
677 678 679 680 681 682 683
        self.check_grad_with_place(
            place,
            ['Filter'],
            'Output',
            max_relative_error=0.03,
            no_grad_set=set(['Input']),
        )
L
liym27 已提交
684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711

    def init_test_case(self):
        self.stride = [1, 1, 1]
        self.input_size = [2, 3, 4, 4, 4]  # NCDHW
        assert np.mod(self.input_size[1], self.groups) == 0
        f_c = self.input_size[1] // self.groups
        self.filter_size = [6, f_c, 3, 3, 3]

    def init_test_case_2(self):
        pass

    def init_dilation(self):
        self.dilations = [1, 1, 1]

    def init_group(self):
        self.groups = 1

    def init_kernel_type(self):
        pass

    def init_paddings(self):
        self.pad = [0, 0, 0]
        self.padding_algorithm = "EXPLICIT"

    def init_data_format(self):
        self.data_format = "NCDHW"


C
cnn 已提交
712
class TestConv3DOp_AsyPadding(TestConv3DOp_2):
713 714 715 716 717 718 719
    def init_test_case(self):
        self.stride = [1, 1, 2]
        self.input_size = [2, 3, 4, 4, 4]  # NCDHW
        assert np.mod(self.input_size[1], self.groups) == 0
        f_c = self.input_size[1] // self.groups
        self.filter_size = [6, f_c, 3, 3, 3]

L
liym27 已提交
720 721 722 723 724
    def init_paddings(self):
        self.pad = [1, 0, 1, 0, 0, 2]
        self.padding_algorithm = "EXPLICIT"


C
cnn 已提交
725
class TestConv3DOp_DiffDataInDiffDim(TestConv3DOp_2):
726 727 728 729 730 731 732 733 734 735 736 737
    def init_test_case(self):
        self.stride = [1, 1, 2]
        self.input_size = [2, 3, 4, 5, 5]  # NCDHW
        assert np.mod(self.input_size[1], self.groups) == 0
        f_c = self.input_size[1] // self.groups
        self.filter_size = [6, f_c, 3, 4, 3]

    def init_paddings(self):
        self.pad = [1, 0, 1, 0, 0, 2]
        self.padding_algorithm = "EXPLICIT"


C
cnn 已提交
738 739 740
create_test_padding_SAME_class(TestConv3DOp_DiffDataInDiffDim)
create_test_padding_VALID_class(TestConv3DOp_DiffDataInDiffDim)
create_test_channel_last_class(TestConv3DOp_DiffDataInDiffDim)
741 742


C
cnn 已提交
743
class TestCase1_AsyPadding(TestConv3DOp_2):
L
liym27 已提交
744 745 746 747 748 749 750 751 752 753 754 755
    def init_test_case(self):
        self.stride = [1, 1, 1]
        self.input_size = [2, 3, 4, 4, 4]  # NCDHW
        assert np.mod(self.input_size[1], self.groups) == 0
        f_c = self.input_size[1] // self.groups
        self.filter_size = [6, f_c, 3, 3, 3]

    def init_paddings(self):
        self.pad = [0, 0, 1, 0, 0, 2]
        self.padding_algorithm = "EXPLICIT"


C
cnn 已提交
756
class TestWithGroup1_AsyPadding(TestConv3DOp_2):
L
liym27 已提交
757 758 759 760 761 762 763 764
    def init_group(self):
        self.groups = 3

    def init_paddings(self):
        self.pad = [1, 1, 1, 0, 0, 2]
        self.padding_algorithm = "EXPLICIT"


C
cnn 已提交
765
class TestWithGroup2_AsyPadding(TestConv3DOp_2):
L
liym27 已提交
766 767 768 769 770 771 772 773 774 775 776 777 778 779 780
    def init_test_case(self):
        self.stride = [1, 1, 1]
        self.input_size = [2, 3, 4, 4, 4]  # NCDHW
        assert np.mod(self.input_size[1], self.groups) == 0
        f_c = self.input_size[1] // self.groups
        self.filter_size = [6, f_c, 3, 3, 3]

    def init_group(self):
        self.groups = 3

    def init_paddings(self):
        self.pad = [1, 1, 0, 1, 0, 2]
        self.padding_algorithm = "EXPLICIT"


C
cnn 已提交
781
class TestWith1x1_AsyPadding(TestConv3DOp_2):
L
liym27 已提交
782 783 784 785 786
    def init_test_case(self):
        self.stride = [1, 1, 1]
        self.input_size = [2, 3, 4, 4, 4]
        assert np.mod(self.input_size[1], self.groups) == 0
        f_c = self.input_size[1] // self.groups
Z
zhupengyang 已提交
787
        self.filter_size = [120, f_c, 1, 1, 1]
L
liym27 已提交
788 789 790 791 792 793 794 795 796 797 798 799

    def init_dilation(self):
        self.dilations = [1, 1, 1]

    def init_group(self):
        self.groups = 3

    def init_paddings(self):
        self.pad = [0, 0, 1, 0, 0, 2]
        self.padding_algorithm = "EXPLICIT"


C
cnn 已提交
800
class TestWithDilation_AsyPadding(TestConv3DOp_2):
L
liym27 已提交
801 802 803 804 805
    def init_test_case(self):
        self.stride = [1, 1, 1]
        self.input_size = [2, 3, 6, 6, 6]
        assert np.mod(self.input_size[1], self.groups) == 0
        f_c = self.input_size[1] // self.groups
Z
zhupengyang 已提交
806
        self.filter_size = [24, f_c, 2, 2, 2]
L
liym27 已提交
807 808 809 810 811 812 813 814 815 816 817 818

    def init_dilation(self):
        self.dilations = [2, 2, 2]

    def init_group(self):
        self.groups = 3

    def init_paddings(self):
        self.pad = [0, 0, 1, 0, 1, 0]
        self.padding_algorithm = "EXPLICIT"


C
cnn 已提交
819
create_test_cudnn_class(TestConv3DOp_AsyPadding)
L
liym27 已提交
820 821 822 823 824
create_test_cudnn_class(TestWithGroup1_AsyPadding)
create_test_cudnn_class(TestWithGroup2_AsyPadding)
create_test_cudnn_class(TestWith1x1_AsyPadding)
create_test_cudnn_class(TestWithDilation_AsyPadding)

C
cnn 已提交
825
create_test_padding_SAME_class(TestConv3DOp_AsyPadding)
L
liym27 已提交
826 827 828
create_test_padding_SAME_class(TestWithGroup1_AsyPadding)
create_test_padding_SAME_class(TestWith1x1_AsyPadding)

C
cnn 已提交
829
create_test_padding_VALID_class(TestConv3DOp_AsyPadding)
L
liym27 已提交
830 831 832
create_test_padding_VALID_class(TestWithGroup1_AsyPadding)
create_test_padding_VALID_class(TestWith1x1_AsyPadding)

C
cnn 已提交
833
create_test_cudnn_padding_SAME_class(TestConv3DOp_AsyPadding)
L
liym27 已提交
834 835 836
create_test_cudnn_padding_SAME_class(TestWithGroup1_AsyPadding)
create_test_cudnn_padding_SAME_class(TestWith1x1_AsyPadding)

C
cnn 已提交
837
create_test_cudnn_padding_VALID_class(TestConv3DOp_AsyPadding)
L
liym27 已提交
838 839 840
create_test_cudnn_padding_VALID_class(TestWithGroup1_AsyPadding)
create_test_cudnn_padding_VALID_class(TestWith1x1_AsyPadding)

C
cnn 已提交
841
create_test_channel_last_class(TestConv3DOp_AsyPadding)
L
liym27 已提交
842 843 844
create_test_channel_last_class(TestWithGroup1_AsyPadding)
create_test_channel_last_class(TestWith1x1_AsyPadding)

C
cnn 已提交
845
create_test_channel_last_class(TestConv3DOp_AsyPadding)
L
liym27 已提交
846 847 848
create_test_channel_last_class(TestWithGroup1_AsyPadding)
create_test_channel_last_class(TestWith1x1_AsyPadding)

C
cnn 已提交
849
create_test_cudnn_channel_last_class(TestConv3DOp_AsyPadding)
L
liym27 已提交
850 851 852
create_test_cudnn_channel_last_class(TestWithGroup1_AsyPadding)
create_test_cudnn_channel_last_class(TestWith1x1_AsyPadding)

C
cnn 已提交
853
create_test_cudnn_channel_last_class(TestConv3DOp_AsyPadding)
L
liym27 已提交
854 855 856
create_test_cudnn_channel_last_class(TestWithGroup1_AsyPadding)
create_test_cudnn_channel_last_class(TestWith1x1_AsyPadding)

武毅 已提交
857 858
# FIXME(typhoonzero): find a way to determine if
# using cudnn > 6 in python
859
# class TestWithDilationCUDNN(TestWithDilation):
武毅 已提交
860
#     def init_op_type(self):
861
#         self.op_type = "conv3d"
武毅 已提交
862

L
liym27 已提交
863 864

# --------- test python API ---------------
C
cnn 已提交
865
class TestConv3DAPI(unittest.TestCase):
L
liym27 已提交
866 867
    def test_api(self):

868 869 870 871 872 873 874 875 876 877 878 879 880 881
        input_NDHWC = fluid.layers.data(
            name="input_NDHWC",
            shape=[2, 5, 5, 5, 3],
            append_batch_size=False,
            dtype="float32",
        )

        input_NCDHW = fluid.layers.data(
            name="input_NCDHW",
            shape=[2, 3, 5, 5, 3],
            append_batch_size=False,
            dtype="float32",
        )

882
        paddle.static.nn.conv3d(
883 884 885 886 887 888 889 890 891 892
            input=input_NDHWC,
            num_filters=3,
            filter_size=[3, 3, 3],
            stride=[1, 1, 1],
            padding=0,
            dilation=[1, 1, 1],
            groups=1,
            data_format="NCDHW",
        )

893
        paddle.static.nn.conv3d(
894 895 896 897 898 899 900 901 902 903
            input=input_NCDHW,
            num_filters=3,
            filter_size=[3, 3, 3],
            stride=[1, 1, 1],
            padding=[1, 2, 1, 0, 1, 0],
            dilation=[1, 1, 1],
            groups=1,
            data_format="NCDHW",
        )

904
        paddle.static.nn.conv3d(
905 906 907 908 909 910 911 912 913 914
            input=input_NCDHW,
            num_filters=3,
            filter_size=[3, 3, 3],
            stride=[1, 1, 1],
            padding=[[0, 0], [0, 0], [1, 1], [1, 1], [1, 1]],
            dilation=[1, 1, 1],
            groups=1,
            data_format="NCDHW",
        )

915
        paddle.static.nn.conv3d(
916 917 918 919 920 921 922 923 924 925
            input=input_NDHWC,
            num_filters=3,
            filter_size=[3, 3, 3],
            stride=[1, 1, 1],
            padding=[[0, 0], [1, 1], [1, 1], [1, 1], [0, 0]],
            dilation=[1, 1, 1],
            groups=1,
            data_format="NDHWC",
        )

926
        paddle.static.nn.conv3d(
927 928 929 930 931 932 933 934 935 936
            input=input_NCDHW,
            num_filters=3,
            filter_size=[3, 3, 3],
            stride=[1, 1, 1],
            padding="SAME",
            dilation=[1, 1, 1],
            groups=1,
            data_format="NCDHW",
        )

937
        paddle.static.nn.conv3d(
938 939 940 941 942 943 944 945 946
            input=input_NCDHW,
            num_filters=3,
            filter_size=[3, 3, 3],
            stride=[1, 1, 1],
            padding="VALID",
            dilation=[1, 1, 1],
            groups=1,
            data_format="NCDHW",
        )
L
liym27 已提交
947 948


C
cnn 已提交
949
class TestConv3DAPI_Error(unittest.TestCase):
L
liym27 已提交
950
    def test_api(self):
951 952 953 954 955 956
        input = fluid.layers.data(
            name="input",
            shape=[2, 5, 5, 5, 4],
            append_batch_size=False,
            dtype="float32",
        )
L
liym27 已提交
957 958 959

        # ValueError: cudnn
        def run_1():
960
            paddle.static.nn.conv3d(
961 962 963 964 965 966 967 968 969 970
                input=input,
                num_filters=3,
                filter_size=3,
                stride=1,
                padding=0,
                dilation=1,
                groups=1,
                use_cudnn=[0],
                data_format="NCDHW",
            )
L
liym27 已提交
971 972 973 974 975

        self.assertRaises(ValueError, run_1)

        # ValueError: data_format
        def run_2():
976
            paddle.static.nn.conv3d(
977 978 979 980 981 982 983 984 985 986
                input=input,
                num_filters=3,
                filter_size=[3, 3, 3],
                stride=[1, 1, 1],
                padding=0,
                dilation=[1, 1, 1],
                groups=1,
                use_cudnn=False,
                data_format="NCHWC",
            )
L
liym27 已提交
987 988 989 990 991

        self.assertRaises(ValueError, run_2)

        # ValueError: padding
        def run_3():
992
            paddle.static.nn.conv3d(
993 994 995 996 997 998 999 1000 1001 1002
                input=input,
                num_filters=3,
                filter_size=3,
                stride=1,
                padding="SAMEE",
                dilation=1,
                groups=1,
                use_cudnn=False,
                data_format="NCDHW",
            )
L
liym27 已提交
1003 1004 1005 1006

        self.assertRaises(ValueError, run_3)

        def run_4():
1007
            paddle.static.nn.conv3d(
1008 1009 1010 1011 1012 1013 1014 1015 1016 1017
                input=input,
                num_filters=3,
                filter_size=3,
                stride=1,
                padding=[[0, 1], [0, 0], [0, 1], [0, 1], [0, 1]],
                dilation=1,
                groups=1,
                use_cudnn=False,
                data_format="NCDHW",
            )
L
liym27 已提交
1018 1019 1020 1021

        self.assertRaises(ValueError, run_4)

        def run_5():
1022
            paddle.static.nn.conv3d(
1023 1024 1025 1026 1027 1028 1029 1030 1031 1032
                input=input,
                num_filters=3,
                filter_size=0,
                stride=0,
                padding=[[0, 1], [0, 1], [0, 1], [0, 1], [0, 1]],
                dilation=1,
                groups=1,
                use_cudnn=False,
                data_format="NDHWC",
            )
L
liym27 已提交
1033 1034 1035 1036

        self.assertRaises(ValueError, run_5)

        # ValueError: channel dimmention
1037 1038 1039 1040 1041 1042
        x = fluid.layers.data(
            name="x",
            shape=[2, 5, 5, 5, -1],
            append_batch_size=False,
            dtype="float32",
        )
L
liym27 已提交
1043 1044

        def run_6():
1045
            paddle.static.nn.conv3d(
1046 1047 1048 1049 1050 1051 1052 1053 1054 1055
                input=x,
                num_filters=3,
                filter_size=3,
                stride=1,
                padding=0,
                dilation=1,
                groups=1,
                use_cudnn=False,
                data_format="NDHWC",
            )
L
liym27 已提交
1056 1057 1058 1059 1060

        self.assertRaises(ValueError, run_6)

        # ValueError: groups
        def run_7():
1061
            paddle.static.nn.conv3d(
1062 1063 1064 1065 1066 1067 1068 1069 1070 1071
                input=input,
                num_filters=3,
                filter_size=3,
                stride=1,
                padding=0,
                dilation=1,
                groups=3,
                use_cudnn=False,
                data_format="NDHWC",
            )
L
liym27 已提交
1072 1073 1074

        self.assertRaises(ValueError, run_7)

1075 1076
        # ValueError: filter num
        def run_8():
1077
            paddle.static.nn.conv3d(
1078 1079 1080 1081 1082 1083 1084 1085 1086 1087
                input=input,
                num_filters=0,
                filter_size=0,
                stride=0,
                padding=0,
                dilation=0,
                groups=1,
                use_cudnn=False,
                data_format="NDHWC",
            )
1088 1089 1090

        self.assertRaises(ValueError, run_8)

L
liym27 已提交
1091

C
chengduoZH 已提交
1092
if __name__ == '__main__':
H
hong 已提交
1093
    paddle.enable_static()
C
chengduoZH 已提交
1094
    unittest.main()