Daniel Konegen / MNIST_example

Dependencies:   mbed-os

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers kernel_util.cc Source File

kernel_util.cc

00001 /* Copyright 2017 The TensorFlow Authors. All Rights Reserved.
00002 
00003 Licensed under the Apache License, Version 2.0 (the "License");
00004 you may not use this file except in compliance with the License.
00005 You may obtain a copy of the License at
00006 
00007     http://www.apache.org/licenses/LICENSE-2.0
00008 
00009 Unless required by applicable law or agreed to in writing, software
00010 distributed under the License is distributed on an "AS IS" BASIS,
00011 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00012 See the License for the specific language governing permissions and
00013 limitations under the License.
00014 ==============================================================================*/
00015 #include "tensorflow/lite/kernels/kernel_util.h"
00016 
00017 #include <algorithm>
00018 #include <cmath>
00019 #include <memory>
00020 
00021 #include "tensorflow/lite/kernels/internal/quantization_util.h"
00022 #include "tensorflow/lite/kernels/internal/round.h"
00023 
00024 namespace tflite {
00025 
00026 TfLiteStatus PopulateConvolutionQuantizationParams(
00027     TfLiteContext* context, const TfLiteTensor* input,
00028     const TfLiteTensor* filter, const TfLiteTensor* bias, TfLiteTensor* output,
00029     const TfLiteFusedActivation& activation, int32_t* multiplier, int* shift,
00030     int32_t* output_activation_min, int32_t* output_activation_max,
00031     int32_t* per_channel_multiplier, int* per_channel_shift) {
00032   TF_LITE_ENSURE_EQ(context, input->quantization.type,
00033                     kTfLiteAffineQuantization);
00034   TF_LITE_ENSURE_EQ(context, filter->quantization.type,
00035                     kTfLiteAffineQuantization);
00036   // TODO(jianlijianli): Enable bias type check and bias scale == input scale
00037   // * filter scale for each channel in affine quantization once bias
00038   // quantization is properly populated.
00039   // TF_LITE_ENSURE_EQ(context, bias->quantization.type,
00040   // kTfLiteAffineQuantization);
00041 
00042   // Check data type.
00043   const auto* affine_quantization =
00044       reinterpret_cast<TfLiteAffineQuantization*>(filter->quantization.params);
00045   TF_LITE_ENSURE(context, affine_quantization);
00046   TF_LITE_ENSURE(context, affine_quantization->scale);
00047   const bool is_per_channel = affine_quantization->scale->size > 1;
00048   if (is_per_channel) {
00049     //  Currently only Int8 is supported for per channel quantization.
00050     TF_LITE_ENSURE_EQ(context, input->type, kTfLiteInt8);
00051     TF_LITE_ENSURE_EQ(context, filter->type, kTfLiteInt8);
00052     TF_LITE_ENSURE_EQ(
00053         context, affine_quantization->scale->size,
00054         filter->dims->data[affine_quantization->quantized_dimension]);
00055   }
00056 
00057   // Populate multiplier and shift using affine quantization.
00058   const int num_channels = affine_quantization->scale->size;
00059   const float input_scale = input->params.scale;
00060   const float output_scale = output->params.scale;
00061   const float* filter_scales = affine_quantization->scale->data;
00062   for (int i = 0; i < num_channels; ++i) {
00063     const double filter_scale = static_cast<double>(filter_scales[i]);
00064     const double effective_output_scale = static_cast<double>(input_scale) *
00065                                           filter_scale /
00066                                           static_cast<double>(output_scale);
00067     int32_t significand;
00068     int shift;
00069     QuantizeMultiplier(effective_output_scale, &significand, &shift);
00070     per_channel_multiplier[i] = significand;
00071     per_channel_shift[i] = shift;
00072   }
00073 
00074   // Populate scalar quantization parameters.
00075   // This check on legacy quantization parameters is kept only for backward
00076   // compatibility.
00077   if (input->type == kTfLiteUInt8) {
00078     // Check bias scale == input scale * filter scale.
00079     double real_multiplier = 0.0;
00080     TF_LITE_ENSURE_STATUS(GetQuantizedConvolutionMultipler(
00081         context, input, filter, bias, output, &real_multiplier));
00082     int exponent;
00083 
00084     // Populate quantization parameteters with multiplier and shift.
00085     QuantizeMultiplier(real_multiplier, multiplier, &exponent);
00086     *shift = -exponent;
00087     CalculateActivationRangeUint8(activation, output, output_activation_min,
00088                                   output_activation_max);
00089   }
00090   return kTfLiteOk;
00091 }
00092 
00093 TfLiteStatus GetQuantizedConvolutionMultipler(TfLiteContext* context,
00094                                               const TfLiteTensor* input,
00095                                               const TfLiteTensor* filter,
00096                                               const TfLiteTensor* bias,
00097                                               TfLiteTensor* output,
00098                                               double* multiplier) {
00099   const double input_product_scale = input->params.scale * filter->params.scale;
00100   // TODO(ahentz): The following conditions must be guaranteed by the training
00101   // pipeline.
00102   if (bias) {
00103     const double bias_scale = bias->params.scale;
00104     TF_LITE_ENSURE(context,
00105                    std::abs(input_product_scale - bias_scale) <=
00106                        1e-6 * std::min(input_product_scale, bias_scale));
00107   }
00108   return GetQuantizedConvolutionMultipler(context, input, filter, output,
00109                                           multiplier);
00110 }
00111 
00112 TfLiteStatus GetQuantizedConvolutionMultipler(TfLiteContext* context,
00113                                               const TfLiteTensor* input,
00114                                               const TfLiteTensor* filter,
00115                                               TfLiteTensor* output,
00116                                               double* multiplier) {
00117   const double input_product_scale = input->params.scale * filter->params.scale;
00118   TF_LITE_ENSURE(context, input_product_scale >= 0);
00119   *multiplier = input_product_scale / output->params.scale;
00120 
00121   return kTfLiteOk;
00122 }
00123 
00124 namespace {
00125 void CalculateActivationRangeQuantizedImpl(TfLiteFusedActivation activation,
00126                                            int32_t qmin, int32_t qmax,
00127                                            TfLiteTensor* output,
00128                                            int32_t* act_min, int32_t* act_max) {
00129   const auto scale = output->params.scale;
00130   const auto zero_point = output->params.zero_point;
00131 
00132   auto quantize = [scale, zero_point](float f) {
00133     return zero_point + static_cast<int32_t>(TfLiteRound(f / scale));
00134   };
00135 
00136   if (activation == kTfLiteActRelu) {
00137     *act_min = std::max(qmin, quantize(0.0));
00138     *act_max = qmax;
00139   } else if (activation == kTfLiteActRelu6) {
00140     *act_min = std::max(qmin, quantize(0.0));
00141     *act_max = std::min(qmax, quantize(6.0));
00142   } else if (activation == kTfLiteActRelu1) {
00143     *act_min = std::max(qmin, quantize(-1.0));
00144     *act_max = std::min(qmax, quantize(1.0));
00145   } else {
00146     *act_min = qmin;
00147     *act_max = qmax;
00148   }
00149 }
00150 }  // namespace
00151 
00152 TfLiteStatus CalculateActivationRangeQuantized(TfLiteContext* context,
00153                                                TfLiteFusedActivation activation,
00154                                                TfLiteTensor* output,
00155                                                int32_t* act_min,
00156                                                int32_t* act_max) {
00157   int32_t qmin = 0;
00158   int32_t qmax = 0;
00159   if (output->type == kTfLiteUInt8) {
00160     qmin = std::numeric_limits<uint8_t>::min();
00161     qmax = std::numeric_limits<uint8_t>::max();
00162   } else if (output->type == kTfLiteInt8) {
00163     qmin = std::numeric_limits<int8_t>::min();
00164     qmax = std::numeric_limits<int8_t>::max();
00165   } else if (output->type == kTfLiteInt16) {
00166     qmin = std::numeric_limits<int16_t>::min();
00167     qmax = std::numeric_limits<int16_t>::max();
00168   } else {
00169     TF_LITE_ENSURE(context, false);
00170   }
00171 
00172   CalculateActivationRangeQuantizedImpl(activation, qmin, qmax, output, act_min,
00173                                         act_max);
00174   return kTfLiteOk;
00175 }
00176 
00177 void CalculateActivationRangeUint8(TfLiteFusedActivation activation,
00178                                    TfLiteTensor* output, int32_t* act_min,
00179                                    int32_t* act_max) {
00180   const int32_t qmin = std::numeric_limits<uint8_t>::min();
00181   const int32_t qmax = std::numeric_limits<uint8_t>::max();
00182 
00183   CalculateActivationRangeQuantizedImpl(activation, qmin, qmax, output, act_min,
00184                                         act_max);
00185 }
00186 
00187 void CalculateActivationRangeInt8(TfLiteFusedActivation activation,
00188                                   TfLiteTensor* output, int32_t* act_min,
00189                                   int32_t* act_max) {
00190   const int32_t qmin = std::numeric_limits<int8_t>::min();
00191   const int32_t qmax = std::numeric_limits<int8_t>::max();
00192 
00193   CalculateActivationRangeQuantizedImpl(activation, qmin, qmax, output, act_min,
00194                                         act_max);
00195 }
00196 
00197 bool HaveSameShapes(const TfLiteTensor* input1, const TfLiteTensor* input2) {
00198   return TfLiteIntArrayEqual(input1->dims, input2->dims);
00199 }
00200 
00201 // TODO(petewarden): Having macros around this is ugly, look at other strategies
00202 // before replicating this approach elsewhere.
00203 #ifndef TF_LITE_STATIC_MEMORY
00204 TfLiteStatus CalculateShapeForBroadcast(TfLiteContext* context,
00205                                         const TfLiteTensor* input1,
00206                                         const TfLiteTensor* input2,
00207                                         TfLiteIntArray** output_shape) {
00208   int64_t dims1 = NumDimensions(input1);
00209   int64_t dims2 = NumDimensions(input2);
00210   int64_t out_dims = std::max(dims1, dims2);
00211   if (NumElements(input1) == 0) {
00212     *output_shape = TfLiteIntArrayCopy(input1->dims);
00213     return kTfLiteOk;
00214   }
00215   std::unique_ptr<TfLiteIntArray, void (*)(TfLiteIntArray*)> shape(
00216       TfLiteIntArrayCreate(out_dims), TfLiteIntArrayFree);
00217   for (int i = 0; i < out_dims; ++i) {
00218     int64_t d1 = i >= dims1 ? 1 : SizeOfDimension(input1, dims1 - i - 1);
00219     int64_t d2 = i >= dims2 ? 1 : SizeOfDimension(input2, dims2 - i - 1);
00220     TF_LITE_ENSURE(context, d1 == d2 || d1 == 1 || d2 == 1);
00221     shape->data[out_dims - i - 1] = std::max(d1, d2);
00222   }
00223   *output_shape = shape.release();
00224   return kTfLiteOk;
00225 }
00226 #endif  // TF_LITE_STATIC_MEMORY
00227 
00228 }  // namespace tflite