From b9070a42a44ec1a0102e2f0b04523d2e96392903 Mon Sep 17 00:00:00 2001 From: Matthew Jackson Date: Thu, 22 Aug 2019 16:13:27 +0100 Subject: COMPMID-2605: Add asymmetric padding support for Deconvolution layer Change-Id: I63b773bdce25f1342ccd3a08ded623a1508f70fe Signed-off-by: Matthew Jackson Reviewed-on: https://review.mlplatform.org/c/1797 Comments-Addressed: Arm Jenkins Tested-by: Arm Jenkins Reviewed-by: Michele Di Giorgio Reviewed-by: Giuseppe Rossini --- tests/validation/CL/DeconvolutionLayer.cpp | 22 ++++++++-- tests/validation/NEON/DeconvolutionLayer.cpp | 35 ++++++++++----- .../fixtures/DeconvolutionLayerFixture.h | 24 ++++++++++- tests/validation/reference/DeconvolutionLayer.cpp | 50 ++++++++++++++++------ 4 files changed, 101 insertions(+), 30 deletions(-) (limited to 'tests') diff --git a/tests/validation/CL/DeconvolutionLayer.cpp b/tests/validation/CL/DeconvolutionLayer.cpp index 44b3428c52..9dafd1ef89 100644 --- a/tests/validation/CL/DeconvolutionLayer.cpp +++ b/tests/validation/CL/DeconvolutionLayer.cpp @@ -55,6 +55,9 @@ const auto data4x4 = datasets::SmallDeconvolutionShapes() * framework::dataset:: const auto data3x3 = datasets::SmallDeconvolutionShapes() * framework::dataset::make("StrideX", 1, 4) * framework::dataset::make("StrideY", 1, 4) * framework::dataset::make("PadX", 0, 2) * framework::dataset::make("PadY", 0, 2) * framework::dataset::make("NumKernels", { 3 }); +const auto data3x3_asymm = datasets::SmallDeconvolutionShapes() * framework::dataset::make("StrideX", 1, 2) * framework::dataset::make("StrideY", 1, 2) * framework::dataset::make("PadLeft", 0, 1) + * framework::dataset::make("PadRight", 0, 1) * framework::dataset::make("PadTop", 0, 1) * framework::dataset::make("PadBottom", 0, 1) * framework::dataset::make("NumKernels", { 3 }); + const auto data3x3_precommit = datasets::SmallDeconvolutionShapes() * framework::dataset::make("StrideX", 1, 2) * framework::dataset::make("StrideY", 1, 2) * framework::dataset::make("PadX", 0, 2) * framework::dataset::make("PadY", 0, 2) * framework::dataset::make("NumKernels", { 3 }); @@ -120,16 +123,19 @@ DATA_TEST_CASE(Validate, framework::DatasetMode::ALL, zip(zip(zip(zip(zip( // *INDENT-ON* template -using CLDeconvolutionLayerFixture4x4 = DeconvolutionValidationFixture; +using CLDeconvolutionLayerFixture4x4 = DeconvolutionValidationFixture; + +template +using CLDeconvolutionLayerFixture3x3 = DeconvolutionValidationFixture; template -using CLDeconvolutionLayerFixture3x3 = DeconvolutionValidationFixture; +using CLDeconvolutionLayerAsymmFixture3x3 = DeconvolutionValidationAsymmFixture; template -using CLDeconvolutionLayerFixture2x2 = DeconvolutionValidationFixture; +using CLDeconvolutionLayerFixture2x2 = DeconvolutionValidationFixture; template -using CLDeconvolutionLayerFixture1x1 = DeconvolutionValidationFixture; +using CLDeconvolutionLayerFixture1x1 = DeconvolutionValidationFixture; TEST_SUITE(Float) TEST_SUITE(FP32) @@ -153,6 +159,14 @@ FIXTURE_DATA_TEST_CASE(RunSmall, CLDeconvolutionLayerFixture3x3, framewor // Validate output validate(CLAccessor(_target), _reference, tolerance_fp32); } +FIXTURE_DATA_TEST_CASE(RunAsymm, CLDeconvolutionLayerAsymmFixture3x3, framework::DatasetMode::NIGHTLY, combine(combine(combine(data3x3_asymm, framework::dataset::make("DataType", + DataType::F32)), + data_layouts_dataset), + add_bias_dataset)) +{ + // Validate output + validate(CLAccessor(_target), _reference, tolerance_fp32); +} FIXTURE_DATA_TEST_CASE(RunLarge, CLDeconvolutionLayerFixture3x3, framework::DatasetMode::NIGHTLY, combine(combine(combine(data3x3, framework::dataset::make("DataType", DataType::F32)), data_layouts_dataset), add_bias_dataset)) diff --git a/tests/validation/NEON/DeconvolutionLayer.cpp b/tests/validation/NEON/DeconvolutionLayer.cpp index 727f501393..500ef10661 100644 --- a/tests/validation/NEON/DeconvolutionLayer.cpp +++ b/tests/validation/NEON/DeconvolutionLayer.cpp @@ -56,6 +56,9 @@ const auto data4x4 = datasets::SmallDeconvolutionShapes() * framework::dataset:: const auto data3x3 = datasets::SmallDeconvolutionShapes() * framework::dataset::make("StrideX", 1, 4) * framework::dataset::make("StrideY", 1, 4) * framework::dataset::make("PadX", 0, 2) * framework::dataset::make("PadY", 0, 2) * framework::dataset::make("NumKernels", { 3 }); +const auto data3x3_asymm = datasets::SmallDeconvolutionShapes() * framework::dataset::make("StrideX", 1, 2) * framework::dataset::make("StrideY", 1, 2) * framework::dataset::make("PadLeft", 0, 1) + * framework::dataset::make("PadRight", 0, 1) * framework::dataset::make("PadTop", 0, 1) * framework::dataset::make("PadBottom", 0, 1) * framework::dataset::make("NumKernels", { 3 }); + const auto data3x3_precommit = datasets::SmallDeconvolutionShapes() * framework::dataset::make("StrideX", 1, 2) * framework::dataset::make("StrideY", 1, 2) * framework::dataset::make("PadX", 0, 2) * framework::dataset::make("PadY", 0, 2) * framework::dataset::make("NumKernels", { 3 }); @@ -74,13 +77,14 @@ DATA_TEST_CASE(Configuration, framework::DatasetMode::ALL, (combine(datasets::Sm input_shape, data_type) { // Create shapes - const unsigned int kernel_size_x = 3; - const unsigned int kernel_size_y = 3; - const unsigned int num_kernels = 1; - const TensorShape weights_shape(kernel_size_x, kernel_size_y, input_shape.z(), num_kernels); - const TensorShape bias_shape(num_kernels); - auto out_dim = deconvolution_output_dimensions(input_shape.x(), input_shape.y(), kernel_size_x, kernel_size_y, 1, 1, 1, 1); - TensorShape output_shape = compute_deconvolution_output_shape(out_dim, TensorInfo(input_shape, 1, data_type), TensorInfo(weights_shape, 1, data_type)); + const unsigned int kernel_size_x = 3; + const unsigned int kernel_size_y = 3; + const unsigned int num_kernels = 1; + const TensorShape weights_shape(kernel_size_x, kernel_size_y, input_shape.z(), num_kernels); + const TensorShape bias_shape(num_kernels); + const PadStrideInfo info(1, 1, 1, 1); + auto out_dim = deconvolution_output_dimensions(input_shape.x(), input_shape.y(), kernel_size_x, kernel_size_y, info); + TensorShape output_shape = compute_deconvolution_output_shape(out_dim, TensorInfo(input_shape, 1, data_type), TensorInfo(weights_shape, 1, data_type)); // Create tensors Tensor src = create_tensor(input_shape, data_type, 1); @@ -157,13 +161,16 @@ DATA_TEST_CASE(Validate, framework::DatasetMode::ALL, zip(zip(zip(zip(zip( // *INDENT-ON* template -using NEDeconvolutionLayerFixture4x4 = DeconvolutionValidationFixture; +using NEDeconvolutionLayerFixture4x4 = DeconvolutionValidationFixture; + +template +using NEDeconvolutionLayerFixture3x3 = DeconvolutionValidationFixture; template -using NEDeconvolutionLayerFixture3x3 = DeconvolutionValidationFixture; +using NEDeconvolutionLayerAsymmFixture3x3 = DeconvolutionValidationAsymmFixture; template -using NEDeconvolutionLayerFixture1x1 = DeconvolutionValidationFixture; +using NEDeconvolutionLayerFixture1x1 = DeconvolutionValidationFixture; TEST_SUITE(Float) TEST_SUITE(FP32) @@ -185,6 +192,14 @@ FIXTURE_DATA_TEST_CASE(RunSmall, NEDeconvolutionLayerFixture3x3, framewor // Validate output validate(Accessor(_target), _reference, tolerance_fp32); } +FIXTURE_DATA_TEST_CASE(RunAsymm, NEDeconvolutionLayerAsymmFixture3x3, framework::DatasetMode::NIGHTLY, combine(combine(combine(data3x3_asymm, framework::dataset::make("DataType", + DataType::F32)), + data_layouts_dataset), + add_bias_dataset)) +{ + // Validate output + validate(Accessor(_target), _reference, tolerance_fp32); +} FIXTURE_DATA_TEST_CASE(RunLarge, NEDeconvolutionLayerFixture3x3, framework::DatasetMode::NIGHTLY, combine(combine(combine(data3x3, framework::dataset::make("DataType", DataType::F32)), data_layouts_dataset), add_bias_dataset)) diff --git a/tests/validation/fixtures/DeconvolutionLayerFixture.h b/tests/validation/fixtures/DeconvolutionLayerFixture.h index 9f90f07c97..a25a65f997 100644 --- a/tests/validation/fixtures/DeconvolutionLayerFixture.h +++ b/tests/validation/fixtures/DeconvolutionLayerFixture.h @@ -218,7 +218,27 @@ public: const TensorShape weights_shape(kernel_size_x, kernel_size_y, input_shape.z(), num_kernels); const TensorShape bias_shape(num_kernels); const PadStrideInfo info(sx, sy, padx, pady, DimensionRoundingType::CEIL); - auto out_dim = deconvolution_output_dimensions(input_shape.x(), input_shape.y(), kernel_size_x, kernel_size_y, padx, pady, sx, sy); + auto out_dim = deconvolution_output_dimensions(input_shape.x(), input_shape.y(), kernel_size_x, kernel_size_y, info); + TensorInfo input_info(input_shape, 1, data_type); + TensorInfo weights_info(weights_shape, 1, data_type); + TensorShape output_shape = compute_deconvolution_output_shape(out_dim, input_info, weights_info); + DeconvolutionLayerFixtureBase::setup(input_shape, weights_shape, bias_shape, output_shape, info, data_type, data_layout, QuantizationInfo(), add_bias); + } +}; + +template +class DeconvolutionValidationAsymmFixture : public DeconvolutionLayerFixtureBase +{ +public: + template + void setup(TensorShape input_shape, unsigned int sx, unsigned int sy, unsigned int pad_left, unsigned int pad_right, unsigned int pad_top, + unsigned int pad_bottom, unsigned int num_kernels, DataType data_type, DataLayout data_layout, bool add_bias) + { + ARM_COMPUTE_ERROR_ON_MSG(kernel_size_x != kernel_size_y, "Only square kernels supported"); + const TensorShape weights_shape(kernel_size_x, kernel_size_y, input_shape.z(), num_kernels); + const TensorShape bias_shape(num_kernels); + const PadStrideInfo info(sx, sy, pad_left, pad_right, pad_top, pad_bottom, DimensionRoundingType::CEIL); + auto out_dim = deconvolution_output_dimensions(input_shape.x(), input_shape.y(), kernel_size_x, kernel_size_y, info); TensorInfo input_info(input_shape, 1, data_type); TensorInfo weights_info(weights_shape, 1, data_type); TensorShape output_shape = compute_deconvolution_output_shape(out_dim, input_info, weights_info); @@ -238,7 +258,7 @@ public: const TensorShape weights_shape(kernel_size_x, kernel_size_y, input_shape.z(), num_kernels); const TensorShape bias_shape(num_kernels); const PadStrideInfo info(sx, sy, padx, pady, DimensionRoundingType::CEIL); - auto out_dim = deconvolution_output_dimensions(input_shape.x(), input_shape.y(), kernel_size_x, kernel_size_y, padx, pady, sx, sy); + auto out_dim = deconvolution_output_dimensions(input_shape.x(), input_shape.y(), kernel_size_x, kernel_size_y, info); TensorInfo input_info(input_shape, 1, data_type, quantization_info); TensorInfo weights_info(weights_shape, 1, data_type, quantization_info); TensorShape output_shape = compute_deconvolution_output_shape(out_dim, input_info, weights_info); diff --git a/tests/validation/reference/DeconvolutionLayer.cpp b/tests/validation/reference/DeconvolutionLayer.cpp index af59830722..343ea5e725 100644 --- a/tests/validation/reference/DeconvolutionLayer.cpp +++ b/tests/validation/reference/DeconvolutionLayer.cpp @@ -38,21 +38,44 @@ SimpleTensor deconvolution_layer(const SimpleTensor &src, const SimpleTens const PadStrideInfo &info) { // Create reference - const int stride_x = info.stride().first; - const int stride_y = info.stride().second; - const int weights_width = weights.shape().x(); - const int weights_height = weights.shape().y(); - const int weights_upper_dims = weights.shape().total_size() / (weights_width * weights_height); + const unsigned int pad_left = info.pad_left(); + const unsigned int pad_right = info.pad_right(); + const unsigned int pad_top = info.pad_top(); + const unsigned int pad_bottom = info.pad_bottom(); + const int stride_x = info.stride().first; + const int stride_y = info.stride().second; + const int weights_width = weights.shape().x(); + const int weights_height = weights.shape().y(); + const int weights_upper_dims = weights.shape().total_size() / (weights_width * weights_height); + + ARM_COMPUTE_ERROR_ON(pad_left > (weights.shape().x() - 1)); + ARM_COMPUTE_ERROR_ON(pad_right > (weights.shape().x() - 1)); + ARM_COMPUTE_ERROR_ON(pad_top > (weights.shape().y() - 1)); + ARM_COMPUTE_ERROR_ON(pad_bottom > (weights.shape().y() - 1)); // Find the upsampled dimensions unsigned int out_x = (src.shape().x() - 1) * stride_x + 1; unsigned int out_y = (src.shape().y() - 1) * stride_y + 1; // Find the padding needed for the convolution with stride 1 in order to match output shape - unsigned int padx = output_shape.x() - (out_x - weights_width + 1); - unsigned int pady = output_shape.y() - (out_y - weights_height + 1); - out_x += padx; - out_y += pady; + unsigned int deconv_pad_x = output_shape.x() - (out_x - weights_width + 1); + unsigned int deconv_pad_y = output_shape.y() - (out_y - weights_height + 1); + out_x += deconv_pad_x; + out_y += deconv_pad_y; + + unsigned int deconv_pad_left = pad_right > pad_left ? pad_right - pad_left : 0; + unsigned int deconv_pad_right = pad_left > pad_right ? pad_left - pad_right : 0; + deconv_pad_x -= deconv_pad_left + deconv_pad_right; + ARM_COMPUTE_ERROR_ON((deconv_pad_x % 2) != 0); + deconv_pad_left += deconv_pad_x / 2; + deconv_pad_right += deconv_pad_x / 2; + + unsigned int deconv_pad_top = pad_bottom > pad_top ? pad_bottom - pad_top : 0; + unsigned int deconv_pad_bottom = pad_top > pad_bottom ? pad_top - pad_bottom : 0; + deconv_pad_y -= deconv_pad_top + deconv_pad_bottom; + ARM_COMPUTE_ERROR_ON((deconv_pad_y % 2) != 0); + deconv_pad_top += deconv_pad_y / 2; + deconv_pad_bottom += deconv_pad_y / 2; TensorShape scaled_shape = src.shape(); scaled_shape.set(0, out_x); @@ -64,7 +87,6 @@ SimpleTensor deconvolution_layer(const SimpleTensor &src, const SimpleTens const int width_scaled = scaled.shape().x(); const int height_scaled = scaled.shape().y(); const int num_2d_slices = src.shape().total_size() / (width_in * height_in); - ARM_COMPUTE_ERROR_ON(info.pad().first > (weights.shape().x() - 1)); if(src.data_type() == DataType::QASYMM8) { @@ -94,10 +116,10 @@ SimpleTensor deconvolution_layer(const SimpleTensor &src, const SimpleTens { const int offset_slice_in = slice * width_in * height_in; const int offset_slice_out = slice * width_scaled * height_scaled; - const int start_x = padx / 2; - const int start_y = pady / 2; - const int end_y = height_scaled - pady / 2; - const int end_x = width_scaled - padx / 2; + const int start_x = deconv_pad_left; + const int start_y = deconv_pad_top; + const int end_x = width_scaled - deconv_pad_right; + const int end_y = height_scaled - deconv_pad_bottom; for(int yi = start_y, in_y = 0; yi < end_y; yi += stride_y, in_y++) { -- cgit v1.2.1