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 "tensorflow/core/framework/fake_input.h" 17 #include "tensorflow/core/framework/node_def_builder.h" 18 #include "tensorflow/core/framework/tensor.h" 19 #include "tensorflow/core/kernels/ops_testutil.h" 20 #include "tensorflow/core/kernels/ops_util.h" 21 #include "tensorflow/core/lib/core/status_test_util.h" 22 #include "tensorflow/core/platform/test.h" 23 #include "tensorflow/core/platform/test_benchmark.h" 24 25 namespace tensorflow { 26 27 class QuantizedOpTest : public OpsTestBase { 28 protected: 29 }; 30 31 TEST_F(QuantizedOpTest, QuantizeV2) { 32 TF_ASSERT_OK(NodeDefBuilder("quantize_op", "QuantizeV2") 33 .Input(FakeInput(DT_FLOAT)) 34 .Input(FakeInput(DT_FLOAT)) 35 .Input(FakeInput(DT_FLOAT)) 36 .Attr("T", DataTypeToEnum<quint8>::v()) 37 .Attr("mode", "MIN_FIRST") 38 .Finalize(node_def())); 39 TF_ASSERT_OK(InitOp()); 40 AddInputFromArray<float>(TensorShape({7}), 41 {0.0, 1.0, 1.25, 1.75, 127.0, 255.0, 500.0}); 42 // min_range = 0 43 AddInputFromArray<float>(TensorShape({1}), {0}); 44 // max_range = 255 45 AddInputFromArray<float>(TensorShape({1}), {255.0f}); 46 TF_ASSERT_OK(RunOpKernel()); 47 Tensor expected(allocator(), DT_QUINT8, TensorShape({7})); 48 // Input element 0.0 should map to 0. 49 // Input element 500.0 is quantized to 255 because max_range = 255. 50 test::FillValues<quint8>(&expected, {0, 1, 1, 2, 127, 255, 255}); 51 test::ExpectTensorEqual<quint8>(expected, *GetOutput(0)); 52 } 53 54 TEST_F(QuantizedOpTest, QuantizeV2Quint8Scaled) { 55 TF_ASSERT_OK(NodeDefBuilder("quantize_op", "QuantizeV2") 56 .Input(FakeInput(DT_FLOAT)) 57 .Input(FakeInput(DT_FLOAT)) 58 .Input(FakeInput(DT_FLOAT)) 59 .Attr("T", DataTypeToEnum<quint8>::v()) 60 .Attr("mode", "SCALED") 61 .Finalize(node_def())); 62 TF_ASSERT_OK(InitOp()); 63 AddInputFromArray<float>(TensorShape({8}), 64 {-255.0, 0.0, 1.0, 1.25, 1.75, 127.0, 255.0, 500.0}); 65 AddInputFromArray<float>(TensorShape({1}), {-255.0f}); 66 AddInputFromArray<float>(TensorShape({1}), {127.0f}); 67 TF_ASSERT_OK(RunOpKernel()); 68 Tensor expected(allocator(), DT_QUINT8, TensorShape({8})); 69 // Input element -5.0 should map to 0 even though min_range = -255, because 70 // we are performing quantization by scaling to quint8. 71 // Input element 0.0 should map to 0. 72 // Input element 500.0 is quantized to 127 because 73 // max(abs(-255), abs(127)) = 255. 74 test::FillValues<quint8>(&expected, {0, 0, 1, 1, 2, 127, 255, 255}); 75 test::ExpectTensorEqual<quint8>(expected, *GetOutput(0)); 76 77 Tensor expected_output_min(allocator(), DT_FLOAT, TensorShape({})); 78 test::FillValues<float>(&expected_output_min, {0.0}); 79 test::ExpectTensorEqual<float>(expected_output_min, *GetOutput(1)); 80 81 Tensor expected_output_max(allocator(), DT_FLOAT, TensorShape({})); 82 test::FillValues<float>(&expected_output_max, {255.0}); 83 test::ExpectTensorEqual<float>(expected_output_max, *GetOutput(2)); 84 } 85 86 TEST_F(QuantizedOpTest, QuantizeV2Quint8ScaledSmallInputRange) { 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 .Finalize(node_def())); 94 TF_ASSERT_OK(InitOp()); 95 AddInputFromArray<float>(TensorShape({3}), {-1.0, 0.0, 2.0}); 96 AddInputFromArray<float>(TensorShape({1}), {-1.0f}); 97 AddInputFromArray<float>(TensorShape({1}), {2.0f}); 98 TF_ASSERT_OK(RunOpKernel()); 99 Tensor expected(allocator(), DT_QUINT8, TensorShape({3})); 100 // Input element -1.0 should map to 0 even though min_range = -1, because 101 // we are performing quantization by scaling to quint8. 102 // Input element 0.0 should map to 0. 103 // Input element 2.0 should map to max quint8 value 255. 104 test::FillValues<quint8>(&expected, {0, 0, 255}); 105 test::ExpectTensorEqual<quint8>(expected, *GetOutput(0)); 106 107 Tensor expected_output_min(allocator(), DT_FLOAT, TensorShape({})); 108 test::FillValues<float>(&expected_output_min, {0.0}); 109 test::ExpectTensorEqual<float>(expected_output_min, *GetOutput(1)); 110 111 Tensor expected_output_max(allocator(), DT_FLOAT, TensorShape({})); 112 test::FillValues<float>(&expected_output_max, {2.0}); 113 test::ExpectTensorEqual<float>(expected_output_max, *GetOutput(2)); 114 } 115 116 TEST_F(QuantizedOpTest, QuantizeV2Qint8Scaled) { 117 TF_ASSERT_OK(NodeDefBuilder("quantize_op", "QuantizeV2") 118 .Input(FakeInput(DT_FLOAT)) 119 .Input(FakeInput(DT_FLOAT)) 120 .Input(FakeInput(DT_FLOAT)) 121 .Attr("T", DataTypeToEnum<qint8>::v()) 122 .Attr("mode", "SCALED") 123 .Finalize(node_def())); 124 TF_ASSERT_OK(InitOp()); 125 AddInputFromArray<float>(TensorShape({7}), 126 {-127.0, 0.0, 1.0, 1.25, 1.75, 64.0, 127.0}); 127 AddInputFromArray<float>(TensorShape({1}), {-127.0f}); 128 AddInputFromArray<float>(TensorShape({1}), {100.0f}); 129 TF_ASSERT_OK(RunOpKernel()); 130 Tensor expected(allocator(), DT_QINT8, TensorShape({7})); 131 // Input element 0.0 should map to 0. 132 // Input element 127.0 maps to 127 instead of 100 because 133 // max(abs(-127), abs(100)) = 127. 134 test::FillValues<qint8>(&expected, {-127, 0, 1, 1, 2, 64, 127}); 135 test::ExpectTensorEqual<qint8>(expected, *GetOutput(0)); 136 137 Tensor expected_output_min(allocator(), DT_FLOAT, TensorShape({})); 138 test::FillValues<float>(&expected_output_min, {-127.0}); 139 test::ExpectTensorEqual<float>(expected_output_min, *GetOutput(1)); 140 141 Tensor expected_output_max(allocator(), DT_FLOAT, TensorShape({})); 142 test::FillValues<float>(&expected_output_max, {127.0}); 143 test::ExpectTensorEqual<float>(expected_output_max, *GetOutput(2)); 144 } 145 146 TEST_F(QuantizedOpTest, QuantizeV2Qint8ScaledSmallInputRange) { 147 TF_ASSERT_OK(NodeDefBuilder("quantize_op", "QuantizeV2") 148 .Input(FakeInput(DT_FLOAT)) 149 .Input(FakeInput(DT_FLOAT)) 150 .Input(FakeInput(DT_FLOAT)) 151 .Attr("T", DataTypeToEnum<qint8>::v()) 152 .Attr("mode", "SCALED") 153 .Finalize(node_def())); 154 TF_ASSERT_OK(InitOp()); 155 AddInputFromArray<float>(TensorShape({3}), {-1.0, 0.0, 2.0}); 156 AddInputFromArray<float>(TensorShape({1}), {-1.0f}); 157 AddInputFromArray<float>(TensorShape({1}), {2.0f}); 158 TF_ASSERT_OK(RunOpKernel()); 159 Tensor expected(allocator(), DT_QINT8, TensorShape({3})); 160 // Input element 0.0 should map to 0. 161 // Input element 2.0 should map to 127, max value of qint8. 162 test::FillValues<qint8>(&expected, {-64, 0, 127}); 163 test::ExpectTensorEqual<qint8>(expected, *GetOutput(0)); 164 165 Tensor expected_output_min(allocator(), DT_FLOAT, TensorShape({})); 166 test::FillValues<float>(&expected_output_min, {-2.0}); 167 test::ExpectTensorEqual<float>(expected_output_min, *GetOutput(1)); 168 169 Tensor expected_output_max(allocator(), DT_FLOAT, TensorShape({})); 170 test::FillValues<float>(&expected_output_max, {2.0}); 171 test::ExpectTensorEqual<float>(expected_output_max, *GetOutput(2)); 172 } 173 174 TEST_F(QuantizedOpTest, QuantizeV2Qint8ScaledRoundToEven) { 175 TF_ASSERT_OK(NodeDefBuilder("quantize_op", "QuantizeV2") 176 .Input(FakeInput(DT_FLOAT)) 177 .Input(FakeInput(DT_FLOAT)) 178 .Input(FakeInput(DT_FLOAT)) 179 .Attr("T", DataTypeToEnum<qint8>::v()) 180 .Attr("mode", "SCALED") 181 .Attr("round_mode", "HALF_TO_EVEN") 182 .Finalize(node_def())); 183 TF_ASSERT_OK(InitOp()); 184 AddInputFromArray<float>(TensorShape({7}), 185 {-126.5, 0.0, 1.0, 2.5, 3.5, 64.0, 127.0}); 186 AddInputFromArray<float>(TensorShape({1}), {-127.0f}); 187 AddInputFromArray<float>(TensorShape({1}), {-127.0f}); 188 TF_ASSERT_OK(RunOpKernel()); 189 Tensor expected(allocator(), DT_QINT8, TensorShape({7})); 190 // Input element 0.0 should map to 0. 191 // Input element 127.0 maps to 127. 192 test::FillValues<qint8>(&expected, {-126, 0, 1, 2, 4, 64, 127}); 193 test::ExpectTensorEqual<qint8>(expected, *GetOutput(0)); 194 195 Tensor expected_output_min(allocator(), DT_FLOAT, TensorShape({})); 196 test::FillValues<float>(&expected_output_min, {-127.0}); 197 test::ExpectTensorEqual<float>(expected_output_min, *GetOutput(1)); 198 199 Tensor expected_output_max(allocator(), DT_FLOAT, TensorShape({})); 200 test::FillValues<float>(&expected_output_max, {127.0}); 201 test::ExpectTensorEqual<float>(expected_output_max, *GetOutput(2)); 202 } 203 204 TEST_F(QuantizedOpTest, QuantizeV2Qint8ScaledRoundAwayFromZero) { 205 TF_ASSERT_OK(NodeDefBuilder("quantize_op", "QuantizeV2") 206 .Input(FakeInput(DT_FLOAT)) 207 .Input(FakeInput(DT_FLOAT)) 208 .Input(FakeInput(DT_FLOAT)) 209 .Attr("T", DataTypeToEnum<qint8>::v()) 210 .Attr("mode", "SCALED") 211 .Attr("round_mode", "HALF_AWAY_FROM_ZERO") 212 .Finalize(node_def())); 213 TF_ASSERT_OK(InitOp()); 214 AddInputFromArray<float>(TensorShape({7}), 215 {-126.5, 0.0, 1.0, 2.5, 3.5, 64.0, 127.0}); 216 AddInputFromArray<float>(TensorShape({1}), {-127.0f}); 217 AddInputFromArray<float>(TensorShape({1}), {-127.0f}); 218 TF_ASSERT_OK(RunOpKernel()); 219 Tensor expected(allocator(), DT_QINT8, TensorShape({7})); 220 // Input element 0.0 should map to 0. 221 // Input element 127.0 maps to 127. 222 test::FillValues<qint8>(&expected, {-127, 0, 1, 3, 4, 64, 127}); 223 test::ExpectTensorEqual<qint8>(expected, *GetOutput(0)); 224 225 Tensor expected_output_min(allocator(), DT_FLOAT, TensorShape({})); 226 test::FillValues<float>(&expected_output_min, {-127.0}); 227 test::ExpectTensorEqual<float>(expected_output_min, *GetOutput(1)); 228 229 Tensor expected_output_max(allocator(), DT_FLOAT, TensorShape({})); 230 test::FillValues<float>(&expected_output_max, {127.0}); 231 test::ExpectTensorEqual<float>(expected_output_max, *GetOutput(2)); 232 } 233 234 TEST_F(QuantizedOpTest, QuantizeV2_32Bit) { 235 TF_ASSERT_OK(NodeDefBuilder("quantize_op", "QuantizeV2") 236 .Input(FakeInput(DT_FLOAT)) 237 .Input(FakeInput(DT_FLOAT)) 238 .Input(FakeInput(DT_FLOAT)) 239 .Attr("T", DataTypeToEnum<qint32>::v()) 240 .Attr("mode", "MIN_FIRST") 241 .Finalize(node_def())); 242 TF_ASSERT_OK(InitOp()); 243 const int element_count = 8; 244 AddInputFromArray<float>( 245 TensorShape({element_count}), 246 {-500.0f, 0.0f, 1.0f, 1.25f, 1.75f, 127.0f, 255.0f, 500.0f}); 247 AddInputFromArray<float>(TensorShape({1}), {-256.0f}); 248 AddInputFromArray<float>(TensorShape({1}), {256.0f}); 249 TF_ASSERT_OK(RunOpKernel()); 250 Tensor expected(allocator(), DT_QINT32, TensorShape({element_count})); 251 test::FillValues<qint32>(&expected, 252 { 253 std::numeric_limits<int32>::min(), 254 0, 255 static_cast<int32>(1.0f * (1 << 23)), 256 static_cast<int32>(1.25f * (1 << 23)), 257 static_cast<int32>(1.75f * (1 << 23)), 258 static_cast<int32>(127.0f * (1 << 23)), 259 static_cast<int32>(255.0f * (1 << 23)), 260 std::numeric_limits<int32>::max(), 261 }); 262 // We expect there will be some fuzziness in the lower bits, since this is 263 // converting from float. 264 const int64 epsilon = 1 << 8; 265 const qint32* output_data = GetOutput(0)->flat<qint32>().data(); 266 const qint32* expected_data = expected.flat<qint32>().data(); 267 for (int i = 0; i < element_count; ++i) { 268 const int64 delta = output_data[i] - expected_data[i]; 269 EXPECT_GT(epsilon, std::abs(delta)) 270 << "output_data[" << i << "]=" << output_data[i] << ", expected_data[" 271 << i << "]=" << expected_data[i] << ", delta=" << delta; 272 } 273 } 274 275 TEST_F(QuantizedOpTest, QuantizeV2Ports) { 276 TF_ASSERT_OK(NodeDefBuilder("quantize_op", "QuantizeV2") 277 .Input(FakeInput(DT_FLOAT)) 278 .Input(FakeInput(DT_FLOAT)) 279 .Input(FakeInput(DT_FLOAT)) 280 .Attr("T", DataTypeToEnum<quint8>::v()) 281 .Attr("mode", "MIN_FIRST") 282 .Finalize(node_def())); 283 TF_ASSERT_OK(InitOp()); 284 AddInputFromArray<float>(TensorShape({6}), 285 {1.0, 1.25, 1.75, 127.0, 255.0, 500.0}); 286 AddInputFromArray<float>(TensorShape({1}), {0}); 287 AddInputFromArray<float>(TensorShape({1}), {255.0f}); 288 TF_ASSERT_OK(RunOpKernel()); 289 Tensor expected(allocator(), DT_QUINT8, TensorShape({6})); 290 test::FillValues<quint8>(&expected, {1, 1, 2, 127, 255, 255}); 291 test::ExpectTensorEqual<quint8>(expected, *GetOutput(0)); 292 const float output_min = GetOutput(1)->flat<float>()(0); 293 const float output_max = GetOutput(2)->flat<float>()(0); 294 EXPECT_NEAR(0.0f, output_min, 1e-5f); 295 EXPECT_NEAR(255.0f, output_max, 1e-5f); 296 } 297 298 TEST_F(QuantizedOpTest, QuantizeV2EqualRange) { 299 TF_ASSERT_OK(NodeDefBuilder("quantize_op", "QuantizeV2") 300 .Input(FakeInput(DT_FLOAT)) 301 .Input(FakeInput(DT_FLOAT)) 302 .Input(FakeInput(DT_FLOAT)) 303 .Attr("T", DataTypeToEnum<quint8>::v()) 304 .Attr("mode", "MIN_FIRST") 305 .Finalize(node_def())); 306 TF_ASSERT_OK(InitOp()); 307 AddInputFromArray<float>(TensorShape({6}), {0.0, 0.0, 0.0, 0.0, 0.0, 0.0}); 308 AddInputFromArray<float>(TensorShape({1}), {0.0f}); 309 AddInputFromArray<float>(TensorShape({1}), {0.0f}); 310 TF_ASSERT_OK(RunOpKernel()); 311 Tensor expected(allocator(), DT_QUINT8, TensorShape({6})); 312 test::FillValues<quint8>(&expected, {0, 0, 0, 0, 0, 0}); 313 test::ExpectTensorEqual<quint8>(expected, *GetOutput(0)); 314 const float output_min = GetOutput(1)->flat<float>()(0); 315 const float output_max = GetOutput(2)->flat<float>()(0); 316 EXPECT_NEAR(0.0f, output_min, 1e-5f); 317 EXPECT_LT(0.0f, output_max); 318 } 319 320 TEST_F(QuantizedOpTest, QuantizeV2MovesMinToIncludeZero) { 321 TF_ASSERT_OK(NodeDefBuilder("quantize_op", "QuantizeV2") 322 .Input(FakeInput(DT_FLOAT)) 323 .Input(FakeInput(DT_FLOAT)) 324 .Input(FakeInput(DT_FLOAT)) 325 .Attr("T", DataTypeToEnum<quint8>::v()) 326 .Attr("mode", "MIN_FIRST") 327 .Finalize(node_def())); 328 TF_ASSERT_OK(InitOp()); 329 AddInputFromArray<float>(TensorShape({3}), {0.1, 0.2, 0.3}); 330 AddInputFromArray<float>(TensorShape({1}), {0.1}); 331 AddInputFromArray<float>(TensorShape({1}), {0.3}); 332 TF_ASSERT_OK(RunOpKernel()); 333 Tensor expected(allocator(), DT_QUINT8, TensorShape({3})); 334 test::FillValues<quint8>(&expected, {85, 170, 255}); 335 test::ExpectTensorEqual<quint8>(expected, *GetOutput(0)); 336 const float output_min = GetOutput(1)->flat<float>()(0); 337 const float output_max = GetOutput(2)->flat<float>()(0); 338 EXPECT_NEAR(0.0f, output_min, 1e-5f); 339 EXPECT_NEAR(0.3f, output_max, 1e-5f); 340 } 341 342 TEST_F(QuantizedOpTest, QuantizeV2MovesMaxToIncludeZero) { 343 TF_ASSERT_OK(NodeDefBuilder("quantize_op", "QuantizeV2") 344 .Input(FakeInput(DT_FLOAT)) 345 .Input(FakeInput(DT_FLOAT)) 346 .Input(FakeInput(DT_FLOAT)) 347 .Attr("T", DataTypeToEnum<quint8>::v()) 348 .Attr("mode", "MIN_FIRST") 349 .Finalize(node_def())); 350 TF_ASSERT_OK(InitOp()); 351 AddInputFromArray<float>(TensorShape({3}), {-0.1, -0.2, -0.3}); 352 AddInputFromArray<float>(TensorShape({1}), {-0.3}); 353 AddInputFromArray<float>(TensorShape({1}), {-0.1}); 354 TF_ASSERT_OK(RunOpKernel()); 355 Tensor expected(allocator(), DT_QUINT8, TensorShape({3})); 356 test::FillValues<quint8>(&expected, {170, 85, 0}); 357 test::ExpectTensorEqual<quint8>(expected, *GetOutput(0)); 358 const float output_min = GetOutput(1)->flat<float>()(0); 359 const float output_max = GetOutput(2)->flat<float>()(0); 360 EXPECT_NEAR(-0.3f, output_min, 1e-5f); 361 EXPECT_NEAR(0.0f, output_max, 1e-5f); 362 } 363 364 TEST_F(QuantizedOpTest, Dequantize) { 365 TF_ASSERT_OK(NodeDefBuilder("dequantize_op", "Dequantize") 366 .Input(FakeInput(DT_QUINT8)) 367 .Input(FakeInput(DT_FLOAT)) 368 .Input(FakeInput(DT_FLOAT)) 369 .Attr("T", DataTypeToEnum<quint8>::v()) 370 .Attr("mode", "MIN_FIRST") 371 .Finalize(node_def())); 372 TF_ASSERT_OK(InitOp()); 373 AddInputFromArray<quint8>(TensorShape({6}), {1, 2, 4, 8, 16, 255}); 374 AddInputFromArray<float>(TensorShape({1}), {0}); 375 AddInputFromArray<float>(TensorShape({1}), {255.0f}); 376 TF_ASSERT_OK(RunOpKernel()); 377 Tensor expected(allocator(), DT_FLOAT, TensorShape({6})); 378 test::FillValues<float>(&expected, {1.0, 2.0, 4.0, 8.0, 16.0, 255.0}); 379 test::ExpectTensorNear<float>(expected, *GetOutput(0), 0.5); 380 } 381 382 } // end namespace tensorflow 383