From c4dbeca3c06e09594be5413a4e0e0d312accaaac Mon Sep 17 00:00:00 2001 From: wangguanzhong Date: Mon, 31 May 2021 20:12:04 +0800 Subject: [PATCH] enhance error message for conv (#33119) * enhance error message for conv * fix ci coverage --- paddle/fluid/operators/conv_op.cc | 12 +++- paddle/fluid/operators/conv_transpose_op.cc | 14 +++- python/paddle/fluid/layers/nn.py | 43 +++++++++--- .../tests/unittests/test_conv2d_layer.py | 6 ++ .../fluid/tests/unittests/test_conv2d_op.py | 67 +++++++++++++++++++ .../tests/unittests/test_conv3d_layer.py | 3 + .../fluid/tests/unittests/test_conv3d_op.py | 15 +++++ .../unittests/test_conv3d_transpose_layer.py | 3 + .../unittests/test_deformable_conv_op.py | 13 ++++ python/paddle/nn/functional/conv.py | 4 ++ 10 files changed, 168 insertions(+), 12 deletions(-) diff --git a/paddle/fluid/operators/conv_op.cc b/paddle/fluid/operators/conv_op.cc index 85bb4e5baa..17ce109610 100644 --- a/paddle/fluid/operators/conv_op.cc +++ b/paddle/fluid/operators/conv_op.cc @@ -73,7 +73,17 @@ std::vector ConvOp::ComputeOutputShape( "the filter's dimension is %d.", in_dims, in_dims.size(), filter_dims, filter_dims.size())); - int in_sub_stride_size = in_dims.size() - strides.size(); + int stride_size = strides.size(); + for (int i = 0; i < stride_size; ++i) { + PADDLE_ENFORCE_GT( + strides[i], 0, + platform::errors::InvalidArgument( + "The stride of Op(Conv) should be larget than 0, but received " + "stride is %d.", + strides[i])); + } + + int in_sub_stride_size = in_dims.size() - stride_size; PADDLE_ENFORCE_EQ( in_dims.size(), strides.size() + 2U, platform::errors::InvalidArgument( diff --git a/paddle/fluid/operators/conv_transpose_op.cc b/paddle/fluid/operators/conv_transpose_op.cc index 4ea936d510..f004ea1c69 100644 --- a/paddle/fluid/operators/conv_transpose_op.cc +++ b/paddle/fluid/operators/conv_transpose_op.cc @@ -66,7 +66,19 @@ void ConvTransposeOp::InferShape(framework::InferShapeContext* ctx) const { "input is [%s], the dimension size of input is [%d], the shape " "of filter is [%s], the dimension size of filter is [%d]. ", in_dims, in_dims.size(), filter_dims, filter_dims.size())); - int in_sub_stride_size = in_dims.size() - strides.size(); + + int stride_size = strides.size(); + for (int i = 0; i < stride_size; ++i) { + PADDLE_ENFORCE_GT( + strides[i], 0, + platform::errors::InvalidArgument( + "The stride of Op(Conv) should be larget than 0, but received " + "stride is %d.", + strides[i])); + } + + int in_sub_stride_size = in_dims.size() - stride_size; + PADDLE_ENFORCE_EQ( in_dims.size() - strides.size(), 2U, platform::errors::InvalidArgument( diff --git a/python/paddle/fluid/layers/nn.py b/python/paddle/fluid/layers/nn.py index f87485c6a8..ee08cb8654 100755 --- a/python/paddle/fluid/layers/nn.py +++ b/python/paddle/fluid/layers/nn.py @@ -1502,6 +1502,9 @@ def conv2d(input, check_variable_and_dtype(input, 'input', ['float16', 'float32', 'float64'], 'conv2d') + if len(input.shape) != 4: + raise ValueError("Input size should be 4, " + "but received {}".format(len(input.shape))) num_channels = input.shape[1] if not isinstance(use_cudnn, bool): raise ValueError("Attr(use_cudnn) should be True or False. Received " @@ -1520,6 +1523,20 @@ def conv2d(input, "Received: %s." % (str(input.shape), str(num_channels))) assert param_attr is not False, "param_attr should not be False here." + if groups is None: + num_filter_channels = num_channels + elif groups <= 0: + raise ValueError("the groups of input must be greater than 0, " + "but received the groups of input is {}".format( + groups)) + else: + if num_channels % groups != 0: + raise ValueError( + "the channel of input must be divisible by groups," + "received: the channel of input is {}, the shape of input is {}" + ", the groups is {}".format(num_channels, input.shape, groups)) + num_filter_channels = num_channels // groups + l_type = 'conv2d' if (num_channels == groups and num_filters % num_channels == 0 and not use_cudnn): @@ -1532,16 +1549,6 @@ def conv2d(input, helper = LayerHelper(l_type, **locals()) dtype = helper.input_dtype() - if groups is None: - num_filter_channels = num_channels - else: - if num_channels % groups != 0: - raise ValueError( - "the channel of input must be divisible by groups," - "received: the channel of input is {}, the shape of input is {}" - ", the groups is {}".format(num_channels, input.shape, groups)) - num_filter_channels = num_channels // groups - filter_size = utils.convert_to_list(filter_size, 2, 'filter_size') stride = utils.convert_to_list(stride, 2, 'stride') dilation = utils.convert_to_list(dilation, 2, 'dilation') @@ -1597,6 +1604,11 @@ def conv2d(input, def _get_default_param_initializer(): filter_elem_num = filter_size[0] * filter_size[1] * num_channels + if filter_elem_num <= 0: + raise ValueError( + "Invalid filter number, excepted number is larger than 0, but" + " received {}, please check the input shape and " + "filter size.".format(filter_elem_num)) std = (2.0 / filter_elem_num)**0.5 return Normal(0.0, std, 0) @@ -1878,6 +1890,12 @@ def conv3d(input, def _get_default_param_initializer(): filter_elem_num = filter_size[0] * filter_size[1] * filter_size[ 2] * num_channels + if filter_elem_num <= 0: + raise ValueError( + "Invalid filter number, excepted number is larger than 0, but" + " received {}, please check the input shape and " + "filter size.".format(filter_elem_num)) + std = (2.0 / filter_elem_num)**0.5 return Normal(0.0, std, 0) @@ -14412,6 +14430,11 @@ def deformable_conv(input, def _get_default_param_initializer(): filter_elem_num = filter_size[0] * filter_size[1] * num_channels + if filter_elem_num <= 0: + raise ValueError( + "Invalid filter number, excepted number is larger than 0, but" + " received {}, please check the input shape and " + "filter size.".format(filter_elem_num)) std = (2.0 / filter_elem_num)**0.5 return Normal(0.0, std, 0) diff --git a/python/paddle/fluid/tests/unittests/test_conv2d_layer.py b/python/paddle/fluid/tests/unittests/test_conv2d_layer.py index f92a05158c..f933d5bf7a 100644 --- a/python/paddle/fluid/tests/unittests/test_conv2d_layer.py +++ b/python/paddle/fluid/tests/unittests/test_conv2d_layer.py @@ -268,6 +268,12 @@ def add_error_cases(suite): suite.addTest( Conv2DErrorTestCase( methodName='runTest', num_channels=5, groups=2)) + suite.addTest( + Conv2DErrorTestCase( + methodName='runTest', num_channels=5, groups=2, stride=0)) + suite.addTest( + Conv2DErrorTestCase( + methodName='runTest', num_channels=5, groups=2, padding=[-1, -1])) def load_tests(loader, standard_tests, pattern): diff --git a/python/paddle/fluid/tests/unittests/test_conv2d_op.py b/python/paddle/fluid/tests/unittests/test_conv2d_op.py index 77eac2fbd7..127469cc0a 100644 --- a/python/paddle/fluid/tests/unittests/test_conv2d_op.py +++ b/python/paddle/fluid/tests/unittests/test_conv2d_op.py @@ -1475,6 +1475,73 @@ class TestConv2DAPI_Error(unittest.TestCase): self.assertRaises(ValueError, run_7) + # ValueError: filter num + def run_8(): + fluid.layers.conv2d( + input=input, + num_filters=0, + filter_size=0, + stride=0, + padding=0, + dilation=0, + groups=1, + use_cudnn=False, + data_format="NCHW") + + self.assertRaises(ValueError, run_8) + + # ValueError: groups + def run_9(): + fluid.layers.conv2d( + input=input, + num_filters=0, + filter_size=0, + stride=0, + padding=0, + dilation=0, + groups=0, + use_cudnn=False, + data_format="NCHW") + + self.assertRaises(ValueError, run_9) + + # ValueError: stride + def run_10(): + fluid.layers.conv2d( + input=input, + num_filters=1, + filter_size=1, + stride=0, + padding=0, + dilation=0, + groups=1, + use_cudnn=False, + data_format="NCHW") + + self.assertRaises(ValueError, run_10) + + def test_api_with_error_input(self): + input = fluid.layers.data( + name="error_input", + shape=[1], + append_batch_size=False, + dtype="float32") + + # ValueError: cudnn + def run_1(): + fluid.layers.conv2d( + input=input, + num_filters=0, + filter_size=0, + stride=0, + padding=0, + dilation=0, + groups=0, + use_cudnn=False, + data_format="NCHW") + + self.assertRaises(ValueError, run_1) + # --------- test environment variable ------ @unittest.skipIf( diff --git a/python/paddle/fluid/tests/unittests/test_conv3d_layer.py b/python/paddle/fluid/tests/unittests/test_conv3d_layer.py index b45e2d1a6a..707991352f 100644 --- a/python/paddle/fluid/tests/unittests/test_conv3d_layer.py +++ b/python/paddle/fluid/tests/unittests/test_conv3d_layer.py @@ -221,6 +221,9 @@ def add_error_cases(suite): suite.addTest( Conv3DErrorTestCase( methodName='runTest', num_channels=5, groups=2)) + suite.addTest( + Conv3DErrorTestCase( + methodName='runTest', num_channels=5, groups=2, padding=[-1, 1, 3])) def load_tests(loader, standard_tests, pattern): diff --git a/python/paddle/fluid/tests/unittests/test_conv3d_op.py b/python/paddle/fluid/tests/unittests/test_conv3d_op.py index 59d1f3216e..5f23d04dde 100644 --- a/python/paddle/fluid/tests/unittests/test_conv3d_op.py +++ b/python/paddle/fluid/tests/unittests/test_conv3d_op.py @@ -984,6 +984,21 @@ class TestConv3DAPI_Error(unittest.TestCase): self.assertRaises(ValueError, run_7) + # ValueError: filter num + def run_8(): + fluid.layers.conv3d( + input=input, + num_filters=0, + filter_size=0, + stride=0, + padding=0, + dilation=0, + groups=1, + use_cudnn=False, + data_format="NDHWC") + + self.assertRaises(ValueError, run_8) + if __name__ == '__main__': unittest.main() diff --git a/python/paddle/fluid/tests/unittests/test_conv3d_transpose_layer.py b/python/paddle/fluid/tests/unittests/test_conv3d_transpose_layer.py index a567ec7273..19249fcfeb 100644 --- a/python/paddle/fluid/tests/unittests/test_conv3d_transpose_layer.py +++ b/python/paddle/fluid/tests/unittests/test_conv3d_transpose_layer.py @@ -238,6 +238,9 @@ def add_error_cases(suite): suite.addTest( Conv3DTransposeErrorTestCase( methodName='runTest', output_size="not_valid")) + suite.addTest( + Conv3DTransposeErrorTestCase( + methodName='runTest', num_channels=5, groups=2, padding=[-1, 1, 3])) def load_tests(loader, standard_tests, pattern): diff --git a/python/paddle/fluid/tests/unittests/test_deformable_conv_op.py b/python/paddle/fluid/tests/unittests/test_deformable_conv_op.py index 80c1088682..13624d189f 100644 --- a/python/paddle/fluid/tests/unittests/test_deformable_conv_op.py +++ b/python/paddle/fluid/tests/unittests/test_deformable_conv_op.py @@ -285,6 +285,19 @@ class TestModulatedDeformableConvInvalidInput(unittest.TestCase): self.assertRaises(TypeError, test_invalid_offset) + def test_invalid_filter(): + paddle.enable_static() + input = fluid.data( + name='input_filter', shape=[None, 3, 32, 32], dtype='float32') + offset = fluid.data( + name='offset_filter', shape=[None, 3, 32, 32], dtype='float32') + mask = fluid.data( + name='mask_filter', shape=[None, 3, 32, 32], dtype='float32') + loss = fluid.layers.deformable_conv( + input, offset, mask, num_filters=4, filter_size=0) + + self.assertRaises(ValueError, test_invalid_filter) + class TestDeformConv2DAPI(unittest.TestCase): def test_api(self): diff --git a/python/paddle/nn/functional/conv.py b/python/paddle/nn/functional/conv.py index 1edbc5f462..67958b8683 100644 --- a/python/paddle/nn/functional/conv.py +++ b/python/paddle/nn/functional/conv.py @@ -85,6 +85,10 @@ def _update_padding_nd(padding, channel_last, num_dims): else: padding_algorithm = "EXPLICIT" padding = utils.convert_to_list(padding, num_dims, 'padding') + if not all([p >= 0 for p in padding]): + raise ValueError( + "Invalid padding, all value should be larger than or equal to 0, but received: {}". + format(padding)) return padding, padding_algorithm -- GitLab