2. ONNX模型简化与验证
任务目标
对YOLO模型导出的ONNX文件执行计算图优化(常量折叠、冗余节点消除等),并在简化前后分别用相同输入运行推理,对比输出张量以验证数值一致性,确保简化不改变模型行为。
环境依赖
Python 3.8+,安装以下包:
pip install onnx onnxruntime onnx-simplifier numpy
完整实现
import onnx
import numpy as np
import onnxruntime as ort
from onnxsim import simplify
# 1. 加载原始模型
model_path = "yolov8n.onnx"
model = onnx.load(model_path)
# 2. 执行简化
model_sim, check_ok = simplify(model)
if not check_ok:
raise RuntimeError("ONNX简化验证失败(shape/值检查未通过)")
onnx.save(model_sim, "yolov8n_sim.onnx")
print(f"简化完成,节点数: {len(model.graph.node)} -> {len(model_sim.graph.node)}")
# 3. 构建推理会话
ort_session_orig = ort.InferenceSession(model_path, providers=["CPUExecutionProvider"])
ort_session_sim = ort.InferenceSession("yolov8n_sim.onnx", providers=["CPUExecutionProvider"])
# 4. 生成固定随机输入(与模型输入shape匹配)
input_name = ort_session_orig.get_inputs()[0].name
input_shape = ort_session_orig.get_inputs()[0].shape
# 将动态维度替换为具体值
input_shape = [1 if dim in ("batch", "N") else (640 if dim == 640 else dim) for dim in input_shape]
# 若仍有动态维度,手动指定
input_shape = [1, 3, 640, 640] if "dynamic" in str(input_shape) else input_shape
np.random.seed(0)
dummy_input = np.random.rand(*input_shape).astype(np.float32)
# 5. 分别推理
outputs_orig = ort_session_orig.run(None, {input_name: dummy_input})
outputs_sim = ort_session_sim.run(None, {input_name: dummy_input})
# 6. 逐输出对比
print(f"输出数量: {len(outputs_orig)}")
for idx, (o1, o2) in enumerate(zip(outputs_orig, outputs_sim)):
if o1.shape != o2.shape:
print(f"输出{idx} shape不一致: {o1.shape} vs {o2.shape}")
continue
max_diff = np.max(np.abs(o1 - o2))
mean_diff = np.mean(np.abs(o1 - o2))
print(f"输出{idx} shape={o1.shape} max_diff={max_diff:.6e} mean_diff={mean_diff:.6e}")
# 7. 一致性判定
all_close = all(
np.allclose(o1, o2, rtol=1e-4, atol=1e-5)
for o1, o2 in zip(outputs_orig, outputs_sim)
)
print("一致性判定:", "通过" if all_close else "失败")
运行方式
- 将待验证的ONNX文件命名为
yolov8n.onnx放在脚本同目录(或修改model_path变量指向实际文件); - 执行脚本:
python simplify_verify.py。
验证方法与通过标准
- 简化成功标志:脚本打印节点数变化,且
check_ok=True(onnx-simplifier内部已做shape与值校验); - 数值一致性:所有输出的
max_diff在1e-4量级以下,且最终打印“一致性判定: 通过”; - 输出文件:同目录生成
yolov8n_sim.onnx,可用Netron打开确认结构已简化。
常见问题
- 动态维度报错:若模型输入含动态batch或动态分辨率,脚本中已做基础替换;若仍失败,手动将
input_shape改为模型实际接受的固定shape(如[1,3,640,640])。 - 简化后无法加载:某些自定义算子不被onnx-simplifier支持,此时
check_ok会返回False,可改用skip_fuse_patterns参数或直接使用onnxruntime的GraphOptimizationLevel做运行时优化。 - 输出顺序差异:若模型有多个输出,onnxruntime返回顺序与模型定义一致,对比时按索引对应即可。