Qiming Bao · Blog · 2026 年 11 月 12 日 · English / 中文

ONNX 图里三个容易被工具忽略的细节

这周我修了 ONNX 工具链里的三个 bug:两个在 ONNX Runtime 的图优化器里,一个在我自己写的导出代码里。它们都没有让程序崩溃,只是让模型跑得比应有的慢一点,或者让测试只在 CI 上失败。三个问题都来自 ONNX 格式里一些很小、写图优化或导出器时容易忽略的细节。

1. 空名字的可选输入,不等于省略的输入

ONNX 里省略一个可选输入有两种写法:直接不写在输入列表末尾,Conv(x, w);或者写成空名字,Conv(x, w, "")。两种都表示"没有 bias",也都是合法的。

ONNX Runtime 的 Conv+BatchNormalization、Conv+Add 和 Conv+Mul 融合用 InputDefs().size() == 3 判断有没有 bias。写成 "" 时 Conv 有三个输入,于是走了"有 bias"的分支;接着检查 bias 是不是常量,空输入当然不是,融合就在所有优化级别下都被跳过了(microsoft/onnxruntime#34139)。只把它当成"没有 bias"还不够:没有 bias 的分支用的辅助函数只能往输入列表末尾追加新 bias,而第 3 个位置已经被 "" 占了。

修复(microsoft/onnxruntime#34141)同时检查 InputDefs()[2]->Exists(),并改用一个能填充空位置、也能处理省略情况的辅助函数来设置新的 bias。

写图优化的规则:可选输入只有在位置存在并且名字非空时才算存在,只数输入个数是不够的。

2. 带切片的 Shape 只需要选中的维度已知

从 opset 15 开始,Shape 支持 start 和 end,比如 Shape(x, start=1) 返回除第一维以外的所有维度。导出的模型里经常这样用,通常是为了让符号化的 batch 维不进入 Reshape。

ONNX Runtime 的常量折叠先收集所有维度的值,遇到第一个符号维度就放弃,然后才应用切片。对于 x: [N, 3],Shape(x, start=1, end=2) 永远是 [3],却因为 N 没有被折叠(microsoft/onnxruntime#34140)。修复(microsoft/onnxruntime#34142)先按秩对 start/end 做截断,再只要求选中的维度已知:

输入 [N, 3, 4, M],start, end修复前修复后
1, 3不折叠折叠为 [3, 4]
-3, -1不折叠折叠为 [3, 4]
0, 2(包含 N)不折叠不折叠

写图优化的规则:先应用算子自己的属性,再判断它的输入"是不是不够静态"。

3. onnx 1.21 之前不接受用 Constant(value_ints) 作为 Pad 的 pads

这个是我自己的 bug。我给 onnxscript 写了一个 adaptive_avg_pool 的 torchlib 实现(microsoft/onnxscript#3201),里面有一个 Pad,它的 pads 来自 op.Constant(value_ints=[...])。本地测试全部通过,CI 上几乎所有任务都失败了:

[ShapeInferenceError] 'pads' input must be a 1D (shape: [2 * num_axes]) tensor of type int64

CI 固定使用 onnx==1.18,而我本地是 onnx 1.23。用一个只包含 Pad 的最小模型就能看出区别,pads 是同样的六个 int64 值,只是构造方式不同:

onnxConstant(value_ints=...)Constant(value=tensor)
1.18、1.19、1.20形状推断报错正常
1.21、1.23正常正常

把 pads 写成张量在所有版本上都能工作。更一般的教训是:用 CI 固定的依赖版本跑测试,而不只是用本地环境里的版本。我现在保留了一个和 onnxscript CI 版本一致的虚拟环境,推送之前先在里面跑一遍。

一个共同点

这三个 bug 都藏在一个"默认情况能正常工作"的背后:大多数模型直接省略不用的 bias,而不是写 "";大多数 Shape 节点取完整的形状;大多数开发者装的是较新的 onnx。只构造常见写法的测试都能通过。对这三个修复来说,真正有用的测试是专门构造那种少见但合法的写法,再和常见写法对比。

编写修复、构造复现和撰写本文时,我使用了 Claude(Anthropic)辅助。所有结果都经过我本人审阅,测试也由我亲自运行。


包启明,现居新西兰奥克兰的 AI 研究者,研究方向为大模型推理与模型部署。博士毕业于奥克兰大学 Strong AI Lab,参与 PyTorch ONNX 导出器、ONNX Runtime、ONNX Runtime GenAI 和 Microsoft PyRIT 等开源项目的贡献。