• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //
2 // Copyright © 2020 Arm Ltd and Contributors. All rights reserved.
3 // SPDX-License-Identifier: MIT
4 //
5 
6 #pragma once
7 
8 #include <armnn_delegate.hpp>
9 
10 #include <flatbuffers/flatbuffers.h>
11 #include <tensorflow/lite/interpreter.h>
12 #include <tensorflow/lite/kernels/register.h>
13 #include <tensorflow/lite/model.h>
14 #include <tensorflow/lite/schema/schema_generated.h>
15 #include <tensorflow/lite/version.h>
16 
17 #include <doctest/doctest.h>
18 
19 namespace
20 {
21 
22 template <typename T>
CreateFullyConnectedTfLiteModel(tflite::TensorType tensorType,tflite::ActivationFunctionType activationType,const std::vector<int32_t> & inputTensorShape,const std::vector<int32_t> & weightsTensorShape,const std::vector<int32_t> & biasTensorShape,const std::vector<int32_t> & outputTensorShape,const std::vector<T> & weightsData,float quantScale=1.0f,int quantOffset=0,float outputQuantScale=2.0f,int outputQuantOffset=0)23 std::vector<char> CreateFullyConnectedTfLiteModel(tflite::TensorType tensorType,
24                                                   tflite::ActivationFunctionType activationType,
25                                                   const std::vector <int32_t>& inputTensorShape,
26                                                   const std::vector <int32_t>& weightsTensorShape,
27                                                   const std::vector <int32_t>& biasTensorShape,
28                                                   const std::vector <int32_t>& outputTensorShape,
29                                                   const std::vector <T>& weightsData,
30                                                   float quantScale = 1.0f,
31                                                   int quantOffset  = 0,
32                                                   float outputQuantScale = 2.0f,
33                                                   int outputQuantOffset  = 0)
34 {
35     using namespace tflite;
36     flatbuffers::FlatBufferBuilder flatBufferBuilder;
37     std::array<flatbuffers::Offset<tflite::Buffer>, 3> buffers;
38     buffers[0] = CreateBuffer(flatBufferBuilder, flatBufferBuilder.CreateVector({}));
39     buffers[1] = CreateBuffer(flatBufferBuilder,
40                      flatBufferBuilder.CreateVector(reinterpret_cast<const uint8_t*>(weightsData.data()),
41                                                     sizeof(T) * weightsData.size()));
42 
43     auto biasTensorType = ::tflite::TensorType_FLOAT32;
44     if (tensorType == ::tflite::TensorType_INT8)
45     {
46         biasTensorType = ::tflite::TensorType_INT32;
47         std::vector<int32_t> biasData = { 10 };
48         buffers[2] = CreateBuffer(flatBufferBuilder,
49                                   flatBufferBuilder.CreateVector(reinterpret_cast<const uint8_t*>(biasData.data()),
50                                                                  sizeof(int32_t) * biasData.size()));
51 
52     }
53     else
54     {
55         std::vector<float> biasData = { 10 };
56         buffers[2] = CreateBuffer(flatBufferBuilder,
57                                   flatBufferBuilder.CreateVector(reinterpret_cast<const uint8_t*>(biasData.data()),
58                                                                  sizeof(float) * biasData.size()));
59     }
60 
61     auto quantizationParameters =
62         CreateQuantizationParameters(flatBufferBuilder,
63                                      0,
64                                      0,
65                                      flatBufferBuilder.CreateVector<float>({ quantScale }),
66                                      flatBufferBuilder.CreateVector<int64_t>({ quantOffset }));
67 
68     auto outputQuantizationParameters =
69         CreateQuantizationParameters(flatBufferBuilder,
70                                      0,
71                                      0,
72                                      flatBufferBuilder.CreateVector<float>({ outputQuantScale }),
73                                      flatBufferBuilder.CreateVector<int64_t>({ outputQuantOffset }));
74 
75     std::array<flatbuffers::Offset<Tensor>, 4> tensors;
76     tensors[0] = CreateTensor(flatBufferBuilder,
77                               flatBufferBuilder.CreateVector<int32_t>(inputTensorShape.data(),
78                                                                       inputTensorShape.size()),
79                               tensorType,
80                               0,
81                               flatBufferBuilder.CreateString("input_0"),
82                               quantizationParameters);
83     tensors[1] = CreateTensor(flatBufferBuilder,
84                               flatBufferBuilder.CreateVector<int32_t>(weightsTensorShape.data(),
85                                                                       weightsTensorShape.size()),
86                               tensorType,
87                               1,
88                               flatBufferBuilder.CreateString("weights"),
89                               quantizationParameters);
90     tensors[2] = CreateTensor(flatBufferBuilder,
91                               flatBufferBuilder.CreateVector<int32_t>(biasTensorShape.data(),
92                                                                       biasTensorShape.size()),
93                               biasTensorType,
94                               2,
95                               flatBufferBuilder.CreateString("bias"),
96                               quantizationParameters);
97 
98     tensors[3] = CreateTensor(flatBufferBuilder,
99                               flatBufferBuilder.CreateVector<int32_t>(outputTensorShape.data(),
100                                                                       outputTensorShape.size()),
101                               tensorType,
102                               0,
103                               flatBufferBuilder.CreateString("output"),
104                               outputQuantizationParameters);
105 
106 
107     // create operator
108     tflite::BuiltinOptions operatorBuiltinOptionsType = BuiltinOptions_FullyConnectedOptions;
109     flatbuffers::Offset<void> operatorBuiltinOptions =
110         CreateFullyConnectedOptions(flatBufferBuilder,
111                                     activationType,
112                                     FullyConnectedOptionsWeightsFormat_DEFAULT, false).Union();
113 
114     const std::vector<int> operatorInputs{ {0, 1, 2} };
115     const std::vector<int> operatorOutputs{ {3} };
116     flatbuffers::Offset <Operator> fullyConnectedOperator =
117         CreateOperator(flatBufferBuilder,
118                        0,
119                        flatBufferBuilder.CreateVector<int32_t>(operatorInputs.data(), operatorInputs.size()),
120                        flatBufferBuilder.CreateVector<int32_t>(operatorOutputs.data(), operatorOutputs.size()),
121                        operatorBuiltinOptionsType, operatorBuiltinOptions);
122 
123     const std::vector<int> subgraphInputs{ {0, 1, 2} };
124     const std::vector<int> subgraphOutputs{ {3} };
125     flatbuffers::Offset <SubGraph> subgraph =
126         CreateSubGraph(flatBufferBuilder,
127                        flatBufferBuilder.CreateVector(tensors.data(), tensors.size()),
128                        flatBufferBuilder.CreateVector<int32_t>(subgraphInputs.data(), subgraphInputs.size()),
129                        flatBufferBuilder.CreateVector<int32_t>(subgraphOutputs.data(), subgraphOutputs.size()),
130                        flatBufferBuilder.CreateVector(&fullyConnectedOperator, 1));
131 
132     flatbuffers::Offset <flatbuffers::String> modelDescription =
133         flatBufferBuilder.CreateString("ArmnnDelegate: FullyConnected Operator Model");
134     flatbuffers::Offset <OperatorCode> operatorCode = CreateOperatorCode(flatBufferBuilder,
135                                                                          tflite::BuiltinOperator_FULLY_CONNECTED);
136 
137     flatbuffers::Offset <Model> flatbufferModel =
138         CreateModel(flatBufferBuilder,
139                     TFLITE_SCHEMA_VERSION,
140                     flatBufferBuilder.CreateVector(&operatorCode, 1),
141                     flatBufferBuilder.CreateVector(&subgraph, 1),
142                     modelDescription,
143                     flatBufferBuilder.CreateVector(buffers.data(), buffers.size()));
144 
145     flatBufferBuilder.Finish(flatbufferModel);
146 
147     return std::vector<char>(flatBufferBuilder.GetBufferPointer(),
148                              flatBufferBuilder.GetBufferPointer() + flatBufferBuilder.GetSize());
149 }
150 
151 template <typename T>
FullyConnectedTest(std::vector<armnn::BackendId> & backends,tflite::TensorType tensorType,tflite::ActivationFunctionType activationType,const std::vector<int32_t> & inputTensorShape,const std::vector<int32_t> & weightsTensorShape,const std::vector<int32_t> & biasTensorShape,const std::vector<int32_t> & outputTensorShape,const std::vector<T> & inputValues,const std::vector<T> & expectedOutputValues,const std::vector<T> & weightsData,float quantScale=1.0f,int quantOffset=0)152 void FullyConnectedTest(std::vector<armnn::BackendId>& backends,
153                         tflite::TensorType tensorType,
154                         tflite::ActivationFunctionType activationType,
155                         const std::vector <int32_t>& inputTensorShape,
156                         const std::vector <int32_t>& weightsTensorShape,
157                         const std::vector <int32_t>& biasTensorShape,
158                         const std::vector <int32_t>& outputTensorShape,
159                         const std::vector <T>& inputValues,
160                         const std::vector <T>& expectedOutputValues,
161                         const std::vector <T>& weightsData,
162                         float quantScale = 1.0f,
163                         int quantOffset  = 0)
164 {
165     using namespace tflite;
166 
167     std::vector<char> modelBuffer = CreateFullyConnectedTfLiteModel(tensorType,
168                                                                     activationType,
169                                                                     inputTensorShape,
170                                                                     weightsTensorShape,
171                                                                     biasTensorShape,
172                                                                     outputTensorShape,
173                                                                     weightsData,
174                                                                     quantScale,
175                                                                     quantOffset);
176 
177     const Model* tfLiteModel = GetModel(modelBuffer.data());
178     // Create TfLite Interpreters
179     std::unique_ptr<Interpreter> armnnDelegateInterpreter;
180     CHECK(InterpreterBuilder(tfLiteModel, ::tflite::ops::builtin::BuiltinOpResolver())
181               (&armnnDelegateInterpreter) == kTfLiteOk);
182     CHECK(armnnDelegateInterpreter != nullptr);
183     CHECK(armnnDelegateInterpreter->AllocateTensors() == kTfLiteOk);
184 
185     std::unique_ptr<Interpreter> tfLiteInterpreter;
186     CHECK(InterpreterBuilder(tfLiteModel, ::tflite::ops::builtin::BuiltinOpResolver())
187               (&tfLiteInterpreter) == kTfLiteOk);
188     CHECK(tfLiteInterpreter != nullptr);
189     CHECK(tfLiteInterpreter->AllocateTensors() == kTfLiteOk);
190 
191     // Create the ArmNN Delegate
192     armnnDelegate::DelegateOptions delegateOptions(backends);
193     std::unique_ptr<TfLiteDelegate, decltype(&armnnDelegate::TfLiteArmnnDelegateDelete)>
194                         theArmnnDelegate(armnnDelegate::TfLiteArmnnDelegateCreate(delegateOptions),
195                                          armnnDelegate::TfLiteArmnnDelegateDelete);
196     CHECK(theArmnnDelegate != nullptr);
197     // Modify armnnDelegateInterpreter to use armnnDelegate
198     CHECK(armnnDelegateInterpreter->ModifyGraphWithDelegate(theArmnnDelegate.get()) == kTfLiteOk);
199 
200     // Set input data
201     auto tfLiteDelegateInputId = tfLiteInterpreter->inputs()[0];
202     auto tfLiteDelageInputData = tfLiteInterpreter->typed_tensor<T>(tfLiteDelegateInputId);
203     for (unsigned int i = 0; i < inputValues.size(); ++i)
204     {
205         tfLiteDelageInputData[i] = inputValues[i];
206     }
207 
208     auto armnnDelegateInputId = armnnDelegateInterpreter->inputs()[0];
209     auto armnnDelegateInputData = armnnDelegateInterpreter->typed_tensor<T>(armnnDelegateInputId);
210     for (unsigned int i = 0; i < inputValues.size(); ++i)
211     {
212         armnnDelegateInputData[i] = inputValues[i];
213     }
214 
215     // Run EnqueWorkload
216     CHECK(tfLiteInterpreter->Invoke() == kTfLiteOk);
217     CHECK(armnnDelegateInterpreter->Invoke() == kTfLiteOk);
218 
219     // Compare output data
220     auto tfLiteDelegateOutputId = tfLiteInterpreter->outputs()[0];
221     auto tfLiteDelageOutputData = tfLiteInterpreter->typed_tensor<T>(tfLiteDelegateOutputId);
222     auto armnnDelegateOutputId = armnnDelegateInterpreter->outputs()[0];
223     auto armnnDelegateOutputData = armnnDelegateInterpreter->typed_tensor<T>(armnnDelegateOutputId);
224     for (size_t i = 0; i < expectedOutputValues.size(); i++)
225     {
226         CHECK(expectedOutputValues[i] == tfLiteDelageOutputData[i]);
227         CHECK(expectedOutputValues[i] == armnnDelegateOutputData[i]);
228         CHECK(tfLiteDelageOutputData[i] == armnnDelegateOutputData[i]);
229     }
230 }
231 
232 } // anonymous namespace