1. ONNX基础入门:ONNX是什么、为什么需要ONNX、ONNX生态概览、ONNX与PyTorch/TensorFlow的关系
1.1 ONNX到底是什么?
ONNX,全称是Open Neural Network Exchange。
说白了,它就是一套模型格式标准。就像JPEG是图片的标准格式一样,ONNX是深度学习模型的标准格式。
我刚开始接触ONNX时,觉得它就是个中间格式。后来用多了才发现,它远不止这么简单。
ONNX的核心价值在于:让模型在不同框架之间自由流动。
你想想看,你用PyTorch训练了一个模型,想部署到移动端。没有ONNX的话,你得用PyTorch Mobile。但如果你用了ONNX,就可以转成ONNX格式,然后用ONNX Runtime跑。甚至还能再转成TensorFlow、CoreML、TensorRT等格式。
嗯,这就是ONNX的魔力。
一句话总结:ONNX是深度学习模型的通用语言。它让模型不再被某个框架绑定。
1.2 为什么需要ONNX?
这个问题,我在项目中遇到过很多次。
有一次,团队用PyTorch训练了一个图像分类模型。部署时发现,客户的生产环境只支持TensorFlow。怎么办?重训?太慢了。转格式?没有标准。
这时候ONNX就派上用场了。
具体来说,ONNX解决了三个核心痛点:
- 框架锁定问题:模型不再依赖某个特定框架。你可以自由切换训练和推理框架。
- 部署碎片化:不同硬件(CPU、GPU、NPU)需要不同的优化。ONNX提供了统一的优化入口。
- 模型互操作性:团队之间可以共享模型,不管对方用什么框架。
我个人的习惯是:所有需要部署的模型,都先转成ONNX。这样后续的优化和部署就灵活多了。
小提示:ONNX不只是格式转换。它还提供了算子标准。不同框架的算子,在ONNX里都有对应的定义。这就保证了转换后的模型行为一致。
1.3 ONNX生态概览
ONNX的生态,比你想象的要大得多。
我把它分成三个层次:
| 层次 | 组件 | 作用 |
|---|---|---|
| 核心层 | ONNX格式、ONNX算子集 | 定义模型结构和算子标准 |
| 工具层 | ONNX Runtime、ONNX-TensorFlow、ONNX-CoreML | 模型转换、推理优化 |
| 生态层 | TensorRT、OpenVINO、NCNN、TNN | 硬件加速、端侧部署 |
核心层是ONNX本身。它定义了模型的计算图结构,以及每个算子的标准。
工具层是ONNX Runtime。这是微软推出的推理引擎。它支持ONNX模型的高效推理。我建议你重点掌握它。
生态层是各种硬件厂商的加速库。比如NVIDIA的TensorRT、Intel的OpenVINO。它们都支持ONNX模型作为输入。
你想想看,一个ONNX模型,可以跑在CPU上,也可以跑在GPU上,还能跑在NPU上。这就是生态的力量。
注意:ONNX生态虽然丰富,但算子兼容性是个坑。有些框架的算子,ONNX不支持。转换时会报错。我曾经花了一整天,就为了处理一个PyTorch的F.interpolate算子。嗯,这个后面会详细讲。
1.4 ONNX与PyTorch/TensorFlow的关系
这个问题,很多初学者会搞混。
我直接说结论:ONNX不是PyTorch的替代品,也不是TensorFlow的替代品。它是它们之间的桥梁。
具体关系是这样的:
- PyTorch:训练模型的主力。动态图,灵活,适合研究。
- TensorFlow:训练和部署都可以。静态图,稳定,适合生产。
- ONNX:模型交换格式。不参与训练,只负责转换和推理。
我个人的工作流是:
- 用PyTorch训练模型
- 导出为ONNX格式
- 用ONNX Runtime做推理优化
- 部署到目标平台
当然,你也可以用TensorFlow训练,然后转ONNX。流程是一样的。
为什么会这样?因为ONNX的设计目标就是解耦训练和推理。训练时,你选最顺手的框架。推理时,你选最高效的引擎。ONNX就是中间的粘合剂。
核心观点:ONNX不是要取代谁,而是要连接谁。它让PyTorch和TensorFlow不再是竞争对手,而是合作伙伴。
1.5 一个简单的例子
说了这么多,不如看个代码。
下面是用PyTorch训练一个简单模型,然后导出为ONNX的例子:
import torch
import torch.nn as nn
# 定义一个简单的模型
class SimpleModel(nn.Module):
def __init__(self):
super().__init__()
self.fc = nn.Linear(10, 5)
def forward(self, x):
return self.fc(x)
# 创建模型实例
model = SimpleModel()
model.eval()
# 创建一个示例输入
dummy_input = torch.randn(1, 10)
# 导出为ONNX
torch.onnx.export(
model, # 模型
dummy_input, # 示例输入
"simple_model.onnx", # 输出文件名
input_names=["input"],
output_names=["output"],
dynamic_axes={
"input": {0: "batch_size"},
"output": {0: "batch_size"}
}
)
print("模型已导出为ONNX格式")
这段代码做了三件事:
- 定义了一个简单的线性模型
- 创建了一个示例输入
- 用torch.onnx.export导出为ONNX
注意那个dynamic_axes参数。它指定了batch_size是动态的。这样导出的模型,可以处理不同batch size的输入。
嗯,这里有个坑。如果你不指定dynamic_axes,导出的模型就固定了batch size。部署时如果输入batch size变了,就会报错。我曾经因为这个被坑过一次。
建议:导出ONNX时,尽量把batch size设为动态。除非你确定部署时batch size是固定的。
1.6 本章小结
这一章,我们聊了ONNX的基础知识。
核心就三点:
- ONNX是模型格式标准,让模型在不同框架间自由流动
- ONNX生态包括核心层、工具层、生态层,覆盖了从转换到部署的全链路
- ONNX与PyTorch/TensorFlow是互补关系,不是替代关系
下一章,我们会深入ONNX Runtime。看看它到底是怎么优化模型推理的。
到时候见。