blob: 993794e325fe041f29a72ec25d66d0dc15053590 [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
jimfly010a088a62018-10-25 17:05:05 +01007#include <string>
telsoa014fcda012018-03-09 14:13:49 +00008#include <armnn/ArmNN.hpp>
9#include <armnn/Tensor.hpp>
10#include <armnn/TypesUtils.hpp>
telsoa014fcda012018-03-09 14:13:49 +000011
David Beckac42efd2018-09-26 17:41:13 +010012#include <test/TensorHelpers.hpp>
telsoa014fcda012018-03-09 14:13:49 +000013#include "QuantizeHelper.hpp"
14
David Beckac42efd2018-09-26 17:41:13 +010015#include <backends/CpuTensorHandle.hpp>
16#include <backends/WorkloadFactory.hpp>
jimfly010a088a62018-10-25 17:05:05 +010017#include "Permute.hpp"
18#include <boost/numeric/conversion/cast.hpp>
telsoa014fcda012018-03-09 14:13:49 +000019
20// Mapping from input type to bias type for fully connected layers.
21// float => float, uint8_t => int32_t
22template<typename T>
23struct FullyConnectedBiasTypeForInputType;
24
25template<>
26struct FullyConnectedBiasTypeForInputType<float>
27{
28 using Type = float;
29};
30
31template<>
32struct FullyConnectedBiasTypeForInputType<uint8_t>
33{
34 using Type = int32_t;
35};
36
telsoa01c577f2c2018-08-31 09:22:23 +010037// Modifies a std::vector in-place using a specified bias.
telsoa014fcda012018-03-09 14:13:49 +000038template<typename T, typename B>
39void 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)
41{
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.");
46
telsoa01c577f2c2018-08-31 09:22:23 +010047 // Note we need to dequantize and re-quantize the image value and the bias.
telsoa014fcda012018-03-09 14:13:49 +000048 for (uint32_t i = 0; i < bias.size(); ++i)
49 {
50 float dBias = SelectiveDequantize(bias[i], bScale, bOffset);
51 for (uint32_t y = 0; y < h; ++y)
52 {
53 for (uint32_t x = 0; x < w; ++x)
54 {
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);
60 }
61 }
62 }
63}
64
jimfly010a088a62018-10-25 17:05:05 +010065template<typename T>
66armnn::TensorInfo GetTensorInfo(unsigned int numberOfBatches,
67 unsigned int numberOfChannels,
68 unsigned int height,
69 unsigned int width,
70 const armnn::DataLayoutIndexed& layout)
71{
72 switch (layout.GetDataLayout())
73 {
74 case armnn::DataLayout::NCHW:
75 return armnn::TensorInfo({numberOfBatches, numberOfChannels, height, width}, armnn::GetDataType<T>());
76 case armnn::DataLayout ::NHWC:
77 return armnn::TensorInfo({numberOfBatches, height, width, numberOfChannels}, armnn::GetDataType<T>());
78 default:
79 throw armnn::InvalidArgumentException("unknown data layout ["
80 + std::to_string(static_cast<int>(layout.GetDataLayout())) + "]");
81 }
82}
83
84
85
telsoa014fcda012018-03-09 14:13:49 +000086template<typename T, typename B>
87LayerTestResult<T, 4> SimpleConvolution2dTestImpl(armnn::IWorkloadFactory& workloadFactory,
jimfly010a088a62018-10-25 17:05:05 +010088 const boost::multi_array<T, 4>& originalInput,
89 const boost::multi_array<T, 4>& originalKernel,
telsoa014fcda012018-03-09 14:13:49 +000090 const boost::multi_array<B, 1>& bias,
jimfly010a088a62018-10-25 17:05:05 +010091 const boost::multi_array<T, 4>& originalOutputExpected,
telsoa014fcda012018-03-09 14:13:49 +000092 float qScale,
93 int32_t qOffset,
jimfly010a088a62018-10-25 17:05:05 +010094 const armnn::DataLayoutIndexed& layout = armnn::DataLayout::NCHW,
telsoa014fcda012018-03-09 14:13:49 +000095 uint32_t padLeft = 0,
96 uint32_t padTop = 0,
97 uint32_t padRight = 0,
98 uint32_t padBottom = 0)
99{
jimfly010a088a62018-10-25 17:05:05 +0100100 unsigned int inputHeight = boost::numeric_cast<unsigned int>(originalInput.shape()[2]);
101 unsigned int inputWidth = boost::numeric_cast<unsigned int>(originalInput.shape()[3]);
102 unsigned int inputChannels = boost::numeric_cast<unsigned int>(originalInput.shape()[1]);
103 unsigned int inputNum = boost::numeric_cast<unsigned int>(originalInput.shape()[0]);
telsoa014fcda012018-03-09 14:13:49 +0000104
jimfly010a088a62018-10-25 17:05:05 +0100105 unsigned int outputHeight = boost::numeric_cast<unsigned int>(originalOutputExpected.shape()[2]);
106 unsigned int outputWidth = boost::numeric_cast<unsigned int>(originalOutputExpected.shape()[3]);
107 unsigned int outputChannels = boost::numeric_cast<unsigned int>(originalOutputExpected.shape()[1]);
108 unsigned int outputNum = boost::numeric_cast<unsigned int>(originalOutputExpected.shape()[0]);
telsoa014fcda012018-03-09 14:13:49 +0000109
jimfly010a088a62018-10-25 17:05:05 +0100110 unsigned int kernelHeight = boost::numeric_cast<unsigned int>(originalKernel.shape()[2]);
111 unsigned int kernelWidth = boost::numeric_cast<unsigned int>(originalKernel.shape()[3]);
112 unsigned int kernelChannels = boost::numeric_cast<unsigned int>(originalKernel.shape()[1]);
113 unsigned int kernelDepthMul = boost::numeric_cast<unsigned int>(originalKernel.shape()[0]);
telsoa014fcda012018-03-09 14:13:49 +0000114
115 bool biasEnabled = bias.size() > 0;
116
telsoa01c577f2c2018-08-31 09:22:23 +0100117 // This function currently assumes 1 batch of input/output (and duplicates this into 2 batches).
telsoa014fcda012018-03-09 14:13:49 +0000118 BOOST_ASSERT(inputNum == 1);
119 BOOST_ASSERT(outputNum == 1);
120
telsoa01c577f2c2018-08-31 09:22:23 +0100121 // If a bias is used, its size must equal the number of output channels.
telsoa014fcda012018-03-09 14:13:49 +0000122 BOOST_ASSERT(!biasEnabled || bias.size() == outputChannels);
123
124
telsoa01c577f2c2018-08-31 09:22:23 +0100125 // Note these tensors will use two (identical) batches.
jimfly010a088a62018-10-25 17:05:05 +0100126 // NOTE: if layout is unknown we will get an exception at this point.
127 armnn::TensorInfo inputTensorInfo = GetTensorInfo<T>(2*inputNum, inputChannels, inputHeight, inputWidth, layout);
128 armnn::TensorInfo outputTensorInfo = GetTensorInfo<T>(
129 2*outputNum, outputChannels, outputHeight, outputWidth, layout);
130 armnn::TensorInfo kernelDesc = GetTensorInfo<T>(kernelDepthMul, kernelChannels, kernelHeight, kernelWidth, layout);
telsoa014fcda012018-03-09 14:13:49 +0000131 armnn::TensorInfo biasDesc({static_cast<unsigned int>(bias.size())}, armnn::GetDataType<B>());
132
133 // Set quantization parameters if the requested type is a quantized type.
134 if(armnn::IsQuantizedType<T>())
135 {
136 inputTensorInfo.SetQuantizationScale(qScale);
137 inputTensorInfo.SetQuantizationOffset(qOffset);
138 outputTensorInfo.SetQuantizationScale(qScale);
139 outputTensorInfo.SetQuantizationOffset(qOffset);
140 kernelDesc.SetQuantizationScale(qScale);
141 kernelDesc.SetQuantizationOffset(qOffset);
142 biasDesc.SetQuantizationScale(qScale*qScale);
143 biasDesc.SetQuantizationOffset(0);
144 }
145
146 LayerTestResult<T, 4> ret(outputTensorInfo);
147
telsoa01c577f2c2018-08-31 09:22:23 +0100148 // Construct input data - two batches of the same input image.
telsoa014fcda012018-03-09 14:13:49 +0000149 std::vector<T> inputImage;
jimfly010a088a62018-10-25 17:05:05 +0100150 inputImage.assign(originalInput.data(), originalInput.data() + 1*inputChannels*inputHeight*inputWidth);
telsoa014fcda012018-03-09 14:13:49 +0000151 std::vector<T> inputData;
152 inputData.insert(inputData.end(), inputImage.begin(), inputImage.end());
153 inputData.insert(inputData.end(), inputImage.begin(), inputImage.end());
jimfly010a088a62018-10-25 17:05:05 +0100154
155 // at this point if we require it permute the input data
156 const armnn::PermutationVector NCHWToNHWC = { 0, 3, 1, 2 };
157 if (layout.GetDataLayout() == armnn::DataLayout::NHWC)
158 {
159 std::vector<T> tmp(inputData.size());
160 armnnUtils::Permute(inputTensorInfo.GetShape(), NCHWToNHWC, inputData.data(), tmp.data());
161 inputData = tmp;
162 }
163
telsoa014fcda012018-03-09 14:13:49 +0000164 auto batchedInput = MakeTensor<T, 4>(inputTensorInfo, inputData);
165
166 std::vector<T> outputImage;
jimfly010a088a62018-10-25 17:05:05 +0100167 outputImage.assign(originalOutputExpected.data(),
168 originalOutputExpected.data() + outputChannels*outputHeight*outputWidth);
telsoa014fcda012018-03-09 14:13:49 +0000169
telsoa01c577f2c2018-08-31 09:22:23 +0100170 // Apply bias to output image if it is enabled.
telsoa014fcda012018-03-09 14:13:49 +0000171 if(biasEnabled)
172 {
173 std::vector<T> biasV;
174 biasV.assign(bias.data(), bias.data() + outputChannels);
175 ApplyBias(outputImage, outputTensorInfo.GetQuantizationScale(), outputTensorInfo.GetQuantizationOffset(),
176 biasV, biasDesc.GetQuantizationScale(), biasDesc.GetQuantizationOffset(),
177 outputWidth, outputHeight);
178 }
179
telsoa01c577f2c2018-08-31 09:22:23 +0100180 // Construct expected output data - two identical images.
telsoa014fcda012018-03-09 14:13:49 +0000181 std::vector<T> outputData;
182 outputData.insert(outputData.end(), outputImage.begin(), outputImage.end());
183 outputData.insert(outputData.end(), outputImage.begin(), outputImage.end());
184
jimfly010a088a62018-10-25 17:05:05 +0100185 // at this point if we require it permute the expected output
186 if (layout.GetDataLayout() == armnn::DataLayout::NHWC)
187 {
188 std::vector<T> tmp(outputData.size());
189 armnnUtils::Permute(outputTensorInfo.GetShape(), NCHWToNHWC, outputData.data(), tmp.data());
190 outputData = tmp;
191 }
telsoa014fcda012018-03-09 14:13:49 +0000192 ret.outputExpected = MakeTensor<T, 4>(outputTensorInfo, outputData);
193
telsoa01c577f2c2018-08-31 09:22:23 +0100194 // Todo: nontrivial padding and strides.
telsoa014fcda012018-03-09 14:13:49 +0000195 uint32_t strideX = 1;
196 uint32_t strideY = 1;
197
198 std::unique_ptr<armnn::ITensorHandle> inputHandle = workloadFactory.CreateTensorHandle(inputTensorInfo);
199 std::unique_ptr<armnn::ITensorHandle> outputHandle = workloadFactory.CreateTensorHandle(outputTensorInfo);
200
201 armnn::Convolution2dQueueDescriptor data;
202 armnn::WorkloadInfo info;
203 armnn::ScopedCpuTensorHandle weightsTensor(kernelDesc);
204 armnn::ScopedCpuTensorHandle biasTensor(biasDesc);
jimfly010a088a62018-10-25 17:05:05 +0100205 // Permute the kernel if necessary
206 boost::multi_array<T, 4> kernel = boost::multi_array<T, 4>(originalKernel);
207 if (layout.GetDataLayout() == armnn::DataLayout::NHWC)
208 {
209 armnnUtils::Permute(kernelDesc.GetShape(), NCHWToNHWC, originalKernel.data(), kernel.data());
210 }
telsoa014fcda012018-03-09 14:13:49 +0000211 AllocateAndCopyDataToITensorHandle(&weightsTensor, &kernel[0][0][0][0]);
212
213 if(biasEnabled)
214 {
215 AllocateAndCopyDataToITensorHandle(&biasTensor, &bias[0]);
216 }
217
218 AddInputToWorkload(data, info, inputTensorInfo, inputHandle.get());
219 AddOutputToWorkload(data, info, outputTensorInfo, outputHandle.get());
220
221 data.m_Weight = &weightsTensor;
telsoa01c577f2c2018-08-31 09:22:23 +0100222 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 +0000223 data.m_Parameters.m_StrideX = strideX;
224 data.m_Parameters.m_StrideY = strideY;
225 data.m_Parameters.m_PadLeft = padLeft;
226 data.m_Parameters.m_PadRight = padRight;
227 data.m_Parameters.m_PadTop = padTop;
228 data.m_Parameters.m_PadBottom = padBottom;
229 data.m_Parameters.m_BiasEnabled = biasEnabled;
jimfly010a088a62018-10-25 17:05:05 +0100230 data.m_Parameters.m_DataLayout = layout.GetDataLayout();
telsoa014fcda012018-03-09 14:13:49 +0000231
232 std::unique_ptr<armnn::IWorkload> workload = workloadFactory.CreateConvolution2d(data, info);
233 inputHandle->Allocate();
234 outputHandle->Allocate();
235
236 CopyDataToITensorHandle(inputHandle.get(), &batchedInput[0][0][0][0]);
237
surmeh013537c2c2018-05-18 16:31:43 +0100238 workloadFactory.Finalize();
239 workload->Execute();
240
241 CopyDataFromITensorHandle(&ret.output[0][0][0][0], outputHandle.get());
242
243 return ret;
244}
245
246template<typename T, typename B>
Francis Murtaghd59116e2018-10-04 16:03:07 +0100247LayerTestResult<T, 4> SimpleConvolution2dNhwcTestImpl(armnn::IWorkloadFactory& workloadFactory,
248 const boost::multi_array<T, 4>& input,
249 const boost::multi_array<T, 4>& kernel,
250 const boost::multi_array<B, 1>& bias,
251 const boost::multi_array<T, 4>& outputExpected,
252 armnn::DataLayout dataLayout,
253 float qScale,
254 int32_t qOffset,
255 uint32_t padLeft = 1,
256 uint32_t padTop = 1,
257 uint32_t padRight = 1,
258 uint32_t padBottom = 1,
259 uint32_t strideX = 1,
260 uint32_t strideY = 1)
261{
262 unsigned int inputNum = boost::numeric_cast<unsigned int>(input.shape()[0]);
263 unsigned int inputChannels = boost::numeric_cast<unsigned int>(input.shape()[3]);
264 unsigned int inputHeight = boost::numeric_cast<unsigned int>(input.shape()[1]);
265 unsigned int inputWidth = boost::numeric_cast<unsigned int>(input.shape()[2]);
266
267 unsigned int kernelChanMul = boost::numeric_cast<unsigned int>(kernel.shape()[0]);
268 unsigned int kernelChannels = boost::numeric_cast<unsigned int>(kernel.shape()[3]);
269 unsigned int kernelHeight = boost::numeric_cast<unsigned int>(kernel.shape()[1]);
270 unsigned int kernelWidth = boost::numeric_cast<unsigned int>(kernel.shape()[2]);
271
272 unsigned int outputNum = boost::numeric_cast<unsigned int>(outputExpected.shape()[0]);
273 unsigned int outputChannels = boost::numeric_cast<unsigned int>(outputExpected.shape()[3]);
274 unsigned int outputHeight = boost::numeric_cast<unsigned int>(outputExpected.shape()[1]);
275 unsigned int outputWidth = boost::numeric_cast<unsigned int>(outputExpected.shape()[2]);
276
277 bool biasEnabled = bias.size() > 0;
278
279 // Creates the tensors.
280 armnn::TensorInfo inputTensorInfo({inputNum, inputHeight, inputWidth, inputChannels}, armnn::GetDataType<T>());
281 armnn::TensorInfo outputTensorInfo({outputNum, outputHeight, outputWidth, outputChannels},
282 armnn::GetDataType<T>());
283 armnn::TensorInfo kernelDesc({kernelChanMul, kernelHeight, kernelWidth, kernelChannels}, armnn::GetDataType<T>());
284 armnn::TensorInfo biasDesc({static_cast<unsigned int>(bias.size())}, armnn::GetDataType<B>());
285
286 // Construct the input data.
287 std::vector<T> inputData;
288 inputData.assign(input.data(), input.data() + inputHeight*inputWidth*inputChannels);
289 auto batchedInput = MakeTensor<T, 4>(inputTensorInfo, inputData);
290
291 // Construct the output data, with bias applied, as appropriate.
292 std::vector<T> outputData;
293 outputData.assign(outputExpected.data(), outputExpected.data() + outputHeight*outputWidth*outputChannels);
294
295 LayerTestResult<T, 4> ret(outputTensorInfo);
296 ret.outputExpected = MakeTensor<T, 4>(outputTensorInfo, outputData);
297
298 std::unique_ptr<armnn::ITensorHandle> inputHandle = workloadFactory.CreateTensorHandle(inputTensorInfo);
299 std::unique_ptr<armnn::ITensorHandle> outputHandle = workloadFactory.CreateTensorHandle(outputTensorInfo);
300
301 armnn::ScopedCpuTensorHandle weightsTensor(kernelDesc);
302 AllocateAndCopyDataToITensorHandle(&weightsTensor, &kernel[0][0][0][0]);
303
304 armnn::ScopedCpuTensorHandle biasTensor(biasDesc);
305
306 armnn::Convolution2dQueueDescriptor data;
307
308 data.m_Weight = &weightsTensor;
309 data.m_Bias = &biasTensor; // Still set this whether or not bias is enabled - can be a source of bugs.
310 data.m_Parameters.m_StrideX = strideX;
311 data.m_Parameters.m_StrideY = strideY;
312 data.m_Parameters.m_PadLeft = padLeft;
313 data.m_Parameters.m_PadRight = padRight;
314 data.m_Parameters.m_PadTop = padTop;
315 data.m_Parameters.m_PadBottom = padBottom;
316 data.m_Parameters.m_BiasEnabled = biasEnabled;
317 data.m_Parameters.m_DataLayout = dataLayout;
318
319 armnn::WorkloadInfo info;
320 AddInputToWorkload(data, info, inputTensorInfo, inputHandle.get());
321 AddOutputToWorkload(data, info, outputTensorInfo, outputHandle.get());
322
323 std::unique_ptr<armnn::IWorkload> workload = workloadFactory.CreateConvolution2d(data, info);
324 inputHandle->Allocate();
325 outputHandle->Allocate();
326
327 CopyDataToITensorHandle(inputHandle.get(), &batchedInput[0][0][0][0]);
328
329 workloadFactory.Finalize();
330 workload->Execute();
331
332 CopyDataFromITensorHandle(&ret.output[0][0][0][0], outputHandle.get());
333
334 return ret;
335}
336
337template<typename T, typename B>
surmeh013537c2c2018-05-18 16:31:43 +0100338LayerTestResult<T, 4> DepthwiseConvolution2dAsymmetricTestImpl(armnn::IWorkloadFactory& workloadFactory,
339 const boost::multi_array<T, 4>& input,
jimfly01382a91d2018-10-26 15:55:50 +0100340 const boost::multi_array<T, 4>& originalKernel,
surmeh013537c2c2018-05-18 16:31:43 +0100341 const boost::multi_array<B, 1>& bias,
342 const boost::multi_array<T, 4>& outputExpected,
343 float qScale,
344 int32_t qOffset,
jimfly01382a91d2018-10-26 15:55:50 +0100345 const armnn::DataLayoutIndexed& layout,
surmeh013537c2c2018-05-18 16:31:43 +0100346 uint32_t padLeft = 0,
347 uint32_t padTop = 0,
348 uint32_t padRight = 0,
349 uint32_t padBottom = 0,
350 uint32_t strideX = 1,
351 uint32_t strideY = 1)
352{
353 unsigned int inputNum = boost::numeric_cast<unsigned int>(input.shape()[0]);
354 unsigned int inputChannels = boost::numeric_cast<unsigned int>(input.shape()[1]);
355 unsigned int inputHeight = boost::numeric_cast<unsigned int>(input.shape()[2]);
356 unsigned int inputWidth = boost::numeric_cast<unsigned int>(input.shape()[3]);
jimfly01382a91d2018-10-26 15:55:50 +0100357 unsigned int kernelChanMul = boost::numeric_cast<unsigned int>(originalKernel.shape()[0]);
358 unsigned int kernelChannels = boost::numeric_cast<unsigned int>(originalKernel.shape()[1]);
359 unsigned int kernelHeight = boost::numeric_cast<unsigned int>(originalKernel.shape()[2]);
360 unsigned int kernelWidth = boost::numeric_cast<unsigned int>(originalKernel.shape()[3]);
surmeh013537c2c2018-05-18 16:31:43 +0100361 unsigned int outputNum = boost::numeric_cast<unsigned int>(outputExpected.shape()[0]);
362 unsigned int outputChannels = boost::numeric_cast<unsigned int>(outputExpected.shape()[1]);
363 unsigned int outputHeight = boost::numeric_cast<unsigned int>(outputExpected.shape()[2]);
364 unsigned int outputWidth = boost::numeric_cast<unsigned int>(outputExpected.shape()[3]);
365
telsoa01c577f2c2018-08-31 09:22:23 +0100366 // If a bias is used, its size must equal the number of output channels.
surmeh013537c2c2018-05-18 16:31:43 +0100367 bool biasEnabled = bias.size() > 0;
368 BOOST_ASSERT(!biasEnabled || bias.size() == outputChannels);
369
telsoa01c577f2c2018-08-31 09:22:23 +0100370 // Creates the tensors.
jimfly01382a91d2018-10-26 15:55:50 +0100371 armnn::TensorInfo inputTensorInfo = GetTensorInfo<T>(inputNum, inputChannels, inputHeight, inputWidth, layout);
372 armnn::TensorInfo outputTensorInfo = GetTensorInfo<T>(outputNum, outputChannels, outputHeight, outputWidth, layout);
373 armnn::TensorInfo kernelDesc = GetTensorInfo<T>(kernelChanMul, kernelChannels, kernelHeight, kernelWidth, layout);
surmeh013537c2c2018-05-18 16:31:43 +0100374 armnn::TensorInfo biasDesc({static_cast<unsigned int>(bias.size())}, armnn::GetDataType<B>());
375
376 // Set quantization parameters if the requested type is a quantized type.
377 if (armnn::IsQuantizedType<T>())
378 {
379 inputTensorInfo.SetQuantizationScale(qScale);
380 inputTensorInfo.SetQuantizationOffset(qOffset);
381 outputTensorInfo.SetQuantizationScale(qScale);
382 outputTensorInfo.SetQuantizationOffset(qOffset);
383 kernelDesc.SetQuantizationScale(qScale);
384 kernelDesc.SetQuantizationOffset(qOffset);
385 biasDesc.SetQuantizationScale(qScale*qScale);
386 biasDesc.SetQuantizationOffset(0);
387 }
388
telsoa01c577f2c2018-08-31 09:22:23 +0100389 // Construct the input data.
surmeh013537c2c2018-05-18 16:31:43 +0100390 std::vector<T> inputData;
391 inputData.assign(input.data(), input.data() + inputChannels*inputHeight*inputWidth);
jimfly01382a91d2018-10-26 15:55:50 +0100392
393 // At this point if we require it permute the input data
394 const armnn::PermutationVector NCHWToNHWC = { 0, 3, 1, 2 };
395 if (layout.GetDataLayout() == armnn::DataLayout::NHWC)
396 {
397 std::vector<T> tmp(inputData.size());
398 armnnUtils::Permute(inputTensorInfo.GetShape(), NCHWToNHWC, inputData.data(), tmp.data());
399 inputData = tmp;
400 }
401
surmeh013537c2c2018-05-18 16:31:43 +0100402 auto batchedInput = MakeTensor<T, 4>(inputTensorInfo, inputData);
403
telsoa01c577f2c2018-08-31 09:22:23 +0100404 // Construct the output data, with bias applied, as appropriate.
surmeh013537c2c2018-05-18 16:31:43 +0100405 std::vector<T> outputData;
406 outputData.assign(outputExpected.data(), outputExpected.data() + outputChannels*outputHeight*outputWidth);
407 if (biasEnabled)
408 {
409 std::vector<T> biasV;
410 biasV.assign(bias.data(), bias.data() + outputChannels);
411 ApplyBias(outputData, outputTensorInfo.GetQuantizationScale(), outputTensorInfo.GetQuantizationOffset(),
412 biasV, biasDesc.GetQuantizationScale(), biasDesc.GetQuantizationOffset(),
413 outputWidth, outputHeight);
414 }
415
416 LayerTestResult<T, 4> ret(outputTensorInfo);
jimfly01382a91d2018-10-26 15:55:50 +0100417
418 // At this point if we require it permute the expected output
419 if (layout.GetDataLayout() == armnn::DataLayout::NHWC)
420 {
421 std::vector<T> tmp(outputData.size());
422 armnnUtils::Permute(outputTensorInfo.GetShape(), NCHWToNHWC, outputData.data(), tmp.data());
423 outputData = tmp;
424 }
425
surmeh013537c2c2018-05-18 16:31:43 +0100426 ret.outputExpected = MakeTensor<T, 4>(outputTensorInfo, outputData);
427
428 std::unique_ptr<armnn::ITensorHandle> inputHandle = workloadFactory.CreateTensorHandle(inputTensorInfo);
429 std::unique_ptr<armnn::ITensorHandle> outputHandle = workloadFactory.CreateTensorHandle(outputTensorInfo);
430
431 armnn::ScopedCpuTensorHandle weightsTensor(kernelDesc);
jimfly01382a91d2018-10-26 15:55:50 +0100432
433 // Permute the kernel if necessary
434 boost::multi_array<T, 4> kernel = boost::multi_array<T, 4>(originalKernel);
435 if (layout.GetDataLayout() == armnn::DataLayout::NHWC)
436 {
437 armnnUtils::Permute(kernelDesc.GetShape(), NCHWToNHWC, originalKernel.data(), kernel.data());
438 }
439
surmeh013537c2c2018-05-18 16:31:43 +0100440 AllocateAndCopyDataToITensorHandle(&weightsTensor, &kernel[0][0][0][0]);
441
442 armnn::ScopedCpuTensorHandle biasTensor(biasDesc);
443 if (biasEnabled)
444 {
445 AllocateAndCopyDataToITensorHandle(&biasTensor, &bias[0]);
446 }
447
448 armnn::DepthwiseConvolution2dQueueDescriptor data;
449 data.m_Weight = &weightsTensor;
telsoa01c577f2c2018-08-31 09:22:23 +0100450 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 +0100451 data.m_Parameters.m_StrideX = strideX;
452 data.m_Parameters.m_StrideY = strideY;
453 data.m_Parameters.m_PadLeft = padLeft;
454 data.m_Parameters.m_PadRight = padRight;
455 data.m_Parameters.m_PadTop = padTop;
456 data.m_Parameters.m_PadBottom = padBottom;
457 data.m_Parameters.m_BiasEnabled = biasEnabled;
jimfly01382a91d2018-10-26 15:55:50 +0100458 data.m_Parameters.m_DataLayout = layout.GetDataLayout();
surmeh013537c2c2018-05-18 16:31:43 +0100459
460 armnn::WorkloadInfo info;
461 AddInputToWorkload(data, info, inputTensorInfo, inputHandle.get());
462 AddOutputToWorkload(data, info, outputTensorInfo, outputHandle.get());
463
464 std::unique_ptr<armnn::IWorkload> workload = workloadFactory.CreateDepthwiseConvolution2d(data, info);
465 inputHandle->Allocate();
466 outputHandle->Allocate();
467
468 CopyDataToITensorHandle(inputHandle.get(), &batchedInput[0][0][0][0]);
469
470 workloadFactory.Finalize();
telsoa014fcda012018-03-09 14:13:49 +0000471 workload->Execute();
472
473 CopyDataFromITensorHandle(&ret.output[0][0][0][0], outputHandle.get());
474
475 return ret;
476}
477
478template<typename T, typename B>
479LayerTestResult<T, 4> DepthwiseConvolution2dDepthMul1TestImpl(armnn::IWorkloadFactory& workloadFactory,
480 float qScale,
481 int32_t qOffset,
482 bool biasEnabled)
483{
484 unsigned int inputHeight = 3;
485 unsigned int inputWidth = 3;
486 unsigned int inputChannels = 2;
487 unsigned int inputNum = 1;
488
489 unsigned int kernelHeight = 3;
490 unsigned int kernelWidth = 3;
491 unsigned int kernelChannels = inputChannels;
492
493 unsigned int outputHeight = 1;
494 unsigned int outputWidth = 1;
495 unsigned int outputChannels = kernelChannels;
496 unsigned int outputNum = inputNum;
497
498 armnn::TensorInfo inputTensorInfo({ inputNum, inputChannels, inputHeight, inputWidth }, armnn::GetDataType<T>());
499 armnn::TensorInfo outputTensorInfo({ outputNum, outputChannels, outputHeight, outputWidth },
500 armnn::GetDataType<T>());
501 armnn::TensorInfo kernelDesc({ 1, outputChannels, kernelHeight, kernelWidth }, armnn::GetDataType<T>());
502 armnn::TensorInfo biasDesc({ outputChannels }, armnn::GetDataType<B>());
503
504 // Set quantization parameters if the requested type is a quantized type.
505 if(armnn::IsQuantizedType<T>())
506 {
507 inputTensorInfo.SetQuantizationScale(qScale);
508 inputTensorInfo.SetQuantizationOffset(qOffset);
509 outputTensorInfo.SetQuantizationScale(qScale);
510 outputTensorInfo.SetQuantizationOffset(qOffset);
511 kernelDesc.SetQuantizationScale(qScale);
512 kernelDesc.SetQuantizationOffset(qOffset);
513 biasDesc.SetQuantizationScale(qScale*qScale);
514 biasDesc.SetQuantizationOffset(0);
515 }
516
517 auto input = MakeTensor<T, 4>(inputTensorInfo, std::vector<T>(
518 QuantizedVector<T>(inputTensorInfo.GetQuantizationScale(), inputTensorInfo.GetQuantizationOffset(), {
519 1.f, 2.f, 1.f,
520 2.f, 1.f, 2.f,
521 1.f, 2.f, 1.f,
522
523 1.f, 2.f, 1.f,
524 2.f, 1.f, 2.f,
525 1.f, 2.f, 1.f,
526 })));
527
528 std::vector<B> biasV(QuantizedVector<B>(biasDesc.GetQuantizationScale(), biasDesc.GetQuantizationOffset(),
529 {0, 2}));
530 auto bias = MakeTensor<B, 1>(biasDesc, biasV);
531
532 auto kernel = MakeTensor<T, 4>(kernelDesc, std::vector<T>(
533 QuantizedVector<T>(kernelDesc.GetQuantizationScale(), kernelDesc.GetQuantizationOffset(), {
534 1.f, 0.f, 1.f,
535 0.f, 0.f, 0.f,
536 -1.f, 0.f, -1.f,
537
538 1.f, 0.f, 1.f,
539 0.f, 0.f, 0.f,
540 -1.f, 0.f, -1.f,
541 })));
542
telsoa01c577f2c2018-08-31 09:22:23 +0100543 // Manually calculated.
telsoa014fcda012018-03-09 14:13:49 +0000544 std::vector<T> outputImage(
545 QuantizedVector<T>(outputTensorInfo.GetQuantizationScale(),
546 outputTensorInfo.GetQuantizationOffset(),
547 {0.f, 0.f})
548 );
549
telsoa01c577f2c2018-08-31 09:22:23 +0100550 // Optionally apply bias to output image.
telsoa014fcda012018-03-09 14:13:49 +0000551 if(biasEnabled)
552 {
553 ApplyBias(outputImage, outputTensorInfo.GetQuantizationScale(), outputTensorInfo.GetQuantizationOffset(),
554 biasV, biasDesc.GetQuantizationScale(), biasDesc.GetQuantizationOffset(),
555 outputWidth, outputHeight);
556 }
557
558 LayerTestResult<T, 4> ret(outputTensorInfo);
559 ret.outputExpected = MakeTensor<T, 4>(outputTensorInfo, outputImage);
560
561 std::unique_ptr<armnn::ITensorHandle> inputHandle = workloadFactory.CreateTensorHandle(inputTensorInfo);
562 std::unique_ptr<armnn::ITensorHandle> outputHandle = workloadFactory.CreateTensorHandle(outputTensorInfo);
563
564 armnn::DepthwiseConvolution2dQueueDescriptor data;
565 armnn::WorkloadInfo info;
566 armnn::ScopedCpuTensorHandle weightsTensor(kernelDesc);
567 armnn::ScopedCpuTensorHandle biasTensor(biasDesc);
568
569 AllocateAndCopyDataToITensorHandle(&weightsTensor, &kernel[0][0][0][0]);
570 AllocateAndCopyDataToITensorHandle(&biasTensor, &bias[0]);
571
572 AddInputToWorkload(data, info, inputTensorInfo, inputHandle.get());
573 AddOutputToWorkload(data, info, outputTensorInfo, outputHandle.get());
574
575 data.m_Weight = &weightsTensor;
telsoa01c577f2c2018-08-31 09:22:23 +0100576 data.m_Bias = &biasTensor; // Still set this whether or not bias is enabled.
telsoa014fcda012018-03-09 14:13:49 +0000577 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
585 std::unique_ptr<armnn::IWorkload> workload = workloadFactory.CreateDepthwiseConvolution2d(data, info);
586 inputHandle->Allocate();
587 outputHandle->Allocate();
588
589 CopyDataToITensorHandle(inputHandle.get(), &input[0][0][0][0]);
590
surmeh013537c2c2018-05-18 16:31:43 +0100591 workloadFactory.Finalize();
telsoa014fcda012018-03-09 14:13:49 +0000592 workload->Execute();
593
594 CopyDataFromITensorHandle(&ret.output[0][0][0][0], outputHandle.get());
595
596 return ret;
597}
598
599template<typename T, typename B>
600LayerTestResult<T, 4> DepthwiseConvolution2dTestImpl(armnn::IWorkloadFactory& workloadFactory,
601 float qScale,
602 int32_t qOffset,
603 bool biasEnabled)
604{
605 unsigned int depthMultiplier = 2;
606
607 unsigned int inputHeight = 8;
608 unsigned int inputWidth = 16;
609 unsigned int inputChannels = 2;
610 unsigned int inputBatchSize = 1;
611
612 unsigned int kernelHeight = 5;
613 unsigned int kernelWidth = 3;
614
615 unsigned int outputHeight = inputHeight - kernelHeight + 1 + 2;
616 unsigned int outputWidth = (inputWidth - kernelWidth + 1)/2;
617 unsigned int outputChannels = inputChannels * depthMultiplier;
618 unsigned int outputBatchSize = inputBatchSize;
619
620 armnn::TensorInfo inputTensorInfo({inputBatchSize, inputChannels, inputHeight, inputWidth},
621 armnn::GetDataType<T>());
622 armnn::TensorInfo outputTensorInfo({outputBatchSize, outputChannels, outputHeight, outputWidth},
623 armnn::GetDataType<T>());
624 armnn::TensorInfo kernelDesc({depthMultiplier, inputChannels, kernelHeight, kernelWidth}, armnn::GetDataType<T>());
625 armnn::TensorInfo biasDesc({outputChannels}, armnn::GetDataType<B>());
626
627 // Set quantization parameters if the requested type is a quantized type.
628 if(armnn::IsQuantizedType<T>())
629 {
630 inputTensorInfo.SetQuantizationScale(qScale);
631 inputTensorInfo.SetQuantizationOffset(qOffset);
632 outputTensorInfo.SetQuantizationScale(qScale);
633 outputTensorInfo.SetQuantizationOffset(qOffset);
634 kernelDesc.SetQuantizationScale(qScale);
635 kernelDesc.SetQuantizationOffset(qOffset);
636 biasDesc.SetQuantizationScale(qScale*qScale);
637 biasDesc.SetQuantizationOffset(0);
638 }
639
640 auto input = MakeTensor<T, 4>(inputTensorInfo, std::vector<T>(
641 QuantizedVector<T>(inputTensorInfo.GetQuantizationScale(), inputTensorInfo.GetQuantizationOffset(), {
642 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,
643 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,
644 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,
645 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,
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.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,
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, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
651 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
652 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
653 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
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 })));
659
660 std::vector<B> biasV(QuantizedVector<B>(biasDesc.GetQuantizationScale(), biasDesc.GetQuantizationOffset(),
661 {0, 2, 1, -1}));
662 auto bias = MakeTensor<B, 1>(biasDesc, biasV);
663
664 auto kernel = MakeTensor<T, 4>(kernelDesc, std::vector<T>(
665 QuantizedVector<T>(kernelDesc.GetQuantizationScale(), kernelDesc.GetQuantizationOffset(), {
666 1, 1, 1,
667 1, -1, 1,
668 1, 1, 1,
669 1, 1, 1,
670 1, 1, 1,
671
672 2, 2, 2,
673 2, 2, 2,
674 2, 2, 2,
675 2, 2, 2,
676 2, 2, 2,
677
678 0, 0, 0,
679 0, -1, 0,
680 0, 0, 0,
681 0, 0, 0,
682 0, 0, 0,
683
684 0, 0, 0,
685 0, 0, 0,
686 0, 1, 0,
687 0, 0, 0,
688 0, 0, 0
689 })));
690
telsoa01c577f2c2018-08-31 09:22:23 +0100691 // Manually calculated.
telsoa014fcda012018-03-09 14:13:49 +0000692 std::vector<T> outputImage = std::vector<T>(
693 QuantizedVector<T>(outputTensorInfo.GetQuantizationScale(), outputTensorInfo.GetQuantizationOffset(), {
694 3.5f, 3.5f, 3.5f, 3.5f, 3.5f, 3.5f, 3.5f,
695 6.0f, 6.0f, 6.0f, 6.0f, 6.0f, 6.0f, 6.0f,
696 5.0f, 5.0f, 5.0f, 5.0f, 5.0f, 5.0f, 5.0f,
697 6.5f, 6.5f, 6.5f, 6.5f, 6.5f, 6.5f, 6.5f,
698 6.5f, 6.5f, 6.5f, 6.5f, 6.5f, 6.5f, 6.5f,
699 5.0f, 5.0f, 5.0f, 5.0f, 5.0f, 5.0f, 5.0f,
700
701 -0.5f, -0.5f, -0.5f, -0.5f, -0.5f, -0.5f, -0.5f,
702 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
703 -0.5f, -0.5f, -0.5f, -0.5f, -0.5f, -0.5f, -0.5f,
704 -0.5f, -0.5f, -0.5f, -0.5f, -0.5f, -0.5f, -0.5f,
705 -0.5f, -0.5f, -0.5f, -0.5f, -0.5f, -0.5f, -0.5f,
706 -0.5f, -0.5f, -0.5f, -0.5f, -0.5f, -0.5f, -0.5f,
707
708 8.0f, 8.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
709 10.0f, 10.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
710 10.0f, 10.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
711 10.0f, 10.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
712 10.0f, 10.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
713 8.0f, 8.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
714
715 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
716 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
717 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
718 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
719 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
720 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f
721 }));
722
telsoa01c577f2c2018-08-31 09:22:23 +0100723 // Optionally apply bias to output image.
telsoa014fcda012018-03-09 14:13:49 +0000724 if(biasEnabled)
725 {
726 ApplyBias(outputImage, outputTensorInfo.GetQuantizationScale(), outputTensorInfo.GetQuantizationOffset(),
727 biasV, biasDesc.GetQuantizationScale(), biasDesc.GetQuantizationOffset(),
728 outputWidth, outputHeight);
729 }
730
731 LayerTestResult<T, 4> ret(outputTensorInfo);
732 ret.outputExpected = MakeTensor<T, 4>(outputTensorInfo, outputImage);
733
734 std::unique_ptr<armnn::ITensorHandle> inputHandle = workloadFactory.CreateTensorHandle(inputTensorInfo);
735 std::unique_ptr<armnn::ITensorHandle> outputHandle = workloadFactory.CreateTensorHandle(outputTensorInfo);
736
737 armnn::DepthwiseConvolution2dQueueDescriptor data;
738 armnn::WorkloadInfo info;
739 armnn::ScopedCpuTensorHandle weightsTensor(kernelDesc);
740 armnn::ScopedCpuTensorHandle biasTensor(biasDesc);
741
742 AllocateAndCopyDataToITensorHandle(&weightsTensor, &kernel[0][0][0][0]);
743 AllocateAndCopyDataToITensorHandle(&biasTensor, &bias[0]);
744
745 AddInputToWorkload(data, info, inputTensorInfo, inputHandle.get());
746 AddOutputToWorkload(data, info, outputTensorInfo, outputHandle.get());
747
748 data.m_Weight = &weightsTensor;
telsoa01c577f2c2018-08-31 09:22:23 +0100749 data.m_Bias = &biasTensor; // Still set this whether or not bias is enabled.
telsoa014fcda012018-03-09 14:13:49 +0000750 data.m_Parameters.m_StrideX = 2;
751 data.m_Parameters.m_StrideY = 1;
752 data.m_Parameters.m_PadLeft = 0;
753 data.m_Parameters.m_PadRight = 0;
754 data.m_Parameters.m_PadTop = 1;
755 data.m_Parameters.m_PadBottom = 1;
756 data.m_Parameters.m_BiasEnabled = biasEnabled;
757
758 std::unique_ptr<armnn::IWorkload> workload = workloadFactory.CreateDepthwiseConvolution2d(data, info);
759 inputHandle->Allocate();
760 outputHandle->Allocate();
761
762 CopyDataToITensorHandle(inputHandle.get(), &input[0][0][0][0]);
763
surmeh013537c2c2018-05-18 16:31:43 +0100764 workloadFactory.Finalize();
telsoa014fcda012018-03-09 14:13:49 +0000765 workload->Execute();
766
767 CopyDataFromITensorHandle(&ret.output[0][0][0][0], outputHandle.get());
768
769 return ret;
770}
771
Nikhil Rajcec6b652018-10-12 13:51:57 +0100772template<typename T, typename B>
773LayerTestResult<T, 4> DepthwiseConvolution2dNhwcTestImpl(armnn::IWorkloadFactory& workloadFactory,
774 const boost::multi_array<T, 4>& input,
775 const boost::multi_array<T, 4>& kernel,
776 const boost::multi_array<B, 1>& bias,
777 const boost::multi_array<T, 4>& outputExpected,
778 float qScale,
779 int32_t qOffset,
780 uint32_t padLeft = 0,
781 uint32_t padTop = 0,
782 uint32_t padRight = 0,
783 uint32_t padBottom = 0,
784 uint32_t strideX = 1,
785 uint32_t strideY = 1)
786{
787 unsigned int inputNum = boost::numeric_cast<unsigned int>(input.shape()[0]);
788 unsigned int inputChannels = boost::numeric_cast<unsigned int>(input.shape()[3]);
789 unsigned int inputHeight = boost::numeric_cast<unsigned int>(input.shape()[1]);
790 unsigned int inputWidth = boost::numeric_cast<unsigned int>(input.shape()[2]);
791
792 unsigned int kernelChanMul = boost::numeric_cast<unsigned int>(kernel.shape()[0]);
793 unsigned int kernelChannels = boost::numeric_cast<unsigned int>(kernel.shape()[3]);
794 unsigned int kernelHeight = boost::numeric_cast<unsigned int>(kernel.shape()[1]);
795 unsigned int kernelWidth = boost::numeric_cast<unsigned int>(kernel.shape()[2]);
796
797 unsigned int outputNum = boost::numeric_cast<unsigned int>(outputExpected.shape()[0]);
798 unsigned int outputChannels = boost::numeric_cast<unsigned int>(outputExpected.shape()[3]);
799 unsigned int outputHeight = boost::numeric_cast<unsigned int>(outputExpected.shape()[1]);
800 unsigned int outputWidth = boost::numeric_cast<unsigned int>(outputExpected.shape()[2]);
801
802 // Creates the tensors.
803 armnn::TensorInfo inputTensorInfo({inputNum, inputHeight, inputWidth, inputChannels}, armnn::GetDataType<T>());
804 armnn::TensorInfo outputTensorInfo({outputNum, outputHeight, outputWidth, outputChannels},
805 armnn::GetDataType<T>());
806 armnn::TensorInfo kernelDesc({kernelChanMul, kernelHeight, kernelWidth, kernelChannels}, armnn::GetDataType<T>());
807 armnn::TensorInfo biasDesc({static_cast<unsigned int>(bias.size())}, armnn::GetDataType<B>());
808
809 // Set quantization parameters if the requested type is a quantized type.
810 if (armnn::IsQuantizedType<T>())
811 {
812 inputTensorInfo.SetQuantizationScale(qScale);
813 inputTensorInfo.SetQuantizationOffset(qOffset);
814 outputTensorInfo.SetQuantizationScale(qScale);
815 outputTensorInfo.SetQuantizationOffset(qOffset);
816 kernelDesc.SetQuantizationScale(qScale);
817 kernelDesc.SetQuantizationOffset(qOffset);
818 biasDesc.SetQuantizationScale(qScale*qScale);
819 biasDesc.SetQuantizationOffset(0);
820 }
821
822 // Construct the input data.
823 std::vector<T> inputData;
824 inputData.assign(input.data(), input.data() + inputHeight*inputWidth*inputChannels);
825 auto batchedInput = MakeTensor<T, 4>(inputTensorInfo, inputData);
826
827 // Construct the output data, with bias applied, as appropriate.
828 std::vector<T> outputData;
829 outputData.assign(outputExpected.data(), outputExpected.data() + outputHeight*outputWidth*outputChannels);
830
831 LayerTestResult<T, 4> ret(outputTensorInfo);
832 ret.outputExpected = MakeTensor<T, 4>(outputTensorInfo, outputData);
833
834 std::unique_ptr<armnn::ITensorHandle> inputHandle = workloadFactory.CreateTensorHandle(inputTensorInfo);
835 std::unique_ptr<armnn::ITensorHandle> outputHandle = workloadFactory.CreateTensorHandle(outputTensorInfo);
836
837 armnn::ScopedCpuTensorHandle weightsTensor(kernelDesc);
838 AllocateAndCopyDataToITensorHandle(&weightsTensor, &kernel[0][0][0][0]);
839
840 armnn::ScopedCpuTensorHandle biasTensor(biasDesc);
841
842 armnn::DepthwiseConvolution2dQueueDescriptor data;
843 data.m_Weight = &weightsTensor;
844 data.m_Bias = &biasTensor; // Still set this whether or not bias is enabled - it can be a source of bugs.
845 data.m_Parameters.m_StrideX = strideX;
846 data.m_Parameters.m_StrideY = strideY;
847 data.m_Parameters.m_PadLeft = padLeft;
848 data.m_Parameters.m_PadRight = padRight;
849 data.m_Parameters.m_PadTop = padTop;
850 data.m_Parameters.m_PadBottom = padBottom;
851 data.m_Parameters.m_DataLayout = armnn::DataLayout::NHWC;
852
853 armnn::WorkloadInfo info;
854 AddInputToWorkload(data, info, inputTensorInfo, inputHandle.get());
855 AddOutputToWorkload(data, info, outputTensorInfo, outputHandle.get());
856
857 std::unique_ptr<armnn::IWorkload> workload = workloadFactory.CreateDepthwiseConvolution2d(data, info);
858
859 inputHandle->Allocate();
860 outputHandle->Allocate();
861
862 CopyDataToITensorHandle(inputHandle.get(), &batchedInput[0][0][0][0]);
863
864 workloadFactory.Finalize();
865 workload->Execute();
866
867 CopyDataFromITensorHandle(&ret.output[0][0][0][0], outputHandle.get());
868
869 return ret;
870}
871
telsoa014fcda012018-03-09 14:13:49 +0000872template<typename T>
873LayerTestResult<T,4> Convolution1dTestImpl(armnn::IWorkloadFactory& workloadFactory,
874 float qScale,
875 int32_t qOffset,
876 bool biasEnabled)
877{
878 using B = typename FullyConnectedBiasTypeForInputType<T>::Type;
879
telsoa01c577f2c2018-08-31 09:22:23 +0100880 // Until we have a specialist 1D convolution layer, we can fake one using
telsoa014fcda012018-03-09 14:13:49 +0000881 // 2D convolution with the final dimension set to 1.
882 // I don't anticipate this being particularly slow, given that convolution is implemented
883 // as a matrix multiplication, at which point dimension doesn't matter.
884
885 unsigned int batchSize = 1;
886 unsigned int inputChannels = 2;
887 unsigned int outputChannels = 3;
telsoa01c577f2c2018-08-31 09:22:23 +0100888 unsigned int inputSize = 5; // The 1D size (could view as 'width' or 'height').
telsoa014fcda012018-03-09 14:13:49 +0000889 unsigned int kernelSize = 3;
890 unsigned int padSize = 2;
891 unsigned int stride = 1;
telsoa01c577f2c2018-08-31 09:22:23 +0100892 unsigned int outputSize = 7; // (inputSize + 2 * padSize - kernelSize + 1) / stride.
telsoa014fcda012018-03-09 14:13:49 +0000893
894 armnn::TensorInfo inputInfo({batchSize, inputChannels, inputSize, 1}, armnn::GetDataType<T>());
895 armnn::TensorInfo outputInfo({batchSize, outputChannels, outputSize, 1}, armnn::GetDataType<T>());
896 armnn::TensorInfo kernelInfo({outputChannels, inputChannels, kernelSize, 1}, armnn::GetDataType<T>());
897 armnn::TensorInfo biasInfo({outputChannels}, armnn::GetDataType<B>());
898
899 // Set quantization parameters if the requested type is a quantized type.
900 if(armnn::IsQuantizedType<T>())
901 {
902 inputInfo.SetQuantizationScale(qScale);
903 inputInfo.SetQuantizationOffset(qOffset);
904 outputInfo.SetQuantizationScale(qScale);
905 outputInfo.SetQuantizationOffset(qOffset);
906 kernelInfo.SetQuantizationScale(qScale);
907 kernelInfo.SetQuantizationOffset(qOffset);
908 biasInfo.SetQuantizationScale(inputInfo.GetQuantizationScale()*kernelInfo.GetQuantizationScale());
909 biasInfo.SetQuantizationOffset(0);
910 }
911
912 std::vector<T> inputData(
913 QuantizedVector<T>(inputInfo.GetQuantizationScale(), inputInfo.GetQuantizationOffset(), {
914 5.0f, -2.0f, 2.5f, 0.0f, 1.0f,
915 -3.0f, 3.2f, 5.0f, 2.0f, 3.0f,
916 }));
917
918 std::vector<T> kernelData(
919 QuantizedVector<T>(kernelInfo.GetQuantizationScale(), kernelInfo.GetQuantizationOffset(), {
920 1.0f, 0.0f, 0.0f,
921 0.0f, 2.0f, -1.5f,
922
923 0.0f, 0.0f, 0.0f,
924 0.2f, 0.2f, 0.2f,
925
926 0.5f, 0.0f, 0.5f,
927 0.0f, -1.0f, 0.0f
928 }));
929
930 std::vector<B> biasData(
931 QuantizedVector<B>(biasInfo.GetQuantizationScale(), biasInfo.GetQuantizationOffset(), {
932 1.0f, 0.0f, 0.0f
933 }));
934
935 std::vector<T> outputData(
936 QuantizedVector<T>(outputInfo.GetQuantizationScale(), outputInfo.GetQuantizationOffset(), {
937 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,
938 -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,
939 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
940 }));
941
telsoa01c577f2c2018-08-31 09:22:23 +0100942 // Optionally apply bias to output image.
telsoa014fcda012018-03-09 14:13:49 +0000943 if(biasEnabled)
944 {
945 ApplyBias(outputData, outputInfo.GetQuantizationScale(), outputInfo.GetQuantizationOffset(),
946 biasData, biasInfo.GetQuantizationScale(), biasInfo.GetQuantizationOffset(),
947 1, outputSize);
948 }
949
950 std::unique_ptr<armnn::ITensorHandle> inputHandle = workloadFactory.CreateTensorHandle(inputInfo);
951 std::unique_ptr<armnn::ITensorHandle> outputHandle = workloadFactory.CreateTensorHandle(outputInfo);
952
953 armnn::Convolution2dQueueDescriptor data;
954 armnn::WorkloadInfo info;
955 armnn::ScopedCpuTensorHandle weightsTensor(kernelInfo);
956 armnn::ScopedCpuTensorHandle biasTensor(biasInfo);
957
958 AllocateAndCopyDataToITensorHandle(&weightsTensor, kernelData.data());
959 AllocateAndCopyDataToITensorHandle(&biasTensor, biasData.data());
960
961 AddInputToWorkload(data, info, inputInfo, inputHandle.get());
962 AddOutputToWorkload(data, info, outputInfo, outputHandle.get());
963
964 data.m_Weight = &weightsTensor;
965 data.m_Bias = &biasTensor;
966 data.m_Parameters.m_StrideX = 1;
967 data.m_Parameters.m_StrideY = stride;
968 data.m_Parameters.m_PadLeft = 0;
969 data.m_Parameters.m_PadRight = 0;
970 data.m_Parameters.m_PadTop = padSize;
971 data.m_Parameters.m_PadBottom = padSize;
972 data.m_Parameters.m_BiasEnabled = biasEnabled;
973
974 std::unique_ptr<armnn::IWorkload> workload = workloadFactory.CreateConvolution2d(data, info);
975 inputHandle->Allocate();
976 outputHandle->Allocate();
977
978 CopyDataToITensorHandle(inputHandle.get(), inputData.data());
979
surmeh013537c2c2018-05-18 16:31:43 +0100980 workloadFactory.Finalize();
telsoa014fcda012018-03-09 14:13:49 +0000981 workload->Execute();
982
telsoa01c577f2c2018-08-31 09:22:23 +0100983 // Output
telsoa014fcda012018-03-09 14:13:49 +0000984 LayerTestResult<T,4> ret(outputInfo);
985 CopyDataFromITensorHandle(&ret.output[0][0][0][0], outputHandle.get());
986 ret.outputExpected = MakeTensor<T, 4>(outputInfo, outputData);
987 return ret;
988}
989
990
991
992template<typename T>
993LayerTestResult<T,4> CompareConvolution2dTestImpl(armnn::IWorkloadFactory& workloadFactory,
994 armnn::IWorkloadFactory& refWorkloadFactory)
995{
996 unsigned int inputHeight = 8;
997 unsigned int inputWidth = 16;
998 unsigned int inputChannels = 3;
999 unsigned int inputNum = 5;
1000
1001 unsigned int kernelHeight = 3;
1002 unsigned int kernelWidth = 3;
1003
1004 unsigned int strideX = 2;
1005 unsigned int strideY = 3;
1006 unsigned int padX = 1;
1007 unsigned int padY = 1;
1008
1009 unsigned int outputNum = inputNum;
1010 unsigned int outputChannels = 2;
1011 unsigned int outputHeight = (inputHeight + 2 * padY - kernelHeight + strideY) / strideY;
1012 unsigned int outputWidth = (inputWidth + 2 * padX - kernelWidth + strideX) / strideX;
1013
1014 armnn::TensorInfo inputTensorInfo;
1015 armnn::TensorInfo outputTensorInfo;
1016 armnn::TensorInfo kernelDesc;
1017 armnn::TensorInfo biasDesc;
1018
1019 unsigned int inputShape[] = {inputNum, inputChannels, inputHeight, inputWidth};
1020 unsigned int outputShape[] = {outputNum, outputChannels, outputHeight, outputWidth};
1021 unsigned int kernelShape[] = {outputChannels, inputChannels, kernelHeight, kernelWidth};
1022 unsigned int biasShape[] = {outputChannels};
1023
1024 inputTensorInfo = armnn::TensorInfo(4, inputShape, armnn::GetDataType<T>());
1025 outputTensorInfo = armnn::TensorInfo(4, outputShape, armnn::GetDataType<T>());
1026 kernelDesc = armnn::TensorInfo(4, kernelShape, armnn::GetDataType<T>());
1027 biasDesc = armnn::TensorInfo(1, biasShape, armnn::GetDataType<T>());
1028
1029 LayerTestResult<T,4> ret(outputTensorInfo);
1030
1031 auto input = MakeRandomTensor<T, 4>(inputTensorInfo, 124908);
1032 auto kernel = MakeRandomTensor<T, 4>(kernelDesc, 891234);
1033 auto bias = MakeRandomTensor<T, 1>(biasDesc, 1028);
1034
1035 std::unique_ptr<armnn::ITensorHandle> inputHandle = workloadFactory.CreateTensorHandle(inputTensorInfo);
1036 std::unique_ptr<armnn::ITensorHandle> outputHandle = workloadFactory.CreateTensorHandle(outputTensorInfo);
1037
1038 armnn::Convolution2dQueueDescriptor data;
1039 armnn::WorkloadInfo info;
1040 armnn::ScopedCpuTensorHandle weightsTensor(kernelDesc);
1041 armnn::ScopedCpuTensorHandle biasTensor(biasDesc);
1042
1043 AllocateAndCopyDataToITensorHandle(&weightsTensor, &kernel[0][0][0][0]);
1044 AllocateAndCopyDataToITensorHandle(&biasTensor, &bias[0]);
1045
1046 AddInputToWorkload(data, info, inputTensorInfo, inputHandle.get());
1047 AddOutputToWorkload(data, info, outputTensorInfo, outputHandle.get());
1048 data.m_Weight = &weightsTensor;
1049 data.m_Bias = &biasTensor;
1050 data.m_Parameters.m_StrideX = strideX;
1051 data.m_Parameters.m_StrideY = strideY;
1052 data.m_Parameters.m_PadLeft = padX;
1053 data.m_Parameters.m_PadRight = padX;
1054 data.m_Parameters.m_PadTop = padY;
1055 data.m_Parameters.m_PadBottom = padY;
1056 data.m_Parameters.m_BiasEnabled = true;
1057
1058 std::unique_ptr<armnn::ITensorHandle> outputHandleRef = refWorkloadFactory.CreateTensorHandle(outputTensorInfo);
1059 std::unique_ptr<armnn::ITensorHandle> inputHandleRef = refWorkloadFactory.CreateTensorHandle(inputTensorInfo);
1060
1061 armnn::Convolution2dQueueDescriptor refData = data;
1062 armnn::WorkloadInfo refInfo = info;
1063 SetWorkloadInput(refData, refInfo, 0, inputTensorInfo, inputHandleRef.get());
1064 SetWorkloadOutput(refData, refInfo, 0, outputTensorInfo, outputHandleRef.get());
1065
1066 std::unique_ptr<armnn::IWorkload> workload = workloadFactory.CreateConvolution2d(data, info);
1067 std::unique_ptr<armnn::IWorkload> workloadRef = refWorkloadFactory.CreateConvolution2d(refData, refInfo);
1068
1069 outputHandleRef->Allocate();
1070 inputHandleRef->Allocate();
1071
1072 inputHandle->Allocate();
1073 outputHandle->Allocate();
1074
1075 CopyDataToITensorHandle(inputHandle.get(), &input[0][0][0][0]);
1076 CopyDataToITensorHandle(inputHandleRef.get(), &input[0][0][0][0]);
1077
surmeh013537c2c2018-05-18 16:31:43 +01001078 workloadFactory.Finalize();
telsoa014fcda012018-03-09 14:13:49 +00001079 workload->Execute();
surmeh013537c2c2018-05-18 16:31:43 +01001080 refWorkloadFactory.Finalize();
telsoa014fcda012018-03-09 14:13:49 +00001081 workloadRef->Execute();
1082
1083 CopyDataFromITensorHandle(&ret.output[0][0][0][0], outputHandle.get());
1084 CopyDataFromITensorHandle(&ret.outputExpected[0][0][0][0], outputHandleRef.get());
1085
1086 return ret;
1087}
1088
1089template<typename T>
1090LayerTestResult<T, 4> CompareDepthwiseConvolution2dTestImpl(armnn::IWorkloadFactory& workloadFactory,
1091 armnn::IWorkloadFactory& refWorkloadFactory)
1092{
1093 unsigned int inputHeight = 8;
1094 unsigned int inputWidth = 16;
1095 unsigned int inputChannels = 3;
1096 unsigned int inputNum = 5;
1097
1098 unsigned int kernelHeight = 3;
1099 unsigned int kernelWidth = 3;
1100 unsigned int channelMultiplier = 1;
1101
1102 unsigned int strideX = 2;
1103 unsigned int strideY = 3;
1104 unsigned int padX = 1;
1105 unsigned int padY = 1;
1106
1107 unsigned int outputNum = inputNum;
1108 unsigned int outputChannels = inputChannels * channelMultiplier;
1109 unsigned int outputHeight = (inputHeight + 2 * padY - kernelHeight + strideY) / strideY;
1110 unsigned int outputWidth = (inputWidth + 2 * padX - kernelWidth + strideX) / strideX;
1111
1112 armnn::TensorInfo inputTensorInfo;
1113 armnn::TensorInfo outputTensorInfo;
1114 armnn::TensorInfo kernelDesc;
1115 armnn::TensorInfo biasDesc;
1116
1117 unsigned int inputShape[] = { inputNum, inputChannels, inputHeight, inputWidth };
1118 unsigned int outputShape[] = { outputNum, outputChannels, outputHeight, outputWidth };
1119 unsigned int kernelShape[] = { channelMultiplier, inputChannels, kernelHeight, kernelWidth };
1120 unsigned int biasShape[] = { outputChannels };
1121
1122 float inputsQScale = armnn::IsQuantizedType<T>() ? 1.0f : 0;
1123 float outputQScale = armnn::IsQuantizedType<T>() ? 2.0f : 0;
1124 int32_t qOffset = 0;
1125
1126 inputTensorInfo = armnn::TensorInfo(4, inputShape, armnn::GetDataType<T>(), inputsQScale, qOffset);
1127 outputTensorInfo = armnn::TensorInfo(4, outputShape, armnn::GetDataType<T>(), outputQScale, qOffset);
1128 kernelDesc = armnn::TensorInfo(4, kernelShape, armnn::GetDataType<T>(), inputsQScale, qOffset);
1129 biasDesc = armnn::TensorInfo(1, biasShape, armnn::GetBiasDataType(armnn::GetDataType<T>()), inputsQScale, qOffset);
1130
1131 LayerTestResult<T, 4> ret(outputTensorInfo);
1132
1133 auto input = MakeRandomTensor<T, 4>(inputTensorInfo, 124908, 0.0f, 255.0f);
1134 auto kernel = MakeRandomTensor<T, 4>(kernelDesc, 891234, 0.0f, 255.0f);
1135 auto bias = MakeRandomTensor<typename FullyConnectedBiasTypeForInputType<T>::Type, 1>(biasDesc, 1028, 0.0f, 255.0f);
1136
1137 std::unique_ptr<armnn::ITensorHandle> inputHandle = workloadFactory.CreateTensorHandle(inputTensorInfo);
1138 std::unique_ptr<armnn::ITensorHandle> outputHandle = workloadFactory.CreateTensorHandle(outputTensorInfo);
1139
1140 armnn::DepthwiseConvolution2dQueueDescriptor data;
1141 armnn::WorkloadInfo info;
1142 armnn::ScopedCpuTensorHandle weightsTensor(kernelDesc);
1143 armnn::ScopedCpuTensorHandle biasTensor(biasDesc);
1144
1145 AllocateAndCopyDataToITensorHandle(&weightsTensor, &kernel[0][0][0][0]);
1146 AllocateAndCopyDataToITensorHandle(&biasTensor, &bias[0]);
1147
1148 AddInputToWorkload(data, info, inputTensorInfo, inputHandle.get());
1149 AddOutputToWorkload(data, info, outputTensorInfo, outputHandle.get());
1150 data.m_Weight = &weightsTensor;
1151 data.m_Bias = &biasTensor;
1152 data.m_Parameters.m_StrideX = strideX;
1153 data.m_Parameters.m_StrideY = strideY;
1154 data.m_Parameters.m_PadLeft = padX;
1155 data.m_Parameters.m_PadRight = padX;
1156 data.m_Parameters.m_PadTop = padY;
1157 data.m_Parameters.m_PadBottom = padY;
1158 data.m_Parameters.m_BiasEnabled = true;
1159
1160 std::unique_ptr<armnn::ITensorHandle> outputHandleRef = refWorkloadFactory.CreateTensorHandle(outputTensorInfo);
1161 std::unique_ptr<armnn::ITensorHandle> inputHandleRef = refWorkloadFactory.CreateTensorHandle(inputTensorInfo);
1162
1163 armnn::DepthwiseConvolution2dQueueDescriptor refData = data;
1164 armnn::WorkloadInfo refInfo = info;
1165 SetWorkloadInput(refData, refInfo, 0, inputTensorInfo, inputHandleRef.get());
1166 SetWorkloadOutput(refData, refInfo, 0, outputTensorInfo, outputHandleRef.get());
1167
1168 std::unique_ptr<armnn::IWorkload> workload = workloadFactory.CreateDepthwiseConvolution2d(data, info);
1169 std::unique_ptr<armnn::IWorkload> workloadRef = refWorkloadFactory.CreateDepthwiseConvolution2d(refData, refInfo);
1170
1171 outputHandleRef->Allocate();
1172 inputHandleRef->Allocate();
1173
1174 inputHandle->Allocate();
1175 outputHandle->Allocate();
1176
1177 CopyDataToITensorHandle(inputHandle.get(), &input[0][0][0][0]);
1178 CopyDataToITensorHandle(inputHandleRef.get(), &input[0][0][0][0]);
1179
surmeh013537c2c2018-05-18 16:31:43 +01001180 workloadFactory.Finalize();
telsoa014fcda012018-03-09 14:13:49 +00001181 workload->Execute();
surmeh013537c2c2018-05-18 16:31:43 +01001182 refWorkloadFactory.Finalize();
telsoa014fcda012018-03-09 14:13:49 +00001183 workloadRef->Execute();
1184
1185 CopyDataFromITensorHandle(&ret.output[0][0][0][0], outputHandle.get());
1186 CopyDataFromITensorHandle(&ret.outputExpected[0][0][0][0], outputHandleRef.get());
1187
1188 return ret;
1189}