4、ONNX基础概念:ONNX计算图、节点与张量、ONNX opset版本、IR版本
好,咱们进入ONNX的核心概念部分。说实话,很多同学一开始接触ONNX,就被一堆术语搞懵了——计算图、节点、张量、opset、IR……感觉像在学一门新语言。别急,我带你一个一个捋清楚。
4.1 ONNX计算图:模型骨架
ONNX计算图,说白了就是描述模型运算流程的一张“地图”。它把整个神经网络拆解成一系列步骤,每个步骤就是一个节点,节点之间通过张量连接。
我个人习惯把计算图想象成“流水线”:
- 节点(Node):就是流水线上的工位,每个工位干一件事,比如卷积、池化、激活。
- 张量(Tensor):就是流水线上传递的“零件”,也就是数据。
- 边(Edge):就是工位之间的传送带,定义了数据流向。
举个例子,一个简单的卷积神经网络,它的计算图大概长这样:
输入张量 → Conv节点 → Relu节点 → Pool节点 → Flatten节点 → Gemm(全连接)节点 → 输出张量
每个节点都有输入和输出,输入输出都是张量。你想想看,这不就是典型的“有向无环图”吗?
核心要点:ONNX计算图是静态图,一旦定义好,结构就固定了。这和TensorFlow的Eager模式(动态图)不同,更接近TF1.x的静态图风格。
4.2 节点与张量:模型的基本单元
咱们深入看看节点和张量这两个核心概念。
4.2.1 节点(Node)
每个节点代表一个算子操作。ONNX定义了一套标准算子库,比如:
- Conv:卷积操作
- Relu:ReLU激活函数
- MaxPool:最大池化
- Gemm:矩阵乘法(全连接层)
- Add:张量加法
- Reshape:张量形状变换
每个节点都有三个关键属性:
- op_type:算子类型,比如"Conv"
- input:输入张量名称列表
- output:输出张量名称列表
- attribute:算子参数,比如卷积的kernel_shape、strides
我记得有一次排查模型转换问题,发现某个自定义算子没被ONNX支持,结果模型死活转不过去。后来我查了ONNX算子列表,才发现需要自己实现一个自定义节点。嗯,这里要注意,不是所有TensorFlow算子都能直接映射到ONNX。
4.2.2 张量(Tensor)
张量就是多维数组,是ONNX中数据的基本载体。它包含:
- 数据类型:float32、int64、bool等
- 形状:比如[N, C, H, W](注意,ONNX默认是NCHW格式)
- 数据值:实际存储的数值
避坑指南:我曾经在转换一个图像模型时,发现推理结果完全不对。查了半天,原来是TensorFlow默认用NHWC,而ONNX用NCHW。数据排布不一样,结果当然天差地别。所以转换时一定要确认数据格式!
4.3 ONNX opset版本:算子的“方言”
opset,全称是Operator Set,翻译过来就是“算子集”。它定义了某个版本下,ONNX支持哪些算子,以及每个算子的行为。
为什么会这样?因为深度学习框架在不断发展,新的算子不断出现,旧算子也可能有行为变更。ONNX通过opset版本来管理这种变化。
举个例子:
- opset 7:支持基本的Conv、Relu、Gemm等
- opset 10:增加了对量化算子的支持
- opset 13:改进了Softmax的行为
- opset 15:增加了对更多NLP算子的支持
我建议你在转换模型时,尽量使用较新的opset版本(比如opset 15或更高),因为新版本通常修复了旧版本的bug,也支持更多算子。但也要注意,目标推理引擎可能只支持特定opset版本。
重要提醒:不同opset版本之间,同一个算子的行为可能不同。比如opset 12之前的Softmax,默认axis=1;opset 13之后,默认axis=-1。如果你不指定axis,转换后的模型行为可能和预期不一致。
查看当前ONNX支持的opset版本,可以用这个命令:
import onnx
print(onnx.defs.onnx_opset_version()) # 输出当前ONNX版本支持的最高opset
4.4 IR版本:模型文件的“格式规范”
IR,全称Intermediate Representation,中间表示。它定义了ONNX模型文件的整体结构规范。
说白了,IR版本就是告诉你:一个ONNX文件应该长什么样,包含哪些部分,各部分如何组织。
目前ONNX主要有两个IR版本:
| IR版本 | 说明 | 主要变化 |
|---|---|---|
| IR v3 | 早期版本,功能有限 | 不支持稀疏张量、外部数据 |
| IR v4 | 当前主流版本 | 支持稀疏张量、外部数据、模型元数据更丰富 |
| IR v5 | 最新版本(ONNX 1.12+) | 支持函数(Function)、训练相关算子 |
我个人习惯使用IR v4版本,兼容性最好。如果你需要用到函数(Function)特性,比如把重复的子图封装成函数,那就得用IR v5。
查看ONNX模型的IR版本:
import onnx
model = onnx.load("model.onnx")
print(model.ir_version) # 输出IR版本号,比如6代表IR v4
核心总结:
- 计算图:模型的骨架,描述运算流程
- 节点:计算图中的算子操作
- 张量:节点之间传递的数据
- opset版本:定义算子行为和可用算子集合
- IR版本:定义模型文件的整体结构规范
这四个概念,你搞清楚了,ONNX就算入门了。下一章,咱们会动手把TensorFlow模型转成ONNX,到时候这些概念都会用上。