4672fe406694234134c862123c7e97dabc7a46b2
[platform/core/ml/nnfw.git] / runtime / onert / backend / cpu / ops / ConvolutionLayer.cc
1 /*
2  * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #include "ConvolutionLayer.h"
18 #include "OperationUtils.h"
19
20 #include "../Tensor.h"
21 #include "ir/Padding.h"
22 #include <cker/operation/Conv.h>
23
24 namespace onert
25 {
26 namespace backend
27 {
28 namespace cpu
29 {
30 namespace ops
31 {
32 ConvolutionLayer::ConvolutionLayer()
33   : _input(nullptr), _kernel(nullptr), _bias(nullptr), _output(nullptr),
34     _paddingType(ir::PaddingType::EXPLICIT), _paddingLeft(0), _paddingTop(0), _paddingRight(0),
35     _paddingBottom(0), _strideWidth(0), _strideHeight(0), _dilationWidthFactor(1),
36     _dilationHeightFactor(1), _activation(ir::Activation::NONE),
37     _conv_kernel(new nnfw::cker::Conv()), _prepare(false)
38 {
39   // DO NOTHING
40 }
41
42 ConvolutionLayer::~ConvolutionLayer() = default;
43
44 void ConvolutionLayer::convFloat32()
45 {
46   float output_activation_min = 0, output_activation_max = 0;
47   CalculateActivationRange(_activation, &output_activation_min, &output_activation_max);
48
49   nnfw::cker::ConvParams op_params;
50   op_params.padding_type = getPaddingType(_paddingType);
51   op_params.padding_values.width = _paddingLeft;
52   op_params.padding_values.height = _paddingTop;
53   op_params.stride_width = _strideWidth;
54   op_params.stride_height = _strideHeight;
55   op_params.dilation_width_factor = _dilationWidthFactor;
56   op_params.dilation_height_factor = _dilationHeightFactor;
57   op_params.float_activation_min = output_activation_min;
58   op_params.float_activation_max = output_activation_max;
59
60   nnfw::cker::Conv &kernel = *_conv_kernel;
61   kernel(op_params, getShape(_input), getBuffer<float>(_input), getShape(_kernel),
62          getBuffer<float>(_kernel), getShape(_bias), getBuffer<float>(_bias), getShape(_output),
63          getBuffer<float>(_output));
64 }
65
66 void ConvolutionLayer::convQ8uPerTensor()
67 {
68   int32_t output_activation_min = 0;
69   int32_t output_activation_max = 0;
70   CalculateActivationRangeQuantized(_activation, _output, &output_activation_min,
71                                     &output_activation_max);
72
73   double real_multiplier = 0.0;
74   int32_t output_multiplier = 0;
75   int32_t output_shift = 0;
76   GetQuantizedConvolutionMultiplier(_input, _kernel, _bias, _output, &real_multiplier);
77   QuantizeMultiplier(real_multiplier, &output_multiplier, &output_shift);
78
79   nnfw::cker::ConvParams op_params;
80   op_params.stride_width = _strideWidth;
81   op_params.stride_height = _strideHeight;
82   op_params.dilation_width_factor = _dilationWidthFactor;
83   op_params.dilation_height_factor = _dilationHeightFactor;
84   op_params.padding_type = getPaddingType(_paddingType);
85   op_params.padding_values.width = _paddingLeft;
86   op_params.padding_values.height = _paddingTop;
87   op_params.input_offset = -_input->data_zero_point();
88   op_params.weights_offset = -_kernel->data_zero_point();
89   op_params.output_offset = _output->data_zero_point();
90   op_params.output_multiplier = output_multiplier;
91   op_params.output_shift = output_shift;
92   op_params.quantized_activation_min = output_activation_min;
93   op_params.quantized_activation_max = output_activation_max;
94   op_params.is_replaced_weights = true;
95
96   nnfw::cker::Conv &kernel = *_conv_kernel;
97   kernel(op_params, getShape(_input), getBuffer<uint8_t>(_input), getShape(_kernel),
98          getBuffer<uint8_t>(_kernel), getShape(_bias), getBuffer<int32_t>(_bias), getShape(_output),
99          getBuffer<uint8_t>(_output));
100 }
101
102 void ConvolutionLayer::convQ8uPerChannel()
103 {
104   nnfw::cker::ConvParams op_params;
105   op_params.padding_values.width = _paddingLeft;
106   op_params.padding_values.height = _paddingTop;
107   op_params.stride_width = _strideWidth;
108   op_params.stride_height = _strideHeight;
109   op_params.dilation_width_factor = _dilationWidthFactor;
110   op_params.dilation_height_factor = _dilationHeightFactor;
111   op_params.input_offset = -_input->data_zero_point();
112   op_params.output_offset = _output->data_zero_point();
113   int32_t output_activation_min = 0;
114   int32_t output_activation_max = 0;
115   CalculateActivationRangeQuantized(_activation, _output, &output_activation_min,
116                                     &output_activation_max);
117   op_params.quantized_activation_min = output_activation_min;
118   op_params.quantized_activation_max = output_activation_max;
119   // NOTE: The following fields of ConvParams are not used:
120   // padding_type, weights_offset, output_{multiplier,shift}, float_activation_{min,max}
121
122   nnfw::cker::Conv &kernel = *_conv_kernel;
123   kernel(op_params, getShape(_input), getBuffer<uint8_t>(_input), getShape(_kernel),
124          getBuffer<uint8_t>(_kernel), _kernel->data_zero_points().data(), getShape(_bias),
125          getBuffer<int32_t>(_bias), getShape(_output), getBuffer<uint8_t>(_output));
126 }
127
128 void ConvolutionLayer::convQ8i()
129 {
130   int32_t output_activation_min = 0;
131   int32_t output_activation_max = 0;
132   CalculateActivationRangeQuantized(_activation, _output, &output_activation_min,
133                                     &output_activation_max);
134
135   nnfw::cker::ConvParams op_params;
136   op_params.input_offset = -_input->data_zero_point();
137   op_params.output_offset = _output->data_zero_point();
138   op_params.stride_height = _strideHeight;
139   op_params.stride_width = _strideWidth;
140   op_params.dilation_height_factor = _dilationHeightFactor;
141   op_params.dilation_width_factor = _dilationWidthFactor;
142   op_params.padding_values.height = _paddingTop;
143   op_params.padding_values.width = _paddingLeft;
144   op_params.quantized_activation_min = output_activation_min;
145   op_params.quantized_activation_max = output_activation_max;
146
147   nnfw::cker::Conv &kernel = *_conv_kernel;
148   kernel(op_params, getShape(_input), reinterpret_cast<const int8_t *>(_input->buffer()),
149          getShape(_kernel), reinterpret_cast<const int8_t *>(_kernel->buffer()), getShape(_bias),
150          reinterpret_cast<const int32_t *>(_bias->buffer()), getShape(_output),
151          reinterpret_cast<int8_t *>(_output->buffer()));
152 }
153
154 void ConvolutionLayer::configure(const IPortableTensor *input, const IPortableTensor *kernel,
155                                  const IPortableTensor *bias, const ir::PaddingType paddingType,
156                                  const uint32_t paddingLeft, const uint32_t paddingRight,
157                                  const uint32_t paddingTop, const uint32_t paddingBottom,
158                                  const uint32_t strideWidth, const uint32_t strideHeight,
159                                  const uint32_t dilationWidthFactor,
160                                  const uint32_t dilationHeightFactor,
161                                  const ir::Activation activation, IPortableTensor *output)
162 {
163   _input = input;
164   _kernel = kernel;
165   _bias = bias;
166   _paddingType = paddingType;
167   _paddingLeft = paddingLeft;
168   _paddingRight = paddingRight;
169   _paddingTop = paddingTop;
170   _paddingBottom = paddingBottom;
171   _strideWidth = strideWidth;
172   _strideHeight = strideHeight;
173   _dilationWidthFactor = dilationWidthFactor;
174   _dilationHeightFactor = dilationHeightFactor;
175   _activation = activation;
176   _output = output;
177 }
178
179 void ConvolutionLayer::run()
180 {
181   prepare();
182
183   if (_input->is_dynamic() || _kernel->is_dynamic())
184   {
185     const auto ifm_shape = _input->getShape().asFeature(_input->layout());
186     const auto ofm_shape = _output->getShape().asFeature(_input->layout());
187     // Kernel format is [depth_out, kernel_height, kernel_width, depth_in].
188     const auto ker_shape = _kernel->getShape();
189     const auto ker_height = ker_shape.dim(1);
190     const auto ker_width = ker_shape.dim(2);
191
192     ir::Stride stride;
193     stride.vertical = _strideWidth;
194     stride.horizontal = _strideWidth;
195
196     ir::Padding param_padding;
197     param_padding.type = _paddingType;
198     param_padding.param.left = _paddingLeft;
199     param_padding.param.right = _paddingRight;
200     param_padding.param.top = _paddingTop;
201     param_padding.param.bottom = _paddingBottom;
202
203     const auto padding =
204       ir::calculatePadding(param_padding, ifm_shape, ofm_shape, stride, ker_width, ker_height,
205                            _dilationWidthFactor, _dilationHeightFactor);
206
207     _paddingLeft = padding.left;
208     _paddingRight = padding.right;
209     _paddingTop = padding.top;
210     _paddingBottom = padding.bottom;
211   }
212   if (_input->data_type() == OperandType::FLOAT32)
213   {
214     convFloat32();
215   }
216   else if (_input->data_type() == OperandType::QUANT_UINT8_ASYMM)
217   {
218     const bool per_channel_quantized = _kernel->data_scales().size() > 1;
219     if (per_channel_quantized)
220       convQ8uPerChannel();
221     else
222       convQ8uPerTensor();
223   }
224   else if (_input->data_type() == OperandType::QUANT_INT8_ASYMM)
225   {
226     convQ8i();
227   }
228   else
229   {
230     throw std::runtime_error{"Conv: unsupported data type"};
231   }
232 }
233
234 void ConvolutionLayer::prepare()
235 {
236   if (_prepare)
237     return;
238
239   nnfw::cker::Conv &kernel = *_conv_kernel;
240   if (_input->data_type() == OperandType::FLOAT32 && _kernel->is_constant())
241   {
242     bool is_transposed = false;
243     kernel.prepareF32(getShape(_kernel), getBuffer<float>(_kernel), getPaddingType(_paddingType),
244                       is_transposed, _dilationWidthFactor, _dilationHeightFactor);
245
246     // Decrease reference of _kernel(weights) only when _kernel is constant
247     if (is_transposed)
248     {
249       auto kernel_tensor = dynamic_cast<const Tensor *>(_kernel);
250       if (kernel_tensor)
251         // TODO Remove const_cast
252         const_cast<Tensor *>(kernel_tensor)->decrease_ref();
253     }
254   }
255   else if (_input->data_type() == OperandType::QUANT_UINT8_ASYMM && _kernel->is_constant() &&
256            !_input->is_dynamic() && !_output->is_dynamic())
257   {
258     const bool per_channel_quantized = _kernel->data_scales().size() > 1;
259     if (per_channel_quantized)
260     {
261       GetQuantizedConvolutionMultipliersAndShifts(
262         _input->data_scale(), _output->data_scale(), _kernel->data_scales().data(),
263         _kernel->data_scales().size(), getShape(_kernel).Dims(0),
264         kernel.per_channel_output_multiplier(), kernel.per_channel_output_shift());
265     }
266     else
267     {
268       kernel.prepareQ8uPerTensor(getShape(_input), getShape(_kernel), getShape(_output),
269                                  _strideWidth, _strideHeight, _dilationWidthFactor,
270                                  _dilationHeightFactor);
271     }
272   }
273   else if (_input->data_type() == OperandType::QUANT_INT8_ASYMM)
274   {
275     if (_kernel->is_constant() && !_input->is_dynamic() && !_output->is_dynamic())
276     {
277       GetQuantizedConvolutionMultipliersAndShifts(
278         _input->data_scale(), _output->data_scale(), _kernel->data_scales().data(),
279         _kernel->data_scales().size(), getShape(_kernel).Dims(0),
280         kernel.per_channel_output_multiplier(), kernel.per_channel_output_shift());
281     }
282     else
283     {
284       throw std::runtime_error{"Conv2D: Int8 dynamic weight is not supported"};
285     }
286   }
287   _prepare = true;
288 }
289
290 } // namespace ops
291 } // namespace cpu
292 } // namespace backend
293 } // namespace onert