aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFrancis Murtagh <francis.murtagh@arm.com>2018-10-05 14:08:48 +0100
committerMatthew Bentham <matthew.bentham@arm.com>2018-10-10 16:16:58 +0100
commit043d0d0e07e0a29648894cb9ba68993e540bb94d (patch)
tree8f757c83d11ba0472f6b636f8ce8fa5b2f3e620e
parent7c22c70418192cf5803ae60dd2da041b0a6aafe5 (diff)
downloadarmnn-043d0d0e07e0a29648894cb9ba68993e540bb94d.tar.gz
IVGCVSW-1860 Support NHWC for Pooling2D
Change-Id: I9059a292e47867df82a5efbba5808fd264949ec9
-rw-r--r--include/armnn/Descriptors.hpp2
-rw-r--r--src/backends/cl/workloads/ClPooling2dBaseWorkload.cpp8
-rw-r--r--src/backends/neon/workloads/NeonPooling2dBaseWorkload.cpp8
-rw-r--r--src/backends/test/ArmComputeCl.cpp1
-rw-r--r--src/backends/test/ArmComputeNeon.cpp1
-rw-r--r--src/backends/test/LayerTests.cpp5
-rw-r--r--src/backends/test/LayerTests.hpp1
-rw-r--r--src/backends/test/Pooling2dTestImpl.hpp135
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<dataTypes...>::ClPooling2dBaseWorkload(
arm_compute::ICLTensor& input = static_cast<IClTensorHandle*>(m_Data.m_Inputs[0])->GetTensor();
arm_compute::ICLTensor& output = static_cast<IClTensorHandle*>(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<dataTypes...>::NeonPooling2dBaseWorkload(
arm_compute::ITensor& input = boost::polymorphic_downcast<INeonTensorHandle*>(m_Data.m_Inputs[0])->GetTensor();
arm_compute::ITensor& output = boost::polymorphic_downcast<INeonTensorHandle*>(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<float, 4> SimpleAveragePooling2dTest(armnn::IWorkloadFactory& wo
return SimpleAveragePooling2dTestCommon<float>(workloadFactory);
}
+LayerTestResult<float, 4> SimpleAveragePooling2dNhwcTest(armnn::IWorkloadFactory& workloadFactory)
+{
+ return SimpleAveragePooling2dNhwcTestCommon<float>(workloadFactory);
+}
+
LayerTestResult<uint8_t, 4> SimpleAveragePooling2dUint8Test(armnn::IWorkloadFactory& workloadFactory)
{
return SimpleAveragePooling2dTestCommon<uint8_t>(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<float, 4> IgnorePaddingMaxPooling2dSize3Test(armnn::IWorkloadF
LayerTestResult<uint8_t, 4> IgnorePaddingMaxPooling2dSize3Uint8Test(armnn::IWorkloadFactory& workloadFactory);
LayerTestResult<float, 4> SimpleAveragePooling2dTest(armnn::IWorkloadFactory& workloadFactory);
+LayerTestResult<float, 4> SimpleAveragePooling2dNhwcTest(armnn::IWorkloadFactory& workloadFactory);
LayerTestResult<uint8_t, 4> SimpleAveragePooling2dUint8Test(armnn::IWorkloadFactory& workloadFactory);
LayerTestResult<float, 4> 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 <armnn/ArmNN.hpp>
-#include <armnn/Tensor.hpp>
-#include <armnn/TypesUtils.hpp>
#include <test/TensorHelpers.hpp>
#include "QuantizeHelper.hpp"
#include <backends/CpuTensorHandle.hpp>
#include <backends/WorkloadFactory.hpp>
-
+#include <backends/WorkloadInfo.hpp>
#include <algorithm>
template<typename T>
LayerTestResult<T, 4> SimplePooling2dTestImpl(
- armnn::IWorkloadFactory& workloadFactory,
- armnn::Pooling2dDescriptor descriptor,
- float qScale,
- int32_t qOffset,
- const boost::multi_array<T, 4>& input,
- const boost::multi_array<T, 4>& outputExpected)
+ armnn::IWorkloadFactory& workloadFactory,
+ armnn::Pooling2dDescriptor descriptor,
+ float qScale,
+ int32_t qOffset,
+ const boost::multi_array<T, 4>& input,
+ const boost::multi_array<T, 4>& outputExpected)
{
unsigned int inputHeight = boost::numeric_cast<unsigned int>(input.shape()[2]);
unsigned int inputWidth = boost::numeric_cast<unsigned int>(input.shape()[3]);
@@ -88,6 +86,80 @@ LayerTestResult<T, 4> SimplePooling2dTestImpl(
return result;
}
+template<typename T>
+LayerTestResult<T, 4> SimplePooling2dNhwcTestImpl(
+ armnn::IWorkloadFactory& workloadFactory,
+ armnn::Pooling2dDescriptor descriptor,
+ float qScale,
+ int32_t qOffset,
+ const boost::multi_array<T, 4>& input,
+ const boost::multi_array<T, 4>& outputExpected)
+{
+ unsigned int inputHeight = boost::numeric_cast<unsigned int>(input.shape()[1]);
+ unsigned int inputWidth = boost::numeric_cast<unsigned int>(input.shape()[2]);
+ unsigned int inputChannels = boost::numeric_cast<unsigned int>(input.shape()[3]);
+ unsigned int inputBatchSize = boost::numeric_cast<unsigned int>(input.shape()[0]);
+
+ unsigned int outputHeight = boost::numeric_cast<unsigned int>(outputExpected.shape()[1]);
+ unsigned int outputWidth = boost::numeric_cast<unsigned int>(outputExpected.shape()[2]);
+ unsigned int outputChannels = boost::numeric_cast<unsigned int>(outputExpected.shape()[3]);
+ unsigned int outputBatchSize = boost::numeric_cast<unsigned int>(outputExpected.shape()[0]);
+
+ armnn::TensorInfo inputTensorInfo({ inputBatchSize, inputHeight, inputWidth, inputChannels },
+ armnn::GetDataType<T>());
+ armnn::TensorInfo outputTensorInfo({ outputBatchSize, outputHeight, outputWidth, outputChannels },
+ armnn::GetDataType<T>());
+
+ // Set quantization parameters if the requested type is a quantized type.
+ if(armnn::IsQuantizedType<T>())
+ {
+ inputTensorInfo.SetQuantizationScale(qScale);
+ inputTensorInfo.SetQuantizationOffset(qOffset);
+ outputTensorInfo.SetQuantizationScale(qScale);
+ outputTensorInfo.SetQuantizationOffset(qOffset);
+ }
+
+ LayerTestResult<T, 4> result(outputTensorInfo);
+
+ std::unique_ptr<armnn::ITensorHandle> inputHandle = workloadFactory.CreateTensorHandle(inputTensorInfo);
+ std::unique_ptr<armnn::ITensorHandle> 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<armnn::IWorkload> 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:
//
@@ -263,6 +335,51 @@ LayerTestResult<T, 4> SimpleAveragePooling2dTestCommon(armnn::IWorkloadFactory&
}
template<typename T>
+LayerTestResult<T, 4> 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<T>());
+ armnn::TensorInfo outputTensorInfo({ 1, 3, 3, 1 }, armnn::GetDataType<T>());
+
+ // Set quantization parameters if the requested type is a quantized type.
+ if(armnn::IsQuantizedType<T>())
+ {
+ inputTensorInfo.SetQuantizationScale(qScale);
+ inputTensorInfo.SetQuantizationOffset(qOffset);
+ outputTensorInfo.SetQuantizationScale(qScale);
+ outputTensorInfo.SetQuantizationOffset(qOffset);
+ }
+
+ auto input = MakeTensor<T, 4>(inputTensorInfo,
+ QuantizedVector<T>(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<T, 4>(outputTensorInfo,
+ QuantizedVector<T>(qScale, qOffset, {
+ 1.0f, 2.5f, 4.0f,
+ 1.0f, 2.5f, 4.0f,
+ 1.0f, 2.5f, 4.0f,
+ }));
+
+ return SimplePooling2dNhwcTestImpl<T>(workloadFactory, descriptor, qScale, qOffset, input, outputExpected);
+}
+
+template<typename T>
LayerTestResult<T, 4> LargeTensorsAveragePooling2dTestCommon(armnn::IWorkloadFactory& workloadFactory,
float qScale = 1.0f,
int32_t qOffset = 0)