ArmNN
 21.02
ExecuteNetwork.cpp
Go to the documentation of this file.
1 //
2 // Copyright © 2017 Arm Ltd and Contributors. All rights reserved.
3 // SPDX-License-Identifier: MIT
4 //
5 
8 
9 #include <armnn/Logging.hpp>
10 #include <Filesystem.hpp>
11 #include <InferenceTest.hpp>
12 
13 #if defined(ARMNN_SERIALIZER)
15 #endif
16 #if defined(ARMNN_CAFFE_PARSER)
18 #endif
19 #if defined(ARMNN_TF_PARSER)
21 #endif
22 #if defined(ARMNN_TF_LITE_PARSER)
24 #endif
25 #if defined(ARMNN_ONNX_PARSER)
27 #endif
28 #if defined(ARMNN_TFLITE_DELEGATE)
29 #include <armnn_delegate.hpp>
30 #include <DelegateOptions.hpp>
31 
32 #include <tensorflow/lite/builtin_ops.h>
33 #include <tensorflow/lite/c/builtin_op_data.h>
34 #include <tensorflow/lite/c/common.h>
35 #include <tensorflow/lite/optional_debug_tools.h>
36 #include <tensorflow/lite/kernels/builtin_op_kernels.h>
37 #include <tensorflow/lite/interpreter.h>
38 #include <tensorflow/lite/kernels/register.h>
39 #endif
40 
41 #include <future>
42 #if defined(ARMNN_TFLITE_DELEGATE)
43 int TfLiteDelegateMainImpl(const ExecuteNetworkParams& params,
44  const std::shared_ptr<armnn::IRuntime>& runtime = nullptr)
45 {
46  using namespace tflite;
47 
48  std::unique_ptr<tflite::FlatBufferModel> model = tflite::FlatBufferModel::BuildFromFile(params.m_ModelPath.c_str());
49 
50  auto tfLiteInterpreter = std::make_unique<Interpreter>();
51  tflite::ops::builtin::BuiltinOpResolver resolver;
52 
53  tflite::InterpreterBuilder builder(*model, resolver);
54  builder(&tfLiteInterpreter);
55  tfLiteInterpreter->AllocateTensors();
56 
57  // Create the Armnn Delegate
58  armnnDelegate::DelegateOptions delegateOptions(params.m_ComputeDevices);
59  std::unique_ptr<TfLiteDelegate, decltype(&armnnDelegate::TfLiteArmnnDelegateDelete)>
60  theArmnnDelegate(armnnDelegate::TfLiteArmnnDelegateCreate(delegateOptions),
62  // Register armnn_delegate to TfLiteInterpreter
63  int status = tfLiteInterpreter->ModifyGraphWithDelegate(std::move(theArmnnDelegate));
64  if (status == kTfLiteError)
65  {
66  ARMNN_LOG(fatal) << "Could not register ArmNN TfLite Delegate to TfLiteInterpreter!";
67  return EXIT_FAILURE;
68  }
69 
70  std::vector<std::string> inputBindings;
71  for (const std::string& inputName: params.m_InputNames)
72  {
73  inputBindings.push_back(inputName);
74  }
75 
78  : armnn::MakeOptional<std::string>(params.m_InputTensorDataFilePaths[0]);
79 
80  const size_t numInputs = inputBindings.size();
81 
82  for(unsigned int inputIndex = 0; inputIndex < numInputs; ++inputIndex)
83  {
84  int input = tfLiteInterpreter->inputs()[inputIndex];
85  TfLiteIntArray* inputDims = tfLiteInterpreter->tensor(input)->dims;
86 
87  long inputSize = 1;
88  for (unsigned int dim = 0; dim < static_cast<unsigned int>(inputDims->size); ++dim)
89  {
90  inputSize *= inputDims->data[dim];
91  }
92 
93  if (params.m_InputTypes[inputIndex].compare("float") == 0)
94  {
95  auto inputData = tfLiteInterpreter->typed_tensor<float>(input);
96 
97  if(inputData == NULL)
98  {
99  ARMNN_LOG(fatal) << "Input tensor is null, input type: "
100  "\"" << params.m_InputTypes[inputIndex] << "\" may be incorrect.";
101  return EXIT_FAILURE;
102  }
103 
104  std::vector<float> tensorData;
105  PopulateTensorWithDataGeneric<float>(tensorData,
106  params.m_InputTensorShapes[inputIndex]->GetNumElements(),
107  dataFile,
108  [](const std::string& s)
109  { return std::stof(s); });
110 
111  std::copy(tensorData.begin(), tensorData.end(), inputData);
112  }
113  else if (params.m_InputTypes[inputIndex].compare("int8") == 0)
114  {
115  auto inputData = tfLiteInterpreter->typed_tensor<int8_t>(input);
116 
117  if(inputData == NULL)
118  {
119  ARMNN_LOG(fatal) << "Input tensor is null, input type: "
120  "\"" << params.m_InputTypes[inputIndex] << "\" may be incorrect.";
121  return EXIT_FAILURE;
122  }
123 
124  std::vector<int8_t> tensorData;
125  PopulateTensorWithDataGeneric<int8_t>(tensorData,
126  params.m_InputTensorShapes[inputIndex]->GetNumElements(),
127  dataFile,
128  [](const std::string& s)
129  { return armnn::numeric_cast<int8_t>(std::stoi(s)); });
130 
131  std::copy(tensorData.begin(), tensorData.end(), inputData);
132  }
133  else if (params.m_InputTypes[inputIndex].compare("int") == 0)
134  {
135  auto inputData = tfLiteInterpreter->typed_tensor<int32_t>(input);
136 
137  if(inputData == NULL)
138  {
139  ARMNN_LOG(fatal) << "Input tensor is null, input type: "
140  "\"" << params.m_InputTypes[inputIndex] << "\" may be incorrect.";
141  return EXIT_FAILURE;
142  }
143 
144  std::vector<int32_t> tensorData;
145  PopulateTensorWithDataGeneric<int32_t>(tensorData,
146  params.m_InputTensorShapes[inputIndex]->GetNumElements(),
147  dataFile,
148  [](const std::string& s)
149  { return std::stoi(s); });
150 
151  std::copy(tensorData.begin(), tensorData.end(), inputData);
152  }
153  else if (params.m_InputTypes[inputIndex].compare("qasymm8") == 0)
154  {
155  auto inputData = tfLiteInterpreter->typed_tensor<uint8_t>(input);
156 
157  if(inputData == NULL)
158  {
159  ARMNN_LOG(fatal) << "Input tensor is null, input type: "
160  "\"" << params.m_InputTypes[inputIndex] << "\" may be incorrect.";
161  return EXIT_FAILURE;
162  }
163 
164  std::vector<uint8_t> tensorData;
165  PopulateTensorWithDataGeneric<uint8_t>(tensorData,
166  params.m_InputTensorShapes[inputIndex]->GetNumElements(),
167  dataFile,
168  [](const std::string& s)
169  { return armnn::numeric_cast<uint8_t>(std::stoi(s)); });
170 
171  std::copy(tensorData.begin(), tensorData.end(), inputData);
172  }
173  else
174  {
175  ARMNN_LOG(fatal) << "Unsupported input tensor data type \"" << params.m_InputTypes[inputIndex] << "\". ";
176  return EXIT_FAILURE;
177  }
178  }
179 
180  for (size_t x = 0; x < params.m_Iterations; x++)
181  {
182  // Run the inference
183  tfLiteInterpreter->Invoke();
184 
185  // Print out the output
186  for (unsigned int outputIndex = 0; outputIndex < params.m_OutputNames.size(); ++outputIndex)
187  {
188  auto tfLiteDelegateOutputId = tfLiteInterpreter->outputs()[outputIndex];
189  TfLiteIntArray* outputDims = tfLiteInterpreter->tensor(tfLiteDelegateOutputId)->dims;
190 
191  long outputSize = 1;
192  for (unsigned int dim = 0; dim < static_cast<unsigned int>(outputDims->size); ++dim)
193  {
194  outputSize *= outputDims->data[dim];
195  }
196 
197  std::cout << params.m_OutputNames[outputIndex] << ": ";
198  if (params.m_OutputTypes[outputIndex].compare("float") == 0)
199  {
200  auto tfLiteDelageOutputData = tfLiteInterpreter->typed_tensor<float>(tfLiteDelegateOutputId);
201  if(tfLiteDelageOutputData == NULL)
202  {
203  ARMNN_LOG(fatal) << "Output tensor is null, output type: "
204  "\"" << params.m_OutputTypes[outputIndex] << "\" may be incorrect.";
205  return EXIT_FAILURE;
206  }
207 
208  for (int i = 0; i < outputSize; ++i)
209  {
210  std::cout << tfLiteDelageOutputData[i] << ", ";
211  if (i % 60 == 0)
212  {
213  std::cout << std::endl;
214  }
215  }
216  }
217  else if (params.m_OutputTypes[outputIndex].compare("int") == 0)
218  {
219  auto tfLiteDelageOutputData = tfLiteInterpreter->typed_tensor<int32_t>(tfLiteDelegateOutputId);
220  if(tfLiteDelageOutputData == NULL)
221  {
222  ARMNN_LOG(fatal) << "Output tensor is null, output type: "
223  "\"" << params.m_OutputTypes[outputIndex] << "\" may be incorrect.";
224  return EXIT_FAILURE;
225  }
226 
227  for (int i = 0; i < outputSize; ++i)
228  {
229  std::cout << tfLiteDelageOutputData[i] << ", ";
230  if (i % 60 == 0)
231  {
232  std::cout << std::endl;
233  }
234  }
235  }
236  else if (params.m_OutputTypes[outputIndex].compare("int8") == 0)
237  {
238  auto tfLiteDelageOutputData = tfLiteInterpreter->typed_tensor<int8_t>(tfLiteDelegateOutputId);
239  if(tfLiteDelageOutputData == NULL)
240  {
241  ARMNN_LOG(fatal) << "Output tensor is null, output type: "
242  "\"" << params.m_OutputTypes[outputIndex] << "\" may be incorrect.";
243  return EXIT_FAILURE;
244  }
245 
246  for (int i = 0; i < outputSize; ++i)
247  {
248  std::cout << signed(tfLiteDelageOutputData[i]) << ", ";
249  if (i % 60 == 0)
250  {
251  std::cout << std::endl;
252  }
253  }
254  }
255  else if (params.m_OutputTypes[outputIndex].compare("qasymm8") == 0)
256  {
257  auto tfLiteDelageOutputData = tfLiteInterpreter->typed_tensor<uint8_t>(tfLiteDelegateOutputId);
258  if(tfLiteDelageOutputData == NULL)
259  {
260  ARMNN_LOG(fatal) << "Output tensor is null, output type: "
261  "\"" << params.m_OutputTypes[outputIndex] << "\" may be incorrect.";
262  return EXIT_FAILURE;
263  }
264 
265  for (int i = 0; i < outputSize; ++i)
266  {
267  std::cout << unsigned(tfLiteDelageOutputData[i]) << ", ";
268  if (i % 60 == 0)
269  {
270  std::cout << std::endl;
271  }
272  }
273  }
274  else
275  {
276  ARMNN_LOG(fatal) << "Output tensor is null, output type: "
277  "\"" << params.m_OutputTypes[outputIndex] <<
278  "\" may be incorrect. Output type can be specified with -z argument";
279  return EXIT_FAILURE;
280  }
281  std::cout << std::endl;
282  }
283  }
284 
285  return status;
286 }
287 #endif
288 template<typename TParser, typename TDataType>
289 int MainImpl(const ExecuteNetworkParams& params,
290  const std::shared_ptr<armnn::IRuntime>& runtime = nullptr)
291 {
292  using TContainer = mapbox::util::variant<std::vector<float>, std::vector<int>, std::vector<unsigned char>>;
293 
294  std::vector<TContainer> inputDataContainers;
295 
296  try
297  {
298  // Creates an InferenceModel, which will parse the model and load it into an IRuntime.
299  typename InferenceModel<TParser, TDataType>::Params inferenceModelParams;
300  inferenceModelParams.m_ModelPath = params.m_ModelPath;
301  inferenceModelParams.m_IsModelBinary = params.m_IsModelBinary;
302  inferenceModelParams.m_ComputeDevices = params.m_ComputeDevices;
303  inferenceModelParams.m_DynamicBackendsPath = params.m_DynamicBackendsPath;
304  inferenceModelParams.m_PrintIntermediateLayers = params.m_PrintIntermediate;
305  inferenceModelParams.m_VisualizePostOptimizationModel = params.m_EnableLayerDetails;
306  inferenceModelParams.m_ParseUnsupported = params.m_ParseUnsupported;
307  inferenceModelParams.m_InferOutputShape = params.m_InferOutputShape;
308  inferenceModelParams.m_EnableFastMath = params.m_EnableFastMath;
309  inferenceModelParams.m_SaveCachedNetwork = params.m_SaveCachedNetwork;
310  inferenceModelParams.m_CachedNetworkFilePath = params.m_CachedNetworkFilePath;
311  inferenceModelParams.m_NumberOfThreads = params.m_NumberOfThreads;
312  inferenceModelParams.m_MLGOTuningFilePath = params.m_MLGOTuningFilePath;
313 
314  for(const std::string& inputName: params.m_InputNames)
315  {
316  inferenceModelParams.m_InputBindings.push_back(inputName);
317  }
318 
319  for(unsigned int i = 0; i < params.m_InputTensorShapes.size(); ++i)
320  {
321  inferenceModelParams.m_InputShapes.push_back(*params.m_InputTensorShapes[i]);
322  }
323 
324  for(const std::string& outputName: params.m_OutputNames)
325  {
326  inferenceModelParams.m_OutputBindings.push_back(outputName);
327  }
328 
329  inferenceModelParams.m_SubgraphId = params.m_SubgraphId;
330  inferenceModelParams.m_EnableFp16TurboMode = params.m_EnableFp16TurboMode;
331  inferenceModelParams.m_EnableBf16TurboMode = params.m_EnableBf16TurboMode;
332 
333  InferenceModel<TParser, TDataType> model(inferenceModelParams,
334  params.m_EnableProfiling,
335  params.m_DynamicBackendsPath,
336  runtime);
337 
338  const size_t numInputs = inferenceModelParams.m_InputBindings.size();
339  for(unsigned int i = 0; i < numInputs; ++i)
340  {
342  armnn::MakeOptional<QuantizationParams>(
343  model.GetInputQuantizationParams()) :
345 
348  armnn::MakeOptional<std::string>(
349  params.m_InputTensorDataFilePaths[i]);
350 
351  unsigned int numElements = model.GetInputSize(i);
352  if (params.m_InputTensorShapes.size() > i && params.m_InputTensorShapes[i])
353  {
354  // If the user has provided a tensor shape for the current input,
355  // override numElements
356  numElements = params.m_InputTensorShapes[i]->GetNumElements();
357  }
358 
359  TContainer tensorData;
360  PopulateTensorWithData(tensorData,
361  numElements,
362  params.m_InputTypes[i],
363  qParams,
364  dataFile);
365 
366  inputDataContainers.push_back(tensorData);
367  }
368 
369  const size_t numOutputs = inferenceModelParams.m_OutputBindings.size();
370  std::vector<TContainer> outputDataContainers;
371 
372  for (unsigned int i = 0; i < numOutputs; ++i)
373  {
374  if (params.m_OutputTypes[i].compare("float") == 0)
375  {
376  outputDataContainers.push_back(std::vector<float>(model.GetOutputSize(i)));
377  }
378  else if (params.m_OutputTypes[i].compare("int") == 0)
379  {
380  outputDataContainers.push_back(std::vector<int>(model.GetOutputSize(i)));
381  }
382  else if (params.m_OutputTypes[i].compare("qasymm8") == 0)
383  {
384  outputDataContainers.push_back(std::vector<uint8_t>(model.GetOutputSize(i)));
385  }
386  else
387  {
388  ARMNN_LOG(fatal) << "Unsupported tensor data type \"" << params.m_OutputTypes[i] << "\". ";
389  return EXIT_FAILURE;
390  }
391  }
392 
393  for (size_t x = 0; x < params.m_Iterations; x++)
394  {
395  // model.Run returns the inference time elapsed in EnqueueWorkload (in milliseconds)
396  auto inference_duration = model.Run(inputDataContainers, outputDataContainers);
397 
398  if (params.m_GenerateTensorData)
399  {
400  ARMNN_LOG(warning) << "The input data was generated, note that the output will not be useful";
401  }
402 
403  // Print output tensors
404  const auto& infosOut = model.GetOutputBindingInfos();
405  for (size_t i = 0; i < numOutputs; i++)
406  {
407  const armnn::TensorInfo& infoOut = infosOut[i].second;
408  auto outputTensorFile = params.m_OutputTensorFiles.empty() ? "" : params.m_OutputTensorFiles[i];
409 
410  TensorPrinter printer(inferenceModelParams.m_OutputBindings[i],
411  infoOut,
412  outputTensorFile,
413  params.m_DequantizeOutput);
414  mapbox::util::apply_visitor(printer, outputDataContainers[i]);
415  }
416 
417  ARMNN_LOG(info) << "\nInference time: " << std::setprecision(2)
418  << std::fixed << inference_duration.count() << " ms\n";
419 
420  // If thresholdTime == 0.0 (default), then it hasn't been supplied at command line
421  if (params.m_ThresholdTime != 0.0)
422  {
423  ARMNN_LOG(info) << "Threshold time: " << std::setprecision(2)
424  << std::fixed << params.m_ThresholdTime << " ms";
425  auto thresholdMinusInference = params.m_ThresholdTime - inference_duration.count();
426  ARMNN_LOG(info) << "Threshold time - Inference time: " << std::setprecision(2)
427  << std::fixed << thresholdMinusInference << " ms" << "\n";
428 
429  if (thresholdMinusInference < 0)
430  {
431  std::string errorMessage = "Elapsed inference time is greater than provided threshold time.";
432  ARMNN_LOG(fatal) << errorMessage;
433  }
434  }
435  }
436  }
437  catch (const armnn::Exception& e)
438  {
439  ARMNN_LOG(fatal) << "Armnn Error: " << e.what();
440  return EXIT_FAILURE;
441  }
442 
443  return EXIT_SUCCESS;
444 }
445 
446 
447 // MAIN
448 int main(int argc, const char* argv[])
449 {
450  // Configures logging for both the ARMNN library and this test program.
451  #ifdef NDEBUG
453  #else
455  #endif
456  armnn::ConfigureLogging(true, true, level);
457 
458 
459  // Get ExecuteNetwork parameters and runtime options from command line
460  ProgramOptions ProgramOptions(argc, argv);
461 
462  // Create runtime
463  std::shared_ptr<armnn::IRuntime> runtime(armnn::IRuntime::Create(ProgramOptions.m_RuntimeOptions));
464 
465  std::string modelFormat = ProgramOptions.m_ExNetParams.m_ModelFormat;
466 
467  // Forward to implementation based on the parser type
468  if (modelFormat.find("armnn") != std::string::npos)
469  {
470  #if defined(ARMNN_SERIALIZER)
471  return MainImpl<armnnDeserializer::IDeserializer, float>(ProgramOptions.m_ExNetParams, runtime);
472  #else
473  ARMNN_LOG(fatal) << "Not built with serialization support.";
474  return EXIT_FAILURE;
475  #endif
476  }
477  else if (modelFormat.find("caffe") != std::string::npos)
478  {
479  #if defined(ARMNN_CAFFE_PARSER)
480  return MainImpl<armnnCaffeParser::ICaffeParser, float>(ProgramOptions.m_ExNetParams, runtime);
481  #else
482  ARMNN_LOG(fatal) << "Not built with Caffe parser support.";
483  return EXIT_FAILURE;
484  #endif
485  }
486  else if (modelFormat.find("onnx") != std::string::npos)
487  {
488  #if defined(ARMNN_ONNX_PARSER)
489  return MainImpl<armnnOnnxParser::IOnnxParser, float>(ProgramOptions.m_ExNetParams, runtime);
490  #else
491  ARMNN_LOG(fatal) << "Not built with Onnx parser support.";
492  return EXIT_FAILURE;
493  #endif
494  }
495  else if (modelFormat.find("tensorflow") != std::string::npos)
496  {
497  #if defined(ARMNN_TF_PARSER)
498  return MainImpl<armnnTfParser::ITfParser, float>(ProgramOptions.m_ExNetParams, runtime);
499  #else
500  ARMNN_LOG(fatal) << "Not built with Tensorflow parser support.";
501  return EXIT_FAILURE;
502  #endif
503  }
504  else if(modelFormat.find("tflite") != std::string::npos)
505  {
506 
507  if (ProgramOptions.m_ExNetParams.m_EnableDelegate)
508  {
509  #if defined(ARMNN_TF_LITE_DELEGATE)
510  return TfLiteDelegateMainImpl(ProgramOptions.m_ExNetParams, runtime);
511  #else
512  ARMNN_LOG(fatal) << "Not built with Arm NN Tensorflow-Lite delegate support.";
513  return EXIT_FAILURE;
514  #endif
515  }
516  #if defined(ARMNN_TF_LITE_PARSER)
517  return MainImpl<armnnTfLiteParser::ITfLiteParser, float>(ProgramOptions.m_ExNetParams, runtime);
518  #else
519  ARMNN_LOG(fatal) << "Not built with Tensorflow-Lite parser support.";
520  return EXIT_FAILURE;
521  #endif
522  }
523  else
524  {
525  ARMNN_LOG(fatal) << "Unknown model format: '" << modelFormat
526  << "'. Please include 'caffe', 'tensorflow', 'tflite' or 'onnx'";
527  return EXIT_FAILURE;
528  }
529 }
ExecuteNetworkParams m_ExNetParams
std::vector< std::string > m_InputTypes
static IRuntimePtr Create(const CreationOptions &options)
Definition: Runtime.cpp:37
std::vector< TensorShapePtr > m_InputTensorShapes
QuantizationParams GetInputQuantizationParams(unsigned int inputIndex=0u) const
const std::vector< armnn::BindingPointInfo > & GetOutputBindingInfos() const
void ConfigureLogging(bool printToStandardOutput, bool printToDebugOutput, LogSeverity severity)
Configures the logging behaviour of the ARMNN library.
Definition: Utils.cpp:18
mapbox::util::variant< std::vector< float >, std::vector< int >, std::vector< unsigned char > > TContainer
virtual const char * what() const noexcept override
Definition: Exceptions.cpp:32
armnn::IRuntime::CreationOptions m_RuntimeOptions
#define ARMNN_LOG(severity)
Definition: Logging.hpp:202
void PopulateTensorWithData(TContainer &tensorData, unsigned int numElements, const std::string &dataTypeStr, const armnn::Optional< QuantizationParams > &qParams, const armnn::Optional< std::string > &dataFile)
std::vector< std::string > m_OutputNames
Copyright (c) 2021 ARM Limited and Contributors.
std::vector< std::string > m_OutputTensorFiles
unsigned int GetOutputSize(unsigned int outputIndex=0u) const
std::vector< std::string > m_InputBindings
std::vector< armnn::BackendId > m_ComputeDevices
std::vector< std::string > m_OutputTypes
std::vector< armnn::TensorShape > m_InputShapes
Holds all parameters necessary to execute a network Check ExecuteNetworkProgramOptions.cpp for a description of each parameter.
std::vector< std::string > m_OutputBindings
std::vector< armnn::BackendId > m_ComputeDevices
std::vector< std::string > m_InputNames
std::vector< std::string > m_InputTensorDataFilePaths
Holds and parses program options for the ExecuteNetwork application.
TfLiteDelegate * TfLiteArmnnDelegateCreate(armnnDelegate::DelegateOptions options)
EmptyOptional is used to initialize the Optional class in case we want to have default value for an O...
Definition: Optional.hpp:32
std::chrono::duration< double, std::milli > Run(const std::vector< TContainer > &inputContainers, std::vector< TContainer > &outputContainers)
int main(int argc, const char *argv[])
Base class for all ArmNN exceptions so that users can filter to just those.
Definition: Exceptions.hpp:46
unsigned int GetInputSize(unsigned int inputIndex=0u) const
void TfLiteArmnnDelegateDelete(TfLiteDelegate *tfLiteDelegate)
Optional< T > MakeOptional(Args &&... args)
Utility template that constructs an object of type T in-place and wraps it inside an Optional<T> obje...
Definition: Optional.hpp:305
int MainImpl(const ExecuteNetworkParams &params, const std::shared_ptr< armnn::IRuntime > &runtime=nullptr)
std::enable_if_t< std::is_unsigned< Source >::value &&std::is_unsigned< Dest >::value, Dest > numeric_cast(Source source)
Definition: NumericCast.hpp:35
LogSeverity
Definition: Utils.hpp:13