diff --git a/docs/user_guides/add_caffe_custom_layer.md b/docs/user_guides/add_caffe_custom_layer.md index f925d16cb6a1a9ae9ec73f5410292c87a79456ac..575be41a577fbd03247c3fe51a6e68286d41bf96 100644 --- a/docs/user_guides/add_caffe_custom_layer.md +++ b/docs/user_guides/add_caffe_custom_layer.md @@ -36,6 +36,7 @@ pip install git+https://github.com/PaddlePaddle/X2Paddle.git@develop bash ./toos/compile.sh /home/root/caffe/src/caffe/proto # /home/root/caffe/src/caffe/proto为caffe.proto的存放路径,生成的caffe_pb2.py也将保存在该路径下 ``` +将生成的caffe_pb2.py替换x2paddle/decoder下的caffe_pb2.py。 ***步骤三 添加自定义Layer的实现代码*** > 【注意】若Caffe自定义layer与Paddle的op一一对应,使用方式一,否则使用方式二。 diff --git a/x2paddle/core/program.py b/x2paddle/core/program.py index 3d59ae95693ddb3361ee67747bd790fea204efc8..ebf0e81094cb4c6db5f8f7997aed4cba7b4a2d28 100644 --- a/x2paddle/core/program.py +++ b/x2paddle/core/program.py @@ -210,9 +210,12 @@ class PaddleGraph(object): if self.edges_in.get(layer_id, 0) == 0 and self.edges_out.get( layer_id, 0) == 0 and layer.kernel != "prim.assert" \ and layer.kernel != "prim.exception" \ - and layer.kernel != "prim.warnings": - if layer.kernel == "paddle.to_tensor": + and layer.kernel != "prim.warnings" \ + and layer.outputs[0] not in self.outputs: + if layer.kernel == "paddle.to_tensor" and layer.outputs[0] in self.inputs_info: self.inputs_info.pop(layer.outputs[0]) + if layer.outputs[0] in self.inputs: + self.inputs.pop(self.inputs.index(layer.outputs[0])) invalid_list.append(layer_id) for layer_id in invalid_list: self.layers.pop(layer_id) @@ -322,6 +325,9 @@ class PaddleGraph(object): if self.source_type == "caffe": custom_import = "from x2paddle.op_mapper.static.caffe2paddle " + \ "import caffe_custom_layer as x2paddle_nn" + elif self.source_type == "onnx": + custom_import = "from x2paddle.op_mapper.static.onnx2paddle " + \ + "import onnx_custom_layer as x2paddle_nn" else: custom_import = "" @@ -351,7 +357,9 @@ class PaddleGraph(object): remove_default_attrs(layer.kernel, layer.attrs) edges_in = self.edges_in.get(layer_id, []) edges_out = self.edges_out.get(layer_id, []) - if len(edges_in) == 0 and len(edges_out) == 0: + if len(edges_in) == 0 and len(edges_out) == 0 and layer.outputs[0] not in self.outputs: + if layer.outputs[0] in self.inputs: + self.inputs.pop(self.inputs.index(layer.outputs[0])) continue line = "" @@ -471,6 +479,9 @@ class PaddleGraph(object): elif self.source_type == "pytorch": custom_import = "from x2paddle.op_mapper.dygraph.pytorch2paddle " + \ "import pytorch_custom_layer as x2paddle_nn" + elif self.source_type == "onnx": + custom_import = "from x2paddle.op_mapper.dygraph.onnx2paddle " + \ + "import onnx_custom_layer as x2paddle_nn" else: custom_import = "" self.head = gen_codes( diff --git a/x2paddle/decoder/onnx_decoder.py b/x2paddle/decoder/onnx_decoder.py index 694b340e21b0e92cca15760dfc9f81aa89d97cd8..49c74200deb923ee7e007d6474ef46c43c9e5d09 100644 --- a/x2paddle/decoder/onnx_decoder.py +++ b/x2paddle/decoder/onnx_decoder.py @@ -31,6 +31,7 @@ import numpy as np from copy import deepcopy import logging as _logging import os +import copy default_op_domain = 'ai.onnx' _logger = _logging.getLogger(__name__) @@ -125,6 +126,17 @@ class ONNXGraphDataNode(GraphNode): shape.append(dim.dim_value) out_shapes.append(shape) return out_shapes + elif isinstance(self.layer, TensorProto): + values = self.layer.dims + out_shapes = list() + shape = list() + for dim in values: + if dim == 0: + shape.append(-1) + else: + shape.append(dim) + out_shapes.append(shape) + return out_shapes else: values = self.layer.dims out_shapes = list() @@ -234,11 +246,12 @@ class ONNXGraph(Graph): """ generate output_nodes node of ONNX model """ - inner_nodes = self.get_inner_nodes() output_nodes = [value.name for value in self.graph.output] for opt_data in output_nodes: - if opt_data not in inner_nodes: - self.output_nodes.append(opt_data) + 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) def is_place_holder_nodes(self, layer): """ @@ -286,7 +299,7 @@ class ONNXGraph(Graph): #generate topo super(ONNXGraph, self).build() - self.input_nodes = self.place_holder_nodes + self.input_nodes = copy.deepcopy(self.place_holder_nodes) def build_connection(self, layer_name, node): """ @@ -403,10 +416,8 @@ class ONNXDecoder(object): check_model(onnx_model) onnx_model = self.optimize_model_skip_op(onnx_model) - onnx_model = self.optimize_model_strip_initializer(onnx_model) onnx_model = self.optimize_node_name(onnx_model) self.graph = ONNXGraph(onnx_model) - #self.onnx_model = onnx_model def build_value_refs(self, nodes): """ diff --git a/x2paddle/op_mapper/dygraph/onnx2paddle/onnx_custom_layer/__init__.py b/x2paddle/op_mapper/dygraph/onnx2paddle/onnx_custom_layer/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..887bb45cdd6c86a62c7cce68d99b3e0cf3328bd1 --- /dev/null +++ b/x2paddle/op_mapper/dygraph/onnx2paddle/onnx_custom_layer/__init__.py @@ -0,0 +1,20 @@ +# Copyright (c) 2020 PaddlePaddle Authors. All Rights Reserved. +# +# 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. + + +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 diff --git a/x2paddle/op_mapper/dygraph/onnx2paddle/onnx_custom_layer/one_hot.py b/x2paddle/op_mapper/dygraph/onnx2paddle/onnx_custom_layer/one_hot.py new file mode 100644 index 0000000000000000000000000000000000000000..def62ed8e8501dba5beb86e0759214b559cc0d0a --- /dev/null +++ b/x2paddle/op_mapper/dygraph/onnx2paddle/onnx_custom_layer/one_hot.py @@ -0,0 +1,38 @@ +# Copyright (c) 2020 PaddlePaddle Authors. All Rights Reserved. +# +# 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. + +import paddle + +class OneHot(object): + def __init__(self, axis): + self.axis = axis + + def __call__(self, indices, depth, values): + indices_shape = indices.shape + rank = len(indices.shape) + real_axis = self.axis + if self.axis < 0: + real_axis = self.axis + rank + 1 + depth_range = paddle.arange(end=depth) + ls = tuple(indices_shape[0: real_axis]) + rs = tuple(indices_shape[real_axis: rank]) + targets = paddle.reshape(depth_range, (1,) * (real_axis-0) + tuple(depth_range.shape) + (1,) * (rank-real_axis)) + mod = paddle.mod(indices, depth) + v = paddle.reshape(mod, ls + (1,) + rs) + out = targets == v + out = paddle.cast(out, "float32") + on_value = paddle.slice(values, axes=[0], starts=[1], ends=[2]) + off_value = paddle.slice(values, axes=[0], starts=[0], ends=[1]) + out = out * (on_value - off_value) + off_value + return out \ No newline at end of file diff --git a/x2paddle/op_mapper/dygraph/onnx2paddle/onnx_custom_layer/pad_all_dim2.py b/x2paddle/op_mapper/dygraph/onnx2paddle/onnx_custom_layer/pad_all_dim2.py new file mode 100644 index 0000000000000000000000000000000000000000..6228ae7bbad5f39db998dab41fc824fa51182f03 --- /dev/null +++ b/x2paddle/op_mapper/dygraph/onnx2paddle/onnx_custom_layer/pad_all_dim2.py @@ -0,0 +1,35 @@ +# Copyright (c) 2020 PaddlePaddle Authors. All Rights Reserved. +# +# 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. + +import paddle +from x2paddle.core.util import * + +class PadAllDim2(object): + def __init__(self, value, mode): + self.layer_attrs = {} + self.layer_attrs['mode'] = mode + self.layer_attrs['data_format'] = 'NCHW' + self.layer_attrs['value'] = value + + + def __call__(self, x, pad): + pad = paddle.reshape(pad, shape=[2, -1]) + pad = paddle.transpose(pad, perm=[1, 0]) + pad = paddle.reverse(pad, axis=[0]) + pad = paddle.flatten(pad) + pad = paddle.cast(pad, dtype="int32") + x = paddle.unsqueeze(x, axis=[0, 1]) + out = paddle.nn.functional.pad(x=x, pad=pad, **self.layer_attrs) + out = paddle.squeeze(out, axis=[0, 1]) + return out \ No newline at end of file diff --git a/x2paddle/op_mapper/dygraph/onnx2paddle/onnx_custom_layer/pad_all_dim4.py b/x2paddle/op_mapper/dygraph/onnx2paddle/onnx_custom_layer/pad_all_dim4.py new file mode 100644 index 0000000000000000000000000000000000000000..d1c2c382cda57a211b465155acace5d578a1657d --- /dev/null +++ b/x2paddle/op_mapper/dygraph/onnx2paddle/onnx_custom_layer/pad_all_dim4.py @@ -0,0 +1,37 @@ +# Copyright (c) 2020 PaddlePaddle Authors. All Rights Reserved. +# +# 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. + +import paddle +from x2paddle.core.util import * + +class PadAllDim4(object): + def __init__(self, value, mode): + self.layer_attrs = {} + self.layer_attrs['mode'] = mode + self.layer_attrs['data_format'] = 'NCHW' + self.layer_attrs['value'] = value + + + def __call__(self, x, pad): + pad = paddle.reshape(pad, shape=[2, -1]) + pad = paddle.transpose(pad, perm=[1, 0]) + pad = paddle.reverse(pad, axis=[0]) + pad = paddle.flatten(pad) + pad = paddle.cast(pad, dtype="int32") + pad1, pad2 = paddle.split(pad, num_or_sections=2, axis=0) + x = paddle.nn.functional.pad(x=x, pad=pad1, **self.layer_attrs) + x = paddle.transpose(x, perm=[2, 3, 0, 1]) + x = paddle.nn.functional.pad(x=x, pad=pad2, **self.layer_attrs) + out = paddle.transpose(x, perm=[2, 3, 0, 1]) + return out \ No newline at end of file diff --git a/x2paddle/op_mapper/dygraph/onnx2paddle/onnx_custom_layer/pad_all_dim4_one_input.py b/x2paddle/op_mapper/dygraph/onnx2paddle/onnx_custom_layer/pad_all_dim4_one_input.py new file mode 100644 index 0000000000000000000000000000000000000000..9ad3048081a1dbb6dfe57cce2a235a8a57aa6c2f --- /dev/null +++ b/x2paddle/op_mapper/dygraph/onnx2paddle/onnx_custom_layer/pad_all_dim4_one_input.py @@ -0,0 +1,32 @@ +# Copyright (c) 2020 PaddlePaddle Authors. All Rights Reserved. +# +# 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. + +import paddle +from x2paddle.core.util import * + +class PadAllDim4WithOneInput(object): + def __init__(self, pad, value, mode): + self.layer_attrs = {} + self.layer_attrs['mode'] = mode + self.layer_attrs['data_format'] = 'NCHW' + self.layer_attrs['value'] = value + self.pad1 = pad[0: 4] + self.pad2 = pad[4: 9] + + def __call__(self, x): + x = paddle.nn.functional.pad(x=x, pad=self.pad1, **self.layer_attrs) + x = paddle.transpose(x, perm=[2, 3, 0, 1]) + x = paddle.nn.functional.pad(x=x, pad=self.pad2, **self.layer_attrs) + out = paddle.transpose(x, perm=[2, 3, 0, 1]) + return out \ No newline at end of file diff --git a/x2paddle/op_mapper/dygraph/onnx2paddle/onnx_custom_layer/pad_two_input.py b/x2paddle/op_mapper/dygraph/onnx2paddle/onnx_custom_layer/pad_two_input.py new file mode 100644 index 0000000000000000000000000000000000000000..e1053eda35b399a3cb3976347d30659e8ef74d7d --- /dev/null +++ b/x2paddle/op_mapper/dygraph/onnx2paddle/onnx_custom_layer/pad_two_input.py @@ -0,0 +1,33 @@ +# Copyright (c) 2020 PaddlePaddle Authors. All Rights Reserved. +# +# 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. + +import paddle +from x2paddle.core.util import * + +class PadWithTwoInput(object): + def __init__(self, value, mode, data_format): + self.layer_attrs = {} + self.layer_attrs['mode'] = mode + self.layer_attrs['data_format'] = data_format + self.layer_attrs['value'] = value + + + def __call__(self, x, pad): + pad = paddle.reshape(pad, shape=[2, -1]) + pad = paddle.transpose(pad, perm=[1, 0]) + pad = paddle.reverse(pad, axis=[0]) + pad = paddle.flatten(pad) + pad = paddle.cast(pad, dtype="int32") + out = paddle.nn.functional.pad(x=x, pad=pad, **self.layer_attrs) + return out \ No newline at end of file diff --git a/x2paddle/op_mapper/dygraph/onnx2paddle/opset9/opset.py b/x2paddle/op_mapper/dygraph/onnx2paddle/opset9/opset.py index 0eeb7993a5bf4f37b30a1ddd14ffffd166d0e222..24c35ccd1279d4a95c9baece326760532d643abe 100644 --- a/x2paddle/op_mapper/dygraph/onnx2paddle/opset9/opset.py +++ b/x2paddle/op_mapper/dygraph/onnx2paddle/opset9/opset.py @@ -104,6 +104,9 @@ class OpSet9(): 'ReduceMax': ['paddle.max', dict(axes='axis', keepdims='keepdim'), dict(keepdim=1)], + 'ReduceProd': ['paddle.prod', + dict(axes='axis', keepdims='keepdim'), + dict(keepdim=1)], # active function 'Relu': ['paddle.nn.ReLU'], 'LeakyRelu': ['paddle.nn.LeakyReLU', @@ -139,6 +142,7 @@ class OpSet9(): self.inputs_info = dict() self.weights = dict() self.nn_name2id = dict() + self.done_weight_list = list() @print_mapping_info def directly_map(self, node, *args, **kwargs): @@ -212,7 +216,7 @@ class OpSet9(): node = parameter dtype = node.dtype shape = node.out_shapes[0] - if len(node.weight.shape) == 0: + if hasattr(node.weight, "shape") and len(node.weight.shape) == 0: self.paddle_graph.add_layer( "paddle.full", inputs={}, @@ -229,8 +233,7 @@ class OpSet9(): shape=shape, attr=string(node.name), dtype=string(dtype), - default_initializer="paddle.nn.initializer.Constant(value=0.0)") - + default_initializer="paddle.nn.initializer.Constant(value=0.0)") def _pad_if_asymmetric(self, node, pads, val_name): # pads: SSEE assert len(pads) & 1 == 0 @@ -296,6 +299,10 @@ class OpSet9(): attrs.update({"align_corners": False, "mode": string(mode), "align_mode": 1}) + val_x_shape = val_x.out_shapes[0] + if mode == "linear" and len(val_x_shape) == 4: + attrs["mode"] = string("bilinear") + attrs["align_corners"] = True self.paddle_graph.add_layer( kernel="paddle.nn.functional.interpolate", inputs=inputs, @@ -379,63 +386,131 @@ class OpSet9(): def Pad(self, node, op_independent=True): val_x = self.graph.get_input_node(node, idx=0, copy=True) pads = node.get_attr('pads') + is_pads_attr = True + if pads is None: + val_pad = self.graph.get_input_node(node, idx=1, copy=True) + pad_shape = val_pad.out_shapes[0] + is_pads_attr = False + pads = _const_weight_or_none(val_pad) + if pads is not None: + is_pads_attr = True mode = node.get_attr('mode', 'constant') value = node.get_attr('value', 0.) data_shape = val_x.out_shapes[0] output_shape = node.out_shapes[0] - assume_pad2d = False + assume_pad = False layer_attrs = {} layer_attrs['mode'] = string(mode) - paddings = [] - if len(pads) == 4: - assume_pad2d |= mode != 'constant' - if data_shape: - assume_pad2d |= data_shape and len(data_shape) == 4 # NCHW - if output_shape: - assume_pad2d |= output_shape and len(output_shape) == 4 # NCHW - if assume_pad2d: - paddle_op = 'paddle.nn.Pad2D' - layer_attrs['data_format'] = string('NCHW') - layer_attrs['value'] = value - else: - paddle_op = 'paddle.fluid.layers.pad' - layer_attrs["pad_value"] = value - if len(pads) == 4: - paddings = np.array(pads).reshape( - (-1, 2)).transpose().flatten().tolist() # SSEE -> SESE - elif len(pads) == 8: - paddings = np.array(pads).reshape( - (-1, 4)).transpose().flatten().tolist() # SSEE -> SESE - if sum(paddings[:4]) == 0: - paddle_op = 'paddle.nn.Pad2D' - paddings = paddings[4:] - layer_attrs['value'] = value - if 'pad_value' in layer_attrs: - layer_attrs.pop('pad_value') - tmp_paddings = copy.deepcopy(paddings) - paddings[0] = tmp_paddings[2] - paddings[1] = tmp_paddings[3] - paddings[2] = tmp_paddings[0] - paddings[3] = tmp_paddings[1] - if paddle_op == 'paddle.nn.Pad2D': - layer_attrs['padding'] = paddings - nn_op_name = name_generator("pad2d", self.nn_name2id) + layer_attrs['value'] = value + if not op_independent: + output_name = node.name + '_paded' else: - layer_attrs['paddings'] = paddings - if op_independent: + output_name = node.name + nn_op_name = name_generator("pad", self.nn_name2id) + layer_outputs = [nn_op_name, output_name] + if is_pads_attr: + paddings = [] + if len(pads) in [2, 4, 6]: + if data_shape: + assume_pad |= data_shape and 2 * (len(data_shape) - 2) == len(pads) # NCHW + if output_shape: + assume_pad |= output_shape and 2 * (len(output_shape) - 2) == len(pads) # NCHW + if assume_pad: + paddle_op = 'paddle.nn.Pad{}D'.format(len(output_shape) - 2) + paddings = np.array(pads).reshape( + (2, -1)).transpose().astype("int32") + paddings = np.flip(paddings, axis=0).flatten().tolist() + layer_attrs['padding'] = paddings + else: + if data_shape: + assume_pad |= data_shape and 2 * len(data_shape) == len(pads) # NCHW + if output_shape: + assume_pad |= output_shape and 2 * len(output_shape) == len(pads) # NCHW + if assume_pad: + paddle_op = 'paddle.nn.functional.pad' + paddings = np.array(pads).reshape( + (2, -1)).transpose().astype("int32").flatten().tolist() + layer_attrs['pad'] = paddings + else: + raise Exception("The padding value {} is wrong!".format(pads)) + elif len(pads) == 8: + if data_shape: + assume_pad |= data_shape and 2 * len(data_shape) == len(pads) # NCHW + if output_shape: + assume_pad |= output_shape and 2 * len(output_shape) == len(pads) # NCHW + if assume_pad: + paddle_op = 'paddle.nn.Pad2D' + paddings = np.array(pads).reshape( + (2, -1)).transpose().astype("int32") + paddings = np.flip(paddings, axis=0).flatten().tolist() + if sum(paddings[:4]) == 0: + paddings = paddings[4:] + layer_attrs['padding'] = paddings + else: + layer_attrs["pad"] = paddings + paddle_op = "custom_layer:PadAllDim4WithOneInput" + else: + raise Exception("The padding value {} is wrong!".format(pads)) self.paddle_graph.add_layer( paddle_op, inputs={'x': val_x.name}, - outputs=[nn_op_name, node.name] if paddle_op == 'paddle.nn.Pad2D' else [node.name], + outputs=layer_outputs[1:] if paddle_op == 'paddle.nn.functional.pad' else layer_outputs, **layer_attrs) + if not op_independent: + return node.name + '_paded' else: - self.paddle_graph.add_layer( - paddle_op, - inputs={'x': val_x.name}, - outputs=[nn_op_name, node.name + '_paded'] if paddle_op == 'paddle.nn.Pad2D' \ - else [node.name + '_paded'], - **layer_attrs) - return node.name + '_paded' + pads_len = val_pad.out_shapes[0][0] + if pads_len in [2, 4, 6]: + if data_shape: + assume_pad |= data_shape and 2 * (len(data_shape) - 2) == pads_len # NCHW + if output_shape: + assume_pad |= output_shape and 2 * (len(output_shape) - 2) == pads_len # NCHW + if assume_pad: + if pads_len == 2: + data_format = "NCL" + elif pads_len == 4: + data_format = "NCHW" + else: + data_format = "NCDHW" + self.paddle_graph.add_layer( + "custom_layer:PadWithTwoInput", + inputs={'x': val_x.name, 'pad': val_pad.name}, + outputs=layer_outputs, + value=value, + mode=string(mode), + data_format=string(data_format)) + else: + if data_shape: + assume_pad |= data_shape and 2 * len(data_shape) == pads_len # NCHW + if output_shape: + assume_pad |= output_shape and 2 * len(output_shape) == pads_len # NCHW + if assume_pad: + if pads_len == 4: + self.paddle_graph.add_layer( + "custom_layer:PadAllDim2", + inputs={'x': val_x.name, 'pad': val_pad.name}, + outputs=layer_outputs, + value=value, + mode=string(mode)) + else: + raise Exception("The padding value is wrong!") + elif pads_len == 8: + if data_shape: + assume_pad |= data_shape and 2 * len(data_shape) == pads_len # NCHW + if output_shape: + assume_pad |= output_shape and 2 * len(output_shape) == pads_len # NCHW + if assume_pad: + self.paddle_graph.add_layer( + "custom_layer:PadAllDim4", + inputs={'x': val_x.name, 'pad': val_pad.name}, + outputs=layer_outputs, + value=value, + mode=string(mode)) + else: + print(pads_len) + raise Exception("The padding value is wrong!") + if not op_independent: + return node.name + '_paded' @print_mapping_info def Unsqueeze(self, node): @@ -637,7 +712,7 @@ class OpSet9(): self.paddle_graph.add_layer( 'paddle.cast', inputs={"x": indices.name}, - outputs=indices_cast, + outputs=[indices_cast], dtype=string('int64')) op_name = name_generator("embedding", self.nn_name2id) output_name = node.name @@ -646,8 +721,9 @@ class OpSet9(): 'paddle.nn.Embedding', inputs={"x": indices_cast}, outputs=layer_outputs, - param_attr=string(val_x.name), - size=val_x.out_shapes[0]) + weight_attr=string(val_x.name), + num_embeddings=val_x.out_shapes[0][0], + embedding_dim=val_x.out_shapes[0][1]) else: from functools import reduce reshape_shape = reduce(lambda x, y: x * y, indices_shape) @@ -819,20 +895,27 @@ class OpSet9(): starts = self.graph.get_input_node(node, idx=1, copy=True) ends = self.graph.get_input_node(node, idx=2, copy=True) starts_value = _const_weight_or_none(starts) + if starts_value is not None: + starts_value = starts_value.tolist() ends_value = _const_weight_or_none(ends) - + if ends_value is not None: + ends_value = ends_value.tolist() + if len(node.inputs) > 2: + s_len = len(val_x.out_shapes[0]) + axes = list(range(s_len)) if len(node.inputs) > 3: - axes = self.graph.get_input_node(node, idx=3, copy=True) - axes = _const_weight_or_none(axes, necessary=True) + axes_node = self.graph.get_input_node(node, idx=3, copy=True) + axes = _const_weight_or_none(axes_node, necessary=True).tolist() if len(node.inputs) > 4: steps = self.graph.get_input_node(node, idx=4, copy=True) - steps = _const_weight_or_none(steps) + steps = _const_weight_or_none(steps).tolist() + layer_attrs = { "axes": axes, "starts": starts.name, "ends": ends.name } - if starts_value is not None and ends_value is not None: + 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)): @@ -862,6 +945,8 @@ class OpSet9(): layer_attrs['starts'] = starts_cast if ends.dtype != 'int32': ends_cast = ends.name + '_cast' + else: + ends_cast = ends.name self.paddle_graph.add_layer( 'paddle.cast', inputs={"x": ends.name}, @@ -877,6 +962,7 @@ class OpSet9(): ends[idx] = 2**31 - 1 layer_attrs = {"axes": axes, "starts": starts, "ends": ends} + if steps is not None: layer_attrs['strides'] = steps self.paddle_graph.add_layer( @@ -1002,11 +1088,17 @@ class OpSet9(): inputs={'x': val_shape.name}, outputs=[val_shape.name], shape=val_shape.out_shapes[0]) + if val_shape.dtype != "int32": + self.paddle_graph.add_layer( + 'paddle.cast', + inputs={'x': val_shape.name}, + outputs=[val_shape.name], + dtype=string("int32")) self.paddle_graph.add_layer( 'paddle.reshape', inputs={'x': val_x.name, 'shape': val_shape.name}, - outputs=node) + outputs=[node.name]) @print_mapping_info def Cast(self, node): @@ -1246,7 +1338,10 @@ class OpSet9(): @print_mapping_info def Transpose(self, node): val_x = self.graph.get_input_node(node, idx=0, copy=True) - perm = node.get_attr('perm') + s_len = len(val_x.out_shapes[0]) + perm_default = list(range(s_len)) + perm_default.reverse() + perm = node.get_attr('perm', perm_default) self.paddle_graph.add_layer( "paddle.transpose", inputs={"x": val_x.name}, @@ -1265,24 +1360,48 @@ class OpSet9(): shape_slope = val_slope.out_shapes[0] if shape_slope == [1]: mode = 'all' - elif len(shape_slope) > 2: - raise Exception("The 'element' mode is not supported yet!") - - if mode == 'channel' and len(shape_slope) == 1: - # paddle params shape need be [1, channel] - slope_data = _const_weight_or_none(val_slope) - slope_data = np.reshape(slope_data, [1] + shape_slope) - self.weights[val_slope.name] = slope_data - num_parameters = val_x.out_shapes[0][1] - else: - num_parameters = 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)) + if mode == "element": + self.paddle_graph.add_layer( + "paddle.zeros", + inputs={}, + outputs=[output_name + "__zeros"], + shape=shape_slope, + dtype=string(node.dtype)) + self.paddle_graph.add_layer( + "paddle.maximum", + inputs={"x": val_x.name, + "y": output_name + "__zeros"}, + outputs=[output_name + "__max"]) + self.paddle_graph.add_layer( + "paddle.minimum", + inputs={"x": val_x.name, + "y": output_name + "__zeros"}, + outputs=[output_name + "__max"]) + self.paddle_graph.add_layer( + "paddle.multiply", + inputs={"x": val_slope.name, + "y": output_name + "__min"}, + outputs=[output_name + "__mul"]) + self.paddle_graph.add_layer( + "paddle.add", + inputs={"x": output_name + "__max", + "y": output_name + "__mul"}, + outputs=[output_name]) + else: + if mode == 'channel': + slope_data = _const_weight_or_none(val_slope) + if len(shape_slope) > 1: + self.weights[val_slope.name] = np.reshape(slope_data, shape_slope[0]) + num_parameters = val_x.out_shapes[0][1] + else: + num_parameters = 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)) @print_mapping_info def Squeeze(self, node): @@ -1550,6 +1669,7 @@ class OpSet9(): strides[1]) paddings = pad_h + pad_w + layer_inputs = {'x': val_x if isinstance(val_x, str) else val_x.name} layer_attrs = { "in_channels": num_in_channels * num_groups, "out_channels": num_out_channels, @@ -1558,15 +1678,35 @@ class OpSet9(): "padding": paddings, "dilation": dilations, "groups": num_groups, - 'weight_attr': string(val_w.name), } + 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] if has_bias: - layer_attrs["bias_attr"] = string(val_b.name) + 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] else: layer_attrs["bias_attr"] = False + input_shape = val_x.out_shapes[0] + if reduce(lambda x,y:x*y, input_shape) in [1, -1] and 1 not in input_shape: + input_shape[1] = num_in_channels * num_groups + input_shape[0] = 0 + input_shape[2] = 0 + self.paddle_graph.add_layer( + "paddle.reshape", + inputs=layer_inputs, + outputs=[layer_inputs["x"]], + shape=input_shape) self.paddle_graph.add_layer( paddle_op, - inputs={'x': val_x if isinstance(val_x, str) else val_x.name}, + inputs=layer_inputs, outputs=layer_outputs, **layer_attrs) @@ -1633,4 +1773,66 @@ class OpSet9(): 'paddle.argmax', inputs={"x": val_x.name}, outputs=[node.name], - **layer_attrs) \ No newline at end of file + **layer_attrs) + + @print_mapping_info + def Size(self, node): + val_x = self.graph.get_input_node(node, idx=0, copy=True) + self.paddle_graph.add_layer( + "paddle.shape", + inputs={"input": val_x.name}, + outputs=[node.name]) + self.paddle_graph.add_layer( + 'paddle.cast', + inputs={"x": node.name}, + outputs=[node.name], + dtype=string('int64')) + self.paddle_graph.add_layer( + "paddle.prod", + inputs={"x": node.name}, + outputs=[node.name]) + + @print_mapping_info + def Sign(self, node): + val_x = self.graph.get_input_node(node, idx=0, copy=True) + if node.dtype not in ["float16", "float32", "float64"]: + self.paddle_graph.add_layer( + "paddle.cast", + inputs={"x": val_x.name}, + outputs=[val_x.name], + dtype=string("float32")) + self.paddle_graph.add_layer( + "paddle.sign", + inputs={"x": val_x.name}, + outputs=[node.name]) + if node.dtype not in ["float16", "float32", "float64"]: + self.paddle_graph.add_layer( + "paddle.cast", + inputs={"x": node.name}, + outputs=[node.name], + dtype=string(node.dtype)) + + @print_mapping_info + def OneHot(self, node): + nn_op_name = name_generator("onehot", self.nn_name2id) + output_name = node.name + layer_outputs = [nn_op_name, output_name] + indices = self.graph.get_input_node(node, idx=0, copy=True) + depth = self.graph.get_input_node(node, idx=1, copy=True) + values = self.graph.get_input_node(node, idx=2, copy=True) + axis = node.get_attr('axis', -1) + self.paddle_graph.add_layer( + "custom_layer:OneHot", + inputs={"indices": indices.name, + "depth": depth.name, + "values": values.name}, + outputs=layer_outputs, + axis=axis) + + @print_mapping_info + def Reciprocal(self, node): + val_x = self.graph.get_input_node(node, idx=0, copy=True) + self.paddle_graph.add_layer( + "paddle.reciprocal", + inputs={"x": val_x.name}, + outputs=[node.name]) diff --git a/x2paddle/op_mapper/dygraph/pytorch2paddle/aten.py b/x2paddle/op_mapper/dygraph/pytorch2paddle/aten.py index 00121bb4128182506daca12e612e36e44375b421..28c2e6ae45e4b777b92cafc05b35d3f5c7086e58 100644 --- a/x2paddle/op_mapper/dygraph/pytorch2paddle/aten.py +++ b/x2paddle/op_mapper/dygraph/pytorch2paddle/aten.py @@ -4537,6 +4537,72 @@ def aten_upsample_bilinear2d(mapper, graph, node): **layer_attrs) return current_inputs, current_outputs +def aten_upsample_nearest2d(mapper, graph, node): + """ 构造使用nearest上采样的PaddleLayer。 + + TorchScript示例: + %4997 : Tensor = aten::upsample_nearest2d(%x.13, %4963, %5421, %4995) + 参数含义: + %4997 (Tensor): 输出,上采样后的Tensor。 + %x.13 (Tensor): 需要上采样的Tensor。 + %4963 (list): 上采样后的大小。 + %4995 (float): 高度的乘数因子。 + %4995 (float): 宽度的乘数因子。 + """ + scope_name = mapper.normalize_scope_name(node) + output_name = mapper._get_outputs_name(node)[0] + layer_outputs = [output_name] + layer_inputs = {} + layer_attrs = {} + inputs_name, inputs_node = mapper._get_inputs_name(node) + # 获取当前节点输出的list + current_outputs = [output_name] + # 处理输入0,即%x.13 + mapper._check_input(graph, inputs_node[0], inputs_name[0], current_outputs, scope_name) + layer_inputs["x"] = inputs_name[0] + # 获取当前节点输入的list + current_inputs = list(layer_inputs.values()) + # 处理输入1,即%4963 + if inputs_name[1] in mapper.attrs: + layer_attrs["size"] = mapper.attrs[inputs_name[1]] + else: + mapper._check_input(graph, inputs_node[1], inputs_name[1], + current_outputs, scope_name) + layer_inputs["size"] = inputs_name[1] + current_inputs.append(inputs_name[1]) + graph.add_layer( + "prim.isinstance", + inputs={"input": inputs_name[1]}, + outputs=[inputs_name[1] + "_isinstance"], + scope_name=scope_name, + cls="paddle.fluid.Variable") + # TODO(syf): paddle.Variable + graph.add_layer( + "prim.if", {"input": inputs_name[1] + "_isinstance"}, + outputs=[inputs_name[0] + "_if1"], + scope_name=scope_name) + if_layer = graph.layers[list(graph.layers.keys())[-1]] + block = PaddleGraph(source_type="pytorch", parent_layer=if_layer, graph_type="dygraph") + block.add_layer( + "prim.var2list", + inputs={"input": inputs_name[1]}, + outputs=[inputs_name[1]], + scope_name=scope_name) + if_layer.add_block(block) + 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] + layer_attrs["align_mode"] = 0 + layer_attrs["mode"] = string("nearest") + graph.add_layer( + "paddle.nn.functional.interpolate", + inputs=layer_inputs, + outputs=layer_outputs, + scope_name=scope_name, + **layer_attrs) + return current_inputs, current_outputs + def aten_values(mapper, graph, node): """ 构造对比大小的PaddleLayer。 diff --git a/x2paddle/op_mapper/dygraph/pytorch2paddle/pytorch_custom_layer/gather.py b/x2paddle/op_mapper/dygraph/pytorch2paddle/pytorch_custom_layer/gather.py index 10850ee5bbf91fa42e39f4dbd67ec1fa0d6682d7..6ef9d488587cdbe01dbf7ae343a008094113bc81 100644 --- a/x2paddle/op_mapper/dygraph/pytorch2paddle/pytorch_custom_layer/gather.py +++ b/x2paddle/op_mapper/dygraph/pytorch2paddle/pytorch_custom_layer/gather.py @@ -13,8 +13,6 @@ # limitations under the License. import paddle -from itertools import product -import numpy as np class Gather(object): def __init__(self, dim): diff --git a/x2paddle/op_mapper/static/onnx2paddle/onnx_custom_layer/__init__.py b/x2paddle/op_mapper/static/onnx2paddle/onnx_custom_layer/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..d8161ae1f8a76bff61a1cd8a23de15b3a1bf9549 --- /dev/null +++ b/x2paddle/op_mapper/static/onnx2paddle/onnx_custom_layer/__init__.py @@ -0,0 +1,20 @@ +# Copyright (c) 2020 PaddlePaddle Authors. All Rights Reserved. +# +# 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. + + +from .one_hot import one_hot +from .pad_two_input import pad_with_two_input +from .pad_all_dim2 import pad_all_dim2 +from .pad_all_dim4 import pad_all_dim4 +from .pad_all_dim4_one_input import pad_all_dim4_one_input \ No newline at end of file diff --git a/x2paddle/op_mapper/static/onnx2paddle/onnx_custom_layer/one_hot.py b/x2paddle/op_mapper/static/onnx2paddle/onnx_custom_layer/one_hot.py new file mode 100644 index 0000000000000000000000000000000000000000..146f643f8c1f014eef79095107ff8de027127c9d --- /dev/null +++ b/x2paddle/op_mapper/static/onnx2paddle/onnx_custom_layer/one_hot.py @@ -0,0 +1,34 @@ +# Copyright (c) 2020 PaddlePaddle Authors. All Rights Reserved. +# +# 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. + +import paddle + +def one_hot(indices, depth, values, axis): + indices_shape = indices.shape + rank = len(indices.shape) + real_axis = axis + if axis < 0: + real_axis = axis + rank + 1 + depth_range = paddle.arange(end=depth) + ls = tuple(indices_shape[0: real_axis]) + rs = tuple(indices_shape[real_axis: rank]) + targets = paddle.reshape(depth_range, (1,) * (real_axis-0) + tuple(depth_range.shape) + (1,) * (rank-real_axis)) + mod = paddle.mod(indices, depth) + v = paddle.reshape(mod, ls + (1,) + rs) + out = targets == v + out = paddle.cast(out, "float32") + on_value = paddle.slice(values, axes=[0], starts=[1], ends=[2]) + off_value = paddle.slice(values, axes=[0], starts=[0], ends=[1]) + out = out * (on_value - off_value) + off_value + return out \ No newline at end of file diff --git a/x2paddle/op_mapper/static/onnx2paddle/onnx_custom_layer/pad_all_dim2.py b/x2paddle/op_mapper/static/onnx2paddle/onnx_custom_layer/pad_all_dim2.py new file mode 100644 index 0000000000000000000000000000000000000000..59af5987039ab962f30b6f25626f365ba76e6dfa --- /dev/null +++ b/x2paddle/op_mapper/static/onnx2paddle/onnx_custom_layer/pad_all_dim2.py @@ -0,0 +1,30 @@ +# Copyright (c) 2020 PaddlePaddle Authors. All Rights Reserved. +# +# 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. + +import paddle + +def pad_all_dim2(x, pad, value, mode): + pad = paddle.reshape(pad, shape=[2, -1]) + pad = paddle.transpose(pad, perm=[1, 0]) + pad = paddle.reverse(pad, axis=[0]) + pad = paddle.flatten(pad) + pad = paddle.cast(pad, dtype="int32") + x = paddle.unsqueeze(x, axis=[0, 1]) + out = paddle.nn.functional.pad(x=x, + pad=pad, + mode=mode, + data_format='NCHW', + value=value) + out = paddle.squeeze(out, axis=[0, 1]) + return out \ No newline at end of file diff --git a/x2paddle/op_mapper/static/onnx2paddle/onnx_custom_layer/pad_all_dim4.py b/x2paddle/op_mapper/static/onnx2paddle/onnx_custom_layer/pad_all_dim4.py new file mode 100644 index 0000000000000000000000000000000000000000..df8c42c3379baa5f0b7f88b4085cb2880a4b396f --- /dev/null +++ b/x2paddle/op_mapper/static/onnx2paddle/onnx_custom_layer/pad_all_dim4.py @@ -0,0 +1,36 @@ +# Copyright (c) 2020 PaddlePaddle Authors. All Rights Reserved. +# +# 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. + +import paddle + +def pad_all_dim4(x, pad, value, mode): + pad = paddle.reshape(pad, shape=[2, -1]) + pad = paddle.transpose(pad, perm=[1, 0]) + pad = paddle.reverse(pad, axis=[0]) + pad = paddle.flatten(pad) + pad = paddle.cast(pad, dtype="int32") + pad1, pad2 = paddle.split(pad, num_or_sections=2, axis=0) + x = paddle.nn.functional.pad(x=x, + pad=pad1, + mode=mode, + data_format='NCHW', + value=value) + x = paddle.transpose(x, perm=[2, 3, 0, 1]) + x = paddle.nn.functional.pad(x=x, + pad=pad2, + mode=mode, + data_format='NCHW', + value=value) + out = paddle.transpose(x, perm=[2, 3, 0, 1]) + return out \ No newline at end of file diff --git a/x2paddle/op_mapper/static/onnx2paddle/onnx_custom_layer/pad_all_dim4_one_input.py b/x2paddle/op_mapper/static/onnx2paddle/onnx_custom_layer/pad_all_dim4_one_input.py new file mode 100644 index 0000000000000000000000000000000000000000..796b9f366cb1666abe88f07462d60b045e396a35 --- /dev/null +++ b/x2paddle/op_mapper/static/onnx2paddle/onnx_custom_layer/pad_all_dim4_one_input.py @@ -0,0 +1,30 @@ +# Copyright (c) 2020 PaddlePaddle Authors. All Rights Reserved. +# +# 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. + +import paddle + +def pad_all_dim4_one_input(x, pad, value, mode): + x = paddle.nn.functional.pad(x=x, + pad=pad[0: 4], + mode=mode, + data_format='NCHW', + value=value) + x = paddle.transpose(x, perm=[2, 3, 0, 1]) + x = paddle.nn.functional.pad(x=x, + pad=pad[4: 9], + mode=mode, + data_format='NCHW', + value=value) + out = paddle.transpose(x, perm=[2, 3, 0, 1]) + return out \ No newline at end of file diff --git a/x2paddle/op_mapper/static/onnx2paddle/onnx_custom_layer/pad_two_input.py b/x2paddle/op_mapper/static/onnx2paddle/onnx_custom_layer/pad_two_input.py new file mode 100644 index 0000000000000000000000000000000000000000..dfcbe49f020907462fd0da8a433089bced11ca95 --- /dev/null +++ b/x2paddle/op_mapper/static/onnx2paddle/onnx_custom_layer/pad_two_input.py @@ -0,0 +1,28 @@ +# Copyright (c) 2020 PaddlePaddle Authors. All Rights Reserved. +# +# 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. + +import paddle + +def pad_with_two_input(x, pad, value, mode, data_format): + pad = paddle.reshape(pad, shape=[2, -1]) + pad = paddle.transpose(pad, perm=[1, 0]) + pad = paddle.reverse(pad, axis=[0]) + pad = paddle.flatten(pad) + pad = paddle.cast(pad, dtype="int32") + out = paddle.nn.functional.pad(x=x, + pad=pad, + value=value, + mode=mode, + data_format=data_format) + return out \ No newline at end of file diff --git a/x2paddle/op_mapper/static/onnx2paddle/opset9/opset.py b/x2paddle/op_mapper/static/onnx2paddle/opset9/opset.py index 3b436f07bba8aba053ada18f57ad2ae72efcd31a..1c89882bff2c02c480a6454d9bda79d1c09838fd 100644 --- a/x2paddle/op_mapper/static/onnx2paddle/opset9/opset.py +++ b/x2paddle/op_mapper/static/onnx2paddle/opset9/opset.py @@ -106,6 +106,9 @@ class OpSet9(): 'ReduceMax': ['paddle.max', dict(axes='axis', keepdims='keepdim'), dict(keepdim=1)], + 'ReduceProd': ['paddle.prod', + dict(axes='axis', keepdims='keepdim'), + dict(keepdim=1)], # active function 'Relu': ['paddle.nn.functional.relu'], 'LeakyRelu': ['paddle.nn.functional.leaky_relu', @@ -203,7 +206,7 @@ class OpSet9(): node = parameter dtype = node.dtype shape = node.out_shapes[0] - if len(node.weight.shape) == 0: + if hasattr(node.weight, "shape") and len(node.weight.shape) == 0: self.paddle_graph.add_layer( "paddle.full", inputs={}, @@ -286,6 +289,10 @@ class OpSet9(): attrs.update({"align_corners": False, "mode": string(mode), "align_mode": 1}) + val_x_shape = val_x.out_shapes[0] + if mode == "linear" and len(val_x_shape) == 4: + attrs["mode"] = string("bilinear") + attrs["align_corners"] = True self.paddle_graph.add_layer( kernel="paddle.nn.functional.interpolate", inputs=inputs, @@ -368,61 +375,136 @@ class OpSet9(): def Pad(self, node, op_independent=True): val_x = self.graph.get_input_node(node, idx=0, copy=True) pads = node.get_attr('pads') + is_pads_attr = True + if pads is None: + val_pad = self.graph.get_input_node(node, idx=1, copy=True) + pad_shape = val_pad.out_shapes[0] + is_pads_attr = False + pads = _const_weight_or_none(val_pad) + if pads is not None: + is_pads_attr = True mode = node.get_attr('mode', 'constant') value = node.get_attr('value', 0.) data_shape = val_x.out_shapes[0] output_shape = node.out_shapes[0] - assume_pad2d = False + assume_pad = False layer_attrs = {} layer_attrs['mode'] = string(mode) - paddings = [] - if len(pads) == 4: - assume_pad2d |= mode != 'constant' - if data_shape: - assume_pad2d |= data_shape and len(data_shape) == 4 # NCHW - if output_shape: - assume_pad2d |= output_shape and len(output_shape) == 4 # NCHW - if assume_pad2d: - paddle_op = 'paddle.nn.functional.pad' - layer_attrs['data_format'] = string('NCHW') - layer_attrs['value'] = value + layer_attrs['value'] = value + if not op_independent: + output_name = node.name + '_paded' else: - paddle_op = 'paddle.fluid.layers.pad' - layer_attrs["pad_value"] = value - if len(pads) == 4: - paddings = np.array(pads).reshape( - (-1, 2)).transpose().flatten().tolist() # SSEE -> SESE - elif len(pads) == 8: - paddings = np.array(pads).reshape( - (-1, 4)).transpose().flatten().tolist() # SSEE -> SESE - if sum(paddings[:4]) == 0: - paddle_op = 'paddle.nn.functional.pad' - paddings = paddings[4:] - layer_attrs['value'] = value - if 'pad_value' in layer_attrs: - layer_attrs.pop('pad_value') - tmp_paddings = copy.deepcopy(paddings) - paddings[0] = tmp_paddings[2] - paddings[1] = tmp_paddings[3] - paddings[2] = tmp_paddings[0] - paddings[3] = tmp_paddings[1] - if paddle_op == 'paddle.nn.functional.pad': - layer_attrs['pad'] = paddings - else: - layer_attrs['paddings'] = paddings - if op_independent: + output_name = node.name + layer_outputs = [output_name] + if is_pads_attr: + paddings = [] + paddle_op = 'paddle.nn.functional.pad' + if len(pads) in [2, 4, 6]: + if data_shape: + assume_pad |= data_shape and 2 * (len(data_shape) - 2) == len(pads) # NCHW + if output_shape: + assume_pad |= output_shape and 2 * (len(output_shape) - 2) == len(pads) # NCHW + if assume_pad: + if len(pads) == 2: + data_format = "NCL" + elif len(pads) == 4: + data_format = "NCHW" + else: + data_format = "NCDHW" + + paddings = np.array(pads).reshape( + (2, -1)).transpose().astype("int32") + paddings = np.flip(paddings, axis=0).flatten().tolist() + layer_attrs['pad'] = paddings + layer_attrs['data_format'] = data_format + else: + if data_shape: + assume_pad |= data_shape and 2 * len(data_shape) == len(pads) # NCHW + if output_shape: + assume_pad |= output_shape and 2 * len(output_shape) == len(pads) # NCHW + if assume_pad: + paddings = np.array(pads).reshape( + (2, -1)).transpose().astype("int32").flatten().tolist() + layer_attrs['pad'] = paddings + else: + raise Exception("The padding value {} is wrong!".format(pads)) + elif len(pads) == 8: + if data_shape: + assume_pad |= data_shape and 2 * len(data_shape) == len(pads) # NCHW + if output_shape: + assume_pad |= output_shape and 2 * len(output_shape) == len(pads) # NCHW + if assume_pad: + paddings = np.array(pads).reshape( + (2, -1)).transpose().astype("int32") + paddings = np.flip(paddings, axis=0).flatten().tolist() + if sum(paddings[:4]) == 0: + paddings = paddings[4:] + layer_attrs['pad'] = paddings + else: + layer_attrs['pad'] = paddings + paddle_op = "custom_layer:pad_all_dim4_one_input" + else: + raise Exception("The padding value {} is wrong!".format(pads)) self.paddle_graph.add_layer( paddle_op, inputs={'x': val_x.name}, - outputs=[node.name], + outputs=layer_outputs, **layer_attrs) + if not op_independent: + return node.name + '_paded' else: - self.paddle_graph.add_layer( - paddle_op, - inputs={'x': val_x.name}, - outputs=[node.name + '_paded'], - **layer_attrs) - return node.name + '_paded' + pads_len = val_pad.out_shapes[0][0] + if pads_len in [2, 4, 6]: + if data_shape: + assume_pad |= data_shape and 2 * (len(data_shape) - 2) == pads_len # NCHW + if output_shape: + assume_pad |= output_shape and 2 * (len(output_shape) - 2) == pads_len # NCHW + if assume_pad: + if pads_len == 2: + data_format = "NCL" + elif pads_len == 4: + data_format = "NCHW" + else: + data_format = "NCDHW" + self.paddle_graph.add_layer( + "custom_layer:pad_with_two_input", + inputs={'x': val_x.name, 'pad': val_pad.name}, + outputs=layer_outputs, + value=value, + mode=string(mode), + data_format=string(data_format)) + else: + if data_shape: + assume_pad |= data_shape and 2 * len(data_shape) == pads_len # NCHW + if output_shape: + assume_pad |= output_shape and 2 * len(output_shape) == pads_len # NCHW + if assume_pad: + if pads_len == 4: + self.paddle_graph.add_layer( + "custom_layer:pad_all_dim2", + inputs={'x': val_x.name, 'pad': val_pad.name}, + outputs=layer_outputs, + value=value, + mode=string(mode)) + else: + raise Exception("The padding value is wrong!") + elif pads_len == 8: + if data_shape: + assume_pad |= data_shape and 2 * len(data_shape) == pads_len # NCHW + if output_shape: + assume_pad |= output_shape and 2 * len(output_shape) == pads_len # NCHW + if assume_pad: + self.paddle_graph.add_layer( + "custom_layer:pad_all_dim4", + inputs={'x': val_x.name, 'pad': val_pad.name}, + outputs=layer_outputs, + value=value, + mode=string(mode)) + else: + print(pads_len) + raise Exception("The padding value is wrong!") + if not op_independent: + return node.name + '_paded' @print_mapping_info def Unsqueeze(self, node): @@ -622,17 +704,13 @@ class OpSet9(): self.paddle_graph.add_layer( 'paddle.cast', inputs={"x": indices.name}, - outputs=indices_cast, + outputs=[indices_cast], dtype=string('int64')) - op_name = name_generator("embedding", self.nn_name2id) - output_name = node.name - layer_outputs = [op_name, output_name] self.paddle_graph.add_layer( - 'paddle.nn.Embedding', - inputs={"x": indices_cast}, - outputs=layer_outputs, - param_attr=string(val_x.name), - size=val_x.out_shapes[0]) + 'paddle.nn.functional.embedding', + inputs={"x": indices_cast, + "weight": val_x.name}, + outputs=[node.name]) else: from functools import reduce reshape_shape = reduce(lambda x, y: x * y, indices_shape) @@ -804,20 +882,27 @@ class OpSet9(): starts = self.graph.get_input_node(node, idx=1, copy=True) ends = self.graph.get_input_node(node, idx=2, copy=True) starts_value = _const_weight_or_none(starts) + if starts_value is not None: + starts_value = starts_value.tolist() ends_value = _const_weight_or_none(ends) - + if ends_value is not None: + ends_value = ends_value.tolist() + if len(node.inputs) > 2: + s_len = len(val_x.out_shapes[0]) + axes = list(range(s_len)) if len(node.inputs) > 3: - axes = self.graph.get_input_node(node, idx=3, copy=True) - axes = _const_weight_or_none(axes, necessary=True) + axes_node = self.graph.get_input_node(node, idx=3, copy=True) + axes = _const_weight_or_none(axes_node, necessary=True).tolist() if len(node.inputs) > 4: steps = self.graph.get_input_node(node, idx=4, copy=True) - steps = _const_weight_or_none(steps) + steps = _const_weight_or_none(steps).tolist() + layer_attrs = { "axes": axes, "starts": starts.name, "ends": ends.name } - if starts_value is not None and ends_value is not None: + 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)): @@ -847,6 +932,8 @@ class OpSet9(): layer_attrs['starts'] = starts_cast if ends.dtype != 'int32': ends_cast = ends.name + '_cast' + else: + ends_cast = ends.name self.paddle_graph.add_layer( 'paddle.cast', inputs={"x": ends.name}, @@ -862,6 +949,7 @@ class OpSet9(): ends[idx] = 2**31 - 1 layer_attrs = {"axes": axes, "starts": starts, "ends": ends} + if steps is not None: layer_attrs['strides'] = steps self.paddle_graph.add_layer( @@ -986,11 +1074,17 @@ class OpSet9(): inputs={'x': val_shape.name}, outputs=[val_shape.name], shape=val_shape.out_shapes[0]) + if val_shape.dtype != "int32": + self.paddle_graph.add_layer( + 'paddle.cast', + inputs={'x': val_shape.name}, + outputs=[val_shape.name], + dtype=string("int32")) self.paddle_graph.add_layer( 'paddle.reshape', inputs={'x': val_x.name, 'shape': val_shape.name}, - outputs=node) + outputs=[node.name]) @print_mapping_info def Cast(self, node): @@ -1221,7 +1315,10 @@ class OpSet9(): @print_mapping_info def Transpose(self, node): val_x = self.graph.get_input_node(node, idx=0, copy=True) - perm = node.get_attr('perm') + s_len = len(val_x.out_shapes[0]) + perm_default = list(range(s_len)) + perm_default.reverse() + perm = node.get_attr('perm', perm_default) self.paddle_graph.add_layer( "paddle.transpose", inputs={"x": val_x.name}, @@ -1230,9 +1327,6 @@ class OpSet9(): @print_mapping_info def PRelu(self, node): - op_name = name_generator("prelu", 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_slope = self.graph.get_input_node(node, idx=1, copy=True) @@ -1240,20 +1334,27 @@ class OpSet9(): shape_slope = val_slope.out_shapes[0] if shape_slope == [1]: mode = 'all' - elif len(shape_slope) > 2: - raise Exception("The 'element' mode is not supported yet!") - - if mode == 'channel' and len(shape_slope) == 1: - # paddle params shape need be [1, channel] - slope_data = _const_weight_or_none(val_slope) - slope_data = np.reshape(slope_data, [1] + shape_slope) - self.params[val_slope.name] = slope_data - - self.paddle_graph.add_layer( - "paddle.nn.functional.prelu", - inputs={"x": val_x.name, - "weight": val_slope.name}, - outputs=[node.name]) + + if mode == "element": + self.paddle_graph.add_layer( + "paddle.static.nn.prelu", + inputs={"x": val_x.name, + "param_attr": val_slope.name}, + outputs=[node.name], + mode="element") + else: + if mode == 'channel': + if len(shape_slope) > 1: + self.paddle_graph.add_layer( + "paddle.reshape", + inputs={"x": val_slope.name}, + outputs=[val_slope.name], + shape=[shape_slope[0]]) + self.paddle_graph.add_layer( + "paddle.nn.functional.prelu", + inputs={"x": val_x.name, + "weight": val_slope.name}, + outputs=[node.name]) @print_mapping_info def Squeeze(self, node): @@ -1521,6 +1622,16 @@ class OpSet9(): } if has_bias: layer_inputs["bias"] = val_b.name + input_shape = val_x.out_shapes[0] + if reduce(lambda x,y:x*y, input_shape) in [1, -1] and 1 not in input_shape: + input_shape[1] = num_in_channels * num_groups + input_shape[0] = 0 + input_shape[2] = 0 + self.paddle_graph.add_layer( + "paddle.reshape", + inputs={"x": layer_inputs["x"]}, + outputs=[layer_inputs["x"]], + shape=input_shape) self.paddle_graph.add_layer( paddle_op, inputs=layer_inputs, @@ -1587,4 +1698,63 @@ class OpSet9(): 'paddle.argmax', inputs={"x": val_x.name}, outputs=[node.name], - **layer_attrs) \ No newline at end of file + **layer_attrs) + + @print_mapping_info + def Size(self, node): + val_x = self.graph.get_input_node(node, idx=0, copy=True) + self.paddle_graph.add_layer( + "paddle.shape", + inputs={"input": val_x.name}, + outputs=[node.name]) + self.paddle_graph.add_layer( + 'paddle.cast', + inputs={"x": node.name}, + outputs=[node.name], + dtype=string('int64')) + self.paddle_graph.add_layer( + "paddle.prod", + inputs={"x": node.name}, + outputs=[node.name]) + + @print_mapping_info + def Sign(self, node): + val_x = self.graph.get_input_node(node, idx=0, copy=True) + if node.dtype not in ["float16", "float32", "float64"]: + self.paddle_graph.add_layer( + "paddle.cast", + inputs={"x": val_x.name}, + outputs=[val_x.name], + dtype=string("float32")) + self.paddle_graph.add_layer( + "paddle.sign", + inputs={"x": val_x.name}, + outputs=[node.name]) + if node.dtype not in ["float16", "float32", "float64"]: + self.paddle_graph.add_layer( + "paddle.cast", + inputs={"x": node.name}, + outputs=[node.name], + dtype=string(node.dtype)) + + @print_mapping_info + def OneHot(self, node): + indices = self.graph.get_input_node(node, idx=0, copy=True) + depth = self.graph.get_input_node(node, idx=1, copy=True) + values = self.graph.get_input_node(node, idx=2, copy=True) + axis = node.get_attr('axis', -1) + self.paddle_graph.add_layer( + "custom_layer:one_hot", + inputs={"indices": indices.name, + "depth": depth.name, + "values": values.name}, + outputs=[node.name], + axis=axis) + + @print_mapping_info + def Reciprocal(self, node): + val_x = self.graph.get_input_node(node, idx=0, copy=True) + self.paddle_graph.add_layer( + "paddle.reciprocal", + inputs={"x": val_x.name}, + outputs=[node.name]) \ No newline at end of file diff --git a/x2paddle/optimizer/pytorch_code_optimizer/hierachical_tree.py b/x2paddle/optimizer/pytorch_code_optimizer/hierachical_tree.py index ee36d2d3748d3915c09b0e3683f38675ad2119c4..6e7e4afed51cf04334df880c2b9d0adcefb2e7c2 100644 --- a/x2paddle/optimizer/pytorch_code_optimizer/hierachical_tree.py +++ b/x2paddle/optimizer/pytorch_code_optimizer/hierachical_tree.py @@ -300,6 +300,7 @@ class HierarchicalTree(Tree): """ depths = sorted(list(self._hierarchical_order.keys()), reverse=True) all_name_old2new = dict() + current_module_name_list = list() for depth in depths[1:]: # Module的名字与子图的对应关系 module_name2sub_layers = dict() @@ -352,6 +353,9 @@ class HierarchicalTree(Tree): module_name = None else: module_name = name + while module_name in current_module_name_list: + module_name += "__0" + current_module_name_list.append(module_name) self.merge_node(module_name2sub_layers[name], sequentials2attrs_table[name], node_name2sub_layers,