/* * Copyright (c) 2017-2021 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_DEPTHWISE_CONVOLUTION_FIXTURE #define ARM_COMPUTE_TEST_DEPTHWISE_CONVOLUTION_FIXTURE #include "arm_compute/core/TensorShape.h" #include "arm_compute/core/Types.h" #include "arm_compute/core/utils/misc/ShapeCalculator.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/Helpers.h" #include "tests/validation/reference/ActivationLayer.h" #include "tests/validation/reference/DepthwiseConvolutionLayer.h" #include "utils/Utils.h" #include namespace arm_compute { namespace test { namespace validation { using namespace arm_compute::misc::shape_calculator; template class DepthwiseConvolutionLayerValidationGenericFixture : public framework::Fixture { public: using TBias = typename std::conditional < std::is_same::value || std::is_same::value, int32_t, T >::type; public: template void setup(TensorShape in_shape, Size2D kernel_size, PadStrideInfo pad_stride_info, Size2D dilation, unsigned int depth_multiplier, DataType input_data_type, DataType weights_data_type, QuantizationInfo input_quantization_info, QuantizationInfo weights_quantization_info, QuantizationInfo output_quantization_info, DataLayout data_layout, ActivationLayerInfo act_info, bool mixed_layout = false) { _mixed_layout = mixed_layout; _input_shape = in_shape; _input_data_type = input_data_type; _weights_data_type = weights_data_type; _input_quantization_info = input_quantization_info; _weights_quantization_info = weights_quantization_info; _output_quantization_info = output_quantization_info; _data_layout = data_layout; _pad_stride_info = pad_stride_info; _act_info = act_info; _depth_multiplier = depth_multiplier; _dilation = dilation; _bias_data_type = is_data_type_quantized(_input_data_type) ? DataType::S32 : _input_data_type; _weights_shape = TensorShape(kernel_size.width, kernel_size.height); const TensorInfo in_info(_input_shape, 1, _input_data_type); const TensorInfo we_info(_weights_shape, 1, _weights_data_type); const ConvolutionInfo info{ _pad_stride_info, _depth_multiplier, _act_info, _dilation }; _output_shape = compute_depthwise_convolution_shape(in_info, we_info, info); _weights_shape.set(2, _output_shape.z()); _biases_shape = TensorShape(_weights_shape[2]); } void configure_target() { TensorShape input_shape = _input_shape; TensorShape weights_shape = _weights_shape; TensorShape output_shape = _output_shape; if(_data_layout == DataLayout::NHWC) { permute(input_shape, PermutationVector(2U, 0U, 1U)); permute(weights_shape, PermutationVector(2U, 0U, 1U)); permute(output_shape, PermutationVector(2U, 0U, 1U)); } // Create tensors _src = create_tensor(input_shape, _input_data_type, 1, _input_quantization_info, _data_layout); _weights = create_tensor(weights_shape, _weights_data_type, 1, _weights_quantization_info, _data_layout); _biases = create_tensor(_biases_shape, _bias_data_type, 1, _input_quantization_info, _data_layout); _target = create_tensor(output_shape, _input_data_type, 1, _output_quantization_info, _data_layout); // Create Depthwise Convolution configure function _dwc.configure(&_src, &_weights, &_biases, &_target, _pad_stride_info, _depth_multiplier, _act_info, _dilation); ARM_COMPUTE_ASSERT(_src.info()->is_resizable()); ARM_COMPUTE_ASSERT(_weights.info()->is_resizable()); ARM_COMPUTE_ASSERT(_biases.info()->is_resizable()); ARM_COMPUTE_ASSERT(_target.info()->is_resizable()); } void allocate_and_run_target() { add_padding_x({ &_src, &_weights, &_biases, &_target }, _data_layout); // Allocate tensors _src.allocator()->allocate(); _weights.allocator()->allocate(); _biases.allocator()->allocate(); _target.allocator()->allocate(); ARM_COMPUTE_ASSERT(!_src.info()->is_resizable()); ARM_COMPUTE_ASSERT(!_weights.info()->is_resizable()); ARM_COMPUTE_ASSERT(!_biases.info()->is_resizable()); ARM_COMPUTE_ASSERT(!_target.info()->is_resizable()); // Fill tensors fill(AccessorType(_src), 0); fill(AccessorType(_weights), 1); fill(AccessorType(_biases), 2); if(_mixed_layout) { mix_layout(_dwc, _src, _target); } else { // Compute function _dwc.run(); } } void compute_reference() { SimpleTensor src{ _input_shape, _input_data_type, 1, _input_quantization_info }; SimpleTensor weights{ _weights_shape, _weights_data_type, 1, _weights_quantization_info }; SimpleTensor biases{ _biases_shape, _bias_data_type, 1, _input_quantization_info }; fill(src, 0); fill(weights, 1); fill(biases, 2); SimpleTensor depth_out = reference::depthwise_convolution(src, weights, biases, _output_shape, _pad_stride_info, _depth_multiplier, _dilation, _output_quantization_info); _reference = (_act_info.enabled()) ? reference::activation_layer(depth_out, _act_info) : depth_out; } protected: void mix_layout(FunctionType &layer, TensorType &src, TensorType &dst) { // Test Multi DataLayout graph cases, when the data layout changes after configure src.info()->set_data_layout(_data_layout == DataLayout::NCHW ? DataLayout::NHWC : DataLayout::NCHW); dst.info()->set_data_layout(_data_layout == DataLayout::NCHW ? DataLayout::NHWC : DataLayout::NCHW); // Compute Convolution function layer.run(); // Reinstating original data layout for the test suite to properly check the values src.info()->set_data_layout(_data_layout); dst.info()->set_data_layout(_data_layout); } template void fill(U &&tensor, int i) { switch(tensor.data_type()) { case DataType::QASYMM8: { std::uniform_int_distribution distribution(0, 10); library->fill(tensor, distribution, i); break; } case DataType::QASYMM8_SIGNED: case DataType::QSYMM8_PER_CHANNEL: { std::uniform_int_distribution distribution(-10, 10); library->fill(tensor, distribution, i); break; } case DataType::F16: { arm_compute::utils::uniform_real_distribution_16bit distribution{ -1.0f, 1.0f }; library->fill(tensor, distribution, i); break; } case DataType::F32: { std::uniform_real_distribution distribution(-1.0f, 1.0f); library->fill(tensor, distribution, i); break; } case DataType::S32: { std::uniform_int_distribution distribution(-100, 100); library->fill(tensor, distribution, i); break; } default: library->fill_tensor_uniform(tensor, i); } } TensorType _target{}; SimpleTensor _reference{}; TensorType _src{}; TensorType _weights{}; TensorType _biases{}; FunctionType _dwc{}; TensorShape _input_shape{}; TensorShape _weights_shape{}; TensorShape _biases_shape{}; TensorShape _output_shape{}; DataType _input_data_type{}; DataType _weights_data_type{}; DataType _bias_data_type{}; QuantizationInfo _input_quantization_info{}; QuantizationInfo _weights_quantization_info{}; QuantizationInfo _output_quantization_info{}; DataLayout _data_layout{}; PadStrideInfo _pad_stride_info{}; ActivationLayerInfo _act_info{}; unsigned int _depth_multiplier{}; Size2D _dilation{}; bool _mixed_layout{ false }; }; template class DepthwiseConvolutionLayerValidationFixture : public DepthwiseConvolutionLayerValidationGenericFixture { public: template void setup(TensorShape in_shape, Size2D kernel_size, PadStrideInfo pad_stride_info, Size2D dilation, unsigned int depth_multiplier, DataType data_type, DataLayout data_layout, ActivationLayerInfo act_info) { DepthwiseConvolutionLayerValidationGenericFixture::setup(in_shape, kernel_size, pad_stride_info, dilation, depth_multiplier, data_type, data_type, QuantizationInfo(), QuantizationInfo(), QuantizationInfo(), data_layout, act_info, mixed_layout); } }; template class DepthwiseConvolutionLayerNativeValidationFixture : 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) { _dilation = dilation; _depth_multiplier = depth_multiplier; _data_type = data_type; _data_layout = data_layout; _input_shape = TensorShape(width, height, channel, batch); _weights_shape = TensorShape(kernel_size.width, kernel_size.height, channel * _depth_multiplier); _biases_shape = TensorShape(_weights_shape.z()); if(padding_valid) { _conv_info = PadStrideInfo(); } else { _conv_info = calculate_same_pad(_input_shape, _weights_shape, PadStrideInfo(stride.width, stride.height), DataLayout::NCHW, _dilation); } } void configure_target() { TensorShape input_shape = _input_shape; TensorShape weights_shape = _weights_shape; if(_data_layout == DataLayout::NHWC) { permute(input_shape, PermutationVector(2U, 0U, 1U)); permute(weights_shape, PermutationVector(2U, 0U, 1U)); } // Create tensors _src = create_tensor(input_shape, _data_type, 1, QuantizationInfo(), _data_layout); _weights = create_tensor(weights_shape, _data_type, 1, QuantizationInfo(), _data_layout); _biases = create_tensor(_biases_shape, _data_type, 1, QuantizationInfo(), _data_layout); _target = create_tensor(TensorShape(), _data_type, 1, QuantizationInfo(), _data_layout); // Create Depthwise Convolution configure function const ConvolutionInfo info { _conv_info, _depth_multiplier, ActivationLayerInfo(), _dilation }; _dwc.configure(_src.info(), _weights.info(), _biases.info(), _target.info(), info); ARM_COMPUTE_ASSERT(_src.info()->is_resizable()); ARM_COMPUTE_ASSERT(_weights.info()->is_resizable()); ARM_COMPUTE_ASSERT(_biases.info()->is_resizable()); ARM_COMPUTE_ASSERT(_target.info()->is_resizable()); } void allocate_and_run_target() { add_padding_x({ &_src, &_weights, &_biases, &_target }, _data_layout); // Allocate tensors _src.allocator()->allocate(); _weights.allocator()->allocate(); _biases.allocator()->allocate(); _target.allocator()->allocate(); ARM_COMPUTE_ASSERT(!_src.info()->is_resizable()); ARM_COMPUTE_ASSERT(!_weights.info()->is_resizable()); ARM_COMPUTE_ASSERT(!_biases.info()->is_resizable()); ARM_COMPUTE_ASSERT(!_target.info()->is_resizable()); // Fill tensors fill(AccessorType(_src), 0); fill(AccessorType(_weights), 1); fill(AccessorType(_biases), 2); arm_compute::ITensorPack pack; pack.add_const_tensor(arm_compute::TensorType::ACL_SRC_0, &_src); pack.add_const_tensor(arm_compute::TensorType::ACL_SRC_1, &_weights); pack.add_const_tensor(arm_compute::TensorType::ACL_SRC_2, &_biases); pack.add_tensor(arm_compute::TensorType::ACL_DST, &_target); // Compute function _dwc.run(pack); } void compute_reference() { 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 ConvolutionInfo info{ _conv_info, _depth_multiplier, ActivationLayerInfo(), _dilation }; const TensorShape dst_shape = compute_depthwise_convolution_shape(TensorInfo(_input_shape, 1, _data_type), TensorInfo(_weights_shape, 1, _data_type), info); _reference = reference::depthwise_convolution(src, weights, biases, dst_shape, _conv_info, _depth_multiplier, _dilation); } 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 _target{}; SimpleTensor _reference{}; TensorType _src{}; TensorType _weights{}; TensorType _biases{}; FunctionType _dwc{}; TensorShape _input_shape{}; TensorShape _weights_shape{}; TensorShape _biases_shape{}; DataType _data_type{}; DataLayout _data_layout{}; PadStrideInfo _conv_info{}; Size2D _dilation{}; unsigned int _depth_multiplier{}; }; template class DepthwiseConvolutionLayerNativeConfigurableValidationFixture : 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 ActivationLayerInfo &act_info, unsigned int n0) { _dilation = dilation; _depth_multiplier = depth_multiplier; _data_type = data_type; _data_layout = data_layout; _act_info = act_info; _n0 = n0; _input_shape = TensorShape(width, height, channel, batch); _weights_shape = TensorShape(kernel_size.width, kernel_size.height, channel * _depth_multiplier); _biases_shape = TensorShape(_weights_shape.z()); if(padding_valid) { _conv_info = PadStrideInfo(); } else { _conv_info = calculate_same_pad(_input_shape, _weights_shape, PadStrideInfo(stride.width, stride.height), DataLayout::NCHW, _dilation); } } void configure_target() { TensorShape input_shape = _input_shape; TensorShape weights_shape = _weights_shape; if(_data_layout == DataLayout::NHWC) { permute(input_shape, PermutationVector(2U, 0U, 1U)); permute(weights_shape, PermutationVector(2U, 0U, 1U)); } // Create tensors _src = create_tensor(input_shape, _data_type, 1, QuantizationInfo(), _data_layout); _weights = create_tensor(weights_shape, _data_type, 1, QuantizationInfo(), _data_layout); _biases = create_tensor(_biases_shape, _data_type, 1, QuantizationInfo(), _data_layout); _target = create_tensor(TensorShape(), _data_type, 1, QuantizationInfo(), _data_layout); DWCWeightsKernelInfo dwc_weights_info; dwc_weights_info.n0 = _n0; DWCKernelInfo dwc_info; dwc_info.activation_info = _act_info; // Create Depthwise Convolution configure function _dwc.configure(&_src, &_weights, &_biases, &_target, dwc_weights_info, dwc_info, _conv_info, _depth_multiplier, _dilation); ARM_COMPUTE_ASSERT(_src.info()->is_resizable()); ARM_COMPUTE_ASSERT(_weights.info()->is_resizable()); ARM_COMPUTE_ASSERT(_biases.info()->is_resizable()); ARM_COMPUTE_ASSERT(_target.info()->is_resizable()); } void allocate_and_run_target() { add_padding_x({ &_src, &_weights, &_biases, &_target }, _data_layout); // Allocate tensors _src.allocator()->allocate(); _weights.allocator()->allocate(); _biases.allocator()->allocate(); _target.allocator()->allocate(); ARM_COMPUTE_ASSERT(!_src.info()->is_resizable()); ARM_COMPUTE_ASSERT(!_weights.info()->is_resizable()); ARM_COMPUTE_ASSERT(!_biases.info()->is_resizable()); ARM_COMPUTE_ASSERT(!_target.info()->is_resizable()); // Fill tensors fill(AccessorType(_src), 0); fill(AccessorType(_weights), 1); fill(AccessorType(_biases), 2); // Test Multi DataLayout graph cases, when the data layout changes after configure _src.info()->set_data_layout(_data_layout == DataLayout::NCHW ? DataLayout::NHWC : DataLayout::NCHW); _target.info()->set_data_layout(_data_layout == DataLayout::NCHW ? DataLayout::NHWC : DataLayout::NCHW); // Compute function _dwc.run(); // Reinstating original data layout for the test suite to properly check the values _target.info()->set_data_layout(_data_layout); } void compute_reference() { 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 ConvolutionInfo info{ _conv_info, _depth_multiplier, _act_info, _dilation }; const TensorShape dst_shape = compute_depthwise_convolution_shape(TensorInfo(_input_shape, 1, _data_type), TensorInfo(_weights_shape, 1, _data_type), info); _reference = reference::activation_layer(reference::depthwise_convolution(src, weights, biases, dst_shape, _conv_info, _depth_multiplier, _dilation), _act_info); } 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; } case DataType::F16: { arm_compute::utils::uniform_real_distribution_16bit distribution{ -1.0f, 1.0f }; library->fill(tensor, distribution, i); break; } default: library->fill_tensor_uniform(tensor, i); } } TensorType _target{}; SimpleTensor _reference{}; TensorType _src{}; TensorType _weights{}; TensorType _biases{}; FunctionType _dwc{}; TensorShape _input_shape{}; TensorShape _weights_shape{}; TensorShape _biases_shape{}; DataType _data_type{}; DataLayout _data_layout{}; PadStrideInfo _conv_info{}; ActivationLayerInfo _act_info{}; Size2D _dilation{}; unsigned int _depth_multiplier{}; unsigned int _n0{}; }; template class DepthwiseConvolutionLayerValidationQuantizedFixture : public DepthwiseConvolutionLayerValidationGenericFixture { public: template void setup(TensorShape in_shape, Size2D kernel_size, PadStrideInfo pad_stride_info, Size2D dilation, unsigned int depth_multiplier, DataType data_type, QuantizationInfo input_quantization_info, QuantizationInfo output_quantization_info, DataLayout data_layout, ActivationLayerInfo act_info) { DepthwiseConvolutionLayerValidationGenericFixture::setup(in_shape, kernel_size, pad_stride_info, dilation, depth_multiplier, data_type, data_type, input_quantization_info, input_quantization_info, output_quantization_info, data_layout, act_info, mixed_layout); } }; template class DepthwiseConvolutionLayerValidationQuantizedPerChannelFixture : public DepthwiseConvolutionLayerValidationGenericFixture { public: template void setup(TensorShape in_shape, Size2D kernel_size, PadStrideInfo pad_stride_info, Size2D dilation, unsigned int depth_multiplier, DataType input_data_type, DataType weights_data_type, QuantizationInfo input_quantization_info, QuantizationInfo output_quantization_info, DataLayout data_layout, ActivationLayerInfo act_info) { const float out_scale = output_quantization_info.uniform().scale; const float in_scale = input_quantization_info.uniform().scale; std::vector weights_scales{}; std::mt19937 gen(library->seed()); std::uniform_real_distribution dis(0.01f, out_scale / in_scale); for(size_t i = 0; i < in_shape.z() * depth_multiplier; ++i) { weights_scales.push_back(dis(gen)); } DepthwiseConvolutionLayerValidationGenericFixture::setup(in_shape, kernel_size, pad_stride_info, dilation, depth_multiplier, input_data_type, weights_data_type, input_quantization_info, QuantizationInfo(weights_scales), output_quantization_info, data_layout, act_info); } }; } // namespace validation } // namespace test } // namespace arm_compute #endif /* ARM_COMPUTE_TEST_DEPTHWISE_CONVOLUTION_FIXTURE */