aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatteo Martincigh <matteo.martincigh@arm.com>2019-02-07 17:51:09 +0000
committerMatteo Martincigh <matteo.martincigh@arm.com>2019-02-08 12:23:05 +0000
commita8d572dc48f47e66cd7abd6ad9b2d3a0f40ea94b (patch)
tree5de7809a8fbc19d6d2a940a51a982bd633156945
parente0a4ad8a8e6ef271883e8029985eeab16d838972 (diff)
downloadarmnn-a8d572dc48f47e66cd7abd6ad9b2d3a0f40ea94b.tar.gz
IVGCVSW-2607 Implement Input range override mechanism
* Added the OverrideInputRange method to the Quantizer API * Created OverrideInputRangeVisitor to implement the override mechanism * Moved the quantizer utility functions to the new NetworkQuantizerUtils files * Moved the map of quantization ranges out of the StaticRangeVisitor and into the NetworkQuantizer * Added unit tests * Code refactoring and cleanup Change-Id: I9c1d006c1b6a35fbc04584a832fbe489f8f9276d Signed-off-by: Matteo Martincigh <matteo.martincigh@arm.com>
-rw-r--r--CMakeLists.txt4
-rw-r--r--include/armnn/INetworkQuantizer.hpp8
-rw-r--r--src/armnn/NetworkQuantizer.cpp28
-rw-r--r--src/armnn/NetworkQuantizer.hpp11
-rw-r--r--src/armnn/NetworkQuantizerUtils.cpp61
-rw-r--r--src/armnn/NetworkQuantizerUtils.hpp56
-rw-r--r--src/armnn/OverrideInputRangeVisitor.cpp47
-rw-r--r--src/armnn/OverrideInputRangeVisitor.hpp45
-rw-r--r--src/armnn/QuantizerVisitor.cpp102
-rw-r--r--src/armnn/QuantizerVisitor.hpp42
-rw-r--r--src/armnn/StaticRangeVisitor.cpp34
-rw-r--r--src/armnn/StaticRangeVisitor.hpp29
-rw-r--r--src/armnn/test/QuantizerTest.cpp101
13 files changed, 410 insertions, 158 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 8d43ef8d8b..e8d63b9a11 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -307,12 +307,16 @@ list(APPEND armnn_sources
src/armnn/Network.hpp
src/armnn/NetworkQuantizer.cpp
src/armnn/NetworkQuantizer.hpp
+ src/armnn/NetworkQuantizerUtils.cpp
+ src/armnn/NetworkQuantizerUtils.hpp
src/armnn/NetworkUtils.cpp
src/armnn/NetworkUtils.hpp
src/armnn/Observable.cpp
src/armnn/Observable.hpp
src/armnn/Optimizer.cpp
src/armnn/Optimizer.hpp
+ src/armnn/OverrideInputRangeVisitor.cpp
+ src/armnn/OverrideInputRangeVisitor.hpp
src/armnn/Profiling.cpp
src/armnn/ProfilingEvent.cpp
src/armnn/ProfilingEvent.hpp
diff --git a/include/armnn/INetworkQuantizer.hpp b/include/armnn/INetworkQuantizer.hpp
index f3cc13ca35..5969fa4edf 100644
--- a/include/armnn/INetworkQuantizer.hpp
+++ b/include/armnn/INetworkQuantizer.hpp
@@ -7,9 +7,6 @@
#include <armnn/INetwork.hpp>
-#include <memory>
-
-
namespace armnn
{
@@ -23,11 +20,14 @@ public:
static INetworkQuantizerPtr Create(INetwork* inputNetwork); ///< Create Quantizer object wrapped in unique_ptr
static void Destroy(INetworkQuantizer* quantizer); ///< Destroy Quantizer object
+ /// Overrides the default quantization values for the input layer with the given id
+ virtual void OverrideInputRange(LayerBindingId layerId, float min, float max) = 0;
+
/// Extract final quantized network
virtual INetworkPtr ExportNetwork() = 0;
protected:
- virtual ~INetworkQuantizer() {};
+ virtual ~INetworkQuantizer() {}
};
} //namespace armnn
diff --git a/src/armnn/NetworkQuantizer.cpp b/src/armnn/NetworkQuantizer.cpp
index f8e5ed23a7..ccbc501618 100644
--- a/src/armnn/NetworkQuantizer.cpp
+++ b/src/armnn/NetworkQuantizer.cpp
@@ -12,11 +12,12 @@
#include "Layer.hpp"
#include "Network.hpp"
#include "NetworkQuantizer.hpp"
+#include "NetworkQuantizerUtils.hpp"
#include "StaticRangeVisitor.hpp"
#include "QuantizerVisitor.hpp"
+#include "OverrideInputRangeVisitor.hpp"
-#include <map>
#include <vector>
#include <cmath>
@@ -38,26 +39,29 @@ void INetworkQuantizer::Destroy(INetworkQuantizer *quantizer)
delete boost::polymorphic_downcast<NetworkQuantizer*>(quantizer);
}
+void NetworkQuantizer::OverrideInputRange(LayerBindingId layerId, float min, float max)
+{
+ const Graph& graph = boost::polymorphic_downcast<const Network*>(m_InputNetwork)->GetGraph();
+ auto inputLayers = graph.GetInputLayers();
+
+ // Walk the input layers of the graph and override the quantization parameters of the one with the given id
+ OverrideInputRangeVisitor overrideInputRangeVisitor(m_GuidToRangesMap, layerId, MinMaxRange{min, max});
+ VisitLayers(inputLayers, overrideInputRangeVisitor);
+}
+
INetworkPtr NetworkQuantizer::ExportNetwork()
{
const Graph& graph = boost::polymorphic_downcast<const Network*>(m_InputNetwork)->GetGraph().TopologicalSort();
- auto VisitLayers = [&graph](ILayerVisitor& visitor)
- {
- for (auto layer : graph)
- {
- layer->Accept(visitor);
- }
- };
// Step 1) Walk the graph and register min/max values for intermediate tensors
- StaticRangeVisitor rangeVisitor;
- VisitLayers(rangeVisitor);
+ StaticRangeVisitor rangeVisitor(m_GuidToRangesMap);
+ VisitLayers(graph, rangeVisitor);
// Step 2) Convert input InputNetwork to Quantized InputNetwork
QuantizerVisitor quantizerVisitor(&rangeVisitor);
- VisitLayers(quantizerVisitor);
+ VisitLayers(graph, quantizerVisitor);
return quantizerVisitor.RetrieveFinalNetwork();
}
-} //namespace armn \ No newline at end of file
+} //namespace armn
diff --git a/src/armnn/NetworkQuantizer.hpp b/src/armnn/NetworkQuantizer.hpp
index 5543b3a444..2f7d36533c 100644
--- a/src/armnn/NetworkQuantizer.hpp
+++ b/src/armnn/NetworkQuantizer.hpp
@@ -9,6 +9,8 @@
#include <armnn/INetworkQuantizer.hpp>
#include <armnn/Types.hpp>
+#include <unordered_map>
+
namespace armnn
{
@@ -17,10 +19,17 @@ class NetworkQuantizer : public INetworkQuantizer
public:
NetworkQuantizer(INetwork* inputNetwork) : m_InputNetwork(inputNetwork) {}
+ void OverrideInputRange(LayerBindingId layerId, float min, float max) override;
INetworkPtr ExportNetwork() override;
private:
+ using MinMaxRange = std::pair<float, float>;
+ using MinMaxRanges = std::vector<MinMaxRange>;
+
INetwork* m_InputNetwork;
+
+ /// Mapping from Guid to an array of ranges for outputs
+ std::unordered_map<LayerGuid, MinMaxRanges> m_GuidToRangesMap;
};
-} //namespace armnn \ No newline at end of file
+} //namespace armnn
diff --git a/src/armnn/NetworkQuantizerUtils.cpp b/src/armnn/NetworkQuantizerUtils.cpp
new file mode 100644
index 0000000000..1bec63b58c
--- /dev/null
+++ b/src/armnn/NetworkQuantizerUtils.cpp
@@ -0,0 +1,61 @@
+//
+// Copyright © 2017 Arm Ltd. All rights reserved.
+// SPDX-License-Identifier: MIT
+//
+
+#include "NetworkQuantizerUtils.hpp"
+
+#include <algorithm>
+#include <cmath>
+#include <stdint.h>
+
+namespace armnn
+{
+
+std::pair<int, float> ComputeQAsymmParams(int numBits, double min, double max)
+{
+ BOOST_ASSERT_MSG(min < max, "min >= max will result in invalid quantization.");
+ double highest = (1 << numBits) - 1;
+
+ min = std::min(0.0, min); // min <= 0.0
+ max = std::max(0.0, max); // max >= 0.0
+
+ // Assumes quantization range [0-highest]
+ double scale = (max-min) / highest;
+ double offset = -min / scale;
+
+ // Clamp offset [0-highest]
+ offset = std::max(0.0, std::min(highest, offset));
+
+ return std::make_pair(static_cast<int>(std::round(offset)), static_cast<float>(scale));
+}
+
+ConstTensor CreateQuantizedConst(const ConstTensor& tensor, std::vector<uint8_t>& backing)
+{
+ float scale = 0.0f;
+ int offset = 0;
+
+ // Reserve the backing memory
+ backing.resize(tensor.GetInfo().GetNumElements());
+
+ DataType type = tensor.GetInfo().GetDataType();
+ switch(type)
+ {
+ case DataType::Float32:
+ {
+ Quantize(static_cast<const float*>(tensor.GetMemoryArea()),
+ backing.data(),
+ backing.size(),
+ scale,
+ offset);
+ }
+ break;
+ default:
+ BOOST_ASSERT_MSG(false, "Can't quantize unsupported data type");
+ }
+
+ TensorInfo qInfo(tensor.GetInfo().GetShape(), DataType::QuantisedAsymm8, scale, offset);
+ return ConstTensor(qInfo, backing);
+}
+
+} // namespace armnn
diff --git a/src/armnn/NetworkQuantizerUtils.hpp b/src/armnn/NetworkQuantizerUtils.hpp
new file mode 100644
index 0000000000..458d21a974
--- /dev/null
+++ b/src/armnn/NetworkQuantizerUtils.hpp
@@ -0,0 +1,56 @@
+//
+// Copyright © 2017 Arm Ltd. All rights reserved.
+// SPDX-License-Identifier: MIT
+//
+
+#pragma once
+
+#include <armnn/Tensor.hpp>
+#include <armnn/TypesUtils.hpp>
+#include <armnn/ILayerVisitor.hpp>
+
+#include <utility>
+#include <limits>
+
+#include <boost/assert.hpp>
+
+namespace armnn
+{
+
+std::pair<int, float> ComputeQAsymmParams(int numBits, double min, double max);
+
+template<typename srcType>
+void Quantize(const srcType* src, uint8_t* dst, size_t numElements, float& scale, int& offset)
+{
+ BOOST_ASSERT(src);
+ BOOST_ASSERT(dst);
+
+ float min = std::numeric_limits<srcType>::max();
+ float max = std::numeric_limits<srcType>::lowest();
+ for (size_t i = 0; i < numElements; ++i)
+ {
+ min = std::min(min, src[i]);
+ max = std::max(max, src[i]);
+ }
+
+ auto qParams = ComputeQAsymmParams(8, min, max);
+ offset = qParams.first;
+ scale = qParams.second;
+ for (size_t i = 0; i < numElements; ++i)
+ {
+ dst[i] = armnn::Quantize<uint8_t>(src[i], scale, offset);
+ }
+}
+
+ConstTensor CreateQuantizedConst(const ConstTensor& tensor, std::vector<uint8_t>& backing);
+
+template <typename LayerContainer>
+void VisitLayers(const LayerContainer& layerContainer, ILayerVisitor& visitor)
+{
+ for (auto layer : layerContainer)
+ {
+ layer->Accept(visitor);
+ }
+}
+
+} // namespace armnn
diff --git a/src/armnn/OverrideInputRangeVisitor.cpp b/src/armnn/OverrideInputRangeVisitor.cpp
new file mode 100644
index 0000000000..4c70d3f4a6
--- /dev/null
+++ b/src/armnn/OverrideInputRangeVisitor.cpp
@@ -0,0 +1,47 @@
+//
+// Copyright © 2017 Arm Ltd. All rights reserved.
+// SPDX-License-Identifier: MIT
+//
+
+#include "OverrideInputRangeVisitor.hpp"
+#include "NetworkQuantizerUtils.hpp"
+#include "Layer.hpp"
+
+#include <boost/assert.hpp>
+
+namespace armnn
+{
+
+OverrideInputRangeVisitor::OverrideInputRangeVisitor(std::unordered_map<LayerGuid, MinMaxRanges>& guidToRangesMap,
+ LayerBindingId layerId,
+ const MinMaxRange& minMaxRange)
+ : m_GuidToRangesMap(guidToRangesMap)
+ , m_LayerId(layerId)
+ , m_MinMaxRange(minMaxRange)
+{}
+
+void OverrideInputRangeVisitor::VisitInputLayer(const IConnectableLayer *layer, LayerBindingId id, const char *name)
+{
+ if (m_LayerId != id)
+ {
+ // Not the layer we are looking for
+ return;
+ }
+
+ SetRange(layer);
+}
+
+void OverrideInputRangeVisitor::SetRange(const IConnectableLayer* layer)
+{
+ BOOST_ASSERT(layer);
+
+ auto& ranges = m_GuidToRangesMap[layer->GetGuid()];
+
+ if (ranges.size() < layer->GetNumOutputSlots())
+ {
+ ranges.resize(layer->GetNumOutputSlots());
+ }
+ ranges[0] = m_MinMaxRange;
+}
+
+} // namespace armnn
diff --git a/src/armnn/OverrideInputRangeVisitor.hpp b/src/armnn/OverrideInputRangeVisitor.hpp
new file mode 100644
index 0000000000..a2da6c702e
--- /dev/null
+++ b/src/armnn/OverrideInputRangeVisitor.hpp
@@ -0,0 +1,45 @@
+//
+// Copyright © 2017 Arm Ltd. All rights reserved.
+// SPDX-License-Identifier: MIT
+//
+
+#pragma once
+
+#include "NetworkQuantizer.hpp"
+#include "LayerVisitorBase.hpp"
+
+#include <unordered_map>
+
+namespace armnn
+{
+
+/// Visitor object for overriding the input range of the quantized input layers in a network
+class OverrideInputRangeVisitor : public LayerVisitorBase<VisitorNoThrowPolicy>
+{
+private:
+ using MinMaxRange = std::pair<float, float>;
+ using MinMaxRanges = std::vector<MinMaxRange>;
+
+public:
+ OverrideInputRangeVisitor(std::unordered_map<LayerGuid, MinMaxRanges>& guidToRangesMap,
+ LayerBindingId layerId,
+ const MinMaxRange& minMaxRange);
+ ~OverrideInputRangeVisitor() = default;
+
+ void VisitInputLayer(const IConnectableLayer *layer, LayerBindingId id, const char *name = nullptr) override;
+
+private:
+ /// Sets the range for the given input layer
+ void SetRange(const IConnectableLayer* layer);
+
+ /// Mapping from a layer Guid to an array of ranges for outputs
+ std::unordered_map<LayerGuid, MinMaxRanges>& m_GuidToRangesMap;
+
+ /// The id of the input layer of which to override the input range
+ LayerBindingId m_LayerId;
+
+ /// The new input range to be applied to the input layer
+ MinMaxRange m_MinMaxRange;
+};
+
+} // namespace armnn
diff --git a/src/armnn/QuantizerVisitor.cpp b/src/armnn/QuantizerVisitor.cpp
index 4e075149aa..1212716f97 100644
--- a/src/armnn/QuantizerVisitor.cpp
+++ b/src/armnn/QuantizerVisitor.cpp
@@ -6,92 +6,16 @@
#include "Network.hpp"
#include "QuantizerVisitor.hpp"
#include "StaticRangeVisitor.hpp"
-
-#include "armnn/TypesUtils.hpp"
-
-#include <cmath>
-#include <stdint.h>
-#include <limits>
+#include "NetworkQuantizerUtils.hpp"
namespace armnn
{
-namespace {
-
-std::pair<int, float> ComputeQAsymmParams(int numBits, double min, double max)
-{
- BOOST_ASSERT_MSG(min < max, "Min >= max will result in invalid quantization.");
- double highest = (1 << numBits)-1;
-
- min = std::min(0.0, min); // min <= 0.0
- max = std::max(0.0, max); // max >= 0.0
-
- // assumes quantization range [0-highest]
- double scale = (max-min) / highest;
- double offset = -min / scale;
-
- // clamp offset [0-highest]
- offset = std::max(0.0, std::min(highest, offset));
-
- return std::make_pair(static_cast<int>(std::round(offset)), static_cast<float>(scale));
-}
-
-template<typename srcType>
-void Quantize(const srcType* src, uint8_t* dst, size_t numElements, float &scale, int &offset)
-{
- BOOST_ASSERT(src);
- BOOST_ASSERT(dst);
-
- float min = std::numeric_limits<srcType>::max();
- float max = std::numeric_limits<srcType>::lowest();
- for (size_t i = 0; i < numElements; ++i)
- {
- min = std::min(min, src[i]);
- max = std::max(max, src[i]);
- }
-
- auto qParams = ComputeQAsymmParams(8, min, max);
- offset = qParams.first;
- scale = qParams.second;
- for (size_t i = 0; i < numElements; ++i)
- {
- dst[i] = armnn::Quantize<uint8_t>(src[i], scale, offset);
- }
-}
-
-ConstTensor CreateQuantizedConst(const ConstTensor& tensor, std::vector<uint8_t> &backing)
-{
- float scale = 0.0f;
- int offset = 0;
- // Reserve the backing memory
- backing.resize(tensor.GetInfo().GetNumElements());
-
- DataType type = tensor.GetInfo().GetDataType();
- switch(type)
- {
- case DataType::Float32:
- {
- Quantize(static_cast<const float*>( tensor.GetMemoryArea()),
- backing.data(),
- backing.size(),
- scale,
- offset);
- }
- break;
- default:
- BOOST_ASSERT_MSG(false, "Can't quantize unsupported data type");
- }
-
- TensorInfo qInfo(tensor.GetInfo().GetShape(), DataType::QuantisedAsymm8, scale, offset);
- return ConstTensor(qInfo, backing);
-}
-
-} // namespace
-
-QuantizerVisitor::QuantizerVisitor(armnn::StaticRangeVisitor* ranges)
-: m_Ranges(ranges)
-, m_QuantizedNetwork(INetwork::Create())
+QuantizerVisitor::QuantizerVisitor(const StaticRangeVisitor *staticRangeVisitor)
+ : m_StaticRangeVisitor(staticRangeVisitor)
+ , m_QuantizedNetwork(INetwork::Create())
{
+ BOOST_ASSERT(m_StaticRangeVisitor);
}
void QuantizerVisitor::SetQuantizedInputConnections(const IConnectableLayer *srcLayer,
@@ -106,17 +30,17 @@ void QuantizerVisitor::SetQuantizedInputConnections(const IConnectableLayer *src
unsigned int slotIdx = outputSlot->CalculateIndexOnOwner();
Layer& layerToFind = outputSlot->GetOwningLayer();
- auto found = m_OldToNewGuidMap.find(layerToFind.GetGuid());
- if (found != m_OldToNewGuidMap.end())
+ auto found = m_OriginalToQuantizedGuidMap.find(layerToFind.GetGuid());
+ if (found != m_OriginalToQuantizedGuidMap.end())
{
// Connect the slots in the quantized model
- IConnectableLayer* prevQuantizedLayer = m_GuidToLayerMap[found->second];
+ IConnectableLayer* prevQuantizedLayer = m_QuantizedGuidToLayerMap[found->second];
IInputSlot& newInputSlot = quantizedLayer->GetInputSlot(i);
IOutputSlot& newOutputSlot = prevQuantizedLayer->GetOutputSlot(slotIdx);
newOutputSlot.Connect(newInputSlot);
// Fetch the min/max ranges that were computed earlier
- auto range = m_Ranges->GetRange(layerToFind.GetGuid(), i);
+ auto range = m_StaticRangeVisitor->GetRange(layerToFind.GetGuid(), i);
auto qParams = ComputeQAsymmParams(8, range.first, range.second);
// Set the quantization params
@@ -128,7 +52,7 @@ void QuantizerVisitor::SetQuantizedInputConnections(const IConnectableLayer *src
}
else
{
- // error in graph traversal order
+ // Error in graph traversal order
BOOST_ASSERT_MSG(false, "Error in graph traversal");
}
}
@@ -136,8 +60,8 @@ void QuantizerVisitor::SetQuantizedInputConnections(const IConnectableLayer *src
void QuantizerVisitor::RecordLayer(const IConnectableLayer* srcLayer, IConnectableLayer* quantizedLayer)
{
- m_OldToNewGuidMap[srcLayer->GetGuid()] = quantizedLayer->GetGuid();
- m_GuidToLayerMap[quantizedLayer->GetGuid()] = quantizedLayer;
+ m_OriginalToQuantizedGuidMap[srcLayer->GetGuid()] = quantizedLayer->GetGuid();
+ m_QuantizedGuidToLayerMap[quantizedLayer->GetGuid()] = quantizedLayer;
}
void QuantizerVisitor::VisitAdditionLayer(const IConnectableLayer *layer, const char *name)
@@ -200,4 +124,4 @@ void QuantizerVisitor::VisitBatchNormalizationLayer(const IConnectableLayer *lay
SetQuantizedInputConnections(layer, newLayer);
}
-} //namespace armnn \ No newline at end of file
+} //namespace armnn
diff --git a/src/armnn/QuantizerVisitor.hpp b/src/armnn/QuantizerVisitor.hpp
index 0dc45822b4..dcaccd4ac7 100644
--- a/src/armnn/QuantizerVisitor.hpp
+++ b/src/armnn/QuantizerVisitor.hpp
@@ -6,31 +6,34 @@
#pragma once
#include "LayerVisitorBase.hpp"
+#include "StaticRangeVisitor.hpp"
+
#include <armnn/INetwork.hpp>
#include <armnn/Types.hpp>
+#include <armnn/INetworkQuantizer.hpp>
-#include <map>
+#include <unordered_map>
namespace armnn
{
-// Forward declarations
+// Forward declaration
class StaticRangeVisitor;
/// Visitor object for quantizing layers in a network
class QuantizerVisitor : public LayerVisitorBase<VisitorNoThrowPolicy>
{
public:
- QuantizerVisitor(StaticRangeVisitor* ranges);
+ QuantizerVisitor(const StaticRangeVisitor* staticRangeVisitor);
~QuantizerVisitor() = default;
- // Functions to quantize the individual layers, overridden from ILayerVisitor
- void VisitInputLayer(const IConnectableLayer *layer, LayerBindingId id, const char *name = nullptr) override;
- void VisitAdditionLayer(const IConnectableLayer *layer, const char *name = nullptr) override;
- void VisitActivationLayer(const IConnectableLayer *layer,
+ /// Functions to quantize the individual layers, overridden from ILayerVisitor
+ void VisitInputLayer(const IConnectableLayer* layer, LayerBindingId id, const char* name = nullptr) override;
+ void VisitAdditionLayer(const IConnectableLayer* layer, const char* name = nullptr) override;
+ void VisitActivationLayer(const IConnectableLayer* layer,
const ActivationDescriptor& activationDescriptor,
- const char *name = nullptr) override;
- void VisitOutputLayer(const IConnectableLayer *layer, LayerBindingId id, const char *name = nullptr) override;
+ const char* name = nullptr) override;
+ void VisitOutputLayer(const IConnectableLayer* layer, LayerBindingId id, const char* name = nullptr) override;
void VisitBatchNormalizationLayer(const IConnectableLayer* layer,
const BatchNormalizationDescriptor& desc,
const ConstTensor& mean,
@@ -39,22 +42,27 @@ public:
const ConstTensor& gamma,
const char* name = nullptr) override;
- // Extract the quantized network
+ /// Extract the quantized network
INetworkPtr RetrieveFinalNetwork() { return std::move(m_QuantizedNetwork); }
-private:
+private:
/// Connects the layer to preceeding layers and sets the quantization parameters based on recorded ranges
- void SetQuantizedInputConnections(const IConnectableLayer *srcLayer, IConnectableLayer *quantizedLayer);
+ void SetQuantizedInputConnections(const IConnectableLayer* srcLayer, IConnectableLayer* quantizedLayer);
/// Record the guids so we can easily find the layers later
void RecordLayer(const IConnectableLayer* srcLayer, IConnectableLayer* qLayer);
+ /// Reference to the static range visitor used to retrieve the quantization ranges
+ const StaticRangeVisitor* m_StaticRangeVisitor;
+
+ /// Quantized version of the model we are building up
+ INetworkPtr m_QuantizedNetwork;
- StaticRangeVisitor* m_Ranges; ///< Previously recorded min/max ranges per intermediate tensor
- INetworkPtr m_QuantizedNetwork; ///< Quantized version of the model we are building up
+ /// Mapping from input network guids to quantized network guids
+ std::unordered_map<LayerGuid, LayerGuid> m_OriginalToQuantizedGuidMap;
- std::map<LayerGuid, LayerGuid> m_OldToNewGuidMap; ///< Mapping from input network guids to quantized network guids
- std::map<LayerGuid, IConnectableLayer*> m_GuidToLayerMap; ///< Mapping from guid to layer in quantized network
+ /// Mapping from guid to layer in quantized network
+ std::unordered_map<LayerGuid, IConnectableLayer*> m_QuantizedGuidToLayerMap;
};
-} //namespace armnn \ No newline at end of file
+} //namespace armnn
diff --git a/src/armnn/StaticRangeVisitor.cpp b/src/armnn/StaticRangeVisitor.cpp
index 1986e427f2..258d499279 100644
--- a/src/armnn/StaticRangeVisitor.cpp
+++ b/src/armnn/StaticRangeVisitor.cpp
@@ -12,6 +12,20 @@
namespace armnn
{
+StaticRangeVisitor::StaticRangeVisitor(std::unordered_map<LayerGuid, MinMaxRanges>& guidToRangesMap)
+ : m_GuidToRangesMap(guidToRangesMap)
+{}
+
+StaticRangeVisitor::MinMaxRange StaticRangeVisitor::GetRange(LayerGuid guid, unsigned int idx) const
+{
+ auto search = m_GuidToRangesMap.find(guid);
+ if (search == m_GuidToRangesMap.end())
+ {
+ return DefaultRange();
+ }
+ return search->second.at(idx);
+}
+
void StaticRangeVisitor::SetRange(const IConnectableLayer* layer, unsigned int outputIdx, float min, float max)
{
auto& ranges = m_GuidToRangesMap[layer->GetGuid()];
@@ -23,20 +37,10 @@ void StaticRangeVisitor::SetRange(const IConnectableLayer* layer, unsigned int o
ranges[outputIdx] = std::make_pair(min, max);
}
-StaticRangeVisitor::MinMaxRange StaticRangeVisitor::GetRange(LayerGuid guid, unsigned int idx) const
-{
- auto found = m_GuidToRangesMap.find(guid);
- if (found != m_GuidToRangesMap.end())
- {
- return found->second.at(idx);
- }
- return DefaultRange();
-}
-
-void StaticRangeVisitor::VisitAdditionLayer(const IConnectableLayer *layer, const char *name)
+void StaticRangeVisitor::VisitAdditionLayer(const IConnectableLayer* layer, const char* name)
{
SetRange(layer, 0, -20.f, 20.f);
-};
+}
void StaticRangeVisitor::VisitBatchNormalizationLayer(const IConnectableLayer* layer,
const BatchNormalizationDescriptor& desc,
@@ -55,9 +59,9 @@ void StaticRangeVisitor::VisitBatchNormalizationLayer(const IConnectableLayer* l
SetRange(layer, 0, -15.0f, 15.0f);
}
-void StaticRangeVisitor::VisitActivationLayer(const IConnectableLayer *layer,
+void StaticRangeVisitor::VisitActivationLayer(const IConnectableLayer* layer,
const ActivationDescriptor& activationDescriptor,
- const char *name)
+ const char* name)
{
switch (activationDescriptor.m_Function)
{
@@ -83,4 +87,4 @@ void StaticRangeVisitor::VisitActivationLayer(const IConnectableLayer *layer,
}
}
-} //namespace armnn \ No newline at end of file
+} //namespace armnn
diff --git a/src/armnn/StaticRangeVisitor.hpp b/src/armnn/StaticRangeVisitor.hpp
index ed02fb57dd..ea27947e7a 100644
--- a/src/armnn/StaticRangeVisitor.hpp
+++ b/src/armnn/StaticRangeVisitor.hpp
@@ -8,9 +8,9 @@
#include "LayerVisitorBase.hpp"
#include <armnn/INetwork.hpp>
+#include <armnn/INetworkQuantizer.hpp>
-#include <map>
-#include <vector>
+#include <unordered_map>
namespace armnn
{
@@ -18,15 +18,16 @@ namespace armnn
/// Visitor class to establish min/max ranges based on the type of the layer
class StaticRangeVisitor : public LayerVisitorBase<VisitorNoThrowPolicy>
{
+private:
+ using MinMaxRange = std::pair<float, float>;
+ using MinMaxRanges = std::vector<MinMaxRange>;
+
public:
- StaticRangeVisitor() = default;
+ StaticRangeVisitor(std::unordered_map<LayerGuid, MinMaxRanges>& guidToRangesMap);
~StaticRangeVisitor() = default;
- using MinMaxRange = std::pair<float, float>;
- using MinMaxRanges = std::vector<MinMaxRange>;
-
/// Functions to set the Range on a per-layer-type basis
- void VisitAdditionLayer(const IConnectableLayer *layer, const char *name = nullptr) override;
+ void VisitAdditionLayer(const IConnectableLayer* layer, const char* name = nullptr) override;
void VisitBatchNormalizationLayer(const IConnectableLayer* layer,
const BatchNormalizationDescriptor& desc,
const ConstTensor& mean,
@@ -34,22 +35,22 @@ public:
const ConstTensor& beta,
const ConstTensor& gamma,
const char* name = nullptr) override;
- void VisitActivationLayer(const IConnectableLayer *layer,
+ void VisitActivationLayer(const IConnectableLayer* layer,
const ActivationDescriptor& activationDescriptor,
- const char *name = nullptr) override;
+ const char* name = nullptr) override;
- /// Retreive the default range
+ /// Retrieve the default range
MinMaxRange DefaultRange() const { return std::make_pair(-15.0f, 15.0f); }
- /// Retreive the Range for a particular output slot on a particular layer
+ /// Retrieve the Range for a particular output slot on a particular layer
MinMaxRange GetRange(LayerGuid guid, unsigned int idx) const;
private:
/// Set the range for an output slot on a layer
void SetRange(const IConnectableLayer* layer, unsigned int outputIdx, float min, float max);
- /// Mapping from Guid to an array of ranges for outputs
- std::map<LayerGuid, MinMaxRanges> m_GuidToRangesMap;
+ /// Mapping from a layer Guid to an array of ranges for outputs
+ std::unordered_map<LayerGuid, MinMaxRanges>& m_GuidToRangesMap;
};
-} //namespace armnn \ No newline at end of file
+} //namespace armnn
diff --git a/src/armnn/test/QuantizerTest.cpp b/src/armnn/test/QuantizerTest.cpp
index 6f9ad31cc0..7f782dc686 100644
--- a/src/armnn/test/QuantizerTest.cpp
+++ b/src/armnn/test/QuantizerTest.cpp
@@ -11,11 +11,19 @@
#include "../LayerVisitorBase.hpp"
#include "../Network.hpp"
#include "../Graph.hpp"
+#include "../NetworkQuantizerUtils.hpp"
+#include "../OverrideInputRangeVisitor.hpp"
#include <boost/test/unit_test.hpp>
+#include <unordered_map>
+
namespace armnn
{
+using MinMaxRange = std::pair<float, float>;
+using MinMaxRanges = std::vector<MinMaxRange>;
+using MinMaxRangeMap = std::unordered_map<LayerGuid, MinMaxRanges>;
+
BOOST_AUTO_TEST_SUITE(Quantizer)
class TestQuantization : public LayerVisitorBase<VisitorThrowingPolicy>
@@ -44,12 +52,9 @@ public:
void VisitLayersTopologically(const INetwork* inputNetwork, ILayerVisitor& visitor)
{
auto network = boost::polymorphic_downcast<const Network*>(inputNetwork);
-
auto graph = network->GetGraph().TopologicalSort();
- for (auto layer : graph)
- {
- layer->Accept(visitor);
- }
+
+ VisitLayers(graph, visitor);
}
BOOST_AUTO_TEST_CASE(QuantizeAddition)
@@ -370,5 +375,89 @@ BOOST_AUTO_TEST_CASE(QuantizeBatchNorm)
VisitLayersTopologically(quantizedNetwork.get(), validator);
}
+BOOST_AUTO_TEST_CASE(OverrideInputRangeEmptyNetwork)
+{
+ MinMaxRangeMap guidToRangesMap; // Empty map of ranges
+ MinMaxRange minMaxRange(-12.3f, 45.6f); // Range to use for the override
+
+ Network network; // Empty network
+ auto inputLayers = network.GetGraph().GetInputLayers(); // Empty list of input layers
+
+ OverrideInputRangeVisitor overrideInputRangeVisitor(guidToRangesMap, 0, minMaxRange);
+ VisitLayers(inputLayers, overrideInputRangeVisitor);
+
+ BOOST_CHECK(guidToRangesMap.empty()); // Check that the map of ranges remained untouched
+}
+
+BOOST_AUTO_TEST_CASE(OverrideInputRangeNoInputLayers)
+{
+ MinMaxRangeMap guidToRangesMap; // Empty map of ranges
+ MinMaxRange minMaxRange(-12.3f, 45.6f); // Range to use for the override
+
+ Network network;
+ network.AddAdditionLayer(); // Network with no input layers
+ auto inputLayers = network.GetGraph().GetInputLayers(); // Empty list of input layers
+
+ OverrideInputRangeVisitor overrideInputRangeVisitor(guidToRangesMap, 0, minMaxRange);
+ VisitLayers(inputLayers, overrideInputRangeVisitor);
+
+ BOOST_CHECK(guidToRangesMap.empty()); // Check that the map of ranges remained untouched
+}
+
+BOOST_AUTO_TEST_CASE(OverrideInputRangeInputLayers)
+{
+ MinMaxRangeMap guidToRangesMap; // Empty map of ranges
+ MinMaxRange minMaxRange(-12.3f, 45.6f); // Range to use for the override
+
+ Network network;
+
+ // Adding the layers
+ IConnectableLayer* input0 = network.AddInputLayer(0);
+ IConnectableLayer* input1 = network.AddInputLayer(1);
+ IConnectableLayer* addition = network.AddAdditionLayer();
+ IConnectableLayer* output = network.AddOutputLayer(2);
+
+ // Connecting the layer
+ input0->GetOutputSlot(0).Connect(addition->GetInputSlot(0));
+ input1->GetOutputSlot(0).Connect(addition->GetInputSlot(1));
+ addition->GetOutputSlot(0).Connect(output->GetInputSlot(0));
+
+ // Setting the TensorInfos
+ TensorShape shape{1U};
+ TensorInfo info(shape, DataType::Float32);
+ input0->GetOutputSlot(0).SetTensorInfo(info);
+ input1->GetOutputSlot(0).SetTensorInfo(info);
+ addition->GetOutputSlot(0).SetTensorInfo(info);
+
+ auto inputLayers = network.GetGraph().GetInputLayers(); // List of input layers
+
+ // Trying to override the input range for the input layer with binding id 3 (does not exist in the network)
+ OverrideInputRangeVisitor overrideInputRangeVisitorLayer3(guidToRangesMap, 3, minMaxRange);
+ VisitLayers(inputLayers, overrideInputRangeVisitorLayer3);
+
+ // Check that the map of ranges remained untouched
+ BOOST_CHECK(guidToRangesMap.empty());
+
+ // Override the input range for the input layer with binding id 1
+ OverrideInputRangeVisitor overrideInputRangeVisitorLayer1(guidToRangesMap, 1, minMaxRange);
+ VisitLayers(inputLayers, overrideInputRangeVisitorLayer1);
+
+ // Check that the map of ranges has been populated
+ BOOST_CHECK(!guidToRangesMap.empty());
+
+ // Check that an entry for the input layer with binding id 0 does not exist
+ BOOST_CHECK(guidToRangesMap.find(input0->GetGuid()) == guidToRangesMap.end());
+
+ // Check that an entry for the input layer with binding id 1 exists
+ BOOST_CHECK(guidToRangesMap.find(input1->GetGuid()) != guidToRangesMap.end());
+
+ // Check that at least a value has been added for the input layer with binding id 1
+ BOOST_CHECK(!guidToRangesMap[input1->GetGuid()].empty());
+
+ // Check the the overridden values are what we intended to set
+ BOOST_CHECK(guidToRangesMap[input1->GetGuid()].at(0).first == minMaxRange.first);
+ BOOST_CHECK(guidToRangesMap[input1->GetGuid()].at(0).second == minMaxRange.second);
+}
+
BOOST_AUTO_TEST_SUITE_END()
-} //namespace armnn
+} // namespace armnn