2 // Copyright © 2017 Arm Ltd. All rights reserved.
3 // SPDX-License-Identifier: MIT
7 #include "QuantizeHelper.hpp"
9 #include <armnn/ArmNN.hpp>
11 #include <ResolveType.hpp>
13 #include <backendsCommon/CpuTensorHandle.hpp>
14 #include <backendsCommon/test/CommonTestUtils.hpp>
15 #include <backendsCommon/test/TensorCopyUtils.hpp>
16 #include <backendsCommon/test/WorkloadTestUtils.hpp>
18 #include <reference/RefWorkloadFactory.hpp>
20 #include <boost/test/unit_test.hpp>
30 using TensorData = std::pair<armnn::TensorInfo, std::vector<T>>;
33 void VerifyInputTensorData(const TensorData<T>& data, const std::string& tensorName)
35 if (data.first.GetNumElements() > data.second.size())
37 throw armnn::InvalidArgumentException("Size of data too small for " + tensorName + ": expected " +
38 std::to_string(data.first.GetNumElements()) + "but got " + std::to_string(data.second.size()));
42 template<typename T, typename BT>
43 void TransposeConvolution2dTestImpl(armnn::IWorkloadFactory& workloadFactory,
44 const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
45 const armnn::TransposeConvolution2dDescriptor& descriptor,
46 const TensorData<T>& input,
47 TensorData<T>& output,
48 const TensorData<T>& weights,
49 const armnn::Optional<TensorData<BT>>& biases)
51 using namespace armnn;
53 VerifyInputTensorData(input, "input");
54 VerifyInputTensorData(weights, "biases");
56 if (descriptor.m_BiasEnabled)
58 if (!biases.has_value())
60 throw InvalidArgumentException("Bias enabled but no bias data provided");
62 VerifyInputTensorData(biases.value(), "biases");
66 ScopedCpuTensorHandle weightsTensor(weights.first);
68 TransposeConvolution2dQueueDescriptor queueDescriptor;
69 queueDescriptor.m_Parameters = descriptor;
70 queueDescriptor.m_Weight = &weightsTensor;
72 AllocateAndCopyDataToITensorHandle(&weightsTensor, weights.second.data());
74 std::unique_ptr<ScopedCpuTensorHandle> biasesTensor;
75 if (descriptor.m_BiasEnabled)
78 biasesTensor = std::make_unique<ScopedCpuTensorHandle>(biases.value().first);
79 queueDescriptor.m_Bias = biasesTensor.get();
81 AllocateAndCopyDataToITensorHandle(biasesTensor.get(), biases.value().second.data());
84 // set up input and output handles
85 std::unique_ptr<ITensorHandle> inputHandle = workloadFactory.CreateTensorHandle(input.first);
86 std::unique_ptr<ITensorHandle> outputHandle = workloadFactory.CreateTensorHandle(output.first);
89 armnn::WorkloadInfo workloadInfo;
90 AddInputToWorkload(queueDescriptor, workloadInfo, input.first, inputHandle.get());
91 AddOutputToWorkload(queueDescriptor, workloadInfo, output.first, outputHandle.get());
93 std::unique_ptr<armnn::IWorkload> workload =
94 workloadFactory.CreateTransposeConvolution2d(queueDescriptor, workloadInfo);
96 inputHandle->Allocate();
97 outputHandle->Allocate();
99 CopyDataToITensorHandle(inputHandle.get(), input.second.data());
101 ExecuteWorkload(*workload, nullptr);
104 output.second = std::vector<T>(output.first.GetNumElements(), 0.0f);
105 CopyDataFromITensorHandle(output.second.data(), outputHandle.get());
108 template<armnn::DataType ArmnnType, armnn::DataType ArmnnBType, typename T = armnn::ResolveType<ArmnnType>>
109 LayerTestResult<T, 4> TransposeConvolution2dTest(
110 armnn::IWorkloadFactory& workloadFactory,
111 const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
112 const armnn::TransposeConvolution2dDescriptor& descriptor,
113 armnn::TensorInfo& inputInfo,
114 const std::vector<float>& inputData,
115 armnn::TensorInfo& outputInfo,
116 const std::vector<float>& expectedOutputData,
117 armnn::TensorInfo& weightsInfo,
118 const std::vector<float>& weightsData,
119 armnn::TensorInfo& biasesInfo,
120 const std::vector<float>& biasesData)
122 using namespace armnn;
124 // set up quantization parameters
125 if (armnn::IsQuantizedType<T>())
127 constexpr float qScale = 0.50f;
128 constexpr int32_t qOffset = 10;
130 inputInfo.SetQuantizationScale(qScale);
131 inputInfo.SetQuantizationOffset(qOffset);
133 outputInfo.SetQuantizationScale(qScale);
134 outputInfo.SetQuantizationOffset(qOffset);
136 weightsInfo.SetQuantizationScale(qScale);
137 weightsInfo.SetQuantizationOffset(qOffset);
139 biasesInfo.SetQuantizationScale(qScale * qScale);
140 biasesInfo.SetQuantizationOffset(0);
144 TensorData<T> input =
147 QuantizedVector<T>(inputInfo.GetQuantizationScale(), inputInfo.GetQuantizationOffset(), inputData)
151 TensorData<T> weights =
154 QuantizedVector<T>(weightsInfo.GetQuantizationScale(), weightsInfo.GetQuantizationOffset(), weightsData)
158 using BT = armnn::ResolveType<ArmnnBType>;
159 Optional<TensorData<BT>> optionalBiases;
160 if (descriptor.m_BiasEnabled)
162 TensorData<BT> biases =
165 QuantizedVector<BT>(biasesInfo.GetQuantizationScale(), biasesInfo.GetQuantizationOffset(), biasesData)
168 optionalBiases = Optional<TensorData<BT>>(biases);
172 TensorData<T> output = { outputInfo, {} };
175 TransposeConvolution2dTestImpl(workloadFactory,
183 // construct result object
184 LayerTestResult<T, 4> testResult(outputInfo);
185 testResult.output = MakeTensor<T, 4>(outputInfo, output.second);
186 testResult.outputExpected = MakeTensor<T, 4>(outputInfo,
187 QuantizedVector<T>(outputInfo.GetQuantizationScale(),
188 outputInfo.GetQuantizationOffset(),
189 expectedOutputData));
195 void SwizzleData(const armnn::TensorInfo& inputInfo,
196 std::vector<T>& inputData,
197 const armnn::TensorInfo& outputInfo,
198 std::vector<T>& outputData,
199 const armnn::TensorInfo& weightsInfo,
200 std::vector<T>& weightsData)
202 constexpr size_t dataTypeSize = sizeof(float);
203 const armnn::PermutationVector nchwToNhwc = { 0, 3, 1, 2 };
205 std::vector<T> tmp(inputData.size());
206 armnnUtils::Permute(inputInfo.GetShape(), nchwToNhwc, inputData.data(), tmp.data(), dataTypeSize);
209 tmp.resize(weightsData.size());
210 armnnUtils::Permute(weightsInfo.GetShape(), nchwToNhwc, weightsData.data(), tmp.data(), dataTypeSize);
213 tmp.resize(outputData.size());
214 armnnUtils::Permute(outputInfo.GetShape(), nchwToNhwc, outputData.data(), tmp.data(), dataTypeSize);
218 } // anonymous namespace
220 template<armnn::DataType ArmnnType, armnn::DataType ArmnnBType, typename T = armnn::ResolveType<ArmnnType>>
221 LayerTestResult<T, 4> SimpleTransposeConvolution2dTest(
222 armnn::IWorkloadFactory& workloadFactory,
223 const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
225 const armnn::DataLayout layout)
227 using namespace armnn;
229 constexpr unsigned int batches = 1u;
230 constexpr unsigned int channels = 1u;
232 constexpr unsigned int wInput = 3u;
233 constexpr unsigned int hInput = wInput;
235 constexpr unsigned int wOutput = 5u;
236 constexpr unsigned int hOutput = wOutput;
238 constexpr unsigned int wWeights = 3u;
239 constexpr unsigned int hWeights = wWeights;
241 TensorShape inputShape = MakeTensorShape(batches, channels, hInput, wInput, layout);
242 TensorShape outputShape = MakeTensorShape(batches, channels, hOutput, wOutput, layout);
243 TensorShape weightsShape = MakeTensorShape(batches, channels, hWeights, wWeights, layout);
245 TensorInfo inputInfo(inputShape, ArmnnType);
246 TensorInfo outputInfo(outputShape, ArmnnType);
247 TensorInfo weightsInfo(weightsShape, ArmnnType);
248 TensorInfo biasesInfo({ channels }, ArmnnBType);
250 std::vector<float> inputData =
257 std::vector<float> weightsData =
264 std::vector<float> biasesData = { 1.f };
266 std::vector<float> expectedOutputData =
268 1.f, 3.f, 6.f, 5.f, 3.f,
269 5.f, 12.f, 21.f, 16.f, 9.f,
270 12.f, 27.f, 45.f, 33.f, 18.f,
271 11.f, 24.f, 39.f, 28.f, 15.f,
272 7.f, 15.f, 24.f, 17.f, 9.f
277 // apply bias to expected output data
278 std::transform(expectedOutputData.begin(), expectedOutputData.end(), expectedOutputData.begin(),
279 [&](float f) -> float { return f + biasesData[0]; });
282 TransposeConvolution2dDescriptor descriptor;
283 descriptor.m_StrideX = 1;
284 descriptor.m_StrideY = 1;
285 descriptor.m_BiasEnabled = biasEnabled;
286 descriptor.m_DataLayout = layout;
288 // swizzle data if needed
289 if (layout == armnn::DataLayout::NHWC)
291 SwizzleData(inputInfo, inputData, outputInfo, expectedOutputData, weightsInfo, weightsData);
294 return TransposeConvolution2dTest<ArmnnType, ArmnnBType>(workloadFactory,
307 template<armnn::DataType ArmnnType, armnn::DataType ArmnnBType, typename T = armnn::ResolveType<ArmnnType>>
308 LayerTestResult<T, 4> PaddedTransposeConvolution2dTest(
309 armnn::IWorkloadFactory& workloadFactory,
310 const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
312 const armnn::DataLayout layout)
314 using namespace armnn;
316 constexpr unsigned int batches = 1u;
317 constexpr unsigned int channels = 1u;
319 constexpr unsigned int wInput = 4u;
320 constexpr unsigned int hInput = wInput;
322 constexpr unsigned int wOutput = 2u;
323 constexpr unsigned int hOutput = wOutput;
325 constexpr unsigned int wWeights = 3u;
326 constexpr unsigned int hWeights = wWeights;
328 TensorShape inputShape = MakeTensorShape(batches, channels, hInput, wInput, layout);
329 TensorShape outputShape = MakeTensorShape(batches, channels, hOutput, wOutput, layout);
330 TensorShape weightsShape = MakeTensorShape(batches, channels, hWeights, wWeights, layout);
332 TensorInfo inputInfo(inputShape, ArmnnType);
333 TensorInfo outputInfo(outputShape, ArmnnType);
334 TensorInfo weightsInfo(weightsShape, ArmnnType);
335 TensorInfo biasesInfo({ channels }, ArmnnBType);
337 std::vector<float> inputData =
345 std::vector<float> weightsData =
352 std::vector<float> biasesData = { 1.f };
354 std::vector<float> expectedOutputData =
362 // apply bias to expected output data
363 std::transform(expectedOutputData.begin(), expectedOutputData.end(), expectedOutputData.begin(),
364 [&](float f) -> float { return f + biasesData[0]; });
367 TransposeConvolution2dDescriptor descriptor;
368 descriptor.m_PadLeft = 2;
369 descriptor.m_PadRight = 2;
370 descriptor.m_PadTop = 2;
371 descriptor.m_PadBottom = 2;
372 descriptor.m_StrideX = 1;
373 descriptor.m_StrideY = 1;
374 descriptor.m_BiasEnabled = biasEnabled;
375 descriptor.m_DataLayout = layout;
377 // swizzle data if needed
378 if (layout == armnn::DataLayout::NHWC)
380 SwizzleData(inputInfo, inputData, outputInfo, expectedOutputData, weightsInfo, weightsData);
383 return TransposeConvolution2dTest<ArmnnType, ArmnnBType>(workloadFactory,
396 template<armnn::DataType ArmnnType, armnn::DataType ArmnnBType, typename T = armnn::ResolveType<ArmnnType>>
397 LayerTestResult<T, 4> StridedTransposeConvolution2dTest(
398 armnn::IWorkloadFactory& workloadFactory,
399 const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
401 const armnn::DataLayout layout)
403 using namespace armnn;
405 constexpr unsigned int batches = 1u;
406 constexpr unsigned int channels = 1u;
408 constexpr unsigned int wInput = 3u;
409 constexpr unsigned int hInput = wInput;
411 constexpr unsigned int wOutput = 7u;
412 constexpr unsigned int hOutput = wOutput;
414 constexpr unsigned int wWeights = 3u;
415 constexpr unsigned int hWeights = wWeights;
417 TensorShape inputShape = MakeTensorShape(batches, channels, hInput, wInput, layout);
418 TensorShape outputShape = MakeTensorShape(batches, channels, hOutput, wOutput, layout);
419 TensorShape weightsShape = MakeTensorShape(batches, channels, hWeights, wWeights, layout);
421 TensorInfo inputInfo(inputShape, ArmnnType);
422 TensorInfo outputInfo(outputShape, ArmnnType);
423 TensorInfo weightsInfo(weightsShape, ArmnnType);
424 TensorInfo biasesInfo({ channels }, ArmnnBType);
426 std::vector<float> inputData =
433 std::vector<float> weightsData =
440 std::vector<float> biasesData = { 1.f };
442 std::vector<float> expectedOutputData =
444 1.f, 2.f, 4.f, 2.f, 4.f, 2.f, 3.f,
445 4.f, 5.f, 10.f, 5.f, 10.f, 5.f, 6.f,
446 8.f, 10.f, 20.f, 10.f, 20.f, 10.f, 12.f,
447 4.f, 5.f, 10.f, 5.f, 10.f, 5.f, 6.f,
448 8.f, 10.f, 20.f, 10.f, 20.f, 10.f, 12.f,
449 4.f, 5.f, 10.f, 5.f, 10.f, 5.f, 6.f,
450 7.f, 8.f, 16.f, 8.f, 16.f, 8.f, 9.f
455 // apply bias to expected output data
456 std::transform(expectedOutputData.begin(), expectedOutputData.end(), expectedOutputData.begin(),
457 [&](float f) -> float { return f + biasesData[0]; });
460 TransposeConvolution2dDescriptor descriptor;
461 descriptor.m_StrideX = 2;
462 descriptor.m_StrideY = 2;
463 descriptor.m_BiasEnabled = biasEnabled;
464 descriptor.m_DataLayout = layout;
466 // swizzle data if needed
467 if (layout == armnn::DataLayout::NHWC)
469 SwizzleData(inputInfo, inputData, outputInfo, expectedOutputData, weightsInfo, weightsData);
472 return TransposeConvolution2dTest<ArmnnType, ArmnnBType>(workloadFactory,
485 template<armnn::DataType ArmnnType, armnn::DataType ArmnnBType, typename T = armnn::ResolveType<ArmnnType>>
486 LayerTestResult<T, 4> MultiChannelTransposeConvolution2dTest(
487 armnn::IWorkloadFactory& workloadFactory,
488 const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
489 const armnn::DataLayout layout)
491 using namespace armnn;
493 TensorShape inputShape = MakeTensorShape(1, 1, 2, 2, layout);
494 TensorShape outputShape = MakeTensorShape(1, 2, 5, 5, layout);
496 TensorShape weightsShape = MakeTensorShape(1, 2, 3, 3, layout);
497 TensorShape biasesShape = { 2 };
499 TensorInfo inputInfo(inputShape, ArmnnType);
500 TensorInfo outputInfo(outputShape, ArmnnType);
501 TensorInfo weightsInfo(weightsShape, ArmnnType);
502 TensorInfo biasesInfo(biasesShape, ArmnnBType);
504 std::vector<float> inputData =
510 std::vector<float> weightsData =
521 std::vector<float> biasesData = { -1.5f, -2.0f };
523 std::vector<float> expectedOutputData =
525 -0.5f, 1.5f, 5.5f, 4.5f, 8.5f,
526 5.5f, 7.5f, 23.5f, 16.5f, 20.5f,
527 14.5f, 22.5f, 60.5f, 40.5f, 52.5f,
528 19.5f, 25.5f, 59.5f, 34.5f, 42.5f,
529 37.5f, 43.5f, 101.5f, 58.5f, 66.5f,
531 0.0f, 2.0f, 8.0f, 6.0f, 10.0f,
532 6.0f, 8.0f, 26.0f, 18.0f, 22.0f,
533 18.0f, 26.0f, 70.0f, 46.0f, 58.0f,
534 22.0f, 28.0f, 66.0f, 38.0f, 46.0f,
535 40.0f, 46.0f, 108.0f, 62.0f, 70.0f,
538 TransposeConvolution2dDescriptor descriptor;
539 descriptor.m_StrideX = 2;
540 descriptor.m_StrideY = 2;
541 descriptor.m_BiasEnabled = true;
542 descriptor.m_DataLayout = layout;
544 // swizzle data if needed
545 if (layout == armnn::DataLayout::NHWC)
547 SwizzleData(inputInfo, inputData, outputInfo, expectedOutputData, weightsInfo, weightsData);
550 return TransposeConvolution2dTest<ArmnnType, ArmnnBType>(workloadFactory,