test_conv3d_transpose_op.py 17.1 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 16
import unittest
import numpy as np
17

K
Kaipeng Deng 已提交
18
import paddle
19

K
Kaipeng Deng 已提交
20
paddle.enable_static()
21
import paddle.fluid.core as core
22
from op_test import OpTest
C
chengduoZH 已提交
23 24


C
chengduoZH 已提交
25
def conv3dtranspose_forward_naive(input_, filter_, attrs):
26 27
    padding_algorithm = attrs['padding_algorithm']
    if padding_algorithm not in ["SAME", "VALID", "EXPLICIT"]:
28 29 30 31
        raise ValueError(
            "Unknown Attr(padding_algorithm): '%s'. "
            "It can only be 'SAME' or 'VALID'." % str(padding_algorithm)
        )
32 33 34

    if attrs['data_format'] == 'NHWC':
        input_ = np.transpose(input_, [0, 4, 1, 2, 3])
C
chengduoZH 已提交
35
    in_n, in_c, in_d, in_h, in_w = input_.shape
36 37
    f_c, f_out_c, f_d, f_h, f_w = filter_.shape
    groups = attrs['groups']
C
chengduoZH 已提交
38
    assert in_c == f_c
39
    out_c = f_out_c * groups
M
minqiyang 已提交
40
    sub_in_c = in_c // groups
C
chengduoZH 已提交
41

42 43 44 45 46
    stride, pad, dilations = (
        attrs['strides'],
        attrs['paddings'],
        attrs['dilations'],
    )
C
chengduoZH 已提交
47

48 49
    def _get_padding_with_SAME(input_shape, kernel_size, kernel_stride):
        padding = []
50 51 52
        for input_size, filter_size, stride_size in zip(
            input_shape, kernel_size, kernel_stride
        ):
53
            out_size = int((input_size + stride_size - 1) / stride_size)
54
            pad_sum = np.max(
55 56
                ((out_size - 1) * stride_size + filter_size - input_size, 0)
            )
57 58 59 60 61 62 63 64 65 66
            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":
67 68
        dilations = [1, 1, 1]
        input_data_shape = input_.shape[2:5]
69 70 71 72 73 74 75 76 77 78
        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]

C
chengduoZH 已提交
79 80 81 82 83 84
    d_bolck_d = dilations[0] * (f_d - 1) + 1
    d_bolck_h = dilations[1] * (f_h - 1) + 1
    d_bolck_w = dilations[2] * (f_w - 1) + 1
    out_d = (in_d - 1) * stride[0] + d_bolck_d
    out_h = (in_h - 1) * stride[1] + d_bolck_h
    out_w = (in_w - 1) * stride[2] + d_bolck_w
C
chengduoZH 已提交
85 86 87 88 89 90
    out = np.zeros((in_n, out_c, out_d, out_h, out_w))

    for n in range(in_n):
        for d in range(in_d):
            for i in range(in_h):
                for j in range(in_w):
91
                    for g in range(groups):
92 93 94 95 96 97
                        input_masked = input_[
                            n, g * sub_in_c : (g + 1) * sub_in_c, d, i, j
                        ]  # (c)
                        input_masked = np.reshape(
                            input_masked, (sub_in_c, 1, 1, 1)
                        )
98 99 100
                        input_masked = np.tile(input_masked, (1, f_d, f_h, f_w))

                        for k in range(f_out_c):
101 102 103 104 105 106 107 108 109 110 111
                            tmp_out = np.sum(
                                input_masked
                                * filter_[
                                    g * sub_in_c : (g + 1) * sub_in_c,
                                    k,
                                    :,
                                    :,
                                    :,
                                ],
                                axis=0,
                            )
112 113 114
                            d1, d2 = d * stride[0], d * stride[0] + d_bolck_d
                            i1, i2 = i * stride[1], i * stride[1] + d_bolck_h
                            j1, j2 = j * stride[2], j * stride[2] + d_bolck_w
115 116 117 118 119 120 121 122 123 124 125 126 127 128 129
                            out[
                                n,
                                g * f_out_c + k,
                                d1 : d2 : dilations[0],
                                i1 : i2 : dilations[1],
                                j1 : j2 : dilations[2],
                            ] += tmp_out

    out = out[
        :,
        :,
        pad_d_0 : out_d - pad_d_1,
        pad_h_0 : out_h - pad_h_1,
        pad_w_0 : out_w - pad_w_1,
    ]
130 131
    if attrs['data_format'] == 'NHWC':
        out = np.transpose(out, [0, 2, 3, 4, 1])
C
chengduoZH 已提交
132 133 134
    return out


C
cnn 已提交
135
class TestConv3DTransposeOp(OpTest):
C
chengduoZH 已提交
136 137
    def setUp(self):
        # init as conv transpose
138
        self.use_cudnn = False
139 140
        self.check_no_input = False
        self.check_no_filter = False
141 142 143
        self.data_format = 'NCHW'
        self.pad = [0, 0, 0]
        self.padding_algorithm = "EXPLICIT"
C
chengduoZH 已提交
144 145 146 147 148 149 150 151 152 153
        self.init_op_type()
        self.init_test_case()

        input_ = np.random.random(self.input_size).astype("float32")
        filter_ = np.random.random(self.filter_size).astype("float32")

        self.inputs = {'Input': input_, 'Filter': filter_}
        self.attrs = {
            'strides': self.stride,
            'paddings': self.pad,
154
            'padding_algorithm': self.padding_algorithm,
155
            'dilations': self.dilations,
156
            'groups': self.groups,
157
            'use_cudnn': self.use_cudnn,
158
            'data_format': self.data_format,
C
chengduoZH 已提交
159
        }
C
chengduoZH 已提交
160

161 162 163
        output = conv3dtranspose_forward_naive(
            input_, filter_, self.attrs
        ).astype("float32")
C
chengduoZH 已提交
164

C
chengduoZH 已提交
165 166 167
        self.outputs = {'Output': output}

    def test_check_output(self):
168 169 170 171 172
        if self.use_cudnn:
            place = core.CUDAPlace(0)
            self.check_output_with_place(place, atol=1e-5)
        else:
            self.check_output()
C
chengduoZH 已提交
173 174

    def test_check_grad(self):
175 176
        if self.use_cudnn:
            place = core.CUDAPlace(0)
177 178 179 180 181 182
            self.check_grad_with_place(
                place,
                set(['Input', 'Filter']),
                'Output',
                max_relative_error=0.03,
            )
183
        else:
184 185 186
            self.check_grad(
                set(['Input', 'Filter']), 'Output', max_relative_error=0.03
            )
C
chengduoZH 已提交
187 188

    def test_check_grad_no_filter(self):
189 190
        if self.use_cudnn:
            place = core.CUDAPlace(0)
191 192 193 194 195 196 197
            self.check_grad_with_place(
                place,
                ['Input'],
                'Output',
                max_relative_error=0.03,
                no_grad_set=set(['Filter']),
            )
198
        elif self.check_no_filter:
199 200 201 202 203 204
            self.check_grad(
                ['Input'],
                'Output',
                max_relative_error=0.03,
                no_grad_set=set(['Filter']),
            )
C
chengduoZH 已提交
205 206

    def test_check_grad_no_input(self):
207 208
        if self.use_cudnn:
            place = core.CUDAPlace(0)
209 210 211 212 213 214 215
            self.check_grad_with_place(
                place,
                ['Filter'],
                'Output',
                max_relative_error=0.03,
                no_grad_set=set(['Input']),
            )
216
        elif self.check_no_input:
217 218 219 220 221 222
            self.check_grad(
                ['Filter'],
                'Output',
                max_relative_error=0.03,
                no_grad_set=set(['Input']),
            )
C
chengduoZH 已提交
223 224 225 226 227

    def init_test_case(self):
        self.pad = [0, 0, 0]
        self.stride = [1, 1, 1]
        self.dilations = [1, 1, 1]
228
        self.groups = 1
C
chengduoZH 已提交
229
        self.input_size = [2, 3, 5, 5, 5]  # NCDHW
C
chengduoZH 已提交
230 231 232 233
        f_c = self.input_size[1]
        self.filter_size = [f_c, 6, 3, 3, 3]

    def init_op_type(self):
C
chengduoZH 已提交
234
        self.op_type = "conv3d_transpose"
C
chengduoZH 已提交
235 236


C
cnn 已提交
237
class TestWithSymmetricPad(TestConv3DTransposeOp):
C
chengduoZH 已提交
238
    def init_test_case(self):
239
        self.check_no_input = True
C
chengduoZH 已提交
240 241 242
        self.pad = [1, 1, 1]
        self.stride = [1, 1, 1]
        self.dilations = [1, 1, 1]
243
        self.groups = 1
K
Kaipeng Deng 已提交
244
        self.input_size = [1, 2, 5, 5, 5]  # NCDHW
C
chengduoZH 已提交
245 246 247 248
        f_c = self.input_size[1]
        self.filter_size = [f_c, 6, 3, 3, 3]


C
cnn 已提交
249
class TestWithAsymmetricPad(TestConv3DTransposeOp):
250 251 252 253 254
    def init_test_case(self):
        self.pad = [1, 0, 1, 0, 1, 2]
        self.stride = [1, 1, 1]
        self.dilations = [1, 1, 1]
        self.groups = 1
K
Kaipeng Deng 已提交
255
        self.input_size = [1, 2, 5, 5, 5]  # NCDHW
256 257 258 259
        f_c = self.input_size[1]
        self.filter_size = [f_c, 6, 3, 3, 3]


C
cnn 已提交
260
class TestWithSAMEPad(TestConv3DTransposeOp):
261
    def init_test_case(self):
262 263
        self.stride = [1, 1, 2]
        self.dilations = [1, 2, 1]
264
        self.groups = 1
K
Kaipeng Deng 已提交
265
        self.input_size = [1, 2, 5, 5, 6]  # NCDHW
266
        f_c = self.input_size[1]
267
        self.filter_size = [f_c, 6, 3, 3, 4]
268 269 270
        self.padding_algorithm = 'SAME'


C
cnn 已提交
271
class TestWithVALIDPad(TestConv3DTransposeOp):
272
    def init_test_case(self):
273
        self.stride = [2, 1, 1]
274 275
        self.dilations = [1, 1, 1]
        self.groups = 1
K
Kaipeng Deng 已提交
276
        self.input_size = [1, 2, 5, 5, 5]  # NCDHW
277
        f_c = self.input_size[1]
278
        self.filter_size = [f_c, 6, 3, 4, 3]
279 280 281
        self.padding_algorithm = 'VALID'


C
cnn 已提交
282
class TestWithStride(TestConv3DTransposeOp):
283
    def init_test_case(self):
284
        self.check_no_filter = True
285
        self.pad = [1, 1, 1]
286
        self.stride = [2, 2, 2]
287
        self.dilations = [1, 1, 1]
288
        self.groups = 1
K
Kaipeng Deng 已提交
289
        self.input_size = [1, 2, 5, 5, 5]  # NCDHW
290
        f_c = self.input_size[1]
291
        self.filter_size = [f_c, 6, 3, 3, 3]
292 293


C
cnn 已提交
294
class TestWithGroups(TestConv3DTransposeOp):
C
chengduoZH 已提交
295 296
    def init_test_case(self):
        self.pad = [1, 1, 1]
297
        self.stride = [1, 1, 1]
C
chengduoZH 已提交
298
        self.dilations = [1, 1, 1]
299
        self.groups = 2
K
Kaipeng Deng 已提交
300
        self.input_size = [1, 2, 5, 5, 5]  # NCHW
C
chengduoZH 已提交
301
        f_c = self.input_size[1]
302
        self.filter_size = [f_c, 3, 3, 3, 3]
C
chengduoZH 已提交
303 304


C
cnn 已提交
305
class TestWithDilation(TestConv3DTransposeOp):
C
chengduoZH 已提交
306 307 308 309
    def init_test_case(self):
        self.pad = [1, 1, 1]
        self.stride = [1, 1, 1]
        self.dilations = [2, 2, 2]
310
        self.groups = 1
K
Kaipeng Deng 已提交
311
        self.input_size = [1, 2, 5, 5, 5]  # NCDHW
C
chengduoZH 已提交
312 313 314 315
        f_c = self.input_size[1]
        self.filter_size = [f_c, 6, 3, 3, 3]


C
cnn 已提交
316
class Test_NHWC(TestConv3DTransposeOp):
317 318 319 320 321
    def init_test_case(self):
        self.pad = [0, 0, 0]
        self.stride = [1, 1, 1]
        self.dilations = [1, 1, 1]
        self.groups = 1
K
Kaipeng Deng 已提交
322
        self.input_size = [1, 5, 5, 5, 2]  # NDHWC
323 324 325 326 327
        f_c = self.input_size[-1]
        self.filter_size = [f_c, 6, 3, 3, 3]
        self.data_format = 'NHWC'


C
chengduoZH 已提交
328
# ------------ test_cudnn ------------
329 330 331
@unittest.skipIf(
    not core.is_compiled_with_cuda(), "core is not compiled with CUDA"
)
C
cnn 已提交
332
class TestCUDNN(TestConv3DTransposeOp):
C
chengduoZH 已提交
333
    def init_op_type(self):
334 335
        self.use_cudnn = True
        self.op_type = "conv3d_transpose"
C
chengduoZH 已提交
336 337


338 339 340
@unittest.skipIf(
    not core.is_compiled_with_cuda(), "core is not compiled with CUDA"
)
341
class TestCUDNNWithSymmetricPad(TestWithSymmetricPad):
C
chengduoZH 已提交
342 343 344 345
    def init_test_case(self):
        self.pad = [1, 1, 1]
        self.stride = [1, 1, 1]
        self.dilations = [1, 1, 1]
346
        self.groups = 1
K
Kaipeng Deng 已提交
347
        self.input_size = [1, 2, 5, 5, 5]  # NCDHW
C
chengduoZH 已提交
348 349 350 351
        f_c = self.input_size[1]
        self.filter_size = [f_c, 6, 3, 3, 3]

    def init_op_type(self):
352 353
        self.use_cudnn = True
        self.op_type = "conv3d_transpose"
C
chengduoZH 已提交
354 355


356 357 358
@unittest.skipIf(
    not core.is_compiled_with_cuda(), "core is not compiled with CUDA"
)
359 360 361 362 363 364
class TestCUDNNWithAsymmetricPad(TestWithAsymmetricPad):
    def init_test_case(self):
        self.pad = [1, 1, 1, 0, 0, 2]
        self.stride = [1, 1, 1]
        self.dilations = [1, 1, 1]
        self.groups = 1
K
Kaipeng Deng 已提交
365
        self.input_size = [1, 2, 4, 4, 4]  # NCDHW
366 367 368 369 370 371 372 373
        f_c = self.input_size[1]
        self.filter_size = [f_c, 6, 3, 3, 3]

    def init_op_type(self):
        self.use_cudnn = True
        self.op_type = "conv3d_transpose"


374 375 376
@unittest.skipIf(
    not core.is_compiled_with_cuda(), "core is not compiled with CUDA"
)
377 378
class TestCUDNNWithSAMEPad(TestWithSAMEPad):
    def init_test_case(self):
379 380
        self.stride = [1, 1, 2]
        self.dilations = [1, 2, 1]
381
        self.groups = 1
K
Kaipeng Deng 已提交
382
        self.input_size = [1, 2, 5, 5, 5]  # NCDHW
383
        f_c = self.input_size[1]
384
        self.filter_size = [f_c, 6, 3, 4, 3]
385 386 387 388 389 390 391
        self.padding_algorithm = 'SAME'

    def init_op_type(self):
        self.use_cudnn = True
        self.op_type = "conv3d_transpose"


392 393 394
@unittest.skipIf(
    not core.is_compiled_with_cuda(), "core is not compiled with CUDA"
)
395 396 397 398 399
class TestCUDNNWithVALIDPad(TestWithVALIDPad):
    def init_test_case(self):
        self.stride = [1, 1, 1]
        self.dilations = [1, 1, 1]
        self.groups = 1
K
Kaipeng Deng 已提交
400
        self.input_size = [1, 2, 5, 5, 5]  # NCDHW
401 402 403 404 405 406 407 408 409
        f_c = self.input_size[1]
        self.filter_size = [f_c, 6, 3, 3, 3]
        self.padding_algorithm = 'VALID'

    def init_op_type(self):
        self.use_cudnn = True
        self.op_type = "conv3d_transpose"


410 411 412
@unittest.skipIf(
    not core.is_compiled_with_cuda(), "core is not compiled with CUDA"
)
413
class TestCUDNNWithStride(TestWithStride):
C
chengduoZH 已提交
414 415 416 417
    def init_test_case(self):
        self.pad = [1, 1, 1]
        self.stride = [2, 2, 2]
        self.dilations = [1, 1, 1]
418
        self.groups = 1
K
Kaipeng Deng 已提交
419
        self.input_size = [1, 2, 5, 5, 5]  # NCDHW
C
chengduoZH 已提交
420 421 422 423
        f_c = self.input_size[1]
        self.filter_size = [f_c, 6, 3, 3, 3]

    def init_op_type(self):
424 425
        self.use_cudnn = True
        self.op_type = "conv3d_transpose"
C
chengduoZH 已提交
426 427


428 429 430
@unittest.skipIf(
    not core.is_compiled_with_cuda(), "core is not compiled with CUDA"
)
431 432 433 434 435 436
class TestCUDNNWithGroups(TestWithGroups):
    def init_test_case(self):
        self.pad = [1, 1, 1]
        self.stride = [1, 1, 1]
        self.dilations = [1, 1, 1]
        self.groups = 2
K
Kaipeng Deng 已提交
437
        self.input_size = [1, 2, 5, 5, 5]  # NCHW
438 439 440 441 442 443 444 445
        f_c = self.input_size[1]
        self.filter_size = [f_c, 3, 3, 3, 3]

    def init_op_type(self):
        self.use_cudnn = True
        self.op_type = "conv3d_transpose"


446 447
# Please Don't remove the following code.
# Currently, CI use cudnn V5.0 which not support dilation conv.
448
# class TestCUDNNWithDilation(TestWithDilation):
C
chengduoZH 已提交
449 450 451 452 453 454 455 456 457
#     def init_test_case(self):
#         self.pad = [1, 1, 1]
#         self.stride = [2, 2, 2]
#         self.dilations = [2, 2, 2]
#         self.input_size = [2, 3, 5, 5, 5]  # NCDHW
#         f_c = self.input_size[1]
#         self.filter_size = [f_c, 6, 3, 3, 3]
#
#     def init_op_type(self):
458
#         self.op_type = "conv3d_transpose"
C
chengduoZH 已提交
459

460

461 462 463
@unittest.skipIf(
    not core.is_compiled_with_cuda(), "core is not compiled with CUDA"
)
C
cnn 已提交
464
class TestCUDNN_NHWC(TestConv3DTransposeOp):
465 466 467 468 469
    def init_test_case(self):
        self.pad = [0, 0, 0]
        self.stride = [1, 1, 1]
        self.dilations = [1, 1, 1]
        self.groups = 1
K
Kaipeng Deng 已提交
470
        self.input_size = [1, 5, 5, 5, 2]  # NDHWC
471 472 473 474 475 476 477 478 479
        f_c = self.input_size[-1]
        self.filter_size = [f_c, 6, 3, 3, 3]
        self.data_format = 'NHWC'

    def init_op_type(self):
        self.use_cudnn = True
        self.op_type = "conv3d_transpose"


480 481 482
@unittest.skipIf(
    not core.is_compiled_with_cuda(), "core is not compiled with CUDA"
)
483 484 485 486 487 488
class TestCUDNNWithSymmetricPad_NHWC(TestWithSymmetricPad):
    def init_test_case(self):
        self.pad = [1, 1, 1]
        self.stride = [1, 1, 1]
        self.dilations = [1, 1, 1]
        self.groups = 1
K
Kaipeng Deng 已提交
489
        self.input_size = [1, 5, 5, 5, 2]  # NDHWC
490 491 492 493 494 495 496 497 498
        f_c = self.input_size[-1]
        self.filter_size = [f_c, 6, 3, 3, 3]
        self.data_format = 'NHWC'

    def init_op_type(self):
        self.use_cudnn = True
        self.op_type = "conv3d_transpose"


499 500 501
@unittest.skipIf(
    not core.is_compiled_with_cuda(), "core is not compiled with CUDA"
)
502 503 504 505 506 507
class TestCUDNNWithAsymmetricPad_NHWC(TestWithAsymmetricPad):
    def init_test_case(self):
        self.pad = [1, 0, 1, 0, 0, 2]
        self.stride = [1, 1, 1]
        self.dilations = [1, 1, 1]
        self.groups = 1
K
Kaipeng Deng 已提交
508
        self.input_size = [1, 5, 5, 5, 2]  # NDHWC
509 510 511 512 513 514 515 516 517
        f_c = self.input_size[-1]
        self.filter_size = [f_c, 6, 3, 3, 3]
        self.data_format = 'NHWC'

    def init_op_type(self):
        self.use_cudnn = True
        self.op_type = "conv3d_transpose"


518 519 520
@unittest.skipIf(
    not core.is_compiled_with_cuda(), "core is not compiled with CUDA"
)
521 522 523 524 525 526
class TestCUDNNWithStride_NHWC(TestWithStride):
    def init_test_case(self):
        self.pad = [1, 1, 1]
        self.stride = [2, 2, 2]
        self.dilations = [1, 1, 1]
        self.groups = 1
K
Kaipeng Deng 已提交
527
        self.input_size = [1, 5, 5, 5, 2]  # NDHWC
528 529 530 531 532 533 534 535 536
        f_c = self.input_size[-1]
        self.filter_size = [f_c, 6, 3, 3, 3]
        self.data_format = 'NHWC'

    def init_op_type(self):
        self.use_cudnn = True
        self.op_type = "conv3d_transpose"


537 538 539
@unittest.skipIf(
    not core.is_compiled_with_cuda(), "core is not compiled with CUDA"
)
540 541 542 543 544 545
class TestCUDNNWithGroups_NHWC(TestWithGroups):
    def init_test_case(self):
        self.pad = [1, 1, 1]
        self.stride = [1, 1, 1]
        self.dilations = [1, 1, 1]
        self.groups = 2
K
Kaipeng Deng 已提交
546
        self.input_size = [1, 5, 5, 5, 2]  # NDHWC
547 548 549 550 551 552 553 554 555
        f_c = self.input_size[-1]
        self.filter_size = [f_c, 3, 3, 3, 3]
        self.data_format = 'NHWC'

    def init_op_type(self):
        self.use_cudnn = True
        self.op_type = "conv3d_transpose"


C
chengduoZH 已提交
556 557
if __name__ == '__main__':
    unittest.main()