跳转到内容
新建笔记

ONNX Simplifier:简化图并验证数值

模型导出后,图中可能留有常量计算、多余的形状变换或可化简的运算。ONNX Simplifier 尝试改写这些结构,使图更简单,便于运行时优化。节点数下降不直接等于延迟下降;目标后端可能已经做了相同优化,也可能对某种改写不够友好。

简化应保持目标输入范围内的输出语义。它不等于量化,也不负责自动补齐 TensorRT 不支持的任意算子。项目说明

保留输入文件,另存简化结果

跳转到“保留输入文件,另存简化结果”

先按 ONNX 基础 生成 affine.onnx,然后执行:

from pathlib import Path
import numpy as np
import onnx
import onnxruntime as ort
from onnxsim import simplify
source = Path("affine.onnx")
target = Path("affine.simplified.onnx")
if source.resolve() == target.resolve():
raise ValueError("keep the original model in a separate file")
original = onnx.load(source)
onnx.checker.check_model(original, full_check=True)
simplified, check_ok = simplify(
original, check_n=3, test_input_shapes={"x": [2, 3]}
)
if not check_ok:
raise RuntimeError("simplifier comparison failed")
onnx.checker.check_model(simplified, full_check=True)
onnx.save(simplified, target)
before = ort.InferenceSession(str(source), providers=["CPUExecutionProvider"])
after = ort.InferenceSession(str(target), providers=["CPUExecutionProvider"])
assert [item.name for item in before.get_outputs()] == [
item.name for item in after.get_outputs()
]
assert after.get_inputs()[0].shape == ["N", 3]
rng = np.random.default_rng(53)
for batch in (1, 2, 8):
x = rng.normal(size=(batch, 3)).astype(np.float32)
expected = before.run(None, {"x": x})
actual = after.run(None, {"x": x})
for left, right in zip(expected, actual):
np.testing.assert_allclose(right, left, rtol=1e-6, atol=1e-6)
print("nodes:", len(original.graph.node), "->", len(simplified.graph.node))
print("dynamic batches 1, 2, 8 passed")

check_n=3 要求工具进行三次测试;test_input_shapes 给动态输入指定测试时使用的尺寸。它与把模型输入永久固定为某个尺寸的选项含义不同。本例随后独立检查 1、2、8 三种批量,并确认符号维仍在。

这个 Mul → Add 小图已经简洁,节点数可能保持为 2。不能为了追求“优化比例”而把没有变化误判为失败。

在普通终端中使用下面的形式,具体选项以当前安装版本的帮助为准:

终端窗口
python -m onnxsim --help
python -m onnxsim affine.onnx affine.simplified.onnx

原 ES6D 项目的文件名也可沿用这个分离输入输出的原则:

终端窗口
python -m onnxsim models/es6d/estimator_weigh.onnx models/es6d/estimator_weigh_simplify.onnx

Jupyter 中的 !python ... 是笔记本的 shell escape,不能把前面的 ! 原样复制到普通 Bash 命令里。

随机样本只能验证已经覆盖的输入,不是对所有输入的等价性证明。实际发布前继续检查:

项目要求
接口输入输出名称、数量、dtype、形状约定保持预期
数值使用真实样本和边界尺寸,对每个输出逐一比较
特殊分支空目标、阈值附近、极端取值、动态尺寸等按业务覆盖
后端简化后仍能被目标运行时或 TensorRT 解析
性能同设备、同尺寸、同精度、同计时边界测量
追溯保存原模型、简化模型、版本、命令、模型哈希与验收结果

若简化导致不兼容,先定位具体改写和后端约束;不要通过忽略失败断言,把问题留给线上推理。

本页 Python 示例已用 ONNX 1.23.1、ONNX Runtime 1.30.0、onnxsim 0.7.3 执行。ES6D 命令保留原项目路径含义,未提供该模型文件,因此没有声称验证过其转换结果。