1 /* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
2 
3 Licensed under the Apache License, Version 2.0 (the "License");
4 you may not use this file except in compliance with the License.
5 You may obtain a copy of the License at
6 
7     http://www.apache.org/licenses/LICENSE-2.0
8 
9 Unless required by applicable law or agreed to in writing, software
10 distributed under the License is distributed on an "AS IS" BASIS,
11 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 See the License for the specific language governing permissions and
13 limitations under the License.
14 ==============================================================================*/
15 
16 #include "tensorflow/lite/c/builtin_op_data.h"
17 #include "tensorflow/lite/c/common.h"
18 #include "tensorflow/lite/micro/kernels/kernel_runner.h"
19 #include "tensorflow/lite/micro/test_helpers.h"
20 #include "tensorflow/lite/micro/testing/micro_test.h"
21 
22 namespace tflite {
23 namespace testing {
24 namespace {
25 
26 template <typename T>
ValidateQuantizeGoldens(TfLiteTensor * tensors,int tensors_size,const float * golden,T * golden_quantized,float scale,int zero_point,int output_len,T * output_data)27 void ValidateQuantizeGoldens(TfLiteTensor* tensors, int tensors_size,
28                              const float* golden, T* golden_quantized,
29                              float scale, int zero_point, int output_len,
30                              T* output_data) {
31   int inputs_array_data[] = {1, 0};
32   TfLiteIntArray* inputs_array = IntArrayFromInts(inputs_array_data);
33   int outputs_array_data[] = {1, 1};
34   TfLiteIntArray* outputs_array = IntArrayFromInts(outputs_array_data);
35 
36   // Version 1 of quantize supports int8_t and uint8_t quantization.
37   const TfLiteRegistration registration = Register_QUANTIZE();
38   micro::KernelRunner runner(registration, tensors, tensors_size, inputs_array,
39                              outputs_array,
40                              /*builtin_data=*/nullptr);
41 
42   TF_LITE_MICRO_EXPECT_EQ(kTfLiteOk, runner.InitAndPrepare());
43   TF_LITE_MICRO_EXPECT_EQ(kTfLiteOk, runner.Invoke());
44 
45   // Use reference quantization from test utils to compare against op output.
46   Quantize(golden, golden_quantized, output_len, scale, zero_point);
47   for (int i = 0; i < output_len; ++i) {
48     TF_LITE_MICRO_EXPECT_EQ(golden_quantized[i], output_data[i]);
49   }
50 }
51 
52 #if !defined(XTENSA)
53 template <typename T>
TestQuantizeFloat(int * input_dims_data,const float * input_data,int * output_dims_data,const float * golden,T * golden_quantized,const float scale,const int zero_point,T * output_data)54 void TestQuantizeFloat(int* input_dims_data, const float* input_data,
55                        int* output_dims_data, const float* golden,
56                        T* golden_quantized, const float scale,
57                        const int zero_point, T* output_data) {
58   TfLiteIntArray* input_dims = IntArrayFromInts(input_dims_data);
59   TfLiteIntArray* output_dims = IntArrayFromInts(output_dims_data);
60   const int output_dims_count = ElementCount(*output_dims);
61 
62   TfLiteTensor output_tensor =
63       CreateQuantizedTensor(output_data, output_dims, scale, zero_point);
64 
65   TfLiteAffineQuantization quant;
66   float scales[] = {1, scale};
67   int zero_points[] = {1, zero_point};
68   quant.scale = FloatArrayFromFloats(scales);
69   quant.zero_point = IntArrayFromInts(zero_points);
70   output_tensor.quantization = {kTfLiteAffineQuantization, &quant};
71 
72   // 1 input, 1 output.
73   constexpr int tensors_size = 2;
74   TfLiteTensor tensors[tensors_size] = {
75       CreateTensor(input_data, input_dims),
76       output_tensor,
77   };
78 
79   ValidateQuantizeGoldens(tensors, tensors_size, golden, golden_quantized,
80                           scale, zero_point, output_dims_count, output_data);
81 }
82 #endif  // defined(XTENSA)
83 
84 template <typename InputType, typename OutputType>
TestRequantize(int * input_dims_data,const float * input_data,InputType * input_quantized,const float input_scale,const int input_zero_point,int * output_dims_data,const float * golden,OutputType * golden_quantized,const float output_scale,const int output_zero_point,OutputType * output_data)85 void TestRequantize(int* input_dims_data, const float* input_data,
86                     InputType* input_quantized, const float input_scale,
87                     const int input_zero_point, int* output_dims_data,
88                     const float* golden, OutputType* golden_quantized,
89                     const float output_scale, const int output_zero_point,
90                     OutputType* output_data) {
91   TfLiteIntArray* input_dims = IntArrayFromInts(input_dims_data);
92   TfLiteIntArray* output_dims = IntArrayFromInts(output_dims_data);
93   const int output_dims_count = ElementCount(*output_dims);
94 
95   TfLiteTensor output_tensor = CreateQuantizedTensor(
96       output_data, output_dims, output_scale, output_zero_point);
97 
98   TfLiteAffineQuantization quant;
99   float scales[] = {1, output_scale};
100   int zero_points[] = {1, output_zero_point};
101   quant.scale = FloatArrayFromFloats(scales);
102   quant.zero_point = IntArrayFromInts(zero_points);
103   output_tensor.quantization = {kTfLiteAffineQuantization, &quant};
104 
105   // 1 input, 1 output.
106   constexpr int tensors_size = 2;
107   TfLiteTensor tensors[tensors_size] = {
108       CreateQuantizedTensor(input_data, input_quantized, input_dims,
109                             input_scale, input_zero_point),
110       output_tensor,
111   };
112 
113   ValidateQuantizeGoldens(tensors, tensors_size, golden, golden_quantized,
114                           output_scale, output_zero_point, output_dims_count,
115                           output_data);
116 }
117 
118 }  // namespace
119 }  // namespace testing
120 }  // namespace tflite
121 
122 TF_LITE_MICRO_TESTS_BEGIN
123 
124 #if !defined(XTENSA)
TF_LITE_MICRO_TEST(QuantizeOpTestInt16)125 TF_LITE_MICRO_TEST(QuantizeOpTestInt16) {
126   const int length = 10;
127   int dims[] = {2, 2, 5};
128   const float values[] = {-63.5, -63,  -62.5, -62,  -61.5,
129                           62,    62.5, 63,    63.5, 64};
130   const float scale = 0.5;
131   const int zero_point = -1;
132   int16_t output[length];
133   int16_t values_quantized[length];
134   tflite::testing::TestQuantizeFloat(
135       dims, values, dims, values, values_quantized, scale, zero_point, output);
136 }
137 
TF_LITE_MICRO_TEST(QuantizeOpTestInt16NoScale)138 TF_LITE_MICRO_TEST(QuantizeOpTestInt16NoScale) {
139   const int length = 10;
140   int dims[] = {2, 2, 5};
141   const float values[] = {-128, -127, -126, -125, -124,
142                           123,  124,  125,  126,  127};
143   const float scale = 1.0;
144   const int zero_point = 0;
145   int16_t output[length];
146   int16_t values_quantized[length];
147   tflite::testing::TestQuantizeFloat(
148       dims, values, dims, values, values_quantized, scale, zero_point, output);
149 }
150 
TF_LITE_MICRO_TEST(QuantizeOpTestInt16toInt16)151 TF_LITE_MICRO_TEST(QuantizeOpTestInt16toInt16) {
152   const int length = 10;
153   int dims[] = {2, 2, 5};
154   const float values[] = {-64, -62, -60, -58, -56, 54, 56, 58, 60, 62};
155   const float input_scale = 2.f;
156   const int input_zero_point = 0;
157   const float output_scale = 0.5;
158   const int output_zero_point = 32;
159   int16_t output_quantized[length];
160   int16_t values_quantized[length];
161   int16_t input_quantized[length];
162   tflite::testing::TestRequantize(dims, values, input_quantized, input_scale,
163                                   input_zero_point, dims, values,
164                                   values_quantized, output_scale,
165                                   output_zero_point, output_quantized);
166 }
167 
TF_LITE_MICRO_TEST(QuantizeOpTestInt16toInt16NoZeroPoint)168 TF_LITE_MICRO_TEST(QuantizeOpTestInt16toInt16NoZeroPoint) {
169   const int length = 10;
170   int dims[] = {2, 2, 5};
171   const float values[] = {-32, -31, -30, -29, -28, 27, 28, 29, 30, 31};
172   const float input_scale = 1.f;
173   const int input_zero_point = 0;
174   const float output_scale = 0.5;
175   const int output_zero_point = 0;
176   int16_t output_quantized[length];
177   int16_t values_quantized[length];
178   int16_t input_quantized[length];
179   tflite::testing::TestRequantize(dims, values, input_quantized, input_scale,
180                                   input_zero_point, dims, values,
181                                   values_quantized, output_scale,
182                                   output_zero_point, output_quantized);
183 }
184 
TF_LITE_MICRO_TEST(QuantizeOpTestInt8toInt8)185 TF_LITE_MICRO_TEST(QuantizeOpTestInt8toInt8) {
186   const int length = 10;
187   int dims[] = {2, 2, 5};
188   const float values[] = {-64, -62, -60, -58, -56, 54, 56, 58, 60, 62};
189   const float input_scale = 2.f;
190   const int input_zero_point = 0;
191   const float output_scale = 0.5;
192   const int output_zero_point = 32;
193   int8_t output_quantized[length];
194   int8_t values_quantized[length];
195   int8_t input_quantized[length];
196   tflite::testing::TestRequantize(dims, values, input_quantized, input_scale,
197                                   input_zero_point, dims, values,
198                                   values_quantized, output_scale,
199                                   output_zero_point, output_quantized);
200 }
201 
TF_LITE_MICRO_TEST(QuantizeOpTestInt8toInt8NoZeroPoint)202 TF_LITE_MICRO_TEST(QuantizeOpTestInt8toInt8NoZeroPoint) {
203   const int length = 10;
204   int dims[] = {2, 2, 5};
205   const float values[] = {-32, -31, -30, -29, -28, 27, 28, 29, 30, 31};
206   const float input_scale = 1.f;
207   const int input_zero_point = 0;
208   const float output_scale = 0.5;
209   const int output_zero_point = 0;
210   int8_t output_quantized[length];
211   int8_t values_quantized[length];
212   int8_t input_quantized[length];
213   tflite::testing::TestRequantize(dims, values, input_quantized, input_scale,
214                                   input_zero_point, dims, values,
215                                   values_quantized, output_scale,
216                                   output_zero_point, output_quantized);
217 }
218 
TF_LITE_MICRO_TEST(QuantizeOpTestInt8toInt16)219 TF_LITE_MICRO_TEST(QuantizeOpTestInt8toInt16) {
220   const int length = 10;
221   int dims[] = {2, 2, 5};
222   const float values[] = {-64, -62, -60, -58, -56, 54, 56, 58, 60, 62};
223   const float input_scale = 2.f;
224   const int input_zero_point = 0;
225   const float output_scale = 0.5;
226   const int output_zero_point = 32;
227   int16_t output_quantized[length];
228   int16_t values_quantized[length];
229   int8_t input_quantized[length];
230   tflite::testing::TestRequantize(dims, values, input_quantized, input_scale,
231                                   input_zero_point, dims, values,
232                                   values_quantized, output_scale,
233                                   output_zero_point, output_quantized);
234 }
235 #endif  // defined(XTENSA)
236 
237 #if !defined(XTENSA)
238 // TODO(b/155682734): Hifimini optimized quantize requires input scale to be
239 // smaller then output scale.
TF_LITE_MICRO_TEST(QuantizeOpTestInt16toInt8)240 TF_LITE_MICRO_TEST(QuantizeOpTestInt16toInt8) {
241   const int length = 10;
242   int dims[] = {2, 2, 5};
243   const float values[] = {-64, -62, -60, -58, -56, 54, 56, 58, 60, 62};
244   const float input_scale = 2.f;
245   const int input_zero_point = 0;
246   const float output_scale = 0.5;
247   const int output_zero_point = 0;
248   int8_t output_quantized[length];
249   int8_t values_quantized[length];
250   int16_t input_quantized[length];
251   tflite::testing::TestRequantize(dims, values, input_quantized, input_scale,
252                                   input_zero_point, dims, values,
253                                   values_quantized, output_scale,
254                                   output_zero_point, output_quantized);
255 }
256 #endif  // defined(XTENSA)
257 
TF_LITE_MICRO_TEST(QuantizeOpTestInt8toInt32)258 TF_LITE_MICRO_TEST(QuantizeOpTestInt8toInt32) {
259   const int length = 10;
260   int dims[] = {2, 2, 5};
261   const float values[] = {-32, -31, -30, -29, -28, 27, 28, 29, 30, 31};
262   const float input_scale = 1.f;
263   const int input_zero_point = 0;
264   const float output_scale = 0.5;
265   const int output_zero_point = 0;
266   int32_t output_quantized[length];
267   int32_t values_quantized[length];
268   int8_t input_quantized[length];
269   tflite::testing::TestRequantize(dims, values, input_quantized, input_scale,
270                                   input_zero_point, dims, values,
271                                   values_quantized, output_scale,
272                                   output_zero_point, output_quantized);
273 }
274 
TF_LITE_MICRO_TEST(QuantizeOpTestInt16toInt32)275 TF_LITE_MICRO_TEST(QuantizeOpTestInt16toInt32) {
276   const int length = 10;
277   int dims[] = {2, 2, 5};
278   const float values[] = {-32, -31, -30, -29, -28, 27, 28, 29, 30, 31};
279   const float input_scale = 1.f;
280   const int input_zero_point = 0;
281   const float output_scale = 0.5;
282   const int output_zero_point = 0;
283   int32_t output_quantized[length];
284   int32_t values_quantized[length];
285   int16_t input_quantized[length];
286   tflite::testing::TestRequantize(dims, values, input_quantized, input_scale,
287                                   input_zero_point, dims, values,
288                                   values_quantized, output_scale,
289                                   output_zero_point, output_quantized);
290 }
291 
TF_LITE_MICRO_TEST(QuantizeOpTestInt16toInt8)292 TF_LITE_MICRO_TEST(QuantizeOpTestInt16toInt8) {
293   constexpr int length = 10;
294   int dims[] = {2, 2, 5};
295   const float values[] = {-32, -31, -30, -29, -28, 27, 28, 29, 30, 31};
296   // TODO(b/155682734): Input scale must be smaller than output scale for
297   // xtensa.
298   const float input_scale = 0.4f;
299   const int input_zero_point = 0;
300   const float output_scale = 1.0f;
301   const int output_zero_point = 0;
302   int8_t output_quantized[length];
303   int8_t values_quantized[length];
304   int16_t input_quantized[length];
305   tflite::testing::TestRequantize(dims, values, input_quantized, input_scale,
306                                   input_zero_point, dims, values,
307                                   values_quantized, output_scale,
308                                   output_zero_point, output_quantized);
309 }
310 
311 TF_LITE_MICRO_TESTS_END
312