diff --git a/ccsrc/transform-update.zip b/ccsrc/transform-update.zip new file mode 100644 index 00000000000..9d43a3c4cd6 Binary files /dev/null and b/ccsrc/transform-update.zip differ diff --git a/mindspore/ccsrc/transform-update.zip b/mindspore/ccsrc/transform-update.zip new file mode 100644 index 00000000000..9d43a3c4cd6 Binary files /dev/null and b/mindspore/ccsrc/transform-update.zip differ diff --git a/mindspore/ccsrc/transform-update/_init_.py b/mindspore/ccsrc/transform-update/_init_.py new file mode 100644 index 00000000000..59ce74f2ab2 --- /dev/null +++ b/mindspore/ccsrc/transform-update/_init_.py @@ -0,0 +1,16 @@ +# Copyright 2022 Huawei Technologies Co., Ltd +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ============================================================================ +"""Transformers for optimizing ast.""" +from .flatten_recursive_stmt import FlattenRecursiveStmt diff --git a/mindspore/ccsrc/transform-update/adasum.py b/mindspore/ccsrc/transform-update/adasum.py new file mode 100644 index 00000000000..df34aa94ab6 --- /dev/null +++ b/mindspore/ccsrc/transform-update/adasum.py @@ -0,0 +1,330 @@ +# Copyright 2021 Huawei Technologies Co., Ltd +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ============================================================================ +"""adasum""" +import copy +import hashlib +import math +from mindspore.nn.cell import Cell +from mindspore.communication.management import create_group +from mindspore.ops import composite as C +from mindspore.ops import functional as F +from mindspore.ops import operations as P +from mindspore.ops.operations._inner_ops import Send, Receive + + +__all__ = ["AdaSum"] + + +MAX_NUM_HASH = 2 ** 31 + + +_update_parameters = C.MultitypeFuncGraph("update_parameters") + + +@_update_parameters.register("Tensor", "Tensor", "Tensor", "Tensor") + #定义一个名为 _update_parameters_after_broadcast 的函数,用于更新参数 +def _update_parameters_after_broadcast(delta_weight, update_delta_weight, parameter, old_parameter): + shape = F.shape(delta_weight) # 获取 delta_weight 的形状 + update_delta_weight = P.Reshape()(update_delta_weight, shape) # 将 update_delta_weight 重塑成与 delta_weight 相同的形状 + new_parameter = old_parameter - update_delta_weight # 计算新的参数值,通过从旧参数中减去 update_delta_weight + return P.Assign()(parameter, new_parameter) # 使用 P.Assign() 函数将新参数值分配给 parameter + + # 定义一个名为 _send_before_receive 的函数,用于发送数据并接收响应 +def _send_before_receive(send_part, send, recv): + send_ok = send(send_part) # 通过调用 send 函数发送数据 send_part,并将返回结果存储在 send_ok 中 + return recv(send_ok) # 调用 recv 函数,将 send_ok 作为参数传递,用于接收响应 + +# 定义一个名为 _receive_before_send 的函数,用于先接收数据后发送数据 +def _receive_before_send(send_part, send, recv): + receive_ok = recv(send_part) # 通过调用 recv 函数,将 send_part 作为参数传递,用于接收数据,并将接收结果存储在 receive_ok 中 + send_part = F.depend(send_part, receive_ok) # 使用 F.depend() 函数建立依赖关系,确保在 send_part 发送之前先执行接收操作 + return F.depend(receive_ok, send(send_part)) # 通过调用 send 函数,将 send_part 作为参数传递,用于发送数据,并将发送结果返回 + +# 定义一个名为 _send_recv_res 的函数,用于发送和接收数据,并根据条件进行数学计算和处理 +def _send_recv_res(left_send, recv_part, local_part, allreduce, parameter_divisibility, allreduce_node_num): + """send result and receive result.""" + if parameter_divisibility: # 如果参数可分割,执行以下操作 + recv_part = P.Squeeze()(recv_part) # 去除 recv_part 中的冗余维度 + local_part = F.depend(local_part, recv_part) # 建立依赖关系,确保在 local_part 使用前 recv_part 已被计算 + eps = 1e-12 + # 计算一些值,这些值将用于后续的计算 + value_0 = P.ReduceSum()(local_part * recv_part) + eps + if left_send: + value_1 = P.ReduceSum()(local_part * local_part) + eps + value_2 = P.ReduceSum()(recv_part * recv_part) + eps + else: + value_1 = P.ReduceSum()(recv_part * recv_part) + eps + value_2 = P.ReduceSum()(local_part * local_part) + eps + # 对计算的值进行全局归约(allreduce) + value_0 = allreduce(value_0) + value_1 = F.depend(allreduce(value_1), value_0) + value_2 = F.depend(allreduce(value_2), value_1) + # 根据左右发送(left_send)的条件,计算最终的结果 res + if left_send: + res = (1 - (value_0 / (2 * value_1))) * local_part + (1 - (value_0 / (2 * value_2))) * recv_part + else: + res = (1 - (value_0 / (2 * value_1))) * recv_part + (1 - (value_0 / (2 * value_2))) * local_part + else: + res = allreduce(local_part) # 如果参数不可分割,直接执行全局归约(allreduce)操作 + res /= allreduce_node_num + return res + + +_adasum_opt_forward = C.MultitypeFuncGraph("adasum_opt_forward") + + +@_adasum_opt_forward.register("Bool", "Function", "Bool", "Int64", "Function", "Function", "Tensor") +# 定义一个名为 _adasum_opt_forward_process 的函数,用于 Adasum 优化器的前向处理 +def _adasum_opt_forward_process(left_send, allreduce, parameter_divisibility, allreduce_node_num, send, recv, delta_w): + """adasum optimizer process.""" + if parameter_divisibility: # 如果参数可分割,执行以下操作 + delta_w = P.Squeeze()(delta_w) # 去除 delta_w 中的冗余维度 + ori_len = F.shape(delta_w)[0] # 获取 delta_w 的原始长度 + divide_len = ori_len / 2 # 计算分割点的位置 + left_part = delta_w[:divide_len] # 将 delta_w 划分为左半部分 + right_part = delta_w[divide_len:] # 将 delta_w 划分为右半部分 + else: # 如果参数不可分割,直接将 delta_w 复制到左半部分和右半部分 + left_part = delta_w + right_part = delta_w + + if left_send: # 如果左边发送数据 + if parameter_divisibility: # 如果参数可分割,执行发送操作,并接收数据部分 + recv_part = _send_before_receive(left_part, send, recv) + else: # 如果参数不可分割,将右半部分视为接收数据部分 + recv_part = right_part + # 计算更新的 delta_w + update_delta_w = _send_recv_res(left_send, recv_part, right_part, allreduce, parameter_divisibility, + allreduce_node_num) + else: # 如果右边发送数据 + if parameter_divisibility: # 如果参数可分割,执行接收操作,并接收数据部分 + recv_part = _receive_before_send(right_part, send, recv) + else: # 如果参数不可分割,将左半部分视为接收数据部分 + recv_part = left_part + # 计算更新的 delta_w + update_delta_w = _send_recv_res(left_send, recv_part, left_part, allreduce, parameter_divisibility, + allreduce_node_num) + + return update_delta_w + + +_adasum_opt_rollback = C.MultitypeFuncGraph("adasum_opt_rollback") + + +@_adasum_opt_rollback.register("Bool", "Bool", "Tensor", "Function", "Function") +# 定义一个名为 _adasum_opt_rollback_process 的函数,用于 Adasum 优化器的回滚处理 +def _adasum_opt_rollback_process(left_send, parameter_divisibility, delta_w, send, recv): + """adasum optimizer rollback process.""" + if parameter_divisibility: # 如果参数可分割,执行以下操作 + if left_send: # 如果左边发送数据,执行发送操作,并接收数据部分 + recv_part = _send_before_receive(delta_w, send, recv) + else: # 如果右边发送数据,执行接收操作,并接收数据部分 + recv_part = _receive_before_send(delta_w, send, recv) + + recv_part = P.Squeeze()(recv_part) # 去除接收数据部分的冗余维度 + recv_part = P.Reshape()(recv_part, (-1,)) # 重塑接收数据部分和 delta_w 为一维张量 + delta_w = P.Reshape()(delta_w, (-1,)) + # 根据左右发送的条件,将接收数据部分和 delta_w 进行拼接 + if left_send: + res = P.Concat()((recv_part, delta_w)) + else: + res = P.Concat()((delta_w, recv_part)) + else: + res = delta_w # 如果参数不可分割,直接返回 delta_w + return res + +#定义了一个名为 AdaSum 的类,一个自定义的神经网络层(Cell),用于执行Adasum算法, +#该算法用于分布式数据并行训练深度学习模型。 +class AdaSum(Cell): + r""" + The Adaptive Summation, or AdaSum, is a novel algorithm for improving distributed data + parallel training of Deep Learning models. + + Args: + rank (int): Rank number. + device_number (int): Device number. + group_number (int): Group number. + parameter_tuple (Tuple(Parameter)): Tuple of parameters. + + Inputs: + - **delta_weights** (Tuple(Tensor)) - Tuple of gradients. + - **parameters** (Tuple(Parameter)) - Tuple of current parameters. + - **old_parameters** (Tuple(Parameter)) - Tuple of last parameters. + + Outputs: + - **adasum_parameters** (Tuple(Tensor)) - Tuple of parameters after adasum process. + """ + def __init__(self, rank, device_number, group_number, parameter_tuple): + super(AdaSum, self).__init__() + self.rank = rank + self.device_number = device_number + self.group_number = group_number + self.parameter_tuple = parameter_tuple + self._generate_communication_op() + self.hyper_map = C.HyperMap() + # 生成通信操作 + # 该方法用于创建Adasum算法所需的通信操作,包括发送、接收、全局归约等操作。 + def _generate_communication_op(self): + """generate communication op.""" + self.calc_times = int(math.log(self.group_number, 2)) + self.send_node = [] + self.send_list_forward = [] + self.recv_list_forward = [] + self.send_list_rollback = [] + self.recv_list_rollback = [] + self.allreduce_list = [] + self.broadcast_list = [] + self.parameter_divisibility_list = [] + self.allreduce_node_num_list = [] + last_delta_weights = [] + group_start_rank = (self.rank // self.device_number) * self.device_number + + for step in range(self.calc_times): + current_group = self.device_number * (2 ** step) + sr_target = self.rank + if (sr_target // current_group) % 2 == 0: + dest_target = sr_target + current_group + self.send_node.append(True) + else: + dest_target = sr_target - current_group + self.send_node.append(False) + + neighbor_ids = [] + group_name_last = 0 + for index in range(2 ** (step + 1)): + node_rank = self.rank // self.device_number + double_d = 2 ** (step + 1) + neighbor_id = (node_rank // double_d * double_d + index) * self.device_number + \ + self.rank % self.device_number + neighbor_ids.append(neighbor_id) + group_name_last += neighbor_id + group_name = "adasum_" + str(step) + "_" + str(group_name_last) + create_group(group_name, neighbor_ids) + + send_left = [] + send_right = [] + recv_left = [] + recv_right = [] + allreduce_node_num = () + left_delta_weights, right_delta_weights, delta_weights_divisibility = \ + self._get_delta_weights_info(last_delta_weights) + self.parameter_divisibility_list.append(delta_weights_divisibility) + weights_index = 0 + fusion_id = (step + 1) * 3 + for shape, dtype in left_delta_weights: + send_tag = self._hash(step, sr_target, weights_index) + send = Send(sr_tag=send_tag, dest_rank=dest_target, group="hccl_world_group") + send.add_prim_attr("fusion", fusion_id) + recv_tag = self._hash(step, dest_target, weights_index) + recv = Receive(sr_tag=recv_tag, src_rank=dest_target, shape=shape, dtype=dtype, + group="hccl_world_group") + recv.add_prim_attr("fusion", fusion_id) + send_left.append(send) + recv_left.append(recv) + weights_index += 1 + for shape, dtype in right_delta_weights: + send_tag = self._hash(step, sr_target, weights_index) + send = Send(sr_tag=send_tag, dest_rank=dest_target, group="hccl_world_group") + send.add_prim_attr("fusion", fusion_id + 1) + recv_tag = self._hash(step, dest_target, weights_index) + recv = Receive(sr_tag=recv_tag, src_rank=dest_target, shape=shape, dtype=dtype, + group="hccl_world_group") + recv.add_prim_attr("fusion", fusion_id + 1) + send_right.append(send) + recv_right.append(recv) + weights_index += 1 + + if self.send_node and self.send_node[-1]: + self.send_list_forward.append(send_left) + self.send_list_rollback.append(send_right) + self.recv_list_forward.append(recv_right) + self.recv_list_rollback.append(recv_left) + last_delta_weights = right_delta_weights + else: + self.send_list_forward.append(send_right) + self.send_list_rollback.append(send_left) + self.recv_list_forward.append(recv_left) + self.recv_list_rollback.append(recv_right) + last_delta_weights = left_delta_weights + + server_all_reduce = P.AllReduce("sum", group_name) + server_all_reduce.add_prim_attr("fusion", fusion_id + 2) + self.allreduce_list.append(server_all_reduce) + + for param_divisibility in delta_weights_divisibility: + if param_divisibility: + allreduce_node_num += (0,) + else: + allreduce_node_num += (2 ** (step + 1),) + self.allreduce_node_num_list.append(allreduce_node_num) + + broadcast_group = [x for x in range(group_start_rank, group_start_rank + self.device_number)] + broadcast_group_name = "broadcast_group_" + str(group_start_rank) + create_group(broadcast_group_name, broadcast_group) + for b_rank in range(len(broadcast_group)): + self.broadcast_list.append(P.Broadcast(b_rank, group=broadcast_group_name)) + self.sync_barrier = P.AllReduce("sum", group=broadcast_group_name) + # 获取梯度信息 + # 该方法用于获取梯度信息,将梯度划分为左右部分,并判断是否可分割。 + def _get_delta_weights_info(self, last_delta_weights): + """get delta weights info.""" + half_delta_weights = [] + if last_delta_weights: + half_delta_weights = last_delta_weights + else: + for parameter in self.parameter_tuple: + new_shape = [int(x) for x in parameter.shape] + half_delta_weights.append((new_shape, parameter.dtype)) + left_delta_weights = [] + right_delta_weights = [] + delta_weights_divisibility = () + for shape, dtype in half_delta_weights: + left_shape = copy.deepcopy(shape) + right_shape = copy.deepcopy(shape) + divisibility_flag = False + for i in range(len(shape)): + if shape[i] > 1: + left_shape[i] = int(shape[i] // 2) + right_shape[i] = shape[i] - int(shape[i] // 2) + divisibility_flag = True + break + left_delta_weights.append((left_shape, dtype)) + right_delta_weights.append((right_shape, dtype)) + delta_weights_divisibility += (divisibility_flag,) + return left_delta_weights, right_delta_weights, delta_weights_divisibility + # 计算哈希值 + # 该方法用于计算哈希值,用于生成通信标签。 + def _hash(self, step, target, weights_index): + target = "tag" + str(step) + str(target) + str(weights_index) + target_hash = hashlib.sha1(target.encode()).hexdigest() + hash_res = int(int(target_hash, 16) % MAX_NUM_HASH) + return hash_res + # 执行Adasum前向处理 + # 该部分代码执行Adasum算法的前向处理,包括数据的发送、接收、计算等。 + def construct(self, delta_weights, parameters, old_parameters): + forward_weights = [delta_weights] + for i in range(self.calc_times): + process_weights = self.hyper_map(F.partial(_adasum_opt_forward, self.send_node[i], self.allreduce_list[i]), + self.parameter_divisibility_list[i], self.allreduce_node_num_list[i], + self.send_list_forward[i], self.recv_list_forward[i], forward_weights[-1]) + forward_weights.append(process_weights) + for i in range(self.calc_times): + j = self.calc_times - i - 1 + process_weights = self.hyper_map(F.partial(_adasum_opt_rollback, self.send_node[j]), + self.parameter_divisibility_list[j], forward_weights[j + 1], + self.send_list_rollback[j], self.recv_list_rollback[j]) + forward_weights[j] = process_weights + adasum_parameters = self.hyper_map(F.partial(_update_parameters), delta_weights, forward_weights[0], + parameters, old_parameters) + return adasum_parameters diff --git a/mindspore/ccsrc/transform-update/array_ops_declare.cc b/mindspore/ccsrc/transform-update/array_ops_declare.cc new file mode 100644 index 00000000000..6fec3a03a81 --- /dev/null +++ b/mindspore/ccsrc/transform-update/array_ops_declare.cc @@ -0,0 +1,204 @@ +/** + * Copyright 2019 Huawei Technologies Co., Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "transform/graph_ir/op_declare/array_ops_declare.h" +#include +#include + +namespace mindspore::transform { +// const +INPUT_MAP(Const) = EMPTY_INPUT_MAP; +//杈撳叆鏄犲皠锛岃涓虹┖ +ATTR_MAP(Const) = {{"value", ATTR_DESC(value, AnyTraits())}}; +//灞炴ф槧灏勶紝灞炴alue绫诲瀷涓篈nyValue() +OUTPUT_MAP(Const) = {{0, OUTPUT_DESC(y)}}; +//杈撳嚭鏄犲皠锛寉绱㈠紩涓0 + +// Constant +INPUT_MAP(Constant) = EMPTY_INPUT_MAP; +//杈撳叆鏄犲皠锛岃涓虹┖ +ATTR_MAP(Constant) = {{"value", ATTR_DESC(value, AnyTraits())}}; +//灞炴ф槧灏勶紝灞炴alue绫诲瀷涓篈nyValue() +OUTPUT_MAP(Constant) = {{0, OUTPUT_DESC(y)}}; +//杈撳嚭鏄犲皠锛寉绱㈠紩涓0 +REG_ADPT_DESC(Constant, kNameConst, ADPT_DESC(Constant, Const)) +//娉ㄥ唽Constant鎿嶄綔鐨勯傞厤鍣ㄦ弿杩癒NameConst + +// ScalarSummary +INPUT_MAP(Summary) = {{2, INPUT_DESC(x)}}; +//杈撳叆鏄犲皠锛寈绱㈠紩涓2 +ATTR_MAP(Summary) = EMPTY_ATTR_MAP +//灞炴ф槧灏勶紝璁句负绌 +#ifndef ENABLE_SECURITY +//濡傛灉鏈畾涔塃NABLE_SECURITY瀹忓彉閲忥紝鍒欐敞鍐岄傞厤鍣ㄦ弿杩颁俊鎭 +//閫傞厤鍣ㄦ弿杩扮敤浜庡皢鐗瑰畾鎿嶄綔涓庢寚瀹氱殑閫傞厤鍣ㄥ叧鑱旓紝灏哠calarSummaryImageSummaryTensorSummaryHistogramSummary鍜孌ebug鎿嶄綔涓嶴ummary閫傞厤鍣ㄦ弿杩板叧鑱 +//鎿嶄綔鐨勫悕绉板拰prim绌洪棿涓嬬殑kPrimScalarSummary銆乲PrimImageSummary銆乲PrimTensorSummary銆乲PrimHistogramSummary鍜宬PrimDebug鐩稿尮閰 +REG_ADPT_DESC(ScalarSummary, prim::kPrimScalarSummary->name(), ADPT_DESC(Summary)) +REG_ADPT_DESC(ImageSummary, prim::kPrimImageSummary->name(), ADPT_DESC(Summary)) +REG_ADPT_DESC(TensorSummary, prim::kPrimTensorSummary->name(), ADPT_DESC(Summary)) +REG_ADPT_DESC(HistogramSummary, prim::kPrimHistogramSummary->name(), ADPT_DESC(Summary)) +#endif +REG_ADPT_DESC(Debug, prim::kPrimDebug->name(), ADPT_DESC(Summary)) +//涓嶈 ENABLE_SECURITY鏄惁瀹氫箟锛岄兘灏咲ebug鎿嶄綔涓嶴ummary閫傞厤鍣ㄦ弿杩板叧鑱 + + +// Data +INPUT_MAP(Data) = EMPTY_INPUT_MAP; +//杈撳叆鏄犲皠锛岃涓虹┖ +ATTR_MAP(Data) = EMPTY_ATTR_MAP; +//灞炴ф槧灏勶紝璁句负绌 +REG_ADPT_DESC(Data, kNameParam, ADPT_DESC(Data)) +//娉ㄥ唽Data鎿嶄綔鐨勯傞厤鍣ㄦ弿杩癒NameParam + +// Shape +INPUT_MAP(Shape) = {{1, INPUT_DESC(x)}}; +//杈撳叆鏄犲皠锛寈绱㈠紩涓1 +ATTR_MAP(Shape) = EMPTY_ATTR_MAP; +//灞炴ф槧灏勶紝璁句负绌 +OUTPUT_MAP(Shape) = {{0, OUTPUT_DESC(y)}}; +//杈撳嚭鏄犲皠锛寉绱㈠紩涓0 +REG_ADPT_DESC(Shape, kNameShape, ADPT_DESC(Shape)) +//娉ㄥ唽Shape鎿嶄綔鐨勯傞厤鍣ㄦ弿杩癒NameShape + +// GetShape +INPUT_MAP(GetShape) = EMPTY_INPUT_MAP; +//杈撳叆鏄犲皠锛岃涓虹┖ +DYN_INPUT_MAP(GetShape) = {{1, DYN_INPUT_DESC(x)}}; +//鍔ㄦ佽緭鍏ユ槧灏勶紝灏嗙储寮曚负1鐨勫姩鎬佽緭鍏ヤ笌鍚嶇О涓簒鐨勫姩鎬佽緭鍏ユ弿杩板叧鑱旇捣鏉ワ紝鐢ㄤ簬鍚庣画鎿嶄綔 +ATTR_MAP(GetShape) = EMPTY_ATTR_MAP; +//灞炴ф槧灏勶紝璁句负绌 +OUTPUT_MAP(GetShape) = {{0, OUTPUT_DESC(y)}}; +//杈撳嚭鏄犲皠锛寉绱㈠紩涓0 +REG_ADPT_DESC(GetShape, kNameGetShape, ADPT_DESC(GetShape)); +//娉ㄥ唽GetShape鎿嶄綔鐨勯傞厤鍣ㄦ弿杩癒NameGetShape + +// Reshape +INPUT_MAP(Reshape) = {{1, INPUT_DESC(x)}, {2, INPUT_DESC(shape)}}; +//杈撳叆鏄犲皠锛寈绱㈠紩涓1锛宻harp绱㈠紩涓2 +ATTR_MAP(Reshape) = EMPTY_ATTR_MAP; +//灞炴ф槧灏勶紝璁句负绌 +OUTPUT_MAP(Reshape) = {{0, OUTPUT_DESC(y)}}; +//杈撳嚭鏄犲皠锛寉绱㈠紩涓0 +REG_ADPT_DESC(Reshape, kNameReshape, ADPT_DESC(Reshape)) +//娉ㄥ唽ReShape鎿嶄綔鐨勯傞厤鍣ㄦ弿杩癒NameReShape +REG_ADPT_DESC(FlattenGrad, kNameFlattenGrad, ADPT_DESC(Reshape)) +//娉ㄥ唽FlattenGrad鎿嶄綔鐨勯傞厤鍣ㄦ弿杩発NameFlattenGrad + +// TransShape +INPUT_MAP(TransShape) = {{1, INPUT_DESC(x)}}; +//杈撳叆鏄犲皠锛寈绱㈠紩涓1 +INPUT_ATTR_MAP(TransShape) = {{2, ATTR_DESC(outShape, AnyTraits(), AnyTraits>())}}; +ATTR_MAP(TransShape) = EMPTY_ATTR_MAP; +//灞炴ф槧灏勶紝璁句负绌 +OUTPUT_MAP(TransShape) = {{0, OUTPUT_DESC(y)}}; +//杈撳嚭鏄犲皠锛寉绱㈠紩涓0 +REG_ADPT_DESC(TransShape, kNameTransShape, ADPT_DESC(TransShape)) +//娉ㄥ唽TransShape鎿嶄綔鐨勯傞厤鍣ㄦ弿杩発NameTransShape + +// MirrorPad +INPUT_MAP(MirrorPad) = {{1, INPUT_DESC(x)}, {2, INPUT_DESC(paddings)}}; +//杈撳叆鏄犲皠锛寈绱㈠紩涓1锛宲addings绱㈠紩涓2 +ATTR_MAP(MirrorPad) = {{"mode", ATTR_DESC(mode, AnyTraits())}}; +//灞炴ф槧灏勶紝灞炴ode绫诲瀷涓簊tring +OUTPUT_MAP(MirrorPad) = {{0, OUTPUT_DESC(y)}}; +//杈撳嚭鏄犲皠锛寉绱㈠紩涓0 +REG_ADPT_DESC(MirrorPad, kNameMirrorPad, ADPT_DESC(MirrorPad)) +//娉ㄥ唽MirrorPad鎿嶄綔鐨勯傞厤鍣ㄦ弿杩発NameMirrorPad + +// MirrorPadGrad +INPUT_MAP(MirrorPadGrad) = {{1, INPUT_DESC(x)}, {2, INPUT_DESC(paddings)}}; +//杈撳叆鏄犲皠锛寈绱㈠紩涓1锛宲addings绱㈠紩涓2 +ATTR_MAP(MirrorPadGrad) = {{"mode", ATTR_DESC(mode, AnyTraits())}}; +//灞炴ф槧灏勶紝灞炴ode绫诲瀷涓簊tring +OUTPUT_MAP(MirrorPadGrad) = {{0, OUTPUT_DESC(y)}}; +//杈撳嚭鏄犲皠锛寉绱㈠紩涓0 +REG_ADPT_DESC(MirrorPadGrad, kNameMirrorPadGrad, ADPT_DESC(MirrorPadGrad)) +//娉ㄥ唽MirrorPadGrad鎿嶄綔鐨勯傞厤鍣ㄦ弿杩発NameMirrorPadGrad + +// ExpandDims +INPUT_MAP(ExpandDims) = {{1, INPUT_DESC(x)}, {2, INPUT_DESC(axis)}}; +//杈撳叆鏄犲皠锛寈绱㈠紩涓1锛宎xis绱㈠紩涓2 +ATTR_MAP(ExpandDims) = EMPTY_ATTR_MAP; +//灞炴ф槧灏勶紝璁句负绌 +OUTPUT_MAP(ExpandDims) = {{0, OUTPUT_DESC(y)}}; +//杈撳嚭鏄犲皠锛寉绱㈠紩涓0 +REG_ADPT_DESC(ExpandDims, kNameExpandDims, ADPT_DESC(ExpandDims)) +//娉ㄥ唽ExpandDims鎿嶄綔鐨勯傞厤鍣ㄦ弿杩発NameExpandDims + +// Squeeze +INPUT_MAP(Squeeze) = {{1, INPUT_DESC(x)}}; +//杈撳叆鏄犲皠锛寈绱㈠紩涓1 +ATTR_MAP(Squeeze) = {{"axis", ATTR_DESC(axis, AnyTraits(), AnyTraits>())}}; +//灞炴ф槧灏勶紝灞炴xis绫诲瀷涓篿nt64_t +OUTPUT_MAP(Squeeze) = {{0, OUTPUT_DESC(y)}}; +//杈撳嚭鏄犲皠锛寉绱㈠紩涓0 +REG_ADPT_DESC(Squeeze, prim::kPrimSqueeze->name(), ADPT_DESC(Squeeze)) +//娉ㄥ唽Squeeze鎿嶄綔鐨勯傞厤鍣ㄦ弿杩発NameSqueeze杩斿洖鐨刵ame鍙橀噺 + +// ReverseSequence +INPUT_MAP(ReverseSequence) = {{1, INPUT_DESC(x)}, {2, INPUT_DESC(seq_lengths)}}; +//杈撳叆鏄犲皠锛寈绱㈠紩涓1锛宻eq_lengths绱㈠紩涓2 +ATTR_MAP(ReverseSequence) = {{"seq_dim", ATTR_DESC(seq_dim, AnyTraits())}, + {"batch_dim", ATTR_DESC(batch_dim, AnyTraits())}}; +//灞炴ф槧灏勶紝灞炴eq_dim绫诲瀷涓篿nt64_t锛屽睘鎬atch_dim绫诲瀷涓篿nt64_t +OUTPUT_MAP(ReverseSequence) = {{0, OUTPUT_DESC(y)}}; +//杈撳嚭鏄犲皠锛寉绱㈠紩涓0 +REG_ADPT_DESC(ReverseSequence, kNameReverseSequence, ADPT_DESC(ReverseSequence)) +//娉ㄥ唽ReverseSequence鎿嶄綔鐨勯傞厤鍣ㄦ弿杩発NameReverseSequence + +// EditDistance +INPUT_MAP(EditDistance) = {{1, INPUT_DESC(hypothesis_indices)}, {2, INPUT_DESC(hypothesis_values)}, + {3, INPUT_DESC(hypothesis_shape)}, {4, INPUT_DESC(truth_indices)}, + {5, INPUT_DESC(truth_values)}, {6, INPUT_DESC(truth_shape)}}; +//杈撳叆鏄犲皠锛宧ypothesis_indices绱㈠紩涓1锛宧ypothesis_values绱㈠紩涓2锛宧ypothesis_shape绱㈠紩涓3锛 +// truth_indices绱㈠紩涓4锛宼ruth_values绱㈠紩涓5,truth_shape绱㈠紩涓6 +ATTR_MAP(EditDistance) = {{"normalize", ATTR_DESC(normalize, AnyTraits())}}; +//灞炴ф槧灏勶紝灞炴ormalize绫诲瀷涓篿nt64_t +OUTPUT_MAP(EditDistance) = {{0, OUTPUT_DESC(output)}}; +//杈撳嚭鏄犲皠锛寉绱㈠紩涓0 +REG_ADPT_DESC(EditDistance, kNameEditDistance, ADPT_DESC(EditDistance)) +//娉ㄥ唽EditDistance鎿嶄綔鐨勯傞厤鍣ㄦ弿杩発NameEditDistance + +// NonZeroWithValue +INPUT_MAP(NonZeroWithValue) = {{1, INPUT_DESC(x)}}; +//杈撳叆鏄犲皠锛寈绱㈠紩涓1 +ATTR_MAP(NonZeroWithValue) = {{"transpose", ATTR_DESC(transpose, AnyTraits())}}; +//灞炴ф槧灏勶紝灞炴ranspose绫诲瀷涓篿nt64_t +OUTPUT_MAP(NonZeroWithValue) = {{0, OUTPUT_DESC(value)}, {1, OUTPUT_DESC(index)}, {2, OUTPUT_DESC(count)}}; +//杈撳嚭鏄犲皠锛寁alue绱㈠紩涓0锛宨ndex绱㈠紩涓1锛宑ount绱㈠紩涓2 +REG_ADPT_DESC(NonZeroWithValue, kNameNonZeroWithValue, ADPT_DESC(NonZeroWithValue)) +//娉ㄥ唽NonZeroWithValue鎿嶄綔鐨勯傞厤鍣ㄦ弿杩発NameNonZeroWithValue + +// NonZeroWithValueShape +INPUT_MAP(NonZeroWithValueShape) = {{1, INPUT_DESC(value)}, {2, INPUT_DESC(index)}, {3, INPUT_DESC(count)}}; +//杈撳叆鏄犲皠锛寈绱㈠紩涓1锛宨ndex绱㈠紩涓2锛宑ount绱㈠紩涓3 +ATTR_MAP(NonZeroWithValueShape) = EMPTY_ATTR_MAP; +//灞炴ф槧灏勶紝璁句负绌 +OUTPUT_MAP(NonZeroWithValueShape) = {{0, OUTPUT_DESC(out_value)}, {1, OUTPUT_DESC(out_index)}}; +//杈撳嚭鏄犲皠锛宱ut_value绱㈠紩涓0锛宱ut_index绱㈠紩涓1锛宱ut_count绱㈠紩涓2 +REG_ADPT_DESC(NonZeroWithValueShape, kNameNonZeroWithValueShape, ADPT_DESC(NonZeroWithValueShape)) +//娉ㄥ唽NonZeroWithValueShape鎿嶄綔鐨勯傞厤鍣ㄦ弿杩発NameNonZeroWithValueShape + +// Unsqueeze +INPUT_MAP(Unsqueeze) = {{1, INPUT_DESC(x)}}; +//杈撳叆鏄犲皠锛寈绱㈠紩涓1 +ATTR_MAP(Unsqueeze) = {{"axis", ATTR_DESC(axes, AnyTraits(), AnyTraits>())}}; +//灞炴ф槧灏勶紝灞炴xis绫诲瀷涓篿nt64_t +OUTPUT_MAP(Unsqueeze) = {{0, OUTPUT_DESC(y)}}; +//杈撳嚭鏄犲皠锛寉绱㈠紩涓0 +REG_ADPT_DESC(Unsqueeze, kNameUnsqueeze, ADPT_DESC(Unsqueeze)) +//娉ㄥ唽Unsqueeze鎿嶄綔鐨勯傞厤鍣ㄦ弿杩発NameUnsqueeze +} // namespace mindspore::transform diff --git a/mindspore/ccsrc/transform-update/base.py b/mindspore/ccsrc/transform-update/base.py new file mode 100644 index 00000000000..08e18ac51c0 --- /dev/null +++ b/mindspore/ccsrc/transform-update/base.py @@ -0,0 +1,538 @@ +# Copyright 2021 Huawei Technologies Co., Ltd +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ============================================================================ +"""base process""" +import os +import time +import math +import copy +import numpy as np +from scipy import linalg as la +from mindspore.context import ParallelMode +import mindspore.nn as nn +from mindspore.nn.optim import LARS +from mindspore import log as logger +from mindspore.common import Parameter +from mindspore.communication.management import get_group_size +from mindspore.train.serialization import load_checkpoint +from mindspore.parallel._utils import _get_global_rank +from mindspore.parallel._auto_parallel_context import auto_parallel_context +from .less_batch_normalization import CommonHeadLastFN + + +__all__ = ["OptimizerProcess", "ParameterProcess"] + +#这段代码定义了一个名为 OptimizerProcess 的类,用于处理优化器的一些操作, +#包括添加梯度中心化(Gradient Centralization)标签和生成新的优化器。 +class OptimizerProcess: + r""" + Process optimizer for Boost. Currently, this class supports adding GC(grad centralization) tags + and creating new optimizers. + + Args: + opt (Cell): Optimizer used. + + Examples: + >>> import numpy as np + >>> from mindspore import Tensor, Parameter, nn + >>> from mindspore import ops + >>> from mindspore.boost import OptimizerProcess + >>> + >>> class Net(nn.Cell): + ... def __init__(self, in_features, out_features): + ... super(Net, self).__init__() + ... self.weight = Parameter(Tensor(np.ones([in_features, out_features]).astype(np.float32)), + ... name='weight') + ... self.matmul = ops.MatMul() + ... + ... def construct(self, x): + ... output = self.matmul(x, self.weight) + ... return output + ... + >>> size, in_features, out_features = 16, 16, 10 + >>> network = Net(in_features, out_features) + >>> optimizer = nn.Momentum(net.trainable_params(), learning_rate=0.1, momentum=0.9) + >>> optimizer_process = OptimizerProcess(optimizer) + >>> optimizer_process.add_grad_centralization(network) + >>> optimizer = optimizer_process.generate_new_optimizer() + """ + # 初始化 OptimizerProcess + # 根据传入的优化器 opt 初始化 OptimizerProcess 实例,并保存相关参数和优化器类。 + def __init__(self, opt): + if isinstance(opt, LARS): + self.is_lars = True + self.single_opt = opt.opt + self.opt_class = type(opt.opt) + self.opt_init_args = opt.opt.init_args + self.lars_init_args = opt.init_args + self.learning_rate = opt.opt.init_learning_rate + else: + self.is_lars = False + self.single_opt = opt + self.opt_class = type(opt) + self.opt_init_args = opt.init_args + self.learning_rate = opt.init_learning_rate + self.origin_params = opt.init_params["params"] + # 构建参数字典 + # 用于构建训练网络中的参数字典。 + def build_params_dict(self, network): + r""" + Build the parameter's dict of the network. + + Args: + network (Cell): The training network. + """ + cells = network.cells_and_names() + params_dict = {} + for _, cell in cells: + for par in cell.get_parameters(expand=False): + params_dict[id(par)] = cell + return params_dict + # 构建带有梯度中心化的参数组 + # 用于构建带有梯度中心化的参数组。 + def build_gc_params_group(self, params_dict, parameters): + r""" + Build the parameter's group with grad centralization. + + Args: + params_dict (dict): The network's parameter dict. + parameters (list): The network's parameter list. + """ + group_params = [] + for group_param in parameters: + if 'order_params' in group_param.keys(): + group_params.append(group_param) + continue + params_gc_value = [] + params_value = [] + for param in group_param['params']: + if 'beta' not in param.name and 'gamma' not in param.name and 'bias' not in param.name: + param_cell = params_dict[id(param)] + if (isinstance(param_cell, nn.Conv2d) and param_cell.group > 1) or \ + isinstance(param_cell, CommonHeadLastFN): + params_value.append(param) + else: + params_gc_value.append(param) + else: + params_value.append(param) + if params_gc_value: + new_group_param = copy.deepcopy(group_param) + new_group_param['params'] = params_gc_value + new_group_param['grad_centralization'] = True + group_params.append(new_group_param) + if params_value: + new_group_param = copy.deepcopy(group_param) + new_group_param['params'] = params_value + group_params.append(new_group_param) + return group_params + # 添加梯度中心化 + # 根据传入的网络添加梯度中心化标签,修改参数组。 + def add_grad_centralization(self, network): + r""" + Add gradient centralization. + + Args: + network (Cell): The training network. + """ + params_dict = self.build_params_dict(network) + + parameters = self.origin_params + if parameters is not None and not isinstance(parameters, list): + parameters = list(parameters) + + if not parameters: + raise ValueError("Optimizer got an empty parameter list.") + + if not isinstance(parameters[0], (dict, Parameter)): + raise TypeError("Only a list of Parameter or dict can be supported.") + + if isinstance(parameters[0], Parameter): + logger.warning("Only group parameters support gradient centralization.") + return + + self.origin_params = self.build_gc_params_group(params_dict, parameters) + # 生成新的优化器 + # 根据保存的参数和初始化参数生成新的优化器实例,并返回该优化器。 + def generate_new_optimizer(self): + """Generate new optimizer.""" + if self.learning_rate is None: + self.learning_rate = self.single_opt.learning_rate + if not self.is_lars: + opt = self.opt_class(params=self.origin_params, learning_rate=self.learning_rate, **self.opt_init_args) + else: + opt = LARS(self.opt_class(params=self.origin_params, learning_rate=self.learning_rate, \ + **self.opt_init_args), **self.lars_init_args) + + return opt + +#这段代码定义了一个名为 ParameterProcess 的类,用于处理参数的一些操作,包括创建参数组和自动设置梯度分割点。 +class ParameterProcess: + r""" + Process parameter for Boost. Currently, this class supports creating group parameters + and automatically setting gradient segmentation point. + + Examples: + >>> from mindspore import Tensor, Parameter, nn + >>> import mindspore.ops as ops + >>> from mindspore.boost import OptimizerProcess + >>> + >>> class Net(nn.Cell): + ... def __init__(self, in_features, out_features): + ... super(Net, self).__init__() + ... self.weight = Parameter(Tensor(np.ones([in_features, out_features]).astype(np.float32)), + ... name='weight') + ... self.weight2 = Parameter(Tensor(np.ones([in_features, out_features]).astype(np.float32)), + ... name='weight2') + ... self.matmul = ops.MatMul() + ... self.matmul2 = ops.MatMul() + ... + ... def construct(self, x): + ... output = self.matmul(x, self.weight) + ... output2 = self.matmul2(x, self.weight2) + ... return output + output2 + ... + >>> size, in_features, out_features = 16, 16, 10 + >>> network = Net(in_features, out_features) + >>> new_parameter = net.trainable_params()[:1] + >>> parameter_process = ParameterProcess() + >>> group_params = parameter_process.generate_group_params(new_parameter, net.trainable_params()) + """ + def __init__(self): + self._parameter_indices = 1 + # 分配参数组 + # 将参数分配到不同的组,并设置梯度分割点。 + def assign_parameter_group(self, parameters, split_point=None): + r""" + Assign parameter group. + + Args: + parameters (list): The network's parameter list. + split_point (list): The gradient split point of this network. default: None. + """ + if not isinstance(parameters, (list, tuple)) or not parameters: + return parameters + + parameter_len = len(parameters) + if split_point: + split_parameter_index = split_point + else: + split_parameter_index = [parameter_len // 2] + for i in range(parameter_len): + if i in split_parameter_index: + self._parameter_indices += 1 + parameters[i].comm_fusion = self._parameter_indices + return parameters + # 生成参数组 + # 生成带有参数组的参数列表,用于优化器的设置。 + def generate_group_params(self, parameters, origin_params): + r""" + Generate group parameters. + + Args: + parameters (list): The network's parameter list. + origin_params (list): The network's origin parameter list. + """ + origin_params_copy = origin_params + if origin_params_copy is not None: + if not isinstance(origin_params_copy, list): + origin_params_copy = list(origin_params_copy) + + if not origin_params_copy: + raise ValueError("Optimizer got an empty parameter list.") + + if not isinstance(origin_params_copy[0], (dict, Parameter)): + raise TypeError("Only a list of Parameter or dict can be supported.") + + if isinstance(origin_params_copy[0], Parameter): + group_params = [{"params": parameters}] + return group_params + + group_params = [] + params_name = [param.name for param in parameters] + new_params_count = copy.deepcopy(params_name) + new_params_clone = {} + max_key_number = 0 + for group_param in origin_params_copy: + if 'order_params' in group_param.keys(): + new_group_param = copy.deepcopy(group_param) + new_group_param['order_params'] = parameters + group_params.append(new_group_param) + continue + params_value = [] + for param in group_param['params']: + if param.name in params_name: + index = params_name.index(param.name) + params_value.append(parameters[index]) + new_params_count.remove(param.name) + new_group_param = copy.deepcopy(group_param) + new_group_param['params'] = params_value + group_params.append(new_group_param) + if len(group_param.keys()) > max_key_number: + max_key_number = len(group_param.keys()) + new_params_clone = copy.deepcopy(group_param) + if new_params_count: + params_value = [] + for param in new_params_count: + index = params_name.index(param) + params_value.append(parameters[index]) + if new_params_clone: + new_params_clone['params'] = params_value + group_params.append(new_params_clone) + else: + group_params.append({"params": params_value}) + return group_params + +#该函数主要用于获取本地 PCA 矩阵的路径。 +def _get_local_pca_mat_path(weight_load_dir, pca_mat_path, n_component, device_number, network): + """ + get local pca mat path. + + Args: + weight_load_dir (str): The weight(ckpt) file directory to be load. + pca_mat_path (str): the path to load pca mat. Default: None. + n_component (int): pca component. + device_number (int): device number. + network (Cell): The network. + """ + if pca_mat_path is not None and os.path.exists(pca_mat_path) and os.path.isfile(pca_mat_path) and \ + pca_mat_path.endswith(".npy"): + # 如果提供的 pca_mat_path 存在且是一个有效的 .npy 文件,则使用该路径 + full_pca_mat_path = pca_mat_path + pca_mat_exist = True + + else: # 否则,如果 weight_load_dir 存在且是一个目录,则构建临时的 PCA 矩阵文件路径 + if weight_load_dir is None or not os.path.exists(weight_load_dir) or not os.path.isdir(weight_load_dir): + raise ValueError("The weight_load_dir: {} is None / not exists / not directory.".format(weight_load_dir)) + + full_pca_mat_path = os.path.join(weight_load_dir, "pca_mat_temp.npy") + pca_mat_exist = False + # 构建保存 PCA 计算结束标志的文件路径 + save_pca_end_path = os.path.join(os.path.dirname(full_pca_mat_path), "save_pca_end.txt") + if os.path.exists(save_pca_end_path): + os.remove(save_pca_end_path) + # 获取全局排名(rank)和本地 PCA 矩阵路径 + rank = _get_global_rank() + local_pca_mat_path = full_pca_mat_path[:-4] + "_rank_" + str(rank) + ".npy" + if os.path.exists(local_pca_mat_path): + os.remove(local_pca_mat_path) + # 如果排名不是设备数量的倍数,则返回本地 PCA 矩阵路径 + if rank % device_number != 0: + return local_pca_mat_path + # 如果 PCA 矩阵已存在,则加载它,否则根据权重数据计算 PCA 矩阵并保存 + if pca_mat_exist: + pca_mat = np.load(full_pca_mat_path) + else: + data = _load_weights(weight_load_dir, network) + pca_mat = _compute_pca_mat(data, n_component) + np.save(full_pca_mat_path, pca_mat) + # 保存本地 PCA 矩阵 + _save_local_pca_mat(pca_mat, full_pca_mat_path, n_component) + return local_pca_mat_path + +#该函数用于加载权重(ckpt)文件中的参数数据。 +def _load_weights(weight_load_dir, network): + """ + load weights. + + Args: + weight_load_dir (str): The weight(ckpt) file directory to be load. + network (Cell): The network. + """ + # 获取网络中需要梯度的参数列表 + param_requires_grad_list = [] + for param in network.trainable_params(): + param_requires_grad_list.append(param.name) + # 初始化参数矩阵元组 + param_mat_tuple = () + # 获取权重文件列表 + weight_file_list = os.listdir(weight_load_dir) + for file in weight_file_list: # 遍历权重文件列表 + if not file.endswith('.ckpt'): + continue + file_path = os.path.join(weight_load_dir, file) + # 加载权重文件 + param_dict = load_checkpoint(file_path) + param_tuple = () + for key, value in param_dict.items(): # 遍历加载的参数字典 + if key in param_requires_grad_list: + param_tuple += (value.asnumpy().reshape((1, -1)),) # 如果参数需要梯度,则将其转换为 NumPy 数组并添加到元组中 + param = np.concatenate(param_tuple, axis=1) # 将参数元组合并为一个参数矩阵,并添加到元组中 + param_mat_tuple += (param,) + param_mat = np.concatenate(param_mat_tuple, axis=0) # 将所有加载的参数矩阵合并为一个参数矩阵 + return param_mat + +#该函数用于计算PCA(主成分分析)的变换矩阵。 +def _compute_pca_mat(data, n_component, randomized=True): + """ + compute pca mat. + + Args: + data (array): array-like of shape (n_samples, n_features) + Training data, where `n_samples` is the number of samples + and `n_features` is the number of features. + n_component (int): pca component. + randomized (bool) if use randomized svd. + """ + if data.shape[0] < n_component: + raise ValueError("The samples: {} is less than: n_component {}.".format(data.shape[0], n_component)) + + if randomized: # 使用随机化的SVD计算PCA主成分 + components = _randomized_svd(data, n_component) + else: # 使用标准的SVD计算PCA主成分 + components = _full_svd(data, n_component) + + return components + +#该函数用于使用随机化的SVD方法计算PCA主成分。 +def _randomized_svd(data, n_component, n_oversample=10, n_iter=1): + """ + compute pca mat use randomized svd. + + Args: + data (array): array-like of shape (n_samples, n_features) + Training data, where `n_samples` is the number of samples + and `n_features` is the number of features. + n_component (int): pca component. + n_oversample (int): oversample num + n_iter (int): iteration count + """ + mean = np.mean(data, axis=0) # 计算数据的均值,并将数据居中化 + data -= mean + n_random = n_component + n_oversample # 计算超采样后的随机向量数目 + n_samples, n_features = data.shape # 获取数据的样本数量和特征数量 + transpose = n_samples < n_features # 判断是否需要转置数据矩阵 + if transpose: + data = data.T + q_mat = _randomized_range_finder(data, n_random, n_iter) # 使用随机化的方法获取样本空间的随机投影矩阵(Q矩阵) + b_mat = q_mat.T @ data # 计算B矩阵,B = Q^T * X + u_hat, _, vt_mat = la.svd(b_mat, full_matrices=False) # 对B矩阵进行SVD分解,得到U_hat和V^T矩阵 + del b_mat + # 计算最终的U矩阵和V^T矩阵 + u_mat = np.dot(q_mat, u_hat) + u_mat, vt_mat = _svd_flip(u_mat, vt_mat, transpose) + if transpose: # 如果需要转置,返回U矩阵的转置;否则返回V^T矩阵的前n_component行 + components = u_mat[:, :n_component].T + else: + components = vt_mat[:n_component, :] + return components + +#该函数用于使用标准的SVD(Singular Value Decomposition)方法计算PCA主成分。 +def _full_svd(data, n_component): + """ + compute pca mat use full svd. + + Args: + data (array): array-like of shape (n_samples, n_features) + Training data, where `n_samples` is the number of samples + and `n_features` is the number of features. + n_component (int): pca component. + """ + mean = np.mean(data, axis=0) # 计算数据的均值,并将数据居中化 + data -= mean + u, _, v = la.svd(data, full_matrices=False) # 使用标准SVD方法分解数据矩阵,得到U、S和V^T矩阵 + _, v = _svd_flip(u, v) # 调整U和V^T矩阵的方向 + components = v[:n_component] # 选取前n_component个主成分,返回V^T的前n_component行 + return components + +#该函数用于使用随机SVD方法计算PCA主成分。 +def _randomized_range_finder(data, size, n_iter=1): + """ + compute pca mat use randomized svd. + + Args: + data (array): array-like of shape (n_samples, n_features) + Training data, where `n_samples` is the number of samples + and `n_features` is the number of features. + size (int): n_component + n_oversample. + n_iter (int): iteration count + """ + # 随机生成一个初始的Q矩阵,其形状为 (n_features, size) + q_mat = np.random.normal(size=(data.shape[1], size)) + # 进行多次迭代,更新Q矩阵 + for _ in range(n_iter): + q_mat, _ = la.lu(data @ q_mat, permute_l=True) # 使用LU分解对 data @ Q 进行正交化 + q_mat, _ = la.lu(data.T @ q_mat, permute_l=True) # 使用LU分解对 data^T @ Q 进行正交化 + + q_mat, _ = la.qr(data @ q_mat, mode="economic") # 对 data @ Q 进行经济型QR分解,得到正交矩阵Q + return q_mat + +#该函数用于在SVD(奇异值分解)计算中对结果进行翻转,以确保奇异向量(左奇异向量和右奇异向量)的一致性。 +def _svd_flip(u, v, transpose=True): + """ + svd flip. + + Args: + u (ndarray): the output of `linalg.svd`. + v (ndarray): the output of `linalg.svd`. + transpose (bool): if data is transposed. + """ + if not transpose: + # 如果数据未转置,则根据左奇异向量的最大绝对值元素的符号来翻转左奇异向量和右奇异向量 + max_abs_cols = np.argmax(np.abs(u), axis=0) + signs = np.sign(u[max_abs_cols, range(u.shape[1])]) + u *= signs + v *= signs[:, np.newaxis] + else: + # 如果数据已经转置,则根据右奇异向量的最大绝对值元素的符号来翻转左奇异向量和右奇异向量 + max_abs_rows = np.argmax(np.abs(v), axis=1) + signs = np.sign(v[range(v.shape[0]), max_abs_rows]) + u *= signs + v *= signs[:, np.newaxis] + return u, v + +#该函数用于将 PCA(主成分分析)矩阵保存到本地文件中。 +def _save_local_pca_mat(pca_mat, full_pca_mat_path, n_component): + """ + save pca mat. + + Args: + pca_mat (numpy.ndarray): pca mat to be saved. + full_pca_mat_path (str): the path of full pca mat. + n_component (int): pca component. + """ + parallel_mode = auto_parallel_context().get_parallel_mode() + rank_size = 1 if parallel_mode == ParallelMode.STAND_ALONE else get_group_size() + local_dim = math.ceil(n_component / rank_size) + for rank_id in range(rank_size): + start_index = rank_id * local_dim + end_index = (rank_id + 1) * local_dim + pca_start_index = min(n_component, start_index) + pca_end_index = min(n_component, end_index) + p_local = np.zeros([local_dim, pca_mat.shape[1]]) + if pca_start_index != pca_end_index: + p_local[0: pca_end_index - pca_start_index, :] = pca_mat[pca_start_index: pca_end_index, :] + local_pca_mat_path = full_pca_mat_path[:-4] + "_rank_" + str(rank_id) + ".npy" + np.save(local_pca_mat_path, p_local) + save_pca_end_path = os.path.join(os.path.dirname(full_pca_mat_path), "save_pca_end.txt") + os.mknod(save_pca_end_path) + +#该函数用于从本地加载 PCA(主成分分析)矩阵。 +def _load_local_pca_mat(local_pca_mat_path, timeout): + """ + load pca mat. + + Args: + local_pca_mat_path (str): local pca mat file path. + """ + save_pca_end_path = os.path.join(os.path.dirname(local_pca_mat_path), "save_pca_end.txt") + start_time = time.time() + while True: + current_time = time.time() + if (current_time - start_time) > timeout: + raise RuntimeError("the time of waiting to load local pca mat is larger than {} second.".format(timeout)) + if os.path.exists(save_pca_end_path): + break + time.sleep(5) + pca_mat = np.load(local_pca_mat_path) + return pca_mat diff --git a/mindspore/ccsrc/transform-update/boost.py b/mindspore/ccsrc/transform-update/boost.py new file mode 100644 index 00000000000..e4caa9f2e61 --- /dev/null +++ b/mindspore/ccsrc/transform-update/boost.py @@ -0,0 +1,409 @@ +# Copyright 2021 Huawei Technologies Co., Ltd +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ============================================================================ +"""boost""" +import threading +from mindspore.nn.optim import SGD +from .less_batch_normalization import LessBN +from .grad_freeze import GradientFreeze +from .base import OptimizerProcess, ParameterProcess +from .base import _get_local_pca_mat_path + + +__all__ = ["AutoBoost"] + +_boost_config_mode = ["auto", "manual", "enable_all", "disable_all"] +_boost_config_level = { + "O0": { + "less_bn": False, + "grad_freeze": False, + "adasum": False, + "grad_accumulation": False, + "dim_reduce": False}, + "O1": { + "less_bn": True, + "grad_freeze": True, + "adasum": False, + "grad_accumulation": False, + "dim_reduce": False}, + "O2": { + "less_bn": True, + "grad_freeze": True, + "adasum": True, + "grad_accumulation": False, + "dim_reduce": False}} + +#定义了一个名为 AutoBoost 的类,用于提供自动加速网络训练和评估的功能。 +class AutoBoost: + r""" + Provide auto accelerating for network. + + Args: + level (str): Boost config level. Default: "O0". + boost_config_dict (dict): User config hyperparameter dict, recommended config format: + + .. code-block:: + + { + "boost": { + "mode": "auto", + "less_bn": False, + "grad_freeze": False, + "adasum": False, + "grad_accumulation": False, + "dim_reduce": False + }, + "common": { + "gradient_split_groups": [50, 100], + "device_number": 8 + }, + "less_bn": { + "fn_flag": True, + "gc_flag": True + }, + "grad_freeze": { + "param_groups": 10, + "freeze_type": 1, + "freeze_p": 0.7, + "total_steps": 65536 + } + "grad_accumulation": { + "grad_accumulation_step": 1 + }, + "dim_reduce": { + "rho": 0.55, + "gamma": 0.9, + "alpha": 0.001, + "sigma": 0.4, + "n_components": 32, + "pca_mat_path": None, + "weight_load_dir": None, + "timeout": 1800 + } + } + + - boost: + + - mode (str): How to set the boost. Supports ["auto", "manual", "enable_all", "disable_all"]. + Default: "auto". + + - auto: Depend on the argument "boost_level" in class Model. + - manual: Depend on "boost_config_dict". + - enable_all: Set all boost functions true. + - disable_all: Set all boost functions false. + + - less_bn (bool): Whether to apply less_bn function. Default: False. + - grad_freeze: (bool): Whether to apply grad_freeze function. Default: False. + - adasum (bool): Whether to apply adasum function. Default: False. + - grad_accumulation (bool): Whether to apply grad_accumulation function. Default: False. + - dim_reduce (bool): Whether to apply dim_reduce function. Default: False. + + If set dim_reduce true, other functions will be false. + If set grad_freeze true and dim_reduce false, other functions will be false. + + - common: + + - gradient_split_groups (list): The gradient split point of this network. Default: [50, 100]. + - device_number (int): Device number. Default: 8. + + - less_bn: + + - fn_flag (bool): Whether changing fc to fn. Default: True. + - gc_flag (bool): Whether to apply gc. Default: True. + + - grad_freeze: + + - param_groups (int): The number of parameter groups. Default: 10. + - freeze_type (int): Gradient freeze grouping strategy, select from [0, 1]. Default: 1. + - freeze_p (float): Gradient freezing probability. Default: 0.7. + - total_steps (int): Total training steps. Default: 65536. + + - grad_accumulation: + + - grad_accumulation_step (int): Steps to accumulate gradients. Default: 1. + + - dim_reduce: + + The leading principles of dim_reduce: + + .. math:: + + \begin{align} + grad\_k &= pca\_mat \cdot grad\\ + dk &= - bk \cdot grad\_k\\ + sk &= rho ^ m \cdot dk\\ + delta\_loss &= sigma \cdot grad\_k.T \cdot sk + \end{align} + + Here: + + - pca_mat (array): Shape (k*n), k is part of n_components, n is the size of weight. + - bk (array): Shape (k*k), is the symmetric positive definite matrix in Quasi-Newton method. + + we need to find the m satisfy: + + .. math:: + new\_loss < old\_loss + delta\_loss + + Then, get delta_grad to update the weights for model: + + .. math:: + + \begin{align} + grad\_k\_proj &= pca\_mat.T \cdot grad\_k\\ + new\_grad\_momentum &= gamma \cdot old\_grad\_momentum + grad - grad\_k\_proj\\ + delta\_grad &= alpha \cdot new\_grad\_momentum - pca\_mat.T \cdot sk + \end{align} + + - rho (float): Generally, it does not need to be modified. Default: 0.55. + - gamma (float): Generally, it does not need to be modified. Default: 0.9. + - alpha (float): Generally, it does not need to be modified. Default: 0.001. + - sigma (float): Generally, it does not need to be modified. Default: 0.4. + - n_components (int): PCA component. Default: 32. + - pca_mat_path (str): The path to load pca mat. Default: None. + - weight_load_dir (str): The directory to load weight files saved as ckpt. Default: None. + - timeout (int): Waiting time to load local pca mat. Default: 1800 (second). + + User can load the config through the JSON file or use the dictionary directly. + The unconfigured parameters will adopt the default values. + + Raises: + ValueError: The boost mode not in ["auto", "manual", "enable_all", "disable_all"]. + + Supported Platforms: + ``Ascend`` + + Examples: + >>> from mindspore.boost import AutoBoost + >>> #1) when configuring the dict directly: + >>> boost_config_dict = {"boost": {"mode": "auto"}} + >>> boost = AutoBoost("O1", boost_config_dict) + >>> + >>> #2) when loading the dict from a json file: + >>> import json + >>> boost_json = "/path/boost_config.json" + >>> with open(boost_json, 'r') as fp: + >>> boost_config_dict = json.load(fp) + >>> boost = AutoBoost("O1", boost_config_dict) + """ + _instance_lock = threading.Lock() + _instance = None + + def __init__(self, level="O0", boost_config_dict=""): + if level not in _boost_config_level.keys(): + level = "O0" + if self._instance.level is None: + # 初始化一些默认参数和配置 + self.level = level + self.boost_config_dict = boost_config_dict + self._fn_flag = True + self._gc_flag = True + self._param_groups = 10 + self._freeze_type = 1 + self._freeze_p = 0.7 + self._total_steps = 65536 + self.gradient_groups = None + self.device_number = 8 + self.grad_accumulation_step = 1 + self.rho = 0.55 + self.gamma = 0.9 + self.alpha = 0.001 + self.sigma = 0.4 + self.n_components = 32 + self.pca_mat_path = None + self.weight_load_dir = None + self.local_pca_mat_path = None + self.timeout = 1800 + self.boost_config = self._get_configuration(level, self.boost_config_dict) + self._param_processer = ParameterProcess() + + # pylint: disable=unused-argument + def __new__(cls, *args, **kwargs): + if AutoBoost._instance is None: + with AutoBoost._instance_lock: + if AutoBoost._instance is None: + AutoBoost._instance = object.__new__(cls) + AutoBoost._instance.level = None + AutoBoost._instance.boost_config_dict = None + return AutoBoost._instance + + # 根据配置自动处理网络训练 + def network_auto_process_train(self, network, optimizer): + r""" + Boost network train. + + Args: + network (Cell): The training network. + optimizer (Cell): Optimizer for updating the weights. + """ + # 如果启用了维度降低功能 + if self.boost_config["dim_reduce"]: + # 获取本地PCA矩阵的路径 + self.local_pca_mat_path = _get_local_pca_mat_path(self.weight_load_dir, self.pca_mat_path, + self.n_components, self.device_number, network) + optimizer = SGD(network.trainable_params(), learning_rate=1) # 创建一个新的SGD优化器,用于维度降低 + setattr(optimizer, "dim_reduce", True) # 设置优化器的dim_reduce属性为True + return network, optimizer # 返回修改后的网络和优化器 + # 如果启用了减少Batch Normalization(LessBN)功能 + if self.boost_config["less_bn"]: + network = LessBN(network, fn_flag=self._fn_flag) # 使用LessBN对网络进行修改,可以选择是否改变全连接层为全标准化层 + optimizer_process = OptimizerProcess(optimizer) # 创建优化器处理器 + group_params = self._param_processer.assign_parameter_group(network.trainable_params(), # 将网络的可训练参数分组 + self.gradient_groups) + # 生成新的参数组并更新优化器参数 + optimizer_process.origin_params = \ + self._param_processer.generate_group_params(group_params, optimizer_process.origin_params) + if self._gc_flag: # 如果启用了梯度中心化(grad centralization),则进行梯度中心化处理 + optimizer_process.add_grad_centralization(network) + optimizer = optimizer_process.generate_new_optimizer() # 生成新的优化器 + # 如果启用了梯度冻结(Gradient Freeze)功能 + if self.boost_config["grad_freeze"]: + # 创建梯度冻结处理器 + freeze_processer = GradientFreeze(self._param_groups, self._freeze_type, + self._freeze_p, self._total_steps) + network, optimizer = freeze_processer.freeze_generate(network, optimizer) # 对网络和优化器进行梯度冻结处理 + # 如果启用了Adasum功能 + if self.boost_config["adasum"]: + setattr(optimizer, "adasum", True) # 设置优化器的adasum属性为True + return network, optimizer # 返回修改后的网络和优化器 + + # 根据配置自动处理网络评估 + def network_auto_process_eval(self, network): + r""" + Boost network eval. + + Args: + network (Cell): The inference network. + """ + # 如果启用了维度降低功能 + if self.boost_config["dim_reduce"]: + return network # 直接返回网络,不进行其他处理 + # 如果启用了减少Batch Normalization(LessBN)功能 + if self.boost_config["less_bn"]: + network = LessBN(network) # 使用LessBN对网络进行修改 + + return network # 返回修改后的网络 + + def set_fn_flag(self, fn_flag): + self._fn_flag = fn_flag + + def set_gc_flag(self, gc_flag): + self._gc_flag = gc_flag + + def set_param_groups(self, param_groups): + self._param_groups = param_groups + + def set_freeze_type(self, freeze_type): + self._freeze_type = freeze_type + + def set_freeze_p(self, freeze_p): + self._freeze_p = freeze_p + + def set_total_steps(self, total_steps): + self._total_steps = total_steps + + def set_device_number(self, device_number): + self.device_number = device_number + + def set_grad_accumulation_step(self, grad_accumulation_step): + self.grad_accumulation_step = grad_accumulation_step + + def set_gradient_split_groups(self, gradient_groups): + # 检查梯度分割点是否为列表或整数,如果不是则引发值错误异常 + if not isinstance(gradient_groups, (list, int)): + raise ValueError(f"gradient_groups `{gradient_groups}` is not in (list, int)") + # 如果传入的是整数,将其转换为包含一个元素的列表 + if isinstance(gradient_groups, int): + gradient_groups = list(gradient_groups) + self.gradient_groups = gradient_groups # 设置梯度分割点组 + + def set_rho(self, rho): + self.rho = rho + + def set_gamma(self, gamma): + self.gamma = gamma + + def set_alpha(self, alpha): + self.alpha = alpha + + def set_sigma(self, sigma): + self.sigma = sigma + + def set_n_components(self, n_components): + self.n_components = n_components + + def set_pca_mat_path(self, pca_mat_path): + self.pca_mat_path = pca_mat_path + + def set_weight_load_dir(self, weight_load_dir): + self.weight_load_dir = weight_load_dir + + def set_timeout(self, timeout): + self.timeout = timeout + + def _get_configuration(self, level, boost_config_dict): + """Get configuration.""" + level_config = _boost_config_level[level] # 获取指定级别的配置信息 + if not boost_config_dict: # 如果没有提供用户配置字典,直接返回指定级别的配置信息 + return level_config + + mode = "auto" # 默认使用 "auto" 模式 + # 检查用户配置字典中是否包含 boost 配置,以及是否有指定 boost 模式 + if 'boost' in boost_config_dict and 'mode' in boost_config_dict['boost']: + mode = boost_config_dict['boost']['mode'] + # 检查 boost 模式是否合法,如果不合法则引发值错误异常 + if mode not in _boost_config_mode: + raise ValueError("The boost mode must be in {}, but got {}".format(_boost_config_mode, mode)) + # 根据不同的模式来处理配置信息 + if mode == "manual": + # 如果是手动模式,使用用户提供的 boost 配置来覆盖默认配置 + for key, value in boost_config_dict["boost"].items(): + if key in level_config: + level_config[key] = value + elif mode == "enable_all": # 如果是启用所有模式,将所有 boost 配置项设置为 True + level_config = {key: True for key in level_config} + elif mode == "disable_all": # 如果是禁用所有模式,将所有 boost 配置项设置为 False + level_config = {key: False for key in level_config} + # 提取有效的 boost 配置信息,只保留在指定级别中的配置项和 "common" 中的配置项 + valid_boost_each_mode_config = [] + for key, boost_each_mode_config in boost_config_dict.items(): + if key in level_config.keys() and level_config[key] or key == "common": + valid_boost_each_mode_config.append(boost_each_mode_config) + # 遍历有效的配置信息,根据配置项调用对应的设置方法 + for boost_each_mode_config in valid_boost_each_mode_config: + for key_s in boost_each_mode_config.keys(): + if key_s in self._boost_config_func_map: + self._boost_config_func_map[key_s](self, boost_each_mode_config[key_s]) + # 返回最终的配置信息 + return level_config + + _boost_config_func_map = { + "fn_flag": set_fn_flag, + "gc_flag": set_gc_flag, + "param_groups": set_param_groups, + "freeze_type": set_freeze_type, + "freeze_p": set_freeze_p, + "total_steps": set_total_steps, + "device_number": set_device_number, + "gradient_split_groups": set_gradient_split_groups, + "grad_accumulation_step": set_grad_accumulation_step, + "rho": set_rho, + "gamma": set_gamma, + "alpha": set_alpha, + "sigma": set_sigma, + "n_components": set_n_components, + "pca_mat_path": set_pca_mat_path, + "weight_load_dir": set_weight_load_dir, + "timeout": set_timeout + } diff --git a/mindspore/ccsrc/transform-update/boost_cell_wrapper.py b/mindspore/ccsrc/transform-update/boost_cell_wrapper.py new file mode 100644 index 00000000000..30750411257 --- /dev/null +++ b/mindspore/ccsrc/transform-update/boost_cell_wrapper.py @@ -0,0 +1,585 @@ +# Copyright 2021 Huawei Technologies Co., Ltd +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ============================================================================ +"""Boost Mode Cell Wrapper.""" +from mindspore.nn.wrap import TrainOneStepCell +import mindspore.context as context +from mindspore.context import ParallelMode +from mindspore.parallel._utils import _get_global_rank, _get_device_num, _get_gradients_mean +from mindspore.communication.management import get_group_size, create_group +from mindspore.nn.cell import Cell +from mindspore.common import Tensor, RowTensor +from mindspore.common.parameter import Parameter, ParameterTuple +from mindspore.nn.wrap.grad_reducer import DistributedGradReducer +from mindspore.ops import functional as F +from mindspore.ops import composite as C +from mindspore.ops import operations as P +from mindspore.common import dtype as mstype +from .boost import AutoBoost +from .grad_freeze import FreezeOpt, freeze_cell +from .adasum import AdaSum +from .dim_reduce import DimReduce +from .grad_accumulation import gradient_accumulation_op, gradient_clear_op +from .base import _load_local_pca_mat + + +__all__ = ["BoostTrainOneStepCell", "BoostTrainOneStepWithLossScaleCell"] + + +_get_delta_weight = C.MultitypeFuncGraph("_get_delta_weight") + + +@_get_delta_weight.register("Tensor", "Tensor") +def _get_delta_weight_process(new_parameter, old_parameter): + delta_w = old_parameter - new_parameter + return delta_w + + +_save_weight = C.MultitypeFuncGraph("_save_weight") + + +@_save_weight.register("Tensor", "Tensor") +def _save_weight_process(new_parameter, old_parameter): + return P.Assign()(new_parameter, old_parameter) + + +_grad_scale = C.MultitypeFuncGraph("grad_scale") +reciprocal = P.Reciprocal() + + +@_grad_scale.register("Tensor", "Tensor") +def tensor_grad_scale(scale, grad): + return grad * F.cast(reciprocal(scale), F.dtype(grad)) + + +@_grad_scale.register("Tensor", "RowTensor") +def tensor_grad_scale_row_tensor(scale, grad): + return RowTensor(grad.indices, + grad.values * F.cast(reciprocal(scale), F.dtype(grad.values)), + grad.dense_shape) + + +_grad_overflow = C.MultitypeFuncGraph("_grad_overflow") +grad_overflow = P.FloatStatus() + + +@_grad_overflow.register("Tensor") +def _tensor_grad_overflow(grad): + return grad_overflow(grad) + + +@_grad_overflow.register("RowTensor") +def _tensor_grad_overflow_row_tensor(grad): + return grad_overflow(grad.values) + +#一个名为 BoostTrainOneStepCell 的类,用于包装训练网络和优化器,实现训练过程中的一步。 +class BoostTrainOneStepCell(TrainOneStepCell): + r""" + Boost Network training package class. + + Wraps the network with an optimizer. The resulting Cell is trained with input '\*inputs'. + The backward graph will be created in the construct function to update the parameter. Different + parallel modes are available for training. + + Args: + network (Cell): The training network. The network only supports single output. + optimizer (Union[Cell]): Optimizer for updating the weights. + sens (numbers.Number): The scaling number to be filled as the input of backpropagation. Default value is 1.0. + + Inputs: + - **(\*inputs)** (Tuple(Tensor)) - Tuple of input tensors with shape :math:`(N, \ldots)`. + + Outputs: + Tensor, a tensor means the loss value, the shape of which is usually :math:`()`. + + Raises: + TypeError: If `sens` is not a number. + + Supported Platforms: + ``Ascend`` ``GPU`` ``CPU`` + + Examples: + >>> from mindspore import boost + >>> net = Net() + >>> loss_fn = nn.SoftmaxCrossEntropyWithLogits() + >>> optim = nn.Momentum(net.trainable_params(), learning_rate=0.1, momentum=0.9) + >>> #1) Using the WithLossCell existing provide + >>> loss_net = nn.WithLossCell(net, loss_fn) + >>> train_net = boost.BoostTrainOneStepCell(loss_net, optim) + >>> + >>> #2) Using user-defined WithLossCell + >>> class MyWithLossCell(Cell): + ... def __init__(self, backbone, loss_fn): + ... super(MyWithLossCell, self).__init__(auto_prefix=False) + ... self._backbone = backbone + ... self._loss_fn = loss_fn + ... + ... def construct(self, x, y, label): + ... out = self._backbone(x, y) + ... return self._loss_fn(out, label) + ... + ... @property + ... def backbone_network(self): + ... return self._backbone + ... + >>> loss_net = MyWithLossCell(net, loss_fn) + >>> train_net = boost.BoostTrainOneStepCell(loss_net, optim) + """ + + def __init__(self, network, optimizer, sens=1.0): + # 调用父类的初始化方法,传入训练网络(network)、优化器(optimizer)和敏感度(sens) + super(BoostTrainOneStepCell, self).__init__(network, optimizer, sens) + self.hyper_map = C.HyperMap() # 创建一个超映射(HyperMap)对象,用于并行操作 + self.freeze = isinstance(optimizer, FreezeOpt) # 检查是否使用梯度冻结(FreezeOpt),如果不是则获取优化器的参数作为权重 + if not self.freeze: + self.weights = self.optimizer.parameters + self.train_strategy = getattr(self.optimizer, 'train_strategy', None) # 获取优化器的训练策略(train_strategy),默认为None + # 检查是否在数据并行或独立模式下使用梯度累积,并根据配置信息设置相应的参数 + auto_boost = AutoBoost() + self.use_grad_accumulation = self.parallel_mode in (ParallelMode.DATA_PARALLEL, ParallelMode.STAND_ALONE) + self.use_grad_accumulation = self.use_grad_accumulation & auto_boost.boost_config["grad_accumulation"] + self.max_accumulation_step = 1 + if self.use_grad_accumulation: + self.max_accumulation_step = auto_boost.grad_accumulation_step + if self.max_accumulation_step <= 1: + self.max_accumulation_step = 1 + self.use_grad_accumulation = False + # 如果启用梯度累积,则创建用于累积梯度的参数和变量 + self.accumulation_step = Parameter(Tensor(0, dtype=mstype.int32), name="accumulation_step") + if self.use_grad_accumulation: + self.grad_accumulation = self.weights.clone(prefix="grad_accumulation", init='zeros') + # 检查是否启用维度降维(dimension reduction) + self.enable_dim_reduce = self.check_dim_reduce_enable() + if self.enable_dim_reduce: + local_pca_mat_path = auto_boost.local_pca_mat_path + rho = auto_boost.rho + gamma = auto_boost.gamma + alpha = auto_boost.alpha + sigma = auto_boost.sigma + _rank = _get_global_rank() + _rank_size = 1 if self.parallel_mode == ParallelMode.STAND_ALONE else get_group_size() + _device_number = auto_boost.device_number + n_components = auto_boost.n_components + timeout = auto_boost.timeout + pca_mat = _load_local_pca_mat(local_pca_mat_path, timeout) # 加载本地的 PCA 矩阵 + self.weights_clone = ParameterTuple(self.weights).clone(prefix="weights_clone", init="same") # 创建权重的克隆,用于维度降维 + # 创建维度降维对象 + self.dim_reduce = DimReduce(self.network, self.optimizer, self.weights, pca_mat, n_components, rho, gamma, + alpha, sigma, _rank, _rank_size) + # 初始化梯度冻结相关的参数和变量 + self.freeze_nets = None + self.step = Parameter(Tensor(0, dtype=mstype.int32)) + if self.freeze: + if self.reducer_flag: + self.mean = _get_gradients_mean() + self.degree = _get_device_num() + else: + self.mean = None + self.degree = None + # 创建梯度冻结的网络 + self.freeze_nets = freeze_cell(self.reducer_flag, self.network, self.optimizer, self.sens, + self.grad, self.use_grad_accumulation, self.mean, self.degree, + self.max_accumulation_step) + # 检查是否启用 Adasum(All-reduce算法),如果启用则配置相关参数 + self.enable_adasum = self.check_adasum_enable() + self.sync_tensor = Parameter(Tensor(0, dtype=mstype.int32)) + if self.enable_adasum: + _rank = _get_global_rank() + _rank_size = get_group_size() + _device_number = auto_boost.device_number + self.device_number = _device_number + group_number = _rank_size // _device_number + # 计算当前设备的排名 + self.server_rank = _rank % _device_number + # 计算每个设备的参数范围 + parameter_rank_number = len(self.weights) // _device_number + self.start = [x * parameter_rank_number for x in range(_device_number)] + self.end = [(x + 1) * parameter_rank_number for x in range(_device_number)] + self.end[-1] = len(self.weights) + # 获取当前设备的参数 + current_weights = self.weights[self.start[self.server_rank]: self.end[self.server_rank]] + # 创建梯度的克隆对象,用于 Adasum 算法 + self.grad_clone = ParameterTuple(current_weights).clone(prefix="delta_weight") + # 创建 Adasum 算法对象 + self.adasum = AdaSum(_rank, _device_number, group_number, self.grad_clone) + # 计算每个组的参数个数 + self.degree = int(self.degree / group_number) + # 创建分组列表 + group_list = [list(range(x * self.degree, (x + 1) * self.degree)) for x in range(group_number)] + # 计算当前设备所在组的名称 + current_index = _rank // _device_number + server_group_name = "allreduce_" + str(current_index) + # 创建分布式梯度归约器 + create_group(server_group_name, group_list[current_index]) + self.grad_reducer = DistributedGradReducer(self.weights, self.mean, self.degree, group=server_group_name) + + def construct(self, *inputs): + if self.freeze: # 如果启用了梯度冻结 + loss = self.gradient_freeze_process(*inputs) # 执行梯度冻结算法的处理 + else: # 执行正常的训练过程 + loss = self.network(*inputs) # 计算网络前向传播的损失 loss + sens = F.fill(loss.dtype, loss.shape, self.sens) # 使用损失 loss 的数据类型和形状创建感知度(sens) + grads = self.grad(self.network, self.weights)(*inputs, sens) # 计算梯度 grads + grads = self.grad_reducer(grads) # 对梯度 grads 进行归约操作,以适应并行训练环境 + if self.use_grad_accumulation: # 如果启用了梯度累积 + loss = self.gradient_accumulation_process(loss, grads, sens, *inputs) # 执行梯度累积算法的处理 + else: + if self.enable_dim_reduce: # 如果启用了维度降低(Dimension Reduction) + # 执行维度降低算法的处理 + loss = F.depend(loss, self.dim_reduce(loss, grads, sens, self.weights, self.weights_clone, *inputs)) + elif self.enable_adasum: # 如果启用了 Adasum 算法 + loss = F.depend(loss, self.adasum_process(loss, grads)) # 执行 Adasum 算法的处理 + else: # 否则,执行正常的优化器更新操作 + loss = F.depend(loss, self.optimizer(grads)) + return loss # 返回损失值 loss + + def gradient_freeze_process(self, *inputs): + r""" + Gradient freeze algorithm process. + + Args: + inputs (tuple(Tensor)): Tuple of input tensors with shape :math:`(N, \ldots)`. + + Outputs: + - **loss** (Tensor) - Network loss, tensor with shape :math:`()`. + """ + if self.train_strategy is None: # 如果未定义训练策略,则使用默认的 step 和 max_index + step = self.step + max_index = len(self.freeze_nets) + else: # 否则,根据训练策略获取当前 step 和 max_index + step = self.train_strategy[self.step] + max_index = len(self.train_strategy) + loss = self.freeze_nets[step](*inputs) # 使用当前 step 执行梯度冻结网络的前向传播,并计算损失 loss + if self.step + 1 >= max_index: # 更新 step,如果超过最大索引 max_index,则重置为 0 + self.step = 0 + else: + self.step += 1 + return loss # 更新 step,如果超过最大索引 max_index,则重置为 0 + + def gradient_accumulation_process(self, loss, grads, sens, *inputs): + r""" + Gradient accumulation algorithm process. + + Args: + loss (Tensor): Tensor with shape :math:`()`. + grads (tuple(Tensor)): Tuple of gradient tensors. + sens (Tensor): Tensor with shape :math:`()`. + inputs (tuple(Tensor)): Tuple of input tensors with shape :math:`(N, \ldots)`. + + Outputs: + - **loss** (Tensor) - Network loss, tensor with shape :math:`()`. + """ + # 使用梯度累积操作将梯度累积到 grad_accumulation 张量中,并依赖于当前损失 loss + loss = F.depend(loss, self.hyper_map(F.partial(gradient_accumulation_op, self.max_accumulation_step), + self.grad_accumulation, grads)) + self.accumulation_step += 1 + + if self.accumulation_step >= self.max_accumulation_step: # 如果累积步数达到最大累积步数,则执行以下操作 + if self.enable_dim_reduce: # 如果启用了维度减少,则依赖于维度减少操作 + loss = F.depend(loss, self.dim_reduce(loss, self.grad_accumulation, sens, self.weights, + self.weights_clone, *inputs)) + elif self.enable_adasum: # 如果启用了 Adasum,则依赖于 Adasum 处理 + loss = F.depend(loss, self.adasum_process(loss, self.grad_accumulation)) + else: # 否则,使用优化器更新梯度累积 grad_accumulation + loss = F.depend(loss, self.optimizer(self.grad_accumulation)) + self.accumulation_step = 0 + + if self.accumulation_step == 0: # 如果累积步数为 0,则清除梯度累积 grad_accumulation + loss = F.depend(loss, self.hyper_map(F.partial(gradient_clear_op), self.grad_accumulation)) + + return loss # 返回损失值 loss + + def adasum_process(self, loss, grads): + r""" + Adasum algorithm process. + + Args: + loss (Tensor): Tensor with shape :math:`()`. + grads (tuple(Tensor)): Tuple of gradient tensors. + + Outputs: + - **loss** (Tensor) - Network loss, tensor with shape :math:`()`. + """ + loss = F.depend(loss, self.optimizer(grads)) # 使用优化器更新梯度 grads,并依赖于当前损失 loss + rank_weights = self.weights[self.start[self.server_rank]: self.end[self.server_rank]] # 计算当前设备的权重张量 rank_weights + grad_clone = F.depend(self.grad_clone, loss) # 依赖于损失 loss 的梯度副本 grad_clone + delta_w = self.hyper_map(F.partial(_get_delta_weight), rank_weights, grad_clone) # 使用超映射操作计算梯度变化 delta_w + adasum_res = self.adasum(delta_w, rank_weights, grad_clone) # 计算 Adasum 结果 adasum_res + sync_tensor = F.depend(self.sync_tensor, adasum_res) # 创建同步张量 sync_tensor,并依赖于 adasum_res + sync_flag = self.adasum.sync_barrier(sync_tensor) # 执行 Adasum 同步屏障操作 + # 遍历每个设备,并根据同步结果更新权重 + for i in range(self.device_number): + weight_tuple = self.weights[self.start[i]: self.end[i]] + node_rank = F.depend(weight_tuple, sync_flag) + update_weights = self.adasum.broadcast_list[i](node_rank) + if i == self.server_rank: # 如果是服务器设备,使用 grad_clone 更新权重 + self.hyper_map(F.partial(_save_weight), self.grad_clone, update_weights) + else: # 如果是其他设备,使用权重元组更新权重 + self.hyper_map(F.partial(_save_weight), weight_tuple, update_weights) + return loss # 返回损失值 loss + + #该方法用于检查是否启用 Adasum 算法。 + def check_adasum_enable(self): + r""" + Check adasum enable. + """ + if not getattr(self.optimizer, "adasum", None) or not self.reducer_flag: # 检查优化器中是否存在 adasum 属性,并且检查是否设置 reducer_flag + return False + _rank_size = get_group_size() # 获取当前训练组的总大小 + _device_number = 8 # 设定设备数量 + group_number = _rank_size // _device_number # 计算训练组的数量 + is_enable = bool(group_number > 1 and group_number & (group_number - 1) == 0) # 判断是否满足启用条件,Adasum 要求训练组数量为 2 的幂 + return is_enable + + #该方法用于检查是否启用维度降低(dim_reduce)功能。 + def check_dim_reduce_enable(self): + r""" + Check dim_reduce enable. + """ + if not getattr(self.optimizer, "dim_reduce", None): # 检查优化器中是否存在 dim_reduce 属性 + return False + return True # 如果存在 dim_reduce 属性,则表示启用了维度降低功能 + + +class BoostTrainOneStepWithLossScaleCell(BoostTrainOneStepCell): + r""" + Boost Network training with loss scaling. + + This is a training step with loss scaling. It takes a network, an optimizer and possibly a scale update + Cell as args. The loss scale value can be updated in both host side or device side. The + BoostTrainOneStepWithLossScaleCell will be compiled to be graph which takes `*inputs` as input data. + The Tensor type of `scale_sense` is acting as loss scaling value. If you want to update it on host side, + the value must be provided. If the Tensor type of `scale_sense` is not given, the loss scale update logic + must be provide by Cell type of `scale_sense`. + + Args: + network (Cell): The training network. The network only supports single output. + optimizer (Cell): Optimizer for updating the weights. + scale_sense (Union[Tensor, Cell]): If this value is Cell type, the loss scaling update logic cell.If this value + is Tensor type, Tensor with shape :math:`()` or :math:`(1,)`. + + Inputs: + - **(*inputs)** (Tuple(Tensor)) - Tuple of input tensors with shape :math:`(N, \ldots)`. + + Outputs: + Tuple of 3 Tensor, the loss, overflow flag and current loss scaling value. + + - **loss** (Tensor) - Tensor with shape :math:`()`. + - **overflow** (Tensor) - Tensor with shape :math:`()`, type is bool. + - **loss scaling value** (Tensor) - Tensor with shape :math:`()` + + Raises: + TypeError: If `scale_sense` is neither Cell nor Tensor. + ValueError: If shape of `scale_sense` is neither (1,) nor (). + + Supported Platforms: + ``Ascend`` ``GPU`` + + Examples: + >>> import numpy as np + >>> from mindspore import Tensor, Parameter, nn + >>> import mindspore.ops as ops + >>> from mindspore.nn import WithLossCell + >>> from mindspore import dtype as mstype + >>> from mindspore import boost + >>> + >>> class Net(nn.Cell): + ... def __init__(self, in_features, out_features): + ... super(Net, self).__init__() + ... self.weight = Parameter(Tensor(np.ones([in_features, out_features]).astype(np.float32)), + ... name='weight') + ... self.matmul = ops.MatMul() + ... + ... def construct(self, x): + ... output = self.matmul(x, self.weight) + ... return output + ... + >>> size, in_features, out_features = 16, 16, 10 + >>> #1) when the type of scale_sense is Cell: + >>> net = Net(in_features, out_features) + >>> loss = nn.MSELoss() + >>> optimizer = nn.Momentum(net.trainable_params(), learning_rate=0.1, momentum=0.9) + >>> net_with_loss = WithLossCell(net, loss) + >>> manager = nn.DynamicLossScaleUpdateCell(loss_scale_value=2**12, scale_factor=2, scale_window=1000) + >>> train_network = boost.BoostTrainOneStepWithLossScaleCell(net_with_loss, optimizer, scale_sense=manager) + >>> input = Tensor(np.ones([out_features, in_features]), mstype.float32) + >>> labels = Tensor(np.ones([out_features,]), mstype.float32) + >>> output = train_network(input, labels) + >>> + >>> #2) when the type of scale_sense is Tensor: + >>> net = Net(in_features, out_features) + >>> loss = nn.MSELoss() + >>> optimizer = nn.Momentum(net.trainable_params(), learning_rate=0.1, momentum=0.9) + >>> net_with_loss = WithLossCell(net, loss) + >>> inputs = Tensor(np.ones([size, in_features]).astype(np.float32)) + >>> label = Tensor(np.zeros([size, out_features]).astype(np.float32)) + >>> scaling_sens = Tensor(np.full((1), np.finfo(np.float32).max), dtype=mstype.float32) + >>> train_network = boost.BoostTrainOneStepWithLossScaleCell(net_with_loss, optimizer, scale_sense=scaling_sens) + >>> output = train_network(inputs, label) + """ + def __init__(self, network, optimizer, scale_sense): + super(BoostTrainOneStepWithLossScaleCell, self).__init__(network, optimizer, sens=None) + self.base = Tensor(1, mstype.float32) # 创建常量 Tensor,用于计算损失缩放 + self.reduce_sum = P.ReduceSum(keep_dims=False) # 创建用于 ReduceSum 操作的 P.ReduceSum 实例 + self.less_equal = P.LessEqual() # 创建用于比较操作的 P.LessEqual 实例 + self.allreduce = P.AllReduce() # 创建用于 AllReduce 操作的 P.AllReduce 实例 + self.is_distributed = (self.parallel_mode != ParallelMode.STAND_ALONE) # 检查是否是分布式训练环境 + self.gpu_target = (context.get_context("device_target") == "GPU") # 检查是否在 GPU 设备上运行 + self.loss_scaling_manager = None # 初始化损失缩放管理器 + if isinstance(scale_sense, Cell): # 如果 scale_sense 是 Cell 类型,则使用指定的损失缩放管理器 + self.loss_scaling_manager = scale_sense + self.scale_sense = Parameter(Tensor(scale_sense.get_loss_scale(), dtype=mstype.float32), + name="scale_sense") + elif isinstance(scale_sense, Tensor): # 如果 scale_sense 是 Tensor 类型,则手动指定损失缩放值 + if scale_sense.shape == (1,) or scale_sense.shape == (): # 检查损失缩放值的形状是否合法 + self.scale_sense = Parameter(scale_sense, name='scale_sense') + else: + raise ValueError("The shape of scale_sense must be (1,) or (), but got {}".format(scale_sense.shape)) + else: # 如果 scale_sense 不是合法类型,则抛出异常 + raise TypeError("The scale_sense must be Cell or Tensor, but got {}".format(type(scale_sense))) + + def construct(self, *inputs): + weights = self.weights # 获取网络权重 + loss = self.network(*inputs) # 计算网络损失 + scaling_sens = self.scale_sense # 获取损失缩放值 + + status, scaling_sens = self._start_overflow_check(loss, scaling_sens) # 开始进行损失缩放溢出检测 + + scaling_sens_filled = C.ones_like(loss) * F.cast(scaling_sens, F.dtype(loss)) # 对损失进行损失缩放 + grads = self.grad(self.network, weights)(*inputs, scaling_sens_filled) # 计算网络梯度,并根据损失缩放值对梯度进行缩放 + grads = self.hyper_map(F.partial(_grad_scale, scaling_sens), grads) + + # get the overflow buffer + # 获取溢出状态的条件标志 + cond = self._get_overflow_status(status, grads) + overflow = self._process_loss_scale(cond) # 处理损失缩放,确定是否发生溢出 + # if there is no overflow, do optimize + # 如果没有溢出,执行优化器操作 + if not overflow: + if self.use_grad_accumulation: + loss = self.gradient_accumulation_process(loss, grads, scaling_sens_filled, *inputs) + else: + if self.enable_dim_reduce: + loss = F.depend(loss, self.dim_reduce(loss, grads, scaling_sens_filled, self.weights, + self.weights_clone, *inputs)) + elif self.enable_adasum: + loss = F.depend(loss, self.adasum_process(loss, grads)) + else: + loss = F.depend(loss, self.optimizer(grads)) + return loss, cond, scaling_sens # 返回损失、溢出标志和损失缩放值 + + #该函数用于重新设置损失缩放值 + def _set_sense_scale(self, sens): + """ + If the user has set the sens in the training process and wants to reassign the value, he can call + this function again to make modification, and sens needs to be of type Tensor. + + Inputs: + - **sens** (Tensor) - The new sense whose shape and type are the same with original `scale_sense`. + """ + if self.scale_sense and isinstance(sens, Tensor): + self.scale_sense.set_data(sens) + else: + raise TypeError("The input type must be Tensor, but got {}".format(type(sens))) + + #该函数用于开始浮点溢出检测 + def _start_overflow_check(self, pre_cond, compute_input): + """ + Start floating-point overflow detection. Create and clear the overflow detection state. + + Specify the argument 'pre_cond' and 'compute_input' to make sure overflow status is cleared at the right time. + Taking this situation as an example, we need to execute state clearing after loss calculation and then detect + overflow in the process of gradient calculation. In this case, pre_cond should be the output of the loss + function, and compute_input should be the input of gradients-computing function. + + Inputs: + - **pre_cond** (Tensor) - A precondition for starting overflow detection. It determines the executing order + of overflow state clearing and prior processions. It makes sure that the function 'start_overflow' + clears status after finishing the process of precondition. + - **compute_input** (object) - The input of subsequent process. Overflow detection should be performed on a + certain computation. Set `compute_input` as the input of the computation, to ensure overflow status is + cleared before executing the computation. + + Outputs: + Tuple[object, object], the first value is False for GPU backend, while it is an instance of + NPUAllocFloatStatus for other backend. The status is used to detect overflow during overflow detection. + The second value is the same as the input of `compute_input`, but contains some information about the + execution order. + """ + status = False + if not self.gpu_target: + # init overflow buffer + # 初始化溢出缓冲区 + status = P.NPUAllocFloatStatus()() + status = F.depend(status, pre_cond) + # clear overflow buffer + # 清除溢出缓冲区 + clear_status = P.NPUClearFloatStatus()(status) + compute_input = F.depend(compute_input, clear_status) + return status, compute_input + + #该函数用于获取浮点溢出状态 + def _get_overflow_status(self, status, compute_output): + """ + Get floating-point overflow status. + + Get overflow results after executing the target process for overflow detection. + + Inputs: + - **status** (object) - A status instance used to detect the overflow. + - **compute_output** - Overflow detection should be performed on a certain computation. Set `compute_output` + as the output of the computation, to ensure overflow status is acquired before executing the + computation. + + Outputs: + bool, whether the overflow occurs or not. + """ + if not self.gpu_target: + + status = F.depend(status, compute_output) + get_status = P.NPUGetFloatStatus()(status) + status = F.depend(status, get_status) + # sum overflow buffer elements, 0:not overflow , >0:overflow + # 求和溢出缓冲区的元素,0 表示没有溢出,>0 表示溢出 + flag_sum = self.reduce_sum(status, (0,)) + else: + flag_sum = self.hyper_map(F.partial(_grad_overflow), compute_output) + flag_sum = P.AddN()(flag_sum) + # convert flag_sum to scalar + # 将 flag_sum 转换为标量 + flag_sum = P.Reshape()(flag_sum, (())) + + if self.is_distributed: + # sum overflow flag over devices + # 在设备上汇总溢出标志 + flag_reduce = self.allreduce(flag_sum) + overflow = self.less_equal(self.base, flag_reduce) + else: + overflow = self.less_equal(self.base, flag_sum) + return overflow + + #该函数用于根据溢出情况计算损失缩放比例 + def _process_loss_scale(self, overflow): + """ + Calculate loss scale according to the overflow. + + Inputs: + - **overflow** (bool) - Whether the overflow occurs or not. + + Outputs: + bool, overflow value. + """ + if self.loss_scaling_manager is not None: + return self.loss_scaling_manager(self.scale_sense, overflow) + return overflow diff --git a/mindspore/ccsrc/transform-update/browse_dataset.py b/mindspore/ccsrc/transform-update/browse_dataset.py new file mode 100644 index 00000000000..6de43a86e09 --- /dev/null +++ b/mindspore/ccsrc/transform-update/browse_dataset.py @@ -0,0 +1,197 @@ +# Copyright 2021 Huawei Technologies Co., Ltd +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ============================================================================== +"""Visualization for detection/segmentation dataset. +妫娴/鍒嗗壊鏁版嵁闆嗙殑鍙鍖栥 +""" +import os +import sys +import importlib +import numpy as np + +from mindspore import log as logger + + +def imshow_det_bbox(image, bboxes, labels, segm=None, class_names=None, score_threshold=0, bbox_color=(0, 255, 0), + text_color=(203, 192, 255), mask_color=(128, 0, 128), thickness=2, font_size=0.8, show=True, + win_name="win", wait_time=2000, out_file=None): + """Draw an image with given bboxes and class labels (with scores). + 鐢ㄧ粰瀹氱殑妗嗗拰绫绘爣绛撅紙甯﹀垎鏁帮級缁樺埗涓骞呭浘鍍忋 + Args: + image (ndarray): The image to be displayed, shaped (C, H, W) or (H, W, C), formatted RGB. + bboxes (ndarray): Bounding boxes (with scores), shaped (N, 4) or (N, 5), + data should be ordered with (N, x, y, w, h). + labels (ndarray): Labels of bboxes, shaped (N, 1). + segm (ndarray): The segmentation masks of image in M classes, shaped (M, H, W) (Default=None). + class_names (list[str], tuple[str], dict): Names of each class to map label to class name + (Default=None, only display label). + score_threshold (float): Minimum score of bboxes to be shown (Default=0). + bbox_color (tuple(int)): Color of bbox lines. + The tuple of color should be in BGR order (Default=(0, 255 ,0), means 'green'). + text_color (tuple(int)): Color of texts. + The tuple of color should be in BGR order (Default=(203, 192, 255), means 'pink'). + mask_color (tuple(int)): Color of mask. + The tuple of color should be in BGR order (Default=(128, 0, 128), means 'purple'). + thickness (int): Thickness of lines (Default=2). + font_size (int, float): Font size of texts (Default=0.8). + show (bool): Whether to show the image (Default=True). + win_name (str): The window name (Default="win"). + wait_time (int): Value of waitKey param (Default=2000, means display interval is 2000ms). + out_file (str, optional): The filename to write the imagee (Default=None). File extension name + is required to indicate the image compression type, e.g. 'jpg', 'png'. + + Returns: + ndarray: The image with bboxes drawn on it. + + Raises: + ImportError: If opencv-python is not installed. + AssertionError: If `image` is not in (H, W, C) or (C, H, W) format. + AssertionError: If `bboxes` is not in (N, 4) or (N, 5) format. + AssertionError: If `labels` is not in (N, 1) format. + AssertionError: If `segm` is not in (M, H, W) format. + AssertionError: If `class_names` is not of type list, tuple or dict. + AssertionError: If `bbox_color` is not a tuple in format of (B, G, R). + AssertionError: If `text_color` is not a tuple in format of (B, G, R). + AssertionError: If `mask_color` is not a tuple in format of (B, G, R). + + Examples: + >>> import numpy as np + >>> import mindspore.dataset as ds + >>> from mindspore.dataset.utils.browse_dataset import imshow_det_bbox + >>> + >>> # Read Detection dataset, such as VOC2012. + >>> voc_dataset_dir = "/path/to/voc_dataset_directory" + >>> dataset = ds.VOCDataset(voc_dataset_dir, task="Detection", shuffle=False, decode=True, num_samples=5) + >>> dataset_iter = dataset.create_dict_iterator(output_numpy=True, num_epochs=1) + >>> + >>> # draw dataset + >>> for index, data in enumerate(dataset_iter): + ... image = data["image"] + ... bbox = data["bbox"] + ... label = data["label"] + ... # draw image with bboxes + ... imshow_det_bbox(image, bbox, label, + ... class_names=['aeroplane', 'bicycle', 'bird', 'boat', 'bottle', 'bus', 'car', 'cat', + ... 'chair', 'cow', 'diningtable', 'dog', 'horse', 'motorbike', 'person', + ... 'pottedplant', 'sheep', 'sofa', 'train', 'tvmonitor'], + ... win_name="my_window", + ... wait_time=5000, + ... show=True, + ... out_file="voc_dataset_{}.jpg".format(str(index))) + + Examples using `imshow_det_bbox` on VOC2012: + + .. image:: browse_dataset.png + + """ + + # 瀹氫箟涓涓悕涓篿mshow_det_bbox鐨勫嚱鏁 +def imshow_det_bbox(image, bboxes, labels, segm=None, class_names=None, score_threshold=0, bbox_color=(0, 255, 0), + text_color=(203, 192, 255), mask_color=(128, 0, 128), thickness=2, font_size=0.8, show=True, + win_name="win", wait_time=2000, out_file=None): + try: + # 灏濊瘯瀵煎叆cv2妯″潡锛圤penCV搴擄級锛屽鏋滃鍏ュけ璐ワ紝鎶涘嚭ImportError寮傚父 + cv2 = importlib.import_module("cv2") + except ModuleNotFoundError: + raise ImportError("Importing cv2 failed, try to install it by running `pip install opencv-python`.") + + # 鏁版嵁楠岃瘉鍜屽弬鏁版鏌 + assert isinstance(image, np.ndarray) and image.ndim == 3 and (image.shape[0] == 3 or image.shape[2] == 3), \ + "image must be a ndarray in (H, W, C) or (C, H, W) format." + if bboxes is not None: + assert isinstance(bboxes, np.ndarray) and bboxes.ndim == 2 and (bboxes.shape[1] == 4 or bboxes.shape[1] == 5), \ + "bboxes must be a ndarray in (N, 4) or (N, 5) format." + assert isinstance(labels, np.ndarray) and labels.ndim == 2 and labels.shape[1] == 1 and \ + labels.shape[0] == bboxes.shape[ + 0], "labels must be a ndarray in (N, 1) format and has same N with bboxes." + if segm is not None: + assert isinstance(segm, np.ndarray) and segm.ndim == 3, "segm must be a ndarray in (M, H, W) format." + H, W = (image.shape[0], image.shape[1]) if image.shape[2] == 3 else (image.shape[1], image.shape[2]) + assert H == segm.shape[1] and W == segm.shape[2], "segm must has same height and width with image." + if bboxes is not None: + assert bboxes.shape[0] <= segm.shape[0], "number of segm masks must not be less than the number of bboxes." + assert isinstance(class_names, (tuple, list, dict)), "class_names must be a list, tuple or dict." + assert isinstance(bbox_color, tuple) and len(bbox_color) == 3, \ + "bbox_color must be a three tuple, formatted (B, G, R)." + assert isinstance(text_color, tuple) and len(text_color) == 3, \ + "text_color must be a three tuple, formatted (B, G, R)." + assert isinstance(mask_color, tuple) and len(mask_color) == 3, \ + "mask_color must be a three tuple, formatted (B, G, R)." + assert isinstance(thickness, int), "thickness must be an int." + assert thickness >= 0, "thickness must be larger than or equal to zero." + assert isinstance(font_size, (int, float)), "font_size must be an int or float." + assert font_size >= 0, "font_size must be larger than or equal to zero." + assert isinstance(show, bool), "show must be a bool." + assert isinstance(win_name, str), "win_name must be a str." + assert isinstance(wait_time, int), "wait_time must be an int." + assert wait_time >= 0, "wait_time must be larger than or equal to zero." + if out_file is not None: + assert isinstance(out_file, str), "out_file must be a str." + + if score_threshold > 0: + assert bboxes.shape[1] == 5 + if not show: + assert out_file is not None + + # 瀵瑰浘鍍忚繘琛屽鐞 + if image.shape[0] == 3: + image = image.transpose((1, 2, 0)) + draw_image = cv2.cvtColor(image, cv2.COLOR_RGB2BGR) + + if bboxes is not None: + bbox_num = bboxes.shape[0] + for i in range(bbox_num): + draw_bbox = bboxes[i] + if len(draw_bbox) > 4: + if draw_bbox[4] < score_threshold: + continue + # bbox + x1, y1 = int(draw_bbox[0]), int(draw_bbox[1]) + x2, y2 = int(draw_bbox[0] + draw_bbox[2]), int(draw_bbox[1] + draw_bbox[3]) + cv2.rectangle(draw_image, (x1, y1), (x2, y2), bbox_color, thickness) + # label + try: + draw_label = str(class_names[labels[i][0]]) if class_names is not None else f'class {labels[i][0]}' + except (IndexError, KeyError): + draw_label = f'class {labels[i][0]}' + if len(draw_bbox) > 4: + draw_label += f'|{draw_bbox[-1]:.02f}' + cv2.putText(draw_image, draw_label, (x1, y2), cv2.FONT_HERSHEY_SIMPLEX, font_size, text_color, thickness) + if segm is not None: + mask = segm[i].astype(bool) + draw_image[mask] = draw_image[mask] * 0.5 + np.array(mask_color) * 0.5 + else: + if segm is not None: + segm_num = segm.shape[0] + for i in range(segm_num): + mask = segm[i].astype(bool) + draw_image[mask] = draw_image[mask] * 0.5 + np.array(mask_color) * 0.5 + # 濡傛灉闇瑕佹樉绀哄浘鍍忥紝璋冪敤cv2.imshow鏂规硶鏄剧ず鍥惧儚 + if show: + cv2.imshow(win_name, draw_image) + if cv2.waitKey(wait_time) == 27: + sys.exit() + + # 濡傛灉闇瑕佷繚瀛樺浘鍍忓埌鏂囦欢锛屼娇鐢╟v2.imwrite鏂规硶淇濆瓨鍥惧儚 + if out_file: + logger.info("Saving image file with name: " + out_file + "...") + cv2.imwrite(out_file, draw_image) + os.chmod(out_file, 0o600) + + # 杩斿洖澶勭悊鍚庣殑鍥惧儚 + return draw_image + + + + diff --git a/mindspore/ccsrc/transform-update/cluster_ops_declare.cc b/mindspore/ccsrc/transform-update/cluster_ops_declare.cc new file mode 100644 index 00000000000..3a6ce21683c --- /dev/null +++ b/mindspore/ccsrc/transform-update/cluster_ops_declare.cc @@ -0,0 +1,32 @@ +/** + * Copyright 2022-2022 Huawei Technologies Co., Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "transform/graph_ir/op_declare/cluster_ops_declare.h" + +namespace mindspore::transform { +// KMeansCentroids +INPUT_MAP(KMeansCentroids) = { + {1, INPUT_DESC(x)}, {2, INPUT_DESC(y)}, {3, INPUT_DESC(sum_square_y)}, {4, INPUT_DESC(sum_square_x)}}; +//杈撳叆鏄犲皠锛寈绱㈠紩涓1锛寉绱㈠紩涓2锛宻um_square_y绱㈠紩涓3锛宻um_square_x绱㈠紩涓4 +ATTR_MAP(KMeansCentroids) = { + {"use_actual_distance", ATTR_DESC(use_actual_distance, AnyTraits(), AnyTraits())}}; +//灞炴ф槧灏勶紝灞炴se_actual_distance绫诲瀷涓篵ool +OUTPUT_MAP(KMeansCentroids) = { + {0, OUTPUT_DESC(segment_sum)}, {1, OUTPUT_DESC(segment_count)}, {2, OUTPUT_DESC(kmean_total_sum)}}; +//杈撳嚭鏄犲皠锛宻egment_sum绱㈠紩涓0锛宻egment_count绱㈠紩涓1锛宬mean_total_sum绱㈠紩涓2 +REG_ADPT_DESC(KMeansCentroids, prim::kPrimKMeansCentroids->name(), ADPT_DESC(KMeansCentroids)) +//娉ㄥ唽KMeansCentroids鎿嶄綔鐨勯傞厤鍣ㄦ弿杩発PrimKMeansCentroids棰勮鐨刵ame鍙橀噺 +} // namespace mindspore::transform diff --git a/mindspore/ccsrc/transform-update/config.py b/mindspore/ccsrc/transform-update/config.py new file mode 100644 index 00000000000..e93b826809c --- /dev/null +++ b/mindspore/ccsrc/transform-update/config.py @@ -0,0 +1,858 @@ +# Copyright 2019-2022 Huawei Technologies Co., Ltd +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ============================================================================== +""" +The configuration module provides various functions to set and get the supported +configuration parameters, and read a configuration file. + +Common imported modules in corresponding API examples are as follows: +閰嶇疆妯″潡鎻愪緵浜嗗悇绉嶅姛鑳芥潵璁剧疆鍜岃幏鍙栨敮鎸侀厤缃弬鏁帮紝骞惰鍙栭厤缃枃浠躲 +.. code-block:: + + import mindspore.dataset as ds +""" +import os +import platform +import random +import numpy +import mindspore._c_dataengine as cde +from mindspore import log as logger +from .validator_helpers import replace_none + +__all__ = ['set_sending_batches', 'load', '_init_device_info', + 'set_seed', 'get_seed', + 'set_prefetch_size', 'get_prefetch_size', + 'set_num_parallel_workers', 'get_num_parallel_workers', + 'set_numa_enable', 'get_numa_enable', + 'set_monitor_sampling_interval', 'get_monitor_sampling_interval', + 'set_callback_timeout', 'get_callback_timeout', + 'set_auto_num_workers', 'get_auto_num_workers', + 'set_enable_shared_mem', 'get_enable_shared_mem', + 'set_enable_autotune', 'get_enable_autotune', + 'set_autotune_interval', 'get_autotune_interval', + 'set_auto_offload', 'get_auto_offload', + 'set_enable_watchdog', 'get_enable_watchdog', + 'set_multiprocessing_timeout_interval', 'get_multiprocessing_timeout_interval'] + +INT32_MAX = 2147483647 +UINT32_MAX = 4294967295 + +_config = cde.GlobalContext.config_manager() + + +def _init_device_info(): + """ + INTERNAL USE ONLY! + As rank_id need to pass into deep layer for numa and device_queue. + One process work with only one rank_id, In standalone scenario, + rank_id may come from env 'CUDA_VISIBLE_DEVICES', For distribute + scenario, rank_id come from _get_global_rank(). + 鐢变簬rank_id闇瑕佷紶閫掑埌numa鍜宒evice_queue鐨勬繁灞傘 + 涓涓繘绋嬪彧浣跨敤涓涓猺ank_id锛屽湪鐙珛鍦烘櫙涓紝rank_id鍙兘鏉ヨ嚜env鈥淐UDA_VISIBLE_DEVICES鈥濓紝鐢ㄤ簬鍒嗗彂鍦烘櫙涓紝rank_id鏉ヨ嚜_get_global_rank锛堬級銆 + """ + # 瀵煎叆蹇呰鐨勬ā鍧楀拰绫 +from mindspore import context +from mindspore.parallel._auto_parallel_context import auto_parallel_context +from mindspore.parallel._utils import _get_global_rank +import os + + # 榛樿鎯呭喌涓嬬鐢 NUMA锛圢on-Uniform Memory Access锛夋敮鎸 + numa_enable = False + + # 浠庣幆澧冨彉閲忎腑鑾峰彇 NUMA_ENABLE锛圖ATASET_ENABLE_NUMA 鍜 MS_ENABLE_NUMA 涔嬩竴锛夌殑鍊硷紝濡傛灉涓 "True" 鍒欏惎鐢 NUMA + numa_enable_env = os.getenv("DATASET_ENABLE_NUMA", None) + if numa_enable_env and numa_enable_env.strip() == 'True': + numa_enable = True + + numa_enable_env = os.getenv("MS_ENABLE_NUMA", None) + if numa_enable_env and numa_enable_env.strip() == 'True': + numa_enable = True + + # 鑾峰彇褰撳墠璁惧鐨勭洰鏍囷紙GPU 鎴 Ascend锛 + device_target = context.get_context("device_target") + + # 濡傛灉璁惧鐩爣鏄 GPU + if device_target == "GPU": + # 鑾峰彇鍏ㄥ眬鎺掑悕锛坓lobal rank锛 + rank_id = _get_global_rank() + + # 鑾峰彇骞惰妯″紡锛坧arallel mode锛 + parallel_mode = auto_parallel_context().get_parallel_mode() + + # 濡傛灉骞惰妯″紡涓 "stand_alone"锛屽垯灏 rank_id 璁剧疆涓鸿澶囩殑 ID锛坉evice_id锛 + if parallel_mode == "stand_alone": + rank_id = context.get_context("device_id") + + # 濡傛灉鍚敤浜 NUMA锛屽垯璁剧疆 NUMA 鏀寔涓 True + if numa_enable: + _config.set_numa_enable(True) + + # 璁剧疆 rank_id + _config.set_rank_id(rank_id) + + # 濡傛灉璁惧鐩爣鏄 Ascend + elif device_target == "Ascend": + # Ascend 鏄竴涓壒娈婃儏鍐碉紝鏈濂戒粠鐜鍙橀噺涓幏鍙栨帓鍚嶄俊鎭 + env_rank_size = os.getenv("RANK_SIZE", None) + env_rank_id = os.getenv("RANK_ID", None) + rank_size = 0 + rank_id = 0 + + # 濡傛灉鐜鍙橀噺涓寘鍚 RANK_SIZE 鍜 RANK_ID锛屽垯瑙f瀽瀹冧滑骞惰缃 rank_size 鍜 rank_id + if env_rank_size and env_rank_id: + try: + rank_size = int(env_rank_size.strip()) + rank_id = int(env_rank_id.strip()) + except ValueError: + raise ValueError("rank_size or rank_id is not int.") + + # 濡傛灉鎺掑悕鏁伴噺澶т簬 1锛屼笖鍚敤浜 NUMA锛屽垯璁剧疆 NUMA 鏀寔涓 True + if rank_size > 1: + if numa_enable: + _config.set_numa_enable(True) + + # 璁剧疆 rank_id + _config.set_rank_id(rank_id) + + +def set_seed(seed): + """ + Set the seed so the random generated number will be fixed for deterministic results. + 璁剧疆绉嶅瓙锛屼娇闅忔満鐢熸垚鐨勬暟瀛楀浐瀹氾紝浠ヨ幏寰楃‘瀹氭х粨鏋溿 + Note: + This set_seed function sets the seed in the Python random library and numpy.random library + for deterministic Python augmentations using randomness. This set_seed function should + be called when iterator is created to reset the random seed. + + Args: + seed(int): Random number seed. It is used to generate deterministic random numbers. + + Raises: + TypeError: If `seed` isn't of type int. + ValueError: If `seed` < 0 or `seed` > UINT32_MAX(4294967295). + + Examples: + >>> # Set a new global configuration value for the seed value. + >>> # Operations with randomness will use the seed value to generate random values. + >>> ds.config.set_seed(1000) + """ + + # 妫鏌ヨ緭鍏ョ殑绉嶅瓙鏄惁涓烘暣鏁颁笖涓嶆槸甯冨皵鍊 + if not isinstance(seed, int) or isinstance(seed, bool): + raise TypeError("seed isn't of type int.") + + # 妫鏌ョ瀛愭槸鍚﹀湪鍚堟硶鑼冨洿鍐 [0, UINT32_MAX(4294967295)] + if seed < 0 or seed > UINT32_MAX: + raise ValueError( + "seed given is not within the required range [0, UINT32_MAX(4294967295)].") + + # 浣跨敤 Config 璁剧疆闅忔満鏁扮瀛 + _config.set_seed(seed) + + # 浣跨敤 random 搴撹缃 Python 鐨勯殢鏈烘暟绉嶅瓙 + random.seed(seed) + + # 娉ㄦ剰锛歯umpy.random 涓嶆槸绾跨▼瀹夊叏鐨勶紝鍥犳鍦ㄥ绾跨▼鐜涓渶瑕佽缃殢鏈烘暟绉嶅瓙 + numpy.random.seed(seed) + + + +def get_seed(): + """ + Get random number seed. If the seed has been set, then will + return the set value, otherwise it will return the default seed value + which equals to std::mt19937::default_seed. + + Returns: + int, random number seed. + + Examples: + >>> # Get the global configuration of seed. + >>> # If set_seed() is never called before, the default value(std::mt19937::default_seed) will be returned. + >>> seed = ds.config.get_seed() + """ + return _config.get_seed() + + +def set_prefetch_size(size): + """ + Set the queue capacity of the thread in pipeline. + 璁剧疆绠¢亾涓嚎绋嬬殑闃熷垪瀹归噺銆 + Args: + size (int): The length of the cache queue. + + Raises: + TypeError: If `size` is not of type int. + ValueError: If `size` <= 0 or `size` > INT32_MAX(2147483647). + + Note: + Since total memory used for prefetch can grow very large with high number of workers, + when the number of workers is greater than 4, the per worker prefetch size will be reduced. + The actual prefetch size at runtime per-worker will be prefetchsize * (4 / num_parallel_workers). + + Examples: + >>> # Set a new global configuration value for the prefetch size. + >>> ds.config.set_prefetch_size(1000) + """ + # 妫鏌ヨ緭鍏ョ殑澶у皬鏄惁涓烘暣鏁颁笖涓嶆槸甯冨皵鍊 + if not isinstance(size, int) or isinstance(size, bool): + raise TypeError("size isn't of type int.") + + # 妫鏌ュぇ灏忔槸鍚﹀湪鍚堟硶鑼冨洿鍐 (0, INT32_MAX(2147483647)] + if size <= 0 or size > INT32_MAX: + raise ValueError( + "size is not within the required range (0, INT32_MAX(2147483647)].") + + # 浣跨敤 Config 璁剧疆鎿嶄綔杩炴帴鍣ㄧ殑澶у皬锛屽嵆璁剧疆棰勫彇澶у皬 + _config.set_op_connector_size(size) + + + +def get_prefetch_size(): + """ + Get the prefetch size as for number of rows. + If `set_prefetch_size` is never called before, the default value 16 will be returned. + 鑾峰彇琛屾暟鐨勯鍙栧ぇ灏忋 + 濡傛灉浠ュ墠浠庢湭璋冪敤杩団渟et_prefetch_size鈥濓紝鍒欏皢杩斿洖榛樿鍊16銆 + Returns: + int, total number of rows to be prefetched. + + Examples: + >>> # Get the global configuration of prefetch size. + >>> # If set_prefetch_size() is never called before, the default value(16) will be returned. + >>> prefetch_size = ds.config.get_prefetch_size() + """ + return _config.get_op_connector_size() + + +def set_num_parallel_workers(num): + """ + Set a new global configuration default value for the number of parallel workers. + This setting will affect the parallelism of all dataset operation. + 涓哄苟琛屽伐浣滅嚎绋嬬殑鏁伴噺璁剧疆涓涓柊鐨勫叏灞閰嶇疆榛樿鍊笺 + 姝よ缃皢褰卞搷鎵鏈夋暟鎹泦鎿嶄綔鐨勫苟琛屾с + Args: + num (int): Number of parallel workers to be used as a default for each operation. + + Raises: + TypeError: If `num` is not of type int. + ValueError: If `num` <= 0 or `num` > INT32_MAX(2147483647). + + Examples: + >>> # Set a new global configuration value for the number of parallel workers. + >>> # Now parallel dataset operators will run with 8 workers. + >>> ds.config.set_num_parallel_workers(8) + """ + + # 妫鏌ヨ緭鍏ョ殑鍊兼槸鍚︿负鏁存暟绫诲瀷锛屼笖涓嶆槸甯冨皵绫诲瀷 + if not isinstance(num, int) or isinstance(num, bool): + raise TypeError("num isn't of type int.") + + # 妫鏌ヨ緭鍏ョ殑鍊兼槸鍚﹀湪鍚堟硶鑼冨洿鍐 + if num <= 0 or num > INT32_MAX: + raise ValueError("Number of parallel workers given is not within the required range" + " (0, INT32_MAX(2147483647)].") + + # 璁剧疆鏁版嵁闆嗙殑骞惰宸ヤ綔绾跨▼鏁 + _config.set_num_parallel_workers(num) + + + +def get_num_parallel_workers(): + """ + Get the global configuration of number of parallel workers. + This is the DEFAULT num_parallel_workers value used for each operation. + 鑾峰彇骞惰宸ヤ綔鑰呮暟閲忕殑鍏ㄥ眬閰嶇疆銆 + 杩欐槸鐢ㄤ簬姣忎釜鎿嶄綔鐨凞EFAULT num_paralle_workers鍊笺 + Returns: + int, number of parallel workers to be used as a default for each operation. + + Examples: + >>> # Get the global configuration of parallel workers. + >>> # If set_num_parallel_workers() is never called before, the default value(8) will be returned. + >>> num_parallel_workers = ds.config.get_num_parallel_workers() + """ + return _config.get_num_parallel_workers() + + +def set_numa_enable(numa_enable): + """ + Set the default state of numa enabled. If numa_enable is True, need to ensure numa library is installed. + 璁剧疆numa enabled鐨勯粯璁ょ姸鎬併傚鏋渘uma_enable涓篢rue锛屽垯闇瑕佺‘淇濆畨瑁呬簡numa搴撱 + Args: + numa_enable (bool): Whether to use numa bind feature. + + Raises: + TypeError: If `numa_enable` is not a boolean data type. + + Examples: + >>> # Set a new global configuration value for the state of numa enabled. + >>> # Now parallel dataset operators will run with numa bind function + >>> ds.config.set_numa_enable(True) + """ + + if not isinstance(numa_enable, bool): + raise TypeError("numa_enable must be a boolean dtype.")#濡傛灉 `numa_enable` 涓嶆槸甯冨皵绫诲瀷锛屽皢寮曞彂涓涓被鍨嬮敊璇紙TypeError锛夊紓甯革紝鍏朵腑鍖呭惈閿欒娑堟伅 "numa_enable must be a boolean dtype." + #濡傛灉 `numa_enable` 鏄竷灏旂被鍨嬶紝璋冪敤 `_config.set_numa_enable(numa_enable)` 鏉ヨ缃甆UMA鏀寔鐨勭姸鎬併傝繖閲屽亣璁 `_config` 鏄竴涓閮ㄩ厤缃璞★紝鐢ㄤ簬璁剧疆NUMA鏀寔鐨勭姸鎬併 + _config.set_numa_enable(numa_enable) + + +def get_numa_enable(): + """ + Get the state of numa to indicate enabled/disabled. + This is the DEFAULT numa enabled value used for the all process. + + Returns: + bool, the default state of numa enabled. + + Examples: + >>> # Get the global configuration of numa. + >>> numa_state = ds.config.get_numa_enable() + """ + return _config.get_numa_enable() + + +def set_monitor_sampling_interval(interval): + """ + Set the default interval (in milliseconds) for monitor sampling. + + Args: + interval (int): Interval (in milliseconds) to be used for performance monitor sampling. + + Raises: + TypeError: If `interval` is not type int. + ValueError: If `interval` <= 0 or `interval` > INT32_MAX(2147483647). + + Examples: + >>> # Set a new global configuration value for the monitor sampling interval. + >>> ds.config.set_monitor_sampling_interval(100) + """ + if not isinstance(interval, int) or isinstance(interval, bool): + raise TypeError("interval isn't of type int.") + if interval <= 0 or interval > INT32_MAX: + raise ValueError( + "Interval given is not within the required range (0, INT32_MAX(2147483647)].") + _config.set_monitor_sampling_interval(interval) + + +def get_monitor_sampling_interval(): + """ + Get the global configuration of sampling interval of performance monitor. + If `set_monitor_sampling_interval` is never called before, the default value(1000) will be returned. + + Returns: + int, interval (in milliseconds) for performance monitor sampling. + + Examples: + >>> # Get the global configuration of monitor sampling interval. + >>> # If set_monitor_sampling_interval() is never called before, the default value(1000) will be returned. + >>> sampling_interval = ds.config.get_monitor_sampling_interval() + """ + return _config.get_monitor_sampling_interval() + + +def set_auto_num_workers(enable): + """ + Set num_parallel_workers for each op automatically(This feature is turned off by default). + + If turned on, the num_parallel_workers in each op will be adjusted automatically, possibly overwriting the + num_parallel_workers passed in by user or the default value (if user doesn't pass anything) set by + ds.config.set_num_parallel_workers(). + + For now, this function is only optimized for YoloV3 dataset with per_batch_map (running map in batch). + This feature aims to provide a baseline for optimized num_workers assignment for each operation. + Operation whose num_parallel_workers is adjusted to a new value will be logged. + + Args: + enable (bool): Whether to enable auto num_workers feature or not. + + Raises: + TypeError: If `enable` is not of boolean type. + + Examples: + >>> # Enable auto_num_worker feature, this might override the num_parallel_workers passed in by user + >>> ds.config.set_auto_num_workers(True) + """ + if not isinstance(enable, bool): + raise TypeError("enable must be of type bool.") + _config.set_auto_num_workers(enable) + + +def _set_auto_workers_config(option): + """ + INTERNAL USE ONLY! + Select the weight profile of auto_num_workers. currently these 7 options are supported. + Option #0 leaf_num_workers:batch_num_workers:map_num_workers=1:1:1 + Option #1 leaf_num_workers:batch_num_workers:map_num_workers=2:1:1 + Option #2 leaf_num_workers:batch_num_workers:map_num_workers=1:2:1 + Option #3 leaf_num_workers:batch_num_workers:map_num_workers=1:1:2 + Option #4 leaf_num_workers:batch_num_workers:map_num_workers=2:2:1 + Option #5 leaf_num_workers:batch_num_workers:map_num_workers=2:1:2 + Option #6 leaf_num_workers:batch_num_workers:map_num_workers=1:2:2 + + Args: + option (int): The id of the profile to use. + + Raises: + TypeError: If `option` is not of type int. + ValueError: If `option` is not within the range of [0, 6]. + """ + if not isinstance(option, int) or isinstance(option, bool): + raise TypeError("option isn't of type int.") + if option < 0 or option > 6: + raise ValueError("option isn't within the required range of [0, 6].") + _config.set_auto_worker_config(option) + + +def get_auto_num_workers(): + """ + Get the setting (turned on or off) automatic number of workers. + + Returns: + bool, whether auto number worker feature is turned on. + + Examples: + >>> # Get the global configuration of auto number worker feature. + >>> flag = ds.config.get_auto_num_workers() + """ + return _config.get_auto_num_workers() + + +def set_callback_timeout(timeout): + """ + Set the default timeout (in seconds) for DSWaitedCallback. + + Args: + timeout (int): Timeout (in seconds) to be used to end the wait in DSWaitedCallback in case of a deadlock. + + Raises: + TypeError: If `timeout` is not type int. + ValueError: If `timeout` <= 0 or `timeout` > INT32_MAX(2147483647). + + Examples: + >>> # Set a new global configuration value for the timeout value. + >>> ds.config.set_callback_timeout(100) + """ + if not isinstance(timeout, int) or isinstance(timeout, bool): + raise TypeError("timeout isn't of type int.") + if timeout <= 0 or timeout > INT32_MAX: + raise ValueError("Timeout given is not within the required range.") + _config.set_callback_timeout(timeout) + + +def get_callback_timeout(): + """ + Get the default timeout for WaitedDSCallback. + + Returns: + int, Timeout (in seconds) to be used to end the wait in DSWaitedCallback in case of a deadlock. + + Examples: + >>> # Get the global configuration of callback timeout. + >>> # If set_callback_timeout() is never called before, the default value(60) will be returned. + >>> callback_timeout = ds.config.get_callback_timeout() + """ + return _config.get_callback_timeout() + + +def __str__(): + """ + String representation of the configurations. + + Returns: + str, configurations. + """ + return str(_config) + + +def load(file): + """ + Load the project configuration from the file. + + Args: + file (str): Path of the configuration file to be loaded. + + Raises: + RuntimeError: If `file` is invalid and parsing fails. + + Examples: + >>> # Set new default configuration according to values in the configuration file. + >>> # example config file: + >>> # { + >>> # "logFilePath": "/tmp", + >>> # "numParallelWorkers": 4, + >>> # "seed": 5489, + >>> # "monitorSamplingInterval": 30 + >>> # } + >>> config_file = "/path/to/config/file" + >>> ds.config.load(config_file) + """ + _config.load(file) + + +def set_enable_autotune(enable, filepath_prefix=None): + """ + Set whether to enable AutoTune. AutoTune is disabled by default. + + AutoTune is used to automatically adjust the global configuration of the data pipeline + according to the workload of environmental resources during the training process to + improve the speed of data processing. + + The optimized global configuration can be saved as a JSON file by setting `json_filepath` + for subsequent reuse. + + Args: + enable (bool): Whether to enable AutoTune. + filepath_prefix (str, optional): The prefix filepath to save the optimized global configuration. + The rank id and the json extension will be appended to the filepath_prefix string in multi-device training, + rank id will be set to 0 in standalone training. + For example, if filepath_prefix="/path/to/some/dir/prefixname" and rank_id is 1, then the path + of the generated file will be "/path/to/some/dir/prefixname_1.json" + If the file already exists, it will be automatically overwritten. Default: None, + means not to save the configuration file, but the tuned result still can be checked through INFO log. + + Raises: + TypeError: If `enable` is not of type boolean. + TypeError: If `json_filepath` is not of type str. + RuntimeError: If `json_filepath` is an empty string. + RuntimeError: If `json_filepath` is a directory. + RuntimeError: If `json_filepath` does not exist. + RuntimeError: If `json_filepath` does not have write permission. + + Note: + - When `enable` is False, `json_filepath` will be ignored. + - The JSON file can be loaded by API `mindspore.dataset.deserialize` to build a tuned pipeline. + - In distributed training scenario, set_enable_autotune() must be called after cluster communication has been + initialized (mindspore.communication.management.init()), otherwise the AutoTune file will always suffix with + rank id 0. + + An example of the generated JSON file is as follows. "remark" file will conclude that if the dataset has been + tuned or not. "summary" filed will show the tuned configuration of dataset pipeline. Users can modify scripts + based on the tuned result. + + .. code-block:: + + { + "remark": "The following file has been auto-generated by the Dataset AutoTune.", + "summary": [ + "CifarOp(ID:5) (num_parallel_workers: 2, prefetch_size:64)", + "MapOp(ID:4) (num_parallel_workers: 2, prefetch_size:64)", + "MapOp(ID:3) (num_parallel_workers: 2, prefetch_size:64)", + "BatchOp(ID:2) (num_parallel_workers: 8, prefetch_size:64)" + ], + "tree": { + ... + } + } + + Examples: + >>> # enable AutoTune and save optimized data pipeline configuration + >>> ds.config.set_enable_autotune(True, "/path/to/autotune_out.json") + >>> + >>> # enable AutoTune + >>> ds.config.set_enable_autotune(True) + """ + if not isinstance(enable, bool): + raise TypeError("enable must be of type bool.") + + save_autoconfig = bool(enable and filepath_prefix is not None) + + if filepath_prefix and not isinstance(filepath_prefix, str): + raise TypeError( + "json_filepath must be a str value but was: {}.".format(filepath_prefix)) + + if enable and filepath_prefix == "": + raise RuntimeError( + "The value of json_filepath cannot be the empty string.") + + if not enable and filepath_prefix is not None: + logger.warning( + "The value of json_filepath is ignored when enable is False.") + + if enable and filepath_prefix is None: + logger.warning( + "Dataset AutoTune is enabled but no json path is specified, check INFO log for tuned result.") + + json_filepath = replace_none(filepath_prefix, "") + _config.set_enable_autotune(enable, save_autoconfig, json_filepath) + + +def get_enable_autotune(): + """ + Get whether AutoTune is currently enabled. + + Returns: + bool, whether AutoTune is currently enabled. + + Examples: + >>> # get the state of AutoTune + >>> autotune_flag = ds.config.get_enable_autotune() + """ + return _config.get_enable_autotune() + + +def set_autotune_interval(interval): + """ + Set the configuration adjustment interval (in steps) for AutoTune. + + The default setting is 0, which will adjust the configuration after each epoch. + Otherwise, the configuration will be adjusted every `interval` steps. + + Args: + interval (int): Interval (in steps) to adjust the configuration of the data pipeline. + + Raises: + TypeError: If `interval` is not of type int. + ValueError: If `interval` is not non-negative. + + Examples: + >>> # set a new interval for AutoTune + >>> ds.config.set_autotune_interval(30) + """ + if not isinstance(interval, int) or isinstance(interval, bool): + raise TypeError("interval must be of type int.") + if interval < 0 or interval > INT32_MAX: + raise ValueError( + "Interval given is not within the required range [0, INT32_MAX(2147483647)].") + _config.set_autotune_interval(interval) + + +def get_autotune_interval(): + """ + Get the current configuration adjustment interval (in steps) for AutoTune. + + Returns: + int, the configuration adjustment interval (in steps) for AutoTune. + + Examples: + >>> # get the global configuration of the autotuning interval + >>> autotune_interval = ds.config.get_autotune_interval() + """ + return _config.get_autotune_interval() + + +def get_enable_shared_mem(): + """ + Get the default state of shared mem enabled variable. + + Note: + `get_enable_shared_mem` is not supported on Windows and MacOS platforms yet. + + Returns: + bool, the state of shared mem enabled variable. + + Examples: + >>> # Get the flag of shared memory feature. + >>> shared_mem_flag = ds.config.get_enable_shared_mem() + """ + # For Windows and MacOS we forbid shared mem function temporarily + enable_shared_mem = _config.get_enable_shared_mem() + if enable_shared_mem and platform.system().lower() in {"windows", "darwin"}: + logger.warning( + "For Windows and MacOS we forbid shared mem function temporarily.") + _config.set_enable_shared_mem(False) + return False + return enable_shared_mem + + +def set_enable_shared_mem(enable): + """ + Set the default state of shared memory flag. If shared_mem_enable is True, will use shared memory queues + to pass data to processes that are created for operators that set python_multiprocessing=True. + + Note: + `set_enable_shared_mem` is not supported on Windows and MacOS platforms yet. + + Args: + enable (bool): Whether to use shared memory in operators when python_multiprocessing=True. + + Raises: + TypeError: If `enable` is not a boolean data type. + + Examples: + >>> # Enable shared memory feature to improve the performance of Python multiprocessing. + >>> ds.config.set_enable_shared_mem(True) + """ + if not isinstance(enable, bool): + raise TypeError("enable must be of type bool.") + if enable: + # For Windows and MacOS we forbid shared mem function temporarily + if platform.system().lower() in {"windows", "darwin"}: + logger.warning("For Windows and MacOS we forbid shared mem function temporarily.") + return + logger.warning("The shared memory is on, multiprocessing performance will be improved. " + "Note: the required shared memory can't exceeds 80% of the available shared memory.") + _config.set_enable_shared_mem(enable) + + +def set_sending_batches(batch_num): + """ + Set the default sending batches when training with sink_mode=True in Ascend device. + + Args: + batch_num (int): the total sending batches, when batch_num is set, it will wait unless sending batches + increase, default is 0 which means will send all batches in dataset. + + Raises: + TypeError: If `batch_num` is not of type int. + + Examples: + >>> # Set a new global configuration value for the sending batches + >>> ds.config.set_sending_batches(10) + """ + if not isinstance(batch_num, int) or isinstance(batch_num, bool): + raise TypeError("batch_num must be an int dtype.") + _config.set_sending_batches(batch_num) + + +def set_auto_offload(offload): + """ + Set the automatic offload flag of the dataset. If set_auto_offload is True, + automatically offload as many dataset operations from the CPU to the Device (GPU or Ascend). + + Args: + offload (bool): Whether to use the automatic offload feature. + + Raises: + TypeError: If offload is not a boolean data type. + + Examples: + >>> # Enable automatic offload feature + >>> ds.config.set_auto_offload(True) + """ + if not isinstance(offload, bool): + raise TypeError("offload must be a bool dtype") + _config.set_auto_offload(offload) + + +def get_auto_offload(): + """ + Get the state of the automatic offload flag (True or False) + + Returns: + bool, Whether the automatic offload feature is enabled. + + Example: + >>> # Get the global configuration of the automatic offload feature. + >>> auto_offload = ds.config.get_auto_offload() + """ + return _config.get_auto_offload() + + +def set_enable_watchdog(enable): + """ + Set the default state of watchdog Python thread as enabled, the default state of watchdog Python thread is enabled. + Watchdog is a thread which cleans up hanging subprocesses. + + Args: + enable (bool): Whether to launch a watchdog Python thread. System default: True. + + Raises: + TypeError: If `enable` is not a boolean data type. + + Examples: + >>> # Set a new global configuration value for the state of watchdog Python thread as enabled. + >>> ds.config.set_enable_watchdog(True) + """ + if not isinstance(enable, bool): + raise TypeError("enable must be a boolean dtype.") + _config.set_enable_watchdog(enable) + + +def get_enable_watchdog(): + """ + Get the state of watchdog Python thread to indicate enabled or disabled state. + This is the DEFAULT watchdog Python thread state value used for the all processes. + + Returns: + bool, the default state of watchdog Python thread enabled. + + Examples: + >>> # Get the global configuration of watchdog Python thread. + >>> watchdog_state = ds.config.get_enable_watchdog() + """ + return _config.get_enable_watchdog() + + +def set_multiprocessing_timeout_interval(interval): + """ + Set the default interval (in seconds) for multiprocessing/multithreading timeout when main process/thread gets + data from subprocesses/child threads. + + Args: + interval (int): Interval (in seconds) to be used for multiprocessing/multithreading timeout when main + process/thread gets data from subprocess/child threads. System default: 300s. + + Raises: + TypeError: If `interval` is not of type int. + ValueError: If `interval` <= 0 or `interval` > INT32_MAX(2147483647). + + Examples: + >>> # Set a new global configuration value for multiprocessing/multithreading timeout when getting data. + >>> ds.config.set_multiprocessing_timeout_interval(300) + """ + if not isinstance(interval, int) or isinstance(interval, bool): + raise TypeError("interval isn't of type int.") + if interval <= 0 or interval > INT32_MAX: + raise ValueError( + "Interval given is not within the required range (0, INT32_MAX(2147483647)).") + _config.set_multiprocessing_timeout_interval(interval) + + +def get_multiprocessing_timeout_interval(): + """ + Get the global configuration of multiprocessing/multithreading timeout when main process/thread gets data from + subprocesses/child threads. + + Returns: + int, interval (in seconds) for multiprocessing/multithreading timeout when main process/thread gets data from + subprocesses/child threads (default is 300s). + + Examples: + >>> # Get the global configuration of multiprocessing/multithreading timeout when main process/thread gets data + >>> # from subprocesses/child threads. If set_multiprocessing_timeout_interval() is never called before, the + >>> # default value(300) will be returned. + >>> multiprocessing_timeout_interval = ds.config.get_multiprocessing_timeout_interval() + """ + return _config.get_multiprocessing_timeout_interval() + + +def set_dynamic_shape(is_dynamic): + """ + Set the dynamic shape flag of the dataset. + + Args: + is_dynamic (bool): Whether the dataset is dynamic shape. Default: False + + Raises: + TypeError: If `is_dynamic` is not a boolean data type. + + Examples: + >>> ds.config.set_dynamic_shape(True) + """ + if not isinstance(is_dynamic, bool): + raise TypeError("is_dynamic must be a boolean dtype.") + _config.set_dynamic_shape(is_dynamic) + + +def get_dynamic_shape(): + """ + Get the dynamic shape flag of the dataset + Returns: + bool, whether the dataset is dynamic shape. + + Examples: + >>> is_dynamic_shape = ds.config.get_dynamic_shape() + """ + return _config.get_dynamic_shape() diff --git a/mindspore/ccsrc/transform-update/control_flow_ops_declare.cc b/mindspore/ccsrc/transform-update/control_flow_ops_declare.cc new file mode 100644 index 00000000000..701acadf5fd --- /dev/null +++ b/mindspore/ccsrc/transform-update/control_flow_ops_declare.cc @@ -0,0 +1,41 @@ +/** + * Copyright 2019 Huawei Technologies Co., Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "transform/graph_ir/op_declare/control_flow_ops_declare.h" + +namespace mindspore::transform { +// Merge +INPUT_MAP(Merge) = EMPTY_INPUT_MAP; +//杈撳叆鏄犲皠锛岃涓虹┖ +DYN_INPUT_MAP(Merge) = {{1, DYN_INPUT_DESC(x)}}; +//鍔ㄦ佽緭鍏ユ槧灏勶紝灏嗙储寮曚负1鐨勫姩鎬佽緭鍏ヤ笌鍚嶇О涓簒鐨勫姩鎬佽緭鍏ユ弿杩板叧鑱旇捣鏉ワ紝鐢ㄤ簬鍚庣画鎿嶄綔 +ATTR_MAP(Merge) = EMPTY_ATTR_MAP; +//灞炴ф槧灏勶紝璁句负绌 +OUTPUT_MAP(Merge) = {{0, OUTPUT_DESC(y)}, {1, OUTPUT_DESC(value_index)}}; +//杈撳嚭鏄犲皠锛寉绱㈠紩涓0锛寁alue_index绱㈠紩涓1 +REG_ADPT_DESC(Merge, kNameMerge, ADPT_DESC(Merge)) +//娉ㄥ唽Merge鎿嶄綔鐨勯傞厤鍣ㄦ弿杩発NameMerge + +// Switch +INPUT_MAP(Switch) = {{1, INPUT_DESC(data)}, {2, INPUT_DESC(pred)}}; +//杈撳叆鏄犲皠锛宒ata绱㈠紩涓1锛宲red绱㈠紩涓2 +OUTPUT_MAP(Switch) = {{0, OUTPUT_DESC(output_false)}, {1, OUTPUT_DESC(output_true)}}; +//杈撳嚭鏄犲皠锛宱utput_false绱㈠紩涓0锛宱utput_true绱㈠紩涓1 +ATTR_MAP(Switch) = EMPTY_ATTR_MAP; +//灞炴ф槧灏勶紝璁句负绌 +REG_ADPT_DESC(Switch, kNameGeSwitch, ADPT_DESC(Switch)) +//娉ㄥ唽Switch鎿嶄綔鐨勯傞厤鍣ㄦ弿杩発NameGeSwitch +} // namespace mindspore::transform diff --git a/mindspore/ccsrc/transform-update/convert.cc b/mindspore/ccsrc/transform-update/convert.cc new file mode 100644 index 00000000000..23a5e63278b --- /dev/null +++ b/mindspore/ccsrc/transform-update/convert.cc @@ -0,0 +1,2328 @@ +/** + * Copyright 2019-2021 Huawei Technologies Co., Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "include/transform/graph_ir/convert.h" + +#include +#include +#include +#include "include/common/utils/utils.h" + +#include "base/core_ops.h" +#include "frontend/operator/ops.h" +#include "utils/log_adapter.h" +#include "ir/graph_utils.h" +#include "utils/symbolic.h" +#include "include/common/utils/config_manager.h" +#include "include/common/utils/convert_utils.h" +#include "utils/ms_context.h" +#include "utils/check_convert_utils.h" +#include "include/transform/graph_ir/op_adapter_map.h" +#include "ops/state_ops.h" +#include "ops/array_ops.h" +#include "ops/elewise_calculation_ops.h" +#include "ops/math_ops.h" +#ifdef ENABLE_D +#include "ops/save_ops.h" +#endif +#include "transform/graph_ir/op_adapter.h" +#include "transform/graph_ir/op_adapter_desc.h" + +namespace mindspore { //namespace:命名空间 +namespace transform { +using std::endl; + +using ge::Operator; +using mindspore::kAnyValue; +using std::make_shared; +using std::shared_ptr; +using std::string; +using std::vector; +using Variable = ge::op::Variable; +using Constant = ge::op::Constant; +using Assign = ge::op::Assign; +using Data = ge::op::Data; + +namespace { +std::vector GetOrderedCNodes(const FuncGraphPtr fg) { //该函数的功能是通过拓扑排序获取按顺序排列的CNode节点。 + MS_EXCEPTION_IF_NULL(fg); ////检查传入的是否为空,如果为空则抛出异常 + auto BelongSameGraph = std::bind(IncludeBelongGraph, fg, std::placeholders::_1); + auto succ_include_fv = [&fg](const AnfNodePtr &node) -> std::vector { + std::vector vecs; + if (node == nullptr) { //如果传入的为空指针,则直接返回空的 + return vecs; + } + if (node->isa()) { //如果传入的是一个CNode节点,则进入条件判断语句块 + auto cnode = node->cast(); //获取该CNode节点的输入,并遍历每个输入。 + auto &inputs = cnode->inputs(); + // Check if free variables used. + for (const auto &input : inputs) { + auto input_fg = GetValueNode(input); //如果输入是一个函数图的值节点(FuncGraphPtr类型),则进入条件判断语句块 + if (input_fg) { + for (auto &fv : input_fg->free_variables_nodes()) { + if (fv->func_graph() == fg && fg->nodes().contains(fv)) {//遍历该函数图的自由变量节点(free_variables_nodes), + vecs.push_back(fv); //如果自由变量节点所属的函数图与传入的函数图相同,并且函数图中包含该自由变量节点,则将该自由变量节点添加到vecs中 + } + } + } + } + (void)vecs.insert(vecs.end(), inputs.begin(), inputs.end()); //将该CNode节点的所有输入添加到vecs的末尾 + } + return vecs; //返回vecs + }; + + return TopoSort(fg->get_return(), succ_include_fv, BelongSameGraph); +} +} // namespace + + +// ---------------implement of DfGraphConvertor------------- +bool IsCaseNode(const CNodePtr node) { //定义了一个名为IsCaseNodeCNodePtr的函数,用于判断给定的节点是否为"case"节点。 + MS_EXCEPTION_IF_NULL(node); //使用宏确保传入的不为空,如果为空则抛出异常 + if (!node->inputs().empty() && node->input(0)->isa() && //通过条件判断语句检查输入是否非空,并且第一个输入是否为nodeCNode类型 + GetCNodeFuncName(node->input(0)->cast()) == "switch_layer") { //通过调用函数GetCNodeFuncName获取第一个输入节点的函数名称,并将其与字符串"switch_layer"进行比较 + return true; //如果函数名称与"switch_layer"相等,则返回true,表示该节点是"case"节点 + } + return false; //如果不满足上述条件,则返回false,表示该节点不是"case"节点 +} + +/* +该函数的目的是获取 CNode 的目标函数名。对于 "case" 节点,目标函数名是 "kNameCase";对于其他节点,目标函数名是 +GetCNodeFuncName(cnode) 的返回值,但如果函数名为"switch_layer",则返回一个空字符串。 +在具体应用中,目标函数名可能用于后续的处理或决策逻辑。 +*/ +std::string GetCNodeTargetFuncName(const CNodePtr cnode) { //接受一个类型为 CNodePtr 的指针 cnode 作为参数,并返回一个 std::string 类型的目标函数名 + if (IsCaseNode(cnode)) { //判断给定的cnode是否是case节点。 + return string(kNameCase); //如果是case节点,则函数直接返回一个字符串常量 kNameCase,表示目标函数名为kNameCase + } + auto name = GetCNodeFuncName(cnode); //调用GetCNodeFuncName函数,用于获取 cnode 的函数名,并将其保存在一个名为 name 的局部变量中。 + if (name == "switch_layer") { //检查函数名name是否为switch_layer + name = ""; //如果是,将 name 清空,即赋值为空字符串 + } + return name; //返回目标函数名name +} + +/* +该函数的作用是根据节点的类型和目标函数名,查找对应的适配器并返回适配器的指针。 +适配器是用于处理不同类型的操作(函数)的一种模式,通过适配器模式, +可以使得图操作转换器能够灵活地处理不同类型的节点和操作。 +*/ +OpAdapterPtr DfGraphConvertor::FindAdapter(const AnfNodePtr node, bool train) { + MS_EXCEPTION_IF_NULL(node); //检查指针 node 是否为空,如果为空,抛出异常 + if (node->isa()) { //条件语句,判断 node 是否为 CNode 类型的节点 + auto cnode = node->cast(); //如果是 CNode 类型的节点,将其转换为 CNodePtr 类型的智能指针,并赋值给cnode变量 + + std::string name = kNameCustomOp; //创建一个名为name的字符串变量,并将其初始化为一个名为 kNameCustomOp 的字符串常量。 + if (!IsCustomCNode(cnode)) { //如果cnode不是自定义节点(根据 IsCustomCNode 函数判断) + name = GetCNodeTargetFuncName(cnode); //则将name设置为 GetCNodeTargetFuncName(cnode)的返回值,即获取cnode的目标函数名 + } + + auto it_adpt = OpAdapterMap::get().find(name); //在OpAdapterMap中查找name对应的适配器。OpAdapterMap 是一个静态单例对象,用于存储不同操作(函数)名对应的适配器。OpAdapterMap::get() 返回 OpAdapterMap 的引用 + if (it_adpt != OpAdapterMap::get().end()) { //如果找到了name对应的适配器, + return it_adpt->second->Get(train); //则调用适配器的Get方法,将train作为参数传递进去,并返回适配器的指针 it_adpt->second + } + MS_LOG(EXCEPTION) << "Can't find OpAdapter for " << name; //如果未找到适配器,则输出异常日志,表示无法找到适配器 + } + + if (node->isa()) { //处理不同类型的节点:ValueNode 和 Parameter。 + return OpAdapterMap::get()[kNameConst]->Get(train); //根据节点的类型,选择对应的适配器,并返回对应的指针。 + } + if (node->isa()) { + return OpAdapterMap::get()[kNameParam]->Get(train); + } + return OpAdapterPtr(nullptr); //如果节点类型不是 CNode、ValueNode 或 Parameter,则返回一个空的 OpAdapterPtr +} + +/* +该函数用于初始化循环变量,并将相关的操作(Operator)添加到 init_input 和 init_ops_ 中 +根据 training_的值决定是否初始化循环变量,并进行相应的变量和操作的创建和管理。 +这在图操作转换过程中可能涉及到控制流和循环的处理。 +*/ +void DfGraphConvertor::InitLoopVar(std::vector *init_input) { + MS_EXCEPTION_IF_NULL(init_input); //宏或函数调用,用于检查指针init_input是否为空,如果为空,则抛出异常 + if (this->training_) { //检查this->training_的值,如果为真(即 training_ 为真),则执行 if 代码块中的内容 + GeTensorDesc desc(GeShape(), ge::FORMAT_NCHW, ge::DT_INT64); //通过调用 std::make_shared(...) 创建了四个名为 var_iter_num、var_loop_cond、var_one 和 var_zero 的智能指针。 + auto var_iter_num = std::make_shared("npu_runconfig/iterations_per_loop"); //这些智能指针指向 Variable 类的实例,每个实例代表一个变量。 + auto var_loop_cond = std::make_shared("npu_runconfig/loop_cond"); + auto var_one = std::make_shared("npu_runconfig/one"); + auto var_zero = std::make_shared("npu_runconfig/zero"); + (void)var_iter_num->update_output_desc_y(desc); //分别为这四个变量(var_iter_num、var_loop_cond、var_one 和 var_zero)更新了输出描述 GeTensorDesc。 + (void)var_loop_cond->update_output_desc_y(desc); + (void)var_one->update_output_desc_y(desc); + (void)var_zero->update_output_desc_y(desc); + vars_["npu_runconfig/iterations_per_loop"] = var_iter_num; //将这四个变量添加到 vars_ 容器中, + vars_["npu_runconfig/loop_cond"] = var_loop_cond; //vars_ 可能是一个类成员变量,用于存储变量的映射关系。 + vars_["npu_runconfig/one"] = var_one; + vars_["npu_runconfig/zero"] = var_zero; + + //创建了四个名为 const_iter_num、const_loop_cond、const_one 和 const_zero 的智能指针。 + //这些智能指针指向 Constant 类的实例,每个实例代表一个常量。 + int64_t value = 0; + auto const_iter_num = std::make_shared("const/npu_runconfig/iterations_per_loop"); + if (ConfigManager::GetInstance().dataset_mode() == DS_SINK_MODE) { + value = ConfigManager::GetInstance().iter_num(); + } else { + MS_LOG(INFO) << "Run with normal(non-sink) mode, the iterator number will always be 1"; + ConfigManager::GetInstance().ResetIterNum(); + } + + //通过调用 set_attr_value 方法为这四个常量设置了不同的属性值(值为整数类型) + value -= 1; // iteration start from 0, the max iteration number for n loop should be n-1 + (void)const_iter_num->set_attr_value(GeTensor(desc, reinterpret_cast(&value), sizeof(int64_t))); + + auto const_loop_cond = std::make_shared("const/npu_runconfig/loop_cond"); + value = 0; + (void)const_loop_cond->set_attr_value(GeTensor(desc, reinterpret_cast(&value), sizeof(int64_t))); + + auto const_one = std::make_shared("const/npu_runconfig/one"); + value = 1; + (void)const_one->set_attr_value(GeTensor(desc, reinterpret_cast(&value), sizeof(int64_t))); + + auto const_zero = std::make_shared("const/npu_runconfig/zero"); + value = 0; + (void)const_zero->set_attr_value(GeTensor(desc, reinterpret_cast(&value), sizeof(int64_t))); + + //分别为这四个常量(const_iter_num、const_loop_cond、const_one 和 const_zero)更新了输出描述 GeTensorDesc。 + (void)const_iter_num->update_output_desc_y(desc); + (void)const_loop_cond->update_output_desc_y(desc); + (void)const_one->update_output_desc_y(desc); + (void)const_zero->update_output_desc_y(desc); + + //创建了四个名为 assign_iter_num、assign_loop_cond、assign_one 和 assign_zero 的智能指针。 + //这些智能指针指向 Assign 类的实例,每个实例代表一个赋值操作。 + //分别通过调用 set_input_ref 和 set_input_value 方法为这四个赋值操作设置了输入引用和输入值 + auto assign_iter_num = std::make_shared("assign/npu_runconfig/iterations_per_loop"); + (void)assign_iter_num->set_input_ref(*var_iter_num).set_input_value(*const_iter_num); + auto assign_loop_cond = std::make_shared("assign/npu_runconfig/loop_cond"); + (void)assign_loop_cond->set_input_ref(*var_loop_cond).set_input_value(*const_loop_cond); + auto assign_one = std::make_shared("assign/npu_runconfig/one"); + (void)assign_one->set_input_ref(*var_one).set_input_value(*const_one); + auto assign_zero = std::make_shared("assign/npu_runconfig/zero"); + (void)assign_zero->set_input_ref(*var_zero).set_input_value(*const_zero); + + //将 var_iter_num、var_loop_cond、var_one 和 var_zero 添加到 init_input 中,init_input 可能是一个传入的参数,用于存储初始化输入的向量。 + //将 var_iter_num、var_loop_cond、var_one、var_zero、const_iter_num、const_loop_cond、const_one、const_zero、assign_iter_num、 + //assign_loop_cond、assign_one 和 assign_zero 添加到 init_ops_ 中,init_ops_ 可能是一个类成员变量,用于存储初始化操作的向量。 + init_input->push_back(*var_iter_num); + init_input->push_back(*var_loop_cond); + init_input->push_back(*var_one); + init_input->push_back(*var_zero); + init_ops_.push_back(var_iter_num); + init_ops_.push_back(var_loop_cond); + init_ops_.push_back(var_one); + init_ops_.push_back(var_zero); + init_ops_.push_back(const_iter_num); + init_ops_.push_back(const_loop_cond); + init_ops_.push_back(const_one); + init_ops_.push_back(const_zero); + init_ops_.push_back(assign_iter_num); + init_ops_.push_back(assign_loop_cond); + init_ops_.push_back(assign_one); + init_ops_.push_back(assign_zero); + } +} + +/* +该函数的作用是根据给定的操作(函数)名 name查找对应的适配器,并返回适配器的指针。 +适配器是用于处理不同类型的操作(函数)的一种模式,通过适配器模式,可以使得图操作转换器能够灵活地处理不同类型的节点和操作。 +*/ +OpAdapterPtr DfGraphConvertor::FindAdapter(const std::string &name, bool train) { + auto it = OpAdapterMap::get().find(name); //在 OpAdapterMap中查找name对应的适配器。OpAdapterMap是一个静态单例对象,用于存储不同操作(函数)名对应的适配器。OpAdapterMap::get() 返回 OpAdapterMap 的引用。 + if (it != OpAdapterMap::get().end()) { //如果找到了 name 对应的适配器,则调用适配器的get方法 + return it->second->Get(train); //将train作为参数传递进去,返回适配器的指针 it->second + } + MS_LOG(EXCEPTION) << "Can't find OpAdapter for " << name; //如果未找到适配器,则输出异常日志,表示无法找到适配器。 +} + +/* +该函数用于生成参数初始化子图,采用Graphviz格式,将描述输出到 init_sout_中。 +Graphviz是一种用于绘制图形的工具,可以将图形可视化,便于理解和调试。 +*/ +void DfGraphConvertor::DrawParamInitSubGraph(const std::string &name, const AnfNodePtr &it) { + // draw init subgraph 根据参数名 name 和节点 it 来绘制参数初始化子图的描述。 + init_sout_ << "op_assign" << it.get() << "[label=<"; //使用<<运算符将描述信息添加到 init_sout_ 中 + //添加了一个形如 "op_assign{it.get()}[label=<" 的字符串 + //其中 it.get() 是节点 it 的指针值。op_assign 是一个子图节点的标识符,用于表示参数初始化的赋值操作。 + init_sout_ << "" << endl; + init_sout_ << ""; + init_sout_ << ""; //使用 HTML table 的形式绘制子图的结构,包括 "resource" 和 "value" 两个列,并设置了相应的标签。 + init_sout_ << ""; + init_sout_ << "" << endl; + init_sout_ << "" << endl; + init_sout_ << "
resourcevalue
" + << "\"assign_" << name << "\"
> shape=plaintext]" << endl; + init_sout_ << "param" << it.get() << "[shape=octagon, label=\"" << name << "\"]" << endl; //绘制一个形如 "param{it.get()}[shape=octagon, label="{name}"]" 的节点,其中 it.get() 是节点 it 的指针值,name 是参数名 + init_sout_ << "const" << it.get() << "[label= \"" << name << "_const" //绘制一个形如 "const{it.get()}[label="{name}_const" shape=ellipse]" 的节点,其中 it.get() 是节点 it 的指针值,name 是参数名。 + << "\" shape=ellipse]" << endl; + init_sout_ << "param" << it.get() << "->" //绘制从 param{it.get()} 节点到 op_assign{it.get()}:1 节点的边,表示赋值操作的资源(resource)部分。 + << "op_assign" << it.get() << ":1" << endl; + init_sout_ << "const" << it.get() << "->" //绘制从 const{it.get()} 节点到 op_assign{it.get()}:2 节点的边,表示赋值操作的值(value)部分。 + << "op_assign" << it.get() << ":2" << endl; +} + +/* +该函数用于设置参数初始化子图,构建子图并存储在 init_graph_中,用于参数初始化的计算。 +这在图操作转换过程中可能涉及到参数初始化和常量传播等步骤。 +*/ +void DfGraphConvertor::SetupParamInitSubGraph(const TensorOrderMap &tensors, std::vector *init_input) { + DfGraphPtr init_graph = std::make_shared("init"); //创建一个名为 init_graph 的 DfGraph 对象,并命名为 "init"。 + std::vector nodes = GetOrderedCNodes(anf_graph_); //通过调用 GetOrderedCNodes(anf_graph_) 获取图中的有序计算节点,并存储在 nodes 中。 + + for (auto &it : nodes) { //遍历nodes中的每个节点it + MS_EXCEPTION_IF_NULL(it); + if (it->isa()) { //检查节点是否为ValueNode类型 + if (IsValueNode(it)) { //对于符号节点 SymbolicKeyInstance,找到对应的变量操作 Variable,将其存储在 op_cache_ 中,并输出一条表示连接的 compute_sout_ 语句。 + auto symbolic = GetValueNode(it); + auto name = std::static_pointer_cast(symbolic->node())->name(); + auto iter = vars_.find(name); // get corresponding variable op + if (iter != vars_.end()) { + op_cache_[it.get()] = iter->second; + // #ifdef DRAW_GE_GRAPH + compute_sout_ << op_draw_name_[params_[name].get()] << " -> " << op_draw_name_[it.get()] + << "[style=\"dotted\"]" << endl; + // #endif + } + } else if (IsValueNode(it)) { //对于引用键节点 RefKey,也找到对应的变量操作 Variable,将其存储在 op_cache_ 中,并输出一条表示连接的 compute_sout_ 语句。 + auto refkey = GetValueNode(it); + MS_EXCEPTION_IF_NULL(refkey); + auto name = refkey->tag(); + auto iter = vars_.find(name); // get corresponding variable op + if (iter != vars_.end()) { + op_cache_[it.get()] = iter->second; + compute_sout_ << op_draw_name_[params_[name].get()] << " -> " << op_draw_name_[it.get()] + << "[style=\"dotted\"]" << endl; + } + } + } + } + + for (auto &it : tensors) { //检查给定的 TensorOrderMap 中的参数,将不存在于 vars_(变量映射)中的参数添加到 vars_ 中,并置其对应的变量操作为 nullptr。 + if (vars_.find(it.first) == vars_.end()) { + MS_LOG(WARNING) << "Init parameter " << it.first << " didn't appear in graph."; + vars_[it.first] = nullptr; + } + } + + // set up init sub graph + if (init_input->size()) { + // init sub graph needs no input + MS_LOG(INFO) << "Build data init subgraph."; + (void)init_graph->SetInputs(*init_input); //设置初始化子图 init_graph_ 的输入为 init_input,并将其存储在 init_graph_ 中。如果 init_input 为空,说明初始化子图不需要输入,则将 init_graph_ 置为 nullptr + this->init_graph_ = init_graph; + } else { + this->init_graph_ = nullptr; + } +} + +/* +该函数的作用是根据数据集的名称、输入索引和节点创建数据集处理器,并将其存储在 out_handle_cache_中,这样在构建图时可以使用处理器进行数据集处理。 +这在图操作转换过程中可能涉及到数据集的处理和输入操作的替换。 +*/ +void DfGraphConvertor::MakeDatasetHandler(const std::string &name, const size_t &input_idx, const AnfNodePtr &it) { + MS_LOG(INFO) << "The " << name << " is the " << input_idx << "(st/nd/th) input"; //输出日志,表示当前处理的数据集的名称和输入索引。 + if (ConfigManager::GetInstance().dataset_mode() == DS_SINK_MODE) { //检查配置管理器中的数据集模式是否为 "DS_SINK_MODE"。 + auto getnext_idx = static_cast(input_idx); //将输入索引转换为 int64_t 类型的变量 getnext_idx。 + DatasetGraphParam param = ConfigManager::GetInstance().dataset_param(); //从配置管理器中获取数据集参数,并将其存储在变量 param 中。 + if (!param.input_indexes().empty() && input_idx <= param.input_indexes().size()) { //如果数据集参数中的输入索引列表 input_indexes() 不为空,并且输入索引 input_idx 小于等于列表的大小,则将 getnext_idx 重新映射为列表中的索引值(减去1,因为索引从0开始)。 + getnext_idx = param.input_indexes()[input_idx] - 1; // input_idx start from 0. + MS_LOG(INFO) << "remap input_index:" << input_idx << " to getnext_index:" << getnext_idx << "."; + } + // use iterator_getnext op with output_name instead of data op in BuildGraph. + if (dataset_iter_getnext_ != nullptr) { ///如果 dataset_iter_getnext_ 不为空,则将处理器存储在 out_handle_cache_ 中。dataset_iter_getnext_ 可能是一个数据集迭代器节点的操作(Operator)。 + out_handle_cache_[it.get()] = OutHandler(dataset_iter_getnext_, "y" + std::to_string(getnext_idx)); + } + } +} + +/* +该函数的目的是根据广播操作、广播描述和广播图等信息构建广播子图,并将其存储在 broadcast_graph_中,用于实现广播操作。 +广播操作是指在计算中将低维数据自动扩展为高维数据,以便于进行张量运算。 +在图操作转换过程中,广播子图的构建可能涉及到维度扩展和数据对齐等处理。 +*/ +void DfGraphConvertor::SetupBroadcast(const std::shared_ptr &broadcast, + const std::vector &broadcast_desc, + const DfGraphPtr &broadcast_graph, std::vector broadcast_input) { + //const std::shared_ptr &broadcast(广播操作的指针)、 + //const std::vector &broadcast_desc(广播描述的向量)、 + //const DfGraphPtr &broadcast_graph(广播图的指针) + //std::vector broadcast_input(广播输入的向量)。 + MS_LOG(INFO) << "build broadcast subgraph"; //输出日志,表示正在构建广播子图。 + if (broadcast_desc.size() != broadcast_input.size()) { //检查广播描述的数量是否等于广播输入的数量,如果不相等,则抛出异常。 + MS_LOG(EXCEPTION) << "Desc number of BroadCast is not equal to number of Input"; + } + //通过调用 create_dynamic_input_x 和 create_dynamic_output_y 方法为广播操作创建动态输入和输出 + (void)broadcast->create_dynamic_input_x(static_cast(broadcast_input.size())); + (void)broadcast->create_dynamic_output_y(static_cast(broadcast_desc.size())); + for (unsigned int i = 0; i < broadcast_input.size(); i++) { //使用循环为广播操作的动态输入和输出设置相应的描述和数据 + (void)broadcast->set_dynamic_input_x(i, broadcast_input[i]); + (void)broadcast->update_dynamic_output_desc_y(i, broadcast_desc[i]); + } + (void)broadcast_graph->SetInputs(broadcast_input); //将广播图 broadcast_graph 的输入设置为 broadcast_input,并将广播图存储在 broadcast_graph_ 中。 + this->broadcast_graph_ = broadcast_graph; +} + +/* +该函数的目的是根据给定的 TensorOrderMap初始化参数,并构建相关的初始化子图和操作。 +在图操作转换过程中,参数初始化是一个重要的步骤,该函数完成了参数的创建、初始化数据的添加以及初始化子图的构建等任务。 +*/ +void DfGraphConvertor::InitParamWithData(const TensorOrderMap &tensors) { + int index = 0; //初始化一些变量,包括 index(索引),init_input(初始化子图的输入操作向量)等。 + std::vector init_input; + for (auto it : tensors) { //对于 tensors 中的每个参数 it,根据参数名查找对应的节点 node。 + std::string name = it.first; + auto node_itor = params_.find(name); + // if name not in params_, create a node in graph + if (node_itor == params_.end()) { //如果参数名不存在于 params_ 中,则表示该参数节点尚未创建,此时创建一个名为 name + "_temp" 的新节点,并将其转换为图操作。 + MS_LOG(WARNING) << name << " is not in params, and create a new node."; + ParameterPtr param = std::make_shared(nullptr); + name = name + "_temp"; + param->set_name(name); + (void)ConvertParameter(param); + node_itor = params_.find(name); + } + auto node = node_itor->second; //根据节点 node 查找对应的操作(Operator)并存储在 op_itor 中,如果未找到操作则抛出异常。 + auto op_itor = op_cache_.find(node.get()); + if (op_itor == op_cache_.end()) { + MS_LOG(EXCEPTION) << "Can not find op for node " << node->ToString() << "."; + } + auto adpt = FindAdapter(kNameParam, training_); //查找参数适配器 adpt,根据参数名 kNameParam 和训练状态 training_ 来获取适配器,如果适配器为空则继续下一个参数。 + if (adpt == nullptr) continue; + auto param_op = adpt->generate(name + "_data"); //根据参数名 name 创建一个名为 name + "_data" 的参数操作 param_op。 + MS_LOG(INFO) << "Add parameter " << name << " as input, index " << index << "."; + + if (!training_) { //如果不处于训练状态 training_,则表示当前是推理阶段,需要创建常量操作,将初始化数据添加到图中。 + auto adpt_const = FindAdapter(kNameConst, training_); //查找常量适配器 adpt_const,根据参数名 kNameConst 和训练状态 training_ 来获取适配器,如果适配器为空则继续下一个参数。 + if (adpt_const == nullptr) continue; + auto const_op = adpt_const->generate(name + "_const"); + (void)adpt_const->setAttr(const_op, "value", it.second); //创建常量操作 const_op,设置常量操作的属性 "value" 为参数的初始化数据。 + + auto const_op_desc = TransformUtil::GetGeTensorDesc(it.second->shape_c(), it.second->data_type(), kOpFormat_NCHW); //创建初始化数据的输出描述 const_op_desc,并将其更新到常量操作的输出描述。 + if (const_op_desc == nullptr) { + MS_LOG(WARNING) << "Create variable " << name << " output descriptor failed!"; + continue; + } + (void)std::static_pointer_cast(const_op)->update_output_desc_y(*const_op_desc); + + vars_[name] = const_op; + op_itor->second = const_op; + continue; + } + + // create tensor descriptor for output descriptor 创建输出描述 desc,表示参数的形状、数据类型和格式。 + auto desc = TransformUtil::GetGeTensorDesc(it.second->shape_c(), it.second->data_type(), kOpFormat_NCHW); + if (desc == nullptr) { + MS_LOG(ERROR) << "Create variable " << name << " output descriptor failed!"; + continue; + } + + // we need three variable ops for each graph with same name + // build init subgraph + //对于非初始化数据(it.second->is_init() == 0),创建三个变量操作:param_op、init_var 和 assign_op,并将其加入 init_ops_ 和 init_input。 + if (it.second->is_init() == 0) { + (void)std::static_pointer_cast(param_op)->set_attr_index(index++); //对于初始化数据,不再创建变量操作,直接将其替换为参数操作 param_op。 + auto init_var = std::make_shared(name); + auto assign_op = std::make_shared("assign_" + name); + (void)init_var->update_output_desc_y(*desc); + (void)assign_op->set_input_ref(*init_var).set_input_value(*param_op); + init_input.push_back(*init_var); + init_ops_.push_back(param_op); + init_ops_.push_back(assign_op); + init_ops_.push_back(init_var); + } + + auto variable = std::make_shared(name); + (void)variable->update_output_desc_y(*desc); + // do not use read variable while variable sink + MS_LOG(DEBUG) << "InitParam, op_name = " << name << ", var = " << variable->GetName() << "."; + op_itor->second = variable; // replace parameter with variable + vars_[name] = variable; // prevent the variable operator from being freed + DrawParamInitSubGraph(name, node); //调用 DrawParamInitSubGraph 函数绘制参数初始化子图的描述。 + } + InitLoopVar(&init_input); //调用 InitLoopVar 函数初始化循环变量。 + SetupParamInitSubGraph(tensors, &init_input); //调用 SetupParamInitSubGraph 函数设置参数初始化子图。 +} + + +/* +该函数的目的是初始化图操作转换器,并根据给定的 TensorOrderMap进行参数初始化和数据处理。 +在图操作转换过程中,参数的初始化和数据处理是图构建过程中的重要步骤。 +*/ +// convert all parameter need initialize to variable +DfGraphConvertor &DfGraphConvertor::InitParam(const TensorOrderMap &tensors) { + size_t input_idx = 0; //初始化变量 input_idx(输入索引)。 + if (error_ != SUCCESS) { //检查 error_ 是否为 SUCCESS,如果不是,则直接返回当前的图操作转换器。 + return *this; + } + if (anf_graph_ == nullptr || anf_graph_->output() == nullptr) { //检查 anf_graph_ 和 anf_graph_->output() 是否合法 + error_ = INVALID_ARGUMENT; //如果不合法,则将 error_ 设置为 INVALID_ARGUMENT 并输出错误信息,然后返回当前的图操作转换器 + MS_LOG(ERROR) << "Invalid AnfGraph in InitParam."; + return *this; + } + + // Processing input with MakeDatasetHandler + for (auto &it : anf_graph_->parameters()) { //遍历 anf_graph_->parameters(),即图的参数节点。 + auto op_itor = op_cache_.find(it.get()); // converted node 对于每个参数节点 it,查找对应的操作(Operator)并存储在 op_itor 中。 + if (it->isa() && op_itor != op_cache_.end()) { //如果节点是 Parameter 类型且在 op_cache_ 中找到了对应的操作,则表示该节点为参数节点,并且还需要进行数据处理。 + string name = std::static_pointer_cast(it)->name(); //获取参数节点的名称 name + auto tensor_itor = tensors.find(name); // in init value map + if (tensor_itor == tensors.end()) { //查找给定的 tensors 中是否存在该参数的初始化数据,如果不存在,则需要进行数据处理。 + DfGraphConvertor::MakeDatasetHandler(name, input_idx, it); //调用 MakeDatasetHandler 函数处理数据集,为参数节点创建数据集处理器,并传递参数的名称、输入索引和节点。 + input_idx++; //递增 input_idx,表示处理下一个输入。 + } + } + } + InitParamWithData(tensors); //调用 InitParamWithData 函数进行参数初始化,根据给定的 tensors 完成参数的创建、初始化数据的添加和初始化子图的构建。 + init_sout_ << "}" << endl; //输出初始化子图的描述。 + return *this; //返回当前的图操作转换器的引用。 +} + +//非活动预处理器块 +/* +该函数的目的是根据已初始化的变量和保存操作 Save来构建保存检查点子图。 +在图操作转换过程中,保存检查点是一个重要的步骤,该函数完成了保存操作和变量的处理,以及保存检查点子图的构建。 +*/ +#if (defined ENABLE_D) //条件编译的预处理指令,当定义了 ENABLE_D 宏时,才会编译以下代码块。 +void DfGraphConvertor::BuildSaveCheckpointGraph() { + std::vector graph_inputs; //初始化变量 graph_inputs(图的输入操作向量) + ge::op::Save save_op("save_parms"); //save_op(保存操作 Save 的实例) + int save_op_is_active = 0; //save_op_is_active(保存操作是否激活的标志,初始值为0) + size_t index = 0; //index(索引,用于保存操作的动态输入索引) + string name; //name(变量的名称) + + auto count_size = std::count_if(vars_.begin(), vars_.end(), [](const auto &it) { + return LongToUlong(it.second == nullptr || it.first.find("/") != std::string::npos); + }); //使用 std::count_if 函数统计 vars_ 中值为 nullptr 或名称中包含 "/" 符号的变量的数量,并将结果保存在 count_size 变量中。 + + (void)save_op.create_dynamic_input_tensors(static_cast(vars_.size() - static_cast(count_size))); + //调用 save_op.create_dynamic_input_tensors 方法创建保存操作 Save 的动态输入张量,数量为 vars_.size() - count_size。 + + // for each "parameter" in anf graph excluding "input" + for (const auto &it : vars_) { //遍历 vars_ 中的每个变量,对于每个非空且名称不包含 "/" 符号的变量,创建对应的变量操作,并将其添加到 save_op 的动态输入张量中。 + name = it.first; + if (it.second == nullptr || name.find("/") != std::string::npos) continue; + Variable variable(name); + (void)variable.update_output_desc_y(it.second->GetOutputDesc(0)); + (void)save_op.set_dynamic_input_tensors(static_cast(index++), variable); + + graph_inputs.push_back(variable); //将每个变量操作添加到 graph_inputs 中,并将其与 save_op 连接起来。 + + if (save_op_is_active == 0) { //如果 save_op_is_active 为0(即没有有效的保存操作),则输出检查点子图的描述。 + checkpoint_sout_ << "op_save" << &save_op << "[label=<"; + checkpoint_sout_ << "" << endl; + checkpoint_sout_ << "" << endl; + checkpoint_sout_ << "" << endl; + checkpoint_sout_ << "
tensor
" + << "\"saveop" + << "\"
> shape=plaintext]" << endl; + } + + checkpoint_sout_ << "param" << it.second << "[shape=octagon, label=\"" << name << "\"]" << endl; + + checkpoint_sout_ << "param" << it.second << "->" + << "op_save" << &save_op << ":1" << endl; + save_op_is_active = 1; + } + if (save_op_is_active) { //如果 save_op_is_active 为1(存在有效的保存操作),则创建保存检查点子图 checkpoint_graph,设置其输入为 graph_inputs 和输出为 graph_output(包含 save_op)。 + std::vector graph_output; + graph_output.emplace_back(save_op); + DfGraphPtr checkpoint_graph = std::make_shared("checkpoint"); + (void)checkpoint_graph->SetInputs(graph_inputs); + (void)checkpoint_graph->SetOutputs(graph_output); + this->save_ckp_graph_ = checkpoint_graph; //将保存检查点子图存储在 save_ckp_graph_ 中。 + } else { + this->save_ckp_graph_ = nullptr; + } + + checkpoint_sout_ << "}" << endl; //输出检查点子图的描述。 + return; +} +#endif + + +/* +该函数的目的是生成广播子图,用于在分布式训练中对参数进行广播。 +在图操作转换过程中,广播是一个重要的步骤,该函数完成了广播操作和广播子图的构建。 +*/ +DfGraphConvertor &DfGraphConvertor::GenerateBroadcastGraph(const TensorOrderMap &tensors) { + if (error_ != SUCCESS) { //检查 error_ 是否为 SUCCESS,如果不是,则直接返回当前的图操作转换器。 + return *this; + } + if (anf_graph_ == nullptr || anf_graph_->output() == nullptr) { //检查 anf_graph_ 和 anf_graph_->output() 是否合法 + error_ = INVALID_ARGUMENT; //如果不合法,则将 error_ 设置为 INVALID_ARGUMENT 并输出错误信息 + MS_LOG(ERROR) << "Invalid AnfGraph in generate broadcast graph"; + return *this; //然后返回当前的图操作转换器 + } + + DfGraphPtr broadcast_graph = std::make_shared("broadcast"); //创建广播子图 broadcast_graph + // collect the operators create for broadcast sub graph, in order to avoid auto release + std::vector broadcast_input; //初始化变量 broadcast_input(广播子图的输入操作向量) + std::vector broadcast_desc; //broadcast_desc(广播子图输入操作的描述) + auto broadcast = std::make_shared("broadcast_parameter"); //创建广播操作 HcomBroadcast,命名为 "broadcast_parameter" + (void)broadcast->set_attr_root_rank(0); //设置广播的根节点 root_rank 为 0 + (void)broadcast->set_attr_group("hccl_world_group"); //设置广播的通信组 group 为 "hccl_world_group" + broadcast_ops_.push_back(broadcast); //将广播操作保存在 broadcast_ops_ 中 + + // find every parameter, build broadcast subgraph (or initialize the parameter with constant) + for (auto &it : anf_graph_->parameters()) { //遍历 anf_graph_->parameters(),即图的参数节点。 + auto op_itor = op_cache_.find(it.get()); // converted node 对于每个参数节点 it,查找对应的操作(Operator)并存储在 op_itor 中。 + if (it->isa() && op_itor != op_cache_.end()) { //如果节点是 Parameter 类型且在 op_cache_ 中找到了对应的操作,并且在给定的 tensors 中存在对应的初始化数据,则表示该节点为参数节点,并且需要进行广播操作。 + string name = std::static_pointer_cast(it)->name(); //获取参数节点的名称 name。 + auto tensor_itor = tensors.find(name); // in init tensor map + if (tensor_itor != tensors.end()) { //查找给定的 tensors 中是否存在该参数的初始化数据,如果存在,则表示需要进行广播。 + auto tensor = tensor_itor->second; + auto shape_ge = tensor->shape_c(); //获取参数的形状 shape_ge。 + + // create tensor descriptor for output descriptor + //创建用于输出描述符的张量描述符 desc,表示参数的形状和数据类型。 + auto desc = TransformUtil::GetGeTensorDesc(shape_ge, tensor->data_type(), kOpFormat_NCHW); + if (desc == nullptr) { + MS_LOG(ERROR) << "Create variable " << name << " output descriptor failed!"; + continue; + } + + // build broadcast subgraph + if (distribute_) { //如果 distribute_ 为真(表示进行分布式训练),则构建广播子图。 + auto broadcast_var = std::make_shared(name); //如果存在需要广播的参数,创建相应的变量操作 broadcast_var,并将其添加到 broadcast_input 和 broadcast_desc 中 + (void)broadcast_var->update_output_desc_y(*desc); + broadcast_input.push_back(*broadcast_var); + broadcast_desc.push_back(*desc); + broadcast_ops_.push_back(broadcast_var); //将变量操作保存在 broadcast_ops_ 中。 + } + } + } + } + + // set up broadcast sub graph + if (!broadcast_input.empty()) { //设置广播子图的输入和输出,并调用 SetupBroadcast 函数进行广播子图的构建。 + DfGraphConvertor::SetupBroadcast(broadcast, broadcast_desc, broadcast_graph, broadcast_input); + } else { + this->broadcast_graph_ = nullptr; + } + return *this; //返回当前的图操作转换器的引用。 +} + +/* +该函数的目的是生成检查点图,用于在图操作转换过程中保存模型的参数。 +在图操作转换过程中,生成检查点图是一个重要的步骤,用于将训练过程中的模型参数保存到文件中,以便在需要时进行模型的恢复和继续训练。 +*/ +DfGraphConvertor &DfGraphConvertor::GenerateCheckpointGraph() { + if (error_ != SUCCESS) { //检查 error_ 是否为 SUCCESS,如果不是,则输出错误信息,并直接返回当前的图操作转换器。 + MS_LOG(ERROR) << "Generate checkpoint graph failed, found error code " << error_ << "."; + return *this; + } + if (anf_graph_ == nullptr || anf_graph_->output() == nullptr) { //检查 anf_graph_ 和 anf_graph_->output() 是否合法 + error_ = INVALID_ARGUMENT; //如果不合法,则将 error_ 设置为 INVALID_ARGUMENT 并输出错误信息 + MS_LOG(ERROR) << "Invalid AnfGraph in GenerateCheckpointGraph"; + return *this; //然后返回当前的图操作转换器 + } +#ifdef ENABLE_D //在条件编译指令 #ifdef ENABLE_D 内部执行以下操作: + auto ms_context = MsContext::GetInstance(); //获取全局唯一的 MsContext 实例 ms_context + MS_EXCEPTION_IF_NULL(ms_context); // 检查 ms_context 是否为空,如果为空,则抛出异常 + if (ms_context->backend_policy() == "ge") { //检查当前的后端策略 backend_policy 是否为 "ge"(表示使用基于GraphEngine的后端) + BuildSaveCheckpointGraph(); //如果后端策略为 "ge",则调用 BuildSaveCheckpointGraph() 函数来构建保存检查点子图 + // Restoring from checkpoint file is done by pyfront, not in graph now. + } +#endif + return *this; //返回当前的图操作转换器的引用 +} + +/* +该函数的主要目的是将所有的AnfNode转换为对应的运算算子,为后续的图操作转换和构建数据流图做准备。 +*/ +DfGraphConvertor &DfGraphConvertor::ConvertAllNode() { + if (error_ != SUCCESS) { //检查 error_ 是否为 SUCCESS,如果不是,则直接返回当前的图操作转换器。 + return *this; + } + if (anf_graph_ == nullptr || anf_graph_->output() == nullptr) { //检查 anf_graph_ 和 anf_graph_->output() 是否合法 + MS_LOG(ERROR) << "Invalid AnfGraph"; + error_ = FAILED; //如果不合法,则将 error_ 设置为 FAILED 并输出错误信息 + return *this; //返回当前的图操作转换器 + } + //清空计算图 compute_sout_、初始化图 init_sout_、恢复检查点图restore_checkpoint_sout_ 和检查点图 checkpoint_sout_ + //的内容,并初始化为新的图。 + compute_sout_.clear(); + compute_sout_ << "digraph {" << endl; + init_sout_.clear(); + init_sout_ << "digraph {" << endl; +#ifdef ENABLE_D //在条件编译指令 #ifdef ENABLE_D 内部执行以下操作: + auto ms_context = MsContext::GetInstance(); //获取全局唯一的 MsContext 实例 ms_context + MS_EXCEPTION_IF_NULL(ms_context); //检查 ms_context 是否为空,如果为空,则抛出异常 + if (ms_context->backend_policy() == "ge") { //检查当前的后端策略 backend_policy 是否为 "ge"(表示使用基于GraphEngine的后端) + checkpoint_sout_.clear(); // 如果后端策略为 "ge",则清空检查点图 checkpoint_sout_ 的内容,并初始化为新的图 + checkpoint_sout_ << "digraph {" << endl; //结束条件编译指令 + } +#endif + restore_checkpoint_sout_.clear(); //清空恢复检查点图 restore_checkpoint_sout_ 的内容,并初始化为新的图 + restore_checkpoint_sout_ << "digraph {" << endl; + + // Convert all anf node to Operator + MS_LOG(DEBUG) << "convert all node"; + std::vector nodes = GetOrderedCNodes(anf_graph_); //获取有序的AnfNode节点列表 nodes,用于按照拓扑排序的顺序遍历所有的AnfNode。 + for (auto &it : nodes) { + (void)Convert(it); //对于每个AnfNode it,调用 Convert 函数将其转换为对应的运算算子(Operator)。 + if (this->error_ != SUCCESS) { //检查转换是否成功,如果出现错误,输出错误信息。 + MS_LOG(ERROR) << "failed to convert node: " << it->DebugString() << "."; + } + } + + // Create dataset iterator and iterator_getnext node + if (ConfigManager::GetInstance().dataset_mode() == DS_SINK_MODE) { //如果处于数据集Sink模式下,则创建数据集迭代器和GetNext算子。 + DatasetGraphParam param = ConfigManager::GetInstance().dataset_param(); + MS_LOG(INFO) << "Dataset param is " << param.ToString() << "."; + // GetNext + auto iter_getnext_op = make_shared("get_next_tmp"); + std::vector getnext_types; + const auto &origin_ge_types = param.ge_types(); + (void)std::transform( + origin_ge_types.begin(), origin_ge_types.end(), std::back_inserter(getnext_types), + [](int64_t t_num) -> enum ge::DataType { return static_cast(t_num); }); + (void)iter_getnext_op->set_attr_output_types(getnext_types); + (void)iter_getnext_op->set_attr_output_shapes(param.shapes()); + (void)iter_getnext_op->set_attr_channel_name(param.queue_name()); + + // save iter_getnext_op for later use + dataset_iter_getnext_ = iter_getnext_op; + } + + // return the data flow graph + return *this; //返回当前的图操作转换器的引用。 +} + +/* +该函数的目的是从缓存中获取特定AnfNode的输出信息,并将其添加到图的输出列表,以便后续构建数据流图时使用。 +在构建数据流图时,可以根据图的输出列表来确定图的输出节点。 +*/ +void DfGraphConvertor::TraceOutputFromTupleGetItem(const AnfNodePtr &anf_out) { + auto it = out_handle_cache_.find(anf_out.get()); //通过传入的 anf_out,在缓存 out_handle_cache_ 中查找对应的输出信息 + if (it != out_handle_cache_.end()) { //如果找到了对应的输出信息(即 it 不等于 out_handle_cache_.end()),则获取该输出信息的 OutHandler 对象 handle + OutHandler handle = it->second; + auto op = handle.op; //从 handle 中获取运算算子(Operator)的指针 op + if (op != nullptr) { //如果 op 不为空,则输出该运算算子的名称、类型以及输出名,并将该运算算子与输出名添加到图的输出列表 graph_outputs_ 中。 + MS_LOG(INFO) << "op name: " << op->GetName() << ", op type: " << op->GetOpType() << ", out_name: " << handle.out; + (void)graph_outputs_.emplace_back(*op, handle.out); + } else { //如果 op 为空,则表示对应的AnfNode还没有被成功转换为运算算子,此时抛出异常。 + MS_LOG(EXCEPTION) << "tuple_getitem: " << anf_out->fullname_with_scope() << " is not converted"; + } + } else { //如果在缓存中找不到对应的输出信息,即 it 等于 out_handle_cache_.end(),则输出警告信息,表示出现了无效的 tuple_getitem + // invalid tuple_getitem e.g. tuple_getitem(tuple_getitem())/tuple_getitem(depend())/tuple_getitem(make_tuple()) + MS_LOG(WARNING) << "Invalid tuple_getitem: " << anf_out->fullname_with_scope(); + } +} + +/* +该函数的目的是跟踪给定AnfNode及其所有的输出,并将其添加到图的输出列表,以便在构建数据流图时使用。 +通过递归调用,可以处理复杂的计算图结构,确保所有输出信息都被正确地记录在图的输出列表中。 +*/ +void DfGraphConvertor::TraceOutput(const AnfNodePtr node) { + MS_EXCEPTION_IF_NULL(node); //检查输入的AnfNode是否为空,如果为空,则抛出异常 + AnfNodePtr anf_out = node; + AnfNodePtr pre_node = nullptr; + + // Trace value node + if (node->isa()) { //如果AnfNode是一个ValueNode(值节点) + auto op = Convert(anf_out); //调用 Convert 函数将其转换为运算算子,并将该运算算子添加到图的输出列表 graph_outputs_ 中。 + if (op != nullptr) { + (void)graph_outputs_.emplace_back(*op, ""); + AddGraphConstInput(op); + } + return; + } + + // Trace Parameter node + TraceOutputFromParameter(anf_out); //如果AnfNode是一个Parameter节点(参数节点),则调用 TraceOutputFromParameter 函数处理该节点。 + + // Then trace cnode + if (!node->isa()) { //检查AnfNode是否是CNode(计算节点) + return; + } + + // trace tuple_getitem + //如果是 tuple_getitem 节点,通过迭代向上跟踪所有的 tuple_getitem 节点,直到找到源头CNode为止,并调用 TraceOutputFromTupleGetItem 处理输出信息。 + while (anf_out->isa() && IsPrimitiveCNode(anf_out, prim::kPrimTupleGetItem)) { + pre_node = anf_out; + anf_out = anf_out->cast()->input(1); + } + // trace every element of make_tuple + //如果AnfNode是CNode且目标函数名为 "MakeTuple",则遍历所有的输入元素并递归调用 TraceOutput 处理每个输入元素。 + auto c = anf_out->cast(); + std::string name = ""; + if (anf_out->isa()) { + name = GetCNodeTargetFuncName(c); + } + + if (name == "MakeTuple") { + for (unsigned int i = 1; i < c->inputs().size(); i++) { + TraceOutput(c->input(i)); + } + } else if (name == prim::kPrimDepend->name()) { //如果目标函数名为 "Depend",则跟踪第一个输入元素。 + if (c->inputs().size() < 3) { // "Depend" primitive have 3 inputs + MS_LOG(EXCEPTION) << "length of inputs is " << c->inputs().size() << ", which is less than 3"; + } + TraceOutput(c->input(1)); + } else if (name == prim::kTupleGetItem) { //如果目标函数名为 "prim::kPrimTupleGetItem",则调用 TraceOutputFromTupleGetItem 处理输出信息。 + TraceOutputFromTupleGetItem(anf_out); + } else { //否则,将AnfNode转换为运算算子,并将其添加到图的输出列表 graph_outputs_ 中。 + //如果在处理 tuple_getitem 时,找到了前置节点(pre_node)的输出信息,则将该信息作为当前节点的输出索引。 + // add outputs + auto op = Convert(anf_out); + std::string index; + if (op != nullptr) { + if ((pre_node != nullptr) && IsPrimitiveCNode(pre_node, prim::kPrimTupleGetItem)) { + auto item = out_handle_cache_.find(pre_node.get()); + if (item != out_handle_cache_.end()) { + index = item->second.out; + } else { + MS_LOG(WARNING) << "Can't get operator: " << anf_out->fullname_with_scope() << " 's output item"; + } + } + MS_LOG(INFO) << "Add graph output: " << anf_out->fullname_with_scope() << ":" << index; + (void)graph_outputs_.emplace_back(*op, index); + } + } +} + +/* +该函数的目的是处理给定的Parameter节点,并将其作为图的输出添加到输出列表中。 +它在处理普通参数和在Dataset图模式下的输入参数时分别进行了不同的处理逻辑,确保所有输出信息都被正确地记录在图的输出列表中。 +*/ +void DfGraphConvertor::TraceOutputFromParameter(const AnfNodePtr &anf_out) { + MS_EXCEPTION_IF_NULL(anf_out); //检查输入的AnfNode是否为空,如果为空,则抛出异常 + if (anf_out->isa()) { //检查AnfNode是否是Parameter节点。如果是Parameter节点,表示该节点是图的输出节点。 + MS_LOG(INFO) << "Add graph output: " << anf_out->fullname_with_scope(); + auto it = out_handle_cache_.find(anf_out.get()); + if (it != out_handle_cache_.end()) { //如果在 out_handle_cache_ 中找到该Parameter节点的输出句柄(OutHandler), + //说明该Parameter节点是在Dataset图模式下的输入参数,需要特殊处理。将其作为图的输出添加到输出列表 graph_outputs_ 中,并记录其对应的运算算子(op)以及输出名称(out_name)。 + // For dataset graph mode, input parameter is converted to a "iterator_get_next:yn" OutHandler. + OutHandler handle = it->second; + auto op = handle.op; + MS_LOG(INFO) << "op name: " << op->GetName() << ", op type: " << op->GetOpType() << ", out_name: " << handle.out; + (void)graph_outputs_.emplace_back(*op, handle.out); + } else { //如果在 out_handle_cache_ 中未找到该Parameter节点的输出句柄,说明该Parameter节点是普通的输入参数, + //将其转换为运算算子并添加到输出列表 graph_outputs_ 中。 + // common parameter case + auto op = Convert(anf_out); + if (op != nullptr) { + MS_LOG(INFO) << "op name: " << op->GetName() << ", op type: " << op->GetOpType(); + (void)graph_outputs_.emplace_back(*op, ""); + } + } + } +} + +/* +该函数的目的是在Dataset图模式下,根据Dataset图的参数信息,设置 iterator_getnext +算子的输出个数和输出描述信息,确保与Dataset图的配置相匹配。 +*/ +void SetupDatasetIterGetNextNode(const OperatorPtr &op) { + if (ConfigManager::GetInstance().dataset_mode() == DS_SINK_MODE) { //检查配置管理器 ConfigManager 的 dataset_mode() 是否为 DS_SINK_MODE,即检查是否在Dataset图模式下。 + DatasetGraphParam param = ConfigManager::GetInstance().dataset_param(); //如果处于Dataset图模式下,从配置管理器中获取Dataset图的参数 param + size_t output_num = param.ge_types().size();//根据参数 param 中的信息,确定 iterator_getnext 算子的输出个数 output_num,即需要设置多少个输出。 + MS_LOG(INFO) << "Set iterator_getnext op's output num = " << output_num << "."; + // set iterator_getnext op's output num 将 op 转换为 ge::op::GetNext 类型的算子,以便进行输出的设置 + shared_ptr iter_getnext = std::static_pointer_cast(op); + (void)iter_getnext->create_dynamic_output_y(static_cast(output_num)); //调用 create_dynamic_output_y 方法,设置 iterator_getnext 算子的输出个数为 output_num + + //对于每个输出,根据 param 中的形状信息和数据类型信息,创建相应的 ge::TensorDesc 对象,并使用 update_dynamic_output_desc_y 方法设置每个输出的描述信息 + for (uint32_t i = 0; i < output_num; i++) { + ge::TensorDesc desc(GeShape(param.shapes()[i]), ge::FORMAT_NCHW, (ge::DataType)param.ge_types()[i]); + // we don't SetRealDimCnt here since GE do not use this output's real-dim + (void)iter_getnext->update_dynamic_output_desc_y((i), desc); + } + } + return; +} + +/* +该函数的目的是处理Case节点的子图,构建Case节点的所有分支的子图,并设置为对应的父算子的子图。 +*/ +void DfGraphConvertor::SetSubgraph(const AnfNodePtr &node) { + if (!node->isa()) { //检查传入的节点 node 是否为CNode类型,如果不是,则直接返回,不做处理 + return; + } + auto cnode = node->cast(); + if (!IsCaseNode(cnode)) { //判断节点 node 是否为Case节点,通过调用 IsCaseNode 方法来判断。 + return; //如果不是Case节点,则同样直接返回,不做处理 + } + std::vector case_inputs; //如果节点 node 是Case节点,那么从Case节点的输入中获取所有的Case分支的输入节点,即 case_inputs。这些输入节点将被用于后续处理子图。 + for (size_t i = 1; i < cnode->inputs().size(); i++) { + case_inputs.emplace_back(cnode->input(i)); + } + std::shared_ptr> branches = std::make_shared>();//创建一个存储DfGraph的指针数组 branches,用于存储Case节点的所有分支子图。 + auto bnode = cnode->input(0)->cast()->input(2)->cast(); + + for (size_t i = 1; i < bnode->inputs().size(); i++) { //从Case节点的输入中获取Case节点的第二个输入,即Case节点的condition值(bnode),并将其转换为CNode类型 + auto branch_node = bnode->input(i)->cast(); + for (size_t j = 2; j < branch_node->inputs().size(); j++) {//遍历 bnode 的所有输入(即Case节点的每个分支),获取每个分支的CNode节点 branch_node + if (std::find(case_inputs.begin(), case_inputs.end(), branch_node->input(j)) == case_inputs.end()) { + case_inputs.emplace_back(branch_node->input(j)); //对于每个分支,遍历其输入节点,将不在 case_inputs 中的输入节点添加到 case_inputs 中,以确保 case_inputs 包含所有分支的输入节点。 + } + } + } + //分别对每个分支节点调用 ProcessSubgraph 方法进行处理,该方法会处理分支节点的子图,并将结果存储在 branches_map_ 中 + for (size_t i = 1; i < bnode->inputs().size(); i++) { + ProcessSubgraph(bnode->input(i), case_inputs); + } + //遍历 bnode 的所有输入(即Case节点的每个分支),将每个分支的子图从 branches_map_ 中取出,并添加到 branches 中 + for (size_t i = 1; i < bnode->inputs().size(); i++) { + (void)branches->emplace_back(branches_map_[bnode->input(i).get()]); + } + + if (op_cache_.find(node.get()) == op_cache_.end()) { + return; + } + + OpAdapterPtr adpt = FindAdapter(node, training_); + if (adpt == nullptr) { + MS_LOG(DEBUG) << "Not found adapter"; + return; + } + //通过调用 Convert 方法将节点 node 转换为 OperatorPtr 类型的算子 op + OperatorPtr op = Convert(node); + (void)adpt->setSubgraph(op, 0, branches); //查找与节点 node 相应的适配器 adpt,并将分支子图 branches 设置为 op 的子图 + return; +} + +/* +该函数的目的是处理Case节点的输入,将每个Case分支的输出信息存储在 tuple_out_handle_cache_ 中, +并将Case节点的输入项存储在case_input_handle_cache_ 中。这些信息将在后续的子图构建中用到。 +*/ +void DfGraphConvertor::GetCaseNodeInput(const CNodePtr node, const CNodePtr input_node) { + std::vector case_inputs; + for (size_t i = 1; i < node->inputs().size(); i++) { //从Case节点的输入中获取所有的Case分支的输入节点,并存储在 case_inputs 中 + case_inputs.emplace_back(node->input(i)); + } + auto bnode = input_node->input(2)->cast(); + MS_EXCEPTION_IF_NULL(bnode); + for (size_t i = 1; i < bnode->inputs().size(); i++) { //从Case节点的输入中获取Case节点的第二个输入,即Case节点的condition值(input_node),并将其转换为CNode类型。 + auto branch_node = bnode->input(i)->cast(); + MS_EXCEPTION_IF_NULL(branch_node); + for (size_t j = 2; j < branch_node->inputs().size(); j++) { + if (std::find(case_inputs.begin(), case_inputs.end(), branch_node->input(j)) == case_inputs.end()) { + case_inputs.emplace_back(branch_node->input(j)); + } + } + } + + const size_t case_index = 1; + const size_t make_tuple_index = 2; + + AnfNodePtr case_index_iter = input_node->input(case_index); + AnfNodePtr make_tuple_iter = input_node->input(make_tuple_index); + auto make_tuple_node = make_tuple_iter->cast(); //获取 input_node 的第二个输入(即Case节点的make_tuple),并转换为CNode类型,并存储在 make_tuple_node 中。 + std::shared_ptr> tuple_items = std::make_shared>();//创建一个存储OutHandler的指针数组 tuple_items,用于存储每个Case分支的输出。 + + for (size_t i = 0; i < case_inputs.size(); i++) { + auto item = case_inputs[i]; + auto op = Convert(item); + if (op != nullptr) { //对于每个输入节点 item,如果可以将其转换为算子 op,则将其添加到 tuple_items 中。 + (void)tuple_items->emplace_back(OutHandler(op, "", item)); + } else if (out_handle_cache_.find(item.get()) != out_handle_cache_.end()) { //否则,如果 item 已经在 out_handle_cache_ 中有对应的OutHandler缓存,则直接将其添加到 tuple_items 中。 + tuple_items->push_back(out_handle_cache_[item.get()]); + } else { ////如果既不能转换为算子也不在 out_handle_cache_ 中,那么添加一个空的OutHandler到 tuple_items 中。 + MS_LOG(DEBUG) << "Add an empty out handler: " << item->ToString(); + tuple_items->emplace_back(OutHandler()); + } + } + + tuple_out_handle_cache_[make_tuple_node.get()] = tuple_items;//将 tuple_items 存储在 tuple_out_handle_cache_ 中,键为 make_tuple_node。 + + std::shared_ptr> case_input_items = std::make_shared>(); + //创建一个存储AnfNodePtr的指针数组 case_input_items,用于存储Case节点的输入项。 + //将Case节点的第一个输入(case_index_iter)和第二个输入(make_tuple_iter)添加到 case_input_items 中, + //并将 case_input_items 存储在 case_input_handle_cache_ 中,键为 node。 + (void)case_input_items->emplace_back(case_index_iter); + (void)case_input_items->emplace_back(make_tuple_iter); + case_input_handle_cache_[node.get()] = case_input_items; +} + +/* +该函数的目的是将在前面的处理过程中成功转换为算子的OutHandler更新到 tuple_out_handle_cache_中, +以确保在后续的处理中能够正确获取相关的算子信息。 +*/ +void DfGraphConvertor::UpdateTupleOutCache() { + for (auto &it : tuple_out_handle_cache_) { //遍历 tuple_out_handle_cache_ 中的每个键值对 + std::size_t len = it.second->size(); //其中键为 it,值为 it.second,即指向 std::vector 的智能指针 + for (std::size_t i = 0; i < len; i++) { //对于每个OutHandler数组,计算其大小,即 len。 + OutHandler handle = (*it.second)[i]; //遍历该OutHandler数组,对于每个OutHandler: + if (handle.op == nullptr) { //如果其 op 为nullptr,表示没有对应的算子,跳过该OutHandler。 + continue; + } + string name = handle.op->GetName(); //否则,获取该OutHandler对应算子的名称 name。 + if (vars_.count(name) && (vars_[name] != nullptr)) { //检查 vars_ 中是否包含该名称,并且对应的算子不为nullptr(即在前面的处理过程中已经成功转换为算子) + (*it.second)[i] = OutHandler(vars_[name], handle.out, handle.node);// 如果满足条件,更新当前OutHandler为 vars_[name] 对应的算子,并保持原来的 out 和 node 信息。 + MS_LOG(INFO) << "update tuple_out_handle_cache_ " << name; //输出日志,表示成功更新了 tuple_out_handle_cache_ 中的信息。 + } + } + } +} + +/* +该函数通过处理ANF图并为图中的每个节点设置所需的输入、输出和依赖项来构建数据流图。 +*/ +DfGraphConvertor &DfGraphConvertor::BuildGraph() { + SetupDatasetIterGetNextNode(dataset_iter_getnext_); //如果数据集模式为DS_SINK_MODE,则设置用于数据集图模式中迭代器的GetNext操作符。 + + if (error_ != SUCCESS) { + return *this; + } + + // Case node set input. + std::vector nodes = GetOrderedCNodes(anf_graph_); + for (auto &it : nodes) { + if (it->isa() && IsCaseNode(it->cast())) { + auto node = it->cast(); + auto input_node = node->input(0)->cast(); + GetCaseNodeInput(node, input_node); //对于ANF图中的每个Case节点,通过遍历其输入节点设置Case节点的输入。 + } + } + + // update tuple_out_handle_cache_ + UpdateTupleOutCache(); //更新tuple_out_handle_cache_,以包含成功转换的OutHandler。 + + // set up dependencies 设置依赖和输入:该函数遍历ANF图中的所有节点,为每个节点设置输入和控制输入,同时处理任何子图并更新操作符描述。 + MS_LOG(DEBUG) << "set up dependencies"; + nodes = GetOrderedCNodes(anf_graph_); + for (auto &it : nodes) { + SetNodeInput(it); + SetOpControlInput(it); + SetSubgraph(it); + UpdateOpDesc(it); + } + + if (error_ == SUCCESS) { //如果没有错误,则使用ANF图的名称创建数据流图(df_graph_)。 + df_graph_ = make_shared(anf_graph_->ToString()); + } else { + return *this; + } + + // set graph input according to the order from anf graph + //设置图输入:根据数据集模式和是否使用自定义输入,设置图的输入,添加任何用于工作与常量的操作符的常量节点作为图的输入。 + std::vector inputs; + if (ConfigManager::GetInstance().dataset_mode() == DS_SINK_MODE) { + inputs.push_back(*dataset_iter_getnext_); + } else { + auto params = anf_graph_->parameters(); + if (use_inputs_) { + params = inputs_; + auto anf_params = anf_graph_->parameters(); + for (size_t i = 0; i < params.size(); i++) { + for (size_t j = 0; j < anf_params.size(); j++) { + if (params[i]->ToString() == anf_params[j]->ToString()) { + params[i] = anf_params[j]; + } + } + } + } + + int index = 0; + for (auto &it : params) { + auto name = std::static_pointer_cast(it)->name(); + // the parameters which has not been converted to var + if (vars_.find(name) == vars_.end()) { + if (HasAbstractMonad(it)) { + MS_LOG(INFO) << it->DebugString() << " is a monad parameter, skip."; + continue; + } + auto op = Convert(it); + MS_EXCEPTION_IF_NULL(op); + MS_LOG(INFO) << "add not var input " << it->ToString() << ", index " << index; + if (op == nullptr) { + MS_LOG(ERROR) << "Convert graph failed!"; + return *this; + } + UpdateDataOpDesc(it, op); + MS_LOG(INFO) << "add input " << it->ToString() << ", index " << index; + (void)std::static_pointer_cast(op)->set_attr_index(index++); + inputs.push_back(*op); + } else if (vars_[name] != nullptr) { + MS_LOG(INFO) << "add var input " << it->ToString(); + auto op = Convert(it); + UpdateConstOpDesc(it, vars_[name]); + MS_EXCEPTION_IF_NULL(op); + inputs.push_back(*op); + } + } + } + + + MS_LOG(DEBUG) << "trace output"; + graph_outputs_.clear(); + TraceOutput(anf_graph_->get_return()->input(1));//对图的输出节点进行跟踪,以填充graph_outputs_向量。 + + // Add const nodes as graph input for some operator work with constant + MS_LOG(INFO) << "graph const input size: " << graph_const_inputs_.size(); + (void)std::transform(graph_const_inputs_.begin(), graph_const_inputs_.end(), std::back_inserter(inputs), + [](const OperatorPtr &x) { return *x; }); + + MS_LOG(INFO) << "set graph input num: " << inputs.size(); + (void)df_graph_->SetInputs(inputs); + + // set graph output + // set the value of finale return apply node as the output of dataflow graph + //设置图输出:使用graph_outputs_向量设置图的输出。 + MS_LOG(DEBUG) << "set output"; + MS_LOG(INFO) << "set graph output num: " << graph_outputs_.size(); + (void)df_graph_->SetOutputs(graph_outputs_); + + compute_sout_ << "}" << endl; + // For the graph(e.g. eval_subgraph) whose IterNum is 1, donot set NeedIteration flag. + //设置NeedIteration标志:如果迭代次数(iter_num)大于1,则将NeedIteration标志设置为true,用于数据流图。 + if (ConfigManager::GetInstance().iter_num() > 1) { + df_graph_->SetNeedIteration(true); + } + return *this; +} + +/* +此函数负责更新运算符的输出描述。 +它确保运算符的输出说明与ConstantkOpFormat_NCHWConstant为参数指定的格式匹配(如果适用)。 +*/ +void DfGraphConvertor::UpdateConstOpDesc(const AnfNodePtr &it, const OperatorPtr &op) const { + if (!it->isa()) { //检查输入是否为节点。如果不是,它会记录一条调试消息,指示它不是参数,并且函数立即返回而无需进一步处理。 + MS_LOG(DEBUG) << "It is not parameter, name: " << it->DebugString(); + return; + } + auto para = it->cast(); //如果是节点,则检索相应的对象(强制转换),并将默认格式分配给字符串变量。 + MS_EXCEPTION_IF_NULL(para); + std::string format = kOpFormat_NCHW; + std::string param_debug_info = para->DebugString(); + auto param_format = param_format_.find(param_debug_info); //检索参数param_debug_info()的调试信息,并尝试在 map 中找到与此参数关联的格式。如果找到格式,它将相应地更新变量并记录调试消息 + if (param_format != param_format_.end()) { + format = param_format->second; //格式未更改,无需更新运算符说明。该函数记录调试消息并返回 + MS_LOG(DEBUG) << "Parameter debug info: " << param_debug_info << ", format is " << format; + } + if (format == kOpFormat_NCHW) { + MS_LOG(DEBUG) << "Format is not changed, no need to update op desc, name: " << param_debug_info; + return; + } + if (!para->has_default()) { + MS_LOG(DEBUG) << "Parameter has no default, no need to update op desc, name: " << param_debug_info; + return; + } + auto value = para->default_param(); + MS_EXCEPTION_IF_NULL(value); + auto tensor = value->cast>(); //假设参数存在默认值,该函数将检索值value() 并将其强制转换为std::shared_ptr + MS_EXCEPTION_IF_NULL(tensor); //使用该函数创建新的运算符描述 (),传递张量的形状、数据类型和更新格式(如果适用)。const_op_descTransformUtil::GetGeTensorDesc + auto const_op_desc = TransformUtil::GetGeTensorDesc(tensor->shape_c(), tensor->data_type(), format); + if (const_op_desc == nullptr) { //如果创建失败(返回 nullptr),该函数将记录警告并返回。 + MS_LOG(WARNING) << "Create parameter " << para->name() << " output descriptor failed!"; + return; + } + (void)std::static_pointer_cast(op)->update_output_desc_y(*const_op_desc); //使用新创建的 .Constantopconst_op_desc +} + +void DfGraphConvertor::UpdateDataOpDesc(const AnfNodePtr &it, const OperatorPtr &op) const { + auto node = std::static_pointer_cast(it); //将节点it转换为std::shared_ptr + if (node == nullptr) { //如果转换失败或node是nullptr,则会记录错误并返回。 + MS_LOG(ERROR) << "Update data op descriptor failed! Invalid node."; + return; + } + + std::vector shape; //从abstract::Shape中提取节点的形状。如果无法提取形状或节点具有无效的形状,它将记录一条消息abstract::NoShape并返回。 + if (auto normal_shape_ptr = dyn_cast(node->Shape()); normal_shape_ptr != nullptr) { + shape = normal_shape_ptr->shape(); + } else if (auto no_shape_ptr = dyn_cast(node->Shape()); no_shape_ptr != nullptr) { + shape = {}; + } else { + MS_LOG(INFO) << "Invalid shape to update data op descriptor."; + return; + } + + if (node->Type() == nullptr) { //检查节点的类型。如果类型不可用(即nullptr),将记录一条消息并返回。 + MS_LOG(INFO) << "Invalid type to update data op descriptor."; + return; + } + TypeId me_type = node->Type()->type_id(); + if (kObjectTypeTensorType == me_type) { //如果节点类型为kObjectTypeTensorType,则尝试从中提取元素类型。 + me_type = dyn_cast(node->Type())->element()->type_id(); + } + std::ostringstream buf; + buf << "[" << shape << "]"; + MS_LOG(INFO) << "input shape is " << buf.str() << ", type is " << me_type;//使用MS_LOG(INFO)在日志中打印节点的形状和类型信息。 + std::string format = "NCHW"; + if (it->isa()) { //如果节点的类型为Parameterparam_format_,则尝试提取其名称并查找其格式 + auto param = it->cast(); + std::string param_name = param->DebugString(); + auto param_format = param_format_.find(param_name); + if (param_format != param_format_.end()) { //确定节点的格式后(如果在param_format_中找不到,则默认为“NCHW”),它会调用形状、类型和格式以获取张量描述符 + format = param_format->second; + MS_LOG(DEBUG) << "parameter: " << param_name << ", format is " << format; + } + } + auto desc = TransformUtil::GetGeTensorDesc(shape, me_type, format); + if (desc == nullptr) { //如果为 null,则记录错误;否则,使用获得的张量描述符更新对象的输入和输出描述符 + MS_LOG(ERROR) << "Update data op descriptor failed! TensorDesc is null."; + } else { + (void)std::static_pointer_cast(op)->update_input_desc_x(*desc); + (void)std::static_pointer_cast(op)->update_output_desc_y(*desc); + } +} + +DfGraphPtr DfGraphConvertor::GetComputeGraph() { return df_graph_; } + +DfGraphPtr DfGraphConvertor::GetInitGraph() { return init_graph_; } + +DfGraphPtr DfGraphConvertor::GetSaveCheckpointGraph() { return save_ckp_graph_; } + +DfGraphPtr DfGraphConvertor::GetBroadcastGraph() { return broadcast_graph_; } + +/* +该函数用于判断一个节点是否为源边节点 +*/ +bool DfGraphConvertor::IsSourceEdgeNode(const AnfNodePtr &node) { + if (!node->isa()) { //判断该节点是否为 CNode 类型,如果不是则返回 false + return false; + } + auto cnode = node->cast(); + if (!IsCustomCNode(cnode)) { //获取该 CNode 的目标函数名,如果为空则返回 false + std::string name = GetCNodeTargetFuncName(cnode); + if (name.empty()) { + return false; + } + + // Ignore apply node Depend, UpdateState, make_tuple. make_tuple in ge pipeline. + //忽略一些特定的节点,如 Depend、UpdateState、make_tuple 和 Return + //如果节点的目标函数名是这些特定节点之一,则返回 false。 + if ((name == prim::kPrimDepend->name()) || (name == prim::kPrimUpdateState->name()) || + (name == prim::kPrimReturn->name()) || (name == prim::kPrimMakeTuple->name())) { + return false; + } + } + // Load and other normal primitives which contain monad node. + //检查该节点的输入是否包含 monad 节点,如果有则返回 true + auto has_monad = std::any_of(cnode->inputs().begin(), cnode->inputs().end(), + [](const AnfNodePtr &node) -> bool { return HasAbstractMonad(node); }); + if (has_monad) { + return true; + } + + // primitive with make_tuple as input + //检查该节点的输入是否包含以 make_tuple 为目标函数的 CNode。如果是,则检查 make_tuple 的输入是否包含 monad 节点,如果有则返回 true。 + for (auto &input : cnode->inputs()) { + if (IsPrimitiveCNode(input, prim::kPrimMakeTuple)) { + auto tuple = input->cast(); + auto ret = std::any_of(tuple->inputs().begin(), tuple->inputs().end(), + [](const AnfNodePtr &node) -> bool { return HasAbstractMonad(node); }); + if (ret) { + return true; + } + } + } + //如果以上条件都不满足,则返回 false,表示该节点不是源边节点。 + return false; +} + +/* +该函数用于判断一个节点是否为控制边节点 +*/ +bool DfGraphConvertor::IsControlEdgeNode(const AnfNodePtr &node) { + if (!node->isa()) { //判断该节点是否为 CNode 类型,如果不是则返回 false + return false; + } + auto cnode = node->cast(); + if (!IsCustomCNode(cnode)) { //获取该 CNode 的目标函数名,如果为空则返回 false + std::string name = GetCNodeTargetFuncName(cnode); + if (name.empty()) { + return false; + } + + // Ignore apply node of Load, Depend, UpdateState, make_tuple, return + //忽略一些特定的节点,如 Load、Depend、UpdateState、make_tuple 和 Return + //如果节点的目标函数名是这些特定节点之一,则返回 false + if ((name == prim::kPrimLoad->name()) || (name == prim::kPrimDepend->name()) || + (name == prim::kPrimUpdateState->name()) || (name == prim::kPrimMakeTuple->name()) || + (name == prim::kPrimReturn->name())) { + return false; + } + } + //如果以上条件都不满足,则返回 true,表示该节点是控制边节点 + return true; +} + +/* +将给定的AnfNodePtr对象转换为OperatorPtr对象。 +在这之前,先调用GetRealOpNode函数获取真实操作节点,并将其传递给Convert函数进行转换。 +如果转换失败,将记录错误日志并返回nullptr,否则返回转换后的OperatorPtr对象。 +*/ +OperatorPtr DfGraphConvertor::ToOperatorPtr(const AnfNodePtr &node) { + auto op = Convert(GetRealOpNode(node)); // 获取真实操作节点 + if (op == nullptr) { //// 如果转换失败,则记录错误日志,并设置error_为FAILED + MS_LOG(ERROR) << "Convert real op node to operator failed, " << node->ToString(); + error_ = FAILED; + return nullptr; + } + //返回转换后的OperatorPtr对象 + return op; +} + +/* +是为DfGraphConvertor类维护的monad_control_edge_cache_缓存添加控制依赖边。 +在这个缓存中,每个源节点src都有一个对应的目标节点集合,表示src所依赖的控制节点。 +*/ +void DfGraphConvertor::AddEdgeToCache(const AnfNodePtr &src, const AnfNodePtr &dest) { + auto item = monad_control_edge_cache_.find(src); //检查源节点是否已存在于控制依赖边缓存中 + if (item == monad_control_edge_cache_.end()) { // 如果源节点不存在于缓存中,则创建一个新的缓存项,并将目标节点添加到该缓存项中 + monad_control_edge_cache_[src] = std::set{dest}; + } else { // 如果源节点已存在于缓存中,则将目标节点添加到该源节点的依赖节点集合中 + // 使用insert函数插入目标节点,set确保不会重复插入重复的目标节点 + (void)item->second.insert(dest); + } +} + +//该函数为Load类型节点添加控制依赖边 +void DfGraphConvertor::AddEdgeForLoad(const AnfNodePtr &node) { + auto func_graph = node->func_graph(); // 获取节点所属的函数图 + MS_EXCEPTION_IF_NULL(func_graph); + auto mng = func_graph->manager(); // 获取函数图的管理器 + if (mng == nullptr) { // 如果管理器为空,则创建一个新的管理器,并将其设置为函数图的管理器 + mng = Manage(func_graph, true); + func_graph->set_manager(mng); + } + auto manager = func_graph->manager(); //再次获取函数图的管理器 + MS_EXCEPTION_IF_NULL(manager); + if (manager->node_users().find(node) == manager->node_users().end()) { // 检查节点是否在管理器的节点用户集合中 + MS_LOG(EXCEPTION) << "Can't find node in nodes_users."; + } + auto &users = manager->node_users()[node]; // 获取节点的用户集合 + // 创建用于存储源节点和目标节点的共享指针列表 + std::shared_ptr> src_node_list = std::make_shared>(); + std::shared_ptr> dst_node_list = std::make_shared>(); + for (const auto &iter : users) { // 遍历节点的用户集合,将相关的源节点和目标节点添加到对应的列表中 + auto user_node = iter.first; + auto name = GetCNodeTargetFuncName(user_node->cast()); + if (name == prim::kPrimUpdateState->name()) { // 如果用户节点是prim::kPrimUpdateState类型,则将其作为目标节点,并查找它的目标操作节点 + FindDestOps(user_node, dst_node_list, false); + continue; + } + if (IsControlEdgeNode(user_node)) { // 如果用户节点是控制边节点(可能是ControlDepend类型),则将其作为源节点 + src_node_list->push_back(user_node); + continue; + } + FindDestOps(user_node, src_node_list, false); // 否则,将用户节点作为普通的源节点,并查找它的目标操作节点 + } + + // add to cache + // 将源节点和目标节点的组合添加到控制依赖边缓存中 + for (auto &dest : *dst_node_list) { + for (auto &src : *src_node_list) { + AddEdgeToCache(src, dest); + } + } +} + +/* +该函数的主要目的是递归地查找给定节点的目标操作节点,并将这些目标操作节点添加到node_list中。 +top参数用于标识当前节点是否为最顶层节点,如果为true,则只有当用户节点是控制边节点时才会将其添加到node_list中。 +如果为false,则不论用户节点类型,都会将其添加到node_list中。 +*/ +void DfGraphConvertor::FindDestOps(const AnfNodePtr &node, const std::shared_ptr> &node_list, + bool top) { + MS_EXCEPTION_IF_NULL(node); // 检查输入节点是否为空 + auto func_graph = node->func_graph(); // 获取节点所属的函数图 + MS_EXCEPTION_IF_NULL(func_graph); + auto mng = func_graph->manager(); // 获取函数图的管理器 + if (mng == nullptr) { // 如果管理器为空,则创建一个新的管理器,并将其设置为函数图的管理器 + mng = Manage(func_graph, true); + func_graph->set_manager(mng); + } + auto manager = func_graph->manager(); // 再次获取函数图的管理器 + MS_EXCEPTION_IF_NULL(manager); + + auto users = manager->node_users()[node]; // 获取节点的用户集合 + for (const auto &iter : users) { // 遍历节点的用户集合 + auto user_node = iter.first; + if (IsControlEdgeNode(user_node)) { // 如果用户节点是控制边节点(可能是ControlDepend类型),并且不是最顶层节点,则将其添加到node_list中 + if (!top) { + node_list->push_back(user_node); + } + } else { // 否则,递归地查找该用户节点的目标操作节点,并将其添加到node_list中 + FindDestOps(user_node, node_list, false); + } + } +} + +/* +该函数主要用于自动收集Monad输入,并根据情况建立相应的控制依赖边。 +在深度学习框架中,Monad通常是指一种特殊的数据依赖关系,用于控制计算图的执行顺序。 +*/ +void DfGraphConvertor::AutoMonadCollectInput(const AnfNodePtr &node) { + if (!IsSourceEdgeNode(node)) { // 检查节点是否为源边节点,如果不是,则不需要处理控制依赖 + return; + } + + // Add control edge if contain monad input. + // 如果是Load类型节点,则为其添加控制依赖边 + std::string name = GetCNodeTargetFuncName(node->cast()); + if (name == prim::kPrimLoad->name()) { + AddEdgeForLoad(node); + } else { // 否则,获取节点对应的操作对象 + auto src_ops = ToOperatorPtr(node); + if (src_ops != nullptr) { // 如果操作对象存在,则查找其目标操作节点并为其添加控制依赖边 + // Find dest ops list + // 查找目标操作节点列表 + std::shared_ptr> dst_node_list = std::make_shared>(); + FindDestOps(node, dst_node_list, true); + for (auto &dest : *dst_node_list) { // 将源节点与目标操作节点逐一添加为控制依赖边 + AddEdgeToCache(node, dest); + } + } + } +} + +/* +该函数用于自动设置Monad输入,即根据控制依赖边缓存(monad_control_edge_cache_)中的信息, +为给定节点建立控制依赖边。 +*/ +void DfGraphConvertor::AutoMonadSetInput(const AnfNodePtr &node) { + // 检查节点是否在控制依赖边缓存中,如果不在,则不需要进行设置 + if (monad_control_edge_cache_.find(node) == monad_control_edge_cache_.end()) { + return; + } + + auto src_ops = ToOperatorPtr(node); // 获取节点对应的操作对象 + if (src_ops != nullptr) { // 如果操作对象存在,则遍历其对应的控制依赖目标节点,并为目标节点添加控制输入 + for (auto &dest : monad_control_edge_cache_[node]) { + auto dest_ops = ToOperatorPtr(dest); + if (dest_ops == nullptr) { // 如果目标操作对象不存在,则跳过该目标节点 + continue; + } + (void)dest_ops->AddControlInput(*src_ops); // 为目标操作对象添加控制输入,建立控制依赖边 +#ifdef DRAW_GE_GRAPH // 在DEBUG模式下,绘制计算图时输出控制依赖关系 + compute_sout_ << op_draw_name_[node.get()] << " -> " << op_draw_name_[dest.get()] << "[style=\"dotted\"]" << endl; +#endif + } + } +} + +/* +该函数主要调用了两个函数来自动设置控制依赖边。 + 这些控制依赖边是为了确保在深度学习框架中,计算图的执行顺序符合数据流和依赖关系的要求,以保证计算结果的正确性。 +*/ +void DfGraphConvertor::AutoMonadSetControlInput(const AnfNodePtr &node) { + AutoMonadCollectInput(node); // 自动收集Monad输入,建立控制依赖边 + AutoMonadSetInput(node); // 自动设置Monad输入,建立控制依赖边 +} + +//该函数主要用于为操作节点设置控制输入,即建立控制依赖边。 +void DfGraphConvertor::SetOpControlInput(const AnfNodePtr &node) { + MS_EXCEPTION_IF_NULL(node); // 检查输入节点是否为空 + AutoMonadSetControlInput(node); // 自动设置Monad输入,建立控制依赖边 + if (control_edge_cache_.find(node.get()) == control_edge_cache_.end()) { // 检查当前节点是否在控制边缓存中 + return; //如果不在,则直接返回 + } + // 获取当前节点的控制边缓存信息 + std::vector control_edges = control_edge_cache_[node.get()]; + if ((control_edges.empty())) { // 如果控制边缓存为空,则记录错误日志并返回 + MS_LOG(ERROR) << "Get control edge node's src or dest operator failed"; + return; + } + + for (auto &item : control_edges) { // 为当前节点的目标操作节点添加控制输入 + (void)item.dest_op->AddControlInput(*item.src_op); + } +} +//不可变的常量向量 +const std::vector trans_var_list = {string(kNameAssign), string(kNameAssignAdd), string(kNameAssignSub)}; + +//该函数用于从Load类型节点中获取对应的常数参数节点 +AnfNodePtr DfGraphConvertor::ParseLoadInput(const CNodePtr &cnode) { + if (cnode->inputs().size() < 3) { // 检查CNode的输入数量是否小于3 + MS_LOG(EXCEPTION) << "input size error, " << cnode->ToString(); + } + const size_t para_index = 1; // 定义常数参数的索引为1(Load节点通常为cnode->inputs()[1]) + return cnode->input(para_index); // 返回Load节点的常数参数对应的AnfNodePtr +} + +//该函数用于处理元组类型节点的输入,并将它们设置为目标操作符的输入。 +void DfGraphConvertor::SetTupleOpInput(const OpAdapterPtr &adpt, const CNodePtr &node, const AnfNodePtr &pred, + const OperatorPtr &src, int index) { + // 从元组的输出句柄缓存中获取处理器向量 + std::shared_ptr> handler_vec = tuple_out_handle_cache_[pred.get()]; + // 创建一个新的处理器向量用于保存没有Monad类型的元素 + std::shared_ptr> handler_vec_without_monad = std::make_shared>(); + bool with_monad = false; // 用于标记处理器向量中是否包含Monad类型元素 + // 遍历处理器向量中的每个元素,判断是否包含Monad类型元素,并将非Monad类型的元素添加到新的处理器向量中 + for (auto &handler : *handler_vec) { + // when tuple with monad type element, the handler operator is nullptr, should be ignored. + if (handler.op == nullptr) { + if ((handler.node != nullptr) && !HasAbstractMonad(handler.node)) { + MS_LOG(WARNING) << "Unsupported node in tuple : " << node->ToString(); + } + continue; + } + with_monad = true; + handler_vec_without_monad->push_back(handler); + } + // 使用OpAdapter的setInput方法将新的处理器向量作为输入设置给目标操作符 + int ret = adpt->setInput(src, index, handler_vec_without_monad); + // 如果设置成功,并且预期的上游节点是一个CNode且它的输入数量与处理器向量大小相符(不包含Monad类型的元素) + // 则添加控制依赖边,绘制计算图,同时将处理器向量中的元素作为图的常量输入添加 + if ((ret == 0) && pred->isa() && (pred->cast()->inputs().size() == handler_vec->size() + 1)) { + for (unsigned int j = 0; j < handler_vec_without_monad->size(); j++) { + AnfNodePtr input_node = pred->cast()->input(j + 1); + if (with_monad) { + input_node = handler_vec_without_monad->at(j).node; + } + compute_sout_ << op_draw_name_[input_node.get()] << " -> " << op_draw_name_[node.get()] << ":" << index << endl; + AddGraphConstInput(handler_vec_without_monad->at(j).op); + } + return; + } + // 如果设置失败或预期的上游节点不满足条件,则记录警告日志 + MS_LOG(WARNING) << "This anf node is not supported as a tuple item : " << node->ToString(); +} + +//该函数主要用于获取实际的输入节点,以便进行后续处理或分析。 +AnfNodePtr DfGraphConvertor::GetRealInputNode(const CNodePtr &node, const AnfNodePtr &input) { + if (input == nullptr || node == nullptr) { // 检查输入节点和CNode是否为空 + return nullptr; + } + AnfNodePtr pred = input; // 获取上游节点 + while (pred->isa() && GetCNodeTargetFuncName(pred->cast()) == prim::kPrimDepend->name()) { + pred = pred->cast()->input(1); + } + // skip input of UMonad, IOMonad + // 跳过UMonad和IOMonad类型的节点 + if (IsValueNode(pred) || IsValueNode(pred)) { + return nullptr; + } + // skip input of the None, UpdateState + // 跳过None类型和UpdateState类型的节点 + if (IsValueNode(pred) || IsPrimitiveCNode(pred, prim::kPrimUpdateState)) { + return nullptr; + } + // 对于Load节点,解析其实际输入节点 + if (IsPrimitiveCNode(pred, prim::kPrimLoad)) { + pred = ParseLoadInput(pred->cast()); + } + + // transform "Const" op to "Variable" op when the next node is "Assign" op. + // 当前节点是"Assign"类型节点,且下一个节点是"Const"类型或"Constant"类型的Parameter节点时,转换"Const" op为"Variable" op + std::string c_name = GetCNodeTargetFuncName(node); + auto pos = std::find(trans_var_list.begin(), trans_var_list.end(), c_name); + if (!training_ && pos != trans_var_list.end() && pred->isa()) { + std::string name = std::static_pointer_cast(pred)->name(); + auto op_itor = op_cache_.find(pred.get()); + if (op_itor == op_cache_.end()) { + MS_LOG(EXCEPTION) << "Can not find op for node " << pred->ToString() << "."; + } + if (op_itor->second != nullptr && + (op_itor->second->GetOpType() == "Constant" || op_itor->second->GetOpType() == "Const") && + vars_.find(name) != vars_.end()) { + auto variable = std::make_shared(name); + auto desc = vars_[name]->GetOutputDesc("y"); + (void)variable->update_output_desc_y(desc); + MS_LOG(DEBUG) << "Trans to variable, var = " << variable->GetName() << "."; + op_itor->second = variable; // replace parameter with variable + vars_[name] = variable; + } + } + return pred; // 返回实际的输入节点 +} + +//该函数用于设置操作节点的输入 +void DfGraphConvertor::SetOpInput(const OpAdapterPtr &adpt, const CNodePtr &node) { + OperatorPtr src = Convert(node); // 将CNode节点转换为OperatorPtr + int case_flag = 0; // case_flag用于标记是否存在特殊处理的情况 + auto &inputs = node->inputs(); // 获取CNode节点的输入列表和输入数量 + size_t input_size = inputs.size(); + // 如果该节点在case_input_handle_cache_中,则将case_flag设置为1,同时更新输入数量为cache中的大小+1 + if (case_input_handle_cache_.find(node.get()) != case_input_handle_cache_.end()) { + case_flag = 1; + input_size = case_input_handle_cache_[node.get()]->size() + 1; + } + + for (size_t i = 1; i < input_size; i++) { // 遍历节点的每个输入 + AnfNodePtr pred = nullptr; + if (case_flag != 0) { // 如果存在特殊处理,则从case_input_handle_cache_中获取输入节点 + pred = case_input_handle_cache_[node.get()]->at(i - 1); + } else { // 否则直接从inputs中获取输入节点 + pred = inputs[i]; + } + pred = GetRealInputNode(node, pred); // 获取实际的输入节点,过滤掉不需要的类型 + if (pred == nullptr) { + continue; + } + + int index = SizeToInt(i); // 计算在Operator中的输入索引 + // find in out_hadnle_cache_ first + // 在out_handle_cache_中查找是否有对应的输出句柄 + auto it = out_handle_cache_.find(pred.get()); + if (it != out_handle_cache_.end()) { // 如果找到,则将输出句柄设置为输入 + int ret = adpt->setInput(src, index, it->second); + if (ret == 0) { // 如果成功设置输入,则根据情况绘制计算图中的控制依赖边,并将句柄中的操作对象作为图的常量输入添加 + if (pred->isa() && GetCNodeTargetFuncName(pred->cast()) == prim::kTupleGetItem) { + compute_sout_ << op_draw_name_[pred->cast()->input(1).get()] << " -> " << op_draw_name_[node.get()] + << ":" << i << endl; + } else if (pred->isa()) { + compute_sout_ << op_draw_name_[pred.get()] << " -> " << op_draw_name_[node.get()] << ":" << i << endl; + } else { + // don't draw anything. + // 不绘制任何内容 + MS_LOG(INFO) << "DRAW_GE_GRAPH: Shouldn't have this case."; + } + AddGraphConstInput(it->second.op); + } + } else if (tuple_out_handle_cache_.find(pred.get()) != tuple_out_handle_cache_.end()) { + // 如果在tuple_out_handle_cache_中找到输出句柄,则进行元组类型节点的输入设置 + SetTupleOpInput(adpt, node, pred, src, index); + } else { + // 如果在out_handle_cache_和tuple_out_handle_cache_中都没有找到输出句柄,则直接将输入节点转换为操作对象,并设置为输入 + auto op = Convert(pred); + int ret = adpt->setInput(src, index, op); + if (ret == 0) { + // 如果成功设置输入,则绘制计算图中的控制依赖边,并将操作对象作为图的常量输入添加 + compute_sout_ << op_draw_name_[pred.get()] << " -> " << op_draw_name_[node.get()] << ":" << i << endl; + AddGraphConstInput(op); + } + } + } +} + +//该函数用于向graph_const_inputs_向量中添加常量输入,以便后续在计算图中使用这些常量作为输入。 +void DfGraphConvertor::AddGraphConstInput(const OperatorPtr &op) { + if (op->GetOpType() == "Constant" || op->GetOpType() == "Const") { // 判断操作对象的类型是否为"Constant"或"Const" + graph_const_inputs_.push_back(op); // 如果是常量类型的操作对象,则将其添加到graph_const_inputs_向量中 + } +} + + +//函数会根据节点的类型和输出句柄的情况,正确地设置操作节点的输入,并在计算图中绘制相应的控制依赖边。 +void DfGraphConvertor::SetNodeInput(const AnfNodePtr node) { + if (!node->isa()) { // 判断节点是否是CNode,如果不是则返回 + return; + } + if (op_cache_.find(node.get()) == op_cache_.end()) { // 判断节点是否在op_cache_中,如果不在则返回 + return; + } + auto cnode = node->cast(); // 获取CNode节点,并查找对应的OpAdapter + OpAdapterPtr adpt = FindAdapter(cnode, training_); + if (adpt == nullptr) { // 如果找不到对应的OpAdapter,则将error_标志设置为NOT_FOUND,并返回 + error_ = NOT_FOUND; + return; + } + + // get Operator from op_cache_, use adapter to set Inputs + // 使用OpAdapter的SetOpInput函数设置CNode节点的输入 + DfGraphConvertor::SetOpInput(adpt, cnode); +} + +//该函数用于处理子图节点(Partial节点) +void DfGraphConvertor::ProcessSubgraph(const AnfNodePtr &node, const std::vector &inputs) { + // 判断节点是否是CNode类型且函数名称是否为"Partial",如果不满足条件则直接返回 + if (!node->isa() || GetCNodeFuncName(node->cast()) != "Partial") { + return; + } + // 获取子图节点对应的FuncGraph + auto graph_node = node->cast()->input(1)->cast(); + MS_EXCEPTION_IF_NULL(graph_node); + FuncGraphPtr anf_graph = graph_node->value()->cast(); + + // 创建新的DfGraphConvertor对象,并使用子图FuncGraph作为输入 + DfGraphConvertor converter(anf_graph); + + // 设置converter的use_inputs_为true,表示使用给定的inputs作为子图的输入 + converter.use_inputs_ = true; + converter.inputs_ = inputs; + + // 将子图转换为DfGraph + (void)converter.ConvertAllNode().BuildGraph(); +#ifdef ENABLE_DUMP_IR // 根据配置决定是否绘制计算图 + std::string name = graph_node->ToString() + "_ge_graph.dot"; + if (MsContext::GetInstance()->get_param(MS_CTX_SAVE_GRAPHS_FLAG)) { + converter.DrawComputeGraph(name); + } +#endif // 将转换后的DfGraph存储到branches_map_中,键为子图节点的指针地址,值为转换后的DfGraph对象 + branches_map_[node.get()] = *(converter.df_graph_); +} + +// Update GE op's shape and type info +//该函数用于更新操作的描述信息 +//将节点的形状(Shape)、类型(Type)和节点本身作为参数,来更新对应的操作的输出描述。 +void DfGraphConvertor::UpdateOpDesc(const AnfNodePtr node) { + if (node == nullptr || !node->isa()) { // 判断节点是否为空或非CNode类型,如果是则直接返回 + return; + } + + if (op_cache_.find(node.get()) == op_cache_.end()) { // 判断节点是否在op_cache_中,如果不在则直接返回 + return; + } + + OpAdapterPtr adpt = FindAdapter(node, training_); // 查找节点对应的OpAdapter + if (adpt == nullptr) { // 如果找不到对应的OpAdapter,则将error_标志设置为NOT_FOUND,并返回 + error_ = NOT_FOUND; + return; + } + + // get Operator from op_cache_ + // 获取节点对应的Operator对象 + OperatorPtr op = Convert(node); + + // 使用OpAdapter的updateOutputDesc函数更新操作的输出描述信息 + adpt->updateOutputDesc(op, node->Shape(), node->Type(), node); +} + +//该函数用于将AnfNode节点转换为对应的Operator对象 +OperatorPtr DfGraphConvertor::Convert(const AnfNodePtr node) { + if (node == nullptr) { // 判断节点是否为空,如果为空则设置error_标志为NOT_FOUND,并返回nullptr + MS_LOG(ERROR) << "node is nullptr"; + error_ = NOT_FOUND; + return nullptr; + } + // find in cache + // 在op_cache_中查找节点对应的Operator,如果找到则直接返回 + if (op_cache_.count(node.get())) { + return op_cache_[node.get()]; + } + + // do not convert primitive node, Load, UpdateState + // 对于原语节点(Primitive节点)、Load节点、UpdateState节点,直接返回nullptr,不进行转换 + if (IsValueNode(node) || IsPrimitiveCNode(node, prim::kPrimLoad) || + IsPrimitiveCNode(node, prim::kPrimUpdateState)) { + return nullptr; + } + + // convert a new one + // 对于CNode节点,调用ConvertCNode函数进行转换 + if (node->isa()) { + return ConvertCNode(node->cast()); + } + // 对于Parameter节点,调用ConvertParameter函数进行转换 + if (node->isa()) { + return ConvertParameter(node); + } + // 对于ValueNode节点,根据节点是否为Monad类型来决定是否进行转换 + if (node->isa()) { + if (IsValueNode(node)) { + return nullptr; + } + return ConvertValueNode(node->cast()); + } + // 对于其他类型的节点,设置error_标志为INVALID_ARGUMENT,并返回nullptr + MS_LOG(ERROR) << "Invalid AnfNode"; + error_ = INVALID_ARGUMENT; + return nullptr; +} + +//该函数用于将MakeTuple节点转换为对应的OutHandler列表。 +void DfGraphConvertor::ConvertMakeTuple(const CNodePtr node) { + // 创建一个共享指针,用于存储MakeTuple节点的输出项 + std::shared_ptr> tuple_items = std::make_shared>(); + // convert each tuple item to a OutHandler + // 遍历MakeTuple节点的输入项,并逐个转换为OutHandler + for (size_t i = 1; i < node->inputs().size(); i++) { + AnfNodePtr item = node->input(i); + if (IsPrimitiveCNode(item, prim::kPrimLoad)) { // 如果输入项是Load节点,需要解析其输入,即加载的数据节点 + item = ParseLoadInput(item->cast()); + } + OperatorPtr op = Convert(item); // 将AnfNode节点转换为对应的Operator对象 + if (op != nullptr) { // 如果转换得到的Operator对象不为空,则将OutHandler添加到tuple_items中 + (void)tuple_items->emplace_back(OutHandler(op, "", item)); + } else if (out_handle_cache_.find(item.get()) != out_handle_cache_.end()) { + // 如果在out_handle_cache_中找到了输入项对应的OutHandler,则将其添加到tuple_items中 + tuple_items->push_back(out_handle_cache_[item.get()]); + } else { // 否则,将一个空的OutHandler添加到tuple_items中 + tuple_items->emplace_back(OutHandler(nullptr, "", item)); + } + } + // 打印调试信息,并将转换得到的OutHandler列表存储到tuple_out_handle_cache_中 + MS_LOG(DEBUG) << "ConvertMakeTuple: " << node.get() << " " << tuple_items->size(); + tuple_out_handle_cache_[node.get()] = tuple_items; +} + +//该函数用于将TopK节点转换为对应的Operator对象,并处理其第二个输入的类型转换。 +void DfGraphConvertor::ConvertTopK(const CNodePtr node) { + MS_EXCEPTION_IF_NULL(node); // 判断节点是否为空 + MS_LOG(INFO) << "Convert TopK second input's type from int64 to int32."; // 打印日志信息,提示将TopK节点的第二个输入的类型从int64转换为int32 + auto value_ptr = node->input(2)->cast(); // 获取TopK节点的第二个输入(k值) + MS_EXCEPTION_IF_NULL(value_ptr); + std::ostringstream ss; // 为第二个输入节点生成一个唯一的标识符,并存储到op_draw_name_中,用于绘制计算图时标识该节点 + ss << "op" << value_ptr.get(); + op_draw_name_[value_ptr.get()] = ss.str(); + // 绘制计算图节点信息,并将其存储到compute_sout_中 + compute_sout_ << ss.str() << "[label= \"" << value_ptr->value()->ToString() << "\" shape=ellipse]" << endl; + // 获取第二个输入节点的值,并将其转换为int64类型 + auto input_value = value_ptr->value(); + auto int64_value = GetValue(input_value); + OpAdapterPtr adpt = FindAdapter(value_ptr, training_); // 查找第二个输入节点对应的OpAdapter + auto op = adpt->generate(value_ptr); // 使用OpAdapter的generate函数生成第二个输入节点对应的Operator对象 + (void)adpt->setAttr(op, "value", static_cast(int64_value)); // 将第二个输入节点的值转换为int32类型,并设置为Operator的属性 + op_cache_[value_ptr.get()] = op; // 将第二个输入节点对应的Operator对象存储到op_cache_中 +} + +//该函数用于将ValuePtr对象转换为std::vector类型的数据。 +std::vector DfGraphConvertor::CastToInt(const ValuePtr &value) { + if (value == nullptr) { // 判断ValuePtr是否为空,如果为空则打印警告信息并返回空的std::vector + MS_LOG(WARNING) << "Value ptr is nullptr."; + return {}; + } + std::vector cur_value = {}; + if (utils::isa(value)) { // 如果ValuePtr对象是ValueSequencePtr类型,表示它是一个值序列 + auto val_seq_ptr = value->cast(); + MS_EXCEPTION_IF_NULL(val_seq_ptr); + if (!val_seq_ptr->value().empty()) { + auto first_val = val_seq_ptr->value().front(); + MS_EXCEPTION_IF_NULL(first_val); + MS_EXCEPTION_IF_NULL(first_val->type()); + if (first_val->type()->number_type() == kNumberTypeInt64) { // 如果值序列中的元素类型是int64,直接将其转换为std::vector + cur_value = GetValue>(value); + } else { // 否则,将值序列中的元素转换为int类型,并转换为std::vector + auto origin_value = GetValue>(value); + (void)std::transform(origin_value.begin(), origin_value.end(), std::back_inserter(cur_value), + [](int index) { return static_cast(index); }); + } + } + } else { // 如果ValuePtr对象不是值序列,直接将其转换为std::vector + MS_EXCEPTION_IF_NULL(value->type()); + if (value->type()->number_type() == kNumberTypeInt64) { + cur_value.push_back(GetValue(value)); + } else { + cur_value.push_back(static_cast(GetValue(value))); + } + } + return cur_value; +} + +//该函数用于将Reshape节点转换为对应的Operator对象,并处理其第二个输入。 +void DfGraphConvertor::ConvertReshape(const CNodePtr node) { + // 打印日志信息,提示将Reshape节点的第二个输入转换为Op属性 + MS_LOG(INFO) << "Convert the second input of reshape to op attr."; + const auto kInputNum = 3; // 定义常量kInputNum,表示Reshape节点应该具有的输入数量 + if (node->size() < kInputNum) { // 判断Reshape节点的输入数量是否小于kInputNum,如果小于,则打印警告信息并返回 + MS_LOG(WARNING) << "Reshape must have two inputs."; + return; + } + OpAdapterPtr adpt = FindAdapter(node, training_); // 查找Reshape节点对应的OpAdapter + if (adpt == nullptr) { + return; + } + auto op = adpt->generate(node); // 使用OpAdapter的generate函数生成Reshape节点对应的Operator对象 + MS_EXCEPTION_IF_NULL(op); + // get shape form attr + // 获取Reshape节点的第一个输入(shape值)对应的ValueNodePtr + auto value_node = node->input(0)->cast(); + MS_EXCEPTION_IF_NULL(value_node); + MS_EXCEPTION_IF_NULL(value_node->value()); + auto primitive = value_node->value()->cast(); // 获取ValueNodePtr中的PrimitivePtr对象 + MS_EXCEPTION_IF_NULL(primitive); + auto value = primitive->GetAttr("shape"); // 获取PrimitivePtr对象中的shape属性的值 + std::vector list; + list = CastToInt(value); // 将shape属性的值转换为std::vector类型 + + (void)op->SetAttr("shape", list); // 将转换得到的shape属性值设置为Operator的属性 + op_cache_[node.get()] = op; // 将Reshape节点对应的Operator对象存储到op_cache_中,以Reshape节点的指针地址作为键,Operator对象作为值 +} + +//该函数用于将Conv2D节点转换为对应的Operator对象,并处理其padding属性。 +void DfGraphConvertor::ConvertConv2D(const CNodePtr node) { + MS_EXCEPTION_IF_NULL(node); // 判断输入的Conv2D节点是否为空,如果为空则返回 + OpAdapterPtr adpt = FindAdapter(node, training_); // 查找Conv2D节点对应的OpAdapter + if (adpt == nullptr) { + return; + } + auto op = adpt->generate(node); // 使用OpAdapter的generate函数生成Conv2D节点对应的Operator对象 + MS_EXCEPTION_IF_NULL(op); + auto value_node = node->input(0)->cast(); // 获取Conv2D节点的第一个输入对应的ValueNodePtr + MS_EXCEPTION_IF_NULL(value_node); + MS_EXCEPTION_IF_NULL(value_node->value()); + auto primitive = value_node->value()->cast(); // 获取ValueNodePtr中的PrimitivePtr对象 + MS_EXCEPTION_IF_NULL(primitive); + auto value = primitive->GetAttr("padding"); // 获取PrimitivePtr对象中的padding属性的值 + if (value != nullptr) { // 如果padding属性的值不为空,表示Conv2D节点有padding属性 + std::string pad_mode = GetValue(value); + (void)op->SetAttr("padding", pad_mode); // 将padding属性的值设置为Operator的属性 + } + op_cache_[node.get()] = op; // 将Conv2D节点对应的Operator对象存储到op_cache_中,以Conv2D节点的指针地址作为键,Operator对象作为值 +} + +//该函数用于追踪处理TupleGetItem节点,获取Item的输入,并返回该输入节点。 +AnfNodePtr DfGraphConvertor::TraceTupleGetItem(const CNodePtr &node, uint64_t *index) { + const int TUPLE_GET_ITEM_INDEX = 2; // 定义常量TUPLE_GET_ITEM_INDEX,表示TupleGetItem节点的索引位置 + if (node->inputs().size() < 3) { // "tuple_getitem" primitive must have 3 inputs + // // 判断"tuple_getitem" primitive的输入数量是否小于3,如果小于3,则抛出异常 + MS_LOG(EXCEPTION) << "length of inputs of TupleGetItem is less than 3"; + } + auto index_node = node->inputs()[TUPLE_GET_ITEM_INDEX]; // 获取TupleGetItem节点的第三个输入,即索引值 + if (!index_node->isa()) { // 判断索引值对应的节点是否为ValueNode,如果不是,则设置error_为INVALID_ARGUMENT,并抛出异常 + error_ = INVALID_ARGUMENT; + MS_LOG(EXCEPTION) << "can't convert get item with non-constant index"; + } + // 获取索引值的整数表示,并保存在index指针所指向的变量中 + *index = LongToUlong(GetValue(GetValueNode(index_node))); + return node->inputs()[1]; // 返回TupleGetItem节点的第二个输入,即获取Item的输入 +} + +//该函数用于追踪处理Depend节点,获取control依赖的输入,并返回该输入节点。 +AnfNodePtr DfGraphConvertor::TraceDepend(const CNodePtr &node) { + auto cnode = node->cast(); // 获取Depend节点的指针cnode + // 判断"Depend" primitive的输入数量是否小于3,如果小于3,则抛出异常 + if (cnode->inputs().size() < 3) { // "Depend" primitive have 3 inputs + MS_LOG(EXCEPTION) << "length of inputs of depend is less than 3"; + } + return cnode->inputs()[1]; // 返回Depend节点的第二个输入,即control依赖的输入 +} + +//该函数用于追踪处理MakeTuple节点,获取Tuple的第index个元素,并返回该输入节点。 +AnfNodePtr DfGraphConvertor::TraceMakeTuple(const CNodePtr &node, uint64_t index) { + if (index + 1 >= node->inputs().size()) { // 判断index + 1是否大于等于make_tuple节点的输入数量,如果是,则抛出异常 + MS_LOG(EXCEPTION) << "length of make_tuple is less than index: " << index; + } + return node->inputs()[index + 1]; // 返回make_tuple节点的第index + 1个输入节点,即获取Tuple的第index个元素 +} + +//该函数用于获取节点的处理器OutHandler,根据节点是否在Tuple内部进行不同的处理。 +OutHandler DfGraphConvertor::GetHandler(const AnfNodePtr &node, const std::stack &index_stack, + AnfNode *const draw_index) { + if (node == nullptr) { // 判断节点是否为nullptr,如果是,则输出错误日志并返回一个空的OutHandler + MS_LOG(ERROR) << "Get nullptr while trace real op"; + return OutHandler(nullptr, ""); + } + std::ostringstream ss; // 创建一个ostringstream对象,用于生成节点的字符串表示 + ss << "op" << node.get(); + if (index_stack.empty()) { // 判断index_stack是否为空 如果为空,则表示不在Tuple内部,直接生成OutHandler并返回 + op_draw_name_[draw_index] = ss.str(); // 将节点的字符串表示保存到op_draw_name_中 + return OutHandler(Convert(node), ""); // 调用Convert函数将节点转换为OperatorPtr,并生成OutHandler返回 + } else { + // 如果index_stack不为空,则表示在Tuple内部 + // 查找该节点的适配器OpAdapterPtr + OpAdapterPtr adpt = FindAdapter(node, training_); + if (adpt == nullptr) { // 如果适配器为空,则输出错误日志并返回一个空的OutHandler + MS_LOG(ERROR) << "Can not get node output as adpt is nullptr!"; + error_ = NOT_FOUND; + return OutHandler(nullptr, ""); + } + OperatorPtr op = Convert(node); // 调用Convert函数将节点转换为OperatorPtr + if (op == nullptr) { // 如果转换后的OperatorPtr为空,则输出错误日志并返回一个空的OutHandler + error_ = NOT_FOUND; + MS_LOG(ERROR) << "Can not convert node for trace real op"; + return OutHandler(nullptr, ""); + } + op_draw_name_[draw_index] = ss.str(); // 将节点的字符串表示保存到op_draw_name_中 + // 调用适配器的getOutput函数获取输出的处理器OutHandler,并返回 + return adpt->getOutput(Convert(node), static_cast(index_stack.top())); + } +} + +// get the real operator through maketuple tuple_getitem depend +//该函数用于追踪获取节点的真实操作节点,即去除所有TupleGetItem、MakeTuple和Depend节点,并返回其处理器OutHandler。 +OutHandler DfGraphConvertor::TraceRealOp(AnfNodePtr node) { + // 判断节点是否为TupleGetItem、MakeTuple或Depend节点 + bool flag = IsPrimitiveCNode(node, prim::kPrimTupleGetItem) || IsPrimitiveCNode(node, prim::kPrimMakeTuple) || + IsPrimitiveCNode(node, prim::kPrimDepend); + std::stack index_stack; // 创建一个栈index_stack,用于保存TupleGetItem节点的索引 + auto draw_index = node.get(); // 保存当前节点的指针地址,用于后续绘图 + while (flag) { // 循环追踪真实操作节点 + flag = false; + if (IsPrimitiveCNode(node, prim::kPrimTupleGetItem)) { + uint64_t index; + // 如果当前节点是TupleGetItem节点,调用TraceTupleGetItem函数获取其真实操作节点和索引 + node = TraceTupleGetItem(node->cast(), &index); + // 将索引压入index_stack中 + index_stack.push(index); + flag = true; + } else if (IsPrimitiveCNode(node, prim::kPrimMakeTuple)) { + if (index_stack.empty()) { + // 如果当前节点是MakeTuple节点且index_stack为空,表示存在错误,输出错误日志并返回一个空的OutHandler + MS_LOG(ERROR) << "TraceRealOp find a make_tuple node"; + return OutHandler(nullptr, ""); + } else { + // 如果当前节点是MakeTuple节点且index_stack不为空,调用TraceMakeTuple函数获取其真实操作节点并弹出索引 + node = TraceMakeTuple(node->cast(), index_stack.top()); + index_stack.pop(); + flag = true; + } + } else if (IsPrimitiveCNode(node, prim::kPrimDepend)) { + // 如果当前节点是Depend节点,调用TraceDepend函数获取其真实操作节点 + node = TraceDepend(node->cast()); + flag = true; + } + } + return GetHandler(node, index_stack, draw_index); // 调用GetHandler函数获取节点的处理器OutHandler并返回 +} + +//该函数用于将TupleGetItem节点转换为对应的算子处理器。 +void DfGraphConvertor::ConvertTupleGetItem(const CNodePtr node) { + auto handle = TraceRealOp(node); // 调用TraceRealOp函数获取TupleGetItem节点的真实操作节点处理器OutHandler + if (handle.op == nullptr) { // 如果真实操作节点处理器为空,输出错误日志并返回 + MS_LOG(ERROR) << "Failed to trace tuple get item"; + return; + } + out_handle_cache_[node.get()] = handle; // 将TupleGetItem节点和其真实操作节点处理器OutHandler添加到out_handle_cache_中缓存 +} + +// Get the real op for tuple_getitem through make tuple, or depend +//该函数用于处理TupleGetItem节点和Depend节点的情况,递归地获取这些节点的真实操作节点。 +AnfNodePtr DfGraphConvertor::GetRealOpNode(AnfNodePtr node) { + const int TUPLE_GET_ITEM_INDEX = 2; + if (IsPrimitiveCNode(node, prim::kPrimTupleGetItem)) { //// 如果当前节点是TupleGetItem节点 + auto node_inputs = node->cast()->inputs(); + if (node_inputs.size() != 3) { // "tuple_getitem" primitive must have 3 inputs + MS_LOG(ERROR) << "tuple get item node not correct!"; + error_ = FAILED; + return node; + } + MS_EXCEPTION_IF_NULL(node_inputs[TUPLE_GET_ITEM_INDEX]); + if (!node_inputs[TUPLE_GET_ITEM_INDEX]->isa()) { // 获取TupleGetItem节点的索引值 + error_ = INVALID_ARGUMENT; + MS_LOG(EXCEPTION) << "can't convert get item with non-constant index"; + } + auto value_ptr = GetValueNode(node_inputs[TUPLE_GET_ITEM_INDEX])->cast(); + if (value_ptr == nullptr) { + MS_LOG(ERROR) << "Can not convert get item as value is nullptr!"; + error_ = FAILED; + return node; + } + int64_t index = value_ptr->value(); + + // make_tuple apply inputs:make_tuple, [tuple_items,] + if (IsPrimitiveCNode(node_inputs[1], prim::kPrimMakeTuple)) { // 如果TupleGetItem节点的输入是MakeTuple节点 + auto tuple_inputs = node->cast()->inputs(); + if (tuple_inputs.size() < LongToSize(index + 1L)) { + MS_LOG(ERROR) << "make tuple input items node not correct! size:" << tuple_inputs.size() + << ", item index:" << index; + error_ = FAILED; + return node; + } + return GetRealOpNode(tuple_inputs[LongToSize(index + 1L)]); // 递归调用GetRealOpNode函数获取MakeTuple节点的真实操作节点 + } + return GetRealOpNode(node_inputs[1]); // 递归调用GetRealOpNode函数获取TupleGetItem节点的真实操作节点 + } + + // depend apply inputs: depend,output,depended_node + if (IsPrimitiveCNode(node, prim::kPrimDepend)) { // 如果当前节点是Depend节点 + auto depend_inputs = node->cast()->inputs(); + if (depend_inputs.size() != 3) { // "Depend" primitive have 3 inputs + MS_LOG(ERROR) << "depend input items not correct"; + error_ = FAILED; + return node; + } + return GetRealOpNode(depend_inputs[1]); // 递归调用GetRealOpNode函数获取Depend节点的真实操作节点 + } + return node; // 其他情况直接返回当前节点 +} + +// convert the anf node to corresponding operator list +/* +此函数的目的是将Depend节点以及MakeTuple节点中的子节点转换为操作符。 +这样,在构建计算图时可以将Depend节点转换为控制边,而MakeTuple节点的子节点可以在后续处理中被正确处理。 +*/ +std::vector DfGraphConvertor::ConvertDependNode(const AnfNodePtr node) { + if (IsPrimitiveCNode(node, prim::kPrimMakeTuple)) { //判断节点是否为MakeTuple节点 + std::vector op_lists; //如果是,则将其输入的各个元素节点逐个转换为操作符,并存储在op_lists中,然后返回op_lists + auto node_inputs = node->cast()->inputs(); + for (size_t index = 1; index < node_inputs.size(); index++) { + auto op = Convert(GetRealOpNode(node_inputs[index])); + if (op == nullptr) { + MS_LOG(ERROR) << "Convert real op node to operator failed"; + error_ = FAILED; + return std::vector({}); + } + op_lists.push_back(op); + } + return op_lists; + } + // 如果当前节点不是MakeTuple节点,则将其转换为操作符并返回 + auto op = Convert(GetRealOpNode(node)); + if (op == nullptr) { + MS_LOG(ERROR) << "Convert real op node to operator failed"; + error_ = FAILED; + return std::vector({}); + } + return std::vector({op}); +} + +/* +该函数检查给定的CNode(计算节点)的类型,并根据节点类型应用特定的操作。 +它返回一个布尔值,表示是否需要进一步处理给定的节点。 +*/ +bool DfGraphConvertor::CheckCNode(const std::string &name, const CNodePtr node) { + // ignore apply node of return + // 忽略特定的特殊节点,并返回false以跳过进一步处理。 + if (name == "" || name == prim::kPrimReturn->name() || name == prim::kPrimDepend->name() || + name == prim::kPrimSwitchLayer->name() || name == prim::kPrimPartial->name()) { + return false; + } + + // Convert TopK second input from int64 to int32. + // 将TopK节点的第二个输入从int64转换为int32。 + if (name == prim::kPrimTopK->name()) { + ConvertTopK(node); + return true; + } + + // Convert Reshape add const input to attr(shape) + // 转换Reshape节点,并将常量输入添加到属性(shape)中。 + if (name == prim::kPrimReshape->name()) { + ConvertReshape(node); + return true; + } + + // Add attr pad mode to Conv2D + // 为Conv2D、DepthwiseConv2dNative和Conv2DBackpropInputV2节点添加padding属性。 + if (name == prim::kPrimConv2D->name() || name == prim::kPrimDepthwiseConv2dNative->name() || + name == kNameConv2DBackpropInputV2) { + ConvertConv2D(node); + return true; + } + + // make_tuple is used for a dynamic_input, convert it to a vector of OutHandlers + // 处理用于动态输入的make_tuple节点,将其转换为OutHandler对象的向量。 + if (name == prim::kPrimMakeTuple->name()) { + ConvertMakeTuple(node); + return false; // 返回false以跳过make_tuple节点的进一步处理。 + } + + // As for nodes with multi outputs, convert tuple_getitem to OutHandle + // 处理具有多个输出的tuple_getitem节点,将其转换为OutHandler对象。 + if (name == prim::kPrimTupleGetItem->name()) { + ConvertTupleGetItem(node); + return false; // 返回false以跳过tuple_getitem节点的进一步处理。 + } + // 如果未满足上述特殊情况,则返回true,表示需要进一步处理该节点。 + return true; +} + +//该函数ConvertCNode用于将CNode(计算节点)转换为相应的运算符 +OperatorPtr DfGraphConvertor::ConvertCNode(const CNodePtr node) { + SaveParamFormat(node); // 调用SaveParamFormat函数保存节点的参数格式 + std::string name = GetCNodeTargetFuncName(node); //获取节点的类型名称 + if (!CheckCNode(name, node)) { //如果通过CheckCNode函数检查该节点,并根据节点的类型应用相应的操作 + return nullptr; //如果CheckCNode返回false,则表示该节点为特殊节点,不需要进一步处理,直接返回nullptr。 + } + + // get corresponding OpAdapter + // 获取相应的OpAdapter + OpAdapterPtr adpt = FindAdapter(node, training_); //通过调用FindAdapter函数获取适用于该节点的OpAdapter + if (adpt == nullptr) { //如果未找到适配器,则将error_设置为NOT_FOUND,并返回nullptr + error_ = NOT_FOUND; + return nullptr; + } + + // get operator + // 获取运算符 + OperatorPtr op = nullptr; + auto it_op = op_cache_.find(node.get()); + if (it_op != op_cache_.end()) { //如果已经存在则直接使用 + op = it_op->second; + } else { + op = adpt->generate(node); //否则通过适配器的generate函数创建运算符。 + } + + // set attribute for primitive + // 设置原语的属性 + (void)adpt->setAttr(op, node); //根据节点类型特殊处理,设置不同的属性。 + + // add into cache + // 将运算符添加到缓存中 + (void)op_cache_.emplace(node.get(), op); + + DrawCNode(node, adpt); // 绘制节点信息,用于可视化 + + return op_cache_[node.get()]; //函数返回节点对应的运算符 +} + +//该函数用于将ANF中的Parameter(参数节点)转换为DataFlow中的变量 +OperatorPtr DfGraphConvertor::ConvertParameter(const AnfNodePtr node) { + // convert Parameter in ANF to variable in DataFlow + // 将ANF中的Parameter转换为DataFlow中的变量 + auto adpt = FindAdapter(node, training_); //通过调用FindAdapter函数获取适用于该节点的adpt + if (adpt == nullptr) { //如果未找到适配器,则抛出异常。 + MS_LOG(EXCEPTION) << "Can not find adapter for Parameter"; + } + auto op = adpt->generate(node); //通过适配器的generate函数创建运算符,并将其添加到缓存中 + op_cache_[node.get()] = op; + + // build index for parameter using name + // 使用名称为参数构建索引 将参数节点添加到params_中 + std::string name = std::static_pointer_cast(node)->name(); + params_[name] = node; + std::ostringstream ss; + ss << "op" << node.get(); + op_draw_name_[node.get()] = ss.str(); //为该节点创建一个运算符的标识符,并将其保存在op_draw_name_中,用于可视化。 + compute_sout_ << ss.str() << "[shape=octagon, label=\"" << name << "\"]" << endl; + return op_cache_[node.get()]; //在可视化输出中绘制该节点,并返回节点对应的运算符 +} + +//该函数用于保存参数的格式信息 +void DfGraphConvertor::SaveParamFormat(const CNodePtr node) { + AnfNodePtr op = node->input(0); + if (IsValueNode(op)) { //检查节点的第一个输入是否是ValueNode类型 + auto prim = GetValueNode(op); //如果是则获取该ValueNode对应的Primitive类型,并遍历它的属性。 + for (auto attr : prim->attrs()) { + if (attr.first == "format") { //若其中有名为"format"的属性,则从属性值中获取格式信息 + std::string format; //格式信息可能是字符串类型或整数类型,函数会根据类型进行处理。 + if (attr.second->isa()) { + bool converted = CheckAndConvertUtils::ConvertAttrValueToString(prim->name(), "format", &attr.second); + if (converted) { + format = attr.second->ToString(); + } else { + CheckAndConvertUtils::GetFormatStringVal(prim, &format); + } + } else if (attr.second->isa()) { + format = attr.second->ToString(); + } + if (format != "NCDHW" && format != "NHWC") { //若格式信息是"NCDHW"或"NHWC",则继续遍历节点的其他输入 + break; + } + for (size_t i = 1; i < node->size(); i++) { + auto input = node->input(i); + if (input->isa()) { //若为Parameter类型的输入,则将其对应的格式保存在param_format_中。 + param_format_[input->DebugString()] = format; + MS_LOG(DEBUG) << "Save Param " << input->DebugString() << " format: " << format; //打印保存的参数格式信息,用于调试 + } + } + } + } + } +} + +//该函数用于将ValueNode转换成多个常量(Constant)节点。 +Status DfGraphConvertor::TryConvertValueNodeToMultiConst(const ValueNodePtr node) { + MS_EXCEPTION_IF_NULL(node); + ValuePtr value = node->value(); + MS_EXCEPTION_IF_NULL(value); + if (!value->isa() && !value->isa()) { + return FAILED; + } + // 检查值是否为 ValueList 或 ValueTuple 类型,如果不是,则返回 FAILED + auto vec = value->isa() ? value->cast()->value() : value->cast()->value(); + if (vec.empty()) { + return FAILED; + } + //获取ValueList或ValueTuple中的元素,并遍历这些元素。如果其中有任何一个元素不是MeTensor类型,函数会返回FAILED。 + std::shared_ptr> tuple_items = std::make_shared>(); + for (size_t i = 0; i < vec.size(); i++) { + MS_EXCEPTION_IF_NULL(vec[i]); + if (vec[i]->isa()) { + // 将 MeTensor 转换成 GeTensor + GeTensorPtr ge_tensor = transform::TransformUtil::ConvertTensor(vec[i]->cast(), kOpFormat_NCHW); + auto const_op = std::make_shared(node->fullname_with_scope() + "/const/inputs/" + std::to_string(i)); + (void)const_op->set_attr_value(*ge_tensor); + (void)const_op->update_output_desc_y(ge_tensor->GetTensorDesc()); + (void)tuple_items->emplace_back(OutHandler(const_op, "")); + } else { // 如果列表或元组中的任何一个元素不是 MeTensor 类型,则返回 FAILED + return FAILED; + } + } + if (tuple_items->empty()) { // 如果转换后的列表或元组为空,则返回 FAILED + return FAILED; + } + + tuple_out_handle_cache_[node.get()] = tuple_items; // 将转换后的列表或元组保存为 OutHandler 的向量 + return SUCCESS; +} + +//该函数用于将ValueNode转换成常量(Constant)操作符。 +OperatorPtr DfGraphConvertor::ConvertValueNode(const ValueNodePtr node) { + // convert valuenode in ANF to Const in DataFlow + // find paramerte referenced by SymbolicKeyInstance of valuenode + // 将 ANF 中的 ValueNode 转换成 DataFlow 中的 Const + // 设置绘制图形所需的信息 + std::ostringstream ss; + ss << "op" << node.get(); + op_draw_name_[node.get()] = ss.str(); + compute_sout_ << ss.str() << "[label= \"" << node->value()->ToString() << "\" shape=ellipse]" << endl; + // 尝试将 ValueNode 转换成多个常量(Constant)节点 + if (TryConvertValueNodeToMultiConst(node) == SUCCESS) { + MS_LOG(INFO) << "Convert value node to multi Constant OP success"; + return nullptr; + } + // 获取对应的 OpAdapter + OpAdapterPtr adpt = FindAdapter(node, training_); + if (adpt == nullptr) { + error_ = NOT_FOUND; + return nullptr; + } + // 生成对应的操作符 Operator + auto op = adpt->generate(node); + // set const's attrs + // 设置常量的属性值 + if (adpt->setAttr(op, "value", node->value()) != 0) { + MS_LOG(WARNING) << "set attr value for const failed"; + } + // 将操作符转换为 Constant 类型 + auto const_op = std::static_pointer_cast(op); + if (const_op == nullptr) { + MS_LOG(ERROR) << "Get Constant operator failed"; + return nullptr; + } + // 更新输出描述 + auto ge_tensor = const_op->get_attr_value(); + auto ge_desc = ge_tensor.GetTensorDesc(); + (void)const_op->update_output_desc_y(ge_desc); + // 将操作符保存在 op_cache_ 中,并返回 + op_cache_[node.get()] = op; + return op_cache_[node.get()]; +} + +//该函数用于绘制CNode节点的图形表示。 +void DfGraphConvertor::DrawCNode(const CNodePtr node, const OpAdapterPtr adpt) { + // 绘制 apply node,即 CNode 节点的图形表示 + if (adpt == nullptr || node == nullptr) { + MS_LOG(ERROR) << "Failed to draw apply node as adpt or node is nullptr!"; + return; + } + std::ostringstream ss; + ss << "op" << node.get(); + op_draw_name_[node.get()] = ss.str(); + // 绘制节点的表格表示 + compute_sout_ << ss.str() << "[label=<"; + compute_sout_ << "" << endl; + // 绘制输入端口的标签 + auto input_map = adpt->getInputMap(); + auto dyn_input_map = adpt->getDynInputMap(); + if (input_map.size() + dyn_input_map.size() > 0) { + compute_sout_ << ""; + for (auto &it : input_map) { + compute_sout_ << ""; + } + for (auto &it : dyn_input_map) { + compute_sout_ << ""; + } + compute_sout_ << "" << endl; + } + // 绘制节点的功能名称和内容 + compute_sout_ << "" << endl; + + // print attrs' values + // 绘制节点的属性值 + auto atts = adpt->GetAttrsFromDrawGraph(); + for (auto &it : atts) { + compute_sout_ << ""; + } + // 清空属性的向量,为下一次绘制做准备 + adpt->clearAttrVect(); + + compute_sout_ << "
" << it.second.name << "" << it.second.name << "
\"" << node->ToString() + << ":" << GetCNodeTargetFuncName(node) << "\"
\"" << it + << "\"
> shape=plaintext]" << endl; +} + +//该函数用于注册运算符适配器 +void DfGraphConvertor::RegisterAdapter(const std::string &name, OpAdapterPtr adpt) { + // 注册运算符适配器,将适配器添加到OpAdapterMap中 + // 使用OpAdapterDesc类对适配器进行包装 + OpAdapterMap::get()[name] = std::make_shared(adpt); +} + +//该函数用于注册运算符适配器,同时支持传入训练适配器和推理适配器 +void DfGraphConvertor::RegisterAdapter(const std::string &name, OpAdapterPtr train_adpt, OpAdapterPtr infer_adpt) { + // 注册运算符适配器,将训练适配器和推理适配器添加到OpAdapterMap中 + // 使用OpAdapterDesc类对训练适配器和推理适配器进行包装 + OpAdapterMap::get()[name] = std::make_shared(train_adpt, infer_adpt); +} +} // namespace transform +} // namespace mindspore diff --git a/mindspore/ccsrc/transform-update/ctc_ops_declare.cc b/mindspore/ccsrc/transform-update/ctc_ops_declare.cc new file mode 100644 index 00000000000..0ff3d3c3a21 --- /dev/null +++ b/mindspore/ccsrc/transform-update/ctc_ops_declare.cc @@ -0,0 +1,48 @@ +/** + * Copyright 2019 Huawei Technologies Co., Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "transform/graph_ir/op_declare/ctc_ops_declare.h" + +namespace mindspore::transform { +// CTCLoss +INPUT_MAP(CTCLoss) = {{1, INPUT_DESC(inputs)}, + {2, INPUT_DESC(labels_indices)}, + {3, INPUT_DESC(labels_values)}, + {4, INPUT_DESC(sequence_length)}}; +//杈撳叆鏄犲皠锛宨nputs绱㈠紩涓1锛宭abels_indices绱㈠紩涓2锛宭abels_values绱㈠紩涓3锛宻equence_length绱㈠紩涓4 +ATTR_MAP(CTCLoss) = { + {"preprocess_collapse_repeated", ATTR_DESC(preprocess_collapse_repeated, AnyTraits())}, + {"ctc_merge_repeated", ATTR_DESC(ctc_merge_repeated, AnyTraits())}, + {"ignore_longer_outputs_than_inputs", ATTR_DESC(ignore_longer_outputs_than_inputs, AnyTraits())}}; +//灞炴ф槧灏勶紝灞炴reprocess_collapse_repeated绫诲瀷涓篵ool锛屽睘鎬tc_merge_repeated绫诲瀷涓篵ool锛屽睘鎬gnore_longer_outputs_than_inputs绫诲瀷涓篵ool +OUTPUT_MAP(CTCLoss) = {{0, OUTPUT_DESC(loss)}, {1, OUTPUT_DESC(gradient)}}; +//杈撳嚭鏄犲皠锛宭oss绱㈠紩涓0锛実radient绱㈠紩涓1 +REG_ADPT_DESC(CTCLoss, kNameCTCLoss, ADPT_DESC(CTCLoss)) +//娉ㄥ唽CTCLoss鎿嶄綔鐨勯傞厤鍣ㄦ弿杩癒NameCTCLoss + +// CTCGreedyDecoder +INPUT_MAP(CTCGreedyDecoder) = {{1, INPUT_DESC(inputs)}, {2, INPUT_DESC(sequence_length)}}; +//杈撳叆鏄犲皠锛宨nputs绱㈠紩涓1锛宻equence_length绱㈠紩涓2 +ATTR_MAP(CTCGreedyDecoder) = {{"merge_repeated", ATTR_DESC(merge_repeated, AnyTraits())}}; +//灞炴ф槧灏勶紝灞炴erge_repeated绫诲瀷涓篵ool +OUTPUT_MAP(CTCGreedyDecoder) = {{0, OUTPUT_DESC(decoded_indices)}, + {1, OUTPUT_DESC(decoded_values)}, + {2, OUTPUT_DESC(decoded_shape)}, + {3, OUTPUT_DESC(log_probability)}}; +//杈撳嚭鏄犲皠锛宒ecoded_indices绱㈠紩涓0锛宒ecoded_values绱㈠紩涓1锛宒ecoded_shape绱㈠紩涓2锛宭og_probability绱㈠紩涓1 +REG_ADPT_DESC(CTCGreedyDecoder, kNameCTCGreedyDecoder, ADPT_DESC(CTCGreedyDecoder)) +//娉ㄥ唽CTCGreedyDecoder鎿嶄綔鐨勯傞厤鍣ㄦ弿杩癒NameCTCGreedyDecoder +} // namespace mindspore::transform diff --git a/mindspore/ccsrc/transform-update/data_flow_ops_declare.cc b/mindspore/ccsrc/transform-update/data_flow_ops_declare.cc new file mode 100644 index 00000000000..e8ccf5c9c61 --- /dev/null +++ b/mindspore/ccsrc/transform-update/data_flow_ops_declare.cc @@ -0,0 +1,53 @@ +/** + * Copyright 2022 Huawei Technologies Co., Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "transform/graph_ir/op_declare/data_flow_ops_declare.h" +#include +#include + +namespace mindspore::transform { +INPUT_MAP(TensorArray) = {{1, INPUT_DESC(size)}}; +//杈撳叆鏄犲皠锛宻ize绱㈠紩涓1 +ATTR_MAP(TensorArray) = {{"dtype", ATTR_DESC(dtype, AnyTraits())}, + {"element_shape", ATTR_DESC(element_shape, AnyTraits>())}, + {"dynamic_size", ATTR_DESC(dynamic_size, AnyTraits())}, + {"clear_after_read", ATTR_DESC(clear_after_read, AnyTraits())}, + {"identical_element_shapes", ATTR_DESC(identical_element_shapes, AnyTraits())}, + {"tensor_array_name", ATTR_DESC(tensor_array_name, AnyTraits())}}; +//灞炴ф槧灏勶紝灞炴type绫诲瀷涓篏EType锛屽睘鎬lement_shape绫诲瀷涓篿nt64_t锛屽睘鎬ynamic_size绫诲瀷涓篵ool锛屽睘鎬dentical_element_shapes绫诲瀷涓篵ool +//灞炴lear_after_read绫诲瀷涓篵ool锛屽睘鎬ensor_array_name绫诲瀷涓篵ool +OUTPUT_MAP(TensorArray) = {{0, OUTPUT_DESC(handle)}, {1, OUTPUT_DESC(flow)}}; +//杈撳嚭鏄犲皠锛宧andle绱㈠紩涓0锛宖low绱㈠紩涓1 +REG_ADPT_DESC(TensorArray, kNameTensorArray, ADPT_DESC(TensorArray)) +//娉ㄥ唽TensorArray,鎿嶄綔鐨勯傞厤鍣ㄦ弿杩癒NameTensorArray, + +INPUT_MAP(TensorArrayWrite) = { + {1, INPUT_DESC(handle)}, {2, INPUT_DESC(index)}, {3, INPUT_DESC(value)}, {4, INPUT_DESC(flow_in)}}; +//杈撳叆鏄犲皠锛宧andle绱㈠紩涓1锛宨ndex绱㈠紩涓2锛寁alue绱㈠紩涓3锛宖low_in绱㈠紩涓4 +ATTR_MAP(TensorArrayWrite) = EMPTY_ATTR_MAP; +OUTPUT_MAP(TensorArrayWrite) = {{0, OUTPUT_DESC(flow_out)}}; +REG_ADPT_DESC(TensorArrayWrite, kNameTensorArrayWrite, ADPT_DESC(TensorArrayWrite)) + +INPUT_MAP(TensorArrayGather) = {{1, INPUT_DESC(handle)}, {2, INPUT_DESC(indices)}, {3, INPUT_DESC(flow_in)}}; +//杈撳叆鏄犲皠锛宧andle绱㈠紩涓1锛宨ndices绱㈠紩涓2锛宖low_in绱㈠紩涓3 +ATTR_MAP(TensorArrayGather) = {{"dtype", ATTR_DESC(dtype, AnyTraits())}, + {"element_shape", ATTR_DESC(element_shape, AnyTraits>())}}; +//灞炴ф槧灏勶紝灞炴type绫诲瀷涓篏EType锛屽睘鎬lement_shape绫诲瀷涓篿nt64_t +OUTPUT_MAP(TensorArrayGather) = {{0, OUTPUT_DESC(value)}}; +//杈撳嚭鏄犲皠锛寁alue绱㈠紩涓0 +REG_ADPT_DESC(TensorArrayGather, kNameTensorArrayGather, ADPT_DESC(TensorArrayGather)) +//娉ㄥ唽TensorArrayGather鎿嶄綔鐨勯傞厤鍣ㄦ弿杩癒NameTensorArrayGather +} // namespace mindspore::transform diff --git a/mindspore/ccsrc/transform-update/datasets.py b/mindspore/ccsrc/transform-update/datasets.py new file mode 100644 index 00000000000..20649425c33 --- /dev/null +++ b/mindspore/ccsrc/transform-update/datasets.py @@ -0,0 +1,4058 @@ +# Copyright 2022 Huawei Technologies Co., Ltd +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ============================================================================== +""" +1. This file is an abstraction of the dataset loading class. It contains +some basic dataset operations(skip, filter, map, batch, ...). +2. Specific dataset loading classes can be found in datasets_vision.py, datasets_text.py, +datasets_audio.py, datasets_standard_format.py and dataets_user_defined.py files. + datasets_vision.py: contains vision dataset loading classes. + datasets_text.py: contains text dataset loading classes. + datasets_audio.py: contains audio dataset loading classes. + datasets_standard_format.py: contains standard format loading classes which + any other kinds of datasets can be converted to. + dataets_user_defined.py: contains basic classes that help users to define + flexible ways to load dataset. +1.璇ユ枃浠舵槸鏁版嵁闆嗗姞杞界被鐨勬娊璞°傚畠鍖呭惈涓浜涘熀鏈殑鏁版嵁闆嗘搷浣滐紙璺宠繃銆佺瓫閫夈佹槧灏勩佹壒澶勭悊鈥︼級銆 +2.鍏蜂綋鐨勬暟鎹泦鍔犺浇绫诲彲浠ュ湪datasets_锛宒atasets_audio.py銆乨atasets_standard_format.py鍜宒ataets_user_defined.py鏂囦欢銆 +datasetsvision.py锛氬寘鍚瑙夋暟鎹泦鍔犺浇绫汇 +datasets_text.py锛氬寘鍚枃鏈暟鎹泦鍔犺浇绫汇 +datasets_audio.py锛氬寘鍚煶棰戞暟鎹泦鍔犺浇绫汇 +datasets_standard_format.py锛氬寘鍚爣鍑嗘牸寮忓姞杞界被鍙互灏嗕换浣曞叾浠栫被鍨嬬殑鏁版嵁闆嗚浆鎹负銆 +dataets_user_defined.py锛氬寘鍚府鍔╃敤鎴峰畾涔夌殑鍩烘湰绫诲姞杞芥暟鎹泦鐨勭伒娲绘柟寮忋 +""" +import atexit +import glob +import json +import os +import signal +import stat + +import gc +import time +import uuid +import multiprocessing +from enum import Enum +from importlib import import_module +import sys +import threading + +import copy +import weakref +import platform +import psutil +import numpy as np + +import mindspore._c_dataengine as cde +from mindspore._c_expression import typing + +from mindspore import log as logger +from mindspore.parallel._ps_context import _is_role_pserver, _is_role_sched, _get_ps_context, _enable_distributed_mindrt +from mindspore.dataset.engine.offload import GetOffloadModel + +import mindspore.dataset.transforms.c_transforms as c_transforms +import mindspore.dataset.transforms.py_transforms as py_transforms +import mindspore.dataset.transforms as transforms +from mindspore.dataset.text.utils import SentencePieceModel, DE_C_INTER_SENTENCEPIECE_MODE +from mindspore.parallel._utils import _get_device_num + +from . import samplers +from .iterators import DictIterator, TupleIterator, DummyIterator, check_iterator_cleanup, _set_iterator_cleanup, \ + ITERATORS_LIST, _unset_iterator_cleanup +from .queue import _SharedQueue, _Queue +from .validators import check_batch, check_shuffle, check_map, check_filter, check_repeat, check_skip, check_zip, \ + check_rename, check_device_send, check_take, check_output_shape, check_project, \ + check_sync_wait, check_zip_dataset, check_add_column, check_concat, check_split, check_bucket_batch_by_length, \ + check_save, check_tuple_iterator, check_dict_iterator, check_schema, check_to_device_send, deprecated +from ..core.config import get_callback_timeout, _init_device_info, get_enable_shared_mem, get_num_parallel_workers, \ + get_enable_watchdog +from ..core.datatypes import mstype_to_detype +from ..core.validator_helpers import replace_none +from ..core.py_util_helpers import ExceptionHandler +from ..transforms.py_transforms_util import FuncWrapper, Implementation +from ..vision.transforms import ToNumpy + +try: + context = import_module("mindspore.context") +except ModuleNotFoundError: + context = None + +if platform.system().lower() == "darwin" and multiprocessing.get_start_method() != "fork": + multiprocessing.set_start_method("fork", True) + +OffloadToManualOffloadMode = { + None: cde.ManualOffloadMode.UNSPECIFIED, + False: cde.ManualOffloadMode.DISABLED, + True: cde.ManualOffloadMode.ENABLED +} + +_train_dataset = None + + +def _set_training_dataset(dataset): + """ + Set the dataset to be used when training recovery has occurred. + + Args: + dataset: the training dataset or iterator + """ + global _train_dataset + _train_dataset = dataset + + +def _get_training_dataset(): + """ + Get the dataset to be used when training recovery has occurred. + + Returns: + training dataset/iterator + """ + return _train_dataset + + +def _reset_training_dataset(step): + """ + Reset the training dataset to the given step number. + + Args: + step (int): Global step number. + """ + dataset = _get_training_dataset() + if dataset is not None: + dataset._reset(step) # pylint: disable=W0212 + else: + raise RuntimeError("Training dataset is not set.") + + +class Shuffle(str, Enum): + """Specify the shuffle mode. + + - Shuffle.GLOBAL: Shuffle both the files and samples. + - Shuffle.FILES: Shuffle files only. + - Shuffle.INFILE: Shuffle data within each file. + """ + GLOBAL: str = "global" + FILES: str = "files" + INFILE: str = "infile" + + +ShuffleToShuffleMode = {Shuffle.FILES: cde.ShuffleMode.FILES, + Shuffle.GLOBAL: cde.ShuffleMode.GLOBAL, + Shuffle.INFILE: cde.ShuffleMode.INFILE} + +# 瀹氫箟涓涓柟娉曪紝灏嗘暟鎹泦鐨勯殢鏈烘墦涔卞弬鏁拌浆鎹负瀵瑰簲鐨 ShuffleMode 鏋氫妇鍊 +def shuffle_to_shuffle_mode(shuffle): + """ + Shuffle Enum to Shuffle Mode + 鏃犲簭鎺掑垪鏋氫妇鍒版棤搴忔帓鍒楁ā寮 + Args: + shuffle (Shuffle): shuffle flag to shuffle mode in C layer + + Returns: + ShuffleMode, shuffle mode + """ + + # 榛樿鐨 shuffle_mode 涓哄叏灞闅忔満鎵撲贡 + shuffle_mode = cde.ShuffleMode.GLOBAL # Global shuffle + + # 妫鏌ヨ緭鍏ョ殑 shuffle 鍙傛暟鏄惁涓 Shuffle 绫诲瀷鎴 None + if not isinstance(shuffle, Shuffle): + # 濡傛灉 shuffle 涓 None 鎴 True锛岃缃 shuffle_mode 涓哄叏灞闅忔満鎵撲贡 + if shuffle is None or shuffle: + shuffle_mode = cde.ShuffleMode.GLOBAL # Global shuffle + else: + # 濡傛灉 shuffle 涓 False锛岃缃 shuffle_mode 涓轰笉杩涜闅忔満鎵撲贡 + shuffle_mode = cde.ShuffleMode.FALSE # No shuffle + else: + # 濡傛灉 shuffle 鍙傛暟涓 Shuffle 鏋氫妇鍊硷紝鍒欏皢鍏惰浆鎹负瀵瑰簲鐨 ShuffleMode 鏋氫妇鍊 + shuffle_mode = ShuffleToShuffleMode[shuffle] + + # 杩斿洖瀵瑰簲鐨 shuffle_mode 鏋氫妇鍊硷紝琛ㄧず闅忔満鎵撲贡妯″紡 + return shuffle_mode + + +def shuffle_to_bool(shuffle): + """ + Shuffle Enum to bool + + Args: + shuffle (Shuffle): shuffle flag to bool + + Returns: + bool, True / False + """ + if shuffle is not None and not isinstance(shuffle, (bool, Shuffle)): + raise TypeError("shuffle must be of boolean or enum of 'Shuffle' values like 'Shuffle.GLOBAL' or " + "'Shuffle.FILES' or 'Shuffle.INFILE'.") + + shuffle_bool = True + if not isinstance(shuffle, Shuffle): + if shuffle is None: + shuffle_bool = None + elif shuffle: + shuffle_bool = True + else: + shuffle_bool = False + else: + shuffle_bool = True + return shuffle_bool + + +@check_zip + # 瀹氫箟涓涓柟娉曠敤浜庡皢澶氫釜鏁版嵁闆嗚繘琛 zip 鎿嶄綔锛屽皢瀹冧滑鍚堝苟涓轰竴涓 ZipDataset 瀵硅薄 +def zip(datasets): + """ + Zip the datasets in the input tuple of datasets. + 鍘嬬缉鏁版嵁闆嗙殑杈撳叆鍏冪粍涓殑鏁版嵁闆嗐 + Args: + datasets (tuple[Dataset]): A tuple of datasets to be zipped together. + The number of datasets must be more than 1. + + Returns: + Dataset, dataset zipped. + + Raises: + ValueError: If the number of datasets is 1. + TypeError: If datasets is not a tuple. + + Examples: + >>> # Create a dataset which is the combination of dataset_1 and dataset_2 + >>> dataset = ds.zip((dataset_1, dataset_2)) + """ + + # 妫鏌ヨ緭鍏ョ殑鏁版嵁闆嗗垪琛ㄩ暱搴︽槸鍚﹀皬浜庣瓑浜1锛屽鏋滄槸鍒欐姏鍑哄奸敊璇 + if len(datasets) <= 1: + raise ValueError( + "Can't zip empty or just one dataset!") + + # 閬嶅巻鏁版嵁闆嗗垪琛紝妫鏌ユ瘡涓厓绱犳槸鍚︽槸 Dataset 瀵硅薄锛屽鏋滀笉鏄垯鎶涘嚭绫诲瀷閿欒 + for dataset in datasets: + if not isinstance(dataset, Dataset): + raise TypeError("Invalid dataset, expected Dataset object, but got %s!" % type(dataset)) + + # 杩斿洖涓涓 ZipDataset 瀵硅薄锛屽叾涓寘鍚簡鍚堝苟鐨勬暟鎹泦 + return ZipDataset(datasets) + + +# 瀹氫箟涓涓柟娉曠敤浜庤幏鍙栬繍绠楃鐨勫鐞嗕俊鎭 +def _get_operator_process(): + """ + Inner implemented method, mainly for passing sub-process id in C layer + 鍐呴儴瀹炵幇鐨勬柟娉曪紝涓昏鐢ㄤ簬鍦–灞備紶閫掑瓙杩涚▼id + Returns: + dict, mapping dict of operator id and corresponding process id. + """ + + # 鑾峰彇鍏ㄥ眬鍙橀噺 _OP_PROCESS 涓殑杩愮畻绗﹀鐞嗕俊鎭 + global _OP_PROCESS + process_info = _OP_PROCESS + + # 鍒濆鍖栦竴涓┖鐨勮繍绠楃澶勭悊淇℃伅瀛楀吀 + op_process = dict() + + # 鑾峰彇 _OP_PROCESS 瀛楀吀涓殑鎵鏈夐敭 + keys = process_info.keys() + + # 鍒濆鍖栦竴涓爣蹇楀彉閲忥紝琛ㄧず鏄惁鎴愬姛鑾峰彇浜嗘墍鏈夎繍绠楃鐨勫鐞嗕俊鎭 + fetched_all = True + + # 閬嶅巻鎵鏈夐敭锛屽皾璇曡幏鍙栨瘡涓繍绠楃鐨勫鐞嗕俊鎭 + for key in keys: + try: + # 浠 _OP_PROCESS 涓幏鍙栨瘡涓繍绠楃鐨勫鐞嗕俊鎭紝骞跺瓨鍌ㄥ湪 op_process 瀛楀吀涓 + op_process[key] = list(process_info[key][1]) + + # 妫鏌ユ槸鍚︽垚鍔熻幏鍙栦簡鎵鏈夎繍绠楃鐨勫鐞嗕俊鎭 + item_full = (len(process_info[key][1]) == process_info[key][0]) + + except KeyError as err: + # 濡傛灉鍙戠敓 KeyError 寮傚父锛屽皢鍏舵姏鍑 + raise err + + # 鏇存柊鏍囧織鍙橀噺锛岃〃绀烘槸鍚︽垚鍔熻幏鍙栦簡鎵鏈夎繍绠楃鐨勫鐞嗕俊鎭 + fetched_all = fetched_all and item_full + + # 杩斿洖鑾峰彇鍒扮殑杩愮畻绗﹀鐞嗕俊鎭瓧鍏稿拰鏄惁鎴愬姛鑾峰彇浜嗘墍鏈夎繍绠楃鐨勬爣蹇楀彉閲 + return op_process, fetched_all + + +# 瀹氫箟涓涓柟娉曠敤浜庤缃暟鎹泦鏂囦欢鐨勬潈闄 +def _set_dataset_permissions(file_name, num_files): + """ + set saved dataset files' permissions to 600 + the rule of dataset filenames should be the same as those in C++. + 灏嗕繚瀛樼殑鏁版嵁闆嗘枃浠剁殑鏉冮檺璁剧疆涓600 + 鏁版嵁闆嗘枃浠跺悕鐨勮鍒欏簲璇ヤ笌C++涓殑瑙勫垯鐩稿悓銆 + """ + + + # 璁$畻鏂囦欢鍚嶄腑鏁板瓧鐨勪綅鏁帮紝浠ヤ究瀵规枃浠跺悕杩涜鏍煎紡鍖 + num_digits = len(str(num_files - 1)) + + # 濡傛灉鍙湁涓涓枃浠讹紝灏嗘枃浠跺悕娣诲姞鍒拌矾寰勫垪琛ㄤ腑 + if num_files == 1: + paths = [file_name] + else: + # 鍚﹀垯锛屾牴鎹枃浠舵暟閲忕敓鎴愪竴绯诲垪鏂囦欢璺緞 + # 鏍煎紡鍖栨枃浠跺悕锛屼緥濡傦細file_name0001锛宖ile_name0002锛... + paths = ["{}{}".format(file_name, str(x).rjust(num_digits, '0')) for x in range(num_files)] + + # 閬嶅巻鏂囦欢璺緞鍒楄〃 + for item in paths: + # 濡傛灉鏂囦欢瀛樺湪锛岃缃枃浠剁殑鐢ㄦ埛璇诲彇鍜屽啓鍏ユ潈闄 + if os.path.exists(item): + os.chmod(item, stat.S_IRUSR | stat.S_IWUSR) + # 鎷兼帴绱㈠紩鏂囦欢鐨勮矾寰 + index_file = item + ".db" + # 濡傛灉绱㈠紩鏂囦欢瀛樺湪锛屼篃璁剧疆鍏剁敤鎴疯鍙栧拰鍐欏叆鏉冮檺 + if os.path.exists(index_file): + os.chmod(index_file, stat.S_IRUSR | stat.S_IWUSR) + + + +class Dataset: + """ + Abstract class to represent a dataset in DataEngine's data pipeline. + 鎶借薄绫绘潵琛ㄧずDataEngine鐨勬暟鎹閬撲腑鐨勬暟鎹泦銆 + This class is the base class of SourceDataset and Dataset, and represents + a node in the data flow graph. + Dataset + ----------------------------------------------------------- + | | | | + VisionBaseDataset TextBaseDataset AudioBaseDataset | + - - - | + | | | | + ---------------------------------------- | + UnionBaseDataset | + | + SourceDataset + - + | + MappableDataset + + DatasetOperator: MapDataset(UnionBaseDataset) + BatchDataset(UnionBaseDataset) + BucketBatchByLengthDataset(UnionBaseDataset) + ShuffleDataset(UnionBaseDataset) + FilterDataset(UnionBaseDataset) + RepeatDataset(UnionBaseDataset) + SkipDataset(UnionBaseDataset) + TakeDataset(UnionBaseDataset) + ZipDataset(UnionBaseDataset) + ConcatDataset(UnionBaseDataset) + RenameDataset(UnionBaseDataset) + ProjectDataset(UnionBaseDataset) + SyncWaitDataset(UnionBaseDataset) + + Impl Dataset - vision: ImageFolderDataset(MappableDataset, VisionBaseDataset) + USPSDataset(SourceDataset, VisionBaseDataset) + Impl Dataset - text: TextFileDataset(SourceDataset, TextBaseDataset) + YahooAnswersDataset(SourceDataset, TextBaseDataset) + Impl Dataset - audio: LJSpeechDataset(MappableDataset, AudioBaseDataset) + TedliumDataset(MappableDataset, AudioBaseDataset) + Impl Dataset - standard: MindDataset(MappableDataset, UnionBaseDataset) + TFRecordDataset(SourceDataset, UnionBaseDataset) + Impl Dataset - user defined: GeneratorDataset(MappableDataset, UnionBaseDataset) + NumpySlicesDataset(GeneratorDataset) + + Args: + num_parallel_workers (int, optional): Number of workers to process the dataset in parallel + (default=None). + """ + + def __init__(self, children=None, num_parallel_workers=None, cache=None): + # Note: children and parent are internal variables, not recommended for external using. + self.children = replace_none(children, []) + if isinstance(self.children, tuple): + self.children = list(self.children) + if not isinstance(self.children, list): + self.children = [self.children] + + self.parent = [] + for child in self.children: + child.parent.append(weakref.ref(self)) + self.num_parallel_workers = num_parallel_workers + self.cache = cache + + self._device_iter = 0 + self._input_indexs = () + self.saved_output_types = None + self.saved_output_shapes = None + self.estimated_output_shapes = None + self.runtime_context = None + self.dynamic_setting = [False, None] + self.saved_min_shapes = None + self.saved_max_shapes = None + self._col_names = None + self.dataset_size = None + self._batch_size = None + self._num_classes = None + self._repeat_count = None + self._class_indexing = None + self._sync = False + + @staticmethod + def _get_operator_id(dataset): + """ + Internal method to iterate the tree and obtain op_id of each operator. + + Returns: + Dataset, the root dataset of the tree. + """ + op_name = dict() + generator_process = dict() + op_name[str(dataset)] = 0 + op_id = 1 + + def process_name(datasets, operator_id): + if not datasets: + return 0 + temp = [] + for item in datasets: + for d in item.children: + temp.append(d) + op_name[str(d)] = operator_id + + from mindspore.dataset.engine.datasets_user_defined import GeneratorDataset + if isinstance(d, GeneratorDataset) and d.sample_fn and d.sample_fn.pids: + generator_process[operator_id] = [d.num_parallel_workers, set(d.sample_fn.pids)] + + operator_id = operator_id + 1 + return process_name(temp, operator_id) + + process_name([dataset], op_id) + if generator_process: + global _OP_PROCESS + _OP_PROCESS.update(generator_process) + return op_name + + def close_pool(self): + """ + Close multiprocessing pool in dataset. If you are familiar with multiprocessing library, you can regard this + as a destructor for a processingPool object. + + Note: + This interface will be deleted or invisible in the future. Please don't use it. + When you find that there are residual processes that do not exit correctly, you can use `kill -9 PROCESS_ID` + to end it, or through www.gitee.com/mindspore/mindspore send us an issue. + """ + logger.warning("This interface will be deleted or invisible in the future. Please don't use it.") + + def create_ir_tree(self): + """ + Internal method to build an IR tree. + 鏋勫缓IR鏍戠殑鍐呴儴鏂规硶銆 + Returns: + DatasetNode, the root node of the IR tree. + Dataset, the root dataset of the IR tree. + """ +# 杩欎釜鏂规硶浼氭墽琛屼互涓嬫搷浣滐細 +# 1. 淇濆瓨褰撳墠瀵硅薄鐨勭埗瀵硅薄鍒 parent 鍙橀噺涓 +# 2. 娓呯┖褰撳墠瀵硅薄鐨 parent 灞炴 +# 3. 鍒涘缓褰撳墠瀵硅薄鐨勬繁灞傚壇鏈紝浠ヤ究鍚庣画鐨勬搷浣滀笉浼氬奖鍝嶅師濮嬫暟鎹泦 +# 4. 璁剧疆鍏ㄥ眬鍙橀噺 _OP_NAME 涓哄綋鍓嶆暟鎹泦鐨勮繍绠楃鏍囪瘑 +# 5. 璋冪敤鏁版嵁闆嗙殑 parse_tree() 鏂规硶鏉ユ瀯寤 IR 鏍 +# 6. 鎭㈠鍘熷鐨 parent 灞炴 +# 7. 璋冪敤 _init_device_info() 鏂规硶鏉ュ垵濮嬪寲璁惧淇℃伅 +# 8. 杩斿洖鏋勫缓鐨 IR 鏍戝拰娣卞眰鍓湰鐨勬暟鎹泦 + + # 淇濆瓨褰撳墠瀵硅薄鐨勭埗瀵硅薄鍒 parent 鍙橀噺涓 + parent = self.parent + + # 娓呯┖褰撳墠瀵硅薄鐨 parent 灞炴 + self.parent = [] + + # 鍒涘缓褰撳墠瀵硅薄鐨勬繁灞傚壇鏈紝浠ヤ究鍚庣画鐨勬搷浣滀笉浼氬奖鍝嶅師濮嬫暟鎹泦 + dataset = copy.deepcopy(self) + + # 璁剧疆鍏ㄥ眬鍙橀噺 _OP_NAME 涓哄綋鍓嶆暟鎹泦鐨勮繍绠楃鏍囪瘑 + global _OP_NAME + _OP_NAME = Dataset._get_operator_id(dataset) + + # 璋冪敤鏁版嵁闆嗙殑 parse_tree() 鏂规硶鏉ユ瀯寤 IR 鏍 + ir_tree = dataset.parse_tree() + + # 鎭㈠鍘熷鐨 parent 灞炴 + self.parent = parent + + # 璋冪敤 _init_device_info() 鏂规硶鏉ュ垵濮嬪寲璁惧淇℃伅 + _init_device_info() + + # 杩斿洖鏋勫缓鐨 IR 鏍戝拰娣卞眰鍓湰鐨勬暟鎹泦 + return ir_tree, dataset + + + def parse_tree(self): + """ + Internal method to parse the API tree into an IR tree. + + Returns: + DatasetNode, the root node of the IR tree. + """ + if len(self.parent) > 1: + raise ValueError("The data pipeline is not a tree (i.e., one node has 2 consumers)") + ir_children = [d.parse_tree() for d in self.children] + # Bootstrap can only be performed on a copy of the original dataset node. + # Bootstrap on original dataset node will make all iterators share the same process pool + self.iterator_bootstrap() + ir_node = self.parse(ir_children) + ir_node = self.post_parse(ir_node) + return ir_node + + def __safe_deepcopy__(self, memodict, exclude=()): + if id(self) in memodict: + return memodict[id(self)] + cls = self.__class__ + new_op = cls.__new__(cls) + memodict[id(self)] = new_op + for arg, value in self.__dict__.items(): + if arg in exclude: + setattr(new_op, arg, value) + else: + try: + setattr(new_op, arg, copy.deepcopy(value, memodict)) + except TypeError: + setattr(new_op, arg, value) + return new_op + + @staticmethod + def _noop_mode(): + if _is_role_sched() or (_is_role_pserver() and not _enable_distributed_mindrt()): + return True + return False + + def iterator_bootstrap(self): + pass + + def __add__(self, datasets): + return self.concat(datasets) + + def to_json(self, filename=""): + """ + Serialize a pipeline into JSON string and dump into file if filename is provided. + + Args: + filename (str): filename of JSON file to be saved as (default=""). + + Returns: + str, JSON string of the pipeline. + """ + ir_tree, _ = self.create_ir_tree() + return json.loads(ir_tree.to_json(filename)) + + @check_bucket_batch_by_length + def bucket_batch_by_length(self, column_names, bucket_boundaries, bucket_batch_sizes, element_length_function=None, + pad_info=None, pad_to_bucket_boundary=False, drop_remainder=False): + """ + Bucket elements according to their lengths. Each bucket will be padded and batched when + they are full. + + A length function is called on each row in the dataset. The row is then + bucketed based on its length and bucket boundaries. When a bucket reaches its + corresponding size specified in bucket_batch_sizes, the entire bucket will be + padded according to pad_info, and then form a batch. + Each batch will be full, except one special case: the last batch for each bucket may not be full. + + Args: + column_names (list[str]): Columns passed to element_length_function. + bucket_boundaries (list[int]): A list consisting of the upper boundaries + of the buckets. Must be strictly increasing. If there are n boundaries, + n+1 buckets are created: One bucket for [0, bucket_boundaries[0]), one + bucket for [bucket_boundaries[i], bucket_boundaries[i+1]) for each + 0>> # Create a dataset where certain counts rows are combined into a batch + >>> # and drops the last incomplete batch if there is one. + >>> import numpy as np + >>> def generate_2_columns(n): + ... for i in range(n): + ... yield (np.array([i]), np.array([j for j in range(i + 1)])) + >>> + >>> column_names = ["col1", "col2"] + >>> dataset = ds.GeneratorDataset(generate_2_columns(8), column_names) + >>> bucket_boundaries = [5, 10] + >>> bucket_batch_sizes = [2, 1, 1] + >>> element_length_function = (lambda col1, col2: max(len(col1), len(col2))) + >>> # Will pad col2 to shape [bucket_boundaries[i]] where i is the + >>> # index of the bucket that is currently being batched. + >>> pad_info = {"col2": ([None], -1)} + >>> pad_to_bucket_boundary = True + >>> dataset = dataset.bucket_batch_by_length(column_names, bucket_boundaries, + ... bucket_batch_sizes, + ... element_length_function, pad_info, + ... pad_to_bucket_boundary) + """ + return BucketBatchByLengthDataset(self, column_names, bucket_boundaries, bucket_batch_sizes, + element_length_function, pad_info, pad_to_bucket_boundary, drop_remainder) + + @check_batch + def batch(self, batch_size, drop_remainder=False, num_parallel_workers=None, per_batch_map=None, + input_columns=None, output_columns=None, column_order=None, pad_info=None, + python_multiprocessing=False, max_rowsize=16): + """ + Combine batch_size number of consecutive rows into batches. + + For any child node, a batch is treated as a single row. + For any column, all the elements within that column must have the same shape. + If a per_batch_map callable is provided, it will be applied to the batches of tensors. + + Note: + The order of using repeat and batch reflects the number of batches and per_batch_map. + It is recommended that the repeat operation applied after the batch operation finished. + + Args: + batch_size (int or function): The number of rows each batch is created with. An + int or callable object which takes exactly 1 parameter, BatchInfo. + drop_remainder (bool, optional): Determines whether or not to drop the last block + whose data row number is less than batch size (default=False). If True, and if there are less + than batch_size rows available to make the last batch, then those rows will + be dropped and not propagated to the child node. + num_parallel_workers (int, optional): Number of workers(threads) to process the dataset in parallel + (default=None). + per_batch_map (Callable[[List[numpy.ndarray], ..., List[numpy.ndarray], BatchInfo], (List[numpy.ndarray],\ + ..., List[numpy.ndarray])], optional): Per batch map callable (default=None). A callable + which takes (list[numpy.ndarray], list[numpy.ndarray], ..., BatchInfo) as input parameters. Each + list[numpy.ndarray] represents a batch of numpy.ndarray on a given column. The number of lists should + match with the number of entries in input_columns. The last parameter of the callable should always be + a BatchInfo object. Per_batch_map should return (list[numpy.ndarray], list[numpy.ndarray], ...). The + length of each list in output should be the same as the input. output_columns is required if the number + of output lists is different from input. + input_columns (Union[str, list[str]], optional): List of names of the input columns. The size of the list + should match with signature of per_batch_map callable (default=None). + output_columns (Union[str, list[str]], optional): List of names assigned to the columns + outputted by the last operation. This parameter is mandatory if len(input_columns) != + len(output_columns). The size of this list must match the number of output + columns of the last operation. (default=None, output columns will have the same + name as the input columns, i.e., the columns will be replaced). + column_order (Union[str, list[str]], optional): Specifies the list of all the columns you need in the whole + dataset (default=None). The parameter is required when len(input_column) != len(output_column). + Caution: the list here is not just the columns specified in parameter input_columns and output_columns. + pad_info (dict, optional): Whether to perform padding on selected columns. pad_info={"col1":([224,224],0)} + would pad column with name "col1" to a tensor of size [224,224] and fill the missing with 0 + (default=None). + python_multiprocessing (bool, optional): Parallelize Python function per_batch_map with multi-processing. + This option could be beneficial if the function is computational heavy (default=False). + max_rowsize(int, optional): Maximum size of row in MB that is used for shared memory allocation to copy + data between processes. This is only used if python_multiprocessing is set to True (default=16). + + Returns: + BatchDataset, dataset batched. + + Examples: + >>> # 1) Create a dataset where every 100 rows are combined into a batch + >>> # and drops the last incomplete batch if there is one. + >>> dataset = dataset.batch(100, True) + >>> + >>> # 2锛塺esize image according to its batch number, if it's 5-th batch, resize to (5^2, 5^2) = (25, 25) + >>> def np_resize(col, BatchInfo): + ... output = col.copy() + ... s = (BatchInfo.get_batch_num() + 1) ** 2 + ... index = 0 + ... for c in col: + ... img = Image.fromarray(c.astype('uint8')).convert('RGB') + ... img = img.resize((s, s)) + ... output[index] = np.array(img) + ... index += 1 + ... return (output,) + >>> dataset = dataset.batch(batch_size=8, input_columns=["image"], per_batch_map=np_resize) + >>> + >>> # 3锛塁reate a dataset where its batch size is dynamic + >>> # Define a callable batch size function and let batch size increase 1 each time. + >>> def add_one(BatchInfo): + ... return BatchInfo.get_batch_num() + 1 + >>> dataset = dataset.batch(batch_size=add_one, drop_remainder=True) + >>> + >>> # 4锛塁reate a dataset with batch, then specify the column order. + >>> # Assume that the original coulmn order is ["image", "label"] and change to ["label", "image"]. + >>> dataset = dataset.batch(32, column_order=["label", "image"]) + """ + return BatchDataset(self, batch_size, drop_remainder, num_parallel_workers, per_batch_map, input_columns, + output_columns, column_order, pad_info, python_multiprocessing, max_rowsize) + + @check_sync_wait + def sync_wait(self, condition_name, num_batch=1, callback=None): + """ + Add a blocking condition to the input Dataset. A synchronize action will be applied. + + Args: + condition_name (str): The condition name that is used to toggle sending next row. + num_batch (int): the number of batches without blocking at the start of each epoch (default=1). + callback (function): The callback function that will be invoked when sync_update is called (default=None). + + Returns: + SyncWaitDataset, dataset added a blocking condition. + + Raises: + RuntimeError: If condition name already exists. + + Examples: + >>> import numpy as np + >>> def gen(): + ... for i in range(100): + ... yield (np.array(i),) + >>> + >>> class Augment: + ... def __init__(self, loss): + ... self.loss = loss + ... + ... def preprocess(self, input_): + ... return input_ + ... + ... def update(self, data): + ... self.loss = data["loss"] + >>> + >>> batch_size = 4 + >>> dataset = ds.GeneratorDataset(gen, column_names=["input"]) + >>> + >>> aug = Augment(0) + >>> dataset = dataset.sync_wait(condition_name="policy", callback=aug.update) + >>> dataset = dataset.map(operations=[aug.preprocess], input_columns=["input"]) + >>> dataset = dataset.batch(batch_size) + >>> count = 0 + >>> for data in dataset.create_dict_iterator(num_epochs=1, output_numpy=True): + ... assert data["input"][0] == count + ... count += batch_size + ... data = {"loss": count} + ... dataset.sync_update(condition_name="policy", data=data) + """ + return SyncWaitDataset(self, condition_name, num_batch, callback) + + @check_shuffle + def shuffle(self, buffer_size): + """ + Randomly shuffles the rows of this dataset using the following policy: + + 1. Make a shuffle buffer that contains the first buffer_size rows. + 2. Randomly select an element from the shuffle buffer to be the next row + propagated to the child node. + 3. Get the next row (if any) from the parent node and put it in the shuffle buffer. + 4. Repeat steps 2 and 3 until there are no more rows left in the shuffle buffer. + + A random seed can be provided to be used on the first epoch. In every subsequent + epoch, the seed is changed to a new one, randomly generated value. + + Args: + buffer_size (int): The size of the buffer (must be larger than 1) for + shuffling. Setting buffer_size equal to the number of rows in the entire + dataset will result in a global shuffle. + + Returns: + Dataset, dataset shuffled. + + Raises: + RuntimeError: If exist sync operators before shuffle. + + Examples: + >>> # dataset is an instance object of Dataset + >>> # Optionally set the seed for the first epoch + >>> ds.config.set_seed(58) + >>> # Create a shuffled dataset using a shuffle buffer of size 4 + >>> dataset = dataset.shuffle(4) + """ + return ShuffleDataset(self, buffer_size) + + def flat_map(self, func): + """ + Map `func` to each row in dataset and flatten the result. + + The specified `func` is a function that must take one `numpy.ndarray` as input + and return a `Dataset`. + 灏嗏渇unc鈥濇槧灏勫埌鏁版嵁闆嗕腑鐨勬瘡涓琛屽苟灞曞钩缁撴灉銆 + 鎸囧畾鐨勨渇unc鈥濇槸涓涓繀椤绘帴鍙椾竴涓渘umpy.ndarray鈥濅綔涓鸿緭鍏ョ殑鍑芥暟骞惰繑鍥炰竴涓滄暟鎹泦鈥濄 + Args: + func (function): A function that must take one `numpy.ndarray` as an argument and + return a `Dataset`. + + Returns: + Dataset, dataset applied by the function. + + Examples: + >>> # 1) flat_map on one column dataset + >>> dataset = ds.NumpySlicesDataset([[0, 1], [2, 3]], shuffle=False) + >>> + >>> def repeat(array): + ... # create a NumpySlicesDataset with the array + ... data = ds.NumpySlicesDataset(array, shuffle=False) + ... # repeat the dataset twice + ... data = data.repeat(2) + ... return data + >>> + >>> dataset = dataset.flat_map(repeat) + >>> # [0, 1, 0, 1, 2, 3, 2, 3] + >>> + >>> # 2) flat_map on multi column dataset + >>> dataset = ds.NumpySlicesDataset(([[0, 1], [2, 3]], [[0, -1], [-2, -3]]), shuffle=False) + + >>> def plus_and_minus(col1, col2): + ... # apply different methods on columns + ... data = ds.NumpySlicesDataset((col1 + 1, col2 - 1), shuffle=False) + ... return data + + >>> dataset = dataset.flat_map(plus_and_minus) + >>> # ([1, 2, 3, 4], [-1, -2, -3, -4]) + + Raises: + TypeError: If `func` is not a function. + TypeError: If `func` doesn't return a Dataset. + """ + # 鍒濆鍖栨暟鎹泦涓 None + dataset = None + + # 妫鏌 func 鏄惁鏄竴涓彲璋冪敤鍑芥暟锛屽惁鍒欐姏鍑虹被鍨嬮敊璇 + if not hasattr(func, '__call__'): + logger.critical("func must be a function.") + raise TypeError("func must be a function.") + + # 閬嶅巻鏁版嵁闆嗕腑鐨勬瘡涓厓绱狅紝浣跨敤 func 澶勭悊姣忎釜鍏冪礌锛屽苟灏嗙粨鏋滄嫾鎺ュ埌 dataset 涓 + for row_data in self.create_tuple_iterator(num_epochs=1, output_numpy=True): + if dataset is None: + dataset = func(*row_data) + else: + dataset += func(*row_data) + + # 妫鏌 dataset 鏄惁鏄 Dataset 绫诲瀷鐨勫璞★紝鍚﹀垯鎶涘嚭绫诲瀷閿欒 + if not isinstance(dataset, Dataset): + logger.critical("flat_map must return a Dataset object.") + raise TypeError("flat_map must return a Dataset object.") + + # 杩斿洖鎷兼帴鍚庣殑鏂版暟鎹泦 + return dataset + + + @check_map + def map(self, operations, input_columns=None, output_columns=None, column_order=None, + num_parallel_workers=None, python_multiprocessing=False, cache=None, callbacks=None, + max_rowsize=16, offload=None): + """ + Apply each operation in operations to this dataset. + + The order of operations is determined by the position of each operation in the operations parameter. + operations[0] will be applied first, then operations[1], then operations[2], etc. + + Each operation will be passed one or more columns from the dataset as input, and zero or + more columns will be outputted. The first operation will be passed the columns specified + in input_columns as input. If there is more than one operator in operations, the outputted + columns of the previous operation are used as the input columns for the next operation. + The columns outputted by the very last operation will be assigned names specified by + output_columns. + + Only the columns specified in column_order will be propagated to the child node. These + columns will be in the same order as specified in column_order. + + Args: + operations (Union[list[TensorOperation], list[functions]]): List of operations to be + applied on the dataset. Operations are applied in the order they appear in this list. + input_columns (Union[str, list[str]], optional): List of the names of the columns that will be passed to + the first operation as input. The size of this list must match the number of + input columns expected by the first operator. (default=None, the first + operation will be passed however many columns that are required, starting from + the first column). + output_columns (Union[str, list[str]], optional): List of names assigned to the columns outputted by + the last operation. This parameter is mandatory if len(input_columns) != + len(output_columns). The size of this list must match the number of output + columns of the last operation. (default=None, output columns will have the same + name as the input columns, i.e., the columns will be replaced). + column_order (list[str], optional): Specifies the list of all the columns you need in the whole + dataset (default=None). The parameter is required when len(input_column) != len(output_column). + Caution: the list here is not just the columns specified in parameter input_columns and output_columns. + num_parallel_workers (int, optional): Number of threads used to process the dataset in + parallel (default=None, the value from the configuration will be used). + python_multiprocessing (bool, optional): Parallelize Python operations with multiple worker processes. This + option could be beneficial if the Python operation is computational heavy (default=False). + cache (DatasetCache, optional): Use tensor caching service to speed up dataset processing. + (default=None, which means no cache is used). + callbacks (DSCallback, list[DSCallback], optional): List of Dataset callbacks to be called (Default=None). + max_rowsize (int, optional): Maximum size of row in MB that is used for shared memory allocation to copy + data between processes. This is only used if python_multiprocessing is set to True (Default=16). + offload (bool, optional): Flag to indicate whether offload is used (Default=None). + + Note: + - Input `operations` accepts TensorOperations defined in mindspore.dataset part, plus user-defined + Python functions (PyFuncs). + - Do not add network computing operators from mindspore.nn and mindspore.ops or others into this + `operations`. + + Returns: + Dataset, dataset after mapping operation. + + Examples: + >>> # dataset is an instance of Dataset which has 2 columns, "image" and "label". + >>> + >>> # Define two operations, where each operation accepts 1 input column and outputs 1 column. + >>> decode_op = c_vision.Decode(rgb=True) + >>> random_jitter_op = c_vision.RandomColorAdjust(brightness=(0.8, 0.8), contrast=(1, 1), + ... saturation=(1, 1), hue=(0, 0)) + >>> + >>> # 1) Simple map example. + >>> + >>> # Apply decode_op on column "image". This column will be replaced by the outputted + >>> # column of decode_op. Since column_order is not provided, both columns "image" + >>> # and "label" will be propagated to the child node in their original order. + >>> dataset = dataset.map(operations=[decode_op], input_columns=["image"]) + >>> + >>> # Decode and rename column "image" to "decoded_image". + >>> dataset = dataset.map(operations=[decode_op], input_columns=["image"], output_columns=["decoded_image"]) + >>> + >>> # Specify the order of the output columns. + >>> dataset = dataset.map(operations=[decode_op], input_columns=["image"], + ... output_columns=None, column_order=["label", "image"]) + >>> + >>> # Rename column "image" to "decoded_image" and also specify the order of the output columns. + >>> dataset = dataset.map(operations=[decode_op], input_columns=["image"], + ... output_columns=["decoded_image"], column_order=["label", "decoded_image"]) + >>> + >>> # Rename column "image" to "decoded_image" and keep only this column. + >>> dataset = dataset.map(operations=[decode_op], input_columns=["image"], + ... output_columns=["decoded_image"], column_order=["decoded_image"]) + >>> + >>> # A simple example for mapping pyfunc. Renaming columns and specifying column order + >>> # work in the same way as the previous examples. + >>> dataset = ds.NumpySlicesDataset(data=[[0, 1, 2]], column_names=["data"]) + >>> dataset = dataset.map(operations=[(lambda x: x + 1)], input_columns=["data"]) + >>> + >>> # 2) Map example with more than one operation. + >>> + >>> # Create a dataset where the images are decoded, then randomly color jittered. + >>> # decode_op takes column "image" as input and outputs one column. The column + >>> # outputted by decode_op is passed as input to random_jitter_op. + >>> # random_jitter_op will output one column. Column "image" will be replaced by + >>> # the column outputted by random_jitter_op (the very last operation). All other + >>> # columns are unchanged. Since column_order is not specified, the order of the + >>> # columns will remain the same. + >>> dataset = dataset.map(operations=[decode_op, random_jitter_op], input_columns=["image"]) + >>> + >>> # Rename the column outputted by random_jitter_op to "image_mapped". + >>> # Specifying column order works in the same way as examples in 1). + >>> dataset = dataset.map(operations=[decode_op, random_jitter_op], input_columns=["image"], + ... output_columns=["image_mapped"]) + >>> + >>> # Map with multiple operations using pyfunc. Renaming columns and specifying column order + >>> # work in the same way as examples in 1). + >>> dataset = ds.NumpySlicesDataset(data=[[0, 1, 2]], column_names=["data"]) + >>> dataset = dataset.map(operations=[(lambda x: x * x), (lambda x: x - 1)], input_columns=["data"], + ... output_columns=["data_mapped"]) + >>> + >>> # 3) Example where number of input columns is not equal to number of output columns. + >>> + >>> # operations[0] is a lambda that takes 2 columns as input and outputs 3 columns. + >>> # operations[1] is a lambda that takes 3 columns as input and outputs 1 column. + >>> # operations[2] is a lambda that takes 1 column as input and outputs 4 columns. + >>> # + >>> # Note: The number of output columns of operation[i] must equal the number of + >>> # input columns of operation[i+1]. Otherwise, this map call will also result + >>> # in an error. + >>> operations = [(lambda x, y: (x, x + y, x + y + 1)), + ... (lambda x, y, z: x * y * z), + ... (lambda x: (x % 2, x % 3, x % 5, x % 7))] + >>> + >>> # Note: Since the number of input columns is not the same as the number of + >>> # output columns, the output_columns and column_order parameters must be + >>> # specified. Otherwise, this map call will also result in an error. + >>> + >>> dataset = ds.NumpySlicesDataset(data=([[0, 1, 2]], [[3, 4, 5]]), column_names=["x", "y"]) + >>> + >>> # Propagate all columns to the child node in this order: + >>> dataset = dataset.map(operations, input_columns=["x", "y"], + ... output_columns=["mod2", "mod3", "mod5", "mod7"], + ... column_order=["mod2", "mod3", "mod5", "mod7"]) + >>> + >>> # Propagate some columns to the child node in this order: + >>> dataset = dataset.map(operations, input_columns=["x", "y"], + ... output_columns=["mod2", "mod3", "mod5", "mod7"], + ... column_order=["mod7", "mod3", "col2"]) + """ + if hasattr(self, 'operator_mixed') and getattr(self, 'operator_mixed') is True: + num_parallel_workers = 1 + logger.warning( + "Input 'operations' of 'map' includes network computing operators like in mindspore.nn, mindspore.ops, " + "mindspore.numpy module and etc, which do not support multi-thread compiling, recommend to replace it " + "with python implemented operator like numpy etc. Here decrease 'num_parallel_workers' into 1.") + + return MapDataset(self, operations, input_columns, output_columns, column_order, num_parallel_workers, + python_multiprocessing, cache, callbacks, max_rowsize, offload) + + @check_filter + def filter(self, predicate, input_columns=None, num_parallel_workers=None): + """ + Filter dataset by prediction. + + Args: + predicate (callable): Python callable which returns a boolean value. If False then filter the element. + input_columns (Union[str, list[str]], optional): List of names of the input columns. If not provided + or provided with None, the predicate will be applied on all columns in the dataset (default=None). + num_parallel_workers (int, optional): Number of workers to process the dataset + in parallel (default=None). + + Returns: + Dataset, dataset filtered. + + Examples: + >>> # generator data(0 ~ 63) + >>> # filter the data that greater than or equal to 11 + >>> dataset = dataset.filter(predicate=lambda data: data < 11, input_columns = ["data"]) + """ + return FilterDataset(self, predicate, input_columns, num_parallel_workers) + + @check_repeat + def repeat(self, count=None): + """ + Repeat this dataset `count` times. Repeat infinitely if the count is None or -1. + + Note: + The order of using repeat and batch reflects the number of batches. It is recommended that + the repeat operation is used after the batch operation. + + Args: + count (int): Number of times the dataset is going to be repeated (default=None). + + Returns: + Dataset, dataset repeated. + + Examples: + >>> # dataset is an instance object of Dataset + >>> + >>> # Create a dataset where the dataset is repeated for 50 epochs + >>> dataset = dataset.repeat(50) + >>> + >>> # Create a dataset where each epoch is shuffled individually + >>> dataset = dataset.shuffle(10) + >>> dataset = dataset.repeat(50) + >>> + >>> # Create a dataset where the dataset is first repeated for + >>> # 50 epochs before shuffling. The shuffle operator will treat + >>> # the entire 50 epochs as one big dataset. + >>> dataset = dataset.repeat(50) + >>> dataset = dataset.shuffle(10) + """ + return RepeatDataset(self, count) + + @check_skip + def skip(self, count): + """ + Skip the first N elements of this dataset. + + Args: + count (int): Number of elements in the dataset to be skipped. + + Returns: + Dataset, dataset that containing rows like origin rows subtract skipped rows. + + Examples: + >>> # dataset is an instance object of Dataset + >>> # Create a dataset which skips first 3 elements from data + >>> dataset = dataset.skip(3) + """ + return SkipDataset(self, count) + + @check_take + def take(self, count=-1): + """ + Takes at most given numbers of elements from the dataset. + + Note: + 1. If count is greater than the number of elements in the dataset or equal to -1, + all the elements in dataset will be taken. + 2. The order of using take and batch matters. If take is before batch operation, + then take the given number of rows; otherwise take the given number of batches. + + Args: + count (int, optional): Number of elements to be taken from the dataset (default=-1). + + Returns: + Dataset, dataset taken. + + Examples: + >>> # dataset is an instance object of Dataset + >>> # Create a dataset where the dataset includes 50 elements. + >>> dataset = dataset.take(50) + """ + return TakeDataset(self, count) +# 瀹氫箟涓涓柟娉曠敤浜庤绠楃粷瀵瑰垎鍓插ぇ灏忕殑鍑芥暟 + def _get_absolute_split_sizes(self, sizes): + """ + Internal method called by split to calculate absolute split sizes and to + do some error checking after calculating absolute split sizes. + split璋冪敤鐨勫唴閮ㄦ柟娉曪紝鐢ㄤ簬璁$畻缁濆鎷嗗垎澶у皬骞跺湪璁$畻缁濆鍒嗗壊澶у皬鍚庤繘琛屼竴浜涢敊璇鏌ャ + Returns: + int, absolute split sizes of the dataset. + """ + # Call get_dataset_size here and check input here because + # don't want to call this once in check_split and another time in + # here again + + # 鑾峰彇鏁版嵁闆嗙殑澶у皬 + dataset_size = self.get_dataset_size() + + # 妫鏌ユ暟鎹泦澶у皬鏄惁宸茬煡涓斿ぇ浜0锛屽惁鍒欐姏鍑鸿繍琛屾椂閿欒 + if dataset_size is None or dataset_size <= 0: + raise RuntimeError("dataset_size is unknown, unable to split.") + + # 妫鏌ヨ緭鍏ョ殑鍒嗗壊鐧惧垎姣旀槸鍚︿负鍒楄〃锛屽惁鍒欐姏鍑鸿繍琛屾椂閿欒 + if not isinstance(sizes, list): + raise RuntimeError("sizes must be a list.") + + # 妫鏌izes鍒楄〃涓殑鎵鏈夊厓绱犳槸鍚﹂兘涓烘暣鏁 + all_int = all(isinstance(item, int) for item in sizes) + if all_int: + # 濡傛灉鎵鏈夊厓绱犻兘鏄暣鏁帮紝妫鏌ュ畠浠殑鍜屾槸鍚︾瓑浜庢暟鎹泦澶у皬锛屽惁鍒欐姏鍑鸿繍琛屾椂閿欒 + sizes_sum = sum(sizes) + if sizes_sum != dataset_size: + raise RuntimeError("Sum of split sizes {} is not equal to dataset size {}." + .format(sizes_sum, dataset_size)) + return sizes + + # 濡傛灉sizes鍒楄〃涓寘鍚潪鏁存暟鍏冪礌锛屽皢鐧惧垎姣旇浆鎹负缁濆澶у皬 + absolute_sizes = [] + for item in sizes: + absolute_size = int(round(item * dataset_size)) + # 妫鏌ヨ绠楀緱鍒扮殑缁濆澶у皬鏄惁澶т簬0锛屽惁鍒欐姏鍑鸿繍琛屾椂閿欒 + if absolute_size == 0: + raise RuntimeError("Split percentage {} is too small.".format(item)) + absolute_sizes.append(absolute_size) + + # 璁$畻缁濆鍒嗗壊澶у皬鐨勬诲拰 + absolute_sizes_sum = sum(absolute_sizes) + + # 濡傛灉浠嶇劧闇瑕佹洿澶氱殑琛岋紝灏嗗畠浠垎閰嶇粰绗竴涓垎鍓层 + # 濡傛灉鏈夊お澶氱殑琛岋紝浠庣涓涓叿鏈夎冻澶熻鏁扮殑鍒嗗壊涓垹闄ゅ浣欑殑琛屻 + size_difference = int(dataset_size - absolute_sizes_sum) + if size_difference > 0: + absolute_sizes[0] += size_difference + else: + for i, _ in enumerate(absolute_sizes): + if absolute_sizes[i] + size_difference > 0: + absolute_sizes[i] += size_difference + break + + # 妫鏌ヨ绠楀緱鍒扮殑缁濆鍒嗗壊澶у皬鐨勬诲拰鏄惁绛変簬鏁版嵁闆嗗ぇ灏忥紝鍚﹀垯鎶涘嚭杩愯鏃堕敊璇 + if sum(absolute_sizes) != dataset_size: + raise RuntimeError("Sum of calculated split sizes {} is not equal to dataset size {}." + .format(absolute_sizes_sum, dataset_size)) + + # 杩斿洖璁$畻寰楀埌鐨勭粷瀵瑰垎鍓插ぇ灏忓垪琛 + return absolute_sizes + + @check_split + def split(self, sizes, randomize=True): + """ + Split the dataset into smaller, non-overlapping datasets. + 灏嗘暟鎹泦鎷嗗垎涓烘洿灏忋佷笉閲嶅彔鐨勬暟鎹泦銆 + This is a general purpose split function which can be called from any operator in the pipeline. + There is another, optimized split function, which will be called automatically if ds.split is + called where ds is a MappableDataset. + + Args: + sizes (Union[list[int], list[float]]): If a list of integers [s1, s2, 鈥, sn] is + provided, the dataset will be split into n datasets of size s1, size s2, 鈥, size sn + respectively. If the sum of all input sizes does not equal the original dataset size, an + error will throw. + If a list of floats [f1, f2, 鈥, fn] is provided, all floats must be between 0 and 1 + and must sum to 1, otherwise an error will throw. The dataset will be split into n + Datasets of size round(f1*K), round(f2*K), 鈥, round(fn*K) where K is the size of the + original dataset. + If after rounding: + + - Any size equals 0, an error will occur. + - The sum of split sizes < K, the difference of K - sigma(round(fi * k)) will be added to the first + split. + - The sum of split sizes > K, the difference of sigma(round(fi * K)) - K will be removed from the first + large enough split such that it will have at least 1 row after removing the difference. + + randomize (bool, optional): Determines whether or not to split the data randomly (default=True). + If True, the data will be randomly split. Otherwise, each split will be created with + consecutive rows from the dataset. + + Note: + 1. Dataset cannot be sharded if split is going to be called. + 2. It is strongly recommended to not shuffle the dataset, but use randomize=True instead. + Shuffling the dataset may not be deterministic, which means the data in each split + will be different in each epoch. + + Raises: + RuntimeError: If get_dataset_size returns None or is not supported for this dataset. + RuntimeError: If `sizes` is list of integers and sum of all elements in sizes does not + equal the dataset size. + RuntimeError: If `sizes` is list of float and there is a split with size 0 after calculations. + RuntimeError: If the dataset is sharded prior to calling split. + ValueError: If `sizes` is list of float and not all floats are between 0 and 1, or if the + floats don't sum to 1. + + Returns: + tuple(Dataset), a tuple of datasets that have been split. + + Examples: + >>> # TextFileDataset is not a mappable dataset, so this non-optimized split will be called. + >>> # Since many datasets have shuffle on by default, set shuffle to False if split will be called! + >>> dataset = ds.TextFileDataset(text_file_dataset_dir, shuffle=False) + >>> train_dataset, test_dataset = dataset.split([0.9, 0.1]) + """ + + if self.is_shuffled(): # 杩欒浠g爜妫鏌ユ暟鎹泦鏄惁宸茬粡琚礂鐗岋紙shuffled锛夛紝濡傛灉鏄紝鍒欏彂鍑鸿鍛婁俊鎭紝鎻愮ず鏁版嵁闆嗗凡缁忚娲楃墝銆 + logger.warning("Dataset is shuffled before split.") + + if self.is_sharded(): # 杩欒浠g爜妫鏌ユ暟鎹泦鏄惁宸茬粡琚垎鐗囷紙sharded锛夛紝濡傛灉鏄紝鍒欏紩鍙戣繍琛屾椂閿欒锛圧untimeError锛夛紝琛ㄧず鍦ㄥ垎鍓蹭箣鍓嶄笉搴旇瀵规暟鎹泦杩涜鍒嗙墖銆 + raise RuntimeError("Dataset should not be sharded before split.") + # 璋冪敤 `_get_absolute_split_sizes` 鏂规硶锛屽皢鐩稿澶у皬鍒楄〃 `sizes` 杞崲涓虹粷瀵瑰ぇ灏忓垪琛 `absolute_sizes`锛岀敤浜庢寚瀹氭瘡涓垎鍓茬殑瀹為檯澶у皬銆 + absolute_sizes = self._get_absolute_split_sizes(sizes) + splits = [] # 鍒涘缓涓涓┖鍒楄〃 `splits`锛岀敤浜庡瓨鍌ㄥ垎鍓插悗鐨勬暟鎹泦銆 + rows_to_skip = 0 # 鍒濆鍖栦竴涓彉閲 `rows_to_skip`锛岀敤浜庤窡韪璺宠繃鐨勮鏁般 + for size in absolute_sizes: # 閬嶅巻缁濆澶у皬鍒楄〃锛屽姣忎釜鍒嗗壊澶у皬鎵ц浠ヤ笅鎿嶄綔锛 + ds = copy.deepcopy(self) # 鍒涘缓褰撳墠鏁版嵁闆嗙殑娣卞害鎷疯礉锛屼互渚垮鍏惰繘琛屾搷浣滆屼笉褰卞搷鍘熷鏁版嵁闆嗐 + if randomize: + # want to shuffle the same way every epoch before split + # in alter_tree, shuffle buffer is minimum 10000, so use 10000 here + ds = ds.shuffle(10000) #瀵规暟鎹泦杩涜闅忔満鍖栵紝杩欓噷浣跨敤浜嗕竴涓殢鏈虹紦鍐插尯澶у皬涓10000鏉ョ‘淇濇礂鐗屻 + ds.reshuffle_each_epoch = False #绂佺敤姣忎釜epoch閲嶆柊娲楃墝锛屼互纭繚鍦ㄦ瘡涓垎鍓蹭腑娲楃墝鐨勬柟寮忎竴鑷淬 + + if rows_to_skip > 0: # 濡傛灉闇瑕佽烦杩囪鏁帮紙rows_to_skip澶т簬0锛夛紝鍒欐墽琛屼互涓嬫搷浣滐細 + ds = ds.skip(rows_to_skip) # 璺宠繃鍓嶉潰鍒嗗壊宸茬粡澶勭悊杩囩殑琛屾暟銆 + + ds = ds.take(size) # 浠庢暟鎹泦涓彇鍑烘寚瀹氭暟閲忕殑琛岋紝浠ュ垱寤哄綋鍓嶅垎鍓层 + splits.append(ds) # 灏嗗綋鍓嶅垎鍓叉坊鍔犲埌 `splits` 鍒楄〃涓 + + rows_to_skip += size # 鏇存柊璺宠繃鐨勮鏁般 + + return tuple(splits) #杩斿洖鍖呭惈鎵鏈夊垎鍓叉暟鎹泦鐨勫厓缁勶紝姣忎釜鍒嗗壊閮芥槸涓涓嫭绔嬬殑鏁版嵁闆 + + @check_zip_dataset + def zip(self, datasets): + """ + Zip the datasets in the sense of input tuple of datasets. Columns in the input datasets must have different + name. + + Args: + datasets (Union[tuple, class Dataset]): A tuple of datasets or a single class Dataset + to be zipped together with this dataset. + + Returns: + Dataset, dataset zipped. + + Examples: + >>> # Create a dataset which is the combination of dataset and dataset_1 + >>> dataset = dataset.zip(dataset_1) + """ + if isinstance(datasets, tuple): + datasets = (self, *datasets) + elif isinstance(datasets, Dataset): + datasets = (self, datasets) + else: + raise TypeError("Invalid datasets, expected Dataset object or tuple of Dataset, but got %s!" % datasets) + return ZipDataset(datasets) + + @check_concat + def concat(self, datasets): + """ + Concatenate the dataset objects in the input list. + Performing "+" operation on dataset objects can achieve the same effect. + + Note: + The column name, and rank and type of the column data must be the same in the input datasets. + + Args: + datasets (Union[list, class Dataset]): A list of datasets or a single class Dataset + to be concatenated together with this dataset. + + Returns: + Dataset, dataset concatenated. + + Examples: + >>> # Create a dataset by concatenating dataset_1 and dataset_2 with "+" operator + >>> dataset = dataset_1 + dataset_2 + >>> # Create a dataset by concatenating dataset_1 and dataset_2 with concat operation + >>> dataset = dataset_1.concat(dataset_2) + """ + if isinstance(datasets, Dataset): + datasets = [self] + [datasets] + elif isinstance(datasets, list): + datasets = [self] + datasets + else: + raise TypeError("Invalid datasets, expected Dataset object or list of Dataset, but got %s!" % datasets) + return ConcatDataset(datasets) + + @check_rename + def rename(self, input_columns, output_columns): + """ + Rename the columns in input datasets. + + Args: + input_columns (Union[str, list[str]]): List of names of the input columns. + output_columns (Union[str, list[str]]): List of names of the output columns. + + Returns: + Dataset, dataset renamed. + + Examples: + >>> # dataset is an instance object of Dataset + >>> input_columns = ["input_col1", "input_col2", "input_col3"] + >>> output_columns = ["output_col1", "output_col2", "output_col3"] + >>> + >>> # Create a dataset where input_col1 is renamed to output_col1, and + >>> # input_col2 is renamed to output_col2, and input_col3 is renamed + >>> # to output_col3. + >>> dataset = dataset.rename(input_columns=input_columns, output_columns=output_columns) + """ + + return RenameDataset(self, input_columns, output_columns) + + @check_project + def project(self, columns): + """ + Project certain columns in input dataset. + + The specified columns will be selected from the dataset and passed into + the pipeline with the order specified. The other columns are discarded. + + Args: + columns(Union[str, list[str]]): List of names of the columns to project. + + Returns: + Dataset, dataset projected. + + Examples: + >>> # dataset is an instance object of Dataset + >>> columns_to_project = ["column3", "column1", "column2"] + >>> + >>> # Create a dataset that consists of column3, column1, column2 + >>> # in that order, regardless of the original order of columns. + >>> dataset = dataset.project(columns=columns_to_project) + """ + + return ProjectDataset(self, columns) + + def apply(self, apply_func): + """ + Apply a function in this dataset. + + Args: + apply_func (function): A function that must take one `Dataset` as an argument and + return a preprocessed `Dataset`. + + Returns: + Dataset, dataset applied by the function. + + Examples: + >>> # dataset is an instance object of Dataset + >>> + >>> # Declare an apply_func function which returns a Dataset object + >>> def apply_func(data): + ... data = data.batch(2) + ... return data + >>> + >>> # Use apply to call apply_func + >>> dataset = dataset.apply(apply_func) + + Raises: + TypeError: If apply_func is not a function. + TypeError: If apply_func doesn't return a Dataset. + """ + + if not hasattr(apply_func, '__call__'): + raise TypeError("apply_func must be a function.") + + dataset = apply_func(self) + if not isinstance(dataset, Dataset): + raise TypeError("apply_func must return a dataset.") + return dataset + + @check_device_send + def device_que(self, send_epoch_end=True, create_data_info_queue=False): + """ + Return a transferred Dataset that transfers data through a device. + + Args: + send_epoch_end (bool, optional): Whether to send end of sequence to device or not (default=True). + create_data_info_queue (bool, optional): Whether to create queue which stores + types and shapes of data or not(default=False). + + Note: + If device is Ascend, features of data will be transferred one by one. The limitation + of data transmission per time is 256M. + + Returns: + Dataset, dataset for transferring. + """ + return TransferDataset(self, send_epoch_end, create_data_info_queue) + + @check_device_send + def to_device(self, send_epoch_end=True, create_data_info_queue=False): + """ + Transfer data from CPU to GPU or Ascend or other devices. + + Args: + send_epoch_end (bool, optional): Whether to send the end of sequence to device or not (default=True). + create_data_info_queue (bool, optional): Whether to create queue which stores + types and shapes of data or not(default=False). + + Note: + This interface will be deleted or invisible in the future. + Please use `device_que` to enable dataset sink mode. + If device is Ascend, features of data will be transferred one by one. The limitation + of data transmission per second is 256M. + + Returns: + TransferDataset, dataset for transferring. + + Raises: + RuntimeError: If distribution file path is given but failed to read. + """ + logger.warning("This interface will be deleted or invisible in the future. " + "Please use 'device_que' to enable dataset sink mode.") + + return TransferDataset(self, send_epoch_end, create_data_info_queue) + + @check_save + def save(self, file_name, num_files=1, file_type='mindrecord'): + """ + Save the dynamic data processed by the dataset pipeline in common dataset format. + Supported dataset formats: `mindrecord` only. And you can use `MindDataset` API to read the saved file(s). + 浠ラ氱敤鏁版嵁闆嗘牸寮忎繚瀛樻暟鎹泦绠¢亾澶勭悊鐨勫姩鎬佹暟鎹 + Implicit type casting exists when saving data as `mindrecord`. The transform table shows how to do type casting. + + .. list-table:: Implicit Type Casting when Saving as `mindrecord` + :widths: 25 25 50 + :header-rows: 1 + + * - Type in `dataset` + - Type in `mindrecord` + - Details + * - bool + - None + - Not supported + * - int8 + - int32 + - + * - uint8 + - bytes(1D uint8) + - Drop dimension + * - int16 + - int32 + - + * - uint16 + - int32 + - + * - int32 + - int32 + - + * - uint32 + - int64 + - + * - int64 + - int64 + - + * - uint64 + - None + - Not supported + * - float16 + - float32 + - + * - float32 + - float32 + - + * - float64 + - float64 + - + * - string + - string + - Multi-dimensional string not supported + + Note: + 1. To save the samples in order, set dataset's shuffle to False and num_files to 1. + 2. Before calling the function, do not use batch operator, repeat operator or data augmentation operators + with random attribute in map operator. + 3. When array dimension is variable, one-dimensional arrays or + multi-dimensional arrays with variable dimension 0 are supported. + 4. Mindrecord does not support uint64, multi-dimensional uint8(drop dimension) nor + multi-dimensional string. + + Args: + file_name (str): Path to dataset file. + num_files (int, optional): Number of dataset files (default=1). + file_type (str, optional): Dataset format (default='mindrecord'). + + """ + ir_tree, api_tree = self.create_ir_tree() #璋冪敤 `create_ir_tree()` 鏂规硶鍒涘缓浜嗗唴閮ㄦ暟鎹粨鏋 `ir_tree` 鍜 `api_tree`锛岀敤浜庝繚瀛樻暟鎹泦鐨勪俊鎭 + + runtime_context = cde.PythonRuntimeContext() #鍒涘缓浜嗕竴涓狿ython杩愯鏃剁幆澧冨璞 `runtime_context`銆 + runtime_context.Init() #鍒濆鍖栬繍琛屾椂鐜銆 + consumer = cde.PythonSaveToDisk(file_name, num_files, file_type) #鍒涘缓浜嗕竴涓繚瀛樺埌纾佺洏鐨勬秷璐硅呭璞 `consumer`锛屾寚瀹氫簡鏂囦欢鍚嶃佹枃浠舵暟閲忓拰鏂囦欢绫诲瀷銆 + consumer.Init(ir_tree) #鍒濆鍖栨秷璐硅呭璞★紝浼犲叆 `ir_tree` 鏁版嵁缁撴瀯銆 + runtime_context.AssignConsumer(consumer) #灏嗘秷璐硅呭璞″垎閰嶇粰杩愯鏃剁幆澧冦 + + consumer.Save() #鎵ц淇濆瓨鎿嶄綔锛屽皢鏁版嵁闆嗕繚瀛樺埌纾佺洏銆 + _set_dataset_permissions(file_name, num_files) #璋冪敤 `_set_dataset_permissions` 鍑芥暟锛岃缃暟鎹泦鏂囦欢鐨勬潈闄愩 + del api_tree #鍒犻櫎 `api_tree` 瀵硅薄锛岄噴鏀惧唴瀛樸 + + @check_tuple_iterator + def create_tuple_iterator(self, columns=None, num_epochs=-1, output_numpy=False, do_copy=True): + """ + Create an iterator over the dataset. The datatype retrieved back will be a list of `numpy.ndarray`. + + To specify which columns to list and the order needed, use columns_list. If columns_list + is not provided, the order of the columns will remain unchanged. + + Args: + columns (list[str], optional): List of columns to be used to specify the order of columns + (default=None, means all columns). + num_epochs (int, optional): Maximum number of epochs that iterator can be iterated. + (default=-1, iterator can be iterated infinite number of epochs) + output_numpy (bool, optional): Whether or not to output NumPy datatype. + If output_numpy=False, iterator will output MSTensor (default=False). + do_copy (bool, optional): when output data type is mindspore.Tensor, + use this param to select the conversion method, only take False for better performance (default=True). + + Returns: + Iterator, tuple iterator over the dataset. + + Examples: + >>> # dataset is an instance object of Dataset + >>> iterator = dataset.create_tuple_iterator() + >>> for item in iterator: + ... # item is a list + ... print(type(item)) + ... break + + """ + if output_numpy is None: + output_numpy = False + + if Dataset._noop_mode(): + return DummyIterator(self, 'tuple', output_numpy) + return TupleIterator(self, columns, num_epochs, output_numpy, do_copy) + + @check_dict_iterator + def create_dict_iterator(self, num_epochs=-1, output_numpy=False): + """ + Create an iterator over the dataset. The data retrieved will be a dictionary datatype. + + The order of the columns in the dictionary may not be the same as the original order. + + Args: + num_epochs (int, optional): Maximum number of epochs that iterator can be iterated + (default=-1, iterator can be iterated infinite number of epochs). + output_numpy (bool, optional): Whether or not to output NumPy datatype, + if output_numpy=False, iterator will output MSTensor (default=False). + + Returns: + Iterator, dictionary iterator over the dataset. + + Examples: + >>> # dataset is an instance object of Dataset + >>> iterator = dataset.create_dict_iterator() + >>> for item in iterator: + ... # item is a dict + ... print(type(item)) + ... break + + """ + if output_numpy is None: + output_numpy = False + + if Dataset._noop_mode(): + return DummyIterator(self, 'dict', output_numpy) + return DictIterator(self, num_epochs, output_numpy) + + def __iter__(self): + """Create an iterator over the dataset.""" + return self.create_tuple_iterator(num_epochs=1) + + @property + def input_indexs(self): + """ + Get the column index, which represents the corresponding relationship between the data column order + and the network when using the sink mode. + + Returns: + int, tuple of the input index information. + + Examples: + >>> # dataset is an instance object of Dataset + >>> # set input_indexs + >>> dataset.input_indexs = 10 + >>> print(dataset.input_indexs) + 10 + """ + if self._input_indexs != (): + return self._input_indexs + + # find input_indexes of children + children_input_index = [child.input_indexs for child in self.children] + + # in case of more than one child, return the first input_indexes + for cix in children_input_index: + if cix != (): + return cix + + # if all children's input_indexes are () or the node is a leaf + return self._input_indexs + + @input_indexs.setter + def input_indexs(self, value): + self._input_indexs = value + + def copy_batch_size(self, value): + self._batch_size = value + + def _init_tree_getters(self): + """ + Get pipeline information. + """ + ir_tree, api_tree = self.create_ir_tree() + + runtime_context = cde.PythonRuntimeContext() + runtime_context.Init() + getter = cde.TreeGetters() + getter.Init(ir_tree) + runtime_context.AssignConsumer(getter) + return getter, runtime_context, api_tree + + def __init_size_getter(self): + """ + Get pipeline information. + """ + ir_tree, api_tree = self.create_ir_tree() + + runtime_context = cde.PythonRuntimeContext() + runtime_context.Init() + getter = cde.DatasetSizeGetters() + getter.Init(ir_tree) + runtime_context.AssignConsumer(getter) + return getter, runtime_context, api_tree + + def get_col_names(self): + """ + Return the names of the columns in dataset. + + Returns: + list, list of column names in the dataset. + + Examples: + >>> # dataset is an instance object of Dataset + >>> col_names = dataset.get_col_names() + """ + if self._col_names is None: + runtime_getter = self._init_tree_getters() + self._col_names = runtime_getter[0].GetColumnNames() + + return self._col_names + + @check_output_shape + def output_shapes(self, estimate=False): + """ + Get the shapes of output data. + + Args: + estimate (bool): If `estimate` is False, will return the shapes of first data row. + Otherwise, will iterate the whole dataset and return the estimated shapes of data row, + where dynamic shape is marked as None (used in dynamic data shapes scenario). Default: False. + + Returns: + list, list of shapes of each column. + + Examples: + >>> import numpy as np + >>> + >>> def generator1(): + ... for i in range(1, 100): + ... yield np.ones((16, i, 83)), np.array(i) + >>> + >>> dataset = ds.GeneratorDataset(generator1, ["data1", "data2"]) + >>> output_shapes = dataset.output_shapes() + """ + # cache single shape + if not estimate and self.saved_output_shapes is not None: + return self.saved_output_shapes + # cache estimate shape + if estimate and self.estimated_output_shapes is not None: + return self.estimated_output_shapes + + # if use set_dynamic_column, the `estimate` does not work, but they get the same result + if self.dynamic_setting[0]: + self.saved_output_shapes, self.saved_min_shapes, self.saved_max_shapes = self._dynamic_output_shapes() + return self.saved_output_shapes + + # We have a hang problem when two-level pipeline with multiprocessing, we need to extend the life cycle + # of runtime_context. We found this hang problem only occur on output_types and output_shapes. + runtime_getter = self._init_tree_getters() + self.runtime_context = runtime_getter[1] + api_tree = runtime_getter[2] + output_shapes = runtime_getter[0].GetOutputShapes(estimate) + del api_tree + del self.runtime_context + + if estimate: + self.estimated_output_shapes = output_shapes + else: + self.saved_output_shapes = output_shapes + return output_shapes + + def output_types(self): + """ + Get the types of output data. + + Returns: + list, list of data types. + + Examples: + >>> # dataset is an instance object of Dataset + >>> output_types = dataset.output_types() + """ + if self.saved_output_types is None: + runtime_getter = self._init_tree_getters() + # We have a hang problem when two-level pipeline with multiprocessing, we need to extend the life cycle + # of runtime_context. We found this hang problem only occur on output_types and output_shapes. + self.runtime_context = runtime_getter[1] + api_tree = runtime_getter[2] + self.saved_output_types = runtime_getter[0].GetOutputTypes() + del api_tree + del self.runtime_context + return self.saved_output_types + + def get_dataset_size(self): + """ + Return the number of batches in an epoch. + + Returns: + int, number of batches. + + Examples: + >>> # dataset is an instance object of Dataset + >>> dataset_size = dataset.get_dataset_size() + """ + if self.dataset_size is None: + runtime_getter = self.__init_size_getter() + self.dataset_size = runtime_getter[0].GetDatasetSize(False) + + return self.dataset_size + + @deprecated("1.5") + def set_dynamic_columns(self, columns=None): + """ + Set dynamic shape information of source data, it should be set after the pipeline is defined. + + Args: + columns (dict): A dict contains shape information of each column in dataset. + The value of shape[i] is :py:obj:`None` indicates that the data length of shape[i] is dynamic. + + Examples: + >>> import numpy as np + >>> + >>> def generator1(): + ... for i in range(1, 100): + ... yield np.ones((16, i, 83)), np.array(i) + >>> + >>> dataset = ds.GeneratorDataset(generator1, ["data1", "data2"]) + >>> dataset.set_dynamic_columns(columns={"data1": [16, None, 83], "data2": []}) + """ + if not isinstance(columns, dict): + raise TypeError("Pass a dict to set dynamic shape, example: {\"data1\": [16, None, 256]}") + self.dynamic_setting[0] = True + self.dynamic_setting[1] = columns + + def dynamic_min_max_shapes(self): + """ + Get minimum and maximum data length of dynamic source data, for dynamic graph compilation. + + Returns: + lists, min_shapes, max_shapes of source data. + + Examples: + >>> import numpy as np + >>> + >>> def generator1(): + ... for i in range(1, 100): + ... yield np.ones((16, i, 83)), np.array(i) + >>> + >>> dataset = ds.GeneratorDataset(generator1, ["data1", "data2"]) + >>> dataset.set_dynamic_columns(columns={"data1": [16, None, 83], "data2": []}) + >>> min_shapes, max_shapes = dataset.dynamic_min_max_shapes() + """ + if self.saved_min_shapes is None or self.saved_max_shapes is None: + self.saved_output_shapes, self.saved_min_shapes, self.saved_max_shapes = self._dynamic_output_shapes() + return self.saved_min_shapes, self.saved_max_shapes + + @staticmethod + def __check_dynamic_column_name(dynamic_columns, dataset_columns): + for column in dynamic_columns: + if column not in dataset_columns: + raise RuntimeError("dynamic column [" + column + "] does not match any column in dataset: " + + str(dataset_columns)) + + @staticmethod + def __check_dynamic_column_shape(data, col, dynamic_columns): + shape_mismatch = "dynamic column [" + col + "] with shape " + str(dynamic_columns[col]) + \ + " does not match dataset column [" + col + "] with shape " + str(list(data[col].shape)) + if data[col].ndim != len(dynamic_columns[col]): + raise RuntimeError(shape_mismatch) + for dim in range(len(dynamic_columns[col])): + if dynamic_columns[col][dim] is not None and dynamic_columns[col][dim] != data[col].shape[dim]: + raise RuntimeError(shape_mismatch) + + def _dynamic_output_shapes(self): + """ + Get dynamic information of source data. + + Returns: + lists, dynamic_shapes, min_shapes, max_shapes of source data. + """ + if not self.dynamic_setting[1]: + raise RuntimeError("dynamic_columns is not set, call set_dynamic_columns() by final Dataset Op.") + + if self.saved_output_shapes is not None and self.saved_min_shapes is not None and \ + self.saved_max_shapes is not None: + return self.saved_output_shapes, self.saved_min_shapes, self.saved_max_shapes + + logger.warning("Calculating dynamic shape of input data, this will take a few minutes...") + # Assume data1 shape is dynamic, data2 shape is fix + dynamic_columns = self.dynamic_setting[1] + # ["data1", "data2"] + dataset_columns = self.get_col_names() + Dataset.__check_dynamic_column_name(dynamic_columns, dataset_columns) + + # Shape[1] of data1 is variable + # {"data1": {(batch_size, 100, feat_len), (16, 200, 83)}, "data2": {(batch_size, feat_len)}} + column_shape_set = {col: set() for col in dataset_columns} + dataset_size_counter = 0 + for data in self.create_dict_iterator(num_epochs=1, output_numpy=True): + dataset_size_counter += 1 + for col in data.keys(): + if col in dynamic_columns: + Dataset.__check_dynamic_column_shape(data, col, dynamic_columns) + column_shape_set[col].add(tuple(data[col].shape)) + + # we get dataset_size after dryrun + self.dataset_size = dataset_size_counter + + min_shapes, max_shapes, dynamic_shapes = list(), list(), list() + for col, shape_set in column_shape_set.items(): + if len(shape_set) > 1: + if col not in dynamic_columns: + raise RuntimeError("column [" + col + "] has dynamic shape but not set by set_dynamic_columns()" + + ", shapes of [" + col + "]: " + str(list(shape_set))) + shape_npy = np.array(list(shape_set)) + max_shape = shape_npy.max(axis=0) + min_shape = shape_npy.min(axis=0) + + # Set min shape to 1 due to unknown shuffle + min_shape = np.where(np.equal(dynamic_columns[col], None), 1, min_shape) + # Set dynamic dim to -1 for ME + dynamic_shape = np.where(np.equal(dynamic_columns[col], None), -1, dynamic_columns[col]) + + max_shapes.append(max_shape.tolist()) + min_shapes.append(min_shape.tolist()) + dynamic_shapes.append(dynamic_shape.tolist()) + else: + # Also append fix shape to keep order of column shape + fix_shape = list(list(shape_set)[0]) + max_shapes.append(fix_shape) + min_shapes.append(fix_shape) + dynamic_shapes.append(fix_shape) + if col in dynamic_columns: + logger.warning("column [" + col + "] has no dynamic shape but set by set_dynamic_columns()") + # Set min shape to 1 due to unknown shuffle + min_shapes[-1] = np.where(np.equal(dynamic_columns[col], None), 1, fix_shape).tolist() + # Set dynamic dim to -1 for ME + dynamic_shapes[-1] = np.where(np.equal(dynamic_columns[col], None), -1, fix_shape).tolist() + return dynamic_shapes, min_shapes, max_shapes + + def num_classes(self): + """ + Get the number of classes in a dataset. + + Returns: + int, number of classes. + + Examples: + >>> # dataset is an instance object of Dataset + >>> num_classes = dataset.num_classes() + """ + if self._num_classes is None: + runtime_getter = self._init_tree_getters() + self._num_classes = runtime_getter[0].GetNumClasses() + + if self._num_classes == -1: + return None + return self._num_classes + + def get_sync_notifiers(self): + if self.children: + return self.children[0].get_sync_notifiers() + return {} + + def disable_sync(self): + if self.children: + return self.children[0].disable_sync() + return {} + + def is_sync(self): + if self.children: + return self.children[0].is_sync() + return False + + def sync_update(self, condition_name, num_batch=None, data=None): + """ + Release a blocking condition and trigger callback with given data. + + Args: + condition_name (str): The condition name that is used to toggle sending next row. + num_batch (Union[int, None]): The number of batches (rows) that are released. + When num_batch is None, it will default to the number specified by the + sync_wait operator (default=None). + data (Any): The data passed to the callback, user defined (default=None). + """ + if (not isinstance(num_batch, int) and num_batch is not None) or \ + (isinstance(num_batch, int) and num_batch <= 0): + # throwing exception, disable all sync_wait in pipeline + self.disable_sync() + raise RuntimeError("Sync_update batch size can only be positive integer, got : {}.".format(num_batch)) + notifiers_dict = self.get_sync_notifiers() + if not isinstance(condition_name, str): + raise TypeError("Argument condition_name with value {} is not of type str, but got {}." + .format(condition_name, type(condition_name))) + if condition_name not in notifiers_dict: + # throwing exception, disable all sync_wait in pipeline + self.disable_sync() + raise RuntimeError("Condition name not found.") + if num_batch is not None: + num_batch *= self.get_batch_size() + notifiers_dict[condition_name](num_batch, data) + + def get_batch_size(self): + """ + Return the size of batch. + + Returns: + int, the number of data in a batch. + + Examples: + >>> # dataset is an instance object of Dataset + >>> batch_size = dataset.get_batch_size() + """ + if self._batch_size is None: + runtime_getter = self._init_tree_getters() + self._batch_size = runtime_getter[0].GetBatchSize() + if self._batch_size is None: + self._batch_size = 1 + return self._batch_size + + def get_repeat_count(self): + """ + Get the replication times in RepeatDataset (default is 1). + + Returns: + int, the count of repeat. + + Examples: + >>> # dataset is an instance object of Dataset + >>> repeat_count = dataset.get_repeat_count() + """ + if self._repeat_count is None: + runtime_getter = self._init_tree_getters() + self._repeat_count = runtime_getter[0].GetRepeatCount() + if self._repeat_count is None: + self._repeat_count = 1 + return self._repeat_count + + def get_class_indexing(self): + """ + Return the class index. + + Returns: + dict, a str-to-int mapping from label name to index. + dict, a str-to-list mapping from label name to index for Coco ONLY. The second number + in the list is used to indicate the super category. + + Examples: + >>> # dataset is an instance object of Dataset + >>> class_indexing = dataset.get_class_indexing() + """ + if self.children: + return self.children[0].get_class_indexing() + return {} + + def reset(self): + """Reset the dataset for next epoch.""" + + def is_shuffled(self): + """Returns True if the dataset or its children is shuffled.""" + for input_dataset in self.children: + if input_dataset.is_shuffled(): + return True + + return False + + def is_sharded(self): + """Returns True if the dataset or its children is sharded.""" + for input_dataset in self.children: + if input_dataset.is_sharded(): + return True + + return False + + def parse(self, children=None): + raise NotImplementedError("Dataset has to implement parse method.") + + @staticmethod + def _update_data_shard(num_shards, shard_id): + """ + Update the shard number and shard id if necessary. + This is normally used in distributed training mode like Parameter Server training. + """ + # If this is in distributed execution mode, + # the shard number and shard id might need to be updated according to the process's rank or role. + if _is_role_pserver() and _enable_distributed_mindrt(): + num_shards = _get_ps_context("worker_num") + shard_id = 0 + return num_shards, shard_id + + def post_parse(self, ir_node): + if self.cache: + ir_node = ir_node.set_cache_client(self.cache.cache_client) + if self.num_parallel_workers: + ir_node = ir_node.set_num_workers(self.num_parallel_workers) + + return ir_node + + +class VisionBaseDataset(Dataset): + """ + Abstract class to represent a vision source dataset which produces content to the data pipeline. + """ + + def __init__(self, children=None, num_parallel_workers=None, cache=None): + super().__init__(children=children, num_parallel_workers=num_parallel_workers, cache=cache) + + def parse(self, children=None): + raise NotImplementedError("Dataset has to implement parse method.") + + +class TextBaseDataset(Dataset): + """ + Abstract class to represent a text source dataset which produces content to the data pipeline. + """ + + def __init__(self, children=None, num_parallel_workers=None, cache=None): + super().__init__(children=children, num_parallel_workers=num_parallel_workers, cache=cache) + + def parse(self, children=None): + raise NotImplementedError("Dataset has to implement parse method.") + + def build_vocab(self, columns, freq_range, top_k, special_tokens, special_first): + """ + Function to create a Vocab from source dataset. + Desired source dataset is a text type dataset. + + Build a vocab from a dataset. This would collect all the unique words in a dataset and return a vocab + which contains top_k most frequent words (if top_k is specified) + + Args: + + columns(Union[str, list[str]]): Column names to get words from. + freq_range(tuple[int]): A tuple of integers (min_frequency, max_frequency). Words within the frequency + range will be stored. + Naturally 0 <= min_frequency <= max_frequency <= total_words. min_frequency/max_frequency + can be set to default, which corresponds to 0/total_words separately. + top_k(int): Number of words to be built into vocab. top_k most frequent words are + taken. The top_k is taken after freq_range. If not enough top_k, all words will be taken + special_tokens(list[str]): A list of strings, each one is a special token. + special_first(bool): Whether special_tokens will be prepended/appended to vocab, If special_tokens + is specified and special_first is set to default, special_tokens will be prepended. + + Returns: + Vocab, vocab built from the dataset. + + Examples: + >>> import numpy as np + >>> + >>> def gen_corpus(): + ... # key: word, value: number of occurrences, reason for using letters is so their order is apparent + ... corpus = {"Z": 4, "Y": 4, "X": 4, "W": 3, "U": 3, "V": 2, "T": 1} + ... for k, v in corpus.items(): + ... yield (np.array([k] * v, dtype='S'),) + >>> column_names = ["column1"] + >>> dataset = ds.GeneratorDataset(gen_corpus, column_names) + >>> dataset = dataset.build_vocab(columns=["column1"], + ... freq_range=(1, 10), top_k=5, + ... special_tokens=["", ""], + ... special_first=True) + + """ + vocab = cde.Vocab() + columns = replace_none(columns, []) + if not isinstance(columns, list): + columns = [columns] + + freq_range = replace_none(freq_range, (0, 9223372036854775807)) + if freq_range[0] is None: + freq_range = (0, freq_range[1]) + if freq_range[1] is None: + freq_range = (freq_range[0], 9223372036854775807) + special_tokens = replace_none(special_tokens, []) + top_k = replace_none(top_k, 9223372036854775807) + + ir_tree, api_tree = self.create_ir_tree() + + # vocab node + vocab_node = cde.BuildVocabNode(ir_tree, vocab, columns, freq_range, top_k, special_tokens, special_first) + + runtime_context = cde.PythonRuntimeContext() + runtime_context.Init() + + # build vocab + consumer = cde.PythonBuildVocabConsumer() + consumer.Init(vocab_node) + runtime_context.AssignConsumer(consumer) + + consumer.Start() + del api_tree + + return vocab + + def build_sentencepiece_vocab(self, columns, vocab_size, character_coverage, model_type, params): + """ + Function to create a SentencePieceVocab from source dataset. + Desired source dataset is a text type dataset. + + Args: + + columns(list[str]): Column names to get words from. + vocab_size(int): Vocabulary size. + character_coverage(float): Percentage of characters covered by the model, must be between + 0.98 and 1.0 Good defaults are: 0.9995 for languages with rich character sets like + Japanese or Chinese character sets, and 1.0 for other languages with small character sets + like English or Latin. + model_type(SentencePieceModel): Model type. Choose from unigram (default), bpe, char, or word. + The input sentence must be pretokenized when using word type. + params(dict): Any extra optional parameters of sentencepiece library according to your raw data + + Returns: + SentencePieceVocab, vocab built from the dataset. + + Examples: + >>> from mindspore.dataset.text import SentencePieceModel + >>> + >>> # You can construct any text dataset as source, take TextFileDataset as example. + >>> dataset = ds.TextFileDataset("/path/to/sentence/piece/vocab/file", shuffle=False) + >>> dataset = dataset.build_sentencepiece_vocab(["text"], 5000, 0.9995, SentencePieceModel.UNIGRAM, {}) + """ + if not isinstance(model_type, SentencePieceModel): + raise TypeError("Argument model_type with value {0} is not of type SentencePieceModel, but got {1}." \ + .format(model_type, type(model_type))) + model_type = DE_C_INTER_SENTENCEPIECE_MODE[model_type] + vocab = cde.SentencePieceVocab() + + ir_tree, api_tree = self.create_ir_tree() + + # vocab node + vocab_node = cde.BuildSentenceVocabNode(ir_tree, vocab, columns, vocab_size, character_coverage, model_type, + params) + + runtime_context = cde.PythonRuntimeContext() + runtime_context.Init() + + # build vocab + consumer = cde.PythonBuildVocabConsumer() + consumer.Init(vocab_node) + runtime_context.AssignConsumer(consumer) + + consumer.Start() + del api_tree + + return vocab + + +class AudioBaseDataset(Dataset): + """ + Abstract class to represent a audio source dataset which produces content to the data pipeline. + """ + + def __init__(self, children=None, num_parallel_workers=None, cache=None): + super().__init__(children=children, num_parallel_workers=num_parallel_workers, cache=cache) + + def parse(self, children=None): + raise NotImplementedError("Dataset has to implement parse method.") + + +class UnionBaseDataset(VisionBaseDataset, TextBaseDataset, AudioBaseDataset): + """ + Abstract class to represent a union source dataset which produces content to the data pipeline. + """ + + def __init__(self, children=None, num_parallel_workers=None, cache=None): + super().__init__(children=children, num_parallel_workers=num_parallel_workers, cache=cache) + + def parse(self, children=None): + raise NotImplementedError("Dataset has to implement parse method.") + + +class SourceDataset(Dataset): + """ + Abstract class to represent a source dataset which produces content to the data pipeline. + """ + + def __init__(self, num_parallel_workers=None, num_samples=None, shuffle=True, num_shards=None, shard_id=None, + cache=None): + super().__init__(num_parallel_workers=num_parallel_workers, cache=cache) + self.num_samples = replace_none(num_samples, 0) + self.num_shards = replace_none(num_shards, 1) + self.shard_id = replace_none(shard_id, 0) + + if shuffle is not None and not isinstance(shuffle, (bool, Shuffle)): + raise TypeError("shuffle must be of boolean or enum of 'Shuffle' values like 'Shuffle.GLOBAL' or " + "'Shuffle.FILES' or 'Shuffle.INFILE'.") + + self.shuffle_flag = 2 # Global shuffle + if not isinstance(shuffle, Shuffle): + if shuffle is None or shuffle: + self.shuffle_flag = 2 # Global shuffle + else: + self.shuffle_flag = 0 # No shuffle + else: + if shuffle == Shuffle.GLOBAL: + self.shuffle_flag = 2 # Global shuffle + elif shuffle == Shuffle.FILES: + self.shuffle_flag = 1 # Files shuffle + elif shuffle == Shuffle.INFILE: + self.shuffle_flag = 3 # Infile shuffle + + def parse(self, children=None): + raise NotImplementedError("Dataset has to implement parse method.") + + @staticmethod + def _find_files(patterns): + """ + Utility function to search for files with the given glob patterns. + + Args: + patterns (Union[str, list[str]]): String or list of patterns to be searched. + + Returns: + list, list of files. + """ + + if not isinstance(patterns, list): + patterns = [patterns] + + file_list = [] + unmatched_patterns = [] + for pattern in patterns: + matches = [match for match in glob.glob(pattern, recursive=True) if os.path.isfile(match)] + + if matches: + file_list.extend(matches) + else: + unmatched_patterns.append(pattern) + + if unmatched_patterns: + raise ValueError("The following patterns did not match any files: {}.".format(unmatched_patterns)) + + if file_list: # not empty + return file_list + raise ValueError("The list of path names matching the patterns is empty.") + + def is_shuffled(self): + return self.shuffle_flag > 0 + + def is_sharded(self): + if self.num_shards is not None: + return self.num_shards > 1 + return False + + +class MappableDataset(SourceDataset): + """ + Abstract class to represent a source dataset which supports use of samplers. + """ + + def parse(self, children=None): + raise NotImplementedError("Dataset has to implement parse method.") + + def __init__(self, num_parallel_workers=None, sampler=None, num_samples=None, shuffle=None, num_shards=None, + shard_id=None, cache=None): + num_shards, shard_id = self._update_data_shard(num_shards, shard_id) + super().__init__(num_parallel_workers=num_parallel_workers, num_samples=num_samples, shuffle=shuffle, + num_shards=num_shards, shard_id=shard_id, cache=cache) + self.shuffle_flag = replace_none(shuffle, True) + self.sampler = samplers.select_sampler(num_samples, sampler, shuffle, num_shards, shard_id) + + def add_sampler(self, new_sampler): + """ + Add a child sampler for the current dataset. + + Args: + new_sampler (Sampler): The child sampler to be added. + + Examples: + >>> new_sampler = ds.DistributedSampler(10, 2) + >>> dataset.add_sampler(new_sampler) # dataset is an instance of Dataset + """ + # Note: By adding a sampler, the sampled IDs will flow to the new_sampler + # after first passing through the current samplers attached to this dataset. + self.dataset_size = None + new_sampler.add_child(self.sampler) + self.sampler = new_sampler + + def use_sampler(self, new_sampler): + """ + Replace the last child sampler of the current dataset, remaining the parent sampler unchanged. + + Args: + new_sampler (Sampler): The new sampler to replace with. + + Examples: + >>> # dataset is an instance object of Dataset + >>> # use a DistributedSampler instead + >>> new_sampler = ds.DistributedSampler(10, 2) + >>> dataset.use_sampler(new_sampler) + """ + if new_sampler is None: + raise TypeError("Input sampler can not be None.") + if not isinstance(new_sampler, (samplers.BuiltinSampler, samplers.Sampler)): + raise TypeError("Input sampler is not an instance of a sampler.") + self.dataset_size = None + + self.sampler = self.sampler.child_sampler + self.add_sampler(new_sampler) + + def is_shuffled(self): + return self.sampler.is_shuffled() + + def is_sharded(self): + return self.sampler.is_sharded() + + @check_split + def split(self, sizes, randomize=True): + """ + Split the dataset into smaller, non-overlapping datasets. + + Args: + sizes (Union[list[int], list[float]]): If a list of integers [s1, s2, 鈥, sn] is + provided, the dataset will be split into n datasets of size s1, size s2, 鈥, size sn + respectively. If the sum of all sizes does not equal the original dataset size, an + error will occur. + If a list of floats [f1, f2, 鈥, fn] is provided, all floats must be between 0 and 1 + and must sum to 1, otherwise an error will occur. The dataset will be split into n + Datasets of size round(f1*K), round(f2*K), 鈥, round(fn*K) where K is the size of the + original dataset. + If after rounding: + + - Any size equals 0, an error will occur. + - The sum of split sizes < K, the difference will be added to the first split. + - The sum of split sizes > K, the difference will be removed from the first large + enough split such that it will have at least 1 row after removing the difference. + + randomize (bool, optional): Determines whether or not to split the data randomly (default=True). + If True, the data will be randomly split. Otherwise, each split will be created with + consecutive rows from the dataset. + + Note: + 1. There is an optimized split function, which will be called automatically when the dataset + that calls this function is a MappableDataset. + 2. Dataset should not be sharded if split is going to be called. Instead, create a + DistributedSampler and specify a split to shard after splitting. If the dataset is + sharded after a split, it is strongly recommended setting the same seed in each instance + of execution, otherwise each shard may not be part of the same split (see Examples). + 3. It is strongly recommended to not shuffle the dataset, but use randomize=True instead. + Shuffling the dataset may not be deterministic, which means the data in each split + will be different in each epoch. Furthermore, if sharding occurs after split, each + shard may not be part of the same split. + + Raises: + RuntimeError: If get_dataset_size returns None or is not supported for this dataset. + RuntimeError: If `sizes` is list of integers and sum of all elements in sizes does not + equal the dataset size. + RuntimeError: If `sizes` is list of float and there is a split with size 0 after calculations. + RuntimeError: If the dataset is sharded prior to calling split. + ValueError: If `sizes` is list of float and not all floats are between 0 and 1, or if the + floats don't sum to 1. + + Returns: + tuple(Dataset), a tuple of datasets that have been split. + + Examples: + >>> # Since many datasets have shuffle on by default, set shuffle to False if split will be called! + >>> dataset = ds.ImageFolderDataset(image_folder_dataset_dir, shuffle=False) + >>> + >>> # Set the seed, and tell split to use this seed when randomizing. + >>> # This is needed because sharding will be done later + >>> ds.config.set_seed(58) + >>> train_dataset, test_dataset = dataset.split([0.9, 0.1]) + >>> + >>> # To shard the train dataset, use a DistributedSampler + >>> train_sampler = ds.DistributedSampler(10, 2) + >>> train_dataset.use_sampler(train_sampler) + """ + if self.is_shuffled(): + logger.warning("Dataset is shuffled before split.") + + if self.is_sharded(): + raise RuntimeError("Dataset should not be sharded before split.") + + absolute_sizes = self._get_absolute_split_sizes(sizes) + splits = [] + current_split_start_index = 0 + for size in absolute_sizes: + ds = copy.deepcopy(self) + ds.dataset_size = None + if randomize: + # want to shuffle the same way every epoch before split, we are assuming + # that the user will call set_seed + random_sampler = samplers.RandomSampler() + random_sampler.reshuffle_each_epoch = False + ds.add_sampler(random_sampler) + + subset_sampler = samplers.SequentialSampler(current_split_start_index, size) + ds.add_sampler(subset_sampler) + + # add sequential sampler, so that if user calls use_sampler, we will + # get rid of the sequential sampler instead of something we need + ds.add_sampler(samplers.SequentialSampler()) + + splits.append(ds) + + current_split_start_index += size + + return tuple(splits) + + +class BucketBatchByLengthDataset(UnionBaseDataset): + """ + The result of applying BucketBatchByLength operator to the input dataset. + """ + + def __init__(self, input_dataset, column_names, bucket_boundaries, bucket_batch_sizes, element_length_function, + pad_info, pad_to_bucket_boundary, drop_remainder): + super().__init__(children=input_dataset) + + self.column_names = to_list(column_names) + self.bucket_boundaries = replace_none(bucket_boundaries, []) + self.bucket_batch_sizes = replace_none(bucket_batch_sizes, []) + self.element_length_function = element_length_function + self.pad_info = replace_none(pad_info, {}) + self.pad_to_bucket_boundary = replace_none(pad_to_bucket_boundary, False) + self.drop_remainder = replace_none(drop_remainder, False) + + def parse(self, children=None): + return cde.BucketBatchByLengthNode(children[0], self.column_names, self.bucket_boundaries, + self.bucket_batch_sizes, self.element_length_function, self.pad_info, + self.pad_to_bucket_boundary, self.drop_remainder) + + +def _check_shm_usage(num_worker, queue_size, max_rowsize, num_queues=1): + """ + Check sufficient shared memory is available for shared memory queues + when training in parallel mode. + """ + threshold_ratio = 0.8 + if platform.system().lower() not in {"windows", "darwin"}: + device_num = _get_device_num() + # In the cluster, _get_device_num indicates the number of the entire cluster. The maximum number of cards + # on the ascend server is 8. + if device_num > 1 and context.get_context("device_target") == "Ascend": + device_num = min(device_num, 8) + shm_estimate_usage = device_num * num_worker * num_queues * \ + (queue_size + 2) * max_rowsize * 1024 * 1024 + try: + shm_available = psutil.disk_usage('/dev/shm').free + if shm_estimate_usage >= threshold_ratio * shm_available: + raise RuntimeError( + "Insufficient shared memory available. Required: {}, Available: {}. " + "The required memory can't exceed 80% of the available shared memory, " + "it's recommended to reduce memory usage by following methods:\n" + "1. reduce value of parameter max_rowsize or num_parallel_workers.\n" + "2. reduce prefetch size by set_prefetch_size().\n" + "3. disable shared memory by set_enable_shared_mem().".format(shm_estimate_usage, shm_available)) + except FileNotFoundError: + raise RuntimeError("Expected /dev/shm to exist.") + + +class BatchDataset(UnionBaseDataset): + """ + The result of applying Batch operator to the input dataset. + + Args: + input_dataset (Dataset): Input Dataset to be batched. + batch_size (Union[int, function]): The number of rows each batch is created with. An + int or callable which takes exactly 1 parameter, BatchInfo. + drop_remainder (bool, optional): Determines whether or not to drop the last + possibly incomplete batch (default=False). If True, and if there are less + than batch_size rows available to make the last batch, then those rows will + be dropped and not propagated to the child node. + num_parallel_workers (int, optional): Number of workers to process the dataset in parallel (default=None). + per_batch_map (callable, optional): Per batch map callable. A callable which takes + (list[Tensor], list[Tensor], ..., BatchInfo) as input parameters. Each list[Tensor] represents a batch of + Tensors on a given column. The number of lists should match with number of entries in input_columns. The + last parameter of the callable must always be a BatchInfo object. + input_columns (Union[str, list[str]], optional): List of names of the input columns. The size of the list must + match with signature of per_batch_map callable. + output_columns (Union[str, list[str]], optional): List of names assigned to the columns outputted by + the last operation. This parameter is mandatory if len(input_columns) != + len(output_columns). The size of this list must match the number of output + columns of the last operation. (default=None, output columns will have the same + name as the input columns, i.e., the columns will be replaced). + column_order (Union[str, list[str]], optional): Specifies the list of all the columns you need in the whole + dataset. The parameter is required when len(input_column) != len(output_column). Caution: the list here + is not just the columns specified in parameter input_columns and output_columns. + pad_info (dict, optional): Whether to perform padding on selected columns. pad_info={"col1":([224,224],0)} + will pad column with name "col1" to a tensor of size [224,224] and fill the missing with 0. + max_rowsize(int, optional): Maximum size of row in MB that is used for shared memory allocation to copy + data between processes. This is only used if python_multiprocessing is set to True (default=16). + + """ + + def __init__(self, input_dataset, batch_size, drop_remainder=False, num_parallel_workers=None, per_batch_map=None, + input_columns=None, output_columns=None, column_order=None, pad_info=None, + python_multiprocessing=False, max_rowsize=16): + super().__init__(children=input_dataset, num_parallel_workers=num_parallel_workers) + + if BatchDataset._is_ancestor_of_repeat(input_dataset): + logger.warning("Repeat is located before batch, data from two epochs can be batched together.") + + BatchDataset._update_batch_size_for_syncwait(input_dataset, batch_size) + + # if batch_size is callable, set batch_size to 1 and batch_size_func to that callable function + self.batch_size = batch_size if not callable(batch_size) else 1 + self.batch_size_func = None if not callable(batch_size) else batch_size + + self.drop_remainder = replace_none(drop_remainder, False) + + self.per_batch_map = per_batch_map + + self.input_columns = to_list(input_columns) + self.output_columns = to_list(output_columns) + self.column_order = to_list(column_order) + + self.pad = bool(pad_info is not None) + self.pad_info = replace_none(pad_info, dict()) + + self.python_multiprocessing = python_multiprocessing + self.process_pool = None + self.max_rowsize = max_rowsize + + def __del__(self): + if hasattr(self, "process_pool") and self.process_pool is not None: + self.process_pool.terminate() + del self.process_pool + + def parse(self, children=None): + return cde.BatchNode(children[0], self.batch_size, self.drop_remainder, self.pad, self.input_columns, + self.output_columns, self.column_order, self.batch_size_func, self.per_batch_map, + self.pad_info, self.process_pool) + + @staticmethod + def _is_ancestor_of_repeat(dataset): + """ + Utility function to find the case where repeat is used before batch. + + Args: + dataset (Dataset): Dataset to be checked. + + Returns: + bool, whether repeat is used before batch. + """ + if isinstance(dataset, RepeatDataset): + return True + flag = False + for input_dataset in dataset.children: + flag = flag | BatchDataset._is_ancestor_of_repeat(input_dataset) + return flag + + @staticmethod + def _update_batch_size_for_syncwait(dataset, batch_size): + """ + Utility function to notify batch size to sync_wait. + + Args: + dataset (Dataset): Dataset to be checked. + batch_size (int): batch size to notify. + """ + if isinstance(dataset, SyncWaitDataset): + dataset.update_sync_batch_size(batch_size) + for input_dataset in dataset.children: + BatchDataset._update_batch_size_for_syncwait(input_dataset, batch_size) + + def __deepcopy__(self, memodict): + return self.__safe_deepcopy__(memodict, exclude=("per_batch_map", "batch_size_func", "__transfer_dataset__")) + + # Iterator bootstrap will be called on iterator construction. + # A deep copy of Dataset object is created prior of iterator_bootstrap. + # This method will create per iterator process pool and bind pyfunc execution to the pool. + def iterator_bootstrap(self): + """ + Per iterator bootstrap callback. + """ + if self.python_multiprocessing and platform.system().lower() == 'windows': + logger.warning("Python multiprocessing is not supported on Windows platform.") + if self.python_multiprocessing and platform.system().lower() != 'windows': + if self.per_batch_map is None: + logger.warning("per_batch_map is None so python_multiprocessing is ignored for batch.") + return + + # If user didn't specify num_parallel_workers, set it to default + if self.num_parallel_workers is None: + self.num_parallel_workers = get_num_parallel_workers() + + self.process_pool = _PythonMultiprocessing(str(self), self.num_parallel_workers, [self.per_batch_map], + self.max_rowsize * self.batch_size) + # Wrap per_batch_map into _PythonCallable + self.per_batch_map = _PythonCallable(self.per_batch_map, 0, self.process_pool) + else: + if self.per_batch_map is not None: + self.per_batch_map = FuncWrapper(self.per_batch_map) + + +class BatchInfo(cde.CBatchInfo): + """ + Only the batch size function and per_batch_map of the batch operator can dynamically adjust parameters + based on the number of batches and epochs during training. + """ + + def get_batch_num(self): + """ + Return the batch number of the current batch. + """ + return + + def get_epoch_num(self): + """ + Return the epoch number of the current batch. + """ + return + + +class BlockReleasePair: + """ + The blocking condition class used by SyncWaitDataset. + + Args: + init_release_rows (int): Number of lines to allow through the pipeline. + callback (function): The callback function that will be called when release is called (default=None). + """ + + def __init__(self, init_release_rows, callback=None): + if isinstance(init_release_rows, int) and init_release_rows <= 0: + raise ValueError("release_rows need to be greater than 0.") + self.row_count = -init_release_rows + self.cv = threading.Condition() + self.callback = callback + self.default_rows = init_release_rows + self.disable = False + + def __deepcopy__(self, memodict): + return self + + def reset(self): + with self.cv: + self.row_count = -self.default_rows + self.cv.notify_all() + + def update_batched_size(self, batch_size): + # sanity check + if isinstance(batch_size, int) and batch_size <= 0: + raise ValueError("batch_size need to be greater than 0.") + + # should only use before the pipeline creates + self.row_count *= batch_size + self.default_rows *= batch_size + + def block_func(self): + """ + Function for handing blocking condition. + + Returns: + bool, True. + """ + with self.cv: + # if disable is true, the always evaluate to true + not_time_out = self.cv.wait_for(lambda: (self.row_count < 0 or self.disable), + timeout=get_callback_timeout()) + # time_out will be False if time out occurs + if not not_time_out: + logger.warning("Timeout happened in sync_wait, maybe dataset.sync_update(condition=...) " + "is not added after dataset.create_dict_iterator(...), now disabling lock.") + self.disable = True + self.row_count += 1 + return True + + def release_func(self, pass_rows=None, data=None): + with self.cv: + if pass_rows is None: + pass_rows = self.default_rows + self.row_count -= pass_rows + if self.callback is not None: + self.callback(data) + self.cv.notify_all() + + def disable_lock(self): + with self.cv: + self.disable = True + self.cv.notify_all() + + +class SyncWaitDataset(UnionBaseDataset): + """ + The result of adding a blocking condition to the input Dataset. + + Args: + input_dataset (Dataset): Input dataset to apply flow control. + num_batch (int): Number of batches without blocking at the start of each epoch. + condition_name (str): Condition name that is used to toggle sending next row. + callback (function): Callback function that will be invoked when sync_update is called (default=None). + + Raises: + RuntimeError: If condition name already exists. + """ + + def __init__(self, input_dataset, condition_name, num_batch, callback=None): + super().__init__(children=input_dataset) + + # set to the default value, waiting for the batch to update it + self._condition_name = condition_name + if isinstance(num_batch, int) and num_batch <= 0: + raise ValueError("num_batch need to be greater than 0.") + + self._pair = BlockReleasePair(num_batch, callback) + if self._condition_name in self.children[0].get_sync_notifiers(): + raise RuntimeError("Condition name is already in use.") + logger.info("Please remember to add dataset.sync_update(condition=%s), otherwise hanging will result. " + "If dataset.sync_update(condition=%s) has already been added, you can ignore the info.", + condition_name, condition_name) + + def parse(self, children=None): + return cde.SyncWaitNode(children[0], self._condition_name, self._pair.block_func) + + def get_sync_notifiers(self): + return {**self.children[0].get_sync_notifiers(), **{self._condition_name: self._pair.release_func}} + + def is_sync(self): + return True + + def update_sync_batch_size(self, batch_size): + if isinstance(batch_size, int) and batch_size <= 0: + raise ValueError("num_batch need to be greater than 0.") + self._pair.update_batched_size(batch_size) + + def disable_sync(self): + logger.info("Disabling Sync") + self._pair.disable_lock() + + @staticmethod + def _is_ancestor_of_batch(dataset): + """ + Utility function to find the case where sync_wait is used before batch. + + Args: + dataset (Dataset): Dataset to be checked. + + Returns: + bool, whether sync_wait is used before batch. + """ + if isinstance(dataset, BatchDataset): + return True + flag = False + for input_dataset in dataset.children: + flag = flag | SyncWaitDataset._is_ancestor_of_batch(input_dataset) + return flag + + def iterator_bootstrap(self): + self._pair.reset() + + +class ShuffleDataset(UnionBaseDataset): + """ + The result of applying Shuffle operator to the input Dataset. + + Args: + input_dataset (Dataset): Input Dataset to be shuffled. + buffer_size (int): Size of the buffer. + + Raises: + RuntimeError: If exist sync operators before shuffle. + """ + + def __init__(self, input_dataset, buffer_size): + super().__init__(children=input_dataset) + self.buffer_size = buffer_size + self.reshuffle_each_epoch = True + + if self.is_sync(): + raise RuntimeError("No shuffle after sync operators.") + + def parse(self, children=None): + return cde.ShuffleNode(children[0], self.buffer_size, self.reshuffle_each_epoch) + + def is_shuffled(self): + return True + + +# Pyfunc collection for multiprocess pyfunc +# This global variable will only be used within subprocesses +_OP_NAME = dict() +_OP_PROCESS = dict() + + +# PythonCallable wrapper for multiprocess pyfunc +class _PythonCallable: + """ + Internal Python function wrapper for multiprocessing pyfunc. + """ + + def __init__(self, py_callable, idx, pool=None): + # Original Python callable from user. + self.py_callable = py_callable + # Process pool created for current iterator. + self.pool = pool + # Python callable index + self.idx = idx + + def __call__(self, *args): + result = None + if self.pool.is_running() and check_iterator_cleanup() is False: + try: + result = self.pool.execute(self.idx, *args) + except multiprocessing.TimeoutError: + pass + if result is None: + # Invoke original Python callable in master process in case the pool is gone. + result = self.py_callable(*args) + return result + + def to_json(self): + return self.py_callable.to_json() + + +class Pipe: + """ + Class to handle communication between the master process and the worker processes. + """ + + def __init__(self, warning_ctl, shared_memory=False, max_rowsize=16): + self.shared_memory = shared_memory + self.eof = multiprocessing.Event() + if self.shared_memory: + self.in_queue = _SharedQueue(1, warning_ctl, max_rowsize=max_rowsize) + self.res_queue = _SharedQueue(1, warning_ctl, max_rowsize=max_rowsize) + else: + self.in_queue = _Queue(1) + self.res_queue = _Queue(1) + self.in_queue._joincancelled = True # pylint: disable=W0212 + self.res_queue._joincancelled = True # pylint: disable=W0212 + + def master_send(self, func_index, data): + self.in_queue.put_nowait((func_index, *data)) + + def master_receive(self): + return self.res_queue.get_until(timeout=1, exit_signal=self.eof) + + def master_close(self): + self.eof.set() + self.res_queue.cancel_join_thread() + self.in_queue.cancel_join_thread() + + def worker_send(self, data): + self.res_queue.put_until(data, timeout=1, exit_signal=self.eof) + + def worker_receive(self): + result = self.in_queue.get_until(timeout=1, exit_signal=self.eof) + if result is None: + return result + if len(result) == 1: + raise RuntimeError(f"Corrupted data. Worker received {len(result)} elements, it should be more than 1.") + func_index, *data = result + return func_index, tuple(data) + + def worker_close(self): + self.res_queue.cancel_join_thread() + self.in_queue.cancel_join_thread() + + +def _main_process_already_exit(): + """ + Judge whether main process already exit. + """ + ppid = os.getppid() + + if (platform.system().lower() != 'windows' and + not _PythonMultiprocessing.is_process_alive(ppid)): + return True + return False + + +def _worker_loop(operations, pipe): + """ + Multiprocess worker process loop. + """ + + def _ignore_sigint(): + """ + We need to ignore sigint signal here so subprocesses can exit normally and clear. + """ + signal.signal(signal.SIGINT, signal.SIG_IGN) + + while not _main_process_already_exit(): + _ignore_sigint() + + result = pipe.worker_receive() + if result is None: + pipe.worker_close() + return + (idx, input_tensors) = result + try: + output_tensors = operations[idx](*input_tensors) + + pipe.worker_send(output_tensors) + except Exception: + pipe.worker_send(ExceptionHandler(where="in map(or batch) worker and execute Python function")) + return + + +def worker_target(operations): + return lambda pipe: _worker_loop(operations, pipe) + + +class _MPWorker(multiprocessing.Process): + """ + Worker process for multiprocessing. + """ + + def __init__(self, operations, warning_ctl, max_rowsize=16): + shared_memory = get_enable_shared_mem() + self.pipe = Pipe(warning_ctl, shared_memory=shared_memory, max_rowsize=max_rowsize) + super().__init__(target=worker_target(operations), args=(self.pipe,), daemon=True) + + def execute(self, idx, *args): + self.pipe.master_send(idx, args) + res = self.pipe.master_receive() + if isinstance(res, ExceptionHandler): + res.reraise() + return res + + def close(self): + try: + if self.is_alive(): + logger.info(f"Closing worker with PID: {self.pid}") + self.pipe.master_close() + super().terminate() + super().join() + super().close() + + except ValueError: + # Process has been closed already + return + return + + def is_alive(self): + try: + return super().is_alive() + except ValueError: + return False + + +class _PythonMultiprocessing(cde.PythonMultiprocessingRuntime): + """ + A wrapper to multiprocessing.pool that performs cleanup and ensure proper termination of forked processes. + """ + + class _ExceptHookHandler: + """ + Internal class ExceptionHandler + """ + + def __init__(self): + sys.excepthook = self.__handler_exception + + @staticmethod + def mp_pool_exit_preprocess(): + if check_iterator_cleanup() is False: + # Set the iterator_cleanup flag to True before exiting, and wait 3s for all apply_async + # applied to the multiprocessing task to prevent multiprocessing from hang when exiting + _set_iterator_cleanup() + time.sleep(3) + + def __handler_exception(self, ex_type, value, tb): + logger.critical("Uncaught exception: ", exc_info=(ex_type, value, tb)) + self.mp_pool_exit_preprocess() + + def __init__(self, op_name, num_parallel_workers, operations, max_row_size=16): + super(_PythonMultiprocessing, self).__init__() + self.op_name = op_name + self.num_parallel_workers = num_parallel_workers + self.operations = operations + self.max_row_size = max_row_size + + self.workers = None + self.pids = None + self.op_id = -1 + + self.queues_map = {} + self.next_queue = 0 + + self.eot = None + self.watch_dog = None + self.ppid = os.getpid() + self.hook = None + self.warning_ctl = None + self.threads_to_workers = {} + + def __del__(self): + try: + self.terminate() + except TypeError: + pass + + # This wait function is for cleaning zombie subprocesses + @staticmethod + def wait_pid(): + """ + This function is used by the main process to release subprocess resources. + """ + try: + while True: + child_pid, _ = os.waitpid(-1, os.WNOHANG) + if child_pid == 0: + break + except OSError: + # waitpid may be failed for some reasons so we ignore this error + pass + + # Dataset need watch_dog thread to monitoring fork multi-processing, + # and thread can't be a member function otherwise python won't collect and release resources. + @staticmethod + def _watch_dog(eot, workers): + """ + This thread is for monitoring subprocesses forked by GeneratorDataset/map/batch + """ + if not isinstance(workers, list): + raise TypeError("[Internal Error] The 2nd parameter of watch dog thread should be list of process, " + "but got {}.".format(type(workers))) + + while not eot.is_set(): + # Monitoring and count how many subprocesses already exit + clear_subprocess_timeout = _PythonMultiprocessing._monitor_subprocess_exit(workers) + # If find subprocess exit, we will wait for 30s and do some waitpid operations + if clear_subprocess_timeout > 0: + start = time.time() + while time.time() - start < clear_subprocess_timeout: + # We need to distinguishing get_dataset_size or train finished normally and hang scenario. + # If get_dataset_size or train finished normally, _stop_subprocess can be execute and + # self.need_abort can be set to True. If main process is hang in get(), self.need_abort + # will never set to True, then we wait for 30s and kill main process + if eot.is_set(): + return + # Sometimes subprocess may be zombie, so in 30s we can wait and do some useful tasks(waitpid). + _PythonMultiprocessing.wait_pid() + # multiprocessing.queue may hang in .get() forever when put() process was killed. + # We have to exit main process otherwise main process will hang. + _PythonMultiprocessing._terminate_processes(workers) + logger.critical("The subprocess of dataset may exit unexpected or be killed, " + "main process will exit. If this is not an artificial operation, you can use " + "ds.config.set_enable_watchdog(False) to block this error.") + os.kill(os.getpid(), signal.SIGTERM) + + @staticmethod + def _terminate_processes(processes): + """Terminate subprocesses""" + + for p in processes: + try: + if p.exitcode is None: + p.terminate() + except Exception: # pylint: disable=broad-except + # process has been closed already + pass + for p in processes: + if p._closed is False: # pylint: disable=W0212 + # We don't use w.join because join can only used in main process or join will raise an error. + p._popen.wait() # pylint: disable=W0212 + + # Monitor the exit number of subprocesses + @staticmethod + def _monitor_subprocess_exit(workers): + """ + To monitor whether process is exit. + + Args: + workers (list of multiprocessing.Process): multiprocessing.Process. + + Returns: + int, the timeout(in seconds) when process exit. + """ + for w in workers: + try: + exit_code = w.exitcode + if exit_code is not None: + # For kill -9, we can exit quickly + if exit_code == -9: + return 1 + # For kill -15, we still exit after 30s + if exit_code == -15: + return 30 + except ValueError: + # process has been closed already + return 0 + return 0 + + @staticmethod + def is_process_alive(pid): + """ + Check if the process is alive or not. + Note: We hit a deadlock when we use psutil or w.exitcode to check whether a process is alive. + Instead we use os.kill(ppid, 0). + + Args: + pid: pid of the process to be checked + + Returns: + True if the process is alive + """ + + try: + os.kill(pid, 0) + except OSError: + return False + return True + + # When main process exit, subprocesses will be terminate + @staticmethod + def _clean_process(ppid, workers): + """ + This is the execute function of clean process, if we found main process exited, we will clean subprocesses. + + Args: + ppid: The process id of main process. + workers: The list of subprocesses. + + """ + signal.signal(signal.SIGINT, signal.SIG_IGN) + while _PythonMultiprocessing.is_process_alive(ppid): + time.sleep(0.1) + + _PythonMultiprocessing._terminate_processes(workers) + os.kill(os.getpid(), signal.SIGTERM) + + def launch(self, op_id=-1): + self.op_id = op_id + logger.info("Launching new Python Multiprocessing pool for Op:" + str(self.op_id)) + self.create_pool() + + def create_pool(self): + """ + + Returns: + + """ + if get_enable_shared_mem(): + self.check_shared_memory() + + if self.workers is not None: + raise Exception("Pool was already created, close it first.") + + # Let gc collect unreferenced memory to avoid child processes in the pool to do it + gc.collect() + + # Construct python worker processes + self.workers = [] + self.warning_ctl = multiprocessing.Value('i', 0) + for _ in range(self.num_parallel_workers): + worker = _MPWorker(self.operations, self.warning_ctl, self.max_row_size) + worker.start() + self.workers.append(worker) + + logger.info("Op: " + str(self.op_id) + " Python multiprocessing pool workers' PIDs: " + str(self.get_pids())) + + self.hook = _PythonMultiprocessing._ExceptHookHandler() + + # The op (Map, Batch, etc) multiprocessing will launch a watch dog thread for monitoring sub processes + self._launch_watch_dog() + + atexit.register(self.terminate) + + def terminate(self): + logger.info("Terminating Python Multiprocessing for Op:" + str(self.op_id)) + self.close_all_workers() + self.abort_watchdog() + + def get_pids(self): + """ + Get list of worker's PIDs + + Returns: + list of strings + """ + if not self.is_mp_enabled: + return [] + if not self.pids: + self.pids = [] + if self.workers: + for w in self.workers: + try: + self.pids.append(w.pid) + except ValueError: + continue + return self.pids + + def add_new_workers(self, num_new_workers): + logger.info( + "Increasing num_parallel_workers of Python Multiprocessing pool for Op:" + str(self.op_id) + + ", old num_workers=" + str(self.num_parallel_workers) + " new num_workers=" + str( + self.num_parallel_workers + + num_new_workers) + ".") + self.terminate() + self.num_parallel_workers += num_new_workers + self.launch(self.op_id) + + def remove_workers(self, num_removed_workers): + logger.info( + "Decreasing num_parallel_workers of Python Multiprocessing pool for Op:" + str(self.op_id) + + ", old num_workers=" + str(self.num_parallel_workers) + " new num_workers=" + str( + self.num_parallel_workers - + num_removed_workers) + ".") + self.terminate() + self.num_parallel_workers -= num_removed_workers + self.launch(self.op_id) + + def is_mp_enabled(self): + return self.workers is not None + + def check_shared_memory(self): + """ + Check if there is enough shared memory in the system. + """ + _check_shm_usage(self.num_parallel_workers, 1, self.max_row_size, 2) + + def execute(self, idx, *args): + """ + Execute + """ + t_id = threading.get_ident() + worker_id = self.threads_to_workers.setdefault(t_id, len(self.threads_to_workers)) + + # todo check_iterator_cleanup + if self.is_running() and check_iterator_cleanup() is False: + return self.workers[worker_id].execute(idx, *args) + + return None + + def _launch_watch_dog(self): + """ + We will launch a watchdog thread and a clean process to cleaning subprocess when there is process was killed. + The watchdog thread will cleanup subprocesses and main process when one of the subprocesses was killed. + The cleaning subprocess will cleanup subprocesses when main process was killed. + """ + if platform.system().lower() != 'windows': + self.cleaning_process = multiprocessing.Process(target=self._clean_process, + args=(self.ppid, self.workers), + daemon=True) + self.cleaning_process.start() + + if get_enable_watchdog(): + self.eot = threading.Event() + self.watch_dog = threading.Thread(target=self._watch_dog, + args=(self.eot, self.workers + [self.cleaning_process]), + daemon=True) + self.watch_dog.start() + + def _abort_watchdog(self): + if not self.eot.is_set(): + self.eot.set() + + def abort_watchdog(self): + if hasattr(self, 'watch_dog') and self.watch_dog is not None and hasattr(self, 'eot') and self.eot is not None: + self._abort_watchdog() + if hasattr(self, 'cleaning_process') and self.cleaning_process is not None: + _PythonMultiprocessing._terminate_processes([self.cleaning_process]) + + def is_running(self): + if hasattr(self, 'workers') and self.workers is not None: + return all([w.is_alive() for w in self.workers]) + return False + + def close_all_workers(self): + if hasattr(self, 'workers') and self.workers is not None: + for w in self.workers: + w.close() + self.workers = None + self.pids = None + + +class MapDataset(UnionBaseDataset): + """ + The result of applying the Map operator to the input Dataset. + + Args: + input_dataset (Dataset): Input Dataset to be mapped. + operations (Union[list[TensorOperation], list[functions]]): A function mapping a nested structure of tensors + to another nested structure of tensor (default=None). + input_columns (Union[str, list[str]]): List of names of the input columns + (default=None, the operations will be applied on the first columns in the dataset). + The size of the list should match the number of inputs of the first operator. + output_columns (Union[str, list[str]], optional): List of names of the output columns. + The size of the list should match the number of outputs of the last operator + (default=None, output columns will be the input columns, i.e., the columns will + be replaced). + column_order (list[str], optional): Specifies the list of all the columns you need in the whole + dataset. The parameter is required when len(input_column) != len(output_column). Caution: the list here + is not just the columns specified in parameter input_columns and output_columns. + num_parallel_workers (int, optional): Number of workers to process the dataset + in parallel (default=None). + python_multiprocessing (bool, optional): Parallelize Python operations with multiple worker process. This + option could be beneficial if the Python operation is computational heavy (default=False). + cache (DatasetCache, optional): Use tensor caching service to speed up dataset processing. + (default=None, which means no cache is used). + callbacks (DSCallback, list[DSCallback], optional): List of Dataset callbacks to be called (Default=None) + max_rowsize(int, optional): Maximum size of row in MB that is used for shared memory allocation to copy + data between processes. This is only used if python_multiprocessing is set to True (default=16). + offload (bool, optional): Flag to indicate whether offload is used (Default=None). + + Raises: + ValueError: If len(input_columns) != len(output_columns) and column_order is not specified. + """ + + def __init__(self, input_dataset, operations=None, input_columns=None, output_columns=None, column_order=None, + num_parallel_workers=None, python_multiprocessing=False, cache=None, callbacks=None, max_rowsize=16, + offload=None): + super().__init__(children=input_dataset, num_parallel_workers=num_parallel_workers, cache=cache) + self.operations = to_list(operations) + for op in self.operations: + # user define c_vision.HWC2CHW without parentheses is error + if type(op) == type: # pylint: disable=unidiomatic-typecheck + raise ValueError("Parameter operations's element of method map should be a dataset processing " + "operation instance, but got: {}. It may be missing parentheses for " + "instantiation.".format(op)) + if not isinstance(op, (c_transforms.TensorOperation, py_transforms.PyTensorOperation)) \ + and not callable(op): + raise ValueError("Parameter operations's element of method map should be a python function or " + "class method which should be callable, but got: {}. It doesn't need parentheses " + "for python function or class method.".format(op)) + + self.input_columns = to_list(input_columns) + self.output_columns = to_list(output_columns) + self.column_order = replace_none(column_order, []) + + # If output_columns were not provided then use input_columns + self.output_columns = self.input_columns if not self.output_columns else self.output_columns + + if self.input_columns and self.output_columns \ + and len(self.input_columns) != len(self.output_columns) \ + and not self.column_order: + raise ValueError("When length of input_columns and output_columns are not equal," + " column_order must be specified.") + + self.python_multiprocessing = python_multiprocessing + self.process_pool = None + + self.callbacks = to_list(callbacks) + self.max_rowsize = max_rowsize + self.offload = offload + + def parse(self, children=None): + operations = self.__decompose_callable_operations() + + count_old_transforms, count_new_transforms, count_non_data_vision_transforms = \ + self.__count_transforms(operations) + count_pyfunc = self.__count_pyfuncs(operations) + if count_new_transforms + count_pyfunc == len(operations): + prev_op = None + for op in operations: + if op.implementation is None: + if prev_op and prev_op.implementation == Implementation.PY: + op.implementation = Implementation.PY + else: + op.implementation = Implementation.C + prev_op = op + operations = transforms.transforms.Compose.reduce(operations) + elif count_old_transforms + count_pyfunc + count_non_data_vision_transforms == len(operations): + operations = transforms.py_transforms.Compose.reduce(operations) + else: + raise RuntimeError("Mixing old legacy c/py_transforms and new unified transforms is not allowed.") + + self.operations = self.__process_final_operations(operations) + self.prepare_multiprocessing() + + callbacks = [cb.create_runtime_obj() for cb in self.callbacks] + return cde.MapNode(children[0], self.operations, self.input_columns, self.output_columns, self.column_order, + callbacks, self.max_rowsize, OffloadToManualOffloadMode.get(self.offload), self.process_pool) + + def __deepcopy__(self, memodict): + return self.__safe_deepcopy__(memodict, exclude=("operations", "callbacks", "__transfer_dataset__")) + + def __del__(self): + if hasattr(self, "process_pool") and self.process_pool is not None: + self.process_pool.terminate() + del self.process_pool + + @staticmethod + def __count_pyfuncs(operations): + """ + Count the number of pyfuncs operations + """ + return sum([1 if isinstance(op, FuncWrapper) else 0 for op in operations]) + + @staticmethod + def __count_transforms(operations): + """ + Count the various flavors of transforms operations + """ + # Count the number of old legacy data and vision c_transforms and py_transforms + count_old_transforms = sum( + [1 if "c_transforms" in str(op) + or isinstance(op, (c_transforms.TensorOperation, py_transforms.PyTensorOperation)) + or ("py_transforms" in str(op) and not isinstance(op, FuncWrapper)) + else 0 for op in operations]) + # Count the number of new unified data and vision transforms + count_new_transforms = sum([1 if hasattr(op, "implementation") and not isinstance(op, FuncWrapper) + else 0 for op in operations]) + # Count the number of non-data transforms and non-vision transforms + count_non_data_vision_transforms = sum( + [1 if "text.transforms" in str(op) or "audio.transforms" in str(op) else 0 for op in operations]) + return count_old_transforms, count_new_transforms, count_non_data_vision_transforms + + @staticmethod + def __operation_valid_for_multiprocessing(op): + if callable(op) and str(op).find("c_transform") < 0: + return True + return False + + @staticmethod + def __process_final_operations(operations): + """ + Build final list of operations + """ + operations_fin = [] + for op in operations: + if hasattr(op, "implementation"): + if op.implementation == Implementation.C and not isinstance(op, (FuncWrapper, ToNumpy)): + operations_fin.append(op.parse()) + elif op.implementation == Implementation.PY: + operations_fin.append(op) + elif isinstance(op, (FuncWrapper, ToNumpy)): + operations_fin.append(op) + else: + raise RuntimeError("Wrong implementation") + else: + if op and getattr(op, 'parse', None): + operations_fin.append(op.parse()) + else: + operations_fin.append(op) + return operations_fin + + # Iterator bootstrap will be called on iterator construction. + # A deep copy of Dataset object is created prior of iterator_bootstrap. + # This method will create per iterator process pool and bind pyfunc execution to the pool. + def prepare_multiprocessing(self): + """ + Per iterator bootstrap callback. + """ + if self.python_multiprocessing and platform.system().lower() == 'windows': + logger.warning("Python multiprocessing is not supported on Windows platform.") + return + if self.python_multiprocessing: + iter_specific_operations = [] + callable_list = [] + + # If user didn't specify num_parallel_workers, set it to default + if self.num_parallel_workers is None: + self.num_parallel_workers = get_num_parallel_workers() + + # Pass #1, look for Python callables and build list + for op in self.operations: + # our c transforms is now callable and should not be run in Python multithreading + if MapDataset.__operation_valid_for_multiprocessing(op): + callable_list.append(op) + + if callable_list: + self.process_pool = _PythonMultiprocessing(str(self), self.num_parallel_workers, callable_list, + self.max_rowsize) + # Pass #2 + idx = 0 + for op in self.operations: + # our c transforms is now callable and should not be run in Python multithreading + if MapDataset.__operation_valid_for_multiprocessing(op): + # Wrap Python callable into _PythonCallable + iter_specific_operations.append(_PythonCallable(op, idx, self.process_pool)) + idx += 1 + else: + # CPP ops remain the same + iter_specific_operations.append(op) + self.operations = iter_specific_operations + + def __decompose_callable_operations(self): + """ + Decompose operations and build list of old legacy ops which are callable + """ + decomposed_operations = transforms.transforms.Compose.decompose(self.operations) + operations = [] + for op in decomposed_operations: + if callable(op) and not hasattr(op, "implementation") and str(op).find( + "c_transform") < 0 and not isinstance(op, c_transforms.TensorOperation) and \ + not isinstance(op, py_transforms.PyTensorOperation): + op = transforms.py_transforms_util.FuncWrapper(op) + operations.append(op) + return operations + + +class FilterDataset(UnionBaseDataset): + """ + The result of applying filter predicate to the input Dataset. + + Args: + input_dataset (Dataset): Input Dataset to be mapped. + predicate (callable): Python callable which returns a boolean value. If False then filter the element. + input_columns (Union[str, list[str]], optional): List of names of the input columns + (default=None, the predicate will be applied to all columns in the dataset). + num_parallel_workers (int, optional): Number of workers to process the dataset + in parallel (default=None). + """ + + def __init__(self, input_dataset, predicate, input_columns=None, num_parallel_workers=None): + super().__init__(children=input_dataset, num_parallel_workers=num_parallel_workers) + self.predicate = lambda *args: bool(predicate(*args)) + self.input_columns = to_list(input_columns) + + def parse(self, children=None): + return cde.FilterNode(children[0], self.predicate, self.input_columns) + + +class RepeatDataset(UnionBaseDataset): + """ + The result of applying Repeat operator to the input Dataset. + + Args: + input_dataset (Dataset): Input Dataset to be repeated. + count (int): Number of times the dataset will be repeated (default=-1, repeat indefinitely). + """ + + def __init__(self, input_dataset, count): + super().__init__(children=input_dataset) + self.count = replace_none(count, -1) + + def parse(self, children=None): + return cde.RepeatNode(children[0], self.count) + + +class SkipDataset(UnionBaseDataset): + """ + The result of applying Skip operator to the input Dataset. + + Args: + input_dataset (Dataset): Input dataset to have elements skipped. + count (int): Number of elements to be skipped in the dataset. + """ + + def __init__(self, input_dataset, count): + super().__init__(input_dataset) + self.count = count + + def parse(self, children=None): + return cde.SkipNode(children[0], self.count) + + +class TakeDataset(UnionBaseDataset): + """ + The result of applying Take operator to the input Dataset. + + Args: + input_dataset (Dataset): Input Dataset to have elements taken from. + count (int): Number of elements to be taken from the dataset. + """ + + def __init__(self, input_dataset, count): + super().__init__(children=input_dataset) + self.count = count + + def parse(self, children=None): + return cde.TakeNode(children[0], self.count) + + +class ZipDataset(UnionBaseDataset): + """ + The result of applying Zip operator to the input Dataset. + + Args: + datasets (tuple): A tuple of datasets to be zipped together. + + Raises: + TypeError: If dataset is not an instance of Dataset. + """ + + def __init__(self, datasets): + super().__init__(children=datasets) + + def parse(self, children=None): + return cde.ZipNode(children) + + def is_sync(self): + return any([c.is_sync() for c in self.children]) + + +class ConcatDataset(UnionBaseDataset): + """ + The result of applying concat dataset operator to the input Dataset. + + Args: + datasets (list): A list of datasets to be concatenated together. + + Raises: + TypeError: If dataset is not an instance of Dataset. + ValueError: If there is no samples in the one of the datasets. + """ + + def __init__(self, datasets): + super().__init__(children=datasets) + for dataset in datasets: + if not isinstance(dataset, Dataset): + raise TypeError("Invalid dataset, expected Dataset object, but got %s!" % type(dataset)) + self.datasets = datasets + self._sampler = samplers.SequentialSampler(num_samples=None) + + self.children_sizes_ = [c.get_dataset_size() for c in self.children] + child_index = 0 + for item in self.children_sizes_: + if item == 0: + raise ValueError("There are no samples in the dataset number %d. Please make sure there are " + "valid samples in the dataset." % child_index) + child_index += 1 + + # _children_flag_and_nums: A list of pair.The first element of pair is flag that characterizes + # whether the dataset is mappable. The second element of pair is length of the dataset + self._children_flag_and_nums = [] + + # _children_start_end_index_: A list of pair.The elements of pair are used to characterize + # the valid position of the dataset corresponding to the subscript when sampling + self._children_start_end_index_ = [] + for index, child in enumerate(self.children): + tem_list = [-1, -1] + self._children_start_end_index_.append(tem_list) + dataset_len = self.children_sizes_[index] + + from mindspore.dataset.engine.datasets_user_defined import GeneratorDataset + if isinstance(child, GeneratorDataset) and not hasattr(child.source, "__getitem__"): + dataset_len = 0 + self.children_sizes_[index] = 0 + + if isinstance(child, MappableDataset): + self._children_flag_and_nums.append((0, dataset_len)) + else: + self._children_flag_and_nums.append((1, dataset_len)) + + def parse(self, children=None): + return cde.ConcatNode(children, self._sampler, self._children_flag_and_nums, self._children_start_end_index_) + + def use_sampler(self, sampler): + """ + Set the distributedSampler to concat dataset + + Args: + sampler (Sampler): The sampler to use for the current dataset. + Currently supported: DistributedSampler. + + Raises: + TypeError: If the sampler is not an instance of DistributedSampler + ValueError: If the parameter shuffle of sampler is True + ValueError: If the parameter NumSamples of sampler is not None. + ValueError: If num_shards <=0. + """ + if not isinstance(sampler, samplers.DistributedSampler): + raise TypeError("The parameter %s of concat must be DistributedSampler!" % sampler) + + if sampler.is_shuffled(): + raise ValueError("The parameter shuffle of DistributedSampler must be False!") + + if sampler.num_shards <= 0: + raise ValueError("The parameter num_shards of DistributedSampler must be positive int!") + + if sampler.get_num_samples() is not None: + raise ValueError("The parameter num_samples of DistributedSampler is not support to be set!") + + self.dataset_size = None + + self._sampler = sampler + cumulative_samples_nums = 0 + for index, child in enumerate(self.children): + if hasattr(child, 'sampler') and child.sampler.get_num_samples() is not None: + raise ValueError("The parameter NumSamples of %s is not support to be set!" % child) + + if isinstance(child, BatchDataset): + raise TypeError("The parameter %s of concat must not be BatchDataset!" % child) + + # if child is mappable and the length is greater than 0 + if not self._children_flag_and_nums[index][0] and self._children_flag_and_nums[index][1]: + + tem_value = cumulative_samples_nums + self._children_flag_and_nums[index][1] + + if not self._children_flag_and_nums[index][1] >= sampler.num_shards: + if tem_value < sampler.num_shards: + self._children_start_end_index_[index][0] = cumulative_samples_nums + self._children_start_end_index_[index][1] = tem_value + else: + self._children_start_end_index_[index][0] = cumulative_samples_nums + self._children_start_end_index_[index][1] = tem_value % sampler.num_shards + + tem_sampler = copy.deepcopy(sampler) + tem_sampler.set_offset(cumulative_samples_nums) + child.use_sampler(tem_sampler) + + cumulative_samples_nums += self.children_sizes_[index] + cumulative_samples_nums %= sampler.num_shards + + +class RenameDataset(UnionBaseDataset): + """ + The result of applying Rename operator to the input Dataset. + + Args: + input_dataset (Dataset): Input Dataset to be Renamed. + input_columns (Union[str, list[str]]): List of names of the input columns. + output_columns (Union[str, list[str]]): List of names of the output columns. + """ + + def __init__(self, input_dataset, input_columns, output_columns): + super().__init__(children=input_dataset) + self.input_column_names = to_list(input_columns) + self.output_column_names = to_list(output_columns) + + def parse(self, children=None): + return cde.RenameNode(children[0], self.input_column_names, self.output_column_names) + + +def to_list(items): + if items is None: + return [] + if isinstance(items, tuple): + return list(items) + if not isinstance(items, list): + return [items] + return items + + +class ProjectDataset(UnionBaseDataset): + """ + The result of applying Project operator to the input Dataset. + + Args: + input_dataset (Dataset): Input Dataset to be Projected. + columns (Union[str, list[str]]): List of names of the columns to project. + """ + + def __init__(self, input_dataset, columns): + super().__init__(children=input_dataset) + self.columns = to_list(columns) + + def parse(self, children=None): + return cde.ProjectNode(children[0], self.columns) + + +class _ToDevice: + """ + Internal class to handle sending data to device. + """ + + def __init__(self, dataset, num_epochs): + ir_tree, self.api_tree = dataset.create_ir_tree() + + self._runtime_context = cde.PythonRuntimeContext() + self._runtime_context.Init() + self._to_device = cde.ToDevice(num_epochs) + self._to_device.Init(ir_tree) + self._runtime_context.AssignConsumer(self._to_device) + + ITERATORS_LIST.append(weakref.ref(self)) + _unset_iterator_cleanup() + + def send(self): + self._to_device.Send() + + def _reset(self, step): + self._to_device.Reset(step) + + def stop_send(self): + """ + send stop send signal to pipeline, it is used when end of sequence is sent at the epoch end. + """ + self._to_device.StopSend() + + def continue_send(self): + """ + send continue send signal to pipeline, it is used when end of sequence is sent at the epoch end. + """ + self._to_device.ContinueSend() + + def get_data_info(self): + """ + Get type and shape of current batch. + """ + return self._to_device.GetDataInfo() + + def release(self): + """ + Manually terminate Device Queue instead of relying on out of scope destruction. + """ + if hasattr(self, '_runtime_context') and self._runtime_context: + if hasattr(self, '_to_device') and self._to_device: + self._runtime_context.Terminate() + del self._to_device + del self._runtime_context + + def __deepcopy__(self, memodict): + return self + + def get_offload_model(self, col_names): + """ + Get offload model containing removed offload ops from pipeline. + """ + offload_model = GetOffloadModel(self._to_device, col_names) + return offload_model + + +class TransferDataset(Dataset): + """ + The result of applying TDT operator to the input Dataset. + + Args: + input_dataset (Dataset): Input Dataset to be transferred. + send_epoch_end (bool, optional): Whether to send end of sequence to device or not (default=True). + create_data_info_queue (bool, optional): Whether to create queue which stores + types and shapes of data or not (default=False). + + Raises: + TypeError: If device_type is empty. + ValueError: If device_type is not 'Ascend', 'GPU' or 'CPU'. + RuntimeError: If dataset is unknown. + """ + + def __init__(self, input_dataset, send_epoch_end=True, create_data_info_queue=False): + super().__init__(children=input_dataset) + self.queue_name = str(uuid.uuid1()) + self.device_type = context.get_context("device_target") if context else "CPU" + self.device_id = context.get_context("device_id") if context else 0 + + self._send_epoch_end = replace_none(send_epoch_end, True) + self._create_data_info_queue = create_data_info_queue + self._to_device = None + self.column_name = input_dataset.get_col_names() + + def parse(self, children=None): + total_batch = 0 + if hasattr(self.children[0], "__total_batch__"): + total_batch = self.children[0].__total_batch__ + return cde.TransferNode(children[0], self.queue_name, self.device_type, self.device_id, self._send_epoch_end, + total_batch, self._create_data_info_queue) + + def create_dict_iterator(self, num_epochs=-1, output_numpy=False): + raise RuntimeError("TransferDataset is not iterable.") + + def create_tuple_iterator(self, columns=None, num_epochs=-1, output_numpy=False, do_copy=True): + raise RuntimeError("TransferDataset is not iterable.") + + def __iter__(self): + raise RuntimeError("TransferDataset is not iterable.") + + def output_shapes(self): + raise RuntimeError("TransferDataset does not support obtaining output_shapes.") + + def output_types(self): + raise RuntimeError("TransferDataset does not support obtaining output_types.") + + @check_to_device_send + def send(self, num_epochs=-1): + """ + Send to device + """ + if Dataset._noop_mode(): + return + if self._to_device is not None: + del self._to_device + self._to_device = _ToDevice(self, num_epochs) + self._to_device.send() + + def stop_send(self): + if self._to_device is not None: + self._to_device.stop_send() + + def continue_send(self): + if self._to_device is not None: + self._to_device.continue_send() + + def _reset(self, step): + if self._to_device is not None: + logger.info("Reset the dataset pipeline to step " + str(step)) + self._to_device._reset(step) # pylint: disable=W0212 + + def get_data_info(self): + """ + Get type and shape of current batch + """ + if self._to_device is not None: + return self._to_device.get_data_info() + raise RuntimeError("Calling get_data_info with bad state.") + + def get_offload_model(self): + if self._to_device is not None: + return self._to_device.get_offload_model(self.column_name) + + raise RuntimeError("get_offload_model, _to_device is None") + + def release(self): + """ + Manually terminate Device Queue instead of relying on out of scope destruction. + """ + if self._to_device is not None: + self._to_device.release() + + +class Schema: + """ + Class to represent a schema of a dataset. + + Args: + schema_file(str): Path of the schema file (default=None). + + Returns: + Schema object, schema info about dataset. + + Raises: + RuntimeError: If schema file failed to load. + + Examples: + >>> from mindspore import dtype as mstype + >>> + >>> # Create schema; specify column name, mindspore.dtype and shape of the column + >>> schema = ds.Schema() + >>> schema.add_column(name='col1', de_type=mstype.int64, shape=[2]) + """ + + @check_schema + def __init__(self, schema_file=None): + self.schema_file = replace_none(schema_file, "") + self.cpp_schema = cde.SchemaObj(self.schema_file) + + @check_add_column + def add_column(self, name, de_type, shape=None): + """ + Add new column to the schema. + + Args: + name (str): The new name of the column. + de_type (str): Data type of the column. + shape (list[int], optional): Shape of the column + (default=None, [-1] which is an unknown shape of rank 1). + + Raises: + ValueError: If column type is unknown. + """ + if isinstance(de_type, typing.Type): + de_type = mstype_to_detype(de_type) + col_type = str(de_type) + else: + col_type = str(cde.DataType(de_type)) + if shape is None: + self.cpp_schema.add_column(name, col_type) + else: + self.cpp_schema.add_column(name, col_type, shape) + + def parse_columns(self, columns): + """ + Parse the columns and add it to self. + + Args: + columns (Union[dict, list[dict], tuple[dict]]): Dataset attribute information, decoded from schema file. + + - list[dict], `name` and `type` must be in keys, `shape` optional. + + - dict, columns.keys() as name, columns.values() is dict, and `type` inside, `shape` optional. + + Raises: + RuntimeError: If failed to parse columns. + RuntimeError: If column's name field is missing. + RuntimeError: If column's type field is missing. + + Examples: + >>> from mindspore.dataset import Schema + >>> schema = Schema() + >>> columns1 = [{'name': 'image', 'type': 'int8', 'shape': [3, 3]}, + ... {'name': 'label', 'type': 'int8', 'shape': [1]}] + >>> schema.parse_columns(columns1) + >>> columns2 = {'image': {'shape': [3, 3], 'type': 'int8'}, 'label': {'shape': [1], 'type': 'int8'}} + >>> schema.parse_columns(columns2) + """ + self.cpp_schema.parse_columns(json.dumps(columns, indent=2)) + + def to_json(self): + """ + Get a JSON string of the schema. + + Returns: + str, JSON string of the schema. + """ + return self.cpp_schema.to_json() + + def from_json(self, json_obj): + """ + Get schema file from JSON object. + + Args: + json_obj(dictionary): Object of JSON parsed. + + Raises: + RuntimeError: if there is unknown item in the object. + RuntimeError: if dataset type is missing in the object. + RuntimeError: if columns are missing in the object. + """ + self.cpp_schema.from_string(json.dumps(json_obj, indent=2)) + + def __str__(self): + return self.to_json() + + @staticmethod + def get_num_rows(schema): + schema_obj = schema + if not isinstance(schema_obj, Schema): + schema_obj = Schema(schema_obj) + return schema_obj.cpp_schema.get_num_rows() + + +class DeserializedDataset(Dataset): + def __init__(self, input_obj): + super().__init__() + self.input_obj = input_obj + + def parse(self, children=None): + if isinstance(self.input_obj, dict): + json_str = json.dumps(self.input_obj) + return cde.Dataset.from_json_string(json_str) + return cde.Dataset.from_json_file(self.input_obj) diff --git a/mindspore/ccsrc/transform-update/datatypes.py b/mindspore/ccsrc/transform-update/datatypes.py new file mode 100644 index 00000000000..aae15d42f59 --- /dev/null +++ b/mindspore/ccsrc/transform-update/datatypes.py @@ -0,0 +1,116 @@ +# Copyright 2019-2022 Huawei Technologies Co., Ltd +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ============================================================================== +""" +Define the data types. +瀹氫箟鏁版嵁绫诲瀷 +""" +import numpy as np + +import mindspore._c_dataengine as cde +from mindspore._c_expression import typing +import mindspore.common.dtype as mstype + + +def nptype_to_detype(type_): + """ + Get de data type corresponding to numpy dtype. + 鑾峰彇numpy dtype瀵瑰簲鐨刣e鏁版嵁绫诲瀷銆 + Args: + type_ (numpy.dtype): Numpy's dtype. + + Returns: + The data type of de. + """ + # 濡傛灉浼犲叆鐨 'type_' 涓嶆槸 NumPy 鏁版嵁绫诲瀷瀵硅薄锛坣p.dtype锛夛紝鍒欏皢鍏惰浆鎹负 np.dtype 瀵硅薄 +if not isinstance(type_, np.dtype): + type_ = np.dtype(type_) + +# 鍒涘缓涓涓瓧鍏革紝灏 NumPy 鏁版嵁绫诲瀷鏄犲皠鍒 CDE锛圡indSpore 鏁版嵁澧炲己搴擄級鐨勬暟鎹被鍨 +# 杩欎釜瀛楀吀鐢ㄤ簬灏 NumPy 鏁版嵁绫诲瀷杞崲涓 CDE 鏁版嵁绫诲瀷 +return { + np.dtype("bool"): cde.DataType("bool"), + np.dtype("int8"): cde.DataType("int8"), + np.dtype("int16"): cde.DataType("int16"), + np.dtype("int32"): cde.DataType("int32"), + np.dtype("int64"): cde.DataType("int64"), + np.dtype("uint8"): cde.DataType("uint8"), + np.dtype("uint16"): cde.DataType("uint16"), + np.dtype("uint32"): cde.DataType("uint32"), + np.dtype("uint64"): cde.DataType("uint64"), + np.dtype("float16"): cde.DataType("float16"), + np.dtype("float32"): cde.DataType("float32"), + np.dtype("float64"): cde.DataType("float64"), + np.dtype("str"): cde.DataType("string"), +}.get(type_) + + + +def mstype_to_detype(type_): + """ + Get de data type corresponding to mindspore dtype. + 鑾峰彇mindspore鏁版嵁绫诲瀷瀵瑰簲鐨刣e鏁版嵁绫诲瀷銆 + Args: + type_ (mindspore.dtype): MindSpore's dtype. + + Returns: + The data type of de. + """ + # 濡傛灉浼犲叆鐨 'type_' 涓嶆槸 NumPy 鏁版嵁绫诲瀷瀵硅薄锛坣p.dtype锛夛紝鍒欏皢鍏惰浆鎹负 np.dtype 瀵硅薄 +if not isinstance(type_, np.dtype): + type_ = np.dtype(type_) + +# 鍒涘缓涓涓瓧鍏革紝灏 NumPy 鏁版嵁绫诲瀷鏄犲皠鍒 CDE锛圡indSpore 鏁版嵁澧炲己搴擄級鐨勬暟鎹被鍨 +# 杩欎釜瀛楀吀鐢ㄤ簬灏 NumPy 鏁版嵁绫诲瀷杞崲涓 CDE 鏁版嵁绫诲瀷 +return { + np.dtype("bool"): cde.DataType("bool"), + np.dtype("int8"): cde.DataType("int8"), + np.dtype("int16"): cde.DataType("int16"), + np.dtype("int32"): cde.DataType("int32"), + np.dtype("int64"): cde.DataType("int64"), + np.dtype("uint8"): cde.DataType("uint8"), + np.dtype("uint16"): cde.DataType("uint16"), + np.dtype("uint32"): cde.DataType("uint32"), + np.dtype("uint64"): cde.DataType("uint64"), + np.dtype("float16"): cde.DataType("float16"), + np.dtype("float32"): cde.DataType("float32"), + np.dtype("float64"): cde.DataType("float64"), + np.dtype("str"): cde.DataType("string"), +}.get(type_) + + +def mstypelist_to_detypelist(type_list): + """ + Get list[de type] corresponding to list[mindspore.dtype]. + 鑾峰彇鍒楄〃[mindspore.dtype]瀵瑰簲鐨勫垪琛╗detype]銆 + Args: + type_list (list[mindspore.dtype]): a list of MindSpore's dtype. + + Returns: + The list of de data type. + """ + + # 閬嶅巻浼犲叆鐨 type_list 鍒楄〃 + for index, _ in enumerate(type_list): + # 濡傛灉鍒楄〃涓殑鍏冪礌涓嶄负 None + if type_list[index] is not None: + # 璋冪敤 mstype_to_detype 鍑芥暟灏 MindSpore 鏁版嵁绫诲瀷杞崲涓 CDE 鏁版嵁绫诲瀷 + type_list[index] = mstype_to_detype(type_list[index]) + else: + # 濡傛灉鍒楄〃涓殑鍏冪礌涓 None锛屽垯灏嗗叾璁剧疆涓虹┖瀛楃涓茬殑 CDE 鏁版嵁绫诲瀷 + type_list[index] = cde.DataType("") + + # 杩斿洖杞崲鍚庣殑 type_list 鍒楄〃 + return type_list + diff --git a/mindspore/ccsrc/transform-update/df_graph_manager.cc b/mindspore/ccsrc/transform-update/df_graph_manager.cc new file mode 100644 index 00000000000..1e1daaf565c --- /dev/null +++ b/mindspore/ccsrc/transform-update/df_graph_manager.cc @@ -0,0 +1,245 @@ +/** + * Copyright 2019 Huawei Technologies Co., Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "include/transform/graph_ir/df_graph_manager.h" + +#include + +#ifndef ENABLE_LITE_ACL +#include "include/common/utils/python_adapter.h" +#include "pipeline/jit/pipeline.h" +#endif +#ifndef NO_DLIB +#include "tdt/tsd_client.h" +#endif + +namespace mindspore { +namespace transform { +// 此构造函数用于初始化 DfGraphWrapper 类的一个新实例。 +// 它接受四个参数:'name'、'id'、'graph_ptr' 和 'options' +DfGraphWrapper::DfGraphWrapper(const std::string &name, const int &id, const DfGraphPtr &graph_ptr, + const OptionMap &options) + : name_(name), id_(id), graph_ptr_(graph_ptr), options_(options) {} + +DfGraphManager::DfGraphManager() { //构造函数 + graph_id_ = 0; + graph_runner_ptr_ = nullptr; + sess_ptr_ = nullptr; +} + +DfGraphManager::~DfGraphManager() { //析构函数 + // in python first destroy after atexit but in c++ destoy before atexit + DeleteGraphRunner(); + DeleteGeSession(); + ClearGraph(); +#ifndef ENABLE_LITE_ACL + python_adapter::set_python_env_flag(false); +#endif +} + +DfGraphManager &DfGraphManager::GetInstance() { + static DfGraphManager instance; + return instance; +} + +// 该函数用于生成图形ID。 +int DfGraphManager::GenerateId() { + graph_id_++; // 递增图形ID + if (graph_id_ <= 0) { // 如果图形ID小于等于0,则将其设置为1,确保ID不为负数 + graph_id_ = 1; + } + MS_LOG(INFO) << "Generate graph Id : " << graph_id_; // 打印生成的图形ID(仅用于日志记录) + return graph_id_; // 返回生成的图形ID +} + +// 该函数用于向图形管理器中添加一个图形。 +// 参数 'name' 表示图形的名称,'graph_ptr' 表示图形的指针,'options' 表示图形的选项。 +Status DfGraphManager::AddGraph(const std::string &name, const DfGraphPtr &graph_ptr, const OptionMap &options) { + std::lock_guard lg(lock_); // 使用互斥锁,确保添加图形的操作是线程安全的 + if (name.empty()) { // 如果图形名称为空,返回无效参数错误 + MS_LOG(ERROR) << "The graph name is null, add graph failed"; + return Status::INVALID_ARGUMENT; + } + + if (graph_ptr == nullptr) { // 如果图形指针为空,返回无效参数错误 + MS_LOG(INFO) << "The new graph {" << name << "}'s pointer is null, add graph failed"; + return Status::INVALID_ARGUMENT; + } + + int id = GenerateId(); // 生成一个新的图形ID + // 创建一个 DfGraphWrapperPtr 对象,用于包装图形信息,并将图形添加到图形管理器中 + DfGraphWrapperPtr wrap_ptr = std::make_shared(name, id, graph_ptr, options); + // 将图形添加到图形管理器中 + auto ret = graphs_.emplace(name, wrap_ptr); + if (ret.second == false) { // 如果图形名称已经存在,将旧的图形覆盖 + MS_LOG(WARNING) << "The graph name:{ " << name << " }is already exists! The old graph will be overwritten!!"; + ret.first->second = wrap_ptr; + } + MS_LOG(INFO) << "Add graph " << name << " to GraphManager success!"; // 成功添加图形后记录日志 + return Status::SUCCESS; // 返回成功状态 +} + +// 该函数用于获取图形管理器中的所有图形,并以一个 DfGraphWrapperPtr 类型的向量返回这些图形。 +std::vector DfGraphManager::GetAllGraphs() { + std::lock_guard lg(lock_); // 使用互斥锁,确保获取图形的操作是线程安全的 + std::vector ret; // 创建用于存储图形的向量 + std::stringstream ss; + ss << "{ "; + for (auto it = graphs_.begin(); it != graphs_.end(); ++it) { // 遍历图形管理器中的所有图形 + ss << it->first << ", "; // 将图形名称添加到日志记录字符串中 + ret.emplace_back(it->second); // 将图形指针添加到返回向量中 + } + ss << "}"; + MS_LOG(INFO) << "Return graphs: " << ss.str(); // 记录获取的图形名称到日志 + + return ret; // 返回存储所有图形的向量 +} + +// 该函数用于获取已保存的图形名称集合。 +// 返回类型为 std::set,表示一个存储唯一图形名称的集合。 +std::set DfGraphManager::GetSavedGraphs() { return saved_graphs_; } // 直接返回保存的图形名称集合 + +// 该函数用于向已保存的图形名称集合中添加新的图形名称。 +// 参数 'id' 表示要添加的图形名称。 +void DfGraphManager::AddSavedGraphs(const std::string &id) { saved_graphs_.insert(id); } // 将新的图形名称 'id' 插入已保存的图形名称集合中 + +// 该函数用于根据图形名称获取对应的 DfGraphWrapperPtr 对象。 +// 参数 'name' 表示要获取的图形名称。 +DfGraphWrapperPtr DfGraphManager::GetGraphByName(const std::string &name) { + std::lock_guard lg(lock_); // 使用互斥锁,确保获取图形的操作是线程安全的 + if (name.empty()) { + MS_LOG(ERROR) << "The graph name is null"; + return nullptr; // 如果图形名称为空,返回空指针 + } + + auto it = graphs_.find(name); + if (it == graphs_.end()) { + MS_LOG(INFO) << "Can't found graph name: " << name; + return nullptr; // 如果图形名称在图形管理器中找不到,返回空指针 + } + MS_LOG(INFO) << "Return graph: " << name; // 记录获取的图形名称到日志 + return it->second; // 返回找到的图形的 DfGraphWrapperPtr 对象 +} + +// 该函数用于清空图形管理器中的所有图形,并释放相关资源。 +void DfGraphManager::ClearGraph() noexcept { + std::lock_guard lg(lock_); // 使用互斥锁,确保清空图形的操作是线程安全的 + graphs_.clear(); // 清空图形管理器中的所有图形 + anf_graphs_.clear(); // 清空图形管理器中的所有 ANF 图形(可能是另一种图形表示) + MS_LOG(INFO) << "Remove all graphs in GraphManager"; // 记录已清空所有图形的日志 +} + +// 该函数用于将给定的 ANF 图形指针与特定的图形名称相关联。 +// 参数 'name' 表示图形的名称,'anf_graph_ptr' 表示要关联的 ANF 图形指针。 +void DfGraphManager::SetAnfGraph(const std::string &name, const AnfGraphPtr &anf_graph_ptr) { + DfGraphWrapperPtr df_graph = GetGraphByName(name); // 获取给定名称的图形包装器对象 + if (df_graph == nullptr) { + MS_LOG(ERROR) << "Can't found graph name: " << name; + return; // 如果找不到给定名称的图形,则返回错误并退出函数 + } + std::lock_guard lg(lock_); // 使用互斥锁,确保设置 ANF 图形的操作是线程安全的 + anf_graphs_[df_graph->id_] = anf_graph_ptr; // 将给定的 ANF 图形指针与图形包装器的 ID 相关联,并存储在 anf_graphs_ 容器中 +} + +// 该函数用于根据给定的图形 ID 获取对应的 ANF 图形指针。 +// 参数 'graph_id' 表示要获取的图形 ID。 +AnfGraphPtr DfGraphManager::GetAnfGraph(uint32_t graph_id) { + std::lock_guard lg(lock_); // 使用互斥锁,确保获取 ANF 图形的操作是线程安全的 + auto iter = anf_graphs_.find(graph_id); + if (iter == anf_graphs_.end()) { + MS_LOG(ERROR) << "Can't found anf graph, graph_id = " << graph_id; + return nullptr; // 如果找不到给定图形 ID 对应的 ANF 图形,则记录错误日志并返回空指针 + } + + return iter->second; // 返回找到的图形 ID 对应的 ANF 图形指针 +} + +// 该函数用于清空 ANF 图形容器,即移除所有已关联的 ANF 图形。 +void DfGraphManager::EraseAnfGraph() { + std::lock_guard lg(lock_); // 使用互斥锁,确保清空 ANF 图形容器的操作是线程安全的 + anf_graphs_.clear(); // 清空 ANF 图形容器,移除所有已关联的 ANF 图形 +} + +// 该函数用于设置与图形管理器关联的 GE(GraphEngine)会话指针。 +// 参数 'sess_ptr' 表示要设置的 GE 会话指针。 +void DfGraphManager::SetGeSession(const std::shared_ptr &sess_ptr) { + std::lock_guard lg(lock_); // 使用互斥锁,确保设置 GE 会话的操作是线程安全的 + if (sess_ptr == nullptr) { + MS_LOG(WARNING) << "You are adding a empty Ge Session"; // 如果传入的 GE 会话指针为空,记录警告日志 + } + + if (sess_ptr_ == nullptr) { + MS_LOG(INFO) << "Add a new Ge Session success"; // 如果之前未设置过 GE 会话,记录设置成功的日志 + } else { // 如果之前已经设置过 GE 会话,记录设置成功的日志,并提示之前的 GE 会话将被覆盖 + MS_LOG(INFO) << "Add a new Ge Session success, the old Ge Session will be overwritten!!"; + } + sess_ptr_ = sess_ptr; // 将传入的 GE 会话指针设置为图形管理器关联的 GE 会话指针 +} + +// 该函数用于获取图形管理器关联的 GE(GraphEngine)会话指针。 +// 返回类型为 std::shared_ptr,表示 GE 会话指针。 +std::shared_ptr DfGraphManager::GetGeSession() { + std::lock_guard lg(lock_); // 使用互斥锁,确保获取 GE 会话指针的操作是线程安全的 + return sess_ptr_; // 返回图形管理器关联的 GE 会话指针 +} + +// 该函数用于删除图形管理器关联的 GE(GraphEngine)会话,并清除与该会话相关的数据。 +void DfGraphManager::DeleteGeSession() noexcept { + std::lock_guard lg(lock_); // 使用互斥锁,确保删除 GE 会话的操作是线程安全的 + if (sess_ptr_ == nullptr) { + MS_LOG(INFO) << "Ge Session is not exist"; // 如果当前没有关联的 GE 会话,记录日志并直接返回 + } else { + sess_ptr_ = nullptr; // 将关联的 GE 会话指针设置为空指针,表示删除 GE 会话 + saved_graphs_.clear(); // 清空已保存的图形名称集合,即移除所有已保存的图形信息 + MS_LOG(INFO) << "Delete Ge Session success"; // 记录删除成功的日志 + } +} + +// 该函数用于设置与图形管理器关联的图形运行器(GraphRunner)指针。 +// 参数 'graph_runner_ptr' 表示要设置的图形运行器指针。 +void DfGraphManager::SetGraphRunner(const std::shared_ptr &graph_runner_ptr) noexcept { + std::lock_guard lg(lock_); // 使用互斥锁,确保设置图形运行器的操作是线程安全的 + if (graph_runner_ptr == nullptr) { // 如果传入的图形运行器指针为空,记录警告日志 + MS_LOG(WARNING) << "You are adding a empty GraphRunner"; + } + + if (graph_runner_ptr_ == nullptr) { // 如果之前未设置过图形运行器,记录设置成功的日志 + MS_LOG(INFO) << "Add a new GraphRunner success"; + } else { // 如果之前已经设置过图形运行器,记录设置成功的日志,并提示之前的图形运行器将被覆盖 + MS_LOG(INFO) << "Add a new GraphRunner success, the old GraphRunner will be overwritten!!"; + } + graph_runner_ptr_ = graph_runner_ptr; // 将传入的图形运行器指针设置为图形管理器关联的图形运行器指针 +} + +// 该函数用于获取图形管理器关联的图形运行器(GraphRunner)指针。 +// 返回类型为 std::shared_ptr,表示图形运行器指针 +std::shared_ptr DfGraphManager::GetGraphRunner() { + std::lock_guard lg(lock_); // 使用互斥锁,确保获取图形运行器指针的操作是线程安全的 + return graph_runner_ptr_; // 返回图形管理器关联的图形运行器指针 +} + +// 该函数用于删除图形管理器关联的图形运行器(GraphRunner)。 +void DfGraphManager::DeleteGraphRunner() noexcept { + std::lock_guard lg(lock_); // 使用互斥锁,确保删除图形运行器的操作是线程安全的 + if (graph_runner_ptr_ == nullptr) { + MS_LOG(INFO) << "GraphRunner is not exist"; // 如果当前没有关联的图形运行器,记录日志并直接返回 + } else { + graph_runner_ptr_ = nullptr; // 将关联的图形运行器指针设置为空指针,表示删除图形运行器 + MS_LOG(INFO) << "Delete GraphRunner success"; // 记录删除成功的日志 + } +} +} // namespace transform +} // namespace mindspore diff --git a/mindspore/ccsrc/transform-update/dim_reduce.py b/mindspore/ccsrc/transform-update/dim_reduce.py new file mode 100644 index 00000000000..1ff7bfd2765 --- /dev/null +++ b/mindspore/ccsrc/transform-update/dim_reduce.py @@ -0,0 +1,331 @@ +# Copyright 2021 Huawei Technologies Co., Ltd +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ============================================================================ +"""dim_reduce""" +import math +import numpy as np +from mindspore.nn.cell import Cell +from mindspore.ops import composite as C +from mindspore.ops import functional as F +from mindspore.ops import operations as P +from mindspore.common.tensor import Tensor +from mindspore.common.parameter import Parameter, ParameterTuple +from mindspore.common import dtype as mstype + + +__all__ = ["DimReduce"] + + +_scale_grad = C.MultitypeFuncGraph("_scale_grad") + + +@_scale_grad.register("Tensor", "Tensor") +def _scale_grad_process(scale, grad): + grad = F.cast(grad, mstype.float32) + grad = P.Div()(grad, scale) + return grad + + +_save_weight = C.MultitypeFuncGraph("_save_weight") + + +@_save_weight.register("Tensor", "Tensor") +def _save_weight_process(parameter, new_parameter): + return P.Assign()(parameter, new_parameter) + + +_pca_projection = C.MultitypeFuncGraph("_pca_projection") + + +@_pca_projection.register("Tensor", "Tensor") +def _pca_projection_process(pca_mat, grad): + grad_k = P.MatMul()(pca_mat, F.reshape(grad, (-1, 1))) + return grad_k + + +_pca_back_projection = C.MultitypeFuncGraph("_pca_back_projection") + + +@_pca_back_projection.register("Tensor", "Tensor", "Tensor") +#该函数用于PCA反投影 +def _pca_back_projection_process(grad_k, pca_mat, grad): + grad_proj = P.MatMul()(F.transpose(pca_mat, (1, 0)), grad_k) + grad_proj_reshape = F.reshape(grad_proj, F.shape(grad)) + return grad_proj_reshape + + +_update_grad_res_momentum = C.MultitypeFuncGraph("_update_grad_res_momentum") + + +@_update_grad_res_momentum.register("Float32", "Float32", "Tensor", "Tensor", "Tensor") +#该函数用于更新梯度残差动量 +def _update_grad_res_momentum_process(gamma, alpha, grad_res_momentum, grad, grad_proj): + grad_res_momentum_new = gamma * grad_res_momentum + grad - grad_proj + P.Assign()(grad_res_momentum, grad_res_momentum_new) + res = alpha * grad_res_momentum_new + return res + + +_get_delta_weight = C.MultitypeFuncGraph("_get_delta_weight") + + +@_get_delta_weight.register("Tensor", "Tensor", "Tensor") +def _get_delta_weight_process(rho, dn, grad_res_momentum): + delta_weight = grad_res_momentum - rho * dn + return delta_weight + + +class DimReduce(Cell): + r""" + The dimension reduce training, is a novel algorithm for accelerating convergence of Deep Learning models. + + .. math:: + + \begin{align} + grad\_k &= pca\_mat \cdot grad\\ + dk &= - bk \cdot grad\_k\\ + sk &= rho ^ m \cdot dk\\ + delta\_loss &= sigma \cdot grad\_k.T \cdot sk + \end{align} + + Here: + + - pca_mat (array): Shape (k*n), k is part of n_components, n is the size of weight. + - bk (array): Shape (k*k), is the symmetric positive definite matrix in Quasi-Newton method. + + we need to find the m satisfy: + + .. math:: + new\_loss < old\_loss + delta\_loss + + Then, get delta_grad to update the weights for model: + + .. math:: + + \begin{align} + grad\_k\_proj &= pca\_mat.T \cdot grad\_k\\ + new\_grad\_momentum &= gamma \cdot old\_grad\_momentum + grad - grad\_k\_proj\\ + delta\_grad &= alpha \cdot new\_grad\_momentum - pca\_mat.T \cdot sk + \end{align} + + Args: + network (Cell): The training network. The network only supports single output. + optimizer (Union[Cell]): Optimizer for updating the weights. + weight (Tuple(Parameter)): Tuple of parameters. + pca_mat_local (numpy.ndarray): For PCA operation, k*n, k is part of n_components, n is the size of weight. + n_components (int): PCA.components. + rho (float): Coefficient. + gamma (float): Coefficient. + alpha (float): Coefficient. + sigma (float): Coefficient. + rank (int): Rank number. + rank_size (int): Rank size. + + Inputs: + - **loss** (Tensor) - Tensor with shape :math:`()`. + - **old_grad** (Tuple(Tensor)) - Tuple of gradient tensors. + - **weight** (Tuple(Tensor)) - Tuple of parameters. + - **weight_clone** (Tuple(Tensor)) - clone of weight + - **(\*inputs)** (Tuple(Tensor)) - Tuple of input tensors with shape :math:`(N, \ldots)`. + + Outputs: + - **loss** (Tensor) - Tensor with shape :math:`()`. + """ + def __init__(self, network, optimizer, weight, pca_mat_local, n_components, rho, gamma, alpha, sigma, rank, + rank_size): + super(DimReduce, self).__init__() + self.network = network + self.optimizer = optimizer + self.rank = rank + self.rank_size = rank_size + self.gamma = gamma + self.alpha = alpha + self.sigma = sigma + + self.float_type = mstype.float32 + self._set_rho_list(rho) + self._set_local_pca_mat(pca_mat_local, n_components, weight) + self._set_init_parameter(weight) + + self.hyper_map = C.HyperMap() + self.concat = P.Concat() + self.matmul = P.MatMul() + self.mul = P.Mul() + self.add = P.Add() + + #该函数是为了设置rho(ρ)值列表,以便在优化算法中使用。 + def _set_rho_list(self, rho): + """set rho list info.""" + self.max_search_time = 2 # 最大搜索次数 + self.rho_list = [] + for i in range(self.max_search_time): + self.rho_list.append(Tensor(np.power(rho, i), dtype=self.float_type)) + self.rho_list.append(Tensor(0, dtype=self.float_type)) + + #该函数用于设置本地PCA矩阵信息 + def _set_local_pca_mat(self, pca_mat_local, n_components, parameter_tuple): + """set pca info.""" + self.n_components = n_components + local_dim = math.ceil(self.n_components / self.rank_size) + + self.start_index = self.rank * local_dim + self.end_index = (self.rank + 1) * local_dim + + start = 0 + self.pca_list_local = () + # 遍历模型参数,将本地PCA矩阵按参数大小分块,并存储在pca_list_local中 + for param in parameter_tuple: + size = np.shape(param.asnumpy().reshape((-1, 1)))[0] + self.pca_list_local += (Tensor(pca_mat_local[:, start:start + size], dtype=self.float_type),) + start += size + + self.dk_pad_flag = False + pad_num = self.rank_size * local_dim - self.n_components + if pad_num: # 如果需要进行填充,则设置dk_pad_flag为True,并创建填充部分 + self.dk_pad_flag = True + self.dk_pad_part = Tensor(np.zeros([pad_num, 1]), dtype=self.float_type) + + if self.rank_size > 1: + self.broadcast_list = [] + for i in range(self.rank_size): # 创建广播列表,用于跨设备通信 + broadcast = P.Broadcast(i) + self.broadcast_list.append(broadcast) + self.allreduce = P.AllReduce() # 用于执行全局归约操作 + self.allgather = P.AllGather() # 用于执行全局收集操作 + + def _set_init_parameter(self, parameter_tuple): + """init parameters.""" + self.true_flag = Tensor(True) + self.false_flag = Tensor(False) + self.epsilon = np.power(10.0, -20) + # 初始化gk_last参数 + self.gk_last = Parameter(Tensor(np.zeros([self.n_components, 1]), dtype=self.float_type), name="gk_last") + self.gk_last_init = Parameter(Tensor(False), name="gk_last_init") + # 初始化bk和sk参数 + self.bk = Parameter(Tensor(np.eye(self.n_components), dtype=self.float_type), name="bk") + self.sk = Parameter(Tensor(np.zeros([self.n_components, 1]), dtype=self.float_type), name="sk") + self.eye = Tensor(np.eye(self.n_components), dtype=self.float_type) + # 初始化grad_res_momentum参数 + self.grad_res_momentum = ParameterTuple(parameter_tuple).clone(prefix="grad_res_momentum", init="zeros") + # 初始化gk_last_back和bk_back参数 + self.gk_last_back = Parameter(Tensor(np.zeros([self.n_components, 1]), dtype=self.float_type), + name="gk_last_back") + self.bk_back = Parameter(Tensor(np.eye(self.n_components), dtype=self.float_type), name="bk_back") + # 初始化grad_proj_init和dn_init参数 + self.grad_proj_init = ParameterTuple(parameter_tuple).clone(prefix="grad_proj_init", init="zeros") + self.dn_init = ParameterTuple(parameter_tuple).clone(prefix="dn_init", init="zeros") + + def construct(self, loss, old_grad, loss_scale, weight, weight_clone, *inputs): + # 更新权重和梯度 + weight = F.depend(weight, loss) + old_grad = F.depend(old_grad, weight) + old_grad = self.hyper_map(F.partial(_scale_grad, loss_scale), old_grad) + old_loss = self.allreduce(loss) / self.rank_size if self.rank_size > 1 else loss + # 计算gk_local + gk_local = self.hyper_map(_pca_projection, self.pca_list_local, old_grad) + gk_local = F.addn(gk_local) + gk_pad = self.allgather(gk_local) if self.rank_size > 1 else gk_local + gk_pad = F.reshape(gk_pad, (-1, 1)) + gk = gk_pad[0:self.n_components, :] + # 保存权重和梯度 + _save_weight(self.gk_last_back, self.gk_last) + _save_weight(self.bk_back, self.bk) + # 计算dk + dk = self._apply_quasi_newton_update(gk) + if self.dk_pad_flag: + dk_pad = self.concat((dk, self.dk_pad_part)) + else: + dk_pad = dk + dk_local = dk_pad[self.start_index: self.end_index, :] + # 计算dn_local和grad_proj_local + dn_local = self.hyper_map(F.partial(_pca_back_projection, dk_local), self.pca_list_local, old_grad) + grad_proj_local = self.hyper_map(F.partial(_pca_back_projection, gk_local), self.pca_list_local, old_grad) + dn = self.dn_init if self.rank_size > 1 else dn_local + grad_proj = self.grad_proj_init if self.rank_size > 1 else grad_proj_local + if self.rank_size > 1: + for broadcast in self.broadcast_list: + dn_part = broadcast(dn_local) + dn = self.hyper_map(self.add, dn, dn_part) + grad_proj_part = broadcast(grad_proj_local) + grad_proj = self.hyper_map(self.add, grad_proj, grad_proj_part) + # 使用线搜索更新rho值 + rho, find = self._line_search(gk, dk, dn, old_loss, weight, weight_clone, *inputs) + if not find: + _save_weight(self.gk_last, self.gk_last_back) + _save_weight(self.bk, self.bk_back) + # 更新梯度并执行优化步骤 + update_grad = self.hyper_map(F.partial(_update_grad_res_momentum, self.gamma, self.alpha), + self.grad_res_momentum, old_grad, grad_proj) + delta_weight = self.hyper_map(F.partial(_get_delta_weight, rho), dn, update_grad) + update = self.optimizer(delta_weight) + weight = F.depend(weight, update) + clone = self.hyper_map(_save_weight, weight_clone, weight) + loss = F.depend(loss, clone) + return loss + + #该函数用于线搜索,寻找最佳的 rho 值,以确保模型参数的更新不引起损失函数增加。 + def _line_search(self, gk, dk, dn, old_loss, weight, weight_clone, *inputs): + """line search rho.""" + res = self.rho_list[-1] # 初始化rho为最小值 + find = self.false_flag # 初始化find标志为False + for i in range(self.max_search_time): # 遍历rho候选列表,尝试不同的rho值 + find = self._find_rho(gk, dk, dn, old_loss, weight, weight_clone, self.rho_list[i], *inputs) + if find: # 如果找到了满足条件的rho值,更新res,并跳出循环 + res = self.rho_list[i] + break + return res, find + + #该函数用于查找最佳 rho 值的方法,以确保模型参数的更新不会导致损失函数增加。 + def _find_rho(self, gk, dk, dn, old_loss, weight, weight_clone, rho, *inputs): + """search rho.""" + res = self.false_flag # 初始化res标志为False + # 计算sn,即损失函数对应于rho值的更新 + sn = self.hyper_map(F.partial(self.mul, -1 * rho), dn) + sn = F.depend(sn, old_loss) + update = self.optimizer(sn) + # 计算使用rho值更新后的损失函数 + new_loss = F.depend(self.network(*inputs), update) + # 如果是分布式训练,对损失函数进行全局平均 + if self.rank_size > 1: + new_loss = self.allreduce(new_loss) / self.rank_size + # 计算损失函数的变化 + old_loss_delta = old_loss + self.sigma * rho * F.squeeze(self.matmul(F.transpose(gk, (1, 0)), dk)) + # 如果使用rho值更新后的损失函数小于旧的损失函数,表示找到了一个满足条件的rho值 + if old_loss_delta > new_loss: + _save_weight(self.sk, rho * dk) + res = self.true_flag + # 更新weight_clone和weight的值 + weight_clone = F.depend(weight_clone, old_loss_delta) + restore = self.hyper_map(_save_weight, weight, weight_clone) + res = F.depend(res, restore) + return res + + #该函数用于应用拟牛顿更新。 + def _apply_quasi_newton_update(self, gk): + """apply quasi_newton update.""" + if self.gk_last_init: + yk = gk - self.gk_last # 计算gk和上一次迭代的gk的差值 + g = self.matmul(F.transpose(yk, (1, 0)), self.sk) + g = F.squeeze(g) + if g > self.epsilon: + pk = 1. / g + t1 = self.eye - self.matmul(pk * yk, F.transpose(self.sk, (1, 0))) + new_bk = self.matmul(self.matmul(F.transpose(t1, (1, 0)), self.bk), t1) + \ + self.matmul(pk * self.sk, F.transpose(self.sk, (1, 0))) + _save_weight(self.bk, new_bk) # 更新拟牛顿矩阵bk + else: + _save_weight(self.gk_last_init, self.true_flag) # 第一次迭代时初始化gk_last_init + _save_weight(self.gk_last, gk) # 保存当前迭代的gk值 + dk = -1 * self.matmul(self.bk, gk) # 计算拟牛顿更新后的dk值 + return dk diff --git a/mindspore/ccsrc/transform-update/flatten_recursive_stmt.py b/mindspore/ccsrc/transform-update/flatten_recursive_stmt.py new file mode 100644 index 00000000000..f0544ae5675 --- /dev/null +++ b/mindspore/ccsrc/transform-update/flatten_recursive_stmt.py @@ -0,0 +1,164 @@ +# Copyright 2022 Huawei Technologies Co., Ltd +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ============================================================================ +"""Ast optimizer for flatten recursive call.""" +from typing import Any, Tuple +import ast +from ast import FunctionDef +from mindspore import log as logger + + +class FlattenRecursiveStmt(ast.NodeTransformer): + """Ast optimizer for flatten recursive call.""" + + def __init__(self): + """ + Constructor of FlattenRecursiveStmt. + + Returns: + An instance of ast optimizer for flatten recursive call. + """ + self._flatten_table: dict = { + ast.Return: ["value"], + ast.Call: ["args"], + ast.BinOp: ["left", "right"], + ast.BoolOp: ["values"], + ast.unaryop: ["operand"], + } + + @staticmethod + def _generate_target_name(node: ast.AST, target_names): + """Generate unique target name.""" + if isinstance(node, ast.Call): #如果节点是ast.Call类型,它将检查节点的属性(表示函数调用的目标)的类型 + func = node.func #如果是func类型,则使用其属性作为目标名称 + if isinstance(func, ast.Name): #如果是ast.Name类型,则将使用其属性id作为目标名称 + target_name = func.id + elif isinstance(func, ast.Attribute): #如果是ast.Attribute类型,则使用其属性attr作为目标名称 + target_name = func.attr + else: #如果不是这两种类型,则会记录一个警告,并将目标名称设置为"function" + logger.warning("unhandled type of func of ast.Call while generating new target name: %s ", type(func)) + target_name = "function" + elif isinstance(node, ast.Return): #如果节点是ast.Return类型,则将目标名称设置为"return_value"。 + target_name = "return_value" + elif isinstance(node, (ast.BinOp, ast.boolop, ast.UnaryOp)): #如果节点是ast.BinOpast、ast.boolop或ast.UnaryOp类型,它将使用相应操作符的类型名称作为目标名称。 + target_name = type(node.op).__name__ + else: #对于其他类型的节点,记录一个警告,并将目标名称设置为该节点的类型名称 + logger.warning("unhandled type of node while generating new target name: %s ", type(node)) + target_name = type(node).__name__ + suffix = 0 + result = target_name + while result in target_names: #在生成的目标名称上添加数字后缀,以确保它在目标名称列表中是唯一的 + suffix += 1 + result = f"{target_name}_{suffix}" + target_names.append(result) + return result #返回生成的目标名称并将其添加到列表中 + + @staticmethod + def _fill_in_original_target_names(target_names, node): + """Fill in original target names before getting unique names.""" + for function_index in range(len(node.body)): #遍历AST(Abstract Syntax Tree)节点的node.body + child = node.body[function_index] + if not isinstance(child, ast.Assign): #在每个节点中,检查是否是一个赋值语句(ast.Assign) + continue #如果不是,继续处理下一个节点。 + targets = child.targets #如果节点是赋值语句,则获取赋值语句的目标(targets)。 + for target in targets: #如果目标是ast.Name类型,则获取变量名(target.id)并检查该名称是否已经存在于target_names列表中。 + if not isinstance(target, ast.Name): + raise RuntimeError("currently only support ast.Name targets") + target_name = target.id + if target_name not in target_names: #如果变量名不在target_names列表中,就将其添加进去 + target_names.append(target_name) + + @staticmethod + # 检查node是否是以下类型之一:ast.Name, ast.Constant, ast.Num, ast.Str, ast.NameConstant, ast.Bytes, ast.Ellipsis + # 如果是,就直接返回一个空字符串和原始node,因为这些节点是不需要生成新赋值节点的。 + def _create_new_assign_node(node: ast.AST, target_names) -> Tuple[str, ast.AST]: + """Create new assign node to be inserted into ast.FunctionDef.""" + if isinstance(node, (ast.Name, ast.Constant, ast.Num, ast.Str, ast.NameConstant, ast.Bytes, ast.Ellipsis)): + return "", node + #对于其他类型的节点,调用FlattenRecursiveStmt._generate_target_name函数来生成一个新的目标名称。 + new_target_name = FlattenRecursiveStmt._generate_target_name(node, target_names) + return new_target_name, ast.Assign(targets=[ast.Name(id=new_target_name, ctx=ast.Store())], value=node) #创建一个新的赋值节点ast. Assign.node作为赋值的值(value)。 返回这个新的目标名称和创建的赋值节点 + + def _flatten_statement(self, node: ast.AST, target_names) -> [ast.AST]: + """Flatten recursive statement according to different node type.""" + flatten_config = self._flatten_table.get(type(node)) #通过查找_flatten_table字典获取node类型对应的展开配置flatten_config。如果没有找到对应的配置,它将返回一个空列表 + if flatten_config is None: + return [] + results = [] + for todo_name in flatten_config: + todos = getattr(node, todo_name) #遍历展开配置中的每个待展开项(todo_name) + #对于列表类型的属性,遍历列表中的每个元素,调用FlattenRecursiveStmt._create_new_assign_node函数来生成一个新的赋值节点,并将生成的新目标名称替换原始元素的位置。 + #如果生成的新节点与原始节点相同,则保留原始节点。 否则,将新节点添加到结果列表中。 + if isinstance(todos, list): + new_list = [] + for todo in todos: + new_target_name, new_node = FlattenRecursiveStmt._create_new_assign_node(todo, target_names) + if id(new_node) == id(todo): + new_list.append(todo) + else: + new_list.append(ast.Name(id=new_target_name, ctx=ast.Load())) + results.append(new_node) + setattr(node, todo_name, new_list) + #对于字典类型的属性,它会遍历字典中的每个键值对,调用FlattenRecursiveStmt._create_new_assign_node函数来生成新的赋值节点,并将生成的新目标名称替换原始值的位置。 + #同样,如果生成的新节点与原始节点相同,则保留原始节点。 否则,将新节点添加到结果列表中。 + elif isinstance(todos, dict): + new_dict = [] + for key, value in todos: + new_target_name, new_node = FlattenRecursiveStmt._create_new_assign_node(value, target_names) + if id(new_node) == id(value): + new_dict[key] = value + else: + new_dict[key] = ast.Name(id=new_target_name, ctx=ast.Load()) + results.append(new_node) + setattr(node, todo_name, new_dict) + else: + new_target_name, new_node = FlattenRecursiveStmt._create_new_assign_node(todos, target_names) + if id(new_node) != id(todos): + setattr(node, todo_name, ast.Name(id=new_target_name, ctx=ast.Load())) + results.append(new_node) + return results #函数返回结果列表,其中包含所有生成的新赋值节点 + + #检查node的名称是否为"construct",如果不是,直接返回原始node + def visit_FunctionDef(self, node: FunctionDef) -> Any: + """Traverse construct node and flatten recursive nodes.""" + if node.name != "construct": + return node + + target_names = [] #创建一个空的target_names列表,并调用_fill_in_original_target_names函数来填充原始目标名称 + self._fill_in_original_target_names(target_names, node) + index = len(node.body) - 1 #从函数体的最后一个语句开始,向前遍历每个语句(由node.body列表表示)。 + while index >= 0: + child = node.body[index] #对于每个语句,函数检查是否是一个赋值语句(ast.Assign),或者是一个表达式语句(ast.Expr)。 + if isinstance(child, ast.Assign): + stmt = child.value #如果是赋值语句,获取赋值语句的值 + elif isinstance(child, ast.Expr): + stmt = child.value #如果是表达式语句,获取表达式的值 + else: + stmt = child #否则,将语句本身作为要处理的节点 + results = self._flatten_statement(stmt, target_names) #调用_flatten_statement函数来展开语句中的递归节点,并传递target_names列表。 该函数将返回一个包含生成的新赋值节点的列表。 + #如果_flatten_statement返回了结果,函数将这些结果逆序遍历,并将每个新节点插入到原始节点的前面,以确保展开的赋值节点按正确的顺序插入。 + if results: + results.reverse() + for result in results: + node.body.insert(index, result) + index += 1 + index -= 1 + return node + + def transform(self, ast_root): #这是FlattenRecursiveStmt类的transform方法,它是FlattenRecursiveStmt的接口方法。 + #这个方法接受一个AST(Abstract Syntax Tree)的根节点ast_root作为输入,并返回经过展开递归节点处理后的AST根节点。 + """Interface of FlattenRecursiveStmt.""" + ast_root = self.visit(ast_root) #调用self.visit(ast_root),这是一个递归的AST遍历过程,它会调用visit_FunctionDef等方法来处理AST中的各种节点。 + ast_root = ast.fix_missing_locations(ast_root) #使用ast.fix_missing_locations(ast_root)来修复AST节点中缺失的位置信息。 在处理AST时,可能会对节点进行插入、删除等操作,导致节点的位置信息丢失。ast.fix_missing_locations会遍历AST并为缺失位置的节点添加默认的位置信息。 + return ast_root #返回经过展开递归节点处理和位置修复后的AST根节点 diff --git a/mindspore/ccsrc/transform-update/functional_ops_declare.cc b/mindspore/ccsrc/transform-update/functional_ops_declare.cc new file mode 100644 index 00000000000..2ea6af2dacc --- /dev/null +++ b/mindspore/ccsrc/transform-update/functional_ops_declare.cc @@ -0,0 +1,45 @@ +/** + * Copyright 2019 Huawei Technologies Co., Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "transform/graph_ir/op_declare/functional_ops_declare.h" + +namespace mindspore::transform { +// Case +INPUT_MAP(Case) = {{1, INPUT_DESC(branch_index)}}; +//杈撳叆鏄犲皠锛宐ranch_index绱㈠紩涓1 +DYN_INPUT_MAP(Case) = {{2, DYN_INPUT_DESC(input)}}; +//鍔ㄦ佽緭鍏ユ槧灏勶紝灏嗙储寮曚负2鐨勫姩鎬佽緭鍏ヤ笌鍚嶇О涓篿nput鐨勫姩鎬佽緭鍏ユ弿杩板叧鑱旇捣鏉ワ紝鐢ㄤ簬鍚庣画鎿嶄綔 +ATTR_MAP(Case) = EMPTY_ATTR_MAP; +//灞炴ф槧灏勶紝璁句负绌 +DYN_OUTPUT_MAP(Case) = {{0, DYN_OUTPUT_DESC(output)}}; +//鍔ㄦ佽緭鍑烘槧灏勶紝灏嗙储寮曚负0鐨勫姩鎬佽緭鍑轰笌鍚嶇О涓簅utput鐨勫姩鎬佽緭鍑烘弿杩板叧鑱旇捣鏉ワ紝鐢ㄤ簬鍚庣画鎿嶄綔 +DYN_SUBGRAPH_MAP(Case) = {{0, DYN_SUBGRAPH_DESC(branches)}}; +//鍔ㄦ佸瓙鍥炬槧灏勶紝灏嗙储寮曚负0鐨勫姩鎬佸瓙鍥句笌鍚嶇О涓篵ranches鐨勫姩鎬佹弿杩板叧鑱旇捣鏉ワ紝鐢ㄤ簬鍚庣画鎿嶄綔 +REG_ADPT_DESC(Case, kNameCase, ADPT_DESC(Case)); +//娉ㄥ唽Case鎿嶄綔鐨勯傞厤鍣ㄦ弿杩発NameCase + +// While +DYN_INPUT_MAP(While) = {{1, DYN_INPUT_DESC(input)}}; +//杈撳叆鏄犲皠锛宨nput绱㈠紩涓1 +ATTR_MAP(While) = {{"parallel_iterations", ATTR_DESC(parallel_iterations, AnyTraits())}}; +//灞炴ф槧灏勶紝灞炴arallel_iterations绫诲瀷涓篿nt32_t +DYN_OUTPUT_MAP(While) = {{0, DYN_OUTPUT_DESC(output)}}; +//鍔ㄦ佽緭鍑烘槧灏勶紝灏嗙储寮曚负0鐨勫姩鎬佽緭鍑轰笌鍚嶇О涓簅utput鐨勫姩鎬佽緭鍑烘弿杩板叧鑱旇捣鏉ワ紝鐢ㄤ簬鍚庣画鎿嶄綔 +SUBGRAPH_MAP(While) = {{0, SUBGRAPH_DESC(cond)}, {1, SUBGRAPH_DESC(body)}}; +//鍔ㄦ佸瓙鍥炬槧灏勶紝灏嗙储寮曚负0鐨勫姩鎬佸瓙鍥句笌鍚嶇О涓篶ond鐨勫姩鎬佹弿杩板叧鑱旓紝灏嗙储寮曚负1鐨勫姩鎬佸瓙鍥句笌鍚嶇О涓篵ody鐨勫姩鎬佹弿杩板叧鑱旓紝鐢ㄤ簬鍚庣画鎿嶄綔 +REG_ADPT_DESC(While, kNameWhile, ADPT_DESC(While)); +//娉ㄥ唽While鎿嶄綔鐨勯傞厤鍣ㄦ弿杩発NameWhile +} // namespace mindspore::transform diff --git a/mindspore/ccsrc/transform-update/grad_accumulation.py b/mindspore/ccsrc/transform-update/grad_accumulation.py new file mode 100644 index 00000000000..ca955ca04ba --- /dev/null +++ b/mindspore/ccsrc/transform-update/grad_accumulation.py @@ -0,0 +1,82 @@ +# Copyright 2021 Huawei Technologies Co., Ltd +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ============================================================================ +"""grad accumulation""" +from mindspore.nn.cell import Cell +from mindspore.common import Parameter, Tensor +from mindspore.common import dtype as mstype +from mindspore.ops import composite as C +from mindspore.ops import functional as F +from mindspore.ops import operations as P + + +__all__ = ["GradientAccumulation", "gradient_accumulation_op", "gradient_clear_op"] + + +# 创建梯度积累操作 +gradient_accumulation_op = C.MultitypeFuncGraph("gradient_accumulation_op") + +# 注册梯度积累操作的计算图,输入参数是积累步数(整数),累积的梯度张量,以及当前步的梯度张量 +@gradient_accumulation_op.register("Int64", "Tensor", "Tensor") +def cumulative_grad_process(accumulation_step, cumulative_grad, grad): + """Apply gradient accumulation to cumulative grad.""" + # 将当前步的梯度张量除以积累步数,并加到累积梯度上 + return P.AssignAdd()(cumulative_grad, grad / accumulation_step) + +# 创建梯度清零操作 +gradient_clear_op = C.MultitypeFuncGraph("gradient_clear_op") + +# 注册梯度清零操作的计算图,输入参数是累积的梯度张量 +@gradient_clear_op.register("Tensor") +def clear_grad(cumulative_grad): + # 创建一个和累积梯度形状一样的全零张量,并将其赋值给累积梯度,实现梯度清零 + zero_grad = P.ZerosLike()(cumulative_grad) + return F.assign(cumulative_grad, zero_grad) + +# 定义梯度积累类 +class GradientAccumulation(Cell): + """ + After accumulating the gradients of multiple steps, call to optimize its update. + + Args: + max_accumulation_step (int): Steps to accumulate gradients. + optimizer (Cell): Optimizer used. + """ + def __init__(self, max_accumulation_step, optimizer): + super(GradientAccumulation, self).__init__() + self._max_accumulation_step = max_accumulation_step + self.optimizer = optimizer + self.weights = optimizer.parameters + self.hyper_map = C.HyperMap() + # 创建用于累积梯度的张量,初始值为全零 + self._grad_accumulation = self.weights.clone(prefix="grad_accumulation", init='zeros') + # 创建用于记录当前累积步数的参数,初始值为0 + self._accumulation_step = Parameter(Tensor(0, dtype=mstype.int32), name="accumulation_step") + + def construct(self, loss, grads): + # 使用梯度积累操作将当前步的梯度添加到累积梯度上 + loss = F.depend(loss, self.hyper_map(F.partial(gradient_accumulation_op, self._max_accumulation_step), + self._grad_accumulation, grads)) + # 增加累积步数 + self._accumulation_step += 1 + # 如果累积步数达到最大累积步数,执行优化器进行梯度更新,并重置累积步数 + if self._accumulation_step >= self._max_accumulation_step: + loss = F.depend(loss, self.optimizer(self._grad_accumulation)) + self._accumulation_step = 0 + + # 如果累积步数为0,执行梯度清零操作 + if self._accumulation_step == 0: + loss = F.depend(loss, self.hyper_map(F.partial(gradient_clear_op), self._grad_accumulation)) + + return loss diff --git a/mindspore/ccsrc/transform-update/grad_freeze.py b/mindspore/ccsrc/transform-update/grad_freeze.py new file mode 100644 index 00000000000..3efff8fdbba --- /dev/null +++ b/mindspore/ccsrc/transform-update/grad_freeze.py @@ -0,0 +1,454 @@ +# Copyright 2021 Huawei Technologies Co., Ltd +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ============================================================================ +"""grad freeze""" + +import numpy as np + +from mindspore.nn.cell import Cell +from mindspore.nn.optim import Optimizer +from mindspore.common import Tensor +from mindspore.common import dtype as mstype +from mindspore.nn.optim import LARS +from mindspore.nn.wrap.grad_reducer import DistributedGradReducer +from mindspore.ops import functional as F + +from .base import ParameterProcess +from .grad_accumulation import GradientAccumulation + +__all__ = ['GradientFreeze', 'FreezeOpt', 'freeze_cell'] + + +CONTINUOUS_STRATEGY = 0 +INTERVAL_STRATEGY = 1 + + +class FreezeOpt(Cell): + """ + Optimizer that supports gradients freezing training. + + Args: + opt (Cell): non-freezing optimizer instance, such as 'Momentum', 'SGD'. + train_parameter_groups (Union[tuple, list]): Groups of parameters for gradients freezing training. + train_strategy (Union[tuple(int), list(int), Tensor]): Strategy for gradients freezing training. + + Supported Platforms: + ``Ascend`` + """ + def __init__(self, opt, train_parameter_groups=None, train_strategy=None): + super(FreezeOpt, self).__init__() + # 检查非冻结优化器是否是 Optimizer 的实例 + # 初始化各种参数,包括是否使用 LARS 优化器,优化器类型等 + # 创建一个优化器列表 self.opts,用于存储梯度冻结训练中的优化器 + + # 检查是否传入的 opt 参数是 Optimizer 类的实例,如果不是则抛出异常 + if not isinstance(opt, Optimizer): + raise TypeError( + f"The first arg 'opt' must be an Optimizer instance, but got {type(opt)}") + + # 如果 train_strategy 参数被指定,但 train_parameter_groups 参数未被指定,抛出异常 + if train_strategy is not None and train_parameter_groups is None: + raise ValueError("When the 'train_strategy' is specified, the value of 'train_parameter_groups' " + "must also be specified") + + # 检查传入的 opt 是否为 LARS(Layer-wise Adaptive Rate Scaling)优化器的实例 + if isinstance(opt, LARS): + self.is_lars = True + self.opt_class = type(opt.opt)# 获取 LARS 内部的优化器类型 + self.opt_init_args = opt.opt.init_args # 获取 LARS 内部优化器的初始化参数 + self.lars_init_args = opt.init_args# 获取 LARS 自身的初始化参数 + self.single_opt = opt.opt# 获取 LARS 内部的优化器实例 + self.parameters = opt.opt.parameters# 获取优化器的参数列表 + self.learning_rate = opt.opt.init_learning_rate# 获取优化器的学习率 + self.dynamic_lr = opt.opt.dynamic_lr# 获取优化器是否使用动态学习率 + else: + self.is_lars = False + self.opt_class = type(opt)# 获取传入的非 LARS 优化器的类型 + self.opt_init_args = opt.init_args# 获取非 LARS 优化器的初始化参数 + self.single_opt = opt# 获取传入的非 LARS 优化器实例 + self.parameters = opt.parameters# 获取优化器的参数列表 + self.learning_rate = opt.init_learning_rate# 获取优化器的学习率 + self.dynamic_lr = opt.dynamic_lr# 获取优化器是否使用动态学习率 + + # 创建一个空的优化器列表 self.opts + self.opts = [] + # 如果 train_parameter_groups 参数未被指定,则默认创建 10 个参数组,每组包含部分优化器的参数 + if train_parameter_groups is None: + self.groups_num = 10 # 定义参数组的数量 + step = 6 # 每组参数包含的参数个数 + parameters = opt.parameters # 获取整个模型的参数列表 + # 将参数列表分割成多个参数组,每组包含 step 个参数 + train_parameter_groups = (tuple(parameters[(i * step):]) for i in range(self.groups_num)) + else: + # 如果用户指定了 train_parameter_groups,则检查其类型并获取参数组的数量 + if not isinstance(train_parameter_groups, (tuple, list)): + raise TypeError( + "The specified 'train_parameter_groups' should be tuple or list") + self.groups_num = len(train_parameter_groups) + + # 初始化训练策略 + self._init_train_strategy(train_strategy) + # 创建不同全局步数下的新学习率 + self._create_new_group_learning_rate() + + # 初始化优化器索引 + self.opt_index = 0 + + # 遍历每个参数组,为每个参数组生成一个相应的优化器实例,并存储在 self.opts 列表中 + for params in train_parameter_groups: + if not isinstance(params, (tuple, list)): + raise TypeError("The each element of 'train_parameter_groups' should be tuple or list " + "to store the Parameter") + # generate one-to-one opt corresponding to the parameter group + # 生成一个与参数组对应的优化器实例,并添加到 self.opts 列表中 + self.opts.append(self._generate_new_optimizer(params)) + self.opt_index += 1 + + """初始化用于梯度冻结的训练策略。 + + Args: + train_strategy: 用于指定梯度冻结的训练策略的参数。可以是以下之一: + - None:表示不使用任何特定的训练策略。 + - 整数列表或元组:包含整数的列表或元组,用于指定哪些梯度应该被冻结。 + - Tensor:一个包含整数的张量,其中每个整数表示哪些梯度应该被冻结。 + + Raises: + ValueError: 如果train_strategy的类型不合法或包含不合法的元素。 + TypeError: 如果train_strategy的类型不是None、tuple、list或Tensor。 + """ + def _init_train_strategy(self, train_strategy): + """Init train strategy for gradient freeze.""" + # 检查传入的train_strategy是否是元组或列表 + if isinstance(train_strategy, (tuple, list)): + # 如果是,遍历其中的元素 + for ele in train_strategy: + # 检查每个元素是否为整数,如果不是则引发异常 + if not isinstance(ele, int): + raise ValueError( + "The element in train_strategy should be int number") + # 如果所有元素都是整数,将train_strategy转换为int32类型的张量,并赋值给self.train_strategy + self.train_strategy = Tensor(train_strategy, mstype.int32) + # 如果train_strategy是Tensor类型 + elif isinstance(train_strategy, Tensor): + # 检查Tensor的维度是否为1,dtype是否为int32,如果不是则引发异常 + if train_strategy.ndim != 1 or train_strategy.dtype != mstype.int32: + raise ValueError("When train_strategy is a Tensor, the dimension should be 1 and " + "the dtype should be int32") + # 如果满足条件,直接赋值给self.train_strategy + self.train_strategy = train_strategy + # 如果train_strategy为None + elif train_strategy is None: + # 直接将self.train_strategy设置为None + self.train_strategy = None + # 如果train_strategy不是上述三种类型之一,引发类型错误异常 + else: + raise TypeError( + "The specified 'train_strategy' should be None, tuple, list or Tensor") + + def _create_new_group_learning_rate(self): + """Create new learning rate for different global step.""" + """创建不同全局步骤的新学习率。""" + # 初始化一个空的动态学习率列表,列表的数量等于组数(groups_num) + self.dynamic_learning_rate = [[] for _ in range(self.groups_num)] + # 如果当前的学习率为None,将学习率设置为单一优化器(single_opt)的学习率 + if self.learning_rate is None: + self.learning_rate = self.single_opt.learning_rate + return + # 如果启用了动态学习率(dynamic_lr)且学习率是列表类型(list)且训练策略是Tensor类型 + if self.dynamic_lr and isinstance(self.learning_rate, list) and isinstance(self.train_strategy, Tensor): + train_strategy = list(self.train_strategy.asnumpy()) + if len(self.learning_rate) <= len(train_strategy): + for i, lr in enumerate(self.learning_rate): + self.dynamic_learning_rate[train_strategy[i]].append(lr) + + def _generate_new_optimizer(self, params): + """Generate new optimizer.""" + + if self.dynamic_learning_rate[self.opt_index]: + lr = self.dynamic_learning_rate[self.opt_index] + else: + lr = self.learning_rate + if not self.is_lars: + opt = self.opt_class(params=params, learning_rate=lr, **self.opt_init_args) + opt._update_local_parameters_name("boost_{}".format(self.opt_index)) # pylint: disable=W0212 + else: + opt = LARS(self.opt_class(params=params, learning_rate=lr, **self.opt_init_args), + **self.lars_init_args) + opt.opt._update_local_parameters_name("boost_{}".format(self.opt_index)) # pylint: disable=W0212 + opt._update_local_parameters_name("boost_{}".format(self.opt_index)) # pylint: disable=W0212 + return opt + + +class _TrainFreezeCell(Cell): + r""" + Gradient freezing training network. + + Args: + net (Cell): The training network.训练网络。接受一个神经网络模型作为参数。 + sens (numbers.Number): The scaling number to be filled as the input of backpropagation. Default value is 1.0.用作反向传播输入的缩放值。默认值为 1.0 + grad (tuple(Tensor)): The gradients of network parameters and inputs.网络参数和输入的梯度。这里 grad 是一个包含参数和输入梯度的元组。 + grad_reducer (Cell): Constructs a gradient reducer Cell, which applies communication and average operations on + single-process gradient values.构建一个梯度缩减器 Cell,用于在单进程梯度值上执行通信和平均操作。 + use_grad_accumulation (bool): Whether use grad accumulation.是否使用梯度累积。指示是否启用梯度的累积。 + optimizer (Union[Cell]): Optimizer for updating the weights.用于更新权重的优化器。接受一个优化器作为参数。 + max_accumulation_step (numbers.Number): Max grad accumulation steps. Default: 1.0最大梯度累积步骤。默认值为 1.0。 + + + Supported Platforms: + ``Ascend`` + """ + # 初始化方法,接受一系列参数,用于配置训练网络 + def __init__(self, net, sens, grad, grad_reducer, use_grad_accumulation, optimizer, max_accumulation_step=1): + # 调用父类 Cell 的初始化方法,并关闭自动前缀 + super(_TrainFreezeCell, self).__init__(auto_prefix=False) + # 将传入的参数保存为类成员变量 + self.net = net + self.grad = grad + self.grad_reducer = grad_reducer + self.opt = optimizer + self.parameters = optimizer.parameters + self.sens = sens + self.use_grad_accumulation = use_grad_accumulation + self.max_accumulation_step = max_accumulation_step + # 如果启用了梯度累积 + if use_grad_accumulation: + # 创建 GradientAccumulation 对象,用于管理梯度累积 + self.grad_accumulation = GradientAccumulation( + self.max_accumulation_step, self.optimizer) + # 构建方法,定义了训练过程中的具体操作 + def construct(self, *inputs): + # 计算网络的损失 + loss = self.net(*inputs) + # 创建一个与损失相同数据类型和形状的感知度张量 + sens = F.fill(loss.dtype, loss.shape, self.sens) + # 计算网络参数的梯度,grad 是一个包含参数梯度的元组 + grads = self.grad(self.net, self.parameters)(*inputs, sens) + # 使用梯度缩减器对梯度进行缩减 + grads = self.grad_reducer(grads) + # 如果启用了梯度累积 + if self.use_grad_accumulation: + # 将损失和梯度传递给梯度累积器,并获得新的损失值 + loss = self.grad_accumulation(loss, grads) + else: + # 否则,执行优化器来更新权重,并将损失与梯度之间建立依赖关系 + loss = F.depend(loss, self.opt(grads)) + # 返回损失值 + return loss + + +class GradientFreeze: + r""" + Freezing the gradients of some layers randomly. The number and + probability of frozen layers can be configured by users + + Args: + param_groups (Union[tuple, list]): Groups of parameters for gradients freezing training. + freeze_type (int): Strategy of gradients freezing training. + freeze_p (float): probability of gradients freezing training. + total_steps (numbers.Number): Steps of the whole training. + + Examples: + >>> gradient_freeze_class = boost.GradientFreeze(10, 1, 0.5, 2000) + >>> network, optimizer = gradient_freeze_class.freeze_generate(network, optimizer) + """ + # 初始化方法,接受一系列参数来配置对象 + def __init__(self, param_groups, freeze_type, freeze_p, total_steps): + # 保存传入的参数为对象的成员变量 + self._param_groups = param_groups # 参数组,包含需要进行参数冻结的参数 + self._freeze_type = freeze_type # 冻结类型,指定如何冻结参数,可能的取值有 'epoch' 或 'step' + self._freeze_p = freeze_p # 冻结比例,用于控制参数冻结的比例 + self._total_steps = total_steps # 总步数,用于计算训练的总步数 + self.grad_reducer = F.identity # grad_reducer 初始化为 F.identity,通常用于梯度的标识操作 + self._param_processer = ParameterProcess() # 创建 ParameterProcess 对象,并将其保存为成员变量 + + # 定义一个方法,用于将参数分组以进行梯度冻结训练 + def split_parameters_groups(self, net, freeze_para_groups_number): + r""" + Split parameter groups for gradients freezing training. + + Args: + net (Cell): The training network. + freeze_para_groups_number (int): The number of gradient freeze groups. + """ + # 创建一个空列表,用于存储分组后的参数 + grouped_params = [] + # 遍历可训练参数 + tmp = [] + for para in net.trainable_params(): + name = para.name + # ensure 'bn' after 'conv' is not split + # 如果参数名称中包含 'bn' 或 'bias',则将参数添加到临时列表中 + if 'bn' in name or 'bias' in name: + tmp.append(para) + # 如果临时列表中的参数数量已经达到 3 个或更多 + elif len(tmp) >= 3: + # 将临时列表中的参数添加到分组参数列表中,并重新初始化临时列表 + grouped_params.append(tmp) + tmp = [para] + # 否则,继续将参数添加到临时列表中 + else: + tmp.append(para) + # 处理可能剩余的参数 + if tmp: + grouped_params.append(tmp) + # 计算每个冻结组之间的步幅,以确保平均分配参数到不同的冻结组 + stride = len(grouped_params) // freeze_para_groups_number + # 创建冻结后的参数组列表,每个元素是一个参数列表 + freeze_grouped_params = [sum(grouped_params[i * stride:], []) + for i in range(freeze_para_groups_number)] + # 返回分组后的冻结参数列表 + return freeze_grouped_params + + #函数签名 + def generate_freeze_index_sequence(self, parameter_groups_number, freeze_strategy, freeze_p, total_steps): + r""" + Generate index sequence for gradient freezing training. + + Args: + parameter_groups_number (int): The number of parameter groups. + freeze_strategy (int): Gradient freeze grouping strategy, select from [0, 1]. + freeze_p (float): Gradient freezing probability. + total_steps (int): Total training steps. + """ + #计算total_step,略高于total_steps的101% + total_step = int(total_steps * 1.01) + #如果parameter_groups_number小于等于1,则返回一个长度为total_step的零列表 + if parameter_groups_number <= 1: + return [0 for _ in range(total_step)] + # local continuous freezing training strategy, as '00001234' + #检查freeze_strategy是否等于CONTINUOUS_STRATEGY常量。 + if freeze_strategy == CONTINUOUS_STRATEGY: + #根据提供的公式使用freeze_p和parameter_groups_number计算zero_cnt。 + zero_cnt = int( + freeze_p * (parameter_groups_number - 1) / (1 - freeze_p) + 0.5) + #创建一个列表sub_idx,其中包括zero_cnt次重复的零,然后是从1到parameter_groups_number的序列。 + sub_idx = [0] * zero_cnt + list(range(1, parameter_groups_number)) + #通过重复sub_idx,直到其长度等于total_step,来生成freeze_idxes列表。 + freeze_idxes = [] + while len(freeze_idxes) < total_step: + freeze_idxes += sub_idx + #返回freeze_idxes列表。 + return freeze_idxes + # interval freezing training strategy, as '01020304' + #检查freeze_strategy是否等于INTERVAL_STRATEGY常量。 + if freeze_strategy == INTERVAL_STRATEGY: + #初始化index_all,其中包括从1到parameter_groups_number的序列,计算每个索引的概率prob,并初始化freeze_idxes、zero_cnt和freeze_cnt + index_all = list(range(1, parameter_groups_number)) + prob = [x / sum(index_all) for x in index_all] + freeze_idxes = [0] + zero_cnt = 1 + freeze_cnt = 0 + #启动一个循环,直到freeze_idxes达到所需的长度。计算当前的freeze_p_cur。 + while len(freeze_idxes) < total_step: + freeze_p_cur = 1.0 * freeze_cnt / (zero_cnt + freeze_cnt) + #检查当前的冻结概率是否小于1 - freeze_p。 + if freeze_p_cur < 1 - freeze_p: + #如果条件成立,则根据提供的概率从index_all中随机选择一个索引,并将其附加到freeze_idxes,然后增加freeze_cnt。 + freeze_idxes.append( + int(np.random.choice(index_all[::-1], p=prob))) + freeze_cnt += 1 + #如果条件不成立,则将零附加到freeze_idxes,并增加zero_cnt。 + else: + freeze_idxes.append(0) + zero_cnt += 1 + #返回freeze_idxes列表。 + return freeze_idxes + #如果freeze_strategy既不是CONTINUOUS_STRATEGY也不是INTERVAL_STRATEGY,则引发一个ValueError,其中包含一条消息,指示不支持的策略。 + raise ValueError( + f"Unsupported freezing training strategy '{freeze_strategy}'") + + # 生成冻结网络和优化器的方法 + def freeze_generate(self, network, optimizer): + r""" + Generate freeze network and optimizer. + + Args: + network (Cell): The training network. + optimizer (Cell): Optimizer for updating the weights. + """ + # 将网络的参数分成多个参数组 + train_para_groups = self.split_parameters_groups( + network, self._param_groups) + # 对每个参数组进行处理,生成每个参数组的参数 + for i in range(self._param_groups): + train_para_groups[i] = self._param_processer.generate_group_params(train_para_groups[i], + optimizer.init_params['params']) + # 生成冻结索引策略 + train_strategy = self.generate_freeze_index_sequence( + self._param_groups, self._freeze_type, self._freeze_p, self._total_steps) + # 创建带有冻结功能的优化器 + optimizer = FreezeOpt(optimizer, train_para_groups, train_strategy) + # 返回网络和优化器 + return network, optimizer + + +# 生成带有冻结功能的网络和优化器的方法 +def freeze_cell(reducer_flag, network, optimizer, sens, grad, use_grad_accumulation, mean=None, degree=None, + max_accumulation_step=1): + r""" + Generate freeze network and optimizer. + + Args: + reducer_flag (bool): Reducer flag. + network (Cell): The training network. + optimizer (Cell): Optimizer for updating the weights. + sens (numbers.Number): The scaling number. + grad (tuple(Tensor)): Tuple of gradient tensors. + use_grad_accumulation (bool): Use gradient accumulation flag. + mean (bool): Gradients mean flag. default: None. + degree (int): Device number. default: None. + max_accumulation_step (int): Max accumulation steps. default: 1. + + Examples: + >>> import numpy as np + >>> from mindspore import Tensor, Parameter, nn + >>> import mindspore.ops as ops + >>> from mindspore.boost.grad_freeze import freeze_cell + >>> + >>> class Net(nn.Cell): + ... def __init__(self, in_features, out_features): + ... super(Net, self).__init__() + ... self.weight = Parameter(Tensor(np.ones([in_features, out_features]).astype(np.float32)), + ... name='weight') + ... self.matmul = ops.MatMul() + ... + ... def construct(self, x): + ... output = self.matmul(x, self.weight) + ... return output + ... + >>> in_features, out_features = 16, 10 + >>> network = Net(in_features, out_features) + >>> optimizer = nn.Momentum(net.trainable_params(), learning_rate=0.1, momentum=0.9) + >>> grad = ops.GradOperation(get_by_list=True, sens_param=True) + >>> freeze_nets = freeze_cell(False, network, optimizer, 1.0, grad, False, None, None, 1) + """ + # 如果reducer_flag为真,表示使用了分布式梯度聚合 + if reducer_flag: + # 创建参数处理器 + param_processer = ParameterProcess() + # 创建梯度reducers,将每个optimizer的参数传递给参数处理器 + grad_reducers = (DistributedGradReducer(param_processer.assign_parameter_group(opt.parameters), + mean, degree) for opt in optimizer.opts) + # 创建冻结网络的元组,每个网络对应一个optimizer + freeze_nets = tuple(_TrainFreezeCell(network, sens, grad, reducer, + use_grad_accumulation, opt, max_accumulation_step) + for reducer, opt in zip(grad_reducers, optimizer.opts)) + else: + # 如果没有使用分布式梯度聚合,直接创建冻结网络的元组,每个网络对应一个optimizer + freeze_nets = tuple(_TrainFreezeCell(network, sens, grad, F.identity, + use_grad_accumulation, opt, max_accumulation_step) + for opt in optimizer.opts) + + # 返回冻结网络的元组 + return freeze_nets diff --git a/mindspore/ccsrc/transform-update/graph_builder.cc b/mindspore/ccsrc/transform-update/graph_builder.cc new file mode 100644 index 00000000000..e40f894e1b9 --- /dev/null +++ b/mindspore/ccsrc/transform-update/graph_builder.cc @@ -0,0 +1,70 @@ +/** + * Copyright 2019 Huawei Technologies Co., Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "include/transform/graph_ir/graph_builder.h" + +#include + +#include "ops/math_ops.h" + +namespace mindspore { +namespace transform { +// 该函数用于构建 MDDataset(MindSpore Dataset)的图形(Graph)。 +DfGraphPtr BuildMDDatasetGraph(const DatasetGraphParam ¶m) { + MS_LOG(INFO) << "BuildMDDatasetGraph."; // 记录日志,表示正在构建 MDDataset 图形 + + // InitData + // 创建一个操作符 "InitData",使用参数 "init_data_tmp" 作为操作符的名称,并设置属性 "channel_name" 为 + // param.queue_name() 的值 + auto d = ge::op::InitData("init_data_tmp").set_attr_channel_name(param.queue_name()); + + // set graph inputs & outputs + // 设置图形的输入和输出 + std::vector inputs{d}; // 将 "InitData" 操作符设置为图形的输入 + std::vector outputs{d}; // 将 "InitData" 操作符设置为图形的输出 + + // 创建一个名为 "dataset" 的 MDDataset 图形,并使用 "dataset_graph" 指针指向该图形 + DfGraphPtr dataset_graph = std::make_shared("dataset"); + + // 将输入和输出设置到 MDDataset 图形中 + (void)dataset_graph->SetInputs(inputs); + (void)dataset_graph->SetOutputs(outputs); + + return dataset_graph; // 返回构建好的 MDDataset 图形的指针 +} + +// 该函数用于构建数据集的图形(Graph)。 +Status BuildDatasetGraph(const DatasetGraphParam ¶m, const std::string &phase) { + Status ret; // 存储函数执行结果的状态对象 + std::string graph_name = phase; // 以给定的 'phase' 参数作为图形的名称 + + MS_LOG(INFO) << "BuildDatasetGraph begin. phase is " << phase; // 记录日志,表示开始构建数据集图形 + MS_LOG(INFO) << "param is " << param.ToString() << "."; // 记录日志,打印参数 'param' 的详细信息 + + // 调用 BuildMDDatasetGraph 函数构建 MDDataset 图形,并将构建好的图形指针存储在 'dataset_graph' 变量中 + DfGraphPtr dataset_graph = BuildMDDatasetGraph(param); + // 将构建好的 MDDataset 图形添加到图形管理器中,并使用 'graph_name' 作为图形的名称 + ret = DfGraphManager::GetInstance().AddGraph(graph_name, dataset_graph); + // 根据 AddGraph 函数的执行结果,进行相应的日志记录 + if (ret != Status::SUCCESS) { // 如果添加图形失败,记录错误日志 + MS_LOG(ERROR) << "BuildDatasetGraph failed."; + } else { // 如果添加图形成功,记录结束日志 + MS_LOG(INFO) << "BuildDatasetGraph end."; + } + return ret; // 返回函数执行结果的状态对象 +} +} // namespace transform +} // namespace mindspore diff --git a/mindspore/ccsrc/transform-update/graph_pattern.py b/mindspore/ccsrc/transform-update/graph_pattern.py new file mode 100644 index 00000000000..5e09fe47584 --- /dev/null +++ b/mindspore/ccsrc/transform-update/graph_pattern.py @@ -0,0 +1,249 @@ +# Copyright 2020 Huawei Technologies Co., Ltd +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ============================================================================ +"""Patterns for describing graphs""" +#鎻忚堪鍥惧舰鐨勬ā寮 +from mindspore.ops import Primitive +from mindspore.common.tensor import Tensor +from mindspore._c_expression import Pattern, OneOf_, Prim_, Call_, NoneOf_, Any, NewTensor_, NewParameter_, Imm + +__all__ = [ + "OneOf", + "Prim", + "Call", + "NoneOf", + "Any", + "NewTensor", + "NewParameter", + "Imm" +] + + +class OneOf(OneOf_): + r""" + Express a pattern which allows a list of patterns. + 琛ㄨ揪涓涓厑璁告ā寮忓垪琛ㄧ殑妯″紡 + """ + def __init__(self, patterns=None): + r""" + Args: + patterns(Union[:class:`mindspore.graph_utils.graph_pattern`, + tuple[:class:`mindspore.graph_utils.graph_pattern`], + list[:class:`mindspore.graph_utils.graph_pattern`]]): list of allowed patterns, + each element should be one of the exposed Pattern instance. + + Raises: + TypeError: raise type error for invalid inputs. + """ + self.patterns = patterns + if isinstance(patterns, Pattern): + OneOf_.__init__(self, [patterns]) + #濡傛灉patterns鏄痬indspore.graph_utils.graph_pattern绫荤殑瀹炰緥锛屽垯浣跨敤鍖呭惈patterns鐨勫崟涓鍏冪礌鍒楄〃鍒濆鍖栧熀绫籓neOf_銆 + elif isinstance(patterns, (tuple, list)) and all(isinstance(pattern, Pattern) for pattern in patterns): + OneOf_.__init__(self, patterns) + #濡傛灉patterns鏄厓缁勬垨鍒楄〃锛屽苟涓斿叾涓殑鎵鏈夊厓绱犻兘鏄痬indspore.graph_utils.graph_pattern绫荤殑瀹炰緥锛 + #鍒欎娇鐢ㄥ寘鍚玴atterns涓ā寮忕殑鍒楄〃鍒濆鍖栧熀绫籓neOf_ + else: + raise TypeError(f"Expect patterns to be a list of Patterns/Pattern, got : {patterns}") + #濡傛灉patterns鏄叾浠栫被鍨嬬殑瀵硅薄锛屾垨鑰呭叾涓寘鍚笉鏄痬indspore.graph_utils.graph_pattern绫荤殑瀹炰緥锛 + #鍒欐姏鍑篢ypeError骞堕檮甯︾浉搴旂殑閿欒娑堟伅銆 + + +class Prim(Prim_): + r""" + Express a pattern of certain primitive type(s). + 琛ㄧず鏌愮鍩烘湰绫诲瀷鐨勬ā寮 + + NOTE: + This pattern will match and only match the primitive value node. If matching primitive CNode is needed, + please refer to CallWith pattern. + """ + def __init__(self, types, name=None): + r""" + Args: + 鏀寔涓夌涓嶅悓鐨則ypes鍙傛暟杈撳叆鏂瑰紡锛 + types (Union[str, :class:`mindspore.ops.Primitive`, list[:class:`mindspore.ops.Primitive`], + tuple[:class:`mindspore.ops.Primitive`]): + Specify allowed types. + If it is a string, the form could be + 1) a single primitive type, e.g. 'Conv2D'鑻ypes鏄竴涓瓧绗︿覆锛屽垯鍙互鏄崟涓熀鏈被鍨嬶紝渚嬪 'Conv2D' + 2) a set of primitive types separated by '|', e.g. 'MatMul|Conv2D' + 澶氫釜鍩烘湰绫诲瀷锛岀敤|绗﹀彿鍒嗛殧锛屼緥濡 'MatMul|Conv2D' + It can also be a Primitive or a list/tuple of Primitives, e.g. [ops.Conv2D(1, 6)] + 濡傛灉types鏄竴涓狿rimitive瀵硅薄锛屽垯琛ㄧず浠呭尮閰嶈鍏蜂綋鐨勫熀鏈被鍨嬨 + 濡傛灉types鏄竴涓垪琛ㄦ垨鍏冪粍锛屽苟涓斿垪琛ㄤ腑鐨勫厓绱犻兘鏄疨rimitive瀵硅薄锛屽垯琛ㄧず鍖归厤澶氫釜鍩烘湰绫诲瀷銆 + name (str): name of the pattern, optional. Default: None. + + Raises: + TypeError: raise type error for invalid argument. + """ + # 濡傛灉鎻愪緵浜 name 鍙傛暟锛屼絾涓嶆槸瀛楃涓茬被鍨嬶紝鍒欐姏鍑 TypeError + if name is not None and not isinstance(name, str): + raise TypeError(f"Expect string, got : {name}") + # 灏嗗弬鏁 name 璧嬪肩粰瀵硅薄鐨 name 灞炴 + self.name = name + # 鏍规嵁 types 鍙傛暟鐨勭被鍨嬭繘琛屼笉鍚岀殑澶勭悊 + if isinstance(types, str): + if self.name is None: + self.name = types + self.types = types.split('|') + # 濡傛灉 types 鏄竴涓瓧绗︿覆锛屽垯灏嗗叾鎸夌収 '|' 绗﹀彿鎷嗗垎锛屽苟瀛樺偍鍦 self.types 涓 + elif isinstance(types, Primitive): + # 濡傛灉 types 鏄竴涓 Primitive 瀵硅薄锛屽垯琛ㄧず鍙厑璁稿尮閰嶈繖涓叿浣撶殑鍩烘湰绫诲瀷 + if self.name is None: + self.name = types.name + self.types = [types] + elif isinstance(types, (tuple, list)) and all(isinstance(tp, Primitive) for tp in types): + # 濡傛灉 types 鏄竴涓寘鍚 Primitive 瀵硅薄鐨勫垪琛ㄦ垨鍏冪粍锛屽垯琛ㄧず鍏佽鍖归厤澶氫釜鍩烘湰绫诲瀷 + if self.name is None: + self.name = "" + for prim in types: + self.name += prim.name + self.types = types + else: + # 濡傛灉 types 鍙傛暟涓嶆槸鍏佽鐨勭被鍨嬶紝鍒欐姏鍑 TypeError + raise TypeError(f"Expecting a primitive type string or a list of Primitives, got : {types}") + # 璋冪敤鐖剁被 Prim_ 鐨勫垵濮嬪寲鏂规硶锛屽皢澶勭悊濂界殑 types 鍜 name 浣滀负鍙傛暟浼犻掔粰鐖剁被 + Prim_.__init__(self, self.types, self.name) + + +class Call(Call_): + #鍒濆鍖栧璞$殑灞炴 + r""" + Express a primitive CNode. + """ + def __init__(self, prim_pattern, inputs=None): + r""" + Args: + prim_pattern (Union[str, :class:`mindspore.graph_utils.graph_pattern.IsPrimTypeOf`, + :class:`mindspore.ops.Primitive`]): Primitive ValueNode in the Primitive CNode. + inputs (Union[list[:class:`mindspore.graph_utils.graph_pattern`], + tuple[:class:`mindspore.graph_utils.graph_pattern`]]): + Specify inputs pattern for the primitive(s), optional. If None, accepts any inputs; if specified, input + patterns should be of right order and each element should be one of the exposed Pattern instance. + 妯″紡鐨勯『搴忓簲璇ユ纭紝姣忎釜鍏冪礌搴旇鏄叕寮鐨凱attern瀹炰緥涔嬩竴 + Raises: + TypeError: raise type error for invalid argument. + """ + #妫鏌rim_pattern鐨勭被鍨嬫槸鍚︽槸Pattern銆丳rimitive鎴栧瓧绗︿覆绫诲瀷銆 + #濡傛灉涓嶆槸杩欎簺绫诲瀷涔嬩竴锛屽垯鎶涘嚭TypeError锛岃〃绀烘湡鏈沺rim_pattern鏄疨attern銆丳rimitive鎴栧瓧绗︿覆绫诲瀷銆 + if not isinstance(prim_pattern, (Pattern, str, Primitive)): + raise TypeError(f"Expect prim_pattern to be Pattern, Primitive or string, got : {prim_pattern}") + #灏嗕紶鍏ョ殑prim_pattern璧嬪肩粰瀹炰緥鍙橀噺self.prim_pattern锛屼互淇濆瓨鍘熻妯″紡锛圥rimitive Pattern锛夋垨鍘熻鍚嶇О锛圥rimitive name锛 + self.prim_pattern = prim_pattern + #灏唖elf.inputs鍒濆鍖栦负绌哄垪琛 + self.inputs = [] + #None锛氫粈涔堥兘涓嶅仛 + if inputs is None: + pass + #妫鏌nputs鏄惁涓篜attern鐨勫垪琛ㄦ垨鍏冪粍锛屼笖鍏朵腑鐨勬墍鏈夊厓绱犻兘鏄疨attern绫诲瀷銆 + elif isinstance(inputs, (tuple, list)) and all(isinstance(input, Pattern) for input in inputs): + self.inputs = inputs + #濡傛灉inputs涓嶆弧瓒充笂杩版潯浠讹紝鎶涘嚭TypeError锛岃〃绀烘湡鏈沬nputs鏄疨attern鐨勫垪琛ㄣ + else: + raise TypeError(f"Expect inputs to be a list of Patterns, got : {inputs}") + Call_.__init__(self, self.prim_pattern, self.inputs) + #璋冪敤鍩虹被Call_鐨勬瀯閫犲嚱鏁版潵瀹屾垚鍒濆鍖栵紝浼犲叆self.prim_pattern浣滀负鍘熻妯″紡鎴栧悕绉帮紝 + #浠ュ強self.inputs浣滀负杈撳叆妯″紡鍒楄〃銆 + +class NoneOf(NoneOf_): + r""" + Express a pattern which forbids a list of patterns. + 琛ㄨ揪涓涓姝㈡ā寮忓垪琛ㄧ殑妯″紡銆 + NOTE: + NoneOf pattern should not be the root pattern. + #NoneOf妯″紡涓嶆槸鏍规ā寮 + """ + def __init__(self, patterns=None): + r""" + Args: + patterns(Union[list[:class:`mindspore.graph_utils.graph_pattern`]]: list of forbidden patterns, each + element should be one of the exposed Pattern instance. + 绂佺敤妯″紡鍒楄〃锛屾瘡涓厓绱犲簲璇ユ槸鍏紑鐨凱attern瀹炰緥涔嬩竴銆 + Raises: + TypeError: raise type error for invalid argument. + """ + self.patterns = patterns + if patterns is None: + NoneOf_.__init__(self, ()) + #濡傛灉 patterns 鍙傛暟涓 None锛屽嵆鏈彁渚 patterns锛屽垯灏 None 浼犻掔粰 NoneOf_ 绫荤殑鍒濆鍖栨柟娉曘 + elif isinstance(patterns, Pattern): + NoneOf_.__init__(self, [patterns]) + #濡傛灉 patterns 鍙傛暟鏄竴涓崟鐙殑 Pattern 瀵硅薄锛屽垯灏嗗寘鍚繖涓 Pattern 瀵硅薄鐨勫垪琛ㄤ紶閫掔粰 NoneOf_ 绫荤殑鍒濆鍖栨柟娉曘 + elif isinstance(patterns, (tuple, list)) and all(isinstance(pattern, Pattern) for pattern in patterns): + NoneOf_.__init__(self, patterns) + #濡傛灉 patterns 鍙傛暟鏄竴涓垪琛ㄦ垨鍏冪粍锛屽苟涓斿垪琛ㄤ腑鐨勫厓绱犻兘鏄 Pattern 瀵硅薄锛 + #鍒欏皢鏁翠釜鍒楄〃浼犻掔粰 NoneOf_ 绫荤殑鍒濆鍖栨柟娉曘 + else: + raise TypeError(f"Expect list of Patterns/Pattern, got : {patterns}") + + +class NewTensor(NewTensor_): + r""" + New Tensor to be used in the target. + 瑕佸湪鐩爣涓娇鐢ㄧ殑鏂板紶閲 + """ + def __init__(self, input_tensor): + r""" + Args: + input_tensor(:class:`mindspore.common.tensor.Tensor`): new tensor to be used in the target. + 瑕佸湪鐩爣涓娇鐢ㄧ殑鏂板紶閲 + Raises: + TypeError: raise type error for invalid argument. + """ + self.input_tensor = input_tensor + # 灏嗕紶鍏ョ殑 input_tensor 鍙傛暟璧嬪肩粰瀵硅薄鐨 input_tensor 灞炴 + if isinstance(input_tensor, Tensor): + # 妫鏌 input_tensor 鍙傛暟鐨勭被鍨 + NewTensor_.__init__(self, input_tensor) + # 濡傛灉 input_tensor 鏄竴涓 Tensor 瀵硅薄锛屽垯璋冪敤 NewTensor_ 绫荤殑鍒濆鍖栨柟娉曪紝骞朵紶鍏 input_tensor 浣滀负鍙傛暟 + else: + raise TypeError(f"Expect input_tensor to be a Tensor锛 got : {input_tensor}") + # 濡傛灉 input_tensor 鍙傛暟涓嶆槸 Tensor 瀵硅薄锛屽垯鎶涘嚭 TypeError锛屾寚绀鸿緭鍏ュ弬鏁扮被鍨嬮敊璇 + + +class NewParameter(NewParameter_): + r""" + New Parameter to be used in the target. + """ + def __init__(self, para_name, default_tensor, requires_grad=False, layerwise_parallel=False): + r""" + Args: + para_name(str): name for the new Parameter. + default_tensor(:class:`mindspore.common.tensor.Tensor`): default value for the new Parameter. + requires_grad(bool): True if the parameter requires gradient. Default: True. + 濡傛灉鍙傛暟闇瑕佹搴︼紝鍒欎负True + layerwise_parallel(bool): switch for layerwise parallel mode. Default: False. + Layerwise_parallel (bool):鍒嗗眰骞惰妯″紡寮鍏炽 + + Raises: + TypeError: raise type error for invalid argument. + """ + # 灏嗕紶鍏ョ殑 para_name銆乨efault_tensor銆乺equires_grad 鍜 layerwise_parallel 鍙傛暟璧嬪肩粰瀵硅薄鐨勭浉搴斿睘鎬 + self.para_name = para_name + self.default_tensor = default_tensor + self.requires_grad = requires_grad + self.layerwise_parallel = layerwise_parallel + # 妫鏌ヤ紶鍏ョ殑鍙傛暟绫诲瀷鏄惁姝g‘锛屽苟鏍规嵁缁撴灉閫夋嫨鍒濆鍖栫埗绫 NewParameter_ + if isinstance(para_name, str) and isinstance(default_tensor, Tensor) and isinstance(requires_grad, bool) and\ + isinstance(layerwise_parallel, bool): + # 濡傛灉 para_name 鏄竴涓瓧绗︿覆锛宒efault_tensor 鏄竴涓 Tensor 瀵硅薄锛宺equires_grad 鍜 layerwise_parallel 閮芥槸甯冨皵鍊硷紝 + # 鍒欒皟鐢 NewParameter_ 绫荤殑鍒濆鍖栨柟娉曪紝骞朵紶鍏 para_name銆乨efault_tensor銆乺equires_grad 鍜 layerwise_parallel 浣滀负鍙傛暟銆 + NewParameter_.__init__(self, self.para_name, self.default_tensor, self.requires_grad, + self.layerwise_parallel) + else: + # 濡傛灉鏈変换浣曚竴涓弬鏁扮被鍨嬩笉姝g‘锛屽垯鎶涘嚭 TypeError锛屾寚绀鸿緭鍏ュ弬鏁扮被鍨嬮敊璇 + raise TypeError(f"Expect para_name(str), default_tensor(Tensor), requires_grad(bool), \ + layerwise_parallel(bool)锛 got : {para_name}, {default_tensor}, \ + {requires_grad}, {layerwise_parallel}") diff --git a/mindspore/ccsrc/transform-update/graph_runner.cc b/mindspore/ccsrc/transform-update/graph_runner.cc new file mode 100644 index 00000000000..b6ded43aa63 --- /dev/null +++ b/mindspore/ccsrc/transform-update/graph_runner.cc @@ -0,0 +1,234 @@ +/** + * Copyright 2019 Huawei Technologies Co., Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * Limitations under the License. + */ + +#include "include/transform/graph_ir/graph_runner.h" +#include +#include +#include + +#ifndef ENABLE_LITE_ACL +#include "pybind11/pybind11.h" +#endif +#include "utils/log_adapter.h" +#include "include/common/utils/config_manager.h" +#include "sys/time.h" +#include "include/common/utils/utils.h" +#include "include/common/utils/callbacks.h" +#ifdef ENABLE_D +#include "include/common/utils/callbacks_ge.h" +#endif +#include "utils/ms_context.h" + +#ifndef ENABLE_LITE_ACL +namespace py = pybind11; +#endif +namespace mindspore { +namespace transform { +// 该函数用于创建新的 GE(GraphEngine)会话。 +// 参数 'sess_options' 表示会话的选项。 +std::shared_ptr GraphRunner::NewSession(const SessionOptions &sess_options) { +#ifdef ENABLE_D + std::shared_ptr ret; // 用于存储创建的 GE 会话的智能指针 + auto ms_context = MsContext::GetInstance(); // 获取 MindSpore 上下文实例 + MS_EXCEPTION_IF_NULL(ms_context); // 检查上下文实例是否为空 + if (ms_context->backend_policy() == "ge") { // 检查当前的后端策略是否为 GE + ret = std::make_shared(sess_options); // 创建一个新的 GE 会话,并使用传入的选项 'sess_options' + if (ret == nullptr) { // 如果创建 GE 会话失败,抛出异常并记录错误日志 + MS_LOG(EXCEPTION) << "Create GE session failed!"; + } + MS_LOG(INFO) << "Create new GE session success!"; // 记录成功创建 GE 会话的日志 + return ret; // 返回新创建的 GE 会话的智能指针 + } +#endif + + MS_LOG(WARNING) << "no GE client, return nullptr!"; // 如果没有启用 GE 后端,记录警告日志并返回空指针 + return nullptr; // 返回空指针,表示没有创建 GE 会话 +} + +// 该构造函数用于初始化GraphRunner对象 +GraphRunner::GraphRunner(const GraphRunnerOptions &options) + : options_(options), graph_manager_(DfGraphManager::GetInstance()) { + // 检查并记录MindSpore的并行策略是否为ONE_DEVICE + if (ConfigManager::GetInstance().parallel_strategy() == ParallelStrategy::ONE_DEVICE) { + MS_LOG(INFO) << "ME run in ONE_DEVICE strategy mode"; + } + + if (options.sess_ptr != nullptr) { // 根据options中传入的sess_ptr判断是否已有现有会话 + sess_ = options.sess_ptr; + } else { // 若sess_ptr为空,则调用NewSession函数创建新的GE会话 + sess_ = NewSession(options.options); + if (sess_ == nullptr) { + MS_LOG(WARNING) << "graph runner sess_ is nullptr!"; + } + } + +#ifdef ENABLE_D + auto ms_context = MsContext::GetInstance(); + MS_EXCEPTION_IF_NULL(ms_context); + if (ms_context->backend_policy() == "ge") { + // register the callback function + // 注册回调函数 + if (sess_->RegisterCallBackFunc(callbacks::kCheckPoint, callbacks::CheckpointSaveCallback) != ge::GRAPH_SUCCESS) { + MS_LOG(EXCEPTION) << "register callback failed!"; + } + + if (sess_->RegisterCallBackFunc(callbacks::kSummary, callbacks::SummarySaveCallback) != ge::GRAPH_SUCCESS) { + MS_LOG(EXCEPTION) << "register summary callback failed!"; + } + } +#endif + // 从图形管理器获取所有的图形包装器 + std::vector wrappers = graph_manager_.GetAllGraphs(); + if (wrappers.empty()) { // 若图形包装器为空,记录日志并直接返回 + MS_LOG(INFO) << "The GraphManager is empty!!"; + return; + } +#ifdef ENABLE_D + if (ms_context->backend_policy() != "ge") { + return; + } + + for (auto &it : wrappers) { // 遍历所有图形包装器,并将未保存的图形添加到GE会话中 + std::set saved_graph = graph_manager_.GetSavedGraphs(); + auto iter_find = saved_graph.find(std::to_string(it->id_)); + if (iter_find != saved_graph.end()) { + continue; + } + MS_LOG(INFO) << "Add the graph " << (*it).name_ << " to GE, it's id is: " << (*it).id_; + graph_manager_.AddSavedGraphs(std::to_string(it->id_)); + (void)sess_->AddGraph(static_cast(it->id_), *(it->graph_ptr_), it->options_); + } +#endif +} + +// 该函数用于运行指定名称的图形(Graph)。 +Status GraphRunner::RunGraph(const RunOptions &options, const std::vector &inputs, + std::vector *outputs) { + std::string name = options.name; // 获取运行选项中的图形名称 + if (name.empty()) { // 如果图形名称为空,记录错误日志并返回无效参数状态 + MS_LOG(ERROR) << "The graph name is null"; + return Status::INVALID_ARGUMENT; + } + // 从图形管理器获取指定名称的图形包装器 + DfGraphWrapperPtr wrap_ptr = graph_manager_.GetGraphByName(name); + if (wrap_ptr == nullptr) { + MS_LOG(ERROR) << "Get graph form DfGraphManager failed!"; // 如果获取图形包装器失败,记录未找到状态的错误日志 + return Status::NOT_FOUND; + } + + if (wrap_ptr->graph_ptr_ == nullptr) { // 如果图形为空,记录警告日志并返回未找到状态 + MS_LOG(WARNING) << "The graph is null"; + return Status::NOT_FOUND; + } + + // call ge::RunGraph() to exec a graph; + // 调用 ge::RunGraph() 来执行图形计算 + std::vector ge_inputs; + std::vector ge_outputs; + + // 将输入参数 'inputs' 转换为 'ge_inputs',用于调用 GE 接口 + (void)std::transform(inputs.begin(), inputs.end(), std::back_inserter(ge_inputs), + [](const GeTensorPtr &i) { return *i; }); + + MS_LOG(INFO) << "Run the graph in GE with " << ge_inputs.size() << " inputs"; // 记录日志,表示正在运行 GE 图形,以及输入的数量 + + struct timeval start_time, end_time; + (void)gettimeofday(&start_time, nullptr); + +#ifdef ENABLE_D + auto ms_context = MsContext::GetInstance(); + MS_EXCEPTION_IF_NULL(ms_context); + if (ms_context->backend_policy() == "ge") { + if (sess_ == nullptr) { + MS_LOG(ERROR) << "The GE session is null, can't run the graph!"; // 如果 GE 会话为空,记录错误日志并返回执行失败状态 + return Status::FAILED; + } + ge::Status ret = sess_->RunGraph(static_cast(wrap_ptr->id_), ge_inputs, ge_outputs); // 调用 GE 接口运行图形 + if (ret != ge::GRAPH_SUCCESS) { + MS_LOG(ERROR) << "Call GE RunGraph Failed, ret is: " << ret; // 如果运行图形失败,记录错误日志并返回执行失败状态 + return Status::FAILED; + } + } +#else + ge_outputs.swap(ge_inputs); // 如果未启用 GE 后端,直接交换输入和输出,用于后续返回输出结果 +#endif + + (void)gettimeofday(&end_time, nullptr); + const uint64_t kUSecondInSecond = 1000000; + uint64_t cost = kUSecondInSecond * static_cast(end_time.tv_sec - start_time.tv_sec); + cost += static_cast(end_time.tv_usec - start_time.tv_usec); + MS_LOG(INFO) << "Call GE RunGraph Success in " << cost << " us, the GE outputs num is: " << ge_outputs.size(); + // 记录日志,表示图形计算成功执行,并打印执行时间和输出数量 + + // 将 GE 输出结果转换为 'outputs',用于返回给调用者 + (void)std::transform(ge_outputs.begin(), ge_outputs.end(), std::back_inserter(*outputs), + [](const GeTensor &ge_tensor) { return std::make_shared(ge_tensor); }); + + return Status::SUCCESS; // 返回执行成功状态,并带有输出结果 +} + +// 该函数用于运行指定名称的图形,并将输入和输出都转换为 MeTensorPtr 类型 +Status GraphRunner::RunGraph(const RunOptions &options, const std::vector &inputs, + std::vector *const outputs) { + std::vector ge_inputs; // 用于存储转换后的输入 GeTensorPtr + for (auto it : inputs) { + MS_EXCEPTION_IF_NULL(it); + MS_LOG(INFO) << "inputs tensor's data size is: " << (*it).DataSize(); // 打印输入 MeTensor 的数据大小 + auto shape = (*it).shape(); + std::string shape_str; + for (const auto &elem : shape) { + shape_str += std::to_string(elem); + shape_str += " "; + } + MS_LOG(INFO) << "inputs tensor's shape is: { " << shape_str << "}"; // 打印输入 MeTensor 的形状 + + // 将输入 MeTensor 转换为 GeTensor,转换后的格式为 kOpFormat_NCHW + auto ge_tensor_ptr = TransformUtil::ConvertTensor(it, kOpFormat_NCHW); + if (ge_tensor_ptr != nullptr) { + ge_inputs.emplace_back(ge_tensor_ptr); // 将转换后的 GeTensorPtr 添加到 ge_inputs 中 + } else { // 如果转换失败,记录日志并返回执行失败状态 + MS_LOG(INFO) << "Convert input Me tensor to Ge tensor failed. Abort this graph"; + return Status::FAILED; + } + } + + std::vector ge_outputs; // 用于存储运行图形后的输出 GeTensorPtr + Status ret; + { + // Release GIL before calling into (potentially long-running) C++ code + // 释放 GIL,然后调用 C++ 代码(可能是长时间运行的代码) +#ifndef ENABLE_LITE_ACL + py::gil_scoped_release release; +#endif + ret = RunGraph(options, ge_inputs, &ge_outputs); // 调用 RunGraph 函数运行图形计算 + } + if (ret != Status::SUCCESS) { + return ret; // 如果运行图形失败,直接返回执行失败状态 + } else { + // convert GeTensor to MeTensor + // 将输出 GeTensor 转换为 MeTensor,并将转换后的 MeTensorPtr 添加到 outputs 中 + for (auto &it : ge_outputs) { + auto tensor = TransformUtil::ConvertGeTensor(it); + if (tensor != nullptr) { + (void)outputs->emplace_back(tensor); + } + } + MS_LOG(INFO) << "Return Me tensor outputs num is: " << outputs->size(); // 打印返回的 MeTensor 数量 + return Status::SUCCESS; // 返回执行成功状态,并带有输出结果 + } +} +} // namespace transform +} // namespace mindspore diff --git a/mindspore/ccsrc/transform-update/group_loss_scale_manager.py b/mindspore/ccsrc/transform-update/group_loss_scale_manager.py new file mode 100644 index 00000000000..1476f9e1276 --- /dev/null +++ b/mindspore/ccsrc/transform-update/group_loss_scale_manager.py @@ -0,0 +1,187 @@ +# Copyright 2022 Huawei Technologies Co., Ltd +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ============================================================================ +"""Group Loss Scale Manager""" +from __future__ import absolute_import +from __future__ import division + +from mindspore.nn.cell import Cell +import mindspore.common.dtype as mstype +from mindspore.ops import operations as P +from mindspore.common.tensor import Tensor +from mindspore.common.parameter import Parameter, ParameterTuple + + +__all__ = ["GroupLossScaleManager"] + + +class GroupLossScaleManager(Cell): + """ + Enhanced hybrid precision algorithm supports multi-layer application of different loss scales and + dynamic updating of loss scales. + 澧炲己鍨嬫贩鍚堢簿搴︾畻娉曟敮鎸佷笉鍚屾崯鑰楄妯$殑澶氬眰搴旂敤鎹熷け瑙勬ā鐨勫姩鎬佹洿鏂般 + Args: + init_loss_scale (Number): The initialized loss scale value. + loss_scale_groups (List): The loss scale groups, which are divided from the param list. + + Inputs: + - **x** (Tensor) - The output of last operator. + - **layer1** (Int) - Current network layer value. + - **layer2** (Int) - Last network layer value. + + Outputs: + - **x** (Tensor) - The output of `_DynamicLossScale` operator. + + Supported Platforms: + ``Ascend`` + + Examples: + >>> import mindspore as ms + >>> from mindspore import boost, nn + >>> + >>> class Net(nn.Cell): + ... def __init__(self, enhanced_amp, num_class=10, num_channel=1): + ... super(Net, self).__init__() + ... self.conv1 = nn.Conv2d(num_channel, 6, 5, pad_mode='valid') + ... self.conv2 = nn.Conv2d(6, 16, 5, pad_mode='valid') + ... self.fc1 = nn.Dense(16*5*5, 120, weight_init='ones') + ... self.fc2 = nn.Dense(120, 84, weight_init='ones') + ... self.fc3 = nn.Dense(84, num_class, weight_init='ones') + ... self.relu = nn.ReLU() + ... self.max_pool2d = nn.MaxPool2d(kernel_size=2, stride=2) + ... self.flatten = nn.Flatten() + ... self.enhanced_amp = enhanced_amp + ... + ... def construct(self, x): + ... x = self.enhanced_amp(x, 0, 1) + ... x = self.max_pool2d(self.relu(self.conv1(x))) + ... x = self.max_pool2d(self.relu(self.conv2(x))) + ... x = self.flatten(x) + ... x = self.enhanced_amp(x, 1, 2) + ... x = self.relu(self.fc1(x)) + ... x = self.relu(self.fc2(x)) + ... x = self.fc3(x) + ... x = self.enhanced_amp(x, 2, 3) + ... return x + >>> + >>> loss_scale_manager = boost.GroupLossScaleManager(4096, []) + >>> net = Net(loss_scale_manager) + >>> param_group1 = [] + >>> param_group2 = [] + >>> for param in net.trainable_params(): + >>> if 'conv' in param.name: + >>> param_group1.append(param) + >>> else: + >>> param_group2.append(param) + >>> loss_scale_manager.loss_scale_groups = [param_group1, param_group2] + >>> loss = nn.SoftmaxCrossEntropyWithLogits() + >>> optim = nn.Momentum(params=net.trainable_params(), learning_rate=0.1, momentum=0.9) + >>> boost_config_dict = {"boost": {"mode": "manual", "less_bn": False, "grad_freeze": False, "adasum": False, \ + >>> "grad_accumulation": False, "dim_reduce": False, "loss_scale_group": True}} + >>> model = ms.Model(net, loss_fn=loss, optimizer=optim, metrics=None, loss_scale_manager=loss_scale_manager, \ + >>> boost_level="O1", boost_config_dict=boost_config_dict) + >>> # For details about how to build the dataset, please refer to the variable `dataset_train` in tutorial + >>> # document on the official website: + >>> # https://www.mindspore.cn/tutorials/zh-CN/master/beginner/quick_start.html + >>> dataset = create_custom_dataset() + >>> model.train(2, dataset) + """ + def __init__(self, init_loss_scale, loss_scale_groups): + super(GroupLossScaleManager, self).__init__() + self._loss_scale = init_loss_scale + self.loss_scale_groups = loss_scale_groups + self.loss_scale_number = 0 + self.layer_loss_scale = None + self.dynamic_loss_scale = None + + def set_loss_scale_status(self, loss_scale_number, init_loss_scale): + """ + Generate dynamic loss scale tuple and set overflow status list. + 鐢熸垚鍔ㄦ佹崯澶辫妯″厓缁勫苟璁剧疆婧㈠嚭鐘舵佸垪琛ㄣ + Args: + loss_scale_number (int): The number of loss scale. + init_loss_scale (float): The initialized loss scale. + """ + # 鍒濆鍖栧姩鎬佹崯澶卞昂搴︾鐞嗗櫒 +self.loss_scale_number = loss_scale_number # 璁剧疆鎹熷け灏哄害鐨勬暟閲 + +# 鍒涘缓涓涓寘鍚姩鎬佹崯澶卞昂搴︾殑鍒楄〃 inner_list +inner_list = [P._DynamicLossScale(layer=x) for x in range(loss_scale_number + 1)] # pylint: disable=W0212 + +# 灏 inner_list 杞崲涓哄厓缁勶紝浣滀负 layer_loss_scale 鐨勫 +self.layer_loss_scale = tuple(inner_list) + +# 鍒涘缓鍔ㄦ佹崯澶卞昂搴︾殑鍙傛暟鍏冪粍 dynamic_loss_scale +self.dynamic_loss_scale = ParameterTuple( + Parameter(Tensor(1, mstype.float32), name='layer_loss_scale_{}'.format(x), requires_grad=False) + for x in range(loss_scale_number + 2)) + +# 鍒濆鍖栧姩鎬佹崯澶卞昂搴︾殑鍊 +if isinstance(init_loss_scale, list): + # 濡傛灉 init_loss_scale 鏄垪琛紝灏嗗垪琛ㄤ腑鐨勫艰缃负鍔ㄦ佹崯澶卞昂搴︾殑鍒濆鍊 + for i, value in enumerate(init_loss_scale): + self.dynamic_loss_scale[i + 1].set_data(value) +else: + # 濡傛灉 init_loss_scale 涓嶆槸鍒楄〃锛屼娇鐢ㄥ悓涓鍒濆鍊艰缃墍鏈夊姩鎬佹崯澶卞昂搴︾殑鍊 + for i in range(self.loss_scale_number): + self.dynamic_loss_scale[i + 1].set_data(init_loss_scale) + + self.dynamic_loss_scale[i + 1].set_data(init_loss_scale) + + def update_loss_scale_status(self, layer, update_ratio): + """ + Update dynamic loss scale. + 鏇存柊鍔ㄦ佹崯澶辫妯° + Args: + layer (int): Current layer. + update_ratio (float): The ratio of loss scale update. + + Outputs: + float, new loss scale. + """ + +# 澧炲姞灞傛璁℃暟鍣 layer +layer = layer + 1 + +# 璁$畻鏂扮殑鎹熷け灏哄害鍊 new_loss_scale锛屽畠鏄綋鍓嶅眰娆$殑鍔ㄦ佹崯澶卞昂搴︿箻浠ユ洿鏂版瘮渚 update_ratio 鐨勭粨鏋 +new_loss_scale = self.dynamic_loss_scale[layer] * update_ratio + +# 浣跨敤 P.Assign() 鎿嶄綔灏嗘柊鐨勬崯澶卞昂搴﹀ new_loss_scale 璧嬪肩粰褰撳墠灞傛鐨勫姩鎬佹崯澶卞昂搴 +P.Assign()(self.dynamic_loss_scale[layer], new_loss_scale) + +# 杩斿洖鏂扮殑鎹熷け灏哄害鍊 new_loss_scale锛岀敤浜庡悗缁殑鎹熷け灏哄害绠$悊 +return new_loss_scale + + + def construct(self, x, layer1, layer2): + x = self.layer_loss_scale[layer1](x, self.dynamic_loss_scale[layer1] / self.dynamic_loss_scale[layer2]) + return x + + def get_loss_scale(self): + """ + Get loss scale value. + 鑾峰彇鎹熷け瑙勬ā鍊笺 + Returns: + bool, `loss_scale` value. + """ + return self._loss_scale + + def get_update_cell(self): + """ + Returns the instance of :class:`mindspore.boost.GroupLossScaleManager`. + 杩斿洖锛歝lass:`mindspore.boost.GroupLossScaleManager`鐨勫疄渚嬨 + Returns: + :class:`mindspore.boost.GroupLossScaleManager`. + """ + return self diff --git a/mindspore/ccsrc/transform-update/hcom_ops_declare.cc b/mindspore/ccsrc/transform-update/hcom_ops_declare.cc new file mode 100644 index 00000000000..537a31fa10f --- /dev/null +++ b/mindspore/ccsrc/transform-update/hcom_ops_declare.cc @@ -0,0 +1,68 @@ +/** + * Copyright 2019 Huawei Technologies Co., Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "transform/graph_ir/op_declare/hcom_ops_declare.h" +#include + +namespace mindspore::transform { +// HCOMAllreduce +INPUT_MAP(HcomAllReduce) = {{1, INPUT_DESC(x)}}; +//杈撳叆鏄犲皠锛寈绱㈠紩涓1 +OUTPUT_MAP(HcomAllReduce) = {{0, OUTPUT_DESC(y)}}; +//杈撳嚭鏄犲皠锛寉绱㈠紩涓0 +ATTR_MAP(HcomAllReduce) = {{"op", ATTR_DESC(reduction, AnyTraits())}, + {"group", ATTR_DESC(group, AnyTraits())}, + {"fusion", ATTR_DESC(fusion, AnyTraits())}}; +//灞炴ф槧灏勶紝灞炴p绫诲瀷涓簊tring锛屽睘鎬roup绫诲瀷涓簊tring锛屽睘鎬usion绫诲瀷涓篿nt64_t +REG_ADPT_DESC(HcomAllReduce, kNameAllReduce, ADPT_DESC(HcomAllReduce)) +//娉ㄥ唽HcomAllReduce鎿嶄綔鐨勯傞厤鍣ㄦ弿杩発NameAllReduce杩斿洖鐨刵ame鍙橀噺 + +// HCOMBraodcast +INPUT_MAP(HcomBroadcast) = EMPTY_INPUT_MAP; +//杈撳叆鏄犲皠锛岃涓虹┖ +DYN_INPUT_MAP(HcomBroadcast) = {{1, DYN_INPUT_DESC(x)}}; +//鍔ㄦ佽緭鍏ユ槧灏勶紝灏嗙储寮曚负1鐨勫姩鎬佽緭鍏ヤ笌鍚嶇О涓簒鐨勫姩鎬佽緭鍏ユ弿杩板叧鑱旇捣鏉ワ紝鐢ㄤ簬鍚庣画鎿嶄綔 +DYN_OUTPUT_MAP(HcomBroadcast) = {{0, DYN_OUTPUT_DESC(y)}}; +//鍔ㄦ佽緭鍑烘槧灏勶紝灏嗙储寮曚负0鐨勫姩鎬佽緭鍑轰笌鍚嶇О涓簓鐨勫姩鎬佽緭鍑烘弿杩板叧鑱旇捣鏉ワ紝鐢ㄤ簬鍚庣画鎿嶄綔 +ATTR_MAP(HcomBroadcast) = {{"root_rank", ATTR_DESC(root_rank, AnyTraits())}, + {"group", ATTR_DESC(group, AnyTraits())}}; +//灞炴ф槧灏勶紝灞炴oot_rank绫诲瀷涓篿nt64_t锛屽睘鎬roup绫诲瀷涓簊tring +REG_ADPT_DESC(HcomBroadcast, kNameBroadcast, ADPT_DESC(HcomBroadcast)) +//娉ㄥ唽HcomBroadcast鎿嶄綔鐨勯傞厤鍣ㄦ弿杩発NameBroadcast杩斿洖鐨刵ame鍙橀噺 + +// HcomAllGather +INPUT_MAP(HcomAllGather) = {{1, INPUT_DESC(x)}}; +//杈撳叆鏄犲皠锛寈绱㈠紩涓1 +OUTPUT_MAP(HcomAllGather) = {{0, OUTPUT_DESC(y)}}; +//杈撳嚭鏄犲皠锛寉绱㈠紩涓0 +ATTR_MAP(HcomAllGather) = {{"group", ATTR_DESC(group, AnyTraits())}, + {"rank_size", ATTR_DESC(rank_size, AnyTraits())}}; +//灞炴ф槧灏勶紝灞炴roup绫诲瀷涓簊tring锛屽睘鎬ank_size绫诲瀷涓篿nt64_t +REG_ADPT_DESC(HcomAllGather, kNameAllgather, ADPT_DESC(HcomAllGather)) +//娉ㄥ唽HcomAllGather鎿嶄綔鐨勯傞厤鍣ㄦ弿杩発NameAllgather杩斿洖鐨刵ame鍙橀噺 + +// HCOMReduceScatter +INPUT_MAP(HcomReduceScatter) = {{1, INPUT_DESC(x)}}; +//杈撳叆鏄犲皠锛寈绱㈠紩涓1 +OUTPUT_MAP(HcomReduceScatter) = {{0, OUTPUT_DESC(y)}}; +//杈撳嚭鏄犲皠锛寉绱㈠紩涓0 +ATTR_MAP(HcomReduceScatter) = {{"group", ATTR_DESC(group, AnyTraits())}, + {"op", ATTR_DESC(reduction, AnyTraits())}, + {"rank_size", ATTR_DESC(rank_size, AnyTraits())}}; +//灞炴ф槧灏勶紝灞炴roup绫诲瀷涓簊tring锛屽睘鎬p绫诲瀷涓簊tring>锛屽睘鎬ank_size绫诲瀷涓篿nt64_t +REG_ADPT_DESC(HcomReduceScatter, kNameReduceScatter, ADPT_DESC(HcomReduceScatter)) +//娉ㄥ唽HcomReduceScatter鎿嶄綔鐨勯傞厤鍣ㄦ弿杩発NameReduceScatter杩斿洖鐨刵ame鍙橀噺 +} // namespace mindspore::transform diff --git a/mindspore/ccsrc/transform-update/image_ops_declare.cc b/mindspore/ccsrc/transform-update/image_ops_declare.cc new file mode 100644 index 00000000000..d70ba1622be --- /dev/null +++ b/mindspore/ccsrc/transform-update/image_ops_declare.cc @@ -0,0 +1,88 @@ +/** + * Copyright 2019 Huawei Technologies Co., Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "transform/graph_ir/op_declare/image_ops_declare.h" +#include +#include + +namespace mindspore::transform { +// ResizeNearestNeighborV2D +INPUT_MAP(ResizeNearestNeighborV2D) = {{1, INPUT_DESC(x)}}; +//杈撳叆鏄犲皠锛寈绱㈠紩涓1 +ATTR_MAP(ResizeNearestNeighborV2D) = { + {"size", ATTR_DESC(size, AnyTraits>(), AnyTraits>())}, + {"align_corners", ATTR_DESC(align_corners, AnyTraits())}}; +//灞炴ф槧灏勶紝灞炴ize绫诲瀷涓篿nt64_t锛屽睘鎬lign_corners绫诲瀷涓篵ool +OUTPUT_MAP(ResizeNearestNeighborV2D) = {{0, OUTPUT_DESC(y)}}; +//杈撳嚭鏄犲皠锛寉绱㈠紩涓0 +REG_ADPT_DESC(ResizeNearestNeighborV2D, kNameResizeNearestNeighborD, ADPT_DESC(ResizeNearestNeighborV2D)) +//娉ㄥ唽ResizeNearestNeighborV2D鎿嶄綔鐨勯傞厤鍣ㄦ弿杩発NameResizeNearestNeighborVD + +// ResizeNearestNeighborV2 +INPUT_MAP(ResizeNearestNeighborV2) = {{1, INPUT_DESC(x)}, {2, INPUT_DESC(size)}}; +//杈撳叆鏄犲皠锛寈绱㈠紩涓1锛宻ize绱㈠紩涓2 +ATTR_MAP(ResizeNearestNeighborV2) = {{"align_corners", ATTR_DESC(align_corners, AnyTraits())}, + {"half_pixel_centers", ATTR_DESC(half_pixel_centers, AnyTraits())}}; +//灞炴ф槧灏勶紝灞炴lign_corners绫诲瀷涓篵ool锛屽睘鎬alf_pixel_centers绫诲瀷涓篵ool +OUTPUT_MAP(ResizeNearestNeighborV2) = {{0, OUTPUT_DESC(y)}}; +//杈撳嚭鏄犲皠锛寉绱㈠紩涓0 +REG_ADPT_DESC(ResizeNearestNeighborV2, kNameResizeNearestNeighborV2, ADPT_DESC(ResizeNearestNeighborV2)) +//娉ㄥ唽ResizeNearestNeighborV2鎿嶄綔鐨勯傞厤鍣ㄦ弿杩発NameResizeNearestNeighborV2 + +// ResizeNearestNeighborV2Grad +INPUT_MAP(ResizeNearestNeighborV2Grad) = {{1, INPUT_DESC(grads)}, {2, INPUT_DESC(size)}}; +//杈撳叆鏄犲皠锛実rads绱㈠紩涓1锛宻ize绱㈠紩涓2 +ATTR_MAP(ResizeNearestNeighborV2Grad) = {{"align_corners", ATTR_DESC(align_corners, AnyTraits())}}; +//灞炴ф槧灏勶紝灞炴lign_corners绫诲瀷涓篵ool +OUTPUT_MAP(ResizeNearestNeighborV2Grad) = {{0, OUTPUT_DESC(y)}}; +//杈撳嚭鏄犲皠锛寉绱㈠紩涓0 +REG_ADPT_DESC(ResizeNearestNeighborV2Grad, kNameResizeNearestNeighborGrad, ADPT_DESC(ResizeNearestNeighborV2Grad)) +//娉ㄥ唽ResizeNearestNeighborV2Grad鎿嶄綔鐨勯傞厤鍣ㄦ弿杩発NameResizeNearestNeighborGrad + +// ResizeBilinearV2Grad +INPUT_MAP(ResizeBilinearV2Grad) = {{1, INPUT_DESC(grads)}, {2, INPUT_DESC(original_image)}}; +//杈撳叆鏄犲皠锛実rads绱㈠紩涓1锛宱riginal_image绱㈠紩涓2 +ATTR_MAP(ResizeBilinearV2Grad) = {{"align_corners", ATTR_DESC(align_corners, AnyTraits())}}; +//灞炴ф槧灏勶紝灞炴lign_corners绫诲瀷涓篵ool +OUTPUT_MAP(ResizeBilinearV2Grad) = {{0, OUTPUT_DESC(y)}}; +//杈撳嚭鏄犲皠锛寉绱㈠紩涓0 +REG_ADPT_DESC(ResizeBilinearV2Grad, kNameResizeBilinearGrad, ADPT_DESC(ResizeBilinearV2Grad)) +//娉ㄥ唽ResizeBilinearV2Grad鎿嶄綔鐨勯傞厤鍣ㄦ弿杩発NameResizeBilinearV2Grad + +// ResizeBilinearV2 +INPUT_MAP(ResizeBilinearV2) = {{1, INPUT_DESC(x)}, {2, INPUT_DESC(size)}}; +//杈撳叆鏄犲皠锛寈绱㈠紩涓1锛宻ize绱㈠紩涓2 +ATTR_MAP(ResizeBilinearV2) = {{"align_corners", ATTR_DESC(align_corners, AnyTraits())}}; +//灞炴ф槧灏勶紝灞炴lign_corners绫诲瀷涓篵ool +OUTPUT_MAP(ResizeBilinearV2) = {{0, OUTPUT_DESC(y)}}; +//杈撳嚭鏄犲皠锛寉绱㈠紩涓0 +REG_ADPT_DESC(ResizeBilinearV2, kNameResizeBilinear, ADPT_DESC(ResizeBilinearV2)) +//娉ㄥ唽ResizeBilinearV2鎿嶄綔鐨勯傞厤鍣ㄦ弿杩発NameResizeBilinearV2 +REG_ADPT_DESC(ResizeBilinearV2New, kNameResizeBilinearV2, ADPT_DESC(ResizeBilinearV2)) +//娉ㄥ唽ResizeBilinearV2New鎿嶄綔鐨勯傞厤鍣ㄦ弿杩発NameResizeBilinearV2 + +// CropAndResize +INPUT_MAP(CropAndResize) = { + {1, INPUT_DESC(x)}, {2, INPUT_DESC(boxes)}, {3, INPUT_DESC(box_index)}, {4, INPUT_DESC(crop_size)}}; + //杈撳叆鏄犲皠锛寈绱㈠紩涓1锛宐oxes绱㈠紩涓2锛宐ox_index绱㈠紩涓3锛宑rop_size绱㈠紩涓4 +ATTR_MAP(CropAndResize) = {{"extrapolation_value", ATTR_DESC(extrapolation_value, AnyTraits())}, + {"method", ATTR_DESC(method, AnyTraits())}}; +//灞炴ф槧灏勶紝灞炴xtrapolation_value绫诲瀷涓篺loat锛屽睘鎬ethod绫诲瀷涓簊tring +OUTPUT_MAP(CropAndResize) = {{0, OUTPUT_DESC(y)}}; +//杈撳嚭鏄犲皠锛寉绱㈠紩涓0 +REG_ADPT_DESC(CropAndResize, kNameCropAndResize, ADPT_DESC(CropAndResize)) +//娉ㄥ唽CropAndResize鎿嶄綔鐨勯傞厤鍣ㄦ弿杩発NameCropAndResize +} // namespace mindspore::transform diff --git a/mindspore/ccsrc/transform-update/io_format_map.cc b/mindspore/ccsrc/transform-update/io_format_map.cc new file mode 100644 index 00000000000..1a4e0fa2098 --- /dev/null +++ b/mindspore/ccsrc/transform-update/io_format_map.cc @@ -0,0 +1,46 @@ +/** + * Copyright 2021 Huawei Technologies Co., Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "transform/graph_ir/io_format_map.h" + +namespace mindspore { +namespace transform { +// 定义一个名为 'IOFormatMap' 的类 +// 静态成员变量 'io_format_map_',用于存储操作名称与其输入输出格式之间的映射关系 +// 定义 'io_format_map_' 映射的初始化值 +mindspore::HashMap IOFormatMap::io_format_map_ = {{"BasicLSTMCell", "ND"}, + {"BasicLSTMCellInputGrad", "ND"}, + {"BasicLSTMCellCStateGrad", "ND"}, + {"Dequant", "ND"}, + {"DynamicGRUV2", "ND"}, + {"DynamicGRUV2Grad", "ND"}, + {"DynamicRNN", "ND"}, + {"DynamicRNNGrad", "ND"}, + {"MatMul", "ND"}, + {"BatchMatMul", "ND"}, + {"BatchMatMulV2", "ND"}, + {"Quant", "ND"}, + {"BasicLSTMCellWeightGrad", "HWCN"}, + {"ExtractImagePatches", "NCHW"}, + {"Conv3D", "format"}, + {"MaxPool3D", "NCDHW"}, + {"Conv3DBackpropFilter", "format"}, + {"Conv3DBackpropInput", "format"}, + {"Conv3DTranspose", "format"}}; +// 静态成员函数 'get()',用于获取 'io_format_map_' 映射 +mindspore::HashMap &IOFormatMap::get() { return io_format_map_; } +} // namespace transform +} // namespace mindspore diff --git a/mindspore/ccsrc/transform-update/less_batch_normalization.py b/mindspore/ccsrc/transform-update/less_batch_normalization.py new file mode 100644 index 00000000000..94b78c9014a --- /dev/null +++ b/mindspore/ccsrc/transform-update/less_batch_normalization.py @@ -0,0 +1,172 @@ +# Copyright 2021 Huawei Technologies Co., Ltd +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ============================================================================ +"""less Batch Normalization""" +# 导入必要的库和模块 +import numpy as np +from mindspore.nn.cell import Cell +from mindspore.nn.layer import Dense +from mindspore.ops import operations as P +from mindspore.common import Tensor, Parameter +from mindspore.common import dtype as mstype +from mindspore.common.initializer import initializer + + +__all__ = ["CommonHeadLastFN", "LessBN"] + +# 定义LessBN模块,包括两个类:CommonHeadLastFN和LessBN +# 这两个类用于自动减少Batch Normalization(BN)以提高网络性能并保持网络准确性。 + +# 定义一个名为CommonHeadLastFN的类,用于最后一层的全连接操作 +class CommonHeadLastFN(Cell): + r""" + The last full Normalization layer. + + This layer implements the operation as: + + .. math:: + \text{inputs} = \text{norm}(\text{inputs}) + \text{kernel} = \text{norm}(\text{kernel}) + \text{outputs} = \text{multiplier} * (\text{inputs} * \text{kernel} + \text{bias}), + + Args: + in_channels (int): The number of channels in the input space. + out_channels (int): The number of channels in the output space. + weight_init (Union[Tensor, str, Initializer, numbers.Number]): The trainable weight_init parameter. The dtype + is same as input x. The values of str refer to the function `initializer`. Default: 'normal'. + bias_init (Union[Tensor, str, Initializer, numbers.Number]): The trainable bias_init parameter. The dtype is + same as input x. The values of str refer to the function `initializer`. Default: 'zeros'. + has_bias (bool): Specifies whether the layer uses a bias vector. Default: True. + + Supported Platforms: + ``Ascend`` ``GPU`` ``CPU`` + + Examples: + >>> input = Tensor(np.array([[180, 234, 154], [244, 48, 247]]), mindspore.float32) + >>> net = CommonHeadLastFN(3, 4) + >>> output = net(input) + """ + + def __init__(self, + in_channels, + out_channels, + weight_init='normal', + bias_init='zeros', + has_bias=True): + + super(CommonHeadLastFN, self).__init__() + # 初始化权重参数 + weight_shape = [out_channels, in_channels] + self.weight = Parameter(initializer(weight_init, weight_shape), requires_grad=True, name='weight') + # 定义L2范数归一化操作,分别应用于输入x和权重kernel + self.x_norm = P.L2Normalize(axis=1) + self.w_norm = P.L2Normalize(axis=1) + # 定义矩阵相乘操作,用于计算x和w的乘积 + self.fc = P.MatMul(transpose_a=False, transpose_b=True) + # 初始化乘子参数 + self.multiplier = Parameter(Tensor(np.ones([1]), mstype.float32), requires_grad=True, name='multiplier') + # 是否使用偏置项 + self.has_bias = has_bias + if self.has_bias: + # 如果使用偏置项,初始化偏置参数 + bias_shape = [out_channels] + self.bias_add = P.BiasAdd() + self.bias = Parameter(initializer(bias_init, bias_shape), requires_grad=True, name='bias') + + def construct(self, x): + # 对输入x进行L2范数归一化 + x = self.x_norm(x) + # 对权重kernel进行L2范数归一化 + w = self.w_norm(self.weight) + # 计算x和w的矩阵相乘 + x = self.fc(x, w) + if self.has_bias: + # 如果使用偏置项,将偏置项添加到x中 + x = self.bias_add(x, self.bias) + # 乘以乘子参数 + x = self.multiplier * x + # 返回结果 + return x + +# 定义LessBN类,用于自动减少Batch Normalization(BN)以提高网络性能并保持准确性 +class LessBN(Cell): + """ + Reduce the number of BN automatically to improve the network performance + and ensure the network accuracy. + + Args: + network (Cell): Network to be modified. + fn_flag (bool): Replace FC with FN. default: False. + + Examples: + >>> network = boost.LessBN(network) + """ + + def __init__(self, network, fn_flag=False): + super(LessBN, self).__init__() + # 存储传入的网络 + self.network = network + # 设置网络的"less_bn"属性 + self.network.set_boost("less_bn") + # 更新网络的单元前缀 + self.network.update_cell_prefix() + # 如果fn_flag为True,将网络中的全连接层替换为FN层 + if fn_flag: + self._convert_to_less_bn_net(self.network) + # 添加延迟内联标志,将网络的部分操作延迟到运行时执行 + self.network.add_flags(defer_inline=True) + + def _convert_dense(self, subcell): + """ + convert dense cell to FN cell + """ + prefix = subcell.param_prefix + # 创建新的FN层,参数与原始全连接层相同 + new_subcell = CommonHeadLastFN(subcell.in_channels, + subcell.out_channels, + subcell.weight, + subcell.bias, + False) + new_subcell.update_parameters_name(prefix + '.') + + return new_subcell + + def _convert_to_less_bn_net(self, net): + """ + convert network to less_bn network + """ + cells = net.name_cells() + dense_name = [] + dense_list = [] + # 遍历网络的所有单元 + for name in cells: + subcell = cells[name] + if subcell == net: + continue + elif isinstance(subcell, (Dense)): + # 如果单元是全连接层,记录其名称和实例 + dense_name.append(name) + dense_list.append(subcell) + else: + # 递归调用,继续查找子单元中的全连接层 + self._convert_to_less_bn_net(subcell) + + if dense_list: + # 如果存在全连接层,将最后一个全连接层替换为FN层 + new_subcell = self._convert_dense(dense_list[-1]) + net.insert_child_to_cell(dense_name[-1], new_subcell) + + def construct(self, *inputs): + # 前向传播方法,将输入传递给网络并返回结果 + return self.network(*inputs) diff --git a/mindspore/ccsrc/transform-update/logging_ops_declare.cc b/mindspore/ccsrc/transform-update/logging_ops_declare.cc new file mode 100644 index 00000000000..dbf30650cc2 --- /dev/null +++ b/mindspore/ccsrc/transform-update/logging_ops_declare.cc @@ -0,0 +1,38 @@ +/** + * Copyright 2019 Huawei Technologies Co., Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "transform/graph_ir/op_declare/logging_ops_declare.h" + +namespace mindspore::transform { +// Print +INPUT_MAP(Print) = EMPTY_INPUT_MAP; +//杈撳叆鏄犲皠锛岃涓虹┖ +DYN_INPUT_MAP(Print) = {{1, DYN_INPUT_DESC(x)}}; +//鍔ㄦ佽緭鍏ユ槧灏勶紝灏嗙储寮曚负1鐨勫姩鎬佽緭鍏ヤ笌鍚嶇О涓簒鐨勫姩鎬佽緭鍏ユ弿杩板叧鑱旇捣鏉ワ紝鐢ㄤ簬鍚庣画鎿嶄綔 +ATTR_MAP(Print) = EMPTY_ATTR_MAP; +//灞炴ф槧灏勶紝璁句负绌 +REG_ADPT_DESC(Print, kNamePrint, ADPT_DESC(Print)) +//娉ㄥ唽Print鎿嶄綔鐨勯傞厤鍣ㄦ弿杩発NamePrint + +INPUT_MAP(Assert) = {{1, INPUT_DESC(input_condition)}}; +//杈撳叆鏄犲皠锛宨nput_condition绱㈠紩涓1 +DYN_INPUT_MAP(Assert) = {{2, DYN_INPUT_DESC(input_data)}}; +//鍔ㄦ佽緭鍏ユ槧灏勶紝灏嗙储寮曚负2鐨勫姩鎬佽緭鍏ヤ笌鍚嶇О涓篿nput_data鐨勫姩鎬佽緭鍏ユ弿杩板叧鑱旇捣鏉ワ紝鐢ㄤ簬鍚庣画鎿嶄綔 +ATTR_MAP(Assert) = {{"summarize", ATTR_DESC(summarize, AnyTraits())}}; +//灞炴ф槧灏勶紝灞炴ummarize绫诲瀷涓篿nt64_t +REG_ADPT_DESC(Assert, kNameAssert, ADPT_DESC(Assert)) +//娉ㄥ唽Assert鎿嶄綔鐨勯傞厤鍣ㄦ弿杩発NameAssert +} // namespace mindspore::transform diff --git a/mindspore/ccsrc/transform-update/math_ops_declare.cc b/mindspore/ccsrc/transform-update/math_ops_declare.cc new file mode 100644 index 00000000000..d2401069cb3 --- /dev/null +++ b/mindspore/ccsrc/transform-update/math_ops_declare.cc @@ -0,0 +1,168 @@ +/** + * Copyright 2021 Huawei Technologies Co., Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "transform/graph_ir/op_declare/math_ops_declare.h" +#include +#include + +namespace mindspore::transform { +// ActsULQ +INPUT_MAP(ActsULQ) = {{1, INPUT_DESC(x)}, {2, INPUT_DESC(clamp_min)}, {3, INPUT_DESC(clamp_max)}}; +//杈撳叆鏄犲皠锛寈绱㈠紩涓1锛宑lamp_min绱㈠紩涓2锛宑lamp_max绱㈠紩涓3 +ATTR_MAP(ActsULQ) = {{"fixed_min", ATTR_DESC(fixed_min, AnyTraits())}, + {"num_bits", ATTR_DESC(num_bits, AnyTraits())}}; +//灞炴ф槧灏勶紝灞炴ixed_min绫诲瀷涓篵ool锛屽睘鎬um_bits绫诲瀷涓篿nt64_t +OUTPUT_MAP(ActsULQ) = {{0, OUTPUT_DESC(y)}, + {1, OUTPUT_DESC(clamp_min_mask)}, + {2, OUTPUT_DESC(clamp_max_mask)}, + {3, OUTPUT_DESC(x_clamped_loss)}}; +//杈撳嚭鏄犲皠锛寉绱㈠紩涓0锛宑lamp_min_mask绱㈠紩涓1锛宑lamp_max_mask绱㈠紩涓2锛寈_clamped_loss绱㈠紩涓3 +REG_ADPT_DESC(ActsULQ, kNameActsULQ, ADPT_DESC(ActsULQ)) +//娉ㄥ唽ActsULQ鎿嶄綔鐨勯傞厤鍣ㄦ弿杩発NameActsULQ + +// ActsULQInputGrad +INPUT_MAP(ActsULQInputGrad) = { + {1, INPUT_DESC(y_grad)}, {2, INPUT_DESC(clamp_min_mask)}, {3, INPUT_DESC(clamp_max_mask)}}; +//杈撳叆鏄犲皠锛寉_grad绱㈠紩涓1锛宑lamp_min_mask绱㈠紩涓2锛宑lamp_max_mask绱㈠紩涓3 +ATTR_MAP(ActsULQInputGrad) = EMPTY_ATTR_MAP; +//灞炴ф槧灏勶紝璁句负绌 +OUTPUT_MAP(ActsULQInputGrad) = {{0, OUTPUT_DESC(x_grad)}}; +//杈撳嚭鏄犲皠锛寈_grad绱㈠紩涓0 +REG_ADPT_DESC(ActsULQInputGrad, kNameActsULQInputGrad, ADPT_DESC(ActsULQInputGrad)) +//娉ㄥ唽ActsULQInputGrad鎿嶄綔鐨勯傞厤鍣ㄦ弿杩発NameActsULQInputGrad + +// ActULQClampMaxGrad +INPUT_MAP(ActULQClampMaxGrad) = { + {1, INPUT_DESC(y_grad)}, {2, INPUT_DESC(clamp_max_mask)}, {3, INPUT_DESC(x_clamped_loss)}}; +//杈撳叆鏄犲皠锛寉_grad绱㈠紩涓1锛宑lamp_max_mask绱㈠紩涓2锛寈_clamped_loss绱㈠紩涓3 +ATTR_MAP(ActULQClampMaxGrad) = EMPTY_ATTR_MAP; +//灞炴ф槧灏勶紝璁句负绌 +OUTPUT_MAP(ActULQClampMaxGrad) = {{0, OUTPUT_DESC(clamp_max_grad)}}; +//杈撳嚭鏄犲皠锛宑lamp_max_grad绱㈠紩涓0 +REG_ADPT_DESC(ActULQClampMaxGrad, kNameActULQClampMaxGrad, ADPT_DESC(ActULQClampMaxGrad)) +//娉ㄥ唽ActsULQClampMaxGrad鎿嶄綔鐨勯傞厤鍣ㄦ弿杩発NameActsULQClampMaxGrad + +// ActULQClampMinGrad +INPUT_MAP(ActULQClampMinGrad) = { + {1, INPUT_DESC(y_grad)}, {2, INPUT_DESC(clamp_min_mask)}, {3, INPUT_DESC(x_clamped_loss)}}; +//杈撳叆鏄犲皠锛寉_grad绱㈠紩涓1锛宑lamp__min_mask绱㈠紩涓2锛寈_clamped_loss绱㈠紩涓3 +ATTR_MAP(ActULQClampMinGrad) = EMPTY_ATTR_MAP; +//灞炴ф槧灏勶紝璁句负绌 +OUTPUT_MAP(ActULQClampMinGrad) = {{0, OUTPUT_DESC(clamp_min_grad)}}; +//杈撳嚭鏄犲皠锛宑lamp_min_grad绱㈠紩涓0 +REG_ADPT_DESC(ActULQClampMinGrad, kNameActULQClampMinGrad, ADPT_DESC(ActULQClampMinGrad)) +//娉ㄥ唽ActsULQClampMinGrad鎿嶄綔鐨勯傞厤鍣ㄦ弿杩発NameActsULQClampMinGrad + +// HistogramFixedWidthD +INPUT_MAP(HistogramFixedWidthD) = {{1, INPUT_DESC(x)}, {2, INPUT_DESC(range)}}; +//杈撳叆鏄犲皠锛寈绱㈠紩涓1锛宺ange)绱㈠紩涓2 +ATTR_MAP(HistogramFixedWidthD) = {{"nbins", ATTR_DESC(nbins, AnyTraits())}, + {"dtype", ATTR_DESC(dtype, AnyTraits())}}; +//灞炴ф槧灏勶紝灞炴bins绫诲瀷涓篿nt64_t锛屽睘鎬type绫诲瀷涓篿nt64_t +OUTPUT_MAP(HistogramFixedWidthD) = {{0, OUTPUT_DESC(y)}}; +//杈撳嚭鏄犲皠锛寉绱㈠紩涓0 +REG_ADPT_DESC(HistogramFixedWidthD, kNameHistogramFixedWidthD, ADPT_DESC(HistogramFixedWidthD)) +//娉ㄥ唽HistogramFixedWidthD鎿嶄綔鐨勯傞厤鍣ㄦ弿杩発NameHistogramFixedWidthD + +// IFMR +INPUT_MAP(IFMR) = { + {1, INPUT_DESC(data)}, {2, INPUT_DESC(data_min)}, {3, INPUT_DESC(data_max)}, {4, INPUT_DESC(cumsum)}}; +//杈撳叆鏄犲皠锛宒ata绱㈠紩涓1锛宒ata_min绱㈠紩涓2锛宒ata_max绱㈠紩涓3锛宑umsum绱㈠紩涓4 +ATTR_MAP(IFMR) = {{"min_percentile", ATTR_DESC(min_percentile, AnyTraits())}, + {"max_percentile", ATTR_DESC(max_percentile, AnyTraits())}, + {"search_range", ATTR_DESC(search_range, AnyTraits>())}, + {"search_step", ATTR_DESC(search_step, AnyTraits())}}; +//灞炴ф槧灏勶紝灞炴in_percentile绫诲瀷涓篺loat锛屽睘鎬ax_percentile绫诲瀷涓篺loat锛屽睘鎬earch_range绫诲瀷涓篺loat锛屽睘鎬earch_step绫诲瀷涓篺loat +OUTPUT_MAP(IFMR) = {{0, OUTPUT_DESC(scale)}, {1, OUTPUT_DESC(offset)}}; +//杈撳嚭鏄犲皠锛宻cale绱㈠紩涓0锛宱ffset绱㈠紩涓1 +REG_ADPT_DESC(IFMR, kNameIFMR, ADPT_DESC(IFMR)) +//娉ㄥ唽IFMR鎿嶄綔鐨勯傞厤鍣ㄦ弿杩発NameIFMR + +// NLLLoss +INPUT_MAP(NLLLoss) = {{1, INPUT_DESC(x)}, {2, INPUT_DESC(target)}, {3, INPUT_DESC(weight)}}; +//杈撳叆鏄犲皠锛寈绱㈠紩涓1锛宼arget绱㈠紩涓2锛寃eight绱㈠紩涓3 +ATTR_MAP(NLLLoss) = {{"reduction", ATTR_DESC(reduction, AnyTraits())}}; +//灞炴ф槧灏勶紝灞炴eduction绫诲瀷涓簊tring +OUTPUT_MAP(NLLLoss) = {{0, OUTPUT_DESC(y)}, {1, OUTPUT_DESC(total_weight)}}; +//杈撳嚭鏄犲皠锛寉绱㈠紩涓0锛宼otal_weight绱㈠紩涓1 +REG_ADPT_DESC(NLLLoss, kNameNLLLoss, ADPT_DESC(NLLLoss)) +//娉ㄥ唽NLLLoss鎿嶄綔鐨勯傞厤鍣ㄦ弿杩発NameNLLLoss + +// NLLLossGrad +INPUT_MAP(NLLLossGrad) = {{1, INPUT_DESC(x)}, + {2, INPUT_DESC(y_grad)}, + {3, INPUT_DESC(target)}, + {4, INPUT_DESC(weight)}, + {5, INPUT_DESC(total_weight)}}; +//杈撳叆鏄犲皠锛寈绱㈠紩涓1锛寉_grad绱㈠紩涓2锛宼arget绱㈠紩涓3锛寃eight绱㈠紩涓4锛宼otal_weight绱㈠紩涓5 +ATTR_MAP(NLLLossGrad) = {{"reduction", ATTR_DESC(reduction, AnyTraits())}}; +//灞炴ф槧灏勶紝灞炴eduction绫诲瀷涓簊tring +OUTPUT_MAP(NLLLossGrad) = {{0, OUTPUT_DESC(x_grad)}}; +//杈撳嚭鏄犲皠锛寈_grad绱㈠紩涓0 +REG_ADPT_DESC(NLLLossGrad, kNameNLLLossGrad, ADPT_DESC(NLLLossGrad)) +//娉ㄥ唽NLLLosGrad鎿嶄綔鐨勯傞厤鍣ㄦ弿杩発NameNLLLossGrad + +// Erf +INPUT_MAP(Erf) = {{1, INPUT_DESC(x)}}; +//杈撳叆鏄犲皠锛寈绱㈠紩涓1 +ATTR_MAP(Erf) = EMPTY_ATTR_MAP; +//灞炴ф槧灏勶紝璁句负绌 +OUTPUT_MAP(Erf) = {{0, OUTPUT_DESC(y)}}; +//杈撳嚭鏄犲皠锛寉绱㈠紩涓0 +REG_ADPT_DESC(Erf, kNameErf, ADPT_DESC(Erf)) +//娉ㄥ唽Erf鎿嶄綔鐨勯傞厤鍣ㄦ弿杩発NameErf + +// Erfc +INPUT_MAP(Erfc) = {{1, INPUT_DESC(x)}}; +//杈撳叆鏄犲皠锛寈绱㈠紩涓1 +ATTR_MAP(Erfc) = EMPTY_ATTR_MAP; +//灞炴ф槧灏勶紝璁句负绌 +OUTPUT_MAP(Erfc) = {{0, OUTPUT_DESC(y)}}; +//杈撳嚭鏄犲皠锛寉绱㈠紩涓0 +REG_ADPT_DESC(Erfc, kNameErfc, ADPT_DESC(Erfc)) +//娉ㄥ唽Erfc鎿嶄綔鐨勯傞厤鍣ㄦ弿杩発NameErfc + +// WtsARQ +INPUT_MAP(WtsARQ) = {{1, INPUT_DESC(w)}, {2, INPUT_DESC(w_min)}, {3, INPUT_DESC(w_max)}}; +//杈撳叆鏄犲皠锛寈绱㈠紩涓1锛寃_min绱㈠紩涓2锛寃_max绱㈠紩涓3 +ATTR_MAP(WtsARQ) = {{"num_bits", ATTR_DESC(num_bits, AnyTraits())}, + {"offset_flag", ATTR_DESC(offset_flag, AnyTraits())}}; +//杈撳叆鏄犲皠锛宯um_bits绱㈠紩涓1锛宱ffset_flag绱㈠紩涓2 +OUTPUT_MAP(WtsARQ) = {{0, OUTPUT_DESC(y)}}; +//杈撳嚭鏄犲皠锛寉绱㈠紩涓0 +REG_ADPT_DESC(WtsARQ, kNameWtsARQ, ADPT_DESC(WtsARQ)) +//娉ㄥ唽WtsARQ鎿嶄綔鐨勯傞厤鍣ㄦ弿杩発NameWtsARQ + +// IsFinite +INPUT_MAP(IsFinite) = {{1, INPUT_DESC(x)}}; +//杈撳叆鏄犲皠锛寈绱㈠紩涓1 +ATTR_MAP(IsFinite) = EMPTY_ATTR_MAP; +//灞炴ф槧灏勶紝璁句负绌 +OUTPUT_MAP(IsFinite) = {{0, OUTPUT_DESC(y)}}; +//杈撳嚭鏄犲皠锛寉绱㈠紩涓0 +REG_ADPT_DESC(IsFinite, kNameIsFinite, ADPT_DESC(IsFinite)) +//娉ㄥ唽IsFinite鎿嶄綔鐨勯傞厤鍣ㄦ弿杩発NameIsFinite + +// IsNan +INPUT_MAP(IsNan) = {{1, INPUT_DESC(x)}}; +//杈撳叆鏄犲皠锛寈绱㈠紩涓1 +ATTR_MAP(IsNan) = EMPTY_ATTR_MAP; +//灞炴ф槧灏勶紝璁句负绌 +OUTPUT_MAP(IsNan) = {{0, OUTPUT_DESC(y)}}; +//杈撳嚭鏄犲皠锛寉绱㈠紩涓0 +REG_ADPT_DESC(IsNan, kNameIsNan, ADPT_DESC(IsNan)) +//娉ㄥ唽IsNan鎿嶄綔鐨勯傞厤鍣ㄦ弿杩発NameIsNan +} // namespace mindspore::transform diff --git a/mindspore/ccsrc/transform-update/matrix_calculation_ops_declare.cc b/mindspore/ccsrc/transform-update/matrix_calculation_ops_declare.cc new file mode 100644 index 00000000000..45a56f49105 --- /dev/null +++ b/mindspore/ccsrc/transform-update/matrix_calculation_ops_declare.cc @@ -0,0 +1,160 @@ +/** + * Copyright 2019-2022 Huawei Technologies Co., Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "transform/graph_ir/op_declare/matrix_calculation_ops_declare.h" + +namespace mindspore::transform { +// TensorScatterUpdate +INPUT_MAP(TensorScatterUpdate) = {{1, INPUT_DESC(x)}, {2, INPUT_DESC(indices)}, {3, INPUT_DESC(updates)}}; +ATTR_MAP(TensorScatterUpdate) = EMPTY_ATTR_MAP; +OUTPUT_MAP(TensorScatterUpdate) = {{0, OUTPUT_DESC(y)}}; +REG_ADPT_DESC(TensorScatterUpdate, kNameTensorScatterUpdate, ADPT_DESC(TensorScatterUpdate)) + +// ScatterUpdate +INPUT_MAP(ScatterUpdate) = {{1, INPUT_DESC(var)}, {2, INPUT_DESC(indices)}, {3, INPUT_DESC(updates)}}; +ATTR_MAP(ScatterUpdate) = {{"use_locking", ATTR_DESC(use_locking, AnyTraits())}}; +OUTPUT_MAP(ScatterUpdate) = {{0, OUTPUT_DESC(var)}}; +REG_ADPT_DESC(ScatterUpdate, kNameScatterUpdate, ADPT_DESC(ScatterUpdate)) + +// ScatterNdUpdate +INPUT_MAP(ScatterNdUpdate) = {{1, INPUT_DESC(var)}, {2, INPUT_DESC(indices)}, {3, INPUT_DESC(updates)}}; +ATTR_MAP(ScatterNdUpdate) = {{"use_locking", ATTR_DESC(use_locking, AnyTraits())}}; +OUTPUT_MAP(ScatterNdUpdate) = {{0, OUTPUT_DESC(var)}}; +REG_ADPT_DESC(ScatterNdUpdate, kNameScatterNdUpdate, ADPT_DESC(ScatterNdUpdate)) + +// ScatterMax +INPUT_MAP(ScatterMax) = {{1, INPUT_DESC(var)}, {2, INPUT_DESC(indices)}, {3, INPUT_DESC(updates)}}; +ATTR_MAP(ScatterMax) = {{"use_locking", ATTR_DESC(use_locking, AnyTraits())}}; +OUTPUT_MAP(ScatterMax) = {{0, OUTPUT_DESC(var)}}; +REG_ADPT_DESC(ScatterMax, kNameScatterMax, ADPT_DESC(ScatterMax)) + +// ScatterMin +INPUT_MAP(ScatterMin) = {{1, INPUT_DESC(var)}, {2, INPUT_DESC(indices)}, {3, INPUT_DESC(updates)}}; +ATTR_MAP(ScatterMin) = {{"use_locking", ATTR_DESC(use_locking, AnyTraits())}}; +OUTPUT_MAP(ScatterMin) = {{0, OUTPUT_DESC(var)}}; +REG_ADPT_DESC(ScatterMin, kNameScatterMin, ADPT_DESC(ScatterMin)) + +// ScatterAdd +INPUT_MAP(ScatterAdd) = {{1, INPUT_DESC(var)}, {2, INPUT_DESC(indices)}, {3, INPUT_DESC(updates)}}; +ATTR_MAP(ScatterAdd) = {{"use_locking", ATTR_DESC(use_locking, AnyTraits())}}; +OUTPUT_MAP(ScatterAdd) = {{0, OUTPUT_DESC(var)}}; +REG_ADPT_DESC(ScatterAdd, kNameScatterAdd, ADPT_DESC(ScatterAdd)) + +// ScatterSub +INPUT_MAP(ScatterSub) = {{1, INPUT_DESC(var)}, {2, INPUT_DESC(indices)}, {3, INPUT_DESC(updates)}}; +ATTR_MAP(ScatterSub) = {{"use_locking", ATTR_DESC(use_locking, AnyTraits())}}; +OUTPUT_MAP(ScatterSub) = {{0, OUTPUT_DESC(var)}}; +REG_ADPT_DESC(ScatterSub, kNameScatterSub, ADPT_DESC(ScatterSub)) + +// ScatterMul +INPUT_MAP(ScatterMul) = {{1, INPUT_DESC(var)}, {2, INPUT_DESC(indices)}, {3, INPUT_DESC(updates)}}; +ATTR_MAP(ScatterMul) = {{"use_locking", ATTR_DESC(use_locking, AnyTraits())}}; +OUTPUT_MAP(ScatterMul) = {{0, OUTPUT_DESC(var)}}; +REG_ADPT_DESC(ScatterMul, kNameScatterMul, ADPT_DESC(ScatterMul)) + +// ScatterDiv +INPUT_MAP(ScatterDiv) = {{1, INPUT_DESC(var)}, {2, INPUT_DESC(indices)}, {3, INPUT_DESC(updates)}}; +ATTR_MAP(ScatterDiv) = {{"use_locking", ATTR_DESC(use_locking, AnyTraits())}}; +OUTPUT_MAP(ScatterDiv) = {{0, OUTPUT_DESC(var)}}; +REG_ADPT_DESC(ScatterDiv, kNameScatterDiv, ADPT_DESC(ScatterDiv)) + +// ScatterNdAdd +INPUT_MAP(ScatterNdAdd) = {{1, INPUT_DESC(var)}, {2, INPUT_DESC(indices)}, {3, INPUT_DESC(updates)}}; +ATTR_MAP(ScatterNdAdd) = {{"use_locking", ATTR_DESC(use_locking, AnyTraits())}}; +OUTPUT_MAP(ScatterNdAdd) = {{0, OUTPUT_DESC(var)}}; +REG_ADPT_DESC(ScatterNdAdd, kNameScatterNdAdd, ADPT_DESC(ScatterNdAdd)) + +// ScatterNdSub +INPUT_MAP(ScatterNdSub) = {{1, INPUT_DESC(var)}, {2, INPUT_DESC(indices)}, {3, INPUT_DESC(updates)}}; +ATTR_MAP(ScatterNdSub) = {{"use_locking", ATTR_DESC(use_locking, AnyTraits())}}; +OUTPUT_MAP(ScatterNdSub) = {{0, OUTPUT_DESC(var)}}; +REG_ADPT_DESC(ScatterNdSub, kNameScatterNdSub, ADPT_DESC(ScatterNdSub)) + +// MatMul +INPUT_MAP(MatMul) = {{1, INPUT_DESC(x1)}, {2, INPUT_DESC(x2)}, {3, INPUT_DESC(bias)}}; +ATTR_MAP(MatMul) = {{"transpose_x1", ATTR_DESC(transpose_x1, AnyTraits())}, + {"transpose_x2", ATTR_DESC(transpose_x2, AnyTraits())}}; +OUTPUT_MAP(MatMul) = {{0, OUTPUT_DESC(y)}}; +REG_ADPT_DESC(MatMul, kNameMatMul, ADPT_DESC(MatMul)) + +// MatMulV2 +INPUT_MAP(MatMulV2) = {{1, INPUT_DESC(x1)}, {2, INPUT_DESC(x2)}, {3, INPUT_DESC(bias)}}; +ATTR_MAP(MatMulV2) = {{"transpose_a", ATTR_DESC(transpose_x1, AnyTraits())}, + {"transpose_b", ATTR_DESC(transpose_x2, AnyTraits())}}; +OUTPUT_MAP(MatMulV2) = {{0, OUTPUT_DESC(y)}}; +REG_ADPT_DESC(MatMulV2, prim::kPrimMatMul->name(), ADPT_DESC(MatMulV2)) + +// MatrixDiag +INPUT_MAP(MatrixDiag) = {{1, INPUT_DESC(x)}}; +ATTR_MAP(MatrixDiag) = EMPTY_ATTR_MAP; +OUTPUT_MAP(MatrixDiag) = {{0, OUTPUT_DESC(y)}}; +REG_ADPT_DESC(MatrixDiag, kNameMatrixDiagD, ADPT_DESC(MatrixDiag)) + +// MatrixDiagPartD +INPUT_MAP(MatrixDiagPartD) = {{1, INPUT_DESC(x)}, {2, INPUT_DESC(assist)}}; +ATTR_MAP(MatrixDiagPartD) = EMPTY_ATTR_MAP; +OUTPUT_MAP(MatrixDiagPartD) = {{0, OUTPUT_DESC(y)}}; +REG_ADPT_DESC(MatrixDiagPartD, kNameMatrixDiagPartD, ADPT_DESC(MatrixDiagPartD)) + +// MatrixSetDiagD +INPUT_MAP(MatrixSetDiagD) = {{1, INPUT_DESC(x)}, {2, INPUT_DESC(diagonal)}, {3, INPUT_DESC(assist)}}; +ATTR_MAP(MatrixSetDiagD) = EMPTY_ATTR_MAP; +OUTPUT_MAP(MatrixSetDiagD) = {{0, OUTPUT_DESC(y)}}; +REG_ADPT_DESC(MatrixSetDiagD, kNameMatrixSetDiagD, ADPT_DESC(MatrixSetDiagD)) + +// DiagPart +INPUT_MAP(DiagPart) = {{1, INPUT_DESC(x)}}; +ATTR_MAP(DiagPart) = EMPTY_ATTR_MAP; +OUTPUT_MAP(DiagPart) = {{0, OUTPUT_DESC(y)}}; +REG_ADPT_DESC(DiagPart, kNameDiagPart, ADPT_DESC(DiagPart)) + +// BatchMatMul +INPUT_MAP(BatchMatMul) = {{1, INPUT_DESC(x1)}, {2, INPUT_DESC(x2)}}; +ATTR_MAP(BatchMatMul) = {{"transpose_x1", ATTR_DESC(adj_x1, AnyTraits())}, + {"transpose_x2", ATTR_DESC(adj_x2, AnyTraits())}}; +OUTPUT_MAP(BatchMatMul) = {{0, OUTPUT_DESC(y)}}; + +// BatchMatMul->BatchMatMulV2 +INPUT_MAP(BatchMatMulV2) = {{1, INPUT_DESC(x1)}, {2, INPUT_DESC(x2)}}; +ATTR_MAP(BatchMatMulV2) = {{"transpose_x1", ATTR_DESC(adj_x1, AnyTraits())}, + {"transpose_x2", ATTR_DESC(adj_x2, AnyTraits())}}; +OUTPUT_MAP(BatchMatMulV2) = {{0, OUTPUT_DESC(y)}}; +REG_ADPT_DESC(BatchMatMul, kNameBatchMatMul, ADPT_DESC(BatchMatMul)) +REG_ADPT_DESC(BatchMatMulV2, kNameBatchMatMulV2, ADPT_DESC(BatchMatMulV2)) + +// L2Loss +INPUT_MAP(L2Loss) = {{1, INPUT_DESC(x)}}; +ATTR_MAP(L2Loss) = EMPTY_ATTR_MAP; +OUTPUT_MAP(L2Loss) = {{0, OUTPUT_DESC(y)}}; +REG_ADPT_DESC(L2Loss, kNameL2Loss, ADPT_DESC(L2Loss)) + +// ScatterElements +INPUT_MAP(ScatterElements) = {{1, INPUT_DESC(data)}, {2, INPUT_DESC(indices)}, {3, INPUT_DESC(updates)}}; +ATTR_MAP(ScatterElements) = {{"axis", ATTR_DESC(axis, AnyTraits())}}; +OUTPUT_MAP(ScatterElements) = {{0, OUTPUT_DESC(y)}}; +REG_ADPT_DESC(ScatterElements, kNameTensorScatterElements, ADPT_DESC(ScatterElements)) + +// FullyConnection +INPUT_MAP(FullyConnection) = {{1, INPUT_DESC(x)}, {2, INPUT_DESC(w)}, {3, INPUT_DESC(b)}, {4, INPUT_DESC(offset_w)}}; + +ATTR_MAP(FullyConnection) = {{"num_output", ATTR_DESC(num_output, AnyTraits())}, + {"transpose", ATTR_DESC(transpose, AnyTraits())}, + {"axis", ATTR_DESC(axis, AnyTraits())}, + {"offset_x", ATTR_DESC(offset_x, AnyTraits())}}; + +OUTPUT_MAP(FullyConnection) = {{0, OUTPUT_DESC(y)}}; +REG_ADPT_DESC(FullyConnection, kNameFullConnection, ADPT_DESC(FullyConnection)) +} // namespace mindspore::transform diff --git a/mindspore/ccsrc/transform-update/mindir_exporter.cc b/mindspore/ccsrc/transform-update/mindir_exporter.cc new file mode 100644 index 00000000000..a2e6b397df6 --- /dev/null +++ b/mindspore/ccsrc/transform-update/mindir_exporter.cc @@ -0,0 +1,1311 @@ +/** + * Copyright 2020-2022 Huawei Technologies Co., Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include + +#include "utils/hash_map.h" +#include "ir/tensor.h" +#include "ir/param_info.h" +#include "ir/func_graph.h" +#include "mindspore/core/ops/core_ops.h" +#include "proto/mind_ir.pb.h" +#include "utils/check_convert_utils.h" +#include "include/common/debug/dump_proto.h" +#include "utils/ms_utils.h" +#include "include/common/utils/utils.h" +#ifndef MINDIR_EXPORT_TENSOR_LAYOUT_CLIP +#include "frontend/parallel/tensor_layout/tensor_layout.h" +#endif +#include "abstract/abstract_function.h" +#include "mindspore/core/utils/file_utils.h" + +namespace mindspore { +using FloatPtr = std::shared_ptr; +using IntPtr = std::shared_ptr; +using UIntPtr = std::shared_ptr; +using ModelProtoPtr = std::shared_ptr; + +// anf type to mindir type map灏 ANF 绫诲瀷鏄犲皠鍒 MindIR 绫诲瀷鐨勬槧灏勮〃 +static mindspore::HashMap g_data_type_map = { + {kNumberTypeBool, mind_ir::TensorProto_DataType_BOOL}, + {kNumberTypeInt8, mind_ir::TensorProto_DataType_INT8}, + {kNumberTypeInt16, mind_ir::TensorProto_DataType_INT16}, + {kNumberTypeInt32, mind_ir::TensorProto_DataType_INT32}, + {kNumberTypeInt64, mind_ir::TensorProto_DataType_INT64}, + {kNumberTypeUInt8, mind_ir::TensorProto_DataType_UINT8}, + {kNumberTypeUInt16, mind_ir::TensorProto_DataType_UINT16}, + {kNumberTypeUInt32, mind_ir::TensorProto_DataType_UINT32}, + {kNumberTypeUInt64, mind_ir::TensorProto_DataType_UINT64}, + {kNumberTypeFloat16, mind_ir::TensorProto_DataType_FLOAT16}, + {kNumberTypeFloat32, mind_ir::TensorProto_DataType_FLOAT}, + {kNumberTypeFloat64, mind_ir::TensorProto_DataType_DOUBLE}, + {kObjectTypeString, mind_ir::TensorProto_DataType_STRING}, + {kNumberTypeComplex64, mind_ir::TensorProto_DataType_COMPLEX64}, + {kNumberTypeComplex128, mind_ir::TensorProto_DataType_COMPLEX128}}; + +static mindspore::HashMap g_data_bits_int_map = { + {8, mind_ir::TensorProto_DataType_INT8}, + {16, mind_ir::TensorProto_DataType_INT16}, + {32, mind_ir::TensorProto_DataType_INT32}, + {64, mind_ir::TensorProto_DataType_INT64}, +}; + +static mindspore::HashMap g_data_bits_uint_map = { + {8, mind_ir::TensorProto_DataType_UINT8}, + {16, mind_ir::TensorProto_DataType_UINT16}, + {32, mind_ir::TensorProto_DataType_UINT32}, + {64, mind_ir::TensorProto_DataType_UINT64}, +}; + +static mindspore::HashMap g_data_bits_float_map = { + {16, mind_ir::TensorProto_DataType_FLOAT16}, + {32, mind_ir::TensorProto_DataType_FLOAT}, + {64, mind_ir::TensorProto_DataType_FLOAT64}, +}; + +static std::set g_export_attr_blacklist = {kAttrDump}; + +// Can build different builder according to format鏍规嵁鏍煎紡鏋勫缓涓嶅悓鐨勭敓鎴愬櫒銆 +class IrExportBuilder; +using IrExportBuilderPtr = std::shared_ptr; +//浣跨敤IrExportBuilderPtr琛ㄧずstd::shared_ptr + +class IrExporter { + public: + explicit IrExporter(IrExportBuilderPtr builder) : builder_(std::move(builder)) {} + virtual ~IrExporter() = default; + std::string GetDumpString(const FuncGraphPtr &func_graph); + ModelProtoPtr GetDumpProto(const FuncGraphPtr &func_graph, const FuncGraphPtr ¶m_layout_fg = nullptr); + + private: + IrExportBuilderPtr builder_; +}; +//澹版槑IrExporter绫 +using IrExporterPtr = std::shared_ptr; + +class IrExportBuilder { + public: + IrExportBuilder() : model_(std::make_shared()) {} + ~IrExportBuilder() = default; + std::string GetProtoString() const; + void BuildModelInfo(); + bool BuildModel(const FuncGraphPtr &func_graph); + ModelProtoPtr Model() { return model_; } + +#ifndef MINDIR_EXPORT_TENSOR_LAYOUT_CLIP + void BuildLayout(const FuncGraphPtr &func_graph); +#endif +//濡傛灉娌℃湁瀹氫箟MINDIR_EXPORT_TENSOR_LAYOUT_CLIP锛屽垯浼氬畾涔変竴涓悕涓築uildLayout鐨勫嚱鏁 + + bool BuildFuncGraph(const FuncGraphPtr &func_graph, mind_ir::GraphProto *const graph_proto); + bool BuildFuncGraphAttrs(const FuncGraphPtr &func_graph, mind_ir::GraphProto *const graph_proto); + bool BuildParameters(const FuncGraphPtr &func_graph, mind_ir::GraphProto *const graph_proto); + bool BuildNodes(const FuncGraphPtr &func_graph, mind_ir::GraphProto *const graph_proto); + bool BuildOutput(const CNodePtr &node, mind_ir::GraphProto *const graph_proto); + bool BuildCNode(const CNodePtr &node, mind_ir::GraphProto *const graph_proto); + std::string BuildInputNode(const AnfNodePtr &node, mind_ir::GraphProto *const graph_proto); + + bool SetValueInfoProto(const AnfNodePtr &node, mind_ir::ValueInfoProto *const value_proto); + bool SetParamToTensorProto(const ParameterPtr ¶m, mind_ir::TensorProto *const tensor_proto); + bool SetTensorProto(const AbstractBasePtr &abstract, mind_ir::TensorProto *const tensor_proto); + bool SetCSRTensorToProto(const AbstractBasePtr &abstract, mind_ir::AttributeProto *const attr_proto); + bool SetCOOTensorToProto(const AbstractBasePtr &abstract, mind_ir::AttributeProto *const attr_proto); + bool SetAttributeProto(const AnfNodePtr &node, mind_ir::NodeProto *const node_proto); + bool SetAbstractToNodeProto(const CNodePtr &node, mind_ir::NodeProto *const node_proto); + bool SetAbstractToNodeProto(const abstract::AbstractBasePtr &abstract, mind_ir::AttributeProto *const attr_proto); + bool SetValueToAttributeProto(const ValuePtr &value, mind_ir::AttributeProto *const attr_proto); + bool SetTypeToAttributeProto(const ValuePtr &value, mind_ir::AttributeProto *const attr_proto); + bool SetScalarToAttributeProto_ir(const ValuePtr &value, mind_ir::AttributeProto *const attr_proto) const; + bool SetScalarToAttributeProtoForInt_ir(const ValuePtr &value, mind_ir::AttributeProto *const attr_proto) const; + bool SetScalarToAttributeProto_irs(const ValuePtr &value, mind_ir::AttributeProto *const attr_proto) const; + bool SetScalarToAttributeProtoForInt_irs(const ValuePtr &value, mind_ir::AttributeProto *const attr_proto) const; + bool SetTypeToAttributeProto_irs(const ValuePtr &value, mind_ir::AttributeProto *const attr_proto); + bool SetTensorToAttributeProto(const ValuePtr &value, mind_ir::AttributeProto *const attr_proto); + bool SetSequenceToAttributeProto(const ValueSequencePtr &value, mind_ir::AttributeProto *const attr_proto); + bool SetSeqElemToAttributeProto(const ValuePtr &value, mind_ir::AttributeProto *const attr_proto); + + mind_ir::TensorProto_DataType GetMindirDataType(TypeId type_id) const; + mind_ir::TensorProto_DataType GetMindirDataBitsIntType(int bits) const; + mind_ir::TensorProto_DataType GetMindirDataBitsFloatType(int bits) const; + mind_ir::TensorProto_DataType GetMindirDataBitsUIntType(int bits) const; + std::string GetNodeName(const AnfNodePtr &node) const; + std::string GetUniqueNodeName(const AnfNodePtr &node); + std::string GetOpTypeName(const AnfNodePtr &node); + size_t GetUniqueID() { return ++unique_id_; } + + private: + bool SetAbstractFuncToAttributeProto(const abstract::AbstractBasePtr &abstract, + mind_ir::AttributeProto *const attr_proto); + std::string GetPrimitiveUniqueName(const PrimitivePtr &primitive_ptr); + bool BuildPrimitives(); + + ModelProtoPtr model_; + mind_ir::NodeProto *last_node_{nullptr}; + std::list todo_; + std::map node_name_map_; + std::map primitive_name_map_; + std::set nodeName_; + size_t unique_id_{0}; + bool top_graph{true}; +}; +//澹版槑IrExportBuilder绫 + +bool IrExportBuilder::SetAbstractFuncToAttributeProto(const abstract::AbstractBasePtr &abstract, + mind_ir::AttributeProto *const attr_proto) { + MS_EXCEPTION_IF_NULL(abstract); + MS_EXCEPTION_IF_NULL(attr_proto); + //濡傛灉abstract銆乤ttr_proto涓虹┖锛屽垯鎶涘嚭寮傚父 + if (abstract->isa()) { + attr_proto->set_type(mind_ir::AttributeProto_AttributeType_FUNCGRAPHCLOSURE); + auto func_name = abstract->cast()->func_graph()->ToString(); + attr_proto->set_s(func_name); + } else if (abstract->isa()) { + attr_proto->set_type(mind_ir::AttributeProto_AttributeType_PRIMITIVECLOSURE); + auto prim = abstract->cast()->prim(); + attr_proto->set_s(GetPrimitiveUniqueName(prim)); + } else if (abstract->isa()) { + attr_proto->set_type(mind_ir::AttributeProto_AttributeType_PARTIALCLOSURE); + auto node_ptr = abstract->cast()->node(); + MS_EXCEPTION_IF_NULL(node_ptr); + attr_proto->set_s(GetUniqueNodeName(node_ptr)); + } else if (abstract->isa()) { + attr_proto->set_type(mind_ir::AttributeProto_AttributeType_UNIONFUNCCLOSURE); + auto visit_func = [this, &attr_proto](const abstract::AbstractFuncAtomPtr &poss) { + auto element_attr_proto = attr_proto->add_values(); + if (!this->SetAbstractFuncToAttributeProto(poss, element_attr_proto)) { + MS_LOG(EXCEPTION) << "Set union function abstract to proto error." << poss->ToString(); + } + }; + abstract->cast()->Visit(visit_func); + } else { + MS_LOG(ERROR) << "The parameter abstract is not an abstractFunction: " << abstract->ToString(); + return false; + } + return true; +} +//瀹炰緥鍖朓rExportBuilder绫讳腑鐨勫嚱鏁癝etAbstractFuncToAttributeProto +//鏍规嵁abstract鍙橀噺涓嬬殑鍚勯」浜嬩緥鏄惁瀛樺湪锛岃缃產ttr_proto鐨勭被鍨嬩负瀵瑰簲鍚嶇О锛岀劧鍚庝粠abstract鍙橀噺涓幏鍙栧搴旂被鍨嬬殑鎸囬拡锛岃繘鑰岃幏鍙栧叾瀵瑰簲鐨勫嚱鏁板浘鍚嶇О +//骞跺皢璇ュ悕绉拌缃负 attr_proto鐨勫瓧绗︿覆灞炴э紝骞惰繑鍥炰负鐪熴傝嫢娌℃湁瀵瑰簲浜嬩緥鍒欐姏鍑哄紓甯稿苟鎶ラ敊锛岃繑鍥炰负鍋囥 + +std::string IrExportBuilder::GetPrimitiveUniqueName(const PrimitivePtr &primitive_ptr) { + auto it = primitive_name_map_.find(primitive_ptr); + if (it != primitive_name_map_.end()) { + return it->second; + } + // Remove this check if we find a way to handle save/load training model with flattened parameters. + if (IsPrimitiveEquals(primitive_ptr, prim::kPrimFlattenConcat)) { + MS_LOG(EXCEPTION) << "Export model with operator '" << primitive_ptr->name() << "' is not supported yet.\n" + << "Please remove 'net.flatten_weights()' in your script and try again."; + } + auto answer = primitive_ptr->name() + ":" + std::to_string(GetUniqueID()); + primitive_name_map_[primitive_ptr] = answer; + return answer; +} +//瀹炰緥鍖朓rExportBuilder绫讳腑鐨勫嚱鏁癎etPrimitiveUniqueName +//濡傛灉鎵惧埌涓绉嶆柟娉曟潵澶勭悊鍏锋湁鎵佸钩鍙傛暟鐨勪繚瀛/鍔犺浇璁粌妯″瀷锛岃鍒犻櫎姝ゆ鏌 + +bool IrExportBuilder::BuildPrimitives() { + // 閬嶅巻 primitive_name_map_ 涓殑姣忎釜鍘熻 + for (auto it = primitive_name_map_.begin(); it != primitive_name_map_.end(); ++it) { + auto prim_proto = model_->add_primitives(); + auto prim = it->first; + prim_proto->set_name(it->second); + prim_proto->set_op_type(prim->name()); + // 鑾峰彇瀹為檯鐨勫師璇紙鍙兘瀛樺湪鍘熻鐨勫寘瑁咃級 + auto real_prim = GetValueWithoutDoSignature(prim)->cast(); + if (real_prim != nullptr) { + prim = real_prim; + } + + // Set primitive attributes閬嶅巻璁剧疆鍘熻鐨勫睘鎬 + for (const auto &attr : prim->attrs()) { + // 妫鏌ュ綋鍓嶅睘鎬ф槸鍚﹀湪榛戝悕鍗曚腑锛屽鏋滄槸鍒欒烦杩 + MS_LOG(DEBUG) << "attr: " << attr.first << " " << attr.second->DumpText() << " " << attr.second->type_name(); + auto iter = g_export_attr_blacklist.find(attr.first); + if (iter != g_export_attr_blacklist.end()) { + continue; + } + // 鍚戝師璇殑 proto 涓坊鍔犱竴涓睘鎬 + mind_ir::AttributeProto *attr_proto = prim_proto->add_attribute(); + attr_proto->set_name(attr.first); + auto attr_value = attr.second; + // 杞崲骞舵鏌ュ睘鎬у + CheckAndConvertUtils::ConvertAttrValueInExport(prim->name(), attr.first, &attr_value); + if (!SetValueToAttributeProto(attr_value, attr_proto)) { + MS_LOG(ERROR) << "Set value to AttributeProto failed."; + return false; + } + } // Loop of attrs + } // Loop of primitives + return true; +} + +std::string IrExporter::GetDumpString(const FuncGraphPtr &func_graph) { + auto dump_proto = GetDumpProto(func_graph); + if (dump_proto == nullptr) { + MS_LOG(EXCEPTION) << "Get dump proto for graph " << func_graph->ToString() << " failed."; + } + return builder_->GetProtoString(); +} +//鑾峰彇DumpString + +ModelProtoPtr IrExporter::GetDumpProto(const FuncGraphPtr &func_graph, const FuncGraphPtr ¶m_layout_fg) { + if ((builder_ == nullptr) || (func_graph == nullptr)) { + MS_LOG(EXCEPTION) << "Input params is null."; + } + + // Export model info + builder_->BuildModelInfo(); + + // Export model and return string + if (!builder_->BuildModel(func_graph)) { + return nullptr; + } + +#ifndef MINDIR_EXPORT_TENSOR_LAYOUT_CLIP + // Export layout information + if (param_layout_fg) { + builder_->BuildLayout(param_layout_fg); + } +#endif + return builder_->Model(); +} + +std::string IrExportBuilder::GetProtoString() const { + MS_LOG(DEBUG) << "BuildModel complete!"; + return model_->SerializeAsString(); +} + +void IrExportBuilder::BuildModelInfo() { + // 鏋勫缓妯″瀷淇℃伅 + constexpr auto ir_version = "0.1.1"; + constexpr auto mindspore_name = "MindSpore"; + model_->set_ir_version(ir_version);// 璁剧疆IR鐗堟湰 + model_->set_producer_name(mindspore_name);// 璁剧疆鐢熶骇鑰呭悕绉 + model_->set_model_version(VERSION);// 璁剧疆妯″瀷鐗堟湰 + model_->set_little_endian(common::IsLittleByteOrder());// 璁剧疆瀛楄妭搴 + model_->set_mind_ir_version(mind_ir::Version_MAX);// 璁剧疆Mind IR鐗堟湰 +} + +#ifndef MINDIR_EXPORT_TENSOR_LAYOUT_CLIP +void IrExportBuilder::BuildLayout(const FuncGraphPtr &func_graph) { + // 鏋勫缓寮犻噺甯冨眬淇℃伅 + MS_EXCEPTION_IF_NULL(func_graph); + std::vector graph_params = func_graph->parameters();// 鑾峰彇鍥剧殑鍙傛暟鑺傜偣 + mind_ir::ParallelProto *parallel_proto = model_->mutable_parallel();// 鑾峰彇妯″瀷鐨勫苟琛屼俊鎭 + // 閬嶅巻鍥剧殑鍙傛暟鑺傜偣 + for (auto para : graph_params) { + std::string name = std::static_pointer_cast(para)->name();// 鑾峰彇鍙傛暟鑺傜偣鐨勫悕绉 + auto tensor_layout = para->user_data();// 鑾峰彇鍙傛暟鑺傜偣鐨勫紶閲忓竷灞淇℃伅 + if (tensor_layout == nullptr) { + MS_LOG(INFO) << "GetParameterLayout nullptr name = " << name; + } else { + mind_ir::LayoutProto *layoutProto = parallel_proto->add_layout();// 娣诲姞寮犻噺甯冨眬淇℃伅鍒版ā鍨嬬殑骞惰淇℃伅涓 + + // Get all the information for layput + // 鑾峰彇寮犻噺甯冨眬鐨勫悇绉嶄俊鎭 + auto device_arrangement = tensor_layout->device_arrangement().array(); + auto tensor_map = tensor_layout->tensor_map().array(); + auto slice_shape = tensor_layout->slice_shape().array(); + int64_t field_size = tensor_layout->get_field_size(); + bool uniform_split = tensor_layout->uniform_split(); + std::string opt_shard_group = tensor_layout->opt_shard_group(); + + // Save all information to Layout Proto + // 灏嗕俊鎭繚瀛樺埌甯冨眬淇℃伅涓 + layoutProto->set_name(name); + for (auto device_arrangement_element : device_arrangement) { + layoutProto->add_device_arrangement_int(device_arrangement_element); + } + for (auto tensor_map_element : tensor_map) { + layoutProto->add_tensor_map_int(tensor_map_element); + } + for (auto slice_shape_element : slice_shape) { + layoutProto->add_slice_shape_int(slice_shape_element); + } + layoutProto->set_field_size(field_size); + layoutProto->set_uniform_split(uniform_split); + layoutProto->set_opt_shard_group(opt_shard_group); + } + } +} +#endif + +bool IrExportBuilder::BuildModel(const FuncGraphPtr &func_graph) { + // 鏋勫缓妯″瀷鐨勫嚱鏁 + MS_EXCEPTION_IF_NULL(func_graph);// 妫鏌ヨ緭鍏ュ嚱鏁板浘鏄惁涓虹┖ + // 娓呯┖寰呭姙鍒楄〃銆佽妭鐐瑰悕绉伴泦鍚堝拰鍘熻鍚嶇О鏄犲皠 + mind_ir::GraphProto *graph_proto = model_->mutable_graph(); + graph_proto->set_name(func_graph->ToString()); + graph_proto->set_bprop_hash(func_graph->bprop_hash()); + // 娓呯┖寰呭姙鍒楄〃銆佽妭鐐瑰悕绉伴泦鍚堝拰鍘熻鍚嶇О鏄犲皠 + todo_.clear(); + nodeName_.clear(); + primitive_name_map_.clear(); + // Build the main funcGraph + // 鏋勫缓涓诲嚱鏁板浘 + // 灏嗕富鍑芥暟鍥惧悕绉版坊鍔犲埌鑺傜偣鍚嶇О闆嗗悎 + (void)nodeName_.insert(func_graph->ToString()); + top_graph = true; + if (!BuildFuncGraph(func_graph, graph_proto)) { + MS_LOG(ERROR) << "Build func_graph " << func_graph->ToString() << " failed."; + return false; + } + + // Build child funcGraphs + // 鏋勫缓瀛愬嚱鏁板浘 + std::set graphVisited; + (void)graphVisited.insert(func_graph); + top_graph = false; + while (!todo_.empty()) { + // 浠庡緟鍔炲垪琛ㄤ腑鍙栧嚭涓涓嚱鏁板浘 + FuncGraphPtr fg = todo_.back(); + todo_.pop_back(); + // 濡傛灉鍑芥暟鍥惧凡缁忚璁块棶杩囷紝鍒欑户缁鐞嗕笅涓涓嚱鏁板浘 + if (graphVisited.count(fg) > 0) { + continue; + } + // 妫鏌ヨ妭鐐瑰悕绉版槸鍚﹂噸澶嶏紝濡傛灉閲嶅鍒欐姤閿 + if (nodeName_.count(fg->ToString()) > 0) { + MS_LOG(ERROR) << "There is a duplicate name: " << fg->ToString(); + return false; + } + // 灏嗗嚱鏁板浘鍚嶇О娣诲姞鍒拌妭鐐瑰悕绉伴泦鍚堝拰宸茶闂殑鍑芥暟鍥鹃泦鍚 + (void)nodeName_.insert(fg->ToString()); + (void)graphVisited.insert(fg); + // 鍒涘缓涓涓柊鐨勫嚱鏁板浘瀵硅薄锛屽苟鏋勫缓璇ュ嚱鏁板浘 + auto graph = model_->add_functions(); + if (!BuildFuncGraph(fg, graph)) { + MS_LOG(ERROR) << "Build func_graph " << fg->ToString() << " failed."; + return false; + } + } + // 鏋勫缓鍘熻淇℃伅 + if (!BuildPrimitives()) { + return false; + } + // Release resource + // 閲婃斁璧勬簮锛屾竻绌鸿妭鐐瑰悕绉伴泦鍚堛佽妭鐐瑰悕绉版槧灏勫拰鍘熻鍚嶇О鏄犲皠 + nodeName_.clear(); + node_name_map_.clear(); + primitive_name_map_.clear(); + return true; +} + +bool IrExportBuilder::BuildFuncGraph(const FuncGraphPtr &func_graph, mind_ir::GraphProto *const graph_proto) { + // Export funcGraph name. + graph_proto->set_name(func_graph->ToString()); + // Export parameters + // 1. parameters should be mapped to ValueInfoProto + // 2. parameters with default value should be mapped to Initializer + //瀵煎嚭鍙傛暟 + //1.鍙傛暟搴旀槧灏勫埌ValueInfoProto + //2.鍏锋湁榛樿鍊肩殑鍙傛暟搴旀槧灏勫埌Initializer + if (!BuildParameters(func_graph, graph_proto)) { + MS_LOG(ERROR) << "Build parameters failed."; + return false; + } + + // Export graph attributes + //瀵煎嚭鍥惧舰灞炴 + if (!BuildFuncGraphAttrs(func_graph, graph_proto)) { + MS_LOG(ERROR) << "Build attributes for graph failed."; + return false; + } + + // Export operator nodes(include output) + //瀵煎嚭鎿嶄綔鍛樿妭鐐癸紙鍖呮嫭杈撳嚭锛 + return BuildNodes(func_graph, graph_proto); +} + +bool IrExportBuilder::BuildFuncGraphAttrs(const FuncGraphPtr &func_graph, mind_ir::GraphProto *const graph_proto) { + MS_EXCEPTION_IF_NULL(func_graph); + MS_EXCEPTION_IF_NULL(graph_proto); + // 閬嶅巻鍑芥暟鍥剧殑鎵鏈夊睘鎬 + for (const auto &attr : func_graph->attrs()) { + // 杈撳嚭璋冭瘯淇℃伅锛屾墦鍗板睘鎬у悕銆佸睘鎬у肩殑鏂囨湰琛ㄧず鍜屽睘鎬у肩殑绫诲瀷鍚 + MS_LOG(DEBUG) << "attr: " << attr.first << " " << attr.second->DumpText() << " " << attr.second->type_name(); + // 鍦ㄥ鍑哄睘鎬ч粦鍚嶅崟涓煡鎵惧綋鍓嶅睘鎬у悕 + auto iter = g_export_attr_blacklist.find(attr.first); + if (iter != g_export_attr_blacklist.end()) { + continue; + } + // 鍒涘缓涓涓狝ttributeProto瀵硅薄锛屽苟璁剧疆灞炴у悕绉 + mind_ir::AttributeProto *attr_proto = graph_proto->add_attribute(); + attr_proto->set_name(attr.first); + // 灏嗗睘鎬у艰浆鎹㈠苟璁剧疆鍒癆ttributeProto涓 + if (!SetValueToAttributeProto(attr.second, attr_proto)) { + MS_LOG(ERROR) << "Set value to AttributeProto for GraphProto failed."; + return false; + } + } + return true; +} + +bool IrExportBuilder::BuildParameters(const FuncGraphPtr &func_graph, mind_ir::GraphProto *const graph_proto) { + // 鏋勫缓鍑芥暟鍥剧殑鍙傛暟淇℃伅骞舵坊鍔犲埌GraphProto涓 + MS_EXCEPTION_IF_NULL(func_graph); + MS_EXCEPTION_IF_NULL(graph_proto); + // 閬嶅巻鍑芥暟鍥剧殑鎵鏈夊弬鏁拌妭鐐 + for (auto &item : func_graph->parameters()) { + MS_EXCEPTION_IF_NULL(item); + auto param = item->cast(); + // 濡傛灉鏃犳硶灏嗚妭鐐硅浆鎹负鍙傛暟鑺傜偣锛岃緭鍑洪敊璇俊鎭苟杩斿洖澶辫触 + if (param == nullptr) { + MS_LOG(ERROR) << "Parameter: '" << item->ToString() << "' could not cast to parameter."; + return false; + } + // 鑾峰彇鍞竴鐨勫弬鏁板悕绉 + std::string param_name = GetUniqueNodeName(param); + // 濡傛灉鏄《灞傚嚱鏁板浘涓斿弬鏁板叿鏈夐粯璁ゅ + if (top_graph && param->has_default()) { + MS_LOG(DEBUG) << "Parameter: '" << item->DebugString(); + mind_ir::TensorProto *parameter_proto = graph_proto->add_parameter(); + // 璁剧疆鍙傛暟鑺傜偣鐨勫悕绉帮紝骞跺皢鍙傛暟杞崲涓篢ensorProto + parameter_proto->set_name(param_name); + if (!SetParamToTensorProto(param, parameter_proto)) { + MS_LOG(ERROR) << "Set parameter " << param->DebugString() << " to TensorProto failed."; + return false; + } + } else { + mind_ir::ValueInfoProto *input_proto = graph_proto->add_input(); + // 璁剧疆鍙傛暟鑺傜偣鐨勫悕绉帮紝骞跺皢鍙傛暟杞崲涓篤alueInfoProto + input_proto->set_name(param_name); + // 妫鏌ュ弬鏁板悕绉版槸鍚﹂噸澶嶏紝濡傛灉鏄垯杈撳嚭閿欒淇℃伅骞惰繑鍥炲け璐 + if (!SetValueInfoProto(param, input_proto)) { + MS_LOG(ERROR) << "Set parameter " << param->DebugString() << " to TensorProto failed."; + return false; + } + } + if (nodeName_.count(param_name) > 0) { + MS_LOG(ERROR) << "parameter name is duplicate:" << param_name; + return false; + } + (void)nodeName_.insert(param_name); + } + return true; +} + +mind_ir::TensorProto_DataType IrExportBuilder::GetMindirDataType(TypeId type_id) const { + auto iter = g_data_type_map.find(type_id); + if (iter == g_data_type_map.end()) { + MS_LOG(ERROR) << "Convert type error, unsupported type! " << type_id; + return mind_ir::TensorProto_DataType_UNDEFINED; + } + return iter->second; +} +//鑾峰彇Mindir鏁版嵁绫诲瀷 + +mind_ir::TensorProto_DataType IrExportBuilder::GetMindirDataBitsIntType(int bits) const { + auto iter = g_data_bits_int_map.find(bits); + if (iter == g_data_bits_int_map.end()) { + MS_LOG(ERROR) << "Convert bits int error, unsupported bits! " << bits; + return mind_ir::TensorProto_DataType_UNDEFINED; + } + return iter->second; +} +//鑾峰彇Mindir鏁版嵁鏄惁涓篿nt绫诲瀷 + +mind_ir::TensorProto_DataType IrExportBuilder::GetMindirDataBitsUIntType(int bits) const { + auto iter = g_data_bits_uint_map.find(bits); + if (iter == g_data_bits_uint_map.end()) { + MS_LOG(ERROR) << "Convert bits uint error, unsupported bits! " << bits; + return mind_ir::TensorProto_DataType_UNDEFINED; + } + return iter->second; +} +//鑾峰彇Mindir鏁版嵁鏄惁涓簎int绫诲瀷 +mind_ir::TensorProto_DataType IrExportBuilder::GetMindirDataBitsFloatType(int bits) const { + auto iter = g_data_bits_float_map.find(bits); + if (iter == g_data_bits_float_map.end()) { + MS_LOG(ERROR) << "Convert bits float error, unsupported bits! " << bits; + return mind_ir::TensorProto_DataType_UNDEFINED; + } + return iter->second; +} +//鑾峰彇Mindir鏁版嵁鏄惁涓篺loat绫诲瀷 +bool IrExportBuilder::SetValueInfoProto(const AnfNodePtr &node, mind_ir::ValueInfoProto *const value_proto) { + if (node == nullptr || value_proto == nullptr) { + MS_LOG(EXCEPTION) << "AnfNode or ValueInfo is null!"; + } + MS_LOG(DEBUG) << "SetValueInfoProto: " << node->DebugString(); + const TypePtr &type = node->Type(); + const BaseShapePtr &shape = node->Shape(); + // For the bprop fg which has not been renormalized. + if (type == nullptr || shape == nullptr) { + return true; + } + if (type->isa() && shape->isa()) { + mind_ir::TensorProto *tensor_proto = value_proto->add_tensor(); + if (!SetTensorProto(node->abstract(), tensor_proto)) { + return false; + } + } else if (type->isa()) { + mind_ir::AttributeProto *attribute = value_proto->mutable_attr_info(); + if (!SetAbstractToNodeProto(node->abstract(), attribute)) { + MS_LOG(ERROR) << "Set shape to Proto for " << node->DebugString() << " failed."; + return false; + } + attribute->set_name("shape"); + } else { + value_proto->set_denotation(type->type_name()); + } + MS_LOG(DEBUG) << "Value type: " << type->type_name(); + return true; +} +//璁剧疆proto鍙傛暟 + +bool IrExportBuilder::SetTensorToAttributeProto(const ValuePtr &value, mind_ir::AttributeProto *const attr_proto) { + if (value == nullptr || attr_proto == nullptr) { + MS_LOG(EXCEPTION) << "ValuePtr or AttributeProto is null!"; + } + attr_proto->set_type(mind_ir::AttributeProto_AttributeType_TENSORS); + mind_ir::TensorProto *tensor_proto = attr_proto->add_tensors(); + tensor_proto->set_name("value0"); + auto data = value->cast(); + MS_EXCEPTION_IF_NULL(data); + tensor_proto->set_raw_data(data->data_c(), static_cast(data->data().nbytes())); + auto dtype = data->data_type(); + auto shape = data->shape_c(); + auto data_type = GetMindirDataType(dtype); + if (data_type == mind_ir::TensorProto_DataType_UNDEFINED) { + return false; + } + tensor_proto->set_data_type(data_type); + for (const auto &dim : shape) { + tensor_proto->add_dims(dim); + } + return true; +} +//璁剧疆proto鍙傛暟 +bool IrExportBuilder::SetCSRTensorToProto(const AbstractBasePtr &abstract, mind_ir::AttributeProto *const attr_proto) { + abstract::AbstractCSRTensorPtr csr_tensor_abs = abstract->cast(); + MS_EXCEPTION_IF_NULL(csr_tensor_abs); + attr_proto->set_type(mind_ir::AttributeProto_AttributeType_CSR_TENSOR); + mind_ir::AttributeProto *indptr = attr_proto->add_values(); + bool res = SetAbstractToNodeProto(csr_tensor_abs->indptr(), indptr); + mind_ir::AttributeProto *indices = attr_proto->add_values(); + res = res && SetAbstractToNodeProto(csr_tensor_abs->indices(), indices); + mind_ir::AttributeProto *values = attr_proto->add_values(); + res = res && SetAbstractToNodeProto(csr_tensor_abs->values(), values); + mind_ir::AttributeProto *shape = attr_proto->add_values(); + res = res && SetAbstractToNodeProto(csr_tensor_abs->shape(), shape); + return res; +} +//璁剧疆proto鍙傛暟 +bool IrExportBuilder::SetCOOTensorToProto(const AbstractBasePtr &abstract, mind_ir::AttributeProto *const attr_proto) { + abstract::AbstractCOOTensorPtr coo_tensor_abs = abstract->cast(); + MS_EXCEPTION_IF_NULL(coo_tensor_abs); + attr_proto->set_type(mind_ir::AttributeProto_AttributeType_COO_TENSOR); + mind_ir::AttributeProto *indices = attr_proto->add_values(); + bool res = SetAbstractToNodeProto(coo_tensor_abs->indices(), indices); + mind_ir::AttributeProto *values = attr_proto->add_values(); + res = res && SetAbstractToNodeProto(coo_tensor_abs->values(), values); + mind_ir::AttributeProto *shape = attr_proto->add_values(); + res = res && SetAbstractToNodeProto(coo_tensor_abs->shape(), shape); + return res; +} +//璁剧疆proto鍙傛暟 +bool IrExportBuilder::SetTensorProto(const AbstractBasePtr &abstract, mind_ir::TensorProto *const tensor_proto) { + auto type = abstract->BuildType(); + auto shape = abstract->BuildShape(); + if (!type->isa() || !shape->isa()) { + MS_LOG(ERROR) << "Type or shape is not supported! " << type->ToString(); + return false; + } + auto tensor = type->cast(); + auto tensor_shape = shape->cast(); + const auto &dims = tensor_shape->shape(); + auto data_type = GetMindirDataType(tensor->element()->type_id()); + if (data_type == mind_ir::TensorProto_DataType_UNDEFINED) { + return false; + } + tensor_proto->set_data_type(data_type); + for (const auto &dim : dims) { + tensor_proto->add_dims(dim); + } + if (tensor_shape->IsDynamic()) { + auto min_shape = tensor_shape->min_shape(); + auto max_shape = tensor_shape->max_shape(); + for (auto item : min_shape) { + tensor_proto->add_min_dims(item); + } + for (auto item : max_shape) { + tensor_proto->add_max_dims(item); + } + } + if (!abstract->name().empty()) { + tensor_proto->set_name(abstract->name()); + } + // Deal Ref + if (!type->isa()) { + return true; + } + + auto abs_ref = abstract->cast(); + if (abs_ref == nullptr) { + MS_LOG(ERROR) << "The abstract " << abstract->ToString() << " should be AbstractRefTensor."; + return false; + } + auto ref_key_value = abs_ref->ref_key_value()->cast(); + if (ref_key_value == nullptr) { + MS_LOG(INFO) << "The ref_key_value of abstract ref " << abstract->ToString() << " is nullptr"; + return true; + } + tensor_proto->set_ref_key(ref_key_value->value()); + return true; +} +//璁剧疆proto鍙傛暟 +bool IrExportBuilder::SetParamToTensorProto(const ParameterPtr ¶m, mind_ir::TensorProto *const tensor_proto) { + if (param == nullptr || tensor_proto == nullptr) { + MS_LOG(EXCEPTION) << "Parameter or TensorProto is null!"; + } + MS_LOG(DEBUG) << "SetParamToTensorProto: " << param->DebugString(); + return SetTensorProto(param->abstract(), tensor_proto); +} +//璁剧疆proto鍙傛暟 +bool IrExportBuilder::BuildNodes(const FuncGraphPtr &func_graph, mind_ir::GraphProto *const graph_proto) { + // 鏋勫缓鍑芥暟鍥句腑鐨勮妭鐐逛俊鎭苟娣诲姞鍒癎raphProto涓 + std::vector nodes = TopoSort(func_graph->get_return(), SuccIncoming, AlwaysInclude);// 浣跨敤鎷撴墤鎺掑簭鑾峰彇鍑芥暟鍥句腑鐨勮妭鐐归『搴 + for (const AnfNodePtr &node : nodes) {// 閬嶅巻鎵鏈夎妭鐐 + MS_EXCEPTION_IF_NULL(node); + // 濡傛灉鑺傜偣涓嶆槸CNode绫诲瀷锛屽垯杈撳嚭璋冭瘯淇℃伅骞剁户缁鐞嗕笅涓涓妭鐐 + if (!node->isa()) { + MS_LOG(DEBUG) << "Node: '" << node->ToString() << "' is not cnode"; + continue; + } + auto cnode = node->cast(); + // 濡傛灉鑺傜偣鏄嚱鏁板浘鐨勮繑鍥炶妭鐐 + if (cnode == func_graph->get_return()) { + // 鏋勫缓杩斿洖鑺傜偣鐨勮緭鍑轰俊鎭苟娣诲姞鍒癎raphProto + if (!BuildOutput(cnode, graph_proto)) { + MS_LOG(ERROR) << "Build output for graph " << func_graph->ToString() << " failed."; + return false; + } + } else { + // 鏋勫缓鏅欳Node鑺傜偣鐨勪俊鎭苟娣诲姞鍒癎raphProto + if (!BuildCNode(cnode, graph_proto)) { + MS_LOG(ERROR) << "Build proto for cnode " << cnode->DebugString() << " failed."; + return false; + } + } + } + return true; +} + +bool IrExportBuilder::BuildOutput(const CNodePtr &node, mind_ir::GraphProto *const graph_proto) { + MS_EXCEPTION_IF_NULL(node); + const int OutputSize = 2; + if (node->size() != OutputSize) { + MS_LOG(ERROR) << "Number of inputs of return node is not equal to 2."; + return false; + } + AnfNodePtr arg = node->input(1); + std::string node_name = BuildInputNode(arg, graph_proto); + if (node_name.empty()) { + MS_LOG(ERROR) << "Build input node failed for arg " << arg->DebugString(); + return false; + } + mind_ir::ValueInfoProto *output_proto = graph_proto->add_output(); + output_proto->set_name(node_name); + return SetValueInfoProto(arg, output_proto); +} +//寤虹珛杈撳嚭 +std::string IrExportBuilder::GetOpTypeName(const AnfNodePtr &node) { + // May be ValueNode/CNode/Parameter + std::string type_name = ""; + if (IsValueNode(node)) { + PrimitivePtr prim = GetValueNode(node); + MS_EXCEPTION_IF_NULL(prim); + type_name = "REF::" + GetPrimitiveUniqueName(prim); + } else if (IsValueNode(node)) { + FuncGraphPtr fg = GetValueNode(node); + MS_EXCEPTION_IF_NULL(fg); + todo_.push_back(fg); + type_name = "REF::" + fg->ToString(); + } else if (node->isa() || node->isa()) { + auto nodeName = GetUniqueNodeName(node); + type_name = "REF::" + nodeName; + if (nodeName_.count(nodeName) == 0) { + MS_LOG(ERROR) << "There is not the name: " << nodeName; + return ""; + } + } else { + MS_LOG(ERROR) << "Need to support op type: " << node->type_name(); + return ""; + } + MS_LOG(DEBUG) << "ExportType: " << type_name; + return type_name; +} +//鑾峰彇OpType鐨勭被鍨嬪悕锛屽彲鑳戒负ValueNode/CNode/Parameter +bool IrExportBuilder::SetAbstractToNodeProto(const AbstractBasePtr &abs, mind_ir::AttributeProto *const attr_proto) { + auto type = abs->BuildType(); + auto shape = abs->BuildShape(); + if (type->isa()) { + attr_proto->set_type(mind_ir::AttributeProto_AttributeType_TUPLE); + auto tuple_abs = abs->cast(); + for (size_t i = 0; i < tuple_abs->size(); i++) { + mind_ir::AttributeProto *attr_values = attr_proto->add_values(); + if (!SetAbstractToNodeProto((*tuple_abs)[i], attr_values)) { + return false; + } + } + } else if (type->isa() && shape->isa()) { + attr_proto->set_type(mind_ir::AttributeProto_AttributeType_TENSORS); + mind_ir::TensorProto *tensor_proto = attr_proto->add_tensors(); + return SetTensorProto(abs, tensor_proto); + } else if (type->isa()) { + if (type->isa()) { + attr_proto->set_type(mind_ir::AttributeProto_AttributeType_BOOL); + } else { + attr_proto->set_type(mind_ir::AttributeProto_AttributeType_TENSORS); + mind_ir::TensorProto *tensor_proto = attr_proto->add_tensors(); + auto data_type = GetMindirDataType(type->type_id()); + tensor_proto->set_data_type(data_type); + tensor_proto->add_dims(1); + } + } else if (type->isa()) { + if (!SetAbstractFuncToAttributeProto(abs, attr_proto)) { + return false; + } + } else if (type->isa()) { + attr_proto->set_type(mind_ir::AttributeProto_AttributeType_STRING); + } else if (type->isa()) { + attr_proto->set_type(mind_ir::AttributeProto_AttributeType_UMONAD); + } else if (type->isa()) { + attr_proto->set_type(mind_ir::AttributeProto_AttributeType_IOMONAD); + } else if (type->isa()) { + auto csr_tensor_abs = abs->cast(); + if (!SetCSRTensorToProto(csr_tensor_abs, attr_proto)) { + return false; + } + } else if (type->isa()) { + auto coo_tensor_abs = abs->cast(); + if (!SetCOOTensorToProto(coo_tensor_abs, attr_proto)) { + return false; + } + } else { + MS_LOG(ERROR) << "Type of cnode need to be supported: " << type->type_name(); + return false; + } + return true; +} +//璁剧疆proto鍙傛暟 +bool IrExportBuilder::SetAbstractToNodeProto(const CNodePtr &node, mind_ir::NodeProto *const node_proto) { + // Get shape of cnode + // 1. need to get shape from tuple element + // 2. save shape in TensorProto + MS_EXCEPTION_IF_NULL(node); + auto type = node->Type(); + auto shape = node->Shape(); + auto abs = node->abstract(); + // For the bprop fg which has not been renormalized. + if (type == nullptr || shape == nullptr) { + return true; + } + mind_ir::AttributeProto *attr_proto = node_proto->add_attribute(); + if (!SetAbstractToNodeProto(abs, attr_proto)) { + MS_LOG(ERROR) << "Set shape to NodeProto for " << node->DebugString() << " failed."; + return false; + } + attr_proto->set_name("shape"); + return true; +} +//璁剧疆proto鍙傛暟 +bool IrExportBuilder::BuildCNode(const CNodePtr &node, mind_ir::GraphProto *const graph_proto) { + // 鏋勫缓璁$畻鍥句腑鐨勪竴涓 CNode 鑺傜偣锛屽苟灏嗗叾琛ㄧず娣诲姞鍒板浘鐨 proto 涓 + auto inputs_size = node->size();// 鑾峰彇 CNode 鐨勮緭鍏ユ暟閲 + if (inputs_size < 1) { + MS_LOG(ERROR) << "Inputs of node " << node->DebugString() << " is empty"; + return false; + } + + // Need to build input node before dealing with cnode + // 闇瑕佸厛鏋勫缓杈撳叆鑺傜偣锛岀劧鍚庡啀澶勭悊 CNode + std::vector input_names; + for (size_t i = 1; i < inputs_size; i++) { + auto input = node->input(i); + std::string node_name = BuildInputNode(input, graph_proto);// 鏋勫缓杈撳叆鑺傜偣骞惰幏鍙栬妭鐐瑰悕 + if (node_name.empty()) { + MS_LOG(ERROR) << "Build input node for " << input->DebugString() << " failed."; + return false; + } + input_names.push_back(node_name);// 灏嗚緭鍏ヨ妭鐐瑰悕鍔犲叆鍒楄〃 + } + + // Build cnode + // 鏋勫缓 CNode + mind_ir::NodeProto *node_proto = graph_proto->add_node();// 娣诲姞涓涓妭鐐硅〃绀哄埌鍥剧殑 proto 涓 + std::string output_name = GetUniqueNodeName(node);// 鑾峰彇鍞竴鐨勮妭鐐瑰悕 + if (nodeName_.count(output_name) > 0) { + MS_LOG(EXCEPTION) << "There is a duplicate name: " << output_name; + } + (void)nodeName_.insert(output_name);// 灏嗚妭鐐瑰悕鍔犲叆宸茬敤鍚嶅瓧闆嗗悎 + node_proto->add_output(output_name);// 璁剧疆鑺傜偣鐨勮緭鍑哄悕 + node_proto->set_name(output_name);// 璁剧疆鑺傜偣鐨勫悕瀛 + node_proto->set_domain(node->fullname_with_scope());// 璁剧疆鑺傜偣鐨勫煙 + AnfNodePtr op = node->input(0);// 鑾峰彇鎿嶄綔鑺傜偣 + std::string type_name = GetOpTypeName(op);// 鑾峰彇鎿嶄綔绫诲瀷鍚 + if (type_name.empty()) { + MS_LOG(ERROR) << "Get op type name for " << op->DebugString() << " failed."; + return false; + } + node_proto->set_op_type(type_name);// 璁剧疆鑺傜偣鐨勬搷浣滅被鍨 + last_node_ = node_proto;// 璁板綍鏈鍚庝竴涓妭鐐 + // Maybe Tensor or Function or nullptr + if (!SetAbstractToNodeProto(node, node_proto)) { + return false; + } + // 灏嗚緭鍏ヨ妭鐐瑰悕鍔犲叆鑺傜偣鐨勮緭鍏ュ垪琛ㄤ腑 + (void)std::for_each(input_names.begin(), input_names.end(), + [&node_proto](const string &name) { node_proto->add_input(name); }); + return true; +} + +std::string IrExportBuilder::BuildInputNode(const AnfNodePtr &node, mind_ir::GraphProto *const graph_proto) { + // Return the NodeName that the node has been processed. + auto iter = node_name_map_.find(node); + if (iter != node_name_map_.end()) { + return iter->second; + } + + std::string node_name = GetUniqueNodeName(node); + // FuncGraph will be added to functions and the input name is the function name. + if (IsValueNode(node)) { + FuncGraphPtr fg = GetValueNode(node); + todo_.push_back(fg); + return fg->ToString(); + } + if (node->isa()) { + (void)nodeName_.insert(node_name); + // When node input is a ValueNode, need to create a Constant Node + mind_ir::NodeProto *node_proto = graph_proto->add_node(); + node_proto->set_name(node_name); + node_proto->add_output(node_name); + if (!SetAttributeProto(node, node_proto)) { + return ""; + } + } + return node_name; +} +//寤虹珛杈撳叆鑺傜偣 +std::string IrExportBuilder::GetUniqueNodeName(const AnfNodePtr &node) { + // Naming anfnode + // 1. parameter is unique in one func_graph + // 2. cnode and valuenode may be reduplicative, so add index to identify. + auto iter = node_name_map_.find(node); + if (iter != node_name_map_.end()) { + return iter->second; + } else { + std::string node_name = GetNodeName(node); + // Compatible before. CNode = FuncGraphName:CNodeName:index ,Parameter = FuncGraphName:ParameterName + if (node->isa()) { + node_name = node_name + ":" + std::to_string(GetUniqueID()); + } + // Avoid duplicate name. + while (nodeName_.count(node_name) > 0) { + node_name = node_name + "_" + std::to_string(GetUniqueID()); + } + node_name_map_[node] = node_name; + return node_name; + } +} +//鑾峰緱鏈‘瀹氳妭鐐圭殑鍚嶅瓧 +std::string IrExportBuilder::GetNodeName(const AnfNodePtr &node) const { + MS_EXCEPTION_IF_NULL(node); + std::string node_name = ""; + if (node->func_graph() != nullptr) { + node_name = node->func_graph()->ToString() + ":"; + } + if (node->isa()) { + // Needn't value + node_name += node->AnfNode::ToString(); + } else { + node_name += node->ToString(); + } + MS_LOG(DEBUG) << "GetNodeName: " << node_name; + return node_name; +} + +bool IrExportBuilder::SetAttributeProto(const AnfNodePtr &node, mind_ir::NodeProto *const node_proto) { + if (node == nullptr || node_proto == nullptr) { + MS_LOG(EXCEPTION) << "AnfNode or NodeProto is null!"; + } + auto value_node = node->cast(); + MS_EXCEPTION_IF_NULL(value_node); + auto value = value_node->value(); + node_proto->set_op_type("Constant"); + mind_ir::AttributeProto *attr_proto = node_proto->add_attribute(); + attr_proto->set_name("value"); + MS_LOG(DEBUG) << "Set Constant attribute: " << value->ToString(); + return SetValueToAttributeProto(value, attr_proto); +} +//鑾峰緱鑺傜偣鍚 +bool IrExportBuilder::SetTypeToAttributeProto(const ValuePtr &value, mind_ir::AttributeProto *const attr_proto) { + if (value == nullptr || attr_proto == nullptr) { + MS_LOG(EXCEPTION) << "ValuePtr or AttributeProto is null!"; + } + attr_proto->set_type(mind_ir::AttributeProto_AttributeType_TENSORS); + mind_ir::TensorProto *tensor_proto = attr_proto->add_tensors(); + if (value->isa()) { + tensor_proto->set_name("value0"); + auto int_value = value->cast(); + auto data_type = GetMindirDataBitsIntType(int_value->nbits()); + if (data_type == mind_ir::TensorProto_DataType_UNDEFINED) { + return false; + } + tensor_proto->set_data_type(data_type); + } else if (value->isa()) { + tensor_proto->set_name("value0"); + auto float_value = value->cast(); + auto data_type = GetMindirDataBitsUIntType(float_value->nbits()); + if (data_type == mind_ir::TensorProto_DataType_UNDEFINED) { + return false; + } + tensor_proto->set_data_type(data_type); + } else if (value->isa()) { + tensor_proto->set_name("value0"); + auto float_value = value->cast(); + auto data_type = GetMindirDataBitsFloatType(float_value->nbits()); + if (data_type == mind_ir::TensorProto_DataType_UNDEFINED) { + return false; + } + tensor_proto->set_data_type(data_type); + } else if (value->isa()) { + tensor_proto->set_name("value0"); + tensor_proto->set_data_type(mind_ir::TensorProto_DataType_BOOL); + } else if (value->isa()) { + tensor_proto->set_name("tensor0"); + auto elem_type = value->cast()->element(); + if (elem_type->isa()) { + auto int_value = elem_type->cast(); + auto data_type = GetMindirDataBitsIntType(int_value->nbits()); + if (data_type == mind_ir::TensorProto_DataType_UNDEFINED) { + return false; + } + tensor_proto->set_data_type(data_type); + } else if (elem_type->isa()) { + auto float_value = elem_type->cast(); + auto data_type = GetMindirDataBitsFloatType(float_value->nbits()); + if (data_type == mind_ir::TensorProto_DataType_UNDEFINED) { + return false; + } + tensor_proto->set_data_type(data_type); + } else { + MS_LOG(ERROR) << "Unsupported type " << elem_type->type_name(); + return false; + } + } else { + MS_LOG(EXCEPTION) << "Unsupported type: " << value->type_name(); + } + return true; +} +//璁剧疆proto鍚 +bool IrExportBuilder::SetValueToAttributeProto(const ValuePtr &value, mind_ir::AttributeProto *const attr_proto) { + if (value == nullptr || attr_proto == nullptr) { + MS_LOG(EXCEPTION) << "ValuePtr or AttributeProto is null!"; + } + if (value->isa() || value->isa()) { + return SetScalarToAttributeProto_ir(value, attr_proto); + } else if (value->isa() || value->isa()) { + return SetTypeToAttributeProto(value, attr_proto); + } else if (value->isa()) { + if (!SetSequenceToAttributeProto(value->cast(), attr_proto)) { + MS_LOG(ERROR) << "Set sequence to AttributeProto failed."; + return false; + } + MS_LOG(DEBUG) << "Attr string: " << value->type_name(); + } else if (value->isa()) { + return SetTensorToAttributeProto(value, attr_proto); + } else if (value->isa()) { + attr_proto->set_type(mind_ir::AttributeProto_AttributeType_NONE); + MS_LOG(DEBUG) << "Attr string: " << value->type_name(); + } else if (value->isa()) { + if (value->isa()) { + attr_proto->set_type(mind_ir::AttributeProto_AttributeType_UMONAD); + } else if (value->isa()) { + attr_proto->set_type(mind_ir::AttributeProto_AttributeType_IOMONAD); + } else { + MS_LOG(ERROR) << "Unsupported Monad type: " << value->type_name(); + return false; + } + } else { + MS_LOG(ERROR) << "Unsupported type: " << value->type_name(); + return false; + } + return true; +} +//璁剧疆proto鍚 +bool IrExportBuilder::SetScalarToAttributeProto_ir(const ValuePtr &value, + mind_ir::AttributeProto *const attr_proto) const { + if (value == nullptr || attr_proto == nullptr) { + MS_LOG(EXCEPTION) << "ValuePtr or AttributeProto is null!"; + } + if (value->isa()) { + attr_proto->set_type(mind_ir::AttributeProto_AttributeType_STRING); + attr_proto->set_s(GetValue(value)); + } else if (value->isa()) { + attr_proto->set_type(mind_ir::AttributeProto_AttributeType_BOOL); + int64_t attr_value = GetValue(value) ? 1 : 0; + attr_proto->set_i(attr_value); + } else if (SetScalarToAttributeProtoForInt_ir(value, attr_proto)) { + return true; + } else if (value->isa()) { + attr_proto->set_type(mind_ir::AttributeProto_AttributeType_FLOAT); + attr_proto->set_f(GetValue(value)); + } else if (value->isa()) { + attr_proto->set_type(mind_ir::AttributeProto_AttributeType_DOUBLE); + attr_proto->set_d(GetValue(value)); + } else { + MS_LOG(ERROR) << "Unsupported scalar type: " << value->type_name(); + return false; + } + return true; +} +//璁剧疆proto鍚 +bool IrExportBuilder::SetScalarToAttributeProtoForInt_ir(const ValuePtr &value, + mind_ir::AttributeProto *const attr_proto) const { + if (value->isa()) { + attr_proto->set_type(mind_ir::AttributeProto_AttributeType_INT8); + attr_proto->set_i(value->cast()->value()); + } else if (value->isa()) { + attr_proto->set_type(mind_ir::AttributeProto_AttributeType_INT16); + attr_proto->set_i(value->cast()->value()); + } else if (value->isa()) { + attr_proto->set_type(mind_ir::AttributeProto_AttributeType_INT32); + attr_proto->set_i(value->cast()->value()); + } else if (value->isa()) { + attr_proto->set_type(mind_ir::AttributeProto_AttributeType_INT64); + attr_proto->set_i(value->cast()->value()); + } else if (value->isa()) { + attr_proto->set_type(mind_ir::AttributeProto_AttributeType_UINT8); + attr_proto->set_i(value->cast()->value()); + } else if (value->isa()) { + attr_proto->set_type(mind_ir::AttributeProto_AttributeType_UINT16); + attr_proto->set_i(value->cast()->value()); + } else if (value->isa()) { + attr_proto->set_type(mind_ir::AttributeProto_AttributeType_UINT32); + attr_proto->set_i(value->cast()->value()); + } else if (value->isa()) { + attr_proto->set_type(mind_ir::AttributeProto_AttributeType_UINT64); + attr_proto->set_i(UlongToLong(value->cast()->value())); + } else { + return false; + } + return true; +} +//璁剧疆proto鍚 +bool IrExportBuilder::SetTypeToAttributeProto_irs(const ValuePtr &value, mind_ir::AttributeProto *const attr_proto) { + if (attr_proto == nullptr) { + MS_LOG(EXCEPTION) << "AttributeProto is null!"; + } + if (value->isa()) { + attr_proto->set_type(mind_ir::AttributeProto_AttributeType_TENSORS); + mind_ir::TensorProto *tensor_proto = attr_proto->add_tensors(); + auto int_value = value->cast(); + auto data_type = GetMindirDataBitsIntType(int_value->nbits()); + if (data_type == mind_ir::TensorProto_DataType_UNDEFINED) { + return false; + } + tensor_proto->set_data_type(data_type); + } else if (value->isa()) { + attr_proto->set_type(mind_ir::AttributeProto_AttributeType_TENSORS); + mind_ir::TensorProto *tensor_proto = attr_proto->add_tensors(); + auto float_value = value->cast(); + auto data_type = GetMindirDataBitsFloatType(float_value->nbits()); + if (data_type == mind_ir::TensorProto_DataType_UNDEFINED) { + return false; + } + tensor_proto->set_data_type(data_type); + } else if (value->isa()) { + attr_proto->set_type(mind_ir::AttributeProto_AttributeType_TENSORS); + mind_ir::TensorProto *tensor_proto = attr_proto->add_tensors(); + auto uint_value = value->cast(); + auto data_type = GetMindirDataBitsFloatType(uint_value->nbits()); + if (data_type == mind_ir::TensorProto_DataType_UNDEFINED) { + return false; + } + tensor_proto->set_data_type(data_type); + } else if (value->isa()) { + attr_proto->set_type(mind_ir::AttributeProto_AttributeType_TENSORS); + mind_ir::TensorProto *tensor_proto = attr_proto->add_tensors(); + tensor_proto->set_data_type(mind_ir::TensorProto_DataType_BOOL); + } else if (value->isa()) { + attr_proto->set_type(mind_ir::AttributeProto_AttributeType_TENSORS); + return SetTensorToAttributeProto(value, attr_proto); + } else { + MS_LOG(EXCEPTION) << "Unsupported type: " << value->type_name(); + } + return true; +} +//璁剧疆proto鍚 +bool IrExportBuilder::SetScalarToAttributeProto_irs(const ValuePtr &value, + mind_ir::AttributeProto *const attr_proto) const { + if (attr_proto == nullptr) { + MS_LOG(EXCEPTION) << "AttributeProto is null!"; + } + if (value->isa()) { + attr_proto->set_type(mind_ir::AttributeProto_AttributeType_STRING); + attr_proto->add_strings(GetValue(value)); + } else if (value->isa()) { + attr_proto->set_type(mind_ir::AttributeProto_AttributeType_BOOL); + attr_proto->add_ints(GetValue(value)); + } else if (SetScalarToAttributeProtoForInt_irs(value, attr_proto)) { + return true; + } else if (value->isa()) { + attr_proto->set_type(mind_ir::AttributeProto_AttributeType_FLOAT); + attr_proto->add_floats(GetValue(value)); + } else if (value->isa()) { + attr_proto->set_type(mind_ir::AttributeProto_AttributeType_DOUBLE); + attr_proto->add_doubles(GetValue(value)); + } else { + MS_LOG(ERROR) << "Unsupported scalar type: " << value->type_name(); + return false; + } + return true; +} +//璁剧疆proto鍚 +bool IrExportBuilder::SetScalarToAttributeProtoForInt_irs(const ValuePtr &value, + mind_ir::AttributeProto *const attr_proto) const { + if (value->isa()) { + attr_proto->set_type(mind_ir::AttributeProto_AttributeType_INT8); + attr_proto->add_ints(value->cast()->value()); + } else if (value->isa()) { + attr_proto->set_type(mind_ir::AttributeProto_AttributeType_INT16); + attr_proto->add_ints(value->cast()->value()); + } else if (value->isa()) { + attr_proto->set_type(mind_ir::AttributeProto_AttributeType_INT32); + attr_proto->add_ints(value->cast()->value()); + } else if (value->isa()) { + attr_proto->set_type(mind_ir::AttributeProto_AttributeType_INT64); + attr_proto->add_ints(value->cast()->value()); + } else if (value->isa()) { + attr_proto->set_type(mind_ir::AttributeProto_AttributeType_UINT8); + attr_proto->add_ints(value->cast()->value()); + } else if (value->isa()) { + attr_proto->set_type(mind_ir::AttributeProto_AttributeType_UINT16); + attr_proto->add_ints(value->cast()->value()); + } else if (value->isa()) { + attr_proto->set_type(mind_ir::AttributeProto_AttributeType_UINT32); + attr_proto->add_ints(value->cast()->value()); + } else if (value->isa()) { + attr_proto->set_type(mind_ir::AttributeProto_AttributeType_UINT64); + attr_proto->add_ints(SizeToInt(value->cast()->value())); + } else { + return false; + } + return true; +} +//璁剧疆proto鍚 +bool IrExportBuilder::SetSeqElemToAttributeProto(const ValuePtr &value, mind_ir::AttributeProto *const attr_proto) { + if (value == nullptr) { + MS_LOG(ERROR) << "Value is nullptr"; + return false; + } + if (value->isa() || value->isa()) { + return SetScalarToAttributeProto_irs(value, attr_proto); + } + return SetTypeToAttributeProto_irs(value, attr_proto); +} + +bool IrExportBuilder::SetSequenceToAttributeProto(const ValueSequencePtr &value, + mind_ir::AttributeProto *const attr_proto) { + if (value == nullptr || attr_proto == nullptr) { + MS_LOG(EXCEPTION) << "ValueSequencePtr or AttributeProto is null!"; + } + if (value->isa()) { + attr_proto->set_type(mind_ir::AttributeProto_AttributeType_TUPLE); + } else if (value->isa()) { + attr_proto->set_type(mind_ir::AttributeProto_AttributeType_LIST); + } else { + MS_LOG(EXCEPTION) << "The sequance value should be ValueTuple or ValueList, but it is " << value->ToString(); + } + auto value_sequence = value->cast(); + MS_EXCEPTION_IF_NULL(value_sequence); + const auto &values = value_sequence->value(); + if (values.empty()) { + MS_LOG(DEBUG) << "SetSequenceToAttributeProto sequence size is 0"; + return true; + } + for (const auto &item : values) { + mind_ir::AttributeProto *attr_values = attr_proto->add_values(); + MS_EXCEPTION_IF_NULL(item); + if (item->isa()) { + if (!SetSequenceToAttributeProto(item->cast(), attr_values)) { + MS_LOG(ERROR) << "Set sequence to AttributeProto failed."; + return false; + } + } else { + if (!SetSeqElemToAttributeProto(item, attr_values)) { + MS_LOG(ERROR) << "Set seq elem to AttributeProto failed."; + return false; + } + } + } + return true; +} +//璁剧疆proto鍚 +std::string GetBinaryProtoString(const FuncGraphPtr &func_graph) { + auto builder = std::make_shared(); + if (builder == nullptr) { + MS_LOG(ERROR) << "Create ir exporter failed!"; + return ""; + } + auto exporter = std::make_shared(builder); + if (exporter == nullptr) { + return ""; + } + auto ret = exporter->GetDumpString(func_graph); + return ret; +} +//鑾峰緱protostring +bool DumpBinaryProto(const FuncGraphPtr &func_graph, const std::string &file_path, + const FuncGraphPtr ¶m_layout_fg) { + auto exporter = std::make_shared(std::make_shared()); + auto proto = exporter->GetDumpProto(func_graph, param_layout_fg); + if (proto == nullptr) { + MS_LOG(ERROR) << "Get binary proto for graph " << func_graph->ToString() << " failed."; + return false; + } + + auto realpath = Common::CreatePrefixPath(file_path, true); + if (!realpath.has_value()) { + MS_LOG(ERROR) << "Get real path of file " << file_path << " failed."; + return false; + } + + ChangeFileMode(realpath.value(), S_IWUSR); + std::ofstream fout(realpath.value()); + if (!fout.is_open()) { + MS_LOG(ERROR) << "Open the file '" << realpath.value() << "' failed!" << ErrnoToString(errno); + return false; + } + + if (!proto->SerializeToOstream(&fout)) { + MS_LOG(ERROR) << "Failed to write the mindir proto to file " << realpath.value(); + fout.close(); + return false; + } + fout.close(); + ChangeFileMode(realpath.value(), S_IRUSR); + return true; +} +} // namespace mindspore diff --git a/mindspore/ccsrc/transform-update/nn_batch_norm_ops_declare.cc b/mindspore/ccsrc/transform-update/nn_batch_norm_ops_declare.cc new file mode 100644 index 00000000000..184ea8efe78 --- /dev/null +++ b/mindspore/ccsrc/transform-update/nn_batch_norm_ops_declare.cc @@ -0,0 +1,99 @@ +/** + * Copyright 2019 Huawei Technologies Co., Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "transform/graph_ir/op_declare/nn_batch_norm_ops_declare.h" +#include +#include + +namespace mindspore::transform { +// BatchNorm +INPUT_MAP(BatchNorm) = {{1, INPUT_DESC(x)}, + {2, INPUT_DESC(scale)}, + {3, INPUT_DESC(offset)}, + {4, INPUT_DESC(mean)}, + {5, INPUT_DESC(variance)}}; +//杈撳叆鏄犲皠锛寈绱㈠紩涓1锛宻cale绱㈠紩涓2锛宱ffset绱㈠紩涓3锛宮ean绱㈠紩涓4锛寁ariance绱㈠紩涓5 +ATTR_MAP(BatchNorm) = {{"format", ATTR_DESC(data_format, AnyTraits())}, + {"epsilon", ATTR_DESC(epsilon, AnyTraits())}, + {"is_training", ATTR_DESC(is_training, AnyTraits())}}; +//灞炴ф槧灏勶紝灞炴ormat绫诲瀷涓簊tring锛屽睘鎬psilon绫诲瀷涓篺loat锛屽睘鎬ormat绫诲瀷涓篵ool +OUTPUT_MAP(BatchNorm) = {{0, OUTPUT_DESC(y)}, + {1, OUTPUT_DESC(batch_mean)}, + {2, OUTPUT_DESC(batch_variance)}, + {3, OUTPUT_DESC(reserve_space_1)}, + {4, OUTPUT_DESC(reserve_space_2)}}; +//杈撳嚭鏄犲皠锛寉绱㈠紩涓0锛宐atch_mean绱㈠紩涓1锛宐atch_variance绱㈠紩涓2锛宺eserve_space_1绱㈠紩涓3锛宺eserve_space_2绱㈠紩涓4 +// BNInference is BatchNorm for caffe +INPUT_MAP(BNInference) = {{1, INPUT_DESC(x)}, {2, INPUT_DESC(mean)}, {3, INPUT_DESC(variance)}, + {4, INPUT_DESC(momentum)}, {5, INPUT_DESC(scale)}, {6, INPUT_DESC(offset)}}; +//杈撳叆鏄犲皠锛寈绱㈠紩涓1锛宮ean绱㈠紩涓2锛寁ariance绱㈠紩涓3锛宮omentum绱㈠紩涓4锛宻cale绱㈠紩涓5锛宱ffset绱㈠紩涓5 +ATTR_MAP(BNInference) = {{"epsilon", ATTR_DESC(epsilon, AnyTraits())}, + {"use_global_stats", ATTR_DESC(use_global_stats, AnyTraits())}, + {"mode", ATTR_DESC(mode, AnyTraits())}}; +//灞炴ф槧灏勶紝灞炴psilon绫诲瀷涓篺loat锛屽睘鎬se_global_stats绫诲瀷涓篵ool锛屽睘鎬ode绫诲瀷涓篵ool +OUTPUT_MAP(BNInference) = {{0, OUTPUT_DESC(y)}}; +//杈撳嚭鏄犲皠锛寉绱㈠紩涓0 +REG_ADPT_DESC(BNInference, kNameBNInference, ADPT_DESC(BNInference)) +//娉ㄥ唽BNInference鎿嶄綔鐨勯傞厤鍣ㄦ弿杩発NameBNInference +REG_ADPT_DESC(BatchNorm, kNameBatchNorm, ADPT_DESC(BatchNorm)) +//娉ㄥ唽BatchNorm鎿嶄綔鐨勯傞厤鍣ㄦ弿杩発NameBatchNorm +REG_ADPT_DESC(FusedBatchNorm, kNameFusedBatchNorm, ADPT_DESC(BatchNorm)) +//娉ㄥ唽FusedBatchNorm鎿嶄綔鐨勯傞厤鍣ㄦ弿杩発NameFusedBatchNorm + +// BatchNormGrad +INPUT_MAP(BatchNormGrad) = {{1, INPUT_DESC(y_backprop)}, + {2, INPUT_DESC(x)}, + {3, INPUT_DESC(scale)}, + {4, INPUT_DESC(reserve_space_1)}, + {5, INPUT_DESC(reserve_space_2)}}; +//杈撳叆鏄犲皠锛寉_backprop绱㈠紩涓1锛寈绱㈠紩涓2锛宻cale绱㈠紩涓3锛宺eserve_space_1绱㈠紩涓4锛宺eserve_space_2绱㈠紩涓5 +ATTR_MAP(BatchNormGrad) = {{"format", ATTR_DESC(data_format, AnyTraits())}, + {"epsilon", ATTR_DESC(epsilon, AnyTraits())}, + {"is_training", ATTR_DESC(is_training, AnyTraits())}}; +//灞炴ф槧灏勶紝灞炴ormat绫诲瀷涓簊tring锛屽睘鎬psilon绫诲瀷涓篺loat锛屽睘鎬ormat绫诲瀷涓篵ool +OUTPUT_MAP(BatchNormGrad) = {{0, OUTPUT_DESC(x_backprop)}, + {1, OUTPUT_DESC(scale_backprop)}, + {2, OUTPUT_DESC(offset_backprop)}, + {3, OUTPUT_DESC(reserve_space_4)}, + {4, OUTPUT_DESC(reserve_space_5)}}; +//杈撳嚭鏄犲皠锛寈_backprop绱㈠紩涓0锛宻cale_backprop绱㈠紩涓1锛宱ffset_backprop绱㈠紩涓2锛宺eserve_space_4绱㈠紩涓3锛宺eserve_space_5绱㈠紩涓4 +REG_ADPT_DESC(BatchNormGrad, kNameBatchNormGrad, ADPT_DESC(BatchNormGrad)) +//娉ㄥ唽BatchNormGrad鎿嶄綔鐨勯傞厤鍣ㄦ弿杩発NameBatchNormGrad + +// L2NormalizeGrad +INPUT_MAP(L2NormalizeGrad) = {{1, INPUT_DESC(x)}, {2, INPUT_DESC(y)}, {3, INPUT_DESC(dy)}}; +//杈撳叆鏄犲皠锛寈绱㈠紩涓1锛寉绱㈠紩涓2锛宒y绱㈠紩涓3 +ATTR_MAP(L2NormalizeGrad) = { + {"axis", ATTR_DESC(dim, AnyTraits>(), AnyTraits>())}, + {"epsilon", ATTR_DESC(eps, AnyTraits())}}; +//灞炴ф槧灏勶紝灞炴xis绫诲瀷涓篿nt64_t锛屽睘鎬psilon绫诲瀷涓篺loat +OUTPUT_MAP(L2NormalizeGrad) = {{0, OUTPUT_DESC(dx)}}; +//杈撳嚭鏄犲皠锛寈_backprop绱㈠紩涓0 +REG_ADPT_DESC(L2NormalizeGrad, kNameL2NormalizeGrad, ADPT_DESC(L2NormalizeGrad)) +//娉ㄥ唽L2NormalizeGrad鎿嶄綔鐨勯傞厤鍣ㄦ弿杩発NameL2NormalizeGrad + +// L2Normalize +INPUT_MAP(L2Normalize) = {{1, INPUT_DESC(x)}}; +//杈撳叆鏄犲皠锛寈绱㈠紩涓1 +ATTR_MAP(L2Normalize) = { + {"axis", ATTR_DESC(axis, AnyTraits>(), AnyTraits>())}, + {"epsilon", ATTR_DESC(eps, AnyTraits())}}; +//灞炴ф槧灏勶紝灞炴xis绫诲瀷涓篿nt64_t锛屽睘鎬psilon绫诲瀷涓篺loat +OUTPUT_MAP(L2Normalize) = {{0, OUTPUT_DESC(y)}}; +//杈撳嚭鏄犲皠锛寉绱㈠紩涓0 +REG_ADPT_DESC(L2Normalize, kNameL2Normalize, ADPT_DESC(L2Normalize)) +//娉ㄥ唽L2Normalize鎿嶄綔鐨勯傞厤鍣ㄦ弿杩発NameL2Normalize +} // namespace mindspore::transform diff --git a/mindspore/ccsrc/transform-update/nn_calculation_ops_declare.cc b/mindspore/ccsrc/transform-update/nn_calculation_ops_declare.cc new file mode 100644 index 00000000000..54869592a8c --- /dev/null +++ b/mindspore/ccsrc/transform-update/nn_calculation_ops_declare.cc @@ -0,0 +1,262 @@ +/** + * Copyright 2019 Huawei Technologies Co., Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "transform/graph_ir/op_declare/nn_calculation_ops_declare.h" +#include +#include + +namespace mindspore::transform { +// BiasAddGrad +INPUT_MAP(BiasAddGrad) = {{1, INPUT_DESC(x)}}; +//杈撳叆鏄犲皠锛寈绱㈠紩涓1 +ATTR_MAP(BiasAddGrad) = {{"format", ATTR_DESC(data_format, AnyTraits())}}; +//灞炴ф槧灏,灞炴ormat绫诲瀷涓簊tring +OUTPUT_MAP(BiasAddGrad) = {{0, OUTPUT_DESC(y)}}; +//杈撳嚭鏄犲皠锛寉绱㈠紩涓0 +REG_ADPT_DESC(BiasAddGrad, prim::kPrimBiasAddGrad->name(), ADPT_DESC(BiasAddGrad)) +//娉ㄥ唽BiasAddGrad鎿嶄綔鐨勯傞厤鍣ㄦ弿杩発PrimBiasAddGrad杩斿洖鐨刵ame鍙橀噺 + +// Conv2D +INPUT_MAP(Conv2D) = {{1, INPUT_DESC(x)}, {2, INPUT_DESC(filter)}, {3, INPUT_DESC(bias)}}; +//杈撳叆鏄犲皠锛寈绱㈠紩涓1锛宖ilter绱㈠紩涓2锛宐ias绱㈠紩涓3 +ATTR_MAP(Conv2D) = { + {"stride", ATTR_DESC(strides, AnyTraits>(), AnyTraits>())}, + {"pad_list", ATTR_DESC(pads, AnyTraits>(), AnyTraits>())}, + {"dilation", ATTR_DESC(dilations, AnyTraits>(), AnyTraits>())}, + {"format", ATTR_DESC(data_format, AnyTraits())}, + {"group", ATTR_DESC(groups, AnyTraits())}, +}; +//灞炴ф槧灏勶紝灞炴tride绫诲瀷涓篿nt64_t锛屽睘鎬ad_list绫诲瀷涓篿nt64_t锛屽睘鎬ilations绫诲瀷涓篿nt64_t锛屽睘鎬ormat绫诲瀷涓簊tring锛屽睘鎬roup绫诲瀷涓篿nt64_t +OUTPUT_MAP(Conv2D) = {{0, OUTPUT_DESC(y)}}; +//杈撳嚭鏄犲皠锛寉绱㈠紩涓0 +REG_ADPT_DESC(Conv2D, prim::kPrimConv2D->name(), ADPT_DESC(Conv2D)) +//娉ㄥ唽Conv2D鎿嶄綔鐨勯傞厤鍣ㄦ弿杩発PrimConv2D杩斿洖鐨刵ame鍙橀噺 + +// Conv2DBackpropInputD +INPUT_MAP(Conv2DBackpropInputD) = {{1, INPUT_DESC(out_backprop)}, {2, INPUT_DESC(filter)}}; +//杈撳叆鏄犲皠锛宱ut_backprop绱㈠紩涓1锛宖ilter绱㈠紩涓2 +INPUT_ATTR_MAP(Conv2DBackpropInputD) = { + {3, ATTR_DESC(input_size, AnyTraits>(), AnyTraits>())}}; +//杈撳叆灞炴ф槧灏勶紝灏嗙储寮曚负3鐨勮緭鍏ヤ笌灞炴т负input_size鐨勫彉閲忕浉鍏宠仈锛岀敤浜庡弽鍗风Н鎿嶄綔鐨勮緭鍏ュ睘鎬ф槧灏 +ATTR_MAP(Conv2DBackpropInputD) = { + {"pad_list", ATTR_DESC(pads, AnyTraits>(), AnyTraits>())}, + {"stride", ATTR_DESC(strides, AnyTraits>())}, + {"dilation", ATTR_DESC(dilations, AnyTraits>(), AnyTraits>())}, + {"format", ATTR_DESC(data_format, AnyTraits())}, + {"group", ATTR_DESC(groups, AnyTraits())}, +}; +//灞炴ф槧灏勶紝灞炴ad_list绫诲瀷涓篿nt64_t锛屽睘鎬tride绫诲瀷涓篿nt64_t锛屽睘鎬ilations绫诲瀷涓篿nt64_t锛屽睘鎬ormat绫诲瀷涓簊tring锛屽睘鎬roup绫诲瀷涓篿nt64_t +OUTPUT_MAP(Conv2DBackpropInputD) = {{0, OUTPUT_DESC(y)}}; +//杈撳嚭鏄犲皠锛寉绱㈠紩涓0 +REG_ADPT_DESC(Conv2DBackpropInputD, prim::kPrimConv2DBackpropInput->name(), ADPT_DESC(Conv2DBackpropInputD)) +//娉ㄥ唽Conv2DBackpropInputD鎿嶄綔鐨勯傞厤鍣ㄦ弿杩発PrimConv2DBackpropInput杩斿洖鐨刵ame鍙橀噺 + +// Conv2DBackpropInput for tf inference +INPUT_MAP(Conv2DBackpropInput) = {{1, INPUT_DESC(input_size)}, {2, INPUT_DESC(filter)}, {3, INPUT_DESC(out_backprop)}}; +//杈撳叆鏄犲皠锛宨nput_size绱㈠紩涓1锛宖ilter绱㈠紩涓2锛宱ut_backprop绱㈠紩涓3 +ATTR_MAP(Conv2DBackpropInput) = { + {"stride", ATTR_DESC(strides, AnyTraits>())}, + {"dilation", ATTR_DESC(dilations, AnyTraits>(), AnyTraits>())}, + {"pad_list", ATTR_DESC(pads, AnyTraits>(), AnyTraits>())}, + {"data_format", ATTR_DESC(data_format, AnyTraits())}, +}; +//灞炴ф槧灏勶紝灞炴tride绫诲瀷涓篿nt64_t锛屽睘鎬ad_list绫诲瀷涓篿nt64_t锛屽睘鎬ilations绫诲瀷涓篿nt64_t锛屽睘鎬ad_list绫诲瀷涓篿nt64_t锛屽睘鎬ata_format绫诲瀷涓簊tring +OUTPUT_MAP(Conv2DBackpropInput) = {{0, OUTPUT_DESC(y)}}; +//杈撳嚭鏄犲皠锛寉绱㈠紩涓0 +REG_ADPT_DESC(Conv2DBackpropInput, kNameConv2DBackpropInputV2, ADPT_DESC(Conv2DBackpropInput)) +//娉ㄥ唽Conv2DBackpropInput鎿嶄綔鐨勯傞厤鍣ㄦ弿杩発NameConv2DBackpropInputV2杩斿洖鐨刵ame鍙橀噺 + +// Deconvolution for caffe inference +INPUT_MAP(Deconvolution) = { + {1, INPUT_DESC(x)}, {2, INPUT_DESC(filter)}, {3, INPUT_DESC(bias)}, {4, INPUT_DESC(offset_w)}}; +//杈撳叆鏄犲皠锛寈绱㈠紩涓1锛宖ilter绱㈠紩涓2锛宐ias绱㈠紩涓3锛宱ffset_w绱㈠紩涓4 +ATTR_MAP(Deconvolution) = { + {"stride", ATTR_DESC(strides, AnyTraits>(), AnyTraits>())}, + {"pad_list", ATTR_DESC(pads, AnyTraits>(), AnyTraits>())}, + {"dilation", ATTR_DESC(dilations, AnyTraits>(), AnyTraits>())}, + {"group", ATTR_DESC(groups, AnyTraits())}, + {"format", ATTR_DESC(data_format, AnyTraits())}, + {"offset", ATTR_DESC(offset_x, AnyTraits())}}; +//灞炴ф槧灏勶紝灞炴tride绫诲瀷涓篿nt64_t锛屽睘鎬ad_list绫诲瀷涓篿nt64_t锛屽睘鎬ilations绫诲瀷涓篿nt64_t +//灞炴roups绫诲瀷涓篿nt64_t锛屽睘鎬ormat绫诲瀷涓簊tring锛屽睘鎬ffset绫诲瀷涓篿nt64_t +OUTPUT_MAP(Deconvolution) = {{0, OUTPUT_DESC(y)}}; +//杈撳嚭鏄犲皠锛寉绱㈠紩涓0 +REG_ADPT_DESC(Deconvolution, kNameDeconvolution, ADPT_DESC(Deconvolution)) +//娉ㄥ唽Deconvolution鎿嶄綔鐨勯傞厤鍣ㄦ弿杩発NameDeconvolution,杩斿洖鐨刵ame鍙橀噺 +REG_ADPT_DESC(Conv2DTranspose, kConv2DTransposeOpName, ADPT_DESC(Conv2DBackpropInputD)) +//娉ㄥ唽Conv2DTranspose鎿嶄綔鐨勯傞厤鍣ㄦ弿杩発Conv2DTransposeOpName杩斿洖鐨刵ame鍙橀噺 + +// Conv2DTransposeD for tf onnx inference +INPUT_MAP(Conv2DTransposeD) = { + {1, INPUT_DESC(x)}, {2, INPUT_DESC(filter)}, {3, INPUT_DESC(bias)}, {4, INPUT_DESC(offset_w)}}; +//杈撳叆鏄犲皠锛寈绱㈠紩涓1锛宖ilter绱㈠紩涓2锛宐ias绱㈠紩涓3锛宱ffset_w绱㈠紩涓4 +ATTR_MAP(Conv2DTransposeD) = { + {"input_size", ATTR_DESC(input_size, AnyTraits>(), AnyTraits>())}, + {"stride", ATTR_DESC(strides, AnyTraits>(), AnyTraits>())}, + {"pad_list", ATTR_DESC(pads, AnyTraits>(), AnyTraits>())}, + {"dilation", ATTR_DESC(dilations, AnyTraits>(), AnyTraits>())}, + {"group", ATTR_DESC(groups, AnyTraits())}, + {"data_format", ATTR_DESC(data_format, AnyTraits())}, + {"output_paddings", ATTR_DESC(output_padding, AnyTraits>(), AnyTraits>())}, + {"offset", ATTR_DESC(offset_x, AnyTraits())}}; +//灞炴ф槧灏勶紝灞炴nput_size绫诲瀷涓篿nt64_t锛屽睘鎬trides绫诲瀷涓篿nt64_t锛屽睘鎬ad_list绫诲瀷涓篿nt64_t锛屽睘鎬ilations绫诲瀷涓篿nt64_t +//灞炴roups绫诲瀷涓篿nt64_t锛屽睘鎬ata_format绫诲瀷涓簊tring锛屽睘鎬utput_paddings绫诲瀷涓篿nt64_t锛屽睘鎬ffset绫诲瀷涓篿nt64_t +OUTPUT_MAP(Conv2DTransposeD) = {{0, OUTPUT_DESC(y)}}; +//杈撳嚭鏄犲皠锛寉绱㈠紩涓0 +REG_ADPT_DESC(Conv2DTransposeD, kNameConv2DTransposeD, ADPT_DESC(Conv2DTransposeD)) +//娉ㄥ唽Conv2DTransposeD鎿嶄綔鐨勯傞厤鍣ㄦ弿杩発NameConv2DTransposeD杩斿洖鐨刵ame鍙橀噺 + +// Conv2DBackpropFilterD +INPUT_MAP(Conv2DBackpropFilterD) = {{1, INPUT_DESC(out_backprop)}, {2, INPUT_DESC(x)}}; +//杈撳叆鏄犲皠锛宱ut_backprop绱㈠紩涓1锛寈绱㈠紩涓2 +INPUT_ATTR_MAP(Conv2DBackpropFilterD) = { + {3, ATTR_DESC(filter_size, AnyTraits>(), AnyTraits>())}}; + +ATTR_MAP(Conv2DBackpropFilterD) = { + {"pad_list", ATTR_DESC(pads, AnyTraits>(), AnyTraits>())}, + {"stride", ATTR_DESC(strides, AnyTraits>(), AnyTraits>())}, + {"dilation", ATTR_DESC(dilations, AnyTraits>(), AnyTraits>())}, + {"format", ATTR_DESC(data_format, AnyTraits())}, + {"group", ATTR_DESC(groups, AnyTraits())}, +}; +//灞炴ф槧灏勶紝灞炴ad_list绫诲瀷涓篿nt64_t锛屽睘鎬tride绫诲瀷涓篿nt64_t锛屽睘鎬ilations绫诲瀷涓篿nt64_t锛屽睘鎬roups绫诲瀷涓篿nt64_t锛屽睘鎬roup绫诲瀷涓篿nt64_t +OUTPUT_MAP(Conv2DBackpropFilterD) = {{0, OUTPUT_DESC(y)}}; +//杈撳嚭鏄犲皠锛寉绱㈠紩涓0 +REG_ADPT_DESC(Conv2DBackpropFilterD, prim::kPrimConv2DBackpropFilter->name(), ADPT_DESC(Conv2DBackpropFilterD)) +//娉ㄥ唽Conv2DBackpropFilterD鎿嶄綔鐨勯傞厤鍣ㄦ弿杩発PrimConv2DBackpropFilter杩斿洖鐨刵ame鍙橀噺 + +// Conv3DTransposeD +INPUT_MAP(Conv3DTransposeD) = { + {1, INPUT_DESC(x)}, {2, INPUT_DESC(filter)}, {3, INPUT_DESC(bias)}, {4, INPUT_DESC(offset_w)}}; +//杈撳叆鏄犲皠锛寈绱㈠紩涓1锛宖ilter绱㈠紩涓2锛宐ias绱㈠紩涓3锛宱ffset_w绱㈠紩涓4 +ATTR_MAP(Conv3DTransposeD) = { + {"input_size", ATTR_DESC(input_size, AnyTraits>(), AnyTraits>())}, + {"strides", ATTR_DESC(strides, AnyTraits>(), AnyTraits>())}, + {"pad_list", ATTR_DESC(pads, AnyTraits>(), AnyTraits>())}, + {"dilations", ATTR_DESC(dilations, AnyTraits>(), AnyTraits>())}, + {"groups", ATTR_DESC(groups, AnyTraits())}, + {"format", ATTR_DESC(data_format, AnyTraits())}, + {"output_padding", ATTR_DESC(output_padding, AnyTraits>(), AnyTraits>())}, +}; +//灞炴ф槧灏勶紝灞炴nput_size绫诲瀷涓篿nt64_t锛屽睘鎬trides绫诲瀷涓篿nt64_t锛屽睘鎬ad_list绫诲瀷涓篿nt64_t锛屽睘鎬ilations绫诲瀷涓篿nt64_t +//灞炴roups绫诲瀷涓篿nt64_t锛屽睘鎬ormat绫诲瀷涓篿nt64_t锛屽睘鎬utput_padding绫诲瀷涓篿nt64_t +OUTPUT_MAP(Conv3DTransposeD) = {{0, OUTPUT_DESC(y)}}; +//杈撳嚭鏄犲皠锛寉绱㈠紩涓0 +REG_ADPT_DESC(Conv3DTransposeD, kNameConv3DTransposeD, ADPT_DESC(Conv3DTransposeD)) +//娉ㄥ唽Conv3DTransposeD鎿嶄綔鐨勯傞厤鍣ㄦ弿杩発NameConv3DTransposeD杩斿洖鐨刵ame鍙橀噺 + +// Conv3D +INPUT_MAP(Conv3D) = {{1, INPUT_DESC(x)}, {2, INPUT_DESC(filter)}, {3, INPUT_DESC(bias)}, {4, INPUT_DESC(offset_w)}}; +//杈撳叆鏄犲皠锛寈绱㈠紩涓1锛宖ilter绱㈠紩涓2锛宐ias绱㈠紩涓3锛宱ffset_w绱㈠紩涓4 +ATTR_MAP(Conv3D) = { + {"strides", ATTR_DESC(strides, AnyTraits>(), AnyTraits>())}, + {"pad_list", ATTR_DESC(pads, AnyTraits>(), AnyTraits>())}, + {"dilations", ATTR_DESC(dilations, AnyTraits>(), AnyTraits>())}, + {"groups", ATTR_DESC(groups, AnyTraits())}, + {"format", ATTR_DESC(data_format, AnyTraits())}, + {"offset_x", ATTR_DESC(offset_x, AnyTraits())}, +}; +//灞炴ф槧灏勶紝灞炴trides绫诲瀷涓篿nt64_t锛屽睘鎬ad_list绫诲瀷涓篿nt64_t锛屽睘鎬ilations绫诲瀷涓篿nt64_t锛屽睘鎬roups绫诲瀷涓篿nt64_t锛屽睘鎬ormat绫诲瀷涓篿nt64_t锛屽睘鎬ffset_x绫诲瀷涓篿nt64_t +OUTPUT_MAP(Conv3D) = {{0, OUTPUT_DESC(y)}}; +//杈撳嚭鏄犲皠锛寉绱㈠紩涓0 +REG_ADPT_DESC(Conv3D, kNameConv3D, ADPT_DESC(Conv3D)) +//娉ㄥ唽Conv3D鎿嶄綔鐨勯傞厤鍣ㄦ弿杩発NameConv3D杩斿洖鐨刵ame鍙橀噺 + +// Conv3DBackpropInputD +INPUT_MAP(Conv3DBackpropInputD) = {{1, INPUT_DESC(out_backprop)}, {2, INPUT_DESC(filter)}}; +//杈撳叆鏄犲皠锛宱ut_backprop绱㈠紩涓1锛宖ilter绱㈠紩涓2 +INPUT_ATTR_MAP(Conv3DBackpropInputD) = { + {3, ATTR_DESC(input_size, AnyTraits>(), AnyTraits>())}}; +ATTR_MAP(Conv3DBackpropInputD) = { + {"pad_list", ATTR_DESC(pads, AnyTraits>(), AnyTraits>())}, + {"strides", ATTR_DESC(strides, AnyTraits>())}, + {"dilations", ATTR_DESC(dilations, AnyTraits>(), AnyTraits>())}, + {"format", ATTR_DESC(data_format, AnyTraits())}, + {"groups", ATTR_DESC(groups, AnyTraits())}, +}; +//灞炴ф槧灏勶紝灞炴trides绫诲瀷涓篿nt64_t锛屽睘鎬ad_list绫诲瀷涓篿nt64_t锛屽睘鎬ilations绫诲瀷涓篿nt64_t锛屽睘鎬roups绫诲瀷涓篿nt64_t锛屽睘鎬ormat绫诲瀷涓篿nt64_t +OUTPUT_MAP(Conv3DBackpropInputD) = {{0, OUTPUT_DESC(y)}}; +//杈撳嚭鏄犲皠锛寉绱㈠紩涓0 +REG_ADPT_DESC(Conv3DBackpropInputD, kNameConv3DBackpropInputD, ADPT_DESC(Conv3DBackpropInputD)) +//娉ㄥ唽Conv3DBackpropInputD鎿嶄綔鐨勯傞厤鍣ㄦ弿杩発NameConv3DBackpropInputD杩斿洖鐨刵ame鍙橀噺 + +// Conv3DBackpropFilterD +INPUT_MAP(Conv3DBackpropFilterD) = {{1, INPUT_DESC(out_backprop)}, {2, INPUT_DESC(x)}}; +//杈撳叆鏄犲皠锛宱ut_backprop绱㈠紩涓1锛宖ilter绱㈠紩涓2 +INPUT_ATTR_MAP(Conv3DBackpropFilterD) = { + {3, ATTR_DESC(filter_size, AnyTraits>(), AnyTraits>())}}; +ATTR_MAP(Conv3DBackpropFilterD) = { + {"strides", ATTR_DESC(strides, AnyTraits>(), AnyTraits>())}, + {"pad_list", ATTR_DESC(pads, AnyTraits>(), AnyTraits>())}, + {"dilations", ATTR_DESC(dilations, AnyTraits>(), AnyTraits>())}, + {"groups", ATTR_DESC(groups, AnyTraits())}, + {"format", ATTR_DESC(data_format, AnyTraits())}, +}; +//灞炴ф槧灏勶紝灞炴trides绫诲瀷涓篿nt64_t锛屽睘鎬ad_list绫诲瀷涓篿nt64_t锛屽睘鎬ilations绫诲瀷涓篿nt64_t锛屽睘鎬roups绫诲瀷涓篿nt64_t锛屽睘鎬ormat绫诲瀷涓篿nt64_t +OUTPUT_MAP(Conv3DBackpropFilterD) = {{0, OUTPUT_DESC(y)}}; +//杈撳嚭鏄犲皠锛寉绱㈠紩涓0 +REG_ADPT_DESC(Conv3DBackpropFilterD, kNameConv3DBackpropFilterD, ADPT_DESC(Conv3DBackpropFilterD)) +//娉ㄥ唽Conv3DBackpropFilterD鎿嶄綔鐨勯傞厤鍣ㄦ弿杩発NameConv3DBackpropFilterD杩斿洖鐨刵ame鍙橀噺 + +// DepthwiseConv2D +INPUT_MAP(DepthwiseConv2D) = {{1, INPUT_DESC(x)}, {2, INPUT_DESC(filter)}, {3, INPUT_DESC(bias)}}; +//杈撳叆鏄犲皠锛寈绱㈠紩涓1锛宖ilter绱㈠紩涓2锛宐ias绱㈠紩涓3 +ATTR_MAP(DepthwiseConv2D) = { + {"stride", ATTR_DESC(strides, AnyTraits>(), AnyTraits>())}, + {"pad_list", ATTR_DESC(pads, AnyTraits>(), AnyTraits>())}, + {"dilation", ATTR_DESC(dilations, AnyTraits>(), AnyTraits>())}, + {"format", ATTR_DESC(data_format, AnyTraits())}, +}; +//灞炴ф槧灏勶紝灞炴trides绫诲瀷涓篿nt64_t锛屽睘鎬ad_list绫诲瀷涓篿nt64_t锛屽睘鎬ilations绫诲瀷涓篿nt64_t锛屽睘鎬ormat绫诲瀷涓篿nt64_t +OUTPUT_MAP(DepthwiseConv2D) = {{0, OUTPUT_DESC(y)}}; +//杈撳嚭鏄犲皠锛寉绱㈠紩涓0 +REG_ADPT_DESC(DepthwiseConv2D, prim::kPrimDepthwiseConv2dNative->name(), ADPT_DESC(DepthwiseConv2D)) +//娉ㄥ唽DepthwiseConv2D鎿嶄綔鐨勯傞厤鍣ㄦ弿杩発PrimDepthwiseConv2dNative杩斿洖鐨刵ame鍙橀噺 + +// DepthwiseConv2DBackpropInputD +INPUT_MAP(DepthwiseConv2DBackpropInputD) = {{2, INPUT_DESC(filter)}, {3, INPUT_DESC(out_backprop)}}; +//杈撳叆鏄犲皠锛宖ilter绱㈠紩涓2锛宱ut_backprop绱㈠紩涓3 +INPUT_ATTR_MAP(DepthwiseConv2DBackpropInputD) = { + {1, ATTR_DESC(input_size, AnyTraits>(), AnyTraits>())}}; +ATTR_MAP(DepthwiseConv2DBackpropInputD) = { + {"stride", ATTR_DESC(strides, AnyTraits>(), AnyTraits>())}, + {"pad_list", ATTR_DESC(pads, AnyTraits>(), AnyTraits>())}, + {"dilation", ATTR_DESC(dilations, AnyTraits>(), AnyTraits>())}, +}; +//灞炴ф槧灏勶紝灞炴trides绫诲瀷涓篿nt64_t锛屽睘鎬ad_list绫诲瀷涓篿nt64_t锛屽睘鎬ilations绫诲瀷涓篿nt64_t +OUTPUT_MAP(DepthwiseConv2DBackpropInputD) = {{0, OUTPUT_DESC(input_grad)}}; +//杈撳嚭鏄犲皠锛宨nput_grad绱㈠紩涓0 +REG_ADPT_DESC(DepthwiseConv2DBackpropInputD, prim::kPrimDepthwiseConv2dNativeBackpropInput->name(), + ADPT_DESC(DepthwiseConv2DBackpropInputD)) +//娉ㄥ唽DepthwiseConv2DBackpropInputD鎿嶄綔鐨勯傞厤鍣ㄦ弿杩発PrimDepthwiseConv2dNativeBackpropInput杩斿洖鐨刵ame鍙橀噺 + +// DepthwiseConv2DBackpropFilterD +INPUT_MAP(DepthwiseConv2DBackpropFilterD) = {{1, INPUT_DESC(input)}, {3, INPUT_DESC(out_backprop)}}; +//杈撳叆鏄犲皠锛宨nput绱㈠紩涓1锛宱ut_backprop绱㈠紩涓3 +INPUT_ATTR_MAP(DepthwiseConv2DBackpropFilterD) = { + {2, ATTR_DESC(filter_size, AnyTraits>(), AnyTraits>())}}; +ATTR_MAP(DepthwiseConv2DBackpropFilterD) = { + {"stride", ATTR_DESC(strides, AnyTraits>(), AnyTraits>())}, + {"pad_list", ATTR_DESC(pads, AnyTraits>(), AnyTraits>())}, + {"dilation", ATTR_DESC(dilations, AnyTraits>(), AnyTraits>())}, +}; +//灞炴ф槧灏勶紝灞炴trides绫诲瀷涓篿nt64_t锛屽睘鎬ads绫诲瀷涓篿nt64_t锛屽睘鎬ilations绫诲瀷涓篿nt64_t +OUTPUT_MAP(DepthwiseConv2DBackpropFilterD) = {{0, OUTPUT_DESC(filter_grad)}}; +//杈撳嚭鏄犲皠锛宖ilter_grad绱㈠紩涓0 +REG_ADPT_DESC(DepthwiseConv2DBackpropFilterD, prim::kPrimDepthwiseConv2dNativeBackpropFilter->name(), + ADPT_DESC(DepthwiseConv2DBackpropFilterD)) +//娉ㄥ唽DepthwiseConv2DBackpropFilterD鎿嶄綔鐨勯傞厤鍣ㄦ弿杩発PrimDepthwiseConv2dNativeBackpropFilter杩斿洖鐨刵ame鍙橀噺 +} // namespace mindspore::transform diff --git a/mindspore/ccsrc/transform-update/nn_detect_ops_declare.cc b/mindspore/ccsrc/transform-update/nn_detect_ops_declare.cc new file mode 100644 index 00000000000..588b20e949a --- /dev/null +++ b/mindspore/ccsrc/transform-update/nn_detect_ops_declare.cc @@ -0,0 +1,115 @@ +/** + * Copyright 2019-2021 Huawei Technologies Co., Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "transform/graph_ir/op_declare/nn_detect_ops_declare.h" +#include +#include + +namespace mindspore::transform { +// BoundingBoxEncode +INPUT_MAP(BoundingBoxEncode) = { + {1, INPUT_DESC(anchor_box)}, + {2, INPUT_DESC(ground_truth_box)}, +}; +//杈撳叆鏄犲皠锛宎nchor_box绱㈠紩涓1锛実round_truth_box绱㈠紩涓2 +ATTR_MAP(BoundingBoxEncode) = { + {"means", ATTR_DESC(means, AnyTraits>(), AnyTraits())}, + {"stds", ATTR_DESC(stds, AnyTraits>(), AnyTraits())}, +}; +//灞炴ф槧灏勶紝灞炴eans绫诲瀷涓篺loat锛屽睘鎬tds绫诲瀷涓篺loat +OUTPUT_MAP(BoundingBoxEncode) = {{0, OUTPUT_DESC(delats)}}; +//杈撳嚭鏄犲皠锛宒elats绱㈠紩涓0 +REG_ADPT_DESC(BoundingBoxEncode, kNameBoundingBoxEncode, ADPT_DESC(BoundingBoxEncode)) +//娉ㄥ唽BoundingBoxEncode鎿嶄綔鐨勯傞厤鍣ㄦ弿杩癒NameBoundingBoxEncode + +// BoundingBoxDecode +INPUT_MAP(BoundingBoxDecode) = { + {1, INPUT_DESC(rois)}, + {2, INPUT_DESC(deltas)}, +}; +//杈撳叆鏄犲皠锛宺ois绱㈠紩涓1锛宒eltas绱㈠紩涓2 +ATTR_MAP(BoundingBoxDecode) = { + {"means", ATTR_DESC(means, AnyTraits>(), AnyTraits())}, + {"stds", ATTR_DESC(stds, AnyTraits>(), AnyTraits())}, + {"max_shape", ATTR_DESC(max_shape, AnyTraits>(), AnyTraits>())}, + {"wh_ratio_clip", ATTR_DESC(wh_ratio_clip, AnyTraits())}, +}; +//灞炴ф槧灏勶紝灞炴eans绫诲瀷涓篺loat锛屽睘鎬tds绫诲瀷涓篺loat锛屽睘鎬ax_shape绫诲瀷涓篿nt64_t锛屽睘鎬h_ratio_clip绫诲瀷涓篺loat +OUTPUT_MAP(BoundingBoxDecode) = {{0, OUTPUT_DESC(bboxes)}}; +//杈撳嚭鏄犲皠锛宐boxes绱㈠紩涓0 +REG_ADPT_DESC(BoundingBoxDecode, kNameBoundingBoxDecode, ADPT_DESC(BoundingBoxDecode)) +//娉ㄥ唽BoundingBoxDecode鎿嶄綔鐨勯傞厤鍣ㄦ弿杩癒NameBoundingBoxDecode + +// Iou +INPUT_MAP(Iou) = {{1, INPUT_DESC(bboxes)}, {2, INPUT_DESC(gtboxes)}}; +//杈撳叆鏄犲皠锛宐boxes绱㈠紩涓1锛実tboxes绱㈠紩涓2 +ATTR_MAP(Iou) = {{"mode", ATTR_DESC(mode, AnyTraits())}}; +//灞炴ф槧灏勶紝灞炴ode绫诲瀷涓簊tring +OUTPUT_MAP(Iou) = {{0, OUTPUT_DESC(overlap)}}; +//杈撳嚭鏄犲皠锛宱verlap绱㈠紩涓0 +REG_ADPT_DESC(Iou, kNameIOU, ADPT_DESC(Iou)) +//娉ㄥ唽IOU鎿嶄綔鐨勯傞厤鍣ㄦ弿杩癒NameIOU + +// CheckValid +INPUT_MAP(CheckValid) = {{1, INPUT_DESC(bbox_tensor)}, {2, INPUT_DESC(img_metas)}}; +//杈撳叆鏄犲皠锛宐box_tensor绱㈠紩涓1锛宨mg_metas绱㈠紩涓2 +ATTR_MAP(CheckValid) = EMPTY_ATTR_MAP; +//灞炴ф槧灏勶紝璁句负绌 +OUTPUT_MAP(CheckValid) = {{0, OUTPUT_DESC(valid_tensor)}}; +//杈撳嚭鏄犲皠锛孋heckValid绱㈠紩涓0 +REG_ADPT_DESC(CheckValid, kNameCheckValid, ADPT_DESC(CheckValid)) +//娉ㄥ唽CheckValid鎿嶄綔鐨勯傞厤鍣ㄦ弿杩癒NameCheckValid + +// Sort +INPUT_MAP(Sort) = {{1, INPUT_DESC(x)}}; +//杈撳叆鏄犲皠锛寈绱㈠紩涓1 +ATTR_MAP(Sort) = {{"axis", ATTR_DESC(axis, AnyTraits())}, + {"descending", ATTR_DESC(descending, AnyTraits())}}; +//灞炴ф槧灏勶紝灞炴xis绫诲瀷涓篿nt64_t锛屽睘鎬escending绫诲瀷涓篵ool +OUTPUT_MAP(Sort) = {{0, OUTPUT_DESC(y1)}, {1, OUTPUT_DESC(y2)}}; +//杈撳嚭鏄犲皠锛寉1绱㈠紩涓0锛寉2绱㈠紩涓1 +REG_ADPT_DESC(Sort, kNameSort, ADPT_DESC(Sort)) +//娉ㄥ唽Sort鎿嶄綔鐨勯傞厤鍣ㄦ弿杩癒NameSort + +// ROIAlign +INPUT_MAP(ROIAlign) = {{1, INPUT_DESC(features)}, {2, INPUT_DESC(rois)}}; +//杈撳叆鏄犲皠锛宖eatures绱㈠紩涓1锛宺ois绱㈠紩涓2 +OUTPUT_MAP(ROIAlign) = {{0, OUTPUT_DESC(y)}}; +//杈撳嚭鏄犲皠锛寉绱㈠紩涓0 +ATTR_MAP(ROIAlign) = {{"pooled_height", ATTR_DESC(pooled_height, AnyTraits())}, + {"pooled_width", ATTR_DESC(pooled_width, AnyTraits())}, + {"spatial_scale", ATTR_DESC(spatial_scale, AnyTraits())}, + {"sample_num", ATTR_DESC(sample_num, AnyTraits())}, + {"roi_end_mode", ATTR_DESC(roi_end_mode, AnyTraits())}}; +//灞炴ф槧灏勶紝灞炴ooled_height绫诲瀷涓篿nt64_t锛屽睘鎬ooled_width绫诲瀷涓篿nt64_t锛屽睘鎬patial_scale绫诲瀷涓篺loat锛屽睘鎬ample_num绫诲瀷涓篿nt64_t锛屽睘鎬oi_end_mode绫诲瀷涓篿nt64_t +REG_ADPT_DESC(ROIAlign, kNameROIAlign, ADPT_DESC(ROIAlign)) +//娉ㄥ唽ROIAlign鎿嶄綔鐨勯傞厤鍣ㄦ弿杩癒NameROIAlign + +// ROIAlignGrad +INPUT_MAP(ROIAlignGrad) = {{1, INPUT_DESC(ydiff)}, {2, INPUT_DESC(rois)}}; +//杈撳叆鏄犲皠锛寉diff绱㈠紩涓1锛宺ois绱㈠紩涓2 +OUTPUT_MAP(ROIAlignGrad) = {{0, OUTPUT_DESC(xdiff)}}; +//杈撳嚭鏄犲皠锛寈diff绱㈠紩涓0 +ATTR_MAP(ROIAlignGrad) = { + {"xdiff_shape", ATTR_DESC(xdiff_shape, AnyTraits>(), AnyTraits>())}, + {"pooled_height", ATTR_DESC(pooled_height, AnyTraits())}, + {"pooled_width", ATTR_DESC(pooled_width, AnyTraits())}, + {"spatial_scale", ATTR_DESC(spatial_scale, AnyTraits())}, + {"sample_num", ATTR_DESC(sample_num, AnyTraits())}}; +//灞炴ф槧灏勶紝灞炴diff_shape绫诲瀷涓篿nt64_t锛屽睘鎬ooled_height绫诲瀷涓篿nt64_t锛屽睘鎬ooled_width绫诲瀷涓篿nt64_t锛屽睘鎬patial_scale绫诲瀷涓篺loat锛屽睘鎬ample_num绫诲瀷涓篿nt64_t +REG_ADPT_DESC(ROIAlignGrad, kNameROIAlignGrad, ADPT_DESC(ROIAlignGrad)) +//娉ㄥ唽ROIAlignGrad鎿嶄綔鐨勯傞厤鍣ㄦ弿杩癒NameROIAlignGrad +} // namespace mindspore::transform diff --git a/mindspore/ccsrc/transform-update/nn_norm_ops_declare.cc b/mindspore/ccsrc/transform-update/nn_norm_ops_declare.cc new file mode 100644 index 00000000000..bf2a05566fa --- /dev/null +++ b/mindspore/ccsrc/transform-update/nn_norm_ops_declare.cc @@ -0,0 +1,245 @@ +/** + * Copyright 2019-2021 Huawei Technologies Co., Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "transform/graph_ir/op_declare/nn_norm_ops_declare.h" +#include +#include + +namespace mindspore::transform { +// SoftmaxV2 +INPUT_MAP(SoftmaxV2) = {{1, INPUT_DESC(x)}}; +//杈撳叆鏄犲皠锛寈绱㈠紩涓1 +ATTR_MAP(SoftmaxV2) = { + {"axis", ATTR_DESC(axes, AnyTraits>(), AnyTraits>())}, +}; +//灞炴ф槧灏勶紝灞炴xis绫诲瀷涓篿nt64_t +OUTPUT_MAP(SoftmaxV2) = {{0, OUTPUT_DESC(y)}}; +//杈撳嚭鏄犲皠锛寉绱㈠紩涓0 +REG_ADPT_DESC(SoftmaxV2, kNameSoftmax, ADPT_DESC(SoftmaxV2)) +//娉ㄥ唽SoftmaxV2鎿嶄綔鐨勯傞厤鍣ㄦ弿杩癒NameSoftmax + +// SoftmaxGrad +INPUT_MAP(SoftmaxGrad) = {{1, INPUT_DESC(softmax)}, {2, INPUT_DESC(grad_softmax)}}; +//杈撳叆鏄犲皠锛宻oftmax绱㈠紩涓1锛実rad_softmax绱㈠紩涓2 +OUTPUT_MAP(SoftmaxGrad) = {{0, OUTPUT_DESC(grad_x)}}; +//杈撳嚭鏄犲皠锛孲oftmaxGrad绱㈠紩涓0 +ATTR_MAP(SoftmaxGrad) = EMPTY_ATTR_MAP; +//灞炴ф槧灏勶紝璁句负绌 +REG_ADPT_DESC(SoftmaxGrad, kNameSoftmaxGrad, ADPT_DESC(SoftmaxGrad)) +//娉ㄥ唽SoftmaxGrad鎿嶄綔鐨勯傞厤鍣ㄦ弿杩癒NameSoftmax + +// SoftmaxCrossEntropyWithLogits +INPUT_MAP(SoftmaxCrossEntropyWithLogits) = {{1, INPUT_DESC(features)}, {2, INPUT_DESC(labels)}}; +//杈撳叆鏄犲皠锛宖eatures绱㈠紩涓1锛宭abels绱㈠紩涓2 +ATTR_MAP(SoftmaxCrossEntropyWithLogits) = EMPTY_ATTR_MAP; +//灞炴ф槧灏勶紝璁句负绌 +OUTPUT_MAP(SoftmaxCrossEntropyWithLogits) = {{0, OUTPUT_DESC(loss)}, {1, OUTPUT_DESC(backprop)}}; +//杈撳嚭鏄犲皠锛宭oss绱㈠紩涓0锛宐ackprop绱㈠紩涓1 +REG_ADPT_DESC(SoftmaxCrossEntropyWithLogits, prim::kPrimSoftmaxCrossEntropyWithLogits->name(), + ADPT_DESC(SoftmaxCrossEntropyWithLogits)) +//娉ㄥ唽SoftmaxCrossEntropyWithLogits鎿嶄綔鐨勯傞厤鍣ㄦ弿杩癝oftmaxCrossEntropyWithLogits + +// SmoothL1Loss +INPUT_MAP(SmoothL1Loss) = {{1, INPUT_DESC(predict)}, {2, INPUT_DESC(label)}}; +//杈撳叆鏄犲皠锛宲redict绱㈠紩涓1锛宭abels绱㈠紩涓2 +ATTR_MAP(SmoothL1Loss) = {{"beta", ATTR_DESC(sigma, AnyTraits())}}; +//灞炴ф槧灏勶紝灞炴eta绫诲瀷涓篺loat +OUTPUT_MAP(SmoothL1Loss) = {{0, OUTPUT_DESC(loss)}}; +//杈撳嚭鏄犲皠锛孲moothL1loss绱㈠紩涓0 +REG_ADPT_DESC(SmoothL1Loss, kNameSmoothL1Loss, ADPT_DESC(SmoothL1Loss)) +//娉ㄥ唽SmoothL1Loss鎿嶄綔鐨勯傞厤鍣ㄦ弿杩発NameSmoothL1Loss + +// SmoothL1LossGrad +INPUT_MAP(SmoothL1LossGrad) = {{1, INPUT_DESC(predict)}, {2, INPUT_DESC(label)}, {3, INPUT_DESC(dout)}}; +//杈撳叆鏄犲皠锛宲redict绱㈠紩涓1锛宭abels绱㈠紩涓2锛宒out绱㈠紩涓3 +ATTR_MAP(SmoothL1LossGrad) = {{"beta", ATTR_DESC(sigma, AnyTraits())}}; +//灞炴ф槧灏勶紝灞炴eta绫诲瀷涓篺loat +OUTPUT_MAP(SmoothL1LossGrad) = {{0, OUTPUT_DESC(gradient)}}; +//杈撳嚭鏄犲皠锛実radient绱㈠紩涓0 +REG_ADPT_DESC(SmoothL1LossGrad, kNameSmoothL1LossGrad, ADPT_DESC(SmoothL1LossGrad)) +//娉ㄥ唽SmoothL1LossGrad鎿嶄綔鐨勯傞厤鍣ㄦ弿杩発NameSmoothL1LossGrad + +// SigmoidCrossEntropyWithLogits +INPUT_MAP(SigmoidCrossEntropyWithLogits) = {{1, INPUT_DESC(predict)}, {2, INPUT_DESC(target)}}; +//杈撳叆鏄犲皠锛宲redict绱㈠紩涓1锛宭abels绱㈠紩涓2 +ATTR_MAP(SigmoidCrossEntropyWithLogits) = EMPTY_ATTR_MAP; +//灞炴ф槧灏勶紝璁句负绌 +OUTPUT_MAP(SigmoidCrossEntropyWithLogits) = {{0, OUTPUT_DESC(loss)}}; +//杈撳嚭鏄犲皠锛宭oss绱㈠紩涓0 +REG_ADPT_DESC(SigmoidCrossEntropyWithLogits, kNameSigmoidCrossEntropyWithLogits, + ADPT_DESC(SigmoidCrossEntropyWithLogits)) +//娉ㄥ唽SigmoidCrossEntropyWithLogits鎿嶄綔鐨勯傞厤鍣ㄦ弿杩発NameSigmoidCrossEntropyWithLogits + +// SigmoidCrossEntropyWithLogitsGrad +INPUT_MAP(SigmoidCrossEntropyWithLogitsGrad) = { + {1, INPUT_DESC(predict)}, {2, INPUT_DESC(target)}, {3, INPUT_DESC(dout)}}; +//杈撳叆鏄犲皠锛宲redict绱㈠紩涓1锛宼arget绱㈠紩涓2锛宒out绱㈠紩涓3 +ATTR_MAP(SigmoidCrossEntropyWithLogitsGrad) = EMPTY_ATTR_MAP; +//灞炴ф槧灏勶紝璁句负绌 +OUTPUT_MAP(SigmoidCrossEntropyWithLogitsGrad) = {{0, OUTPUT_DESC(gradient)}}; +//杈撳嚭鏄犲皠锛実radient绱㈠紩涓0 +REG_ADPT_DESC(SigmoidCrossEntropyWithLogitsGrad, kNameSigmoidCrossEntropyWithLogitsGrad, + ADPT_DESC(SigmoidCrossEntropyWithLogitsGrad)) +//娉ㄥ唽SigmoidCrossEntropyWithLogitsGrad鎿嶄綔鐨勯傞厤鍣ㄦ弿杩発NameSigmoidCrossEntropyWithLogitsGrad + +// SigmoidCrossEntropyWithLogitsV2 +INPUT_MAP(SigmoidCrossEntropyWithLogitsV2) = { + {1, INPUT_DESC(predict)}, {2, INPUT_DESC(target)}, {3, INPUT_DESC(weight)}, {4, INPUT_DESC(pos_weight)}}; +//杈撳叆鏄犲皠锛宲redict绱㈠紩涓1锛宼arget绱㈠紩涓2锛寃eight绱㈠紩涓3锛宲os_weight绱㈠紩涓4 +ATTR_MAP(SigmoidCrossEntropyWithLogitsV2) = {{"reduction", ATTR_DESC(reduction, AnyTraits())}}; +//灞炴ф槧灏勶紝灞炴eduction绫诲瀷涓簊tring +OUTPUT_MAP(SigmoidCrossEntropyWithLogitsV2) = {{0, OUTPUT_DESC(loss)}}; +//杈撳嚭鏄犲皠锛宭oss绱㈠紩涓0 +REG_ADPT_DESC(SigmoidCrossEntropyWithLogitsV2, kNameSigmoidCrossEntropyWithLogitsV2, + ADPT_DESC(SigmoidCrossEntropyWithLogitsV2)) +//娉ㄥ唽SigmoidCrossEntropyWithLogitsV2鎿嶄綔鐨勯傞厤鍣ㄦ弿杩発NameSigmoidCrossEntropyWithLogitsV2 + +// LogSoftmaxGrad +INPUT_MAP(LogSoftmaxGrad) = {{1, INPUT_DESC(x)}, {2, INPUT_DESC(grad)}}; +//杈撳叆鏄犲皠锛寈绱㈠紩涓1锛実rad绱㈠紩涓2 +ATTR_MAP(LogSoftmaxGrad) = { + {"axis", ATTR_DESC(axis, AnyTraits>(), AnyTraits>())}}; +//灞炴ф槧灏勶紝灞炴xis绫诲瀷涓篿nt64_t +OUTPUT_MAP(LogSoftmaxGrad) = {{0, OUTPUT_DESC(y)}}; +//杈撳嚭鏄犲皠锛寉绱㈠紩涓0 +REG_ADPT_DESC(LogSoftmaxGrad, prim::kPrimLogSoftmaxGrad->name(), ADPT_DESC(LogSoftmaxGrad)) +//娉ㄥ唽LogSoftmaxGrad鎿嶄綔鐨勯傞厤鍣ㄦ弿杩発PrimLogSoftmaxGrad + +// LogSoftmaxV2 +INPUT_MAP(LogSoftmaxV2) = {{1, INPUT_DESC(logits)}}; +//杈撳叆鏄犲皠锛宭ogits绱㈠紩涓1 +ATTR_MAP(LogSoftmaxV2) = { + {"axis", ATTR_DESC(axes, AnyTraits>(), AnyTraits>())}}; +//灞炴ф槧灏勶紝灞炴xes绫诲瀷涓篿nt64_t +OUTPUT_MAP(LogSoftmaxV2) = {{0, OUTPUT_DESC(logsoftmax)}}; +//杈撳嚭鏄犲皠锛宭ogsoftmax绱㈠紩涓0 +REG_ADPT_DESC(LogSoftmaxV2, prim::kPrimLogSoftmax->name(), ADPT_DESC(LogSoftmaxV2)) +//娉ㄥ唽LogSoftmaxV2鎿嶄綔鐨勯傞厤鍣ㄦ弿杩発PrimLogSoftmax + +// LayerNorm +INPUT_MAP(LayerNorm) = {{1, INPUT_DESC(x)}, {2, INPUT_DESC(gamma)}, {3, INPUT_DESC(beta)}}; +//杈撳叆鏄犲皠锛寈绱㈠紩涓1锛実amma绱㈠紩涓2锛宐eta绱㈠紩涓3 +ATTR_MAP(LayerNorm) = {{"begin_norm_axis", ATTR_DESC(begin_norm_axis, AnyTraits())}, + {"begin_params_axis", ATTR_DESC(begin_params_axis, AnyTraits())}, + {"epsilon", ATTR_DESC(epsilon, AnyTraits())}}; +//灞炴ф槧灏勶紝灞炴egin_norm_axis绫诲瀷涓篿nt64_t锛屽睘鎬egin_params_axis绫诲瀷涓篿nt64_t锛屽睘鎬psilon绫诲瀷涓篺loat +OUTPUT_MAP(LayerNorm) = {{0, OUTPUT_DESC(y)}, {1, OUTPUT_DESC(mean)}, {2, OUTPUT_DESC(variance)}}; +//杈撳嚭鏄犲皠锛寉绱㈠紩涓0锛宮ean绱㈠紩涓1锛寁ariance绱㈠紩涓2 +REG_ADPT_DESC(LayerNorm, prim::kPrimLayerNorm->name(), ADPT_DESC(LayerNorm)) +//娉ㄥ唽LayerNorm鎿嶄綔鐨勯傞厤鍣ㄦ弿杩発PrimLayerNorm + +// LayerNormGrad +INPUT_MAP(LayerNormGrad) = { + {1, INPUT_DESC(x)}, {2, INPUT_DESC(dy)}, {3, INPUT_DESC(variance)}, {4, INPUT_DESC(mean)}, {5, INPUT_DESC(gamma)}}; +//杈撳叆鏄犲皠锛寈绱㈠紩涓1锛宒y绱㈠紩涓2锛寁ariance绱㈠紩涓3锛宮ean绱㈠紩涓4锛実amma绱㈠紩涓5 +ATTR_MAP(LayerNormGrad) = EMPTY_ATTR_MAP; +//灞炴ф槧灏勶紝璁句负绌 +OUTPUT_MAP(LayerNormGrad) = {{0, OUTPUT_DESC(pd_x)}, {1, OUTPUT_DESC(pd_gamma)}, {2, OUTPUT_DESC(pd_beta)}}; +//杈撳嚭鏄犲皠锛宲d_x绱㈠紩涓0锛宲d_gamma绱㈠紩涓1锛宲d_beta绱㈠紩涓2 +REG_ADPT_DESC(LayerNormGrad, prim::kPrimLayerNormGrad->name(), ADPT_DESC(LayerNormGrad)) +//娉ㄥ唽LayerNormGrad鎿嶄綔鐨勯傞厤鍣ㄦ弿杩発PrimLayerNormGrad + +// LRN +INPUT_MAP(LRN) = {{1, INPUT_DESC(x)}}; +//杈撳叆鏄犲皠锛寈绱㈠紩涓1 +ATTR_MAP(LRN) = {{"depth_radius", ATTR_DESC(depth_radius, AnyTraits())}, + {"bias", ATTR_DESC(bias, AnyTraits())}, + {"alpha", ATTR_DESC(alpha, AnyTraits())}, + {"beta", ATTR_DESC(beta, AnyTraits())}, + {"norm_region", ATTR_DESC(norm_region, AnyTraits())}}; +//灞炴ф槧灏勶紝灞炴epth_radius绫诲瀷涓篿nt64_t锛屽睘鎬ias绫诲瀷涓篺loat锛屽睘鎬lpha绫诲瀷涓篺loat锛屽睘鎬eta绫诲瀷涓篺loat锛屽睘鎬orm_region绫诲瀷涓篺loat +OUTPUT_MAP(LRN) = {{0, OUTPUT_DESC(y)}}; +//杈撳嚭鏄犲皠锛寉绱㈠紩涓0 +REG_ADPT_DESC(LRN, kNameLRN, ADPT_DESC(LRN)) +//娉ㄥ唽LRN鎿嶄綔鐨勯傞厤鍣ㄦ弿杩発NameLRN + +// LRNGrad +INPUT_MAP(LRNGrad) = {{1, INPUT_DESC(grads)}, {2, INPUT_DESC(x)}, {3, INPUT_DESC(y)}}; +//杈撳叆鏄犲皠锛実rads绱㈠紩涓1锛寈绱㈠紩涓2锛寉绱㈠紩涓3 +ATTR_MAP(LRNGrad) = {{"depth_radius", ATTR_DESC(depth_radius, AnyTraits())}, + {"bias", ATTR_DESC(bias, AnyTraits())}, + {"alpha", ATTR_DESC(alpha, AnyTraits())}, + {"beta", ATTR_DESC(beta, AnyTraits())}}; +//灞炴ф槧灏勶紝灞炴epth_radius绫诲瀷涓篿nt64_t锛屽睘鎬ias绫诲瀷涓篺loat锛屽睘鎬lpha绫诲瀷涓篺loat锛屽睘鎬eta绫诲瀷涓篺loat +OUTPUT_MAP(LRNGrad) = {{0, OUTPUT_DESC(z)}}; +//杈撳嚭鏄犲皠锛寊绱㈠紩涓0 +REG_ADPT_DESC(LRNGrad, kNameLRNGrad, ADPT_DESC(LRNGrad)) +//娉ㄥ唽LRNGrad鎿嶄綔鐨勯傞厤鍣ㄦ弿杩発NameLRNGrad + +// DropoutDoMask +INPUT_MAP(DropOutDoMask) = {{1, INPUT_DESC(x)}, {2, INPUT_DESC(mask)}, {3, INPUT_DESC(keep_prob)}}; +//杈撳叆鏄犲皠锛寈绱㈠紩涓1锛宮ask绱㈠紩涓2锛宬eep_prob绱㈠紩涓3 +ATTR_MAP(DropOutDoMask) = EMPTY_ATTR_MAP; +//灞炴ф槧灏勶紝璁句负绌 +OUTPUT_MAP(DropOutDoMask) = {{0, OUTPUT_DESC(y)}}; +//杈撳嚭鏄犲皠锛寉绱㈠紩涓0 +REG_ADPT_DESC(DropOutDoMask, kNameDropoutDoMask, ADPT_DESC(DropOutDoMask)) +//娉ㄥ唽DropOutDoMask鎿嶄綔鐨勯傞厤鍣ㄦ弿杩発NameDropOutDoMask + +// BinaryCrossEntropy +INPUT_MAP(BinaryCrossEntropy) = {{1, INPUT_DESC(x)}, {2, INPUT_DESC(y)}, {3, INPUT_DESC(weight)}}; +//杈撳叆鏄犲皠锛寈绱㈠紩涓1锛寉绱㈠紩涓2锛寃eight绱㈠紩涓3 +ATTR_MAP(BinaryCrossEntropy) = {{"reduction", ATTR_DESC(reduction, AnyTraits())}}; +//灞炴ф槧灏勶紝灞炴eduction绫诲瀷涓簊tring +OUTPUT_MAP(BinaryCrossEntropy) = {{0, OUTPUT_DESC(output)}}; +//杈撳嚭鏄犲皠锛宱utput绱㈠紩涓0 +REG_ADPT_DESC(BinaryCrossEntropy, kNameBinaryCrossEntropy, ADPT_DESC(BinaryCrossEntropy)) +//娉ㄥ唽BinaryCrossEntropy鎿嶄綔鐨勯傞厤鍣ㄦ弿杩発NameBinaryCrossEntropy + +// BinaryCrossEntropyGrad +INPUT_MAP(BinaryCrossEntropyGrad) = { + {1, INPUT_DESC(x)}, {2, INPUT_DESC(y)}, {3, INPUT_DESC(grad_output)}, {4, INPUT_DESC(weight)}}; +//杈撳叆鏄犲皠锛寈绱㈠紩涓1锛寉绱㈠紩涓2锛実rad_output绱㈠紩涓3锛寃eight绱㈠紩涓3 +ATTR_MAP(BinaryCrossEntropyGrad) = {{"reduction", ATTR_DESC(reduction, AnyTraits())}}; +//灞炴ф槧灏勶紝灞炴eduction绫诲瀷涓簊tring +OUTPUT_MAP(BinaryCrossEntropyGrad) = {{0, OUTPUT_DESC(output)}}; +//杈撳嚭鏄犲皠锛宱utput绱㈠紩涓0 +REG_ADPT_DESC(BinaryCrossEntropyGrad, kNameBinaryCrossEntropyGrad, ADPT_DESC(BinaryCrossEntropyGrad)) +//娉ㄥ唽BinaryCrossEntropyGrad鎿嶄綔鐨勯傞厤鍣ㄦ弿杩発NameBinaryCrossEntropyGrad + +// Centralization +INPUT_MAP(Centralization) = {{1, INPUT_DESC(x)}}; +//杈撳叆鏄犲皠锛寈绱㈠紩涓1 +ATTR_MAP(Centralization) = {{"axes", ATTR_DESC(axes, AnyTraits>())}}; +//灞炴ф槧灏勶紝灞炴xes绫诲瀷涓篿nt64_t +OUTPUT_MAP(Centralization) = {{0, OUTPUT_DESC(y)}}; +//杈撳嚭鏄犲皠锛寉绱㈠紩涓0 +REG_ADPT_DESC(Centralization, kNameCentralization, ADPT_DESC(Centralization)) +//娉ㄥ唽Centralization鎿嶄綔鐨勯傞厤鍣ㄦ弿杩発NameCentralization + +// Scale +INPUT_MAP(Scale) = {{1, INPUT_DESC(x)}, {2, INPUT_DESC(scale)}, {3, INPUT_DESC(bias)}}; +//杈撳叆鏄犲皠锛寈绱㈠紩涓1锛宻cale绱㈠紩涓2锛宐ias绱㈠紩涓3 +ATTR_MAP(Scale) = {{"axis", ATTR_DESC(axis, AnyTraits())}, + {"num_axes", ATTR_DESC(num_axes, AnyTraits())}, + {"scale_from_blob", ATTR_DESC(scale_from_blob, AnyTraits())}}; +//灞炴ф槧灏勶紝灞炴xes绫诲瀷涓篿nt64_t锛屽睘鎬um_axes绫诲瀷涓篿nt64_t锛屽睘鎬cale_from_blob绫诲瀷涓篵ool +OUTPUT_MAP(Scale) = {{0, OUTPUT_DESC(y)}}; +//杈撳嚭鏄犲皠锛寉绱㈠紩涓0 +REG_ADPT_DESC(Scale, kNameScale, ADPT_DESC(Scale)) +//娉ㄥ唽Scale鎿嶄綔鐨勯傞厤鍣ㄦ弿杩発NameScale + +// KlDivLossGrad +INPUT_MAP(KlDivLossGrad) = {{1, INPUT_DESC(grad)}, {2, INPUT_DESC(input)}, {3, INPUT_DESC(target)}}; +//杈撳叆鏄犲皠锛実rad绱㈠紩涓1锛宨nput绱㈠紩涓2锛宼arget绱㈠紩涓3 +ATTR_MAP(KlDivLossGrad) = {{"reduction", ATTR_DESC(reduction, AnyTraits())}, + {"log_target", ATTR_DESC(log_target, AnyTraits())}}; +//灞炴ф槧灏勶紝灞炴eduction绫诲瀷涓簊tring锛屽睘鎬og_target绫诲瀷涓篵ool +OUTPUT_MAP(KlDivLossGrad) = {{0, OUTPUT_DESC(y)}}; +//杈撳嚭鏄犲皠锛寉绱㈠紩涓0 +REG_ADPT_DESC(KlDivLossGrad, kNameKlDivLossGrad, ADPT_DESC(KlDivLossGrad)) +//娉ㄥ唽KlDivLossGrad鎿嶄綔鐨勯傞厤鍣ㄦ弿杩発NameKlDivLossGrad +} // namespace mindspore::transform diff --git a/mindspore/ccsrc/transform-update/nn_pooling_ops_declare.cc b/mindspore/ccsrc/transform-update/nn_pooling_ops_declare.cc new file mode 100644 index 00000000000..e13224296a7 --- /dev/null +++ b/mindspore/ccsrc/transform-update/nn_pooling_ops_declare.cc @@ -0,0 +1,240 @@ +/** + * Copyright 2019-2021 Huawei Technologies Co., Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "transform/graph_ir/op_declare/nn_pooling_ops_declare.h" +#include + +namespace mindspore::transform { +// MaxPool +INPUT_MAP(MaxPool) = {{1, INPUT_DESC(x)}}; +// 输入映射,x的索引为1 +ATTR_MAP(MaxPool) = {{"kernel_size", ATTR_DESC(ksize, AnyTraits(), AnyTraits>())}, + {"strides", ATTR_DESC(strides, AnyTraits(), AnyTraits>())}, + {"pad_mode", ATTR_DESC(padding, AnyTraits())}, + {"format", ATTR_DESC(data_format, AnyTraits())}}; +// 属性映射,有四个属性,"kernel_size""strides",类型为int64_t和std::vector型,"pad_mode""format"类型为std::string型 +OUTPUT_MAP(MaxPool) = {{0, OUTPUT_DESC(y)}}; +// 输出映射,y索引为0 +REG_ADPT_DESC(MaxPool, kNameMaxPool, ADPT_DESC(MaxPool)) + +// MaxPool3D +INPUT_MAP(MaxPool3D) = {{1, INPUT_DESC(x)}}; +// 输入映射,x的索引为1 +ATTR_MAP(MaxPool3D) = {{"kernel_size", ATTR_DESC(ksize, AnyTraits(), AnyTraits>())}, + {"strides", ATTR_DESC(strides, AnyTraits(), AnyTraits>())}, + {"pad_mode", ATTR_DESC(padding, AnyTraits())}, + {"pad_list", ATTR_DESC(pads, AnyTraits(), AnyTraits>())}, + {"dilation", ATTR_DESC(dilation, AnyTraits(), AnyTraits>())}, + {"ceil_mode", ATTR_DESC(ceil_mode, AnyTraits())}, + {"format", ATTR_DESC(data_format, AnyTraits())}}; +// 属性映射,有七个属性,"kernel_size""pad_list""strides""format""ceil_mode",类型为int64_t和std::vector型,"pad_mode""format"类型为std::string型 +OUTPUT_MAP(MaxPool3D) = {{0, OUTPUT_DESC(y)}}; +// 输出映射,y索引为0 +REG_ADPT_DESC(MaxPool3D, kNameMaxPool3D, ADPT_DESC(MaxPool3D)) +// 注册MaxPool3D操作的适配器描述kNameMaxPool3D + +// MaxPool3DGrad +INPUT_MAP(MaxPool3DGrad) = {{1, INPUT_DESC(orig_x)}, {2, INPUT_DESC(orig_y)}, {3, INPUT_DESC(grads)}}; +// 输入映射,orig_x的索引为1,orig_y的索引为2,grads索引为3 +ATTR_MAP(MaxPool3DGrad) = {{"kernel_size", ATTR_DESC(ksize, AnyTraits(), AnyTraits>())}, + {"strides", ATTR_DESC(strides, AnyTraits(), AnyTraits>())}, + {"pad_list", ATTR_DESC(pads, AnyTraits(), AnyTraits>())}, + {"format", ATTR_DESC(data_format, AnyTraits())}}; +// 属性映射,有四个属性,"kernel_size""strides""pad_list",类型为int64_t和std::vector型,"format"类型为std::string型 +OUTPUT_MAP(MaxPool3DGrad) = {{0, OUTPUT_DESC(y)}}; +// 输出映射,y索引为0 +REG_ADPT_DESC(MaxPool3DGrad, kNameMaxPool3DGrad, ADPT_DESC(MaxPool3DGrad)) +// 注册MaxPool3DGrad操作的适配器描述kNameMaxPool3DGrad + +// MaxPool3DGradGrad +INPUT_MAP(MaxPool3DGradGrad) = {{1, INPUT_DESC(orig_x)}, {2, INPUT_DESC(orig_y)}, {3, INPUT_DESC(grads)}}; +// 输入映射,orig_x的索引为1,orig_y的索引为2,grads索引为3 +ATTR_MAP(MaxPool3DGradGrad) = { + {"kernel_size", ATTR_DESC(ksize, AnyTraits(), AnyTraits>())}, + {"strides", ATTR_DESC(strides, AnyTraits(), AnyTraits>())}, + {"pad_list", ATTR_DESC(pads, AnyTraits(), AnyTraits>())}, + {"format", ATTR_DESC(data_format, AnyTraits())}}; +// 属性映射,有四个属性,"kernel_size""strides""pad_list",类型为int64_t和std::vector型,"format"类型为std::string型 +OUTPUT_MAP(MaxPool3DGradGrad) = {{0, OUTPUT_DESC(y)}}; +// 输出映射,y索引为0 +REG_ADPT_DESC(MaxPool3DGradGrad, kNameMaxPool3DGradGrad, ADPT_DESC(MaxPool3DGradGrad)) +// 注册MaxPool3DGradGrad操作的适配器描述kNameMaxPool3DGradGrad + +// AvgPool +INPUT_MAP(AvgPool) = {{1, INPUT_DESC(x)}}; +// 输入映射,x的索引为1 +ATTR_MAP(AvgPool) = {{"kernel_size", ATTR_DESC(ksize, AnyTraits(), AnyTraits>())}, + {"strides", ATTR_DESC(strides, AnyTraits(), AnyTraits>())}, + {"pad_mode", ATTR_DESC(padding, AnyTraits())}, + {"format", ATTR_DESC(data_format, AnyTraits())}}; +// 属性映射,有四个属性,"kernel_size""strides""pad_list",类型为int64_t和std::vector型,"format"类型为std::string型 +OUTPUT_MAP(AvgPool) = {{0, OUTPUT_DESC(y)}}; +// 输出映射,y索引为0 +REG_ADPT_DESC(AvgPool, kNameAvgPool, ADPT_DESC(AvgPool)) +// 注册AvgPool操作的适配器描述kNameAvgPool + +// MaxPoolGrad +INPUT_MAP(MaxPoolGrad) = {{1, INPUT_DESC(x1)}, {2, INPUT_DESC(x2)}, {3, INPUT_DESC(grad)}}; +// 输入映射,orig_x的索引为1,orig_y的索引为2,grads索引为3 +ATTR_MAP(MaxPoolGrad) = {{"kernel_size", ATTR_DESC(ksize, AnyTraits(), AnyTraits>())}, + {"strides", ATTR_DESC(strides, AnyTraits(), AnyTraits>())}, + {"pad_mode", ATTR_DESC(padding, AnyTraits())}, + {"format", ATTR_DESC(data_format, AnyTraits())}}; +// 属性映射,有四个属性,"kernel_size""strides""pad_list",类型为int64_t和std::vector型,"format"类型为std::string型 +OUTPUT_MAP(MaxPoolGrad) = {{0, OUTPUT_DESC(y)}}; +// 输出映射,y索引为0 +REG_ADPT_DESC(MaxPoolGrad, kNameMaxPoolGrad, ADPT_DESC(MaxPoolGrad)) +// 注册MaxPoolGrad操作的适配器描述kNameMaxPoolGrad + +// MaxPoolGradGrad +INPUT_MAP(MaxPoolGradGrad) = {{1, INPUT_DESC(x1)}, {2, INPUT_DESC(x2)}, {3, INPUT_DESC(grad)}}; +// 输入映射,orig_x的索引为1,orig_y的索引为2,grads索引为3 +ATTR_MAP(MaxPoolGradGrad) = {{"kernel_size", ATTR_DESC(ksize, AnyTraits(), AnyTraits>())}, + {"strides", ATTR_DESC(strides, AnyTraits(), AnyTraits>())}, + {"pad_mode", ATTR_DESC(padding, AnyTraits())}, + {"format", ATTR_DESC(data_format, AnyTraits())}}; +// 属性映射,有四个属性,"kernel_size""strides""pad_list",类型为int64_t和std::vector型,"format"类型为std::string型 +OUTPUT_MAP(MaxPoolGradGrad) = {{0, OUTPUT_DESC(y)}}; +// 输出映射,y索引为0 +REG_ADPT_DESC(MaxPoolGradGrad, kNameMaxPoolGradGrad, ADPT_DESC(MaxPoolGradGrad)) +// 注册MaxPoolGradGrad操作的适配器描述kNameMaxPoolGradGrad + +// avgpoolgrad +INPUT_MAP(AvgPoolGrad) = {{1, INPUT_DESC(orig_input_shape)}, {2, INPUT_DESC(input_grad)}}; +// 输入映射,orig_input_shape的索引为1,input_grad的索引为2 +ATTR_MAP(AvgPoolGrad) = {{"kernel_size", ATTR_DESC(ksize, AnyTraits(), AnyTraits>())}, + {"strides", ATTR_DESC(strides, AnyTraits(), AnyTraits>())}, + {"pad_mode", ATTR_DESC(padding, AnyTraits())}, + {"format", ATTR_DESC(data_format, AnyTraits())}}; +// 属性映射,有四个属性,"kernel_size""strides""pad_list",类型为int64_t和std::vector型,"format"类型为std::string型 +OUTPUT_MAP(AvgPoolGrad) = {{0, OUTPUT_DESC(out_grad)}}; +// 输出映射,out_grad索引为0 +REG_ADPT_DESC(AvgPoolGrad, kNameAvgPoolGrad, ADPT_DESC(AvgPoolGrad)) +// 注册AvgPoolGrad操作的适配器描述kNameAvgPoolGrad + +// MaxPoolWithArgmax +INPUT_MAP(MaxPoolWithArgmax) = {{1, INPUT_DESC(x)}}; +// 输入映射,x的索引为1 +ATTR_MAP(MaxPoolWithArgmax) = { + {"kernel_size", ATTR_DESC(ksize, AnyTraits(), AnyTraits>())}, + {"strides", ATTR_DESC(strides, AnyTraits(), AnyTraits>())}, + {"pad_mode", ATTR_DESC(padding, AnyTraits())}}; +// 属性映射,有三个属性,"kernel_size""strides"类型为int64_t和std::vector型,"pad_mode"类型为std::string型 +OUTPUT_MAP(MaxPoolWithArgmax) = {{0, OUTPUT_DESC(y)}, {1, OUTPUT_DESC(argmax)}}; +// 输出映射,y索引为0,argmax索引为1 +REG_ADPT_DESC(MaxPoolWithArgmax, kNameMaxPoolWithArgmax, ADPT_DESC(MaxPoolWithArgmax)) +// 注册MaxPoolWithArgmax操作的适配器描述kNameMaxPoolWithArgmax + +// MaxPoolGradWithArgmax +INPUT_MAP(MaxPoolGradWithArgmax) = {{1, INPUT_DESC(x)}, {2, INPUT_DESC(grad)}, {3, INPUT_DESC(argmax)}}; +// 输入映射,x的索引为1,grad的索引为2,argmax索引为3 +ATTR_MAP(MaxPoolGradWithArgmax) = { + {"kernel_size", ATTR_DESC(ksize, AnyTraits(), AnyTraits>())}, + {"strides", ATTR_DESC(strides, AnyTraits(), AnyTraits>())}, + {"pad_mode", ATTR_DESC(padding, AnyTraits())}}; +// 属性映射,有三个属性,"kernel_size""strides"类型为int64_t和std::vector型,"pad_mode"类型为std::string型 +OUTPUT_MAP(MaxPoolGradWithArgmax) = {{0, OUTPUT_DESC(y)}}; +// 输出映射,y索引为0 +REG_ADPT_DESC(MaxPoolGradWithArgmax, kNameMaxPoolGradWithArgmax, ADPT_DESC(MaxPoolGradWithArgmax)) +// 注册MaxPoolGradWithArgmax操作的适配器描述kNameMaxPoolGradWithArgmax + +// MaxPoolGradGradWithArgmax +INPUT_MAP(MaxPoolGradGradWithArgmax) = {{1, INPUT_DESC(x)}, {2, INPUT_DESC(grad)}, {3, INPUT_DESC(argmax)}}; +// 输入映射,x的索引为1,grad的索引为2,argmax索引为3 +ATTR_MAP(MaxPoolGradGradWithArgmax) = { + {"kernel_size", ATTR_DESC(ksize, AnyTraits(), AnyTraits>())}, + {"strides", ATTR_DESC(strides, AnyTraits(), AnyTraits>())}, + {"pad_mode", ATTR_DESC(padding, AnyTraits())}}; +// 属性映射,有三个属性,"kernel_size""strides"类型为int64_t和std::vector型,"pad_mode"类型为std::string型 +OUTPUT_MAP(MaxPoolGradGradWithArgmax) = {{0, OUTPUT_DESC(y)}}; +// 输出映射,y索引为0 +REG_ADPT_DESC(MaxPoolGradGradWithArgmax, kNameMaxPoolGradGradWithArgmax, ADPT_DESC(MaxPoolGradGradWithArgmax)) +// 注册MaxPoolGradGradWithArgmax操作的适配器描述kNameMaxPoolGradGradWithArgmax + +// Pooling +INPUT_MAP(Pooling) = {{1, INPUT_DESC(x)}}; +// 输入映射,x的索引为1 +ATTR_MAP(Pooling) = {{"mode", ATTR_DESC(mode, AnyTraits())}, + {"global", ATTR_DESC(global_pooling, AnyTraits())}, + {"kernel_size", ATTR_DESC(window, AnyTraits(), AnyTraits>())}, + {"strides", ATTR_DESC(stride, AnyTraits(), AnyTraits>())}, + {"pad", ATTR_DESC(pad, AnyTraits(), AnyTraits>())}, + {"dilation", ATTR_DESC(dilation, AnyTraits(), AnyTraits>())}, + {"round_mode", ATTR_DESC(ceil_mode, AnyTraits())}, + {"format", ATTR_DESC(data_format, AnyTraits())}}; +//属性映射,有八个属性,"kernel_size""strides""pad""dilation"类型为int64_t和std::vector型,"format"类型为std::string型 +//"mode""round_mode"类型为int64_t型,"global"类型为bool型 +OUTPUT_MAP(Pooling) = {{0, OUTPUT_DESC(y)}}; +// 输出映射,y索引为0 +REG_ADPT_DESC(Pooling, kNamePooling, ADPT_DESC(Pooling)) +// 注册Pooling操作的适配器描述kNamePooling + +// MaxPoolV3 +INPUT_MAP(MaxPoolV3) = {{1, INPUT_DESC(x)}}; +// 输入映射,x的索引为1 +ATTR_MAP(MaxPoolV3) = {{"kernel_size", ATTR_DESC(ksize, AnyTraits(), AnyTraits>())}, + {"strides", ATTR_DESC(strides, AnyTraits(), AnyTraits>())}, + {"padding_mode", ATTR_DESC(padding_mode, AnyTraits())}, + {"pad", ATTR_DESC(pads, AnyTraits(), AnyTraits>())}, + {"format", ATTR_DESC(data_format, AnyTraits())}, + {"global", ATTR_DESC(global_pooling, AnyTraits())}, + {"ceil_mode", ATTR_DESC(ceil_mode, AnyTraits())}}; +// 属性映射,有七个属性,"kernel_size""strides""pad"类型为int64_t和std::vector型,"format"类型为std::string型 +//"round_mode"类型为int64_t型,"global""ceil_mode"类型为bool型 +OUTPUT_MAP(MaxPoolV3) = {{0, OUTPUT_DESC(y)}}; +// 输出映射,y索引为0 +REG_ADPT_DESC(MaxPoolV3, kNameMaxPoolV3, ADPT_DESC(MaxPoolV3)) +// 注册MaxPoolV3操作的适配器描述kNameMaxPoolV3 + +// AvgPoolV2 +INPUT_MAP(AvgPoolV2) = {{1, INPUT_DESC(x)}}; +// 输入映射,x的索引为1 +ATTR_MAP(AvgPoolV2) = {{"kernel_size", ATTR_DESC(ksize, AnyTraits(), AnyTraits>())}, + {"strides", ATTR_DESC(strides, AnyTraits(), AnyTraits>())}, + {"padding_mode", ATTR_DESC(padding_mode, AnyTraits())}, + {"pad", ATTR_DESC(pads, AnyTraits(), AnyTraits>())}, + {"format", ATTR_DESC(data_format, AnyTraits())}, + {"global", ATTR_DESC(global_pooling, AnyTraits())}, + {"ceil_mode", ATTR_DESC(ceil_mode, AnyTraits())}}; +// 属性映射,有七个属性,"kernel_size""strides""pad"类型为int64_t和std::vector型,"format"类型为std::string型 +//"round_mode"类型为int64_t型,"global""ceil_mode"类型为bool型 +OUTPUT_MAP(AvgPoolV2) = {{0, OUTPUT_DESC(y)}}; +// 输出映射,y索引为0 +REG_ADPT_DESC(AvgPoolV2, kNameAvgPoolV2, ADPT_DESC(AvgPoolV2)) +// 注册AvgPoolV2操作的适配器描述kNameAvgPoolV2 + +// GlobalAveragePool +INPUT_MAP(GlobalAveragePool) = {{1, INPUT_DESC(x)}}; +// 输入映射,x的索引为1 +ATTR_MAP(GlobalAveragePool) = EMPTY_ATTR_MAP; +//属性映射,空 +OUTPUT_MAP(GlobalAveragePool) = {{0, OUTPUT_DESC(y)}}; +// 输出映射,y索引为0 +REG_ADPT_DESC(GlobalAveragePool, kNameGlobalAvgPool, ADPT_DESC(GlobalAveragePool)) +// 注册GlobalAveragePool操作的适配器描述kNameGlobalAvgPool + +// Upsample +INPUT_MAP(Upsample) = {{1, INPUT_DESC(x)}}; +// 输入映射,x的索引为1 +ATTR_MAP(Upsample) = {{"scale", ATTR_DESC(scale, AnyTraits())}, + {"stride_h", ATTR_DESC(stride_h, AnyTraits())}, + {"stride_w", ATTR_DESC(stride_w, AnyTraits())}}; +// 属性映射,有三个属性,"stride_h""stride_w"类型为int64_t型,"scale"类型为float型 +OUTPUT_MAP(Upsample) = {{0, OUTPUT_DESC(y)}}; +// 输出映射,y索引为0 +REG_ADPT_DESC(Upsample, kNameUpsample, ADPT_DESC(Upsample)) +// 注册Upsample操作的适配器描述kNameUpsample +} // namespace mindspore::transform diff --git a/mindspore/ccsrc/transform-update/nn_training_ops_declare.cc b/mindspore/ccsrc/transform-update/nn_training_ops_declare.cc new file mode 100644 index 00000000000..c8b78ecfe30 --- /dev/null +++ b/mindspore/ccsrc/transform-update/nn_training_ops_declare.cc @@ -0,0 +1,315 @@ +/** + * Copyright 2019-2021 Huawei Technologies Co., Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "transform/graph_ir/op_declare/nn_training_ops_declare.h" + +namespace mindspore::transform { +// ApplyMomentum +INPUT_MAP(ApplyMomentum) = { + {1, INPUT_DESC(var)}, {2, INPUT_DESC(accum)}, {3, INPUT_DESC(lr)}, {4, INPUT_DESC(grad)}, {5, INPUT_DESC(momentum)}}; +// 输入映射,有四个输入,accum索引为2,lr索引为3,grad索引为4,momentum索引为5 +ATTR_MAP(ApplyMomentum) = {{"use_nesterov", ATTR_DESC(use_nesterov, AnyTraits())}, + {"use_locking", ATTR_DESC(use_locking, AnyTraits())}}; +// 属性映射,有两个属性"use_nesterov""use_locking",类型未bool型 +OUTPUT_MAP(ApplyMomentum) = {{0, OUTPUT_DESC(var)}};' + //输出映射,var的索引为0 + REG_ADPT_DESC(ApplyMomentum, kNameApplyMomentum, ADPT_DESC(ApplyMomentum)) + // 注册ApplyMomentum操作的适配器描述kNameApplyMomentum + // + // LarsV2Update +INPUT_MAP(LarsV2Update) = {{1, INPUT_DESC(w)}, + {2, INPUT_DESC(g)}, + {3, INPUT_DESC(w_square_sum)}, + {4, INPUT_DESC(g_square_sum)}, + {5, INPUT_DESC(weight_decay)}, + {6, INPUT_DESC(learning_rate)}}; +// 输入映射,有六个输入,w索引为1,g索引为2,w_square_sum索引为3,g_square_sum索引为4,weight_decay索引为5,learning_rate索引为6 +ATTR_MAP(LarsV2Update) = {{"epsilon", ATTR_DESC(epsilon, AnyTraits())}, + {"hyperpara", ATTR_DESC(hyperpara, AnyTraits())}, + {"use_clip", ATTR_DESC(use_clip, AnyTraits())}}; +// 属性映射,有三个属性,"epsilon""hyperpara",类型为float型,"use_clip"类型为bool型 +OUTPUT_MAP(LarsV2Update) = {{0, OUTPUT_DESC(g_new)}}; +// 输出映射,g_new的索引为0 +REG_ADPT_DESC(LarsV2Update, kNameLARSUpdate, ADPT_DESC(LarsV2Update)) +// 注册LarsV2Update操作的适配器描述kNameLARSUpdate + +// ApplyAdam +INPUT_MAP(ApplyAdam) = {{1, INPUT_DESC(var)}, {2, INPUT_DESC(m)}, {3, INPUT_DESC(v)}, + {4, INPUT_DESC(beta1_power)}, {5, INPUT_DESC(beta2_power)}, {6, INPUT_DESC(lr)}, + {7, INPUT_DESC(beta1)}, {8, INPUT_DESC(beta2)}, {9, INPUT_DESC(epsilon)}, + {10, INPUT_DESC(grad)}}; +// 输入映射,有十个输入,var索引为1,m索引为2,v索引为3,beta1_power索引为4,beta2_power索引为5,lr索引为6 +// betal索引为7,beta2索引为8,epsilon索引为9,grad索引为10 +ATTR_MAP(ApplyAdam) = {{"use_locking", ATTR_DESC(use_locking, AnyTraits())}, + {"use_nesterov", ATTR_DESC(use_nesterov, AnyTraits())}}; +// 属性映射,有两个属性"use_locking""use_nesterov",类型为bool型 +OUTPUT_MAP(ApplyAdam) = {{0, OUTPUT_DESC(var)}}; +// 输出映射,var的索引为0 +// ApplyAdamD +INPUT_MAP(ApplyAdamD) = {{1, INPUT_DESC(var)}, {2, INPUT_DESC(m)}, {3, INPUT_DESC(v)}, + {4, INPUT_DESC(beta1_power)}, {5, INPUT_DESC(beta2_power)}, {6, INPUT_DESC(lr)}, + {7, INPUT_DESC(beta1)}, {8, INPUT_DESC(beta2)}, {9, INPUT_DESC(epsilon)}, + {10, INPUT_DESC(grad)}}; +// 输入映射,有十个输入,var索引为1,m索引为2,v索引为3,beta1_power索引为4,beta2_power索引为5,lr索引为6 +// betal索引为7,beta2索引为8,epsilon索引为9,grad索引为10 +ATTR_MAP(ApplyAdamD) = {{"use_locking", ATTR_DESC(use_locking, AnyTraits())}, + {"use_nesterov", ATTR_DESC(use_nesterov, AnyTraits())}}; +// 属性映射,有两个属性"use_locking""use_nesterov",类型为bool型 +OUTPUT_MAP(ApplyAdamD) = {{0, OUTPUT_DESC(var)}, {1, OUTPUT_DESC(m)}, {2, OUTPUT_DESC(v)}}; +// 输出映射,共三个,var的索引为0,m索引为1,v索引为2 +REG_ADPT_DESC(ApplyAdamD, kNameApplyAdam, ADPT_DESC(ApplyAdamD)) +// 注册ApplyAdamD操作的适配器描述kNameApplyAdam +REG_ADPT_DESC(ApplyAdam, kNameApplyAdam, ADPT_DESC(ApplyAdam)) +// 注册ApplyAdam操作的适配器描述kNameApplyAdam + +// ApplyAdagradD +INPUT_MAP(ApplyAdagradD) = {{1, INPUT_DESC(var)}, {2, INPUT_DESC(accum)}, {3, INPUT_DESC(lr)}, {4, INPUT_DESC(grad)}}; +// 输入映射,有四个输入,var索引为1,accum索引为2,lr索引为3,grad索引为4 +ATTR_MAP(ApplyAdagradD) = {{"update_slots", ATTR_DESC(update_slots, AnyTraits())}, + {"use_locking", ATTR_DESC(use_locking, AnyTraits())}}; +// 属性映射,有两个属性"use_locking""use_nesterov",类型为bool型 +OUTPUT_MAP(ApplyAdagradD) = {{0, OUTPUT_DESC(var)}, {1, OUTPUT_DESC(accum)}}; +// 输出映射,共两个,var的索引为0,accum索引为1 +REG_ADPT_DESC(ApplyAdagradD, kNameApplyAdagrad, ADPT_DESC(ApplyAdagradD)) +// 注册ApplyAdagradD操作的适配器描述 kNameApplyAdagrad +// +// ApplyAdagradV2D +INPUT_MAP(ApplyAdagradV2D) = {{1, INPUT_DESC(var)}, {2, INPUT_DESC(accum)}, {3, INPUT_DESC(lr)}, {4, INPUT_DESC(grad)}}; +// 输入映射,有四个输入,var索引为1,accum索引为2,lr索引为3,grad索引为4 +ATTR_MAP(ApplyAdagradV2D) = {{"epsilon", ATTR_DESC(epsilon, AnyTraits())}, + {"update_slots", ATTR_DESC(update_slots, AnyTraits())}, + {"use_locking", ATTR_DESC(use_locking, AnyTraits())}}; +// 属性映射,有三个属性,"epsilon",类型为float型,"update_slots""use_locking"类型为bool型 +OUTPUT_MAP(ApplyAdagradV2D) = {{0, OUTPUT_DESC(var)}, {1, OUTPUT_DESC(accum)}}; +// 输出映射,共两个,var的索引为0,accum索引为1 +REG_ADPT_DESC(ApplyAdagradV2D, kNameApplyAdagradV2D, ADPT_DESC(ApplyAdagradV2D)) +// 注册ApplyAdagradV2D操作的适配器描述 kNameApplyAdagradV2D + +// ApplyAddSignD +INPUT_MAP(ApplyAddSignD) = {{1, INPUT_DESC(var)}, {2, INPUT_DESC(m)}, {3, INPUT_DESC(lr)}, + {4, INPUT_DESC(alpha)}, {5, INPUT_DESC(sign_decay)}, {6, INPUT_DESC(beta)}, + {7, INPUT_DESC(grad)}}; +// 输入映射,有七个输入,var索引为1,m索引为2,lr索引为3,alpha索引为4,sign_decay索引为5,beta索引为6,grad索引为7 +ATTR_MAP(ApplyAddSignD) = {{"use_locking", ATTR_DESC(use_locking, AnyTraits())}}; +//属性映射,有一个属性,"use_locking"类型为bool型 +OUTPUT_MAP(ApplyAddSignD) = {{0, OUTPUT_DESC(var)}, {1, OUTPUT_DESC(m)}}; +// 输出映射,共两个,var的索引为0,m索引为1 +REG_ADPT_DESC(ApplyAddSignD, kNameApplyAddSignD, ADPT_DESC(ApplyAddSignD)) +// 注册ApplyAddSignD操作的适配器描述kNameApplyAddSignD + +// SparseApplyAdagradV2D +INPUT_MAP(SparseApplyAdagradV2D) = { + {1, INPUT_DESC(var)}, {2, INPUT_DESC(accum)}, {3, INPUT_DESC(grad)}, {4, INPUT_DESC(indices)}}; +// 输入映射,有四个输入,var索引为1,accum索引为2,grad索引为3,indices索引为4 +ATTR_MAP(SparseApplyAdagradV2D) = {{"lr", ATTR_DESC(lr, AnyTraits())}, + {"epsilon", ATTR_DESC(epsilon, AnyTraits())}, + {"update_slots", ATTR_DESC(update_slots, AnyTraits())}, + {"use_locking", ATTR_DESC(use_locking, AnyTraits())}}; +// 属性映射,有四个属性,"lr""epsilon",类型为float型,"update_slots""use_locking"类型为bool型 +OUTPUT_MAP(SparseApplyAdagradV2D) = {{0, OUTPUT_DESC(var)}, {1, OUTPUT_DESC(accum)}}; +// 输出映射,共两个,var的索引为0,accum索引为1 +REG_ADPT_DESC(SparseApplyAdagradV2D, kNameSparseApplyAdagradV2D, ADPT_DESC(SparseApplyAdagradV2D)) +// 注册SparseApplyAdagradV2D操作的适配器描述kNameSparseApplyAdagradV2D + +// DataFormatDimMap +INPUT_MAP(DataFormatDimMap) = {{1, INPUT_DESC(x)}}; +// 输入映射,x索引为1 +ATTR_MAP(DataFormatDimMap) = {{"src_format", ATTR_DESC(src_format, AnyTraits())}, + {"dst_format", ATTR_DESC(dst_format, AnyTraits())}}; +// 属性映射,有两个属性,"src_format""dst_format",类型为td::string型 +OUTPUT_MAP(DataFormatDimMap) = {{0, OUTPUT_DESC(y)}}; +// 输出映射,y的索引为0 +REG_ADPT_DESC(DataFormatDimMap, kNameDataFormatDimMap, ADPT_DESC(DataFormatDimMap)) +// 注册DataFormatDimMap操作的适配器描述kNameDataFormatDimMap + +// ApplyAdadeltaD +INPUT_MAP(ApplyAdadeltaD) = {{1, INPUT_DESC(var)}, {2, INPUT_DESC(accum)}, {3, INPUT_DESC(accum_update)}, + {4, INPUT_DESC(lr)}, {5, INPUT_DESC(rho)}, {6, INPUT_DESC(epsilon)}, + {7, INPUT_DESC(grad)}}; +// 输入映射,有七个输入,var索引为1,accum索引为2,accum_update索引为3,lr索引为4,rho索引为5,epsilon索引为6,grad索引为7 +ATTR_MAP(ApplyAdadeltaD) = {{"use_locking", ATTR_DESC(use_locking, AnyTraits())}}; +// 属性映射,有一个属性,"use_locking"类型为bool型 +OUTPUT_MAP(ApplyAdadeltaD) = {{0, OUTPUT_DESC(var)}, {1, OUTPUT_DESC(accum)}, {2, OUTPUT_DESC(accum_update)}}; +// 输出映射,共三个,var的索引为0,accum索引为1,accum_update索引为2 +REG_ADPT_DESC(ApplyAdadeltaD, kNameApplyAdadelta, ADPT_DESC(ApplyAdadeltaD)) +// 注册ApplyAdadeltaD操作的适配器描述 kNameApplyAdadelta + +// ApplyAdaMaxD +INPUT_MAP(ApplyAdaMaxD) = {{1, INPUT_DESC(var)}, {2, INPUT_DESC(m)}, {3, INPUT_DESC(v)}, + {4, INPUT_DESC(beta1_power)}, {5, INPUT_DESC(lr)}, {6, INPUT_DESC(beta1)}, + {7, INPUT_DESC(beta2)}, {8, INPUT_DESC(epsilon)}, {9, INPUT_DESC(grad)}}; +// 输入映射,有十个输入,var索引为1,m索引为2,v索引为3,beta1_power索引为4,lr索引为5 +// betal索引为6,beta2索引为7,epsilon索引为8,grad索引为9 +ATTR_MAP(ApplyAdaMaxD) = {{"use_locking", ATTR_DESC(use_locking, AnyTraits())}}; +// 属性映射,有一个属性,"use_locking"类型为bool型 +OUTPUT_MAP(ApplyAdaMaxD) = {{0, OUTPUT_DESC(var)}, {1, OUTPUT_DESC(m)}, {2, OUTPUT_DESC(v)}}; +// 输出映射,共三个,var的索引为0,m索引为1,v索引为2 +REG_ADPT_DESC(ApplyAdaMaxD, kNameApplyAdaMax, ADPT_DESC(ApplyAdaMaxD)) +// 注册ApplyAdaMaxD操作的适配器描述 kNameApplyAdaMax + +// ApplyGradientDescent +INPUT_MAP(ApplyGradientDescent) = {{1, INPUT_DESC(var)}, {2, INPUT_DESC(alpha)}, {3, INPUT_DESC(delta)}}; +// 输入映射,有三个输入,var索引为1,alpha索引为2,delta索引为3 +ATTR_MAP(ApplyGradientDescent) = {{"use_locking", ATTR_DESC(use_locking, AnyTraits())}}; +// 属性映射,有一个属性,"use_locking"类型为bool型 +OUTPUT_MAP(ApplyGradientDescent) = {{0, OUTPUT_DESC(var)}}; +// 输出映射,var的索引为0 +REG_ADPT_DESC(ApplyGradientDescent, kNameApplyGradientDescent, ADPT_DESC(ApplyGradientDescent)) +// 注册ApplyGradientDescent操作的适配器描述kNameApplyGradientDescent + +// ApplyPowerSignD +INPUT_MAP(ApplyPowerSignD) = {{1, INPUT_DESC(var)}, {2, INPUT_DESC(m)}, {3, INPUT_DESC(lr)}, + {4, INPUT_DESC(logbase)}, {5, INPUT_DESC(sign_decay)}, {6, INPUT_DESC(beta)}, + {7, INPUT_DESC(grad)}}; +// 输入映射,有七个输入,var索引为1,m索引为2,lr索引为3,logbase索引为4,sign_decay索引为5 +// beta索引为6,grad索引为7 +ATTR_MAP(ApplyPowerSignD) = {{"use_locking", ATTR_DESC(use_locking, AnyTraits())}}; +// 属性映射,有一个属性,"use_locking"类型为bool型 +OUTPUT_MAP(ApplyPowerSignD) = {{0, OUTPUT_DESC(var)}, {1, OUTPUT_DESC(m)}}; +// 输出映射,共两个,var的索引为0,m索引为1 +REG_ADPT_DESC(ApplyPowerSignD, kNameApplyPowerSign, ADPT_DESC(ApplyPowerSignD)) +// 注册ApplyPowerSignD操作的适配器描述kNameApplyPowerSign + +// ApplyProximalGradientDescent +INPUT_MAP(ApplyProximalGradientDescent) = { + {1, INPUT_DESC(var)}, {2, INPUT_DESC(alpha)}, {3, INPUT_DESC(l1)}, {4, INPUT_DESC(l2)}, {5, INPUT_DESC(delta)}}; +// 输入映射,有五个输入,var索引为1,alpha索引为2,l1索引为3,l2索引为4,delta索引为5 +ATTR_MAP(ApplyProximalGradientDescent) = {{"use_locking", ATTR_DESC(use_locking, AnyTraits())}}; +// 属性映射,有一个属性,"use_locking"类型为bool型 +OUTPUT_MAP(ApplyProximalGradientDescent) = {{0, OUTPUT_DESC(var)}}; +// 输出映射,var的索引为0 +REG_ADPT_DESC(ApplyProximalGradientDescent, kNameApplyProximalGradientDescent, ADPT_DESC(ApplyProximalGradientDescent)) +// 注册ApplyProximalGradientDescent操作的适配器描述kNameApplyProximalGradientDescent +// +// SGD +INPUT_MAP(SGD) = {{1, INPUT_DESC(parameters)}, {2, INPUT_DESC(gradient)}, {3, INPUT_DESC(learning_rate)}, + {4, INPUT_DESC(accum)}, {5, INPUT_DESC(momentum)}, {6, INPUT_DESC(stat)}}; +// 输入映射,有六个输入,parameters索引为1,gradient索引为2,lr索引为3,logbase索引为4,sign_decay索引为5,stat索引为6 +ATTR_MAP(SGD) = {{"dampening", ATTR_DESC(dampening, AnyTraits())}, + {"weight_decay", ATTR_DESC(weight_decay, AnyTraits())}, + {"nesterov", ATTR_DESC(nesterov, AnyTraits())}}; +// 属性映射,有三个属性,"dampening""weight_decay"类型为float型,"nesterov"类型为bool型 +OUTPUT_MAP(SGD) = {{0, OUTPUT_DESC(parameters)}}; +// 输出映射,parameters的索引为0 +REG_ADPT_DESC(SGD, kNameSGD, ADPT_DESC(SGD)) +// 注册SGD操作的适配器描述kNameSGD + +// SparseApplyAdagradD +INPUT_MAP(SparseApplyAdagradD) = { + {1, INPUT_DESC(var)}, {2, INPUT_DESC(accum)}, {3, INPUT_DESC(grad)}, {4, INPUT_DESC(indices)}}; +// 输入映射,有四个输入,var索引为1,accum索引为2,grad索引为3,indices索引为4 +ATTR_MAP(SparseApplyAdagradD) = {{"lr", ATTR_DESC(lr, AnyTraits())}, + {"use_locking", ATTR_DESC(use_locking, AnyTraits())}}; +// 属性映射,有两个属性,"lr"类型为float型,"use_locking"类型为bool型 +OUTPUT_MAP(SparseApplyAdagradD) = {{0, OUTPUT_DESC(var)}}; +// 输出映射,var的索引为0 +REG_ADPT_DESC(SparseApplyAdagradD, kNameSparseApplyAdagrad, ADPT_DESC(SparseApplyAdagradD)) +// 注册SparseApplyAdagradD操作的适配器描述 kNameSparseApplyAdagrad + +// ApplyProximalAdagradD +INPUT_MAP(ApplyProximalAdagradD) = {{1, INPUT_DESC(var)}, {2, INPUT_DESC(accum)}, {3, INPUT_DESC(lr)}, + {4, INPUT_DESC(l1)}, {5, INPUT_DESC(l2)}, {6, INPUT_DESC(grad)}}; +// 输入映射,有六个输入,var索引为1,accum索引为2,lr索引为3,l1索引为4,l2索引为5,grad索引为6 +ATTR_MAP(ApplyProximalAdagradD) = {{"use_locking", ATTR_DESC(use_locking, AnyTraits())}}; +// 属性映射,有一个属性,"use_locking"类型为bool型 +OUTPUT_MAP(ApplyProximalAdagradD) = {{0, OUTPUT_DESC(var)}, {1, OUTPUT_DESC(accum)}}; +// 输出映射,var的索引为0,accum的索引为1 +REG_ADPT_DESC(ApplyProximalAdagradD, kNameApplyProximalAdagrad, ADPT_DESC(ApplyProximalAdagradD)) +// 注册ApplyProximalAdagradD操作的适配器描述kNameApplyProximalAdagrad +// +// SparseApplyProximalAdagradD +INPUT_MAP(SparseApplyProximalAdagradD) = {{1, INPUT_DESC(var)}, {2, INPUT_DESC(accum)}, {3, INPUT_DESC(lr)}, + {4, INPUT_DESC(l1)}, {5, INPUT_DESC(l2)}, {6, INPUT_DESC(grad)}, + {7, INPUT_DESC(indices)}}; +// 输入映射,有七个输入,var索引为1,accum索引为2,lr索引为3,l1索引为4,l2索引为5,grad索引为6,indices索引为7 +ATTR_MAP(SparseApplyProximalAdagradD) = {{"use_locking", ATTR_DESC(use_locking, AnyTraits())}}; +// 属性映射,有一个属性,"use_locking"类型为bool型 +OUTPUT_MAP(SparseApplyProximalAdagradD) = {{0, OUTPUT_DESC(var)}, {1, OUTPUT_DESC(accum)}}; +// 输出映射,var的索引为0,accum的索引为1 +REG_ADPT_DESC(SparseApplyProximalAdagradD, kNameSparseApplyProximalAdagradD, ADPT_DESC(SparseApplyProximalAdagradD)) +// 注册SparseApplyProximalAdagradD操作的适配器描述kNameSparseApplyProximalAdagradD + +// SparseApplyFtrlD +INPUT_MAP(SparseApplyFtrlD) = {{1, INPUT_DESC(var)}, + {2, INPUT_DESC(accum)}, + {3, INPUT_DESC(linear)}, + {4, INPUT_DESC(grad)}, + {5, INPUT_DESC(indices)}}; +// 输入映射,有五个输入,var索引为1,accum索引为2,linear索引为3,grad索引为4,indices索引为5 +ATTR_MAP(SparseApplyFtrlD) = {{"use_locking", ATTR_DESC(use_locking, AnyTraits())}, + {"lr", ATTR_DESC(lr, AnyTraits())}, + {"l1", ATTR_DESC(l1, AnyTraits())}, + {"l2", ATTR_DESC(l2, AnyTraits())}, + {"lr_power", ATTR_DESC(lr_power, AnyTraits())}}; +// 属性映射,有五个属性,"use_locking""lr""l1""l2""lr_power"类型为float型 +OUTPUT_MAP(SparseApplyFtrlD) = {{0, OUTPUT_DESC(var)}}; +// 输出映射,var的索引为0 +REG_ADPT_DESC(SparseApplyFtrlD, kNameSparseApplyFtrlD, ADPT_DESC(SparseApplyFtrlD)) +// 注册SparseApplyFtrlD操作的适配器描述kNameSparseApplyFtrlD + +// SparseApplyFtrlV2D +INPUT_MAP(SparseApplyFtrlV2D) = {{1, INPUT_DESC(var)}, + {2, INPUT_DESC(accum)}, + {3, INPUT_DESC(linear)}, + {4, INPUT_DESC(grad)}, + {5, INPUT_DESC(indices)}}; +// 输入映射,有五个输入,var索引为1,accum索引为2,linear索引为3,grad索引为4,indices索引为5 +ATTR_MAP(SparseApplyFtrlV2D) = {{"lr", ATTR_DESC(lr, AnyTraits())}, {"l1", ATTR_DESC(l1, AnyTraits())}}; +// 属性映射,有两个属性,"l1""l2"类型为float型 +OUTPUT_MAP(SparseApplyFtrlV2D) = {{0, OUTPUT_DESC(var)}, {1, OUTPUT_DESC(accum)}, {2, OUTPUT_DESC(linear)}}; +// 输出映射,var的索引为0,accum的索引为1,linear索引为2 +REG_ADPT_DESC(SparseApplyFtrlV2D, kNameSparseApplyFtrlV2D, ADPT_DESC(SparseApplyFtrlV2D)) +// 注册SparseApplyFtrlV2D操作的适配器描述 kNameSparseApplyFtrlV2D + +// ApplyFtrl +INPUT_MAP(ApplyFtrl) = {{1, INPUT_DESC(var)}, {2, INPUT_DESC(accum)}, {3, INPUT_DESC(linear)}, + {4, INPUT_DESC(grad)}, {5, INPUT_DESC(lr)}, {6, INPUT_DESC(l1)}, + {7, INPUT_DESC(l2)}, {8, INPUT_DESC(lr_power)}}; +// 输入映射,有八个输入,var索引为1,accum索引为2,linear索引为3,grad索引为4,lr索引为5,l1索引为6,l2索引为7,lr_power索引为8 +ATTR_MAP(ApplyFtrl) = {{"use_locking", ATTR_DESC(use_locking, AnyTraits())}}; +// 属性映射,有一个属性,"use_locking"类型为bool型 +OUTPUT_MAP(ApplyFtrl) = {{0, OUTPUT_DESC(var)}}; +// 输出映射,var的索引为0 +REG_ADPT_DESC(ApplyFtrl, kNameApplyFtrl, ADPT_DESC(ApplyFtrl)) +// 注册ApplyFtrl操作的适配器描述kNameApplyFtrl + +// ApplyRMSPropD +INPUT_MAP(ApplyRMSPropD) = { + {1, INPUT_DESC(var)}, {2, INPUT_DESC(ms)}, {3, INPUT_DESC(mom)}, {4, INPUT_DESC(lr)}, {5, INPUT_DESC(grad)}}; +// 输入映射,有五个输入,var索引为1,ms索引为2,mom索引为3,lr索引为4,grad索引为5 +INPUT_ATTR_MAP(ApplyRMSPropD) = {{6, ATTR_DESC(rho, AnyTraits())}, + {7, ATTR_DESC(momentum, AnyTraits())}, + {8, ATTR_DESC(epsilon, AnyTraits())}}; +//输入属性映射,共3个,rho索引为6,momentum索引为7,epsilon索引为8 +ATTR_MAP(ApplyRMSPropD) = {{"use_locking", ATTR_DESC(use_locking, AnyTraits())}} +// 属性映射,有一个属性,"use_locking"类型为bool型; +OUTPUT_MAP(ApplyRMSPropD) = {{0, OUTPUT_DESC(var)}}; +// 输出映射,var的索引为0 +REG_ADPT_DESC(ApplyRMSPropD, kNameApplyRMSProp, ADPT_DESC(ApplyRMSPropD)) +// 注册ApplyRMSPropD操作的适配器描述 kNameApplyRMSProp + +// ApplyCenteredRMSProp +INPUT_MAP(ApplyCenteredRMSProp) = {{1, INPUT_DESC(var)}, {2, INPUT_DESC(mg)}, {3, INPUT_DESC(ms)}, + {4, INPUT_DESC(mom)}, {5, INPUT_DESC(grad)}, {6, INPUT_DESC(lr)}, + {7, INPUT_DESC(rho)}, {8, INPUT_DESC(momentum)}, {9, INPUT_DESC(epsilon)}}; +// 输入映射,有九个输入,var索引为1,mg索引为2,ms索引为3,mom索引为4,grad索引为5,lr索引为6,rho索引为7,momentum索引为8,epsilon索引为9 +ATTR_MAP(ApplyCenteredRMSProp) = {{"use_locking", ATTR_DESC(use_locking, AnyTraits())}}; +// 输入属性映射,rho索引为6,momentum索引为7,epsilon索引为8 +OUTPUT_MAP(ApplyCenteredRMSProp) = {{0, OUTPUT_DESC(var)}}; +// 输出映射,var的索引为0 +REG_ADPT_DESC(ApplyCenteredRMSProp, kNameApplyCenteredRMSProp, ADPT_DESC(ApplyCenteredRMSProp)) +// 注册ApplyCenteredRMSProp操作的适配器描述kNameApplyCenteredRMSProp +} // namespace mindspore::transform diff --git a/mindspore/ccsrc/transform-update/nonlinear_fuc_ops_declare.cc b/mindspore/ccsrc/transform-update/nonlinear_fuc_ops_declare.cc new file mode 100644 index 00000000000..eb95dfc5e58 --- /dev/null +++ b/mindspore/ccsrc/transform-update/nonlinear_fuc_ops_declare.cc @@ -0,0 +1,290 @@ +/** + * Copyright 2019-2021 Huawei Technologies Co., Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "transform/graph_ir/op_declare/nonlinear_fuc_ops_declare.h" + +namespace mindspore::transform { +// Relu +INPUT_MAP(Relu) = {{1, INPUT_DESC(x)}}; +//输入映射,x的索引为1 +ATTR_MAP(Relu) = EMPTY_ATTR_MAP; +//属性映射,空 +OUTPUT_MAP(Relu) = {{0, OUTPUT_DESC(y)}}; +//输出映射,y的索引为0 +REG_ADPT_DESC(Relu, prim::kPrimRelu->name(), ADPT_DESC(Relu)) +// 注册Relu操作的适配器描述prim::kPrimRelu->name() + +// ReluV2 +INPUT_MAP(ReluV2) = {{1, INPUT_DESC(x)}}; +// 输入映射,x的索引为1 +ATTR_MAP(ReluV2) = EMPTY_ATTR_MAP; +// 属性映射,空 +OUTPUT_MAP(ReluV2) = {{0, OUTPUT_DESC(y)}, {1, OUTPUT_DESC(mask)}}; +// 输出映射,y的索引为0,mask索引为1 +REG_ADPT_DESC(ReluV2, kNameReluV2, ADPT_DESC(ReluV2)) +// 注册ReluV2操作的适配器描述kNameReluV2 + +// Elu +INPUT_MAP(Elu) = {{1, INPUT_DESC(x)}}; +// 输入映射,x的索引为1 +ATTR_MAP(Elu) = {{"alpha", ATTR_DESC(alpha, AnyTraits())}}; +// 属性映射,属性"alpha"的类型为float +OUTPUT_MAP(Elu) = {{0, OUTPUT_DESC(y)}}; +// 输出映射,y的索引为0 +REG_ADPT_DESC(Elu, kNameElu, ADPT_DESC(Elu)) +// 注册Elu操作的适配器描述kNameElu + +// EluGrad +INPUT_MAP(EluGrad) = {{1, INPUT_DESC(grads)}, {2, INPUT_DESC(activations)}}; +// 输入映射,有两个,x的索引为1,activations索引为2 +ATTR_MAP(EluGrad) = EMPTY_ATTR_MAP; +// 属性映射,空 +OUTPUT_MAP(EluGrad) = {{0, OUTPUT_DESC(y)}}; +// 输出映射,y的索引为0 +REG_ADPT_DESC(EluGrad, kNameEluGrad, ADPT_DESC(EluGrad)) +// 注册EluGrad操作的适配器描述kNameEluGrad + +// PRelu +INPUT_MAP(PRelu) = {{1, INPUT_DESC(x)}, {2, INPUT_DESC(weight)}}; +// 输入映射,有两个,x的索引为1,weight索引为2 +ATTR_MAP(PRelu) = EMPTY_ATTR_MAP; +// 属性映射,空 +OUTPUT_MAP(PRelu) = {{0, OUTPUT_DESC(y)}}; +// 输出映射,y的索引为0 +REG_ADPT_DESC(PRelu, kNamePrelu, ADPT_DESC(PRelu)) +// 注册PRelu操作的适配器描述kNamePrelu + +// PReluGrad +INPUT_MAP(PReluGrad) = {{1, INPUT_DESC(grads)}, {2, INPUT_DESC(features)}, {3, INPUT_DESC(weights)}}; +// 输入映射,有三个,grads的索引为1,features索引为2,weights索引为3 +ATTR_MAP(PReluGrad) = EMPTY_ATTR_MAP; +// 属性映射,空 +OUTPUT_MAP(PReluGrad) = {{0, OUTPUT_DESC(dx)}, {1, OUTPUT_DESC(da)}}; +// 输出映射,dx的索引为0,da索引为1 +REG_ADPT_DESC(PReluGrad, kNamePreluGrad, ADPT_DESC(PReluGrad)) +// 注册PReluGrad操作的适配器描述kNamePreluGrad + +// Selu +INPUT_MAP(Selu) = {{1, INPUT_DESC(x)}}; +// 输入映射,x的索引为1 +ATTR_MAP(Selu) = EMPTY_ATTR_MAP; +// 属性映射,空 +OUTPUT_MAP(Selu) = {{0, OUTPUT_DESC(y)}}; +// 输出映射,y的索引为0 +REG_ADPT_DESC(Selu, kNameSelu, ADPT_DESC(Selu)) +// 注册Selu操作的适配器描述kNameSelu + +// Sigmoid +INPUT_MAP(Sigmoid) = {{1, INPUT_DESC(x)}}; +// 输入映射,x的索引为1 +ATTR_MAP(Sigmoid) = EMPTY_ATTR_MAP; +// 属性映射,空 +OUTPUT_MAP(Sigmoid) = {{0, OUTPUT_DESC(y)}}; +// 输出映射,y的索引为0 +REG_ADPT_DESC(Sigmoid, kNameSigmoid, ADPT_DESC(Sigmoid)) +// 注册Sigmoid操作的适配器描述kNameSigmoid + +// SigmoidGrad +INPUT_MAP(SigmoidGrad) = {{1, INPUT_DESC(y)}, {2, INPUT_DESC(dy)}}; +// 输入映射,有两个,y的索引为1,dy索引为2 +ATTR_MAP(SigmoidGrad) = EMPTY_ATTR_MAP; +// 属性映射,空 +OUTPUT_MAP(SigmoidGrad) = {{0, OUTPUT_DESC(z)}}; +// 输出映射,y的索引为0 +REG_ADPT_DESC(SigmoidGrad, kNameSigmoidGrad, ADPT_DESC(SigmoidGrad)) +// 注册SigmoidGrad操作的适配器描述kNameSigmoidGrad + +// HardSwish +INPUT_MAP(HardSwish) = {{1, INPUT_DESC(x)}}; +// 输入映射,x的索引为1 +ATTR_MAP(HardSwish) = EMPTY_ATTR_MAP; +// 属性映射,空 +OUTPUT_MAP(HardSwish) = {{0, OUTPUT_DESC(y)}}; +// 输出映射,y的索引为0 +REG_ADPT_DESC(HardSwish, kNameHSwish, ADPT_DESC(HardSwish)) +// 注册HardSwish操作的适配器描述kNameHSwish + +// HardSwishGrad +INPUT_MAP(HardSwishGrad) = {{1, INPUT_DESC(grad)}, {2, INPUT_DESC(x)}}; +// 输入映射,有两个,grad的索引为1,x索引为2 +ATTR_MAP(HardSwishGrad) = EMPTY_ATTR_MAP; +// 属性映射,空 +OUTPUT_MAP(HardSwishGrad) = {{0, OUTPUT_DESC(y)}}; +// 输出映射,y的索引为0 +REG_ADPT_DESC(HardSwishGrad, kNameHSwishGrad, ADPT_DESC(HardSwishGrad)) +// 注册HardSwishGrad操作的适配器描述kNameHSwishGrad + +// HSigmoid +INPUT_MAP(HardSigmoid) = {{1, INPUT_DESC(input_x)}}; +// 输入映射,input_x的索引为1 +ATTR_MAP(HardSigmoid) = {{"alpha", ATTR_DESC(alpha, AnyTraits())}, + {"beta", ATTR_DESC(beta, AnyTraits())}}; +// 属性映射,属性"alpha""beta"的类型为float +OUTPUT_MAP(HardSigmoid) = {{0, OUTPUT_DESC(output_y)}}; +// 输出映射,y的索引为0 +REG_ADPT_DESC(HardSigmoid, kNameHSigmoid, ADPT_DESC(HardSigmoid)) +// 注册HardSigmoid操作的适配器描述kNameHSigmoid + +// Relu6 +INPUT_MAP(Relu6) = {{1, INPUT_DESC(x)}}; +// 输入映射,x的索引为1 +ATTR_MAP(Relu6) = EMPTY_ATTR_MAP; +// 属性映射,空 +OUTPUT_MAP(Relu6) = {{0, OUTPUT_DESC(y)}}; +// 输出映射,y的索引为0 +REG_ADPT_DESC(Relu6, kNameReLU6, ADPT_DESC(Relu6)) +// 注册Relu6操作的适配器描述kNameReLU6 + +// Relu6Grad +INPUT_MAP(Relu6Grad) = {{1, INPUT_DESC(gradients)}, {2, INPUT_DESC(features)}}; +// 输入映射,有两个,gradients的索引为1,features索引为2 +ATTR_MAP(Relu6Grad) = EMPTY_ATTR_MAP; +// 属性映射,空 +OUTPUT_MAP(Relu6Grad) = {{0, OUTPUT_DESC(backprops)}}; +// 输出映射,backprops的索引为0 +REG_ADPT_DESC(Relu6Grad, kNameReLU6Grad, ADPT_DESC(Relu6Grad)) +// 注册Relu6Grad操作的适配器描述kNameReLU6Grad + +// Softsign +INPUT_MAP(Softsign) = {{1, INPUT_DESC(x)}}; +// 输入映射,x的索引为1 +ATTR_MAP(Softsign) = EMPTY_ATTR_MAP; +// 属性映射,空 +OUTPUT_MAP(Softsign) = {{0, OUTPUT_DESC(y)}}; +// 输出映射,y的索引为0 +REG_ADPT_DESC(Softsign, kNameSoftsign, ADPT_DESC(Softsign)) +// 注册Softsign操作的适配器描述kNameSoftsign + +// Softplus +INPUT_MAP(Softplus) = {{1, INPUT_DESC(x)}}; +// 输入映射,x的索引为1 +ATTR_MAP(Softplus) = EMPTY_ATTR_MAP; +// 属性映射,空 +OUTPUT_MAP(Softplus) = {{0, OUTPUT_DESC(y)}}; +// 输出映射,y的索引为0 +REG_ADPT_DESC(Softplus, kNameSoftplus, ADPT_DESC(Softplus)) +// 注册Softplus操作的适配器描述 kNameSoftplus + +// SoftplusGrad +INPUT_MAP(SoftplusGrad) = {{1, INPUT_DESC(gradients)}, {2, INPUT_DESC(features)}}; +// 输入映射,有两个,gradients的索引为1,features索引为2 +ATTR_MAP(SoftplusGrad) = EMPTY_ATTR_MAP; +// 属性映射,空 +OUTPUT_MAP(SoftplusGrad) = {{0, OUTPUT_DESC(backprops)}}; +// 输出映射,backprops的索引为0 +REG_ADPT_DESC(SoftplusGrad, kNameSoftplusGrad, ADPT_DESC(SoftplusGrad)) +// 注册SoftplusGrad操作的适配器描述kNameSoftplusGrad + +// ReluGrad +INPUT_MAP(ReluGrad) = {{1, INPUT_DESC(gradients)}, {2, INPUT_DESC(features)}}; +// 输入映射,有两个,gradients的索引为1,features索引为2 +ATTR_MAP(ReluGrad) = EMPTY_ATTR_MAP; +// 属性映射,空 +OUTPUT_MAP(ReluGrad) = {{0, OUTPUT_DESC(backprops)}}; +// 输出映射,backprops的索引为0 +REG_ADPT_DESC(ReluGrad, prim::kPrimReluGrad->name(), ADPT_DESC(ReluGrad)) +// 注册ReluGrad操作的适配器描述prim::kPrimReluGrad->name() + +// ReluGradV2 +INPUT_MAP(ReluGradV2) = {{1, INPUT_DESC(gradients)}, {2, INPUT_DESC(mask)}}; +// 输入映射,有两个,gradients的索引为1,mask索引为2 +ATTR_MAP(ReluGradV2) = EMPTY_ATTR_MAP; +// 属性映射,空 +OUTPUT_MAP(ReluGradV2) = {{0, OUTPUT_DESC(backprops)}}; +// 输出映射,backprops的索引为0 +REG_ADPT_DESC(ReluGradV2, kNameReluGradV2, ADPT_DESC(ReluGradV2)) +// 注册ReluGradV2操作的适配器描述kNameReluGradV2 + +// Tanh +INPUT_MAP(Tanh) = {{1, INPUT_DESC(x)}}; +// 输入映射,x的索引为1 +ATTR_MAP(Tanh) = EMPTY_ATTR_MAP; +// 属性映射,空 +OUTPUT_MAP(Tanh) = {{0, OUTPUT_DESC(y)}}; +// 输出映射,y的索引为0 +REG_ADPT_DESC(Tanh, prim::kPrimTanh->name(), ADPT_DESC(Tanh)) +// 注册Tanh操作的适配器描述prim::kPrimTanh->name() + +// TanhGrad +INPUT_MAP(TanhGrad) = {{1, INPUT_DESC(y)}, {2, INPUT_DESC(dy)}}; +// 输入映射,有两个,y的索引为1,dy索引为2 +ATTR_MAP(TanhGrad) = EMPTY_ATTR_MAP; +// 属性映射,空 +OUTPUT_MAP(TanhGrad) = {{0, OUTPUT_DESC(z)}}; +// 输出映射,z的索引为0 +REG_ADPT_DESC(TanhGrad, prim::kPrimTanhGrad->name(), ADPT_DESC(TanhGrad)) +// 注册NPUTanhGrad操作的适配器描述prim::kPrimTanhGrad->name() + +// Mish +INPUT_MAP(Mish) = {{1, INPUT_DESC(x)}}; +// 输入映射,x的索引为1 +ATTR_MAP(Mish) = EMPTY_ATTR_MAP; +// 属性映射,空 +OUTPUT_MAP(Mish) = {{0, OUTPUT_DESC(y)}}; +// 输出映射,y的索引为0 +REG_ADPT_DESC(Mish, kNameMish, ADPT_DESC(Mish)) +// 注册Mish操作的适配器描述kNameMish + +// GeLU +INPUT_MAP(Gelu) = {{1, INPUT_DESC(x)}}; +// 输入映射,x的索引为1 +ATTR_MAP(Gelu) = EMPTY_ATTR_MAP; +// 属性映射,空 +OUTPUT_MAP(Gelu) = {{0, OUTPUT_DESC(y)}}; +// 输出映射,y的索引为0 +REG_ADPT_DESC(Gelu, prim::kPrimGeLU->name(), ADPT_DESC(Gelu)) +// 注册Gelu操作的适配器描述prim::kPrimGeLU->name() + +// GeLUGrad +INPUT_MAP(GeluGrad) = {{1, INPUT_DESC(dy)}, {2, INPUT_DESC(x)}, {3, INPUT_DESC(y)}}; +// 输入映射,有三个,x的索引为1,activations索引为2,y索引为3 +ATTR_MAP(GeluGrad) = EMPTY_ATTR_MAP; +// 属性映射,空 +OUTPUT_MAP(GeluGrad) = {{0, OUTPUT_DESC(z)}}; +// 输出映射,z的索引为0 +REG_ADPT_DESC(GeluGrad, prim::kPrimGeLUGrad->name(), ADPT_DESC(GeluGrad)) +// 注册GeluGrad操作的适配器描述prim::kPrimGeLUGrad->name() + +// FastGeLU +INPUT_MAP(FastGelu) = {{1, INPUT_DESC(x)}}; +// 输入映射,x的索引为1 +ATTR_MAP(FastGelu) = EMPTY_ATTR_MAP; +// 属性映射,空 +OUTPUT_MAP(FastGelu) = {{0, OUTPUT_DESC(y)}}; +// 输出映射,y的索引为0 +REG_ADPT_DESC(FastGelu, prim::kPrimFastGeLU->name(), ADPT_DESC(FastGelu)) +// 注册FastGelu操作的适配器描述 prim::kPrimFastGeLU->name() + +// FastGeLUGrad +INPUT_MAP(FastGeluGrad) = {{1, INPUT_DESC(dy)}, {2, INPUT_DESC(x)}}; +// 输入映射,有两个,dy的索引为1,x索引为2 +ATTR_MAP(FastGeluGrad) = EMPTY_ATTR_MAP; +// 属性映射,空 +OUTPUT_MAP(FastGeluGrad) = {{0, OUTPUT_DESC(z)}}; +// 输出映射,z的索引为0 +REG_ADPT_DESC(FastGeluGrad, prim::kPrimFastGeLUGrad->name(), ADPT_DESC(FastGeluGrad)) +// 注册FastGeluGrad操作的适配器描述 prim::kPrimFastGeLUGrad->name() + +// LeakyRelu +INPUT_MAP(LeakyRelu) = {{1, INPUT_DESC(x)}}; +// 输入映射,x的索引为1 +ATTR_MAP(LeakyRelu) = {{"alpha", ATTR_DESC(negative_slope, AnyTraits())}}; +// 属性映射,属性"alpha"的类型为float +OUTPUT_MAP(LeakyRelu) = {{0, OUTPUT_DESC(y)}}; +// 输出映射,y的索引为0 +REG_ADPT_DESC(LeakyRelu, prim::kPrimLeakyRelu->name(), ADPT_DESC(LeakyRelu)) +// 注册LeakyRelu操作的适配器描述 prim::kPrimLeakyRelu->name() +} // namespace mindspore::transform diff --git a/mindspore/ccsrc/transform-update/npu_loss_scale_ops_declare.cc b/mindspore/ccsrc/transform-update/npu_loss_scale_ops_declare.cc new file mode 100644 index 00000000000..5ff7ccd9d66 --- /dev/null +++ b/mindspore/ccsrc/transform-update/npu_loss_scale_ops_declare.cc @@ -0,0 +1,49 @@ +/** + * Copyright 2019 Huawei Technologies Co., Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "transform/graph_ir/op_declare/npu_loss_scale_ops_declare.h" + +namespace mindspore::transform { +// NPUGetFloatStatus +INPUT_MAP(NPUGetFloatStatus) = {{1, INPUT_DESC(addr)}}; +// 输入映射,addr索引为1 +OUTPUT_MAP(NPUGetFloatStatus) = {{0, OUTPUT_DESC(data)}}; +// 输出映射,data索引为0 +ATTR_MAP(NPUGetFloatStatus) = EMPTY_ATTR_MAP; +//属性映射,空 +REG_ADPT_DESC(NPUGetFloatStatus, kNameNPUGetFloatStatus, ADPT_DESC(NPUGetFloatStatus)) +// 注册NPUGetFloatStatus操作的适配器描述kNameNPUGetFloatStatus + +// NPUAllocFloatStatus +INPUT_MAP(NPUAllocFloatStatus) = EMPTY_INPUT_MAP; +// 输入映射,空 +ATTR_MAP(NPUAllocFloatStatus) = EMPTY_ATTR_MAP; +// 属性映射,空 +OUTPUT_MAP(NPUAllocFloatStatus) = {{0, OUTPUT_DESC(data)}}; +// 输出映射,data索引为0 +REG_ADPT_DESC(NPUAllocFloatStatus, kNameNPUAllocFloatStatus, ADPT_DESC(NPUAllocFloatStatus)) +// 注册NPUAllocFloatStatus操作的适配器描述kNameNPUAllocFloatStatus + +// NPUClearFloatStatus +INPUT_MAP(NPUClearFloatStatus) = {{1, INPUT_DESC(addr)}}; +// 输入映射,addr索引为1 +OUTPUT_MAP(NPUClearFloatStatus) = {{0, OUTPUT_DESC(data)}}; +// 输出映射,data索引为0 +ATTR_MAP(NPUClearFloatStatus) = EMPTY_ATTR_MAP; +//属性映射,空 +REG_ADPT_DESC(NPUClearFloatStatus, kNameNPUClearFloatStatus, ADPT_DESC(NPUClearFloatStatus)) +// 注册NPUClearFloatStatus操作的适配器描述kNameNPUClearFloatStatus +} // namespace mindspore::transform diff --git a/mindspore/ccsrc/transform-update/onnx_exporter.cc b/mindspore/ccsrc/transform-update/onnx_exporter.cc new file mode 100644 index 00000000000..0d521a8ab5b --- /dev/null +++ b/mindspore/ccsrc/transform-update/onnx_exporter.cc @@ -0,0 +1,3762 @@ +/** + * Copyright 2020-2022 Huawei Technologies Co., Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include + +#include "mindspore/core/ops/core_ops.h" +#include "ir/func_graph.h" +#include "ir/param_info.h" +#include "ir/tensor.h" +#include "proto/onnx.pb.h" +#include "utils/check_convert_utils.h" +#include "utils/hash_map.h" +#include "utils/ms_context.h" + +namespace mindspore { +const int ONNX_VERSION = 11; +const int kZeroNum = 0; +const int kOneNum = 1; +const int kTwoNum = 2; +const int kThreeNum = 3; +const int kFourNum = 4; +const int kFiveNum = 5; +const int64_t kOneNumLong = 1; +const float weight_for_mul = 0.5; +enum OpMergeMode { + OP_MERGE_UNDEFINED = 0, // undefined behavior + OP_MERGE_IGNORE = 1, // indicate an input op merged into other op in compute node list + OP_MERGE_CONV = 2, // indicate `MindSpore Conv + BiasAdd` --> `ONNX Conv` + OP_MERGE_GEMM = 3, // indicate `MindSpore MatMul + BiasAdd` --> `ONNX Gemm` + OP_MERGE_BATCH_NORM = 4, // indicate `MindSpore BatchNorm(x)[0]` --> `ONNX Batch Normalization` + OP_MERGE_MAXPOOL_WITH_ARGMAX = 5, // indicate `MindSpore MaxPoolWithArgmax(x)[0]` --> `ONNX MaxPool` + OP_MERGE_LAYER_NORM = 6, // indicate `MindSpore LayerNorm(x)[0]` --> `ONNX MeanVarianceNormalization` + OP_MERGE_CONV2D_TRANSPOSE = 7, // indicate `MindSpore ConvTranspose + BiasAdd` --> `ONNX ConvTranspose` +}; + +struct OpMergedInfo { + OpMergeMode mode = OP_MERGE_UNDEFINED; + int referred_count = 0; +}; + +using GenAttrFuncType = + std::function; + +bool IsIgnoredIdentityNode(const AnfNodePtr &node) { + return IsPrimitiveCNode(node, prim::kPrimDepend) || IsPrimitiveCNode(node, prim::kPrimLoad); +} + +/* + If true, the node should not be referenced by anything and should not be contributing to any + ref counts itself + 濡傛灉杩斿洖鍊间负true锛屽垯璇ヨ妭鐐逛笉搴旇浠讳綍瀵硅薄寮曠敤锛屼篃涓嶅簲鍙備笌浠讳綍ref鑷韩璁℃暟 + */ +bool IsZeroRefcountNode(const AnfNodePtr &node) { return HasAbstractMonad(node) || IsIgnoredIdentityNode(node); } + +// Ideally this should be applied to every node->input() call, not only inside GetNodeInputName +//杩欏簲璇ュ簲鐢ㄤ簬姣忎釜node->input锛堬級璋冪敤 +static AnfNodePtr GetRealInput(const AnfNodePtr &origin_input) { + AnfNodePtr input = origin_input; + while (IsIgnoredIdentityNode(input)) { + input = input->cast()->inputs().at(1); + } + return input; +} + +template +void SetAttrValueToProto(const ValuePtr &value, onnx::AttributeProto_AttributeType attr_type, + onnx::AttributeProto *const attr_proto, const PrimitivePtr &) { + auto casted_value = dyn_cast(value); + if (casted_value == nullptr) { + MS_LOG(EXCEPTION) << "Cast value " << value->ToString() << " to type T failed."; + } + auto attr_value = casted_value->value(); + switch (attr_type) { + case onnx::AttributeProto_AttributeType_INT: + attr_proto->set_i(static_cast<::google::protobuf::int64>(attr_value)); + break; + case onnx::AttributeProto_AttributeType_FLOAT: + attr_proto->set_f(static_cast(attr_value)); + break; + case onnx::AttributeProto_AttributeType_INTS: + for (size_t i = 0; i < rep_cnt; ++i) { + attr_proto->add_ints(static_cast<::google::protobuf::int64>(attr_value)); + } + break; + case onnx::AttributeProto_AttributeType_FLOATS: + for (size_t i = 0; i < rep_cnt; ++i) { + attr_proto->add_floats(static_cast(attr_value)); + } + break; + default: + MS_LOG(EXCEPTION) << "Convert attribute fail, unexpected ONNX type " << attr_type; + } + attr_proto->set_type(attr_type); +} +//璁剧疆proto鍙傛暟 +template +void SetAttrTupleValueToProto(const ValuePtr &value, onnx::AttributeProto_AttributeType attr_type, + onnx::AttributeProto *const attr_proto, const PrimitivePtr &) { + auto tuple_ptr = dyn_cast(value); + if (tuple_ptr == nullptr) { + MS_LOG(EXCEPTION) << "Cast value from type " << value->type_name() << " to ValueTuple failed."; + } + switch (attr_type) { + case onnx::AttributeProto_AttributeType_INTS: + for (size_t i = beg_idx; i < tuple_ptr->size(); ++i) { + attr_proto->add_ints(GetValue((*tuple_ptr)[i])); + } + break; + case onnx::AttributeProto_AttributeType_INT: + attr_proto->set_i(GetValue((*tuple_ptr)[beg_idx])); + break; + case onnx::AttributeProto_AttributeType_FLOATS: + for (size_t i = beg_idx; i < tuple_ptr->size(); ++i) { + attr_proto->add_floats(GetValue((*tuple_ptr)[i])); + } + break; + default: + MS_LOG(EXCEPTION) << "Convert attribute fail, unexpected ONNX type " << attr_type; + } + attr_proto->set_type(attr_type); +} +//璁剧疆proto鍙傛暟 +void SetPoolingPadMode(const ValuePtr &value, onnx::AttributeProto_AttributeType, + onnx::AttributeProto *const attr_proto, const PrimitivePtr &) { + attr_proto->set_type(onnx::AttributeProto_AttributeType_STRING); + int64_t attr_value; + CheckAndConvertUtils::GetPadModEnumValue(value, &attr_value, true); + if (attr_value == PadMode::VALID) { + attr_proto->set_s("VALID"); + } else { + attr_proto->set_s("SAME_UPPER"); + } +} +//璁剧疆姹犲寲妯″瀷 +void SetConvPadding(const ValuePtr &value, onnx::AttributeProto_AttributeType, onnx::AttributeProto *const attr_proto, + const PrimitivePtr &prim) { + attr_proto->set_type(onnx::AttributeProto_AttributeType_STRING); + int64_t attr_value; + CheckAndConvertUtils::GetPadModEnumValue(value, &attr_value); + if (attr_value == PadMode::VALID) { + attr_proto->set_s("VALID"); + } else if (attr_value == PadMode::SAME) { + attr_proto->set_s("SAME_UPPER"); + } else { // pad_mode is 'pad', use attribute 'pad_list' to fill ONNX attribute 'pads' + attr_proto->set_name("pads"); + SetAttrTupleValueToProto(prim->GetAttr("pad_list"), onnx::AttributeProto_AttributeType_INTS, attr_proto, prim); + } +} + +void SetConvTransposePadding(const ValuePtr &value, onnx::AttributeProto_AttributeType, + onnx::AttributeProto *const attr_proto, const PrimitivePtr &prim) { + attr_proto->set_type(onnx::AttributeProto_AttributeType_STRING); + int64_t attr_value; + CheckAndConvertUtils::GetPadModEnumValue(value, &attr_value); + if (attr_value == PadMode::VALID) { + attr_proto->set_s("VALID"); + } else if (attr_value == PadMode::SAME) { + attr_proto->set_s("SAME_LOWER"); + } else { // pad_mode is 'pad', use attribute 'pad_list' to fill ONNX attribute 'pads' + attr_proto->set_name("pads"); + SetAttrTupleValueToProto(prim->GetAttr("pad_list"), onnx::AttributeProto_AttributeType_INTS, attr_proto, prim); + } +} + +PrimitivePtr GetPrimitive(const CNodePtr &node) { + AnfNodePtr op = node->input(kZeroNum); + auto op_value = dyn_cast(op); + MS_EXCEPTION_IF_NULL(op_value); + auto prim = dyn_cast(op_value->value()); + MS_EXCEPTION_IF_NULL(prim); + return prim; +} + +template +T GetOpAttribute(const CNodePtr &node, const std::string &name) { + ValuePtr attr = GetPrimitive(node)->GetAttr(name); + return GetValue(attr); +} + +template +std::shared_ptr GetOpAttributePtr(const CNodePtr &node, const std::string &name) { + ValuePtr attr = GetPrimitive(node)->GetAttr(name); + auto result = dyn_cast(attr); + MS_EXCEPTION_IF_NULL(result); + return result; +} + +std::string MakeOutputName(const std::string &node_name, int output_index) { + return node_name + "_" + std::to_string(output_index); +} + +int64_t RavelIndex(const std::vector &index, const std::vector &shape) { + MS_EXCEPTION_IF_CHECK_FAIL(index.size() <= shape.size(), "Index ndims must be <= shape ndims"); + int64_t result = 0; + int64_t stride = 1; + for (size_t i = 0; i < shape.size() - index.size(); ++i) { + stride *= shape[shape.size() - 1 - i]; + } + for (size_t i = 0; i < index.size(); ++i) { + size_t rev_i = index.size() - 1 - i; + result += index[rev_i] * stride; + stride *= shape[rev_i]; + } + return result; +} + +namespace fp16 { +uint32_t FieldMask(unsigned int field_size) { + const unsigned int BYTE_SIZE = 8; + uint32_t mask = std::numeric_limits::max(); + return mask >> (BYTE_SIZE * sizeof(mask) - field_size); +} + +uint32_t ExponentBias(unsigned int exponent_size) { return (1U << (exponent_size - 1U)) - 1U; } + +uint32_t Fp32ToFp16(float value) { + const unsigned int FP32_M = 23; + const unsigned int FP32_E = 32 - 1 - FP32_M; + const unsigned int FP16_M = 10; + const unsigned int FP16_E = 16 - 1 - FP16_M; + + uint32_t fp32_bits; + auto ret = memcpy_s(reinterpret_cast(&fp32_bits), sizeof(fp32_bits), + reinterpret_cast(&value), sizeof(value)); + if (ret != 0) { + MS_LOG(ERROR) << "Set data memcpy_s failed, ret = " << ret; + } + + uint32_t mantissa = fp32_bits & FieldMask(FP32_M); + uint32_t fp32_exp_mask = FieldMask(FP32_E); + uint32_t fp32_exponent = (fp32_bits >> FP32_M) & fp32_exp_mask; + if (fp32_exponent == fp32_exp_mask) { + MS_LOG(EXCEPTION) << "Tried to convert inf or nan to float16: " << value; + } + uint32_t sign = fp32_bits >> (FP32_E + FP32_M); + + uint32_t fp16_bits = 0; + fp16_bits |= sign << (FP16_E + FP16_M); + uint32_t fp16_exponent = 0; + if (fp32_exponent != 0) { + fp16_exponent = fp32_exponent - ExponentBias(FP32_E) + ExponentBias(FP16_E); + } + if (fp16_exponent >= FieldMask(FP16_E)) { // inf, nan (==), underflow, or overflow (>) + MS_LOG(EXCEPTION) << "Conversion of " << value << " to float16 resulted in exponent overflow or underflow"; + } + fp16_bits |= fp16_exponent << FP16_M; + fp16_bits |= mantissa >> (FP32_M - FP16_M); + + return fp16_bits; +} +} // namespace fp16 + +void AddFloatScalarInitializer(const std::string &name, float value, onnx::TensorProto_DataType type, + onnx::GraphProto *graph_proto) { + onnx::TensorProto *initializer = graph_proto->add_initializer(); + initializer->set_name(name); + if (type == onnx::TensorProto_DataType_FLOAT16) { + uint32_t fp16 = fp16::Fp32ToFp16(value); + initializer->add_int32_data(static_cast(fp16)); + } else if (type == onnx::TensorProto_DataType_FLOAT) { + initializer->add_float_data(value); + } else { + MS_LOG(EXCEPTION) << "Unsupported type: " << type; + } + initializer->set_data_type(type); +} + +void AddInt64Tensor1DInitializer(const std::string &name, const std::vector &values, + onnx::GraphProto *graph_proto) { + onnx::TensorProto *initializer = graph_proto->add_initializer(); + initializer->set_name(name); + initializer->set_data_type(onnx::TensorProto_DataType_INT64); + initializer->add_dims(values.size()); + for (auto value : values) { + initializer->add_int64_data(value); + } +} + +void AddFloatTensor1DInitializer(const std::string &name, const std::vector &values, + onnx::TensorProto_DataType type, onnx::GraphProto *graph_proto) { + onnx::TensorProto *initializer = graph_proto->add_initializer(); + initializer->set_name(name); + initializer->add_dims(values.size()); + if (type == onnx::TensorProto_DataType_FLOAT16) { + for (auto value : values) { + uint32_t fp16 = fp16::Fp32ToFp16(value); + initializer->add_int32_data(static_cast(fp16)); + } + } else if (type == onnx::TensorProto_DataType_FLOAT) { + for (auto value : values) { + initializer->add_float_data(value); + } + } else { + MS_LOG(EXCEPTION) << "Unsupported type: " << type; + } + initializer->set_data_type(type); +} + +void AddOp(const std::string &type, const std::vector &inputs, const std::vector &outputs, + onnx::GraphProto *graph_proto) { + onnx::NodeProto *op = graph_proto->add_node(); + op->set_op_type(type); + op->set_name(outputs.at(0) + type); + for (const auto &input : inputs) { + op->add_input(input); + } + for (const auto &output : outputs) { + op->add_output(output); + } +} + +void AddClipOp(const std::string &input, const std::string &output, float min, float max, + onnx::TensorProto_DataType type, onnx::GraphProto *graph_proto) { + auto min_input_name = output + "__min_initializer"; + AddFloatScalarInitializer(min_input_name, min, type, graph_proto); + + auto max_input_name = output + "__max_initializer"; + AddFloatScalarInitializer(max_input_name, max, type, graph_proto); + + AddOp("Clip", {input, min_input_name, max_input_name}, {output}, graph_proto); +} + +void AddSliceOp(const std::string &input, const std::string &output, const std::vector &start, + const std::vector &end, const std::vector &axis, const std::vector &step, + onnx::GraphProto *graph_proto) { + auto starts_name = output + "__starts_initializer"; + AddInt64Tensor1DInitializer(starts_name, start, graph_proto); + + auto ends_name = output + "__ends_initializer"; + AddInt64Tensor1DInitializer(ends_name, end, graph_proto); + + auto axes_name = output + "__axes_initializer"; + AddInt64Tensor1DInitializer(axes_name, axis, graph_proto); + + auto steps_name = output + "__steps_initializer"; + AddInt64Tensor1DInitializer(steps_name, step, graph_proto); + + AddOp("Slice", {input, starts_name, ends_name, axes_name, steps_name}, {output}, graph_proto); +} + +void AddSplitOp(const std::string &input, const std::vector &outputs, const std::vector &split, + int64_t axis, onnx::GraphProto *graph_proto) { + if (outputs.size() != split.size()) { + MS_LOG(EXCEPTION) << "Number of splits and number of outputs do not match"; + } + + onnx::NodeProto *split_proto = graph_proto->add_node(); + std::string op_type = "Split"; + split_proto->set_op_type(op_type); + split_proto->set_name(outputs.at(0) + op_type); + split_proto->add_input(input); + for (const auto &output : outputs) { + split_proto->add_output(output); + } + onnx::AttributeProto *axis_attr_proto = split_proto->add_attribute(); + axis_attr_proto->set_name("axis"); + axis_attr_proto->set_type(onnx::AttributeProto_AttributeType_INT); + axis_attr_proto->set_i(axis); + onnx::AttributeProto *split_attr_proto = split_proto->add_attribute(); + split_attr_proto->set_name("split"); + split_attr_proto->set_type(onnx::AttributeProto_AttributeType_INTS); + for (int64_t n : split) { + split_attr_proto->add_ints(n); + } +} + +void AddReshapeOp(const std::string &input, const std::string &output, const std::vector &shape, + onnx::GraphProto *graph_proto) { + auto shape_name = output + "__shape_initializer"; + AddInt64Tensor1DInitializer(shape_name, shape, graph_proto); + AddOp("Reshape", {input, shape_name}, {output}, graph_proto); +} + +onnx::TensorProto *AddConstantOfShapeOp(const std::string &shape, const std::string &output, + onnx::GraphProto *graph_proto) { + onnx::NodeProto *op = graph_proto->add_node(); + std::string op_type = "ConstantOfShape"; + op->set_op_type(op_type); + op->set_name(output + op_type); + op->add_input(shape); + op->add_output(output); + onnx::AttributeProto *value_attr = op->add_attribute(); + value_attr->set_name("value"); + value_attr->set_type(onnx::AttributeProto_AttributeType_TENSOR); + onnx::TensorProto *value_proto = value_attr->mutable_t(); + value_proto->add_dims(1); + return value_proto; +} + +void AddCastOp(const std::string &input, const std::string &output, onnx::TensorProto_DataType target_type, + onnx::GraphProto *graph_proto) { + onnx::NodeProto *node_proto = graph_proto->add_node(); + std::string op_type = "Cast"; + node_proto->set_op_type(op_type); + node_proto->set_name(output + op_type); + node_proto->add_input(input); + node_proto->add_output(output); + + onnx::AttributeProto *target_type_attr = node_proto->add_attribute(); + target_type_attr->set_name("to"); + target_type_attr->set_type(onnx::AttributeProto_AttributeType_INT); + target_type_attr->set_i(target_type); +} + +void AddReduceOp(const std::string &op_type, const std::string &input, const std::string &output, + const std::vector &axes, bool keepdims, onnx::GraphProto *graph_proto) { + onnx::NodeProto *node_proto = graph_proto->add_node(); + node_proto->set_name(output + op_type); + node_proto->set_op_type(op_type); + node_proto->add_input(input); + node_proto->add_output(output); + + onnx::AttributeProto *keep_dims_proto = node_proto->add_attribute(); + keep_dims_proto->set_name("keepdims"); + keep_dims_proto->set_type(onnx::AttributeProto_AttributeType_INT); + keep_dims_proto->set_i(static_cast(keepdims)); + + onnx::AttributeProto *axes_proto = node_proto->add_attribute(); + axes_proto->set_name("axes"); + axes_proto->set_type(onnx::AttributeProto_AttributeType_INTS); + + for (auto axis : axes) { + axes_proto->add_ints(axis); + } +} + +void AddMeanVarianceNormalizationOp(const std::string &input, const std::string &gamma, const std::string &beta, + const std::string &output, const std::vector &axes, float epsilon, + const std::vector &input_shape, onnx::TensorProto_DataType input_type, + onnx::GraphProto *graph_proto) { + auto input_name = output + "_input"; + AddCastOp(input, input_name, onnx::TensorProto_DataType_FLOAT, graph_proto); + auto gamma_name = output + "_gamma"; + AddCastOp(gamma, gamma_name, onnx::TensorProto_DataType_FLOAT, graph_proto); + auto beta_name = output + "_beta"; + AddCastOp(beta, beta_name, onnx::TensorProto_DataType_FLOAT, graph_proto); + + // MeanVarianceNormalization is replaced with equivalent ops because it is not supported by CUDAExecutionProvider + auto meanvariancenormal_node_name = output + "_normalized"; + + auto mean_name = output + "_mean"; + AddReduceOp("ReduceMean", input_name, mean_name, axes, true, graph_proto); + auto centered_name = output + "_centered"; + AddOp("Sub", {input_name, mean_name}, {centered_name}, graph_proto); + + auto sqsum_name = output + "_sqsum"; + AddReduceOp("ReduceSumSquare", centered_name, sqsum_name, axes, true, graph_proto); + float reduce_size = std::accumulate(axes.begin(), axes.end(), 1.0f, + [&input_shape](auto acc, auto axis) { return acc * input_shape[axis]; }); + auto reduce_size_name = output + "_reduce_size"; + AddFloatScalarInitializer(reduce_size_name, reduce_size, onnx::TensorProto_DataType_FLOAT, graph_proto); + auto variance_name = output + "_variance"; + AddOp("Div", {sqsum_name, reduce_size_name}, {variance_name}, graph_proto); + + auto epsilon_name = output + "_epsilon"; + AddFloatScalarInitializer(epsilon_name, epsilon, onnx::TensorProto_DataType_FLOAT, graph_proto); + auto variance_with_epsilon_name = output + "_variance_with_epsilon"; + AddOp("Add", {variance_name, epsilon_name}, {variance_with_epsilon_name}, graph_proto); + auto std_name = output + "_std"; + AddOp("Sqrt", {variance_with_epsilon_name}, {std_name}, graph_proto); + + AddOp("Div", {centered_name, std_name}, {meanvariancenormal_node_name}, graph_proto); + + // Add mul and add node + auto mul_node_name = output + "_rescaled"; + AddOp("Mul", {meanvariancenormal_node_name, gamma_name}, {mul_node_name}, graph_proto); + + // add beta + auto add_node_name = output; + if (input_type == onnx::TensorProto_DataType_FLOAT16) { + add_node_name += "_shifted"; + } + AddOp("Add", {mul_node_name, beta_name}, {add_node_name}, graph_proto); + + if (input_type == onnx::TensorProto_DataType_FLOAT16) { + AddCastOp(add_node_name, output, onnx::TensorProto_DataType_FLOAT16, graph_proto); + } +} + +void AddConcatOp(const std::vector &inputs, const std::string &output, int axis, + onnx::GraphProto *graph_proto) { + onnx::NodeProto *concat_proto = graph_proto->add_node(); + auto op_type = "Concat"; + concat_proto->set_op_type(op_type); + concat_proto->set_name(output + op_type); + for (const auto &input : inputs) { + concat_proto->add_input(input); + } + concat_proto->add_output(output); + onnx::AttributeProto *axis_proto = concat_proto->add_attribute(); + axis_proto->set_name("axis"); + axis_proto->set_type(onnx::AttributeProto_AttributeType_INT); + axis_proto->set_i(axis); +} + +void ConvertBoxesToXywh(const std::string &startpoints, const std::string &endpoints, const std::string ¢erpoints, + const std::string &dimensions, onnx::TensorProto_DataType type, onnx::GraphProto *graph_proto) { + auto coord_sums_name = centerpoints + "__to_div"; + AddOp("Add", {startpoints, endpoints}, {coord_sums_name}, graph_proto); + auto two_name = centerpoints + "__two_initializer"; + AddFloatScalarInitializer(two_name, 2.0f, type, graph_proto); + AddOp("Div", {coord_sums_name, two_name}, {centerpoints}, graph_proto); + + auto coord_diffs_name = dimensions + "__to_add"; + AddOp("Sub", {endpoints, startpoints}, {coord_diffs_name}, graph_proto); + auto one_name = dimensions + "__one_initializer"; + AddFloatScalarInitializer(one_name, 1.0f, type, graph_proto); + AddOp("Add", {coord_diffs_name, one_name}, {dimensions}, graph_proto); +} + +void ConvertBoxesToXyxy(const std::string ¢erpoints, const std::string &dimensions, const std::string &startpoints, + const std::string &endpoints, onnx::TensorProto_DataType type, onnx::GraphProto *graph_proto) { + auto half_name = startpoints + "__half_initializer"; + AddFloatScalarInitializer(half_name, 0.5f, type, graph_proto); + + auto half_dim_name = startpoints + "__half_dim"; + auto half_dim_to_sub_name = startpoints + "__to_sub"; + AddOp("Mul", {dimensions, half_name}, {half_dim_to_sub_name}, graph_proto); + AddOp("Sub", {half_dim_to_sub_name, half_name}, {half_dim_name}, graph_proto); + + AddOp("Sub", {centerpoints, half_dim_name}, {startpoints}, graph_proto); + AddOp("Add", {centerpoints, half_dim_name}, {endpoints}, graph_proto); +} + +void ClipPointsComponent(const std::string &points, const std::string &clipped, float max, int64_t component_idx, + onnx::TensorProto_DataType type, onnx::GraphProto *graph_proto) { + auto res_to_clip_name = clipped + "__clip"; + AddSliceOp(points, res_to_clip_name, {component_idx}, {component_idx + 1}, {1}, {1}, graph_proto); + AddClipOp(res_to_clip_name, clipped, 0.0f, max, type, graph_proto); +} + +namespace while_loop_export { +namespace { +const char CONTROL_PATTERN[] = "\u21B5"; // 鈫 +const char LOOP_BODY_PATTERN[] = "\u21BB"; // 鈫 +const char AFTER_LOOP_PATTERN[] = "\u2193"; // 鈫 + +const size_t LOOP_BODY_INPUT = 2; +const size_t AFTER_LOOP_INPUT = 3; + +bool IsSubgraphNameCorrect(const FuncGraphPtr &func_graph, const std::string &part_pattern) { + auto name = func_graph->ToString(); + return name.find("construct") != std::string::npos && name.find(part_pattern) != std::string::npos; +} + +template +const std::shared_ptr GetNodeInput(const CNodePtr &node, size_t i) { + auto input = GetRealInput(node->input(i)); + auto result = dyn_cast(input); + if (result == nullptr) { + MS_LOG(EXCEPTION) << "Failed to get input " << i << " of node " << node->DebugString(); + } + return result; +} + +template +const std::shared_ptr GetNodeInputValue(const CNodePtr &node, size_t i) { + auto input = GetNodeInput(node, i); + auto result = dyn_cast(input->value()); + if (result == nullptr) { + MS_LOG(EXCEPTION) << "Failed to get a value from input " << i << " of node " << node->DebugString(); + } + return result; +} + +CNodePtr FindLoopSwitchNode(const FuncGraphPtr &control_subgraph) { + if (!IsSubgraphNameCorrect(control_subgraph, CONTROL_PATTERN)) { + MS_LOG(EXCEPTION) << "Expected a loop control structure"; + } + auto lazy_call_node = GetNodeInput(control_subgraph->get_return(), kOneNum); + if (lazy_call_node->inputs().size() != kOneNum || !lazy_call_node->input(kZeroNum)->isa()) { + MS_LOG(EXCEPTION) << "Expected a lazy call node"; + } + auto switch_node = GetNodeInput(lazy_call_node, kZeroNum); + if (!switch_node->IsApply(prim::kPrimSwitch)) { + MS_LOG(EXCEPTION) << "Expected a switch node"; + } + return switch_node; +} + +FuncGraphPtr GetSubgraph(const CNodePtr &switch_node, size_t input_index, const std::string &name_pattern) { + auto input_node = GetNodeInput(switch_node, input_index); + if (!input_node->IsApply(prim::kPrimPartial)) { + MS_LOG(EXCEPTION) << "Expected a partial node"; + } + + auto subgraph = GetNodeInputValue(input_node, kOneNum); + if (!IsSubgraphNameCorrect(subgraph, name_pattern)) { + MS_LOG(EXCEPTION) << "Expected a loop part: " << name_pattern; + } + + return subgraph; +} + +// The inputs of this node are the outputs of ONNX Loop +CNodePtr FindLoopRepeatNode(const FuncGraphPtr &loop_subgraph, const FuncGraphPtr &control_subgraph) { + auto repeat_node = GetNodeInput(loop_subgraph->return_node(), kOneNum); + auto maybe_control_graph = GetNodeInputValue(repeat_node, kZeroNum); + MS_EXCEPTION_IF_CHECK_FAIL(maybe_control_graph == control_subgraph, "Loop matching failed"); + return repeat_node; +} + +struct LoopConditionInfo { + int64_t begin; + int64_t end; + int64_t step; +}; + +/* + NOTE: loop support is currently very limited, because proper condition export requires more graph surgery (copying + condition expression before and inside Loop subgraph) + The only while loop form supported currently is the one used in GNMT v2's Beam Search. Python example: + i = begin + while i < end + ... + i += step + To enable proper support for arbitrary while loop contitions, condition calculation should be duplicated inside the + Loop supgraph. But exporting the same ops twice with different names is not currently supported. + */ +LoopConditionInfo TraceLoopConditionInfo(const CNodePtr &start_node, const CNodePtr &cond_node, + const FuncGraphPtr &control_subgraph, const CNodePtr &loop_repeat_node) { + MS_EXCEPTION_IF_CHECK_FAIL(cond_node->IsApply(prim::kPrimLess), "Expected Less node"); + + auto counter = GetNodeInput(cond_node, kOneNum); + auto end_tensor = GetNodeInputValue(cond_node, kTwoNum); + MS_EXCEPTION_IF_CHECK_FAIL(end_tensor->shape_c().empty(), "Expected a scalar tensor"); + auto end = *reinterpret_cast(end_tensor->data_c()); + + const auto &subgraph_args = control_subgraph->parameters(); + auto counter_input_pos = std::find(subgraph_args.begin(), subgraph_args.end(), counter) - subgraph_args.begin(); + + auto begin_tensor = GetNodeInputValue(start_node, 1UL + static_cast(counter_input_pos)); + MS_EXCEPTION_IF_CHECK_FAIL(begin_tensor->shape_c().empty(), "Expected a scalar tensor"); + auto begin = *reinterpret_cast(begin_tensor->data_c()); + + auto increment_node = GetNodeInput(loop_repeat_node, 1UL + static_cast(counter_input_pos)); + MS_EXCEPTION_IF_CHECK_FAIL(increment_node->IsApply(prim::kPrimAdd), "Expected Add node"); + auto step_tensor = GetNodeInputValue(increment_node, kTwoNum); + MS_EXCEPTION_IF_CHECK_FAIL(step_tensor->shape_c().empty(), "Expected a scalar tensor"); + auto step = *reinterpret_cast(step_tensor->data_c()); + + return LoopConditionInfo{begin, end, step}; +} + +// result[i] is which control subgraph input should be taken for pos i to match the order of loop subgraph inputs +std::vector TraceLoopToControlMap(const FuncGraphPtr &control_subgraph) { + std::vector result; + + auto switch_node = FindLoopSwitchNode(control_subgraph); + auto loop_partial_node = GetNodeInput(switch_node, kTwoNum); + const auto &control_params = control_subgraph->parameters(); + int64_t auxiliary_inputs_num = 2; + for (size_t i = static_cast(auxiliary_inputs_num); i < loop_partial_node->inputs().size(); ++i) { + auto loop_param = GetNodeInput(loop_partial_node, i); + auto control_param_pos = + std::find(control_params.begin(), control_params.end(), loop_param) - control_params.begin(); + result.push_back(control_param_pos); + } + + return result; +} + +std::vector TraceAfterToLoopMap(const FuncGraphPtr &control_subgraph) { + std::vector result; + + auto switch_node = FindLoopSwitchNode(control_subgraph); + auto loop_partial_node = GetNodeInput(switch_node, kTwoNum); + auto after_partial_node = GetNodeInput(switch_node, kThreeNum); + const auto &loop_params = loop_partial_node->inputs(); + int64_t auxiliary_inputs_num = 2; + for (size_t i = static_cast(auxiliary_inputs_num); i < after_partial_node->inputs().size(); ++i) { + auto after_param = GetNodeInput(after_partial_node, i); + auto after_param_pos = std::find(loop_params.begin(), loop_params.end(), after_param) - loop_params.begin(); + result.push_back(after_param_pos - auxiliary_inputs_num); + } + + return result; +} + +std::vector TraceIgnoredLoopParams(const CNodePtr &start_node, const std::vector &loop_to_control_map) { + auto inputs_num = start_node->inputs().size() - 1; + std::vector result(inputs_num); + for (size_t loop_i = 0; loop_i < inputs_num; ++loop_i) { + auto control_i = loop_to_control_map.at(loop_i); + const auto &input = start_node->input(control_i + 1); + if ((input->isa() && input->cast()->has_default()) || HasAbstractMonad(input)) { + result.at(loop_i) = true; + } + } + return result; +} +} // namespace + +bool IsControlSubgraph(const ValuePtr &func_graph_node) { + auto func_graph = dyn_cast(func_graph_node); + return func_graph != nullptr && IsSubgraphNameCorrect(func_graph, CONTROL_PATTERN); +} + +bool IsLoopBodyReturnNode(const CNodePtr &node, const FuncGraphPtr &func_graph) { + return IsSubgraphNameCorrect(func_graph, LOOP_BODY_PATTERN) && node == func_graph->get_return(); +} + +bool IsAfterLoopReturnNode(const CNodePtr &node, const FuncGraphPtr &func_graph) { + return IsSubgraphNameCorrect(func_graph, AFTER_LOOP_PATTERN) && node == func_graph->get_return(); +} + +struct LoopParts { + LoopConditionInfo loop_condition_info; + std::vector> after_param_to_output_indices; + std::vector ignored_loop_param_indices; + std::vector> used_loop_to_control_param_indices; + CNodePtr repeat_node; + FuncGraphPtr loop_subgraph; + FuncGraphPtr after_loop_subgraph; +}; +// 鍖归厤鍥炬ā寮忕殑涓诲嚱鏁帮紝鎺ュ彈涓涓狢Node鑺傜偣浣滀负璧峰鑺傜偣 +LoopParts MatchGraph(const CNodePtr &start_node) { + LoopParts result;// 瀛樺偍鍖归厤缁撴灉鐨勫璞 + // 鑾峰彇鎺у埗瀛愬浘锛屾牴鎹瓹Node鐨勮緭鍏ヨ幏鍙朧alueNode锛屽啀鑾峰彇鍏朵腑鐨凢uncGraph + auto control_subgraph_value = dyn_cast(start_node->input(0)); + MS_EXCEPTION_IF_NULL(control_subgraph_value); + auto control_subgraph = dyn_cast(control_subgraph_value->value()); + MS_EXCEPTION_IF_NULL(control_subgraph); + // 瀵绘壘寰幆涓殑Switch鑺傜偣锛屾壘鍒版潯浠惰妭鐐瑰拰寰幆浣撳瓙鍥 + auto switch_node = FindLoopSwitchNode(control_subgraph); + auto cond_node = GetNodeInput(switch_node, kOneNum); + // 鑾峰彇寰幆浣撳瓙鍥 + result.loop_subgraph = GetSubgraph(switch_node, LOOP_BODY_INPUT, LOOP_BODY_PATTERN); + // 瀵绘壘寰幆涓殑Repeat鑺傜偣 + result.repeat_node = FindLoopRepeatNode(result.loop_subgraph, control_subgraph); + // 璺熻釜寰幆鏉′欢淇℃伅 + result.loop_condition_info = TraceLoopConditionInfo(start_node, cond_node, control_subgraph, result.repeat_node); + // 鑾峰彇寰幆鍚庡瓙鍥 + result.after_loop_subgraph = GetSubgraph(switch_node, AFTER_LOOP_INPUT, AFTER_LOOP_PATTERN); + // 璺熻釜寰幆涓庢帶鍒惰妭鐐瑰弬鏁扮殑鏄犲皠鍏崇郴 + auto loop_to_control_order_map = TraceLoopToControlMap(control_subgraph); + // 璺熻釜蹇界暐鐨勫惊鐜弬鏁版帺鐮 + auto ignored_loop_params_mask = TraceIgnoredLoopParams(start_node, loop_to_control_order_map); + // 澶勭悊寰幆杈撳叆鍙傛暟 + auto loop_inputs_num = start_node->inputs().size() - 1; + for (size_t i = 0; i < loop_inputs_num; ++i) { + if (ignored_loop_params_mask.at(i)) { + result.ignored_loop_param_indices.push_back(i); + } else { + result.used_loop_to_control_param_indices.push_back(std::make_pair(i, loop_to_control_order_map.at(i))); + } + } + // 璺熻釜寰幆鍚庡瓙鍥惧埌寰幆鍐呭弬鏁扮殑鏄犲皠 + auto after_to_loop_order_map = TraceAfterToLoopMap(control_subgraph); + // 澶勭悊寰幆鍚庡弬鏁板埌寰幆杈撳嚭鍙傛暟鐨勬槧灏 + for (size_t after_i = 0; after_i < result.after_loop_subgraph->parameters().size(); ++after_i) { + auto loop_i = after_to_loop_order_map.at(after_i); + if (!ignored_loop_params_mask.at(loop_i)) { + auto output_i = loop_i; + for (size_t i = 0; i < loop_i; ++i) { + output_i -= static_cast(ignored_loop_params_mask.at(i)); + } + result.after_param_to_output_indices.push_back(std::make_pair(after_i, output_i)); + } + } + + return result; +} +} // namespace while_loop_export + +class OpAttrInfo { + public: + OpAttrInfo(const std::string &attr_name, const string &onnx_attr_name, + onnx::AttributeProto_AttributeType onnx_attr_type, const GenAttrFuncType &fn_gen_attr) + : attr_name_(attr_name), + onnx_attr_name_(onnx_attr_name), + onnx_attr_type_(onnx_attr_type), + fn_gen_attr_(fn_gen_attr) {} + ~OpAttrInfo() {} + + const std::string &attr_name() const { return attr_name_; } + const std::string &onnx_attr_name() const { return onnx_attr_name_; } + onnx::AttributeProto_AttributeType onnx_attr_type() const { return onnx_attr_type_; } + GenAttrFuncType fn_gen_attr() const { return fn_gen_attr_; } + + private: + std::string attr_name_; // attribute name of MindSpore + std::string onnx_attr_name_; // corresponding attribute name of ONNX + onnx::AttributeProto_AttributeType onnx_attr_type_; // corresponding attribute type of ONNX + GenAttrFuncType fn_gen_attr_; // function used convert +}; + +struct InputConversion { + int input_index; + onnx::TensorProto_DataType input_type; + onnx::TensorProto_DataType target_type; +}; + +struct OutputConversion { + int output_index; + enum class Mode { FIXED, INPUT } mode; + union { + onnx::TensorProto_DataType target_type; + int input_with_matching_type; + }; +}; + +class OpNameInfo { + public: + OpNameInfo &set_op_type(const std::string &op_type) { + op_type_ = op_type; + return *this; + } + + const std::string &op_type() const { return op_type_; } + + OpNameInfo &set_onnx_type(const std::string &onnx_type) { + onnx_type_ = onnx_type; + return *this; + } + + const std::string &onnx_type() const { return onnx_type_; } + + OpNameInfo &Attr(const std::string &attr_name, const std::string &onnx_attr_name, + onnx::AttributeProto_AttributeType onnx_attr_type, const GenAttrFuncType &fn_gen_attr) { + (void)op_attrs_.emplace_back(OpAttrInfo(attr_name, onnx_attr_name, onnx_attr_type, fn_gen_attr)); + return *this; + } + + const std::vector &op_attrs() const { return op_attrs_; } + + const std::vector &input_casts() const { return input_casts_; } + + OpNameInfo &CastInput(int input_index, onnx::TensorProto_DataType input_type, + onnx::TensorProto_DataType target_type) { + input_casts_.push_back({input_index, input_type, target_type}); + return *this; + } + + const std::vector &output_casts() const { return output_casts_; } + + OpNameInfo &CastOutputToFixedType(onnx::TensorProto_DataType type, int output_index = 0) { + output_casts_.push_back({output_index, OutputConversion::Mode::FIXED, {type}}); + return *this; + } + + OpNameInfo &CastOutputToInputType(int input_index, int output_index = 0) { + auto rule = OutputConversion{output_index, OutputConversion::Mode::INPUT}; + rule.input_with_matching_type = input_index; + output_casts_.push_back(rule); + return *this; + } + + int num_outputs() const { return num_outputs_; } + + OpNameInfo &set_num_outputs(int n) { + num_outputs_ = n; + return *this; + } + + private: + std::string op_type_; // operator type of MindSpore + std::string onnx_type_; // corresponding ONNX operator type + std::vector op_attrs_; // operator attributes map info + std::vector input_casts_; // if input input_index has type input_type, cast it to target_type + std::vector output_casts_; // cast output output_index to fixed type or input type + int num_outputs_ = 1; +}; + +#define OPERATOR_ONNX_CONVERT_DEFINE(name, onnx_name, impl) \ + OpNameInfo GetOpOnnxConvertInfo_##name() { return impl.set_op_type(#name).set_onnx_type(#onnx_name); } + +OPERATOR_ONNX_CONVERT_DEFINE(Add, Add, OpNameInfo()) +OPERATOR_ONNX_CONVERT_DEFINE(Mul, Mul, OpNameInfo()) +OPERATOR_ONNX_CONVERT_DEFINE(Pow, Pow, OpNameInfo()) + +OPERATOR_ONNX_CONVERT_DEFINE(ReLU, Relu, OpNameInfo()) +OPERATOR_ONNX_CONVERT_DEFINE(Sigmoid, Sigmoid, OpNameInfo()) + +OPERATOR_ONNX_CONVERT_DEFINE(Flatten, Flatten, OpNameInfo()) + +OPERATOR_ONNX_CONVERT_DEFINE( + Conv2D, Conv, + OpNameInfo() + .Attr("dilation", "dilations", onnx::AttributeProto_AttributeType_INTS, SetAttrTupleValueToProto<2>) + .Attr("group", "group", onnx::AttributeProto_AttributeType_INT, SetAttrValueToProto) + .Attr("kernel_size", "kernel_shape", onnx::AttributeProto_AttributeType_INTS, SetAttrTupleValueToProto<0>) + .Attr("pad_mode", "auto_pad", onnx::AttributeProto_AttributeType_STRING, SetConvPadding) + .Attr("stride", "strides", onnx::AttributeProto_AttributeType_INTS, SetAttrTupleValueToProto<2>)) +OPERATOR_ONNX_CONVERT_DEFINE( + Conv3D, Conv, + OpNameInfo() + .Attr("dilations", "dilations", onnx::AttributeProto_AttributeType_INTS, SetAttrTupleValueToProto) + .Attr("group", "group", onnx::AttributeProto_AttributeType_INT, SetAttrValueToProto) + .Attr("kernel_size", "kernel_shape", onnx::AttributeProto_AttributeType_INTS, SetAttrTupleValueToProto<0>) + .Attr("pad_mode", "auto_pad", onnx::AttributeProto_AttributeType_STRING, SetConvPadding) + .Attr("strides", "strides", onnx::AttributeProto_AttributeType_INTS, SetAttrTupleValueToProto)) +OPERATOR_ONNX_CONVERT_DEFINE( + Conv3DTranspose, ConvTranspose, + OpNameInfo() + .Attr("dilations", "dilations", onnx::AttributeProto_AttributeType_INTS, SetAttrTupleValueToProto) + .Attr("group", "group", onnx::AttributeProto_AttributeType_INT, SetAttrValueToProto) + .Attr("kernel_size", "kernel_shape", onnx::AttributeProto_AttributeType_INTS, SetAttrTupleValueToProto<0>) + .Attr("pad_mode", "auto_pad", onnx::AttributeProto_AttributeType_STRING, SetConvTransposePadding) + .Attr("strides", "strides", onnx::AttributeProto_AttributeType_INTS, SetAttrTupleValueToProto) + .Attr("output_padding", "output_padding", onnx::AttributeProto_AttributeType_INTS, + SetAttrTupleValueToProto)) +OPERATOR_ONNX_CONVERT_DEFINE(BiasAdd, Add, OpNameInfo()) +OPERATOR_ONNX_CONVERT_DEFINE(MatMul, Gemm, + OpNameInfo() + .Attr("transpose_a", "transA", onnx::AttributeProto_AttributeType_INT, + SetAttrValueToProto) + .Attr("transpose_b", "transB", onnx::AttributeProto_AttributeType_INT, + SetAttrValueToProto)) + +OPERATOR_ONNX_CONVERT_DEFINE(BatchNorm, BatchNormalization, + OpNameInfo() + .Attr("epsilon", "epsilon", onnx::AttributeProto_AttributeType_FLOAT, + SetAttrValueToProto) + .CastInput(0, onnx::TensorProto_DataType_FLOAT16, onnx::TensorProto_DataType_FLOAT) + .CastOutputToInputType(0)) + +OPERATOR_ONNX_CONVERT_DEFINE(Reshape, Reshape, OpNameInfo()) +OPERATOR_ONNX_CONVERT_DEFINE(Cast, Cast, OpNameInfo()) +OPERATOR_ONNX_CONVERT_DEFINE(PReLU, PRelu, OpNameInfo()) +OPERATOR_ONNX_CONVERT_DEFINE(Argmax, ArgMax, + OpNameInfo() + .Attr("axis", "axis", onnx::AttributeProto_AttributeType_INT, + SetAttrValueToProto) + .Attr("", "keepdims", onnx::AttributeProto_AttributeType_INT, + [](ValuePtr, onnx::AttributeProto_AttributeType, + onnx::AttributeProto *const attr_proto, const PrimitivePtr &) { + attr_proto->set_type(onnx::AttributeProto_AttributeType_INT); + attr_proto->set_i(0); + }) + .CastOutputToFixedType(onnx::TensorProto_DataType_INT32)) + +OPERATOR_ONNX_CONVERT_DEFINE(SimpleMean, AveragePool, OpNameInfo()) +OPERATOR_ONNX_CONVERT_DEFINE( + MaxPool, MaxPool, + OpNameInfo() + .Attr("kernel_size", "kernel_shape", onnx::AttributeProto_AttributeType_INTS, SetAttrTupleValueToProto<2>) + .Attr("pad_mode", "auto_pad", onnx::AttributeProto_AttributeType_STRING, SetPoolingPadMode) + .Attr("strides", "strides", onnx::AttributeProto_AttributeType_INTS, SetAttrTupleValueToProto<2>)) + +OPERATOR_ONNX_CONVERT_DEFINE( + MaxPoolWithArgmax, MaxPool, + OpNameInfo() + .Attr("kernel_size", "kernel_shape", onnx::AttributeProto_AttributeType_INTS, SetAttrTupleValueToProto<2>) + .Attr("pad_mode", "auto_pad", onnx::AttributeProto_AttributeType_STRING, SetPoolingPadMode) + .Attr("strides", "strides", onnx::AttributeProto_AttributeType_INTS, SetAttrTupleValueToProto<2>)) + +OPERATOR_ONNX_CONVERT_DEFINE( + AvgPool, AveragePool, + OpNameInfo() + .Attr("kernel_size", "kernel_shape", onnx::AttributeProto_AttributeType_INTS, SetAttrTupleValueToProto<2>) + .Attr("pad_mode", "auto_pad", onnx::AttributeProto_AttributeType_STRING, SetPoolingPadMode) + .Attr("strides", "strides", onnx::AttributeProto_AttributeType_INTS, SetAttrTupleValueToProto<2>)) + +OPERATOR_ONNX_CONVERT_DEFINE(Gather, Gather, OpNameInfo()) +OPERATOR_ONNX_CONVERT_DEFINE(MakeTuple, SequenceConstruct, OpNameInfo()) +OPERATOR_ONNX_CONVERT_DEFINE(RealDiv, Div, OpNameInfo()) +OPERATOR_ONNX_CONVERT_DEFINE(Sub, Sub, OpNameInfo()) +OPERATOR_ONNX_CONVERT_DEFINE(Maximum, Max, + OpNameInfo() + .CastInput(0, onnx::TensorProto_DataType_INT32, onnx::TensorProto_DataType_FLOAT) + .CastInput(1, onnx::TensorProto_DataType_INT32, onnx::TensorProto_DataType_FLOAT) + .CastOutputToInputType(0)) +OPERATOR_ONNX_CONVERT_DEFINE(Minimum, Min, + OpNameInfo() + .CastInput(0, onnx::TensorProto_DataType_INT32, onnx::TensorProto_DataType_FLOAT) + .CastInput(1, onnx::TensorProto_DataType_INT32, onnx::TensorProto_DataType_FLOAT) + .CastOutputToInputType(0)) +OPERATOR_ONNX_CONVERT_DEFINE(Transpose, Transpose, OpNameInfo()) +OPERATOR_ONNX_CONVERT_DEFINE(Exp, Exp, OpNameInfo()) +OPERATOR_ONNX_CONVERT_DEFINE(Softplus, Softplus, OpNameInfo()) +OPERATOR_ONNX_CONVERT_DEFINE(Tanh, Tanh, OpNameInfo()) +OPERATOR_ONNX_CONVERT_DEFINE(Abs, Abs, OpNameInfo()) + +// MindSpore Softmax axis(int, Tuple) +OPERATOR_ONNX_CONVERT_DEFINE(Softmax, Softmax, + OpNameInfo().Attr("axis", "axis", onnx::AttributeProto_AttributeType_INT, + SetAttrTupleValueToProto<0>)) + +// MindSpore LogSoftmax axis(int) +OPERATOR_ONNX_CONVERT_DEFINE(LogSoftmax, LogSoftmax, + OpNameInfo().Attr("axis", "axis", onnx::AttributeProto_AttributeType_INT, + SetAttrValueToProto)) + +OPERATOR_ONNX_CONVERT_DEFINE(Softsign, Softsign, OpNameInfo()) +OPERATOR_ONNX_CONVERT_DEFINE(Sqrt, Sqrt, OpNameInfo()) +OPERATOR_ONNX_CONVERT_DEFINE(Equal, Equal, OpNameInfo()) +OPERATOR_ONNX_CONVERT_DEFINE(Floor, Floor, OpNameInfo()) +OPERATOR_ONNX_CONVERT_DEFINE(ACos, Acos, OpNameInfo()) + +OPERATOR_ONNX_CONVERT_DEFINE(GatherNd, GatherND, + OpNameInfo().CastInput(1, onnx::TensorProto_DataType_INT32, + onnx::TensorProto_DataType_INT64)) +OPERATOR_ONNX_CONVERT_DEFINE(Select, Where, OpNameInfo()) +OPERATOR_ONNX_CONVERT_DEFINE(Log, Log, OpNameInfo()) +OPERATOR_ONNX_CONVERT_DEFINE(Greater, Greater, OpNameInfo()) +OPERATOR_ONNX_CONVERT_DEFINE(LogicalAnd, And, OpNameInfo()) +OPERATOR_ONNX_CONVERT_DEFINE(ReverseSequence, ReverseSequence, + OpNameInfo() + .Attr("seq_dim", "time_axis", onnx::AttributeProto_AttributeType_INT, + SetAttrValueToProto) + .Attr("batch_dim", "batch_axis", onnx::AttributeProto_AttributeType_INT, + SetAttrValueToProto) + .CastInput(1, onnx::TensorProto_DataType_INT32, onnx::TensorProto_DataType_INT64)) +OPERATOR_ONNX_CONVERT_DEFINE(Less, Less, OpNameInfo()) +OPERATOR_ONNX_CONVERT_DEFINE(TensorScatterUpdate, ScatterND, + OpNameInfo().CastInput(1, onnx::TensorProto_DataType_INT32, + onnx::TensorProto_DataType_INT64)) + +#define OP_CONVERT_FUNCTION_NAME(name) GetOpOnnxConvertInfo_##name + +void RegisterOpConverters(const std::function &fn) { + fn(OP_CONVERT_FUNCTION_NAME(Add)()); + fn(OP_CONVERT_FUNCTION_NAME(Mul)()); + fn(OP_CONVERT_FUNCTION_NAME(Pow)()); + fn(OP_CONVERT_FUNCTION_NAME(ReLU)()); + fn(OP_CONVERT_FUNCTION_NAME(Sigmoid)()); + fn(OP_CONVERT_FUNCTION_NAME(Conv2D)()); + fn(OP_CONVERT_FUNCTION_NAME(Conv3D)()); + fn(OP_CONVERT_FUNCTION_NAME(Conv3DTranspose)()); + fn(OP_CONVERT_FUNCTION_NAME(Argmax)()); + fn(OP_CONVERT_FUNCTION_NAME(Flatten)()); + fn(OP_CONVERT_FUNCTION_NAME(MaxPool)()); + fn(OP_CONVERT_FUNCTION_NAME(MaxPoolWithArgmax)()); + fn(OP_CONVERT_FUNCTION_NAME(AvgPool)()); + + fn(OP_CONVERT_FUNCTION_NAME(BatchNorm)()); + fn(OP_CONVERT_FUNCTION_NAME(MatMul)()); + fn(OP_CONVERT_FUNCTION_NAME(MakeTuple)()); + fn(OP_CONVERT_FUNCTION_NAME(RealDiv)()); + fn(OP_CONVERT_FUNCTION_NAME(BiasAdd)()); + fn(OP_CONVERT_FUNCTION_NAME(Sub)()); + fn(OP_CONVERT_FUNCTION_NAME(Maximum)()); + fn(OP_CONVERT_FUNCTION_NAME(Minimum)()); + fn(OP_CONVERT_FUNCTION_NAME(Exp)()); + + fn(OP_CONVERT_FUNCTION_NAME(Softplus)()); + fn(OP_CONVERT_FUNCTION_NAME(Tanh)()); + fn(OP_CONVERT_FUNCTION_NAME(Softmax)()); + fn(OP_CONVERT_FUNCTION_NAME(LogSoftmax)()); + fn(OP_CONVERT_FUNCTION_NAME(Abs)()); + fn(OP_CONVERT_FUNCTION_NAME(Softsign)()); + fn(OP_CONVERT_FUNCTION_NAME(Sqrt)()); + fn(OP_CONVERT_FUNCTION_NAME(Equal)()); + fn(OP_CONVERT_FUNCTION_NAME(Floor)()); + fn(OP_CONVERT_FUNCTION_NAME(ACos)()); + + fn(OP_CONVERT_FUNCTION_NAME(GatherNd)()); + fn(OP_CONVERT_FUNCTION_NAME(Select)()); + fn(OP_CONVERT_FUNCTION_NAME(Log)()); + fn(OP_CONVERT_FUNCTION_NAME(Less)()); + fn(OP_CONVERT_FUNCTION_NAME(Greater)()); + fn(OP_CONVERT_FUNCTION_NAME(LogicalAnd)()); + fn(OP_CONVERT_FUNCTION_NAME(ReverseSequence)()); + fn(OP_CONVERT_FUNCTION_NAME(TensorScatterUpdate)()); +} + +class OpConvertRegistry { + public: + ~OpConvertRegistry() { Clear(); } + + static void RegisterOneOpConverter(OpNameInfo &&op_info) { GetSingleton().op_map_[op_info.op_type()] = op_info; } + + static void RegisterAllOpConverters() { RegisterOpConverters(RegisterOneOpConverter); } + + static OpConvertRegistry &GetSingleton() { + static OpConvertRegistry registry = OpConvertRegistry(); + return registry; + } + + static const mindspore::HashMap &GetOpConvertMap() { return GetSingleton().op_map_; } + + void Clear() noexcept { op_map_.clear(); } + + private: + OpConvertRegistry() {} + + mindspore::HashMap op_map_; +}; + +class OnnxExporter { + public: + OnnxExporter() {} + ~OnnxExporter() {} + + std::string GetOnnxProtoString(const FuncGraphPtr &func_graph); + + private: + void InitModelInfo(); + + void ExportFuncGraph(const FuncGraphPtr &func_graph, std::map *node_map_ptr, + onnx::GraphProto *graph_proto, bool export_inputs = true); + void ExportInputs(const FuncGraphPtr &func_graph, std::map *node_map_ptr, + onnx::GraphProto *graph_proto); + + std::string ExportPrimitive(const FuncGraphPtr &func_graph, std::map *node_map_ptr, + const PrimitivePtr &prim, const std::vector &inputs, + onnx::GraphProto *graph_proto); + + static onnx::TensorProto_DataType GetOnnxDataType(TypeId type_id); + static onnx::TensorProto_DataType GetOutputType(const AnfNodePtr &node, int64_t output_index = -1); + void SetValueInfoType(const AnfNodePtr &node, onnx::ValueInfoProto *value_proto, int64_t output_index = -1) const; + + void MatchAndMark(const FuncGraphPtr &func_graph, const std::vector &nodes, + mindspore::HashMap *op_merged_infos_ptr); + void MatchAndMarkCNode(const FuncGraphPtr &func_graph, const CNodePtr &cnode, + mindspore::HashMap *op_merged_infos_ptr) const; + void ExportNodes(const FuncGraphPtr &func_graph, std::map *node_map_ptr, + onnx::GraphProto *graph_proto); + + void ExportCNode(const FuncGraphPtr &func_graph, const CNodePtr &node, + std::map *node_map_ptr, onnx::GraphProto *graph_proto); + void ExportWhileLoop(const CNodePtr &start_node, std::map *node_map_ptr, + onnx::GraphProto *graph_proto); + + void ExportPrimReshape(const FuncGraphPtr &func_graph, const CNodePtr &node, + std::map *node_map_ptr, onnx::GraphProto *graph_proto); + void ExportPrimReduce(const FuncGraphPtr &func_graph, const CNodePtr &node, + std::map *node_map_ptr, onnx::GraphProto *graph_proto); + void ExportPrimTranspose(const FuncGraphPtr &func_graph, const CNodePtr &node, + std::map *node_map_ptr, onnx::GraphProto *graph_proto); + void ExportPrimStridedSlice(const FuncGraphPtr &func_graph, const CNodePtr &node, + std::map *node_map_ptr, onnx::GraphProto *graph_proto); + onnx::NodeProto *PrimResizeExportHelper(const FuncGraphPtr &, const CNodePtr &node, + std::map *node_map_ptr, + onnx::GraphProto *const graph_proto); + void ExportPrimResizeNearestNeighbor(const FuncGraphPtr &func_graph, const CNodePtr &node, + std::map *node_map_ptr, onnx::GraphProto *graph_proto); + void ExportPrimResizeBilinear(const FuncGraphPtr &func_graph, const CNodePtr &node, + std::map *node_map_ptr, onnx::GraphProto *graph_proto); + void ExportPrimExpandDims(const FuncGraphPtr &func_graph, const CNodePtr &node, + std::map *node_map_ptr, onnx::GraphProto *graph_proto); + void ExportPrimPad(const FuncGraphPtr &func_graph, const CNodePtr &node, + std::map *node_map_ptr, onnx::GraphProto *graph_proto); + void ExportPrimBatchMatMul(const FuncGraphPtr &func_graph, const CNodePtr &node, + std::map *node_map_ptr, onnx::GraphProto *graph_proto); + void ExportPrimGeLU(const FuncGraphPtr &func_graph, const CNodePtr &node, + std::map *node_map_ptr, onnx::GraphProto *graph_proto); + void ExportPrimConcat(const FuncGraphPtr &func_graph, const CNodePtr &node, + std::map *node_map_ptr, onnx::GraphProto *graph_proto); + void ExportPrimCast(const FuncGraphPtr &func_graph, const CNodePtr &node, + std::map *node_map_ptr, onnx::GraphProto *graph_proto); + void ExportPrimPReLU(const FuncGraphPtr &func_graph, const CNodePtr &node, + std::map *node_map_ptr, onnx::GraphProto *graph_proto); + void ExportPrimReLU6(const FuncGraphPtr &func_graph, const CNodePtr &node, + std::map *node_map_ptr, onnx::GraphProto *graph_proto); + void ExportPrimDepthwiseConv2d(const FuncGraphPtr &func_graph, const CNodePtr &node, + std::map *node_map_ptr, onnx::GraphProto *graph_proto); + void ExportPrimTile(const FuncGraphPtr &func_graph, const CNodePtr &node, + std::map *node_map_ptr, onnx::GraphProto *graph_proto); + void ExportPrimSquare(const FuncGraphPtr &func_graph, const CNodePtr &node, + std::map *node_map_ptr, onnx::GraphProto *graph_proto); + void ExportPrimGatherV2(const FuncGraphPtr &func_graph, const CNodePtr &node, + std::map *node_map_ptr, onnx::GraphProto *graph_proto); + void ExportPrimTupleGetItem(const FuncGraphPtr &func_graph, const CNodePtr &node, + std::map *node_map_ptr, onnx::GraphProto *graph_proto); + void ExportPrimTopK(const FuncGraphPtr &func_graph, const CNodePtr &node, + std::map *node_map_ptr, onnx::GraphProto *graph_proto); + void ExportPrimBoundingBoxDecode(const FuncGraphPtr &func_graph, const CNodePtr &node, + std::map *node_map_ptr, onnx::GraphProto *graph_proto); + void ExportPrimNMSWithMask(const FuncGraphPtr &func_graph, const CNodePtr &node, + std::map *node_map_ptr, onnx::GraphProto *graph_proto); + void ExportPrimSplit(const FuncGraphPtr &func_graph, const CNodePtr &node, + std::map *node_map_ptr, onnx::GraphProto *graph_proto); + void ExportPrimROIAlign(const FuncGraphPtr &func_graph, const CNodePtr &node, + std::map *node_map_ptr, onnx::GraphProto *graph_proto); + void ExportPrimSlice(const FuncGraphPtr &func_graph, const CNodePtr &node, + std::map *node_map_ptr, onnx::GraphProto *graph_proto); + void ExportPrimOnesLike(const FuncGraphPtr &func_graph, const CNodePtr &node, + std::map *node_map_ptr, onnx::GraphProto *graph_proto); + void ExportPrimArgMaxWithValue(const FuncGraphPtr &func_graph, const CNodePtr &node, + std::map *node_map_ptr, onnx::GraphProto *graph_proto); + void ExportPrimOneHot(const FuncGraphPtr &func_graph, const CNodePtr &node, + std::map *node_map_ptr, onnx::GraphProto *graph_proto); + void PrimConv2DTransposeExportHelper(const CNodePtr &conv_node, const CNodePtr &bias_add_node, + std::map *node_map_ptr, + onnx::GraphProto *const graph_proto); + void ExportPrimConv2DTranspose(const FuncGraphPtr &func_graph, const CNodePtr &node, + std::map *node_map_ptr, onnx::GraphProto *graph_proto); + void ExportPrimGreaterEqual(const FuncGraphPtr &func_graph, const CNodePtr &node, + std::map *node_map_ptr, onnx::GraphProto *graph_proto); + void ExportPrimSqueeze(const FuncGraphPtr &func_graph, const CNodePtr &node, + std::map *node_map_ptr, onnx::GraphProto *graph_proto); + void ExportPrimLSTM(const FuncGraphPtr &, const CNodePtr &node, std::map *node_map_ptr, + onnx::GraphProto *graph_proto); + void ExportPrimReverseV2(const FuncGraphPtr &func_graph, const CNodePtr &node, + std::map *node_map_ptr, onnx::GraphProto *graph_proto); + void ExportPrimTensorCopySlices(const FuncGraphPtr &, const CNodePtr &node, + std::map *node_map_ptr, onnx::GraphProto *graph_proto); + void ExportPrimStack(const FuncGraphPtr &, const CNodePtr &node, std::map *node_map_ptr, + onnx::GraphProto *graph_proto); + void ExportMergeConv(const FuncGraphPtr &func_graph, const CNodePtr &node, + std::map *node_map_ptr, onnx::GraphProto *graph_proto); + void ExportMergeGemm(const FuncGraphPtr &func_graph, const CNodePtr &node, + std::map *node_map_ptr, onnx::GraphProto *graph_proto); + void ExportMergeBatchNorm(const FuncGraphPtr &func_graph, const CNodePtr &node, + std::map *node_map_ptr, onnx::GraphProto *graph_proto); + void ExportMergeMaxPoolWithArgmax(const FuncGraphPtr &func_graph, const CNodePtr &node, + std::map *node_map_ptr, onnx::GraphProto *graph_proto); + void ExportMergeLayerNorm(const FuncGraphPtr &func_graph, const CNodePtr &node, + std::map *node_map_ptr, onnx::GraphProto *graph_proto); + void ExportMergeConv2DTranspose(const FuncGraphPtr &, const CNodePtr &node, + std::map *node_map_ptr, onnx::GraphProto *graph_proto); + + void ExportOutput(const FuncGraphPtr &func_graph, const AnfNodePtr &return_arg, + std::map *node_map_ptr, onnx::GraphProto *graph_proto); + std::string GetNodeInputName(const AnfNodePtr &node, std::map *node_map_ptr, + onnx::GraphProto *const); + + void ConvertTupleToTensor(const ValuePtr &value, onnx::TensorProto *tensor_proto) const; + void SetTensorData(const ValuePtr &value, onnx::TensorProto *tensor_proto); + + void AddOutputWithCast(onnx::NodeProto *node_proto, const std::string &output_name, + onnx::TensorProto_DataType target_type, onnx::GraphProto *graph_proto) const; + + std::string GenerateUniqueName() { return std::to_string(++onnx_node_index_); } + std::string RegisterNodeWithUniqueName(const AnfNodePtr &node, std::map *node_map_ptr) { + auto name = GenerateUniqueName(); + (*node_map_ptr)[node] = name; + return name; + } + std::string GenerateUniqueParameterName(const ParameterPtr &node, std::map *node_map_ptr) { + auto node_name = node->ToString(); + MS_EXCEPTION_IF_CHECK_FAIL(node_name != "", "Cannot get the name of an ignored parameter"); + auto dup_iter = std::find_if(node_map_ptr->begin(), node_map_ptr->end(), + [&node_name](const auto &pair) { return pair.second == node_name; }); + if (dup_iter != node_map_ptr->end()) { + node_name = GenerateUniqueName() + node_name; + } + return node_name; + } + + void ResetNodeIndex() { onnx_node_index_ = 0; } + + static int64_t GetInt64Value(const AnfNodePtr &node) { + auto value_node_ptr = dyn_cast(node); + MS_EXCEPTION_IF_NULL(value_node_ptr); + return GetValue(value_node_ptr->value()); + } + + onnx::ModelProto model_; + + size_t onnx_node_index_ = 0; + + std::map renamed_node_map_; +}; + +std::string OnnxExporter::GetOnnxProtoString(const FuncGraphPtr &func_graph) { + if (func_graph == nullptr) { + return ""; + } + ResetNodeIndex(); + OpConvertRegistry::GetSingleton().Clear(); + OpConvertRegistry::RegisterAllOpConverters(); + InitModelInfo(); + onnx::GraphProto *graph_proto = model_.mutable_graph(); + std::map node_map; + ExportFuncGraph(func_graph, &node_map, graph_proto); + return model_.SerializeAsString(); +} + +void OnnxExporter::InitModelInfo() { + model_.set_ir_version(onnx::IR_VERSION_2019_1_22); + model_.set_producer_name("MindSpore"); + model_.set_producer_version("1.0"); + onnx::OperatorSetIdProto *opset_proto = model_.add_opset_import(); + opset_proto->set_version(ONNX_VERSION); +} + +void OnnxExporter::ExportFuncGraph(const FuncGraphPtr &func_graph, std::map *node_map_ptr, + onnx::GraphProto *const graph_proto, bool export_inputs) { + MS_LOG(INFO) << "Begin exporting onnx model for graph " << func_graph->ToString(); + + // set graph name + graph_proto->set_name(func_graph->ToString()); + + // export inputs if graph is not inlined + if (export_inputs) { + ExportInputs(func_graph, node_map_ptr, graph_proto); + } + + // export computational nodes and output nodes + ExportNodes(func_graph, node_map_ptr, graph_proto); + + // add names for easier debugging + for (auto &node : *graph_proto->mutable_node()) { + if (!node.has_name()) { + node.set_name(node.output(0) + node.op_type()); + } + } + + MS_LOG(INFO) << "End exporting onnx model for graph " << func_graph->ToString(); +} + +void OnnxExporter::ExportInputs(const FuncGraphPtr &func_graph, std::map *node_map_ptr, + onnx::GraphProto *const graph_proto) { + for (auto ¶m : func_graph->parameters()) { + const ParameterPtr param_ptr = dyn_cast(param); + if (param_ptr == nullptr) { + MS_LOG(EXCEPTION) << "Parameter '" << param->ToString() << "' could not cast to parameter."; + } + + if (param_ptr->has_default()) { + continue; + } + + // set onnx input. + std::string name; + auto renamed_iter = renamed_node_map_.find(param_ptr); + if (renamed_iter != renamed_node_map_.end()) { + name = renamed_iter->second; + if (name == "") { + continue; + } + } else { + name = GenerateUniqueParameterName(param_ptr, node_map_ptr); + (*node_map_ptr)[param_ptr] = name; + } + + onnx::ValueInfoProto *input_proto = graph_proto->add_input(); + input_proto->set_name(name); + SetValueInfoType(param_ptr, input_proto); + } +} + +onnx::TensorProto_DataType OnnxExporter::GetOnnxDataType(TypeId type_id) { + // clang-format off + static mindspore::HashMap type_map = { + {kNumberTypeBool, onnx::TensorProto_DataType_BOOL}, + {kNumberTypeInt8, onnx::TensorProto_DataType_INT8}, + {kNumberTypeInt16, onnx::TensorProto_DataType_INT16}, + {kNumberTypeInt32, onnx::TensorProto_DataType_INT32}, + {kNumberTypeInt64, onnx::TensorProto_DataType_INT64}, + {kNumberTypeUInt8, onnx::TensorProto_DataType_UINT8}, + {kNumberTypeUInt16, onnx::TensorProto_DataType_UINT16}, + {kNumberTypeUInt32, onnx::TensorProto_DataType_UINT32}, + {kNumberTypeUInt64, onnx::TensorProto_DataType_UINT64}, + {kNumberTypeFloat16, onnx::TensorProto_DataType_FLOAT16}, + {kNumberTypeFloat32, onnx::TensorProto_DataType_FLOAT}, + {kNumberTypeFloat64, onnx::TensorProto_DataType_DOUBLE}, + }; + // clang-format on + + auto iter = type_map.find(type_id); + if (iter == type_map.end()) { + MS_LOG(EXCEPTION) << "Convert type error, unsupported type " << type_id; + } + + return iter->second; +} + +void OnnxExporter::SetValueInfoType(const AnfNodePtr &node, onnx::ValueInfoProto *const value_proto, + int64_t output_index) const { + auto dtype = GetOutputType(node, output_index); + auto shape = node->Shape(); + + abstract::ShapePtr output_shape; + if (shape->isa()) { + auto tuple_shape = dyn_cast(shape); + auto base_shape = tuple_shape->shape().at(static_cast(output_index)); + output_shape = dyn_cast(base_shape); + if (output_shape == nullptr) { + MS_LOG(EXCEPTION) << "Expected " << node->ToString() << " to output a tuple of tensors. Instead got " + << base_shape->ToString() << " from output " << output_index; + } + } else if (shape->isa()) { + output_shape = dyn_cast(shape); + } else { + MS_LOG(EXCEPTION) << "Unsupported shape: " << shape->ToString(); + } + + auto *type_proto = value_proto->mutable_type(); + type_proto->mutable_tensor_type()->set_elem_type(dtype); + auto *shape_proto = type_proto->mutable_tensor_type()->mutable_shape(); + + for (const auto dim : output_shape->shape()) { + shape_proto->add_dim()->set_dim_value(dim); + } +} + +void OnnxExporter::MatchAndMark(const FuncGraphPtr &func_graph, const std::vector &nodes, + mindspore::HashMap *op_merged_infos_ptr) { + auto &op_merged_infos = *op_merged_infos_ptr; + + for (auto &node : nodes) { + if (!node->isa() || IsZeroRefcountNode(node)) { + continue; + } + auto cnode = node->cast(); + if (cnode == func_graph->get_return()) { + // if the key `input` does not exist, just create a new one + op_merged_infos[cnode].referred_count += 1; + } + for (auto &orig_input : cnode->inputs()) { + auto input = GetRealInput(orig_input); + if (!input->isa() || IsZeroRefcountNode(input)) { + continue; + } + // if the key `input` does not exist, just create a new one + op_merged_infos[input].referred_count += 1; + } + MatchAndMarkCNode(func_graph, cnode, op_merged_infos_ptr); + } +} + +struct MergeRule { + PrimitivePtr node_type; + PrimitivePtr prev_type; + OpMergeMode merge_mode; +}; + +void OnnxExporter::MatchAndMarkCNode(const FuncGraphPtr &func_graph, const CNodePtr &cnode, + mindspore::HashMap *op_merged_infos_ptr) const { + auto &op_merged_infos = *op_merged_infos_ptr; + const auto ignore = [&op_merged_infos](const AnfNodePtr &node) { + op_merged_infos[node].mode = OP_MERGE_IGNORE; + op_merged_infos[node].referred_count -= 1; + }; + + const std::vector first_input_merge_rules = { + {prim::kPrimBiasAdd, prim::kPrimConv2D, OP_MERGE_CONV}, + {prim::kPrimBiasAdd, prim::kPrimConv2DTranspose, OP_MERGE_CONV2D_TRANSPOSE}, + {prim::kPrimBiasAdd, prim::kPrimConv3D, OP_MERGE_CONV}, + {prim::kPrimBiasAdd, prim::kPrimConv3DTranspose, OP_MERGE_CONV}, + {prim::kPrimBiasAdd, prim::kPrimMatMul, OP_MERGE_GEMM}, + {prim::kPrimTupleGetItem, prim::kPrimBatchNorm, OP_MERGE_BATCH_NORM}, + {prim::kPrimTupleGetItem, prim::kPrimMaxPoolWithArgmax, OP_MERGE_MAXPOOL_WITH_ARGMAX}, + {prim::kPrimTupleGetItem, prim::kPrimLayerNorm, OP_MERGE_LAYER_NORM}, + }; + + auto rule = std::find_if(first_input_merge_rules.begin(), first_input_merge_rules.end(), [&cnode](const auto &rule) { + return cnode->IsApply(rule.node_type) && IsPrimitiveCNode(cnode->input(1), rule.prev_type); + }); + if (rule != first_input_merge_rules.end()) { + if (cnode->IsApply(prim::kPrimTupleGetItem) && GetInt64Value(cnode->input(kTwoNum)) != 0) { + MS_LOG(EXCEPTION) << "Multiple outputs for node \"" << cnode->input(1)->ToString() << "\" are not supported"; + } + op_merged_infos[cnode].mode = rule->merge_mode; + ignore(cnode->input(1)); + } else if (while_loop_export::IsLoopBodyReturnNode(cnode, func_graph)) { + // Ignore to replace with other outputs + ignore(cnode); + auto repeat_node = dyn_cast(GetRealInput(cnode->input(1))); + MS_EXCEPTION_IF_NULL(repeat_node); + ignore(repeat_node); + } else if (while_loop_export::IsAfterLoopReturnNode(cnode, func_graph)) { + // Ignore to inline after-loop subgraph in main graph + ignore(cnode); + auto first_input = GetRealInput(cnode->input(1)); + if (IsPrimitiveCNode(first_input, prim::kPrimMakeTuple)) { + ignore(first_input); + } + } else if (cnode == func_graph->get_return()) { + auto first_input = GetRealInput(cnode->input(1)); // Unpack Depend + if (IsPrimitiveCNode(first_input, prim::kPrimMakeTuple)) { + // Ignore MakeTuple output node to avoid exporting it to SequenceConstruct + // and handle multiple outputs in ExportOutput + ignore(first_input); + } + } else if (cnode->IsApply(prim::kPrimConcat) && IsPrimitiveCNode(cnode->input(1), prim::kPrimMakeTuple)) { + // Ignore MakeTuple to handle it in ExportPrimConcat + ignore(cnode->input(1)); + } +} + +/** + * AnfNode + * +-- CNode + * +-- ANode + * | +-- Parameter + * | `-- ValueNode + */ +void OnnxExporter::ExportNodes(const FuncGraphPtr &func_graph, std::map *node_map_ptr, + onnx::GraphProto *const graph_proto) { + std::vector nodes = TopoSort(func_graph->get_return(), SuccIncoming, AlwaysInclude); + + mindspore::HashMap op_merged_infos; + MatchAndMark(func_graph, nodes, &op_merged_infos); + for (const AnfNodePtr &node : nodes) { + if (!node->isa()) { + continue; + } + auto cnode = node->cast(); + + auto iter = op_merged_infos.find(cnode); + // the node is not referenced by any other nodes, skip it + if (iter == op_merged_infos.end()) { + continue; + } + auto merged_info = iter->second; + // the op node is merged with other node and not used any more, skip it + if (merged_info.mode == OP_MERGE_IGNORE && merged_info.referred_count == 0) { + continue; + } + if (cnode == func_graph->get_return()) { + ExportOutput(func_graph, cnode->input(kOneNum), node_map_ptr, graph_proto); + continue; + } + switch (merged_info.mode) { + case OP_MERGE_CONV: + ExportMergeConv(func_graph, cnode, node_map_ptr, graph_proto); + break; + case OP_MERGE_GEMM: + ExportMergeGemm(func_graph, cnode, node_map_ptr, graph_proto); + break; + case OP_MERGE_BATCH_NORM: + ExportMergeBatchNorm(func_graph, cnode, node_map_ptr, graph_proto); + break; + case OP_MERGE_MAXPOOL_WITH_ARGMAX: + ExportMergeMaxPoolWithArgmax(func_graph, cnode, node_map_ptr, graph_proto); + break; + case OP_MERGE_LAYER_NORM: + ExportMergeLayerNorm(func_graph, cnode, node_map_ptr, graph_proto); + break; + case OP_MERGE_CONV2D_TRANSPOSE: + ExportMergeConv2DTranspose(func_graph, cnode, node_map_ptr, graph_proto); + break; + default: + ExportCNode(func_graph, cnode, node_map_ptr, graph_proto); + break; + } + } +} + +void OnnxExporter::ExportPrimReshape(const FuncGraphPtr &, const CNodePtr &node, + std::map *node_map_ptr, + onnx::GraphProto *const graph_proto) { + // 瀵煎嚭 ONNX 鏍煎紡涓殑 Reshape 鎿嶄綔 + auto name_x = GetNodeInputName(node->input(kOneNum), node_map_ptr, graph_proto);// 鑾峰彇杈撳叆鑺傜偣鐨勫悕绉 + auto input_shape = node->input(kTwoNum);// 鑾峰彇杈撳叆绫诲瀷锛堝彲鑳芥槸 ValueNode 鎴栬呭叾浠) + std::string name_shape; + // 濡傛灉杈撳叆绫诲瀷鏄 ValueNode锛屽垯灏嗗叾杞崲涓 Constant 鑺傜偣 + if (input_shape->isa()) { + name_shape = RegisterNodeWithUniqueName(input_shape, node_map_ptr); + onnx::NodeProto *node_proto = graph_proto->add_node(); + auto name = prim::kPrimReshape->name(); + // 璁剧疆 Constant 鑺傜偣鐨勪俊鎭 + node_proto->set_name(name_shape + name); + node_proto->add_output(name_shape); + node_proto->set_op_type("Constant"); + onnx::AttributeProto *attr_proto = node_proto->add_attribute(); + attr_proto->set_name("value"); + attr_proto->set_type(onnx::AttributeProto_AttributeType_TENSOR); + ConvertTupleToTensor(dyn_cast(input_shape)->value(), attr_proto->mutable_t()); + } else { + // 濡傛灉杈撳叆褰㈢姸涓嶆槸 ValueNode锛屽垯鎶涘嚭寮傚父 + name_shape = GetNodeInputName(input_shape, node_map_ptr, graph_proto); + MS_LOG(EXCEPTION) << "Need to insert op convert variable from tuple to tensor for Reshape."; + } + // 娉ㄥ唽褰撳墠 Reshape 鑺傜偣骞惰缃浉搴旂殑淇℃伅 + auto node_name = RegisterNodeWithUniqueName(node, node_map_ptr); + onnx::NodeProto *node_proto = graph_proto->add_node(); + node_proto->set_op_type(prim::kPrimReshape->name()); + node_proto->add_output(node_name); + node_proto->add_input(name_x); + node_proto->add_input(name_shape); +} + +void OnnxExporter::ExportPrimReduce(const FuncGraphPtr &, const CNodePtr &node, + std::map *node_map_ptr, + onnx::GraphProto *const graph_proto) { + auto input_data = GetNodeInputName(node->input(kOneNum), node_map_ptr, graph_proto); + auto input_axis = node->input(kTwoNum); + auto keep_dims = GetOpAttribute(node, "keep_dims"); + + auto node_name = RegisterNodeWithUniqueName(node, node_map_ptr); + + std::string name; + if (node->IsApply(prim::kPrimReduceSum)) { + name = "ReduceSum"; + } else if (node->IsApply(prim::kPrimReduceMean)) { + name = "ReduceMean"; + } else { + MS_LOG(EXCEPTION) << "Unsupported reduce op: " << node->ToString(); + } + + std::vector axes; + if (input_axis->isa()) { + auto axis_value = dyn_cast(input_axis)->value(); + if (axis_value->isa()) { + auto int_ptr = dyn_cast(axis_value); + axes.push_back(int_ptr->value()); + } else if (axis_value->isa()) { + auto int_ptr = dyn_cast(axis_value); + axes.push_back(int_ptr->value()); + } else if (axis_value->isa()) { + auto tuple_ptr = dyn_cast(axis_value); + axes = GetValue>(tuple_ptr); + } else { + MS_LOG(EXCEPTION) << "Cannot convert value " << axis_value->ToString() << " of type " + << axis_value->type()->ToString() << " for \"axes\" attribute of " << name; + } + } else { + MS_LOG(EXCEPTION) << "Need to insert op convert variable from tuple to attributes for " << name; + } + + AddReduceOp(name, input_data, node_name, axes, keep_dims, graph_proto); +} +//瀵煎嚭Reduce鎿嶄綔 +void OnnxExporter::ExportPrimTranspose(const FuncGraphPtr &, const CNodePtr &node, + std::map *node_map_ptr, + onnx::GraphProto *const graph_proto) { + auto input_data = GetNodeInputName(node->input(kOneNum), node_map_ptr, graph_proto); + auto input_perm = node->input(kTwoNum); + auto node_name = RegisterNodeWithUniqueName(node, node_map_ptr); + onnx::NodeProto *node_proto = graph_proto->add_node(); + auto name = prim::kPrimTranspose->name(); + + node_proto->set_name(node_name + name); + node_proto->set_op_type(name); + node_proto->add_output(node_name); + node_proto->add_input(input_data); + + if (input_perm->isa()) { + onnx::AttributeProto *attr_proto = node_proto->add_attribute(); + attr_proto->set_name("perm"); + attr_proto->set_type(onnx::AttributeProto_AttributeType_INTS); + auto perm_value = dyn_cast(input_perm)->value(); + auto int_ptr = dyn_cast(perm_value); + if (int_ptr == nullptr) { + auto tuple_ptr = dyn_cast(perm_value); + MS_EXCEPTION_IF_NULL(tuple_ptr); + for (size_t i = 0; i < tuple_ptr->size(); ++i) { + attr_proto->add_ints(GetValue((*tuple_ptr)[i])); + } + } else { + attr_proto->add_ints(int_ptr->value()); + } + } else { + MS_LOG(EXCEPTION) << "The input input_perm of Transpose is not a ValueNode! " + << "Need to insert op convert variable from tuple to attributes for " << name; + } +} + +/* + See: + - mindspore/ccsrc/backend/kernel_compiler/cpu/stridedslice_cpu_kernel.cc + - mindspore/ccsrc/backend/kernel_compiler/common_utils.cc + */ +void OnnxExporter::ExportPrimStridedSlice(const FuncGraphPtr &, const CNodePtr &node, + std::map *node_map_ptr, + onnx::GraphProto *const graph_proto) { + auto input_data = GetNodeInputName(node->input(kOneNum), node_map_ptr, graph_proto); + auto node_name = RegisterNodeWithUniqueName(node, node_map_ptr); + auto name = node_name + prim::kPrimStridedSlice->name(); + + auto begin = node->input(kTwoNum); + if (!begin->isa()) { + MS_LOG(EXCEPTION) << "The input begin of StridedSlice is not a ValueNode! " + << "Need to insert op convert variable from tuple to tensor for " << name; + } + auto begin_value_node = dyn_cast(begin); + auto begin_value = GetValue>(begin_value_node->value()); + auto begin_ignore_mask = GetOpAttribute(node, "begin_mask"); + for (size_t i = 0; i < begin_value.size(); ++i) { + if ((begin_ignore_mask & (1 << i)) != 0) { + begin_value[i] = 0; + } + } + + auto end = node->input(kThreeNum); + if (!end->isa()) { + MS_LOG(EXCEPTION) << "The input end of StridedSlice is not a ValueNode! " + << "Need to insert op convert variable from tuple to tensor for " << name; + } + auto end_value_node = dyn_cast(end); + auto end_value = GetValue>(end_value_node->value()); + const auto &x_shape = dyn_cast(node->input(kOneNum)->Shape())->shape(); + auto end_ignore_mask = GetOpAttribute(node, "end_mask"); + for (size_t i = 0; i < end_value.size(); ++i) { + if ((static_cast(end_ignore_mask) & (1 << i)) != 0) { + end_value[i] = x_shape[i]; + } + } + + std::vector axes_value; + for (size_t i = 0; i < x_shape.size(); ++i) { + axes_value.push_back(static_cast(i)); + } + + auto strides = node->input(kFourNum); + if (!strides->isa()) { + MS_LOG(EXCEPTION) << "The input strides of StridedSlice is not a ValueNode! " + << "Need to insert op convert variable from tuple to tensor for " << name; + } + auto strides_value_node = dyn_cast(strides); + auto strides_value = GetValue>(strides_value_node->value()); + + auto shrink_axis_mask = GetOpAttribute(node, "shrink_axis_mask"); + for (size_t i = 0; i < end_value.size(); ++i) { + if ((shrink_axis_mask & (1 << i)) != 0) { + strides_value[i] = end_value[i] > begin_value[i] ? 1 : -1; + end_value[i] = begin_value[i] + strides_value[i]; + } + } + + auto slice_name = node_name; + if (shrink_axis_mask != 0) { + slice_name = node_name + "__reshape"; + } + + AddSliceOp(input_data, slice_name, begin_value, end_value, axes_value, strides_value, graph_proto); + + if (shrink_axis_mask != 0) { + onnx::NodeProto *squeeze_op = graph_proto->add_node(); + squeeze_op->set_op_type("Squeeze"); + squeeze_op->add_input(slice_name); + squeeze_op->add_output(node_name); + onnx::AttributeProto *axes_attr = squeeze_op->add_attribute(); + axes_attr->set_name("axes"); + axes_attr->set_type(onnx::AttributeProto_AttributeType_INTS); + for (size_t i = 0; i < x_shape.size(); ++i) { + if ((shrink_axis_mask & (1 << i)) != 0) { + axes_attr->add_ints(i); + } + } + } +} + +onnx::NodeProto *OnnxExporter::PrimResizeExportHelper(const FuncGraphPtr &, const CNodePtr &node, + std::map *node_map_ptr, + onnx::GraphProto *const graph_proto) { + auto input_data = GetNodeInputName(node->input(kOneNum), node_map_ptr, graph_proto); + auto x_shape = dyn_cast(node->input(kOneNum)->Shape()); + + AnfNodePtr op = node->input(kZeroNum); + auto op_value = dyn_cast(op); + auto prim = dyn_cast(op_value->value()); + std::vector resize_size; + + auto tuple_ptr = dyn_cast(prim->GetAttr("size")); // size may be Tuple or List + if (tuple_ptr == nullptr) { + MS_LOG(EXCEPTION) << "Got null pointer, currently the " << prim->name() + << " operator in your model is not support for exporting onnx."; + } + + for (size_t i = 0; i < x_shape->shape().size() - kTwoNum; i++) { + resize_size.push_back(x_shape->shape()[i]); + } + for (size_t i = 0; i < tuple_ptr->size(); i++) { + ValuePtr elem = (*tuple_ptr)[i]; + resize_size.push_back(dyn_cast(elem)->value()); + } + auto resize_size_ptr = MakeValue>(resize_size); + auto size = NewValueNode(resize_size_ptr)->cast(); + + auto name_size = RegisterNodeWithUniqueName(size, node_map_ptr); + onnx::NodeProto *node_proto_size = graph_proto->add_node(); + node_proto_size->add_output(name_size); + node_proto_size->set_op_type("Constant"); + onnx::AttributeProto *attr_proto = node_proto_size->add_attribute(); + attr_proto->set_name("value"); + attr_proto->set_type(onnx::AttributeProto_AttributeType_TENSOR); + ConvertTupleToTensor(resize_size_ptr, attr_proto->mutable_t()); + + auto node_name = RegisterNodeWithUniqueName(node, node_map_ptr); + + onnx::TensorProto *roi_initializer_proto = graph_proto->add_initializer(); + auto roi_name = node_name + "roi_initializer"; + roi_initializer_proto->set_name(roi_name); + roi_initializer_proto->set_data_type(GetOnnxDataType(kNumberTypeFloat32)); + roi_initializer_proto->add_dims(0); + + onnx::TensorProto *scales_initializer_proto = graph_proto->add_initializer(); + auto scales_name = node_name + "scales_initializer"; + scales_initializer_proto->set_name(scales_name); + scales_initializer_proto->set_data_type(GetOnnxDataType(kNumberTypeFloat32)); + scales_initializer_proto->add_dims(0); + + onnx::NodeProto *node_proto = graph_proto->add_node(); + + node_proto->set_op_type("Resize"); + node_proto->add_output(node_name); + node_proto->add_input(input_data); + node_proto->add_input(roi_name); + node_proto->add_input(scales_name); + node_proto->add_input(name_size); + + return node_proto; +} + +void OnnxExporter::ExportPrimResizeNearestNeighbor(const FuncGraphPtr &graph, const CNodePtr &node, + std::map *node_map_ptr, + onnx::GraphProto *const graph_proto) { + onnx::NodeProto *node_proto = PrimResizeExportHelper(graph, node, node_map_ptr, graph_proto); + + auto align_corners = GetOpAttribute(node, "align_corners"); + std::string coordinate_transformation_mode = align_corners ? "align_corners" : "asymmetric"; + // `nearest_mode` is based on ResizeNearestNeighborCPUKernel::LaunchKernel in + // mindspore/ccsrc/backend/kernel_compiler/cpu/resize_nearest_neighbor_cpu_kernel.cc + std::string nearest_mode = align_corners ? "round_prefer_ceil" : "floor"; + + onnx::AttributeProto *coordinate_mode_proto = node_proto->add_attribute(); + coordinate_mode_proto->set_name("coordinate_transformation_mode"); + coordinate_mode_proto->set_type(onnx::AttributeProto_AttributeType_STRING); + coordinate_mode_proto->set_s(coordinate_transformation_mode); + + onnx::AttributeProto *nearest_mode_proto = node_proto->add_attribute(); + nearest_mode_proto->set_name("nearest_mode"); + nearest_mode_proto->set_type(onnx::AttributeProto_AttributeType_STRING); + nearest_mode_proto->set_s(nearest_mode); +} + +void OnnxExporter::ExportPrimResizeBilinear(const FuncGraphPtr &graph, const CNodePtr &node, + std::map *node_map_ptr, + onnx::GraphProto *const graph_proto) { + onnx::NodeProto *node_proto = PrimResizeExportHelper(graph, node, node_map_ptr, graph_proto); + + auto align_corners = GetOpAttribute(node, "align_corners"); + std::string coordinate_transformation_mode = align_corners ? "align_corners" : "asymmetric"; + + onnx::AttributeProto *coordinate_mode_proto = node_proto->add_attribute(); + coordinate_mode_proto->set_name("coordinate_transformation_mode"); + coordinate_mode_proto->set_type(onnx::AttributeProto_AttributeType_STRING); + coordinate_mode_proto->set_s(coordinate_transformation_mode); + + onnx::AttributeProto *mode_proto = node_proto->add_attribute(); + mode_proto->set_name("mode"); + mode_proto->set_type(onnx::AttributeProto_AttributeType_STRING); + mode_proto->set_s("linear"); +} + +// MindSpore ExpandDims -> ONNX Reshape +void OnnxExporter::ExportPrimExpandDims(const FuncGraphPtr &, const CNodePtr &node, + std::map *node_map_ptr, + onnx::GraphProto *const graph_proto) { + auto input_x = GetNodeInputName(node->input(kOneNum), node_map_ptr, graph_proto); + auto axis = GetInt64Value(node->input(kTwoNum)); + auto x_shape = dyn_cast(node->input(kOneNum)->Shape()); + auto name = prim::kPrimExpandDims->name(); + + std::vector new_shape; + for (size_t i = 0; i < x_shape->shape().size(); i++) { + new_shape.push_back(x_shape->shape()[i]); + } + if (axis < 0) { + axis = axis + kOneNumLong + SizeToLong(x_shape->shape().size()); + } + (void)new_shape.insert(new_shape.begin() + axis, kOneNum); + auto new_shape_value = MakeValue>(new_shape); + auto shape = NewValueNode(new_shape_value)->cast(); + std::string name_shape; + + if (shape->isa()) { + name_shape = RegisterNodeWithUniqueName(shape, node_map_ptr); + onnx::NodeProto *node_proto = graph_proto->add_node(); + node_proto->add_output(name_shape); + node_proto->set_op_type("Constant"); + onnx::AttributeProto *attr_proto = node_proto->add_attribute(); + attr_proto->set_name("value"); + attr_proto->set_type(onnx::AttributeProto_AttributeType_TENSOR); + ConvertTupleToTensor(dyn_cast(shape)->value(), attr_proto->mutable_t()); + } else { + name_shape = GetNodeInputName(shape, node_map_ptr, graph_proto); + MS_LOG(EXCEPTION) << "Need to insert op convert variable from tuple to tensor for " << name; + } + + auto node_name = RegisterNodeWithUniqueName(node, node_map_ptr); + onnx::NodeProto *node_proto = graph_proto->add_node(); + node_proto->set_op_type("Reshape"); + node_proto->add_output(node_name); + node_proto->add_input(input_x); + node_proto->add_input(name_shape); +} + +// MindSpore Pad -> ONNX Pad +void OnnxExporter::ExportPrimPad(const FuncGraphPtr &, const CNodePtr &node, + std::map *node_map_ptr, onnx::GraphProto *const graph_proto) { + auto x_name = GetNodeInputName(node->input(kOneNum), node_map_ptr, graph_proto); + + auto paddings = GetOpAttributePtr(node, "paddings"); + std::vector> paddings_values = GetValue>>(paddings); + std::vector pads_sequence; + for (size_t i = 0; i < paddings_values.size(); ++i) { + pads_sequence.push_back(paddings_values[i][0]); + } + for (size_t j = 0; j < paddings_values.size(); ++j) { + pads_sequence.push_back(paddings_values[j][1]); + } + auto pads_ptr = MakeValue>(pads_sequence); + auto pads = NewValueNode(pads_ptr)->cast(); + + auto pads_name = RegisterNodeWithUniqueName(pads, node_map_ptr); + onnx::NodeProto *pads_node = graph_proto->add_node(); + pads_node->add_output(pads_name); + pads_node->set_op_type("Constant"); + onnx::AttributeProto *pads_attr_proto = pads_node->add_attribute(); + pads_attr_proto->set_name("value"); + pads_attr_proto->set_type(onnx::AttributeProto_AttributeType_TENSOR); + ConvertTupleToTensor(pads_ptr, pads_attr_proto->mutable_t()); + + auto ms_pad_node_name = RegisterNodeWithUniqueName(node, node_map_ptr); + onnx::NodeProto *onnx_pad_node = graph_proto->add_node(); + onnx_pad_node->set_op_type("Pad"); + onnx_pad_node->add_output(ms_pad_node_name); + onnx_pad_node->add_input(x_name); + onnx_pad_node->add_input(pads_name); +} + +// MindSpore BatchMatMul -> ONNX Transpose + MatMul +void OnnxExporter::ExportPrimBatchMatMul(const FuncGraphPtr &, const CNodePtr &node, + std::map *node_map_ptr, + onnx::GraphProto *const graph_proto) { + auto input_x = GetNodeInputName(node->input(kOneNum), node_map_ptr, graph_proto); + auto input_y = GetNodeInputName(node->input(kTwoNum), node_map_ptr, graph_proto); + + AnfNodePtr batchmatmul_op = node->input(kZeroNum); + auto op_value = dyn_cast(batchmatmul_op); + auto prim = dyn_cast(op_value->value()); + auto transpose_a = GetValue(prim->GetAttr("transpose_a")); + auto transpose_b = GetValue(prim->GetAttr("transpose_b")); + std::string transpose_input_x_name = ""; + std::string transpose_input_y_name = ""; + + if (transpose_a) { + auto input_x_shape = dyn_cast(node->input(kOneNum)->Shape()); + // Add Transpose node after input_x of BatchMatMul + transpose_input_x_name = GenerateUniqueName(); + onnx::NodeProto *transpose_inputx_node_proto = graph_proto->add_node(); + transpose_inputx_node_proto->add_input(input_x); + transpose_inputx_node_proto->add_output(transpose_input_x_name); + transpose_inputx_node_proto->set_op_type(prim::kPrimTranspose->name()); + onnx::AttributeProto *attr_proto = transpose_inputx_node_proto->add_attribute(); + attr_proto->set_name("perm"); + attr_proto->set_type(onnx::AttributeProto_AttributeType_INTS); + for (size_t i = 0; i < input_x_shape->shape().size() - kTwoNum; i++) { + attr_proto->add_ints(SizeToLong(i)); + } + attr_proto->add_ints(SizeToLong(input_x_shape->shape().size()) - IntToLong(kOneNum)); + attr_proto->add_ints(SizeToLong(input_x_shape->shape().size()) - IntToLong(kTwoNum)); + } + if (transpose_b) { + auto input_y_shape = dyn_cast(node->input(kTwoNum)->Shape()); + // Add Transpose node after input_y of BatchMatMul + transpose_input_y_name = GenerateUniqueName(); + onnx::NodeProto *transpose_inputy_node_proto = graph_proto->add_node(); + transpose_inputy_node_proto->add_input(input_y); + transpose_inputy_node_proto->add_output(transpose_input_y_name); + transpose_inputy_node_proto->set_op_type(prim::kPrimTranspose->name()); + onnx::AttributeProto *attr_proto = transpose_inputy_node_proto->add_attribute(); + attr_proto->set_name("perm"); + attr_proto->set_type(onnx::AttributeProto_AttributeType_INTS); + for (size_t i = 0; i < input_y_shape->shape().size() - kTwoNum; i++) { + attr_proto->add_ints(SizeToLong(i)); + } + attr_proto->add_ints(SizeToLong(input_y_shape->shape().size()) - IntToLong(kOneNum)); + attr_proto->add_ints(SizeToLong(input_y_shape->shape().size()) - IntToLong(kTwoNum)); + } + + auto node_name = RegisterNodeWithUniqueName(node, node_map_ptr); + onnx::NodeProto *node_proto = graph_proto->add_node(); + node_proto->set_op_type("MatMul"); + node_proto->add_output(node_name); + node_proto->set_name(node_name + "MatMul"); + if (transpose_a) { + node_proto->add_input(transpose_input_x_name); + } else { + node_proto->add_input(input_x); + } + if (transpose_b) { + node_proto->add_input(transpose_input_y_name); + } else { + node_proto->add_input(input_y); + } +} + +// MindSpore GeLU -> ONNX 0.5 * X * (1.0 + tanh((sqrt(2/pi) * (x + 0.044715 * pow(x, 3))))) +void OnnxExporter::ExportPrimGeLU(const FuncGraphPtr &, const CNodePtr &node, + std::map *node_map_ptr, + onnx::GraphProto *const graph_proto) { + auto input_x = GetNodeInputName(node->input(kOneNum), node_map_ptr, graph_proto); + auto onnx_type = GetOutputType(node->input(kOneNum)); + + // Add pow node + auto pow_name = GenerateUniqueName(); + auto exp_node_name = pow_name + "exponent_initializer"; + AddFloatTensor1DInitializer(exp_node_name, {3.0}, onnx_type, graph_proto); + AddOp("Pow", {input_x, exp_node_name}, {pow_name}, graph_proto); + + // Add first Mul Node + auto fmul_name = GenerateUniqueName(); + auto fmul_input_node_name = fmul_name + "input_y_for_mul_initializer"; + AddFloatTensor1DInitializer(fmul_input_node_name, {0.044715}, onnx_type, graph_proto); + AddOp("Mul", {pow_name, fmul_input_node_name}, {fmul_name}, graph_proto); + + // Add first Add node + auto fadd_name = GenerateUniqueName(); + AddOp("Add", {input_x, fmul_name}, {fadd_name}, graph_proto); + + // Add second Mul Node + auto smul_name = GenerateUniqueName(); + auto smul_input_node_name = smul_name + "input_y_for_smul_initializer"; + AddFloatTensor1DInitializer(smul_input_node_name, {0.7978845608}, onnx_type, graph_proto); + AddOp("Mul", {fadd_name, smul_input_node_name}, {smul_name}, graph_proto); + + // Add tanh node + auto tanh_name = GenerateUniqueName(); + AddOp("Tanh", {smul_name}, {tanh_name}, graph_proto); + + // Add second Add node + auto sadd_name = GenerateUniqueName(); + auto sadd_input_node_name = sadd_name + "input_y_for_sadd_initializer"; + AddFloatTensor1DInitializer(sadd_input_node_name, {1.0}, onnx_type, graph_proto); + AddOp("Add", {tanh_name, sadd_input_node_name}, {sadd_name}, graph_proto); + + // Add third Mul Node + auto tmul_name = GenerateUniqueName(); + auto tmul_input_node_name = tmul_name + "input_y_for_tmul_initializer"; + AddFloatTensor1DInitializer(tmul_input_node_name, {0.5}, onnx_type, graph_proto); + AddOp("Mul", {sadd_name, tmul_input_node_name}, {tmul_name}, graph_proto); + + // Add fourth Mul Node + auto fomul_node_name = RegisterNodeWithUniqueName(node, node_map_ptr); + AddOp("Mul", {input_x, tmul_name}, {fomul_node_name}, graph_proto); +} + +void OnnxExporter::ExportPrimConcat(const FuncGraphPtr &, const CNodePtr &node, + std::map *node_map_ptr, + onnx::GraphProto *const graph_proto) { + auto node_name = RegisterNodeWithUniqueName(node, node_map_ptr); + + // Get inputs first: otherwise if an input is a constant, topological order will break + auto input_node = node->input(kOneNum)->cast(); + std::vector input_names; + if (input_node->IsApply(prim::kPrimMakeTuple)) { + for (size_t i = 1; i < input_node->inputs().size(); ++i) { + auto input_name = GetNodeInputName(input_node->input(i), node_map_ptr, graph_proto); + input_names.push_back(input_name); + } + } else { + auto input_data = GetNodeInputName(node->input(kOneNum), node_map_ptr, graph_proto); + input_names.push_back(input_data); + } + + AddConcatOp(input_names, node_name, GetOpAttribute(node, "axis"), graph_proto); +} + +void OnnxExporter::ExportPrimCast(const FuncGraphPtr &, const CNodePtr &node, + std::map *node_map_ptr, + onnx::GraphProto *const graph_proto) { + auto input_data = GetNodeInputName(node->input(kOneNum), node_map_ptr, graph_proto); + auto input_type = node->input(kTwoNum); + + auto node_name = RegisterNodeWithUniqueName(node, node_map_ptr); + onnx::NodeProto *node_proto = graph_proto->add_node(); + node_proto->set_op_type(prim::kPrimCast->name()); + node_proto->add_output(node_name); + node_proto->add_input(input_data); + + if (input_type->isa()) { + onnx::AttributeProto *attr_proto = node_proto->add_attribute(); + attr_proto->set_name("to"); + attr_proto->set_type(onnx::AttributeProto_AttributeType_INT); + auto type_value = dyn_cast(input_type)->value(); + auto type_ptr = dyn_cast(type_value); + MS_EXCEPTION_IF_NULL(type_ptr); + attr_proto->set_i(GetOnnxDataType(type_ptr->type_id())); + } else { + MS_LOG(EXCEPTION) << "Need to convert MindSpore Cast input(1) to ONNX Cast to attribute."; + } +} + +void OnnxExporter::ExportPrimPReLU(const FuncGraphPtr &, const CNodePtr &node, + std::map *node_map_ptr, + onnx::GraphProto *const graph_proto) { + auto input_x = GetNodeInputName(node->input(kOneNum), node_map_ptr, graph_proto); + auto input_slope = GetNodeInputName(node->input(kTwoNum), node_map_ptr, graph_proto); + + auto x_shape = dyn_cast(node->input(kOneNum)->Shape()); + auto slope_shape = dyn_cast(node->input(kTwoNum)->Shape()); + MS_EXCEPTION_IF_NULL(x_shape); + MS_EXCEPTION_IF_NULL(slope_shape); + + // format of x is NCHW, input format is NCHW, if length of input_slope is 1, insert Unsqueeze [1,2] + if (x_shape->shape().size() == kFourNum && slope_shape->shape().size() == kOneNum) { + auto node_name = GenerateUniqueName(); + onnx::NodeProto *node_proto = graph_proto->add_node(); + node_proto->set_op_type("Unsqueeze"); + node_proto->add_output(node_name); + + onnx::AttributeProto *attr_proto = node_proto->add_attribute(); + attr_proto->set_type(onnx::AttributeProto_AttributeType_INTS); + attr_proto->set_name("axes"); + attr_proto->add_ints(kOneNum); + attr_proto->add_ints(kTwoNum); + + node_proto->add_input(input_slope); + input_slope = node_name; + } + + auto node_name = RegisterNodeWithUniqueName(node, node_map_ptr); + onnx::NodeProto *node_proto = graph_proto->add_node(); + node_proto->set_op_type("PRelu"); + node_proto->add_output(node_name); + node_proto->add_input(input_x); + node_proto->add_input(input_slope); +} + +void OnnxExporter::ExportPrimReLU6(const FuncGraphPtr &, const CNodePtr &node, + std::map *node_map_ptr, + onnx::GraphProto *const graph_proto) { + auto node_name = RegisterNodeWithUniqueName(node, node_map_ptr); + + auto input_x_name = GetNodeInputName(node->input(kOneNum), node_map_ptr, graph_proto); + auto onnx_input_type = GetOutputType(node->input(kOneNum)); + AddClipOp(input_x_name, node_name, 0.0f, 6.0f, onnx_input_type, graph_proto); +} + +void OnnxExporter::ExportPrimDepthwiseConv2d(const FuncGraphPtr &, const CNodePtr &node, + std::map *node_map_ptr, + onnx::GraphProto *const graph_proto) { + auto input_x = GetNodeInputName(node->input(kOneNum), node_map_ptr, graph_proto); + auto input_w = GetNodeInputName(node->input(kTwoNum), node_map_ptr, graph_proto); + auto x_shape = dyn_cast(node->input(kOneNum)->Shape()); + auto w_shape = dyn_cast(node->input(kTwoNum)->Shape()); + MS_EXCEPTION_IF_NULL(x_shape); + MS_EXCEPTION_IF_NULL(w_shape); + if (x_shape->shape().size() != kFourNum || w_shape->shape().size() != kFourNum) { + MS_LOG(EXCEPTION) << "DepthwiseConv2d input shape should be 4d."; + } + if (w_shape->shape()[kZeroNum] != kOneNum && w_shape->shape()[kOneNum] != kOneNum) { + MS_LOG(EXCEPTION) << "DepthwiseConv2d weight shape[0] != 1 and shape[1] != 1, cannot reshape"; + } + // create w_shape constant node + auto node_name = GenerateUniqueName(); + onnx::NodeProto *node_proto = graph_proto->add_node(); + auto name_w_shape = node_name; + node_proto->add_output(name_w_shape); + node_proto->set_op_type("Constant"); + // create Value Tensor + onnx::AttributeProto *attr_proto = node_proto->add_attribute(); + attr_proto->set_name("value"); + attr_proto->set_type(onnx::AttributeProto_AttributeType_TENSOR); + onnx::TensorProto *tensor_proto = attr_proto->mutable_t(); + tensor_proto->add_dims(static_cast<::google::protobuf::int64>(w_shape->shape().size())); + tensor_proto->set_data_type(onnx::TensorProto_DataType_INT64); + // reshape + tensor_proto->add_int64_data(w_shape->shape()[kOneNum]); + tensor_proto->add_int64_data(w_shape->shape()[kZeroNum]); + tensor_proto->add_int64_data(w_shape->shape()[kTwoNum]); + tensor_proto->add_int64_data(w_shape->shape()[kThreeNum]); + + // add reshape node + node_name = GenerateUniqueName(); + node_proto = graph_proto->add_node(); + node_proto->set_op_type(prim::kPrimReshape->name()); + node_proto->add_input(input_w); + node_proto->add_input(name_w_shape); + input_w = node_name; + node_proto->add_output(input_w); + + // add conv node + node_name = RegisterNodeWithUniqueName(node, node_map_ptr); + node_proto = graph_proto->add_node(); + node_proto->set_op_type("Conv"); + node_proto->add_input(input_x); + node_proto->add_input(input_w); + node_proto->add_output(node_name); + // set attributes + AnfNodePtr op = node->input(0); + auto op_value = dyn_cast(op); + auto prim = dyn_cast(op_value->value()); + // set dilations + onnx::AttributeProto *onnx_attr_proto = node_proto->add_attribute(); + onnx_attr_proto->set_name("dilations"); + SetAttrTupleValueToProto<2>(prim->GetAttr("dilation"), onnx::AttributeProto_AttributeType_INTS, onnx_attr_proto, + prim); + // set group + onnx_attr_proto = node_proto->add_attribute(); + onnx_attr_proto->set_name("group"); + onnx_attr_proto->set_type(onnx::AttributeProto_AttributeType_INT); + onnx_attr_proto->set_i(x_shape->shape()[1]); + // set kernel_shape + onnx_attr_proto = node_proto->add_attribute(); + onnx_attr_proto->set_name("kernel_shape"); + SetAttrTupleValueToProto<0>(prim->GetAttr("kernel_size"), onnx::AttributeProto_AttributeType_INTS, onnx_attr_proto, + prim); + + // set pad + onnx_attr_proto = node_proto->add_attribute(); + int64_t attr_value; + CheckAndConvertUtils::GetPadModEnumValue(prim->GetAttr("pad_mode"), &attr_value); + onnx_attr_proto->set_name("auto_pad"); + onnx_attr_proto->set_type(onnx::AttributeProto_AttributeType_STRING); + if (attr_value == PadMode::VALID) { + onnx_attr_proto->set_s("VALID"); + } else if (attr_value == PadMode::SAME) { + onnx_attr_proto->set_s("SAME_UPPER"); + } else { + onnx_attr_proto->set_name("pads"); + SetAttrTupleValueToProto(prim->GetAttr("pad_list"), onnx::AttributeProto_AttributeType_INTS, onnx_attr_proto, prim); + } + // set strides + onnx_attr_proto = node_proto->add_attribute(); + onnx_attr_proto->set_name("strides"); + SetAttrTupleValueToProto<2>(prim->GetAttr("stride"), onnx::AttributeProto_AttributeType_INTS, onnx_attr_proto, prim); +} + +void OnnxExporter::ExportPrimTile(const FuncGraphPtr &, const CNodePtr &node, + std::map *node_map_ptr, + onnx::GraphProto *const graph_proto) { + auto name_x = GetNodeInputName(node->input(kOneNum), node_map_ptr, graph_proto); + auto multiples = node->input(kTwoNum); + std::string name_multiples; + if (multiples->isa()) { + onnx::NodeProto *node_proto = graph_proto->add_node(); + name_multiples = RegisterNodeWithUniqueName(multiples, node_map_ptr); + node_proto->add_output(name_multiples); + node_proto->set_op_type("Constant"); + onnx::AttributeProto *attr_proto = node_proto->add_attribute(); + attr_proto->set_name("value"); + attr_proto->set_type(onnx::AttributeProto_AttributeType_TENSOR); + ConvertTupleToTensor(dyn_cast(multiples)->value(), attr_proto->mutable_t()); + } else { + name_multiples = GetNodeInputName(multiples, node_map_ptr, graph_proto); + MS_LOG(EXCEPTION) << "Need to insert op convert variable from tuple to tensor for Tile."; + } + + auto node_name = RegisterNodeWithUniqueName(node, node_map_ptr); + onnx::NodeProto *node_proto = graph_proto->add_node(); + node_proto->set_op_type("Tile"); + node_proto->add_output(node_name); + node_proto->add_input(name_x); + node_proto->add_input(name_multiples); +} + +void OnnxExporter::ExportPrimSquare(const FuncGraphPtr &, const CNodePtr &node, + std::map *node_map_ptr, + onnx::GraphProto *const graph_proto) { + auto name_x = GetNodeInputName(node->input(kOneNum), node_map_ptr, graph_proto); + auto name_exponent = GenerateUniqueName(); + onnx::NodeProto *node_proto_exp = graph_proto->add_node(); + node_proto_exp->add_output(name_exponent); + + node_proto_exp->set_op_type("Constant"); + onnx::AttributeProto *attr_proto = node_proto_exp->add_attribute(); + attr_proto->set_name("value"); + attr_proto->set_type(onnx::AttributeProto_AttributeType_TENSOR); + onnx::TensorProto *tensor_proto = attr_proto->mutable_t(); + const float exponent_value = 2.0; + tensor_proto->set_name("exponent"); + tensor_proto->add_dims(static_cast<::google::protobuf::int64>(1)); + tensor_proto->set_data_type(GetOnnxDataType(kNumberTypeFloat32)); + tensor_proto->add_float_data(exponent_value); + + auto node_name = RegisterNodeWithUniqueName(node, node_map_ptr); + onnx::NodeProto *node_proto = graph_proto->add_node(); + node_proto->set_op_type("Pow"); + node_proto->add_output(node_name); + node_proto->add_input(name_x); + node_proto->add_input(name_exponent); +} + +void OnnxExporter::ExportPrimGatherV2(const FuncGraphPtr &, const CNodePtr &node, + std::map *node_map_ptr, + onnx::GraphProto *const graph_proto) { + auto name_x = GetNodeInputName(node->input(kOneNum), node_map_ptr, graph_proto); + auto name_indices = GetNodeInputName(node->input(kTwoNum), node_map_ptr, graph_proto); + auto axis = node->input(kThreeNum)->cast()->value(); + auto node_name = RegisterNodeWithUniqueName(node, node_map_ptr); + onnx::NodeProto *node_proto = graph_proto->add_node(); + node_proto->set_op_type("Gather"); + node_proto->add_output(node_name); + node_proto->add_input(name_x); + node_proto->add_input(name_indices); + onnx::AttributeProto *attr_proto = node_proto->add_attribute(); + attr_proto->set_name("axis"); + attr_proto->set_type(onnx::AttributeProto_AttributeType_INT); + attr_proto->set_i(static_cast<::google::protobuf::int64>(dyn_cast(axis)->value())); +} + +/* + This is a workaround for nodes with several outputs used at once + MatchAndMark cannot help here, because it only supports a single output + Proposed convention: + * Nodes with several outputs are registered as + `(*node_map_ptr)[node] = node_idx;`, just like nodes with a single output + * Their outputs are named "{node_idx}_{output_idx}" + * TupleGetItem automatically passes the outputs to the next nodes + See OnnxExporter::ExportPrimTopK for a usage example +*/ +void OnnxExporter::ExportPrimTupleGetItem(const FuncGraphPtr &, const CNodePtr &node, + std::map *node_map_ptr, + onnx::GraphProto *const graph_proto) { + auto index = GetInt64Value(node->input(kTwoNum)); + + auto input_node_name = GetNodeInputName(node->input(kOneNum), node_map_ptr, graph_proto); + auto input_name = MakeOutputName(input_node_name, index); + + auto node_name = RegisterNodeWithUniqueName(node, node_map_ptr); + + onnx::NodeProto *node_proto = graph_proto->add_node(); + node_proto->set_op_type("Identity"); + node_proto->add_input(input_name); + node_proto->add_output(node_name); +} + +void OnnxExporter::ExportPrimTopK(const FuncGraphPtr &, const CNodePtr &node, + std::map *node_map_ptr, + onnx::GraphProto *const graph_proto) { + auto x_input_name = GetNodeInputName(node->input(kOneNum), node_map_ptr, graph_proto); + + auto node_name = RegisterNodeWithUniqueName(node, node_map_ptr); + + auto k_input_name = node_name + "k_initializer"; + auto k = GetInt64Value(node->input(kTwoNum)); + AddInt64Tensor1DInitializer(k_input_name, {k}, graph_proto); + + onnx::NodeProto *node_proto = graph_proto->add_node(); + node_proto->set_op_type("TopK"); + node_proto->add_input(x_input_name); + node_proto->add_input(k_input_name); + node_proto->add_output(MakeOutputName(node_name, kZeroNum)); // Values + auto indices_name = MakeOutputName(node_name, kOneNum); + auto indices_cast_name = indices_name + "_cast"; + node_proto->add_output(indices_cast_name); + + onnx::AttributeProto *sorted_attr_proto = node_proto->add_attribute(); + sorted_attr_proto->set_name("sorted"); + sorted_attr_proto->set_type(onnx::AttributeProto_AttributeType_INT); + auto sorted = GetOpAttribute(node, "sorted"); + sorted_attr_proto->set_i(sorted); + AddCastOp(indices_cast_name, indices_name, onnx::TensorProto_DataType_INT32, graph_proto); +} + +// Based on mindspore/ccsrc/backend/kernel_compiler/cpu/boundingbox_decode_cpu_kernel.cc +void OnnxExporter::ExportPrimBoundingBoxDecode(const FuncGraphPtr &, const CNodePtr &node, + std::map *node_map_ptr, + onnx::GraphProto *const graph_proto) { + auto node_name = RegisterNodeWithUniqueName(node, node_map_ptr); + + auto anchor_bbox_input_name = GetNodeInputName(node->input(kOneNum), node_map_ptr, graph_proto); + auto deltas_input_name = GetNodeInputName(node->input(kTwoNum), node_map_ptr, graph_proto); + auto onnx_input_type = GetOutputType(node->input(kOneNum)); + + auto means = GetOpAttributePtr(node, "means"); + std::vector mean_values = GetValue>(means); + auto means_name = node_name + "means_initializer"; + AddFloatTensor1DInitializer(means_name, mean_values, onnx_input_type, graph_proto); + + auto stds = GetOpAttributePtr(node, "stds"); + std::vector std_values = GetValue>(stds); + auto stds_name = node_name + "stds_initializer"; + AddFloatTensor1DInitializer(stds_name, std_values, onnx_input_type, graph_proto); + + auto wh_ratio_clip = GetOpAttribute(node, "wh_ratio_clip"); + auto max_ratio = static_cast(std::abs(std::log(wh_ratio_clip))); + + auto unstd_deltas_name = node_name + "unstd_deltas"; + auto sd_to_add_name = unstd_deltas_name + "__add"; + AddOp("Mul", {deltas_input_name, stds_name}, {sd_to_add_name}, graph_proto); + AddOp("Add", {sd_to_add_name, means_name}, {unstd_deltas_name}, graph_proto); + + auto center_deltas_name = node_name + "center_deltas"; + auto log_scale_deltas_name = node_name + "log_scale_deltas"; + auto lsd_to_clip_name = log_scale_deltas_name + "__clip"; + AddSplitOp(unstd_deltas_name, {center_deltas_name, lsd_to_clip_name}, {kTwoNum, kTwoNum}, 1, graph_proto); + AddClipOp(lsd_to_clip_name, log_scale_deltas_name, -max_ratio, max_ratio, onnx_input_type, graph_proto); + + auto anchor_starts_name = node_name + "anchor_starts"; + auto anchor_ends_name = node_name + "anchor_ends"; + AddSplitOp(anchor_bbox_input_name, {anchor_starts_name, anchor_ends_name}, {kTwoNum, kTwoNum}, 1, graph_proto); + + auto anchor_centers_name = node_name + "anchor_centers"; + auto anchor_dimensions_name = node_name + "anchor_dimensions"; + ConvertBoxesToXywh(anchor_starts_name, anchor_ends_name, anchor_centers_name, anchor_dimensions_name, onnx_input_type, + graph_proto); + + auto anchor_shifts_name = node_name + "anchor_shifts"; + AddOp("Mul", {anchor_dimensions_name, center_deltas_name}, {anchor_shifts_name}, graph_proto); + auto result_centers_name = node_name + "result_centers"; + AddOp("Add", {anchor_centers_name, anchor_shifts_name}, {result_centers_name}, graph_proto); + + auto anchor_scales_name = node_name + "anchor_scales"; + AddOp("Exp", {log_scale_deltas_name}, {anchor_scales_name}, graph_proto); + auto result_dimensions_name = node_name + "result_dimensions"; + AddOp("Mul", {anchor_dimensions_name, anchor_scales_name}, {result_dimensions_name}, graph_proto); + + auto result_starts_to_clip_name = node_name + "result_starts_to_clip"; + auto result_ends_to_clip_name = node_name + "result_ends_to_clip"; + ConvertBoxesToXyxy(result_centers_name, result_dimensions_name, result_starts_to_clip_name, result_ends_to_clip_name, + onnx_input_type, graph_proto); + + auto max_shape = GetOpAttributePtr(node, "max_shape"); + auto max_y = GetValue((*max_shape)[0]); + auto max_x = GetValue((*max_shape)[1]); + auto result_start_xs_name = node_name + "result_start_x"; + auto result_start_ys_name = node_name + "result_start_y"; + auto result_end_xs_name = node_name + "result_end_x"; + auto result_end_ys_name = node_name + "result_end_y"; + ClipPointsComponent(result_starts_to_clip_name, result_start_xs_name, static_cast(max_x), 0, onnx_input_type, + graph_proto); + ClipPointsComponent(result_starts_to_clip_name, result_start_ys_name, static_cast(max_y), 1, onnx_input_type, + graph_proto); + ClipPointsComponent(result_ends_to_clip_name, result_end_xs_name, static_cast(max_x), 0, onnx_input_type, + graph_proto); + ClipPointsComponent(result_ends_to_clip_name, result_end_ys_name, static_cast(max_y), 1, onnx_input_type, + graph_proto); + + AddConcatOp({result_start_xs_name, result_start_ys_name, result_end_xs_name, result_end_ys_name}, node_name, kOneNum, + graph_proto); +} + +void OnnxExporter::ExportPrimNMSWithMask(const FuncGraphPtr &, const CNodePtr &node, + std::map *node_map_ptr, + onnx::GraphProto *const graph_proto) { + auto node_name = RegisterNodeWithUniqueName(node, node_map_ptr); + + auto bboxes_input_name = GetNodeInputName(node->input(kOneNum), node_map_ptr, graph_proto); + auto iou_threshold = GetOpAttribute(node, "iou_threshold"); + auto selected_boxes_output_name = MakeOutputName(node_name, kZeroNum); + auto selected_idx_output_name = MakeOutputName(node_name, kOneNum); + auto selected_mask_output_name = MakeOutputName(node_name, kTwoNum); + auto onnx_input_type = GetOutputType(node->input(kOneNum)); + + // Preprocessing + + auto boxes_count_name = node_name + "max_output_boxes"; + auto max_output_boxes_to_squeeze_name = boxes_count_name + "_to_reshape"; + auto input_shape_name = node_name + "input_shape"; + AddOp("Shape", {bboxes_input_name}, {input_shape_name}, graph_proto); + AddSliceOp(input_shape_name, max_output_boxes_to_squeeze_name, {0}, {1}, {0}, {1}, graph_proto); + AddReshapeOp(max_output_boxes_to_squeeze_name, boxes_count_name, {}, graph_proto); + + auto scores_name = node_name + "scores"; + auto flat_scores_name = scores_name + "_flat"; + auto sorted_scores_name = flat_scores_name + "_sorted"; + auto scores_to_flatten_name = scores_name + "_to_reshape"; + auto descending_order_name = node_name + "descending_indices"; + const int BBOX_NUM_EL = 4; + AddSliceOp(bboxes_input_name, scores_to_flatten_name, {BBOX_NUM_EL}, {BBOX_NUM_EL + 1}, {1}, {1}, graph_proto); + AddReshapeOp(scores_to_flatten_name, flat_scores_name, {-1}, graph_proto); + AddOp("TopK", {flat_scores_name, max_output_boxes_to_squeeze_name}, {sorted_scores_name, descending_order_name}, + graph_proto); + AddReshapeOp(sorted_scores_name, scores_name, {1, 1, -1}, graph_proto); + auto iou_threshold_name = node_name + "iou_threshold_initializer"; + AddFloatScalarInitializer(iou_threshold_name, iou_threshold, onnx::TensorProto_DataType_FLOAT, graph_proto); + + AddOp("Gather", {bboxes_input_name, descending_order_name}, {selected_boxes_output_name}, + graph_proto); // Output 0: boxes + auto boxes_name = node_name + "boxes"; + auto boxes_to_reshape_name = boxes_name + "_to_reshape"; + AddSliceOp(selected_boxes_output_name, boxes_to_reshape_name, {0}, {BBOX_NUM_EL}, {1}, {1}, graph_proto); + AddReshapeOp(boxes_to_reshape_name, boxes_name, {1, -1, BBOX_NUM_EL}, graph_proto); + + if (onnx_input_type == onnx::TensorProto_DataType_FLOAT16) { + auto fp32_boxes_name = boxes_name + "_fp32"; + AddCastOp(boxes_name, fp32_boxes_name, onnx::TensorProto_DataType_FLOAT, graph_proto); + boxes_name = fp32_boxes_name; + + auto fp32_scores_name = scores_name + "_fp32"; + AddCastOp(scores_name, fp32_scores_name, onnx::TensorProto_DataType_FLOAT, graph_proto); + scores_name = fp32_scores_name; + } + + // NMS op + + auto selected_indices_name = node_name + "selected_indices"; + AddOp("NonMaxSuppression", {boxes_name, scores_name, boxes_count_name, iou_threshold_name}, {selected_indices_name}, + graph_proto); + + // Output 1: indices + + auto flat_indices_name = node_name + "flat_indices"; + auto flat_indices_to_squeeze_name = flat_indices_name + "__reshape"; + const int BOX_INDEX_POS = 2; + AddSliceOp(selected_indices_name, flat_indices_to_squeeze_name, {BOX_INDEX_POS}, {BOX_INDEX_POS + 1}, {1}, {1}, + graph_proto); + AddReshapeOp(flat_indices_to_squeeze_name, flat_indices_name, {-1}, graph_proto); + + auto zero_name = node_name + "zero_initializer"; + onnx::TensorProto *zero_initializer = graph_proto->add_initializer(); + zero_initializer->set_name(zero_name); + zero_initializer->set_data_type(onnx::TensorProto_DataType_INT32); + zero_initializer->add_int32_data(0); + auto one_name = node_name + "one_initializer"; + onnx::TensorProto *one_initializer = graph_proto->add_initializer(); + one_initializer->set_name(one_name); + one_initializer->set_data_type(onnx::TensorProto_DataType_INT32); + one_initializer->add_int32_data(1); + auto int32_boxes_count_name = boxes_count_name + "_int32"; + AddCastOp(boxes_count_name, int32_boxes_count_name, onnx::TensorProto_DataType_INT32, graph_proto); + AddOp("Range", {zero_name, int32_boxes_count_name, one_name}, {selected_idx_output_name}, graph_proto); + + // Output 2: mask + + auto empty_mask_name = selected_mask_output_name + "__scatter"; + onnx::TensorProto *empty_mask_value_proto = + AddConstantOfShapeOp(max_output_boxes_to_squeeze_name, empty_mask_name, graph_proto); + empty_mask_value_proto->set_data_type(onnx::TensorProto_DataType_BOOL); + empty_mask_value_proto->add_int32_data(0); + + auto true_elements_name = node_name + "true"; + auto true_elements_shape_name = true_elements_name + "_shape"; + AddOp("Shape", {flat_indices_name}, {true_elements_shape_name}, graph_proto); + onnx::TensorProto *true_elements_value_proto = + AddConstantOfShapeOp(true_elements_shape_name, true_elements_name, graph_proto); + true_elements_value_proto->set_data_type(onnx::TensorProto_DataType_BOOL); + true_elements_value_proto->add_int32_data(1); + + AddOp("ScatterElements", {empty_mask_name, flat_indices_name, true_elements_name}, {selected_mask_output_name}, + graph_proto); +} + +void OnnxExporter::ExportPrimSplit(const FuncGraphPtr &, const CNodePtr &node, + std::map *node_map_ptr, + onnx::GraphProto *const graph_proto) { + auto node_name = RegisterNodeWithUniqueName(node, node_map_ptr); + auto input_name = GetNodeInputName(node->input(kOneNum), node_map_ptr, graph_proto); + + auto axis = GetOpAttribute(node, "axis"); + auto output_num = GetOpAttribute(node, "output_num"); + if (output_num == 0) { + MS_LOG(EXCEPTION) << "output_num must be > 0"; + } + const auto &input_shape = dyn_cast(node->input(kOneNum)->Shape())->shape(); + + if (axis < 0 || static_cast(axis) >= input_shape.size()) { + MS_LOG(EXCEPTION) << "`axis` is out of range"; + } + if (input_shape[static_cast(axis)] % output_num != 0) { + MS_LOG(EXCEPTION) << "Input dim is not divisible by `output_num`"; + } + + onnx::NodeProto *split_proto = graph_proto->add_node(); + split_proto->set_op_type("Split"); + split_proto->add_input(input_name); + for (int64_t i = 0; i < output_num; ++i) { + split_proto->add_output(MakeOutputName(node_name, i)); + } + + onnx::AttributeProto *axis_attr_proto = split_proto->add_attribute(); + axis_attr_proto->set_name("axis"); + axis_attr_proto->set_type(onnx::AttributeProto_AttributeType_INT); + axis_attr_proto->set_i(axis); + + onnx::AttributeProto *split_attr_proto = split_proto->add_attribute(); + split_attr_proto->set_name("split"); + split_attr_proto->set_type(onnx::AttributeProto_AttributeType_INTS); + for (int64_t i = 0; i < output_num; ++i) { + split_attr_proto->add_ints(input_shape[static_cast(axis)] / output_num); + } +} + +/* + Based on mindspore-project/mindspore/ccsrc/backend/kernel_compiler/cpu/roi_align_cpu_kernel.cc + Notes: + * MS version uses avg pool, leaving corresponding ONNX attr as is + * MS has two ROI end modes, implemented with pre-processing + */ +void OnnxExporter::ExportPrimROIAlign(const FuncGraphPtr &, const CNodePtr &node, + std::map *node_map_ptr, + onnx::GraphProto *const graph_proto) { + auto node_name = RegisterNodeWithUniqueName(node, node_map_ptr); + auto features_input_name = GetNodeInputName(node->input(kOneNum), node_map_ptr, graph_proto); + auto rois_input_name = GetNodeInputName(node->input(kTwoNum), node_map_ptr, graph_proto); + auto onnx_input_type = GetOutputType(node->input(kOneNum)); + + auto roi_indices_name = node_name + "roi_indices"; + auto roi_indices_column_name = roi_indices_name + "_column"; + auto roi_starts_name = node_name + "roi_starts"; + auto roi_ends_name = node_name + "roi_ends"; + AddSplitOp(rois_input_name, {roi_indices_column_name, roi_starts_name, roi_ends_name}, {1, kTwoNum, kTwoNum}, 1, + graph_proto); + + // Indices transformation + + auto flat_roi_indices_name = roi_indices_name + "_flat"; + AddReshapeOp(roi_indices_column_name, flat_roi_indices_name, {-1}, graph_proto); + auto int_roi_indices_name = roi_indices_name + "_int"; + // This should be fine if indices are whole numbers less than 2^23 + AddCastOp(flat_roi_indices_name, int_roi_indices_name, onnx::TensorProto_DataType_INT64, graph_proto); + + // ROI end mode + + auto roi_end_mode = GetOpAttribute(node, "roi_end_mode"); + auto roi_end_mode_name = node_name + "roi_end_mode_initializer"; + AddFloatScalarInitializer(roi_end_mode_name, roi_end_mode, onnx_input_type, graph_proto); + + auto corrected_roi_ends_name = roi_ends_name + "_corrected"; + AddOp("Add", {roi_ends_name, roi_end_mode_name}, {corrected_roi_ends_name}, graph_proto); + + // Contatenate ROIs + + auto corrected_rois_name = node_name + "corrected_rois"; + AddConcatOp({roi_starts_name, corrected_roi_ends_name}, corrected_rois_name, kOneNum, graph_proto); + + // RoiAlign op + + onnx::NodeProto *roi_align_proto = graph_proto->add_node(); + roi_align_proto->set_op_type("RoiAlign"); + roi_align_proto->add_input(features_input_name); + roi_align_proto->add_input(corrected_rois_name); + roi_align_proto->add_input(int_roi_indices_name); + roi_align_proto->add_output(node_name); + onnx::AttributeProto *height_attr_proto = roi_align_proto->add_attribute(); + height_attr_proto->set_name("output_height"); + height_attr_proto->set_type(onnx::AttributeProto_AttributeType_INT); + height_attr_proto->set_i(GetOpAttribute(node, "pooled_height")); + onnx::AttributeProto *width_attr_proto = roi_align_proto->add_attribute(); + width_attr_proto->set_name("output_width"); + width_attr_proto->set_type(onnx::AttributeProto_AttributeType_INT); + width_attr_proto->set_i(GetOpAttribute(node, "pooled_width")); + onnx::AttributeProto *scale_attr_proto = roi_align_proto->add_attribute(); + scale_attr_proto->set_name("spatial_scale"); + scale_attr_proto->set_type(onnx::AttributeProto_AttributeType_FLOAT); + scale_attr_proto->set_f(GetOpAttribute(node, "spatial_scale")); + onnx::AttributeProto *sampling_ratio_attr_proto = roi_align_proto->add_attribute(); + sampling_ratio_attr_proto->set_name("sampling_ratio"); + sampling_ratio_attr_proto->set_type(onnx::AttributeProto_AttributeType_INT); + sampling_ratio_attr_proto->set_i(GetOpAttribute(node, "sample_num")); +} + +void OnnxExporter::ExportPrimSlice(const FuncGraphPtr &, const CNodePtr &node, + std::map *node_map_ptr, + onnx::GraphProto *const graph_proto) { + auto node_name = RegisterNodeWithUniqueName(node, node_map_ptr); + auto input_x_name = GetNodeInputName(node->input(kOneNum), node_map_ptr, graph_proto); + auto begin_input_name = GetNodeInputName(node->input(kTwoNum), node_map_ptr, graph_proto); + auto size_input_name = GetNodeInputName(node->input(kThreeNum), node_map_ptr, graph_proto); + + auto end_name = node_name + "end"; + AddOp("Add", {begin_input_name, size_input_name}, {end_name}, graph_proto); + AddOp("Slice", {input_x_name, begin_input_name, end_name}, {node_name}, graph_proto); +} + +void OnnxExporter::ExportPrimOnesLike(const FuncGraphPtr &, const CNodePtr &node, + std::map *node_map_ptr, + onnx::GraphProto *const graph_proto) { + auto node_name = RegisterNodeWithUniqueName(node, node_map_ptr); + auto input_x_name = GetNodeInputName(node->input(kOneNum), node_map_ptr, graph_proto); + + auto shape_name = node_name + "shape"; + AddOp("Shape", {input_x_name}, {shape_name}, graph_proto); + + auto dtype = node->input(kOneNum)->Type(); + auto elem_type = dyn_cast(dtype)->element()->type_id(); + + onnx::TensorProto *one_proto = AddConstantOfShapeOp(shape_name, node_name, graph_proto); + switch (elem_type) { + case kNumberTypeInt32: + one_proto->set_data_type(onnx::TensorProto_DataType_INT32); + one_proto->add_int32_data(1); + break; + case kNumberTypeInt64: + one_proto->set_data_type(onnx::TensorProto_DataType_INT64); + one_proto->add_int64_data(1); + break; + case kNumberTypeFloat32: + one_proto->set_data_type(onnx::TensorProto_DataType_FLOAT); + one_proto->add_float_data(1.0f); + break; + case kNumberTypeFloat64: + one_proto->set_data_type(onnx::TensorProto_DataType_DOUBLE); + one_proto->add_double_data(1.0); + break; + default: + MS_LOG(EXCEPTION) << "Unsupported dtype: " << elem_type; + } +} + +void OnnxExporter::ExportPrimArgMaxWithValue(const FuncGraphPtr &, const CNodePtr &node, + std::map *node_map_ptr, + onnx::GraphProto *const graph_proto) { + auto node_name = RegisterNodeWithUniqueName(node, node_map_ptr); + auto input_x_name = GetNodeInputName(node->input(kOneNum), node_map_ptr, graph_proto); + auto axis = GetOpAttribute(node, "axis"); + auto keep_dims = GetOpAttribute(node, "keep_dims"); + + auto indices_output_name = MakeOutputName(node_name, kZeroNum); + auto indices_cast_name = indices_output_name + "_cast"; + + onnx::NodeProto *argmax_proto = graph_proto->add_node(); + argmax_proto->set_op_type("ArgMax"); + argmax_proto->add_input(input_x_name); + argmax_proto->add_output(indices_cast_name); + onnx::AttributeProto *argmax_axis_attr_proto = argmax_proto->add_attribute(); + argmax_axis_attr_proto->set_name("axis"); + argmax_axis_attr_proto->set_type(onnx::AttributeProto_AttributeType_INT); + argmax_axis_attr_proto->set_i(axis); + onnx::AttributeProto *argmax_keepdims_attr_proto = argmax_proto->add_attribute(); + argmax_keepdims_attr_proto->set_name("keepdims"); + argmax_keepdims_attr_proto->set_type(onnx::AttributeProto_AttributeType_INT); + argmax_keepdims_attr_proto->set_i(keep_dims); + + AddCastOp(indices_cast_name, indices_output_name, onnx::TensorProto_DataType_INT32, graph_proto); + + auto max_output_name = MakeOutputName(node_name, kOneNum); + AddReduceOp("ReduceMax", input_x_name, max_output_name, {axis}, keep_dims, graph_proto); +} + +void OnnxExporter::ExportPrimOneHot(const FuncGraphPtr &, const CNodePtr &node, + std::map *node_map_ptr, + onnx::GraphProto *const graph_proto) { + auto node_name = RegisterNodeWithUniqueName(node, node_map_ptr); + auto indices_input_name = GetNodeInputName(node->input(kOneNum), node_map_ptr, graph_proto); + auto depth_input_name = GetNodeInputName(node->input(kTwoNum), node_map_ptr, graph_proto); + auto on_input_name = GetNodeInputName(node->input(kThreeNum), node_map_ptr, graph_proto); + auto off_input_name = GetNodeInputName(node->input(kFourNum), node_map_ptr, graph_proto); + auto axis = GetOpAttribute(node, "axis"); + + if (GetOutputType(node->input(kOneNum)) == onnx::TensorProto_DataType_INT32) { + auto indices_cast_name = node_name + "_indices_as_int32"; + AddCastOp(indices_input_name, indices_cast_name, onnx::TensorProto_DataType_INT64, graph_proto); + indices_input_name = indices_cast_name; + } + + auto on_1d_name = node_name + "on_1d"; + AddReshapeOp(on_input_name, on_1d_name, {-1}, graph_proto); + auto off_1d_name = node_name + "off_1d"; + AddReshapeOp(off_input_name, off_1d_name, {-1}, graph_proto); + + auto on_off_name = node_name + "on_off"; + AddConcatOp({off_1d_name, on_1d_name}, on_off_name, kZeroNum, graph_proto); + + onnx::NodeProto *one_hot_proto = graph_proto->add_node(); + one_hot_proto->set_op_type("OneHot"); + one_hot_proto->add_input(indices_input_name); + one_hot_proto->add_input(depth_input_name); + one_hot_proto->add_input(on_off_name); + one_hot_proto->add_output(node_name); + onnx::AttributeProto *one_hot_axis_attr_proto = one_hot_proto->add_attribute(); + one_hot_axis_attr_proto->set_name("axis"); + one_hot_axis_attr_proto->set_type(onnx::AttributeProto_AttributeType_INT); + one_hot_axis_attr_proto->set_i(axis); +} + +/* + Based on nn.Conv2dTranspose + Warning: `output_shape` is an input in MS and an attribute in ONNX. Hence + it is not possible to change the output shape in runtime + */ +void OnnxExporter::PrimConv2DTransposeExportHelper(const CNodePtr &conv_node, const CNodePtr &bias_add_node, + std::map *node_map_ptr, + onnx::GraphProto *const graph_proto) { + std::string node_name; + + std::vector inputs{conv_node->input(kOneNum), conv_node->input(kTwoNum)}; + if (bias_add_node != nullptr) { + inputs.push_back(bias_add_node->input(kTwoNum)); + node_name = RegisterNodeWithUniqueName(bias_add_node, node_map_ptr); + } else { + node_name = RegisterNodeWithUniqueName(conv_node, node_map_ptr); + } + + onnx::NodeProto *node_proto = graph_proto->add_node(); + node_proto->set_op_type("ConvTranspose"); + for (const auto &input : inputs) { + node_proto->add_input(GetNodeInputName(input, node_map_ptr, graph_proto)); + } + node_proto->add_output(node_name); + + auto prim = GetPrimitive(conv_node); + auto attrs_convert_info = + OpNameInfo() + .Attr("dilation", "dilations", onnx::AttributeProto_AttributeType_INTS, SetAttrTupleValueToProto) + .Attr("group", "group", onnx::AttributeProto_AttributeType_INT, SetAttrValueToProto) + .Attr("kernel_size", "kernel_shape", onnx::AttributeProto_AttributeType_INTS, SetAttrTupleValueToProto<0>) + .Attr("pad_mode", "auto_pad", onnx::AttributeProto_AttributeType_STRING, SetConvTransposePadding) + .Attr("stride", "strides", onnx::AttributeProto_AttributeType_INTS, SetAttrTupleValueToProto); + for (const auto &attr_info : attrs_convert_info.op_attrs()) { + onnx::AttributeProto *attr_proto = node_proto->add_attribute(); + attr_proto->set_name(attr_info.onnx_attr_name()); + auto ms_attr = GetOpAttributePtr(conv_node, attr_info.attr_name()); + MS_EXCEPTION_IF_NULL(ms_attr); + attr_info.fn_gen_attr()(ms_attr, attr_info.onnx_attr_type(), attr_proto, prim); + } + + // Set output shape + + auto input_shape_node = GetRealInput(conv_node->input(kThreeNum)); + if (!input_shape_node->isa()) { + MS_LOG(EXCEPTION) << "For ONNX export third argument must be constant " + "(Python tuple). Instead got " + << input_shape_node->ToString(); + } + auto input_shape_value_ptr = input_shape_node->cast()->value(); + if (!input_shape_value_ptr->isa()) { + MS_LOG(EXCEPTION) << "Expected ValueTuple, got " << input_shape_value_ptr->ToString() << " of type " + << input_shape_value_ptr->type()->ToString(); + } + + onnx::AttributeProto *output_shape_attr_proto = node_proto->add_attribute(); + output_shape_attr_proto->set_name("output_shape"); + SetAttrTupleValueToProto<0>(input_shape_value_ptr, onnx::AttributeProto_AttributeType_INTS, output_shape_attr_proto, + prim); +} + +void OnnxExporter::ExportPrimConv2DTranspose(const FuncGraphPtr &, const CNodePtr &node, + std::map *node_map_ptr, + onnx::GraphProto *graph_proto) { + PrimConv2DTransposeExportHelper(node, nullptr, node_map_ptr, graph_proto); +} + +void OnnxExporter::ExportPrimGreaterEqual(const FuncGraphPtr &, const CNodePtr &node, + std::map *node_map_ptr, + onnx::GraphProto *const graph_proto) { + auto node_name = RegisterNodeWithUniqueName(node, node_map_ptr); + + auto input_x_name = GetNodeInputName(node->input(kOneNum), node_map_ptr, graph_proto); + auto input_y_name = GetNodeInputName(node->input(kTwoNum), node_map_ptr, graph_proto); + auto less_name = node_name + "less"; + + AddOp("Less", {input_x_name, input_y_name}, {less_name}, graph_proto); + AddOp("Not", {less_name}, {node_name}, graph_proto); +} + +void OnnxExporter::ExportPrimSqueeze(const FuncGraphPtr &, const CNodePtr &node, + std::map *node_map_ptr, + onnx::GraphProto *const graph_proto) { + auto node_name = RegisterNodeWithUniqueName(node, node_map_ptr); + + auto input_name = GetNodeInputName(node->input(kOneNum), node_map_ptr, graph_proto); + + onnx::NodeProto *node_proto = graph_proto->add_node(); + node_proto->set_op_type("Squeeze"); + node_proto->add_input(input_name); + node_proto->add_output(node_name); + + auto axes = GetOpAttributePtr(node, "axis"); + auto axes_value = GetValue>(axes); + if (!axes_value.empty()) { + onnx::AttributeProto *axes_proto = node_proto->add_attribute(); + axes_proto->set_name("axes"); + axes_proto->set_type(onnx::AttributeProto_AttributeType_INTS); + for (auto axis : axes_value) { + axes_proto->add_ints(axis); + } + } +} + +void MakeLSTMWeight(const std::string &input, const std::string &output, const std::vector &output_shape, + onnx::GraphProto *graph_proto) { + // 鍒涘缓LSTM鏉冮噸 + auto reshaped_name = output + "__split";// 灏嗚緭鍏ユ暟鎹繘琛岄噸濉 + AddReshapeOp(input, reshaped_name, output_shape, graph_proto); + // 鍒嗗壊杈撳叆鏁版嵁涓轰笉鍚岄儴鍒 + auto split_i_name = output + "__concat_i"; + auto split_o_name = output + "__concat_o"; + auto split_f_name = output + "__concat_f"; + auto split_c_name = output + "__concat_c"; + int64_t hidden_size = output_shape[kOneNum] / kFourNum; + AddSplitOp(reshaped_name, {split_i_name, split_f_name, split_c_name, split_o_name}, + {hidden_size, hidden_size, hidden_size, hidden_size}, 1, graph_proto); + // 灏嗗垎鍓插悗鐨勯儴鍒嗚繛鎺ユ垚杈撳嚭 + AddConcatOp({split_i_name, split_o_name, split_f_name, split_c_name}, output, 1, graph_proto); +} +// 瀵煎嚭 LSTM 灞傜殑鏉冮噸鍒 ONNX 鏍煎紡 +void ExportLSTMWeights(const CNodePtr &node, const std::string &node_name, const std::string &weights_name, + onnx::TensorProto_DataType dtype, const std::string &onnx_input_weights_name, + const std::string &onnx_hidden_weights_name, const std::string &onnx_bias_name, + onnx::GraphProto *graph_proto) { + // 浠庤妭鐐逛腑鑾峰彇鍚勭灞炴 + auto input_size = GetOpAttribute(node, "input_size"); + auto hidden_size = GetOpAttribute(node, "hidden_size"); + auto num_layers = GetOpAttribute(node, "num_layers"); + auto has_bias = GetOpAttribute(node, "has_bias"); + auto bidirectional = GetOpAttribute(node, "bidirectional"); + auto num_dir = 1 + static_cast(bidirectional); + auto num_gates = 4; + auto gate_size = num_gates * hidden_size; + // 妫鏌ユ槸鍚︽敮鎸佸灞 LSTM + if (num_layers != 1) { + MS_LOG(EXCEPTION) << "Converter for multilayer LSTM is not implemented"; + } + // 妫鏌ユ槸鍚︽敮鎸佸弻鍚戞ā寮 + if (bidirectional) { + MS_LOG(EXCEPTION) << "Bidirectional mode for P.LSTM is not implemented"; + } + auto ms_context = MsContext::GetInstance(); + MS_EXCEPTION_IF_NULL(ms_context); + auto target_device = ms_context->get_param(MS_CTX_DEVICE_TARGET); + // 妫鏌ヨ澶囩洰鏍囨槸鍚﹀悎娉 + if (target_device != "CPU" && target_device != "GPU") { + MS_LOG(EXCEPTION) << "Unsupported target device: " << target_device; + } + // 鐢熸垚杈撳叆鏉冮噸銆侀殣钘忔潈閲嶃佽緭鍏ュ亸宸拰闅愯棌鍋忓樊鐨勫悕绉 + auto input_weights_name = node_name + "_input_weights"; + auto hidden_weights_name = node_name + "_hidden_weights"; + auto input_bias_name = node_name + "_input_bias"; + auto hidden_bias_name = node_name + "_hidden_bias"; + // 鍑嗗鏉冮噸鍜屽亸宸殑鍒嗗壊鍙傛暟 + std::vector split_sizes = {input_size * gate_size, hidden_size * gate_size}; + std::vector split_outputs = {input_weights_name, hidden_weights_name}; + if (has_bias) { + if (target_device == "GPU") { + (void)split_sizes.insert(split_sizes.end(), {gate_size, gate_size}); + (void)split_outputs.insert(split_outputs.end(), {input_bias_name, hidden_bias_name}); + } else if (target_device == "CPU") { + split_sizes.push_back(gate_size); + split_outputs.push_back(input_bias_name); + } else { + MS_LOG(EXCEPTION) << "Impossible branch"; + } + } + // 娣诲姞鍒嗗壊鎿嶄綔 + AddSplitOp(weights_name, split_outputs, split_sizes, 0, graph_proto); + // 鍒涘缓杈撳叆鏉冮噸鍜岄殣钘忔潈閲嶇殑 ONNX 寮犻噺 + MakeLSTMWeight(input_weights_name, onnx_input_weights_name, {num_dir, gate_size, input_size}, graph_proto); + MakeLSTMWeight(hidden_weights_name, onnx_hidden_weights_name, {num_dir, gate_size, hidden_size}, graph_proto); + // 澶勭悊鍋忓樊 + if (has_bias) { + auto onnx_input_bias_name = node_name + "_onnx_input_bias"; + auto onnx_hidden_bias_name = node_name + "_onnx_hidden_bias"; + if (target_device == "GPU") { + // 鍒涘缓 GPU 涓嬬殑鍋忓樊寮犻噺 + MakeLSTMWeight(input_bias_name, onnx_input_bias_name, {num_dir, gate_size}, graph_proto); + MakeLSTMWeight(hidden_bias_name, onnx_hidden_bias_name, {num_dir, gate_size}, graph_proto); + } else if (target_device == "CPU") { + // 鍒涘缓 CPU 涓嬬殑鍋忓樊寮犻噺 + MakeLSTMWeight(input_bias_name, onnx_input_bias_name, {num_dir, gate_size}, graph_proto); + // 鍒涘缓鐢ㄤ簬濉厖鐨勯浂寮犻噺 + auto bias_shape_name = node_name + "_bias_shape"; + AddOp("Shape", {onnx_input_bias_name}, {bias_shape_name}, graph_proto); + onnx::TensorProto *zero_padding = AddConstantOfShapeOp(bias_shape_name, onnx_hidden_bias_name, graph_proto); + zero_padding->set_data_type(dtype); + // 鏍规嵁鏁版嵁绫诲瀷娣诲姞涓嶅悓绫诲瀷鐨勯浂鍊 + if (dtype == onnx::TensorProto_DataType_FLOAT16) { + zero_padding->add_int32_data(0); // float 0 and int 0 have identical representations + } else if (dtype == onnx::TensorProto_DataType_FLOAT) { + zero_padding->add_float_data(0.0f); + } else { + MS_LOG(EXCEPTION) << "Unsupported type: " << dtype; + } + } else { + MS_LOG(EXCEPTION) << "Impossible branch"; + } + // 娣诲姞杩炴帴鎿嶄綔锛屽皢鍋忓樊杩炴帴璧锋潵 + AddConcatOp({onnx_input_bias_name, onnx_hidden_bias_name}, onnx_bias_name, 1, graph_proto); + } +} + +void OnnxExporter::ExportPrimLSTM(const FuncGraphPtr &, const CNodePtr &node, + std::map *node_map_ptr, + onnx::GraphProto *const graph_proto) { + auto node_name = RegisterNodeWithUniqueName(node, node_map_ptr); + + // MS inputs + auto x_input_name = GetNodeInputName(node->input(kOneNum), node_map_ptr, graph_proto); + auto init_h_input_name = GetNodeInputName(node->input(kTwoNum), node_map_ptr, graph_proto); + auto init_c_input_name = GetNodeInputName(node->input(kThreeNum), node_map_ptr, graph_proto); + + auto hidden_size = GetOpAttribute(node, "hidden_size"); + auto has_bias = GetOpAttribute(node, "has_bias"); + auto bidirectional = GetOpAttribute(node, "bidirectional"); + std::string direction = bidirectional ? "bidirectional" : "forward"; + auto x_input_shape = dyn_cast(node->input(kOneNum)->Shape())->shape(); + auto seq_len = x_input_shape[0]; + auto batch_size = x_input_shape[1]; + auto num_dir = 1 + static_cast(bidirectional); + + auto weights_name = GetNodeInputName(node->input(kFourNum), node_map_ptr, graph_proto); + auto dtype = GetOutputType(node->input(kOneNum)); + auto onnx_input_weights_name = node_name + "_onnx_input_weights"; + auto onnx_hidden_weights_name = node_name + "_onnx_hidden_weights"; + auto onnx_bias_name = node_name + "_onnx_bias"; + + ExportLSTMWeights(node, node_name, weights_name, dtype, onnx_input_weights_name, onnx_hidden_weights_name, + onnx_bias_name, graph_proto); + + // Create LSTM node + onnx::NodeProto *lstm_node_proto = graph_proto->add_node(); + lstm_node_proto->set_op_type("LSTM"); + lstm_node_proto->add_input(x_input_name); + lstm_node_proto->add_input(onnx_input_weights_name); + lstm_node_proto->add_input(onnx_hidden_weights_name); + lstm_node_proto->add_input(has_bias ? onnx_bias_name : ""); + lstm_node_proto->add_input(""); // seqlens + lstm_node_proto->add_input(init_h_input_name); + lstm_node_proto->add_input(init_c_input_name); + + auto Y_output_name = node_name + "_Y"; + lstm_node_proto->add_output(Y_output_name); + lstm_node_proto->add_output(MakeOutputName(node_name, kOneNum)); + lstm_node_proto->add_output(MakeOutputName(node_name, kTwoNum)); + + onnx::AttributeProto *hidden_size_proto = lstm_node_proto->add_attribute(); + hidden_size_proto->set_name("hidden_size"); + hidden_size_proto->set_type(onnx::AttributeProto_AttributeType_INT); + hidden_size_proto->set_i(hidden_size); + + onnx::AttributeProto *direction_proto = lstm_node_proto->add_attribute(); + direction_proto->set_name("direction"); + direction_proto->set_type(onnx::AttributeProto_AttributeType_STRING); + direction_proto->set_s(direction); + + // Transpose 1st output of the LSTM node + onnx::NodeProto *transpose_node_proto = graph_proto->add_node(); + auto transpose_node_name = node_name + "_Y_transposed"; + transpose_node_proto->set_name(transpose_node_name); + transpose_node_proto->set_op_type("Transpose"); + transpose_node_proto->add_input(Y_output_name); + transpose_node_proto->add_output(transpose_node_name); + + onnx::AttributeProto *perm_proto = transpose_node_proto->add_attribute(); + perm_proto->set_name("perm"); + perm_proto->set_type(onnx::AttributeProto_AttributeType_INTS); + perm_proto->add_ints(kZeroNum); + perm_proto->add_ints(kTwoNum); + perm_proto->add_ints(kOneNum); + perm_proto->add_ints(kThreeNum); + + // Reshape + auto output_name = MakeOutputName(node_name, kZeroNum); + AddReshapeOp(transpose_node_name, output_name, {seq_len, batch_size, num_dir * hidden_size}, graph_proto); +} + +void OnnxExporter::ExportPrimReverseV2(const FuncGraphPtr &, const CNodePtr &node, + std::map *node_map_ptr, + onnx::GraphProto *const graph_proto) { + auto output = RegisterNodeWithUniqueName(node, node_map_ptr); + auto input = GetNodeInputName(node->input(kOneNum), node_map_ptr, graph_proto); + + auto axes_ptr = GetOpAttributePtr(node, "axis"); + auto axes_vec = GetValue>(axes_ptr); + size_t n_axes = axes_vec.size(); + auto shape = dyn_cast(node->input(kOneNum)->Shape())->shape(); + + std::vector starts_vec(n_axes, -1); + std::vector ends_vec(n_axes); + (void)std::transform(axes_vec.begin(), axes_vec.end(), ends_vec.begin(), + [&shape](size_t ax) { return -shape.at(ax) - 1; }); + std::vector steps_vec(n_axes, -1); + + AddSliceOp(input, output, starts_vec, ends_vec, axes_vec, steps_vec, graph_proto); +} + +void OnnxExporter::ExportPrimTensorCopySlices(const FuncGraphPtr &, const CNodePtr &node, + std::map *node_map_ptr, + onnx::GraphProto *graph_proto) { + auto node_name = RegisterNodeWithUniqueName(node, node_map_ptr); + + auto x_input = node->input(kOneNum); + auto value_input = node->input(kTwoNum); + + auto x_input_name = GetNodeInputName(x_input, node_map_ptr, graph_proto); + auto value_input_name = GetNodeInputName(value_input, node_map_ptr, graph_proto); + + const auto &x_shape = dyn_cast(x_input->Shape())->shape(); + const auto &value_shape = dyn_cast(value_input->Shape())->shape(); + + auto begin_node = dyn_cast(node->input(kThreeNum)); + MS_EXCEPTION_IF_NULL(begin_node); + auto begin = GetValue>(begin_node->value()); + + auto end_node = dyn_cast(node->input(kFourNum)); + MS_EXCEPTION_IF_NULL(end_node); + auto end = GetValue>(end_node->value()); + + auto strides_node = dyn_cast(node->input(kFiveNum)); + MS_EXCEPTION_IF_NULL(strides_node); + auto strides = GetValue>(strides_node->value()); + + MS_EXCEPTION_IF_CHECK_FAIL( + begin.size() == end.size() && end.size() == strides.size() && strides.size() <= x_shape.size(), + "Sizes of begin, end, and strides must be equal"); + // MindSpore only allows contuguous slices of memory + // Contiguous slice size follows the pattern: [1, ..., 1, n, :, ..., :] + bool found_slice = false; + for (size_t i = 0; i < begin.size(); ++i) { + int64_t dim = end[i] - begin[i]; + if (!found_slice && dim != 1) { + found_slice = true; + } else if (found_slice && dim != x_shape[i]) { + MS_LOG(EXCEPTION) << "Slice must be contiguous"; + } + } + for (auto stride : strides) { + MS_EXCEPTION_IF_CHECK_FAIL(stride == 1, "Slice must be contiguous"); + } + + int64_t flat_begin_index = RavelIndex(begin, x_shape); + + std::vector end_inclusive; + (void)std::transform(end.begin(), end.end(), std::back_inserter(end_inclusive), [](auto x) { return x - 1; }); + (void)std::transform(x_shape.begin() + end.size(), x_shape.end(), std::back_inserter(end_inclusive), + [](auto x) { return x - 1; }); + int64_t flat_end_index = RavelIndex(end_inclusive, x_shape) + 1; + + int64_t x_size = std::accumulate(x_shape.begin(), x_shape.end(), 1, std::multiplies()); + int64_t value_size = std::accumulate(value_shape.begin(), value_shape.end(), 1, std::multiplies()); + MS_EXCEPTION_IF_CHECK_FAIL(value_size == flat_end_index - flat_begin_index, "Cannot copy 'value' to target slice"); + + auto flat_x_name = node_name + "_flat_x"; + AddReshapeOp(x_input_name, flat_x_name, {-1}, graph_proto); + auto begin_slice_name = node_name + "_begin_slice"; + AddSliceOp(flat_x_name, begin_slice_name, {0}, {static_cast(flat_begin_index)}, {0}, {1}, graph_proto); + auto end_slice_name = node_name + "_end_slice"; + AddSliceOp(flat_x_name, end_slice_name, {static_cast(flat_end_index)}, {x_size}, {0}, {1}, graph_proto); + + auto flat_value_name = node_name + "_flat_value"; + AddReshapeOp(value_input_name, flat_value_name, {-1}, graph_proto); + + auto flat_result_name = node_name + "_flat_result"; + AddConcatOp({begin_slice_name, flat_value_name, end_slice_name}, flat_result_name, 0, graph_proto); + AddReshapeOp(flat_result_name, node_name, x_shape, graph_proto); +} + +void OnnxExporter::ExportPrimStack(const FuncGraphPtr &, const CNodePtr &node, + std::map *node_map_ptr, onnx::GraphProto *graph_proto) { + auto node_name = RegisterNodeWithUniqueName(node, node_map_ptr); + + auto input_name = GetNodeInputName(node->input(kOneNum), node_map_ptr, graph_proto); + + onnx::NodeProto *node_proto = graph_proto->add_node(); + node_proto->set_name(node_name + "Stack"); + node_proto->set_op_type("ConcatFromSequence"); + node_proto->add_input(input_name); + node_proto->add_output(node_name); + + onnx::AttributeProto *axis_proto = node_proto->add_attribute(); + axis_proto->set_name("axis"); + axis_proto->set_type(onnx::AttributeProto_AttributeType_INT); + axis_proto->set_i(GetOpAttribute(node, "axis")); + + onnx::AttributeProto *new_axis_proto = node_proto->add_attribute(); + new_axis_proto->set_name("new_axis"); + new_axis_proto->set_type(onnx::AttributeProto_AttributeType_INT); + new_axis_proto->set_i(true); +} + +void OnnxExporter::ExportCNode(const FuncGraphPtr &func_graph, const CNodePtr &node, + std::map *node_map_ptr, onnx::GraphProto *const graph_proto) { + using ExportFunc = std::function *, onnx::GraphProto *const)>; + static std::vector> export_table = { + {prim::kPrimReshape, &OnnxExporter::ExportPrimReshape}, + {prim::kPrimReduceMean, &OnnxExporter::ExportPrimReduce}, + {prim::kPrimReduceSum, &OnnxExporter::ExportPrimReduce}, + {prim::kPrimTranspose, &OnnxExporter::ExportPrimTranspose}, + {prim::kPrimStridedSlice, &OnnxExporter::ExportPrimStridedSlice}, + {prim::kPrimResizeNearestNeighbor, &OnnxExporter::ExportPrimResizeNearestNeighbor}, + {prim::kPrimResizeBilinear, &OnnxExporter::ExportPrimResizeBilinear}, + {prim::kPrimConcat, &OnnxExporter::ExportPrimConcat}, + {prim::kPrimCast, &OnnxExporter::ExportPrimCast}, + {prim::kPrimPRelu, &OnnxExporter::ExportPrimPReLU}, + {prim::kPrimRelu6, &OnnxExporter::ExportPrimReLU6}, + {prim::kPrimDepthwiseConv2dNative, &OnnxExporter::ExportPrimDepthwiseConv2d}, + {prim::kPrimTile, &OnnxExporter::ExportPrimTile}, + {prim::kPrimSquare, &OnnxExporter::ExportPrimSquare}, + {prim::kPrimGather, &OnnxExporter::ExportPrimGatherV2}, + {prim::kPrimTupleGetItem, &OnnxExporter::ExportPrimTupleGetItem}, + {prim::kPrimTopK, &OnnxExporter::ExportPrimTopK}, + {prim::kPrimBoundingBoxDecode, &OnnxExporter::ExportPrimBoundingBoxDecode}, + {prim::kPrimNMSWithMask, &OnnxExporter::ExportPrimNMSWithMask}, + {prim::kPrimSplit, &OnnxExporter::ExportPrimSplit}, + {prim::kPrimROIAlign, &OnnxExporter::ExportPrimROIAlign}, + {prim::kPrimSlice, &OnnxExporter::ExportPrimSlice}, + {prim::kPrimOnesLike, &OnnxExporter::ExportPrimOnesLike}, + {prim::kPrimArgMaxWithValue, &OnnxExporter::ExportPrimArgMaxWithValue}, + {prim::kPrimOneHot, &OnnxExporter::ExportPrimOneHot}, + {prim::kPrimConv2DTranspose, &OnnxExporter::ExportPrimConv2DTranspose}, + {prim::kPrimGreaterEqual, &OnnxExporter::ExportPrimGreaterEqual}, + {prim::kPrimSqueeze, &OnnxExporter::ExportPrimSqueeze}, + {prim::kPrimExpandDims, &OnnxExporter::ExportPrimExpandDims}, + {prim::kPrimPad, &OnnxExporter::ExportPrimPad}, + {prim::kPrimBatchMatMul, &OnnxExporter::ExportPrimBatchMatMul}, + {prim::kPrimGeLU, &OnnxExporter::ExportPrimGeLU}, + {prim::kPrimLstm, &OnnxExporter::ExportPrimLSTM}, + {prim::kPrimReverseV2, &OnnxExporter::ExportPrimReverseV2}, + {prim::kPrimTensorCopySlices, &OnnxExporter::ExportPrimTensorCopySlices}, + {prim::kPrimStack, &OnnxExporter::ExportPrimStack}, + }; + + auto iter = std::find_if(export_table.begin(), export_table.end(), + [&node](const auto &item) { return node->IsApply(item.first); }); + if (iter != export_table.end()) { + iter->second(this, func_graph, node, node_map_ptr, graph_proto); + return; + } + + auto inputs = node->inputs(); + if (inputs.size() < 1) { + MS_LOG(EXCEPTION) << "Inputs of apply node is empty"; + } + + AnfNodePtr op = inputs[kZeroNum]; + std::vector op_inputs; + // first process node input 1,2,..., since when node input is a ValueNode, here need to create a Constant Operator + for (size_t i = 1; i < inputs.size(); i++) { + if (!HasAbstractMonad(inputs[i])) { + op_inputs.push_back(inputs[i]); + } + } + + if (!op->isa()) { + MS_LOG(EXCEPTION) << "Need to support node op type " << op->type_name(); + } + + auto op_value = dyn_cast(op)->value(); + if (op_value->isa()) { + auto prim = dyn_cast(op_value); + (*node_map_ptr)[node] = ExportPrimitive(func_graph, node_map_ptr, prim, op_inputs, graph_proto); + } else if (while_loop_export::IsControlSubgraph(op_value)) { + ExportWhileLoop(node, node_map_ptr, graph_proto); + } else { + MS_LOG(EXCEPTION) << "Need to support node op value type " << op_value->type_name(); + } +} + +void OnnxExporter::ExportWhileLoop(const CNodePtr &start_node, std::map *node_map_ptr, + onnx::GraphProto *graph_proto) { + auto node_name = RegisterNodeWithUniqueName(start_node, node_map_ptr); + auto loop_parts = while_loop_export::MatchGraph(start_node); + + // 1. Make Loop op + + onnx::NodeProto *loop_proto = graph_proto->add_node(); + loop_proto->set_op_type("Loop"); + + auto loop_count_name = node_name + "_M"; + const auto &loop_counter_params = loop_parts.loop_condition_info; + int64_t loop_count = (loop_counter_params.end - loop_counter_params.begin) / loop_counter_params.step; + onnx::TensorProto *loop_count_proto = graph_proto->add_initializer(); + loop_count_proto->set_name(loop_count_name); + loop_count_proto->set_data_type(onnx::TensorProto_DataType_INT64); + loop_count_proto->add_int64_data(loop_count); + + auto loop_cond_name = node_name + "_cond"; + auto *cond_value = graph_proto->add_initializer(); + cond_value->set_name(loop_cond_name); + cond_value->set_data_type(onnx::TensorProto_DataType_BOOL); + cond_value->add_int32_data(true); + + loop_proto->add_input(loop_count_name); + loop_proto->add_input(loop_cond_name); + for (const auto &[loop_i, control_i] : loop_parts.used_loop_to_control_param_indices) { + auto name = GetNodeInputName(start_node->input(control_i + 1), node_map_ptr, graph_proto); + loop_proto->add_input(name); + loop_proto->add_output(MakeOutputName(node_name + "_loop", loop_i)); + } + + onnx::AttributeProto *subgraph_attr = loop_proto->add_attribute(); + subgraph_attr->set_type(onnx::AttributeProto_AttributeType_GRAPH); + subgraph_attr->set_name("body"); + onnx::GraphProto *loop_subgraph_proto = subgraph_attr->mutable_g(); + + // 2. Create subgraph for loop body + + auto subgraph_name = loop_parts.loop_subgraph->ToString(); + auto subgraph_input_cond_name = subgraph_name + "_input_cond"; + + auto *iter_num_input = loop_subgraph_proto->add_input(); + iter_num_input->set_name(subgraph_name + "_input_M"); + (void)iter_num_input->mutable_type()->mutable_tensor_type()->mutable_shape(); // side-effect: shape created + iter_num_input->mutable_type()->mutable_tensor_type()->set_elem_type(onnx::TensorProto_DataType_INT64); + + auto *cond_input = loop_subgraph_proto->add_input(); + cond_input->set_name(subgraph_input_cond_name); + cond_input->mutable_type()->mutable_tensor_type()->set_elem_type(cond_value->data_type()); + + auto *cond_output = loop_subgraph_proto->add_output(); + cond_output->set_name(cond_input->name()); + cond_output->mutable_type()->mutable_tensor_type()->set_elem_type(cond_value->data_type()); + + MS_EXCEPTION_IF_CHECK_FAIL(renamed_node_map_.empty(), "renamed_nodes must be cleared after subgraph export"); + for (size_t i : loop_parts.ignored_loop_param_indices) { + const auto ¶m = loop_parts.loop_subgraph->parameters().at(i); + renamed_node_map_[param] = ""; + } + + // Export everything except the control call and the output (see MatchAndMark) + ExportFuncGraph(loop_parts.loop_subgraph, node_map_ptr, loop_subgraph_proto); + + // Export outputs manually + for (const auto &loop_to_control_i : loop_parts.used_loop_to_control_param_indices) { + const auto &input = loop_parts.repeat_node->input(loop_to_control_i.second + 1); + ExportOutput(loop_parts.loop_subgraph, input, node_map_ptr, loop_subgraph_proto); + } + renamed_node_map_.clear(); + + // 3. Export part after loop + + MS_EXCEPTION_IF_CHECK_FAIL(renamed_node_map_.empty(), "renamed_nodes must be cleared after subgraph export"); + const auto &after_loop_params = loop_parts.after_loop_subgraph->parameters(); + for (const auto &[after_i, output_i] : loop_parts.after_param_to_output_indices) { + MS_EXCEPTION_IF_CHECK_FAIL(static_cast(output_i) < loop_proto->output_size(), "Output index out of bounds"); + renamed_node_map_[after_loop_params.at(after_i)] = loop_proto->output(output_i); + } + ExportFuncGraph(loop_parts.after_loop_subgraph, node_map_ptr, graph_proto, false); + + auto after_loop_retval = GetRealInput(loop_parts.after_loop_subgraph->get_return()->input(1)); + if (after_loop_retval->isa() && after_loop_retval->cast()->IsApply(prim::kPrimMakeTuple)) { + auto tuple_retval = dyn_cast(after_loop_retval); + for (size_t i = 1; i < tuple_retval->inputs().size(); ++i) { + auto output_name = GetNodeInputName(tuple_retval->input(i), node_map_ptr, graph_proto); + AddOp("Identity", {output_name}, {MakeOutputName(node_name, i - 1)}, graph_proto); + } + } else { + auto output_name = GetNodeInputName(after_loop_retval, node_map_ptr, graph_proto); + AddOp("Identity", {output_name}, {node_name}, graph_proto); + } + renamed_node_map_.clear(); +} + +onnx::TensorProto_DataType OnnxExporter::GetOutputType(const AnfNodePtr &node, int64_t output_index) { + auto unpacked = GetRealInput(node); + if (IsPrimitiveCNode(unpacked, prim::kPrimTupleGetItem)) { + if (output_index != -1) { + MS_LOG(EXCEPTION) << "Unexpected output index for TupleGetItem: " << output_index; + } + auto cnode = dyn_cast(unpacked); + unpacked = cnode->input(kOneNum); + output_index = GetInt64Value(cnode->input(kTwoNum)); + } + + /* + Special cases (MS and ONNX type differences) go here + Example: + if (IsPrimitiveCNode(unpacked, prim::kPrim) && output_index == ) { + return onnx::TensorProto_DataType_; + } + */ + + if (output_index == -1) { + auto tensor = dyn_cast(unpacked->Type()); + if (tensor == nullptr) { + MS_LOG(EXCEPTION) << "Expected output of node " << unpacked->ToString() + << " to be a single tensor. Instead got: " << unpacked->Type()->ToString(); + } + return GetOnnxDataType(tensor->element()->type_id()); + } else { + auto tuple_type = dyn_cast(unpacked->Type()); + if (tuple_type == nullptr) { + MS_LOG(EXCEPTION) << "Expected output of node " << unpacked->ToString() + << " to be a tuple. Instead got: " << unpacked->Type()->ToString(); + } + auto element_type = tuple_type->elements()[static_cast(output_index)]; + MS_EXCEPTION_IF_NULL(element_type); + auto tensor_type = dyn_cast(element_type); + if (tensor_type == nullptr) { + MS_LOG(EXCEPTION) << "Expected output " << output_index << " of node " << unpacked->ToString() + << " to be a tensor. Instead got: " << element_type->ToString(); + } + return GetOnnxDataType(tensor_type->element()->type_id()); + } +} + +void OnnxExporter::AddOutputWithCast(onnx::NodeProto *node_proto, const std::string &output_name, + onnx::TensorProto_DataType target_type, onnx::GraphProto *graph_proto) const { + if (target_type == onnx::TensorProto_DataType_UNDEFINED) { + node_proto->add_output(output_name); + } else { + auto output_to_cast_name = output_name + "_output_to_cast"; + node_proto->add_output(output_to_cast_name); + AddCastOp(output_to_cast_name, output_name, target_type, graph_proto); + } +} + +std::string OnnxExporter::ExportPrimitive(const FuncGraphPtr &, std::map *node_map_ptr, + const PrimitivePtr &prim, const std::vector &inputs, + onnx::GraphProto *const graph_proto) { + auto op_map = OpConvertRegistry::GetOpConvertMap(); + MS_EXCEPTION_IF_NULL(prim); + auto op_iter = op_map.find(prim->name()); + if (op_iter == op_map.end()) { + MS_LOG(EXCEPTION) << "Can not find key " << prim->name() << " in convert map. " + << "Exporting " << prim->name() << " operator is not yet supported."; + } + // Get input first, because input maybe valuenode which need create constant node + std::vector input_list; + for (const auto &input : inputs) { + auto input_name = GetNodeInputName(input, node_map_ptr, graph_proto); + input_list.push_back(input_name); + } + + const OpNameInfo &op_convert_info = op_iter->second; + auto node_name = GenerateUniqueName(); + + std::vector output_cast_types(op_convert_info.num_outputs(), + onnx::TensorProto_DataType_UNDEFINED); + // Cast inputs if needed + for (const auto &rule : op_convert_info.input_casts()) { + auto original_type = GetOutputType(inputs[static_cast(rule.input_index)]); + if (original_type != rule.input_type) { + continue; + } + + auto cast_input_name = node_name + "cast_input_" + std::to_string(rule.input_index); + AddCastOp(input_list[static_cast(rule.input_index)], cast_input_name, rule.target_type, graph_proto); + input_list[static_cast(rule.input_index)] = cast_input_name; + + auto output_cast = std::find_if( + op_convert_info.output_casts().begin(), op_convert_info.output_casts().end(), [&rule](const OutputConversion &x) { + return x.mode == OutputConversion::Mode::INPUT && x.input_with_matching_type == rule.input_index; + }); + if (output_cast != op_convert_info.output_casts().end()) { + output_cast_types[static_cast(output_cast->output_index)] = original_type; + } + } + + for (const auto &output_cast : op_convert_info.output_casts()) { + if (output_cast.mode == OutputConversion::Mode::FIXED) { + output_cast_types[static_cast(output_cast.output_index)] = output_cast.target_type; + } + } + + onnx::NodeProto *node_proto = graph_proto->add_node(); + node_proto->set_name(node_name + op_convert_info.onnx_type()); + node_proto->set_op_type(op_convert_info.onnx_type()); + + // Set outputs + if (op_convert_info.num_outputs() == 1) { + AddOutputWithCast(node_proto, node_name, output_cast_types[0], graph_proto); + } else { + for (int i = 0; i < op_convert_info.num_outputs(); ++i) { + auto output_name = MakeOutputName(node_name, i); + AddOutputWithCast(node_proto, output_name, output_cast_types[static_cast(i)], graph_proto); + } + } + + // Set inputs + for (const auto &input_name : input_list) { + node_proto->add_input(input_name); + } + + // Set node attribute + for (const OpAttrInfo &attr : op_convert_info.op_attrs()) { + const std::string &attr_name = attr.attr_name(); + ValuePtr attr_value = nullptr; + if (!attr_name.empty()) { + attr_value = prim->GetAttr(attr_name); + if (attr_value == nullptr) { + MS_LOG(EXCEPTION) << "Primitive " << prim->name() << " does not have attribute " << attr_name; + } + } + onnx::AttributeProto *onnx_attr_proto = node_proto->add_attribute(); + onnx_attr_proto->set_name(attr.onnx_attr_name()); + attr.fn_gen_attr()(attr_value, attr.onnx_attr_type(), onnx_attr_proto, prim); + } + return node_name; +} + +void OnnxExporter::ExportMergeConv(const FuncGraphPtr &func_graph, const CNodePtr &node, + std::map *node_map_ptr, + onnx::GraphProto *const graph_proto) { + auto conv_node = dyn_cast(node->input(kOneNum)); + auto input_x = conv_node->input(kOneNum); // conv input x + auto input_w = conv_node->input(kTwoNum); // conv weight(filter) + auto input_b = node->input(kTwoNum); // conv bias + + PrimitivePtr prim_conv = dyn_cast((dyn_cast(conv_node->input(kZeroNum)))->value()); + std::vector inputs{input_x, input_w, input_b}; + (*node_map_ptr)[node] = ExportPrimitive(func_graph, node_map_ptr, prim_conv, inputs, graph_proto); +} + +void OnnxExporter::ExportMergeGemm(const FuncGraphPtr &func_graph, const CNodePtr &node, + std::map *node_map_ptr, + onnx::GraphProto *const graph_proto) { + auto matmul_node = dyn_cast(node->input(kOneNum)); + auto input_x = matmul_node->input(kOneNum); // matmul input x + auto input_y = matmul_node->input(kTwoNum); // matmul input y + auto input_b = node->input(kTwoNum); // matmul bias + + PrimitivePtr prim_matmul = dyn_cast((dyn_cast(matmul_node->input(kZeroNum)))->value()); + std::vector inputs{input_x, input_y, input_b}; + (*node_map_ptr)[node] = ExportPrimitive(func_graph, node_map_ptr, prim_matmul, inputs, graph_proto); +} + +void OnnxExporter::ExportMergeBatchNorm(const FuncGraphPtr &func_graph, const CNodePtr &node, + std::map *node_map_ptr, + onnx::GraphProto *const graph_proto) { + auto batch_norm_node = dyn_cast(node->input(kOneNum)); + + auto is_training = GetOpAttribute(batch_norm_node, "is_training"); + if (is_training) { + auto input_x_name = GetNodeInputName(batch_norm_node->input(kOneNum), node_map_ptr, graph_proto); + auto scale_input_name = GetNodeInputName(batch_norm_node->input(kTwoNum), node_map_ptr, graph_proto); + auto bias_input_name = GetNodeInputName(batch_norm_node->input(kThreeNum), node_map_ptr, graph_proto); + + auto onnx_type = GetOutputType(batch_norm_node->input(kOneNum)); + + auto output_name = RegisterNodeWithUniqueName(node, node_map_ptr); + + auto input_shape_ptr = batch_norm_node->input(kOneNum)->Shape(); + auto input_shape = input_shape_ptr->cast()->shape(); + + std::vector normalize_axes = {0}; + for (size_t i = kTwoNum; i < input_shape.size(); ++i) { + normalize_axes.push_back(static_cast(i)); + } + + std::vector scale_bias_shape(input_shape.size(), 1); + scale_bias_shape[1] = -1; + auto reshaped_scale_name = output_name + "_reshaped_scale"; + AddReshapeOp(scale_input_name, reshaped_scale_name, scale_bias_shape, graph_proto); + auto reshaped_bias_name = output_name + "_reshaped_bias"; + AddReshapeOp(bias_input_name, reshaped_bias_name, scale_bias_shape, graph_proto); + auto epsilon = GetOpAttribute(batch_norm_node, "epsilon"); + + AddMeanVarianceNormalizationOp(input_x_name, reshaped_scale_name, reshaped_bias_name, output_name, normalize_axes, + epsilon, input_shape, onnx_type, graph_proto); + } else { + PrimitivePtr prim_batch_norm = GetPrimitive(batch_norm_node); + std::vector inputs; + for (size_t i = 1; i < batch_norm_node->inputs().size(); i++) { + inputs.push_back(batch_norm_node->input(i)); + } + (*node_map_ptr)[node] = ExportPrimitive(func_graph, node_map_ptr, prim_batch_norm, inputs, graph_proto); + } +} + +void OnnxExporter::ExportMergeMaxPoolWithArgmax(const FuncGraphPtr &func_graph, const CNodePtr &node, + std::map *node_map_ptr, + onnx::GraphProto *const graph_proto) { + auto maxpool_with_argmax_node = dyn_cast(node->input(kOneNum)); + + PrimitivePtr prim_maxpool_with_argmax = + dyn_cast((dyn_cast(maxpool_with_argmax_node->input(kZeroNum)))->value()); + std::vector inputs; + for (size_t i = 1; i < maxpool_with_argmax_node->inputs().size(); i++) { + inputs.push_back(maxpool_with_argmax_node->input(i)); + } + (*node_map_ptr)[node] = ExportPrimitive(func_graph, node_map_ptr, prim_maxpool_with_argmax, inputs, graph_proto); +} + +// LayerNorm(N, C1, H, W) --> reshape(1, C2, 1, W) + MeanVarianceNormalization + reshape(N, C1, H, W) +void OnnxExporter::ExportMergeLayerNorm(const FuncGraphPtr &, const CNodePtr &node, + std::map *node_map_ptr, + onnx::GraphProto *const graph_proto) { + auto LayerNormNode = dyn_cast(node->input(kOneNum)); + auto layernorm_input_x = GetNodeInputName(LayerNormNode->input(kOneNum), node_map_ptr, graph_proto); + auto layernorm_input_gamma = GetNodeInputName(LayerNormNode->input(kTwoNum), node_map_ptr, graph_proto); + auto layernorm_input_beta = GetNodeInputName(LayerNormNode->input(kThreeNum), node_map_ptr, graph_proto); + + auto begin_norm_axis = GetOpAttribute(LayerNormNode, "begin_norm_axis"); + auto begin_params_axis = GetOpAttribute(LayerNormNode, "begin_params_axis"); + if (begin_norm_axis != -1 || begin_params_axis != -1) { + MS_LOG(EXCEPTION) << "begin_norm_axis != -1 and begin_params_axis != -1 are not implemented"; + } + + auto onnx_type = GetOutputType(LayerNormNode->input(kOneNum)); + auto input_shape = dyn_cast(LayerNormNode->input(kOneNum)->Shape())->shape(); + auto node_name = RegisterNodeWithUniqueName(node, node_map_ptr); + auto epsilon = GetOpAttribute(LayerNormNode, "epsilon"); + std::vector reduce_axes = {static_cast(input_shape.size()) - 1}; + + AddMeanVarianceNormalizationOp(layernorm_input_x, layernorm_input_gamma, layernorm_input_beta, node_name, reduce_axes, + epsilon, input_shape, onnx_type, graph_proto); +} + +void OnnxExporter::ExportMergeConv2DTranspose(const FuncGraphPtr &, const CNodePtr &node, + std::map *node_map_ptr, + onnx::GraphProto *const graph_proto) { + auto conv_node = dyn_cast(node->input(kOneNum)); + PrimConv2DTransposeExportHelper(conv_node, node, node_map_ptr, graph_proto); +} + +/* + Kinds of return values: + 1) A single Tensor + 2) A Tuple returned by an op with multiple outputs like TopK + 3) A Tuple returned by MakeTuple. This corresponds to `return x, y` + or equivalent in Python, where x and y are Tensors + In this case MakeTuple itself is not exported, so this case must be handled + separately from the previous one + 4) A constant tuple (ValueNode). Example: + class MyCell(nn.Cell): + def __init__(self): + super().__init__() + self.x = ms.Tensor(np.zeros((1, 2, 3))) + + def construct(self): + return self.x, self.x + + */ +void OnnxExporter::ExportOutput(const FuncGraphPtr &, const AnfNodePtr &return_arg, + std::map *node_map_ptr, onnx::GraphProto *const graph_proto) { + AnfNodePtr arg = GetRealInput(return_arg); + if (IsPrimitiveCNode(arg, prim::kPrimMakeTuple)) { + auto arg_cnode = dyn_cast(arg); + for (size_t i = 1; i < arg_cnode->inputs().size(); ++i) { + const auto &output = arg_cnode->input(i); + auto output_name = GetNodeInputName(output, node_map_ptr, graph_proto); + onnx::ValueInfoProto *output_proto = graph_proto->add_output(); + output_proto->set_name(output_name); + SetValueInfoType(output, output_proto); + } + } else if (arg->isa() && arg->cast()->value()->isa()) { + // Several outputs, all constants + auto tuple = arg->cast()->value()->cast(); + for (size_t i = 0; i < tuple->value().size(); ++i) { + const auto &element = tuple->value().at(i); + std::string output_name = GenerateUniqueName(); + + onnx::TensorProto *initializer = graph_proto->add_initializer(); + initializer->set_name(output_name); + SetTensorData(element, initializer); + + onnx::ValueInfoProto *output_proto = graph_proto->add_output(); + output_proto->set_name(output_name); + SetValueInfoType(arg, output_proto, i); + } + } else if (arg->Type()->isa()) { + auto arg_name = GetNodeInputName(arg, node_map_ptr, graph_proto); + auto tuple = dyn_cast(arg->Type()); + + for (size_t i = 0; i < tuple->size(); ++i) { + auto output_name = MakeOutputName(arg_name, i); + onnx::ValueInfoProto *output_proto = graph_proto->add_output(); + output_proto->set_name(output_name); + SetValueInfoType(arg, output_proto, i); + } + } else if (arg->Type()->isa()) { + auto arg_name = GetNodeInputName(arg, node_map_ptr, graph_proto); + onnx::ValueInfoProto *output_proto = graph_proto->add_output(); + output_proto->set_name(arg_name); + SetValueInfoType(arg, output_proto); + } else { + MS_LOG(EXCEPTION) << "Unsupported network output type " << arg->Type()->ToString() << " in node " + << arg->ToString(); + } +} + +std::string OnnxExporter::GetNodeInputName(const AnfNodePtr &orig_node, std::map *node_map_ptr, + onnx::GraphProto *const) { + auto node = GetRealInput(orig_node); + + // if node is renamed and not ignored, use alternative name + // if it is ignored, try to find the actual name in global map + auto renamed_iter = renamed_node_map_.find(node); + if (renamed_iter != renamed_node_map_.end() && renamed_iter->second != "") { + return renamed_iter->second; + } + + auto iter = node_map_ptr->find(node); + if (iter != node_map_ptr->end()) { + return iter->second; + } + + if (node->isa() || (node->isa() && !node->cast()->has_default())) { + MS_LOG(EXCEPTION) << "Can not find node '" << node->DebugString() << "' in node_map"; + } + + // for ValueNode or Parameter with default input, create an initializer + // same value can be used in several subgraphs, so create initializers in root graph + if (node->isa()) { + auto node_name = RegisterNodeWithUniqueName(node, node_map_ptr); + auto value = node->cast()->value(); + + onnx::TensorProto *initializer_proto = model_.mutable_graph()->add_initializer(); + initializer_proto->set_name(node_name); + SetTensorData(value, initializer_proto); + + (*node_map_ptr)[node] = node_name; + return node_name; + } + + if (node->isa()) { + auto param = dyn_cast(node); + auto node_name = GenerateUniqueParameterName(param, node_map_ptr); + + onnx::TensorProto *initializer_proto = model_.mutable_graph()->add_initializer(); + initializer_proto->set_name(node_name); + SetTensorData(param->default_param(), initializer_proto); + + (*node_map_ptr)[node] = node_name; + return node_name; + } + + MS_LOG(EXCEPTION) << "Unexpected node type " << node->type_name(); +} + +void OnnxExporter::ConvertTupleToTensor(const ValuePtr &value, onnx::TensorProto *const tensor_proto) const { + auto tuple_ptr = dyn_cast(value); + MS_EXCEPTION_IF_NULL(tuple_ptr); + if (tuple_ptr->size() == 0) { + MS_LOG(EXCEPTION) << "Convert tuple to tensor fail, the size of converted tuple is 0."; + } + + ValuePtr first_element = (*tuple_ptr)[0]; + if (!first_element->isa()) { // For non-scalars x->type() contains nullptr + MS_LOG(EXCEPTION) << "Expected tuple elements to be scalars. Got: " << value->ToString(); + } + auto type_id = first_element->type()->type_id(); + for (size_t i = 1; i < tuple_ptr->size(); ++i) { + const auto element_type = (*tuple_ptr)[i]->type(); + if (element_type == nullptr || element_type->type_id() != type_id) { + MS_LOG(EXCEPTION) << "Convert tuple to tensor fail, type of tuple elements is not same."; + } + } + + onnx::TensorProto_DataType result_type = onnx::TensorProto_DataType_UNDEFINED; + if (first_element->isa()) { + result_type = onnx::TensorProto_DataType_INT64; + } else if (first_element->isa()) { + result_type = onnx::TensorProto_DataType_FLOAT; + } else { + MS_LOG(EXCEPTION) << "Convert tuple to tensor fail, unexpected tuple element type " + << first_element->type()->type_name() << "."; + } + + tensor_proto->add_dims(static_cast<::google::protobuf::int64>(tuple_ptr->size())); + tensor_proto->set_data_type(result_type); + for (size_t i = 0; i < tuple_ptr->size(); ++i) { + ValuePtr elem = (*tuple_ptr)[i]; + if (elem->isa()) { + tensor_proto->add_int64_data(dyn_cast(elem)->value()); + } else if (elem->isa()) { + tensor_proto->add_int64_data(dyn_cast(elem)->value()); + } else if (elem->isa()) { + tensor_proto->add_int64_data(dyn_cast(elem)->value()); + } else if (elem->isa()) { + tensor_proto->add_int64_data(dyn_cast(elem)->value()); + } else if (elem->isa()) { + tensor_proto->add_float_data(dyn_cast(elem)->value()); + } else { + MS_LOG(EXCEPTION) << "Convert tuple to tensor fail, unexpected tuple element type " << elem->type()->type_name() + << "."; + } + } +} + +void OnnxExporter::SetTensorData(const ValuePtr &value, onnx::TensorProto *tensor_proto) { + if (value->isa()) { + auto attr_value = dyn_cast(value)->value(); + tensor_proto->set_data_type(onnx::TensorProto_DataType_INT32); + tensor_proto->add_int32_data(attr_value); + } else if (value->isa()) { + auto attr_value = dyn_cast(value)->value(); + tensor_proto->set_data_type(onnx::TensorProto_DataType_INT64); + tensor_proto->add_int64_data(attr_value); + } else if (value->isa()) { + auto data = dyn_cast(value); + tensor_proto->set_raw_data(data->data_c(), static_cast(data->data().nbytes())); + auto dtype = data->data_type(); + auto shape = data->shape_c(); + + tensor_proto->set_data_type(GetOnnxDataType(dtype)); + for (const auto dim : shape) { + tensor_proto->add_dims(dim); + } + } else if (value->isa()) { // Note: this is a tuple of primitives, not Tensors + ConvertTupleToTensor(value, tensor_proto); + } else { + MS_LOG(EXCEPTION) << "Need to set value " << value->ToString() << " attribute for Constant node"; + } +} + +std::string GetOnnxProtoString(const FuncGraphPtr &func_graph) { + OnnxExporter exporter; + return exporter.GetOnnxProtoString(func_graph); +} +} // namespace mindspore diff --git a/mindspore/ccsrc/transform-update/op_adapter.cc b/mindspore/ccsrc/transform-update/op_adapter.cc new file mode 100644 index 00000000000..58bbb025bdd --- /dev/null +++ b/mindspore/ccsrc/transform-update/op_adapter.cc @@ -0,0 +1,824 @@ +/** + * Copyright 2019-2021 Huawei Technologies Co., Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "transform/graph_ir/op_adapter.h" +#include "utils/check_convert_utils.h" + +namespace mindspore { +namespace transform { +// 静态函数 CustomInferFunc +// 参数:const Operator &,一个操作的引用 +// 返回:uint32_t,一个无符号整数 +static uint32_t CustomInferFunc(const Operator &) { return 0; } // 这个函数返回固定值 0,可以根据具体需求实现不同的逻辑来计算返回值 + +// 该函数用于判断给定的操作是否为自定义操作 +// 参数:op,OperatorPtr类型的操作指针 +// 返回:bool类型,表示给定操作是否为自定义操作 +bool OpAdapterImpl::IsCustomOp(const OperatorPtr &op) { + MS_EXCEPTION_IF_NULL(op); // 检查操作指针是否为空,若为空则抛出异常 + auto it = cus_input_map_->find(op->GetOpType()); // 在自定义操作映射表中查找给定操作的类型 + if (it == cus_input_map_->end()) { + return false; // 若在映射表中未找到该操作类型,则返回false,表示该操作不是自定义操作 + } + return true; // 若在映射表中找到了该操作类型,则返回true,表示该操作是自定义操作 +} + +// 该函数用于生成自定义操作的输入映射表 +// 参数:op,CusOperatorPtr类型的自定义操作指针 +// 参数:prim,PrimitivePtr类型的操作原语指针 +// 返回:Status类型,表示操作执行的状态 +Status OpAdapterImpl::GenerateCustomOpInputMap(const CusOperatorPtr &op, const PrimitivePtr &prim) { + MS_EXCEPTION_IF_NULL(op); // 检查自定义操作指针是否为空,若为空则抛出异常 + MS_EXCEPTION_IF_NULL(prim); // 检查操作原语指针是否为空,若为空则抛出异常 + // Create the map of custom op from input index to input name. + // 创建用于存储自定义操作输入映射的 map,键为输入索引,值为输入名称 + mindspore::HashMap input_map; + auto value = prim->GetAttr("input_names"); // 获取操作原语中名为 "input_names" 的属性值 + if (value == nullptr) { // 如果获取的属性值为空,表示该自定义操作没有输入映射 + (*cus_output_map_)[prim->name()] = input_map; //将一个空的映射存储到 cus_output_map_ 中 + return NOT_FOUND; //返回 NOT_FOUND 状态 + } + // 将属性值转换为 std::vector 类型 + auto input_names = GetValue>(value); + for (size_t i = 0; i < input_names.size(); ++i) { // 遍历输入名称列表,将索引与输入名称的映射存储到 input_map 中,并将输入名称注册到自定义操作中 + // input_map begin form 1 + // 输入索引从 1 开始 + input_map[i + 1] = input_names[i]; + op->CustomInputRegister(input_names[i]); + } + // 如果在 cus_input_map_ 中未找到该自定义操作的输入映射,则将 input_map 存储到 cus_input_map_ 中 + if (cus_input_map_->find(prim->name()) == cus_input_map_->end()) { + (*cus_input_map_)[prim->name()] = input_map; + } + return SUCCESS; // 操作执行成功,返回 SUCCESS 状态 +} + +// 该函数用于生成自定义操作的输出映射表 +// 参数:op,CusOperatorPtr类型的自定义操作指针 +// 参数:prim,PrimitivePtr类型的操作原语指针 +// 返回:Status类型,表示操作执行的状态 +Status OpAdapterImpl::GenerateCustomOpOutputMap(const CusOperatorPtr &op, const PrimitivePtr &prim) { + MS_EXCEPTION_IF_NULL(op); // 检查自定义操作指针是否为空,若为空则抛出异常 + MS_EXCEPTION_IF_NULL(prim); // 检查操作原语指针是否为空,若为空则抛出异常 + // Create the map of custom op from output index to output name. + // 创建用于存储自定义操作输出映射的 map,键为输出索引,值为输出名称 + mindspore::HashMap output_map; + // 获取操作原语中名为 "output_names" 的属性值 + auto value = prim->GetAttr("output_names"); + if (value == nullptr) { // 如果获取的属性值为空,表示该自定义操作没有输出映射 + // generate a empty output_map for it + (*cus_output_map_)[prim->name()] = output_map; //将一个空的映射存储到 cus_output_map_ 中 + return NOT_FOUND; //返回 NOT_FOUND 状态 + } + // 将属性值转换为 std::vector 类型 + auto output_names = GetValue>(value); + for (size_t i = 0; i < output_names.size(); ++i) { // 遍历输出名称列表,将索引与输出名称的映射存储到 output_map 中,并将输出名称注册到自定义操作中 + // output_map begin form 0 + // 输出索引从 0 开始 + output_map[i] = output_names[i]; + op->CustomOutputRegister(output_names[i]); + } + // 如果在 cus_output_map_ 中未找到该自定义操作的输出映射,则将 output_map 存储到 cus_output_map_ 中 + if (cus_output_map_->find(prim->name()) == cus_output_map_->end()) { + (*cus_output_map_)[prim->name()] = output_map; + } + return SUCCESS; // 操作执行成功,返回 SUCCESS 状态 +} + +// 该函数用于生成自定义操作 +// 参数:anf,AnfNodePtr类型的节点指针 +// 返回:OperatorPtr类型,表示生成的自定义操作指针 +OperatorPtr OpAdapterImpl::GenerateCustomOp(const AnfNodePtr anf) { + MS_EXCEPTION_IF_NULL(anf); // 检查节点指针是否为空,若为空则抛出异常 + auto node = anf->cast(); // 将节点转换为 CNodePtr 类型 + if (node == nullptr) { // 如果转换失败,返回空指针 + return nullptr; + } + + if (node->inputs().empty()) { // 如果节点输入为空,抛出异常 + MS_LOG(EXCEPTION) << "length of node inputs is empty"; + } + + auto prim = GetValueNode(node->inputs()[0]); // 获取节点的原语指针 + MS_EXCEPTION_IF_NULL(prim); // 检查原语指针是否为空,若为空则抛出异常 + // 创建 ge::CustomOperator 类型的自定义操作,并传入节点的全名和原语的名称 + auto op = std::make_shared(node->fullname_with_scope(), prim->name()); + // 生成自定义操作的输入映射表,并注册到自定义操作中 + if (GenerateCustomOpInputMap(op, prim) != SUCCESS) { + MS_LOG(WARNING) << "Custom op node has no input_names, op[" << prim->name() << "]."; + } + // 生成自定义操作的输出映射表,并注册到自定义操作中 + if (GenerateCustomOpOutputMap(op, prim) != SUCCESS) { + MS_LOG(WARNING) << "Custom op node has no output_names, op[" << prim->name() << "]."; + } + // 注册自定义推理函数 + op->CustomInferFuncRegister(CustomInferFunc); + // 返回生成的自定义操作指针 + return op; +} + +// 该函数用于设置操作的子图函数 +// 参数:op,OperatorPtr类型的操作指针 +// 参数:index,int类型的索引值 +// 参数:branches,std::shared_ptr>类型的子图指针 +// 返回:Status类型,表示操作执行的状态 +Status OpAdapterImpl::SetOpSubgraphFunc(const OperatorPtr &op, int index, + const std::shared_ptr> &branches) { + MS_EXCEPTION_IF_NULL(op); // 检查操作指针是否为空,若为空则抛出异常 + auto it = dyn_subgraph_map_.find(index); // 在动态子图映射表中查找给定索引的映射 + if (it != dyn_subgraph_map_.end()) { // 如果找到了映射 + auto size = branches->size(); // 获取子图指针中的子图数量 + it->second.create_dyn_subgraph(op, static_cast(size)); // 创建操作的动态子图 + for (size_t i = 0; i < size; i++) { // 设置操作的子图 + it->second.set_subgraph(op, static_cast(i), std::make_shared((*branches)[i])); + } + return SUCCESS; // 操作执行成功,返回 SUCCESS 状态 + } + return NOT_FOUND; // 未找到对应的映射,返回 NOT_FOUND 状态 +} + +// 该函数用于设置自定义操作的输入 +// 参数:op,CusOperatorPtr类型的自定义操作指针 +// 参数:index,int类型的输入索引 +// 参数:input,OperatorPtr类型的输入操作指针 +// 返回:Status类型,表示操作执行的状态 +Status OpAdapterImpl::SetCustomOpInput(const CusOperatorPtr &op, int index, const OperatorPtr &input) { + MS_EXCEPTION_IF_NULL(op); // 检查自定义操作指针是否为空,若为空则抛出异常 + MS_EXCEPTION_IF_NULL(input); // 检查输入操作指针是否为空,若为空则抛出异常 + auto it = cus_input_map_->find(op->GetOpType()); // 在自定义操作输入映射表中查找给定自定义操作类型的映射 + if (it == cus_input_map_->end()) { // 如果未找到映射,则返回 NOT_FOUND 状态 + return NOT_FOUND; + } + mindspore::HashMap &input_map = it->second; // 获取自定义操作输入映射表 + + if ((input_map.find(index) != input_map.end())) { // 如果在输入映射表中找到给定索引的映射 + MS_LOG(DEBUG) << "Link op " << input->GetName() << " to " << op->GetName() << ":" << input_map[index]; + (void)op->SetInput(input_map[index], *input); // 设置自定义操作的输入 + return SUCCESS; // 操作执行成功,返回 SUCCESS 状态 + } + return NOT_FOUND; // 未找到给定索引的映射,返回 NOT_FOUND 状态 +} + +// 该函数用于设置普通操作的输入 +// 参数:op,OperatorPtr类型的普通操作指针 +// 参数:index,int类型的输入索引 +// 参数:input,OperatorPtr类型的输入操作指针 +// 返回:Status类型,表示操作执行的状态 +Status OpAdapterImpl::SetNormalOpInput(const OperatorPtr &op, int index, const OperatorPtr &input) { + MS_EXCEPTION_IF_NULL(op); // 检查普通操作指针是否为空,若为空则抛出异常 + auto it = input_map_.find(index); // 在输入映射表中查找给定索引的映射 + if (input != nullptr && it != input_map_.end()) { // 如果输入操作指针不为空,并且找到了索引的映射 + MS_LOG(DEBUG) << "Link op " << input->GetName() << " to " << op->GetName() << ":" << it->second.name; + it->second.set_op(op, input); // 设置普通操作的输入 + return SUCCESS; // 操作执行成功,返回 SUCCESS 状态 + } + return NOT_FOUND; // 输入操作指针为空或未找到给定索引的映射,返回 NOT_FOUND 状态 +} + +// 该函数用于设置操作的输入 +// 参数:op,OperatorPtr类型的操作指针 +// 参数:index,int类型的输入索引 +// 参数:input,OperatorPtr类型的输入操作指针 +// 返回:int类型,表示操作执行的状态,其中非负数为成功状态,负数为失败状态 +int OpAdapterImpl::setInput(const OperatorPtr &op, int index, const OperatorPtr &input) { + if (IsCustomOp(op)) { // 如果是自定义操作 + auto cus_op = std::dynamic_pointer_cast(op); // 将操作指针转换为自定义操作指针 + return static_cast(SetCustomOpInput(cus_op, index, input)); // 调用 SetCustomOpInput 设置自定义操作的输入,并返回状态 + } else { // 如果是普通操作 + return static_cast(SetNormalOpInput(op, index, input));// 调用 SetNormalOpInput 设置普通操作的输入,并返回状态 + } +} + +// 该函数用于设置自定义操作的输入 +// 参数:op,CusOperatorPtr类型的自定义操作指针 +// 参数:index,int类型的输入索引 +// 参数:handle,OutHandler类型的输出处理器 +// 返回:Status类型,表示操作执行的状态 +Status OpAdapterImpl::SetCustomOpInput(const CusOperatorPtr &op, int index, const OutHandler &handle) { + MS_EXCEPTION_IF_NULL(op); // 检查自定义操作指针是否为空,若为空则抛出异常 + auto it = cus_input_map_->find(op->GetOpType()); // 在输入映射表中查找给定自定义操作类型的映射 + if (it == cus_input_map_->end()) { // 如果未找到给定自定义操作类型的映射 + return NOT_FOUND; // 返回 NOT_FOUND 状态,表示未找到该类型的自定义操作 + } + + mindspore::HashMap &input_map = it->second; // 获取输入映射表中该自定义操作类型的映射 + if ((handle.op != nullptr) && (input_map.find(index) != input_map.end())) { // 如果输出处理器中的操作指针不为空,并且找到了给定索引的映射 + if (handle.out.empty()) { // 如果输出处理器中输出名称为空 + MS_LOG(DEBUG) << "Link op " << handle.op->GetName() << " to " << op->GetName() << ":" << input_map[index]; + (void)op->SetInput(input_map[index], *(handle.op)); // 设置自定义操作的输入 + } else { // 如果输出处理器中输出名称不为空 + MS_LOG(DEBUG) << "Link op " << handle.op->GetName() << ":" << handle.out << " to " << op->GetName() << ":" + << input_map[index]; + (void)op->SetInput(input_map[index], *(handle.op), handle.out); // 设置自定义操作的输入和输出名称 + } + return SUCCESS; // 操作执行成功,返回 SUCCESS 状态 + } + return NOT_FOUND; // 输出处理器中的操作指针为空或未找到给定索引的映射,返回 NOT_FOUND 状态 +} + +// 该函数用于设置普通操作的输入 +// 参数:op,OperatorPtr类型的操作指针 +// 参数:index,int类型的输入索引 +// 参数:handle,OutHandler类型的输出处理器 +// 返回:Status类型,表示操作执行的状态 +Status OpAdapterImpl::SetNormalOpInput(const OperatorPtr &op, int index, const OutHandler &handle) { + MS_EXCEPTION_IF_NULL(op); // 检查操作指针是否为空,若为空则抛出异常 + auto it = input_map_.find(index); // 在输入映射表中查找给定索引的映射 + if ((handle.op != nullptr) && (it != input_map_.end())) { // 如果输出处理器中的操作指针不为空,并且找到了给定索引的映射 + if (handle.out.empty()) { // 如果输出处理器中输出名称为空 + MS_LOG(DEBUG) << "Link op " << handle.op->GetName() << " to " << op->GetName() << ":" << it->second.name; + it->second.set_op(op, handle.op); // 设置普通操作的输入 + } else { // 如果输出处理器中输出名称不为空 + MS_LOG(DEBUG) << "Link op " << handle.op->GetName() << ":" << handle.out << " to " << op->GetName() << ":" + << it->second.name; + it->second.set_handle(op, handle); // 设置普通操作的输入和输出处理器 + } + return SUCCESS; // 操作执行成功,返回 SUCCESS 状态 + } + return NOT_FOUND; // 输出处理器中的操作指针为空或未找到给定索引的映射,返回 NOT_FOUND 状态 +} + +// 该函数用于设置操作的输入 +// 参数:op,OperatorPtr类型的操作指针 +// 参数:index,int类型的输入索引 +// 参数:handle,OutHandler类型的输出处理器 +// 返回:int类型,表示操作执行的结果 +int OpAdapterImpl::setInput(const OperatorPtr &op, int index, const OutHandler &handle) { + if (IsCustomOp(op)) { // 如果是自定义操作 + auto cus_op = std::dynamic_pointer_cast(op); // 将操作指针转换为自定义操作指针 + return static_cast(SetCustomOpInput(cus_op, index, handle)); // 调用 SetCustomOpInput 函数设置自定义操作的输入,并将结果转换为整数返回 + } else { // 如果不是自定义操作 + return static_cast(SetNormalOpInput(op, index, handle)); // 调用 SetNormalOpInput 函数设置普通操作的输入,并将结果转换为整数返回 + } +} + +// 该函数用于设置操作的动态输入 + +// 参数:op,OperatorPtr类型的操作指针 +// 参数:index,int类型的输入索引 +// 参数:handler_vec,std::shared_ptr>类型的输出处理器向量 +// 返回:int类型,表示操作执行的结果 +int OpAdapterImpl::setInput(const OperatorPtr &op, int index, + const std::shared_ptr> &handler_vec) { + MS_EXCEPTION_IF_NULL(handler_vec); // 检查输出处理器向量的有效性 + if (IsCustomOp(op)) { // 如果是自定义操作 + MS_LOG(ERROR) << "Custom Op do not support dynamic input"; // 输出错误信息,自定义操作不支持动态输入 + return static_cast(FAILED); // 返回执行失败的状态码 + } + MS_EXCEPTION_IF_NULL(op); // 检查操作指针的有效性 + auto it = dyn_input_map_.find(index); // 查找对应索引的动态输入信息 + if (it != dyn_input_map_.end()) { // 如果找到了对应索引的动态输入信息 + it->second.create_dyn_input(op, static_cast(handler_vec->size())); // 创建动态输入 + for (unsigned int i = 0; i < handler_vec->size(); ++i) { // 遍历输出处理器向量 + OutHandler h = (*handler_vec)[i]; // 获取输出处理器 + MS_EXCEPTION_IF_NULL(h.op); // 检查输出处理器的操作指针有效性 + if (h.out.empty()) { // 如果输出处理器中没有指定输出名称 + MS_LOG(DEBUG) << "Link op " << h.op->GetName() << " to " << op->GetName() << ":" << it->second.name; + // 输出调试信息,将输出处理器中的操作链接为动态输入的一部分 + it->second.set_op(op, (i), h.op); + } else { // 如果输出处理器中指定了输出名称 + MS_LOG(DEBUG) << "Link op " << h.op->GetName() << ":" << h.out << " to " << op->GetName() << ":" + << it->second.name; + // 输出调试信息,将输出处理器中的输出链接为动态输入的一部分 + it->second.set_handle(op, i, h); + } + } + return 0; // 返回执行成功的状态码 + } + return static_cast(NOT_FOUND); // 返回未找到的状态码 +} + +// 该函数用于获取操作的输出处理器 +// 参数:op,OperatorPtr类型的操作指针 +// 参数:index,int类型的输出索引 +// 返回:OutHandler类型的输出处理器 +OutHandler OpAdapterImpl::getOutput(const OperatorPtr &op, int index) { + MS_EXCEPTION_IF_NULL(op);// 检查操作指针的有效性 + if (IsCustomOp(op)) { // 如果是自定义操作 + return getCustomOutput(op, index); // 调用 getCustomOutput 函数获取自定义操作的输出处理器 + } + return getNormalOutput(op, index); // 否则,调用 getNormalOutput 函数获取普通操作的输出处理器 +} + +// 该函数用于获取自定义操作的输出处理器 +// 参数:op,OperatorPtr类型的操作指针 +// 参数:index,int类型的输出索引 +// 返回:OutHandler类型的输出处理器 +OutHandler OpAdapterImpl::getCustomOutput(const OperatorPtr &op, int index) { + MS_EXCEPTION_IF_NULL(op); // 检查操作指针的有效性 + auto it = cus_output_map_->find(op->GetOpType()); + if (it == cus_output_map_->end()) { // 查找自定义操作的输出映射 + MS_LOG(ERROR) << "OpAdpator(" << op->GetName() << ") has both OUTPUT is not supported!"; // 如果没有找到输出映射,输出错误日志 + return OutHandler(); // 返回空的输出处理器 + } + + mindspore::HashMap &output_map = it->second; // 获取输出映射 + + if ((output_map.find(index) != output_map.end())) { // 检查是否找到输出索引对应的输出名称 + return OutHandler(op, output_map[index]); // 如果找到,创建并返回输出处理器 + } + MS_LOG(ERROR) << "OpAdpator(" << op->GetName() << ") has no OUTPUT index(" << index << ")!"; // 如果没有找到输出索引,输出错误日志 + return OutHandler(); // 返回空的输出处理器 +} + +// 该函数用于获取普通操作的输出处理器 +// 参数:op,OperatorPtr类型的操作指针 +// 参数:index,int类型的输出索引 +// 返回:OutHandler类型的输出处理器 +OutHandler OpAdapterImpl::getNormalOutput(const OperatorPtr &op, int index) { + MS_EXCEPTION_IF_NULL(op); // 检查操作指针的有效性 + if (!dyn_output_map_.empty() && !output_map_.empty()) { // 检查是否同时存在动态输出映射和普通输出映射 + MS_LOG(ERROR) << "OpAdpator(" << op->GetName() << ") has both OUTPUT and DYN_OUTPUT is not supported!"; // 如果同时存在动态输出映射和普通输出映射,输出错误日志 + return OutHandler(); // 返回空的输出处理器 + } + auto it = output_map_.find(index); // 在普通输出映射中查找指定的输出索引 + if (it != output_map_.end()) { // 如果找到了输出索引 + return OutHandler(op, it->second.name); // 创建并返回输出处理器 + } else if (!dyn_output_map_.empty()) { // 如果普通输出映射为空,但动态输出映射不为空 + return OutHandler(op, dyn_output_map_.begin()->second.name + std::to_string(index)); // 根据动态输出映射的第一个输出名称构造输出处理器 + } else { // 如果既没有普通输出映射,也没有动态输出映射 + MS_LOG(ERROR) << "OpAdpator(" << op->GetName() << ") has no OUTPUT and DYN_OUTPUT index(" << index << ")!"; // 输出错误日志 + return OutHandler(); // 返回空的输出处理器 + } +} + +// 该函数用于更新单个输出的描述符 +// 参数:op,OperatorPtr类型的操作指针 +// 参数:shp,abstract::BaseShapePtr类型的输出形状指针 +// 参数:type,TypePtr类型的输出数据类型 +// 参数:format,string类型的输出数据格式 +// 返回:Status类型的状态,SUCCESS表示成功,FAILED表示失败 +Status OpAdapterImpl::UpdateSingleOutputDesc(const OperatorPtr &op, const abstract::BaseShapePtr &shp, + const TypePtr &type, const std::string &format) { + MS_EXCEPTION_IF_NULL(type); // 检查输出数据类型的有效性 + + auto desc = CreateOutputDesc(dyn_cast(shp), type, format); + if (desc == nullptr) { // 检查输出描述符是否为空 + MS_LOG(ERROR) << "Update output descriptor failed!"; // 输出错误日志 + return FAILED; // 返回失败状态 + } + + if (IsCustomOp(op)) { // 检查是否为自定义操作 + if (cus_output_map_->find(op->GetOpType()) == cus_output_map_->end() || + ((*cus_output_map_)[op->GetOpType()].empty())) { // 检查是否存在自定义输出映射,并且不为空 + MS_LOG(ERROR) << "This op does not create custom output map"; // 输出错误日志 + return FAILED; // 返回失败状态 + } + auto cus_op = std::dynamic_pointer_cast(op); // 将操作指针转换为自定义操作指针 + MS_EXCEPTION_IF_NULL(cus_op); // 检查自定义操作指针的有效性 + mindspore::HashMap output_map = (*cus_output_map_)[op->GetOpType()]; // 获取自定义输出映射 + (void)cus_op->UpdateOutputDesc(output_map[0], *desc); // 更新自定义操作的输出描述符 + } else { // 如果不是自定义操作 + if (output_map_.empty()) { // 检查普通输出映射是否为空 + MS_LOG(INFO) << "This op does not have output map"; // 输出提示信息 + return FAILED; // 返回失败状态 + } + output_map_.begin()->second.update_out_desc(op, *desc); // 更新普通操作的输出描述符 + } + return SUCCESS; // 返回成功状态 +} + +// 该函数用于获取自定义操作的输出数量 +// 参数:cus_op,CusOperatorPtr类型的自定义操作指针 +// 返回:size_t类型的输出数量,表示自定义操作的输出数量 +size_t OpAdapterImpl::GetCustomOpOutputSize(const CusOperatorPtr &cus_op) { + MS_EXCEPTION_IF_NULL(cus_op); // 检查自定义操作指针的有效性 + if (cus_output_map_->find(cus_op->GetOpType()) == cus_output_map_->end()) { // 检查是否存在自定义输出映射 + MS_LOG(ERROR) << "This op does not create custom output map"; // 输出错误日志 + return 0; // 返回0,表示自定义操作的输出数量为0 + } + size_t output_size = (*cus_output_map_)[cus_op->GetOpType()].size(); // 获取自定义操作的输出数量 + return output_size; // 返回自定义操作的输出数量 +} + +// 该函数用于创建输出的GeTensorDesc对象,用于描述输出的形状、数据类型和格式。 +// 参数: +// - shape_ptr: abstract::ShapePtr类型的形状指针,表示输出的形状。 +// - type: TypePtr类型的类型指针,表示输出的数据类型。 +// - format: std::string类型的格式字符串,表示输出的数据格式。 +// 返回: +// - std::shared_ptr类型的指针,表示输出的GeTensorDesc对象。 +std::shared_ptr OpAdapterImpl::CreateOutputDesc(const abstract::ShapePtr &shape_ptr, const TypePtr &type, + const std::string &format) { + if (type == nullptr) { // 检查输出的数据类型是否为空 + MS_LOG(ERROR) << "Type ptr is nullptr"; // 输出错误日志 + return nullptr; // 返回空指针,表示创建输出描述失败 + } + + TypeId me_type = type->type_id(); // 获取输出的数据类型ID + if (kObjectTypeTensorType == me_type) { // 如果输出类型是TensorType + me_type = dyn_cast(type)->element()->type_id(); // 获取Tensor元素的数据类型ID + } + // 调用TransformUtil::GetGeTensorDesc函数,创建GeTensorDesc对象并返回 + return TransformUtil::GetGeTensorDesc((shape_ptr == nullptr) ? ShapeVector{} : shape_ptr->shape(), me_type, format); +} + +// 该函数用于更新多输出的输出描述。 +// 参数: +// - op: OperatorPtr类型的指针,表示要更新输出描述的运算符。 +// - shp: abstract::BaseShapePtr类型的形状指针,表示输出的形状。 +// - type: TypePtr类型的类型指针,表示输出的数据类型。 +// - format: std::string类型的格式字符串,表示输出的数据格式。 +// 返回: +// - Status类型,表示更新输出描述的操作状态。 +Status OpAdapterImpl::UpdateMultiOutputDesc(const OperatorPtr &op, const abstract::BaseShapePtr &shp, + const TypePtr &type, const std::string &format) { + auto tuple_shp = dyn_cast(shp); // 转换成TupleShape类型 + MS_EXCEPTION_IF_NULL(tuple_shp); + + size_t output_size = 0; + bool is_custom_op = IsCustomOp(op); // 检查是否为自定义运算符 + if (is_custom_op) { + output_size = GetCustomOpOutputSize(std::dynamic_pointer_cast(op)); // 获取自定义运算符的输出数量 + } else { + output_size = output_map_.size(); // 获取普通运算符的输出数量 + } + + if (output_size == 0) { // 如果输出数量为0,返回失败状态 + MS_LOG(INFO) << "This op does not have output map"; + return FAILED; + } + + if (output_size != tuple_shp->shape().size()) { // 检查输出数量是否与TupleShape的大小相等 + MS_LOG(ERROR) << "output_map is not equal tuple_shape size"; + return FAILED; // 如果输出数量不相等,返回失败状态 + } + + for (size_t i = 0; i < tuple_shp->shape().size(); ++i) { + auto tuple_type = dyn_cast(type); // 转换成Tuple类型 + MS_EXCEPTION_IF_NULL(tuple_type); + TypePtr type_elem = tuple_type->elements()[i]; // 获取Tuple中第i个元素的类型 + // 调用CreateOutputDesc函数,创建GeTensorDesc对象 + auto desc = CreateOutputDesc(dyn_cast(tuple_shp->shape()[i]), type_elem, format); + if (desc == nullptr) { + MS_LOG(ERROR) << "Create output descriptor failed!"; + return FAILED; // 如果创建输出描述失败,返回失败状态 + } + + if (is_custom_op) { // 如果是自定义运算符 + // 调用CustomOperator的UpdateOutputDesc函数,更新输出描述 + (void)std::dynamic_pointer_cast(op)->UpdateOutputDesc((*cus_output_map_)[op->GetOpType()][i], + *desc); + } else { + auto it = output_map_.find(i); + if (it != output_map_.end()) { + it->second.update_out_desc(op, *desc); // 更新普通运算符的输出描述 + } + } + } + return SUCCESS; // 返回成功状态 +} + +// 该函数用于创建GeTensorDesc对象,表示给定AnfNode的描述。 +// 参数: +// - node: AnfNodePtr类型的指针,表示要创建描述的节点。 +// - format: std::string类型的格式字符串,表示描述的数据格式。 +// 返回: +// - std::shared_ptr类型的指针,表示创建的GeTensorDesc对象。 +std::shared_ptr OpAdapterImpl::CreateNodeDesc(const AnfNodePtr &node, const std::string &format) { + MS_EXCEPTION_IF_NULL(node); + TypeId me_type = node->Type()->type_id(); // 获取节点的数据类型 + if (kObjectTypeTensorType == me_type) { + me_type = dyn_cast(node->Type())->element()->type_id(); + } + // 检查数据类型是否有效,如果无效则返回nullptr + if (me_type <= kNumberTypeBegin || me_type >= kNumberTypeEnd) { + return nullptr; + } + + std::vector shape; + auto shape_ptr = dyn_cast(node->Shape()); + if (shape_ptr != nullptr) { // 获取节点的形状 + shape = shape_ptr->shape(); + } + // 调用TransformUtil的GetGeTensorDesc函数,创建GeTensorDesc对象 + auto desc = TransformUtil::GetGeTensorDesc(shape, me_type, format); + // 检查是否成功创建GeTensorDesc对象,如果失败返回nullptr + if (desc == nullptr) { + MS_LOG(ERROR) << "Update output descriptor failed!"; + return nullptr; + } + return desc; // 返回创建的GeTensorDesc对象的指针 +} + +// 该函数用于更新普通算子的输入描述。 +// 参数: +// - op: OperatorPtr类型的指针,表示要更新输入描述的算子。 +// - node: AnfNodePtr类型的指针,表示算子对应的CNode节点。 +// - format: std::string类型的格式字符串,表示描述的数据格式。 +void OpAdapterImpl::UpdateNormalOpInputDesc(const OperatorPtr &op, const AnfNodePtr &node, const std::string format) { + if (op == nullptr) { //检查算子是否为空,如果为空则打印错误日志并返回 + MS_LOG(ERROR) << "op is nullptr"; + return; + } + MS_EXCEPTION_IF_NULL(node); + + auto inputs = node->cast()->inputs(); //遍历CNode节点的输入,从第二个输入开始,因为第一个输入是算子本身 + for (size_t i = 1; i < inputs.size(); ++i) { // 对于输入节点,查找是否有对应的输入描述 + auto it = input_map_.find(i); + if (it != input_map_.end()) { //如果找到,则调用CreateNodeDesc函数创建新的输入描述,并使用该描述更新输入节点的描述。 + auto desc = CreateNodeDesc(inputs[i], format); + if (desc == nullptr) { // 如果创建描述失败,则继续处理下一个输入节点。 + continue; + } + + it->second.update_input_desc(op, *desc); + } + } +} + +// 该函数用于更新自定义算子的输入描述。 +// 参数: +// - op: CusOperatorPtr类型的指针,表示要更新输入描述的自定义算子。 +// - node: AnfNodePtr类型的指针,表示自定义算子对应的CNode节点。 +// - format: std::string类型的格式字符串,表示描述的数据格式。 +void OpAdapterImpl::UpdateCustomOpInputDesc(const CusOperatorPtr &op, const AnfNodePtr &node, + const std::string format) { + if (op == nullptr) { //检查算子是否为空,如果为空则打印错误日志并返回 + MS_LOG(ERROR) << "op is nullptr"; + return; + } + MS_EXCEPTION_IF_NULL(node); + //检查自定义算子是否创建了输入映射,如果未创建则打印错误日志并返回 + if (cus_input_map_->find(op->GetOpType()) == cus_input_map_->end() || ((*cus_input_map_)[op->GetOpType()].empty())) { + MS_LOG(ERROR) << "This op does not create custom input map"; + return; + } + //遍历CNode节点的输入,从第二个输入开始,因为第一个输入是算子本身 + mindspore::HashMap &input_map = (*cus_input_map_)[op->GetOpType()]; + auto inputs = node->cast()->inputs(); + for (size_t i = 1; i < inputs.size(); ++i) { // 对于输入节点,查找是否有对应的输入描述 + if (input_map.find(i) != input_map.end()) { //如果找到,则调用CreateNodeDesc函数创建新的输入描述,并使用该描述更新自定义算子的输入描述。 + auto desc = CreateNodeDesc(inputs[i], format); + if (desc == nullptr) { // 如果创建描述失败,则继续处理下一个输入节点。 + continue; + } + (void)op->UpdateInputDesc(input_map[i], *desc); + } + } +} + +// 该函数用于更新算子的输入描述。 +// 参数: +// - op: OperatorPtr类型的指针,表示要更新输入描述的算子。 +// - node: AnfNodePtr类型的指针,表示算子对应的CNode节点。 +void OpAdapterImpl::updateInputDesc(const OperatorPtr &op, const AnfNodePtr &node) { + MS_EXCEPTION_IF_NULL(op); //检查算子和节点是否为空,如果为空则抛出异常。 + MS_EXCEPTION_IF_NULL(node); + std::string format = GetOpIOFormat(node); //获取节点的数据格式(即IO格式)通过调用GetOpIOFormat函数。 + if (IsCustomOp(op)) { //检查算子是否为自定义算子 + auto cus_op = std::dynamic_pointer_cast(op); //如果是,则将算子转换为CustomOperator类型 + UpdateCustomOpInputDesc(cus_op, node, format); //调用UpdateCustomOpInputDesc函数来更新输入描述 + } else { //如果不是自定义算子,则调用UpdateNormalOpInputDesc函数来更新输入描述。 + UpdateNormalOpInputDesc(op, node, format); + } +} + +// 该函数用于根据输出的形状和数据类型信息更新运算符的输出描述。 +// 参数: +// - op: 指向OperatorPtr的指针,表示需要更新输出描述的运算符。 +// - shp: 指向BaseShapePtr的指针,表示输出的形状信息。 +// - type: 指向TypePtr的指针,表示输出的数据类型信息。 +// - node: 指向AnfNodePtr的指针,表示与运算符对应的CNode节点。 +void OpAdapterImpl::updateOutputDesc(const OperatorPtr &op, const abstract::BaseShapePtr &shp, const TypePtr &type, + const AnfNodePtr &node) { + if (op == nullptr) { //检查运算符和节点指针是否为空,如果为空,则会引发异常 + MS_LOG(ERROR) << "op is nullptr"; + return; + } + MS_EXCEPTION_IF_NULL(node); + MS_LOG(INFO) << "Op name is " << op->GetName() << " anf is " << node->DebugString(); + + auto normal_shape_ptr = dyn_cast(shp); + auto no_shape_ptr = dyn_cast(shp); + std::string format = GetOpIOFormat(node); //通过调用GetOpIOFormat函数来获取节点的数据格式(IO格式) + //函数检查形状信息的类型 + if ((normal_shape_ptr != nullptr) || (no_shape_ptr != nullptr)) { //如果形状类型为Shape或NoShape(单个输出) + if (UpdateSingleOutputDesc(op, shp, type, format) != SUCCESS) { //则调用UpdateSingleOutputDesc函数来更新输出描述。 + return; + } + } else if (dyn_cast(shp) != nullptr) { // 如果形状类型为TupleShape(多个输出) + if (UpdateMultiOutputDesc(op, shp, type, format) != SUCCESS) { //调用UpdateMultiOutputDesc函数来更新输出描述 + return; + } + } else { //如果形状类型未知,则记录警告并返回。 + MS_LOG(WARNING) << "Update output desc failed, unknown output shape type"; + return; + } + MS_EXCEPTION_IF_NULL(node); + if (!node->isa()) { //更新输出描述后,函数检查节点是否为CNode(即计算节点) + return; //如果节点不是CNode,则函数返回,因为非计算节点不需要更新输入描述。 + } + + // Need to update input_desc while the output_desc is updated + // 如果节点是CNode,则函数调用updateInputDesc函数来同时更新输入描述。 + updateInputDesc(op, node); +} + +// 该函数用于为运算符设置属性。 +// 参数: +// - op: 指向OperatorPtr的指针,表示要设置属性的运算符。 +// - attr_key: 属性的键,表示要设置的属性名称。 +// - attr_value: 指向ValuePtr的指针,表示要设置的属性值。 +int OpAdapterImpl::setAttr(const OperatorPtr &op, const std::string &attr_key, const ValuePtr &attr_value) { + auto it = attr_map_.find(attr_key); //通过在attr_map_中查找给定的attr_key来检查是否有对应的属性信息 + if (it != attr_map_.end()) { // 如果找到了匹配的属性信息,则会调用相应的set_attr函数来设置属性。 + // switch case for each avalilable attribute type + // 在设置属性之前,函数会打印属性名称和属性值,并将属性信息添加到adpt_对象以绘制图形。 + MS_LOG(INFO) << "Set attr: " << attr_key << "(" << it->second.name << "), value: " << attr_value->ToString(); + adpt_->AddAttrToDrawGraph(attr_key + std::string("=") + attr_value->ToString()); + it->second.set_attr(op, attr_value); + return 0; // 成功则设置属性返回0。 + } + return static_cast(NOT_FOUND); // 如果未找到匹配的属性信息,则函数返回NOT_FOUND。 +} + +//该函数用于设置自定义操作的属性。 +int OpAdapterImpl::SetCustomOpAttr(const CusOperatorPtr &op, const PrimitivePtr &prim) { + enum ValueType { //定义一个枚举类型ValueType,包括三个枚举值:SINGLE_VALUE、SEQUEUE_VALUE和UNKNOWN_VALUE。 + SINGLE_VALUE = 0, + SEQUEUE_VALUE, + UNKNOWN_VALUE, + }; + + MS_EXCEPTION_IF_NULL(prim); //使用断言(MS_EXCEPTION_IF_NULL)确保prim和op指针不为空 + MS_EXCEPTION_IF_NULL(op); + + ValueType value_type = SINGLE_VALUE; //初始化一个变量value_type为SINGLE_VALUE + for (auto item : prim->attrs()) { //通过遍历prim对象的属性来设置操作的属性 + //对于每个属性,它首先检查其类型,然后根据类型调用适当的函数将属性值设置到操作中。 + //支持的属性类型包括Int32Imm、StringImm、BoolImm和FP32Imm。 + /* 如果属性的类型是ValueSequence,则将value_type设置为SEQUEUE_VALUE,并根据序列中第一个元素的类型设置属性值。 + 如果属性的类型不在支持的类型列表中,则抛出异常。*/ + if (item.second->isa()) { + (void)op->SetAttr(item.first, GetValue(item.second)); + } else if (item.second->isa()) { + (void)op->SetAttr(item.first, GetValue(item.second)); + } else if (item.second->isa()) { + (void)op->SetAttr(item.first, GetValue(item.second)); + } else if (item.second->isa()) { + (void)op->SetAttr(item.first, GetValue(item.second)); + } else if (item.second->isa()) { + value_type = SEQUEUE_VALUE; + auto val_seq = item.second->cast(); + if ((*val_seq)[0]->isa()) { + (void)op->SetAttr(item.first, GetValue>(item.second)); + } else if ((*val_seq)[0]->isa()) { + (void)op->SetAttr(item.first, GetValue>(item.second)); + } else if ((*val_seq)[0]->isa()) { + (void)op->SetAttr(item.first, GetValue>(item.second)); + } else if ((*val_seq)[0]->isa()) { + (void)op->SetAttr(item.first, GetValue>(item.second)); + } else { + MS_LOG(EXCEPTION) << "Unsupported custom attribute type in adaptor, prim name: " << prim->name() + << ", attr name: " << item.first << ", value: " << item.second->ToString(); + } + } else { + MS_LOG(WARNING) << "Unsupported custom attribute type in adaptor, prim name: " << prim->name() + << ", attr name: " << item.first << ", value: " << item.second->ToString(); + return static_cast(NOT_FOUND); + } + /*在设置属性值之后,根据value_type的值,函数使用适当的字符串表示将属性添加到绘制图中。 + 如果value_type为SINGLE_VALUE,则将属性名和属性值以等号连接并添加到绘制图中; + 如果value_type为SEQUEUE_VALUE,则将属性名和省略号添加到绘制图中。*/ + if (value_type == SINGLE_VALUE) { + adpt_->AddAttrToDrawGraph(item.first + std::string("=") + item.second->ToString()); + } else if (value_type == SEQUEUE_VALUE) { + adpt_->AddAttrToDrawGraph(item.first + std::string("=") + "[...]"); + } + } + return 0; //函数返回0表示成功设置属性。 +} + +// 该函数用于为普通运算符设置属性。 +// 参数: +// - op: 指向OperatorPtr的指针,表示要设置属性的运算符。 +// - prim: 指向PrimitivePtr的指针,表示运算符的原语(Primitive)信息。 +int OpAdapterImpl::SetNormalOpAttr(const OperatorPtr &op, const PrimitivePtr &prim) { + MS_EXCEPTION_IF_NULL(prim); //检查prim和op是否为空,如果为空,则会抛出异常 + MS_EXCEPTION_IF_NULL(op); + for (auto &it : attr_map_) { //函数遍历attr_map_ + auto value = prim->GetAttr(it.first); //对于每个属性(attr_map_中的每个键值对),它会先从prim中获取对应的属性值。 + if (value != nullptr) { //如果在prim中找到了匹配的属性值,则会对属性值进行一系列转换(如将部分属性转换为字符串形式,或将IR属性转换为Op属性) + // convert parts of attr to str eg. data_format or change ir attr to op attr eg. axis[0] + (void)CheckAndConvertUtils::ConvertAttrValueToString(prim->name(), it.first, &value); + (void)CheckAndConvertUtils::CheckIrAttrtoOpAttr(prim->name(), it.first, &value); + // set attr from primitive + int ret = setAttr(op, it.first, value); //然后调用setAttr函数设置运算符的属性 + if (ret) { + return ret; + } + } else { //// 如果在prim中未找到匹配的属性值,则会检查是否存在extra_attr_(额外的属性信息) + // set attr from extra_attr + auto it_extra = extra_attr_->find(it.first); + if (it_extra != extra_attr_->end()) { // 如果在extra_attr_中找到了匹配的属性值,则同样调用setAttr函数设置运算符的属性 + int ret = setAttr(op, it.first, it_extra->second); + if (ret) { // 如果遍历过程中发现设置属性时返回了非零值(即设置属性失败),则函数会立即返回该错误码。 + return ret; + } + } + } + } + return 0; //成功设置属性返回0。 +} + +// 该函数用于为运算符设置属性。 +// 参数: +// - op: 指向OperatorPtr的指针,表示要设置属性的运算符。 +// - prim: 指向PrimitivePtr的指针,表示运算符的原语(Primitive)信息。 +int OpAdapterImpl::setAttr(const OperatorPtr &op, const PrimitivePtr &prim) { + int ret = 0; + if (IsCustomPrim(prim)) { //判断prim是否为自定义原语(CustomPrimitive) + auto cus_op = std::dynamic_pointer_cast(op); + ret = SetCustomOpAttr(cus_op, prim); //如果是自定义原语,则调用SetCustomOpAttr函数为运算符设置属性。 + } else { // 否则,调用SetNormalOpAttr函数为运算符设置属性 + ret = SetNormalOpAttr(op, prim); + } + return ret; //返回值为非零表示设置属性失败,返回对应的错误码。 +} + +// 该函数用于为运算符设置属性。 +// 参数: +// - op: 指向OperatorPtr的指针,表示要设置属性的运算符。 +// - node: 指向AnfNodePtr的指针,表示运算符对应的图节点(AnfNode)。 +int OpAdapterImpl::setAttr(const OperatorPtr &op, const AnfNodePtr &node) { + // no attribute for lonely node + MS_EXCEPTION_IF_NULL(node); + if (!node->isa()) { //判断node是否为CNode(计算节点)。 + return 0; //如果不是CNode,则表示该节点没有属性,直接返回0。 + } + // 将节点转换为CNodePtr(计算节点的表示)。 + auto cnode = node->cast(); + if (cnode == nullptr) { + return 0; + } + // 获取计算节点的输入。 + auto &inputs = cnode->inputs(); + if (inputs.empty()) { + return 0; + } + + // get Attr T from abstract of anfnode first, + // if attr "T" appears in primitive, the primitive T will cover this one + // 首先从AnfNode的抽象信息中获取属性"T"。 + // 如果原语(primitive)中有属性"T",则原语中的"T"将覆盖此处的"T"属性。 + if (attr_map_.find("T") != attr_map_.end()) { + // get dtype from inputs[1], if the node has no inputs, set the attr T with output dtype + // 从inputs[1]中获取数据类型(dtype)。如果节点没有输入,则使用输出的数据类型来设置属性"T"。 + TypePtr type; + if (inputs.size() > 1) { + type = inputs[1]->Type(); + } else { + type = node->Type(); + } + if (type != nullptr) { // 使用上面获得的数据类型来设置属性"T" + (void)setAttr(op, "T", MakeValue(type)); + } + } + + // set attr from primitive and ExtraAttr + // 从原语和ExtraAttr中设置属性。 + if (IsValueNode(inputs[0])) { + // set attr from primitive + // 从原语中设置属性。 + PrimitivePtr prim = GetValueNode(inputs[0]); + int ret = setAttr(op, prim); + if (ret != 0) { + return ret; + } + } + + // set attr from const input + // 从常量输入中设置属性。 + for (auto &it : input_attr_map_) { + // 检查输入索引是否在范围内,并且输入是否为ValueNode(常量节点)。 + if (inputs.size() <= it.first || !inputs[it.first]->isa()) { + continue; + } + // 从输入中获取常量值。 + auto const_value = GetValueNode(inputs[it.first]); + MS_LOG(INFO) << "Set attr: input_" << it.first << "(" << it.second.name << "), value: " << const_value->ToString(); + if (const_value->isa()) { // 如果常量值为None,则跳过设置属性。 + continue; + } + // 将属性信息添加到绘图图形中。 + adpt_->AddAttrToDrawGraph(it.second.name + std::string("=") + const_value->ToString()); + // 使用提供的设置器函数(it.second.set_attr)和常量值来设置属性。 + it.second.set_attr(op, const_value); + } + return 0; +} +} // namespace transform +} // namespace mindspore diff --git a/mindspore/ccsrc/transform-update/op_adapter.h b/mindspore/ccsrc/transform-update/op_adapter.h new file mode 100644 index 00000000000..302f9855804 --- /dev/null +++ b/mindspore/ccsrc/transform-update/op_adapter.h @@ -0,0 +1,478 @@ +/** + * Copyright 2019-2021 Huawei Technologies Co., Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MINDSPORE_CCSRC_TRANSFORM_GRAPH_IR_OP_ADAPTER_H_ +#define MINDSPORE_CCSRC_TRANSFORM_GRAPH_IR_OP_ADAPTER_H_ + +#include +#include +#include + +#include "utils/hash_map.h" +#include "transform/graph_ir/op_adapter_util.h" +#include "transform/graph_ir/op_adapter_base.h" +#include "include/common/utils/utils.h" +namespace mindspore { +namespace transform { +class OpAdapterImpl { + public: + // 构造函数,用于初始化OpAdapterImpl对象。接受一系列输入参数,并将它们保存到对应的成员变量中。 + OpAdapterImpl(const mindspore::HashMap &input_map, + const mindspore::HashMap &dyn_input_map, + const mindspore::HashMap &output_map, + const mindspore::HashMap &dyn_output_map, + const mindspore::HashMap &dyn_subgraph_map, + const mindspore::HashMap &attr_map, + const mindspore::HashMap &enum_map, + const mindspore::HashMap &input_attr_map, + mindspore::HashMap> *cus_input_map, + mindspore::HashMap> *cus_output_map, + mindspore::HashMap *extra_attr, + mindspore::HashMap *name_counts, BaseOpAdapter *adpt) + : input_map_(input_map), + dyn_input_map_(dyn_input_map), + output_map_(output_map), + dyn_output_map_(dyn_output_map), + dyn_subgraph_map_(dyn_subgraph_map), + attr_map_(attr_map), + enum_map_(enum_map), + input_attr_map_(input_attr_map), + cus_input_map_(cus_input_map), + cus_output_map_(cus_output_map), + extra_attr_(extra_attr), + name_counts_(name_counts), + adpt_(adpt) { + MS_EXCEPTION_IF_NULL(cus_input_map_); + MS_EXCEPTION_IF_NULL(cus_output_map_); + MS_EXCEPTION_IF_NULL(extra_attr_); + MS_EXCEPTION_IF_NULL(name_counts_); + MS_EXCEPTION_IF_NULL(adpt_); + } + ~OpAdapterImpl() {} // 析构函数,用于释放资源。 + bool IsCustomOp(const OperatorPtr &op); // 判断一个运算符是否为自定义运算符。 + Status GenerateCustomOpInputMap(const CusOperatorPtr &op, const PrimitivePtr &prim); // 生成自定义运算符的输入映射。 + Status GenerateCustomOpOutputMap(const CusOperatorPtr &op, const PrimitivePtr &prim); // 生成自定义运算符的输出映射。 + OperatorPtr GenerateCustomOp(const AnfNodePtr anf); // 生成自定义运算符。 + Status SetOpSubgraphFunc(const OperatorPtr &op, int index, const std::shared_ptr> &branches); // 设置运算符的子图函数。 + Status SetCustomOpInput(const CusOperatorPtr &op, int index, const OperatorPtr &input); // 设置自定义运算符的输入。 + Status SetNormalOpInput(const OperatorPtr &op, int index, const OperatorPtr &input); // 设置普通运算符的输入。 + int setInput(const OperatorPtr &op, int index, const OperatorPtr &input); // 设置运算符的输入。 + Status SetCustomOpInput(const CusOperatorPtr &op, int index, const OutHandler &handle); // 设置自定义运算符的输入。 + Status SetNormalOpInput(const OperatorPtr &op, int index, const OutHandler &handle); // 设置普通运算符的输入。 + int setInput(const OperatorPtr &op, int index, const OutHandler &handle); // 设置运算符的输入。 + int setInput(const OperatorPtr &op, int index, const std::shared_ptr> &handler_vec); // 设置运算符的输入。 + OutHandler getOutput(const OperatorPtr &op, int index); // 获取运算符的输出处理器。 + OutHandler getCustomOutput(const OperatorPtr &op, int index); // 获取自定义运算符的输出处理器。 + OutHandler getNormalOutput(const OperatorPtr &op, int index); // 获取普通运算符的输出处理器 + Status UpdateSingleOutputDesc(const OperatorPtr &op, const abstract::BaseShapePtr &shp, const TypePtr &type, + const std::string &format); // 更新单个输出的描述信息。 + size_t GetCustomOpOutputSize(const CusOperatorPtr &cus_op); // 获取自定义运算符的输出数量。 + std::shared_ptr CreateOutputDesc(const abstract::ShapePtr &shape_ptr, const TypePtr &type, + const std::string &format); // 创建输出描述。 + Status UpdateMultiOutputDesc(const OperatorPtr &op, const abstract::BaseShapePtr &shp, const TypePtr &type, + const std::string &format); // 更新多个输出的描述信息。 + std::shared_ptr CreateNodeDesc(const AnfNodePtr &node, const std::string &format); // 创建节点描述。 + void UpdateNormalOpInputDesc(const OperatorPtr &op, const AnfNodePtr &node, const std::string format); // 更新普通运算符的输入描述。 + void UpdateCustomOpInputDesc(const CusOperatorPtr &op, const AnfNodePtr &node, const std::string format);// 更新自定义运算符的输入描述。 + void updateInputDesc(const OperatorPtr &op, const AnfNodePtr &node);// 更新运算符的输入描述。 + void updateOutputDesc(const OperatorPtr &op, const abstract::BaseShapePtr &shp, const TypePtr &type, + const AnfNodePtr &node); // 更新运算符的输出描述。 + int setAttr(const OperatorPtr &op, const std::string &attr_key, const ValuePtr &attr_value); // 设置运算符的属性。 + int SetCustomOpAttr(const CusOperatorPtr &op, const PrimitivePtr &prim); // 设置自定义运算符的属性。 + int SetNormalOpAttr(const OperatorPtr &op, const PrimitivePtr &prim); // 设置普通运算符的属性。 + int setAttr(const OperatorPtr &op, const PrimitivePtr &prim); // 设置运算符的属性。 + int setAttr(const OperatorPtr &op, const AnfNodePtr &node); // 设置运算符的属性。 + + private: // 一系列输入映射和输出映射。 + const mindspore::HashMap &input_map_; + const mindspore::HashMap &dyn_input_map_; + const mindspore::HashMap &output_map_; + const mindspore::HashMap &dyn_output_map_; + const mindspore::HashMap &dyn_subgraph_map_; + const mindspore::HashMap &attr_map_; + const mindspore::HashMap &enum_map_; + const mindspore::HashMap &input_attr_map_; + // 自定义输入映射和输出映射。 + mindspore::HashMap> *const cus_input_map_; + mindspore::HashMap> *const cus_output_map_; + mindspore::HashMap *const extra_attr_; + mindspore::HashMap *const name_counts_; + BaseOpAdapter *const adpt_; +}; + +template +class OpAdapter : public BaseOpAdapter { + public: + // 使用OpType作为模板参数的构造函数。初始化OpAdapterImpl对象。 + using OpType = T; + OpAdapter() + : impl_(std::make_shared(input_map_, dyn_input_map_, output_map_, dyn_output_map_, + dyn_subgraph_map_, attr_map_, enum_map_, input_attr_map_, &cus_input_map_, + &cus_output_map_, &extra_attr_, &name_counts_, this)) { + MS_EXCEPTION_IF_NULL(impl_); + } + // 使用ExtraAttr作为输入参数的构造函数。初始化OpAdapterImpl对象。 + explicit OpAdapter(const ExtraAttr &extra_attr) + : extra_attr_(extra_attr), + impl_(std::make_shared(input_map_, dyn_input_map_, output_map_, dyn_output_map_, + dyn_subgraph_map_, attr_map_, enum_map_, input_attr_map_, &cus_input_map_, + &cus_output_map_, &extra_attr_, &name_counts_, this)) { + MS_EXCEPTION_IF_NULL(impl_); + } + // 析构函数,用于释放资源。 + ~OpAdapter() override {} + // 判断一个运算符是否为自定义运算符。 + bool IsCustomOp(const OperatorPtr &op) { return impl_->IsCustomOp(op); } + // 生成自定义运算符的输入映射。 + Status GenerateCustomOpInputMap(const CusOperatorPtr &op, const PrimitivePtr &prim) { + return impl_->GenerateCustomOpInputMap(op, prim); + } + // 生成自定义运算符的输出映射。 + Status GenerateCustomOpOutputMap(const CusOperatorPtr &op, const PrimitivePtr &prim) { + return impl_->GenerateCustomOpOutputMap(op, prim); + } + + // Convert ME UserCustom AnfNode to GE CustomOp. And set it's attrs. + // 将ME UserCustom AnfNode转换为GE CustomOp,并设置其属性。 + OperatorPtr GenerateCustomOp(const AnfNodePtr anf) { return impl_->GenerateCustomOp(anf); } + // 生成普通运算符。 + OperatorPtr GenerateNormalOp(const AnfNodePtr &anf) { + OperatorPtr op = nullptr; + // There are duplicate names in ANF graph, do not assign ANF node name to GE + // GE will generate unique name automatically + if (anf != nullptr && anf->fullname_with_scope() != "") { + MS_LOG(DEBUG) << anf->fullname_with_scope(); + op = std::make_shared(anf->fullname_with_scope()); + } else { + MS_LOG(DEBUG) << "no fullname_with_scope"; + op = std::make_shared(); + } + + // set dynamic output num if op use DYNAMIC_OUTPUT + if ((op != nullptr) && (!dyn_output_map_.empty()) && (anf != nullptr)) { + TypePtr type = anf->Type(); + if (type == nullptr) { + MS_LOG(EXCEPTION) << "Dynamic output node:" << op->GetName() << "'s Type is a nullptr!"; + } + size_t num = type->isa() ? (type->cast>()->size()) : 1; + MS_LOG(INFO) << "create_dyn_output for node:" << anf->ToString() << ", type:" << type->ToString() + << ", num:" << num; + dyn_output_map_.begin()->second.create_dyn_output(op, static_cast(num)); + } + return op; + } + // 实现generate函数,根据传入的AnfNode生成对应的OperatorPtr。 + OperatorPtr generate(const AnfNodePtr &anf) override { + OperatorPtr op = nullptr; + if (IsCustomCNode(anf)) { + op = GenerateCustomOp(anf); + } else { + op = GenerateNormalOp(anf); + } + if (op == nullptr) { + MS_LOG(EXCEPTION) << "Can not generate op for " << anf->fullname_with_scope(); + } + return op; + } + // 实现generate函数,根据传入的op_name生成对应的OperatorPtr。 + OperatorPtr generate(const std::string &op_name) override { return std::make_shared(op_name); } + // 获取输入映射。 + const mindspore::HashMap &getInputMap() override { return input_map_; } + // 获取输入属性映射。 + const mindspore::HashMap &getInputAttrMap() override { return input_attr_map_; } + // 获取动态输入映射。 + const mindspore::HashMap &getDynInputMap() override { return dyn_input_map_; } + // 获取输出映射。 + const mindspore::HashMap &getOutputMap() override { return output_map_; } + // 获取动态子图映射。 + const mindspore::HashMap &getDynSubgraphMap() override { return dyn_subgraph_map_; } + // 设置运算符的子图函数。 + Status SetOpSubgraphFunc(const OperatorPtr &op, int index, const std::shared_ptr> &branches) { + return impl_->SetOpSubgraphFunc(op, index, branches); + } + // 设置运算符的子图。 + int setSubgraph(const OperatorPtr &op, int index, const std::shared_ptr> &branches) override { + return static_cast(SetOpSubgraphFunc(op, index, branches)); + } + // 设置自定义运算符的输入。 + Status SetCustomOpInput(const CusOperatorPtr &op, int index, const OperatorPtr &input) { + return impl_->SetCustomOpInput(op, index, input); + } + // 设置普通运算符的输入。 + Status SetNormalOpInput(const OperatorPtr &op, int index, const OperatorPtr &input) { + return impl_->SetNormalOpInput(op, index, input); + } + // 设置运算符的输入。 + int setInput(const OperatorPtr &op, int index, const OperatorPtr &input) override { + return impl_->setInput(op, index, input); + } + // 设置自定义运算符的输入。 + Status SetCustomOpInput(const CusOperatorPtr &op, int index, const OutHandler &handle) { + return impl_->SetCustomOpInput(op, index, handle); + } + // 设置普通运算符的输入。 + Status SetNormalOpInput(const OperatorPtr &op, int index, const OutHandler &handle) { + return impl_->SetNormalOpInput(op, index, handle); + } + // 设置运算符的输入 + int setInput(const OperatorPtr &op, int index, const OutHandler &handle) override { + return impl_->setInput(op, index, handle); + } + // 设置运算符的输入。 + int setInput(const OperatorPtr &op, int index, const std::shared_ptr> &handler_vec) override { + return impl_->setInput(op, index, handler_vec); + } + // 获取运算符的输出处理器。 + OutHandler getOutput(const OperatorPtr &op, int index) override { return impl_->getOutput(op, index); } + // 获取自定义运算符的输出处理器。 + OutHandler getCustomOutput(const OperatorPtr &op, int index) { return impl_->getCustomOutput(op, index); } + // 获取普通运算符的输出处理器。 + OutHandler getNormalOutput(const OperatorPtr &op, int index) { return impl_->getNormalOutput(op, index); } + // 更新单个输出的描述信息。 + Status UpdateSingleOutputDesc(const OperatorPtr &op, const abstract::BaseShapePtr &shp, const TypePtr &type, + const std::string &format) { + return impl_->UpdateSingleOutputDesc(op, shp, type, format); + } + // 获取自定义运算符的输出数量。 + size_t GetCustomOpOutputSize(const CusOperatorPtr &cus_op) { return impl_->GetCustomOpOutputSize(cus_op); } + // 创建输出描述。 + std::shared_ptr CreateOutputDesc(const abstract::ShapePtr &shape_ptr, const TypePtr &type, + const std::string &format) { + return impl_->CreateOutputDesc(shape_ptr, type, format); + } + // 更新多个输出的描述信息。 + Status UpdateMultiOutputDesc(const OperatorPtr &op, const abstract::BaseShapePtr &shp, const TypePtr &type, + const std::string &format) { + return impl_->UpdateMultiOutputDesc(op, shp, type, format); + } + // 创建节点描述。 + std::shared_ptr CreateNodeDesc(const AnfNodePtr &node, const std::string &format) { + return impl_->CreateNodeDesc(node, format); + } + // 更新普通运算符的输入描述。 + void UpdateNormalOpInputDesc(const OperatorPtr &op, const AnfNodePtr node, const std::string format) { + return impl_->UpdateNormalOpInputDesc(op, node, format); + } + // 更新自定义运算符的输入描述。 + void UpdateCustomOpInputDesc(const CusOperatorPtr &op, const AnfNodePtr &node, const std::string format) { + return impl_->UpdateCustomOpInputDesc(op, node, format); + } + // 更新运算符的输入描述。 + void updateInputDesc(const OperatorPtr &op, const AnfNodePtr &node) { impl_->updateInputDesc(op, node); } + // 更新运算符的输出描述。 + void updateOutputDesc(const OperatorPtr &op, const abstract::BaseShapePtr &shp, const TypePtr &type, + const AnfNodePtr &node) override { + impl_->updateOutputDesc(op, shp, type, node); + } + // 设置运算符的属性。 + int setAttr(const OperatorPtr &op, const std::string &attrKey, const ValuePtr &attrValue) override { + return impl_->setAttr(op, attrKey, attrValue); + } + // 设置自定义运算符的属性。 + int SetCustomOpAttr(const CusOperatorPtr &op, const PrimitivePtr &prim) { return impl_->SetCustomOpAttr(op, prim); } + // 设置普通运算符的属性。 + int SetNormalOpAttr(const OperatorPtr &op, const PrimitivePtr &prim) { return impl_->SetNormalOpAttr(op, prim); } + // 设置运算符的属性。 + int setAttr(const OperatorPtr &op, const PrimitivePtr &prim) override { return impl_->setAttr(op, prim); } + // 设置运算符的属性。 + int setAttr(const OperatorPtr &op, const AnfNodePtr &node) override { return impl_->setAttr(op, node); } + // 获取额外属性。 + mindspore::HashMap GetExtraAttr() override { return extra_attr_; } + + private: + template + static S ConvertAny(const ValuePtr &value, const AnyTraits &) { + return GetValue(value); + } + + // specialization for reverse bool + static bool ConvertAny(const ValuePtr &value, const AnyTraits &, bool reverse) { + return reverse != GetValue(value); + } + + template + static Q ConvertAny(const ValuePtr &value, const AnyTraits

&traits_from, const AnyTraits &traits_to) { + return ConvertAnyUtil(value, traits_from, traits_to); + } + + // specialization for tensor + static GeTensor ConvertAny(const ValuePtr &value, const AnyTraits &traits) { + // To-DO the format may read from ME tensor + return ConvertAnyUtil(value, traits); + } + + // specialization for int + static int64_t ConvertAny(const ValuePtr &value, const AnyTraits) { + return static_cast(GetValue(value)); + } + + // specialization for int or tuple broadcast to Vector + static std::vector ConvertAny(const ValuePtr &value, const std::string &name, + const AnyTraits> anyTraitsInt) { + return ConvertAnyUtil(value, name, anyTraitsInt); + } + + static std::vector> ConvertAny(const ValuePtr &value, + const AnyTraits>>) { + MS_EXCEPTION_IF_NULL(value); + MS_LOG(INFO) << "Value: " << value->type_name(); + std::vector> list; + if (!value->isa()) { + MS_LOG(EXCEPTION) << "Value should be ValueTuple, but got " << value->type_name(); + } + auto vec = value->cast(); + MS_EXCEPTION_IF_NULL(vec); + for (auto &it : vec->value()) { + MS_EXCEPTION_IF_NULL(it); + if (!it->isa()) { + MS_LOG(EXCEPTION) << "It should be ValueTuple, but got " << it->type_name(); + } + auto sub_vector = it->cast(); + std::vector sublist; + for (auto &item : sub_vector->value()) { + sublist.push_back(static_cast(GetValue(item))); + } + list.push_back(sublist); + } + return list; + } + + static std::vector ConvertAny(const ValuePtr &value, const AnyTraits>>, + const AnyTraits>) { + MS_EXCEPTION_IF_NULL(value); + MS_LOG(DEBUG) << "Value: " << value->type_name(); + if (!value->isa()) { + MS_LOG(EXCEPTION) << "Value should be ValueList, but got " << value->type_name(); + } + auto vec = value->cast(); + std::vector list; + for (auto &it : vec->value()) { + MS_EXCEPTION_IF_NULL(it); + if (!it->isa()) { + MS_LOG(EXCEPTION) << "It should be ValueList, but got " << it->type_name(); + } + auto sub_vector = it->cast(); + for (auto &item : sub_vector->value()) { + list.push_back(static_cast(GetValue(item))); + } + } + return list; + } + + static std::vector ConvertAny(const ValuePtr &value, const AnyTraits>, + const AnyTraits>) { + MS_EXCEPTION_IF_NULL(value); + MS_LOG(INFO) << "Value: " << value->type_name(); + std::vector list; + if (value->isa()) { + auto vec = value->cast(); + MS_EXCEPTION_IF_NULL(vec); + for (auto &it : vec->value()) { + list.push_back(static_cast(GetValue(it))); + } + return list; + } + if (value->isa()) { + list.push_back(static_cast(GetValue(value))); + return list; + } + MS_LOG(EXCEPTION) << "Value should be ValueTuple or Scalar, but got " << value->type_name(); + } + + static std::string ConvertAny(const ValuePtr &value, const AnyTraits> anyTraitsVec, + const AnyTraits anyTraitsStr) { + return ConvertAnyUtil(value, anyTraitsVec, anyTraitsStr); + } + + static std::vector ConvertAny(const ValuePtr &value, const AnyTraits> anyTraitsVec, + const AnyTraits anyTraitsFlo) { + return ConvertAnyUtil(value, anyTraitsVec, anyTraitsFlo); + } + + static std::vector ConvertAny(const ValuePtr &value, const std::string &format, + const AnyTraits> anyTraitsVec, + const AnyTraits anyTraitsInt) { + return ConvertAnyUtil(value, format, anyTraitsVec, anyTraitsInt); + } + + // convert value list for value tuple to vector + template + static std::vector ConvertAny(const ValuePtr &value, const AnyTraits

&anyTraitsP, + const AnyTraits> anyTraitsQ) { + return ConvertAnyUtil(value, anyTraitsP, anyTraitsQ); + } + + static int64_t ConvertAny(const ValuePtr &value, const AnyTraits) { + auto name = GetValue(value); + auto it = enum_map_.find(name); + int v = 0; + if (it != enum_map_.end()) { + v = it->second; + } + return v; + } + + static GeDataType ConvertAny(const ValuePtr &value, const AnyTraits anyTraitsGE) { + return ConvertAnyUtil(value, anyTraitsGE); + } + + // convert any value to tensor + static GeTensor ConvertAny(const ValuePtr &value, const AnyTraits anyTraitsValue) { + return ConvertAnyUtil(value, anyTraitsValue); + } + + static const mindspore::HashMap input_map_; + static const mindspore::HashMap dyn_input_map_; + static const mindspore::HashMap output_map_; + static const mindspore::HashMap dyn_output_map_; + static const mindspore::HashMap dyn_subgraph_map_; + static const mindspore::HashMap attr_map_; + static const mindspore::HashMap enum_map_; + // convert input from anf graph to Attr in Operators + static const mindspore::HashMap input_attr_map_; + static mindspore::HashMap> cus_input_map_; + static mindspore::HashMap> cus_output_map_; + mindspore::HashMap extra_attr_; + mindspore::HashMap name_counts_; + const std::shared_ptr impl_; +}; + +template +const mindspore::HashMap OpAdapter::input_map_; +template +const mindspore::HashMap OpAdapter::dyn_input_map_; +template +const mindspore::HashMap OpAdapter::output_map_; +template +const mindspore::HashMap OpAdapter::dyn_output_map_; +template +const mindspore::HashMap OpAdapter::dyn_subgraph_map_; +template +const mindspore::HashMap OpAdapter::attr_map_; +template +const mindspore::HashMap OpAdapter::enum_map_; +template +const mindspore::HashMap OpAdapter::input_attr_map_; +template +mindspore::HashMap> OpAdapter::cus_input_map_; +template +mindspore::HashMap> OpAdapter::cus_output_map_; + +// specialization for method +} // namespace transform +} // namespace mindspore + +#endif // MINDSPORE_CCSRC_TRANSFORM_GRAPH_IR_OP_ADAPTER_H_ diff --git a/mindspore/ccsrc/transform-update/op_adapter_base.h b/mindspore/ccsrc/transform-update/op_adapter_base.h new file mode 100644 index 00000000000..7cb85816dae --- /dev/null +++ b/mindspore/ccsrc/transform-update/op_adapter_base.h @@ -0,0 +1,175 @@ +/** + * Copyright 2019-2021 Huawei Technologies Co., Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MINDSPORE_CCSRC_TRANSFORM_GRAPH_IR_OP_ADAPTER_BASE_H_ +#define MINDSPORE_CCSRC_TRANSFORM_GRAPH_IR_OP_ADAPTER_BASE_H_ + +#include +#include +#include +#include +#include + +#include "utils/hash_map.h" +#include "include/transform/graph_ir/util.h" +#include "ir/anf.h" +#include "ir/primitive.h" +#include "ir/value.h" +#include "include/transform/graph_ir/types.h" +#include "graph/operator_reg.h" +#include "external/ge/ge_api.h" +#include "graph/tensor.h" + +namespace ge { +class CustomOperator : public Operator { //定义类 CustomOperator + public: //函数 + CustomOperator(const string &name, const string &type) : Operator(name, type) {} + + ~CustomOperator() override{}; + + void CustomInputRegister(const string &name) { Operator::InputRegister(name); } + + void CustomOutputRegister(const string &name) { Operator::OutputRegister(name); } + + void CustomInferFuncRegister(const std::function &func) { + Operator::InferFuncRegister(func); + } +}; +} // namespace ge + +namespace mindspore { +namespace transform { +using CusOperatorPtr = std::shared_ptr; +using CustomOperator = ge::CustomOperator; +using AttrFunc = std::function; +using OutputFunc = std::function; +using InputOpFunc = std::function; +using InputHandleFunc = std::function; +using CreateDynInputOpFunc = std::function; +using DynInputOpFunc = std::function; +using DynInputHandleFunc = std::function; +using UpdateOutputDescFunc = std::function; +using CreateDynOutputOpFunc = std::function; +using CreateDynSubGraphFunc = std::function; +using DynSubGraphFunc = std::function; + +//定义结构体 +struct AttrDesc { + std::string name; + AttrFunc set_attr; +}; + +struct InputDesc { + std::string name; + InputOpFunc set_op; + InputHandleFunc set_handle; + UpdateOutputDescFunc update_input_desc; +}; + +struct DynInputDesc { + std::string name; + CreateDynInputOpFunc create_dyn_input; + DynInputOpFunc set_op; + DynInputHandleFunc set_handle; +}; + +struct DynSubGraphDesc { + std::string name; + CreateDynSubGraphFunc create_dyn_subgraph; + DynSubGraphFunc set_subgraph; +}; + +struct OutputDesc { + std::string name; + UpdateOutputDescFunc update_out_desc; +}; + +struct DynOutputDesc { + std::string name; + CreateDynOutputOpFunc create_dyn_output; +}; + +class BaseOpAdapter { //定义类BaseOpAdapter + public: //函数 + virtual ~BaseOpAdapter() {} + virtual OperatorPtr generate(const AnfNodePtr &anf) = 0; + virtual OperatorPtr generate(const std::string &type) { return std::make_shared(type); } + virtual int setSubgraph(const OperatorPtr &op, int index, const std::shared_ptr> &branches) = 0; + virtual int setInput(const OperatorPtr &op, int index, const OperatorPtr &input) = 0; + virtual int setInput(const OperatorPtr &op, int index, const OutHandler &handle) = 0; + virtual int setInput(const OperatorPtr &op, int index, + const std::shared_ptr> &handler_vec) = 0; + virtual int setAttr(const OperatorPtr &op, const std::string &attrKey, const ValuePtr &attrValue) = 0; + virtual int setAttr(const OperatorPtr &op, const PrimitivePtr &prim) = 0; + virtual int setAttr(const OperatorPtr &op, const AnfNodePtr &node) = 0; + virtual mindspore::HashMap GetExtraAttr() = 0; + template ::value>::type> + int setAttr(const OperatorPtr &op, const std::string &attrKey, const std::shared_ptr &attrValue) { + return setAttr(op, attrKey, MakeValue(attrValue)); + } + template ::value>::type> + int setAttr(const OperatorPtr &op, const std::string &attrKey, const T &attrValue) { + return setAttr(op, attrKey, MakeValue(attrValue)); + } + virtual OutHandler getOutput(const OperatorPtr &op, int index) = 0; + virtual void updateOutputDesc(const OperatorPtr &op, const abstract::BaseShapePtr &shp, const TypePtr &type, + const AnfNodePtr &node) = 0; + virtual const mindspore::HashMap &getInputMap() = 0; + virtual const mindspore::HashMap &getInputAttrMap() = 0; + virtual const mindspore::HashMap &getDynInputMap() = 0; + virtual const mindspore::HashMap &getOutputMap() = 0; + virtual const mindspore::HashMap &getDynSubgraphMap() = 0; + void AddAttrToDrawGraph(const std::string &attr_str) { attrs_vec_.push_back(attr_str); } + const std::vector &GetAttrsFromDrawGraph() const { return attrs_vec_; } + void clearAttrVect() { attrs_vec_.clear(); } + + private: //成员变量 + std::vector attrs_vec_; +}; + +using OpAdapterPtr = std::shared_ptr; + +enum AttrType { //enum关键字 + ATTR_INT = 0, + ATTR_FLOAT, + ATTR_DOUBLE, + ATTR_STRING, + ATTR_TENSOR, + ATTR_BOOL, + ATTR_LIST_INT, + ATTR_LIST_ANY_INT, + ATTR_ENUM +}; + +struct GeEnum {}; +struct TFType {}; +struct GEType {}; + +// declare Any type +template +struct AnyTraits { + using type = T; +}; + +template <> +struct AnyTraits { + using type = int64_t; +}; + +using ExtraAttr = mindspore::HashMap; +} // namespace transform +} // namespace mindspore +#endif // MINDSPORE_CCSRC_TRANSFORM_GRAPH_IR_OP_ADAPTER_BASE_H_ diff --git a/mindspore/ccsrc/transform-update/op_adapter_desc.h b/mindspore/ccsrc/transform-update/op_adapter_desc.h new file mode 100644 index 00000000000..1b87cae93c4 --- /dev/null +++ b/mindspore/ccsrc/transform-update/op_adapter_desc.h @@ -0,0 +1,75 @@ +/** + * Copyright 2019 Huawei Technologies Co., Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MINDSPORE_CCSRC_TRANSFORM_GRAPH_IR_OP_ADAPTER_DESC_H_ +#define MINDSPORE_CCSRC_TRANSFORM_GRAPH_IR_OP_ADAPTER_DESC_H_ + +#include +#include "transform/graph_ir/op_adapter.h" + +namespace mindspore { +namespace transform { +class OpAdapterDesc { //定义了一个类OpAdapterDesc + public: //函数 + OpAdapterDesc() : train_(nullptr), infer_(nullptr) {} + + OpAdapterDesc(const OpAdapterPtr &train, const OpAdapterPtr &infer) : train_(train), infer_(infer) {} + + explicit OpAdapterDesc(const OpAdapterPtr &common) : train_(common), infer_(common) {} + + OpAdapterDesc(const OpAdapterDesc &desc) { + this->train_ = desc.train_; + this->infer_ = desc.infer_; + } + + OpAdapterDesc(OpAdapterDesc &&desc) { + this->train_ = desc.train_; + this->infer_ = desc.infer_; + desc.train_ = nullptr; + desc.infer_ = nullptr; + } + + ~OpAdapterDesc() = default; + + OpAdapterPtr Get(bool train) const { return train ? train_ : infer_; } + + OpAdapterDesc &operator=(const OpAdapterDesc &desc) { + if (this != &desc) { + this->train_ = desc.train_; + this->infer_ = desc.infer_; + } + return *this; + } + + OpAdapterDesc &operator=(OpAdapterDesc &&desc) { + if (this != &desc) { + this->train_ = desc.train_; + this->infer_ = desc.infer_; + desc.train_ = nullptr; + desc.infer_ = nullptr; + } + return *this; + } + + private: //成员变量 + OpAdapterPtr train_; + OpAdapterPtr infer_; +}; + +using OpAdapterDescPtr = std::shared_ptr; +} // namespace transform +} // namespace mindspore +#endif // MINDSPORE_CCSRC_TRANSFORM_GRAPH_IR_OP_ADAPTER_DESC_H_ diff --git a/mindspore/ccsrc/transform-update/op_adapter_map.cc b/mindspore/ccsrc/transform-update/op_adapter_map.cc new file mode 100644 index 00000000000..c5d1e9536ec --- /dev/null +++ b/mindspore/ccsrc/transform-update/op_adapter_map.cc @@ -0,0 +1,43 @@ +/** + * Copyright 2019-2021 Huawei Technologies Co., Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "include/transform/graph_ir/op_adapter_map.h" +#include +#include "graph/operator.h" +#include "transform/graph_ir/op_adapter_desc.h" + +namespace mindspore { +namespace transform { +namespace { +// 定义一个 HashMap 来存储字符串到 OpAdapterDescPtr 的映射关系。 +// 键是 std::string 类型,值是 OpAdapterDesc 的 shared_ptr。 +mindspore::HashMap adpt_map_ = { + {kNameCustomOp, std::make_shared(std::make_shared>())}}; +// 使用初始化列表将一个元素插入到 HashMap 中。 +// 键是 "kNameCustomOp",值是一个使用 OpAdapter 作为模板参数构造的 OpAdapterDesc 的 shared_ptr。 +} // namespace + +// 特例化模板,为 ge::Operator 类型的 OpAdapter 创建一个定制的输入映射。 +// 使用 mindspore::HashMap 作为值的 HashMap,然后使用 std::string 作为键的 HashMap。 +template <> +mindspore::HashMap> OpAdapter::cus_input_map_{}; +// 特例化模板,为 ge::Operator 类型的 OpAdapter 创建一个定制的输出映射。 +// 使用 mindspore::HashMap 作为值的 HashMap,然后使用 std::string 作为键的 HashMap。 +template <> +mindspore::HashMap> OpAdapter::cus_output_map_{}; +// OpAdapterMap 类的成员函数,用于返回 OpAdapterMap 的 adpt_map_ 成员引用。 +mindspore::HashMap &OpAdapterMap::get() { return adpt_map_; } +} // namespace transform +} // namespace mindspore diff --git a/mindspore/ccsrc/transform-update/op_adapter_util.cc b/mindspore/ccsrc/transform-update/op_adapter_util.cc new file mode 100644 index 00000000000..a98852cc66d --- /dev/null +++ b/mindspore/ccsrc/transform-update/op_adapter_util.cc @@ -0,0 +1,392 @@ +/** + * Copyright 2019 Huawei Technologies Co., Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "transform/graph_ir/op_adapter_util.h" + +#include +#include +#include + +#include "include/common/utils/utils.h" +#include "utils/check_convert_utils.h" +#include "transform/graph_ir/op_adapter_base.h" +#include "transform/graph_ir/io_format_map.h" + +namespace mindspore { +namespace transform { +// ConvertAnyUtil 函数用于将 MindSpore 中的 Tensor(mindspore::tensor::Tensor)转换为 GE(GraphEngine)中的Tensor(GeTensor)。 +GeTensor ConvertAnyUtil(const ValuePtr &value, const AnyTraits &) { + // To-DO the format may read from ME tensor + // TODO:可能需要从 ME(MindSpore Execution) Tensor 读取格式信息(format) + MS_EXCEPTION_IF_NULL(value); + //// 将 value 强制转换为 MeTensorPtr 类型,MeTensorPtr 是一个智能指针,表示 ME Tensor(MindSpore Execution Tensor)。 + auto me_tensor = value->cast(); + // 调用 TransformUtil::ConvertTensor 函数将 ME Tensor 转换为 GE Tensor。 + // 这里的 kOpFormat_ND 是指定转换后的 GE Tensor 使用的格式,可能是 ND 格式(N-Dimensional)。 + auto ge_tensor = TransformUtil::ConvertTensor(me_tensor, kOpFormat_ND); + // 如果转换后的 GE Tensor 为空,则返回一个空的 GeTensor 对象,否则返回转换后的 GE Tensor。 + return ge_tensor == nullptr ? GeTensor() : *ge_tensor; +} + +// ConvertAnyUtil 函数用于将一个 ValuePtr 类型的值转换为 std::vector 类型。 +// 转换的方式取决于传入的 name 参数和数据类型 AnyTraits>。 +std::vector ConvertAnyUtil(const ValuePtr &value, const std::string &name, + const AnyTraits>) { + MS_EXCEPTION_IF_NULL(value); + std::vector list; // 创建一个 int64_t 类型的 vector,用于存储转换后的结果。 + if (name == "pad") { // 如果传入的 name 是 "pad",则执行特定的转换逻辑。 + if (!value->isa()) { // 确保 value 是 ValueSequence 类型。 + MS_LOG(EXCEPTION) << "Value should be ValueTuple, but got" << value->type_name(); + } + auto vec = value->cast(); // 将 value 转换为 ValueSequencePtr 类型。 + // 调整 vector 的大小以容纳转换后的结果。 + // 由于结果包含两个额外的元素(1和1),因此比 ValueSequence 的大小大2。 + list.resize(vec->value().size() + 2); + // 将额外的两个元素设置为 1。 + list[0] = 1; + list[1] = 1; + // 使用 std::transform 将 ValueSequence 中的元素转换为 int64_t,并存储到 vector 中。 + (void)std::transform(vec->value().begin(), vec->value().end(), list.begin() + 2, + [](const ValuePtr &val) { return static_cast(GetValue(val)); }); + } else { // 如果 name 不是 "pad",则执行通用的转换逻辑。 + int64_t data = GetValue(value); // 从 value 中获取 int64_t 类型的数据。 + int size = 2; // 2 int in list // 设置 vector 的大小为2,以容纳两个 int64_t 类型的元素。 + // 调用 TransformUtil::ConvertIntToList 函数将 int64_t 转换为 std::vector。 + list = TransformUtil::ConvertIntToList(data, size); + } + + return list; // 返回转换后的 std::vector 对象。 +} + +// ConvertAnyUtil 函数用于将一个 ValuePtr 类型的值转换为 std::string 类型。 +// 转换的方式取决于传入的数据类型 AnyTraits> 和 AnyTraits。 +std::string ConvertAnyUtil(const ValuePtr &value, const AnyTraits>, const AnyTraits) { + MS_EXCEPTION_IF_NULL(value); + auto vec = value->cast(); // 将 value 转换为 ValueTuplePtr 类型。 + if (vec == nullptr) { // 如果 vec 为空指针,则抛出异常,说明传入的 value 不是 ValueTuplePtr 类型。 + MS_LOG(EXCEPTION) << "not ValueTuplePtr"; + } + std::ostringstream buffer; // 创建一个 ostringstream 对象,用于构建字符串。 + int i = 0; // 用于辅助构建字符串的计数器。 + for (auto &it : vec->value()) { // 遍历 value 中的元素。 + if (i != 0) { // 在每个元素之前加入逗号(除了第一个元素)。 + buffer << ","; + } + buffer << GetValue(it); // 将元素的值转换为 int64_t,并添加到字符串流中。 + i++; // 增加计数器。 + } + return buffer.str(); // 返回构建的字符串。 +} + +// ConvertAnyUtil 函数用于将一个 ValuePtr 类型的值转换为 std::vector 类型。 +// 转换的方式取决于传入的数据类型 AnyTraits> 和 AnyTraits。 +std::vector ConvertAnyUtil(const ValuePtr &value, const AnyTraits>, const AnyTraits) { + MS_EXCEPTION_IF_NULL(value); + auto vec = value->cast(); // 将 value 转换为 ValueTuplePtr 类型。 + if (vec == nullptr) { // 如果 vec 为空指针,则抛出异常,说明传入的 value 不是 ValueTuplePtr 类型。 + MS_LOG(EXCEPTION) << "not ValueTuplePtr"; + } + std::vector list; // 创建一个 std::vector 对象,用于存储转换后的结果。 + list.resize(vec->value().size()); // 调整 vector 的大小以容纳转换后的结果,大小与 ValueTuple 中的元素个数相同。 + // 使用 std::transform 将 ValueTuple 中的每个元素转换为 float,并存储到 vector 中。 + (void)std::transform(vec->value().begin(), vec->value().end(), list.begin(), + [](const ValuePtr &val) { return static_cast(GetValue(val)); }); + return list; // 返回转换后的 std::vector 对象。 +} + +// ConvertAnyUtil 函数用于将一个 ValuePtr 类型的值转换为 std::vector 类型。 +// 转换的方式取决于传入的 format 参数和数据类型 AnyTraits> 和 AnyTraits。 +std::vector ConvertAnyUtil(const ValuePtr &value, const std::string &format, + const AnyTraits>, const AnyTraits) { + MS_EXCEPTION_IF_NULL(value); + auto vec = value->cast(); // 将 value 转换为 ValueTuplePtr 类型。 + if (vec == nullptr) { // 如果 vec 为空指针,则抛出异常,说明传入的 value 不是 ValueTuplePtr 类型。 + MS_LOG(EXCEPTION) << "not ValueTuplePtr"; + } + std::vector list; // 创建一个 std::vector 对象,用于存储转换后的结果。 + list.resize(vec->value().size()); // 调整 vector 的大小以容纳转换后的结果,大小与 ValueTuple 中的元素个数相同。 + // 使用 std::transform 将 ValueTuple 中的每个元素转换为 int64_t,并存储到 vector 中。 + (void)std::transform(vec->value().begin(), vec->value().end(), list.begin(), + [](const ValuePtr &val) { return static_cast(GetValue(val)); }); + if (format == kOpFormat_NHWC) { // 根据传入的 format 参数执行特定的格式转换。 + if (list.size() < 4) { // 如果格式为 NHWC,但列表大小小于4,则抛出异常。 + MS_LOG(EXCEPTION) << "The size of list is less than 4"; + } else { // 如果格式为 NHWC,并且列表大小大于等于4,则进行格式转换。 + // 将列表中的第1个元素和第2个元素交换位置,将第3个元素和第4个元素交换位置。 + int64_t temp = list[1]; + list[1] = list[2]; + list[2] = list[3]; + list[3] = temp; + } + } + return list; // 返回转换后的 std::vector 对象。 +} + +// ConvertAnyUtil 函数用于将一个 ValuePtr 类型的值转换为 GeDataType(GraphEngine 的数据类型)。 +// 转换的方式取决于传入的数据类型 AnyTraits。 +GeDataType ConvertAnyUtil(const ValuePtr &value, const AnyTraits) { + MS_EXCEPTION_IF_NULL(value); + if (!value->isa()) { // 确保 value 是 Type 类型。 + MS_LOG(EXCEPTION) << "error convert Value to TypePtr for value: " << value->ToString() + << ", type: " << value->type_name() << ", value should be a Typeptr"; + } + auto type = value->cast(); // 将 value 转换为 TypePtr 类型。 + MS_EXCEPTION_IF_NULL(type); // 确保转换后的 type 不为空指针。 + TypeId me_type = type->type_id(); // 获取 TypePtr 对象的 TypeId(MindSpore 中的数据类型标识)。 + // 如果 TypePtr 对象的 TypeId 是 kObjectTypeTensorType,表示其为 TensorType 类型。 + // 需要进一步获取其元素类型的 TypeId,以便进行后续的 GraphEngine 数据类型转换。 + if (kObjectTypeTensorType == me_type) { + me_type = dyn_cast(type)->element()->type_id(); + } + return TransformUtil::ConvertDataType(me_type); // 调用 TransformUtil::ConvertDataType 函数将 MindSpore 的数据类型转换为 GraphEngine 的数据类型。 +} + +// VectorToTensorUtil 函数用于将一个 ValuePtr 类型的值转换为 GeTensor(GraphEngine 的 Tensor)。 +// 该函数支持将 tuple 或 list 转换为 GeTensor,目前仅支持一维数据。 +GeTensor VectorToTensorUtil(const ValuePtr &value) { + // convert tuple or list to ge tensor, only supported one dim for now + // 转换 tuple 或 list 到 ge tensor,目前仅支持一维数据 + MS_EXCEPTION_IF_NULL(value); + // 获取 tuple 或 list 中的元素值。 + auto vec = value->isa() ? value->cast()->value() : value->cast()->value(); + if (vec.empty()) { // 如果 tuple 或 list 为空,则返回一个空的 GeTensor。 + MS_LOG(WARNING) << "Convert a none tuple to an empty ge tensor"; + return GeTensor(GeTensorDesc(ge::Shape({0}))); + } + MS_EXCEPTION_IF_NULL(vec[0]); // 获取第一个元素,并确保它不为空。 + // 根据第一个元素的数据类型执行相应的转换逻辑。 + // 如果第一个元素是 Int32Imm 类型,表示需要将数据转换为 int32_t 类型的 GeTensor。 + if (vec[0]->isa()) { + MS_LOG(INFO) << "convert value to tensor with data type = Int32"; + // 将数据转换为 int32_t 类型的 std::vector。 + auto data = ConvertAnyUtil(value, AnyTraits(), AnyTraits>()); + // 获取对应的 GeTensorDesc 描述信息。 + auto desc = TransformUtil::GetGeTensorDesc({static_cast(vec.size())}, kNumberTypeInt32, kOpFormat_NCHW); + // 如果获取描述信息失败,则抛出异常。 + if (desc == nullptr) { + MS_LOG(EXCEPTION) << "Update conversion descriptor failed!"; + } + // 创建 GeTensor,并使用 int32_t 类型的数据填充 Tensor 数据。 + return GeTensor(*desc, reinterpret_cast(data.data()), data.size() * sizeof(int32_t)); + // 如果第一个元素是 Int64Imm 类型,表示需要将数据转换为 int64_t 类型的 GeTensor。 + } else if (vec[0]->isa()) { + MS_LOG(INFO) << "convert value to tensor with data type = Int64"; + // 将数据转换为 int64_t 类型的 std::vector。 + auto data = ConvertAnyUtil(value, AnyTraits(), AnyTraits>()); + // 获取对应的 GeTensorDesc 描述信息。 + auto desc = TransformUtil::GetGeTensorDesc({static_cast(vec.size())}, kNumberTypeInt64, kOpFormat_NCHW); + if (desc == nullptr) { // 如果获取描述信息失败,则抛出异常。 + MS_LOG(EXCEPTION) << "Update conversion descriptor failed!"; + } + // 创建 GeTensor,并使用 int64_t 类型的数据填充 Tensor 数据。 + return GeTensor(*desc, reinterpret_cast(data.data()), data.size() * sizeof(int64_t)); + // 如果第一个元素是 FP32Imm 类型,表示需要将数据转换为 float 类型的 GeTensor。 + } else if (vec[0]->isa()) { + MS_LOG(INFO) << "convert value to tensor with data type = Float32"; + // 将数据转换为 float 类型的 std::vector。 + auto data = ConvertAnyUtil(value, AnyTraits(), AnyTraits>()); + // 获取对应的 GeTensorDesc 描述信息。 + auto desc = TransformUtil::GetGeTensorDesc({static_cast(vec.size())}, kNumberTypeFloat32, kOpFormat_NCHW); + if (desc == nullptr) { // 如果获取描述信息失败,则抛出异常。 + MS_LOG(EXCEPTION) << "Update conversion descriptor failed!"; + } + // 创建 GeTensor,并使用 float 类型的数据填充 Tensor 数据。 + return GeTensor(*desc, reinterpret_cast(data.data()), data.size() * sizeof(float)); + } else if (vec[0]->isa()) { // 如果第一个元素是 BoolImm 类型,表示需要将数据转换为 bool 类型的 GeTensor。 + MS_LOG(INFO) << "convert value to tensor with data type = Bool"; + // We use uint8_t to save bool type data + // 将数据转换为 bool 类型的 std::vector。 + // 这里使用 uint8_t + auto data = ConvertAnyUtil(value, AnyTraits(), AnyTraits>()); + auto desc = TransformUtil::GetGeTensorDesc({static_cast(vec.size())}, kNumberTypeBool, kOpFormat_NCHW); + if (desc == nullptr) { + MS_LOG(EXCEPTION) << "Update conversion descriptor failed!"; + } + return GeTensor(*desc, static_cast(data.data()), data.size() * sizeof(uint8_t)); + } else { + MS_LOG(EXCEPTION) << "Unsupported data type of tuple or list elements: " << vec[0]->type_name(); + } +} + +// ConvertAnyUtil 函数用于将一个 ValuePtr 类型的值转换为 GeTensor(GraphEngine 的 Tensor)。 +// 转换的方式取决于传入的数据类型 AnyTraits。 +GeTensor ConvertAnyUtil(const ValuePtr &value, const AnyTraits) { + MS_EXCEPTION_IF_NULL(value); + if (value->isa()) { // 检查 ValuePtr 是否是 MeTensor 类型,如果是,则执行 MeTensor 到 GeTensor 的转换。 + // convert me tensor to ge tensor + // 将 MeTensor 转换为 GeTensor + return ConvertAnyUtil(value, AnyTraits()); + // 检查 ValuePtr 是否是 ValueList 或 ValueTuple 类型,如果是,则执行 List 或 Tuple 到 GeTensor 的转换。 + } else if (value->isa() || value->isa()) { + return VectorToTensorUtil(value); + // 检查 ValuePtr 是否是 Int32Imm 类型,如果是,则执行 Int32Imm 到 GeTensor 的转换。 + } else if (value->isa()) { + // convert scalar Int to GeTensor + // 将标量 Int32 转换为 GeTensor + MS_LOG(INFO) << "convert scalar to tensor with data type = Int32"; + GeTensorDesc desc(GeShape(), ge::FORMAT_NCHW, ge::DT_INT32); // 创建 GeTensorDesc 描述信息。 + auto v = GetValue(value); + desc.SetRealDimCnt(0); // 设置描述信息的实际维度数为0(标量)。 + return GeTensor(desc, reinterpret_cast(&v), sizeof(int32_t)); // 创建 GeTensor,并使用 int32_t 类型的数据填充 Tensor 数据。 + } + // 检查 ValuePtr 是否是 Int64Imm 类型,如果是,则执行 Int64Imm 到 GeTensor 的转换。 + else if (value->isa()) { + // convert scalar Int64 to GeTensor + // 将标量 Int64 转换为 GeTensor + MS_LOG(INFO) << "convert scalar to tensor with data type = Int64"; + GeTensorDesc desc(GeShape(), ge::FORMAT_NCHW, ge::DT_INT64); // 创建 GeTensorDesc 描述信息。 + auto v = GetValue(value); + desc.SetRealDimCnt(0); // 设置描述信息的实际维度数为0(标量)。 + return GeTensor(desc, reinterpret_cast(&v), sizeof(int64_t)); // 创建 GeTensor,并使用 int64_t 类型的数据填充 Tensor 数据。 + } + // 检查 ValuePtr 是否是 FP32Imm 类型,如果是,则执行 FP32Imm 到 GeTensor 的转换。 + else if (value->isa()) { + // convert scalar FP32 to GeTensor + MS_LOG(INFO) << "convert scalar to tensor with data type = FP32"; // 将标量 FP32 转换为 GeTensor + GeTensorDesc desc(GeShape(), ge::FORMAT_NCHW, ge::DT_FLOAT); // 创建 GeTensorDesc 描述信息。 + auto v = GetValue(value); + desc.SetRealDimCnt(0); // 设置描述信息的实际维度数为0(标量)。 + return GeTensor(desc, reinterpret_cast(&v), sizeof(float)); // 创建 GeTensor,并使用 float 类型的数据填充 Tensor 数据。 + } + // 检查 ValuePtr 是否是 BoolImm 类型,如果是,则执行 BoolImm 到 GeTensor 的转换。 + else if (value->isa()) { + // convert scalar FP32 to GeTensor + // 将标量 Bool 转换为 GeTensor + MS_LOG(INFO) << "convert scalar to tensor with data type = Bool"; + GeTensorDesc desc(GeShape(), ge::FORMAT_NCHW, ge::DT_BOOL); // 创建 GeTensorDesc 描述信息。 + auto v = GetValue(value); + desc.SetRealDimCnt(0); // 设置描述信息的实际维度数为0(标量)。 + return GeTensor(desc, reinterpret_cast(&v), sizeof(bool)); // 创建 GeTensor,并使用 bool 类型的数据填充 Tensor 数据。 + } + // 检查 ValuePtr 是否是 StringImm 类型,如果是,则执行 StringImm 到 GeTensor 的转换。 + else if (value->isa()) { + // convert String to GeTensor + // 将标量 String 转换为 GeTensor + MS_LOG(INFO) << "convert string to tensor with data type = String"; + std::string v = GetValue(value); // 获取 string 类型的值。 + std::vector ge_shape; // 创建 GeTensorDesc 描述信息。 + GeShape shape(ge_shape); + GeTensorDesc desc(shape, ge::FORMAT_NCHW, ge::DT_STRING); + GeTensor str_tensor(desc); + (void)str_tensor.SetData(v); + return str_tensor; + } else { + MS_LOG(WARNING) << "Unsupported value type: " << value->type_name() + << " to convert to tensor. Value: " << value->ToString(); + } + return GeTensor(); +} + +// IsCustomPrim 函数用于判断给定的 PrimitivePtr 是否为自定义的操作(Custom Primitive)。 +bool IsCustomPrim(const PrimitivePtr &prim) { + if (prim == nullptr) { // 如果给定的 PrimitivePtr 为空指针,则返回 false。 + return false; + } + // 从 Primitive 的属性中获取名为 "_custom_op_flag" 的属性值。 + ValuePtr flag = prim->GetAttr("_custom_op_flag"); + if (flag == nullptr) { // 如果获取到的属性值为空指针,则返回 false。 + return false; + } + // 将属性值转换为 bool 类型,并存储在变量 is_custom_op 中。 + bool is_custom_op = GetValue(flag); + // 如果 is_custom_op 为 false,同时 Primitive 的属性中有名为 "_custom_op_impl_config_path" 的属性, + // 则抛出异常,提示非自定义操作不应该分配 "_custom_op_impl_config_path" 属性。 + if (!is_custom_op && prim->GetAttr("_custom_op_impl_config_path") != nullptr) { + MS_LOG(EXCEPTION) << "The custom op flag is false, but the op information config path is not null, non-custom op " + "can not assign the op information config path."; + } + + return is_custom_op; // 返回 is_custom_op,表示给定的 Primitive 是否为自定义的操作。 +} + +// IsCustomCNode 函数用于判断给定的 AnfNodePtr 是否为自定义的 CNode。 +// 自定义 CNode 是指其第一个输入是 ValueNode,而该 ValueNode 包含一个自定义的 PrimitivePtr。 +bool IsCustomCNode(const AnfNodePtr &anf) { + if (anf == nullptr) { // 如果给定的 AnfNodePtr 为空指针,则返回 false。 + return false; + } + auto node = anf->cast(); // 将 AnfNodePtr 转换为 CNodePtr。 + if (node == nullptr) { // 如果转换失败,说明给定的 AnfNodePtr 不是 CNode,返回 false。 + return false; + } + if (node->inputs().empty()) { // 检查 CNode 的输入是否为空,如果为空,抛出异常。 + MS_LOG(EXCEPTION) << "Length of node inputs is empty"; + } + MS_EXCEPTION_IF_NULL(node->inputs()[0]); // 检查 CNode 的第一个输入是否为空指针,如果是,抛出异常。 + // 检查 CNode 的第一个输入是否为 ValueNode,如果不是,返回 false,表示不是自定义 CNode。 + if (!node->inputs()[0]->isa()) { + return false; + } + // 尝试将 CNode 的第一个输入转换为 ValueNode,并获取其包含的 PrimitivePtr。 + auto cus_prim = GetValueNode(node->inputs()[0]); + if (cus_prim == nullptr) { // 如果获取的 PrimitivePtr 为空指针,返回 false,表示不是自定义 CNode。 + return false; + } + + return IsCustomPrim(cus_prim); // 调用 IsCustomPrim 函数判断获取的 PrimitivePtr 是否为自定义的操作,返回判断结果。 +} + +// GetOpIOFormat 函数用于获取给定 AnfNodePtr 对应的操作的输入输出格式(IO Format)。 +std::string GetOpIOFormat(const AnfNodePtr &anf) { + std::string ret; + if (anf == nullptr) { // 检查给定的 AnfNodePtr 是否为空指针,如果是,输出错误日志并返回空字符串。 + MS_LOG(ERROR) << "The anf is nullptr"; + return ret; + } + auto node = anf->cast(); // 尝试将 AnfNodePtr 转换为 CNodePtr。 + if (node == nullptr) { // 如果转换失败,说明给定的 AnfNodePtr 不是 CNode,输出错误日志并返回空字符串。 + MS_LOG(ERROR) << "The anf is not a cnode."; + return ret; + } + if (node->inputs().empty()) { // 检查 CNode 的输入是否为空,如果为空,抛出异常。 + MS_LOG(EXCEPTION) << "Length of node inputs is empty."; + } + MS_EXCEPTION_IF_NULL(node->inputs()[0]); // 检查 CNode 的第一个输入是否为空指针,如果是,抛出异常。 + if (!node->inputs()[0]->isa()) { // 检查 CNode 的第一个输入是否为 ValueNode,如果不是,输出错误日志并返回空字符串。 + MS_LOG(ERROR) << "The anf is not a value node."; + return ret; + } + auto prim = GetValueNode(node->inputs()[0]); // 尝试将 CNode 的第一个输入转换为 ValueNode,并获取其包含的 PrimitivePtr。 + if (prim == nullptr) { // 如果获取的 PrimitivePtr 为空指针,输出错误日志并返回空字符串。 + MS_LOG(ERROR) << "The anf is not a Primitive."; + return ret; + } + if (prim->HasAttr("io_format")) { // 检查 PrimitivePtr 是否有名为 "io_format" 的属性,如果有,则返回其属性值作为 IO Format。 + return GetValue(prim->GetAttr("io_format")); + } + // 如果 PrimitivePtr 没有名为 "io_format" 的属性,则从 IOFormatMap 中查找操作名对应的 IO Format。 + auto io_format_map = IOFormatMap::get(); + auto iter = io_format_map.find(prim->name()); + if (iter == io_format_map.end()) { // 如果在 IOFormatMap 中没有找到对应的 IO Format,则默认返回 "NCHW"。 + return "NCHW"; + } + // 检查该 IO Format 是否是 "format" 类型的属性,如果是,进一步处理后返回具体的格式值。 + if (iter->second == "format") { + ValuePtr format = prim->GetAttr("format"); + MS_EXCEPTION_IF_NULL(format); + if (format->isa()) { + bool converted = CheckAndConvertUtils::ConvertAttrValueToString(prim->name(), "format", &format); + if (converted) { + return GetValue(format); + } + } else { + return GetValue(format); + } + } + return iter->second; // 如果不是 "format" 类型的属性,直接返回 IO Format。 +} +} // namespace transform +} // namespace mindspore diff --git a/mindspore/ccsrc/transform-update/op_adapter_util.h b/mindspore/ccsrc/transform-update/op_adapter_util.h new file mode 100644 index 00000000000..3ac31810cba --- /dev/null +++ b/mindspore/ccsrc/transform-update/op_adapter_util.h @@ -0,0 +1,72 @@ +/** + * Copyright 2019 Huawei Technologies Co., Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MINDSPORE_CCSRC_TRANSFORM_GRAPH_IR_OP_ADAPTER_UTIL_H_ +#define MINDSPORE_CCSRC_TRANSFORM_GRAPH_IR_OP_ADAPTER_UTIL_H_ + +#include +#include + +#include "transform/graph_ir/op_adapter_base.h" + +namespace mindspore { +namespace transform { +template +static Q ConvertAnyUtil(const ValuePtr &value, const AnyTraits

&, const AnyTraits &) { + return static_cast(GetValue

(value)); +} + +GeTensor ConvertAnyUtil(const ValuePtr &value, const AnyTraits &traits); + +std::vector ConvertAnyUtil(const ValuePtr &value, const std::string &name, + const AnyTraits>); + +std::string ConvertAnyUtil(const ValuePtr &value, const AnyTraits>, const AnyTraits); + +std::vector ConvertAnyUtil(const ValuePtr &value, const AnyTraits>, const AnyTraits); + +std::vector ConvertAnyUtil(const ValuePtr &value, const std::string &format, + const AnyTraits>, const AnyTraits); + +GeDataType ConvertAnyUtil(const ValuePtr &value, const AnyTraits); + +template +// ConvertAnyUtil 函数用于将给定的 ValuePtr 转换为具有类型 P 的元素的 std::vector。 +// 这里 P 和 Q 可以是不同的类型。 +std::vector ConvertAnyUtil(const ValuePtr &value, AnyTraits

, const AnyTraits>) { + MS_EXCEPTION_IF_NULL(value); // 检查给定的 ValuePtr 是否为空指针,如果是,抛出异常。 + // 检查给定的 ValuePtr 是否为 ValueTuple 或 ValueList,如果不是,抛出异常。 + if (!value->isa() && !value->isa()) { + MS_LOG(EXCEPTION) << "error convert Value to vector for value: " << value->ToString() + << ", type: " << value->type_name() << ", value should be a tuple or list"; + } + // 获取 ValuePtr 中的数据集合,可以是 ValueTuple 或 ValueList。 + auto vec = value->isa() ? value->cast()->value() : value->cast()->value(); + std::vector data; // 创建 std::vector,用于存储转换后的结果。 + for (auto &it : vec) { // 遍历集合中的每个元素,对每个元素调用 ConvertAnyUtil 进行转换,并将结果添加到 data 中。 + data.push_back(ConvertAnyUtil(it, AnyTraits

(), AnyTraits())); + } + return data; // 返回转换后的 std::vector。 +} + +GeTensor ConvertAnyUtil(const ValuePtr &value, const AnyTraits); + +bool IsCustomPrim(const PrimitivePtr &prim); +bool IsCustomCNode(const AnfNodePtr &node); +std::string GetOpIOFormat(const AnfNodePtr &node); +} // namespace transform +} // namespace mindspore +#endif // MINDSPORE_CCSRC_TRANSFORM_GRAPH_IR_OP_ADAPTER_UTIL_H_ diff --git a/mindspore/ccsrc/transform-update/op_declare_macro.h b/mindspore/ccsrc/transform-update/op_declare_macro.h new file mode 100644 index 00000000000..1c290a2ca29 --- /dev/null +++ b/mindspore/ccsrc/transform-update/op_declare_macro.h @@ -0,0 +1,213 @@ +/** + * Copyright 2019-2021 Huawei Technologies Co., Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MINDSPORE_CCSRC_TRANSFORM_GRAPH_IR_OP_DECLARE_MACRO_H_ +#define MINDSPORE_CCSRC_TRANSFORM_GRAPH_IR_OP_DECLARE_MACRO_H_ + +#include +#include +#include "utils/hash_map.h" +#include "transform/graph_ir/op_adapter.h" +#include "transform/graph_ir/op_adapter_desc.h" +#include "include/transform/graph_ir/op_adapter_map.h" +#include "mindspore/core/base/core_ops.h" + +namespace mindspore::transform { +//定义了一个宏 DECLARE_OP_ADAPTER(T),用于声明对应的Op Adapter +#define DECLARE_OP_ADAPTER(T) \ + using T = ge::op::T; \//定义一个别名 T,表示对应GE框架中的操作ge::op::T + template <> \ + const mindspore::HashMap OpAdapter::input_map_; \//一个模板特化,它声明了一个静态成员变量 input_map_,用于存储 T 类型的操作的输入映射信息。 + // InputDesc 是一个自定义的结构体,用于描述输入的信息,例如输入的名称和相应的处理函数。 + template <> \ + const mindspore::HashMap OpAdapter::attr_map_; //一个模板特化,它声明了一个静态成员变量 attr_map_,用于存储 T 类型的操作的属性映射信息。 + //AttrDesc 是一个自定义的结构体,用于描述属性的信息,例如属性的名称和相应的处理函数。 + +#define DECLARE_OP_USE_OUTPUT(T) \ + template <> \ + const mindspore::HashMap OpAdapter::output_map_;// 声明了 OpAdapter 类的一个模板特化,使用模板参数 T来定义 output_map_。 + // 这个宏用于为 OpAdapter 类声明一个 output_map_ 的模板特化。它将整数键与 OutputDesc 值关联起来。 output_map_ 模板用于将整数标识符映射到 OutputDesc + + +#define DECLARE_OP_USE_ENUM(T) \ + template <> \ + const mindspore::HashMap OpAdapter::enum_map_{};// 声明了 OpAdapter 类的一个模板特化,使用模板参数 T,来定义了一个空的 enum_map_。 + // 这个宏用于为 OpAdapter 类声明一个 enum_map_ 的模板特化。它将字符串键与整数值关联起来。模板参数 T 表示数据类型。 + +#define DECLARE_OP_USE_INPUT_ATTR(T) \ + template <> \ + const mindspore::HashMap OpAdapter::input_attr_map_;// 声明了 OpAdapter 类的一个模板特化,使用模板参数 T,来定义 input_attr_map_。 + // 这个宏用于为 OpAdapter 类声明一个 input_attr_map_ 的模板特化。它将无符号整数键与 AttrDesc 值关联起来。 + + +#define DECLARE_OP_USE_DYN_INPUT(T) \ + template <> \ + const mindspore::HashMap OpAdapter::dyn_input_map_;//声明了 OpAdapter 类的一个模板特化,使用模板参数 T,来定义 dyn_input_map_。 + // 这个宏用于为 OpAdapter 类声明一个 dyn_input_map_ 的模板特化。它将整数键与 DynInputDesc 值关联起来。 + +#define DECLARE_OP_USE_DYN_SUBGRAPH(T) \ + template <> \ + const mindspore::HashMap OpAdapter::dyn_subgraph_map_;//声明了 OpAdapter 类的一个模板特化,使用模板参数 T,来定义dyn_subgraph_map_。 +// 这个宏用于为 OpAdapter 类声明一个 dyn_input_map_ 的模板特化。它将整数键与 DynInputDesc值关联起来。 + +#define DECLARE_OP_USE_DYN_OUTPUT(T) \ + template <> \ + const mindspore::HashMap OpAdapter::dyn_output_map_;//声明了 OpAdapter 类的一个模板特化,使用模板参数 T,来定义dyn_output_map_。 +// 这个宏用于为 OpAdapter 类声明一个 dyn_input_map_ 的模板特化。它将整数键与 DynInputDesc值关联起来。 + +#define INPUT_MAP(T) \ + template <> \ + const mindspore::HashMap OpAdapter::input_map_ + // 定义宏 EMPTY_INPUT_MAP,表示一个空的输入映射,使用 mindspore::HashMap() 初始化。 + + // 定义宏 INPUT_DESC(name),用于为输入描述创建一个匿名函数对象。 +#define EMPTY_INPUT_MAP mindspore::HashMap() +#define INPUT_DESC(name) \ + { \ +#name, \ + [](const OperatorPtr op, const OperatorPtr input) { \//设置输入 + auto p = std::static_pointer_cast(op); \ + (void)p->set_input_##name(*input); \ + }, \ + [](const OperatorPtr op, const OutHandler& handle) { \//处理输出 + auto p = std::static_pointer_cast(op); \ + (void)p->set_input_##name(*(handle.op), handle.out); \ + }, \ + [](const OperatorPtr op, const GeTensorDesc desc) { \//更新描述 + auto p = std::static_pointer_cast(op); \ + (void)p->update_input_desc_##name(desc); \ + } \ + }//为输入描述提供注释和操作 + + + // 定义宏 DYN_INPUT_MAP(T),用于为 OpAdapter 类声明动态输入映射特化。 +#define DYN_INPUT_MAP(T) \ + template <> \ + const mindspore::HashMap OpAdapter::dyn_input_map_ + + // 定义宏 DYN_INPUT_DESC(name),用于为动态输入描述创建一个匿名函数对象。 +#define DYN_INPUT_DESC(name) \ + { \ +#name, \ + [](const OperatorPtr op, unsigned int num) { \//创建 + auto p = std::static_pointer_cast(op); \ + (void)p->create_dynamic_input_##name(num); \ + }, \ + [](const OperatorPtr op, unsigned int index, const OperatorPtr input) { \//设置输入 + auto p = std::static_pointer_cast(op); \ + (void)p->set_dynamic_input_##name(index, *input); \ + }, \ + [](const OperatorPtr op, unsigned int index, const OutHandler& handle) { \//处理输出 + auto p = std::static_pointer_cast(op); \ + (void)p->set_dynamic_input_##name(index, *(handle.op), handle.out); \ + } \ + }//为输入描述提供注释和操作 + +// 定义宏 DYN_SUBGRAPH_MAP(T),用于为 OpAdapter 类声明动态子图映射特化。 +#define DYN_SUBGRAPH_MAP(T) \ + template <> \ + const mindspore::HashMap OpAdapter::dyn_subgraph_map_ + +// 定义宏 DYN_SUBGRAPH_DESC(name),用于为动态子图描述创建一个匿名函数对象。 +#define DYN_SUBGRAPH_DESC(name) \ + { \ +#name, \ + [](const OperatorPtr op, unsigned int num) { \//创建动态子图 + auto p = std::static_pointer_cast(op); \ + (void)p->create_dynamic_subgraph_##name(num); \ + }, \ + [](const OperatorPtr op, unsigned int index, const DfGraphPtr graph) { \//设置子图构建器 + auto p = std::static_pointer_cast(op); \ + (void)p->set_dynamic_subgraph_builder_##name(index, [graph](){return *graph;}); \ + } \ + }// 为动态子图描述提供注释和操作 + + // 定义宏 ATTR_MAP(T),用于为 OpAdapter 类声明属性映射特化。 +#define ATTR_MAP(T) \ + template <> \ + const mindspore::HashMap OpAdapter::attr_map_ +#define EMPTY_ATTR_MAP mindspore::HashMap() + // 定义宏 EMPTY_ATTR_MAP,表示一个空的属性映射,使用 mindspore::HashMap() 初始化。 +#define ATTR_DESC(name, ...) \ + // 定义宏 ATTR_DESC(name, ...),用于为属性描述创建一个匿名函数对象。 + { \ +#name, \ + [](const OperatorPtr op, const ValuePtr& value) { \ //设置属性值 + auto p = std::static_pointer_cast(op); \ + (void)p->set_attr_##name(ConvertAny(value, __VA_ARGS__)); \ + } \ + }// 为属性描述提供注释和操作 + + // 定义宏 INPUT_ATTR_MAP(T),用于为 OpAdapter 类声明输入属性映射特化。 + + //INPUT_ATTR_MAP 宏定义了一个针对类型 T 的模板特化。在这个特化中,有一个名为 input_attr_map_ 的常量哈希映射,它将无符号整数映射到 AttrDesc 对象 +#define INPUT_ATTR_MAP(T) \ + template <> \ + const mindspore::HashMap OpAdapter::input_attr_map_ //输入属性映射表 + + //OUTPUT_MAP 宏定义了一个针对类型 T 的模板特化。在这个特化中,有一个名为 output_map_ 的常量哈希映射,它将整数映射到 OutputDesc 对象 +#define OUTPUT_MAP(T) \ + template <> \ + const mindspore::HashMap OpAdapter::output_map_ //输出属性映射表 +#define OUTPUT_DESC(name) \ + { \ +#name, \ + [](const OperatorPtr op, const GeTensorDesc desc) { \ //定义一个 Lambda 表达式,接收 OperatorPtr 和 GeTensorDesc 参数 + auto p = std::static_pointer_cast(op); \ //将 OperatorPtr 转换为 OpType 的智能指针 + (void)p->update_output_desc_##name(desc); \ //调用 OpType 类的成员函数 update_output_desc_name,其中 name 是宏展开的参数 + } \ + } + + //DYN_OUTPUT_MAP 宏定义了一个针对类型 T 的模板特化。在这个特化中,有一个名为 dyn_output_map_ 的常量哈希映射,它将整数映射到 DynOutputDesc 对象 +#define DYN_OUTPUT_MAP(T) \ + template <> \ + const mindspore::HashMap OpAdapter::dyn_output_map_ //动态输出映射表 + +#define DYN_OUTPUT_DESC(name) \ + { \ +#name, \ + [](const OperatorPtr op, unsigned int num) { \ //定义一个 Lambda 表达式,接收 OperatorPtr 和 unsigned int 参数 + auto p = std::static_pointer_cast(op); \ //将 OperatorPtr 转换为 OpType 的智能指针 + (void)p->create_dynamic_output_##name(num); \//调用 OpType 类的成员函数 create_dynamic_output_name,其中 name 是宏展开的参数,num 是传递给函数的 unsigned int 参数 + } \ + } + +#define ADPT_DESC_ONE(T) std::make_shared(std::make_shared>()) +//使用 std::make_shared>() 创建了一个 OpAdapter 类型的智能指针,并将其作为参数传递给 std::make_shared() 来创建 OpAdapterDesc 类型的智能指针。 +#define ADPT_DESC_TWO(T, I) \ + std::make_shared(std::make_shared>(), std::make_shared>()) +//这个宏定义返回一个 std::shared_ptr 对象。 +//它使用 std::make_shared>()`` 和 std::make_shared>()创建了两个不同类型的智能指针,然后将它们作为参数传递给std::make_shared()来创建OpAdapterDesc类型的智能指针。 +//这个宏表示有两个模板参数T和I` 的情况。 +#define GET_MACRO(_1, _2, DESC, ...) DESC +//这个宏定义是一个辅助宏,用于根据参数的数量来选择不同的宏定义。在这里,它根据传入的参数数量选择 ADPT_DESC_TWO 或 ADPT_DESC_ONE。 +#define ADPT_DESC(...) GET_MACRO(__VA_ARGS__, ADPT_DESC_TWO, ADPT_DESC_ONE, ...)(__VA_ARGS__) +//这个宏定义是根据传入的参数数量选择调用 ADPT_DESC_TWO 或 ADPT_DESC_ONE 宏。它将传入的参数原样传递给 GET_MACRO 宏,然后根据参数的数量选择正确的宏。 +#define REG_ADPT_DESC(name, name_str, adpt_desc) \ + static struct RegAdptDesc##name { \ + public: \ + RegAdptDesc##name() { OpAdapterMap::get()[name_str] = adpt_desc; } \ + \ + private: \ + int ph_{0}; \// ph_{0} 是一个无用的成员,用于确保结构体有独一无二的实例化。 + } g_reg_adpt_desc_##name; + + //这个宏定义用于注册适配器描述。它在静态存储区定义了一个结构体 RegAdptDesc##name,其中 name 是传入的参数。 + //然后,在结构体的构造函数中,将适配器描述 adpt_desc 添加到 OpAdapterMap 的映射中,映射的键是 name_str。 + //这个宏允许在程序运行时自动注册适配器描述。 + +} // namespace mindspore::transform +#endif // MINDSPORE_CCSRC_TRANSFORM_GRAPH_IR_OP_DECLARE_MACRO_H_ diff --git a/mindspore/ccsrc/transform-update/pad_ops_declare.cc b/mindspore/ccsrc/transform-update/pad_ops_declare.cc new file mode 100644 index 00000000000..72846124e76 --- /dev/null +++ b/mindspore/ccsrc/transform-update/pad_ops_declare.cc @@ -0,0 +1,103 @@ +/** + * Copyright 2019-2021 Huawei Technologies Co., Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "transform/graph_ir/op_declare/pad_ops_declare.h" +#include + +namespace mindspore::transform { +// PadD +INPUT_MAP(PadD) = {{1, INPUT_DESC(x)}}; +// 输入映射,x索引为1 +ATTR_MAP(PadD) = {{"paddings", ATTR_DESC(paddings, AnyTraits>>())}}; +// 属性映射,"paddings"类型为float,"sqrt_mode"类型为std::vector> +OUTPUT_MAP(PadD) = {{0, OUTPUT_DESC(y)}}; +// 输出映射,y索引为0 +REG_ADPT_DESC(PadD, kNamePadD, ADPT_DESC(PadD)) +// 注册PadD操作的适配器描述kNamePadD + +// Pad +INPUT_MAP(Pad) = {{1, INPUT_DESC(x)}, {2, INPUT_DESC(paddings)}}; +// 输入映射,x索引为1,paddings索引为2 +ATTR_MAP(Pad) = EMPTY_ATTR_MAP; +// 属性映射,空 +OUTPUT_MAP(Pad) = {{0, OUTPUT_DESC(y)}}; +// 输出映射,y索引为0 +REG_ADPT_DESC(Pad, kNamePadV1, ADPT_DESC(Pad)) +// 注册Pad操作的适配器描述kNamePadV1 + +// BroadcastToD +INPUT_MAP(BroadcastToD) = {{1, INPUT_DESC(x)}}; +// 输入映射,x索引为1 +ATTR_MAP(BroadcastToD) = {{"shape", ATTR_DESC(shape, AnyTraits(), AnyTraits>())}}; +// 属性映射,"shape"类型为int64_t和std::vector> +OUTPUT_MAP(BroadcastToD) = {{0, OUTPUT_DESC(y)}}; +// 输出映射,y索引为0 +REG_ADPT_DESC(BroadcastToD, kNameBroadcastTo, ADPT_DESC(BroadcastToD)) +// 注册BroadcastToD操作的适配器描述kNameBroadcastTo + +// Diag +INPUT_MAP(Diag) = {{1, INPUT_DESC(x)}}; +// 输入映射,x索引为1 +ATTR_MAP(Diag) = EMPTY_ATTR_MAP; +// 属性映射,空 +OUTPUT_MAP(Diag) = {{0, OUTPUT_DESC(y)}}; +//输出映射,y索引为0 +REG_ADPT_DESC(Diag, kNameDiag, ADPT_DESC(Diag)) +// 注册Diag操作的适配器描述kNameDiag + +// FillD +INPUT_MAP(FillD) = {{1, INPUT_DESC(value)}}; +// 输入映射,value索引为1 +ATTR_MAP(FillD) = {{"dims", ATTR_DESC(dims, AnyTraits>())}}; +// 属性映射,属性"dims"的类型是std::vector +OUTPUT_MAP(FillD) = {{0, OUTPUT_DESC(y)}}; +// 输出映射,y索引为0 +REG_ADPT_DESC(FillD, kNameFillD, ADPT_DESC(FillD)) +// 注册FillD操作的适配器描述kNameFillD + +// Fill +INPUT_MAP(Fill) = {{1, INPUT_DESC(dims)}, {2, INPUT_DESC(value)}}; +// 输入映射,dims索引为1,value索引为2 +ATTR_MAP(Fill) = EMPTY_ATTR_MAP; +// 属性映射,空 +OUTPUT_MAP(Fill) = {{0, OUTPUT_DESC(y)}}; +// 输出映射,y索引为0 +REG_ADPT_DESC(Fill, kNameFillV1, ADPT_DESC(Fill)) +// 注册Fill操作的适配器描述kNameFillV1 + + +// PadV3 +INPUT_MAP(PadV3) = {{1, INPUT_DESC(x)}, {2, INPUT_DESC(paddings)}, {3, INPUT_DESC(constant_values)}}; +// 输入映射,x索引为1,paddings索引为2,constant_values索引为3 +ATTR_MAP(PadV3) = {{"mode", ATTR_DESC(mode, AnyTraits())}, + {"pad_contiguous", ATTR_DESC(paddings_contiguous, AnyTraits())}}; +// 属性映射,属性"dims""pad_contiguous"的类型分别是std::string和bool +OUTPUT_MAP(PadV3) = {{0, OUTPUT_DESC(y)}}; +// 输出映射,y索引为0 +REG_ADPT_DESC(PadV3, kNamePadV3, ADPT_DESC(PadV3)) +// 注册PadV3操作的适配器描述kNamePadV3 + + +// PadV2 +INPUT_MAP(PadV2) = {{1, INPUT_DESC(x)}, {2, INPUT_DESC(paddings)}, {3, INPUT_DESC(constant_values)}}; +// 输入映射,x索引为1,paddings索引为2,constant_values索引为3 +ATTR_MAP(PadV2) = EMPTY_ATTR_MAP; +// 属性映射,空 +OUTPUT_MAP(PadV2) = {{0, OUTPUT_DESC(y)}}; +// 输出映射,y索引为0 +REG_ADPT_DESC(PadV2, kNamePadV2, ADPT_DESC(PadV2)) +// 注册PadV2操作的适配器描述kNamePadV2 +} // namespace mindspore::transform diff --git a/mindspore/ccsrc/transform-update/preprocess_imagenet_validate_dataset.py b/mindspore/ccsrc/transform-update/preprocess_imagenet_validate_dataset.py new file mode 100644 index 00000000000..61308bdfa15 --- /dev/null +++ b/mindspore/ccsrc/transform-update/preprocess_imagenet_validate_dataset.py @@ -0,0 +1,74 @@ +# Copyright 2019 Huawei Technologies Co., Ltd +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ============================================================================== +"""Process imagenet validate dataset. +澶勭悊imagenet楠岃瘉鏁版嵁闆 +""" +import os +import stat +from mindspore import log as logger + + +def preprocess_imagenet_validation_dataset(train_dataset_path, validation_dataset_path, image_label_mapping_file): + """ + 鍦ㄨ鍙杋magenet楠岃瘉鏁版嵁闆嗕箣鍓嶈皟鐢ㄦ鍑芥暟锛岀敤浜庨澶勭悊鏁版嵁闆嗐 + + Args: + train_dataset_path (str): 璁粌鏁版嵁闆嗚矾寰 + validation_dataset_path (str): 楠岃瘉鏁版嵁闆嗚矾寰 + image_label_mapping_file (str): imagenet_validate_dataset_2012_image_dir_map.txt 鏂囦欢璺緞 + """ + # 鑾峰彇璁粌鏁版嵁闆嗙殑缁濆璺緞 + train_dataset_path = os.path.realpath(train_dataset_path) + + # 鑾峰彇璁粌鏁版嵁闆嗕腑鐨勫瓙鐩綍鍒楄〃 + sub_dir = [dir_.name for dir_ in os.scandir(train_dataset_path) if dir_.is_dir()] + + # 閬嶅巻瀛愮洰褰曞苟鍦ㄩ獙璇佹暟鎹泦璺緞涓嬪垱寤哄搴旂殑瀛愮洰褰 + for sub_dir_name in sub_dir: + validate_sub_dir = os.path.join(validation_dataset_path, sub_dir_name) + validate_sub_dir = os.path.realpath(validate_sub_dir) + + # 濡傛灉楠岃瘉鏁版嵁闆嗗瓙鐩綍涓嶅瓨鍦紝鍒欏垱寤轰箣 + if not os.path.exists(validate_sub_dir): + os.makedirs(validate_sub_dir, mode=stat.S_IRWXU) + + # 鑾峰彇鏄犲皠鏂囦欢鐨勭粷瀵硅矾寰 + real_file_path = os.path.realpath(image_label_mapping_file) + + # 璇诲彇鏄犲皠鏂囦欢涓殑鏄犲皠鍏崇郴 + mappings = [mapping.strip() for mapping in open(real_file_path).readlines()] + + # 閬嶅巻鏄犲皠鍏崇郴锛屽皢鍥惧儚浠庤缁冪洰褰曠Щ鍔ㄥ埌楠岃瘉鐩綍涓殑瀵瑰簲瀛愮洰褰 + for mapping in mappings: + image_dir = mapping.split(':') + old_image_path = os.path.join(validation_dataset_path, image_dir[0]) + old_image_path = os.path.realpath(old_image_path) + + # 濡傛灉鍘熷鍥惧儚璺緞涓嶅瓨鍦紝鍒欏彂鍑鸿鍛 + if not os.path.exists(old_image_path): + logger.warning('Image is not existed %s', old_image_path) + + new_image_sub_dir = os.path.join(validation_dataset_path, image_dir[1]) + new_image_sub_dir = os.path.realpath(new_image_sub_dir) + new_image_path = os.path.join(new_image_sub_dir, image_dir[0]) + new_image_path = os.path.realpath(new_image_path) + + # 濡傛灉鏂板浘鍍忕殑瀛愮洰褰曚笉瀛樺湪锛屽垯鍙戝嚭璀﹀憡 + if not os.path.exists(new_image_sub_dir): + logger.warning('Image sub dir is not existed %s', new_image_sub_dir) + + # 灏嗗浘鍍忎粠鏃ц矾寰勭Щ鍒版柊璺緞 + os.rename(old_image_path, new_image_path) + diff --git a/mindspore/ccsrc/transform-update/python_pass_register.py b/mindspore/ccsrc/transform-update/python_pass_register.py new file mode 100644 index 00000000000..9bc19273822 --- /dev/null +++ b/mindspore/ccsrc/transform-update/python_pass_register.py @@ -0,0 +1,265 @@ +# Copyright 2020 Huawei Technologies Co., Ltd +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ============================================================================ +"""Python pass register""" +from inspect import isfunction # 检查对象是否为函数 +from mindspore.graph_utils.graph_pattern import Pattern, NewParameter # 用于图模式匹配的类 +from mindspore._c_expression import PyPassManager_ # 用于管理优化传递的类 + +# __all__ 列表用于指定在使用 "from import *" 时导出的符号 +# 它包含本脚本中定义的函数和类的名称,以便从外部访问 + +__all__ = [ + "register_pass", # 注册新的优化传递到传递管理器中的函数 + "unregister_pass", # 从传递管理器中注销优化传递的函数 + "gen_new_parameter", # 生成用于图转换的新参数的函数 + "cancel_new_parameter", # 取消生成新参数的函数 + "set_renorm", # 设置图转换的重新归一化标志的函数 + "set_reopt" # 设置图转换的重新优化标志的函数 +] + +# PyPassManager类继承自PyPassManager_,用于注册和注销Python优化传递,以便在编译期间对图进行修改。 +class PyPassManager(PyPassManager_): + r""" + Used to register and unregister python passes which can be used to alter graphs. + + Args: + requires_grad(bool): Do automatic-differentiation after modified graph if true. Default: True + run_only_once (bool): Specify whether or not to run pass only once. Default: False. + + Raises: + TypeError: If argument has invalid type. + """ + #创建一个PyPassManager对象,并用指定的参数requires_grad和run_only_once对其进行初始化。 + #PyPassManager是用于管理优化传递的类,它允许注册和注销Python优化传递函数,以便在编译期间对计算图进行修改。 + #通过创建PyPassManager对象,可以将Python优化传递函数注册到特定的编译阶段,并指定是否进行自动微分和是否只运行一次。 + def __init__(self, requires_grad=True, run_only_once=False): + # 检查输入的参数run_only_once是否为bool类型,否则抛出TypeError异常 + if not isinstance(requires_grad, bool): + raise TypeError(f"Expect bool, got : ({type(requires_grad)}){requires_grad}") + # 检查输入的参数requires_grad是否为bool类型,否则抛出TypeError异常 + if not isinstance(run_only_once, bool): + raise TypeError(f"Expect bool, got : ({type(run_only_once)}){run_only_once}") + # 检查输入的参数run_only_once是否为bool类型,否则抛出TypeError异常 + self.requires_grad = requires_grad + self.run_only_once_ = run_only_once + # 将输入的参数requires_grad和run_only_once保存到实例变量中 + PyPassManager_.__init__(self) + # 调用父类PyPassManager_的构造函数初始化PyPassManager_ + + # register函数的作用是将Python优化传递函数注册到优化传递管理器中,以便在编译期间对计算图进行修改和优化。 + + # 创建一个PyPassManager对象后,可以使用该对象调用register函数,将编写的Python优化传递函数注册到传递管理器中。 + # 注册后,该优化传递函数会在编译期间的特定阶段被自动调用,对计算图进行修改。 + def register(self, py_pass): + if not isfunction(py_pass): + raise TypeError(f"Expect function pass, got : ({type(py_pass)}){py_pass}") + # 检查输入的py_pass是否为函数类型,如果不是,则抛出TypeError异常 + pattern, target = py_pass() + # 调用py_pass函数,获取其返回的图模式匹配的模式和目标 + pass_name = py_pass.__name__ + # 获取py_pass函数的名称作为优化传递的名称 + if not isinstance(pattern, Pattern): + raise TypeError(f"Expect pattern of Pattern type, got : ({type(pattern)}){pattern}") + # 检查pattern是否为Pattern类型,如果不是,则抛出TypeError异常 + if not isinstance(target, Pattern): + raise TypeError(f"Expect target of Pattern type, got : ({type(target)}){target}") + # 检查target是否为Pattern类型,如果不是,则抛出TypeError异常 + super().register(pass_name, pattern, target, self.requires_grad, self.run_only_once_) + # 调用父类PyPassManager_的register方法,将优化传递函数注册到传递管理器中 + # 参数包括优化传递名称、图模式匹配的模式、目标、是否进行自动微分和是否只运行一次的标志 + + #该unregister方法用于从传递管理器中注销已注册的优化传递函数。优化传递函数的注册是通过register方法完成的。 + def unregister(self, py_pass): + #如果输入的py_pass参数是字符串类型,说明要注销一个已注册的优化传递函数 + if isinstance(py_pass, str): + super().unregister(py_pass) + return + # 调用父类PyPassManager_的unregister方法,传递优化传递名称,从传递管理器中注销优化传递函数 + + # 如果输入的py_pass参数是函数类型,说明要注销一个已注册的优化传递函数 + if isfunction(py_pass): + super().unregister(py_pass.__name__) + return + # 调用父类PyPassManager_的unregister方法,传递优化传递函数的名称,从传递管理器中注销优化传递函数 + + raise TypeError(f"Expect py_pass to be string or function, got ({type(py_pass)}){py_pass}") + # 如果输入的py_pass参数既不是字符串也不是函数,则抛出TypeError异常 + + #__call__方法在Python类中被称为"调用"方法,它使得该类的实例可以像函数一样被调用。 + #在PyPassManager类中,__call__方法用于将输入的Python优化传递函数(py_pass)注册到传递管理器中,并在成功注册后返回该优化传递函数本身。 + def __call__(self, py_pass): + self.register(py_pass) + # 调用register方法,将输入的py_pass函数注册到传递管理器中 + return py_pass + # 返回输入的py_pass函数本身 + + + #这个方法用于生成用于图转换的新参数。 + #首先,它检查传入的 pattern 参数是否是 NewParameter 类型的实例,如果不是,就会抛出异常。 + #然后,它调用父类的 gen_new_parameter 方法,完成新参数的生成过程。 + def gen_new_parameter(self, pattern): + if not isinstance(pattern, NewParameter): + raise TypeError(f"Expect pattern to be a NewParameter Pattern, got {pattern}") + # 检查输入的pattern参数是否为NewParameter类型,如果不是,则抛出TypeError异常 + super().gen_new_parameter(pattern) + # 调用父类PyPassManager_的gen_new_parameter方法,生成用于图转换的新参数 + + #这个方法用于设置一个参数 should_renorm,用于指示是否进行重新规范化。 + #首先,它检查传入的 should_renorm 参数是否是布尔类型,如果不是,就会抛出异常。 + #然后,它调用父类的 set_renorm 方法,将 should_renorm 参数传递给它,完成重新规范化设置的操作。 + def set_renorm(self, should_renorm): + if not isinstance(should_renorm, bool): + raise TypeError(f"Expect should_renorm to be a bool, got {should_renorm}") + # 检查输入的should_renorm参数是否为bool类型,如果不是,则抛出TypeError异常 + super().set_renorm(should_renorm) + # 调用父类的set_renorm方法,并将should_renorm参数传递给它 + + #这个方法用于设置一个参数 do_reopt,用于指示是否进行重新优化。 + #首先,它检查传入的 do_reopt 参数是否是布尔类型,如果不是,就会抛出异常。 + #然后,它调用父类的 set_reopt 方法,将 do_reopt 参数传递给它,完成重新优化设置的操作。 + + def set_reopt(self, do_reopt): + if not isinstance(do_reopt, bool): + raise TypeError(f"Expect do_reopt to be a bool, got {do_reopt}") + # 检查输入的do_reopt参数是否为bool类型,如果不是,则抛出TypeError异常 + super().set_reopt(do_reopt) + # 调用父类的set_reopt方法,并将do_reopt参数传递给它 + +def register_pass(requires_grad=True, run_only_once=False): + """ + Register python pass to specified pipeline phase which would be used in compilation. + 将python传递注册到指定的管道阶段,该阶段将在编译中使用。 + + Args: 参数: + requires_grad(bool): Do automatic-differentiation after modified graph if true. Default: True. + 如果修改后的图为true,则执行自动微分。默认值:True + run_only_once(bool): Run this pass only once if set true. Otherwise run the pass until converge. Default: + False. + 如果设置为true,则只运行一次。否则,运行通道,直到收敛。默认值:False + + Returns: + This function should be used as a decorator, return the decoratorated pass function. + 这个函数应该被用作装饰器,返回经过装饰的传递函数。 + + Examples: + >>> from mindspore.graph_utils.graph_pattern import Call, Any + >>> from mindspore.ops import operations as P + >>> @register_pass() + >>> def toy_pass(): + >>> x = Any() + >>> pattern = Call(P.Softmax(), [x]) + >>> target = Call(P.ReLU(), [x]) + >>> return pattern, target + """ + return PyPassManager(requires_grad, run_only_once) + + +def unregister_pass(py_pass): + """ + Unregister python pass. + 注销python路径 + + Args: + py_pass(Union(str, function)): target python pass to unregister. + 注销指定的python路径 + """ + ppm = PyPassManager() + ppm.unregister(py_pass) + + +def gen_new_parameter(pattern): + """ + Generate specified parameter every time a network gets compiled. + 每次编译网络时生成指定的参数。 + + NOTE: + In this way, every pass uses this pattern would be using the same Parameter. If use NewParameter without + gen_new_parameter, every pass match would build a new Parameter. + This would register a pass to add new parameter in the compilation pipeline, so later compilation would + ALSO add this parameter unless the pass is unregistered. To unregister this pass, call + cancel_new_parameter(pattern) + 这样,每次使用此模式的pass都将使用相同的Parameter。如使用gen_new_parameter之外的NewParameter , + 每次通过匹配都会构建一个新的Parameter。 + 这将注册一个在编译管道中添加新参数的pass,因此以后的编译将:除非pass未注册,否则也添加此参数。 + 要注销此pass,请调用cancel_new_parameter(模式) + + Args: + pattern (NewParameter): NewParameter type, could be used to build nested patterns across multiple passes + after gen_new_parameter. + NewParameter类型,可用于在gen_new_parameter之后跨多个pass构建嵌套模式。 + + Raises: + TypeError: If argument has invalid type. + 参数的类型无效 + + Examples: + >>> from mindspore.graph_utils.graph_pattern import NewParameter + >>> abc = NewParameter("abc") + >>> gen_new_parameter(abc) + """ + ppm = PyPassManager() + ppm.gen_new_parameter(pattern) + + +def cancel_new_parameter(pattern): + """ + Use with gen_new_parameter to unregister gen_new_parameter pass. + + Args: + pattern (NewParameter): NewParameter type, cancel the pass which would add new parameter as this pattern + describes. + NewParameter类型,取消将添加新参数的传递,如该模式所描述的。 + Examples: + >>> from mindspore.graph_utils.graph_pattern import NewParameter + >>> abc = NewParameter("abc") + >>> gen_new_parameter(abs) + >>> # some compilations + >>> cancel_new_parameter(abc) + """ + if not isinstance(pattern, NewParameter): + raise TypeError(f"Expect pattern to be a NewParameter Pattern, got {pattern}") + ppm = PyPassManager() + ppm.unregister(pattern.para_name) + + +def set_renorm(should_renorm): + """ + Set whether or not to do renormalization after modified graph in python pass(es). + + Args: + should_renorm(bool): whether or not to do renormalization after modified graph in python pass(es). + + NOTE: + This interface is mainly intended for testing modifying graph without worrying about its validity. Turn off + renormalization may BREAK the network. + """ + ppm = PyPassManager() + ppm.set_renorm(should_renorm) + + +def set_reopt(do_reopt): + """ + Set whether or not to do optimization after modified graph in python pass(es). + + Args: + do_reopt(bool): whether or not to do optimization after modified graph in python pass(es). + 在python的pass中修改图形后是否进行重新规范化。 + NOTE: + This interface is mainly intended for testing modifying graph without worrying about its validity. Turn off + renormalization may BREAK the network. + 该接口主要用于测试修改图,而不用担心修改图的有效性。关闭重整化可能会破坏网络。 + """ + ppm = PyPassManager() + ppm.set_reopt(do_reopt) diff --git a/mindspore/ccsrc/transform-update/quantize_ops_declare.cc b/mindspore/ccsrc/transform-update/quantize_ops_declare.cc new file mode 100644 index 00000000000..8023ebde77d --- /dev/null +++ b/mindspore/ccsrc/transform-update/quantize_ops_declare.cc @@ -0,0 +1,44 @@ +/** + * Copyright 2019 Huawei Technologies Co., Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "transform/graph_ir/op_declare/quantize_ops_declare.h" + +namespace mindspore::transform { +// AscendQuant +INPUT_MAP(AscendQuant) = {{1, INPUT_DESC(x)}}; +//输入映射,x索引为1 +ATTR_MAP(AscendQuant) = {{"scale", ATTR_DESC(scale, AnyTraits())}, + {"offset", ATTR_DESC(offset, AnyTraits())}, + {"sqrt_mode", ATTR_DESC(sqrt_mode, AnyTraits())}, + {"round_mode", ATTR_DESC(round_mode, AnyTraits())}}; +// 属性映射,列出了四个属性,"scale""offset"类型为float,"sqrt_mode"类型为bool,"round_mode"类型为std::string +OUTPUT_MAP(AscendQuant) = {{0, OUTPUT_DESC(y)}}; +//输出映射,y索引为0 +REG_ADPT_DESC(AscendQuant, kNameAscendQuant, ADPT_DESC(AscendQuant)) +// 注册AscendQuant操作的适配器描述kNameAscendQuant + +// AscendDequant +INPUT_MAP(AscendDequant) = {{1, INPUT_DESC(x)}, {2, INPUT_DESC(deq_scale)}}; +// 输入映射,x索引为1,deq_scale索引为2 +ATTR_MAP(AscendDequant) = {{"sqrt_mode", ATTR_DESC(sqrt_mode, AnyTraits())}, + {"relu_flag", ATTR_DESC(relu_flag, AnyTraits())}, + {"dtype", ATTR_DESC(dtype, AnyTraits())}}; +// 属性映射,列出了四个属性,"sqrt_mode""relu_flag"类型为bool,"dtype"类型为GEType +OUTPUT_MAP(AscendDequant) = {{0, OUTPUT_DESC(y)}}; +//输出映射,y索引为0 +REG_ADPT_DESC(AscendDequant, kNameAscendDequant, ADPT_DESC(AscendDequant)) +//注册AscendDequant操作的适配器描述kNameAscendDequant +} // namespace mindspore::transform diff --git a/mindspore/ccsrc/transform-update/random_ops_declare.cc b/mindspore/ccsrc/transform-update/random_ops_declare.cc new file mode 100644 index 00000000000..959841812d1 --- /dev/null +++ b/mindspore/ccsrc/transform-update/random_ops_declare.cc @@ -0,0 +1,63 @@ +/** + * Copyright 2019-2021 Huawei Technologies Co., Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "transform/graph_ir/op_declare/random_ops_declare.h" + +namespace mindspore::transform { +// DropOutGenMask +INPUT_MAP(DropOutGenMask) = {{1, INPUT_DESC(shape)}, {2, INPUT_DESC(prob)}}; +// 输入映射,shape索引为1,prob索引为2 +ATTR_MAP(DropOutGenMask) = {{"Seed0", ATTR_DESC(seed, AnyTraits())}, + {"Seed1", ATTR_DESC(seed2, AnyTraits())}}; +//属性映射,列出了两个属性,"Seed0""Seed1"类型为int64_t +OUTPUT_MAP(DropOutGenMask) = {{0, OUTPUT_DESC(y)}}; +//输出映射,y索引为0 +REG_ADPT_DESC(DropOutGenMask, prim::kPrimDropoutGenMask->name(), ADPT_DESC(DropOutGenMask)) +// 注册DropOutGenMask操作的适配器描述prim::kPrimDropoutGenMask->name() +// +// LinSpace +INPUT_MAP(LinSpace) = {{1, INPUT_DESC(start)}, {2, INPUT_DESC(stop)}, {3, INPUT_DESC(num)}}; +// 输入映射,start索引为1,stop索引为2,num索引为3 +ATTR_MAP(LinSpace) = EMPTY_ATTR_MAP; +//属性映射,空 +OUTPUT_MAP(LinSpace) = {{0, OUTPUT_DESC(output)}}; +//输出映射,output索引为空 +REG_ADPT_DESC(LinSpace, kNameLinSpace, ADPT_DESC(LinSpace)) +// 注册LinSpace操作的适配器描述kNameLinSpace + +// RandomChoiceWithMask +INPUT_MAP(RandomChoiceWithMask) = {{1, INPUT_DESC(x)}}; +//输入映射,x索引为1 +ATTR_MAP(RandomChoiceWithMask) = {{"count", ATTR_DESC(count, AnyTraits())}, + {"seed", ATTR_DESC(seed, AnyTraits())}, + {"seed2", ATTR_DESC(seed2, AnyTraits())}}; +// 属性映射,列出了三个属性,"count""seed""seed2"类型为int64_t +OUTPUT_MAP(RandomChoiceWithMask) = {{0, OUTPUT_DESC(y)}, {1, OUTPUT_DESC(mask)}}; +// 输出映射,y索引为0,mask索引为1 +REG_ADPT_DESC(RandomChoiceWithMask, kNameRandomChoiceWithMask, ADPT_DESC(RandomChoiceWithMask)) +// 注册RandomChoiceWithMask操作的适配器描述kNameRandomChoiceWithMask + +// TruncatedNormal +INPUT_MAP(TruncatedNormal) = {{1, INPUT_DESC(shape)}}; +// 输入映射,shape索引为1 +ATTR_MAP(TruncatedNormal) = {{"seed", ATTR_DESC(seed, AnyTraits())}, + {"seed2", ATTR_DESC(seed2, AnyTraits())}}; +// 属性映射,列出了两个属性,"seed""seed2"类型为int64_t +OUTPUT_MAP(TruncatedNormal) = {{0, OUTPUT_DESC(y)}}; +// 输出映射,y索引为0 +REG_ADPT_DESC(TruncatedNormal, kNameTruncatedNormal, ADPT_DESC(TruncatedNormal)) +// 注册TruncatedNormal操作的适配器描述kNameTruncatedNormal +} // namespace mindspore::transform diff --git a/mindspore/ccsrc/transform-update/reduce_ops_declare.cc b/mindspore/ccsrc/transform-update/reduce_ops_declare.cc new file mode 100644 index 00000000000..a54607887bc --- /dev/null +++ b/mindspore/ccsrc/transform-update/reduce_ops_declare.cc @@ -0,0 +1,161 @@ +/** + * Copyright 2019-2021 Huawei Technologies Co., Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "transform/graph_ir/op_declare/reduce_ops_declare.h" +#include + +namespace mindspore::transform { +// BNTrainingReduce +INPUT_MAP(BNTrainingReduce) = {{1, INPUT_DESC(x)}}; +// 输入映射,x索引为1 +ATTR_MAP(BNTrainingReduce) = EMPTY_ATTR_MAP; +// 属性映射,空 +OUTPUT_MAP(BNTrainingReduce) = {{0, OUTPUT_DESC(sum)}, {1, OUTPUT_DESC(square_sum)}}; +// 输出映射,sum索引为0,square_sum索引为1 +REG_ADPT_DESC(BNTrainingReduce, kNameBNTrainingReduce, ADPT_DESC(BNTrainingReduce)) +//注册BNTrainingReduce操作的适配器描述kNameBNTrainingReduce + +// BNTrainingReduceGrad +INPUT_MAP(BNTrainingReduceGrad) = {{1, INPUT_DESC(grads)}, {2, INPUT_DESC(x)}, {3, INPUT_DESC(diff_scale)}, + {4, INPUT_DESC(diff_offset)}, {5, INPUT_DESC(scale)}, {6, INPUT_DESC(batch_mean)}, + {7, INPUT_DESC(batch_variance)}}; +//输入映射,共七个,grad索引为1,x索引为2,diff_scale索引为3,diff_offset索引为4,scale索引为5,batch_mean索引为6,batch_variance索引为7 +ATTR_MAP(BNTrainingReduceGrad) = {{"epsilon", ATTR_DESC(epsilon, AnyTraits())}}; +// 属性映射,列出了"epsilon"类型为float +OUTPUT_MAP(BNTrainingReduceGrad) = {{0, OUTPUT_DESC(y)}}; +//输出映射,y索引为0 +REG_ADPT_DESC(BNTrainingReduceGrad, kNameBNTrainingReduceGrad, ADPT_DESC(BNTrainingReduceGrad)) +// 注册BNTrainingReduceGrad操作的适配器描述kNameBNTrainingReduceGrad + +// BNTrainingUpdate +INPUT_MAP(BNTrainingUpdate) = {{1, INPUT_DESC(x)}, {2, INPUT_DESC(sum)}, {3, INPUT_DESC(square_sum)}, + {4, INPUT_DESC(scale)}, {5, INPUT_DESC(offset)}, {6, INPUT_DESC(mean)}, + {7, INPUT_DESC(variance)}}; +// 输入映射,共七个,grad索引为1,x索引为2,diff_scale索引为3,diff_offset索引为4,scale索引为5,batch_mean索引为6,batch_variance索引为7 +ATTR_MAP(BNTrainingUpdate) = {{"factor", ATTR_DESC(factor, AnyTraits())}, + {"epsilon", ATTR_DESC(epsilon, AnyTraits())}}; +// 属性映射,列出了"factor""epsilon"类型为float +OUTPUT_MAP(BNTrainingUpdate) = {{0, OUTPUT_DESC(y)}, + {1, OUTPUT_DESC(mean)}, + {2, OUTPUT_DESC(variance)}, + {3, OUTPUT_DESC(batch_mean)}, + {4, OUTPUT_DESC(batch_variance)}}; +// 输出映射,共五个,y索引为0,mean索引为1,variance索引为2,batch_mean索引为3,batch_variance索引为4 +REG_ADPT_DESC(BNTrainingUpdate, kNameBNTrainingUpdate, ADPT_DESC(BNTrainingUpdate)) +// 注册BNTrainingUpdate操作的适配器描述kNameBNTrainingUpdate + +// BNTrainingUpdateGrad +INPUT_MAP(BNTrainingUpdateGrad) = { + {1, INPUT_DESC(grads)}, {2, INPUT_DESC(x)}, {3, INPUT_DESC(batch_mean)}, {4, INPUT_DESC(batch_variance)}}; +// 输入映射,共五个,grads索引为1,x索引为2,batch_mean索引为3,batch_variance索引为4 +ATTR_MAP(BNTrainingUpdateGrad) = {{"epsilon", ATTR_DESC(epsilon, AnyTraits())}}; +// 属性映射,"epsilon"类型为float +OUTPUT_MAP(BNTrainingUpdateGrad) = {{0, OUTPUT_DESC(diff_scale)}, {1, OUTPUT_DESC(diff_offset)}}; +// 输出映射,共两个,diff_scale索引为0,diff_offset索引为1 +REG_ADPT_DESC(BNTrainingUpdateGrad, kNameBNTrainingUpdateGrad, ADPT_DESC(BNTrainingUpdateGrad)) +// 注册BNTrainingUpdateGrad操作的适配器描述kNameBNTrainingUpdateGrad + +// ReduceAnyD +INPUT_MAP(ReduceAnyD) = {{1, INPUT_DESC(x)}}; +// 输入映射,x索引为1 +INPUT_ATTR_MAP(ReduceAnyD) = { + {2, ATTR_DESC(axes, AnyTraits>(), AnyTraits>())}}; +// 输入属性映射,axes索引为2,类型为int64_t +ATTR_MAP(ReduceAnyD) = {{"keep_dims", ATTR_DESC(keep_dims, AnyTraits())}}; +// 属性映射,"keep_dims"类型为bool +OUTPUT_MAP(ReduceAnyD) = {{0, OUTPUT_DESC(y)}}; +// 输出映射,y索引为0 +REG_ADPT_DESC(ReduceAnyD, kNameReduceAnyD, ADPT_DESC(ReduceAnyD)) +// 注册ReduceAnyD操作的适配器描述 kNameReduceAnyD + +// ReduceSumD +INPUT_MAP(ReduceSumD) = {{1, INPUT_DESC(x)}}; +//输入映射,x索引为1 +INPUT_ATTR_MAP(ReduceSumD) = { + {2, ATTR_DESC(axes, AnyTraits>(), AnyTraits>())}}; +// 输入属性映射,axes索引为2,类型为int64_t +ATTR_MAP(ReduceSumD) = {{"keep_dims", ATTR_DESC(keep_dims, AnyTraits())}}; +// 属性映射,"keep_dims"类型为bool +OUTPUT_MAP(ReduceSumD) = {{0, OUTPUT_DESC(y)}}; +// 输出映射,y索引为0 +REG_ADPT_DESC(ReduceSumD, prim::kPrimReduceSum->name(), ADPT_DESC(ReduceSumD)) +// 注册ReduceSumD操作的适配器描述 prim::kPrimReduceSum->name() + +// ReduceProdD +INPUT_MAP(ReduceProdD) = {{1, INPUT_DESC(x)}}; +// 输入映射,x索引为1 +INPUT_ATTR_MAP(ReduceProdD) = { + {2, ATTR_DESC(axes, AnyTraits>(), AnyTraits>())}}; +// 输入属性映射,axes索引为2,类型为int64_t +ATTR_MAP(ReduceProdD) = {{"keep_dims", ATTR_DESC(keep_dims, AnyTraits())}}; +// 属性映射,"keep_dims"类型为bool +OUTPUT_MAP(ReduceProdD) = {{0, OUTPUT_DESC(y)}}; +// 输出映射,y索引为0 +REG_ADPT_DESC(ReduceProdD, kNameReduceProd, ADPT_DESC(ReduceProdD)) +// 注册ReduceProdD操作的适配器描述kNameReduceProd + + +// ReduceAllD +INPUT_MAP(ReduceAllD) = {{1, INPUT_DESC(x)}}; +// 输入映射,x索引为1 +INPUT_ATTR_MAP(ReduceAllD) = { + {2, ATTR_DESC(axes, AnyTraits>(), AnyTraits>())}}; +// 属性映射,axes索引为2,类型为int64_t +ATTR_MAP(ReduceAllD) = {{"keep_dims", ATTR_DESC(keep_dims, AnyTraits())}}; +// 属性映射,"keep_dims"类型为bool +OUTPUT_MAP(ReduceAllD) = {{0, OUTPUT_DESC(y)}}; +// 输出映射,y索引为0 +REG_ADPT_DESC(ReduceAllD, prim::kPrimReduceAll->name(), ADPT_DESC(ReduceAllD)) +// 注册ReduceAllD操作的适配器描述prim::kPrimReduceAll->name() + +// ReduceMeanD +INPUT_MAP(ReduceMeanD) = {{1, INPUT_DESC(x)}}; +// 输入映射,x索引为1 +INPUT_ATTR_MAP(ReduceMeanD) = { + {2, ATTR_DESC(axes, AnyTraits>(), AnyTraits>())}}; +// 输入属性映射,axes索引为2,类型为int64_t +ATTR_MAP(ReduceMeanD) = {{"keep_dims", ATTR_DESC(keep_dims, AnyTraits())}}; +// 属性映射,"keep_dims"类型为bool +OUTPUT_MAP(ReduceMeanD) = {{0, OUTPUT_DESC(y)}}; +REG_ADPT_DESC(ReduceMeanD, prim::kPrimReduceMean->name(), ADPT_DESC(ReduceMeanD)) +// 注册ReduceMeanD操作的适配器描述prim::kPrimReduceAll->name() +// +// ReduceMinD +INPUT_MAP(ReduceMinD) = {{1, INPUT_DESC(x)}}; +// 输入映射,x索引为1 +INPUT_ATTR_MAP(ReduceMinD) = { + {2, ATTR_DESC(axes, AnyTraits>(), AnyTraits>())}}; +// 输入属性映射,axes索引为2,类型为int64_t +ATTR_MAP(ReduceMinD) = {{"keep_dims", ATTR_DESC(keep_dims, AnyTraits())}}; +// 属性映射,"keep_dims"类型为bool +OUTPUT_MAP(ReduceMinD) = {{0, OUTPUT_DESC(y)}}; +// 输出映射,y索引为0 +REG_ADPT_DESC(ReduceMinD, prim::kPrimReduceMin->name(), ADPT_DESC(ReduceMinD)) +// 注册ReduceMinD操作的适配器描述prim::kPrimReduceAll->name() + +// ReduceMaxD +INPUT_MAP(ReduceMaxD) = {{1, INPUT_DESC(x)}}; +// 输入映射,x索引为1 +INPUT_ATTR_MAP(ReduceMaxD) = { + {2, ATTR_DESC(axes, AnyTraits>(), AnyTraits>())}}; +// 输入属性映射,axes索引为2,类型为int64_t +ATTR_MAP(ReduceMaxD) = {{"keep_dims", ATTR_DESC(keep_dims, AnyTraits())}}; +// 属性映射,"keep_dims"类型为bool +OUTPUT_MAP(ReduceMaxD) = {{0, OUTPUT_DESC(y)}}; +// 输出映射,y索引为0 +REG_ADPT_DESC(ReduceMaxD, prim::kPrimReduceMax->name(), ADPT_DESC(ReduceMaxD)) +// 注册ReduceMaxD操作的适配器描述prim::kPrimReduceAll->name() +} // namespace mindspore::transform diff --git a/mindspore/ccsrc/transform-update/rnn_declare.cc b/mindspore/ccsrc/transform-update/rnn_declare.cc new file mode 100644 index 00000000000..fcb4e37e45d --- /dev/null +++ b/mindspore/ccsrc/transform-update/rnn_declare.cc @@ -0,0 +1,192 @@ +/** + * Copyright 2019-2021 Huawei Technologies Co., Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "transform/graph_ir/op_declare/rnn_declare.h" + +namespace mindspore::transform { +// BasicLSTMCell +INPUT_MAP(BasicLSTMCell) = { + {1, INPUT_DESC(x)}, {2, INPUT_DESC(h)}, {3, INPUT_DESC(c)}, {4, INPUT_DESC(w)}, {5, INPUT_DESC(b)}}; +// 输入映射,共五个,x索引为1,h索引为2,c索引为3,w索引为4,b索引为5 +ATTR_MAP(BasicLSTMCell) = {{"keep_prob", ATTR_DESC(keep_prob, AnyTraits())}, + {"forget_bias", ATTR_DESC(forget_bias, AnyTraits())}, + {"state_is_tuple", ATTR_DESC(state_is_tuple, AnyTraits())}, + {"activation", ATTR_DESC(activation, AnyTraits())}}; +// 属性映射,列出了"keep_prob"、"forget_bias"、"state_is_tuple"、"activation"四个属性,类型分别为bool和std::string +OUTPUT_MAP(BasicLSTMCell) = {{0, OUTPUT_DESC(ct)}, {1, OUTPUT_DESC(ht)}, {2, OUTPUT_DESC(it)}, {3, OUTPUT_DESC(jt)}, + {4, OUTPUT_DESC(ft)}, {5, OUTPUT_DESC(ot)}, {6, OUTPUT_DESC(tanhct)}} +// 输出映射,共七个,cty索引为0,ht索引为1,it索引为2,jt索引为3,ft索引为4,ot索引为5,tanhct索引为6 +REG_ADPT_DESC(BasicLSTMCell, kNameBasicLSTMCell, ADPT_DESC(BasicLSTMCell)) +// 注册BasicLSTMCell操作的适配器描述kNameBasicLSTMCell + +// BasicLSTMCellInputGrad +INPUT_MAP(BasicLSTMCellInputGrad) = {{1, INPUT_DESC(dgate)}, {2, INPUT_DESC(w)}}; +// 输入映射,共两个,dgate索引为1,w索引为2 +ATTR_MAP(BasicLSTMCellInputGrad) = {{"keep_prob", ATTR_DESC(keep_prob, AnyTraits())}}; +// 属性映射,列出了"keep_prob"属性,类型为float +OUTPUT_MAP(BasicLSTMCellInputGrad) = {{0, OUTPUT_DESC(dxt)}, {1, OUTPUT_DESC(dht)}}; +// 输出映射,共两个,dxt索引为0,dht索引为1 +REG_ADPT_DESC(BasicLSTMCellInputGrad, kNameBasicLSTMCellInputGrad, ADPT_DESC(BasicLSTMCellInputGrad)) +// 注册BasicLSTMCellInputGrad操作的适配器描述kNameBasicLSTMCellInputGrad + + +// BasicLSTMCellWeightGrad +INPUT_MAP(BasicLSTMCellWeightGrad) = {{1, INPUT_DESC(x)}, {2, INPUT_DESC(h)}, {3, INPUT_DESC(dgate)}}; +// 输入映射,共三个,dgate索引为1,w索引为2,dgate索引为3 +ATTR_MAP(BasicLSTMCellWeightGrad) = EMPTY_ATTR_MAP; +//属性映射,空 +OUTPUT_MAP(BasicLSTMCellWeightGrad) = {{0, OUTPUT_DESC(dw)}, {1, OUTPUT_DESC(db)}}; +// 输出映射,共两个,dw索引为0,db索引为1 +REG_ADPT_DESC(BasicLSTMCellWeightGrad, kNameBasicLSTMCellWeightGrad, ADPT_DESC(BasicLSTMCellWeightGrad)) +// 注册BasicLSTMCellWeightGrad操作的适配器描述kNameBasicLSTMCellWeightGrad + +// BasicLSTMCellCStateGrad +INPUT_MAP(BasicLSTMCellCStateGrad) = {{1, INPUT_DESC(c)}, {2, INPUT_DESC(dht)}, {3, INPUT_DESC(dct)}, + {4, INPUT_DESC(it)}, {5, INPUT_DESC(jt)}, {6, INPUT_DESC(ft)}, + {7, INPUT_DESC(ot)}, {8, INPUT_DESC(tanhct)}}; +// 输入映射,共八个,c索引为1,dht索引为2,dct索引为3,it索引为4,jt索引为5,ft索引为6,ot索引为7,tanhct索引为8 +ATTR_MAP(BasicLSTMCellCStateGrad) = {{"forget_bias", ATTR_DESC(forget_bias, AnyTraits())}, + {"activation", ATTR_DESC(activation, AnyTraits())}}; +// 属性映射,列出了"forget_bias"和"activation"属性,类型分别为float和std::string +OUTPUT_MAP(BasicLSTMCellCStateGrad) = {{0, OUTPUT_DESC(dgate)}, {1, OUTPUT_DESC(dct_1)}}; +// 输出映射,共两个,dgate索引为0,dct_1索引为1 +REG_ADPT_DESC(BasicLSTMCellCStateGrad, kNameBasicLSTMCellCStateGrad, ADPT_DESC(BasicLSTMCellCStateGrad)) +// 注册BasicLSTMCellCStateGrad操作的适配器描述kNameBasicLSTMCellCStateGrad + + +// LSTMInputGrad +INPUT_MAP(LSTMInputGrad) = {{1, INPUT_DESC(w)}, {2, INPUT_DESC(init_c)}, {3, INPUT_DESC(c)}, {4, INPUT_DESC(dy)}, + {5, INPUT_DESC(dh)}, {6, INPUT_DESC(dc)}, {7, INPUT_DESC(i)}, {8, INPUT_DESC(j)}, + {9, INPUT_DESC(f)}, {10, INPUT_DESC(o)}, {11, INPUT_DESC(tanhct)}}; +//输入映射,共十一个,w索引为1,init_c索引为2,di索引为3,dy索引为4,dh索引为5,dc索引为6,i索引为7,j索引为8,f索引为9,o索引为10,tanhct索引为11 +ATTR_MAP(LSTMInputGrad) = EMPTY_ATTR_MAP; +// 属性映射,空 +OUTPUT_MAP(LSTMInputGrad) = { + {0, OUTPUT_DESC(dx)}, {1, OUTPUT_DESC(dh_prev)}, {2, OUTPUT_DESC(dc_prev)}, {4, OUTPUT_DESC(dgate)}}; +// 输出映射,共四个,dh_prev索引为1,dc_prev索引为2,dgate索引为4 +REG_ADPT_DESC(LSTMInputGrad, kNameLSTMInputGrad, ADPT_DESC(LSTMInputGrad)) +// 注册LSTMInputGrad操作的适配器描述kNameLSTMInputGrad + +// DynamicRNN +INPUT_MAP(DynamicRNN) = {{1, INPUT_DESC(x)}, {2, INPUT_DESC(w)}, {3, INPUT_DESC(b)}, + {4, INPUT_DESC(seq_length)}, {5, INPUT_DESC(init_h)}, {6, INPUT_DESC(init_c)}, + {7, INPUT_DESC(wci)}, {8, INPUT_DESC(wcf)}, {9, INPUT_DESC(wco)}, + {10, INPUT_DESC(mask)}}; +// 输入映射,共十个,x索引为1,w索引为2,b索引为3,seq_length索引为4,init_h索引为5,init_c索引为6,wci索引为7,wcf索引为8,wco索引为9,mask索引为10 +ATTR_MAP(DynamicRNN) = {{"cell_type", ATTR_DESC(cell_type, AnyTraits())}, + {"direction", ATTR_DESC(direction, AnyTraits())}, + {"cell_depth", ATTR_DESC(cell_depth, AnyTraits())}, + {"use_peephole", ATTR_DESC(use_peephole, AnyTraits())}, + {"keep_prob", ATTR_DESC(keep_prob, AnyTraits())}, + {"cell_clip", ATTR_DESC(cell_clip, AnyTraits())}, + {"num_proj", ATTR_DESC(num_proj, AnyTraits())}, + {"time_major", ATTR_DESC(time_major, AnyTraits())}, + {"ivation", ATTR_DESC(activation, AnyTraits())}, + {"forget_bias", ATTR_DESC(forget_bias, AnyTraits())}, + {"is_training", ATTR_DESC(is_training, AnyTraits())}}; +// 属性映射,列出了"cell_type""ivation"和"direction"属性,类型为std::string +//"cell_depth"和"num_proj",类型为int64_t +//"use_peephole""is_training"和"time_major",类型为bool +//"keep_prob""cell_clip""forget_bias",类型为float +OUTPUT_MAP(DynamicRNN) = {{0, OUTPUT_DESC(y)}, {1, OUTPUT_DESC(output_h)}, {2, OUTPUT_DESC(output_c)}, + {3, OUTPUT_DESC(i)}, {4, OUTPUT_DESC(j)}, {5, OUTPUT_DESC(f)}, + {6, OUTPUT_DESC(o)}, {7, OUTPUT_DESC(tanhc)}}; +// 输出映射,共十个,x索引为1,w索引为2,b索引为3,seq_length索引为4,init_h索引为5,init_c索引为6,wci索引为7,wcf索引为8,wco索引为9,mask索引为10 +REG_ADPT_DESC(DynamicRNN, kNameDynamicRNN, ADPT_DESC(DynamicRNN)) +// 注册DynamicRNN操作的适配器描述kNameDynamicRNN + +// DynamicRNNGrad +INPUT_MAP(DynamicRNNGrad) = { + {1, INPUT_DESC(x)}, {2, INPUT_DESC(w)}, {3, INPUT_DESC(b)}, {4, INPUT_DESC(y)}, + {5, INPUT_DESC(init_h)}, {6, INPUT_DESC(init_c)}, {7, INPUT_DESC(h)}, {8, INPUT_DESC(c)}, + {9, INPUT_DESC(dy)}, {10, INPUT_DESC(dh)}, {11, INPUT_DESC(dc)}, {12, INPUT_DESC(i)}, + {13, INPUT_DESC(j)}, {14, INPUT_DESC(f)}, {15, INPUT_DESC(o)}, {16, INPUT_DESC(tanhct)}}; +// 输入映射,共十个,x索引为1,w索引为2,b索引为3,y索引为4,init_h索引为5,init_c索引为6,h索引为7,c索引为8,dy索引为9,dh索引为10,dc索引为11,i索引为12 +//j索引为13,f索引为14,o索引为15,tanhct索引为16 +ATTR_MAP(DynamicRNNGrad) = {{"cell_type", ATTR_DESC(cell_type, AnyTraits())}, + {"direction", ATTR_DESC(direction, AnyTraits())}, + {"cell_depth", ATTR_DESC(cell_depth, AnyTraits())}, + {"use_peephole", ATTR_DESC(use_peephole, AnyTraits())}, + {"keep_prob", ATTR_DESC(keep_prob, AnyTraits())}, + {"cell_clip", ATTR_DESC(cell_clip, AnyTraits())}, + {"num_proj", ATTR_DESC(num_proj, AnyTraits())}, + {"time_major", ATTR_DESC(time_major, AnyTraits())}, + {"forget_bias", ATTR_DESC(forget_bias, AnyTraits())}}; +// 属性映射,列出了"cell_type"和"direction"属性,类型为std::string +//"cell_depth"和"num_proj",类型为int64_t +//"use_peephole""is_training"和"time_major",类型为bool +//"keep_prob""cell_clip""forget_bias",类型为float +OUTPUT_MAP(DynamicRNNGrad) = {{0, OUTPUT_DESC(dw)}, + {1, OUTPUT_DESC(db)}, + {2, OUTPUT_DESC(dx)}, + {3, OUTPUT_DESC(dh_prev)}, + {4, OUTPUT_DESC(dc_prev)}}; +// 输出映射,共五个,dw索引为0,db索引为1,dh_prev索引为2,dh_prev索引为3,dc_prev索引为4 +REG_ADPT_DESC(DynamicRNNGrad, kNameDynamicRNNGrad, ADPT_DESC(DynamicRNNGrad)) +// 注册DynamicRNNGrad操作的适配器描述kNameDynamicRNNGrad + +// DynamicGRUV2 +INPUT_MAP(DynamicGRUV2) = {{1, INPUT_DESC(x)}, {2, INPUT_DESC(weight_input)}, {3, INPUT_DESC(weight_hidden)}, + {4, INPUT_DESC(bias_input)}, {5, INPUT_DESC(bias_hidden)}, {6, INPUT_DESC(seq_length)}, + {7, INPUT_DESC(init_h)}}; +// 输入映射,共七个,x索引为1,weight_input索引为2,weight_hidden索引为3,bias_input索引为4,bias_hidden索引为5,seq_length索引为6,init_h索引为7 +ATTR_MAP(DynamicGRUV2) = {{"direction", ATTR_DESC(direction, AnyTraits())}, + {"cell_depth", ATTR_DESC(cell_depth, AnyTraits())}, + {"keep_prob", ATTR_DESC(keep_prob, AnyTraits())}, + {"cell_clip", ATTR_DESC(cell_clip, AnyTraits())}, + {"num_proj", ATTR_DESC(num_proj, AnyTraits())}, + {"time_major", ATTR_DESC(time_major, AnyTraits())}, + {"activation", ATTR_DESC(activation, AnyTraits())}, + {"gate_order", ATTR_DESC(gate_order, AnyTraits())}, + {"reset_after", ATTR_DESC(reset_after, AnyTraits())}, + {"is_training", ATTR_DESC(is_training, AnyTraits())}}; +// 属性映射,列出了"direction"和"activation""gate_order"属性,类型为std::string +//"cell_depth"和"num_proj",类型为int64_t +//"reset_after""is_training"和"time_major",类型为bool +//"keep_prob""cell_clip""forget_bias",类型为float +OUTPUT_MAP(DynamicGRUV2) = {{0, OUTPUT_DESC(y)}, {1, OUTPUT_DESC(output_h)}, {2, OUTPUT_DESC(update)}, + {3, OUTPUT_DESC(reset)}, {4, OUTPUT_DESC(new)}, {5, OUTPUT_DESC(hidden_new)}}; +// 输入映射,共六个,y索引为0,output_h索引为1,update索引为2,reset索引为3,new索引为4,hidden_new索引为5 +REG_ADPT_DESC(DynamicGRUV2, kNameDynamicGRUV2, ADPT_DESC(DynamicGRUV2)) +// 注册DynamicGRUV2操作的适配器描述kNameDynamicGRUV2 +// +// DynamicGRUV2Grad +INPUT_MAP(DynamicGRUV2Grad) = { + {1, INPUT_DESC(x)}, {2, INPUT_DESC(weight_input)}, {3, INPUT_DESC(weight_hidden)}, + {4, INPUT_DESC(y)}, {5, INPUT_DESC(init_h)}, {6, INPUT_DESC(h)}, + {7, INPUT_DESC(dy)}, {8, INPUT_DESC(dh)}, {9, INPUT_DESC(update)}, + {10, INPUT_DESC(reset)}, {11, INPUT_DESC(new)}, {12, INPUT_DESC(hidden_new)}, + {13, INPUT_DESC(seq_length)}, {14, INPUT_DESC(mask)}}; +// 输入映射,共十四个,x索引为1,weight_input索引为2,weight_hidden索引为3,y索引为4,init_h索引为5,h索引为6,dy索引为7,dh索引为8, +//update索引为9,reset索引为10,new索引为11,hidden_new索引为12,seq_length索引为13,mask索引为14 +ATTR_MAP(DynamicGRUV2Grad) = {{"direction", ATTR_DESC(direction, AnyTraits())}, + {"cell_depth", ATTR_DESC(cell_depth, AnyTraits())}, + {"keep_prob", ATTR_DESC(keep_prob, AnyTraits())}, + {"cell_clip", ATTR_DESC(cell_clip, AnyTraits())}, + {"num_proj", ATTR_DESC(num_proj, AnyTraits())}, + {"time_major", ATTR_DESC(time_major, AnyTraits())}, + {"gate_order", ATTR_DESC(gate_order, AnyTraits())}, + {"reset_after", ATTR_DESC(reset_after, AnyTraits())}}; +// 属性映射,列出了"direction"和"activation""gate_order"属性,类型为std::string +//"cell_depth"和"num_proj",类型为int64_t +//"reset_after""is_training"和"time_major",类型为bool +//"keep_prob""cell_clip",类型为float +OUTPUT_MAP(DynamicGRUV2Grad) = {{0, OUTPUT_DESC(dw_input)}, {1, OUTPUT_DESC(dw_hidden)}, {2, OUTPUT_DESC(db_input)}, + {3, OUTPUT_DESC(db_hidden)}, {4, OUTPUT_DESC(dx)}, {5, OUTPUT_DESC(dh_prev)}}; +// 输入映射,共六个,dw_input索引为0,dw_hidden索引为1,db_input索引为2,db_hidden索引为3,dx索引为4,dh_prev索引为5 +REG_ADPT_DESC(DynamicGRUV2Grad, kNameDynamicGRUV2Grad, ADPT_DESC(DynamicGRUV2Grad)) +// 注册DynamicGRUV2Grad操作的适配器描述kNameDynamicGRUV2Grad +} // namespace mindspore::transform diff --git a/mindspore/ccsrc/transform-update/rpn_ops_declare.cc b/mindspore/ccsrc/transform-update/rpn_ops_declare.cc new file mode 100644 index 00000000000..3cf19fc7276 --- /dev/null +++ b/mindspore/ccsrc/transform-update/rpn_ops_declare.cc @@ -0,0 +1,35 @@ +/** + * Copyright 2019 Huawei Technologies Co., Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "transform/graph_ir/op_declare/rpn_ops_declare.h" + +namespace mindspore::transform { +// NMSWithMask +INPUT_MAP(NMSWithMask) = {{1, INPUT_DESC(box_scores)}}; +//输入映射,输入的形参是box_scores +ATTR_MAP(NMSWithMask) = {{"iou_threshold", ATTR_DESC(iou_threshold, AnyTraits())}}; +//属性映射,其中包含一个属性 "iou_threshold"。该属性使用名为 "iou_threshold" 的属性描述(ATTR_DESC), +//并指定其值的类型为 float。"iou_threshold" 属性可能用于指定非极大值抑制 (NMS) 过程中的 IoU 阈值。 +OUTPUT_MAP(NMSWithMask) = { + {0, OUTPUT_DESC(selected_boxes)}, {1, OUTPUT_DESC(selected_idx)}, {2, OUTPUT_DESC(selected_mask)}}; +//定义 "NMSWithMask" 运算符的输出映射(OUTPUT_MAP),将输出索引0映射为名为 "selected_boxes" 的输出描述(OUTPUT_DESC), +//将输出索引1映射为名为 "selected_idx" 的输出描述,将输出索引2映射为名为 "selected_mask" 的输出描述。 +//这表示 "NMSWithMask" 运算符在计算过程中会产生三个输出结果。 +REG_ADPT_DESC(NMSWithMask, kNameNMSWithMask, ADPT_DESC(NMSWithMask)) +//注册 "NMSWithMask" 运算符的适配器描述(REG_ADPT_DESC)。 +//适配器描述中包含运算符名称 "kNameNMSWithMask" 和适配器描述(ADPT_DESC)。 +//这将把 "NMSWithMask" 运算符与其在框架中的实现关联起来。 +} // namespace mindspore::transform diff --git a/mindspore/ccsrc/transform-update/selection_ops_declare.cc b/mindspore/ccsrc/transform-update/selection_ops_declare.cc new file mode 100644 index 00000000000..f7ab6952b0d --- /dev/null +++ b/mindspore/ccsrc/transform-update/selection_ops_declare.cc @@ -0,0 +1,320 @@ +/** + * Copyright 2019-2021 Huawei Technologies Co., Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include "transform/graph_ir/op_declare/selection_ops_declare.h" + +namespace mindspore::transform { +// CumsumD +INPUT_MAP(CumsumD) = {{1, INPUT_DESC(x)}}; +//一个输入映射x +INPUT_ATTR_MAP(CumsumD) = {{2, ATTR_DESC(axis, AnyTraits())}}; +//CumsumD操作的输入属性映射,有一个名为"axis"的属性,类型为int64_t +ATTR_MAP(CumsumD) = {{"exclusive", ATTR_DESC(exclusive, AnyTraits())}, + {"reverse", ATTR_DESC(reverse, AnyTraits())}}; +// CumsumD操作的属性映射,列出了"exclusive"和"reverse"两个属性,类型分别为bool +OUTPUT_MAP(CumsumD) = {{0, OUTPUT_DESC(y)}}; +// CumsumD操作的输出映射,有一个输出"y",索引为0 +REG_ADPT_DESC(CumsumD, kNameCumSum, ADPT_DESC(CumsumD)) +// 注册CumsumD操作的适配器描述kNameCumSum +// +// GatherV2 +INPUT_MAP(GatherV2) = {{1, INPUT_DESC(x)}, {2, INPUT_DESC(indices)}, {3, INPUT_DESC(axis)}}; +// GatherV2操作的输入映射,有三个输入:"x"、"indices"、"axis",索引分别为1、2、3 +ATTR_MAP(GatherV2) = EMPTY_ATTR_MAP; +// GatherV2操作没有属性,为空的属性映射 +OUTPUT_MAP(GatherV2) = {{0, OUTPUT_DESC(y)}}; +// GatherV2操作的输出映射,有一个输出"y",索引为0 +// +// CumprodD +INPUT_MAP(CumprodD) = {{1, INPUT_DESC(x)}}; +// CumprodD操作的输入映射,有一个输入"x",索引为1 +INPUT_ATTR_MAP(CumprodD) = {{2, ATTR_DESC(axis, AnyTraits())}}; +// CumprodD操作的输入属性映射,有一个名为"axis"的属性,类型为int64_tz +ATTR_MAP(CumprodD) = {{"exclusive", ATTR_DESC(exclusive, AnyTraits())}, + {"reverse", ATTR_DESC(reverse, AnyTraits())}}; +// CumprodD操作的属性映射,列出了"exclusive"和"reverse"两个属性,类型分别为bool +OUTPUT_MAP(CumprodD) = {{0, OUTPUT_DESC(y)}}; +// CumprodD操作的输出映射,有一个输出"y",索引为0 +REG_ADPT_DESC(CumprodD, kNameCumProd, ADPT_DESC(CumprodD)) +// 注册CumprodD操作的适配器描述kNameCumProd + +INPUT_MAP(SliceD) = {{1, INPUT_DESC(x)}}; +// SliceD操作的输入映射,有一个输入"x",索引为1 +INPUT_ATTR_MAP(SliceD) = {{2, ATTR_DESC(offsets, AnyTraits(), AnyTraits>())}, + {3, ATTR_DESC(size, AnyTraits(), AnyTraits>())}}; +// 有两个输入属性,分别是"offsets"和"size",对应的类型分别是int64_t和std::vector。 +ATTR_MAP(SliceD) = EMPTY_ATTR_MAP; +//SliceD操作没有属性,为空的属性映射 +OUTPUT_MAP(SliceD) = {{0, OUTPUT_DESC(y)}}; +// SliceD操作的输出映射 +REG_ADPT_DESC(SliceD, kNameSlice, ADPT_DESC(SliceD)) +//注册SliceD操作的适配器描述kNameSlice + +// TopK +INPUT_MAP(TopK) = {{1, INPUT_DESC(x)}, {2, INPUT_DESC(k)}}; +// TopK操作的输入映射,有两个输入:"x"、"k",索引分别为1、2 +ATTR_MAP(TopK) = {{"sorted", ATTR_DESC(sorted, AnyTraits())}}; +//属性映射,有一个属性sort类型是bool +OUTPUT_MAP(TopK) = {{0, OUTPUT_DESC(values)}, {1, OUTPUT_DESC(indices)}}; +//输入映射,有两个输出:"values"、"indices",索引分别为0、1 +REG_ADPT_DESC(TopK, kNameTopK, ADPT_DESC(TopK)) +// 注册TopK操作的适配器描述kNameTopK + +// InTopK +INPUT_MAP(InTopKD) = {{1, INPUT_DESC(x1)}, {2, INPUT_DESC(x2)}}; +// InTopKD操作的输入映射,有两个输入:"x1"、"x2",索引分别为1、2 +ATTR_MAP(InTopKD) = {{"k", ATTR_DESC(k, AnyTraits())}}; +//属性映射,有一个属性k类型是int64_t +OUTPUT_MAP(InTopKD) = {{0, OUTPUT_DESC(y)}}; +//输出映射,有一个输出y,索引是0 +REG_ADPT_DESC(InTopKD, kNameInTopKD, ADPT_DESC(InTopKD)) +//注册InTopKD操作的适配器kNameInTopK + +// TileD +INPUT_MAP(TileD) = {{1, INPUT_DESC(x)}}; +//输入映射,有一个输入x,索引是1 +INPUT_ATTR_MAP(TileD) = {{2, ATTR_DESC(multiples, AnyTraits(), AnyTraits>())}}; +// 输入属性multiples,对应的类型是int64_t和std::vector,索引是2 +ATTR_MAP(TileD) = EMPTY_ATTR_MAP; +//属性映射,空 +OUTPUT_MAP(TileD) = {{0, OUTPUT_DESC(y)}}; +//输出映射,有一个输出y,索引时0 +REG_ADPT_DESC(TileD, kNameTile, ADPT_DESC(TileD)) +// 注册TileD操作的适配器kNameTile + +// OneHot +INPUT_MAP(OneHot) = {{1, INPUT_DESC(x)}, {2, INPUT_DESC(depth)}, {3, INPUT_DESC(on_value)}, {4, INPUT_DESC(off_value)}}; +// 输入映射,有四个输入x、depth、on_value、off_value,索引分别是1、2、3、4 +ATTR_MAP(OneHot) = {{"axis", ATTR_DESC(axis, AnyTraits())}}; +//属性映射,一个属性axis,类型为int64_t +OUTPUT_MAP(OneHot) = {{0, OUTPUT_DESC(y)}}; +//输出映射,一个输出y,索引为0 +REG_ADPT_DESC(OneHot, prim::kPrimOneHot->name(), ADPT_DESC(OneHot)) +//将名为 "OneHot" 的操作与适配器描述进行注册。 +//将"OneHot" 的名称(prim::kPrimOneHot->name())和适配器描述(ADPT_DESC(OneHot))关联起来 + +// GatherV2D +INPUT_MAP(GatherV2D) = {{1, INPUT_DESC(x)}, {2, INPUT_DESC(indices)}}; +//输入映射,有两个输入x、indices,索引分别为1、2 +INPUT_ATTR_MAP(GatherV2D) = {{3, ATTR_DESC(axis, AnyTraits())}}; +//输入属性映射,int64_t型的axis,索引为3 +ATTR_MAP(GatherV2D) = EMPTY_ATTR_MAP; +//属性映射,空 +OUTPUT_MAP(GatherV2D) = {{0, OUTPUT_DESC(y)}}; +//有一个输出映射y,索引为0 +REG_ADPT_DESC(GatherV2D, prim::kPrimGather->name(), ADPT_DESC(GatherV2D)) +//这行代码是将名为 "GatherV2D" 的操作与适配器描述进行注册。 +//它将 "GatherV2D" 的名称(prim::kPrimGather->name())和适配器描述(ADPT_DESC(GatherV2D))关联起来,以便在特定计算框架或引擎中能够正确地执行和优化 "GatherV2D" 操作。 +REG_ADPT_DESC(Gather, kNameGather, ADPT_DESC(GatherV2D)) +//将名为 "Gather" 的操作与适配器描述进行注册。 +//将 "Gather" 的名称(kNameGather)和适配器描述(ADPT_DESC(GatherV2D))关联起来,以便在特定计算框架或引擎中能够正确地执行和优化 "Gather" 操作。 + +// ScatterNdD +INPUT_MAP(ScatterNdD) = {{1, INPUT_DESC(indices)}, {2, INPUT_DESC(x)}}; +//输入映射,有两个,indices和x,索引为1、2 +INPUT_ATTR_MAP(ScatterNdD) = { + {3, ATTR_DESC(shape, AnyTraits>(), AnyTraits>())}}; +//输入属性映射,int64_t型的shape,索引为3 +ATTR_MAP(ScatterNdD) = EMPTY_ATTR_MAP; +//属性映射为空 +OUTPUT_MAP(ScatterNdD) = {{0, OUTPUT_DESC(y)}}; +//输出映射y,索引为0 +REG_ADPT_DESC(ScatterNdD, kNameScatterNdD, ADPT_DESC(ScatterNdD)) +// 将名为 "ScatterNdD" 的操作与适配器描述进行注册。 +// 将 "ScatterNdD" 的名称(kNameScatterNdD)和适配器描述(ADPT_DESC(ScatterNdD))关联起来,以便在特定计算框架或引擎中能够正确地执行和优化 "ScatterNdD" 操作。 +// +// ScatterNonAliasingAdd +INPUT_MAP(ScatterNonAliasingAdd) = {{1, INPUT_DESC(x)}, {2, INPUT_DESC(indices)}, {3, INPUT_DESC(updates)}}; +//输入映射,共三个,x索引为1,indices索引为2,updates索引为3 +ATTR_MAP(ScatterNonAliasingAdd) = EMPTY_ATTR_MAP; +//属性映射,空 +OUTPUT_MAP(ScatterNonAliasingAdd) = {{0, OUTPUT_DESC(y)}}; +//输出映射,y索引为0 +REG_ADPT_DESC(ScatterNonAliasingAdd, kNameScatterNonAliasingAdd, ADPT_DESC(ScatterNonAliasingAdd)) +// 注册ScatterNonAliasingAdd操作的适配器描述kNameScatterNonAliasingAdd +// +// GatherNd +INPUT_MAP(GatherNd) = {{1, INPUT_DESC(x)}, {2, INPUT_DESC(indices)}}; +//输入映射,共两个,x索引为1,indices索引为2 +ATTR_MAP(GatherNd) = EMPTY_ATTR_MAP; +//属性映射,空 +OUTPUT_MAP(GatherNd) = {{0, OUTPUT_DESC(y)}}; +//输出映射,y索引为0 +REG_ADPT_DESC(GatherNd, kNameGatherNd, ADPT_DESC(GatherNd)) +// 注册GatherNd操作的适配器描述 kNameGatherNd + +// GatherD +INPUT_MAP(GatherD) = {{1, INPUT_DESC(x)}, {2, INPUT_DESC(dim)}, {3, INPUT_DESC(index)}}; +//输入映射,共三个,x索引为1,dim索引为2,index索引为3 +ATTR_MAP(GatherD) = EMPTY_ATTR_MAP; +//属性映射,空 +OUTPUT_MAP(GatherD) = {{0, OUTPUT_DESC(y)}}; +//输出映射,y索引为0 +REG_ADPT_DESC(GatherD, kNameGatherD, ADPT_DESC(GatherD)) +// 注册kNameGatherD操作的适配器描述 GatherD + +// Range +INPUT_MAP(RangeD) = {{1, INPUT_DESC(x)}}; +//输入映射,x索引为1 +ATTR_MAP(RangeD) = {{"start", ATTR_DESC(start, AnyTraits())}, + {"limit", ATTR_DESC(limit, AnyTraits())}, + {"delta", ATTR_DESC(delta, AnyTraits())}}; +//属性映射,列出了"start"、"limit"、"delta"三个属性,类型分别为float +REG_ADPT_DESC(RangeD, kNameRange, ADPT_DESC(RangeD)) +//注册RangeD操作的适配器描述 kNameRange + +// InplaceAddD +INPUT_MAP(InplaceAddD) = {{1, INPUT_DESC(x)}, {2, INPUT_DESC(v)}}; +// 输入映射,x索引为1,y索引为2 +ATTR_MAP(InplaceAddD) = {{"indices", ATTR_DESC(indices, AnyTraits>())}}; +//属性映射,属性indices类型为int64_t +OUTPUT_MAP(InplaceAddD) = {{0, OUTPUT_DESC(y)}}; +//输出映射,y索引为0 +REG_ADPT_DESC(InplaceAddD, kNameInplaceAddD, ADPT_DESC(InplaceAddD)) +// 注册RangeD操作的适配器描述 kNameRange + +// InplaceSubD +INPUT_MAP(InplaceSubD) = {{1, INPUT_DESC(x)}, {2, INPUT_DESC(v)}}; +//输入映射,共两个,x索引为1,y索引为2 +ATTR_MAP(InplaceSubD) = {{"indices", ATTR_DESC(indices, AnyTraits>())}}; +// 属性映射,属性indices类型为int64_t +OUTPUT_MAP(InplaceSubD) = {{0, OUTPUT_DESC(y)}}; +// 输出映射,y索引为0 +REG_ADPT_DESC(InplaceSubD, kNameInplaceSubD, ADPT_DESC(InplaceSubD)) +// 注册InplaceSubD操作的适配器描述kNameInplaceSubD + +// InplaceUpdateD +INPUT_MAP(InplaceUpdateD) = {{1, INPUT_DESC(x)}, {2, INPUT_DESC(v)}}; +// 输入映射,共两个,x索引为1,y索引为2 +ATTR_MAP(InplaceUpdateD) = {{"indices", ATTR_DESC(indices, AnyTraits>())}}; +// 属性映射,属性indices类型为int64_t +OUTPUT_MAP(InplaceUpdateD) = {{0, OUTPUT_DESC(y)}}; +// 输出映射,y索引为0 +REG_ADPT_DESC(InplaceUpdateD, kNameInplaceUpdateD, ADPT_DESC(InplaceUpdateD)) +// 注册InplaceUpdateD操作的适配器描述kNameInplaceUpdateD + +// Select +INPUT_MAP(Select) = {{1, INPUT_DESC(condition)}, {2, INPUT_DESC(x1)}, {3, INPUT_DESC(x2)}}; +// 输入映射,共三个,condition索引为1,x1索引为2,x2索引为3 +ATTR_MAP(Select) = EMPTY_ATTR_MAP; +//属性映射,空 +OUTPUT_MAP(Select) = {{0, OUTPUT_DESC(y)}}; +//输出映射,y索引为0 +REG_ADPT_DESC(Select, prim::kPrimSelect->name(), ADPT_DESC(Select)) +// 注册InplaceUpdateD操作的适配器描述prim::kPrimSelect->name() + +// StridedSliceGrad +INPUT_MAP(StridedSliceGrad) = { + {1, INPUT_DESC(dy)}, {2, INPUT_DESC(shape)}, {3, INPUT_DESC(begin)}, {4, INPUT_DESC(end)}, {5, INPUT_DESC(strides)}}; +// 输入映射,共五个,dy索引为1,shape索引为2,begin索引为3,end索引为4,strides索引为5 +ATTR_MAP(StridedSliceGrad) = {{"begin_mask", ATTR_DESC(begin_mask, AnyTraits())}, + {"end_mask", ATTR_DESC(end_mask, AnyTraits())}, + {"ellipsis_mask", ATTR_DESC(ellipsis_mask, AnyTraits())}, + {"new_axis_mask", ATTR_DESC(new_axis_mask, AnyTraits())}, + {"shrink_axis_mask", ATTR_DESC(shrink_axis_mask, AnyTraits())}}; +// 属性映射,列出了"begin_mask"、"end_mask"、"ellipsis_mask"、"new_axis_mask"、"shrink_axis_mask"五个属性,类型分别为int64_t +OUTPUT_MAP(StridedSliceGrad) = {{0, OUTPUT_DESC(output)}}; +// 输出映射,output索引为0 +REG_ADPT_DESC(StridedSliceGrad, kNameStridedSliceGrad, ADPT_DESC(StridedSliceGrad)) +//注册StridedSliceGradD操作的适配器描述kNameStridedSliceGrad + +// StridedSlice +INPUT_MAP(StridedSlice) = {{1, INPUT_DESC(x)}, {2, INPUT_DESC(begin)}, {3, INPUT_DESC(end)}, {4, INPUT_DESC(strides)}}; +// 输入映射,共四个,x索引为1,begin索引为2,end索引为3,strides索引为4 +ATTR_MAP(StridedSlice) = {{"begin_mask", ATTR_DESC(begin_mask, AnyTraits())}, + {"end_mask", ATTR_DESC(end_mask, AnyTraits())}, + {"ellipsis_mask", ATTR_DESC(ellipsis_mask, AnyTraits())}, + {"new_axis_mask", ATTR_DESC(new_axis_mask, AnyTraits())}, + {"shrink_axis_mask", ATTR_DESC(shrink_axis_mask, AnyTraits())}}; +// 属性映射,列出了"begin_mask"、"end_mask"、"ellipsis_mask"、"new_axis_mask"、"shrink_axis_mask"五个属性,类型分别为int64_t +OUTPUT_MAP(StridedSlice) = {{0, OUTPUT_DESC(y)}}; +// 输出映射,y索引为0 +REG_ADPT_DESC(StridedSlice, kNameStridedSlice, ADPT_DESC(StridedSlice)) +// 注册StridedSlice操作的适配器描述kNameStridedSlice + +// StridedSliceV2 +INPUT_MAP(StridedSliceV2) = { + {1, INPUT_DESC(x)}, {2, INPUT_DESC(begin)}, {3, INPUT_DESC(end)}, {4, INPUT_DESC(axes)}, {5, INPUT_DESC(strides)}}; +// 输入映射,共五个,x索引为1,begin索引为2,end索引为3,axes索引为4,strides索引为5 +ATTR_MAP(StridedSliceV2) = {{"begin_mask", ATTR_DESC(begin_mask, AnyTraits())}, + {"end_mask", ATTR_DESC(end_mask, AnyTraits())}, + {"ellipsis_mask", ATTR_DESC(ellipsis_mask, AnyTraits())}, + {"new_axis_mask", ATTR_DESC(new_axis_mask, AnyTraits())}, + {"shrink_axis_mask", ATTR_DESC(shrink_axis_mask, AnyTraits())}}; +// 属性映射,列出了"begin_mask"、"end_mask"、"ellipsis_mask"、"new_axis_mask"、"shrink_axis_mask"五个属性,类型分别为int64_t +OUTPUT_MAP(StridedSliceV2) = {{0, OUTPUT_DESC(y)}}; +// 输出映射,y索引为0 +REG_ADPT_DESC(StridedSliceV2, kNameStridedSliceV2, ADPT_DESC(StridedSliceV2)) +// 注册StridedSliceV2操作的适配器描述StridedSliceV2 + +// UnsortedSegmentSum +INPUT_MAP(UnsortedSegmentSumD) = {{1, INPUT_DESC(x)}, {2, INPUT_DESC(segment_ids)}}; +// 输入映射,共两个,x索引为1,segment_ids索引为2 +INPUT_ATTR_MAP(UnsortedSegmentSumD) = {{3, ATTR_DESC(num_segments, AnyTraits())}}; +// 输入属性映射,num_segments类型是int64_t,索引是3 +ATTR_MAP(UnsortedSegmentSumD) = EMPTY_ATTR_MAP; +// 属性映射,空 +OUTPUT_MAP(UnsortedSegmentSumD) = {{0, OUTPUT_DESC(y)}}; +//输出映射,y索引为0 +REG_ADPT_DESC(UnsortedSegmentSumD, prim::kPrimUnsortedSegmentSum->name(), ADPT_DESC(UnsortedSegmentSumD)) +//注册UnsortedSegmentSumD操作的适配器描述prim::kPrimUnsortedSegmentSum->name() + +// UnsortedSegmentProdD +INPUT_MAP(UnsortedSegmentProdD) = {{1, INPUT_DESC(x)}, {2, INPUT_DESC(segment_ids)}}; +// 输入映射,共两个,x索引为1,segment_ids索引为2 +INPUT_ATTR_MAP(UnsortedSegmentProdD) = {{3, ATTR_DESC(num_segments, AnyTraits())}}; +// 输入属性映射,num_segments类型是int64_t,索引是3 +ATTR_MAP(UnsortedSegmentProdD) = EMPTY_ATTR_MAP; +// 属性映射,空 +OUTPUT_MAP(UnsortedSegmentProdD) = {{0, OUTPUT_DESC(y)}}; +// 输出映射,y索引为0 +REG_ADPT_DESC(UnsortedSegmentProdD, kNameUnsortedSegmentProdD, ADPT_DESC(UnsortedSegmentProdD)) +// 注册UnsortedSegmentSumD操作的适配器描述kNameUnsortedSegmentProdD + + +// UnsortedSegmentMaxD +INPUT_MAP(UnsortedSegmentMaxD) = {{1, INPUT_DESC(x)}, {2, INPUT_DESC(segment_ids)}}; +// 输入映射,共两个,x索引为1,segment_ids索引为2 +INPUT_ATTR_MAP(UnsortedSegmentMaxD) = {{3, ATTR_DESC(num_segments, AnyTraits())}}; +// 输入属性映射,num_segments类型是int64_t,索引是3 +ATTR_MAP(UnsortedSegmentMaxD) = EMPTY_ATTR_MAP; +// 属性映射,空 +OUTPUT_MAP(UnsortedSegmentMaxD) = {{0, OUTPUT_DESC(y)}}; +// 输出映射,y索引为0 +REG_ADPT_DESC(UnsortedSegmentMaxD, kNameUnsortedSegmentMaxD, ADPT_DESC(UnsortedSegmentMaxD)) +// 注册UnsortedSegmentMaxD操作的适配器描述kNameUnsortedSegmentMaxD + +// UnsortedSegmentMin +INPUT_MAP(UnsortedSegmentMin) = {{1, INPUT_DESC(x)}, {2, INPUT_DESC(segment_ids)}, {3, INPUT_DESC(num_segments)}}; +// 输入映射,共三个,x索引为1,segment_ids索引为2,num_segments索引为3 +ATTR_MAP(UnsortedSegmentMin) = EMPTY_ATTR_MAP; +// 属性映射,空 +OUTPUT_MAP(UnsortedSegmentMin) = {{0, OUTPUT_DESC(y)}}; +// 输出映射,y索引为0 +REG_ADPT_DESC(UnsortedSegmentMin, prim::kPrimUnsortedSegmentMin->name(), ADPT_DESC(UnsortedSegmentMin)) +// 注册UnsortedSegmentMin操作的适配器描述prim::kPrimUnsortedSegmentMin->name() + +// ReverseV2 +INPUT_MAP(ReverseV2D) = {{1, INPUT_DESC(x)}}; +// 输入映射,x索引为1 +ATTR_MAP(ReverseV2D) = {{"axis", ATTR_DESC(axis, AnyTraits(), AnyTraits>())}}; +//属性映射,axis类型为int64_t和std::vector +OUTPUT_MAP(ReverseV2D) = {{0, OUTPUT_DESC(y)}}; +//输出映射,y索引为0 +REG_ADPT_DESC(ReverseV2D, kNameReverseV2, ADPT_DESC(ReverseV2D)) +// 注册ReverseV2D操作的适配器描述kNameReverseV2 +} // namespace mindspore::transform diff --git a/mindspore/ccsrc/transform-update/split_combination_ops_declare.cc b/mindspore/ccsrc/transform-update/split_combination_ops_declare.cc new file mode 100644 index 00000000000..a7ed72213fa --- /dev/null +++ b/mindspore/ccsrc/transform-update/split_combination_ops_declare.cc @@ -0,0 +1,94 @@ +/** + * Copyright 2019-2021 Huawei Technologies Co., Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "transform/graph_ir/op_declare/split_combination_ops_declare.h" +#include + +namespace mindspore::transform { +// SplitD +INPUT_MAP(SplitD) = {{1, INPUT_DESC(x)}}; +//输入映射,将输入索引1映射为名为x的输入描述(INPUT_DESC) +ATTR_MAP(SplitD) = {{"axis", ATTR_DESC(split_dim, AnyTraits())},//指定维度 + {"output_num", ATTR_DESC(num_split, AnyTraits())}};//指定输出数量 +// 属性映射 +DYN_OUTPUT_MAP(SplitD) = {{0, DYN_OUTPUT_DESC(y)}}; +//动态输出映射 //将输出索引0映射为名为 "y" 的动态输出描述(DYN_OUTPUT_DESC) +REG_ADPT_DESC(SplitD, kNameSplitD, ADPT_DESC(SplitD)) +//注册 "SplitD" 运算符的适配器描述(REG_ADPT_DESC),适配器描述中包含运算符名称 "kNameSplitD" 和适配器描述(ADPT_DESC)。 +// 这将把 "SplitD" 运算符与其在框架中的实现关联起来。 +// +// Pack +INPUT_MAP(Pack) = EMPTY_INPUT_MAP; +//定义 "Pack" 运算符的输入映射(INPUT_MAP)为空。这表示 "Pack" 运算符没有显式的输入,因此没有任何输入描述。 +DYN_INPUT_MAP(Pack) = {{1, DYN_INPUT_DESC(x)}}; +//定义 "Pack" 运算符的动态输入映射(DYN_INPUT_MAP),将输入索引1映射为名为 "x" 的动态输入描述(DYN_INPUT_DESC) +ATTR_MAP(Pack) = {{"num", ATTR_DESC(N, AnyTraits())}, {"axis", ATTR_DESC(axis, AnyTraits())}}; +//定义 "Pack" 运算符的属性映射(ATTR_MAP) 两个属性:数量/维度 +OUTPUT_MAP(Pack) = {{0, OUTPUT_DESC(y)}};//输出映射(OUTPUT_MAP),将输出索引0映射为名为 "y" 的输出描述(OUTPUT_DESC) +REG_ADPT_DESC(Pack, prim::kStack, ADPT_DESC(Pack))//注册 "Pack" 运算符的适配器描述(REG_ADPT_DESC) + +// ParallelConcat +INPUT_MAP(ParallelConcat) = EMPTY_INPUT_MAP; +//定义 "ParallelConcat" 运算符的输入映射(INPUT_MAP)为空。 +//这表示 "ParallelConcat" 运算符没有显式的输入,因此没有任何输入描述 +DYN_INPUT_MAP(ParallelConcat) = {{1, DYN_INPUT_DESC(values)}}; +//定义 "ParallelConcat" 运算符的动态输入映射(DYN_INPUT_MAP),将输入索引1映射为名为 "values" 的动态输入描述(DYN_INPUT_DESC)。 +//这表示 "ParallelConcat" 运算符会接收一个动态数量的输入,而每个输入都可以使用 "values" 来标识 +ATTR_MAP(ParallelConcat) = {//属性映射(ATTR_MAP),包含两个属性 + {"shape", ATTR_DESC(shape, AnyTraits>())},//连接操作时的形状 + {"N", ATTR_DESC(N, AnyTraits())},//连接的数量 +}; +OUTPUT_MAP(ParallelConcat) = {{0, OUTPUT_DESC(output_data)}}; +//输出映射(OUTPUT_MAP) +REG_ADPT_DESC(ParallelConcat, kNameParallelConcat, ADPT_DESC(ParallelConcat)) +//注册 "ParallelConcat" 运算符的适配器描述(REG_ADPT_DESC)。 +//适配器描述中包含运算符名称 "kNameParallelConcat" 和适配器描述(ADPT_DESC)。 +//这将把 "ParallelConcat" 运算符与其在框架中的实现关联起来,并使用 "kNameParallelConcat" 作为标识来调用适配器。 + + + +// ConcatD +INPUT_MAP(ConcatD) = EMPTY_INPUT_MAP; +//定义 "ConcatD" 运算符的输入映射(INPUT_MAP)为空。 +//这表示 "ConcatD" 运算符没有显式的输入,因此没有任何输入描述 +DYN_INPUT_MAP(ConcatD) = {{1, DYN_INPUT_DESC(x)}}; +//定义 "ConcatD" 运算符的动态输入映射(DYN_INPUT_MAP),将输入索引1映射为名为 "x" 的动态输入描述(DYN_INPUT_DESC)。 +//这表示 "ConcatD" 运算符会接收一个动态数量的输入,而每个输入都可以使用 "x" 来标识。 +ATTR_MAP(ConcatD) = {//属性映射 + {"axis", ATTR_DESC(concat_dim, AnyTraits())},//连接操作时的维度 + {"inputNums", ATTR_DESC(N, AnyTraits())},//输入数量 +}; +OUTPUT_MAP(ConcatD) = {{0, OUTPUT_DESC(y)}}; +//定义 "ConcatD" 运算符的输出映射(OUTPUT_MAP),将输出索引0映射为名为 "y" 的输出描述(OUTPUT_DESC)。 +//这表示 "ConcatD" 运算符会产生一个输出结果,并使用 "y" 来标识该输出。 +REG_ADPT_DESC(ConcatD, prim::kPrimConcat->name(), ADPT_DESC(ConcatD)) +//注册 "ConcatD" 运算符的适配器描述(REG_ADPT_DESC)。适配器描述中包含运算符名称 "prim::kPrimConcat->name()" 和适配器描述(ADPT_DESC)。 +// 这将把 "ConcatD" 运算符与其在框架中的实现关联起来,并使用 "prim::kPrimConcat->name()" 作为标识来调用适配器。 +// +// ConcatV2D Inference for tf +INPUT_MAP(ConcatV2D) = EMPTY_INPUT_MAP;//输入映射为空 +DYN_INPUT_MAP(ConcatV2D) = {{1, DYN_INPUT_DESC(x)}};//动态输入映射,可以有多个数量的映射 +ATTR_MAP(ConcatV2D) = { + {"axis", ATTR_DESC(concat_dim, AnyTraits())},//连接操作时的维度 + {"N", ATTR_DESC(N, AnyTraits())},//连接的数量 +}; +OUTPUT_MAP(ConcatV2D) = {{0, OUTPUT_DESC(y)}}; +//定义 "ConcatV2D" 运算符的输出映射(OUTPUT_MAP),将输出索引0映射为名为 "y" 的输出描述(OUTPUT_DESC)。 +//这表示 "ConcatV2D" 运算符会产生一个输出结果,并使用 "y" 来标识该输出。 +REG_ADPT_DESC(ConcatV2D, kNameConcatV2D, ADPT_DESC(ConcatV2D)) +//注册 "ConcatV2D" 运算符的适配器描述(REG_ADPT_DESC)。适配器描述中包含运算符名称 "kNameConcatV2D" 和适配器描述(ADPT_DESC)。 +//这将把 "ConcatV2D" 运算符与其在框架中的实现关联起来,并使用 "kNameConcatV2D" 作为标识来调用适配器。 +} // namespace mindspore::transform diff --git a/mindspore/ccsrc/transform-update/state_ops_declare.cc b/mindspore/ccsrc/transform-update/state_ops_declare.cc new file mode 100644 index 00000000000..8e77f0ef5e5 --- /dev/null +++ b/mindspore/ccsrc/transform-update/state_ops_declare.cc @@ -0,0 +1,25 @@ +/** + * Copyright 2019 Huawei Technologies Co., Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "transform/graph_ir/op_declare/state_ops_declare.h" +//"Variable" 运算符通常用于表示模型中的变量(例如权重和偏置项) +namespace mindspore::transform { +// Variable +INPUT_MAP(Variable) = {{1, INPUT_DESC(x)}}; +// 定义 "Variable" 运算符的输入映射(INPUT_MAP),将输入1映射为名为 "x" 的输入描述(INPUT_DESC) +ATTR_MAP(Variable) = EMPTY_ATTR_MAP; +// 定义 "Variable" 运算符的属性映射(ATTR_MAP),此处为空(EMPTY_ATTR_MAP) +} // namespace mindspore::transform diff --git a/mindspore/ccsrc/transform-update/test/transforms.py b/mindspore/ccsrc/transform-update/test/transforms.py new file mode 100644 index 00000000000..f053caeae29 --- /dev/null +++ b/mindspore/ccsrc/transform-update/test/transforms.py @@ -0,0 +1,1262 @@ +# Copyright 2020-2022 Huawei Technologies Co., Ltd +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +The module text.transforms is inherited from _c_dataengine +and is implemented based on ICU4C and cppjieba in C++. +It's a high performance module to process NLP text. +Users can use Vocab to build their own dictionary, +use appropriate tokenizers to split sentences into different tokens, +and use Lookup to find the index of tokens in Vocab. +妯″潡text.transforms缁ф壙鑷猒c_dataengine骞跺熀浜嶪CU4C鍜宑ppjieba鍦–++涓疄鐜般 +瀹冩槸涓涓鐞哊LP鏂囨湰鐨勯珮鎬ц兘妯″潡銆 +鐢ㄦ埛鍙互浣跨敤Vocab鏋勫缓鑷繁鐨勮瘝鍏革紝浣跨敤閫傚綋鐨勬爣璁板櫒灏嗗彞瀛愬垎鍓叉垚涓嶅悓鐨勬爣璁帮紝骞朵娇鐢↙ookup鏌ユ壘Vocab涓殑浠ょ墝绱㈠紩銆 +.. Note:: + A constructor's arguments for every class in this module must be saved into the + class attributes (self.xxx) to support save() and load(). + +Examples: + >>> text_file_dataset_dir = ["/path/to/text_file_dataset_file"] # contains 1 or multiple text files + >>> # Create a dataset for text sentences saved as line data in a file + >>> text_file_dataset = ds.TextFileDataset(dataset_files=text_file_dataset_dir, shuffle=False) + >>> # Tokenize sentences to unicode characters + >>> tokenizer = text.UnicodeCharTokenizer() + >>> # Load vocabulary from list + >>> vocab = text.Vocab.from_list(word_list=['娣', '鍦', '娆', '杩', '鎮']) + >>> # Use Lookup operator to map tokens to ids + >>> lookup = text.Lookup(vocab=vocab) + >>> text_file_dataset = text_file_dataset.map(operations=[tokenizer, lookup]) + >>> # if text line in dataset_file is: + >>> # 娣卞湷娆㈣繋鎮 + >>> # then the output will be: + >>> # {'text': array([0, 1, 2, 3, 4], dtype=int32)} +""" +import json +import os +import re +import platform +import numpy as np + +import mindspore._c_dataengine as cde +from mindspore.common import dtype as mstype + +from .utils import JiebaMode, NormalizeForm, to_str, SPieceTokenizerOutType, SPieceTokenizerLoadType, SentencePieceVocab +from .validators import check_lookup, check_jieba_add_dict, check_to_vectors, \ + check_jieba_add_word, check_jieba_init, check_with_offsets, check_unicode_script_tokenizer, \ + check_wordpiece_tokenizer, check_regex_replace, check_regex_tokenizer, check_basic_tokenizer, check_ngram, \ + check_pair_truncate, check_to_number, check_bert_tokenizer, check_python_tokenizer, check_slidingwindow, \ + check_sentence_piece_tokenizer +from ..core.datatypes import mstype_to_detype +from ..core.validator_helpers import replace_none +from ..transforms.py_transforms_util import Implementation +from ..transforms.transforms import TensorOperation +from ..transforms.validators import invalidate_callable + + +class TextTensorOperation(TensorOperation): + """ + Base class of Text Tensor Ops + 鏂囨湰寮犻噺杩愮畻鐨勫熀绫 + """ + + def __init__(self): + super().__init__() + self.implementation = Implementation.C + + def parse(self): + raise NotImplementedError("TextTensorOperation has to implement parse() method.") + + +DE_C_INTER_JIEBA_MODE = { + JiebaMode.MIX: cde.JiebaMode.DE_JIEBA_MIX, + JiebaMode.MP: cde.JiebaMode.DE_JIEBA_MP, + JiebaMode.HMM: cde.JiebaMode.DE_JIEBA_HMM +} + +DE_C_INTER_SENTENCEPIECE_LOADTYPE = { + SPieceTokenizerLoadType.FILE: cde.SPieceTokenizerLoadType.DE_SPIECE_TOKENIZER_LOAD_KFILE, + SPieceTokenizerLoadType.MODEL: cde.SPieceTokenizerLoadType.DE_SPIECE_TOKENIZER_LOAD_KMODEL +} + +DE_C_INTER_SENTENCEPIECE_OUTTYPE = { + SPieceTokenizerOutType.STRING: cde.SPieceTokenizerOutType.DE_SPIECE_TOKENIZER_OUTTYPE_KString, + SPieceTokenizerOutType.INT: cde.SPieceTokenizerOutType.DE_SPIECE_TOKENIZER_OUTTYPE_KINT +} + + +class JiebaTokenizer(TextTensorOperation): + """ + Tokenize Chinese string into words based on dictionary. + 鏍规嵁瀛楀吀灏嗕腑鏂囧瓧绗︿覆鏍囪涓哄崟璇嶃 + Note: + The integrity of the HMMSEgment algorithm and MPSegment algorithm files must be confirmed. + + Args: + hmm_path (str): Dictionary file is used by HMMSegment algorithm. + The dictionary can be obtained on the official website of cppjieba. + mp_path (str): Dictionary file is used by MPSegment algorithm. + The dictionary can be obtained on the official website of cppjieba. + mode (JiebaMode, optional): Valid values can be any of [JiebaMode.MP, JiebaMode.HMM, + JiebaMode.MIX](default=JiebaMode.MIX). + + - JiebaMode.MP, tokenize with MPSegment algorithm. + - JiebaMode.HMM, tokenize with Hidden Markov Model Segment algorithm. + - JiebaMode.MIX, tokenize with a mix of MPSegment and HMMSegment algorithm. + with_offsets (bool, optional): Whether or not output offsets of tokens (default=False). + + Raises: + ValueError: If path of HMMSegment dict is not provided. + ValueError: If path of MPSegment dict is not provided. + TypeError: If `hmm_path` or `mp_path` is not of type string. + TypeError: If `with_offsets` is not of type bool. + + Supported Platforms: + ``CPU`` + + Examples: + >>> from mindspore.dataset.text import JiebaMode + >>> # If with_offsets=False, default output one column {["text", dtype=str]} + >>> jieba_hmm_file = "/path/to/jieba/hmm/file" + >>> jieba_mp_file = "/path/to/jieba/mp/file" + >>> tokenizer_op = text.JiebaTokenizer(jieba_hmm_file, jieba_mp_file, mode=JiebaMode.MP, with_offsets=False) + >>> text_file_dataset = text_file_dataset.map(operations=tokenizer_op) + >>> # If with_offsets=False, then output three columns {["token", dtype=str], ["offsets_start", dtype=uint32], + >>> # ["offsets_limit", dtype=uint32]} + >>> tokenizer_op = text.JiebaTokenizer(jieba_hmm_file, jieba_mp_file, mode=JiebaMode.MP, with_offsets=True) + >>> text_file_dataset_1 = text_file_dataset_1.map(operations=tokenizer_op, input_columns=["text"], + ... output_columns=["token", "offsets_start", "offsets_limit"], + ... column_order=["token", "offsets_start", "offsets_limit"]) + """ + + @check_jieba_init + def __init__(self, hmm_path, mp_path, mode=JiebaMode.MIX, with_offsets=False): + super().__init__() + if not isinstance(mode, JiebaMode): + raise TypeError("Wrong input type for mode, should be JiebaMode.") + + self.mode = mode + self.__check_path__(hmm_path) + self.hmm_path = hmm_path + self.__check_path__(mp_path) + self.mp_path = mp_path + self.with_offsets = with_offsets + self.words = [] + + # 瀹氫箟涓涓悕涓篲_check_path__鐨勭鏈夋柟娉 +def __check_path__(self, model_path): + """妫鏌ユā鍨嬭矾寰勬槸鍚﹀瓨鍦""" + # 浣跨敤os.path.exists()妫鏌ユā鍨嬭矾寰勬槸鍚﹀瓨鍦 + if not os.path.exists(os.path.realpath(model_path)): + # 濡傛灉妯″瀷璺緞涓嶅瓨鍦紝鎶涘嚭ValueError寮傚父骞舵彁渚涚浉搴旈敊璇秷鎭 + raise ValueError(" jieba mode file {} is not exist.".format(model_path)) + +# 瀹氫箟涓涓悕涓簆arse鐨勬柟娉 +def parse(self): + # 鍒涘缓涓涓猚de.JiebaTokenizerOperation瀵硅薄锛岀敤浜庝腑鏂囧垎璇嶆搷浣滐紝浣跨敤浠ヤ笅鍙傛暟杩涜鍒濆鍖栵細 + # - self.hmm_path: HMM妯″瀷鏂囦欢鐨勮矾寰勶紝鐢ㄤ簬鍒嗚瘝鐨勯殣椹皵鍙か妯″瀷 + # - self.mp_path: MP妯″瀷鏂囦欢鐨勮矾寰勶紝鐢ㄤ簬鍒嗚瘝鐨勬渶澶ф鐜囨ā鍨 + # - DE_C_INTER_JIEBA_MODE.get(self.mode): 浣跨敤self.mode浣滀负閿粠DE_C_INTER_JIEBA_MODE瀛楀吀涓幏鍙栧搴旂殑鍊硷紝 + # 鐢ㄤ簬鎸囧畾jieba鍒嗚瘝鐨勬ā寮 + # - self.with_offsets: 鏄惁杩斿洖鍒嗚瘝鐨勫亸绉婚噺淇℃伅 + jieba_tokenizer = cde.JiebaTokenizerOperation(self.hmm_path, self.mp_path, + DE_C_INTER_JIEBA_MODE.get(self.mode), + self.with_offsets) + + # 閬嶅巻self.words涓殑璇嶈鍒楄〃锛屽皢姣忎釜璇嶈娣诲姞鍒板垎璇嶅櫒涓 + for word in self.words: + jieba_tokenizer.add_word(word[0], word[1]) + + # 杩斿洖宸查厤缃殑jieba鍒嗚瘝鍣ㄥ璞 + return jieba_tokenizer + + + @invalidate_callable + @check_jieba_add_word + def add_word(self, word, freq=None): + """ + Add a user defined word to JiebaTokenizer's dictionary. + 灏嗙敤鎴峰畾涔夌殑鍗曡瘝娣诲姞鍒癑iebaTokenizer鐨勫瓧鍏镐腑銆 + Args: + word (str): The word to be added to the JiebaTokenizer instance. + The added word will not be written into the built-in dictionary on disk. + freq (int, optional): The frequency of the word to be added. The higher the frequency, + the better chance the word will be tokenized (default=None, use default frequency). + + Examples: + >>> from mindspore.dataset.text import JiebaMode + >>> jieba_hmm_file = "/path/to/jieba/hmm/file" + >>> jieba_mp_file = "/path/to/jieba/mp/file" + >>> jieba_op = text.JiebaTokenizer(jieba_hmm_file, jieba_mp_file, mode=JiebaMode.MP) + >>> sentence_piece_vocab_file = "/path/to/sentence/piece/vocab/file" + >>> with open(sentence_piece_vocab_file, 'r') as f: + ... for line in f: + ... word = line.split(',')[0] + ... jieba_op.add_word(word) + >>> text_file_dataset = text_file_dataset.map(operations=jieba_op, input_columns=["text"]) + """ + + if freq is None: + self.words.append((word, 0)) + else: + self.words.append((word, freq)) + + @invalidate_callable + @check_jieba_add_dict + def add_dict(self, user_dict): + """ + Add a user defined word to JiebaTokenizer's dictionary. + 灏嗙敤鎴峰畾涔夌殑鍗曡瘝娣诲姞鍒癑iebaTokenizer鐨勫瓧鍏镐腑銆 + Args: + user_dict (Union[str, dict]): One of the two loading methods is file path(str) loading + (according to the Jieba dictionary format) and the other is Python dictionary(dict) loading, + Python Dict format: {word1:freq1, word2:freq2,...}. + Jieba dictionary format : word(required), freq(optional), such as: + + .. code-block:: + + word1 freq1 + word2 None + word3 freq3 + + Only valid word-freq pairs in user provided file will be added into the dictionary. + Rows containing invalid input will be ignored. No error nor warning Status is returned. + + Examples: + >>> from mindspore.dataset.text import JiebaMode + >>> jieba_hmm_file = "/path/to/jieba/hmm/file" + >>> jieba_mp_file = "/path/to/jieba/mp/file" + >>> user_dict = {"鐢烽粯濂虫唱": 10} + >>> jieba_op = text.JiebaTokenizer(jieba_hmm_file, jieba_mp_file, mode=JiebaMode.MP) + >>> jieba_op.add_dict(user_dict) + >>> text_file_dataset = text_file_dataset.map(operations=jieba_op, input_columns=["text"]) + """ + + + # 妫鏌ser_dict鐨勬暟鎹被鍨 + if isinstance(user_dict, str): + # 濡傛灉user_dict鏄瓧绗︿覆绫诲瀷锛岃皟鐢╛_add_dict_py_file鏂规硶锛岃鏂规硶鐢ㄤ簬娣诲姞Python璇嶅吀鏂囦欢 + self.__add_dict_py_file(user_dict) + elif isinstance(user_dict, dict): + # 濡傛灉user_dict鏄瓧鍏哥被鍨嬶紝閬嶅巻瀛楀吀涓殑閿煎锛岀劧鍚庤皟鐢╝dd_word鏂规硶娣诲姞姣忎釜璇嶈鍙婂叾瀵瑰簲鐨勮瘝鎬 + for k, v in user_dict.items(): + self.add_word(k, v) + else: + # 濡傛灉user_dict鐨勬暟鎹被鍨嬩笉鏄瓧绗︿覆鎴栧瓧鍏革紝鎶涘嚭TypeError寮傚父锛屽苟鎻愪緵鐩稿簲閿欒娑堟伅 + raise TypeError("The type of user_dict must be str or dict.") + + + # 瀹氫箟涓涓悕涓篲_add_dict_py_file鐨勭鏈夋柟娉曪紝鐢ㄤ簬浠庢枃浠朵腑娣诲姞鐢ㄦ埛鑷畾涔夎瘝鍏 +def __add_dict_py_file(self, file_path): + """閫氳繃鏂囦欢娣诲姞鐢ㄦ埛鑷畾涔夎瘝鍏""" + # 璋冪敤__parser_file鏂规硶瑙f瀽璇嶅吀鏂囦欢骞惰繑鍥炶瘝璇垪琛 + words_list = self.__parser_file(file_path) + + # 閬嶅巻璇嶈鍒楄〃锛屽皢姣忎釜璇嶈鍙婂叾棰戠巼娣诲姞鍒板垎璇嶅櫒涓 + for data in words_list: + if data[1] is None: + freq = 0 + else: + freq = int(data[1]) + self.add_word(data[0], freq) + +# 瀹氫箟涓涓悕涓篲_decode鐨勭鏈夋柟娉曪紝鐢ㄤ簬灏嗚瘝鍏告枃浠惰В鐮佷负UTF-8鏍煎紡 +def __decode(self, data): + """灏嗚瘝鍏告枃浠惰В鐮佷负UTF-8鏍煎紡""" + try: + data = data.decode('utf-8') + except UnicodeDecodeError: + # 濡傛灉瑙g爜澶辫触锛屾姏鍑篤alueError寮傚父骞舵彁渚涚浉搴旈敊璇秷鎭 + raise ValueError("user dict file must be utf8 format.") + return data.lstrip('\ufeff') + +# 瀹氫箟涓涓悕涓篲_parser_file鐨勭鏈夋柟娉曪紝鐢ㄤ簬瑙f瀽鐢ㄦ埛鑷畾涔夎瘝鍏告枃浠 +def __parser_file(self, file_path): + """瑙f瀽鐢ㄦ埛鑷畾涔夎瘝鍏告枃浠""" + # 妫鏌ヨ瘝鍏告枃浠舵槸鍚﹀瓨鍦 + if not os.path.exists(file_path): + # 濡傛灉璇嶅吀鏂囦欢涓嶅瓨鍦紝鎶涘嚭ValueError寮傚父骞舵彁渚涚浉搴旈敊璇秷鎭 + raise ValueError("user dict file {} is not exist.".format(file_path)) + + # 鑾峰彇璇嶅吀鏂囦欢鐨勭粷瀵硅矾寰 + real_file_path = os.path.realpath(file_path) + + # 鎵撳紑璇嶅吀鏂囦欢浠ヨ繘琛岃鍙 + file_dict = open(real_file_path) + + # 浣跨敤姝e垯琛ㄨ揪寮忓畾涔夋暟鎹尮閰嶆ā寮忥紝鍖归厤璇嶈鍜岄鐜 + data_re = re.compile('^\\s*([^\\s*]+?)\\s*([0-9]+)?\\s*$', re.U) + + # 鍒濆鍖栬瘝璇垪琛 + words_list = [] + + # 閫愯閬嶅巻璇嶅吀鏂囦欢 + for item in file_dict: + data = item.strip() + if not isinstance(data, str): + # 瑙g爜鏁版嵁涓篣TF-8鏍煎紡 + data = self.__decode(data) + # 浣跨敤姝e垯琛ㄨ揪寮忓尮閰嶆暟鎹 + tmp = data_re.match(data) + if not tmp: + continue + # 鑾峰彇鍖归厤鐨勮瘝璇拰棰戠巼锛屾坊鍔犲埌璇嶈鍒楄〃涓 + words = tmp.groups() + words_list.append(words) + + # 鍏抽棴璇嶅吀鏂囦欢 + file_dict.close() + + # 杩斿洖璇嶈鍒楄〃 + return words_list + + + +class Lookup(TextTensorOperation): + """ + Look up a word into an id according to the input vocabulary table. + + Args: + vocab (Vocab): A vocabulary object. + unknown_token (str, optional): Word is used for lookup. In case of the word is out of vocabulary (OOV), + the result of lookup will be replaced with unknown_token. If the unknown_token is not specified or + it is OOV, runtime error will be thrown (default=None, means no unknown_token is specified). + data_type (mindspore.dtype, optional): The data type that lookup operation maps + string to(default=mindspore.int32). + + Raises: + TypeError: If `vocab` is not of type text.Vocab. + TypeError: If `unknown_token` is not of type string. + TypeError: If `data_type` is not of type mindspore.dtype. + + Supported Platforms: + ``CPU`` + + Examples: + >>> # Load vocabulary from list + >>> vocab = text.Vocab.from_list(['娣', '鍦', '娆', '杩', '鎮']) + >>> # Use Lookup operator to map tokens to ids + >>> lookup = text.Lookup(vocab) + >>> text_file_dataset = text_file_dataset.map(operations=[lookup]) + """ + + @check_lookup + def __init__(self, vocab, unknown_token=None, data_type=mstype.int32): + super().__init__() + # 鍒濆鍖朙ookup瀵硅薄鐨勫睘鎬 + # - vocab: 璇嶆眹琛ㄥ璞★紝鐢ㄤ簬鏌ユ壘鍗曡瘝鐨勭储寮 + # - unknown_token: 鏈煡璇嶆眹鐨勬爣璁帮紝褰撴煡璇㈢殑鍗曡瘝涓嶅湪璇嶆眹琛ㄤ腑鏃朵娇鐢 + # - data_type: 鏁版嵁绫诲瀷锛岀敤浜庢寚瀹氭煡鎵炬搷浣滅殑杈撳嚭鏁版嵁绫诲瀷锛岄粯璁や负32浣嶆暣鏁帮紙mstype.int32锛 + self.vocab = vocab + self.unknown_token = unknown_token + self.data_type = data_type + + def parse(self): + # 杩斿洖涓涓猚de.LookupOperation瀵硅薄锛岃瀵硅薄浣跨敤浠ヤ笅鍙傛暟杩涜鍒濆鍖栵細 + # - self.vocab.c_vocab: 璇嶆眹琛ㄥ璞$殑C API琛ㄧず + # - self.unknown_token: 鏈煡璇嶆眹鐨勬爣璁 + # - str(mstype_to_detype(self.data_type)): 杈撳嚭鏁版嵁绫诲瀷鐨勫瓧绗︿覆琛ㄧず + return cde.LookupOperation(self.vocab.c_vocab, self.unknown_token, str(mstype_to_detype(self.data_type))) + + + +class Ngram(TextTensorOperation): + """ + Generate n-gram from a 1-D string Tensor. + 浠庝竴缁村瓧绗︿覆寮犻噺鐢熸垚n-gram銆 + Refer to https://en.wikipedia.org/wiki/N-gram#Examples for an overview of what n-gram is and how it works. + + Args: + n (list[int]): n in n-gram, which is a list of positive integers. For example, if n=[4, 3], then the result + would be a 4-gram followed by a 3-gram in the same tensor. If the number of words is not enough to make up + for a n-gram, an empty string will be returned. For example, 3 grams on ["mindspore", "best"] will result in + an empty string produced. + left_pad (tuple, optional): Padding performed on left side of the sequence shaped like ("pad_token", pad_width). + `pad_width` will be capped at n-1. For example, specifying left_pad=("_", 2) would pad left side of the + sequence with "__" (default=("", 0)). + right_pad (tuple, optional): Padding performed on right side of the sequence shaped like + ("pad_token", pad_width). `pad_width` will be capped at n-1. For example, specifying right_pad=("_", 2) + would pad right side of the sequence with "__" (default=("", 0)). + separator (str, optional): Symbol used to join strings together. For example, if 2-gram is + ["mindspore", "amazing"] with separator="-", the result would be ["mindspore-amazing"] + (default=" ", which will use whitespace as separator). + + Raises: + TypeError: If values of `n` not positive is not of type int. + ValueError: If values of `n` not positive. + ValueError: If `left_pad` is not a tuple of length 2. + ValueError: If `right_pad` is not a tuple of length 2. + TypeError: If `separator` is not of type string. + + Supported Platforms: + ``CPU`` + + Examples: + >>> ngram_op = text.Ngram(3, separator="-") + >>> output = ngram_op(["WildRose Country", "Canada's Ocean Playground", "Land of Living Skies"]) + >>> # output + >>> # ["WildRose Country-Canada's Ocean Playground-Land of Living Skies"] + >>> # same ngram_op called through map + >>> text_file_dataset = text_file_dataset.map(operations=ngram_op) + """ + + @check_ngram + def __init__(self, n, left_pad=("", 0), right_pad=("", 0), separator=" "): + super().__init__() + self.ngrams = n + self.left_pad = left_pad + self.right_pad = right_pad + self.separator = separator + + # 瀹氫箟涓涓悕涓簆arse鐨勬柟娉 +def parse(self): + # 杩斿洖涓涓猚de.NgramOperation瀵硅薄锛岃瀵硅薄浣跨敤浠ヤ笅鍙傛暟杩涜鍒濆鍖栵細 + # - self.ngrams: N-gram鐨刵鍊硷紝鐢ㄤ簬鎸囧畾瑕佺敓鎴愮殑N-gram鐨勯暱搴 + # - self.left_pad: 鏄惁鍦ㄦ枃鏈乏渚ц繘琛屽~鍏 + # - self.right_pad: 鏄惁鍦ㄦ枃鏈彸渚ц繘琛屽~鍏 + # - self.separator: 鐢ㄤ簬鍒嗛殧N-gram涓殑鍗曡瘝鐨勫垎闅旂 + return cde.NgramOperation(self.ngrams, self.left_pad, self.right_pad, self.separator) + + + +class PythonTokenizer: + """ + Class that applies user-defined string tokenizer into input string. + 璇ョ被灏嗙敤鎴峰畾涔夌殑瀛楃涓叉爣璁板寲鍣ㄥ簲鐢ㄤ簬杈撳叆瀛楃涓层 + Args: + tokenizer (Callable): Python function that takes a `str` and returns a list of `str` as tokens. + + Raises: + TypeError: If `tokenizer` is not a callable Python function. + + Supported Platforms: + ``CPU`` + + Examples: + >>> def my_tokenizer(line): + ... return line.split() + >>> text_file_dataset = text_file_dataset.map(operations=text.PythonTokenizer(my_tokenizer)) + """ + + @check_python_tokenizer + def __init__(self, tokenizer): + self.pyfunc = tokenizer + self.tokenizer = np.vectorize(lambda x: np.array(tokenizer(x), dtype='U'), signature='()->(n)') + self.random = False + + # 瀹氫箟涓涓悕涓篲_call__鐨勬柟娉 +def __call__(self, in_array): + # 妫鏌ヨ緭鍏ユ槸鍚︿负NumPy鏁扮粍 + if not isinstance(in_array, np.ndarray): + # 濡傛灉杈撳叆涓嶆槸NumPy鏁扮粍锛屾姏鍑篢ypeError寮傚父骞舵彁渚涚浉搴旈敊璇秷鎭 + raise TypeError("input should be a NumPy array. Got {}.".format(type(in_array))) + + # 濡傛灉杈撳叆鏁扮粍鐨勬暟鎹被鍨嬫槸np.bytes_锛屽皢鍏惰浆鎹负瀛楃涓 + if in_array.dtype.type is np.bytes_: + in_array = to_str(in_array) + + try: + # 浣跨敤Tokenizer瀵硅薄瀵硅緭鍏ユ暟缁勮繘琛屽鐞嗭紝灏嗗叾杞崲涓簍okens + tokens = self.tokenizer(in_array) + except Exception as e: + # 濡傛灉鍦ㄥ鐞嗚繃绋嬩腑鍑虹幇寮傚父锛屾姏鍑篟untimeError寮傚父骞舵彁渚涚浉搴旈敊璇秷鎭 + raise RuntimeError("Error occurred in Pyfunc [" + str(self.pyfunc.__name__) + "], error message: " + str(e)) + + # 杩斿洖澶勭悊鍚庣殑tokens + return tokens + +# 瀹氫箟涓涓悕涓簍o_json鐨勬柟娉曪紝鐢ㄤ簬灏嗘搷浣滀俊鎭浆鎹负JSON鏍煎紡 +def to_json(self): + json_obj = {} + # 瀛樺偍鎿嶄綔鍚嶇О + json_obj["tensor_op_name"] = self.pyfunc.__name__ + # 瀛樺偍Python妯″潡淇℃伅 + json_obj["python_module"] = self.__class__.__module__ + # 灏嗘搷浣滀俊鎭浆鎹负JSON瀛楃涓插苟杩斿洖 + return json.dumps(json_obj) + + + +class SentencePieceTokenizer(TextTensorOperation): + """ + Tokenize scalar token or 1-D tokens to tokens by sentencepiece. + 閫氳繃鍙ュ瓙鐗囨灏嗘爣閲忔爣璁版垨1-D鏍囪鏍囪涓烘爣璁般 + Args: + mode (Union[str, SentencePieceVocab]): SentencePiece model. + If the input parameter is a file, it represents the path of SentencePiece mode to be loaded. + If the input parameter is a SentencePieceVocab object, it should be constructed in advanced. + out_type (SPieceTokenizerOutType): The type of output, it can be any of [SPieceTokenizerOutType.STRING, + SPieceTokenizerOutType.INT]. + + - SPieceTokenizerOutType.STRING, means output type of SentencePice Tokenizer is string. + - SPieceTokenizerOutType.INT, means output type of SentencePice Tokenizer is int. + + Raises: + TypeError: If `mode` is not of type string or SentencePieceVocab. + TypeError: If `out_type` is not of type SPieceTokenizerOutType. + + Supported Platforms: + ``CPU`` + + Examples: + >>> from mindspore.dataset.text import SentencePieceModel, SPieceTokenizerOutType + >>> sentence_piece_vocab_file = "/path/to/sentence/piece/vocab/file" + >>> vocab = text.SentencePieceVocab.from_file([sentence_piece_vocab_file], 5000, 0.9995, + ... SentencePieceModel.UNIGRAM, {}) + >>> tokenizer = text.SentencePieceTokenizer(vocab, out_type=SPieceTokenizerOutType.STRING) + >>> text_file_dataset = text_file_dataset.map(operations=tokenizer) + """ + + @check_sentence_piece_tokenizer + def __init__(self, mode, out_type): + super().__init__() + self.mode = mode + self.out_type = out_type + + # 瀹氫箟涓涓悕涓簆arse鐨勬柟娉 +def parse(self): + # 妫鏌elf.mode鏄惁鏄疭entencePieceVocab绫诲瀷鐨勫璞 + if isinstance(self.mode, SentencePieceVocab): + # 濡傛灉鏄疭entencePieceVocab绫诲瀷鐨勫璞★紝灏唖elf.mode璁剧疆涓篶_sentence_piece_vocab + self.mode = self.mode.c_sentence_piece_vocab + + # 杩斿洖涓涓猚de.SentencePieceTokenizerOperation瀵硅薄锛岃瀵硅薄浣跨敤浠ヤ笅鍙傛暟杩涜鍒濆鍖栵細 + # - self.mode: SentencePiece妯″紡锛岀敤浜庢寚瀹氬垎璇嶇殑鏂瑰紡 + # - DE_C_INTER_SENTENCEPIECE_OUTTYPE.get(self.out_type): 浣跨敤self.out_type浣滀负閿粠DE_C_INTER_SENTENCEPIECE_OUTTYPE瀛楀吀涓幏鍙栧搴旂殑鍊硷紝 + # 鐢ㄤ簬鎸囧畾杈撳嚭绫诲瀷 + return cde.SentencePieceTokenizerOperation(self.mode, DE_C_INTER_SENTENCEPIECE_OUTTYPE.get(self.out_type)) + + + +class SlidingWindow(TextTensorOperation): + """ + Construct a tensor from given data (only support 1-D for now), where each element in the dimension axis + is a slice of data starting at the corresponding position, with a specified width. + 鏍规嵁缁欏畾鐨勬暟鎹瀯閫犲紶閲忥紙鐩墠浠呮敮鎸1-D锛夛紝鍏朵腑缁村害杞翠腑鐨勬瘡涓厓绱犳槸浠庣浉搴斾綅缃紑濮嬬殑鍏锋湁鎸囧畾瀹藉害鐨勬暟鎹垏鐗囥 + Args: + width (int): The width of the window. It must be an integer and greater than zero. + axis (int, optional): The axis along which the sliding window is computed (default=0). + + Raises: + TypeError: If `width` is not of type int. + ValueError: If value of `width` is not positive. + TypeError: If `axis` is not of type int. + + Supported Platforms: + ``CPU`` + + Examples: + >>> dataset = ds.NumpySlicesDataset(data=[[1, 2, 3, 4, 5]], column_names="col1") + >>> # Data before + >>> # | col1 | + >>> # +--------------+ + >>> # | [[1, 2, 3, 4, 5]] | + >>> # +--------------+ + >>> dataset = dataset.map(operations=text.SlidingWindow(3, 0)) + >>> # Data after + >>> # | col1 | + >>> # +--------------+ + >>> # | [[1, 2, 3], | + >>> # | [2, 3, 4], | + >>> # | [3, 4, 5]] | + >>> # +--------------+ + """ + + @check_slidingwindow + def __init__(self, width, axis=0): + super().__init__() + self.width = width + self.axis = axis + + def parse(self): + return cde.SlidingWindowOperation(self.width, self.axis) + + +class ToNumber(TextTensorOperation): + """ + Tensor operation to convert every element of a string tensor to a number. + 寮犻噺杩愮畻锛屽皢瀛楃涓插紶閲忕殑姣忎釜鍏冪礌杞崲涓轰竴涓暟瀛椼 + Strings are cast according to the rules specified in the following links, except that any strings which represent + negative numbers cannot be cast to an unsigned integer type, rules links are as follows: + https://en.cppreference.com/w/cpp/string/basic_string/stof, + https://en.cppreference.com/w/cpp/string/basic_string/stoul, + + Args: + data_type (mindspore.dtype): Type to be cast to. Must be a numeric type in mindspore.dtype. + + Raises: + TypeError: If `data_type` is not of type mindspore.dtype. + RuntimeError: If strings are invalid to cast, or are out of range after being cast. + + Supported Platforms: + ``CPU`` + + Examples: + >>> from mindspore import dtype as mstype + >>> data = [["1", "2", "3"]] + >>> dataset = ds.NumpySlicesDataset(data) + >>> to_number_op = text.ToNumber(mstype.int8) + >>> dataset = dataset.map(operations=to_number_op) + """ + + @check_to_number + def __init__(self, data_type): + super().__init__() + data_type = mstype_to_detype(data_type) + self.data_type = str(data_type) + + # 瀹氫箟涓涓悕涓簆arse鐨勬柟娉 +def parse(self): + # 杩斿洖涓涓猚de.ToNumberOperation瀵硅薄锛岃瀵硅薄浣跨敤浠ヤ笅鍙傛暟杩涜鍒濆鍖栵細 + # - self.data_type: 鏁版嵁绫诲瀷锛岀敤浜庢寚瀹氳灏嗘暟鎹浆鎹负鐨勬暟瀛楃被鍨 + return cde.ToNumberOperation(self.data_type) + + + +class ToVectors(TextTensorOperation): + """ + Look up a token into vectors according to the input vector table. + 鏍规嵁杈撳叆鍚戦噺琛紝灏嗕护鐗屾煡鎵句负鍚戦噺銆 + Args: + vectors (Vectors): A vectors object. + unk_init (sequence, optional): Sequence used to initialize out-of-vectors (OOV) token + (default=None, initialize with zero vectors). + lower_case_backup (bool, optional): Whether to look up the token in the lower case. If False, each token in the + original case will be looked up; if True, each token in the original case will be looked up first, if not + found in the keys of the property stoi, the token in the lower case will be looked up (default=False). + + Raises: + TypeError: If `unk_init` is not of type sequence. + TypeError: If elements of `unk_init` is not of type float or int. + TypeError: If `lower_case_backup` is not of type bool. + + Supported Platforms: + ``CPU`` + + Examples: + >>> # Load vectors from file + >>> vectors = text.Vectors.from_file("/path/to/vectors/file") + >>> # Use ToVectors operator to map tokens to vectors + >>> to_vectors = text.ToVectors(vectors) + >>> text_file_dataset = text_file_dataset.map(operations=[to_vectors]) + """ + + @check_to_vectors + def __init__(self, vectors, unk_init=None, lower_case_backup=False): + super().__init__() + self.vectors = vectors + self.unk_init = unk_init if unk_init is not None else [] + self.lower_case_backup = lower_case_backup + + # 瀹氫箟涓涓悕涓簆arse鐨勬柟娉 +def parse(self): + # 杩斿洖涓涓猚de.ToVectorsOperation瀵硅薄锛岃瀵硅薄浣跨敤浠ヤ笅鍙傛暟杩涜鍒濆鍖栵細 + # - self.vectors: 鐢ㄤ簬灏嗘枃鏈浆鎹负鍚戦噺鐨勯璁粌宓屽叆鍚戦噺 + # - self.unk_init: 鏈煡鍗曡瘝鐨勫垵濮嬪寲鏂规硶锛岀敤浜庡鐞嗘湭鐭ョ殑鍗曡瘝 + # - self.lower_case_backup: 鏄惁浣跨敤灏忓啓澶囦唤锛岀敤浜庡鐞嗘湭鐭ュ崟璇嶇殑鎯呭喌 + return cde.ToVectorsOperation(self.vectors, self.unk_init, self.lower_case_backup) + + + +class TruncateSequencePair(TextTensorOperation): + """ + Truncate a pair of rank-1 tensors such that the total length is less than max_length. + + This operation takes two input tensors and returns two output Tensors. + 鎴柇涓瀵圭З涓1鐨勫紶閲忥紝浣垮叾鎬婚暱搴﹀皬浜巑ax_length銆 + 姝ゆ搷浣滆幏鍙栦袱涓緭鍏ュ紶閲忓苟杩斿洖涓や釜杈撳嚭寮犻噺銆 + Args: + max_length (int): Maximum length required. + + Raises: + TypeError: If `max_length` is not of type int. + + Supported Platforms: + ``CPU`` + + Examples: + >>> dataset = ds.NumpySlicesDataset(data={"col1": [[1, 2, 3]], "col2": [[4, 5]]}) + >>> # Data before + >>> # | col1 | col2 | + >>> # +-----------+-----------| + >>> # | [1, 2, 3] | [4, 5] | + >>> # +-----------+-----------+ + >>> truncate_sequence_pair_op = text.TruncateSequencePair(max_length=4) + >>> dataset = dataset.map(operations=truncate_sequence_pair_op) + >>> # Data after + >>> # | col1 | col2 | + >>> # +-----------+-----------+ + >>> # | [1, 2] | [4, 5] | + >>> # +-----------+-----------+ + """ + + @check_pair_truncate + def __init__(self, max_length): + super().__init__() + self.max_length = max_length + + def parse(self): + return cde.TruncateSequencePairOperation(self.max_length) + + +class UnicodeCharTokenizer(TextTensorOperation): + """ + Tokenize a scalar tensor of UTF-8 string to Unicode characters. + 灏哢TF-8瀛楃涓茬殑鏍囬噺寮犻噺鏍囪涓篣nicode瀛楃銆 + Args: + with_offsets (bool, optional): Whether or not output offsets of tokens (default=False). + + Raises: + TypeError: If `with_offsets` is not of type bool. + + Supported Platforms: + ``CPU`` + + Examples: + >>> # If with_offsets=False, default output one column {["text", dtype=str]} + >>> tokenizer_op = text.UnicodeCharTokenizer(with_offsets=False) + >>> text_file_dataset = text_file_dataset.map(operations=tokenizer_op) + >>> # If with_offsets=True, then output three columns {["token", dtype=str], ["offsets_start", dtype=uint32], + >>> # ["offsets_limit", dtype=uint32]} + >>> tokenizer_op = text.UnicodeCharTokenizer(with_offsets=True) + >>> text_file_dataset = text_file_dataset.map(operations=tokenizer_op, input_columns=["text"], + ... output_columns=["token", "offsets_start", "offsets_limit"], + ... column_order=["token", "offsets_start", "offsets_limit"]) + """ + + @check_with_offsets + def __init__(self, with_offsets=False): + super().__init__() + self.with_offsets = with_offsets + + # 瀹氫箟涓涓悕涓簆arse鐨勬柟娉 +def parse(self): + # 杩斿洖涓涓猚de.UnicodeCharTokenizerOperation瀵硅薄锛岃瀵硅薄浣跨敤浠ヤ笅鍙傛暟杩涜鍒濆鍖栵細 + # - self.with_offsets: 鏄惁杩斿洖瀛楃鍋忕Щ淇℃伅锛岀敤浜庢寚瀹氭槸鍚﹀湪鍒嗚瘝缁撴灉涓寘鍚瓧绗︾殑鍋忕Щ淇℃伅 + return cde.UnicodeCharTokenizerOperation(self.with_offsets) + + + +class WordpieceTokenizer(TextTensorOperation): + """ + Tokenize the input text to subword tokens. + 灏嗚緭鍏ユ枃鏈爣璁颁负瀛愬崟璇嶆爣璁般 + Args: + vocab (Vocab): Vocabulary used to look up words. + suffix_indicator (str, optional): Prefix flags used to indicate subword suffixes. Default: '##'. + max_bytes_per_token (int, optional): The maximum length of tokenization, words exceeding this length will + not be split. Default: 100. + unknown_token (str, optional): The output for unknown words. When set to an empty string, the corresponding + unknown word will be directly returned as the output. Otherwise, the set string will be returned as the + output. Default: '[UNK]'. + with_offsets (bool, optional): Whether to return the offsets of tokens. Default: False. + + Raises: + TypeError: If `vocab` is not of type :class:`mindspore.dataset.text.Vocab`. + TypeError: If `suffix_indicator` is not of type str. + TypeError: If `max_bytes_per_token` is not of type int. + TypeError: If `unknown_token` is not of type str. + TypeError: If `with_offsets` is not of type bool. + ValueError: If `max_bytes_per_token` is negative. + + Supported Platforms: + ``CPU`` + + Examples: + >>> vocab_list = ["book", "cholera", "era", "favor", "##ite", "my", "is", "love", "dur", "##ing", "the"] + >>> vocab = text.Vocab.from_list(vocab_list) + >>> # If with_offsets=False, default output one column {["text", dtype=str]} + >>> tokenizer_op = text.WordpieceTokenizer(vocab=vocab, unknown_token='[UNK]', + ... max_bytes_per_token=100, with_offsets=False) + >>> text_file_dataset = text_file_dataset.map(operations=tokenizer_op) + >>> # If with_offsets=True, then output three columns {["token", dtype=str], ["offsets_start", dtype=uint32], + >>> # ["offsets_limit", dtype=uint32]} + >>> tokenizer_op = text.WordpieceTokenizer(vocab=vocab, unknown_token='[UNK]', + ... max_bytes_per_token=100, with_offsets=True) + >>> text_file_dataset = text_file_dataset.map(operations=tokenizer_op, input_columns=["text"], + ... output_columns=["token", "offsets_start", "offsets_limit"], + ... column_order=["token", "offsets_start", "offsets_limit"]) + """ + + @check_wordpiece_tokenizer + def __init__(self, vocab, suffix_indicator='##', max_bytes_per_token=100, unknown_token='[UNK]', + with_offsets=False): + super().__init__() + self.vocab = vocab + self.suffix_indicator = suffix_indicator + self.max_bytes_per_token = max_bytes_per_token + self.unknown_token = unknown_token + self.with_offsets = with_offsets + + # 瀹氫箟涓涓悕涓簆arse鐨勬柟娉 +def parse(self): + # 杩斿洖涓涓猚de.WordpieceTokenizerOperation瀵硅薄锛岃瀵硅薄浣跨敤浠ヤ笅鍙傛暟杩涜鍒濆鍖栵細 + # - self.vocab.c_vocab: 璇嶆眹琛ㄥ璞$殑C API琛ㄧず + # - self.suffix_indicator: 鐢ㄤ簬琛ㄧず瀛愯瘝鍚庣紑鐨勬爣璇嗙 + # - self.max_bytes_per_token: 姣忎釜瀛愯瘝鐨勬渶澶у瓧鑺傛暟 + # - self.unknown_token: 鏈煡瀛愯瘝鐨勬爣璁 + # - self.with_offsets: 鏄惁杩斿洖瀛愯瘝鐨勫亸绉讳俊鎭紝鐢ㄤ簬鎸囧畾鏄惁鍦ㄥ垎璇嶇粨鏋滀腑鍖呭惈瀛愯瘝鐨勫亸绉讳俊鎭 + return cde.WordpieceTokenizerOperation(self.vocab.c_vocab, self.suffix_indicator, self.max_bytes_per_token, + self.unknown_token, self.with_offsets) +if platform.system().lower() != 'windows': + DE_C_INTER_NORMALIZE_FORM = { + NormalizeForm.NONE: cde.NormalizeForm.DE_NORMALIZE_NONE, + NormalizeForm.NFC: cde.NormalizeForm.DE_NORMALIZE_NFC, + NormalizeForm.NFKC: cde.NormalizeForm.DE_NORMALIZE_NFKC, + NormalizeForm.NFD: cde.NormalizeForm.DE_NORMALIZE_NFD, + NormalizeForm.NFKD: cde.NormalizeForm.DE_NORMALIZE_NFKD + } + + + + class BasicTokenizer(TextTensorOperation): + """ + Tokenize the input UTF-8 encoded string by specific rules. + + Note: + `BasicTokenizer` is not supported on Windows platform yet. + + Args: + lower_case (bool, optional): Whether to perform lowercase processing on the text. If True, will fold the + text to lower case and strip accented characters. If False, will only perform normalization on the + text, with mode specified by `normalization_form`. Default: False. + keep_whitespace (bool, optional): If True, the whitespace will be kept in the output. Default: False. + normalization_form (NormalizeForm, optional): + `Unicode normalization forms `_, only valid when `lower_case` + is False, can be NormalizeForm.NONE, NormalizeForm.NFC, NormalizeForm.NFKC, NormalizeForm.NFD or + NormalizeForm.NFKD. Default: NormalizeForm.NONE. + + - NormalizeForm.NONE, no normalization. + - NormalizeForm.NFC, Canonical Decomposition, followed by Canonical Composition. + - NormalizeForm.NFKC, Compatibility Decomposition, followed by Canonical Composition. + - NormalizeForm.NFD, Canonical Decomposition. + - NormalizeForm.NFKD, Compatibility Decomposition. + + preserve_unused_token (bool, optional): Whether to preserve special tokens. If True, will not split special + tokens like '[CLS]', '[SEP]', '[UNK]', '[PAD]', '[MASK]'. Default: True. + with_offsets (bool, optional): Whether to return the offsets of tokens. Default: False. + + Raises: + TypeError: If `lower_case` is not of type bool. + TypeError: If `keep_whitespace` is not of type bool. + TypeError: If `normalization_form` is not of type :class:`mindspore.dataset.text.NormalizeForm`. + TypeError: If `preserve_unused_token` is not of type bool. + TypeError: If `with_offsets` is not of type bool. + RuntimeError: If dtype of input Tensor is not str. + + Supported Platforms: + ``CPU`` + + Examples: + >>> from mindspore.dataset.text import NormalizeForm + >>> + >>> # If with_offsets=False, default output one column {["text", dtype=str]} + >>> tokenizer_op = text.BasicTokenizer(lower_case=False, + ... keep_whitespace=False, + ... normalization_form=NormalizeForm.NONE, + ... preserve_unused_token=True, + ... with_offsets=False) + >>> text_file_dataset = text_file_dataset.map(operations=tokenizer_op) + >>> # If with_offsets=True, then output three columns {["token", dtype=str], + >>> # ["offsets_start", dtype=uint32], + >>> # ["offsets_limit", dtype=uint32]} + >>> tokenizer_op = text.BasicTokenizer(lower_case=False, + ... keep_whitespace=False, + ... normalization_form=NormalizeForm.NONE, + ... preserve_unused_token=True, + ... with_offsets=True) + >>> text_file_dataset_1 = text_file_dataset_1.map(operations=tokenizer_op, input_columns=["text"], + ... output_columns=["token", "offsets_start", + ... "offsets_limit"], + ... column_order=["token", "offsets_start", + ... "offsets_limit"]) + """ + + @check_basic_tokenizer + def __init__(self, lower_case=False, keep_whitespace=False, normalization_form=NormalizeForm.NONE, + preserve_unused_token=True, with_offsets=False): + super().__init__() + if not isinstance(normalization_form, NormalizeForm): + raise TypeError("Wrong input type for normalization_form, should be enum of 'NormalizeForm'.") + + self.lower_case = lower_case + self.keep_whitespace = keep_whitespace + self.normalization_form = DE_C_INTER_NORMALIZE_FORM.get(normalization_form) + self.preserve_unused_token = preserve_unused_token + self.with_offsets = with_offsets + + def parse(self): + return cde.BasicTokenizerOperation(self.lower_case, self.keep_whitespace, self.normalization_form, + self.preserve_unused_token, self.with_offsets) + + + class BertTokenizer(TextTensorOperation): + """ + Tokenizer used for Bert text process. + + Note: + `BertTokenizer` is not supported on Windows platform yet. + + Args: + vocab (Vocab): Vocabulary used to look up words. + suffix_indicator (str, optional): Prefix flags used to indicate subword suffixes. Default: '##'. + max_bytes_per_token (int, optional): The maximum length of tokenization, words exceeding this length will + not be split. Default: 100. + unknown_token (str, optional): The output for unknown words. When set to an empty string, the corresponding + unknown word will be directly returned as the output. Otherwise, the set string will be returned as the + output. Default: '[UNK]'. + lower_case (bool, optional): Whether to perform lowercase processing on the text. If True, will fold the + text to lower case and strip accented characters. If False, will only perform normalization on the + text, with mode specified by `normalization_form`. Default: False. + keep_whitespace (bool, optional): If True, the whitespace will be kept in the output. Default: False. + normalization_form (NormalizeForm, optional): + `Unicode normalization forms `_, only valid when `lower_case` + is False, can be NormalizeForm.NONE, NormalizeForm.NFC, NormalizeForm.NFKC, NormalizeForm.NFD or + NormalizeForm.NFKD. Default: NormalizeForm.NONE. + + - NormalizeForm.NONE, no normalization. + - NormalizeForm.NFC, Canonical Decomposition, followed by Canonical Composition. + - NormalizeForm.NFKC, Compatibility Decomposition, followed by Canonical Composition. + - NormalizeForm.NFD, Canonical Decomposition. + - NormalizeForm.NFKD, Compatibility Decomposition. + + preserve_unused_token (bool, optional): Whether to preserve special tokens. If True, will not split special + tokens like '[CLS]', '[SEP]', '[UNK]', '[PAD]', '[MASK]'. Default: True. + with_offsets (bool, optional): Whether to return the offsets of tokens. Default: False. + + Raises: + TypeError: If `vocab` is not of type :class:`mindspore.dataset.text.Vocab`. + TypeError: If `suffix_indicator` is not of type str. + TypeError: If `max_bytes_per_token` is not of type int. + ValueError: If `max_bytes_per_token` is negative. + TypeError: If `unknown_token` is not of type str. + TypeError: If `lower_case` is not of type bool. + TypeError: If `keep_whitespace` is not of type bool. + TypeError: If `normalization_form` is not of type :class:`mindspore.dataset.text.NormalizeForm`. + TypeError: If `preserve_unused_token` is not of type bool. + TypeError: If `with_offsets` is not of type bool. + + Supported Platforms: + ``CPU`` + + Examples: + >>> from mindspore.dataset.text import NormalizeForm + >>> + >>> # If with_offsets=False, default output one column {["text", dtype=str]} + >>> vocab_list = ["搴", "鍓", "鏄", "鏈", "鍏", "鐤", "鏄", "鍦", "涓", "闇", "涓", "澶", "鏈", "浣", + ... "鎬", "鏁", "涔","绻", "楂", "瀛", "鍢", "鍝", "澶", "绗", "鍢", "i", "am", "mak", + ... "make", "small", "mistake", "##s", "during", "work", "##ing", "hour", "馃榾", "馃槂", + ... "馃槃", "馃榿", "+", "/", "-", "=", "12", "28", "40", "16", " ", "I", "[CLS]", "[SEP]", + ... "[UNK]", "[PAD]", "[MASK]", "[unused1]", "[unused10]"] + >>> vocab = text.Vocab.from_list(vocab_list) + >>> tokenizer_op = text.BertTokenizer(vocab=vocab, suffix_indicator='##', max_bytes_per_token=100, + ... unknown_token='[UNK]', lower_case=False, keep_whitespace=False, + ... normalization_form=NormalizeForm.NONE, preserve_unused_token=True, + ... with_offsets=False) + >>> text_file_dataset = text_file_dataset.map(operations=tokenizer_op) + >>> # If with_offsets=True, then output three columns {["token", dtype=str], + >>> # ["offsets_start", dtype=uint32], + >>> # ["offsets_limit", dtype=uint32]} + >>> tokenizer_op = text.BertTokenizer(vocab=vocab, suffix_indicator='##', max_bytes_per_token=100, + ... unknown_token='[UNK]', lower_case=False, keep_whitespace=False, + ... normalization_form=NormalizeForm.NONE, preserve_unused_token=True, + ... with_offsets=True) + >>> text_file_dataset_1 = text_file_dataset_1.map(operations=tokenizer_op, input_columns=["text"], + ... output_columns=["token", "offsets_start", + ... "offsets_limit"], + ... column_order=["token", "offsets_start", + ... "offsets_limit"]) + """ + + @check_bert_tokenizer + def __init__(self, vocab, suffix_indicator='##', max_bytes_per_token=100, unknown_token='[UNK]', + lower_case=False, keep_whitespace=False, normalization_form=NormalizeForm.NONE, + preserve_unused_token=True, with_offsets=False): + super().__init__() + if not isinstance(normalization_form, NormalizeForm): + raise TypeError("Wrong input type for normalization_form, should be enum of 'NormalizeForm'.") + + self.vocab = vocab + self.suffix_indicator = suffix_indicator + self.max_bytes_per_token = max_bytes_per_token + self.unknown_token = unknown_token + self.lower_case = lower_case + self.keep_whitespace = keep_whitespace + self.normalization_form = DE_C_INTER_NORMALIZE_FORM.get(normalization_form) + self.preserve_unused_token = preserve_unused_token + self.with_offsets = with_offsets + + def parse(self): + return cde.BertTokenizerOperation(self.vocab.c_vocab, self.suffix_indicator, self.max_bytes_per_token, + self.unknown_token, self.lower_case, self.keep_whitespace, + self.normalization_form, self.preserve_unused_token, self.with_offsets) + + + class CaseFold(TextTensorOperation): + """ + Apply case fold operation on UTF-8 string tensor, which is aggressive that can convert more characters into + lower case. Supported normalization forms please refer to + `ICU_Normalizer2 `_ . + + Note: + CaseFold is not supported on Windows platform yet. + + Supported Platforms: + ``CPU`` + + Examples: + >>> case_op = text.CaseFold() + >>> text_file_dataset = text_file_dataset.map(operations=case_op) + """ + + def parse(self): + return cde.CaseFoldOperation() + + + class FilterWikipediaXML(TextTensorOperation): + """ + Filter Wikipedia XML dumps to "clean" text consisting only of lowercase letters (a-z, converted from A-Z), + and spaces (never consecutive). + + Note: + FilterWikipediaXML is not supported on Windows platform yet. + + Supported Platforms: + ``CPU`` + + Examples: + >>> import mindspore.dataset.text.transforms as text + >>> + >>> replace_op = text.FilterWikipediaXML() + >>> text_file_dataset = text_file_dataset.map(operations=replace_op) + """ + + def parse(self): + return cde.FilterWikipediaXMLOperation() + + + class NormalizeUTF8(TextTensorOperation): + """ + Apply normalize operation on UTF-8 string tensor. + + Note: + NormalizeUTF8 is not supported on Windows platform yet. + + Args: + normalize_form (NormalizeForm, optional): Valid values can be [NormalizeForm.NONE, NormalizeForm.NFC, + NormalizeForm.NFKC, NormalizeForm.NFD, NormalizeForm.NFKD] any of the four unicode + normalized forms(default=NormalizeForm.NFKC). + See http://unicode.org/reports/tr15/ for details. + + - NormalizeForm.NONE, do nothing for input string tensor. + - NormalizeForm.NFC, normalize with Normalization Form C. + - NormalizeForm.NFKC, normalize with Normalization Form KC. + - NormalizeForm.NFD, normalize with Normalization Form D. + - NormalizeForm.NFKD, normalize with Normalization Form KD. + + Raises: + TypeError: If `normalize_form` is not of type NormalizeForm. + + Supported Platforms: + ``CPU`` + + Examples: + >>> from mindspore.dataset.text import NormalizeForm + >>> normalize_op = text.NormalizeUTF8(normalize_form=NormalizeForm.NFC) + >>> text_file_dataset = text_file_dataset.map(operations=normalize_op) + """ + + def __init__(self, normalize_form=NormalizeForm.NFKC): + super().__init__() + if not isinstance(normalize_form, NormalizeForm): + raise TypeError("Wrong input type for normalization_form, should be enum of 'NormalizeForm'.") + + normalize_form = replace_none(normalize_form, NormalizeForm.NFKC) + self.normalize_form = DE_C_INTER_NORMALIZE_FORM.get(normalize_form) + + def parse(self): + return cde.NormalizeUTF8Operation(self.normalize_form) + + + class RegexReplace(TextTensorOperation): + """ + Replace a part of UTF-8 string tensor with given text according to regular expressions. + + See https://unicode-org.github.io/icu/userguide/strings/regexp.html for supported regex pattern. + + Note: + RegexReplace is not supported on Windows platform yet. + + Args: + pattern (str): the regex expression patterns. + replace (str): the string to replace matched element. + replace_all (bool, optional): If False, only replace first matched element; + if True, replace all matched elements (default=True). + + Raises: + TypeError: If `pattern` is not of type string. + TypeError: If `replace` is not of type string. + TypeError: If `replace_all` is not of type bool. + + Supported Platforms: + ``CPU`` + + Examples: + >>> pattern = 'Canada' + >>> replace = 'China' + >>> replace_op = text.RegexReplace(pattern, replace) + >>> text_file_dataset = text_file_dataset.map(operations=replace_op) + """ + + @check_regex_replace + def __init__(self, pattern, replace, replace_all=True): + super().__init__() + self.pattern = pattern + self.replace = replace + self.replace_all = replace_all + + def parse(self): + return cde.RegexReplaceOperation(self.pattern, self.replace, self.replace_all) + + + class RegexTokenizer(TextTensorOperation): + """ + Tokenize a scalar tensor of UTF-8 string by regex expression pattern. + + See https://unicode-org.github.io/icu/userguide/strings/regexp.html for supported regex pattern. + + Note: + RegexTokenizer is not supported on Windows platform yet. + + Args: + delim_pattern (str): The pattern of regex delimiters. + The original string will be split by matched elements. + keep_delim_pattern (str, optional): The string matched by 'delim_pattern' can be kept as a token + if it can be matched by 'keep_delim_pattern'. The default value is an empty str + which means that delimiters will not be kept as an output token (default=''). + with_offsets (bool, optional): Whether or not output offsets of tokens(default=False). + + Raises: + TypeError: If `delim_pattern` is not of type string. + TypeError: If `keep_delim_pattern` is not of type string. + TypeError: If `with_offsets` is not of type bool. + + Supported Platforms: + ``CPU`` + + Examples: + >>> # If with_offsets=False, default output is one column {["text", dtype=str]} + >>> delim_pattern = r"[ |,]" + >>> tokenizer_op = text.RegexTokenizer(delim_pattern, with_offsets=False) + >>> text_file_dataset = text_file_dataset.map(operations=tokenizer_op) + >>> # If with_offsets=True, then output three columns {["token", dtype=str], + >>> # ["offsets_start", dtype=uint32], + >>> # ["offsets_limit", dtype=uint32]} + >>> tokenizer_op = text.RegexTokenizer(delim_pattern, with_offsets=True) + >>> text_file_dataset_1 = text_file_dataset_1.map(operations=tokenizer_op, input_columns=["text"], + ... output_columns=["token", "offsets_start", + ... "offsets_limit"], + ... column_order=["token", "offsets_start", + ... "offsets_limit"]) + """ + + @check_regex_tokenizer + def __init__(self, delim_pattern, keep_delim_pattern='', with_offsets=False): + super().__init__() + self.delim_pattern = delim_pattern + self.keep_delim_pattern = keep_delim_pattern + self.with_offsets = with_offsets + + def parse(self): + return cde.RegexTokenizerOperation(self.delim_pattern, self.keep_delim_pattern, self.with_offsets) + + + class UnicodeScriptTokenizer(TextTensorOperation): + """ + Tokenize a scalar tensor of UTF-8 string based on Unicode script boundaries. + + Note: + UnicodeScriptTokenizer is not supported on Windows platform yet. + + Args: + keep_whitespace (bool, optional): Whether or not emit whitespace tokens (default=False). + with_offsets (bool, optional): Whether or not output offsets of tokens (default=False). + + Raises: + TypeError: If `keep_whitespace` is not of type bool. + TypeError: If `with_offsets` is not of type bool. + + Supported Platforms: + ``CPU`` + + Examples: + >>> # If with_offsets=False, default output one column {["text", dtype=str]} + >>> tokenizer_op = text.UnicodeScriptTokenizer(keep_whitespace=True, with_offsets=False) + >>> text_file_dataset = text_file_dataset.map(operations=tokenizer_op) + >>> # If with_offsets=True, then output three columns {["token", dtype=str], + >>> # ["offsets_start", dtype=uint32], + >>> # ["offsets_limit", dtype=uint32]} + >>> tokenizer_op = text.UnicodeScriptTokenizer(keep_whitespace=True, with_offsets=True) + >>> text_file_dataset = text_file_dataset.map(operations=tokenizer_op, input_columns=["text"], + ... output_columns=["token", "offsets_start", "offsets_limit"], + ... column_order=["token", "offsets_start", "offsets_limit"]) + + """ + + @check_unicode_script_tokenizer + def __init__(self, keep_whitespace=False, with_offsets=False): + super().__init__() + keep_whitespace = replace_none(keep_whitespace, False) + with_offsets = replace_none(with_offsets, False) + self.keep_whitespace = keep_whitespace + self.with_offsets = with_offsets + + def parse(self): + return cde.UnicodeScriptTokenizerOperation(self.keep_whitespace, self.with_offsets) + + + class WhitespaceTokenizer(TextTensorOperation): + """ + Tokenize a scalar tensor of UTF-8 string on ICU4C defined whitespaces, such as: ' ', '\\\\t', '\\\\r', '\\\\n'. + + Note: + WhitespaceTokenizer is not supported on Windows platform yet. + + Args: + with_offsets (bool, optional): Whether or not output offsets of tokens (default=False). + + Raises: + TypeError: If `with_offsets` is not of type bool. + + Supported Platforms: + ``CPU`` + + Examples: + >>> # If with_offsets=False, default output one column {["text", dtype=str]} + >>> tokenizer_op = text.WhitespaceTokenizer(with_offsets=False) + >>> text_file_dataset = text_file_dataset.map(operations=tokenizer_op) + >>> # If with_offsets=True, then output three columns {["token", dtype=str], + >>> # ["offsets_start", dtype=uint32], + >>> # ["offsets_limit", dtype=uint32]} + >>> tokenizer_op = text.WhitespaceTokenizer(with_offsets=True) + >>> text_file_dataset = text_file_dataset.map(operations=tokenizer_op, input_columns=["text"], + ... output_columns=["token", "offsets_start", "offsets_limit"], + ... column_order=["token", "offsets_start", "offsets_limit"]) + """ + + @check_with_offsets + def __init__(self, with_offsets=False): + super().__init__() + self.with_offsets = with_offsets + + def parse(self): + return cde.WhitespaceTokenizerOperation(self.with_offsets) diff --git a/mindspore/ccsrc/transform-update/transformation_ops_declare.cc b/mindspore/ccsrc/transform-update/transformation_ops_declare.cc new file mode 100644 index 00000000000..edf4a344e2e --- /dev/null +++ b/mindspore/ccsrc/transform-update/transformation_ops_declare.cc @@ -0,0 +1,133 @@ +/** + * Copyright 2019-2021 Huawei Technologies Co., Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "transform/graph_ir/op_declare/transformation_ops_declare.h" +#include + +namespace mindspore::transform { +// Flatten +// 将输入张量展平为一个1D张量 +INPUT_MAP(Flatten) = {{1, INPUT_DESC(x)}};//输入映射,Flatten有一个输入参数,它的索引为1,且该输入参数形参x +ATTR_MAP(Flatten) = EMPTY_ATTR_MAP;//属性映射,空,该操作不需要任何额外的属性参数 +OUTPUT_MAP(Flatten) = {{0, OUTPUT_DESC(y)}};//输出映射,Flatten有一个输出参数,它的索引为0,且该输入参数形参y +REG_ADPT_DESC(Flatten, prim::kPrimFlatten->name(), ADPT_DESC(Flatten)) +// 通过调用"prim::kPrimFlatten"的name()函数来获取"Flatten"操作的名称 另一个先前定义的适配器描述。该描述告诉MindSpore如何在运行时执行"Flatten"操作。 +//将操作 "Flatten"注册到适配器描述(REG_ADPT_DESC)中,以便在MindSpore深度学习框架中能够使用该操作进行图计算 +//适配器描述将此操作注册到名为 prim::kPrimFlatten->name() 的图操作 + +// Unpack +INPUT_MAP(Unpack) = {{1, INPUT_DESC(x)}}; +ATTR_MAP(Unpack) = {{"axis", ATTR_DESC(axis, AnyTraits())}, {"num", ATTR_DESC(num, AnyTraits())}}; + //具有两个属性 //axis:表示拆分的轴 //num:表示拆分后生成的张量数量 +DYN_OUTPUT_MAP(Unpack) = {{0, DYN_OUTPUT_DESC(y)}};///动态输出参数 y,表示可以输出多个张量。 +REG_ADPT_DESC(Unpack, prim::kUnstack, ADPT_DESC(Unpack)) +//适配器描述将此操作注册到名为 prim::kUnstack 的图操作 + +// ExtractImagePatches +INPUT_MAP(ExtractImagePatches) = {{1, INPUT_DESC(x)}}; +//一个输入参数x +ATTR_MAP(ExtractImagePatches) = { +//具有四个属性 + {"ksizes", ATTR_DESC(ksizes, AnyTraits(), AnyTraits>())}, + //ksizes:表示在输入数据的每个维度上滑动的窗口大小。 + {"strides", ATTR_DESC(strides, AnyTraits(), AnyTraits>())}, + //strides:表示在输入数据的每个维度上滑动的步长。 + {"rates", ATTR_DESC(rates, AnyTraits(), AnyTraits>())}, + //rates:表示在输入数据的每个维度上的dilation(扩张)率 + {"padding", ATTR_DESC(padding, AnyTraits())}}; + //padding:表示在输入数据的周围添加的填充类型。 +OUTPUT_MAP(ExtractImagePatches) = {{0, OUTPUT_DESC(y)}}; + //一个输出参数y +REG_ADPT_DESC(ExtractImagePatches, kNameExtractImagePatches, ADPT_DESC(ExtractImagePatches)) + //适配器描述将此操作注册到名为 kNameExtractImagePatches 的图操作 + +// Transpose +INPUT_MAP(TransposeD) = {{1, INPUT_DESC(x)}}; +// 一个输入参数x +INPUT_ATTR_MAP(TransposeD) = {{2, ATTR_DESC(perm, AnyTraits(), AnyTraits>())}}; +//2是属性索引,表示这是操作"TransposeD"的第二个输入属性 +// perm:属性的名称 该属性的数据类型为int64_t 该属性是一个std::vector类型的值,即一维整数数组 +ATTR_MAP(TransposeD) = EMPTY_ATTR_MAP; +// Do not set Transpose operator output descriptor +REG_ADPT_DESC(TransposeD, prim::kPrimTranspose->name(), ADPT_DESC(TransposeD)) + +// SpaceToDepth +INPUT_MAP(SpaceToDepth) = {{1, INPUT_DESC(x)}}; +ATTR_MAP(SpaceToDepth) = {{"block_size", ATTR_DESC(block_size, AnyTraits())}}; +//属性 block_size,表示空间到深度转换的块大小 +OUTPUT_MAP(SpaceToDepth) = {{0, OUTPUT_DESC(y)}}; +REG_ADPT_DESC(SpaceToDepth, kNameSpaceToDepth, ADPT_DESC(SpaceToDepth)) + +// DepthToSpace +INPUT_MAP(DepthToSpace) = {{1, INPUT_DESC(x)}}; +ATTR_MAP(DepthToSpace) = {{"block_size", ATTR_DESC(block_size, AnyTraits())}}; +//属性 block_size,表示深度到空间转换的块大小 +OUTPUT_MAP(DepthToSpace) = {{0, OUTPUT_DESC(y)}}; +//有一个输出参数 y +REG_ADPT_DESC(DepthToSpace, kNameDepthToSpace, ADPT_DESC(DepthToSpace)) +//适配器描述将此操作注册到名为 kNameDepthToSpace 的图操作 +// +// SpaceToBatchD +INPUT_MAP(SpaceToBatchD) = {{1, INPUT_DESC(x)}}; + +ATTR_MAP(SpaceToBatchD) = { + {"block_size", ATTR_DESC(block_size, AnyTraits())}, + //block_size:表示空间到批处理转换的块大小 + {"paddings", ATTR_DESC(paddings, AnyTraits>>(), AnyTraits>())}}; + //paddings:表示空间到批处理转换的填充方式 +OUTPUT_MAP(SpaceToBatchD) = {{0, OUTPUT_DESC(y)}}; +REG_ADPT_DESC(SpaceToBatchD, kNameSpaceToBatch, ADPT_DESC(SpaceToBatchD)) + +// SpaceToBatchNDD +INPUT_MAP(SpaceToBatchNDD) = {{1, INPUT_DESC(x)}}; +ATTR_MAP(SpaceToBatchNDD) = { + {"block_shape", ATTR_DESC(block_shape, AnyTraits>())}, + //block_shape:表示空间到批处理转换的块形状 + {"paddings", ATTR_DESC(paddings, AnyTraits>>(), AnyTraits>())}}; + //paddings:表示空间到批处理转换的填充方式 +OUTPUT_MAP(SpaceToBatchNDD) = {{0, OUTPUT_DESC(y)}}; +//有一个输出参数 y +REG_ADPT_DESC(SpaceToBatchNDD, kNameSpaceToBatchNDD, ADPT_DESC(SpaceToBatchNDD)) +//适配器描述将此操作注册到名为 kNameSpaceToBatchNDD 的图操作 +// +// BatchToSpaceD +INPUT_MAP(BatchToSpaceD) = {{1, INPUT_DESC(x)}}; +//该操作接受一个输入参数 x +ATTR_MAP(BatchToSpaceD) = { + {"block_size", ATTR_DESC(block_size, AnyTraits())}, + //block_size:表示批处理到空间转换的块大小 + {"crops", ATTR_DESC(crops, AnyTraits>>(), AnyTraits>())}}; + //crops:表示批处理到空间转换的裁剪方式 +OUTPUT_MAP(BatchToSpaceD) = {{0, OUTPUT_DESC(y)}}; +// 有一个输出参数 y +REG_ADPT_DESC(BatchToSpaceD, kNameBatchToSpace, ADPT_DESC(BatchToSpaceD)) +//适配器描述将此操作注册到名为 kNameBatchToSpace 的图操作 + +// BatchToSpaceNDD +INPUT_MAP(BatchToSpaceNDD) = {{1, INPUT_DESC(x)}}; +//接受一个输入参数 x +ATTR_MAP(BatchToSpaceNDD) = { + {"block_shape", ATTR_DESC(block_shape, AnyTraits>())}, + //block_shape:表示批处理到空间转换的块形状 + {"crops", ATTR_DESC(crops, AnyTraits>>(), AnyTraits>())}}; + //crops:表示批处理到空间转换的裁剪方式 +OUTPUT_MAP(BatchToSpaceNDD) = {{0, OUTPUT_DESC(y)}}; +//有一个输出参数 y +REG_ADPT_DESC(BatchToSpaceNDD, kNameBatchToSpaceNd, ADPT_DESC(BatchToSpaceNDD)) +//适配器描述将此操作注册到名为 kNameBatchToSpaceNd 的图操作 +} // namespace mindspore::transform +// 定义了一系列图操作,每个操作有不同的输入、输出和属性, +//并且将这些操作注册到相应的适配器描述中,以便后续在MindSpore深度学习框架中使用这些操作进行图计算。 \ No newline at end of file diff --git a/mindspore/ccsrc/transform-update/transforms.py b/mindspore/ccsrc/transform-update/transforms.py new file mode 100644 index 00000000000..5252119e2cd --- /dev/null +++ b/mindspore/ccsrc/transform-update/transforms.py @@ -0,0 +1,2170 @@ +# Copyright 2021-2022 Huawei Technologies Co., Ltd +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ============================================================================== +""" +The module audio.transforms is inherited from _c_dataengine and is +implemented based on C++. It's a high performance module to process +audio. Users can apply suitable augmentations on audio data to improve +their training models. +瀵归煶棰戞暟鎹簲鐢ㄩ傚綋鐨勫寮哄姛鑳戒互鏀硅繘浠栦滑鐨勮缁冩ā寮忋 +""" + +import numpy as np + +import mindspore._c_dataengine as cde +from .utils import BorderType, DensityFunction, FadeShape, GainType, Interpolation, MelType, Modulation, NormType, \ + ResampleMethod, ScaleType, WindowType +from .validators import check_allpass_biquad, check_amplitude_to_db, check_band_biquad, check_bandpass_biquad, \ + check_bandreject_biquad, check_bass_biquad, check_biquad, check_complex_norm, check_compute_deltas, \ + check_contrast, check_db_to_amplitude, check_dc_shift, check_deemph_biquad, check_detect_pitch_frequency, \ + check_dither, check_equalizer_biquad, check_fade, check_flanger, check_gain, check_griffin_lim, \ + check_highpass_biquad, check_inverse_mel_scale, check_lfilter, check_lowpass_biquad, check_magphase, \ + check_mask_along_axis, check_mask_along_axis_iid, check_masking, check_mel_scale, check_mu_law_coding, \ + check_overdrive, check_phase_vocoder, check_phaser, check_resample, check_riaa_biquad, check_sliding_window_cmn, \ + check_spectral_centroid, check_spectrogram, check_time_stretch, check_treble_biquad, check_vad, check_vol +from ..transforms.py_transforms_util import Implementation +from ..transforms.transforms import TensorOperation + + +class AudioTensorOperation(TensorOperation): + """ + Base class of Audio Tensor Ops. + 闊抽甯搁噺杩愮畻鐨勫熀绫 + """ + + def __init__(self): + super().__init__() + self.implementation = Implementation.C + + def __call__(self, *input_tensor_list): + for tensor in input_tensor_list: + if not isinstance(tensor, (np.ndarray,)): + raise TypeError("Input should be NumPy audio, got {}.".format(type(tensor))) + return super().__call__(*input_tensor_list) + + def parse(self): + raise NotImplementedError("AudioTensorOperation has to implement parse() method.") + + +class AllpassBiquad(AudioTensorOperation): + r""" + Design two-pole all-pass filter with central frequency and bandwidth for audio waveform. + + An all-pass filter changes the audio's frequency to phase relationship without changing + its frequency to amplitude relationship. The system function is: + + .. math:: + H(s) = \frac{s^2 - \frac{s}{Q} + 1}{s^2 + \frac{s}{Q} + 1} + 閽堝闊抽娉㈠舰璁捐浜嗗叿鏈変腑蹇冮鐜囧拰甯﹀鐨勫弻鏋佸叏閫氭护娉㈠櫒銆 + 鍏ㄩ氭护娉㈠櫒鍦ㄤ笉鏀瑰彉鐨勬儏鍐典笅鏀瑰彉闊抽鐨勯鐜-鐩镐綅鍏崇郴鍏堕鐜囦笌鎸箙鐨勫叧绯汇傜郴缁熷姛鑳戒负锛 + H锛坰锛=\frac锝泂^2-\frac锝泂锝濓經Q锝+1锝 + + Similar to `SoX `_ implementation. + + Note: + The dimension of the audio waveform to be processed needs to be (..., time). + + Args: + sample_rate (int): Sampling rate (in Hz), which can't be zero. + central_freq (float): Central frequency (in Hz). + Q (float, optional): `Quality factor `_ , + in range of (0, 1]. Default: 0.707. + + Raises: + TypeError: If `sample_rate` is not of type integer. + ValueError: If `sample_rate` is 0. + TypeError: If `central_freq` is not of type float. + TypeError: If `Q` is not of type float. + ValueError: If `Q` is not in range of (0, 1]. + RuntimeError: If input tensor is not in shape of <..., time>. + + Supported Platforms: + ``CPU`` + + Examples: + >>> import numpy as np + >>> + >>> waveform = np.array([[2.716064453125e-03, 6.34765625e-03], [9.246826171875e-03, 1.0894775390625e-02]]) + >>> numpy_slices_dataset = ds.NumpySlicesDataset(data=waveform, column_names=["audio"]) + >>> transforms = [audio.AllpassBiquad(44100, 200.0)] + >>> numpy_slices_dataset = numpy_slices_dataset.map(operations=transforms, input_columns=["audio"]) + """ + + @check_allpass_biquad + # 瀹氫箟涓涓被鐨勬瀯閫犲嚱鏁帮紝鐢ㄤ簬鍒濆鍖栧璞$殑灞炴 +def __init__(self, sample_rate, central_freq, Q=0.707): + super().__init__() # 璋冪敤鐖剁被鐨勬瀯閫犲嚱鏁 + self.sample_rate = sample_rate # 鍒濆鍖杝ample_rate灞炴 + self.central_freq = central_freq # 鍒濆鍖朿entral_freq灞炴 + self.quality_factor = Q # 鍒濆鍖杚uality_factor灞炴 + +# 瀹氫箟涓涓悕涓簆arse鐨勬柟娉 +def parse(self): + # 杩斿洖涓涓猚de.AllpassBiquadOperation瀵硅薄锛岃瀵硅薄浣跨敤鍒濆鍖栨椂浼犲叆鐨勫睘鎬у + return cde.AllpassBiquadOperation(self.sample_rate, self.central_freq, self.quality_factor) + +# 瀹氫箟涓涓悕涓篋E_C_SCALE_TYPE鐨勫瓧鍏革紝鍏朵腑鍖呭惈涓涓敭鍊煎 +# ScaleType.POWER瀵瑰簲cde.ScaleType.DE_SCALE_TYPE_POWER +DE_C_SCALE_TYPE = {ScaleType.POWER: cde.ScaleType.DE_SCALE_TYPE_POWER} + +class AmplitudeToDB(AudioTensorOperation): + r""" + Turn the input audio waveform from the amplitude/power scale to decibel scale. + + Note: + The dimension of the audio waveform to be processed needs to be (..., freq, time). + 灏嗚緭鍏ラ煶棰戞尝褰粠鎸箙/鍔熺巼鍒诲害杞埌鍒嗚礉鍒诲害銆 + 娉細 + 瑕佸鐞嗙殑闊抽娉㈠舰鐨勭淮搴﹂渶瑕佷负锛堚︼紝freq锛宼ime锛夈 + Args: + stype (ScaleType, optional): Scale of the input waveform, which can be + ScaleType.POWER or ScaleType.MAGNITUDE. Default: ScaleType.POWER. + ref_value (float, optional): Multiplier reference value for generating + `db_multiplier`. Default: 1.0. The formula is + + :math:`\text{db_multiplier} = Log10(max(\text{ref_value}, amin))`. + + amin (float, optional): Lower bound to clamp the input waveform, which must + be greater than zero. Default: 1e-10. + top_db (float, optional): Minimum cut-off decibels, which must be non-negative. Default: 80.0. + + Raises: + TypeError: If `stype` is not of type :class:`mindspore.dataset.audio.utils.ScaleType`. + TypeError: If `ref_value` is not of type float. + ValueError: If `ref_value` is not a positive number. + TypeError: If `amin` is not of type float. + ValueError: If `amin` is not a positive number. + TypeError: If `top_db` is not of type float. + ValueError: If `top_db` is not a positive number. + RuntimeError: If input tensor is not in shape of <..., freq, time>. + + Supported Platforms: + ``CPU`` + + Examples: + >>> import numpy as np + >>> from mindspore.dataset.audio import ScaleType + >>> + >>> waveform = np.random.random([1, 400 // 2 + 1, 30]) + >>> numpy_slices_dataset = ds.NumpySlicesDataset(data=waveform, column_names=["audio"]) + >>> transforms = [audio.AmplitudeToDB(stype=ScaleType.POWER)] + >>> numpy_slices_dataset = numpy_slices_dataset.map(operations=transforms, input_columns=["audio"]) + """ + + @check_amplitude_to_db + def __init__(self, stype=ScaleType.POWER, ref_value=1.0, amin=1e-10, top_db=80.0): + super().__init__() + self.stype = stype + self.ref_value = ref_value + self.amin = amin + self.top_db = top_db + + # 瀹氫箟涓涓悕涓簆arse鐨勬柟娉 +def parse(self): + # 杩斿洖涓涓猚de.AmplitudeToDBOperation瀵硅薄锛岃瀵硅薄浣跨敤浠ヤ笅鍙傛暟杩涜鍒濆鍖栵細 + # - DE_C_SCALE_TYPE.get(self.stype): 浣跨敤self.stype浣滀负閿粠DE_C_SCALE_TYPE瀛楀吀涓幏鍙栧搴旂殑鍊硷紝 + # 鐢ㄤ簬鎸囧畾骞呭害鍒板垎璐濊浆鎹㈢殑姣斾緥灏虹被鍨 + # - self.ref_value: 鐢ㄤ簬鎸囧畾骞呭害鍒板垎璐濊浆鎹㈢殑鍙傝冨 + # - self.amin: 鐢ㄤ簬鎸囧畾骞呭害鍒板垎璐濊浆鎹腑鐨勬渶灏忚緭鍏ュ + # - self.top_db: 鐢ㄤ簬鎸囧畾骞呭害鍒板垎璐濊浆鎹㈢殑鏈澶у垎璐濆 + return cde.AmplitudeToDBOperation(DE_C_SCALE_TYPE.get(self.stype), self.ref_value, self.amin, self.top_db) + + + +class Angle(AudioTensorOperation): + """ + Calculate the angle of complex number sequence. + 璁$畻澶嶆暟搴忓垪鐨勮搴︺ + Note: + The dimension of the audio waveform to be processed needs to be (..., complex=2). + The first dimension represents the real part while the second represents the imaginary. + 娉細瑕佸鐞嗙殑闊抽娉㈠舰鐨勭淮搴﹂渶瑕佹槸锛堚︼紝澶嶆暟=2锛夈 + 绗竴涓淮搴﹁〃绀哄疄閮紝鑰岀浜屼釜缁村害琛ㄧず铏氶儴銆 + Raises: + RuntimeError: If input tensor is not in shape of <..., complex=2>. + + Supported Platforms: + ``CPU`` + + Examples: + >>> import numpy as np + >>> + >>> waveform = np.array([[1.43, 5.434], [23.54, 89.38]]) + >>> numpy_slices_dataset = ds.NumpySlicesDataset(data=waveform, column_names=["audio"]) + >>> transforms = [audio.Angle()] + >>> numpy_slices_dataset = numpy_slices_dataset.map(operations=transforms, input_columns=["audio"]) + """ + + def parse(self): + return cde.AngleOperation() + + +class BandBiquad(AudioTensorOperation): + """ + Design two-pole band-pass filter for audio waveform. + + The frequency response drops logarithmically around the center frequency. The + bandwidth gives the slope of the drop. The frequencies at band edge will be + half of their original amplitudes. + 璁捐浜嗙敤浜庨煶棰戞尝褰㈢殑鍙屾瀬甯﹂氭护娉㈠櫒銆 + 棰戠巼鍝嶅簲鍦ㄤ腑蹇冮鐜囬檮杩戝憟瀵规暟涓嬮檷銆傝繖涓甫瀹界粰鍑轰簡涓嬮檷鐨勬枩鐜囥傞甯﹁竟缂樼殑棰戠巼涓哄叾鍘熷鎸箙鐨勪竴鍗娿 + Similar to `SoX `_ implementation. + + Note: + The dimension of the audio waveform to be processed needs to be (..., time). + + Args: + sample_rate (int): Sampling rate (in Hz), which can't be zero. + central_freq (float): Central frequency (in Hz). + Q (float, optional): `Quality factor `_ , + in range of (0, 1]. Default: 0.707. + noise (bool, optional) : If True, uses the alternate mode for un-pitched audio (e.g. percussion). + If False, uses mode oriented to pitched audio, i.e. voice, singing, or instrumental music. Default: False. + + Raises: + TypeError: If `sample_rate` is not of type integer. + ValueError: If `sample_rate` is 0. + TypeError: If `central_freq` is not of type float. + TypeError: If `Q` is not of type float. + ValueError: If `Q` is not in range of (0, 1]. + TypeError: If `noise` is not of type bool. + RuntimeError: If input tensor is not in shape of <..., time>. + + Supported Platforms: + ``CPU`` + + Examples: + >>> import numpy as np + >>> + >>> waveform = np.array([[2.716064453125e-03, 6.34765625e-03], [9.246826171875e-03, 1.0894775390625e-02]]) + >>> numpy_slices_dataset = ds.NumpySlicesDataset(data=waveform, column_names=["audio"]) + >>> transforms = [audio.BandBiquad(44100, 200.0)] + >>> numpy_slices_dataset = numpy_slices_dataset.map(operations=transforms, input_columns=["audio"]) + """ + + @check_band_biquad + def __init__(self, sample_rate, central_freq, Q=0.707, noise=False): + super().__init__() + self.sample_rate = sample_rate + self.central_freq = central_freq + self.quality_factor = Q + self.noise = noise + + # 瀹氫箟涓涓悕涓簆arse鐨勬柟娉 +def parse(self): + # 杩斿洖涓涓猚de.BandBiquadOperation瀵硅薄锛岃瀵硅薄浣跨敤浠ヤ笅鍙傛暟杩涜鍒濆鍖栵細 + # - self.sample_rate: 閲囨牱鐜囷紝鐢ㄤ簬璁剧疆婊ゆ尝鍣ㄧ殑閲囨牱鐜 + # - self.central_freq: 涓績棰戠巼锛岀敤浜庤缃护娉㈠櫒鐨勪腑蹇冮鐜 + # - self.quality_factor: 璐ㄩ噺鍥犲瓙锛岀敤浜庤缃护娉㈠櫒鐨勮川閲忓洜瀛 + # - self.noise: 鍣0鍙傛暟锛岀敤浜庤缃护娉㈠櫒鐨勫櫔澹板弬鏁 + return cde.BandBiquadOperation(self.sample_rate, self.central_freq, self.quality_factor, self.noise) + + + +class BandpassBiquad(AudioTensorOperation): + r""" + Design two-pole Butterworth band-pass filter for audio waveform. + + The frequency response of the Butterworth filter is maximally flat (i.e. has no ripples) + in the passband and rolls off towards zero in the stopband. + + The system function of Butterworth band-pass filter is: + 璁捐浜嗙敤浜庨煶棰戞尝褰㈢殑鍙屾瀬宸寸壒娌冩柉甯﹂氭护娉㈠櫒銆 + 宸寸壒娌冩柉婊ゆ尝鍣ㄧ殑棰戠巼鍝嶅簲鏄渶澶у钩鍧︾殑锛堝嵆娌℃湁娉㈢汗锛夊湪閫氬甫涓苟涓斿湪闃诲甫涓粴鍚戦浂銆 + + 宸寸壒娌冩柉甯﹂氭护娉㈠櫒鐨勭郴缁熷姛鑳芥槸锛 + .. math:: + H(s) = \begin{cases} + \frac{s}{s^2 + \frac{s}{Q} + 1}, &\text{if const_skirt_gain=True}; \cr + \frac{\frac{s}{Q}}{s^2 + \frac{s}{Q} + 1}, &\text{if const_skirt_gain=False}. + \end{cases} + + Similar to `SoX `_ implementation. + + Note: + The dimension of the audio waveform to be processed needs to be (..., time). + + Args: + sample_rate (int): Sampling rate (in Hz), which can't be zero. + central_freq (float): Central frequency (in Hz). + Q (float, optional): `Quality factor `_ , + in range of (0, 1]. Default: 0.707. + const_skirt_gain (bool, optional) : If True, uses a constant skirt gain (peak gain = Q); + If False, uses a constant 0dB peak gain. Default: False. + + Raises: + TypeError: If `sample_rate` is not of type integer. + ValueError: If `sample_rate` is 0. + TypeError: If `central_freq` is not of type float. + TypeError: If `Q` is not of type float. + ValueError: If `Q` is not in range of (0, 1]. + TypeError: If `const_skirt_gain` is not of type bool. + RuntimeError: If input tensor is not in shape of <..., time>. + + Supported Platforms: + ``CPU`` + + Examples: + >>> import numpy as np + >>> + >>> waveform = np.array([[2.716064453125e-03, 6.34765625e-03], [9.246826171875e-03, 1.0894775390625e-02]]) + >>> numpy_slices_dataset = ds.NumpySlicesDataset(data=waveform, column_names=["audio"]) + >>> transforms = [audio.BandpassBiquad(44100, 200.0)] + >>> numpy_slices_dataset = numpy_slices_dataset.map(operations=transforms, input_columns=["audio"]) + """ + + @check_bandpass_biquad + def __init__(self, sample_rate, central_freq, Q=0.707, const_skirt_gain=False): + super().__init__() + self.sample_rate = sample_rate + self.central_freq = central_freq + self.quality_factor = Q + self.const_skirt_gain = const_skirt_gain + + def parse(self): + return cde.BandpassBiquadOperation(self.sample_rate, self.central_freq, self.quality_factor, + self.const_skirt_gain) + + +class BandrejectBiquad(AudioTensorOperation): + r""" + Design two-pole Butterworth band-reject filter for audio waveform. + + The frequency response of the Butterworth filter is maximally flat (i.e. has no ripples) + in the passband and rolls off towards zero in the stopband. + + The system function of Butterworth band-reject filter is: + 閽堝闊抽娉㈠舰璁捐浜嗗弻鏋佸反鐗规矁鏂甫闃绘护娉㈠櫒銆 + 宸寸壒娌冩柉婊ゆ尝鍣ㄧ殑棰戠巼鍝嶅簲鏄渶澶у钩鍧︾殑锛堝嵆娌℃湁娉㈢汗锛夊湪閫氬甫涓苟涓斿湪闃诲甫涓粴鍚戦浂銆 + 宸寸壒娌冩柉甯﹂樆婊ゆ尝鍣ㄧ殑绯荤粺鍔熻兘鏄細 + .. math:: + H(s) = \frac{s^2 + 1}{s^2 + \frac{s}{Q} + 1} + + Similar to `SoX `_ implementation. + + Note: + The dimension of the audio waveform to be processed needs to be (..., time). + + Args: + sample_rate (int): Sampling rate (in Hz), which can't be zero. + central_freq (float): Central frequency (in Hz). + Q (float, optional): `Quality factor `_ , + in range of (0, 1]. Default: 0.707. + + Raises: + TypeError: If `sample_rate` is not of type integer. + ValueError: If `sample_rate` is 0. + TypeError: If `central_freq` is not of type float. + TypeError: If `Q` is not of type float. + ValueError: If `Q` is not in range of (0, 1]. + RuntimeError: If input tensor is not in shape of <..., time>. + + Supported Platforms: + ``CPU`` + + Examples: + >>> import numpy as np + >>> + >>> waveform = np.array([[2.716064453125e-03, 6.34765625e-03],[9.246826171875e-03, 1.0894775390625e-02]]) + >>> numpy_slices_dataset = ds.NumpySlicesDataset(data=waveform, column_names=["audio"]) + >>> transforms = [audio.BandrejectBiquad(44100, 200.0)] + >>> numpy_slices_dataset = numpy_slices_dataset.map(operations=transforms, input_columns=["audio"]) + """ + + @check_bandreject_biquad + def __init__(self, sample_rate, central_freq, Q=0.707): + super().__init__() + self.sample_rate = sample_rate + self.central_freq = central_freq + self.quality_factor = Q + + def parse(self): + return cde.BandrejectBiquadOperation(self.sample_rate, self.central_freq, self.quality_factor) + + +class BassBiquad(AudioTensorOperation): + r""" + Design a bass tone-control effect, also known as two-pole low-shelf filter for audio waveform. + + A low-shelf filter passes all frequencies, but increase or reduces frequencies below the shelf + frequency by specified amount. The system function is: + 璁捐涓绉嶄綆闊虫帶鍒舵晥鏋滐紝涔熺О涓轰簩鏋佷綆鏋舵护娉㈠櫒锛岀敤浜庨煶棰戞尝褰€ + 浣庢灦婊ゆ尝鍣ㄩ氳繃鎵鏈夐鐜囷紝浣嗗鍔犳垨鍑忓皯浣庝簬鏋剁殑棰戠巼銆傞鐜囨寚瀹氱殑鏁伴噺銆傜郴缁熷姛鑳戒负锛 + .. math:: + H(s) = A\frac{s^2 + \frac{\sqrt{A}}{Q}s + A}{As^2 + \frac{\sqrt{A}}{Q}s + 1} + + Similar to `SoX `_ implementation. + + Note: + The dimension of the audio waveform to be processed needs to be (..., time). + + Args: + sample_rate (int): Sampling rate (in Hz), which can't be zero. + gain (float): Desired gain at the boost (or attenuation) in dB. + central_freq (float, optional): Central frequency (in Hz). Default: 100.0. + Q (float, optional): `Quality factor `_ , + in range of (0, 1]. Default: 0.707. + + Raises: + TypeError: If `sample_rate` is not of type integer. + ValueError: If `sample_rate` is 0. + TypeError: If `gain` is not of type float. + TypeError: If `central_freq` is not of type float. + TypeError: If `Q` is not of type float. + ValueError: If `Q` is not in range of (0, 1]. + RuntimeError: If input tensor is not in shape of <..., time>. + + Supported Platforms: + ``CPU`` + + Examples: + >>> import numpy as np + >>> + >>> waveform = np.array([[2.716064453125e-03, 6.34765625e-03], [9.246826171875e-03, 1.0894775390625e-02]]) + >>> numpy_slices_dataset = ds.NumpySlicesDataset(data=waveform, column_names=["audio"]) + >>> transforms = [audio.BassBiquad(44100, 100.0)] + >>> numpy_slices_dataset = numpy_slices_dataset.map(operations=transforms, input_columns=["audio"]) + """ + + @check_bass_biquad + def __init__(self, sample_rate, gain, central_freq=100.0, Q=0.707): + super().__init__() + self.sample_rate = sample_rate + self.gain = gain + self.central_freq = central_freq + self.quality_factor = Q + + # 瀹氫箟涓涓悕涓簆arse鐨勬柟娉 +def parse(self): + # 杩斿洖涓涓猚de.BassBiquadOperation瀵硅薄锛岃瀵硅薄浣跨敤浠ヤ笅鍙傛暟杩涜鍒濆鍖栵細 + # - self.sample_rate: 閲囨牱鐜囷紝鐢ㄤ簬璁剧疆婊ゆ尝鍣ㄧ殑閲囨牱鐜 + # - self.gain: 澧炵泭锛岀敤浜庤缃护娉㈠櫒鐨勫鐩 + # - self.central_freq: 涓績棰戠巼锛岀敤浜庤缃护娉㈠櫒鐨勪腑蹇冮鐜 + # - self.quality_factor: 璐ㄩ噺鍥犲瓙锛岀敤浜庤缃护娉㈠櫒鐨勮川閲忓洜瀛 + return cde.BassBiquadOperation(self.sample_rate, self.gain, self.central_freq, self.quality_factor) + + + +class Biquad(TensorOperation): + """ + Perform a biquad filter of input audio. + 瀵硅緭鍏ラ煶棰戞墽琛屽弻鍥涘厓婊ゆ尝鍣ㄣ + Args: + b0 (float): Numerator coefficient of current input, x[n]. + b1 (float): Numerator coefficient of input one time step ago x[n-1]. + b2 (float): Numerator coefficient of input two time steps ago x[n-2]. + a0 (float): Denominator coefficient of current output y[n], the value can't be zero, typically 1. + a1 (float): Denominator coefficient of current output y[n-1]. + a2 (float): Denominator coefficient of current output y[n-2]. + + Examples: + >>> import numpy as np + >>> + >>> waveform = np.array([[2.716064453125e-03, 6.34765625e-03], [9.246826171875e-03, 1.0894775390625e-02]]) + >>> biquad_op = audio.Biquad(0.01, 0.02, 0.13, 1, 0.12, 0.3) + >>> waveform_filtered = biquad_op(waveform) + """ + + @check_biquad + def __init__(self, b0, b1, b2, a0, a1, a2): + super().__init__() + self.b0 = b0 + self.b1 = b1 + self.b2 = b2 + self.a0 = a0 + self.a1 = a1 + self.a2 = a2 + + def parse(self): + return cde.BiquadOperation(self.b0, self.b1, self.b2, self.a0, self.a1, self.a2) + + +class ComplexNorm(AudioTensorOperation): + """ + Compute the norm of complex number sequence. + 璁$畻澶嶆暟搴忓垪鐨勮寖鏁般 + Note: + The dimension of the audio waveform to be processed needs to be (..., complex=2). + The first dimension represents the real part while the second represents the imaginary. + + Args: + power (float, optional): Power of the norm, which must be non-negative. Default: 1.0. + + Raises: + TypeError: If `power` is not of type float. + ValueError: If `power` is a negative number. + RuntimeError: If input tensor is not in shape of <..., complex=2>. + + Supported Platforms: + ``CPU`` + + Examples: + >>> import numpy as np + >>> + >>> waveform = np.random.random([2, 4, 2]) + >>> numpy_slices_dataset = ds.NumpySlicesDataset(data=waveform, column_names=["audio"]) + >>> transforms = [audio.ComplexNorm()] + >>> numpy_slices_dataset = numpy_slices_dataset.map(operations=transforms, input_columns=["audio"]) + """ + + @check_complex_norm + def __init__(self, power=1.0): + super().__init__() + self.power = power + + def parse(self): + return cde.ComplexNormOperation(self.power) + + +DE_C_BORDER_TYPE = { + BorderType.CONSTANT: cde.BorderType.DE_BORDER_CONSTANT, + BorderType.EDGE: cde.BorderType.DE_BORDER_EDGE, + BorderType.REFLECT: cde.BorderType.DE_BORDER_REFLECT, + BorderType.SYMMETRIC: cde.BorderType.DE_BORDER_SYMMETRIC, +} + + +class ComputeDeltas(AudioTensorOperation): + r""" + Compute delta coefficients of a spectrogram. + 璁$畻璋卞浘鐨刣elta绯绘暟銆 + .. math:: + d_{t}=\frac{{\textstyle\sum_{n=1}^{N}}n(c_{t+n}-c_{t-n})}{2{\textstyle\sum_{n=1}^{N}}n^{2}} + + Args: + win_length (int): The window length used for computing delta, must be no less than 3 (default=5). + pad_mode (BorderType): Mode parameter passed to padding (default=BorderType.EDGE).It can be any of + [BorderType.CONSTANT, BorderType.EDGE, BorderType.REFLECT, BordBorderTypeer.SYMMETRIC]. + + - BorderType.CONSTANT, means it fills the border with constant values. + + - BorderType.EDGE, means it pads with the last value on the edge. + + - BorderType.REFLECT, means it reflects the values on the edge omitting the last + value of edge. + + - BorderType.SYMMETRIC, means it reflects the values on the edge repeating the last + value of edge. + + Examples: + >>> import numpy as np + >>> from mindspore.dataset.audio import BorderType + >>> + >>> waveform = np.random.random([1, 400//2+1, 30]) + >>> numpy_slices_dataset = ds.NumpySlicesDataset(data=waveform, column_names=["audio"]) + >>> transforms = [audio.ComputeDeltas(win_length=7, pad_mode = BorderType.EDGE)] + >>> numpy_slices_dataset = numpy_slices_dataset.map(operations=transforms, input_columns=["audio"]) + """ + + @check_compute_deltas + def __init__(self, win_length=5, pad_mode=BorderType.EDGE): + super().__init__() + self.win_len = win_length + self.pad_mode = pad_mode + + # 瀹氫箟涓涓悕涓簆arse鐨勬柟娉 +def parse(self): + # 杩斿洖涓涓猚de.ComputeDeltasOperation瀵硅薄锛岃瀵硅薄浣跨敤浠ヤ笅鍙傛暟杩涜鍒濆鍖栵細 + # - self.win_len: 绐楀彛闀垮害锛岀敤浜庤绠楁暟鎹殑澧為噺 + # - DE_C_BORDER_TYPE.get(self.pad_mode): 浣跨敤self.pad_mode浣滀负閿粠DE_C_BORDER_TYPE瀛楀吀涓幏鍙栧搴旂殑鍊硷紝 + # 鐢ㄤ簬鎸囧畾杈圭晫澶勭悊妯″紡 + return cde.ComputeDeltasOperation(self.win_len, DE_C_BORDER_TYPE.get(self.pad_mode)) + + + +class Contrast(AudioTensorOperation): + """ + Apply contrast effect for audio waveform. + + Comparable with compression, this effect modifies an audio signal to make it sound louder. + 瀵归煶棰戞尝褰㈠簲鐢ㄥ姣斿害鏁堟灉銆 + Similar to `SoX `_ implementation. + + + Note: + The dimension of the audio waveform to be processed needs to be (..., time). + + Args: + enhancement_amount (float, optional): Controls the amount of the enhancement, + in range of [0, 100]. Default: 75.0. Note that `enhancement_amount` equal + to 0 still gives a significant contrast enhancement. + + Raises: + TypeError: If `enhancement_amount` is not of type float. + ValueError: If `enhancement_amount` is not in range [0, 100]. + RuntimeError: If input tensor is not in shape of <..., time>. + + Supported Platforms: + ``CPU`` + + Examples: + >>> import numpy as np + >>> + >>> waveform = np.array([[2.716064453125e-03, 6.34765625e-03], [9.246826171875e-03, 1.0894775390625e-02]]) + >>> numpy_slices_dataset = ds.NumpySlicesDataset(data=waveform, column_names=["audio"]) + >>> transforms = [audio.Contrast()] + >>> numpy_slices_dataset = numpy_slices_dataset.map(operations=transforms, input_columns=["audio"]) + """ + + @check_contrast + def __init__(self, enhancement_amount=75.0): + super().__init__() + self.enhancement_amount = enhancement_amount + + # 瀹氫箟涓涓悕涓簆arse鐨勬柟娉 +def parse(self): + # 杩斿洖涓涓猚de.ContrastOperation瀵硅薄锛岃瀵硅薄浣跨敤浠ヤ笅鍙傛暟杩涜鍒濆鍖栵細 + # - self.enhancement_amount: 瀵规瘮搴﹀寮哄弬鏁帮紝鐢ㄤ簬璋冩暣瀵规瘮搴︾殑绋嬪害 + return cde.ContrastOperation(self.enhancement_amount) + + + +class DBToAmplitude(AudioTensorOperation): + """ + Turn a waveform from the decibel scale to the power/amplitude scale. + 灏嗘尝褰粠鍒嗚礉鍒诲害杞崲涓哄姛鐜/鎸箙鍒诲害銆 + Args: + ref (float): Reference which the output will be scaled by. + power (float): If power equals 1, will compute DB to power. If 0.5, will compute DB to amplitude. + + Examples: + >>> import numpy as np + >>> + >>> waveform = np.array([[2.716064453125e-03, 6.34765625e-03], [9.246826171875e-03, 1.0894775390625e-02]]) + >>> numpy_slices_dataset = ds.NumpySlicesDataset(data=waveform, column_names=["audio"]) + >>> transforms = [audio.DBToAmplitude(0.5, 0.5)] + >>> numpy_slices_dataset = numpy_slices_dataset.map(operations=transforms, input_columns=["audio"]) + """ + + @check_db_to_amplitude + def __init__(self, ref, power): + super().__init__() + self.ref = ref + self.power = power + + # 瀹氫箟涓涓悕涓簆arse鐨勬柟娉 +def parse(self): + # 杩斿洖涓涓猚de.DBToAmplitudeOperation瀵硅薄锛岃瀵硅薄浣跨敤浠ヤ笅鍙傛暟杩涜鍒濆鍖栵細 + # - self.ref: 鍙傝冨硷紝鐢ㄤ簬骞呭害鍒板垎璐濈殑杞崲 + # - self.power: 骞呭害鍒板垎璐濊浆鎹㈢殑骞傛鏂癸紝閫氬父涓10 + return cde.DBToAmplitudeOperation(self.ref, self.power) + + + +class DCShift(AudioTensorOperation): + """ + Apply a DC shift to the audio. + 瀵归煶棰戣繘琛岀洿娴佽浆鎹€ + Args: + shift (float): The amount to shift the audio, the value must be in the range [-2.0, 2.0]. + limiter_gain (float, optional): Used only on peaks to prevent clipping, + the value should be much less than 1, such as 0.05 or 0.02. + + Examples: + >>> import numpy as np + >>> + >>> waveform = np.array([0.60, 0.97, -1.04, -1.26, 0.97, 0.91, 0.48, 0.93]) + >>> numpy_slices_dataset = ds.NumpySlicesDataset(data=waveform, column_names=["audio"]) + >>> transforms = [audio.DCShift(0.5, 0.02)] + >>> numpy_slices_dataset = numpy_slices_dataset.map(operations=transforms, input_columns=["audio"]) + """ + + @check_dc_shift + def __init__(self, shift, limiter_gain=None): + super().__init__() + self.shift = shift + self.limiter_gain = limiter_gain if limiter_gain else shift + + # 瀹氫箟涓涓悕涓簆arse鐨勬柟娉 +def parse(self): + # 杩斿洖涓涓猚de.DCShiftOperation瀵硅薄锛岃瀵硅薄浣跨敤浠ヤ笅鍙傛暟杩涜鍒濆鍖栵細 + # - self.shift: 鐩存祦鍋忕Щ閲忥紝鐢ㄤ簬瀵归煶棰戜俊鍙疯繘琛岀洿娴佸亸绉昏皟鏁 + # - self.limiter_gain: 闄愬箙澧炵泭锛岀敤浜庨檺鍒堕煶棰戜俊鍙风殑鎸箙鑼冨洿 + return cde.DCShiftOperation(self.shift, self.limiter_gain) + + + +class DeemphBiquad(AudioTensorOperation): + """ + Design two-pole deemph filter for audio waveform of dimension of (..., time). + 閽堝锛堚︼紝鏃堕棿锛夌淮搴︾殑闊抽娉㈠舰锛岃璁′簡鍙屾瀬deemph婊ゆ尝鍣ㄣ + Args: + sample_rate (int): sampling rate of the waveform, e.g. 44100 (Hz), + the value must be 44100 or 48000. + + Examples: + >>> import numpy as np + >>> + >>> waveform = np.array([[2.716064453125e-03, 6.34765625e-03], [9.246826171875e-03, 1.0894775390625e-02]]) + >>> numpy_slices_dataset = ds.NumpySlicesDataset(data=waveform, column_names=["audio"]) + >>> transforms = [audio.DeemphBiquad(44100)] + >>> numpy_slices_dataset = numpy_slices_dataset.map(operations=transforms, input_columns=["audio"]) + """ + + @check_deemph_biquad + def __init__(self, sample_rate): + super().__init__() + self.sample_rate = sample_rate + + def parse(self): + return cde.DeemphBiquadOperation(self.sample_rate) + + +class DetectPitchFrequency(AudioTensorOperation): + """ + Detect pitch frequency. + + It is implemented using normalized cross-correlation function and median smoothing. + 妫娴嬮煶璋冮鐜囥 + 瀹冧娇鐢ㄥ綊涓鍖栦簰鐩稿叧鍑芥暟鍜屼腑鍊煎钩婊戞潵瀹炵幇銆 + Args: + sample_rate (int): Sampling rate of the waveform, e.g. 44100 (Hz), the value can't be zero. + frame_time (float, optional): Duration of a frame, the value must be greater than zero (default=0.01). + win_length (int, optional): The window length for median smoothing (in number of frames), the value must be + greater than zero (default=30). + freq_low (int, optional): Lowest frequency that can be detected (Hz), the value must be greater than zero + (default=85). + freq_high (int, optional): Highest frequency that can be detected (Hz), the value must be greater than zero + (default=3400). + + Examples: + >>> import numpy as np + >>> + >>> waveform = np.array([[0.716064e-03, 5.347656e-03, 6.246826e-03, 2.089477e-02, 7.138305e-02], + ... [4.156616e-02, 1.394653e-02, 3.550292e-02, 0.614379e-02, 3.840209e-02]]) + >>> numpy_slices_dataset = ds.NumpySlicesDataset(data=waveform, column_names=["audio"]) + >>> transforms = [audio.DetectPitchFrequency(30, 0.1, 3, 5, 25)] + >>> numpy_slices_dataset = numpy_slices_dataset.map(operations=transforms, input_columns=["audio"]) + """ + + @check_detect_pitch_frequency + def __init__(self, sample_rate, frame_time=0.01, win_length=30, freq_low=85, freq_high=3400): + super().__init__() + self.sample_rate = sample_rate + self.frame_time = frame_time + self.win_length = win_length + self.freq_low = freq_low + self.freq_high = freq_high + + # 瀹氫箟涓涓悕涓簆arse鐨勬柟娉 +def parse(self): + # 杩斿洖涓涓猚de.DetectPitchFrequencyOperation瀵硅薄锛岃瀵硅薄浣跨敤浠ヤ笅鍙傛暟杩涜鍒濆鍖栵細 + # - self.sample_rate: 閲囨牱鐜囷紝鐢ㄤ簬闊抽珮妫娴嬫搷浣 + # - self.frame_time: 甯ф椂闂达紝鐢ㄤ簬闊抽珮妫娴嬫搷浣 + # - self.win_length: 绐楀彛闀垮害锛岀敤浜庨煶楂樻娴嬫搷浣 + # - self.freq_low: 浣庨闃堝硷紝鐢ㄤ簬闊抽珮妫娴嬫搷浣 + # - self.freq_high: 楂橀闃堝硷紝鐢ㄤ簬闊抽珮妫娴嬫搷浣 + return cde.DetectPitchFrequencyOperation(self.sample_rate, self.frame_time, + self.win_length, self.freq_low, self.freq_high) + +# 瀹氫箟涓涓悕涓篋E_C_DENSITY_FUNCTION鐨勫瓧鍏革紝鍏朵腑鍖呭惈澶氫釜閿煎 +# 杩欎簺閿煎鐢ㄤ簬灏咲ensityFunction鏋氫妇绫诲瀷鏄犲皠鍒癱de.DensityFunction鏋氫妇绫诲瀷 +DE_C_DENSITY_FUNCTION = {DensityFunction.TPDF: cde.DensityFunction.DE_DENSITY_FUNCTION_TPDF, + DensityFunction.RPDF: cde.DensityFunction.DE_DENSITY_FUNCTION_RPDF, + DensityFunction.GPDF: cde.DensityFunction.DE_DENSITY_FUNCTION_GPDF} + + + +class Dither(AudioTensorOperation): + """ + Dither increases the perceived dynamic range of audio stored at a + particular bit-depth by eliminating nonlinear truncation distortion. + 鎶栧姩澧炲姞浜嗗瓨鍌ㄥ湪閫氳繃娑堥櫎闈炵嚎鎬ф埅鏂け鐪熸潵纭畾鐗瑰畾鐨勬瘮鐗规繁搴︺ + Args: + density_function (DensityFunction, optional): The density function of a continuous + random variable. Can be one of DensityFunction.TPDF (Triangular Probability Density Function), + DensityFunction.RPDF (Rectangular Probability Density Function) or + DensityFunction.GPDF (Gaussian Probability Density Function) + (default=DensityFunction.TPDF). + noise_shaping (bool, optional): A filtering process that shapes the spectral + energy of quantisation error (default=False). + + Examples: + >>> import numpy as np + >>> + >>> waveform = np.array([[1, 2, 3], [4, 5, 6]]) + >>> numpy_slices_dataset = ds.NumpySlicesDataset(data=waveform, column_names=["audio"]) + >>> transforms = [audio.Dither()] + >>> numpy_slices_dataset = numpy_slices_dataset.map(operations=transforms, input_columns=["audio"]) + """ + + @check_dither + def __init__(self, density_function=DensityFunction.TPDF, noise_shaping=False): + super().__init__() + self.density_function = density_function + self.noise_shaping = noise_shaping + + def parse(self): + return cde.DitherOperation(DE_C_DENSITY_FUNCTION.get(self.density_function), self.noise_shaping) + + +class EqualizerBiquad(AudioTensorOperation): + """ + Design biquad equalizer filter and perform filtering. Similar to SoX implementation. + 璁捐浜岄樁鍧囪 鍣ㄦ护娉㈠櫒骞惰繘琛屾护娉€ + Args: + sample_rate (int): Sampling rate of the waveform, e.g. 44100 (Hz), the value can't be zero. + center_freq (float): Central frequency (in Hz). + gain (float): Desired gain at the boost (or attenuation) in dB. + Q (float, optional): https://en.wikipedia.org/wiki/Q_factor, range: (0, 1] (default=0.707). + + Examples: + >>> import numpy as np + >>> + >>> waveform = np.array([[2.716064453125e-03, 6.34765625e-03], [9.246826171875e-03, 1.0894775390625e-02]]) + >>> numpy_slices_dataset = ds.NumpySlicesDataset(data=waveform, column_names=["audio"]) + >>> transforms = [audio.EqualizerBiquad(44100, 1500, 5.5, 0.7)] + >>> numpy_slices_dataset = numpy_slices_dataset.map(operations=transforms, input_columns=["audio"]) + """ + + @check_equalizer_biquad + def __init__(self, sample_rate, center_freq, gain, Q=0.707): + super().__init__() + self.sample_rate = sample_rate + self.center_freq = center_freq + self.gain = gain + self.quality_factor = Q + + def parse(self): + return cde.EqualizerBiquadOperation(self.sample_rate, self.center_freq, self.gain, self.quality_factor) + + +DE_C_FADE_SHAPE = {FadeShape.QUARTER_SINE: cde.FadeShape.DE_FADE_SHAPE_QUARTER_SINE, + FadeShape.HALF_SINE: cde.FadeShape.DE_FADE_SHAPE_HALF_SINE, + FadeShape.LINEAR: cde.FadeShape.DE_FADE_SHAPE_LINEAR, + FadeShape.LOGARITHMIC: cde.FadeShape.DE_FADE_SHAPE_LOGARITHMIC, + FadeShape.EXPONENTIAL: cde.FadeShape.DE_FADE_SHAPE_EXPONENTIAL} + + +class Fade(AudioTensorOperation): + """ + Add a fade in and/or fade out to an waveform. + 灏嗘贰鍏ュ拰/鎴栨贰鍑烘坊鍔犲埌娉㈠舰涓 + Args: + fade_in_len (int, optional): Length of fade-in (time frames), which must be non-negative (default=0). + fade_out_len (int, optional): Length of fade-out (time frames), which must be non-negative (default=0). + fade_shape (FadeShape, optional): Shape of fade (default=FadeShape.LINEAR). Can be one of + FadeShape.QUARTER_SINE, FadeShape.HALF_SINE, FadeShape.LINEAR, FadeShape.LOGARITHMIC or + FadeShape.EXPONENTIAL. + + -FadeShape.QUARTER_SINE, means it tend to 0 in an quarter sin function. + + -FadeShape.HALF_SINE, means it tend to 0 in an half sin function. + + -FadeShape.LINEAR, means it linear to 0. + + -FadeShape.LOGARITHMIC, means it tend to 0 in an logrithmic function. + + -FadeShape.EXPONENTIAL, means it tend to 0 in an exponential function. + + Raises: + RuntimeError: If fade_in_len exceeds waveform length. + RuntimeError: If fade_out_len exceeds waveform length. + + Examples: + >>> import numpy as np + >>> from mindspore.dataset.audio import FadeShape + >>> + >>> waveform = np.array([[2.716064453125e-03, 6.34765625e-03, 9.246826171875e-03, 1.0894775390625e-02]]) + >>> dataset = ds.NumpySlicesDataset(data=waveform, column_names=["audio"]) + >>> transforms = [audio.Fade(fade_in_len=3, fade_out_len=2, fade_shape=FadeShape.LINEAR)] + >>> dataset = dataset.map(operations=transforms, input_columns=["audio"]) + """ + + @check_fade + def __init__(self, fade_in_len=0, fade_out_len=0, fade_shape=FadeShape.LINEAR): + super().__init__() + self.fade_in_len = fade_in_len + self.fade_out_len = fade_out_len + self.fade_shape = fade_shape + + # 瀹氫箟涓涓悕涓簆arse鐨勬柟娉 +def parse(self): + # 杩斿洖涓涓猚de.FadeOperation瀵硅薄锛岃瀵硅薄浣跨敤浠ヤ笅鍙傛暟杩涜鍒濆鍖栵細 + # - self.fade_in_len: 娣″叆闀垮害锛岀敤浜庢寚瀹氭贰鍏ユ晥鏋滅殑鎸佺画鏃堕棿 + # - self.fade_out_len: 娣″嚭闀垮害锛岀敤浜庢寚瀹氭贰鍑烘晥鏋滅殑鎸佺画鏃堕棿 + # - DE_C_FADE_SHAPE.get(self.fade_shape): 浣跨敤self.fade_shape浣滀负閿粠DE_C_FADE_SHAPE瀛楀吀涓幏鍙栧搴旂殑鍊硷紝 + # 鐢ㄤ簬鎸囧畾娣″叆娣″嚭鏁堟灉鐨勫舰鐘 + return cde.FadeOperation(self.fade_in_len, self.fade_out_len, DE_C_FADE_SHAPE.get(self.fade_shape)) + + + +class Filtfilt(AudioTensorOperation): + """ + Apply an IIR filter forward and backward to a waveform. + 灏咺IR婊ゆ尝鍣ㄥ悜鍓嶅拰鍚戝悗搴旂敤浜庢尝褰€ + Args: + a_coeffs (Sequence): denominator coefficients of difference equation of dimension of (n_order + 1). + Lower delays coefficients are first, e.g. [a0, a1, a2, ...]. + Must be same size as b_coeffs (pad with 0's as necessary). + b_coeffs (Sequence): numerator coefficients of difference equation of dimension of (n_order + 1). + Lower delays coefficients are first, e.g. [b0, b1, b2, ...]. + Must be same size as a_coeffs (pad with 0's as necessary). + clamp (bool, optional): If True, clamp the output signal to be in the range [-1, 1]. Default=True. + + Examples: + >>> import numpy as np + >>> + >>> waveform = np.array([[2.716064453125e-03, 6.34765625e-03], [9.246826171875e-03, 1.0894775390625e-02]]) + >>> a_coeffs = [0.1, 0.2, 0.3] + >>> b_coeffs = [0.1, 0.2, 0.3] + >>> numpy_slices_dataset = ds.NumpySlicesDataset(data=waveform, column_names=["audio"]) + >>> transforms = [audio.Filtfilt(a_coeffs, b_coeffs)] + >>> numpy_slices_dataset = numpy_slices_dataset.map(operations=transforms, input_columns=["audio"]) + """ + + @check_lfilter + def __init__(self, a_coeffs, b_coeffs, clamp=True): + super().__init__() + self.a_coeffs = a_coeffs + self.b_coeffs = b_coeffs + self.clamp = clamp + + def parse(self): + return cde.FiltfiltOperation(self.a_coeffs, self.b_coeffs, self.clamp) + + +DE_C_MODULATION = {Modulation.SINUSOIDAL: cde.Modulation.DE_MODULATION_SINUSOIDAL, + Modulation.TRIANGULAR: cde.Modulation.DE_MODULATION_TRIANGULAR} + +DE_C_INTERPOLATION = {Interpolation.LINEAR: cde.Interpolation.DE_INTERPOLATION_LINEAR, + Interpolation.QUADRATIC: cde.Interpolation.DE_INTERPOLATION_QUADRATIC} + + +class Flanger(AudioTensorOperation): + """ + Apply a flanger effect to the audio. + 瀵归煶棰戝簲鐢ㄧ炕杈规晥鏋溿 + Args: + sample_rate (int): Sampling rate of the waveform, e.g. 44100 (Hz). + delay (float, optional): Desired delay in milliseconds (ms), range: [0, 30] (default=0.0). + depth (float, optional): Desired delay depth in milliseconds (ms), range: [0, 10] (default=2.0). + regen (float, optional): Desired regen (feedback gain) in dB, range: [-95, 95] (default=0.0). + width (float, optional): Desired width (delay gain) in dB, range: [0, 100] (default=71.0). + speed (float, optional): Modulation speed in Hz, range: [0.1, 10] (default=0.5). + phase (float, optional): Percentage phase-shift for multi-channel, range: [0, 100] (default=25.0). + modulation (Modulation, optional): Modulation of the input tensor (default=Modulation.SINUSOIDAL). + It can be one of Modulation.SINUSOIDAL or Modulation.TRIANGULAR. + interpolation (Interpolation, optional): Interpolation of the input tensor (default=Interpolation.LINEAR). + It can be one of Interpolation.LINEAR or Interpolation.QUADRATIC. + + Examples: + >>> import numpy as np + >>> + >>> waveform = np.array([[2.716064453125e-03, 6.34765625e-03], [9.246826171875e-03, 1.0894775390625e-02]]) + >>> numpy_slices_dataset = ds.NumpySlicesDataset(data=waveform, column_names=["audio"]) + >>> transforms = [audio.Flanger(44100)] + >>> numpy_slices_dataset = numpy_slices_dataset.map(operations=transforms, input_columns=["audio"]) + """ + + @check_flanger + def __init__(self, sample_rate, delay=0.0, depth=2.0, regen=0.0, width=71.0, speed=0.5, phase=25.0, + modulation=Modulation.SINUSOIDAL, interpolation=Interpolation.LINEAR): + super().__init__() + self.sample_rate = sample_rate + self.delay = delay + self.depth = depth + self.regen = regen + self.width = width + self.speed = speed + self.phase = phase + self.modulation = modulation + self.interpolation = interpolation + + # 瀹氫箟涓涓悕涓簆arse鐨勬柟娉 +def parse(self): + # 杩斿洖涓涓猚de.FlangerOperation瀵硅薄锛岃瀵硅薄浣跨敤浠ヤ笅鍙傛暟杩涜鍒濆鍖栵細 + # - self.sample_rate: 閲囨牱鐜囷紝鐢ㄤ簬璁剧疆娣峰搷鏁堟灉鐨勯噰鏍风巼 + # - self.delay: 寤惰繜锛岀敤浜庢帶鍒舵贩鍝嶆晥鏋滅殑寤惰繜鏃堕棿 + # - self.depth: 娣卞害锛岀敤浜庢帶鍒舵贩鍝嶆晥鏋滅殑娣卞害绋嬪害 + # - self.regen: 鍐嶇敓锛岀敤浜庢帶鍒舵贩鍝嶆晥鏋滅殑鍐嶇敓鐜 + # - self.width: 瀹藉害锛岀敤浜庢帶鍒舵贩鍝嶆晥鏋滅殑瀹藉害 + # - self.speed: 閫熷害锛岀敤浜庢帶鍒舵贩鍝嶆晥鏋滅殑閫熷害 + # - self.phase: 鐩镐綅锛岀敤浜庢帶鍒舵贩鍝嶆晥鏋滅殑鐩镐綅 + # - DE_C_MODULATION.get(self.modulation): 浣跨敤self.modulation浣滀负閿粠DE_C_MODULATION瀛楀吀涓幏鍙栧搴旂殑鍊硷紝 + # 鐢ㄤ簬鎸囧畾娣峰搷鏁堟灉鐨勮皟鍒剁被鍨 + # - DE_C_INTERPOLATION.get(self.interpolation): 浣跨敤self.interpolation浣滀负閿粠DE_C_INTERPOLATION瀛楀吀涓幏鍙栧搴旂殑鍊硷紝 + # 鐢ㄤ簬鎸囧畾娣峰搷鏁堟灉鐨勬彃鍊兼柟娉 + return cde.FlangerOperation(self.sample_rate, self.delay, self.depth, self.regen, self.width, self.speed, + self.phase, DE_C_MODULATION.get(self.modulation), + DE_C_INTERPOLATION.get(self.interpolation)) + + + +class FrequencyMasking(AudioTensorOperation): + """ + Apply masking to a spectrogram in the frequency domain. + 瀵归鍩熶腑鐨勯璋卞浘搴旂敤鎺╄斀銆 + Note: + The dimension of the audio waveform to be processed needs to be (..., freq, time). + 娉細瑕佸鐞嗙殑闊抽娉㈠舰鐨勭淮搴﹂渶瑕佷负锛堚︼紝freq锛宼ime锛夈 + Args: + iid_masks (bool, optional): Whether to apply different masks to each example/channel. Default: False. + freq_mask_param (int, optional): When `iid_masks` is True, length of the mask will be uniformly sampled + from [0, freq_mask_param]; When `iid_masks` is False, directly use it as length of the mask. + The value should be in range of [0, freq_length], where `freq_length` is the length of audio waveform + in frequency domain. Default: 0. + mask_start (int, optional): Starting point to apply mask, only works when `iid_masks` is True. The value should + be in range of [0, freq_length - freq_mask_param], where `freq_length` is the length of audio waveform + in frequency domain. Default: 0. + mask_value (float, optional): Value to assign to the masked columns. Default: 0.0. + + Raises: + TypeError: If `iid_masks` is not of type bool. + TypeError: If `freq_mask_param` is not of type integer. + ValueError: If `freq_mask_param` is greater than the length of audio waveform in frequency domain. + TypeError: If `mask_start` is not of type integer. + ValueError: If `mask_start` is a negative number. + TypeError: If `mask_value` is not of type float. + ValueError: If `mask_value` is a negative number. + RuntimeError: If input tensor is not in shape of <..., freq, time>. + + Supported Platforms: + ``CPU`` + + Examples: + >>> import numpy as np + >>> + >>> waveform = np.random.random([1, 3, 2]) + >>> numpy_slices_dataset = ds.NumpySlicesDataset(data=waveform, column_names=["audio"]) + >>> transforms = [audio.FrequencyMasking(freq_mask_param=1)] + >>> numpy_slices_dataset = numpy_slices_dataset.map(operations=transforms, input_columns=["audio"]) + + .. image:: frequency_masking_original.png + + .. image:: frequency_masking.png + """ + + @check_masking + def __init__(self, iid_masks=False, freq_mask_param=0, mask_start=0, mask_value=0.0): + super().__init__() + self.iid_masks = iid_masks + self.frequency_mask_param = freq_mask_param + self.mask_start = mask_start + self.mask_value = mask_value + + # 瀹氫箟涓涓悕涓簆arse鐨勬柟娉 +def parse(self): + # 杩斿洖涓涓猚de.FrequencyMaskingOperation瀵硅薄锛岃瀵硅薄浣跨敤浠ヤ笅鍙傛暟杩涜鍒濆鍖栵細 + # - self.iid_masks: 棰戠巼鎺╄斀鍙傛暟锛岀敤浜庢寚瀹氬摢浜涢鐜囧皢琚帺钄 + # - self.frequency_mask_param: 棰戠巼鎺╄斀鍙傛暟锛岀敤浜庢寚瀹氭帺钄界殑棰戠巼鑼冨洿 + # - self.mask_start: 鎺╄斀寮濮嬶紝鐢ㄤ簬鎸囧畾棰戠巼鎺╄斀鐨勮捣濮嬩綅缃 + # - self.mask_value: 鎺╄斀鍊硷紝鐢ㄤ簬鎸囧畾鎺╄斀鍚庣殑棰戠巼鍊 + return cde.FrequencyMaskingOperation(self.iid_masks, self.frequency_mask_param, self.mask_start, + self.mask_value) + + + +class Gain(AudioTensorOperation): + """ + Apply amplification or attenuation to the whole waveform. + 瀵规暣涓尝褰㈣繘琛屾斁澶ф垨琛板噺銆 + Args: + gain_db (float): Gain adjustment in decibels (dB) (default=1.0). + + Examples: + >>> import numpy as np + >>> + >>> waveform = np.array([[2.716064453125e-03, 6.34765625e-03], [9.246826171875e-03, 1.0894775390625e-02]]) + >>> numpy_slices_dataset = ds.NumpySlicesDataset(data=waveform, column_names=["audio"]) + >>> transforms = [audio.Gain(1.2)] + >>> numpy_slices_dataset = numpy_slices_dataset.map(operations=transforms, input_columns=["audio"]) + """ + + @check_gain + def __init__(self, gain_db=1.0): + super().__init__() + self.gain_db = gain_db + + # 瀹氫箟涓涓悕涓簆arse鐨勬柟娉 +def parse(self): + # 杩斿洖涓涓猚de.MelScaleOperation瀵硅薄锛岃瀵硅薄浣跨敤浠ヤ笅鍙傛暟杩涜鍒濆鍖栵細 + # - self.n_mels: 姊呭皵婊ゆ尝鍣ㄦ暟閲忥紝鐢ㄤ簬鎸囧畾姊呭皵棰戣氨鐨勬护娉㈠櫒鏁伴噺 + # - self.sample_rate: 閲囨牱鐜囷紝鐢ㄤ簬姊呭皵棰戣氨璁$畻 + # - self.f_min: 鏈浣庨鐜囷紝鐢ㄤ簬姊呭皵棰戣氨璁$畻鐨勯鐜囪寖鍥 + # - self.f_max: 鏈楂橀鐜囷紝鐢ㄤ簬姊呭皵棰戣氨璁$畻鐨勯鐜囪寖鍥 + # - self.n_stft: 鐭椂鍌呴噷鍙跺彉鎹㈢殑棰戝煙鐐规暟锛岀敤浜庢灏旈璋辫绠 + # - DE_C_NORM_TYPE.get(self.norm): 浣跨敤self.norm浣滀负閿粠DE_C_NORM_TYPE瀛楀吀涓幏鍙栧搴旂殑鍊硷紝 + # 鐢ㄤ簬鎸囧畾姊呭皵棰戣氨鐨勫綊涓鍖栫被鍨 + # - DE_C_MEL_TYPE.get(self.mel_type): 浣跨敤self.mel_type浣滀负閿粠DE_C_MEL_TYPE瀛楀吀涓幏鍙栧搴旂殑鍊硷紝 + # 鐢ㄤ簬鎸囧畾姊呭皵棰戣氨鐨勭被鍨 + return cde.MelScaleOperation(self.n_mels, self.sample_rate, self.f_min, self.f_max, self.n_stft, + DE_C_NORM_TYPE.get(self.norm), DE_C_MEL_TYPE.get(self.mel_type)) + + + +class GriffinLim(AudioTensorOperation): + r""" + Approximate magnitude spectrogram inversion using the GriffinLim algorithm. + + .. math:: + x(n)=\frac{\sum_{m=-\infty}^{\infty} w(m S-n) y_{w}(m S, n)}{\sum_{m=-\infty}^{\infty} w^{2}(m S-n)} + + where w represents the window function, y represents the reconstructed signal of each frame and x represents the + whole signal. + 浣跨敤GriffinLim绠楁硶鐨勮繎浼奸渿绾ц氨鍥惧弽婕斻 + + 鍏朵腑w琛ㄧず绐楀嚱鏁帮紝y琛ㄧず姣忓抚鐨勯噸鏋勪俊鍙凤紝x琛ㄧず鏁翠釜淇″彿銆 + Args: + n_fft (int, optional): Size of FFT (default=400). + n_iter (int, optional): Number of iteration for phase recovery (default=32). + win_length (int, optional): Window size for GriffinLim (default=None, will be set to n_fft). + hop_length (int, optional): Length of hop between STFT windows (default=None, will be set to win_length // 2). + window_type (WindowType, optional): Window type for GriffinLim, which can be WindowType.BARTLETT, + WindowType.BLACKMAN, WindowType.HAMMING, WindowType.HANN or WindowType.KAISER (default=WindowType.HANN). + Currently kaiser window is not supported on macOS. + power (float, optional): Exponent for the magnitude spectrogram (default=2.0). + momentum (float, optional): The momentum for fast Griffin-Lim (default=0.99). + length (int, optional): Length of the expected output waveform (default=None, will be set to the value of last + dimension of the stft matrix). + rand_init (bool, optional): Flag for random phase initialization or all-zero phase initialization + (default=True). + + Examples: + >>> import numpy as np + >>> + >>> waveform = np.random.random([201, 6]) + >>> numpy_slices_dataset = ds.NumpySlicesDataset(data=waveform, column_names=["audio"]) + >>> transforms = [audio.GriffinLim(n_fft=400)] + >>> numpy_slices_dataset = numpy_slices_dataset.map(operations=transforms, input_columns=["audio"]) + """ + + @check_griffin_lim + def __init__(self, n_fft=400, n_iter=32, win_length=None, hop_length=None, window_type=WindowType.HANN, power=2, + momentum=0.99, length=None, rand_init=True): + super().__init__() + self.n_fft = n_fft + self.n_iter = n_iter + self.win_length = win_length if win_length else self.n_fft + self.hop_length = hop_length if hop_length else self.win_length // 2 + self.window_type = window_type + self.power = power + self.momentum = momentum + self.length = length if length else 0 + self.rand_init = rand_init + + def parse(self): + return cde.GriffinLimOperation(self.n_fft, self.n_iter, self.win_length, self.hop_length, + DE_C_WINDOW_TYPE.get(self.window_type), self.power, self.momentum, self.length, + self.rand_init) + + +class HighpassBiquad(AudioTensorOperation): + """ + Design biquad highpass filter and perform filtering. Similar to SoX implementation. + 璁捐鍙屽洓闃堕珮閫氭护娉㈠櫒骞惰繘琛屾护娉€傜被浼间簬SoX瀹炵幇銆 + Args: + sample_rate (int): Sampling rate of the waveform, e.g. 44100 (Hz), the value can't be zero. + cutoff_freq (float): Filter cutoff frequency (in Hz). + Q (float, optional): Quality factor, https://en.wikipedia.org/wiki/Q_factor, range: (0, 1] (default=0.707). + + Examples: + >>> import numpy as np + >>> + >>> waveform = np.array([[2.716064453125e-03, 6.34765625e-03], [9.246826171875e-03, 1.0894775390625e-02]]) + >>> numpy_slices_dataset = ds.NumpySlicesDataset(data=waveform, column_names=["audio"]) + >>> transforms = [audio.HighpassBiquad(44100, 1500, 0.7)] + >>> numpy_slices_dataset = numpy_slices_dataset.map(operations=transforms, input_columns=["audio"]) + """ + + @check_highpass_biquad + def __init__(self, sample_rate, cutoff_freq, Q=0.707): + super().__init__() + self.sample_rate = sample_rate + self.cutoff_freq = cutoff_freq + self.quality_factor = Q + + def parse(self): + return cde.HighpassBiquadOperation(self.sample_rate, self.cutoff_freq, self.quality_factor) + + +class InverseMelScale(AudioTensorOperation): + """ + Solve for a normal STFT form a mel frequency STFT, using a conversion matrix. + 浣跨敤杞崲鐭╅樀浠巑el棰戠巼STFT涓眰瑙f甯窼TFT銆 + Args: + n_stft (int): Number of bins in STFT. + n_mels (int, optional): Number of mel filterbanks (default=128). + sample_rate (int, optional): Sample rate of audio signal (default=16000). + f_min (float, optional): Minimum frequency (default=0.0). + f_max (float, optional): Maximum frequency (default=None, will be set to sample_rate // 2). + max_iter (int, optional): Maximum number of optimization iterations (default=100000). + tolerance_loss (float, optional): Value of loss to stop optimization at (default=1e-5). + tolerance_change (float, optional): Difference in losses to stop optimization at (default=1e-8). + sgdargs (dict, optional): Arguments for the SGD optimizer (default=None, will be set to + {'sgd_lr': 0.1, 'sgd_momentum': 0.9}). + norm (NormType, optional): Normalization method, can be NormType.SLANEY or NormType.NONE + (default=NormType.NONE). + mel_type (MelType, optional): Mel scale to use, can be MelType.SLANEY or MelType.HTK (default=MelType.HTK). + + Examples: + >>> import numpy as np + >>> + >>> waveform = np.random.randn(2, 2, 3, 2) + >>> numpy_slices_dataset = ds.NumpySlicesDataset(data=waveform, column_names=["audio"]) + >>> transforms = [audio.InverseMelScale(20, 3, 16000, 0, 8000, 10)] + >>> numpy_slices_dataset = numpy_slices_dataset.map(operations=transforms, input_columns=["audio"]) + """ + + @check_inverse_mel_scale + def __init__(self, n_stft, n_mels=128, sample_rate=16000, f_min=0.0, f_max=None, max_iter=100000, + tolerance_loss=1e-5, tolerance_change=1e-8, sgdargs=None, norm=NormType.NONE, mel_type=MelType.HTK): + super().__init__() + self.n_stft = n_stft + self.n_mels = n_mels + self.sample_rate = sample_rate + self.f_min = f_min + self.f_max = f_max if f_max is not None else sample_rate // 2 + self.max_iter = max_iter + self.tolerance_loss = tolerance_loss + self.tolerance_change = tolerance_change + if sgdargs is None: + self.sgdargs = {'sgd_lr': 0.1, 'sgd_momentum': 0.9} + else: + self.sgdargs = sgdargs + self.norm = norm + self.mel_type = mel_type + + def parse(self): + return cde.InverseMelScaleOperation(self.n_stft, self.n_mels, self.sample_rate, self.f_min, self.f_max, + self.max_iter, self.tolerance_loss, self.tolerance_change, self.sgdargs, + DE_C_NORM_TYPE.get(self.norm), DE_C_MEL_TYPE.get(self.mel_type)) + + +class LFilter(AudioTensorOperation): + """ + Design two-pole filter for audio waveform of dimension of (..., time). + 閽堝锛堚︼紝鏃堕棿锛夌淮搴︾殑闊抽娉㈠舰璁捐鍙屾瀬婊ゆ尝鍣ㄣ + Args: + a_coeffs (sequence): denominator coefficients of difference equation of dimension of (n_order + 1). + Lower delays coefficients are first, e.g. [a0, a1, a2, ...]. + Must be same size as b_coeffs (pad with 0's as necessary). + b_coeffs (sequence): numerator coefficients of difference equation of dimension of (n_order + 1). + Lower delays coefficients are first, e.g. [b0, b1, b2, ...]. + Must be same size as a_coeffs (pad with 0's as necessary). + clamp (bool, optional): If True, clamp the output signal to be in the range [-1, 1] (default=True). + + Examples: + >>> import numpy as np + >>> + >>> waveform = np.array([[2.716064453125e-03, 6.34765625e-03], [9.246826171875e-03, 1.0894775390625e-02]]) + >>> a_coeffs = [0.1, 0.2, 0.3] + >>> b_coeffs = [0.1, 0.2, 0.3] + >>> numpy_slices_dataset = ds.NumpySlicesDataset(data=waveform, column_names=["audio"]) + >>> transforms = [audio.LFilter(a_coeffs, b_coeffs)] + >>> numpy_slices_dataset = numpy_slices_dataset.map(operations=transforms, input_columns=["audio"]) + """ + + @check_lfilter + def __init__(self, a_coeffs, b_coeffs, clamp=True): + super().__init__() + self.a_coeffs = a_coeffs + self.b_coeffs = b_coeffs + self.clamp = clamp + + def parse(self): + return cde.LFilterOperation(self.a_coeffs, self.b_coeffs, self.clamp) + + +class LowpassBiquad(AudioTensorOperation): + r""" + Design two-pole low-pass filter for audio waveform. + + A low-pass filter passes frequencies lower than a selected cutoff frequency + but attenuates frequencies higher than it. The system function is: + 璁捐浜嗛煶棰戞尝褰㈢殑鍙屾瀬浣庨氭护娉㈠櫒銆 + 浣庨氭护娉㈠櫒閫氳繃浣庝簬閫夊畾鎴棰戠巼鐨勯鐜囷紝浣嗘槸琛板噺楂樹簬瀹冪殑棰戠巼銆傜郴缁熷姛鑳芥槸锛 + .. math:: + H(s) = \frac{1}{s^2 + \frac{s}{Q} + 1} + + Similar to `SoX `_ implementation. + + Note: + The dimension of the audio waveform to be processed needs to be (..., time). + + Args: + sample_rate (int): Sampling rate (in Hz), which can't be zero. + cutoff_freq (float): Filter cutoff frequency (in Hz). + Q (float, optional): `Quality factor `_ , + in range of (0, 1]. Default: 0.707. + + Raises: + TypeError: If `sample_rate` is not of type integer. + ValueError: If `sample_rate` is 0. + TypeError: If `cutoff_freq` is not of type float. + TypeError: If `Q` is not of type float. + ValueError: If `Q` is not in range of (0, 1]. + RuntimeError: If input tensor is not in shape of <..., time>. + + Supported Platforms: + ``CPU`` + + Examples: + >>> import numpy as np + >>> + >>> waveform = np.array([[0.8236, 0.2049, 0.3335], [0.5933, 0.9911, 0.2482], + ... [0.3007, 0.9054, 0.7598], [0.5394, 0.2842, 0.5634], [0.6363, 0.2226, 0.2288]]) + >>> numpy_slices_dataset = ds.NumpySlicesDataset(data=waveform, column_names=["audio"]) + >>> transforms = [audio.LowpassBiquad(4000, 1500, 0.7)] + >>> numpy_slices_dataset = numpy_slices_dataset.map(operations=transforms, input_columns=["audio"]) + """ + + @check_lowpass_biquad + def __init__(self, sample_rate, cutoff_freq, Q=0.707): + super().__init__() + self.sample_rate = sample_rate + self.cutoff_freq = cutoff_freq + self.quality_factor = Q + + def parse(self): + return cde.LowpassBiquadOperation(self.sample_rate, self.cutoff_freq, self.quality_factor) + + +class Magphase(AudioTensorOperation): + """ + Separate a complex-valued spectrogram with shape (..., 2) into its magnitude and phase. + 灏嗗舰鐘朵负锛堚︼紝2锛夌殑澶嶅艰氨鍥惧垎绂讳负鍏跺箙搴﹀拰鐩镐綅銆 + Args: + power (float): Power of the norm, which must be non-negative (default=1.0). + + Examples: + >>> import numpy as np + >>> + >>> waveform = np.random.random([2, 4, 2]) + >>> numpy_slices_dataset = ds.NumpySlicesDataset(data=waveform, column_names=["audio"]) + >>> transforms = [audio.Magphase()] + >>> numpy_slices_dataset = numpy_slices_dataset.map(operations=transforms, input_columns=["audio"]) + """ + + @check_magphase + def __init__(self, power=1.0): + super().__init__() + self.power = power + + def parse(self): + return cde.MagphaseOperation(self.power) + + +class MaskAlongAxis(AudioTensorOperation): + """ + Apply a mask along `axis`. Mask will be applied from indices `[mask_start, mask_start + mask_width)`. + 娌胯酱搴旂敤閬僵銆傛帺鐮佸皢浠庣储寮曗淸Mask_start锛孧ask_start+Mask_width锛夆濆簲鐢ㄣ + Args: + mask_start (int): Starting position of the mask, which must be non negative. + mask_width (int): The width of the mask, which must be non negative. + mask_value (float): Value to assign to the masked columns. + axis (int): Axis to apply masking on (1 for frequency and 2 for time). + + Examples: + >>> import numpy as np + >>> + >>> waveform = np.random.random([1, 20, 20]) + >>> numpy_slices_dataset = ds.NumpySlicesDataset(data=waveform, column_names=["audio"]) + >>> transforms = [audio.MaskAlongAxis(0, 10, 0.5, 1)] + >>> numpy_slices_dataset = numpy_slices_dataset.map(operations=transforms, input_columns=["audio"]) + """ + + @check_mask_along_axis + def __init__(self, mask_start, mask_width, mask_value, axis): + super().__init__() + self.mask_start = mask_start + self.mask_width = mask_width + self.mask_value = mask_value + self.axis = axis + + def parse(self): + return cde.MaskAlongAxisOperation(self.mask_start, self.mask_width, self.mask_value, self.axis) + + +class MaskAlongAxisIID(AudioTensorOperation): + """ + Apply a mask along `axis`. Mask will be applied from indices `[mask_start, mask_start + mask_width)`, where + `mask_width` is sampled from `uniform[0, mask_param]`, and `mask_start` from `uniform[0, max_length - mask_width]`, + `max_length` is the number of columns of the specified axis of the spectrogram. + 娌库滆酱鈥濆簲鐢ㄩ伄缃┿傛帺鐮佸皢浠庣储寮曗淸Mask_start锛孧ask_start+Mask_width锛夆濆簲鐢紝鍏朵腑 + 鈥渕ask_width鈥濅粠鈥渦niform[0]锛宮ask_param]鈥濋噰鏍凤紝鈥渕ask_start鈥濅粠鈥渦niform[0]锛宮ax_length-mask_width]鈥濋噰鏍凤紝鈥渕ax_length鈥濇槸澹拌氨鍥剧殑鎸囧畾杞寸殑鍒楁暟銆 + Args: + mask_param (int): Number of columns to be masked, will be uniformly sampled from + [0, mask_param], must be non negative. + mask_value (float): Value to assign to the masked columns. + axis (int): Axis to apply masking on (1 for frequency and 2 for time). + + Examples: + >>> import numpy as np + >>> + >>> waveform= np.random.random([1, 20, 20]) + >>> numpy_slices_dataset = ds.NumpySlicesDataset(data=waveform, column_names=["audio"]) + >>> transforms = [audio.MaskAlongAxisIID(5, 0.5, 2)] + >>> numpy_slices_dataset = numpy_slices_dataset.map(operations=transforms, input_columns=["audio"]) + """ + + @check_mask_along_axis_iid + def __init__(self, mask_param, mask_value, axis): + super().__init__() + self.mask_param = mask_param + self.mask_value = mask_value + self.axis = axis + + def parse(self): + return cde.MaskAlongAxisIIDOperation(self.mask_param, self.mask_value, self.axis) + + +DE_C_MEL_TYPE = {MelType.SLANEY: cde.MelType.DE_MEL_TYPE_SLANEY, + MelType.HTK: cde.MelType.DE_MEL_TYPE_HTK} + +DE_C_NORM_TYPE = {NormType.NONE: cde.NormType.DE_NORM_TYPE_NONE, + NormType.SLANEY: cde.NormType.DE_NORM_TYPE_SLANEY} + + +class MelScale(AudioTensorOperation): + """ + Convert normal STFT to STFT at the Mel scale. + 灏嗘甯窼TFT杞崲涓烘灏斿埢搴︾殑STFT銆 + Args: + n_mels (int, optional): Number of mel filterbanks (default=128). + sample_rate (int, optional): Sample rate of audio signal (default=16000). + f_min (float, optional): Minimum frequency (default=0). + f_max (float, optional): Maximum frequency (default=None, will be set to sample_rate // 2). + n_stft (int, optional): Number of bins in STFT (default=201). + norm (NormType, optional): Type of norm, value should be NormType.SLANEY or NormType::NONE. + If norm is NormType.SLANEY, divide the triangular mel weight by the width of the mel band. + (default=NormType.NONE). + mel_type (MelType, optional): Type to use, value should be MelType.SLANEY or MelType.HTK (default=MelType.HTK). + + Examples: + >>> import numpy as np + >>> + >>> waveform = np.array([[0.8236, 0.2049, 0.3335], [0.5933, 0.9911, 0.2482], + ... [0.3007, 0.9054, 0.7598], [0.5394, 0.2842, 0.5634], [0.6363, 0.2226, 0.2288]]) + >>> numpy_slices_dataset = ds.NumpySlicesDataset(data=waveform, column_names=["audio"]) + >>> transforms = [audio.MelScale(4000, 1500, 0.7)] + >>> numpy_slices_dataset = numpy_slices_dataset.map(operations=transforms, input_columns=["audio"]) + """ + + @check_mel_scale + def __init__(self, n_mels=128, sample_rate=16000, f_min=0, f_max=None, n_stft=201, norm=NormType.NONE, + mel_type=MelType.HTK): + super().__init__() + self.n_mels = n_mels + self.sample_rate = sample_rate + self.f_min = f_min + self.f_max = f_max if f_max is not None else sample_rate // 2 + self.n_stft = n_stft + self.norm = norm + self.mel_type = mel_type + + # 瀹氫箟涓涓悕涓簆arse鐨勬柟娉 +def parse(self): + # 杩斿洖涓涓猚de.MelScaleOperation瀵硅薄锛岃瀵硅薄浣跨敤浠ヤ笅鍙傛暟杩涜鍒濆鍖栵細 + # - self.n_mels: 姊呭皵婊ゆ尝鍣ㄦ暟閲忥紝鐢ㄤ簬鎸囧畾姊呭皵棰戣氨鐨勬护娉㈠櫒鏁伴噺 + # - self.sample_rate: 閲囨牱鐜囷紝鐢ㄤ簬姊呭皵棰戣氨璁$畻 + # - self.f_min: 鏈浣庨鐜囷紝鐢ㄤ簬姊呭皵棰戣氨璁$畻鐨勯鐜囪寖鍥 + # - self.f_max: 鏈楂橀鐜囷紝鐢ㄤ簬姊呭皵棰戣氨璁$畻鐨勯鐜囪寖鍥 + # - self.n_stft: 鐭椂鍌呴噷鍙跺彉鎹㈢殑棰戝煙鐐规暟锛岀敤浜庢灏旈璋辫绠 + # - DE_C_NORM_TYPE.get(self.norm): 浣跨敤self.norm浣滀负閿粠DE_C_NORM_TYPE瀛楀吀涓幏鍙栧搴旂殑鍊硷紝 + # 鐢ㄤ簬鎸囧畾姊呭皵棰戣氨鐨勫綊涓鍖栫被鍨 + # - DE_C_MEL_TYPE.get(self.mel_type): 浣跨敤self.mel_type浣滀负閿粠DE_C_MEL_TYPE瀛楀吀涓幏鍙栧搴旂殑鍊硷紝 + # 鐢ㄤ簬鎸囧畾姊呭皵棰戣氨鐨勭被鍨 + return cde.MelScaleOperation(self.n_mels, self.sample_rate, self.f_min, self.f_max, self.n_stft, + DE_C_NORM_TYPE.get(self.norm), DE_C_MEL_TYPE.get(self.mel_type)) + + + +class MuLawDecoding(AudioTensorOperation): + """ + Decode mu-law encoded signal. + 瀵刮煎畾寰嬬紪鐮佷俊鍙疯繘琛岃В鐮併 + Args: + quantization_channels (int): Number of channels, which must be positive (Default: 256). + + Examples: + >>> import numpy as np + >>> + >>> waveform = np.random.random([1, 3, 4]) + >>> numpy_slices_dataset = ds.NumpySlicesDataset(data=waveform, column_names=["audio"]) + >>> transforms = [audio.MuLawDecoding()] + >>> numpy_slices_dataset = numpy_slices_dataset.map(operations=transforms, input_columns=["audio"]) + """ + + @check_mu_law_coding + def __init__(self, quantization_channels=256): + super().__init__() + self.quantization_channels = quantization_channels + + def parse(self): + return cde.MuLawDecodingOperation(self.quantization_channels) + + +class MuLawEncoding(AudioTensorOperation): + """ + Encode signal based on mu-law companding. + 鍩轰簬渭寰嬪帇缂╂墿灞曞淇″彿杩涜缂栫爜銆 + Args: + quantization_channels (int): Number of channels, which must be positive (Default: 256). + + Examples: + >>> import numpy as np + >>> + >>> waveform = np.random.random([1, 3, 4]) + >>> numpy_slices_dataset = ds.NumpySlicesDataset(data=waveform, column_names=["audio"]) + >>> transforms = [audio.MuLawEncoding()] + >>> numpy_slices_dataset = numpy_slices_dataset.map(operations=transforms, input_columns=["audio"]) + """ + + @check_mu_law_coding + def __init__(self, quantization_channels=256): + super().__init__() + self.quantization_channels = quantization_channels + + def parse(self): + return cde.MuLawEncodingOperation(self.quantization_channels) + + +class Overdrive(AudioTensorOperation): + """ + Apply overdrive on input audio. + 瀵硅緭鍏ラ煶棰戝簲鐢ㄩ┍鍔ㄣ + Args: + gain (float): Desired gain at the boost (or attenuation) in dB, in range of [0, 100] (default=20.0). + color (float): Controls the amount of even harmonic content in the over-driven output, + in range of [0, 100] (default=20.0). + + Examples: + >>> import numpy as np + >>> + >>> waveform = np.array([[1, 2, 3], [4, 5, 6]], dtype=np.float32) + >>> numpy_slices_dataset = ds.NumpySlicesDataset(data=waveform, column_names=["audio"]) + >>> transforms = [audio.Overdrive()] + >>> numpy_slices_dataset = numpy_slices_dataset.map(operations=transforms, input_columns=["audio"]) + """ + + @check_overdrive + def __init__(self, gain=20.0, color=20.0): + super().__init__() + self.gain = gain + self.color = color + + def parse(self): + return cde.OverdriveOperation(self.gain, self.color) + + +class Phaser(AudioTensorOperation): + """ + Apply a phasing effect to the audio. + 瀵归煶棰戝簲鐢ㄥ畾鐩告晥鏋溿 + Args: + sample_rate (int): Sampling rate of the waveform, e.g. 44100 (Hz). + gain_in (float): Desired input gain at the boost (or attenuation) in dB. + Allowed range of values is [0, 1] (default=0.4). + gain_out (float): Desired output gain at the boost (or attenuation) in dB. + Allowed range of values is [0, 1e9] (default=0.74). + delay_ms (float): Desired delay in milli seconds. Allowed range of values is [0, 5] (default=3.0). + decay (float): Desired decay relative to gain-in. Allowed range of values is [0, 0.99] (default=0.4). + mod_speed (float): Modulation speed in Hz. Allowed range of values is [0.1, 2] (default=0.5). + sinusoidal (bool): If True, use sinusoidal modulation (preferable for multiple instruments). + If False, use triangular modulation (gives single instruments a sharper + phasing effect) (default=True). + + Examples: + >>> import numpy as np + >>> + >>> waveform = np.array([[1, 2, 3], [4, 5, 6]], dtype=np.float32) + >>> numpy_slices_dataset = ds.NumpySlicesDataset(data=waveform, column_names=["audio"]) + >>> transforms = [audio.Phaser(44100)] + >>> numpy_slices_dataset = numpy_slices_dataset.map(operations=transforms, input_columns=["audio"]) + """ + + @check_phaser + def __init__(self, sample_rate, gain_in=0.4, gain_out=0.74, delay_ms=3.0, decay=0.4, mod_speed=0.5, + sinusoidal=True): + super().__init__() + self.decay = decay + self.delay_ms = delay_ms + self.gain_in = gain_in + self.gain_out = gain_out + self.mod_speed = mod_speed + self.sample_rate = sample_rate + self.sinusoidal = sinusoidal + + def parse(self): + return cde.PhaserOperation(self.sample_rate, self.gain_in, self.gain_out, + self.delay_ms, self.decay, self.mod_speed, self.sinusoidal) + + +class PhaseVocoder(AudioTensorOperation): + """ + Given a STFT tensor, speed up in time without modifying pitch by a factor of rate. + 缁欏畾STFT寮犻噺锛屽湪涓嶄互閫熺巼鍥犲瓙淇敼闊抽珮鐨勬儏鍐典笅鍙婃椂鍔犻熴 + Args: + rate (float): Speed-up factor. + phase_advance (numpy.ndarray): Expected phase advance in each bin in shape of (freq, 1). + + Examples: + >>> import numpy as np + >>> + >>> waveform = np.random.random([2, 44, 10, 2]) + >>> numpy_slices_dataset = ds.NumpySlicesDataset(data=waveform, column_names=["audio"]) + >>> phase_advance = np.random.random([44, 1]) + >>> transforms = [audio.PhaseVocoder(rate=2, phase_advance=phase_advance)] + >>> numpy_slices_dataset = numpy_slices_dataset.map(operations=transforms, input_columns=["audio"]) + """ + + @check_phase_vocoder + def __init__(self, rate, phase_advance): + super().__init__() + self.rate = rate + self.phase_advance = cde.Tensor(phase_advance) + + def parse(self): + return cde.PhaseVocoderOperation(self.rate, self.phase_advance) + + +DE_C_RESAMPLE_METHOD = {ResampleMethod.SINC_INTERPOLATION: cde.ResampleMethod.DE_RESAMPLE_SINC_INTERPOLATION, + ResampleMethod.KAISER_WINDOW: cde.ResampleMethod.DE_RESAMPLE_KAISER_WINDOW} + + +class Resample(AudioTensorOperation): + """ + Resample a signal from one frequency to another. A resample method can be given. + 灏嗕俊鍙蜂粠涓涓鐜囬噸鏂伴噰鏍峰埌鍙︿竴涓鐜囥傚彲浠ョ粰鍑轰竴绉嶉噸鏂伴噰鏍风殑鏂规硶銆 + Args: + orig_freq (float, optional): The original frequency of the signal, which must be positive (default=16000). + new_freq (float, optional): The desired frequency, which must be positive (default=16000). + resample_method (ResampleMethod, optional): The resample method, which can be + ResampleMethod.SINC_INTERPOLATION and ResampleMethod.KAISER_WINDOW + (default=ResampleMethod.SINC_INTERPOLATION). + lowpass_filter_width (int, optional): Controls the shaperness of the filter, more means sharper but less + efficient, which must be positive (default=6). + rolloff (float, optional): The roll-off frequency of the filter, as a fraction of the Nyquist. Lower values + reduce anti-aliasing, but also reduce some of the highest frequencies, range: (0, 1] (default=0.99). + beta (float, optional): The shape parameter used for kaiser window (default=None, will use 14.769656459379492). + + Examples: + >>> import numpy as np + >>> from mindspore.dataset.audio import ResampleMethod + >>> + >>> waveform = np.random.random([1, 30]) + >>> numpy_slices_dataset = ds.NumpySlicesDataset(data=waveform, column_names=["audio"]) + >>> transforms = [audio.Resample(orig_freq=48000, new_freq=16000, + ... resample_method=ResampleMethod.SINC_INTERPOLATION, + ... lowpass_filter_width=6, rolloff=0.99, beta=None)] + >>> numpy_slices_dataset = numpy_slices_dataset.map(operations=transforms, input_columns=["audio"]) + """ + + @check_resample + def __init__(self, orig_freq=16000, new_freq=16000, resample_method=ResampleMethod.SINC_INTERPOLATION, + lowpass_filter_width=6, rolloff=0.99, beta=None): + super().__init__() + self.orig_freq = orig_freq + self.new_freq = new_freq + self.resample_method = resample_method + self.lowpass_filter_width = lowpass_filter_width + self.rolloff = rolloff + kaiser_beta = 14.769656459379492 + self.beta = beta if beta is not None else kaiser_beta + + def parse(self): + return cde.ResampleOperation(self.orig_freq, self.new_freq, DE_C_RESAMPLE_METHOD.get(self.resample_method), + self.lowpass_filter_width, self.rolloff, self.beta) + + +class RiaaBiquad(AudioTensorOperation): + """ + Apply RIAA vinyl playback equalization. Similar to SoX implementation. + 搴旂敤RIAA涔欑儻鍩烘挱鏀惧潎琛° + Args: + sample_rate (int): sampling rate of the waveform, e.g. 44100 (Hz), + can only be one of 44100, 48000, 88200, 96000. + + Examples: + >>> import numpy as np + >>> + >>> waveform = np.array([[1, 2, 3], [4, 5, 6]], dtype=np.float64) + >>> numpy_slices_dataset = ds.NumpySlicesDataset(data=waveform, column_names=["audio"]) + >>> transforms = [audio.RiaaBiquad(44100)] + >>> numpy_slices_dataset = numpy_slices_dataset.map(operations=transforms, input_columns=["audio"]) + """ + + @check_riaa_biquad + def __init__(self, sample_rate): + super().__init__() + self.sample_rate = sample_rate + + def parse(self): + return cde.RiaaBiquadOperation(self.sample_rate) + + +class SlidingWindowCmn(AudioTensorOperation): + """ + Apply sliding-window cepstral mean (and optionally variance) normalization per utterance. + 瀵规瘡涓瘽璇簲鐢ㄦ粦鍔ㄧ獥鍙e掗璋卞潎鍊硷紙浠ュ強鍙夌殑鏂瑰樊锛夊綊涓鍖栥 + Args: + cmn_window (int, optional): Window in frames for running average CMN computation (default=600). + min_cmn_window (int, optional): Minimum CMN window used at start of decoding (adds latency only at start). + Only applicable if center is False, ignored if center is True (default=100). + center (bool, optional): If True, use a window centered on the current frame. If False, window is + to the left. (default=False). + norm_vars (bool, optional): If True, normalize variance to one. (default=False). + + Examples: + >>> import numpy as np + >>> + >>> waveform = np.array([[[1, 2, 3], [4, 5, 6]]], dtype=np.float64) + >>> numpy_slices_dataset = ds.NumpySlicesDataset(data=waveform, column_names=["audio"]) + >>> transforms = [audio.SlidingWindowCmn()] + >>> numpy_slices_dataset = numpy_slices_dataset.map(operations=transforms, input_columns=["audio"]) + """ + + @check_sliding_window_cmn + def __init__(self, cmn_window=600, min_cmn_window=100, center=False, norm_vars=False): + super().__init__() + self.cmn_window = cmn_window + self.min_cmn_window = min_cmn_window + self.center = center + self.norm_vars = norm_vars + + # 瀹氫箟涓涓悕涓簆arse鐨勬柟娉 +def parse(self): + # 杩斿洖涓涓猚de.SlidingWindowCmnOperation瀵硅薄锛岃瀵硅薄浣跨敤浠ヤ笅鍙傛暟杩涜鍒濆鍖栵細 + # - self.cmn_window: 婊戝姩绐楀彛澶у皬锛岀敤浜庤绠楁粦鍔ㄧ獥鍙g殑澶у皬 + # - self.min_cmn_window: 鏈灏忔粦鍔ㄧ獥鍙eぇ灏忥紝鐢ㄤ簬璁剧疆鏈灏忕殑婊戝姩绐楀彛澶у皬 + # - self.center: 涓績鍖栵紝鐢ㄤ簬鎺у埗鏄惁瀵规粦鍔ㄧ獥鍙h绠楄繘琛屼腑蹇冨寲澶勭悊 + # - self.norm_vars: 褰掍竴鍖栨柟宸紝鐢ㄤ簬鎺у埗鏄惁瀵规粦鍔ㄧ獥鍙h绠楄繘琛屾柟宸綊涓鍖栧鐞 + return cde.SlidingWindowCmnOperation(self.cmn_window, self.min_cmn_window, self.center, self.norm_vars) + +# 瀹氫箟涓涓悕涓篋E_C_WINDOW_TYPE鐨勫瓧鍏革紝鍏朵腑鍖呭惈澶氫釜閿煎 +# 杩欎簺閿煎鐢ㄤ簬灏哤indowType鏋氫妇绫诲瀷鏄犲皠鍒癱de.WindowType鏋氫妇绫诲瀷 +DE_C_WINDOW_TYPE = {WindowType.BARTLETT: cde.WindowType.DE_WINDOW_TYPE_BARTLETT, + WindowType.BLACKMAN: cde.WindowType.DE_WINDOW_TYPE_BLACKMAN, + WindowType.HAMMING: cde.WindowType.DE_WINDOW_TYPE_HAMMING, + WindowType.HANN: cde.WindowType.DE_WINDOW_TYPE_HANN, + WindowType.KAISER: cde.WindowType.DE_WINDOW_TYPE_KAISER} + + + +class SpectralCentroid(TensorOperation): + """ + Create a spectral centroid from an audio signal. + 浠庨煶棰戜俊鍙峰垱寤洪璋辫川蹇冦 + Args: + sample_rate (int): Sampling rate of the waveform, e.g. 44100 (Hz). + n_fft (int, optional): Size of FFT, creates n_fft // 2 + 1 bins (default=400). + win_length (int, optional): Window size (default=None, will use n_fft). + hop_length (int, optional): Length of hop between STFT windows (default=None, will use win_length // 2). + pad (int, optional): Two sided padding of signal (default=0). + window (WindowType, optional): Window function that is applied/multiplied to each frame/window, + which can be WindowType.BARTLETT, WindowType.BLACKMAN, WindowType.HAMMING, WindowType.HANN + or WindowType.KAISER (default=WindowType.HANN). + + Examples: + >>> import numpy as np + >>> + >>> waveform = np.random.random([5, 10, 20]) + >>> numpy_slices_dataset = ds.NumpySlicesDataset(data=waveform, column_names=["audio"]) + >>> transforms = [audio.SpectralCentroid(44100)] + >>> numpy_slices_dataset = numpy_slices_dataset.map(operations=transforms, input_columns=["audio"]) + """ + + @check_spectral_centroid + def __init__(self, sample_rate, n_fft=400, win_length=None, hop_length=None, pad=0, window=WindowType.HANN): + super().__init__() + self.sample_rate = sample_rate + self.pad = pad + self.window = window + self.n_fft = n_fft + self.win_length = win_length if win_length else n_fft + self.hop_length = hop_length if hop_length else self.win_length // 2 + + def parse(self): + return cde.SpectralCentroidOperation(self.sample_rate, self.n_fft, self.win_length, self.hop_length, + self.pad, DE_C_WINDOW_TYPE.get(self.window)) + + +class Spectrogram(TensorOperation): + """ + Create a spectrogram from an audio signal. + 鏍规嵁闊抽淇″彿鍒涘缓澹拌氨鍥俱 + Args: + n_fft (int, optional): Size of FFT, creates n_fft // 2 + 1 bins (default=400). + win_length (int, optional): Window size (default=None, will use n_fft). + hop_length (int, optional): Length of hop between STFT windows (default=None, will use win_length // 2). + pad (int): Two sided padding of signal (default=0). + window (WindowType, optional): Window function that is applied/multiplied to each frame/window, + which can be WindowType.BARTLETT, WindowType.BLACKMAN, WindowType.HAMMING, WindowType.HANN + or WindowType.KAISER (default=WindowType.HANN). Currently kaiser window is not supported on macOS. + power (float, optional): Exponent for the magnitude spectrogram, which must be greater + than or equal to 0, e.g., 1 for energy, 2 for power, etc. (default=2.0). + normalized (bool, optional): Whether to normalize by magnitude after stft (default=False). + center (bool, optional): Whether to pad waveform on both sides (default=True). + pad_mode (BorderType, optional): Controls the padding method used when center is True, + which can be BorderType.REFLECT, BorderType.CONSTANT, BorderType.EDGE, BorderType.SYMMETRIC + (default=BorderType.REFLECT). + onesided (bool, optional): Controls whether to return half of results to avoid redundancy (default=True). + + Examples: + >>> import numpy as np + >>> + >>> waveform = np.random.random([5, 10, 20]) + >>> numpy_slices_dataset = ds.NumpySlicesDataset(data=waveform, column_names=["audio"]) + >>> transforms = [audio.Spectrogram()] + >>> numpy_slices_dataset = numpy_slices_dataset.map(operations=transforms, input_columns=["audio"]) + """ + + @check_spectrogram + def __init__(self, n_fft=400, win_length=None, hop_length=None, pad=0, window=WindowType.HANN, power=2.0, + normalized=False, center=True, pad_mode=BorderType.REFLECT, onesided=True): + super().__init__() + self.n_fft = n_fft + self.win_length = win_length if win_length else n_fft + self.hop_length = hop_length if hop_length else self.win_length // 2 + self.pad = pad + self.window = window + self.power = power + self.normalized = normalized + self.center = center + self.pad_mode = pad_mode + self.onesided = onesided + + # 瀹氫箟涓涓悕涓簆arse鐨勬柟娉 +def parse(self): + # 杩斿洖涓涓猚de.SpectrogramOperation瀵硅薄锛岃瀵硅薄浣跨敤浠ヤ笅鍙傛暟杩涜鍒濆鍖栵細 + # - self.n_fft: FFT澶у皬锛岀敤浜庡倕閲屽彾鍙樻崲鐨勭獥鍙eぇ灏 + # - self.win_length: 绐楀彛闀垮害锛岀敤浜庣煭鏃跺倕閲屽彾鍙樻崲鐨勭獥鍙eぇ灏 + # - self.hop_length: 璺宠穬闀垮害锛岀敤浜庣煭鏃跺倕閲屽彾鍙樻崲鐨勫抚涔嬮棿鐨勮窛绂 + # - self.pad: 闆跺~鍏咃紝鐢ㄤ簬鍦ㄧ獥鍙d箣澶栧~鍏呴浂鍊间互澧炲姞鍒嗘瀽绐楀彛鐨勫ぇ灏 + # - DE_C_WINDOW_TYPE.get(self.window): 浣跨敤self.window浣滀负閿粠DE_C_WINDOW_TYPE瀛楀吀涓幏鍙栧搴旂殑鍊硷紝 + # 鐢ㄤ簬鎸囧畾绐楀彛鍑芥暟鐨勭被鍨 + # - self.power: 鑳介噺璋辩殑骞傛鏂癸紝閫氬父涓2 + # - self.normalized: 鏄惁瀵硅兘閲忚氨杩涜褰掍竴鍖 + # - self.center: 鏄惁瀵圭獥鍙e嚱鏁拌繘琛屽眳涓鐞 + # - DE_C_BORDER_TYPE.get(self.pad_mode): 浣跨敤self.pad_mode浣滀负閿粠DE_C_BORDER_TYPE瀛楀吀涓幏鍙栧搴旂殑鍊硷紝 + # 鐢ㄤ簬鎸囧畾杈圭晫澶勭悊妯″紡 + # - self.onesided: 鏄惁浠呰绠楀崟杈归璋 + return cde.SpectrogramOperation(self.n_fft, self.win_length, self.hop_length, self.pad, + DE_C_WINDOW_TYPE.get(self.window), self.power, self.normalized, + self.center, DE_C_BORDER_TYPE.get(self.pad_mode), self.onesided) + + + +class TimeMasking(AudioTensorOperation): + """ + Apply masking to a spectrogram in the time domain. + + Note: + The dimension of the audio waveform to be processed needs to be (..., freq, time). + 鍦ㄦ椂鍩熶腑瀵瑰0璋卞浘搴旂敤鎺╄斀銆 + 娉細 + 瑕佸鐞嗙殑闊抽娉㈠舰鐨勭淮搴﹂渶瑕佷负锛堚︼紝freq锛宼ime锛夈 + + Args: + iid_masks (bool, optional): Whether to apply different masks to each example/channel. Default: False. + time_mask_param (int, optional): When `iid_masks` is True, length of the mask will be uniformly sampled + from [0, time_mask_param]; When `iid_masks` is False, directly use it as length of the mask. + The value should be in range of [0, time_length], where `time_length` is the length of audio waveform + in time domain. Default: 0. + mask_start (int, optional): Starting point to apply mask, only works when `iid_masks` is True. The value should + be in range of [0, time_length - time_mask_param], where `time_length` is the length of audio waveform + in time domain. Default: 0. + mask_value (float, optional): Value to assign to the masked columns. Default: 0.0. + + Raises: + TypeError: If `iid_masks` is not of type bool. + TypeError: If `time_mask_param` is not of type int. + ValueError: If `time_mask_param` is greater than the length of audio waveform in time domain. + TypeError: If `mask_start` is not of type int. + ValueError: If `mask_start` a negative number. + TypeError: If `mask_value` is not of type float. + ValueError: If `mask_value` is a negative number. + RuntimeError: If input tensor is not in shape of <..., freq, time>. + + Supported Platforms: + ``CPU`` + + Examples: + >>> import numpy as np + >>> + >>> waveform = np.random.random([4, 3, 2]) + >>> numpy_slices_dataset = ds.NumpySlicesDataset(data=waveform, column_names=["audio"]) + >>> transforms = [audio.TimeMasking(time_mask_param=1)] + >>> numpy_slices_dataset = numpy_slices_dataset.map(operations=transforms, input_columns=["audio"]) + + .. image:: time_masking_original.png + + .. image:: time_masking.png + """ + + @check_masking + def __init__(self, iid_masks=False, time_mask_param=0, mask_start=0, mask_value=0.0): + super().__init__() + self.iid_masks = iid_masks + self.time_mask_param = time_mask_param + self.mask_start = mask_start + self.mask_value = mask_value + + def parse(self): + return cde.TimeMaskingOperation(self.iid_masks, self.time_mask_param, self.mask_start, self.mask_value) + + +class TimeStretch(AudioTensorOperation): + """ + Stretch Short Time Fourier Transform (STFT) in time without modifying pitch for a given rate. + + Note: + The dimension of the audio waveform to be processed needs to be (..., freq, time, complex=2). + The first dimension represents the real part while the second represents the imaginary. + 鍦ㄤ笉淇敼缁欏畾閫熺巼鐨勯煶楂樼殑鎯呭喌涓嬶紝鍦ㄦ椂闂翠笂鎷変几鐭椂鍌呯珛鍙跺彉鎹€ + + 娉細 + 瑕佸鐞嗙殑闊抽娉㈠舰鐨勭淮搴﹂渶瑕佷负锛堚︼紝freq锛宼ime锛宑omplex=2锛夈 + 绗竴涓淮搴﹁〃绀哄疄閮紝鑰岀浜屼釜缁村害琛ㄧず铏氶儴銆 + Args: + hop_length (int, optional): Length of hop between STFT windows, i.e. the number of samples + between consecutive frames. Default: None, will use `n_freq - 1`. + n_freq (int, optional): Number of filter banks from STFT. Default: 201. + fixed_rate (float, optional): Rate to speed up or slow down by. Default: None, will keep + the original rate. + + Raises: + TypeError: If `hop_length` is not of type integer. + ValueError: If `hop_length` is not a positive number. + TypeError: If `n_freq` is not of type integer. + ValueError: If `n_freq` is not a positive number. + TypeError: If `fixed_rate` is not of type float. + ValueError: If `fixed_rate` is not a positive number. + RuntimeError: If input tensor is not in shape of <..., freq, num_frame, complex=2>. + + Supported Platforms: + ``CPU`` + + Examples: + >>> import numpy as np + >>> + >>> waveform = np.random.random([44, 10, 2]) + >>> numpy_slices_dataset = ds.NumpySlicesDataset(data=waveform, column_names=["audio"]) + >>> transforms = [audio.TimeStretch()] + >>> numpy_slices_dataset = numpy_slices_dataset.map(operations=transforms, input_columns=["audio"]) + + .. image:: time_stretch_rate1.5.png + + .. image:: time_stretch_original.png + + .. image:: time_stretch_rate0.8.png + """ + + @check_time_stretch + def __init__(self, hop_length=None, n_freq=201, fixed_rate=None): + super().__init__() + self.n_freq = n_freq + self.fixed_rate = fixed_rate + + n_fft = (n_freq - 1) * 2 + self.hop_length = hop_length if hop_length is not None else n_fft // 2 + self.fixed_rate = fixed_rate if fixed_rate is not None else 1 + + def parse(self): + return cde.TimeStretchOperation(self.hop_length, self.n_freq, self.fixed_rate) + + +class TrebleBiquad(AudioTensorOperation): + """ + Design a treble tone-control effect. Similar to SoX implementation. + 璁捐楂橀煶闊宠皟鎺у埗鏁堟灉銆 + Args: + sample_rate (int): Sampling rate of the waveform, e.g. 44100 (Hz), the value can't be zero. + gain (float): Desired gain at the boost (or attenuation) in dB. + central_freq (float, optional): Central frequency (in Hz) (default=3000). + Q(float, optional): Quality factor, https://en.wikipedia.org/wiki/Q_factor, range: (0, 1] (default=0.707). + + Examples: + >>> import numpy as np + >>> + >>> waveform = np.array([[1, 2, 3], [4, 5, 6]], dtype=np.float64) + >>> numpy_slices_dataset = ds.NumpySlicesDataset(data=waveform, column_names=["audio"]) + >>> transforms = [audio.TrebleBiquad(44100, 200.0)] + >>> numpy_slices_dataset = numpy_slices_dataset.map(operations=transforms, input_columns=["audio"]) + """ + + @check_treble_biquad + def __init__(self, sample_rate, gain, central_freq=3000, Q=0.707): + super().__init__() + self.sample_rate = sample_rate + self.gain = gain + self.central_freq = central_freq + self.quality_factor = Q + + def parse(self): + return cde.TrebleBiquadOperation(self.sample_rate, self.gain, self.central_freq, self.quality_factor) + + +class Vad(AudioTensorOperation): + """ + Attempt to trim silent background sounds from the end of the voice recording. + 灏濊瘯浠庤闊冲綍鍒剁粨鏉熸椂淇壀鏃犲0鑳屾櫙澹伴煶銆 + Args: + sample_rate (int): Sample rate of audio signal. + trigger_level (float, optional): The measurement level used to trigger activity detection (default=7.0). + trigger_time (float, optional): The time constant (in seconds) used to help ignore short sounds (default=0.25). + search_time (float, optional): The amount of audio (in seconds) to search for quieter/shorter sounds to include + prior to the detected trigger point (default=1.0). + allowed_gap (float, optional): The allowed gap (in seconds) between quiteter/shorter sounds to include prior to + the detected trigger point (default=0.25). + pre_trigger_time (float, optional): The amount of audio (in seconds) to preserve before the trigger point and + any found quieter/shorter bursts (default=0.0). + boot_time (float, optional): The time for the initial noise estimate (default=0.35). + noise_up_time (float, optional): Time constant used by the adaptive noise estimator, when the noise level is + increasing (default=0.1). + noise_down_time (float, optional): Time constant used by the adaptive noise estimator, when the noise level is + decreasing (default=0.01). + noise_reduction_amount (float, optional): The amount of noise reduction used in the detection algorithm + (default=1.35). + measure_freq (float, optional): The frequency of the algorithm鈥檚 processing (default=20.0). + measure_duration (float, optional): The duration of measurement (default=None, use twice the measurement + period). + measure_smooth_time (float, optional): The time constant used to smooth spectral measurements (default=0.4). + hp_filter_freq (float, optional): The "Brick-wall" frequency of high-pass filter applied at the input to the + detector algorithm (default=50.0). + lp_filter_freq (float, optional): The "Brick-wall" frequency of low-pass filter applied at the input to the + detector algorithm (default=6000.0). + hp_lifter_freq (float, optional): The "Brick-wall" frequency of high-pass lifter applied at the input to the + detector algorithm (default=150.0). + lp_lifter_freq (float, optional): The "Brick-wall" frequency of low-pass lifter applied at the input to the + detector algorithm (default=2000.0). + + Examples: + >>> import numpy as np + >>> + >>> waveform = np.random.random([2, 1000]) + >>> numpy_slices_dataset = ds.NumpySlicesDataset(data=waveform, column_names=["audio"]) + >>> transforms = [audio.Vad(sample_rate=600)] + >>> numpy_slices_dataset = numpy_slices_dataset.map(operations=transforms, input_columns=["audio"]) + """ + + @check_vad + def __init__(self, sample_rate, trigger_level=7.0, trigger_time=0.25, search_time=1.0, allowed_gap=0.25, + pre_trigger_time=0.0, boot_time=0.35, noise_up_time=0.1, noise_down_time=0.01, + noise_reduction_amount=1.35, measure_freq=20.0, measure_duration=None, measure_smooth_time=0.4, + hp_filter_freq=50.0, lp_filter_freq=6000.0, hp_lifter_freq=150.0, lp_lifter_freq=2000.0): + super().__init__() + self.sample_rate = sample_rate + self.trigger_level = trigger_level + self.trigger_time = trigger_time + self.search_time = search_time + self.allowed_gap = allowed_gap + self.pre_trigger_time = pre_trigger_time + self.boot_time = boot_time + self.noise_up_time = noise_up_time + self.noise_down_time = noise_down_time + self.noise_reduction_amount = noise_reduction_amount + self.measure_freq = measure_freq + self.measure_duration = measure_duration if measure_duration else 2.0 / measure_freq + self.measure_smooth_time = measure_smooth_time + self.hp_filter_freq = hp_filter_freq + self.lp_filter_freq = lp_filter_freq + self.hp_lifter_freq = hp_lifter_freq + self.lp_lifter_freq = lp_lifter_freq + + # 瀹氫箟涓涓悕涓簆arse鐨勬柟娉 +def parse(self): + # 杩斿洖涓涓猚de.VadOperation瀵硅薄锛岃瀵硅薄浣跨敤浠ヤ笅鍙傛暟杩涜鍒濆鍖栵細 + # - self.sample_rate: 閲囨牱鐜囷紝鐢ㄤ簬澹板娲诲姩妫娴嬫搷浣 + # - self.trigger_level: 瑙﹀彂绾у埆锛岀敤浜庢帶鍒朵綍鏃惰Е鍙戝0瀛︽椿鍔ㄦ娴 + # - self.trigger_time: 瑙﹀彂鏃堕棿锛岀敤浜庤缃Е鍙戝0瀛︽椿鍔ㄦ娴嬬殑鏃堕棿 + # - self.search_time: 鎼滅储鏃堕棿锛岀敤浜庤缃0瀛︽椿鍔ㄦ娴嬬殑鎼滅储鏃堕棿 + # - self.allowed_gap: 鍏佽鐨勯棿闅旀椂闂达紝鐢ㄤ簬璁剧疆澹板娲诲姩妫娴嬫湡闂村厑璁哥殑闈欓粯鏃堕棿 + # - self.pre_trigger_time: 棰勮Е鍙戞椂闂达紝鐢ㄤ簬璁剧疆澹板娲诲姩妫娴嬪墠鐨勬椂闂 + # - self.boot_time: 鍚姩鏃堕棿锛岀敤浜庤缃0瀛︽椿鍔ㄦ娴嬬殑鍚姩鏃堕棿 + # - self.noise_up_time: 鍣0涓婂崌鏃堕棿锛岀敤浜庤缃0瀛︽椿鍔ㄦ娴嬩腑鍣0涓婂崌鐨勬椂闂 + # - self.noise_down_time: 鍣0涓嬮檷鏃堕棿锛岀敤浜庤缃0瀛︽椿鍔ㄦ娴嬩腑鍣0涓嬮檷鐨勬椂闂 + # - self.noise_reduction_amount: 鍣0闄嶄綆閲忥紝鐢ㄤ簬鎺у埗鍣0鐨勯檷浣庣▼搴 + # - self.measure_freq: 娴嬮噺棰戠巼锛岀敤浜庤缃0瀛︽椿鍔ㄦ娴嬬殑娴嬮噺棰戠巼 + # - self.measure_duration: 娴嬮噺鎸佺画鏃堕棿锛岀敤浜庤缃0瀛︽椿鍔ㄦ娴嬬殑娴嬮噺鎸佺画鏃堕棿 + # - self.measure_smooth_time: 娴嬮噺骞虫粦鏃堕棿锛岀敤浜庡钩婊戝0瀛︽椿鍔ㄦ娴嬬殑娴嬮噺缁撴灉 + # - self.hp_filter_freq: 楂橀氭护娉㈠櫒棰戠巼锛岀敤浜庤缃0瀛︽椿鍔ㄦ娴嬩腑鐨勯珮閫氭护娉㈠櫒棰戠巼 + # - self.lp_filter_freq: 浣庨氭护娉㈠櫒棰戠巼锛岀敤浜庤缃0瀛︽椿鍔ㄦ娴嬩腑鐨勪綆閫氭护娉㈠櫒棰戠巼 + # - self.hp_lifter_freq: 楂橀氭彁鍗囧櫒棰戠巼锛岀敤浜庤缃0瀛︽椿鍔ㄦ娴嬩腑鐨勯珮閫氭彁鍗囧櫒棰戠巼 + # - self.lp_lifter_freq: 浣庨氭彁鍗囧櫒棰戠巼锛岀敤浜庤缃0瀛︽椿鍔ㄦ娴嬩腑鐨勪綆閫氭彁鍗囧櫒棰戠巼 + return cde.VadOperation(self.sample_rate, self.trigger_level, self.trigger_time, self.search_time, + self.allowed_gap, self.pre_trigger_time, self.boot_time, self.noise_up_time, + self.noise_down_time, self.noise_reduction_amount, self.measure_freq, + self.measure_duration, self.measure_smooth_time, self.hp_filter_freq, + self.lp_filter_freq, self.hp_lifter_freq, self.lp_lifter_freq) + +# 瀹氫箟涓涓悕涓篋E_C_GAIN_TYPE鐨勫瓧鍏革紝鍏朵腑鍖呭惈澶氫釜閿煎 +# 杩欎簺閿煎鐢ㄤ簬灏咷ainType鏋氫妇绫诲瀷鏄犲皠鍒癱de.GainType鏋氫妇绫诲瀷 +DE_C_GAIN_TYPE = {GainType.AMPLITUDE: cde.GainType.DE_GAIN_TYPE_AMPLITUDE, + GainType.POWER: cde.GainType.DE_GAIN_TYPE_POWER, + GainType.DB: cde.GainType.DE_GAIN_TYPE_DB} + + + +class Vol(AudioTensorOperation): + """ + Apply amplification or attenuation to the whole waveform. + 瀵规暣涓尝褰㈣繘琛屾斁澶ф垨琛板噺銆 + Args: + gain (float): Value of gain adjustment. + If gain_type = amplitude, gain stands for nonnegative amplitude ratio. + If gain_type = power, gain stands for power. + If gain_type = db, gain stands for decibels. + gain_type (GainType, optional): Type of gain, contains the following three enumeration values + GainType.AMPLITUDE, GainType.POWER and GainType.DB (default=GainType.AMPLITUDE). + + Examples: + >>> import numpy as np + >>> from mindspore.dataset.audio import GainType + >>> + >>> waveform = np.random.random([20, 30]) + >>> numpy_slices_dataset = ds.NumpySlicesDataset(data=waveform, column_names=["audio"]) + >>> transforms = [audio.Vol(gain=10, gain_type=GainType.DB)] + >>> numpy_slices_dataset = numpy_slices_dataset.map(operations=transforms, input_columns=["audio"]) + """ + + @check_vol + def __init__(self, gain, gain_type=GainType.AMPLITUDE): + super().__init__() + self.gain = gain + self.gain_type = gain_type + + # 瀹氫箟涓涓悕涓簆arse鐨勬柟娉 +def parse(self): + # 杩斿洖涓涓猚de.VadOperation瀵硅薄锛岃瀵硅薄浣跨敤浠ヤ笅鍙傛暟杩涜鍒濆鍖栵細 + # - self.sample_rate: 閲囨牱鐜囷紝鐢ㄤ簬澹板娲诲姩妫娴嬫搷浣 + # - self.trigger_level: 瑙﹀彂绾у埆锛岀敤浜庢帶鍒朵綍鏃惰Е鍙戝0瀛︽椿鍔ㄦ娴 + # - self.trigger_time: 瑙﹀彂鏃堕棿锛岀敤浜庤缃Е鍙戝0瀛︽椿鍔ㄦ娴嬬殑鏃堕棿 + # - self.search_time: 鎼滅储鏃堕棿锛岀敤浜庤缃0瀛︽椿鍔ㄦ娴嬬殑鎼滅储鏃堕棿 + # - self.allowed_gap: 鍏佽鐨勯棿闅旀椂闂达紝鐢ㄤ簬璁剧疆澹板娲诲姩妫娴嬫湡闂村厑璁哥殑闈欓粯鏃堕棿 + # - self.pre_trigger_time: 棰勮Е鍙戞椂闂达紝鐢ㄤ簬璁剧疆澹板娲诲姩妫娴嬪墠鐨勬椂闂 + # - self.boot_time: 鍚姩鏃堕棿锛岀敤浜庤缃0瀛︽椿鍔ㄦ娴嬬殑鍚姩鏃堕棿 + # - self.noise_up_time: 鍣0涓婂崌鏃堕棿锛岀敤浜庤缃0瀛︽椿鍔ㄦ娴嬩腑鍣0涓婂崌鐨勬椂闂 + # - self.noise_down_time: 鍣0涓嬮檷鏃堕棿锛岀敤浜庤缃0瀛︽椿鍔ㄦ娴嬩腑鍣0涓嬮檷鐨勬椂闂 + # - self.noise_reduction_amount: 鍣0闄嶄綆閲忥紝鐢ㄤ簬鎺у埗鍣0鐨勯檷浣庣▼搴 + # - self.measure_freq: 娴嬮噺棰戠巼锛岀敤浜庤缃0瀛︽椿鍔ㄦ娴嬬殑娴嬮噺棰戠巼 + # - self.measure_duration: 娴嬮噺鎸佺画鏃堕棿锛岀敤浜庤缃0瀛︽椿鍔ㄦ娴嬬殑娴嬮噺鎸佺画鏃堕棿 + # - self.measure_smooth_time: 娴嬮噺骞虫粦鏃堕棿锛岀敤浜庡钩婊戝0瀛︽椿鍔ㄦ娴嬬殑娴嬮噺缁撴灉 + # - self.hp_filter_freq: 楂橀氭护娉㈠櫒棰戠巼锛岀敤浜庤缃0瀛︽椿鍔ㄦ娴嬩腑鐨勯珮閫氭护娉㈠櫒棰戠巼 + # - self.lp_filter_freq: 浣庨氭护娉㈠櫒棰戠巼锛岀敤浜庤缃0瀛︽椿鍔ㄦ娴嬩腑鐨勪綆閫氭护娉㈠櫒棰戠巼 + # - self.hp_lifter_freq: 楂橀氭彁鍗囧櫒棰戠巼锛岀敤浜庤缃0瀛︽椿鍔ㄦ娴嬩腑鐨勯珮閫氭彁鍗囧櫒棰戠巼 + # - self.lp_lifter_freq: 浣庨氭彁鍗囧櫒棰戠巼锛岀敤浜庤缃0瀛︽椿鍔ㄦ娴嬩腑鐨勪綆閫氭彁鍗囧櫒棰戠巼 + return cde.VadOperation(self.sample_rate, self.trigger_level, self.trigger_time, self.search_time, + self.allowed_gap, self.pre_trigger_time, self.boot_time, self.noise_up_time, + self.noise_down_time, self.noise_reduction_amount, self.measure_freq, + self.measure_duration, self.measure_smooth_time, self.hp_filter_freq, + self.lp_filter_freq, self.hp_lifter_freq, self.lp_lifter_freq) + +# 瀹氫箟涓涓悕涓篋E_C_GAIN_TYPE鐨勫瓧鍏革紝鍏朵腑鍖呭惈澶氫釜閿煎 +# 杩欎簺閿煎鐢ㄤ簬灏咷ainType鏋氫妇绫诲瀷鏄犲皠鍒癱de.GainType鏋氫妇绫诲瀷 +DE_C_GAIN_TYPE = {GainType.AMPLITUDE: cde.GainType.DE_GAIN_TYPE_AMPLITUDE, + GainType.POWER: cde.GainType.DE_GAIN_TYPE_POWER, + GainType.DB: cde.GainType.DE_GAIN_TYPE_DB} + diff --git a/mindspore/ccsrc/transform-update/transforms/transforms.py b/mindspore/ccsrc/transform-update/transforms/transforms.py new file mode 100644 index 00000000000..840932a9381 --- /dev/null +++ b/mindspore/ccsrc/transform-update/transforms/transforms.py @@ -0,0 +1,1052 @@ +# Copyright 2019-2022 Huawei Technologies Co., Ltd +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ============================================================================== +""" +The module transforms provides common operations, including Compose, OneHot and TypeCast. +鎻愪緵甯歌鎿嶄綔锛屽寘鎷珻ompose銆丱neHot鍜孴ypeCast銆 +""" +import json +from abc import ABC + +import sys +from enum import IntEnum +import numpy as np + +import mindspore._c_dataengine as cde +from mindspore._c_expression import typing +from mindspore.common import dtype as mstype +import mindspore.dataset.transforms.c_transforms as c_transforms +import mindspore.dataset.transforms.py_transforms as py_transforms +import mindspore.dataset.vision.c_transforms as c_vision +from . import py_transforms_util as util +from .py_transforms_util import Implementation, FuncWrapper +from .validators import check_fill_value, check_slice_option, check_slice_op, check_one_hot_op, check_compose_call, \ + check_mask_op_new, check_pad_end, check_concat_type, check_random_transform_ops, check_plugin, check_type_cast +from ..core.datatypes import mstype_to_detype, nptype_to_detype +from ..vision.py_transforms_util import is_pil + + +class TensorOperation: + """ + Base class Tensor Ops + """ + + def __init__(self): + super().__init__() + self.implementation = None + self.callable_op_ = None + + def __call__(self, *input_tensor_list): + """ + Call method. + 璋冪敤鏂规硶銆 + """ + # 妫鏌ユ槸鍚︿娇鐢 Python 瀹炵幇鐨勬搷浣滐紝鎴栬呬紶鍏ョ殑鏄 PIL 鍥惧儚骞朵笖鏈 execute_py 鏂规硶 + if (self.implementation == Implementation.PY) or \ + (len(input_tensor_list) == 1 and is_pil(input_tensor_list[0]) and getattr(self, 'execute_py', None)): + # 濡傛灉鏄紝璋冪敤 execute_py 鏂规硶杩涜鎿嶄綔澶勭悊锛屽苟杩斿洖缁撴灉 + return self.execute_py(*input_tensor_list) + + # 濡傛灉涓嶆槸 Python 瀹炵幇鐨勬搷浣滐紝灏嗚緭鍏ュ紶閲忚浆鎹负 CDE 鐨 Tensor 瀵硅薄 + tensor_row = [] + for tensor in input_tensor_list: + try: + tensor_row.append(cde.Tensor(np.asarray(tensor))) + except (RuntimeError, TypeError): + # 濡傛灉杞崲澶辫触锛屾姏鍑虹被鍨嬮敊璇紓甯 + raise TypeError("Invalid user input. Got {}: {}, cannot be converted into tensor." \ + .format(type(tensor), tensor)) + + # 妫鏌ユ槸鍚﹀凡缁忓垱寤轰簡 callable_op_ 瀵硅薄锛屽鏋滄病鏈夛紝璋冪敤 parse 鏂规硶鍒涘缓瀹 + if not hasattr(self, 'callable_op_') or self.callable_op_ is None: + self.callable_op_ = cde.Execute(self.parse()) + + # 璋冪敤 callable_op_ 瀵硅薄澶勭悊杈撳叆寮犻噺锛屽苟鑾峰彇杈撳嚭寮犻噺鍒楄〃 + output_tensor_list = self.callable_op_(tensor_row) + + # 灏嗚緭鍑哄紶閲忓垪琛ㄨ浆鎹负 NumPy 鏁扮粍鍒楄〃 + output_numpy_list = [x.as_decoded_array() for x in output_tensor_list] + + # 濡傛灉杈撳嚭鍒楄〃涓彧鏈変竴涓厓绱狅紝鐩存帴杩斿洖璇ュ厓绱狅紝鍚﹀垯杩斿洖杈撳嚭鍒楄〃鐨勫厓缁 + return output_numpy_list[0] if len(output_numpy_list) == 1 else tuple(output_numpy_list) + + + @staticmethod + def parse(): + """parse function - not yet implemented""" + raise NotImplementedError("TensorOperation has to implement parse() method.") + + +class PyTensorOperation: + """ + Base Python Tensor Operations class + """ + + def __init__(self): + self.transforms = [] + self.output_type = None + + def __call__(self, img): + """ + Call method. + + Args: + img (PIL Image): Image to be augmented. + + Returns: + PIL Image, augmented image. + """ + return self.execute_py(img) + + @classmethod + # 瀹氫箟涓涓被鏂规硶锛岀敤浜庝粠 JSON 瀛楃涓插弽搴忓垪鍖栧垱寤烘搷浣滃璞 + def from_json(cls, json_string): + """ + Base from_json for Python tensor operations class + Python寮犻噺杩愮畻绫荤殑鍩虹from_json + """ + + # 浣跨敤 json.loads 灏 JSON 瀛楃涓茶В鏋愪负 JSON 瀵硅薄 + json_obj = json.loads(json_string) + + # 鍒涘缓涓涓柊鐨勬搷浣滃璞 + new_op = cls.__new__(cls) + + # 灏嗘搷浣滃璞$殑灞炴у瓧鍏歌缃负 JSON 瀵硅薄鐨勫睘鎬у瓧鍏 + new_op.__dict__ = json_obj + + # 濡傛灉 JSON 瀵硅薄涓寘鍚 "transforms" 閿 + if "transforms" in json_obj.keys(): + # 瀵逛簬鍏锋湁 transforms 浣滀负杈撳叆鐨勬搷浣滐紝闇瑕佷负姣忎釜 transform 璋冪敤 _from_json() 鏂规硶杩涜鍙嶅簭鍒楀寲 + transforms = [] + for json_op in json_obj["transforms"]: + # 浠庢搷浣滅殑 python_module 鑾峰彇妯″潡锛屽苟鏍规嵁 tensor_op_name 鍒涘缓涓涓柊鐨勬搷浣滃璞 + # 骞朵娇鐢 tensor_op_params 鍒濆鍖栨柊鎿嶄綔鐨勫睘鎬 + transforms.append(getattr( + sys.modules.get(json_op.get("python_module")), + json_op["tensor_op_name"]).from_json(json.dumps(json_op["tensor_op_params"]))) + new_op.transforms = transforms + + # 濡傛灉 JSON 瀵硅薄涓寘鍚 "output_type" 閿 + if "output_type" in json_obj.keys(): + # 灏 "output_type" 杞崲涓 NumPy 鏁版嵁绫诲瀷锛屽苟璁剧疆涓烘搷浣滃璞$殑灞炴 + output_type = np.dtype(json_obj["output_type"]) + new_op.output_type = output_type + + # 杩斿洖浠 JSON 瀛楃涓插垱寤虹殑鎿嶄綔瀵硅薄 + return new_op + +# 瀹氫箟涓涓柟娉曪紝鐢ㄤ簬灏嗘搷浣滃璞″簭鍒楀寲涓 JSON 鏍煎紡鐨勫瓧绗︿覆 + def to_json(self): + """ + Base to_json for Python tensor operations class + Python寮犻噺杩愮畻绫荤殑鍩虹to_json + """ + + + # 鍒涘缓涓涓┖鐨 JSON 瀵硅薄 + json_obj = {} + json_trans = {} + + # 濡傛灉鎿嶄綔瀵硅薄鐨勫睘鎬у瓧鍏镐腑鍖呭惈 "transforms" 閿 + if "transforms" in self.__dict__.keys(): + # 瀵逛簬鍏锋湁 transforms 浣滀负杈撳叆鐨勬搷浣滐紝闇瑕佽皟鐢 _to_json() 鏂规硶瀵规瘡涓 transform 杩涜搴忓垪鍖 + json_list = [] + for transform in self.transforms: + json_list.append(json.loads(transform.to_json())) + json_trans["transforms"] = json_list + + # 绉婚櫎鎿嶄綔瀵硅薄灞炴у瓧鍏镐腑鐨 "transforms" 閿 + self.__dict__.pop("transforms") + + # 濡傛灉鎿嶄綔瀵硅薄鐨勫睘鎬у瓧鍏镐腑鍖呭惈 "output_type" 閿 + if "output_type" in self.__dict__.keys(): + # 灏 "output_type" 杞崲涓 NumPy 鏁版嵁绫诲瀷鍚嶇О锛屽苟璁剧疆鍒 json_trans 涓 + json_trans["output_type"] = np.dtype( + self.__dict__["output_type"]).name + + # 绉婚櫎鎿嶄綔瀵硅薄灞炴у瓧鍏镐腑鐨 "output_type" 閿 + self.__dict__.pop("output_type") + + # 灏嗘搷浣滃璞$殑灞炴у瓧鍏告坊鍔犲埌 json_obj 涓綔涓 "tensor_op_params" + json_obj["tensor_op_params"] = self.__dict__ + + # 鍚堝苟 json_trans 鍒 "tensor_op_params" 涓 + json_obj.get("tensor_op_params").update(json_trans) + + # 娣诲姞鎿嶄綔瀵硅薄鐨勭被鍚嶅拰妯″潡鍚嶅埌 json_obj 涓 + json_obj["tensor_op_name"] = self.__class__.__name__ + json_obj["python_module"] = self.__class__.__module__ + + # 灏 json_obj 杞崲涓 JSON 鏍煎紡鐨勫瓧绗︿覆骞惰繑鍥 + return json.dumps(json_obj) + + + +class CompoundOperation(TensorOperation, PyTensorOperation, ABC): + """ + Compound Tensor Operations class + """ + + def __init__(self, transforms): + super(CompoundOperation, self).__init__() + self.transforms = [] + trans_with_imple = [] + for op in transforms: + if callable(op) and not hasattr(op, "implementation") and \ + not isinstance(op, c_transforms.TensorOperation) and \ + not isinstance(op, py_transforms.PyTensorOperation) and \ + not isinstance(op, c_vision.ImageTensorOperation): + op = util.FuncWrapper(op) + if hasattr(op, "implementation"): + if op.implementation is not None: + trans_with_imple.append(op) + else: + raise RuntimeError("Mixing old legacy c/py_transforms and new unified transforms is not allowed.") + self.transforms.append(op) + + if all([t.implementation == Implementation.PY for t in self.transforms]): + self.implementation = Implementation.PY + elif all([t.implementation is not None for t in self.transforms]): + self.implementation = Implementation.C + elif not trans_with_imple: + self.implementation = None + elif all([t.implementation == Implementation.PY for t in trans_with_imple]): + self.implementation = Implementation.PY + elif all([t.implementation == Implementation.C for t in trans_with_imple]): + self.implementation = Implementation.C + + @staticmethod + def parse(): + """parse function - not yet implemented""" + raise NotImplementedError("CompoundOperation has to implement parse() method.") + + def parse_transforms(self): + operations = [] + for op in self.transforms: + if op and getattr(op, 'parse', None): + operations.append(op.parse()) + else: + operations.append(op) + return operations + + +def not_random(function): + """ + Specify the function as "not random", i.e., it produces deterministic result. + A Python function can only be cached after it is specified as "not random". + """ + function.random = False + return function + + +class Compose(CompoundOperation): + """ + Compose a list of transforms into a single transform. + + .. Note:: + Compose takes a list of transformations either provided in transforms.py or from user-defined implementation; + each can be an initialized transformation class or a lambda function, as long as the output from the last + transformation is a single tensor of type numpy.ndarray. + + Args: + transforms (list): List of transformations to be applied. + + Raises: + TypeError: If `transforms` is not of type list. + ValueError: If `transforms` is empty. + TypeError: If elements of `transforms` are neither Python callable objects nor data + processing operations in transforms.py. + + Supported Platforms: + ``CPU`` + + Examples: + >>> compose = transforms.Compose([vision.Decode(), vision.RandomCrop(512)]) + >>> image_folder_dataset = image_folder_dataset.map(operations=compose) + >>> image_folder_dataset_dir = "/path/to/image_folder_dataset_directory" + >>> + >>> # create a dataset that reads all files in dataset_dir with 8 threads + >>> image_folder_dataset = ds.ImageFolderDataset(image_folder_dataset_dir, num_parallel_workers=8) + >>> # create a list of transformations to be applied to the image data + >>> transform = transforms.Compose([vision.Decode(to_pil=True), + ... vision.RandomHorizontalFlip(0.5), + ... vision.ToTensor(), + ... vision.Normalize((0.491, 0.482, 0.447), (0.247, 0.243, 0.262), is_hwc=False), + ... vision.RandomErasing()]) + >>> # apply the transform to the dataset through dataset.map function + >>> image_folder_dataset = image_folder_dataset.map(operations=transform, input_columns=["image"]) + >>> + >>> # Compose is also be invoked implicitly, by just passing in a list of ops + >>> # the above example then becomes: + >>> transforms_list = [vision.Decode(to_pil=True), + ... vision.RandomHorizontalFlip(0.5), + ... vision.ToTensor(), + ... vision.Normalize((0.491, 0.482, 0.447), (0.247, 0.243, 0.262), is_hwc=False), + ... vision.RandomErasing()] + >>> + >>> # apply the transform to the dataset through dataset.map() + >>> image_folder_dataset_1 = image_folder_dataset_1.map(operations=transforms_list, input_columns=["image"]) + >>> + >>> # Certain C++ and Python ops can be combined, but not all of them + >>> # An example of combined operations + >>> arr = [0, 1] + >>> dataset = ds.NumpySlicesDataset(arr, column_names=["cols"], shuffle=False) + >>> transformed_list = [transforms.OneHot(2), + ... transforms.Mask(transforms.Relational.EQ, 1)] + >>> dataset = dataset.map(operations=transformed_list, input_columns=["cols"]) + >>> + >>> # Here is an example of mixing vision ops + >>> import numpy as np + >>> op_list=[vision.Decode(), + ... vision.Resize((224, 244)), + ... vision.ToPIL(), + ... np.array, # need to convert PIL image to a NumPy array to pass it to C++ operation + ... vision.Resize((24, 24))] + >>> image_folder_dataset = image_folder_dataset.map(operations=op_list, input_columns=["image"]) + """ + + @check_random_transform_ops + def __init__(self, transforms): + super().__init__(transforms) + self.transforms = Compose.decompose(self.transforms) + if all(hasattr(transform, "random") and not transform.random for transform in self.transforms): + self.random = False + + @staticmethod + # 瀹氫箟涓涓柟娉曪紝鐢ㄤ簬灏嗗鍚堟搷浣滆В鏋勪负鍗曚釜鎿嶄綔锛屽苟杩斿洖瑙f瀯鍚庣殑鎿嶄綔鍒楄〃 + def decompose(operations): + """ + Remove all compose operation from the given list of operations. + 浠庣粰瀹氱殑鎿嶄綔鍒楄〃涓垹闄ゆ墍鏈夌粍鍚堟搷浣溿 + Args: + operations: list of transforms + + Returns: + list of operations without compose operations. + """ + + # 鍒涘缓涓涓┖鐨勬柊鎿嶄綔鍒楄〃锛岀敤浜庡瓨鍌ㄨВ鏋勫悗鐨勫崟涓搷浣 + new_operations = [] + + # 閬嶅巻浼犲叆鐨勬搷浣滃垪琛 + for op in operations: + # 濡傛灉褰撳墠鎿嶄綔鏄 Compose 绫诲瀷鐨勫鍚堟搷浣 + if isinstance(op, Compose): + # 閫掑綊璋冪敤 decompose 鏂规硶灏嗗鍚堟搷浣滆В鏋勪负鍗曚釜鎿嶄綔锛屽苟灏嗗叾娣诲姞鍒版柊鎿嶄綔鍒楄〃涓 + new_operations.extend(Compose.decompose(op.transforms)) + else: + # 濡傛灉褰撳墠鎿嶄綔涓嶆槸澶嶅悎鎿嶄綔锛岀洿鎺ュ皢鍏舵坊鍔犲埌鏂版搷浣滃垪琛ㄤ腑 + new_operations.append(op) + + # 杩斿洖瑙f瀯鍚庣殑鎿嶄綔鍒楄〃 + return new_operations + + @staticmethod + # 瀹氫箟涓涓柟娉曪紝鐢ㄤ簬灏嗚繛缁殑 C 瀹炵幇鐨勬搷浣滅粍鍚堜负涓涓 Compose 鎿嶄綔锛岃繑鍥炴柊鐨勬搷浣滃垪琛 + def reduce(operations): + """ + Wraps adjacent Python operations in a Compose to allow mixing of Python and C++ operations. + 鍦–ompose涓皝瑁呯浉閭荤殑Python鎿嶄綔锛屼互鍏佽娣峰悎Python鍜孋++鎿嶄綔銆 + Args: + operations (list): list of tensor operations. + + Returns: + list, the reduced list of operations. + """ + # 鍒涘缓涓涓┖鐨勬柊鎿嶄綔鍒楄〃 new_ops锛屼互鍙婅褰曡捣濮嬪拰缁撴潫绱㈠紩鐨勫彉閲 start_ind 鍜 end_ind + new_ops, start_ind, end_ind = [], 0, 0 + + # 閬嶅巻浼犲叆鐨勬搷浣滃垪琛 + for i, op in enumerate(operations): + # 濡傛灉褰撳墠鎿嶄綔鏄 C 瀹炵幇鐨勬搷浣滀笖涓嶆槸 FuncWrapper 绫诲瀷鐨勬搷浣 + if op.implementation == Implementation.C and not isinstance(op, FuncWrapper): + # 閲嶇疆璧峰鍜岀粨鏉熺储寮曪紝濡傛灉璧峰鍜岀粨鏉熺储寮曚笉鐩哥瓑锛屽垯璇存槑瀛樺湪杩炵画鐨 C 瀹炵幇鐨勬搷浣 + if start_ind != end_ind: + # 濡傛灉鍙湁涓涓搷浣滐紝鍒欑洿鎺ュ皢璇ユ搷浣滄坊鍔犲埌鏂版搷浣滃垪琛 + if end_ind == start_ind + 1: + composed_op = operations[start_ind] + else: + # 鍚﹀垯锛屽皢杩炵画鐨勬搷浣滅粍鍚堜负涓涓 Compose 鎿嶄綔锛屽疄鐜板垏鎹㈠埌 Python 瀹炵幇 + composed_op = Compose(operations[start_ind:end_ind]) + composed_op.implementation = Implementation.PY + # 灏嗙粍鍚堝悗鐨勬搷浣滄坊鍔犲埌鏂版搷浣滃垪琛 + new_ops.append(composed_op) + # 灏嗗綋鍓嶆搷浣滄坊鍔犲埌鏂版搷浣滃垪琛紝骞舵洿鏂拌捣濮嬪拰缁撴潫绱㈠紩 + new_ops.append(op) + start_ind, end_ind = i + 1, i + 1 + else: + # 濡傛灉褰撳墠鎿嶄綔涓嶆槸 C 瀹炵幇鐨勬搷浣滄垨鑰呮槸 FuncWrapper 绫诲瀷鐨勬搷浣滐紝澧炲姞缁撴潫绱㈠紩 + end_ind += 1 + + # 棰濆妫鏌ワ紝浠ラ槻鏈鍚庝竴涓搷浣滄槸 Python 瀹炵幇鐨勬搷浣 + if start_ind != end_ind: + if end_ind == start_ind + 1: + composed_op = operations[start_ind] + else: + composed_op = Compose(operations[start_ind:end_ind]) + composed_op.implementation = Implementation.PY + # 灏嗙粍鍚堝悗鐨勬搷浣滄坊鍔犲埌鏂版搷浣滃垪琛 + new_ops.append(composed_op) + + # 杩斿洖鏂扮殑鎿嶄綔鍒楄〃锛屽皢杩炵画鐨 C 瀹炵幇鐨勬搷浣滅粍鍚堜负涓涓 Compose 鎿嶄綔 + return new_ops + + + @check_compose_call + def execute_py(self, *args): + """ + Execute method. + + Returns: + lambda function, Lambda function that takes in an args to apply transformations on. + """ + return util.compose(self.transforms, *args) + + def parse(self): + operations = self.parse_transforms() + return cde.ComposeOperation(operations) + + +class Concatenate(TensorOperation): + """ + Tensor operation that concatenates all columns into a single tensor, only 1D tenspr is supported. + + Args: + axis (int, optional): Concatenate the tensors along given axis (Default=0). + prepend (numpy.array, optional): NumPy array to be prepended to the already concatenated tensors + (Default=None). + append (numpy.array, optional): NumPy array to be appended to the already concatenated tensors (Default=None). + + Raises: + TypeError: If `axis` is not of type int. + TypeError: If `prepend` is not of type numpy.ndarray. + TypeError: If `append` is not of type numpy.ndarray. + + Supported Platforms: + ``CPU`` + + Examples: + >>> import numpy as np + >>> # concatenate string + >>> prepend_tensor = np.array(["dw", "df"], dtype='S') + >>> append_tensor = np.array(["dwsdf", "df"], dtype='S') + >>> concatenate_op = transforms.Concatenate(0, prepend_tensor, append_tensor) + >>> data = [["This","is","a","string"]] + >>> dataset = ds.NumpySlicesDataset(data) + >>> dataset = dataset.map(operations=concatenate_op) + """ + + @check_concat_type + def __init__(self, axis=0, prepend=None, append=None): + super().__init__() + self.axis = axis + self.prepend = cde.Tensor(np.array(prepend)) if prepend is not None else prepend + self.append = cde.Tensor(np.array(append)) if append is not None else append + self.implementation = Implementation.C + + def parse(self): + return cde.ConcatenateOperation(self.axis, self.prepend, self.append) + + +class Duplicate(TensorOperation): + """ + Duplicate the input tensor to output, only support transform one column each time. + + Raises: + RuntimeError: If given tensor has two columns. + + Supported Platforms: + ``CPU`` + + Examples: + >>> # Data before + >>> # | x | + >>> # +---------+ + >>> # | [1,2,3] | + >>> # +---------+ + >>> data = [[1,2,3]] + >>> numpy_slices_dataset = ds.NumpySlicesDataset(data, ["x"]) + >>> numpy_slices_dataset = numpy_slices_dataset.map(operations=transforms.Duplicate(), + ... input_columns=["x"], + ... output_columns=["x", "y"], + ... column_order=["x", "y"]) + >>> # Data after + >>> # | x | y | + >>> # +---------+---------+ + >>> # | [1,2,3] | [1,2,3] | + >>> # +---------+---------+ + """ + + def __init__(self): + super().__init__() + self.implementation = Implementation.C + + def parse(self): + return cde.DuplicateOperation() + + +class Fill(TensorOperation): + """ + Tensor operation to fill all elements in the tensor with the specified value. + The output tensor will have the same shape and type as the input tensor. + + Args: + fill_value (Union[str, bytes, int, float, bool]) : scalar value + to fill the tensor with. + + Raises: + TypeError: If `fill_value` is not of type str, float, bool, int or bytes. + + Supported Platforms: + ``CPU`` + + + Examples: + >>> import numpy as np + >>> # generate a 1D integer numpy array from 0 to 4 + >>> def generator_1d(): + ... for i in range(5): + ... yield (np.array([i]),) + >>> generator_dataset = ds.GeneratorDataset(generator_1d, column_names="col1") + >>> # [[0], [1], [2], [3], [4]] + >>> fill_op = transforms.Fill(3) + >>> generator_dataset = generator_dataset.map(operations=fill_op) + >>> # [[3], [3], [3], [3], [3]] + """ + + @check_fill_value + def __init__(self, fill_value): + super().__init__() + self.fill_value = cde.Tensor(np.array(fill_value)) + self.implementation = Implementation.C + + def parse(self): + return cde.FillOperation(self.fill_value) + + +class Mask(TensorOperation): + r""" + Mask content of the input tensor with the given predicate. + Any element of the tensor that matches the predicate will be evaluated to True, otherwise False. + + Args: + operator (Relational): relational operators, it can be any of [Relational.EQ, Relational.NE, Relational.LT, + Relational.GT, Relational.LE, Relational.GE], take Relational.EQ as example, EQ refers to equal. + constant (Union[str, int, float, bool]): Constant to be compared to. + dtype (mindspore.dtype, optional): Type of the generated mask. Default: mindspore.dtype.bool\_. + + Raises: + TypeError: `operator` is not of type Relational. + TypeError: `constant` is not of type string int, float or bool. + TypeError: `dtype` is not of type mindspore.dtype. + + Supported Platforms: + ``CPU`` + + Examples: + >>> from mindspore.dataset.transforms import Relational + >>> # Data before + >>> # | col | + >>> # +---------+ + >>> # | [1,2,3] | + >>> # +---------+ + >>> data = [[1, 2, 3]] + >>> numpy_slices_dataset = ds.NumpySlicesDataset(data, ["col"]) + >>> numpy_slices_dataset = numpy_slices_dataset.map(operations=transforms.Mask(Relational.EQ, 2)) + >>> # Data after + >>> # | col | + >>> # +--------------------+ + >>> # | [False,True,False] | + >>> # +--------------------+ + """ + + @check_mask_op_new + def __init__(self, operator, constant, dtype=mstype.bool_): + super().__init__() + self.operator = operator + self.dtype = mstype_to_detype(dtype) + self.constant = cde.Tensor(np.array(constant)) + self.implementation = Implementation.C + + def parse(self): + return cde.MaskOperation(DE_C_RELATIONAL.get(self.operator), self.constant, self.dtype) + + +class OneHot(TensorOperation): + """ + Tensor operation to apply one hot encoding. + + Args: + num_classes (int): Number of classes of objects in dataset. + It should be larger than the largest label number in the dataset. + smoothing_rate (float, optional): Adjustable hyperparameter for label smoothing level. + (Default=0.0 means no smoothing is applied.) + + Raises: + TypeError: `num_classes` is not of type int. + TypeError: `smoothing_rate` is not of type float or int. + ValueError: `smoothing_rate` is not in range [0.0, 1.0]. + RuntimeError: Input tensor is not of type int. + RuntimeError: Input tensor is not a 1-D tensor. + + Supported Platforms: + ``CPU`` + + Examples: + >>> # Assume that dataset has 10 classes, thus the label ranges from 0 to 9 + >>> onehot_op = transforms.OneHot(num_classes=10) + >>> mnist_dataset = mnist_dataset.map(operations=onehot_op, input_columns=["label"]) + """ + + @check_one_hot_op + def __init__(self, num_classes, smoothing_rate=0.0): + super().__init__() + self.num_classes = num_classes + self.random = False + self.smoothing_rate = smoothing_rate + + def parse(self): + return cde.OneHotOperation(self.num_classes, self.smoothing_rate) + + +class PadEnd(TensorOperation): + """ + Pad input tensor according to pad_shape, input tensor needs to have same rank. + + Args: + pad_shape (list(int)): List of integers representing the shape needed. Dimensions that set to `None` will + not be padded (i.e., original dim will be used). Shorter dimensions will truncate the values. + pad_value (Union[str, bytes, int, float, bool], optional): Value used to pad. Default to 0 or empty + string in case of tensors of strings. + + Raises: + TypeError: If `pad_shape` is not of type list. + TypeError: If `pad_value` is not of type str, float, bool, int or bytes. + TypeError: If elements of `pad_shape` is not of type int. + ValueError: If elements of `pad_shape` is not of positive. + + Supported Platforms: + ``CPU`` + + Examples: + >>> # Data before + >>> # | col | + >>> # +---------+ + >>> # | [1,2,3] | + >>> # +---------| + >>> data = [[1, 2, 3]] + >>> numpy_slices_dataset = ds.NumpySlicesDataset(data, ["col"]) + >>> numpy_slices_dataset = numpy_slices_dataset.map(operations=transforms.PadEnd(pad_shape=[4], + ... pad_value=10)) + >>> # Data after + >>> # | col | + >>> # +------------+ + >>> # | [1,2,3,10] | + >>> # +------------| + """ + + @check_pad_end + def __init__(self, pad_shape, pad_value=None): + super().__init__() + self.pad_shape = cde.TensorShape(pad_shape) + self.pad_value = cde.Tensor(np.array(pad_value)) if pad_value is not None else pad_value + self.implementation = Implementation.C + + def parse(self): + return cde.PadEndOperation(self.pad_shape, self.pad_value) + + +class Plugin(TensorOperation): + """ + Plugin support for MindData. Use this class to dynamically load a .so file (shared library) and execute its symbols. + + Args: + lib_path (str): Path to .so file which is compiled to support MindData plugin. + func_name (str): Name of the function to load from the .so file. + user_args (str, optional): Serialized args to pass to the plugin. Only needed if "func_name" requires one. + + Raises: + TypeError: If `lib_path` is not of type string. + TypeError: If `func_name` is not of type string. + TypeError: If `user_args` is not of type string. + + Supported Platforms: + ``CPU`` + + Examples: + >>> plugin = transforms.Plugin("pluginlib.so", "PluginDecode") + >>> image_folder_dataset = image_folder_dataset.map(operations=plugin) + """ + + @check_plugin + def __init__(self, lib_path, func_name, user_args=None): + super().__init__() + self.lib_path = lib_path + self.func_name = func_name + self.user_args = str() if (user_args is None) else user_args + self.implementation = Implementation.C + + def parse(self): + return cde.PluginOperation(self.lib_path, self.func_name, self.user_args) + + +class RandomApply(CompoundOperation): + """ + Randomly perform a series of transforms with a given probability. + + Args: + transforms (list): List of transformations to be applied. + prob (float, optional): The probability to apply the transformation list (default=0.5). + + Raises: + TypeError: If `transforms` is not of type list. + ValueError: If `transforms` is empty. + TypeError: If elements of `transforms` are neither Python callable objects nor data + processing operations in transforms.py. + TypeError: If `prob` is not of type float. + ValueError: If `prob` is not in range [0.0, 1.0]. + + Supported Platforms: + ``CPU`` + + Examples: + >>> from mindspore.dataset.transforms import Compose + >>> transforms_list = [vision.RandomHorizontalFlip(0.5), + ... vision.Normalize((0.491, 0.482, 0.447), (0.247, 0.243, 0.262)), + ... vision.RandomErasing()] + >>> composed_transform = Compose([vision.Decode(to_pil=True), + ... transforms.RandomApply(transforms_list, prob=0.6), + ... vision.ToTensor()]) + >>> image_folder_dataset = image_folder_dataset.map(operations=composed_transform, input_columns=["image"]) + """ + + @check_random_transform_ops + def __init__(self, transforms, prob=0.5): + super().__init__(transforms) + self.prob = prob + + def execute_py(self, img): + """ + Execute method. + + Args: + img (PIL image): Image to be randomly applied a list transformations. + + Returns: + img (PIL image), Transformed image. + """ + return util.random_apply(img, self.transforms, self.prob) + + def parse(self): + operations = self.parse_transforms() + return cde.RandomApplyOperation(self.prob, operations) + + +class RandomChoice(CompoundOperation): + """ + Randomly select one transform from a list of transforms to perform operation. + + Args: + transforms (list): List of transformations to be chosen from to apply. + + Raises: + TypeError: If `transforms` is not of type list. + ValueError: If `transforms` is empty. + TypeError: If elements of `transforms` are neither Python callable objects nor data + processing operations in transforms.py. + + Supported Platforms: + ``CPU`` + + Examples: + >>> from mindspore.dataset.transforms import Compose + >>> transforms_list = [vision.RandomHorizontalFlip(0.5), + ... vision.Normalize((0.491, 0.482, 0.447), (0.247, 0.243, 0.262)), + ... vision.RandomErasing()] + >>> composed_transform = Compose([vision.Decode(), + ... transforms.RandomChoice(transforms_list), + ... vision.ToTensor()]) + >>> image_folder_dataset = image_folder_dataset.map(operations=composed_transform, input_columns=["image"]) + + """ + + @check_random_transform_ops + def __init__(self, transforms): + super().__init__(transforms) + + def execute_py(self, img): + """ + Execute method. + + Args: + img (PIL image): Image to be applied transformation. + + + Returns: + img (PIL image), Transformed image. + """ + return util.random_choice(img, self.transforms) + + def parse(self): + operations = self.parse_transforms() + return cde.RandomChoiceOperation(operations) + + +class RandomOrder(PyTensorOperation): + """ + Perform a series of transforms to the input image in a random order. + + Args: + transforms (list): List of the transformations to apply. + + Raises: + TypeError: If `transforms` is not of type list. + TypeError: If elements of `transforms` are neither Python callable objects nor data + processing operations in mindspore.dataset.transforms.transforms. + ValueError: If `transforms` is empty. + + Supported Platforms: + ``CPU`` + + Examples: + >>> from mindspore.dataset.transforms import Compose + >>> transforms_list = [vision.RandomHorizontalFlip(0.5), + ... vision.Normalize((0.491, 0.482, 0.447), (0.247, 0.243, 0.262)), + ... vision.RandomErasing()] + >>> composed_transform = Compose([vision.Decode(to_pil=False), + ... transforms.RandomOrder(transforms_list), + ... vision.ToTensor()]) + >>> image_folder_dataset = image_folder_dataset.map(operations=composed_transform, input_columns=["image"]) + """ + + @check_random_transform_ops + def __init__(self, transforms): + super().__init__() + self.transforms = transforms + self.implementation = Implementation.PY + + def execute_py(self, img): + """ + Execute method. + + Args: + img (PIL image): Image to apply transformations in a random order. + + Returns: + img (PIL image), Transformed image. + """ + return util.random_order(img, self.transforms) + + +class Relational(IntEnum): + """ + Relationship operator. + + Possible enumeration values are: Relational.EQ, Relational.NE, Relational.GT, Relational.GE, Relational.LT, + Relational.LE. + + - Relational.EQ: refers to Equality. + - Relational.NE: refers not equal, or Inequality. + - Relational.GT: refers to Greater than. + - Relational.GE: refers to Greater than or equal to. + - Relational.LT: refers to Less than. + - Relational.LE: refers to Less than or equal to. + """ + EQ = 0 + NE = 1 + GT = 2 + GE = 3 + LT = 4 + LE = 5 + + +DE_C_RELATIONAL = {Relational.EQ: cde.RelationalOp.EQ, + Relational.NE: cde.RelationalOp.NE, + Relational.GT: cde.RelationalOp.GT, + Relational.GE: cde.RelationalOp.GE, + Relational.LT: cde.RelationalOp.LT, + Relational.LE: cde.RelationalOp.LE} + + +class _SliceOption(cde.SliceOption): + """ + Internal class SliceOption to be used with SliceOperation + + Args: + _SliceOption(Union[int, list(int), slice, None, Ellipsis, bool, _SliceOption]): + + 1. :py:obj:`int`: Slice this index only along the dimension. Negative index is supported. + 2. :py:obj:`list(int)`: Slice these indices along the dimension. Negative indices are supported. + 3. :py:obj:`slice`: Slice the generated indices from the slice object along the dimension. + 4. :py:obj:`None`: Slice the whole dimension. Similar to :py:obj:`:` in Python indexing. + 5. :py:obj:`Ellipsis`: Slice the whole dimension. Similar to :py:obj:`:` in Python indexing. + 6. :py:obj:`boolean`: Slice the whole dimension. Similar to :py:obj:`:` in Python indexing. + """ + + @check_slice_option + def __init__(self, slice_option): + if isinstance(slice_option, int) and not isinstance(slice_option, bool): + slice_option = [slice_option] + elif slice_option is Ellipsis: + slice_option = True + elif slice_option is None: + slice_option = True + super().__init__(slice_option) + + +class Slice(TensorOperation): + """ + Slice operation to extract a tensor out using the given n slices. + + The functionality of Slice is similar to NumPy's indexing feature (Currently only rank-1 tensors are supported). + + Args: + slices (Union[int, list[int], slice, None, Ellipsis]): + Maximum `n` number of arguments to slice a tensor of rank `n` . + One object in slices can be one of: + + 1. :py:obj:`int`: Slice this index only along the first dimension. Negative index is supported. + 2. :py:obj:`list(int)`: Slice these indices along the first dimension. Negative indices are supported. + 3. :py:obj:`slice`: Slice the generated indices from the + `slice `_ object along the + first dimension. Similar to start:stop:step. + 4. :py:obj:`None`: Slice the whole dimension. Similar to :py:obj:`[:]` in Python indexing. + 5. :py:obj:`Ellipsis`: Slice the whole dimension, same result with `None`. + + Raises: + TypeError: If `slices` is not of type int, list[int], :py:obj:`slice`, :py:obj:`None` or :py:obj:`Ellipsis`. + + Supported Platforms: + ``CPU`` + + Examples: + >>> # Data before + >>> # | col | + >>> # +---------+ + >>> # | [1,2,3] | + >>> # +---------| + >>> data = [[1, 2, 3]] + >>> numpy_slices_dataset = ds.NumpySlicesDataset(data, ["col"]) + >>> # slice indices 1 and 2 only + >>> numpy_slices_dataset = numpy_slices_dataset.map(operations=transforms.Slice(slice(1,3))) + >>> # Data after + >>> # | col | + >>> # +---------+ + >>> # | [2,3] | + >>> # +---------| + """ + + @check_slice_op + def __init__(self, *slices): + super().__init__() + slice_input_ = list(slices) + slice_input_ = [_SliceOption(slice_dim) for slice_dim in slice_input_] + self.slice_input_ = slice_input_ + self.implementation = Implementation.C + + def parse(self): + return cde.SliceOperation(self.slice_input_) + + +class TypeCast(TensorOperation): + """ + Tensor operation to cast to a given MindSpore data type or NumPy data type. + + Note: + This operation supports running on Ascend or GPU platforms by Offload. + + Args: + data_type (Union[mindspore.dtype, numpy.dtype]): mindspore.dtype or numpy.dtype (e.g. :class:`numpy.float32`) + to be cast to. + + Raises: + TypeError: If `data_type` is not of MindSpore data type bool, int, float, string or type :class:`numpy.dtype`. + + Supported Platforms: + ``CPU`` ``Ascend`` ``GPU`` + + Examples: + >>> import numpy as np + >>> from mindspore import dtype as mstype + >>> + >>> # Generate 1d int numpy array from 0 - 63 + >>> def generator_1d(): + ... for i in range(64): + ... yield (np.array([i]),) + >>> + >>> dataset = ds.GeneratorDataset(generator_1d, column_names='col') + >>> type_cast_op = transforms.TypeCast(mstype.int32) + >>> dataset = dataset.map(operations=type_cast_op) + """ + + @check_type_cast + def __init__(self, data_type): + super().__init__() + if isinstance(data_type, typing.Type): + data_type = mstype_to_detype(data_type) + else: + data_type = nptype_to_detype(data_type) + self.data_type = str(data_type) + self.implementation = Implementation.C + + def parse(self): + return cde.TypeCastOperation(self.data_type) + + +class Unique(TensorOperation): + """ + Perform the unique operation on the input tensor, only support transform one column each time. + + Return 3 tensor: unique output tensor, index tensor, count tensor. + + - Output tensor contains all the unique elements of the input tensor + in the same order that they occur in the input tensor. + - Index tensor that contains the index of each element of the input tensor in the unique output tensor. + - Count tensor that contains the count of each element of the output tensor in the input tensor. + + Note: + Call batch op before calling this function. + + Raises: + RuntimeError: If given Tensor has two columns. + + Supported Platforms: + ``CPU`` + + Examples: + >>> # Data before + >>> # | x | + >>> # +--------------------+ + >>> # | [[0,1,2], [1,2,3]] | + >>> # +--------------------+ + >>> data = [[[0,1,2], [1,2,3]]] + >>> dataset = ds.NumpySlicesDataset(data, ["x"]) + >>> dataset = dataset.map(operations=transforms.Unique(), + ... input_columns=["x"], + ... output_columns=["x", "y", "z"], + ... column_order=["x", "y", "z"]) + >>> # Data after + >>> # | x | y |z | + >>> # +---------+-----------------+---------+ + >>> # | [0,1,2,3] | [0,1,2,1,2,3] | [1,2,2,1] + >>> # +---------+-----------------+---------+ + """ + + def __init__(self): + super().__init__() + self.implementation = Implementation.C + + def parse(self): + return cde.UniqueOperation() diff --git a/mindspore/ccsrc/transform-update/util.cc b/mindspore/ccsrc/transform-update/util.cc new file mode 100644 index 00000000000..d66b03b5b1c --- /dev/null +++ b/mindspore/ccsrc/transform-update/util.cc @@ -0,0 +1,524 @@ +/** + * Copyright 2019 Huawei Technologies Co., Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "include/transform/graph_ir/util.h" + +#include +#include + +#include "securec/include/securec.h" +#include "include/common/utils/convert_utils.h" +#include "include/common/utils/utils.h" + +namespace mindspore { +namespace transform { +using std::make_shared; +using std::shared_ptr; +using std::string; +using std::vector; + +const size_t kErrorSize = 0; + +//该函数主要用于生成一个包含相同 数据的 int64_tvector,方便在一些情况下使用。 +vector TransformUtil::ConvertIntToList(int64_t data, int size) { + vector list{}; //创建一个空的 类型的 vector list。 + if (size <= 0) { //检查 size 是否小于等于 0 + MS_LOG(WARNING) << "size <= 0"; //如果是,输出警告日志并返回空的 list。 + return list; + } + for (int i = 0; i < size; ++i) { // 使用 for 循环将 data 复制 size 次,并将每次复制的结果添加到list中。 + list.push_back(data); + } + return list;//循环结束后,返回存储有复制结果的list +} + +static std::map datatype_trans_map = { + {MeDataType::kNumberTypeFloat16, GeDataType::DT_FLOAT16}, {MeDataType::kNumberTypeFloat32, GeDataType::DT_FLOAT}, + {MeDataType::kNumberTypeFloat64, GeDataType::DT_DOUBLE}, {MeDataType::kNumberTypeInt8, GeDataType::DT_INT8}, + {MeDataType::kNumberTypeInt16, GeDataType::DT_INT16}, {MeDataType::kNumberTypeInt32, GeDataType::DT_INT32}, + {MeDataType::kNumberTypeInt64, GeDataType::DT_INT64}, {MeDataType::kNumberTypeUInt8, GeDataType::DT_UINT8}, + {MeDataType::kNumberTypeUInt16, GeDataType::DT_UINT16}, {MeDataType::kNumberTypeUInt32, GeDataType::DT_UINT32}, + {MeDataType::kNumberTypeUInt64, GeDataType::DT_UINT64}, {MeDataType::kNumberTypeBool, GeDataType::DT_BOOL}}; + +// ConvertDataType 函数用于将 MeDataType 类型的数据转换为 GeDataType 类型。 +GeDataType TransformUtil::ConvertDataType(const MeDataType &type) { + MS_LOG(DEBUG) << "Convert me data type: " << TypeIdLabel(type) << " to ge data type"; + // 在 datatype_trans_map 中查找 MeDataType 对应的 GeDataType。 + if (datatype_trans_map.find(type) != datatype_trans_map.end()) { + return datatype_trans_map[type]; + } else { // 如果找不到对应的映射关系,则返回 GeDataType::DT_UNDEFINED。 + return GeDataType::DT_UNDEFINED; + } +} + +static std::map datatype_size_map = { + {MeDataType::kNumberTypeFloat16, sizeof(float) / 2}, {MeDataType::kNumberTypeFloat32, sizeof(float)}, // 1/2 of float + {MeDataType::kNumberTypeFloat64, sizeof(double)}, {MeDataType::kNumberTypeInt8, sizeof(int8_t)}, + {MeDataType::kNumberTypeInt16, sizeof(int16_t)}, {MeDataType::kNumberTypeInt32, sizeof(int32_t)}, + {MeDataType::kNumberTypeInt64, sizeof(int64_t)}, {MeDataType::kNumberTypeUInt8, sizeof(uint8_t)}, + {MeDataType::kNumberTypeUInt16, sizeof(uint16_t)}, {MeDataType::kNumberTypeUInt32, sizeof(uint32_t)}, + {MeDataType::kNumberTypeUInt64, sizeof(uint64_t)}, {MeDataType::kNumberTypeBool, sizeof(bool)}}; + +// GetDataTypeSize 函数用于获取给定 MeDataType 类型数据在内存中占据的字节大小。 +size_t TransformUtil::GetDataTypeSize(const MeDataType &type) { + if (datatype_size_map.find(type) != datatype_size_map.end()) { // 在 datatype_size_map 中查找 MeDataType 对应的字节大小。 + return datatype_size_map[type]; + } else { // 如果找不到对应的大小,输出错误日志并返回一个特定的错误大小(kErrorSize) + MS_LOG(ERROR) << "Illegal tensor data type!"; + return kErrorSize; + } +} + +// ConvertFormat 函数用于将给定的字符串格式 `format` 转换为对应的 GeFormat 枚举类型。 +GeFormat TransformUtil::ConvertFormat(const string &format) { + // 通过比较字符串格式 `format`,判断对应的 GeFormat 枚举类型,并进行转换并返回。 + if (format == kOpFormat_NCHW) { + return GeFormat::FORMAT_NCHW; + } else if (format == kOpFormat_NDHWC) { + return GeFormat::FORMAT_NDHWC; + } else if (format == kOpFormat_NCDHW) { + return GeFormat::FORMAT_NCDHW; + } else if (format == kOpFormat_DHWNC) { + return GeFormat::FORMAT_DHWNC; + } else if (format == kOpFormat_DHWCN) { + return GeFormat::FORMAT_DHWCN; + } else if (format == kOpFormat_NC1HWC0) { + return GeFormat::FORMAT_NC1HWC0; + } else if (format == kOpFormat_NHWC) { + return GeFormat::FORMAT_NHWC; + } else if (format == kOpFormat_HWCN) { + return GeFormat::FORMAT_HWCN; + } else if (format == kOpFormat_ND) { + return GeFormat::FORMAT_ND; + } else { // 如果 `format` 不是支持的数据格式,输出错误日志并返回默认的 GeFormat::FORMAT_ND 格式。 + MS_LOG(ERROR) << "Illegal tensor data format: (" << format << "). Use ND format instead."; + return GeFormat::FORMAT_ND; + } +} + +static int64_t IntegerCastFunc(size_t temp) { return static_cast(temp); } + +// GetGeTensorDesc 函数用于根据给定的 MeTensor 的形状(ShapeVector)、数据类型(MeDataType)和格式(format), +// 创建对应的 GeTensorDesc 对象,并返回一个指向该对象的 shared_ptr。 +// GeTensorDesc 是 Ascend AI Core 引擎中定义的张量描述类,用于描述张量的形状、数据类型和数据格式。 +std::shared_ptr TransformUtil::GetGeTensorDesc(const ShapeVector &me_shape, const MeDataType &me_type, + const std::string &format) { + // convert me shape to ge shape + // 将 MeTensor 的形状(ShapeVector)转换为 GeShape 对象(std::vector) + std::vector ge_shape; + + if (me_shape.size() == 1) { + ge_shape.push_back(static_cast(me_shape[0])); + } else { + ge_shape.resize(me_shape.size()); + (void)std::transform(me_shape.begin(), me_shape.end(), ge_shape.begin(), IntegerCastFunc); + } + + GeShape shape(ge_shape); + if (shape.GetDimNum() == 0) { // 如果 GeShape 对象的维度数为 0,则输出提示信息日志。 + MS_LOG(INFO) << "The dims size of Ge tensor is zero"; + } + // convert me format to ge format + // 将 MeTensor 的格式(format)转换为 GeFormat 枚举类型。 + GeFormat ge_format = ConvertFormat(format); + if (ge_format == GeFormat::FORMAT_ND) { + MS_LOG(INFO) << "Set ND data format"; + } + // convert me datatype to ge datatype + // 将 MeTensor 的数据类型(me_type)转换为 GeDataType 枚举类型。 + GeDataType data_type = ConvertDataType(me_type); + if (data_type == GeDataType::DT_UNDEFINED) { // 如果数据类型转换失败,则输出错误日志,并返回空指针。 + MS_LOG(ERROR) << "undefined data type :" << me_type; + return nullptr; + } + // 创建 GeTensorDesc 对象,并设置相应的形状、数据类型和数据格式信息。 + auto desc = std::make_shared(shape, ge_format, data_type); + if (desc == nullptr) { + MS_LOG(ERROR) << "Create GeTensorDesc failed!"; + return nullptr; + } + // 设置实际维度数量,即 GeTensorDesc 对象的形状维度数。 + MS_LOG(INFO) << "SetRealDimCnt is :" << me_shape.size(); + desc->SetRealDimCnt(SizeToInt(me_shape.size())); + return desc; // 返回指向创建的 GeTensorDesc 对象的 shared_ptr。 +} + +// if failed, return empty vector. +// ConvertInputTensors 函数用于将给定的 MeTensor 列表(me_tensors)转换为对应的 GeTensor 列表,并返回转换后的结果。 +// MeTensor 是 MindSpore 引擎中定义的张量类,用于存储张量的数据和相关信息。 +// GeTensor 是 Ascend AI Core 引擎中定义的张量类,用于存储张量的数据和相关信息。 +// 函数遍历输入的 MeTensor 列表,对每个 MeTensor 进行转换,并将转换后的 GeTensor 添加到结果列表 ge_tensors 中。 +// 如果在转换过程中遇到错误,则输出相应的错误日志,并返回空列表。 +std::vector TransformUtil::ConvertInputTensors(const std::vector &me_tensors, + const std::string &format) { + std::vector ge_tensors; + // 遍历输入的 MeTensor 列表,对每个 MeTensor 进行转换,并将转换后的 GeTensor 添加到结果列表 ge_tensors 中。 + for (size_t index = 0; index < me_tensors.size(); index++) { + MS_EXCEPTION_IF_NULL(me_tensors[index]); + // 输出当前 MeTensor 的数据大小、形状和数据类型信息。 + MS_LOG(INFO) << "me_tensor " << index << " 's data size is: " << me_tensors[index]->DataSize(); + auto shape = me_tensors[index]->shape(); + std::string shape_str; + for (size_t i = 0; i < shape.size(); i++) { + shape_str += std::to_string(shape[i]); + shape_str += " "; + } + MS_LOG(INFO) << "me_tensor " << index << " 's shape is: { " << shape_str << "}"; + MS_LOG(INFO) << "me_tensor " << index << " 's type is: " << me_tensors[index]->data_type(); + // 调用 ConvertTensor 函数将当前的 MeTensor 转换为对应的 GeTensor。 + auto ge_tensor_ptr = TransformUtil::ConvertTensor(me_tensors[index], format); + if (ge_tensor_ptr != nullptr) { // 如果转换成功,则将转换后的 GeTensor 添加到结果列表 ge_tensors 中。 + ge_tensors.emplace_back(ge_tensor_ptr); + } else { // 如果转换过程中遇到错误,则输出相应的错误日志,并清空结果列表 ge_tensors,并返回空列表。 + MS_LOG(ERROR) << "Convert me_tensor " << index << " to Ge Tensor failed!"; + ge_tensors.clear(); + return ge_tensors; + } + } + return ge_tensors; // 返回转换后的 GeTensor 列表。 +} + +// ConvertTensor 函数用于将给定的 MeTensor(`tensor`)转换为对应的 GeTensor,并返回转换后的结果。 +// MeTensor 是 MindSpore 引擎中定义的张量类,用于存储张量的数据和相关信息。 +// GeTensor 是 Ascend AI Core 引擎中定义的张量类,用于存储张量的数据和相关信息。 +GeTensorPtr TransformUtil::ConvertTensor(const MeTensorPtr &tensor, const std::string &format) { + // get tensor data type size + // 获取 MeTensor 的数据类型大小(type_size),即数据类型占用的字节数。 + MS_EXCEPTION_IF_NULL(tensor); + size_t type_size = GetDataTypeSize(tensor->data_type()); + if (type_size == kErrorSize) { // 如果数据类型大小获取失败,则输出错误日志,并返回空指针。 + MS_LOG(ERROR) << "The Me Tensor data type size is wrong, type size is: " << type_size; + return nullptr; + } + // 获取 MeTensor 的元素数量和数据缓冲区大小(data_buff_size)。 + size_t elements_num = IntToSize(tensor->ElementsNum()); + + // get tensor buff size + size_t data_buff_size = elements_num * type_size; + if (data_buff_size == 0) { // 如果数据缓冲区大小为 0,则输出提示信息日志。 + MS_LOG(INFO) << "The Me Tensor data buff size is 0."; + } + // create ge tensor + // 创建 GeTensorDesc 对象,并根据 MeTensor 的形状、数据类型和数据格式信息创建对应的 GeTensor 对象。 + auto desc = GetGeTensorDesc(tensor->shape_c(), tensor->data_type(), format); + if (desc == nullptr) { // 如果创建 GeTensorDesc 对象失败,则输出错误日志,并返回空指针。 + MS_LOG(ERROR) << "Failed to get Tensor Desc"; + return nullptr; + } + // 创建 GeTensor 对象,并设置相应的数据缓冲区、形状、数据类型等信息。 + GeTensorPtr tensor_ptr = make_shared(*desc, static_cast(tensor->data_c()), data_buff_size); + if (tensor_ptr != nullptr) { // 如果创建 GeTensor 对象成功,则输出转换成功的提示信息,并返回指向创建的 GeTensor 对象的 shared_ptr。 + MS_LOG(INFO) << "Convert Me Tensor to Ge Tensor success!"; + } + return tensor_ptr; +} + +// ConvertGeTensors 函数用于将给定的 GeTensor(`ge_tensors`)向量转换为对应的 MeTensor(MindSpore +// 引擎中的张量)向量,并返回转换后的结果。 `ge_tensors` 是 Ascend AI Core 引擎中的张量向量,用于存储计算结果的张量。 +// `request_dims` 是一个与 `ge_tensors` 同样大小的向量,用于指定转换后的 MeTensor 的形状(ShapeVector)。 +std::vector TransformUtil::ConvertGeTensors(const std::vector &ge_tensors, + const std::vector &request_dims) { + std::vector outputs; + // 遍历 `ge_tensors` 向量中的每个 GeTensor,将其转换为对应的 MeTensor 对象 + for (size_t index = 0; index < ge_tensors.size(); index++) { + MeTensorPtr me_tensor_ptr = nullptr; + // 根据索引值 `index` 来获取对应的请求形状(`request_dims`)。 + if (index < request_dims.size()) { + me_tensor_ptr = ConvertGeTensor(ge_tensors[index], request_dims[index]); + } else { // 如果请求形状的向量长度小于当前索引 `index`,则使用空的形状向量来进行转换。 + ShapeVector empty_shape; + me_tensor_ptr = ConvertGeTensor(ge_tensors[index], empty_shape); + } + + if (me_tensor_ptr != nullptr) { // 如果转换成功,则将转换后的 MeTensor 存储在输出向量 `outputs` 中。 + outputs.emplace_back(me_tensor_ptr); + } else { // 如果转换失败,则输出相应的错误日志,并返回已经成功转换的 MeTensor 向量 `outputs`。 + MS_LOG(ERROR) << "Convert Ge Tensor " << index << " to Me Tensor failed!"; + return outputs; + } + } + return outputs; // 返回转换后的 MeTensor 向量 `outputs`。 +} + +// ConvertGeTensors 函数用于将给定的 GeTensor(`ge_tensors`)向量转换为对应的 MeTensor(MindSpore 引擎中的张量)向量,并返回转换后的结果。 +//`ge_tensors` 是 Ascend AI Core 引擎中的张量向量,用于存储计算结果的张量。 +std::vector TransformUtil::ConvertGeTensors(const std::vector &ge_tensors) { + std::vector outputs; + // 遍历 `ge_tensors` 向量中的每个 GeTensor,将其转换为对应的 MeTensor 对象。 + for (size_t index = 0; index < ge_tensors.size(); index++) { + MeTensorPtr me_tensor_ptr = ConvertGeTensor(ge_tensors[index]); + if (me_tensor_ptr != nullptr) { // 如果转换成功,则将转换后的 MeTensor 存储在输出向量 `outputs` 中。 + outputs.emplace_back(me_tensor_ptr); + } else { // 如果转换失败,则输出相应的错误日志,并返回已经成功转换的 MeTensor 向量 `outputs`。 + MS_LOG(ERROR) << "Convert Ge Tensor " << index << " to Me Tensor failed!"; + return outputs; + } + } + return outputs; // 返回转换后的 MeTensor 向量 `outputs`。 +} + +// ConvertGeDataType 函数用于将给定的 GeDataType(Ascend AI Core 引擎中的数据类型)转换为对应的 MeDataType(MindSpore引擎中的数据类型)。 +//`type` 是 Ascend AI Core 引擎中的数据类型,需要被转换为对应的 MeDataType。 +MeDataType TransformUtil::ConvertGeDataType(const GeDataType &type) { + switch (type) { // 对于不同的 GeDataType 值,根据其对应的数据类型进行转换。 + case GeDataType::DT_FLOAT16: + return MeDataType::kNumberTypeFloat16; + case GeDataType::DT_FLOAT: + return MeDataType::kNumberTypeFloat32; + case GeDataType::DT_DOUBLE: + return MeDataType::kNumberTypeFloat64; + case GeDataType::DT_INT64: + return MeDataType::kNumberTypeInt64; + case GeDataType::DT_INT32: + return MeDataType::kNumberTypeInt32; + case GeDataType::DT_INT16: + return MeDataType::kNumberTypeInt16; + case GeDataType::DT_INT8: + return MeDataType::kNumberTypeInt8; + case GeDataType::DT_BOOL: + return MeDataType::kNumberTypeBool; + case GeDataType::DT_UINT8: + return MeDataType::kNumberTypeUInt8; + case GeDataType::DT_UINT16: + return MeDataType::kNumberTypeUInt16; + case GeDataType::DT_UINT32: + return MeDataType::kNumberTypeUInt32; + case GeDataType::DT_UINT64: + return MeDataType::kNumberTypeUInt64; + // 对于其他未列出的 GeDataType 值,或者无法转换的值,返回 MeDataType::kTypeUnknown,表示未知数据类型。 + case GeDataType::DT_UNDEFINED: + case GeDataType::DT_DUAL_SUB_UINT8: + case GeDataType::DT_DUAL_SUB_INT8: + case GeDataType::DT_DUAL: + return MeDataType::kTypeUnknown; + default: + return MeDataType::kTypeUnknown; + } +} + +namespace { +// IsGeShapeCompatible 函数用于检查给定的 GeTensor 的形状 `ge_shape` 是否与请求的形状 `request_dims` 兼容。 +// `ge_shape` 是 GeTensor 的形状,`request_dims` 是请求的形状。 +bool IsGeShapeCompatible(const GeShape &ge_shape, const ShapeVector &request_dims) { + MS_LOG(INFO) << "GeTensor's shape is " << TransformUtil::PrintVector(ge_shape.GetDims()); + MS_LOG(INFO) << "Me request shape is " << TransformUtil::PrintVector(request_dims); + + const int GE_DIMS = 4; + std::vector ge_dims = ge_shape.GetDims(); + if (request_dims.size() > ge_dims.size()) { // 如果请求的维度数量大于 GeTensor 的维度数量,说明形状不兼容,返回 false。 + MS_LOG(ERROR) << "Request shape's dims count greater than ge shape's"; + return false; + } + + // convert NHWC to NCHW + // 如果请求的维度数量等于 1,且 GeTensor 的维度数量等于 4,且对应维度值满足 NHWC 到 NCHW 的转换条件,返回兼容。 + if ((request_dims.size() == 1) && (ge_dims.size() == GE_DIMS) && (request_dims[0] == ge_dims[1]) && + (ge_dims[0] == 1) && (ge_dims[2] == 1) && (ge_dims[3] == 1)) { + MS_LOG(INFO) << "Ge tensor shape and request shape is compatible"; + return true; + } + + std::string::size_type i = 0; + // 逐一比较 `request_dims` 和 `ge_shape` 的维度值,如果任何维度值不相等,说明形状不兼容,返回 false。 + for (; i < request_dims.size(); i++) { + if (ge_dims[i] != request_dims[i]) { + MS_LOG(ERROR) << "Request shape's dims value not equal to ge shape's"; + return false; + } + } + // 对于 `request_dims` 中已比较的维度后的未提供的维度,要求其在 `ge_shape` 中对应的维度值为1,否则说明形状不兼容,返回 false。 + for (; i < ge_dims.size(); i++) { + if (ge_dims[i] != 1) { + MS_LOG(ERROR) << "GeShape's extend dims is not equal to 1"; + return false; + } + } + // 形状兼容,返回 true。 + MS_LOG(INFO) << "Ge tensor shape and request shape is compatible"; + return true; +} +} // namespace + +// ConvertMeShape 函数用于将 MeTensor 的形状表示转换为 GeTensor 的形状表示。 +GeShape TransformUtil::ConvertMeShape(const ShapeVector &me_dims) { + std::vector ge_dims; + //将 `me_dims` 中的维度值拷贝到一个新的 vector `ge_dims` 中,并使用这个新的 vector 创建一个 GeShape 对象 + (void)std::copy(me_dims.begin(), me_dims.end(), std::back_inserter(ge_dims)); + return GeShape(ge_dims); //返回 GeShape 对象,表示 MeTensor 形状转换为 GeTensor 形状的结果。 +} + +// ConvertGeShape 函数用于将 GeTensor 的形状表示转换为 MeTensor 的形状表示。 +ShapeVector TransformUtil::ConvertGeShape(const GeShape &ge_shape) { + //将 GeShape 对象中的维度值拷贝到一个新的 ShapeVector `me_dims` 中,并使用这个新的 ShapeVector 表示 MeTensor + ShapeVector me_dims; + std::vector ge_dims = ge_shape.GetDims(); + (void)std::copy(ge_dims.begin(), ge_dims.end(), std::back_inserter(me_dims)); + return me_dims; //返回 `me_dims`,表示 GeTensor 形状转换为 MeTensor 形状的结果 +} + +// ConvertGeShape 函数用于将 GeTensor 的形状表示转换为 MeTensor 的形状表示。 +// 参数 `ge_shape` 是 GeTensor 的形状,以 GeShape 对象表示。 +// 参数 `request_dims` 是 MeTensor 请求的形状,以 ShapeVector 表示。 +ShapeVector TransformUtil::ConvertGeShape(const GeShape &ge_shape, const ShapeVector &request_dims) { + vector ret; + if (ge_shape.GetDimNum() == 0) { + MS_LOG(DEBUG) << "GeTensor's shape is scalar"; + return ret; + } + if (IsGeShapeCompatible(ge_shape, request_dims) == true) { //比较 GeShape 和 MeTensor 请求形状是否兼容 + ret = request_dims; //如果兼容则返回 MeTensor 请求形状 `request_dims` + } else { //否则返回 GeTensor 形状转换后的 MeTensor 形状 `ret` + MS_LOG(ERROR) << "GeShape and Me request shape are incompatible, return GeShape"; + ret = ConvertGeShape(ge_shape); + } + return ret; +} + +// GenerateMeTensor 函数用于根据给定的 GeTensor 对象 `ge_tensor`,生成相应的 MeTensor 对象,并将其形状和数据复制到MeTensor 中。 +// 参数 `ge_tensor` 是给定的 GeTensor 对象,表示待转换的 GeTensor。 +// 参数 `me_dims` 是 MeTensor 的形状,以ShapeVector 表示。 +// 参数 `me_type` 是 MeTensor 的数据类型,以 TypeId 表示。 +MeTensorPtr TransformUtil::GenerateMeTensor(const GeTensorPtr &ge_tensor, const ShapeVector &me_dims, + const TypeId &me_type) { + MeTensor me_tensor(me_type, me_dims); + + // Get the writable data pointer of the tensor and cast it to its data type + // 获取 MeTensor 的可写数据指针,并将其转换为指定的数据类型 + auto me_data_ptr = reinterpret_cast(me_tensor.data_c()); + size_t me_data_size = static_cast(me_tensor.data().nbytes()); + MS_EXCEPTION_IF_NULL(me_data_ptr); + MS_EXCEPTION_IF_NULL(ge_tensor); + if (me_data_size < ge_tensor->GetSize()) { + MS_LOG(ERROR) << "ME tensor data size[" << me_data_size << " bytes] is less than GE tensor [" + << ge_tensor->GetSize() << " bytes]"; + return nullptr; + } + + // Copy or use the writable data pointer of the ME tensor + // 复制或使用 MeTensor 的可写数据指针 + MS_EXCEPTION_IF_NULL(ge_tensor->GetData()); + if (ge_tensor->GetSize() == 0) { + MS_LOG(ERROR) << "GE tensor data size is zero!"; + return nullptr; + } + + // Use memcpy here, not memcpy_s, just because the size of ge_tensor may be bigger than 2GB + // which is the size limit of memcpy_s + // 使用 memcpy 进行数据拷贝,而不使用 memcpy_s,这是因为 ge_tensor 的大小可能大于 2GB, + // 而 memcpy_s 有 2GB 的大小限制。 + (void)memcpy(me_data_ptr, ge_tensor->GetData(), ge_tensor->GetSize()); + + return make_shared(me_tensor); +} + +// ConvertGeTensor 函数用于将给定的 GeTensor 对象 `ge_tensor` 转换为相应的 MeTensor 对象。 +// 参数 `ge_tensor` 是给定的 GeTensor 对象,表示待转换的 GeTensor。 +MeTensorPtr TransformUtil::ConvertGeTensor(const GeTensorPtr &ge_tensor) { + MS_EXCEPTION_IF_NULL(ge_tensor); + // 获取 GeTensor 的形状,并将其转换为 MeTensor 的形状 + GeShape ge_shape = ge_tensor->GetTensorDesc().GetShape(); + vector me_dims = ConvertGeShape(ge_shape); + // 获取 GeTensor 的数据类型,并将其转换为 MeTensor 的数据类型 + TypeId type_id = ConvertGeDataType(ge_tensor->GetTensorDesc().GetDataType()); + if (type_id == MeDataType::kTypeUnknown) { + MS_LOG(ERROR) << "Could not convert Ge Tensor because of unsupported data type: " + << static_cast(ge_tensor->GetTensorDesc().GetDataType()); + return nullptr; + } + // 调用 GenerateMeTensor 函数,根据转换后的 MeTensor 形状和数据类型,生成相应的 MeTensor 对象 + return GenerateMeTensor(ge_tensor, me_dims, type_id); +} + +// if request_dims is empty, use ge tensor's shape,otherwise convert to request shape +// ConvertGeTensor 函数用于将给定的 GeTensor 对象 `ge_tensor` 转换为相应的 MeTensor 对象,并根据给定的 MeTensor 形状 `request_dims` 进行转换。 +// 参数 `ge_tensor` 是给定的 GeTensor 对象,表示待转换的 GeTensor。 +// 参数 `request_dims` 是要求的 MeTensor 形状,用于与 GeTensor 的形状进行兼容性检查和转换。 +MeTensorPtr TransformUtil::ConvertGeTensor(const GeTensorPtr ge_tensor, const ShapeVector &request_dims) { + MS_EXCEPTION_IF_NULL(ge_tensor); + // 获取 GeTensor 的形状,并将其与给定的 MeTensor 形状 `request_dims` 进行兼容性检查和转换 + GeShape ge_shape = ge_tensor->GetTensorDesc().GetShape(); + vector me_dims = ConvertGeShape(ge_shape, request_dims); + // 输出 GeTensor 的数据类型 + MS_LOG(INFO) << "GE tensor type is " << static_cast(ge_tensor->GetTensorDesc().GetDataType()); + // Create a tensor with wanted data type and shape + // 创建具有指定数据类型和形状的 MeTensor 对象 + TypeId type_id = ConvertGeDataType(ge_tensor->GetTensorDesc().GetDataType()); + if (type_id == MeDataType::kTypeUnknown) { + MS_LOG(ERROR) << "Could not convert Ge Tensor because of unsupported data type: " + << static_cast(ge_tensor->GetTensorDesc().GetDataType()); + return nullptr; //如果转换失败,则返回 nullptr + } + return GenerateMeTensor(ge_tensor, me_dims, type_id); //返回其指针,表示转换成功 +} + +//PrintGeTensor 函数用于打印给定的 GeTensor 对象的数据内容 +std::string TransformUtil::PrintGeTensor(const GeTensorPtr ge_tensor) { + std::string ret; + if (ge_tensor == nullptr) { //检查输入的 ge_tensor 是否为空 + MS_LOG(ERROR) << "Input ge tensor is nullptr"; //如果为空,则输出错误日志并返回空字符串 + return ret; + } + //获取了ge_tensor 的数据类型,并根据数据类型使用 MakeVector 函数将 ge_tensor 的数据内容转换成相应的向量 + //根据数据类型的不同,函数将数据内容转换成不同类型的向量,包括 uint32_t、float_t、int32_t、double_t、int64_t、uint64_t、int16_t、uint16_t、int8_t 和 uint8_t。 + //使用 PrintVector 函数将转换后的向量打印成字符串,并返回该字符串 + MS_LOG(INFO) << "Ge Tensor data type is : " << static_cast(ge_tensor->GetTensorDesc().GetDataType()); + switch (static_cast(ge_tensor->GetTensorDesc().GetDataType())) { + case GeDataType::DT_UINT32: + ret = PrintVector(MakeVector(ge_tensor->GetData(), ge_tensor->GetSize())); + break; + case GeDataType::DT_FLOAT: + ret = PrintVector(MakeVector(ge_tensor->GetData(), ge_tensor->GetSize())); + break; + case GeDataType::DT_INT32: + ret = PrintVector(MakeVector(ge_tensor->GetData(), ge_tensor->GetSize())); + break; + case GeDataType::DT_DOUBLE: + ret = PrintVector(MakeVector(ge_tensor->GetData(), ge_tensor->GetSize())); + break; + case GeDataType::DT_INT64: + ret = PrintVector(MakeVector(ge_tensor->GetData(), ge_tensor->GetSize())); + break; + case GeDataType::DT_UINT64: + ret = PrintVector(MakeVector(ge_tensor->GetData(), ge_tensor->GetSize())); + break; + case GeDataType::DT_INT16: + ret = PrintVector(MakeVector(ge_tensor->GetData(), ge_tensor->GetSize())); + break; + case GeDataType::DT_UINT16: + ret = PrintVector(MakeVector(ge_tensor->GetData(), ge_tensor->GetSize())); + break; + case GeDataType::DT_DUAL_SUB_INT8: + case GeDataType::DT_INT8: + ret = PrintVector(MakeVector(ge_tensor->GetData(), ge_tensor->GetSize())); + break; + case GeDataType::DT_UINT8: + case GeDataType::DT_DUAL_SUB_UINT8: + ret = PrintVector(MakeVector(ge_tensor->GetData(), ge_tensor->GetSize())); + break; + case GeDataType::DT_FLOAT16: + case GeDataType::DT_BOOL: + case GeDataType::DT_UNDEFINED: + case GeDataType::DT_DUAL: + //如果给定的 ge_tensor 的数据类型不在上述支持的类型列表中(如 DT_FLOAT16、DT_BOOL、DT_UNDEFINED 和 DT_DUAL),则输出错误日志,并返回空字符串。 + default: + MS_LOG(ERROR) << "Unsupported to print type:" << static_cast(ge_tensor->GetTensorDesc().GetDataType()) + << " ge tensor"; + break; + } + return ret; +} +} // namespace transform +} // namespace mindspore