blob: 4d645520280072776cd0e6fad6289ed8296537cc [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)
Narumol Prangnawaratfe1827d2023-05-09 16:01:12 +010074 if (builder(&m_TfLiteInterpreter) != kTfLiteOk)
75 {
76 LogAndThrow("Error loading the model into the TfLiteInterpreter.");
77 }
Teresa Charlin16ea11e2023-10-26 13:31:38 +010078 // Populate a DelegateOptions from the ExecuteNetworkParams.
79 armnnDelegate::DelegateOptions delegateOptions = m_Params.ToDelegateOptions();
80 delegateOptions.SetRuntimeOptions(runtimeOptions);
81 std::unique_ptr<TfLiteDelegate, decltype(&armnnOpaqueDelegate::TfLiteArmnnOpaqueDelegateDelete)>
82 theArmnnDelegate(armnnOpaqueDelegate::TfLiteArmnnOpaqueDelegateCreate(delegateOptions),
83 armnnOpaqueDelegate::TfLiteArmnnOpaqueDelegateDelete);
Narumol Prangnawaratfe1827d2023-05-09 16:01:12 +010084
Teresa Charlin16ea11e2023-10-26 13:31:38 +010085 // Register armnn_delegate to TfLiteInterpreter
86 auto result = m_TfLiteInterpreter->ModifyGraphWithDelegate(std::move(theArmnnDelegate));
87 if (result != kTfLiteOk)
88 {
89 LogAndThrow("Could not register ArmNN TfLite Opaque Delegate to TfLiteInterpreter: " +
90 TfLiteStatusToString(result) + ".");
91 }
Narumol Prangnawarat46e574e2023-05-05 16:39:05 +010092#else
93 LogAndThrow("Not built with Arm NN Tensorflow-Lite opaque delegate support.");
94#endif
95 }
96 else if (m_Params.m_TfLiteExecutor == ExecuteNetworkParams::TfLiteExecutor::ArmNNTfLiteDelegate)
97 {
98#if defined(ARMNN_TFLITE_DELEGATE)
Narumol Prangnawaratfe1827d2023-05-09 16:01:12 +010099 if (builder(&m_TfLiteInterpreter) != kTfLiteOk)
100 {
101 LogAndThrow("Error loading the model into the TfLiteInterpreter.");
102 }
Teresa Charlin83b42912022-07-07 14:24:59 +0100103 // Create the Armnn Delegate
104 // Populate a DelegateOptions from the ExecuteNetworkParams.
105 armnnDelegate::DelegateOptions delegateOptions = m_Params.ToDelegateOptions();
Colm Donelan35a06892023-02-06 15:01:57 +0000106 delegateOptions.SetRuntimeOptions(runtimeOptions);
Teresa Charlin83b42912022-07-07 14:24:59 +0100107 std::unique_ptr<TfLiteDelegate, decltype(&armnnDelegate::TfLiteArmnnDelegateDelete)>
108 theArmnnDelegate(armnnDelegate::TfLiteArmnnDelegateCreate(delegateOptions),
109 armnnDelegate::TfLiteArmnnDelegateDelete);
110 // Register armnn_delegate to TfLiteInterpreter
Colm Donelan0dfb2652023-06-22 10:19:17 +0100111 auto result = m_TfLiteInterpreter->ModifyGraphWithDelegate(std::move(theArmnnDelegate));
112 if (result != kTfLiteOk)
Teresa Charlin83b42912022-07-07 14:24:59 +0100113 {
Colm Donelan0dfb2652023-06-22 10:19:17 +0100114 LogAndThrow("Could not register ArmNN TfLite Delegate to TfLiteInterpreter: " +
115 TfLiteStatusToString(result) + ".");
Teresa Charlin83b42912022-07-07 14:24:59 +0100116 }
Narumol Prangnawarat46e574e2023-05-05 16:39:05 +0100117#else
118 LogAndThrow("Not built with Arm NN Tensorflow-Lite delegate support.");
119#endif
Teresa Charlin83b42912022-07-07 14:24:59 +0100120 }
121 else
122 {
Narumol Prangnawarate6b0e902023-06-08 12:22:57 +0100123 if (builder(&m_TfLiteInterpreter) != kTfLiteOk)
124 {
125 LogAndThrow("Error loading the model into the TfLiteInterpreter.");
126 }
Teresa Charlin83b42912022-07-07 14:24:59 +0100127 std::cout << "Running on TfLite without ArmNN delegate\n";
128 }
129
Narumol Prangnawaratfe1827d2023-05-09 16:01:12 +0100130 if (m_TfLiteInterpreter->AllocateTensors() != kTfLiteOk)
131 {
132 LogAndThrow("Failed to allocate tensors in the TfLiteInterpreter.");
133 }
134
Teresa Charlinf53b28f2022-11-11 11:14:50 +0000135 const size_t numInputs = m_TfLiteInterpreter->inputs().size();
Teresa Charlin83b42912022-07-07 14:24:59 +0100136
137 for(unsigned int inputIndex = 0; inputIndex < numInputs; ++inputIndex)
138 {
Cathal Corbettaa212302022-08-04 17:58:09 +0100139 armnn::Optional<std::string> dataFile = m_Params.m_GenerateTensorData
140 ? armnn::EmptyOptional()
141 : armnn::MakeOptional<std::string>(m_Params.m_InputTensorDataFilePaths[inputIndex]);
142
Teresa Charlin83b42912022-07-07 14:24:59 +0100143 int input = m_TfLiteInterpreter->inputs()[inputIndex];
Cathal Corbettaa212302022-08-04 17:58:09 +0100144 const auto& inputName = m_TfLiteInterpreter->tensor(input)->name;
Teresa Charlin83b42912022-07-07 14:24:59 +0100145
Colm Donelan3811a972023-01-25 21:19:49 +0000146 // Before we start, check if the tensor is constant.
147 if (!tflite::IsConstantTensor(m_TfLiteInterpreter->tensor(input)))
Teresa Charlin83b42912022-07-07 14:24:59 +0100148 {
Colm Donelan3811a972023-01-25 21:19:49 +0000149 TfLiteIntArray* inputDims = m_TfLiteInterpreter->tensor(input)->dims;
150
151 unsigned int inputSize = 1;
152 for (unsigned int dim = 0; dim < static_cast<unsigned int>(inputDims->size); ++dim)
Teresa Charlin83b42912022-07-07 14:24:59 +0100153 {
Colm Donelan3811a972023-01-25 21:19:49 +0000154 inputSize *= inputDims->data[dim];
Teresa Charlin83b42912022-07-07 14:24:59 +0100155 }
Colm Donelan3811a972023-01-25 21:19:49 +0000156
157 const auto& dataType = m_TfLiteInterpreter->tensor(input)->type;
158
159 switch (dataType)
Teresa Charlin83b42912022-07-07 14:24:59 +0100160 {
Colm Donelan3811a972023-01-25 21:19:49 +0000161 case kTfLiteFloat32:
162 {
163 auto inputData = m_TfLiteInterpreter->typed_tensor<float>(input);
164 PopulateTensorWithData<float>(inputData, inputSize, dataFile, inputName);
165 break;
166 }
167 case kTfLiteInt32:
168 {
169 auto inputData = m_TfLiteInterpreter->typed_tensor<int32_t>(input);
170 PopulateTensorWithData<int32_t>(inputData, inputSize, dataFile, inputName);
171 break;
172 }
173 case kTfLiteUInt8:
174 {
175 auto inputData = m_TfLiteInterpreter->typed_tensor<uint8_t>(input);
176 PopulateTensorWithData<uint8_t>(inputData, inputSize, dataFile, inputName);
177 break;
178 }
179 case kTfLiteInt16:
180 {
181 auto inputData = m_TfLiteInterpreter->typed_tensor<int16_t>(input);
182 PopulateTensorWithData<int16_t>(inputData, inputSize, dataFile, inputName);
183 break;
184 }
185 case kTfLiteInt8:
186 {
187 auto inputData = m_TfLiteInterpreter->typed_tensor<int8_t>(input);
188 PopulateTensorWithData<int8_t>(inputData, inputSize, dataFile, inputName);
189 break;
190 }
191 default:
192 {
193 LogAndThrow("Unsupported input tensor data type");
194 }
Teresa Charlin83b42912022-07-07 14:24:59 +0100195 }
Colm Donelan3811a972023-01-25 21:19:49 +0000196 }
197 else
198 {
199 ARMNN_LOG(info) << "Input tensor \"" << inputName << "\" is constant and will not be populated with data.";
Teresa Charlin83b42912022-07-07 14:24:59 +0100200 }
201 }
202}
203
204std::vector<const void *> TfLiteExecutor::Execute()
205{
206 int status = 0;
207 std::vector<const void*> results;
208 for (size_t x = 0; x < m_Params.m_Iterations; x++)
209 {
210 // Start timer to record inference time in milliseconds.
211 const auto start_time = armnn::GetTimeNow();
212 // Run the inference
213 status = m_TfLiteInterpreter->Invoke();
214 const auto duration = armnn::GetTimeDuration(start_time);
215
Kevin May2fef6f62022-11-14 17:07:49 +0000216 if (!m_Params.m_DontPrintOutputs)
Teresa Charlin83b42912022-07-07 14:24:59 +0100217 {
Kevin May2fef6f62022-11-14 17:07:49 +0000218 // Print out the output
219 for (unsigned int outputIndex = 0; outputIndex < m_TfLiteInterpreter->outputs().size(); ++outputIndex)
Teresa Charlin83b42912022-07-07 14:24:59 +0100220 {
Kevin May2fef6f62022-11-14 17:07:49 +0000221 auto tfLiteDelegateOutputId = m_TfLiteInterpreter->outputs()[outputIndex];
222 TfLiteIntArray* outputDims = m_TfLiteInterpreter->tensor(tfLiteDelegateOutputId)->dims;
223 // If we've been asked to write to a file then set a file output stream. Otherwise use stdout.
224 FILE* outputTensorFile = stdout;
225 if (!m_Params.m_OutputTensorFiles.empty())
Teresa Charlin83b42912022-07-07 14:24:59 +0100226 {
Kevin May2fef6f62022-11-14 17:07:49 +0000227 outputTensorFile = fopen(m_Params.m_OutputTensorFiles[outputIndex].c_str(), "w");
228 if (outputTensorFile == NULL)
229 {
230 LogAndThrow("Specified output tensor file, \"" + m_Params.m_OutputTensorFiles[outputIndex] +
231 "\", cannot be created. Defaulting to stdout. Error was: " + std::strerror(errno));
232 }
233 else
234 {
235 ARMNN_LOG(info) << "Writing output " << outputIndex << "' of iteration: " << x + 1
236 << " to file: '" << m_Params.m_OutputTensorFiles[outputIndex] << "'";
237 }
Teresa Charlin83b42912022-07-07 14:24:59 +0100238 }
Kevin May2fef6f62022-11-14 17:07:49 +0000239 long outputSize = 1;
240 for (unsigned int dim = 0; dim < static_cast<unsigned int>(outputDims->size); ++dim)
Teresa Charlin83b42912022-07-07 14:24:59 +0100241 {
Kevin May2fef6f62022-11-14 17:07:49 +0000242 outputSize *= outputDims->data[dim];
Teresa Charlin83b42912022-07-07 14:24:59 +0100243 }
Kevin May2fef6f62022-11-14 17:07:49 +0000244
245 std::cout << m_TfLiteInterpreter->tensor(tfLiteDelegateOutputId)->name << ": ";
246 results.push_back(m_TfLiteInterpreter->tensor(tfLiteDelegateOutputId)->allocation);
247
248 switch (m_TfLiteInterpreter->tensor(tfLiteDelegateOutputId)->type)
249 {
250
251 case kTfLiteFloat32:
252 {
253 auto tfLiteDelegateOutputData = m_TfLiteInterpreter->typed_tensor<float>(
254 tfLiteDelegateOutputId);
255
256 for (int i = 0; i < outputSize; ++i)
257 {
258 fprintf(outputTensorFile, "%f ", tfLiteDelegateOutputData[i]);
259 }
260 break;
261 }
262 case kTfLiteInt32:
263 {
264 auto tfLiteDelegateOutputData = m_TfLiteInterpreter->typed_tensor<int32_t>(
265 tfLiteDelegateOutputId);
266 for (int i = 0; i < outputSize; ++i)
267 {
268 fprintf(outputTensorFile, "%d ", tfLiteDelegateOutputData[i]);
269 }
270 break;
271 }
272 case kTfLiteUInt8:
273 {
274 auto tfLiteDelegateOutputData = m_TfLiteInterpreter->typed_tensor<uint8_t>(
275 tfLiteDelegateOutputId);
276 for (int i = 0; i < outputSize; ++i)
277 {
278 fprintf(outputTensorFile, "%u ", tfLiteDelegateOutputData[i]);
279 }
280 break;
281 }
282 case kTfLiteInt8:
283 {
284 auto tfLiteDelegateOutputData = m_TfLiteInterpreter->typed_tensor<int8_t>(
285 tfLiteDelegateOutputId);
286 for (int i = 0; i < outputSize; ++i)
287 {
288 fprintf(outputTensorFile, "%d ", tfLiteDelegateOutputData[i]);
289 }
290 break;
291 }
Ryan OShea39831da2023-01-26 17:43:45 +0000292 case kTfLiteBool:
293 {
294 auto tfLiteDelegateOutputData = m_TfLiteInterpreter->typed_tensor<bool>(
295 tfLiteDelegateOutputId);
296 for (int i = 0; i < outputSize; ++i) {
297 fprintf(outputTensorFile, "%u ", tfLiteDelegateOutputData[i]);
298 }
299 break;
300 }
Kevin May2fef6f62022-11-14 17:07:49 +0000301 default:
302 {
303 LogAndThrow("Unsupported output type");
304 }
305 }
306
307 std::cout << std::endl;
Teresa Charlin83b42912022-07-07 14:24:59 +0100308 }
Teresa Charlin83b42912022-07-07 14:24:59 +0100309 }
310 CheckInferenceTimeThreshold(duration, m_Params.m_ThresholdTime);
311 }
312
313 std::cout << status;
314 return results;
315}
316
317void TfLiteExecutor::CompareAndPrintResult(std::vector<const void*> otherOutput)
318{
319 for (unsigned int outputIndex = 0; outputIndex < m_TfLiteInterpreter->outputs().size(); ++outputIndex)
320 {
321 auto tfLiteDelegateOutputId = m_TfLiteInterpreter->outputs()[outputIndex];
Colm Doneland0472622023-03-06 12:34:54 +0000322 size_t size = m_TfLiteInterpreter->tensor(tfLiteDelegateOutputId)->bytes;
323 double result = ComputeByteLevelRMSE(m_TfLiteInterpreter->tensor(tfLiteDelegateOutputId)->allocation,
324 otherOutput[outputIndex], size);
325 std::cout << "Byte level root mean square error: " << result << "\n";
Teresa Charlin83b42912022-07-07 14:24:59 +0100326 }
327};