提交 bd84c83f 编写于 作者: C Channingss

[onnx]support paddle1.8, remove onnxruntime

上级 164d8ce5
- repo: local - repo: https://github.com/PaddlePaddle/mirrors-yapf.git
sha: 0d79c0c469bab64f7229c9aca2b1186ef47f0e37
hooks: hooks:
- id: yapf - id: yapf
name: yapf
entry: yapf
language: system
args: [-i, --style .style.yapf]
files: \.py$ files: \.py$
- repo: https://github.com/pre-commit/pre-commit-hooks - repo: https://github.com/pre-commit/pre-commit-hooks
sha: a11d9314b22d8f8c7556443875b731ef05965464 sha: a11d9314b22d8f8c7556443875b731ef05965464
hooks: hooks:
...@@ -18,6 +14,7 @@ ...@@ -18,6 +14,7 @@
- id: check-symlinks - id: check-symlinks
- id: check-added-large-files - id: check-added-large-files
- repo: local - repo: local
hooks: hooks:
- id: copyright_checker - id: copyright_checker
name: copyright_checker name: copyright_checker
......
language: python language: python
python: python:
- '2.7'
- '3.5' - '3.5'
- '3.6'
script: script:
- if [[ $TRAVIS_PYTHON_VERSION != 2.7 ]]; then /bin/bash ./tools/check_code_style.sh; fi - if [[ $TRAVIS_PYTHON_VERSION != 2.7 ]]; then /bin/bash ./tools/check_code_style.sh; fi
......
...@@ -11,3 +11,6 @@ x2paddle -f tensorflow -m tf.pb -s pd-model --without_data_format_optimization - ...@@ -11,3 +11,6 @@ x2paddle -f tensorflow -m tf.pb -s pd-model --without_data_format_optimization -
``` ```
> 1. 目前Tensorflow的CV模型大部分均为`NHWC`的输入格式,而Paddle的默认输入格式为`NCHW`,因此X2Paddle在转换过程中,会对如`axis`, `shape`等参数进行转换,适应Paddle的NCHW格式。但在这种情况下,可能会由于TensorFlow模型太复杂,导致出错。 指定`--without_data_format_optimization`后,会停止对`axis`,`shape`等参数的优化(这可能会带来一定数量的transpose操作) > 1. 目前Tensorflow的CV模型大部分均为`NHWC`的输入格式,而Paddle的默认输入格式为`NCHW`,因此X2Paddle在转换过程中,会对如`axis`, `shape`等参数进行转换,适应Paddle的NCHW格式。但在这种情况下,可能会由于TensorFlow模型太复杂,导致出错。 指定`--without_data_format_optimization`后,会停止对`axis`,`shape`等参数的优化(这可能会带来一定数量的transpose操作)
**Q3. ONNX模型转换过程中,提示『Unknown shape for input tensor[tensor name: "input"] -> shape: ['batch', 'sequence'], Please define shape of input here』**
A:该提示信息表示从ONNX的模型中获取到输入tensor(tensor名为"input:)的shape是语义象征性的['batch', 'sequence'],而不是dim为int类型的shape,从而可能会因为部分node的shape无法推理,导致转换失败。所以用户可以尝试手动在提示后输入详细的shape信息,如:-1,3,224,224 其中-1表示Batch
...@@ -10,12 +10,12 @@ X2Paddle在多个主流的CV模型上,测试过TensorFlow/Caffe/ONNX模型的 ...@@ -10,12 +10,12 @@ X2Paddle在多个主流的CV模型上,测试过TensorFlow/Caffe/ONNX模型的
## 环境依赖 ## 环境依赖
python == 2.7 | python >= 3.5 python == 2.7 | python >= 3.5
paddlepaddle >= 1.6.0 paddlepaddle >= 1.8.0
**按需安装以下依赖** **按需安装以下依赖**
tensorflow : tensorflow == 1.14.0 tensorflow : tensorflow == 1.14.0
caffe : 无 caffe : 无
onnx : onnx == 1.6.0 onnxruntime == 1.0.0 onnx : onnx == 1.6.0
## 安装 ## 安装
### 安装方式一(推荐) ### 安装方式一(推荐)
...@@ -44,10 +44,15 @@ x2paddle --framework=caffe --prototxt=deploy.prototxt --weight=deploy.caffemodel ...@@ -44,10 +44,15 @@ x2paddle --framework=caffe --prototxt=deploy.prototxt --weight=deploy.caffemodel
``` ```
x2paddle --framework=onnx --model=onnx_model.onnx --save_dir=pd_model x2paddle --framework=onnx --model=onnx_model.onnx --save_dir=pd_model
``` ```
### Paddle2ONNX
```
# 注意:paddle_infer_model_dir下需包含__model__和__params__两个文件
x2paddle --framework=paddle2onnx --model=paddle_infer_model_dir --save_dir=onnx_model
```
### 参数选项 ### 参数选项
| 参数 | | | 参数 | |
|----------|--------------| |----------|--------------|
|--framework | 源模型类型 (tensorflow、caffe、onnx) | |--framework | 源模型类型 (tensorflow、caffe、onnx、paddle2onnx) |
|--prototxt | 当framework为caffe时,该参数指定caffe模型的proto文件路径 | |--prototxt | 当framework为caffe时,该参数指定caffe模型的proto文件路径 |
|--weight | 当framework为caffe时,该参数指定caffe模型的参数文件路径 | |--weight | 当framework为caffe时,该参数指定caffe模型的参数文件路径 |
|--save_dir | 指定转换后的模型保存目录路径 | |--save_dir | 指定转换后的模型保存目录路径 |
...@@ -58,6 +63,7 @@ x2paddle --framework=onnx --model=onnx_model.onnx --save_dir=pd_model ...@@ -58,6 +63,7 @@ x2paddle --framework=onnx --model=onnx_model.onnx --save_dir=pd_model
|--params_merge | **[可选]** 当指定该参数时,转换完成后,inference_model中的所有模型参数将合并保存为一个文件__params__ | |--params_merge | **[可选]** 当指定该参数时,转换完成后,inference_model中的所有模型参数将合并保存为一个文件__params__ |
## 使用转换后的模型 ## 使用转换后的模型
转换后的模型包括`model_with_code``inference_model`两个目录。 转换后的模型包括`model_with_code``inference_model`两个目录。
`model_with_code`中保存了模型参数,和转换后的python模型代码 `model_with_code`中保存了模型参数,和转换后的python模型代码
......
...@@ -11,8 +11,7 @@ setuptools.setup( ...@@ -11,8 +11,7 @@ setuptools.setup(
version=x2paddle.__version__, version=x2paddle.__version__,
author="dltp-sz", author="dltp-sz",
author_email="dltp-sz@baidu.com", author_email="dltp-sz@baidu.com",
description= description="a toolkit for converting trained model to PaddlePaddle from other deep learning frameworks.",
"a toolkit for converting trained model to PaddlePaddle from other deep learning frameworks.",
long_description=long_description, long_description=long_description,
long_description_content_type="text/plain", long_description_content_type="text/plain",
url="https://github.com/PaddlePaddle/x2paddle", url="https://github.com/PaddlePaddle/x2paddle",
...@@ -23,6 +22,4 @@ setuptools.setup( ...@@ -23,6 +22,4 @@ setuptools.setup(
"Operating System :: OS Independent", "Operating System :: OS Independent",
], ],
license='Apache 2.0', license='Apache 2.0',
entry_points={'console_scripts': [ entry_points={'console_scripts': ['x2paddle=x2paddle.convert:main', ]})
'x2paddle=x2paddle.convert:main',
]})
...@@ -5,12 +5,14 @@ model_dir = sys.argv[1] ...@@ -5,12 +5,14 @@ model_dir = sys.argv[1]
new_model_dir = sys.argv[2] new_model_dir = sys.argv[2]
exe = fluid.Executor(fluid.CPUPlace()) exe = fluid.Executor(fluid.CPUPlace())
[inference_program, feed_target_names, [inference_program, feed_target_names,
fetch_targets] = fluid.io.load_inference_model(dirname=model_dir, executor=exe) fetch_targets] = fluid.io.load_inference_model(
dirname=model_dir, executor=exe)
print(feed_target_names) print(feed_target_names)
fluid.io.save_inference_model(dirname=new_model_dir, fluid.io.save_inference_model(
feeded_var_names=feed_target_names, dirname=new_model_dir,
target_vars=fetch_targets, feeded_var_names=feed_target_names,
executor=exe, target_vars=fetch_targets,
main_program=inference_program, executor=exe,
params_filename="__params__") main_program=inference_program,
params_filename="__params__")
__version__ = "0.7.1" __version__ = "0.7.4"
# Copyright (c) 2019 PaddlePaddle Authors. All Rights Reserved. # Copyright (c) 2020 PaddlePaddle Authors. All Rights Reserved.
# #
# Licensed under the Apache License, Version 2.0 (the "License" # Licensed under the Apache License, Version 2.0 (the "License"
# you may not use this file except in compliance with the License. # you may not use this file except in compliance with the License.
...@@ -19,32 +19,37 @@ import sys ...@@ -19,32 +19,37 @@ import sys
def arg_parser(): def arg_parser():
parser = argparse.ArgumentParser() parser = argparse.ArgumentParser()
parser.add_argument("--model", parser.add_argument(
"-m", "--model",
type=_text_type, "-m",
default=None, type=_text_type,
help="define model file path for tensorflow or onnx") default=None,
parser.add_argument("--prototxt", help="define model file path for tensorflow or onnx")
"-p", parser.add_argument(
type=_text_type, "--prototxt",
default=None, "-p",
help="prototxt file of caffe model") type=_text_type,
parser.add_argument("--weight", default=None,
"-w", help="prototxt file of caffe model")
type=_text_type, parser.add_argument(
default=None, "--weight",
help="weight file of caffe model") "-w",
parser.add_argument("--save_dir", type=_text_type,
"-s", default=None,
type=_text_type, help="weight file of caffe model")
default=None, parser.add_argument(
help="path to save translated model") "--save_dir",
"-s",
type=_text_type,
default=None,
help="path to save translated model")
parser.add_argument( parser.add_argument(
"--framework", "--framework",
"-f", "-f",
type=_text_type, type=_text_type,
default=None, default=None,
help="define which deeplearning framework(tensorflow/caffe/onnx)") help="define which deeplearning framework(tensorflow/caffe/onnx/paddle2onnx)"
)
parser.add_argument( parser.add_argument(
"--caffe_proto", "--caffe_proto",
"-c", "-c",
...@@ -52,27 +57,30 @@ def arg_parser(): ...@@ -52,27 +57,30 @@ def arg_parser():
default=None, default=None,
help="optional: the .py file compiled by caffe proto file of caffe model" help="optional: the .py file compiled by caffe proto file of caffe model"
) )
parser.add_argument("--version", parser.add_argument(
"-v", "--version",
action="store_true", "-v",
default=False, action="store_true",
help="get version of x2paddle") default=False,
help="get version of x2paddle")
parser.add_argument( parser.add_argument(
"--without_data_format_optimization", "--without_data_format_optimization",
"-wo", "-wo",
action="store_true", action="store_true",
default=False, default=False,
help="tf model conversion without data format optimization") help="tf model conversion without data format optimization")
parser.add_argument("--define_input_shape", parser.add_argument(
"-d", "--define_input_shape",
action="store_true", "-d",
default=False, action="store_true",
help="define input shape for tf model") default=False,
parser.add_argument("--params_merge", help="define input shape for tf model")
"-pm", parser.add_argument(
action="store_true", "--params_merge",
default=False, "-pm",
help="define whether merge the params") action="store_true",
default=False,
help="define whether merge the params")
return parser return parser
...@@ -117,7 +125,6 @@ def tf2paddle(model_path, ...@@ -117,7 +125,6 @@ def tf2paddle(model_path,
optimizer.merge_bias() optimizer.merge_bias()
optimizer.optimize_sub_graph() optimizer.optimize_sub_graph()
# optimizer.merge_batch_norm() # optimizer.merge_batch_norm()
# optimizer.merge_prelu() # optimizer.merge_prelu()
else: else:
...@@ -165,16 +172,35 @@ def onnx2paddle(model_path, save_dir, params_merge=False): ...@@ -165,16 +172,35 @@ 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.onnx.onnx_helper import ONNXOpMapperFactory
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
import onnxruntime
model = ONNXDecoder(model_path) model = ONNXDecoder(model_path)
mapper = ONNXOpMapper(model, save_dir) factory = ONNXOpMapperFactory()
mapper = factory.create_onnx_op_mapper(model)
print("Model optimizing ...")
optimizer = ONNXOptimizer(mapper) optimizer = ONNXOptimizer(mapper)
print("Model optimized.")
optimizer.delete_redundance_code() print("Paddle model and code generating ...")
mapper.save_inference_model(save_dir, params_merge) mapper.save_inference_model(save_dir, params_merge)
print("Paddle model and code generated.")
def paddle2onnx(model_path, save_dir):
from x2paddle.decoder.paddle_decoder import PaddleDecoder
from x2paddle.op_mapper.paddle_op_mapper import PaddleOpMapper
model = PaddleDecoder(model_path, '__model__', '__params__')
mapper = PaddleOpMapper()
mapper.convert(model.program, save_dir)
def paddle2onnx(model_path, save_dir):
from x2paddle.decoder.paddle_decoder import PaddleDecoder
from x2paddle.op_mapper.paddle_op_mapper import PaddleOpMapper
model = PaddleDecoder(model_path, '__model__', '__params__')
mapper = PaddleOpMapper()
mapper.convert(model.program, save_dir)
def main(): def main():
...@@ -196,22 +222,13 @@ def main(): ...@@ -196,22 +222,13 @@ def main():
assert args.framework is not None, "--framework is not defined(support tensorflow/caffe/onnx)" assert args.framework is not None, "--framework is not defined(support tensorflow/caffe/onnx)"
assert args.save_dir is not None, "--save_dir is not defined" assert args.save_dir is not None, "--save_dir is not defined"
if args.framework == "onnx":
try:
import onnxruntime as rt
version = rt.__version__
if version != '1.0.0':
print("[ERROR] onnxruntime==1.0.0 is required")
return
except:
print(
"[ERROR] onnxruntime is not installed, use \"pip install onnxruntime==1.0.0\"."
)
try: try:
import paddle import paddle
v0, v1, v2 = paddle.__version__.split('.') v0, v1, v2 = paddle.__version__.split('.')
if int(v0) != 1 or int(v1) < 6: print("paddle.__version__ = {}".format(paddle.__version__))
if v0 == '0' and v1 == '0' and v2 == '0':
print("[WARNING] You are use develop version of paddlepaddle")
elif int(v0) != 1 or int(v1) < 6:
print("[ERROR] paddlepaddle>=1.6.0 is required") print("[ERROR] paddlepaddle>=1.6.0 is required")
return return
except: except:
...@@ -243,11 +260,18 @@ def main(): ...@@ -243,11 +260,18 @@ def main():
elif args.framework == "onnx": elif args.framework == "onnx":
assert args.model is not None, "--model should be defined while translating onnx model" assert args.model is not None, "--model should be defined while translating onnx model"
params_merge = False params_merge = False
if args.params_merge: if args.params_merge:
params_merge = True params_merge = True
onnx2paddle(args.model, args.save_dir, params_merge) onnx2paddle(args.model, args.save_dir, params_merge)
elif args.framework == "paddle2onnx":
assert args.model is not None, "--model should be defined while translating paddle model to onnx"
paddle2onnx(args.model, args.save_dir)
else: else:
raise Exception("--framework only support tensorflow/caffe/onnx now") raise Exception(
"--framework only support tensorflow/caffe/onnx/paddle2onnx now")
if __name__ == "__main__": if __name__ == "__main__":
......
...@@ -46,8 +46,9 @@ class Layer(object): ...@@ -46,8 +46,9 @@ class Layer(object):
for input in self.inputs: for input in self.inputs:
if isinstance(input, GraphNode): if isinstance(input, GraphNode):
if hasattr(input, "index"): if hasattr(input, "index"):
in_list += (input.layer_name + in_list += (
"[{}]".format(input.index) + ", ") input.layer_name + "[{}]".format(input.index) + ", "
)
else: else:
in_list += (input.layer_name + ", ") in_list += (input.layer_name + ", ")
elif isinstance(input, six.string_types): elif isinstance(input, six.string_types):
...@@ -71,8 +72,8 @@ class Layer(object): ...@@ -71,8 +72,8 @@ class Layer(object):
layer_code = layer_code + key + "={}, ".format(input) layer_code = layer_code + key + "={}, ".format(input)
elif isinstance(self.inputs, GraphNode): elif isinstance(self.inputs, GraphNode):
if hasattr(self.inputs, "index"): if hasattr(self.inputs, "index"):
layer_code += (self.inputs.layer_name + layer_code += (
"[{}]".format(self.inputs.index)) self.inputs.layer_name + "[{}]".format(self.inputs.index))
else: else:
layer_code += (self.inputs.layer_name) layer_code += (self.inputs.layer_name)
if self.op != "=": if self.op != "=":
...@@ -88,6 +89,8 @@ class Layer(object): ...@@ -88,6 +89,8 @@ class Layer(object):
for key, value in param_attr.items(): for key, value in param_attr.items():
if '\n' in str(value): if '\n' in str(value):
value = string(str(value).replace('\n', ',')) value = string(str(value).replace('\n', ','))
if str(key) == 'attr':
value = 'ParamAttr(' + str(value) + ')'
layer_code = layer_code + key + "={}, ".format(value) layer_code = layer_code + key + "={}, ".format(value)
layer_code = layer_code.strip(", ") layer_code = layer_code.strip(", ")
......
...@@ -29,11 +29,14 @@ def export_paddle_param(param, param_name, dir): ...@@ -29,11 +29,14 @@ def export_paddle_param(param, param_name, dir):
"bool": [framework_pb2.VarType.BOOL, None] "bool": [framework_pb2.VarType.BOOL, None]
} }
shape = param.shape shape = param.shape
if str(param.dtype) in ['uint8', 'uint_8', 'bool']:
param = param.astype('int64')
if len(shape) == 0: if len(shape) == 0:
assert param.size == 1, "Unexpected situation happend!" assert param.size == 1, "Unexpected situation happend!"
shape = [1] shape = [1]
assert str(param.dtype) in dtype_map, "Unknown dtype of params." assert str(
param.dtype) in dtype_map, "Unknown dtype {} of params: {}.".format(
str(param.dtype), param_name)
fp = open(os.path.join(dir, param_name), 'wb') fp = open(os.path.join(dir, param_name), 'wb')
numpy.array([0], dtype='int32').tofile(fp) numpy.array([0], dtype='int32').tofile(fp)
numpy.array([0], dtype='int64').tofile(fp) numpy.array([0], dtype='int64').tofile(fp)
...@@ -64,10 +67,8 @@ def run_net(param_dir="./"): ...@@ -64,10 +67,8 @@ def run_net(param_dir="./"):
b = os.path.exists(os.path.join(param_dir, var.name)) b = os.path.exists(os.path.join(param_dir, var.name))
return b return b
fluid.io.load_vars(exe, fluid.io.load_vars(
param_dir, exe, param_dir, fluid.default_main_program(), predicate=if_exist)
fluid.default_main_program(),
predicate=if_exist)
class OpMapper(object): class OpMapper(object):
...@@ -98,8 +99,8 @@ class OpMapper(object): ...@@ -98,8 +99,8 @@ class OpMapper(object):
def add_codes(self, codes, indent=0): def add_codes(self, codes, indent=0):
if isinstance(codes, list): if isinstance(codes, list):
for code in codes: for code in codes:
self.paddle_codes += (self.tab * indent + code.strip('\n') + self.paddle_codes += (
'\n') self.tab * indent + code.strip('\n') + '\n')
elif isinstance(codes, str): elif isinstance(codes, str):
self.paddle_codes += (self.tab * indent + codes.strip('\n') + '\n') self.paddle_codes += (self.tab * indent + codes.strip('\n') + '\n')
else: else:
...@@ -135,24 +136,25 @@ class OpMapper(object): ...@@ -135,24 +136,25 @@ class OpMapper(object):
os.path.join(os.path.join(py_code_dir, var.name))) os.path.join(os.path.join(py_code_dir, var.name)))
return b return b
fluid.io.load_vars(exe, fluid.io.load_vars(
py_code_dir, exe,
fluid.default_main_program(), py_code_dir,
predicate=if_exist) fluid.default_main_program(),
predicate=if_exist)
if params_merge: if params_merge:
fluid.io.save_inference_model(dirname=os.path.join( fluid.io.save_inference_model(
save_dir, "inference_model"), dirname=os.path.join(save_dir, "inference_model"),
feeded_var_names=input_names, feeded_var_names=input_names,
target_vars=outputs, target_vars=outputs,
executor=exe, executor=exe,
params_filename="__params__") params_filename="__params__")
else: else:
fluid.io.save_inference_model(dirname=os.path.join( fluid.io.save_inference_model(
save_dir, "inference_model"), dirname=os.path.join(save_dir, "inference_model"),
feeded_var_names=input_names, feeded_var_names=input_names,
target_vars=outputs, target_vars=outputs,
executor=exe, executor=exe,
params_filename=None) params_filename=None)
except: except:
raise Exception( raise Exception(
"Paddle code was saved in {}/model.py, but seems there's wrong exist, please check model.py manually." "Paddle code was saved in {}/model.py, but seems there's wrong exist, please check model.py manually."
......
...@@ -49,13 +49,11 @@ class CaffeResolver(object): ...@@ -49,13 +49,11 @@ class CaffeResolver(object):
class CaffeGraphNode(GraphNode): class CaffeGraphNode(GraphNode):
def __init__(self, layer, type_str, layer_name=None): def __init__(self, layer, type_str, layer_name=None):
if layer_name is None: if layer_name is None:
super(CaffeGraphNode, super(CaffeGraphNode, self).__init__(
self).__init__(layer, layer, layer.name.replace('/', '_').replace('-', '_'))
layer.name.replace('/', '_').replace('-', '_'))
else: else:
super(CaffeGraphNode, super(CaffeGraphNode, self).__init__(
self).__init__(layer, layer, layer_name.replace('/', '_').replace('-', '_'))
layer_name.replace('/', '_').replace('-', '_'))
self.layer_type = type_str self.layer_type = type_str
self.fluid_code = FluidCode() self.fluid_code = FluidCode()
self.data = None self.data = None
...@@ -171,6 +169,14 @@ class CaffeGraph(Graph): ...@@ -171,6 +169,14 @@ class CaffeGraph(Graph):
self.input2layers(input_layers) self.input2layers(input_layers)
self.transform_input_layers(layers, input_layers) self.transform_input_layers(layers, input_layers)
layers = input_layers + layers layers = input_layers + layers
for layer in layers:
if hasattr(layer, 'name'):
name = getattr(layer, 'name')
setattr(layer, 'name', name.replace('/', '_').replace('-', '_'))
for i, name in enumerate(layer.bottom):
layer.bottom[i] = name.replace('/', '_').replace('-', '_')
for i, name in enumerate(layer.top):
layer.top[i] = name.replace('/', '_').replace('-', '_')
top_layer = {} top_layer = {}
for layer in layers: for layer in layers:
...@@ -232,10 +238,12 @@ class CaffeDecoder(object): ...@@ -232,10 +238,12 @@ class CaffeDecoder(object):
def load_using_pb(self): def load_using_pb(self):
data = self.resolver.NetParameter() data = self.resolver.NetParameter()
data.MergeFromString(open(self.model_path, 'rb').read()) data.MergeFromString(open(self.model_path, 'rb').read())
pair = lambda layer: (layer.name, self.normalize_pb_data(layer))
layers = data.layers or data.layer layers = data.layers or data.layer
for layer in layers:
setattr(layer, 'name',
layer.name.replace('/', '_').replace('-', '_'))
pair = lambda layer: (layer.name, self.normalize_pb_data(layer))
self.params = [pair(layer) for layer in layers if layer.blobs] self.params = [pair(layer) for layer in layers if layer.blobs]
def normalize_pb_data(self, layer): def normalize_pb_data(self, layer):
...@@ -246,21 +254,20 @@ class CaffeDecoder(object): ...@@ -246,21 +254,20 @@ class CaffeDecoder(object):
if layer.type == 'PReLU': if layer.type == 'PReLU':
c_o, c_i, h, w = map(int, [1] + \ c_o, c_i, h, w = map(int, [1] + \
list(dims) + [1]* (3 - len(dims))) list(dims) + [1]* (3 - len(dims)))
elif layer.type == 'Normalize': elif layer.type == 'Normalize' and len(dims) == 4:
data = np.asarray(list(blob.data), dtype=np.float32) data = np.asarray(list(blob.data), dtype=np.float32)
transformed.append(data) transformed.append(data)
continue continue
else: else:
c_o, c_i, h, w = map(int, [1] * (4 - len(dims)) \ c_o, c_i, h, w = map(int,
+ list(dims)) [1] * (4 - len(dims)) + list(dims))
else: else:
c_o = blob.num c_o = blob.num
c_i = blob.channels c_i = blob.channels
h = blob.height h = blob.height
w = blob.width w = blob.width
data = np.asarray(list(blob.data), data = np.asarray(
dtype=np.float32).reshape(c_o, c_i, h, w) list(blob.data), dtype=np.float32).reshape(c_o, c_i, h, w)
transformed.append(data) transformed.append(data)
return transformed return transformed
此差异已折叠。
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
from x2paddle.core.graph import GraphNode, Graph from x2paddle.core.graph import GraphNode, Graph
from x2paddle.core.fluid_code import FluidCode from x2paddle.core.fluid_code import FluidCode
from x2paddle.decoder.onnx_shape_inference import SymbolicShapeInference
from onnx.checker import ValidationError from onnx.checker import ValidationError
from onnx.checker import check_model from onnx.checker import check_model
from onnx.utils import polish_model from onnx.utils import polish_model
...@@ -53,7 +54,7 @@ class ONNXGraphNode(GraphNode): ...@@ -53,7 +54,7 @@ class ONNXGraphNode(GraphNode):
convert ONNX node attributes to dict convert ONNX node attributes to dict
""" """
return { return {
attr.name: self.get_attribute_value2(attr) attr.name: self.get_attribute_value(attr)
for attr in self.layer.attribute for attr in self.layer.attribute
} }
...@@ -64,16 +65,15 @@ class ONNXGraphNode(GraphNode): ...@@ -64,16 +65,15 @@ class ONNXGraphNode(GraphNode):
return None return None
return self.attr_map['value'] return self.attr_map['value']
def get_attribute_value2(self, attr): def get_attribute_value(self, attr):
""" """
get_attribute_value enhanced get_attribute_value enhanced
""" """
if attr.type == onnx.AttributeProto.TENSOR: if attr.type == onnx.AttributeProto.TENSOR:
dtype = np.dtype(TENSOR_TYPE_TO_NP_TYPE[attr.t.data_type]) dtype = np.dtype(TENSOR_TYPE_TO_NP_TYPE[attr.t.data_type])
data = attr.t.raw_data data = attr.t.raw_data
value = np.frombuffer(data, value = np.frombuffer(
dtype=dtype, data, dtype=dtype, count=(len(data) // dtype.itemsize))
count=(len(data) // dtype.itemsize))
elif attr.type == onnx.AttributeProto.STRING: elif attr.type == onnx.AttributeProto.STRING:
value = attr.s value = attr.s
value = value.decode() if isinstance(value, bytes) else value value = value.decode() if isinstance(value, bytes) else value
...@@ -131,43 +131,90 @@ class ONNXGraphDataNode(GraphNode): ...@@ -131,43 +131,90 @@ class ONNXGraphDataNode(GraphNode):
class ONNXGraph(Graph): class ONNXGraph(Graph):
def __init__(self, onnx_model): def __init__(self, onnx_model):
super(ONNXGraph, self).__init__(onnx_model.graph) super(ONNXGraph, self).__init__(onnx_model)
self.onnx_model = onnx_model self.fixed_input_shape = {}
self.initializer = {} self.initializer = {}
self.place_holder_nodes = list() self.place_holder_nodes = list()
self.value_infos = {}
self.graph = onnx_model.graph
self.get_place_holder_nodes() self.get_place_holder_nodes()
self.value_infos = self.inferred_model_value_info(self.model) print("shape inferencing ...")
self.results_of_inference = dict() self.graph = SymbolicShapeInference.infer_shapes(
onnx_model, fixed_input_shape=self.fixed_input_shape)
print("shape inferenced.")
self.build()
self.collect_value_infos()
self.allocate_shapes()
def get_inner_nodes(self): def get_inner_nodes(self):
""" """
generate inner node of ONNX model generate inner node of ONNX model
""" """
inner_nodes = [] inner_nodes = []
if not isinstance(self.model, onnx.GraphProto): if not isinstance(self.graph, onnx.GraphProto):
logger.error('graph is not a GraphProto instance') logger.error('graph is not a GraphProto instance')
return return
for initializer in self.model.initializer: for initializer in self.graph.initializer:
name = initializer.name name = initializer.name
inner_nodes.append(name) inner_nodes.append(name)
return inner_nodes return inner_nodes
def get_symbolic_shape(self, dims):
shape = []
for dim in dims:
if dim.HasField('dim_param'):
shape.append(dim.dim_param)
else:
shape.append(dim.dim_value)
return shape
def check_input_shape(self, vi):
if vi.type.HasField('tensor_type'):
for dim in vi.type.tensor_type.shape.dim:
if dim.HasField(
'dim_param') and vi.name not in self.fixed_input_shape:
shape = self.get_symbolic_shape(
vi.type.tensor_type.shape.dim)
print(
"Unknown shape for input tensor[tensor name: '{}'] -> shape: {}, Please define shape of input here,\nNote:you can use visualization tools like Netron to check input shape."
.format(vi.name, shape))
right_shape_been_input = False
while not right_shape_been_input:
try:
shape = raw_input(
"Shape of Input(e.g. -1,3,224,224), enter 'N' to skip: "
)
except:
shape = input(
"Shape of Input(e.g. -1,3,224,224), enter 'N' to skip: "
)
if shape.count("-1") > 1:
print("Only 1 dimension can be -1, type again:)")
else:
right_shape_been_input = True
if shape == 'N':
break
shape = [int(dim) for dim in shape.strip().split(',')]
assert shape.count(-1) <= 1, "Only one dimension can be -1"
self.fixed_input_shape[vi.name] = shape
break
def get_place_holder_nodes(self): def get_place_holder_nodes(self):
""" """
generate place_holder node of ONNX model generate place_holder node of ONNX model
""" """
inner_nodes = self.get_inner_nodes() inner_nodes = self.get_inner_nodes()
input_nodes = [value.name for value in self.model.input] for ipt_vi in self.graph.input:
for ipt_data in input_nodes: if ipt_vi.name not in inner_nodes:
if ipt_data not in inner_nodes: self.check_input_shape(ipt_vi)
self.place_holder_nodes.append(ipt_data) self.place_holder_nodes.append(ipt_vi.name)
def get_output_nodes(self): def get_output_nodes(self):
""" """
generate output_nodes node of ONNX model generate output_nodes node of ONNX model
""" """
inner_nodes = self.get_inner_nodes() inner_nodes = self.get_inner_nodes()
output_nodes = [value.name for value in self.model.output] output_nodes = [value.name for value in self.graph.output]
for opt_data in output_nodes: for opt_data in output_nodes:
if opt_data not in inner_nodes: if opt_data not in inner_nodes:
self.output_nodes.append(opt_data) self.output_nodes.append(opt_data)
...@@ -184,11 +231,11 @@ class ONNXGraph(Graph): ...@@ -184,11 +231,11 @@ class ONNXGraph(Graph):
""" """
build topo_sort of ONNX model build topo_sort of ONNX model
""" """
for layer in self.model.node: for layer in self.graph.node:
node = ONNXGraphNode(layer) node = ONNXGraphNode(layer)
self.node_map[layer.name] = node self.node_map[layer.name] = node
for layer in self.model.input: for layer in self.graph.input:
if layer.name not in self.node_map: if layer.name not in self.node_map:
is_place_holder = self.is_place_holder_nodes(layer.name) is_place_holder = self.is_place_holder_nodes(layer.name)
self.node_map[layer.name] = ONNXGraphDataNode( self.node_map[layer.name] = ONNXGraphDataNode(
...@@ -197,7 +244,7 @@ class ONNXGraph(Graph): ...@@ -197,7 +244,7 @@ class ONNXGraph(Graph):
is_global_input=is_place_holder) is_global_input=is_place_holder)
#set data node's weight #set data node's weight
for initializer in self.model.initializer: for initializer in self.graph.initializer:
name = initializer.name name = initializer.name
weight = to_array(initializer) weight = to_array(initializer)
if name in self.node_map: if name in self.node_map:
...@@ -205,9 +252,8 @@ class ONNXGraph(Graph): ...@@ -205,9 +252,8 @@ class ONNXGraph(Graph):
self.node_map[name].weight = weight self.node_map[name].weight = weight
self.node_map[name].embeded_as = [] self.node_map[name].embeded_as = []
else: else:
self.node_map[name] = ONNXGraphDataNode(initializer, self.node_map[name] = ONNXGraphDataNode(
layer_name=name, initializer, layer_name=name, is_global_input=False)
is_global_input=False)
self.node_map[name].weight = weight self.node_map[name].weight = weight
self.node_map[name].embeded_as = [] self.node_map[name].embeded_as = []
...@@ -230,7 +276,7 @@ class ONNXGraph(Graph): ...@@ -230,7 +276,7 @@ class ONNXGraph(Graph):
continue continue
if in_node not in self.node_map: if in_node not in self.node_map:
flag = 0 flag = 0
for nd in self.model.node: for nd in self.graph.node:
for idx, opt in enumerate(nd.output): for idx, opt in enumerate(nd.output):
if opt == in_node: if opt == in_node:
self.connect(nd.name, layer_name) self.connect(nd.name, layer_name)
...@@ -258,81 +304,86 @@ class ONNXGraph(Graph): ...@@ -258,81 +304,86 @@ class ONNXGraph(Graph):
ipt_node.index = node.which_child[ipt_node.layer_name] ipt_node.index = node.which_child[ipt_node.layer_name]
return ipt_node return ipt_node
def graph_weights(self, graph): def graph_weights(self):
""" """
generator for weights generator for weights
""" """
if not isinstance(graph, onnx.GraphProto): if not isinstance(self.graph, onnx.GraphProto):
logger.error('graph is not a GraphProto instance') logger.error('graph is not a GraphProto instance')
return return
for initializer in graph.initializer: for initializer in self.graph.initializer:
name = initializer.name name = initializer.name
weight = to_array(initializer) weight = to_array(initializer)
yield name, weight yield name, weight
def inferred_model_value_info(self, graph): def collect_value_infos(self):
""" """
collect value/type info for an ONNX model collect value/type info for an ONNX model
""" """
assert isinstance(graph, assert isinstance(self.graph,
onnx.GraphProto), 'model is not a ModelProto instance' onnx.GraphProto), 'model is not a ModelProto instance'
value_info = Dict() for item in self.graph.value_info:
for item in graph.value_info: self.value_infos[item.name] = {
value_info[item.name] = {
'dtype': 'dtype':
TENSOR_TYPE_TO_NP_TYPE[item.type.tensor_type.elem_type], TENSOR_TYPE_TO_NP_TYPE[item.type.tensor_type.elem_type],
'shape': 'shape':
[dim.dim_value for dim in item.type.tensor_type.shape.dim], [dim.dim_value for dim in item.type.tensor_type.shape.dim],
'external': False 'external': False
} }
for item in graph.input:
assert item.name not in value_info # for item in self.graph.input:
value_info[item.name] = { # self.value_infos[item.name] = {
'dtype': # 'dtype':
TENSOR_TYPE_TO_NP_TYPE[item.type.tensor_type.elem_type], # TENSOR_TYPE_TO_NP_TYPE[item.type.tensor_type.elem_type],
'shape': # 'shape':
[dim.dim_value for dim in item.type.tensor_type.shape.dim], # [dim.dim_value for dim in item.type.tensor_type.shape.dim],
'external': True # 'external': True
} # }
for item in graph.output: # for item in self.graph.output:
assert item.name not in value_info # #assert item.name not in value_info
value_info[item.name] = { # self.value_infos[item.name] = {
'dtype': # 'dtype':
TENSOR_TYPE_TO_NP_TYPE[item.type.tensor_type.elem_type], # TENSOR_TYPE_TO_NP_TYPE[item.type.tensor_type.elem_type],
'shape': # 'shape':
[dim.dim_value for dim in item.type.tensor_type.shape.dim], # [dim.dim_value for dim in item.type.tensor_type.shape.dim],
'external': True # 'external': True
} # }
return value_info
def allocate_shapes(self):
"""
run shape inference
"""
for layer in self.graph.node:
node = self.node_map[layer.name]
for opt in layer.output:
if opt in self.value_infos:
value_info = self.value_infos[opt]
#if len(value_info['shape']) == 0 or value_info[
# 'dtype'] is None or 0 in value_info['shape']:
# #TODO add node shape inference
node.dtype = value_info['dtype']
node.out_shapes.append(value_info['shape'])
else:
node.out_shapes.append([])
class ONNXDecoder(object): class ONNXDecoder(object):
def __init__(self, onnx_model): def __init__(self, onnx_model):
model = onnx.load(onnx_model) onnx_model = onnx.load(onnx_model)
print('model ir_version: {}, op version: {}'.format( print('model ir_version: {}, op version: {}'.format(
model.ir_version, model.opset_import[0].version)) onnx_model.ir_version, onnx_model.opset_import[0].version))
if model.opset_import[0].version < 9: self.op_set = onnx_model.opset_import[0].version
_logger.warning(
'Now, onnx2paddle support convert onnx model opset_verison == 9,' check_model(onnx_model)
'opset_verison of your onnx model is %d < 9,'
'some operator maybe unsuccessful in convertion.', onnx_model = self.optimize_model_skip_op(onnx_model)
model.opset_import[0].version) onnx_model = self.optimize_model_strip_initializer(onnx_model)
onnx_model = self.optimize_node_name(onnx_model)
check_model(model) self.graph = ONNXGraph(onnx_model)
self.check_model_running_state(onnx_model) #self.onnx_model = onnx_model
model = onnx.shape_inference.infer_shapes(model)
model = self.optimize_model_skip_op_for_inference(model)
model = self.optimize_model_strip_initializer(model)
self.standardize_variable_name(model.graph)
self.model = model
graph = model.graph
self.onnx_graph = ONNXGraph(model)
self.onnx_graph.build()
def build_value_refs(self, nodes): def build_value_refs(self, nodes):
""" """
...@@ -375,14 +426,13 @@ class ONNXDecoder(object): ...@@ -375,14 +426,13 @@ class ONNXDecoder(object):
processed += 1 processed += 1
return processed return processed
def optimize_model_skip_op_for_inference(self, model, op_list=None): def optimize_model_skip_op(self, model, op_list=None):
""" """
skip ops can be bypassed for inference skip ops can be bypassed for inference
""" """
nodes = model.graph.node
if op_list is None: if op_list is None:
op_list = ['Dropout'] op_list = ['Dropout']
nodes = model.graph.node
input_refs, output_refs = self.build_value_refs(nodes) input_refs, output_refs = self.build_value_refs(nodes)
ret = type(model)() ret = type(model)()
ret.CopyFrom(model) ret.CopyFrom(model)
...@@ -475,38 +525,11 @@ class ONNXDecoder(object): ...@@ -475,38 +525,11 @@ class ONNXDecoder(object):
name = name.replace(s, '_') name = name.replace(s, '_')
return 'x2paddle_' + name return 'x2paddle_' + name
def check_model_running_state(self, model_path): def optimize_node_name(self, model):
import onnxruntime as rt
model = onnx.load(model_path)
model = onnx.shape_inference.infer_shapes(model)
if len(model.graph.value_info) < len(model.graph.node) - 1:
_logger.warning(
'During conversion of your model, some operators will be assignd node.out_shape==None, '
'refer to https://github.com/onnx/onnx/blob/master/docs/ShapeInference.md'
)
try:
datatype_map = {
'tensor(int64)': 'int',
'tensor(float)': 'float32',
'tensor(int32)': 'int32'
}
input_dict = {}
sess = rt.InferenceSession(model_path)
for ipt in sess.get_inputs():
datatype = datatype_map[ipt.type]
input_dict[ipt.name] = np.random.random(
ipt.shape).astype(datatype)
res = sess.run(None, input_feed=input_dict)
except:
raise Exception(
"onnxruntime inference onnx model failed, Please confirm the correctness of onnx model by onnxruntime, if onnx model is correct, please submit issue in github."
)
def standardize_variable_name(self, graph):
""" """
standardize variable name for paddle's code standardize variable name for paddle's code
""" """
graph = model.graph
for initializer in graph.initializer: for initializer in graph.initializer:
initializer.name = self.make_variable_name(initializer.name) initializer.name = self.make_variable_name(initializer.name)
for ipt in graph.input: for ipt in graph.input:
...@@ -525,3 +548,4 @@ class ONNXDecoder(object): ...@@ -525,3 +548,4 @@ class ONNXDecoder(object):
node.input[i] = self.make_variable_name(node.input[i]) node.input[i] = self.make_variable_name(node.input[i])
for i in range(len(node.output)): for i in range(len(node.output)):
node.output[i] = self.make_variable_name(node.output[i]) node.output[i] = self.make_variable_name(node.output[i])
return model
此差异已折叠。
# 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 paddle.fluid as fluid
class PaddleDecoder(object):
def __init__(self,
model_dir,
model_filename='__model__',
params_filename=None):
exe = fluid.Executor(fluid.CPUPlace())
[self.program, feed, fetchs] = fluid.io.load_inference_model(
model_dir,
exe,
model_filename=model_filename,
params_filename=params_filename)
...@@ -48,7 +48,10 @@ class TFGraphNode(GraphNode): ...@@ -48,7 +48,10 @@ class TFGraphNode(GraphNode):
@property @property
def out_shapes(self): def out_shapes(self):
values = self.layer.attr["_output_shapes"].list.shape if self.layer_type == "OneShotIterator":
values = self.layer.attr["output_shapes"].list.shape
else:
values = self.layer.attr["_output_shapes"].list.shape
out_shapes = list() out_shapes = list()
for value in values: for value in values:
shape = [dim.size for dim in value.dim] shape = [dim.size for dim in value.dim]
...@@ -62,6 +65,8 @@ class TFGraphNode(GraphNode): ...@@ -62,6 +65,8 @@ class TFGraphNode(GraphNode):
dtype = self.layer.attr[k].type dtype = self.layer.attr[k].type
if dtype > 0: if dtype > 0:
break break
if dtype == 0:
dtype = self.layer.attr['output_types'].list.type[0]
if dtype not in self.dtype_map: if dtype not in self.dtype_map:
raise Exception("Dtype[{}] not in dtype_map".format(dtype)) raise Exception("Dtype[{}] not in dtype_map".format(dtype))
return self.dtype_map[dtype] return self.dtype_map[dtype]
...@@ -115,13 +120,13 @@ class TFGraph(Graph): ...@@ -115,13 +120,13 @@ class TFGraph(Graph):
def build(self): def build(self):
for layer in self.model.node: for layer in self.model.node:
self.node_map[layer.name.replace('/', '_').replace( self.node_map[layer.name.replace('/', '_').replace(
'-', '_')] = TFGraphNode(layer, data_format=self.tf_data_format) '-', '_')] = TFGraphNode(
layer, data_format=self.tf_data_format)
for layer_name, node in self.node_map.items(): for layer_name, node in self.node_map.items():
for in_node in node.layer.input: for in_node in node.layer.input:
in_node = in_node.replace('/', in_node = in_node.replace('/', '_').replace('-', '_').replace(
'_').replace('-', '^', '')
'_').replace('^', '')
if in_node not in self.node_map: if in_node not in self.node_map:
if in_node.strip().split(':')[0] in self.node_map: if in_node.strip().split(':')[0] in self.node_map:
self.connect(in_node.strip().split(':')[0], layer_name) self.connect(in_node.strip().split(':')[0], layer_name)
...@@ -226,7 +231,7 @@ class TFGraph(Graph): ...@@ -226,7 +231,7 @@ class TFGraph(Graph):
def _remove_identity_node(self): def _remove_identity_node(self):
identity_ops = [ identity_ops = [
'Identity', 'StopGradient', 'Switch', 'Merge', 'Identity', 'StopGradient', 'Switch', 'Merge',
'PlaceholderWithDefault' 'PlaceholderWithDefault', 'IteratorGetNext'
] ]
identity_node = list() identity_node = list()
for node_name, node in self.node_map.items(): for node_name, node in self.node_map.items():
...@@ -317,7 +322,7 @@ class TFDecoder(object): ...@@ -317,7 +322,7 @@ class TFDecoder(object):
graph_def = cp.deepcopy(graph_def) graph_def = cp.deepcopy(graph_def)
input_map = dict() input_map = dict()
for layer in graph_def.node: for layer in graph_def.node:
if layer.op != "Placeholder": if layer.op != "Placeholder" and layer.op != "OneShotIterator":
continue continue
graph_node = TFGraphNode(layer) graph_node = TFGraphNode(layer)
dtype = graph_node.layer.attr['dtype'].type dtype = graph_node.layer.attr['dtype'].type
...@@ -335,6 +340,14 @@ class TFDecoder(object): ...@@ -335,6 +340,14 @@ class TFDecoder(object):
if shape.count(-1) > 1: if shape.count(-1) > 1:
need_define_shape = 2 need_define_shape = 2
if need_define_shape == 1:
try:
shape = graph_node.out_shapes[0]
if len(shape) > 0 and shape.count(-1) < 2:
need_define_shape = 0
except:
pass
if need_define_shape > 0: if need_define_shape > 0:
shape = None shape = None
if graph_node.get_attr("shape"): if graph_node.get_attr("shape"):
...@@ -377,10 +390,10 @@ class TFDecoder(object): ...@@ -377,10 +390,10 @@ class TFDecoder(object):
shape=shape, shape=shape,
name="x2paddle_{}".format(layer.name)) name="x2paddle_{}".format(layer.name))
except: except:
x2paddle_input = tf.placeholder(dtype=dtype, x2paddle_input = tf.placeholder(
shape=shape, dtype=dtype,
name="x2paddle_{}".format( shape=shape,
layer.name)) name="x2paddle_{}".format(layer.name))
input_map["{}:0".format(layer.name)] = x2paddle_input input_map["{}:0".format(layer.name)] = x2paddle_input
if shape.count(None) > 0: if shape.count(None) > 0:
......
...@@ -122,16 +122,17 @@ def convolutiondepthwise_layer(inputs, ...@@ -122,16 +122,17 @@ def convolutiondepthwise_layer(inputs,
c_out = num_output if num_output is not None else input_shape[0][1] c_out = num_output if num_output is not None else input_shape[0][1]
group = int(c_in / (c_in / c_out)) if c_in > c_out else int(c_in / group = int(c_in / (c_in / c_out)) if c_in > c_out else int(c_in /
(c_out / c_in)) (c_out / c_in))
out = fluid.layers.conv2d(input, out = fluid.layers.conv2d(
dilation=[dila_h, dila_w], input,
filter_size=[k_h, k_w], dilation=[dila_h, dila_w],
stride=[s_h, s_w], filter_size=[k_h, k_w],
padding=[p_h, p_w], stride=[s_h, s_w],
groups=group, padding=[p_h, p_w],
num_filters=c_out, groups=group,
param_attr=name + '_weights', num_filters=c_out,
bias_attr=name + '_bias', param_attr=name + '_weights',
name=name) bias_attr=name + '_bias',
name=name)
return out return out
...@@ -142,7 +143,8 @@ def convolutiondepthwise_weights(name, data=None): ...@@ -142,7 +143,8 @@ def convolutiondepthwise_weights(name, data=None):
return weights_name return weights_name
register(kind='ConvolutionDepthwise', register(
shape=convolutiondepthwise_shape, kind='ConvolutionDepthwise',
layer=convolutiondepthwise_layer, shape=convolutiondepthwise_shape,
weights=convolutiondepthwise_weights) layer=convolutiondepthwise_layer,
weights=convolutiondepthwise_weights)
...@@ -12,7 +12,6 @@ def detectionoutput_layer(inputs, ...@@ -12,7 +12,6 @@ def detectionoutput_layer(inputs,
share_location=True, share_location=True,
keep_top_k=100, keep_top_k=100,
confidence_threshold=0.1, confidence_threshold=0.1,
num_classes=2,
input_shape=None, input_shape=None,
name=None): name=None):
nms_param_str = nms_param nms_param_str = nms_param
...@@ -37,9 +36,9 @@ def detectionoutput_layer(inputs, ...@@ -37,9 +36,9 @@ def detectionoutput_layer(inputs,
pb = fluid.layers.reshape(x=pb, shape=[-1, 4]) pb = fluid.layers.reshape(x=pb, shape=[-1, 4])
pbv = fluid.layers.reshape(x=pbv, shape=[-1, 4]) pbv = fluid.layers.reshape(x=pbv, shape=[-1, 4])
mbox_loc = inputs[0] mbox_loc = inputs[0]
mbox_loc = fluid.layers.reshape(x=mbox_loc, shape=[0, -1, 4]) mbox_loc = fluid.layers.reshape(x=mbox_loc, shape=[-1, pb.shape[0], 4])
mbox_conf_flatten = fluid.layers.reshape(x=mbox_conf_flatten, mbox_conf_flatten = fluid.layers.reshape(
shape=[0, -1, num_classes]) x=mbox_conf_flatten, shape=[0, pb.shape[0], -1])
default = {"nms_threshold": 0.3, "top_k": 10, "eta": 1.0} default = {"nms_threshold": 0.3, "top_k": 10, "eta": 1.0}
fields = ['eta', 'top_k', 'nms_threshold'] fields = ['eta', 'top_k', 'nms_threshold']
...@@ -65,7 +64,8 @@ def detectionoutput_weights(name, data=None): ...@@ -65,7 +64,8 @@ def detectionoutput_weights(name, data=None):
return weights_name return weights_name
register(kind='DetectionOutput', register(
shape=detectionoutput_shape, kind='DetectionOutput',
layer=detectionoutput_layer, shape=detectionoutput_shape,
weights=detectionoutput_weights) layer=detectionoutput_layer,
weights=detectionoutput_weights)
...@@ -20,9 +20,8 @@ def normalize_layer(inputs, ...@@ -20,9 +20,8 @@ def normalize_layer(inputs,
attr=name + '_scale') attr=name + '_scale')
scale_param = fluid.layers.reshape(x=scale_param, \ scale_param = fluid.layers.reshape(x=scale_param, \
shape=[1] if channel_shared else [input_shape[0][1]]) shape=[1] if channel_shared else [input_shape[0][1]])
out = fluid.layers.elementwise_mul(x=l2_norm, out = fluid.layers.elementwise_mul(
y=scale_param, x=l2_norm, y=scale_param, axis=-1 if channel_shared else 1)
axis=-1 if channel_shared else 1)
return out return out
...@@ -31,7 +30,8 @@ def normalize_weights(name, data=None): ...@@ -31,7 +30,8 @@ def normalize_weights(name, data=None):
return weights_name return weights_name
register(kind='Normalize', register(
shape=normalize_shape, kind='Normalize',
layer=normalize_layer, shape=normalize_shape,
weights=normalize_weights) layer=normalize_layer,
weights=normalize_weights)
...@@ -23,7 +23,8 @@ def permute_weights(name, data=None): ...@@ -23,7 +23,8 @@ def permute_weights(name, data=None):
return weights_name return weights_name
register(kind='Permute', register(
shape=permute_shape, kind='Permute',
layer=permute_layer, shape=permute_shape,
weights=permute_weights) layer=permute_layer,
weights=permute_weights)
...@@ -30,18 +30,19 @@ def priorbox_layer(inputs, ...@@ -30,18 +30,19 @@ def priorbox_layer(inputs,
steps = tuple(step) if type(step) is list or type(step) is tuple else (step, steps = tuple(step) if type(step) is list or type(step) is tuple else (step,
step) step)
box, variance_ = fluid.layers.prior_box(input, box, variance_ = fluid.layers.prior_box(
image, input,
min_sizes=min_size, image,
max_sizes=max_size, min_sizes=min_size,
aspect_ratios=aspect_ratio, max_sizes=max_size,
variance=variance, aspect_ratios=aspect_ratio,
flip=flip, variance=variance,
clip=clip, flip=flip,
steps=steps, clip=clip,
offset=offset, steps=steps,
name=name, offset=offset,
min_max_aspect_ratios_order=True) name=name,
min_max_aspect_ratios_order=True)
box = fluid.layers.reshape(box, [1, 1, -1]) box = fluid.layers.reshape(box, [1, 1, -1])
variance_ = fluid.layers.reshape(variance_, [1, 1, -1]) variance_ = fluid.layers.reshape(variance_, [1, 1, -1])
out = fluid.layers.concat([box, variance_], axis=1) out = fluid.layers.concat([box, variance_], axis=1)
...@@ -53,7 +54,8 @@ def priorbox_weights(name, data=None): ...@@ -53,7 +54,8 @@ def priorbox_weights(name, data=None):
return weights_name return weights_name
register(kind='PriorBox', register(
shape=priorbox_shape, kind='PriorBox',
layer=priorbox_layer, shape=priorbox_shape,
weights=priorbox_weights) layer=priorbox_layer,
weights=priorbox_weights)
...@@ -23,8 +23,7 @@ def register(kind, shape, layer, weights): ...@@ -23,8 +23,7 @@ def register(kind, shape, layer, weights):
kind = [kind] kind = [kind]
else: else:
assert type( assert type(
kind kind) is list, 'invalid param "kind" for register, not a list or str'
) is list, 'invalid param "kind" for register, not a list or str'
for k in kind: for k in kind:
assert type( assert type(
......
...@@ -21,11 +21,12 @@ def roipooling_layer(inputs, ...@@ -21,11 +21,12 @@ def roipooling_layer(inputs,
input = inputs[0] input = inputs[0]
roi = inputs[1] roi = inputs[1]
roi = fluid.layers.slice(roi, axes=[1], starts=[1], ends=[5]) roi = fluid.layers.slice(roi, axes=[1], starts=[1], ends=[5])
out = fluid.layers.roi_pool(input, out = fluid.layers.roi_pool(
roi, input,
pooled_height=pooled_h, roi,
pooled_width=pooled_w, pooled_height=pooled_h,
spatial_scale=spatial_scale) pooled_width=pooled_w,
spatial_scale=spatial_scale)
return out return out
...@@ -34,7 +35,8 @@ def roipooling_weights(name, data=None): ...@@ -34,7 +35,8 @@ def roipooling_weights(name, data=None):
return weights_name return weights_name
register(kind='ROIPooling', register(
shape=roipooling_shape, kind='ROIPooling',
layer=roipooling_layer, shape=roipooling_shape,
weights=roipooling_weights) layer=roipooling_layer,
weights=roipooling_weights)
...@@ -30,11 +30,12 @@ def select_layer(inputs, ...@@ -30,11 +30,12 @@ def select_layer(inputs,
out = [] out = []
for i in range(len(slice_point)): for i in range(len(slice_point)):
out.append( out.append(
fluid.layers.slice(input, fluid.layers.slice(
axes=[axis], input,
starts=[slice_point[i]], axes=[axis],
ends=[slice_point[i + 1]], starts=[slice_point[i]],
name=name + '_' + str(i))) ends=[slice_point[i + 1]],
name=name + '_' + str(i)))
if i == len(slice_point) - 2: if i == len(slice_point) - 2:
break break
return out return out
...@@ -45,7 +46,8 @@ def select_weights(name, data=None): ...@@ -45,7 +46,8 @@ def select_weights(name, data=None):
return weights_name return weights_name
register(kind='Select', register(
shape=select_shape, kind='Select',
layer=select_layer, shape=select_shape,
weights=select_weights) layer=select_layer,
weights=select_weights)
...@@ -17,7 +17,8 @@ def shufflechannel_weights(name, data=None): ...@@ -17,7 +17,8 @@ def shufflechannel_weights(name, data=None):
return weights_name return weights_name
register(kind='ShuffleChannel', register(
shape=shufflechannel_shape, kind='ShuffleChannel',
layer=shufflechannel_layer, shape=shufflechannel_shape,
weights=shufflechannel_weights) layer=shufflechannel_layer,
weights=shufflechannel_weights)
...@@ -33,8 +33,8 @@ def get_kernel_parameters(params): ...@@ -33,8 +33,8 @@ def get_kernel_parameters(params):
[s_h, s_w] = [params.stride] * 2 [s_h, s_w] = [params.stride] * 2
elif len(params.stride) > 0: elif len(params.stride) > 0:
s_h = params.stride_h if params.stride_h > 0 else params.stride[0] s_h = params.stride_h if params.stride_h > 0 else params.stride[0]
s_w = params.stride_w if params.stride_w > 0 else params.stride[ s_w = params.stride_w if params.stride_w > 0 else params.stride[len(
len(params.stride) - 1] params.stride) - 1]
elif params.stride_h > 0 or params.stride_w > 0: elif params.stride_h > 0 or params.stride_w > 0:
s_h = params.stride_h s_h = params.stride_h
s_w = params.stride_w s_w = params.stride_w
...@@ -293,12 +293,15 @@ def shape_reshape(layer, input_shape): ...@@ -293,12 +293,15 @@ def shape_reshape(layer, input_shape):
explicit_count *= count(l) explicit_count *= count(l)
for i in range(len(copy_axes)): for i in range(len(copy_axes)):
explicit_count *= outshape[start_axis + copy_axes[i]] explicit_count *= outshape[start_axis + copy_axes[i]]
outshape[start_axis + inferred_axis] = -1 assert input_count % explicit_count == 0, "[Reshape]botom count[%d] "\
outshape[0] = 0 "must be divisible by product of the specified dimensions[%d] "\
else: % (input_count, explicit_count)
outshape[0] = -1 outshape[start_axis + inferred_axis] = int(input_count / explicit_count)
output_count = count(outshape) output_count = count(outshape)
assert output_count == input_count, "[Reshape]output count[%d] must match input count[%d]" % (
output_count, input_count)
outshape[0] = -1
return [outshape] return [outshape]
...@@ -342,10 +345,9 @@ def shape_flatten(layer, input_shape): ...@@ -342,10 +345,9 @@ def shape_flatten(layer, input_shape):
output_shape = inshape[0:start_axis] output_shape = inshape[0:start_axis]
if len(inshape[start_axis:end_axis]) != 0: if len(inshape[start_axis:end_axis]) != 0:
flat_sz = reduce(lambda a, b: a * b, inshape[start_axis:end_axis]) flat_sz = reduce(lambda a, b: a * b, inshape[start_axis:end_axis])
flat_sz = -1
output_shape[0] = 0
output_shape += [flat_sz] output_shape += [flat_sz]
output_shape += inshape[end_axis:len(inshape)] output_shape += inshape[end_axis:len(inshape)]
output_shape[0] = -1
return [output_shape] return [output_shape]
......
...@@ -24,21 +24,18 @@ def InstanceNormalization_layer(inputs, name=None): ...@@ -24,21 +24,18 @@ def InstanceNormalization_layer(inputs, name=None):
epsilon = 1e-5 epsilon = 1e-5
input_ = inputs[0] input_ = inputs[0]
mean = fluid.layers.reduce_mean(input_, dim=[2, 3], keep_dim=True) mean = fluid.layers.reduce_mean(input_, dim=[2, 3], keep_dim=True)
var = fluid.layers.reduce_mean(fluid.layers.square(input_ - mean), var = fluid.layers.reduce_mean(
dim=[2, 3], fluid.layers.square(input_ - mean), dim=[2, 3], keep_dim=True)
keep_dim=True)
if name is not None: if name is not None:
scale_name = name + "_scale" scale_name = name + "_scale"
offset_name = name + "_offset" offset_name = name + "_offset"
scale_param = inputs[1] scale_param = inputs[1]
offset_param = inputs[2] offset_param = inputs[2]
scale = fluid.layers.create_parameter(name=scale_param.name, scale = fluid.layers.create_parameter(
shape=input_.shape[1:2], name=scale_param.name, shape=input_.shape[1:2], dtype="float32")
dtype="float32") offset = fluid.layers.create_parameter(
offset = fluid.layers.create_parameter(name=offset_param.name, name=offset_param.name, shape=input_.shape[1:2], dtype="float32")
shape=input_.shape[1:2],
dtype="float32")
tmp = fluid.layers.elementwise_mul(x=(input_ - mean), y=scale, axis=1) tmp = fluid.layers.elementwise_mul(x=(input_ - mean), y=scale, axis=1)
tmp = tmp / fluid.layers.sqrt(var + epsilon) tmp = tmp / fluid.layers.sqrt(var + epsilon)
...@@ -51,8 +48,9 @@ def InstanceNormalization_weights(name, data=None): ...@@ -51,8 +48,9 @@ def InstanceNormalization_weights(name, data=None):
return weights_name return weights_name
register(kind='InstanceNormalization', register(
shape=InstanceNormalization_shape, kind='InstanceNormalization',
layer=InstanceNormalization_layer, shape=InstanceNormalization_shape,
child_func=None, layer=InstanceNormalization_layer,
weights=InstanceNormalization_weights) child_func=None,
weights=InstanceNormalization_weights)
...@@ -36,8 +36,7 @@ def register(kind, shape, layer, child_func, weights): ...@@ -36,8 +36,7 @@ def register(kind, shape, layer, child_func, weights):
kind = [kind] kind = [kind]
else: else:
assert type( assert type(
kind kind) is list, 'invalid param "kind" for register, not a list or str'
) is list, 'invalid param "kind" for register, not a list or str'
for k in kind: for k in kind:
assert type( assert type(
......
# 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.
from x2paddle.op_mapper.onnx.opset9 import ONNXOpMapperOpSet9
class ONNXOpMapperFactory:
def __init__(self):
self.support_op_sets = [9, ]
self.default_op_set = 9
def create_onnx_op_mapper(self, decoder):
run_op_set = self.default_op_set
OpMapper = ''
if decoder.op_set in self.support_op_sets:
OpMapper = 'ONNXOpMapperOpSet' + str(decoder.op_set)
elif decoder.op_set < self.default_op_set:
OpMapper = 'ONNXOpMapperOpSet' + str(self.default_op_set)
else:
for op_set in self.support_op_sets:
if decoder.op_set > op_set:
run_op_set = op_set
else:
break
OpMapper = 'ONNXOpMapperOpSet' + str(run_op_set)
print(
'Now, onnx2paddle support convert onnx model opset_verison {},'
'opset_verison of your onnx model is {}, automatically treated as op_set: {}.'
.format(self.support_op_sets, decoder.op_set, run_op_set))
return eval(OpMapper)(decoder)
...@@ -28,61 +28,55 @@ default_op_mapping_field_values['FILL_NAME_FIELD'] = True ...@@ -28,61 +28,55 @@ default_op_mapping_field_values['FILL_NAME_FIELD'] = True
default_op_mapping = { default_op_mapping = {
'Shape': ['shape', ['X'], ['Out']], 'Shape': ['shape', ['X'], ['Out']],
'Clip': [ 'Clip': [
'clip', ['X'], ['Out'], 'clip', ['X'], ['Out'], dict(), dict(
dict(), min=(_np.asarray(
dict( [255, 255, 127, 255], dtype=_np.uint8).view(_np.float32)[0]),
min=(_np.asarray([255, 255, 127, 255], max=(_np.asarray(
dtype=_np.uint8).view(_np.float32)[0]), [255, 255, 127, 127], dtype=_np.uint8).view(_np.float32)[0]), )
max=(_np.asarray([255, 255, 127, 127],
dtype=_np.uint8).view(_np.float32)[0]),
)
], ],
'Erf': ['erf', ['X'], ['Out']], 'Erf': ['erf', ['X'], ['Out']],
'Ceil': ['ceil', ['X'], ['Out']], 'Ceil': ['ceil', ['X'], ['Out']],
'ReduceMean': [ 'ReduceMean': [
'reduce_mean', ['X'], ['Out'], 'reduce_mean', ['X'], ['Out'], dict(
dict(axes='dim', keepdims='keep_dim'), axes='dim', keepdims='keep_dim'), dict(keep_dim=1)
dict(keep_dim=1)
], ],
'ReduceSum': [ 'ReduceSum': [
'reduce_sum', ['X'], ['Out'], 'reduce_sum', ['X'], ['Out'], dict(
dict(axes='dim', keepdims='keep_dim'), axes='dim', keepdims='keep_dim'), dict(keep_dim=1)
dict(keep_dim=1)
], ],
'ReduceMin': [ 'ReduceMin': [
'reduce_min', ['X'], ['Out'], 'reduce_min', ['X'], ['Out'], dict(
dict(axes='dim', keepdims='keep_dim'), axes='dim', keepdims='keep_dim'), dict(keep_dim=1)
dict(keep_dim=1) ],
'ReduceMax': [
'reduce_max', ['X'], ['Out'], dict(
axes='dim', keepdims='keep_dim'), dict(keep_dim=1)
], ],
#active function #active function
'Relu': ['relu', ['X'], ['Out']], 'Relu': ['relu', ['X'], ['Out']],
'LeakyRelu': ['leaky_relu', ['X'], ['Out'], 'LeakyRelu': ['leaky_relu', ['X'], ['Out'], dict(), dict(alpha=.01)],
dict(), dict(alpha=.01)], 'Elu': ['elu', ['X'], ['Out'], dict(), dict(alpha=1.)],
'Elu': ['elu', ['X'], ['Out'],
dict(), dict(alpha=1.)],
'ThresholdedRelu': [ 'ThresholdedRelu': [
'thresholded_relu', ['X'], ['Out'], 'thresholded_relu', ['X'], ['Out'], dict(alpha='threshold'),
dict(alpha='threshold'),
dict(alpha=1.) dict(alpha=1.)
], ],
'Tanh': ['tanh', ['X'], ['Out']], 'Tanh': ['tanh', ['X'], ['Out']],
'Sigmoid': ['sigmoid', ['X'], ['Out']], 'Sigmoid': ['sigmoid', ['X'], ['Out']],
'HardSigmoid': [ 'HardSigmoid': [
'hard_sigmoid', ['X'], ['Out'], 'hard_sigmoid', ['X'], ['Out'], dict(
dict(alpha='slope', beta='offset'), alpha='slope', beta='offset'), dict(
dict(slope=.2, offset=.5) slope=.2, offset=.5)
], ],
'Softsign': ['softsign', ['X'], ['Out']], 'Softsign': ['softsign', ['X'], ['Out']],
'Softplus': ['softplus', ['X'], ['Out']], 'Softplus': ['softplus', ['X'], ['Out']],
'Exp': ['exp', ['X'], ['Out']], 'Exp': ['exp', ['X'], ['Out']],
'Softmax': ['softmax', ['X'], ['Out'], 'Softmax': ['softmax', ['X'], ['Out'], dict(), dict(axis=1)],
dict(), dict(axis=1)],
'Sqrt': ['sqrt', ['X'], ['Out']], 'Sqrt': ['sqrt', ['X'], ['Out']],
'Floor': ['floor', ['X'], ['Out']], 'Floor': ['floor', ['X'], ['Out']],
'Abs': ['abs', ['X'], ['Out']], 'Abs': ['abs', ['X'], ['Out']],
} }
default_ioa_constraint = { default_ioa_constraint = {
'Gather': 'Gather': [(lambda i, o, a: a.get('axis', 0) == 0,
[(lambda i, o, a: a.get('axis', 0) == 0, 'only axis = 0 is supported')], 'only axis = 0 is supported')],
} }
import onnx
import numpy as np
from onnx import onnx_pb, helper
im2seq_counter = 0
def im2sequence(op, block):
global im2sequence_counter
n, c, h, w = block.var(op.input('X')[0]).shape
assert h > 0 and w > 0, "Only supported fixed input shape for im2sequence operator."
stride_h, stride_w = op.attr('strides')
paddings = op.attr('paddings')
assert op.attr(
'out_stride'
) != 1, "Only out_stride==1 is supported for im2sequence operator."
h = h + paddings[0] + paddings[1]
w = w + paddings[1] + paddings[2]
kernel_h, kernel_w = op.attr('kernels')
out_h = 1 + (h - kernel_h + stride_h - 1) // stride_h
out_w = 1 + (w - kernel_w + stride_w - 1) // stride_w
h_steps = list()
for i in range(out_h):
h_steps.append([i * stride_h, i * stride_h + kernel_h])
w_steps = list()
for i in range(out_w):
w_steps.append([i * stride_w, i * stride_w + kernel_w])
nodes = list()
slice_blocks = list()
for i in range(out_h):
for j in range(out_w):
starts_name = "im2sequence.starts.{}.{}.{}".format(im2seq_counter,
i, j)
starts_tensor = helper.make_tensor(
name=starts_name,
data_type=onnx_pb.TensorProto.INT64,
dims=[4],
vals=[0, 0, h_steps[i][0], w_steps[j][0]])
ends_name = "im2sequence.ends.{}.{}.{}".format(im2seq_counter, i, j)
ends_tensor = helper.make_tensor(
name=ends_name,
data_type=onnx_pb.TensorProto.INT64,
dims=[4],
vals=[999999, 999999, h_steps[i][1], w_steps[j][1]])
starts_node = helper.make_node(
'Constant',
inputs=[],
outputs=[starts_name],
value=starts_tensor)
ends_node = helper.make_node(
'Constant', inputs=[], outputs=[ends_name], value=ends_tensor)
nodes.extend([starts_node, ends_node])
slice_block_name = "im2sequence.slice.{}.{}.{}".format(
im2seq_counter, i, j)
slice_block_node = helper.make_node(
'Slice',
inputs=[op.input('X')[0], starts_name, ends_name],
outputs=[slice_block_name])
flatten_block_name = "im2sequence.flatten.{}.{}.{}".format(
im2seq_counter, i, j)
flatten_block_node = helper.make_node(
"Flatten",
inputs=[slice_block_name],
outputs=[flatten_block_name],
axis=0)
nodes.extend([slice_block_node, flatten_block_node])
slice_blocks.append(flatten_block_name)
concat_block_name = "im2sequence.concat_block.{}".format(im2seq_counter)
# concat_block_node = helper.make_node("Concat", inputs=slice_blocks, outputs=[concat_block_name], axis=0)
concat_block_node = helper.make_node(
"Concat", inputs=slice_blocks, outputs=op.output('Out'), axis=0)
nodes.append(concat_block_node)
print("\n\n==========Importance Notice===========")
print(
"Since im2sequence operator is used in your paddlepaddle model, the translated onnx model only support input data with batch_size=1."
)
print("======================================\n")
return nodes
# 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)
# Ine 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_concat_final_results = outputs['Out']
node_concat_final_results = onnx.helper.make_node(
'Concat',
inputs=inputs_concat_final_results,
outputs=outputs_concat_final_results,
axis=2)
node_list.append(node_concat_final_results)
return node_list
此差异已折叠。
此差异已折叠。
此差异已折叠。
...@@ -41,10 +41,11 @@ class CaffeOptimizer(object): ...@@ -41,10 +41,11 @@ class CaffeOptimizer(object):
if is_delete_node: if is_delete_node:
parent_node.fluid_code.clear() parent_node.fluid_code.clear()
node.fluid_code.clear() node.fluid_code.clear()
node.fluid_code.add_layer("batch_norm", node.fluid_code.add_layer(
inputs=input, "batch_norm",
output=node, inputs=input,
param_attr=parent_param_attr) output=node,
param_attr=parent_param_attr)
def merge_op_activation(self): def merge_op_activation(self):
for node_name in self.graph.topo_sort: for node_name in self.graph.topo_sort:
...@@ -62,7 +63,8 @@ class CaffeOptimizer(object): ...@@ -62,7 +63,8 @@ class CaffeOptimizer(object):
if is_delete_node: if is_delete_node:
parent_node.fluid_code.clear() parent_node.fluid_code.clear()
node.fluid_code.clear() node.fluid_code.clear()
node.fluid_code.add_layer(op, node.fluid_code.add_layer(
inputs=input, op,
output=node, inputs=input,
param_attr=parent_param_attr) output=node,
param_attr=parent_param_attr)
...@@ -13,7 +13,6 @@ ...@@ -13,7 +13,6 @@
# limitations under the License. # limitations under the License.
# TODO useless node remove # TODO useless node remove
from x2paddle.op_mapper.onnx_op_mapper import ONNXOpMapper
class ONNXOptimizer(object): class ONNXOptimizer(object):
......
此差异已折叠。
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
| ShuffleNet | [code](https://github.com/TropComplique/shufflenet-v2-tensorflow) |-| | ShuffleNet | [code](https://github.com/TropComplique/shufflenet-v2-tensorflow) |-|
| mNASNet | [code](https://github.com/tensorflow/tpu/tree/master/models/official/mnasnet) |-| | mNASNet | [code](https://github.com/tensorflow/tpu/tree/master/models/official/mnasnet) |-|
| EfficientNet | [code](https://github.com/tensorflow/tpu/tree/master/models/official/efficientnet) |-| | EfficientNet | [code](https://github.com/tensorflow/tpu/tree/master/models/official/efficientnet) |-|
| Inception_V3 | [code](https://github.com/tensorflow/models/blob/master/research/slim/nets/inception_v3.py) |-|
| Inception_V4 | [code](https://github.com/tensorflow/models/blob/master/research/slim/nets/inception_v4.py) |-| | Inception_V4 | [code](https://github.com/tensorflow/models/blob/master/research/slim/nets/inception_v4.py) |-|
| Inception_ResNet_V2 | [code](https://github.com/tensorflow/models/blob/master/research/slim/nets/inception_resnet_v2.py) |-| | Inception_ResNet_V2 | [code](https://github.com/tensorflow/models/blob/master/research/slim/nets/inception_resnet_v2.py) |-|
| VGG16 | [code](https://github.com/tensorflow/models/tree/master/research/slim/nets) |-| | VGG16 | [code](https://github.com/tensorflow/models/tree/master/research/slim/nets) |-|
...@@ -47,8 +48,8 @@ ...@@ -47,8 +48,8 @@
## ONNX ## ONNX
**注:** 部分模型来源于PyTorch,PyTorch的转换可参考[pytorch_to_onnx.md](pytorch_to_onnx.md) **注:** 部分模型来源于PyTorch,PyTorch的转换可参考[pytorch_to_onnx.md](pytorch_to_onnx.md)
| 模型 | 来源 | operator version| | 模型 | 来源 | operator version|备注|
|-------|--------|---------| |-------|--------|---------|---------|
| ResNet18 | [torchvison.model.resnet18](https://github.com/pytorch/vision/blob/master/torchvision/models/resnet.py) |9| | ResNet18 | [torchvison.model.resnet18](https://github.com/pytorch/vision/blob/master/torchvision/models/resnet.py) |9|
| ResNet34 | [torchvison.model.resnet34](https://github.com/pytorch/vision/blob/master/torchvision/models/resnet.py) |9| | ResNet34 | [torchvison.model.resnet34](https://github.com/pytorch/vision/blob/master/torchvision/models/resnet.py) |9|
| ResNet50 | [torchvison.model.resnet50](https://github.com/pytorch/vision/blob/master/torchvision/models/resnet.py) |9| | ResNet50 | [torchvison.model.resnet50](https://github.com/pytorch/vision/blob/master/torchvision/models/resnet.py) |9|
...@@ -64,4 +65,6 @@ ...@@ -64,4 +65,6 @@
| mNASNet | [pytorch(personal practice)](https://github.com/rwightman/gen-efficientnet-pytorch) |9| | mNASNet | [pytorch(personal practice)](https://github.com/rwightman/gen-efficientnet-pytorch) |9|
| EfficientNet | [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| | 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)| | |Ultra-Light-Fast-Generic-Face-Detector-1MB| [onnx_model](https://github.com/Linzaer/Ultra-Light-Fast-Generic-Face-Detector-1MB/tree/master/models/onnx)|9 |
|BERT| [pytorch(huggingface)](https://github.com/huggingface/transformers/blob/master/notebooks/04-onnx-export.ipynb)|11|转换时需指定input shape,见[文档Q3](FAQ.md)|
|GPT2| [pytorch(huggingface)](https://github.com/huggingface/transformers/blob/master/notebooks/04-onnx-export.ipynb)|11|转换时需指定input shape,见[文档Q3](FAQ.md)|
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册