ArmNN
 21.11
FullyConnectedEndToEndTestImpl.hpp
Go to the documentation of this file.
1 //
2 // Copyright © 2021 Arm Ltd and Contributors. All rights reserved.
3 // SPDX-License-Identifier: MIT
4 //
5 #pragma once
6 
7 #include "CommonTestUtils.hpp"
8 
9 #include <ResolveType.hpp>
10 
11 #include <armnn/INetwork.hpp>
12 
14 
15 #include <doctest/doctest.h>
16 
17 #include <vector>
18 
19 namespace
20 {
21 
22 armnn::INetworkPtr CreateFullyConnectedNetworkNonConstWeights(const armnn::TensorInfo& inputTensorInfo,
23  const armnn::TensorInfo& outputTensorInfo,
24  const armnn::TensorInfo& weightsTensorInfo,
26 {
28 
29  armnn::IConnectableLayer* inputLayer = network->AddInputLayer(0, "Input");
30  armnn::IConnectableLayer* weightsInputLayer = network->AddInputLayer(1, "Weights_Input");
31  armnn::IConnectableLayer* fullyConnectedLayer = network->AddFullyConnectedLayer(descriptor, "Fully_Connected");
32  armnn::IConnectableLayer* outputLayer = network->AddOutputLayer(0, "Output");
33 
34  Connect(inputLayer, fullyConnectedLayer, inputTensorInfo, 0, 0);
35  Connect(weightsInputLayer, fullyConnectedLayer, weightsTensorInfo, 0, 1);
36  Connect(fullyConnectedLayer, outputLayer, outputTensorInfo, 0, 0);
37 
38  return network;
39 }
40 
41 armnn::INetworkPtr CreateFullyConnectedNetworkNonConstWeightsConstBias(const armnn::TensorInfo& inputTensorInfo,
42  const armnn::TensorInfo& outputTensorInfo,
43  const armnn::TensorInfo& weightsTensorInfo,
44  const armnn::TensorInfo& biasTensorInfo,
45  const armnn::ConstTensor& biasConstantTensor,
47 {
49 
50  armnn::IConnectableLayer* inputLayer = network->AddInputLayer(0, "Input");
51  armnn::IConnectableLayer* weightsInputLayer = network->AddInputLayer(1, "Weights_Input");
52  armnn::IConnectableLayer* biasLayer = network->AddConstantLayer(biasConstantTensor, "Weights");
53  armnn::IConnectableLayer* fullyConnectedLayer = network->AddFullyConnectedLayer(descriptor, "Fully_Connected");
54  armnn::IConnectableLayer* outputLayer = network->AddOutputLayer(0, "Output");
55 
56  Connect(inputLayer, fullyConnectedLayer, inputTensorInfo, 0, 0);
57  Connect(weightsInputLayer, fullyConnectedLayer, weightsTensorInfo, 0, 1);
58  Connect(biasLayer, fullyConnectedLayer, biasTensorInfo, 0, 2);
59  Connect(fullyConnectedLayer, outputLayer, outputTensorInfo, 0, 0);
60 
61  return network;
62 }
63 
64 armnn::INetworkPtr CreateFullyConnectedNetworkConstWeightsNonConstBias(const armnn::TensorInfo& inputTensorInfo,
65  const armnn::TensorInfo& outputTensorInfo,
66  const armnn::TensorInfo& weightsTensorInfo,
67  const armnn::TensorInfo& biasTensorInfo,
68  const armnn::ConstTensor& weightsConstantTensor,
70 {
72 
73  armnn::IConnectableLayer* inputLayer = network->AddInputLayer(0, "Input");
74  armnn::IConnectableLayer* weightsLayer = network->AddConstantLayer(weightsConstantTensor, "Weights");
75  armnn::IConnectableLayer* biasLayer = network->AddInputLayer(2, "Bias_Input");
76  armnn::IConnectableLayer* fullyConnectedLayer = network->AddFullyConnectedLayer(descriptor, "Fully_Connected");
77  armnn::IConnectableLayer* outputLayer = network->AddOutputLayer(0, "Output");
78 
79  Connect(inputLayer, fullyConnectedLayer, inputTensorInfo, 0, 0);
80  Connect(weightsLayer, fullyConnectedLayer, weightsTensorInfo, 0, 1);
81  Connect(biasLayer, fullyConnectedLayer, biasTensorInfo, 0, 2);
82  Connect(fullyConnectedLayer, outputLayer, outputTensorInfo, 0, 0);
83 
84  return network;
85 }
86 
87 armnn::INetworkPtr CreateFullyConnectedNetworkNoTensorInfoConstWeights(const armnn::TensorInfo& inputTensorInfo,
88  const armnn::TensorInfo& outputTensorInfo,
89  const armnn::ConstTensor& weightsConstantTensor,
91 {
93 
94  armnn::IConnectableLayer* inputLayer = network->AddInputLayer(0, "Input");
95  armnn::IConnectableLayer* weightsLayer = network->AddConstantLayer(weightsConstantTensor, "Weights");
96  armnn::IConnectableLayer* fullyConnectedLayer = network->AddFullyConnectedLayer(descriptor, "Fully_Connected");
97  armnn::IConnectableLayer* outputLayer = network->AddOutputLayer(0, "Output");
98 
99  Connect(inputLayer, fullyConnectedLayer, inputTensorInfo, 0, 0);
100  weightsLayer->GetOutputSlot(0).Connect(fullyConnectedLayer->GetInputSlot(1));
101  Connect(fullyConnectedLayer, outputLayer, outputTensorInfo, 0, 0);
102 
103  return network;
104 }
105 
106 armnn::INetworkPtr CreateFullyConnectedNetworkNoConnectedWeightsExplicit(const armnn::TensorInfo& inputTensorInfo,
107  const armnn::TensorInfo& outputTensorInfo,
108  const armnn::TensorInfo& biasTensorInfo,
110 {
112 
113  armnn::IConnectableLayer* inputLayer = network->AddInputLayer(0, "Input");
114  armnn::IConnectableLayer* biasLayer = network->AddInputLayer(2, "Bias_Input");
115  armnn::IConnectableLayer* fullyConnectedLayer = network->AddFullyConnectedLayer(descriptor, "Fully_Connected");
116  armnn::IConnectableLayer* outputLayer = network->AddOutputLayer(0, "Output");
117 
118  Connect(inputLayer, fullyConnectedLayer, inputTensorInfo, 0, 0);
119  Connect(biasLayer, fullyConnectedLayer, biasTensorInfo, 0, 2);
120  Connect(fullyConnectedLayer, outputLayer, outputTensorInfo, 0, 0);
121 
122  return network;
123 }
124 
125 armnn::INetworkPtr CreateFullyConnectedNetworkNoConnectedWeightsAndBias(const armnn::TensorInfo& inputTensorInfo,
126  const armnn::TensorInfo& outputTensorInfo,
128 {
130 
131  armnn::IConnectableLayer* inputLayer = network->AddInputLayer(0, "Input");
132  armnn::IConnectableLayer* fullyConnectedLayer = network->AddFullyConnectedLayer(descriptor, "Fully_Connected");
133  armnn::IConnectableLayer* outputLayer = network->AddOutputLayer(0, "Output");
134 
135  Connect(inputLayer, fullyConnectedLayer, inputTensorInfo, 0, 0);
136  Connect(fullyConnectedLayer, outputLayer, outputTensorInfo, 0, 0);
137 
138  return network;
139 }
140 
141 armnn::INetworkPtr CreateFullyConnectedNetworkNoConnectedBiasExplicit(const armnn::TensorInfo& inputTensorInfo,
142  const armnn::TensorInfo& outputTensorInfo,
143  const armnn::TensorInfo& weightsTensorInfo,
144  const armnn::ConstTensor& weightsConstantTensor,
146 {
148 
149  armnn::IConnectableLayer* inputLayer = network->AddInputLayer(0, "Input");
150  armnn::IConnectableLayer* weightsLayer = network->AddConstantLayer(weightsConstantTensor, "Weights");
151  armnn::IConnectableLayer* fullyConnectedLayer = network->AddFullyConnectedLayer(descriptor, "Fully_Connected");
152  armnn::IConnectableLayer* outputLayer = network->AddOutputLayer(0, "Output");
153 
154  Connect(inputLayer, fullyConnectedLayer, inputTensorInfo, 0, 0);
155  Connect(weightsLayer, fullyConnectedLayer, weightsTensorInfo, 0, 1);
156  Connect(fullyConnectedLayer, outputLayer, outputTensorInfo, 0, 0);
157 
158  return network;
159 }
160 
161 template<armnn::DataType ArmnnType, typename T = armnn::ResolveType<ArmnnType>>
162 void FullyConnectedWithDynamicWeightsEndToEnd(const std::vector<armnn::BackendId>& backends)
163 {
164  using namespace armnn;
165 
166  armnn::TensorInfo inputTensorInfo({ 1, 1, 2, 3 }, ArmnnType);
167  inputTensorInfo.SetQuantizationScale(0.1f);
168  inputTensorInfo.SetQuantizationOffset(63);
169  inputTensorInfo.SetConstant(true);
170 
171  armnn::TensorInfo outputTensorInfo({ 1, 2 }, ArmnnType);
172  outputTensorInfo.SetQuantizationScale(5.f);
173  outputTensorInfo.SetQuantizationOffset(10);
174 
175  armnn::TensorInfo weightsTensorInfo({ 2, 6 }, ArmnnType);
176  weightsTensorInfo.SetQuantizationScale(0.2f);
177  weightsTensorInfo.SetQuantizationOffset(93);
178  weightsTensorInfo.SetConstant(true);
179 
180  FullyConnectedDescriptor descriptor;
181  descriptor.m_ConstantWeights = false;
182  descriptor.m_BiasEnabled = false;
183  descriptor.m_TransposeWeightMatrix = true;
184 
185  std::vector<T> inputData {
186  -1.2f, 6.1f, -3.5f,
187  18.8f, -5.5f, 2.9f
188  };
189 
190  std::vector<T> weightsData {
191  -8.4f, 20.0f, -10.4f, -8, 16.4f, -11.8f,
192  23.4f, 10.4f, -14.0f, -3.8f, -11.8f, 11.4f
193  };
194 
195  std::vector<T> floatExpectedOutputData {
196  -107.04f, 110.f
197  };
198  std::vector<T> expectedOutputData = armnnUtils::QuantizedVector<T>(floatExpectedOutputData);
199 
200  armnn::INetworkPtr network = CreateFullyConnectedNetworkNonConstWeights(inputTensorInfo,
201  outputTensorInfo,
202  weightsTensorInfo,
203  descriptor);
204 
205  CHECK(network);
206 
207  std::map<int, std::vector<T>> inputTensorData = {{ 0, inputData }, {1, weightsData}};
208  std::map<int, std::vector<T>> expectedOutputTensorData = {{ 0, expectedOutputData }};
209 
210  EndToEndLayerTestImpl<ArmnnType, ArmnnType>(move(network),
211  inputTensorData,
212  expectedOutputTensorData,
213  backends,
214  1.0f);
215 }
216 
217 template<armnn::DataType ArmnnType, typename T = armnn::ResolveType<ArmnnType>>
218 void FullyConnectedWithDynamicOrConstantInputsEndToEnd(const std::vector<armnn::BackendId>& backends,
219  const bool transposeWeights,
220  const bool constantWeightsOrBias)
221 {
222  unsigned int inputWidth = 1;
223  unsigned int inputHeight = 1;
224  unsigned int inputChannels = 5;
225  unsigned int inputNum = 2;
226 
227  unsigned int outputChannels = 3;
228  unsigned int outputNum = 2;
229 
230  unsigned int inputShape[] = { inputNum, inputChannels, inputHeight, inputWidth };
231  unsigned int outputShape[] = { outputNum, outputChannels };
232  unsigned int weightsShape[] = { inputChannels, outputChannels };
233 
234  if (transposeWeights)
235  {
236  std::swap(weightsShape[0], weightsShape[1]);
237  }
238 
239  unsigned int biasShape[] = { outputChannels };
240 
241  armnn::TensorInfo inputTensorInfo = armnn::TensorInfo(4, inputShape, armnn::DataType::Float32, 0.0f, 0, true);
242  armnn::TensorInfo outputTensorInfo = armnn::TensorInfo(2, outputShape, armnn::DataType::Float32);
243  armnn::TensorInfo weightsDesc = armnn::TensorInfo(2, weightsShape, armnn::DataType::Float32, 0.0f, 0, true);
244  armnn::TensorInfo biasesDesc = armnn::TensorInfo(1, biasShape, armnn::DataType::Float32, 0.0f, 0, true);
245 
246  std::vector<float> input =
247  {
248  1.0f, 2.0f, 3.0f, 4.0f, 5.0f,
249  5.0f, 4.0f, 3.0f, 2.0f, 1.0f
250  };
251 
252  std::vector<float> weights =
253  {
254  .5f, 2.f, .5f,
255  .5f, 2.f, 1.f,
256  .5f, 2.f, 2.f,
257  .5f, 2.f, 3.f,
258  .5f, 2.f, 4.f
259  };
260 
261  if (transposeWeights)
262  {
263  weights =
264  {
265  .5f, .5f, .5f, .5f, .5f,
266  2.f, 2.f, 2.f, 2.f, 2.f,
267  .5f, 1.f, 2.f, 3.f, 4.f
268  };
269  }
270 
271  std::vector<float> biasValues = std::vector<float>({10.f, 20.f, 30.f});
272 
273  std::vector<float> expectedOutput =
274  {
275  0.5f + 1.0f + 1.5f + 2.0f + 2.5f + biasValues[0],
276  2.0f + 4.0f + 6.0f + 8.0f + 10.f + biasValues[1],
277  0.5f + 2.0f + 6.0f + 12.f + 20.f + biasValues[2],
278 
279  2.5f + 2.0f + 1.5f + 1.0f + 0.5f + biasValues[0],
280  10.0f + 8.0f + 6.0f + 4.0f + 2.f + biasValues[1],
281  2.5f + 4.0f + 6.0f + 6.f + 4.f + biasValues[2]
282  };
283 
284  FullyConnectedDescriptor descriptor;
285  descriptor.m_BiasEnabled = true;
286  descriptor.m_TransposeWeightMatrix = transposeWeights;
287  descriptor.m_ConstantWeights = constantWeightsOrBias;
288 
289  if (!constantWeightsOrBias)
290  {
291  // Tests non constant weights and constant bias.
292  ConstTensor biasConstantTensor(biasesDesc, biasValues.data());
293 
294  armnn::INetworkPtr network = CreateFullyConnectedNetworkNonConstWeightsConstBias(inputTensorInfo,
295  outputTensorInfo,
296  weightsDesc,
297  biasesDesc,
298  biasConstantTensor,
299  descriptor);
300  CHECK(network);
301 
302  std::map<int, std::vector<T>> inputTensorData = {{ 0, input }, {1, weights}};
303  std::map<int, std::vector<T>> expectedOutputTensorData = {{ 0, expectedOutput }};
304 
305  EndToEndLayerTestImpl<ArmnnType, ArmnnType>(move(network),
306  inputTensorData,
307  expectedOutputTensorData,
308  backends,
309  1.0f);
310  }
311  else
312  {
313  // Tests constant weights and non constant bias.
314  ConstTensor weightsConstantTensor(weightsDesc, weights.data());
315 
316  armnn::INetworkPtr network = CreateFullyConnectedNetworkConstWeightsNonConstBias(inputTensorInfo,
317  outputTensorInfo,
318  weightsDesc,
319  biasesDesc,
320  weightsConstantTensor,
321  descriptor);
322  CHECK(network);
323 
324  std::map<int, std::vector<T>> inputTensorData = {{ 0, input }, {2, biasValues}};
325  std::map<int, std::vector<T>> expectedOutputTensorData = {{ 0, expectedOutput }};
326 
327  EndToEndLayerTestImpl<ArmnnType, ArmnnType>(move(network),
328  inputTensorData,
329  expectedOutputTensorData,
330  backends,
331  1.0f);
332  }
333 }
334 
335 template<armnn::DataType ArmnnType, typename T = armnn::ResolveType<ArmnnType>>
336 void FullyConnectedErrorChecking(const std::vector<armnn::BackendId>& backends,
337  const bool explicitCheck,
338  const bool biasEnabled,
339  const bool connectedWeights,
340  const bool connectedBias,
341  const bool tensorInfoSet)
342 {
343  unsigned int inputWidth = 1;
344  unsigned int inputHeight = 1;
345  unsigned int inputChannels = 5;
346  unsigned int inputNum = 2;
347 
348  unsigned int outputChannels = 3;
349  unsigned int outputNum = 2;
350 
351  unsigned int inputShape[] = { inputNum, inputChannels, inputHeight, inputWidth };
352  unsigned int outputShape[] = { outputNum, outputChannels };
353  unsigned int weightsShape[] = { inputChannels, outputChannels };
354 
355  unsigned int biasShape[] = { outputChannels };
356 
357  armnn::TensorInfo inputTensorInfo = armnn::TensorInfo(4, inputShape, armnn::DataType::Float32, 0.0f, 0, true);
358  armnn::TensorInfo outputTensorInfo = armnn::TensorInfo(2, outputShape, armnn::DataType::Float32);
359  armnn::TensorInfo weightsDesc = armnn::TensorInfo(2, weightsShape, armnn::DataType::Float32, 0.0f, 0, true);
360  armnn::TensorInfo biasesDesc = armnn::TensorInfo(1, biasShape, armnn::DataType::Float32, 0.0f, 0, true);
361 
362  std::vector<float> weights =
363  {
364  .5f, 2.f, .5f,
365  .5f, 2.f, 1.f,
366  .5f, 2.f, 2.f,
367  .5f, 2.f, 3.f,
368  .5f, 2.f, 4.f
369  };
370 
371  FullyConnectedDescriptor descriptor;
372  descriptor.m_BiasEnabled = biasEnabled;
373 
374  if(explicitCheck)
375  {
376  if(!biasEnabled)
377  {
378  try
379  {
380  CreateFullyConnectedNetworkNoConnectedWeightsExplicit(inputTensorInfo,
381  outputTensorInfo,
382  biasesDesc,
383  descriptor);
384  FAIL("LayerValidationException should have been thrown");
385  }
386  catch (const LayerValidationException& exc)
387  {
388  CHECK(strcmp(exc.what(), "Tried to connect bias to FullyConnected layer when bias is not enabled: "
389  "Failed to connect to input slot 2 on FullyConnected layer "
390  "\"Fully_Connected\" as the slot does not exist or is unavailable") == 0);
391  }
392  }
393  else if (!connectedWeights)
394  {
395  armnn::INetworkPtr network = CreateFullyConnectedNetworkNoConnectedWeightsExplicit(inputTensorInfo,
396  outputTensorInfo,
397  biasesDesc,
398  descriptor);
399  CHECK(network);
400 
401  // Create runtime in which test will run
403  IRuntimePtr runtime(IRuntime::Create(options));
404 
405  try
406  {
407  Optimize(*network, backends, runtime->GetDeviceSpec());
408  FAIL("LayerValidationException should have been thrown");
409  }
410  catch (const LayerValidationException& exc)
411  {
412  CHECK(strcmp(exc.what(), "Fully_Connected layer weights not set: Input slot(s) 1 not connected "
413  "to an output slot on FullyConnected layer \"Fully_Connected\"") == 0);
414  }
415  }
416  else if (!connectedBias)
417  {
418  // Tests with constant weights.
419  ConstTensor weightsConstantTensor(weightsDesc, weights.data());
420 
421  armnn::INetworkPtr network = CreateFullyConnectedNetworkNoConnectedBiasExplicit(inputTensorInfo,
422  outputTensorInfo,
423  weightsDesc,
424  weightsConstantTensor,
425  descriptor);
426  CHECK(network);
427 
428  // Create runtime in which test will run
430  IRuntimePtr runtime(IRuntime::Create(options));
431 
432  try
433  {
434  Optimize(*network, backends, runtime->GetDeviceSpec());
435  FAIL("LayerValidationException should have been thrown");
436  }
437  catch (const LayerValidationException& exc)
438  {
439  CHECK(strcmp(exc.what(), "Fully_Connected layer bias not set: Input slot(s) 2 not connected "
440  "to an output slot on FullyConnected layer \"Fully_Connected\"") == 0);
441  }
442  }
443  }
444  else if(!connectedWeights && !connectedBias)
445  {
446  armnn::INetworkPtr network = CreateFullyConnectedNetworkNoConnectedWeightsAndBias(inputTensorInfo,
447  outputTensorInfo,
448  descriptor);
449  CHECK(network);
450 
451  // Create runtime in which test will run
453  IRuntimePtr runtime(IRuntime::Create(options));
454 
455  try
456  {
457  Optimize(*network, backends, runtime->GetDeviceSpec());
458  FAIL("LayerValidationException should have been thrown");
459  }
460  catch (const LayerValidationException& exc)
461  {
462  CHECK(strcmp(exc.what(), "Fully_Connected layer weights and bias not set: Input slot(s) 1 & 2 not "
463  "connected to an output slot on FullyConnected layer \"Fully_Connected\"") == 0);
464  }
465 
466  }
467  else if(!tensorInfoSet)
468  {
469  // Tests with constant weights.
470  ConstTensor weightsConstantTensor(weightsDesc, weights.data());
471 
472  armnn::INetworkPtr network = CreateFullyConnectedNetworkNoTensorInfoConstWeights(inputTensorInfo,
473  outputTensorInfo,
474  weightsConstantTensor,
475  descriptor);
476  CHECK(network);
477 
478  // Create runtime in which test will run
480  IRuntimePtr runtime(IRuntime::Create(options));
481 
482  try
483  {
484  Optimize(*network, backends, runtime->GetDeviceSpec());
485  FAIL("LayerValidationException should have been thrown");
486  }
487  catch (const LayerValidationException& exc)
488  {
489  CHECK(strcmp(exc.what(), "Output slot TensorInfo not set on Constant layer \"Weights\"") == 0);
490  }
491  }
492 }
493 
494 } // anonymous namespace
static IRuntimePtr Create(const CreationOptions &options)
Definition: Runtime.cpp:40
Interface for a layer that is connectable to other layers via InputSlots and OutputSlots.
Definition: INetwork.hpp:61
void swap(OriginsDescriptor &first, OriginsDescriptor &second)
bool m_TransposeWeightMatrix
Enable/disable transpose weight matrix.
std::unique_ptr< IRuntime, void(*)(IRuntime *runtime)> IRuntimePtr
Definition: IRuntime.hpp:31
virtual const char * what() const noexcept override
Definition: Exceptions.cpp:32
Copyright (c) 2021 ARM Limited and Contributors.
IOptimizedNetworkPtr Optimize(const INetwork &network, const std::vector< BackendId > &backendPreferences, const IDeviceSpec &deviceSpec, const OptimizerOptions &options=OptimizerOptions(), Optional< std::vector< std::string > &> messages=EmptyOptional())
Create an optimized version of the network.
Definition: Network.cpp:1605
A FullyConnectedDescriptor for the FullyConnectedLayer.
bool m_BiasEnabled
Enable/disable bias.
A tensor defined by a TensorInfo (shape and data type) and an immutable backing store.
Definition: Tensor.hpp:327
void SetQuantizationScale(float scale)
Definition: Tensor.cpp:475
virtual const IInputSlot & GetInputSlot(unsigned int index) const =0
Get a const input slot handle by slot index.
void SetConstant(const bool IsConstant=true)
Marks the data corresponding to this tensor info as constant.
Definition: Tensor.cpp:516
virtual const IOutputSlot & GetOutputSlot(unsigned int index) const =0
Get the const output slot handle by slot index.
void SetQuantizationOffset(int32_t offset)
Definition: Tensor.cpp:491
void Connect(armnn::IConnectableLayer *from, armnn::IConnectableLayer *to, const armnn::TensorInfo &tensorInfo, unsigned int fromIndex, unsigned int toIndex)
Definition: TestUtils.cpp:12
std::unique_ptr< INetwork, void(*)(INetwork *network)> INetworkPtr
Definition: INetwork.hpp:197
virtual int Connect(IInputSlot &destination)=0
static INetworkPtr Create(NetworkOptions networkOptions={})
Definition: Network.cpp:478
bool m_ConstantWeights
Enable/disable constant weights and biases.