• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* Copyright 2021 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/lite/c/builtin_op_data.h"
17 #include "tensorflow/lite/c/common.h"
18 #include "tensorflow/lite/kernels/internal/common.h"
19 #include "tensorflow/lite/kernels/internal/quantization_util.h"
20 #include "tensorflow/lite/kernels/internal/reference/conv.h"
21 #include "tensorflow/lite/kernels/internal/reference/integer_ops/conv.h"
22 #include "tensorflow/lite/kernels/internal/tensor_ctypes.h"
23 #include "tensorflow/lite/kernels/kernel_util.h"
24 #include "tensorflow/lite/kernels/padding.h"
25 #include "tensorflow/lite/micro/kernels/conv.h"
26 #include "tensorflow/lite/micro/kernels/kernel_util.h"
27 
28 namespace tflite {
29 
30 const int kConvInputTensor = 0;
31 const int kConvWeightsTensor = 1;
32 const int kConvBiasTensor = 2;
33 const int kConvOutputTensor = 0;
34 
35 // Conv is quantized along dimension 0:
36 // https://www.tensorflow.org/lite/performance/quantization_spec
37 const int kConvQuantizedDimension = 0;
38 
39 // Returns a ConvParams struct with all the parameters needed for a
40 // float computation.
ConvParamsFloat(const TfLiteConvParams & params,const OpDataConv & data)41 ConvParams ConvParamsFloat(const TfLiteConvParams& params,
42                            const OpDataConv& data) {
43   ConvParams op_params;
44   CalculateActivationRange(params.activation, &op_params.float_activation_min,
45                            &op_params.float_activation_max);
46   op_params.padding_type = tflite::micro::RuntimePaddingType(params.padding);
47   op_params.padding_values.width = data.padding.width;
48   op_params.padding_values.height = data.padding.height;
49   op_params.stride_width = params.stride_width;
50   op_params.stride_height = params.stride_height;
51   op_params.dilation_width_factor = params.dilation_width_factor;
52   op_params.dilation_height_factor = params.dilation_height_factor;
53   return op_params;
54 }
55 
56 // Returns a ConvParams struct with all the parameters needed for a
57 // quantized computation.
ConvParamsQuantized(const TfLiteConvParams & params,const OpDataConv & data)58 ConvParams ConvParamsQuantized(const TfLiteConvParams& params,
59                                const OpDataConv& data) {
60   ConvParams op_params;
61   op_params.input_offset = -data.input_zero_point;
62   op_params.weights_offset = -data.filter_zero_point;
63   op_params.output_offset = data.output_zero_point;
64   op_params.output_multiplier = data.output_multiplier;
65   op_params.output_shift = -data.output_shift;
66   op_params.padding_type = tflite::micro::RuntimePaddingType(params.padding);
67   op_params.padding_values.height = data.padding.height;
68   op_params.padding_values.width = data.padding.width;
69   op_params.stride_height = params.stride_height;
70   op_params.stride_width = params.stride_width;
71   op_params.dilation_height_factor = params.dilation_height_factor;
72   op_params.dilation_width_factor = params.dilation_width_factor;
73   op_params.quantized_activation_min = data.output_activation_min;
74   op_params.quantized_activation_max = data.output_activation_max;
75   return op_params;
76 }
77 
CalculateOpDataConv(TfLiteContext * context,TfLiteNode * node,const TfLiteConvParams & params,int width,int height,int filter_width,int filter_height,int out_width,int out_height,const TfLiteType data_type,OpDataConv * data)78 TfLiteStatus CalculateOpDataConv(TfLiteContext* context, TfLiteNode* node,
79                                  const TfLiteConvParams& params, int width,
80                                  int height, int filter_width,
81                                  int filter_height, int out_width,
82                                  int out_height, const TfLiteType data_type,
83                                  OpDataConv* data) {
84   bool has_bias = node->inputs->size == 3;
85   // Check number of inputs/outputs
86   TF_LITE_ENSURE(context, has_bias || node->inputs->size == 2);
87   TF_LITE_ENSURE_EQ(context, node->outputs->size, 1);
88 
89   // Matching GetWindowedOutputSize in TensorFlow.
90   auto padding = params.padding;
91   data->padding = ComputePaddingHeightWidth(
92       params.stride_height, params.stride_width, params.dilation_height_factor,
93       params.dilation_width_factor, height, width, filter_height, filter_width,
94       padding, &out_height, &out_width);
95 
96   const TfLiteTensor* input = GetInput(context, node, kConvInputTensor);
97   TF_LITE_ENSURE(context, input != nullptr);
98   const TfLiteTensor* filter = GetInput(context, node, kConvWeightsTensor);
99   TF_LITE_ENSURE(context, filter != nullptr);
100   const TfLiteTensor* bias =
101       GetOptionalInputTensor(context, node, kConvBiasTensor);
102   TfLiteTensor* output = GetOutput(context, node, kConvOutputTensor);
103   TF_LITE_ENSURE(context, output != nullptr);
104 
105   // Note that quantized inference requires that all tensors have their
106   // parameters set. This is usually done during quantized training.
107   if (data_type != kTfLiteFloat32) {
108     int output_channels = filter->dims->data[kConvQuantizedDimension];
109 
110     TF_LITE_ENSURE_STATUS(tflite::PopulateConvolutionQuantizationParams(
111         context, input, filter, bias, output, params.activation,
112         &data->output_multiplier, &data->output_shift,
113         &data->output_activation_min, &data->output_activation_max,
114         data->per_channel_output_multiplier,
115         reinterpret_cast<int*>(data->per_channel_output_shift),
116         output_channels));
117   }
118 
119   data->input_zero_point = input->params.zero_point;
120   data->filter_zero_point = filter->params.zero_point;
121   data->output_zero_point = output->params.zero_point;
122 
123   return kTfLiteOk;
124 }
125 
ConvPrepare(TfLiteContext * context,TfLiteNode * node)126 TfLiteStatus ConvPrepare(TfLiteContext* context, TfLiteNode* node) {
127   TFLITE_DCHECK(node->user_data != nullptr);
128   TFLITE_DCHECK(node->builtin_data != nullptr);
129 
130   OpDataConv* data = static_cast<OpDataConv*>(node->user_data);
131   const auto& params =
132       *(static_cast<const TfLiteConvParams*>(node->builtin_data));
133 
134   TfLiteTensor* output = GetOutput(context, node, kConvOutputTensor);
135   TF_LITE_ENSURE(context, output != nullptr);
136   const TfLiteTensor* input = GetInput(context, node, kConvInputTensor);
137   TF_LITE_ENSURE(context, input != nullptr);
138   const TfLiteTensor* filter = GetInput(context, node, kConvWeightsTensor);
139   TF_LITE_ENSURE(context, filter != nullptr);
140 
141   const int input_width = input->dims->data[2];
142   const int input_height = input->dims->data[1];
143   const int filter_width = filter->dims->data[2];
144   const int filter_height = filter->dims->data[1];
145   const int output_width = output->dims->data[2];
146   const int output_height = output->dims->data[1];
147 
148   // Dynamically allocate per-channel quantization parameters.
149   const int num_channels = filter->dims->data[kConvQuantizedDimension];
150   data->per_channel_output_multiplier =
151       static_cast<int32_t*>(context->AllocatePersistentBuffer(
152           context, num_channels * sizeof(int32_t)));
153   data->per_channel_output_shift =
154       static_cast<int32_t*>(context->AllocatePersistentBuffer(
155           context, num_channels * sizeof(int32_t)));
156 
157   // All per-channel quantized tensors need valid zero point and scale arrays.
158   if (input->type == kTfLiteInt8) {
159     TF_LITE_ENSURE_EQ(context, filter->quantization.type,
160                       kTfLiteAffineQuantization);
161 
162     const auto* affine_quantization =
163         static_cast<TfLiteAffineQuantization*>(filter->quantization.params);
164     TFLITE_DCHECK(affine_quantization != nullptr);
165     TFLITE_DCHECK(affine_quantization->scale != nullptr);
166     TFLITE_DCHECK(affine_quantization->zero_point != nullptr);
167 
168     TF_LITE_ENSURE(context,
169                    affine_quantization->scale->size == 1 ||
170                        affine_quantization->scale->size ==
171                            filter->dims->data[kConvQuantizedDimension]);
172     TF_LITE_ENSURE_EQ(context, affine_quantization->scale->size,
173                       affine_quantization->zero_point->size);
174   }
175 
176   TF_LITE_ENSURE_STATUS(CalculateOpDataConv(
177       context, node, params, input_width, input_height, filter_width,
178       filter_height, output_width, output_height, input->type, data));
179 
180   return kTfLiteOk;
181 }
182 }  // namespace tflite
183