2 // Copyright © 2017 Arm Ltd. All rights reserved.
3 // SPDX-License-Identifier: MIT
8 #include <armnn/ArmNN.hpp>
9 #include <armnn/Tensor.hpp>
10 #include <armnn/TypesUtils.hpp>
12 #include <test/TensorHelpers.hpp>
13 #include "QuantizeHelper.hpp"
15 #include <backends/CpuTensorHandle.hpp>
16 #include <backends/WorkloadFactory.hpp>
17 #include "Permute.hpp"
18 #include <boost/numeric/conversion/cast.hpp>
20 // Mapping from input type to bias type for fully connected layers.
21 // float => float, uint8_t => int32_t
23 struct FullyConnectedBiasTypeForInputType;
26 struct FullyConnectedBiasTypeForInputType<float>
32 struct FullyConnectedBiasTypeForInputType<uint8_t>
37 // Modifies a std::vector in-place using a specified bias.
38 template<typename T, typename B>
39 void ApplyBias(std::vector<T>& v, float vScale, int32_t vOffset,
40 const std::vector<B>& bias, float bScale, int32_t bOffset, uint32_t w, uint32_t h)
42 BOOST_ASSERT_MSG((armnn::IsQuantizedType<T>() && vScale != 0.0f) || (!armnn::IsQuantizedType<T>()),
43 "Invalid type and parameter combination.");
44 BOOST_ASSERT_MSG((armnn::IsQuantizedType<B>() && bScale != 0.0f) || (!armnn::IsQuantizedType<B>()),
45 "Invalid type and parameter combination.");
47 // Note we need to dequantize and re-quantize the image value and the bias.
48 for (uint32_t i = 0; i < bias.size(); ++i)
50 float dBias = SelectiveDequantize(bias[i], bScale, bOffset);
51 for (uint32_t y = 0; y < h; ++y)
53 for (uint32_t x = 0; x < w; ++x)
55 uint32_t offset = (i * h + y) * w + x;
56 BOOST_ASSERT(offset < v.size());
57 T& outRef = v[offset];
58 float dOutput = SelectiveDequantize(outRef, vScale, vOffset);
59 outRef = SelectiveQuantize<T>(dOutput + dBias, vScale, vOffset);
65 template<typename T, typename B>
66 LayerTestResult<T, 4> SimpleConvolution2dTestImpl(armnn::IWorkloadFactory& workloadFactory,
67 const boost::multi_array<T, 4>& originalInput,
68 const boost::multi_array<T, 4>& originalKernel,
69 const boost::multi_array<B, 1>& bias,
70 const boost::multi_array<T, 4>& originalOutputExpected,
73 const armnn::DataLayoutIndexed& layout = armnn::DataLayout::NCHW,
76 uint32_t padRight = 0,
77 uint32_t padBottom = 0)
79 unsigned int inputHeight = boost::numeric_cast<unsigned int>(originalInput.shape()[2]);
80 unsigned int inputWidth = boost::numeric_cast<unsigned int>(originalInput.shape()[3]);
81 unsigned int inputChannels = boost::numeric_cast<unsigned int>(originalInput.shape()[1]);
82 unsigned int inputNum = boost::numeric_cast<unsigned int>(originalInput.shape()[0]);
84 unsigned int outputHeight = boost::numeric_cast<unsigned int>(originalOutputExpected.shape()[2]);
85 unsigned int outputWidth = boost::numeric_cast<unsigned int>(originalOutputExpected.shape()[3]);
86 unsigned int outputChannels = boost::numeric_cast<unsigned int>(originalOutputExpected.shape()[1]);
87 unsigned int outputNum = boost::numeric_cast<unsigned int>(originalOutputExpected.shape()[0]);
89 unsigned int kernelHeight = boost::numeric_cast<unsigned int>(originalKernel.shape()[2]);
90 unsigned int kernelWidth = boost::numeric_cast<unsigned int>(originalKernel.shape()[3]);
91 unsigned int kernelChannels = boost::numeric_cast<unsigned int>(originalKernel.shape()[1]);
92 unsigned int kernelDepthMul = boost::numeric_cast<unsigned int>(originalKernel.shape()[0]);
94 bool biasEnabled = bias.size() > 0;
96 // This function currently assumes 1 batch of input/output (and duplicates this into 2 batches).
97 BOOST_ASSERT(inputNum == 1);
98 BOOST_ASSERT(outputNum == 1);
100 // If a bias is used, its size must equal the number of output channels.
101 BOOST_ASSERT(!biasEnabled || bias.size() == outputChannels);
104 // Note these tensors will use two (identical) batches.
105 armnn::TensorInfo inputTensorInfo = GetTensorInfo<T>(2*inputNum, inputChannels, inputHeight, inputWidth, layout);
106 armnn::TensorInfo outputTensorInfo = GetTensorInfo<T>(
107 2*outputNum, outputChannels, outputHeight, outputWidth, layout);
108 armnn::TensorInfo kernelDesc = GetTensorInfo<T>(kernelDepthMul, kernelChannels, kernelHeight, kernelWidth, layout);
109 armnn::TensorInfo biasDesc({static_cast<unsigned int>(bias.size())}, armnn::GetDataType<B>());
111 // Set quantization parameters if the requested type is a quantized type.
112 if(armnn::IsQuantizedType<T>())
114 inputTensorInfo.SetQuantizationScale(qScale);
115 inputTensorInfo.SetQuantizationOffset(qOffset);
116 outputTensorInfo.SetQuantizationScale(qScale);
117 outputTensorInfo.SetQuantizationOffset(qOffset);
118 kernelDesc.SetQuantizationScale(qScale);
119 kernelDesc.SetQuantizationOffset(qOffset);
120 biasDesc.SetQuantizationScale(qScale*qScale);
121 biasDesc.SetQuantizationOffset(0);
124 LayerTestResult<T, 4> ret(outputTensorInfo);
126 // Construct input data - two batches of the same input image.
127 std::vector<T> inputImage;
128 inputImage.assign(originalInput.data(), originalInput.data() + 1*inputChannels*inputHeight*inputWidth);
129 std::vector<T> inputData;
130 inputData.insert(inputData.end(), inputImage.begin(), inputImage.end());
131 inputData.insert(inputData.end(), inputImage.begin(), inputImage.end());
133 // at this point if we require it permute the input data
134 const armnn::PermutationVector NCHWToNHWC = { 0, 3, 1, 2 };
135 if (layout.GetDataLayout() == armnn::DataLayout::NHWC)
137 std::vector<T> tmp(inputData.size());
138 armnnUtils::Permute(inputTensorInfo.GetShape(), NCHWToNHWC, inputData.data(), tmp.data());
142 auto batchedInput = MakeTensor<T, 4>(inputTensorInfo, inputData);
144 std::vector<T> outputImage;
145 outputImage.assign(originalOutputExpected.data(),
146 originalOutputExpected.data() + outputChannels*outputHeight*outputWidth);
148 // Apply bias to output image if it is enabled.
151 std::vector<T> biasV;
152 biasV.assign(bias.data(), bias.data() + outputChannels);
153 ApplyBias(outputImage, outputTensorInfo.GetQuantizationScale(), outputTensorInfo.GetQuantizationOffset(),
154 biasV, biasDesc.GetQuantizationScale(), biasDesc.GetQuantizationOffset(),
155 outputWidth, outputHeight);
158 // Construct expected output data - two identical images.
159 std::vector<T> outputData;
160 outputData.insert(outputData.end(), outputImage.begin(), outputImage.end());
161 outputData.insert(outputData.end(), outputImage.begin(), outputImage.end());
163 // at this point if we require it permute the expected output
164 if (layout.GetDataLayout() == armnn::DataLayout::NHWC)
166 std::vector<T> tmp(outputData.size());
167 armnnUtils::Permute(outputTensorInfo.GetShape(), NCHWToNHWC, outputData.data(), tmp.data());
170 ret.outputExpected = MakeTensor<T, 4>(outputTensorInfo, outputData);
172 // Todo: nontrivial padding and strides.
173 uint32_t strideX = 1;
174 uint32_t strideY = 1;
176 std::unique_ptr<armnn::ITensorHandle> inputHandle = workloadFactory.CreateTensorHandle(inputTensorInfo);
177 std::unique_ptr<armnn::ITensorHandle> outputHandle = workloadFactory.CreateTensorHandle(outputTensorInfo);
179 armnn::Convolution2dQueueDescriptor data;
180 armnn::WorkloadInfo info;
181 armnn::ScopedCpuTensorHandle weightsTensor(kernelDesc);
182 armnn::ScopedCpuTensorHandle biasTensor(biasDesc);
183 // Permute the kernel if necessary
184 boost::multi_array<T, 4> kernel = boost::multi_array<T, 4>(originalKernel);
185 if (layout.GetDataLayout() == armnn::DataLayout::NHWC)
187 armnnUtils::Permute(kernelDesc.GetShape(), NCHWToNHWC, originalKernel.data(), kernel.data());
189 AllocateAndCopyDataToITensorHandle(&weightsTensor, &kernel[0][0][0][0]);
193 AllocateAndCopyDataToITensorHandle(&biasTensor, &bias[0]);
196 AddInputToWorkload(data, info, inputTensorInfo, inputHandle.get());
197 AddOutputToWorkload(data, info, outputTensorInfo, outputHandle.get());
199 data.m_Weight = &weightsTensor;
200 data.m_Bias = &biasTensor; // Still set this whether or not bias is enabled - can be a source of bugs.
201 data.m_Parameters.m_StrideX = strideX;
202 data.m_Parameters.m_StrideY = strideY;
203 data.m_Parameters.m_PadLeft = padLeft;
204 data.m_Parameters.m_PadRight = padRight;
205 data.m_Parameters.m_PadTop = padTop;
206 data.m_Parameters.m_PadBottom = padBottom;
207 data.m_Parameters.m_BiasEnabled = biasEnabled;
208 data.m_Parameters.m_DataLayout = layout.GetDataLayout();
210 std::unique_ptr<armnn::IWorkload> workload = workloadFactory.CreateConvolution2d(data, info);
211 inputHandle->Allocate();
212 outputHandle->Allocate();
214 CopyDataToITensorHandle(inputHandle.get(), &batchedInput[0][0][0][0]);
216 workloadFactory.Finalize();
219 CopyDataFromITensorHandle(&ret.output[0][0][0][0], outputHandle.get());
224 template<typename T, typename B>
225 LayerTestResult<T, 4> SimpleConvolution2dNhwcTestImpl(armnn::IWorkloadFactory& workloadFactory,
226 const boost::multi_array<T, 4>& input,
227 const boost::multi_array<T, 4>& kernel,
228 const boost::multi_array<B, 1>& bias,
229 const boost::multi_array<T, 4>& outputExpected,
230 armnn::DataLayout dataLayout,
233 uint32_t padLeft = 1,
235 uint32_t padRight = 1,
236 uint32_t padBottom = 1,
237 uint32_t strideX = 1,
238 uint32_t strideY = 1)
240 unsigned int inputNum = boost::numeric_cast<unsigned int>(input.shape()[0]);
241 unsigned int inputChannels = boost::numeric_cast<unsigned int>(input.shape()[3]);
242 unsigned int inputHeight = boost::numeric_cast<unsigned int>(input.shape()[1]);
243 unsigned int inputWidth = boost::numeric_cast<unsigned int>(input.shape()[2]);
245 unsigned int kernelChanMul = boost::numeric_cast<unsigned int>(kernel.shape()[0]);
246 unsigned int kernelChannels = boost::numeric_cast<unsigned int>(kernel.shape()[3]);
247 unsigned int kernelHeight = boost::numeric_cast<unsigned int>(kernel.shape()[1]);
248 unsigned int kernelWidth = boost::numeric_cast<unsigned int>(kernel.shape()[2]);
250 unsigned int outputNum = boost::numeric_cast<unsigned int>(outputExpected.shape()[0]);
251 unsigned int outputChannels = boost::numeric_cast<unsigned int>(outputExpected.shape()[3]);
252 unsigned int outputHeight = boost::numeric_cast<unsigned int>(outputExpected.shape()[1]);
253 unsigned int outputWidth = boost::numeric_cast<unsigned int>(outputExpected.shape()[2]);
255 bool biasEnabled = bias.size() > 0;
257 // Creates the tensors.
258 armnn::TensorInfo inputTensorInfo({inputNum, inputHeight, inputWidth, inputChannels}, armnn::GetDataType<T>());
259 armnn::TensorInfo outputTensorInfo({outputNum, outputHeight, outputWidth, outputChannels},
260 armnn::GetDataType<T>());
261 armnn::TensorInfo kernelDesc({kernelChanMul, kernelHeight, kernelWidth, kernelChannels}, armnn::GetDataType<T>());
262 armnn::TensorInfo biasDesc({static_cast<unsigned int>(bias.size())}, armnn::GetDataType<B>());
264 // Construct the input data.
265 std::vector<T> inputData;
266 inputData.assign(input.data(), input.data() + inputHeight*inputWidth*inputChannels);
267 auto batchedInput = MakeTensor<T, 4>(inputTensorInfo, inputData);
269 // Construct the output data, with bias applied, as appropriate.
270 std::vector<T> outputData;
271 outputData.assign(outputExpected.data(), outputExpected.data() + outputHeight*outputWidth*outputChannels);
273 LayerTestResult<T, 4> ret(outputTensorInfo);
274 ret.outputExpected = MakeTensor<T, 4>(outputTensorInfo, outputData);
276 std::unique_ptr<armnn::ITensorHandle> inputHandle = workloadFactory.CreateTensorHandle(inputTensorInfo);
277 std::unique_ptr<armnn::ITensorHandle> outputHandle = workloadFactory.CreateTensorHandle(outputTensorInfo);
279 armnn::ScopedCpuTensorHandle weightsTensor(kernelDesc);
280 AllocateAndCopyDataToITensorHandle(&weightsTensor, &kernel[0][0][0][0]);
282 armnn::ScopedCpuTensorHandle biasTensor(biasDesc);
284 armnn::Convolution2dQueueDescriptor data;
286 data.m_Weight = &weightsTensor;
287 data.m_Bias = &biasTensor; // Still set this whether or not bias is enabled - can be a source of bugs.
288 data.m_Parameters.m_StrideX = strideX;
289 data.m_Parameters.m_StrideY = strideY;
290 data.m_Parameters.m_PadLeft = padLeft;
291 data.m_Parameters.m_PadRight = padRight;
292 data.m_Parameters.m_PadTop = padTop;
293 data.m_Parameters.m_PadBottom = padBottom;
294 data.m_Parameters.m_BiasEnabled = biasEnabled;
295 data.m_Parameters.m_DataLayout = dataLayout;
297 armnn::WorkloadInfo info;
298 AddInputToWorkload(data, info, inputTensorInfo, inputHandle.get());
299 AddOutputToWorkload(data, info, outputTensorInfo, outputHandle.get());
301 std::unique_ptr<armnn::IWorkload> workload = workloadFactory.CreateConvolution2d(data, info);
302 inputHandle->Allocate();
303 outputHandle->Allocate();
305 CopyDataToITensorHandle(inputHandle.get(), &batchedInput[0][0][0][0]);
307 workloadFactory.Finalize();
310 CopyDataFromITensorHandle(&ret.output[0][0][0][0], outputHandle.get());
315 template<typename T, typename B>
316 LayerTestResult<T, 4> DepthwiseConvolution2dAsymmetricTestImpl(armnn::IWorkloadFactory& workloadFactory,
317 const boost::multi_array<T, 4>& input,
318 const boost::multi_array<T, 4>& originalKernel,
319 const boost::multi_array<B, 1>& bias,
320 const boost::multi_array<T, 4>& outputExpected,
323 const armnn::DataLayoutIndexed& layout,
324 uint32_t padLeft = 0,
326 uint32_t padRight = 0,
327 uint32_t padBottom = 0,
328 uint32_t strideX = 1,
329 uint32_t strideY = 1)
331 unsigned int inputNum = boost::numeric_cast<unsigned int>(input.shape()[0]);
332 unsigned int inputChannels = boost::numeric_cast<unsigned int>(input.shape()[1]);
333 unsigned int inputHeight = boost::numeric_cast<unsigned int>(input.shape()[2]);
334 unsigned int inputWidth = boost::numeric_cast<unsigned int>(input.shape()[3]);
335 unsigned int kernelChanMul = boost::numeric_cast<unsigned int>(originalKernel.shape()[0]);
336 unsigned int kernelChannels = boost::numeric_cast<unsigned int>(originalKernel.shape()[1]);
337 unsigned int kernelHeight = boost::numeric_cast<unsigned int>(originalKernel.shape()[2]);
338 unsigned int kernelWidth = boost::numeric_cast<unsigned int>(originalKernel.shape()[3]);
339 unsigned int outputNum = boost::numeric_cast<unsigned int>(outputExpected.shape()[0]);
340 unsigned int outputChannels = boost::numeric_cast<unsigned int>(outputExpected.shape()[1]);
341 unsigned int outputHeight = boost::numeric_cast<unsigned int>(outputExpected.shape()[2]);
342 unsigned int outputWidth = boost::numeric_cast<unsigned int>(outputExpected.shape()[3]);
344 // If a bias is used, its size must equal the number of output channels.
345 bool biasEnabled = bias.size() > 0;
346 BOOST_ASSERT(!biasEnabled || bias.size() == outputChannels);
348 // Creates the tensors.
349 armnn::TensorInfo inputTensorInfo = GetTensorInfo<T>(inputNum, inputChannels, inputHeight, inputWidth, layout);
350 armnn::TensorInfo outputTensorInfo = GetTensorInfo<T>(outputNum, outputChannels, outputHeight, outputWidth, layout);
351 armnn::TensorInfo kernelDesc = GetTensorInfo<T>(kernelChanMul, kernelChannels, kernelHeight, kernelWidth, layout);
352 armnn::TensorInfo biasDesc({static_cast<unsigned int>(bias.size())}, armnn::GetDataType<B>());
354 // Set quantization parameters if the requested type is a quantized type.
355 if (armnn::IsQuantizedType<T>())
357 inputTensorInfo.SetQuantizationScale(qScale);
358 inputTensorInfo.SetQuantizationOffset(qOffset);
359 outputTensorInfo.SetQuantizationScale(qScale);
360 outputTensorInfo.SetQuantizationOffset(qOffset);
361 kernelDesc.SetQuantizationScale(qScale);
362 kernelDesc.SetQuantizationOffset(qOffset);
363 biasDesc.SetQuantizationScale(qScale*qScale);
364 biasDesc.SetQuantizationOffset(0);
367 // Construct the input data.
368 std::vector<T> inputData;
369 inputData.assign(input.data(), input.data() + inputChannels*inputHeight*inputWidth);
371 // At this point if we require it permute the input data
372 const armnn::PermutationVector NCHWToNHWC = { 0, 3, 1, 2 };
373 if (layout.GetDataLayout() == armnn::DataLayout::NHWC)
375 std::vector<T> tmp(inputData.size());
376 armnnUtils::Permute(inputTensorInfo.GetShape(), NCHWToNHWC, inputData.data(), tmp.data());
380 auto batchedInput = MakeTensor<T, 4>(inputTensorInfo, inputData);
382 // Construct the output data, with bias applied, as appropriate.
383 std::vector<T> outputData;
384 outputData.assign(outputExpected.data(), outputExpected.data() + outputChannels*outputHeight*outputWidth);
387 std::vector<T> biasV;
388 biasV.assign(bias.data(), bias.data() + outputChannels);
389 ApplyBias(outputData, outputTensorInfo.GetQuantizationScale(), outputTensorInfo.GetQuantizationOffset(),
390 biasV, biasDesc.GetQuantizationScale(), biasDesc.GetQuantizationOffset(),
391 outputWidth, outputHeight);
394 LayerTestResult<T, 4> ret(outputTensorInfo);
396 // At this point if we require it permute the expected output
397 if (layout.GetDataLayout() == armnn::DataLayout::NHWC)
399 std::vector<T> tmp(outputData.size());
400 armnnUtils::Permute(outputTensorInfo.GetShape(), NCHWToNHWC, outputData.data(), tmp.data());
404 ret.outputExpected = MakeTensor<T, 4>(outputTensorInfo, outputData);
406 std::unique_ptr<armnn::ITensorHandle> inputHandle = workloadFactory.CreateTensorHandle(inputTensorInfo);
407 std::unique_ptr<armnn::ITensorHandle> outputHandle = workloadFactory.CreateTensorHandle(outputTensorInfo);
409 armnn::ScopedCpuTensorHandle weightsTensor(kernelDesc);
411 // Permute the kernel if necessary
412 boost::multi_array<T, 4> kernel = boost::multi_array<T, 4>(originalKernel);
413 if (layout.GetDataLayout() == armnn::DataLayout::NHWC)
415 armnnUtils::Permute(kernelDesc.GetShape(), NCHWToNHWC, originalKernel.data(), kernel.data());
418 AllocateAndCopyDataToITensorHandle(&weightsTensor, &kernel[0][0][0][0]);
420 armnn::ScopedCpuTensorHandle biasTensor(biasDesc);
423 AllocateAndCopyDataToITensorHandle(&biasTensor, &bias[0]);
426 armnn::DepthwiseConvolution2dQueueDescriptor data;
427 data.m_Weight = &weightsTensor;
428 data.m_Bias = &biasTensor; // Still set this whether or not bias is enabled - it can be a source of bugs.
429 data.m_Parameters.m_StrideX = strideX;
430 data.m_Parameters.m_StrideY = strideY;
431 data.m_Parameters.m_PadLeft = padLeft;
432 data.m_Parameters.m_PadRight = padRight;
433 data.m_Parameters.m_PadTop = padTop;
434 data.m_Parameters.m_PadBottom = padBottom;
435 data.m_Parameters.m_BiasEnabled = biasEnabled;
436 data.m_Parameters.m_DataLayout = layout.GetDataLayout();
438 armnn::WorkloadInfo info;
439 AddInputToWorkload(data, info, inputTensorInfo, inputHandle.get());
440 AddOutputToWorkload(data, info, outputTensorInfo, outputHandle.get());
442 std::unique_ptr<armnn::IWorkload> workload = workloadFactory.CreateDepthwiseConvolution2d(data, info);
443 inputHandle->Allocate();
444 outputHandle->Allocate();
446 CopyDataToITensorHandle(inputHandle.get(), &batchedInput[0][0][0][0]);
448 workloadFactory.Finalize();
451 CopyDataFromITensorHandle(&ret.output[0][0][0][0], outputHandle.get());
456 template<typename T, typename B>
457 LayerTestResult<T, 4> DepthwiseConvolution2dDepthMul1TestImpl(armnn::IWorkloadFactory& workloadFactory,
461 const armnn::DataLayoutIndexed& layout)
463 unsigned int inputHeight = 3;
464 unsigned int inputWidth = 3;
465 unsigned int inputChannels = 2;
466 unsigned int inputNum = 1;
468 unsigned int kernelHeight = 3;
469 unsigned int kernelWidth = 3;
470 unsigned int kernelChannels = inputChannels;
472 unsigned int outputHeight = 1;
473 unsigned int outputWidth = 1;
474 unsigned int outputChannels = kernelChannels;
475 unsigned int outputNum = inputNum;
477 armnn::TensorInfo inputTensorInfo = GetTensorInfo<T>(inputNum, inputChannels, inputHeight, inputWidth, layout);
478 armnn::TensorInfo outputTensorInfo = GetTensorInfo<T>(outputNum, outputChannels, outputHeight, outputWidth, layout);
479 armnn::TensorInfo kernelDesc = GetTensorInfo<T>(1, outputChannels, kernelHeight, kernelWidth, layout);
480 armnn::TensorInfo biasDesc({ outputChannels }, armnn::GetDataType<B>());
482 // Set quantization parameters if the requested type is a quantized type.
483 if(armnn::IsQuantizedType<T>())
485 inputTensorInfo.SetQuantizationScale(qScale);
486 inputTensorInfo.SetQuantizationOffset(qOffset);
487 outputTensorInfo.SetQuantizationScale(qScale);
488 outputTensorInfo.SetQuantizationOffset(qOffset);
489 kernelDesc.SetQuantizationScale(qScale);
490 kernelDesc.SetQuantizationOffset(qOffset);
491 biasDesc.SetQuantizationScale(qScale*qScale);
492 biasDesc.SetQuantizationOffset(0);
494 std::vector<T> inputData = std::vector<T>(
495 QuantizedVector<T>(inputTensorInfo.GetQuantizationScale(), inputTensorInfo.GetQuantizationOffset(), {
504 // at this point if we require it permute the input data
505 const armnn::PermutationVector NCHWToNHWC = { 0, 3, 1, 2 };
506 if (layout.GetDataLayout() == armnn::DataLayout::NHWC)
508 std::vector<T> tmp(inputData.size());
509 armnnUtils::Permute(inputTensorInfo.GetShape(), NCHWToNHWC, inputData.data(), tmp.data());
512 auto input = MakeTensor<T, 4>(inputTensorInfo, inputData);
514 std::vector<B> biasV(QuantizedVector<B>(biasDesc.GetQuantizationScale(), biasDesc.GetQuantizationOffset(),
516 auto bias = MakeTensor<B, 1>(biasDesc, biasV);
518 std::vector<T> kernelData = std::vector<T>(
519 QuantizedVector<T>(kernelDesc.GetQuantizationScale(), kernelDesc.GetQuantizationOffset(), {
528 if (layout.GetDataLayout() == armnn::DataLayout::NHWC)
530 std::vector<T> tmp(kernelData.size());
531 armnnUtils::Permute(kernelDesc.GetShape(), NCHWToNHWC, kernelData.data(), tmp.data());
534 auto kernel = MakeTensor<T, 4>(kernelDesc, kernelData);
536 // Manually calculated.
537 std::vector<T> outputImage(
538 QuantizedVector<T>(outputTensorInfo.GetQuantizationScale(),
539 outputTensorInfo.GetQuantizationOffset(),
543 // Optionally apply bias to output image.
546 ApplyBias(outputImage, outputTensorInfo.GetQuantizationScale(), outputTensorInfo.GetQuantizationOffset(),
547 biasV, biasDesc.GetQuantizationScale(), biasDesc.GetQuantizationOffset(),
548 outputWidth, outputHeight);
551 LayerTestResult<T, 4> ret(outputTensorInfo);
552 if (layout.GetDataLayout() == armnn::DataLayout::NHWC)
554 std::vector<T> tmp(outputImage.size());
555 armnnUtils::Permute(outputTensorInfo.GetShape(), NCHWToNHWC, outputImage.data(), tmp.data());
559 ret.outputExpected = MakeTensor<T, 4>(outputTensorInfo, outputImage);
561 std::unique_ptr<armnn::ITensorHandle> inputHandle = workloadFactory.CreateTensorHandle(inputTensorInfo);
562 std::unique_ptr<armnn::ITensorHandle> outputHandle = workloadFactory.CreateTensorHandle(outputTensorInfo);
564 armnn::DepthwiseConvolution2dQueueDescriptor data;
565 armnn::WorkloadInfo info;
566 armnn::ScopedCpuTensorHandle weightsTensor(kernelDesc);
567 armnn::ScopedCpuTensorHandle biasTensor(biasDesc);
569 AllocateAndCopyDataToITensorHandle(&weightsTensor, &kernel[0][0][0][0]);
570 AllocateAndCopyDataToITensorHandle(&biasTensor, &bias[0]);
572 AddInputToWorkload(data, info, inputTensorInfo, inputHandle.get());
573 AddOutputToWorkload(data, info, outputTensorInfo, outputHandle.get());
575 data.m_Weight = &weightsTensor;
576 data.m_Bias = &biasTensor; // Still set this whether or not bias is enabled.
577 data.m_Parameters.m_StrideX = 1;
578 data.m_Parameters.m_StrideY = 1;
579 data.m_Parameters.m_PadLeft = 0;
580 data.m_Parameters.m_PadRight = 0;
581 data.m_Parameters.m_PadTop = 0;
582 data.m_Parameters.m_PadBottom = 0;
583 data.m_Parameters.m_BiasEnabled = biasEnabled;
584 data.m_Parameters.m_DataLayout = layout.GetDataLayout();
586 std::unique_ptr<armnn::IWorkload> workload = workloadFactory.CreateDepthwiseConvolution2d(data, info);
587 inputHandle->Allocate();
588 outputHandle->Allocate();
590 CopyDataToITensorHandle(inputHandle.get(), &input[0][0][0][0]);
592 workloadFactory.Finalize();
595 CopyDataFromITensorHandle(&ret.output[0][0][0][0], outputHandle.get());
600 template<typename T, typename B>
601 LayerTestResult<T, 4> DepthwiseConvolution2dTestImpl(armnn::IWorkloadFactory& workloadFactory,
605 const armnn::DataLayoutIndexed& layout)
607 unsigned int depthMultiplier = 2;
609 unsigned int inputHeight = 8;
610 unsigned int inputWidth = 16;
611 unsigned int inputChannels = 2;
612 unsigned int inputBatchSize = 1;
614 unsigned int kernelHeight = 5;
615 unsigned int kernelWidth = 3;
617 unsigned int outputHeight = inputHeight - kernelHeight + 1 + 2;
618 unsigned int outputWidth = (inputWidth - kernelWidth + 1)/2;
619 unsigned int outputChannels = inputChannels * depthMultiplier;
620 unsigned int outputBatchSize = inputBatchSize;
622 armnn::TensorInfo inputTensorInfo = GetTensorInfo<T>(
623 inputBatchSize, inputChannels, inputHeight, inputWidth, layout);
624 armnn::TensorInfo outputTensorInfo = GetTensorInfo<T>(
625 outputBatchSize, outputChannels, outputHeight, outputWidth, layout);
626 armnn::TensorInfo kernelDesc = GetTensorInfo<T>(
627 depthMultiplier, inputChannels, kernelHeight, kernelWidth, layout);
628 armnn::TensorInfo biasDesc({outputChannels}, armnn::GetDataType<B>());
630 // Set quantization parameters if the requested type is a quantized type.
631 if(armnn::IsQuantizedType<T>())
633 inputTensorInfo.SetQuantizationScale(qScale);
634 inputTensorInfo.SetQuantizationOffset(qOffset);
635 outputTensorInfo.SetQuantizationScale(qScale);
636 outputTensorInfo.SetQuantizationOffset(qOffset);
637 kernelDesc.SetQuantizationScale(qScale);
638 kernelDesc.SetQuantizationOffset(qOffset);
639 biasDesc.SetQuantizationScale(qScale*qScale);
640 biasDesc.SetQuantizationOffset(0);
643 // NOTE: originalInputData is in NCHW format
644 std::vector<T> originalInputData = std::vector<T>(
645 QuantizedVector<T>(inputTensorInfo.GetQuantizationScale(), inputTensorInfo.GetQuantizationOffset(), {
646 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,
647 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,
648 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,
649 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,
650 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,
651 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,
652 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,
653 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,
654 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
655 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
656 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
657 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
658 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
659 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
660 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
661 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
663 std::vector<T> inputData = originalInputData;
664 // at this point if we require it permute the input data
665 const armnn::PermutationVector NCHWToNHWC = { 0, 3, 1, 2 };
666 if (layout.GetDataLayout() == armnn::DataLayout::NHWC)
668 armnnUtils::Permute(inputTensorInfo.GetShape(), NCHWToNHWC, originalInputData.data(), inputData.data());
670 auto input = MakeTensor<T, 4>(inputTensorInfo, inputData);
672 std::vector<B> biasV(QuantizedVector<B>(biasDesc.GetQuantizationScale(), biasDesc.GetQuantizationOffset(),
674 auto bias = MakeTensor<B, 1>(biasDesc, biasV);
676 std::vector<T> originalKernelData = std::vector<T>(
677 QuantizedVector<T>(kernelDesc.GetQuantizationScale(), kernelDesc.GetQuantizationOffset(), {
702 std::vector<T> kernelData = originalKernelData;
703 if (layout.GetDataLayout() == armnn::DataLayout::NHWC)
705 armnnUtils::Permute(kernelDesc.GetShape(), NCHWToNHWC, originalKernelData.data(), kernelData.data());
707 auto kernel = MakeTensor<T, 4>(kernelDesc, kernelData);
709 // Manually calculated.
710 std::vector<T> originalOutputImage = std::vector<T>(
711 QuantizedVector<T>(outputTensorInfo.GetQuantizationScale(), outputTensorInfo.GetQuantizationOffset(), {
712 3.5f, 3.5f, 3.5f, 3.5f, 3.5f, 3.5f, 3.5f,
713 6.0f, 6.0f, 6.0f, 6.0f, 6.0f, 6.0f, 6.0f,
714 5.0f, 5.0f, 5.0f, 5.0f, 5.0f, 5.0f, 5.0f,
715 6.5f, 6.5f, 6.5f, 6.5f, 6.5f, 6.5f, 6.5f,
716 6.5f, 6.5f, 6.5f, 6.5f, 6.5f, 6.5f, 6.5f,
717 5.0f, 5.0f, 5.0f, 5.0f, 5.0f, 5.0f, 5.0f,
719 -0.5f, -0.5f, -0.5f, -0.5f, -0.5f, -0.5f, -0.5f,
720 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
721 -0.5f, -0.5f, -0.5f, -0.5f, -0.5f, -0.5f, -0.5f,
722 -0.5f, -0.5f, -0.5f, -0.5f, -0.5f, -0.5f, -0.5f,
723 -0.5f, -0.5f, -0.5f, -0.5f, -0.5f, -0.5f, -0.5f,
724 -0.5f, -0.5f, -0.5f, -0.5f, -0.5f, -0.5f, -0.5f,
726 8.0f, 8.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
727 10.0f, 10.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
728 10.0f, 10.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
729 10.0f, 10.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
730 10.0f, 10.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
731 8.0f, 8.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
733 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
734 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
735 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
736 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
737 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
738 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f
741 // Optionally apply bias to output image.
744 ApplyBias(originalOutputImage,
745 outputTensorInfo.GetQuantizationScale(),
746 outputTensorInfo.GetQuantizationOffset(),
748 biasDesc.GetQuantizationScale(),
749 biasDesc.GetQuantizationOffset(),
754 LayerTestResult<T, 4> ret(outputTensorInfo);
755 std::vector<T> outputImage = originalOutputImage;
756 if (layout.GetDataLayout() == armnn::DataLayout::NHWC)
758 armnnUtils::Permute(outputTensorInfo.GetShape(), NCHWToNHWC, originalOutputImage.data(), outputImage.data());
761 ret.outputExpected = MakeTensor<T, 4>(outputTensorInfo, outputImage);
763 std::unique_ptr<armnn::ITensorHandle> inputHandle = workloadFactory.CreateTensorHandle(inputTensorInfo);
764 std::unique_ptr<armnn::ITensorHandle> outputHandle = workloadFactory.CreateTensorHandle(outputTensorInfo);
766 armnn::DepthwiseConvolution2dQueueDescriptor data;
767 armnn::WorkloadInfo info;
768 armnn::ScopedCpuTensorHandle weightsTensor(kernelDesc);
769 armnn::ScopedCpuTensorHandle biasTensor(biasDesc);
771 AllocateAndCopyDataToITensorHandle(&weightsTensor, &kernel[0][0][0][0]);
772 AllocateAndCopyDataToITensorHandle(&biasTensor, &bias[0]);
774 AddInputToWorkload(data, info, inputTensorInfo, inputHandle.get());
775 AddOutputToWorkload(data, info, outputTensorInfo, outputHandle.get());
777 data.m_Weight = &weightsTensor;
778 data.m_Bias = &biasTensor; // Still set this whether or not bias is enabled.
779 data.m_Parameters.m_StrideX = 2;
780 data.m_Parameters.m_StrideY = 1;
781 data.m_Parameters.m_PadLeft = 0;
782 data.m_Parameters.m_PadRight = 0;
783 data.m_Parameters.m_PadTop = 1;
784 data.m_Parameters.m_PadBottom = 1;
785 data.m_Parameters.m_BiasEnabled = biasEnabled;
786 data.m_Parameters.m_DataLayout = layout.GetDataLayout();
788 std::unique_ptr<armnn::IWorkload> workload = workloadFactory.CreateDepthwiseConvolution2d(data, info);
789 inputHandle->Allocate();
790 outputHandle->Allocate();
792 CopyDataToITensorHandle(inputHandle.get(), &input[0][0][0][0]);
794 workloadFactory.Finalize();
797 CopyDataFromITensorHandle(&ret.output[0][0][0][0], outputHandle.get());
802 template<typename T, typename B>
803 LayerTestResult<T, 4> DepthwiseConvolution2dNhwcTestImpl(armnn::IWorkloadFactory& workloadFactory,
804 const boost::multi_array<T, 4>& input,
805 const boost::multi_array<T, 4>& kernel,
806 const boost::multi_array<B, 1>& bias,
807 const boost::multi_array<T, 4>& outputExpected,
810 uint32_t padLeft = 0,
812 uint32_t padRight = 0,
813 uint32_t padBottom = 0,
814 uint32_t strideX = 1,
815 uint32_t strideY = 1)
817 unsigned int inputNum = boost::numeric_cast<unsigned int>(input.shape()[0]);
818 unsigned int inputChannels = boost::numeric_cast<unsigned int>(input.shape()[3]);
819 unsigned int inputHeight = boost::numeric_cast<unsigned int>(input.shape()[1]);
820 unsigned int inputWidth = boost::numeric_cast<unsigned int>(input.shape()[2]);
822 unsigned int kernelChanMul = boost::numeric_cast<unsigned int>(kernel.shape()[0]);
823 unsigned int kernelChannels = boost::numeric_cast<unsigned int>(kernel.shape()[3]);
824 unsigned int kernelHeight = boost::numeric_cast<unsigned int>(kernel.shape()[1]);
825 unsigned int kernelWidth = boost::numeric_cast<unsigned int>(kernel.shape()[2]);
827 unsigned int outputNum = boost::numeric_cast<unsigned int>(outputExpected.shape()[0]);
828 unsigned int outputChannels = boost::numeric_cast<unsigned int>(outputExpected.shape()[3]);
829 unsigned int outputHeight = boost::numeric_cast<unsigned int>(outputExpected.shape()[1]);
830 unsigned int outputWidth = boost::numeric_cast<unsigned int>(outputExpected.shape()[2]);
832 // Creates the tensors.
833 armnn::TensorInfo inputTensorInfo({inputNum, inputHeight, inputWidth, inputChannels}, armnn::GetDataType<T>());
834 armnn::TensorInfo outputTensorInfo({outputNum, outputHeight, outputWidth, outputChannels},
835 armnn::GetDataType<T>());
836 armnn::TensorInfo kernelDesc({kernelChanMul, kernelHeight, kernelWidth, kernelChannels}, armnn::GetDataType<T>());
837 armnn::TensorInfo biasDesc({static_cast<unsigned int>(bias.size())}, armnn::GetDataType<B>());
839 // Set quantization parameters if the requested type is a quantized type.
840 if (armnn::IsQuantizedType<T>())
842 inputTensorInfo.SetQuantizationScale(qScale);
843 inputTensorInfo.SetQuantizationOffset(qOffset);
844 outputTensorInfo.SetQuantizationScale(qScale);
845 outputTensorInfo.SetQuantizationOffset(qOffset);
846 kernelDesc.SetQuantizationScale(qScale);
847 kernelDesc.SetQuantizationOffset(qOffset);
848 biasDesc.SetQuantizationScale(qScale*qScale);
849 biasDesc.SetQuantizationOffset(0);
852 // Construct the input data.
853 std::vector<T> inputData;
854 inputData.assign(input.data(), input.data() + inputHeight*inputWidth*inputChannels);
855 auto batchedInput = MakeTensor<T, 4>(inputTensorInfo, inputData);
857 // Construct the output data, with bias applied, as appropriate.
858 std::vector<T> outputData;
859 outputData.assign(outputExpected.data(), outputExpected.data() + outputHeight*outputWidth*outputChannels);
861 LayerTestResult<T, 4> ret(outputTensorInfo);
862 ret.outputExpected = MakeTensor<T, 4>(outputTensorInfo, outputData);
864 std::unique_ptr<armnn::ITensorHandle> inputHandle = workloadFactory.CreateTensorHandle(inputTensorInfo);
865 std::unique_ptr<armnn::ITensorHandle> outputHandle = workloadFactory.CreateTensorHandle(outputTensorInfo);
867 armnn::ScopedCpuTensorHandle weightsTensor(kernelDesc);
868 AllocateAndCopyDataToITensorHandle(&weightsTensor, &kernel[0][0][0][0]);
870 armnn::ScopedCpuTensorHandle biasTensor(biasDesc);
872 armnn::DepthwiseConvolution2dQueueDescriptor data;
873 data.m_Weight = &weightsTensor;
874 data.m_Bias = &biasTensor; // Still set this whether or not bias is enabled - it can be a source of bugs.
875 data.m_Parameters.m_StrideX = strideX;
876 data.m_Parameters.m_StrideY = strideY;
877 data.m_Parameters.m_PadLeft = padLeft;
878 data.m_Parameters.m_PadRight = padRight;
879 data.m_Parameters.m_PadTop = padTop;
880 data.m_Parameters.m_PadBottom = padBottom;
881 data.m_Parameters.m_DataLayout = armnn::DataLayout::NHWC;
883 armnn::WorkloadInfo info;
884 AddInputToWorkload(data, info, inputTensorInfo, inputHandle.get());
885 AddOutputToWorkload(data, info, outputTensorInfo, outputHandle.get());
887 std::unique_ptr<armnn::IWorkload> workload = workloadFactory.CreateDepthwiseConvolution2d(data, info);
889 inputHandle->Allocate();
890 outputHandle->Allocate();
892 CopyDataToITensorHandle(inputHandle.get(), &batchedInput[0][0][0][0]);
894 workloadFactory.Finalize();
897 CopyDataFromITensorHandle(&ret.output[0][0][0][0], outputHandle.get());
903 LayerTestResult<T,4> Convolution1dTestImpl(armnn::IWorkloadFactory& workloadFactory,
908 using B = typename FullyConnectedBiasTypeForInputType<T>::Type;
910 // Until we have a specialist 1D convolution layer, we can fake one using
911 // 2D convolution with the final dimension set to 1.
912 // I don't anticipate this being particularly slow, given that convolution is implemented
913 // as a matrix multiplication, at which point dimension doesn't matter.
915 unsigned int batchSize = 1;
916 unsigned int inputChannels = 2;
917 unsigned int outputChannels = 3;
918 unsigned int inputSize = 5; // The 1D size (could view as 'width' or 'height').
919 unsigned int kernelSize = 3;
920 unsigned int padSize = 2;
921 unsigned int stride = 1;
922 unsigned int outputSize = 7; // (inputSize + 2 * padSize - kernelSize + 1) / stride.
924 armnn::TensorInfo inputInfo({batchSize, inputChannels, inputSize, 1}, armnn::GetDataType<T>());
925 armnn::TensorInfo outputInfo({batchSize, outputChannels, outputSize, 1}, armnn::GetDataType<T>());
926 armnn::TensorInfo kernelInfo({outputChannels, inputChannels, kernelSize, 1}, armnn::GetDataType<T>());
927 armnn::TensorInfo biasInfo({outputChannels}, armnn::GetDataType<B>());
929 // Set quantization parameters if the requested type is a quantized type.
930 if(armnn::IsQuantizedType<T>())
932 inputInfo.SetQuantizationScale(qScale);
933 inputInfo.SetQuantizationOffset(qOffset);
934 outputInfo.SetQuantizationScale(qScale);
935 outputInfo.SetQuantizationOffset(qOffset);
936 kernelInfo.SetQuantizationScale(qScale);
937 kernelInfo.SetQuantizationOffset(qOffset);
938 biasInfo.SetQuantizationScale(inputInfo.GetQuantizationScale()*kernelInfo.GetQuantizationScale());
939 biasInfo.SetQuantizationOffset(0);
942 std::vector<T> inputData(
943 QuantizedVector<T>(inputInfo.GetQuantizationScale(), inputInfo.GetQuantizationOffset(), {
944 5.0f, -2.0f, 2.5f, 0.0f, 1.0f,
945 -3.0f, 3.2f, 5.0f, 2.0f, 3.0f,
948 std::vector<T> kernelData(
949 QuantizedVector<T>(kernelInfo.GetQuantizationScale(), kernelInfo.GetQuantizationOffset(), {
960 std::vector<B> biasData(
961 QuantizedVector<B>(biasInfo.GetQuantizationScale(), biasInfo.GetQuantizationOffset(), {
965 std::vector<T> outputData(
966 QuantizedVector<T>(outputInfo.GetQuantizationScale(), outputInfo.GetQuantizationOffset(), {
967 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,
968 -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,
969 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
972 // Optionally apply bias to output image.
975 ApplyBias(outputData, outputInfo.GetQuantizationScale(), outputInfo.GetQuantizationOffset(),
976 biasData, biasInfo.GetQuantizationScale(), biasInfo.GetQuantizationOffset(),
980 std::unique_ptr<armnn::ITensorHandle> inputHandle = workloadFactory.CreateTensorHandle(inputInfo);
981 std::unique_ptr<armnn::ITensorHandle> outputHandle = workloadFactory.CreateTensorHandle(outputInfo);
983 armnn::Convolution2dQueueDescriptor data;
984 armnn::WorkloadInfo info;
985 armnn::ScopedCpuTensorHandle weightsTensor(kernelInfo);
986 armnn::ScopedCpuTensorHandle biasTensor(biasInfo);
988 AllocateAndCopyDataToITensorHandle(&weightsTensor, kernelData.data());
989 AllocateAndCopyDataToITensorHandle(&biasTensor, biasData.data());
991 AddInputToWorkload(data, info, inputInfo, inputHandle.get());
992 AddOutputToWorkload(data, info, outputInfo, outputHandle.get());
994 data.m_Weight = &weightsTensor;
995 data.m_Bias = &biasTensor;
996 data.m_Parameters.m_StrideX = 1;
997 data.m_Parameters.m_StrideY = stride;
998 data.m_Parameters.m_PadLeft = 0;
999 data.m_Parameters.m_PadRight = 0;
1000 data.m_Parameters.m_PadTop = padSize;
1001 data.m_Parameters.m_PadBottom = padSize;
1002 data.m_Parameters.m_BiasEnabled = biasEnabled;
1004 std::unique_ptr<armnn::IWorkload> workload = workloadFactory.CreateConvolution2d(data, info);
1005 inputHandle->Allocate();
1006 outputHandle->Allocate();
1008 CopyDataToITensorHandle(inputHandle.get(), inputData.data());
1010 workloadFactory.Finalize();
1011 workload->Execute();
1014 LayerTestResult<T,4> ret(outputInfo);
1015 CopyDataFromITensorHandle(&ret.output[0][0][0][0], outputHandle.get());
1016 ret.outputExpected = MakeTensor<T, 4>(outputInfo, outputData);
1022 template<typename T>
1023 LayerTestResult<T,4> CompareConvolution2dTestImpl(armnn::IWorkloadFactory& workloadFactory,
1024 armnn::IWorkloadFactory& refWorkloadFactory)
1026 unsigned int inputHeight = 8;
1027 unsigned int inputWidth = 16;
1028 unsigned int inputChannels = 3;
1029 unsigned int inputNum = 5;
1031 unsigned int kernelHeight = 3;
1032 unsigned int kernelWidth = 3;
1034 unsigned int strideX = 2;
1035 unsigned int strideY = 3;
1036 unsigned int padX = 1;
1037 unsigned int padY = 1;
1039 unsigned int outputNum = inputNum;
1040 unsigned int outputChannels = 2;
1041 unsigned int outputHeight = (inputHeight + 2 * padY - kernelHeight + strideY) / strideY;
1042 unsigned int outputWidth = (inputWidth + 2 * padX - kernelWidth + strideX) / strideX;
1044 armnn::TensorInfo inputTensorInfo;
1045 armnn::TensorInfo outputTensorInfo;
1046 armnn::TensorInfo kernelDesc;
1047 armnn::TensorInfo biasDesc;
1049 unsigned int inputShape[] = {inputNum, inputChannels, inputHeight, inputWidth};
1050 unsigned int outputShape[] = {outputNum, outputChannels, outputHeight, outputWidth};
1051 unsigned int kernelShape[] = {outputChannels, inputChannels, kernelHeight, kernelWidth};
1052 unsigned int biasShape[] = {outputChannels};
1054 inputTensorInfo = armnn::TensorInfo(4, inputShape, armnn::GetDataType<T>());
1055 outputTensorInfo = armnn::TensorInfo(4, outputShape, armnn::GetDataType<T>());
1056 kernelDesc = armnn::TensorInfo(4, kernelShape, armnn::GetDataType<T>());
1057 biasDesc = armnn::TensorInfo(1, biasShape, armnn::GetDataType<T>());
1059 LayerTestResult<T,4> ret(outputTensorInfo);
1061 auto input = MakeRandomTensor<T, 4>(inputTensorInfo, 124908);
1062 auto kernel = MakeRandomTensor<T, 4>(kernelDesc, 891234);
1063 auto bias = MakeRandomTensor<T, 1>(biasDesc, 1028);
1065 std::unique_ptr<armnn::ITensorHandle> inputHandle = workloadFactory.CreateTensorHandle(inputTensorInfo);
1066 std::unique_ptr<armnn::ITensorHandle> outputHandle = workloadFactory.CreateTensorHandle(outputTensorInfo);
1068 armnn::Convolution2dQueueDescriptor data;
1069 armnn::WorkloadInfo info;
1070 armnn::ScopedCpuTensorHandle weightsTensor(kernelDesc);
1071 armnn::ScopedCpuTensorHandle biasTensor(biasDesc);
1073 AllocateAndCopyDataToITensorHandle(&weightsTensor, &kernel[0][0][0][0]);
1074 AllocateAndCopyDataToITensorHandle(&biasTensor, &bias[0]);
1076 AddInputToWorkload(data, info, inputTensorInfo, inputHandle.get());
1077 AddOutputToWorkload(data, info, outputTensorInfo, outputHandle.get());
1078 data.m_Weight = &weightsTensor;
1079 data.m_Bias = &biasTensor;
1080 data.m_Parameters.m_StrideX = strideX;
1081 data.m_Parameters.m_StrideY = strideY;
1082 data.m_Parameters.m_PadLeft = padX;
1083 data.m_Parameters.m_PadRight = padX;
1084 data.m_Parameters.m_PadTop = padY;
1085 data.m_Parameters.m_PadBottom = padY;
1086 data.m_Parameters.m_BiasEnabled = true;
1088 std::unique_ptr<armnn::ITensorHandle> outputHandleRef = refWorkloadFactory.CreateTensorHandle(outputTensorInfo);
1089 std::unique_ptr<armnn::ITensorHandle> inputHandleRef = refWorkloadFactory.CreateTensorHandle(inputTensorInfo);
1091 armnn::Convolution2dQueueDescriptor refData = data;
1092 armnn::WorkloadInfo refInfo = info;
1093 SetWorkloadInput(refData, refInfo, 0, inputTensorInfo, inputHandleRef.get());
1094 SetWorkloadOutput(refData, refInfo, 0, outputTensorInfo, outputHandleRef.get());
1096 std::unique_ptr<armnn::IWorkload> workload = workloadFactory.CreateConvolution2d(data, info);
1097 std::unique_ptr<armnn::IWorkload> workloadRef = refWorkloadFactory.CreateConvolution2d(refData, refInfo);
1099 outputHandleRef->Allocate();
1100 inputHandleRef->Allocate();
1102 inputHandle->Allocate();
1103 outputHandle->Allocate();
1105 CopyDataToITensorHandle(inputHandle.get(), &input[0][0][0][0]);
1106 CopyDataToITensorHandle(inputHandleRef.get(), &input[0][0][0][0]);
1108 workloadFactory.Finalize();
1109 workload->Execute();
1110 refWorkloadFactory.Finalize();
1111 workloadRef->Execute();
1113 CopyDataFromITensorHandle(&ret.output[0][0][0][0], outputHandle.get());
1114 CopyDataFromITensorHandle(&ret.outputExpected[0][0][0][0], outputHandleRef.get());
1119 template<typename T>
1120 LayerTestResult<T, 4> CompareDepthwiseConvolution2dTestImpl(armnn::IWorkloadFactory& workloadFactory,
1121 armnn::IWorkloadFactory& refWorkloadFactory,
1122 const armnn::DataLayoutIndexed& layout)
1124 unsigned int inputHeight = 8;
1125 unsigned int inputWidth = 16;
1126 unsigned int inputChannels = 3;
1127 unsigned int inputNum = 5;
1129 unsigned int kernelHeight = 3;
1130 unsigned int kernelWidth = 3;
1131 unsigned int channelMultiplier = 1;
1133 unsigned int strideX = 2;
1134 unsigned int strideY = 3;
1135 unsigned int padX = 1;
1136 unsigned int padY = 1;
1138 unsigned int outputNum = inputNum;
1139 unsigned int outputChannels = inputChannels * channelMultiplier;
1140 unsigned int outputHeight = (inputHeight + 2 * padY - kernelHeight + strideY) / strideY;
1141 unsigned int outputWidth = (inputWidth + 2 * padX - kernelWidth + strideX) / strideX;
1143 armnn::TensorInfo inputTensorInfo;
1144 armnn::TensorInfo outputTensorInfo;
1145 armnn::TensorInfo kernelDesc;
1146 armnn::TensorInfo biasDesc;
1149 std::vector<unsigned int> inputShape;
1150 std::vector<unsigned int> outputShape;
1151 std::vector<unsigned int> kernelShape;
1152 std::vector<unsigned int> biasShape= { outputChannels };
1153 switch (layout.GetDataLayout())
1155 case armnn::DataLayout::NCHW:
1156 inputShape = { inputNum, inputChannels, inputHeight, inputWidth };
1157 outputShape = { outputNum, outputChannels, outputHeight, outputWidth };
1158 kernelShape = { channelMultiplier, inputChannels, kernelHeight, kernelWidth };
1160 case armnn::DataLayout ::NHWC:
1161 inputShape = { inputNum, inputHeight, inputWidth, inputChannels };
1162 outputShape = { outputNum, outputHeight, outputWidth, outputChannels };
1163 kernelShape = { channelMultiplier, kernelHeight, kernelWidth, inputChannels };
1166 throw armnn::InvalidArgumentException("unknown data layout ["
1167 + std::to_string(static_cast<int>(layout.GetDataLayout())) + "]");
1170 float inputsQScale = armnn::IsQuantizedType<T>() ? 1.0f : 0;
1171 float outputQScale = armnn::IsQuantizedType<T>() ? 2.0f : 0;
1172 int32_t qOffset = 0;
1174 inputTensorInfo = armnn::TensorInfo(4, inputShape.data(), armnn::GetDataType<T>(), inputsQScale, qOffset);
1175 outputTensorInfo = armnn::TensorInfo(4, outputShape.data(), armnn::GetDataType<T>(), outputQScale, qOffset);
1176 kernelDesc = armnn::TensorInfo(4, kernelShape.data(), armnn::GetDataType<T>(), inputsQScale, qOffset);
1177 biasDesc = armnn::TensorInfo(
1178 1, biasShape.data(), armnn::GetBiasDataType(armnn::GetDataType<T>()), inputsQScale, qOffset);
1180 LayerTestResult<T, 4> ret(outputTensorInfo);
1182 auto input = MakeRandomTensor<T, 4>(inputTensorInfo, 124908, 0.0f, 255.0f);
1183 auto kernel = MakeRandomTensor<T, 4>(kernelDesc, 891234, 0.0f, 255.0f);
1184 auto bias = MakeRandomTensor<typename FullyConnectedBiasTypeForInputType<T>::Type, 1>(
1185 biasDesc, 1028, 0.0f, 255.0f);
1187 std::unique_ptr<armnn::ITensorHandle> inputHandle = workloadFactory.CreateTensorHandle(inputTensorInfo);
1188 std::unique_ptr<armnn::ITensorHandle> outputHandle = workloadFactory.CreateTensorHandle(outputTensorInfo);
1190 armnn::DepthwiseConvolution2dQueueDescriptor data;
1191 armnn::WorkloadInfo info;
1192 armnn::ScopedCpuTensorHandle weightsTensor(kernelDesc);
1193 armnn::ScopedCpuTensorHandle biasTensor(biasDesc);
1195 AllocateAndCopyDataToITensorHandle(&weightsTensor, &kernel[0][0][0][0]);
1196 AllocateAndCopyDataToITensorHandle(&biasTensor, &bias[0]);
1198 AddInputToWorkload(data, info, inputTensorInfo, inputHandle.get());
1199 AddOutputToWorkload(data, info, outputTensorInfo, outputHandle.get());
1200 data.m_Weight = &weightsTensor;
1201 data.m_Bias = &biasTensor;
1202 data.m_Parameters.m_StrideX = strideX;
1203 data.m_Parameters.m_StrideY = strideY;
1204 data.m_Parameters.m_PadLeft = padX;
1205 data.m_Parameters.m_PadRight = padX;
1206 data.m_Parameters.m_PadTop = padY;
1207 data.m_Parameters.m_PadBottom = padY;
1208 data.m_Parameters.m_BiasEnabled = true;
1209 data.m_Parameters.m_DataLayout = layout.GetDataLayout();
1211 std::unique_ptr<armnn::ITensorHandle> outputHandleRef = refWorkloadFactory.CreateTensorHandle(outputTensorInfo);
1212 std::unique_ptr<armnn::ITensorHandle> inputHandleRef = refWorkloadFactory.CreateTensorHandle(inputTensorInfo);
1214 armnn::DepthwiseConvolution2dQueueDescriptor refData = data;
1215 armnn::WorkloadInfo refInfo = info;
1216 SetWorkloadInput(refData, refInfo, 0, inputTensorInfo, inputHandleRef.get());
1217 SetWorkloadOutput(refData, refInfo, 0, outputTensorInfo, outputHandleRef.get());
1219 std::unique_ptr<armnn::IWorkload> workload = workloadFactory.CreateDepthwiseConvolution2d(data, info);
1220 std::unique_ptr<armnn::IWorkload> workloadRef = refWorkloadFactory.CreateDepthwiseConvolution2d(refData, refInfo);
1222 outputHandleRef->Allocate();
1223 inputHandleRef->Allocate();
1225 inputHandle->Allocate();
1226 outputHandle->Allocate();
1228 CopyDataToITensorHandle(inputHandle.get(), &input[0][0][0][0]);
1229 CopyDataToITensorHandle(inputHandleRef.get(), &input[0][0][0][0]);
1231 workloadFactory.Finalize();
1232 workload->Execute();
1233 refWorkloadFactory.Finalize();
1234 workloadRef->Execute();
1236 CopyDataFromITensorHandle(&ret.output[0][0][0][0], outputHandle.get());
1237 CopyDataFromITensorHandle(&ret.outputExpected[0][0][0][0], outputHandleRef.get());