// // Copyright © 2017 Arm Ltd. All rights reserved. // SPDX-License-Identifier: MIT // #include #include "ParserFlatbuffersFixture.hpp" #include "../TfLiteParser.hpp" #include BOOST_AUTO_TEST_SUITE(TensorflowLiteParser) struct SimpleConv2DFixture : public ParserFlatbuffersFixture { explicit SimpleConv2DFixture() { m_JsonString = R"( { "version": 3, "operator_codes": [ { "builtin_code": "CONV_2D" } ], "subgraphs": [ { "tensors": [ { "shape": [ 1, 3, 3, 1 ], "type": "UINT8", "buffer": 0, "name": "inputTensor", "quantization": { "min": [ 0.0 ], "max": [ 255.0 ], "scale": [ 1.0 ], "zero_point": [ 0 ], } }, { "shape": [ 1, 1, 1, 1 ], "type": "UINT8", "buffer": 1, "name": "outputTensor", "quantization": { "min": [ 0.0 ], "max": [ 511.0 ], "scale": [ 2.0 ], "zero_point": [ 0 ], } }, { "shape": [ 1, 3, 3, 1 ], "type": "UINT8", "buffer": 2, "name": "filterTensor", "quantization": { "min": [ 0.0 ], "max": [ 255.0 ], "scale": [ 1.0 ], "zero_point": [ 0 ], } } ], "inputs": [ 0 ], "outputs": [ 1 ], "operators": [ { "opcode_index": 0, "inputs": [ 0, 2 ], "outputs": [ 1 ], "builtin_options_type": "Conv2DOptions", "builtin_options": { "padding": "VALID", "stride_w": 1, "stride_h": 1, "fused_activation_function": "NONE" }, "custom_options_format": "FLEXBUFFERS" } ], } ], "buffers" : [ { }, { }, { "data": [ 2,1,0, 6,2,1, 4,1,2 ], }, { }, ] } )"; SetupSingleInputSingleOutput("inputTensor", "outputTensor"); } }; BOOST_FIXTURE_TEST_CASE( ParseSimpleConv2D, SimpleConv2DFixture ) { RunTest<4, armnn::DataType::QAsymmU8>( 0, { 1, 2, 3, 4, 5, 6, 7, 8, 9, }, // because of the output scaling we need to take half of the values { (1*2 + 2*1 + 3*0 + 4*6 + 5*2 + 6*1 + 7*4 + 8*1 + 9*2) /2 }); } struct Conv2DWithBiasesFixture : public ParserFlatbuffersFixture { explicit Conv2DWithBiasesFixture(const std::string & inputShape, const std::string & outputShape, const std::string & filterShape, const std::string & filterData, const std::string & biasShape, const std::string & biasData, const std::string & strides, const std::string & activation="NONE", const std::string & filterScale="1.0", const std::string & filterZeroPoint="0", const std::string & outputScale="2.0", const std::string & outputZeroPoint="0") { m_JsonString = R"( { "version": 3, "operator_codes": [ { "builtin_code": "CONV_2D" } ], "subgraphs": [ { "tensors": [ { "shape": )" + inputShape + R"(, "type": "UINT8", "buffer": 0, "name": "inputTensor", "quantization": { "min": [ 0.0 ], "max": [ 255.0 ], "scale": [ 1.0 ], "zero_point": [ 0 ], } }, { "shape": )" + outputShape + R"(, "type": "UINT8", "buffer": 1, "name": "outputTensor", "quantization": { "min": [ 0.0 ], "max": [ 511.0 ], "scale": [ )" + outputScale + R"( ], "zero_point": [ )" + outputZeroPoint + R"( ], } }, { "shape": )" + filterShape + R"( , "type": "UINT8", "buffer": 2, "name": "filterTensor", "quantization": { "min": [ 0.0 ], "max": [ 255.0 ], "scale": [ )" + filterScale + R"( ], "zero_point": [ )" + filterZeroPoint + R"( ], } }, { "shape": )" + biasShape + R"( , "type": "INT32", "buffer": 3, "name": "biasTensor", "quantization": { "min": [ 0.0 ], "max": [ 255.0 ], "scale": [ 1.0 ], "zero_point": [ 0 ], } } ], "inputs": [ 0 ], "outputs": [ 1 ], "operators": [ { "opcode_index": 0, "inputs": [ 0, 2, 3 ], "outputs": [ 1 ], "builtin_options_type": "Conv2DOptions", "builtin_options": { "padding": "SAME", "stride_w": )" + strides + R"(, "stride_h": )" + strides + R"(, "fused_activation_function": )" + activation + R"( }, "custom_options_format": "FLEXBUFFERS" } ], } ], "buffers" : [ { }, { }, { "data": )" + filterData + R"(, }, { "data": )" + biasData + R"(, }, ] } )"; SetupSingleInputSingleOutput("inputTensor", "outputTensor"); } }; struct SimpleConv2DWithBiasesFixture : Conv2DWithBiasesFixture { SimpleConv2DWithBiasesFixture() : Conv2DWithBiasesFixture("[ 1, 2, 2, 1 ]", // inputShape "[ 1, 2, 2, 1 ]", // outputShape "[ 1, 2, 2, 1 ]", // filterShape "[ 2,1, 0,6 ]", // filterData "[ 1 ]", // biasShape "[ 10, 0, 0, 0 ]", // biasData "1") // stride w and h {} }; BOOST_FIXTURE_TEST_CASE( ParseConv2DWithBias, SimpleConv2DWithBiasesFixture ) { RunTest<4, armnn::DataType::QAsymmU8>( 0, { 1, 2, 3, 4, }, // because of the output scaling we need to take half of the values { (1*2 + 2*1 + 3*0 + 4*6 + 10)/2, (2*2 + 0*1 + 4*0 + 0*6 + 10)/2, (3*2 + 4*1 + 0*0 + 0*6 + 10)/2, (4*2 + 0*1 + 0*0 + 0*6 + 10)/2 }); } struct Conv2DShapeTestFixture : Conv2DWithBiasesFixture { static std::string GenerateInts(unsigned int n) { std::stringstream ss; ss << " [ "; for( unsigned int i=0; i 0 ) { ss << " , "; } ss << " " << (i%256); } ss << " ] "; return ss.str(); } Conv2DShapeTestFixture() : Conv2DWithBiasesFixture("[ 1, 224, 224, 3 ]", // inputShape "[ 1, 112, 112, 32 ]", // outputShape "[ 32, 3, 3, 3 ]", // filterShape GenerateInts(32*3*3*3), // filterData "[ 32 ]", // biasShape GenerateInts(32*4), // biasData "2") // stride w and h {} }; BOOST_FIXTURE_TEST_CASE( ParseConv2D_112x112_out, Conv2DShapeTestFixture ) { } struct ReluConv2DWithBiasesFixture : Conv2DWithBiasesFixture { ReluConv2DWithBiasesFixture() : Conv2DWithBiasesFixture("[ 1, 2, 2, 1 ]", // inputShape "[ 1, 2, 2, 1 ]", // outputShape "[ 1, 2, 2, 1 ]", // filterShape "[ 2,1, 0,6 ]", // filterData "[ 1 ]", // biasShape "[ 16, 0, 0, 0 ]", // biasData "1", // stride w and h "RELU", // activation "1.0", // filter scale "4", // filter zero point "2.0", // output scale "20") // output zero point {} }; BOOST_FIXTURE_TEST_CASE( ParseConv2DAndReluWithBias, ReluConv2DWithBiasesFixture ) { uint8_t bias = 16; uint8_t outZero = 20; uint8_t fz = 4; // filter zero point RunTest<4, armnn::DataType::QAsymmU8>( 0, { 1, 2, 4, 8, }, // factors to consider: // - the filter zero point is non zero, hence the (x-fz) // - the output scale is 2 hence the /2 // - output zero point is non zero, hence the +outZero // - RELU cuts negative values and then we add the output zero point { std::max(outZero, static_cast((1*(2-fz) + 2*(1-fz) + 4*(0-fz) + 8*(6-fz) + bias)/2 + outZero)), std::max(outZero, static_cast((2*(2-fz) + 0*(1-fz) + 8*(0-fz) + 0*(6-fz) + bias)/2 + outZero)), std::max(outZero, static_cast((4*(2-fz) + 8*(1-fz) + 0*(0-fz) + 0*(6-fz) + bias)/2 + outZero)), std::max(outZero, static_cast((8*(2-fz) + 0*(1-fz) + 0*(0-fz) + 0*(6-fz) + bias)/2 + outZero)) }); } struct Relu6Conv2DWithBiasesFixture : Conv2DWithBiasesFixture { Relu6Conv2DWithBiasesFixture() : Conv2DWithBiasesFixture("[ 1, 2, 2, 1 ]", // inputShape "[ 1, 2, 2, 1 ]", // outputShape "[ 1, 2, 2, 1 ]", // filterShape "[ 2,1, 0,6 ]", // filterData "[ 1 ]", // biasShape "[ 0, 0, 0, 0 ]", // biasData "1", // stride w and h "RELU6", // activation "1.0", // filter scale "0", // filter zero point "2.0", // output scale "0") // output zero point {} }; BOOST_FIXTURE_TEST_CASE( ParseConv2DAndRelu6WithBias, Relu6Conv2DWithBiasesFixture ) { uint8_t relu6Min = 6 / 2; // divide by output scale RunTest<4, armnn::DataType::QAsymmU8>( 0, { 1, 2, 4, 1, }, // factors to consider: // - the output scale is 2 hence the /2 // - RELU6 cuts output values at +6 { std::min(relu6Min, static_cast((1*2 + 2*1 + 4*0 + 1*6)/2)), std::min(relu6Min, static_cast((2*2 + 0*1 + 1*0 + 0*6)/2)), std::min(relu6Min, static_cast((4*2 + 1*1 + 0*0 + 0*6)/2)), std::min(relu6Min, static_cast((1*2 + 0*1 + 0*0 + 0*6)/2)) }); } BOOST_AUTO_TEST_SUITE_END()