跳转到内容
新建笔记

ONNX:计算图、版本与模型检查

ONNX 在部署链路中的位置

跳转到“ONNX 在部署链路中的位置”

ONNX 用计算图描述模型的运算、参数和输入输出约定。onnx Python 包负责读取、构造和检查这个模型;真正执行推理的是 ONNX Runtime 等运行时。一个文件能通过结构检查,不等于目标设备支持其中的全部算子,也不等于转换前后数值相同。

对象表达什么不能据此推断什么
图的输入、输出张量名称、类型、形状等接口图像一定是 RGB、一定按 0~1 归一化
节点 node如 Add、Mul、Conv 等运算每个节点恰好对应一次 GPU kernel
initializer图中保存的常量或权重它一定是待训练参数
opset_import各算子域采用的算子规范版本Python 包版本与 opset 编号相同
ir_version模型容器与图表示的版本把数字改小就能自动兼容旧运行时

算子版本按域解释:例如默认域模型采用 opset 18,并不意味着每个算子都有一个叫“版本 18”的实现;它采用该算子在不超过 18 的规范中适用的最新定义。自定义算子还需要匹配的域和运行时实现。ONNX 概念说明

用一个小模型理解计算图

跳转到“用一个小模型理解计算图”

令输入 X∈RN×3X\in\mathbb{R}^{N\times3},每个元素都计算:

Yij=2Xij+1.Y_{ij}=2X_{ij}+1.

这里的 2 和 1 是标量,通过广播作用到所有元素。N 是批量维,第二维固定为 3。符号维 N 表示尺寸可变;这个名称本身不声明“只能在 1 到 8 之间”。

flowchart LR
X["输入 X:N × 3,float32"] --> Mul["逐元素乘 2"]
Mul --> Add["逐元素加 1"]
Add --> Y["输出 Y:N × 3,float32"]

下面的独立脚本生成 affine.onnx。显式选择 opset 18、IR 9 是这个基础示例的兼容约定;真实模型应按所用功能和目标运行时确定版本。

import numpy as np
import onnx
from onnx import TensorProto, helper, numpy_helper
x_info = helper.make_tensor_value_info("x", TensorProto.FLOAT, ["N", 3])
y_info = helper.make_tensor_value_info("y", TensorProto.FLOAT, ["N", 3])
constants = [
numpy_helper.from_array(np.array(2.0, dtype=np.float32), name="scale"),
numpy_helper.from_array(np.array(1.0, dtype=np.float32), name="bias"),
]
graph = helper.make_graph(
[helper.make_node("Mul", ["x", "scale"], ["scaled"]),
helper.make_node("Add", ["scaled", "bias"], ["y"])],
"affine_demo", [x_info], [y_info], initializer=constants,
)
model = helper.make_model(
graph, producer_name="vitalogos-example", ir_version=9,
opset_imports=[helper.make_opsetid("", 18)],
)
onnx.checker.check_model(model, full_check=True)
onnx.save(model, "affine.onnx")
print("saved affine.onnx")

check_model 会检查模型结构与类型/形状一致性等约束;它没有用你的真实业务数据验证精度。

对刚生成的文件运行:

import onnx
model = onnx.load("affine.onnx")
onnx.checker.check_model(model, full_check=True)
print("IR:", model.ir_version)
print("opsets:", [(item.domain or "ai.onnx", item.version)
for item in model.opset_import])
for label, values in [("input", model.graph.input),
("output", model.graph.output)]:
for value in values:
tensor = value.type.tensor_type
shape = [dimension.dim_value if dimension.HasField("dim_value")
else dimension.dim_param or "?"
for dimension in tensor.shape.dim]
print(label, value.name,
onnx.TensorProto.DataType.Name(tensor.elem_type), shape)

该例应显示输入 x、输出 y,类型 FLOAT,形状 ['N', 3]。ONNX 的 FLOAT 对应此例使用的 32 位浮点数,不能仅因 Python 默认整数或浮点数“也能表示这些值”就换成其他 dtype。

大模型可能把权重保存为外部数据文件。复制或发布时,必须连同模型所引用的外部权重一起保留目录关系,不能只搬走 .onnx 文件。ONNX 外部数据说明

转换完成后的验证顺序

跳转到“转换完成后的验证顺序”
  1. 固定输入约定:名称、dtype、布局、尺寸范围、单位、归一化及输出语义。
  2. 检查模型结构、IR/opset 和目标运行时支持范围。
  3. 用同一输入分别执行原模型和 ONNX,比较对应输出。
  4. 覆盖正常输入、边界尺寸以及有业务意义的困难样本。
  5. 最后测量目标硬件上的性能;不要把首次加载、预热或输入拷贝漏出所声明的指标。

对于浮点输出,常用的逐元素判据为:

∣ytest−yref∣≤atol+rtol∣yref∣.|y_{\mathrm{test}}-y_{\mathrm{ref}}| \leq \mathrm{atol}+\mathrm{rtol}|y_{\mathrm{ref}}|.

容差由模型和业务确定。张量误差小仍可能改变分类边界、检测阈值或排序结果,因此还应比较最终任务指标。

本页模型构造、读取和结构检查已在 Python 3.11、ONNX 1.23.1 环境执行。版本记录描述本次验证环境,不代表所有项目都应升级到该版本。