这篇是操作手册:目标是让一个空环境走到一个能检测自己定义类别的模型,并导出成可部署的格式。每一步给命令、预期输出和常见报错的解释;原理不展开,需要解释”为什么”的地方一句话点到为止。
一、开工前的三件事
硬件路线。三条路,代价和能力差别很大:本机有 NVIDIA 显卡,训练推理都在本地;没有显卡或显存太小,租按时长计费的云 GPU(Colab 的免费额度、AutoDL 一类国内平台,几块钱一小时),流程与本地完全一致;纯 CPU 只够跑推理和极小的训练任务,正式训练不现实。
示例任务。为了可复现,全文分两段走:先用 ultralytics 包内置的 coco8(8 张图的小数据集,首次使用自动下载)把”训练、验证、导出”全流程几分钟跑通;再换成自己的数据集,重复同样的命令。这样不必先花几天找数据、做标注,管线问题和个人数据问题不会搅在一起。
版本。写作时 ultralytics 包的版本是 8.4.x,v8、v11、v12 都由这一个包提供,用法一致。文中命令以官方文档为准,参数名隔几个版本偶尔会变。
二、环境搭建
建独立环境。不要装在 base 或系统 Python 里——torch 的 CUDA 版本会被其他项目锁死。二选一:
# conda
conda create -n yolo python=3.11 -y
conda activate yolo
# 或 venv
python -m venv .venv && source .venv/bin/activate
装 PyTorch。新手最大的坑在这一步:直接 pip install torch 装到的是 CPU 版,训练慢一个数量级,而且不报任何错。正确顺序是先查显卡驱动支持的 CUDA 版本:
nvidia-smi # 右上角显示驱动支持的 CUDA 上限
然后去 PyTorch 官网选对应的安装命令(CUDA 11.8、12.x 各有对应的一行),装完验证:
python -c "import torch; print(torch.cuda.is_available())" # 必须输出 True
输出 False 就不要往下走——后面所有”训练慢”的问题都会溯源到这里。
装 ultralytics 并验证环境:
pip install ultralytics
yolo predict model=yolov8n.pt source='https://ultralytics.com/images/bus.jpg'
第一次运行会自动下载 yolov8n 权重(约 6 MB)。下载慢或失败时,到 GitHub 的 ultralytics 仓库 releases 页手动下载对应权重的 pt 文件,放到命令的执行目录即可。成功后终端会打印检测到的目标,结果图存在 runs/detect/predict/ 下——能看到输出,环境就通了。
pip 下载慢时换国内镜像源(-i https://pypi.tuna.tsinghua.edu.cn/simple);Windows 用户注意两点:路径分隔符用正斜杠或双反斜杠,数据集路径避免中文。
工作目录建议分三块:datasets/ 放数据集、weights/ 放手动下载的权重、脚本单独一个目录。ultralytics 的运行产物固定写进发起命令时所在目录的 runs/ 下,所以从哪里执行命令要固定下来,否则产物会散落在各处。
三、先跑一次推理
环境验证和正式使用是同一条命令,值得把参数说清:
yolo predict model=yolov8n.pt source='bus.jpg' conf=0.25 iou=0.7 save=True
| 参数 | 作用 | 默认 |
|---|---|---|
| model | 权重路径或名称 | — |
| source | 图片、目录、视频、摄像头编号 | — |
| conf | 置信度阈值,低于它的框直接丢弃 | 0.25 |
| iou | NMS 的重叠阈值 | 0.7 |
| classes | 只保留指定类别,如 classes=0 | 全部 |
| save | 是否保存结果图 | True |
终端每次推理会打印一段汇总,大致长这样:
image 1/1 /path/bus.jpg 640x480 4 persons, 1 bus, 38.2ms
Speed: 5.0ms preprocess, 38.2ms inference, 120.0ms postprocess per image
Results saved to runs/detect/predict
一行一张图:检测到的目标按类别计数,后面是三个阶段的耗时——预处理、推理、后处理。读耗时分布对后面的调优很重要:推理耗时才是模型本身的成本,预处理和后处理慢属于工程问题,换思路解决而不是换模型。
结果存在 runs/detect/predict/,每次运行目录自动递增(predict2、predict3),不会覆盖。
这一步的意义不止是”看到效果”:训练自己的模型之后,唯一的区别是把 model 换成自己训练出的权重路径,其余命令一字不变。先把输出长什么样记熟,后面验收自己的模型时才不会陌生。
四、构建自己的数据集
目录约定。ultralytics 按固定结构组织数据,有一个必须知道的细节:它会自动把路径中的 images 替换成 labels 来找标注文件。所以图片放 images/train、标注放 labels/train,两边文件同名(仅扩展名不同):
datasets/mydata/
├── images/
│ ├── train/ # 训练图片
│ └── val/ # 验证图片
└── labels/
├── train/ # 每张图一个同名 txt
└── val/
标注格式每行一个目标:class_id cx cy w h,四个坐标按图片宽高归一化到 0 到 1,类别 id 从 0 开始。标错类别 id(从 1 开始数)是最常见的翻车点,症状是训练能跑、loss 正常、但某些类永远检不出来。
标注工具。个人项目用 labelimg 足够:pip install labelimg 装好启动,先把左侧的标注格式切成 YOLO(默认是 VOC 的 XML),再打开图片目录、指定标注保存目录;常用快捷键 w 画框、d 下一张、a 上一张、del 删框。它导出的 txt 与图片同名,直接放进 labels 目录即可。标注量上千或多人协作时换 CVAT,支持任务分发与进度管理。
训练数据的一个便捷来源是业务场景的视频:用 ffmpeg 每秒抽一帧(ffmpeg -i video.mp4 -vf fps=1 frames/%05d.jpg),比逐张截图高效得多——注意同一视频抽出的帧要划进同一个子集,否则相邻帧分跨训练与验证集,指标会虚高。
标注前先写三条规范,多人标注时尤其重要:遮挡目标框到可见部分还是整体轮廓;被画面边缘截断的目标怎么处理;框要贴多紧(贴着目标轮廓外一两像素,而不是留出大片背景)。框的口径不统一,模型学到的就是一套混合标准,指标怎么调都上不去。
改了标注要删缓存:首次训练时会在数据集目录生成 labels.cache 缓存文件,之后修改标注并不会自动刷新——症状是明明改了标注、训练却毫无变化。删掉各标注目录下的 .cache 文件再训练。
data.yaml 是数据集的说明书:
path: /absolute/path/to/datasets/mydata # 建议绝对路径,少一层解析歧义
train: images/train
val: images/val
nc: 2 # 类别数
names: ['helmet', 'head'] # 类别名,顺序与标注的 id 一一对应
划分数据集按 8:1:1 或 7:2:1,原则是同一场景的连拍不要跨训练集和验证集。划分本身是个十几行的脚本:收集全部图片路径,打乱,按比例切三份,把图片与同名标注复制到对应目录:
import random, shutil, pathlib
random.seed(0)
imgs = sorted(pathlib.Path('raw_images').glob('*.jpg'))
random.shuffle(imgs)
n = len(imgs)
splits = {'train': imgs[:int(n*0.8)],
'val': imgs[int(n*0.8):int(n*0.9)],
'test': imgs[int(n*0.9):]}
for name, files in splits.items():
(pathlib.Path(f'datasets/mydata/images/{name}')).mkdir(parents=True, exist_ok=True)
(pathlib.Path(f'datasets/mydata/labels/{name}')).mkdir(parents=True, exist_ok=True)
for f in files:
shutil.copy(f, f'datasets/mydata/images/{name}/{f.name}')
shutil.copy(f.with_suffix('.txt'), f'datasets/mydata/labels/{name}/{f.stem}.txt')
负样本:图里没有目标的图片照常放进 images、在 labels 里放一个空 txt 文件,对压误检有直接帮助,不要省。
五、训练
先跑通管线。用内置的 coco8 把全流程在几分钟内走一遍,确认环境和命令没有问题:
yolo train model=yolov8n.pt data=coco8.yaml epochs=10 imgsz=640
数据集会自动下载。这条命令跑通之后,训练自己的数据只是换一个 data 参数。
选模型档位。n、s、m、l、x 五档结构相同、宽深不同:n 最快最省显存,x 精度最高、最慢最吃显存。经验是边缘设备从 n 起步,单卡 8 GB 显存 s 或 m,效果不够再往上调——先小后大,每次只动一档。
正式训练自己的数据:
yolo train model=yolov8n.pt data=/absolute/path/to/data.yaml epochs=150 imgsz=640 batch=16
关键参数逐个说:
| 参数 | 作用 | 建议 |
|---|---|---|
| epochs | 总轮数 | 100 到 300 起步,配合早停 |
| batch | 每批图片数 | 显存不够可设 -1(自动探测) |
| imgsz | 输入尺寸 | 默认 640;小目标多就调大,代价是显存 |
| device | 用哪块卡 | 0;多卡写 0,1 |
| workers | 数据加载进程 | Windows 报多进程错误时设 0 |
| patience | 早停耐心值 | 默认 100,验证指标多少轮不涨就停 |
| close_mosaic | 最后几轮关闭 Mosaic 增强 | 默认 10,让收尾见到真实分布 |
| cache | 数据缓存进内存 | 数据集不大且内存充足时开 |
训练产物在 runs/detect/train/(重复运行递增):
weights/best.pt:验证集上最优的一版,部署用它weights/last.pt:最后一轮的权重,断点续训用它results.png:损失与指标曲线confusion_matrix.png、PR_curve.png、F1_curve.png、labels.jpg:评估图args.yaml:本次训练的全部参数,复现实验就靠它
训练曲线的三个信号:验证损失已经平台化就该早停;验证损失掉头向上是过拟合;训练损失还在降而验证指标不动,说明增强过度或数据不足。
习惯写代码的话,CLI 的每个参数在 Python API 里一一对应:
from ultralytics import YOLO
model = YOLO('yolov8n.pt')
model.train(data='/absolute/path/to/data.yaml', epochs=150, imgsz=640, batch=16)
需要循环做多组实验时,API 比反复敲命令顺手。多卡训练把 device 写成 ‘0,1’,batch 会自动均分到各卡。
训练时终端每个 epoch 打一行:三个损失(框回归、分类、DFL)、本批目标数与验证指标。最初若干轮指标乱跳是正常的——模型正从 COCO 预训练状态向新类别迁移,看趋势不看单点。
显存不足的三招按顺序试:batch 降(16 → 8 → 4)、imgsz 降(640 → 512)、模型换 n 档。中断后续训:
yolo train resume model=runs/detect/train/weights/last.pt
训练时长的估法很朴素:先设 epochs=10 跑一次,用这 10 轮的耗时乘以目标轮数外推。数据量、imgsz、batch、硬件四个变量都在影响它,别人的数字没有参考价值,自己的外推才有。
六、验证与读结果
yolo val model=runs/detect/train/weights/best.pt
输出的末尾长这样:
Class Images Instances Box(P R mAP50 mAP50-95)
all 128 929 0.867 0.804 0.888 0.642
helmet 128 214 0.912 0.876 0.921 0.703
head 128 715 0.831 0.742 0.859 0.592
两组数:mAP@0.5 与 mAP@[0.5:0.95],后者才是严格口径;还有一张按类别拆开的表——这张表往往比总分有用,哪一类低、是精度低还是召回低,一眼可见。
训练目录里的评估图各有用途:results.png 看损失与指标曲线(平台化早停、反弹过拟合);混淆矩阵看类别互混和 background 行列(误检与漏检的分布);F1 曲线的峰值横坐标是部署时置信度阈值的推荐值;PR 曲线看哪一类在高召回区塌得快。
错误归因按固定三类走:漏检(有目标没框出来)查场景覆盖与标注、误检(框了不存在的东西)查负样本与置信度阈值、定位不准(类别对但 IoU 不够)查小目标占比与输入尺寸。一次只改一件事,改完用同一份验证集重评。
一个自检信号值得单独说:验证指标高得离谱时,先检查数据划分——同一场景的图如果同时出现在训练集和验证集,指标会好看但毫无意义。
七、部署导出
导出 ONNX:
yolo export model=runs/detect/train/weights/best.pt format=onnx
得到 best.onnx 后,最省事的推理方式是继续用 ultralytics 的命令行(装上 onnxruntime 包即可),前后处理与后处理框架都已内置:
pip install onnxruntime
yolo predict model=best.onnx source='test.jpg'
需要集成到自己的程序里时,用 onnxruntime 的会话接口:输入是 letterbox 缩放并归一化后的 640×640 张量,输出需要自己做解码与 NMS——前后处理的坐标换算细节要照着官方 predict 流程实现,这是自研集成的主要工作量。
其他目标:NVIDIA GPU 上要极限性能时导出 TensorRT(format=engine,需本机安装 TensorRT);Jetson 一类边缘盒子同样走 TensorRT 路线;Intel CPU 用 OpenVINO。导出后务必用同一批验证图对比精度——量化(int8)有损失,fp16 几乎没有。
部署时的阈值不要沿用训练评估的默认值:按业务对漏检和误报的代价重新定。F1 曲线的峰值是个起点,业务偏防漏检就往下调、偏防误报就往上调。
部署形态按场景三选一:本地程序直接加载模型文件;服务端做成接口(模型常驻内存,注意并发与批处理);边缘设备走对应硬件的推理框架。共同的要点是:训练框架与部署环境分离后,每一次模型更新都要重跑一遍验证集,确认精度没有在转换中损失。
八、常见问题速查
| 类别 | 症状 | 原因与处理 |
|---|---|---|
| 环境 | torch.cuda.is_available() 为 False | 装成了 CPU 版 torch,按官网 CUDA 命令重装 |
| 环境 | 权重下载失败或极慢 | 到 GitHub releases 手动下载放到当前目录 |
| 环境 | pip 安装超时 | 换国内镜像源 |
| 数据 | 某些类永远检不出 | 类别 id 未从 0 开始,或 names 顺序与 id 不符 |
| 数据 | 报错找不到标注文件 | 目录结构不合规:images 必须与 labels 对应 |
| 数据 | 中文路径报错 | 数据集路径避免中文,或统一用正斜杠 |
| 训练 | 显存溢出(CUDA out of memory) | batch 降、imgsz 降、换 n 档 |
| 训练 | Windows 多进程报错 | workers=0 |
| 训练 | 训练很慢但没报错 | torch 是 CPU 版;或 workers 设太小 |
| 训练 | 中断后想继续 | yolo train resume model=…last.pt |
| 评估 | 验证指标高得离谱 | 数据划分泄漏,按场景重新划分 |
| 评估 | mAP@0.5 高而 @0.5:0.95 低 | 定位是短板,查小目标占比与输入尺寸 |
| 数据 | 改了标注训练没变化 | labels.cache 缓存陈旧,删除数据集里的 .cache 文件 |
| 使用 | 摄像头输入没反应 | source=0 传整数是设备号,字符串才是文件路径 |
| 训练 | 想多卡并行 | device 写 ‘0,1’,batch 自动均分 |
| 部署 | 导出后精度下降 | fp16 可忽略,int8 需重新验证 |
| 部署 | onnxruntime 推理结果异常 | 前处理的 letterbox 与坐标还原没对齐 |
九、跑通之后
这套命令行对检测之外的任务完全同构:实例分割用 yolov8n-seg.pt 这类权重,姿态估计用 -pose,旋转框(OBB)用 -obb,分类用 -cls,data.yaml 与训练、验证、导出的流程一致。多目标跟踪用 yolo track。全部参数用 yolo help 或官方文档查。
流程走通只是起点,接下来真正拉开效果的是三件事:按第六节的错误分析持续修数据、按业务重新标定置信度阈值、以及建立线上数据的回流机制。检测模型的上限从来不是训练参数决定的,是数据决定的。
参考
- Ultralytics 官方文档 — 命令行参数、data.yaml 字段与导出格式的权威来源
- ultralytics 仓库 — 包源码与 releases(权重手动下载处)
- PyTorch 官网 — 按驱动与 CUDA 版本选择安装命令
- labelimg — 轻量标注工具
- CVAT — 团队标注平台
- onnxruntime — ONNX 推理运行时
- TensorRT — NVIDIA 推理加速