Imported Upstream version 1.9.0
[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
19 #include "../Tensor.h"
20 #include "ir/Padding.h"
21 #include <cker/operation/Conv.h>
22
23 namespace onert
24 {
25 namespace backend
26 {
27 namespace cpu
28 {
29 namespace ops
30 {
31 ConvolutionLayer::ConvolutionLayer()
32     : _input(nullptr), _kernel(nullptr), _bias(nullptr), _output(nullptr),
33       _paddingType(ir::PaddingType::EXPLICIT), _paddingLeft(0), _paddingTop(0), _paddingRight(0),
34       _paddingBottom(0), _strideWidth(0), _strideHeight(0), _dilationWidthFactor(1),
35       _dilationHeightFactor(1), _activation(ir::Activation::NONE),
36       _conv_kernel(new nnfw::cker::Conv()), _prepare(false)
37 {
38   // DO NOTHING
39 }
40
41 ConvolutionLayer::~ConvolutionLayer() = default;
42
43 void ConvolutionLayer::convFloat32()
44 {
45   float output_activation_min = 0, output_activation_max = 0;
46   CalculateActivationRange(_activation, &output_activation_min, &output_activation_max);
47
48   nnfw::cker::ConvParams op_params;
49   op_params.padding_type = getPaddingType(_paddingType);
50   op_params.padding_values.width = _paddingLeft;
51   op_params.padding_values.height = _paddingTop;
52   op_params.stride_width = _strideWidth;
53   op_params.stride_height = _strideHeight;
54   op_params.dilation_width_factor = _dilationWidthFactor;
55   op_params.dilation_height_factor = _dilationHeightFactor;
56   op_params.float_activation_min = output_activation_min;
57   op_params.float_activation_max = output_activation_max;
58
59   nnfw::cker::Conv &kernel = *_conv_kernel;
60   kernel(op_params, getTensorShape(_input), reinterpret_cast<const float *>(_input->buffer()),
61          getTensorShape(_kernel), reinterpret_cast<const float *>(_kernel->buffer()),
62          getTensorShape(_bias), reinterpret_cast<const float *>(_bias->buffer()),
63          getTensorShape(_output), reinterpret_cast<float *>(_output->buffer()));
64 }
65
66 void ConvolutionLayer::convQuant8()
67 {
68   int32_t output_activation_min = 0;
69   int32_t output_activation_max = 0;
70   CalculateActivationRangeUint8(_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_offset();
88   op_params.weights_offset = -_kernel->data_offset();
89   op_params.output_offset = _output->data_offset();
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, getTensorShape(_input), reinterpret_cast<const uint8_t *>(_input->buffer()),
98          getTensorShape(_kernel), reinterpret_cast<const uint8_t *>(_kernel->buffer()),
99          getTensorShape(_bias), reinterpret_cast<const int32_t *>(_bias->buffer()),
100          getTensorShape(_output), reinterpret_cast<uint8_t *>(_output->buffer()));
101 }
102
103 void ConvolutionLayer::configure(const IPortableTensor *input, const IPortableTensor *kernel,
104                                  const IPortableTensor *bias, const ir::PaddingType paddingType,
105                                  const uint32_t paddingLeft, const uint32_t paddingRight,
106                                  const uint32_t paddingTop, const uint32_t paddingBottom,
107                                  const uint32_t strideWidth, const uint32_t strideHeight,
108                                  const uint32_t dilationWidthFactor,
109                                  const uint32_t dilationHeightFactor,
110                                  const ir::Activation activation, IPortableTensor *output)
111 {
112   _input = input;
113   _kernel = kernel;
114   _bias = bias;
115   _paddingType = paddingType;
116   _paddingLeft = paddingLeft;
117   _paddingRight = paddingRight;
118   _paddingTop = paddingTop;
119   _paddingBottom = paddingBottom;
120   _strideWidth = strideWidth;
121   _strideHeight = strideHeight;
122   _dilationWidthFactor = dilationWidthFactor;
123   _dilationHeightFactor = dilationHeightFactor;
124   _activation = activation;
125   _output = output;
126 }
127
128 void ConvolutionLayer::run()
129 {
130   prepare();
131
132   if (_input->is_dynamic() || _kernel->is_dynamic())
133   {
134     const auto ifm_shape = _input->getShape().asFeature(_input->layout());
135     const auto ofm_shape = _output->getShape().asFeature(_input->layout());
136     // Kernel format is [depth_out, kernel_height, kernel_width, depth_in].
137     const auto ker_shape = _kernel->getShape();
138     const auto ker_height = ker_shape.dim(1);
139     const auto ker_width = ker_shape.dim(2);
140
141     ir::Stride stride;
142     stride.vertical = _strideWidth;
143     stride.horizontal = _strideWidth;
144
145     ir::Padding param_padding;
146     param_padding.type = _paddingType;
147     param_padding.param.left = _paddingLeft;
148     param_padding.param.right = _paddingRight;
149     param_padding.param.top = _paddingTop;
150     param_padding.param.bottom = _paddingBottom;
151
152     const auto padding =
153         ir::calculatePadding(param_padding, ifm_shape, ofm_shape, stride, ker_width, ker_height,
154                              _dilationWidthFactor, _dilationHeightFactor);
155
156     _paddingLeft = padding.left;
157     _paddingRight = padding.right;
158     _paddingTop = padding.top;
159     _paddingBottom = padding.bottom;
160   }
161   if (_input->data_type() == OperandType::FLOAT32)
162   {
163     convFloat32();
164   }
165   else if (_input->data_type() == OperandType::QUANT_UINT8_ASYMM)
166   {
167     convQuant8();
168   }
169   else
170   {
171     throw std::runtime_error{"Conv: unsupported data type"};
172   }
173 }
174
175 void ConvolutionLayer::prepare()
176 {
177   if (_prepare)
178     return;
179
180   nnfw::cker::Conv &kernel = *_conv_kernel;
181   if (_input->data_type() == OperandType::FLOAT32 && _kernel->is_constant())
182   {
183     bool is_transposed = false;
184     kernel.prepare(getTensorShape(_kernel), reinterpret_cast<const float *>(_kernel->buffer()),
185                    getPaddingType(_paddingType), is_transposed, _dilationWidthFactor,
186                    _dilationHeightFactor);
187
188     // Decrease reference of _kernel(weights) only when _kernel is constant
189     if (is_transposed)
190     {
191       auto kernel_tensor = dynamic_cast<const Tensor *>(_kernel);
192       if (kernel_tensor)
193         // TODO Remove const_cast
194         const_cast<Tensor *>(kernel_tensor)->decrease_ref();
195     }
196   }
197   else if (_input->data_type() == OperandType::QUANT_UINT8_ASYMM && _kernel->is_constant() &&
198            !_input->is_dynamic() && !_output->is_dynamic())
199   {
200     kernel.prepareQuant(getTensorShape(_input), getTensorShape(_kernel), getTensorShape(_output),
201                         _strideWidth, _strideHeight);
202   }
203   _prepare = true;
204 }
205
206 #undef ANDROID_NN_CONV_PARAMETERS
207
208 } // namespace ops
209 } // namespace cpu
210 } // namespace backend
211 } // namespace onert