From 678d83a5c3ec1b19ddb9df07a990262ce4bd65e1 Mon Sep 17 00:00:00 2001 From: Manuel Bottini Date: Mon, 7 Jan 2019 16:05:36 +0000 Subject: COMPMID-1838: Add 4D softmax support for NEON and achieve parity with CL Change-Id: I15c4a747cde2536b1caba2baf4ded9ca76e6dae2 Signed-off-by: Manuel Bottini Reviewed-on: https://review.mlplatform.org/487 Tested-by: Arm Jenkins Reviewed-by: VidhyaSudhan Loganathan --- .../runtime/NEON/functions/NESoftmaxLayer.h | 62 +++++--- src/runtime/NEON/functions/NESoftmaxLayer.cpp | 152 +++++++++++++++++--- tests/validation/NEON/SoftmaxLayer.cpp | 159 ++++++++++++++------- 3 files changed, 285 insertions(+), 88 deletions(-) diff --git a/arm_compute/runtime/NEON/functions/NESoftmaxLayer.h b/arm_compute/runtime/NEON/functions/NESoftmaxLayer.h index 3f5ec8e820..4932aeff5a 100644 --- a/arm_compute/runtime/NEON/functions/NESoftmaxLayer.h +++ b/arm_compute/runtime/NEON/functions/NESoftmaxLayer.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2018 ARM Limited. + * Copyright (c) 2017-2019 ARM Limited. * * SPDX-License-Identifier: MIT * @@ -25,6 +25,8 @@ #define __ARM_COMPUTE_NESOFTMAXLAYER_H__ #include "arm_compute/core/NEON/kernels/NEFillBorderKernel.h" +#include "arm_compute/core/NEON/kernels/NEFlattenLayerKernel.h" +#include "arm_compute/core/NEON/kernels/NEReshapeLayerKernel.h" #include "arm_compute/core/NEON/kernels/NESoftmaxLayerKernel.h" #include "arm_compute/runtime/IFunction.h" #include "arm_compute/runtime/MemoryGroup.h" @@ -49,6 +51,14 @@ class NESoftmaxLayer : public IFunction public: /** Constructor */ NESoftmaxLayer(std::shared_ptr memory_manager = nullptr); + /** Prevent instances of this class from being copied (As this class contains pointers) */ + NESoftmaxLayer(const NESoftmaxLayer &) = delete; + /** Default move constructor */ + NESoftmaxLayer(NESoftmaxLayer &&) = default; + /** Prevent instances of this class from being copied (As this class contains pointers) */ + NESoftmaxLayer &operator=(const NESoftmaxLayer &) = delete; + /** Default move assignment operator */ + NESoftmaxLayer &operator=(NESoftmaxLayer &&) = default; /** Set the input and output tensors. * * @param[in,out] input Source tensor. Data types supported: QASYMM8/F16/F32. If the width is not a @@ -56,24 +66,20 @@ public: * last value of each row to the nearest multiple. * @param[out] output Destination tensor. Data types supported: same as @p input. * @param[in] beta (Optional) A scaling factor for the exponent. - * @param[in] axis (Optional) Reduction axis. It has the purpose of squashing the first @p axis - * dimensions together. For instance, given a [4x4x4x4] image, + * @param[in] axis (Optional) Reduction axis. Defaults to 1. Must be in range [1, input_num_dimensions). + * It has the purpose of squashing the first @p axis dimensions together. For instance, given a [4x4x4x4] image, * when @p axis is 2, the Softmax reduction will be applied on each of the [4x4] planes of the input image. - * - * @note The value of @p axis must be always 1 for NEON */ void configure(ITensor *input, ITensor *output, float beta = 1.0f, size_t axis = 1); /** Static function to check if given info will lead to a valid configuration of @ref NESoftmaxLayer * - * @param[in] input Source tensor. Data types supported: QASYMM8/F16/F32. - * @param[in] output Destination tensor. Data types supported: same as @p input + * @param[in] input Source tensor info. Data types supported: QASYMM8/F16/F32. + * @param[in] output Destination tensor info. Data types supported: same as @p input * @param[in] beta (Optional) A scaling factor for the exponent. - * @param[in] axis (Optional) Reduction axis. It has the purpose of squashing the first @p axis - * dimensions together. For instance, given a [4x4x4x4] image, + * @param[in] axis (Optional) Reduction axis. Defaults to 1. Must be in range [1, input_num_dimensions). + * It has the purpose of squashing the first @p axis dimensions together. For instance, given a [4x4x4x4] image, * when @p axis is 2, the Softmax reduction will be applied on each of the [4x4] planes of the input image. * - * @note The value of @p axis must be always 1 for NEON - * * @return a status */ static Status validate(const ITensorInfo *input, const ITensorInfo *output, float beta = 1.0f, size_t axis = 1); @@ -82,12 +88,32 @@ public: void run() override; private: - MemoryGroup _memory_group; - NELogits1DMaxKernel _max_kernel; - NELogits1DSoftmaxKernel _softmax_kernel; - NEFillBorderKernel _fill_border_kernel; - Tensor _max; - Tensor _tmp; + /** Utility method to configure the kernels needed to flatten the input + * tensor. + * + * @note This function changes the internal state of this class. In particular, + * it initializes the kernel @p _flatten_kernel and the tensors @p _input_flat and + * @p _output_flat + * + * @param[in] input Original source tensor. + * @param[in] output Original destination tensor. + * @param[in] axis (Optional) Reduction axis. Defaults to 1. Must be in range [1, input_num_dimensions). + * It has the purpose of squashing the first @p axis dimensions together. For instance, given a [4x4x4x4] image, + * when @p axis is 2, the Softmax reduction will be applied on each of the [4x4] planes of the input image. + */ + void configure_reshape_input_kernel(const ITensor *input, const ITensor *output, size_t axis); + + MemoryGroup _memory_group; + NELogits1DMaxKernel _max_kernel; + NELogits1DSoftmaxKernel _softmax_kernel; + std::unique_ptr _flat_or_reshape_kernel_ptr; + NEFillBorderKernel _fill_border_kernel; + NEReshapeLayerKernel _reshape_kernel; + Tensor _max; + Tensor _tmp; + Tensor _input_flattened; + Tensor _output_flattened; + bool _needs_flattening; }; -} +} // namespace arm_compute #endif /* __ARM_COMPUTE_NESOFTMAXLAYER_H__ */ diff --git a/src/runtime/NEON/functions/NESoftmaxLayer.cpp b/src/runtime/NEON/functions/NESoftmaxLayer.cpp index 9be9e6817a..36b7d47d28 100644 --- a/src/runtime/NEON/functions/NESoftmaxLayer.cpp +++ b/src/runtime/NEON/functions/NESoftmaxLayer.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2018 ARM Limited. + * Copyright (c) 2017-2019 ARM Limited. * * SPDX-License-Identifier: MIT * @@ -25,54 +25,155 @@ #include "arm_compute/core/Helpers.h" #include "arm_compute/core/NEON/kernels/NESoftmaxLayerKernel.h" +#include "arm_compute/core/utils/misc/ShapeCalculator.h" #include "arm_compute/runtime/NEON/NEScheduler.h" +#include "utils/TypePrinter.h" #include -using namespace arm_compute; - +namespace arm_compute +{ NESoftmaxLayer::NESoftmaxLayer(std::shared_ptr memory_manager) - : _memory_group(std::move(memory_manager)), _max_kernel(), _softmax_kernel(), _fill_border_kernel(), _max(), _tmp() + : _memory_group(std::move(memory_manager)), _max_kernel(), _softmax_kernel(), _flat_or_reshape_kernel_ptr(nullptr), _fill_border_kernel(), _reshape_kernel(), _max(), _tmp(), _input_flattened(), + _output_flattened(), _needs_flattening(false) +{ +} + +void NESoftmaxLayer::configure_reshape_input_kernel(const ITensor *input, const ITensor *output, size_t axis) { + // Flatten the input + const TensorShape shape_flatten = misc::shape_calculator::compute_softmax_shape(input->info(), axis); + + // Initialize the flat input + _input_flattened.allocator()->init(input->info()->clone()->set_is_resizable(true).reset_padding().set_tensor_shape(shape_flatten)); + + // If we need to flatten the input, we can use NEFlattenKernel or NEReshapeKernel + // If flattening on the third axes, we use NEFlattenKernel. + // In all other cases we have to use NEReshapeKernel + if(axis != 3) + { + auto reshape_kernel_ptr = support::cpp14::make_unique(); + reshape_kernel_ptr->configure(input, &_input_flattened); + _flat_or_reshape_kernel_ptr = std::move(reshape_kernel_ptr); + } + else + { + auto flatten_kernel_ptr = support::cpp14::make_unique(); + flatten_kernel_ptr->configure(input, &_input_flattened); + _flat_or_reshape_kernel_ptr = std::move(flatten_kernel_ptr); + } + + // We need to init the output tensor here. Indeed, the reshape kernel expects + // both tensors to be already initialized + auto_init_if_empty(*output->info(), *input->info()->clone()); } void NESoftmaxLayer::configure(ITensor *input, ITensor *output, float beta, size_t axis) { + // Perform validation step ARM_COMPUTE_ERROR_ON_NULLPTR(input, output); - ARM_COMPUTE_UNUSED(axis); + ARM_COMPUTE_ERROR_THROW_ON(NESoftmaxLayer::validate(input->info(), output->info(), beta, axis)); - // Configure Kernels - _max_kernel.configure(input, &_max); - _fill_border_kernel.configure(input, _max_kernel.border_size(), BorderMode::REPLICATE); - _softmax_kernel.configure(input, &_max, output, beta, &_tmp); + // We don't need flattening only in the case the input is 2D and axis is 1 + _needs_flattening = axis != 1; + + // If we are dealing with a 4D tensor, we will: + // - Flatten the input, so that we end up with a [width*height*depth] * batches 2D tensor + // - Execute all the pipeline (reduction + normalization) on the flattened tensor + // - Reshape the flattened output into the real output + if(_needs_flattening) + { + // Add to the memory manager _input_flattened + _memory_group.manage(&_input_flattened); + + // Configure _flatten_kernel and _input_flattened + configure_reshape_input_kernel(input, output, axis); + } + + // We want to deal with a 2D input. Either it is the flattened version of the original input (4D case) + // or it is the original input case (2D case) + ITensor *input_2D = (_needs_flattening ? &_input_flattened : input); + + // Create intermediate tensors shapes + const TensorInfo input_info = input_2D->info()->clone()->reset_padding().set_is_resizable(true); + DataType tmp_data_type = is_data_type_quantized_asymmetric(input_2D->info()->data_type()) ? DataType::F32 : input_2D->info()->data_type(); + TensorInfo tensor_info_tmp(input_info.clone()->set_data_type(tmp_data_type)); // Init intermediate tensors - _max.allocator()->init(*_max.info()); - _tmp.allocator()->init(*_tmp.info()); + TensorShape max_sum_shape = input_2D->info()->tensor_shape(); + max_sum_shape.set(0, 1); + _max.allocator()->init(input_info.clone()->set_tensor_shape(max_sum_shape)); + _tmp.allocator()->init(tensor_info_tmp); // Manage intermediate buffers _memory_group.manage(&_max); _memory_group.manage(&_tmp); - // Allocate intermediate tensors + // Configure Kernels + _max_kernel.configure(input_2D, &_max); + if(_needs_flattening) + { + // Add to the memory manager _output_flattened + _memory_group.manage(&_output_flattened); + + // The normalization kernel stores the result in a flat output tensor + _softmax_kernel.configure(input_2D, &_max, &_output_flattened, beta, &_tmp); + _input_flattened.allocator()->allocate(); + + // Reshape the flat output into the requested (4D) output + _reshape_kernel.configure(&_output_flattened, output); + + // Allocate the intermediate flat tensors + _output_flattened.allocator()->allocate(); + } + else + { + // Softmax 2D case + _fill_border_kernel.configure(input_2D, _max_kernel.border_size(), BorderMode::REPLICATE); + _softmax_kernel.configure(input_2D, &_max, output, beta, &_tmp); + } + + // Allocate intermediate buffers _max.allocator()->allocate(); _tmp.allocator()->allocate(); } Status NESoftmaxLayer::validate(const ITensorInfo *input, const ITensorInfo *output, float beta, size_t axis) { - ARM_COMPUTE_RETURN_ERROR_ON_MSG(axis != 1, "Axis must be 1 for NEON"); - // Perform validation step ARM_COMPUTE_RETURN_ERROR_ON_NULLPTR(input, output); - ARM_COMPUTE_RETURN_ERROR_ON_MSG(input->num_dimensions() > 2, "Only 2D inputs are supported"); - - const TensorShape max_shape = TensorShape(input->tensor_shape()).set(0, 1); - const TensorInfo tensor_info_max_sum = TensorInfo(*input).set_tensor_shape(max_shape).reset_padding(); - const TensorInfo dont_care; + ARM_COMPUTE_RETURN_ERROR_ON_MSG(input->num_dimensions() > 4, "Only up to 4 dimensions are supported"); + ARM_COMPUTE_UNUSED(beta); + ARM_COMPUTE_RETURN_ERROR_ON(axis < 1 || input->num_dimensions() < axis); + + // Create intermediate tensor info + DataType tmp_data_type = input->data_type(); + const TensorInfo tensor_info_tmp(input->clone()->set_data_type(tmp_data_type).set_is_resizable(true)); + + TensorShape max_sum_shape = input->tensor_shape(); + max_sum_shape.set(0, 1); + const TensorInfo tensor_info_max_sum(input->clone()->set_tensor_shape(max_sum_shape).set_data_type(tmp_data_type).set_quantization_info(input->quantization_info()).set_is_resizable(true)); + const TensorInfo dont_care; + + const bool needs_flattening = (axis != 1); + + if(needs_flattening) + { + const TensorShape shape_flatten = misc::shape_calculator::compute_softmax_shape(input, axis); + TensorInfo tensor_info_flat(input->clone()->set_tensor_shape(shape_flatten).set_is_resizable(true)); + + if(axis != 3) + { + ARM_COMPUTE_RETURN_ON_ERROR(NEReshapeLayerKernel::validate(input, &tensor_info_flat)); + } + else + { + ARM_COMPUTE_RETURN_ON_ERROR(NEFlattenLayerKernel::validate(input, &tensor_info_flat)); + } + } ARM_COMPUTE_RETURN_ON_ERROR(NELogits1DMaxKernel::validate(input, &tensor_info_max_sum)); - ARM_COMPUTE_RETURN_ON_ERROR(NELogits1DSoftmaxKernel::validate(input, &tensor_info_max_sum, output, beta, &dont_care)); + ARM_COMPUTE_RETURN_ON_ERROR(NELogits1DSoftmaxKernel::validate(&tensor_info_tmp, &tensor_info_max_sum, output, beta, &dont_care)); return Status{}; } @@ -81,9 +182,20 @@ void NESoftmaxLayer::run() { _memory_group.acquire(); + if(_needs_flattening) + { + NEScheduler::get().schedule(_flat_or_reshape_kernel_ptr.get(), Window::DimY); + } + NEScheduler::get().schedule(&_fill_border_kernel, Window::DimY); NEScheduler::get().schedule(&_max_kernel, Window::DimY); NEScheduler::get().schedule(&_softmax_kernel, Window::DimY); + if(_needs_flattening) + { + NEScheduler::get().schedule(&_reshape_kernel, Window::DimY); + } + _memory_group.release(); } +} // namespace arm_compute \ No newline at end of file diff --git a/tests/validation/NEON/SoftmaxLayer.cpp b/tests/validation/NEON/SoftmaxLayer.cpp index 21c77e7059..8f91b51d9a 100644 --- a/tests/validation/NEON/SoftmaxLayer.cpp +++ b/tests/validation/NEON/SoftmaxLayer.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2018 ARM Limited. + * Copyright (c) 2017-2019 ARM Limited. * * SPDX-License-Identifier: MIT * @@ -44,10 +44,7 @@ namespace { /** Tolerance for float operations */ constexpr AbsoluteTolerance tolerance_f32(0.000001f); -#ifdef __ARM_FEATURE_FP16_VECTOR_ARITHMETIC -constexpr RelativeTolerance rel_tolerance_f16(0.1f); -constexpr AbsoluteTolerance abs_tolerance_f16(0.01f); -#endif /* __ARM_FEATURE_FP16_VECTOR_ARITHMETIC*/ +RelativeTolerance tolerance_f16(half(0.2)); /** Tolerance for quantized operations */ constexpr AbsoluteTolerance tolerance_qasymm8(1); @@ -65,11 +62,13 @@ const auto CNNDataTypes = framework::dataset::make("DataType", TEST_SUITE(NEON) TEST_SUITE(SoftmaxLayer) -DATA_TEST_CASE(Configuration, framework::DatasetMode::ALL, combine(concat(datasets::SoftmaxLayerSmallShapes(), datasets::SoftmaxLayerLargeShapes()), CNNDataTypes), shape, data_type) +DATA_TEST_CASE(Configuration, framework::DatasetMode::ALL, combine(concat(datasets::Small2DShapes(), datasets::Medium2DShapes()), CNNDataTypes), shape, data_type) { + const QuantizationInfo quantization_info = is_data_type_quantized_asymmetric(data_type) ? QuantizationInfo(1.f / 255.f, 0) : QuantizationInfo(); + // Create tensors - Tensor src = create_tensor(shape, data_type, 1); - Tensor dst = create_tensor(shape, data_type, 1); + Tensor src = create_tensor(shape, data_type, 1, quantization_info); + Tensor dst = create_tensor(shape, data_type, 1, QuantizationInfo(1.f / 256.f, 0)); ARM_COMPUTE_EXPECT(src.info()->is_resizable(), framework::LogLevel::ERRORS); ARM_COMPUTE_EXPECT(dst.info()->is_resizable(), framework::LogLevel::ERRORS); @@ -83,31 +82,67 @@ DATA_TEST_CASE(Configuration, framework::DatasetMode::ALL, combine(concat(datase validate(src.info()->valid_region(), valid_region); validate(dst.info()->valid_region(), valid_region); - // Validate padding - const int step = 16 / data_size_from_type(data_type); - const PaddingSize padding = PaddingCalculator(shape.x(), step).required_padding(); - validate(src.info()->padding(), padding); - validate(dst.info()->padding(), PaddingSize()); + // NESoftmaxLayer configures the paddings only in the 2D case + if(shape.num_dimensions() <= 2) + { + // Validate padding + const int step = 16 / data_size_from_type(data_type); + const PaddingSize padding = PaddingCalculator(shape.x(), step).required_padding(); + validate(src.info()->padding(), padding); + validate(dst.info()->padding(), PaddingSize()); + } } // *INDENT-OFF* // clang-format off -DATA_TEST_CASE(Validate, framework::DatasetMode::ALL, zip(zip( - framework::dataset::make("InputInfo", { TensorInfo(TensorShape(27U, 13U), 1, DataType::F32), // Mismatching data types - TensorInfo(TensorShape(27U, 13U), 1, DataType::F32), // Mismatching shapes - TensorInfo(TensorShape(32U, 16U, 2U), 1, DataType::F32), // Invalid input dimensionality - TensorInfo(TensorShape(32U, 16U), 1, DataType::F32), - }), - framework::dataset::make("OutputInfo",{ TensorInfo(TensorShape(27U, 13U), 1, DataType::F16), - TensorInfo(TensorShape(27U, 11U), 1, DataType::F32), - TensorInfo(TensorShape(27U, 11U), 1, DataType::F32), - TensorInfo(TensorShape(32U, 16U), 1, DataType::F32), - })), - framework::dataset::make("Expected", { false, false, false, true })), - input_info, output_info, expected) -{ - bool is_valid = bool(NESoftmaxLayer::validate(&input_info.clone()->set_is_resizable(false), &output_info.clone()->set_is_resizable(false))); - ARM_COMPUTE_EXPECT(is_valid == expected, framework::LogLevel::ERRORS); +DATA_TEST_CASE(Validate, framework::DatasetMode::ALL, zip(zip(zip(zip( + framework::dataset::make("InputInfo", { TensorInfo(TensorShape(27U, 13U), 1, DataType::F32), // Mismatching data types + TensorInfo(TensorShape(27U, 13U), 1, DataType::F32), // Mismatching shapes + TensorInfo(TensorShape(27U, 13U), 1, DataType::QASYMM8, // Invalid output quantization info + QuantizationInfo(1.f/256, 12)), + TensorInfo(TensorShape(27U, 13U), 1, DataType::F32), // Window shrink + TensorInfo(TensorShape(27U, 13U, 2U), 1, DataType::F32),// Invalid input dimensionality + TensorInfo(TensorShape(32U, 13U), 1, DataType::F32), + TensorInfo(TensorShape(32U, 13U), 1, DataType::QASYMM8, + QuantizationInfo(1.f/256, 12)), + TensorInfo(TensorShape(32U, 13U), 1, DataType::QASYMM8, //Invalid axis value + QuantizationInfo(1.f/256, 12)), + }), + framework::dataset::make("OutputInfo",{ TensorInfo(TensorShape(27U, 13U), 1, DataType::F16), + TensorInfo(TensorShape(27U, 11U), 1, DataType::F32), + TensorInfo(TensorShape(27U, 13U), 1, DataType::QASYMM8, + QuantizationInfo(1.f/256, 12)), + TensorInfo(TensorShape(27U, 13U), 1, DataType::F32), + TensorInfo(TensorShape(27U, 13U, 2U), 1, DataType::F32), + TensorInfo(TensorShape(32U, 13U), 1, DataType::F32), + TensorInfo(TensorShape(32U, 13U), 1, DataType::QASYMM8, + QuantizationInfo(1.f/256, 0)), + TensorInfo(TensorShape(32U, 13U), 1, DataType::QASYMM8, + QuantizationInfo(1.f/256, 0)), + })), + framework::dataset::make("beta", { 1.0, + 2.0, + 1.0, + 2.0, + 1.0, + 2.0, + 1.0, + 2.0, + 1.0, + })), + framework::dataset::make("axis", { 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + })), + framework::dataset::make("Expected", { false, false, false, false, false, true, true, false })), + input_info, output_info, beta, axis, expected) +{ + ARM_COMPUTE_EXPECT(bool(NESoftmaxLayer::validate(&input_info.clone()->set_is_resizable(false), &output_info.clone()->set_is_resizable(false), beta, axis)) == expected, framework::LogLevel::ERRORS); } // clang-format on // *INDENT-ON* @@ -118,13 +153,21 @@ using NESoftmaxLayerFixture = SoftmaxValidationFixture, framework::DatasetMode::PRECOMMIT, combine(combine(combine(datasets::SoftmaxLayerSmallShapes(), +FIXTURE_DATA_TEST_CASE(RunSmall, NESoftmaxLayerFixture, framework::DatasetMode::PRECOMMIT, combine(combine(combine(datasets::Small4DShapes(), framework::dataset::make("DataType", DataType::F16)), framework::dataset::make("Beta", { 1.0f, 2.0f })), framework::dataset::make("Axis", { 1 }))) { // Validate output - validate(Accessor(_target), _reference, rel_tolerance_f16, 0.f, abs_tolerance_f16); + validate(Accessor(_target), _reference, tolerance_f16); +} +FIXTURE_DATA_TEST_CASE(RunSmall4D, NESoftmaxLayerFixture, framework::DatasetMode::PRECOMMIT, combine(combine(combine(datasets::Small4DShapes(), + framework::dataset::make("DataType", DataType::F32)), + framework::dataset::make("Beta", { 1.0f, 2.0f })), + framework::dataset::make("Axis", { 1, 2, 3 }))) +{ + // Validate output + validate(Accessor(_target), _reference, tolerance_f32); } FIXTURE_DATA_TEST_CASE(RunLarge, NESoftmaxLayerFixture, framework::DatasetMode::NIGHTLY, combine(combine(combine(datasets::SoftmaxLayerLargeShapes(), framework::dataset::make("DataType", DataType::F16)), @@ -132,16 +175,24 @@ FIXTURE_DATA_TEST_CASE(RunLarge, NESoftmaxLayerFixture, framework::Dataset framework::dataset::make("Axis", { 1 }))) { // Validate output - validate(Accessor(_target), _reference, rel_tolerance_f16, 0.f, abs_tolerance_f16); + validate(Accessor(_target), _reference, tolerance_f16); } -TEST_SUITE_END() -#endif /* __ARM_FEATURE_FP16_VECTOR_ARITHMETIC */ +TEST_SUITE_END() //FP16 +#endif /* __ARM_FEATURE_FP16_VECTOR_ARITHMETIC */ TEST_SUITE(FP32) -FIXTURE_DATA_TEST_CASE(RunSmall, NESoftmaxLayerFixture, framework::DatasetMode::PRECOMMIT, combine(combine(combine(datasets::SoftmaxLayerSmallShapes(), - framework::dataset::make("DataType", DataType::F32)), - framework::dataset::make("Beta", { 1.0f, 2.0f })), - framework::dataset::make("Axis", { 1 }))) +FIXTURE_DATA_TEST_CASE(RunSmall2D, NESoftmaxLayerFixture, framework::DatasetMode::PRECOMMIT, combine(combine(combine(datasets::SoftmaxLayerSmallShapes(), + framework::dataset::make("DataType", DataType::F32)), + framework::dataset::make("Beta", { 1.0f, 2.0f })), + framework::dataset::make("Axis", { 1 }))) +{ + // Validate output + validate(Accessor(_target), _reference, tolerance_f32); +} +FIXTURE_DATA_TEST_CASE(RunSmall4D, NESoftmaxLayerFixture, framework::DatasetMode::PRECOMMIT, combine(combine(combine(datasets::Small4DShapes(), + framework::dataset::make("DataType", DataType::F32)), + framework::dataset::make("Beta", { 1.0f, 2.0f })), + framework::dataset::make("Axis", { 1, 2, 3 }))) { // Validate output validate(Accessor(_target), _reference, tolerance_f32); @@ -154,19 +205,28 @@ FIXTURE_DATA_TEST_CASE(RunLarge, NESoftmaxLayerFixture, framework::Datase // Validate output validate(Accessor(_target), _reference, tolerance_f32); } -TEST_SUITE_END() -TEST_SUITE_END() +TEST_SUITE_END() //FP32 +TEST_SUITE_END() //Float template using NESoftmaxLayerQuantizedFixture = SoftmaxValidationQuantizedFixture; TEST_SUITE(Quantized) TEST_SUITE(QASYMM8) -FIXTURE_DATA_TEST_CASE(RunSmall, NESoftmaxLayerQuantizedFixture, framework::DatasetMode::ALL, combine(combine(combine(datasets::SoftmaxLayerSmallShapes(), - framework::dataset::make("DataType", DataType::QASYMM8)), - combine(framework::dataset::make("QuantizationInfo", { QuantizationInfo(0.5f, -10) }), - framework::dataset::make("Beta", { 1.0f, 2.f }))), - framework::dataset::make("Axis", { 1 }))) +FIXTURE_DATA_TEST_CASE(RunSmall2D, NESoftmaxLayerQuantizedFixture, framework::DatasetMode::ALL, combine(combine(combine(datasets::SoftmaxLayerSmallShapes(), + framework::dataset::make("DataType", DataType::QASYMM8)), + combine(framework::dataset::make("QuantizationInfo", { QuantizationInfo(0.5f, -10) }), + framework::dataset::make("Beta", { 1.0f, 2.f }))), + framework::dataset::make("Axis", { 1 }))) +{ + // Validate output + validate(Accessor(_target), _reference, tolerance_qasymm8); +} +FIXTURE_DATA_TEST_CASE(RunSmall4D, NESoftmaxLayerQuantizedFixture, framework::DatasetMode::ALL, combine(combine(combine(datasets::Small4DShapes(), + framework::dataset::make("DataType", DataType::QASYMM8)), + combine(framework::dataset::make("QuantizationInfo", { QuantizationInfo(0.5f, -10) }), + framework::dataset::make("Beta", { 1.0f, 2.f }))), + framework::dataset::make("Axis", { 1, 2, 3 }))) { // Validate output validate(Accessor(_target), _reference, tolerance_qasymm8); @@ -180,12 +240,11 @@ FIXTURE_DATA_TEST_CASE(RunLarge, NESoftmaxLayerQuantizedFixture, framew // Validate output validate(Accessor(_target), _reference, tolerance_qasymm8); } +TEST_SUITE_END() //QASYMM8 +TEST_SUITE_END() //Quantized -TEST_SUITE_END() -TEST_SUITE_END() - -TEST_SUITE_END() -TEST_SUITE_END() +TEST_SUITE_END() //SoftmaxLayer +TEST_SUITE_END() //NEON } // namespace validation } // namespace test } // namespace arm_compute -- cgit v1.2.1