1. ONNX基础:ONNX发展史、ONNX生态、ONNX模型结构解析、ONNX Runtime安装
各位同学,欢迎来到《ONNX模型与OpenVINO部署实战》的第一章。
说实话,每次带新人做模型部署,我第一件事就是让他们搞懂ONNX。为什么?因为在实际工程中,你永远不知道甲方给的是PyTorch还是TensorFlow,甚至可能是某个冷门框架。ONNX就是那个“万能转换器”。
这一章,咱们把ONNX的来龙去脉、生态结构、以及最关键的Runtime安装,一次性讲透。
1.1 ONNX发展史:从微软内部工具到行业标准
ONNX的全称是Open Neural Network Exchange,直译就是“开放神经网络交换格式”。
2017年,微软和Facebook联合推出了这个项目。我记得当时PyTorch刚火起来,TensorFlow还是老大,两个框架互不兼容。模型从研究到生产,中间要经历“九九八十一道转换工序”,痛苦得很。
ONNX的初衷很简单:让模型能跨框架、跨平台、跨硬件运行。说白了,就是给深度学习模型搞了个“通用语言”。
几个关键时间节点:
- 2017年9月:微软和Facebook联合发布ONNX 1.0,支持Caffe2、PyTorch、CNTK等框架。
- 2018年:ONNX加入Linux基金会,成为开源社区项目。AWS、Intel、IBM等巨头纷纷加入。
- 2019年:ONNX Runtime正式发布,解决了“ONNX模型谁来跑”的问题。
- 2020年至今:ONNX成为AI部署的事实标准。几乎所有主流框架都支持导出ONNX格式。
核心观点:ONNX不是框架,是“中间表示”。它不负责训练,只负责“翻译”和“搬运”。
1.2 ONNX生态:你需要的工具链都在这里
ONNX的生态,我习惯把它分成三层:
| 层级 | 工具/组件 | 作用 |
|---|---|---|
| 模型转换层 | torch.onnx、tf2onnx、onnx.helper | 将训练好的模型导出为ONNX格式 |
| 模型优化层 | onnx-simplifier、onnxoptimizer | 简化计算图、去除冗余节点、常量折叠 |
| 推理执行层 | ONNX Runtime、TensorRT、OpenVINO | 加载ONNX模型并执行推理 |
这里我想重点聊聊onnx-simplifier。我在项目中遇到过好几次,导出的ONNX模型里有一堆“if-else”节点和形状推断操作,推理速度慢得离谱。用simplifier一跑,节点数直接砍半。嗯,这个工具我建议你装到开发环境里,早晚用得上。
另外,ONNX的算子集(Operator Set)也在不断更新。目前已经到opset 21了。你想想看,每个版本都会新增或修改一些算子。如果你用的框架版本太老,导出的模型可能在新版Runtime上跑不了。避坑指南:导出时指定opset版本,比如opset_version=11或13,兼容性最好。
1.3 ONNX模型结构解析:一张图看懂计算图
ONNX模型本质上是一个有向无环图(DAG)。我刚开始接触时,觉得这玩意儿很玄乎。后来拆开一个模型文件一看,其实就三样东西:
- 节点(Node):每个节点代表一个算子,比如Conv、Relu、Add。
- 张量(Tensor):节点之间的输入输出数据,包括权重、偏置、中间特征图。
- 图(Graph):所有节点和边的集合,定义了数据流动的路径。
举个例子,一个简单的卷积网络,ONNX结构大概长这样:
# 伪代码展示ONNX图结构
graph {
node: Conv (input: "input", output: "conv1_out")
node: Relu (input: "conv1_out", output: "relu1_out")
node: MaxPool (input: "relu1_out", output: "pool1_out")
...
initializer: weight_conv1 (shape: [64,3,3,3])
initializer: bias_conv1 (shape: [64])
input: "input" (shape: [1,3,224,224])
output: "output" (shape: [1,1000])
}
你可能会问:“为什么我导出的ONNX文件比原始模型大?” 原因很简单——ONNX把权重参数也打包进去了。这些参数以initializer的形式存储在模型文件中。我曾经把一个ResNet50导出成ONNX,文件从200MB涨到250MB,就是因为权重格式从float16转成了float32。
小技巧:用Netron工具(一个在线模型可视化工具)打开ONNX文件,可以直观看到计算图结构。我调试模型时,经常用它来检查节点连接是否正确。
1.4 ONNX Runtime安装:三步搞定
ONNX Runtime是微软推出的跨平台推理引擎。说白了,它就是用来“跑”ONNX模型的。安装方式很简单,我推荐用pip直接装:
# CPU版本(最常用)
pip install onnxruntime
# GPU版本(需要CUDA和cuDNN)
pip install onnxruntime-gpu
# 验证安装
python -c "import onnxruntime; print(onnxruntime.__version__)"
这里有几个坑,我踩过不止一次:
- 版本匹配问题:onnxruntime-gpu的版本必须和CUDA版本对应。比如onnxruntime 1.15.0对应CUDA 11.8。装错了会报“DLL load failed”错误。
- Python版本:建议用Python 3.8-3.11,太老或太新的版本可能没有预编译包。
- Windows用户注意:如果遇到“找不到msvcp140.dll”,去装个Visual C++ Redistributable就好了。
警告:不要在生产环境用pip install --upgrade onnxruntime。我曾经手贱升级了版本,结果模型推理结果全变了。后来发现是算子实现有差异。建议锁定版本号,比如onnxruntime==1.15.0。
安装完成后,写个简单的推理脚本测试一下:
import onnxruntime as ort
import numpy as np
# 创建推理会话
session = ort.InferenceSession("model.onnx")
# 获取输入输出信息
input_name = session.get_inputs()[0].name
output_name = session.get_outputs()[0].name
# 构造随机输入
input_data = np.random.randn(1, 3, 224, 224).astype(np.float32)
# 执行推理
output = session.run([output_name], {input_name: input_data})
print("推理结果形状:", output[0].shape)
这段代码虽然简单,但包含了ONNX Runtime推理的核心流程:创建会话、获取输入输出、构造数据、执行推理。我在实际项目中,基本就是在这个框架上做扩展,比如加个预处理、后处理、性能统计之类的。
小结
这一章我们聊了ONNX的来龙去脉、生态工具、模型结构,以及Runtime的安装。说实话,ONNX本身不复杂,但它的生态很庞大。你只要记住:ONNX是桥梁,Runtime是引擎,工具链是辅助。
下一章,我们会动手把PyTorch模型导出成ONNX,然后踩一踩那些常见的坑。嗯,到时候你会感谢今天装好的onnx-simplifier的。