8a48497d506fac1e066ea0300b0a26f7b0805873
[platform/core/ml/nnfw.git] / runtime / onert / backend / cpu / ops / DepthwiseConvolutionLayer.cc
1 /*
2  * Copyright (c) 2019 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 "DepthwiseConvolutionLayer.h"
18
19 #include <cker/operation/DepthwiseConv.h>
20
21 namespace onert
22 {
23 namespace backend
24 {
25 namespace cpu
26 {
27 namespace ops
28 {
29
30 void DepthwiseConvolutionLayer::convFloat32()
31 {
32   float output_activation_min = 0, output_activation_max = 0;
33   CalculateActivationRange(_activation, &output_activation_min, &output_activation_max);
34
35   nnfw::cker::DepthwiseConvParams op_params;
36   op_params.stride_width = _strideWidth;
37   op_params.stride_height = _strideHeight;
38   op_params.dilation_width_factor = _dilationWidth;
39   op_params.dilation_height_factor = _dilationHeight;
40   op_params.padding_values.width = _paddingLeft;
41   op_params.padding_values.height = _paddingTop;
42   op_params.depth_multiplier = _multiplier;
43   op_params.float_activation_min = output_activation_min;
44   op_params.float_activation_max = output_activation_max;
45
46   nnfw::cker::DepthwiseConv<float, float>(
47     op_params, getShape(_input), getBuffer<float>(_input), getShape(_kernel),
48     getBuffer<float>(_kernel), getShape(_bias), getBuffer<float>(_bias), getShape(_output),
49     getBuffer<float>(_output), _external_context->ruy_context());
50 }
51
52 void DepthwiseConvolutionLayer::convQ8uPerTensor()
53 {
54   int32_t output_activation_min = 0;
55   int32_t output_activation_max = 0;
56   CalculateActivationRangeQuantized(_activation, _output, &output_activation_min,
57                                     &output_activation_max);
58
59   double real_multiplier = 0.0;
60   int32_t output_multiplier = 0;
61   int32_t output_shift = 0;
62   GetQuantizedConvolutionMultiplier(_input, _kernel, _bias, _output, &real_multiplier);
63   QuantizeMultiplier(real_multiplier, &output_multiplier, &output_shift);
64
65   nnfw::cker::DepthwiseConvParams op_params;
66   op_params.stride_width = _strideWidth;
67   op_params.stride_height = _strideHeight;
68   op_params.dilation_width_factor = _dilationWidth;
69   op_params.dilation_height_factor = _dilationHeight;
70   op_params.padding_values.width = _paddingLeft;
71   op_params.padding_values.height = _paddingTop;
72   op_params.depth_multiplier = _multiplier;
73   op_params.input_offset = -_input->data_zero_point();
74   op_params.weights_offset = -_kernel->data_zero_point();
75   op_params.output_offset = _output->data_zero_point();
76   op_params.output_multiplier = output_multiplier;
77   op_params.output_shift = output_shift;
78   op_params.quantized_activation_min = output_activation_min;
79   op_params.quantized_activation_max = output_activation_max;
80
81   nnfw::cker::DepthwiseConv<uint8_t, int32_t>(
82     op_params, getShape(_input), getBuffer<uint8_t>(_input), getShape(_kernel),
83     getBuffer<uint8_t>(_kernel), getShape(_bias), getBuffer<int32_t>(_bias), getShape(_output),
84     getBuffer<uint8_t>(_output), _external_context->ruy_context());
85 }
86
87 void DepthwiseConvolutionLayer::convQ8uPerChannel()
88 {
89   nnfw::cker::DepthwiseConvParams op_params;
90   op_params.padding_values.width = _paddingLeft;
91   op_params.padding_values.height = _paddingTop;
92   op_params.stride_width = _strideWidth;
93   op_params.stride_height = _strideHeight;
94   op_params.dilation_width_factor = _dilationWidth;
95   op_params.dilation_height_factor = _dilationHeight;
96   op_params.depth_multiplier = _multiplier;
97   op_params.input_offset = -_input->data_zero_point();
98   op_params.output_offset = _output->data_zero_point();
99   int32_t output_activation_min = 0;
100   int32_t output_activation_max = 0;
101   CalculateActivationRangeQuantized(_activation, _output, &output_activation_min,
102                                     &output_activation_max);
103   op_params.quantized_activation_min = output_activation_min;
104   op_params.quantized_activation_max = output_activation_max;
105   // NOTE: The following fields of ConvParams are not used:
106   // padding_type, weights_offset, output_{multiplier,shift}, float_activation_{min,max}
107
108   nnfw::cker::reference_integer_ops::DepthwiseConvPerChannel(
109     op_params, _per_channel_output_multiplier.data(), _per_channel_output_shift.data(),
110     getShape(_input), getBuffer<uint8_t>(_input), getShape(_kernel), getBuffer<uint8_t>(_kernel),
111     _kernel->data_zero_points().data(), getShape(_bias), getBuffer<int32_t>(_bias),
112     getShape(_output), getBuffer<uint8_t>(_output));
113 }
114
115 void DepthwiseConvolutionLayer::convQ8i()
116 {
117   if (!_prepared)
118   {
119     prepareQ8i();
120     _prepared = true;
121   }
122
123   int32_t output_activation_min = 0;
124   int32_t output_activation_max = 0;
125   CalculateActivationRangeQuantized(_activation, _output, &output_activation_min,
126                                     &output_activation_max);
127
128   nnfw::cker::DepthwiseConvParams op_params;
129   op_params.padding_type = nnfw::cker::PaddingType::kSame;
130   op_params.padding_values.width = _paddingLeft;
131   op_params.padding_values.height = _paddingTop;
132   op_params.depth_multiplier = _multiplier;
133   op_params.stride_width = _strideWidth;
134   op_params.stride_height = _strideHeight;
135   op_params.dilation_width_factor = _dilationWidth;
136   op_params.dilation_height_factor = _dilationHeight;
137   op_params.input_offset = -_input->data_zero_point();
138   op_params.weights_offset = 0;
139   op_params.output_offset = _output->data_zero_point();
140   op_params.quantized_activation_min = output_activation_min;
141   op_params.quantized_activation_max = output_activation_max;
142
143   nnfw::cker::optimized_integer_ops::DepthwiseConvPerChannel(
144     op_params, _per_channel_output_multiplier.data(), _per_channel_output_shift.data(),
145     getShape(_input), getBuffer<int8_t>(_input), getShape(_kernel), getBuffer<int8_t>(_kernel),
146     getShape(_bias), getBuffer<int32_t>(_bias), getShape(_output), getBuffer<int8_t>(_output),
147     _external_context->ruy_context());
148 }
149
150 void DepthwiseConvolutionLayer::prepareQ8i()
151 {
152   GetQuantizedConvolutionMultipliersAndShifts(
153     _input->data_scale(), _output->data_scale(), _kernel->data_scales().data(),
154     _kernel->data_scales().size(), getShape(_kernel).Dims(3), _per_channel_output_multiplier,
155     _per_channel_output_shift);
156 }
157
158 void DepthwiseConvolutionLayer::prepareQ8uPerChannel()
159 {
160   GetQuantizedConvolutionMultipliersAndShifts(
161     _input->data_scale(), _output->data_scale(), _kernel->data_scales().data(),
162     _kernel->data_scales().size(), getShape(_kernel).Dims(3), _per_channel_output_multiplier,
163     _per_channel_output_shift);
164 }
165
166 void DepthwiseConvolutionLayer::configure(
167   const IPortableTensor *input, const IPortableTensor *kernel, const IPortableTensor *bias,
168   const uint32_t paddingLeft, const uint32_t paddingRight, const uint32_t paddingTop,
169   const uint32_t paddingBottom, const uint32_t strideWidth, const uint32_t strideHeight,
170   const uint32_t multiplier, const uint32_t dilationWidth, const uint32_t dilationHeight,
171   const ir::Activation activation, IPortableTensor *output,
172   const std::shared_ptr<ExternalContext> &external_context)
173 {
174   _input = input;
175   _kernel = kernel;
176   _bias = bias;
177   _paddingLeft = paddingLeft;
178   _paddingRight = paddingRight;
179   _paddingTop = paddingTop;
180   _paddingBottom = paddingBottom;
181   _strideWidth = strideWidth;
182   _strideHeight = strideHeight;
183   _multiplier = multiplier;
184   _dilationWidth = dilationWidth;
185   _dilationHeight = dilationHeight;
186   _activation = activation;
187   _output = output;
188   _external_context = external_context;
189
190   if (_input->data_type() == OperandType::QUANT_INT8_ASYMM)
191   {
192     if (_kernel->is_constant() && !_input->is_dynamic() && !_output->is_dynamic())
193     {
194       prepareQ8i();
195       _prepared = true;
196     }
197   }
198   else if (_input->data_type() == OperandType::QUANT_UINT8_ASYMM && _kernel->is_constant() &&
199            !_input->is_dynamic() && !_output->is_dynamic())
200   {
201     const bool per_channel_quantized = _kernel->data_scales().size() > 1;
202     if (per_channel_quantized)
203     {
204       prepareQ8uPerChannel();
205       _prepared = true;
206     }
207   }
208 }
209
210 void DepthwiseConvolutionLayer::run()
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{"DepthwiseConv: unsupported data type"};
231   }
232 }
233
234 } // namespace ops
235 } // namespace cpu
236 } // namespace backend
237 } // namespace onert