blob: 6191adf61f601738627b2804acc79a5c56e9691c [file] [log] [blame]
Aron Virginas-Tar735a4502019-06-26 15:02:47 +01001//
2// Copyright © 2017 Arm Ltd. All rights reserved.
3// SPDX-License-Identifier: MIT
4//
Aron Virginas-Tar735a4502019-06-26 15:02:47 +01005
Aron Virginas-Tar00d306e2019-08-28 18:08:46 +01006#pragma once
Aron Virginas-Tar735a4502019-06-26 15:02:47 +01007
8#include <armnn/ArmNN.hpp>
9
Aron Virginas-Tar00d306e2019-08-28 18:08:46 +010010#include <Permute.hpp>
Aron Virginas-Tar735a4502019-06-26 15:02:47 +010011#include <ResolveType.hpp>
12
13#include <backendsCommon/CpuTensorHandle.hpp>
Aron Virginas-Tar00d306e2019-08-28 18:08:46 +010014
Aron Virginas-Tar735a4502019-06-26 15:02:47 +010015#include <backendsCommon/test/CommonTestUtils.hpp>
Aron Virginas-Tar00d306e2019-08-28 18:08:46 +010016#include <backendsCommon/test/QuantizeHelper.hpp>
Aron Virginas-Tar735a4502019-06-26 15:02:47 +010017#include <backendsCommon/test/TensorCopyUtils.hpp>
18#include <backendsCommon/test/WorkloadTestUtils.hpp>
19
20#include <reference/RefWorkloadFactory.hpp>
21
22#include <boost/test/unit_test.hpp>
23
24#include <string>
25#include <utility>
26#include <vector>
27
28namespace
29{
30
31template<typename T>
32using TensorData = std::pair<armnn::TensorInfo, std::vector<T>>;
33
34template<typename T>
35void VerifyInputTensorData(const TensorData<T>& data, const std::string& tensorName)
36{
37 if (data.first.GetNumElements() > data.second.size())
38 {
39 throw armnn::InvalidArgumentException("Size of data too small for " + tensorName + ": expected " +
40 std::to_string(data.first.GetNumElements()) + "but got " + std::to_string(data.second.size()));
41 }
42}
43
44template<typename T, typename BT>
45void TransposeConvolution2dTestImpl(armnn::IWorkloadFactory& workloadFactory,
46 const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
47 const armnn::TransposeConvolution2dDescriptor& descriptor,
48 const TensorData<T>& input,
49 TensorData<T>& output,
50 const TensorData<T>& weights,
51 const armnn::Optional<TensorData<BT>>& biases)
52{
53 using namespace armnn;
54
55 VerifyInputTensorData(input, "input");
56 VerifyInputTensorData(weights, "biases");
57
58 if (descriptor.m_BiasEnabled)
59 {
60 if (!biases.has_value())
61 {
62 throw InvalidArgumentException("Bias enabled but no bias data provided");
63 }
64 VerifyInputTensorData(biases.value(), "biases");
65 }
66
67 // set up weights
68 ScopedCpuTensorHandle weightsTensor(weights.first);
69
70 TransposeConvolution2dQueueDescriptor queueDescriptor;
71 queueDescriptor.m_Parameters = descriptor;
72 queueDescriptor.m_Weight = &weightsTensor;
73
74 AllocateAndCopyDataToITensorHandle(&weightsTensor, weights.second.data());
75
76 std::unique_ptr<ScopedCpuTensorHandle> biasesTensor;
77 if (descriptor.m_BiasEnabled)
78 {
79 // set up biases
80 biasesTensor = std::make_unique<ScopedCpuTensorHandle>(biases.value().first);
81 queueDescriptor.m_Bias = biasesTensor.get();
82
83 AllocateAndCopyDataToITensorHandle(biasesTensor.get(), biases.value().second.data());
84 }
85
86 // set up input and output handles
87 std::unique_ptr<ITensorHandle> inputHandle = workloadFactory.CreateTensorHandle(input.first);
88 std::unique_ptr<ITensorHandle> outputHandle = workloadFactory.CreateTensorHandle(output.first);
89
90 // set up workload
91 armnn::WorkloadInfo workloadInfo;
92 AddInputToWorkload(queueDescriptor, workloadInfo, input.first, inputHandle.get());
93 AddOutputToWorkload(queueDescriptor, workloadInfo, output.first, outputHandle.get());
94
95 std::unique_ptr<armnn::IWorkload> workload =
96 workloadFactory.CreateTransposeConvolution2d(queueDescriptor, workloadInfo);
97
98 inputHandle->Allocate();
99 outputHandle->Allocate();
100
101 CopyDataToITensorHandle(inputHandle.get(), input.second.data());
102
Aron Virginas-Tarf800de22019-08-16 17:49:42 +0100103 ExecuteWorkload(*workload, memoryManager);
Aron Virginas-Tar735a4502019-06-26 15:02:47 +0100104
105 // copy output
106 output.second = std::vector<T>(output.first.GetNumElements(), 0.0f);
107 CopyDataFromITensorHandle(output.second.data(), outputHandle.get());
108}
109
110template<armnn::DataType ArmnnType, armnn::DataType ArmnnBType, typename T = armnn::ResolveType<ArmnnType>>
Aron Virginas-Tard8edabb2019-08-12 14:29:59 +0100111LayerTestResult<T, 4> TransposeConvolution2dTest(
Aron Virginas-Tar735a4502019-06-26 15:02:47 +0100112 armnn::IWorkloadFactory& workloadFactory,
113 const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
114 const armnn::TransposeConvolution2dDescriptor& descriptor,
115 armnn::TensorInfo& inputInfo,
116 const std::vector<float>& inputData,
117 armnn::TensorInfo& outputInfo,
118 const std::vector<float>& expectedOutputData,
119 armnn::TensorInfo& weightsInfo,
120 const std::vector<float>& weightsData,
121 armnn::TensorInfo& biasesInfo,
122 const std::vector<float>& biasesData)
123{
124 using namespace armnn;
125
126 // set up quantization parameters
127 if (armnn::IsQuantizedType<T>())
128 {
Aron Virginas-Tard8edabb2019-08-12 14:29:59 +0100129 constexpr float qScale = 0.50f;
130 constexpr int32_t qOffset = 10;
Aron Virginas-Tar735a4502019-06-26 15:02:47 +0100131
132 inputInfo.SetQuantizationScale(qScale);
133 inputInfo.SetQuantizationOffset(qOffset);
134
135 outputInfo.SetQuantizationScale(qScale);
136 outputInfo.SetQuantizationOffset(qOffset);
137
138 weightsInfo.SetQuantizationScale(qScale);
139 weightsInfo.SetQuantizationOffset(qOffset);
140
141 biasesInfo.SetQuantizationScale(qScale * qScale);
142 biasesInfo.SetQuantizationOffset(0);
143 }
144
145 // set up input
146 TensorData<T> input =
147 {
148 inputInfo,
149 QuantizedVector<T>(inputInfo.GetQuantizationScale(), inputInfo.GetQuantizationOffset(), inputData)
150 };
151
152 // set up weights
153 TensorData<T> weights =
154 {
155 weightsInfo,
156 QuantizedVector<T>(weightsInfo.GetQuantizationScale(), weightsInfo.GetQuantizationOffset(), weightsData)
157 };
158
159 // set up biases
160 using BT = armnn::ResolveType<ArmnnBType>;
161 Optional<TensorData<BT>> optionalBiases;
162 if (descriptor.m_BiasEnabled)
163 {
164 TensorData<BT> biases =
165 {
166 biasesInfo,
167 QuantizedVector<BT>(biasesInfo.GetQuantizationScale(), biasesInfo.GetQuantizationOffset(), biasesData)
168 };
169
170 optionalBiases = Optional<TensorData<BT>>(biases);
171 }
172
173 // set up output
174 TensorData<T> output = { outputInfo, {} };
175
176 // execute test
177 TransposeConvolution2dTestImpl(workloadFactory,
178 memoryManager,
179 descriptor,
180 input,
181 output,
182 weights,
183 optionalBiases);
184
185 // construct result object
186 LayerTestResult<T, 4> testResult(outputInfo);
187 testResult.output = MakeTensor<T, 4>(outputInfo, output.second);
188 testResult.outputExpected = MakeTensor<T, 4>(outputInfo,
189 QuantizedVector<T>(outputInfo.GetQuantizationScale(),
190 outputInfo.GetQuantizationOffset(),
191 expectedOutputData));
192
193 return testResult;
194}
195
Aron Virginas-Tard8edabb2019-08-12 14:29:59 +0100196template<typename T>
197void SwizzleData(const armnn::TensorInfo& inputInfo,
198 std::vector<T>& inputData,
199 const armnn::TensorInfo& outputInfo,
200 std::vector<T>& outputData,
201 const armnn::TensorInfo& weightsInfo,
202 std::vector<T>& weightsData)
203{
204 constexpr size_t dataTypeSize = sizeof(float);
205 const armnn::PermutationVector nchwToNhwc = { 0, 3, 1, 2 };
206
207 std::vector<T> tmp(inputData.size());
208 armnnUtils::Permute(inputInfo.GetShape(), nchwToNhwc, inputData.data(), tmp.data(), dataTypeSize);
209 inputData = tmp;
210
211 tmp.resize(weightsData.size());
212 armnnUtils::Permute(weightsInfo.GetShape(), nchwToNhwc, weightsData.data(), tmp.data(), dataTypeSize);
213 weightsData = tmp;
214
215 tmp.resize(outputData.size());
216 armnnUtils::Permute(outputInfo.GetShape(), nchwToNhwc, outputData.data(), tmp.data(), dataTypeSize);
217 outputData = tmp;
218}
219
Aron Virginas-Tar735a4502019-06-26 15:02:47 +0100220} // anonymous namespace
221
222template<armnn::DataType ArmnnType, armnn::DataType ArmnnBType, typename T = armnn::ResolveType<ArmnnType>>
Aron Virginas-Tard8edabb2019-08-12 14:29:59 +0100223LayerTestResult<T, 4> SimpleTransposeConvolution2dTest(
Aron Virginas-Tar735a4502019-06-26 15:02:47 +0100224 armnn::IWorkloadFactory& workloadFactory,
225 const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
226 bool biasEnabled,
227 const armnn::DataLayout layout)
228{
229 using namespace armnn;
230
231 constexpr unsigned int batches = 1u;
232 constexpr unsigned int channels = 1u;
233
234 constexpr unsigned int wInput = 3u;
235 constexpr unsigned int hInput = wInput;
236
237 constexpr unsigned int wOutput = 5u;
238 constexpr unsigned int hOutput = wOutput;
239
240 constexpr unsigned int wWeights = 3u;
241 constexpr unsigned int hWeights = wWeights;
242
243 TensorShape inputShape = MakeTensorShape(batches, channels, hInput, wInput, layout);
244 TensorShape outputShape = MakeTensorShape(batches, channels, hOutput, wOutput, layout);
245 TensorShape weightsShape = MakeTensorShape(batches, channels, hWeights, wWeights, layout);
246
247 TensorInfo inputInfo(inputShape, ArmnnType);
248 TensorInfo outputInfo(outputShape, ArmnnType);
249 TensorInfo weightsInfo(weightsShape, ArmnnType);
250 TensorInfo biasesInfo({ channels }, ArmnnBType);
251
252 std::vector<float> inputData =
253 {
254 1.f, 1.f, 1.f,
255 1.f, 1.f, 1.f,
256 1.f, 1.f, 1.f
257 };
258
259 std::vector<float> weightsData =
260 {
261 1.f, 2.f, 3.f,
262 4.f, 5.f, 6.f,
263 7.f, 8.f, 9.f
264 };
265
266 std::vector<float> biasesData = { 1.f };
267
268 std::vector<float> expectedOutputData =
269 {
270 1.f, 3.f, 6.f, 5.f, 3.f,
271 5.f, 12.f, 21.f, 16.f, 9.f,
272 12.f, 27.f, 45.f, 33.f, 18.f,
273 11.f, 24.f, 39.f, 28.f, 15.f,
274 7.f, 15.f, 24.f, 17.f, 9.f
275 };
276
277 if (biasEnabled)
278 {
279 // apply bias to expected output data
280 std::transform(expectedOutputData.begin(), expectedOutputData.end(), expectedOutputData.begin(),
281 [&](float f) -> float { return f + biasesData[0]; });
282 }
283
284 TransposeConvolution2dDescriptor descriptor;
285 descriptor.m_StrideX = 1;
286 descriptor.m_StrideY = 1;
287 descriptor.m_BiasEnabled = biasEnabled;
288 descriptor.m_DataLayout = layout;
289
290 // swizzle data if needed
291 if (layout == armnn::DataLayout::NHWC)
292 {
Aron Virginas-Tard8edabb2019-08-12 14:29:59 +0100293 SwizzleData(inputInfo, inputData, outputInfo, expectedOutputData, weightsInfo, weightsData);
Aron Virginas-Tar735a4502019-06-26 15:02:47 +0100294 }
295
Aron Virginas-Tard8edabb2019-08-12 14:29:59 +0100296 return TransposeConvolution2dTest<ArmnnType, ArmnnBType>(workloadFactory,
297 memoryManager,
298 descriptor,
299 inputInfo,
300 inputData,
301 outputInfo,
302 expectedOutputData,
303 weightsInfo,
304 weightsData,
305 biasesInfo,
306 biasesData);
Aron Virginas-Tar735a4502019-06-26 15:02:47 +0100307}
308
309template<armnn::DataType ArmnnType, armnn::DataType ArmnnBType, typename T = armnn::ResolveType<ArmnnType>>
Aron Virginas-Tard8edabb2019-08-12 14:29:59 +0100310LayerTestResult<T, 4> PaddedTransposeConvolution2dTest(
Aron Virginas-Tar735a4502019-06-26 15:02:47 +0100311 armnn::IWorkloadFactory& workloadFactory,
312 const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
313 bool biasEnabled,
314 const armnn::DataLayout layout)
315{
316 using namespace armnn;
317
318 constexpr unsigned int batches = 1u;
319 constexpr unsigned int channels = 1u;
320
321 constexpr unsigned int wInput = 4u;
322 constexpr unsigned int hInput = wInput;
323
324 constexpr unsigned int wOutput = 2u;
325 constexpr unsigned int hOutput = wOutput;
326
327 constexpr unsigned int wWeights = 3u;
328 constexpr unsigned int hWeights = wWeights;
329
330 TensorShape inputShape = MakeTensorShape(batches, channels, hInput, wInput, layout);
331 TensorShape outputShape = MakeTensorShape(batches, channels, hOutput, wOutput, layout);
332 TensorShape weightsShape = MakeTensorShape(batches, channels, hWeights, wWeights, layout);
333
334 TensorInfo inputInfo(inputShape, ArmnnType);
335 TensorInfo outputInfo(outputShape, ArmnnType);
336 TensorInfo weightsInfo(weightsShape, ArmnnType);
337 TensorInfo biasesInfo({ channels }, ArmnnBType);
338
339 std::vector<float> inputData =
340 {
341 1.f, 3.f, 2.f, 1.f,
342 1.f, 3.f, 3.f, 1.f,
343 2.f, 1.f, 1.f, 3.f,
344 3.f, 2.f, 3.f, 3.f
345 };
346
347 std::vector<float> weightsData =
348 {
349 1.f, 2.f, 3.f,
350 0.f, 1.f, 0.f,
351 2.f, 1.f, 2.f
352 };
353
354 std::vector<float> biasesData = { 1.f };
355
356 std::vector<float> expectedOutputData =
357 {
358 21.f, 21.f,
359 28.f, 27.f
360 };
361
362 if (biasEnabled)
363 {
364 // apply bias to expected output data
365 std::transform(expectedOutputData.begin(), expectedOutputData.end(), expectedOutputData.begin(),
366 [&](float f) -> float { return f + biasesData[0]; });
367 }
368
369 TransposeConvolution2dDescriptor descriptor;
370 descriptor.m_PadLeft = 2;
371 descriptor.m_PadRight = 2;
372 descriptor.m_PadTop = 2;
373 descriptor.m_PadBottom = 2;
374 descriptor.m_StrideX = 1;
375 descriptor.m_StrideY = 1;
376 descriptor.m_BiasEnabled = biasEnabled;
377 descriptor.m_DataLayout = layout;
378
379 // swizzle data if needed
380 if (layout == armnn::DataLayout::NHWC)
381 {
Aron Virginas-Tard8edabb2019-08-12 14:29:59 +0100382 SwizzleData(inputInfo, inputData, outputInfo, expectedOutputData, weightsInfo, weightsData);
Aron Virginas-Tar735a4502019-06-26 15:02:47 +0100383 }
384
Aron Virginas-Tard8edabb2019-08-12 14:29:59 +0100385 return TransposeConvolution2dTest<ArmnnType, ArmnnBType>(workloadFactory,
386 memoryManager,
387 descriptor,
388 inputInfo,
389 inputData,
390 outputInfo,
391 expectedOutputData,
392 weightsInfo,
393 weightsData,
394 biasesInfo,
395 biasesData);
Aron Virginas-Tar735a4502019-06-26 15:02:47 +0100396}
397
Aron Virginas-Tard8edabb2019-08-12 14:29:59 +0100398template<armnn::DataType ArmnnType, armnn::DataType ArmnnBType, typename T = armnn::ResolveType<ArmnnType>>
399LayerTestResult<T, 4> StridedTransposeConvolution2dTest(
400 armnn::IWorkloadFactory& workloadFactory,
401 const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
402 bool biasEnabled,
403 const armnn::DataLayout layout)
Aron Virginas-Tar735a4502019-06-26 15:02:47 +0100404{
405 using namespace armnn;
406
407 constexpr unsigned int batches = 1u;
408 constexpr unsigned int channels = 1u;
409
410 constexpr unsigned int wInput = 3u;
411 constexpr unsigned int hInput = wInput;
412
413 constexpr unsigned int wOutput = 7u;
414 constexpr unsigned int hOutput = wOutput;
415
416 constexpr unsigned int wWeights = 3u;
417 constexpr unsigned int hWeights = wWeights;
418
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);
422
423 TensorInfo inputInfo(inputShape, ArmnnType);
424 TensorInfo outputInfo(outputShape, ArmnnType);
425 TensorInfo weightsInfo(weightsShape, ArmnnType);
426 TensorInfo biasesInfo({ channels }, ArmnnBType);
427
428 std::vector<float> inputData =
429 {
430 1.f, 1.f, 1.f,
431 1.f, 1.f, 1.f,
432 1.f, 1.f, 1.f
433 };
434
435 std::vector<float> weightsData =
436 {
437 1.f, 2.f, 3.f,
438 4.f, 5.f, 6.f,
439 7.f, 8.f, 9.f
440 };
441
442 std::vector<float> biasesData = { 1.f };
443
444 std::vector<float> expectedOutputData =
445 {
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
453 };
454
455 if (biasEnabled)
456 {
457 // apply bias to expected output data
458 std::transform(expectedOutputData.begin(), expectedOutputData.end(), expectedOutputData.begin(),
459 [&](float f) -> float { return f + biasesData[0]; });
460 }
461
462 TransposeConvolution2dDescriptor descriptor;
463 descriptor.m_StrideX = 2;
464 descriptor.m_StrideY = 2;
465 descriptor.m_BiasEnabled = biasEnabled;
466 descriptor.m_DataLayout = layout;
467
468 // swizzle data if needed
469 if (layout == armnn::DataLayout::NHWC)
470 {
Aron Virginas-Tard8edabb2019-08-12 14:29:59 +0100471 SwizzleData(inputInfo, inputData, outputInfo, expectedOutputData, weightsInfo, weightsData);
Aron Virginas-Tar735a4502019-06-26 15:02:47 +0100472 }
473
Aron Virginas-Tard8edabb2019-08-12 14:29:59 +0100474 return TransposeConvolution2dTest<ArmnnType, ArmnnBType>(workloadFactory,
475 memoryManager,
476 descriptor,
477 inputInfo,
478 inputData,
479 outputInfo,
480 expectedOutputData,
481 weightsInfo,
482 weightsData,
483 biasesInfo,
484 biasesData);
485}
486
487template<armnn::DataType ArmnnType, armnn::DataType ArmnnBType, typename T = armnn::ResolveType<ArmnnType>>
488LayerTestResult<T, 4> MultiChannelTransposeConvolution2dTest(
489 armnn::IWorkloadFactory& workloadFactory,
490 const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
491 const armnn::DataLayout layout)
492{
493 using namespace armnn;
494
495 TensorShape inputShape = MakeTensorShape(1, 1, 2, 2, layout);
496 TensorShape outputShape = MakeTensorShape(1, 2, 5, 5, layout);
497
Aron Virginas-Taraec942c2019-08-14 14:37:42 +0100498 // OIHW for NCHW; OHWI for NHWC
499 TensorShape weightsShape = MakeTensorShape(2, 1, 3, 3, layout);
Aron Virginas-Tard8edabb2019-08-12 14:29:59 +0100500 TensorShape biasesShape = { 2 };
501
502 TensorInfo inputInfo(inputShape, ArmnnType);
503 TensorInfo outputInfo(outputShape, ArmnnType);
504 TensorInfo weightsInfo(weightsShape, ArmnnType);
505 TensorInfo biasesInfo(biasesShape, ArmnnBType);
506
507 std::vector<float> inputData =
508 {
509 1.f, 2.f,
510 3.f, 4.f,
511 };
512
513 std::vector<float> weightsData =
514 {
515 1.f, 3.f, 5.f,
516 7.f, 9.f, 11.f,
517 13.f, 15.f, 17.f,
518
519 2.f, 4.f, 6.f,
520 8.f, 10.f, 12.f,
521 14.f, 16.f, 18.f
522 };
523
524 std::vector<float> biasesData = { -1.5f, -2.0f };
525
526 std::vector<float> expectedOutputData =
527 {
528 -0.5f, 1.5f, 5.5f, 4.5f, 8.5f,
529 5.5f, 7.5f, 23.5f, 16.5f, 20.5f,
530 14.5f, 22.5f, 60.5f, 40.5f, 52.5f,
531 19.5f, 25.5f, 59.5f, 34.5f, 42.5f,
532 37.5f, 43.5f, 101.5f, 58.5f, 66.5f,
533
534 0.0f, 2.0f, 8.0f, 6.0f, 10.0f,
535 6.0f, 8.0f, 26.0f, 18.0f, 22.0f,
536 18.0f, 26.0f, 70.0f, 46.0f, 58.0f,
537 22.0f, 28.0f, 66.0f, 38.0f, 46.0f,
538 40.0f, 46.0f, 108.0f, 62.0f, 70.0f,
539 };
540
541 TransposeConvolution2dDescriptor descriptor;
542 descriptor.m_StrideX = 2;
543 descriptor.m_StrideY = 2;
544 descriptor.m_BiasEnabled = true;
545 descriptor.m_DataLayout = layout;
546
547 // swizzle data if needed
548 if (layout == armnn::DataLayout::NHWC)
549 {
550 SwizzleData(inputInfo, inputData, outputInfo, expectedOutputData, weightsInfo, weightsData);
551 }
552
553 return TransposeConvolution2dTest<ArmnnType, ArmnnBType>(workloadFactory,
554 memoryManager,
555 descriptor,
556 inputInfo,
557 inputData,
558 outputInfo,
559 expectedOutputData,
560 weightsInfo,
561 weightsData,
562 biasesInfo,
563 biasesData);
564}