Release 18.08
[platform/upstream/armnn.git] / src / armnn / backends / test / FullyConnectedTestImpl.hpp
1 //
2 // Copyright © 2017 Arm Ltd. All rights reserved.
3 // See LICENSE file in the project root for full license information.
4 //
5
6 template<typename T, typename B>
7 LayerTestResult<T, 2> SimpleFullyConnectedTestImpl(
8     armnn::IWorkloadFactory& workloadFactory,
9     armnn::TensorInfo inputTensorInfo,
10     armnn::TensorInfo outputTensorInfo,
11     armnn::TensorInfo weightsDesc,
12     armnn::TensorInfo biasesDesc,
13     boost::multi_array<T, 2>& weights,
14     boost::multi_array<B, 1>& bias,
15     boost::multi_array<T, 4>& input,
16     bool biasEnabled,
17     bool transposeWeights)
18 {
19     std::unique_ptr<armnn::ITensorHandle> inputHandle = workloadFactory.CreateTensorHandle(inputTensorInfo);
20     std::unique_ptr<armnn::ITensorHandle> outputHandle = workloadFactory.CreateTensorHandle(outputTensorInfo);
21
22     armnn::FullyConnectedQueueDescriptor data;
23     armnn::WorkloadInfo info;
24     armnn::ScopedCpuTensorHandle weightsTensor(weightsDesc);
25     armnn::ScopedCpuTensorHandle biasTensor(biasesDesc);
26
27     AllocateAndCopyDataToITensorHandle(&weightsTensor, &weights[0][0]);
28     AllocateAndCopyDataToITensorHandle(&biasTensor, &bias[0]);
29
30     AddInputToWorkload(data, info, inputTensorInfo, inputHandle.get());
31     AddOutputToWorkload(data, info, outputTensorInfo, outputHandle.get());
32     data.m_Weight = &weightsTensor;
33     data.m_Bias = &biasTensor;
34     data.m_Parameters.m_BiasEnabled = biasEnabled;
35     data.m_Parameters.m_TransposeWeightMatrix = transposeWeights;
36
37     std::unique_ptr<armnn::IWorkload> workload = workloadFactory.CreateFullyConnected(data, info);
38     LayerTestResult<T, 2> result(outputTensorInfo);
39
40     inputHandle->Allocate();
41     outputHandle->Allocate();
42     CopyDataToITensorHandle(inputHandle.get(), &input[0][0][0][0]);
43
44     workloadFactory.Finalize();
45     workload->Execute();
46
47     CopyDataFromITensorHandle(&result.output[0][0], outputHandle.get());
48
49     return result;
50 }
51
52 LayerTestResult<float, 2> FullyConnectedFloat32Test(armnn::IWorkloadFactory& workloadFactory, bool biasEnabled,
53     bool transposeWeights)
54 {
55     unsigned int inputWidth = 1;
56     unsigned int inputHeight = 1;
57     unsigned int inputChannels = 5;
58     unsigned int inputNum = 2;
59
60     unsigned int outputChannels = 3;
61     unsigned int outputNum = 2;
62
63     // Define the tensor descriptors.
64     armnn::TensorInfo inputTensorInfo;
65     armnn::TensorInfo outputTensorInfo;
66     armnn::TensorInfo weightsDesc;
67     armnn::TensorInfo biasesDesc;
68
69     unsigned int inputShape[] = { inputNum, inputChannels, inputHeight, inputWidth };
70     unsigned int outputShape[] = { outputNum, outputChannels };
71     unsigned int weightsShape[] = { inputChannels, outputChannels };
72     if (transposeWeights)
73     {
74         std::swap(weightsShape[0], weightsShape[1]);
75     }
76     unsigned int biasShape[] = { outputChannels };
77
78     inputTensorInfo = armnn::TensorInfo(4, inputShape, armnn::DataType::Float32);
79     outputTensorInfo = armnn::TensorInfo(2, outputShape, armnn::DataType::Float32);
80     weightsDesc = armnn::TensorInfo(2, weightsShape, armnn::DataType::Float32);
81     biasesDesc = armnn::TensorInfo(1, biasShape, armnn::DataType::Float32);
82
83     LayerTestResult<float, 2> result(outputTensorInfo);
84
85     boost::multi_array<float, 4> input = MakeTensor<float, 4>(inputTensorInfo, std::vector<float>(
86         {
87             1.0f, 2.0f, 3.0f, 4.0f, 5.0f,
88
89             5.0f, 4.0f, 3.0f, 2.0f, 1.0f
90         })
91     );
92
93     boost::multi_array<float, 2> weights = MakeTensor<float, 2>(weightsDesc, std::vector<float>(
94         {
95             .5f, 2.f, .5f,
96             .5f, 2.f, 1.f,
97             .5f, 2.f, 2.f,
98             .5f, 2.f, 3.f,
99             .5f, 2.f, 4.f
100         }));
101
102     if (transposeWeights)
103     {
104         weights = MakeTensor<float, 2>(weightsDesc, std::vector<float>(
105         {
106             .5f, .5f, .5f, .5f, .5f,
107             2.f, 2.f, 2.f, 2.f, 2.f,
108             .5f, 1.f, 2.f, 3.f, 4.f
109         }));
110     }
111
112
113     std::vector<float> biasValues({0.f, 0.f, 0.f});
114     if (biasEnabled)
115     {
116         biasValues =  std::vector<float>({10.f, 20.f, 30.f});
117     }
118     boost::multi_array<float, 1> bias = MakeTensor<float, 1>(biasesDesc, biasValues);
119
120     result = SimpleFullyConnectedTestImpl<float>(
121         workloadFactory,
122         inputTensorInfo, outputTensorInfo,
123         weightsDesc, biasesDesc,
124         weights, bias, input,
125         biasEnabled, transposeWeights
126     );
127
128     result.outputExpected = MakeTensor<float, 2>(outputTensorInfo, std::vector<float>(
129         {
130             0.5f + 1.0f + 1.5f + 2.0f + 2.5f + biasValues[0],
131             2.0f + 4.0f + 6.0f + 8.0f + 10.f + biasValues[1],
132             0.5f + 2.0f + 6.0f + 12.f + 20.f + biasValues[2],
133
134             2.5f + 2.0f + 1.5f + 1.0f + 0.5f + biasValues[0],
135             10.0f + 8.0f + 6.0f + 4.0f + 2.f + biasValues[1],
136             2.5f + 4.0f + 6.0f + 6.f + 4.f   + biasValues[2]
137         })
138     );
139
140     return result;
141 }
142
143 LayerTestResult<uint8_t, 2> FullyConnectedUint8Test(armnn::IWorkloadFactory& workloadFactory, bool biasEnabled)
144 {
145     constexpr static unsigned int inputWidth = 3u;
146     constexpr static unsigned int inputHeight = 2u;
147     constexpr static unsigned int inputChannels = 1u;
148
149     constexpr static unsigned int inputSize = inputWidth * inputHeight * inputChannels;
150
151     constexpr static unsigned int outputChannels = 2u;
152
153     armnn::TensorInfo inputTensorInfo({ 1, inputChannels, inputHeight, inputWidth }, armnn::DataType::QuantisedAsymm8);
154     inputTensorInfo.SetQuantizationScale(0.1f);
155     inputTensorInfo.SetQuantizationOffset(63);
156
157     armnn::TensorInfo outputTensorInfo({ 1, outputChannels }, armnn::DataType::QuantisedAsymm8);
158     outputTensorInfo.SetQuantizationScale(5.f);
159     outputTensorInfo.SetQuantizationOffset(biasEnabled ? -50 : 10);
160
161     armnn::TensorInfo weightsDesc({ outputChannels, inputSize }, armnn::DataType::QuantisedAsymm8);
162     weightsDesc.SetQuantizationScale(0.2f);
163     weightsDesc.SetQuantizationOffset(93);
164
165     armnn::TensorInfo biasesDesc({ outputChannels }, armnn::DataType::Signed32);
166     biasesDesc.SetQuantizationScale(inputTensorInfo.GetQuantizationScale() * weightsDesc.GetQuantizationScale());
167     biasesDesc.SetQuantizationOffset(0);
168
169     LayerTestResult<uint8_t, 2> result(outputTensorInfo);
170
171     auto input = MakeTensor<uint8_t, 4>(inputTensorInfo, std::vector<uint8_t>{51, 124, 28,
172         251, 8, 92});
173
174     auto weights = MakeTensor<uint8_t, 2>(weightsDesc, std::vector<uint8_t>{51, 193, 42, 53, 175, 34,
175         210, 145, 23, 74, 34, 150});
176
177         // scale = 0.02
178         // offset = 0
179     auto bias = MakeTensor<int32_t, 1>(biasesDesc, std::vector<int32_t>{9250, 67500});
180
181     result = SimpleFullyConnectedTestImpl<uint8_t>(
182         workloadFactory,
183         inputTensorInfo, outputTensorInfo,
184         weightsDesc, biasesDesc,
185         weights, bias, input,
186         biasEnabled, true
187     );
188
189     // Manually calculated.
190     // Note one of these values has been clamped to 0.
191     if (biasEnabled)
192     {
193         result.outputExpected = MakeTensor<uint8_t, 2>(outputTensorInfo, std::vector<uint8_t>{0, 242});
194     }
195     else
196     {
197         result.outputExpected = MakeTensor<uint8_t, 2>(outputTensorInfo, std::vector<uint8_t>{0, 32});
198     }
199
200     return result;
201 }
202
203
204
205 //
206 // ArmNN variant of the AndroidNN fully_connected_float_large test.
207 //
208 // Tests the fully connected layer with large values, optionally transposing weights.
209 // Note this is templated for consistency, but the nature of this tests makes it unlikely to be useful in Uint8 mode.
210 //
211 template<typename T>
212 LayerTestResult<T, 2> FullyConnectedLargeTestCommon(armnn::IWorkloadFactory& workloadFactory,
213                                                         bool transposeWeights,
214                                                         float qScale = 0.0f,
215                                                         int32_t qOffset = 0)
216 {
217     unsigned int inputWidth = 1;
218     unsigned int inputHeight = 1;
219     unsigned int inputChannels = 5;
220     unsigned int inputNum = 1;
221
222     unsigned int outputChannels = 1;
223     unsigned int outputNum = 1;
224
225     // Define the tensor descriptors.
226     armnn::TensorInfo inputTensorInfo;
227     armnn::TensorInfo outputTensorInfo;
228     armnn::TensorInfo weightsDesc;
229     armnn::TensorInfo biasesDesc;
230
231     unsigned int inputShape[] = { inputNum, inputChannels, inputHeight, inputWidth };
232     unsigned int outputShape[] = { outputNum, outputChannels };
233     unsigned int weightsShape[] = { inputChannels, outputChannels };
234     if (transposeWeights)
235     {
236         std::swap(weightsShape[0], weightsShape[1]);
237     }
238
239     unsigned int biasShape[] = { outputChannels };
240
241     inputTensorInfo = armnn::TensorInfo(4, inputShape, armnn::GetDataType<T>());
242     outputTensorInfo = armnn::TensorInfo(2, outputShape, armnn::GetDataType<T>());
243     weightsDesc = armnn::TensorInfo(2, weightsShape, armnn::GetDataType<T>());
244     biasesDesc = armnn::TensorInfo(1, biasShape, armnn::GetDataType<T>());
245
246     // Set quantization parameters if the requested type is a quantized type.
247     if(armnn::IsQuantizedType<T>())
248     {
249         inputTensorInfo.SetQuantizationScale(qScale);
250         inputTensorInfo.SetQuantizationOffset(qOffset);
251         outputTensorInfo.SetQuantizationScale(qScale);
252         outputTensorInfo.SetQuantizationOffset(qOffset);
253     }
254
255     LayerTestResult<T, 2> result(outputTensorInfo);
256
257     boost::multi_array<T, 4> input = MakeTensor<T, 4>(inputTensorInfo,
258         QuantizedVector<T>(qScale, qOffset, {
259             1.0f, 10.0f, 100.0f, 1000.0f, 10000.0f,
260         })
261     );
262
263     boost::multi_array<T, 2> weights = MakeTensor<T, 2>(weightsDesc,
264         QuantizedVector<T>(qScale, qOffset, {
265             2.0f, 3.0f, 4.0f, 5.0f, 6.0f
266         })
267     );
268
269     std::vector<T> biasValues({900000.f});
270     boost::multi_array<T, 1> bias = MakeTensor<T, 1>(biasesDesc, biasValues);
271
272     result = SimpleFullyConnectedTestImpl<T>(
273         workloadFactory,
274         inputTensorInfo, outputTensorInfo,
275         weightsDesc, biasesDesc,
276         weights, bias, input,
277         true, transposeWeights
278     );
279
280     result.outputExpected = MakeTensor<T, 2>(outputTensorInfo,
281         QuantizedVector<T>(qScale, qOffset, {
282             965432.0f,
283         })
284     );
285
286     return result;
287 }