DocLayout-PageNumDet / misc /onnx_export_guide.md
wqzh's picture
Upload 31 files
1edc459 verified
|
Raw History Blame Contribute Delete
7.83 kB
# YOLO11 PT 转 ONNX 教程
## 一、使用命令行导出
```bash
yolo export model=path/to/model.pt format=onnx imgsz=640 half=False simplify=True
```
常用参数:
- `format=onnx`: 导出为 ONNX 格式
- `imgsz=640`: 输入图像尺寸,需与训练时一致
- `half=False`: FP32 精度;设为 True 为 FP16(需设备支持)
- `simplify=True`: 简化 ONNX 图结构,减小模型体积
- `dynamic=True`: 支持动态 batch 和输入尺寸
- `nms=True`: 将 NMS 内置到模型中
- `device=0`: 指定 GPU 设备号
## 二、使用 Python API 导出
```python
from ultralytics import YOLO
model = YOLO("path/to/model.pt")
model.export(
format="onnx",
imgsz=640,
half=False,
simplify=True,
device=0,
verbose=True
)
```
导出后的 ONNX 文件默认保存在与 PT 文件同级目录下,文件名为 `model.onnx`。
## 三、使用 ONNXRuntime 加载推理
```python
import cv2
import numpy as np
import onnxruntime as ort
sess = ort.InferenceSession("model.onnx", providers=["CUDAExecutionProvider"])
input_name = sess.get_inputs()[0].name
output_name = sess.get_outputs()[0].name
# 预处理
img = cv2.imread("image.jpg")
img = cv2.resize(img, (640, 640))
img = img.astype(np.float32) / 255.0
img = np.transpose(img, (2, 0, 1))
input_tensor = np.expand_dims(img, axis=0)
# 推理
outputs = sess.run([output_name], {input_name: input_tensor})
```
# FQA
## Q1: export_to_onnx 中 imgsz 参数设置为640,意味着推理的时候,输入的图片必须要是640x640大小的吗?
答案:不是必须的。
ONNX模型导出时,imgsz=640 默认会固定输入形状为 1, 3, 640, 640。推理时需要 resize + pad 到 640x640。
但如果导出时加 dynamic=True,可以支持动态尺寸输入。
我需要解释清楚这两点,简洁回答。
不是。分两种情况:
1. 默认导出(静态 shape):imgsz=640
- ONNX 输入被固定为 [1, 3, 640, 640]
- 推理时 必须 把图片处理成 640×640
- 但原始图片可以是任意尺寸,通过 resize + 等比填充 到 640×640(我代码里已经实现了)
2. 动态 shape 导出:加 dynamic=True
model.export(format="onnx", imgsz=640, dynamic=True)
- ONNX 输入为 [1, 3, ?, ?]
- 推理时可以用任意尺寸输入(如 320×320、800×800)
- 灵活性更高,但推理速度可能略慢
总结:你导出时写的 imgsz=640 只是指定训练/导出的参考尺寸,原始图片不需要是 640×640,推理前会 resize 处理。
## Q2: 默认参数设置
以下是 yolo export 各参数的默认值(来自 default.yaml):
|参数 | 默认值 | 说明 |
|--- | --- | ---|
|save name | 自动 | {原文件名}.{格式后缀},如 yolo11n.pt → yolo11n.onnx|
|nms | False | 不把 NMS 内置到模型,需自己后处理|
|simplify | True | 默认启用 ONNX 图简化|
|dynamic | False | 默认固定输入 shape|
|half | False | 默认 FP32 精度|
|batch | 1 | 导出时固定为 batch=1(与训练默认的16不同)|
|device | None | 自动选择(优先 GPU)
|verbose | False | 导出时静默输出(与训练默认的True不同)|
|int8 | False | 不启用 INT8 量化|
所以你这行命令:
yolo export model=page-nano-best-epo50.pt format=onnx imgsz=640 half=False simplify=True
等价于:
- 保存为 page-nano-best-epo50.onnx
- FP32,固定 shape [1,3,640,640]
- 不带 NMS(需要自己后处理)
- 简化 ONNX 图
- 静默输出
## Q3: 建议使用dynamic, nms 参数吗?
dynamic=True:
- 优点:推理时可以用任意尺寸,灵活性高
- 缺点:某些部署环境不支持,推理可能稍慢,某些优化器无法优化
nms=True:
- 优点:模型输出直接就是过滤后的结果,推理代码简单
- 缺点:NMS 参数固化在模型中,不方便调整;某些后处理流程可能更复杂
需要根据用户是用于 ONNXRuntime 推理的场景来分析。
分场景:
dynamic=True:
- ✅ 建议开:你需要同一模型处理不同尺寸的图片
- ❌ 不建议:
- 固定尺寸推理(如全 640×640)
- 部署到某些不支持动态 shape 的环境(TensorRT、部分嵌入式)
nms=True:
- ✅ 建议开:
- 想让推理代码更简单(模型直接输出最终结果)
- 固定 conf/iou 阈值即可
- ❌ 不建议:
- 需要频繁调 conf/iou(改了要重新 export)
- 需要自定义后处理逻辑(如按类别不同阈值)
你当前场景(ONNXRuntime + 自己写后处理代码):
dynamic=False # 固定 640x640 性能更好
nms=False # 你已经自己实现了后处理+NMS
如果未来想简化推理代码,可以重新 export:
yolo export model=xxx.pt format=onnx nms=True conf=0.25 iou=0.45
输出直接是 N x 7 的 [x1,y1,x2,y2,conf,cls_id],省去后处理。
## Q4: 如何知道目前模型的iou, conf?
这两个参数不在模型里,是你在推理时自己设定的。模型权重里只存网络参数,不存 conf/iou。
但你可以查模型训练时用的默认验证参数:
from ultralytics import YOLO
model = YOLO("models/page-nano-best-epo50.pt")
print(model.model.args)
看 conf 和 iou(或 nms.iou_thres)字段,这是训练验证时用的参考值。
实际建议:
- 用 yolo val 看不同阈值下的 mAP,选平衡点
- 检测任务常用:conf=0.25, iou=0.45(Ultralytics 默认)
- 如果你 export 时用了 nms=True,需要显式指定
## Q5: mAP50-95 偏低是什么情况?
## mAP50-95 < 0.5 的效果判断
**不能一概而论**,需要结合任务难度和模型规模来看。以下是通用参考:
---
## 不同场景下的参考标准
### 通用目标检测(COCO 80类,复杂场景)
| mAP50-95 | 水平 | 代表模型 |
|----------|------|---------|
| > 0.55 | 优秀 | YOLOv8x、YOLOv10x |
| 0.45 ~ 0.55 | 良好 | YOLOv8m、YOLOv8l |
| 0.35 ~ 0.45 | 中等 | YOLOv8s、YOLOv8n |
| < 0.35 | 较差 | 小模型/训练不足 |
> 在 COCO 这种**80类、复杂场景**的基准上,0.5 已经是不错的水平了,YOLOv8s 也才 0.44 左右。
### 简单/少类别任务(工业质检、特定场景)
| mAP50-95 | 水平 |
|----------|------|
| > 0.8 | 优秀,可上线 |
| 0.6 ~ 0.8 | 良好,基本可用 |
| 0.4 ~ 0.6 | 中等,需要优化 |
| < 0.4 | 较差,不建议上线 |
> 类别少、场景简单时,0.5 只能算**及格线**,通常还有较大提升空间。
---
## 0.5 以下意味着什么
1. **定位精度不足**:mAP50-95 对框的位置很敏感,低于 0.5 说明很多框"检测到了但框得不准"
2. **可能存在的问题**:
- 标注框质量差(标注本身就不准)
- 小目标多(小目标 IoU 天然低)
- 训练不充分(epoch 不够、数据量少)
- 模型太小(容量不足)
- 锚框不匹配
---
## 怎么判断能不能用
看你的**业务需求**:
- **只需要"有没有"**(如安防告警、粗略计数):看 mAP50,如果 mAP50 高(>0.8),即使 mAP50-95 低也能用
- **需要精确位置**(如医疗影像、工业测量、自动驾驶):mAP50-95 < 0.5 **不够用**,需要继续优化
- **需要区分密集目标**:mAP50-95 低可能导致框偏移,重叠目标分不清
---
## 提升 mAP50-95 的方向
1. **提高标注质量**:检查标注框是否紧密贴合目标
2. **增加训练轮次**:通常 300 epoch 比 100 epoch 定位更准
3. **用更大的模型**:m/l/x 版本定位精度更高
4. **数据增强**:Mosaic、MixUp 有助于定位
5. **调整锚框**:用 `autoanchor=True` 自动计算
6. **多尺度训练**:`imgsz` 设大一些(如 1280)
**总结**:如果是 COCO 级别的复杂任务,0.5 以下属正常中等水平;如果是简单少类别任务,0.5 以下说明还有明显优化空间。关键看你的 mAP50 是多少——如果 mAP50 高但 mAP50-95 低,问题主要在**定位精度**。