test_conv2d_op.py 16.4 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.

15 16
from __future__ import print_function

17 18
import unittest
import numpy as np
D
dzhwinter 已提交
19

20
import paddle.fluid.core as core
21
from op_test import OpTest
22 23


C
chengduoZH 已提交
24 25 26 27 28
def conv2d_forward_naive(input, filter, group, conv_param):
    in_n, in_c, in_h, in_w = input.shape
    out_c, f_c, f_h, f_w = filter.shape
    assert f_c * group == in_c
    assert np.mod(out_c, group) == 0
M
minqiyang 已提交
29
    sub_out_c = out_c // group
C
chengduoZH 已提交
30

C
chengduoZH 已提交
31 32
    stride, pad, dilation = conv_param['stride'], conv_param['pad'], conv_param[
        'dilation']
M
minqiyang 已提交
33 34
    out_h = 1 + (in_h + 2 * pad[0] - (dilation[0] * (f_h - 1) + 1)) // stride[0]
    out_w = 1 + (in_w + 2 * pad[1] - (dilation[1] * (f_w - 1) + 1)) // stride[1]
C
chengduoZH 已提交
35 36
    out = np.zeros((in_n, out_c, out_h, out_w))

武毅 已提交
37 38
    d_bolck_h = (dilation[0] * (f_h - 1) + 1)
    d_bolck_w = (dilation[1] * (f_w - 1) + 1)
C
chengduoZH 已提交
39

C
chengduoZH 已提交
40
    input_pad = np.pad(input, ((0, ), (0, ), (pad[0], ), (pad[1], )),
C
chengduoZH 已提交
41 42
                       mode='constant',
                       constant_values=0)
C
chengduoZH 已提交
43 44 45 46 47

    filter_dilation = np.zeros((out_c, f_c, d_bolck_h, d_bolck_w))
    filter_dilation[:, :, 0:d_bolck_h:dilation[0], 0:d_bolck_w:dilation[
        1]] = filter

C
chengduoZH 已提交
48 49 50
    for i in range(out_h):
        for j in range(out_w):
            for g in range(group):
C
chengduoZH 已提交
51 52
                input_pad_masked = \
                    input_pad[:, g * f_c:(g + 1) * f_c,
C
chengduoZH 已提交
53 54
                    i * stride[0]:i * stride[0] + d_bolck_h,
                    j * stride[1]:j * stride[1] + d_bolck_w]
C
chengduoZH 已提交
55

C
chengduoZH 已提交
56 57
                f_sub = filter_dilation[g * sub_out_c:(g + 1) *
                                        sub_out_c, :, :, :]
C
chengduoZH 已提交
58
                for k in range(sub_out_c):
C
chengduoZH 已提交
59 60 61
                    out[:, g * sub_out_c + k, i, j] = \
                        np.sum(input_pad_masked * f_sub[k, :, :, :],
                               axis=(1, 2, 3))
C
chengduoZH 已提交
62

63
    return out, in_n, out_h, out_w, out_c
C
chengduoZH 已提交
64 65


H
hedaoyuan 已提交
66
class TestConv2dOp(OpTest):
67
    def setUp(self):
K
Kexin Zhao 已提交
68
        self.op_type = "conv2d"
69
        self.use_cudnn = False
70
        self.exhaustive_search = False
71
        self.use_cuda = False
72
        self.use_mkldnn = False
73
        self.fuse_relu_before_depthwise_conv = False
74
        self.data_format = "AnyLayout"
K
Kexin Zhao 已提交
75
        self.dtype = np.float32
K
Kexin Zhao 已提交
76
        self.init_kernel_type()
C
chengduoZH 已提交
77
        self.init_group()
C
chengduoZH 已提交
78
        self.init_dilation()
C
chengduoZH 已提交
79
        self.init_test_case()
C
chengduoZH 已提交
80

C
chengduoZH 已提交
81 82 83 84 85
        conv2d_param = {
            'stride': self.stride,
            'pad': self.pad,
            'dilation': self.dilations
        }
86

K
Kexin Zhao 已提交
87
        input = np.random.random(self.input_size).astype(self.dtype)
G
guomingz 已提交
88
        if not self.has_cuda():
89 90 91 92 93 94 95 96
            self.fuse_relu_before_depthwise_conv = False
        if self.fuse_relu_before_depthwise_conv:
            input = input - 0.5
            input -= (input < 0) * 0.1
            input += (input >= 0) * 0.1
            input2 = np.maximum(input, 0.0)
        else:
            input2 = input
G
guomingz 已提交
97
        filter = np.random.uniform(-1, 1, self.filter_size).astype(self.dtype)
98
        output, _, _, _, _ = conv2d_forward_naive(input2, filter, self.groups,
99 100
                                                  conv2d_param)
        output = output.astype(self.dtype)
K
Kexin Zhao 已提交
101 102

        self.inputs = {
K
Kexin Zhao 已提交
103 104
            'Input': OpTest.np_dtype_to_fluid_dtype(input),
            'Filter': OpTest.np_dtype_to_fluid_dtype(filter)
K
Kexin Zhao 已提交
105
        }
H
hedaoyuan 已提交
106
        self.attrs = {
C
chengduoZH 已提交
107 108
            'strides': self.stride,
            'paddings': self.pad,
C
chengduoZH 已提交
109
            'groups': self.groups,
110
            'dilations': self.dilations,
111
            'use_cudnn': self.use_cudnn,
112
            'use_mkldnn': self.use_mkldnn,
113
            'data_format': self.data_format,
114 115
            'fuse_relu_before_depthwise_conv':
            self.fuse_relu_before_depthwise_conv,
116
            'exhaustive_search': self.exhaustive_search
H
hedaoyuan 已提交
117
        }
118 119
        self.outputs = {'Output': output}

G
guomingz 已提交
120
    def has_cuda(self):
121 122
        return core.is_compiled_with_cuda() and (self.use_cudnn or
                                                 self.use_cuda)
123

H
hedaoyuan 已提交
124
    def test_check_output(self):
G
guomingz 已提交
125
        place = core.CUDAPlace(0) if self.has_cuda() else core.CPUPlace()
126
        self.check_output_with_place(place, atol=1e-5)
H
hedaoyuan 已提交
127

H
hedaoyuan 已提交
128
    def test_check_grad(self):
K
Kexin Zhao 已提交
129 130
        if self.dtype == np.float16:
            return
G
guomingz 已提交
131
        place = core.CUDAPlace(0) if self.has_cuda() else core.CPUPlace()
132
        self.check_grad_with_place(
Y
Yu Yang 已提交
133
            place, {'Input', 'Filter'}, 'Output', max_relative_error=0.02)
H
hedaoyuan 已提交
134

135
    def test_check_grad_no_filter(self):
K
Kexin Zhao 已提交
136 137
        if self.dtype == np.float16:
            return
G
guomingz 已提交
138
        place = core.CUDAPlace(0) if self.has_cuda() else core.CPUPlace()
139 140 141 142 143
        self.check_grad_with_place(
            place, ['Input'],
            'Output',
            max_relative_error=0.02,
            no_grad_set=set(['Filter']))
144 145

    def test_check_grad_no_input(self):
K
Kexin Zhao 已提交
146 147
        if self.dtype == np.float16:
            return
G
guomingz 已提交
148
        place = core.CUDAPlace(0) if self.has_cuda() else core.CPUPlace()
149 150 151 152 153
        self.check_grad_with_place(
            place, ['Filter'],
            'Output',
            max_relative_error=0.02,
            no_grad_set=set(['Input']))
154

C
chengduoZH 已提交
155 156 157 158 159
    def init_test_case(self):
        self.pad = [0, 0]
        self.stride = [1, 1]
        self.input_size = [2, 3, 5, 5]  # NCHW
        assert np.mod(self.input_size[1], self.groups) == 0
M
minqiyang 已提交
160
        f_c = self.input_size[1] // self.groups
C
chengduoZH 已提交
161 162
        self.filter_size = [6, f_c, 3, 3]

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

C
chengduoZH 已提交
166
    def init_group(self):
H
hedaoyuan 已提交
167 168
        self.groups = 1

K
Kexin Zhao 已提交
169 170
    def init_kernel_type(self):
        pass
武毅 已提交
171

H
hedaoyuan 已提交
172

C
chengduoZH 已提交
173 174 175 176 177 178
class TestWithPad(TestConv2dOp):
    def init_test_case(self):
        self.pad = [1, 1]
        self.stride = [1, 1]
        self.input_size = [2, 3, 5, 5]  # NCHW
        assert np.mod(self.input_size[1], self.groups) == 0
M
minqiyang 已提交
179
        f_c = self.input_size[1] // self.groups
C
chengduoZH 已提交
180 181 182 183 184 185 186 187 188
        self.filter_size = [6, f_c, 3, 3]


class TestWithStride(TestConv2dOp):
    def init_test_case(self):
        self.pad = [1, 1]
        self.stride = [2, 2]
        self.input_size = [2, 3, 6, 6]  # NCHW
        assert np.mod(self.input_size[1], self.groups) == 0
M
minqiyang 已提交
189
        f_c = self.input_size[1] // self.groups
C
chengduoZH 已提交
190 191 192
        self.filter_size = [6, f_c, 3, 3]


H
hedaoyuan 已提交
193
class TestWithGroup(TestConv2dOp):
C
chengduoZH 已提交
194
    def init_group(self):
H
hedaoyuan 已提交
195 196
        self.groups = 3

武毅 已提交
197

C
chengduoZH 已提交
198 199 200 201 202 203
class TestWith1x1(TestConv2dOp):
    def init_test_case(self):
        self.pad = [0, 0]
        self.stride = [1, 1]
        self.input_size = [2, 3, 5, 5]  # NCHW
        assert np.mod(self.input_size[1], self.groups) == 0
M
minqiyang 已提交
204
        f_c = self.input_size[1] // self.groups
C
chengduoZH 已提交
205 206 207 208 209 210
        self.filter_size = [6, f_c, 1, 1]

    def init_group(self):
        self.groups = 3


211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252
class TestWithDepthWise3x3(TestConv2dOp):
    def init_test_case(self):
        self.pad = [1, 1]
        self.stride = [1, 1]
        self.input_size = [3, 4, 10, 10]  # NCHW
        assert np.mod(self.input_size[1], self.groups) == 0
        f_c = self.input_size[1] // self.groups
        self.filter_size = [8, f_c, 3, 3]

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

    def init_group(self):
        self.groups = 4


class TestWithDepthWise5x5(TestConv2dOp):
    def init_test_case(self):
        self.pad = [0, 0]
        self.stride = [1, 1]
        self.input_size = [2, 4, 10, 10]  # NCHW
        assert np.mod(self.input_size[1], self.groups) == 0
        f_c = self.input_size[1] // self.groups
        self.filter_size = [8, f_c, 5, 5]

    def init_group(self):
        self.groups = 4


class TestWithDepthWise7x7(TestConv2dOp):
    def init_test_case(self):
        self.pad = [1, 1]
        self.stride = [2, 2]
        self.input_size = [2, 8, 10, 10]  # NCHW
        assert np.mod(self.input_size[1], self.groups) == 0
        f_c = self.input_size[1] // self.groups
        self.filter_size = [16, f_c, 7, 7]

    def init_group(self):
        self.groups = 8


C
chengduoZH 已提交
253 254 255 256 257 258
class TestWithDilation(TestConv2dOp):
    def init_test_case(self):
        self.pad = [0, 0]
        self.stride = [1, 1]
        self.input_size = [2, 3, 10, 10]  # NCHW
        assert np.mod(self.input_size[1], self.groups) == 0
M
minqiyang 已提交
259
        f_c = self.input_size[1] // self.groups
C
chengduoZH 已提交
260
        self.filter_size = [6, f_c, 3, 3]
C
chengduoZH 已提交
261

C
chengduoZH 已提交
262 263
    def init_dilation(self):
        self.dilations = [2, 2]
C
chengduoZH 已提交
264

C
chengduoZH 已提交
265
    def init_group(self):
C
chengduoZH 已提交
266
        self.groups = 3
武毅 已提交
267

C
chengduoZH 已提交
268

269 270 271 272 273 274
class TestWithInput1x1Filter1x1(TestConv2dOp):
    def init_test_case(self):
        self.pad = [0, 0]
        self.stride = [1, 1]
        self.input_size = [2, 3, 1, 1]  # NCHW
        assert np.mod(self.input_size[1], self.groups) == 0
M
minqiyang 已提交
275
        f_c = self.input_size[1] // self.groups
276 277 278 279 280 281
        self.filter_size = [6, f_c, 1, 1]

    def init_group(self):
        self.groups = 3


282
#----------------Conv2dCUDNN----------------
C
chengduoZH 已提交
283

K
Kexin Zhao 已提交
284

C
chengduo 已提交
285
def create_test_cudnn_class(parent):
C
chengduo 已提交
286 287 288 289 290
    @unittest.skipIf(not core.is_compiled_with_cuda(),
                     "core is not compiled with CUDA")
    class TestCUDNNCase(parent):
        def init_kernel_type(self):
            self.use_cudnn = True
K
Kexin Zhao 已提交
291

C
chengduo 已提交
292
    cls_name = "{0}_{1}".format(parent.__name__, "CUDNN")
C
chengduo 已提交
293 294
    TestCUDNNCase.__name__ = cls_name
    globals()[cls_name] = TestCUDNNCase
K
Kexin Zhao 已提交
295

K
Kexin Zhao 已提交
296

C
chengduo 已提交
297 298 299 300 301 302
create_test_cudnn_class(TestConv2dOp)
create_test_cudnn_class(TestWithPad)
create_test_cudnn_class(TestWithStride)
create_test_cudnn_class(TestWithGroup)
create_test_cudnn_class(TestWith1x1)
create_test_cudnn_class(TestWithInput1x1Filter1x1)
K
Kexin Zhao 已提交
303

C
chengduo 已提交
304
#----------------Conv2dCUDNN----------------
K
Kexin Zhao 已提交
305

C
chengduoZH 已提交
306

C
chengduo 已提交
307
def create_test_cudnn_fp16_class(parent, grad_check=True):
C
chengduo 已提交
308 309 310 311 312 313
    @unittest.skipIf(not core.is_compiled_with_cuda(),
                     "core is not compiled with CUDA")
    class TestConv2DCUDNNFp16(parent):
        def init_kernel_type(self):
            self.use_cudnn = True
            self.dtype = np.float16
武毅 已提交
314

C
chengduo 已提交
315 316 317 318 319
        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)
K
Kexin Zhao 已提交
320

C
chengduo 已提交
321
        def test_check_grad_no_filter(self):
K
Kexin Zhao 已提交
322
            place = core.CUDAPlace(0)
C
chengduo 已提交
323 324 325 326 327 328 329 330
            if core.is_float16_supported(place) and grad_check:
                self.check_grad_with_place(
                    place, ['Input'],
                    'Output',
                    max_relative_error=0.02,
                    no_grad_set=set(['Filter']))

        def test_check_grad_no_input(self):
K
Kexin Zhao 已提交
331
            place = core.CUDAPlace(0)
C
chengduo 已提交
332 333 334 335 336 337 338
            if core.is_float16_supported(place) and grad_check:
                self.check_grad_with_place(
                    place, ['Filter'],
                    'Output',
                    max_relative_error=0.02,
                    no_grad_set=set(['Input']))

C
chengduo 已提交
339
    cls_name = "{0}_{1}".format(parent.__name__, "CUDNNFp16")
C
chengduo 已提交
340 341 342 343
    TestConv2DCUDNNFp16.__name__ = cls_name
    globals()[cls_name] = TestConv2DCUDNNFp16


C
chengduo 已提交
344 345 346 347 348 349
create_test_cudnn_fp16_class(TestConv2dOp, grad_check=False)
create_test_cudnn_fp16_class(TestWithPad, grad_check=False)
create_test_cudnn_fp16_class(TestWithStride, grad_check=False)
create_test_cudnn_fp16_class(TestWithGroup, grad_check=False)
create_test_cudnn_fp16_class(TestWith1x1, grad_check=False)
create_test_cudnn_fp16_class(TestWithInput1x1Filter1x1, grad_check=False)
C
chengduo 已提交
350 351

# -------TestDepthwiseConv
K
Kexin Zhao 已提交
352 353


354 355
class TestDepthwiseConv(TestConv2dOp):
    def init_test_case(self):
356
        self.use_cuda = True
357 358 359 360 361
        self.pad = [1, 1]
        self.stride = [2, 2]
        self.input_size = [2, 3, 5, 5]  # NCHW
        self.groups = 3
        assert np.mod(self.input_size[1], self.groups) == 0
M
minqiyang 已提交
362
        f_c = self.input_size[1] // self.groups
363
        self.filter_size = [3, f_c, 3, 3]
364
        self.op_type = "depthwise_conv2d"
365 366 367 368


class TestDepthwiseConv2(TestConv2dOp):
    def init_test_case(self):
369 370 371 372 373 374 375 376 377 378 379 380 381 382
        self.use_cuda = True
        self.pad = [1, 1]
        self.stride = [1, 1]
        self.input_size = [2, 3, 5, 5]  # NCHW
        self.groups = 3
        assert np.mod(self.input_size[1], self.groups) == 0
        f_c = self.input_size[1] // self.groups
        self.filter_size = [3, f_c, 3, 3]
        self.op_type = "depthwise_conv2d"


class TestDepthwiseConv3(TestConv2dOp):
    def init_test_case(self):
        self.use_cuda = True
383 384 385 386 387
        self.pad = [1, 1]
        self.stride = [1, 1]
        self.input_size = [2, 3, 5, 5]  # NCHW
        self.groups = 3
        assert np.mod(self.input_size[1], self.groups) == 0
M
minqiyang 已提交
388
        f_c = self.input_size[1] // self.groups
389
        self.filter_size = [6, f_c, 3, 3]
390
        self.op_type = "depthwise_conv2d"
391 392


393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420
class TestDepthwiseConvWithDilation(TestConv2dOp):
    def init_test_case(self):
        self.use_cuda = True
        self.pad = [1, 1]
        self.stride = [2, 2]
        self.input_size = [2, 3, 5, 5]  # NCHW
        self.groups = 3
        self.dilations = [2, 2]
        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]
        self.op_type = "depthwise_conv2d"


class TestDepthwiseConvWithDilation2(TestConv2dOp):
    def init_test_case(self):
        self.use_cuda = True
        self.pad = [1, 1]
        self.stride = [1, 1]
        self.input_size = [2, 3, 5, 5]  # NCHW
        self.groups = 3
        self.dilations = [2, 2]
        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]
        self.op_type = "depthwise_conv2d"


421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492
class TestDepthwiseConvandFuse(TestConv2dOp):
    def init_test_case(self):
        self.fuse_relu_before_depthwise_conv = True
        self.use_cuda = True
        self.pad = [1, 1]
        self.stride = [2, 2]
        self.input_size = [2, 3, 5, 5]  # NCHW
        self.groups = 3
        assert np.mod(self.input_size[1], self.groups) == 0
        f_c = self.input_size[1] // self.groups
        self.filter_size = [3, f_c, 3, 3]
        self.op_type = "depthwise_conv2d"


class TestDepthwiseConv2andFuse(TestConv2dOp):
    def init_test_case(self):
        self.fuse_relu_before_depthwise_conv = True
        self.use_cuda = True
        self.pad = [1, 1]
        self.stride = [1, 1]
        self.input_size = [2, 3, 5, 5]  # NCHW
        self.groups = 3
        assert np.mod(self.input_size[1], self.groups) == 0
        f_c = self.input_size[1] // self.groups
        self.filter_size = [3, f_c, 3, 3]
        self.op_type = "depthwise_conv2d"


class TestDepthwiseConv3andFuse(TestConv2dOp):
    def init_test_case(self):
        self.fuse_relu_before_depthwise_conv = True
        self.use_cuda = True
        self.pad = [1, 1]
        self.stride = [1, 1]
        self.input_size = [2, 3, 5, 5]  # NCHW
        self.groups = 3
        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]
        self.op_type = "depthwise_conv2d"


class TestDepthwiseConvWithDilationandFuse(TestConv2dOp):
    def init_test_case(self):
        self.fuse_relu_before_depthwise_conv = True
        self.use_cuda = True
        self.pad = [1, 1]
        self.stride = [2, 2]
        self.input_size = [2, 3, 5, 5]  # NCHW
        self.groups = 3
        self.dilations = [2, 2]
        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]
        self.op_type = "depthwise_conv2d"


class TestDepthwiseConvWithDilation2andFuse(TestConv2dOp):
    def init_test_case(self):
        self.fuse_relu_before_depthwise_conv = True
        self.use_cuda = True
        self.pad = [1, 1]
        self.stride = [1, 1]
        self.input_size = [2, 3, 5, 5]  # NCHW
        self.groups = 3
        self.dilations = [2, 2]
        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]
        self.op_type = "depthwise_conv2d"


493 494 495 496 497 498
class TestCUDNNExhaustiveSearch(TestConv2dOp):
    def init_kernel_type(self):
        self.use_cudnn = True
        self.exhaustive_search = True


499 500
# Please Don't remove the following code.
# Currently, CI use cudnn V5.0 which not support dilation conv.
501
# class TestCUDNNWithDilation(TestWithDilation):
C
chengduoZH 已提交
502 503 504
#     def init_op_type(self):
#         self.op_type = "conv_cudnn"

505 506
if __name__ == '__main__':
    unittest.main()