From 613b49cdd8f4e40460711a4d84ed42f980477a17 Mon Sep 17 00:00:00 2001 From: David Monahan Date: Thu, 27 Jun 2019 11:37:47 +0100 Subject: IVGCVSW-3166 Add support for TransposeConv2D to 1.2 HalPolicy Signed-off-by: David Monahan Signed-off-by: Narumol Prangnawarat Change-Id: I7b855efaddb18e8f184f4b85196b3b95b69bca6e --- 1.2/HalPolicy.cpp | 154 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1.2/HalPolicy.hpp | 2 + NnapiSupport.txt | 2 +- 3 files changed, 157 insertions(+), 1 deletion(-) diff --git a/1.2/HalPolicy.cpp b/1.2/HalPolicy.cpp index dee2175d..64d808bc 100644 --- a/1.2/HalPolicy.cpp +++ b/1.2/HalPolicy.cpp @@ -158,6 +158,8 @@ bool HalPolicy::ConvertOperation(const Operation& operation, const Model& model, return ConvertResize(operation, model, data, armnn::ResizeMethod::Bilinear); case V1_2::OperationType::RESIZE_NEAREST_NEIGHBOR: return ConvertResize(operation, model, data, armnn::ResizeMethod::NearestNeighbor); + case V1_2::OperationType::TRANSPOSE_CONV_2D: + return ConvertTransposeConvolution2d(operation, model, data); case V1_2::OperationType::SOFTMAX: return ConvertSoftmax(operation, model, data); case V1_2::OperationType::SPACE_TO_DEPTH: @@ -1485,5 +1487,157 @@ bool HalPolicy::ConvertLstm(const Operation& operation, const Model& model, Conv SetupAndTrackLayerOutputSlot(operation, 3, *layer, 3, model, data)); } +bool HalPolicy::ConvertTransposeConvolution2d(const Operation& operation, const Model& model, ConversionData& data) +{ + LayerInputHandle input = ConvertToLayerInputHandle(operation, 0, model, data); + + if (!input.IsValid()) + { + return Fail("%s: Operation has invalid inputs", __func__); + } + + const Operand* output = GetOutputOperand(operation, 0, model); + + if (!output) + { + return Fail("%s: Could not read output 0", __func__); + } + + const armnn::TensorInfo& inputInfo = input.GetTensorInfo(); + const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output); + if (IsDynamicTensor(outputInfo)) + { + return Fail("%s: Dynamic output tensors are not supported", __func__); + } + + // ArmNN does not currently support non-fixed weights or bias + // Find the shape of the weights tensor. In AndroidNN this will be [ 1, H, W, I * M ] + const Operand* weightsOperand = GetInputOperand(operation, 1, model); + + if (weightsOperand == nullptr) + { + return Fail("%s: Operand is invalid", __func__); + } + armnn::TransposeConvolution2dDescriptor desc; + desc.m_DataLayout = armnn::DataLayout::NHWC; + + // Determine whether padding is implicit or explicit + bool implicitPadding = operation.inputs.size() == 9; + + if (implicitPadding ) + { + desc.m_DataLayout = OptionalDataLayout(operation, 8, model, data); + } + else + { + desc.m_DataLayout = OptionalDataLayout(operation, 10, model, data); + } + + armnnUtils::DataLayoutIndexed dataLayoutIndexed(desc.m_DataLayout); + unsigned int widthIndex = dataLayoutIndexed.GetWidthIndex(); + unsigned int heightIndex = dataLayoutIndexed.GetHeightIndex(); + + const armnn::PermutationVector OHWIToOIHW = {0, 2, 3, 1}; + + // The shape of the weight is [depth_out, filter_height, filter_width, depth_in]. + // We have to permute it to OIHW if the data layout is NCHW. + const ConstTensorPin weightsPin = (desc.m_DataLayout == armnn::DataLayout::NCHW) ? + ConvertOperationInputToConstTensorPin(operation, 1, model, data, OHWIToOIHW) : + ConvertOperationInputToConstTensorPin(operation, 1, model, data); + + // Bias is a 1D tensor + const ConstTensorPin biasPin = + ConvertOperationInputToConstTensorPin(operation, 2, model, data); + + if (!weightsPin.IsValid()) + { + return Fail("%s: Operation has invalid weights", __func__); + } + + if (!biasPin.IsValid()) + { + return Fail("%s: Operation has invalid biases", __func__); + } + + armnn::ConstTensor weights = weightsPin.GetConstTensor(); + armnn::ConstTensor bias = biasPin.GetConstTensor(); + SanitizeBiasQuantizationScale(bias.GetInfo(), weights.GetInfo(), inputInfo); + + ActivationFn activation; + + if (implicitPadding) + { + android::nn::PaddingScheme paddingScheme; + if (!GetInputPaddingScheme(operation, 4, paddingScheme, model, data) || + !GetInputScalar(operation, 5, OperandType::INT32, desc.m_StrideX, model, data) || + !GetInputScalar(operation, 6, OperandType::INT32, desc.m_StrideY, model, data) || + !GetInputActivationFunction(operation, 7, activation, model, data)) + { + return Fail("%s: Operation has invalid inputs (implicit padding)", __func__); + } + + const uint32_t kernelX = weights.GetShape()[widthIndex]; + const uint32_t kernelY = weights.GetShape()[heightIndex]; + const uint32_t inputX = inputInfo.GetShape()[widthIndex]; + const uint32_t inputY = inputInfo.GetShape()[heightIndex]; + + CalcPadding(inputX, kernelX, desc.m_StrideX, desc.m_PadLeft, desc.m_PadRight, paddingScheme); + CalcPadding(inputY, kernelY, desc.m_StrideY, desc.m_PadTop, desc.m_PadBottom, paddingScheme); + } + else if (operation.inputs.size() == 11) + { + // explicit padding + if (!GetInputScalar(operation, 3, OperandType::INT32, desc.m_PadLeft, model, data) || + !GetInputScalar(operation, 4, OperandType::INT32, desc.m_PadRight, model, data) || + !GetInputScalar(operation, 5, OperandType::INT32, desc.m_PadTop, model, data) || + !GetInputScalar(operation, 6, OperandType::INT32, desc.m_PadBottom, model, data) || + !GetInputScalar(operation, 7, OperandType::INT32, desc.m_StrideX, model, data) || + !GetInputScalar(operation, 8, OperandType::INT32, desc.m_StrideY, model, data) || + !GetInputActivationFunction(operation, 9, activation, model, data)) + { + return Fail("%s: Operation has invalid inputs (explicit padding)", __func__); + } + } + else + { + return Fail("%s: Unsupported number of operation inputs", __func__); + } + + desc.m_BiasEnabled = true; + armnn::Optional biases(bias.GetInfo()); + + bool isSupported = false; + FORWARD_LAYER_SUPPORT_FUNC(__func__, + IsTransposeConvolution2dSupported, + data.m_Backends, + isSupported, + inputInfo, + outputInfo, + desc, + weights.GetInfo(), + biases); + if (!isSupported) + { + return false; + } + + armnn::IConnectableLayer* startLayer = + data.m_Network->AddTransposeConvolution2dLayer(desc, weights, armnn::Optional(bias)); + if (!startLayer) + { + return Fail("%s: AddTransposeConvolution2dLayer failed", __func__); + } + + armnn::IConnectableLayer* endLayer = ProcessActivation(outputInfo, activation, startLayer, data); + if (!endLayer) + { + return Fail("%s: ProcessActivation failed", __func__); + } + + input.Connect(startLayer->GetInputSlot(0)); + + return SetupAndTrackLayerOutputSlot(operation, 0, *endLayer, model, data); +} + } // namespace hal_1_2 } // namespace armnn_driver diff --git a/1.2/HalPolicy.hpp b/1.2/HalPolicy.hpp index a51b9a60..5d6158a3 100644 --- a/1.2/HalPolicy.hpp +++ b/1.2/HalPolicy.hpp @@ -73,6 +73,8 @@ private: static bool ConvertTanH(const Operation& operation, const Model& model, ConversionData& data); static bool ConvertLstm(const Operation& operation, const Model& model, ConversionData& data); + + static bool ConvertTransposeConvolution2d(const Operation& operation, const Model& model, ConversionData& data); }; } // namespace hal_1_2 diff --git a/NnapiSupport.txt b/NnapiSupport.txt index 47c5149a..4a494305 100644 --- a/NnapiSupport.txt +++ b/NnapiSupport.txt @@ -57,6 +57,7 @@ PRELU (FLOAT32,QUANT8_ASYMM) QUANTIZE (FLOAT32,QUANT8_ASYMM) RESIZE_NEAREST_NEIGHBOR (FLOAT32,QUANT8_ASYMM) SOFTMAX (FLOAT32,QUANT8_ASYMM) +TRANSPOSE_CONV_2D (FLOAT32,QUANT8_ASYMM) --- Unsupported operators --- @@ -74,7 +75,6 @@ The following AndroidNN HAL 1.2 operations are currently not supported: CONCATENATION LSTM QUANTIZED_16BIT_LSTM -TRANSPOSE_CONV_2D Where operations are not supported by the ArmNN Android NN Driver, the driver indicates this to the framework appropriately and the framework implements those operations using a CPU implementation. -- cgit v1.2.1