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:模型交换格式。不参与训练,只负责转换和推理。

我个人的工作流是:

  1. 用PyTorch训练模型
  2. 导出为ONNX格式
  3. 用ONNX Runtime做推理优化
  4. 部署到目标平台

当然,你也可以用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。看看它到底是怎么优化模型推理的。

到时候见。