ArmNN
 22.05
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 
114  ConstTensor biases;
115 
116  armnn::IConnectableLayer* inputLayer = network->AddInputLayer(0, "Input");
117  armnn::IConnectableLayer* biasLayer = network->AddConstantLayer(biases, "Bias_Input");
118  armnn::IConnectableLayer* fullyConnectedLayer = network->AddFullyConnectedLayer(descriptor, "Fully_Connected");
119  armnn::IConnectableLayer* outputLayer = network->AddOutputLayer(0, "Output");
120 
121  Connect(inputLayer, fullyConnectedLayer, inputTensorInfo, 0, 0);
122  Connect(biasLayer, fullyConnectedLayer, biasTensorInfo, 0, 2);
123  Connect(fullyConnectedLayer, outputLayer, outputTensorInfo, 0, 0);
124 
125  return network;
126 }
127 
128 armnn::INetworkPtr CreateFullyConnectedNetworkNoConnectedWeightsAndBias(const armnn::TensorInfo& inputTensorInfo,
129  const armnn::TensorInfo& outputTensorInfo,
131 {
133 
134  armnn::IConnectableLayer* inputLayer = network->AddInputLayer(0, "Input");
135  armnn::IConnectableLayer* fullyConnectedLayer = network->AddFullyConnectedLayer(descriptor, "Fully_Connected");
136  armnn::IConnectableLayer* outputLayer = network->AddOutputLayer(0, "Output");
137 
138  Connect(inputLayer, fullyConnectedLayer, inputTensorInfo, 0, 0);
139  Connect(fullyConnectedLayer, outputLayer, outputTensorInfo, 0, 0);
140 
141  return network;
142 }
143 
144 armnn::INetworkPtr CreateFullyConnectedNetworkNoConnectedBiasExplicit(const armnn::TensorInfo& inputTensorInfo,
145  const armnn::TensorInfo& outputTensorInfo,
146  const armnn::TensorInfo& weightsTensorInfo,
147  const armnn::ConstTensor& weightsConstantTensor,
149 {
151 
152  armnn::IConnectableLayer* inputLayer = network->AddInputLayer(0, "Input");
153  armnn::IConnectableLayer* weightsLayer = network->AddConstantLayer(weightsConstantTensor, "Weights");
154  armnn::IConnectableLayer* fullyConnectedLayer = network->AddFullyConnectedLayer(descriptor, "Fully_Connected");
155  armnn::IConnectableLayer* outputLayer = network->AddOutputLayer(0, "Output");
156 
157  Connect(inputLayer, fullyConnectedLayer, inputTensorInfo, 0, 0);
158  Connect(weightsLayer, fullyConnectedLayer, weightsTensorInfo, 0, 1);
159  Connect(fullyConnectedLayer, outputLayer, outputTensorInfo, 0, 0);
160 
161  return network;
162 }
163 
164 template<armnn::DataType ArmnnType, typename T = armnn::ResolveType<ArmnnType>>
165 void FullyConnectedWithDynamicWeightsEndToEnd(const std::vector<armnn::BackendId>& backends)
166 {
167  using namespace armnn;
168 
169  armnn::TensorInfo inputTensorInfo({ 1, 1, 2, 3 }, ArmnnType);
170  inputTensorInfo.SetQuantizationScale(0.1f);
171  inputTensorInfo.SetQuantizationOffset(63);
172  inputTensorInfo.SetConstant(true);
173 
174  armnn::TensorInfo outputTensorInfo({ 1, 2 }, ArmnnType);
175  outputTensorInfo.SetQuantizationScale(5.f);
176  outputTensorInfo.SetQuantizationOffset(10);
177 
178  armnn::TensorInfo weightsTensorInfo({ 2, 6 }, ArmnnType);
179  weightsTensorInfo.SetQuantizationScale(0.2f);
180  weightsTensorInfo.SetQuantizationOffset(93);
181  weightsTensorInfo.SetConstant(true);
182 
183  FullyConnectedDescriptor descriptor;
184  descriptor.m_ConstantWeights = false;
185  descriptor.m_BiasEnabled = false;
186  descriptor.m_TransposeWeightMatrix = true;
187 
188  std::vector<T> inputData {
189  -1.2f, 6.1f, -3.5f,
190  18.8f, -5.5f, 2.9f
191  };
192 
193  std::vector<T> weightsData {
194  -8.4f, 20.0f, -10.4f, -8, 16.4f, -11.8f,
195  23.4f, 10.4f, -14.0f, -3.8f, -11.8f, 11.4f
196  };
197 
198  std::vector<T> floatExpectedOutputData {
199  -107.04f, 110.f
200  };
201  std::vector<T> expectedOutputData = armnnUtils::QuantizedVector<T>(floatExpectedOutputData);
202 
203  armnn::INetworkPtr network = CreateFullyConnectedNetworkNonConstWeights(inputTensorInfo,
204  outputTensorInfo,
205  weightsTensorInfo,
206  descriptor);
207 
208  CHECK(network);
209 
210  std::map<int, std::vector<T>> inputTensorData = {{ 0, inputData }, {1, weightsData}};
211  std::map<int, std::vector<T>> expectedOutputTensorData = {{ 0, expectedOutputData }};
212 
213  EndToEndLayerTestImpl<ArmnnType, ArmnnType>(move(network),
214  inputTensorData,
215  expectedOutputTensorData,
216  backends,
217  1.0f);
218 }
219 
220 template<armnn::DataType ArmnnType, typename T = armnn::ResolveType<ArmnnType>>
221 void FullyConnectedWithDynamicOrConstantInputsEndToEnd(const std::vector<armnn::BackendId>& backends,
222  const bool transposeWeights,
223  const bool constantWeightsOrBias)
224 {
225  unsigned int inputWidth = 1;
226  unsigned int inputHeight = 1;
227  unsigned int inputChannels = 5;
228  unsigned int inputNum = 2;
229 
230  unsigned int outputChannels = 3;
231  unsigned int outputNum = 2;
232 
233  unsigned int inputShape[] = { inputNum, inputChannels, inputHeight, inputWidth };
234  unsigned int outputShape[] = { outputNum, outputChannels };
235  unsigned int weightsShape[] = { inputChannels, outputChannels };
236 
237  if (transposeWeights)
238  {
239  std::swap(weightsShape[0], weightsShape[1]);
240  }
241 
242  unsigned int biasShape[] = { outputChannels };
243 
244  armnn::TensorInfo inputTensorInfo = armnn::TensorInfo(4, inputShape, armnn::DataType::Float32, 0.0f, 0, true);
245  armnn::TensorInfo outputTensorInfo = armnn::TensorInfo(2, outputShape, armnn::DataType::Float32);
246  armnn::TensorInfo weightsDesc = armnn::TensorInfo(2, weightsShape, armnn::DataType::Float32, 0.0f, 0, true);
247  armnn::TensorInfo biasesDesc = armnn::TensorInfo(1, biasShape, armnn::DataType::Float32, 0.0f, 0, true);
248 
249  std::vector<float> input =
250  {
251  1.0f, 2.0f, 3.0f, 4.0f, 5.0f,
252  5.0f, 4.0f, 3.0f, 2.0f, 1.0f
253  };
254 
255  std::vector<float> weights =
256  {
257  .5f, 2.f, .5f,
258  .5f, 2.f, 1.f,
259  .5f, 2.f, 2.f,
260  .5f, 2.f, 3.f,
261  .5f, 2.f, 4.f
262  };
263 
264  if (transposeWeights)
265  {
266  weights =
267  {
268  .5f, .5f, .5f, .5f, .5f,
269  2.f, 2.f, 2.f, 2.f, 2.f,
270  .5f, 1.f, 2.f, 3.f, 4.f
271  };
272  }
273 
274  std::vector<float> biasValues = std::vector<float>({10.f, 20.f, 30.f});
275 
276  std::vector<float> expectedOutput =
277  {
278  0.5f + 1.0f + 1.5f + 2.0f + 2.5f + biasValues[0],
279  2.0f + 4.0f + 6.0f + 8.0f + 10.f + biasValues[1],
280  0.5f + 2.0f + 6.0f + 12.f + 20.f + biasValues[2],
281 
282  2.5f + 2.0f + 1.5f + 1.0f + 0.5f + biasValues[0],
283  10.0f + 8.0f + 6.0f + 4.0f + 2.f + biasValues[1],
284  2.5f + 4.0f + 6.0f + 6.f + 4.f + biasValues[2]
285  };
286 
287  FullyConnectedDescriptor descriptor;
288  descriptor.m_BiasEnabled = true;
289  descriptor.m_TransposeWeightMatrix = transposeWeights;
290  descriptor.m_ConstantWeights = constantWeightsOrBias;
291 
292  if (!constantWeightsOrBias)
293  {
294  // Tests non constant weights and constant bias.
295  ConstTensor biasConstantTensor(biasesDesc, biasValues.data());
296 
297  armnn::INetworkPtr network = CreateFullyConnectedNetworkNonConstWeightsConstBias(inputTensorInfo,
298  outputTensorInfo,
299  weightsDesc,
300  biasesDesc,
301  biasConstantTensor,
302  descriptor);
303  CHECK(network);
304 
305  std::map<int, std::vector<T>> inputTensorData = {{ 0, input }, {1, weights}};
306  std::map<int, std::vector<T>> expectedOutputTensorData = {{ 0, expectedOutput }};
307 
308  EndToEndLayerTestImpl<ArmnnType, ArmnnType>(move(network),
309  inputTensorData,
310  expectedOutputTensorData,
311  backends,
312  1.0f);
313  }
314  else
315  {
316  // Tests constant weights and non constant bias.
317  ConstTensor weightsConstantTensor(weightsDesc, weights.data());
318 
319  armnn::INetworkPtr network = CreateFullyConnectedNetworkConstWeightsNonConstBias(inputTensorInfo,
320  outputTensorInfo,
321  weightsDesc,
322  biasesDesc,
323  weightsConstantTensor,
324  descriptor);
325  CHECK(network);
326 
327  std::map<int, std::vector<T>> inputTensorData = {{ 0, input }, {2, biasValues}};
328  std::map<int, std::vector<T>> expectedOutputTensorData = {{ 0, expectedOutput }};
329 
330  EndToEndLayerTestImpl<ArmnnType, ArmnnType>(move(network),
331  inputTensorData,
332  expectedOutputTensorData,
333  backends,
334  1.0f);
335  }
336 }
337 
338 template<armnn::DataType ArmnnType, typename T = armnn::ResolveType<ArmnnType>>
339 void FullyConnectedErrorChecking(const std::vector<armnn::BackendId>& backends,
340  const bool explicitCheck,
341  const bool biasEnabled,
342  const bool connectedWeights,
343  const bool connectedBias,
344  const bool tensorInfoSet)
345 {
346  unsigned int inputWidth = 1;
347  unsigned int inputHeight = 1;
348  unsigned int inputChannels = 5;
349  unsigned int inputNum = 2;
350 
351  unsigned int outputChannels = 3;
352  unsigned int outputNum = 2;
353 
354  unsigned int inputShape[] = { inputNum, inputChannels, inputHeight, inputWidth };
355  unsigned int outputShape[] = { outputNum, outputChannels };
356  unsigned int weightsShape[] = { inputChannels, outputChannels };
357 
358  unsigned int biasShape[] = { outputChannels };
359 
360  armnn::TensorInfo inputTensorInfo = armnn::TensorInfo(4, inputShape, armnn::DataType::Float32, 0.0f, 0, true);
361  armnn::TensorInfo outputTensorInfo = armnn::TensorInfo(2, outputShape, armnn::DataType::Float32);
362  armnn::TensorInfo weightsDesc = armnn::TensorInfo(2, weightsShape, armnn::DataType::Float32, 0.0f, 0, true);
363  armnn::TensorInfo biasesDesc = armnn::TensorInfo(1, biasShape, armnn::DataType::Float32, 0.0f, 0, true);
364 
365  std::vector<float> weights =
366  {
367  .5f, 2.f, .5f,
368  .5f, 2.f, 1.f,
369  .5f, 2.f, 2.f,
370  .5f, 2.f, 3.f,
371  .5f, 2.f, 4.f
372  };
373 
374  FullyConnectedDescriptor descriptor;
375  descriptor.m_BiasEnabled = biasEnabled;
376 
377  if(explicitCheck)
378  {
379  if(!biasEnabled)
380  {
381  try
382  {
383  CreateFullyConnectedNetworkNoConnectedWeightsExplicit(inputTensorInfo,
384  outputTensorInfo,
385  biasesDesc,
386  descriptor);
387  FAIL("LayerValidationException should have been thrown");
388  }
389  catch (const LayerValidationException& exc)
390  {
391  CHECK(strcmp(exc.what(), "Tried to connect bias to FullyConnected layer when bias is not enabled: "
392  "Failed to connect to input slot 2 on FullyConnected layer "
393  "\"Fully_Connected\" as the slot does not exist or is unavailable") == 0);
394  }
395  }
396  else if (!connectedWeights)
397  {
398  armnn::INetworkPtr network = CreateFullyConnectedNetworkNoConnectedWeightsExplicit(inputTensorInfo,
399  outputTensorInfo,
400  biasesDesc,
401  descriptor);
402  CHECK(network);
403 
404  // Create runtime in which test will run
406  IRuntimePtr runtime(IRuntime::Create(options));
407 
408  CHECK_THROWS_AS(Optimize(*network, backends, runtime->GetDeviceSpec()), LayerValidationException);
409  }
410  else if (!connectedBias)
411  {
412  // Tests with constant weights.
413  ConstTensor weightsConstantTensor(weightsDesc, weights.data());
414 
415  armnn::INetworkPtr network = CreateFullyConnectedNetworkNoConnectedBiasExplicit(inputTensorInfo,
416  outputTensorInfo,
417  weightsDesc,
418  weightsConstantTensor,
419  descriptor);
420  CHECK(network);
421 
422  // Create runtime in which test will run
424  IRuntimePtr runtime(IRuntime::Create(options));
425 
426  CHECK_THROWS_AS(Optimize(*network, backends, runtime->GetDeviceSpec()), LayerValidationException);
427  }
428  }
429  else if(!connectedWeights && !connectedBias)
430  {
431  armnn::INetworkPtr network = CreateFullyConnectedNetworkNoConnectedWeightsAndBias(inputTensorInfo,
432  outputTensorInfo,
433  descriptor);
434  CHECK(network);
435 
436  // Create runtime in which test will run
438  IRuntimePtr runtime(IRuntime::Create(options));
439 
440  CHECK_THROWS_AS(Optimize(*network, backends, runtime->GetDeviceSpec()), LayerValidationException);
441  }
442  else if(!tensorInfoSet)
443  {
444  // Tests with constant weights.
445  ConstTensor weightsConstantTensor(weightsDesc, weights.data());
446 
447  armnn::INetworkPtr network = CreateFullyConnectedNetworkNoTensorInfoConstWeights(inputTensorInfo,
448  outputTensorInfo,
449  weightsConstantTensor,
450  descriptor);
451  CHECK(network);
452 
453  // Create runtime in which test will run
455  IRuntimePtr runtime(IRuntime::Create(options));
456 
457  try
458  {
459  Optimize(*network, backends, runtime->GetDeviceSpec());
460  FAIL("LayerValidationException should have been thrown");
461  }
462  catch (const LayerValidationException& exc)
463  {
464  CHECK(strcmp(exc.what(), "Output slot TensorInfo not set on Constant layer \"Weights\"") == 0);
465  }
466  }
467 }
468 
469 } // anonymous namespace
static IRuntimePtr Create(const CreationOptions &options)
Definition: Runtime.cpp:49
Interface for a layer that is connectable to other layers via InputSlots and OutputSlots.
Definition: INetwork.hpp:66
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:33
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:1847
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:473
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:514
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:489
void Connect(armnn::IConnectableLayer *from, armnn::IConnectableLayer *to, const armnn::TensorInfo &tensorInfo, unsigned int fromIndex, unsigned int toIndex)
Definition: TestUtils.cpp:14
std::unique_ptr< INetwork, void(*)(INetwork *network)> INetworkPtr
Definition: INetwork.hpp:241
virtual int Connect(IInputSlot &destination)=0
static INetworkPtr Create(NetworkOptions networkOptions={})
Definition: Network.cpp:476
bool m_ConstantWeights
Enable/disable constant weights and biases.