1 /**
2 * Copyright 2020-2022 Huawei Technologies Co., Ltd
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 "src/litert/delegate/npu/op/scale_npu.h"
18 #include <memory>
19 #include "src/litert/delegate/npu/npu_converter_utils.h"
20
21 namespace mindspore::lite {
22 constexpr int INPUT_INDEX = 0;
23 constexpr int SCALE_INDEX = 1;
24 constexpr int BIAS_INDEX = 2;
25
IsSupport(const schema::Primitive * primitive,const std::vector<mindspore::MSTensor> & in_tensors,const std::vector<mindspore::MSTensor> & out_tensors)26 int ScaleNPUOp::IsSupport(const schema::Primitive *primitive, const std::vector<mindspore::MSTensor> &in_tensors,
27 const std::vector<mindspore::MSTensor> &out_tensors) {
28 auto scale_prim = primitive->value_as_ScaleFusion();
29 if (scale_prim == nullptr) {
30 MS_LOG(ERROR) << "Get null primitive value for op: " << name_;
31 return RET_ERROR;
32 }
33 CHECK_LESS_RETURN(in_tensors.size(), 1);
34 auto input_dims = in_tensors.at(INPUT_INDEX).Shape().size();
35 axis_ = scale_prim->axis();
36 if (axis_ < 0) {
37 axis_ = axis_ + input_dims;
38 }
39 if (axis_ != NHWC_C && axis_ != NCHW_C) {
40 if (in_tensors.size() <= BIAS_INDEX) {
41 MS_LOG(INFO) << "Npu Scale op does not support axis: " << axis_ << ", trying to convert to Mul op.";
42 use_mul_ = true;
43 return RET_OK;
44 } else {
45 MS_LOG(WARNING) << "Npu Scale axis attr only support 1 or channel, now is " << axis_;
46 return RET_NOT_SUPPORT;
47 }
48 }
49 if (input_dims < NPU_SHAPE_SIZE) {
50 need_expand_ = true;
51 }
52 return RET_OK;
53 }
54
Init(const schema::Primitive * primitive,const std::vector<mindspore::MSTensor> & in_tensors,const std::vector<mindspore::MSTensor> & out_tensors)55 int ScaleNPUOp::Init(const schema::Primitive *primitive, const std::vector<mindspore::MSTensor> &in_tensors,
56 const std::vector<mindspore::MSTensor> &out_tensors) {
57 auto scale_prim = primitive->value_as_ScaleFusion();
58 if (scale_prim == nullptr) {
59 MS_LOG(ERROR) << "Get null primitive value for op ." << name_;
60 return RET_ERROR;
61 }
62
63 if (use_mul_) {
64 mul_ = new (std::nothrow) hiai::op::Mul(name_ + "_mul");
65 if (mul_ == nullptr) {
66 MS_LOG(ERROR) << "New Mul npu operator for op " << name_ << "_mul failed.";
67 return RET_ERROR;
68 }
69 scale_ops_.emplace_back(mul_);
70 } else {
71 // note that Scale only support the default axis(i.e., 1), setting axis is meaningless.
72 scale_ = new (std::nothrow) hiai::op::Scale(name_);
73 if (scale_ == nullptr) {
74 MS_LOG(ERROR) << "New Scale npu operator for op " << name_ << " failed.";
75 return RET_ERROR;
76 }
77 scale_ops_.emplace_back(scale_);
78 }
79
80 if (need_expand_) {
81 out_reshape_ = new (std::nothrow) hiai::op::Reshape(name_ + "_restore");
82 if (out_reshape_ == nullptr) {
83 MS_LOG(ERROR) << "New Reshape npu operator for op " << name_ << "_restore failed.";
84 return RET_ERROR;
85 }
86 scale_ops_.emplace_back(out_reshape_);
87 }
88
89 act_type_ = scale_prim->activation_type();
90 if (act_type_ != schema::ActivationType_NO_ACTIVATION) {
91 act_ = new (std::nothrow) hiai::op::Activation(name_ + "_act");
92 if (act_ == nullptr) {
93 MS_LOG(ERROR) << "New activation npu operator for op " << name_ << " failed.";
94 return RET_ERROR;
95 }
96 scale_ops_.emplace_back(act_);
97 }
98 return RET_OK;
99 }
100
GetNPUOp()101 ge::Operator *ScaleNPUOp::GetNPUOp() {
102 if (act_type_ != schema::ActivationType_NO_ACTIVATION) {
103 return act_;
104 } else if (use_mul_) {
105 return mul_;
106 } else if (need_expand_) {
107 return out_reshape_;
108 } else {
109 return scale_;
110 }
111 }
112
SetNPUInputs(const std::vector<mindspore::MSTensor> & in_tensors,const std::vector<mindspore::MSTensor> & out_tensors,const std::vector<ge::Operator * > & npu_inputs)113 int ScaleNPUOp::SetNPUInputs(const std::vector<mindspore::MSTensor> &in_tensors,
114 const std::vector<mindspore::MSTensor> &out_tensors,
115 const std::vector<ge::Operator *> &npu_inputs) {
116 if (use_mul_) {
117 auto ret = ConvertScaleToMul(npu_inputs, in_tensors);
118 if (ret != RET_OK) {
119 MS_LOG(ERROR) << "Convert Scale to Mul failed, op name: " << name_;
120 return RET_ERROR;
121 }
122 } else {
123 auto ret = Adopt4DScale(npu_inputs, in_tensors);
124 if (ret != RET_OK) {
125 MS_LOG(ERROR) << "Adopt 4D Scale op failed, op name: " << name_;
126 return RET_ERROR;
127 }
128 }
129 if (act_type_ != schema::ActivationType_NO_ACTIVATION) {
130 auto ret = SetActivation();
131 if (ret != RET_OK) {
132 MS_LOG(ERROR) << "Set Activation failed, op name: " << name_;
133 return RET_ERROR;
134 }
135 }
136 return RET_OK;
137 }
138
SetActivation()139 int ScaleNPUOp::SetActivation() {
140 ge::Operator *act_input = nullptr;
141 if (use_mul_) {
142 act_input = mul_;
143 } else if (need_expand_) {
144 act_input = out_reshape_;
145 } else {
146 act_input = scale_;
147 }
148 MS_CHECK_TRUE_MSG(act_input != nullptr, RET_ERROR, "Scale activation input is nullptr.");
149 act_->set_input_x(*act_input);
150 auto act_mode = ConverterToNPUActivationMode(act_type_);
151 if (act_mode == ACTIVATION_INVALID) {
152 MS_LOG(ERROR) << "Unsupported activation type for scale op " << name_;
153 return RET_ERROR;
154 }
155 act_->set_attr_mode(act_mode);
156 return RET_OK;
157 }
158
ConvertScaleToMul(const std::vector<ge::Operator * > & npu_inputs,const std::vector<mindspore::MSTensor> & in_tensors)159 int ScaleNPUOp::ConvertScaleToMul(const std::vector<ge::Operator *> &npu_inputs,
160 const std::vector<mindspore::MSTensor> &in_tensors) {
161 auto input_shape = in_tensors.at(INPUT_INDEX).Shape();
162 auto scale_shape = in_tensors.at(SCALE_INDEX).Shape();
163 mul_->set_input_x1(*npu_inputs.at(INPUT_INDEX));
164 if (input_shape.size() == scale_shape.size()) {
165 mul_->set_input_x2(*npu_inputs.at(SCALE_INDEX));
166 } else {
167 int64_t valid_dims = input_shape.size();
168 std::vector<int> valid_shape(valid_dims, 1);
169 for (size_t i = 0; i < scale_shape.size(); i++) {
170 valid_shape[axis_ + i] = static_cast<int>(scale_shape[i]);
171 }
172 auto reshape = new (std::nothrow) hiai::op::Reshape(name_ + "_mul_reshape");
173 if (reshape == nullptr) {
174 MS_LOG(ERROR) << "New Reshape npu operator for op " << name_ << "_mul_reshape failed.";
175 return RET_ERROR;
176 }
177 scale_ops_.emplace_back(reshape);
178 auto valid_data_ptr = reinterpret_cast<const uint8_t *>(valid_shape.data());
179 auto shape = GetNPUConst<int>(valid_data_ptr, {valid_dims}, ge::DT_INT32, name_ + "_mul_expand_shape");
180 if (shape == nullptr) {
181 MS_LOG(ERROR) << "Get shape const for op " << name_ << "_mul failed.";
182 return RET_ERROR;
183 }
184 scale_ops_.emplace_back(shape);
185 reshape->set_input_x(*npu_inputs.at(SCALE_INDEX));
186 reshape->set_input_shape(*shape);
187 mul_->set_input_x2(*reshape);
188 }
189 return RET_OK;
190 }
191
Adopt4DScale(const std::vector<ge::Operator * > & npu_inputs,const std::vector<mindspore::MSTensor> & in_tensors)192 int ScaleNPUOp::Adopt4DScale(const std::vector<ge::Operator *> &npu_inputs,
193 const std::vector<mindspore::MSTensor> &in_tensors) {
194 MS_ASSERT(scale_ != nullptr);
195 // handle input
196 auto org_input_tensor = in_tensors.at(INPUT_INDEX);
197 ge::Operator *actual_input = npu_inputs.at(INPUT_INDEX);
198 std::vector<int64_t> org_input_shape = org_input_tensor.Shape();
199 if (need_expand_) {
200 actual_input = ChangeDims(npu_inputs.at(INPUT_INDEX), org_input_shape, name_ + "_expand_input", true);
201 if (actual_input == nullptr) {
202 MS_LOG(ERROR) << "Change Scale op input dims failed.";
203 return RET_ERROR;
204 }
205 }
206 scale_->set_input_x(*actual_input);
207
208 // handle scale, note that the scale axis can only be 1.
209 auto org_scale_tensor = in_tensors.at(SCALE_INDEX);
210 ge::Operator *actual_scale = npu_inputs.at(SCALE_INDEX);
211 if (org_scale_tensor.Shape().size() == DIMENSION_2D) {
212 std::vector<int64_t> expand_scale_shape = org_scale_tensor.Shape();
213 expand_scale_shape.emplace_back(1);
214 actual_scale = ChangeDims(npu_inputs.at(SCALE_INDEX), expand_scale_shape, name_ + "_expand_scale");
215 if (actual_scale == nullptr) {
216 MS_LOG(ERROR) << "Change Scale op scale dims failed.";
217 return RET_ERROR;
218 }
219 }
220 scale_->set_input_scale(*actual_scale);
221
222 // handle bias
223 if (in_tensors.size() > BIAS_INDEX) {
224 auto org_bias_tensor = in_tensors.at(BIAS_INDEX);
225 ge::Operator *actual_bias = npu_inputs.at(BIAS_INDEX);
226 if (org_bias_tensor.Shape().size() == DIMENSION_2D) {
227 std::vector<int64_t> expand_bias_shape = org_bias_tensor.Shape();
228 expand_bias_shape.emplace_back(1);
229 actual_bias = ChangeDims(npu_inputs.at(BIAS_INDEX), expand_bias_shape, name_ + "_expand_bias");
230 if (actual_bias == nullptr) {
231 MS_LOG(ERROR) << "Change Scale op bias dims failed.";
232 return RET_ERROR;
233 }
234 }
235 scale_->set_input_bias(*actual_bias);
236 }
237
238 // restore to origin input shape
239 if (need_expand_) {
240 int64_t dims = org_input_shape.size();
241 std::vector<int> valid_shape;
242 for (int i = 0; i < dims; i++) {
243 valid_shape.emplace_back(static_cast<int>(org_input_shape.at(i)));
244 }
245 auto valid_data_ptr = reinterpret_cast<const uint8_t *>(valid_shape.data());
246 auto shape = GetNPUConst<int>(valid_data_ptr, {dims}, ge::DT_INT32, name_ + "_restore_shape");
247 if (shape == nullptr) {
248 MS_LOG(ERROR) << "Get NPU Const for shape restoration failed.";
249 return RET_ERROR;
250 }
251 scale_ops_.emplace_back(shape);
252 out_reshape_->set_input_x(*scale_);
253 out_reshape_->set_input_shape(*shape);
254 }
255 return RET_OK;
256 }
257
ChangeDims(const ge::Operator * input,std::vector<int64_t> dst_shape,std::string name,bool need_expand_4d)258 ge::Operator *ScaleNPUOp::ChangeDims(const ge::Operator *input, std::vector<int64_t> dst_shape, std::string name,
259 bool need_expand_4d) {
260 MS_ASSERT(input != nullptr);
261 auto reshape = new (std::nothrow) hiai::op::Reshape(name);
262 if (reshape == nullptr) {
263 MS_LOG(ERROR) << "New Reshape NPU operator failed.";
264 return nullptr;
265 }
266 scale_ops_.emplace_back(reshape);
267 MS_CHECK_LE(dst_shape.size(), NPU_SHAPE_SIZE, nullptr);
268 int64_t actual_dim = need_expand_4d ? NPU_SHAPE_SIZE : dst_shape.size();
269 std::vector<int> valid_shape(actual_dim, 1);
270 for (int i = 0; i < dst_shape.size(); i++) {
271 valid_shape[i] = static_cast<int>(dst_shape.at(i));
272 }
273 auto valid_data_ptr = reinterpret_cast<const uint8_t *>(valid_shape.data());
274 auto shape = GetNPUConst<int>(valid_data_ptr, {actual_dim}, ge::DT_INT32, name_ + "_shape");
275 if (shape == nullptr) {
276 MS_LOG(ERROR) << "Get NPU Const for shape restoration failed.";
277 return nullptr;
278 }
279 scale_ops_.emplace_back(shape);
280 reshape->set_input_x(*input);
281 reshape->set_input_shape(*shape);
282 return reshape;
283 }
284
~ScaleNPUOp()285 ScaleNPUOp::~ScaleNPUOp() {
286 for (auto op : scale_ops_) {
287 if (op != nullptr) {
288 delete op;
289 op = nullptr;
290 }
291 }
292 }
293 } // namespace mindspore::lite
294