From e604cde16fc16dc5fea9fc2c1c38ac8e8e2c4a59 Mon Sep 17 00:00:00 2001 From: FrancisMurtagh Date: Mon, 11 Feb 2019 11:06:33 +0000 Subject: IVGCVSW-2624 Support static quantization of FullyConnected Change-Id: Ib18085e7e4b2e9d55e29b14122410732e3bdd088 Signed-off-by: FrancisMurtagh --- src/armnn/QuantizerVisitor.cpp | 32 +++++++++++- src/armnn/QuantizerVisitor.hpp | 13 ++++- src/armnn/StaticRangeVisitor.cpp | 24 +++++++++ src/armnn/StaticRangeVisitor.hpp | 9 ++++ src/armnn/test/QuantizerTest.cpp | 107 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 182 insertions(+), 3 deletions(-) diff --git a/src/armnn/QuantizerVisitor.cpp b/src/armnn/QuantizerVisitor.cpp index b5085be0a2..97a8bc1ad2 100644 --- a/src/armnn/QuantizerVisitor.cpp +++ b/src/armnn/QuantizerVisitor.cpp @@ -79,7 +79,37 @@ void QuantizerVisitor::VisitActivationLayer(const IConnectableLayer* layer, SetQuantizedInputConnections(layer, newLayer); } -void QuantizerVisitor::VisitInputLayer(const IConnectableLayer* layer, LayerBindingId id, const char* name) +void QuantizerVisitor::VisitFullyConnectedLayer(const IConnectableLayer *layer, + const FullyConnectedDescriptor& desc, + const ConstTensor& weights, + const char *name) +{ + std::vector weightsBacking; + ConstTensor qWeights = CreateQuantizedConst(weights, weightsBacking); + + IConnectableLayer* newLayer = m_QuantizedNetwork->AddFullyConnectedLayer(desc, qWeights, name); + RecordLayer(layer, newLayer); + SetQuantizedInputConnections(layer, newLayer); +} + +void QuantizerVisitor::VisitFullyConnectedLayer(const IConnectableLayer *layer, + const FullyConnectedDescriptor& desc, + const ConstTensor& weights, + const ConstTensor& bias, + const char *name) +{ + std::vector weightsBacking; + ConstTensor qWeights = CreateQuantizedConst(weights, weightsBacking); + + std::vector biasBacking; + ConstTensor qBias = CreateQuantizedConst(bias, biasBacking); + + IConnectableLayer* newLayer = m_QuantizedNetwork->AddFullyConnectedLayer(desc, qWeights, qBias, name); + RecordLayer(layer, newLayer); + SetQuantizedInputConnections(layer, newLayer); +} + +void QuantizerVisitor::VisitInputLayer(const IConnectableLayer *layer, LayerBindingId id, const char *name) { IConnectableLayer* newLayer = m_QuantizedNetwork->AddInputLayer(id, name); RecordLayer(layer, newLayer); diff --git a/src/armnn/QuantizerVisitor.hpp b/src/armnn/QuantizerVisitor.hpp index dcaccd4ac7..53b3edc3fd 100644 --- a/src/armnn/QuantizerVisitor.hpp +++ b/src/armnn/QuantizerVisitor.hpp @@ -41,8 +41,17 @@ public: const ConstTensor& beta, const ConstTensor& gamma, const char* name = nullptr) override; - - /// Extract the quantized network + void VisitFullyConnectedLayer(const IConnectableLayer *layer, + const FullyConnectedDescriptor&, + const ConstTensor&, + const char *name = nullptr) override; + void VisitFullyConnectedLayer(const IConnectableLayer *layer, + const FullyConnectedDescriptor&, + const ConstTensor&, + const ConstTensor&, + 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 258d499279..eac434eecb 100644 --- a/src/armnn/StaticRangeVisitor.cpp +++ b/src/armnn/StaticRangeVisitor.cpp @@ -87,4 +87,28 @@ void StaticRangeVisitor::VisitActivationLayer(const IConnectableLayer* layer, } } +void StaticRangeVisitor::VisitFullyConnectedLayer(const armnn::IConnectableLayer *layer, + const armnn::FullyConnectedDescriptor& desc, + const ConstTensor& weights, + const char *name) +{ + boost::ignore_unused(desc); + boost::ignore_unused(weights); + boost::ignore_unused(name); + SetRange(layer, 0, -15.0f, 15.0f); +} + +void StaticRangeVisitor::VisitFullyConnectedLayer(const armnn::IConnectableLayer *layer, + const armnn::FullyConnectedDescriptor& desc, + const ConstTensor& weights, + const ConstTensor& bias, + const char *name) +{ + boost::ignore_unused(desc); + boost::ignore_unused(weights); + boost::ignore_unused(bias); + boost::ignore_unused(name); + SetRange(layer, 0, -15.0f, 15.0f); +} + } //namespace armnn diff --git a/src/armnn/StaticRangeVisitor.hpp b/src/armnn/StaticRangeVisitor.hpp index ea27947e7a..94a6ea0e50 100644 --- a/src/armnn/StaticRangeVisitor.hpp +++ b/src/armnn/StaticRangeVisitor.hpp @@ -38,6 +38,15 @@ public: void VisitActivationLayer(const IConnectableLayer* layer, const ActivationDescriptor& activationDescriptor, const char* name = nullptr) override; + void VisitFullyConnectedLayer(const armnn::IConnectableLayer *layer, + const armnn::FullyConnectedDescriptor& desc, + const ConstTensor& weights, + const char *name) override; + void VisitFullyConnectedLayer(const armnn::IConnectableLayer *layer, + const armnn::FullyConnectedDescriptor& desc, + const ConstTensor& weights, + const ConstTensor& bias, + const char *name) override; /// Retrieve the default range MinMaxRange DefaultRange() const { return std::make_pair(-15.0f, 15.0f); } diff --git a/src/armnn/test/QuantizerTest.cpp b/src/armnn/test/QuantizerTest.cpp index ba10fd8b79..dd90368524 100644 --- a/src/armnn/test/QuantizerTest.cpp +++ b/src/armnn/test/QuantizerTest.cpp @@ -459,5 +459,112 @@ BOOST_AUTO_TEST_CASE(OverrideInputRangeInputLayers) BOOST_CHECK(guidToRangesMap[input1->GetGuid()].at(0).second == minMaxRange.second); } +INetworkPtr CreateNetworkWithFullyConnectedLayer(const bool biasEnabled) +{ + FullyConnectedDescriptor desc; + desc.m_BiasEnabled = biasEnabled; + 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); + + // Add the layers + IConnectableLayer* input0 = network->AddInputLayer(0); + IConnectableLayer* fullyConnected; + if (desc.m_BiasEnabled) + { + std::vector biasData{10.0f, 20.0f, 30.0f}; + ConstTensor bias(info, biasData); + fullyConnected = network->AddFullyConnectedLayer(desc, weights, bias); + } + else + { + fullyConnected = network->AddFullyConnectedLayer(desc, weights); + } + IConnectableLayer* output = network->AddOutputLayer(1); + + // Establish connections + input0->GetOutputSlot(0).Connect(fullyConnected->GetInputSlot(0)); + fullyConnected->GetOutputSlot(0).Connect(output->GetInputSlot(0)); + + //Set TensorInfo + input0->GetOutputSlot(0).SetTensorInfo(info); + fullyConnected->GetOutputSlot(0).SetTensorInfo(info); + + return network; +} + +class TestFullyConnectedQuantization : public TestQuantization +{ +public: + virtual void VisitFullyConnectedLayer(const IConnectableLayer* layer, + const FullyConnectedDescriptor& desc, + 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 constants + 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 VisitFullyConnectedLayer(const IConnectableLayer* layer, + const FullyConnectedDescriptor& desc, + const ConstTensor& weights, + const ConstTensor& bias, + 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((weights.GetInfo().GetDataType() == DataType::QuantisedAsymm8)); + BOOST_TEST((bias.GetInfo().GetDataType() == DataType::QuantisedAsymm8)); + + BOOST_CHECK_CLOSE(weights.GetInfo().GetQuantizationScale(), 3.0f/255.0f, 0.000001f); + BOOST_CHECK_CLOSE(bias.GetInfo().GetQuantizationScale(), 30.0f/255.0f, 0.000001f); + + BOOST_TEST((weights.GetInfo().GetQuantizationOffset() == 85)); + } +}; + +void ValidateFullyConnectedLayer(const bool biasEnabled) +{ + auto network = CreateNetworkWithFullyConnectedLayer(biasEnabled); + auto quantizedNetwork = INetworkQuantizer::Create(network.get())->ExportNetwork(); + TestFullyConnectedQuantization validator; + VisitLayersTopologically(quantizedNetwork.get(), validator); +} + +BOOST_AUTO_TEST_CASE(QuantizeFullyConnected) +{ + ValidateFullyConnectedLayer(false); +} + +BOOST_AUTO_TEST_CASE(QuantizeFullyConnectedBiasEnabled) +{ + ValidateFullyConnectedLayer(true); +} + BOOST_AUTO_TEST_SUITE_END() } // namespace armnn -- cgit v1.2.1