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> TransposeConvolution2dTestImpl(
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.25f;
128 constexpr int32_t qOffset = 50;
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));
194 } // anonymous namespace
196 template<armnn::DataType ArmnnType, armnn::DataType ArmnnBType, typename T = armnn::ResolveType<ArmnnType>>
197 LayerTestResult<T, 4> SimpleTransposeConvolution2dTestImpl(
198 armnn::IWorkloadFactory& workloadFactory,
199 const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
201 const armnn::DataLayout layout)
203 using namespace armnn;
205 constexpr unsigned int batches = 1u;
206 constexpr unsigned int channels = 1u;
208 constexpr unsigned int wInput = 3u;
209 constexpr unsigned int hInput = wInput;
211 constexpr unsigned int wOutput = 5u;
212 constexpr unsigned int hOutput = wOutput;
214 constexpr unsigned int wWeights = 3u;
215 constexpr unsigned int hWeights = wWeights;
217 TensorShape inputShape = MakeTensorShape(batches, channels, hInput, wInput, layout);
218 TensorShape outputShape = MakeTensorShape(batches, channels, hOutput, wOutput, layout);
219 TensorShape weightsShape = MakeTensorShape(batches, channels, hWeights, wWeights, layout);
221 TensorInfo inputInfo(inputShape, ArmnnType);
222 TensorInfo outputInfo(outputShape, ArmnnType);
223 TensorInfo weightsInfo(weightsShape, ArmnnType);
224 TensorInfo biasesInfo({ channels }, ArmnnBType);
226 std::vector<float> inputData =
233 std::vector<float> weightsData =
240 std::vector<float> biasesData = { 1.f };
242 std::vector<float> expectedOutputData =
244 1.f, 3.f, 6.f, 5.f, 3.f,
245 5.f, 12.f, 21.f, 16.f, 9.f,
246 12.f, 27.f, 45.f, 33.f, 18.f,
247 11.f, 24.f, 39.f, 28.f, 15.f,
248 7.f, 15.f, 24.f, 17.f, 9.f
253 // apply bias to expected output data
254 std::transform(expectedOutputData.begin(), expectedOutputData.end(), expectedOutputData.begin(),
255 [&](float f) -> float { return f + biasesData[0]; });
258 TransposeConvolution2dDescriptor descriptor;
259 descriptor.m_StrideX = 1;
260 descriptor.m_StrideY = 1;
261 descriptor.m_BiasEnabled = biasEnabled;
262 descriptor.m_DataLayout = layout;
264 // swizzle data if needed
265 if (layout == armnn::DataLayout::NHWC)
267 constexpr size_t dataTypeSize = sizeof(float);
268 const armnn::PermutationVector nchwToNhwc = { 0, 3, 1, 2 };
270 std::vector<float> tmp(inputData.size());
271 armnnUtils::Permute(inputInfo.GetShape(), nchwToNhwc, inputData.data(), tmp.data(), dataTypeSize);
274 tmp.resize(weightsData.size());
275 armnnUtils::Permute(weightsInfo.GetShape(), nchwToNhwc, weightsData.data(), tmp.data(), dataTypeSize);
278 tmp.resize(expectedOutputData.size());
279 armnnUtils::Permute(outputInfo.GetShape(), nchwToNhwc, expectedOutputData.data(), tmp.data(), dataTypeSize);
280 expectedOutputData = tmp;
283 return TransposeConvolution2dTestImpl<ArmnnType, ArmnnBType>(workloadFactory,
296 template<armnn::DataType ArmnnType, armnn::DataType ArmnnBType, typename T = armnn::ResolveType<ArmnnType>>
297 LayerTestResult<T, 4> PaddedTransposeConvolution2dTestImpl(
298 armnn::IWorkloadFactory& workloadFactory,
299 const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
301 const armnn::DataLayout layout)
303 using namespace armnn;
305 constexpr unsigned int batches = 1u;
306 constexpr unsigned int channels = 1u;
308 constexpr unsigned int wInput = 4u;
309 constexpr unsigned int hInput = wInput;
311 constexpr unsigned int wOutput = 2u;
312 constexpr unsigned int hOutput = wOutput;
314 constexpr unsigned int wWeights = 3u;
315 constexpr unsigned int hWeights = wWeights;
317 TensorShape inputShape = MakeTensorShape(batches, channels, hInput, wInput, layout);
318 TensorShape outputShape = MakeTensorShape(batches, channels, hOutput, wOutput, layout);
319 TensorShape weightsShape = MakeTensorShape(batches, channels, hWeights, wWeights, layout);
321 TensorInfo inputInfo(inputShape, ArmnnType);
322 TensorInfo outputInfo(outputShape, ArmnnType);
323 TensorInfo weightsInfo(weightsShape, ArmnnType);
324 TensorInfo biasesInfo({ channels }, ArmnnBType);
326 std::vector<float> inputData =
334 std::vector<float> weightsData =
341 std::vector<float> biasesData = { 1.f };
343 std::vector<float> expectedOutputData =
351 // apply bias to expected output data
352 std::transform(expectedOutputData.begin(), expectedOutputData.end(), expectedOutputData.begin(),
353 [&](float f) -> float { return f + biasesData[0]; });
356 TransposeConvolution2dDescriptor descriptor;
357 descriptor.m_PadLeft = 2;
358 descriptor.m_PadRight = 2;
359 descriptor.m_PadTop = 2;
360 descriptor.m_PadBottom = 2;
361 descriptor.m_StrideX = 1;
362 descriptor.m_StrideY = 1;
363 descriptor.m_BiasEnabled = biasEnabled;
364 descriptor.m_DataLayout = layout;
366 // swizzle data if needed
367 if (layout == armnn::DataLayout::NHWC)
369 constexpr size_t dataTypeSize = sizeof(float);
370 const armnn::PermutationVector nchwToNhwc = { 0, 3, 1, 2 };
372 std::vector<float> tmp(inputData.size());
373 armnnUtils::Permute(inputInfo.GetShape(), nchwToNhwc, inputData.data(), tmp.data(), dataTypeSize);
376 tmp.resize(weightsData.size());
377 armnnUtils::Permute(weightsInfo.GetShape(), nchwToNhwc, weightsData.data(), tmp.data(), dataTypeSize);
380 tmp.resize(expectedOutputData.size());
381 armnnUtils::Permute(outputInfo.GetShape(), nchwToNhwc, expectedOutputData.data(), tmp.data(), dataTypeSize);
382 expectedOutputData = tmp;
385 return TransposeConvolution2dTestImpl<ArmnnType, ArmnnBType>(workloadFactory,
398 template<armnn::DataType ArmnnType, armnn::DataType ArmnnBType, typename T = armnn::ResolveType<ArmnnType>>
399 LayerTestResult<T, 4> StridedTransposeConvolution2dTestImpl(
400 armnn::IWorkloadFactory& workloadFactory,
401 const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
403 const armnn::DataLayout layout)
405 using namespace armnn;
407 constexpr unsigned int batches = 1u;
408 constexpr unsigned int channels = 1u;
410 constexpr unsigned int wInput = 3u;
411 constexpr unsigned int hInput = wInput;
413 constexpr unsigned int wOutput = 7u;
414 constexpr unsigned int hOutput = wOutput;
416 constexpr unsigned int wWeights = 3u;
417 constexpr unsigned int hWeights = wWeights;
419 TensorShape inputShape = MakeTensorShape(batches, channels, hInput, wInput, layout);
420 TensorShape outputShape = MakeTensorShape(batches, channels, hOutput, wOutput, layout);
421 TensorShape weightsShape = MakeTensorShape(batches, channels, hWeights, wWeights, layout);
423 TensorInfo inputInfo(inputShape, ArmnnType);
424 TensorInfo outputInfo(outputShape, ArmnnType);
425 TensorInfo weightsInfo(weightsShape, ArmnnType);
426 TensorInfo biasesInfo({ channels }, ArmnnBType);
428 std::vector<float> inputData =
435 std::vector<float> weightsData =
442 std::vector<float> biasesData = { 1.f };
444 std::vector<float> expectedOutputData =
446 1.f, 2.f, 4.f, 2.f, 4.f, 2.f, 3.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 8.f, 10.f, 20.f, 10.f, 20.f, 10.f, 12.f,
451 4.f, 5.f, 10.f, 5.f, 10.f, 5.f, 6.f,
452 7.f, 8.f, 16.f, 8.f, 16.f, 8.f, 9.f
457 // apply bias to expected output data
458 std::transform(expectedOutputData.begin(), expectedOutputData.end(), expectedOutputData.begin(),
459 [&](float f) -> float { return f + biasesData[0]; });
462 TransposeConvolution2dDescriptor descriptor;
463 descriptor.m_StrideX = 2;
464 descriptor.m_StrideY = 2;
465 descriptor.m_BiasEnabled = biasEnabled;
466 descriptor.m_DataLayout = layout;
468 // swizzle data if needed
469 if (layout == armnn::DataLayout::NHWC)
471 constexpr size_t dataTypeSize = sizeof(float);
472 const armnn::PermutationVector nchwToNhwc = { 0, 3, 1, 2 };
474 std::vector<float> tmp(inputData.size());
475 armnnUtils::Permute(inputInfo.GetShape(), nchwToNhwc, inputData.data(), tmp.data(), dataTypeSize);
478 tmp.resize(weightsData.size());
479 armnnUtils::Permute(weightsInfo.GetShape(), nchwToNhwc, weightsData.data(), tmp.data(), dataTypeSize);
482 tmp.resize(expectedOutputData.size());
483 armnnUtils::Permute(outputInfo.GetShape(), nchwToNhwc, expectedOutputData.data(), tmp.data(), dataTypeSize);
484 expectedOutputData = tmp;
487 return TransposeConvolution2dTestImpl<ArmnnType, ArmnnBType>(workloadFactory,