blob: b2a3c889e6980d93127c8dbb55e2dea430b7e1e1 [file] [log] [blame]
Sadik Armagan32ca1442020-11-13 17:51:56 +00001//
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
19namespace
20{
21
22template <typename T, typename B = float>
23std::vector<char> CreateConv2dTfLiteModel(tflite::BuiltinOperator convolutionOperatorCode,
24 tflite::TensorType tensorType,
25 uint32_t strideX,
26 uint32_t strideY,
27 uint32_t dilationX,
28 uint32_t dilationY,
29 tflite::Padding padding,
30 tflite::ActivationFunctionType fused_activation_function,
31 const std::vector <int32_t>& inputTensorShape,
32 const std::vector <int32_t>& filterTensorShape,
33 const std::vector <int32_t>& biasTensorShape,
34 const std::vector <int32_t>& outputTensorShape,
35 const std::vector <T>& filterData,
36 const std::vector <B>& biasData,
37 float filterScale = 1.0f,
38 int filterOffset = 0,
39 float outputQuantScale = 2.0f,
40 int outputQuantOffset = 0,
41 float quantScale = 1.0f,
42 int quantOffset = 0,
43 int32_t depth_multiplier = 1)
44{
45 using namespace tflite;
46 flatbuffers::FlatBufferBuilder flatBufferBuilder;
47
48 std::array<flatbuffers::Offset<tflite::Buffer>, 3> buffers;
49 buffers[0] = CreateBuffer(flatBufferBuilder, flatBufferBuilder.CreateVector({}));
50 buffers[1] = CreateBuffer(flatBufferBuilder,
51 flatBufferBuilder.CreateVector(reinterpret_cast<const uint8_t*>(filterData.data()),
52 sizeof(T) * filterData.size()));
53
54 buffers[2] = CreateBuffer(flatBufferBuilder,
55 flatBufferBuilder.CreateVector(reinterpret_cast<const uint8_t*>(biasData.data()),
56 sizeof(B) * biasData.size()));
57
58 auto quantizationParameters =
59 CreateQuantizationParameters(flatBufferBuilder,
60 0,
61 0,
62 flatBufferBuilder.CreateVector<float>({ quantScale }),
63 flatBufferBuilder.CreateVector<int64_t>({ quantOffset }));
64 auto outputQuantizationParameters =
65 CreateQuantizationParameters(flatBufferBuilder,
66 0,
67 0,
68 flatBufferBuilder.CreateVector<float>({ outputQuantScale }),
69 flatBufferBuilder.CreateVector<int64_t>({ outputQuantOffset }));
70 auto filterQuantizationParameters =
71 CreateQuantizationParameters(flatBufferBuilder,
72 0,
73 0,
74 flatBufferBuilder.CreateVector<float>({ filterScale }),
75 flatBufferBuilder.CreateVector<int64_t>({ filterOffset }));
76
77 std::array<flatbuffers::Offset<Tensor>, 4> tensors;
78 tensors[0] = CreateTensor(flatBufferBuilder,
79 flatBufferBuilder.CreateVector<int32_t>(inputTensorShape.data(),
80 inputTensorShape.size()),
81 tensorType,
82 0,
83 flatBufferBuilder.CreateString("input"),
84 quantizationParameters);
85 tensors[1] = CreateTensor(flatBufferBuilder,
86 flatBufferBuilder.CreateVector<int32_t>(filterTensorShape.data(),
87 filterTensorShape.size()),
88 tensorType,
89 1,
90 flatBufferBuilder.CreateString("filter"),
91 filterQuantizationParameters);
92
93 auto biasTensorType = ::tflite::TensorType_FLOAT32;
Jan Eilerseb616122020-11-20 11:59:40 +000094 if (tensorType == ::tflite::TensorType_INT8 || tensorType == ::tflite::TensorType_UINT8)
Sadik Armagan32ca1442020-11-13 17:51:56 +000095 {
96 biasTensorType = ::tflite::TensorType_INT32;
97 }
98 tensors[2] = CreateTensor(flatBufferBuilder,
99 flatBufferBuilder.CreateVector<int32_t>(biasTensorShape.data(), biasTensorShape.size()),
100 biasTensorType,
101 2,
102 flatBufferBuilder.CreateString("bias"),
103 quantizationParameters);
104 tensors[3] = CreateTensor(flatBufferBuilder,
105 flatBufferBuilder.CreateVector<int32_t>(outputTensorShape.data(),
106 outputTensorShape.size()),
107 tensorType,
108 0,
109 flatBufferBuilder.CreateString("output"),
110 outputQuantizationParameters);
111
112 flatbuffers::Offset<void> operatorBuiltinOptions;
113 tflite::BuiltinOptions operatorBuiltinOptionsType;
114
115 if(convolutionOperatorCode == tflite::BuiltinOperator_DEPTHWISE_CONV_2D)
116 {
117 operatorBuiltinOptionsType = tflite::BuiltinOptions_DepthwiseConv2DOptions;
118 operatorBuiltinOptions = CreateDepthwiseConv2DOptions(flatBufferBuilder,
119 padding,
120 strideX,
121 strideY,
122 depth_multiplier,
123 fused_activation_function,
124 dilationX,
125 dilationY).Union();
126 }
127 if(convolutionOperatorCode == tflite::BuiltinOperator_CONV_2D)
128 {
129 operatorBuiltinOptionsType = tflite::BuiltinOptions_Conv2DOptions;
130 operatorBuiltinOptions = CreateConv2DOptions(flatBufferBuilder,
131 padding,
132 strideX,
133 strideY,
134 fused_activation_function,
135 dilationX,
136 dilationY).Union();
137 }
138
139 // create operator
Keith Davis892fafe2020-11-26 17:40:35 +0000140 const std::vector<int> operatorInputs{0, 1, 2};
141 const std::vector<int> operatorOutputs{3};
Sadik Armagan32ca1442020-11-13 17:51:56 +0000142 flatbuffers::Offset <Operator> convolutionOperator =
143 CreateOperator(flatBufferBuilder,
144 0,
145 flatBufferBuilder.CreateVector<int32_t>(operatorInputs.data(), operatorInputs.size()),
146 flatBufferBuilder.CreateVector<int32_t>(operatorOutputs.data(), operatorOutputs.size()),
147 operatorBuiltinOptionsType,
148 operatorBuiltinOptions);
149
Keith Davis892fafe2020-11-26 17:40:35 +0000150 const std::vector<int> subgraphInputs{0, 1, 2};
151 const std::vector<int> subgraphOutputs{3};
Sadik Armagan32ca1442020-11-13 17:51:56 +0000152 flatbuffers::Offset <SubGraph> subgraph =
153 CreateSubGraph(flatBufferBuilder,
154 flatBufferBuilder.CreateVector(tensors.data(), tensors.size()),
155 flatBufferBuilder.CreateVector<int32_t>(subgraphInputs.data(), subgraphInputs.size()),
156 flatBufferBuilder.CreateVector<int32_t>(subgraphOutputs.data(), subgraphOutputs.size()),
157 flatBufferBuilder.CreateVector(&convolutionOperator, 1));
158
159 flatbuffers::Offset <flatbuffers::String> modelDescription =
160 flatBufferBuilder.CreateString("ArmnnDelegate: Convolution2d Operator Model");
161 flatbuffers::Offset <OperatorCode> operatorCode = CreateOperatorCode(flatBufferBuilder, convolutionOperatorCode);
162
163 flatbuffers::Offset <Model> flatbufferModel =
164 CreateModel(flatBufferBuilder,
165 TFLITE_SCHEMA_VERSION,
166 flatBufferBuilder.CreateVector(&operatorCode, 1),
167 flatBufferBuilder.CreateVector(&subgraph, 1),
168 modelDescription,
169 flatBufferBuilder.CreateVector(buffers.data(), buffers.size()));
170
171 flatBufferBuilder.Finish(flatbufferModel);
172
173 return std::vector<char>(flatBufferBuilder.GetBufferPointer(),
174 flatBufferBuilder.GetBufferPointer() + flatBufferBuilder.GetSize());
175}
176
177template <typename T, typename B = float>
178void ConvolutionTest(tflite::BuiltinOperator convolutionOperatorCode,
179 tflite::TensorType tensorType,
180 uint32_t strideX,
181 uint32_t strideY,
182 uint32_t dilationX,
183 uint32_t dilationY,
184 tflite::Padding padding,
185 tflite::ActivationFunctionType fused_activation_function,
186 std::vector<armnn::BackendId>& backends,
187 std::vector<int32_t>& inputShape,
188 std::vector<int32_t>& filterShape,
189 std::vector<int32_t>& outputShape,
190 std::vector<T>& inputValues,
191 std::vector<T>& filterValues,
192 std::vector<T>& expectedOutputValues,
193 const std::vector<int32_t>& biasShape = {},
194 const std::vector<B>& biasValues = {},
195 float filterScale = 1.0f,
196 int filterOffset = 0,
197 float outputQuantScale = 2.0f,
198 int outputQuantOffset = 0,
199 float quantScale = 1.0f,
200 int quantOffset = 0,
201 int32_t depth_multiplier = 1)
202
203{
204 using namespace tflite;
205
206 std::vector<char> modelBuffer;
207 modelBuffer = CreateConv2dTfLiteModel(convolutionOperatorCode,
208 tensorType,
209 strideX,
210 strideY,
211 dilationX,
212 dilationY,
213 padding,
214 fused_activation_function,
215 inputShape,
216 filterShape,
217 biasShape,
218 outputShape,
219 filterValues,
220 biasValues,
221 filterScale,
222 filterOffset,
223 outputQuantScale,
224 outputQuantOffset,
225 quantScale,
226 quantOffset,
227 depth_multiplier);
228
229
230 const Model* tfLiteModel = GetModel(modelBuffer.data());
231 // Create TfLite Interpreters
232 std::unique_ptr<Interpreter> armnnDelegateInterpreter;
233 CHECK(InterpreterBuilder(tfLiteModel, ::tflite::ops::builtin::BuiltinOpResolver())
234 (&armnnDelegateInterpreter) == kTfLiteOk);
235 CHECK(armnnDelegateInterpreter != nullptr);
236 CHECK(armnnDelegateInterpreter->AllocateTensors() == kTfLiteOk);
237
238 std::unique_ptr<Interpreter> tfLiteInterpreter;
239 CHECK(InterpreterBuilder(tfLiteModel, ::tflite::ops::builtin::BuiltinOpResolver())
240 (&tfLiteInterpreter) == kTfLiteOk);
241 CHECK(tfLiteInterpreter != nullptr);
242 CHECK(tfLiteInterpreter->AllocateTensors() == kTfLiteOk);
243
244 // Create the ArmNN Delegate
245 armnnDelegate::DelegateOptions delegateOptions(backends);
246 std::unique_ptr<TfLiteDelegate, decltype(&armnnDelegate::TfLiteArmnnDelegateDelete)>
247 theArmnnDelegate(armnnDelegate::TfLiteArmnnDelegateCreate(delegateOptions),
248 armnnDelegate::TfLiteArmnnDelegateDelete);
249 CHECK(theArmnnDelegate != nullptr);
250 // Modify armnnDelegateInterpreter to use armnnDelegate
251 CHECK(armnnDelegateInterpreter->ModifyGraphWithDelegate(theArmnnDelegate.get()) == kTfLiteOk);
252
253 // Set input data
254 auto tfLiteDelegateInputId = tfLiteInterpreter->inputs()[0];
255 auto tfLiteDelageInputData = tfLiteInterpreter->typed_tensor<T>(tfLiteDelegateInputId);
256 for (unsigned int i = 0; i < inputValues.size(); ++i)
257 {
258 tfLiteDelageInputData[i] = inputValues[i];
259 }
260
261 auto armnnDelegateInputId = armnnDelegateInterpreter->inputs()[0];
262 auto armnnDelegateInputData = armnnDelegateInterpreter->typed_tensor<T>(armnnDelegateInputId);
263 for (unsigned int i = 0; i < inputValues.size(); ++i)
264 {
265 armnnDelegateInputData[i] = inputValues[i];
266 }
267 // Run EnqueueWorkload
268 CHECK(tfLiteInterpreter->Invoke() == kTfLiteOk);
269 CHECK(armnnDelegateInterpreter->Invoke() == kTfLiteOk);
270
271 // Compare output data
272 auto tfLiteDelegateOutputId = tfLiteInterpreter->outputs()[0];
273 auto tfLiteDelagateOutputData = tfLiteInterpreter->typed_tensor<T>(tfLiteDelegateOutputId);
274 auto armnnDelegateOutputId = armnnDelegateInterpreter->outputs()[0];
275 auto armnnDelegateOutputData = armnnDelegateInterpreter->typed_tensor<T>(armnnDelegateOutputId);
276 for (size_t i = 0; i < expectedOutputValues.size(); i++)
277 {
278 CHECK(tfLiteDelagateOutputData[i] == armnnDelegateOutputData[i]);
279 CHECK(doctest::Approx(tfLiteDelagateOutputData[i]).epsilon(0.000001f) == expectedOutputValues[i]);
280 CHECK(doctest::Approx(armnnDelegateOutputData[i]).epsilon(0.000001f) == expectedOutputValues[i]);
281 }
282}
283
284template <typename T>
285std::vector<char> CreateTransposeConvTfLiteModel(tflite::TensorType tensorType,
286 uint32_t strideX,
287 uint32_t strideY,
288 tflite::Padding padding,
289 const std::vector <int32_t>& transposeTensorShape,
290 const std::vector <int32_t>& filterTensorShape,
291 const std::vector <int32_t>& inputTensorShape,
292 const std::vector <int32_t>& outputTensorShape,
293 const std::vector <int32_t>& transposeData,
294 const std::vector <T>& filterData,
295 float filterScale = 1.0f,
296 int filterOffset = 0,
297 float outputQuantScale = 2.0f,
298 int outputQuantOffset = 0,
299 float quantScale = 1.0f,
300 int quantOffset = 0)
301{
302 using namespace tflite;
303 flatbuffers::FlatBufferBuilder flatBufferBuilder;
304
305 std::array<flatbuffers::Offset<tflite::Buffer>, 3> buffers;
306 buffers[0] = CreateBuffer(flatBufferBuilder, flatBufferBuilder.CreateVector({}));
307 buffers[1] = CreateBuffer(flatBufferBuilder,
308 flatBufferBuilder.CreateVector(reinterpret_cast<const uint8_t*>(transposeData.data()),
309 sizeof(int32_t) * transposeData.size()));
310 buffers[2] = CreateBuffer(flatBufferBuilder,
311 flatBufferBuilder.CreateVector(reinterpret_cast<const uint8_t*>(filterData.data()),
312 sizeof(T) * filterData.size()));
313
314 auto quantizationParameters =
315 CreateQuantizationParameters(flatBufferBuilder,
316 0,
317 0,
318 flatBufferBuilder.CreateVector<float>({ quantScale }),
319 flatBufferBuilder.CreateVector<int64_t>({ quantOffset }));
320 auto outputQuantizationParameters =
321 CreateQuantizationParameters(flatBufferBuilder,
322 0,
323 0,
324 flatBufferBuilder.CreateVector<float>({ outputQuantScale }),
325 flatBufferBuilder.CreateVector<int64_t>({ outputQuantOffset }));
326 auto filterQuantizationParameters =
327 CreateQuantizationParameters(flatBufferBuilder,
328 0,
329 0,
330 flatBufferBuilder.CreateVector<float>({ filterScale }),
331 flatBufferBuilder.CreateVector<int64_t>({ filterOffset }));
332
333 std::array<flatbuffers::Offset<Tensor>, 4> tensors;
334 tensors[0] = CreateTensor(flatBufferBuilder,
335 flatBufferBuilder.CreateVector<int32_t>(transposeTensorShape.data(),
336 transposeTensorShape.size()),
337 tflite::TensorType_INT32,
338 1);
339 tensors[1] = CreateTensor(flatBufferBuilder,
340 flatBufferBuilder.CreateVector<int32_t>(filterTensorShape.data(),
341 filterTensorShape.size()),
342 tensorType,
343 2,
344 flatBufferBuilder.CreateString("filter"),
345 filterQuantizationParameters);
346 tensors[2] = CreateTensor(flatBufferBuilder,
347 flatBufferBuilder.CreateVector<int32_t>(inputTensorShape.data(),
348 inputTensorShape.size()),
349 tensorType,
350 0,
351 flatBufferBuilder.CreateString("input"),
352 quantizationParameters);
353 tensors[3] = CreateTensor(flatBufferBuilder,
354 flatBufferBuilder.CreateVector<int32_t>(outputTensorShape.data(),
355 outputTensorShape.size()),
356 tensorType,
357 0,
358 flatBufferBuilder.CreateString("output"),
359 outputQuantizationParameters);
360
361 tflite::BuiltinOptions operatorBuiltinOptionsType = tflite::BuiltinOptions_TransposeConvOptions;
362 flatbuffers::Offset<void> operatorBuiltinOptions =
363 CreateTransposeConvOptions(flatBufferBuilder, padding, strideX, strideY).Union();
364
365 // create operator
Keith Davis892fafe2020-11-26 17:40:35 +0000366 const std::vector<int> operatorInputs{0, 1, 2};
367 const std::vector<int> operatorOutputs{3};
Sadik Armagan32ca1442020-11-13 17:51:56 +0000368 flatbuffers::Offset <Operator> convolutionOperator =
369 CreateOperator(flatBufferBuilder,
370 0,
371 flatBufferBuilder.CreateVector<int32_t>(operatorInputs.data(), operatorInputs.size()),
372 flatBufferBuilder.CreateVector<int32_t>(operatorOutputs.data(), operatorOutputs.size()),
373 operatorBuiltinOptionsType,
374 operatorBuiltinOptions);
375
Keith Davis892fafe2020-11-26 17:40:35 +0000376 const std::vector<int> subgraphInputs{0, 1, 2};
377 const std::vector<int> subgraphOutputs{3};
Sadik Armagan32ca1442020-11-13 17:51:56 +0000378 flatbuffers::Offset <SubGraph> subgraph =
379 CreateSubGraph(flatBufferBuilder,
380 flatBufferBuilder.CreateVector(tensors.data(), tensors.size()),
381 flatBufferBuilder.CreateVector<int32_t>(subgraphInputs.data(), subgraphInputs.size()),
382 flatBufferBuilder.CreateVector<int32_t>(subgraphOutputs.data(), subgraphOutputs.size()),
383 flatBufferBuilder.CreateVector(&convolutionOperator, 1));
384
385 flatbuffers::Offset <flatbuffers::String> modelDescription =
386 flatBufferBuilder.CreateString("ArmnnDelegate: TransposeConv Operator Model");
387 flatbuffers::Offset <OperatorCode> operatorCode =
388 CreateOperatorCode(flatBufferBuilder, tflite::BuiltinOperator_TRANSPOSE_CONV);
389
390 flatbuffers::Offset <Model> flatbufferModel =
391 CreateModel(flatBufferBuilder,
392 TFLITE_SCHEMA_VERSION,
393 flatBufferBuilder.CreateVector(&operatorCode, 1),
394 flatBufferBuilder.CreateVector(&subgraph, 1),
395 modelDescription,
396 flatBufferBuilder.CreateVector(buffers.data(), buffers.size()));
397
398 flatBufferBuilder.Finish(flatbufferModel);
399
400 return std::vector<char>(flatBufferBuilder.GetBufferPointer(),
401 flatBufferBuilder.GetBufferPointer() + flatBufferBuilder.GetSize());
402}
403
404template <typename T>
405void TransposeConvTest(std::vector<armnn::BackendId>& backends,
406 tflite::TensorType tensorType,
407 uint32_t strideX,
408 uint32_t strideY,
409 tflite::Padding padding,
410 const std::vector <int32_t>& transposeTensorShape,
411 const std::vector <int32_t>& filterTensorShape,
412 const std::vector <int32_t>& inputTensorShape,
413 const std::vector <int32_t>& outputTensorShape,
414 const std::vector <int32_t>& transposeData,
415 const std::vector <T>& filterData,
416 std::vector<T>& inputValues,
417 std::vector<T>& expectedOutputValues,
418 float filterScale = 1.0f,
419 int filterOffset = 0,
420 float outputQuantScale = 1.0f,
421 int outputQuantOffset = 0,
422 float quantScale = 1.0f,
423 int quantOffset = 0)
424{
425 using namespace tflite;
426
427 std::vector<char> modelBuffer;
428 modelBuffer = CreateTransposeConvTfLiteModel<T>(tensorType,
429 strideX,
430 strideY,
431 padding,
432 transposeTensorShape,
433 filterTensorShape,
434 inputTensorShape,
435 outputTensorShape,
436 transposeData,
437 filterData,
438 filterScale,
439 filterOffset,
440 outputQuantScale,
441 outputQuantOffset,
442 quantScale,
443 quantOffset);
444
445
446 const Model* tfLiteModel = GetModel(modelBuffer.data());
447 // Create TfLite Interpreters
448 std::unique_ptr<Interpreter> armnnDelegateInterpreter;
449 CHECK(InterpreterBuilder(tfLiteModel, ::tflite::ops::builtin::BuiltinOpResolver())
450 (&armnnDelegateInterpreter) == kTfLiteOk);
451 CHECK(armnnDelegateInterpreter != nullptr);
452 CHECK(armnnDelegateInterpreter->AllocateTensors() == kTfLiteOk);
453
454 std::unique_ptr<Interpreter> tfLiteInterpreter;
455 CHECK(InterpreterBuilder(tfLiteModel, ::tflite::ops::builtin::BuiltinOpResolver())
456 (&tfLiteInterpreter) == kTfLiteOk);
457 CHECK(tfLiteInterpreter != nullptr);
458 CHECK(tfLiteInterpreter->AllocateTensors() == kTfLiteOk);
459
460 // Create the ArmNN Delegate
461 armnnDelegate::DelegateOptions delegateOptions(backends);
462 std::unique_ptr<TfLiteDelegate, decltype(&armnnDelegate::TfLiteArmnnDelegateDelete)>
463 theArmnnDelegate(armnnDelegate::TfLiteArmnnDelegateCreate(delegateOptions),
464 armnnDelegate::TfLiteArmnnDelegateDelete);
465 CHECK(theArmnnDelegate != nullptr);
466 // Modify armnnDelegateInterpreter to use armnnDelegate
467 CHECK(armnnDelegateInterpreter->ModifyGraphWithDelegate(theArmnnDelegate.get()) == kTfLiteOk);
468
469 // Set input data
470 auto tfLiteDelegateInputId = tfLiteInterpreter->inputs()[2];
471 auto tfLiteDelageInputData = tfLiteInterpreter->typed_tensor<T>(tfLiteDelegateInputId);
472 for (unsigned int i = 0; i < inputValues.size(); ++i)
473 {
474 tfLiteDelageInputData[i] = inputValues[i];
475 }
476
477 auto armnnDelegateInputId = armnnDelegateInterpreter->inputs()[2];
478 auto armnnDelegateInputData = armnnDelegateInterpreter->typed_tensor<T>(armnnDelegateInputId);
479 for (unsigned int i = 0; i < inputValues.size(); ++i)
480 {
481 armnnDelegateInputData[i] = inputValues[i];
482 }
483 // Run EnqueueWorkload
484 CHECK(tfLiteInterpreter->Invoke() == kTfLiteOk);
485 CHECK(armnnDelegateInterpreter->Invoke() == kTfLiteOk);
486
487 // Compare output data
488 auto tfLiteDelegateOutputId = tfLiteInterpreter->outputs()[0];
489 auto tfLiteDelagateOutputData = tfLiteInterpreter->typed_tensor<T>(tfLiteDelegateOutputId);
490 auto armnnDelegateOutputId = armnnDelegateInterpreter->outputs()[0];
491 auto armnnDelegateOutputData = armnnDelegateInterpreter->typed_tensor<T>(armnnDelegateOutputId);
492 for (size_t i = 0; i < expectedOutputValues.size(); i++)
493 {
494 CHECK(armnnDelegateOutputData[i] == expectedOutputValues[i]);
495 CHECK(tfLiteDelagateOutputData[i] == expectedOutputValues[i]);
496 CHECK(tfLiteDelagateOutputData[i] == armnnDelegateOutputData[i]);
497 }
498}
499
500} // anonymous namespace
501
502
503
504