• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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