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