diff --git a/.github/ISSUE_TEMPLATE/caffe2paddle.md b/.github/ISSUE_TEMPLATE/caffe2paddle.md deleted file mode 100644 index d650fcfbd85663600f57c2055211fa9c2d4b20d4..0000000000000000000000000000000000000000 --- a/.github/ISSUE_TEMPLATE/caffe2paddle.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -name: caffe2paddle -about: Caffe模型转换至PaddlePaddle,请说明Caffe模型的来源,模型类型(例如图像分类、目标检测等) ---- - -Caffe模型转换至PaddlePaddle,请说明Caffe模型的来源,模型类型(例如图像分类、目标检测等) - -如有原模型文件或github链接,如方便可一并附上,方便开发人员分析。 diff --git a/.github/ISSUE_TEMPLATE/main.md b/.github/ISSUE_TEMPLATE/main.md new file mode 100644 index 0000000000000000000000000000000000000000..e63251e100ea4c211fa4912b8f950bfaf1370332 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/main.md @@ -0,0 +1,20 @@ +--- +name: X2Paddle +about: 第三方模型转换至PaddlePaddle,请说明模型的来源,模型类型(例如图像分类、目标检测等) +--- + +请说明模型的来源,模型类型(例如图像分类、目标检测等) + +如有原模型文件或github链接,如方便可一并附上,方便开发人员分析。 + +**为了便于开发人员快速定位原因,建议将模型文件上传(或通过百度网盘分享)** + +## 示例 + +模型来源: TensorFlow +模型说明: 图像分类模型,其相应repo为 https://github.com/xxxx +模型文件: 链接: https://pan.baidu.com/s/1LRzTSJwsLOul99Tj5_43wQ 密码: d3dg +转换过程出错提示如下: +``` +Exception: Error happened when mapping node ['prior_box@0'] to onnx, which op_type is 'prior_box' with inputs: {'Image': ['image'], 'Input': ['elementwise_add_7.tmp_1']} and outputs: {'Boxes': ['prior_box_0.tmp_0'], 'Variances': ['prior_box_0.tmp_1']} +``` diff --git a/.github/ISSUE_TEMPLATE/onnx2paddle.md b/.github/ISSUE_TEMPLATE/onnx2paddle.md deleted file mode 100644 index de8a7aebce34994f1269adad7d326a71851e9700..0000000000000000000000000000000000000000 --- a/.github/ISSUE_TEMPLATE/onnx2paddle.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -name: onnx2paddle -about: ONNX模型转换至PaddlePaddle,请说明ONNX模型的来源,模型类型(例如图像分类、目标检测等) ---- -ONNX模型转换至PaddlePaddle,请说明ONNX模型的来源,模型类型(例如图像分类、目标检测等) - -如有原模型文件或github链接,如方便可一并附上,方便开发人员分析。 diff --git a/.github/ISSUE_TEMPLATE/other.md b/.github/ISSUE_TEMPLATE/other.md deleted file mode 100644 index 7202cce897890f3d4a8e970cc4e379ba5db02821..0000000000000000000000000000000000000000 --- a/.github/ISSUE_TEMPLATE/other.md +++ /dev/null @@ -1,4 +0,0 @@ ---- -name: 其它类型 -about: 例如Bug类,需求建议类 ---- diff --git a/.github/ISSUE_TEMPLATE/paddle2onnx.md b/.github/ISSUE_TEMPLATE/paddle2onnx.md index 4f589a66df1a653995d17d0d6e422dff3764bcd7..a4674f500df5f117b4688d734d8079a4223fe2a9 100644 --- a/.github/ISSUE_TEMPLATE/paddle2onnx.md +++ b/.github/ISSUE_TEMPLATE/paddle2onnx.md @@ -1,7 +1,8 @@ --- name: paddle2onnx -about: Paddle模型转换至ONNX,请说明Paddle模型的来源,模型类型(例如图像分类、目标检测等) +about: Paddle2ONNX相关问题请直接前往https://github.com/PaddlePaddle/Paddle2ONNX/issues 提问题 --- -Paddle模型转换至ONNX,请说明Paddle模型的来源,模型类型(例如图像分类、目标检测等) -如有原模型文件或github链接,如方便可一并附上,方便开发人员分析,同时建议说明模型转换的使用场景:) +Paddle2ONNX的问题请在Paddle2ONNX的repo下提问 + +Repo链接: [https://github.com/PaddlePaddle/Paddle2ONNX/issues](https://github.com/PaddlePaddle/Paddle2ONNX/issues) diff --git a/.github/ISSUE_TEMPLATE/tensorflow2paddle.md b/.github/ISSUE_TEMPLATE/tensorflow2paddle.md deleted file mode 100644 index 88af18bf67a737206b6757ec55f1d581d62460b3..0000000000000000000000000000000000000000 --- a/.github/ISSUE_TEMPLATE/tensorflow2paddle.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -name: tensorflow2paddle -about: TensorFlow模型转换至PaddlePaddle,请说明TensorFlow模型的来源,模型类型(例如图像分类、目标检测等) ---- - -TensorFlow模型转换至PaddlePaddle,请说明TensorFlow模型的来源,模型类型(例如图像分类、目标检测等) - -如有原模型文件或github链接,可以一并附上,方便开发人员分析。 diff --git a/x2paddle/core/program.py b/x2paddle/core/program.py index ebf0e81094cb4c6db5f8f7997aed4cba7b4a2d28..c6b255700430f489bec55a2c72e573e7ed49ba13 100644 --- a/x2paddle/core/program.py +++ b/x2paddle/core/program.py @@ -18,7 +18,7 @@ from __future__ import division import paddle.fluid as fluid import paddle from paddle.fluid.proto import framework_pb2 -from collections import OrderedDict +import collections import numpy import sys import os @@ -38,7 +38,7 @@ class PaddleLayer(object): outputs, list), "parameter 'outputs' for PaddleLayer should be type of list" for k, v in inputs.items(): - if isinstance(v, list): + if isinstance(v, (list, tuple)): for i in v: assert isinstance( i, six.string_types @@ -66,7 +66,7 @@ class PaddleLayer(object): class PaddleGraph(object): def __init__(self, source_type=None, parent_layer=None, graph_type="static"): - self.layers = OrderedDict() + self.layers = collections.OrderedDict() self.edges_out = dict() self.edges_in = dict() self.inputs = list() @@ -94,7 +94,7 @@ class PaddleGraph(object): self.script = script def clear(self): - self.layers = OrderedDict() + self.layers = collections.OrderedDict() self.edges_out = dict() self.edges_in = dict() self.inputs = list() @@ -168,7 +168,7 @@ class PaddleGraph(object): for layer_id, layer in self.layers.items(): for input_key, input_var in layer.inputs.items(): vs = input_var - if not isinstance(vs, list): + if not isinstance(vs, (list, tuple)): vs = [vs] for v in vs: assert v in outputs_from_nodes or ( @@ -521,7 +521,7 @@ class PaddleGraph(object): gen_codes( comment_list, indent=1)) - use_structured_name = False if self.source_type in ["tf", "onnx"] else True + use_structured_name = False if self.source_type in ["tf"] else True self.run_func.extend( gen_codes(["paddle.disable_static()", "params = paddle.load('{}/model.pdparams')".format(osp.abspath(code_dir)), @@ -590,7 +590,7 @@ class PaddleGraph(object): elif len(layer.outputs) == 2: line = layer.outputs[1] else: - if layer.kernel == "paddle.nn.LSTM": + if layer.kernel in ["paddle.nn.LSTM"]: line = "{}, ({})".format(layer.outputs[1], ', '.join(layer.outputs[-2:])) else: line = ','.join(layer.outputs[1:]) @@ -599,8 +599,13 @@ class PaddleGraph(object): line += " = self.{}".format(layer.outputs[0]) else: line += " = self.{}(".format(layer.outputs[0]) - for k, v in layer.inputs.items(): - line += "{}, ".format(v) + for v in layer.inputs.values(): + if isinstance(v, list): + line += "[{}], ".format(", ".join(v)) + elif isinstance(v, tuple): + line += "({}), ".format(", ".join(v)) + else: + line += "{}, ".format(v) line = line.strip(", ") line += ")" self.forward_func.extend(gen_codes([line], indent=indent)) @@ -627,6 +632,8 @@ class PaddleGraph(object): for k, v in layer.inputs.items(): if isinstance(v, list): line += "{}=[{}], ".format(k, ", ".join(v)) + elif isinstance(v, tuple): + line += "{}=({}), ".format(k, ", ".join(v)) else: if k == "args": line += v @@ -666,7 +673,7 @@ class PaddleGraph(object): paddle.disable_static() restore = paddle.load(osp.join(save_dir, "model.pdparams")) model = getattr(x2paddle_code, self.name)() - if self.source_type in ["tf", "onnx"]: + if self.source_type in ["tf"]: model.set_dict(restore, use_structured_name=False) else: model.set_dict(restore) diff --git a/x2paddle/decoder/onnx_decoder.py b/x2paddle/decoder/onnx_decoder.py index 49c74200deb923ee7e007d6474ef46c43c9e5d09..af6cc44cae7640361ab81267c1391a082c538ed2 100644 --- a/x2paddle/decoder/onnx_decoder.py +++ b/x2paddle/decoder/onnx_decoder.py @@ -96,6 +96,11 @@ class ONNXGraphNode(GraphNode): return default return self.attr_map[name] + def output(self, index=0): + if index >0 and len(self.layer.output) <= index: + raise IndexError('Output numbers of Node:{} is {} <= index:{}'.format(self.layer_name, len(self.layer.output), index)) + return self.layer.output[index] + class ONNXGraphDataNode(GraphNode): def __init__(self, layer, layer_name=None, is_global_input=False): @@ -246,12 +251,7 @@ class ONNXGraph(Graph): """ generate output_nodes node of ONNX model """ - output_nodes = [value.name for value in self.graph.output] - for opt_data in output_nodes: - n = super(ONNXGraph, self).get_node(opt_data) - if n is None: - self.topo_sort.append(self.node_map[opt_data]) - self.output_nodes.append(opt_data) + self.output_nodes = [value.name for value in self.graph.output] def is_place_holder_nodes(self, layer): """ diff --git a/x2paddle/op_mapper/dygraph/onnx2paddle/onnx_custom_layer/__init__.py b/x2paddle/op_mapper/dygraph/onnx2paddle/onnx_custom_layer/__init__.py index 887bb45cdd6c86a62c7cce68d99b3e0cf3328bd1..70ef686164d791c9af12c72f7e306025e015b2c1 100644 --- a/x2paddle/op_mapper/dygraph/onnx2paddle/onnx_custom_layer/__init__.py +++ b/x2paddle/op_mapper/dygraph/onnx2paddle/onnx_custom_layer/__init__.py @@ -17,4 +17,4 @@ from .one_hot import OneHot from .pad_two_input import PadWithTwoInput from .pad_all_dim2 import PadAllDim2 from .pad_all_dim4 import PadAllDim4 -from .pad_all_dim4_one_input import PadAllDim4WithOneInput \ No newline at end of file +from .pad_all_dim4_one_input import PadAllDim4WithOneInput diff --git a/x2paddle/op_mapper/dygraph/onnx2paddle/opset9/opset.py b/x2paddle/op_mapper/dygraph/onnx2paddle/opset9/opset.py index 24c35ccd1279d4a95c9baece326760532d643abe..b3f7bca3a7a07ef55c870f7b8dbbfebe411351e8 100644 --- a/x2paddle/op_mapper/dygraph/onnx2paddle/opset9/opset.py +++ b/x2paddle/op_mapper/dygraph/onnx2paddle/opset9/opset.py @@ -42,6 +42,31 @@ def _const_weight_or_none(node, necessary=False): return None +def _rename_or_remove_weight(weights, origin_name, target_name=None, is_remove=True): + ''' + Rename parameters by Paddle's naming rule of parameters. + + Args: + weights(dict[String:np.ndarray]): Dict stored paramters, the key in weights is name of parameter. + origin_name(String): Name of parameter to rename or remove. + target_name(String, optional): if target_name is not None, add new key-value pair + {target_name:weights[origin_name]} to weights, and target_name must follow paddle's + naming rule of parameters. Default: None. + is_remove: if is_remove is True, remove origin key-value pair. Default: True. + Returns: + None + ''' + if origin_name not in weights: + raise KeyError('{} not a key in {}'.format(origin_name, weights)) + if is_remove: + # remove weight + data = weights.pop(origin_name) + else: + data = weights[origin_name] + if target_name is not None: + # rename weight + weights[target_name] = data + def _is_static_shape(shape): negtive_dims = 0 error_dims = 0 @@ -125,6 +150,9 @@ class OpSet9(): dict(threshold='threshold'), dict(threshold=float(sys.maxsize))], 'Exp': ['paddle.exp'], + 'LogSoftmax': ['paddle.nn.functional.log_softmax', + dict(axis='axis'), + dict(axis=1)], 'Softmax': ['paddle.nn.Softmax', dict(axis='axis'), dict(axis=1)], @@ -164,11 +192,12 @@ class OpSet9(): layer_attrs[pd_attr_name] = onnx_attrs[onnx_attr_name] else: layer_attrs[pd_attr_name] = op_info[2][onnx_attr_name] - if paddle_op.startswith("paddle.nn"): + if paddle_op.startswith("paddle.nn") and 'functional' not in paddle_op: op_name = paddle_op[10:].lower() op_name = name_generator(op_name, self.nn_name2id) output_name = node.name layer_outputs = [op_name, output_name] + self.paddle_graph.add_layer( kernel=paddle_op, inputs={"x": input.name}, @@ -258,14 +287,12 @@ class OpSet9(): val_scales = self.graph.get_input_node(node, idx=1, copy=True) # TODO(syf): paddle.nn.functional.interpolate will support the length # which is the same as the rank of input. -# inputs['scale_factor'] = val_scales.name attrs['scale_factor'] = self.weights[val_scales.name].tolist()[2:] elif len(node.layer.input) == 3: # opset 11 val_scales = self.graph.get_input_node(node, idx=2, copy=True) # TODO(syf): paddle.nn.functional.interpolate will support the length # which is the same as the rank of input. -# inputs['scale_factor'] = val_scales.name attrs['scale_factor'] = self.weights[val_scales.name].tolist()[2:] elif len(node.layer.input) == 4: # opset 11 @@ -602,11 +629,11 @@ class OpSet9(): val_scale = self.graph.get_input_node(node, idx=1, copy=True) val_b = self.graph.get_input_node(node, idx=2, copy=True) epsilon = node.get_attr('epsilon', 1e-5) + self.weights[op_name+'.scale'] = self.weights[val_scale.name] + self.weights[op_name+'.bias'] = self.weights[val_b.name] layer_attrs = { 'num_features': node.out_shapes[0][1], 'epsilon': epsilon, - 'weight_attr': string(val_scale.name), - 'bias_attr': string(val_b.name) } dim = len(val_x.out_shapes[0]) if dim == 3: @@ -717,11 +744,11 @@ class OpSet9(): op_name = name_generator("embedding", self.nn_name2id) output_name = node.name layer_outputs = [op_name, output_name] + self.weights[op_name + '.weight'] = _const_weight_or_none(val_x) self.paddle_graph.add_layer( 'paddle.nn.Embedding', inputs={"x": indices_cast}, outputs=layer_outputs, - weight_attr=string(val_x.name), num_embeddings=val_x.out_shapes[0][0], embedding_dim=val_x.out_shapes[0][1]) else: @@ -918,10 +945,6 @@ class OpSet9(): if starts_value is not None and ends_value is not None and axes is not None: starts_value = starts_value.copy() ends_value = ends_value.copy() - #for idx in range(len(ends_value)): - # if ends_value[idx] > 2**31 - 1: - # ends_value[idx] = 2**31 - 1 - #print(val_x.out_shapes) for idx in range(len(ends_value)): if starts_value[idx] >= val_x.out_shapes[0][axes[idx]]: starts_value[idx] = val_x.out_shapes[0][axes[idx]] - 1 @@ -1316,6 +1339,11 @@ class OpSet9(): epsilon = node.get_attr('epsilon', 1e-5) c = val_x.out_shapes[0][1] + _rename_or_remove_weight(self.weights, val_scale.name, op_name+'.weight') + _rename_or_remove_weight(self.weights, val_b.name, op_name+'.bias') + _rename_or_remove_weight(self.weights, val_var.name, op_name+'._variance') + _rename_or_remove_weight(self.weights, val_mean.name, op_name+'._mean') + # Attribute: spatial is used in BatchNormalization-1,6,7 spatial = bool(node.get_attr('spatial')) layer_attrs = { @@ -1323,10 +1351,6 @@ class OpSet9(): "momentum": momentum, "epsilon": epsilon, "is_test": True, - "param_attr": string(val_scale.name), - "bias_attr": string(val_b.name), - "moving_mean_name": string(val_mean.name), - "moving_variance_name": string(val_var.name), "use_global_stats": False, } self.paddle_graph.add_layer( @@ -1358,7 +1382,7 @@ class OpSet9(): mode = 'channel' shape_slope = val_slope.out_shapes[0] - if shape_slope == [1]: + if shape_slope == [1] * len(shape_slope): mode = 'all' if mode == "element": @@ -1391,17 +1415,19 @@ class OpSet9(): else: if mode == 'channel': slope_data = _const_weight_or_none(val_slope) + _rename_or_remove_weight(self.weights, val_slope.name) if len(shape_slope) > 1: - self.weights[val_slope.name] = np.reshape(slope_data, shape_slope[0]) + self.weights[op_name+'._weight'] = np.reshape(slope_data, shape_slope[0]) num_parameters = val_x.out_shapes[0][1] else: num_parameters = 1 + _rename_or_remove_weight(self.weights, val_slope.name) + self.weights[op_name+'._weight'] = np.reshape(self.weights[val_slope.name], [1]) self.paddle_graph.add_layer( "paddle.nn.PReLU", inputs={"x": val_x.name}, outputs=layer_outputs, - num_parameters=num_parameters, - weight_attr=string(val_slope.name)) + num_parameters=num_parameters) @print_mapping_info def Squeeze(self, node): @@ -1679,19 +1705,15 @@ class OpSet9(): "dilation": dilations, "groups": num_groups, } - val_w_name = val_w.name - while val_w_name in self.done_weight_list: - val_w_name += "__repeat" - self.done_weight_list.append(val_w_name) - layer_attrs["weight_attr"] = string(val_w_name) - self.weights[val_w_name] = self.weights[val_w.name] + remove_weight = True if val_w.name in self.done_weight_list else False + if remove_weight: + self.done_weight_list.append(val_w.name) + _rename_or_remove_weight(self.weights, val_w.name, op_name+'.weight', remove_weight) if has_bias: - val_b_name = val_b.name - while val_b_name in self.done_weight_list: - val_b_name += "__repeat" - self.done_weight_list.append(val_b_name) - layer_attrs["bias_attr"] = string(val_b_name) - self.weights[val_b_name] = self.weights[val_b.name] + remove_bias = True if val_b.name in self.done_weight_list else False + if remove_bias: + self.done_weight_list.append(val_b_name) + _rename_or_remove_weight(self.weights, val_b.name, op_name+'.bias', remove_bias) else: layer_attrs["bias_attr"] = False input_shape = val_x.out_shapes[0] @@ -1712,6 +1734,9 @@ class OpSet9(): @print_mapping_info def ConvTranspose(self, node): + op_name = name_generator("conv_trans", self.nn_name2id) + output_name = node.name + layer_outputs = [op_name, output_name] val_x = self.graph.get_input_node(node, idx=0, copy=True) val_w = self.graph.get_input_node(node, idx=1, copy=True) val_b = None @@ -1725,7 +1750,7 @@ class OpSet9(): assert 2 <= convnd <= 3, 'only Conv2DTranspose and Conv3DTranspose supported' num_in_channels = val_w.out_shapes[0][0] num_out_channels = val_w.out_shapes[0][1] - paddle_op = 'paddle.nn.functional.conv{}d_transpose'.format(convnd) + paddle_op = 'paddle.nn.Conv{}DTranspose'.format(convnd) num_groups = node.get_attr('group', 1) strides = node.get_attr('strides', [1] * convnd) @@ -1743,23 +1768,26 @@ class OpSet9(): output_size[1] = (val_x.out_shapes[0][3] - 1 ) * strides[1] - 2 * paddings[1] + dilations[1] * ( kernel_shape[1] - 1) + 1 + out_padding[1] + # Conv2DTranspose缺少output_size,只能在forward里头传进output_size - inputs_dict = {'x': val_x if isinstance(val_x, str) else val_x.name, - "weight": val_w.name} + inputs_dict = {'x': val_x if isinstance(val_x, str) else val_x.name} layer_attrs = { + "in_channels": num_in_channels, + "out_channels": num_out_channels, + "kernel_size": kernel_shape, "stride": strides, "dilation": dilations, "padding": paddings, "groups": num_groups, - "output_size": node.out_shapes[0][2:]} + "output_padding":out_padding} + + _rename_or_remove_weight(self.weights, val_w.name, op_name+'.weight',) if val_b is not None: - inputs_dict["bias"] = val_b.name - else: - layer_attrs["bias"] = None + _rename_or_remove_weight(self.weights, val_b.name, op_name+'.bias') self.paddle_graph.add_layer( - kernel="paddle.nn.functional.conv2d_transpose", + kernel=paddle_op, inputs=inputs_dict, - outputs=[node.name], + outputs=layer_outputs, **layer_attrs) @print_mapping_info @@ -1774,6 +1802,7 @@ class OpSet9(): inputs={"x": val_x.name}, outputs=[node.name], **layer_attrs) + @print_mapping_info def Size(self, node): @@ -1836,3 +1865,115 @@ class OpSet9(): "paddle.reciprocal", inputs={"x": val_x.name}, outputs=[node.name]) + + @print_mapping_info + def LSTM(self, node): + x = self.graph.get_input_node(node, idx=0, copy=True) + input_weight = self.graph.get_input_node(node, idx=1, copy=True) + hidden_weight = self.graph.get_input_node(node, idx=2, copy=True) + + input_nums = len(node.layer.input) + exist_input_nums = 3 + have_bias = False + if input_nums > 3 and node.layer.input[3] != '': + bias = self.graph.get_input_node(node, idx=exist_input_nums, copy=True) + have_bias = True + exist_input_nums += 1 + if input_nums > 4 and node.layer.input[4] != '': + sequence_lens = self.graph.get_input_node(node, idx=exist_input_nums, copy=True) + exist_input_nums += 1 + if input_nums > 5 and node.layer.input[5] != '': + init_h = self.graph.get_input_node(node, idx=exist_input_nums, copy=True) + self.paddle_graph.add_layer( + 'paddle.reshape', + inputs={"x": init_h.name}, + outputs=[init_h.name], + shape=init_h.out_shapes[0] + ) + exist_input_nums += 1 + if input_nums > 6 and node.layer.input[6] != '': + init_c = self.graph.get_input_node(node, idx=exist_input_nums, copy=True) + self.paddle_graph.add_layer( + 'paddle.reshape', + inputs={"x": init_c.name}, + outputs=[init_c.name], + shape=init_c.out_shapes[0] + ) + + input_weight_np = _const_weight_or_none(input_weight) + _rename_or_remove_weight(self.weights, input_weight.name) + hidden_size = node.get_attr('hidden_size', input_weight_np.shape[1]/4) + input_size = input_weight_np.shape[2] + hidden_weight_np = _const_weight_or_none(hidden_weight) + _rename_or_remove_weight(self.weights, hidden_weight.name) + bias_np = _const_weight_or_none(bias) + _rename_or_remove_weight(self.weights, bias.name) + input_bias_np = bias_np[:, :4*hidden_size] + hidden_bias_np = bias_np[:, 4*hidden_size:] + + # parameters order in paddle:lstm: + # 1. gate order in paddle is: input, forget, cell, output. + # 2. gate orfer in onnx is: input, output, forget, cell. + + def reform_weights(w, n, intervals): + slices = [w[:,x * n: y * n] for x, y in intervals] + return np.concatenate(slices, axis=1) + + def transform_weight_with_bias(weights, n, intervals): + return [reform_weights(w, n, intervals) for w in weights] + + reform_permutation = [(0, 1), (2, 4), (1, 2)] + + weights = transform_weight_with_bias( + [input_weight_np, hidden_weight_np, input_bias_np, hidden_bias_np], + hidden_size, reform_permutation) + + op_name = name_generator("lstm", self.nn_name2id) + y_out = node.output(0) + yh_out = node.output(1) + yc_out = node.output(2) + direction = node.get_attr('direction', 'forward') + + def generate_paddle_param_names(op_name, suffix=''): + param_names = [] + param_names.extend(['{}.weight_ih_l0{}', '{}.weight_hh_l0{}']) + if have_bias != False: param_names.append('{}.bias_ih_l0{}') + if have_bias != False: param_names.append('{}.bias_hh_l0{}') + param_names = [x.format(op_name, suffix) for x in param_names] + return param_names + + def assign_params(op_name, weights, weight_idx=0, suffix=''): + param_names = generate_paddle_param_names(op_name, suffix) + print(param_names) + for param_name, weight in zip(param_names, weights): + self.weights[param_name] = weight[weight_idx] + + if direction == 'backward': + raise Exception("LSTM support 'forward' or 'bidirectional', except '{}'.".format(direction)) + else: + assign_params(op_name, weights) + if direction == 'bidirectional': + assign_params(op_name, weights, 1, '_reverse') + + self.paddle_graph.add_layer( + 'paddle.nn.LSTM', + inputs={'input': x.name, 'initial_states': (init_h.name, init_c.name)}, + outputs=[op_name, y_out, yh_out, yc_out], + input_size=input_size, + hidden_size=hidden_size, + num_layers=1, + direction=string(direction), + time_major=True) + + self.paddle_graph.add_layer( + 'paddle.reshape', + inputs={"x": y_out}, + outputs=[y_out], + shape=[0, 0, -1, hidden_size] + ) + self.paddle_graph.add_layer( + 'paddle.transpose', + inputs={"x": y_out}, + outputs=[y_out], + perm=[0,2,1,3] + ) diff --git a/x2paddle/op_mapper/dygraph/pytorch2paddle/aten.py b/x2paddle/op_mapper/dygraph/pytorch2paddle/aten.py index 28c2e6ae45e4b777b92cafc05b35d3f5c7086e58..9e372892147fbd66fed75a42ec768b0b2d6be5a1 100644 --- a/x2paddle/op_mapper/dygraph/pytorch2paddle/aten.py +++ b/x2paddle/op_mapper/dygraph/pytorch2paddle/aten.py @@ -1310,7 +1310,6 @@ def aten_dim(mapper, graph, node): """ scope_name = mapper.normalize_scope_name(node) output_name = mapper._get_outputs_name(node)[0] - layer_outputs = [output_name] layer_inputs = {} inputs_name, inputs_node = mapper._get_inputs_name(node) # 获取当前节点输出的list @@ -1322,9 +1321,9 @@ def aten_dim(mapper, graph, node): current_inputs = list(layer_inputs.values()) graph.add_layer( - "prim.shape", inputs=layer_inputs, outputs=layer_outputs, scope_name=scope_name) + "prim.shape", inputs=layer_inputs, outputs=[output_name], scope_name=scope_name) graph.add_layer( - "prim.len", inputs={"input": output_name}, outputs=layer_outputs, scope_name=scope_name) + "prim.len", inputs={"input": output_name}, outputs=[output_name], scope_name=scope_name) return current_inputs, current_outputs @@ -4512,21 +4511,10 @@ def aten_upsample_bilinear2d(mapper, graph, node): current_outputs, scope_name) layer_inputs["align_corners"] = inputs_name[2] current_inputs.append(inputs_name[2]) -# # 处理输入3和4,构造assert -# list_layer_inputs = {} -# mapper._check_input(graph, inputs_node[3], inputs_name[3], current_outputs, scope_name) -# list_layer_inputs["key"] = inputs_name[3] -# current_inputs.append(inputs_name[3]) -# mapper._check_input(graph, inputs_node[4], inputs_name[4], current_outputs, scope_name) -# list_layer_inputs["value"] = inputs_name[4] -# current_inputs.append(inputs_name[4]) -# graph.add_layer( -# "prim.assert", -# inputs=list_layer_inputs, -# outputs=[output_name + "_assert"], -# scope_name=scope_name, -# type="eq") - layer_inputs["scale_factor"] = inputs_name[3] + if "size" in layer_attrs and layer_attrs["size"] is None: + mapper._check_input(graph, inputs_node[3], inputs_name[3], + current_outputs, scope_name) + layer_inputs["scale_factor"] = inputs_name[3] layer_attrs["align_mode"] = 0 layer_attrs["mode"] = string("bilinear") graph.add_layer( @@ -4592,7 +4580,10 @@ def aten_upsample_nearest2d(mapper, graph, node): block = PaddleGraph(source_type="pytorch", parent_layer=if_layer, graph_type="dygraph") if_layer.add_block(block) if_layer.inputs["input-0"] = inputs_name[1] - layer_inputs["scale_factor"] = inputs_name[3] + if "size" in layer_attrs and layer_attrs["size"] is None: + mapper._check_input(graph, inputs_node[3], inputs_name[3], + current_outputs, scope_name) + layer_inputs["scale_factor"] = inputs_name[3] layer_attrs["align_mode"] = 0 layer_attrs["mode"] = string("nearest") graph.add_layer( diff --git a/x2paddle/op_mapper/dygraph/pytorch2paddle/prim2code.py b/x2paddle/op_mapper/dygraph/pytorch2paddle/prim2code.py index 9940d3e1568bb956e6accdffe96156549d69f3ac..0ca02fc87e156e2bb55ab10516fea27d1164abe0 100644 --- a/x2paddle/op_mapper/dygraph/pytorch2paddle/prim2code.py +++ b/x2paddle/op_mapper/dygraph/pytorch2paddle/prim2code.py @@ -182,7 +182,7 @@ def prim_equal(layer, indent=1, init_func=[], forward_func=[], layer_id=None, di def prim_exception(layer, indent=1, init_func=[], forward_func=[], layer_id=None, different_attrs=None): - line = "raise RaiseException({})".format(get_value(layer, "input", different_attrs)) + line = "raise Exception({})".format(get_value(layer, "input", different_attrs)) forward_func.extend(gen_codes([line], indent=indent)) @@ -458,10 +458,12 @@ def prim_slice(layer, indent=1, init_func=[], forward_func=[], layer_id=None, di forward_func.extend(gen_codes([line], indent=indent)) -def prim_startswith(layer, indent=1, init_func=[], forward_func=[], layer_id=None, different_attrs=None): +def prim_startswith(layer, indent=1, init_func=[], forward_func=[], layer_id=None, different_attrs=None, is_return_line=False): line = "{} = {}.startswith({})".format(layer.outputs[0], get_value(layer, "input", different_attrs), get_value(layer, "start_str", different_attrs)) + if is_return_line: + return line.split(" = ")[1] forward_func.extend(gen_codes([line], indent=indent)) @@ -471,7 +473,7 @@ def prim_str(layer, indent=1, init_func=[], forward_func=[], layer_id=None, diff def prim_sub(layer, indent=1, init_func=[], forward_func=[], layer_id=None, different_attrs=None): - if int(get_value(layer, "alpha", different_attrs)) == 1: + if int(float(get_value(layer, "alpha", different_attrs))) == 1: line = "{} = {} - {}".format(layer.outputs[0], get_value(layer, "x", different_attrs), get_value(layer, "y", different_attrs)) diff --git a/x2paddle/op_mapper/dygraph/tf2paddle/tf_op_mapper.py b/x2paddle/op_mapper/dygraph/tf2paddle/tf_op_mapper.py index 37136a12b2fa6b2c36e9382561f6b501c46edd69..e30bef79e204692e66975a7638effddf8bcded34 100644 --- a/x2paddle/op_mapper/dygraph/tf2paddle/tf_op_mapper.py +++ b/x2paddle/op_mapper/dygraph/tf2paddle/tf_op_mapper.py @@ -1065,6 +1065,24 @@ class TFOpMapper(OpMapper): ], num_or_sections=num_split, axis=dim) + + def SplitV(self, node): + input = self.graph.get_input_node(node, 0) + size_splits = self.graph.get_input_node(node, 1) + assert size_splits.layer_type == "Const", "size_splits of SplitV OP should be Const" + size_splits = size_splits.value.tolist() + dim = self.graph.get_input_node(node, 2) + assert dim.layer_type == "Const", "dim of SplitV OP should be Const" + dim = dim.value + + self.paddle_graph.add_layer( + kernel="paddle.split", + inputs={"x": input.name}, + outputs=[ + "{}_p{}".format(node.layer_name, i) for i in range(len(size_splits)) + ], + num_or_sections=size_splits, + axis=dim) def Slice(self, node): input = self.graph.get_input_node(node, 0) diff --git a/x2paddle/op_mapper/static/onnx2paddle/opset9/opset.py b/x2paddle/op_mapper/static/onnx2paddle/opset9/opset.py index 1c89882bff2c02c480a6454d9bda79d1c09838fd..cd2be216883a599243cc730b73bdf1fd562529d9 100644 --- a/x2paddle/op_mapper/static/onnx2paddle/opset9/opset.py +++ b/x2paddle/op_mapper/static/onnx2paddle/opset9/opset.py @@ -1757,4 +1757,4 @@ class OpSet9(): self.paddle_graph.add_layer( "paddle.reciprocal", inputs={"x": val_x.name}, - outputs=[node.name]) \ No newline at end of file + outputs=[node.name]) diff --git a/x2paddle/op_mapper/static/tf2paddle/tf_op_mapper.py b/x2paddle/op_mapper/static/tf2paddle/tf_op_mapper.py index 338b15e9ed03bbd973de175fddcc72aedb1b2745..7df97dc6dda557102a1b1cd8e7b68a0ef5963b7d 100644 --- a/x2paddle/op_mapper/static/tf2paddle/tf_op_mapper.py +++ b/x2paddle/op_mapper/static/tf2paddle/tf_op_mapper.py @@ -1042,6 +1042,24 @@ class TFOpMapper(OpMapper): ], num_or_sections=num_split, axis=dim) + + def SplitV(self, node): + input = self.graph.get_input_node(node, 0) + size_splits = self.graph.get_input_node(node, 1) + assert size_splits.layer_type == "Const", "size_splits of SplitV OP should be Const" + size_splits = size_splits.value.tolist() + dim = self.graph.get_input_node(node, 2) + assert dim.layer_type == "Const", "dim of SplitV OP should be Const" + dim = dim.value + + self.paddle_graph.add_layer( + kernel="paddle.split", + inputs={"x": input.name}, + outputs=[ + "{}_p{}".format(node.layer_name, i) for i in range(len(size_splits)) + ], + num_or_sections=size_splits, + axis=dim) def Slice(self, node): input = self.graph.get_input_node(node, 0) diff --git a/x2paddle/optimizer/fusion/dygraph/if_fuser.py b/x2paddle/optimizer/fusion/dygraph/if_fuser.py index 70cffa7f0fe0e7b4184407b7aeaf3b224a6a1615..877dcc1219db1ccd2e1a418add496dd1de03f0ae 100644 --- a/x2paddle/optimizer/fusion/dygraph/if_fuser.py +++ b/x2paddle/optimizer/fusion/dygraph/if_fuser.py @@ -41,13 +41,21 @@ class DygraphIfFuser(FuseBase): return for id in graph.edges_in[layer_id]: input_layer = graph.layers[id] + input_layer_id = id if input_layer.outputs == [layer.inputs["input"]]: if input_layer.kernel == "prim.if": matches.pop(layer_id) return input_id = id break + if list(layer.inputs.values()).count(input_layer.outputs[0]) > 1 or \ + (input_layer_id in graph.edges_out and len(graph.edges_out[input_layer_id]) > 1): + matches.pop(layer_id) + return func_name = input_layer.kernel.replace(".", "_") + if func_name in ["prim_if", "prim_loop"]: + matches.pop(layer_id) + return from x2paddle.op_mapper.dygraph.pytorch2paddle import prim2code func = getattr(prim2code, func_name) line = func(input_layer, is_return_line=True) diff --git a/x2paddle/optimizer/fusion/dygraph/interpolate_bilinear_fuser.py b/x2paddle/optimizer/fusion/dygraph/interpolate_bilinear_fuser.py index 9e7cff3f8af1de23178d53aff3b5eaa24bc4f277..84ed97211fe16042dfa2bbce7193e19c2d6e2561 100644 --- a/x2paddle/optimizer/fusion/dygraph/interpolate_bilinear_fuser.py +++ b/x2paddle/optimizer/fusion/dygraph/interpolate_bilinear_fuser.py @@ -186,7 +186,6 @@ class DygraphInterpolateBilinearFuser(FuseBase): inputs={ "input": "interpolate-input-0", "size": "interpolate-input-3", - "scale_factor": gen_name(21) }, outputs=[gen_name(23)]) pattern_block_block.add_layer( @@ -295,6 +294,5 @@ class DygraphInterpolateBilinearFuser(FuseBase): layer = matches[layers_id[9]] new_layer.outputs[0] = layer.outputs[0] new_layer.layer_id = layers_id[7] - new_layer.inputs.pop("scale_factor") new_layer.inputs["size"] = size return new_layer diff --git a/x2paddle/optimizer/fusion/dygraph/tf_batchnorm_fuser.py b/x2paddle/optimizer/fusion/dygraph/tf_batchnorm_fuser.py index f3af6b08db3a7e9f817a47b26db389b6e279bf4c..6a53b1db29959a8cf7088347647db092b24f458c 100644 --- a/x2paddle/optimizer/fusion/dygraph/tf_batchnorm_fuser.py +++ b/x2paddle/optimizer/fusion/dygraph/tf_batchnorm_fuser.py @@ -74,7 +74,7 @@ class DygraphTFBatchNormFuser(FuseBase): inputs={}, outputs=[gen_name(8)]) pattern.add_layer( - "fluid.layers.elementwise_sub", + "paddle.subtract", inputs={"x": gen_name(8), "y": gen_name(7)}, outputs=[gen_name(9)]) pattern.add_layer( @@ -131,7 +131,7 @@ class DygraphTFBatchNormFuser(FuseBase): inputs={}, outputs=[gen_name(8)]) pattern.add_layer( - "fluid.layers.elementwise_sub", + "paddle.subtract", inputs={"x": gen_name(8), "y": gen_name(7)}, outputs=[gen_name(9)]) pattern.add_layer( @@ -180,7 +180,7 @@ class DygraphTFBatchNormFuser(FuseBase): if matches[out_layer_id].kernel == "paddle.multiply": gamma_layer_id = graph.edges_in[out_layer_id][1] gamma_layer = matches[gamma_layer_id] - if layer.kernel == "fluid.layers.elementwise_sub": + if layer.kernel == "paddle.subtract": in_layer_id = graph.edges_in[layer_id][0] beta_layer = matches[in_layer_id] in_layer_id = graph.edges_in[layer_id][1] diff --git a/x2paddle/optimizer/fusion/static/tf_batchnorm_fuser.py b/x2paddle/optimizer/fusion/static/tf_batchnorm_fuser.py index 796556bb96908be48fb4eca654c054b821da575f..1299b34d7664c10cc078679605932719ae0d9d11 100644 --- a/x2paddle/optimizer/fusion/static/tf_batchnorm_fuser.py +++ b/x2paddle/optimizer/fusion/static/tf_batchnorm_fuser.py @@ -73,7 +73,7 @@ class StaticTFBatchNormFuser(FuseBase): inputs={}, outputs=[gen_name(8)]) pattern.add_layer( - "fluid.layers.elementwise_sub", + "paddle.subtract", inputs={"x": gen_name(8), "y": gen_name(7)}, outputs=[gen_name(9)]) pattern.add_layer( @@ -130,7 +130,7 @@ class StaticTFBatchNormFuser(FuseBase): inputs={}, outputs=[gen_name(8)]) pattern.add_layer( - "fluid.layers.elementwise_sub", + "paddle.subtract", inputs={"x": gen_name(8), "y": gen_name(7)}, outputs=[gen_name(9)]) pattern.add_layer( @@ -179,7 +179,7 @@ class StaticTFBatchNormFuser(FuseBase): if matches[out_layer_id].kernel == "paddle.multiply": gamma_layer_id = graph.edges_in[out_layer_id][1] gamma_layer = matches[gamma_layer_id] - if layer.kernel == "fluid.layers.elementwise_sub": + if layer.kernel == "paddle.subtract": in_layer_id = graph.edges_in[layer_id][0] beta_layer = matches[in_layer_id] in_layer_id = graph.edges_in[layer_id][1]