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 #include <algorithm>
16 #include <cmath>
17 #include <cstdlib>
18 #include <functional>
19 #include <iterator>
20 #include <limits>
21 #include <random>
22 #include <string>
23 #include <vector>
24
25 #include <gtest/gtest.h>
26 #include "tensorflow/lite/kernels/internal/optimized/integer_ops/pooling.h"
27 #include "tensorflow/lite/kernels/internal/reference/integer_ops/pooling.h"
28 #include "tensorflow/lite/kernels/internal/test_util.h"
29
30 namespace tflite {
31 namespace {
32
33 // Runs the reference and optimized AveragePool functions and asserts the values
34 // are the same.
RunOneAveragePoolTest(const PoolParams & params,const RuntimeShape & input_shape,const int8 * input_data,const RuntimeShape & output_shape)35 void RunOneAveragePoolTest(const PoolParams& params,
36 const RuntimeShape& input_shape,
37 const int8* input_data,
38 const RuntimeShape& output_shape) {
39 const int buffer_size = output_shape.FlatSize();
40 std::vector<int8> optimized_averagePool_output(buffer_size);
41 std::vector<int8> reference_averagePool_output(buffer_size);
42
43 reference_integer_ops::AveragePool(params, input_shape, input_data,
44 output_shape,
45 reference_averagePool_output.data());
46 optimized_integer_ops::AveragePool(params, input_shape, input_data,
47 output_shape,
48 optimized_averagePool_output.data());
49
50 for (int i = 0; i < buffer_size; i++) {
51 EXPECT_TRUE(reference_averagePool_output[i] ==
52 optimized_averagePool_output[i]);
53 }
54 }
55
56 // Creates random input shape (batch, height, width, depth), then computes
57 // output shape based on value of `padding_same`:
58 // `padding_same` == true, calculate output with padding == "SAME"
59 // `padding_same` == false, calculate output with padding == "VALID"
60 // With input/output shapes computed, fills the input data and calls the
61 // test function.
CreateDataAndRunAveragePool(bool padding_same)62 void CreateDataAndRunAveragePool(bool padding_same) {
63 const int batch = UniformRandomInt(1, 2);
64 const int input_depth = UniformRandomInt(1, 700);
65 const int output_depth = input_depth;
66 const int input_width_offset = UniformRandomInt(1, 30);
67 const int input_height_offset = UniformRandomInt(1, 30);
68 const int stride_width = UniformRandomInt(1, 10);
69 const int stride_height = UniformRandomInt(1, 10);
70 const int filter_width = UniformRandomInt(1, 10);
71 const int filter_height = UniformRandomInt(1, 10);
72 const int input_width = input_width_offset + filter_width;
73 const int input_height = input_height_offset + filter_height;
74 const int output_width =
75 padding_same ? (input_width + stride_width - 1) / stride_width
76 : (input_width - filter_width + stride_width) / stride_width;
77 const int output_height =
78 padding_same
79 ? (input_height + stride_height - 1) / stride_height
80 : (input_height - filter_height + stride_height) / stride_height;
81
82 auto input_shape =
83 RuntimeShape({batch, input_height, input_width, input_depth});
84 auto output_shape =
85 RuntimeShape({batch, output_height, output_width, output_depth});
86 const int buffer_size = input_shape.FlatSize();
87 std::vector<int8> input_data(buffer_size);
88 FillRandom(&input_data);
89
90 PoolParams params;
91 params.stride_height = stride_height;
92 params.stride_width = stride_width;
93 params.filter_height = filter_height;
94 params.filter_width = filter_width;
95 params.quantized_activation_min =
96 static_cast<int8_t>(std::numeric_limits<int8_t>::lowest());
97 params.quantized_activation_max =
98 static_cast<int8_t>(std::numeric_limits<int8_t>::max());
99 auto compute_padding = [](int stride, int in_size, int filter_size,
100 int out_size) {
101 int padding = ((out_size - 1) * stride + filter_size - in_size) / 2;
102 return padding > 0 ? padding : 0;
103 };
104 params.padding_values.width =
105 compute_padding(stride_width, input_width, filter_width, output_width);
106 params.padding_values.height = compute_padding(stride_height, input_height,
107 filter_height, output_height);
108 RunOneAveragePoolTest(params, input_shape, input_data.data(), output_shape);
109 }
110
TEST(TestAveragePool,SymmetricQuantAveragePool)111 TEST(TestAveragePool, SymmetricQuantAveragePool) {
112 const int kTestsToRun = 10;
113 for (int i = 0; i < kTestsToRun; i++) {
114 CreateDataAndRunAveragePool(/*padding_same=*/true);
115 CreateDataAndRunAveragePool(/*padding_same=*/false);
116 }
117 }
118
119 // Creates random input shape (batch, height, width, depth), then computes
120 // output shape based on value of `padding_same`:
121 // `padding_same` == true, calculate output with padding == "SAME"
122 // `padding_same` == false, calculate output with padding == "VALID"
123 // With input/output shapes computed, fills the input data and calls the
124 // test function.
CreateExtremalDataAndRunAveragePool(bool padding_same)125 void CreateExtremalDataAndRunAveragePool(bool padding_same) {
126 const int batch = UniformRandomInt(1, 2);
127 const int input_depth = UniformRandomInt(1, 700);
128 const int output_depth = input_depth;
129 const int input_width_offset = UniformRandomInt(1, 30);
130 const int input_height_offset = UniformRandomInt(1, 30);
131 const int stride_width = UniformRandomInt(1, 128);
132 const int stride_height = UniformRandomInt(1, 128);
133 const int filter_width = UniformRandomInt(1, 28);
134 const int filter_height = UniformRandomInt(1, 28);
135 if (filter_width * filter_height > 64) {
136 std::cout << "should test 32 version" << std::endl;
137 }
138 const int input_width = input_width_offset + filter_width;
139 const int input_height = input_height_offset + filter_height;
140 const int output_width =
141 padding_same ? (input_width + stride_width - 1) / stride_width
142 : (input_width - filter_width + stride_width) / stride_width;
143 const int output_height =
144 padding_same
145 ? (input_height + stride_height - 1) / stride_height
146 : (input_height - filter_height + stride_height) / stride_height;
147
148 auto input_shape =
149 RuntimeShape({batch, input_height, input_width, input_depth});
150 auto output_shape =
151 RuntimeShape({batch, output_height, output_width, output_depth});
152
153 PoolParams params;
154 params.stride_height = stride_height;
155 params.stride_width = stride_width;
156 params.filter_height = filter_height;
157 params.filter_width = filter_width;
158 params.quantized_activation_min =
159 static_cast<int8_t>(std::numeric_limits<int8_t>::lowest());
160 params.quantized_activation_max =
161 static_cast<int8_t>(std::numeric_limits<int8_t>::max());
162 auto compute_padding = [](int stride, int in_size, int filter_size,
163 int out_size) {
164 int padding = ((out_size - 1) * stride + filter_size - in_size) / 2;
165 return padding > 0 ? padding : 0;
166 };
167 params.padding_values.width =
168 compute_padding(stride_width, input_width, filter_width, output_width);
169 params.padding_values.height = compute_padding(stride_height, input_height,
170 filter_height, output_height);
171
172 const int buffer_size = input_shape.FlatSize();
173 std::vector<int8> input_data(buffer_size);
174
175 // Test small values
176 int8 min = std::numeric_limits<int8>::min();
177 int8 max = std::numeric_limits<int8>::min() + 10;
178 FillRandom(&input_data, min, max);
179 RunOneAveragePoolTest(params, input_shape, input_data.data(), output_shape);
180
181 // Test large values
182 min = std::numeric_limits<int8>::max() - 10;
183 max = std::numeric_limits<int8>::max();
184 FillRandom(&input_data, min, max);
185 RunOneAveragePoolTest(params, input_shape, input_data.data(), output_shape);
186 }
187
TEST(TestAveragePool,SymmetricQuantExtremalAveragePool)188 TEST(TestAveragePool, SymmetricQuantExtremalAveragePool) {
189 CreateExtremalDataAndRunAveragePool(/*padding_same=*/true);
190 CreateExtremalDataAndRunAveragePool(/*padding_same=*/false);
191 }
192
193 } // namespace
194 } // namespace tflite
195