From 043d0d0e07e0a29648894cb9ba68993e540bb94d Mon Sep 17 00:00:00 2001 From: Francis Murtagh Date: Fri, 5 Oct 2018 14:08:48 +0100 Subject: IVGCVSW-1860 Support NHWC for Pooling2D Change-Id: I9059a292e47867df82a5efbba5808fd264949ec9 --- include/armnn/Descriptors.hpp | 2 + .../cl/workloads/ClPooling2dBaseWorkload.cpp | 8 +- .../neon/workloads/NeonPooling2dBaseWorkload.cpp | 8 +- src/backends/test/ArmComputeCl.cpp | 1 + src/backends/test/ArmComputeNeon.cpp | 1 + src/backends/test/LayerTests.cpp | 5 + src/backends/test/LayerTests.hpp | 1 + src/backends/test/Pooling2dTestImpl.hpp | 135 +++++++++++++++++++-- 8 files changed, 148 insertions(+), 13 deletions(-) diff --git a/include/armnn/Descriptors.hpp b/include/armnn/Descriptors.hpp index 2de031e94a..c2efa211a6 100644 --- a/include/armnn/Descriptors.hpp +++ b/include/armnn/Descriptors.hpp @@ -180,6 +180,7 @@ struct Pooling2dDescriptor , m_StrideY(0) , m_OutputShapeRounding(OutputShapeRounding::Floor) , m_PaddingMethod(PaddingMethod::Exclude) + , m_DataLayout(DataLayout::NCHW) {}; PoolingAlgorithm m_PoolType; @@ -193,6 +194,7 @@ struct Pooling2dDescriptor uint32_t m_StrideY; OutputShapeRounding m_OutputShapeRounding; PaddingMethod m_PaddingMethod; + DataLayout m_DataLayout; }; struct FullyConnectedDescriptor diff --git a/src/backends/cl/workloads/ClPooling2dBaseWorkload.cpp b/src/backends/cl/workloads/ClPooling2dBaseWorkload.cpp index 98911856fe..e61ad4f28e 100644 --- a/src/backends/cl/workloads/ClPooling2dBaseWorkload.cpp +++ b/src/backends/cl/workloads/ClPooling2dBaseWorkload.cpp @@ -17,8 +17,8 @@ arm_compute::Status ClPooling2dWorkloadValidate(const TensorInfo& input, const TensorInfo& output, const Pooling2dDescriptor& descriptor) { - const arm_compute::TensorInfo aclInputInfo = BuildArmComputeTensorInfo(input); - const arm_compute::TensorInfo aclOutputInfo = BuildArmComputeTensorInfo(output); + const arm_compute::TensorInfo aclInputInfo = BuildArmComputeTensorInfo(input, descriptor.m_DataLayout); + const arm_compute::TensorInfo aclOutputInfo = BuildArmComputeTensorInfo(output, descriptor.m_DataLayout); arm_compute::PoolingLayerInfo layerInfo = BuildArmComputePoolingLayerInfo(descriptor); @@ -35,6 +35,10 @@ ClPooling2dBaseWorkload::ClPooling2dBaseWorkload( arm_compute::ICLTensor& input = static_cast(m_Data.m_Inputs[0])->GetTensor(); arm_compute::ICLTensor& output = static_cast(m_Data.m_Outputs[0])->GetTensor(); + arm_compute::DataLayout aclDataLayout = ConvertDataLayout(m_Data.m_Parameters.m_DataLayout); + input.info()->set_data_layout(aclDataLayout); + output.info()->set_data_layout(aclDataLayout); + arm_compute::PoolingLayerInfo layerInfo = BuildArmComputePoolingLayerInfo(m_Data.m_Parameters); // Run the layer. diff --git a/src/backends/neon/workloads/NeonPooling2dBaseWorkload.cpp b/src/backends/neon/workloads/NeonPooling2dBaseWorkload.cpp index 109e856506..054f9c5f75 100644 --- a/src/backends/neon/workloads/NeonPooling2dBaseWorkload.cpp +++ b/src/backends/neon/workloads/NeonPooling2dBaseWorkload.cpp @@ -17,8 +17,8 @@ arm_compute::Status NeonPooling2dWorkloadValidate(const TensorInfo& input, const TensorInfo& output, const Pooling2dDescriptor& descriptor) { - const arm_compute::TensorInfo aclInputInfo = BuildArmComputeTensorInfo(input); - const arm_compute::TensorInfo aclOutputInfo = BuildArmComputeTensorInfo(output); + const arm_compute::TensorInfo aclInputInfo = BuildArmComputeTensorInfo(input, descriptor.m_DataLayout); + const arm_compute::TensorInfo aclOutputInfo = BuildArmComputeTensorInfo(output, descriptor.m_DataLayout); arm_compute::PoolingLayerInfo layerInfo = BuildArmComputePoolingLayerInfo(descriptor); @@ -35,6 +35,10 @@ NeonPooling2dBaseWorkload::NeonPooling2dBaseWorkload( arm_compute::ITensor& input = boost::polymorphic_downcast(m_Data.m_Inputs[0])->GetTensor(); arm_compute::ITensor& output = boost::polymorphic_downcast(m_Data.m_Outputs[0])->GetTensor(); + arm_compute::DataLayout aclDataLayout = ConvertDataLayout(m_Data.m_Parameters.m_DataLayout); + input.info()->set_data_layout(aclDataLayout); + output.info()->set_data_layout(aclDataLayout); + arm_compute::PoolingLayerInfo layerInfo = BuildArmComputePoolingLayerInfo(m_Data.m_Parameters); m_PoolingLayer.configure(&input, &output, layerInfo); diff --git a/src/backends/test/ArmComputeCl.cpp b/src/backends/test/ArmComputeCl.cpp index faa3d4f37b..f4ead34c58 100644 --- a/src/backends/test/ArmComputeCl.cpp +++ b/src/backends/test/ArmComputeCl.cpp @@ -122,6 +122,7 @@ ARMNN_AUTO_TEST_CASE(IgnorePaddingL2Pooling2dSize3, IgnorePaddingL2Pooling2dSize ARMNN_AUTO_TEST_CASE(UNSUPPORTED_IgnorePaddingL2Pooling2dSize3Uint8, IgnorePaddingL2Pooling2dSize3Uint8Test) ARMNN_AUTO_TEST_CASE(SimpleAveragePooling2d, SimpleAveragePooling2dTest) +ARMNN_AUTO_TEST_CASE(SimpleAveragePooling2dNhwc, SimpleAveragePooling2dNhwcTest) ARMNN_AUTO_TEST_CASE(SimpleAveragePooling2dUint8, SimpleAveragePooling2dUint8Test) ARMNN_AUTO_TEST_CASE(IgnorePaddingAveragePooling2dSize3x2Stride2x2, IgnorePaddingAveragePooling2dSize3x2Stride2x2Test, diff --git a/src/backends/test/ArmComputeNeon.cpp b/src/backends/test/ArmComputeNeon.cpp index 7a60c31f73..045aa30889 100644 --- a/src/backends/test/ArmComputeNeon.cpp +++ b/src/backends/test/ArmComputeNeon.cpp @@ -232,6 +232,7 @@ BOOST_AUTO_TEST_CASE(DepthwiseConv2dUtils) ARMNN_AUTO_TEST_CASE(SimpleMaxPooling2dSize3x3Stride2x4, SimpleMaxPooling2dSize3x3Stride2x4Test, true) ARMNN_AUTO_TEST_CASE(SimpleMaxPooling2dSize3x3Stride2x4Uint8, SimpleMaxPooling2dSize3x3Stride2x4Uint8Test, true) ARMNN_AUTO_TEST_CASE(SimpleAveragePooling2d, SimpleAveragePooling2dTest) +ARMNN_AUTO_TEST_CASE(SimpleAveragePooling2dNhwc, SimpleAveragePooling2dNhwcTest) ARMNN_AUTO_TEST_CASE(SimpleAveragePooling2dUint8, SimpleAveragePooling2dUint8Test) ARMNN_AUTO_TEST_CASE(LargeTensorsAveragePooling2d, LargeTensorsAveragePooling2dTest) diff --git a/src/backends/test/LayerTests.cpp b/src/backends/test/LayerTests.cpp index a7fb6a824e..17f3ae12e1 100644 --- a/src/backends/test/LayerTests.cpp +++ b/src/backends/test/LayerTests.cpp @@ -4929,6 +4929,11 @@ LayerTestResult SimpleAveragePooling2dTest(armnn::IWorkloadFactory& wo return SimpleAveragePooling2dTestCommon(workloadFactory); } +LayerTestResult SimpleAveragePooling2dNhwcTest(armnn::IWorkloadFactory& workloadFactory) +{ + return SimpleAveragePooling2dNhwcTestCommon(workloadFactory); +} + LayerTestResult SimpleAveragePooling2dUint8Test(armnn::IWorkloadFactory& workloadFactory) { return SimpleAveragePooling2dTestCommon(workloadFactory, 0.5, -1); diff --git a/src/backends/test/LayerTests.hpp b/src/backends/test/LayerTests.hpp index 71a468b36b..f5abd985c8 100644 --- a/src/backends/test/LayerTests.hpp +++ b/src/backends/test/LayerTests.hpp @@ -88,6 +88,7 @@ LayerTestResult IgnorePaddingMaxPooling2dSize3Test(armnn::IWorkloadF LayerTestResult IgnorePaddingMaxPooling2dSize3Uint8Test(armnn::IWorkloadFactory& workloadFactory); LayerTestResult SimpleAveragePooling2dTest(armnn::IWorkloadFactory& workloadFactory); +LayerTestResult SimpleAveragePooling2dNhwcTest(armnn::IWorkloadFactory& workloadFactory); LayerTestResult SimpleAveragePooling2dUint8Test(armnn::IWorkloadFactory& workloadFactory); LayerTestResult IgnorePaddingAveragePooling2dSize3x2Stride2x2Test(armnn::IWorkloadFactory& workloadFactory, bool forceNoPadding); diff --git a/src/backends/test/Pooling2dTestImpl.hpp b/src/backends/test/Pooling2dTestImpl.hpp index c87548cd5b..7366bcaf44 100644 --- a/src/backends/test/Pooling2dTestImpl.hpp +++ b/src/backends/test/Pooling2dTestImpl.hpp @@ -5,25 +5,23 @@ #pragma once #include -#include -#include #include #include "QuantizeHelper.hpp" #include #include - +#include #include template LayerTestResult SimplePooling2dTestImpl( - armnn::IWorkloadFactory& workloadFactory, - armnn::Pooling2dDescriptor descriptor, - float qScale, - int32_t qOffset, - const boost::multi_array& input, - const boost::multi_array& outputExpected) + armnn::IWorkloadFactory& workloadFactory, + armnn::Pooling2dDescriptor descriptor, + float qScale, + int32_t qOffset, + const boost::multi_array& input, + const boost::multi_array& outputExpected) { unsigned int inputHeight = boost::numeric_cast(input.shape()[2]); unsigned int inputWidth = boost::numeric_cast(input.shape()[3]); @@ -88,6 +86,80 @@ LayerTestResult SimplePooling2dTestImpl( return result; } +template +LayerTestResult SimplePooling2dNhwcTestImpl( + armnn::IWorkloadFactory& workloadFactory, + armnn::Pooling2dDescriptor descriptor, + float qScale, + int32_t qOffset, + const boost::multi_array& input, + const boost::multi_array& outputExpected) +{ + unsigned int inputHeight = boost::numeric_cast(input.shape()[1]); + unsigned int inputWidth = boost::numeric_cast(input.shape()[2]); + unsigned int inputChannels = boost::numeric_cast(input.shape()[3]); + unsigned int inputBatchSize = boost::numeric_cast(input.shape()[0]); + + unsigned int outputHeight = boost::numeric_cast(outputExpected.shape()[1]); + unsigned int outputWidth = boost::numeric_cast(outputExpected.shape()[2]); + unsigned int outputChannels = boost::numeric_cast(outputExpected.shape()[3]); + unsigned int outputBatchSize = boost::numeric_cast(outputExpected.shape()[0]); + + armnn::TensorInfo inputTensorInfo({ inputBatchSize, inputHeight, inputWidth, inputChannels }, + armnn::GetDataType()); + armnn::TensorInfo outputTensorInfo({ outputBatchSize, outputHeight, outputWidth, outputChannels }, + armnn::GetDataType()); + + // Set quantization parameters if the requested type is a quantized type. + if(armnn::IsQuantizedType()) + { + inputTensorInfo.SetQuantizationScale(qScale); + inputTensorInfo.SetQuantizationOffset(qOffset); + outputTensorInfo.SetQuantizationScale(qScale); + outputTensorInfo.SetQuantizationOffset(qOffset); + } + + LayerTestResult result(outputTensorInfo); + + std::unique_ptr inputHandle = workloadFactory.CreateTensorHandle(inputTensorInfo); + std::unique_ptr outputHandle = workloadFactory.CreateTensorHandle(outputTensorInfo); + + armnn::Pooling2dQueueDescriptor queueDescriptor; + queueDescriptor.m_Parameters = descriptor; + queueDescriptor.m_Parameters.m_DataLayout = armnn::DataLayout::NHWC; + + armnn::WorkloadInfo workloadInfo; + AddInputToWorkload(queueDescriptor, workloadInfo, inputTensorInfo, inputHandle.get()); + AddOutputToWorkload(queueDescriptor, workloadInfo, outputTensorInfo, outputHandle.get()); + + // Don't execute if Pooling is not supported, as an exception will be raised. + armnn::Compute compute = workloadFactory.GetCompute(); + const size_t reasonIfUnsupportedMaxLen = 255; + char reasonIfUnsupported[reasonIfUnsupportedMaxLen+1]; + result.supported = armnn::IsPooling2dSupported(compute, inputTensorInfo, outputTensorInfo, + queueDescriptor.m_Parameters, + reasonIfUnsupported, reasonIfUnsupportedMaxLen); + if (!result.supported) + { + return result; + } + + std::unique_ptr workload = workloadFactory.CreatePooling2d(queueDescriptor, workloadInfo); + + inputHandle->Allocate(); + outputHandle->Allocate(); + + CopyDataToITensorHandle(inputHandle.get(), &input[0][0][0][0]); + + workload->Execute(); + + CopyDataFromITensorHandle(&result.output[0][0][0][0], outputHandle.get()); + + result.outputExpected = outputExpected; + + return result; +} + // // Tests max pooling with the following parameters: // @@ -262,6 +334,51 @@ LayerTestResult SimpleAveragePooling2dTestCommon(armnn::IWorkloadFactory& return SimplePooling2dTestImpl(workloadFactory, descriptor, qScale, qOffset, input, outputExpected); } +template +LayerTestResult SimpleAveragePooling2dNhwcTestCommon(armnn::IWorkloadFactory& workloadFactory, + float qScale = 1.0f, + int32_t qOffset = 0) +{ + armnn::Pooling2dDescriptor descriptor; + descriptor.m_PoolType = armnn::PoolingAlgorithm::Average; + descriptor.m_PoolWidth = descriptor.m_PoolHeight = 2; + descriptor.m_StrideX = descriptor.m_StrideY = 2; + descriptor.m_PadLeft = 1; + descriptor.m_PadRight = 1; + descriptor.m_PadTop = 1; + descriptor.m_PadBottom = 1; + descriptor.m_PaddingMethod = armnn::PaddingMethod::Exclude; + + armnn::TensorInfo inputTensorInfo({ 1, 4, 4, 1 }, armnn::GetDataType()); + armnn::TensorInfo outputTensorInfo({ 1, 3, 3, 1 }, armnn::GetDataType()); + + // Set quantization parameters if the requested type is a quantized type. + if(armnn::IsQuantizedType()) + { + inputTensorInfo.SetQuantizationScale(qScale); + inputTensorInfo.SetQuantizationOffset(qOffset); + outputTensorInfo.SetQuantizationScale(qScale); + outputTensorInfo.SetQuantizationOffset(qOffset); + } + + auto input = MakeTensor(inputTensorInfo, + QuantizedVector(qScale, qOffset, { + 1.0f, 2.0f, 3.0f, 4.0f, + 1.0f, 2.0f, 3.0f, 4.0f, + 1.0f, 2.0f, 3.0f, 4.0f, + 1.0f, 2.0f, 3.0f, 4.0f, + })); + + auto outputExpected = MakeTensor(outputTensorInfo, + QuantizedVector(qScale, qOffset, { + 1.0f, 2.5f, 4.0f, + 1.0f, 2.5f, 4.0f, + 1.0f, 2.5f, 4.0f, + })); + + return SimplePooling2dNhwcTestImpl(workloadFactory, descriptor, qScale, qOffset, input, outputExpected); +} + template LayerTestResult LargeTensorsAveragePooling2dTestCommon(armnn::IWorkloadFactory& workloadFactory, float qScale = 1.0f, -- cgit v1.2.1