blob: 433d538b7809f76852c899a7846440876a79df55 [file] [log] [blame]
Teresa Charlin83b42912022-07-07 14:24:59 +01001//
Colm Donelandce33822024-02-09 17:29:00 +00002// Copyright © 2022-2024 Arm Ltd and Contributors. All rights reserved.
Teresa Charlin83b42912022-07-07 14:24:59 +01003// SPDX-License-Identifier: MIT
4//
5
Narumol Prangnawarat46e574e2023-05-05 16:39:05 +01006#if defined(ARMNN_TFLITE_OPAQUE_DELEGATE)
7#include <../delegate/opaque/include/armnn_delegate.hpp>
8#endif
9
10#include <tensorflow/lite/core/c/c_api.h>
Teresa Charlin83b42912022-07-07 14:24:59 +010011#include "TfliteExecutor.hpp"
Colm Donelan3811a972023-01-25 21:19:49 +000012#include "tensorflow/lite/kernels/kernel_util.h"
Teresa Charlin83b42912022-07-07 14:24:59 +010013
Colm Donelandce33822024-02-09 17:29:00 +000014#include <chrono>
Colm Donelan0dfb2652023-06-22 10:19:17 +010015#include <string>
Colm Donelandce33822024-02-09 17:29:00 +000016#include <thread>
Colm Donelan0dfb2652023-06-22 10:19:17 +010017
18std::string TfLiteStatusToString(const TfLiteStatus status)
19{
20 switch (status)
21 {
22 case kTfLiteOk:
23 return "Status: Ok.";
24 // Generally referring to an error in the runtime (i.e. interpreter)
25 case kTfLiteError:
26 return "Status: Tf runtime error.";
27 // Generally referring to an error from a TfLiteDelegate itself.
28 case kTfLiteDelegateError:
29 return "Status: The loaded delegate has returned an error.";
30 // Generally referring to an error in applying a delegate due to
31 // incompatibility between runtime and delegate, e.g., this error is returned
32 // when trying to apply a TF Lite delegate onto a model graph that's already
33 // immutable.
34 case kTfLiteApplicationError:
35 return "Status: Application error. An incompatibility between the Tf runtime and the loaded delegate.";
36 // Generally referring to serialized delegate data not being found.
37 // See tflite::delegates::Serialization.
38 case kTfLiteDelegateDataNotFound:
39 return "Status: data not found.";
40 // Generally referring to data-writing issues in delegate serialization.
41 // See tflite::delegates::Serialization.
42 case kTfLiteDelegateDataWriteError:
43 return "Status: Error writing serialization data.";
44 // Generally referring to data-reading issues in delegate serialization.
45 // See tflite::delegates::Serialization.
46 case kTfLiteDelegateDataReadError:
47 return "Status: Error reading serialization data.";
48 // Generally referring to issues when the TF Lite model has ops that cannot be
49 // resolved at runtime. This could happen when the specific op is not
50 // registered or built with the TF Lite framework.
51 case kTfLiteUnresolvedOps:
52 return "Status: Model contains an operation that is not recognised by the runtime.";
53 // Generally referring to invocation cancelled by the user.
54 case kTfLiteCancelled:
55 return "Status: invocation has been cancelled by the user.";
56 }
57 return "Unknown status result.";
58}
59
Colm Donelan35a06892023-02-06 15:01:57 +000060TfLiteExecutor::TfLiteExecutor(const ExecuteNetworkParams& params, armnn::IRuntime::CreationOptions runtimeOptions)
61 : m_Params(params)
Teresa Charlin83b42912022-07-07 14:24:59 +010062{
Colm Donelandce33822024-02-09 17:29:00 +000063 using namespace std::chrono_literals;
Teresa Charlinc814e802022-08-05 13:57:04 +010064 m_Model = tflite::FlatBufferModel::BuildFromFile(m_Params.m_ModelPath.c_str());
Colm Donelan18e6f042023-01-24 22:10:12 +000065 if (!m_Model)
66 {
67 LogAndThrow("Failed to load TfLite model from: " + m_Params.m_ModelPath);
68 }
Teresa Charlin83b42912022-07-07 14:24:59 +010069 m_TfLiteInterpreter = std::make_unique<Interpreter>();
70 tflite::ops::builtin::BuiltinOpResolver resolver;
71
Teresa Charlinc814e802022-08-05 13:57:04 +010072 tflite::InterpreterBuilder builder(*m_Model, resolver);
Narumol Prangnawarat46e574e2023-05-05 16:39:05 +010073
74 if (m_Params.m_TfLiteExecutor == ExecuteNetworkParams::TfLiteExecutor::ArmNNTfLiteOpaqueDelegate)
Teresa Charlin83b42912022-07-07 14:24:59 +010075 {
Narumol Prangnawarat46e574e2023-05-05 16:39:05 +010076#if defined(ARMNN_TFLITE_OPAQUE_DELEGATE)
Narumol Prangnawaratfe1827d2023-05-09 16:01:12 +010077 if (builder(&m_TfLiteInterpreter) != kTfLiteOk)
78 {
79 LogAndThrow("Error loading the model into the TfLiteInterpreter.");
80 }
Teresa Charlin16ea11e2023-10-26 13:31:38 +010081 // Populate a DelegateOptions from the ExecuteNetworkParams.
82 armnnDelegate::DelegateOptions delegateOptions = m_Params.ToDelegateOptions();
83 delegateOptions.SetRuntimeOptions(runtimeOptions);
84 std::unique_ptr<TfLiteDelegate, decltype(&armnnOpaqueDelegate::TfLiteArmnnOpaqueDelegateDelete)>
85 theArmnnDelegate(armnnOpaqueDelegate::TfLiteArmnnOpaqueDelegateCreate(delegateOptions),
86 armnnOpaqueDelegate::TfLiteArmnnOpaqueDelegateDelete);
Narumol Prangnawaratfe1827d2023-05-09 16:01:12 +010087
Teresa Charlin16ea11e2023-10-26 13:31:38 +010088 // Register armnn_delegate to TfLiteInterpreter
89 auto result = m_TfLiteInterpreter->ModifyGraphWithDelegate(std::move(theArmnnDelegate));
90 if (result != kTfLiteOk)
91 {
92 LogAndThrow("Could not register ArmNN TfLite Opaque Delegate to TfLiteInterpreter: " +
93 TfLiteStatusToString(result) + ".");
94 }
Narumol Prangnawarat46e574e2023-05-05 16:39:05 +010095#else
96 LogAndThrow("Not built with Arm NN Tensorflow-Lite opaque delegate support.");
97#endif
98 }
99 else if (m_Params.m_TfLiteExecutor == ExecuteNetworkParams::TfLiteExecutor::ArmNNTfLiteDelegate)
100 {
101#if defined(ARMNN_TFLITE_DELEGATE)
Narumol Prangnawaratfe1827d2023-05-09 16:01:12 +0100102 if (builder(&m_TfLiteInterpreter) != kTfLiteOk)
103 {
104 LogAndThrow("Error loading the model into the TfLiteInterpreter.");
105 }
Teresa Charlin83b42912022-07-07 14:24:59 +0100106 // Create the Armnn Delegate
107 // Populate a DelegateOptions from the ExecuteNetworkParams.
108 armnnDelegate::DelegateOptions delegateOptions = m_Params.ToDelegateOptions();
Colm Donelan35a06892023-02-06 15:01:57 +0000109 delegateOptions.SetRuntimeOptions(runtimeOptions);
Teresa Charlin83b42912022-07-07 14:24:59 +0100110 std::unique_ptr<TfLiteDelegate, decltype(&armnnDelegate::TfLiteArmnnDelegateDelete)>
111 theArmnnDelegate(armnnDelegate::TfLiteArmnnDelegateCreate(delegateOptions),
112 armnnDelegate::TfLiteArmnnDelegateDelete);
113 // Register armnn_delegate to TfLiteInterpreter
Colm Donelan0dfb2652023-06-22 10:19:17 +0100114 auto result = m_TfLiteInterpreter->ModifyGraphWithDelegate(std::move(theArmnnDelegate));
115 if (result != kTfLiteOk)
Teresa Charlin83b42912022-07-07 14:24:59 +0100116 {
Colm Donelandce33822024-02-09 17:29:00 +0000117 // We'll make an exception for kTfLiteApplicationError and allow it through in special circumstances.
118 if (result == kTfLiteApplicationError)
119 {
120 std::cout << std::endl;
121 ARMNN_LOG(warning) << "*****";
122 ARMNN_LOG(warning) << "***** Calling ModifyGraphWithDelegate on the TfLite runtime resulted in "
123 "kTfLiteApplicationError. We will allow inference to continue but be warned the "
124 "results may be surprising!";
125 ARMNN_LOG(warning) << "***** There will now be a 5 second delay.";
126 ARMNN_LOG(warning) << "*****\n";
127 // Insert an intentional delay so the user is aware of this significant warning.
128 std::this_thread::sleep_for(5000ms);
129 }
130 else
131 {
132 LogAndThrow("Could not register ArmNN TfLite Delegate to TfLiteInterpreter: " +
133 TfLiteStatusToString(result) + ".");
134 }
Teresa Charlin83b42912022-07-07 14:24:59 +0100135 }
Narumol Prangnawarat46e574e2023-05-05 16:39:05 +0100136#else
137 LogAndThrow("Not built with Arm NN Tensorflow-Lite delegate support.");
138#endif
Teresa Charlin83b42912022-07-07 14:24:59 +0100139 }
140 else
141 {
Narumol Prangnawarate6b0e902023-06-08 12:22:57 +0100142 if (builder(&m_TfLiteInterpreter) != kTfLiteOk)
143 {
144 LogAndThrow("Error loading the model into the TfLiteInterpreter.");
145 }
Teresa Charlin83b42912022-07-07 14:24:59 +0100146 std::cout << "Running on TfLite without ArmNN delegate\n";
147 }
148
Narumol Prangnawaratfe1827d2023-05-09 16:01:12 +0100149 if (m_TfLiteInterpreter->AllocateTensors() != kTfLiteOk)
150 {
151 LogAndThrow("Failed to allocate tensors in the TfLiteInterpreter.");
152 }
153
Teresa Charlinf53b28f2022-11-11 11:14:50 +0000154 const size_t numInputs = m_TfLiteInterpreter->inputs().size();
Teresa Charlin83b42912022-07-07 14:24:59 +0100155
156 for(unsigned int inputIndex = 0; inputIndex < numInputs; ++inputIndex)
157 {
Cathal Corbettaa212302022-08-04 17:58:09 +0100158 armnn::Optional<std::string> dataFile = m_Params.m_GenerateTensorData
159 ? armnn::EmptyOptional()
160 : armnn::MakeOptional<std::string>(m_Params.m_InputTensorDataFilePaths[inputIndex]);
161
Teresa Charlin83b42912022-07-07 14:24:59 +0100162 int input = m_TfLiteInterpreter->inputs()[inputIndex];
Cathal Corbettaa212302022-08-04 17:58:09 +0100163 const auto& inputName = m_TfLiteInterpreter->tensor(input)->name;
Teresa Charlin83b42912022-07-07 14:24:59 +0100164
Colm Donelan3811a972023-01-25 21:19:49 +0000165 // Before we start, check if the tensor is constant.
166 if (!tflite::IsConstantTensor(m_TfLiteInterpreter->tensor(input)))
Teresa Charlin83b42912022-07-07 14:24:59 +0100167 {
Colm Donelan3811a972023-01-25 21:19:49 +0000168 TfLiteIntArray* inputDims = m_TfLiteInterpreter->tensor(input)->dims;
169
170 unsigned int inputSize = 1;
171 for (unsigned int dim = 0; dim < static_cast<unsigned int>(inputDims->size); ++dim)
Teresa Charlin83b42912022-07-07 14:24:59 +0100172 {
Colm Donelan3811a972023-01-25 21:19:49 +0000173 inputSize *= inputDims->data[dim];
Teresa Charlin83b42912022-07-07 14:24:59 +0100174 }
Colm Donelan3811a972023-01-25 21:19:49 +0000175
176 const auto& dataType = m_TfLiteInterpreter->tensor(input)->type;
177
178 switch (dataType)
Teresa Charlin83b42912022-07-07 14:24:59 +0100179 {
Colm Donelan3811a972023-01-25 21:19:49 +0000180 case kTfLiteFloat32:
181 {
182 auto inputData = m_TfLiteInterpreter->typed_tensor<float>(input);
183 PopulateTensorWithData<float>(inputData, inputSize, dataFile, inputName);
184 break;
185 }
186 case kTfLiteInt32:
187 {
188 auto inputData = m_TfLiteInterpreter->typed_tensor<int32_t>(input);
189 PopulateTensorWithData<int32_t>(inputData, inputSize, dataFile, inputName);
190 break;
191 }
192 case kTfLiteUInt8:
193 {
194 auto inputData = m_TfLiteInterpreter->typed_tensor<uint8_t>(input);
195 PopulateTensorWithData<uint8_t>(inputData, inputSize, dataFile, inputName);
196 break;
197 }
198 case kTfLiteInt16:
199 {
200 auto inputData = m_TfLiteInterpreter->typed_tensor<int16_t>(input);
201 PopulateTensorWithData<int16_t>(inputData, inputSize, dataFile, inputName);
202 break;
203 }
204 case kTfLiteInt8:
205 {
206 auto inputData = m_TfLiteInterpreter->typed_tensor<int8_t>(input);
207 PopulateTensorWithData<int8_t>(inputData, inputSize, dataFile, inputName);
208 break;
209 }
210 default:
211 {
212 LogAndThrow("Unsupported input tensor data type");
213 }
Teresa Charlin83b42912022-07-07 14:24:59 +0100214 }
Colm Donelan3811a972023-01-25 21:19:49 +0000215 }
216 else
217 {
218 ARMNN_LOG(info) << "Input tensor \"" << inputName << "\" is constant and will not be populated with data.";
Teresa Charlin83b42912022-07-07 14:24:59 +0100219 }
220 }
221}
222
223std::vector<const void *> TfLiteExecutor::Execute()
224{
Colm Donelandce33822024-02-09 17:29:00 +0000225 TfLiteStatus status;
Teresa Charlin83b42912022-07-07 14:24:59 +0100226 std::vector<const void*> results;
227 for (size_t x = 0; x < m_Params.m_Iterations; x++)
228 {
229 // Start timer to record inference time in milliseconds.
230 const auto start_time = armnn::GetTimeNow();
231 // Run the inference
232 status = m_TfLiteInterpreter->Invoke();
Colm Donelandce33822024-02-09 17:29:00 +0000233 if (status != kTfLiteOk)
234 {
235 LogAndThrow("Failed to execute the inference on the TfLite runtime.. The result was: " +
236 TfLiteStatusToString(status) + ".");
237 }
Teresa Charlin83b42912022-07-07 14:24:59 +0100238 const auto duration = armnn::GetTimeDuration(start_time);
239
Kevin May2fef6f62022-11-14 17:07:49 +0000240 if (!m_Params.m_DontPrintOutputs)
Teresa Charlin83b42912022-07-07 14:24:59 +0100241 {
Kevin May2fef6f62022-11-14 17:07:49 +0000242 // Print out the output
243 for (unsigned int outputIndex = 0; outputIndex < m_TfLiteInterpreter->outputs().size(); ++outputIndex)
Teresa Charlin83b42912022-07-07 14:24:59 +0100244 {
Kevin May2fef6f62022-11-14 17:07:49 +0000245 auto tfLiteDelegateOutputId = m_TfLiteInterpreter->outputs()[outputIndex];
246 TfLiteIntArray* outputDims = m_TfLiteInterpreter->tensor(tfLiteDelegateOutputId)->dims;
247 // If we've been asked to write to a file then set a file output stream. Otherwise use stdout.
248 FILE* outputTensorFile = stdout;
249 if (!m_Params.m_OutputTensorFiles.empty())
Teresa Charlin83b42912022-07-07 14:24:59 +0100250 {
Kevin May2fef6f62022-11-14 17:07:49 +0000251 outputTensorFile = fopen(m_Params.m_OutputTensorFiles[outputIndex].c_str(), "w");
252 if (outputTensorFile == NULL)
253 {
254 LogAndThrow("Specified output tensor file, \"" + m_Params.m_OutputTensorFiles[outputIndex] +
255 "\", cannot be created. Defaulting to stdout. Error was: " + std::strerror(errno));
256 }
257 else
258 {
259 ARMNN_LOG(info) << "Writing output " << outputIndex << "' of iteration: " << x + 1
260 << " to file: '" << m_Params.m_OutputTensorFiles[outputIndex] << "'";
261 }
Teresa Charlin83b42912022-07-07 14:24:59 +0100262 }
Kevin May2fef6f62022-11-14 17:07:49 +0000263 long outputSize = 1;
264 for (unsigned int dim = 0; dim < static_cast<unsigned int>(outputDims->size); ++dim)
Teresa Charlin83b42912022-07-07 14:24:59 +0100265 {
Kevin May2fef6f62022-11-14 17:07:49 +0000266 outputSize *= outputDims->data[dim];
Teresa Charlin83b42912022-07-07 14:24:59 +0100267 }
Kevin May2fef6f62022-11-14 17:07:49 +0000268
269 std::cout << m_TfLiteInterpreter->tensor(tfLiteDelegateOutputId)->name << ": ";
Kevin May2fef6f62022-11-14 17:07:49 +0000270 switch (m_TfLiteInterpreter->tensor(tfLiteDelegateOutputId)->type)
271 {
272
273 case kTfLiteFloat32:
274 {
275 auto tfLiteDelegateOutputData = m_TfLiteInterpreter->typed_tensor<float>(
276 tfLiteDelegateOutputId);
Colm Donelandce33822024-02-09 17:29:00 +0000277 results.push_back(tfLiteDelegateOutputData);
Kevin May2fef6f62022-11-14 17:07:49 +0000278
279 for (int i = 0; i < outputSize; ++i)
280 {
281 fprintf(outputTensorFile, "%f ", tfLiteDelegateOutputData[i]);
282 }
283 break;
284 }
285 case kTfLiteInt32:
286 {
287 auto tfLiteDelegateOutputData = m_TfLiteInterpreter->typed_tensor<int32_t>(
288 tfLiteDelegateOutputId);
Colm Donelandce33822024-02-09 17:29:00 +0000289 results.push_back(tfLiteDelegateOutputData);
Kevin May2fef6f62022-11-14 17:07:49 +0000290 for (int i = 0; i < outputSize; ++i)
291 {
292 fprintf(outputTensorFile, "%d ", tfLiteDelegateOutputData[i]);
293 }
294 break;
295 }
296 case kTfLiteUInt8:
297 {
298 auto tfLiteDelegateOutputData = m_TfLiteInterpreter->typed_tensor<uint8_t>(
299 tfLiteDelegateOutputId);
Colm Donelandce33822024-02-09 17:29:00 +0000300 results.push_back(tfLiteDelegateOutputData);
Kevin May2fef6f62022-11-14 17:07:49 +0000301 for (int i = 0; i < outputSize; ++i)
302 {
303 fprintf(outputTensorFile, "%u ", tfLiteDelegateOutputData[i]);
304 }
305 break;
306 }
307 case kTfLiteInt8:
308 {
309 auto tfLiteDelegateOutputData = m_TfLiteInterpreter->typed_tensor<int8_t>(
310 tfLiteDelegateOutputId);
Colm Donelandce33822024-02-09 17:29:00 +0000311 results.push_back(tfLiteDelegateOutputData);
Kevin May2fef6f62022-11-14 17:07:49 +0000312 for (int i = 0; i < outputSize; ++i)
313 {
314 fprintf(outputTensorFile, "%d ", tfLiteDelegateOutputData[i]);
315 }
316 break;
317 }
Ryan OShea39831da2023-01-26 17:43:45 +0000318 case kTfLiteBool:
319 {
320 auto tfLiteDelegateOutputData = m_TfLiteInterpreter->typed_tensor<bool>(
321 tfLiteDelegateOutputId);
Colm Donelandce33822024-02-09 17:29:00 +0000322 results.push_back(tfLiteDelegateOutputData);
Ryan OShea39831da2023-01-26 17:43:45 +0000323 for (int i = 0; i < outputSize; ++i) {
324 fprintf(outputTensorFile, "%u ", tfLiteDelegateOutputData[i]);
325 }
326 break;
327 }
Kevin May2fef6f62022-11-14 17:07:49 +0000328 default:
329 {
330 LogAndThrow("Unsupported output type");
331 }
332 }
Kevin May2fef6f62022-11-14 17:07:49 +0000333 std::cout << std::endl;
Teresa Charlin83b42912022-07-07 14:24:59 +0100334 }
Teresa Charlin83b42912022-07-07 14:24:59 +0100335 }
336 CheckInferenceTimeThreshold(duration, m_Params.m_ThresholdTime);
337 }
338
Teresa Charlin83b42912022-07-07 14:24:59 +0100339 return results;
340}
341
342void TfLiteExecutor::CompareAndPrintResult(std::vector<const void*> otherOutput)
343{
344 for (unsigned int outputIndex = 0; outputIndex < m_TfLiteInterpreter->outputs().size(); ++outputIndex)
345 {
346 auto tfLiteDelegateOutputId = m_TfLiteInterpreter->outputs()[outputIndex];
Colm Donelandce33822024-02-09 17:29:00 +0000347 size_t size = m_TfLiteInterpreter->tensor(tfLiteDelegateOutputId)->bytes;
348 double result = 1; // Presume failure.
349 switch (m_TfLiteInterpreter->tensor(tfLiteDelegateOutputId)->type)
350 {
351 case kTfLiteFloat32:
352 {
353 auto tfLiteDelegateOutputData = m_TfLiteInterpreter->typed_tensor<float>(tfLiteDelegateOutputId);
354 result = ComputeByteLevelRMSE(tfLiteDelegateOutputData, otherOutput[outputIndex], size);
355 break;
356 }
357 case kTfLiteInt32:
358 {
359 auto tfLiteDelegateOutputData = m_TfLiteInterpreter->typed_tensor<int32_t>(tfLiteDelegateOutputId);
360 result = ComputeByteLevelRMSE(tfLiteDelegateOutputData, otherOutput[outputIndex], size);
361 break;
362 }
363 case kTfLiteUInt8:
364 {
365 auto tfLiteDelegateOutputData = m_TfLiteInterpreter->typed_tensor<uint8_t>(tfLiteDelegateOutputId);
366 result = ComputeByteLevelRMSE(tfLiteDelegateOutputData, otherOutput[outputIndex], size);
367 break;
368 }
369 case kTfLiteInt8:
370 {
371 auto tfLiteDelegateOutputData = m_TfLiteInterpreter->typed_tensor<int8_t>(tfLiteDelegateOutputId);
372 result = ComputeByteLevelRMSE(tfLiteDelegateOutputData, otherOutput[outputIndex], size);
373 break;
374 }
375 case kTfLiteBool:
376 {
377 auto tfLiteDelegateOutputData = m_TfLiteInterpreter->typed_tensor<bool>(tfLiteDelegateOutputId);
378 result = ComputeByteLevelRMSE(tfLiteDelegateOutputData, otherOutput[outputIndex], size);
379 break;
380 }
381 default:
382 {
383 LogAndThrow("Unsupported output type");
384 }
385 }
Colm Doneland0472622023-03-06 12:34:54 +0000386 std::cout << "Byte level root mean square error: " << result << "\n";
Teresa Charlin83b42912022-07-07 14:24:59 +0100387 }
388};