| /* Copyright 2015 The TensorFlow Authors. All Rights Reserved. |
| |
| Licensed under the Apache License, Version 2.0 (the "License"); |
| you may not use this file except in compliance with the License. |
| You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, software |
| distributed under the License is distributed on an "AS IS" BASIS, |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| See the License for the specific language governing permissions and |
| limitations under the License. |
| ==============================================================================*/ |
| |
| #include <functional> |
| #include <memory> |
| #include <vector> |
| |
| #include "tensorflow/cc/ops/array_ops.h" |
| #include "tensorflow/core/common_runtime/kernel_benchmark_testlib.h" |
| #include "tensorflow/core/framework/allocator.h" |
| #include "tensorflow/core/framework/fake_input.h" |
| #include "tensorflow/core/framework/node_def_builder.h" |
| #include "tensorflow/core/framework/op_kernel.h" |
| #include "tensorflow/core/framework/tensor.h" |
| #include "tensorflow/core/framework/tensor_testutil.h" |
| #include "tensorflow/core/framework/types.h" |
| #include "tensorflow/core/framework/types.pb.h" |
| #include "tensorflow/core/kernels/ops_testutil.h" |
| #include "tensorflow/core/lib/core/status_test_util.h" |
| #include "tensorflow/core/lib/strings/str_util.h" |
| #include "tensorflow/core/platform/test_benchmark.h" |
| |
| namespace tensorflow { |
| namespace { |
| |
| class QuantizeAndDequantizeTest : public OpsTestBase {}; |
| |
| // Convert a simple scalar tensor. |
| TEST_F(QuantizeAndDequantizeTest, Convert_scalar_tensor) { |
| TF_ASSERT_OK( |
| NodeDefBuilder("quantize_and_dequantize_op", "QuantizeAndDequantizeV2") |
| .Input(FakeInput(DT_FLOAT)) |
| .Input(FakeInput(DT_FLOAT)) |
| .Input(FakeInput(DT_FLOAT)) |
| .Attr("signed_input", true) |
| .Attr("num_bits", 8) |
| .Attr("range_given", false) |
| .Finalize(node_def())); |
| TF_ASSERT_OK(InitOp()); |
| AddInputFromArray<float>(TensorShape({1}), {-3.5}); |
| AddInputFromArray<float>(TensorShape({}), {0.0}); // Min |
| AddInputFromArray<float>(TensorShape({}), {0.0}); // Max |
| |
| TF_ASSERT_OK(RunOpKernel()); |
| Tensor expected(allocator(), DT_FLOAT, TensorShape({1})); |
| test::FillValues<float>(&expected, {-3.5}); |
| test::ExpectTensorEqual<float>(expected, *GetOutput(0)); |
| |
| // Ensure that the inputs haven't been changed. |
| EXPECT_EQ(inputs_[1]->scalar<float>()(), 0.0); |
| EXPECT_EQ(inputs_[2]->scalar<float>()(), 0.0); |
| } |
| |
| TEST_F(QuantizeAndDequantizeTest, Convert_scalar_tensor_V3) { |
| TF_ASSERT_OK( |
| NodeDefBuilder("quantize_and_dequantize_op", "QuantizeAndDequantizeV3") |
| .Input(FakeInput(DT_FLOAT)) |
| .Input(FakeInput(DT_FLOAT)) |
| .Input(FakeInput(DT_FLOAT)) |
| .Input(FakeInput(DT_INT32)) |
| .Attr("signed_input", true) |
| .Attr("range_given", false) |
| .Finalize(node_def())); |
| TF_ASSERT_OK(InitOp()); |
| AddInputFromArray<float>(TensorShape({1}), {-3.5}); |
| AddInputFromArray<float>(TensorShape({}), {0.0}); // Min |
| AddInputFromArray<float>(TensorShape({}), {0.0}); // Max |
| AddInputFromArray<int32>(TensorShape({}), {8}); // num_bits |
| |
| TF_ASSERT_OK(RunOpKernel()); |
| Tensor expected(allocator(), DT_FLOAT, TensorShape({1})); |
| test::FillValues<float>(&expected, {-3.5}); |
| test::ExpectTensorEqual<float>(expected, *GetOutput(0)); |
| |
| // Ensure that the inputs haven't been changed. |
| EXPECT_EQ(inputs_[1]->scalar<float>()(), 0.0); |
| EXPECT_EQ(inputs_[2]->scalar<float>()(), 0.0); |
| } |
| |
| // Convert a 1D tensor with signed 8 bits. |
| TEST_F(QuantizeAndDequantizeTest, Convert_1D_tensor_with_int8) { |
| TF_ASSERT_OK( |
| NodeDefBuilder("quantize_and_dequantize_op", "QuantizeAndDequantizeV2") |
| .Input(FakeInput(DT_FLOAT)) |
| .Input(FakeInput(DT_FLOAT)) |
| .Input(FakeInput(DT_FLOAT)) |
| .Attr("signed_input", true) |
| .Attr("num_bits", 8) |
| .Attr("range_given", false) |
| .Finalize(node_def())); |
| TF_ASSERT_OK(InitOp()); |
| AddInputFromArray<float>(TensorShape({7}), |
| {-1, -0.5, 0, 0.3, 0.8, 0.555, 0.50390625}); |
| AddInputFromArray<float>(TensorShape({}), {0.0}); // Min |
| AddInputFromArray<float>(TensorShape({}), {0.0}); // Max |
| |
| // With int8, the tensor is quantized to {-128, -64, 0, 38, 102, 71, 64}. |
| // Scale is: 1/127 |
| // Then it is dequantized to {-1, -0.5, 0, 38.0/128, 102.0/128, 71.0/128, 0.5} |
| TF_ASSERT_OK(RunOpKernel()); |
| Tensor expected(allocator(), DT_FLOAT, TensorShape({7})); |
| test::FillValues<float>( |
| &expected, {-1, -0.5, 0, 38.0 / 128, 102.0 / 128, 71.0 / 128, 0.5}); |
| test::ExpectTensorNear<float>(expected, *GetOutput(0), 1e-5); |
| |
| // Ensure that the inputs haven't been changed. |
| EXPECT_EQ(inputs_[1]->scalar<float>()(), 0.0); |
| EXPECT_EQ(inputs_[2]->scalar<float>()(), 0.0); |
| } |
| |
| // Convert a 1D tensor with signed 8 bits and round_mode half_up. |
| TEST_F(QuantizeAndDequantizeTest, Convert_1D_tensor_with_int8_round_half_up) { |
| TF_ASSERT_OK( |
| NodeDefBuilder("quantize_and_dequantize_op", "QuantizeAndDequantizeV2") |
| .Input(FakeInput(DT_FLOAT)) |
| .Input(FakeInput(DT_FLOAT)) |
| .Input(FakeInput(DT_FLOAT)) |
| .Attr("signed_input", true) |
| .Attr("num_bits", 8) |
| .Attr("range_given", false) |
| .Attr("round_mode", "HALF_UP") |
| .Finalize(node_def())); |
| TF_ASSERT_OK(InitOp()); |
| AddInputFromArray<float>(TensorShape({7}), |
| {-1, -0.5, 0, 0.3, 0.8, 0.555, 0.50390625}); |
| AddInputFromArray<float>(TensorShape({}), {0.0}); // Min |
| AddInputFromArray<float>(TensorShape({}), {0.0}); // Max |
| |
| // With int8, the tensor is quantized to {-128, -64, 0, 38, 102, 71, 65}. |
| // Scale is: 1/127 |
| // Then it is dequantized to {-1, -0.5, 0, 38.0/128, 102.0/128, 71.0/128, |
| // 65.0 /128} |
| TF_ASSERT_OK(RunOpKernel()); |
| Tensor expected(allocator(), DT_FLOAT, TensorShape({7})); |
| test::FillValues<float>(&expected, {-1, -0.5, 0, 38.0 / 128, 102.0 / 128, |
| 71.0 / 128, 65.0 / 128}); |
| test::ExpectTensorNear<float>(expected, *GetOutput(0), 1e-5); |
| |
| // Ensure that the inputs haven't been changed. |
| EXPECT_EQ(inputs_[1]->scalar<float>()(), 0.0); |
| EXPECT_EQ(inputs_[2]->scalar<float>()(), 0.0); |
| } |
| |
| // Convert a 1D tensor with signed 8 bits and round_mode half_up, using |
| // narrow range quantization. |
| TEST_F(QuantizeAndDequantizeTest, |
| Convert_1D_tensor_with_int8_round_half_up_narrow_range) { |
| TF_ASSERT_OK( |
| NodeDefBuilder("quantize_and_dequantize_op", "QuantizeAndDequantizeV2") |
| .Input(FakeInput(DT_FLOAT)) |
| .Input(FakeInput(DT_FLOAT)) |
| .Input(FakeInput(DT_FLOAT)) |
| .Attr("signed_input", true) |
| .Attr("num_bits", 8) |
| .Attr("range_given", false) |
| .Attr("round_mode", "HALF_UP") |
| .Attr("narrow_range", true) |
| .Finalize(node_def())); |
| TF_ASSERT_OK(InitOp()); |
| AddInputFromArray<float>(TensorShape({7}), |
| {-1, -0.5, 0, 0.3, 0.8, 0.555, 0.50390625}); |
| AddInputFromArray<float>(TensorShape({}), {0.0}); // Min |
| AddInputFromArray<float>(TensorShape({}), {0.0}); // Max |
| |
| // With int8, the tensor is quantized to {-127, -63, 0, 38, 102, 70, 64}. |
| // Scale is: 1/127 |
| // Then it is dequantized to {-1, -63.0/127, 0, 38.0/127, 102.0/127, 70.0/127, |
| // 64.0/127} |
| TF_ASSERT_OK(RunOpKernel()); |
| Tensor expected(allocator(), DT_FLOAT, TensorShape({7})); |
| test::FillValues<float>(&expected, {-1, -63.0 / 127, 0, 38.0 / 127, |
| 102.0 / 127, 70.0 / 127, 64.0 / 127}); |
| test::ExpectTensorNear<float>(expected, *GetOutput(0), 1e-5); |
| |
| // Ensure that the inputs haven't been changed. |
| EXPECT_EQ(inputs_[1]->scalar<float>()(), 0.0); |
| EXPECT_EQ(inputs_[2]->scalar<float>()(), 0.0); |
| } |
| |
| // Convert a 1D tensor with signed 8 bits. |
| TEST_F(QuantizeAndDequantizeTest, Convert_1D_tensor_with_int8_V3) { |
| TF_ASSERT_OK( |
| NodeDefBuilder("quantize_and_dequantize_op", "QuantizeAndDequantizeV3") |
| .Input(FakeInput(DT_FLOAT)) |
| .Input(FakeInput(DT_FLOAT)) |
| .Input(FakeInput(DT_FLOAT)) |
| .Input(FakeInput(DT_INT32)) |
| .Attr("signed_input", true) |
| .Attr("range_given", false) |
| .Finalize(node_def())); |
| TF_ASSERT_OK(InitOp()); |
| AddInputFromArray<float>(TensorShape({6}), {-1, -0.5, 0, 0.3, 0.8, 0.555}); |
| AddInputFromArray<float>(TensorShape({}), {0.0}); // Min |
| AddInputFromArray<float>(TensorShape({}), {0.0}); // Max |
| AddInputFromArray<int32>(TensorShape({}), {8}); // num_bits |
| |
| // With int8, the tensor is quantized to {-128, -64, 0, 38, 102, 71}. |
| // Scale is: 1/128 |
| // Then it is dequantized to {-1, -64.0/128, 0, 38.0/128, 102.0/128, 71.0/128} |
| TF_ASSERT_OK(RunOpKernel()); |
| Tensor expected(allocator(), DT_FLOAT, TensorShape({6})); |
| test::FillValues<float>(&expected, |
| {-1, -0.5, 0, 38.0 / 128, 102.0 / 128, 71.0 / 128}); |
| test::ExpectTensorNear<float>(expected, *GetOutput(0), 1e-5); |
| |
| // Ensure that the inputs haven't been changed. |
| EXPECT_EQ(inputs_[1]->scalar<float>()(), 0.0); |
| EXPECT_EQ(inputs_[2]->scalar<float>()(), 0.0); |
| } |
| |
| // Convert a 1D tensor with signed 8 bits, using narrow range quantization. |
| TEST_F(QuantizeAndDequantizeTest, Convert_1D_tensor_with_int8_narrow_range_V3) { |
| TF_ASSERT_OK( |
| NodeDefBuilder("quantize_and_dequantize_op", "QuantizeAndDequantizeV3") |
| .Input(FakeInput(DT_FLOAT)) |
| .Input(FakeInput(DT_FLOAT)) |
| .Input(FakeInput(DT_FLOAT)) |
| .Input(FakeInput(DT_INT32)) |
| .Attr("signed_input", true) |
| .Attr("range_given", false) |
| .Attr("narrow_range", true) |
| .Finalize(node_def())); |
| TF_ASSERT_OK(InitOp()); |
| AddInputFromArray<float>(TensorShape({7}), |
| {-1, -0.5, 0, 0.3, 0.8, 0.555, 0.50390625}); |
| AddInputFromArray<float>(TensorShape({}), {0.0}); // Min |
| AddInputFromArray<float>(TensorShape({}), {0.0}); // Max |
| AddInputFromArray<int32>(TensorShape({}), {8}); // num_bits |
| |
| // With int8, the tensor is quantized to {-127, -64, 0, 38, 102, 70, 64}. |
| // Scale is: 1/127 |
| // Then it is dequantized to {-1, -64.0/127, 0, 38.0/127, 102.0/127, 70.0/127, |
| // 64.0/127} |
| TF_ASSERT_OK(RunOpKernel()); |
| Tensor expected(allocator(), DT_FLOAT, TensorShape({7})); |
| test::FillValues<float>(&expected, {-1, -64.0 / 127, 0, 38.0 / 127, |
| 102.0 / 127, 70.0 / 127, 64.0 / 127}); |
| test::ExpectTensorNear<float>(expected, *GetOutput(0), 1e-5); |
| |
| // Ensure that the inputs haven't been changed. |
| EXPECT_EQ(inputs_[1]->scalar<float>()(), 0.0); |
| EXPECT_EQ(inputs_[2]->scalar<float>()(), 0.0); |
| } |
| |
| // Convert a 1D tensor with signed 4 bits. |
| TEST_F(QuantizeAndDequantizeTest, Convert_1D_tensor_with_int4) { |
| TF_ASSERT_OK( |
| NodeDefBuilder("quantize_and_dequantize_op", "QuantizeAndDequantizeV2") |
| .Input(FakeInput(DT_FLOAT)) |
| .Input(FakeInput(DT_FLOAT)) |
| .Input(FakeInput(DT_FLOAT)) |
| .Attr("signed_input", true) |
| .Attr("num_bits", 4) |
| .Attr("range_given", false) |
| .Finalize(node_def())); |
| TF_ASSERT_OK(InitOp()); |
| AddInputFromArray<float>(TensorShape({6}), {-1, -0.5, 0, 0.3125, 0.8, 0.555}); |
| AddInputFromArray<float>(TensorShape({}), {0.0}); // Min |
| AddInputFromArray<float>(TensorShape({}), {0.0}); // Max |
| |
| // With int4, the tensor is quantized to {-8, -4, 0, 2, 6, 4}. |
| // Scale is: 1/8 |
| TF_ASSERT_OK(RunOpKernel()); |
| Tensor expected(allocator(), DT_FLOAT, TensorShape({6})); |
| test::FillValues<float>(&expected, {-1, -0.5, 0, 0.25, 0.75, 0.5}); |
| test::ExpectTensorNear<float>(expected, *GetOutput(0), 1e-5); |
| |
| // Ensure that the inputs haven't been changed. |
| EXPECT_EQ(inputs_[1]->scalar<float>()(), 0.0); |
| EXPECT_EQ(inputs_[2]->scalar<float>()(), 0.0); |
| } |
| |
| // Convert a 1D tensor with signed 4 bits and round_mode hafl_up. |
| TEST_F(QuantizeAndDequantizeTest, Convert_1D_tensor_with_int4_round_half_up) { |
| TF_ASSERT_OK( |
| NodeDefBuilder("quantize_and_dequantize_op", "QuantizeAndDequantizeV2") |
| .Input(FakeInput(DT_FLOAT)) |
| .Input(FakeInput(DT_FLOAT)) |
| .Input(FakeInput(DT_FLOAT)) |
| .Attr("signed_input", true) |
| .Attr("num_bits", 4) |
| .Attr("range_given", false) |
| .Attr("round_mode", "HALF_UP") |
| .Finalize(node_def())); |
| TF_ASSERT_OK(InitOp()); |
| AddInputFromArray<float>(TensorShape({6}), {-1, -0.5, 0, 0.3125, 0.8, 0.555}); |
| AddInputFromArray<float>(TensorShape({}), {0.0}); // Min |
| AddInputFromArray<float>(TensorShape({}), {0.0}); // Max |
| |
| // With int4, the tensor is quantized to {-8, -4, 0, 3, 6, 4}. |
| // Scale is: 1/8 |
| TF_ASSERT_OK(RunOpKernel()); |
| Tensor expected(allocator(), DT_FLOAT, TensorShape({6})); |
| test::FillValues<float>(&expected, {-1, -0.5, 0, 0.375, 0.75, 0.5}); |
| test::ExpectTensorNear<float>(expected, *GetOutput(0), 1e-5); |
| |
| // Ensure that the inputs haven't been changed. |
| EXPECT_EQ(inputs_[1]->scalar<float>()(), 0.0); |
| EXPECT_EQ(inputs_[2]->scalar<float>()(), 0.0); |
| } |
| |
| // Convert a 1D tensor with signed 4 bits. |
| TEST_F(QuantizeAndDequantizeTest, Convert_1D_tensor_with_int4_V3) { |
| TF_ASSERT_OK( |
| NodeDefBuilder("quantize_and_dequantize_op", "QuantizeAndDequantizeV3") |
| .Input(FakeInput(DT_FLOAT)) |
| .Input(FakeInput(DT_FLOAT)) |
| .Input(FakeInput(DT_FLOAT)) |
| .Input(FakeInput(DT_INT32)) |
| .Attr("signed_input", true) |
| .Attr("range_given", false) |
| .Finalize(node_def())); |
| TF_ASSERT_OK(InitOp()); |
| AddInputFromArray<float>(TensorShape({6}), {-1, -0.5, 0, 0.3, 0.8, 0.555}); |
| AddInputFromArray<float>(TensorShape({}), {0.0}); // Min |
| AddInputFromArray<float>(TensorShape({}), {0.0}); // Max |
| AddInputFromArray<int32>(TensorShape({}), {4}); // num_bits |
| |
| // With int4, the tensor is quantized to {-8, -4, 0, 2, 6, 4}. |
| // Scale is: 1/8 |
| TF_ASSERT_OK(RunOpKernel()); |
| Tensor expected(allocator(), DT_FLOAT, TensorShape({6})); |
| test::FillValues<float>(&expected, {-1, -0.5, 0, 0.25, 0.75, 0.5}); |
| test::ExpectTensorNear<float>(expected, *GetOutput(0), 1e-5); |
| |
| // Ensure that the inputs haven't been changed. |
| EXPECT_EQ(inputs_[1]->scalar<float>()(), 0.0); |
| EXPECT_EQ(inputs_[2]->scalar<float>()(), 0.0); |
| } |
| |
| // Convert a 2D tensor with signed 8 bits with given range. |
| TEST_F(QuantizeAndDequantizeTest, Convert_2D_tensor_with_int8_range_given) { |
| TF_ASSERT_OK( |
| NodeDefBuilder("quantize_and_dequantize_op", "QuantizeAndDequantizeV2") |
| .Input(FakeInput(DT_FLOAT)) |
| .Input(FakeInput(DT_FLOAT)) |
| .Input(FakeInput(DT_FLOAT)) |
| .Attr("signed_input", true) |
| .Attr("num_bits", 8) |
| .Attr("range_given", true) |
| .Finalize(node_def())); |
| TF_ASSERT_OK(InitOp()); |
| // Note that the last two values are saturated. |
| AddInputFromArray<float>(TensorShape({2, 4}), |
| {-0.8, -0.5, 0, 0.3, 0.8, 0.555, -2, 33}); |
| AddInputFromArray<float>(TensorShape({}), {-1.0}); // Min |
| AddInputFromArray<float>(TensorShape({}), {1.0}); // Max |
| |
| // Note that the range is given as [-1, 1]. |
| // With int8, the tensor is quantized to {-102, -64, 0, 38, 102, 70, -128, |
| // 127}. |
| // Scale is: 1/127 |
| TF_ASSERT_OK(RunOpKernel()); |
| Tensor expected(allocator(), DT_FLOAT, TensorShape({2, 4})); |
| test::FillValues<float>( |
| &expected, {-102.0 / 127, -64.0 / 127, 0, 38.0 / 127, 102.0 / 127, |
| 70.0 / 127, -128.0 / 127, 1}); |
| test::ExpectTensorNear<float>(expected, *GetOutput(0), 1e-5); |
| } |
| |
| // Convert a 2D tensor with signed 8 bits, given range and round_mode half_up. |
| TEST_F(QuantizeAndDequantizeTest, |
| Convert_2D_tensor_with_int8_range_given_round_half_up) { |
| TF_ASSERT_OK( |
| NodeDefBuilder("quantize_and_dequantize_op", "QuantizeAndDequantizeV2") |
| .Input(FakeInput(DT_FLOAT)) |
| .Input(FakeInput(DT_FLOAT)) |
| .Input(FakeInput(DT_FLOAT)) |
| .Attr("signed_input", true) |
| .Attr("num_bits", 8) |
| .Attr("range_given", true) |
| .Attr("round_mode", "HALF_UP") |
| .Finalize(node_def())); |
| TF_ASSERT_OK(InitOp()); |
| // Note that the last two values are saturated. |
| AddInputFromArray<float>(TensorShape({2, 4}), |
| {-0.8, -0.5, 0, 0.3, 0.8, 0.555, -2, 33}); |
| AddInputFromArray<float>(TensorShape({}), {-1.0}); // Min |
| AddInputFromArray<float>(TensorShape({}), {1.0}); // Max |
| |
| // Note that the range is given as [-1, 1]. |
| // With int8, the tensor is quantized to {-102, -63, 0, 38, 102, 70, -128, |
| // 127}. |
| // Scale is: 1/127 |
| TF_ASSERT_OK(RunOpKernel()); |
| Tensor expected(allocator(), DT_FLOAT, TensorShape({2, 4})); |
| test::FillValues<float>( |
| &expected, {-102.0 / 127, -63.0 / 127, 0, 38.0 / 127, 102.0 / 127, |
| 70.0 / 127, -128.0 / 127, 1}); |
| test::ExpectTensorNear<float>(expected, *GetOutput(0), 1e-5); |
| } |
| |
| // Convert a 2D tensor with signed 8 bits with given range. |
| TEST_F(QuantizeAndDequantizeTest, Convert_2D_tensor_with_int8_range_given_V3) { |
| TF_ASSERT_OK( |
| NodeDefBuilder("quantize_and_dequantize_op", "QuantizeAndDequantizeV3") |
| .Input(FakeInput(DT_FLOAT)) |
| .Input(FakeInput(DT_FLOAT)) |
| .Input(FakeInput(DT_FLOAT)) |
| .Input(FakeInput(DT_INT32)) |
| .Attr("signed_input", true) |
| .Attr("range_given", true) |
| .Finalize(node_def())); |
| TF_ASSERT_OK(InitOp()); |
| // Note that the last two values are saturated. |
| AddInputFromArray<float>(TensorShape({2, 4}), |
| {-0.8, -0.5, 0, 0.3, 0.8, 0.555, -2, 33}); |
| AddInputFromArray<float>(TensorShape({}), {-1.0}); // Min |
| AddInputFromArray<float>(TensorShape({}), {1.0}); // Max |
| AddInputFromArray<int32>(TensorShape({}), {8}); // num_bits |
| |
| // Note that the range is given as [-1, 1]. |
| // With int8, the tensor is quantized to {-102, -64, 0, 38, 102, 70, -128, |
| // 127}. |
| // Scale is: 1/127 |
| TF_ASSERT_OK(RunOpKernel()); |
| Tensor expected(allocator(), DT_FLOAT, TensorShape({2, 4})); |
| test::FillValues<float>( |
| &expected, {-102.0 / 127, -64.0 / 127, 0, 38.0 / 127, 102.0 / 127, |
| 70.0 / 127, -128.0 / 127, 1}); |
| test::ExpectTensorNear<float>(expected, *GetOutput(0), 1e-5); |
| } |
| |
| // Convert a 4D tensor with unsigned 8 bits with given range. |
| TEST_F(QuantizeAndDequantizeTest, Convert_4D_tensor_with_uint8_range_given) { |
| TF_ASSERT_OK( |
| NodeDefBuilder("quantize_and_dequantize_op", "QuantizeAndDequantizeV2") |
| .Input(FakeInput(DT_FLOAT)) |
| .Input(FakeInput(DT_FLOAT)) |
| .Input(FakeInput(DT_FLOAT)) |
| .Attr("signed_input", false) |
| .Attr("num_bits", 8) |
| .Attr("range_given", true) |
| .Finalize(node_def())); |
| TF_ASSERT_OK(InitOp()); |
| AddInputFromArray<float>(TensorShape({2, 2, 1, 1}), {-0.5, 0, 0.3, 0.8}); |
| AddInputFromArray<float>(TensorShape({}), {0.0}); // Min |
| AddInputFromArray<float>(TensorShape({}), {1.0}); // Max |
| |
| // Note that the range is given as [0, 1]. |
| // With int8, the tensor is quantized to {0, 0, 76, 204} |
| // Scale is: 1/255 |
| TF_ASSERT_OK(RunOpKernel()); |
| Tensor expected(allocator(), DT_FLOAT, TensorShape({2, 2, 1, 1})); |
| test::FillValues<float>(&expected, {0, 0, 76.0 / 255, 204.0 / 255}); |
| test::ExpectTensorNear<float>(expected, *GetOutput(0), 1e-5); |
| } |
| |
| // Convert a 4D tensor with unsigned 8 bits, given range and round_mode half_up. |
| TEST_F(QuantizeAndDequantizeTest, |
| Convert_4D_tensor_with_uint8_range_given_round_half_up) { |
| TF_ASSERT_OK( |
| NodeDefBuilder("quantize_and_dequantize_op", "QuantizeAndDequantizeV2") |
| .Input(FakeInput(DT_FLOAT)) |
| .Input(FakeInput(DT_FLOAT)) |
| .Input(FakeInput(DT_FLOAT)) |
| .Attr("signed_input", false) |
| .Attr("num_bits", 8) |
| .Attr("range_given", true) |
| .Attr("round_mode", "HALF_UP") |
| .Finalize(node_def())); |
| TF_ASSERT_OK(InitOp()); |
| AddInputFromArray<float>(TensorShape({2, 2, 1, 1}), {-0.5, 0, 0.3, 0.8}); |
| AddInputFromArray<float>(TensorShape({}), {0.0}); // Min |
| AddInputFromArray<float>(TensorShape({}), {1.0}); // Max |
| |
| // Note that the range is given as [0, 1]. |
| // With int8, the tensor is quantized to {0, 0, 77, 204} |
| // Scale is: 1/255 |
| TF_ASSERT_OK(RunOpKernel()); |
| Tensor expected(allocator(), DT_FLOAT, TensorShape({2, 2, 1, 1})); |
| test::FillValues<float>(&expected, {0, 0, 77.0 / 255, 204.0 / 255}); |
| test::ExpectTensorNear<float>(expected, *GetOutput(0), 1e-5); |
| } |
| |
| // Convert a 4D tensor with unsigned 8 bits with given range. |
| TEST_F(QuantizeAndDequantizeTest, Convert_4D_tensor_with_uint8_range_given_V3) { |
| TF_ASSERT_OK( |
| NodeDefBuilder("quantize_and_dequantize_op", "QuantizeAndDequantizeV3") |
| .Input(FakeInput(DT_FLOAT)) |
| .Input(FakeInput(DT_FLOAT)) |
| .Input(FakeInput(DT_FLOAT)) |
| .Input(FakeInput(DT_INT32)) |
| .Attr("signed_input", false) |
| .Attr("range_given", true) |
| .Finalize(node_def())); |
| TF_ASSERT_OK(InitOp()); |
| AddInputFromArray<float>(TensorShape({2, 2, 1, 1}), {-0.5, 0, 0.3, 0.8}); |
| AddInputFromArray<float>(TensorShape({}), {0.0}); // Min |
| AddInputFromArray<float>(TensorShape({}), {1.0}); // Max |
| AddInputFromArray<int32>(TensorShape({}), {8}); // num_bits |
| |
| // Note that the range is given as [0, 1]. |
| // With int8, the tensor is quantized to {0, 0, 76, 204} |
| // Scale is: 1/255 |
| TF_ASSERT_OK(RunOpKernel()); |
| Tensor expected(allocator(), DT_FLOAT, TensorShape({2, 2, 1, 1})); |
| test::FillValues<float>(&expected, {0, 0, 76.0 / 255, 204.0 / 255}); |
| test::ExpectTensorNear<float>(expected, *GetOutput(0), 1e-5); |
| } |
| |
| // Convert a tensor with all 0. |
| TEST_F(QuantizeAndDequantizeTest, Convert_tensor_with_all_0) { |
| TF_ASSERT_OK( |
| NodeDefBuilder("quantize_and_dequantize_op", "QuantizeAndDequantizeV2") |
| .Input(FakeInput(DT_FLOAT)) |
| .Input(FakeInput(DT_FLOAT)) |
| .Input(FakeInput(DT_FLOAT)) |
| .Attr("signed_input", false) |
| .Attr("num_bits", 8) |
| .Attr("range_given", false) |
| .Finalize(node_def())); |
| TF_ASSERT_OK(InitOp()); |
| AddInputFromArray<float>(TensorShape({2, 2, 1, 1}), {0, 0, 0, 0}); |
| AddInputFromArray<float>(TensorShape({}), {0.0}); // Min |
| AddInputFromArray<float>(TensorShape({}), {0.0}); // Max |
| |
| TF_ASSERT_OK(RunOpKernel()); |
| Tensor expected(allocator(), DT_FLOAT, TensorShape({2, 2, 1, 1})); |
| test::FillValues<float>(&expected, {0, 0, 0, 0}); |
| test::ExpectTensorNear<float>(expected, *GetOutput(0), 1e-5); |
| } |
| |
| // Convert a tensor with all 0. |
| TEST_F(QuantizeAndDequantizeTest, Convert_tensor_with_all_0_V3) { |
| TF_ASSERT_OK( |
| NodeDefBuilder("quantize_and_dequantize_op", "QuantizeAndDequantizeV3") |
| .Input(FakeInput(DT_FLOAT)) |
| .Input(FakeInput(DT_FLOAT)) |
| .Input(FakeInput(DT_FLOAT)) |
| .Input(FakeInput(DT_INT32)) |
| .Attr("signed_input", false) |
| .Attr("range_given", false) |
| .Finalize(node_def())); |
| TF_ASSERT_OK(InitOp()); |
| AddInputFromArray<float>(TensorShape({2, 2, 1, 1}), {0, 0, 0, 0}); |
| AddInputFromArray<float>(TensorShape({}), {0.0}); // Min |
| AddInputFromArray<float>(TensorShape({}), {0.0}); // Max |
| AddInputFromArray<int32>(TensorShape({}), {8}); // num_bits |
| |
| TF_ASSERT_OK(RunOpKernel()); |
| Tensor expected(allocator(), DT_FLOAT, TensorShape({2, 2, 1, 1})); |
| test::FillValues<float>(&expected, {0, 0, 0, 0}); |
| test::ExpectTensorNear<float>(expected, *GetOutput(0), 1e-5); |
| } |
| |
| // Range is invalid |
| TEST_F(QuantizeAndDequantizeTest, Invalid_range_given) { |
| TF_ASSERT_OK( |
| NodeDefBuilder("quantize_and_dequantize_Op", "QuantizeAndDequantizeV2") |
| .Input(FakeInput(DT_FLOAT)) |
| .Input(FakeInput(DT_FLOAT)) |
| .Input(FakeInput(DT_FLOAT)) |
| .Attr("num_bits", 8) |
| .Attr("range_given", true) |
| .Finalize(node_def())); |
| TF_ASSERT_OK(InitOp()); |
| AddInputFromArray<float>(TensorShape({2, 2, 1, 1}), {-0.5, 0, 0.3, 0.8}); |
| AddInputFromArray<float>(TensorShape({}), {1.0}); // Min |
| AddInputFromArray<float>(TensorShape({}), {0.0}); // Max |
| |
| Status s = RunOpKernel(); |
| EXPECT_TRUE(absl::StrContains(s.ToString(), |
| "Invalid range: input_min 1 > input_max 0")) |
| << s; |
| } |
| |
| // Range is invalid |
| TEST_F(QuantizeAndDequantizeTest, Invalid_range_given_V3) { |
| TF_ASSERT_OK( |
| NodeDefBuilder("quantize_and_dequantize_Op", "QuantizeAndDequantizeV3") |
| .Input(FakeInput(DT_FLOAT)) |
| .Input(FakeInput(DT_FLOAT)) |
| .Input(FakeInput(DT_FLOAT)) |
| .Input(FakeInput(DT_INT32)) |
| .Attr("range_given", true) |
| .Finalize(node_def())); |
| TF_ASSERT_OK(InitOp()); |
| AddInputFromArray<float>(TensorShape({2, 2, 1, 1}), {-0.5, 0, 0.3, 0.8}); |
| AddInputFromArray<float>(TensorShape({}), {1.0}); // Min |
| AddInputFromArray<float>(TensorShape({}), {0.0}); // Max |
| AddInputFromArray<int32>(TensorShape({}), {8}); // num_bits |
| |
| Status s = RunOpKernel(); |
| EXPECT_TRUE(absl::StrContains(s.ToString(), |
| "Invalid range: input_min 1 > input_max 0")) |
| << s; |
| } |
| |
| #define BM_SIMPLE_QUAN_DEQUAN(DEVICE) \ |
| static void BM_SIMPLE_QUAN_DEQUAN_##DEVICE(int iters) { \ |
| auto root = Scope::NewRootScope().ExitOnError(); \ |
| ops::QuantizeAndDequantizeV2(root, -3.5, -3.5, -3.5); \ |
| TF_CHECK_OK(root.status()); \ |
| Graph* g = new Graph(OpRegistry::Global()); \ |
| TF_CHECK_OK(root.ToGraph(g)); \ |
| test::Benchmark(#DEVICE, g).Run(iters); \ |
| } \ |
| BENCHMARK(BM_SIMPLE_QUAN_DEQUAN_##DEVICE); |
| |
| BM_SIMPLE_QUAN_DEQUAN(cpu); |
| BM_SIMPLE_QUAN_DEQUAN(gpu); |
| |
| } // namespace |
| } // namespace tensorflow |