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 <unistd.h>
     16 #include <cassert>
     17 #include <cmath>
     18 #include <cstdio>
     19 #include <cstdlib>
     20 #include <iostream>
     21 #include <limits>
     22 
     23 #include "tensorflow/contrib/lite/builtin_op_data.h"
     24 #include "tensorflow/contrib/lite/context.h"
     25 #include "tensorflow/contrib/lite/kernels/internal/optimized/optimized_ops.h"
     26 #include "tensorflow/contrib/lite/kernels/internal/quantization_util.h"
     27 #include "tensorflow/contrib/lite/kernels/internal/reference/reference_ops.h"
     28 #include "tensorflow/contrib/lite/kernels/internal/tensor.h"
     29 #include "tensorflow/contrib/lite/kernels/kernel_util.h"
     30 #include "tensorflow/contrib/lite/kernels/op_macros.h"
     31 
     32 namespace tflite {
     33 namespace ops {
     34 namespace builtin {
     35 namespace activations {
     36 
     37 struct OpData {
     38   int32_t input_multiplier = 0;
     39   int input_left_shift = 0;
     40   int32_t input_range_radius = 0;
     41   int diff_min = 0;
     42 };
     43 
     44 void* Init(TfLiteContext* context, const char* buffer, size_t length) {
     45   // This is a builtin op, so we don't use the contents in 'buffer', if any.
     46   // Instead, we allocate a new object to carry information from Prepare() to
     47   // Eval().
     48   return new OpData;
     49 }
     50 
     51 void Free(TfLiteContext* context, void* buffer) {
     52   delete reinterpret_cast<OpData*>(buffer);
     53 }
     54 
     55 TfLiteStatus GenericPrepare(TfLiteContext* context, TfLiteNode* node) {
     56   TF_LITE_ENSURE_EQ(context, NumInputs(node), 1);
     57   TF_LITE_ENSURE_EQ(context, NumOutputs(node), 1);
     58   TfLiteTensor* input = GetInput(context, node, 0);
     59   TfLiteTensor* output = GetOutput(context, node, 0);
     60   TF_LITE_ENSURE_EQ(context, input->type, output->type);
     61 
     62   return context->ResizeTensor(context, output,
     63                                TfLiteIntArrayCopy(input->dims));
     64 }
     65 
     66 TfLiteStatus SigmoidPrepare(TfLiteContext* context, TfLiteNode* node) {
     67   OpData* data = reinterpret_cast<OpData*>(node->user_data);
     68 
     69   TF_LITE_ENSURE_EQ(context, NumInputs(node), 1);
     70   TF_LITE_ENSURE_EQ(context, NumOutputs(node), 1);
     71   TfLiteTensor* input = GetInput(context, node, 0);
     72   TfLiteTensor* output = GetOutput(context, node, 0);
     73   TF_LITE_ENSURE_EQ(context, input->type, output->type);
     74 
     75   if (input->type == kTfLiteUInt8) {
     76     TF_LITE_ENSURE_EQ(context, output->params.zero_point, 0);
     77     TF_LITE_ENSURE(context, output->params.scale == 1. / 256);
     78 
     79     static constexpr int kInputIntegerBits = 4;
     80 
     81     const double input_real_multiplier =
     82         input->params.scale *
     83         static_cast<double>(1 << (31 - kInputIntegerBits));
     84 
     85     QuantizeMultiplierGreaterThanOne(input_real_multiplier,
     86                                      &data->input_multiplier,
     87                                      &data->input_left_shift);
     88     data->input_range_radius =
     89         CalculateInputRadius(kInputIntegerBits, data->input_left_shift);
     90   }
     91 
     92   return context->ResizeTensor(context, output,
     93                                TfLiteIntArrayCopy(input->dims));
     94 }
     95 
     96 TfLiteStatus SoftmaxPrepare(TfLiteContext* context, TfLiteNode* node) {
     97   auto* params = reinterpret_cast<TfLiteSoftmaxParams*>(node->builtin_data);
     98   OpData* data = reinterpret_cast<OpData*>(node->user_data);
     99 
    100   TF_LITE_ENSURE_EQ(context, NumInputs(node), 1);
    101   TF_LITE_ENSURE_EQ(context, NumOutputs(node), 1);
    102   TfLiteTensor* input = GetInput(context, node, 0);
    103   TfLiteTensor* output = GetOutput(context, node, 0);
    104   TF_LITE_ENSURE_EQ(context, input->type, output->type);
    105 
    106   TF_LITE_ENSURE(context,
    107                  NumDimensions(input) == 2 || NumDimensions(input) == 4);
    108 
    109   if (input->type == kTfLiteUInt8) {
    110     TF_LITE_ENSURE_EQ(context, output->params.zero_point, 0);
    111     TF_LITE_ENSURE(context, output->params.scale == 1. / 256);
    112 
    113     static const int kScaledDiffIntegerBits = 5;
    114 
    115     tflite::PreprocessSoftmaxScaling(
    116         params->beta, input->params.scale, kScaledDiffIntegerBits,
    117         &data->input_multiplier, &data->input_left_shift);
    118     data->diff_min = -1.0 * tflite::CalculateInputRadius(
    119                                 kScaledDiffIntegerBits, data->input_left_shift);
    120   }
    121 
    122   return context->ResizeTensor(context, output,
    123                                TfLiteIntArrayCopy(input->dims));
    124 }
    125 
    126 TfLiteStatus ReluEval(TfLiteContext* context, TfLiteNode* node) {
    127   TfLiteTensor* input = GetInput(context, node, 0);
    128   TfLiteTensor* output = GetOutput(context, node, 0);
    129   switch (input->type) {
    130     case kTfLiteFloat32: {
    131       size_t elements = input->bytes / sizeof(float);
    132       float* in = input->data.f;
    133       float* in_end = in + elements;
    134       float* out = output->data.f;
    135       for (; in < in_end; in++, out++) *out = std::max(0.f, *in);
    136       return kTfLiteOk;
    137     } break;
    138     default:
    139       context->ReportError(context, "Only float32 supported currently.");
    140       return kTfLiteError;
    141   }
    142 }
    143 
    144 TfLiteStatus Relu1Eval(TfLiteContext* context, TfLiteNode* node) {
    145   TfLiteTensor* input = GetInput(context, node, 0);
    146   TfLiteTensor* output = GetOutput(context, node, 0);
    147   switch (input->type) {
    148     case kTfLiteFloat32: {
    149       size_t elements = input->bytes / sizeof(float);
    150       float* in = input->data.f;
    151       float* in_end = in + elements;
    152       float* out = output->data.f;
    153       for (; in < in_end; in++, out++) {
    154         *out = std::min(std::max(-1.f, *in), 1.f);
    155       }
    156       return kTfLiteOk;
    157     } break;
    158     default:
    159       context->ReportError(context, "Only float32 supported currently.");
    160       return kTfLiteError;
    161   }
    162 }
    163 
    164 TfLiteStatus Relu6Eval(TfLiteContext* context, TfLiteNode* node) {
    165   TfLiteTensor* input = GetInput(context, node, 0);
    166   TfLiteTensor* output = GetOutput(context, node, 0);
    167   switch (input->type) {
    168     case kTfLiteFloat32: {
    169       size_t elements = input->bytes / sizeof(float);
    170       float* in = input->data.f;
    171       float* in_end = in + elements;
    172       float* out = output->data.f;
    173       for (; in < in_end; in++, out++) *out = std::min(std::max(0.f, *in), 6.f);
    174       return kTfLiteOk;
    175     } break;
    176     default:
    177       context->ReportError(context, "Only float32 supported currently.");
    178       return kTfLiteError;
    179   }
    180 }
    181 
    182 TfLiteStatus TanhEval(TfLiteContext* context, TfLiteNode* node) {
    183   TfLiteTensor* input = GetInput(context, node, 0);
    184   TfLiteTensor* output = GetOutput(context, node, 0);
    185   switch (input->type) {
    186     case kTfLiteFloat32: {
    187       size_t elements = input->bytes / sizeof(float);
    188       float* in = input->data.f;
    189       float* in_end = in + elements;
    190       float* out = output->data.f;
    191       for (; in < in_end; in++, out++) *out = std::tanh(*in);
    192       return kTfLiteOk;
    193     } break;
    194     default:
    195       context->ReportError(context, "Only float32 supported currently.");
    196       return kTfLiteError;
    197   }
    198 }
    199 
    200 // Sigmoid is also know as "Logistic".
    201 TfLiteStatus SigmoidEval(TfLiteContext* context, TfLiteNode* node) {
    202   OpData* data = reinterpret_cast<OpData*>(node->user_data);
    203 
    204   TfLiteTensor* input = GetInput(context, node, 0);
    205   TfLiteTensor* output = GetOutput(context, node, 0);
    206   switch (input->type) {
    207     case kTfLiteFloat32: {
    208       size_t elements = input->bytes / sizeof(float);
    209       float* in = input->data.f;
    210       float* in_end = in + elements;
    211       float* out = output->data.f;
    212       for (; in < in_end; in++, out++) *out = 1.f / (1.f + std::exp(-*in));
    213       break;
    214     }
    215     case kTfLiteUInt8: {
    216       optimized_ops::Logistic(
    217           GetTensorData<uint8_t>(input), GetTensorDims(input),
    218           input->params.zero_point, data->input_range_radius,
    219           data->input_multiplier, data->input_left_shift,
    220           GetTensorData<uint8_t>(output), GetTensorDims(output));
    221       break;
    222     }
    223     default:
    224       context->ReportError(context, "Only float32 supported currently.");
    225       return kTfLiteError;
    226   }
    227   return kTfLiteOk;
    228 }
    229 
    230 // Takes a 2D tensor and perform softmax along the second dimension.
    231 void Softmax2DFloat(TfLiteTensor* input, TfLiteTensor* output,
    232                     TfLiteSoftmaxParams* params) {
    233   const int batch_size = input->dims->data[0];
    234   const int input_size = input->dims->data[1];
    235   float* in = input->data.f;
    236   float* out = output->data.f;
    237   TF_LITE_ASSERT(input_size > 0);
    238 
    239   // For each batch
    240   for (int b = 0; b < batch_size; b++) {
    241     // Find the max coeff.
    242     float max_coeff = in[0];
    243     for (int i = 1; i < input_size; i++) {
    244       if (in[i] > max_coeff) max_coeff = in[i];
    245     }
    246 
    247     // Compute the normalized sum of exps.
    248     float exp_sum = 0.0;
    249     for (int i = 0; i < input_size; i++) {
    250       out[i] = std::exp((in[i] - max_coeff) * params->beta);
    251       exp_sum += out[i];
    252     }
    253 
    254     // Divide by the sum of exps.
    255     float reciprocal_sum_exp = 1.f / exp_sum;
    256     for (int i = 0; i < input_size; i++) {
    257       out[i] *= reciprocal_sum_exp;
    258     }
    259 
    260     // Advance in and out pointers for the next batch.
    261     in += input_size;
    262     out += input_size;
    263   }
    264 }
    265 
    266 void Softmax2DQuantized(TfLiteTensor* input, TfLiteTensor* output,
    267                         TfLiteSoftmaxParams* params, OpData* data) {
    268   // TODO(ahentz): this is arguably a dirty trick. Since the implementation
    269   // always traverses the last dimension of a 4D tensor, we will pretend our 2D
    270   // tensor is 4D in a special way. We will convert a (X, Y) shape into a (X,
    271   // 1, 1, Y) shape.
    272   const int batch_size = input->dims->data[0];
    273   const int input_size = input->dims->data[1];
    274   optimized_ops::Softmax(GetTensorData<uint8_t>(input),
    275                          GetTensorDims({batch_size, 1, 1, input_size}),
    276                          data->input_multiplier, data->input_left_shift,
    277                          data->diff_min, GetTensorData<uint8_t>(output),
    278                          GetTensorDims({batch_size, 1, 1, input_size}));
    279 }
    280 
    281 // Takes a 4D tensor and perform softmax along the forth dimension.
    282 void Softmax4DFloat(TfLiteTensor* input, TfLiteTensor* output,
    283                     TfLiteSoftmaxParams* params) {
    284   optimized_ops::Softmax(GetTensorData<float>(input), GetTensorDims(input),
    285                          params->beta, GetTensorData<float>(output),
    286                          GetTensorDims(output));
    287 }
    288 
    289 void Softmax4DQuantized(TfLiteTensor* input, TfLiteTensor* output,
    290                         TfLiteSoftmaxParams* params, OpData* data) {
    291   optimized_ops::Softmax(GetTensorData<uint8_t>(input), GetTensorDims(input),
    292                          data->input_multiplier, data->input_left_shift,
    293                          data->diff_min, GetTensorData<uint8_t>(output),
    294                          GetTensorDims(output));
    295 }
    296 
    297 TfLiteStatus SoftmaxEval(TfLiteContext* context, TfLiteNode* node) {
    298   auto* params = reinterpret_cast<TfLiteSoftmaxParams*>(node->builtin_data);
    299   OpData* data = reinterpret_cast<OpData*>(node->user_data);
    300 
    301   TfLiteTensor* input = GetInput(context, node, 0);
    302   TfLiteTensor* output = GetOutput(context, node, 0);
    303 
    304   // TODO(ahentz): consider an implementation that works for many (all?)
    305   // dimensions.
    306   switch (input->type) {
    307     case kTfLiteFloat32: {
    308       if (NumDimensions(input) == 2) {
    309         Softmax2DFloat(input, output, params);
    310         return kTfLiteOk;
    311       }
    312       if (NumDimensions(input) == 4) {
    313         Softmax4DFloat(input, output, params);
    314         return kTfLiteOk;
    315       }
    316       context->ReportError(context,
    317                            "Only 2D and 4D tensors supported currently.");
    318       return kTfLiteError;
    319     }
    320     case kTfLiteUInt8: {
    321       if (NumDimensions(input) == 2) {
    322         Softmax2DQuantized(input, output, params, data);
    323         return kTfLiteOk;
    324       }
    325       if (NumDimensions(input) == 4) {
    326         Softmax4DQuantized(input, output, params, data);
    327         return kTfLiteOk;
    328       }
    329       context->ReportError(context,
    330                            "Only 2D and 4D tensors supported currently.");
    331       return kTfLiteError;
    332     }
    333     default:
    334       context->ReportError(context,
    335                            "Only float32 and uint8_t supported currently.");
    336       return kTfLiteError;
    337   }
    338 }
    339 
    340 }  // namespace activations
    341 
    342 TfLiteRegistration* Register_RELU() {
    343   static TfLiteRegistration r = {/*init=*/nullptr, /*free=*/nullptr,
    344                                  activations::GenericPrepare,
    345                                  activations::ReluEval};
    346   return &r;
    347 }
    348 
    349 TfLiteRegistration* Register_RELU_N1_TO_1() {
    350   static TfLiteRegistration r = {/*init=*/nullptr, /*free=*/nullptr,
    351                                  activations::GenericPrepare,
    352                                  activations::Relu1Eval};
    353   return &r;
    354 }
    355 
    356 TfLiteRegistration* Register_RELU6() {
    357   static TfLiteRegistration r = {/*init=*/nullptr, /*free=*/nullptr,
    358                                  activations::GenericPrepare,
    359                                  activations::Relu6Eval};
    360   return &r;
    361 }
    362 
    363 TfLiteRegistration* Register_TANH() {
    364   static TfLiteRegistration r = {/*init=*/nullptr, /*free=*/nullptr,
    365                                  activations::GenericPrepare,
    366                                  activations::TanhEval};
    367   return &r;
    368 }
    369 
    370 TfLiteRegistration* Register_LOGISTIC() {
    371   static TfLiteRegistration r = {activations::Init, activations::Free,
    372                                  activations::SigmoidPrepare,
    373                                  activations::SigmoidEval};
    374   return &r;
    375 }
    376 
    377 TfLiteRegistration* Register_SOFTMAX() {
    378   static TfLiteRegistration r = {activations::Init, activations::Free,
    379                                  activations::SoftmaxPrepare,
    380                                  activations::SoftmaxEval};
    381   return &r;
    382 }
    383 
    384 }  // namespace builtin
    385 }  // namespace ops
    386 }  // namespace tflite
    387