| // |
| // Copyright © 2017 Arm Ltd. All rights reserved. |
| // SPDX-License-Identifier: MIT |
| // |
| |
| #pragma once |
| |
| #include <armnn/ArmNN.hpp> |
| |
| #include <Permute.hpp> |
| #include <ResolveType.hpp> |
| |
| #include <backendsCommon/CpuTensorHandle.hpp> |
| |
| #include <backendsCommon/test/DataLayoutUtils.hpp> |
| #include <backendsCommon/test/QuantizeHelper.hpp> |
| #include <backendsCommon/test/TensorCopyUtils.hpp> |
| #include <backendsCommon/test/WorkloadTestUtils.hpp> |
| |
| #include <reference/RefWorkloadFactory.hpp> |
| |
| #include <boost/test/unit_test.hpp> |
| |
| #include <string> |
| #include <utility> |
| #include <vector> |
| |
| namespace |
| { |
| |
| template<typename T> |
| using TensorData = std::pair<armnn::TensorInfo, std::vector<T>>; |
| |
| template<typename T> |
| void VerifyInputTensorData(const TensorData<T>& data, const std::string& tensorName) |
| { |
| if (data.first.GetNumElements() > data.second.size()) |
| { |
| throw armnn::InvalidArgumentException("Size of data too small for " + tensorName + ": expected " + |
| std::to_string(data.first.GetNumElements()) + "but got " + std::to_string(data.second.size())); |
| } |
| } |
| |
| template<typename T, typename BT> |
| void TransposeConvolution2dTestImpl(armnn::IWorkloadFactory& workloadFactory, |
| const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager, |
| const armnn::TransposeConvolution2dDescriptor& descriptor, |
| const TensorData<T>& input, |
| TensorData<T>& output, |
| const TensorData<T>& weights, |
| const armnn::Optional<TensorData<BT>>& biases) |
| { |
| using namespace armnn; |
| |
| VerifyInputTensorData(input, "input"); |
| VerifyInputTensorData(weights, "biases"); |
| |
| if (descriptor.m_BiasEnabled) |
| { |
| if (!biases.has_value()) |
| { |
| throw InvalidArgumentException("Bias enabled but no bias data provided"); |
| } |
| VerifyInputTensorData(biases.value(), "biases"); |
| } |
| |
| // set up weights |
| ScopedCpuTensorHandle weightsTensor(weights.first); |
| |
| TransposeConvolution2dQueueDescriptor queueDescriptor; |
| queueDescriptor.m_Parameters = descriptor; |
| queueDescriptor.m_Weight = &weightsTensor; |
| |
| AllocateAndCopyDataToITensorHandle(&weightsTensor, weights.second.data()); |
| |
| std::unique_ptr<ScopedCpuTensorHandle> biasesTensor; |
| if (descriptor.m_BiasEnabled) |
| { |
| // set up biases |
| biasesTensor = std::make_unique<ScopedCpuTensorHandle>(biases.value().first); |
| queueDescriptor.m_Bias = biasesTensor.get(); |
| |
| AllocateAndCopyDataToITensorHandle(biasesTensor.get(), biases.value().second.data()); |
| } |
| |
| // set up input and output handles |
| std::unique_ptr<ITensorHandle> inputHandle = workloadFactory.CreateTensorHandle(input.first); |
| std::unique_ptr<ITensorHandle> outputHandle = workloadFactory.CreateTensorHandle(output.first); |
| |
| // set up workload |
| armnn::WorkloadInfo workloadInfo; |
| AddInputToWorkload(queueDescriptor, workloadInfo, input.first, inputHandle.get()); |
| AddOutputToWorkload(queueDescriptor, workloadInfo, output.first, outputHandle.get()); |
| |
| std::unique_ptr<armnn::IWorkload> workload = |
| workloadFactory.CreateTransposeConvolution2d(queueDescriptor, workloadInfo); |
| |
| inputHandle->Allocate(); |
| outputHandle->Allocate(); |
| |
| CopyDataToITensorHandle(inputHandle.get(), input.second.data()); |
| |
| ExecuteWorkload(*workload, memoryManager); |
| |
| // copy output |
| output.second = std::vector<T>(output.first.GetNumElements(), 0.0f); |
| CopyDataFromITensorHandle(output.second.data(), outputHandle.get()); |
| } |
| |
| template<armnn::DataType ArmnnType, armnn::DataType ArmnnBType, typename T = armnn::ResolveType<ArmnnType>> |
| LayerTestResult<T, 4> TransposeConvolution2dTest( |
| armnn::IWorkloadFactory& workloadFactory, |
| const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager, |
| const armnn::TransposeConvolution2dDescriptor& descriptor, |
| armnn::TensorInfo& inputInfo, |
| const std::vector<float>& inputData, |
| armnn::TensorInfo& outputInfo, |
| const std::vector<float>& expectedOutputData, |
| armnn::TensorInfo& weightsInfo, |
| const std::vector<float>& weightsData, |
| armnn::TensorInfo& biasesInfo, |
| const std::vector<float>& biasesData) |
| { |
| using namespace armnn; |
| |
| // set up quantization parameters |
| if (armnn::IsQuantizedType<T>()) |
| { |
| constexpr float qScale = 0.50f; |
| constexpr int32_t qOffset = 10; |
| |
| inputInfo.SetQuantizationScale(qScale); |
| inputInfo.SetQuantizationOffset(qOffset); |
| |
| outputInfo.SetQuantizationScale(qScale); |
| outputInfo.SetQuantizationOffset(qOffset); |
| |
| weightsInfo.SetQuantizationScale(qScale); |
| weightsInfo.SetQuantizationOffset(qOffset); |
| |
| biasesInfo.SetQuantizationScale(qScale * qScale); |
| biasesInfo.SetQuantizationOffset(0); |
| } |
| |
| // set up input |
| TensorData<T> input = |
| { |
| inputInfo, |
| QuantizedVector<T>(inputInfo.GetQuantizationScale(), inputInfo.GetQuantizationOffset(), inputData) |
| }; |
| |
| // set up weights |
| TensorData<T> weights = |
| { |
| weightsInfo, |
| QuantizedVector<T>(weightsInfo.GetQuantizationScale(), weightsInfo.GetQuantizationOffset(), weightsData) |
| }; |
| |
| // set up biases |
| using BT = armnn::ResolveType<ArmnnBType>; |
| Optional<TensorData<BT>> optionalBiases; |
| if (descriptor.m_BiasEnabled) |
| { |
| TensorData<BT> biases = |
| { |
| biasesInfo, |
| QuantizedVector<BT>(biasesInfo.GetQuantizationScale(), biasesInfo.GetQuantizationOffset(), biasesData) |
| }; |
| |
| optionalBiases = Optional<TensorData<BT>>(biases); |
| } |
| |
| // set up output |
| TensorData<T> output = { outputInfo, {} }; |
| |
| // execute test |
| TransposeConvolution2dTestImpl(workloadFactory, |
| memoryManager, |
| descriptor, |
| input, |
| output, |
| weights, |
| optionalBiases); |
| |
| // construct result object |
| LayerTestResult<T, 4> testResult(outputInfo); |
| testResult.output = MakeTensor<T, 4>(outputInfo, output.second); |
| testResult.outputExpected = MakeTensor<T, 4>(outputInfo, |
| QuantizedVector<T>(outputInfo.GetQuantizationScale(), |
| outputInfo.GetQuantizationOffset(), |
| expectedOutputData)); |
| |
| return testResult; |
| } |
| |
| template<typename T> |
| void SwizzleData(armnn::TensorInfo& inputInfo, |
| std::vector<T>& inputData, |
| armnn::TensorInfo& outputInfo, |
| std::vector<T>& outputData, |
| armnn::TensorInfo& weightsInfo, |
| std::vector<T>& weightsData) |
| { |
| PermuteTensorNchwToNhwc<T>(inputInfo, inputData); |
| PermuteTensorNchwToNhwc<T>(outputInfo, outputData); |
| PermuteTensorNchwToNhwc<T>(weightsInfo, weightsData); |
| } |
| |
| } // anonymous namespace |
| |
| template<armnn::DataType ArmnnType, armnn::DataType ArmnnBType, typename T = armnn::ResolveType<ArmnnType>> |
| LayerTestResult<T, 4> SimpleTransposeConvolution2dTest( |
| armnn::IWorkloadFactory& workloadFactory, |
| const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager, |
| bool biasEnabled, |
| const armnn::DataLayout layout) |
| { |
| using namespace armnn; |
| |
| constexpr unsigned int batches = 1u; |
| constexpr unsigned int channels = 1u; |
| |
| constexpr unsigned int wInput = 3u; |
| constexpr unsigned int hInput = wInput; |
| |
| constexpr unsigned int wOutput = 5u; |
| constexpr unsigned int hOutput = wOutput; |
| |
| constexpr unsigned int wWeights = 3u; |
| constexpr unsigned int hWeights = wWeights; |
| |
| TensorShape inputShape = { batches, channels, hInput, wInput }; |
| TensorShape outputShape = { batches, channels, hOutput, wOutput }; |
| TensorShape weightsShape = { batches, channels, hWeights, wWeights }; |
| |
| TensorInfo inputInfo(inputShape, ArmnnType); |
| TensorInfo outputInfo(outputShape, ArmnnType); |
| TensorInfo weightsInfo(weightsShape, ArmnnType); |
| TensorInfo biasesInfo({ channels }, ArmnnBType); |
| |
| std::vector<float> inputData = |
| { |
| 1.f, 1.f, 1.f, |
| 1.f, 1.f, 1.f, |
| 1.f, 1.f, 1.f |
| }; |
| |
| std::vector<float> weightsData = |
| { |
| 1.f, 2.f, 3.f, |
| 4.f, 5.f, 6.f, |
| 7.f, 8.f, 9.f |
| }; |
| |
| std::vector<float> biasesData = { 1.f }; |
| |
| std::vector<float> expectedOutputData = |
| { |
| 1.f, 3.f, 6.f, 5.f, 3.f, |
| 5.f, 12.f, 21.f, 16.f, 9.f, |
| 12.f, 27.f, 45.f, 33.f, 18.f, |
| 11.f, 24.f, 39.f, 28.f, 15.f, |
| 7.f, 15.f, 24.f, 17.f, 9.f |
| }; |
| |
| if (biasEnabled) |
| { |
| // apply bias to expected output data |
| std::transform(expectedOutputData.begin(), expectedOutputData.end(), expectedOutputData.begin(), |
| [&](float f) -> float { return f + biasesData[0]; }); |
| } |
| |
| TransposeConvolution2dDescriptor descriptor; |
| descriptor.m_StrideX = 1; |
| descriptor.m_StrideY = 1; |
| descriptor.m_BiasEnabled = biasEnabled; |
| descriptor.m_DataLayout = layout; |
| |
| // swizzle data if needed |
| if (layout == armnn::DataLayout::NHWC) |
| { |
| SwizzleData(inputInfo, inputData, outputInfo, expectedOutputData, weightsInfo, weightsData); |
| } |
| |
| return TransposeConvolution2dTest<ArmnnType, ArmnnBType>(workloadFactory, |
| memoryManager, |
| descriptor, |
| inputInfo, |
| inputData, |
| outputInfo, |
| expectedOutputData, |
| weightsInfo, |
| weightsData, |
| biasesInfo, |
| biasesData); |
| } |
| |
| template<armnn::DataType ArmnnType, armnn::DataType ArmnnBType, typename T = armnn::ResolveType<ArmnnType>> |
| LayerTestResult<T, 4> PaddedTransposeConvolution2dTest( |
| armnn::IWorkloadFactory& workloadFactory, |
| const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager, |
| bool biasEnabled, |
| const armnn::DataLayout layout) |
| { |
| using namespace armnn; |
| |
| constexpr unsigned int batches = 1u; |
| constexpr unsigned int channels = 1u; |
| |
| constexpr unsigned int wInput = 4u; |
| constexpr unsigned int hInput = wInput; |
| |
| constexpr unsigned int wOutput = 2u; |
| constexpr unsigned int hOutput = wOutput; |
| |
| constexpr unsigned int wWeights = 3u; |
| constexpr unsigned int hWeights = wWeights; |
| |
| TensorShape inputShape = { batches, channels, hInput, wInput }; |
| TensorShape outputShape = { batches, channels, hOutput, wOutput }; |
| TensorShape weightsShape = { batches, channels, hWeights, wWeights }; |
| |
| TensorInfo inputInfo(inputShape, ArmnnType); |
| TensorInfo outputInfo(outputShape, ArmnnType); |
| TensorInfo weightsInfo(weightsShape, ArmnnType); |
| TensorInfo biasesInfo({ channels }, ArmnnBType); |
| |
| std::vector<float> inputData = |
| { |
| 1.f, 3.f, 2.f, 1.f, |
| 1.f, 3.f, 3.f, 1.f, |
| 2.f, 1.f, 1.f, 3.f, |
| 3.f, 2.f, 3.f, 3.f |
| }; |
| |
| std::vector<float> weightsData = |
| { |
| 1.f, 2.f, 3.f, |
| 0.f, 1.f, 0.f, |
| 2.f, 1.f, 2.f |
| }; |
| |
| std::vector<float> biasesData = { 1.f }; |
| |
| std::vector<float> expectedOutputData = |
| { |
| 21.f, 21.f, |
| 28.f, 27.f |
| }; |
| |
| if (biasEnabled) |
| { |
| // apply bias to expected output data |
| std::transform(expectedOutputData.begin(), expectedOutputData.end(), expectedOutputData.begin(), |
| [&](float f) -> float { return f + biasesData[0]; }); |
| } |
| |
| TransposeConvolution2dDescriptor descriptor; |
| descriptor.m_PadLeft = 2; |
| descriptor.m_PadRight = 2; |
| descriptor.m_PadTop = 2; |
| descriptor.m_PadBottom = 2; |
| descriptor.m_StrideX = 1; |
| descriptor.m_StrideY = 1; |
| descriptor.m_BiasEnabled = biasEnabled; |
| descriptor.m_DataLayout = layout; |
| |
| // swizzle data if needed |
| if (layout == armnn::DataLayout::NHWC) |
| { |
| SwizzleData(inputInfo, inputData, outputInfo, expectedOutputData, weightsInfo, weightsData); |
| } |
| |
| return TransposeConvolution2dTest<ArmnnType, ArmnnBType>(workloadFactory, |
| memoryManager, |
| descriptor, |
| inputInfo, |
| inputData, |
| outputInfo, |
| expectedOutputData, |
| weightsInfo, |
| weightsData, |
| biasesInfo, |
| biasesData); |
| } |
| |
| template<armnn::DataType ArmnnType, armnn::DataType ArmnnBType, typename T = armnn::ResolveType<ArmnnType>> |
| LayerTestResult<T, 4> StridedTransposeConvolution2dTest( |
| armnn::IWorkloadFactory& workloadFactory, |
| const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager, |
| bool biasEnabled, |
| const armnn::DataLayout layout) |
| { |
| using namespace armnn; |
| |
| constexpr unsigned int batches = 1u; |
| constexpr unsigned int channels = 1u; |
| |
| constexpr unsigned int wInput = 3u; |
| constexpr unsigned int hInput = wInput; |
| |
| constexpr unsigned int wOutput = 7u; |
| constexpr unsigned int hOutput = wOutput; |
| |
| constexpr unsigned int wWeights = 3u; |
| constexpr unsigned int hWeights = wWeights; |
| |
| TensorShape inputShape = { batches, channels, hInput, wInput }; |
| TensorShape outputShape = { batches, channels, hOutput, wOutput }; |
| TensorShape weightsShape = { batches, channels, hWeights, wWeights }; |
| |
| TensorInfo inputInfo(inputShape, ArmnnType); |
| TensorInfo outputInfo(outputShape, ArmnnType); |
| TensorInfo weightsInfo(weightsShape, ArmnnType); |
| TensorInfo biasesInfo({ channels }, ArmnnBType); |
| |
| std::vector<float> inputData = |
| { |
| 1.f, 1.f, 1.f, |
| 1.f, 1.f, 1.f, |
| 1.f, 1.f, 1.f |
| }; |
| |
| std::vector<float> weightsData = |
| { |
| 1.f, 2.f, 3.f, |
| 4.f, 5.f, 6.f, |
| 7.f, 8.f, 9.f |
| }; |
| |
| std::vector<float> biasesData = { 1.f }; |
| |
| std::vector<float> expectedOutputData = |
| { |
| 1.f, 2.f, 4.f, 2.f, 4.f, 2.f, 3.f, |
| 4.f, 5.f, 10.f, 5.f, 10.f, 5.f, 6.f, |
| 8.f, 10.f, 20.f, 10.f, 20.f, 10.f, 12.f, |
| 4.f, 5.f, 10.f, 5.f, 10.f, 5.f, 6.f, |
| 8.f, 10.f, 20.f, 10.f, 20.f, 10.f, 12.f, |
| 4.f, 5.f, 10.f, 5.f, 10.f, 5.f, 6.f, |
| 7.f, 8.f, 16.f, 8.f, 16.f, 8.f, 9.f |
| }; |
| |
| if (biasEnabled) |
| { |
| // apply bias to expected output data |
| std::transform(expectedOutputData.begin(), expectedOutputData.end(), expectedOutputData.begin(), |
| [&](float f) -> float { return f + biasesData[0]; }); |
| } |
| |
| TransposeConvolution2dDescriptor descriptor; |
| descriptor.m_StrideX = 2; |
| descriptor.m_StrideY = 2; |
| descriptor.m_BiasEnabled = biasEnabled; |
| descriptor.m_DataLayout = layout; |
| |
| // swizzle data if needed |
| if (layout == armnn::DataLayout::NHWC) |
| { |
| SwizzleData(inputInfo, inputData, outputInfo, expectedOutputData, weightsInfo, weightsData); |
| } |
| |
| return TransposeConvolution2dTest<ArmnnType, ArmnnBType>(workloadFactory, |
| memoryManager, |
| descriptor, |
| inputInfo, |
| inputData, |
| outputInfo, |
| expectedOutputData, |
| weightsInfo, |
| weightsData, |
| biasesInfo, |
| biasesData); |
| } |
| |
| template<armnn::DataType ArmnnType, armnn::DataType ArmnnBType, typename T = armnn::ResolveType<ArmnnType>> |
| LayerTestResult<T, 4> MultiChannelTransposeConvolution2dTest( |
| armnn::IWorkloadFactory& workloadFactory, |
| const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager, |
| const armnn::DataLayout layout) |
| { |
| using namespace armnn; |
| |
| TensorShape inputShape = { 1, 1, 2, 2 }; |
| TensorShape outputShape = { 1, 2, 5, 5 }; |
| |
| // OIHW for NCHW; OHWI for NHWC |
| TensorShape weightsShape = { 2, 1, 3, 3 }; |
| TensorShape biasesShape = { 2 }; |
| |
| TensorInfo inputInfo(inputShape, ArmnnType); |
| TensorInfo outputInfo(outputShape, ArmnnType); |
| TensorInfo weightsInfo(weightsShape, ArmnnType); |
| TensorInfo biasesInfo(biasesShape, ArmnnBType); |
| |
| std::vector<float> inputData = |
| { |
| 1.f, 2.f, |
| 3.f, 4.f, |
| }; |
| |
| std::vector<float> weightsData = |
| { |
| 1.f, 3.f, 5.f, |
| 7.f, 9.f, 11.f, |
| 13.f, 15.f, 17.f, |
| |
| 2.f, 4.f, 6.f, |
| 8.f, 10.f, 12.f, |
| 14.f, 16.f, 18.f |
| }; |
| |
| std::vector<float> biasesData = { -1.5f, -2.0f }; |
| |
| std::vector<float> expectedOutputData = |
| { |
| -0.5f, 1.5f, 5.5f, 4.5f, 8.5f, |
| 5.5f, 7.5f, 23.5f, 16.5f, 20.5f, |
| 14.5f, 22.5f, 60.5f, 40.5f, 52.5f, |
| 19.5f, 25.5f, 59.5f, 34.5f, 42.5f, |
| 37.5f, 43.5f, 101.5f, 58.5f, 66.5f, |
| |
| 0.0f, 2.0f, 8.0f, 6.0f, 10.0f, |
| 6.0f, 8.0f, 26.0f, 18.0f, 22.0f, |
| 18.0f, 26.0f, 70.0f, 46.0f, 58.0f, |
| 22.0f, 28.0f, 66.0f, 38.0f, 46.0f, |
| 40.0f, 46.0f, 108.0f, 62.0f, 70.0f, |
| }; |
| |
| TransposeConvolution2dDescriptor descriptor; |
| descriptor.m_StrideX = 2; |
| descriptor.m_StrideY = 2; |
| descriptor.m_BiasEnabled = true; |
| descriptor.m_DataLayout = layout; |
| |
| // swizzle data if needed |
| if (layout == armnn::DataLayout::NHWC) |
| { |
| SwizzleData(inputInfo, inputData, outputInfo, expectedOutputData, weightsInfo, weightsData); |
| } |
| |
| return TransposeConvolution2dTest<ArmnnType, ArmnnBType>(workloadFactory, |
| memoryManager, |
| descriptor, |
| inputInfo, |
| inputData, |
| outputInfo, |
| expectedOutputData, |
| weightsInfo, |
| weightsData, |
| biasesInfo, |
| biasesData); |
| } |