未验证 提交 22c18dd4 编写于 作者: J Jason 提交者: GitHub

Merge pull request #309 from Channingss/paddle_onnx

Paddle2ONNX support export ONNX opset9,10,11
...@@ -61,6 +61,7 @@ x2paddle --framework=paddle2onnx --model=paddle_infer_model_dir --save_dir=onnx_ ...@@ -61,6 +61,7 @@ x2paddle --framework=paddle2onnx --model=paddle_infer_model_dir --save_dir=onnx_
|--without_data_format_optimization | **[可选]** For TensorFlow, 当指定该参数时,关闭NHWC->NCHW的优化,见[文档Q2](FAQ.md) | |--without_data_format_optimization | **[可选]** For TensorFlow, 当指定该参数时,关闭NHWC->NCHW的优化,见[文档Q2](FAQ.md) |
|--define_input_shape | **[可选]** For TensorFlow, 当指定该参数时,强制用户输入每个Placeholder的shape,见[文档Q2](FAQ.md) | |--define_input_shape | **[可选]** For TensorFlow, 当指定该参数时,强制用户输入每个Placeholder的shape,见[文档Q2](FAQ.md) |
|--params_merge | **[可选]** 当指定该参数时,转换完成后,inference_model中的所有模型参数将合并保存为一个文件__params__ | |--params_merge | **[可选]** 当指定该参数时,转换完成后,inference_model中的所有模型参数将合并保存为一个文件__params__ |
|--onnx_opset | **[可选]** 当framework为paddle2onnx时,该参数可设置转换为ONNX的OpSet版本,目前支持9、10、11,默认为10 |
......
...@@ -75,6 +75,12 @@ def arg_parser(): ...@@ -75,6 +75,12 @@ def arg_parser():
action="store_true", action="store_true",
default=False, default=False,
help="define input shape for tf model") help="define input shape for tf model")
parser.add_argument(
"--onnx_opset",
"-oo",
type=int,
default=10,
help="when paddle2onnx set onnx opset version to export")
parser.add_argument( parser.add_argument(
"--params_merge", "--params_merge",
"-pm", "-pm",
...@@ -172,7 +178,7 @@ def onnx2paddle(model_path, save_dir, params_merge=False): ...@@ -172,7 +178,7 @@ def onnx2paddle(model_path, save_dir, params_merge=False):
return return
print("Now translating model from onnx to paddle.") print("Now translating model from onnx to paddle.")
from x2paddle.op_mapper.onnx_op_mapper import ONNXOpMapper from x2paddle.op_mapper.onnx2paddle.onnx_op_mapper import ONNXOpMapper
from x2paddle.decoder.onnx_decoder import ONNXDecoder from x2paddle.decoder.onnx_decoder import ONNXDecoder
from x2paddle.optimizer.onnx_optimizer import ONNXOptimizer from x2paddle.optimizer.onnx_optimizer import ONNXOptimizer
model = ONNXDecoder(model_path) model = ONNXDecoder(model_path)
...@@ -186,12 +192,12 @@ def onnx2paddle(model_path, save_dir, params_merge=False): ...@@ -186,12 +192,12 @@ def onnx2paddle(model_path, save_dir, params_merge=False):
print("Paddle model and code generated.") print("Paddle model and code generated.")
def paddle2onnx(model_path, save_dir): def paddle2onnx(model_path, save_dir, opset_number):
from x2paddle.decoder.paddle_decoder import PaddleDecoder from x2paddle.decoder.paddle_decoder import PaddleDecoder
from x2paddle.op_mapper.paddle_op_mapper import PaddleOpMapper from x2paddle.op_mapper.paddle2onnx.paddle_op_mapper import PaddleOpMapper
model = PaddleDecoder(model_path, '__model__', '__params__') model = PaddleDecoder(model_path, '__model__', '__params__')
mapper = PaddleOpMapper() mapper = PaddleOpMapper()
mapper.convert(model.program, save_dir) mapper.convert(model.program, save_dir, opset_number=opset_number)
def main(): def main():
...@@ -258,7 +264,7 @@ def main(): ...@@ -258,7 +264,7 @@ def main():
elif args.framework == "paddle2onnx": elif args.framework == "paddle2onnx":
assert args.model is not None, "--model should be defined while translating paddle model to onnx" assert args.model is not None, "--model should be defined while translating paddle model to onnx"
paddle2onnx(args.model, args.save_dir) paddle2onnx(args.model, args.save_dir, args.onnx_opset)
else: else:
raise Exception( raise Exception(
......
...@@ -12,7 +12,7 @@ ...@@ -12,7 +12,7 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
from x2paddle.op_mapper.onnx_opsets.opset9 import OpSet9 from x2paddle.op_mapper.onnx2paddle.opsets.opset9 import OpSet9
from x2paddle.core.op_mapper import OpMapper from x2paddle.core.op_mapper import OpMapper
from x2paddle.op_mapper.onnx_opsets.custom_layer import * from x2paddle.op_mapper.onnx_opsets.custom_layer import *
from x2paddle.decoder.onnx_decoder import ONNXGraph, ONNXGraphNode, ONNXGraphDataNode from x2paddle.decoder.onnx_decoder import ONNXGraph, ONNXGraphNode, ONNXGraphDataNode
......
...@@ -13,10 +13,6 @@ ...@@ -13,10 +13,6 @@
# limitations under the License. # limitations under the License.
from .register import get_registered_layers from .register import get_registered_layers
#custom layer import begins
from . import InstanceNormalization
#custom layer import ends
custom_layers = get_registered_layers() custom_layers = get_registered_layers()
......
# Copyright (c) 2019 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 math
import sys
import x2paddle
import os
import numpy as np
import paddle.fluid.core as core
import paddle.fluid as fluid
import onnx
from onnx import helper, onnx_pb
from x2paddle.op_mapper.paddle2onnx.opset9.opset import OpSet9
class OpSet10(OpSet9):
def __init__(self):
super(OpSet10, self).__init__()
def slice(self, op, block):
axes = op.attr('axes')
starts = op.attr('starts')
ends = op.attr('ends')
axes_name = self.get_name(op.type, 'axes')
starts_name = self.get_name(op.type, 'starts')
ends_name = self.get_name(op.type, 'ends')
axes_node = self.make_constant_node(axes_name,
onnx_pb.TensorProto.INT64, axes)
starts_node = self.make_constant_node(starts_name,
onnx_pb.TensorProto.INT64, starts)
ends_node = self.make_constant_node(ends_name,
onnx_pb.TensorProto.INT64, ends)
node = helper.make_node(
"Slice",
inputs=[op.input('Input')[0], starts_name, ends_name, axes_name],
outputs=op.output('Out'), )
return [starts_node, ends_node, axes_node, node]
def im2sequence(self, op, block):
from .paddle_custom_layer.im2sequence import im2sequence
return im2sequence(op, block)
def yolo_box(self, op, block):
from .paddle_custom_layer.yolo_box import yolo_box
return yolo_box(op, block)
def multiclass_nms(self, op, block):
from .paddle_custom_layer.multiclass_nms import multiclass_nms
return multiclass_nms(op, block)
import onnx
import numpy as np
from onnx import onnx_pb, helper
from x2paddle.op_mapper.paddle2onnx.opset9.paddle_custom_layer.im2sequence import im2sequence as im2sequence9
def im2sequence(op, block):
return im2sequence9(op, block)
...@@ -12,45 +12,21 @@ ...@@ -12,45 +12,21 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
from .register import register import math
import sys
import os
def InstanceNormalization_shape(input_shape): import numpy as np
return input_shape import paddle.fluid.core as core
import paddle.fluid as fluid
import onnx
def InstanceNormalization_layer(inputs, name=None): import warnings
# TODO(lvmengsi@baidu.com): Check the accuracy when using fluid.layers.layer_norm. from onnx import helper, onnx_pb
epsilon = 1e-5 from x2paddle.op_mapper.paddle2onnx.opset9.paddle_custom_layer.multiclass_nms import multiclass_nms as multiclass_nms9
input_ = inputs[0]
mean = fluid.layers.reduce_mean(input_, dim=[2, 3], keep_dim=True)
var = fluid.layers.reduce_mean( def multiclass_nms(op, block):
fluid.layers.square(input_ - mean), dim=[2, 3], keep_dim=True) """
if name is not None: Convert the paddle multiclass_nms to onnx op.
scale_name = name + "_scale" This op is get the select boxes from origin boxes.
offset_name = name + "_offset" """
return multiclass_nms9(op, block)
scale_param = inputs[1]
offset_param = inputs[2]
scale = fluid.layers.create_parameter(
name=scale_param.name, shape=input_.shape[1:2], dtype="float32")
offset = fluid.layers.create_parameter(
name=offset_param.name, shape=input_.shape[1:2], dtype="float32")
tmp = fluid.layers.elementwise_mul(x=(input_ - mean), y=scale, axis=1)
tmp = tmp / fluid.layers.sqrt(var + epsilon)
tmp = fluid.layers.elementwise_add(tmp, offset, axis=1)
return tmp
def InstanceNormalization_weights(name, data=None):
weights_name = [name + '_scale']
return weights_name
register(
kind='InstanceNormalization',
shape=InstanceNormalization_shape,
layer=InstanceNormalization_layer,
child_func=None,
weights=InstanceNormalization_weights)
import onnx
import numpy as np
from onnx import onnx_pb, helper
from x2paddle.op_mapper.paddle2onnx.opset9.paddle_custom_layer.yolo_box import yolo_box as yolo_box9
def yolo_box(op, block):
return yolo_box9(op, block)
# Copyright (c) 2019 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 math
import sys
import x2paddle
import os
import numpy as np
import paddle.fluid.core as core
import paddle.fluid as fluid
import onnx
from onnx import helper, onnx_pb
from x2paddle.op_mapper.paddle2onnx.opset10.opset import OpSet10
class OpSet11(OpSet10):
def __init__(self):
super(OpSet11, self).__init__()
def relu6(self, op, block):
min_name = self.get_name(op.type, 'min')
max_name = self.get_name(op.type, 'max')
min_node = self.make_constant_node(min_name, onnx_pb.TensorProto.FLOAT,
0)
max_node = self.make_constant_node(max_name, onnx_pb.TensorProto.FLOAT,
op.attr('threshold'))
node = helper.make_node(
'Clip',
inputs=[op.input('X')[0], min_name, max_name],
outputs=op.output('Out'), )
return [min_node, max_node, node]
def bilinear_interp(self, op, block):
input_names = op.input_names
coordinate_transformation_mode = ''
align_corners = op.attr('align_corners')
align_mode = op.attr('align_mode')
if align_corners:
coordinate_transformation_mode = 'align_corners'
elif align_mode == 1:
coordinate_transformation_mode = 'asymmetric'
else:
coordinate_transformation_mode = 'half_pixel'
if ('OutSize' in input_names and len(op.input('OutSize')) > 0) or (
'SizeTensor' in input_names and
len(op.input('SizeTensor')) > 0):
node_list = list()
roi_node = self.make_constant_node(
self.get_name(op.type, 'roi'), onnx_pb.TensorProto.FLOAT,
[1, 1, 1, 1, 1, 1, 1, 1])
roi_name = self.get_name(op.type, 'roi')
roi_node = self.make_constant_node(
roi_name, onnx_pb.TensorProto.FLOAT, [1, 1, 1, 1, 1, 1, 1, 1])
empty_name = self.get_name(op.type, 'empty')
empty_tensor = helper.make_tensor(
empty_name,
onnx_pb.TensorProto.FLOAT, (0, ),
np.array([]).astype('float32'),
raw=False)
empty_node = helper.make_node(
'Constant', [], outputs=[empty_name], value=empty_tensor)
shape_name0 = self.get_name(op.type, 'shape')
shape_node0 = helper.make_node(
'Shape', inputs=op.input('X'), outputs=[shape_name0])
starts_name = self.get_name(op.type, 'slice.starts')
starts_node = self.make_constant_node(
starts_name, onnx_pb.TensorProto.INT64, [0])
ends_name = self.get_name(op.type, 'slice.ends')
ends_node = self.make_constant_node(ends_name,
onnx_pb.TensorProto.INT64, [2])
shape_name1 = self.get_name(op.type, 'shape')
shape_node1 = helper.make_node(
'Slice',
inputs=[shape_name0, starts_name, ends_name],
outputs=[shape_name1])
node_list.extend([
roi_node, empty_node, shape_node0, starts_node, ends_node,
shape_node1
])
if 'OutSize' in input_names and len(op.input('OutSize')) > 0:
cast_shape_name = self.get_name(op.type, "shape.cast")
cast_shape_node = helper.make_node(
'Cast',
inputs=op.input('OutSize'),
outputs=[cast_shape_name],
to=onnx_pb.TensorProto.INT64)
node_list.append(cast_shape_node)
else:
concat_shape_name = self.get_name(op.type, "shape.concat")
concat_shape_node = helper.make_node(
"Concat",
inputs=op.input('SizeTensor'),
outputs=[concat_shape_name],
axis=0)
cast_shape_name = self.get_name(op.type, "shape.cast")
cast_shape_node = helper.make_node(
'Cast',
inputs=[concat_shape_name],
outputs=[cast_shape_name],
to=onnx_pb.TensorProto.INT64)
node_list.extend([concat_shape_node, cast_shape_node])
shape_name3 = self.get_name(op.type, "shape.concat")
shape_node3 = helper.make_node(
'Concat',
inputs=[shape_name1, cast_shape_name],
outputs=[shape_name3],
axis=0)
result_node = helper.make_node(
'Resize',
inputs=[op.input('X')[0], roi_name, empty_name, shape_name3],
outputs=op.output('Out'),
mode='linear',
coordinate_transformation_mode=coordinate_transformation_mode)
node_list.extend([shape_node3, result_node])
return node_list
elif 'Scale' in input_names and len(op.input('Scale')) > 0:
node = helper.make_node(
'Resize',
inputs=[op.input('X')[0], op.input('Scale')[0]],
outputs=op.output('Out'),
mode='linear',
coordinate_transformation_mode=coordinate_transformation_mode)
else:
out_shape = [op.attr('out_h'), op.attr('out_w')]
scale = op.attr('scale')
if out_shape.count(-1) > 0:
scale_name = self.get_name(op.type, 'scale')
scale_node = self.make_constant_node(scale_name,
onnx_pb.TensorProto.FLOAT,
[1, 1, scale, scale])
roi_name = self.get_name(op.type, 'roi')
roi_node = self.make_constant_node(roi_name,
onnx_pb.TensorProto.FLOAT,
[1, 1, 1, 1, 1, 1, 1, 1])
node = helper.make_node(
'Resize',
inputs=[op.input('X')[0], roi_name, scale_name],
outputs=op.output('Out'),
mode='nearest',
coordinate_transformation_mode=coordinate_transformation_mode
)
return [scale_node, roi_node, node]
else:
raise Exception("Unexpected situation happend")
return node
def nearest_interp(self, op, block):
input_names = op.input_names
coordinate_transformation_mode = ''
align_corners = op.attr('align_corners')
if align_corners:
coordinate_transformation_mode = 'align_corners'
else:
coordinate_transformation_mode = 'asymmetric'
if 'OutSize' in input_names and len(op.input('OutSize')) > 0:
node = helper.make_node(
'Resize',
inputs=[op.input('X')[0], '', op.input('OutSize')[0]],
outputs=op.output('Out'),
mode='nearest',
coordinate_transformation_mode=coordinate_transformation_mode)
elif 'Scale' in input_names and len(op.input('Scale')) > 0:
node = helper.make_node(
'Resize',
inputs=[op.input('X')[0], op.input('Scale')[0]],
outputs=op.output('Out'),
mode='nearest',
coordinate_transformation_mode=coordinate_transformation_mode)
else:
out_shape = [op.attr('out_h'), op.attr('out_w')]
scale = op.attr('scale')
if out_shape.count(-1) > 0:
scale_name = self.get_name(op.type, 'scale')
scale_node = self.make_constant_node(scale_name,
onnx_pb.TensorProto.FLOAT,
[1, 1, scale, scale])
roi_name = self.get_name(op.type, 'roi')
roi_node = self.make_constant_node(roi_name,
onnx_pb.TensorProto.FLOAT,
[1, 1, 1, 1, 1, 1, 1, 1])
node = helper.make_node(
'Resize',
inputs=[op.input('X')[0], roi_name, scale_name],
outputs=op.output('Out'),
mode='nearest',
coordinate_transformation_mode=coordinate_transformation_mode
)
return [scale_node, roi_node, node]
else:
raise Exception("Unexpected situation happend")
return node
def hard_swish(self, op, block):
min_name = self.get_name(op.type, 'min')
max_name = self.get_name(op.type, 'max')
scale_name = self.get_name(op.type, 'scale')
offset_name = self.get_name(op.type, 'offset')
min_node = self.make_constant_node(min_name, onnx_pb.TensorProto.FLOAT,
0)
max_node = self.make_constant_node(max_name, onnx_pb.TensorProto.FLOAT,
op.attr('threshold'))
scale_node = self.make_constant_node(scale_name,
onnx_pb.TensorProto.FLOAT,
op.attr('scale'))
offset_node = self.make_constant_node(offset_name,
onnx_pb.TensorProto.FLOAT,
op.attr('offset'))
name0 = self.get_name(op.type, 'add')
node0 = helper.make_node(
'Add', inputs=[op.input('X')[0], offset_name], outputs=[name0])
name1 = self.get_name(op.type, 'relu')
node1 = helper.make_node(
'Clip',
inputs=[name0, min_name, max_name],
outputs=[name1], )
name2 = self.get_name(op.type, 'mul')
node2 = helper.make_node(
'Mul', inputs=[op.input('X')[0], name1], outputs=[name2])
node3 = helper.make_node(
'Div', inputs=[name2, scale_name], outputs=op.output('Out'))
return [
min_node, max_node, scale_node, offset_node, node0, node1, node2,
node3
]
def im2sequence(self, op, block):
from .paddle_custom_layer.im2sequence import im2sequence
return im2sequence(op, block)
def yolo_box(self, op, block):
from .paddle_custom_layer.yolo_box import yolo_box
return yolo_box(op, block)
def multiclass_nms(self, op, block):
from .paddle_custom_layer.multiclass_nms import multiclass_nms
return multiclass_nms(op, block)
import onnx
import numpy as np
from onnx import onnx_pb, helper
from x2paddle.op_mapper.paddle2onnx.opset10.paddle_custom_layer.im2sequence import im2sequence as im2sequence10
def im2sequence(op, block):
return im2sequence10(op, block)
...@@ -125,7 +125,7 @@ def multiclass_nms(op, block): ...@@ -125,7 +125,7 @@ def multiclass_nms(op, block):
vals=[value])) vals=[value]))
node_list.append(node) node_list.append(node)
# Ine this code block, we will deocde the raw score data, reshape N * C * M to 1 * N*C*M # In this code block, we will deocde the raw score data, reshape N * C * M to 1 * N*C*M
# and the same time, decode the select indices to 1 * D, gather the select_indices # and the same time, decode the select indices to 1 * D, gather the select_indices
outputs_gather_1 = [result_name + "@gather_1"] outputs_gather_1 = [result_name + "@gather_1"]
node_gather_1 = onnx.helper.make_node( node_gather_1 = onnx.helper.make_node(
...@@ -405,12 +405,36 @@ def multiclass_nms(op, block): ...@@ -405,12 +405,36 @@ def multiclass_nms(op, block):
inputs_concat_final_results = outputs_cast_topk_class + outputs_unsqueeze_topk_scores +\ inputs_concat_final_results = outputs_cast_topk_class + outputs_unsqueeze_topk_scores +\
outputs_gather_select_boxes outputs_gather_select_boxes
outputs_concat_final_results = outputs['Out'] outputs_sort_by_socre_results = [result_name + "@concat_topk_scores"]
node_concat_final_results = onnx.helper.make_node( node_sort_by_socre_results = onnx.helper.make_node(
'Concat', 'Concat',
inputs=inputs_concat_final_results, inputs=inputs_concat_final_results,
outputs=outputs_concat_final_results, outputs=outputs_sort_by_socre_results,
axis=2) axis=2)
node_list.append(node_concat_final_results) node_list.append(node_sort_by_socre_results)
# select topk classes indices
outputs_squeeze_cast_topk_class = [result_name + "@squeeze_cast_topk_class"]
node_squeeze_cast_topk_class = onnx.helper.make_node(
'Squeeze',
inputs=outputs_cast_topk_class,
outputs=outputs_squeeze_cast_topk_class,
axes=[0, 2])
node_list.append(node_squeeze_cast_topk_class)
outputs_topk_select_classes_indices = [result_name + "@topk_select_topk_classes_scores",\
result_name + "@topk_select_topk_classes_indices"]
node_topk_select_topk_indices = onnx.helper.make_node(
'TopK',
inputs=outputs_squeeze_cast_topk_class + outputs_cast_topk_indices,
outputs=outputs_topk_select_classes_indices,
largest=0)
node_list.append(node_topk_select_topk_indices)
outputs_concat_final_results = outputs['Out']
node_concat_final_results = onnx.helper.make_node(
'Gather',
inputs=outputs_sort_by_socre_results +
[outputs_topk_select_classes_indices[1]],
outputs=outputs_concat_final_results,
axis=1)
node_list.append(node_concat_final_results)
return node_list return node_list
...@@ -23,7 +23,7 @@ import onnx ...@@ -23,7 +23,7 @@ import onnx
from onnx import helper, onnx_pb from onnx import helper, onnx_pb
class PaddleOpMapper(object): class OpSet9(object):
def __init__(self): def __init__(self):
self.paddle_onnx_dtype_map = { self.paddle_onnx_dtype_map = {
core.VarDesc.VarType.FP32: onnx_pb.TensorProto.FLOAT, core.VarDesc.VarType.FP32: onnx_pb.TensorProto.FLOAT,
...@@ -34,62 +34,8 @@ class PaddleOpMapper(object): ...@@ -34,62 +34,8 @@ class PaddleOpMapper(object):
core.VarDesc.VarType.INT64: onnx_pb.TensorProto.INT64, core.VarDesc.VarType.INT64: onnx_pb.TensorProto.INT64,
core.VarDesc.VarType.BOOL: onnx_pb.TensorProto.BOOL core.VarDesc.VarType.BOOL: onnx_pb.TensorProto.BOOL
} }
self.name_counter = dict() self.name_counter = dict()
def convert(self, program, save_dir):
weight_nodes = self.convert_weights(program)
op_nodes = list()
input_nodes = list()
output_nodes = list()
unsupported_ops = set()
print("Translating PaddlePaddle to ONNX...\n")
for block in program.blocks:
for i, op in enumerate(block.ops):
sys.stdout.write(
"\rTotal:{}, Current:{} : {} ".format(
len(block.ops), i + 1, op.type))
sys.stdout.flush()
if not hasattr(self, op.type):
unsupported_ops.add(op.type)
continue
if len(unsupported_ops) > 0:
continue
node = getattr(self, op.type)(op, block)
if op.type == 'feed':
input_nodes.append(node)
elif op.type == 'fetch':
output_nodes.append(node)
else:
if isinstance(node, list):
op_nodes = op_nodes + node
else:
op_nodes.append(node)
if len(unsupported_ops) > 0:
print("\nThere's {} ops are not supported yet".format(
len(unsupported_ops)))
for op in unsupported_ops:
print("=========== {} ===========".format(op))
return
graph = helper.make_graph(
nodes=weight_nodes + op_nodes,
name='onnx_model_from_paddle',
initializer=[],
inputs=input_nodes,
outputs=output_nodes)
model = helper.make_model(graph, producer_name='X2Paddle')
onnx.checker.check_model(model)
if not os.path.isdir(save_dir):
os.makedirs(save_dir)
with open(os.path.join(save_dir, 'x2paddle_model.onnx'), 'wb') as f:
f.write(model.SerializeToString())
print("\nTranslated model saved in {}".format(
os.path.join(save_dir, 'x2paddle_model.onnx')))
def get_name(self, op_name, var_name): def get_name(self, op_name, var_name):
name = 'p2o.{}.{}'.format(op_name, var_name) name = 'p2o.{}.{}'.format(op_name, var_name)
if name not in self.name_counter: if name not in self.name_counter:
...@@ -98,6 +44,21 @@ class PaddleOpMapper(object): ...@@ -98,6 +44,21 @@ class PaddleOpMapper(object):
self.name_counter[name] += 1 self.name_counter[name] += 1
return name + '.{}'.format(self.name_counter[name]) return name + '.{}'.format(self.name_counter[name])
def make_constant_node(self, name, dtype, value=None):
if isinstance(value, list):
dims = (len(value), )
elif value is None:
dims = ()
value = []
else:
dims = ()
value = [value]
tensor = helper.make_tensor(
name=name, data_type=dtype, dims=dims, vals=value)
node = helper.make_node(
'Constant', inputs=[], outputs=[name], value=tensor)
return node
def convert_weights(self, program): def convert_weights(self, program):
var_names = program.global_block().vars var_names = program.global_block().vars
nodes = list() nodes = list()
...@@ -118,21 +79,6 @@ class PaddleOpMapper(object): ...@@ -118,21 +79,6 @@ class PaddleOpMapper(object):
nodes.append(node) nodes.append(node)
return nodes return nodes
def make_constant_node(self, name, dtype, value=None):
if isinstance(value, list):
dims = (len(value), )
elif value is None:
dims = ()
value = []
else:
dims = ()
value = [value]
tensor = helper.make_tensor(
name=name, data_type=dtype, dims=dims, vals=value)
node = helper.make_node(
'Constant', inputs=[], outputs=[name], value=tensor)
return node
def conv2d(self, op, block): def conv2d(self, op, block):
kernel_shape = block.var(op.input('Filter')[0]).shape kernel_shape = block.var(op.input('Filter')[0]).shape
node = helper.make_node( node = helper.make_node(
...@@ -182,41 +128,6 @@ class PaddleOpMapper(object): ...@@ -182,41 +128,6 @@ class PaddleOpMapper(object):
alpha=op.attr('alpha')) alpha=op.attr('alpha'))
return node return node
def swish(self, op, block):
"""
The activation swish, y = x / (1 + exp(-beta * x))
"""
beta = op.attr('beta')
beta_name = self.get_name(op.type, 'beta')
beta_node = onnx.helper.make_node(
'Constant',
name=beta_name,
inputs=[],
outputs=[beta_name],
value=onnx.helper.make_tensor(
name=beta_name,
data_type=onnx.TensorProto.FLOAT,
dims=(),
vals=[beta]))
beta_x_name = self.get_name(op.type, 'beta_x')
beta_x_node = onnx.helper.make_node(
'Mul',
name=beta_x_name,
inputs=[op.input('X')[0], beta_name],
outputs=[beta_x_name])
sigmoid_name = self.get_name(op.type, 'sigmoid')
sigmoid_node = onnx.helper.make_node(
'Sigmoid',
name=sigmoid_name,
inputs=[beta_x_name],
outputs=[sigmoid_name])
swish_node = onnx.helper.make_node(
'Mul',
inputs=[op.input('X')[0], sigmoid_name],
outputs=op.output('Out'))
return [beta_node, beta_x_node, sigmoid_node, swish_node]
def elementwise_add(self, op, block): def elementwise_add(self, op, block):
axis = op.attr('axis') axis = op.attr('axis')
x_shape = block.var(op.input('X')[0]).shape x_shape = block.var(op.input('X')[0]).shape
...@@ -285,6 +196,8 @@ class PaddleOpMapper(object): ...@@ -285,6 +196,8 @@ class PaddleOpMapper(object):
pool_type[op.attr('pooling_type')][1], pool_type[op.attr('pooling_type')][1],
inputs=op.input('X'), inputs=op.input('X'),
outputs=op.output('Out'), ) outputs=op.output('Out'), )
elif op.attr('adaptive'):
raise Excpetion("ONNX cannot support adaptive pool")
else: else:
input_shape = block.var(op.input('X')[0]).shape input_shape = block.var(op.input('X')[0]).shape
k_size = op.attr('ksize') k_size = op.attr('ksize')
...@@ -431,17 +344,14 @@ class PaddleOpMapper(object): ...@@ -431,17 +344,14 @@ class PaddleOpMapper(object):
return self.conv2d(op, block) return self.conv2d(op, block)
def relu6(self, op, block): def relu6(self, op, block):
min_name = self.get_name(op.type, 'min') threshold = op.attr('threshold')
max_name = self.get_name(op.type, 'max')
min_node = self.make_constant_node(min_name, onnx_pb.TensorProto.FLOAT,
0)
max_node = self.make_constant_node(max_name, onnx_pb.TensorProto.FLOAT,
op.attr('threshold'))
node = helper.make_node( node = helper.make_node(
'Clip', 'Clip',
inputs=[op.input('X')[0], min_name, max_name], inputs=[op.input('X')[0]],
outputs=op.output('Out'), ) outputs=op.output('Out'),
return [min_node, max_node, node] max=threshold,
min=0.0)
return [node]
def shape(self, op, block): def shape(self, op, block):
node = helper.make_node( node = helper.make_node(
...@@ -469,21 +379,14 @@ class PaddleOpMapper(object): ...@@ -469,21 +379,14 @@ class PaddleOpMapper(object):
axes = op.attr('axes') axes = op.attr('axes')
starts = op.attr('starts') starts = op.attr('starts')
ends = op.attr('ends') ends = op.attr('ends')
axes_name = self.get_name(op.type, 'axes')
starts_name = self.get_name(op.type, 'starts')
ends_name = self.get_name(op.type, 'ends')
axes_node = self.make_constant_node(axes_name,
onnx_pb.TensorProto.INT64, axes)
starts_node = self.make_constant_node(starts_name,
onnx_pb.TensorProto.INT64, starts)
ends_node = self.make_constant_node(ends_name,
onnx_pb.TensorProto.INT64, ends)
node = helper.make_node( node = helper.make_node(
"Slice", "Slice",
inputs=[op.input('Input')[0], starts_name, ends_name, axes_name], inputs=[op.input('Input')[0], starts_name, ends_name, axes_name],
outputs=op.output('Out'), ) outputs=op.output('Out'),
return [starts_node, ends_node, axes_node, node] axes=axes,
starts=starts,
ends=ends)
return [node]
def fill_constant(self, op, block): def fill_constant(self, op, block):
value = op.attr('value') value = op.attr('value')
...@@ -578,27 +481,15 @@ class PaddleOpMapper(object): ...@@ -578,27 +481,15 @@ class PaddleOpMapper(object):
def bilinear_interp(self, op, block): def bilinear_interp(self, op, block):
input_names = op.input_names input_names = op.input_names
coordinate_transformation_mode = 'half_pixel' input_shape = block.vars[op.input('X')[0]].shape
if op.attr('align_corners'): if op.attr('align_corners') or op.attr('align_mode') == 0:
coordinate_transformation_mode = 'align_corners' raise Exception(
"Resize in onnx(opset<=10) only support coordinate_transformation_mode: 'asymmetric'."
)
if ('OutSize' in input_names and len(op.input('OutSize')) > 0) or ( if ('OutSize' in input_names and len(op.input('OutSize')) > 0) or (
'SizeTensor' in input_names and 'SizeTensor' in input_names and
len(op.input('SizeTensor')) > 0): len(op.input('SizeTensor')) > 0):
node_list = list() node_list = list()
roi_node = self.make_constant_node(
self.get_name(op.type, 'roi'), onnx_pb.TensorProto.FLOAT,
[1, 1, 1, 1, 1, 1, 1, 1])
roi_name = self.get_name(op.type, 'roi')
roi_node = self.make_constant_node(
roi_name, onnx_pb.TensorProto.FLOAT, [1, 1, 1, 1, 1, 1, 1, 1])
empty_name = self.get_name(op.type, 'empty')
empty_tensor = helper.make_tensor(
empty_name,
onnx_pb.TensorProto.FLOAT, (0, ),
np.array([]).astype('float32'),
raw=False)
empty_node = helper.make_node(
'Constant', [], outputs=[empty_name], value=empty_tensor)
shape_name0 = self.get_name(op.type, 'shape') shape_name0 = self.get_name(op.type, 'shape')
shape_node0 = helper.make_node( shape_node0 = helper.make_node(
'Shape', inputs=op.input('X'), outputs=[shape_name0]) 'Shape', inputs=op.input('X'), outputs=[shape_name0])
...@@ -613,16 +504,7 @@ class PaddleOpMapper(object): ...@@ -613,16 +504,7 @@ class PaddleOpMapper(object):
'Slice', 'Slice',
inputs=[shape_name0, starts_name, ends_name], inputs=[shape_name0, starts_name, ends_name],
outputs=[shape_name1]) outputs=[shape_name1])
node_list.extend([ node_list.extend([shape_node0, starts_node, ends_node, shape_node1])
roi_node, empty_node, shape_node0, starts_node, ends_node,
shape_node1
])
# shape_name2 = self.get_name(op.type, "shape.cast")
# shape_node2 = helper.make_node(
# 'Cast',
# inputs=op.input('OutSize'),
# outputs=[shape_name2],
# to=onnx_pb.TensorProto.INT64)
if 'OutSize' in input_names and len(op.input('OutSize')) > 0: if 'OutSize' in input_names and len(op.input('OutSize')) > 0:
cast_shape_name = self.get_name(op.type, "shape.cast") cast_shape_name = self.get_name(op.type, "shape.cast")
cast_shape_node = helper.make_node( cast_shape_node = helper.make_node(
...@@ -632,7 +514,8 @@ class PaddleOpMapper(object): ...@@ -632,7 +514,8 @@ class PaddleOpMapper(object):
to=onnx_pb.TensorProto.INT64) to=onnx_pb.TensorProto.INT64)
node_list.append(cast_shape_node) node_list.append(cast_shape_node)
else: else:
concat_shape_name = self.get_name(op.type, "shape.concat") concat_shape_name = self.get_name(
op.type, op.output('Out')[0] + "shape.concat")
concat_shape_node = helper.make_node( concat_shape_node = helper.make_node(
"Concat", "Concat",
inputs=op.input('SizeTensor'), inputs=op.input('SizeTensor'),
...@@ -645,27 +528,46 @@ class PaddleOpMapper(object): ...@@ -645,27 +528,46 @@ class PaddleOpMapper(object):
outputs=[cast_shape_name], outputs=[cast_shape_name],
to=onnx_pb.TensorProto.INT64) to=onnx_pb.TensorProto.INT64)
node_list.extend([concat_shape_node, cast_shape_node]) node_list.extend([concat_shape_node, cast_shape_node])
shape_name3 = self.get_name(op.type, "shape.concat") shape_name2 = self.get_name(op.type, "shape.concat")
shape_node3 = helper.make_node( shape_node2 = helper.make_node(
'Concat', 'Concat',
inputs=[shape_name1, cast_shape_name], inputs=[shape_name1, cast_shape_name],
outputs=[shape_name3], outputs=[shape_name2],
axis=0) axis=0)
node_list.append(shape_node2)
cast_shape_name2 = self.get_name(op.type, "shape.cast")
cast_shape_node2 = helper.make_node(
'Cast',
inputs=[shape_name2],
outputs=[cast_shape_name2],
to=onnx_pb.TensorProto.FLOAT)
node_list.append(cast_shape_node2)
cast_shape_name0 = self.get_name(op.type, "shape.cast")
cast_shape_node0 = helper.make_node(
'Cast',
inputs=[shape_name0],
outputs=[cast_shape_name0],
to=onnx_pb.TensorProto.FLOAT)
node_list.append(cast_shape_node0)
outputs_h_w_scales = op.output('Out')[0] + "@out_hw_scales"
node_h_w_scales = helper.make_node(
'Div',
inputs=[cast_shape_name2, cast_shape_name0],
outputs=[outputs_h_w_scales])
node_list.append(node_h_w_scales)
result_node = helper.make_node( result_node = helper.make_node(
'Resize', 'Resize',
inputs=[op.input('X')[0], roi_name, empty_name, shape_name3], inputs=[op.input('X')[0], outputs_h_w_scales],
outputs=op.output('Out'), outputs=op.output('Out'),
mode='linear', mode='linear')
coordinate_transformation_mode=coordinate_transformation_mode) node_list.extend([result_node])
node_list.extend([shape_node3, result_node])
return node_list return node_list
elif 'Scale' in input_names and len(op.input('Scale')) > 0: elif 'Scale' in input_names and len(op.input('Scale')) > 0:
node = helper.make_node( node = helper.make_node(
'Resize', 'Resize',
inputs=[op.input('X')[0], op.input('Scale')[0]], inputs=[op.input('X')[0], op.input('Scale')[0]],
outputs=op.output('Out'), outputs=op.output('Out'),
mode='linear', mode='linear')
coordinate_transformation_mode=coordinate_transformation_mode)
else: else:
out_shape = [op.attr('out_h'), op.attr('out_w')] out_shape = [op.attr('out_h'), op.attr('out_w')]
scale = op.attr('scale') scale = op.attr('scale')
...@@ -674,41 +576,34 @@ class PaddleOpMapper(object): ...@@ -674,41 +576,34 @@ class PaddleOpMapper(object):
scale_node = self.make_constant_node(scale_name, scale_node = self.make_constant_node(scale_name,
onnx_pb.TensorProto.FLOAT, onnx_pb.TensorProto.FLOAT,
[1, 1, scale, scale]) [1, 1, scale, scale])
roi_name = self.get_name(op.type, 'roi')
roi_node = self.make_constant_node(roi_name,
onnx_pb.TensorProto.FLOAT,
[1, 1, 1, 1, 1, 1, 1, 1])
node = helper.make_node( node = helper.make_node(
'Resize', 'Resize',
inputs=[op.input('X')[0], roi_name, scale_name], inputs=[op.input('X')[0], scale_name],
outputs=op.output('Out'), outputs=op.output('Out'),
mode='nearest', mode='linear')
coordinate_transformation_mode=coordinate_transformation_mode return [scale_node, node]
)
return [scale_node, roi_node, node]
else: else:
raise Exception("Unexpected situation happend") raise Exception("Unexpected situation happend")
return node return node
def nearest_interp(self, op, block): def nearest_interp(self, op, block):
input_names = op.input_names input_names = op.input_names
coordinate_transformation_mode = 'half_pixel'
if op.attr('align_corners'): if op.attr('align_corners'):
coordinate_transformation_mode = 'align_corners' raise Exception(
"Resize in onnx(opset<=10) only support coordinate_transformation_mode: 'asymmetric'."
)
if 'OutSize' in input_names and len(op.input('OutSize')) > 0: if 'OutSize' in input_names and len(op.input('OutSize')) > 0:
node = helper.make_node( node = helper.make_node(
'Resize', 'Resize',
inputs=[op.input('X')[0], '', op.input('OutSize')[0]], inputs=[op.input('X')[0], op.input('OutSize')[0]],
outputs=op.output('Out'), outputs=op.output('Out'),
mode='nearest', mode='nearest')
coordinate_transformation_mode=coordinate_transformation_mode)
elif 'Scale' in input_names and len(op.input('Scale')) > 0: elif 'Scale' in input_names and len(op.input('Scale')) > 0:
node = helper.make_node( node = helper.make_node(
'Resize', 'Resize',
inputs=[op.input('X')[0], op.input('Scale')[0]], inputs=[op.input('X')[0], op.input('Scale')[0]],
outputs=op.output('Out'), outputs=op.output('Out'),
mode='nearest', mode='nearest')
coordinate_transformation_mode=coordinate_transformation_mode)
else: else:
out_shape = [op.attr('out_h'), op.attr('out_w')] out_shape = [op.attr('out_h'), op.attr('out_w')]
scale = op.attr('scale') scale = op.attr('scale')
...@@ -717,18 +612,12 @@ class PaddleOpMapper(object): ...@@ -717,18 +612,12 @@ class PaddleOpMapper(object):
scale_node = self.make_constant_node(scale_name, scale_node = self.make_constant_node(scale_name,
onnx_pb.TensorProto.FLOAT, onnx_pb.TensorProto.FLOAT,
[1, 1, scale, scale]) [1, 1, scale, scale])
roi_name = self.get_name(op.type, 'roi')
roi_node = self.make_constant_node(roi_name,
onnx_pb.TensorProto.FLOAT,
[1, 1, 1, 1, 1, 1, 1, 1])
node = helper.make_node( node = helper.make_node(
'Resize', 'Resize',
inputs=[op.input('X')[0], roi_name, scale_name], inputs=[op.input('X')[0], scale_name],
outputs=op.output('Out'), outputs=op.output('Out'),
mode='nearest', mode='nearest')
coordinate_transformation_mode=coordinate_transformation_mode return [scale_node, node]
)
return [scale_node, roi_node, node]
else: else:
raise Exception("Unexpected situation happend") raise Exception("Unexpected situation happend")
return node return node
...@@ -745,14 +634,8 @@ class PaddleOpMapper(object): ...@@ -745,14 +634,8 @@ class PaddleOpMapper(object):
return node return node
def hard_swish(self, op, block): def hard_swish(self, op, block):
min_name = self.get_name(op.type, 'min')
max_name = self.get_name(op.type, 'max')
scale_name = self.get_name(op.type, 'scale') scale_name = self.get_name(op.type, 'scale')
offset_name = self.get_name(op.type, 'offset') offset_name = self.get_name(op.type, 'offset')
min_node = self.make_constant_node(min_name, onnx_pb.TensorProto.FLOAT,
0)
max_node = self.make_constant_node(max_name, onnx_pb.TensorProto.FLOAT,
op.attr('threshold'))
scale_node = self.make_constant_node(scale_name, scale_node = self.make_constant_node(scale_name,
onnx_pb.TensorProto.FLOAT, onnx_pb.TensorProto.FLOAT,
op.attr('scale')) op.attr('scale'))
...@@ -764,19 +647,20 @@ class PaddleOpMapper(object): ...@@ -764,19 +647,20 @@ class PaddleOpMapper(object):
node0 = helper.make_node( node0 = helper.make_node(
'Add', inputs=[op.input('X')[0], offset_name], outputs=[name0]) 'Add', inputs=[op.input('X')[0], offset_name], outputs=[name0])
name1 = self.get_name(op.type, 'relu') name1 = self.get_name(op.type, 'relu')
min_value = op.attr('min')
max_value = op.attr('max')
node1 = helper.make_node( node1 = helper.make_node(
'Clip', 'Clip',
inputs=[name0, min_name, max_name], inputs=[name0],
outputs=[name1], ) outputs=[name1],
max=max_value,
min=min_value)
name2 = self.get_name(op.type, 'mul') name2 = self.get_name(op.type, 'mul')
node2 = helper.make_node( node2 = helper.make_node(
'Mul', inputs=[op.input('X')[0], name1], outputs=[name2]) 'Mul', inputs=[op.input('X')[0], name1], outputs=[name2])
node3 = helper.make_node( node3 = helper.make_node(
'Div', inputs=[name2, scale_name], outputs=op.output('Out')) 'Div', inputs=[name2, scale_name], outputs=op.output('Out'))
return [ return [scale_node, offset_node, node0, node1, node2, node3]
min_node, max_node, scale_node, offset_node, node0, node1, node2,
node3
]
def elementwise_mul(self, op, block): def elementwise_mul(self, op, block):
axis = op.attr('axis') axis = op.attr('axis')
...@@ -852,3 +736,11 @@ class PaddleOpMapper(object): ...@@ -852,3 +736,11 @@ class PaddleOpMapper(object):
def im2sequence(self, op, block): def im2sequence(self, op, block):
from .paddle_custom_layer.im2sequence import im2sequence from .paddle_custom_layer.im2sequence import im2sequence
return im2sequence(op, block) return im2sequence(op, block)
def yolo_box(self, op, block):
from .paddle_custom_layer.yolo_box import yolo_box
return yolo_box(op, block)
def multiclass_nms(self, op, block):
from .paddle_custom_layer.multiclass_nms import multiclass_nms
return multiclass_nms(op, block)
# Copyright (c) 2019 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 math
import sys
import os
import numpy as np
import paddle.fluid.core as core
import paddle.fluid as fluid
import onnx
import warnings
from onnx import helper, onnx_pb
def multiclass_nms(op, block):
"""
Convert the paddle multiclass_nms to onnx op.
This op is get the select boxes from origin boxes.
"""
inputs = dict()
outputs = dict()
attrs = dict()
for name in op.input_names:
inputs[name] = op.input(name)
for name in op.output_names:
outputs[name] = op.output(name)
for name in op.attr_names:
attrs[name] = op.attr(name)
result_name = outputs['Out'][0]
background = attrs['background_label']
normalized = attrs['normalized']
if normalized == False:
warnings.warn(
'The parameter normalized of multiclass_nms OP of Paddle is False, which has diff with ONNX. \
Please set normalized=True in multiclass_nms of Paddle')
#convert the paddle attribute to onnx tensor
name_score_threshold = [outputs['Out'][0] + "@score_threshold"]
name_iou_threshold = [outputs['Out'][0] + "@iou_threshold"]
name_keep_top_k = [outputs['Out'][0] + '@keep_top_k']
name_keep_top_k_2D = [outputs['Out'][0] + '@keep_top_k_1D']
node_score_threshold = onnx.helper.make_node(
'Constant',
inputs=[],
outputs=name_score_threshold,
value=onnx.helper.make_tensor(
name=name_score_threshold[0] + "@const",
data_type=onnx.TensorProto.FLOAT,
dims=(),
vals=[float(attrs['score_threshold'])]))
node_iou_threshold = onnx.helper.make_node(
'Constant',
inputs=[],
outputs=name_iou_threshold,
value=onnx.helper.make_tensor(
name=name_iou_threshold[0] + "@const",
data_type=onnx.TensorProto.FLOAT,
dims=(),
vals=[float(attrs['nms_threshold'])]))
node_keep_top_k = onnx.helper.make_node(
'Constant',
inputs=[],
outputs=name_keep_top_k,
value=onnx.helper.make_tensor(
name=name_keep_top_k[0] + "@const",
data_type=onnx.TensorProto.INT64,
dims=(),
vals=[np.int64(attrs['keep_top_k'])]))
node_keep_top_k_2D = onnx.helper.make_node(
'Constant',
inputs=[],
outputs=name_keep_top_k_2D,
value=onnx.helper.make_tensor(
name=name_keep_top_k_2D[0] + "@const",
data_type=onnx.TensorProto.INT64,
dims=[1, 1],
vals=[np.int64(attrs['keep_top_k'])]))
# the paddle data format is x1,y1,x2,y2
kwargs = {'center_point_box': 0}
name_select_nms = [outputs['Out'][0] + "@select_index"]
node_select_nms= onnx.helper.make_node(
'NonMaxSuppression',
inputs=inputs['BBoxes'] + inputs['Scores'] + name_keep_top_k +\
name_iou_threshold + name_score_threshold,
outputs=name_select_nms)
# step 1 nodes select the nms class
node_list = [
node_score_threshold, node_iou_threshold, node_keep_top_k,
node_keep_top_k_2D, node_select_nms
]
# create some const value to use
name_const_value = [result_name+"@const_0",
result_name+"@const_1",\
result_name+"@const_2",\
result_name+"@const_-1"]
value_const_value = [0, 1, 2, -1]
for name, value in zip(name_const_value, value_const_value):
node = onnx.helper.make_node(
'Constant',
inputs=[],
outputs=[name],
value=onnx.helper.make_tensor(
name=name + "@const",
data_type=onnx.TensorProto.INT64,
dims=[1],
vals=[value]))
node_list.append(node)
# In this code block, we will deocde the raw score data, reshape N * C * M to 1 * N*C*M
# and the same time, decode the select indices to 1 * D, gather the select_indices
outputs_gather_1 = [result_name + "@gather_1"]
node_gather_1 = onnx.helper.make_node(
'Gather',
inputs=name_select_nms + [result_name + "@const_1"],
outputs=outputs_gather_1,
axis=1)
node_list.append(node_gather_1)
outputs_squeeze_gather_1 = [result_name + "@sequeeze_gather_1"]
node_squeeze_gather_1 = onnx.helper.make_node(
'Squeeze',
inputs=outputs_gather_1,
outputs=outputs_squeeze_gather_1,
axes=[1])
node_list.append(node_squeeze_gather_1)
outputs_gather_2 = [result_name + "@gather_2"]
node_gather_2 = onnx.helper.make_node(
'Gather',
inputs=name_select_nms + [result_name + "@const_2"],
outputs=outputs_gather_2,
axis=1)
node_list.append(node_gather_2)
#slice the class is not 0
if background == 0:
outputs_nonzero = [result_name + "@nonzero"]
node_nonzero = onnx.helper.make_node(
'NonZero', inputs=outputs_squeeze_gather_1, outputs=outputs_nonzero)
node_list.append(node_nonzero)
else:
name_thresh = [result_name + "@thresh"]
node_thresh = onnx.helper.make_node(
'Constant',
inputs=[],
outputs=name_thresh,
value=onnx.helper.make_tensor(
name=name_thresh[0] + "@const",
data_type=onnx.TensorProto.INT32,
dims=[1],
vals=[-1]))
node_list.append(node_thresh)
outputs_cast = [result_name + "@cast"]
node_cast = onnx.helper.make_node(
'Cast', inputs=outputs_squeeze_gather_1, outputs=outputs_cast, to=6)
node_list.append(node_cast)
outputs_greater = [result_name + "@greater"]
node_greater = onnx.helper.make_node(
'Greater',
inputs=outputs_cast + name_thresh,
outputs=outputs_greater)
node_list.append(node_greater)
outputs_nonzero = [result_name + "@nonzero"]
node_nonzero = onnx.helper.make_node(
'NonZero', inputs=outputs_greater, outputs=outputs_nonzero)
node_list.append(node_nonzero)
outputs_gather_1_nonzero = [result_name + "@gather_1_nonzero"]
node_gather_1_nonzero = onnx.helper.make_node(
'Gather',
inputs=outputs_gather_1 + outputs_nonzero,
outputs=outputs_gather_1_nonzero,
axis=0)
node_list.append(node_gather_1_nonzero)
outputs_gather_2_nonzero = [result_name + "@gather_2_nonzero"]
node_gather_2_nonzero = onnx.helper.make_node(
'Gather',
inputs=outputs_gather_2 + outputs_nonzero,
outputs=outputs_gather_2_nonzero,
axis=0)
node_list.append(node_gather_2_nonzero)
# reshape scores N * C * M to (N*C*M) * 1
outputs_reshape_scores_rank1 = [result_name + "@reshape_scores_rank1"]
node_reshape_scores_rank1 = onnx.helper.make_node(
"Reshape",
inputs=inputs['Scores'] + [result_name + "@const_-1"],
outputs=outputs_reshape_scores_rank1)
node_list.append(node_reshape_scores_rank1)
# get the shape of scores
outputs_shape_scores = [result_name + "@shape_scores"]
node_shape_scores = onnx.helper.make_node(
'Shape', inputs=inputs['Scores'], outputs=outputs_shape_scores)
node_list.append(node_shape_scores)
# gather the index: 2 shape of scores
outputs_gather_scores_dim1 = [result_name + "@gather_scores_dim1"]
node_gather_scores_dim1 = onnx.helper.make_node(
'Gather',
inputs=outputs_shape_scores + [result_name + "@const_2"],
outputs=outputs_gather_scores_dim1,
axis=0)
node_list.append(node_gather_scores_dim1)
# mul class * M
outputs_mul_classnum_boxnum = [result_name + "@mul_classnum_boxnum"]
node_mul_classnum_boxnum = onnx.helper.make_node(
'Mul',
inputs=outputs_gather_1_nonzero + outputs_gather_scores_dim1,
outputs=outputs_mul_classnum_boxnum)
node_list.append(node_mul_classnum_boxnum)
# add class * M * index
outputs_add_class_M_index = [result_name + "@add_class_M_index"]
node_add_class_M_index = onnx.helper.make_node(
'Add',
inputs=outputs_mul_classnum_boxnum + outputs_gather_2_nonzero,
outputs=outputs_add_class_M_index)
node_list.append(node_add_class_M_index)
# Squeeze the indices to 1 dim
outputs_squeeze_select_index = [result_name + "@squeeze_select_index"]
node_squeeze_select_index = onnx.helper.make_node(
'Squeeze',
inputs=outputs_add_class_M_index,
outputs=outputs_squeeze_select_index,
axes=[0, 2])
node_list.append(node_squeeze_select_index)
# gather the data from flatten scores
outputs_gather_select_scores = [result_name + "@gather_select_scores"]
node_gather_select_scores = onnx.helper.make_node('Gather',
inputs=outputs_reshape_scores_rank1 + \
outputs_squeeze_select_index,
outputs=outputs_gather_select_scores,
axis=0)
node_list.append(node_gather_select_scores)
# get nums to input TopK
outputs_shape_select_num = [result_name + "@shape_select_num"]
node_shape_select_num = onnx.helper.make_node(
'Shape',
inputs=outputs_gather_select_scores,
outputs=outputs_shape_select_num)
node_list.append(node_shape_select_num)
outputs_gather_select_num = [result_name + "@gather_select_num"]
node_gather_select_num = onnx.helper.make_node(
'Gather',
inputs=outputs_shape_select_num + [result_name + "@const_0"],
outputs=outputs_gather_select_num,
axis=0)
node_list.append(node_gather_select_num)
outputs_unsqueeze_select_num = [result_name + "@unsqueeze_select_num"]
node_unsqueeze_select_num = onnx.helper.make_node(
'Unsqueeze',
inputs=outputs_gather_select_num,
outputs=outputs_unsqueeze_select_num,
axes=[0])
node_list.append(node_unsqueeze_select_num)
outputs_concat_topK_select_num = [result_name + "@conat_topK_select_num"]
node_conat_topK_select_num = onnx.helper.make_node(
'Concat',
inputs=outputs_unsqueeze_select_num + name_keep_top_k_2D,
outputs=outputs_concat_topK_select_num,
axis=0)
node_list.append(node_conat_topK_select_num)
outputs_cast_concat_topK_select_num = [
result_name + "@concat_topK_select_num"
]
node_outputs_cast_concat_topK_select_num = onnx.helper.make_node(
'Cast',
inputs=outputs_concat_topK_select_num,
outputs=outputs_cast_concat_topK_select_num,
to=6)
node_list.append(node_outputs_cast_concat_topK_select_num)
# get min(topK, num_select)
outputs_compare_topk_num_select = [result_name + "@compare_topk_num_select"]
node_compare_topk_num_select = onnx.helper.make_node(
'ReduceMin',
inputs=outputs_cast_concat_topK_select_num,
outputs=outputs_compare_topk_num_select,
keepdims=0)
node_list.append(node_compare_topk_num_select)
# unsqueeze the indices to 1D tensor
outputs_unsqueeze_topk_select_indices = [
result_name + "@unsqueeze_topk_select_indices"
]
node_unsqueeze_topk_select_indices = onnx.helper.make_node(
'Unsqueeze',
inputs=outputs_compare_topk_num_select,
outputs=outputs_unsqueeze_topk_select_indices,
axes=[0])
node_list.append(node_unsqueeze_topk_select_indices)
# cast the indices to INT64
outputs_cast_topk_indices = [result_name + "@cast_topk_indices"]
node_cast_topk_indices = onnx.helper.make_node(
'Cast',
inputs=outputs_unsqueeze_topk_select_indices,
outputs=outputs_cast_topk_indices,
to=7)
node_list.append(node_cast_topk_indices)
# select topk scores indices
outputs_topk_select_topk_indices = [result_name + "@topk_select_topk_values",\
result_name + "@topk_select_topk_indices"]
node_topk_select_topk_indices = onnx.helper.make_node(
'TopK',
inputs=outputs_gather_select_scores + outputs_cast_topk_indices,
outputs=outputs_topk_select_topk_indices)
node_list.append(node_topk_select_topk_indices)
# gather topk label, scores, boxes
outputs_gather_topk_scores = [result_name + "@gather_topk_scores"]
node_gather_topk_scores = onnx.helper.make_node(
'Gather',
inputs=outputs_gather_select_scores +
[outputs_topk_select_topk_indices[1]],
outputs=outputs_gather_topk_scores,
axis=0)
node_list.append(node_gather_topk_scores)
outputs_gather_topk_class = [result_name + "@gather_topk_class"]
node_gather_topk_class = onnx.helper.make_node(
'Gather',
inputs=outputs_gather_1_nonzero +
[outputs_topk_select_topk_indices[1]],
outputs=outputs_gather_topk_class,
axis=1)
node_list.append(node_gather_topk_class)
# gather the boxes need to gather the boxes id, then get boxes
outputs_gather_topk_boxes_id = [result_name + "@gather_topk_boxes_id"]
node_gather_topk_boxes_id = onnx.helper.make_node(
'Gather',
inputs=outputs_gather_2_nonzero +
[outputs_topk_select_topk_indices[1]],
outputs=outputs_gather_topk_boxes_id,
axis=1)
node_list.append(node_gather_topk_boxes_id)
# squeeze the gather_topk_boxes_id to 1 dim
outputs_squeeze_topk_boxes_id = [result_name + "@squeeze_topk_boxes_id"]
node_squeeze_topk_boxes_id = onnx.helper.make_node(
'Squeeze',
inputs=outputs_gather_topk_boxes_id,
outputs=outputs_squeeze_topk_boxes_id,
axes=[0, 2])
node_list.append(node_squeeze_topk_boxes_id)
outputs_gather_select_boxes = [result_name + "@gather_select_boxes"]
node_gather_select_boxes = onnx.helper.make_node(
'Gather',
inputs=inputs['BBoxes'] + outputs_squeeze_topk_boxes_id,
outputs=outputs_gather_select_boxes,
axis=1)
node_list.append(node_gather_select_boxes)
# concat the final result
# before concat need to cast the class to float
outputs_cast_topk_class = [result_name + "@cast_topk_class"]
node_cast_topk_class = onnx.helper.make_node(
'Cast',
inputs=outputs_gather_topk_class,
outputs=outputs_cast_topk_class,
to=1)
node_list.append(node_cast_topk_class)
outputs_unsqueeze_topk_scores = [result_name + "@unsqueeze_topk_scores"]
node_unsqueeze_topk_scores = onnx.helper.make_node(
'Unsqueeze',
inputs=outputs_gather_topk_scores,
outputs=outputs_unsqueeze_topk_scores,
axes=[0, 2])
node_list.append(node_unsqueeze_topk_scores)
inputs_concat_final_results = outputs_cast_topk_class + outputs_unsqueeze_topk_scores +\
outputs_gather_select_boxes
outputs_sort_by_socre_results = [result_name + "@concat_topk_scores"]
node_sort_by_socre_results = onnx.helper.make_node(
'Concat',
inputs=inputs_concat_final_results,
outputs=outputs_sort_by_socre_results,
axis=2)
node_list.append(node_sort_by_socre_results)
# select topk classes indices
outputs_squeeze_cast_topk_class = [result_name + "@squeeze_cast_topk_class"]
node_squeeze_cast_topk_class = onnx.helper.make_node(
'Squeeze',
inputs=outputs_cast_topk_class,
outputs=outputs_squeeze_cast_topk_class,
axes=[0, 2])
node_list.append(node_squeeze_cast_topk_class)
outputs_neg_squeeze_cast_topk_class = [
result_name + "@neg_squeeze_cast_topk_class"
]
node_neg_squeeze_cast_topk_class = onnx.helper.make_node(
'Neg',
inputs=outputs_squeeze_cast_topk_class,
outputs=outputs_neg_squeeze_cast_topk_class)
node_list.append(node_neg_squeeze_cast_topk_class)
outputs_topk_select_classes_indices = [result_name + "@topk_select_topk_classes_scores",\
result_name + "@topk_select_topk_classes_indices"]
node_topk_select_topk_indices = onnx.helper.make_node(
'TopK',
inputs=outputs_neg_squeeze_cast_topk_class + outputs_cast_topk_indices,
outputs=outputs_topk_select_classes_indices)
node_list.append(node_topk_select_topk_indices)
outputs_concat_final_results = outputs['Out']
node_concat_final_results = onnx.helper.make_node(
'Gather',
inputs=outputs_sort_by_socre_results +
[outputs_topk_select_classes_indices[1]],
outputs=outputs_concat_final_results,
axis=1)
node_list.append(node_concat_final_results)
return node_list
# Copyright (c) 2019 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 math
import sys
import x2paddle
import os
import numpy as np
import paddle.fluid.core as core
import paddle.fluid as fluid
import onnx
from onnx import helper, onnx_pb
from x2paddle.op_mapper.paddle2onnx.opset9.opset import OpSet9
from x2paddle.op_mapper.paddle2onnx.opset10.opset import OpSet10
from x2paddle.op_mapper.paddle2onnx.opset11.opset import OpSet11
class PaddleOpMapper(object):
def __init__(self):
self.support_opsets = [9, 10, 11]
self.default_opset = 10
self.name_counter = dict()
self.op_set = None
def convert(self, program, save_dir, opset_number=10):
self.op_set = self.create_opset(opset_number)
weight_nodes = self.op_set.convert_weights(program)
op_nodes = list()
input_nodes = list()
output_nodes = list()
unsupported_ops = set()
print("Translating PaddlePaddle to ONNX...\n")
for block in program.blocks:
for i, op in enumerate(block.ops):
sys.stdout.write("\rTotal:{}, Current:{} : {} ".format(
len(block.ops), i + 1, op.type))
sys.stdout.flush()
if not hasattr(self.op_set, op.type):
unsupported_ops.add(op.type)
continue
if len(unsupported_ops) > 0:
continue
node = getattr(self.op_set, op.type)(op, block)
if op.type == 'feed':
print(node.name)
input_nodes.append(node)
elif op.type == 'fetch':
output_nodes.append(node)
else:
if isinstance(node, list):
op_nodes = op_nodes + node
else:
op_nodes.append(node)
if len(unsupported_ops) > 0:
print("\nThere's {} ops are not supported yet".format(
len(unsupported_ops)))
for op in unsupported_ops:
print("=========== {} ===========".format(op))
return
graph = helper.make_graph(
nodes=weight_nodes + op_nodes,
name='onnx_model_from_paddle',
initializer=[],
inputs=input_nodes,
outputs=output_nodes)
opset_imports = [helper.make_opsetid("", opset_number)]
model = helper.make_model(
graph, producer_name='X2Paddle', opset_imports=opset_imports)
onnx.checker.check_model(model)
if not os.path.isdir(save_dir):
os.makedirs(save_dir)
with open(os.path.join(save_dir, 'x2paddle_model.onnx'), 'wb') as f:
f.write(model.SerializeToString())
print("\nTranslated model saved in {}".format(
os.path.join(save_dir, 'x2paddle_model.onnx')))
def create_opset(self, opset_number):
run_opset = self.default_opset
opset = ''
if opset_number in self.support_opsets:
run_opset = opset_number
else:
for support_opset_number in self.support_opsets:
if support_opset_number < opset_number:
run_opset = support_opset_number
else:
break
print(
'Now, onnx2paddle support convert onnx model opset_verison {},'
'opset_verison of your onnx model is {}, automatically treated as op_set: {}.'
.format(self.support_opsets, opset_number, run_opset))
opset = 'OpSet' + str(run_opset)
return eval(opset)()
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册