blob: d8c104007c50438102b6cfe50ad30d4d15b4ab29 [file] [log] [blame]
telsoa014fcda012018-03-09 14:13:49 +00001//
2// Copyright © 2017 Arm Ltd. All rights reserved.
David Beckecb56cd2018-09-05 12:52:57 +01003// SPDX-License-Identifier: MIT
telsoa014fcda012018-03-09 14:13:49 +00004//
5#pragma once
6
7#include <armnn/ArmNN.hpp>
8#include <armnn/Tensor.hpp>
9#include <armnn/TypesUtils.hpp>
telsoa014fcda012018-03-09 14:13:49 +000010
David Beckac42efd2018-09-26 17:41:13 +010011#include <test/TensorHelpers.hpp>
telsoa014fcda012018-03-09 14:13:49 +000012#include "QuantizeHelper.hpp"
13
David Beckac42efd2018-09-26 17:41:13 +010014#include <backends/CpuTensorHandle.hpp>
15#include <backends/WorkloadFactory.hpp>
telsoa014fcda012018-03-09 14:13:49 +000016
17// Mapping from input type to bias type for fully connected layers.
18// float => float, uint8_t => int32_t
19template<typename T>
20struct FullyConnectedBiasTypeForInputType;
21
22template<>
23struct FullyConnectedBiasTypeForInputType<float>
24{
25 using Type = float;
26};
27
28template<>
29struct FullyConnectedBiasTypeForInputType<uint8_t>
30{
31 using Type = int32_t;
32};
33
telsoa01c577f2c2018-08-31 09:22:23 +010034// Modifies a std::vector in-place using a specified bias.
telsoa014fcda012018-03-09 14:13:49 +000035template<typename T, typename B>
36void ApplyBias(std::vector<T>& v, float vScale, int32_t vOffset,
37 const std::vector<B>& bias, float bScale, int32_t bOffset, uint32_t w, uint32_t h)
38{
39 BOOST_ASSERT_MSG((armnn::IsQuantizedType<T>() && vScale != 0.0f) || (!armnn::IsQuantizedType<T>()),
40 "Invalid type and parameter combination.");
41 BOOST_ASSERT_MSG((armnn::IsQuantizedType<B>() && bScale != 0.0f) || (!armnn::IsQuantizedType<B>()),
42 "Invalid type and parameter combination.");
43
telsoa01c577f2c2018-08-31 09:22:23 +010044 // Note we need to dequantize and re-quantize the image value and the bias.
telsoa014fcda012018-03-09 14:13:49 +000045 for (uint32_t i = 0; i < bias.size(); ++i)
46 {
47 float dBias = SelectiveDequantize(bias[i], bScale, bOffset);
48 for (uint32_t y = 0; y < h; ++y)
49 {
50 for (uint32_t x = 0; x < w; ++x)
51 {
52 uint32_t offset = (i * h + y) * w + x;
53 BOOST_ASSERT(offset < v.size());
54 T& outRef = v[offset];
55 float dOutput = SelectiveDequantize(outRef, vScale, vOffset);
56 outRef = SelectiveQuantize<T>(dOutput + dBias, vScale, vOffset);
57 }
58 }
59 }
60}
61
telsoa014fcda012018-03-09 14:13:49 +000062template<typename T, typename B>
63LayerTestResult<T, 4> SimpleConvolution2dTestImpl(armnn::IWorkloadFactory& workloadFactory,
64 const boost::multi_array<T, 4>& input,
65 const boost::multi_array<T, 4>& kernel,
66 const boost::multi_array<B, 1>& bias,
67 const boost::multi_array<T, 4>& outputExpected,
68 float qScale,
69 int32_t qOffset,
70 uint32_t padLeft = 0,
71 uint32_t padTop = 0,
72 uint32_t padRight = 0,
73 uint32_t padBottom = 0)
74{
75 unsigned int inputHeight = boost::numeric_cast<unsigned int>(input.shape()[2]);
76 unsigned int inputWidth = boost::numeric_cast<unsigned int>(input.shape()[3]);
77 unsigned int inputChannels = boost::numeric_cast<unsigned int>(input.shape()[1]);
78 unsigned int inputNum = boost::numeric_cast<unsigned int>(input.shape()[0]);
79
80 unsigned int outputHeight = boost::numeric_cast<unsigned int>(outputExpected.shape()[2]);
81 unsigned int outputWidth = boost::numeric_cast<unsigned int>(outputExpected.shape()[3]);
82 unsigned int outputChannels = boost::numeric_cast<unsigned int>(outputExpected.shape()[1]);
83 unsigned int outputNum = boost::numeric_cast<unsigned int>(outputExpected.shape()[0]);
84
85 unsigned int kernelHeight = boost::numeric_cast<unsigned int>(kernel.shape()[2]);
86 unsigned int kernelWidth = boost::numeric_cast<unsigned int>(kernel.shape()[3]);
surmeh013537c2c2018-05-18 16:31:43 +010087 unsigned int kernelChannels = boost::numeric_cast<unsigned int>(kernel.shape()[1]);
88 unsigned int kernelDepthMul = boost::numeric_cast<unsigned int>(kernel.shape()[0]);
telsoa014fcda012018-03-09 14:13:49 +000089
90 bool biasEnabled = bias.size() > 0;
91
telsoa01c577f2c2018-08-31 09:22:23 +010092 // This function currently assumes 1 batch of input/output (and duplicates this into 2 batches).
telsoa014fcda012018-03-09 14:13:49 +000093 BOOST_ASSERT(inputNum == 1);
94 BOOST_ASSERT(outputNum == 1);
95
telsoa01c577f2c2018-08-31 09:22:23 +010096 // If a bias is used, its size must equal the number of output channels.
telsoa014fcda012018-03-09 14:13:49 +000097 BOOST_ASSERT(!biasEnabled || bias.size() == outputChannels);
98
99
telsoa01c577f2c2018-08-31 09:22:23 +0100100 // Note these tensors will use two (identical) batches.
telsoa014fcda012018-03-09 14:13:49 +0000101 armnn::TensorInfo inputTensorInfo({2*inputNum, inputChannels, inputHeight, inputWidth}, armnn::GetDataType<T>());
102 armnn::TensorInfo outputTensorInfo({2*outputNum, outputChannels, outputHeight, outputWidth},
103 armnn::GetDataType<T>());
surmeh013537c2c2018-05-18 16:31:43 +0100104 armnn::TensorInfo kernelDesc({kernelDepthMul, kernelChannels, kernelHeight, kernelWidth}, armnn::GetDataType<T>());
telsoa014fcda012018-03-09 14:13:49 +0000105 armnn::TensorInfo biasDesc({static_cast<unsigned int>(bias.size())}, armnn::GetDataType<B>());
106
107 // Set quantization parameters if the requested type is a quantized type.
108 if(armnn::IsQuantizedType<T>())
109 {
110 inputTensorInfo.SetQuantizationScale(qScale);
111 inputTensorInfo.SetQuantizationOffset(qOffset);
112 outputTensorInfo.SetQuantizationScale(qScale);
113 outputTensorInfo.SetQuantizationOffset(qOffset);
114 kernelDesc.SetQuantizationScale(qScale);
115 kernelDesc.SetQuantizationOffset(qOffset);
116 biasDesc.SetQuantizationScale(qScale*qScale);
117 biasDesc.SetQuantizationOffset(0);
118 }
119
120 LayerTestResult<T, 4> ret(outputTensorInfo);
121
telsoa01c577f2c2018-08-31 09:22:23 +0100122 // Construct input data - two batches of the same input image.
telsoa014fcda012018-03-09 14:13:49 +0000123 std::vector<T> inputImage;
124 inputImage.assign(input.data(), input.data() + 1*inputChannels*inputHeight*inputWidth);
125 std::vector<T> inputData;
126 inputData.insert(inputData.end(), inputImage.begin(), inputImage.end());
127 inputData.insert(inputData.end(), inputImage.begin(), inputImage.end());
128 auto batchedInput = MakeTensor<T, 4>(inputTensorInfo, inputData);
129
130 std::vector<T> outputImage;
131 outputImage.assign(outputExpected.data(), outputExpected.data() + outputChannels*outputHeight*outputWidth);
132
telsoa01c577f2c2018-08-31 09:22:23 +0100133 // Apply bias to output image if it is enabled.
telsoa014fcda012018-03-09 14:13:49 +0000134 if(biasEnabled)
135 {
136 std::vector<T> biasV;
137 biasV.assign(bias.data(), bias.data() + outputChannels);
138 ApplyBias(outputImage, outputTensorInfo.GetQuantizationScale(), outputTensorInfo.GetQuantizationOffset(),
139 biasV, biasDesc.GetQuantizationScale(), biasDesc.GetQuantizationOffset(),
140 outputWidth, outputHeight);
141 }
142
telsoa01c577f2c2018-08-31 09:22:23 +0100143 // Construct expected output data - two identical images.
telsoa014fcda012018-03-09 14:13:49 +0000144 std::vector<T> outputData;
145 outputData.insert(outputData.end(), outputImage.begin(), outputImage.end());
146 outputData.insert(outputData.end(), outputImage.begin(), outputImage.end());
147
148 ret.outputExpected = MakeTensor<T, 4>(outputTensorInfo, outputData);
149
telsoa01c577f2c2018-08-31 09:22:23 +0100150 // Todo: nontrivial padding and strides.
telsoa014fcda012018-03-09 14:13:49 +0000151 uint32_t strideX = 1;
152 uint32_t strideY = 1;
153
154 std::unique_ptr<armnn::ITensorHandle> inputHandle = workloadFactory.CreateTensorHandle(inputTensorInfo);
155 std::unique_ptr<armnn::ITensorHandle> outputHandle = workloadFactory.CreateTensorHandle(outputTensorInfo);
156
157 armnn::Convolution2dQueueDescriptor data;
158 armnn::WorkloadInfo info;
159 armnn::ScopedCpuTensorHandle weightsTensor(kernelDesc);
160 armnn::ScopedCpuTensorHandle biasTensor(biasDesc);
161
162 AllocateAndCopyDataToITensorHandle(&weightsTensor, &kernel[0][0][0][0]);
163
164 if(biasEnabled)
165 {
166 AllocateAndCopyDataToITensorHandle(&biasTensor, &bias[0]);
167 }
168
169 AddInputToWorkload(data, info, inputTensorInfo, inputHandle.get());
170 AddOutputToWorkload(data, info, outputTensorInfo, outputHandle.get());
171
172 data.m_Weight = &weightsTensor;
telsoa01c577f2c2018-08-31 09:22:23 +0100173 data.m_Bias = &biasTensor; // Still set this whether or not bias is enabled - can be a source of bugs.
telsoa014fcda012018-03-09 14:13:49 +0000174 data.m_Parameters.m_StrideX = strideX;
175 data.m_Parameters.m_StrideY = strideY;
176 data.m_Parameters.m_PadLeft = padLeft;
177 data.m_Parameters.m_PadRight = padRight;
178 data.m_Parameters.m_PadTop = padTop;
179 data.m_Parameters.m_PadBottom = padBottom;
180 data.m_Parameters.m_BiasEnabled = biasEnabled;
181
182 std::unique_ptr<armnn::IWorkload> workload = workloadFactory.CreateConvolution2d(data, info);
183 inputHandle->Allocate();
184 outputHandle->Allocate();
185
186 CopyDataToITensorHandle(inputHandle.get(), &batchedInput[0][0][0][0]);
187
surmeh013537c2c2018-05-18 16:31:43 +0100188 workloadFactory.Finalize();
189 workload->Execute();
190
191 CopyDataFromITensorHandle(&ret.output[0][0][0][0], outputHandle.get());
192
193 return ret;
194}
195
196template<typename T, typename B>
Francis Murtaghd59116e2018-10-04 16:03:07 +0100197LayerTestResult<T, 4> SimpleConvolution2dNhwcTestImpl(armnn::IWorkloadFactory& workloadFactory,
198 const boost::multi_array<T, 4>& input,
199 const boost::multi_array<T, 4>& kernel,
200 const boost::multi_array<B, 1>& bias,
201 const boost::multi_array<T, 4>& outputExpected,
202 armnn::DataLayout dataLayout,
203 float qScale,
204 int32_t qOffset,
205 uint32_t padLeft = 1,
206 uint32_t padTop = 1,
207 uint32_t padRight = 1,
208 uint32_t padBottom = 1,
209 uint32_t strideX = 1,
210 uint32_t strideY = 1)
211{
212 unsigned int inputNum = boost::numeric_cast<unsigned int>(input.shape()[0]);
213 unsigned int inputChannels = boost::numeric_cast<unsigned int>(input.shape()[3]);
214 unsigned int inputHeight = boost::numeric_cast<unsigned int>(input.shape()[1]);
215 unsigned int inputWidth = boost::numeric_cast<unsigned int>(input.shape()[2]);
216
217 unsigned int kernelChanMul = boost::numeric_cast<unsigned int>(kernel.shape()[0]);
218 unsigned int kernelChannels = boost::numeric_cast<unsigned int>(kernel.shape()[3]);
219 unsigned int kernelHeight = boost::numeric_cast<unsigned int>(kernel.shape()[1]);
220 unsigned int kernelWidth = boost::numeric_cast<unsigned int>(kernel.shape()[2]);
221
222 unsigned int outputNum = boost::numeric_cast<unsigned int>(outputExpected.shape()[0]);
223 unsigned int outputChannels = boost::numeric_cast<unsigned int>(outputExpected.shape()[3]);
224 unsigned int outputHeight = boost::numeric_cast<unsigned int>(outputExpected.shape()[1]);
225 unsigned int outputWidth = boost::numeric_cast<unsigned int>(outputExpected.shape()[2]);
226
227 bool biasEnabled = bias.size() > 0;
228
229 // Creates the tensors.
230 armnn::TensorInfo inputTensorInfo({inputNum, inputHeight, inputWidth, inputChannels}, armnn::GetDataType<T>());
231 armnn::TensorInfo outputTensorInfo({outputNum, outputHeight, outputWidth, outputChannels},
232 armnn::GetDataType<T>());
233 armnn::TensorInfo kernelDesc({kernelChanMul, kernelHeight, kernelWidth, kernelChannels}, armnn::GetDataType<T>());
234 armnn::TensorInfo biasDesc({static_cast<unsigned int>(bias.size())}, armnn::GetDataType<B>());
235
236 // Construct the input data.
237 std::vector<T> inputData;
238 inputData.assign(input.data(), input.data() + inputHeight*inputWidth*inputChannels);
239 auto batchedInput = MakeTensor<T, 4>(inputTensorInfo, inputData);
240
241 // Construct the output data, with bias applied, as appropriate.
242 std::vector<T> outputData;
243 outputData.assign(outputExpected.data(), outputExpected.data() + outputHeight*outputWidth*outputChannels);
244
245 LayerTestResult<T, 4> ret(outputTensorInfo);
246 ret.outputExpected = MakeTensor<T, 4>(outputTensorInfo, outputData);
247
248 std::unique_ptr<armnn::ITensorHandle> inputHandle = workloadFactory.CreateTensorHandle(inputTensorInfo);
249 std::unique_ptr<armnn::ITensorHandle> outputHandle = workloadFactory.CreateTensorHandle(outputTensorInfo);
250
251 armnn::ScopedCpuTensorHandle weightsTensor(kernelDesc);
252 AllocateAndCopyDataToITensorHandle(&weightsTensor, &kernel[0][0][0][0]);
253
254 armnn::ScopedCpuTensorHandle biasTensor(biasDesc);
255
256 armnn::Convolution2dQueueDescriptor data;
257
258 data.m_Weight = &weightsTensor;
259 data.m_Bias = &biasTensor; // Still set this whether or not bias is enabled - can be a source of bugs.
260 data.m_Parameters.m_StrideX = strideX;
261 data.m_Parameters.m_StrideY = strideY;
262 data.m_Parameters.m_PadLeft = padLeft;
263 data.m_Parameters.m_PadRight = padRight;
264 data.m_Parameters.m_PadTop = padTop;
265 data.m_Parameters.m_PadBottom = padBottom;
266 data.m_Parameters.m_BiasEnabled = biasEnabled;
267 data.m_Parameters.m_DataLayout = dataLayout;
268
269 armnn::WorkloadInfo info;
270 AddInputToWorkload(data, info, inputTensorInfo, inputHandle.get());
271 AddOutputToWorkload(data, info, outputTensorInfo, outputHandle.get());
272
273 std::unique_ptr<armnn::IWorkload> workload = workloadFactory.CreateConvolution2d(data, info);
274 inputHandle->Allocate();
275 outputHandle->Allocate();
276
277 CopyDataToITensorHandle(inputHandle.get(), &batchedInput[0][0][0][0]);
278
279 workloadFactory.Finalize();
280 workload->Execute();
281
282 CopyDataFromITensorHandle(&ret.output[0][0][0][0], outputHandle.get());
283
284 return ret;
285}
286
287template<typename T, typename B>
surmeh013537c2c2018-05-18 16:31:43 +0100288LayerTestResult<T, 4> DepthwiseConvolution2dAsymmetricTestImpl(armnn::IWorkloadFactory& workloadFactory,
289 const boost::multi_array<T, 4>& input,
290 const boost::multi_array<T, 4>& kernel,
291 const boost::multi_array<B, 1>& bias,
292 const boost::multi_array<T, 4>& outputExpected,
293 float qScale,
294 int32_t qOffset,
295 uint32_t padLeft = 0,
296 uint32_t padTop = 0,
297 uint32_t padRight = 0,
298 uint32_t padBottom = 0,
299 uint32_t strideX = 1,
300 uint32_t strideY = 1)
301{
302 unsigned int inputNum = boost::numeric_cast<unsigned int>(input.shape()[0]);
303 unsigned int inputChannels = boost::numeric_cast<unsigned int>(input.shape()[1]);
304 unsigned int inputHeight = boost::numeric_cast<unsigned int>(input.shape()[2]);
305 unsigned int inputWidth = boost::numeric_cast<unsigned int>(input.shape()[3]);
306 unsigned int kernelChanMul = boost::numeric_cast<unsigned int>(kernel.shape()[0]);
307 unsigned int kernelChannels = boost::numeric_cast<unsigned int>(kernel.shape()[1]);
308 unsigned int kernelHeight = boost::numeric_cast<unsigned int>(kernel.shape()[2]);
309 unsigned int kernelWidth = boost::numeric_cast<unsigned int>(kernel.shape()[3]);
310 unsigned int outputNum = boost::numeric_cast<unsigned int>(outputExpected.shape()[0]);
311 unsigned int outputChannels = boost::numeric_cast<unsigned int>(outputExpected.shape()[1]);
312 unsigned int outputHeight = boost::numeric_cast<unsigned int>(outputExpected.shape()[2]);
313 unsigned int outputWidth = boost::numeric_cast<unsigned int>(outputExpected.shape()[3]);
314
telsoa01c577f2c2018-08-31 09:22:23 +0100315 // If a bias is used, its size must equal the number of output channels.
surmeh013537c2c2018-05-18 16:31:43 +0100316 bool biasEnabled = bias.size() > 0;
317 BOOST_ASSERT(!biasEnabled || bias.size() == outputChannels);
318
telsoa01c577f2c2018-08-31 09:22:23 +0100319 // Creates the tensors.
surmeh013537c2c2018-05-18 16:31:43 +0100320 armnn::TensorInfo inputTensorInfo({inputNum, inputChannels, inputHeight, inputWidth}, armnn::GetDataType<T>());
321 armnn::TensorInfo outputTensorInfo({outputNum, outputChannels, outputHeight, outputWidth},
322 armnn::GetDataType<T>());
323 armnn::TensorInfo kernelDesc({kernelChanMul, kernelChannels, kernelHeight, kernelWidth}, armnn::GetDataType<T>());
324 armnn::TensorInfo biasDesc({static_cast<unsigned int>(bias.size())}, armnn::GetDataType<B>());
325
326 // Set quantization parameters if the requested type is a quantized type.
327 if (armnn::IsQuantizedType<T>())
328 {
329 inputTensorInfo.SetQuantizationScale(qScale);
330 inputTensorInfo.SetQuantizationOffset(qOffset);
331 outputTensorInfo.SetQuantizationScale(qScale);
332 outputTensorInfo.SetQuantizationOffset(qOffset);
333 kernelDesc.SetQuantizationScale(qScale);
334 kernelDesc.SetQuantizationOffset(qOffset);
335 biasDesc.SetQuantizationScale(qScale*qScale);
336 biasDesc.SetQuantizationOffset(0);
337 }
338
telsoa01c577f2c2018-08-31 09:22:23 +0100339 // Construct the input data.
surmeh013537c2c2018-05-18 16:31:43 +0100340 std::vector<T> inputData;
341 inputData.assign(input.data(), input.data() + inputChannels*inputHeight*inputWidth);
342 auto batchedInput = MakeTensor<T, 4>(inputTensorInfo, inputData);
343
telsoa01c577f2c2018-08-31 09:22:23 +0100344 // Construct the output data, with bias applied, as appropriate.
surmeh013537c2c2018-05-18 16:31:43 +0100345 std::vector<T> outputData;
346 outputData.assign(outputExpected.data(), outputExpected.data() + outputChannels*outputHeight*outputWidth);
347 if (biasEnabled)
348 {
349 std::vector<T> biasV;
350 biasV.assign(bias.data(), bias.data() + outputChannels);
351 ApplyBias(outputData, outputTensorInfo.GetQuantizationScale(), outputTensorInfo.GetQuantizationOffset(),
352 biasV, biasDesc.GetQuantizationScale(), biasDesc.GetQuantizationOffset(),
353 outputWidth, outputHeight);
354 }
355
356 LayerTestResult<T, 4> ret(outputTensorInfo);
357 ret.outputExpected = MakeTensor<T, 4>(outputTensorInfo, outputData);
358
359 std::unique_ptr<armnn::ITensorHandle> inputHandle = workloadFactory.CreateTensorHandle(inputTensorInfo);
360 std::unique_ptr<armnn::ITensorHandle> outputHandle = workloadFactory.CreateTensorHandle(outputTensorInfo);
361
362 armnn::ScopedCpuTensorHandle weightsTensor(kernelDesc);
363 AllocateAndCopyDataToITensorHandle(&weightsTensor, &kernel[0][0][0][0]);
364
365 armnn::ScopedCpuTensorHandle biasTensor(biasDesc);
366 if (biasEnabled)
367 {
368 AllocateAndCopyDataToITensorHandle(&biasTensor, &bias[0]);
369 }
370
371 armnn::DepthwiseConvolution2dQueueDescriptor data;
372 data.m_Weight = &weightsTensor;
telsoa01c577f2c2018-08-31 09:22:23 +0100373 data.m_Bias = &biasTensor; // Still set this whether or not bias is enabled - it can be a source of bugs.
surmeh013537c2c2018-05-18 16:31:43 +0100374 data.m_Parameters.m_StrideX = strideX;
375 data.m_Parameters.m_StrideY = strideY;
376 data.m_Parameters.m_PadLeft = padLeft;
377 data.m_Parameters.m_PadRight = padRight;
378 data.m_Parameters.m_PadTop = padTop;
379 data.m_Parameters.m_PadBottom = padBottom;
380 data.m_Parameters.m_BiasEnabled = biasEnabled;
381
382 armnn::WorkloadInfo info;
383 AddInputToWorkload(data, info, inputTensorInfo, inputHandle.get());
384 AddOutputToWorkload(data, info, outputTensorInfo, outputHandle.get());
385
386 std::unique_ptr<armnn::IWorkload> workload = workloadFactory.CreateDepthwiseConvolution2d(data, info);
387 inputHandle->Allocate();
388 outputHandle->Allocate();
389
390 CopyDataToITensorHandle(inputHandle.get(), &batchedInput[0][0][0][0]);
391
392 workloadFactory.Finalize();
telsoa014fcda012018-03-09 14:13:49 +0000393 workload->Execute();
394
395 CopyDataFromITensorHandle(&ret.output[0][0][0][0], outputHandle.get());
396
397 return ret;
398}
399
400template<typename T, typename B>
401LayerTestResult<T, 4> DepthwiseConvolution2dDepthMul1TestImpl(armnn::IWorkloadFactory& workloadFactory,
402 float qScale,
403 int32_t qOffset,
404 bool biasEnabled)
405{
406 unsigned int inputHeight = 3;
407 unsigned int inputWidth = 3;
408 unsigned int inputChannels = 2;
409 unsigned int inputNum = 1;
410
411 unsigned int kernelHeight = 3;
412 unsigned int kernelWidth = 3;
413 unsigned int kernelChannels = inputChannels;
414
415 unsigned int outputHeight = 1;
416 unsigned int outputWidth = 1;
417 unsigned int outputChannels = kernelChannels;
418 unsigned int outputNum = inputNum;
419
420 armnn::TensorInfo inputTensorInfo({ inputNum, inputChannels, inputHeight, inputWidth }, armnn::GetDataType<T>());
421 armnn::TensorInfo outputTensorInfo({ outputNum, outputChannels, outputHeight, outputWidth },
422 armnn::GetDataType<T>());
423 armnn::TensorInfo kernelDesc({ 1, outputChannels, kernelHeight, kernelWidth }, armnn::GetDataType<T>());
424 armnn::TensorInfo biasDesc({ outputChannels }, armnn::GetDataType<B>());
425
426 // Set quantization parameters if the requested type is a quantized type.
427 if(armnn::IsQuantizedType<T>())
428 {
429 inputTensorInfo.SetQuantizationScale(qScale);
430 inputTensorInfo.SetQuantizationOffset(qOffset);
431 outputTensorInfo.SetQuantizationScale(qScale);
432 outputTensorInfo.SetQuantizationOffset(qOffset);
433 kernelDesc.SetQuantizationScale(qScale);
434 kernelDesc.SetQuantizationOffset(qOffset);
435 biasDesc.SetQuantizationScale(qScale*qScale);
436 biasDesc.SetQuantizationOffset(0);
437 }
438
439 auto input = MakeTensor<T, 4>(inputTensorInfo, std::vector<T>(
440 QuantizedVector<T>(inputTensorInfo.GetQuantizationScale(), inputTensorInfo.GetQuantizationOffset(), {
441 1.f, 2.f, 1.f,
442 2.f, 1.f, 2.f,
443 1.f, 2.f, 1.f,
444
445 1.f, 2.f, 1.f,
446 2.f, 1.f, 2.f,
447 1.f, 2.f, 1.f,
448 })));
449
450 std::vector<B> biasV(QuantizedVector<B>(biasDesc.GetQuantizationScale(), biasDesc.GetQuantizationOffset(),
451 {0, 2}));
452 auto bias = MakeTensor<B, 1>(biasDesc, biasV);
453
454 auto kernel = MakeTensor<T, 4>(kernelDesc, std::vector<T>(
455 QuantizedVector<T>(kernelDesc.GetQuantizationScale(), kernelDesc.GetQuantizationOffset(), {
456 1.f, 0.f, 1.f,
457 0.f, 0.f, 0.f,
458 -1.f, 0.f, -1.f,
459
460 1.f, 0.f, 1.f,
461 0.f, 0.f, 0.f,
462 -1.f, 0.f, -1.f,
463 })));
464
telsoa01c577f2c2018-08-31 09:22:23 +0100465 // Manually calculated.
telsoa014fcda012018-03-09 14:13:49 +0000466 std::vector<T> outputImage(
467 QuantizedVector<T>(outputTensorInfo.GetQuantizationScale(),
468 outputTensorInfo.GetQuantizationOffset(),
469 {0.f, 0.f})
470 );
471
telsoa01c577f2c2018-08-31 09:22:23 +0100472 // Optionally apply bias to output image.
telsoa014fcda012018-03-09 14:13:49 +0000473 if(biasEnabled)
474 {
475 ApplyBias(outputImage, outputTensorInfo.GetQuantizationScale(), outputTensorInfo.GetQuantizationOffset(),
476 biasV, biasDesc.GetQuantizationScale(), biasDesc.GetQuantizationOffset(),
477 outputWidth, outputHeight);
478 }
479
480 LayerTestResult<T, 4> ret(outputTensorInfo);
481 ret.outputExpected = MakeTensor<T, 4>(outputTensorInfo, outputImage);
482
483 std::unique_ptr<armnn::ITensorHandle> inputHandle = workloadFactory.CreateTensorHandle(inputTensorInfo);
484 std::unique_ptr<armnn::ITensorHandle> outputHandle = workloadFactory.CreateTensorHandle(outputTensorInfo);
485
486 armnn::DepthwiseConvolution2dQueueDescriptor data;
487 armnn::WorkloadInfo info;
488 armnn::ScopedCpuTensorHandle weightsTensor(kernelDesc);
489 armnn::ScopedCpuTensorHandle biasTensor(biasDesc);
490
491 AllocateAndCopyDataToITensorHandle(&weightsTensor, &kernel[0][0][0][0]);
492 AllocateAndCopyDataToITensorHandle(&biasTensor, &bias[0]);
493
494 AddInputToWorkload(data, info, inputTensorInfo, inputHandle.get());
495 AddOutputToWorkload(data, info, outputTensorInfo, outputHandle.get());
496
497 data.m_Weight = &weightsTensor;
telsoa01c577f2c2018-08-31 09:22:23 +0100498 data.m_Bias = &biasTensor; // Still set this whether or not bias is enabled.
telsoa014fcda012018-03-09 14:13:49 +0000499 data.m_Parameters.m_StrideX = 1;
500 data.m_Parameters.m_StrideY = 1;
501 data.m_Parameters.m_PadLeft = 0;
502 data.m_Parameters.m_PadRight = 0;
503 data.m_Parameters.m_PadTop = 0;
504 data.m_Parameters.m_PadBottom = 0;
505 data.m_Parameters.m_BiasEnabled = biasEnabled;
506
507 std::unique_ptr<armnn::IWorkload> workload = workloadFactory.CreateDepthwiseConvolution2d(data, info);
508 inputHandle->Allocate();
509 outputHandle->Allocate();
510
511 CopyDataToITensorHandle(inputHandle.get(), &input[0][0][0][0]);
512
surmeh013537c2c2018-05-18 16:31:43 +0100513 workloadFactory.Finalize();
telsoa014fcda012018-03-09 14:13:49 +0000514 workload->Execute();
515
516 CopyDataFromITensorHandle(&ret.output[0][0][0][0], outputHandle.get());
517
518 return ret;
519}
520
521template<typename T, typename B>
522LayerTestResult<T, 4> DepthwiseConvolution2dTestImpl(armnn::IWorkloadFactory& workloadFactory,
523 float qScale,
524 int32_t qOffset,
525 bool biasEnabled)
526{
527 unsigned int depthMultiplier = 2;
528
529 unsigned int inputHeight = 8;
530 unsigned int inputWidth = 16;
531 unsigned int inputChannels = 2;
532 unsigned int inputBatchSize = 1;
533
534 unsigned int kernelHeight = 5;
535 unsigned int kernelWidth = 3;
536
537 unsigned int outputHeight = inputHeight - kernelHeight + 1 + 2;
538 unsigned int outputWidth = (inputWidth - kernelWidth + 1)/2;
539 unsigned int outputChannels = inputChannels * depthMultiplier;
540 unsigned int outputBatchSize = inputBatchSize;
541
542 armnn::TensorInfo inputTensorInfo({inputBatchSize, inputChannels, inputHeight, inputWidth},
543 armnn::GetDataType<T>());
544 armnn::TensorInfo outputTensorInfo({outputBatchSize, outputChannels, outputHeight, outputWidth},
545 armnn::GetDataType<T>());
546 armnn::TensorInfo kernelDesc({depthMultiplier, inputChannels, kernelHeight, kernelWidth}, armnn::GetDataType<T>());
547 armnn::TensorInfo biasDesc({outputChannels}, armnn::GetDataType<B>());
548
549 // Set quantization parameters if the requested type is a quantized type.
550 if(armnn::IsQuantizedType<T>())
551 {
552 inputTensorInfo.SetQuantizationScale(qScale);
553 inputTensorInfo.SetQuantizationOffset(qOffset);
554 outputTensorInfo.SetQuantizationScale(qScale);
555 outputTensorInfo.SetQuantizationOffset(qOffset);
556 kernelDesc.SetQuantizationScale(qScale);
557 kernelDesc.SetQuantizationOffset(qOffset);
558 biasDesc.SetQuantizationScale(qScale*qScale);
559 biasDesc.SetQuantizationOffset(0);
560 }
561
562 auto input = MakeTensor<T, 4>(inputTensorInfo, std::vector<T>(
563 QuantizedVector<T>(inputTensorInfo.GetQuantizationScale(), inputTensorInfo.GetQuantizationOffset(), {
564 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f,
565 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
566 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f,
567 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f,
568 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f,
569 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f,
570 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f,
571 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f,
572 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
573 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
574 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
575 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
576 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
577 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
578 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
579 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
580 })));
581
582 std::vector<B> biasV(QuantizedVector<B>(biasDesc.GetQuantizationScale(), biasDesc.GetQuantizationOffset(),
583 {0, 2, 1, -1}));
584 auto bias = MakeTensor<B, 1>(biasDesc, biasV);
585
586 auto kernel = MakeTensor<T, 4>(kernelDesc, std::vector<T>(
587 QuantizedVector<T>(kernelDesc.GetQuantizationScale(), kernelDesc.GetQuantizationOffset(), {
588 1, 1, 1,
589 1, -1, 1,
590 1, 1, 1,
591 1, 1, 1,
592 1, 1, 1,
593
594 2, 2, 2,
595 2, 2, 2,
596 2, 2, 2,
597 2, 2, 2,
598 2, 2, 2,
599
600 0, 0, 0,
601 0, -1, 0,
602 0, 0, 0,
603 0, 0, 0,
604 0, 0, 0,
605
606 0, 0, 0,
607 0, 0, 0,
608 0, 1, 0,
609 0, 0, 0,
610 0, 0, 0
611 })));
612
telsoa01c577f2c2018-08-31 09:22:23 +0100613 // Manually calculated.
telsoa014fcda012018-03-09 14:13:49 +0000614 std::vector<T> outputImage = std::vector<T>(
615 QuantizedVector<T>(outputTensorInfo.GetQuantizationScale(), outputTensorInfo.GetQuantizationOffset(), {
616 3.5f, 3.5f, 3.5f, 3.5f, 3.5f, 3.5f, 3.5f,
617 6.0f, 6.0f, 6.0f, 6.0f, 6.0f, 6.0f, 6.0f,
618 5.0f, 5.0f, 5.0f, 5.0f, 5.0f, 5.0f, 5.0f,
619 6.5f, 6.5f, 6.5f, 6.5f, 6.5f, 6.5f, 6.5f,
620 6.5f, 6.5f, 6.5f, 6.5f, 6.5f, 6.5f, 6.5f,
621 5.0f, 5.0f, 5.0f, 5.0f, 5.0f, 5.0f, 5.0f,
622
623 -0.5f, -0.5f, -0.5f, -0.5f, -0.5f, -0.5f, -0.5f,
624 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
625 -0.5f, -0.5f, -0.5f, -0.5f, -0.5f, -0.5f, -0.5f,
626 -0.5f, -0.5f, -0.5f, -0.5f, -0.5f, -0.5f, -0.5f,
627 -0.5f, -0.5f, -0.5f, -0.5f, -0.5f, -0.5f, -0.5f,
628 -0.5f, -0.5f, -0.5f, -0.5f, -0.5f, -0.5f, -0.5f,
629
630 8.0f, 8.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
631 10.0f, 10.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
632 10.0f, 10.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
633 10.0f, 10.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
634 10.0f, 10.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
635 8.0f, 8.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
636
637 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
638 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
639 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
640 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
641 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
642 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f
643 }));
644
telsoa01c577f2c2018-08-31 09:22:23 +0100645 // Optionally apply bias to output image.
telsoa014fcda012018-03-09 14:13:49 +0000646 if(biasEnabled)
647 {
648 ApplyBias(outputImage, outputTensorInfo.GetQuantizationScale(), outputTensorInfo.GetQuantizationOffset(),
649 biasV, biasDesc.GetQuantizationScale(), biasDesc.GetQuantizationOffset(),
650 outputWidth, outputHeight);
651 }
652
653 LayerTestResult<T, 4> ret(outputTensorInfo);
654 ret.outputExpected = MakeTensor<T, 4>(outputTensorInfo, outputImage);
655
656 std::unique_ptr<armnn::ITensorHandle> inputHandle = workloadFactory.CreateTensorHandle(inputTensorInfo);
657 std::unique_ptr<armnn::ITensorHandle> outputHandle = workloadFactory.CreateTensorHandle(outputTensorInfo);
658
659 armnn::DepthwiseConvolution2dQueueDescriptor data;
660 armnn::WorkloadInfo info;
661 armnn::ScopedCpuTensorHandle weightsTensor(kernelDesc);
662 armnn::ScopedCpuTensorHandle biasTensor(biasDesc);
663
664 AllocateAndCopyDataToITensorHandle(&weightsTensor, &kernel[0][0][0][0]);
665 AllocateAndCopyDataToITensorHandle(&biasTensor, &bias[0]);
666
667 AddInputToWorkload(data, info, inputTensorInfo, inputHandle.get());
668 AddOutputToWorkload(data, info, outputTensorInfo, outputHandle.get());
669
670 data.m_Weight = &weightsTensor;
telsoa01c577f2c2018-08-31 09:22:23 +0100671 data.m_Bias = &biasTensor; // Still set this whether or not bias is enabled.
telsoa014fcda012018-03-09 14:13:49 +0000672 data.m_Parameters.m_StrideX = 2;
673 data.m_Parameters.m_StrideY = 1;
674 data.m_Parameters.m_PadLeft = 0;
675 data.m_Parameters.m_PadRight = 0;
676 data.m_Parameters.m_PadTop = 1;
677 data.m_Parameters.m_PadBottom = 1;
678 data.m_Parameters.m_BiasEnabled = biasEnabled;
679
680 std::unique_ptr<armnn::IWorkload> workload = workloadFactory.CreateDepthwiseConvolution2d(data, info);
681 inputHandle->Allocate();
682 outputHandle->Allocate();
683
684 CopyDataToITensorHandle(inputHandle.get(), &input[0][0][0][0]);
685
surmeh013537c2c2018-05-18 16:31:43 +0100686 workloadFactory.Finalize();
telsoa014fcda012018-03-09 14:13:49 +0000687 workload->Execute();
688
689 CopyDataFromITensorHandle(&ret.output[0][0][0][0], outputHandle.get());
690
691 return ret;
692}
693
Nikhil Rajcec6b652018-10-12 13:51:57 +0100694template<typename T, typename B>
695LayerTestResult<T, 4> DepthwiseConvolution2dNhwcTestImpl(armnn::IWorkloadFactory& workloadFactory,
696 const boost::multi_array<T, 4>& input,
697 const boost::multi_array<T, 4>& kernel,
698 const boost::multi_array<B, 1>& bias,
699 const boost::multi_array<T, 4>& outputExpected,
700 float qScale,
701 int32_t qOffset,
702 uint32_t padLeft = 0,
703 uint32_t padTop = 0,
704 uint32_t padRight = 0,
705 uint32_t padBottom = 0,
706 uint32_t strideX = 1,
707 uint32_t strideY = 1)
708{
709 unsigned int inputNum = boost::numeric_cast<unsigned int>(input.shape()[0]);
710 unsigned int inputChannels = boost::numeric_cast<unsigned int>(input.shape()[3]);
711 unsigned int inputHeight = boost::numeric_cast<unsigned int>(input.shape()[1]);
712 unsigned int inputWidth = boost::numeric_cast<unsigned int>(input.shape()[2]);
713
714 unsigned int kernelChanMul = boost::numeric_cast<unsigned int>(kernel.shape()[0]);
715 unsigned int kernelChannels = boost::numeric_cast<unsigned int>(kernel.shape()[3]);
716 unsigned int kernelHeight = boost::numeric_cast<unsigned int>(kernel.shape()[1]);
717 unsigned int kernelWidth = boost::numeric_cast<unsigned int>(kernel.shape()[2]);
718
719 unsigned int outputNum = boost::numeric_cast<unsigned int>(outputExpected.shape()[0]);
720 unsigned int outputChannels = boost::numeric_cast<unsigned int>(outputExpected.shape()[3]);
721 unsigned int outputHeight = boost::numeric_cast<unsigned int>(outputExpected.shape()[1]);
722 unsigned int outputWidth = boost::numeric_cast<unsigned int>(outputExpected.shape()[2]);
723
724 // Creates the tensors.
725 armnn::TensorInfo inputTensorInfo({inputNum, inputHeight, inputWidth, inputChannels}, armnn::GetDataType<T>());
726 armnn::TensorInfo outputTensorInfo({outputNum, outputHeight, outputWidth, outputChannels},
727 armnn::GetDataType<T>());
728 armnn::TensorInfo kernelDesc({kernelChanMul, kernelHeight, kernelWidth, kernelChannels}, armnn::GetDataType<T>());
729 armnn::TensorInfo biasDesc({static_cast<unsigned int>(bias.size())}, armnn::GetDataType<B>());
730
731 // Set quantization parameters if the requested type is a quantized type.
732 if (armnn::IsQuantizedType<T>())
733 {
734 inputTensorInfo.SetQuantizationScale(qScale);
735 inputTensorInfo.SetQuantizationOffset(qOffset);
736 outputTensorInfo.SetQuantizationScale(qScale);
737 outputTensorInfo.SetQuantizationOffset(qOffset);
738 kernelDesc.SetQuantizationScale(qScale);
739 kernelDesc.SetQuantizationOffset(qOffset);
740 biasDesc.SetQuantizationScale(qScale*qScale);
741 biasDesc.SetQuantizationOffset(0);
742 }
743
744 // Construct the input data.
745 std::vector<T> inputData;
746 inputData.assign(input.data(), input.data() + inputHeight*inputWidth*inputChannels);
747 auto batchedInput = MakeTensor<T, 4>(inputTensorInfo, inputData);
748
749 // Construct the output data, with bias applied, as appropriate.
750 std::vector<T> outputData;
751 outputData.assign(outputExpected.data(), outputExpected.data() + outputHeight*outputWidth*outputChannels);
752
753 LayerTestResult<T, 4> ret(outputTensorInfo);
754 ret.outputExpected = MakeTensor<T, 4>(outputTensorInfo, outputData);
755
756 std::unique_ptr<armnn::ITensorHandle> inputHandle = workloadFactory.CreateTensorHandle(inputTensorInfo);
757 std::unique_ptr<armnn::ITensorHandle> outputHandle = workloadFactory.CreateTensorHandle(outputTensorInfo);
758
759 armnn::ScopedCpuTensorHandle weightsTensor(kernelDesc);
760 AllocateAndCopyDataToITensorHandle(&weightsTensor, &kernel[0][0][0][0]);
761
762 armnn::ScopedCpuTensorHandle biasTensor(biasDesc);
763
764 armnn::DepthwiseConvolution2dQueueDescriptor data;
765 data.m_Weight = &weightsTensor;
766 data.m_Bias = &biasTensor; // Still set this whether or not bias is enabled - it can be a source of bugs.
767 data.m_Parameters.m_StrideX = strideX;
768 data.m_Parameters.m_StrideY = strideY;
769 data.m_Parameters.m_PadLeft = padLeft;
770 data.m_Parameters.m_PadRight = padRight;
771 data.m_Parameters.m_PadTop = padTop;
772 data.m_Parameters.m_PadBottom = padBottom;
773 data.m_Parameters.m_DataLayout = armnn::DataLayout::NHWC;
774
775 armnn::WorkloadInfo info;
776 AddInputToWorkload(data, info, inputTensorInfo, inputHandle.get());
777 AddOutputToWorkload(data, info, outputTensorInfo, outputHandle.get());
778
779 std::unique_ptr<armnn::IWorkload> workload = workloadFactory.CreateDepthwiseConvolution2d(data, info);
780
781 inputHandle->Allocate();
782 outputHandle->Allocate();
783
784 CopyDataToITensorHandle(inputHandle.get(), &batchedInput[0][0][0][0]);
785
786 workloadFactory.Finalize();
787 workload->Execute();
788
789 CopyDataFromITensorHandle(&ret.output[0][0][0][0], outputHandle.get());
790
791 return ret;
792}
793
telsoa014fcda012018-03-09 14:13:49 +0000794template<typename T>
795LayerTestResult<T,4> Convolution1dTestImpl(armnn::IWorkloadFactory& workloadFactory,
796 float qScale,
797 int32_t qOffset,
798 bool biasEnabled)
799{
800 using B = typename FullyConnectedBiasTypeForInputType<T>::Type;
801
telsoa01c577f2c2018-08-31 09:22:23 +0100802 // Until we have a specialist 1D convolution layer, we can fake one using
telsoa014fcda012018-03-09 14:13:49 +0000803 // 2D convolution with the final dimension set to 1.
804 // I don't anticipate this being particularly slow, given that convolution is implemented
805 // as a matrix multiplication, at which point dimension doesn't matter.
806
807 unsigned int batchSize = 1;
808 unsigned int inputChannels = 2;
809 unsigned int outputChannels = 3;
telsoa01c577f2c2018-08-31 09:22:23 +0100810 unsigned int inputSize = 5; // The 1D size (could view as 'width' or 'height').
telsoa014fcda012018-03-09 14:13:49 +0000811 unsigned int kernelSize = 3;
812 unsigned int padSize = 2;
813 unsigned int stride = 1;
telsoa01c577f2c2018-08-31 09:22:23 +0100814 unsigned int outputSize = 7; // (inputSize + 2 * padSize - kernelSize + 1) / stride.
telsoa014fcda012018-03-09 14:13:49 +0000815
816 armnn::TensorInfo inputInfo({batchSize, inputChannels, inputSize, 1}, armnn::GetDataType<T>());
817 armnn::TensorInfo outputInfo({batchSize, outputChannels, outputSize, 1}, armnn::GetDataType<T>());
818 armnn::TensorInfo kernelInfo({outputChannels, inputChannels, kernelSize, 1}, armnn::GetDataType<T>());
819 armnn::TensorInfo biasInfo({outputChannels}, armnn::GetDataType<B>());
820
821 // Set quantization parameters if the requested type is a quantized type.
822 if(armnn::IsQuantizedType<T>())
823 {
824 inputInfo.SetQuantizationScale(qScale);
825 inputInfo.SetQuantizationOffset(qOffset);
826 outputInfo.SetQuantizationScale(qScale);
827 outputInfo.SetQuantizationOffset(qOffset);
828 kernelInfo.SetQuantizationScale(qScale);
829 kernelInfo.SetQuantizationOffset(qOffset);
830 biasInfo.SetQuantizationScale(inputInfo.GetQuantizationScale()*kernelInfo.GetQuantizationScale());
831 biasInfo.SetQuantizationOffset(0);
832 }
833
834 std::vector<T> inputData(
835 QuantizedVector<T>(inputInfo.GetQuantizationScale(), inputInfo.GetQuantizationOffset(), {
836 5.0f, -2.0f, 2.5f, 0.0f, 1.0f,
837 -3.0f, 3.2f, 5.0f, 2.0f, 3.0f,
838 }));
839
840 std::vector<T> kernelData(
841 QuantizedVector<T>(kernelInfo.GetQuantizationScale(), kernelInfo.GetQuantizationOffset(), {
842 1.0f, 0.0f, 0.0f,
843 0.0f, 2.0f, -1.5f,
844
845 0.0f, 0.0f, 0.0f,
846 0.2f, 0.2f, 0.2f,
847
848 0.5f, 0.0f, 0.5f,
849 0.0f, -1.0f, 0.0f
850 }));
851
852 std::vector<B> biasData(
853 QuantizedVector<B>(biasInfo.GetQuantizationScale(), biasInfo.GetQuantizationOffset(), {
854 1.0f, 0.0f, 0.0f
855 }));
856
857 std::vector<T> outputData(
858 QuantizedVector<T>(outputInfo.GetQuantizationScale(), outputInfo.GetQuantizationOffset(), {
859 4.5f, -10.8f, 5.0f + 6.4f - 7.5f, -2.0f + 10.0f -3.0f, 2.5f + 4.0f - 4.5f, 6.0f, 1.0f,
860 -0.6f, -0.6f + 0.64f, -0.6f + 0.64f + 1.0f, 0.64f + 1.0f + 0.4f, 1.0f + 0.4f + 0.6f, 0.4f + 0.6f, 0.6f,
861 2.5f, -1.0f + 3.0f, 1.25f - 3.2f + 2.5f, -1.0f - 5.0f, 1.25f + 0.5f - 2.0f, -3.0f, 0.5f
862 }));
863
telsoa01c577f2c2018-08-31 09:22:23 +0100864 // Optionally apply bias to output image.
telsoa014fcda012018-03-09 14:13:49 +0000865 if(biasEnabled)
866 {
867 ApplyBias(outputData, outputInfo.GetQuantizationScale(), outputInfo.GetQuantizationOffset(),
868 biasData, biasInfo.GetQuantizationScale(), biasInfo.GetQuantizationOffset(),
869 1, outputSize);
870 }
871
872 std::unique_ptr<armnn::ITensorHandle> inputHandle = workloadFactory.CreateTensorHandle(inputInfo);
873 std::unique_ptr<armnn::ITensorHandle> outputHandle = workloadFactory.CreateTensorHandle(outputInfo);
874
875 armnn::Convolution2dQueueDescriptor data;
876 armnn::WorkloadInfo info;
877 armnn::ScopedCpuTensorHandle weightsTensor(kernelInfo);
878 armnn::ScopedCpuTensorHandle biasTensor(biasInfo);
879
880 AllocateAndCopyDataToITensorHandle(&weightsTensor, kernelData.data());
881 AllocateAndCopyDataToITensorHandle(&biasTensor, biasData.data());
882
883 AddInputToWorkload(data, info, inputInfo, inputHandle.get());
884 AddOutputToWorkload(data, info, outputInfo, outputHandle.get());
885
886 data.m_Weight = &weightsTensor;
887 data.m_Bias = &biasTensor;
888 data.m_Parameters.m_StrideX = 1;
889 data.m_Parameters.m_StrideY = stride;
890 data.m_Parameters.m_PadLeft = 0;
891 data.m_Parameters.m_PadRight = 0;
892 data.m_Parameters.m_PadTop = padSize;
893 data.m_Parameters.m_PadBottom = padSize;
894 data.m_Parameters.m_BiasEnabled = biasEnabled;
895
896 std::unique_ptr<armnn::IWorkload> workload = workloadFactory.CreateConvolution2d(data, info);
897 inputHandle->Allocate();
898 outputHandle->Allocate();
899
900 CopyDataToITensorHandle(inputHandle.get(), inputData.data());
901
surmeh013537c2c2018-05-18 16:31:43 +0100902 workloadFactory.Finalize();
telsoa014fcda012018-03-09 14:13:49 +0000903 workload->Execute();
904
telsoa01c577f2c2018-08-31 09:22:23 +0100905 // Output
telsoa014fcda012018-03-09 14:13:49 +0000906 LayerTestResult<T,4> ret(outputInfo);
907 CopyDataFromITensorHandle(&ret.output[0][0][0][0], outputHandle.get());
908 ret.outputExpected = MakeTensor<T, 4>(outputInfo, outputData);
909 return ret;
910}
911
912
913
914template<typename T>
915LayerTestResult<T,4> CompareConvolution2dTestImpl(armnn::IWorkloadFactory& workloadFactory,
916 armnn::IWorkloadFactory& refWorkloadFactory)
917{
918 unsigned int inputHeight = 8;
919 unsigned int inputWidth = 16;
920 unsigned int inputChannels = 3;
921 unsigned int inputNum = 5;
922
923 unsigned int kernelHeight = 3;
924 unsigned int kernelWidth = 3;
925
926 unsigned int strideX = 2;
927 unsigned int strideY = 3;
928 unsigned int padX = 1;
929 unsigned int padY = 1;
930
931 unsigned int outputNum = inputNum;
932 unsigned int outputChannels = 2;
933 unsigned int outputHeight = (inputHeight + 2 * padY - kernelHeight + strideY) / strideY;
934 unsigned int outputWidth = (inputWidth + 2 * padX - kernelWidth + strideX) / strideX;
935
936 armnn::TensorInfo inputTensorInfo;
937 armnn::TensorInfo outputTensorInfo;
938 armnn::TensorInfo kernelDesc;
939 armnn::TensorInfo biasDesc;
940
941 unsigned int inputShape[] = {inputNum, inputChannels, inputHeight, inputWidth};
942 unsigned int outputShape[] = {outputNum, outputChannels, outputHeight, outputWidth};
943 unsigned int kernelShape[] = {outputChannels, inputChannels, kernelHeight, kernelWidth};
944 unsigned int biasShape[] = {outputChannels};
945
946 inputTensorInfo = armnn::TensorInfo(4, inputShape, armnn::GetDataType<T>());
947 outputTensorInfo = armnn::TensorInfo(4, outputShape, armnn::GetDataType<T>());
948 kernelDesc = armnn::TensorInfo(4, kernelShape, armnn::GetDataType<T>());
949 biasDesc = armnn::TensorInfo(1, biasShape, armnn::GetDataType<T>());
950
951 LayerTestResult<T,4> ret(outputTensorInfo);
952
953 auto input = MakeRandomTensor<T, 4>(inputTensorInfo, 124908);
954 auto kernel = MakeRandomTensor<T, 4>(kernelDesc, 891234);
955 auto bias = MakeRandomTensor<T, 1>(biasDesc, 1028);
956
957 std::unique_ptr<armnn::ITensorHandle> inputHandle = workloadFactory.CreateTensorHandle(inputTensorInfo);
958 std::unique_ptr<armnn::ITensorHandle> outputHandle = workloadFactory.CreateTensorHandle(outputTensorInfo);
959
960 armnn::Convolution2dQueueDescriptor data;
961 armnn::WorkloadInfo info;
962 armnn::ScopedCpuTensorHandle weightsTensor(kernelDesc);
963 armnn::ScopedCpuTensorHandle biasTensor(biasDesc);
964
965 AllocateAndCopyDataToITensorHandle(&weightsTensor, &kernel[0][0][0][0]);
966 AllocateAndCopyDataToITensorHandle(&biasTensor, &bias[0]);
967
968 AddInputToWorkload(data, info, inputTensorInfo, inputHandle.get());
969 AddOutputToWorkload(data, info, outputTensorInfo, outputHandle.get());
970 data.m_Weight = &weightsTensor;
971 data.m_Bias = &biasTensor;
972 data.m_Parameters.m_StrideX = strideX;
973 data.m_Parameters.m_StrideY = strideY;
974 data.m_Parameters.m_PadLeft = padX;
975 data.m_Parameters.m_PadRight = padX;
976 data.m_Parameters.m_PadTop = padY;
977 data.m_Parameters.m_PadBottom = padY;
978 data.m_Parameters.m_BiasEnabled = true;
979
980 std::unique_ptr<armnn::ITensorHandle> outputHandleRef = refWorkloadFactory.CreateTensorHandle(outputTensorInfo);
981 std::unique_ptr<armnn::ITensorHandle> inputHandleRef = refWorkloadFactory.CreateTensorHandle(inputTensorInfo);
982
983 armnn::Convolution2dQueueDescriptor refData = data;
984 armnn::WorkloadInfo refInfo = info;
985 SetWorkloadInput(refData, refInfo, 0, inputTensorInfo, inputHandleRef.get());
986 SetWorkloadOutput(refData, refInfo, 0, outputTensorInfo, outputHandleRef.get());
987
988 std::unique_ptr<armnn::IWorkload> workload = workloadFactory.CreateConvolution2d(data, info);
989 std::unique_ptr<armnn::IWorkload> workloadRef = refWorkloadFactory.CreateConvolution2d(refData, refInfo);
990
991 outputHandleRef->Allocate();
992 inputHandleRef->Allocate();
993
994 inputHandle->Allocate();
995 outputHandle->Allocate();
996
997 CopyDataToITensorHandle(inputHandle.get(), &input[0][0][0][0]);
998 CopyDataToITensorHandle(inputHandleRef.get(), &input[0][0][0][0]);
999
surmeh013537c2c2018-05-18 16:31:43 +01001000 workloadFactory.Finalize();
telsoa014fcda012018-03-09 14:13:49 +00001001 workload->Execute();
surmeh013537c2c2018-05-18 16:31:43 +01001002 refWorkloadFactory.Finalize();
telsoa014fcda012018-03-09 14:13:49 +00001003 workloadRef->Execute();
1004
1005 CopyDataFromITensorHandle(&ret.output[0][0][0][0], outputHandle.get());
1006 CopyDataFromITensorHandle(&ret.outputExpected[0][0][0][0], outputHandleRef.get());
1007
1008 return ret;
1009}
1010
1011template<typename T>
1012LayerTestResult<T, 4> CompareDepthwiseConvolution2dTestImpl(armnn::IWorkloadFactory& workloadFactory,
1013 armnn::IWorkloadFactory& refWorkloadFactory)
1014{
1015 unsigned int inputHeight = 8;
1016 unsigned int inputWidth = 16;
1017 unsigned int inputChannels = 3;
1018 unsigned int inputNum = 5;
1019
1020 unsigned int kernelHeight = 3;
1021 unsigned int kernelWidth = 3;
1022 unsigned int channelMultiplier = 1;
1023
1024 unsigned int strideX = 2;
1025 unsigned int strideY = 3;
1026 unsigned int padX = 1;
1027 unsigned int padY = 1;
1028
1029 unsigned int outputNum = inputNum;
1030 unsigned int outputChannels = inputChannels * channelMultiplier;
1031 unsigned int outputHeight = (inputHeight + 2 * padY - kernelHeight + strideY) / strideY;
1032 unsigned int outputWidth = (inputWidth + 2 * padX - kernelWidth + strideX) / strideX;
1033
1034 armnn::TensorInfo inputTensorInfo;
1035 armnn::TensorInfo outputTensorInfo;
1036 armnn::TensorInfo kernelDesc;
1037 armnn::TensorInfo biasDesc;
1038
1039 unsigned int inputShape[] = { inputNum, inputChannels, inputHeight, inputWidth };
1040 unsigned int outputShape[] = { outputNum, outputChannels, outputHeight, outputWidth };
1041 unsigned int kernelShape[] = { channelMultiplier, inputChannels, kernelHeight, kernelWidth };
1042 unsigned int biasShape[] = { outputChannels };
1043
1044 float inputsQScale = armnn::IsQuantizedType<T>() ? 1.0f : 0;
1045 float outputQScale = armnn::IsQuantizedType<T>() ? 2.0f : 0;
1046 int32_t qOffset = 0;
1047
1048 inputTensorInfo = armnn::TensorInfo(4, inputShape, armnn::GetDataType<T>(), inputsQScale, qOffset);
1049 outputTensorInfo = armnn::TensorInfo(4, outputShape, armnn::GetDataType<T>(), outputQScale, qOffset);
1050 kernelDesc = armnn::TensorInfo(4, kernelShape, armnn::GetDataType<T>(), inputsQScale, qOffset);
1051 biasDesc = armnn::TensorInfo(1, biasShape, armnn::GetBiasDataType(armnn::GetDataType<T>()), inputsQScale, qOffset);
1052
1053 LayerTestResult<T, 4> ret(outputTensorInfo);
1054
1055 auto input = MakeRandomTensor<T, 4>(inputTensorInfo, 124908, 0.0f, 255.0f);
1056 auto kernel = MakeRandomTensor<T, 4>(kernelDesc, 891234, 0.0f, 255.0f);
1057 auto bias = MakeRandomTensor<typename FullyConnectedBiasTypeForInputType<T>::Type, 1>(biasDesc, 1028, 0.0f, 255.0f);
1058
1059 std::unique_ptr<armnn::ITensorHandle> inputHandle = workloadFactory.CreateTensorHandle(inputTensorInfo);
1060 std::unique_ptr<armnn::ITensorHandle> outputHandle = workloadFactory.CreateTensorHandle(outputTensorInfo);
1061
1062 armnn::DepthwiseConvolution2dQueueDescriptor data;
1063 armnn::WorkloadInfo info;
1064 armnn::ScopedCpuTensorHandle weightsTensor(kernelDesc);
1065 armnn::ScopedCpuTensorHandle biasTensor(biasDesc);
1066
1067 AllocateAndCopyDataToITensorHandle(&weightsTensor, &kernel[0][0][0][0]);
1068 AllocateAndCopyDataToITensorHandle(&biasTensor, &bias[0]);
1069
1070 AddInputToWorkload(data, info, inputTensorInfo, inputHandle.get());
1071 AddOutputToWorkload(data, info, outputTensorInfo, outputHandle.get());
1072 data.m_Weight = &weightsTensor;
1073 data.m_Bias = &biasTensor;
1074 data.m_Parameters.m_StrideX = strideX;
1075 data.m_Parameters.m_StrideY = strideY;
1076 data.m_Parameters.m_PadLeft = padX;
1077 data.m_Parameters.m_PadRight = padX;
1078 data.m_Parameters.m_PadTop = padY;
1079 data.m_Parameters.m_PadBottom = padY;
1080 data.m_Parameters.m_BiasEnabled = true;
1081
1082 std::unique_ptr<armnn::ITensorHandle> outputHandleRef = refWorkloadFactory.CreateTensorHandle(outputTensorInfo);
1083 std::unique_ptr<armnn::ITensorHandle> inputHandleRef = refWorkloadFactory.CreateTensorHandle(inputTensorInfo);
1084
1085 armnn::DepthwiseConvolution2dQueueDescriptor refData = data;
1086 armnn::WorkloadInfo refInfo = info;
1087 SetWorkloadInput(refData, refInfo, 0, inputTensorInfo, inputHandleRef.get());
1088 SetWorkloadOutput(refData, refInfo, 0, outputTensorInfo, outputHandleRef.get());
1089
1090 std::unique_ptr<armnn::IWorkload> workload = workloadFactory.CreateDepthwiseConvolution2d(data, info);
1091 std::unique_ptr<armnn::IWorkload> workloadRef = refWorkloadFactory.CreateDepthwiseConvolution2d(refData, refInfo);
1092
1093 outputHandleRef->Allocate();
1094 inputHandleRef->Allocate();
1095
1096 inputHandle->Allocate();
1097 outputHandle->Allocate();
1098
1099 CopyDataToITensorHandle(inputHandle.get(), &input[0][0][0][0]);
1100 CopyDataToITensorHandle(inputHandleRef.get(), &input[0][0][0][0]);
1101
surmeh013537c2c2018-05-18 16:31:43 +01001102 workloadFactory.Finalize();
telsoa014fcda012018-03-09 14:13:49 +00001103 workload->Execute();
surmeh013537c2c2018-05-18 16:31:43 +01001104 refWorkloadFactory.Finalize();
telsoa014fcda012018-03-09 14:13:49 +00001105 workloadRef->Execute();
1106
1107 CopyDataFromITensorHandle(&ret.output[0][0][0][0], outputHandle.get());
1108 CopyDataFromITensorHandle(&ret.outputExpected[0][0][0][0], outputHandleRef.get());
1109
1110 return ret;
1111}