aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichalis Spyrou <michalis.spyrou@arm.com>2018-10-03 14:18:19 +0100
committerAnthony Barbier <anthony.barbier@arm.com>2018-11-02 16:55:45 +0000
commitafbc5ffb0b567ae93fa2765066bd136d72be88ff (patch)
tree328005d70d5526609a9d84173a317fd1f10b4ed2
parent67d94d29c154a376d12e582421323c1d250da20c (diff)
downloadComputeLibrary-afbc5ffb0b567ae93fa2765066bd136d72be88ff.tar.gz
COMPMID-1621 Deconvolution wrong output calculation
Change-Id: Ida71312bcf6dbd854f2ab1efc65f74910c79e152 Reviewed-on: https://eu-gerrit-1.euhpc.arm.com/151510 Tested-by: bsgcomp <bsgcomp@arm.com> Reviewed-by: Michele DiGiorgio <michele.digiorgio@arm.com>
-rw-r--r--arm_compute/core/CPP/CPPKernels.h1
-rw-r--r--arm_compute/core/CPP/kernels/CPPFlipWeightsKernel.h85
-rw-r--r--arm_compute/core/Utils.h20
-rw-r--r--arm_compute/core/utils/misc/ShapeCalculator.h17
-rw-r--r--arm_compute/graph/nodes/DeconvolutionLayerNode.h4
-rw-r--r--arm_compute/runtime/CL/functions/CLDeconvolutionLayer.h15
-rw-r--r--arm_compute/runtime/NEON/functions/NEDeconvolutionLayer.h15
-rw-r--r--src/core/CPP/kernels/CPPFlipWeightsKernel.cpp106
-rw-r--r--src/core/Utils.cpp15
-rw-r--r--src/graph/nodes/DeconvolutionLayerNode.cpp6
-rw-r--r--src/runtime/CL/functions/CLDeconvolutionLayer.cpp50
-rw-r--r--src/runtime/NEON/functions/NEDeconvolutionLayer.cpp50
-rw-r--r--tests/validation/CL/DeconvolutionLayer.cpp4
-rw-r--r--tests/validation/NEON/DeconvolutionLayer.cpp2
-rw-r--r--tests/validation/fixtures/DeconvolutionLayerFixture.h4
-rw-r--r--tests/validation/reference/DeconvolutionLayer.cpp44
16 files changed, 361 insertions, 77 deletions
diff --git a/arm_compute/core/CPP/CPPKernels.h b/arm_compute/core/CPP/CPPKernels.h
index a0c5707a79..bf24a94aa7 100644
--- a/arm_compute/core/CPP/CPPKernels.h
+++ b/arm_compute/core/CPP/CPPKernels.h
@@ -27,6 +27,7 @@
/* Header regrouping all the CPP kernels */
#include "arm_compute/core/CPP/kernels/CPPCornerCandidatesKernel.h"
#include "arm_compute/core/CPP/kernels/CPPDetectionWindowNonMaximaSuppressionKernel.h"
+#include "arm_compute/core/CPP/kernels/CPPFlipWeightsKernel.h"
#include "arm_compute/core/CPP/kernels/CPPPermuteKernel.h"
#include "arm_compute/core/CPP/kernels/CPPSortEuclideanDistanceKernel.h"
#include "arm_compute/core/CPP/kernels/CPPUpsampleKernel.h"
diff --git a/arm_compute/core/CPP/kernels/CPPFlipWeightsKernel.h b/arm_compute/core/CPP/kernels/CPPFlipWeightsKernel.h
new file mode 100644
index 0000000000..801934159d
--- /dev/null
+++ b/arm_compute/core/CPP/kernels/CPPFlipWeightsKernel.h
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2018 ARM Limited.
+ *
+ * SPDX-License-Identifier: MIT
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#ifndef __ARM_COMPUTE_CPP_FLIP_WEIGHTS_KERNEL_H__
+#define __ARM_COMPUTE_CPP_FLIP_WEIGHTS_KERNEL_H__
+
+#include "arm_compute/core/CPP/ICPPKernel.h"
+
+namespace arm_compute
+{
+class ITensor;
+
+/** CPP kernel to perform 180 degrees flipping on deconvolution weights. */
+class CPPFlipWeightsKernel : public ICPPKernel
+{
+public:
+ const char *name() const override
+ {
+ return "CPPFlipWeightsKernel";
+ }
+ /** Default constructor */
+ CPPFlipWeightsKernel();
+ /** Prevent instances of this class from being copied (As this class contains pointers) */
+ CPPFlipWeightsKernel(const CPPFlipWeightsKernel &) = delete;
+ /** Prevent instances of this class from being copied (As this class contains pointers) */
+ CPPFlipWeightsKernel &operator=(const CPPFlipWeightsKernel &) = delete;
+ /** Allow instances of this class to be moved */
+ CPPFlipWeightsKernel(CPPFlipWeightsKernel &&) = default;
+ /** Allow instances of this class to be moved */
+ CPPFlipWeightsKernel &operator=(CPPFlipWeightsKernel &&) = default;
+ /** Default destructor */
+ ~CPPFlipWeightsKernel() = default;
+
+ /** Set the input and output of the kernel.
+ *
+ * @param[in] input The input tensor to flip. Data types supported: QASYMM8/F16/F32
+ * @param[out] output The output tensor. Data types supported: Same as @p input
+ */
+ void configure(const ITensor *input, ITensor *output);
+
+ // Inherited methods overridden:
+ void run(const Window &window, const ThreadInfo &info) override;
+
+ /** Function to perform flipping.
+ *
+ * @param[in] window_input Input region on which to execute the kernel.
+ * @param[in] window Output region on which to execute the kernel.
+ */
+ template <typename T>
+ void flip_weights(const Window &window_input, const Window &window);
+
+ /** Common signature for all the specialised Flip functions
+ *
+ * @param[in] window_input Input region on which to execute the kernel.
+ * @param[in] window Output region on which to execute the kernel.
+ */
+ using FlipWeightsFunction = void (CPPFlipWeightsKernel::*)(const Window &window_input, const Window &window);
+
+private:
+ const ITensor *_input;
+ ITensor *_output;
+ FlipWeightsFunction _func;
+};
+} // namespace arm_compute
+#endif /*__ARM_COMPUTE_CPP_FLIP_WEIGHTS_KERNEL_H__ */
diff --git a/arm_compute/core/Utils.h b/arm_compute/core/Utils.h
index c742ebc50e..7ee24e2736 100644
--- a/arm_compute/core/Utils.h
+++ b/arm_compute/core/Utils.h
@@ -827,22 +827,20 @@ TensorShape deconvolution_output_shape(const std::pair<unsigned int, unsigned in
/** Returns expected width and height of the deconvolution's output tensor.
*
- * @param[in] in_width Width of input tensor (Number of columns)
- * @param[in] in_height Height of input tensor (Number of rows)
- * @param[in] kernel_width Kernel width.
- * @param[in] kernel_height Kernel height.
- * @param[in] padx X axis padding.
- * @param[in] pady Y axis padding.
- * @param[in] inner_border_right The number of zeros added to right edge of the input.
- * @param[in] inner_border_top The number of zeros added to top edge of the input.
- * @param[in] stride_x X axis input stride.
- * @param[in] stride_y Y axis input stride.
+ * @param[in] in_width Width of input tensor (Number of columns)
+ * @param[in] in_height Height of input tensor (Number of rows)
+ * @param[in] kernel_width Kernel width.
+ * @param[in] kernel_height Kernel height.
+ * @param[in] padx X axis padding.
+ * @param[in] pady Y axis padding.
+ * @param[in] stride_x X axis input stride.
+ * @param[in] stride_y Y axis input stride.
*
* @return A pair with the new width in the first position and the new height in the second.
*/
const std::pair<unsigned int, unsigned int> deconvolution_output_dimensions(unsigned int in_width, unsigned int in_height,
unsigned int kernel_width, unsigned int kernel_height,
- unsigned int padx, unsigned int pady, unsigned int inner_border_right, unsigned int inner_border_top,
+ unsigned int padx, unsigned int pady,
unsigned int stride_x, unsigned int stride_y);
/** Returns expected width and height of output scaled tensor depending on dimensions rounding mode.
diff --git a/arm_compute/core/utils/misc/ShapeCalculator.h b/arm_compute/core/utils/misc/ShapeCalculator.h
index 804ff3c709..f68401c1b9 100644
--- a/arm_compute/core/utils/misc/ShapeCalculator.h
+++ b/arm_compute/core/utils/misc/ShapeCalculator.h
@@ -229,11 +229,20 @@ inline TensorShape compute_depthwise_convolution_shape(const ITensorInfo &input,
return output_shape;
}
-inline TensorShape compute_deconvolution_shape(const ITensorInfo &input, unsigned int sx, unsigned int sy, unsigned int inner_border_right, unsigned int inner_border_top, const PadStrideInfo &info)
+inline TensorShape compute_deconvolution_shape(const ITensorInfo &input, const ITensorInfo &weights, unsigned int sx, unsigned int sy, unsigned int inner_border_right, unsigned int inner_border_top,
+ std::pair<unsigned int, unsigned int> &out_dims)
{
- TensorShape scale_out_shape(input.tensor_shape());
- const unsigned int out_x = input.dimension(0) + (input.dimension(0) - 1) * (sx - 1) + inner_border_right + 2 * info.pad().first;
- const unsigned int out_y = input.dimension(1) + (input.dimension(1) - 1) * (sy - 1) + inner_border_top + 2 * info.pad().second;
+ // Find the upsampled dimensions
+ unsigned int out_x = (input.dimension(0) - 1) * sx + inner_border_right + 1;
+ unsigned int out_y = (input.dimension(1) - 1) * sy + inner_border_top + 1;
+
+ // Find the padding needed for the convolution with stride 1 in order to match output shape
+ unsigned int padx = out_dims.first - (out_x - weights.dimension(0) + 1);
+ unsigned int pady = out_dims.second - (out_y - weights.dimension(1) + 1);
+ out_x += padx;
+ out_y += pady;
+
+ TensorShape scale_out_shape(input.tensor_shape());
scale_out_shape.set(0, out_x);
scale_out_shape.set(1, out_y);
diff --git a/arm_compute/graph/nodes/DeconvolutionLayerNode.h b/arm_compute/graph/nodes/DeconvolutionLayerNode.h
index 73210a299e..19501482c6 100644
--- a/arm_compute/graph/nodes/DeconvolutionLayerNode.h
+++ b/arm_compute/graph/nodes/DeconvolutionLayerNode.h
@@ -55,14 +55,12 @@ public:
* @param[in] input_descriptor Input descriptor
* @param[in] weights_descriptor Weights descriptor
* @param[in] info Convolution operation attributes
- * @param[in] inner_border Inner border (right, top)
*
* @return Output descriptor
*/
static TensorDescriptor compute_output_descriptor(const TensorDescriptor &input_descriptor,
const TensorDescriptor &weights_descriptor,
- const PadStrideInfo &info,
- const Size2D &inner_border);
+ const PadStrideInfo &info);
// Inherited overridden methods:
NodeType type() const override;
diff --git a/arm_compute/runtime/CL/functions/CLDeconvolutionLayer.h b/arm_compute/runtime/CL/functions/CLDeconvolutionLayer.h
index 4dce1e1801..6716cd6fdd 100644
--- a/arm_compute/runtime/CL/functions/CLDeconvolutionLayer.h
+++ b/arm_compute/runtime/CL/functions/CLDeconvolutionLayer.h
@@ -27,6 +27,8 @@
#include "arm_compute/runtime/CL/functions/CLConvolutionLayer.h"
#include "arm_compute/runtime/CL/functions/CLDeconvolutionLayerUpsample.h"
+#include "arm_compute/core/CPP/kernels/CPPFlipWeightsKernel.h"
+
#include "arm_compute/runtime/CL/CLMemoryGroup.h"
#include "arm_compute/runtime/CL/CLTensor.h"
#include "arm_compute/runtime/IFunction.h"
@@ -62,6 +64,14 @@ class CLDeconvolutionLayer : public IFunction
public:
/** Constructor */
CLDeconvolutionLayer(std::shared_ptr<IMemoryManager> memory_manager = nullptr);
+ /** Prevent instances of this class from being copied (As this class contains pointers) */
+ CLDeconvolutionLayer(const CLDeconvolutionLayer &) = delete;
+ /** Default move constructor */
+ CLDeconvolutionLayer(CLDeconvolutionLayer &&) = default;
+ /** Prevent instances of this class from being copied (As this class contains pointers) */
+ CLDeconvolutionLayer &operator=(const CLDeconvolutionLayer &) = delete;
+ /** Default move assignment operator */
+ CLDeconvolutionLayer &operator=(CLDeconvolutionLayer &&) = default;
/** Set the input, weights, biases and output tensors.
*
* @param[in,out] input Input tensor. 3 lower dimensions represent a single input, and an optional 4th dimension for batch of inputs. Data types supported: QASYMM8/F16/F32.
@@ -74,7 +84,7 @@ public:
* @param[in] weights_info (Optional) Weights information needed for @ref CLConvolutionLayer, specifies if the weights tensor has been reshaped with @ref CLWeightsReshapeKernel.
*
*/
- void configure(ICLTensor *input, const ICLTensor *weights, const ICLTensor *bias, ICLTensor *output, const PadStrideInfo &info,
+ void configure(ICLTensor *input, ICLTensor *weights, const ICLTensor *bias, ICLTensor *output, const PadStrideInfo &info,
unsigned int inner_border_right, unsigned int inner_border_top, const WeightsInfo &weights_info = WeightsInfo());
/** Static function to check if given info will lead to a valid configuration of @ref CLDeconvolutionLayer
*
@@ -100,7 +110,10 @@ private:
CLMemoryGroup _memory_group;
CLDeconvolutionLayerUpsample _scale_f;
CLConvolutionLayer _conv_f;
+ CPPFlipWeightsKernel _flip_weights;
CLTensor _scaled_output;
+ ICLTensor *_weights;
+ CLTensor _weights_flipped;
bool _is_prepared;
};
}
diff --git a/arm_compute/runtime/NEON/functions/NEDeconvolutionLayer.h b/arm_compute/runtime/NEON/functions/NEDeconvolutionLayer.h
index 3e527168c1..0cca555621 100644
--- a/arm_compute/runtime/NEON/functions/NEDeconvolutionLayer.h
+++ b/arm_compute/runtime/NEON/functions/NEDeconvolutionLayer.h
@@ -28,6 +28,7 @@
#include "arm_compute/runtime/NEON/functions/NEConvolutionLayer.h"
#include "arm_compute/runtime/NEON/functions/NEDirectConvolutionLayer.h"
+#include "arm_compute/core/CPP/kernels/CPPFlipWeightsKernel.h"
#include "arm_compute/core/Types.h"
#include "arm_compute/runtime/IFunction.h"
#include "arm_compute/runtime/IMemoryManager.h"
@@ -111,12 +112,14 @@ public:
void prepare() override;
private:
- MemoryGroup _memory_group;
- NEConvolutionLayer _conv_f;
- CPPUpsample _upsample_f;
- Tensor _scaled_output;
- ITensor *_input;
- PadStrideInfo _info;
+ MemoryGroup _memory_group;
+ NEConvolutionLayer _conv_f;
+ CPPUpsample _upsample_f;
+ CPPFlipWeightsKernel _flip_weights;
+ Tensor _scaled_output;
+ Tensor _weights_flipped;
+ ITensor *_input;
+ PadStrideInfo _info;
std::pair<unsigned int, unsigned int> _inner_border;
bool _is_prepared;
};
diff --git a/src/core/CPP/kernels/CPPFlipWeightsKernel.cpp b/src/core/CPP/kernels/CPPFlipWeightsKernel.cpp
new file mode 100644
index 0000000000..741218e4f7
--- /dev/null
+++ b/src/core/CPP/kernels/CPPFlipWeightsKernel.cpp
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 2018 ARM Limited.
+ *
+ * SPDX-License-Identifier: MIT
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#include "arm_compute/core/CPP/kernels/CPPFlipWeightsKernel.h"
+
+#include "arm_compute/core/Error.h"
+#include "arm_compute/core/Helpers.h"
+#include "arm_compute/core/ITensor.h"
+#include "arm_compute/core/TensorInfo.h"
+#include "arm_compute/core/Types.h"
+#include "arm_compute/core/Validate.h"
+#include "arm_compute/core/utils/misc/ShapeCalculator.h"
+
+#include <cstddef>
+#include <cstdint>
+
+using namespace arm_compute;
+
+CPPFlipWeightsKernel::CPPFlipWeightsKernel()
+ : _input(nullptr), _output(nullptr), _func(nullptr)
+{
+}
+
+template <typename T>
+void CPPFlipWeightsKernel::flip_weights(const Window &window_input, const Window &window)
+{
+ // Create iterators
+ Iterator in(_input, window_input);
+
+ Iterator out(_output, window);
+
+ const int kernel_size = _input->info()->dimension(0);
+
+ execute_window_loop(window_input, [&](const Coordinates & id)
+ {
+ *((reinterpret_cast<T *>(out.ptr()) + kernel_size * (kernel_size - id.y() - 1) + (kernel_size - id.x() - 1))) = *(reinterpret_cast<const T *>(in.ptr()));
+ },
+ in, out);
+}
+
+void CPPFlipWeightsKernel::configure(const ITensor *input, ITensor *output)
+{
+ ARM_COMPUTE_ERROR_ON_NULLPTR(input, output);
+
+ _input = input;
+ _output = output;
+
+ // Configure kernel window
+ Window win = calculate_max_window(*input->info(), Steps());
+
+ // The CPPFlipWeightsKernel doesn't need padding so update_window_and_padding() can be skipped
+ Coordinates coord;
+ coord.set_num_dimensions(output->info()->num_dimensions());
+ output->info()->set_valid_region(ValidRegion(coord, output->info()->tensor_shape()));
+
+ ICPPKernel::configure(win);
+
+ switch(input->info()->data_type())
+ {
+ case DataType::F32:
+ _func = &CPPFlipWeightsKernel::flip_weights<float>;
+ break;
+ case DataType::F16:
+ _func = &CPPFlipWeightsKernel::flip_weights<half>;
+ break;
+ case DataType::QASYMM8:
+ _func = &CPPFlipWeightsKernel::flip_weights<uint8_t>;
+ break;
+ default:
+ ARM_COMPUTE_ERROR("Not supported");
+ }
+}
+
+void CPPFlipWeightsKernel::run(const Window &window, const ThreadInfo &info)
+{
+ ARM_COMPUTE_UNUSED(info);
+ ARM_COMPUTE_ERROR_ON_UNCONFIGURED_KERNEL(this);
+ ARM_COMPUTE_ERROR_ON_INVALID_SUBWINDOW(ICPPKernel::window(), window);
+ ARM_COMPUTE_ERROR_ON(_func == nullptr);
+
+ Window out_window{ window };
+ out_window.set(Window::DimX, Window::Dimension(0, 0, 0));
+ out_window.set(Window::DimY, Window::Dimension(0, 0, 0));
+
+ (this->*_func)(window, out_window);
+}
diff --git a/src/core/Utils.cpp b/src/core/Utils.cpp
index 229579d8d9..a6a5771ec1 100644
--- a/src/core/Utils.cpp
+++ b/src/core/Utils.cpp
@@ -334,17 +334,14 @@ TensorShape arm_compute::deconvolution_output_shape(const std::pair<unsigned int
const std::pair<unsigned int, unsigned int> arm_compute::deconvolution_output_dimensions(
unsigned int in_width, unsigned int in_height, unsigned int kernel_width, unsigned int kernel_height, unsigned int padx, unsigned int pady,
- unsigned int inner_border_right, unsigned int inner_border_top, unsigned int stride_x, unsigned int stride_y)
+ unsigned int stride_x, unsigned int stride_y)
{
ARM_COMPUTE_ERROR_ON(in_width < 1 || in_height < 1);
- ARM_COMPUTE_ERROR_ON(((in_width - 1) * stride_x + kernel_width + inner_border_right) < 2 * padx);
- ARM_COMPUTE_ERROR_ON(((in_height - 1) * stride_y + kernel_height + inner_border_top) < 2 * pady);
- const int padx_deconv = (kernel_width - padx - 1);
- const int pady_deconv = (kernel_height - pady - 1);
- ARM_COMPUTE_ERROR_ON(padx_deconv < 0);
- ARM_COMPUTE_ERROR_ON(pady_deconv < 0);
- const int w = stride_x * (in_width - 1) + kernel_width + inner_border_right - 2 * padx_deconv;
- const int h = stride_y * (in_height - 1) + kernel_height + inner_border_top - 2 * pady_deconv;
+ ARM_COMPUTE_ERROR_ON(((in_width - 1) * stride_x + kernel_width) < 2 * padx);
+ ARM_COMPUTE_ERROR_ON(((in_height - 1) * stride_y + kernel_height) < 2 * pady);
+ const int w = stride_x * (in_width - 1) + kernel_width - 2 * padx;
+ const int h = stride_y * (in_height - 1) + kernel_height - 2 * pady;
+
return std::make_pair<unsigned int, unsigned int>(w, h);
}
diff --git a/src/graph/nodes/DeconvolutionLayerNode.cpp b/src/graph/nodes/DeconvolutionLayerNode.cpp
index 9329ae3c23..e7ccffd04f 100644
--- a/src/graph/nodes/DeconvolutionLayerNode.cpp
+++ b/src/graph/nodes/DeconvolutionLayerNode.cpp
@@ -51,8 +51,7 @@ Size2D DeconvolutionLayerNode::inner_border() const
TensorDescriptor DeconvolutionLayerNode::compute_output_descriptor(const TensorDescriptor &input_descriptor,
const TensorDescriptor &weights_descriptor,
- const PadStrideInfo &info,
- const Size2D &inner_border)
+ const PadStrideInfo &info)
{
unsigned int output_width = 0;
unsigned int output_height = 0;
@@ -65,7 +64,6 @@ TensorDescriptor DeconvolutionLayerNode::compute_output_descriptor(const TensorD
std::tie(output_width, output_height) = deconvolution_output_dimensions(input_width, input_height,
kernel_width, kernel_height,
info.pad().first, info.pad().second,
- inner_border.x(), inner_border.y(),
info.stride().first, info.stride().second);
TensorDescriptor output_descriptor = input_descriptor;
@@ -96,7 +94,7 @@ TensorDescriptor DeconvolutionLayerNode::configure_output(size_t idx) const
ARM_COMPUTE_ERROR_ON(src == nullptr || weights == nullptr);
- TensorDescriptor output_info = compute_output_descriptor(src->desc(), weights->desc(), _info, _inner_border);
+ TensorDescriptor output_info = compute_output_descriptor(src->desc(), weights->desc(), _info);
return output_info;
}
diff --git a/src/runtime/CL/functions/CLDeconvolutionLayer.cpp b/src/runtime/CL/functions/CLDeconvolutionLayer.cpp
index 3f5b8c92dd..26d44e9c96 100644
--- a/src/runtime/CL/functions/CLDeconvolutionLayer.cpp
+++ b/src/runtime/CL/functions/CLDeconvolutionLayer.cpp
@@ -27,6 +27,8 @@
#include "arm_compute/core/Utils.h"
#include "arm_compute/core/Validate.h"
#include "arm_compute/core/utils/misc/ShapeCalculator.h"
+#include "arm_compute/runtime/CL/CLScheduler.h"
+#include "arm_compute/runtime/CPP/CPPScheduler.h"
#include <memory>
#include <tuple>
@@ -38,7 +40,10 @@ CLDeconvolutionLayer::CLDeconvolutionLayer(std::shared_ptr<IMemoryManager> memor
: _memory_group(std::move(memory_manager)),
_scale_f(),
_conv_f(),
+ _flip_weights(),
_scaled_output(),
+ _weights(),
+ _weights_flipped(),
_is_prepared(false)
{
}
@@ -59,7 +64,7 @@ Status CLDeconvolutionLayer::validate(const ITensorInfo *input, const ITensorInf
ARM_COMPUTE_RETURN_ERROR_ON_MSG(inner_border_top > stride_y - 1, "inner_border_top must be smaller than stride_y");
auto out_dims = deconvolution_output_dimensions(input->dimension(0), input->dimension(1), weights->dimension(0), weights->dimension(1),
- info.pad().first, info.pad().second, inner_border_right, inner_border_top, stride_x, stride_y);
+ info.pad().first, info.pad().second, stride_x, stride_y);
const TensorShape output_shape = deconvolution_output_shape(out_dims, input->tensor_shape(), weights->tensor_shape());
@@ -81,8 +86,9 @@ Status CLDeconvolutionLayer::validate(const ITensorInfo *input, const ITensorInf
ARM_COMPUTE_RETURN_ERROR_ON_MSG(output->dimension(Window::DimY) != output_shape.y(), "Output's height is invalid.");
ARM_COMPUTE_RETURN_ERROR_ON_MSG(output->dimension(Window::DimZ) != output_shape.z(), "Output's depth is invalid.");
- TensorInfo scale_out_info(input->clone()->set_is_resizable(true).reset_padding().set_tensor_shape(compute_deconvolution_shape(*input, stride_x, stride_y, inner_border_right, inner_border_top,
- info)));
+ TensorInfo scale_out_info(input->clone()->set_is_resizable(true).reset_padding().set_tensor_shape(compute_deconvolution_shape(*input, *weights, stride_x, stride_y, inner_border_right,
+ inner_border_top,
+ out_dims)));
const PadStrideInfo conv_info(1, 1, 0, 0, 0, 0, DimensionRoundingType::CEIL);
ARM_COMPUTE_RETURN_ON_ERROR(CLDeconvolutionLayerUpsample::validate(input, &scale_out_info, BorderSize(inner_border_right, inner_border_top), info));
@@ -91,7 +97,7 @@ Status CLDeconvolutionLayer::validate(const ITensorInfo *input, const ITensorInf
return Status{};
}
-void CLDeconvolutionLayer::configure(ICLTensor *input, const ICLTensor *weights, const ICLTensor *bias, ICLTensor *output, const PadStrideInfo &info,
+void CLDeconvolutionLayer::configure(ICLTensor *input, ICLTensor *weights, const ICLTensor *bias, ICLTensor *output, const PadStrideInfo &info,
unsigned int inner_border_right, unsigned int inner_border_top, const WeightsInfo &weights_info)
{
ARM_COMPUTE_ERROR_ON_NULLPTR(input, weights, output);
@@ -99,8 +105,12 @@ void CLDeconvolutionLayer::configure(ICLTensor *input, const ICLTensor *weights,
const unsigned int stride_x = info.stride().first;
const unsigned int stride_y = info.stride().second;
+ _weights = weights;
+ _weights_flipped.allocator()->init(TensorInfo(weights->info()->tensor_shape(), 1, weights->info()->data_type()));
+ _flip_weights.configure(weights, &_weights_flipped);
+
auto out_dims = deconvolution_output_dimensions(input->info()->dimension(0), input->info()->dimension(1), weights->info()->dimension(0), weights->info()->dimension(1),
- info.pad().first, info.pad().second, inner_border_right, inner_border_top, stride_x, stride_y);
+ info.pad().first, info.pad().second, stride_x, stride_y);
const TensorShape output_shape = deconvolution_output_shape(out_dims, input->info()->tensor_shape(), weights->info()->tensor_shape());
@@ -113,22 +123,31 @@ void CLDeconvolutionLayer::configure(ICLTensor *input, const ICLTensor *weights,
_is_prepared = false;
_memory_group.manage(&_scaled_output);
+ _memory_group.manage(&_weights_flipped);
- // configure scale function
- // Init and allocate intermediate tensor for output, same size as input but the first two axis are the same as the output tensor
- TensorShape scale_out_shape(input->info()->tensor_shape());
- const unsigned int out_x = input->info()->dimension(0) + (input->info()->dimension(0) - 1) * (stride_x - 1) + inner_border_right + 2 * info.pad().first;
- const unsigned int out_y = input->info()->dimension(1) + (input->info()->dimension(1) - 1) * (stride_y - 1) + inner_border_top + 2 * info.pad().second;
+ // Find the upsampled dimensions
+ unsigned int out_x = (input->info()->dimension(0) - 1) * stride_x + inner_border_right + 1;
+ unsigned int out_y = (input->info()->dimension(1) - 1) * stride_y + inner_border_top + 1;
+
+ // Find the padding needed for the convolution with stride 1 in order to match output shape
+ unsigned int padx = out_dims.first - (out_x - weights->info()->dimension(0) + 1);
+ unsigned int pady = out_dims.second - (out_y - weights->info()->dimension(1) + 1);
+ out_x += padx;
+ out_y += pady;
+
+ TensorShape scale_out_shape(input->info()->tensor_shape());
scale_out_shape.set(0, out_x);
scale_out_shape.set(1, out_y);
TensorInfo scale_out_info(scale_out_shape, 1, input->info()->data_type(), input->info()->quantization_info());
_scaled_output.allocator()->init(scale_out_info);
- _scale_f.configure(input, &_scaled_output, BorderSize(inner_border_top, inner_border_right), info);
+ // configure scale function
+ const PadStrideInfo upsample_info(stride_x, stride_y, padx / 2, pady / 2);
+ _scale_f.configure(input, &_scaled_output, BorderSize(inner_border_top, inner_border_right), upsample_info);
// setup the function to convolve the upscaled output
const PadStrideInfo conv_info(1, 1, 0, 0, 0, 0, DimensionRoundingType::CEIL);
- _conv_f.configure(&_scaled_output, weights, bias, output, conv_info, weights_info);
+ _conv_f.configure(&_scaled_output, &_weights_flipped, bias, output, conv_info, weights_info);
_scaled_output.allocator()->allocate();
}
@@ -148,7 +167,14 @@ void CLDeconvolutionLayer::prepare()
{
if(!_is_prepared)
{
+ _weights_flipped.allocator()->allocate();
+ _weights_flipped.map(true);
+ _weights->map(CLScheduler::get().queue(), true);
+ CPPScheduler::get().schedule(&_flip_weights, Window::DimZ);
+ _weights_flipped.unmap();
+ _weights->unmap(CLScheduler::get().queue());
_conv_f.prepare();
+
_is_prepared = true;
}
}
diff --git a/src/runtime/NEON/functions/NEDeconvolutionLayer.cpp b/src/runtime/NEON/functions/NEDeconvolutionLayer.cpp
index fda9f57499..6ca60c66a4 100644
--- a/src/runtime/NEON/functions/NEDeconvolutionLayer.cpp
+++ b/src/runtime/NEON/functions/NEDeconvolutionLayer.cpp
@@ -27,6 +27,7 @@
#include "arm_compute/core/Utils.h"
#include "arm_compute/core/Validate.h"
#include "arm_compute/core/utils/misc/ShapeCalculator.h"
+#include "arm_compute/runtime/CPP/CPPScheduler.h"
using namespace arm_compute;
using namespace arm_compute::misc::shape_calculator;
@@ -35,7 +36,9 @@ NEDeconvolutionLayer::NEDeconvolutionLayer(std::shared_ptr<IMemoryManager> memor
: _memory_group(std::move(memory_manager)),
_conv_f(),
_upsample_f(),
+ _flip_weights(),
_scaled_output(),
+ _weights_flipped(),
_input(nullptr),
_info(),
_inner_border(),
@@ -60,7 +63,7 @@ Status NEDeconvolutionLayer::validate(const ITensorInfo *input, const ITensorInf
ARM_COMPUTE_RETURN_ERROR_ON_MSG(inner_border_top > stride_y - 1, "inner_border_top must be smaller than stride_y");
auto out_dims = deconvolution_output_dimensions(input->dimension(0), input->dimension(1), weights->dimension(0), weights->dimension(1),
- info.pad().first, info.pad().second, inner_border_right, inner_border_top, stride_x, stride_y);
+ info.pad().first, info.pad().second, stride_x, stride_y);
ARM_COMPUTE_RETURN_ERROR_ON_MISMATCHING_DATA_TYPES(input, weights, bias);
@@ -74,14 +77,14 @@ Status NEDeconvolutionLayer::validate(const ITensorInfo *input, const ITensorInf
ARM_COMPUTE_RETURN_ERROR_ON_MISMATCHING_DATA_TYPES(input, output);
const TensorShape output_shape = deconvolution_output_shape(out_dims, input->tensor_shape(), weights->tensor_shape());
-
ARM_COMPUTE_RETURN_ERROR_ON_MSG(output->dimension(Window::DimX) != output_shape.x(), "Output's width is invalid.");
ARM_COMPUTE_RETURN_ERROR_ON_MSG(output->dimension(Window::DimY) != output_shape.y(), "Output's height is invalid.");
ARM_COMPUTE_RETURN_ERROR_ON_MSG(output->dimension(Window::DimZ) != output_shape.z(), "Output's depth is invalid.");
}
- TensorInfo scale_out_info(input->clone()->set_is_resizable(true).reset_padding().set_tensor_shape(compute_deconvolution_shape(*input, stride_x, stride_y, inner_border_right, inner_border_top,
- info)));
+ TensorInfo scale_out_info(input->clone()->set_is_resizable(true).reset_padding().set_tensor_shape(compute_deconvolution_shape(*input, *weights, stride_x, stride_y, inner_border_right,
+ inner_border_top,
+ out_dims)));
const PadStrideInfo conv_info(1, 1, 0, 0, 0, 0, DimensionRoundingType::CEIL);
for(size_t i = 2; i < Coordinates::num_max_dimensions; ++i)
@@ -107,25 +110,44 @@ void NEDeconvolutionLayer::configure(ITensor *input, const ITensor *weights, con
const unsigned int stride_x = info.stride().first;
const unsigned int stride_y = info.stride().second;
+ _weights_flipped.allocator()->init(TensorInfo(weights->info()->tensor_shape(), 1, weights->info()->data_type()));
+ _flip_weights.configure(weights, &_weights_flipped);
+
+ auto out_dims = deconvolution_output_dimensions(input->info()->dimension(0), input->info()->dimension(1), weights->info()->dimension(0), weights->info()->dimension(1),
+ info.pad().first, info.pad().second, stride_x, stride_y);
+
+ const TensorShape output_shape = deconvolution_output_shape(out_dims, input->info()->tensor_shape(), weights->info()->tensor_shape());
+ // Output auto initialization if not yet initialized
+ auto_init_if_empty(*output->info(), output_shape, 1, input->info()->data_type(), input->info()->quantization_info());
+
// Perform validation step
ARM_COMPUTE_ERROR_THROW_ON(NEDeconvolutionLayer::validate(input->info(), weights->info(), bias == nullptr ? nullptr : bias->info(), output->info(), info, inner_border_right, inner_border_top));
_memory_group.manage(&_scaled_output);
- // configure scale function
- // Init and allocate intermmidiate tensor for output, same size as input but the first two axis are the same as the output tensor
- const TensorInfo scale_out_info(compute_deconvolution_shape(*input->info(), stride_x, stride_y, inner_border_right, inner_border_top, info), 1, input->info()->data_type());
+ // Find the upsampled dimensions
+ unsigned int out_x = (input->info()->dimension(0) - 1) * stride_x + inner_border_right + 1;
+ unsigned int out_y = (input->info()->dimension(1) - 1) * stride_y + inner_border_top + 1;
+
+ // Find the padding needed for the convolution with stride 1 in order to match output shape
+ unsigned int padx = out_dims.first - (out_x - weights->info()->dimension(0) + 1);
+ unsigned int pady = out_dims.second - (out_y - weights->info()->dimension(1) + 1);
+ out_x += padx;
+ out_y += pady;
+
+ TensorShape scale_out_shape(input->info()->tensor_shape());
+ scale_out_shape.set(0, out_x);
+ scale_out_shape.set(1, out_y);
+ TensorInfo scale_out_info(scale_out_shape, 1, input->info()->data_type(), input->info()->quantization_info());
_scaled_output.allocator()->init(scale_out_info);
+ const PadStrideInfo upsample_info(stride_x, stride_y, padx / 2, pady / 2);
+ _upsample_f.configure(input, &_scaled_output, upsample_info, inner_border_right, inner_border_top);
+
// setup the function to convolve the upscaled output
const PadStrideInfo conv_info(1, 1, 0, 0, 0, 0, DimensionRoundingType::CEIL);
- _conv_f.configure(&_scaled_output, weights, bias, output, conv_info);
-
- // Allocate auxiliary tensors
+ _conv_f.configure(&_scaled_output, &_weights_flipped, bias, output, conv_info);
_scaled_output.allocator()->allocate();
-
- // configure upsample function
- _upsample_f.configure(input, &_scaled_output, info, inner_border_right, inner_border_top);
}
void NEDeconvolutionLayer::run()
@@ -144,6 +166,8 @@ void NEDeconvolutionLayer::prepare()
{
if(!_is_prepared)
{
+ _weights_flipped.allocator()->allocate();
+ CPPScheduler::get().schedule(&_flip_weights, Window::DimZ);
_conv_f.prepare();
_is_prepared = true;
}
diff --git a/tests/validation/CL/DeconvolutionLayer.cpp b/tests/validation/CL/DeconvolutionLayer.cpp
index 2aa7cfe7c1..84a2b01797 100644
--- a/tests/validation/CL/DeconvolutionLayer.cpp
+++ b/tests/validation/CL/DeconvolutionLayer.cpp
@@ -48,7 +48,7 @@ RelativeTolerance<half_float::half> tolerance_f16(half_float::half(0.2)); /**< T
constexpr AbsoluteTolerance<float> tolerance_qasymm8(1.0); /**< Tolerance value for comparing reference's output against implementation's output for quantized data types */
constexpr float tolerance_num = 0.07f; /**< Tolerance number */
-const auto data4x4 = datasets::SmallDeconvolutionShapes() * framework::dataset::make("StrideX", 1, 4) * framework::dataset::make("StrideY", 1, 4) * framework::dataset::make("PadX", 0, 3)
+const auto data4x4 = datasets::SmallDeconvolutionShapes() * framework::dataset::make("StrideX", 1, 5) * framework::dataset::make("StrideY", 1, 5) * framework::dataset::make("PadX", 0, 3)
* framework::dataset::make("PadY", 0, 3) * framework::dataset::make("ax", 0) * framework::dataset::make("ay", 0) * framework::dataset::make("NumKernels", { 1, 3 });
const auto data3x3 = datasets::SmallDeconvolutionShapes() * framework::dataset::make("StrideX", 1, 4) * framework::dataset::make("StrideY", 1, 4) * framework::dataset::make("PadX", 0, 2)
@@ -71,7 +71,7 @@ DATA_TEST_CASE(Configuration, framework::DatasetMode::ALL, (combine(datasets::Sm
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, 0, 0, 1, 1);
+ 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 = deconvolution_output_shape(out_dim, input_shape, weights_shape);
// Create tensors
diff --git a/tests/validation/NEON/DeconvolutionLayer.cpp b/tests/validation/NEON/DeconvolutionLayer.cpp
index 277953badb..eb643b8e7c 100644
--- a/tests/validation/NEON/DeconvolutionLayer.cpp
+++ b/tests/validation/NEON/DeconvolutionLayer.cpp
@@ -67,7 +67,7 @@ DATA_TEST_CASE(Configuration, framework::DatasetMode::ALL, (combine(datasets::Sm
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, 0, 0, 1, 1);
+ 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 = deconvolution_output_shape(out_dim, input_shape, weights_shape);
// Create tensors
diff --git a/tests/validation/fixtures/DeconvolutionLayerFixture.h b/tests/validation/fixtures/DeconvolutionLayerFixture.h
index 6a8b4f220c..d3a7be74b0 100644
--- a/tests/validation/fixtures/DeconvolutionLayerFixture.h
+++ b/tests/validation/fixtures/DeconvolutionLayerFixture.h
@@ -160,7 +160,7 @@ public:
const TensorShape bias_shape(num_kernels);
const PadStrideInfo info(sx, sy, padx, pady, DimensionRoundingType::CEIL);
const std::pair<unsigned int, unsigned int> inner_border(inner_border_right, inner_border_top);
- auto out_dim = deconvolution_output_dimensions(input_shape.x(), input_shape.y(), kernel_size_x, kernel_size_y, padx, pady, inner_border.first, inner_border.second, sx, sy);
+ auto out_dim = deconvolution_output_dimensions(input_shape.x(), input_shape.y(), kernel_size_x, kernel_size_y, padx, pady, sx, sy);
TensorShape output_shape = deconvolution_output_shape(out_dim, input_shape, weights_shape);
DeconvolutionLayerFixtureBase<TensorType, AccessorType, FunctionType, T>::setup(input_shape, weights_shape, bias_shape, output_shape, info, inner_border, data_type, QuantizationInfo());
}
@@ -179,7 +179,7 @@ public:
const TensorShape bias_shape(num_kernels);
const PadStrideInfo info(sx, sy, padx, pady, DimensionRoundingType::CEIL);
const std::pair<unsigned int, unsigned int> inner_border(inner_border_right, inner_border_top);
- auto out_dim = deconvolution_output_dimensions(input_shape.x(), input_shape.y(), kernel_size_x, kernel_size_y, padx, pady, inner_border.first, inner_border.second, sx, sy);
+ auto out_dim = deconvolution_output_dimensions(input_shape.x(), input_shape.y(), kernel_size_x, kernel_size_y, padx, pady, sx, sy);
TensorShape output_shape = deconvolution_output_shape(out_dim, input_shape, weights_shape);
DeconvolutionLayerFixtureBase<TensorType, AccessorType, FunctionType, T>::setup(input_shape, weights_shape, bias_shape, output_shape, info, inner_border, data_type, quantization_info);
}
diff --git a/tests/validation/reference/DeconvolutionLayer.cpp b/tests/validation/reference/DeconvolutionLayer.cpp
index ba28b46d3a..5ca3b44baa 100644
--- a/tests/validation/reference/DeconvolutionLayer.cpp
+++ b/tests/validation/reference/DeconvolutionLayer.cpp
@@ -38,11 +38,23 @@ SimpleTensor<T> deconvolution_layer(const SimpleTensor<T> &src, const SimpleTens
const PadStrideInfo &info, const std::pair<unsigned int, unsigned int> &a)
{
// Create reference
- const int stride_x = info.stride().first;
- const int stride_y = info.stride().second;
+ 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);
+
+ // Find the upsampled dimensions
+ unsigned int out_x = (src.shape().x() - 1) * stride_x + a.first + 1;
+ unsigned int out_y = (src.shape().y() - 1) * stride_y + a.second + 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;
+
TensorShape scaled_shape = src.shape();
- int out_x = src.shape().x() + (src.shape().x() - 1) * (stride_x - 1) + a.first + 2 * info.pad().first;
- int out_y = src.shape().y() + (src.shape().y() - 1) * (stride_y - 1) + a.second + 2 * info.pad().second;
scaled_shape.set(0, out_x);
scaled_shape.set(1, out_y);
SimpleTensor<T> scaled{ scaled_shape, src.data_type(), 1, src.quantization_info() };
@@ -69,14 +81,28 @@ SimpleTensor<T> deconvolution_layer(const SimpleTensor<T> &src, const SimpleTens
std::fill_n(scaled.data(), scaled.num_elements(), T(0));
}
+ // Flip weights by 180 degrees
+ SimpleTensor<T> weights_flipped{ weights.shape(), weights.data_type(), 1, weights.quantization_info() };
+ for(int ud = 0; ud < weights_upper_dims; ++ud)
+ {
+ const int offset = ud * weights_width * weights_height;
+ for(int y = 0; y < weights_height; ++y)
+ {
+ for(int x = 0; x < weights_width; ++x)
+ {
+ weights_flipped[offset + (weights_height - 1 - y) * weights_width + (weights_width - 1 - x)] = weights[offset + y * weights_width + x];
+ }
+ }
+ }
+
for(int slice = 0; slice < num_2d_slices; ++slice)
{
const int offset_slice_in = slice * width_in * height_in;
const int offset_slice_out = slice * width_scaled * height_scaled;
- const int start_x = info.pad().first;
- const int start_y = ay + info.pad().second;
- const int end_y = height_scaled - info.pad().second;
- const int end_x = width_scaled - ax - info.pad().first;
+ const int start_x = padx / 2;
+ const int start_y = ay + pady / 2;
+ const int end_y = height_scaled - pady / 2;
+ const int end_x = width_scaled - ax - padx / 2;
for(int yi = start_y, in_y = 0; yi < end_y; yi += stride_y, in_y++)
{
@@ -90,7 +116,7 @@ SimpleTensor<T> deconvolution_layer(const SimpleTensor<T> &src, const SimpleTens
}
const PadStrideInfo conv_info(1, 1, 0, 0, 0, 0, DimensionRoundingType::CEIL);
- return convolution_layer(scaled, weights, bias, output_shape, conv_info);
+ return convolution_layer(scaled, weights_flipped, bias, output_shape, conv_info);
}
template SimpleTensor<uint8_t> deconvolution_layer(const SimpleTensor<uint8_t> &src, const SimpleTensor<uint8_t> &weights, const SimpleTensor<int32_t> &bias, const TensorShape &output_shape,