From 3dad5acc5d8eda6fc472b9a255c1d893d4e1f942 Mon Sep 17 00:00:00 2001 From: Jim Flynn Date: Mon, 11 Feb 2019 10:30:30 +0000 Subject: IVGCVSW-2622 Add static quantization of 2DConvolution Change-Id: If7985a54eba97f7c61413e0804879e4afbf65c4d Signed-off-by: Jim Flynn --- src/armnn/QuantizerVisitor.cpp | 35 ++++++++++++++++ src/armnn/QuantizerVisitor.hpp | 11 +++++ src/armnn/StaticRangeVisitor.cpp | 21 ++++++++++ src/armnn/StaticRangeVisitor.hpp | 9 +++++ src/armnn/test/QuantizerTest.cpp | 87 ++++++++++++++++++++++++++++++++++++++++ 5 files changed, 163 insertions(+) diff --git a/src/armnn/QuantizerVisitor.cpp b/src/armnn/QuantizerVisitor.cpp index 97a8bc1ad2..c5e203ef86 100644 --- a/src/armnn/QuantizerVisitor.cpp +++ b/src/armnn/QuantizerVisitor.cpp @@ -153,4 +153,39 @@ void QuantizerVisitor::VisitBatchNormalizationLayer(const IConnectableLayer* lay SetQuantizedInputConnections(layer, newLayer); } +void QuantizerVisitor::VisitConvolution2dLayer(const IConnectableLayer* layer, + const Convolution2dDescriptor& convolution2dDescriptor, + const ConstTensor& weights, + const char* name) +{ + std::vector weightsBacking; + ConstTensor qWeights = CreateQuantizedConst(weights, weightsBacking); + + IConnectableLayer* newLayer = m_QuantizedNetwork->AddConvolution2dLayer(convolution2dDescriptor, + qWeights, + name); + RecordLayer(layer, newLayer); + SetQuantizedInputConnections(layer, newLayer); +} + +void QuantizerVisitor::VisitConvolution2dLayer(const IConnectableLayer* layer, + const Convolution2dDescriptor& convolution2dDescriptor, + const ConstTensor& weights, + const ConstTensor& biases, + const char* name) +{ + std::vector weightsBacking; + ConstTensor qWeights = CreateQuantizedConst(weights, weightsBacking); + + std::vector biasesBacking; + ConstTensor qBiases = CreateQuantizedConst(weights, biasesBacking); + + IConnectableLayer* newLayer = m_QuantizedNetwork->AddConvolution2dLayer(convolution2dDescriptor, + qWeights, + qBiases, + name); + RecordLayer(layer, newLayer); + SetQuantizedInputConnections(layer, newLayer); +} + } //namespace armnn diff --git a/src/armnn/QuantizerVisitor.hpp b/src/armnn/QuantizerVisitor.hpp index 53b3edc3fd..fbf9cfa20e 100644 --- a/src/armnn/QuantizerVisitor.hpp +++ b/src/armnn/QuantizerVisitor.hpp @@ -52,6 +52,17 @@ public: const char *name = nullptr) override; // Extract the quantized network + void VisitConvolution2dLayer(const IConnectableLayer* layer, + const Convolution2dDescriptor& convolution2dDescriptor, + const ConstTensor& weights, + const char* name = nullptr) override; + void VisitConvolution2dLayer(const IConnectableLayer* layer, + const Convolution2dDescriptor& convolution2dDescriptor, + const ConstTensor& weights, + const ConstTensor& biases, + const char* name = nullptr) override; + + /// Extract the quantized network INetworkPtr RetrieveFinalNetwork() { return std::move(m_QuantizedNetwork); } private: diff --git a/src/armnn/StaticRangeVisitor.cpp b/src/armnn/StaticRangeVisitor.cpp index eac434eecb..fa95938b37 100644 --- a/src/armnn/StaticRangeVisitor.cpp +++ b/src/armnn/StaticRangeVisitor.cpp @@ -59,6 +59,27 @@ void StaticRangeVisitor::VisitBatchNormalizationLayer(const IConnectableLayer* l SetRange(layer, 0, -15.0f, 15.0f); } +void StaticRangeVisitor::VisitConvolution2dLayer(const IConnectableLayer* layer, + const Convolution2dDescriptor& convolution2dDescriptor, + const ConstTensor& weights, + const char* name) +{ + boost::ignore_unused(convolution2dDescriptor); + boost::ignore_unused(weights); + boost::ignore_unused(name); + SetRange(layer, 0, -15.0f, 15.0f); +} + +void StaticRangeVisitor::VisitConvolution2dLayer(const IConnectableLayer* layer, + const Convolution2dDescriptor& convolution2dDescriptor, + const ConstTensor& weights, + const ConstTensor& biases, + const char* name) +{ + boost::ignore_unused(biases); + VisitConvolution2dLayer(layer, convolution2dDescriptor, weights, name); +} + void StaticRangeVisitor::VisitActivationLayer(const IConnectableLayer* layer, const ActivationDescriptor& activationDescriptor, const char* name) diff --git a/src/armnn/StaticRangeVisitor.hpp b/src/armnn/StaticRangeVisitor.hpp index 94a6ea0e50..81a0f4aede 100644 --- a/src/armnn/StaticRangeVisitor.hpp +++ b/src/armnn/StaticRangeVisitor.hpp @@ -35,6 +35,15 @@ public: const ConstTensor& beta, const ConstTensor& gamma, const char* name = nullptr) override; + void VisitConvolution2dLayer(const IConnectableLayer* layer, + const Convolution2dDescriptor& convolution2dDescriptor, + const ConstTensor& weights, + const char* name = nullptr) override; + void VisitConvolution2dLayer(const IConnectableLayer* layer, + const Convolution2dDescriptor& convolution2dDescriptor, + const ConstTensor& weights, + const ConstTensor& biases, + const char* name = nullptr) override; void VisitActivationLayer(const IConnectableLayer* layer, const ActivationDescriptor& activationDescriptor, const char* name = nullptr) override; diff --git a/src/armnn/test/QuantizerTest.cpp b/src/armnn/test/QuantizerTest.cpp index dd90368524..a960c6b772 100644 --- a/src/armnn/test/QuantizerTest.cpp +++ b/src/armnn/test/QuantizerTest.cpp @@ -566,5 +566,92 @@ BOOST_AUTO_TEST_CASE(QuantizeFullyConnectedBiasEnabled) ValidateFullyConnectedLayer(true); } +class TestConv2dQuantization : public TestQuantization +{ +public: + virtual void VisitConvolution2dLayer(const IConnectableLayer *layer, + const Convolution2dDescriptor &convolution2dDescriptor, + const ConstTensor &weights, + 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 weights const + BOOST_TEST((weights.GetInfo().GetDataType() == DataType::QuantisedAsymm8)); + BOOST_CHECK_CLOSE(weights.GetInfo().GetQuantizationScale(), 3.0f / 255.0f, 0.000001f); + BOOST_TEST((weights.GetInfo().GetQuantizationOffset() == 85)); + } + + virtual void VisitConvolution2dLayer(const IConnectableLayer *layer, + const Convolution2dDescriptor &convolution2dDescriptor, + const ConstTensor &weights, + const ConstTensor &biases, + const char *name = nullptr) + { + VisitConvolution2dLayer(layer, convolution2dDescriptor, weights, name); + + // test biases const + BOOST_TEST((biases.GetInfo().GetDataType() == DataType::QuantisedAsymm8)); + BOOST_CHECK_CLOSE(biases.GetInfo().GetQuantizationScale(), 3.0f / 255.0f, 0.000001f); + BOOST_TEST((biases.GetInfo().GetQuantizationOffset() == 85)); + } +}; + +void TestQuantizeConvolution2d(bool useBiases) +{ + auto network = INetwork::Create(); + + TensorShape shape{3U}; + TensorInfo info(shape, DataType::Float32); + + std::vector weightsData{-1.0f, 1.5f, 2.0f}; + ConstTensor weights(info, weightsData); + + Convolution2dDescriptor descriptor; + descriptor.m_BiasEnabled = useBiases; + + // Add the layers + IConnectableLayer* input0 = network->AddInputLayer(0); + IConnectableLayer* conv2d; + if (useBiases) + { + std::vector biasesData{-1.0f, 1.5f, 2.0f}; + ConstTensor biases(info, biasesData); + conv2d = network->AddConvolution2dLayer(descriptor, weights, biases); + } + else + { + conv2d = network->AddConvolution2dLayer(descriptor, weights); + } + IConnectableLayer* output = network->AddOutputLayer(1); + + // Establish connections + input0->GetOutputSlot(0).Connect(conv2d->GetInputSlot(0)); + conv2d->GetOutputSlot(0).Connect(output->GetInputSlot(0)); + + //Set TensorInfo + input0->GetOutputSlot(0).SetTensorInfo(info); + conv2d->GetOutputSlot(0).SetTensorInfo(info); + + auto quantizedNetwork = INetworkQuantizer::Create(network.get())->ExportNetwork(); + TestConv2dQuantization validator; + VisitLayersTopologically(quantizedNetwork.get(), validator); +} + +BOOST_AUTO_TEST_CASE(QuantizeConvolution2d) +{ + TestQuantizeConvolution2d(false); +} + +BOOST_AUTO_TEST_CASE(QuantizeConvolution2dWithBiases) +{ + TestQuantizeConvolution2d(true); +} + BOOST_AUTO_TEST_SUITE_END() } // namespace armnn -- cgit v1.2.1