From 857aa45407df9dbe99a11d03a4be2b20bd0110ae Mon Sep 17 00:00:00 2001 From: Derek Lamberti Date: Thu, 7 Feb 2019 11:14:11 +0000 Subject: IVGCVSW-2609 Quantize BatchNormalizationLayer Change-Id: I7b847112a0322ffc8b88a0708d8439bfb97cfe2c Signed-off-by: Derek Lamberti --- src/armnn/QuantizerVisitor.cpp | 83 ++++++++++++++++++++++++++++ src/armnn/QuantizerVisitor.hpp | 7 +++ src/armnn/StaticRangeVisitor.cpp | 18 ++++++ src/armnn/StaticRangeVisitor.hpp | 7 +++ src/armnn/layers/BatchNormalizationLayer.cpp | 8 +-- src/armnn/test/QuantizerTest.cpp | 83 ++++++++++++++++++++++++++++ 6 files changed, 202 insertions(+), 4 deletions(-) diff --git a/src/armnn/QuantizerVisitor.cpp b/src/armnn/QuantizerVisitor.cpp index fd08b2d2e5..afe3713036 100644 --- a/src/armnn/QuantizerVisitor.cpp +++ b/src/armnn/QuantizerVisitor.cpp @@ -7,6 +7,8 @@ #include "QuantizerVisitor.hpp" #include "StaticRangeVisitor.hpp" +#include "armnn/TypesUtils.hpp" + #include #include #include @@ -34,6 +36,56 @@ std::pair ComputeQAsymmParams(int numBits, double min, double max) return std::make_pair(static_cast(std::round(offset)), static_cast(scale)); } +template +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::max(); + float max = std::numeric_limits::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(src[i], scale, offset); + } +} + +ConstTensor CreateQuantizedConst(const ConstTensor& tensor, std::vector &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( 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) @@ -108,4 +160,35 @@ void QuantizerVisitor::VisitOutputLayer(const IConnectableLayer *layer, LayerBin SetQuantizedInputConnections(layer, newLayer); } +void QuantizerVisitor::VisitBatchNormalizationLayer(const IConnectableLayer *layer, + const BatchNormalizationDescriptor& desc, + const ConstTensor& mean, + const ConstTensor& variance, + const ConstTensor& beta, + const ConstTensor& gamma, + const char *name) +{ + std::vector meanBacking; + ConstTensor qMean = CreateQuantizedConst(mean, meanBacking); + + std::vector varianceBacking; + ConstTensor qVariance = CreateQuantizedConst(variance, varianceBacking); + + std::vector betaBacking; + ConstTensor qBeta = CreateQuantizedConst(beta, betaBacking); + + std::vector gammaBacking; + ConstTensor qGamma = CreateQuantizedConst(variance, gammaBacking); + + IConnectableLayer* newLayer = m_QuantizedNetwork->AddBatchNormalizationLayer(desc, + qMean, + qVariance, + qBeta, + qGamma, + name); + + RecordLayer(layer, newLayer); + SetQuantizedInputConnections(layer, newLayer); +} + } //namespace armnn \ No newline at end of file diff --git a/src/armnn/QuantizerVisitor.hpp b/src/armnn/QuantizerVisitor.hpp index 5ff457ec33..d6aee6babf 100644 --- a/src/armnn/QuantizerVisitor.hpp +++ b/src/armnn/QuantizerVisitor.hpp @@ -28,6 +28,13 @@ public: void VisitInputLayer(const IConnectableLayer *layer, LayerBindingId id, const char *name = nullptr) override; void VisitAdditionLayer(const IConnectableLayer *layer, 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, + const ConstTensor& variance, + const ConstTensor& beta, + const ConstTensor& gamma, + const char* name = nullptr) override; // Extract the quantized network INetworkPtr RetrieveFinalNetwork() { return std::move(m_QuantizedNetwork); } diff --git a/src/armnn/StaticRangeVisitor.cpp b/src/armnn/StaticRangeVisitor.cpp index 8e90ba8b51..cc8c26e778 100644 --- a/src/armnn/StaticRangeVisitor.cpp +++ b/src/armnn/StaticRangeVisitor.cpp @@ -5,6 +5,7 @@ #include "StaticRangeVisitor.hpp" +#include namespace armnn { @@ -35,4 +36,21 @@ void StaticRangeVisitor::VisitAdditionLayer(const IConnectableLayer *layer, cons SetRange(layer, 0, -20.f, 20.f); }; +void StaticRangeVisitor::VisitBatchNormalizationLayer(const IConnectableLayer* layer, + const BatchNormalizationDescriptor& desc, + const ConstTensor& mean, + const ConstTensor& variance, + const ConstTensor& beta, + const ConstTensor& gamma, + const char* name) +{ + boost::ignore_unused(desc); + boost::ignore_unused(mean); + boost::ignore_unused(variance); + boost::ignore_unused(beta); + boost::ignore_unused(gamma); + boost::ignore_unused(name); + SetRange(layer, 0, -15.0f, 15.0f); +} + } //namespace armnn \ No newline at end of file diff --git a/src/armnn/StaticRangeVisitor.hpp b/src/armnn/StaticRangeVisitor.hpp index af59dace9e..4276a178f3 100644 --- a/src/armnn/StaticRangeVisitor.hpp +++ b/src/armnn/StaticRangeVisitor.hpp @@ -27,6 +27,13 @@ public: /// Functions to set the Range on a per-layer-type basis void VisitAdditionLayer(const IConnectableLayer *layer, const char *name = nullptr) override; + void VisitBatchNormalizationLayer(const IConnectableLayer* layer, + const BatchNormalizationDescriptor& desc, + const ConstTensor& mean, + const ConstTensor& variance, + const ConstTensor& beta, + const ConstTensor& gamma, + const char* name = nullptr) override; /// Retreive the default range MinMaxRange DefaultRange() const { return std::make_pair(-15.0f, 15.0f); } diff --git a/src/armnn/layers/BatchNormalizationLayer.cpp b/src/armnn/layers/BatchNormalizationLayer.cpp index 85132053fb..2212f47d3c 100644 --- a/src/armnn/layers/BatchNormalizationLayer.cpp +++ b/src/armnn/layers/BatchNormalizationLayer.cpp @@ -71,10 +71,10 @@ Layer::ConstantTensors BatchNormalizationLayer::GetConstantTensorsByRef() void BatchNormalizationLayer::Accept(ILayerVisitor& visitor) const { - ConstTensor meanTensor(m_Mean->GetTensorInfo(), m_Mean->GetTensor()) ; - ConstTensor varianceTensor(m_Variance->GetTensorInfo(), m_Variance->GetTensor()) ; - ConstTensor betaTensor(m_Beta->GetTensorInfo(), m_Beta->GetTensor()) ; - ConstTensor gammaTensor(m_Gamma->GetTensorInfo(), m_Gamma->GetTensor()) ; + ConstTensor meanTensor(m_Mean->GetTensorInfo(), m_Mean->Map(true)); + ConstTensor varianceTensor(m_Variance->GetTensorInfo(), m_Variance->Map(true)); + ConstTensor betaTensor(m_Beta->GetTensorInfo(), m_Beta->Map(true)); + ConstTensor gammaTensor(m_Gamma->GetTensorInfo(), m_Gamma->Map(true)); visitor.VisitBatchNormalizationLayer(this, GetParameters(), meanTensor, varianceTensor, betaTensor, gammaTensor); } diff --git a/src/armnn/test/QuantizerTest.cpp b/src/armnn/test/QuantizerTest.cpp index 56b1497967..fbafbd8f1e 100644 --- a/src/armnn/test/QuantizerTest.cpp +++ b/src/armnn/test/QuantizerTest.cpp @@ -92,5 +92,88 @@ BOOST_AUTO_TEST_CASE(QuantizeAddition) VisitLayersTopologically(quantizedNetwork.get(), validator); } +BOOST_AUTO_TEST_CASE(QuantizeBatchNorm) +{ + + class TestQuantization : public LayerVisitorBase + { + public: + virtual void VisitBatchNormalizationLayer(const IConnectableLayer* layer, + const BatchNormalizationDescriptor& desc, + const ConstTensor& mean, + const ConstTensor& variance, + const ConstTensor& beta, + const ConstTensor& gamma, + const char* name = nullptr) + { + TensorInfo info = layer->GetOutputSlot(0).GetTensorInfo(); + + BOOST_TEST((info.GetDataType() == DataType::QuantisedAsymm8)); + + BOOST_TEST((info.GetQuantizationOffset() == 128)); + + // Based off current static value [-15.0f, 15.0f] + BOOST_CHECK_CLOSE(info.GetQuantizationScale(), 30.0f/255.0f, 0.000001f ); + + //Test constants + BOOST_TEST((mean.GetInfo().GetDataType() == DataType::QuantisedAsymm8)); + BOOST_TEST((variance.GetInfo().GetDataType() == DataType::QuantisedAsymm8)); + BOOST_TEST((beta.GetInfo().GetDataType() == DataType::QuantisedAsymm8)); + BOOST_TEST((gamma.GetInfo().GetDataType() == DataType::QuantisedAsymm8)); + + BOOST_CHECK_CLOSE(mean.GetInfo().GetQuantizationScale(), 3.0f/255.0f, 0.000001f); + BOOST_CHECK_CLOSE(variance.GetInfo().GetQuantizationScale(), 3.0f/255.0f, 0.000001f); + BOOST_CHECK_CLOSE(beta.GetInfo().GetQuantizationScale(), 3.0f/255.0f, 0.000001f); + BOOST_CHECK_CLOSE(gamma.GetInfo().GetQuantizationScale(), 3.0f/255.0f, 0.000001f); + + BOOST_TEST((mean.GetInfo().GetQuantizationOffset() == 85)); + } + + virtual void VisitInputLayer(const IConnectableLayer* layer, + LayerBindingId id, + const char* name = nullptr) + {} + + virtual void VisitOutputLayer(const IConnectableLayer* layer, + LayerBindingId id, + const char* name = nullptr) + {} + }; + + auto network = INetwork::Create(); + + TensorShape shape{3U}; + TensorInfo info(shape, DataType::Float32); + + std::vector meanData{-1.0f, 1.5f, 2.0f}; + std::vector varData{-1.0f, 1.5f, 2.0f}; + std::vector betaData{-1.0f, 1.5f, 2.0f}; + std::vector gammaData{-1.0f, 1.5f, 2.0f}; + + ConstTensor mean(info, meanData); + ConstTensor var(info, varData); + ConstTensor beta(info, betaData); + ConstTensor gamma(info, gammaData); + + BatchNormalizationDescriptor desc; + + // Add the layers + IConnectableLayer* input0 = network->AddInputLayer(0); + IConnectableLayer* batchNorm = network->AddBatchNormalizationLayer(desc, mean, var, beta, gamma); + IConnectableLayer* output = network->AddOutputLayer(1); + + // Establish connections + input0->GetOutputSlot(0).Connect(batchNorm->GetInputSlot(0)); + batchNorm->GetOutputSlot(0).Connect(output->GetInputSlot(0)); + + //Set TensorInfo + input0->GetOutputSlot(0).SetTensorInfo(info); + batchNorm->GetOutputSlot(0).SetTensorInfo(info); + + auto quantizedNetwork = INetworkQuantizer::Create(network.get())->ExportNetwork(); + TestQuantization validator; + VisitLayersTopologically(quantizedNetwork.get(), validator); +} + BOOST_AUTO_TEST_SUITE_END() } //namespace armnn -- cgit v1.2.1