From 37722d9a81627520fa347eb65199dbfeb84b26bd Mon Sep 17 00:00:00 2001 From: morgolock Date: Thu, 9 Apr 2020 14:17:48 +0100 Subject: COMPMID-2449: Implement NEUnPoolLayer Change-Id: I5677c87bba97dd395a3e13dbce34a3dd2c437033 Signed-off-by: morgolock Reviewed-on: https://review.mlplatform.org/c/ml/ComputeLibrary/+/3289 Tested-by: Arm Jenkins Reviewed-by: Georgios Pinitas Comments-Addressed: Arm Jenkins --- Android.bp | 2 + arm_compute/core/NEON/NEKernels.h | 1 + .../core/NEON/kernels/NEMaxUnpoolingLayerKernel.h | 97 +++++++++++++ arm_compute/core/utils/misc/ShapeCalculator.h | 31 ++++ arm_compute/runtime/NEON/NEFunctions.h | 1 + .../runtime/NEON/functions/NEMaxUnpoolingLayer.h | 77 ++++++++++ .../NEON/kernels/NEMaxUnpoolingLayerKernel.cpp | 146 +++++++++++++++++++ src/core/NEON/kernels/NEPoolingLayerKernel.cpp | 68 ++++++--- src/runtime/NEON/functions/NEMaxUnpoolingLayer.cpp | 54 +++++++ src/runtime/NEON/functions/NEPoolingLayer.cpp | 4 +- tests/validation/NEON/MaxUnpoolingLayer.cpp | 82 +++++++++++ .../validation/fixtures/MaxUnpoolingLayerFixture.h | 159 +++++++++++++++++++++ tests/validation/fixtures/PoolingLayerFixture.h | 1 - tests/validation/reference/MaxUnpoolingLayer.cpp | 106 ++++++++++++++ tests/validation/reference/MaxUnpoolingLayer.h | 46 ++++++ tests/validation/reference/PoolingLayer.cpp | 78 +++++----- 16 files changed, 892 insertions(+), 61 deletions(-) create mode 100644 arm_compute/core/NEON/kernels/NEMaxUnpoolingLayerKernel.h create mode 100644 arm_compute/runtime/NEON/functions/NEMaxUnpoolingLayer.h create mode 100644 src/core/NEON/kernels/NEMaxUnpoolingLayerKernel.cpp create mode 100644 src/runtime/NEON/functions/NEMaxUnpoolingLayer.cpp create mode 100644 tests/validation/NEON/MaxUnpoolingLayer.cpp create mode 100644 tests/validation/fixtures/MaxUnpoolingLayerFixture.h create mode 100644 tests/validation/reference/MaxUnpoolingLayer.cpp create mode 100644 tests/validation/reference/MaxUnpoolingLayer.h diff --git a/Android.bp b/Android.bp index 59fb270d0d..b484a1e909 100644 --- a/Android.bp +++ b/Android.bp @@ -309,6 +309,7 @@ cc_library_static { "src/core/NEON/kernels/NELKTrackerKernel.cpp", "src/core/NEON/kernels/NELocallyConnectedMatrixMultiplyKernel.cpp", "src/core/NEON/kernels/NEMagnitudePhaseKernel.cpp", + "src/core/NEON/kernels/NEMaxUnpoolingLayerKernel.cpp", "src/core/NEON/kernels/NEMeanStdDevKernel.cpp", "src/core/NEON/kernels/NEMeanStdDevNormalizationKernel.cpp", "src/core/NEON/kernels/NEMedian3x3Kernel.cpp", @@ -685,6 +686,7 @@ cc_library_static { "src/runtime/NEON/functions/NELaplacianReconstruct.cpp", "src/runtime/NEON/functions/NELocallyConnectedLayer.cpp", "src/runtime/NEON/functions/NEMagnitude.cpp", + "src/runtime/NEON/functions/NEMaxUnpoolingLayer.cpp", "src/runtime/NEON/functions/NEMeanStdDev.cpp", "src/runtime/NEON/functions/NEMeanStdDevNormalizationLayer.cpp", "src/runtime/NEON/functions/NEMedian3x3.cpp", diff --git a/arm_compute/core/NEON/NEKernels.h b/arm_compute/core/NEON/NEKernels.h index 38701f434a..dfe0ccaafc 100644 --- a/arm_compute/core/NEON/NEKernels.h +++ b/arm_compute/core/NEON/NEKernels.h @@ -106,6 +106,7 @@ #include "arm_compute/core/NEON/kernels/NELKTrackerKernel.h" #include "arm_compute/core/NEON/kernels/NELocallyConnectedMatrixMultiplyKernel.h" #include "arm_compute/core/NEON/kernels/NEMagnitudePhaseKernel.h" +#include "arm_compute/core/NEON/kernels/NEMaxUnpoolingLayerKernel.h" #include "arm_compute/core/NEON/kernels/NEMeanStdDevKernel.h" #include "arm_compute/core/NEON/kernels/NEMeanStdDevNormalizationKernel.h" #include "arm_compute/core/NEON/kernels/NEMedian3x3Kernel.h" diff --git a/arm_compute/core/NEON/kernels/NEMaxUnpoolingLayerKernel.h b/arm_compute/core/NEON/kernels/NEMaxUnpoolingLayerKernel.h new file mode 100644 index 0000000000..269317b6c1 --- /dev/null +++ b/arm_compute/core/NEON/kernels/NEMaxUnpoolingLayerKernel.h @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2020 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_NEMAXUNPOOLINGLAYERKERNEL_H +#define ARM_COMPUTE_NEMAXUNPOOLINGLAYERKERNEL_H + +#include "arm_compute/core/NEON/INEKernel.h" + +namespace arm_compute +{ +class ITensor; + +/** Interface for the pooling layer kernel */ +class NEMaxUnpoolingLayerKernel : public INEKernel +{ +public: + const char *name() const override + { + return "NEMaxUnpoolingLayerKernel"; + } + /** Default constructor */ + NEMaxUnpoolingLayerKernel(); + /** Prevent instances of this class from being copied (As this class contains pointers) */ + NEMaxUnpoolingLayerKernel(const NEMaxUnpoolingLayerKernel &) = delete; + /** Prevent instances of this class from being copied (As this class contains pointers) */ + NEMaxUnpoolingLayerKernel &operator=(const NEMaxUnpoolingLayerKernel &) = delete; + /** Allow instances of this class to be moved */ + NEMaxUnpoolingLayerKernel(NEMaxUnpoolingLayerKernel &&) = default; + /** Allow instances of this class to be moved */ + NEMaxUnpoolingLayerKernel &operator=(NEMaxUnpoolingLayerKernel &&) = default; + /** Default destructor */ + ~NEMaxUnpoolingLayerKernel() = default; + /** Set the input and output tensors. + * + * @note Output shape must be equal to the shape of the original input to pool. + * + * @param[in] input Source tensor. Data types supported: F16/F32. + * @param[out] indices The indices of the maximal values. Data type supported: U32. + * @param[out] output Destination tensor. Data types supported: Same as @p input. + * @param[in] pool_info Contains pooling operation information described in @ref PoolingLayerInfo. + */ + void configure(const ITensor *input, const ITensor *indices, ITensor *output, const PoolingLayerInfo &pool_info); + /** Static function to check if given info will lead to a valid configuration of @ref NEMaxUnpoolingLayerKernel + * + * @param[in] input Source tensor. Data types supported: F16/F32. + * @param[in] output Destination tensor. Data types supported: Same as @p input. + * @param[in] indices The indices of the maximal values. Data type supported: U32. + * @param[in] pool_info Contains pooling operation information described in @ref PoolingLayerInfo. + * + * @return a status + */ + static Status validate(const ITensorInfo *input, const ITensorInfo *indices, const ITensorInfo *output, const PoolingLayerInfo &pool_info); + + // Inherited methods overridden: + void run(const Window &window, const ThreadInfo &info) override; + +private: + /** Function to perform 2x2 pooling and compute the pooling indices. The indices can be used for max unpool. + * + * @param[in] window_input Input region on which to execute the kernel. + */ + template + void unpooling2(const Window &window_input); + + using UnpoolingFunction = void (NEMaxUnpoolingLayerKernel::*)(const Window &window); + +private: + UnpoolingFunction _func; + const ITensor *_input; + ITensor *_output; + const ITensor *_indices; + PoolingLayerInfo _pool_info; + DataLayout _data_layout; + unsigned int _num_elems_processed_per_iteration; +}; +} // namespace arm_compute +#endif /*ARM_COMPUTE_NEMAXUNPOOLINGLAYERKERNEL_H */ diff --git a/arm_compute/core/utils/misc/ShapeCalculator.h b/arm_compute/core/utils/misc/ShapeCalculator.h index dfccec8b37..bc85c6986f 100644 --- a/arm_compute/core/utils/misc/ShapeCalculator.h +++ b/arm_compute/core/utils/misc/ShapeCalculator.h @@ -809,6 +809,37 @@ inline TensorShape compute_pool_shape(const ITensorInfo &input, PoolingLayerInfo return output_shape; } +/** Calculate the output unpool shape of a tensor + * + * @param[in] input Input tensor info + * @param[in] pool_info Pooling layer info + * + * @return the calculated shape + */ +inline TensorShape compute_unpool_shape(const ITensorInfo &input, PoolingLayerInfo pool_info) +{ + const unsigned int idx_width = get_data_layout_dimension_index(input.data_layout(), DataLayoutDimension::WIDTH); + const unsigned int idx_height = get_data_layout_dimension_index(input.data_layout(), DataLayoutDimension::HEIGHT); + const TensorShape input_shape = input.tensor_shape(); + ARM_COMPUTE_ERROR_ON(input_shape[idx_height] <= 1 || input_shape[idx_width] <= 1); + const PadStrideInfo pad_stride_info = pool_info.pad_stride_info; + const unsigned int stride_x = pad_stride_info.stride().first; + const unsigned int stride_y = pad_stride_info.stride().second; + + const int pad_left = pad_stride_info.pad_left(); + const int pad_top = pad_stride_info.pad_top(); + const int pad_right = pad_stride_info.pad_right(); + const int pad_bottom = pad_stride_info.pad_bottom(); + + TensorShape output_shape = input_shape; + const unsigned int out_width = (input_shape[idx_width] - 1) * stride_x - pad_left - pad_right + pool_info.pool_size.width; + const unsigned int out_height = (input_shape[idx_height] - 1) * stride_y - pad_top - pad_bottom + pool_info.pool_size.height; + + output_shape.set(idx_width, out_width); + output_shape.set(idx_height, out_height); + return output_shape; +} + /** Calculate the output roi align shape of a tensor * * @param[in] input Input tensor info diff --git a/arm_compute/runtime/NEON/NEFunctions.h b/arm_compute/runtime/NEON/NEFunctions.h index de364fa9af..19137b8830 100644 --- a/arm_compute/runtime/NEON/NEFunctions.h +++ b/arm_compute/runtime/NEON/NEFunctions.h @@ -105,6 +105,7 @@ #include "arm_compute/runtime/NEON/functions/NELaplacianReconstruct.h" #include "arm_compute/runtime/NEON/functions/NELocallyConnectedLayer.h" #include "arm_compute/runtime/NEON/functions/NEMagnitude.h" +#include "arm_compute/runtime/NEON/functions/NEMaxUnpoolingLayer.h" #include "arm_compute/runtime/NEON/functions/NEMeanStdDev.h" #include "arm_compute/runtime/NEON/functions/NEMeanStdDevNormalizationLayer.h" #include "arm_compute/runtime/NEON/functions/NEMedian3x3.h" diff --git a/arm_compute/runtime/NEON/functions/NEMaxUnpoolingLayer.h b/arm_compute/runtime/NEON/functions/NEMaxUnpoolingLayer.h new file mode 100644 index 0000000000..f93e4caf93 --- /dev/null +++ b/arm_compute/runtime/NEON/functions/NEMaxUnpoolingLayer.h @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2020 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_NEMAXUNPOOLINGLAYER_H +#define ARM_COMPUTE_NEMAXUNPOOLINGLAYER_H + +#include "arm_compute/runtime/IFunction.h" + +#include "arm_compute/core/NEON/kernels/NEMaxUnpoolingLayerKernel.h" +#include "arm_compute/core/NEON/kernels/NEMemsetKernel.h" + +namespace arm_compute +{ +class ITensor; + +/** Function to perform MaxUnpooling. This function calls the following NEON kernels: + * + * -# @ref NEMemsetKernel + * -# @ref NEMaxUnpoolingLayerKernel + */ +class NEMaxUnpoolingLayer : public IFunction +{ +public: + /** Constructor */ + NEMaxUnpoolingLayer(); + /** Set the input and output tensors. + * + * @note F16 is supported for pool sizes 2 and 3 only + * + * @param[in, out] input Source tensor. (Written to only when padding != 0) Data types supported: QASYMM8/QASYMM8_SIGNED/F16/F32. + * @param[out] output Destination tensor. Data types supported: Same as @p input. + * @param[out] indices The indices of the maximal values. Data type supported: U32. + * @param[in] pool_info Contains pooling operation information described in @ref PoolingLayerInfo. + */ + void configure(ITensor *input, ITensor *indices, ITensor *output, const PoolingLayerInfo &pool_info); + /** Static function to check if given info will lead to a valid configuration of @ref NEMaxUnpoolingLayer + * + * @note F16 is supported for pool sizes 2 and 3 only + * + * @param[in] input Source tensor. (Written to only when padding != 0) Data types supported: QASYMM8/QASYMM8_SIGNED/F16/F32. + * @param[in] indices The indices of the maximal values. Data type supported: U32. + * @param[in] output Destination tensor. Data types supported: Same as @p input. + * @param[in] pool_info Contains pooling operation information described in @ref PoolingLayerInfo. + * + * @return a status + */ + static Status validate(const ITensorInfo *input, const ITensorInfo *indices, const ITensorInfo *output, const PoolingLayerInfo &pool_info); + + // Inherited methods overridden: + void run() override; + +private: + NEMemsetKernel _memset_kernel; + NEMaxUnpoolingLayerKernel _unpooling_layer_kernel; +}; +} +#endif /* ARM_COMPUTE_NEMAXUNPOOLINGLAYER_H */ diff --git a/src/core/NEON/kernels/NEMaxUnpoolingLayerKernel.cpp b/src/core/NEON/kernels/NEMaxUnpoolingLayerKernel.cpp new file mode 100644 index 0000000000..1967c553bd --- /dev/null +++ b/src/core/NEON/kernels/NEMaxUnpoolingLayerKernel.cpp @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2020 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/NEON/kernels/NEMaxUnpoolingLayerKernel.h" + +#include "arm_compute/core/AccessWindowStatic.h" +#include "arm_compute/core/CPP/Validate.h" +#include "arm_compute/core/Error.h" +#include "arm_compute/core/Helpers.h" +#include "arm_compute/core/ITensor.h" +#include "arm_compute/core/NEON/NEAsymm.h" +#include "arm_compute/core/NEON/NEFixedPoint.h" +#include "arm_compute/core/NEON/NEMath.h" +#include "arm_compute/core/TensorInfo.h" +#include "arm_compute/core/Utils.h" +#include "arm_compute/core/Validate.h" +#include "arm_compute/core/Window.h" +#include "arm_compute/core/utils/misc/ShapeCalculator.h" + +#include "support/ToolchainSupport.h" + +namespace arm_compute +{ +using namespace misc::shape_calculator; + +namespace +{ +Status validate_arguments(const ITensorInfo *input, const ITensorInfo *output, const PoolingLayerInfo &pool_info, const ITensorInfo *indices) +{ + ARM_COMPUTE_RETURN_ERROR_ON_NULLPTR(input, output, indices); + int pool_stride_x = 0; + int pool_stride_y = 0; + PoolingType pool_type = pool_info.pool_type; + const PadStrideInfo pad_stride_info = pool_info.pad_stride_info; + std::tie(pool_stride_x, pool_stride_y) = pad_stride_info.stride(); + const int pool_size_x = pool_info.pool_size.width; + const int pool_size_y = pool_info.pool_size.height; + const Size2D pool_size(pool_size_x, pool_size_y); + ARM_COMPUTE_RETURN_ERROR_ON_CPU_F16_UNSUPPORTED(input); + ARM_COMPUTE_RETURN_ERROR_ON_DATA_TYPE_CHANNEL_NOT_IN(indices, 1, DataType::U32); + ARM_COMPUTE_RETURN_ERROR_ON_MSG(pool_type != PoolingType::MAX, "Pooling indices only supported for MAX pooling method"); + ARM_COMPUTE_RETURN_ERROR_ON_DATA_TYPE_CHANNEL_NOT_IN(input, 1, DataType::F32); + ARM_COMPUTE_RETURN_ERROR_ON_MSG((pool_size != Size2D(2, 2)), "Pooling indices only supported for pool size 2x2"); + if(output->total_size() != 0) + { + ARM_COMPUTE_RETURN_ERROR_ON_MISMATCHING_DATA_TYPES(input, output); + ARM_COMPUTE_RETURN_ERROR_ON_MISMATCHING_DATA_LAYOUT(input, output); + } + + return Status{}; +} +} // namespace + +NEMaxUnpoolingLayerKernel::NEMaxUnpoolingLayerKernel() + : _func(nullptr), _input(nullptr), _output(nullptr), _indices(nullptr), _pool_info(), _data_layout(DataLayout::UNKNOWN), _num_elems_processed_per_iteration(0) +{ +} + +void NEMaxUnpoolingLayerKernel::configure(const ITensor *input, const ITensor *indices, ITensor *output, const PoolingLayerInfo &pool_info) +{ + ARM_COMPUTE_ERROR_ON_NULLPTR(input, output); + const Size2D pool_size(pool_info.pool_size.width, pool_info.pool_size.height); + ARM_COMPUTE_ERROR_THROW_ON(validate_arguments(input->info(), output->info(), pool_info, indices->info())); + _input = input; + _output = output; + _indices = indices; + _pool_info = pool_info; + _data_layout = input->info()->data_layout(); + switch(input->info()->data_type()) + { + case DataType::F32: + _func = &NEMaxUnpoolingLayerKernel::unpooling2; + break; + case DataType::QASYMM8: + _func = &NEMaxUnpoolingLayerKernel::unpooling2; + break; + case DataType::QASYMM8_SIGNED: + _func = &NEMaxUnpoolingLayerKernel::unpooling2; + break; +#ifdef __ARM_FEATURE_FP16_VECTOR_ARITHMETIC + case DataType::F16: + _func = &NEMaxUnpoolingLayerKernel::unpooling2; + break; +#endif /* __ARM_FEATURE_FP16_VECTOR_ARITHMETIC */ + default: + break; + } + const TensorShape output_shape = compute_unpool_shape(*input->info(), pool_info); + auto_init_if_empty(*output->info(), input->info()->clone()->set_tensor_shape(output_shape)); + _num_elems_processed_per_iteration = 1; + auto window = calculate_max_window(*input->info(), Steps(_num_elems_processed_per_iteration)); + INEKernel::configure(window); +} +template +void NEMaxUnpoolingLayerKernel::unpooling2(const Window &window) +{ + Iterator input(_input, window); + Iterator indices(_indices, window); + auto out_ptr = reinterpret_cast(_output->buffer()); + const int out_stride_w = static_cast(_output->info()->strides_in_bytes()[3]); + execute_window_loop(window, [&](const Coordinates & id) + { + auto vindices = reinterpret_cast(indices.ptr()); + auto vinput = reinterpret_cast(input.ptr()); + out_ptr[id[3] * out_stride_w / sizeof(T) + *vindices] = *vinput; + }, + input, indices); +} + +Status NEMaxUnpoolingLayerKernel::validate(const ITensorInfo *input, const ITensorInfo *indices, const ITensorInfo *output, const PoolingLayerInfo &pool_info) +{ + ARM_COMPUTE_RETURN_ERROR_ON_NULLPTR(input, indices, output); + ARM_COMPUTE_RETURN_ON_ERROR(validate_arguments(input, output, pool_info, indices)); + return Status{}; +} + +void NEMaxUnpoolingLayerKernel::run(const Window &window, const ThreadInfo &info) +{ + ARM_COMPUTE_UNUSED(info); + ARM_COMPUTE_ERROR_ON_UNCONFIGURED_KERNEL(this); + ARM_COMPUTE_ERROR_ON_INVALID_SUBWINDOW(INEKernel::window(), window); + ARM_COMPUTE_ERROR_ON(_func == nullptr); + // Run function + (this->*_func)(window); +} +} // namespace arm_compute diff --git a/src/core/NEON/kernels/NEPoolingLayerKernel.cpp b/src/core/NEON/kernels/NEPoolingLayerKernel.cpp index 6d61f51f31..d6b17534d3 100644 --- a/src/core/NEON/kernels/NEPoolingLayerKernel.cpp +++ b/src/core/NEON/kernels/NEPoolingLayerKernel.cpp @@ -36,7 +36,6 @@ #include "arm_compute/core/Validate.h" #include "arm_compute/core/Window.h" #include "arm_compute/core/utils/misc/ShapeCalculator.h" - #include "support/ToolchainSupport.h" #include "arm_compute/core/NEON/wrapper/wrapper.h" @@ -137,6 +136,7 @@ Status validate_arguments(const ITensorInfo *input, const ITensorInfo *output, c ARM_COMPUTE_RETURN_ERROR_ON_CPU_F16_UNSUPPORTED(input); if(indices) { + ARM_COMPUTE_RETURN_ERROR_ON_DATA_TYPE_CHANNEL_NOT_IN(input, 1, DataType::F32, DataType::F16); ARM_COMPUTE_RETURN_ERROR_ON_DATA_TYPE_CHANNEL_NOT_IN(indices, 1, DataType::U32); ARM_COMPUTE_RETURN_ERROR_ON_MSG(pool_type != PoolingType::MAX, "Pooling indices only supported for MAX pooling method"); } @@ -156,7 +156,6 @@ Status validate_arguments(const ITensorInfo *input, const ITensorInfo *output, c if(indices) { ARM_COMPUTE_RETURN_ERROR_ON_MSG((pool_size != Size2D(2, 2)), "Pooling indices only supported for pool size 2x2"); - ARM_COMPUTE_RETURN_ERROR_ON((indices->dimension(get_data_layout_dimension_index(indices->data_layout(), DataLayoutDimension::WIDTH)) != pooled_w) || (indices->dimension(get_data_layout_dimension_index(indices->data_layout(), DataLayoutDimension::HEIGHT)) != pooled_h)); } @@ -1489,6 +1488,39 @@ void NEPoolingLayerKernel::poolingMxN_f32_nchw(const Window &window_input, const input, output); } +inline uint32_t offset_no_padding(uint32_t padded_offset, const Coordinates &id, const ITensorInfo &info, int pool_stride_x, int pool_stride_y) +{ + const int pad_left = info.padding().left; + const int pad_right = info.padding().right; + const int pad_top = info.padding().top; + const int pad_bottom = info.padding().bottom; + const int in_stride_y = static_cast(info.strides_in_bytes().y()); + const int in_stride_w = static_cast(info.strides_in_bytes()[3]); + const int pad_horiz = pad_left + pad_right; + const int pad_vert = pad_top + pad_bottom; + + if(info.data_layout() == DataLayout::NCHW) + { + const uint32_t offset_base = padded_offset + - sizeof(float) * pad_horiz * id.y() * pool_stride_y /* subtract padding elems per row */ + - pad_top * sizeof(float) /* top padding */ + - sizeof(float) * pad_horiz * info.tensor_shape()[1] * id.z() - pad_vert * in_stride_y * id.z() /* for each Z plane there are height*pad_right padding elems */ + - in_stride_w * id[3]; + + return offset_base; + } + else + { + const uint32_t offset_base = padded_offset + - sizeof(float) * pad_horiz * id.y() * pool_stride_x // subtract padding elems per row + - pad_top * sizeof(float) // top padding + - sizeof(float) * pad_horiz * info.tensor_shape()[1] * id.z() * pool_stride_y // for each Z plane there are width*pad_right padding elems + - in_stride_w * id[3]; + + return offset_base; + } +} + void NEPoolingLayerKernel::pooling2_f32_nchw_maxpool_indices(const Window &window_input, const Window &window) { Iterator input(_input, window_input); @@ -1502,11 +1534,10 @@ void NEPoolingLayerKernel::pooling2_f32_nchw_maxpool_indices(const Window &windo std::tie(pool_stride_x, pool_stride_y) = _pool_info.pad_stride_info.stride(); const uint8_t *const input_top_ptr = _input->ptr_to_element(Coordinates(-static_cast(pool_pad_left), -static_cast(pool_pad_top))); const uint8_t *const input_bottom_ptr = _input->ptr_to_element(Coordinates(-static_cast(pool_pad_left), -static_cast(pool_pad_top) + 1)); - - const Strides &input_strides = _input->info()->strides_in_bytes(); - const auto in_stridew = input_strides[1]; - - execute_window_loop(window, [&](const Coordinates &) + const int pad_left = _input->info()->padding().left; + const int pad_right = _input->info()->padding().right; + const int in_stride_y = static_cast(_input->info()->strides_in_bytes().y()); + execute_window_loop(window, [&](const Coordinates & id) { const auto input_offset_top = input_top_ptr + input.offset(); const auto input_offset_bottom = input_bottom_ptr + input.offset(); @@ -1521,8 +1552,9 @@ void NEPoolingLayerKernel::pooling2_f32_nchw_maxpool_indices(const Window &windo final_res = vget_lane_f32(res, 0); // Store result *(reinterpret_cast(output.ptr())) = final_res; - const uint32_t offset_top = (uint32_t)(input.offset() / sizeof(float)); - const uint32_t offset_bottom = (uint32_t)offset_top + (in_stridew / sizeof(float)); + const uint32_t offset_base = offset_no_padding(input.offset(), id, *_input->info(), pool_stride_x, pool_stride_y); + const uint32_t offset_top = (uint32_t)(offset_base / sizeof(float)); + const uint32_t offset_bottom = offset_top + in_stride_y / sizeof(float) - pad_right - pad_left; const uint32x2_t voffset_top = { offset_top, offset_top + 1u }; const uint32x2_t voffset_bottom = { offset_bottom, offset_bottom + 1u }; const uint32x2_t tmp_indices = vbsl_u32(vcgt_f32(top_data, bottom_data), voffset_top, voffset_bottom); @@ -1867,10 +1899,8 @@ void NEPoolingLayerKernel::pooling2_f32_nhwc_maxpool_indices(const Window &windo float32x4_t vres; const int pad_right = _input->info()->padding().right; - const int pad_top = _input->info()->padding().top; const int in_stride_y = static_cast(_input->info()->strides_in_bytes().y()); const int in_stride_z = static_cast(_input->info()->strides_in_bytes().z()); - const int in_stride_w = static_cast(_input->info()->strides_in_bytes()[3]); execute_window_loop(window, [&](const Coordinates & id) { @@ -1904,17 +1934,11 @@ void NEPoolingLayerKernel::pooling2_f32_nhwc_maxpool_indices(const Window &windo // Store result vst1q_f32(reinterpret_cast(output.ptr()), vres); - const uint32_t offset_base = input.offset() - - sizeof(float) * pad_right * id.y() * pool_stride_x /* subtract padding elems per row */ - - pad_top * sizeof(float) /* top padding */ - - sizeof(float) * pad_right * _input->info()->tensor_shape()[1] * id.z() * pool_stride_y /* for each Z plane there are width*pad_right padding elems */ - - in_stride_w * id[3] + _input->info()->tensor_shape()[0] * sizeof(float) * id[3]; - - const uint32_t offset_x0 = (uint32_t)offset_base / sizeof(float); - const uint32_t offset_x1 = (uint32_t)offset_x0 + in_stride_y / sizeof(float) - pad_right; - const uint32_t offset_x2 = (uint32_t)offset_x0 + in_stride_z / sizeof(float) - pad_right * _input->info()->tensor_shape()[1]; - const uint32_t offset_x3 = (uint32_t)offset_x2 + in_stride_y / sizeof(float) - pad_right; - + const uint32_t offset_base = offset_no_padding(input.offset(), id, *_input->info(), pool_stride_x, pool_stride_y); + const uint32_t offset_x0 = (uint32_t)offset_base / sizeof(float); + const uint32_t offset_x1 = (uint32_t)offset_x0 + in_stride_y / sizeof(float) - pad_right; + const uint32_t offset_x2 = (uint32_t)offset_x0 + in_stride_z / sizeof(float) - pad_right * _input->info()->tensor_shape()[1]; + const uint32_t offset_x3 = (uint32_t)offset_x2 + in_stride_y / sizeof(float) - pad_right; const uint32x4_t voffset_x0 = { offset_x0, offset_x0 + 1, offset_x0 + 2, offset_x0 + 3 }; const uint32x4_t voffset_x1 = { offset_x1, offset_x1 + 1, offset_x1 + 2, offset_x1 + 3 }; const uint32x4_t voffset_x2 = { offset_x2, offset_x2 + 1, offset_x2 + 2, offset_x2 + 3 }; diff --git a/src/runtime/NEON/functions/NEMaxUnpoolingLayer.cpp b/src/runtime/NEON/functions/NEMaxUnpoolingLayer.cpp new file mode 100644 index 0000000000..e606370b0a --- /dev/null +++ b/src/runtime/NEON/functions/NEMaxUnpoolingLayer.cpp @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2020 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/runtime/NEON/functions/NEMaxUnpoolingLayer.h" + +#include "arm_compute/core/ITensor.h" +#include "arm_compute/runtime/NEON/NEScheduler.h" + +namespace arm_compute +{ +NEMaxUnpoolingLayer::NEMaxUnpoolingLayer() + + : _memset_kernel(), _unpooling_layer_kernel() +{ +} + +void NEMaxUnpoolingLayer::configure(ITensor *input, ITensor *indices, ITensor *output, const PoolingLayerInfo &pool_info) +{ + const PixelValue zero_value(0.f); + _memset_kernel.configure(output, zero_value); + _unpooling_layer_kernel.configure(input, indices, output, pool_info); +} + +Status NEMaxUnpoolingLayer::validate(const ITensorInfo *input, const ITensorInfo *indices, const ITensorInfo *output, const PoolingLayerInfo &pool_info) +{ + return NEMaxUnpoolingLayerKernel::validate(input, indices, output, pool_info); +} + +void NEMaxUnpoolingLayer::run() +{ + NEScheduler::get().schedule(&_memset_kernel, Window::DimY); + NEScheduler::get().schedule(&_unpooling_layer_kernel, Window::DimY); +} +} /* namespace arm_compute */ diff --git a/src/runtime/NEON/functions/NEPoolingLayer.cpp b/src/runtime/NEON/functions/NEPoolingLayer.cpp index 12921cf40e..fb1b04ab6f 100644 --- a/src/runtime/NEON/functions/NEPoolingLayer.cpp +++ b/src/runtime/NEON/functions/NEPoolingLayer.cpp @@ -49,8 +49,8 @@ void NEPoolingLayer::configure(ITensor *input, ITensor *output, const PoolingLay case DataLayout::NCHW: { // Configure border depending on operation required (quantize border in case of asymmetric data_type) - BorderMode border_mode = (pool_info.pool_type == PoolingType::MAX) ? BorderMode::REPLICATE : BorderMode::CONSTANT; - PixelValue zero_value(0.f); + BorderMode border_mode = (!indices && pool_info.pool_type == PoolingType::MAX) ? BorderMode::REPLICATE : BorderMode::CONSTANT; + PixelValue zero_value((indices) ? std::numeric_limits::min() : 0.f); if(is_data_type_quantized_asymmetric(input->info()->data_type()) && !pool_info.exclude_padding) { zero_value = PixelValue(0, input->info()->data_type(), input->info()->quantization_info()); diff --git a/tests/validation/NEON/MaxUnpoolingLayer.cpp b/tests/validation/NEON/MaxUnpoolingLayer.cpp new file mode 100644 index 0000000000..949d569c89 --- /dev/null +++ b/tests/validation/NEON/MaxUnpoolingLayer.cpp @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2020 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/Types.h" +#include "arm_compute/runtime/NEON/functions/NEMaxUnpoolingLayer.h" +#include "arm_compute/runtime/NEON/functions/NEPoolingLayer.h" +#include "arm_compute/runtime/Tensor.h" +#include "arm_compute/runtime/TensorAllocator.h" +#include "tests/NEON/Accessor.h" +#include "tests/datasets/ShapeDatasets.h" +#include "tests/framework/Asserts.h" +#include "tests/framework/Macros.h" +#include "tests/framework/datasets/Datasets.h" +#include "tests/validation/Validation.h" +#include "tests/validation/fixtures/MaxUnpoolingLayerFixture.h" + +namespace arm_compute +{ +namespace test +{ +namespace validation +{ +TEST_SUITE(NEON) +TEST_SUITE(PoolingLayer) + +template +using NEMaxUnpoolingLayerFixture = MaxUnpoolingLayerValidationFixture; + +const auto PoolingLayerIndicesDatasetFPSmall = combine(combine(framework::dataset::make("PoolType", { PoolingType::MAX }), framework::dataset::make("PoolingSize", { Size2D(2, 2) })), + framework::dataset::make("PadStride", { PadStrideInfo(2, 2, 0, 0), PadStrideInfo(2, 1, 0, 0) })); + +TEST_SUITE(Float) +TEST_SUITE(FP32) +FIXTURE_DATA_TEST_CASE(MaxUnpooling, NEMaxUnpoolingLayerFixture, framework::DatasetMode::PRECOMMIT, combine(combine(datasets::SmallShapes(), combine(PoolingLayerIndicesDatasetFPSmall, + framework::dataset::make("DataType", DataType::F32))), + framework::dataset::make("DataLayout", { DataLayout::NCHW, DataLayout::NHWC }) + + )) +{ + // Validate output + validate(Accessor(_target), _reference); +} +TEST_SUITE_END() // FP32 +#ifdef __ARM_FEATURE_FP16_VECTOR_ARITHMETIC +TEST_SUITE(FP16) +FIXTURE_DATA_TEST_CASE(MaxUnpooling, NEMaxUnpoolingLayerFixture, framework::DatasetMode::PRECOMMIT, combine(combine(datasets::SmallShapes(), combine(PoolingLayerIndicesDatasetFPSmall, + framework::dataset::make("DataType", DataType::F16))), + framework::dataset::make("DataLayout", { DataLayout::NCHW, DataLayout::NHWC }) + + )) +{ + // Validate output + validate(Accessor(_target), _reference); +} +TEST_SUITE_END() // FP16 +#endif /* __ARM_FEATURE_FP16_VECTOR_ARITHMETIC */ +TEST_SUITE_END() // Float +TEST_SUITE_END() // PoolingLayer +TEST_SUITE_END() // NEON +} // namespace validation +} // namespace test +} // namespace arm_compute diff --git a/tests/validation/fixtures/MaxUnpoolingLayerFixture.h b/tests/validation/fixtures/MaxUnpoolingLayerFixture.h new file mode 100644 index 0000000000..ee08f59e7e --- /dev/null +++ b/tests/validation/fixtures/MaxUnpoolingLayerFixture.h @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2020 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_TEST_POOLING_LAYER_FIXTURE +#define ARM_COMPUTE_TEST_POOLING_LAYER_FIXTURE + +#include "arm_compute/core/TensorShape.h" +#include "arm_compute/core/Types.h" +#include "arm_compute/core/utils/misc/ShapeCalculator.h" +#include "arm_compute/runtime/Tensor.h" +#include "tests/AssetsLibrary.h" +#include "tests/Globals.h" +#include "tests/IAccessor.h" +#include "tests/framework/Asserts.h" +#include "tests/framework/Fixture.h" +#include "tests/validation/reference/MaxUnpoolingLayer.h" +#include "tests/validation/reference/PoolingLayer.h" +#include +namespace arm_compute +{ +namespace test +{ +namespace validation +{ +template +class MaxUnpoolingLayerValidationGenericFixture : public framework::Fixture +{ +public: + template + void setup(TensorShape shape, PoolingLayerInfo pool_info, DataType data_type, DataLayout data_layout) + { + std::mt19937 gen(library->seed()); + std::uniform_int_distribution<> offset_dis(0, 20); + const float scale = data_type == DataType::QASYMM8_SIGNED ? 1.f / 127.f : 1.f / 255.f; + const int scale_in = data_type == DataType::QASYMM8_SIGNED ? -offset_dis(gen) : offset_dis(gen); + const int scale_out = data_type == DataType::QASYMM8_SIGNED ? -offset_dis(gen) : offset_dis(gen); + const QuantizationInfo input_qinfo(scale, scale_in); + const QuantizationInfo output_qinfo(scale, scale_out); + _pool_info = pool_info; + _target = compute_target(shape, pool_info, data_type, data_layout, input_qinfo, output_qinfo); + _reference = compute_reference(shape, pool_info, data_type, input_qinfo, output_qinfo); + } + +protected: + template + void fill(U &&tensor) + { + if(!is_data_type_quantized(tensor.data_type())) + { + std::uniform_real_distribution<> distribution(-1.f, 1.f); + library->fill(tensor, distribution, 0); + } + else // data type is quantized_asymmetric + { + library->fill_tensor_uniform(tensor, 0); + } + } + + TensorType compute_target(TensorShape input_shape, PoolingLayerInfo pool_info, + DataType data_type, DataLayout data_layout, + QuantizationInfo input_qinfo, QuantizationInfo output_qinfo) + { + // Change shape in case of NHWC. + if(data_layout == DataLayout::NHWC) + { + permute(input_shape, PermutationVector(2U, 0U, 1U)); + } + + // Create tensors + TensorType src = create_tensor(input_shape, data_type, 1, input_qinfo, data_layout); + const TensorShape dst_shape = misc::shape_calculator::compute_pool_shape(*(src.info()), pool_info); + TensorType dst = create_tensor(dst_shape, data_type, 1, output_qinfo, data_layout); + TensorType unpooled = create_tensor(input_shape, data_type, 1, output_qinfo, data_layout); + TensorType indices = create_tensor(dst_shape, DataType::U32, 1, output_qinfo, data_layout); + + // Create and configure function + PoolingFunctionType pool_layer; + pool_layer.configure(&src, &dst, pool_info, &indices); + // Create and configure function + + MaxUnpoolingFunctionType unpool_layer; + unpool_layer.configure(&dst, &indices, &unpooled, pool_info); + + ARM_COMPUTE_EXPECT(src.info()->is_resizable(), framework::LogLevel::ERRORS); + ARM_COMPUTE_EXPECT(dst.info()->is_resizable(), framework::LogLevel::ERRORS); + ARM_COMPUTE_EXPECT(indices.info()->is_resizable(), framework::LogLevel::ERRORS); + + // Allocate tensors + src.allocator()->allocate(); + dst.allocator()->allocate(); + indices.allocator()->allocate(); + unpooled.allocator()->allocate(); + + ARM_COMPUTE_EXPECT(!src.info()->is_resizable(), framework::LogLevel::ERRORS); + ARM_COMPUTE_EXPECT(!dst.info()->is_resizable(), framework::LogLevel::ERRORS); + ARM_COMPUTE_EXPECT(!indices.info()->is_resizable(), framework::LogLevel::ERRORS); + ARM_COMPUTE_EXPECT(!unpooled.info()->is_resizable(), framework::LogLevel::ERRORS); + + // Fill tensors + fill(AccessorType(src)); + + // Compute function + pool_layer.run(); + unpool_layer.run(); + return unpooled; + } + + SimpleTensor compute_reference(TensorShape input_shape, PoolingLayerInfo info, DataType data_type, + QuantizationInfo input_qinfo, QuantizationInfo output_qinfo) + { + SimpleTensor src(input_shape, data_type, 1, input_qinfo); + SimpleTensor indices{}; + // Fill reference + fill(src); + auto pooled_tensor = reference::pooling_layer(src, info, output_qinfo, &indices); + return reference::max_unpooling_layer(pooled_tensor, info, output_qinfo, indices, input_shape); + } + + TensorType _target{}; + SimpleTensor _reference{}; + PoolingLayerInfo _pool_info{}; +}; + +template +class MaxUnpoolingLayerValidationFixture : public MaxUnpoolingLayerValidationGenericFixture +{ +public: + template + void setup(TensorShape shape, PoolingType pool_type, Size2D pool_size, PadStrideInfo pad_stride_info, DataType data_type, DataLayout data_layout) + { + MaxUnpoolingLayerValidationGenericFixture::setup(shape, PoolingLayerInfo(pool_type, pool_size, data_layout, pad_stride_info, true), + data_type, data_layout); + } +}; + +} // namespace validation +} // namespace test +} // namespace arm_compute +#endif /* ARM_COMPUTE_TEST_POOLING_LAYER_FIXTURE */ diff --git a/tests/validation/fixtures/PoolingLayerFixture.h b/tests/validation/fixtures/PoolingLayerFixture.h index eb40cea0c2..b9b5b3857b 100644 --- a/tests/validation/fixtures/PoolingLayerFixture.h +++ b/tests/validation/fixtures/PoolingLayerFixture.h @@ -86,7 +86,6 @@ protected: { permute(shape, PermutationVector(2U, 0U, 1U)); } - // Create tensors TensorType src = create_tensor(shape, data_type, 1, input_qinfo, data_layout); const TensorShape dst_shape = misc::shape_calculator::compute_pool_shape(*(src.info()), info); diff --git a/tests/validation/reference/MaxUnpoolingLayer.cpp b/tests/validation/reference/MaxUnpoolingLayer.cpp new file mode 100644 index 0000000000..d74a930856 --- /dev/null +++ b/tests/validation/reference/MaxUnpoolingLayer.cpp @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2020 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 "MaxUnpoolingLayer.h" + +#include "arm_compute/core/Types.h" +#include "arm_compute/core/utils/misc/ShapeCalculator.h" +#include "tests/validation/Helpers.h" + +namespace arm_compute +{ +namespace test +{ +namespace validation +{ +namespace reference +{ +using namespace arm_compute::misc::shape_calculator; + +template +SimpleTensor max_unpooling_layer_internal(const SimpleTensor &src, const PoolingLayerInfo &info, + const QuantizationInfo &output_qinfo, SimpleTensor &indices, + TensorShape output_shape, DataLayout data_layout) +{ + ARM_COMPUTE_UNUSED(info); + ARM_COMPUTE_UNUSED(output_qinfo); + ARM_COMPUTE_UNUSED(data_layout); + // Create reference + SimpleTensor dst{ output_shape, src.data_type(), 1 }; + ARM_COMPUTE_ERROR_ON(indices.shape().total_size() == 0); + std::fill_n(dst.data(), dst.num_elements(), 0); + const auto w_indices = static_cast(indices.shape()[0]); + const auto h_indices = static_cast(indices.shape()[1]); + const auto z_indices = static_cast(indices.shape()[2]); + const auto b_indices = static_cast(indices.shape()[3]); + const auto w_dst = static_cast(dst.shape()[0]); + const auto h_dst = static_cast(dst.shape()[1]); + const auto z_dst = static_cast(dst.shape()[2]); + for(int b = 0; b < b_indices; ++b) + { + for(int r = 0; r < z_indices; ++r) + { + for(int h = 0; h < h_indices; ++h) + { + for(int w = 0; w < w_indices; ++w) + { + const uint32_t index_into_dst = indices[b * z_indices * h_indices * w_indices + r * h_indices * w_indices + h * w_indices + w]; + const auto input_val = src[b * z_indices * h_indices * w_indices + r * h_indices * w_indices + h * w_indices + w]; + auto *ptr = &dst[b * z_dst * h_dst * w_dst]; + ptr[index_into_dst] = input_val; + } + } + } + } + return dst; +} + +template <> +SimpleTensor max_unpooling_layer( + const SimpleTensor &src, const PoolingLayerInfo &info, + const QuantizationInfo &output_qinfo, SimpleTensor &indices, + TensorShape output_shape, DataLayout data_layout) + +{ + SimpleTensor src_tmp = convert_from_asymmetric(src); + SimpleTensor dst_tmp = max_unpooling_layer_internal(src_tmp, info, output_qinfo, indices, output_shape, data_layout); + SimpleTensor dst = convert_to_asymmetric(dst_tmp, output_qinfo); + return dst; +} + +template +SimpleTensor max_unpooling_layer(const SimpleTensor &src, const PoolingLayerInfo &info, + const QuantizationInfo &output_qinfo, SimpleTensor &indices, + TensorShape output_shape, DataLayout data_layout) +{ + return max_unpooling_layer_internal(src, info, output_qinfo, indices, output_shape, data_layout); +} + +template SimpleTensor max_unpooling_layer(const SimpleTensor &src, const PoolingLayerInfo &info, + const QuantizationInfo &output_qinfo, SimpleTensor &indices, + TensorShape output_shape, DataLayout data_layout); + +} // namespace reference +} // namespace validation +} // namespace test +} // namespace arm_compute diff --git a/tests/validation/reference/MaxUnpoolingLayer.h b/tests/validation/reference/MaxUnpoolingLayer.h new file mode 100644 index 0000000000..b594265099 --- /dev/null +++ b/tests/validation/reference/MaxUnpoolingLayer.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2020 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_TEST_MAXUNPOOLING_LAYER_H +#define ARM_COMPUTE_TEST_MAXUNPOOLING_LAYER_H + +#include "tests/SimpleTensor.h" +#include "tests/validation/Helpers.h" + +namespace arm_compute +{ +namespace test +{ +namespace validation +{ +namespace reference +{ +template +SimpleTensor max_unpooling_layer(const SimpleTensor &src, const PoolingLayerInfo &info, const QuantizationInfo &output_qinfo, SimpleTensor &indices, + TensorShape output_shape, DataLayout data_layout = DataLayout::NCHW); + +} // namespace reference +} // namespace validation +} // namespace test +} // namespace arm_compute +#endif /* ARM_COMPUTE_TEST_MAXUNPOOLING_LAYER_H */ diff --git a/tests/validation/reference/PoolingLayer.cpp b/tests/validation/reference/PoolingLayer.cpp index 778e28d7c1..c110a67842 100644 --- a/tests/validation/reference/PoolingLayer.cpp +++ b/tests/validation/reference/PoolingLayer.cpp @@ -43,9 +43,10 @@ SimpleTensor pooling_layer_internal(const SimpleTensor &src, const Pooling ARM_COMPUTE_ERROR_ON(info.is_global_pooling && (src.shape().x() != src.shape().y())); // Create reference SimpleTensor dst{ compute_pool_shape(TensorInfo(src.shape(), 1, src.data_type()), info), src.data_type(), 1 }; + auto pooled_shape = compute_pool_shape(TensorInfo(src.shape(), 1, src.data_type()), info); if(indices) { - *indices = SimpleTensor { compute_pool_shape(TensorInfo(src.shape(), 1, src.data_type()), info), DataType::U32, 1 }; + *indices = SimpleTensor { pooled_shape, DataType::U32, 1 }; } const int pool_size_x = info.is_global_pooling ? src.shape().x() : info.pool_size.width; const int pool_size_y = info.is_global_pooling ? src.shape().y() : info.pool_size.height; @@ -58,56 +59,62 @@ SimpleTensor pooling_layer_internal(const SimpleTensor &src, const Pooling int pad_bottom = info.pad_stride_info.pad_bottom(); bool exclude_padding = info.exclude_padding; - const auto w_src = static_cast(src.shape()[0]); - const auto h_src = static_cast(src.shape()[1]); - const int upper_dims = src.shape().total_size() / (w_src * h_src); + const auto w_src = static_cast(src.shape()[0]); + const auto h_src = static_cast(src.shape()[1]); + const auto z_src = static_cast(src.shape()[2]); + const auto b_src = static_cast(src.shape()[3]); + + const int upper_dims = src.shape().total_size() / (w_src * h_src); + + const auto w_dst = static_cast(dst.shape()[0]); + const auto h_dst = static_cast(dst.shape()[1]); + const auto z_dst = static_cast(dst.shape()[2]); - const auto w_dst = static_cast(dst.shape()[0]); - const auto h_dst = static_cast(dst.shape()[1]); TensorShape shape_nhwc(src.shape()); permute(shape_nhwc, PermutationVector(2U, 0U, 1U)); - if(type == PoolingType::MAX) { - for(int r = 0; r < upper_dims; ++r) + for(int b = 0; b < b_src; ++b) { - for(int h = 0; h < h_dst; ++h) + for(int r = 0; r < z_src; ++r) { - for(int w = 0; w < w_dst; ++w) + for(int h = 0; h < h_dst; ++h) { - int wstart = w * pool_stride_x - pad_left; - int hstart = h * pool_stride_y - pad_top; - int wend = std::min(wstart + pool_size_x, w_src); - int hend = std::min(hstart + pool_size_y, h_src); - wstart = std::max(wstart, 0); - hstart = std::max(hstart, 0); - - auto max_val = std::numeric_limits::lowest(); - int max_index{ 0 }; - for(int y = hstart; y < hend; ++y) + for(int w = 0; w < w_dst; ++w) { - for(int x = wstart; x < wend; ++x) + int wstart = w * pool_stride_x - pad_left; + int hstart = h * pool_stride_y - pad_top; + int wend = std::min(wstart + pool_size_x, w_src); + int hend = std::min(hstart + pool_size_y, h_src); + wstart = std::max(wstart, 0); + hstart = std::max(hstart, 0); + auto max_val = std::numeric_limits::lowest(); + int max_index{ 0 }; + for(int y = hstart; y < hend; ++y) { - const auto val = static_cast(src[r * h_src * w_src + y * w_src + x]); - if(val > max_val) + for(int x = wstart; x < wend; ++x) { - max_val = val; - if(data_layout == DataLayout::NCHW) + const auto val = static_cast(src[b * z_src * h_src * w_src + r * h_src * w_src + y * w_src + x]); + if(val > max_val) { - max_index = coord2index(src.shape(), Coordinates(x, y, r)); - } - else - { - max_index = coord2index(shape_nhwc, Coordinates(r, x, y)); + max_val = val; + if(data_layout == DataLayout::NCHW) + { + max_index = coord2index(src.shape(), Coordinates(x, y, r, 0)); + } + else + { + max_index = coord2index(shape_nhwc, Coordinates(r, x, y, 0)); + } } } } - } - dst[r * h_dst * w_dst + h * w_dst + w] = static_cast(max_val); - if(indices) - { - (*indices)[r * h_dst * w_dst + h * w_dst + w] = max_index; + dst[b * z_dst * h_dst * w_dst + r * h_dst * w_dst + h * w_dst + w] = static_cast(max_val); + if(indices) + { + (*indices)[b * z_dst * h_dst * w_dst + r * h_dst * w_dst + h * w_dst + w] = max_index; + } } } } @@ -164,7 +171,6 @@ SimpleTensor pooling_layer_internal(const SimpleTensor &src, const Pooling } } } - return dst; } -- cgit v1.2.1