1 /* Copyright 2015 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 <random>
17
18 #include "tensorflow/core/framework/fake_input.h"
19 #include "tensorflow/core/framework/node_def_builder.h"
20 #include "tensorflow/core/framework/tensor.h"
21 #include "tensorflow/core/kernels/ops_testutil.h"
22 #include "tensorflow/core/kernels/ops_util.h"
23 #include "tensorflow/core/lib/core/status_test_util.h"
24 #include "tensorflow/core/platform/test.h"
25 #include "tensorflow/core/platform/test_benchmark.h"
26
27 namespace tensorflow {
28
29 class QuantizedOpTest : public OpsTestBase {
30 protected:
31 };
32
33 struct ParameterizedQuantizeOpTest : public OpsTestBase,
34 public ::testing::WithParamInterface<int> {
35 };
36
TEST_F(QuantizedOpTest,QuantizeV2)37 TEST_F(QuantizedOpTest, QuantizeV2) {
38 TF_ASSERT_OK(NodeDefBuilder("quantize_op", "QuantizeV2")
39 .Input(FakeInput(DT_FLOAT))
40 .Input(FakeInput(DT_FLOAT))
41 .Input(FakeInput(DT_FLOAT))
42 .Attr("T", DataTypeToEnum<quint8>::v())
43 .Attr("mode", "MIN_FIRST")
44 .Finalize(node_def()));
45 TF_ASSERT_OK(InitOp());
46 AddInputFromArray<float>(TensorShape({7}),
47 {0.0, 1.0, 1.25, 1.75, 127.0, 255.0, 500.0});
48 // min_range = 0
49 AddInputFromArray<float>(TensorShape({1}), {0});
50 // max_range = 255
51 AddInputFromArray<float>(TensorShape({1}), {255.0f});
52 TF_ASSERT_OK(RunOpKernel());
53 Tensor expected(allocator(), DT_QUINT8, TensorShape({7}));
54 // Input element 0.0 should map to 0.
55 // Input element 500.0 is quantized to 255 because max_range = 255.
56 test::FillValues<quint8>(&expected, {0, 1, 1, 2, 127, 255, 255});
57 test::ExpectTensorEqual<quint8>(expected, *GetOutput(0));
58 }
59
60 // Creates a tensor with the specified dims, using values chosen from data,
61 // multiplied by (1 + index) along the axis dimension.
62 template <typename T>
ScalePerSliceAlongAxis(std::vector<int64> dims,int axis,const std::vector<T> & data)63 std::vector<T> ScalePerSliceAlongAxis(std::vector<int64> dims, int axis,
64 const std::vector<T>& data) {
65 uint32 seed = 123;
66 std::minstd_rand rng(seed);
67 int64_t out_size = 1;
68 for (int dim : dims) {
69 out_size *= dim;
70 }
71 int minor_size = 1;
72 for (int i = axis + 1; i < dims.size(); ++i) {
73 minor_size *= dims[i];
74 }
75 std::vector<T> out(out_size);
76 int num_slices = (axis == -1) ? 1 : dims[axis];
77 for (int out_idx = 0; out_idx < out_size; ++out_idx) {
78 int in_idx = rng() % data.size();
79 T multiplier = ((out_idx / minor_size) % num_slices) + 1;
80 out[out_idx] = data[in_idx] * multiplier;
81 }
82 return out;
83 }
84
TEST_P(ParameterizedQuantizeOpTest,QuantizeV2Quint8Scaled)85 TEST_P(ParameterizedQuantizeOpTest, QuantizeV2Quint8Scaled) {
86 const int axis = GetParam();
87 TF_ASSERT_OK(NodeDefBuilder("quantize_op", "QuantizeV2")
88 .Input(FakeInput(DT_FLOAT))
89 .Input(FakeInput(DT_FLOAT))
90 .Input(FakeInput(DT_FLOAT))
91 .Attr("T", DataTypeToEnum<quint8>::v())
92 .Attr("mode", "SCALED")
93 .Attr("axis", axis)
94 .Finalize(node_def()));
95 TF_ASSERT_OK(InitOp());
96 const std::vector<int64> dims = {2, 3, 4, 5};
97 int num_slices = (axis == -1) ? 1 : dims[axis];
98
99 // Each channel contains the same 8 values multiplied by (channel + 1).
100 AddInputFromArray<float>(
101 TensorShape(dims),
102 ScalePerSliceAlongAxis<float>(
103 dims, axis, {-255.0, 0.0, 1.0, 1.25, 1.75, 64.0, 127.0, 500.0}));
104 std::vector<float> min_ranges(num_slices), max_ranges(num_slices);
105 for (int slice_idx = 0; slice_idx < num_slices; ++slice_idx) {
106 min_ranges[slice_idx] = (slice_idx + 1) * -255.0;
107 max_ranges[slice_idx] = (slice_idx + 1) * 127.0;
108 }
109 AddInputFromArray<float>(TensorShape({num_slices}), min_ranges);
110 AddInputFromArray<float>(TensorShape({num_slices}), max_ranges);
111 TF_ASSERT_OK(RunOpKernel());
112 // Input values < 0 should map to 0 even though min_range = -255, because
113 // we are performing quantization by scaling to quint8.
114 // Input value 0.0 should map to 0.
115 // The scale factor chosen should be 255 / 127 = 2.00787
116 // Output values are clipped to 255.
117
118 Tensor expected(allocator(), DT_QUINT8, TensorShape(dims));
119 test::FillValues<quint8>(
120 &expected,
121 ScalePerSliceAlongAxis<quint8>(dims, -1, {0, 0, 2, 3, 4, 129, 255, 255}));
122
123 auto output_min = *GetOutput(1);
124 auto output_max = *GetOutput(2);
125
126 for (int slice_idx = 0; slice_idx < num_slices; ++slice_idx) {
127 EXPECT_EQ(output_min.flat<float>()(slice_idx), 0);
128 EXPECT_EQ(output_max.flat<float>()(slice_idx), 127.0 * (slice_idx + 1));
129 }
130
131 auto output = *GetOutput(0);
132 test::ExpectTensorEqual<quint8>(expected, *GetOutput(0));
133 }
134
TEST_F(QuantizedOpTest,QuantizeV2Quint8ScaledSmallInputRange)135 TEST_F(QuantizedOpTest, QuantizeV2Quint8ScaledSmallInputRange) {
136 TF_ASSERT_OK(NodeDefBuilder("quantize_op", "QuantizeV2")
137 .Input(FakeInput(DT_FLOAT))
138 .Input(FakeInput(DT_FLOAT))
139 .Input(FakeInput(DT_FLOAT))
140 .Attr("T", DataTypeToEnum<quint8>::v())
141 .Attr("mode", "SCALED")
142 .Finalize(node_def()));
143 TF_ASSERT_OK(InitOp());
144 AddInputFromArray<float>(TensorShape({3}), {-1.0, 0.0, 2.0});
145 AddInputFromArray<float>(TensorShape({1}), {-1.0f});
146 AddInputFromArray<float>(TensorShape({1}), {2.0f});
147 TF_ASSERT_OK(RunOpKernel());
148 Tensor expected(allocator(), DT_QUINT8, TensorShape({3}));
149 // Input element -1.0 should map to 0 even though min_range = -1, because
150 // we are performing quantization by scaling to quint8.
151 // Input element 0.0 should map to 0.
152 // Input element 2.0 should map to max quint8 value 255.
153 test::FillValues<quint8>(&expected, {0, 0, 255});
154 test::ExpectTensorEqual<quint8>(expected, *GetOutput(0));
155
156 Tensor expected_output_min(allocator(), DT_FLOAT, TensorShape({}));
157 test::FillValues<float>(&expected_output_min, {0.0});
158 test::ExpectTensorEqual<float>(expected_output_min, *GetOutput(1));
159
160 Tensor expected_output_max(allocator(), DT_FLOAT, TensorShape({}));
161 test::FillValues<float>(&expected_output_max, {2.0});
162 test::ExpectTensorEqual<float>(expected_output_max, *GetOutput(2));
163 }
164
TEST_P(ParameterizedQuantizeOpTest,QuantizeV2Qint8Scaled)165 TEST_P(ParameterizedQuantizeOpTest, QuantizeV2Qint8Scaled) {
166 const int axis = GetParam();
167 TF_ASSERT_OK(NodeDefBuilder("quantize_op", "QuantizeV2")
168 .Input(FakeInput(DT_FLOAT))
169 .Input(FakeInput(DT_FLOAT))
170 .Input(FakeInput(DT_FLOAT))
171 .Attr("T", DataTypeToEnum<qint8>::v())
172 .Attr("mode", "SCALED")
173 .Attr("narrow_range", false)
174 .Attr("axis", axis)
175 .Finalize(node_def()));
176 TF_ASSERT_OK(InitOp());
177 const std::vector<int64> dims = {2, 3, 4, 5};
178 int num_slices = (axis == -1) ? 1 : dims[axis];
179
180 // Each channel contains the same 7 values multiplied by (channel + 1).
181 AddInputFromArray<float>(
182 TensorShape(dims),
183 ScalePerSliceAlongAxis<float>(
184 dims, axis, {-128.0, 0.0, 1.0, 1.25, 1.75, 64.0, 127.0}));
185 std::vector<float> min_ranges(num_slices), max_ranges(num_slices);
186 for (int slice_idx = 0; slice_idx < num_slices; ++slice_idx) {
187 min_ranges[slice_idx] = (slice_idx + 1) * -128.0;
188 max_ranges[slice_idx] = (slice_idx + 1) * 100.0;
189 }
190 AddInputFromArray<float>(TensorShape({num_slices}), min_ranges);
191 AddInputFromArray<float>(TensorShape({num_slices}), max_ranges);
192 TF_ASSERT_OK(RunOpKernel());
193
194 // Input element 0.0 should map to 0.
195 // Input element 127.0 maps to 127 instead of 100.
196 // (i.e. the max_ranges[] values should be ignored because their magnitude is
197 // less than the min_ranges[] values).
198 Tensor expected(allocator(), DT_QINT8, TensorShape(dims));
199 test::FillValues<qint8>(
200 &expected,
201 ScalePerSliceAlongAxis<qint8>(dims, -1, {-128, 0, 1, 1, 2, 64, 127}));
202
203 auto output_min = *GetOutput(1);
204 auto output_max = *GetOutput(2);
205
206 for (int slice_idx = 0; slice_idx < num_slices; ++slice_idx) {
207 EXPECT_EQ(output_min.flat<float>()(slice_idx), -128.0 * (slice_idx + 1));
208 EXPECT_EQ(output_max.flat<float>()(slice_idx), 127.0 * (slice_idx + 1));
209 }
210
211 auto output = *GetOutput(0);
212 test::ExpectTensorEqual<qint8>(expected, *GetOutput(0));
213 }
214
TEST_P(ParameterizedQuantizeOpTest,QuantizeV2Qint8ScaledNarrowRange)215 TEST_P(ParameterizedQuantizeOpTest, QuantizeV2Qint8ScaledNarrowRange) {
216 const int axis = GetParam();
217 TF_ASSERT_OK(NodeDefBuilder("quantize_op", "QuantizeV2")
218 .Input(FakeInput(DT_FLOAT))
219 .Input(FakeInput(DT_FLOAT))
220 .Input(FakeInput(DT_FLOAT))
221 .Attr("T", DataTypeToEnum<qint8>::v())
222 .Attr("mode", "SCALED")
223 .Attr("narrow_range", true)
224 .Attr("axis", axis)
225 .Finalize(node_def()));
226 TF_ASSERT_OK(InitOp());
227 const std::vector<int64> dims = {2, 3, 4, 5};
228 int num_slices = (axis == -1) ? 1 : dims[axis];
229
230 // Each channel contains the same 7 values multiplied by (channel + 1).
231 AddInputFromArray<float>(
232 TensorShape(dims),
233 ScalePerSliceAlongAxis<float>(
234 dims, axis, {-128.0, 0.0, 1.0, 1.25, 1.75, 64.0, 127.0}));
235 std::vector<float> min_ranges(num_slices), max_ranges(num_slices);
236 for (int slice_idx = 0; slice_idx < num_slices; ++slice_idx) {
237 min_ranges[slice_idx] = (slice_idx + 1) * -128.0;
238 max_ranges[slice_idx] = (slice_idx + 1) * 100.0;
239 }
240 AddInputFromArray<float>(TensorShape({num_slices}), min_ranges);
241 AddInputFromArray<float>(TensorShape({num_slices}), max_ranges);
242 TF_ASSERT_OK(RunOpKernel());
243
244 // Input element 0.0 should map to 0.
245 // Input element 127.0 maps to 127 instead of 100.
246 // (i.e. the max_ranges[] values should be ignored because their magnitude is
247 // less than the min_ranges[] values).
248 Tensor expected(allocator(), DT_QINT8, TensorShape(dims));
249 test::FillValues<qint8>(
250 &expected,
251 ScalePerSliceAlongAxis<qint8>(dims, -1, {-127, 0, 1, 1, 2, 64, 126}));
252
253 auto output_min = *GetOutput(1);
254 auto output_max = *GetOutput(2);
255
256 for (int slice_idx = 0; slice_idx < num_slices; ++slice_idx) {
257 EXPECT_EQ(output_min.flat<float>()(slice_idx), -128.0 * (slice_idx + 1));
258 EXPECT_EQ(output_max.flat<float>()(slice_idx), 128.0 * (slice_idx + 1));
259 }
260
261 auto output = *GetOutput(0);
262 test::ExpectTensorEqual<qint8>(expected, *GetOutput(0));
263 }
264
265 // Instantiate parameterized tests for axis = -1, 1, 3.
266 INSTANTIATE_TEST_SUITE_P(All, ParameterizedQuantizeOpTest,
267 ::testing::Values(-1, 1, 3));
268
TEST_F(QuantizedOpTest,QuantizeV2Qint8ScaledSmallInputRange)269 TEST_F(QuantizedOpTest, QuantizeV2Qint8ScaledSmallInputRange) {
270 TF_ASSERT_OK(NodeDefBuilder("quantize_op", "QuantizeV2")
271 .Input(FakeInput(DT_FLOAT))
272 .Input(FakeInput(DT_FLOAT))
273 .Input(FakeInput(DT_FLOAT))
274 .Attr("T", DataTypeToEnum<qint8>::v())
275 .Attr("mode", "SCALED")
276 .Finalize(node_def()));
277 TF_ASSERT_OK(InitOp());
278 AddInputFromArray<float>(TensorShape({3}), {-0.064, 0.0, 0.127});
279 AddInputFromArray<float>(TensorShape({1}), {-0.064f});
280 AddInputFromArray<float>(TensorShape({1}), {0.127f});
281 TF_ASSERT_OK(RunOpKernel());
282 Tensor expected(allocator(), DT_QINT8, TensorShape({3}));
283 // Input element 0.0 should map to 0.
284 // Input element 2.0 should map to 127, max value of qint8.
285 test::FillValues<qint8>(&expected, {-64, 0, 127});
286 test::ExpectTensorEqual<qint8>(expected, *GetOutput(0));
287
288 Tensor expected_output_min(allocator(), DT_FLOAT, TensorShape({}));
289 test::FillValues<float>(&expected_output_min, {-0.128});
290 test::ExpectTensorEqual<float>(expected_output_min, *GetOutput(1));
291
292 Tensor expected_output_max(allocator(), DT_FLOAT, TensorShape({}));
293 test::FillValues<float>(&expected_output_max, {0.127});
294 test::ExpectTensorEqual<float>(expected_output_max, *GetOutput(2));
295 }
296
TEST_F(QuantizedOpTest,QuantizeV2Qint8ScaledRoundToEven)297 TEST_F(QuantizedOpTest, QuantizeV2Qint8ScaledRoundToEven) {
298 TF_ASSERT_OK(NodeDefBuilder("quantize_op", "QuantizeV2")
299 .Input(FakeInput(DT_FLOAT))
300 .Input(FakeInput(DT_FLOAT))
301 .Input(FakeInput(DT_FLOAT))
302 .Attr("T", DataTypeToEnum<qint8>::v())
303 .Attr("mode", "SCALED")
304 .Attr("round_mode", "HALF_TO_EVEN")
305 .Finalize(node_def()));
306 TF_ASSERT_OK(InitOp());
307 AddInputFromArray<float>(TensorShape({7}),
308 {-126.5, 0.0, 1.0, 2.5, 3.5, 64.0, 127.0});
309 AddInputFromArray<float>(TensorShape({1}), {-128.0f});
310 AddInputFromArray<float>(TensorShape({1}), {-128.0f});
311 TF_ASSERT_OK(RunOpKernel());
312 Tensor expected(allocator(), DT_QINT8, TensorShape({7}));
313 // Input element 0.0 should map to 0.
314 // Input element 127.0 maps to 127.
315 test::FillValues<qint8>(&expected, {-126, 0, 1, 2, 4, 64, 127});
316 test::ExpectTensorEqual<qint8>(expected, *GetOutput(0));
317
318 Tensor expected_output_min(allocator(), DT_FLOAT, TensorShape({}));
319 test::FillValues<float>(&expected_output_min, {-128.0});
320 test::ExpectTensorEqual<float>(expected_output_min, *GetOutput(1));
321
322 Tensor expected_output_max(allocator(), DT_FLOAT, TensorShape({}));
323 test::FillValues<float>(&expected_output_max, {127.0});
324 test::ExpectTensorEqual<float>(expected_output_max, *GetOutput(2));
325 }
326
TEST_F(QuantizedOpTest,QuantizeV2Qint8ScaledRoundAwayFromZero)327 TEST_F(QuantizedOpTest, QuantizeV2Qint8ScaledRoundAwayFromZero) {
328 TF_ASSERT_OK(NodeDefBuilder("quantize_op", "QuantizeV2")
329 .Input(FakeInput(DT_FLOAT))
330 .Input(FakeInput(DT_FLOAT))
331 .Input(FakeInput(DT_FLOAT))
332 .Attr("T", DataTypeToEnum<qint8>::v())
333 .Attr("mode", "SCALED")
334 .Attr("round_mode", "HALF_AWAY_FROM_ZERO")
335 .Finalize(node_def()));
336 TF_ASSERT_OK(InitOp());
337 AddInputFromArray<float>(TensorShape({7}),
338 {-126.5, 0.0, 1.0, 2.5, 3.5, 64.0, 127.0});
339 AddInputFromArray<float>(TensorShape({1}), {-128.0f});
340 AddInputFromArray<float>(TensorShape({1}), {-128.0f});
341 TF_ASSERT_OK(RunOpKernel());
342 Tensor expected(allocator(), DT_QINT8, TensorShape({7}));
343 // Input element 0.0 should map to 0.
344 // Input element 127.0 maps to 127.
345 test::FillValues<qint8>(&expected, {-127, 0, 1, 3, 4, 64, 127});
346 test::ExpectTensorEqual<qint8>(expected, *GetOutput(0));
347
348 Tensor expected_output_min(allocator(), DT_FLOAT, TensorShape({}));
349 test::FillValues<float>(&expected_output_min, {-128.0});
350 test::ExpectTensorEqual<float>(expected_output_min, *GetOutput(1));
351
352 Tensor expected_output_max(allocator(), DT_FLOAT, TensorShape({}));
353 test::FillValues<float>(&expected_output_max, {127.0});
354 test::ExpectTensorEqual<float>(expected_output_max, *GetOutput(2));
355 }
356
TEST_F(QuantizedOpTest,QuantizeV2_32Bit)357 TEST_F(QuantizedOpTest, QuantizeV2_32Bit) {
358 TF_ASSERT_OK(NodeDefBuilder("quantize_op", "QuantizeV2")
359 .Input(FakeInput(DT_FLOAT))
360 .Input(FakeInput(DT_FLOAT))
361 .Input(FakeInput(DT_FLOAT))
362 .Attr("T", DataTypeToEnum<qint32>::v())
363 .Attr("mode", "MIN_FIRST")
364 .Finalize(node_def()));
365 TF_ASSERT_OK(InitOp());
366 const int element_count = 8;
367 AddInputFromArray<float>(
368 TensorShape({element_count}),
369 {-500.0f, 0.0f, 1.0f, 1.25f, 1.75f, 127.0f, 255.0f, 500.0f});
370 AddInputFromArray<float>(TensorShape({1}), {-256.0f});
371 AddInputFromArray<float>(TensorShape({1}), {256.0f});
372 TF_ASSERT_OK(RunOpKernel());
373 Tensor expected(allocator(), DT_QINT32, TensorShape({element_count}));
374 test::FillValues<qint32>(&expected,
375 {
376 std::numeric_limits<int32>::min(),
377 0,
378 static_cast<int32>(1.0f * (1 << 23)),
379 static_cast<int32>(1.25f * (1 << 23)),
380 static_cast<int32>(1.75f * (1 << 23)),
381 static_cast<int32>(127.0f * (1 << 23)),
382 static_cast<int32>(255.0f * (1 << 23)),
383 std::numeric_limits<int32>::max(),
384 });
385 // We expect there will be some fuzziness in the lower bits, since this is
386 // converting from float.
387 const int64_t epsilon = 1 << 8;
388 const qint32* output_data = GetOutput(0)->flat<qint32>().data();
389 const qint32* expected_data = expected.flat<qint32>().data();
390 for (int i = 0; i < element_count; ++i) {
391 const int64_t delta = output_data[i] - expected_data[i];
392 EXPECT_GT(epsilon, std::abs(delta))
393 << "output_data[" << i << "]=" << output_data[i] << ", expected_data["
394 << i << "]=" << expected_data[i] << ", delta=" << delta;
395 }
396 }
397
TEST_F(QuantizedOpTest,QuantizeV2Ports)398 TEST_F(QuantizedOpTest, QuantizeV2Ports) {
399 TF_ASSERT_OK(NodeDefBuilder("quantize_op", "QuantizeV2")
400 .Input(FakeInput(DT_FLOAT))
401 .Input(FakeInput(DT_FLOAT))
402 .Input(FakeInput(DT_FLOAT))
403 .Attr("T", DataTypeToEnum<quint8>::v())
404 .Attr("mode", "MIN_FIRST")
405 .Finalize(node_def()));
406 TF_ASSERT_OK(InitOp());
407 AddInputFromArray<float>(TensorShape({6}),
408 {1.0, 1.25, 1.75, 127.0, 255.0, 500.0});
409 AddInputFromArray<float>(TensorShape({1}), {0});
410 AddInputFromArray<float>(TensorShape({1}), {255.0f});
411 TF_ASSERT_OK(RunOpKernel());
412 Tensor expected(allocator(), DT_QUINT8, TensorShape({6}));
413 test::FillValues<quint8>(&expected, {1, 1, 2, 127, 255, 255});
414 test::ExpectTensorEqual<quint8>(expected, *GetOutput(0));
415 const float output_min = GetOutput(1)->flat<float>()(0);
416 const float output_max = GetOutput(2)->flat<float>()(0);
417 EXPECT_NEAR(0.0f, output_min, 1e-5f);
418 EXPECT_NEAR(255.0f, output_max, 1e-5f);
419 }
420
TEST_F(QuantizedOpTest,QuantizeV2EqualRange)421 TEST_F(QuantizedOpTest, QuantizeV2EqualRange) {
422 TF_ASSERT_OK(NodeDefBuilder("quantize_op", "QuantizeV2")
423 .Input(FakeInput(DT_FLOAT))
424 .Input(FakeInput(DT_FLOAT))
425 .Input(FakeInput(DT_FLOAT))
426 .Attr("T", DataTypeToEnum<quint8>::v())
427 .Attr("mode", "MIN_FIRST")
428 .Finalize(node_def()));
429 TF_ASSERT_OK(InitOp());
430 AddInputFromArray<float>(TensorShape({6}), {0.0, 0.0, 0.0, 0.0, 0.0, 0.0});
431 AddInputFromArray<float>(TensorShape({1}), {0.0f});
432 AddInputFromArray<float>(TensorShape({1}), {0.0f});
433 TF_ASSERT_OK(RunOpKernel());
434 Tensor expected(allocator(), DT_QUINT8, TensorShape({6}));
435 test::FillValues<quint8>(&expected, {0, 0, 0, 0, 0, 0});
436 test::ExpectTensorEqual<quint8>(expected, *GetOutput(0));
437 const float output_min = GetOutput(1)->flat<float>()(0);
438 const float output_max = GetOutput(2)->flat<float>()(0);
439 EXPECT_NEAR(0.0f, output_min, 1e-5f);
440 EXPECT_LT(0.0f, output_max);
441 }
442
TEST_F(QuantizedOpTest,QuantizeV2MovesMinToIncludeZero)443 TEST_F(QuantizedOpTest, QuantizeV2MovesMinToIncludeZero) {
444 TF_ASSERT_OK(NodeDefBuilder("quantize_op", "QuantizeV2")
445 .Input(FakeInput(DT_FLOAT))
446 .Input(FakeInput(DT_FLOAT))
447 .Input(FakeInput(DT_FLOAT))
448 .Attr("T", DataTypeToEnum<quint8>::v())
449 .Attr("mode", "MIN_FIRST")
450 .Finalize(node_def()));
451 TF_ASSERT_OK(InitOp());
452 AddInputFromArray<float>(TensorShape({3}), {0.1, 0.2, 0.3});
453 AddInputFromArray<float>(TensorShape({1}), {0.1});
454 AddInputFromArray<float>(TensorShape({1}), {0.3});
455 TF_ASSERT_OK(RunOpKernel());
456 Tensor expected(allocator(), DT_QUINT8, TensorShape({3}));
457 test::FillValues<quint8>(&expected, {85, 170, 255});
458 test::ExpectTensorEqual<quint8>(expected, *GetOutput(0));
459 const float output_min = GetOutput(1)->flat<float>()(0);
460 const float output_max = GetOutput(2)->flat<float>()(0);
461 EXPECT_NEAR(0.0f, output_min, 1e-5f);
462 EXPECT_NEAR(0.3f, output_max, 1e-5f);
463 }
464
TEST_F(QuantizedOpTest,QuantizeV2MovesMaxToIncludeZero)465 TEST_F(QuantizedOpTest, QuantizeV2MovesMaxToIncludeZero) {
466 TF_ASSERT_OK(NodeDefBuilder("quantize_op", "QuantizeV2")
467 .Input(FakeInput(DT_FLOAT))
468 .Input(FakeInput(DT_FLOAT))
469 .Input(FakeInput(DT_FLOAT))
470 .Attr("T", DataTypeToEnum<quint8>::v())
471 .Attr("mode", "MIN_FIRST")
472 .Finalize(node_def()));
473 TF_ASSERT_OK(InitOp());
474 AddInputFromArray<float>(TensorShape({3}), {-0.1, -0.2, -0.3});
475 AddInputFromArray<float>(TensorShape({1}), {-0.3});
476 AddInputFromArray<float>(TensorShape({1}), {-0.1});
477 TF_ASSERT_OK(RunOpKernel());
478 Tensor expected(allocator(), DT_QUINT8, TensorShape({3}));
479 test::FillValues<quint8>(&expected, {170, 85, 0});
480 test::ExpectTensorEqual<quint8>(expected, *GetOutput(0));
481 const float output_min = GetOutput(1)->flat<float>()(0);
482 const float output_max = GetOutput(2)->flat<float>()(0);
483 EXPECT_NEAR(-0.3f, output_min, 1e-5f);
484 EXPECT_NEAR(0.0f, output_max, 1e-5f);
485 }
486
TEST_F(QuantizedOpTest,Dequantize)487 TEST_F(QuantizedOpTest, Dequantize) {
488 TF_ASSERT_OK(NodeDefBuilder("dequantize_op", "Dequantize")
489 .Input(FakeInput(DT_QUINT8))
490 .Input(FakeInput(DT_FLOAT))
491 .Input(FakeInput(DT_FLOAT))
492 .Attr("T", DataTypeToEnum<quint8>::v())
493 .Attr("mode", "MIN_FIRST")
494 .Finalize(node_def()));
495 TF_ASSERT_OK(InitOp());
496 AddInputFromArray<quint8>(TensorShape({6}), {1, 2, 4, 8, 16, 255});
497 AddInputFromArray<float>(TensorShape({1}), {0});
498 AddInputFromArray<float>(TensorShape({1}), {255.0f});
499 TF_ASSERT_OK(RunOpKernel());
500 Tensor expected(allocator(), DT_FLOAT, TensorShape({6}));
501 test::FillValues<float>(&expected, {1.0, 2.0, 4.0, 8.0, 16.0, 255.0});
502 test::ExpectTensorNear<float>(expected, *GetOutput(0), 0.5);
503 }
504
TEST_F(QuantizedOpTest,QuantizeV2DisableEnsureMinimumRange)505 TEST_F(QuantizedOpTest, QuantizeV2DisableEnsureMinimumRange) {
506 TF_ASSERT_OK(NodeDefBuilder("quantize_op", "QuantizeV2")
507 .Input(FakeInput(DT_FLOAT))
508 .Input(FakeInput(DT_FLOAT))
509 .Input(FakeInput(DT_FLOAT))
510 .Attr("T", DataTypeToEnum<qint8>::v())
511 .Attr("mode", "MIN_FIRST")
512 .Attr("ensure_minimum_range", 0.0f)
513 .Finalize(node_def()));
514 TF_ASSERT_OK(InitOp());
515 AddInputFromArray<float>(TensorShape({3}), {-0.000001, 0.0, 0.000042});
516 AddInputFromArray<float>(TensorShape({1}), {-0.000128});
517 AddInputFromArray<float>(TensorShape({1}), {0.000127});
518 TF_ASSERT_OK(RunOpKernel());
519 Tensor expected(allocator(), DT_QINT8, TensorShape({3}));
520 test::FillValues<qint8>(&expected, {-1, 0, 42});
521 for (int i = 0; i < 3; ++i) {
522 LOG(INFO) << GetOutput(0)->flat<qint8>()(i);
523 }
524 test::ExpectTensorEqual<qint8>(expected, *GetOutput(0));
525 const float output_min = GetOutput(1)->flat<float>()(0);
526 const float output_max = GetOutput(2)->flat<float>()(0);
527 LOG(INFO) << "output_min = " << output_min;
528 LOG(INFO) << "output_max = " << output_max;
529 EXPECT_NEAR(-0.000128f, output_min, 1e-7f);
530 EXPECT_NEAR(0.000127, output_max, 1e-7f);
531 }
532
533 } // end namespace tensorflow
534