aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNina Drozd <nina.drozd@arm.com>2019-04-25 15:45:20 +0100
committerNina Drozd <nina.drozd@arm.com>2019-05-03 14:43:50 +0100
commit59e15b00ea51fee4baeea750dc11ab1952dfab1d (patch)
tree97e6c9230bf153d404ad3c3e0e285acdb0b6232d
parent8b194fbe79d44cba566ad8b508d1c8902987ae3c (diff)
downloadarmnn-59e15b00ea51fee4baeea750dc11ab1952dfab1d.tar.gz
IVGCVSW-2834 Add dynamic quantization via datasets
* Add QuantizationDataSet class for quantization data parsed from CSV file * Add QuantizationInput for retrieving quantization data for each layer ID * Add unit tests for command line processor and QuantizationDataSet Change-Id: Iaf0a747b5f25a59a766ac04f7158e8cb7909d179 Signed-off-by: Nina Drozd <nina.drozd@arm.com>
-rw-r--r--CMakeLists.txt41
-rw-r--r--include/armnn/INetworkQuantizer.hpp4
-rw-r--r--src/armnn/NetworkQuantizer.cpp5
-rw-r--r--src/armnn/NetworkQuantizer.hpp1
-rw-r--r--src/armnnQuantizer/ArmNNQuantizerMain.cpp43
-rw-r--r--src/armnnQuantizer/CommandLineProcessor.cpp5
-rw-r--r--src/armnnQuantizer/CommandLineProcessor.hpp8
-rw-r--r--src/armnnQuantizer/QuantizationDataSet.cpp165
-rw-r--r--src/armnnQuantizer/QuantizationDataSet.hpp55
-rw-r--r--src/armnnQuantizer/QuantizationInput.cpp103
-rw-r--r--src/armnnQuantizer/QuantizationInput.hpp54
-rw-r--r--src/armnnQuantizer/test/QuantizationDataSetTests.cpp144
12 files changed, 605 insertions, 23 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 6dc1a1f02d..34dbd91526 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -138,12 +138,37 @@ if(BUILD_ARMNN_QUANTIZER)
message(ERROR, "In order to build the ArmNN Quantization Tool you must set BUILD_ARMNN_SERIALIZER = YES")
endif()
- add_executable_ex(ArmnnQuantizer
+ set(armnn_quantizer_sources)
+ list(APPEND armnn_quantizer_sources
+ src/armnnQuantizer/QuantizationDataSet.hpp
+ src/armnnQuantizer/QuantizationDataSet.cpp
+ src/armnnQuantizer/QuantizationInput.hpp
+ src/armnnQuantizer/QuantizationInput.cpp
src/armnnQuantizer/CommandLineProcessor.hpp
src/armnnQuantizer/CommandLineProcessor.cpp
+ )
+
+ add_library_ex(armnnQuantizer SHARED ${armnn_quantizer_sources})
+
+ target_include_directories(armnnQuantizer PRIVATE include/armnnDeserializer)
+ target_include_directories(armnnQuantizer PRIVATE src/armnnUtils)
+ target_include_directories(armnnQuantizer PRIVATE src/armnn)
+
+ include_directories(SYSTEM "${FLATBUFFERS_INCLUDE_PATH}")
+ set_target_properties(armnnQuantizer PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR})
+
+ target_link_libraries(armnnQuantizer
+ ${Boost_SYSTEM_LIBRARY}
+ ${Boost_PROGRAM_OPTIONS_LIBRARY}
+ ${Boost_FILESYSTEM_LIBRARY}
+ ${Boost_LOG_LIBRARY}
+ ${Boost_THREAD_LIBRARY} )
+
+ add_executable_ex(ArmnnQuantizer
src/armnnQuantizer/ArmNNQuantizerMain.cpp)
target_include_directories(ArmnnQuantizer PRIVATE include/armnnDeserializer)
+ target_include_directories(ArmnnQuantizer PRIVATE src/armnn)
target_link_libraries(ArmnnQuantizer
${Boost_SYSTEM_LIBRARY}
@@ -153,8 +178,10 @@ if(BUILD_ARMNN_QUANTIZER)
${Boost_THREAD_LIBRARY} )
target_link_libraries(ArmnnQuantizer
+ armnnQuantizer
armnnSerializer
armnn
+ armnnUtils
${FLATBUFFERS_LIBRARY})
if(Threads_FOUND AND (NOT ("${CMAKE_SYSTEM_NAME}" STREQUAL Android)))
@@ -554,6 +581,12 @@ if(BUILD_UNIT_TESTS)
)
endif()
+ if(BUILD_ARMNN_QUANTIZER)
+ list(APPEND unittest_sources
+ src/armnnQuantizer/test/QuantizationDataSetTests.cpp
+ )
+ endif()
+
if(BUILD_ARMNN_SERIALIZER)
enable_language(ASM)
list(APPEND unittest_sources
@@ -652,6 +685,12 @@ if(BUILD_UNIT_TESTS)
target_link_libraries(UnitTests armnnSerializer)
endif()
+ if(BUILD_ARMNN_QUANTIZER)
+ target_include_directories(UnitTests SYSTEM PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/src/armnnQuantizer)
+ target_include_directories(UnitTests SYSTEM PRIVATE "${FLATBUFFERS_INCLUDE_PATH}")
+ target_link_libraries(UnitTests armnnQuantizer armnnSerializer)
+ endif()
+
if(BUILD_ONNX_PARSER)
target_link_libraries(UnitTests armnnOnnxParser)
endif()
diff --git a/include/armnn/INetworkQuantizer.hpp b/include/armnn/INetworkQuantizer.hpp
index 54c1c889d3..89548d1057 100644
--- a/include/armnn/INetworkQuantizer.hpp
+++ b/include/armnn/INetworkQuantizer.hpp
@@ -7,6 +7,7 @@
#include <armnn/INetwork.hpp>
#include <armnn/Types.hpp>
+#include <armnn/Tensor.hpp>
namespace armnn
{
@@ -37,6 +38,9 @@ public:
/// Overrides the default quantization values for the input layer with the given id
virtual void OverrideInputRange(LayerBindingId layerId, float min, float max) = 0;
+ /// Refine input network with a set of refinement data for specified LayerBindingId
+ virtual void Refine(const InputTensors& inputTensors) = 0;
+
/// Extract final quantized network
virtual INetworkPtr ExportNetwork() = 0;
diff --git a/src/armnn/NetworkQuantizer.cpp b/src/armnn/NetworkQuantizer.cpp
index f577aea00e..4692a6803f 100644
--- a/src/armnn/NetworkQuantizer.cpp
+++ b/src/armnn/NetworkQuantizer.cpp
@@ -49,6 +49,11 @@ void NetworkQuantizer::OverrideInputRange(LayerBindingId layerId, float min, flo
VisitLayers(inputLayers, overrideInputRangeVisitor);
}
+void NetworkQuantizer::Refine(const InputTensors& inputTensors)
+{
+ //Implementation in a following commit
+}
+
INetworkPtr NetworkQuantizer::ExportNetwork()
{
const Graph& graph = boost::polymorphic_downcast<const Network*>(m_InputNetwork)->GetGraph().TopologicalSort();
diff --git a/src/armnn/NetworkQuantizer.hpp b/src/armnn/NetworkQuantizer.hpp
index 5e93f70290..4f6359f36d 100644
--- a/src/armnn/NetworkQuantizer.hpp
+++ b/src/armnn/NetworkQuantizer.hpp
@@ -21,6 +21,7 @@ public:
: m_InputNetwork(inputNetwork), m_Options(options) {}
void OverrideInputRange(LayerBindingId layerId, float min, float max) override;
+ void Refine(const InputTensors& inputTensors) override;
INetworkPtr ExportNetwork() override;
private:
diff --git a/src/armnnQuantizer/ArmNNQuantizerMain.cpp b/src/armnnQuantizer/ArmNNQuantizerMain.cpp
index 9ac8966753..103597a72d 100644
--- a/src/armnnQuantizer/ArmNNQuantizerMain.cpp
+++ b/src/armnnQuantizer/ArmNNQuantizerMain.cpp
@@ -7,6 +7,8 @@
#include <armnnDeserializer/IDeserializer.hpp>
#include <armnn/INetworkQuantizer.hpp>
#include <armnnSerializer/ISerializer.hpp>
+#include "QuantizationDataSet.hpp"
+#include "QuantizationInput.hpp"
#include <algorithm>
#include <fstream>
@@ -41,31 +43,32 @@ int main(int argc, char* argv[])
armnn::INetworkPtr network = parser->CreateNetworkFromBinary(binaryContent);
armnn::INetworkQuantizerPtr quantizer = armnn::INetworkQuantizer::Create(network.get(), quantizerOptions);
- std::string csvFileName = cmdline.GetCsvFileName();
- if (csvFileName != "")
+ if (cmdline.HasQuantizationData())
{
- // Call the Quantizer::Refine() function which will update the min/max ranges for the quantize constants
- std::ifstream csvFileStream(csvFileName);
- std::string line;
- std::string csvDirectory = cmdline.GetCsvFileDirectory();
- while(getline(csvFileStream, line))
+ armnnQuantizer::QuantizationDataSet dataSet = cmdline.GetQuantizationDataSet();
+ if (!dataSet.IsEmpty())
{
- std::istringstream s(line);
- std::vector<std::string> row;
- std::string entry;
- while(getline(s, entry, ','))
+ // Get the Input Tensor Infos
+ armnnQuantizer::InputLayerVisitor inputLayerVisitor;
+ network->Accept(inputLayerVisitor);
+
+ for(armnnQuantizer::QuantizationInput quantizationInput : dataSet)
{
- entry.erase(std::remove(entry.begin(), entry.end(), ' '), entry.end());
- entry.erase(std::remove(entry.begin(), entry.end(), '"'), entry.end());
- row.push_back(entry);
+ armnn::InputTensors inputTensors;
+ std::vector<std::vector<float>> inputData(quantizationInput.GetNumberOfInputs());
+ std::vector<armnn::LayerBindingId> layerBindingIds = quantizationInput.GetLayerBindingIds();
+ unsigned int count = 0;
+ for (armnn::LayerBindingId layerBindingId : quantizationInput.GetLayerBindingIds())
+ {
+ armnn::TensorInfo tensorInfo = inputLayerVisitor.GetTensorInfo(layerBindingId);
+ inputData[count] = quantizationInput.GetDataForEntry(layerBindingId);
+ armnn::ConstTensor inputTensor(tensorInfo, inputData[count].data());
+ inputTensors.push_back(std::make_pair(layerBindingId, inputTensor));
+ count++;
+ }
+ quantizer->Refine(inputTensors);
}
- std::string rawFileName = cmdline.GetCsvFileDirectory() + "/" + row[2];
- // passId: row[0]
- // bindingId: row[1]
- // rawFileName: file contains the RAW input tensor data
- // LATER: Quantizer::Refine() function will be called with those arguments when it is implemented
}
- csvFileStream.close();
}
armnn::INetworkPtr quantizedNetwork = quantizer->ExportNetwork();
diff --git a/src/armnnQuantizer/CommandLineProcessor.cpp b/src/armnnQuantizer/CommandLineProcessor.cpp
index 16afe289f6..4f0d989d3f 100644
--- a/src/armnnQuantizer/CommandLineProcessor.cpp
+++ b/src/armnnQuantizer/CommandLineProcessor.cpp
@@ -149,6 +149,9 @@ bool CommandLineProcessor::ProcessCommandLine(int argc, char* argv[])
boost::filesystem::path csvFilePath(m_CsvFileName);
m_CsvFileDirectory = csvFilePath.parent_path().c_str();
}
+
+ // If CSV file is defined, create a QuantizationDataSet for specified CSV file.
+ m_QuantizationDataSet = QuantizationDataSet(m_CsvFileName);
}
if (!armnnQuantizer::ValidateOutputDirectory(m_OutputDirectory))
@@ -158,7 +161,7 @@ bool CommandLineProcessor::ProcessCommandLine(int argc, char* argv[])
std::string output(m_OutputDirectory);
output.append(m_OutputFileName);
-
+
if (boost::filesystem::exists(output))
{
std::cerr << "Output file [" << output << "] already exists" << std::endl;
diff --git a/src/armnnQuantizer/CommandLineProcessor.hpp b/src/armnnQuantizer/CommandLineProcessor.hpp
index 7e366a7664..ae39abb603 100644
--- a/src/armnnQuantizer/CommandLineProcessor.hpp
+++ b/src/armnnQuantizer/CommandLineProcessor.hpp
@@ -6,6 +6,8 @@
#include <string>
#include <iostream>
+#include <vector>
+#include "QuantizationDataSet.hpp"
namespace armnnQuantizer
{
@@ -31,13 +33,17 @@ public:
std::string GetOutputDirectoryName() {return m_OutputDirectory;}
std::string GetOutputFileName() {return m_OutputFileName;}
std::string GetQuantizationScheme() {return m_QuantizationScheme;}
-private:
+ QuantizationDataSet GetQuantizationDataSet() {return m_QuantizationDataSet;}
+ bool HasQuantizationData() {return !m_QuantizationDataSet.IsEmpty();}
+
+protected:
std::string m_InputFileName;
std::string m_CsvFileName;
std::string m_CsvFileDirectory;
std::string m_OutputDirectory;
std::string m_OutputFileName;
std::string m_QuantizationScheme;
+ QuantizationDataSet m_QuantizationDataSet;
};
} // namespace armnnQuantizer
diff --git a/src/armnnQuantizer/QuantizationDataSet.cpp b/src/armnnQuantizer/QuantizationDataSet.cpp
new file mode 100644
index 0000000000..d225883854
--- /dev/null
+++ b/src/armnnQuantizer/QuantizationDataSet.cpp
@@ -0,0 +1,165 @@
+//
+// Copyright © 2017 Arm Ltd. All rights reserved.
+// SPDX-License-Identifier: MIT
+//
+
+#include "QuantizationDataSet.hpp"
+#include "CsvReader.hpp"
+
+#define BOOST_FILESYSTEM_NO_DEPRECATED
+
+#include <boost/filesystem/operations.hpp>
+#include <boost/filesystem/path.hpp>
+
+namespace armnnQuantizer
+{
+
+QuantizationDataSet::QuantizationDataSet()
+{
+}
+
+QuantizationDataSet::QuantizationDataSet(const std::string csvFilePath):
+ m_QuantizationInputs(),
+ m_CsvFilePath(csvFilePath)
+{
+ ParseCsvFile();
+}
+
+void AddInputData(unsigned int passId,
+ armnn::LayerBindingId bindingId,
+ const std::string& inputFilePath,
+ std::map<unsigned int, QuantizationInput>& passIdToQuantizationInput)
+{
+ auto iterator = passIdToQuantizationInput.find(passId);
+ if (iterator == passIdToQuantizationInput.end())
+ {
+ QuantizationInput input(passId, bindingId, inputFilePath);
+ passIdToQuantizationInput.emplace(passId, input);
+ }
+ else
+ {
+ auto existingQuantizationInput = iterator->second;
+ existingQuantizationInput.AddEntry(bindingId, inputFilePath);
+ }
+}
+
+QuantizationDataSet::~QuantizationDataSet()
+{
+}
+
+void InputLayerVisitor::VisitInputLayer(const armnn::IConnectableLayer* layer,
+ armnn::LayerBindingId id,
+ const char* name)
+{
+ m_TensorInfos.emplace(id, layer->GetInputSlot(0).GetConnection()->GetTensorInfo());
+}
+
+armnn::TensorInfo InputLayerVisitor::GetTensorInfo(armnn::LayerBindingId layerBindingId)
+{
+ auto iterator = m_TensorInfos.find(layerBindingId);
+ if (iterator != m_TensorInfos.end())
+ {
+ return m_TensorInfos.at(layerBindingId);
+ }
+ else
+ {
+ throw armnn::Exception("Could not retrieve tensor info for binding ID " + std::to_string(layerBindingId));
+ }
+}
+
+
+unsigned int GetPassIdFromCsvRow(std::vector<armnnUtils::CsvRow> csvRows, unsigned int rowIndex)
+{
+ unsigned int passId;
+ try
+ {
+ passId = static_cast<unsigned int>(std::stoi(csvRows[rowIndex].values[0]));
+ }
+ catch (std::invalid_argument)
+ {
+ throw armnn::ParseException("Pass ID [" + csvRows[rowIndex].values[0] + "]" +
+ " is not correct format on CSV row " + std::to_string(rowIndex));
+ }
+ return passId;
+}
+
+armnn::LayerBindingId GetBindingIdFromCsvRow(std::vector<armnnUtils::CsvRow> csvRows, unsigned int rowIndex)
+{
+ armnn::LayerBindingId bindingId;
+ try
+ {
+ bindingId = std::stoi(csvRows[rowIndex].values[1]);
+ }
+ catch (std::invalid_argument)
+ {
+ throw armnn::ParseException("Binding ID [" + csvRows[rowIndex].values[0] + "]" +
+ " is not correct format on CSV row " + std::to_string(rowIndex));
+ }
+ return bindingId;
+}
+
+std::string GetFileNameFromCsvRow(std::vector<armnnUtils::CsvRow> csvRows, unsigned int rowIndex)
+{
+ std::string fileName = csvRows[rowIndex].values[2];
+
+ if (!boost::filesystem::exists(fileName))
+ {
+ throw armnn::ParseException("File [ " + fileName + "] provided on CSV row " + std::to_string(rowIndex) +
+ " does not exist.");
+ }
+
+ if (fileName.empty())
+ {
+ throw armnn::ParseException("Filename cannot be empty on CSV row " + std::to_string(rowIndex));
+ }
+ return fileName;
+}
+
+
+void QuantizationDataSet::ParseCsvFile()
+{
+ std::map<unsigned int, QuantizationInput> passIdToQuantizationInput;
+ armnnUtils::CsvReader reader;
+
+ if (m_CsvFilePath == "")
+ {
+ throw armnn::Exception("CSV file not specified.");
+ }
+
+ // Parse CSV file and extract data
+ std::vector<armnnUtils::CsvRow> csvRows = reader.ParseFile(m_CsvFilePath);
+ if (csvRows.empty())
+ {
+ throw armnn::Exception("CSV file [" + m_CsvFilePath + "] is empty.");
+ }
+
+ for (unsigned int i = 0; i < csvRows.size(); ++i)
+ {
+ if (csvRows[i].values.size() != 3)
+ {
+ throw armnn::Exception("CSV file [" + m_CsvFilePath + "] does not have correct number of entries " +
+ "on line " + std::to_string(i) + ". Expected 3 entries " +
+ "but was " + std::to_string(csvRows[i].values.size()));
+ }
+
+ unsigned int passId = GetPassIdFromCsvRow(csvRows, i);
+ armnn::LayerBindingId bindingId = GetBindingIdFromCsvRow(csvRows, i);
+ std::string rawFileName = GetFileNameFromCsvRow(csvRows, i);
+
+ AddInputData(passId, bindingId, rawFileName, passIdToQuantizationInput);
+ }
+
+ if (passIdToQuantizationInput.empty())
+ {
+ throw armnn::Exception("Could not parse CSV file.");
+ }
+
+ // Once all entries in CSV file are parsed successfully and QuantizationInput map is populated, populate
+ // QuantizationInputs iterator for easier access and clear the map
+ for (auto itr = passIdToQuantizationInput.begin(); itr != passIdToQuantizationInput.end(); ++itr)
+ {
+ m_QuantizationInputs.emplace_back(itr->second);
+ }
+}
+
+}
diff --git a/src/armnnQuantizer/QuantizationDataSet.hpp b/src/armnnQuantizer/QuantizationDataSet.hpp
new file mode 100644
index 0000000000..3a97630ccf
--- /dev/null
+++ b/src/armnnQuantizer/QuantizationDataSet.hpp
@@ -0,0 +1,55 @@
+//
+// Copyright © 2017 Arm Ltd. All rights reserved.
+// SPDX-License-Identifier: MIT
+//
+
+#pragma once
+
+#include <map>
+#include "QuantizationInput.hpp"
+#include "armnn/LayerVisitorBase.hpp"
+#include "armnn/Tensor.hpp"
+
+namespace armnnQuantizer
+{
+
+/// QuantizationDataSet is a structure which is created after parsing a quantization CSV file.
+/// It contains records of filenames which contain refinement data per pass ID for binding ID.
+class QuantizationDataSet
+{
+ using QuantizationInputs = std::vector<armnnQuantizer::QuantizationInput>;
+public:
+
+ using iterator = QuantizationInputs::iterator;
+ using const_iterator = QuantizationInputs::const_iterator;
+
+ QuantizationDataSet();
+ QuantizationDataSet(std::string csvFilePath);
+ ~QuantizationDataSet();
+ bool IsEmpty() const {return m_QuantizationInputs.empty();}
+
+ iterator begin() { return m_QuantizationInputs.begin(); }
+ iterator end() { return m_QuantizationInputs.end(); }
+ const_iterator begin() const { return m_QuantizationInputs.begin(); }
+ const_iterator end() const { return m_QuantizationInputs.end(); }
+ const_iterator cbegin() const { return m_QuantizationInputs.cbegin(); }
+ const_iterator cend() const { return m_QuantizationInputs.cend(); }
+
+private:
+ void ParseCsvFile();
+
+ QuantizationInputs m_QuantizationInputs;
+ std::string m_CsvFilePath;
+};
+
+/// Visitor class implementation to gather the TensorInfo for LayerBindingID for creation of ConstTensor for Refine.
+class InputLayerVisitor : public armnn::LayerVisitorBase<armnn::VisitorNoThrowPolicy>
+{
+public:
+ void VisitInputLayer(const armnn::IConnectableLayer *layer, armnn::LayerBindingId id, const char* name);
+ armnn::TensorInfo GetTensorInfo(armnn::LayerBindingId);
+private:
+ std::map<armnn::LayerBindingId, armnn::TensorInfo> m_TensorInfos;
+};
+
+} // namespace armnnQuantizer \ No newline at end of file
diff --git a/src/armnnQuantizer/QuantizationInput.cpp b/src/armnnQuantizer/QuantizationInput.cpp
new file mode 100644
index 0000000000..bb7aff1c2c
--- /dev/null
+++ b/src/armnnQuantizer/QuantizationInput.cpp
@@ -0,0 +1,103 @@
+//
+// Copyright © 2017 Arm Ltd. All rights reserved.
+// SPDX-License-Identifier: MIT
+//
+
+#include "QuantizationInput.hpp"
+
+#include <iostream>
+#include <fstream>
+#include <cstring>
+#include "armnn/Exceptions.hpp"
+
+namespace armnnQuantizer
+{
+
+QuantizationInput::QuantizationInput(const unsigned int passId,
+ const armnn::LayerBindingId bindingId,
+ const std::string fileName):
+ m_PassId(passId)
+{
+ m_LayerBindingIdToFileName.emplace(bindingId, fileName);
+}
+
+QuantizationInput::QuantizationInput(const QuantizationInput& other)
+{
+ m_PassId = other.GetPassId();
+ m_LayerBindingIdToFileName.clear();
+ for (armnn::LayerBindingId bindingId : other.GetLayerBindingIds())
+ {
+ std::string filename = other.GetFileName(bindingId);
+ AddEntry(bindingId, filename);
+ }
+}
+
+void QuantizationInput::AddEntry(const armnn::LayerBindingId bindingId, const std::string fileName)
+{
+ m_LayerBindingIdToFileName.emplace(bindingId, fileName);
+}
+
+std::vector<float> QuantizationInput::GetDataForEntry(const armnn::LayerBindingId bindingId) const
+{
+ if (m_LayerBindingIdToFileName.at(bindingId).empty())
+ {
+ throw armnn::Exception("Layer binding ID not found");
+ }
+
+ std::string fileName = m_LayerBindingIdToFileName.at(bindingId);
+ std::ifstream in(fileName.c_str(), std::ifstream::binary);
+ if (!in.is_open())
+ {
+ throw armnn::Exception("Failed to open input tensor file " + fileName);
+ }
+
+ std::string line;
+ std::vector<float> values;
+ char* pEnd;
+
+ while (std::getline(in, line, ' '))
+ {
+ values.emplace_back(std::strtof(line.c_str(), &pEnd));
+ }
+ return values;
+}
+
+std::vector<armnn::LayerBindingId> QuantizationInput::GetLayerBindingIds() const
+{
+ std::vector<armnn::LayerBindingId> layerBindingIDs;
+
+ for (auto iterator = m_LayerBindingIdToFileName.begin(); iterator != m_LayerBindingIdToFileName.end(); ++iterator)
+ {
+ layerBindingIDs.emplace_back(iterator->first);
+ }
+ return layerBindingIDs;
+}
+
+unsigned long QuantizationInput::GetNumberOfInputs() const
+{
+ return m_LayerBindingIdToFileName.size();
+}
+
+unsigned int QuantizationInput::GetPassId() const
+{
+ return m_PassId;
+}
+
+std::string QuantizationInput::GetFileName(const armnn::LayerBindingId bindingId) const
+{
+ auto iterator = m_LayerBindingIdToFileName.find(bindingId);
+ if (iterator != m_LayerBindingIdToFileName.end())
+ {
+ return m_LayerBindingIdToFileName.at(bindingId);
+ }
+ else
+ {
+ throw armnn::Exception("Could not retrieve filename for binding ID " + std::to_string(bindingId));
+ }
+}
+
+QuantizationInput::~QuantizationInput() noexcept
+{
+}
+
+} \ No newline at end of file
diff --git a/src/armnnQuantizer/QuantizationInput.hpp b/src/armnnQuantizer/QuantizationInput.hpp
new file mode 100644
index 0000000000..ebabdd704f
--- /dev/null
+++ b/src/armnnQuantizer/QuantizationInput.hpp
@@ -0,0 +1,54 @@
+//
+// Copyright © 2017 Arm Ltd. All rights reserved.
+// SPDX-License-Identifier: MIT
+//
+
+#pragma once
+
+#include <map>
+#include <armnn/Types.hpp>
+#include <armnn/INetworkQuantizer.hpp>
+
+namespace armnnQuantizer
+{
+
+/// QuantizationInput for specific pass ID, can list a corresponding raw data file for each LayerBindingId.
+class QuantizationInput
+{
+public:
+
+ /// Constructor for QuantizationInput
+ QuantizationInput(const unsigned int passId,
+ const armnn::LayerBindingId bindingId,
+ const std::string fileName);
+
+ QuantizationInput(const QuantizationInput& other);
+
+ // Add binding ID to image tensor filepath entry
+ void AddEntry(const armnn::LayerBindingId bindingId, const std::string fileName);
+
+ // Retrieve tensor data for entry with provided binding ID
+ std::vector<float> GetDataForEntry(const armnn::LayerBindingId bindingId) const;
+
+ /// Retrieve Layer Binding IDs for this QuantizationInput.
+ std::vector<armnn::LayerBindingId> GetLayerBindingIds() const;
+
+ /// Get number of inputs for this QuantizationInput.
+ unsigned long GetNumberOfInputs() const;
+
+ /// Retrieve Pass ID for this QuantizationInput.
+ unsigned int GetPassId() const;
+
+ /// Retrieve filename path for specified Layer Binding ID.
+ std::string GetFileName(const armnn::LayerBindingId bindingId) const;
+
+ /// Destructor
+ ~QuantizationInput() noexcept;
+
+private:
+ unsigned int m_PassId;
+ std::map<armnn::LayerBindingId, std::string> m_LayerBindingIdToFileName;
+
+};
+
+} \ No newline at end of file
diff --git a/src/armnnQuantizer/test/QuantizationDataSetTests.cpp b/src/armnnQuantizer/test/QuantizationDataSetTests.cpp
new file mode 100644
index 0000000000..2b46aae26e
--- /dev/null
+++ b/src/armnnQuantizer/test/QuantizationDataSetTests.cpp
@@ -0,0 +1,144 @@
+//
+// Copyright © 2017 Arm Ltd. All rights reserved.
+// SPDX-License-Identifier: MIT
+//
+
+#include <boost/test/unit_test.hpp>
+
+#include "../QuantizationDataSet.hpp"
+#include <iostream>
+#include <fstream>
+#include <vector>
+#include <map>
+
+#define BOOST_FILESYSTEM_NO_DEPRECATED
+
+#include <boost/filesystem/operations.hpp>
+#include <boost/filesystem/fstream.hpp>
+#include <boost/filesystem/path.hpp>
+#include <boost/optional/optional.hpp>
+
+
+using namespace armnnQuantizer;
+
+struct CsvTestHelper {
+
+ CsvTestHelper()
+ {
+ BOOST_TEST_MESSAGE("setup fixture");
+ }
+
+ ~CsvTestHelper()
+ {
+ BOOST_TEST_MESSAGE("teardown fixture");
+ TearDown();
+ }
+
+ std::string CreateTempCsvFile(std::map<int, std::vector<float>> csvData)
+ {
+ boost::filesystem::path fileDir = boost::filesystem::temp_directory_path();
+ boost::filesystem::path p{fileDir / boost::filesystem::unique_path("%%%%-%%%%-%%%%.csv")};
+
+ boost::filesystem::path tensorInput1{fileDir / boost::filesystem::unique_path("input_0_0.raw")};
+ boost::filesystem::path tensorInput2{fileDir / boost::filesystem::unique_path("input_1_0.raw")};
+ boost::filesystem::path tensorInput3{fileDir / boost::filesystem::unique_path("input_2_0.raw")};
+
+ try
+ {
+ boost::filesystem::ofstream ofs{p};
+
+ boost::filesystem::ofstream ofs1{tensorInput1};
+ boost::filesystem::ofstream ofs2{tensorInput2};
+ boost::filesystem::ofstream ofs3{tensorInput3};
+
+
+ for(auto entry : csvData.at(0))
+ {
+ ofs1 << entry << " ";
+ }
+ for(auto entry : csvData.at(1))
+ {
+ ofs2 << entry << " ";
+ }
+ for(auto entry : csvData.at(2))
+ {
+ ofs3 << entry << " ";
+ }
+
+ ofs << "0, 0, " << tensorInput1.c_str() << std::endl;
+ ofs << "2, 0, " << tensorInput3.c_str() << std::endl;
+ ofs << "1, 0, " << tensorInput2.c_str() << std::endl;
+
+ ofs.close();
+ ofs1.close();
+ ofs2.close();
+ ofs3.close();
+ }
+ catch (std::exception &e)
+ {
+ std::cerr << "Unable to write to file at location [" << p.c_str() << "] : " << e.what() << std::endl;
+ BOOST_TEST(false);
+ }
+
+ m_CsvFile = p;
+ return p.string();
+ }
+
+ void TearDown()
+ {
+ RemoveCsvFile();
+ }
+
+ void RemoveCsvFile()
+ {
+ if (m_CsvFile)
+ {
+ try
+ {
+ boost::filesystem::remove(*m_CsvFile);
+ }
+ catch (std::exception &e)
+ {
+ std::cerr << "Unable to delete file [" << *m_CsvFile << "] : " << e.what() << std::endl;
+ BOOST_TEST(false);
+ }
+ }
+ }
+
+ boost::optional<boost::filesystem::path> m_CsvFile;
+};
+
+
+BOOST_AUTO_TEST_SUITE(QuantizationDataSetTests)
+
+BOOST_FIXTURE_TEST_CASE(CheckDataSet, CsvTestHelper)
+{
+
+ std::map<int, std::vector<float>> csvData;
+ csvData.insert(std::pair<int, std::vector<float>>(0, { 0.111111f, 0.222222f, 0.333333f }));
+ csvData.insert(std::pair<int, std::vector<float>>(1, { 0.444444f, 0.555555f, 0.666666f }));
+ csvData.insert(std::pair<int, std::vector<float>>(2, { 0.777777f, 0.888888f, 0.999999f }));
+
+ std::string myCsvFile = CsvTestHelper::CreateTempCsvFile(csvData);
+ QuantizationDataSet dataSet(myCsvFile);
+ BOOST_TEST(!dataSet.IsEmpty());
+
+ int csvRow = 0;
+ for(armnnQuantizer::QuantizationInput input : dataSet)
+ {
+ BOOST_TEST(input.GetPassId() == csvRow);
+
+ BOOST_TEST(input.GetLayerBindingIds().size() == 1);
+ BOOST_TEST(input.GetLayerBindingIds()[0] == 0);
+ BOOST_TEST(input.GetDataForEntry(0).size() == 3);
+
+ // Check that QuantizationInput data for binding ID 0 corresponds to float values
+ // used for populating the CSV file using by QuantizationDataSet
+ BOOST_TEST(input.GetDataForEntry(0).at(0) == csvData.at(csvRow).at(0));
+ BOOST_TEST(input.GetDataForEntry(0).at(1) == csvData.at(csvRow).at(1));
+ BOOST_TEST(input.GetDataForEntry(0).at(2) == csvData.at(csvRow).at(2));
+ ++csvRow;
+ }
+}
+
+BOOST_AUTO_TEST_SUITE_END(); \ No newline at end of file