Home | History | Annotate | Download | only in kernels
      1 /* Copyright 2017 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 <cstdarg>
     16 #include <gtest/gtest.h>
     17 #include "tensorflow/contrib/lite/interpreter.h"
     18 #include "tensorflow/contrib/lite/kernels/register.h"
     19 #include "tensorflow/contrib/lite/kernels/test_util.h"
     20 #include "tensorflow/contrib/lite/model.h"
     21 
     22 namespace tflite {
     23 namespace {
     24 
     25 using ::testing::ElementsAreArray;
     26 
     27 class BaseActivationsOpModel : public SingleOpModel {
     28  public:
     29   // Most activations don't take any options, so this constructor works for
     30   // them.
     31   BaseActivationsOpModel(BuiltinOperator type, TensorData input) {
     32     input_ = AddInput(input);
     33     if (input.type == TensorType_UINT8) {
     34       output_ = AddOutput({input.type, {}, 0, 0, 1. / 256});
     35     } else {
     36       output_ = AddOutput({input.type, {}});
     37     }
     38     SetBuiltinOp(type, BuiltinOptions_NONE, 0);
     39     BuildInterpreter({GetShape(input_)});
     40   }
     41 
     42   // A dedicated constructor for SOFTMAX, which does some options.
     43   BaseActivationsOpModel(float softmax_beta, TensorData input) {
     44     input_ = AddInput(input);
     45     if (input.type == TensorType_UINT8) {
     46       output_ = AddOutput({input.type, {}, 0, 0, 1. / 256});
     47     } else {
     48       output_ = AddOutput({input.type, {}});
     49     }
     50     SetBuiltinOp(BuiltinOperator_SOFTMAX, BuiltinOptions_SoftmaxOptions,
     51                  CreateSoftmaxOptions(builder_, softmax_beta).Union());
     52     BuildInterpreter({GetShape(input_)});
     53   }
     54 
     55  protected:
     56   int input_;
     57   int output_;
     58 };
     59 
     60 class FloatActivationsOpModel : public BaseActivationsOpModel {
     61  public:
     62   using BaseActivationsOpModel::BaseActivationsOpModel;
     63 
     64   void SetInput(std::initializer_list<float> data) {
     65     PopulateTensor(input_, data);
     66   }
     67   std::vector<float> GetOutput() { return ExtractVector<float>(output_); }
     68 };
     69 
     70 // TODO(ahentz): I don't quite understand the tradeoffs in the quantized
     71 // implementation of sigmoid and software, but a tolerance of twice the output
     72 // scale seems reasonable. We might want to change this if we have a better
     73 // theoretical bound.
     74 const float kQuantizedTolerance = 2 * (1. / 256);
     75 
     76 class QuantizedActivationsOpModel : public BaseActivationsOpModel {
     77  public:
     78   using BaseActivationsOpModel::BaseActivationsOpModel;
     79 
     80   void SetInput(std::initializer_list<float> data) {
     81     QuantizeAndPopulate<uint8_t>(input_, data);
     82   }
     83   std::vector<uint8_t> GetOutput() { return ExtractVector<uint8_t>(output_); }
     84   std::vector<float> GetDequantizedOutput() {
     85     return Dequantize<uint8_t>(ExtractVector<uint8_t>(output_),
     86                                GetScale(output_), GetZeroPoint(output_));
     87   }
     88 };
     89 
     90 TEST(FloatActivationsOpTest, Relu) {
     91   FloatActivationsOpModel m(BuiltinOperator_RELU,
     92                             /*input=*/{TensorType_FLOAT32, {1, 2, 4, 1}});
     93   m.SetInput({
     94       0, -6, 2, 4,   //
     95       3, -2, 10, 1,  //
     96   });
     97   m.Invoke();
     98   EXPECT_THAT(m.GetOutput(), ElementsAreArray({
     99                                  0, 0, 2, 4,   //
    100                                  3, 0, 10, 1,  //
    101                              }));
    102 }
    103 
    104 TEST(FloatActivationsOpTest, Relu1) {
    105   FloatActivationsOpModel m(BuiltinOperator_RELU_N1_TO_1,
    106                             /*input=*/{TensorType_FLOAT32, {1, 2, 4, 1}});
    107   m.SetInput({
    108       0.0, -0.6, 0.2, -0.4,  //
    109       0.3, -2.0, 1.1, -0.1,  //
    110   });
    111   m.Invoke();
    112   EXPECT_THAT(m.GetOutput(), ElementsAreArray({
    113                                  0.0, -0.6, 0.2, -0.4,  //
    114                                  0.3, -1.0, 1.0, -0.1,  //
    115                              }));
    116 }
    117 
    118 TEST(FloatActivationsOpTest, Relu6) {
    119   FloatActivationsOpModel m(BuiltinOperator_RELU6,
    120                             /*input=*/{TensorType_FLOAT32, {1, 2, 4, 1}});
    121   m.SetInput({
    122       0, -6, 2, 4,   //
    123       3, -2, 10, 1,  //
    124   });
    125   m.Invoke();
    126   EXPECT_THAT(m.GetOutput(), ElementsAreArray({
    127                                  0, 0, 2, 4,  //
    128                                  3, 0, 6, 1,  //
    129                              }));
    130 }
    131 
    132 TEST(FloatActivationsOpTest, Tanh) {
    133   FloatActivationsOpModel m(BuiltinOperator_TANH,
    134                             /*input=*/{TensorType_FLOAT32, {1, 2, 4, 1}});
    135   m.SetInput({
    136       0, -6, 2, 4,   //
    137       3, -2, 10, 1,  //
    138   });
    139   m.Invoke();
    140   EXPECT_THAT(m.GetOutput(), ElementsAreArray(ArrayFloatNear({
    141                                  0, -0.9999877, 0.9640275, 0.999329,    //
    142                                  0.99505475, -0.9640275, 1, 0.7615941,  //
    143                              })));
    144 }
    145 
    146 TEST(FloatActivationsOpTest, Sigmoid) {
    147   FloatActivationsOpModel m(BuiltinOperator_LOGISTIC,
    148                             /*input=*/{TensorType_FLOAT32, {1, 2, 4, 1}});
    149   m.SetInput({
    150       0, -6, 2, 4,   //
    151       3, -2, 10, 1,  //
    152   });
    153   m.Invoke();
    154   EXPECT_THAT(m.GetOutput(), ElementsAreArray(ArrayFloatNear({
    155                                  0.5, 0.002473, 0.880797, 0.982014,       //
    156                                  0.952574, 0.119203, 0.999955, 0.731059,  //
    157                              })));
    158 }
    159 
    160 TEST(QuantizedActivationsOpTest, Sigmoid) {
    161   QuantizedActivationsOpModel m(
    162       BuiltinOperator_LOGISTIC,
    163       /*input=*/{TensorType_UINT8, {1, 2, 4, 1}, -10, 10});
    164   m.SetInput({
    165       0, -6, 2, 4,   //
    166       3, -2, 10, 1,  //
    167   });
    168   m.Invoke();
    169   EXPECT_THAT(m.GetDequantizedOutput(),
    170               ElementsAreArray(ArrayFloatNear(
    171                   {
    172                       0.5, 0.002473, 0.880797, 0.982014,       //
    173                       0.952574, 0.119203, 0.999955, 0.731059,  //
    174                   },
    175                   kQuantizedTolerance)));
    176   EXPECT_THAT(m.GetOutput(),
    177               ElementsAreArray({128, 1, 227, 251, 244, 32, 255, 188}));
    178 }
    179 
    180 TEST(FloatActivationsOpTest, Softmax4D) {
    181   FloatActivationsOpModel m(0.1,
    182                             /*input=*/{TensorType_FLOAT32, {1, 2, 1, 4}});
    183   m.SetInput({
    184       0, -6, 2, 4,   // depth = 0
    185       3, -2, 10, 1,  // depth = 1
    186   });
    187   m.Invoke();
    188   EXPECT_THAT(m.GetOutput(), ElementsAreArray(ArrayFloatNear({
    189                                  .23463, .12877, .28658, .35003,  //
    190                                  .22528, .13664, .45365, .18443,  //
    191                              })));
    192 
    193   // Same input, but a different shape.
    194   FloatActivationsOpModel m2(0.1,
    195                              /*input=*/{TensorType_FLOAT32, {4, 1, 1, 2}});
    196   m2.SetInput({
    197       0, -6,  //
    198       2, 4,   //
    199       3, -2,  //
    200       10, 1,  //
    201   });
    202   m2.Invoke();
    203   EXPECT_THAT(m2.GetOutput(), ElementsAreArray(ArrayFloatNear({
    204                                   0.645656, 0.354344,  //
    205                                   0.450166, 0.549834,  //
    206                                   0.622459, 0.377541,  //
    207                                   0.710949, 0.28905,   //
    208                               })));
    209 }
    210 
    211 TEST(QuantizedActivationsOpTest, Softmax4D) {
    212   QuantizedActivationsOpModel m(
    213       0.1,
    214       /*input=*/{TensorType_UINT8, {1, 2, 1, 4}, -10, 10});
    215   m.SetInput({
    216       0, -6, 2, 4,   // depth = 0
    217       3, -2, 10, 1,  // depth = 1
    218   });
    219   m.Invoke();
    220   EXPECT_THAT(m.GetDequantizedOutput(),
    221               ElementsAreArray(ArrayFloatNear(
    222                   {
    223                       .23463, .12877, .28658, .35003,  //
    224                       .22528, .13664, .45365, .18443,  //
    225                   },
    226                   kQuantizedTolerance)));
    227 
    228   // Same input, but a different shape.
    229   QuantizedActivationsOpModel m2(
    230       0.1,
    231       /*input=*/{TensorType_UINT8, {4, 1, 1, 2}, -10, 10});
    232   m2.SetInput({
    233       0, -6,  //
    234       2, 4,   //
    235       3, -2,  //
    236       10, 1,  //
    237   });
    238   m2.Invoke();
    239   EXPECT_THAT(m2.GetDequantizedOutput(), ElementsAreArray(ArrayFloatNear(
    240                                              {
    241                                                  0.645656, 0.354344,  //
    242                                                  0.450166, 0.549834,  //
    243                                                  0.622459, 0.377541,  //
    244                                                  0.710949, 0.28905,   //
    245                                              },
    246                                              kQuantizedTolerance)));
    247 }
    248 
    249 TEST(FloatActivationsOpTest, Softmax2D) {
    250   FloatActivationsOpModel m(0.1,
    251                             /*input=*/{TensorType_FLOAT32, {2, 4}});
    252   m.SetInput({
    253       0, -6, 2, 4,   //
    254       3, -2, 10, 1,  //
    255   });
    256   m.Invoke();
    257   EXPECT_THAT(m.GetOutput(), ElementsAreArray(ArrayFloatNear({
    258                                  .23463, .12877, .28658, .35003,  //
    259                                  .22528, .13664, .45365, .18443,  //
    260                              })));
    261 
    262   // Same input, but a different shape.
    263   FloatActivationsOpModel m2(0.1,
    264                              /*input=*/{TensorType_FLOAT32, {4, 2}});
    265   m2.SetInput({
    266       0, -6,  //
    267       2, 4,   //
    268       3, -2,  //
    269       10, 1,  //
    270   });
    271   m2.Invoke();
    272   EXPECT_THAT(m2.GetOutput(), ElementsAreArray(ArrayFloatNear({
    273                                   0.645656, 0.354344,  //
    274                                   0.450166, 0.549834,  //
    275                                   0.622459, 0.377541,  //
    276                                   0.710949, 0.28905,   //
    277                               })));
    278 }
    279 
    280 TEST(QuantizedActivationsOpTest, Softmax2D) {
    281   QuantizedActivationsOpModel m(0.1,
    282                                 /*input=*/{TensorType_UINT8, {2, 4}, -10, 10});
    283   m.SetInput({
    284       0, -6, 2, 4,   //
    285       3, -2, 10, 1,  //
    286   });
    287   m.Invoke();
    288   EXPECT_THAT(m.GetDequantizedOutput(),
    289               ElementsAreArray(ArrayFloatNear(
    290                   {
    291                       .23463, .12877, .28658, .35003,  //
    292                       .22528, .13664, .45365, .18443,  //
    293                   },
    294                   kQuantizedTolerance)));
    295 
    296   // Same input, but a different shape.
    297   QuantizedActivationsOpModel m2(0.1,
    298                                  /*input=*/{TensorType_UINT8, {4, 2}, -10, 10});
    299   m2.SetInput({
    300       0, -6,  //
    301       2, 4,   //
    302       3, -2,  //
    303       10, 1,  //
    304   });
    305   m2.Invoke();
    306   EXPECT_THAT(m2.GetDequantizedOutput(), ElementsAreArray(ArrayFloatNear(
    307                                              {
    308                                                  0.645656, 0.354344,  //
    309                                                  0.450166, 0.549834,  //
    310                                                  0.622459, 0.377541,  //
    311                                                  0.710949, 0.28905,   //
    312                                              },
    313                                              kQuantizedTolerance)));
    314 }
    315 
    316 }  // namespace
    317 }  // namespace tflite
    318 
    319 int main(int argc, char** argv) {
    320   ::tflite::LogToStderr();
    321   ::testing::InitGoogleTest(&argc, argv);
    322   return RUN_ALL_TESTS();
    323 }
    324