From 38e05bd2836b1b65b440330a9c283038ba4192c3 Mon Sep 17 00:00:00 2001 From: Jan Eilers Date: Wed, 26 Jun 2019 13:10:09 +0100 Subject: IVGCVSW-3236 Extend Ref LSTM with layer normalization support * Add descriptor values * Update lstm queue descriptor validate function * Update lstm workload * Update isLstmSupported (Cl and Ref), LayerSupportBase, ILayerSupport * Update lstm layer * Add unit tests Signed-off-by: Jan Eilers Change-Id: I932175d550facfb342325051eaa7bd2084ebdc18 Signed-off-by: Jan Eilers --- src/backends/backendsCommon/test/LayerTests.cpp | 166 +++++++++ src/backends/backendsCommon/test/LayerTests.hpp | 11 + src/backends/backendsCommon/test/LstmTestImpl.hpp | 386 ++++++++++++++++++++- .../backendsCommon/test/WorkloadDataValidation.cpp | 149 +++++++- 4 files changed, 695 insertions(+), 17 deletions(-) (limited to 'src/backends/backendsCommon/test') diff --git a/src/backends/backendsCommon/test/LayerTests.cpp b/src/backends/backendsCommon/test/LayerTests.cpp index ca39438fbf..56c0ab6b12 100644 --- a/src/backends/backendsCommon/test/LayerTests.cpp +++ b/src/backends/backendsCommon/test/LayerTests.cpp @@ -1665,6 +1665,153 @@ LayerTestResult CopyViaSplitterInt16Test( return CopyViaSplitterTestImpl(workloadFactory, memoryManager, 1.0f, 0); } +void LstmUtilsZeroVectorTest() +{ + armnn::TensorInfo inputDesc({4}, armnn::DataType::Float32); + boost::multi_array input = MakeTensor(inputDesc, std::vector( + {2., 3., 3., 4.})); + + boost::multi_array expectedOutput = MakeTensor(inputDesc, std::vector( + {0., 0., 0., 0.})); + + return LstmUtilsZeroVectorTestImpl(input, 4, expectedOutput); +} + +void LstmUtilsMeanStddevNormalizationNoneZeroInputTest() +{ + uint32_t batchSize = 2; + uint32_t vecSize = 4; + armnn::TensorInfo inputDesc({batchSize, vecSize}, armnn::DataType::Float32); + boost::multi_array input = MakeTensor(inputDesc, std::vector( + { 0.1f, 0.2f, 0.3f, 0.4f, //batch 0 + 0.9f, 1.0f, 1.1f, 1.2f })); //batch 1 + + boost::multi_array expectedOutput = MakeTensor(inputDesc, std::vector( + { -1.34164071f, -0.447213531f, 0.44721365f, 1.34164071f, //batch 0 + -1.34163153f, -0.447210163f, 0.447211236f, 1.3416326f })); //batch 1 + + return LstmUtilsMeanStddevNormalizationTestImpl(input, + vecSize, batchSize, expectedOutput); +} + +void LstmUtilsMeanStddevNormalizationAllZeroInputTest() +{ + uint32_t batchSize = 2; + uint32_t vecSize = 4; + armnn::TensorInfo inputDesc({batchSize, vecSize}, armnn::DataType::Float32); + boost::multi_array input = MakeTensor(inputDesc, std::vector( + { 0.0f, 0.0f, 0.0f, 0.0f, //batch 0 + 0.0f, 0.0f, 0.0f, 0.0f })); //batch 1 + + boost::multi_array expectedOutput = MakeTensor(inputDesc, std::vector( + { 0.0f, 0.0f, 0.0f, 0.0f, //batch 0 + 0.0f, 0.0f, 0.0f, 0.0f })); //batch 1 + + return LstmUtilsMeanStddevNormalizationTestImpl(input, + vecSize, batchSize, expectedOutput); +} + +void LstmUtilsMeanStddevNormalizationMixedZeroInputTest() +{ + uint32_t batchSize = 2; + uint32_t vecSize = 4; + armnn::TensorInfo inputDesc({batchSize, vecSize}, armnn::DataType::Float32); + boost::multi_array input = MakeTensor(inputDesc, std::vector( + { 0.0f, 0.0f, 0.0f, 0.0f, //batch 0 + 0.1f, 0.2f, 0.3f, 0.4f })); //batch 1 + + boost::multi_array expectedOutput = MakeTensor(inputDesc, std::vector( + { 0.0f, 0.0f, 0.0f, 0.0f, //batch 0 + -1.34164071f, -0.447213531f, 0.44721365f, 1.34164071f })); //batch 1 + + return LstmUtilsMeanStddevNormalizationTestImpl(input, + vecSize, batchSize, expectedOutput); +} + + +void LstmUtilsVectorBatchVectorCwiseProductTest() +{ + uint32_t batchSize = 4; + uint32_t vecSize = 29; + armnn::TensorInfo vecDesc({vecSize}, armnn::DataType::Float32); + boost::multi_array vector = MakeTensor(vecDesc, std::vector( + { 1.1f, 2.2f, 3.3f, 4.4f, 5.5f, 6.6f, 7.7f, 8.8f, 9.9f, 10.1f, + 11.11f, 12.12f, 13.13f, 14.14f, 15.15f, 16.16f, 17.17f, 18.18f, 19.19f, 20.2f, + 21.21f, 22.22f, 23.23f, 24.24f, 25.25f, 26.26f, 27.27f, 28.28f, 0.0f})); + + armnn::TensorInfo batchVecDesc({batchSize, vecSize}, armnn::DataType::Float32); + boost::multi_array batchVector = MakeTensor(batchVecDesc, std::vector( + { /* batch 0 */ + 1.1f, 2.2f, 3.3f, 4.4f, 5.5f, 6.6f, 7.7f, 8.8f, 9.9f, 10.1f, + 11.11f, 12.12f, 13.13f, 14.14f, 15.15f, 16.16f, 17.17f, 18.18f, 19.19f, 20.2f, + 21.21f, 22.22f, 23.23f, 24.24f, 25.25f, 26.26f, 27.27f, 28.28f, 0.0f, + /* batch 1 */ + -1.1f, -2.2f, -3.3f, -4.4f, -5.5f, -6.6f, -7.7f, -8.8f, -9.9f, -10.1f, + -11.11f, -12.12f, -13.13f, -14.14f, -15.15f, -16.16f, -17.17f, -18.18f, -19.19f, -20.2f, + -21.21f, -22.22f, -23.23f, -24.24f, -25.25f, -26.26f, -27.27f, -28.28f, 0.0f, + /* batch 2 */ + 1.1f, -2.2f, 3.3f, -4.4f, 5.5f, -6.6f, 7.7f, -8.8f, 9.9f, -10.1f, + 11.11f, -12.12f, 13.13f, -14.14f, 15.15f, -16.16f, 17.17f, -18.18f, 19.19f, -20.2f, + 21.21f, -22.22f, 23.23f, -24.24f, 25.25f, -26.26f, 27.27f, -28.28f, 0.0f, + /* batch 3 */ + -1.1f, 2.2f, -3.3f, 4.4f, -5.5f, 6.6f, -7.7f, 8.8f, -9.9f, 10.1f, + -11.11f, 12.12f, -13.13f, 14.14f, -15.15f, 16.16f, -17.17f, 18.18f, -19.19f, 20.2f, + -21.21f, 22.22f, -23.23f, 24.24f, -25.25f, 26.26f, -27.27f, 28.28f, 0.0f})); + + // Expect output = input * output + output. + boost::multi_array expectedOutput = MakeTensor(batchVecDesc, std::vector( + { /* batch 0 */ + 1.210000f, 4.840000f, 10.889999f, 19.360001f, 30.250000f, 43.559998f, + 59.289997f, 77.440002f, 98.009995f, 102.010010f, 123.432091f, 146.894394f, + 172.396896f, 199.939606f, 229.522491f, 261.145599f, 294.808899f, 330.512421f, + 368.256134f, 408.040039f, 449.864075f, 493.728363f, 539.632874f, 587.577576f, + 637.562500f, 689.587585f, 743.652954f, 799.758423f, 0.000000f, + /* batch 1 */ + -1.210000f, -4.840000f, -10.889999f, -19.360001f, -30.250000f, -43.559998f, + -59.289997f, -77.440002f, -98.009995f, -102.010010f, -123.432091f, -146.894394f, + -172.396896f, -199.939606f, -229.522491f, -261.145599f, -294.808899f, -330.512421f, + -368.256134f, -408.040039f, -449.864075f, -493.728363f, -539.632874f, -587.577576f, + -637.562500f, -689.587585f, -743.652954f, -799.758423f, 0.000000f, + /* batch 2 */ + 1.210000f, -4.840000f, 10.889999f, -19.360001f, 30.250000f, -43.559998f, + 59.289997f, -77.440002f, 98.009995f, -102.010010f, 123.432091f, -146.894394f, + 172.396896f, -199.939606f, 229.522491f, -261.145599f, 294.808899f, -330.512421f, + 368.256134f, -408.040039f, 449.864075f, -493.728363f, 539.632874f, -587.577576f, + 637.562500f, -689.587585f, 743.652954f, -799.758423f, 0.000000f, + /* batch 3 */ + -1.210000f, 4.840000f, -10.889999f, 19.360001f, -30.250000f, 43.559998f, + -59.289997f, 77.440002f, -98.009995f, 102.010010f, -123.432091f, 146.894394f, + -172.396896f, 199.939606f, -229.522491f, 261.145599f, -294.808899f, 330.512421f, + -368.256134f, 408.040039f, -449.864075f, 493.728363f, -539.632874f, 587.577576f, + -637.562500f, 689.587585f, -743.652954f, 799.758423f, 0.000000f})); + + return LstmUtilsVectorBatchVectorCwiseProductTestImpl(vector, batchVector, + vecSize, batchSize, expectedOutput); +} + + +void LstmUtilsVectorBatchVectorAddTest() +{ + uint32_t batchSize = 2; + uint32_t vecSize = 3; + armnn::TensorInfo vecDesc({vecSize}, armnn::DataType::Float32); + boost::multi_array vector = MakeTensor(vecDesc, std::vector( + { 0.0f, -0.5f, 1.0f})); + + armnn::TensorInfo batchVecDesc({batchSize, vecSize}, armnn::DataType::Float32); + boost::multi_array batchVector = MakeTensor(batchVecDesc, std::vector( + { 1.0f, 2.0f, 3.0f, //batch 0 + 4.0f, 5.0f, 6.0f})); //batch 1 + + boost::multi_array expectedOutput = MakeTensor(batchVecDesc, std::vector( + { 1.0f, 1.5f, 4.0f, + 4.0f, 4.5f, 7.0f})); + + return LstmUtilsVectorBatchVectorAddTestImpl(vector, batchVector, + vecSize, batchSize, expectedOutput); +} + + LayerTestResult LstmLayerFloat32WithCifgWithPeepholeNoProjectionTest( armnn::IWorkloadFactory& workloadFactory, const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager) @@ -1721,6 +1868,25 @@ LayerTestResult LstmLayerFloat32NoCifgNoPeepholeNoProjectionTest( workloadFactory, memoryManager, input, expectedOutput); } + +LayerTestResult LstmLayerFloat32NoCifgWithPeepholeWithProjectionWithLayerNormTest( + armnn::IWorkloadFactory& workloadFactory, + const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager) +{ + armnn::TensorInfo inputDesc({ 2, 5 }, armnn::DataType::Float32); + boost::multi_array input = MakeTensor(inputDesc, std::vector( + {0.7f, 0.8f, 0.1f, 0.2f, 0.3f, //batch 0 + 0.3f, 0.2f, 0.9f, 0.8f, 0.1f})); //batch 1 + + armnn::TensorInfo outputDesc({ 2, 3 }, armnn::DataType::Float32); + boost::multi_array expectedOutput = MakeTensor(outputDesc, std::vector( + { 0.0244077f, 0.128027f, -0.00170918f, //batch 0 + -0.00692428f, 0.0848741f, 0.063445f})); //batch 1 + return LstmLayerNoCifgWithPeepholeWithProjectionWithLayerNormTestImpl( + workloadFactory, memoryManager, input, expectedOutput); +} + + LayerTestResult LstmLayerInt16NoCifgNoPeepholeNoProjectionTest( armnn::IWorkloadFactory& workloadFactory, const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager) diff --git a/src/backends/backendsCommon/test/LayerTests.hpp b/src/backends/backendsCommon/test/LayerTests.hpp index 405ccff35b..66324e104c 100644 --- a/src/backends/backendsCommon/test/LayerTests.hpp +++ b/src/backends/backendsCommon/test/LayerTests.hpp @@ -1458,6 +1458,13 @@ LayerTestResult PermuteFloat32ValueSet3Test( armnn::IWorkloadFactory& workloadFactory, const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager); +void LstmUtilsZeroVectorTest(); +void LstmUtilsMeanStddevNormalizationNoneZeroInputTest(); +void LstmUtilsMeanStddevNormalizationAllZeroInputTest(); +void LstmUtilsMeanStddevNormalizationMixedZeroInputTest(); +void LstmUtilsVectorBatchVectorCwiseProductTest(); +void LstmUtilsVectorBatchVectorAddTest(); + LayerTestResult LstmLayerFloat32WithCifgWithPeepholeNoProjectionTest( armnn::IWorkloadFactory& workloadFactory, const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager); @@ -1470,6 +1477,10 @@ LayerTestResult LstmLayerFloat32NoCifgWithPeepholeWithProjectionTest( armnn::IWorkloadFactory& workloadFactory, const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager); +LayerTestResult LstmLayerFloat32NoCifgWithPeepholeWithProjectionWithLayerNormTest( + armnn::IWorkloadFactory& workloadFactory, + const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager); + LayerTestResult LstmLayerInt16NoCifgNoPeepholeNoProjectionTest( armnn::IWorkloadFactory& workloadFactory, const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager); diff --git a/src/backends/backendsCommon/test/LstmTestImpl.hpp b/src/backends/backendsCommon/test/LstmTestImpl.hpp index dae9c8a3f1..2ed0a974fc 100644 --- a/src/backends/backendsCommon/test/LstmTestImpl.hpp +++ b/src/backends/backendsCommon/test/LstmTestImpl.hpp @@ -16,6 +16,119 @@ #include #include +#include "reference/workloads/LstmUtils.hpp" + +//LstmUtils Tests +// TODO: Add tests for the remaining functions in LstmUtils.hpp + +template> +void LstmUtilsVectorBatchVectorAddTestImpl( + boost::multi_array& vec, + boost::multi_array& batchVec, + uint32_t vSize, + uint32_t nBatch, + boost::multi_array& expectedOutput ) +{ + float qScale = 0.0f; + int32_t qOffset = 0; + armnn::TensorInfo tensorInfo({nBatch, vSize}, ArmnnType, qScale, qOffset ); + + // Make encoder and decoder + std::unique_ptr> vecDecoder = armnn::MakeDecoder(tensorInfo, vec.data()); + std::unique_ptr> batchVecDecoder = armnn::MakeDecoder(tensorInfo, batchVec.data()); + std::unique_ptr> batchVecEncoder = armnn::MakeEncoder(tensorInfo, batchVec.data()); + + VectorBatchVectorAdd(*vecDecoder, vSize, *batchVecDecoder, nBatch, *batchVecEncoder); + + // check shape and compare values + BOOST_TEST(CompareTensors(batchVec, expectedOutput)); + + // check if iterator is back at start position + batchVecEncoder->Set(1.0f); + BOOST_TEST(batchVec[0][0] == 1.0f); +} + +template> +void LstmUtilsZeroVectorTestImpl( + boost::multi_array& input, + uint32_t vSize, + boost::multi_array& expectedOutput) { + + float qScale = 0.0f; + int32_t qOffset = 0; + + armnn::TensorInfo tensorInfo({vSize}, ArmnnType, qScale, qOffset ); + + // Make encoder for input + std::unique_ptr> outputEncoder = armnn::MakeEncoder(tensorInfo, input.data()); + + // call ZeroVector + ZeroVector(*outputEncoder, vSize); + + // check shape and compare values + BOOST_TEST(CompareTensors(input, expectedOutput)); + + // check if iterator is back at start position + outputEncoder->Set(1.0f); + BOOST_TEST(input[0] == 1.0f); + +} + + +template> +void LstmUtilsMeanStddevNormalizationTestImpl( + boost::multi_array& input, + uint32_t vSize, + uint32_t nBatch, + boost::multi_array& expectedOutput) +{ + float qScale = 0.0f; + int32_t qOffset = 0; + armnn::TensorInfo tensorInfo({nBatch, vSize}, ArmnnType, qScale, qOffset ); + + // Make encoder and decoder for input + std::unique_ptr> inputDecoder = armnn::MakeDecoder(tensorInfo, input.data()); + std::unique_ptr> outputEncoder = armnn::MakeEncoder(tensorInfo, input.data()); + + MeanStddevNormalization(*inputDecoder, *outputEncoder, vSize, nBatch, 1e-8f); + + // check shape and compare values + BOOST_TEST(CompareTensors(input, expectedOutput)); + + // check if iterator is back at start position + outputEncoder->Set(1.0f); + BOOST_TEST(input[0][0] == 1.0f); +} + +template> +void LstmUtilsVectorBatchVectorCwiseProductTestImpl( + boost::multi_array& vec, + boost::multi_array& batchVec, + uint32_t vSize, + uint32_t nBatch, + boost::multi_array& expectedOutput) +{ + float qScale = 0.0f; + int32_t qOffset = 0; + armnn::TensorInfo tensorInfo({nBatch, vSize}, ArmnnType, qScale, qOffset ); + + // Make encoder and decoder + std::unique_ptr> vecDecoder = armnn::MakeDecoder(tensorInfo, vec.data()); + std::unique_ptr> batchVecDecoder = armnn::MakeDecoder(tensorInfo, batchVec.data()); + std::unique_ptr> batchVecEncoder = armnn::MakeEncoder(tensorInfo, batchVec.data()); + + VectorBatchVectorCwiseProduct(*vecDecoder, vSize, *batchVecDecoder, nBatch, *batchVecEncoder); + + // check shape and compare values + BOOST_TEST(CompareTensors(batchVec, expectedOutput)); + + // check if iterator is back at start position + batchVecEncoder->Set(1.0f); + BOOST_TEST(batchVec[0][0] == 1.0f); +} + +// Lstm Layer tests: + template> LayerTestResult LstmNoCifgNoPeepholeNoProjectionTestImpl( @@ -187,7 +300,6 @@ LstmNoCifgNoPeepholeNoProjectionTestImpl( data.m_RecurrentToForgetWeights = &recurrentToForgetWeightsTensor; data.m_RecurrentToCellWeights = &recurrentToCellWeightsTensor; data.m_RecurrentToOutputWeights = &recurrentToOutputWeightsTensor; - data.m_CellToInputWeights = &cellToInputWeightsTensor; data.m_InputGateBias = &inputGateBiasTensor; data.m_ForgetGateBias = &forgetGateBiasTensor; data.m_CellBias = &cellBiasTensor; @@ -1157,3 +1269,275 @@ LayerTestResult LstmLayerWithCifgWithPeepholeNoProjectionTestImpl( return ret3; } + + +template> +LayerTestResult +LstmLayerNoCifgWithPeepholeWithProjectionWithLayerNormTestImpl(armnn::IWorkloadFactory& workloadFactory, + const armnn::IBackendInternal::IMemoryManagerSharedPtr& memoryManager, + const boost::multi_array& input, + const boost::multi_array& outputExpected, + float qScale = 0.0f, + int32_t qOffset = 0, + armnn::DataType constantDataType = armnn::DataType::Float32) +{ + unsigned int batchSize = 2; + unsigned int outputSize = 3; + unsigned int inputSize = 5; + unsigned numUnits = 4; + + armnn::TensorInfo inputTensorInfo({batchSize , inputSize}, ArmnnType, qScale, qOffset); + armnn::TensorInfo cellStateInTensorInfo({batchSize , numUnits}, ArmnnType, qScale, qOffset); + armnn::TensorInfo outputStateInTensorInfo({batchSize , outputSize}, ArmnnType, qScale, qOffset); + + // Scratch buffer size without CIFG [batchSize, numUnits * 4] + armnn::TensorInfo scratchBufferTensorInfo({batchSize, numUnits * 4}, ArmnnType, qScale, qOffset); + armnn::TensorInfo cellStateOutTensorInfo({batchSize, numUnits}, ArmnnType, qScale, qOffset); + armnn::TensorInfo outputStateOutTensorInfo({batchSize, outputSize}, ArmnnType, qScale, qOffset); + armnn::TensorInfo outputTensorInfo({batchSize, outputSize}, ArmnnType, qScale, qOffset); + + LayerTestResult ret(outputTensorInfo); + + std::vector inputVector; + inputVector.assign(input.data(), input.data() + (batchSize * inputSize)); + auto inputTensor = MakeTensor(inputTensorInfo, inputVector); + + std::vector cellStateInVector(batchSize * numUnits, 0.f); + auto cellStateInTensor = MakeTensor(cellStateInTensorInfo, cellStateInVector); + + std::vector outputStateInVector(batchSize * outputSize, 0.f); + auto outputStateInTensor = MakeTensor(outputStateInTensorInfo, outputStateInVector); + + std::vector scratchBufferVector(batchSize * numUnits * 4, 0.f); + auto scratchBufferTensor = MakeTensor(scratchBufferTensorInfo, scratchBufferVector); + + std::vector outputStateOutVector(batchSize * outputSize, 0.f); + auto outputStateOutTensor = MakeTensor(outputStateOutTensorInfo, outputStateOutVector); + + std::vector cellStateOutVector(batchSize * numUnits, 0.f); + auto cellStateOutTensor = MakeTensor(cellStateOutTensorInfo, cellStateOutVector); + + std::vector outputVector; + outputVector.assign(outputExpected.data(), outputExpected.data() + (batchSize * outputSize)); + ret.outputExpected = MakeTensor(outputTensorInfo, outputVector); + + std::unique_ptr inputHandle = workloadFactory.CreateTensorHandle(inputTensorInfo); + std::unique_ptr cellStateInHandle = + workloadFactory.CreateTensorHandle(cellStateInTensorInfo); + std::unique_ptr outputStateInHandle = + workloadFactory.CreateTensorHandle(outputStateInTensorInfo); + + std::unique_ptr scratchHandle = workloadFactory.CreateTensorHandle(scratchBufferTensorInfo); + std::unique_ptr outputStateOutHandle = + workloadFactory.CreateTensorHandle(outputStateOutTensorInfo); + std::unique_ptr cellStateOutHandle = + workloadFactory.CreateTensorHandle(cellStateOutTensorInfo); + std::unique_ptr outputHandle = workloadFactory.CreateTensorHandle(outputTensorInfo); + + armnn::LstmQueueDescriptor data; + armnn::WorkloadInfo info; + + AddInputToWorkload(data, info, inputTensorInfo, inputHandle.get()); + AddInputToWorkload(data, info, outputStateInTensorInfo, outputStateInHandle.get()); + AddInputToWorkload(data, info, cellStateInTensorInfo, cellStateInHandle.get()); + + AddOutputToWorkload(data, info, scratchBufferTensorInfo, scratchHandle.get()); + AddOutputToWorkload(data, info, outputStateOutTensorInfo, outputStateOutHandle.get()); + AddOutputToWorkload(data, info, cellStateOutTensorInfo, cellStateOutHandle.get()); + AddOutputToWorkload(data, info, outputTensorInfo, outputHandle.get()); + + armnn::TensorInfo tensorInfo3({outputSize}, constantDataType, qScale, qOffset); + armnn::TensorInfo tensorInfo4({numUnits}, constantDataType, qScale, qOffset); + armnn::TensorInfo tensorInfo4x5({numUnits, inputSize}, constantDataType, qScale, qOffset); + armnn::TensorInfo tensorInfo4x3({numUnits, outputSize}, constantDataType, qScale, qOffset); + armnn::TensorInfo tensorInfo3x4({outputSize, numUnits}, constantDataType, qScale, qOffset); + + auto inputToInputWeights = + MakeTensor(tensorInfo4x5, { 0.5f, 0.6f, 0.7f, -0.8f, -0.9f, + 0.1f, 0.2f, 0.3f, -0.4f, 0.5f, + -0.8f, 0.7f, -0.6f, 0.5f, -0.4f, + -0.5f, -0.4f, -0.3f, -0.2f, -0.1f}); //{numUnits, inputSize} + + auto inputToForgetWeights = + MakeTensor(tensorInfo4x5, {-0.6f, -0.1f, 0.3f, 0.2f, 0.9f, + -0.5f, -0.2f, -0.4f, 0.3f, -0.8f, + -0.4f, 0.3f, -0.5f, -0.4f, -0.6f, + 0.3f, -0.4f, -0.6f, -0.5f, -0.5f}); //{numUnits, inputSize} + + auto inputToCellWeights = + MakeTensor(tensorInfo4x5, {-0.4f, -0.3f, -0.2f, -0.1f, -0.5f, + 0.5f, -0.2f, -0.3f, -0.2f, -0.6f, + 0.6f, -0.1f, -0.4f, -0.3f, -0.7f, + 0.7f, -0.9f, -0.5f, 0.8f, 0.6f}); //{numUnits, inputSize} + + auto inputToOutputWeights = + MakeTensor(tensorInfo4x5, {-0.8f, -0.4f, -0.2f, -0.9f, -0.1f, + -0.7f, 0.3f, -0.3f, -0.8f, -0.2f, + 0.6f, -0.2f, 0.4f, -0.7f, -0.3f, + -0.5f, 0.1f, 0.5f, -0.6f, -0.4f}); //{numUnits, inputSize} + + auto inputGateBias = + MakeTensor(tensorInfo4, {0.03f, 0.15f, 0.22f, 0.38f}); //{numUnits} + + auto forgetGateBias = + MakeTensor(tensorInfo4, {0.1f, -0.3f, -0.2f, 0.1f}); //{numUnits} + + auto cellBias = + MakeTensor(tensorInfo4, {-0.05f, 0.72f, 0.25f, 0.08f}); //{numUnits} + + auto outputGateBias = + MakeTensor(tensorInfo4, {0.05f, -0.01f, 0.2f, 0.1f}); //{numUnits} + + auto recurrentToInputWeights = + MakeTensor(tensorInfo4x3, {-0.2f, -0.3f, 0.4f, + 0.1f, -0.5f, 0.9f, + -0.2f, -0.3f, -0.7f, + 0.05f, -0.2f, -0.6f}); //{numUnits, outputSize} + + auto recurrentToCellWeights = + MakeTensor(tensorInfo4x3, {-0.3f, 0.2f, 0.1f, + -0.3f, 0.8f, -0.08f, + -0.2f, 0.3f, 0.8f, + -0.6f, -0.1f, 0.2f}); //{numUnits, outputSize} + + auto recurrentToForgetWeights = + MakeTensor(tensorInfo4x3, {-0.5f, -0.3f, -0.5f, + -0.2f, 0.6f, 0.4f, + 0.9f, 0.3f, -0.1f, + 0.2f, 0.5f, 0.2f}); //{numUnits, outputSize} + + auto recurrentToOutputWeights = + MakeTensor(tensorInfo4x3, { 0.3f, -0.1f, 0.1f, + -0.2f, -0.5f, -0.7f, + -0.2f, -0.6f, -0.1f, + -0.4f, -0.7f, -0.2f}); //{numUnits, outputSize} + + auto cellToInputWeights = + MakeTensor(tensorInfo4, {0.05f, 0.1f, 0.25f, 0.15f}); //{numUnits} + + auto cellToForgetWeights = + MakeTensor(tensorInfo4, {-0.02f, -0.15f, -0.25f, -0.03f}); //{numUnits} + + auto cellToOutputWeights = + MakeTensor(tensorInfo4, {0.1f, -0.1f, -0.5f, 0.05f}); //{numUnits} + + auto projectionWeights = + MakeTensor(tensorInfo3x4, + {-0.1f, 0.2f, 0.01f, -0.2f, + 0.1f, 0.5f, 0.3f, 0.08f, + 0.07f, 0.2f, -0.4f, 0.2f}); //{outputSize, numUnits} + + std::vector projectionBiasVector(outputSize, 0.f); + auto projectionBias = MakeTensor(tensorInfo3, projectionBiasVector); //{outputSize} + + auto inputLayerNormWeights = + MakeTensor(tensorInfo4, {0.1f, 0.2f, 0.3f, 0.5f}); //{numUnits} + + auto forgetLayerNormWeights = + MakeTensor(tensorInfo4, {0.2f, 0.2f, 0.4f, 0.3f}); //{numUnits} + + auto cellLayerNormWeights = + MakeTensor(tensorInfo4, {0.7f, 0.2f, 0.3f, 0.8f}); //{numUnits} + + auto outputLayerNormWeights = + MakeTensor(tensorInfo4, {0.6f, 0.2f, 0.2f, 0.5f}); //{numUnits} + + + armnn::ScopedCpuTensorHandle inputToInputWeightsTensor(tensorInfo4x5); + armnn::ScopedCpuTensorHandle inputToForgetWeightsTensor(tensorInfo4x5); + armnn::ScopedCpuTensorHandle inputToCellWeightsTensor(tensorInfo4x5); + armnn::ScopedCpuTensorHandle inputToOutputWeightsTensor(tensorInfo4x5); + armnn::ScopedCpuTensorHandle recurrentToForgetWeightsTensor(tensorInfo4x3); + armnn::ScopedCpuTensorHandle recurrentToInputWeightsTensor(tensorInfo4x3); + armnn::ScopedCpuTensorHandle recurrentToCellWeightsTensor(tensorInfo4x3); + armnn::ScopedCpuTensorHandle recurrentToOutputWeightsTensor(tensorInfo4x3); + armnn::ScopedCpuTensorHandle cellToInputWeightsTensor(tensorInfo4); + armnn::ScopedCpuTensorHandle inputGateBiasTensor(tensorInfo4); + armnn::ScopedCpuTensorHandle forgetGateBiasTensor(tensorInfo4); + armnn::ScopedCpuTensorHandle cellBiasTensor(tensorInfo4); + armnn::ScopedCpuTensorHandle outputGateBiasTensor(tensorInfo4); + armnn::ScopedCpuTensorHandle cellToForgetWeightsTensor(tensorInfo4); + armnn::ScopedCpuTensorHandle cellToOutputWeightsTensor(tensorInfo4); + armnn::ScopedCpuTensorHandle projectionWeightsTensor(tensorInfo3x4); + armnn::ScopedCpuTensorHandle projectionBiasTensor(tensorInfo3); + + armnn::ScopedCpuTensorHandle inputLayerNormWeightsTensor(tensorInfo4); + armnn::ScopedCpuTensorHandle forgetLayerNormWeightsTensor(tensorInfo4); + armnn::ScopedCpuTensorHandle cellLayerNormWeightsTensor(tensorInfo4); + armnn::ScopedCpuTensorHandle outputLayerNormWeightsTensor(tensorInfo4); + + AllocateAndCopyDataToITensorHandle(&inputToInputWeightsTensor, &inputToInputWeights[0][0]); + AllocateAndCopyDataToITensorHandle(&inputToForgetWeightsTensor, &inputToForgetWeights[0][0]); + AllocateAndCopyDataToITensorHandle(&inputToCellWeightsTensor, &inputToCellWeights[0][0]); + AllocateAndCopyDataToITensorHandle(&inputToOutputWeightsTensor, &inputToOutputWeights[0][0]); + AllocateAndCopyDataToITensorHandle(&recurrentToInputWeightsTensor, &recurrentToInputWeights[0][0]); + AllocateAndCopyDataToITensorHandle(&recurrentToForgetWeightsTensor, &recurrentToForgetWeights[0][0]); + AllocateAndCopyDataToITensorHandle(&recurrentToCellWeightsTensor, &recurrentToCellWeights[0][0]); + AllocateAndCopyDataToITensorHandle(&recurrentToOutputWeightsTensor, &recurrentToOutputWeights[0][0]); + AllocateAndCopyDataToITensorHandle(&cellToInputWeightsTensor, &cellToInputWeights[0]); + AllocateAndCopyDataToITensorHandle(&inputGateBiasTensor, &inputGateBias[0]); + AllocateAndCopyDataToITensorHandle(&forgetGateBiasTensor, &forgetGateBias[0]); + AllocateAndCopyDataToITensorHandle(&cellBiasTensor, &cellBias[0]); + AllocateAndCopyDataToITensorHandle(&outputGateBiasTensor, &outputGateBias[0]); + AllocateAndCopyDataToITensorHandle(&cellToForgetWeightsTensor, &cellToForgetWeights[0]); + AllocateAndCopyDataToITensorHandle(&cellToOutputWeightsTensor, &cellToOutputWeights[0]); + AllocateAndCopyDataToITensorHandle(&projectionWeightsTensor, &projectionWeights[0][0]); + AllocateAndCopyDataToITensorHandle(&projectionBiasTensor, &projectionBias[0]); + + AllocateAndCopyDataToITensorHandle(&inputLayerNormWeightsTensor, &inputLayerNormWeights[0]); + AllocateAndCopyDataToITensorHandle(&forgetLayerNormWeightsTensor, &forgetLayerNormWeights[0]); + AllocateAndCopyDataToITensorHandle(&cellLayerNormWeightsTensor, &cellLayerNormWeights[0]); + AllocateAndCopyDataToITensorHandle(&outputLayerNormWeightsTensor, &outputLayerNormWeights[0]); + + data.m_InputToInputWeights = &inputToInputWeightsTensor; + data.m_InputToForgetWeights = &inputToForgetWeightsTensor; + data.m_InputToCellWeights = &inputToCellWeightsTensor; + data.m_InputToOutputWeights = &inputToOutputWeightsTensor; + data.m_RecurrentToInputWeights = &recurrentToInputWeightsTensor; + data.m_RecurrentToForgetWeights = &recurrentToForgetWeightsTensor; + data.m_RecurrentToCellWeights = &recurrentToCellWeightsTensor; + data.m_RecurrentToOutputWeights = &recurrentToOutputWeightsTensor; + data.m_CellToInputWeights = &cellToInputWeightsTensor; + data.m_InputGateBias = &inputGateBiasTensor; + data.m_ForgetGateBias = &forgetGateBiasTensor; + data.m_CellBias = &cellBiasTensor; + data.m_OutputGateBias = &outputGateBiasTensor; + data.m_CellToForgetWeights = &cellToForgetWeightsTensor; + data.m_CellToOutputWeights = &cellToOutputWeightsTensor; + data.m_ProjectionWeights = &projectionWeightsTensor; + data.m_ProjectionBias = &projectionBiasTensor; + + data.m_InputLayerNormWeights = &inputLayerNormWeightsTensor; + data.m_ForgetLayerNormWeights = &forgetLayerNormWeightsTensor; + data.m_CellLayerNormWeights = &cellLayerNormWeightsTensor; + data.m_OutputLayerNormWeights = &outputLayerNormWeightsTensor; + + // Flags to set test configuration + data.m_Parameters.m_ActivationFunc = 4; + data.m_Parameters.m_CifgEnabled = false; + data.m_Parameters.m_PeepholeEnabled = true; + data.m_Parameters.m_ProjectionEnabled = true; + data.m_Parameters.m_LayerNormEnabled = true; + + + std::unique_ptr workload = workloadFactory.CreateLstm(data, info); + inputHandle->Allocate(); + outputStateInHandle->Allocate(); + cellStateInHandle->Allocate(); + + scratchHandle->Allocate(); + outputStateOutHandle->Allocate(); + cellStateOutHandle->Allocate(); + outputHandle->Allocate(); + + CopyDataToITensorHandle(inputHandle.get(), &inputTensor[0][0]); + CopyDataToITensorHandle(outputStateInHandle.get(), &outputStateInTensor[0][0]); + CopyDataToITensorHandle(cellStateInHandle.get(), &cellStateInTensor[0][0]); + + workload->Execute(); + + CopyDataFromITensorHandle(&ret.output[0][0], outputHandle.get()); + + return ret; + +} \ No newline at end of file diff --git a/src/backends/backendsCommon/test/WorkloadDataValidation.cpp b/src/backends/backendsCommon/test/WorkloadDataValidation.cpp index 7c7af2ddce..c6960986b3 100644 --- a/src/backends/backendsCommon/test/WorkloadDataValidation.cpp +++ b/src/backends/backendsCommon/test/WorkloadDataValidation.cpp @@ -453,22 +453,139 @@ BOOST_AUTO_TEST_CASE(ReshapeQueueDescriptor_Validate_MismatchingNumElements) BOOST_AUTO_TEST_CASE(LstmQueueDescriptor_Validate) { - armnn::TensorInfo inputTensorInfo; - armnn::TensorInfo outputTensorInfo; - - unsigned int inputShape[] = { 1, 2 }; - unsigned int outputShape[] = { 1 }; - - inputTensorInfo = armnn::TensorInfo(2, inputShape, armnn::DataType::Float32); - outputTensorInfo = armnn::TensorInfo(1, outputShape, armnn::DataType::Float32); - - LstmQueueDescriptor invalidData; - WorkloadInfo invalidInfo; - - AddInputToWorkload(invalidData, invalidInfo, inputTensorInfo, nullptr); - AddOutputToWorkload(invalidData, invalidInfo, outputTensorInfo, nullptr); - - BOOST_CHECK_THROW(invalidData.Validate(invalidInfo), armnn::InvalidArgumentException); + armnn::DataType dataType = armnn::DataType::Float32; + + float qScale = 0.0f; + int32_t qOffset = 0; + + unsigned int batchSize = 2; + unsigned int outputSize = 3; + unsigned int inputSize = 5; + unsigned numUnits = 4; + + armnn::TensorInfo inputTensorInfo({batchSize , inputSize}, dataType, qScale, qOffset ); + armnn::TensorInfo outputStateInTensorInfo({batchSize , outputSize}, dataType, qScale, qOffset); + armnn::TensorInfo cellStateInTensorInfo({batchSize , numUnits}, dataType, qScale, qOffset); + + // Scratch buffer size with CIFG [batchSize, numUnits * 4] + armnn::TensorInfo scratchBufferTensorInfo({batchSize, numUnits * 4}, dataType, qScale, qOffset); + armnn::TensorInfo cellStateOutTensorInfo({batchSize, numUnits}, dataType, qScale, qOffset); + armnn::TensorInfo outputStateOutTensorInfo({batchSize, outputSize}, dataType, qScale, qOffset); + armnn::TensorInfo outputTensorInfo({batchSize, outputSize}, dataType, qScale, qOffset); + + armnn::TensorInfo tensorInfo3({outputSize}, dataType, qScale, qOffset); + armnn::TensorInfo tensorInfo4({numUnits}, dataType, qScale, qOffset); + armnn::TensorInfo tensorInfo4x5({numUnits, inputSize}, dataType, qScale, qOffset); + armnn::TensorInfo tensorInfo4x3({numUnits, outputSize}, dataType, qScale, qOffset); + armnn::TensorInfo tensorInfo3x4({outputSize, numUnits}, dataType, qScale, qOffset); + + LstmQueueDescriptor data; + WorkloadInfo info; + + AddInputToWorkload(data, info, inputTensorInfo, nullptr); + AddInputToWorkload(data, info, outputStateInTensorInfo, nullptr); + AddInputToWorkload(data, info, cellStateInTensorInfo, nullptr); + + AddOutputToWorkload(data, info, scratchBufferTensorInfo, nullptr); + AddOutputToWorkload(data, info, outputStateOutTensorInfo, nullptr); + AddOutputToWorkload(data, info, cellStateOutTensorInfo, nullptr); + // AddOutputToWorkload(data, info, outputTensorInfo, nullptr); is left out + + armnn::ScopedCpuTensorHandle inputToInputWeightsTensor(tensorInfo4x5); + armnn::ScopedCpuTensorHandle inputToForgetWeightsTensor(tensorInfo4x5); + armnn::ScopedCpuTensorHandle inputToCellWeightsTensor(tensorInfo4x5); + armnn::ScopedCpuTensorHandle inputToOutputWeightsTensor(tensorInfo4x5); + armnn::ScopedCpuTensorHandle recurrentToForgetWeightsTensor(tensorInfo4x3); + armnn::ScopedCpuTensorHandle recurrentToInputWeightsTensor(tensorInfo4x3); + armnn::ScopedCpuTensorHandle recurrentToCellWeightsTensor(tensorInfo4x3); + armnn::ScopedCpuTensorHandle recurrentToOutputWeightsTensor(tensorInfo4x3); + armnn::ScopedCpuTensorHandle cellToInputWeightsTensor(tensorInfo4); + armnn::ScopedCpuTensorHandle inputGateBiasTensor(tensorInfo4); + armnn::ScopedCpuTensorHandle forgetGateBiasTensor(tensorInfo4); + armnn::ScopedCpuTensorHandle cellBiasTensor(tensorInfo4); + armnn::ScopedCpuTensorHandle outputGateBiasTensor(tensorInfo4); + armnn::ScopedCpuTensorHandle cellToForgetWeightsTensor(tensorInfo4); + armnn::ScopedCpuTensorHandle cellToOutputWeightsTensor(tensorInfo4); + armnn::ScopedCpuTensorHandle projectionWeightsTensor(tensorInfo3x4); + armnn::ScopedCpuTensorHandle projectionBiasTensor(tensorInfo3); + armnn::ScopedCpuTensorHandle inputLayerNormWeightsTensor(tensorInfo4); + armnn::ScopedCpuTensorHandle forgetLayerNormWeightsTensor(tensorInfo4); + armnn::ScopedCpuTensorHandle cellLayerNormWeightsTensor(tensorInfo4); + armnn::ScopedCpuTensorHandle outputLayerNormWeightsTensor(tensorInfo4); + + data.m_InputToInputWeights = &inputToInputWeightsTensor; + data.m_InputToForgetWeights = &inputToForgetWeightsTensor; + data.m_InputToCellWeights = &inputToCellWeightsTensor; + data.m_InputToOutputWeights = &inputToOutputWeightsTensor; + data.m_RecurrentToInputWeights = &recurrentToInputWeightsTensor; + data.m_RecurrentToForgetWeights = &recurrentToForgetWeightsTensor; + data.m_RecurrentToCellWeights = &recurrentToCellWeightsTensor; + data.m_RecurrentToOutputWeights = &recurrentToOutputWeightsTensor; + data.m_CellToInputWeights = &cellToInputWeightsTensor; + data.m_InputGateBias = &inputGateBiasTensor; + data.m_ForgetGateBias = &forgetGateBiasTensor; + data.m_CellBias = &cellBiasTensor; + data.m_OutputGateBias = &outputGateBiasTensor; + data.m_CellToForgetWeights = &cellToForgetWeightsTensor; + data.m_CellToOutputWeights = &cellToOutputWeightsTensor; + data.m_ProjectionWeights = &projectionWeightsTensor; + data.m_ProjectionBias = &projectionBiasTensor; + + data.m_InputLayerNormWeights = &inputLayerNormWeightsTensor; + data.m_ForgetLayerNormWeights = &forgetLayerNormWeightsTensor; + data.m_CellLayerNormWeights = &cellLayerNormWeightsTensor; + data.m_OutputLayerNormWeights = &outputLayerNormWeightsTensor; + + // Flags to set test configuration + data.m_Parameters.m_ActivationFunc = 4; + data.m_Parameters.m_CifgEnabled = false; + data.m_Parameters.m_PeepholeEnabled = true; + data.m_Parameters.m_ProjectionEnabled = true; + data.m_Parameters.m_LayerNormEnabled = true; + + // check wrong number of outputs + BOOST_CHECK_THROW(data.Validate(info), armnn::InvalidArgumentException); + AddOutputToWorkload(data, info, outputTensorInfo, nullptr); + + // check wrong cifg parameter configuration + data.m_Parameters.m_CifgEnabled = true; + armnn::TensorInfo scratchBufferTensorInfo2({batchSize, numUnits * 3}, dataType, qScale, qOffset); + SetWorkloadOutput(data, info, 0, scratchBufferTensorInfo2, nullptr); + BOOST_CHECK_THROW(data.Validate(info), armnn::InvalidArgumentException); + data.m_Parameters.m_CifgEnabled = false; + SetWorkloadOutput(data, info, 0, scratchBufferTensorInfo, nullptr); + + // check wrong inputGateBias configuration + data.m_InputGateBias = nullptr; + BOOST_CHECK_THROW(data.Validate(info), armnn::InvalidArgumentException); + data.m_InputGateBias = &inputGateBiasTensor; + + // check inconsistant projection parameters + data.m_Parameters.m_ProjectionEnabled = false; + BOOST_CHECK_THROW(data.Validate(info), armnn::InvalidArgumentException); + data.m_Parameters.m_ProjectionEnabled = true; + data.m_ProjectionWeights = nullptr; + BOOST_CHECK_THROW(data.Validate(info), armnn::InvalidArgumentException); + data.m_ProjectionWeights = &projectionWeightsTensor; + + // check missing input layer normalisation weights + data.m_InputLayerNormWeights = nullptr; + BOOST_CHECK_THROW(data.Validate(info), armnn::InvalidArgumentException); + data.m_InputLayerNormWeights = &inputLayerNormWeightsTensor; + + // layer norm disabled but normalisation weights are present + data.m_Parameters.m_LayerNormEnabled = false; + BOOST_CHECK_THROW(data.Validate(info), armnn::InvalidArgumentException); + data.m_Parameters.m_LayerNormEnabled = true; + + // check invalid outputTensor shape + armnn::TensorInfo incorrectOutputTensorInfo({batchSize, outputSize + 1}, dataType, qScale, qOffset); + SetWorkloadOutput(data, info, 3, incorrectOutputTensorInfo, nullptr); + BOOST_CHECK_THROW(data.Validate(info), armnn::InvalidArgumentException); + SetWorkloadOutput(data, info, 3, outputTensorInfo, nullptr); + + // check correct configuration + BOOST_CHECK_NO_THROW(data.Validate(info)); } BOOST_AUTO_TEST_SUITE_END() -- cgit v1.2.1