2 * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 #include "ConvolutionLayer.h"
18 #include "OperationUtils.h"
20 #include "../Tensor.h"
21 #include "ir/Padding.h"
22 #include <cker/operation/Conv.h>
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)
42 ConvolutionLayer::~ConvolutionLayer() = default;
44 void ConvolutionLayer::convFloat32()
46 float output_activation_min = 0, output_activation_max = 0;
47 CalculateActivationRange(_activation, &output_activation_min, &output_activation_max);
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;
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));
66 void ConvolutionLayer::convQ8uPerTensor()
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);
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);
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;
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));
102 void ConvolutionLayer::convQ8uPerChannel()
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}
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));
128 void ConvolutionLayer::convQ8i()
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);
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;
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()));
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)
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;
179 void ConvolutionLayer::run()
183 if (_input->is_dynamic() || _kernel->is_dynamic())
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);
193 stride.vertical = _strideWidth;
194 stride.horizontal = _strideWidth;
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;
204 ir::calculatePadding(param_padding, ifm_shape, ofm_shape, stride, ker_width, ker_height,
205 _dilationWidthFactor, _dilationHeightFactor);
207 _paddingLeft = padding.left;
208 _paddingRight = padding.right;
209 _paddingTop = padding.top;
210 _paddingBottom = padding.bottom;
212 if (_input->data_type() == OperandType::FLOAT32)
216 else if (_input->data_type() == OperandType::QUANT_UINT8_ASYMM)
218 const bool per_channel_quantized = _kernel->data_scales().size() > 1;
219 if (per_channel_quantized)
224 else if (_input->data_type() == OperandType::QUANT_INT8_ASYMM)
230 throw std::runtime_error{"Conv: unsupported data type"};
234 void ConvolutionLayer::prepare()
239 nnfw::cker::Conv &kernel = *_conv_kernel;
240 if (_input->data_type() == OperandType::FLOAT32 && _kernel->is_constant())
242 bool is_transposed = false;
243 kernel.prepareF32(getShape(_kernel), getBuffer<float>(_kernel), getPaddingType(_paddingType),
244 is_transposed, _dilationWidthFactor, _dilationHeightFactor);
246 // Decrease reference of _kernel(weights) only when _kernel is constant
249 auto kernel_tensor = dynamic_cast<const Tensor *>(_kernel);
251 // TODO Remove const_cast
252 const_cast<Tensor *>(kernel_tensor)->decrease_ref();
255 else if (_input->data_type() == OperandType::QUANT_UINT8_ASYMM && _kernel->is_constant() &&
256 !_input->is_dynamic() && !_output->is_dynamic())
258 const bool per_channel_quantized = _kernel->data_scales().size() > 1;
259 if (per_channel_quantized)
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());
268 kernel.prepareQ8uPerTensor(getShape(_input), getShape(_kernel), getShape(_output),
269 _strideWidth, _strideHeight, _dilationWidthFactor,
270 _dilationHeightFactor);
273 else if (_input->data_type() == OperandType::QUANT_INT8_ASYMM)
275 if (_kernel->is_constant() && !_input->is_dynamic() && !_output->is_dynamic())
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());
284 throw std::runtime_error{"Conv2D: Int8 dynamic weight is not supported"};
292 } // namespace backend