blob: 8e29615c47e36be3cd0c7269c06a57e3c42db9f7 [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
telsoa014fcda012018-03-09 14:13:49 +0000694template<typename T>
695LayerTestResult<T,4> Convolution1dTestImpl(armnn::IWorkloadFactory& workloadFactory,
696 float qScale,
697 int32_t qOffset,
698 bool biasEnabled)
699{
700 using B = typename FullyConnectedBiasTypeForInputType<T>::Type;
701
telsoa01c577f2c2018-08-31 09:22:23 +0100702 // Until we have a specialist 1D convolution layer, we can fake one using
telsoa014fcda012018-03-09 14:13:49 +0000703 // 2D convolution with the final dimension set to 1.
704 // I don't anticipate this being particularly slow, given that convolution is implemented
705 // as a matrix multiplication, at which point dimension doesn't matter.
706
707 unsigned int batchSize = 1;
708 unsigned int inputChannels = 2;
709 unsigned int outputChannels = 3;
telsoa01c577f2c2018-08-31 09:22:23 +0100710 unsigned int inputSize = 5; // The 1D size (could view as 'width' or 'height').
telsoa014fcda012018-03-09 14:13:49 +0000711 unsigned int kernelSize = 3;
712 unsigned int padSize = 2;
713 unsigned int stride = 1;
telsoa01c577f2c2018-08-31 09:22:23 +0100714 unsigned int outputSize = 7; // (inputSize + 2 * padSize - kernelSize + 1) / stride.
telsoa014fcda012018-03-09 14:13:49 +0000715
716 armnn::TensorInfo inputInfo({batchSize, inputChannels, inputSize, 1}, armnn::GetDataType<T>());
717 armnn::TensorInfo outputInfo({batchSize, outputChannels, outputSize, 1}, armnn::GetDataType<T>());
718 armnn::TensorInfo kernelInfo({outputChannels, inputChannels, kernelSize, 1}, armnn::GetDataType<T>());
719 armnn::TensorInfo biasInfo({outputChannels}, armnn::GetDataType<B>());
720
721 // Set quantization parameters if the requested type is a quantized type.
722 if(armnn::IsQuantizedType<T>())
723 {
724 inputInfo.SetQuantizationScale(qScale);
725 inputInfo.SetQuantizationOffset(qOffset);
726 outputInfo.SetQuantizationScale(qScale);
727 outputInfo.SetQuantizationOffset(qOffset);
728 kernelInfo.SetQuantizationScale(qScale);
729 kernelInfo.SetQuantizationOffset(qOffset);
730 biasInfo.SetQuantizationScale(inputInfo.GetQuantizationScale()*kernelInfo.GetQuantizationScale());
731 biasInfo.SetQuantizationOffset(0);
732 }
733
734 std::vector<T> inputData(
735 QuantizedVector<T>(inputInfo.GetQuantizationScale(), inputInfo.GetQuantizationOffset(), {
736 5.0f, -2.0f, 2.5f, 0.0f, 1.0f,
737 -3.0f, 3.2f, 5.0f, 2.0f, 3.0f,
738 }));
739
740 std::vector<T> kernelData(
741 QuantizedVector<T>(kernelInfo.GetQuantizationScale(), kernelInfo.GetQuantizationOffset(), {
742 1.0f, 0.0f, 0.0f,
743 0.0f, 2.0f, -1.5f,
744
745 0.0f, 0.0f, 0.0f,
746 0.2f, 0.2f, 0.2f,
747
748 0.5f, 0.0f, 0.5f,
749 0.0f, -1.0f, 0.0f
750 }));
751
752 std::vector<B> biasData(
753 QuantizedVector<B>(biasInfo.GetQuantizationScale(), biasInfo.GetQuantizationOffset(), {
754 1.0f, 0.0f, 0.0f
755 }));
756
757 std::vector<T> outputData(
758 QuantizedVector<T>(outputInfo.GetQuantizationScale(), outputInfo.GetQuantizationOffset(), {
759 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,
760 -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,
761 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
762 }));
763
telsoa01c577f2c2018-08-31 09:22:23 +0100764 // Optionally apply bias to output image.
telsoa014fcda012018-03-09 14:13:49 +0000765 if(biasEnabled)
766 {
767 ApplyBias(outputData, outputInfo.GetQuantizationScale(), outputInfo.GetQuantizationOffset(),
768 biasData, biasInfo.GetQuantizationScale(), biasInfo.GetQuantizationOffset(),
769 1, outputSize);
770 }
771
772 std::unique_ptr<armnn::ITensorHandle> inputHandle = workloadFactory.CreateTensorHandle(inputInfo);
773 std::unique_ptr<armnn::ITensorHandle> outputHandle = workloadFactory.CreateTensorHandle(outputInfo);
774
775 armnn::Convolution2dQueueDescriptor data;
776 armnn::WorkloadInfo info;
777 armnn::ScopedCpuTensorHandle weightsTensor(kernelInfo);
778 armnn::ScopedCpuTensorHandle biasTensor(biasInfo);
779
780 AllocateAndCopyDataToITensorHandle(&weightsTensor, kernelData.data());
781 AllocateAndCopyDataToITensorHandle(&biasTensor, biasData.data());
782
783 AddInputToWorkload(data, info, inputInfo, inputHandle.get());
784 AddOutputToWorkload(data, info, outputInfo, outputHandle.get());
785
786 data.m_Weight = &weightsTensor;
787 data.m_Bias = &biasTensor;
788 data.m_Parameters.m_StrideX = 1;
789 data.m_Parameters.m_StrideY = stride;
790 data.m_Parameters.m_PadLeft = 0;
791 data.m_Parameters.m_PadRight = 0;
792 data.m_Parameters.m_PadTop = padSize;
793 data.m_Parameters.m_PadBottom = padSize;
794 data.m_Parameters.m_BiasEnabled = biasEnabled;
795
796 std::unique_ptr<armnn::IWorkload> workload = workloadFactory.CreateConvolution2d(data, info);
797 inputHandle->Allocate();
798 outputHandle->Allocate();
799
800 CopyDataToITensorHandle(inputHandle.get(), inputData.data());
801
surmeh013537c2c2018-05-18 16:31:43 +0100802 workloadFactory.Finalize();
telsoa014fcda012018-03-09 14:13:49 +0000803 workload->Execute();
804
telsoa01c577f2c2018-08-31 09:22:23 +0100805 // Output
telsoa014fcda012018-03-09 14:13:49 +0000806 LayerTestResult<T,4> ret(outputInfo);
807 CopyDataFromITensorHandle(&ret.output[0][0][0][0], outputHandle.get());
808 ret.outputExpected = MakeTensor<T, 4>(outputInfo, outputData);
809 return ret;
810}
811
812
813
814template<typename T>
815LayerTestResult<T,4> CompareConvolution2dTestImpl(armnn::IWorkloadFactory& workloadFactory,
816 armnn::IWorkloadFactory& refWorkloadFactory)
817{
818 unsigned int inputHeight = 8;
819 unsigned int inputWidth = 16;
820 unsigned int inputChannels = 3;
821 unsigned int inputNum = 5;
822
823 unsigned int kernelHeight = 3;
824 unsigned int kernelWidth = 3;
825
826 unsigned int strideX = 2;
827 unsigned int strideY = 3;
828 unsigned int padX = 1;
829 unsigned int padY = 1;
830
831 unsigned int outputNum = inputNum;
832 unsigned int outputChannels = 2;
833 unsigned int outputHeight = (inputHeight + 2 * padY - kernelHeight + strideY) / strideY;
834 unsigned int outputWidth = (inputWidth + 2 * padX - kernelWidth + strideX) / strideX;
835
836 armnn::TensorInfo inputTensorInfo;
837 armnn::TensorInfo outputTensorInfo;
838 armnn::TensorInfo kernelDesc;
839 armnn::TensorInfo biasDesc;
840
841 unsigned int inputShape[] = {inputNum, inputChannels, inputHeight, inputWidth};
842 unsigned int outputShape[] = {outputNum, outputChannels, outputHeight, outputWidth};
843 unsigned int kernelShape[] = {outputChannels, inputChannels, kernelHeight, kernelWidth};
844 unsigned int biasShape[] = {outputChannels};
845
846 inputTensorInfo = armnn::TensorInfo(4, inputShape, armnn::GetDataType<T>());
847 outputTensorInfo = armnn::TensorInfo(4, outputShape, armnn::GetDataType<T>());
848 kernelDesc = armnn::TensorInfo(4, kernelShape, armnn::GetDataType<T>());
849 biasDesc = armnn::TensorInfo(1, biasShape, armnn::GetDataType<T>());
850
851 LayerTestResult<T,4> ret(outputTensorInfo);
852
853 auto input = MakeRandomTensor<T, 4>(inputTensorInfo, 124908);
854 auto kernel = MakeRandomTensor<T, 4>(kernelDesc, 891234);
855 auto bias = MakeRandomTensor<T, 1>(biasDesc, 1028);
856
857 std::unique_ptr<armnn::ITensorHandle> inputHandle = workloadFactory.CreateTensorHandle(inputTensorInfo);
858 std::unique_ptr<armnn::ITensorHandle> outputHandle = workloadFactory.CreateTensorHandle(outputTensorInfo);
859
860 armnn::Convolution2dQueueDescriptor data;
861 armnn::WorkloadInfo info;
862 armnn::ScopedCpuTensorHandle weightsTensor(kernelDesc);
863 armnn::ScopedCpuTensorHandle biasTensor(biasDesc);
864
865 AllocateAndCopyDataToITensorHandle(&weightsTensor, &kernel[0][0][0][0]);
866 AllocateAndCopyDataToITensorHandle(&biasTensor, &bias[0]);
867
868 AddInputToWorkload(data, info, inputTensorInfo, inputHandle.get());
869 AddOutputToWorkload(data, info, outputTensorInfo, outputHandle.get());
870 data.m_Weight = &weightsTensor;
871 data.m_Bias = &biasTensor;
872 data.m_Parameters.m_StrideX = strideX;
873 data.m_Parameters.m_StrideY = strideY;
874 data.m_Parameters.m_PadLeft = padX;
875 data.m_Parameters.m_PadRight = padX;
876 data.m_Parameters.m_PadTop = padY;
877 data.m_Parameters.m_PadBottom = padY;
878 data.m_Parameters.m_BiasEnabled = true;
879
880 std::unique_ptr<armnn::ITensorHandle> outputHandleRef = refWorkloadFactory.CreateTensorHandle(outputTensorInfo);
881 std::unique_ptr<armnn::ITensorHandle> inputHandleRef = refWorkloadFactory.CreateTensorHandle(inputTensorInfo);
882
883 armnn::Convolution2dQueueDescriptor refData = data;
884 armnn::WorkloadInfo refInfo = info;
885 SetWorkloadInput(refData, refInfo, 0, inputTensorInfo, inputHandleRef.get());
886 SetWorkloadOutput(refData, refInfo, 0, outputTensorInfo, outputHandleRef.get());
887
888 std::unique_ptr<armnn::IWorkload> workload = workloadFactory.CreateConvolution2d(data, info);
889 std::unique_ptr<armnn::IWorkload> workloadRef = refWorkloadFactory.CreateConvolution2d(refData, refInfo);
890
891 outputHandleRef->Allocate();
892 inputHandleRef->Allocate();
893
894 inputHandle->Allocate();
895 outputHandle->Allocate();
896
897 CopyDataToITensorHandle(inputHandle.get(), &input[0][0][0][0]);
898 CopyDataToITensorHandle(inputHandleRef.get(), &input[0][0][0][0]);
899
surmeh013537c2c2018-05-18 16:31:43 +0100900 workloadFactory.Finalize();
telsoa014fcda012018-03-09 14:13:49 +0000901 workload->Execute();
surmeh013537c2c2018-05-18 16:31:43 +0100902 refWorkloadFactory.Finalize();
telsoa014fcda012018-03-09 14:13:49 +0000903 workloadRef->Execute();
904
905 CopyDataFromITensorHandle(&ret.output[0][0][0][0], outputHandle.get());
906 CopyDataFromITensorHandle(&ret.outputExpected[0][0][0][0], outputHandleRef.get());
907
908 return ret;
909}
910
911template<typename T>
912LayerTestResult<T, 4> CompareDepthwiseConvolution2dTestImpl(armnn::IWorkloadFactory& workloadFactory,
913 armnn::IWorkloadFactory& refWorkloadFactory)
914{
915 unsigned int inputHeight = 8;
916 unsigned int inputWidth = 16;
917 unsigned int inputChannels = 3;
918 unsigned int inputNum = 5;
919
920 unsigned int kernelHeight = 3;
921 unsigned int kernelWidth = 3;
922 unsigned int channelMultiplier = 1;
923
924 unsigned int strideX = 2;
925 unsigned int strideY = 3;
926 unsigned int padX = 1;
927 unsigned int padY = 1;
928
929 unsigned int outputNum = inputNum;
930 unsigned int outputChannels = inputChannels * channelMultiplier;
931 unsigned int outputHeight = (inputHeight + 2 * padY - kernelHeight + strideY) / strideY;
932 unsigned int outputWidth = (inputWidth + 2 * padX - kernelWidth + strideX) / strideX;
933
934 armnn::TensorInfo inputTensorInfo;
935 armnn::TensorInfo outputTensorInfo;
936 armnn::TensorInfo kernelDesc;
937 armnn::TensorInfo biasDesc;
938
939 unsigned int inputShape[] = { inputNum, inputChannels, inputHeight, inputWidth };
940 unsigned int outputShape[] = { outputNum, outputChannels, outputHeight, outputWidth };
941 unsigned int kernelShape[] = { channelMultiplier, inputChannels, kernelHeight, kernelWidth };
942 unsigned int biasShape[] = { outputChannels };
943
944 float inputsQScale = armnn::IsQuantizedType<T>() ? 1.0f : 0;
945 float outputQScale = armnn::IsQuantizedType<T>() ? 2.0f : 0;
946 int32_t qOffset = 0;
947
948 inputTensorInfo = armnn::TensorInfo(4, inputShape, armnn::GetDataType<T>(), inputsQScale, qOffset);
949 outputTensorInfo = armnn::TensorInfo(4, outputShape, armnn::GetDataType<T>(), outputQScale, qOffset);
950 kernelDesc = armnn::TensorInfo(4, kernelShape, armnn::GetDataType<T>(), inputsQScale, qOffset);
951 biasDesc = armnn::TensorInfo(1, biasShape, armnn::GetBiasDataType(armnn::GetDataType<T>()), inputsQScale, qOffset);
952
953 LayerTestResult<T, 4> ret(outputTensorInfo);
954
955 auto input = MakeRandomTensor<T, 4>(inputTensorInfo, 124908, 0.0f, 255.0f);
956 auto kernel = MakeRandomTensor<T, 4>(kernelDesc, 891234, 0.0f, 255.0f);
957 auto bias = MakeRandomTensor<typename FullyConnectedBiasTypeForInputType<T>::Type, 1>(biasDesc, 1028, 0.0f, 255.0f);
958
959 std::unique_ptr<armnn::ITensorHandle> inputHandle = workloadFactory.CreateTensorHandle(inputTensorInfo);
960 std::unique_ptr<armnn::ITensorHandle> outputHandle = workloadFactory.CreateTensorHandle(outputTensorInfo);
961
962 armnn::DepthwiseConvolution2dQueueDescriptor data;
963 armnn::WorkloadInfo info;
964 armnn::ScopedCpuTensorHandle weightsTensor(kernelDesc);
965 armnn::ScopedCpuTensorHandle biasTensor(biasDesc);
966
967 AllocateAndCopyDataToITensorHandle(&weightsTensor, &kernel[0][0][0][0]);
968 AllocateAndCopyDataToITensorHandle(&biasTensor, &bias[0]);
969
970 AddInputToWorkload(data, info, inputTensorInfo, inputHandle.get());
971 AddOutputToWorkload(data, info, outputTensorInfo, outputHandle.get());
972 data.m_Weight = &weightsTensor;
973 data.m_Bias = &biasTensor;
974 data.m_Parameters.m_StrideX = strideX;
975 data.m_Parameters.m_StrideY = strideY;
976 data.m_Parameters.m_PadLeft = padX;
977 data.m_Parameters.m_PadRight = padX;
978 data.m_Parameters.m_PadTop = padY;
979 data.m_Parameters.m_PadBottom = padY;
980 data.m_Parameters.m_BiasEnabled = true;
981
982 std::unique_ptr<armnn::ITensorHandle> outputHandleRef = refWorkloadFactory.CreateTensorHandle(outputTensorInfo);
983 std::unique_ptr<armnn::ITensorHandle> inputHandleRef = refWorkloadFactory.CreateTensorHandle(inputTensorInfo);
984
985 armnn::DepthwiseConvolution2dQueueDescriptor refData = data;
986 armnn::WorkloadInfo refInfo = info;
987 SetWorkloadInput(refData, refInfo, 0, inputTensorInfo, inputHandleRef.get());
988 SetWorkloadOutput(refData, refInfo, 0, outputTensorInfo, outputHandleRef.get());
989
990 std::unique_ptr<armnn::IWorkload> workload = workloadFactory.CreateDepthwiseConvolution2d(data, info);
991 std::unique_ptr<armnn::IWorkload> workloadRef = refWorkloadFactory.CreateDepthwiseConvolution2d(refData, refInfo);
992
993 outputHandleRef->Allocate();
994 inputHandleRef->Allocate();
995
996 inputHandle->Allocate();
997 outputHandle->Allocate();
998
999 CopyDataToITensorHandle(inputHandle.get(), &input[0][0][0][0]);
1000 CopyDataToITensorHandle(inputHandleRef.get(), &input[0][0][0][0]);
1001
surmeh013537c2c2018-05-18 16:31:43 +01001002 workloadFactory.Finalize();
telsoa014fcda012018-03-09 14:13:49 +00001003 workload->Execute();
surmeh013537c2c2018-05-18 16:31:43 +01001004 refWorkloadFactory.Finalize();
telsoa014fcda012018-03-09 14:13:49 +00001005 workloadRef->Execute();
1006
1007 CopyDataFromITensorHandle(&ret.output[0][0][0][0], outputHandle.get());
1008 CopyDataFromITensorHandle(&ret.outputExpected[0][0][0][0], outputHandleRef.get());
1009
1010 return ret;
1011}