diff options
Diffstat (limited to 'tests')
-rw-r--r-- | tests/ExecuteNetwork/ArmNNExecutor.cpp | 155 | ||||
-rw-r--r-- | tests/ExecuteNetwork/ExecuteNetworkParams.cpp | 2 | ||||
-rw-r--r-- | tests/ExecuteNetwork/ExecuteNetworkParams.hpp | 7 | ||||
-rw-r--r-- | tests/ExecuteNetwork/ExecuteNetworkProgramOptions.cpp | 12 | ||||
-rw-r--r-- | tests/ExecuteNetwork/FileComparisonExecutor.cpp | 95 | ||||
-rw-r--r-- | tests/ExecuteNetwork/TfliteExecutor.cpp | 96 | ||||
-rw-r--r-- | tests/InferenceModel.hpp | 8 | ||||
-rw-r--r-- | tests/NetworkExecutionUtils/NetworkExecutionUtils.hpp | 130 |
8 files changed, 408 insertions, 97 deletions
diff --git a/tests/ExecuteNetwork/ArmNNExecutor.cpp b/tests/ExecuteNetwork/ArmNNExecutor.cpp index 4518f1426f..ece3dafea4 100644 --- a/tests/ExecuteNetwork/ArmNNExecutor.cpp +++ b/tests/ExecuteNetwork/ArmNNExecutor.cpp @@ -7,27 +7,157 @@ #include "ArmNNExecutor.hpp" #include "NetworkExecutionUtils/NetworkExecutionUtils.hpp" -#include <armnn/IAsyncExecutionCallback.hpp> #include <AsyncExecutionCallback.hpp> - - +#include <armnn/IAsyncExecutionCallback.hpp> +#if defined(ARMNN_SERIALIZER) +#include <armnnSerializer/ISerializer.hpp> +#endif using namespace armnn; using namespace std::chrono; +#if defined(ARMNN_SERIALIZER) +/** + * Given a reference to an INetwork and a target directory, serialize the network to a file + * called "<timestamp>_network.armnn" + * + * @param network The network to serialize. + * @param dumpDir The target directory. + * @return the full path to the serialized file. + */ +std::string SerializeNetwork(const armnn::INetwork& network, const std::string& dumpDir) +{ + if (dumpDir.empty()) + { + throw InvalidArgumentException("An output directory must be specified."); + } + fs::path outputDirectory(dumpDir); + if (!exists(outputDirectory)) + { + throw InvalidArgumentException( + fmt::format("The specified directory does not exist: {}", outputDirectory.c_str())); + } + auto serializer(armnnSerializer::ISerializer::Create()); + // Serialize the Network + serializer->Serialize(network); + + fs::path fileName; + fileName += dumpDir; + // used to get a timestamp to name diagnostic files (the ArmNN serialized graph + // and getSupportedOperations.txt files) + timespec ts; + if (clock_gettime(CLOCK_MONOTONIC_RAW, &ts) == 0) + { + std::stringstream ss; + ss << std::to_string(ts.tv_sec) << "_" << std::to_string(ts.tv_nsec) << "_network.armnn"; + fileName += ss.str(); + } + else + { + // This is incredibly unlikely but just in case. + throw RuntimeException("clock_gettime, CLOCK_MONOTONIC_RAW returned a non zero result."); + } + + // Save serialized network to a file + std::ofstream serializedFile(fileName, std::ios::out | std::ios::binary); + auto serialized = serializer->SaveSerializedToStream(serializedFile); + if (!serialized) + { + throw RuntimeException(fmt::format("An error occurred when serializing to file %s", fileName.c_str())); + } + serializedFile.flush(); + serializedFile.close(); + return fileName; +} + +/** + * Given a reference to an optimized network and a target directory, serialize the network in .dot file format to + * a file called "<timestamp>_optimized_networkgraph.dot" + * + * @param network The network to serialize. + * @param dumpDir The target directory. + * @return the full path to the serialized file. + */ +std::string SerializeNetworkToDotFile(const armnn::IOptimizedNetwork& optimizedNetwork, const std::string& dumpDir) +{ + if (dumpDir.empty()) + { + throw InvalidArgumentException("An output directory must be specified."); + } + fs::path outputDirectory(dumpDir); + if (!exists(outputDirectory)) + { + throw InvalidArgumentException( + fmt::format("The specified directory does not exist: {}", outputDirectory.c_str())); + } + + fs::path fileName; + fileName += dumpDir; + // used to get a timestamp to name diagnostic files (the ArmNN serialized graph + // and getSupportedOperations.txt files) + timespec ts; + if (clock_gettime(CLOCK_MONOTONIC_RAW, &ts) == 0) + { + std::stringstream ss; + ss << std::to_string(ts.tv_sec) << "_" << std::to_string(ts.tv_nsec) << "_optimized_networkgraph.dot"; + fileName += ss.str(); + } + else + { + // This is incredibly unlikely but just in case. + throw RuntimeException("clock_gettime, CLOCK_MONOTONIC_RAW returned a non zero result."); + } + + // Write the network graph to a dot file. + std::ofstream fileStream; + fileStream.open(fileName, std::ofstream::out | std::ofstream::trunc); + if (!fileStream.good()) + { + throw RuntimeException(fmt::format("An error occurred when creating %s", fileName.c_str())); + } + + if (optimizedNetwork.SerializeToDot(fileStream) != armnn::Status::Success) + { + throw RuntimeException(fmt::format("An error occurred when serializing to file %s", fileName.c_str())); + } + fileStream.flush(); + fileStream.close(); + return fileName; +} +#endif + ArmNNExecutor::ArmNNExecutor(const ExecuteNetworkParams& params, armnn::IRuntime::CreationOptions runtimeOptions) -: m_Params(params) + : m_Params(params) { - runtimeOptions.m_EnableGpuProfiling = params.m_EnableProfiling; + runtimeOptions.m_EnableGpuProfiling = params.m_EnableProfiling; runtimeOptions.m_DynamicBackendsPath = params.m_DynamicBackendsPath; // Create/Get the static ArmNN Runtime. Note that the m_Runtime will be shared by all ArmNNExecutor // instances so the RuntimeOptions cannot be altered for different ArmNNExecutor instances. m_Runtime = GetRuntime(runtimeOptions); - auto parser = CreateParser(); + auto parser = CreateParser(); auto network = parser->CreateNetwork(m_Params); - auto optNet = OptimizeNetwork(network.get()); + auto optNet = OptimizeNetwork(network.get()); + // If the user has asked for detailed data write out the .armnn amd .dot files. + if (params.m_SerializeToArmNN) + { +#if defined(ARMNN_SERIALIZER) + // .armnn first. + // This could throw multiple exceptions if the directory cannot be created or the file cannot be written. + std::string targetDirectory(armnnUtils::Filesystem::CreateDirectory("/ArmNNSerializeNetwork")); + std::string fileName; + fileName = SerializeNetwork(*network, targetDirectory); + ARMNN_LOG(info) << "The pre-optimized network has been serialized to:" << fileName; + // and the .dot file. + // Most of the possible exceptions should have already occurred with the .armnn file. + fileName = + SerializeNetworkToDotFile(*optNet, targetDirectory); + ARMNN_LOG(info) << "The optimized network has been serialized to:" << fileName; +#else + ARMNN_LOG(info) << "Arm NN has not been built with ARMNN_SERIALIZER enabled."; +#endif + } m_IOInfo = GetIOInfo(optNet.get()); armnn::ProfilingDetailsMethod profilingDetailsMethod = ProfilingDetailsMethod::Undefined; @@ -97,6 +227,9 @@ ArmNNExecutor::~ArmNNExecutor() { profiler->Print(std::cout); } + + // We're finished with the network. + m_Runtime->UnloadNetwork(m_NetworkId); } void ArmNNExecutor::ExecuteAsync() @@ -176,6 +309,12 @@ void ArmNNExecutor::ExecuteAsync() void ArmNNExecutor::ExecuteSync() { + // If we've only been asked to serialize the networks, don't execute the inference. + if (m_Params.m_SerializeToArmNN) + { + ARMNN_LOG(info) << "serialize-to-armnn has been specified. No inference will be executed."; + return; + } for (size_t x = 0; x < m_Params.m_Iterations; x++) { std::shared_ptr<armnn::IProfiler> profiler = m_Runtime->GetProfiler(m_NetworkId); @@ -800,6 +939,7 @@ armnn::BindingPointInfo ArmNNExecutor::TfliteParser::GetOutputBindingPointInfo(s #if defined(ARMNN_ONNX_PARSER) +ARMNN_NO_DEPRECATE_WARN_BEGIN ArmNNExecutor::OnnxParser::OnnxParser() : m_Parser(armnnOnnxParser::IOnnxParser::Create()){} armnn::INetworkPtr ArmNNExecutor::OnnxParser::CreateNetwork(const ExecuteNetworkParams& params) @@ -843,4 +983,5 @@ armnn::BindingPointInfo ArmNNExecutor::OnnxParser::GetOutputBindingPointInfo(siz { return m_Parser->GetNetworkOutputBindingInfo(outputName); } +ARMNN_NO_DEPRECATE_WARN_END #endif diff --git a/tests/ExecuteNetwork/ExecuteNetworkParams.cpp b/tests/ExecuteNetwork/ExecuteNetworkParams.cpp index 4f5a290a0d..ccd64f531e 100644 --- a/tests/ExecuteNetwork/ExecuteNetworkParams.cpp +++ b/tests/ExecuteNetwork/ExecuteNetworkParams.cpp @@ -109,7 +109,7 @@ void ExecuteNetworkParams::ValidateParams() } } -#if defined(ARMNN_TFLITE_DELEGATE) +#if defined(ARMNN_TFLITE_DELEGATE)||defined(ARMNN_TFLITE_OPAQUE_DELEGATE) /** * A utility method that populates a DelegateOptions object from this ExecuteNetworkParams. * diff --git a/tests/ExecuteNetwork/ExecuteNetworkParams.hpp b/tests/ExecuteNetwork/ExecuteNetworkParams.hpp index ffcb4f482c..5eaae24b5e 100644 --- a/tests/ExecuteNetwork/ExecuteNetworkParams.hpp +++ b/tests/ExecuteNetwork/ExecuteNetworkParams.hpp @@ -1,5 +1,5 @@ // -// Copyright © 2022 Arm Ltd and Contributors. All rights reserved. +// Copyright © 2022, 2024 Arm Ltd and Contributors. All rights reserved. // SPDX-License-Identifier: MIT // @@ -8,7 +8,7 @@ #include <armnn/BackendId.hpp> #include <armnn/Tensor.hpp> -#if defined(ARMNN_TFLITE_DELEGATE) +#if defined(ARMNN_TFLITE_DELEGATE)||defined(ARMNN_TFLITE_OPAQUE_DELEGATE) #include <DelegateOptions.hpp> #endif @@ -67,10 +67,11 @@ struct ExecuteNetworkParams std::string m_ComparisonFile; std::vector<armnn::BackendId> m_ComparisonComputeDevices; bool m_CompareWithTflite; + bool m_SerializeToArmNN; // Ensures that the parameters for ExecuteNetwork fit together void ValidateParams(); -#if defined(ARMNN_TFLITE_DELEGATE) +#if defined(ARMNN_TFLITE_DELEGATE)||defined(ARMNN_TFLITE_OPAQUE_DELEGATE) /// A utility method that populates a DelegateOptions object from this ExecuteNetworkParams. armnnDelegate::DelegateOptions ToDelegateOptions() const; #endif diff --git a/tests/ExecuteNetwork/ExecuteNetworkProgramOptions.cpp b/tests/ExecuteNetwork/ExecuteNetworkProgramOptions.cpp index 5c1337f769..87b38c5f78 100644 --- a/tests/ExecuteNetwork/ExecuteNetworkProgramOptions.cpp +++ b/tests/ExecuteNetwork/ExecuteNetworkProgramOptions.cpp @@ -216,7 +216,8 @@ ProgramOptions::ProgramOptions() : m_CxxOptions{"ExecuteNetwork", ("m,model-path", "Path to model file, e.g. .armnn, .tflite, .onnx. " - "DEPRECATED: .pb and .prototxt model files no longer load and are deprecated.", + "DEPRECATED: .pb and .prototxt model files no longer loaded and are deprecated." + "DEPRECATED: .onnx model files will no longer loaded from 24.08 onwards.", cxxopts::value<std::string>(m_ExNetParams.m_ModelPath)); m_CxxOptions.add_options("b) Ordering") @@ -237,7 +238,8 @@ ProgramOptions::ProgramOptions() : m_CxxOptions{"ExecuteNetwork", cxxopts::value<std::string>(m_RuntimeOptions.m_DynamicBackendsPath)) ("P, thread-pool-size", - "Run the network using the Arm NN thread pool with the number of threads provided. ", + "Run the network using the Arm NN thread pool with the number of threads provided. " + "DECRECATED: The asynchronous execution interface will be removed in 24.08", cxxopts::value<size_t>(m_ExNetParams.m_ThreadPoolSize)->default_value("0")) ("d,input-tensor-data", @@ -351,6 +353,12 @@ ProgramOptions::ProgramOptions() : m_CxxOptions{"ExecuteNetwork", "Perform an per byte root mean square error calculation of the output of the inference with" " the tflite ref model.", cxxopts::value<bool>(m_ExNetParams.m_CompareWithTflite)->default_value("false") + ->implicit_value("true")) + ("serialize-to-armnn", + "Serialize the loaded network to an .armnn file. This option will also serialize the optimized network" + " in dot format. This option only works with both the TfLite parser and the Arm NN serializer" + " enabled in the build. An inference will NOT be executed.", + cxxopts::value<bool>(m_ExNetParams.m_SerializeToArmNN)->default_value("false") ->implicit_value("true")); m_CxxOptions.add_options("d) Optimization") diff --git a/tests/ExecuteNetwork/FileComparisonExecutor.cpp b/tests/ExecuteNetwork/FileComparisonExecutor.cpp index 844004bf71..26d535a958 100644 --- a/tests/ExecuteNetwork/FileComparisonExecutor.cpp +++ b/tests/ExecuteNetwork/FileComparisonExecutor.cpp @@ -1,14 +1,18 @@ // -// Copyright © 2023 Arm Ltd and Contributors. All rights reserved. +// Copyright © 2023-2024 Arm Ltd and Contributors. All rights reserved. // SPDX-License-Identifier: MIT // #include "FileComparisonExecutor.hpp" #include <NetworkExecutionUtils/NetworkExecutionUtils.hpp> +#include <armnn/Numpy.hpp> #include <algorithm> #include <ghc/filesystem.hpp> #include <iterator> +// File limit size of 1Mb +constexpr uint32_t MAX_FILE_SIZE = 1048576; + using namespace armnn; /** @@ -173,11 +177,10 @@ Tensor ReadTensorFromFile(const std::string fileName) { throw FileNotFoundException("The file \"" + fileName + "\" could not be found."); } - // The format we are reading in is based on NetworkExecutionUtils::WriteToFile. This could potentially - // be an enormous tensor. We'll limit what we can read in to 1Mb. - std::uintmax_t maxFileSize = 1048576; - std::uintmax_t fileSize = ghc::filesystem::file_size(fileName); - if (fileSize > maxFileSize) + // The format we are reading in is based on NetworkExecutionUtils::WriteToFile. + // This could potentially be an enormous tensor. + std::uintmax_t fileSize = ghc::filesystem::file_size(fileName); + if (fileSize > MAX_FILE_SIZE) { throw InvalidArgumentException("The file \"" + fileName + "\" exceeds max size of 1 Mb."); } @@ -245,6 +248,75 @@ Tensor ReadTensorFromFile(const std::string fileName) return result; } +/** + * Open the given file and read the data out of it to construct a Tensor. This could throw FileNotFoundException + * or InvalidArgumentException + * + * @param fileName the file to be read. + * @return a populated tensor. + */ +Tensor ReadTensorFromNumpyFile(const std::string fileName) +{ + if (!ghc::filesystem::exists(fileName)) + { + throw FileNotFoundException("The file \"" + fileName + "\" could not be found."); + } + // The format we are reading in is based on NetworkExecutionUtils::WriteToFile. This could potentially + // be an enormous tensor. We'll limit what we can read in to 1Mb. + std::uintmax_t fileSize = ghc::filesystem::file_size(fileName); + if (fileSize > MAX_FILE_SIZE) + { + throw InvalidArgumentException("The file \"" + fileName + "\" exceeds max size of 1 Mb."); + } + + std::ifstream ifStream(fileName, std::ios::binary); + armnnNumpy::HeaderInfo headerInfo; + armnnNumpy::Header header; + + CreateHeaderInfo(ifStream, headerInfo); + CreateHeader(ifStream, headerInfo, header); + uint32_t numElements = armnnNumpy::getNumElements(header); + + switch (armnnNumpy::getArmNNDataType(header.m_DescrString)) + { + case DataType::Float32: { + float* floats = new float[numElements]; + armnnNumpy::ReadData<float>(ifStream, floats, numElements); + TensorInfo info({ numElements, 1, 1, 1 }, DataType::Float32); + return Tensor(info, floats); + } + case DataType::Signed32: { + int* ints = new int[numElements]; + armnnNumpy::ReadData<int>(ifStream, ints, numElements); + TensorInfo info({ numElements, 1, 1, 1 }, DataType::Signed32); + return Tensor(info, ints); + } + case DataType::QSymmS8: { + int8_t* ints = new int8_t[numElements]; + armnnNumpy::ReadData<int8_t>(ifStream, ints, numElements); + TensorInfo info({ numElements, 1, 1, 1 }, DataType::QSymmS8); + return Tensor(info, ints); + } + case DataType::QAsymmS8: { + int8_t* ints = new int8_t[numElements]; + armnnNumpy::ReadData<int8_t>(ifStream, ints, numElements); + TensorInfo info({ numElements, 1, 1, 1 }, DataType::QAsymmS8); + return Tensor(info, ints); + } + case DataType::QAsymmU8: { + uint8_t* ints = new uint8_t[numElements]; + armnnNumpy::ReadData<uint8_t>(ifStream, ints, numElements); + TensorInfo info({ numElements, 1, 1, 1 }, DataType::QAsymmU8); + return Tensor(info, ints); + } + default: + throw InvalidArgumentException("The tensor data could not be read from \"" + fileName + "\""); + } + + Tensor result; + return result; +} + FileComparisonExecutor::FileComparisonExecutor(const ExecuteNetworkParams& params) : m_Params(params) {} @@ -284,7 +356,16 @@ std::vector<const void*> FileComparisonExecutor::Execute() std::vector<const void*> results; for (auto file : fileNames) { - Tensor t = ReadTensorFromFile(file); + Tensor t; + if (file.find(".npy") == std::string::npos) + { + t = ReadTensorFromFile(file); + } + else + { + t = ReadTensorFromNumpyFile(file); + } + outputs.push_back({ 0, Tensor(t.GetInfo(), t.GetMemoryArea()) }); results.push_back(t.GetMemoryArea()); } diff --git a/tests/ExecuteNetwork/TfliteExecutor.cpp b/tests/ExecuteNetwork/TfliteExecutor.cpp index 433d538b78..7a4ff55237 100644 --- a/tests/ExecuteNetwork/TfliteExecutor.cpp +++ b/tests/ExecuteNetwork/TfliteExecutor.cpp @@ -9,7 +9,9 @@ #include <tensorflow/lite/core/c/c_api.h> #include "TfliteExecutor.hpp" -#include "tensorflow/lite/kernels/kernel_util.h" +#include <tensorflow/lite/kernels/kernel_util.h> + +#include <../delegate/common/src/DelegateUtils.hpp> #include <chrono> #include <string> @@ -246,8 +248,10 @@ std::vector<const void *> TfLiteExecutor::Execute() TfLiteIntArray* outputDims = m_TfLiteInterpreter->tensor(tfLiteDelegateOutputId)->dims; // If we've been asked to write to a file then set a file output stream. Otherwise use stdout. FILE* outputTensorFile = stdout; + bool isNumpyOutput = false; if (!m_Params.m_OutputTensorFiles.empty()) { + isNumpyOutput = m_Params.m_OutputTensorFiles[outputIndex].find(".npy") != std::string::npos; outputTensorFile = fopen(m_Params.m_OutputTensorFiles[outputIndex].c_str(), "w"); if (outputTensorFile == NULL) { @@ -256,29 +260,43 @@ std::vector<const void *> TfLiteExecutor::Execute() } else { - ARMNN_LOG(info) << "Writing output " << outputIndex << "' of iteration: " << x + 1 + ARMNN_LOG(info) << "Writing output " << outputIndex << " of iteration: " << x + 1 << " to file: '" << m_Params.m_OutputTensorFiles[outputIndex] << "'"; } } + long outputSize = 1; for (unsigned int dim = 0; dim < static_cast<unsigned int>(outputDims->size); ++dim) { outputSize *= outputDims->data[dim]; } + armnn::TensorShape shape(static_cast<unsigned int>(outputDims->size), outputDims->data); + armnn::DataType dataType(GetDataType(*m_TfLiteInterpreter->tensor(tfLiteDelegateOutputId))); + std::cout << m_TfLiteInterpreter->tensor(tfLiteDelegateOutputId)->name << ": "; switch (m_TfLiteInterpreter->tensor(tfLiteDelegateOutputId)->type) { - case kTfLiteFloat32: { auto tfLiteDelegateOutputData = m_TfLiteInterpreter->typed_tensor<float>( tfLiteDelegateOutputId); results.push_back(tfLiteDelegateOutputData); - for (int i = 0; i < outputSize; ++i) + if (isNumpyOutput) + { + armnnNumpy::WriteToNumpyFile(m_Params.m_OutputTensorFiles[outputIndex], + tfLiteDelegateOutputData, + outputSize, + dataType, + shape); + } + else { - fprintf(outputTensorFile, "%f ", tfLiteDelegateOutputData[i]); + for (int i = 0; i < outputSize; ++i) + { + fprintf(outputTensorFile, "%f ", tfLiteDelegateOutputData[i]); + } } break; } @@ -287,10 +305,23 @@ std::vector<const void *> TfLiteExecutor::Execute() auto tfLiteDelegateOutputData = m_TfLiteInterpreter->typed_tensor<int32_t>( tfLiteDelegateOutputId); results.push_back(tfLiteDelegateOutputData); - for (int i = 0; i < outputSize; ++i) + + if (isNumpyOutput) + { + armnnNumpy::WriteToNumpyFile(m_Params.m_OutputTensorFiles[outputIndex], + tfLiteDelegateOutputData, + outputSize, + dataType, + shape); + } + else { - fprintf(outputTensorFile, "%d ", tfLiteDelegateOutputData[i]); + for (int i = 0; i < outputSize; ++i) + { + fprintf(outputTensorFile, "%d ", tfLiteDelegateOutputData[i]); + } } + break; } case kTfLiteUInt8: @@ -298,10 +329,23 @@ std::vector<const void *> TfLiteExecutor::Execute() auto tfLiteDelegateOutputData = m_TfLiteInterpreter->typed_tensor<uint8_t>( tfLiteDelegateOutputId); results.push_back(tfLiteDelegateOutputData); - for (int i = 0; i < outputSize; ++i) + + if (isNumpyOutput) { - fprintf(outputTensorFile, "%u ", tfLiteDelegateOutputData[i]); + armnnNumpy::WriteToNumpyFile(m_Params.m_OutputTensorFiles[outputIndex], + tfLiteDelegateOutputData, + outputSize, + dataType, + shape); } + else + { + for (int i = 0; i < outputSize; ++i) + { + fprintf(outputTensorFile, "%u ", tfLiteDelegateOutputData[i]); + } + } + break; } case kTfLiteInt8: @@ -309,10 +353,23 @@ std::vector<const void *> TfLiteExecutor::Execute() auto tfLiteDelegateOutputData = m_TfLiteInterpreter->typed_tensor<int8_t>( tfLiteDelegateOutputId); results.push_back(tfLiteDelegateOutputData); - for (int i = 0; i < outputSize; ++i) + + if (isNumpyOutput) + { + armnnNumpy::WriteToNumpyFile(m_Params.m_OutputTensorFiles[outputIndex], + tfLiteDelegateOutputData, + outputSize, + dataType, + shape); + } + else { - fprintf(outputTensorFile, "%d ", tfLiteDelegateOutputData[i]); + for (int i = 0; i < outputSize; ++i) + { + fprintf(outputTensorFile, "%d ", tfLiteDelegateOutputData[i]); + } } + break; } case kTfLiteBool: @@ -320,8 +377,21 @@ std::vector<const void *> TfLiteExecutor::Execute() auto tfLiteDelegateOutputData = m_TfLiteInterpreter->typed_tensor<bool>( tfLiteDelegateOutputId); results.push_back(tfLiteDelegateOutputData); - for (int i = 0; i < outputSize; ++i) { - fprintf(outputTensorFile, "%u ", tfLiteDelegateOutputData[i]); + + if (isNumpyOutput) + { + armnnNumpy::WriteToNumpyFile(m_Params.m_OutputTensorFiles[outputIndex], + tfLiteDelegateOutputData, + outputSize, + dataType, + shape); + } + else + { + for (int i = 0; i < outputSize; ++i) + { + fprintf(outputTensorFile, "%u ", tfLiteDelegateOutputData[i]); + } } break; } diff --git a/tests/InferenceModel.hpp b/tests/InferenceModel.hpp index c053a4429a..a1b55c0996 100644 --- a/tests/InferenceModel.hpp +++ b/tests/InferenceModel.hpp @@ -1,5 +1,5 @@ // -// Copyright © 2022-2023 Arm Ltd and Contributors. All rights reserved. +// Copyright © 2022-2024 Arm Ltd and Contributors. All rights reserved. // SPDX-License-Identifier: MIT // @@ -276,6 +276,7 @@ public: #endif #if defined(ARMNN_ONNX_PARSER) +ARMNN_NO_DEPRECATE_WARN_BEGIN template <> struct CreateNetworkImpl<armnnOnnxParser::IOnnxParser> { @@ -343,6 +344,7 @@ public: return network; } }; +ARMNN_NO_DEPRECATE_WARN_END #endif @@ -660,11 +662,11 @@ public: // Start timer to record inference time in EnqueueWorkload (in milliseconds) const auto start_time = armnn::GetTimeNow(); - +ARMNN_NO_DEPRECATE_WARN_BEGIN armnn::Status ret = m_Runtime->Execute(workingMemHandleRef, MakeInputTensors(inputContainers), MakeOutputTensors(outputContainers)); - +ARMNN_NO_DEPRECATE_WARN_END const auto duration = armnn::GetTimeDuration(start_time); // if profiling is enabled print out the results diff --git a/tests/NetworkExecutionUtils/NetworkExecutionUtils.hpp b/tests/NetworkExecutionUtils/NetworkExecutionUtils.hpp index 0df3bf5ef5..1b881ed928 100644 --- a/tests/NetworkExecutionUtils/NetworkExecutionUtils.hpp +++ b/tests/NetworkExecutionUtils/NetworkExecutionUtils.hpp @@ -1,5 +1,5 @@ // -// Copyright © 2022, 2023 Arm Ltd and Contributors. All rights reserved. +// Copyright © 2022, 2023-2024 Arm Ltd and Contributors. All rights reserved. // SPDX-License-Identifier: MIT // @@ -13,6 +13,7 @@ #include <armnn/Optional.hpp> // for Optional, EmptyOptional #include <armnn/Tensor.hpp> // for Tensor, TensorInfo #include <armnn/TypesUtils.hpp> // for Dequantize +#include <armnn/Numpy.hpp> // for Numpy #include <chrono> // for duration #include <functional> // for function #include <fstream> @@ -132,51 +133,75 @@ void PopulateTensorWithData(T* tensor, { const bool readFromFile = dataFile.has_value() && !dataFile.value().empty(); - std::ifstream inputTensorFile; if (!readFromFile) { std::fill(tensor, tensor + numElements, 0); return; } - else + + if (dataFile.value().find(".npy") == std::string::npos) { + std::ifstream inputTensorFile; inputTensorFile = std::ifstream(dataFile.value()); - } - auto parseElementFunc = GetParseElementFunc<T>(); - std::string line; - unsigned int index = 0; - while (std::getline(inputTensorFile, line)) - { - std::vector<std::string> tokens = armnn::stringUtils::StringTokenizer(line, "\t ,:"); - for (const std::string& token : tokens) + auto parseElementFunc = GetParseElementFunc<T>(); + std::string line; + unsigned int index = 0; + while (std::getline(inputTensorFile, line)) { - if (!token.empty()) // See https://stackoverflow.com/questions/10437406/ + std::vector<std::string> tokens = armnn::stringUtils::StringTokenizer(line, "\t ,:"); + for (const std::string &token: tokens) { - try + if (!token.empty()) // See https://stackoverflow.com/questions/10437406/ { - if (index == numElements) + try { - ARMNN_LOG(error) << "Number of elements: " << (index +1) << " in file \"" << dataFile.value() - << "\" does not match number of elements: " << numElements - << " for input \"" << inputName << "\"."; + if (index == numElements) + { + ARMNN_LOG(error) << "Number of elements: " << (index + 1) << " in file \"" + << dataFile.value() + << "\" does not match number of elements: " << numElements + << " for input \"" << inputName << "\"."; + } + *(tensor + index) = parseElementFunc(token); + index++; + } + catch (const std::exception &) + { + ARMNN_LOG(error) << "'" << token << "' is not a valid number. It has been ignored."; } - *(tensor + index) = parseElementFunc(token); - index++; - } - catch (const std::exception&) - { - ARMNN_LOG(error) << "'" << token << "' is not a valid number. It has been ignored."; } } } - } - if (index != numElements) + if (index != numElements) + { + ARMNN_LOG(error) << "Number of elements: " << (index + 1) << " in file \"" << inputName + << "\" does not match number of elements: " << numElements + << " for input \"" << inputName << "\"."; + } + } + else { - ARMNN_LOG(error) << "Number of elements: " << (index +1) << " in file \"" << inputName - << "\" does not match number of elements: " << numElements - << " for input \"" << inputName << "\"."; + std::ifstream ifStream(dataFile.value(), std::ifstream::binary); + + armnnNumpy::HeaderInfo headerInfo; + armnnNumpy::Header header; + + CreateHeaderInfo(ifStream, headerInfo); + CreateHeader(ifStream, headerInfo, header); + + if (!armnnNumpy::compareCTypes<T>(header.m_DescrString)) + { + ARMNN_LOG(error) << "Data type in numpy file " << inputName << " does not match expected data type."; + } + else if(numElements != armnnNumpy::getNumElements(header)) + { + ARMNN_LOG(error) << "Number of elements in numpy " << inputName + << " does not match expected number of elements."; + } + + armnnNumpy::ReadData<T>(ifStream, tensor, numElements); } } @@ -220,11 +245,22 @@ void PrintTensor(OutputWriteInfo& info, const char* formatString) if (info.m_OutputTensorFile.has_value()) { - WriteToFile(info.m_OutputTensorFile.value(), - info.m_OutputName, - array, - info.m_Tensor.GetNumElements(), - info.m_DataType); + if (info.m_OutputTensorFile.value().find(".npy") == std::string::npos) + { + WriteToFile(info.m_OutputTensorFile.value(), + info.m_OutputName, + array, + info.m_Tensor.GetNumElements(), + info.m_DataType); + } + else + { + armnnNumpy::WriteToNumpyFile(info.m_OutputTensorFile.value(), + array, + info.m_Tensor.GetNumElements(), + info.m_DataType, + info.m_Tensor.GetInfo().GetShape()); + } } if (info.m_PrintTensor) @@ -236,34 +272,6 @@ void PrintTensor(OutputWriteInfo& info, const char* formatString) } } -template <typename T> -void PrintQuantizedTensor(OutputWriteInfo& info) -{ - std::vector<float> dequantizedValues; - auto tensor = info.m_Tensor; - dequantizedValues = DequantizeArray<T>(tensor.GetMemoryArea(), - tensor.GetNumElements(), - tensor.GetInfo().GetQuantizationScale(), - tensor.GetInfo().GetQuantizationOffset()); - - if (info.m_OutputTensorFile.has_value()) - { - WriteToFile(info.m_OutputTensorFile.value(), - info.m_OutputName, - dequantizedValues.data(), - tensor.GetNumElements(), - info.m_DataType); - } - - if (info.m_PrintTensor) - { - std::for_each(dequantizedValues.begin(), dequantizedValues.end(), [&](float value) - { - printf("%f ", value); - }); - } -} - template<typename T, typename TParseElementFunc> std::vector<T> ParseArrayImpl(std::istream& stream, TParseElementFunc parseElementFunc, const char* chars = "\t ,:") { |