4. YOLOv8 ONNX 导出实战
好,咱们进入实战环节。前面讲了那么多理论,说白了都是为了这一步——把训练好的 YOLOv8 模型转成 ONNX,为后面的 TensorRT 部署铺路。
我个人习惯把 ONNX 导出看作是模型部署的“中间人”。它不负责最终推理,但没它,TensorRT 就吃不下你的模型。嗯,这里要注意,导出过程其实挺容易踩坑的,尤其是你第一次搞的时候。
核心目标:将 PyTorch 训练的 YOLOv8 模型(检测 & 分割)导出为 ONNX 格式,并验证其正确性。
4.1 导出 YOLOv8 检测模型
YOLOv8 官方其实已经把导出接口封装得很简单了。你不需要自己写复杂的 torch.onnx.export 脚本。直接用 CLI 或者 Python API 就行。
方法一:命令行导出(推荐新手)
yolo export model=yolov8n.pt format=onnx imgsz=640
就这么一行。它会自动处理输入输出、动态轴这些。我在项目中第一次用的时候,说实话,有点不敢相信这么简单。
方法二:Python API 导出(更灵活)
from ultralytics import YOLO
# 加载模型
model = YOLO('yolov8n.pt')
# 导出 ONNX
model.export(format='onnx', imgsz=640, dynamic=True, simplify=True)
这里我解释几个关键参数:
| 参数 | 作用 | 我的建议 |
|---|---|---|
imgsz |
输入图像尺寸 | 保持与训练时一致,通常是 640 |
dynamic |
动态 batch 尺寸 | 部署时建议开启,方便批量推理 |
simplify |
ONNX 模型简化 | 强烈建议开启,能去掉冗余算子 |
小技巧:如果你不确定导出是否成功,可以在命令行加个 --verbose 参数,它会打印详细的导出日志。我曾经靠这个日志定位过一次算子不兼容的问题。
4.2 导出 YOLOv8 分割模型
分割模型的导出和检测模型几乎一样,只是模型文件换成了分割版的。YOLOv8 的分割模型名字里带个 -seg。
# 命令行
yolo export model=yolov8n-seg.pt format=onnx imgsz=640
# Python API
model = YOLO('yolov8n-seg.pt')
model.export(format='onnx', imgsz=640, dynamic=True, simplify=True)
你可能会问,检测和分割导出的 ONNX 有什么区别?
区别在输出层。检测模型输出的是边界框和类别概率。分割模型除了这些,还会输出一个原型掩码(prototype masks)和掩码系数。嗯,这个后面讲 TensorRT 后处理时会细说。
注意:分割模型的 ONNX 文件通常比检测模型大 2-3 倍。因为多了掩码分支。导出时如果内存不够,可以试试 imgsz=480 或者关闭 dynamic。
4.3 验证导出的 ONNX 模型
导出完了别急着高兴。一定要验证一下。我见过太多人导出后直接拿去部署,结果推理结果全是乱的。
验证步骤:
- 检查模型结构:用
onnx.shape_inference或 Netron 可视化工具看一眼输入输出。 - 数值一致性验证:用同一张图在 PyTorch 和 ONNX 上跑,对比输出差异。
下面这个脚本是我常用的验证方法:
import onnx
import onnxruntime as ort
import numpy as np
import torch
from ultralytics import YOLO
# 1. 检查 ONNX 模型结构
onnx_model = onnx.load('yolov8n.onnx')
onnx.checker.check_model(onnx_model)
print("ONNX 模型结构检查通过")
# 2. 数值一致性验证
# PyTorch 推理
pt_model = YOLO('yolov8n.pt')
img = np.random.randn(1, 3, 640, 640).astype(np.float32)
pt_results = pt_model(img, verbose=False)
# ONNX Runtime 推理
ort_session = ort.InferenceSession('yolov8n.onnx')
ort_inputs = {ort_session.get_inputs()[0].name: img}
ort_results = ort_session.run(None, ort_inputs)
# 对比输出(这里只对比第一个输出的最大差异)
diff = np.max(np.abs(pt_results[0].cpu().numpy() - ort_results[0]))
print(f"PyTorch vs ONNX 最大差异: {diff:.6f}")
if diff < 1e-3:
print("✅ 验证通过!")
else:
print("❌ 差异过大,请检查导出参数")
关键指标:PyTorch 和 ONNX 的输出差异通常应该在 1e-3 到 1e-5 之间。如果差异大于 1e-2,说明导出有问题。
4.4 常见导出错误与解决
这部分是我最想跟你分享的。我在项目中踩过的坑,你大概率也会遇到。
错误 1:算子不支持
报错信息类似 Unsupported operator: torchvision::nms。YOLOv8 的后处理里用了非标准算子。
解决:导出时加上 opset=12 或者 opset=13,老版本的算子集更稳定。
model.export(format='onnx', opset=12)
错误 2:动态轴设置失败
报错 RuntimeError: Only batches with 1 image are supported。这通常是因为你开启了 dynamic=True,但模型里某些层不支持动态 batch。
解决:先关掉 dynamic,导出静态 batch 的模型。或者手动指定动态轴:
model.export(format='onnx', dynamic={'input': {0: 'batch'}})
错误 3:内存溢出
尤其是导出分割模型时,显存不够直接崩。
解决:降低 imgsz,或者用 CPU 导出(虽然慢,但稳)。
model.export(format='onnx', imgsz=480, device='cpu')
错误 4:简化失败
onnxsim 有时候会报错,说模型里有不支持的节点。
解决:先不简化,导出后再手动用 onnxsim 工具简化。
python -m onnxsim yolov8n.onnx yolov8n_simplified.onnx
我曾经踩过的坑:有一次我导出的 ONNX 模型在验证时差异很小,但部署到 TensorRT 后精度暴跌。查了两天才发现,是因为导出时忘了设置 half=False,模型自动用了 FP16,导致精度损失。所以,如果你要部署 FP16,导出时一定要用 FP32,让 TensorRT 自己去转精度。
4.5 本章知识结构图
下面这张图总结了 YOLOv8 ONNX 导出的完整流程和关键节点。你可以把它当作一个检查清单。
4.6 本章小结
说白了,YOLOv8 的 ONNX 导出并不复杂。核心就三步:选对模型、设对参数、做对验证。你只要记住我上面说的那几个坑,基本不会出大问题。
我个人建议,每次导出后都跑一遍数值验证脚本。别嫌麻烦。这个习惯能帮你省下后面 TensorRT 部署时的大量调试时间。