blob: 38c6675ddb3940d0a469ff9c944e79aeeb0947e6 [file] [log] [blame]
//
// Copyright © 2017 Arm Ltd. All rights reserved.
// SPDX-License-Identifier: MIT
//
#include <boost/test/unit_test.hpp>
#include "ParserFlatbuffersFixture.hpp"
#include "../TfLiteParser.hpp"
#include <sstream>
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::QuantisedAsymm8>(
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::QuantisedAsymm8>(
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<n; ++i ) {
if (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::QuantisedAsymm8>(
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<uint8_t>((1*(2-fz) + 2*(1-fz) + 4*(0-fz) + 8*(6-fz) + bias)/2 + outZero)),
std::max(outZero, static_cast<uint8_t>((2*(2-fz) + 0*(1-fz) + 8*(0-fz) + 0*(6-fz) + bias)/2 + outZero)),
std::max(outZero, static_cast<uint8_t>((4*(2-fz) + 8*(1-fz) + 0*(0-fz) + 0*(6-fz) + bias)/2 + outZero)),
std::max(outZero, static_cast<uint8_t>((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::QuantisedAsymm8>(
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<uint8_t>((1*2 + 2*1 + 4*0 + 1*6)/2)),
std::min(relu6Min, static_cast<uint8_t>((2*2 + 0*1 + 1*0 + 0*6)/2)),
std::min(relu6Min, static_cast<uint8_t>((4*2 + 1*1 + 0*0 + 0*6)/2)),
std::min(relu6Min, static_cast<uint8_t>((1*2 + 0*1 + 0*0 + 0*6)/2))
});
}
BOOST_AUTO_TEST_SUITE_END()