From 2fc70c5f7bebd95da7c718907011c92fd29e3603 Mon Sep 17 00:00:00 2001 From: Matteo Martincigh Date: Wed, 5 Jun 2019 14:12:48 +0100 Subject: IVGCVSW-3226 Refactor the reference normalization workload * Refactored RefNormalizationFloat32Workload into RefNormalizationWorkload * Added ref support of Uint8 norm workloads * Added workload unit tests for Uint8 Change-Id: I063ce919c267e02a32e739848e49d75fd98a5eb6 Signed-off-by: Matteo Martincigh --- src/backends/backendsCommon/WorkloadData.cpp | 17 ++ .../backendsCommon/test/WorkloadDataValidation.cpp | 2 +- src/backends/reference/RefLayerSupport.cpp | 27 ++- src/backends/reference/RefWorkloadFactory.cpp | 2 +- src/backends/reference/backend.mk | 2 +- .../reference/test/RefCreateWorkloadTests.cpp | 18 +- src/backends/reference/workloads/CMakeLists.txt | 4 +- .../workloads/RefNormalizationFloat32Workload.cpp | 190 ------------------- .../workloads/RefNormalizationFloat32Workload.hpp | 21 --- .../workloads/RefNormalizationWorkload.cpp | 210 +++++++++++++++++++++ .../workloads/RefNormalizationWorkload.hpp | 23 +++ src/backends/reference/workloads/RefWorkloads.hpp | 2 +- 12 files changed, 292 insertions(+), 226 deletions(-) delete mode 100644 src/backends/reference/workloads/RefNormalizationFloat32Workload.cpp delete mode 100644 src/backends/reference/workloads/RefNormalizationFloat32Workload.hpp create mode 100644 src/backends/reference/workloads/RefNormalizationWorkload.cpp create mode 100644 src/backends/reference/workloads/RefNormalizationWorkload.hpp diff --git a/src/backends/backendsCommon/WorkloadData.cpp b/src/backends/backendsCommon/WorkloadData.cpp index a95abf12ae..9482136b59 100644 --- a/src/backends/backendsCommon/WorkloadData.cpp +++ b/src/backends/backendsCommon/WorkloadData.cpp @@ -608,6 +608,23 @@ void NormalizationQueueDescriptor::Validate(const WorkloadInfo& workloadInfo) co { ValidateNumInputs(workloadInfo, "NormalizationQueueDescriptor", 1); ValidateNumOutputs(workloadInfo, "NormalizationQueueDescriptor", 1); + + // Check the supported data types + std::vector supportedTypes = + { + DataType::Float16, + DataType::Float32, + DataType::QuantisedAsymm8 + }; + + ValidateDataTypes(workloadInfo.m_InputTensorInfos[0], + supportedTypes, + "NormalizationQueueDescriptor"); + + ValidateDataTypes(workloadInfo.m_OutputTensorInfos[0], + { workloadInfo.m_InputTensorInfos[0].GetDataType() }, + "NormalizationQueueDescriptor"); + ValidateTensorShapesMatch(workloadInfo.m_InputTensorInfos[0], workloadInfo.m_OutputTensorInfos[0], "NormalizationQueueDescriptor", diff --git a/src/backends/backendsCommon/test/WorkloadDataValidation.cpp b/src/backends/backendsCommon/test/WorkloadDataValidation.cpp index a2e049d06d..7c7af2ddce 100644 --- a/src/backends/backendsCommon/test/WorkloadDataValidation.cpp +++ b/src/backends/backendsCommon/test/WorkloadDataValidation.cpp @@ -173,7 +173,7 @@ BOOST_AUTO_TEST_CASE(NormalizationQueueDescriptor_Validate_WrongInputHeight) invalidData.m_Parameters.m_K = kappa; //Invalid argument exception is expected, because input height != output height. - BOOST_CHECK_THROW(RefNormalizationFloat32Workload(invalidData, invalidInfo), armnn::InvalidArgumentException); + BOOST_CHECK_THROW(RefNormalizationWorkload(invalidData, invalidInfo), armnn::InvalidArgumentException); } BOOST_AUTO_TEST_CASE(SplitterQueueDescriptor_Validate_WrongWindow) diff --git a/src/backends/reference/RefLayerSupport.cpp b/src/backends/reference/RefLayerSupport.cpp index f177385837..60536081be 100644 --- a/src/backends/reference/RefLayerSupport.cpp +++ b/src/backends/reference/RefLayerSupport.cpp @@ -959,12 +959,29 @@ bool RefLayerSupport::IsNormalizationSupported(const TensorInfo& input, const NormalizationDescriptor& descriptor, Optional reasonIfUnsupported) const { - ignore_unused(output); ignore_unused(descriptor); - return IsSupportedForDataTypeRef(reasonIfUnsupported, - input.GetDataType(), - &TrueFunc<>, - &FalseFuncU8<>); + + // Define supported types + std::array supportedTypes = + { + DataType::Float16, + DataType::Float32, + DataType::QuantisedAsymm8 + }; + + bool supported = true; + + supported &= CheckSupportRule(TypeAnyOf(input, supportedTypes), reasonIfUnsupported, + "Reference normalization: input type not supported."); + + supported &= CheckSupportRule(TypeAnyOf(output, supportedTypes), reasonIfUnsupported, + "Reference normalization: output type not supported."); + + supported &= CheckSupportRule(ShapesAreSameTotalSize(input, output), reasonIfUnsupported, + "Reference normalization: input and output shapes have different " + "num total elements."); + + return supported; } bool RefLayerSupport::IsOutputSupported(const TensorInfo& output, diff --git a/src/backends/reference/RefWorkloadFactory.cpp b/src/backends/reference/RefWorkloadFactory.cpp index 76139026cc..319a620d2b 100644 --- a/src/backends/reference/RefWorkloadFactory.cpp +++ b/src/backends/reference/RefWorkloadFactory.cpp @@ -189,7 +189,7 @@ std::unique_ptr RefWorkloadFactory::CreateDetectionPostProcess( std::unique_ptr RefWorkloadFactory::CreateNormalization( const NormalizationQueueDescriptor& descriptor, const WorkloadInfo& info) const { - return MakeWorkload(descriptor, info); + return std::make_unique(descriptor, info); } std::unique_ptr RefWorkloadFactory::CreateAddition(const AdditionQueueDescriptor& descriptor, diff --git a/src/backends/reference/backend.mk b/src/backends/reference/backend.mk index 6f951130f3..2822c305f5 100644 --- a/src/backends/reference/backend.mk +++ b/src/backends/reference/backend.mk @@ -47,7 +47,7 @@ BACKEND_SOURCES := \ workloads/RefLstmWorkload.cpp \ workloads/RefMeanFloat32Workload.cpp \ workloads/RefMeanUint8Workload.cpp \ - workloads/RefNormalizationFloat32Workload.cpp \ + workloads/RefNormalizationWorkload.cpp \ workloads/RefPadWorkload.cpp \ workloads/RefPermuteWorkload.cpp \ workloads/RefPooling2dWorkload.cpp \ diff --git a/src/backends/reference/test/RefCreateWorkloadTests.cpp b/src/backends/reference/test/RefCreateWorkloadTests.cpp index 8216ed5a99..3da9de9263 100644 --- a/src/backends/reference/test/RefCreateWorkloadTests.cpp +++ b/src/backends/reference/test/RefCreateWorkloadTests.cpp @@ -372,14 +372,24 @@ static void RefCreateNormalizationWorkloadTest(DataLayout dataLayout) CheckInputOutput(std::move(workload), TensorInfo(inputShape, DataType), TensorInfo(outputShape, DataType)); } -BOOST_AUTO_TEST_CASE(CreateRefNormalizationNchwWorkload) +BOOST_AUTO_TEST_CASE(CreateRefNormalizationFloat32NchwWorkload) { - RefCreateNormalizationWorkloadTest(DataLayout::NCHW); + RefCreateNormalizationWorkloadTest(DataLayout::NCHW); } -BOOST_AUTO_TEST_CASE(CreateRefNormalizationNhwcWorkload) +BOOST_AUTO_TEST_CASE(CreateRefNormalizationFloat32NhwcWorkload) { - RefCreateNormalizationWorkloadTest(DataLayout::NHWC); + RefCreateNormalizationWorkloadTest(DataLayout::NHWC); +} + +BOOST_AUTO_TEST_CASE(CreateRefNormalizationUint8NchwWorkload) +{ + RefCreateNormalizationWorkloadTest(DataLayout::NCHW); +} + +BOOST_AUTO_TEST_CASE(CreateRefNormalizationUint8NhwcWorkload) +{ + RefCreateNormalizationWorkloadTest(DataLayout::NHWC); } template diff --git a/src/backends/reference/workloads/CMakeLists.txt b/src/backends/reference/workloads/CMakeLists.txt index 82502c513c..9d5c4442fc 100644 --- a/src/backends/reference/workloads/CMakeLists.txt +++ b/src/backends/reference/workloads/CMakeLists.txt @@ -76,8 +76,8 @@ list(APPEND armnnRefBackendWorkloads_sources RefLstmWorkload.hpp RefConcatWorkload.cpp RefConcatWorkload.hpp - RefNormalizationFloat32Workload.cpp - RefNormalizationFloat32Workload.hpp + RefNormalizationWorkload.cpp + RefNormalizationWorkload.hpp RefPadWorkload.cpp RefPadWorkload.hpp RefPermuteWorkload.cpp diff --git a/src/backends/reference/workloads/RefNormalizationFloat32Workload.cpp b/src/backends/reference/workloads/RefNormalizationFloat32Workload.cpp deleted file mode 100644 index 3a2f2b9658..0000000000 --- a/src/backends/reference/workloads/RefNormalizationFloat32Workload.cpp +++ /dev/null @@ -1,190 +0,0 @@ -// -// Copyright © 2017 Arm Ltd. All rights reserved. -// SPDX-License-Identifier: MIT -// - -#include "RefNormalizationFloat32Workload.hpp" - -#include "RefWorkloadUtils.hpp" -#include "TensorBufferArrayView.hpp" - -#include "Profiling.hpp" - -#include - -#include -#include - -using namespace armnnUtils; - -namespace armnn -{ - -// Helper function to compute "Within" normalization using Krichevsky 2012: Local Brightness Normalization. -static void NormalizeWithinUingLbr(const float* inputData, - float* outputData, - const TensorShape& tensorShape, - uint32_t norm_size, - float alpha, - float beta, - float kappa) -{ - const unsigned int batchSize = tensorShape[0]; - const unsigned int depth = tensorShape[1]; - const unsigned int rows = tensorShape[2]; - const unsigned int cols = tensorShape[3]; - - int radius = boost::numeric_cast(norm_size / 2u); /* Strong Assumption on rounding Mode */ - - for (unsigned int n = 0; n < batchSize; n++) - { - for (unsigned int c = 0; c < depth; c++) - { - for (unsigned int h = 0; h < rows; h++) - { - for (unsigned int w = 0; w < cols; w++) - { - float accumulated_scale = 0.0; - for (int y = -radius; y <= radius; y++) - { - for (int x = -radius; x <= radius; x++) - { - int i = boost::numeric_cast(w) + x; - int j = boost::numeric_cast(h) + y; - - if ((i < 0) || (i >= boost::numeric_cast(cols))) - { - continue; - } - - if ((j < 0) || (j >= boost::numeric_cast(rows))) - { - continue; - } - - float inval = inputData[n * cols * rows * depth + - c * cols * rows + - boost::numeric_cast(j) * cols + - boost::numeric_cast(i)]; - - accumulated_scale += inval*inval; - } - } - outputData[n * cols * rows * depth + - c * cols * rows + - h * cols + - w] = inputData[n * cols * rows * depth + - c * cols * rows + - h * cols + - w] / (powf((kappa + (accumulated_scale * alpha)), beta)); - } - } - } - } -} - -// Helper function to compute "Across" normalization using Krichevsky 2012: Local Brightness Normalization. -void NormalizeAcrossUingLbr(const float* inputData, - float* outputData, - const TensorShape& tensorShape, - uint32_t norm_size, - float alpha, - float beta, - float kappa, - DataLayout dataLayout) -{ - TensorBufferArrayView input(tensorShape, - inputData, - dataLayout); - TensorBufferArrayView output(tensorShape, - outputData, - dataLayout); - - DataLayoutIndexed dataLayoutIndexed(dataLayout); - - const unsigned int batchSize = tensorShape[0]; - const unsigned int depth = tensorShape[dataLayoutIndexed.GetChannelsIndex()]; - const unsigned int rows = tensorShape[dataLayoutIndexed.GetHeightIndex()]; - const unsigned int cols = tensorShape[dataLayoutIndexed.GetWidthIndex()]; - - int radius = boost::numeric_cast(norm_size / 2u); /* Strong Assumption on rounding Mode */ - - for (unsigned int n = 0; n < batchSize; n++) - { - for (unsigned int c = 0; c < depth; c++) - { - for (unsigned int h = 0; h < rows; h++) - { - for (unsigned int w = 0; w < cols; w++) - { - float accumulated_scale = 0.0; - for (int z = -radius; z <= radius; z++) - { - int k = boost::numeric_cast(c) + z; - - if ((k < 0) || (k >= boost::numeric_cast(depth))) - { - continue; - } - - float inval = input.Get(n, boost::numeric_cast(k), h, w); - - accumulated_scale += inval * inval; - } - - float scale = kappa + (accumulated_scale * alpha); - scale = powf(scale, -beta); - - output.Get(n, c, h, w) = scale * input.Get(n, c, h, w); - } - } - } - } -} - -void RefNormalizationFloat32Workload::Execute() const -{ - ARMNN_SCOPED_PROFILING_EVENT(Compute::CpuRef, "RefNormalizationFloat32Workload_Execute"); - - const TensorInfo& inputInfo = GetTensorInfo(m_Data.m_Inputs[0]); - - float* outputData = GetOutputTensorDataFloat(0, m_Data); - const float* inputData = GetInputTensorDataFloat(0, m_Data); - - if (NormalizationAlgorithmMethod::LocalBrightness == m_Data.m_Parameters.m_NormMethodType) - { - if (NormalizationAlgorithmChannel::Within == m_Data.m_Parameters.m_NormChannelType) - { - NormalizeWithinUingLbr(inputData, - outputData, - inputInfo.GetShape(), - m_Data.m_Parameters.m_NormSize, - m_Data.m_Parameters.m_Alpha, - m_Data.m_Parameters.m_Beta, - m_Data.m_Parameters.m_K); - } - else if (NormalizationAlgorithmChannel::Across == m_Data.m_Parameters.m_NormChannelType) - { - NormalizeAcrossUingLbr(inputData, - outputData, - inputInfo.GetShape(), - m_Data.m_Parameters.m_NormSize, - m_Data.m_Parameters.m_Alpha, - m_Data.m_Parameters.m_Beta, - m_Data.m_Parameters.m_K, - m_Data.m_Parameters.m_DataLayout); - } - else - { - BOOST_LOG_TRIVIAL(warning) << "Illegal NORMALIZATION mode in normalization_f32"; - return; - } - } - else - { - BOOST_LOG_TRIVIAL(warning) << "Lcr method (Jarret 2009: Local Contrast Normalization) not supported yet."; - return; - } -} - -} //namespace armnn diff --git a/src/backends/reference/workloads/RefNormalizationFloat32Workload.hpp b/src/backends/reference/workloads/RefNormalizationFloat32Workload.hpp deleted file mode 100644 index 9dff187bd4..0000000000 --- a/src/backends/reference/workloads/RefNormalizationFloat32Workload.hpp +++ /dev/null @@ -1,21 +0,0 @@ -// -// Copyright © 2017 Arm Ltd. All rights reserved. -// SPDX-License-Identifier: MIT -// - -#pragma once - -#include -#include - -namespace armnn -{ - -class RefNormalizationFloat32Workload : public Float32Workload -{ -public: - using Float32Workload::Float32Workload; - virtual void Execute() const override; -}; - -} //namespace armnn diff --git a/src/backends/reference/workloads/RefNormalizationWorkload.cpp b/src/backends/reference/workloads/RefNormalizationWorkload.cpp new file mode 100644 index 0000000000..8ff2d9cf92 --- /dev/null +++ b/src/backends/reference/workloads/RefNormalizationWorkload.cpp @@ -0,0 +1,210 @@ +// +// Copyright © 2017 Arm Ltd. All rights reserved. +// SPDX-License-Identifier: MIT +// + +#include "RefNormalizationWorkload.hpp" + +#include "RefWorkloadUtils.hpp" +#include "Decoders.hpp" +#include "Encoders.hpp" + +#include + +#include +#include + +#include +#include + +using namespace armnn; +using namespace armnnUtils; + +namespace +{ + +// Helper function to compute "Within" normalization using Krichevsky 2012: Local Brightness Normalization. +void NormalizeWithinUingLbr(Decoder& inputData, + Encoder& outputData, + const TensorShape& tensorShape, + uint32_t norm_size, + float alpha, + float beta, + float kappa) +{ + const unsigned int batchSize = tensorShape[0]; + const unsigned int depth = tensorShape[1]; + const unsigned int rows = tensorShape[2]; + const unsigned int cols = tensorShape[3]; + + int radius = boost::numeric_cast(norm_size / 2u); /* Strong Assumption on rounding Mode */ + + for (unsigned int n = 0; n < batchSize; n++) + { + for (unsigned int c = 0; c < depth; c++) + { + for (unsigned int h = 0; h < rows; h++) + { + for (unsigned int w = 0; w < cols; w++) + { + float accumulated_scale = 0.0; + for (int y = -radius; y <= radius; y++) + { + for (int x = -radius; x <= radius; x++) + { + int i = boost::numeric_cast(w) + x; + int j = boost::numeric_cast(h) + y; + + if ((i < 0) || (i >= boost::numeric_cast(cols))) + { + continue; + } + + if ((j < 0) || (j >= boost::numeric_cast(rows))) + { + continue; + } + + unsigned int inputIndex = n * cols * rows * depth + + c * cols * rows + + boost::numeric_cast(j) * cols + + boost::numeric_cast(i); + inputData[inputIndex]; + float inval = inputData.Get(); + + accumulated_scale += inval*inval; + } + } + + unsigned int index = n * cols * rows * depth + + c * cols * rows + + h * cols + + w; + inputData[index]; + outputData[index]; + outputData.Set(inputData.Get() / (powf((kappa + (accumulated_scale * alpha)), beta))); + } + } + } + } +} + +// Helper function to compute "Across" normalization using Krichevsky 2012: Local Brightness Normalization. +void NormalizeAcrossUingLbr(Decoder& inputData, + Encoder& outputData, + const TensorShape& tensorShape, + uint32_t norm_size, + float alpha, + float beta, + float kappa, + DataLayout dataLayout) +{ + DataLayoutIndexed dataLayoutIndexed(dataLayout); + + const unsigned int batchSize = tensorShape[0]; + const unsigned int depth = tensorShape[dataLayoutIndexed.GetChannelsIndex()]; + const unsigned int rows = tensorShape[dataLayoutIndexed.GetHeightIndex()]; + const unsigned int cols = tensorShape[dataLayoutIndexed.GetWidthIndex()]; + + int radius = boost::numeric_cast(norm_size / 2u); /* Strong Assumption on rounding Mode */ + + for (unsigned int n = 0; n < batchSize; n++) + { + for (unsigned int c = 0; c < depth; c++) + { + for (unsigned int h = 0; h < rows; h++) + { + for (unsigned int w = 0; w < cols; w++) + { + float accumulated_scale = 0.0; + for (int z = -radius; z <= radius; z++) + { + int k = boost::numeric_cast(c) + z; + + if ((k < 0) || (k >= boost::numeric_cast(depth))) + { + continue; + } + + unsigned inputIndex = dataLayoutIndexed.GetIndex(tensorShape, + n, + boost::numeric_cast(k), + h, + w); + + inputData[inputIndex]; + float inval = inputData.Get(); + + accumulated_scale += inval * inval; + } + + float scale = kappa + (accumulated_scale * alpha); + scale = powf(scale, -beta); + + unsigned index = dataLayoutIndexed.GetIndex(tensorShape, n, c, h, w); + + inputData[index]; + outputData[index]; + outputData.Set(scale * inputData.Get()); + } + } + } + } +} + +} // Anonymous namespace + +namespace armnn +{ + +RefNormalizationWorkload::RefNormalizationWorkload(const NormalizationQueueDescriptor& descriptor, + const WorkloadInfo& info) + : BaseWorkload(descriptor, info) +{} + +void RefNormalizationWorkload::Execute() const +{ + ARMNN_SCOPED_PROFILING_EVENT(Compute::CpuRef, "RefNormalizationWorkload_Execute"); + + const TensorInfo& inputInfo = GetTensorInfo(m_Data.m_Inputs[0]); + + auto inputDecoder = MakeDecoder(inputInfo, m_Data.m_Inputs[0]->Map()); + auto outputEncoder = MakeEncoder(inputInfo, m_Data.m_Outputs[0]->Map()); + + if (NormalizationAlgorithmMethod::LocalBrightness == m_Data.m_Parameters.m_NormMethodType) + { + if (NormalizationAlgorithmChannel::Within == m_Data.m_Parameters.m_NormChannelType) + { + NormalizeWithinUingLbr(*inputDecoder, + *outputEncoder, + inputInfo.GetShape(), + m_Data.m_Parameters.m_NormSize, + m_Data.m_Parameters.m_Alpha, + m_Data.m_Parameters.m_Beta, + m_Data.m_Parameters.m_K); + } + else if (NormalizationAlgorithmChannel::Across == m_Data.m_Parameters.m_NormChannelType) + { + NormalizeAcrossUingLbr(*inputDecoder, + *outputEncoder, + inputInfo.GetShape(), + m_Data.m_Parameters.m_NormSize, + m_Data.m_Parameters.m_Alpha, + m_Data.m_Parameters.m_Beta, + m_Data.m_Parameters.m_K, + m_Data.m_Parameters.m_DataLayout); + } + else + { + BOOST_LOG_TRIVIAL(warning) << "Illegal NORMALIZATION mode in normalization_f32"; + return; + } + } + else + { + BOOST_LOG_TRIVIAL(warning) << "Lcr method (Jarret 2009: Local Contrast Normalization) not supported yet."; + return; + } +} + +} // namespace armnn diff --git a/src/backends/reference/workloads/RefNormalizationWorkload.hpp b/src/backends/reference/workloads/RefNormalizationWorkload.hpp new file mode 100644 index 0000000000..6d33c8afb2 --- /dev/null +++ b/src/backends/reference/workloads/RefNormalizationWorkload.hpp @@ -0,0 +1,23 @@ +// +// Copyright © 2017 Arm Ltd. All rights reserved. +// SPDX-License-Identifier: MIT +// + +#pragma once + +#include +#include + +namespace armnn +{ + +class RefNormalizationWorkload : public BaseWorkload +{ +public: + explicit RefNormalizationWorkload(const NormalizationQueueDescriptor& descriptor, + const WorkloadInfo& info); + + virtual void Execute() const override; +}; + +} // namespace armnn diff --git a/src/backends/reference/workloads/RefWorkloads.hpp b/src/backends/reference/workloads/RefWorkloads.hpp index ce1e688dcf..96f98ee7a8 100644 --- a/src/backends/reference/workloads/RefWorkloads.hpp +++ b/src/backends/reference/workloads/RefWorkloads.hpp @@ -30,7 +30,7 @@ #include "RefSoftmaxWorkload.hpp" #include "RefResizeBilinearFloat32Workload.hpp" #include "ResizeBilinear.hpp" -#include "RefNormalizationFloat32Workload.hpp" +#include "RefNormalizationWorkload.hpp" #include "RefDetectionPostProcessWorkload.hpp" #include "BatchNormImpl.hpp" #include "Activation.hpp" -- cgit v1.2.1