xref: /aosp_15_r20/external/armnn/src/backends/backendsCommon/test/layerTests/FullyConnectedTestImpl.cpp (revision 89c4ff92f2867872bb9e2354d150bf0c8c502810)
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