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