ArmNN
 20.02
NetworkExecutionUtils.hpp
Go to the documentation of this file.
1 //
2 // Copyright © 2017 Arm Ltd. All rights reserved.
3 // SPDX-License-Identifier: MIT
4 //
5 #include <armnn/ArmNN.hpp>
6 #include <armnn/TypesUtils.hpp>
7 
8 #if defined(ARMNN_SERIALIZER)
10 #endif
11 #if defined(ARMNN_CAFFE_PARSER)
13 #endif
14 #if defined(ARMNN_TF_PARSER)
16 #endif
17 #if defined(ARMNN_TF_LITE_PARSER)
19 #endif
20 #if defined(ARMNN_ONNX_PARSER)
22 #endif
23 #include "CsvReader.hpp"
24 #include "../InferenceTest.hpp"
25 
26 #include <Profiling.hpp>
27 #include <ResolveType.hpp>
28 
29 #include <boost/algorithm/string/trim.hpp>
30 #include <boost/algorithm/string/split.hpp>
31 #include <boost/algorithm/string/classification.hpp>
32 #include <boost/program_options.hpp>
33 #include <boost/variant.hpp>
34 
35 #include <iostream>
36 #include <fstream>
37 #include <functional>
38 #include <future>
39 #include <algorithm>
40 #include <iterator>
41 
42 namespace
43 {
44 
45 // Configure boost::program_options for command-line parsing and validation.
46 namespace po = boost::program_options;
47 
48 template<typename T, typename TParseElementFunc>
49 std::vector<T> ParseArrayImpl(std::istream& stream, TParseElementFunc parseElementFunc, const char * chars = "\t ,:")
50 {
51  std::vector<T> result;
52  // Processes line-by-line.
53  std::string line;
54  while (std::getline(stream, line))
55  {
56  std::vector<std::string> tokens;
57  try
58  {
59  // Coverity fix: boost::split() may throw an exception of type boost::bad_function_call.
60  boost::split(tokens, line, boost::algorithm::is_any_of(chars), boost::token_compress_on);
61  }
62  catch (const std::exception& e)
63  {
64  ARMNN_LOG(error) << "An error occurred when splitting tokens: " << e.what();
65  continue;
66  }
67  for (const std::string& token : tokens)
68  {
69  if (!token.empty()) // See https://stackoverflow.com/questions/10437406/
70  {
71  try
72  {
73  result.push_back(parseElementFunc(token));
74  }
75  catch (const std::exception&)
76  {
77  ARMNN_LOG(error) << "'" << token << "' is not a valid number. It has been ignored.";
78  }
79  }
80  }
81  }
82 
83  return result;
84 }
85 
86 bool CheckOption(const po::variables_map& vm,
87  const char* option)
88 {
89  // Check that the given option is valid.
90  if (option == nullptr)
91  {
92  return false;
93  }
94 
95  // Check whether 'option' is provided.
96  return vm.find(option) != vm.end();
97 }
98 
99 void CheckOptionDependency(const po::variables_map& vm,
100  const char* option,
101  const char* required)
102 {
103  // Check that the given options are valid.
104  if (option == nullptr || required == nullptr)
105  {
106  throw po::error("Invalid option to check dependency for");
107  }
108 
109  // Check that if 'option' is provided, 'required' is also provided.
110  if (CheckOption(vm, option) && !vm[option].defaulted())
111  {
112  if (CheckOption(vm, required) == 0 || vm[required].defaulted())
113  {
114  throw po::error(std::string("Option '") + option + "' requires option '" + required + "'.");
115  }
116  }
117 }
118 
119 void CheckOptionDependencies(const po::variables_map& vm)
120 {
121  CheckOptionDependency(vm, "model-path", "model-format");
122  CheckOptionDependency(vm, "model-path", "input-name");
123  CheckOptionDependency(vm, "model-path", "output-name");
124  CheckOptionDependency(vm, "input-tensor-shape", "model-path");
125 }
126 
127 template<armnn::DataType NonQuantizedType>
128 auto ParseDataArray(std::istream & stream);
129 
130 template<armnn::DataType QuantizedType>
131 auto ParseDataArray(std::istream& stream,
132  const float& quantizationScale,
133  const int32_t& quantizationOffset);
134 
135 template<>
136 auto ParseDataArray<armnn::DataType::Float32>(std::istream & stream)
137 {
138  return ParseArrayImpl<float>(stream, [](const std::string& s) { return std::stof(s); });
139 }
140 
141 template<>
142 auto ParseDataArray<armnn::DataType::Signed32>(std::istream & stream)
143 {
144  return ParseArrayImpl<int>(stream, [](const std::string & s) { return std::stoi(s); });
145 }
146 
147 template<>
148 auto ParseDataArray<armnn::DataType::QAsymmU8>(std::istream& stream)
149 {
150  return ParseArrayImpl<uint8_t>(stream,
151  [](const std::string& s) { return boost::numeric_cast<uint8_t>(std::stoi(s)); });
152 }
153 
154 template<>
155 auto ParseDataArray<armnn::DataType::QAsymmU8>(std::istream& stream,
156  const float& quantizationScale,
157  const int32_t& quantizationOffset)
158 {
159  return ParseArrayImpl<uint8_t>(stream,
160  [&quantizationScale, &quantizationOffset](const std::string & s)
161  {
162  return boost::numeric_cast<uint8_t>(
163  armnn::Quantize<uint8_t>(std::stof(s),
164  quantizationScale,
165  quantizationOffset));
166  });
167 }
168 std::vector<unsigned int> ParseArray(std::istream& stream)
169 {
170  return ParseArrayImpl<unsigned int>(stream,
171  [](const std::string& s) { return boost::numeric_cast<unsigned int>(std::stoi(s)); });
172 }
173 
174 std::vector<std::string> ParseStringList(const std::string & inputString, const char * delimiter)
175 {
176  std::stringstream stream(inputString);
177  return ParseArrayImpl<std::string>(stream, [](const std::string& s) { return boost::trim_copy(s); }, delimiter);
178 }
179 
180 void RemoveDuplicateDevices(std::vector<armnn::BackendId>& computeDevices)
181 {
182  // Mark the duplicate devices as 'Undefined'.
183  for (auto i = computeDevices.begin(); i != computeDevices.end(); ++i)
184  {
185  for (auto j = std::next(i); j != computeDevices.end(); ++j)
186  {
187  if (*j == *i)
188  {
190  }
191  }
192  }
193 
194  // Remove 'Undefined' devices.
195  computeDevices.erase(std::remove(computeDevices.begin(), computeDevices.end(), armnn::Compute::Undefined),
196  computeDevices.end());
197 }
198 
199 struct TensorPrinter : public boost::static_visitor<>
200 {
201  TensorPrinter(const std::string& binding,
202  const armnn::TensorInfo& info,
203  const std::string& outputTensorFile,
204  bool dequantizeOutput)
205  : m_OutputBinding(binding)
206  , m_Scale(info.GetQuantizationScale())
207  , m_Offset(info.GetQuantizationOffset())
208  , m_OutputTensorFile(outputTensorFile)
209  , m_DequantizeOutput(dequantizeOutput)
210  {}
211 
212  void operator()(const std::vector<float>& values)
213  {
214  ForEachValue(values, [](float value)
215  {
216  printf("%f ", value);
217  });
218  WriteToFile(values);
219  }
220 
221  void operator()(const std::vector<uint8_t>& values)
222  {
223  if(m_DequantizeOutput)
224  {
225  auto& scale = m_Scale;
226  auto& offset = m_Offset;
227  std::vector<float> dequantizedValues;
228  ForEachValue(values, [&scale, &offset, &dequantizedValues](uint8_t value)
229  {
230  auto dequantizedValue = armnn::Dequantize(value, scale, offset);
231  printf("%f ", dequantizedValue);
232  dequantizedValues.push_back(dequantizedValue);
233  });
234  WriteToFile(dequantizedValues);
235  }
236  else
237  {
238  const std::vector<int> intValues(values.begin(), values.end());
239  operator()(intValues);
240  }
241  }
242 
243  void operator()(const std::vector<int>& values)
244  {
245  ForEachValue(values, [](int value)
246  {
247  printf("%d ", value);
248  });
249  WriteToFile(values);
250  }
251 
252 private:
253  template<typename Container, typename Delegate>
254  void ForEachValue(const Container& c, Delegate delegate)
255  {
256  std::cout << m_OutputBinding << ": ";
257  for (const auto& value : c)
258  {
259  delegate(value);
260  }
261  printf("\n");
262  }
263 
264  template<typename T>
265  void WriteToFile(const std::vector<T>& values)
266  {
267  if (!m_OutputTensorFile.empty())
268  {
269  std::ofstream outputTensorFile;
270  outputTensorFile.open(m_OutputTensorFile, std::ofstream::out | std::ofstream::trunc);
271  if (outputTensorFile.is_open())
272  {
273  outputTensorFile << m_OutputBinding << ": ";
274  std::copy(values.begin(), values.end(), std::ostream_iterator<T>(outputTensorFile, " "));
275  }
276  else
277  {
278  ARMNN_LOG(info) << "Output Tensor File: " << m_OutputTensorFile << " could not be opened!";
279  }
280  outputTensorFile.close();
281  }
282  }
283 
284  std::string m_OutputBinding;
285  float m_Scale=0.0f;
286  int m_Offset=0;
287  std::string m_OutputTensorFile;
288  bool m_DequantizeOutput = false;
289 };
290 
291 
292 
293 template<armnn::DataType ArmnnType, typename T = armnn::ResolveType<ArmnnType>>
294 std::vector<T> GenerateDummyTensorData(unsigned int numElements)
295 {
296  return std::vector<T>(numElements, static_cast<T>(0));
297 }
298 
299 using TContainer = boost::variant<std::vector<float>, std::vector<int>, std::vector<unsigned char>>;
300 using QuantizationParams = std::pair<float, int32_t>;
301 
302 void PopulateTensorWithData(TContainer& tensorData,
303  unsigned int numElements,
304  const std::string& dataTypeStr,
306  const armnn::Optional<std::string>& dataFile)
307 {
308  const bool readFromFile = dataFile.has_value() && !dataFile.value().empty();
309  const bool quantizeData = qParams.has_value();
310 
311  std::ifstream inputTensorFile;
312  if (readFromFile)
313  {
314  inputTensorFile = std::ifstream(dataFile.value());
315  }
316 
317  if (dataTypeStr.compare("float") == 0)
318  {
319  if (quantizeData)
320  {
321  const float qScale = qParams.value().first;
322  const int qOffset = qParams.value().second;
323 
324  tensorData = readFromFile ?
325  ParseDataArray<armnn::DataType::QAsymmU8>(inputTensorFile, qScale, qOffset) :
326  GenerateDummyTensorData<armnn::DataType::QAsymmU8>(numElements);
327  }
328  else
329  {
330  tensorData = readFromFile ?
331  ParseDataArray<armnn::DataType::Float32>(inputTensorFile) :
332  GenerateDummyTensorData<armnn::DataType::Float32>(numElements);
333  }
334  }
335  else if (dataTypeStr.compare("int") == 0)
336  {
337  tensorData = readFromFile ?
338  ParseDataArray<armnn::DataType::Signed32>(inputTensorFile) :
339  GenerateDummyTensorData<armnn::DataType::Signed32>(numElements);
340  }
341  else if (dataTypeStr.compare("qasymm8") == 0)
342  {
343  tensorData = readFromFile ?
344  ParseDataArray<armnn::DataType::QAsymmU8>(inputTensorFile) :
345  GenerateDummyTensorData<armnn::DataType::QAsymmU8>(numElements);
346  }
347  else
348  {
349  std::string errorMessage = "Unsupported tensor data type " + dataTypeStr;
350  ARMNN_LOG(fatal) << errorMessage;
351 
352  inputTensorFile.close();
353  throw armnn::Exception(errorMessage);
354  }
355 
356  inputTensorFile.close();
357 }
358 
359 } // anonymous namespace
360 
361 bool generateTensorData = true;
362 
364 {
365  using TensorShapePtr = std::unique_ptr<armnn::TensorShape>;
366 
367  const char* m_ModelPath;
369  std::vector<armnn::BackendId> m_ComputeDevices;
371  std::vector<string> m_InputNames;
372  std::vector<TensorShapePtr> m_InputTensorShapes;
373  std::vector<string> m_InputTensorDataFilePaths;
374  std::vector<string> m_InputTypes;
376  std::vector<string> m_OutputTypes;
377  std::vector<string> m_OutputNames;
378  std::vector<string> m_OutputTensorFiles;
384  size_t m_SubgraphId;
385  bool m_EnableLayerDetails = false;
387  bool m_ParseUnsupported = false;
388 };
389 
390 template<typename TParser, typename TDataType>
391 int MainImpl(const ExecuteNetworkParams& params,
392  const std::shared_ptr<armnn::IRuntime>& runtime = nullptr)
393 {
394  using TContainer = boost::variant<std::vector<float>, std::vector<int>, std::vector<unsigned char>>;
395 
396  std::vector<TContainer> inputDataContainers;
397 
398  try
399  {
400  // Creates an InferenceModel, which will parse the model and load it into an IRuntime.
401  typename InferenceModel<TParser, TDataType>::Params inferenceModelParams;
402  inferenceModelParams.m_ModelPath = params.m_ModelPath;
403  inferenceModelParams.m_IsModelBinary = params.m_IsModelBinary;
404  inferenceModelParams.m_ComputeDevices = params.m_ComputeDevices;
405  inferenceModelParams.m_DynamicBackendsPath = params.m_DynamicBackendsPath;
406  inferenceModelParams.m_PrintIntermediateLayers = params.m_PrintIntermediate;
407  inferenceModelParams.m_VisualizePostOptimizationModel = params.m_EnableLayerDetails;
408  inferenceModelParams.m_ParseUnsupported = params.m_ParseUnsupported;
409 
410  for(const std::string& inputName: params.m_InputNames)
411  {
412  inferenceModelParams.m_InputBindings.push_back(inputName);
413  }
414 
415  for(unsigned int i = 0; i < params.m_InputTensorShapes.size(); ++i)
416  {
417  inferenceModelParams.m_InputShapes.push_back(*params.m_InputTensorShapes[i]);
418  }
419 
420  for(const std::string& outputName: params.m_OutputNames)
421  {
422  inferenceModelParams.m_OutputBindings.push_back(outputName);
423  }
424 
425  inferenceModelParams.m_SubgraphId = params.m_SubgraphId;
426  inferenceModelParams.m_EnableFp16TurboMode = params.m_EnableFp16TurboMode;
427 
428  InferenceModel<TParser, TDataType> model(inferenceModelParams,
429  params.m_EnableProfiling,
430  params.m_DynamicBackendsPath,
431  runtime);
432 
433  const size_t numInputs = inferenceModelParams.m_InputBindings.size();
434  for(unsigned int i = 0; i < numInputs; ++i)
435  {
437  armnn::MakeOptional<QuantizationParams>(model.GetInputQuantizationParams()) :
439 
442  armnn::MakeOptional<std::string>(params.m_InputTensorDataFilePaths[i]);
443 
444  unsigned int numElements = model.GetInputSize(i);
445  if (params.m_InputTensorShapes.size() > i && params.m_InputTensorShapes[i])
446  {
447  // If the user has provided a tensor shape for the current input,
448  // override numElements
449  numElements = params.m_InputTensorShapes[i]->GetNumElements();
450  }
451 
452  TContainer tensorData;
453  PopulateTensorWithData(tensorData,
454  numElements,
455  params.m_InputTypes[i],
456  qParams,
457  dataFile);
458 
459  inputDataContainers.push_back(tensorData);
460  }
461 
462  const size_t numOutputs = inferenceModelParams.m_OutputBindings.size();
463  std::vector<TContainer> outputDataContainers;
464 
465  for (unsigned int i = 0; i < numOutputs; ++i)
466  {
467  if (params.m_OutputTypes[i].compare("float") == 0)
468  {
469  outputDataContainers.push_back(std::vector<float>(model.GetOutputSize(i)));
470  }
471  else if (params.m_OutputTypes[i].compare("int") == 0)
472  {
473  outputDataContainers.push_back(std::vector<int>(model.GetOutputSize(i)));
474  }
475  else if (params.m_OutputTypes[i].compare("qasymm8") == 0)
476  {
477  outputDataContainers.push_back(std::vector<uint8_t>(model.GetOutputSize(i)));
478  }
479  else
480  {
481  ARMNN_LOG(fatal) << "Unsupported tensor data type \"" << params.m_OutputTypes[i] << "\". ";
482  return EXIT_FAILURE;
483  }
484  }
485 
486  // model.Run returns the inference time elapsed in EnqueueWorkload (in milliseconds)
487  auto inference_duration = model.Run(inputDataContainers, outputDataContainers);
488 
489  if (params.m_GenerateTensorData)
490  {
491  ARMNN_LOG(warning) << "The input data was generated, note that the output will not be useful";
492  }
493 
494  // Print output tensors
495  const auto& infosOut = model.GetOutputBindingInfos();
496  for (size_t i = 0; i < numOutputs; i++)
497  {
498  const armnn::TensorInfo& infoOut = infosOut[i].second;
499  auto outputTensorFile = params.m_OutputTensorFiles.empty() ? "" : params.m_OutputTensorFiles[i];
500 
501  TensorPrinter printer(inferenceModelParams.m_OutputBindings[i],
502  infoOut,
503  outputTensorFile,
504  params.m_DequantizeOutput);
505  boost::apply_visitor(printer, outputDataContainers[i]);
506  }
507 
508  ARMNN_LOG(info) << "\nInference time: " << std::setprecision(2)
509  << std::fixed << inference_duration.count() << " ms";
510 
511  // If thresholdTime == 0.0 (default), then it hasn't been supplied at command line
512  if (params.m_ThresholdTime != 0.0)
513  {
514  ARMNN_LOG(info) << "Threshold time: " << std::setprecision(2)
515  << std::fixed << params.m_ThresholdTime << " ms";
516  auto thresholdMinusInference = params.m_ThresholdTime - inference_duration.count();
517  ARMNN_LOG(info) << "Threshold time - Inference time: " << std::setprecision(2)
518  << std::fixed << thresholdMinusInference << " ms" << "\n";
519 
520  if (thresholdMinusInference < 0)
521  {
522  std::string errorMessage = "Elapsed inference time is greater than provided threshold time.";
523  ARMNN_LOG(fatal) << errorMessage;
524  }
525  }
526  }
527  catch (armnn::Exception const& e)
528  {
529  ARMNN_LOG(fatal) << "Armnn Error: " << e.what();
530  return EXIT_FAILURE;
531  }
532 
533  return EXIT_SUCCESS;
534 }
535 
536 // This will run a test
537 int RunTest(const std::string& format,
538  const std::string& inputTensorShapesStr,
539  const vector<armnn::BackendId>& computeDevices,
540  const std::string& dynamicBackendsPath,
541  const std::string& path,
542  const std::string& inputNames,
543  const std::string& inputTensorDataFilePaths,
544  const std::string& inputTypes,
545  bool quantizeInput,
546  const std::string& outputTypes,
547  const std::string& outputNames,
548  const std::string& outputTensorFiles,
549  bool dequantizeOuput,
550  bool enableProfiling,
551  bool enableFp16TurboMode,
552  const double& thresholdTime,
553  bool printIntermediate,
554  const size_t subgraphId,
555  bool enableLayerDetails = false,
556  bool parseUnsupported = false,
557  const std::shared_ptr<armnn::IRuntime>& runtime = nullptr)
558 {
559  std::string modelFormat = boost::trim_copy(format);
560  std::string modelPath = boost::trim_copy(path);
561  std::vector<std::string> inputNamesVector = ParseStringList(inputNames, ",");
562  std::vector<std::string> inputTensorShapesVector = ParseStringList(inputTensorShapesStr, ":");
563  std::vector<std::string> inputTensorDataFilePathsVector = ParseStringList(
564  inputTensorDataFilePaths, ",");
565  std::vector<std::string> outputNamesVector = ParseStringList(outputNames, ",");
566  std::vector<std::string> inputTypesVector = ParseStringList(inputTypes, ",");
567  std::vector<std::string> outputTypesVector = ParseStringList(outputTypes, ",");
568  std::vector<std::string> outputTensorFilesVector = ParseStringList(outputTensorFiles, ",");
569 
570  // Parse model binary flag from the model-format string we got from the command-line
571  bool isModelBinary;
572  if (modelFormat.find("bin") != std::string::npos)
573  {
574  isModelBinary = true;
575  }
576  else if (modelFormat.find("txt") != std::string::npos || modelFormat.find("text") != std::string::npos)
577  {
578  isModelBinary = false;
579  }
580  else
581  {
582  ARMNN_LOG(fatal) << "Unknown model format: '" << modelFormat << "'. Please include 'binary' or 'text'";
583  return EXIT_FAILURE;
584  }
585 
586  if ((inputTensorShapesVector.size() != 0) && (inputTensorShapesVector.size() != inputNamesVector.size()))
587  {
588  ARMNN_LOG(fatal) << "input-name and input-tensor-shape must have the same amount of elements.";
589  return EXIT_FAILURE;
590  }
591 
592  if ((inputTensorDataFilePathsVector.size() != 0) &&
593  (inputTensorDataFilePathsVector.size() != inputNamesVector.size()))
594  {
595  ARMNN_LOG(fatal) << "input-name and input-tensor-data must have the same amount of elements.";
596  return EXIT_FAILURE;
597  }
598 
599  if ((outputTensorFilesVector.size() != 0) &&
600  (outputTensorFilesVector.size() != outputNamesVector.size()))
601  {
602  ARMNN_LOG(fatal) << "output-name and write-outputs-to-file must have the same amount of elements.";
603  return EXIT_FAILURE;
604  }
605 
606  if (inputTypesVector.size() == 0)
607  {
608  //Defaults the value of all inputs to "float"
609  inputTypesVector.assign(inputNamesVector.size(), "float");
610  }
611  else if ((inputTypesVector.size() != 0) && (inputTypesVector.size() != inputNamesVector.size()))
612  {
613  ARMNN_LOG(fatal) << "input-name and input-type must have the same amount of elements.";
614  return EXIT_FAILURE;
615  }
616 
617  if (outputTypesVector.size() == 0)
618  {
619  //Defaults the value of all outputs to "float"
620  outputTypesVector.assign(outputNamesVector.size(), "float");
621  }
622  else if ((outputTypesVector.size() != 0) && (outputTypesVector.size() != outputNamesVector.size()))
623  {
624  ARMNN_LOG(fatal) << "output-name and output-type must have the same amount of elements.";
625  return EXIT_FAILURE;
626  }
627 
628  // Parse input tensor shape from the string we got from the command-line.
629  std::vector<std::unique_ptr<armnn::TensorShape>> inputTensorShapes;
630 
631  if (!inputTensorShapesVector.empty())
632  {
633  inputTensorShapes.reserve(inputTensorShapesVector.size());
634 
635  for(const std::string& shape : inputTensorShapesVector)
636  {
637  std::stringstream ss(shape);
638  std::vector<unsigned int> dims = ParseArray(ss);
639 
640  try
641  {
642  // Coverity fix: An exception of type armnn::InvalidArgumentException is thrown and never caught.
643  inputTensorShapes.push_back(std::make_unique<armnn::TensorShape>(dims.size(), dims.data()));
644  }
645  catch (const armnn::InvalidArgumentException& e)
646  {
647  ARMNN_LOG(fatal) << "Cannot create tensor shape: " << e.what();
648  return EXIT_FAILURE;
649  }
650  }
651  }
652 
653  // Check that threshold time is not less than zero
654  if (thresholdTime < 0)
655  {
656  ARMNN_LOG(fatal) << "Threshold time supplied as a command line argument is less than zero.";
657  return EXIT_FAILURE;
658  }
659 
660  ExecuteNetworkParams params;
661  params.m_ModelPath = modelPath.c_str();
662  params.m_IsModelBinary = isModelBinary;
663  params.m_ComputeDevices = computeDevices;
664  params.m_DynamicBackendsPath = dynamicBackendsPath;
665  params.m_InputNames = inputNamesVector;
666  params.m_InputTensorShapes = std::move(inputTensorShapes);
667  params.m_InputTensorDataFilePaths = inputTensorDataFilePathsVector;
668  params.m_InputTypes = inputTypesVector;
669  params.m_QuantizeInput = quantizeInput;
670  params.m_OutputTypes = outputTypesVector;
671  params.m_OutputNames = outputNamesVector;
672  params.m_OutputTensorFiles = outputTensorFilesVector;
673  params.m_DequantizeOutput = dequantizeOuput;
674  params.m_EnableProfiling = enableProfiling;
675  params.m_EnableFp16TurboMode = enableFp16TurboMode;
676  params.m_ThresholdTime = thresholdTime;
677  params.m_PrintIntermediate = printIntermediate;
678  params.m_SubgraphId = subgraphId;
679  params.m_EnableLayerDetails = enableLayerDetails;
680  params.m_GenerateTensorData = inputTensorDataFilePathsVector.empty();
681  params.m_ParseUnsupported = parseUnsupported;
682 
683  // Warn if ExecuteNetwork will generate dummy input data
684  if (params.m_GenerateTensorData)
685  {
686  ARMNN_LOG(warning) << "No input files provided, input tensors will be filled with 0s.";
687  }
688 
689  // Forward to implementation based on the parser type
690  if (modelFormat.find("armnn") != std::string::npos)
691  {
692 #if defined(ARMNN_SERIALIZER)
693  return MainImpl<armnnDeserializer::IDeserializer, float>(params, runtime);
694 #else
695  ARMNN_LOG(fatal) << "Not built with serialization support.";
696  return EXIT_FAILURE;
697 #endif
698  }
699  else if (modelFormat.find("caffe") != std::string::npos)
700  {
701 #if defined(ARMNN_CAFFE_PARSER)
702  return MainImpl<armnnCaffeParser::ICaffeParser, float>(params, runtime);
703 #else
704  ARMNN_LOG(fatal) << "Not built with Caffe parser support.";
705  return EXIT_FAILURE;
706 #endif
707  }
708  else if (modelFormat.find("onnx") != std::string::npos)
709 {
710 #if defined(ARMNN_ONNX_PARSER)
711  return MainImpl<armnnOnnxParser::IOnnxParser, float>(params, runtime);
712 #else
713  ARMNN_LOG(fatal) << "Not built with Onnx parser support.";
714  return EXIT_FAILURE;
715 #endif
716  }
717  else if (modelFormat.find("tensorflow") != std::string::npos)
718  {
719 #if defined(ARMNN_TF_PARSER)
720  return MainImpl<armnnTfParser::ITfParser, float>(params, runtime);
721 #else
722  ARMNN_LOG(fatal) << "Not built with Tensorflow parser support.";
723  return EXIT_FAILURE;
724 #endif
725  }
726  else if(modelFormat.find("tflite") != std::string::npos)
727  {
728 #if defined(ARMNN_TF_LITE_PARSER)
729  if (! isModelBinary)
730  {
731  ARMNN_LOG(fatal) << "Unknown model format: '" << modelFormat << "'. Only 'binary' format supported \
732  for tflite files";
733  return EXIT_FAILURE;
734  }
735  return MainImpl<armnnTfLiteParser::ITfLiteParser, float>(params, runtime);
736 #else
737  ARMNN_LOG(fatal) << "Unknown model format: '" << modelFormat <<
738  "'. Please include 'caffe', 'tensorflow', 'tflite' or 'onnx'";
739  return EXIT_FAILURE;
740 #endif
741  }
742  else
743  {
744  ARMNN_LOG(fatal) << "Unknown model format: '" << modelFormat <<
745  "'. Please include 'caffe', 'tensorflow', 'tflite' or 'onnx'";
746  return EXIT_FAILURE;
747  }
748 }
749 
750 int RunCsvTest(const armnnUtils::CsvRow &csvRow, const std::shared_ptr<armnn::IRuntime>& runtime,
751  const bool enableProfiling, const bool enableFp16TurboMode, const double& thresholdTime,
752  const bool printIntermediate, bool enableLayerDetails = false, bool parseUnuspported = false)
753 {
754  IgnoreUnused(runtime);
755  std::string modelFormat;
756  std::string modelPath;
757  std::string inputNames;
758  std::string inputTensorShapes;
759  std::string inputTensorDataFilePaths;
760  std::string outputNames;
761  std::string inputTypes;
762  std::string outputTypes;
763  std::string dynamicBackendsPath;
764  std::string outputTensorFiles;
765 
766  size_t subgraphId = 0;
767 
768  const std::string backendsMessage = std::string("The preferred order of devices to run layers on by default. ")
769  + std::string("Possible choices: ")
771 
772  po::options_description desc("Options");
773  try
774  {
775  desc.add_options()
776  ("model-format,f", po::value(&modelFormat),
777  "armnn-binary, caffe-binary, caffe-text, tflite-binary, onnx-binary, onnx-text, tensorflow-binary or "
778  "tensorflow-text.")
779  ("model-path,m", po::value(&modelPath), "Path to model file, e.g. .armnn, .caffemodel, .prototxt, "
780  ".tflite, .onnx")
781  ("compute,c", po::value<std::vector<armnn::BackendId>>()->multitoken(),
782  backendsMessage.c_str())
783  ("dynamic-backends-path,b", po::value(&dynamicBackendsPath),
784  "Path where to load any available dynamic backend from. "
785  "If left empty (the default), dynamic backends will not be used.")
786  ("input-name,i", po::value(&inputNames), "Identifier of the input tensors in the network separated by comma.")
787  ("subgraph-number,n", po::value<size_t>(&subgraphId)->default_value(0), "Id of the subgraph to be "
788  "executed. Defaults to 0.")
789  ("input-tensor-shape,s", po::value(&inputTensorShapes),
790  "The shape of the input tensors in the network as a flat array of integers separated by comma. "
791  "Several shapes can be passed separating them by semicolon. "
792  "This parameter is optional, depending on the network.")
793  ("input-tensor-data,d", po::value(&inputTensorDataFilePaths)->default_value(""),
794  "Path to files containing the input data as a flat array separated by whitespace. "
795  "Several paths can be passed separating them by comma. If not specified, the network will be run with dummy "
796  "data (useful for profiling).")
797  ("input-type,y",po::value(&inputTypes), "The type of the input tensors in the network separated by comma. "
798  "If unset, defaults to \"float\" for all defined inputs. "
799  "Accepted values (float, int or qasymm8).")
800  ("quantize-input,q",po::bool_switch()->default_value(false),
801  "If this option is enabled, all float inputs will be quantized to qasymm8. "
802  "If unset, default to not quantized. "
803  "Accepted values (true or false)")
804  ("output-type,z",po::value(&outputTypes), "The type of the output tensors in the network separated by comma. "
805  "If unset, defaults to \"float\" for all defined outputs. "
806  "Accepted values (float, int or qasymm8).")
807  ("output-name,o", po::value(&outputNames),
808  "Identifier of the output tensors in the network separated by comma.")
809  ("dequantize-output,l",po::bool_switch()->default_value(false),
810  "If this option is enabled, all quantized outputs will be dequantized to float. "
811  "If unset, default to not get dequantized. "
812  "Accepted values (true or false)")
813  ("write-outputs-to-file,w", po::value(&outputTensorFiles),
814  "Comma-separated list of output file paths keyed with the binding-id of the output slot. "
815  "If left empty (the default), the output tensors will not be written to a file.");
816  }
817  catch (const std::exception& e)
818  {
819  // Coverity points out that default_value(...) can throw a bad_lexical_cast,
820  // and that desc.add_options() can throw boost::io::too_few_args.
821  // They really won't in any of these cases.
822  BOOST_ASSERT_MSG(false, "Caught unexpected exception");
823  ARMNN_LOG(fatal) << "Fatal internal error: " << e.what();
824  return EXIT_FAILURE;
825  }
826 
827  std::vector<const char*> clOptions;
828  clOptions.reserve(csvRow.values.size());
829  for (const std::string& value : csvRow.values)
830  {
831  clOptions.push_back(value.c_str());
832  }
833 
834  po::variables_map vm;
835  try
836  {
837  po::store(po::parse_command_line(static_cast<int>(clOptions.size()), clOptions.data(), desc), vm);
838 
839  po::notify(vm);
840 
841  CheckOptionDependencies(vm);
842  }
843  catch (const po::error& e)
844  {
845  std::cerr << e.what() << std::endl << std::endl;
846  std::cerr << desc << std::endl;
847  return EXIT_FAILURE;
848  }
849 
850  // Get the value of the switch arguments.
851  bool quantizeInput = vm["quantize-input"].as<bool>();
852  bool dequantizeOutput = vm["dequantize-output"].as<bool>();
853 
854  // Get the preferred order of compute devices.
855  std::vector<armnn::BackendId> computeDevices = vm["compute"].as<std::vector<armnn::BackendId>>();
856 
857  // Remove duplicates from the list of compute devices.
858  RemoveDuplicateDevices(computeDevices);
859 
860  // Check that the specified compute devices are valid.
861  std::string invalidBackends;
862  if (!CheckRequestedBackendsAreValid(computeDevices, armnn::Optional<std::string&>(invalidBackends)))
863  {
864  ARMNN_LOG(fatal) << "The list of preferred devices contains invalid backend IDs: "
865  << invalidBackends;
866  return EXIT_FAILURE;
867  }
868 
869  return RunTest(modelFormat, inputTensorShapes, computeDevices, dynamicBackendsPath, modelPath, inputNames,
870  inputTensorDataFilePaths, inputTypes, quantizeInput, outputTypes, outputNames, outputTensorFiles,
871  dequantizeOutput, enableProfiling, enableFp16TurboMode, thresholdTime, printIntermediate, subgraphId,
872  enableLayerDetails, parseUnuspported);
873 }
std::vector< string > m_OutputTypes
float Dequantize(QuantizedType value, float scale, int32_t offset)
Dequantize an 8-bit data type into a floating point data type.
Definition: TypesUtils.cpp:47
std::vector< string > m_OutputTensorFiles
std::unique_ptr< armnn::TensorShape > TensorShapePtr
int RunTest(const std::string &format, const std::string &inputTensorShapesStr, const vector< armnn::BackendId > &computeDevices, const std::string &dynamicBackendsPath, const std::string &path, const std::string &inputNames, const std::string &inputTensorDataFilePaths, const std::string &inputTypes, bool quantizeInput, const std::string &outputTypes, const std::string &outputNames, const std::string &outputTensorFiles, bool dequantizeOuput, bool enableProfiling, bool enableFp16TurboMode, const double &thresholdTime, bool printIntermediate, const size_t subgraphId, bool enableLayerDetails=false, bool parseUnsupported=false, const std::shared_ptr< armnn::IRuntime > &runtime=nullptr)
std::vector< TensorShapePtr > m_InputTensorShapes
QuantizationParams GetInputQuantizationParams(unsigned int inputIndex=0u) const
const std::vector< armnn::BindingPointInfo > & GetOutputBindingInfos() const
std::vector< std::string > values
Definition: CsvReader.hpp:15
int RunCsvTest(const armnnUtils::CsvRow &csvRow, const std::shared_ptr< armnn::IRuntime > &runtime, const bool enableProfiling, const bool enableFp16TurboMode, const double &thresholdTime, const bool printIntermediate, bool enableLayerDetails=false, bool parseUnuspported=false)
virtual const char * what() const noexcept override
Definition: Exceptions.cpp:32
#define ARMNN_LOG(severity)
Definition: Logging.hpp:163
BackendRegistry & BackendRegistryInstance()
void IgnoreUnused(Ts &&...)
std::string GetBackendIdsAsString() const
unsigned int GetOutputSize(unsigned int outputIndex=0u) const
boost::variant< std::vector< float >, std::vector< int >, std::vector< unsigned char > > TContainer
std::vector< std::string > m_InputBindings
std::vector< armnn::BackendId > m_ComputeDevices
std::vector< string > m_InputNames
std::vector< armnn::TensorShape > m_InputShapes
std::vector< std::string > m_OutputBindings
std::vector< armnn::BackendId > m_ComputeDevices
bool has_value() const noexcept
Definition: Optional.hpp:53
bool generateTensorData
std::vector< string > m_OutputNames
std::enable_if_t< std::is_unsigned< Source >::value &&std::is_unsigned< Dest >::value, Dest > numeric_cast(Source source)
Definition: NumericCast.hpp:33
std::pair< float, int32_t > QuantizationParams
std::vector< string > m_InputTensorDataFilePaths
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)
Base class for all ArmNN exceptions so that users can filter to just those.
Definition: Exceptions.hpp:46
std::vector< string > m_InputTypes
unsigned int GetInputSize(unsigned int inputIndex=0u) const
int MainImpl(const ExecuteNetworkParams &params, const std::shared_ptr< armnn::IRuntime > &runtime=nullptr)