blob: 04e510f938241354d4ed24f877ebdbe32279bfdc [file] [log] [blame]
Teresa Charlin83b42912022-07-07 14:24:59 +01001//
Mike Kelly5446a4d2023-01-20 15:51:05 +00002// Copyright © 2022-2023 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 Donelan0dfb2652023-06-22 10:19:17 +010014#include <string>
15
16std::string TfLiteStatusToString(const TfLiteStatus status)
17{
18 switch (status)
19 {
20 case kTfLiteOk:
21 return "Status: Ok.";
22 // Generally referring to an error in the runtime (i.e. interpreter)
23 case kTfLiteError:
24 return "Status: Tf runtime error.";
25 // Generally referring to an error from a TfLiteDelegate itself.
26 case kTfLiteDelegateError:
27 return "Status: The loaded delegate has returned an error.";
28 // Generally referring to an error in applying a delegate due to
29 // incompatibility between runtime and delegate, e.g., this error is returned
30 // when trying to apply a TF Lite delegate onto a model graph that's already
31 // immutable.
32 case kTfLiteApplicationError:
33 return "Status: Application error. An incompatibility between the Tf runtime and the loaded delegate.";
34 // Generally referring to serialized delegate data not being found.
35 // See tflite::delegates::Serialization.
36 case kTfLiteDelegateDataNotFound:
37 return "Status: data not found.";
38 // Generally referring to data-writing issues in delegate serialization.
39 // See tflite::delegates::Serialization.
40 case kTfLiteDelegateDataWriteError:
41 return "Status: Error writing serialization data.";
42 // Generally referring to data-reading issues in delegate serialization.
43 // See tflite::delegates::Serialization.
44 case kTfLiteDelegateDataReadError:
45 return "Status: Error reading serialization data.";
46 // Generally referring to issues when the TF Lite model has ops that cannot be
47 // resolved at runtime. This could happen when the specific op is not
48 // registered or built with the TF Lite framework.
49 case kTfLiteUnresolvedOps:
50 return "Status: Model contains an operation that is not recognised by the runtime.";
51 // Generally referring to invocation cancelled by the user.
52 case kTfLiteCancelled:
53 return "Status: invocation has been cancelled by the user.";
54 }
55 return "Unknown status result.";
56}
57
Colm Donelan35a06892023-02-06 15:01:57 +000058TfLiteExecutor::TfLiteExecutor(const ExecuteNetworkParams& params, armnn::IRuntime::CreationOptions runtimeOptions)
59 : m_Params(params)
Teresa Charlin83b42912022-07-07 14:24:59 +010060{
Teresa Charlinc814e802022-08-05 13:57:04 +010061 m_Model = tflite::FlatBufferModel::BuildFromFile(m_Params.m_ModelPath.c_str());
Colm Donelan18e6f042023-01-24 22:10:12 +000062 if (!m_Model)
63 {
64 LogAndThrow("Failed to load TfLite model from: " + m_Params.m_ModelPath);
65 }
Teresa Charlin83b42912022-07-07 14:24:59 +010066 m_TfLiteInterpreter = std::make_unique<Interpreter>();
67 tflite::ops::builtin::BuiltinOpResolver resolver;
68
Teresa Charlinc814e802022-08-05 13:57:04 +010069 tflite::InterpreterBuilder builder(*m_Model, resolver);
Narumol Prangnawarat46e574e2023-05-05 16:39:05 +010070
71 if (m_Params.m_TfLiteExecutor == ExecuteNetworkParams::TfLiteExecutor::ArmNNTfLiteOpaqueDelegate)
Teresa Charlin83b42912022-07-07 14:24:59 +010072 {
Narumol Prangnawarat46e574e2023-05-05 16:39:05 +010073#if defined(ARMNN_TFLITE_OPAQUE_DELEGATE)
74 // Use default settings until options have been enabled
75 flatbuffers::FlatBufferBuilder flatBufferBuilder;
76 TFLiteSettingsBuilder tfliteSettingsBuilder(flatBufferBuilder);
77 flatbuffers::Offset<TFLiteSettings> tfliteSettings = tfliteSettingsBuilder.Finish();
78 flatBufferBuilder.Finish(tfliteSettings);
79 const TFLiteSettings* settings =
80 flatbuffers::GetRoot<TFLiteSettings>(flatBufferBuilder.GetBufferPointer());
81
82 std::unique_ptr<delegates::DelegatePluginInterface> delegatePlugIn =
83 delegates::DelegatePluginRegistry::CreateByName("armnn_delegate", *settings);
84
85 // Create Armnn Opaque Delegate from Armnn Delegate Plugin
86 delegates::TfLiteDelegatePtr armnnDelegate = delegatePlugIn->Create();
87
88 // Add Delegate to the builder
89 builder.AddDelegate(armnnDelegate.get());
Narumol Prangnawaratfe1827d2023-05-09 16:01:12 +010090 if (builder(&m_TfLiteInterpreter) != kTfLiteOk)
91 {
92 LogAndThrow("Error loading the model into the TfLiteInterpreter.");
93 }
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 Donelan0dfb2652023-06-22 10:19:17 +0100117 LogAndThrow("Could not register ArmNN TfLite Delegate to TfLiteInterpreter: " +
118 TfLiteStatusToString(result) + ".");
Teresa Charlin83b42912022-07-07 14:24:59 +0100119 }
Narumol Prangnawarat46e574e2023-05-05 16:39:05 +0100120#else
121 LogAndThrow("Not built with Arm NN Tensorflow-Lite delegate support.");
122#endif
Teresa Charlin83b42912022-07-07 14:24:59 +0100123 }
124 else
125 {
Narumol Prangnawarate6b0e902023-06-08 12:22:57 +0100126 if (builder(&m_TfLiteInterpreter) != kTfLiteOk)
127 {
128 LogAndThrow("Error loading the model into the TfLiteInterpreter.");
129 }
Teresa Charlin83b42912022-07-07 14:24:59 +0100130 std::cout << "Running on TfLite without ArmNN delegate\n";
131 }
132
Narumol Prangnawaratfe1827d2023-05-09 16:01:12 +0100133 if (m_TfLiteInterpreter->AllocateTensors() != kTfLiteOk)
134 {
135 LogAndThrow("Failed to allocate tensors in the TfLiteInterpreter.");
136 }
137
Teresa Charlinf53b28f2022-11-11 11:14:50 +0000138 const size_t numInputs = m_TfLiteInterpreter->inputs().size();
Teresa Charlin83b42912022-07-07 14:24:59 +0100139
140 for(unsigned int inputIndex = 0; inputIndex < numInputs; ++inputIndex)
141 {
Cathal Corbettaa212302022-08-04 17:58:09 +0100142 armnn::Optional<std::string> dataFile = m_Params.m_GenerateTensorData
143 ? armnn::EmptyOptional()
144 : armnn::MakeOptional<std::string>(m_Params.m_InputTensorDataFilePaths[inputIndex]);
145
Teresa Charlin83b42912022-07-07 14:24:59 +0100146 int input = m_TfLiteInterpreter->inputs()[inputIndex];
Cathal Corbettaa212302022-08-04 17:58:09 +0100147 const auto& inputName = m_TfLiteInterpreter->tensor(input)->name;
Teresa Charlin83b42912022-07-07 14:24:59 +0100148
Colm Donelan3811a972023-01-25 21:19:49 +0000149 // Before we start, check if the tensor is constant.
150 if (!tflite::IsConstantTensor(m_TfLiteInterpreter->tensor(input)))
Teresa Charlin83b42912022-07-07 14:24:59 +0100151 {
Colm Donelan3811a972023-01-25 21:19:49 +0000152 TfLiteIntArray* inputDims = m_TfLiteInterpreter->tensor(input)->dims;
153
154 unsigned int inputSize = 1;
155 for (unsigned int dim = 0; dim < static_cast<unsigned int>(inputDims->size); ++dim)
Teresa Charlin83b42912022-07-07 14:24:59 +0100156 {
Colm Donelan3811a972023-01-25 21:19:49 +0000157 inputSize *= inputDims->data[dim];
Teresa Charlin83b42912022-07-07 14:24:59 +0100158 }
Colm Donelan3811a972023-01-25 21:19:49 +0000159
160 const auto& dataType = m_TfLiteInterpreter->tensor(input)->type;
161
162 switch (dataType)
Teresa Charlin83b42912022-07-07 14:24:59 +0100163 {
Colm Donelan3811a972023-01-25 21:19:49 +0000164 case kTfLiteFloat32:
165 {
166 auto inputData = m_TfLiteInterpreter->typed_tensor<float>(input);
167 PopulateTensorWithData<float>(inputData, inputSize, dataFile, inputName);
168 break;
169 }
170 case kTfLiteInt32:
171 {
172 auto inputData = m_TfLiteInterpreter->typed_tensor<int32_t>(input);
173 PopulateTensorWithData<int32_t>(inputData, inputSize, dataFile, inputName);
174 break;
175 }
176 case kTfLiteUInt8:
177 {
178 auto inputData = m_TfLiteInterpreter->typed_tensor<uint8_t>(input);
179 PopulateTensorWithData<uint8_t>(inputData, inputSize, dataFile, inputName);
180 break;
181 }
182 case kTfLiteInt16:
183 {
184 auto inputData = m_TfLiteInterpreter->typed_tensor<int16_t>(input);
185 PopulateTensorWithData<int16_t>(inputData, inputSize, dataFile, inputName);
186 break;
187 }
188 case kTfLiteInt8:
189 {
190 auto inputData = m_TfLiteInterpreter->typed_tensor<int8_t>(input);
191 PopulateTensorWithData<int8_t>(inputData, inputSize, dataFile, inputName);
192 break;
193 }
194 default:
195 {
196 LogAndThrow("Unsupported input tensor data type");
197 }
Teresa Charlin83b42912022-07-07 14:24:59 +0100198 }
Colm Donelan3811a972023-01-25 21:19:49 +0000199 }
200 else
201 {
202 ARMNN_LOG(info) << "Input tensor \"" << inputName << "\" is constant and will not be populated with data.";
Teresa Charlin83b42912022-07-07 14:24:59 +0100203 }
204 }
205}
206
207std::vector<const void *> TfLiteExecutor::Execute()
208{
209 int status = 0;
210 std::vector<const void*> results;
211 for (size_t x = 0; x < m_Params.m_Iterations; x++)
212 {
213 // Start timer to record inference time in milliseconds.
214 const auto start_time = armnn::GetTimeNow();
215 // Run the inference
216 status = m_TfLiteInterpreter->Invoke();
217 const auto duration = armnn::GetTimeDuration(start_time);
218
Kevin May2fef6f62022-11-14 17:07:49 +0000219 if (!m_Params.m_DontPrintOutputs)
Teresa Charlin83b42912022-07-07 14:24:59 +0100220 {
Kevin May2fef6f62022-11-14 17:07:49 +0000221 // Print out the output
222 for (unsigned int outputIndex = 0; outputIndex < m_TfLiteInterpreter->outputs().size(); ++outputIndex)
Teresa Charlin83b42912022-07-07 14:24:59 +0100223 {
Kevin May2fef6f62022-11-14 17:07:49 +0000224 auto tfLiteDelegateOutputId = m_TfLiteInterpreter->outputs()[outputIndex];
225 TfLiteIntArray* outputDims = m_TfLiteInterpreter->tensor(tfLiteDelegateOutputId)->dims;
226 // If we've been asked to write to a file then set a file output stream. Otherwise use stdout.
227 FILE* outputTensorFile = stdout;
228 if (!m_Params.m_OutputTensorFiles.empty())
Teresa Charlin83b42912022-07-07 14:24:59 +0100229 {
Kevin May2fef6f62022-11-14 17:07:49 +0000230 outputTensorFile = fopen(m_Params.m_OutputTensorFiles[outputIndex].c_str(), "w");
231 if (outputTensorFile == NULL)
232 {
233 LogAndThrow("Specified output tensor file, \"" + m_Params.m_OutputTensorFiles[outputIndex] +
234 "\", cannot be created. Defaulting to stdout. Error was: " + std::strerror(errno));
235 }
236 else
237 {
238 ARMNN_LOG(info) << "Writing output " << outputIndex << "' of iteration: " << x + 1
239 << " to file: '" << m_Params.m_OutputTensorFiles[outputIndex] << "'";
240 }
Teresa Charlin83b42912022-07-07 14:24:59 +0100241 }
Kevin May2fef6f62022-11-14 17:07:49 +0000242 long outputSize = 1;
243 for (unsigned int dim = 0; dim < static_cast<unsigned int>(outputDims->size); ++dim)
Teresa Charlin83b42912022-07-07 14:24:59 +0100244 {
Kevin May2fef6f62022-11-14 17:07:49 +0000245 outputSize *= outputDims->data[dim];
Teresa Charlin83b42912022-07-07 14:24:59 +0100246 }
Kevin May2fef6f62022-11-14 17:07:49 +0000247
248 std::cout << m_TfLiteInterpreter->tensor(tfLiteDelegateOutputId)->name << ": ";
249 results.push_back(m_TfLiteInterpreter->tensor(tfLiteDelegateOutputId)->allocation);
250
251 switch (m_TfLiteInterpreter->tensor(tfLiteDelegateOutputId)->type)
252 {
253
254 case kTfLiteFloat32:
255 {
256 auto tfLiteDelegateOutputData = m_TfLiteInterpreter->typed_tensor<float>(
257 tfLiteDelegateOutputId);
258
259 for (int i = 0; i < outputSize; ++i)
260 {
261 fprintf(outputTensorFile, "%f ", tfLiteDelegateOutputData[i]);
262 }
263 break;
264 }
265 case kTfLiteInt32:
266 {
267 auto tfLiteDelegateOutputData = m_TfLiteInterpreter->typed_tensor<int32_t>(
268 tfLiteDelegateOutputId);
269 for (int i = 0; i < outputSize; ++i)
270 {
271 fprintf(outputTensorFile, "%d ", tfLiteDelegateOutputData[i]);
272 }
273 break;
274 }
275 case kTfLiteUInt8:
276 {
277 auto tfLiteDelegateOutputData = m_TfLiteInterpreter->typed_tensor<uint8_t>(
278 tfLiteDelegateOutputId);
279 for (int i = 0; i < outputSize; ++i)
280 {
281 fprintf(outputTensorFile, "%u ", tfLiteDelegateOutputData[i]);
282 }
283 break;
284 }
285 case kTfLiteInt8:
286 {
287 auto tfLiteDelegateOutputData = m_TfLiteInterpreter->typed_tensor<int8_t>(
288 tfLiteDelegateOutputId);
289 for (int i = 0; i < outputSize; ++i)
290 {
291 fprintf(outputTensorFile, "%d ", tfLiteDelegateOutputData[i]);
292 }
293 break;
294 }
Ryan OShea39831da2023-01-26 17:43:45 +0000295 case kTfLiteBool:
296 {
297 auto tfLiteDelegateOutputData = m_TfLiteInterpreter->typed_tensor<bool>(
298 tfLiteDelegateOutputId);
299 for (int i = 0; i < outputSize; ++i) {
300 fprintf(outputTensorFile, "%u ", tfLiteDelegateOutputData[i]);
301 }
302 break;
303 }
Kevin May2fef6f62022-11-14 17:07:49 +0000304 default:
305 {
306 LogAndThrow("Unsupported output type");
307 }
308 }
309
310 std::cout << std::endl;
Teresa Charlin83b42912022-07-07 14:24:59 +0100311 }
Teresa Charlin83b42912022-07-07 14:24:59 +0100312 }
313 CheckInferenceTimeThreshold(duration, m_Params.m_ThresholdTime);
314 }
315
316 std::cout << status;
317 return results;
318}
319
320void TfLiteExecutor::CompareAndPrintResult(std::vector<const void*> otherOutput)
321{
322 for (unsigned int outputIndex = 0; outputIndex < m_TfLiteInterpreter->outputs().size(); ++outputIndex)
323 {
324 auto tfLiteDelegateOutputId = m_TfLiteInterpreter->outputs()[outputIndex];
Colm Doneland0472622023-03-06 12:34:54 +0000325 size_t size = m_TfLiteInterpreter->tensor(tfLiteDelegateOutputId)->bytes;
326 double result = ComputeByteLevelRMSE(m_TfLiteInterpreter->tensor(tfLiteDelegateOutputId)->allocation,
327 otherOutput[outputIndex], size);
328 std::cout << "Byte level root mean square error: " << result << "\n";
Teresa Charlin83b42912022-07-07 14:24:59 +0100329 }
330};