1 背景介绍
使用深度学习开源框架训练完网络模型后,在部署之前通常需要进行格式转换,地平线工具链模型转换目前支持Caffe1.0和ONNX(opset_version=10/11 且 ir_version≤7)两种。ONNX(Open Neural Network Exchange)格式是一种常用的开源神经网络格式,被较多推理引擎支持,例如Pytorch、PaddlePaddle、TensorFlow等。本文将详细介绍如何将PaddlePaddle格式的模型导出到ONNX格式。
2 实验环境
本教程的实验环境如下:
Python库 | Version |
|---|---|
paddlepaddle | 2.4.1 |
paddle2onnx | 1.0.5 |
onnx | 1.13.0 |
onnxruntime | 1.14.0 |
3 paddle.onnx.export函数简介
paddle.onnx.export函数可以将PaddlePaddle模型导出为ONNX模型,函数介绍如下,其中x_spec用于配置paddle.onnx.export的input_spec参数。
#layer: 导出的Layer对象,即需要转换的网络模型
#path: 存储模型的路径前缀,导出后会自动添加后缀“.onnx”
#input_spec: 用于配置模型输入属性
#opset_version: 默认为9,请手动配置10或11
https://www.paddlepaddle.org.cn/documentation/docs/zh/api/paddle/onnx/export_cn.html
代码实操
PaddlePaddle与ONNX模型导出
以下代码展示了搭建一个简单分类模型并以PaddlePaddle和ONNX格式保存的过程。
def __init__(self, num_classes=10):
super(MyNet, self).init()
self.num_classes = num_classes
self.features = nn.Sequential(
nn.Conv2D(in_channels=1, out_channels=2,
kernel_size=3, stride=1, padding=1),
nn.ReLU())
self.linear = nn.Sequential(nn.Linear(98, num_classes))
def forward(self, inputs):
x = self.features(inputs)
x = paddle.flatten(x, 1)
x = self.linear(x)
return x
model = MyNet()
x_spec = paddle.static.InputSpec([1, 1, 7, 7], 'float32', 'input1')
#将模型以PaddlePaddle的格式保存,以验证和ONNX模型推理的一致性
paddle.jit.save(layer=model, path='./pd_model/pdmodel',
input_spec=[x_spec])
#将模型导出为ONNX格式保存
paddle.onnx.export(layer=model, path='./model',
input_spec=[x_spec], opset_version=11)
ONNX正确性验证
check = onnx.checker.check_model(onnx_model)
print('Check: ', check)
PaddlePaddle与ONNX的一致性检查
可以使用以下代码检查导出的ONNX模型和原始的PaddlePaddle模型是否有相同的计算结果。
ort_inputs = {ort_sess.get_inputs()[0].name: input1}
ort_outs = ort_sess.run(None, ort_inputs)
model.eval()
paddle_input = paddle.to_tensor(input1)
paddle_outs = model(paddle_input)
print(paddle_outs.numpy())
np.testing.assert_allclose(tf_outs.numpy(), ort_outs[0], rtol=1e-03, atol=1e-05)
print("onnx model check finsh.")
多输入的情况
若您的模型存在多输入,则可参考下方代码保存成PaddlePaddle和ONNX格式。ONNX的正确性验证和PaddlePaddle与ONNX的一致性检查不再赘述,仿照上述代码编写即可。
def __init__(self, num_classes=10):
super(MyNet, self).init()
self.num_classes = num_classes
self.features_1 = nn.Sequential(
nn.Conv2D(in_channels=1, out_channels=2,
kernel_size=3, stride=1, padding=1),
nn.ReLU())
self.features_2 = nn.Sequential(
nn.Conv2D(in_channels=1, out_channels=2,
kernel_size=3, stride=1, padding=1),
nn.ReLU())
self.linear = nn.Sequential(nn.Linear(98, num_classes))
def forward(self, inputs1, inputs2):
x = self.features_1(inputs1)
y = self.features_2(inputs2)
z = paddle.concat((x, y), 1)
z = paddle.flatten(z, 1)
z = self.linear(z)
return z
model = MyNet()
y_spec = paddle.static.InputSpec([1, 1, 7, 7], 'float32', 'input2')
paddle.jit.save(layer=model, path='./pd_model/pdmodel',
input_spec=[x_spec, y_spec])
paddle.onnx.export(layer=model, path='./model',
input_spec=[x_spec, y_spec], opset_version=11)
5 ONNX模型可视化

6 ir_version和opset_version修改
地平线工具链支持的ONNX模型需要满足 opset_version=10/11 且 ir_version≤7,当拿到的ONNX模型不满足这两个要求时,可以修改代码重新导出,或者尝试编写脚本直接修改ONNX模型的对应属性,第二种方式的示例代码如下:
model.ir_version = 6
model.opset_import[0].version = 10
onnx.save_model(model, "./model_version.onnx")
调整结束后,使用Netron可视化model_version.onnx,如下图所示:

此时,ONNX模型的ir_version=6,opset_version=10,满足地平线工具链的转换条件。

