1. ONNX基础认知:什么是ONNX、为什么需要ONNX、ONNX的生态与工具链概览

1.1 什么是ONNX?说白了就是个模型“通用语言”

ONNX,全称是Open Neural Network Exchange。名字挺长,但核心意思就一个:让不同AI框架之间能互相“对话”

我刚开始做模型部署那会儿,最头疼的就是框架切换。PyTorch训练好的模型,想转到TensorRT上推理?得先转成Caffe,再转成ONNX,中间还可能报错。那时候我就想,要是有一个统一的中间格式就好了。

ONNX就是干这个的。它定义了一套标准的数据结构、算子集合和计算图表示。你可以在PyTorch里训练,导出成ONNX,然后直接扔到ONNX Runtime、TensorRT或者OpenVINO里跑。说白了,它就是个“翻译官”,让不同框架之间不再鸡同鸭讲。

核心要点:ONNX是一种中间表示(Intermediate Representation, IR),它不依赖任何特定的训练框架,只描述模型的计算逻辑。

1.2 为什么需要ONNX?我踩过的坑告诉你

你可能觉得,我就在PyTorch里一条路走到黑不行吗?嗯,我以前也这么想。直到有一次,客户要求模型必须跑在华为昇腾芯片上。PyTorch原生不支持,TensorRT也不行,只能转成OM格式。而ONNX就是通往OM的必经之路。

总结下来,ONNX解决了三个核心痛点:

  • 框架解耦:训练用PyTorch,部署用ONNX Runtime,互不绑架。你换框架,模型不用重写。
  • 硬件适配:不同硬件厂商(NVIDIA、Intel、华为、AMD)都优先支持ONNX。你导出一次,到处部署。
  • 生态互通:ONNX连接了训练、优化、量化、推理整个链条。没有它,每一步都得手动适配。

我的经验:如果你做模型部署,ONNX几乎是绕不开的一环。哪怕你只用PyTorch,也建议养成导出ONNX的习惯。万一哪天要换硬件,你就知道这个习惯有多值钱了。

1.3 ONNX的生态与工具链概览

ONNX不是孤零零的一个格式,它背后有一套完整的工具链。我把它分成三块:

1.3.1 模型导出与转换

  • PyTorch → ONNX:torch.onnx.export(),这是最常用的路径。我建议导出时加上 dynamic_axes 参数,让输入尺寸可变。
  • TensorFlow → ONNX:tf2onnx 工具。注意TensorFlow的算子命名和ONNX不太一样,有时候需要手动映射。
  • 其他框架:Keras、MXNet、Caffe2 都有对应的转换工具。不过说实话,PyTorch和TensorFlow占了90%的场景。

1.3.2 模型优化与调试

  • ONNX Runtime:微软出品的推理引擎,支持CPU、GPU、甚至手机端。我项目里90%的ONNX模型都用它跑。
  • ONNX Simplifier:一个神器。它能帮你简化计算图,去掉冗余节点。我遇到过模型导出后多出几百个Shape节点,跑一遍simplify就干净了。
  • Netron:可视化工具。拖进去一个ONNX文件,就能看到完整的计算图。调试时我必开它。

1.3.3 硬件后端支持

硬件平台 推理引擎 ONNX支持情况
NVIDIA GPU TensorRT 原生支持,性能最优
Intel CPU/GPU OpenVINO 支持,需转IR格式
华为昇腾 MindSpore Lite 支持,需转OM格式
AMD GPU ROCm 通过ONNX Runtime支持
ARM/手机端 ONNX Runtime Mobile 支持,需精简算子

注意:不是所有ONNX算子都能在所有硬件上跑。比如TensorRT就不支持某些动态Shape的算子。我曾经在导出时没注意,结果部署时发现算子不支持,只能手写自定义实现。嗯,这个坑后面章节会详细讲。

1.4 一个简单的ONNX导出示例

光说不练假把式。我写个最简单的例子,让你感受一下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()
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模型导出成功!")

这段代码跑完,你就能在当前目录看到 simple_model.onnx 文件。用Netron打开它,你会看到一个线性层,输入输出都标明了。嗯,就是这么简单。

1.5 本章小结

ONNX不是什么高深的东西。它就是AI模型界的“普通话”,让不同框架、不同硬件之间能顺畅沟通。我个人觉得,掌握ONNX是模型部署工程师的必备技能。后面的章节,我们会深入自定义算子注册,那才是真正考验功底的地方。

下一章,我会带你手写第一个自定义算子。准备好了吗?