Home | History | Annotate | Download | only in kernels
      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