diff --git a/README.md b/README.md index e53be51f97b74d357126ffaca4e62c48449f776e..2a77b9bbee199708451f704ff072ef67ef18e85d 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ X2Paddle支持将其余深度学习框架训练得到的模型,转换至Paddle X2Paddle is a toolkit for converting trained model to PaddlePaddle from other deep learning frameworks. ## 转换模型库 -X2Paddle在多个主流的CV模型上,测试过TensorFlow/Caffe/ONNX模型的转换,可以在[X2Paddle-Model-Zoo](x2paddle_model_zoo.md)查看我们的模型测试列表。如果你在新的模型上进行了测试转换,也欢迎继续补充该列表;如若无法转换,可通过ISSUE反馈给我们,我们会尽快跟进。 +X2Paddle在多个主流的CV模型上,测试过TensorFlow/Caffe/ONNX模型的转换,可以在[X2Paddle-Model-Zoo](x2paddle_model_zoo.md)查看我们的模型测试列表,可以在[OP-LIST](op_list.md)中查看目前X2Paddle支持的OP列表。如果你在新的模型上进行了测试转换,也欢迎继续补充该列表;如若无法转换,可通过ISSUE反馈给我们,我们会尽快跟进。 ## 环境依赖 @@ -29,7 +29,7 @@ python setup.py install ### 安装方式二 我们会定期更新pip源上的x2paddle版本 ``` -pip install x2paddle +pip install x2paddle --index https://pypi.Python.org/simple/ ``` ## 使用方法 ### TensorFlow @@ -38,7 +38,7 @@ x2paddle --framework=tensorflow --model=tf_model.pb --save_dir=pd_model ``` ### Caffe ``` -x2paddle --framework=caffe --prototxt=deploy.proto --weight=deploy.caffemodel --save_dir=pd_model +x2paddle --framework=caffe --prototxt=deploy.prototxt --weight=deploy.caffemodel --save_dir=pd_model ``` ### ONNX ``` diff --git a/op_list.md b/op_list.md new file mode 100644 index 0000000000000000000000000000000000000000..7f12eb48923e64dd6205e98c30d1672492fa2000 --- /dev/null +++ b/op_list.md @@ -0,0 +1,52 @@ +# X2Paddle支持OP列表 +> 目前X2Paddle支持40+的TensorFlow OP,30+的Caffe Layer,覆盖了大部分CV分类模型常用的操作。我们在如下列表中给出了目前X2Paddle支持的全部OP。 + +**注:** 目前,部分OP暂未支持,如您在转换过程中出现OP不支持的情况,可自行添加或反馈给我们。欢迎通过[ISSUE反馈](https://github.com/PaddlePaddle/X2Paddle/issues/new)的方式告知我们(模型名,代码实现或模型获取方式),我们会及时跟进:) + +## TensorFlow + +| 序号 | OP | 序号 | OP | 序号 | OP | 序号 | OP | +|------|------|------|------|------|------|------|------| +| 1 | Relu | 2 | Relu6 | 3 | Shape | 4 | Abs | +| 5 | Sigmoid | 6 | Exp | 7 | Rsqrt | 8 | swish_f32 | +| 9 | Tanh | 10 | LeakyRelu | 11 | Add | 12 | RealDiv | +| 13 | Sub | 14 | Maximum | 15 | Mul | 16 | FloorDiv | +| 17 | Placeholder | 18 | Const | 19 | Transpose | 20 | FusedBatchNorm | +| 21 | Conv2D | 22 | BiasAdd | 23 | MaxPool | 24 | DepthwiseConv2dNative | +| 25 | Reshape | 26 | AvgPool | 27 | SplitV | 28 | SquaredDifference | +| 29 | Tile | 30 | Pack | 31 | Pad | 32 | ResizeBilinear | +| 33 | Mean | 34 | MatMul | 35 | ArgMax | 36 | StridedSlice | +| 37 | Slice | 38 | Sum | 39 | Max | 40 | Conv2DBackpropInput | +| 41 | Cast | 42 | Split | 43 | Squeeze | 44 | ResizeNearestNeighbor | +| 45 | Softmax | 46 | Range | 47 | ConcatV2 | + +## Caffe + +| 序号 | OP | 序号 | OP | 序号 | OP | 序号 | OP | +|------|------|------|------|------|------|------|------| +| 1 | Input | 2 | Convolution | 3 | Deconvolution | 4 | Pooling | +| 5 | LRN | 6 | InnerProduct | 7 | Softmax | 8 | Slice | +| 9 | Concat | 10 | PReLU | 11 | Accuracy | 12 | Eltwise | +| 13 | BatchNorm | 14 | Scale | 15 | Reshape | 16 | ArgMax | +| 17 | Crop | 18 | Flatten | 19 | Power | 20 | Reduction | +| 21 | Axpy | 22 | ROIPolling | 23 | Permute | 24 | DetectionOutput | +| 25 | Normalize | 26 | Select | 27 | ShuffleChannel | 28 | ConvolutionDepthwise | +| 29 | ReLU | 30 | AbsVal | 31 | Sigmoid | 32 | TanH | + +## ONNX + +| 序号 | OP | 序号 | OP | 序号 | OP | 序号 | OP | +|------|------|------|------|------|------|------|------| +| 1 | Relu | 2 | LeakyRelu | 3 | Elu | 4 | ThresholdedRelu | +| 5 | Prelu | 6 | Tanh | 7 | Shrink | 8 | Sigmoid | +| 9 | Pow | 10 | Softplus | 11 | Softsign | 12 | HardSigmoid | +| 13 | Exp | 14 | Add | 15 | Div | 16 | Sub | +| 17 | Mul | 18 | Shape | 19 | Clip | 20 | AveragePool | +| 21 | Sqrt | 22 | ReduceSum | 23 | ReduceMin | 24 | ReduceMean | +| 25 | Constant | 26 | Pad | 27 | Unsqueeze | 28 | Resize | +| 29 | Upsample | 30 | Expand | 31 | Gather | 32 | Slice | +| 33 | Cast | 34 | Split | 35 | Reshape | 36 | ConstantOfShape | +| 37 | Ceil | 38 | Concat | 39 | Flatten | 40 | ConvTranspose | +| 41 | MatMul | 42 | Sum | 43 | Transpose | 44 | BatchNormalization | +| 45 | Squeeze | 46 | Equal | 47 | Identity | 48 | GlobalAveragePool | +| 49 | MaxPool | 50 | Conv | 51 | Gemm | diff --git a/x2paddle/decoder/tf_decoder.py b/x2paddle/decoder/tf_decoder.py index 25f11a99c1c446d3c668e53eed5e7b10683cc66f..06c30529b3a8a68691301324bd143c2e3db0e80d 100644 --- a/x2paddle/decoder/tf_decoder.py +++ b/x2paddle/decoder/tf_decoder.py @@ -312,7 +312,11 @@ class TFDecoder(object): right_shape_been_input = False while not right_shape_been_input: - shape = input("Shape of Input(e.g. None,224,224,3): ") + try: + shape = raw_input( + "Shape of Input(e.g. None,224,224,3): ") + except: + shape = input("Shape of Input(e.g. None,224,224,3): ") if shape.count("None") > 1: print("Only 1 dimension can be None, type again:)") else: diff --git a/x2paddle/op_mapper/tf_op_mapper.py b/x2paddle/op_mapper/tf_op_mapper.py index 847ebc8b96aab160b1747c1bb73a7fe6ecd4dae0..bf2d5519ebd0a738b1680088e6b28a2470e366bc 100644 --- a/x2paddle/op_mapper/tf_op_mapper.py +++ b/x2paddle/op_mapper/tf_op_mapper.py @@ -168,7 +168,11 @@ class TFOpMapper(OpMapper): x_input = y y_input = x x_shape = y.out_shapes[0] + if len(x_shape) == 0: + x_shape = [1] y_shape = x.out_shapes[0] + if len(y_shape) == 0: + y_shape = [1] else: if len(x_shape) == 1 and len(y_shape) == 4 and x_shape[ 0] == y_shape[-1] and y_shape.count(-1) < 1: @@ -1006,7 +1010,7 @@ class TFOpMapper(OpMapper): attr = { "bias_attr": False, "param_attr": string(kernel.layer_name), - "num_filters": k_size[3], + "num_filters": k_size[2], "filter_size": k_size[0:2], "stride": strides[2:4], "dilation": dilations[2:4], @@ -1112,40 +1116,6 @@ class TFOpMapper(OpMapper): output=node, param_attr=attr) - def ResizeNearestNeighbor(self, node): - input = self.graph.get_node(node.layer.input[0], copy=True) - resize_shape = self.graph.get_node(node.layer.input[1], copy=True) - self.add_omit_nodes(resize_shape.layer_name, node.layer_name) - if resize_shape.layer_type == "Const": - resize_shape = resize_shape.value.tolist() - else: - resize_shape = self.decoder.infer_shape_tensor(resize_shape) - align_corners = node.get_attr("align_corners") - attr = {"align_corners": align_corners, "out_shape": resize_shape} - node.fluid_code.add_layer("resize_nearest", - inputs=input, - output=node, - param_attr=attr) - - def ResizeBilinear(self, node): - input = self.graph.get_node(node.layer.input[0], copy=True) - resize_shape = self.graph.get_node(node.layer.input[1], copy=True) - self.add_omit_nodes(resize_shape.layer_name, node.layer_name) - if resize_shape.layer_type == "Const": - resize_shape = resize_shape.value.tolist() - else: - resize_shape = self.decoder.infer_shape_tensor(resize_shape) - align_corners = node.get_attr("align_corners") - attr = { - "align_corners": align_corners, - "out_shape": resize_shape, - "align_mode": 1 - } - node.fluid_code.add_layer("resize_bilinear", - inputs=input, - output=node, - param_attr=attr) - def ResizeNearestNeighbor(self, node): input = self.graph.get_node(node.layer.input[0], copy=True) resize_shape = self.graph.get_node(node.layer.input[1], copy=True) @@ -1191,37 +1161,6 @@ class TFOpMapper(OpMapper): output=node, param_attr=None) - def RandomUniform(self, node): - shape = self.graph.get_node(node.layer.input[0], copy=True) - self.add_omit_nodes(shape.layer_name, node.layer_name) - if shape.layer_type == "Const": - shape = shape.value.tolist() - else: - shape = self.decoder.infer_shape_tensor(shape) - if node.tf_data_format == "NHWC" and len(shape) == 4: - shape = [shape[i] for i in [0, 3, 1, 2]] - attr = {"shape": shape, "min": 0.0, "max": 0.9999} - if shape[0] < 0: - input = self.batch_node - node.fluid_code.add_layer("uniform_random_batch_size_like", - inputs=input, - output=node, - param_attr=attr) - else: - node.fluid_code.add_layer("uniform_random", - inputs=None, - output=node, - param_attr=attr) - - def GreaterEqual(self, node): - x = self.graph.get_node(node.layer.input[0], copy=True) - y = self.graph.get_node(node.layer.input[1], copy=True) - inputs = {"x": x, "y": y} - node.fluid_code.add_layer("greater_equal", - inputs=inputs, - output=node, - param_attr=None) - def RandomUniform(self, node): shape = self.graph.get_node(node.layer.input[0], copy=True) self.add_omit_nodes(shape.layer_name, node.layer_name) diff --git a/x2paddle/op_mapper/tf_op_mapper_nhwc.py b/x2paddle/op_mapper/tf_op_mapper_nhwc.py index 24fd7de2a1787a03141909b8f55c5d5ff734b563..06fec6923cca4ffdfee4b04070ba2c7f1bcd9906 100644 --- a/x2paddle/op_mapper/tf_op_mapper_nhwc.py +++ b/x2paddle/op_mapper/tf_op_mapper_nhwc.py @@ -121,10 +121,29 @@ class TFOpMapperNHWC(OpMapper): pd_param_name = list(param.values())[0] tf_param = node.get_attr(tf_param_name) attr[pd_param_name] = tf_param - node.fluid_code.add_layer(op_info[0], - inputs=input, - output=node, - param_attr=attr) + + if len(input.out_shapes[0]) == 4 and op_info[0] != 'shape': + attr1 = {"perm": [0, 3, 1, 2]} + node.fluid_code.add_layer('transpose', + inputs=input, + output=node, + param_attr=attr1) + input = node + node.fluid_code.add_layer(op_info[0], + inputs=input, + output=node, + param_attr=attr) + input = node + attr2 = {"perm": [0, 2, 3, 1]} + node.fluid_code.add_layer('transpose', + inputs=input, + output=node, + param_attr=attr2) + else: + node.fluid_code.add_layer(op_info[0], + inputs=input, + output=node, + param_attr=attr) def elementwise_map(self, node): assert node.layer_type in self.elementwise_ops @@ -149,7 +168,11 @@ class TFOpMapperNHWC(OpMapper): x_input = y y_input = x x_shape = y.out_shapes[0] + if len(x_shape) == 0: + x_shape = [1] y_shape = x.out_shapes[0] + if len(y_shape) == 0: + y_shape = [1] else: raise Exception("Unexpected situation happend") @@ -193,11 +216,30 @@ class TFOpMapperNHWC(OpMapper): output="y_tmp", param_attr=attr) y_input = "y_tmp" - inputs = {"x": x_input, "y": y_input} - node.fluid_code.add_layer(op_type, - inputs=inputs, - output=node, - param_attr=None) + if len(x_shape) == 4 and len(y_shape) == 4: + node.fluid_code.add_layer("transpose", + inputs=x_input, + output=x_input, + param_attr={'perm': [0, 3, 1, 2]}) + node.fluid_code.add_layer("transpose", + inputs=y_input, + output=y_input, + param_attr={'perm': [0, 3, 1, 2]}) + inputs = {"x": x_input, "y": y_input} + node.fluid_code.add_layer(op_type, + inputs=inputs, + output=node, + param_attr=None) + node.fluid_code.add_layer("transpose", + inputs=node, + output=node, + param_attr={'perm': [0, 2, 3, 1]}) + else: + inputs = {"x": x_input, "y": y_input} + node.fluid_code.add_layer(op_type, + inputs=inputs, + output=node, + param_attr=None) def Placeholder(self, node): shape = node.out_shapes[0] @@ -965,7 +1007,7 @@ class TFOpMapperNHWC(OpMapper): attr = { "bias_attr": False, "param_attr": string(kernel.layer_name), - "num_filters": k_size[3], + "num_filters": k_size[2], "filter_size": k_size[0:2], "stride": strides[2:4], "dilation": dilations[2:4], @@ -978,9 +1020,7 @@ class TFOpMapperNHWC(OpMapper): if pad_mode == "SAME": if node.tf_data_format == "NHWC": - print(out_shape) out_shape = [out_shape[i] for i in [0, 3, 1, 2]] - print(out_shape) for i in range(4): if out_shape[i] < 0: out_shape[i] = 999999 diff --git a/x2paddle/optimizer/tf_optimizer.py b/x2paddle/optimizer/tf_optimizer.py index 99156844a24c619e15d83be2d9345feba73b7e3e..6431e439ccf463a1aa2c08ba1b1eac3f34d41175 100644 --- a/x2paddle/optimizer/tf_optimizer.py +++ b/x2paddle/optimizer/tf_optimizer.py @@ -232,84 +232,35 @@ class TFOptimizer(object): 'act'] node.fluid_code.clear() self.graph.remove_node(node.layer_name) + self.graph.identity_map[node.layer_name] = input.layer_name def remove_transpose(self): graph_copy = cp.deepcopy(self.graph) nhwc_insensitive_ops = [ 'Relu', 'Relu6', 'Abs', 'Sigmoid', 'Exp', 'Rsqrt', 'swish_f32', - 'LeakyRelu', 'Cast' + 'LeakyRelu', 'Cast', 'Tanh' ] elementwise_ops = [ 'Sub', 'Add', 'RealDiv', 'Maximum', 'Mul', 'FloorDiv', 'GreaterEqual' ] - for node_name in self.graph.topo_sort: - node = graph_copy.get_node(node_name) - if node is None: - continue - if node.layer_type in nhwc_insensitive_ops: - graph_copy.remove_node(node_name) - optimize_ops = [ 'Conv2D', 'MaxPool', 'FusedBatchNorm', 'DepthwiseConv2dNative', 'AvgPool', 'Pad', 'Conv2DBackpropInput', 'ResizeNearestNeighbor', 'ResizeBilinear', "Placeholder" ] + can_be_optimized_ops = [ + 'Conv2D', 'MaxPool', 'FusedBatchNorm', 'DepthwiseConv2dNative', + 'AvgPool', 'Pad', 'Conv2DBackpropInput', 'ResizeNearestNeighbor', + 'ResizeBilinear', "Placeholder", 'Relu', 'Relu6', 'Abs', 'Sigmoid', + 'Exp', 'Rsqrt', 'swish_f32', 'LeakyRelu', 'Cast', 'Tanh' + ] for node_name in self.graph.topo_sort: node = graph_copy.get_node(node_name) if node is None: continue - if node.layer_type in elementwise_ops: - is_nhwc = True - for in_name in node.inputs: - in_node = graph_copy.get_node(in_name) - if hasattr(in_node, "is_nhwc"): - if not in_node.is_nhwc: - is_nhwc = False - else: - if len(in_node.fluid_code.layers) < 2: - is_nhwc = False - continue - if in_node.fluid_code.layers[ - -1].op != "transpose" or in_node.fluid_code.layers[ - -1].param_attr["perm"] != [0, 2, 3, 1]: - is_nhwc = False - continue - node.is_nhwc = is_nhwc - - for i in range(len(self.graph.topo_sort)): - node_name = self.graph.topo_sort[-1 * i - 1] - node = graph_copy.get_node(node_name) - if node is None: - continue - if node.layer_type in elementwise_ops: - can_be_removed = True - if len(node.fluid_code.layers) > 1: - can_be_removed = False - if not node.is_nhwc: - can_be_removed = False - for out_name in node.outputs: - out_node = graph_copy.get_node(out_name) - if hasattr(out_node, "is_nhwc"): - if not out_node.is_nhwc: - can_be_removed = False - else: - if len(out_node.fluid_code.layers) < 2: - can_be_removed = False - break - if out_node.fluid_code.layers[ - 0].op != "transpose" or out_node.fluid_code.layers[ - 0].param_attr["perm"] != [0, 3, 1, 2]: - can_be_removed = False - break - node.can_be_removed = can_be_removed - - for node_name in self.graph.topo_sort: - node = graph_copy.get_node(node_name) - if node is None: - continue - if node.layer_type in optimize_ops: + if node.layer_type in can_be_optimized_ops: if node.fluid_code.layers[ -1].op != "transpose" or node.fluid_code.layers[ -1].param_attr["perm"] != [0, 2, 3, 1]: @@ -327,6 +278,9 @@ class TFOptimizer(object): 0].param_attr["perm"] != [0, 3, 1, 2]: can_be_removed = False break + elif out_node.layer_type in elementwise_ops: + can_be_removed = False + break if can_be_removed and len(node.fluid_code.layers) > 1: true_node = self.graph.get_node(node_name) if true_node.layer_type == "Placeholder": @@ -346,8 +300,6 @@ class TFOptimizer(object): del true_node.fluid_code.layers[-1] for out_name in output_names: out_node = self.graph.get_node(out_name) - if out_node.layer_type in elementwise_ops: - continue out_node.fluid_code.layers[ 1].inputs = out_node.fluid_code.layers[0].inputs del out_node.fluid_code.layers[0] @@ -357,50 +309,248 @@ class TFOptimizer(object): if node is None: continue if node.layer_type in elementwise_ops: - if not node.can_be_removed: + can_be_removed = True + if node.fluid_code.layers[ + -1].op != "transpose" or node.fluid_code.layers[ + -1].param_attr["perm"] != [0, 2, 3, 1]: + continue + can_be_removed = True + + output_names = node.outputs + for out_name in output_names: + out_node = graph_copy.get_node(out_name) + if len(out_node.fluid_code.layers) < 3: + can_be_removed = False + break + if hasattr(out_node, "can_be_removed"): + if not out_node.can_be_removed: + can_be_removed = False + break + if out_node.layer_type in can_be_optimized_ops: + if out_node.fluid_code.layers[ + 0].op != "transpose" or out_node.fluid_code.layers[ + 0].param_attr["perm"] != [0, 3, 1, 2]: + can_be_removed = False + break + elif out_node.layer_type in elementwise_ops: + if out_node.fluid_code.layers[ + 0].op != "transpose" and out_node.fluid_code.layers[ + 1].op != "transpose": + can_be_removed = False + break + if out_node.fluid_code.layers[0].op == "transpose": + if out_node.fluid_code.layers[0].param_attr[ + "perm"] != [0, 3, 1, 2]: + can_be_removed = False + break + if out_node.fluid_code.layers[1].op == "transpose": + if out_node.fluid_code.layers[1].param_attr[ + "perm"] != [0, 3, 1, 2]: + can_be_removed = False + break + + if can_be_removed and len(node.fluid_code.layers) > 1: true_node = self.graph.get_node(node_name) - for i, in_name in enumerate(node.inputs): - in_node = graph_copy.get_node(in_name) - if hasattr(in_node, "is_nhwc") and in_node.is_nhwc: - if i == 0: - l = Layer() - l.op = "transpose" - l.inputs = true_node.fluid_code.layers[ - 0].inputs["x"] - l.param_attr = {"perm": [0, 2, 3, 1]} - l.output = "nhwc_" + l.inputs.layer_name - true_node.fluid_code.layers[0].inputs[ - "x"] = l.output - true_node.fluid_code.layers.insert(0, l) - elif i == 1: - l = Layer() - l.op = "transpose" - l.inputs = true_node.fluid_code.layers[ - 0].inputs["y"] - l.param_attr = {"perm": [0, 2, 3, 1]} - l.output = "nhwc_" + l.inputs.layer_name - true_node.fluid_code.layers[0].inputs[ - "y"] = l.output - true_node.fluid_code.layers.insert(0, l) - else: - raise Exception("Unexpected situation happend") + true_node.fluid_code.layers[ + -2].output = true_node.fluid_code.layers[-1].output + del true_node.fluid_code.layers[-1] + for out_name in output_names: + out_node = self.graph.get_node(out_name) + if out_node.layer_type in can_be_optimized_ops: + out_node.fluid_code.layers[ + 1].inputs = out_node.fluid_code.layers[0].inputs + del out_node.fluid_code.layers[0] + elif out_node.layer_type in elementwise_ops: + if out_node.inputs[0] in node.layer_name: + if out_node.fluid_code.layers[ + 1].op == 'transpose': + out_node.fluid_code.layers[2].inputs[ + 'x'] = out_node.fluid_code.layers[ + 0].inputs + del out_node.fluid_code.layers[0] + else: + out_node.fluid_code.layers[1].inputs[ + 'x'] = out_node.fluid_code.layers[ + 0].inputs + del out_node.fluid_code.layers[0] + elif out_node.inputs[1] in node.layer_name: + if out_node.fluid_code.layers[ + 1].op == 'transpose': + out_node.fluid_code.layers[2].inputs[ + 'y'] = out_node.fluid_code.layers[ + 1].inputs + del out_node.fluid_code.layers[1] + else: + out_node.fluid_code.layers[1].inputs[ + 'y'] = out_node.fluid_code.layers[ + 0].inputs + del out_node.fluid_code.layers[0] + graph_copy = cp.deepcopy(self.graph) + for node_name in self.graph.topo_sort: + node = graph_copy.get_node(node_name) + if node is None or len(node.fluid_code.layers) < 2: + continue + if node.layer_type in can_be_optimized_ops and node.layer_type != "Placeholder": + if node.fluid_code.layers[ + -1].op != "transpose" or node.fluid_code.layers[ + -1].param_attr["perm"] != [0, 2, 3, 1]: continue - else: - for out_name in node.outputs: + can_be_removed = True + output_names = node.outputs + for out_name in output_names: + out_node = graph_copy.get_node(out_name) + if hasattr(out_node, "can_be_removed"): + if not out_node.can_be_removed: + can_be_removed = False + break + if len(out_node.fluid_code.layers) < 2: + can_be_removed = False + break + if out_node.layer_type in can_be_optimized_ops: + if out_node.fluid_code.layers[ + 0].op != "transpose" or out_node.fluid_code.layers[ + 0].param_attr["perm"] != [0, 3, 1, 2]: + can_be_removed = False + break + elif out_node.layer_type in elementwise_ops: + if out_node.fluid_code.layers[ + 0].op != "transpose" and out_node.fluid_code.layers[ + 1].op != "transpose": + can_be_removed = False + break + if out_node.fluid_code.layers[ + 0].op == "expand" or out_node.fluid_code.layers[ + 1].op == "expand": + can_be_removed = False + break + if out_node.fluid_code.layers[0].op == "transpose": + if out_node.fluid_code.layers[0].param_attr[ + "perm"] != [0, 3, 1, 2]: + can_be_removed = False + break + if out_node.fluid_code.layers[1].op == "transpose": + if out_node.fluid_code.layers[1].param_attr[ + "perm"] != [0, 3, 1, 2]: + can_be_removed = False + break + elif out_node.layer_type not in elementwise_ops and out_node.layer_type not in can_be_optimized_ops: + can_be_removed = False + break + + if can_be_removed: + true_node = self.graph.get_node(node_name) + if len(true_node.fluid_code.layers) < 2: + continue + true_node.fluid_code.layers[ + -2].output = true_node.fluid_code.layers[-1].output + del true_node.fluid_code.layers[-1] + for out_name in output_names: out_node = self.graph.get_node(out_name) - if out_node.layer_type not in elementwise_ops: - assert out_node.fluid_code.layers[ - 0].op == "transpose", "unexpected situation happend" + if out_node.layer_type in can_be_optimized_ops: out_node.fluid_code.layers[ 1].inputs = out_node.fluid_code.layers[0].inputs del out_node.fluid_code.layers[0] + elif out_node.layer_type in elementwise_ops: + if out_node.inputs[0] in node.layer_name: + if out_node.fluid_code.layers[ + 1].op == 'transpose': + if out_node.fluid_code.layers[ + 2].op == 'transpose': + out_node.fluid_code.layers[3].inputs[ + 'x'] = out_node.fluid_code.layers[ + 0].inputs + else: + out_node.fluid_code.layers[2].inputs[ + 'x'] = out_node.fluid_code.layers[ + 0].inputs + del out_node.fluid_code.layers[0] + else: + out_node.fluid_code.layers[1].inputs[ + 'x'] = out_node.fluid_code.layers[ + 0].inputs + del out_node.fluid_code.layers[0] + elif out_node.inputs[1] in node.layer_name: + if out_node.fluid_code.layers[ + 1].op == 'transpose': + out_node.fluid_code.layers[2].inputs[ + 'y'] = out_node.fluid_code.layers[ + 1].inputs + del out_node.fluid_code.layers[1] + else: + out_node.fluid_code.layers[1].inputs[ + 'y'] = out_node.fluid_code.layers[ + 0].inputs + del out_node.fluid_code.layers[0] + + graph_copy = cp.deepcopy(self.graph) + for node_name in self.graph.topo_sort: + node = graph_copy.get_node(node_name) + if node is None: + continue + if node.layer_type in elementwise_ops: + can_be_removed = True + if len(node.fluid_code.layers) < 3: + continue + + numTranspose = 0 + numNotTranspose = 0 + + for i in range(len(node.fluid_code.layers)): + if node.fluid_code.layers[i].op == 'transpose': + numTranspose += 1 + elif node.fluid_code.layers[i].op != 'expand': + numNotTranspose += 1 + if numTranspose > numNotTranspose: + if node.fluid_code.layers[0].op == 'expand': + if node.fluid_code.layers[ + 1].op != 'transpose' or node.fluid_code.layers[ + 2].op != 'transpose': + continue + else: + true_node = self.graph.get_node(node_name) + true_node.fluid_code.layers[3].inputs[ + 'x'] = true_node.fluid_code.layers[1].inputs + true_node.fluid_code.layers[3].inputs[ + 'y'] = true_node.fluid_code.layers[2].inputs + + l = Layer() + l.op = 'transpose' + l.inputs = true_node.fluid_code.layers[3].output + l.param_attr = {'perm': [0, 3, 1, 2]} + if isinstance(l.inputs, six.string_types): + l.output = l.inputs + else: + l.output = l.inputs.layer_name + true_node.fluid_code.layers.append(l) + del true_node.fluid_code.layers[1] + del true_node.fluid_code.layers[1] + else: + if node.fluid_code.layers[ + 0].op != 'transpose' or node.fluid_code.layers[ + 1].op != 'transpose': + continue + else: + true_node = self.graph.get_node(node_name) + true_node.fluid_code.layers[2].inputs[ + 'x'] = true_node.fluid_code.layers[0].inputs + true_node.fluid_code.layers[2].inputs[ + 'y'] = true_node.fluid_code.layers[1].inputs + + l = Layer() + l.op = 'transpose' + l.inputs = true_node.fluid_code.layers[2].output + l.param_attr = {'perm': [0, 3, 1, 2]} + l.output = l.inputs.layer_name + true_node.fluid_code.layers.append(l) + del true_node.fluid_code.layers[0] + del true_node.fluid_code.layers[0] def make_nchw_input_output(self): for i, name in enumerate(self.graph.input_nodes): node = self.graph.get_node(name) if len(node.out_shapes[0]) == 4 and node.tf_data_format == "NHWC": shape = node.fluid_code.layers[0].param_attr["shape"] - shape = [shape[i] for i in [0, 3, 1, 2]] + shape = [shape[j] for j in [0, 3, 1, 2]] node.fluid_code.layers[0].param_attr["shape"] = shape node.fluid_code.layers[0].output = "nhwc_" + name attr = {"perm": [0, 2, 3, 1]} diff --git a/x2paddle_model_zoo.md b/x2paddle_model_zoo.md index 42abe0691a8374fff602d2a3eb4b1aadb2c5b455..92553ffc4b8e6717bc5446f6bb5b9bc1103d6dce 100644 --- a/x2paddle_model_zoo.md +++ b/x2paddle_model_zoo.md @@ -65,3 +65,4 @@ | mNASNet | [pytorch(personal practice)](https://github.com/rwightman/gen-efficientnet-pytorch) |9| | EfficientNet | [pytorch(personal practice)](https://github.com/rwightman/gen-efficientnet-pytorch) |9| | SqueezeNet | [onnx official](https://s3.amazonaws.com/download.onnx/models/opset_9/squeezenet.tar.gz) |9| +|Ultra-Light-Fast-Generic-Face-Detector-1MB| [onnx_model](https://github.com/Linzaer/Ultra-Light-Fast-Generic-Face-Detector-1MB/tree/master/models/onnx)| |