From b63a31170aee1d28267d83a4bc67b57708fb6b05 Mon Sep 17 00:00:00 2001 From: Matthew Sloyan Date: Wed, 8 Sep 2021 13:05:51 +0100 Subject: IVGCVSW-6163 Add Conv3d FrontEnd and Ref Implementation * Added front-end * Added Reference workload * Added Serializer & Deserializer support * Added unit tests * Added NDHWC DataLayout Signed-off-by: Matthew Sloyan Change-Id: Iec4d39e7433b5334d52fa44cf8efc6bcd39319d8 --- Android.mk | 1 + CMakeLists.txt | 3 + docs/01_02_deserializer_serializer.dox | 1 + docs/05_operator_list.dox | 43 + include/armnn/BackendHelper.hpp | 7 + include/armnn/Descriptors.hpp | 68 ++ include/armnn/DescriptorsFwd.hpp | 1 + include/armnn/INetwork.hpp | 11 + include/armnn/Types.hpp | 5 +- include/armnn/TypesUtils.hpp | 7 +- include/armnn/backends/ILayerSupport.hpp | 7 + include/armnnUtils/DataLayoutIndexed.hpp | 2 + src/armnn/BackendHelper.cpp | 15 + src/armnn/LayersFwd.hpp | 2 + src/armnn/Network.cpp | 49 +- src/armnn/Network.hpp | 5 + src/armnn/SerializeLayerParameters.cpp | 27 + src/armnn/SerializeLayerParameters.hpp | 5 + src/armnn/layers/Convolution3dLayer.cpp | 172 ++++ src/armnn/layers/Convolution3dLayer.hpp | 68 ++ src/armnn/test/InferOutputTests.cpp | 3 + src/armnn/test/InferOutputTests.hpp | 37 + src/armnnDeserializer/Deserializer.cpp | 55 ++ src/armnnDeserializer/Deserializer.hpp | 1 + .../test/DeserializeConvolution3d.cpp | 223 +++++ src/armnnSerializer/ArmnnSchema.fbs | 29 +- src/armnnSerializer/ArmnnSchema_generated.h | 308 +++++- src/armnnSerializer/Serializer.cpp | 58 ++ src/armnnSerializer/Serializer.hpp | 9 +- src/armnnSerializer/SerializerUtils.cpp | 2 + src/armnnSerializer/test/SerializerTests.cpp | 55 ++ src/armnnUtils/DataLayoutIndexed.cpp | 6 + src/backends/backendsCommon/LayerSupportBase.cpp | 10 + src/backends/backendsCommon/LayerSupportBase.hpp | 7 + src/backends/backendsCommon/WorkloadData.cpp | 61 ++ src/backends/backendsCommon/WorkloadData.hpp | 15 + src/backends/backendsCommon/WorkloadFactory.cpp | 38 +- src/backends/backendsCommon/WorkloadFactory.hpp | 5 +- src/backends/backendsCommon/common.mk | 1 + src/backends/backendsCommon/test/CMakeLists.txt | 2 + .../test/IsLayerSupportedTestImpl.hpp | 2 + src/backends/backendsCommon/test/LayerTests.hpp | 1 + .../test/layerTests/Conv3dTestImpl.cpp | 1038 ++++++++++++++++++++ .../test/layerTests/Conv3dTestImpl.hpp | 141 +++ src/backends/reference/RefLayerSupport.cpp | 70 ++ src/backends/reference/RefLayerSupport.hpp | 7 + src/backends/reference/RefWorkloadFactory.cpp | 6 + src/backends/reference/RefWorkloadFactory.hpp | 3 + src/backends/reference/backend.mk | 2 + src/backends/reference/test/RefLayerTests.cpp | 33 + src/backends/reference/workloads/CMakeLists.txt | 4 + src/backends/reference/workloads/Conv3dImpl.cpp | 151 +++ src/backends/reference/workloads/Conv3dImpl.hpp | 38 + .../workloads/RefConvolution3dWorkload.cpp | 76 ++ .../workloads/RefConvolution3dWorkload.hpp | 38 + src/backends/reference/workloads/RefWorkloads.hpp | 1 + 56 files changed, 3000 insertions(+), 35 deletions(-) create mode 100644 src/armnn/layers/Convolution3dLayer.cpp create mode 100644 src/armnn/layers/Convolution3dLayer.hpp create mode 100644 src/armnnDeserializer/test/DeserializeConvolution3d.cpp create mode 100644 src/backends/backendsCommon/test/layerTests/Conv3dTestImpl.cpp create mode 100644 src/backends/backendsCommon/test/layerTests/Conv3dTestImpl.hpp create mode 100644 src/backends/reference/workloads/Conv3dImpl.cpp create mode 100644 src/backends/reference/workloads/Conv3dImpl.hpp create mode 100644 src/backends/reference/workloads/RefConvolution3dWorkload.cpp create mode 100644 src/backends/reference/workloads/RefConvolution3dWorkload.hpp diff --git a/Android.mk b/Android.mk index 69fe9eefb4..7137baf79e 100644 --- a/Android.mk +++ b/Android.mk @@ -161,6 +161,7 @@ LOCAL_SRC_FILES := \ src/armnn/layers/ConcatLayer.cpp \ src/armnn/layers/ConstantLayer.cpp \ src/armnn/layers/Convolution2dLayer.cpp \ + src/armnn/layers/Convolution3dLayer.cpp \ src/armnn/layers/ConvertBf16ToFp32Layer.cpp \ src/armnn/layers/ConvertFp16ToFp32Layer.cpp \ src/armnn/layers/ConvertFp32ToBf16Layer.cpp \ diff --git a/CMakeLists.txt b/CMakeLists.txt index f28c2f7d39..0989e12ad5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -231,6 +231,8 @@ list(APPEND armnn_sources src/armnn/layers/ConstantLayer.cpp src/armnn/layers/Convolution2dLayer.hpp src/armnn/layers/Convolution2dLayer.cpp + src/armnn/layers/Convolution3dLayer.hpp + src/armnn/layers/Convolution3dLayer.cpp src/armnn/layers/ConvertBf16ToFp32Layer.cpp src/armnn/layers/ConvertBf16ToFp32Layer.hpp src/armnn/layers/ConvertFp16ToFp32Layer.hpp @@ -793,6 +795,7 @@ if(BUILD_UNIT_TESTS) src/armnnDeserializer/test/DeserializeComparison.cpp src/armnnDeserializer/test/DeserializeConstant.cpp src/armnnDeserializer/test/DeserializeConvolution2d.cpp + src/armnnDeserializer/test/DeserializeConvolution3d.cpp src/armnnDeserializer/test/DeserializeDepthToSpace.cpp src/armnnDeserializer/test/DeserializeDepthwiseConv2d.cpp src/armnnDeserializer/test/DeserializeDivision.cpp diff --git a/docs/01_02_deserializer_serializer.dox b/docs/01_02_deserializer_serializer.dox index 55259ddeb8..6bd0d4a1a2 100644 --- a/docs/01_02_deserializer_serializer.dox +++ b/docs/01_02_deserializer_serializer.dox @@ -30,6 +30,7 @@ The Arm NN SDK Serializer currently supports the following layers: - Concat - Constant - Convolution2d +- Convolution3d - DepthToSpace - DepthwiseConvolution2d - Dequantize diff --git a/docs/05_operator_list.dox b/docs/05_operator_list.dox index 527cd92465..90aee130bf 100644 --- a/docs/05_operator_list.dox +++ b/docs/05_operator_list.dox @@ -836,6 +836,49 @@ where N = batches, C = channels, H = height, W = width QASYMMS8 QUANTIZEDSYMM8PERAXIS + + Convolution3dLayer + Layer to compute a 3D convolution operation. + + + CpuRef + + + + +
+
BFLOAT16 +
FLOAT16 +
FLOAT32 +
QASYMMS8 +
QASYMMU8 +
QSYMMS8 +
QSYMMS16 +
+ + CpuAcc + + + + + + GpuAcc + + + + DebugLayer Layer to print out inter layer tensor information. diff --git a/include/armnn/BackendHelper.hpp b/include/armnn/BackendHelper.hpp index e3478a79c5..80676deed2 100644 --- a/include/armnn/BackendHelper.hpp +++ b/include/armnn/BackendHelper.hpp @@ -108,6 +108,13 @@ public: const Optional& biases, Optional reasonIfUnsupported = EmptyOptional()); + bool IsConvolution3dSupported(const TensorInfo& input, + const TensorInfo& output, + const Convolution3dDescriptor& descriptor, + const TensorInfo& weights, + const Optional& biases, + Optional reasonIfUnsupported = EmptyOptional()); + bool IsDebugSupported(const TensorInfo& input, const TensorInfo& output, Optional reasonIfUnsupported = EmptyOptional()); diff --git a/include/armnn/Descriptors.hpp b/include/armnn/Descriptors.hpp index d571f2297b..9a5128a127 100644 --- a/include/armnn/Descriptors.hpp +++ b/include/armnn/Descriptors.hpp @@ -468,6 +468,74 @@ struct Convolution2dDescriptor : BaseDescriptor DataLayout m_DataLayout; }; +/// A Convolution3dDescriptor for the Convolution3dLayer. +struct Convolution3dDescriptor : BaseDescriptor +{ + Convolution3dDescriptor() + : m_PadLeft(0) + , m_PadRight(0) + , m_PadTop(0) + , m_PadBottom(0) + , m_PadFront(0) + , m_PadBack(0) + , m_StrideX(1) + , m_StrideY(1) + , m_StrideZ(1) + , m_DilationX(1) + , m_DilationY(1) + , m_DilationZ(1) + , m_BiasEnabled(false) + , m_DataLayout(DataLayout::NDHWC) + {} + + bool operator ==(const Convolution3dDescriptor& rhs) const + { + return m_PadLeft == rhs.m_PadLeft && + m_PadRight == rhs.m_PadRight && + m_PadTop == rhs.m_PadTop && + m_PadBottom == rhs.m_PadBottom && + m_PadFront == rhs.m_PadFront && + m_PadBack == rhs.m_PadBack && + m_StrideX == rhs.m_StrideX && + m_StrideY == rhs.m_StrideY && + m_StrideZ == rhs.m_StrideZ && + m_DilationX == rhs.m_DilationX && + m_DilationY == rhs.m_DilationY && + m_DilationZ == rhs.m_DilationZ && + m_BiasEnabled == rhs.m_BiasEnabled && + m_DataLayout == rhs.m_DataLayout; + } + + /// Padding left value in the width dimension. + uint32_t m_PadLeft; + /// Padding right value in the width dimension. + uint32_t m_PadRight; + /// Padding top value in the height dimension. + uint32_t m_PadTop; + /// Padding bottom value in the height dimension. + uint32_t m_PadBottom; + /// Padding front value in the depth dimension. + uint32_t m_PadFront; + /// Padding back value in the depth dimension. + uint32_t m_PadBack; + /// Stride value when proceeding through input for the width dimension. + uint32_t m_StrideX; + /// Stride value when proceeding through input for the height dimension. + uint32_t m_StrideY; + /// Stride value when proceeding through input for the depth dimension. + uint32_t m_StrideZ; + /// Dilation along x axis + uint32_t m_DilationX; + /// Dilation along y axis + uint32_t m_DilationY; + /// Dilation along z axis + uint32_t m_DilationZ; + /// Enable/disable bias. + bool m_BiasEnabled; + /// The data layout to be used (NDHWC). + DataLayout m_DataLayout; +}; + /// A DepthwiseConvolution2dDescriptor for the DepthwiseConvolution2dLayer. struct DepthwiseConvolution2dDescriptor : BaseDescriptor { diff --git a/include/armnn/DescriptorsFwd.hpp b/include/armnn/DescriptorsFwd.hpp index 396b7285fd..5c4615d6bb 100644 --- a/include/armnn/DescriptorsFwd.hpp +++ b/include/armnn/DescriptorsFwd.hpp @@ -16,6 +16,7 @@ struct BatchToSpaceNdDescriptor; struct ChannelShuffleDescriptor; struct ComparisonDescriptor; struct Convolution2dDescriptor; +struct Convolution3dDescriptor; struct DepthwiseConvolution2dDescriptor; struct DetectionPostProcessDescriptor; struct ElementwiseUnaryDescriptor; diff --git a/include/armnn/INetwork.hpp b/include/armnn/INetwork.hpp index 37aeaf47fe..8ec8de0600 100644 --- a/include/armnn/INetwork.hpp +++ b/include/armnn/INetwork.hpp @@ -241,6 +241,17 @@ public: const ConstTensor& biases, const char* name = nullptr); + /// Adds a 3D convolution layer to the network. + /// @param convolution3dDescriptor - Description of the 3D convolution layer. + /// @param weights - Tensor for the weights data. + /// @param biases - Optional tensor for the bias data. If specified, must match the output tensor shape. + /// @param name - Optional name for the layer. + /// @return - Interface for configuring the layer. + IConnectableLayer* AddConvolution3dLayer(const Convolution3dDescriptor& convolution3dDescriptor, + const ConstTensor& weights, + const Optional& biases, + const char* name = nullptr); + /// Adds a depth to space layer to the network. /// @param depthToSpaceDescriptor - Parameters for the depth to space operation. /// @param name - Optional name for the layer. diff --git a/include/armnn/Types.hpp b/include/armnn/Types.hpp index 2fab6b44a9..ef52368365 100644 --- a/include/armnn/Types.hpp +++ b/include/armnn/Types.hpp @@ -53,7 +53,8 @@ enum class DataType enum class DataLayout { NCHW = 1, - NHWC = 2 + NHWC = 2, + NDHWC = 3 }; /// Define the behaviour of the internal profiler when outputting network details @@ -422,6 +423,8 @@ using InferenceTimingPair = std::pair; X(Shape) \ X(UnidirectionalSequenceLstm) \ X(ChannelShuffle) \ + X(Convolution3d) \ + // New layers should be added at last to minimize instability. /// When adding a new layer, adapt also the LastLayer enum value in the diff --git a/include/armnn/TypesUtils.hpp b/include/armnn/TypesUtils.hpp index 20fcbbd784..b644daafd8 100644 --- a/include/armnn/TypesUtils.hpp +++ b/include/armnn/TypesUtils.hpp @@ -218,9 +218,10 @@ constexpr const char* GetDataLayoutName(DataLayout dataLayout) { switch (dataLayout) { - case DataLayout::NCHW: return "NCHW"; - case DataLayout::NHWC: return "NHWC"; - default: return "Unknown"; + case DataLayout::NCHW: return "NCHW"; + case DataLayout::NHWC: return "NHWC"; + case DataLayout::NDHWC: return "NDHWC"; + default: return "Unknown"; } } diff --git a/include/armnn/backends/ILayerSupport.hpp b/include/armnn/backends/ILayerSupport.hpp index f511ee4c89..3744f316b1 100644 --- a/include/armnn/backends/ILayerSupport.hpp +++ b/include/armnn/backends/ILayerSupport.hpp @@ -106,6 +106,13 @@ public: const Optional& biases, Optional reasonIfUnsupported = EmptyOptional()) const = 0; + virtual bool IsConvolution3dSupported(const TensorInfo& input, + const TensorInfo& output, + const Convolution3dDescriptor& descriptor, + const TensorInfo& weights, + const Optional& biases, + Optional reasonIfUnsupported = EmptyOptional()) const = 0; + virtual bool IsDebugSupported(const TensorInfo& input, const TensorInfo& output, Optional reasonIfUnsupported = EmptyOptional()) const = 0; diff --git a/include/armnnUtils/DataLayoutIndexed.hpp b/include/armnnUtils/DataLayoutIndexed.hpp index b26f22043b..163d34b159 100644 --- a/include/armnnUtils/DataLayoutIndexed.hpp +++ b/include/armnnUtils/DataLayoutIndexed.hpp @@ -23,6 +23,7 @@ public: unsigned int GetChannelsIndex() const { return m_ChannelsIndex; } unsigned int GetHeightIndex() const { return m_HeightIndex; } unsigned int GetWidthIndex() const { return m_WidthIndex; } + unsigned int GetDepthIndex() const { return m_DepthIndex; } inline unsigned int GetIndex(const armnn::TensorShape& shape, unsigned int batchIndex, unsigned int channelIndex, @@ -63,6 +64,7 @@ private: unsigned int m_ChannelsIndex; unsigned int m_HeightIndex; unsigned int m_WidthIndex; + unsigned int m_DepthIndex; }; /// Equality methods diff --git a/src/armnn/BackendHelper.cpp b/src/armnn/BackendHelper.cpp index c17d076955..1616fd1aad 100644 --- a/src/armnn/BackendHelper.cpp +++ b/src/armnn/BackendHelper.cpp @@ -282,6 +282,21 @@ bool LayerSupportHandle::IsConvolution2dSupported(const TensorInfo& input, reasonIfUnsupported.value()); } +bool LayerSupportHandle::IsConvolution3dSupported(const TensorInfo& input, + const TensorInfo& output, + const Convolution3dDescriptor& descriptor, + const TensorInfo& weights, + const Optional& biases, + Optional reasonIfUnsupported) +{ + return m_LayerSupport->IsConvolution3dSupported(input, + output, + descriptor, + weights, + biases, + reasonIfUnsupported.value()); +} + bool LayerSupportHandle::IsDebugSupported(const TensorInfo& input, const TensorInfo& output, Optional reasonIfUnsupported) diff --git a/src/armnn/LayersFwd.hpp b/src/armnn/LayersFwd.hpp index 6f39ca0508..49c39b3985 100644 --- a/src/armnn/LayersFwd.hpp +++ b/src/armnn/LayersFwd.hpp @@ -21,6 +21,7 @@ #include "layers/ConvertFp32ToBf16Layer.hpp" #include "layers/ConvertFp32ToFp16Layer.hpp" #include "layers/Convolution2dLayer.hpp" +#include "layers/Convolution3dLayer.hpp" #include "layers/DebugLayer.hpp" #include "layers/DepthToSpaceLayer.hpp" #include "layers/DepthwiseConvolution2dLayer.hpp" @@ -119,6 +120,7 @@ DECLARE_LAYER(ConvertFp16ToFp32) DECLARE_LAYER(ConvertFp32ToBf16) DECLARE_LAYER(ConvertFp32ToFp16) DECLARE_LAYER(Convolution2d) +DECLARE_LAYER(Convolution3d) DECLARE_LAYER(Debug) DECLARE_LAYER(DepthToSpace) DECLARE_LAYER(DepthwiseConvolution2d) diff --git a/src/armnn/Network.cpp b/src/armnn/Network.cpp index 84097176e7..4070802be8 100644 --- a/src/armnn/Network.cpp +++ b/src/armnn/Network.cpp @@ -113,6 +113,15 @@ IConnectableLayer* INetwork::AddConvolution2dLayer(const Convolution2dDescriptor } +IConnectableLayer* INetwork::AddConvolution3dLayer(const Convolution3dDescriptor& convolution3dDescriptor, + const ConstTensor& weights, + const Optional& biases, + const char* name) +{ + return pNetworkImpl->AddConvolution3dLayer(convolution3dDescriptor, weights, biases, name); +} + + IConnectableLayer* INetwork::AddDepthToSpaceLayer(const DepthToSpaceDescriptor& depthToSpaceDescriptor, const char* name) { @@ -1991,22 +2000,21 @@ IConnectableLayer* NetworkImpl::AddConvolution2dLayer(const Convolution2dDescrip return AddConvolution2dLayerImpl(convolution2dDescriptor, weights, optionalBiases, name); } -IConnectableLayer* NetworkImpl::AddDepthwiseConvolution2dLayerImpl( - const DepthwiseConvolution2dDescriptor& convolution2dDescriptor, - const ConstTensor& weights, - const Optional& biases, - const char* name) +IConnectableLayer* NetworkImpl::AddConvolution3dLayer(const Convolution3dDescriptor& convolution3dDescriptor, + const ConstTensor& weights, + const Optional& biases, + const char* name) { - if (convolution2dDescriptor.m_BiasEnabled && !biases.has_value()) + if (convolution3dDescriptor.m_BiasEnabled && !biases.has_value()) { - throw InvalidArgumentException("AddDepthwiseConvolution2dLayer: biases cannot be empty"); + throw InvalidArgumentException("AddConvolution2dLayer: biases cannot be empty"); } - const auto layer = m_Graph->AddLayer(convolution2dDescriptor, name); + const auto layer = m_Graph->AddLayer(convolution3dDescriptor, name); layer->m_Weight = std::make_shared(weights); - if (convolution2dDescriptor.m_BiasEnabled) + if (convolution3dDescriptor.m_BiasEnabled) { layer->m_Bias = std::make_shared(biases.value()); } @@ -2020,6 +2028,29 @@ IConnectableLayer* NetworkImpl::AddDepthToSpaceLayer(const DepthToSpaceDescripto return m_Graph->AddLayer(depthToSpaceDescriptor, name); } +IConnectableLayer* NetworkImpl::AddDepthwiseConvolution2dLayerImpl( + const DepthwiseConvolution2dDescriptor& convolution2dDescriptor, + const ConstTensor& weights, + const Optional& biases, + const char* name) +{ + if (convolution2dDescriptor.m_BiasEnabled && !biases.has_value()) + { + throw InvalidArgumentException("AddDepthwiseConvolution2dLayer: biases cannot be empty"); + } + + const auto layer = m_Graph->AddLayer(convolution2dDescriptor, name); + + layer->m_Weight = std::make_shared(weights); + + if (convolution2dDescriptor.m_BiasEnabled) + { + layer->m_Bias = std::make_shared(biases.value()); + } + + return layer; +} + IConnectableLayer* NetworkImpl::AddDepthwiseConvolution2dLayer( const DepthwiseConvolution2dDescriptor& convolution2dDescriptor, const ConstTensor& weights, diff --git a/src/armnn/Network.hpp b/src/armnn/Network.hpp index 67c5b5af52..11759c71de 100644 --- a/src/armnn/Network.hpp +++ b/src/armnn/Network.hpp @@ -89,6 +89,11 @@ public: const ConstTensor& biases, const char* name = nullptr); + IConnectableLayer* AddConvolution3dLayer(const Convolution3dDescriptor& convolution3dDescriptor, + const ConstTensor& weights, + const Optional& biases, + const char* name = nullptr); + IConnectableLayer* AddConstantLayer(const ConstTensor& input, const char* name = nullptr); IConnectableLayer* AddDepthToSpaceLayer(const DepthToSpaceDescriptor& depthToSpaceDescriptor, diff --git a/src/armnn/SerializeLayerParameters.cpp b/src/armnn/SerializeLayerParameters.cpp index 73e0cbce78..da2c39d4b6 100644 --- a/src/armnn/SerializeLayerParameters.cpp +++ b/src/armnn/SerializeLayerParameters.cpp @@ -101,6 +101,33 @@ void StringifyLayerParameters::Serialize(ParameterStrin fn("DataLayout", GetDataLayoutName(desc.m_DataLayout)); } +void StringifyLayerParameters::Serialize(ParameterStringifyFunction& fn, + const Convolution3dDescriptor& desc) +{ + { + std::stringstream ss; + ss << "(" << desc.m_PadTop << "," << desc.m_PadLeft + << "," << desc.m_PadBottom << "," << desc.m_PadRight + << "," << desc.m_PadFront << "," << desc.m_PadBack << ")"; + fn("Padding(T,L,B,R,F,B)",ss.str()); + } + + { + std::stringstream ss; + ss << "(" << desc.m_StrideX << "," << desc.m_StrideY << "," << desc.m_StrideZ << ")"; + fn("Stride(X,Y,Z)", ss.str()); + } + + { + std::stringstream ss; + ss << "(" << desc.m_DilationX << "," << desc.m_DilationY << "," << desc.m_DilationZ << ")"; + fn("Dilation(X,Y)", ss.str()); + } + + fn("BiasEnabled",(desc.m_BiasEnabled ? "true" : "false")); + fn("DataLayout", GetDataLayoutName(desc.m_DataLayout)); +} + void StringifyLayerParameters::Serialize(ParameterStringifyFunction& fn, const DetectionPostProcessDescriptor& desc) { diff --git a/src/armnn/SerializeLayerParameters.hpp b/src/armnn/SerializeLayerParameters.hpp index f8fe5e2992..8a3630ce9d 100644 --- a/src/armnn/SerializeLayerParameters.hpp +++ b/src/armnn/SerializeLayerParameters.hpp @@ -55,6 +55,11 @@ template <> struct StringifyLayerParameters static void Serialize(ParameterStringifyFunction& fn, const Convolution2dDescriptor& desc); }; +template <> struct StringifyLayerParameters +{ + static void Serialize(ParameterStringifyFunction& fn, const Convolution3dDescriptor& desc); +}; + template <> struct StringifyLayerParameters { static void Serialize(ParameterStringifyFunction& fn, const DetectionPostProcessDescriptor& desc); diff --git a/src/armnn/layers/Convolution3dLayer.cpp b/src/armnn/layers/Convolution3dLayer.cpp new file mode 100644 index 0000000000..0e38c0b129 --- /dev/null +++ b/src/armnn/layers/Convolution3dLayer.cpp @@ -0,0 +1,172 @@ +// +// Copyright © 2021 Arm Ltd and Contributors. All rights reserved. +// SPDX-License-Identifier: MIT +// + +#include "Convolution3dLayer.hpp" +#include "LayerCloneBase.hpp" + +#include + +#include + +using namespace armnnUtils; + +namespace armnn +{ + +Convolution3dLayer::Convolution3dLayer(const Convolution3dDescriptor& param, const char* name) + : LayerWithParameters(1, 1, LayerType::Convolution3d, param, name) +{ +} + +void Convolution3dLayer::SerializeLayerParameters(ParameterStringifyFunction& fn) const +{ + const std::vector& inputShapes = + { + GetInputSlot(0).GetConnection()->GetTensorInfo().GetShape(), + m_Weight->GetTensorInfo().GetShape() + }; + + // Conv3d Filter Layout: [D,H,W,I,O] + const TensorShape filterShape = inputShapes[1]; + DataLayoutIndexed dataLayoutIndex(m_Param.m_DataLayout); + unsigned int filterDepth = filterShape[0]; + unsigned int filterHeight = filterShape[1]; + unsigned int filterWidth = filterShape[2]; + unsigned int inChannels = filterShape[3]; + unsigned int outChannels = filterShape[4]; + + fn("FilterDepth",std::to_string(filterDepth)); + fn("FilterHeight",std::to_string(filterHeight)); + fn("FilterWidth",std::to_string(filterWidth)); + fn("InputChannels",std::to_string(inChannels)); + fn("OutputChannels",std::to_string(outChannels)); + + LayerWithParameters::SerializeLayerParameters(fn); +} + +std::unique_ptr Convolution3dLayer::CreateWorkload(const IWorkloadFactory& factory) const +{ + // At this level constant data should not be released. + ARMNN_ASSERT_MSG(m_Weight != nullptr, "Convolution3dLayer: Weights data should not be null."); + + Convolution3dQueueDescriptor descriptor; + descriptor.m_Weight = m_Weight.get(); + + if (m_Param.m_BiasEnabled) + { + ARMNN_ASSERT_MSG(m_Bias != nullptr, "Convolution3dLayer: Bias data should not be null."); + descriptor.m_Bias = m_Bias.get(); + } + + SetAdditionalInfo(descriptor); + + return factory.CreateConvolution3d(descriptor, PrepInfoAndDesc(descriptor)); +} + +Convolution3dLayer* Convolution3dLayer::Clone(Graph& graph) const +{ + auto layer = CloneBase(graph, m_Param, GetName()); + + layer->m_Weight = m_Weight ? m_Weight : nullptr; + + if (layer->m_Param.m_BiasEnabled) + { + layer->m_Bias = m_Bias ? m_Bias : nullptr; + } + + return std::move(layer); +} + +std::vector Convolution3dLayer::InferOutputShapes(const std::vector& inputShapes) const +{ + ARMNN_ASSERT(inputShapes.size() == 2); + const TensorShape& inputShape = inputShapes[0]; + const TensorShape& filterShape = inputShapes[1]; + + ARMNN_ASSERT_MSG(inputShape.GetNumDimensions() == 5, "Convolutions will always have 5D input."); + + ARMNN_ASSERT( m_Param.m_StrideX > 0); + ARMNN_ASSERT( m_Param.m_StrideY > 0); + ARMNN_ASSERT( m_Param.m_StrideZ > 0); + + DataLayoutIndexed dataLayoutIndex(m_Param.m_DataLayout); + + unsigned int inWidth = inputShape[dataLayoutIndex.GetWidthIndex()]; + unsigned int inHeight = inputShape[dataLayoutIndex.GetHeightIndex()]; + unsigned int inDepth = inputShape[dataLayoutIndex.GetDepthIndex()]; + unsigned int inBatchSize = inputShape[0]; + + // Conv3d Filter Layout: [D,H,W,I,O] + unsigned int filterDepth = filterShape[0]; + unsigned int dilatedFilterDepth = filterDepth + (m_Param.m_DilationZ - 1) * (filterDepth - 1); + unsigned int readDepth = (inDepth + m_Param.m_PadFront + m_Param.m_PadBack) - dilatedFilterDepth; + unsigned int outDepth = 1 + (readDepth / m_Param.m_StrideZ); + + unsigned int filterHeight = filterShape[1]; + unsigned int dilatedFilterHeight = filterHeight + (m_Param.m_DilationY - 1) * (filterHeight - 1); + unsigned int readHeight = (inHeight + m_Param.m_PadTop + m_Param.m_PadBottom) - dilatedFilterHeight; + unsigned int outHeight = 1 + (readHeight / m_Param.m_StrideY); + + unsigned int filterWidth = filterShape[2]; + unsigned int dilatedFilterWidth = filterWidth + (m_Param.m_DilationX - 1) * (filterWidth - 1); + unsigned int readWidth = (inWidth + m_Param.m_PadLeft + m_Param.m_PadRight) - dilatedFilterWidth; + unsigned int outWidth = 1 + (readWidth / m_Param.m_StrideX); + + unsigned int outChannels = filterShape[4]; + unsigned int outBatchSize = inBatchSize; + + TensorShape tensorShape = TensorShape( { outBatchSize, outDepth, outHeight, outWidth, outChannels } ); + + return std::vector({ tensorShape }); +} + +void Convolution3dLayer::ValidateTensorShapesFromInputs() +{ + VerifyLayerConnections(1, CHECK_LOCATION()); + + const TensorShape& outputShape = GetOutputSlot(0).GetTensorInfo().GetShape(); + + VerifyShapeInferenceType(outputShape, m_ShapeInferenceMethod); + + // check if we m_Weight data is not nullptr + ARMNN_ASSERT_MSG(m_Weight != nullptr, "Convolution3dLayer: Weights data should not be null."); + + auto inferredShapes = InferOutputShapes({ + GetInputSlot(0).GetConnection()->GetTensorInfo().GetShape(), + m_Weight->GetTensorInfo().GetShape() }); + + ARMNN_ASSERT(inferredShapes.size() == 1); + + ValidateAndCopyShape(outputShape, inferredShapes[0], m_ShapeInferenceMethod, "Convolution3dLayer"); +} + +Layer::ConstantTensors Convolution3dLayer::GetConstantTensorsByRef() +{ + return {m_Weight, m_Bias}; +} + +ARMNN_NO_DEPRECATE_WARN_BEGIN +void Convolution3dLayer::Accept(ILayerVisitor& visitor) const +{ + IgnoreUnused(visitor); + throw armnn::Exception("Convolution3dLayer: VisitConvolution3dLayer is not implemented"); +} +ARMNN_NO_DEPRECATE_WARN_END + +void Convolution3dLayer::ExecuteStrategy(IStrategy& strategy) const +{ + ManagedConstTensorHandle managedWeight(m_Weight); + std::vector constTensors { { managedWeight.GetTensorInfo(), managedWeight.Map() } }; + + ManagedConstTensorHandle managedBias(m_Bias); + if (GetParameters().m_BiasEnabled) + { + constTensors.emplace_back(ConstTensor(managedBias.GetTensorInfo(), managedBias.Map())); + } + + strategy.ExecuteStrategy(this, GetParameters(), constTensors, GetName()); +} + +} // namespace armnn diff --git a/src/armnn/layers/Convolution3dLayer.hpp b/src/armnn/layers/Convolution3dLayer.hpp new file mode 100644 index 0000000000..bef5715098 --- /dev/null +++ b/src/armnn/layers/Convolution3dLayer.hpp @@ -0,0 +1,68 @@ +// +// Copyright © 2021 Arm Ltd and Contributors. All rights reserved. +// SPDX-License-Identifier: MIT +// + +#pragma once + +#include "LayerWithParameters.hpp" + +namespace armnn +{ + +class ScopedTensorHandle; + +/// This layer represents a convolution 3d operation. +class Convolution3dLayer : public LayerWithParameters +{ +public: + + /// A unique pointer to store Weight values. + std::shared_ptr m_Weight; + /// A unique pointer to store Bias values. + std::shared_ptr m_Bias; + + /// Makes a workload for the Convolution3d type. + /// @param [in] graph The graph where this layer can be found. + /// @param [in] factory The workload factory which will create the workload. + /// @return A pointer to the created workload, or nullptr if not created. + virtual std::unique_ptr CreateWorkload(const IWorkloadFactory& factory) const override; + + /// Creates a dynamically-allocated copy of this layer. + /// @param [in] graph The graph into which this layer is being cloned. + Convolution3dLayer* Clone(Graph& graph) const override; + + /// Check if the input tensor shape(s) + /// will lead to a valid configuration of @ref Convolution3dLayer. + /// @param [in] shapeInferenceMethod Indicates if output shape shall be overwritten or just validated. + void ValidateTensorShapesFromInputs() override; + + /// By default returns inputShapes if the number of inputs are equal to number of outputs, + /// otherwise infers the output shapes from given input shapes and layer properties. + /// @param [in] inputShapes The input shapes layer has. + /// @return A vector to the inferred output shape. + std::vector InferOutputShapes(const std::vector& inputShapes) const override; + + ARMNN_NO_DEPRECATE_WARN_BEGIN + void Accept(ILayerVisitor& visitor) const override; + ARMNN_NO_DEPRECATE_WARN_END + + void ExecuteStrategy(IStrategy& strategy) const override; + + void SerializeLayerParameters(ParameterStringifyFunction& fn) const override; + +protected: + /// Constructor to create a Convolution3dLayer. + /// @param [in] param Convolution3dDescriptor to configure the convolution3d operation. + /// @param [in] name Optional name for the layer. + Convolution3dLayer(const Convolution3dDescriptor& param, const char* name); + + /// Default destructor + ~Convolution3dLayer() = default; + + /// Retrieve the handles to the constant values stored by the layer. + /// @return A vector of the constant tensors stored by this layer. + ConstantTensors GetConstantTensorsByRef() override; +}; + +} // namespace diff --git a/src/armnn/test/InferOutputTests.cpp b/src/armnn/test/InferOutputTests.cpp index 81ad7b2d38..5365b831cf 100644 --- a/src/armnn/test/InferOutputTests.cpp +++ b/src/armnn/test/InferOutputTests.cpp @@ -38,6 +38,9 @@ ARMNN_SIMPLE_TEST_CASE(StackValidateTensorShapesFromInputsNoMatch, StackValidate // Convolution2D ARMNN_SIMPLE_TEST_CASE(Convolution2dInferOutputShape, Convolution2dInferOutputShapeTest) +// Convolution3D +ARMNN_SIMPLE_TEST_CASE(Convolution3dInferOutputShape, Convolution3dInferOutputShapeTest) + // DepthwiseConvolution2D ARMNN_SIMPLE_TEST_CASE(DepthwiseConvolution2dInferOutputShape, DepthwiseConvolution2dInferOutputShapeTest) diff --git a/src/armnn/test/InferOutputTests.hpp b/src/armnn/test/InferOutputTests.hpp index 6e2676ec8e..e2c854551f 100644 --- a/src/armnn/test/InferOutputTests.hpp +++ b/src/armnn/test/InferOutputTests.hpp @@ -464,6 +464,43 @@ void Convolution2dInferOutputShapeTest() CHECK(expectedOutputShape == convolution2dLayer->InferOutputShapes(shapes).at(0)); } +void Convolution3dInferOutputShapeTest() +{ + armnn::Graph graph; + + armnn::Convolution3dDescriptor descriptor; + descriptor.m_DilationX = 1; + descriptor.m_DilationY = 1; + descriptor.m_DilationZ = 1; + descriptor.m_PadTop = 1; + descriptor.m_PadBottom = 1; + descriptor.m_PadLeft = 1; + descriptor.m_PadRight = 1; + descriptor.m_PadFront = 1; + descriptor.m_PadBack = 1; + descriptor.m_StrideX = 2; + descriptor.m_StrideY = 2; + descriptor.m_StrideZ = 2; + descriptor.m_DataLayout = armnn::DataLayout::NDHWC; + + armnn::Convolution3dLayer* const convolution3dLayer = + graph.AddLayer(descriptor, "convolution3d"); + + std::vector shapes; + const std::vector inputSize = {1, 5, 5, 5, 1}; + armnn::TensorShape inputShape(5, inputSize.data()); + shapes.push_back(inputShape); + + const std::vector filterSize = {3, 3, 3, 1, 1 }; + armnn::TensorShape filterShape(5, filterSize.data()); + shapes.push_back(filterShape); + + const std::vector expectedOutputSizes = {1, 3, 3, 3, 1}; + armnn::TensorShape expectedOutputShape(5, expectedOutputSizes.data()); + + CHECK(expectedOutputShape == convolution3dLayer->InferOutputShapes(shapes).at(0)); +} + void TransposeConvolution2dInferOutputShapeTest() { armnn::Graph graph; diff --git a/src/armnnDeserializer/Deserializer.cpp b/src/armnnDeserializer/Deserializer.cpp index 13415814a2..eaeab780e4 100644 --- a/src/armnnDeserializer/Deserializer.cpp +++ b/src/armnnDeserializer/Deserializer.cpp @@ -221,6 +221,7 @@ m_ParserFunctions(Layer_MAX+1, &IDeserializer::DeserializerImpl::ParseUnsupporte m_ParserFunctions[Layer_ConcatLayer] = &DeserializerImpl::ParseConcat; m_ParserFunctions[Layer_ConstantLayer] = &DeserializerImpl::ParseConstant; m_ParserFunctions[Layer_Convolution2dLayer] = &DeserializerImpl::ParseConvolution2d; + m_ParserFunctions[Layer_Convolution3dLayer] = &DeserializerImpl::ParseConvolution3d; m_ParserFunctions[Layer_DepthToSpaceLayer] = &DeserializerImpl::ParseDepthToSpace; m_ParserFunctions[Layer_DepthwiseConvolution2dLayer] = &DeserializerImpl::ParseDepthwiseConvolution2d; m_ParserFunctions[Layer_DequantizeLayer] = &DeserializerImpl::ParseDequantize; @@ -304,6 +305,8 @@ LayerBaseRawPtr IDeserializer::DeserializerImpl::GetBaseLayer(const GraphPtr& gr return graphPtr->layers()->Get(layerIndex)->layer_as_ConstantLayer()->base(); case Layer::Layer_Convolution2dLayer: return graphPtr->layers()->Get(layerIndex)->layer_as_Convolution2dLayer()->base(); + case Layer::Layer_Convolution3dLayer: + return graphPtr->layers()->Get(layerIndex)->layer_as_Convolution3dLayer()->base(); case Layer::Layer_DepthToSpaceLayer: return graphPtr->layers()->Get(layerIndex)->layer_as_DepthToSpaceLayer()->base(); case Layer::Layer_DepthwiseConvolution2dLayer: @@ -444,6 +447,8 @@ armnn::DataLayout ToDataLayout(armnnSerializer::DataLayout dataLayout) { case armnnSerializer::DataLayout::DataLayout_NHWC: return armnn::DataLayout::NHWC; + case armnnSerializer::DataLayout::DataLayout_NDHWC: + return armnn::DataLayout::NDHWC; case armnnSerializer::DataLayout::DataLayout_NCHW: default: return armnn::DataLayout::NCHW; @@ -1392,6 +1397,56 @@ void IDeserializer::DeserializerImpl::ParseConvolution2d(GraphPtr graph, unsigne RegisterOutputSlots(graph, layerIndex, layer); } +void IDeserializer::DeserializerImpl::ParseConvolution3d(GraphPtr graph, unsigned int layerIndex) +{ + CHECK_LAYERS(graph, 0, layerIndex); + auto inputs = GetInputs(graph, layerIndex); + CHECK_LOCATION(); + CHECK_VALID_SIZE(inputs.size(), 1); + + auto outputs = GetOutputs(graph, layerIndex); + CHECK_VALID_SIZE(outputs.size(), 1); + + auto serializerLayer = graph->layers()->Get(layerIndex)->layer_as_Convolution3dLayer(); + auto layerName = GetLayerName(graph, layerIndex); + auto serializerDescriptor = serializerLayer->descriptor(); + + armnn::Convolution3dDescriptor descriptor; + descriptor.m_PadLeft = serializerDescriptor->padLeft(); + descriptor.m_PadRight = serializerDescriptor->padRight(); + descriptor.m_PadTop = serializerDescriptor->padTop(); + descriptor.m_PadBottom = serializerDescriptor->padBottom(); + descriptor.m_PadFront = serializerDescriptor->padFront(); + descriptor.m_PadBack = serializerDescriptor->padBack(); + descriptor.m_StrideX = serializerDescriptor->strideX(); + descriptor.m_StrideY = serializerDescriptor->strideY(); + descriptor.m_StrideZ = serializerDescriptor->strideZ(); + descriptor.m_DilationX = serializerDescriptor->dilationX(); + descriptor.m_DilationY = serializerDescriptor->dilationY(); + descriptor.m_DilationZ = serializerDescriptor->dilationZ(); + descriptor.m_BiasEnabled = serializerDescriptor->biasEnabled();; + descriptor.m_DataLayout = ToDataLayout(serializerDescriptor->dataLayout()); + + armnn::ConstTensor weights = ToConstTensor(serializerLayer->weights()); + armnn::ConstTensor biases; + + armnn::Optional optionalBiases = armnn::EmptyOptional(); + if (descriptor.m_BiasEnabled) + { + biases = ToConstTensor(serializerLayer->biases()); + optionalBiases = armnn::Optional(biases); + } + IConnectableLayer* layer = m_Network->AddConvolution3dLayer(descriptor, + weights, + optionalBiases, + layerName.c_str()); + armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]); + layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo); + + RegisterInputSlots(graph, layerIndex, layer); + RegisterOutputSlots(graph, layerIndex, layer); +} + void IDeserializer::DeserializerImpl::ParseDepthToSpace(GraphPtr graph, unsigned int layerIndex) { CHECK_LAYERS(graph, 0, layerIndex); diff --git a/src/armnnDeserializer/Deserializer.hpp b/src/armnnDeserializer/Deserializer.hpp index a07e41ff76..d2291c07a7 100644 --- a/src/armnnDeserializer/Deserializer.hpp +++ b/src/armnnDeserializer/Deserializer.hpp @@ -93,6 +93,7 @@ private: void ParseConcat(GraphPtr graph, unsigned int layerIndex); void ParseConstant(GraphPtr graph, unsigned int layerIndex); void ParseConvolution2d(GraphPtr graph, unsigned int layerIndex); + void ParseConvolution3d(GraphPtr graph, unsigned int layerIndex); void ParseDepthToSpace(GraphPtr graph, unsigned int layerIndex); void ParseDepthwiseConvolution2d(GraphPtr graph, unsigned int layerIndex); void ParseDequantize(GraphPtr graph, unsigned int layerIndex); diff --git a/src/armnnDeserializer/test/DeserializeConvolution3d.cpp b/src/armnnDeserializer/test/DeserializeConvolution3d.cpp new file mode 100644 index 0000000000..057ab6fbda --- /dev/null +++ b/src/armnnDeserializer/test/DeserializeConvolution3d.cpp @@ -0,0 +1,223 @@ +// +// Copyright © 2021 Arm Ltd and Contributors. All rights reserved. +// SPDX-License-Identifier: MIT +// + +#include "ParserFlatbuffersSerializeFixture.hpp" +#include + +#include + +TEST_SUITE("Deserializer_Convolution3d") +{ +struct Convolution3dFixture : public ParserFlatbuffersSerializeFixture +{ + explicit Convolution3dFixture(const std::string& inputShape, + const std::string& outputShape, + const std::string& weightsShape, + const std::string& dataType) + { + m_JsonString = R"( + { + inputIds: [0], + outputIds: [2], + layers: [ + { + layer_type: "InputLayer", + layer: { + base: { + layerBindingId: 0, + base: { + layerName: "InputLayer", + layerType: "Input", + inputSlots: [{ + index: 0, + connection: {sourceLayerIndex:0, outputSlotIndex:0 }, + }], + outputSlots: [ + { + index: 0, + tensorInfo: { + dimensions: )" + inputShape + R"(, + dataType: )" + dataType + R"(, + quantizationScale: 0.1, + dimensionSpecificity: [ + true, + true, + true, + true, + true + ] + } + } + ] + } + } + } + }, + { + layer_type: "Convolution3dLayer", + layer: { + base: { + index: 1, + layerName: "convolution3d", + layerType: "Convolution2d", + inputSlots: [ + { + index: 0, + connection: { + sourceLayerIndex: 0, + outputSlotIndex: 0 + } + } + ], + outputSlots: [ + { + index: 0, + tensorInfo: { + dimensions: )" + outputShape + R"(, + dataType: )" + dataType + R"(, + quantizationScale: 0.1, + dimensionSpecificity: [ + true, + true, + true, + true, + true + ] + } + } + ] + }, + descriptor: { + strideX: 2, + strideY: 2, + strideZ: 2 + }, + weights: { + info: { + dimensions: )" + weightsShape + R"(, + dataType: )" + dataType + R"(, + quantizationScale: 0.1, + dimensionSpecificity: [ + true, + true, + true, + true, + true + ] + }, + data_type: "ByteData", + data: { + data: [ + 1, 1, 1, + 1, 1, 1, + 1, 1, 1, + + 0, 0, 0, + 0, 0, 0, + 0, 0, 0, + + 0, 0, 0, + 0, 0, 0, + 0, 0, 0 + ] + } + } + } + }, + { + layer_type: "OutputLayer", + layer: { + base: { + layerBindingId: 2, + base: { + index: 2, + layerName: "OutputLayer", + layerType: "Output", + inputSlots: [ + { + connection: { + sourceLayerIndex: 1, + outputSlotIndex: 0 + } + } + ], + outputSlots: [{ + index: 0, + tensorInfo: { + dimensions: )" + outputShape + R"(, + dataType: )" + dataType + R"( + }, + }] + } + } + } + } + ], + featureVersions: { + bindingIdsScheme: 1, + weightsLayoutScheme: 1, + constantTensorsAsInputs: 1 + } + } + )"; + Setup(); + } + + +}; + +struct SimpleConvolution3dFixture : Convolution3dFixture +{ + SimpleConvolution3dFixture() : Convolution3dFixture( + "[ 1, 5, 5, 5, 1 ]", + "[ 1, 2, 2, 2, 1 ]", + "[ 3, 3, 3, 1, 1 ]", + "QAsymmS8") {} +}; + +TEST_CASE_FIXTURE(SimpleConvolution3dFixture, "Convolution3dInt8") +{ + RunTest<5, armnn::DataType::QAsymmS8>( + 0, + {{"InputLayer", { 0, 1, 2, 3, 4, + 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, + + 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, + 30, 31, 32, 33, 34, + 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, + + 45, 46, 47, 48, 49, + 50, 51, 52, 53, 54, + 55, 56, 57, 58, 59, + 60, 61, 62, 63, 64, + 65, 66, 67, 68, 69, + + 70, 71, 72, 73, 74, + 75, 76, 77, 78, 79, + 80, 81, 82, 83, 84, + 85, 86, 87, 88, 89, + 90, 91, 92, 93, 94, + 95, 96, 97, 98, 99, + + 100, 101, 102, 103, 104, + 105, 106, 107, 108, 109, + 110, 111, 112, 113, 114, + 115, 116, 117, 118, 119, + 120, 121, 122, 123, 124 + }}}, + {{"OutputLayer", {5, 7, + + 14, 16, + + 50, 52, + + 59, 61}}}); +} + +} diff --git a/src/armnnSerializer/ArmnnSchema.fbs b/src/armnnSerializer/ArmnnSchema.fbs index 740090bcc8..77982888c8 100644 --- a/src/armnnSerializer/ArmnnSchema.fbs +++ b/src/armnnSerializer/ArmnnSchema.fbs @@ -45,7 +45,8 @@ enum DataType : byte { enum DataLayout : byte { NHWC = 0, - NCHW = 1 + NCHW = 1, + NDHWC = 2 } enum ReduceOperation: byte { @@ -177,6 +178,7 @@ enum LayerType : uint { Shape = 62, UnidirectionalSequenceLstm = 63, ChannelShuffle = 64, + Convolution3d = 65, } // Base layer table to be used as part of other layers @@ -282,6 +284,30 @@ table Convolution2dDescriptor { dataLayout:DataLayout = NCHW; } +table Convolution3dLayer { + base:LayerBase; + descriptor:Convolution3dDescriptor; + weights:ConstTensor; + biases:ConstTensor; +} + +table Convolution3dDescriptor { + padLeft:uint; + padRight:uint; + padTop:uint; + padBottom:uint; + padFront:uint; + padBack:uint; + strideX:uint; + strideY:uint; + strideZ:uint; + dilationX:uint = 1; + dilationY:uint = 1; + dilationZ:uint = 1; + biasEnabled:bool = false; + dataLayout:DataLayout = NDHWC; +} + table DepthToSpaceLayer { base:LayerBase; descriptor:DepthToSpaceDescriptor; @@ -1012,6 +1038,7 @@ union Layer { ShapeLayer, UnidirectionalSequenceLstmLayer, ChannelShuffleLayer, + Convolution3dLayer, } table AnyLayer { diff --git a/src/armnnSerializer/ArmnnSchema_generated.h b/src/armnnSerializer/ArmnnSchema_generated.h index 653ea6a1e5..8234aa9c47 100644 --- a/src/armnnSerializer/ArmnnSchema_generated.h +++ b/src/armnnSerializer/ArmnnSchema_generated.h @@ -86,6 +86,12 @@ struct Convolution2dLayerBuilder; struct Convolution2dDescriptor; struct Convolution2dDescriptorBuilder; +struct Convolution3dLayer; +struct Convolution3dLayerBuilder; + +struct Convolution3dDescriptor; +struct Convolution3dDescriptorBuilder; + struct DepthToSpaceLayer; struct DepthToSpaceLayerBuilder; @@ -533,29 +539,32 @@ inline const char *EnumNameDataType(DataType e) { enum DataLayout { DataLayout_NHWC = 0, DataLayout_NCHW = 1, + DataLayout_NDHWC = 2, DataLayout_MIN = DataLayout_NHWC, - DataLayout_MAX = DataLayout_NCHW + DataLayout_MAX = DataLayout_NDHWC }; -inline const DataLayout (&EnumValuesDataLayout())[2] { +inline const DataLayout (&EnumValuesDataLayout())[3] { static const DataLayout values[] = { DataLayout_NHWC, - DataLayout_NCHW + DataLayout_NCHW, + DataLayout_NDHWC }; return values; } inline const char * const *EnumNamesDataLayout() { - static const char * const names[3] = { + static const char * const names[4] = { "NHWC", "NCHW", + "NDHWC", nullptr }; return names; } inline const char *EnumNameDataLayout(DataLayout e) { - if (flatbuffers::IsOutRange(e, DataLayout_NHWC, DataLayout_NCHW)) return ""; + if (flatbuffers::IsOutRange(e, DataLayout_NHWC, DataLayout_NDHWC)) return ""; const size_t index = static_cast(e); return EnumNamesDataLayout()[index]; } @@ -757,11 +766,12 @@ enum LayerType { LayerType_Shape = 62, LayerType_UnidirectionalSequenceLstm = 63, LayerType_ChannelShuffle = 64, + LayerType_Convolution3d = 65, LayerType_MIN = LayerType_Addition, - LayerType_MAX = LayerType_ChannelShuffle + LayerType_MAX = LayerType_Convolution3d }; -inline const LayerType (&EnumValuesLayerType())[65] { +inline const LayerType (&EnumValuesLayerType())[66] { static const LayerType values[] = { LayerType_Addition, LayerType_Input, @@ -827,13 +837,14 @@ inline const LayerType (&EnumValuesLayerType())[65] { LayerType_Cast, LayerType_Shape, LayerType_UnidirectionalSequenceLstm, - LayerType_ChannelShuffle + LayerType_ChannelShuffle, + LayerType_Convolution3d }; return values; } inline const char * const *EnumNamesLayerType() { - static const char * const names[66] = { + static const char * const names[67] = { "Addition", "Input", "Multiplication", @@ -899,13 +910,14 @@ inline const char * const *EnumNamesLayerType() { "Shape", "UnidirectionalSequenceLstm", "ChannelShuffle", + "Convolution3d", nullptr }; return names; } inline const char *EnumNameLayerType(LayerType e) { - if (flatbuffers::IsOutRange(e, LayerType_Addition, LayerType_ChannelShuffle)) return ""; + if (flatbuffers::IsOutRange(e, LayerType_Addition, LayerType_Convolution3d)) return ""; const size_t index = static_cast(e); return EnumNamesLayerType()[index]; } @@ -1250,11 +1262,12 @@ enum Layer { Layer_ShapeLayer = 63, Layer_UnidirectionalSequenceLstmLayer = 64, Layer_ChannelShuffleLayer = 65, + Layer_Convolution3dLayer = 66, Layer_MIN = Layer_NONE, - Layer_MAX = Layer_ChannelShuffleLayer + Layer_MAX = Layer_Convolution3dLayer }; -inline const Layer (&EnumValuesLayer())[66] { +inline const Layer (&EnumValuesLayer())[67] { static const Layer values[] = { Layer_NONE, Layer_ActivationLayer, @@ -1321,13 +1334,14 @@ inline const Layer (&EnumValuesLayer())[66] { Layer_CastLayer, Layer_ShapeLayer, Layer_UnidirectionalSequenceLstmLayer, - Layer_ChannelShuffleLayer + Layer_ChannelShuffleLayer, + Layer_Convolution3dLayer }; return values; } inline const char * const *EnumNamesLayer() { - static const char * const names[67] = { + static const char * const names[68] = { "NONE", "ActivationLayer", "AdditionLayer", @@ -1394,13 +1408,14 @@ inline const char * const *EnumNamesLayer() { "ShapeLayer", "UnidirectionalSequenceLstmLayer", "ChannelShuffleLayer", + "Convolution3dLayer", nullptr }; return names; } inline const char *EnumNameLayer(Layer e) { - if (flatbuffers::IsOutRange(e, Layer_NONE, Layer_ChannelShuffleLayer)) return ""; + if (flatbuffers::IsOutRange(e, Layer_NONE, Layer_Convolution3dLayer)) return ""; const size_t index = static_cast(e); return EnumNamesLayer()[index]; } @@ -1669,6 +1684,10 @@ template<> struct LayerTraits { static const Layer enum_value = Layer_ChannelShuffleLayer; }; +template<> struct LayerTraits { + static const Layer enum_value = Layer_Convolution3dLayer; +}; + bool VerifyLayer(flatbuffers::Verifier &verifier, const void *obj, Layer type); bool VerifyLayerVector(flatbuffers::Verifier &verifier, const flatbuffers::Vector> *values, const flatbuffers::Vector *types); @@ -3227,6 +3246,254 @@ inline flatbuffers::Offset CreateConvolution2dDescripto return builder_.Finish(); } +struct Convolution3dLayer FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { + typedef Convolution3dLayerBuilder Builder; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { + VT_BASE = 4, + VT_DESCRIPTOR = 6, + VT_WEIGHTS = 8, + VT_BIASES = 10 + }; + const armnnSerializer::LayerBase *base() const { + return GetPointer(VT_BASE); + } + const armnnSerializer::Convolution3dDescriptor *descriptor() const { + return GetPointer(VT_DESCRIPTOR); + } + const armnnSerializer::ConstTensor *weights() const { + return GetPointer(VT_WEIGHTS); + } + const armnnSerializer::ConstTensor *biases() const { + return GetPointer(VT_BIASES); + } + bool Verify(flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyOffset(verifier, VT_BASE) && + verifier.VerifyTable(base()) && + VerifyOffset(verifier, VT_DESCRIPTOR) && + verifier.VerifyTable(descriptor()) && + VerifyOffset(verifier, VT_WEIGHTS) && + verifier.VerifyTable(weights()) && + VerifyOffset(verifier, VT_BIASES) && + verifier.VerifyTable(biases()) && + verifier.EndTable(); + } +}; + +struct Convolution3dLayerBuilder { + typedef Convolution3dLayer Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_base(flatbuffers::Offset base) { + fbb_.AddOffset(Convolution3dLayer::VT_BASE, base); + } + void add_descriptor(flatbuffers::Offset descriptor) { + fbb_.AddOffset(Convolution3dLayer::VT_DESCRIPTOR, descriptor); + } + void add_weights(flatbuffers::Offset weights) { + fbb_.AddOffset(Convolution3dLayer::VT_WEIGHTS, weights); + } + void add_biases(flatbuffers::Offset biases) { + fbb_.AddOffset(Convolution3dLayer::VT_BIASES, biases); + } + explicit Convolution3dLayerBuilder(flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + Convolution3dLayerBuilder &operator=(const Convolution3dLayerBuilder &); + flatbuffers::Offset Finish() { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset CreateConvolution3dLayer( + flatbuffers::FlatBufferBuilder &_fbb, + flatbuffers::Offset base = 0, + flatbuffers::Offset descriptor = 0, + flatbuffers::Offset weights = 0, + flatbuffers::Offset biases = 0) { + Convolution3dLayerBuilder builder_(_fbb); + builder_.add_biases(biases); + builder_.add_weights(weights); + builder_.add_descriptor(descriptor); + builder_.add_base(base); + return builder_.Finish(); +} + +struct Convolution3dDescriptor FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { + typedef Convolution3dDescriptorBuilder Builder; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { + VT_PADLEFT = 4, + VT_PADRIGHT = 6, + VT_PADTOP = 8, + VT_PADBOTTOM = 10, + VT_PADFRONT = 12, + VT_PADBACK = 14, + VT_STRIDEX = 16, + VT_STRIDEY = 18, + VT_STRIDEZ = 20, + VT_DILATIONX = 22, + VT_DILATIONY = 24, + VT_DILATIONZ = 26, + VT_BIASENABLED = 28, + VT_DATALAYOUT = 30 + }; + uint32_t padLeft() const { + return GetField(VT_PADLEFT, 0); + } + uint32_t padRight() const { + return GetField(VT_PADRIGHT, 0); + } + uint32_t padTop() const { + return GetField(VT_PADTOP, 0); + } + uint32_t padBottom() const { + return GetField(VT_PADBOTTOM, 0); + } + uint32_t padFront() const { + return GetField(VT_PADFRONT, 0); + } + uint32_t padBack() const { + return GetField(VT_PADBACK, 0); + } + uint32_t strideX() const { + return GetField(VT_STRIDEX, 0); + } + uint32_t strideY() const { + return GetField(VT_STRIDEY, 0); + } + uint32_t strideZ() const { + return GetField(VT_STRIDEZ, 0); + } + uint32_t dilationX() const { + return GetField(VT_DILATIONX, 1); + } + uint32_t dilationY() const { + return GetField(VT_DILATIONY, 1); + } + uint32_t dilationZ() const { + return GetField(VT_DILATIONZ, 1); + } + bool biasEnabled() const { + return GetField(VT_BIASENABLED, 0) != 0; + } + armnnSerializer::DataLayout dataLayout() const { + return static_cast(GetField(VT_DATALAYOUT, 2)); + } + bool Verify(flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyField(verifier, VT_PADLEFT) && + VerifyField(verifier, VT_PADRIGHT) && + VerifyField(verifier, VT_PADTOP) && + VerifyField(verifier, VT_PADBOTTOM) && + VerifyField(verifier, VT_PADFRONT) && + VerifyField(verifier, VT_PADBACK) && + VerifyField(verifier, VT_STRIDEX) && + VerifyField(verifier, VT_STRIDEY) && + VerifyField(verifier, VT_STRIDEZ) && + VerifyField(verifier, VT_DILATIONX) && + VerifyField(verifier, VT_DILATIONY) && + VerifyField(verifier, VT_DILATIONZ) && + VerifyField(verifier, VT_BIASENABLED) && + VerifyField(verifier, VT_DATALAYOUT) && + verifier.EndTable(); + } +}; + +struct Convolution3dDescriptorBuilder { + typedef Convolution3dDescriptor Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_padLeft(uint32_t padLeft) { + fbb_.AddElement(Convolution3dDescriptor::VT_PADLEFT, padLeft, 0); + } + void add_padRight(uint32_t padRight) { + fbb_.AddElement(Convolution3dDescriptor::VT_PADRIGHT, padRight, 0); + } + void add_padTop(uint32_t padTop) { + fbb_.AddElement(Convolution3dDescriptor::VT_PADTOP, padTop, 0); + } + void add_padBottom(uint32_t padBottom) { + fbb_.AddElement(Convolution3dDescriptor::VT_PADBOTTOM, padBottom, 0); + } + void add_padFront(uint32_t padFront) { + fbb_.AddElement(Convolution3dDescriptor::VT_PADFRONT, padFront, 0); + } + void add_padBack(uint32_t padBack) { + fbb_.AddElement(Convolution3dDescriptor::VT_PADBACK, padBack, 0); + } + void add_strideX(uint32_t strideX) { + fbb_.AddElement(Convolution3dDescriptor::VT_STRIDEX, strideX, 0); + } + void add_strideY(uint32_t strideY) { + fbb_.AddElement(Convolution3dDescriptor::VT_STRIDEY, strideY, 0); + } + void add_strideZ(uint32_t strideZ) { + fbb_.AddElement(Convolution3dDescriptor::VT_STRIDEZ, strideZ, 0); + } + void add_dilationX(uint32_t dilationX) { + fbb_.AddElement(Convolution3dDescriptor::VT_DILATIONX, dilationX, 1); + } + void add_dilationY(uint32_t dilationY) { + fbb_.AddElement(Convolution3dDescriptor::VT_DILATIONY, dilationY, 1); + } + void add_dilationZ(uint32_t dilationZ) { + fbb_.AddElement(Convolution3dDescriptor::VT_DILATIONZ, dilationZ, 1); + } + void add_biasEnabled(bool biasEnabled) { + fbb_.AddElement(Convolution3dDescriptor::VT_BIASENABLED, static_cast(biasEnabled), 0); + } + void add_dataLayout(armnnSerializer::DataLayout dataLayout) { + fbb_.AddElement(Convolution3dDescriptor::VT_DATALAYOUT, static_cast(dataLayout), 2); + } + explicit Convolution3dDescriptorBuilder(flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + Convolution3dDescriptorBuilder &operator=(const Convolution3dDescriptorBuilder &); + flatbuffers::Offset Finish() { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset CreateConvolution3dDescriptor( + flatbuffers::FlatBufferBuilder &_fbb, + uint32_t padLeft = 0, + uint32_t padRight = 0, + uint32_t padTop = 0, + uint32_t padBottom = 0, + uint32_t padFront = 0, + uint32_t padBack = 0, + uint32_t strideX = 0, + uint32_t strideY = 0, + uint32_t strideZ = 0, + uint32_t dilationX = 1, + uint32_t dilationY = 1, + uint32_t dilationZ = 1, + bool biasEnabled = false, + armnnSerializer::DataLayout dataLayout = armnnSerializer::DataLayout_NDHWC) { + Convolution3dDescriptorBuilder builder_(_fbb); + builder_.add_dilationZ(dilationZ); + builder_.add_dilationY(dilationY); + builder_.add_dilationX(dilationX); + builder_.add_strideZ(strideZ); + builder_.add_strideY(strideY); + builder_.add_strideX(strideX); + builder_.add_padBack(padBack); + builder_.add_padFront(padFront); + builder_.add_padBottom(padBottom); + builder_.add_padTop(padTop); + builder_.add_padRight(padRight); + builder_.add_padLeft(padLeft); + builder_.add_dataLayout(dataLayout); + builder_.add_biasEnabled(biasEnabled); + return builder_.Finish(); +} + struct DepthToSpaceLayer FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { typedef DepthToSpaceLayerBuilder Builder; enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { @@ -9963,6 +10230,9 @@ struct AnyLayer FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { const armnnSerializer::ChannelShuffleLayer *layer_as_ChannelShuffleLayer() const { return layer_type() == armnnSerializer::Layer_ChannelShuffleLayer ? static_cast(layer()) : nullptr; } + const armnnSerializer::Convolution3dLayer *layer_as_Convolution3dLayer() const { + return layer_type() == armnnSerializer::Layer_Convolution3dLayer ? static_cast(layer()) : nullptr; + } bool Verify(flatbuffers::Verifier &verifier) const { return VerifyTableStart(verifier) && VerifyField(verifier, VT_LAYER_TYPE) && @@ -10232,6 +10502,10 @@ template<> inline const armnnSerializer::ChannelShuffleLayer *AnyLayer::layer_as return layer_as_ChannelShuffleLayer(); } +template<> inline const armnnSerializer::Convolution3dLayer *AnyLayer::layer_as() const { + return layer_as_Convolution3dLayer(); +} + struct AnyLayerBuilder { typedef AnyLayer Table; flatbuffers::FlatBufferBuilder &fbb_; @@ -10722,6 +10996,10 @@ inline bool VerifyLayer(flatbuffers::Verifier &verifier, const void *obj, Layer auto ptr = reinterpret_cast(obj); return verifier.VerifyTable(ptr); } + case Layer_Convolution3dLayer: { + auto ptr = reinterpret_cast(obj); + return verifier.VerifyTable(ptr); + } default: return true; } } diff --git a/src/armnnSerializer/Serializer.cpp b/src/armnnSerializer/Serializer.cpp index 9a3a270de5..efaf9f81cd 100644 --- a/src/armnnSerializer/Serializer.cpp +++ b/src/armnnSerializer/Serializer.cpp @@ -397,6 +397,54 @@ void SerializerStrategy::SerializeConvolution2dLayer(const armnn::IConnectableLa CreateAnyLayer(flatBufferLayer.o, serializer::Layer::Layer_Convolution2dLayer); } +// Build FlatBuffer for Convolution2dLayer +void SerializerStrategy::SerializeConvolution3dLayer(const armnn::IConnectableLayer* layer, + const armnn::Convolution3dDescriptor& descriptor, + const std::vector& constants, + const char* name) +{ + IgnoreUnused(name); + + const armnn::ConstTensor weights = constants[0]; + + // Create FlatBuffer BaseLayer + auto flatBufferBaseLayer = CreateLayerBase(layer, serializer::LayerType::LayerType_Convolution2d); + + auto flatBufferDescriptor = CreateConvolution3dDescriptor(m_flatBufferBuilder, + descriptor.m_PadLeft, + descriptor.m_PadRight, + descriptor.m_PadTop, + descriptor.m_PadBottom, + descriptor.m_PadFront, + descriptor.m_PadBack, + descriptor.m_StrideX, + descriptor.m_StrideY, + descriptor.m_StrideZ, + descriptor.m_DilationX, + descriptor.m_DilationY, + descriptor.m_DilationZ, + descriptor.m_BiasEnabled, + GetFlatBufferDataLayout(descriptor.m_DataLayout)); + auto flatBufferWeightsConstTensorInfo = CreateConstTensorInfo(weights); + flatbuffers::Offset flatBufferBiasesConstTensorInfo; + + if (constants.size() > 1) + { + const armnn::ConstTensor biases = constants[1]; + flatBufferBiasesConstTensorInfo = CreateConstTensorInfo(biases); + } + + // Create the FlatBuffer Convolution2dLayer + auto flatBufferLayer = CreateConvolution3dLayer(m_flatBufferBuilder, + flatBufferBaseLayer, + flatBufferDescriptor, + flatBufferWeightsConstTensorInfo, + flatBufferBiasesConstTensorInfo); + + // Add the AnyLayer to the FlatBufferLayers + CreateAnyLayer(flatBufferLayer.o, serializer::Layer::Layer_Convolution3dLayer); +} + void SerializerStrategy::SerializeDepthToSpaceLayer(const armnn::IConnectableLayer* layer, const armnn::DepthToSpaceDescriptor& descriptor, const char* name) @@ -2054,6 +2102,16 @@ void SerializerStrategy::ExecuteStrategy(const armnn::IConnectableLayer* layer, name); break; } + case armnn::LayerType::Convolution3d : + { + const armnn::Convolution3dDescriptor& layerDescriptor = + static_cast(descriptor); + SerializeConvolution3dLayer(layer, + layerDescriptor, + constants, + name); + break; + } case armnn::LayerType::DepthToSpace : { const armnn::DepthToSpaceDescriptor& layerDescriptor = diff --git a/src/armnnSerializer/Serializer.hpp b/src/armnnSerializer/Serializer.hpp index 43fb0f46b1..1161095c33 100644 --- a/src/armnnSerializer/Serializer.hpp +++ b/src/armnnSerializer/Serializer.hpp @@ -144,12 +144,17 @@ private: const char* name = nullptr); void SerializeConstantLayer(const armnn::IConnectableLayer* layer, - const std::vector& contants, + const std::vector& constants, const char* name = nullptr); void SerializeConvolution2dLayer(const armnn::IConnectableLayer* layer, const armnn::Convolution2dDescriptor& descriptor, - const std::vector& contants, + const std::vector& constants, + const char* name = nullptr); + + void SerializeConvolution3dLayer(const armnn::IConnectableLayer* layer, + const armnn::Convolution3dDescriptor& descriptor, + const std::vector& constants, const char* name = nullptr); void SerializeDepthToSpaceLayer(const armnn::IConnectableLayer* layer, diff --git a/src/armnnSerializer/SerializerUtils.cpp b/src/armnnSerializer/SerializerUtils.cpp index 85ce01d132..fca6db8449 100644 --- a/src/armnnSerializer/SerializerUtils.cpp +++ b/src/armnnSerializer/SerializerUtils.cpp @@ -97,6 +97,8 @@ armnnSerializer::DataLayout GetFlatBufferDataLayout(armnn::DataLayout dataLayout { case armnn::DataLayout::NHWC: return armnnSerializer::DataLayout::DataLayout_NHWC; + case armnn::DataLayout::NDHWC: + return armnnSerializer::DataLayout::DataLayout_NDHWC; case armnn::DataLayout::NCHW: default: return armnnSerializer::DataLayout::DataLayout_NCHW; diff --git a/src/armnnSerializer/test/SerializerTests.cpp b/src/armnnSerializer/test/SerializerTests.cpp index cd7fd5ca5b..2f8fd73717 100644 --- a/src/armnnSerializer/test/SerializerTests.cpp +++ b/src/armnnSerializer/test/SerializerTests.cpp @@ -439,6 +439,61 @@ TEST_CASE("SerializeConvolution2dWithPerAxisParams") deserializedNetwork->ExecuteStrategy(verifier); } +TEST_CASE("SerializeConvolution3d") +{ + const std::string layerName("convolution3d"); + const armnn::TensorInfo inputInfo ({ 1, 5, 5, 5, 1 }, armnn::DataType::Float32); + const armnn::TensorInfo outputInfo({ 1, 2, 2, 2, 1 }, armnn::DataType::Float32); + + const armnn::TensorInfo weightsInfo({ 3, 3, 3, 1, 1 }, armnn::DataType::Float32); + const armnn::TensorInfo biasesInfo ({ 1 }, armnn::DataType::Float32); + + std::vector weightsData = GenerateRandomData(weightsInfo.GetNumElements()); + armnn::ConstTensor weights(weightsInfo, weightsData); + + std::vector biasesData = GenerateRandomData(biasesInfo.GetNumElements()); + armnn::ConstTensor biases(biasesInfo, biasesData); + + armnn::Convolution3dDescriptor descriptor; + descriptor.m_PadLeft = 0; + descriptor.m_PadRight = 0; + descriptor.m_PadTop = 0; + descriptor.m_PadBottom = 0; + descriptor.m_PadFront = 0; + descriptor.m_PadBack = 0; + descriptor.m_DilationX = 1; + descriptor.m_DilationY = 1; + descriptor.m_DilationZ = 1; + descriptor.m_StrideX = 2; + descriptor.m_StrideY = 2; + descriptor.m_StrideZ = 2; + descriptor.m_BiasEnabled = true; + descriptor.m_DataLayout = armnn::DataLayout::NDHWC; + + armnn::INetworkPtr network = armnn::INetwork::Create(); + armnn::IConnectableLayer* const inputLayer = network->AddInputLayer(0); + armnn::IConnectableLayer* const convLayer = + network->AddConvolution3dLayer(descriptor, + weights, + armnn::Optional(biases), + layerName.c_str()); + armnn::IConnectableLayer* const outputLayer = network->AddOutputLayer(0); + + inputLayer->GetOutputSlot(0).Connect(convLayer->GetInputSlot(0)); + convLayer->GetOutputSlot(0).Connect(outputLayer->GetInputSlot(0)); + + inputLayer->GetOutputSlot(0).SetTensorInfo(inputInfo); + convLayer->GetOutputSlot(0).SetTensorInfo(outputInfo); + + armnn::INetworkPtr deserializedNetwork = DeserializeNetwork(SerializeNetwork(*network)); + CHECK(deserializedNetwork); + + const std::vector& constants {weights, biases}; + LayerVerifierBaseWithDescriptorAndConstants verifier( + layerName, {inputInfo}, {outputInfo}, descriptor, constants); + deserializedNetwork->ExecuteStrategy(verifier); +} + TEST_CASE("SerializeDepthToSpace") { const std::string layerName("depthToSpace"); diff --git a/src/armnnUtils/DataLayoutIndexed.cpp b/src/armnnUtils/DataLayoutIndexed.cpp index 18c005a7c3..c1c98fc0fd 100644 --- a/src/armnnUtils/DataLayoutIndexed.cpp +++ b/src/armnnUtils/DataLayoutIndexed.cpp @@ -25,6 +25,12 @@ DataLayoutIndexed::DataLayoutIndexed(armnn::DataLayout dataLayout) m_HeightIndex = 2; m_WidthIndex = 3; break; + case armnn::DataLayout::NDHWC: + m_DepthIndex = 1; + m_HeightIndex = 2; + m_WidthIndex = 3; + m_ChannelsIndex = 4; + break; default: throw armnn::InvalidArgumentException("Unknown DataLayout value: " + std::to_string(static_cast(dataLayout))); diff --git a/src/backends/backendsCommon/LayerSupportBase.cpp b/src/backends/backendsCommon/LayerSupportBase.cpp index 2753c927d5..2c3f827622 100644 --- a/src/backends/backendsCommon/LayerSupportBase.cpp +++ b/src/backends/backendsCommon/LayerSupportBase.cpp @@ -165,6 +165,16 @@ bool LayerSupportBase::IsConvolution2dSupported(const TensorInfo&, // input return DefaultLayerSupport(__func__, __FILE__, __LINE__, reasonIfUnsupported); } +bool LayerSupportBase::IsConvolution3dSupported(const TensorInfo&, // input + const TensorInfo&, // output + const Convolution3dDescriptor&, // descriptor + const TensorInfo&, // weights + const Optional&, // biases + Optional reasonIfUnsupported) const +{ + return DefaultLayerSupport(__func__, __FILE__, __LINE__, reasonIfUnsupported); +} + bool LayerSupportBase::IsDebugSupported(const TensorInfo&, // input const TensorInfo&, // output Optional reasonIfUnsupported) const diff --git a/src/backends/backendsCommon/LayerSupportBase.hpp b/src/backends/backendsCommon/LayerSupportBase.hpp index cc68a220e2..240b1dab73 100644 --- a/src/backends/backendsCommon/LayerSupportBase.hpp +++ b/src/backends/backendsCommon/LayerSupportBase.hpp @@ -94,6 +94,13 @@ public: const Optional& biases, Optional reasonIfUnsupported = EmptyOptional()) const override; + bool IsConvolution3dSupported(const TensorInfo& input, + const TensorInfo& output, + const Convolution3dDescriptor& descriptor, + const TensorInfo& weights, + const Optional& biases, + Optional reasonIfUnsupported = EmptyOptional()) const override; + bool IsDebugSupported(const TensorInfo& input, const TensorInfo& output, Optional reasonIfUnsupported = EmptyOptional()) const override; diff --git a/src/backends/backendsCommon/WorkloadData.cpp b/src/backends/backendsCommon/WorkloadData.cpp index a6def847fa..fe22133104 100644 --- a/src/backends/backendsCommon/WorkloadData.cpp +++ b/src/backends/backendsCommon/WorkloadData.cpp @@ -1322,6 +1322,67 @@ void Convolution2dQueueDescriptor::Validate(const WorkloadInfo& workloadInfo) co } } +void Convolution3dQueueDescriptor::Validate(const WorkloadInfo& workloadInfo) const +{ + const std::string descriptorName{"Convolution3dQueueDescriptor"}; + + ValidateNumInputs(workloadInfo, descriptorName, 1); + ValidateNumOutputs(workloadInfo, descriptorName, 1); + + const TensorInfo& inputTensorInfo = workloadInfo.m_InputTensorInfos[0]; + const TensorInfo& outputTensorInfo = workloadInfo.m_OutputTensorInfos[0]; + + ValidateTensorNumDimensions(inputTensorInfo, descriptorName, 5, "input"); + ValidateTensorNumDimensions(outputTensorInfo, descriptorName, 5, "output"); + + ValidatePointer(m_Weight, descriptorName, "weight"); + + const TensorInfo& weightTensorInfo = m_Weight->GetTensorInfo(); + ValidateTensorNumDimensions(weightTensorInfo, descriptorName, 5, "weight"); + + ValidateWeightDataType(inputTensorInfo, weightTensorInfo, descriptorName); + + Optional optionalBiasTensorInfo; + if (m_Parameters.m_BiasEnabled) + { + ValidatePointer(m_Bias, descriptorName, "bias"); + + optionalBiasTensorInfo = MakeOptional(m_Bias->GetTensorInfo()); + const TensorInfo& biasTensorInfo = optionalBiasTensorInfo.value(); + + ValidateTensorDataType(biasTensorInfo, GetBiasDataType(inputTensorInfo.GetDataType()), descriptorName, "bias"); + ValidateBiasTensorQuantization(biasTensorInfo, inputTensorInfo, weightTensorInfo, descriptorName); + } + + if (m_Parameters.m_StrideX <= 0 || m_Parameters.m_StrideY <= 0 || m_Parameters.m_StrideZ <= 0 ) + { + throw InvalidArgumentException( + fmt::format("{}: strideX (provided {}), strideY (provided {}) or strideZ (provided {})" + "cannot be either negative or 0.", + descriptorName, m_Parameters.m_StrideX, m_Parameters.m_StrideY, m_Parameters.m_StrideZ)); + } + + ValidatePerAxisQuantization(inputTensorInfo, + outputTensorInfo, + weightTensorInfo, + optionalBiasTensorInfo, + descriptorName); + + std::vector supportedTypes = + { + DataType::BFloat16, + DataType::Float16, + DataType::Float32, + DataType::QAsymmS8, + DataType::QAsymmU8, + DataType::QSymmS16, + DataType::QSymmS8 + }; + + ValidateDataTypes(inputTensorInfo, supportedTypes, descriptorName); + ValidateTensorDataTypesMatch(inputTensorInfo, outputTensorInfo, descriptorName, "input", "output"); +} + void DepthwiseConvolution2dQueueDescriptor::Validate(const WorkloadInfo& workloadInfo) const { const std::string descriptorName{"DepthwiseConvolution2dQueueDescriptor"}; diff --git a/src/backends/backendsCommon/WorkloadData.hpp b/src/backends/backendsCommon/WorkloadData.hpp index b90c29c1b4..896081ecfd 100644 --- a/src/backends/backendsCommon/WorkloadData.hpp +++ b/src/backends/backendsCommon/WorkloadData.hpp @@ -208,6 +208,21 @@ struct Convolution2dQueueDescriptor : QueueDescriptorWithParameters +{ + Convolution3dQueueDescriptor() + : m_Weight(nullptr) + , m_Bias(nullptr) + { + } + + const ConstTensorHandle* m_Weight; + const ConstTensorHandle* m_Bias; + + void Validate(const WorkloadInfo& workloadInfo) const; +}; + /// Depthwise Convolution 2D layer workload data. /// /// @note diff --git a/src/backends/backendsCommon/WorkloadFactory.cpp b/src/backends/backendsCommon/WorkloadFactory.cpp index 00263eca04..666f83de71 100644 --- a/src/backends/backendsCommon/WorkloadFactory.cpp +++ b/src/backends/backendsCommon/WorkloadFactory.cpp @@ -225,14 +225,13 @@ bool IWorkloadFactory::IsLayerConfigurationSupported(const BackendId& backendId, const TensorInfo output = OverrideDataType(layer.GetOutputSlot(0).GetTensorInfo(), dataType); ARMNN_ASSERT(cLayer->m_Weight.get() != nullptr); - const Convolution2dDescriptor& descriptor = cLayer->GetParameters(); + const Convolution2dDescriptor& descriptor = cLayer->GetParameters(); // Construct optional biases object based on the value of m_BiasEnabled Optional biases; if (descriptor.m_BiasEnabled) { - biases = - OverrideDataType(cLayer->m_Bias->GetTensorInfo(), GetBiasTypeFromWeightsType(dataType)); + biases = OverrideDataType(cLayer->m_Bias->GetTensorInfo(), GetBiasTypeFromWeightsType(dataType)); } result = layerSupportObject.IsConvolution2dSupported( @@ -244,6 +243,33 @@ bool IWorkloadFactory::IsLayerConfigurationSupported(const BackendId& backendId, reason); break; } + case LayerType::Convolution3d: + { + auto cLayer = PolymorphicDowncast(&layer); + + const TensorInfo input = OverrideDataType(layer.GetInputSlot(0).GetConnection()->GetTensorInfo(), + dataType); + const TensorInfo output = OverrideDataType(layer.GetOutputSlot(0).GetTensorInfo(), dataType); + ARMNN_ASSERT(cLayer->m_Weight.get() != nullptr); + + const Convolution3dDescriptor& descriptor = cLayer->GetParameters(); + + // Construct optional biases object based on the value of m_BiasEnabled + Optional biases; + if (descriptor.m_BiasEnabled) + { + biases = OverrideDataType(cLayer->m_Bias->GetTensorInfo(), GetBiasTypeFromWeightsType(dataType)); + } + + result = layerSupportObject.IsConvolution3dSupported( + input, + output, + descriptor, + OverrideDataType(cLayer->m_Weight->GetTensorInfo(), dataType), + biases, + reason); + break; + } case LayerType::Debug: { const TensorInfo& input = layer.GetInputSlot(0).GetConnection()->GetTensorInfo(); @@ -1570,6 +1596,12 @@ std::unique_ptr IWorkloadFactory::CreateConvolution2d(const Convoluti return std::unique_ptr(); } +std::unique_ptr IWorkloadFactory::CreateConvolution3d(const Convolution3dQueueDescriptor& /*descriptor*/, + const WorkloadInfo& /*info*/) const +{ + return std::unique_ptr(); +} + std::unique_ptr IWorkloadFactory::CreateDebug(const DebugQueueDescriptor& /*descriptor*/, const WorkloadInfo& /*info*/) const { diff --git a/src/backends/backendsCommon/WorkloadFactory.hpp b/src/backends/backendsCommon/WorkloadFactory.hpp index e84657ea13..c16fcb882b 100644 --- a/src/backends/backendsCommon/WorkloadFactory.hpp +++ b/src/backends/backendsCommon/WorkloadFactory.hpp @@ -115,7 +115,10 @@ public: const WorkloadInfo& info) const; virtual std::unique_ptr CreateConvolution2d(const Convolution2dQueueDescriptor& descriptor, - const WorkloadInfo& info) const; + const WorkloadInfo& info) const; + + virtual std::unique_ptr CreateConvolution3d(const Convolution3dQueueDescriptor& descriptor, + const WorkloadInfo& info) const; virtual std::unique_ptr CreateDebug(const DebugQueueDescriptor& descriptor, const WorkloadInfo& info) const; diff --git a/src/backends/backendsCommon/common.mk b/src/backends/backendsCommon/common.mk index 7d3558c804..8c1037ad19 100644 --- a/src/backends/backendsCommon/common.mk +++ b/src/backends/backendsCommon/common.mk @@ -50,6 +50,7 @@ COMMON_TEST_SOURCES := \ test/layerTests/ConcatTestImpl.cpp \ test/layerTests/ConstantTestImpl.cpp \ test/layerTests/Conv2dTestImpl.cpp \ + test/layerTests/Conv3dTestImpl.cpp \ test/layerTests/ConvertBf16ToFp32TestImpl.cpp \ test/layerTests/ConvertFp16ToFp32TestImpl.cpp \ test/layerTests/ConvertFp32ToBf16TestImpl.cpp \ diff --git a/src/backends/backendsCommon/test/CMakeLists.txt b/src/backends/backendsCommon/test/CMakeLists.txt index c17ab0fb0f..ea33513097 100644 --- a/src/backends/backendsCommon/test/CMakeLists.txt +++ b/src/backends/backendsCommon/test/CMakeLists.txt @@ -75,6 +75,8 @@ list(APPEND armnnBackendsCommonUnitTests_sources layerTests/ConstantTestImpl.hpp layerTests/Conv2dTestImpl.cpp layerTests/Conv2dTestImpl.hpp + layerTests/Conv3dTestImpl.cpp + layerTests/Conv3dTestImpl.hpp layerTests/ConvertBf16ToFp32TestImpl.cpp layerTests/ConvertBf16ToFp32TestImpl.hpp layerTests/ConvertFp16ToFp32TestImpl.cpp diff --git a/src/backends/backendsCommon/test/IsLayerSupportedTestImpl.hpp b/src/backends/backendsCommon/test/IsLayerSupportedTestImpl.hpp index c2d21842f2..76312ce984 100644 --- a/src/backends/backendsCommon/test/IsLayerSupportedTestImpl.hpp +++ b/src/backends/backendsCommon/test/IsLayerSupportedTestImpl.hpp @@ -638,6 +638,8 @@ DECLARE_LAYER_POLICY_1_PARAM(ConvertFp32ToFp16) DECLARE_LAYER_POLICY_2_PARAM(Convolution2d) +DECLARE_LAYER_POLICY_2_PARAM(Convolution3d) + DECLARE_LAYER_POLICY_1_PARAM(MemCopy) DECLARE_LAYER_POLICY_1_PARAM(MemImport) diff --git a/src/backends/backendsCommon/test/LayerTests.hpp b/src/backends/backendsCommon/test/LayerTests.hpp index 9f1fa88b16..0dcd3d1564 100644 --- a/src/backends/backendsCommon/test/LayerTests.hpp +++ b/src/backends/backendsCommon/test/LayerTests.hpp @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include diff --git a/src/backends/backendsCommon/test/layerTests/Conv3dTestImpl.cpp b/src/backends/backendsCommon/test/layerTests/Conv3dTestImpl.cpp new file mode 100644 index 0000000000..2f02189051 --- /dev/null +++ b/src/backends/backendsCommon/test/layerTests/Conv3dTestImpl.cpp @@ -0,0 +1,1038 @@ +// +// Copyright © 2021 Arm Ltd and Contributors. All rights reserved. +// SPDX-License-Identifier: MIT +// + +#include "Conv3dTestImpl.hpp" + +#include + +#include + +#include + +#include +#include + +#include + +using namespace armnnUtils; + +// +// Helper templates +// + +// Helper template that returns a quantized bias depending on the number of output channels. +template> +std::vector GetBiasData(bool biasEnabled, float qScale, armnn::TensorInfo outputInfo, armnn::DataLayout layout) +{ + if(!biasEnabled) + { + return std::vector(); + } + else + { + const armnnUtils::DataLayoutIndexed dataLayoutIndexed(layout); + const unsigned int outputChannels = outputInfo.GetShape()[dataLayoutIndexed.GetChannelsIndex()]; + + switch (outputChannels) + { + case 1: + { + return QuantizedVector({2}, qScale, 0); + } + case 2: + default: + { + return QuantizedVector({0, 2}, qScale, 0); + } + } + } +} + +// Modifies a std::vector in-place using a specified bias. +template +void ApplyBiasToData(std::vector& v, const std::vector& bias, + float vScale, int32_t vOffset, + float bScale, int32_t bOffset) +{ + ARMNN_ASSERT_MSG((armnn::IsQuantizedType() && vScale != 0.0f) || (!armnn::IsQuantizedType()), + "Invalid type and parameter combination."); + ARMNN_ASSERT_MSG((armnn::IsQuantizedType() && bScale != 0.0f) || (!armnn::IsQuantizedType()), + "Invalid type and parameter combination."); + + for (uint32_t i = 0; i < bias.size(); ++i) + { + for (long unsigned int j = i; j < v.size(); j+=bias.size()) + { + // Note we need to dequantize and re-quantize the image value and the bias. + float dBias = SelectiveDequantize(bias[i], bScale, bOffset); + + T& outRef = v[j]; + float dOutput = SelectiveDequantize(outRef, vScale, vOffset); + outRef = SelectiveQuantize(dOutput + dBias, vScale, vOffset); + } + } +} + +// Set the quantization scale and offset values for data types. +template +void SetScaleOffset(float& qScale, int32_t& qOffset) +{ + switch (ArmnnType) + { + case armnn::DataType::QAsymmU8: + { + qScale = 0.1f; + qOffset = 128; + break; + } + case armnn::DataType::QAsymmS8: + case armnn::DataType::QSymmS16: + { + qScale = 0.1f; + qOffset = 0; + break; + } + case armnn::DataType::BFloat16: + case armnn::DataType::Float16: + case armnn::DataType::Float32: + default: + { + qScale = 0.f; + qOffset = 0; + break; + } + } +} + +// Create a vector from 0 to size and quantize (if required). +template +std::vector CreateQuantizedData(int32_t size, float qScale, int32_t qOffset) +{ + std::vector data; + for (int32_t i = 0; i < size; ++i) + { + data.push_back(static_cast(i)); + } + + return QuantizedVector(data, qScale, qOffset); +} + +// Create a vector from 0 to size divided and then quantized (if required) to create smaller floating point values. +template +std::vector CreateSmallQuantizedData(int32_t size, float divisor, float qScale, int32_t qOffset) +{ + std::vector data; + for (int32_t i = 0; i < size; ++i) + { + float value = static_cast(i); + data.push_back(value/divisor); + } + + return QuantizedVector(data, qScale, qOffset);; +} + +// +// Convolution3d implementations +// + +template, + typename B = armnn::ResolveType> +LayerTestResult SimpleConvolution3dTestImpl( + armnn::IWorkloadFactory& workloadFactory, + const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager, + const armnn::ITensorHandleFactory& tensorHandleFactory, + const std::vector& input, + const std::vector& kernel, + const std::vector& bias, + const std::vector& outputExpected, + const armnn::TensorShape& inputShape, + const armnn::TensorShape& kernelShape, + const armnn::TensorShape& outputExpectedShape, + const armnn::DataLayout dataLayout, + float qScale, + int32_t qOffset, + uint32_t strideX = 1, + uint32_t strideY = 1, + uint32_t strideZ = 1, + uint32_t dilationX = 1, + uint32_t dilationY = 1, + uint32_t dilationZ = 1, + uint32_t padLeft = 0, + uint32_t padTop = 0, + uint32_t padRight = 0, + uint32_t padBottom = 0, + uint32_t padFront = 0, + uint32_t padBack = 0) +{ + unsigned int inputNum = armnn::numeric_cast(inputShape[0]); + unsigned int inputDepth = armnn::numeric_cast(inputShape[1]); + unsigned int inputHeight = armnn::numeric_cast(inputShape[2]); + unsigned int inputWidth = armnn::numeric_cast(inputShape[3]); + unsigned int inputChannels = armnn::numeric_cast(inputShape[4]); + + // Conv3d weights/kernel layout: [D,H,W,I,O] + unsigned int kernelDepth = armnn::numeric_cast(kernelShape[0]); + unsigned int kernelHeight = armnn::numeric_cast(kernelShape[1]); + unsigned int kernelWidth = armnn::numeric_cast(kernelShape[2]); + unsigned int kernelInChannels = armnn::numeric_cast(kernelShape[3]); + unsigned int kernelOutChannels = armnn::numeric_cast(kernelShape[4]); + + unsigned int outputNum = armnn::numeric_cast(outputExpectedShape[0]); + unsigned int outputDepth = armnn::numeric_cast(outputExpectedShape[1]); + unsigned int outputHeight = armnn::numeric_cast(outputExpectedShape[2]); + unsigned int outputWidth = armnn::numeric_cast(outputExpectedShape[3]); + unsigned int outputChannels = armnn::numeric_cast(outputExpectedShape[4]); + + bool biasEnabled = bias.size() > 0; + + // If a bias is used, its size must equal the number of output channels. + ARMNN_ASSERT(!biasEnabled || bias.size() == outputChannels); + + // Creates the tensors. + armnn::TensorInfo inputTensorInfo({inputNum, inputDepth, inputHeight, inputWidth, inputChannels}, ArmnnType); + armnn::TensorInfo outputTensorInfo({outputNum, outputDepth, outputHeight, outputWidth, outputChannels}, ArmnnType); + armnn::TensorInfo kernelDesc({kernelDepth, kernelHeight, kernelWidth, kernelInChannels, kernelOutChannels}, + ArmnnType); + armnn::TensorInfo biasDesc({static_cast(bias.size())}, ArmnnBType); + + // 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); + kernelDesc.SetQuantizationScale(qScale); + kernelDesc.SetQuantizationOffset(qOffset); + biasDesc.SetQuantizationScale(qScale*qScale); + biasDesc.SetQuantizationOffset(0); + } + + // Construct the input data. + std::vector inputData; + inputData.assign(input.data(), input.data() + inputNum*inputDepth*inputHeight*inputWidth*inputChannels); + + // Construct the output data and apply bias if needed. + std::vector outputData; + outputData.assign(outputExpected.data(), outputExpected.data() + + outputNum*outputDepth*outputHeight*outputWidth*outputChannels); + + if (biasEnabled) + { + ApplyBiasToData(outputData, bias, + outputTensorInfo.GetQuantizationScale(), outputTensorInfo.GetQuantizationOffset(), + biasDesc.GetQuantizationScale(), biasDesc.GetQuantizationOffset()); + } + + std::vector actualOutput(outputTensorInfo.GetNumElements()); + + std::unique_ptr inputHandle = tensorHandleFactory.CreateTensorHandle(inputTensorInfo); + std::unique_ptr outputHandle = tensorHandleFactory.CreateTensorHandle(outputTensorInfo); + + armnn::ScopedTensorHandle weightsTensor(kernelDesc); + AllocateAndCopyDataToITensorHandle(&weightsTensor, kernel.data()); + + armnn::ScopedTensorHandle biasTensor(biasDesc); + if (biasEnabled) + { + AllocateAndCopyDataToITensorHandle(&biasTensor, bias.data()); + } + + armnn::Convolution3dQueueDescriptor data; + data.m_Weight = &weightsTensor; + data.m_Bias = &biasTensor; // Still set this whether or not bias is enabled - it can be a source of bugs. + data.m_Parameters.m_StrideX = strideX; + data.m_Parameters.m_StrideY = strideY; + data.m_Parameters.m_StrideZ = strideZ; + data.m_Parameters.m_PadLeft = padLeft; + data.m_Parameters.m_PadRight = padRight; + data.m_Parameters.m_PadTop = padTop; + data.m_Parameters.m_PadBottom = padBottom; + data.m_Parameters.m_PadFront = padFront; + data.m_Parameters.m_PadBack = padBack; + data.m_Parameters.m_DilationX = dilationX; + data.m_Parameters.m_DilationY = dilationY; + data.m_Parameters.m_DilationZ = dilationZ; + data.m_Parameters.m_DataLayout = dataLayout; + data.m_Parameters.m_BiasEnabled = biasEnabled; + + armnn::WorkloadInfo info; + AddInputToWorkload(data, info, inputTensorInfo, inputHandle.get()); + AddOutputToWorkload(data, info, outputTensorInfo, outputHandle.get()); + + std::unique_ptr workload = workloadFactory.CreateConvolution3d(data, info); + inputHandle->Allocate(); + outputHandle->Allocate(); + + CopyDataToITensorHandle(inputHandle.get(), inputData.data()); + + ExecuteWorkload(*workload, memoryManager); + + CopyDataFromITensorHandle(actualOutput.data(), outputHandle.get()); + + return LayerTestResult(actualOutput, + outputData, + outputHandle->GetShape(), + outputTensorInfo.GetShape()); +} + +template> +LayerTestResult SimpleConvolution3d3x3x3TestCommon( + armnn::IWorkloadFactory& workloadFactory, + const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager, + const armnn::ITensorHandleFactory& tensorHandleFactory, + bool biasEnabled, + armnn::DataLayout dataLayout) +{ + float qScale; + int32_t qOffset; + SetScaleOffset(qScale, qOffset); + + armnn::TensorInfo inputDesc({ 1, 5, 5, 5, 1 }, ArmnnType); + std::vector input = CreateQuantizedData(125, qScale, qOffset); + + armnn::TensorInfo kernelDesc({ 3, 3, 3, 1, 1 }, ArmnnType); + std::vector kernel = QuantizedVector( + { + 1, 1, 1, + 1, 1, 1, + 1, 1, 1, + + 0, 0, 0, + 0, 1, 0, + 0, 0, 0, + + 1, 1, 1, + 1, 1, 1, + 1, 1, 1, + }, + qScale, qOffset); + + armnn::TensorInfo outputDesc({ 1, 3, 3, 3, 1 }, ArmnnType); + std::vector outputData = QuantizedVector( + { + 589, 608, 627, + 684, 703, 722, + 779, 798, 817, + + 1064, 1083, 1102, + 1159, 1178, 1197, + 1254, 1273, 1292, + + 1539, 1558, 1577, + 1634, 1653, 1672, + 1729, 1748, 1767 + }, + qScale, qOffset); + + return SimpleConvolution3dTestImpl( + workloadFactory, + memoryManager, + tensorHandleFactory, + input, + kernel, + GetBiasData(biasEnabled, qScale * qScale, outputDesc, dataLayout), + outputData, + inputDesc.GetShape(), + kernelDesc.GetShape(), + outputDesc.GetShape(), + dataLayout, + qScale, + qOffset + ); +} + +template> +LayerTestResult Convolution3d2x2x2Strides3x5x5TestCommon( + armnn::IWorkloadFactory& workloadFactory, + const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager, + const armnn::ITensorHandleFactory& tensorHandleFactory, + bool biasEnabled, + armnn::DataLayout dataLayout) +{ + float qScale; + int32_t qOffset; + SetScaleOffset(qScale, qOffset); + + armnn::TensorInfo inputDesc({ 1, 3, 10, 10, 1 }, ArmnnType); + std::vector input = CreateQuantizedData(300, qScale, qOffset); + + armnn::TensorInfo kernelDesc({ 3, 5, 5, 1, 1 }, ArmnnType); + std::vector kernel = QuantizedVector( + { + 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, + + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + + 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, + }, + qScale, qOffset); + + armnn::TensorInfo outputDesc({ 1, 1, 3, 3, 1 }, ArmnnType); + std::vector outputData = QuantizedVector( + { + 11650, 11800, 11950, + + 13150, 13300, 13450, + + 14650, 14800, 14950 + }, + qScale, qOffset); + + return SimpleConvolution3dTestImpl( + workloadFactory, + memoryManager, + tensorHandleFactory, + input, + kernel, + GetBiasData(biasEnabled, qScale * qScale, outputDesc, dataLayout), + outputData, + inputDesc.GetShape(), + kernelDesc.GetShape(), + outputDesc.GetShape(), + dataLayout, + qScale, + qOffset, + 2, // strideX + 2, // strideY + 2 // strideZ + ); +} + +template> +LayerTestResult Convolution3d2x2x2Dilation2x2x2TestCommon( + armnn::IWorkloadFactory& workloadFactory, + const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager, + const armnn::ITensorHandleFactory& tensorHandleFactory, + bool biasEnabled, + armnn::DataLayout dataLayout) +{ + float qScale; + int32_t qOffset; + SetScaleOffset(qScale, qOffset); + + armnn::TensorInfo inputDesc({ 1, 5, 5, 5, 2 }, ArmnnType); + std::vector input = CreateQuantizedData(250, qScale, qOffset); + + armnn::TensorInfo kernelDesc({ 2, 2, 2, 2, 2 }, ArmnnType); + std::vector kernel = QuantizedVector( + { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, -1, -1, + 1, 1, -1, 1, -1, 1, -1, 1, -1, -1, -1, 1, -1, 1, -1, 1, + }, + qScale, qOffset); + + // Since the dilation rate is 3 this will dilate the kernel to be 4x4, + // therefore the output will be 2x2 + armnn::TensorInfo outputDesc({ 1, 2, 2, 2, 2 }, ArmnnType); + std::vector outputData = QuantizedVector( + { + -1124, 974, + -1148, 978, + + -1244, 994, + -1268, 998, + + -1724, 1074, + -1748, 1078, + + -1844, 1094, + -1868, 1098 + }, + qScale, qOffset); + + return SimpleConvolution3dTestImpl( + workloadFactory, + memoryManager, + tensorHandleFactory, + input, + kernel, + GetBiasData(biasEnabled, qScale * qScale, outputDesc, dataLayout), + outputData, + inputDesc.GetShape(), + kernelDesc.GetShape(), + outputDesc.GetShape(), + dataLayout, + qScale, + qOffset, + 1, // strideX + 1, // strideY + 1, // strideZ + 3, // dilationX + 3, // dilationY + 3 // dilationZ + ); +} + +template> +LayerTestResult Convolution3dPaddingSame3x3x3TestCommon( + armnn::IWorkloadFactory& workloadFactory, + const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager, + const armnn::ITensorHandleFactory& tensorHandleFactory, + bool biasEnabled, + armnn::DataLayout dataLayout) +{ + float qScale; + int32_t qOffset; + SetScaleOffset(qScale, qOffset); + + armnn::TensorInfo inputDesc({ 1, 5, 5, 5, 1 }, ArmnnType); + std::vector input = CreateQuantizedData(125, qScale, qOffset); + + armnn::TensorInfo kernelDesc({ 3, 3, 3, 1, 1 }, ArmnnType); + std::vector kernel = QuantizedVector( + { + 1, 1, 1, + 1, 1, 1, + 1, 1, 1, + + 0, 0, 0, + 0, 0, 0, + 0, 0, 0, + + 1, 1, 1, + 1, 1, 1, + 1, 1, 1, + }, + qScale, qOffset); + + armnn::TensorInfo outputDesc({ 1, 5, 5, 5, 1 }, ArmnnType); + std::vector outputData = QuantizedVector( + { + 112, 171, 177, 183, 124, + 183, 279, 288, 297, 201, + 213, 324, 333, 342, 231, + 243, 369, 378, 387, 261, + 172, 261, 267, 273, 184, + + 224, 342, 354, 366, 248, + 366, 558, 576, 594, 402, + 426, 648, 666, 684, 462, + 486, 738, 756, 774, 522, + 344, 522, 534, 546, 368, + + 424, 642, 654, 666, 448, + 666, 1008, 1026, 1044, 702, + 726, 1098, 1116, 1134, 762, + 786, 1188, 1206, 1224, 822, + 544, 822, 834, 846, 568, + 624, 942, 954, 966, 648, + + 966, 1458, 1476, 1494, 1002, + 1026, 1548, 1566, 1584, 1062, + 1086, 1638, 1656, 1674, 1122, + 744, 1122, 1134, 1146, 768, + 312, 471, 477, 483, 324, + 483, 729, 738, 747, 501, + 513, 774, 783, 792, 531, + 543, 819, 828, 837, 561, + 372, 561, 567, 573, 384 + }, + qScale, qOffset); + + return SimpleConvolution3dTestImpl( + workloadFactory, + memoryManager, + tensorHandleFactory, + input, + kernel, + GetBiasData(biasEnabled, qScale * qScale, outputDesc, dataLayout), + outputData, + inputDesc.GetShape(), + kernelDesc.GetShape(), + outputDesc.GetShape(), + dataLayout, + qScale, + qOffset, + 1, // strideX + 1, // strideY + 1, // strideZ + 1, // dilationX + 1, // dilationY + 1, // dilationZ + 1, // padLeft + 1, // padTop + 1, // padRight + 1, // padBottom + 1, // padFront + 1 // padBack + ); +} + +LayerTestResult Convolution3dStrideDilationPadding3x3x3TestCommonFloat32( + armnn::IWorkloadFactory& workloadFactory, + const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager, + const armnn::ITensorHandleFactory& tensorHandleFactory, + bool biasEnabled, + armnn::DataLayout dataLayout) +{ + float qScale = 0.f; + int32_t qOffset = 0; + + armnn::TensorInfo inputDesc({ 1, 3, 10, 10, 2 }, armnn::DataType::Float32); + std::vector input = CreateSmallQuantizedData(600, 100.0f, qScale, qOffset); + + armnn::TensorInfo kernelDesc({ 3, 3, 3, 2, 2 }, armnn::DataType::Float32); + std::vector kernel = CreateSmallQuantizedData(108, 100.0f, qScale, qOffset); + + // Since the dilation rate is 2 this will dilate the kernel to be 5x5: d(K-1)+1 --> 2 x (3-1) + 1 = 5, + // therefore the output will be 1x4x4: (I − K + 2P)/S +1 => trunc((10 - 3 + 2x2 )/3 + 1)) + // where, dilation size = d = 2; kernel size = K = 3; input size = I = 10; padding size = P = 2; stride = S = 3 + armnn::TensorInfo outputDesc({ 1, 1, 4, 4, 2 }, armnn::DataType::Float32); + std::vector outputData = + { + 12.0312f, 12.2268f, 17.7512f, 18.0494f, + 18.176f, 18.4814f, 5.6912f, 5.7938f, + 19.1664f, 19.5078f, 28.119f, 28.6383f, + 28.6914f, 29.2215f, 8.9094f, 9.0873f, + + 23.1264f, 23.5398f, 33.843f, 34.4703f, + 34.4154f, 35.0535f, 10.6734f, 10.8873f, + 6.2712f, 6.417f, 9.0718f, 9.2929f, + 9.2194f, 9.4441f, 2.7862f, 2.8615f + }; + + return SimpleConvolution3dTestImpl( + workloadFactory, + memoryManager, + tensorHandleFactory, + input, + kernel, + GetBiasData(biasEnabled, qScale * qScale, outputDesc, dataLayout), + outputData, + inputDesc.GetShape(), + kernelDesc.GetShape(), + outputDesc.GetShape(), + dataLayout, + qScale, + qOffset, + 3, // strideX + 3, // strideY + 3, // strideZ + 2, // dilationX + 2, // dilationY + 2, // dilationZ + 1, // padLeft + 1, // padTop + 1, // padRight + 1, // padBottom + 1, // padFront + 1 // padBack + ); +} + +LayerTestResult Convolution3d2x2x2Stride3x3x3SmallTestCommonFloat32( + armnn::IWorkloadFactory& workloadFactory, + const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager, + const armnn::ITensorHandleFactory& tensorHandleFactory, + bool biasEnabled, + armnn::DataLayout dataLayout) +{ + float qScale = 0.f; + int32_t qOffset = 0; + + armnn::TensorInfo inputDesc({ 1, 3, 10, 10, 1 }, armnn::DataType::Float32); + std::vector input = CreateSmallQuantizedData(300, 100.0f, qScale, qOffset); + + armnn::TensorInfo kernelDesc({ 3, 3, 3, 1, 1 }, armnn::DataType::Float32); + std::vector kernel = + { + 0.125977f, 0.150391f, 0.101562f, + 0.0585938f, 0.0864258f, 0.043457f, + 0.034668f, 0.0322266f, 0.0385742f, + + 0.125977f, 0.150391f, -0.101562f, + -0.0585938f,-0.0864258f,-0.043457f, + -0.0104630f, 0.0154114f, 0.0013768f, + + 0.0344238f, 0.035644f, 0.0495605f, + 0.0683594f, 0.099121f, -0.0461426f, + -0.0996094f,-0.126953f, -0.043457f, + }; + + armnn::TensorInfo outputDesc({ 1, 1, 4, 4, 1 }, armnn::DataType::Float32); + std::vector outputData = + { + -0.08156067f, -0.06891209f, -0.05589598f, -0.04310101f, + 0.04584253f, 0.05855697f, 0.07129729f, 0.08325434f, + 0.17304349f, 0.18521416f, 0.19818866f, 0.21096253f, + 0.29965734f, 0.312698f, 0.32547557f, 0.33818722f + }; + + return SimpleConvolution3dTestImpl( + workloadFactory, + memoryManager, + tensorHandleFactory, + input, + kernel, + GetBiasData(biasEnabled, qScale * qScale, outputDesc, dataLayout), + outputData, + inputDesc.GetShape(), + kernelDesc.GetShape(), + outputDesc.GetShape(), + dataLayout, + qScale, + qOffset, + 2, // strideX + 2, // strideY + 2 // strideZ + ); +} + +LayerTestResult Convolution3d2x3x3TestCommonFloat16( + armnn::IWorkloadFactory& workloadFactory, + const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager, + const armnn::ITensorHandleFactory& tensorHandleFactory, + bool biasEnabled, + armnn::DataLayout dataLayout) +{ + using namespace half_float::literal; + + float qScale = 0.f; + int32_t qOffset = 0; + + armnn::TensorInfo inputDesc({ 1, 2, 3, 3, 2 }, armnn::DataType::Float16); + const std::vector input = + { + 1._h, 2._h, 3._h, + 4._h, 5._h, 6._h, + + 7._h, 8._h, 9._h, + 10._h, 11._h, 12._h, + + 13._h, 14._h, 15._h, + 16._h, 17._h, 18._h, + + 19._h, 20._h, 21._h, + 22._h, 23._h, 24._h, + + 25._h, 26._h, 27._h, + 28._h, 29._h, 30._h, + + 31._h, 32._h, 33._h, + 34._h, 35._h, 36._h + }; + + armnn::TensorInfo kernelDesc({ 2, 2, 2, 2, 2 }, armnn::DataType::Float16); + std::vector kernel = + { + -1._h, -1._h, -1._h, -1._h, -1._h, -1._h, -1._h, -1._h, + -1._h, -1._h, -1._h, 1._h, 1._h, 1._h, -1._h, -1._h, + 1._h, 1._h, -1._h, 1._h, -1._h, 1._h, -1._h, 1._h, + -1._h, -1._h, -1._h, 1._h, -1._h, 1._h, -1._h, 1._h, + }; + + armnn::TensorInfo outputDesc({ 1, 1, 2, 2, 2 }, armnn::DataType::Float16); + std::vector outputData = + { + -176._h, 128._h, + -200._h, 132._h, + + -248._h, 140._h, + -272._h, 144._h + }; + + return SimpleConvolution3dTestImpl( + workloadFactory, + memoryManager, + tensorHandleFactory, + input, + kernel, + GetBiasData(biasEnabled, qScale * qScale, outputDesc, dataLayout), + outputData, + inputDesc.GetShape(), + kernelDesc.GetShape(), + outputDesc.GetShape(), + dataLayout, + qScale, + qOffset + ); +} + +LayerTestResult Convolution3d2x2x2SmallTestCommonFloat16( + armnn::IWorkloadFactory& workloadFactory, + const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager, + const armnn::ITensorHandleFactory& tensorHandleFactory, + bool biasEnabled, + armnn::DataLayout dataLayout) +{ + using namespace half_float::literal; + + float qScale = 0.f; + int32_t qOffset = 0; + + armnn::TensorInfo inputDesc({ 1, 2, 4, 4, 1 }, armnn::DataType::Float16); + const std::vector input = + { + 0.0367984_h, 0.0380895_h, 0.0420157_h, 0.0675631_h, + 0.0938920_h, 0.0476106_h, 0.1035490_h, 0.1260370_h, + 0.0461647_h, 0.0883828_h, 0.1159540_h, 0.0498519_h, + 0.0104630_h, 0.0154114_h, 0.00137681_h, 0.0344238_h, + + 0.0356445_h, 0.0495605_h, 0.0683594_h, 0.0991211_h, + 0.0461426_h, 0.0996094_h, 0.1269530_h, 0.0393066_h, + 0.103516_h, 0.032544_h, 0.124334_h, 0.0564566_h, + 0.0123544_h, 0.0461647_h, 0.0883828_h, 0.1159540_h, + }; + + armnn::TensorInfo kernelDesc({ 2, 2, 2, 1, 1 }, armnn::DataType::Float16); + std::vector kernel = + { + -0.126184_h, -0.150468_h, + -0.101412_h, -0.0586369_h, + + -0.0435089_h, 0.0347555_h, + 0.0323111_h, 0.0385381_h + }; + + armnn::TensorInfo outputDesc({ 1, 1, 3, 3, 1 }, armnn::DataType::Float16); + std::vector outputData = + { + -0.01718917_h, -0.01370182_h, -0.02727737_h, + + -0.02282543_h, -0.03144084_h, -0.04468598_h, + + -0.02228982_h, -0.02244923_h, -0.02042268_h + }; + + return SimpleConvolution3dTestImpl( + workloadFactory, + memoryManager, + tensorHandleFactory, + input, + kernel, + GetBiasData(biasEnabled, qScale * qScale, outputDesc, dataLayout), + outputData, + inputDesc.GetShape(), + kernelDesc.GetShape(), + outputDesc.GetShape(), + dataLayout, + qScale, + qOffset + ); +} + +LayerTestResult SimpleConvolution3d3x3x3Float32Test( + armnn::IWorkloadFactory& workloadFactory, + const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager, + const armnn::ITensorHandleFactory& tensorHandleFactory, + bool biasEnabled) +{ + return SimpleConvolution3d3x3x3TestCommon( + workloadFactory, memoryManager, tensorHandleFactory, biasEnabled, armnn::DataLayout::NDHWC); +} + +LayerTestResult SimpleConvolution3d3x3x3Int8Test( + armnn::IWorkloadFactory& workloadFactory, + const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager, + const armnn::ITensorHandleFactory& tensorHandleFactory, + bool biasEnabled) +{ + return SimpleConvolution3d3x3x3TestCommon( + workloadFactory, memoryManager, tensorHandleFactory, biasEnabled, armnn::DataLayout::NDHWC); +} + +LayerTestResult SimpleConvolution3d3x3x3Uint8Test( + armnn::IWorkloadFactory& workloadFactory, + const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager, + const armnn::ITensorHandleFactory& tensorHandleFactory, + bool biasEnabled) +{ + return SimpleConvolution3d3x3x3TestCommon( + workloadFactory, memoryManager, tensorHandleFactory, biasEnabled, armnn::DataLayout::NDHWC); +} + +LayerTestResult SimpleConvolution3d3x3x3Int16Test( + armnn::IWorkloadFactory& workloadFactory, + const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager, + const armnn::ITensorHandleFactory& tensorHandleFactory, + bool biasEnabled) +{ + return SimpleConvolution3d3x3x3TestCommon( + workloadFactory, memoryManager, tensorHandleFactory, biasEnabled, armnn::DataLayout::NDHWC); +} + + +LayerTestResult Convolution3d2x2x2Strides3x5x5Float32Test( + armnn::IWorkloadFactory& workloadFactory, + const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager, + const armnn::ITensorHandleFactory& tensorHandleFactory, + bool biasEnabled) +{ + return Convolution3d2x2x2Strides3x5x5TestCommon( + workloadFactory, memoryManager, tensorHandleFactory, biasEnabled, armnn::DataLayout::NDHWC); +} + +LayerTestResult Convolution3d2x2x2Strides3x5x5Int8Test( + armnn::IWorkloadFactory& workloadFactory, + const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager, + const armnn::ITensorHandleFactory& tensorHandleFactory, + bool biasEnabled) +{ + return Convolution3d2x2x2Strides3x5x5TestCommon( + workloadFactory, memoryManager, tensorHandleFactory, biasEnabled, armnn::DataLayout::NDHWC); +} + +LayerTestResult Convolution3d2x2x2Strides3x5x5Uint8Test( + armnn::IWorkloadFactory& workloadFactory, + const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager, + const armnn::ITensorHandleFactory& tensorHandleFactory, + bool biasEnabled) +{ + return Convolution3d2x2x2Strides3x5x5TestCommon( + workloadFactory, memoryManager, tensorHandleFactory, biasEnabled, armnn::DataLayout::NDHWC); +} + +LayerTestResult Convolution3d2x2x2Strides3x5x5Int16Test( + armnn::IWorkloadFactory& workloadFactory, + const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager, + const armnn::ITensorHandleFactory& tensorHandleFactory, + bool biasEnabled) +{ + return Convolution3d2x2x2Strides3x5x5TestCommon( + workloadFactory, memoryManager, tensorHandleFactory, biasEnabled, armnn::DataLayout::NDHWC); +} + +LayerTestResult Convolution3d2x2x2Dilation2x2x2Float32Test( + armnn::IWorkloadFactory& workloadFactory, + const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager, + const armnn::ITensorHandleFactory& tensorHandleFactory, + bool biasEnabled) +{ + return Convolution3d2x2x2Dilation2x2x2TestCommon( + workloadFactory, memoryManager, tensorHandleFactory, biasEnabled, armnn::DataLayout::NDHWC); +} + +LayerTestResult Convolution3d2x2x2Dilation2x2x2Int8Test( + armnn::IWorkloadFactory& workloadFactory, + const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager, + const armnn::ITensorHandleFactory& tensorHandleFactory, + bool biasEnabled) +{ + return Convolution3d2x2x2Dilation2x2x2TestCommon( + workloadFactory, memoryManager, tensorHandleFactory, biasEnabled, armnn::DataLayout::NDHWC); +} + +LayerTestResult Convolution3d2x2x2Dilation2x2x2Uint8Test( + armnn::IWorkloadFactory& workloadFactory, + const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager, + const armnn::ITensorHandleFactory& tensorHandleFactory, + bool biasEnabled) +{ + return Convolution3d2x2x2Dilation2x2x2TestCommon( + workloadFactory, memoryManager, tensorHandleFactory, biasEnabled, armnn::DataLayout::NDHWC); +} + +LayerTestResult Convolution3d2x2x2Dilation2x2x2Int16Test( + armnn::IWorkloadFactory& workloadFactory, + const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager, + const armnn::ITensorHandleFactory& tensorHandleFactory, + bool biasEnabled) +{ + return Convolution3d2x2x2Dilation2x2x2TestCommon( + workloadFactory, memoryManager, tensorHandleFactory, biasEnabled, armnn::DataLayout::NDHWC); +} + +LayerTestResult Convolution3dPaddingSame3x3x3Float32Test( + armnn::IWorkloadFactory& workloadFactory, + const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager, + const armnn::ITensorHandleFactory& tensorHandleFactory, + bool biasEnabled) +{ + return Convolution3dPaddingSame3x3x3TestCommon( + workloadFactory, memoryManager, tensorHandleFactory, biasEnabled, armnn::DataLayout::NDHWC); +} + +LayerTestResult Convolution3dPaddingSame3x3x3Int8Test( + armnn::IWorkloadFactory& workloadFactory, + const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager, + const armnn::ITensorHandleFactory& tensorHandleFactory, + bool biasEnabled) +{ + return Convolution3dPaddingSame3x3x3TestCommon( + workloadFactory, memoryManager, tensorHandleFactory, biasEnabled, armnn::DataLayout::NDHWC); +} + +LayerTestResult Convolution3dPaddingSame3x3x3Uint8Test( + armnn::IWorkloadFactory& workloadFactory, + const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager, + const armnn::ITensorHandleFactory& tensorHandleFactory, + bool biasEnabled) +{ + return Convolution3dPaddingSame3x3x3TestCommon( + workloadFactory, memoryManager, tensorHandleFactory, biasEnabled, armnn::DataLayout::NDHWC); +} + +LayerTestResult Convolution3dPaddingSame3x3x3Int16Test( + armnn::IWorkloadFactory& workloadFactory, + const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager, + const armnn::ITensorHandleFactory& tensorHandleFactory, + bool biasEnabled) +{ + return Convolution3dPaddingSame3x3x3TestCommon( + workloadFactory, memoryManager, tensorHandleFactory, biasEnabled, armnn::DataLayout::NDHWC); +} + +LayerTestResult Convolution3dStrideDilationPadding3x3x3Float32Test( + armnn::IWorkloadFactory& workloadFactory, + const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager, + const armnn::ITensorHandleFactory& tensorHandleFactory, + bool biasEnabled) +{ + return Convolution3dStrideDilationPadding3x3x3TestCommonFloat32( + workloadFactory, memoryManager, tensorHandleFactory, biasEnabled, armnn::DataLayout::NDHWC); +} + +LayerTestResult Convolution3d2x2x2Stride3x3x3SmallFloat32Test( + armnn::IWorkloadFactory& workloadFactory, + const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager, + const armnn::ITensorHandleFactory& tensorHandleFactory, + bool biasEnabled) +{ + return Convolution3d2x2x2Stride3x3x3SmallTestCommonFloat32( + workloadFactory, memoryManager, tensorHandleFactory, biasEnabled, armnn::DataLayout::NDHWC); +} + +LayerTestResult Convolution3d2x3x3Float16Test( + armnn::IWorkloadFactory& workloadFactory, + const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager, + const armnn::ITensorHandleFactory& tensorHandleFactory, + bool biasEnabled) +{ + return Convolution3d2x3x3TestCommonFloat16( + workloadFactory, memoryManager, tensorHandleFactory, biasEnabled, armnn::DataLayout::NDHWC); +} + +LayerTestResult Convolution3d2x2x2SmallFloat16Test( + armnn::IWorkloadFactory& workloadFactory, + const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager, + const armnn::ITensorHandleFactory& tensorHandleFactory, + bool biasEnabled) +{ + return Convolution3d2x2x2SmallTestCommonFloat16( + workloadFactory, memoryManager, tensorHandleFactory, biasEnabled, armnn::DataLayout::NDHWC); +} diff --git a/src/backends/backendsCommon/test/layerTests/Conv3dTestImpl.hpp b/src/backends/backendsCommon/test/layerTests/Conv3dTestImpl.hpp new file mode 100644 index 0000000000..45a6786e39 --- /dev/null +++ b/src/backends/backendsCommon/test/layerTests/Conv3dTestImpl.hpp @@ -0,0 +1,141 @@ +// +// Copyright © 2021 Arm Ltd and Contributors. All rights reserved. +// SPDX-License-Identifier: MIT +// + +#pragma once + +#include "LayerTestResult.hpp" + +#include + +#include + +#include + +#include +#include + +// +// Convolution3d +// + +LayerTestResult SimpleConvolution3d3x3x3Float32Test( + armnn::IWorkloadFactory& workloadFactory, + const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager, + const armnn::ITensorHandleFactory& tensorHandleFactory, + bool biasEnabled); + +LayerTestResult SimpleConvolution3d3x3x3Int8Test( + armnn::IWorkloadFactory& workloadFactory, + const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager, + const armnn::ITensorHandleFactory& tensorHandleFactory, + bool biasEnabled); + +LayerTestResult SimpleConvolution3d3x3x3Uint8Test( + armnn::IWorkloadFactory& workloadFactory, + const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager, + const armnn::ITensorHandleFactory& tensorHandleFactory, + bool biasEnabled); + +LayerTestResult SimpleConvolution3d3x3x3Int16Test( + armnn::IWorkloadFactory& workloadFactory, + const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager, + const armnn::ITensorHandleFactory& tensorHandleFactory, + bool biasEnabled); + +LayerTestResult Convolution3d2x2x2Strides3x5x5Float32Test( + armnn::IWorkloadFactory& workloadFactory, + const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager, + const armnn::ITensorHandleFactory& tensorHandleFactory, + bool biasEnabled); + +LayerTestResult Convolution3d2x2x2Strides3x5x5Int8Test( + armnn::IWorkloadFactory& workloadFactory, + const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager, + const armnn::ITensorHandleFactory& tensorHandleFactory, + bool biasEnabled); + +LayerTestResult Convolution3d2x2x2Strides3x5x5Uint8Test( + armnn::IWorkloadFactory& workloadFactory, + const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager, + const armnn::ITensorHandleFactory& tensorHandleFactory, + bool biasEnabled); + +LayerTestResult Convolution3d2x2x2Strides3x5x5Int16Test( + armnn::IWorkloadFactory& workloadFactory, + const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager, + const armnn::ITensorHandleFactory& tensorHandleFactory, + bool biasEnabled); + +LayerTestResult Convolution3d2x2x2Dilation2x2x2Float32Test( + armnn::IWorkloadFactory& workloadFactory, + const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager, + const armnn::ITensorHandleFactory& tensorHandleFactory, + bool biasEnabled); + +LayerTestResult Convolution3d2x2x2Dilation2x2x2Int8Test( + armnn::IWorkloadFactory& workloadFactory, + const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager, + const armnn::ITensorHandleFactory& tensorHandleFactory, + bool biasEnabled); + +LayerTestResult Convolution3d2x2x2Dilation2x2x2Uint8Test( + armnn::IWorkloadFactory& workloadFactory, + const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager, + const armnn::ITensorHandleFactory& tensorHandleFactory, + bool biasEnabled); + +LayerTestResult Convolution3d2x2x2Dilation2x2x2Int16Test( + armnn::IWorkloadFactory& workloadFactory, + const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager, + const armnn::ITensorHandleFactory& tensorHandleFactory, + bool biasEnabled); + +LayerTestResult Convolution3dPaddingSame3x3x3Float32Test( + armnn::IWorkloadFactory& workloadFactory, + const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager, + const armnn::ITensorHandleFactory& tensorHandleFactory, + bool biasEnabled); + +LayerTestResult Convolution3dPaddingSame3x3x3Int8Test( + armnn::IWorkloadFactory& workloadFactory, + const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager, + const armnn::ITensorHandleFactory& tensorHandleFactory, + bool biasEnabled); + +LayerTestResult Convolution3dPaddingSame3x3x3Uint8Test( + armnn::IWorkloadFactory& workloadFactory, + const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager, + const armnn::ITensorHandleFactory& tensorHandleFactory, + bool biasEnabled); + +LayerTestResult Convolution3dPaddingSame3x3x3Int16Test( + armnn::IWorkloadFactory& workloadFactory, + const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager, + const armnn::ITensorHandleFactory& tensorHandleFactory, + bool biasEnabled); + +LayerTestResult Convolution3dStrideDilationPadding3x3x3Float32Test( + armnn::IWorkloadFactory& workloadFactory, + const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager, + const armnn::ITensorHandleFactory& tensorHandleFactory, + bool biasEnabled); + +LayerTestResult Convolution3d2x2x2Stride3x3x3SmallFloat32Test( + armnn::IWorkloadFactory& workloadFactory, + const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager, + const armnn::ITensorHandleFactory& tensorHandleFactory, + bool biasEnabled); + +LayerTestResult Convolution3d2x3x3Float16Test( + armnn::IWorkloadFactory& workloadFactory, + const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager, + const armnn::ITensorHandleFactory& tensorHandleFactory, + bool biasEnabled); + +LayerTestResult Convolution3d2x2x2SmallFloat16Test( + armnn::IWorkloadFactory& workloadFactory, + const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager, + const armnn::ITensorHandleFactory& tensorHandleFactory, + bool biasEnabled); diff --git a/src/backends/reference/RefLayerSupport.cpp b/src/backends/reference/RefLayerSupport.cpp index aaf9aa0e7c..c0ede678bf 100644 --- a/src/backends/reference/RefLayerSupport.cpp +++ b/src/backends/reference/RefLayerSupport.cpp @@ -605,6 +605,76 @@ bool RefLayerSupport::IsConvolution2dSupported(const TensorInfo& input, return supported; } +bool RefLayerSupport::IsConvolution3dSupported(const TensorInfo& input, + const TensorInfo& output, + const Convolution3dDescriptor& descriptor, + const TensorInfo& weights, + const Optional& biases, + Optional reasonIfUnsupported) const +{ + bool supported = true; + + // Define supported types. + std::array supportedTypes = + { + DataType::BFloat16, + DataType::Float32, + DataType::Float16, + DataType::QAsymmS8, + DataType::QAsymmU8, + DataType::QSymmS8, + DataType::QSymmS16 + }; + + supported &= CheckSupportRule(TypeAnyOf(input, supportedTypes), reasonIfUnsupported, + "Reference Convolution3d: input is not a supported type."); + + supported &= CheckSupportRule(TypeAnyOf(output, supportedTypes), reasonIfUnsupported, + "Reference Convolution3d: output is not a supported type."); + + supported &= CheckSupportRule(TypesAreEqual(input, output), reasonIfUnsupported, + "Reference Convolution3d: input and output types mismatched."); + + const DataType inputType = input.GetDataType(); + if (IsQuantized8BitType(inputType)) + { + std::array supportedWeightTypes = + { + DataType::QAsymmS8, + DataType::QAsymmU8, + DataType::QSymmS8 + }; + + supported &= CheckSupportRule(TypeAnyOf(weights, supportedWeightTypes), reasonIfUnsupported, + "Reference Convolution3d: weights type not supported for quantized input."); + } + else + { + supported &= CheckSupportRule(TypeAnyOf(weights, supportedTypes), reasonIfUnsupported, + "Reference Convolution3d: weights is not a supported type."); + + supported &= CheckSupportRule(TypesAreEqual(input, weights), reasonIfUnsupported, + "Reference Convolution3d: input and weights types mismatched."); + } + + if (biases.has_value()) + { + std::array biasesSupportedTypes = + { + DataType::BFloat16, + DataType::Float32, + DataType::Float16, + DataType::Signed32 + }; + + supported &= CheckSupportRule(TypeAnyOf(biases.value(), biasesSupportedTypes), reasonIfUnsupported, + "Reference Convolution3d: biases is not a supported type."); + } + IgnoreUnused(descriptor); + + return supported; +} + bool RefLayerSupport::IsDebugSupported(const TensorInfo& input, const TensorInfo& output, Optional reasonIfUnsupported) const diff --git a/src/backends/reference/RefLayerSupport.hpp b/src/backends/reference/RefLayerSupport.hpp index 2693dc1779..627418e3e1 100644 --- a/src/backends/reference/RefLayerSupport.hpp +++ b/src/backends/reference/RefLayerSupport.hpp @@ -92,6 +92,13 @@ public: const Optional& biases, Optional reasonIfUnsupported = EmptyOptional()) const override; + bool IsConvolution3dSupported(const TensorInfo& input, + const TensorInfo& output, + const Convolution3dDescriptor& descriptor, + const TensorInfo& weights, + const Optional& biases, + Optional reasonIfUnsupported = EmptyOptional()) const override; + bool IsDebugSupported(const TensorInfo& input, const TensorInfo& output, Optional reasonIfUnsupported = EmptyOptional()) const override; diff --git a/src/backends/reference/RefWorkloadFactory.cpp b/src/backends/reference/RefWorkloadFactory.cpp index 681b73a748..18a5af277f 100644 --- a/src/backends/reference/RefWorkloadFactory.cpp +++ b/src/backends/reference/RefWorkloadFactory.cpp @@ -241,6 +241,12 @@ std::unique_ptr RefWorkloadFactory::CreateConvolution2d(const Convolu return std::make_unique(descriptor, info); } +std::unique_ptr RefWorkloadFactory::CreateConvolution3d(const Convolution3dQueueDescriptor& descriptor, + const WorkloadInfo& info) const +{ + return std::make_unique(descriptor, info); +} + std::unique_ptr RefWorkloadFactory::CreateDebug(const DebugQueueDescriptor& descriptor, const WorkloadInfo& info) const { diff --git a/src/backends/reference/RefWorkloadFactory.hpp b/src/backends/reference/RefWorkloadFactory.hpp index fe3eb54795..d00d3ca822 100644 --- a/src/backends/reference/RefWorkloadFactory.hpp +++ b/src/backends/reference/RefWorkloadFactory.hpp @@ -115,6 +115,9 @@ public: std::unique_ptr CreateConvolution2d(const Convolution2dQueueDescriptor& descriptor, const WorkloadInfo& info) const override; + std::unique_ptr CreateConvolution3d(const Convolution3dQueueDescriptor& descriptor, + const WorkloadInfo& info) const override; + std::unique_ptr CreateDebug(const DebugQueueDescriptor& descriptor, const WorkloadInfo& info) const override; diff --git a/src/backends/reference/backend.mk b/src/backends/reference/backend.mk index 2dc2bc4919..7d6c59a273 100644 --- a/src/backends/reference/backend.mk +++ b/src/backends/reference/backend.mk @@ -27,6 +27,7 @@ BACKEND_SOURCES := \ workloads/BatchToSpaceNd.cpp \ workloads/Broadcast.cpp \ workloads/ConvImpl.cpp \ + workloads/Conv3dImpl.cpp \ workloads/Debug.cpp \ workloads/DepthToSpace.cpp \ workloads/DetectionPostProcess.cpp \ @@ -58,6 +59,7 @@ BACKEND_SOURCES := \ workloads/RefConvertFp32ToBf16Workload.cpp \ workloads/RefConvertFp32ToFp16Workload.cpp \ workloads/RefConvolution2dWorkload.cpp \ + workloads/RefConvolution3dWorkload.cpp \ workloads/RefDebugWorkload.cpp \ workloads/RefDepthToSpaceWorkload.cpp \ workloads/RefDepthwiseConvolution2dWorkload.cpp \ diff --git a/src/backends/reference/test/RefLayerTests.cpp b/src/backends/reference/test/RefLayerTests.cpp index 4afee79c85..f5d388d007 100644 --- a/src/backends/reference/test/RefLayerTests.cpp +++ b/src/backends/reference/test/RefLayerTests.cpp @@ -208,6 +208,39 @@ ARMNN_AUTO_TEST_CASE_WITH_THF(Convolution2d3x3Stride2x2BFloat16SmallValue, false, DataLayout::NHWC); +// Convolution 3d +ARMNN_AUTO_TEST_CASE_WITH_THF(SimpleConvolution3d3x3x3Float32, SimpleConvolution3d3x3x3Float32Test, false) +ARMNN_AUTO_TEST_CASE_WITH_THF(SimpleConvolution3d3x3x3Int8, SimpleConvolution3d3x3x3Int8Test, false) +ARMNN_AUTO_TEST_CASE_WITH_THF(SimpleConvolution3d3x3x3Uint8, SimpleConvolution3d3x3x3Uint8Test, false) +ARMNN_AUTO_TEST_CASE_WITH_THF(SimpleConvolution3d3x3x3Int16, SimpleConvolution3d3x3x3Int16Test, false) + +ARMNN_AUTO_TEST_CASE_WITH_THF(Convolution3d2x2x2Strides3x5x5Float32, Convolution3d2x2x2Strides3x5x5Float32Test, false) +ARMNN_AUTO_TEST_CASE_WITH_THF(Convolution3d2x2x2Strides3x5x5TestInt8, Convolution3d2x2x2Strides3x5x5Int8Test, true) +ARMNN_AUTO_TEST_CASE_WITH_THF(Convolution3d2x2x2Strides3x5x5TestUint8, Convolution3d2x2x2Strides3x5x5Uint8Test, false) +ARMNN_AUTO_TEST_CASE_WITH_THF(Convolution3d2x2x2Strides3x5x5TestInt16, Convolution3d2x2x2Strides3x5x5Int16Test, true) + +ARMNN_AUTO_TEST_CASE_WITH_THF(Convolution3dPaddingSame3x3x3Float32, Convolution3dPaddingSame3x3x3Float32Test, false) +ARMNN_AUTO_TEST_CASE_WITH_THF(Convolution3dPaddingSame3x3x3TestInt8, Convolution3dPaddingSame3x3x3Int8Test, false) +ARMNN_AUTO_TEST_CASE_WITH_THF(Convolution3dPaddingSame3x3x3TestUint8, Convolution3dPaddingSame3x3x3Uint8Test, false) +ARMNN_AUTO_TEST_CASE_WITH_THF(Convolution3dPaddingSame3x3x3TestInt16, Convolution3dPaddingSame3x3x3Int16Test, false) + +ARMNN_AUTO_TEST_CASE_WITH_THF(Convolution3d2x2x2Dilation2x2x2Float32, Convolution3d2x2x2Dilation2x2x2Float32Test, true) +ARMNN_AUTO_TEST_CASE_WITH_THF(Convolution3d2x2x2Dilation2x2x2TestInt8, Convolution3d2x2x2Dilation2x2x2Int8Test, true) +ARMNN_AUTO_TEST_CASE_WITH_THF(Convolution3d2x2x2Dilation2x2x2TestUint8, Convolution3d2x2x2Dilation2x2x2Uint8Test, true) +ARMNN_AUTO_TEST_CASE_WITH_THF(Convolution3d2x2x2Dilation2x2x2TestInt16, Convolution3d2x2x2Dilation2x2x2Int16Test, true) + +ARMNN_AUTO_TEST_CASE_WITH_THF(Convolution3dStrideDilationPadding3x3x3Float32, + Convolution3dStrideDilationPadding3x3x3Float32Test, + true) + +ARMNN_AUTO_TEST_CASE_WITH_THF(Convolution3d2x2x2Stride3x3x3SmallTestFloat32, + Convolution3d2x2x2Stride3x3x3SmallFloat32Test, + false) + +ARMNN_AUTO_TEST_CASE_WITH_THF(Convolution3d2x3x3TestFloat16, Convolution3d2x3x3Float16Test, true) +ARMNN_AUTO_TEST_CASE_WITH_THF(Convolution3d2x2x2SmallTestFloat16, Convolution3d2x2x2SmallFloat16Test, false) + + // Depthwise Convolution ARMNN_AUTO_TEST_CASE_WITH_THF(DepthwiseConvolution2d, DepthwiseConvolution2dTest, true, DataLayout::NCHW) ARMNN_AUTO_TEST_CASE_WITH_THF(DepthwiseConvolution2dUint8, DepthwiseConvolution2dUint8Test, true, DataLayout::NCHW) diff --git a/src/backends/reference/workloads/CMakeLists.txt b/src/backends/reference/workloads/CMakeLists.txt index 0ab8c6b0bb..e169c03ad8 100644 --- a/src/backends/reference/workloads/CMakeLists.txt +++ b/src/backends/reference/workloads/CMakeLists.txt @@ -18,6 +18,8 @@ list(APPEND armnnRefBackendWorkloads_sources Broadcast.hpp ConvImpl.cpp ConvImpl.hpp + Conv3dImpl.cpp + Conv3dImpl.hpp Debug.cpp Debug.hpp Decoders.hpp @@ -87,6 +89,8 @@ list(APPEND armnnRefBackendWorkloads_sources RefConvertFp32ToFp16Workload.hpp RefConvolution2dWorkload.cpp RefConvolution2dWorkload.hpp + RefConvolution3dWorkload.cpp + RefConvolution3dWorkload.hpp RefElementwiseWorkload.cpp RefElementwiseWorkload.hpp RefDebugWorkload.cpp diff --git a/src/backends/reference/workloads/Conv3dImpl.cpp b/src/backends/reference/workloads/Conv3dImpl.cpp new file mode 100644 index 0000000000..484d887cfc --- /dev/null +++ b/src/backends/reference/workloads/Conv3dImpl.cpp @@ -0,0 +1,151 @@ +// +// Copyright © 2021 Arm Ltd and Contributors. All rights reserved. +// SPDX-License-Identifier: MIT +// + +#include "Conv3dImpl.hpp" + +namespace armnn +{ + +void Convolve3d(const TensorShape& rInputShape, + Decoder& rInputDecoder, + const TensorShape& rOutputShape, + Encoder& rOutputEncoder, + const TensorShape& rFilterShape, + Decoder& rFilterDecoder, + bool biasEnabled, + Decoder* pBiasDecoder, + DataLayout dataLayout, + unsigned int paddingTop, + unsigned int paddingLeft, + unsigned int paddingFront, + unsigned int xStride, + unsigned int yStride, + unsigned int zStride, + unsigned int xDilation, + unsigned int yDilation, + unsigned int zDilation) +{ + if (biasEnabled && !pBiasDecoder) + { + throw InvalidArgumentException("Bias is enabled but the bias data is invalid"); + } + const armnnUtils::DataLayoutIndexed dataLayoutIndexed(dataLayout); + + const unsigned int channelsIndex = dataLayoutIndexed.GetChannelsIndex(); + const unsigned int heightIndex = dataLayoutIndexed.GetHeightIndex(); + const unsigned int widthIndex = dataLayoutIndexed.GetWidthIndex(); + const unsigned int depthIndex = dataLayoutIndexed.GetDepthIndex(); + + const unsigned int inChannels = rInputShape[channelsIndex]; + const unsigned int outChannels = rOutputShape[channelsIndex]; + + const unsigned int batchSize = rOutputShape[0]; + const unsigned int outputHeight = rOutputShape[heightIndex]; + const unsigned int outputWidth = rOutputShape[widthIndex]; + const unsigned int outputDepth = rOutputShape[depthIndex]; + const unsigned int inputHeight = rInputShape[heightIndex]; + const unsigned int inputWidth = rInputShape[widthIndex]; + const unsigned int inputDepth = rInputShape[depthIndex]; + + // Conv3d weights layout: [D,H,W,I,O] + const unsigned int filterDepth = rFilterShape[0]; + const unsigned int filterHeight = rFilterShape[1]; + const unsigned int filterWidth = rFilterShape[2]; + + const std::vector inputVec = rInputDecoder.DecodeTensor(rInputShape); + const std::vector filterVec = rFilterDecoder.DecodeTensor(rFilterShape); + + const TensorShape biasShape{outChannels}; + const std::vector biasVec = biasEnabled ? pBiasDecoder->DecodeTensor(biasShape) : std::vector(); + + for (unsigned int batchIdx = 0; batchIdx < batchSize; batchIdx++) + { + for (unsigned int zOutput = 0; zOutput < outputDepth; zOutput++) + { + for (unsigned int xOutput = 0; xOutput < outputWidth; xOutput++) + { + for (unsigned int yOutput = 0; yOutput < outputHeight; yOutput++) + { + for (unsigned int cOutput = 0; cOutput < outChannels; cOutput++) + { + // This loop goes over each output element. + float sum = 0.0f; + + // Loop over each input channel. + for (unsigned int zFilter = 0; zFilter < filterDepth; zFilter++) + { + for (unsigned int yFilter = 0; yFilter < filterHeight; yFilter++) + { + for (unsigned int xFilter = 0; xFilter < filterWidth; xFilter++) + { + for (unsigned int cInput = 0; cInput < inChannels; cInput++) + { + // This loop goes over each input element for each output element. + unsigned int filterIndex = 0; + + // Conv3d weights layout: [D,H,W,I,O] + // Keep this implementation, as using DataLayoutIndexed::GetIndex + // causes large performance regression. + filterIndex = zFilter * filterHeight * filterWidth * inChannels * outChannels + + yFilter * filterWidth * inChannels * outChannels + + xFilter * inChannels * outChannels + + cInput * outChannels + + cOutput; + + unsigned int yInput = yOutput * yStride + yFilter * yDilation; + unsigned int xInput = xOutput * xStride + xFilter * xDilation; + unsigned int zInput = zOutput * zStride + zFilter * zDilation; + + float inputValue; + + // Check if we're in the padding. + if (yInput < paddingTop || yInput >= inputHeight + paddingTop || + xInput < paddingLeft || xInput >= inputWidth + paddingLeft || + zInput < paddingFront || zInput >= inputDepth + paddingFront) + { + inputValue = 0.0f; + } + else + { + unsigned int inputIndex = 0; + + // Keep this implementation, as using DataLayoutIndexed::GetIndex + // causes large performance regression. + inputIndex = batchIdx * inputDepth * inputHeight * inputWidth * inChannels + + (zInput-paddingFront) * inputHeight * inputWidth * inChannels + + (yInput-paddingTop) * inputWidth * inChannels + + (xInput-paddingLeft) * inChannels + + cInput; + + inputValue = inputVec[inputIndex]; + } + + sum += filterVec[filterIndex] * inputValue; + } + } + } + } + + if (biasEnabled) + { + sum += biasVec[cOutput]; + } + + unsigned int outIdx = batchIdx * outputDepth * outputHeight * outputWidth * outChannels + + zOutput * outputHeight * outputWidth * outChannels + + yOutput * outputWidth * outChannels + + xOutput * outChannels + + cOutput; + + rOutputEncoder[outIdx]; + rOutputEncoder.Set(sum); + } + } + } + } + } +} + +} // namespace armnn diff --git a/src/backends/reference/workloads/Conv3dImpl.hpp b/src/backends/reference/workloads/Conv3dImpl.hpp new file mode 100644 index 0000000000..5cf2ed942d --- /dev/null +++ b/src/backends/reference/workloads/Conv3dImpl.hpp @@ -0,0 +1,38 @@ +// +// Copyright © 2021 Arm Ltd and Contributors. All rights reserved. +// SPDX-License-Identifier: MIT +// + +#pragma once + +#include "BaseIterator.hpp" +#include "Decoders.hpp" +#include "Encoders.hpp" + +#include + +#include + +namespace armnn +{ + +void Convolve3d(const TensorShape& rInputShape, + Decoder& rInputDecoder, + const TensorShape& rOutputShape, + Encoder& rOutputEncoder, + const TensorShape& rFilterShape, + Decoder& rFilterDecoder, + bool biasEnabled, + Decoder* pBiasDecoder, + DataLayout dataLayout, + unsigned int paddingTop, + unsigned int paddingLeft, + unsigned int paddingFront, + unsigned int xStride, + unsigned int yStride, + unsigned int zStride, + unsigned int xDilation, + unsigned int yDilation, + unsigned int zDilation); + +} //namespace armnn diff --git a/src/backends/reference/workloads/RefConvolution3dWorkload.cpp b/src/backends/reference/workloads/RefConvolution3dWorkload.cpp new file mode 100644 index 0000000000..ea425daec9 --- /dev/null +++ b/src/backends/reference/workloads/RefConvolution3dWorkload.cpp @@ -0,0 +1,76 @@ +// +// Copyright © 2021 Arm Ltd and Contributors. All rights reserved. +// SPDX-License-Identifier: MIT +// + +#include "RefConvolution3dWorkload.hpp" + +#include "Conv3dImpl.hpp" +#include "RefWorkloadUtils.hpp" + +#include "Profiling.hpp" + +namespace armnn +{ +RefConvolution3dWorkload::RefConvolution3dWorkload( + const Convolution3dQueueDescriptor& descriptor, const WorkloadInfo& info) + : BaseWorkload(descriptor, info) +{ + WorkloadInfo detailsInfo; + detailsInfo.m_InputTensorInfos = info.m_InputTensorInfos; + detailsInfo.m_OutputTensorInfos = info.m_OutputTensorInfos; + detailsInfo.m_WeightsTensorInfo = armnn::Optional(descriptor.m_Weight->GetTensorInfo()); + if (descriptor.m_Parameters.m_BiasEnabled) + { + detailsInfo.m_BiasTensorInfo = armnn::Optional(descriptor.m_Bias->GetTensorInfo()); + } + + // Report Profiling Details + ARMNN_REPORT_PROFILING_WORKLOAD_DESC("RefConvolution3dWorkload_Construct", + descriptor.m_Parameters, + detailsInfo, + this->GetGuid()); + + m_Weight = std::make_unique(*( descriptor.m_Weight )); + const TensorInfo& rFilterInfo = m_Weight->GetTensorInfo(); + + m_FilterShape = rFilterInfo.GetShape(); + m_FilterDecoder = MakeDecoder(rFilterInfo, m_Weight.get()->Map(true)); + + if ( descriptor.m_Parameters.m_BiasEnabled ) + { + m_Bias = std::make_unique(*( descriptor.m_Bias )); + const TensorInfo& biasInfo = m_Bias->GetTensorInfo(); + m_BiasDecoder = MakeDecoder(biasInfo, m_Bias->Map(true)); + } +} + +void RefConvolution3dWorkload::Execute() const +{ + Execute(m_Data.m_Inputs, m_Data.m_Outputs); +} + +void RefConvolution3dWorkload::ExecuteAsync(WorkingMemDescriptor& workingMemDescriptor) +{ + Execute(workingMemDescriptor.m_Inputs, workingMemDescriptor.m_Outputs); +} + +void RefConvolution3dWorkload::Execute(std::vector inputs, std::vector outputs) const +{ + ARMNN_SCOPED_PROFILING_EVENT_GUID(Compute::CpuRef, "RefConvolution3dWorkload_Execute", this->GetGuid()); + + std::unique_ptr> inputDecoder = MakeDecoder(GetTensorInfo(inputs[0]), inputs[0]->Map()); + std::unique_ptr> outputEncoder = MakeEncoder(GetTensorInfo(outputs[0]), outputs[0]->Map()); + + const TensorShape& inputShape = GetTensorInfo(inputs[0]).GetShape(); + const TensorShape& outputShape = GetTensorInfo(outputs[0]).GetShape(); + + Convolve3d(inputShape, *inputDecoder, outputShape, *outputEncoder, m_FilterShape, + *m_FilterDecoder, m_Data.m_Parameters.m_BiasEnabled, m_BiasDecoder.get(), + m_Data.m_Parameters.m_DataLayout, + m_Data.m_Parameters.m_PadTop, m_Data.m_Parameters.m_PadLeft, m_Data.m_Parameters.m_PadFront, + m_Data.m_Parameters.m_StrideX, m_Data.m_Parameters.m_StrideY, m_Data.m_Parameters.m_StrideZ, + m_Data.m_Parameters.m_DilationX, m_Data.m_Parameters.m_DilationY, m_Data.m_Parameters.m_DilationZ); +} + +} //namespace armnn diff --git a/src/backends/reference/workloads/RefConvolution3dWorkload.hpp b/src/backends/reference/workloads/RefConvolution3dWorkload.hpp new file mode 100644 index 0000000000..0373a8b900 --- /dev/null +++ b/src/backends/reference/workloads/RefConvolution3dWorkload.hpp @@ -0,0 +1,38 @@ +// +// Copyright © 2021 Arm Ltd and Contributors. All rights reserved. +// SPDX-License-Identifier: MIT +// + +#pragma once + +#include +#include +#include "Decoders.hpp" +#include "Encoders.hpp" + +namespace armnn +{ + +class RefConvolution3dWorkload : public BaseWorkload +{ +public: + explicit RefConvolution3dWorkload(const Convolution3dQueueDescriptor& descriptor, + const WorkloadInfo& info); + + + void Execute() const override; + void ExecuteAsync(WorkingMemDescriptor& workingMemDescriptor) override; + +private: + void Execute(std::vector inputs, std::vector outputs) const; + std::unique_ptr m_Weight; + std::unique_ptr m_Bias; + + std::unique_ptr> m_FilterDecoder; + std::unique_ptr> m_BiasDecoder; + + TensorShape m_FilterShape; +}; + +} //namespace armnn + diff --git a/src/backends/reference/workloads/RefWorkloads.hpp b/src/backends/reference/workloads/RefWorkloads.hpp index 1cf84eed9e..ed3aa90e5f 100644 --- a/src/backends/reference/workloads/RefWorkloads.hpp +++ b/src/backends/reference/workloads/RefWorkloads.hpp @@ -22,6 +22,7 @@ #include "RefChannelShuffleWorkload.hpp" #include "RefComparisonWorkload.hpp" #include "RefConvolution2dWorkload.hpp" +#include "RefConvolution3dWorkload.hpp" #include "RefConstantWorkload.hpp" #include "RefConcatWorkload.hpp" #include "RefConvertBf16ToFp32Workload.hpp" -- cgit v1.2.1