2、ONNX模型基础:ONNX格式解析、ONNX算子集、ONNX Runtime推理引擎、模型可视化工具Netron

好,咱们正式开始聊ONNX。说实话,我第一次接触ONNX的时候,心里想的是:这不就是又一种模型格式吗?后来踩了几个坑才明白,ONNX真正的价值不在于它是什么格式,而在于它打通了不同框架之间的壁垒。你想想看,PyTorch训练好的模型想部署到手机上,或者TensorFlow的模型想用ONNX Runtime加速,没有ONNX还真不好办。

2.1 ONNX格式解析:不只是个文件

ONNX的全称是Open Neural Network Exchange,说白了就是「开放神经网络交换格式」。我个人的理解是,它就像模型界的通用语言——不管你是用PyTorch、TensorFlow还是其他框架训练的,最后都能转成ONNX,然后在各种平台上跑起来。

咱们来看看ONNX文件里到底装了啥。一个标准的ONNX模型,其实就是一个Protobuf序列化的文件。嗯,这里要注意,Protobuf是Google搞的一种高效数据格式,比JSON紧凑多了。

# 用Python读取ONNX模型的结构
import onnx

model = onnx.load("model.onnx")
print(model.ir_version)  # IR版本号
print(model.producer_name)  # 生产框架名称
print(model.graph)  # 计算图结构

我在项目中遇到过一个问题:加载ONNX模型时提示版本不兼容。后来发现是ONNX的IR版本和算子集版本对不上。所以我的建议是,导出模型时一定要指定好opset版本,别用默认值。

ONNX模型的核心结构包括三部分:

  • 计算图(Graph):描述整个网络的计算流程,包含节点、边、初始值等
  • 节点(Node):每个节点代表一个算子操作,比如Conv、Relu、MatMul
  • 张量(Tensor):模型中的权重、偏置等参数,以及中间计算结果

核心要点:ONNX模型本质上是一个有向无环图(DAG),节点是算子,边是张量数据流。理解这个结构,后面做剪枝和量化就顺了。

2.2 ONNX算子集:别被名字吓到

算子集,英文叫Opset。说白了就是ONNX支持的所有操作的集合。每个版本的ONNX都会增加一些新算子,或者废弃一些旧算子。我记得ONNX 1.0的时候只有几十个算子,现在都上百了。

常见的算子我列一下:

算子名称 功能描述 常见场景
Conv 卷积操作 CNN网络
Relu 激活函数 几乎所有网络
MatMul 矩阵乘法 全连接层、注意力机制
BatchNormalization 批归一化 训练稳定
Reshape 张量形状变换 维度调整

你可能会问:为什么要有算子集这个概念?直接支持所有操作不就行了?嗯,这里有个现实问题——不同框架对同一个操作可能有不同的实现细节。比如PyTorch的Conv和TensorFlow的Conv,参数顺序、填充方式都可能不一样。ONNX算子集就是把这些差异统一起来,定义一套标准接口。

避坑指南:我曾经在导出Transformer模型时,发现ONNX不支持某个自定义算子。解决办法是手动用ONNX支持的算子重新实现,或者用onnx-simplifier做简化。记住,不是所有PyTorch操作都能直接转成ONNX。

2.3 ONNX Runtime推理引擎:轻量级加速器

ONNX Runtime,简称ORT,是微软开源的推理引擎。我刚开始用的时候觉得它就是个普通的推理库,后来发现它做了大量优化——比如算子融合、内存复用、图优化等等。说白了,就是让你的模型跑得更快、更省内存。

安装很简单:

pip install onnxruntime

如果你有GPU,可以装GPU版本:

pip install onnxruntime-gpu

推理代码也很直观:

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做性能对比。比如同一个模型,用PyTorch推理和用ORT推理,速度能差多少?我实测过,在CPU上ORT通常能快20%-50%,在GPU上优化更明显。

注意:ONNX Runtime的版本要和ONNX模型的opset版本匹配。我遇到过因为版本不匹配导致推理结果错误的情况,排查了半天才发现是版本问题。建议用最新稳定版。

2.4 模型可视化工具Netron:看图说话

Netron是我最常用的模型可视化工具,没有之一。它支持ONNX、PyTorch、TensorFlow等几十种格式。你想想看,一个几百层的网络,光看代码根本理不清结构,但用Netron一看就一目了然。

使用方式有两种:

  • 在线版:打开 netron.app,直接把模型文件拖进去
  • 本地版:pip install netron,然后运行 netron model.onnx

Netron能展示的信息包括:

  • 每个节点的输入输出张量形状
  • 算子的参数(比如卷积核大小、步长)
  • 权重值的分布(点击节点可以查看)
  • 模型的计算图拓扑结构

我记得有一次做模型剪枝,需要确认某个卷积层后面有没有跟着BN层。用Netron一看,整个网络结构清清楚楚,比翻代码快多了。我的建议是,每次导出ONNX模型后,先用Netron看一眼,确认结构对不对。

实用技巧:Netron支持搜索节点。如果你的模型有几百层,直接搜"Conv"就能定位到所有卷积层。这个功能在做剪枝时特别有用——你可以快速找到哪些层需要剪。

好了,ONNX的基础知识就聊到这儿。下一章咱们会深入讲如何把PyTorch模型转成ONNX,以及常见的坑怎么避开。记住,理解ONNX的结构和工具,是后面做剪枝和轻量化的前提。别急着上手,先把基础打牢。