From 44f5572f3d6ba8e39c4a18a991049992d590ce39 Mon Sep 17 00:00:00 2001 From: Giorgio Arena Date: Fri, 12 Jul 2019 14:49:49 +0100 Subject: COMPMID-2179 New generic depthwise convolution for NEON F32 NHWC Change-Id: I2b883785c0500d4bdb6ee4700382ee058be2cd36 Signed-off-by: Giorgio Arena Reviewed-on: https://review.mlplatform.org/c/1538 Comments-Addressed: Arm Jenkins Tested-by: Arm Jenkins Reviewed-by: Gian Marco Iodice --- tests/NEON/Helper.h | 20 +++ .../NEON/DepthwiseConvolutionLayerKernel.cpp | 180 +++++++++++++++++++++ .../fixtures/DepthwiseConvolutionLayerFixture.h | 109 +++++++++++++ 3 files changed, 309 insertions(+) create mode 100644 tests/validation/NEON/DepthwiseConvolutionLayerKernel.cpp (limited to 'tests') diff --git a/tests/NEON/Helper.h b/tests/NEON/Helper.h index c30cbc9ca9..7446e5aaa8 100644 --- a/tests/NEON/Helper.h +++ b/tests/NEON/Helper.h @@ -88,6 +88,26 @@ public: } }; +/** As above but this also setups a Zero border on the input tensor of the kernel's bordersize */ +template +class NESynthetizeFunctionWithZeroConstantKernelBorder : public INESimpleFunction +{ +public: + /** Configure the kernel. + * + * @param[in] first First configuration argument. + * @param[in] args Rest of the configuration arguments. + */ + template + void configure(T first, Args &&... args) + { + auto k = arm_compute::support::cpp14::make_unique(); + k->configure(first, std::forward(args)...); + _kernel = std::move(k); + _border_handler.configure(first, BorderSize(_kernel->border_size()), BorderMode::CONSTANT, PixelValue()); + } +}; + } // namespace test } // namespace arm_compute #endif /* __ARM_COMPUTE_TEST_NEON_HELPER_H__ */ diff --git a/tests/validation/NEON/DepthwiseConvolutionLayerKernel.cpp b/tests/validation/NEON/DepthwiseConvolutionLayerKernel.cpp new file mode 100644 index 0000000000..3af835855b --- /dev/null +++ b/tests/validation/NEON/DepthwiseConvolutionLayerKernel.cpp @@ -0,0 +1,180 @@ +/* + * Copyright (c) 2019 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/NEDepthwiseConvolutionLayerKernel.h" +#include "tests/NEON/Accessor.h" +#include "tests/NEON/Helper.h" +#include "tests/framework/Macros.h" +#include "tests/framework/datasets/Datasets.h" +#include "tests/validation/Validation.h" +#include "tests/validation/fixtures/DepthwiseConvolutionLayerFixture.h" + +namespace arm_compute +{ +namespace test +{ +namespace validation +{ +using namespace arm_compute::misc::shape_calculator; + +// Create function for NEDepthwiseConvolutionLayerKernel +using NEDepthwiseConvolutionLayer = NESynthetizeFunctionWithZeroConstantKernelBorder; + +// Fixture for NEDepthwiseConvolutionLayerKernel +template +using NEDepthwiseConvolutionLayerKernelFixture = DepthwiseConvolutionLayerKernelValidationFixture; + +namespace +{ +// *INDENT-OFF* +// clang-format off +RelativeTolerance rel_tolerance_f32(0.001f); +constexpr float abs_tolerance_f32(0.0001f); + +/** Width values to test - Precommit */ +const auto width_values = framework::dataset::make("width", { 17U, 47U } ); + +/** Height values to test - Precommit */ +const auto height_values = framework::dataset::make("height", { 19U, 43U } ); + +/** Channel values to test - Precommit */ +const auto channel_values = framework::dataset::make("channels", { 32U, 128U }); + +/** Batch values to test - Precommit */ +const auto batch_values = framework::dataset::make("batch", { 1U, 3U }); + +/** Kernel size values to test - Precommit */ +const auto kernel_sz_values = framework::dataset::make("kernel_size", { Size2D(3U, 5U), Size2D(5U, 3U) }); + +/** Depth multiplier values to test - Precommit */ +const auto depth_multiplier_values = framework::dataset::make("depth_multiplier", { 1U, 3U }); + +/** Dilation values to test - Precommit */ +const auto dilation_values = framework::dataset::make("dilation", { Size2D(1U, 1U), Size2D(3U, 3U) }); + +/** Stride values to test - All */ +const auto stride_values = framework::dataset::make("stride", { Size2D(1U, 1U), Size2D(3U, 2U) }); + +/** Padding values to test - All */ +const auto padding_valid_values = framework::dataset::make("padding_valid", { true, false }); + +/** Data type values to test - All */ +const auto data_type_values = framework::dataset::make("data_type", { DataType::F32 }); + +/** Data layout values to test - All */ +const auto data_layout_values = framework::dataset::make("data_layout", { DataLayout::NHWC }); + +/** Configuration test */ +void validate_configuration(size_t width_value, size_t height_value, size_t channel_value, size_t batch_value, Size2D kernel_sz_value, size_t depth_multiplier_value, Size2D dilation_value, Size2D stride_value, bool padding_valid_value, DataType data_type_value, DataLayout data_layout_value) +{ + TensorShape src_shape(width_value, height_value, channel_value, batch_value); + TensorShape weights_shape(kernel_sz_value.width, kernel_sz_value.height, channel_value * depth_multiplier_value); + TensorShape biases_shape(channel_value * depth_multiplier_value); + + if(data_layout_value == DataLayout::NHWC) + { + permute(src_shape, PermutationVector(2U, 0U, 1U, 3U)); + permute(weights_shape, PermutationVector(2U, 0U, 1U)); + } + + TensorInfo src_info(src_shape, 1, data_type_value); + TensorInfo weights_info(weights_shape, 1, data_type_value); + TensorInfo biases_info(biases_shape, 1, data_type_value); + + src_info.set_data_layout(data_layout_value); + weights_info.set_data_layout(data_layout_value); + biases_info.set_data_layout(data_layout_value); + + PadStrideInfo conv_info; + if(padding_valid_value) + { + conv_info = PadStrideInfo(); + } + else + { + conv_info = calculate_same_pad(src_shape, weights_shape, PadStrideInfo(stride_value.width, stride_value.height), data_layout_value, dilation_value); + } + + const TensorShape dst_shape = compute_depthwise_convolution_shape(src_info, weights_info, conv_info, depth_multiplier_value, dilation_value); + + // Create tensors + Tensor src = create_tensor(src_shape, data_type_value, 1, QuantizationInfo(), data_layout_value); + Tensor weights = create_tensor(weights_shape, data_type_value, 1, QuantizationInfo(), data_layout_value); + Tensor biases = create_tensor(biases_shape, data_type_value, 1, QuantizationInfo(), data_layout_value); + Tensor dst = create_tensor(dst_shape, data_type_value, 1, QuantizationInfo(), data_layout_value); + + ARM_COMPUTE_EXPECT(src.info()->is_resizable(), framework::LogLevel::ERRORS); + ARM_COMPUTE_EXPECT(weights.info()->is_resizable(), framework::LogLevel::ERRORS); + ARM_COMPUTE_EXPECT(biases.info()->is_resizable(), framework::LogLevel::ERRORS); + ARM_COMPUTE_EXPECT(dst.info()->is_resizable(), framework::LogLevel::ERRORS); + + // Create and configure function + NEDepthwiseConvolutionLayer dwc; + dwc.configure(&src, &weights, &biases, &dst, conv_info, depth_multiplier_value, dilation_value); +} +} // namespace + +TEST_SUITE(NEON) +TEST_SUITE(DepthwiseConvolutionLayer) +TEST_SUITE(Float) +TEST_SUITE(FP32) +DATA_TEST_CASE(Configuration, framework::DatasetMode::ALL, combine(combine(combine(combine(combine(combine(combine(combine(combine(combine(width_values, + height_values), + channel_values), + batch_values), + kernel_sz_values), + depth_multiplier_values), + dilation_values), + stride_values), + padding_valid_values), + data_type_values), + data_layout_values), +width_value, height_value, channel_value, batch_value, kernel_sz_value, depth_multiplier_value, dilation_value, stride_value, padding_valid_value, data_type_value, data_layout_value) +{ + validate_configuration(width_value, height_value, channel_value, batch_value, kernel_sz_value, depth_multiplier_value, dilation_value, stride_value, padding_valid_value, data_type_value, data_layout_value); +} + +FIXTURE_DATA_TEST_CASE(RunSmall, NEDepthwiseConvolutionLayerKernelFixture, framework::DatasetMode::ALL, + combine(combine(combine(combine(combine(combine(combine(combine(combine(combine(width_values, + height_values), + channel_values), + batch_values), + kernel_sz_values), + depth_multiplier_values), + dilation_values), + stride_values), + padding_valid_values), + data_type_values), + data_layout_values)) +{ + // Validate output + validate(Accessor(_target), _reference, rel_tolerance_f32, 0.f, abs_tolerance_f32); +} + +TEST_SUITE_END() // FP32 +TEST_SUITE_END() // Float +TEST_SUITE_END() // DepthwiseConvolutionLayer +TEST_SUITE_END() // NEON +} // namespace validation +} // namespace test +} // namespace arm_compute \ No newline at end of file diff --git a/tests/validation/fixtures/DepthwiseConvolutionLayerFixture.h b/tests/validation/fixtures/DepthwiseConvolutionLayerFixture.h index b01e1760aa..30b8df9da5 100644 --- a/tests/validation/fixtures/DepthwiseConvolutionLayerFixture.h +++ b/tests/validation/fixtures/DepthwiseConvolutionLayerFixture.h @@ -192,6 +192,115 @@ public: } }; +template +class DepthwiseConvolutionLayerKernelValidationFixture : public DepthwiseConvolutionLayerValidationGenericFixture +{ +public: + template + void setup(size_t width, size_t height, size_t channel, size_t batch, Size2D kernel_size, size_t depth_multiplier, Size2D dilation, Size2D stride, bool padding_valid, DataType data_type, + DataLayout data_layout) + { + const TensorShape src_shape(width, height, channel, batch); + const TensorShape weights_shape(kernel_size.width, kernel_size.height, channel * depth_multiplier); + const TensorShape biases_shape(weights_shape.z()); + + PadStrideInfo conv_info; + if(padding_valid) + { + conv_info = PadStrideInfo(); + } + else + { + conv_info = calculate_same_pad(src_shape, weights_shape, PadStrideInfo(stride.width, stride.height), DataLayout::NCHW, dilation); + } + + _target = compute_target(src_shape, weights_shape, biases_shape, conv_info, dilation, depth_multiplier, data_type, data_layout); + _reference = compute_reference(src_shape, weights_shape, biases_shape, conv_info, dilation, depth_multiplier, data_type); + } + +protected: + template + void fill(U &&tensor, int i) + { + switch(tensor.data_type()) + { + case DataType::F32: + { + std::uniform_real_distribution<> distribution(-1.0f, 1.0f); + library->fill(tensor, distribution, i); + break; + } + default: + library->fill_tensor_uniform(tensor, i); + } + } + + TensorType compute_target(TensorShape input_shape, TensorShape weights_shape, TensorShape biases_shape, PadStrideInfo &conv_info, Size2D dilation, + unsigned int depth_multiplier, const DataType data_type, const DataLayout data_layout) + { + if(data_layout == DataLayout::NHWC) + { + permute(input_shape, PermutationVector(2U, 0U, 1U)); + permute(weights_shape, PermutationVector(2U, 0U, 1U)); + } + + // Create tensors + TensorType src = create_tensor(input_shape, data_type, 1, QuantizationInfo(), data_layout); + TensorType weights = create_tensor(weights_shape, data_type, 1, QuantizationInfo(), data_layout); + TensorType biases = create_tensor(biases_shape, data_type, 1, QuantizationInfo(), data_layout); + TensorType dst = create_tensor(TensorShape(), data_type, 1, QuantizationInfo(), data_layout); + + // Create Depthwise Convolution configure function + FunctionType dwc; + dwc.configure(&src, &weights, &biases, &dst, conv_info, depth_multiplier, dilation); + + ARM_COMPUTE_EXPECT(src.info()->is_resizable(), framework::LogLevel::ERRORS); + ARM_COMPUTE_EXPECT(weights.info()->is_resizable(), framework::LogLevel::ERRORS); + ARM_COMPUTE_EXPECT(biases.info()->is_resizable(), framework::LogLevel::ERRORS); + ARM_COMPUTE_EXPECT(dst.info()->is_resizable(), framework::LogLevel::ERRORS); + + // Allocate tensors + src.allocator()->allocate(); + weights.allocator()->allocate(); + biases.allocator()->allocate(); + dst.allocator()->allocate(); + + ARM_COMPUTE_EXPECT(!src.info()->is_resizable(), framework::LogLevel::ERRORS); + ARM_COMPUTE_EXPECT(!weights.info()->is_resizable(), framework::LogLevel::ERRORS); + ARM_COMPUTE_EXPECT(!biases.info()->is_resizable(), framework::LogLevel::ERRORS); + ARM_COMPUTE_EXPECT(!dst.info()->is_resizable(), framework::LogLevel::ERRORS); + + // Fill tensors + fill(AccessorType(src), 0); + fill(AccessorType(weights), 1); + fill(AccessorType(biases), 2); + + // Compute function + dwc.run(); + + return dst; + } + + SimpleTensor compute_reference(const TensorShape &input_shape, const TensorShape &weights_shape, const TensorShape &biases_shape, const PadStrideInfo &conv_info, + const Size2D &dilation, unsigned int depth_multiplier, const DataType data_type) + { + SimpleTensor src{ input_shape, data_type }; + SimpleTensor weights{ weights_shape, data_type }; + SimpleTensor biases{ biases_shape, data_type }; + + fill(src, 0); + fill(weights, 1); + fill(biases, 2); + + const TensorShape dst_shape = compute_depthwise_convolution_shape(TensorInfo(input_shape, 1, data_type), TensorInfo(weights_shape, 1, data_type), conv_info, + depth_multiplier, dilation); + return reference::depthwise_convolution(src, weights, biases, dst_shape, conv_info, depth_multiplier, dilation); + } + + TensorType _target{}; + SimpleTensor _reference{}; +}; + template class DepthwiseConvolutionLayerValidationQuantizedFixture : public DepthwiseConvolutionLayerValidationGenericFixture { -- cgit v1.2.1