diff --git a/x2paddle/convert.py b/x2paddle/convert.py index c43236c6732a8676b8babae3af727d033f71f46a..0f51a2f79664070b8f490124c2ba8acaba5e5529 100644 --- a/x2paddle/convert.py +++ b/x2paddle/convert.py @@ -141,11 +141,14 @@ def tf2paddle(model_path, from x2paddle.optimizer.tensorflow.bias import BiasOpt from x2paddle.optimizer.tensorflow.transpose import TransposeOpt from x2paddle.optimizer.tensorflow.batch_norm import BatchNormOpt + from x2paddle.optimizer.tensorflow.prelu import PReLUOpt bias_opt = BiasOpt() transpose_opt = TransposeOpt() batch_norm_opt = BatchNormOpt() + prelu_opt = PReLUOpt() bias_opt.run(mapper.paddle_graph) batch_norm_opt.run(mapper.paddle_graph) + prelu_opt.run(mapper.paddle_graph) transpose_opt.run(mapper.paddle_graph) mapper.paddle_graph.gen_model(save_dir) diff --git a/x2paddle/op_mapper/dygraph/tf2paddle/tf_op_mapper.py b/x2paddle/op_mapper/dygraph/tf2paddle/tf_op_mapper.py index a1f56f26c3c871bff3908a2baffb522b6b39d742..ad4206e13faf522fe53f388e2e4850c18811cce9 100644 --- a/x2paddle/op_mapper/dygraph/tf2paddle/tf_op_mapper.py +++ b/x2paddle/op_mapper/dygraph/tf2paddle/tf_op_mapper.py @@ -508,14 +508,6 @@ class TFOpMapper(OpMapper): param = self.graph.get_node(node.layer.input[1]) input_name = input.name - if input.dtype == 'bool': - cast_name = gen_name('reshape', 'cast') - self.paddle_graph.add_layer( - kernel="paddle.cast", - inputs={"x": input_name}, - outputs=[cast_name], - dtype="'int32'") - input_name = cast_name if param.layer_type == "Const": shape = param.value.tolist() @@ -540,13 +532,6 @@ class TFOpMapper(OpMapper): outputs=[node.name], shape=out_shape.tolist()) - if input.dtype == 'bool': - self.paddle_graph.add_layer( - kernel="paddle.cast", - inputs={"x": node.name}, - outputs=[node.name], - dtype="'bool'") - def Pad(self, node): input = self.graph.get_node(node.layer.input[0]) paddings = self.graph.get_node(node.layer.input[1]) @@ -592,14 +577,6 @@ class TFOpMapper(OpMapper): def Shape(self, node): input = self.graph.get_node(node.layer.input[0]) input_name = input.name - if input.dtype == 'bool': - cast_name = gen_name('shape', 'cast') - self.paddle_graph.add_layer( - kernel="paddle.cast", - inputs={"x": input.name}, - outputs=[cast_name], - dtype=string("int32")) - input_name = cast_name self.paddle_graph.add_layer( kernel="paddle.shape", inputs={"input": input_name}, @@ -791,26 +768,11 @@ class TFOpMapper(OpMapper): axis += len(inputs[0].out_shapes[0]) input_names = [i.name for i in inputs] - for i, ipt in enumerate(inputs): - if ipt.dtype == 'bool': - cast_name = gen_name('concat', 'cast') - self.paddle_graph.add_layer( - kernel="paddle.cast", - inputs={"x": ipt.name}, - outputs=[cast_name], - dtype="'int32'") - input_names[i] = cast_name self.paddle_graph.add_layer( kernel="paddle.concat", inputs={"x": input_names}, outputs=[node.name], axis=axis) - if node.dtype == 'bool': - self.paddle_graph.add_layer( - kernel="paddle.cast", - inputs={"x": node.name}, - outputs=[node.name], - dtype="'bool'") def StridedSlice(self, node): input = self.graph.get_node(node.layer.input[0]) diff --git a/x2paddle/optimizer/fusion/dygraph/__init__.py b/x2paddle/optimizer/fusion/dygraph/__init__.py index 6d35844a5d47de24679eb38e4bf35fcc9e26f305..b74f9d29e1879609826c689766731e99582adcde 100644 --- a/x2paddle/optimizer/fusion/dygraph/__init__.py +++ b/x2paddle/optimizer/fusion/dygraph/__init__.py @@ -28,6 +28,8 @@ from .fc_fuser import DygraphFcFuser from .fc_fuse_pass import DygraphFcFusePass from .interpolate_bilinear_fuser import DygraphInterpolateBilinearFuser from .interpolate_bilinear_fuse_pass import DygraphInterpolateBilinearFusePass +from .prelu_fuser import DygraphPReLUFuser +from .prelu_fuse_pass import DygraphPReLUFusePass from .reshape_fuser import DygraphReshapeFuser from .reshape_fuse_pass import DygraphReshapeFusePass from .tf_batchnorm_fuser import DygraphTFBatchNormFuser diff --git a/x2paddle/optimizer/fusion/dygraph/prelu_fuse_pass.py b/x2paddle/optimizer/fusion/dygraph/prelu_fuse_pass.py new file mode 100644 index 0000000000000000000000000000000000000000..4366ff179a73822951e4ac3e36e0048a07b6f96f --- /dev/null +++ b/x2paddle/optimizer/fusion/dygraph/prelu_fuse_pass.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. + +from x2paddle.optimizer.pass_ import Pass +from x2paddle.optimizer.fusion.dygraph import DygraphPReLUFuser +from x2paddle.optimizer.pass_manager import pass_register + + +@pass_register +class DygraphPReLUFusePass(Pass): + name = "dygraph_prelu_fuse_pass" + + def __init__(self): + Pass.__init__(self) + + def apply(self, graph): + fuser = DygraphPReLUFuser() + fuser.operate(graph, match_kind="edge") + + +# 用于注册 +dygraph_prelu_fuse_pass = DygraphPReLUFusePass() \ No newline at end of file diff --git a/x2paddle/optimizer/fusion/dygraph/prelu_fuser.py b/x2paddle/optimizer/fusion/dygraph/prelu_fuser.py new file mode 100644 index 0000000000000000000000000000000000000000..ddab5605b9cf6deffa3e247df5430a95fc0474bc --- /dev/null +++ b/x2paddle/optimizer/fusion/dygraph/prelu_fuser.py @@ -0,0 +1,142 @@ +# 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 copy +import numpy as np +from collections import OrderedDict +from x2paddle.optimizer.pattern_matcher import FuseBase +from x2paddle.core.program import PaddleGraph, PaddleLayer +from x2paddle.core.util import * + + +class DygraphPReLUFuser(FuseBase): + def __init__(self): + self.prelu_index = 0 + super(DygraphPReLUFuser, self).__init__(graph_type="dygraph") + + def build_pattern(self): + """ 描述需要替换的prelu图结构。 + prelu层模式python实现代码示例: + conv2_alphas = self.conv2_alphas + conv2_mul_1_y = paddle.full(dtype='float32', shape=[1], fill_value=0.5) + conv2_Relu = self.relu1(conv2_Conv2D) + conv2_Abs = paddle.abs(x=conv2_Conv2D) + conv2_sub = fluid.layers.elementwise_sub(x=conv2_Conv2D, y=conv2_Abs) + conv2_mul = paddle.multiply(x=conv2_alphas, y=conv2_sub, axis=1) + conv2_mul_1 = paddle.multiply(x=conv2_mul, y=conv2_mul_1_y, axis=1) + conv2_add = paddle.add(x=conv2_Relu, y=conv2_mul_1) + """ + + def gen_name(id): + return "x" + str(id) + + self.pattern.add_layer( + "self.create_parameter", + inputs={}, + outputs=[gen_name(0)]) + self.pattern.add_layer( + "paddle.full", + inputs={}, + outputs=[gen_name(1)], + shape=[1], + fill_value=0.5) + self.pattern.add_layer( + "paddle.nn.ReLU", + inputs={"x": "prelu-input-0"}, + outputs=[gen_name(2)]) + self.pattern.add_layer( + "paddle.abs", + inputs={"x": "prelu-input-0"}, + outputs=[gen_name(3)]) + self.pattern.add_layer( + "fluid.layers.elementwise_sub", + inputs={"x": "prelu-input-0", + "y": gen_name(3)}, + outputs=[gen_name(4)]) + self.pattern.add_layer( + "paddle.multiply", + inputs={"x": gen_name(0), + "y": gen_name(4)}, + outputs=[gen_name(5)]) + self.pattern.add_layer( + "paddle.multiply", + inputs={"x": gen_name(5), + "y": gen_name(1)}, + outputs=[gen_name(6)]) + self.pattern.add_layer( + "paddle.add", + inputs={"x": gen_name(2), + "y": gen_name(6)}, + outputs=[gen_name(7)]) + self.pattern.build(inputs={"input-0": "prelu-input-0", }) + + + def insert_new_layer(self, graph, parameters, matches): + new_layers, last_layer_id = self.gen_new_layer(matches, parameters, graph) + matches_copy = copy.deepcopy(matches) + for layer_id, layer in matches_copy.items(): + for i in range(3): + if layer_id == new_layers[i].id: + matches.pop(new_layers[i].id) + prefix_layers = OrderedDict() + mid_layers = OrderedDict() + suffix_layers = OrderedDict() + is_need_id = False + for layer_id, layer in graph.layers.items(): + if is_need_id: + suffix_layers[layer_id] = layer + else: + if layer_id == last_layer_id: + for i in range(3): + mid_layers[new_layers[i].id] = new_layers[i] + is_need_id = True + prefix_layers[layer_id] = layer + prefix_layers.update(mid_layers) + prefix_layers.update(suffix_layers) + graph.layers = prefix_layers + + def gen_new_layer(self, matches, parameters, graph): + layer_id_list = list(matches.keys()) + layer_id_list.sort(key = int) + for layer_id, layer in matches.items(): + if layer.kernel == "paddle.nn.ReLU": + input_name = layer.inputs["x"] + if layer.kernel == "self.create_parameter": + param_name = layer.outputs[0] + if layer.kernel == "paddle.add": + output_name = layer.outputs[0] + transpose0 = PaddleLayer( + id=layer_id_list[-1] + "_1", + kernel="paddle.transpose", + inputs={"x": input_name}, + outputs=["{}_transpose_for_prelu".format(input_name)], + perm=[0, 3, 1, 2]) + prelu_name = "merge_prelu{}".format(self.prelu_index) + self.prelu_index += 1 + param = parameters[param_name] + c = param.shape[0] + prelu = PaddleLayer(id=layer_id_list[-1] + "_2", + kernel="paddle.nn.PReLU", + inputs={"input": "{}_transpose_for_prelu".format(input_name)}, + outputs=[prelu_name, "{}_prelu".format(input_name)], + num_parameters=c, + weight_attr=string(param_name)) + transpose1 = PaddleLayer( + id=layer_id_list[-1] + "_3", + kernel="paddle.transpose", + inputs={"x": "{}_prelu".format(input_name)}, + outputs=[output_name], + perm=[0, 2, 3, 1]) + return [transpose0, prelu, transpose1], layer_id_list[-1] + diff --git a/x2paddle/optimizer/optimizer.py b/x2paddle/optimizer/optimizer.py index 663d0e26daf451aae5e5f5b74c1e2d8c2fe8a29f..927ad0523f67b64c29cbdc2195bfc79735cbf0fb 100644 --- a/x2paddle/optimizer/optimizer.py +++ b/x2paddle/optimizer/optimizer.py @@ -41,6 +41,7 @@ class GraphOptimizer(object): self.passes = [ "dygraph_conv2d_add_fuse_pass", "dygraph_tf_batchnorm_fuse_pass", + "dygraph_prelu_fuse_pass", "transpose_eliminate_pass" ] else: diff --git a/x2paddle/optimizer/tensorflow/batch_norm.py b/x2paddle/optimizer/tensorflow/batch_norm.py index 3e3d81360598c2b66a00735cd23059998f3bef2d..f2d1db83bf64eb1c886ad1a4def3a7e2861b1be4 100644 --- a/x2paddle/optimizer/tensorflow/batch_norm.py +++ b/x2paddle/optimizer/tensorflow/batch_norm.py @@ -20,10 +20,12 @@ class BatchNormOpt: input_ids0 = graph.edges_in[layer_id] mul_layer0 = graph.layers[input_ids0[0]] sub_layer0 = graph.layers[input_ids0[1]] + if mul_layer0.kernel != "fluid.layers.elementwise_mul": continue if sub_layer0.kernel != "fluid.layers.elementwise_sub": continue + axis = mul_layer0.attrs.get('axis', -1) if axis != -1 and axis != 3: continue @@ -116,7 +118,7 @@ class BatchNormOpt: other = graph.layers[input_ids6[1]] if variance.kernel != "fluid.layers.create_parameter": continue - if other.kernel != "fluid.layers.create_parameter": + if other.kernel != "fluid.layers.fill_constant": continue if len(graph.edges_out.get(input_ids6[0], [])) != 1: continue @@ -127,10 +129,6 @@ class BatchNormOpt: variance_shape = graph.parameters[variance.outputs[0]].shape if variance_shape != beta_shape: continue - if other.outputs[0] not in graph.parameters: - continue - if graph.parameters[other.outputs[0]].size != 1: - continue ids = set([ layer_id, mul_layer0.id, sub_layer0.id, mul_layer1.id, beta.id, @@ -163,7 +161,7 @@ class BatchNormOpt: kernel="fluid.layers.batch_norm", inputs={"input": "transpose_for_bn"}, outputs=layer.outputs, - epsilon=graph.parameters[other.outputs[0]], + epsilon=other.attrs["value"], param_attr="'{}'".format(gamma.outputs[0]), bias_attr="'{}'".format(beta.outputs[0]), moving_mean_name="'{}'".format(mean.outputs[0]), diff --git a/x2paddle/optimizer/tensorflow/prelu.py b/x2paddle/optimizer/tensorflow/prelu.py new file mode 100644 index 0000000000000000000000000000000000000000..6eba183ec09d11228103a78b374e1820574b7b73 --- /dev/null +++ b/x2paddle/optimizer/tensorflow/prelu.py @@ -0,0 +1,122 @@ +import copy +import numpy as np +from collections import OrderedDict +from x2paddle.core.program import PaddleLayer +from x2paddle.core.util import * + + +class PReLUOpt: + def __init__(self): + pass + + def run(self, graph): + print("Optimize: PReLUOpt...") + layers = copy.deepcopy(graph.layers) + for layer_id, layer in layers.items(): + if layer.kernel != "fluid.layers.elementwise_add": + continue + axis = layer.attrs.get('axis', -1) + if axis != -1 and axis != 3: + continue + + input_ids0 = graph.edges_in[layer_id] + relu_layer0 = graph.layers[input_ids0[0]] + mul_layer0 = graph.layers[input_ids0[1]] + + if relu_layer0.kernel != "fluid.layers.relu": + continue + if mul_layer0.kernel != "fluid.layers.elementwise_mul": + continue + + axis = mul_layer0.attrs.get('axis', -1) + if axis != -1 and axis != 3: + continue + if len(graph.edges_out.get(input_ids0[0], [])) != 1: + continue + if len(graph.edges_out.get(input_ids0[1], [])) != 1: + continue + + input_ids1_0 = graph.edges_in[input_ids0[0]] + input_ids1_1 = graph.edges_in[input_ids0[1]] + fill_layer = graph.layers[input_ids1_1[1]] + mul_layer1 = graph.layers[input_ids1_1[0]] + if fill_layer.kernel != "fluid.layers.fill_constant": + continue + if mul_layer1.kernel != "fluid.layers.elementwise_mul": + continue + axis = mul_layer1.attrs.get('axis', -1) + if axis != -1 and axis != 0: + continue + if len(graph.edges_out.get(input_ids1_1[1], [])) != 1: + continue + if len(graph.edges_out.get(input_ids1_0[0], [])) != 3: + continue + + input_ids2 = graph.edges_in[input_ids1_1[0]] + alpha = graph.layers[input_ids2[0]] + sub_layer = graph.layers[input_ids2[1]] + if alpha.kernel != "fluid.layers.create_parameter": + continue + if sub_layer.kernel != "fluid.layers.elementwise_sub": + continue + axis = sub_layer.attrs.get('axis', -1) + if axis != -1 and axis != 3: + continue + if len(graph.edges_out.get(input_ids2[0], [])) != 1: + continue + if len(graph.edges_out.get(input_ids2[1], [])) != 1: + continue + if alpha.outputs[0] not in graph.parameters: + continue + + input_ids3 = graph.edges_in[input_ids2[1]] + add_layer = graph.layers[input_ids3[0]] + abs_layer = graph.layers[input_ids3[1]] + if abs_layer.kernel != "fluid.layers.abs": + continue + if len(graph.edges_out.get(input_ids3[1], [])) != 1: + continue + + + ids = set([ + layer.id, relu_layer0.id, mul_layer0.id, fill_layer.id, mul_layer1.id, alpha.id, + sub_layer.id, abs_layer.id]) + + for id in ids: + del graph.layers[id] + if id in graph.edges_in: + del graph.edges_in[id] + if id in graph.edges_out: + del graph.edges_out[id] + + copy_layers = copy.deepcopy(graph.layers) + graph.layers = OrderedDict() + for k, v in copy_layers.items(): + if k != add_layer.id: + graph.layers[k] = v + continue + graph.layers[k] = v + transpose0 = PaddleLayer( + id='{}_1'.format(k), + kernel="fluid.layers.transpose", + inputs={"x": v.outputs[0]}, + outputs=["transpose_for_prelu"], + perm=[0, 3, 1, 2]) + prelu = PaddleLayer( + id='{}_2'.format(k), + kernel="fluid.layers.prelu", + inputs={"x": "transpose_for_prelu"}, + outputs=layer.outputs, + mode=string("channel"), + param_attr="'{}'".format(alpha.outputs[0])) + transpose1 = PaddleLayer( + id=layer_id, + kernel="fluid.layers.transpose", + inputs={"x": layer.outputs[0]}, + outputs=layer.outputs, + perm=[0, 2, 3, 1]) + graph.layers[transpose0.id] = transpose0 + graph.layers[prelu.id] = prelu + graph.layers[transpose1.id] = transpose1 + graph.parameters[alpha.outputs[0]] = np.expand_dims(graph.parameters[alpha.outputs[0]], axis=(0, 2, 3)) + graph.build() \ No newline at end of file