1 //
2 // Copyright © 2017,2022-2023 Arm Ltd and Contributors. All rights reserved.
3 // SPDX-License-Identifier: MIT
4 //
5
6 #include "FullyConnectedTestImpl.hpp"
7
8
9 #include <armnnUtils/QuantizeHelper.hpp>
10
11 #include <armnn/backends/TensorHandle.hpp>
12
13 #include <DataTypeUtils.hpp>
14 #include <armnnTestUtils/TensorCopyUtils.hpp>
15 #include <armnnTestUtils/WorkloadTestUtils.hpp>
16
17 #include <armnnTestUtils/TensorHelpers.hpp>
18
19 //
20 // Implementation templates
21 //
22
23 template<typename T, typename B>
SimpleFullyConnectedTestImpl(armnn::IWorkloadFactory & workloadFactory,const armnn::IBackendInternal::IMemoryManagerSharedPtr & memoryManager,const armnn::ITensorHandleFactory & tensorHandleFactory,armnn::TensorInfo inputTensorInfo,armnn::TensorInfo outputTensorInfo,armnn::TensorInfo weightsTensorInfo,armnn::TensorInfo biasesTensorInfo,std::vector<T> & weights,std::vector<B> & bias,std::vector<T> & input,bool biasEnabled,bool transposeWeights,bool constantWeights)24 LayerTestResult<T, 2> SimpleFullyConnectedTestImpl(
25 armnn::IWorkloadFactory& workloadFactory,
26 const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
27 const armnn::ITensorHandleFactory& tensorHandleFactory,
28 armnn::TensorInfo inputTensorInfo,
29 armnn::TensorInfo outputTensorInfo,
30 armnn::TensorInfo weightsTensorInfo,
31 armnn::TensorInfo biasesTensorInfo,
32 std::vector<T>& weights,
33 std::vector<B>& bias,
34 std::vector<T>& input,
35 bool biasEnabled,
36 bool transposeWeights,
37 bool constantWeights)
38 {
39 std::unique_ptr<armnn::ITensorHandle> input0Handle = tensorHandleFactory.CreateTensorHandle(inputTensorInfo);
40 std::unique_ptr<armnn::ITensorHandle> input1Handle = tensorHandleFactory.CreateTensorHandle(weightsTensorInfo);
41 std::unique_ptr<armnn::ITensorHandle> outputHandle = tensorHandleFactory.CreateTensorHandle(outputTensorInfo);
42
43 std::vector<T> actualOutput(outputTensorInfo.GetNumElements());
44
45 armnn::FullyConnectedQueueDescriptor data;
46 armnn::WorkloadInfo info;
47
48 AddInputToWorkload(data, info, inputTensorInfo, input0Handle.get());
49 AddInputToWorkload(data, info, weightsTensorInfo, input1Handle.get());
50 AddOutputToWorkload(data, info, outputTensorInfo, outputHandle.get());
51
52 data.m_Parameters.m_BiasEnabled = biasEnabled;
53 data.m_Parameters.m_TransposeWeightMatrix = transposeWeights;
54 data.m_Parameters.m_ConstantWeights = constantWeights;
55
56 std::unique_ptr<armnn::ITensorHandle> input2Handle = nullptr;
57 if (biasEnabled)
58 {
59 input2Handle = tensorHandleFactory.CreateTensorHandle(biasesTensorInfo);
60 AddInputToWorkload(data, info, biasesTensorInfo, input2Handle.get());
61 }
62
63 std::unique_ptr<armnn::IWorkload> workload = workloadFactory.CreateWorkload(armnn::LayerType::FullyConnected,
64 data,
65 info);
66 LayerTestResult<T, 2> result(outputTensorInfo);
67
68 input0Handle->Allocate();
69 input1Handle->Allocate();
70 outputHandle->Allocate();
71 CopyDataToITensorHandle(input0Handle.get(), input.data());
72 CopyDataToITensorHandle(input1Handle.get(), weights.data());
73 if (biasEnabled)
74 {
75 input2Handle->Allocate();
76 CopyDataToITensorHandle(input2Handle.get(), bias.data());
77 }
78
79 ExecuteWorkload(*workload, memoryManager);
80
81 CopyDataFromITensorHandle(actualOutput.data(), outputHandle.get());
82 result.m_ActualData = actualOutput;
83
84 return result;
85 }
86
87 template<armnn::DataType ArmnnType, typename T>
FullyConnectedTest(armnn::IWorkloadFactory & workloadFactory,const armnn::IBackendInternal::IMemoryManagerSharedPtr & memoryManager,const armnn::ITensorHandleFactory & tensorHandleFactory,bool biasEnabled,bool constantWeights)88 LayerTestResult<T, 2> FullyConnectedTest(
89 armnn::IWorkloadFactory& workloadFactory,
90 const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
91 const armnn::ITensorHandleFactory& tensorHandleFactory,
92 bool biasEnabled,
93 bool constantWeights)
94 {
95 constexpr static unsigned int inputWidth = 3u;
96 constexpr static unsigned int inputHeight = 2u;
97 constexpr static unsigned int inputChannels = 1u;
98
99 constexpr static unsigned int inputSize = inputWidth * inputHeight * inputChannels;
100
101 constexpr static unsigned int outputChannels = 2u;
102
103 armnn::TensorInfo inputTensorInfo({ 1, inputChannels, inputHeight, inputWidth }, ArmnnType);
104 inputTensorInfo.SetQuantizationScale(0.1f);
105 inputTensorInfo.SetQuantizationOffset(63);
106
107 armnn::TensorInfo outputTensorInfo({ 1, outputChannels }, ArmnnType);
108 outputTensorInfo.SetQuantizationScale(5.f);
109 outputTensorInfo.SetQuantizationOffset(biasEnabled ? -50 : 10);
110
111 armnn::TensorInfo weightsDesc({ outputChannels, inputSize }, ArmnnType);
112 weightsDesc.SetQuantizationScale(0.2f);
113 weightsDesc.SetQuantizationOffset(93);
114
115 armnn::TensorInfo biasesDesc({ outputChannels }, GetBiasTypeFromWeightsType(weightsDesc.GetDataType()).value());
116 biasesDesc.SetQuantizationScale(inputTensorInfo.GetQuantizationScale() * weightsDesc.GetQuantizationScale());
117 biasesDesc.SetQuantizationOffset(0);
118
119 LayerTestResult<T, 2> result(outputTensorInfo);
120
121 std::vector<T> input = ConvertToDataType<ArmnnType>(
122 {
123 -1.2f, 6.1f, -3.5f,
124 18.8f, -5.5f, 2.9f
125 },
126 inputTensorInfo);
127
128 std::vector<T> weights = ConvertToDataType<ArmnnType>(
129 {
130 -8.4f, 20.0f, -10.4f, -8, 16.4f, -11.8f,
131 23.4f, 10.4f, -14.0f, -3.8f, -11.8f, 11.4f
132 },
133 weightsDesc);
134
135 std::vector<int32_t> bias = {9250, 67500};
136
137 result = SimpleFullyConnectedTestImpl<T>(workloadFactory,
138 memoryManager,
139 tensorHandleFactory,
140 inputTensorInfo,
141 outputTensorInfo,
142 weightsDesc,
143 biasesDesc,
144 weights,
145 bias,
146 input,
147 biasEnabled,
148 true,
149 constantWeights);
150
151 if (biasEnabled)
152 {
153 result.m_ExpectedData = ConvertToDataType<ArmnnType>({80.f, 1460.f}, outputTensorInfo);
154 }
155 else
156 {
157 result.m_ExpectedData = ConvertToDataType<ArmnnType>({-107.04f, 110.f}, outputTensorInfo);
158 }
159
160 return result;
161 }
162
163 //
164 // ArmNN variant of the AndroidNN fully_connected_float_large test.
165 //
166 // Tests the fully connected layer with large values, optionally transposing weights.
167 // Note this is templated for consistency, but the nature of this tests makes it unlikely to be useful in Uint8 mode.
168 //
169 template<armnn::DataType ArmnnType, typename T = armnn::ResolveType<ArmnnType>>
FullyConnectedLargeTestCommon(armnn::IWorkloadFactory & workloadFactory,const armnn::IBackendInternal::IMemoryManagerSharedPtr & memoryManager,const armnn::ITensorHandleFactory & tensorHandleFactory,bool transposeWeights,float qScale=1.0f,int32_t qOffset=0)170 LayerTestResult<T, 2> FullyConnectedLargeTestCommon(
171 armnn::IWorkloadFactory& workloadFactory,
172 const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
173 const armnn::ITensorHandleFactory& tensorHandleFactory,
174 bool transposeWeights,
175 float qScale = 1.0f,
176 int32_t qOffset = 0)
177 {
178 unsigned int inputWidth = 1;
179 unsigned int inputHeight = 1;
180 unsigned int inputChannels = 5;
181 unsigned int inputNum = 1;
182
183 unsigned int outputChannels = 1;
184 unsigned int outputNum = 1;
185
186 // Define the tensor descriptors.
187 armnn::TensorInfo inputTensorInfo;
188 armnn::TensorInfo outputTensorInfo;
189 armnn::TensorInfo weightsDesc;
190 armnn::TensorInfo biasesDesc;
191
192 unsigned int inputShape[] = { inputNum, inputChannels, inputHeight, inputWidth };
193 unsigned int outputShape[] = { outputNum, outputChannels };
194 unsigned int weightsShape[] = { inputChannels, outputChannels };
195 if (transposeWeights)
196 {
197 std::swap(weightsShape[0], weightsShape[1]);
198 }
199
200 unsigned int biasShape[] = { outputChannels };
201
202 inputTensorInfo = armnn::TensorInfo(4, inputShape, ArmnnType);
203 outputTensorInfo = armnn::TensorInfo(2, outputShape, ArmnnType);
204 weightsDesc = armnn::TensorInfo(2, weightsShape, ArmnnType);
205 biasesDesc = armnn::TensorInfo(1, biasShape, ArmnnType);
206
207 // Set quantization parameters if the requested type is a quantized type.
208 if(armnn::IsQuantizedType<T>())
209 {
210 inputTensorInfo.SetQuantizationScale(qScale);
211 inputTensorInfo.SetQuantizationOffset(qOffset);
212 outputTensorInfo.SetQuantizationScale(qScale);
213 outputTensorInfo.SetQuantizationOffset(qOffset);
214 }
215
216 LayerTestResult<T, 2> result(outputTensorInfo);
217
218 std::vector<T> input = armnnUtils::QuantizedVector<T>(
219 {
220 1.0f, 10.0f, 100.0f, 1000.0f, 10000.0f,
221 },
222 qScale, qOffset);
223
224 std::vector<T> weights = armnnUtils::QuantizedVector<T>(
225 {
226 2.0f, 3.0f, 4.0f, 5.0f, 6.0f
227 },
228 qScale, qOffset);
229
230 std::vector<T> biasValues({900000.f});
231
232 result = SimpleFullyConnectedTestImpl<T>(
233 workloadFactory,
234 memoryManager,
235 tensorHandleFactory,
236 inputTensorInfo, outputTensorInfo,
237 weightsDesc, biasesDesc,
238 weights, biasValues, input,
239 true, transposeWeights, true
240 );
241
242 result.m_ExpectedData = armnnUtils::QuantizedVector<T>({ 965432.0f }, qScale, qOffset);
243
244 return result;
245 }
246
247 //
248 // Explicit template specializations
249 //
250
251 template LayerTestResult<armnn::ResolveType<armnn::DataType::QAsymmU8>, 2>
252 FullyConnectedTest<armnn::DataType::QAsymmU8>(
253 armnn::IWorkloadFactory& workloadFactory,
254 const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
255 const armnn::ITensorHandleFactory& tensorHandleFactory,
256 bool biasEnabled,
257 bool constWeights);
258
259 template LayerTestResult<armnn::ResolveType<armnn::DataType::QSymmS16>, 2>
260 FullyConnectedTest<armnn::DataType::QSymmS16>(
261 armnn::IWorkloadFactory& workloadFactory,
262 const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
263 const armnn::ITensorHandleFactory& tensorHandleFactory,
264 bool biasEnabled,
265 bool constWeights);
266
267 //
268 // Implementation functions
269 //
270
FullyConnectedFloat32Test(armnn::IWorkloadFactory & workloadFactory,const armnn::IBackendInternal::IMemoryManagerSharedPtr & memoryManager,const armnn::ITensorHandleFactory & tensorHandleFactory,bool biasEnabled,bool transposeWeights)271 LayerTestResult<float, 2> FullyConnectedFloat32Test(
272 armnn::IWorkloadFactory& workloadFactory,
273 const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
274 const armnn::ITensorHandleFactory& tensorHandleFactory,
275 bool biasEnabled,
276 bool transposeWeights)
277 {
278 unsigned int inputWidth = 1;
279 unsigned int inputHeight = 1;
280 unsigned int inputChannels = 5;
281 unsigned int inputNum = 2;
282
283 unsigned int outputChannels = 3;
284 unsigned int outputNum = 2;
285
286 // Define the tensor descriptors.
287 armnn::TensorInfo inputTensorInfo;
288 armnn::TensorInfo outputTensorInfo;
289 armnn::TensorInfo weightsDesc;
290 armnn::TensorInfo biasesDesc;
291
292 unsigned int inputShape[] = { inputNum, inputChannels, inputHeight, inputWidth };
293 unsigned int outputShape[] = { outputNum, outputChannels };
294 unsigned int weightsShape[] = { inputChannels, outputChannels };
295
296 if (transposeWeights)
297 {
298 std::swap(weightsShape[0], weightsShape[1]);
299 }
300
301 unsigned int biasShape[] = { outputChannels };
302
303 inputTensorInfo = armnn::TensorInfo(4, inputShape, armnn::DataType::Float32);
304 outputTensorInfo = armnn::TensorInfo(2, outputShape, armnn::DataType::Float32);
305 weightsDesc = armnn::TensorInfo(2, weightsShape, armnn::DataType::Float32);
306 biasesDesc = armnn::TensorInfo(1, biasShape, armnn::DataType::Float32);
307
308 LayerTestResult<float, 2> result(outputTensorInfo);
309
310 std::vector<float> input =
311 {
312 1.0f, 2.0f, 3.0f, 4.0f, 5.0f,
313 5.0f, 4.0f, 3.0f, 2.0f, 1.0f
314 };
315
316 std::vector<float> weights =
317 {
318 .5f, 2.f, .5f,
319 .5f, 2.f, 1.f,
320 .5f, 2.f, 2.f,
321 .5f, 2.f, 3.f,
322 .5f, 2.f, 4.f
323 };
324
325 if (transposeWeights)
326 {
327 weights =
328 {
329 .5f, .5f, .5f, .5f, .5f,
330 2.f, 2.f, 2.f, 2.f, 2.f,
331 .5f, 1.f, 2.f, 3.f, 4.f
332 };
333 }
334
335 std::vector<float> biasValues({0.f, 0.f, 0.f});
336 if (biasEnabled)
337 {
338 biasValues = std::vector<float>({10.f, 20.f, 30.f});
339 }
340
341 result = SimpleFullyConnectedTestImpl<float>(
342 workloadFactory,
343 memoryManager,
344 tensorHandleFactory,
345 inputTensorInfo, outputTensorInfo,
346 weightsDesc, biasesDesc,
347 weights, biasValues, input,
348 biasEnabled, transposeWeights, true
349 );
350
351 std::vector<float> expectedOutput =
352 {
353 0.5f + 1.0f + 1.5f + 2.0f + 2.5f + biasValues[0],
354 2.0f + 4.0f + 6.0f + 8.0f + 10.f + biasValues[1],
355 0.5f + 2.0f + 6.0f + 12.f + 20.f + biasValues[2],
356
357 2.5f + 2.0f + 1.5f + 1.0f + 0.5f + biasValues[0],
358 10.0f + 8.0f + 6.0f + 4.0f + 2.f + biasValues[1],
359 2.5f + 4.0f + 6.0f + 6.f + 4.f + biasValues[2]
360 };
361 result.m_ExpectedData = expectedOutput;
362
363 return result;
364 }
365
FullyConnectedLargeTest(armnn::IWorkloadFactory & workloadFactory,const armnn::IBackendInternal::IMemoryManagerSharedPtr & memoryManager,const armnn::ITensorHandleFactory & tensorHandleFactory,bool transposeWeights)366 LayerTestResult<float, 2> FullyConnectedLargeTest(
367 armnn::IWorkloadFactory& workloadFactory,
368 const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager,
369 const armnn::ITensorHandleFactory& tensorHandleFactory,
370 bool transposeWeights)
371 {
372 return FullyConnectedLargeTestCommon<armnn::DataType::Float32>(workloadFactory,
373 memoryManager,
374 tensorHandleFactory,
375 transposeWeights);
376 }
377