From 4715cf9da26c4e914b9528f736e77d6773285169 Mon Sep 17 00:00:00 2001 From: Sang-Hoon Park Date: Wed, 8 Jan 2020 16:02:47 +0000 Subject: COMPMID-2760: add support for QASYMM8_SIGNED to CLGEMMConvolutionLayer Signed-off-by: Sang-Hoon Park Change-Id: I55ab81d0f96c78af0396652cacf6640fc98ef3c2 Reviewed-on: https://review.mlplatform.org/c/2584 Reviewed-by: Michele Di Giorgio Comments-Addressed: Arm Jenkins Tested-by: Arm Jenkins --- arm_compute/core/Utils.h | 10 ++++++++++ src/core/Utils.cpp | 15 +++++++++++++++ src/runtime/CL/functions/CLGEMMConvolutionLayer.cpp | 14 +++----------- src/runtime/NEON/functions/NEGEMMConvolutionLayer.cpp | 16 +++------------- tests/validation/CL/ConvolutionLayer.cpp | 17 ++++++++++++++++- 5 files changed, 47 insertions(+), 25 deletions(-) diff --git a/arm_compute/core/Utils.h b/arm_compute/core/Utils.h index 3a8f6b319f..a2c18bb205 100644 --- a/arm_compute/core/Utils.h +++ b/arm_compute/core/Utils.h @@ -977,6 +977,16 @@ QuantizationInfo get_softmax_output_quantization_info(DataType input_type, bool */ float calculate_resize_ratio(size_t input_size, size_t output_size, bool align_corners = false); +/** Returns a pair of minimum and maximum values for a quantized activation + * + * @param[in] act_info The information for activation + * @param[in] data_type The used data type + * @param[in] oq_info The output quantization information + * + * @return The pair with minimum and maximum values + */ +std::pair get_quantized_activation_min_max(ActivationLayerInfo act_info, DataType data_type, UniformQuantizationInfo oq_info); + /** Convert a tensor format into a string. * * @param[in] format @ref Format to be translated to string. diff --git a/src/core/Utils.cpp b/src/core/Utils.cpp index 30e5b66540..f930804ce3 100644 --- a/src/core/Utils.cpp +++ b/src/core/Utils.cpp @@ -471,6 +471,21 @@ float arm_compute::calculate_resize_ratio(size_t input_size, size_t output_size, return static_cast(in) / static_cast(out); } +std::pair arm_compute::get_quantized_activation_min_max(ActivationLayerInfo act_info, DataType data_type, UniformQuantizationInfo oq_info) +{ + const bool is_qasymm8_signed = is_data_type_quantized_asymmetric_signed(data_type); + const auto a = act_info.a(); + const auto b = act_info.b(); + const int a_int = is_qasymm8_signed ? quantize_qasymm8_signed(a, oq_info) : quantize_qasymm8(a, oq_info); + const int b_int = is_qasymm8_signed ? quantize_qasymm8_signed(b, oq_info) : quantize_qasymm8(b, oq_info); + const auto type_max_value = std::get<1>(get_min_max(data_type)).get(); + + const int32_t min_activation = act_info.activation() != ActivationLayerInfo::ActivationFunction::LU_BOUNDED_RELU ? oq_info.offset : b_int; + const int32_t max_activation = act_info.activation() == ActivationLayerInfo::ActivationFunction::RELU ? type_max_value : a_int; + + return std::make_pair(min_activation, max_activation); +} + #ifdef ARM_COMPUTE_ASSERTS_ENABLED void arm_compute::print_consecutive_elements(std::ostream &s, DataType dt, const uint8_t *ptr, unsigned int n, int stream_width, const std::string &element_delim) { diff --git a/src/runtime/CL/functions/CLGEMMConvolutionLayer.cpp b/src/runtime/CL/functions/CLGEMMConvolutionLayer.cpp index dbb68619db..682812b1c8 100644 --- a/src/runtime/CL/functions/CLGEMMConvolutionLayer.cpp +++ b/src/runtime/CL/functions/CLGEMMConvolutionLayer.cpp @@ -345,11 +345,7 @@ void CLGEMMConvolutionLayer::configure(const ICLTensor *input, const ICLTensor * { if(supported_acts.count(act_info.activation()) != 0) { - const int a_const_int = quantize_qasymm8(act_info.a(), output_quant_info); - const int b_const_int = quantize_qasymm8(act_info.b(), output_quant_info); - - min_activation = act_info.activation() != ActivationLayerInfo::ActivationFunction::LU_BOUNDED_RELU ? output_quant_info.offset : b_const_int; - max_activation = act_info.activation() == ActivationLayerInfo::ActivationFunction::RELU ? 255 : a_const_int; + std::tie(min_activation, max_activation) = get_quantized_activation_min_max(act_info, data_type, output_quant_info); } else { @@ -402,7 +398,7 @@ Status CLGEMMConvolutionLayer::validate(const ITensorInfo *input, const ITensorI { ARM_COMPUTE_RETURN_ERROR_ON_NULLPTR(input, weights, output); ARM_COMPUTE_RETURN_ERROR_ON_MSG(weights_info.are_reshaped(), "Weights already reshaped are not supported!"); - ARM_COMPUTE_RETURN_ERROR_ON_DATA_TYPE_CHANNEL_NOT_IN(input, 1, DataType::QASYMM8, DataType::F16, DataType::F32); + ARM_COMPUTE_RETURN_ERROR_ON_DATA_TYPE_CHANNEL_NOT_IN(input, 1, DataType::QASYMM8, DataType::QASYMM8_SIGNED, DataType::F16, DataType::F32); const bool is_quantized_per_channel = is_data_type_quantized_per_channel(weights->data_type()); if(is_quantized_per_channel) @@ -559,11 +555,7 @@ Status CLGEMMConvolutionLayer::validate(const ITensorInfo *input, const ITensorI { if(supported_acts.count(act_info.activation()) != 0) { - const int a_const_int = quantize_qasymm8(act_info.a(), output_quant_info); - const int b_const_int = quantize_qasymm8(act_info.b(), output_quant_info); - - min_activation = act_info.activation() != ActivationLayerInfo::ActivationFunction::LU_BOUNDED_RELU ? output_quant_info.offset : b_const_int; - max_activation = act_info.activation() == ActivationLayerInfo::ActivationFunction::RELU ? 255 : a_const_int; + std::tie(min_activation, max_activation) = get_quantized_activation_min_max(act_info, data_type, output_quant_info); } else { diff --git a/src/runtime/NEON/functions/NEGEMMConvolutionLayer.cpp b/src/runtime/NEON/functions/NEGEMMConvolutionLayer.cpp index 920917a58b..5701d60208 100644 --- a/src/runtime/NEON/functions/NEGEMMConvolutionLayer.cpp +++ b/src/runtime/NEON/functions/NEGEMMConvolutionLayer.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 ARM Limited. + * Copyright (c) 2017-2020 ARM Limited. * * SPDX-License-Identifier: MIT * @@ -140,12 +140,7 @@ void NEGEMMConvolutionLayer::configure_mm(const ITensor *input, const ITensor *w if(supported_acts.count(act_info.activation()) != 0) { - const bool is_quantized_signed = is_data_type_quantized_asymmetric_signed(data_type); - const int a_const_int = is_quantized_signed ? quantize_qasymm8_signed(act_info.a(), uoqinfo) : quantize_qasymm8(act_info.a(), uoqinfo); - const int b_const_int = is_quantized_signed ? quantize_qasymm8_signed(act_info.b(), uoqinfo) : quantize_qasymm8(act_info.b(), uoqinfo); - - min_activation = act_info.activation() != ActivationLayerInfo::ActivationFunction::LU_BOUNDED_RELU ? uoqinfo.offset : b_const_int; - max_activation = act_info.activation() == ActivationLayerInfo::ActivationFunction::RELU ? max_activation : a_const_int; + std::tie(min_activation, max_activation) = get_quantized_activation_min_max(act_info, data_type, uoqinfo); } GEMMLowpOutputStageInfo output_info; @@ -203,12 +198,7 @@ Status NEGEMMConvolutionLayer::validate_mm(const ITensorInfo *input, const ITens }; if(is_activation_enabled && supported_acts.count(act_info.activation()) != 0) { - const bool is_quantized_signed = is_data_type_quantized_asymmetric_signed(data_type); - const int a_const_int = is_quantized_signed ? quantize_qasymm8_signed(act_info.a(), uoqinfo) : quantize_qasymm8(act_info.a(), uoqinfo); - const int b_const_int = is_quantized_signed ? quantize_qasymm8_signed(act_info.b(), uoqinfo) : quantize_qasymm8(act_info.b(), uoqinfo); - - min_activation = act_info.activation() != ActivationLayerInfo::ActivationFunction::LU_BOUNDED_RELU ? uoqinfo.offset : b_const_int; - max_activation = act_info.activation() == ActivationLayerInfo::ActivationFunction::RELU ? max_activation : a_const_int; + std::tie(min_activation, max_activation) = get_quantized_activation_min_max(act_info, data_type, uoqinfo); } GEMMLowpOutputStageInfo output_info; diff --git a/tests/validation/CL/ConvolutionLayer.cpp b/tests/validation/CL/ConvolutionLayer.cpp index 5ee5d849c3..130af572ff 100644 --- a/tests/validation/CL/ConvolutionLayer.cpp +++ b/tests/validation/CL/ConvolutionLayer.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 ARM Limited. + * Copyright (c) 2017-2020 ARM Limited. * * SPDX-License-Identifier: MIT * @@ -57,6 +57,7 @@ const auto CNNDataTypes = framework::dataset::make("DataType", DataType::F16, DataType::F32, DataType::QASYMM8, + DataType::QASYMM8_SIGNED, }); /** Grouped CNN data types */ @@ -319,6 +320,20 @@ FIXTURE_DATA_TEST_CASE(RunLarge, CLGEMMConvolutionLayerQuantizedFixture validate(CLAccessor(_target), _reference, tolerance_qasymm8); } TEST_SUITE_END() // QASYMM8 +TEST_SUITE(QASYMM8_SIGNED) + +FIXTURE_DATA_TEST_CASE(RunSmall, CLGEMMConvolutionLayerQuantizedFixture, framework::DatasetMode::PRECOMMIT, + combine(combine(combine(combine(combine(datasets::SmallConvolutionLayerReducedDataset(), + framework::dataset::make("ReshapeWeights", { true })), + framework::dataset::make("DataType", DataType::QASYMM8_SIGNED)), + framework::dataset::make("DataLayout", { DataLayout::NCHW, DataLayout::NHWC })), + QuantizationData), + QuantizedActivationFunctionsSmallDataset)) +{ + // Validate output + validate(CLAccessor(_target), _reference, tolerance_qasymm8); +} +TEST_SUITE_END() // QASYMM8_SIGNED TEST_SUITE(QSYMM8_PER_CHANNEL) FIXTURE_DATA_TEST_CASE(RunSmall, CLGEMMConvolutionLayerQuantizedPerChannelFixture, framework::DatasetMode::PRECOMMIT, -- cgit v1.2.1