blob: 8a17dec47a0b8dc94c6beb1e460324c57e3af817 [file] [log] [blame]
telsoa01c577f2c2018-08-31 09:22:23 +01001//
2// Copyright © 2017 Arm Ltd. All rights reserved.
3// See LICENSE file in the project root for full license information.
4//
5
6#include <boost/test/unit_test.hpp>
7#include "ParserFlatbuffersFixture.hpp"
8#include "../TfLiteParser.hpp"
9#include <sstream>
10
11BOOST_AUTO_TEST_SUITE(TensorflowLiteParser)
12
13struct SimpleConv2DFixture : public ParserFlatbuffersFixture
14{
15 explicit SimpleConv2DFixture()
16 {
17 m_JsonString = R"(
18 {
19 "version": 3,
20 "operator_codes": [ { "builtin_code": "CONV_2D" } ],
21 "subgraphs": [ {
22 "tensors": [
23 {
24 "shape": [ 1, 3, 3, 1 ],
25 "type": "UINT8",
26 "buffer": 0,
27 "name": "inputTensor",
28 "quantization": {
29 "min": [ 0.0 ],
30 "max": [ 255.0 ],
31 "scale": [ 1.0 ],
32 "zero_point": [ 0 ],
33 }
34 },
35 {
36 "shape": [ 1, 1, 1, 1 ],
37 "type": "UINT8",
38 "buffer": 1,
39 "name": "outputTensor",
40 "quantization": {
41 "min": [ 0.0 ],
42 "max": [ 511.0 ],
43 "scale": [ 2.0 ],
44 "zero_point": [ 0 ],
45 }
46 },
47 {
48 "shape": [ 1, 3, 3, 1 ],
49 "type": "UINT8",
50 "buffer": 2,
51 "name": "filterTensor",
52 "quantization": {
53 "min": [ 0.0 ],
54 "max": [ 255.0 ],
55 "scale": [ 1.0 ],
56 "zero_point": [ 0 ],
57 }
58 }
59 ],
60 "inputs": [ 0 ],
61 "outputs": [ 1 ],
62 "operators": [
63 {
64 "opcode_index": 0,
65 "inputs": [ 0, 2 ],
66 "outputs": [ 1 ],
67 "builtin_options_type": "Conv2DOptions",
68 "builtin_options": {
69 "padding": "VALID",
70 "stride_w": 1,
71 "stride_h": 1,
72 "fused_activation_function": "NONE"
73 },
74 "custom_options_format": "FLEXBUFFERS"
75 }
76 ],
77 } ],
78 "buffers" : [
79 { },
80 { },
81 { "data": [ 2,1,0, 6,2,1, 4,1,2 ], },
82 { },
83 ]
84 }
85 )";
86 SetupSingleInputSingleOutput("inputTensor", "outputTensor");
87 }
88};
89
90BOOST_FIXTURE_TEST_CASE( ParseSimpleConv2D, SimpleConv2DFixture )
91{
92 RunTest<4, uint8_t>(
93 0,
94 {
95 1, 2, 3,
96 4, 5, 6,
97 7, 8, 9,
98 },
99 // because of the output scaling we need to take half of the values
100 {
101 (1*2 + 2*1 + 3*0 +
102 4*6 + 5*2 + 6*1 +
103 7*4 + 8*1 + 9*2) /2
104 });
105}
106
107struct Conv2DWithBiasesFixture : public ParserFlatbuffersFixture
108{
109 explicit Conv2DWithBiasesFixture(const std::string & inputShape,
110 const std::string & outputShape,
111 const std::string & filterShape,
112 const std::string & filterData,
113 const std::string & biasShape,
114 const std::string & biasData,
115 const std::string & strides,
116 const std::string & activation="NONE",
117 const std::string & filterScale="1.0",
118 const std::string & filterZeroPoint="0",
119 const std::string & outputScale="2.0",
120 const std::string & outputZeroPoint="0")
121 {
122 m_JsonString = R"(
123 {
124 "version": 3,
125 "operator_codes": [ { "builtin_code": "CONV_2D" } ],
126 "subgraphs": [ {
127 "tensors": [
128 {
129 "shape": )" + inputShape + R"(,
130 "type": "UINT8",
131 "buffer": 0,
132 "name": "inputTensor",
133 "quantization": {
134 "min": [ 0.0 ],
135 "max": [ 255.0 ],
136 "scale": [ 1.0 ],
137 "zero_point": [ 0 ],
138 }
139 },
140 {
141 "shape": )" + outputShape + R"(,
142 "type": "UINT8",
143 "buffer": 1,
144 "name": "outputTensor",
145 "quantization": {
146 "min": [ 0.0 ],
147 "max": [ 511.0 ],
148 "scale": [ )" + outputScale + R"( ],
149 "zero_point": [ )" + outputZeroPoint + R"( ],
150 }
151 },
152 {
153 "shape": )" + filterShape + R"( ,
154 "type": "UINT8",
155 "buffer": 2,
156 "name": "filterTensor",
157 "quantization": {
158 "min": [ 0.0 ],
159 "max": [ 255.0 ],
160 "scale": [ )" + filterScale + R"( ],
161 "zero_point": [ )" + filterZeroPoint + R"( ],
162 }
163 },
164 {
165 "shape": )" + biasShape + R"( ,
166 "type": "INT32",
167 "buffer": 3,
168 "name": "biasTensor",
169 "quantization": {
170 "min": [ 0.0 ],
171 "max": [ 255.0 ],
172 "scale": [ 1.0 ],
173 "zero_point": [ 0 ],
174 }
175 }
176 ],
177 "inputs": [ 0 ],
178 "outputs": [ 1 ],
179 "operators": [
180 {
181 "opcode_index": 0,
182 "inputs": [ 0, 2, 3 ],
183 "outputs": [ 1 ],
184 "builtin_options_type": "Conv2DOptions",
185 "builtin_options": {
186 "padding": "SAME",
187 "stride_w": )" + strides + R"(,
188 "stride_h": )" + strides + R"(,
189 "fused_activation_function": )" + activation + R"(
190 },
191 "custom_options_format": "FLEXBUFFERS"
192 }
193 ],
194 } ],
195 "buffers" : [
196 { },
197 { },
198 { "data": )" + filterData + R"(, },
199 { "data": )" + biasData + R"(, },
200 ]
201 }
202 )";
203 SetupSingleInputSingleOutput("inputTensor", "outputTensor");
204 }
205};
206
207struct SimpleConv2DWithBiasesFixture : Conv2DWithBiasesFixture
208{
209 SimpleConv2DWithBiasesFixture()
210 : Conv2DWithBiasesFixture("[ 1, 2, 2, 1 ]", // inputShape
211 "[ 1, 2, 2, 1 ]", // outputShape
212 "[ 1, 2, 2, 1 ]", // filterShape
213 "[ 2,1, 0,6 ]", // filterData
214 "[ 1 ]", // biasShape
215 "[ 10, 0, 0, 0 ]", // biasData
216 "1") // stride w and h
217 {}
218};
219
220BOOST_FIXTURE_TEST_CASE( ParseConv2DWithBias, SimpleConv2DWithBiasesFixture )
221{
222 RunTest<4, uint8_t>(
223 0,
224 {
225 1, 2,
226 3, 4,
227 },
228 // because of the output scaling we need to take half of the values
229 {
230 (1*2 + 2*1 + 3*0 + 4*6 + 10)/2,
231 (2*2 + 0*1 + 4*0 + 0*6 + 10)/2,
232 (3*2 + 4*1 + 0*0 + 0*6 + 10)/2,
233 (4*2 + 0*1 + 0*0 + 0*6 + 10)/2
234 });
235}
236
237struct Conv2DShapeTestFixture : Conv2DWithBiasesFixture
238{
239 static std::string GenerateInts(unsigned int n)
240 {
241 std::stringstream ss;
242 ss << " [ ";
243 for( unsigned int i=0; i<n; ++i ) {
244 if (i > 0 )
245 {
246 ss << " , ";
247 }
248 ss << " " << (i%256);
249 }
250 ss << " ] ";
251 return ss.str();
252 }
253
254 Conv2DShapeTestFixture()
255 : Conv2DWithBiasesFixture("[ 1, 224, 224, 3 ]", // inputShape
256 "[ 1, 112, 112, 32 ]", // outputShape
257 "[ 32, 3, 3, 3 ]", // filterShape
258 GenerateInts(32*3*3*3), // filterData
259 "[ 32 ]", // biasShape
260 GenerateInts(32*4), // biasData
261 "2") // stride w and h
262 {}
263};
264
265BOOST_FIXTURE_TEST_CASE( ParseConv2D_112x112_out, Conv2DShapeTestFixture )
266{
267}
268
269struct ReluConv2DWithBiasesFixture : Conv2DWithBiasesFixture
270{
271 ReluConv2DWithBiasesFixture()
272 : Conv2DWithBiasesFixture("[ 1, 2, 2, 1 ]", // inputShape
273 "[ 1, 2, 2, 1 ]", // outputShape
274 "[ 1, 2, 2, 1 ]", // filterShape
275 "[ 2,1, 0,6 ]", // filterData
276 "[ 1 ]", // biasShape
277 "[ 16, 0, 0, 0 ]", // biasData
278 "1", // stride w and h
279 "RELU", // activation
280 "1.0", // filter scale
281 "4", // filter zero point
282 "2.0", // output scale
283 "20") // output zero point
284 {}
285};
286
287BOOST_FIXTURE_TEST_CASE( ParseConv2DAndReluWithBias, ReluConv2DWithBiasesFixture )
288{
289 uint8_t bias = 16;
290 uint8_t outZero = 20;
291 uint8_t fz = 4; // filter zero point
292
293 RunTest<4, uint8_t>(
294 0,
295 {
296 1, 2,
297 4, 8,
298 },
299 // factors to consider:
300 // - the filter zero point is non zero, hence the (x-fz)
301 // - the output scale is 2 hence the /2
302 // - output zero point is non zero, hence the +outZero
303 // - RELU cuts negative values and then we add the output zero point
304 {
305 std::max(outZero, static_cast<uint8_t>((1*(2-fz) + 2*(1-fz) + 4*(0-fz) + 8*(6-fz) + bias)/2 + outZero)),
306 std::max(outZero, static_cast<uint8_t>((2*(2-fz) + 0*(1-fz) + 8*(0-fz) + 0*(6-fz) + bias)/2 + outZero)),
307 std::max(outZero, static_cast<uint8_t>((4*(2-fz) + 8*(1-fz) + 0*(0-fz) + 0*(6-fz) + bias)/2 + outZero)),
308 std::max(outZero, static_cast<uint8_t>((8*(2-fz) + 0*(1-fz) + 0*(0-fz) + 0*(6-fz) + bias)/2 + outZero))
309 });
310}
311
312struct Relu6Conv2DWithBiasesFixture : Conv2DWithBiasesFixture
313{
314 Relu6Conv2DWithBiasesFixture()
315 : Conv2DWithBiasesFixture("[ 1, 2, 2, 1 ]", // inputShape
316 "[ 1, 2, 2, 1 ]", // outputShape
317 "[ 1, 2, 2, 1 ]", // filterShape
318 "[ 2,1, 0,6 ]", // filterData
319 "[ 1 ]", // biasShape
320 "[ 0, 0, 0, 0 ]", // biasData
321 "1", // stride w and h
322 "RELU6", // activation
323 "1.0", // filter scale
324 "0", // filter zero point
325 "2.0", // output scale
326 "0") // output zero point
327 {}
328};
329
330BOOST_FIXTURE_TEST_CASE( ParseConv2DAndRelu6WithBias, Relu6Conv2DWithBiasesFixture )
331{
332 uint8_t relu6Min = 6 / 2; // divide by output scale
333
334 RunTest<4, uint8_t>(
335 0,
336 {
337 1, 2,
338 4, 1,
339 },
340 // factors to consider:
341 // - the output scale is 2 hence the /2
342 // - RELU6 cuts output values at +6
343 {
344 std::min(relu6Min, static_cast<uint8_t>((1*2 + 2*1 + 4*0 + 1*6)/2)),
345 std::min(relu6Min, static_cast<uint8_t>((2*2 + 0*1 + 1*0 + 0*6)/2)),
346 std::min(relu6Min, static_cast<uint8_t>((4*2 + 1*1 + 0*0 + 0*6)/2)),
347 std::min(relu6Min, static_cast<uint8_t>((1*2 + 0*1 + 0*0 + 0*6)/2))
348 });
349}
350
351BOOST_AUTO_TEST_SUITE_END()