ArmNN
 20.05
FullyConnected.cpp
Go to the documentation of this file.
1 //
2 // Copyright © 2017 Arm Ltd. All rights reserved.
3 // SPDX-License-Identifier: MIT
4 //
5 
6 #include <boost/test/unit_test.hpp>
9 #include "Runtime.hpp"
10 #include "Network.hpp"
11 #include "Graph.hpp"
12 
13 BOOST_AUTO_TEST_SUITE(TensorflowParser)
14 
15 // In Tensorflow fully connected layers are expressed as a MatMul followed by an Add.
16 // The TfParser must detect this case and convert them to a FullyConnected layer.
17 struct FullyConnectedFixture : public armnnUtils::ParserPrototxtFixture<armnnTfParser::ITfParser>
18 {
19  FullyConnectedFixture()
20  {
21  // Input = tf.placeholder(tf.float32, [1, 1], "input")
22  // Weights = tf.constant([2], tf.float32, [1, 1])
23  // Matmul = tf.matmul(input, weights)
24  // Bias = tf.constant([1], tf.float32)
25  // Output = tf.add(matmul, bias, name="output")
26  m_Prototext = R"(
27 node {
28  name: "input"
29  op: "Placeholder"
30  attr {
31  key: "dtype"
32  value {
33  type: DT_FLOAT
34  }
35  }
36  attr {
37  key: "shape"
38  value {
39  shape {
40  dim {
41  size: 1
42  }
43  dim {
44  size: 1
45  }
46  }
47  }
48  }
49 }
50 node {
51  name: "Const"
52  op: "Const"
53  attr {
54  key: "dtype"
55  value {
56  type: DT_FLOAT
57  }
58  }
59  attr {
60  key: "value"
61  value {
62  tensor {
63  dtype: DT_FLOAT
64  tensor_shape {
65  dim {
66  size: 1
67  }
68  dim {
69  size: 1
70  }
71  }
72  float_val: 2.0
73  }
74  }
75  }
76 }
77 node {
78  name: "MatMul"
79  op: "MatMul"
80  input: "input"
81  input: "Const"
82  attr {
83  key: "T"
84  value {
85  type: DT_FLOAT
86  }
87  }
88  attr {
89  key: "transpose_a"
90  value {
91  b: false
92  }
93  }
94  attr {
95  key: "transpose_b"
96  value {
97  b: false
98  }
99  }
100 }
101 node {
102  name: "Const_1"
103  op: "Const"
104  attr {
105  key: "dtype"
106  value {
107  type: DT_FLOAT
108  }
109  }
110  attr {
111  key: "value"
112  value {
113  tensor {
114  dtype: DT_FLOAT
115  tensor_shape {
116  dim {
117  size: 1
118  }
119  }
120  float_val: 1.0
121  }
122  }
123  }
124 }
125 node {
126  name: "output"
127  op: "Add"
128  input: "MatMul"
129  input: "Const_1"
130  attr {
131  key: "T"
132  value {
133  type: DT_FLOAT
134  }
135  }
136 }
137  )";
138  SetupSingleInputSingleOutput({ 1, 1 }, "input", "output");
139  }
140 };
141 
143 {
144  RunTest<1>({ 3 }, { 7 });
145 }
146 
147 // Similar to FullyConnectedFixture, but this time the MatMul's output is used by two Adds. This should result
148 // in two FullyConnected layers being created.
149 // I
150 // |
151 // M -- C
152 // / \'
153 // C-- A A -- C
154 // \ /
155 // A
156 struct MatMulUsedInTwoFcFixture : public armnnUtils::ParserPrototxtFixture<armnnTfParser::ITfParser>
157 {
158  MatMulUsedInTwoFcFixture()
159  {
160  m_Prototext = R"(
161 node {
162  name: "input"
163  op: "Placeholder"
164  attr {
165  key: "dtype"
166  value {
167  type: DT_FLOAT
168  }
169  }
170  attr {
171  key: "shape"
172  value {
173  shape {
174  dim {
175  size: 1
176  }
177  dim {
178  size: 1
179  }
180  }
181  }
182  }
183 }
184 node {
185  name: "Const"
186  op: "Const"
187  attr {
188  key: "dtype"
189  value {
190  type: DT_FLOAT
191  }
192  }
193  attr {
194  key: "value"
195  value {
196  tensor {
197  dtype: DT_FLOAT
198  tensor_shape {
199  dim {
200  size: 1
201  }
202  dim {
203  size: 1
204  }
205  }
206  float_val: 2.0
207  }
208  }
209  }
210 }
211 node {
212  name: "MatMul"
213  op: "MatMul"
214  input: "input"
215  input: "Const"
216  attr {
217  key: "T"
218  value {
219  type: DT_FLOAT
220  }
221  }
222  attr {
223  key: "transpose_a"
224  value {
225  b: false
226  }
227  }
228  attr {
229  key: "transpose_b"
230  value {
231  b: false
232  }
233  }
234 }
235 node {
236  name: "Const_1"
237  op: "Const"
238  attr {
239  key: "dtype"
240  value {
241  type: DT_FLOAT
242  }
243  }
244  attr {
245  key: "value"
246  value {
247  tensor {
248  dtype: DT_FLOAT
249  tensor_shape {
250  dim {
251  size: 1
252  }
253  }
254  float_val: 5.0
255  }
256  }
257  }
258 }
259 node {
260  name: "Const_2"
261  op: "Const"
262  attr {
263  key: "dtype"
264  value {
265  type: DT_FLOAT
266  }
267  }
268  attr {
269  key: "value"
270  value {
271  tensor {
272  dtype: DT_FLOAT
273  tensor_shape {
274  dim {
275  size: 1
276  }
277  }
278  float_val: 15.0
279  }
280  }
281  }
282 }
283 node {
284  name: "Add"
285  op: "Add"
286  input: "MatMul"
287  input: "Const_1"
288  attr {
289  key: "T"
290  value {
291  type: DT_FLOAT
292  }
293  }
294 }
295 node {
296  name: "Add_1"
297  op: "Add"
298  input: "MatMul"
299  input: "Const_2"
300  attr {
301  key: "T"
302  value {
303  type: DT_FLOAT
304  }
305  }
306 }
307 node {
308  name: "output"
309  op: "Add"
310  input: "Add"
311  input: "Add_1"
312  attr {
313  key: "T"
314  value {
315  type: DT_FLOAT
316  }
317  }
318 }
319  )";
320  SetupSingleInputSingleOutput({ 1, 1 }, "input", "output");
321  }
322 };
323 
324 BOOST_FIXTURE_TEST_CASE(MatMulUsedInTwoFc, MatMulUsedInTwoFcFixture)
325 {
326  RunTest<1>({ 3 }, { 32 });
327  // Ideally we would check here that the armnn network has 5 layers:
328  // Input, 2 x FullyConnected (biased), Add and Output.
329  // This would make sure the parser hasn't incorrectly added some unconnected layers corresponding to the MatMul.
330 }
331 
332 // Similar to MatMulUsedInTwoFc, but this time the Adds are 'staggered' (see diagram), which means that only one
333 // FullyConnected layer can be created (the other should just be an Add).
334 // I
335 // |
336 // M -- C1
337 // / \'
338 // C2 -- A |
339 // \ /
340 // A
341 struct MatMulUsedInTwoFcStaggeredFixture : public armnnUtils::ParserPrototxtFixture<armnnTfParser::ITfParser>
342 {
343  MatMulUsedInTwoFcStaggeredFixture()
344  {
345  // Input = tf.placeholder(tf.float32, shape=[1,1], name = "input")
346  // Const1 = tf.constant([17], tf.float32, [1,1])
347  // Mul = tf.matmul(input, const1)
348  // Monst2 = tf.constant([7], tf.float32, [1])
349  // Fc = tf.add(mul, const2)
350  // Output = tf.add(mul, fc, name="output")
351  m_Prototext = R"(
352 node {
353  name: "input"
354  op: "Placeholder"
355  attr {
356  key: "dtype"
357  value {
358  type: DT_FLOAT
359  }
360  }
361  attr {
362  key: "shape"
363  value {
364  shape {
365  dim {
366  size: 1
367  }
368  dim {
369  size: 1
370  }
371  }
372  }
373  }
374 }
375 node {
376  name: "Const"
377  op: "Const"
378  attr {
379  key: "dtype"
380  value {
381  type: DT_FLOAT
382  }
383  }
384  attr {
385  key: "value"
386  value {
387  tensor {
388  dtype: DT_FLOAT
389  tensor_shape {
390  dim {
391  size: 1
392  }
393  dim {
394  size: 1
395  }
396  }
397  float_val: 17.0
398  }
399  }
400  }
401 }
402 node {
403  name: "MatMul"
404  op: "MatMul"
405  input: "input"
406  input: "Const"
407  attr {
408  key: "T"
409  value {
410  type: DT_FLOAT
411  }
412  }
413  attr {
414  key: "transpose_a"
415  value {
416  b: false
417  }
418  }
419  attr {
420  key: "transpose_b"
421  value {
422  b: false
423  }
424  }
425 }
426 node {
427  name: "Const_1"
428  op: "Const"
429  attr {
430  key: "dtype"
431  value {
432  type: DT_FLOAT
433  }
434  }
435  attr {
436  key: "value"
437  value {
438  tensor {
439  dtype: DT_FLOAT
440  tensor_shape {
441  dim {
442  size: 1
443  }
444  }
445  float_val: 7.0
446  }
447  }
448  }
449 }
450 node {
451  name: "Add"
452  op: "Add"
453  input: "MatMul"
454  input: "Const_1"
455  attr {
456  key: "T"
457  value {
458  type: DT_FLOAT
459  }
460  }
461 }
462 node {
463  name: "output"
464  op: "Add"
465  input: "MatMul"
466  input: "Add"
467  attr {
468  key: "T"
469  value {
470  type: DT_FLOAT
471  }
472  }
473 }
474  )";
475  SetupSingleInputSingleOutput({ 1, 1 }, "input", "output");
476  }
477 };
478 
479 BOOST_FIXTURE_TEST_CASE(MatMulUsedInTwoFcStaggered, MatMulUsedInTwoFcStaggeredFixture)
480 {
481  RunTest<1>({ 2 }, { 75 });
482  // Ideally we would check here that the armnn network has 5 layers:
483  // Input, FullyConnected (biased), FullyConnected (non biased), Add and Output.
484 }
485 
486 // A MatMul in isolation, not connected to an add. Should result in a non-biased FullyConnected layer.
487 struct MatMulFixture : public armnnUtils::ParserPrototxtFixture<armnnTfParser::ITfParser>
488 {
489  MatMulFixture()
490  {
491  // Input = tf.placeholder(tf.float32, shape = [1, 1], name = "input")
492  // Const = tf.constant([17], tf.float32, [1, 1])
493  // Output = tf.matmul(input, const, name = "output")
494  m_Prototext = R"(
495 node {
496  name: "input"
497  op: "Placeholder"
498  attr {
499  key: "dtype"
500  value {
501  type: DT_FLOAT
502  }
503  }
504  attr {
505  key: "shape"
506  value {
507  shape {
508  dim {
509  size: 1
510  }
511  dim {
512  size: 1
513  }
514  }
515  }
516  }
517 }
518 node {
519  name: "Const"
520  op: "Const"
521  attr {
522  key: "dtype"
523  value {
524  type: DT_FLOAT
525  }
526  }
527  attr {
528  key: "value"
529  value {
530  tensor {
531  dtype: DT_FLOAT
532  tensor_shape {
533  dim {
534  size: 1
535  }
536  dim {
537  size: 1
538  }
539  }
540  float_val: 17.0
541  }
542  }
543  }
544 }
545 node {
546  name: "output"
547  op: "MatMul"
548  input: "input"
549  input: "Const"
550  attr {
551  key: "T"
552  value {
553  type: DT_FLOAT
554  }
555  }
556  attr {
557  key: "transpose_a"
558  value {
559  b: false
560  }
561  }
562  attr {
563  key: "transpose_b"
564  value {
565  b: false
566  }
567  }
568 }
569  )";
570  SetupSingleInputSingleOutput({ 1, 1 }, "input", "output");
571  }
572 };
573 
574 BOOST_FIXTURE_TEST_CASE(MatMul, MatMulFixture)
575 {
576  RunTest<1>({ 2 }, { 34 });
577 }
578 
BOOST_AUTO_TEST_SUITE(TensorflowLiteParser)
void FullyConnected(const TensorShape &rInputShape, Decoder< float > &rInputDecoder, const TensorShape &rOutputShape, Encoder< float > &rOutputEncoder, Decoder< float > &rWeightDecoder, Decoder< float > &rBiasDecoder, const bool biasEnabled, const unsigned int K, const bool transposeWeights)
Performs a matrix multiplication and optionally adds a bias.
BOOST_AUTO_TEST_SUITE_END()
BOOST_FIXTURE_TEST_CASE(MatMul, MatMulFixture)
void SetupSingleInputSingleOutput(const std::string &inputName, const std::string &outputName)
Parses and loads the network defined by the m_Prototext string.