From ee18dc8d1725f472850ab0c398fd7cbc4b850891 Mon Sep 17 00:00:00 2001 From: James Conroy Date: Wed, 17 Jul 2019 11:27:46 +0100 Subject: IVGCVSW-3469 Add front end for Quantized LSTM layer * Added new layer QuantizedLstm (Android Q) * Made necessary changes to APIs * Added unit tests Change-Id: I3b9f16b0e7e49f51932cf204c87cb7118798123a Signed-off-by: James Conroy --- src/armnn/test/ConstTensorLayerVisitor.cpp | 237 +++++++++++++++++++++++++++++ src/armnn/test/ConstTensorLayerVisitor.hpp | 29 ++++ src/armnn/test/InferOutputTests.cpp | 3 + src/armnn/test/InferOutputTests.hpp | 48 +++++- 4 files changed, 316 insertions(+), 1 deletion(-) (limited to 'src/armnn/test') diff --git a/src/armnn/test/ConstTensorLayerVisitor.cpp b/src/armnn/test/ConstTensorLayerVisitor.cpp index e17ee46c81..cfcdb1d2ff 100644 --- a/src/armnn/test/ConstTensorLayerVisitor.cpp +++ b/src/armnn/test/ConstTensorLayerVisitor.cpp @@ -107,6 +107,64 @@ void TestLstmLayerVisitor::CheckInputParameters(const LstmInputParams& inputPara CheckConstTensorPtrs("CellBias", m_InputParams.m_CellBias, inputParams.m_CellBias); } +void TestQuantizedLstmLayerVisitor::CheckConstTensorPtrs(const std::string& name, + const ConstTensor* expected, + const ConstTensor* actual) +{ + if (expected == nullptr) + { + BOOST_CHECK_MESSAGE(actual == nullptr, name + " actual should have been a nullptr"); + } + else + { + BOOST_CHECK_MESSAGE(actual != nullptr, name + " actual should have been set"); + if (actual != nullptr) + { + CheckConstTensors(*expected, *actual); + } + } +} + +void TestQuantizedLstmLayerVisitor::CheckInputParameters(const QuantizedLstmInputParams& inputParams) +{ + CheckConstTensorPtrs("InputToInputWeights", + m_InputParams.m_InputToInputWeights, + inputParams.m_InputToInputWeights); + + CheckConstTensorPtrs("InputToForgetWeights", + m_InputParams.m_InputToForgetWeights, + inputParams.m_InputToForgetWeights); + + CheckConstTensorPtrs("InputToCellWeights", + m_InputParams.m_InputToCellWeights, + inputParams.m_InputToCellWeights); + + CheckConstTensorPtrs("InputToOutputWeights", + m_InputParams.m_InputToOutputWeights, + inputParams.m_InputToOutputWeights); + + CheckConstTensorPtrs("RecurrentToInputWeights", + m_InputParams.m_RecurrentToInputWeights, + inputParams.m_RecurrentToInputWeights); + + CheckConstTensorPtrs("RecurrentToForgetWeights", + m_InputParams.m_RecurrentToForgetWeights, + inputParams.m_RecurrentToForgetWeights); + + CheckConstTensorPtrs("RecurrentToCellWeights", + m_InputParams.m_RecurrentToCellWeights, + inputParams.m_RecurrentToCellWeights); + + CheckConstTensorPtrs("RecurrentToOutputWeights", + m_InputParams.m_RecurrentToOutputWeights, + inputParams.m_RecurrentToOutputWeights); + + CheckConstTensorPtrs("InputGateBias", m_InputParams.m_InputGateBias, inputParams.m_InputGateBias); + CheckConstTensorPtrs("ForgetGateBias", m_InputParams.m_ForgetGateBias, inputParams.m_ForgetGateBias); + CheckConstTensorPtrs("CellBias", m_InputParams.m_CellBias, inputParams.m_CellBias); + CheckConstTensorPtrs("OutputGateBias", m_InputParams.m_OutputGateBias, inputParams.m_OutputGateBias); +} + BOOST_AUTO_TEST_SUITE(TestConstTensorLayerVisitor) BOOST_AUTO_TEST_CASE(CheckConvolution2dLayer) @@ -1185,6 +1243,185 @@ BOOST_AUTO_TEST_CASE(CheckNamedLstmLayerProjection) layer->Accept(visitor); } +BOOST_AUTO_TEST_CASE(CheckQuantizedLstmLayer) +{ + std::vector inputToInputWeightsData = {1, 2, 3, 4, 5, 6, 7, 8, 9}; + std::vector inputToInputWeightsDimensions = {1, 1, 3, 3}; + ConstTensor inputToInputWeights( + TensorInfo(4, inputToInputWeightsDimensions.data(), DataType::QuantisedAsymm8), inputToInputWeightsData); + + std::vector inputToForgetWeightsData = {1, 2, 3, 4, 5, 6, 7, 8, 9}; + std::vector inputToForgetWeightsDimensions = {1, 1, 3, 3}; + ConstTensor inputToForgetWeights( + TensorInfo(4, inputToForgetWeightsDimensions.data(), DataType::QuantisedAsymm8), inputToForgetWeightsData); + + std::vector inputToCellWeightsData = {1, 2, 3, 4, 5, 6, 7, 8, 9}; + std::vector inputToCellWeightsDimensions = {1, 1, 3, 3}; + ConstTensor inputToCellWeights( + TensorInfo(4, inputToCellWeightsDimensions.data(), DataType::QuantisedAsymm8), inputToCellWeightsData); + + std::vector inputToOutputWeightsData = {1, 2, 3, 4, 5, 6, 7, 8, 9}; + std::vector inputToOutputWeightsDimensions = {1, 1, 3, 3}; + ConstTensor inputToOutputWeights( + TensorInfo(4, inputToOutputWeightsDimensions.data(), DataType::QuantisedAsymm8), inputToOutputWeightsData); + + + std::vector recurrentToInputWeightsData = {1, 2, 3, 4, 5, 6, 7, 8, 9}; + std::vector recurrentToInputWeightsDimensions = {1, 1, 3, 3}; + ConstTensor recurrentToInputWeights(TensorInfo( + 4, recurrentToInputWeightsDimensions.data(), DataType::QuantisedAsymm8), recurrentToInputWeightsData); + + std::vector recurrentToForgetWeightsData = {1, 2, 3, 4, 5, 6, 7, 8, 9}; + std::vector recurrentToForgetWeightsDimensions = {1, 1, 3, 3}; + ConstTensor recurrentToForgetWeights(TensorInfo( + 4, recurrentToForgetWeightsDimensions.data(), DataType::QuantisedAsymm8), recurrentToForgetWeightsData); + + std::vector recurrentToCellWeightsData = {1, 2, 3, 4, 5, 6, 7, 8, 9}; + std::vector recurrentToCellWeightsDimensions = {1, 1, 3, 3}; + ConstTensor recurrentToCellWeights(TensorInfo( + 4, recurrentToCellWeightsDimensions.data(), DataType::QuantisedAsymm8), recurrentToCellWeightsData); + + std::vector recurrentToOutputWeightsData = {1, 2, 3, 4, 5, 6, 7, 8, 9}; + std::vector recurrentToOutputWeightsDimensions = {1, 1, 3, 3}; + ConstTensor recurrentToOutputWeights(TensorInfo( + 4, recurrentToOutputWeightsDimensions.data(), DataType::QuantisedAsymm8), recurrentToOutputWeightsData); + + + std::vector inputGateBiasData = {1, 2, 3, 4, 5, 6, 7, 8, 9}; + std::vector inputGateBiasDimensions = {1, 1, 3, 3}; + ConstTensor inputGateBias( + TensorInfo(4, inputGateBiasDimensions.data(), DataType::Signed32), inputGateBiasData); + + std::vector forgetGateBiasData = {1, 2, 3, 4, 5, 6, 7, 8, 9}; + std::vector forgetGateBiasDimensions = {1, 1, 3, 3}; + ConstTensor forgetGateBias(TensorInfo( + 4, forgetGateBiasDimensions.data(), DataType::Signed32), forgetGateBiasData); + + std::vector cellBiasData = {1, 2, 3, 4, 5, 6, 7, 8, 9}; + std::vector cellBiasDimensions = {1, 1, 3, 3}; + ConstTensor cellBias(TensorInfo( + 4, cellBiasDimensions.data(), DataType::Signed32), cellBiasData); + + std::vector outputGateBiasData = {1, 2, 3, 4, 5, 6, 7, 8, 9}; + std::vector outputGateBiasDimensions = {1, 1, 3, 3}; + ConstTensor outputGateBias(TensorInfo( + 4, outputGateBiasDimensions.data(), DataType::Signed32), outputGateBiasData); + + QuantizedLstmInputParams params; + + params.m_InputToInputWeights = &inputToInputWeights; + params.m_InputToForgetWeights = &inputToForgetWeights; + params.m_InputToCellWeights = &inputToCellWeights; + params.m_InputToOutputWeights = &inputToOutputWeights; + + params.m_RecurrentToInputWeights = &recurrentToInputWeights; + params.m_RecurrentToForgetWeights = &recurrentToForgetWeights; + params.m_RecurrentToCellWeights = &recurrentToCellWeights; + params.m_RecurrentToOutputWeights = &recurrentToOutputWeights; + + params.m_InputGateBias = &inputGateBias; + params.m_ForgetGateBias = &forgetGateBias; + params.m_CellBias = &cellBias; + params.m_OutputGateBias = &outputGateBias; + + TestQuantizedLstmLayerVisitor visitor(params); + + Network net; + + IConnectableLayer* const layer = net.AddQuantizedLstmLayer(params); + layer->Accept(visitor); +} + +BOOST_AUTO_TEST_CASE(CheckNamedQuantizedLstmLayer) +{ + const char* layerName = "LstmLayer"; + std::vector inputToInputWeightsData = {1, 2, 3, 4, 5, 6, 7, 8, 9}; + std::vector inputToInputWeightsDimensions = {1, 1, 3, 3}; + ConstTensor inputToInputWeights( + TensorInfo(4, inputToInputWeightsDimensions.data(), DataType::QuantisedAsymm8), inputToInputWeightsData); + + std::vector inputToForgetWeightsData = {1, 2, 3, 4, 5, 6, 7, 8, 9}; + std::vector inputToForgetWeightsDimensions = {1, 1, 3, 3}; + ConstTensor inputToForgetWeights( + TensorInfo(4, inputToForgetWeightsDimensions.data(), DataType::QuantisedAsymm8), inputToForgetWeightsData); + + std::vector inputToCellWeightsData = {1, 2, 3, 4, 5, 6, 7, 8, 9}; + std::vector inputToCellWeightsDimensions = {1, 1, 3, 3}; + ConstTensor inputToCellWeights( + TensorInfo(4, inputToCellWeightsDimensions.data(), DataType::QuantisedAsymm8), inputToCellWeightsData); + + std::vector inputToOutputWeightsData = {1, 2, 3, 4, 5, 6, 7, 8, 9}; + std::vector inputToOutputWeightsDimensions = {1, 1, 3, 3}; + ConstTensor inputToOutputWeights( + TensorInfo(4, inputToOutputWeightsDimensions.data(), DataType::QuantisedAsymm8), inputToOutputWeightsData); + + + std::vector recurrentToInputWeightsData = {1, 2, 3, 4, 5, 6, 7, 8, 9}; + std::vector recurrentToInputWeightsDimensions = {1, 1, 3, 3}; + ConstTensor recurrentToInputWeights(TensorInfo( + 4, recurrentToInputWeightsDimensions.data(), DataType::QuantisedAsymm8), recurrentToInputWeightsData); + + std::vector recurrentToForgetWeightsData = {1, 2, 3, 4, 5, 6, 7, 8, 9}; + std::vector recurrentToForgetWeightsDimensions = {1, 1, 3, 3}; + ConstTensor recurrentToForgetWeights(TensorInfo( + 4, recurrentToForgetWeightsDimensions.data(), DataType::QuantisedAsymm8), recurrentToForgetWeightsData); + + std::vector recurrentToCellWeightsData = {1, 2, 3, 4, 5, 6, 7, 8, 9}; + std::vector recurrentToCellWeightsDimensions = {1, 1, 3, 3}; + ConstTensor recurrentToCellWeights(TensorInfo( + 4, recurrentToCellWeightsDimensions.data(), DataType::QuantisedAsymm8), recurrentToCellWeightsData); + + std::vector recurrentToOutputWeightsData = {1, 2, 3, 4, 5, 6, 7, 8, 9}; + std::vector recurrentToOutputWeightsDimensions = {1, 1, 3, 3}; + ConstTensor recurrentToOutputWeights(TensorInfo( + 4, recurrentToOutputWeightsDimensions.data(), DataType::QuantisedAsymm8), recurrentToOutputWeightsData); + + + std::vector inputGateBiasData = {1, 2, 3, 4, 5, 6, 7, 8, 9}; + std::vector inputGateBiasDimensions = {1, 1, 3, 3}; + ConstTensor inputGateBias( + TensorInfo(4, inputGateBiasDimensions.data(), DataType::Signed32), inputGateBiasData); + + std::vector forgetGateBiasData = {1, 2, 3, 4, 5, 6, 7, 8, 9}; + std::vector forgetGateBiasDimensions = {1, 1, 3, 3}; + ConstTensor forgetGateBias(TensorInfo( + 4, forgetGateBiasDimensions.data(), DataType::Signed32), forgetGateBiasData); + + std::vector cellBiasData = {1, 2, 3, 4, 5, 6, 7, 8, 9}; + std::vector cellBiasDimensions = {1, 1, 3, 3}; + ConstTensor cellBias(TensorInfo( + 4, cellBiasDimensions.data(), DataType::Signed32), cellBiasData); + + std::vector outputGateBiasData = {1, 2, 3, 4, 5, 6, 7, 8, 9}; + std::vector outputGateBiasDimensions = {1, 1, 3, 3}; + ConstTensor outputGateBias(TensorInfo( + 4, outputGateBiasDimensions.data(), DataType::Signed32), outputGateBiasData); + + QuantizedLstmInputParams params; + + params.m_InputToInputWeights = &inputToInputWeights; + params.m_InputToForgetWeights = &inputToForgetWeights; + params.m_InputToCellWeights = &inputToCellWeights; + params.m_InputToOutputWeights = &inputToOutputWeights; + + params.m_RecurrentToInputWeights = &recurrentToInputWeights; + params.m_RecurrentToForgetWeights = &recurrentToForgetWeights; + params.m_RecurrentToCellWeights = &recurrentToCellWeights; + params.m_RecurrentToOutputWeights = &recurrentToOutputWeights; + + params.m_InputGateBias = &inputGateBias; + params.m_ForgetGateBias = &forgetGateBias; + params.m_CellBias = &cellBias; + params.m_OutputGateBias = &outputGateBias; + + TestQuantizedLstmLayerVisitor visitor(params, layerName); + + Network net; + + IConnectableLayer* const layer = net.AddQuantizedLstmLayer(params, layerName); + layer->Accept(visitor); +} + BOOST_AUTO_TEST_SUITE_END() } // namespace armnn diff --git a/src/armnn/test/ConstTensorLayerVisitor.hpp b/src/armnn/test/ConstTensorLayerVisitor.hpp index 80409b331f..203c5fd91b 100644 --- a/src/armnn/test/ConstTensorLayerVisitor.hpp +++ b/src/armnn/test/ConstTensorLayerVisitor.hpp @@ -7,6 +7,7 @@ #include "TestLayerVisitor.hpp" #include #include +#include namespace armnn { @@ -220,4 +221,32 @@ private: LstmInputParams m_InputParams; }; + +class TestQuantizedLstmLayerVisitor : public TestLayerVisitor +{ +public: + explicit TestQuantizedLstmLayerVisitor(const QuantizedLstmInputParams& params, + const char* name = nullptr) + : TestLayerVisitor(name) + , m_InputParams(params) + {} + + void VisitQuantizedLstmLayer(const IConnectableLayer* layer, + const QuantizedLstmInputParams& params, + const char* name = nullptr) + { + CheckLayerPointer(layer); + CheckLayerName(name); + CheckInputParameters(params); + } + +protected: + void CheckInputParameters(const QuantizedLstmInputParams& inputParams); + void CheckConstTensorPtrs(const std::string& name, const ConstTensor* expected, const ConstTensor* actual); + +private: + QuantizedLstmInputParams m_InputParams; +}; + + } // namespace armnn diff --git a/src/armnn/test/InferOutputTests.cpp b/src/armnn/test/InferOutputTests.cpp index 4581d87a5b..8606745623 100644 --- a/src/armnn/test/InferOutputTests.cpp +++ b/src/armnn/test/InferOutputTests.cpp @@ -40,4 +40,7 @@ ARMNN_SIMPLE_TEST_CASE(DepthwiseConvolution2dInferOutputShape, DepthwiseConvolut // TransposeConvolution2D ARMNN_SIMPLE_TEST_CASE(TransposeConvolution2dInferOutputShape, TransposeConvolution2dInferOutputShapeTest) +// QuantizedLstm +ARMNN_SIMPLE_TEST_CASE(QuantizedLstmInferOutputShape, QuantizedLstmInferOutputShapeTest) + BOOST_AUTO_TEST_SUITE_END() diff --git a/src/armnn/test/InferOutputTests.hpp b/src/armnn/test/InferOutputTests.hpp index 58a081a130..2dd2ff0e73 100644 --- a/src/armnn/test/InferOutputTests.hpp +++ b/src/armnn/test/InferOutputTests.hpp @@ -443,4 +443,50 @@ void DepthwiseConvolution2dInferOutputShapeTest() armnn::TensorShape expectedOutputShape(4, expectedOutputSizes.data()); BOOST_CHECK(expectedOutputShape == depthwiseConvolution2dLayer->InferOutputShapes(shapes).at(0)); -} \ No newline at end of file +} + +// QuantizedLstm +void QuantizedLstmInferOutputShapeImpl(const std::vector& inputShapes, + std::vector& outputShapes) +{ + armnn::Graph graph; + armnn::QuantizedLstmLayer* const quantizedLstmLayer = graph.AddLayer("quantizedLstm"); + outputShapes = quantizedLstmLayer->InferOutputShapes(inputShapes); +} + +void QuantizedLstmInferOutputShapeTest() +{ + // Input shapes + const std::vector inputShape{ 2, 5 }; + const std::vector previousCellStateInShape{ 2, 10 }; + const std::vector previousOutputInShape{ 2, 10 }; + armnn::TensorShape inputTensorShape(2, inputShape.data()); + armnn::TensorShape previousCellStateInTensorShape(2, previousCellStateInShape.data()); + armnn::TensorShape previousOutputInTensorShape(2, previousOutputInShape.data()); + + std::vector inShapes + { + inputTensorShape, + previousCellStateInTensorShape, + previousOutputInTensorShape + }; + + // Output shapes + const std::vector cellStateOutShape{ 2, 10 }; + const std::vector outputShape{ 2, 10 }; + armnn::TensorShape cellStateOutTensorShape(2, cellStateOutShape.data()); + armnn::TensorShape outputTensorShape(2, outputShape.data()); + + std::vector expectedOutShapes + { + cellStateOutTensorShape, + outputTensorShape + }; + + std::vector actualOutShapes; + BOOST_CHECK_NO_THROW(QuantizedLstmInferOutputShapeImpl(inShapes, actualOutShapes)); + + BOOST_CHECK(actualOutShapes.size() == 2); + BOOST_CHECK(expectedOutShapes[0] == actualOutShapes[0]); + BOOST_CHECK(expectedOutShapes[1] == actualOutShapes[1]); +} -- cgit v1.2.1