1 /* Copyright 2020 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/conv_test.h"
19 #include "tensorflow/lite/micro/kernels/kernel_runner.h"
20 #include "tensorflow/lite/micro/micro_utils.h"
21 #include "tensorflow/lite/micro/test_helpers.h"
22 #include "tensorflow/lite/micro/testing/micro_test.h"
23
24 namespace tflite {
25 namespace testing {
26 namespace {
27
28 // Common inputs and outputs.
29 constexpr int kInputElements = 32;
30 static const int kInputShape[] = {4, 1, 4, 4, 2};
31 static const float kInputData[kInputElements] = {
32 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
33 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32};
34
35 constexpr int kFilterElements = 18;
36 static const int kFilterShape[] = {4, 1, 3, 3, 2};
37 static const float kFilterData[kFilterElements] = {
38 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18};
39
40 constexpr int kBiasElements = 1;
41 static const int kBiasShape[] = {4, 1, 1, 1, 1};
42 static const float kBiasData[kBiasElements] = {0};
43
44 constexpr int kOutputElements = 16;
45 static const int kOutputShape[] = {4, 1, 4, 4, 1};
46 static const float kGoldenData[kOutputElements] = {
47 184, 412, 568, 528, 678, 1347, 1689, 1434,
48 1494, 2715, 3057, 2442, 1968, 3352, 3652, 2760};
49
50 // Transpose conv uses TfLiteConvParams.
51 static TfLiteConvParams common_conv_params = {kTfLitePaddingSame, // padding
52 1, // stride_width
53 1, // stride_height
54 kTfLiteActNone,
55 1,
56 1};
57
58 template <typename T>
InvokeTransposeConv(TfLiteTensor * tensors,int tensors_size,int output_length,TfLiteConvParams * conv_params,T * output_data)59 TfLiteStatus InvokeTransposeConv(TfLiteTensor* tensors, int tensors_size,
60 int output_length,
61 TfLiteConvParams* conv_params,
62 T* output_data) {
63 int inputs_array_data[] = {4, 0, 1, 2, 3};
64 TfLiteIntArray* inputs_array = IntArrayFromInts(inputs_array_data);
65 int outputs_array_data[] = {1, 4};
66 TfLiteIntArray* outputs_array = IntArrayFromInts(outputs_array_data);
67
68 const TfLiteRegistration registration = tflite::Register_TRANSPOSE_CONV();
69 micro::KernelRunner runner(registration, tensors, tensors_size, inputs_array,
70 outputs_array, conv_params);
71
72 const char* init_data = reinterpret_cast<const char*>(conv_params);
73 TfLiteStatus status = runner.InitAndPrepare(init_data);
74 if (status != kTfLiteOk) {
75 return status;
76 }
77 return runner.Invoke();
78 }
79
80 template <typename T>
ValidateTransposeConvGoldens(TfLiteTensor * tensors,int tensors_size,const T * expected_output_data,int output_length,TfLiteConvParams * conv_params,T * output_data,float tolerance)81 TfLiteStatus ValidateTransposeConvGoldens(TfLiteTensor* tensors,
82 int tensors_size,
83 const T* expected_output_data,
84 int output_length,
85 TfLiteConvParams* conv_params,
86 T* output_data, float tolerance) {
87 TfLiteStatus status = InvokeTransposeConv(
88 tensors, tensors_size, output_length, conv_params, output_data);
89 if (status != kTfLiteOk) {
90 return status;
91 }
92 for (int i = 0; i < output_length; ++i) {
93 TF_LITE_MICRO_EXPECT_NEAR(expected_output_data[i], output_data[i],
94 tolerance);
95 }
96 return kTfLiteOk;
97 }
98
TestTransposeConvFloat(const int * input_dims_data,const float * input_data,const int * filter_dims_data,const float * filter_data,const int * bias_dims_data,const float * bias_data,const int * output_dims_data,const float * expected_output_data,TfLiteConvParams * conv_params,float * output_data)99 TfLiteStatus TestTransposeConvFloat(
100 const int* input_dims_data, const float* input_data,
101 const int* filter_dims_data, const float* filter_data,
102 const int* bias_dims_data, const float* bias_data,
103 const int* output_dims_data, const float* expected_output_data,
104 TfLiteConvParams* conv_params, float* output_data) {
105 TfLiteIntArray* input_dims = IntArrayFromInts(input_dims_data);
106 TfLiteIntArray* filter_dims = IntArrayFromInts(filter_dims_data);
107 TfLiteIntArray* bias_dims = IntArrayFromInts(bias_dims_data);
108 TfLiteIntArray* output_dims = IntArrayFromInts(output_dims_data);
109 const int output_dims_count = ElementCount(*output_dims);
110
111 const int output_shape_dims_data[] = {1, 0};
112 int32_t* output_shape = nullptr;
113 TfLiteIntArray* output_shape_dims = IntArrayFromInts(output_shape_dims_data);
114
115 constexpr int inputs_size = 4;
116 constexpr int outputs_size = 1;
117 constexpr int tensors_size = inputs_size + outputs_size;
118 TfLiteTensor tensors[tensors_size] = {
119 CreateTensor(output_shape, output_shape_dims),
120 CreateTensor(filter_data, filter_dims),
121 CreateTensor(input_data, input_dims),
122 CreateTensor(bias_data, bias_dims),
123 CreateTensor(output_data, output_dims),
124 };
125
126 return ValidateTransposeConvGoldens(tensors, tensors_size,
127 expected_output_data, output_dims_count,
128 conv_params, output_data, 0.001f);
129 }
130
TestTransposeConvQuantized(const int * input_dims_data,const float * input_data,int8_t * input_quantized,float input_scale,int input_zero_point,const int * filter_dims_data,const float * filter_data,int8_t * filter_quantized,float filter_scale,const int * bias_dims_data,const float * bias_data,int32_t * bias_quantized,float * bias_scales,int * bias_zero_points,const int * output_dims_data,const float * expected_output_data,int8_t * expected_output_quantized,float output_scale,int output_zero_point,TfLiteConvParams * conv_params,int8_t * output_data)131 TfLiteStatus TestTransposeConvQuantized(
132 const int* input_dims_data, const float* input_data,
133 int8_t* input_quantized, float input_scale, int input_zero_point,
134 const int* filter_dims_data, const float* filter_data,
135 int8_t* filter_quantized, float filter_scale, const int* bias_dims_data,
136 const float* bias_data, int32_t* bias_quantized, float* bias_scales,
137 int* bias_zero_points, const int* output_dims_data,
138 const float* expected_output_data, int8_t* expected_output_quantized,
139 float output_scale, int output_zero_point, TfLiteConvParams* conv_params,
140 int8_t* output_data) {
141 TfLiteIntArray* input_dims = IntArrayFromInts(input_dims_data);
142 TfLiteIntArray* filter_dims = IntArrayFromInts(filter_dims_data);
143 TfLiteIntArray* bias_dims = IntArrayFromInts(bias_dims_data);
144 TfLiteIntArray* output_dims = IntArrayFromInts(output_dims_data);
145 const int output_dims_count = ElementCount(*output_dims);
146
147 int filter_zero_points[5];
148 float filter_scales[5];
149 TfLiteAffineQuantization filter_quant;
150 TfLiteTensor filter_tensor = CreateSymmetricPerChannelQuantizedTensor(
151 filter_data, filter_quantized, filter_dims, filter_scales,
152 filter_zero_points, &filter_quant, 0 /* quantized dimension */);
153 tflite::Quantize(expected_output_data, expected_output_quantized,
154 output_dims_count, output_scale, 0);
155
156 const int output_shape_dims_data[] = {1, 0};
157 int32_t* output_shape = nullptr;
158 TfLiteIntArray* output_shape_dims = IntArrayFromInts(output_shape_dims_data);
159
160 constexpr int inputs_size = 4;
161 constexpr int outputs_size = 1;
162 constexpr int tensors_size = inputs_size + outputs_size;
163 TfLiteTensor tensors[tensors_size] = {
164 CreateTensor(output_shape, output_shape_dims), filter_tensor,
165 CreateQuantizedTensor(input_data, input_quantized, input_dims,
166 input_scale, input_zero_point),
167 CreateQuantizedBiasTensor(bias_data, bias_quantized, bias_dims,
168 input_scale, filter_scale),
169 CreateQuantizedTensor(output_data, output_dims, output_scale,
170 output_zero_point)};
171
172 return ValidateTransposeConvGoldens(
173 tensors, tensors_size, expected_output_quantized, output_dims_count,
174 conv_params, output_data, 1.0f);
175 }
176 } // namespace
177 } // namespace testing
178 } // namespace tflite
179
180 TF_LITE_MICRO_TESTS_BEGIN
181
TF_LITE_MICRO_TEST(SimpleTestFloat)182 TF_LITE_MICRO_TEST(SimpleTestFloat) {
183 float output_data[tflite::testing::kOutputElements];
184
185 TF_LITE_MICRO_EXPECT_EQ(
186 kTfLiteOk,
187 tflite::testing::TestTransposeConvFloat(
188 tflite::testing::kInputShape, tflite::testing::kInputData,
189 tflite::testing::kFilterShape, tflite::testing::kFilterData,
190 tflite::testing::kBiasShape, tflite::testing::kBiasData,
191 tflite::testing::kOutputShape, tflite::testing::kGoldenData,
192 &tflite::testing::common_conv_params, output_data));
193 }
194
TF_LITE_MICRO_TEST(SimpleTestQuantizedPerChannel)195 TF_LITE_MICRO_TEST(SimpleTestQuantizedPerChannel) {
196 int8_t output_data[tflite::testing::kOutputElements];
197
198 const float input_scale = 0.5f;
199 const float output_scale = 1.0f;
200 const float filter_scale = 1.0f;
201 const int input_zero_point = 0;
202 const int output_zero_point = 0;
203
204 int8_t input_quantized[tflite::testing::kInputElements];
205 int8_t filter_quantized[tflite::testing::kFilterElements];
206 int32_t bias_quantized[tflite::testing::kBiasElements];
207 int8_t golden_quantized[tflite::testing::kOutputElements];
208 int zero_points[tflite::testing::kBiasElements + 1];
209 float scales[tflite::testing::kBiasElements + 1];
210
211 TF_LITE_MICRO_EXPECT_EQ(
212 kTfLiteOk,
213 tflite::testing::TestTransposeConvQuantized(
214 tflite::testing::kInputShape, tflite::testing::kInputData,
215 input_quantized, input_scale, input_zero_point,
216 tflite::testing::kFilterShape, tflite::testing::kFilterData,
217 filter_quantized, filter_scale, tflite::testing::kBiasShape,
218 tflite::testing::kBiasData, bias_quantized, scales, zero_points,
219 tflite::testing::kOutputShape, tflite::testing::kGoldenData,
220 golden_quantized, output_scale, output_zero_point,
221 &tflite::testing::common_conv_params, output_data));
222 }
223
TF_LITE_MICRO_TEST(InputOutputDifferentTypeIsError)224 TF_LITE_MICRO_TEST(InputOutputDifferentTypeIsError) {
225 using tflite::testing::CreateQuantizedTensor;
226 using tflite::testing::CreateTensor;
227 using tflite::testing::IntArrayFromInts;
228
229 TfLiteIntArray* input_dims = IntArrayFromInts(tflite::testing::kInputShape);
230 TfLiteIntArray* filter_dims = IntArrayFromInts(tflite::testing::kFilterShape);
231 TfLiteIntArray* bias_dims = IntArrayFromInts(tflite::testing::kBiasShape);
232 TfLiteIntArray* output_dims = IntArrayFromInts(tflite::testing::kOutputShape);
233 const int output_dims_count = tflite::ElementCount(*output_dims);
234 constexpr int inputs_size = 4;
235 constexpr int outputs_size = 1;
236 constexpr int tensors_size = inputs_size + outputs_size;
237
238 int8_t output_data[tflite::testing::kOutputElements];
239
240 const int output_shape_dims_data[] = {1, 0};
241 int32_t* output_shape = nullptr;
242 TfLiteIntArray* output_shape_dims = IntArrayFromInts(output_shape_dims_data);
243
244 TfLiteTensor tensors[tensors_size] = {
245 CreateTensor(output_shape, output_shape_dims),
246 CreateTensor(tflite::testing::kInputData, input_dims),
247 CreateTensor(tflite::testing::kFilterData, filter_dims),
248 CreateTensor(tflite::testing::kBiasData, bias_dims),
249 CreateQuantizedTensor(output_data, output_dims, /*scale=*/1.0f,
250 /*zero_point=*/0),
251 };
252 TF_LITE_MICRO_EXPECT_EQ(
253 kTfLiteError, tflite::testing::InvokeTransposeConv(
254 tensors, tensors_size, output_dims_count,
255 &tflite::testing::common_conv_params, output_data));
256 }
257
TF_LITE_MICRO_TEST(HybridModeIsError)258 TF_LITE_MICRO_TEST(HybridModeIsError) {
259 using tflite::testing::CreateQuantizedTensor;
260 using tflite::testing::CreateTensor;
261 using tflite::testing::IntArrayFromInts;
262
263 TfLiteIntArray* input_dims = IntArrayFromInts(tflite::testing::kInputShape);
264 TfLiteIntArray* filter_dims = IntArrayFromInts(tflite::testing::kFilterShape);
265 TfLiteIntArray* bias_dims = IntArrayFromInts(tflite::testing::kBiasShape);
266 TfLiteIntArray* output_dims = IntArrayFromInts(tflite::testing::kOutputShape);
267 const int output_dims_count = tflite::ElementCount(*output_dims);
268
269 constexpr int inputs_size = 4;
270 constexpr int outputs_size = 1;
271 constexpr int tensors_size = inputs_size + outputs_size;
272
273 int8_t filter_data[tflite::testing::kFilterElements] = {};
274 float output_data[tflite::testing::kOutputElements];
275
276 const int output_shape_dims_data[] = {1, 0};
277 int32_t* output_shape = nullptr;
278 TfLiteIntArray* output_shape_dims = IntArrayFromInts(output_shape_dims_data);
279
280 TfLiteTensor tensors[tensors_size] = {
281 CreateTensor(output_shape, output_shape_dims),
282 CreateTensor(tflite::testing::kInputData, input_dims),
283 CreateQuantizedTensor(filter_data, filter_dims,
284 /*scale=*/1.0f,
285 /*zero_point=*/0),
286 CreateTensor(tflite::testing::kBiasData, bias_dims),
287 CreateTensor(output_data, output_dims),
288 };
289
290 TF_LITE_MICRO_EXPECT_EQ(
291 kTfLiteError, tflite::testing::InvokeTransposeConv(
292 tensors, tensors_size, output_dims_count,
293 &tflite::testing::common_conv_params, output_data));
294 }
295
296 TF_LITE_MICRO_TESTS_END
297