From 5b2159e43a5e078053e706883e3af44ae733b5b9 Mon Sep 17 00:00:00 2001 From: Matteo Martincigh Date: Mon, 11 Feb 2019 13:24:38 +0000 Subject: IVGCVSW-2620 Support static quantization of Constant * Added VisitConstantLayer to QuantizerVisitor * Added unit tests and refactored QuantizerTest.cpp * Code cleanup Change-Id: I118fd2be085fc98879c5cfaa09698a7c98ba13f0 Signed-off-by: Matteo Martincigh --- include/armnn/INetwork.hpp | 2 +- src/armnn/OverrideInputRangeVisitor.hpp | 3 +- src/armnn/QuantizerVisitor.cpp | 13 +- src/armnn/QuantizerVisitor.hpp | 4 + src/armnn/StaticRangeVisitor.cpp | 31 +++ src/armnn/StaticRangeVisitor.hpp | 4 + src/armnn/layers/ConstantLayer.hpp | 4 +- src/armnn/test/ConstTensorLayerVisitor.hpp | 68 +++--- src/armnn/test/QuantizerTest.cpp | 318 +++++++++++++++++------------ src/armnn/test/TestLayerVisitor.cpp | 2 +- 10 files changed, 280 insertions(+), 169 deletions(-) diff --git a/include/armnn/INetwork.hpp b/include/armnn/INetwork.hpp index aaca13bba9..a59000b334 100644 --- a/include/armnn/INetwork.hpp +++ b/include/armnn/INetwork.hpp @@ -266,7 +266,7 @@ public: /// @param name - Optional name for the layer. /// @return - Interface for configuring the layer. virtual IConnectableLayer* AddConstantLayer(const ConstTensor& input, - const char* name = nullptr) = 0; + const char* name = nullptr) = 0; /// Adds a reshape layer to the network. /// @param reshapeDescriptor - Parameters for the reshape operation. diff --git a/src/armnn/OverrideInputRangeVisitor.hpp b/src/armnn/OverrideInputRangeVisitor.hpp index f09eeb9f50..511c851bef 100644 --- a/src/armnn/OverrideInputRangeVisitor.hpp +++ b/src/armnn/OverrideInputRangeVisitor.hpp @@ -18,8 +18,7 @@ namespace armnn class OverrideInputRangeVisitor : public LayerVisitorBase { private: - using MinMaxRange = std::pair; - using MinMaxRanges = std::vector; + using MinMaxRange = RangeTracker::MinMaxRange; public: OverrideInputRangeVisitor(RangeTracker& ranges, diff --git a/src/armnn/QuantizerVisitor.cpp b/src/armnn/QuantizerVisitor.cpp index 850a3c1fae..8e3265fb75 100644 --- a/src/armnn/QuantizerVisitor.cpp +++ b/src/armnn/QuantizerVisitor.cpp @@ -44,7 +44,7 @@ void QuantizerVisitor::SetQuantizedInputConnections(const IConnectableLayer* src newOutputSlot.Connect(newInputSlot); // Fetch the min/max ranges that were computed earlier - auto range = m_Ranges.GetRange(layerToFind.GetGuid(), i); + auto range = m_Ranges.GetRange(layerToFind.GetGuid(), slotIdx); auto qParams = ComputeQAsymmParams(8, range.first, range.second); // Set the quantization params @@ -242,4 +242,15 @@ void QuantizerVisitor::VisitSoftmaxLayer(const IConnectableLayer* layer, SetQuantizedInputConnections(layer, newLayer); } +void QuantizerVisitor::VisitConstantLayer(const IConnectableLayer* layer, + const ConstTensor& input, + const char* name) +{ + std::vector inputBacking; + ConstTensor qInput = CreateQuantizedConst(input, inputBacking); + + IConnectableLayer* newLayer = m_QuantizedNetwork->AddConstantLayer(qInput, name); + RecordLayer(layer, newLayer); +} + } //namespace armnn diff --git a/src/armnn/QuantizerVisitor.hpp b/src/armnn/QuantizerVisitor.hpp index 8829bdfaa8..1beaf5adde 100644 --- a/src/armnn/QuantizerVisitor.hpp +++ b/src/armnn/QuantizerVisitor.hpp @@ -80,6 +80,10 @@ public: const Pooling2dDescriptor& pooling2dDescriptor, const char* name = nullptr) override; + void VisitConstantLayer(const IConnectableLayer* layer, + const ConstTensor& input, + 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 b1cbb2d574..cc1255e56e 100644 --- a/src/armnn/StaticRangeVisitor.cpp +++ b/src/armnn/StaticRangeVisitor.cpp @@ -9,6 +9,8 @@ #include #include +#include + namespace armnn { @@ -150,4 +152,33 @@ void StaticRangeVisitor::VisitSoftmaxLayer(const IConnectableLayer* layer, SetRange(layer, 0, 0.f, 1.f); } +void StaticRangeVisitor::VisitConstantLayer(const IConnectableLayer* layer, + const ConstTensor& input, + const char* name) +{ + boost::ignore_unused(name); + + if (input.GetDataType() != DataType::Float32) + { + throw InvalidArgumentException("Quantization is supported only for FP32 tensors"); + } + + // Work out the range based on the input constants + unsigned int inputNumElements = input.GetNumElements(); + const float* inputData = reinterpret_cast(input.GetMemoryArea()); + + float min = std::numeric_limits::max(); + float max = std::numeric_limits::lowest(); + + for (unsigned int i = 0; i < inputNumElements; i++) + { + const float inputValue = inputData[i]; + + min = std::min(min, inputValue); + max = std::max(max, inputValue); + } + + SetRange(layer, 0, min, max); +} + } //namespace armnn diff --git a/src/armnn/StaticRangeVisitor.hpp b/src/armnn/StaticRangeVisitor.hpp index a7028d7a51..2f80dcb45b 100644 --- a/src/armnn/StaticRangeVisitor.hpp +++ b/src/armnn/StaticRangeVisitor.hpp @@ -71,6 +71,10 @@ public: const SoftmaxDescriptor& softmaxDescriptor, const char* name = nullptr) override; + void VisitConstantLayer(const IConnectableLayer* layer, + const ConstTensor& input, + const char* name = nullptr) override; + private: /// Set the range for an output slot on a layer void SetRange(const IConnectableLayer* layer, unsigned int outputIdx, float min, float max); diff --git a/src/armnn/layers/ConstantLayer.hpp b/src/armnn/layers/ConstantLayer.hpp index d045952e7e..7a6cf9d10d 100644 --- a/src/armnn/layers/ConstantLayer.hpp +++ b/src/armnn/layers/ConstantLayer.hpp @@ -37,11 +37,12 @@ public: std::vector InferOutputShapes(const std::vector& inputShapes) const override; /// Free up the constant source data stored by the layer. - void ReleaseConstantData() override {}; + void ReleaseConstantData() override {} void Accept(ILayerVisitor& visitor) const override; std::unique_ptr m_LayerOutput; + protected: /// Constructor to create a ConstantLayer. /// @param [in] name Optional name for the layer. @@ -52,7 +53,6 @@ protected: /// Retrieve the handles to the constant values stored by the layer. ConstantTensors GetConstantTensorsByRef() override { return {m_LayerOutput}; } - }; } // namespace diff --git a/src/armnn/test/ConstTensorLayerVisitor.hpp b/src/armnn/test/ConstTensorLayerVisitor.hpp index 513a471465..80409b331f 100644 --- a/src/armnn/test/ConstTensorLayerVisitor.hpp +++ b/src/armnn/test/ConstTensorLayerVisitor.hpp @@ -17,12 +17,14 @@ public: explicit TestConvolution2dLayerVisitor(const Convolution2dDescriptor& convolution2dDescriptor, const ConstTensor& weights, const Optional& biases, - const char* name = nullptr) : TestLayerVisitor(name), - m_Descriptor(convolution2dDescriptor), - m_Weights(weights), - m_Biases(biases) {}; + const char* name = nullptr) + : TestLayerVisitor(name) + , m_Descriptor(convolution2dDescriptor) + , m_Weights(weights) + , m_Biases(biases) + {} - virtual ~TestConvolution2dLayerVisitor() {}; + virtual ~TestConvolution2dLayerVisitor() {} void VisitConvolution2dLayer(const IConnectableLayer* layer, const Convolution2dDescriptor& convolution2dDescriptor, @@ -52,12 +54,14 @@ public: explicit TestDepthwiseConvolution2dLayerVisitor(const DepthwiseConvolution2dDescriptor& descriptor, const ConstTensor& weights, const Optional& biases, - const char* name = nullptr) : TestLayerVisitor(name), - m_Descriptor(descriptor), - m_Weights(weights), - m_Biases(biases) {}; + const char* name = nullptr) + : TestLayerVisitor(name) + , m_Descriptor(descriptor) + , m_Weights(weights) + , m_Biases(biases) + {} - virtual ~TestDepthwiseConvolution2dLayerVisitor() {}; + virtual ~TestDepthwiseConvolution2dLayerVisitor() {} void VisitDepthwiseConvolution2dLayer(const IConnectableLayer* layer, const DepthwiseConvolution2dDescriptor& convolution2dDescriptor, @@ -87,12 +91,14 @@ public: explicit TestFullyConnectedLayerVistor(const FullyConnectedDescriptor& descriptor, const ConstTensor& weights, const Optional biases, - const char* name = nullptr) : TestLayerVisitor(name), - m_Descriptor(descriptor), - m_Weights(weights), - m_Biases(biases) {}; + const char* name = nullptr) + : TestLayerVisitor(name) + , m_Descriptor(descriptor) + , m_Weights(weights) + , m_Biases(biases) + {} - virtual ~TestFullyConnectedLayerVistor() {}; + virtual ~TestFullyConnectedLayerVistor() {} void VisitFullyConnectedLayer(const IConnectableLayer* layer, const FullyConnectedDescriptor& fullyConnectedDescriptor, @@ -123,12 +129,15 @@ public: const ConstTensor& variance, const ConstTensor& beta, const ConstTensor& gamma, - const char* name = nullptr) : TestLayerVisitor(name), - m_Descriptor(descriptor), - m_Mean(mean), - m_Variance(variance), - m_Beta(beta), - m_Gamma(gamma) {}; + const char* name = nullptr) + : TestLayerVisitor(name) + , m_Descriptor(descriptor) + , m_Mean(mean) + , m_Variance(variance) + , m_Beta(beta) + , m_Gamma(gamma) + {} + void VisitBatchNormalizationLayer(const IConnectableLayer* layer, const BatchNormalizationDescriptor& descriptor, const ConstTensor& mean, @@ -148,6 +157,7 @@ public: protected: void CheckDescriptor(const BatchNormalizationDescriptor& descriptor); + private: BatchNormalizationDescriptor m_Descriptor; ConstTensor m_Mean; @@ -159,8 +169,11 @@ private: class TestConstantLayerVisitor : public TestLayerVisitor { public: - explicit TestConstantLayerVisitor(const ConstTensor& input, const char* name = nullptr) : - TestLayerVisitor(name), m_Input(input) {}; + explicit TestConstantLayerVisitor(const ConstTensor& input, + const char* name = nullptr) + : TestLayerVisitor(name) + , m_Input(input) + {} void VisitConstantLayer(const IConnectableLayer* layer, const ConstTensor& input, @@ -180,8 +193,11 @@ class TestLstmLayerVisitor : public TestLayerVisitor public: explicit TestLstmLayerVisitor(const LstmDescriptor& descriptor, const LstmInputParams& params, - const char* name = nullptr) : - TestLayerVisitor(name), m_Descriptor(descriptor), m_InputParams(params) {}; + const char* name = nullptr) + : TestLayerVisitor(name) + , m_Descriptor(descriptor) + , m_InputParams(params) + {} void VisitLstmLayer(const IConnectableLayer* layer, const LstmDescriptor& descriptor, @@ -204,4 +220,4 @@ private: LstmInputParams m_InputParams; }; -} // 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 5fcfc5d12e..6820e14f16 100644 --- a/src/armnn/test/QuantizerTest.cpp +++ b/src/armnn/test/QuantizerTest.cpp @@ -25,14 +25,17 @@ using MinMaxRange = std::pair; using MinMaxRanges = std::vector; using MinMaxRangeMap = std::unordered_map; +const float g_QuantizationBase = 255.0f; +const float g_TestTolerance = 0.000001f; + BOOST_AUTO_TEST_SUITE(Quantizer) class TestQuantization : public LayerVisitorBase { public: - virtual void VisitInputLayer(const IConnectableLayer* layer, - LayerBindingId id, - const char* name = nullptr) + void VisitInputLayer(const IConnectableLayer* layer, + LayerBindingId id, + const char* name = nullptr) override { TensorInfo info = layer->GetOutputSlot(0).GetTensorInfo(); @@ -41,12 +44,12 @@ public: BOOST_TEST((info.GetQuantizationOffset() == 128)); // Based off current default [-15.0f, 15.0f] - BOOST_CHECK_CLOSE(info.GetQuantizationScale(), 30.0f/255.0f, 0.000001f); + BOOST_CHECK_CLOSE(info.GetQuantizationScale(), 30.0f / g_QuantizationBase, g_TestTolerance); } - virtual void VisitOutputLayer(const IConnectableLayer* layer, - LayerBindingId id, - const char* name = nullptr) + void VisitOutputLayer(const IConnectableLayer* layer, + LayerBindingId id, + const char* name = nullptr) override {} }; @@ -58,25 +61,25 @@ void VisitLayersTopologically(const INetwork* inputNetwork, ILayerVisitor& visit VisitLayers(graph, visitor); } -BOOST_AUTO_TEST_CASE(QuantizeAddition) +class TestAdditionQuantization : public TestQuantization { - class TestAdditionQuantization : public TestQuantization +public: + void VisitAdditionLayer(const IConnectableLayer* layer, + const char* name = nullptr) override { - public: - virtual void VisitAdditionLayer(const IConnectableLayer* layer, - const char* name = nullptr) - { - TensorInfo info = layer->GetOutputSlot(0).GetTensorInfo(); + TensorInfo info = layer->GetOutputSlot(0).GetTensorInfo(); - BOOST_TEST((info.GetDataType() == DataType::QuantisedAsymm8)); + BOOST_TEST((info.GetDataType() == DataType::QuantisedAsymm8)); - BOOST_TEST((info.GetQuantizationOffset() == 128)); + BOOST_TEST((info.GetQuantizationOffset() == 128)); - // Based off current static value [-20.0f, 20.0f] - BOOST_CHECK_CLOSE(info.GetQuantizationScale(), 40.0f/255.0f, 0.000001f); - } - }; + // Based off current static value [-20.0f, 20.0f] + BOOST_CHECK_CLOSE(info.GetQuantizationScale(), 40.0f / g_QuantizationBase, g_TestTolerance); + } +}; +BOOST_AUTO_TEST_CASE(QuantizeAddition) +{ auto network = INetwork::Create(); // Add the layers @@ -90,7 +93,7 @@ BOOST_AUTO_TEST_CASE(QuantizeAddition) input1->GetOutputSlot(0).Connect(addition->GetInputSlot(1)); addition->GetOutputSlot(0).Connect(output->GetInputSlot(0)); - //Set TensorInfo + // Set TensorInfo TensorShape shape{1U}; TensorInfo info(shape, DataType::Float32); input0->GetOutputSlot(0).SetTensorInfo(info); @@ -105,9 +108,9 @@ BOOST_AUTO_TEST_CASE(QuantizeAddition) class TestActivationQuantization : public TestQuantization { public: - virtual void VisitActivationLayer(const IConnectableLayer* layer, - const ActivationDescriptor& descriptor, - const char* name = nullptr) + void VisitActivationLayer(const IConnectableLayer* layer, + const ActivationDescriptor& descriptor, + const char* name = nullptr) override { TensorInfo info = layer->GetOutputSlot(0).GetTensorInfo(); @@ -116,13 +119,14 @@ public: BOOST_TEST((info.GetQuantizationOffset() == 0)); // Based off current static value [-20.0f, 20.0f] - BOOST_CHECK_CLOSE(info.GetQuantizationScale(), 15.0f/255.0f, 0.000001f); + BOOST_CHECK_CLOSE(info.GetQuantizationScale(), 15.0f / g_QuantizationBase, g_TestTolerance); } }; INetworkPtr CreateNetworkWithActivationLayer(const ActivationDescriptor& descriptor) { auto network = INetwork::Create(); + // Add the layers IConnectableLayer* input0 = network->AddInputLayer(0); IConnectableLayer* activation = network->AddActivationLayer(descriptor); @@ -132,7 +136,7 @@ INetworkPtr CreateNetworkWithActivationLayer(const ActivationDescriptor& descrip input0->GetOutputSlot(0).Connect(activation->GetInputSlot(0)); activation->GetOutputSlot(0).Connect(output->GetInputSlot(0)); - //Set TensorInfo + // Set TensorInfo TensorShape shape{1U}; TensorInfo info(shape, DataType::Float32); input0->GetOutputSlot(0).SetTensorInfo(info); @@ -197,26 +201,26 @@ BOOST_AUTO_TEST_CASE(QuantizeSoftReLuActivation) VisitLayersTopologically(quantizedNetwork.get(), validator); } -BOOST_AUTO_TEST_CASE(QuantizeBoundedReluActivation) +class TestBoundedReluActivationQuantization : public TestQuantization { - class TestBoundedReluActivationQuantization : public TestQuantization +public: + void VisitActivationLayer(const IConnectableLayer* layer, + const ActivationDescriptor& descriptor, + const char* name = nullptr) override { - public: - virtual void VisitActivationLayer(const IConnectableLayer* layer, - const ActivationDescriptor& descriptor, - const char* name = nullptr) - { - TensorInfo info = layer->GetOutputSlot(0).GetTensorInfo(); + TensorInfo info = layer->GetOutputSlot(0).GetTensorInfo(); - BOOST_TEST((info.GetDataType() == DataType::QuantisedAsymm8)); + BOOST_TEST((info.GetDataType() == DataType::QuantisedAsymm8)); - BOOST_TEST((info.GetQuantizationOffset() == 0)); + BOOST_TEST((info.GetQuantizationOffset() == 0)); - // Based off current static value [0.0f, 3.5f(<-layer upper bound)] - BOOST_CHECK_CLOSE(info.GetQuantizationScale(), 3.5f/255.0f, 0.000001f); - } - }; + // Based off current static value [0.0f, 3.5f(<-layer upper bound)] + BOOST_CHECK_CLOSE(info.GetQuantizationScale(), 3.5f / g_QuantizationBase, g_TestTolerance); + } +}; +BOOST_AUTO_TEST_CASE(QuantizeBoundedReluActivation) +{ ActivationDescriptor descriptor; descriptor.m_Function = ActivationFunction::BoundedReLu; descriptor.m_A = 3.5f; @@ -229,26 +233,26 @@ BOOST_AUTO_TEST_CASE(QuantizeBoundedReluActivation) VisitLayersTopologically(quantizedNetwork.get(), validator); } -BOOST_AUTO_TEST_CASE(QuantizeTanHActivation) +class TestTanHActivationQuantization : public TestQuantization { - class TestTanHActivationQuantization : public TestQuantization +public: + void VisitActivationLayer(const IConnectableLayer* layer, + const ActivationDescriptor& descriptor, + const char* name = nullptr) override { - public: - virtual void VisitActivationLayer(const IConnectableLayer* layer, - const ActivationDescriptor& descriptor, - const char* name = nullptr) - { - TensorInfo info = layer->GetOutputSlot(0).GetTensorInfo(); + TensorInfo info = layer->GetOutputSlot(0).GetTensorInfo(); - BOOST_TEST((info.GetDataType() == DataType::QuantisedAsymm8)); + BOOST_TEST((info.GetDataType() == DataType::QuantisedAsymm8)); - BOOST_TEST((info.GetQuantizationOffset() == 128)); + BOOST_TEST((info.GetQuantizationOffset() == 128)); - // Based off current static value [-1.0f, 1.0f] - BOOST_CHECK_CLOSE(info.GetQuantizationScale(), 2.0f/255.0f, 0.000001f); - } - }; + // Based off current static value [-1.0f, 1.0f] + BOOST_CHECK_CLOSE(info.GetQuantizationScale(), 2.0f / g_QuantizationBase, g_TestTolerance); + } +}; +BOOST_AUTO_TEST_CASE(QuantizeTanHActivation) +{ ActivationDescriptor descriptor; descriptor.m_Function = ActivationFunction::TanH; descriptor.m_A = 3.5f; @@ -264,9 +268,9 @@ BOOST_AUTO_TEST_CASE(QuantizeTanHActivation) class TestLeakyReLuActivationQuantization : public TestQuantization { public: - virtual void VisitActivationLayer(const IConnectableLayer* layer, - const ActivationDescriptor& descriptor, - const char* name = nullptr) + void VisitActivationLayer(const IConnectableLayer* layer, + const ActivationDescriptor& descriptor, + const char* name = nullptr) override { TensorInfo info = layer->GetOutputSlot(0).GetTensorInfo(); @@ -275,7 +279,7 @@ public: BOOST_TEST((info.GetQuantizationOffset() == 64)); // Based off current static value [-5.0f, 15.0f] - BOOST_CHECK_CLOSE(info.GetQuantizationScale(), 20.0f/255.0f, 0.000001f); + BOOST_CHECK_CLOSE(info.GetQuantizationScale(), 20.0f / g_QuantizationBase, g_TestTolerance); } protected: // used by the descendant classes which test layers @@ -307,54 +311,44 @@ BOOST_AUTO_TEST_CASE(QuantizeLeakyReLuActivation) VisitLayersTopologically(quantizedNetwork.get(), validator); } -BOOST_AUTO_TEST_CASE(QuantizeBatchNorm) +class TestBatchNormalizationQuantization : public TestQuantization { - - class TestQuantization : public LayerVisitorBase +public: + 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 { - 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)); + TensorInfo info = layer->GetOutputSlot(0).GetTensorInfo(); - // Based off current static value [-15.0f, 15.0f] - BOOST_CHECK_CLOSE(info.GetQuantizationScale(), 30.0f/255.0f, 0.000001f); + BOOST_TEST((info.GetDataType() == DataType::QuantisedAsymm8)); - //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_TEST((info.GetQuantizationOffset() == 128)); - 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); + // Based off current static value [-15.0f, 15.0f] + BOOST_CHECK_CLOSE(info.GetQuantizationScale(), 30.0f / g_QuantizationBase, g_TestTolerance); - BOOST_TEST((mean.GetInfo().GetQuantizationOffset() == 85)); - } + // 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)); - virtual void VisitInputLayer(const IConnectableLayer* layer, - LayerBindingId id, - const char* name = nullptr) - {} + float expectedQuantizationScale = 3.0f / g_QuantizationBase; + BOOST_CHECK_CLOSE(mean.GetInfo().GetQuantizationScale(), expectedQuantizationScale, g_TestTolerance); + BOOST_CHECK_CLOSE(variance.GetInfo().GetQuantizationScale(), expectedQuantizationScale, g_TestTolerance); + BOOST_CHECK_CLOSE(beta.GetInfo().GetQuantizationScale(), expectedQuantizationScale, g_TestTolerance); + BOOST_CHECK_CLOSE(gamma.GetInfo().GetQuantizationScale(), expectedQuantizationScale, g_TestTolerance); - virtual void VisitOutputLayer(const IConnectableLayer* layer, - LayerBindingId id, - const char* name = nullptr) - {} - }; + BOOST_TEST((mean.GetInfo().GetQuantizationOffset() == 85)); + } +}; +BOOST_AUTO_TEST_CASE(QuantizeBatchNorm) +{ auto network = INetwork::Create(); TensorShape shape{3U}; @@ -381,12 +375,12 @@ BOOST_AUTO_TEST_CASE(QuantizeBatchNorm) input0->GetOutputSlot(0).Connect(batchNorm->GetInputSlot(0)); batchNorm->GetOutputSlot(0).Connect(output->GetInputSlot(0)); - //Set TensorInfo + // Set TensorInfo input0->GetOutputSlot(0).SetTensorInfo(info); batchNorm->GetOutputSlot(0).SetTensorInfo(info); auto quantizedNetwork = INetworkQuantizer::Create(network.get())->ExportNetwork(); - TestQuantization validator; + TestBatchNormalizationQuantization validator; VisitLayersTopologically(quantizedNetwork.get(), validator); } @@ -501,7 +495,7 @@ INetworkPtr CreateNetworkWithFullyConnectedLayer(const bool biasEnabled) input0->GetOutputSlot(0).Connect(fullyConnected->GetInputSlot(0)); fullyConnected->GetOutputSlot(0).Connect(output->GetInputSlot(0)); - //Set TensorInfo + // Set TensorInfo input0->GetOutputSlot(0).SetTensorInfo(info); fullyConnected->GetOutputSlot(0).SetTensorInfo(info); @@ -524,18 +518,20 @@ public: 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 ); + BOOST_CHECK_CLOSE(info.GetQuantizationScale(), 30.0f / g_QuantizationBase, g_TestTolerance ); - //Test weights + // Test weights BOOST_TEST((weights.GetInfo().GetDataType() == DataType::QuantisedAsymm8)); - BOOST_CHECK_CLOSE(weights.GetInfo().GetQuantizationScale(), 3.0f/255.0f, 0.000001f); + BOOST_CHECK_CLOSE(weights.GetInfo().GetQuantizationScale(), 3.0f / g_QuantizationBase, g_TestTolerance); BOOST_TEST((weights.GetInfo().GetQuantizationOffset() == 85)); // Test biases if (biases.has_value()) { BOOST_TEST((biases.value().GetInfo().GetDataType() == DataType::QuantisedAsymm8)); - BOOST_CHECK_CLOSE(biases.value().GetInfo().GetQuantizationScale(), 30.0f/255.0f, 0.000001f); + BOOST_CHECK_CLOSE(biases.value().GetInfo().GetQuantizationScale(), + 30.0f / g_QuantizationBase, + g_TestTolerance); } } }; @@ -561,23 +557,23 @@ BOOST_AUTO_TEST_CASE(QuantizeFullyConnectedBiasEnabled) class TestConv2dQuantization : public TestQuantization { public: - virtual void VisitConvolution2dLayer(const IConnectableLayer *layer, - const Convolution2dDescriptor& convolution2dDescriptor, - const ConstTensor& weights, - const Optional& biases, - const char *name = nullptr) + void VisitConvolution2dLayer(const IConnectableLayer *layer, + const Convolution2dDescriptor& convolution2dDescriptor, + const ConstTensor& weights, + const Optional& biases, + const char *name = nullptr) override { 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); + BOOST_CHECK_CLOSE(info.GetQuantizationScale(), 30.0f / g_QuantizationBase, g_TestTolerance); // Test weights // Instantiate expected values - const float quantizationScale = 3.0f / 255.0f; - const float tolerance = 3.0f / 255.0f; + const float quantizationScale = 3.0f / g_QuantizationBase; + const float tolerance = 3.0f / g_QuantizationBase; const int quantizationOffset = 85; BOOST_TEST((weights.GetInfo().GetDataType() == DataType::QuantisedAsymm8)); BOOST_CHECK_CLOSE(weights.GetInfo().GetQuantizationScale(), quantizationScale, tolerance); @@ -625,7 +621,7 @@ void TestQuantizeConvolution2d(bool useBiases) input0->GetOutputSlot(0).Connect(conv2d->GetInputSlot(0)); conv2d->GetOutputSlot(0).Connect(output->GetInputSlot(0)); - //Set TensorInfo + // Set TensorInfo input0->GetOutputSlot(0).SetTensorInfo(info); conv2d->GetOutputSlot(0).SetTensorInfo(info); @@ -647,23 +643,23 @@ BOOST_AUTO_TEST_CASE(QuantizeConvolution2dWithBiases) class TestDepthwiseConv2dQuantization : public TestQuantization { public: - virtual void VisitDepthwiseConvolution2dLayer(const IConnectableLayer *layer, - const DepthwiseConvolution2dDescriptor& desc, - const ConstTensor& weights, - const Optional& biases, - const char *name = nullptr) + void VisitDepthwiseConvolution2dLayer(const IConnectableLayer *layer, + const DepthwiseConvolution2dDescriptor& desc, + const ConstTensor& weights, + const Optional& biases, + const char *name = nullptr) override { 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); + BOOST_CHECK_CLOSE(info.GetQuantizationScale(), 30.0f / g_QuantizationBase, g_TestTolerance); // Test weights // Instantiate expected values - const float quantizationScale = 3.0f / 255.0f; - const float tolerance = 3.0f / 255.0f; + const float quantizationScale = 3.0f / g_QuantizationBase; + const float tolerance = 3.0f / g_QuantizationBase; const int quantizationOffset = 85; BOOST_TEST((weights.GetInfo().GetDataType() == DataType::QuantisedAsymm8)); BOOST_CHECK_CLOSE(weights.GetInfo().GetQuantizationScale(), quantizationScale, tolerance); @@ -733,9 +729,9 @@ BOOST_AUTO_TEST_CASE(QuantizeDepthwiseConvolution2dWithBiases) class TestSoftmaxQuantization : public TestQuantization { public: - virtual void VisitSoftmaxLayer(const IConnectableLayer* layer, - const SoftmaxDescriptor& descriptor, - const char* name = nullptr) + void VisitSoftmaxLayer(const IConnectableLayer* layer, + const SoftmaxDescriptor& descriptor, + const char* name = nullptr) override { TensorInfo info = layer->GetOutputSlot(0).GetTensorInfo(); @@ -743,13 +739,14 @@ public: BOOST_TEST((info.GetQuantizationOffset() == 0)); - BOOST_CHECK_CLOSE(info.GetQuantizationScale(), 1.0f/255.0f, 0.000001f ); + BOOST_CHECK_CLOSE(info.GetQuantizationScale(), 1.0f / g_QuantizationBase, g_TestTolerance ); } }; INetworkPtr CreateNetworkWithSoftmaxLayer(const SoftmaxDescriptor& descriptor) { auto network = INetwork::Create(); + // Add the layers IConnectableLayer* input0 = network->AddInputLayer(0); IConnectableLayer* softmax = network->AddSoftmaxLayer(descriptor); @@ -759,7 +756,7 @@ INetworkPtr CreateNetworkWithSoftmaxLayer(const SoftmaxDescriptor& descriptor) input0->GetOutputSlot(0).Connect(softmax->GetInputSlot(0)); softmax->GetOutputSlot(0).Connect(output->GetInputSlot(0)); - //Set TensorInfo + // Set TensorInfo TensorShape shape{1U}; TensorInfo info(shape, DataType::Float32); input0->GetOutputSlot(0).SetTensorInfo(info); @@ -822,9 +819,9 @@ BOOST_AUTO_TEST_CASE(QuantizePermute) class TestPermuteQuantization : public TestLeakyReLuActivationQuantization { public: - virtual void VisitPermuteLayer(const IConnectableLayer* layer, - const PermuteDescriptor& desc, - const char* name = nullptr) + void VisitPermuteLayer(const IConnectableLayer* layer, + const PermuteDescriptor& desc, + const char* name = nullptr) override { CheckForwardedQuantizationSettings(layer); } @@ -853,9 +850,9 @@ BOOST_AUTO_TEST_CASE(QuantizeSpaceToBatch) class TestSpaceToBatchQuantization : public TestLeakyReLuActivationQuantization { public: - virtual void VisitSpaceToBatchNdLayer(const IConnectableLayer* layer, - const SpaceToBatchNdDescriptor& spaceToBatchNdDescriptor, - const char* name = nullptr) override + void VisitSpaceToBatchNdLayer(const IConnectableLayer* layer, + const SpaceToBatchNdDescriptor& spaceToBatchNdDescriptor, + const char* name = nullptr) override { CheckForwardedQuantizationSettings(layer); } @@ -882,9 +879,9 @@ BOOST_AUTO_TEST_CASE(QuantizeSpaceToBatch) class TestPooling2dQuantization : public TestLeakyReLuActivationQuantization { public: - virtual void VisitPooling2dLayer(const IConnectableLayer* layer, - const Pooling2dDescriptor& desc, - const char* name = nullptr) + void VisitPooling2dLayer(const IConnectableLayer* layer, + const Pooling2dDescriptor& desc, + const char* name = nullptr) override { TensorInfo info = layer->GetOutputSlot(0).GetTensorInfo(); @@ -893,7 +890,7 @@ public: BOOST_TEST((info.GetQuantizationOffset() == 64)); // Based off parent LeakyReLu [-5.f, 15.f] - BOOST_CHECK_CLOSE(info.GetQuantizationScale(), 20.0f/255.0f, 0.000001f); + BOOST_CHECK_CLOSE(info.GetQuantizationScale(), 20.0f / g_QuantizationBase, g_TestTolerance); } }; @@ -931,5 +928,54 @@ BOOST_AUTO_TEST_CASE(QuantizePooling2d) VisitLayersTopologically(quantizedNetwork.get(), validator); } +class TestConstantQuantization : public TestAdditionQuantization +{ +public: + void VisitConstantLayer(const IConnectableLayer* layer, + const ConstTensor& input, + const char* name = nullptr) override + { + BOOST_CHECK(std::string(name) == "ConstantLayer"); + + TensorInfo info = layer->GetOutputSlot(0).GetTensorInfo(); + BOOST_CHECK(info.GetDataType() == DataType::QuantisedAsymm8); + BOOST_CHECK(info.GetQuantizationOffset() == 64); + + // Based off the range of values in the const tensor used for the test: [-2.0f, 6.0f] + BOOST_CHECK_CLOSE(info.GetQuantizationScale(), 8.0f / g_QuantizationBase, g_TestTolerance); + } +}; + +BOOST_AUTO_TEST_CASE(QuantizeConstant) +{ + auto network = INetwork::Create(); + + // Constant layer data + const char* name = "ConstantLayer"; + std::vector data = {-2.0f, -1.0f, 0.0f, 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f}; + std::vector dimensions = {1, 1, 3, 3}; + TensorInfo tensorInfo(4, dimensions.data(), DataType::Float32); + ConstTensor constantTensor(tensorInfo, data); + + // Add the layers + IConnectableLayer* input = network->AddInputLayer(0); + IConnectableLayer* constant = network->AddConstantLayer(constantTensor, name); + IConnectableLayer* addition = network->AddAdditionLayer(); + IConnectableLayer* output = network->AddOutputLayer(1); + + // Establish connections + input->GetOutputSlot(0).Connect(addition->GetInputSlot(0)); + constant->GetOutputSlot(0).Connect(addition->GetInputSlot(1)); + addition->GetOutputSlot(0).Connect(output->GetInputSlot(0)); + + // Set TensorInfo in the remaining layers + input->GetOutputSlot(0).SetTensorInfo(tensorInfo); + addition->GetOutputSlot(0).SetTensorInfo(tensorInfo); + + auto quantizedNetwork = INetworkQuantizer::Create(network.get())->ExportNetwork(); + TestConstantQuantization validator; + VisitLayersTopologically(quantizedNetwork.get(), validator); +} + BOOST_AUTO_TEST_SUITE_END() } // namespace armnn diff --git a/src/armnn/test/TestLayerVisitor.cpp b/src/armnn/test/TestLayerVisitor.cpp index 4c028b3709..ba30dbc666 100644 --- a/src/armnn/test/TestLayerVisitor.cpp +++ b/src/armnn/test/TestLayerVisitor.cpp @@ -23,7 +23,7 @@ void TestLayerVisitor::CheckLayerName(const char* name) { BOOST_CHECK_EQUAL(m_LayerName, name); } -}; +} void TestLayerVisitor::CheckLayerPointer(const IConnectableLayer* layer) { -- cgit v1.2.1