4. YOLOv8 训练自己的数据集:数据标注格式、数据集划分、YAML 配置与参数调优
好,到了真正动手的环节了。前面我们聊了 YOLOv8 的架构和安装,但说实话,那些都是开胃菜。真正让模型为你工作的,是这一步——训练你自己的数据集。
我刚开始带项目的时候,总觉得训练模型是最高大上的部分。后来被现实狠狠教育了一顿——数据准备占了整个项目 70% 的时间。你想想看,模型再牛,喂进去的数据是垃圾,吐出来的只能是更精致的垃圾。所以这一章,咱们把数据标注、划分、配置、调参这几个硬骨头啃下来。
核心要点:一个高质量的数据集 + 合理的配置文件 = 训练成功的一半。另一半?嗯,是耐心。
4.1 数据标注格式:LabelImg vs Roboflow
YOLOv8 默认使用 YOLO 格式的标注文件。每个图片对应一个同名的 .txt 文件,内容长这样:
0 0.5 0.5 0.3 0.4
1 0.2 0.8 0.1 0.15
每行代表一个目标:类别索引 中心点x 中心点y 宽度 高度。注意,坐标都是归一化到 [0,1] 的。为什么要归一化?说白了,不管你的图片是 1920x1080 还是 640x480,模型看到的坐标范围都一样,训练更稳定。
那怎么生成这种标注文件?两个主流工具:LabelImg 和 Roboflow。
| 对比项 | LabelImg | Roboflow |
|---|---|---|
| 运行方式 | 本地安装,Python 或 exe | 在线网页,无需安装 |
| 标注格式 | 支持 YOLO、VOC、COCO 等 | 支持 30+ 格式导出 |
| 团队协作 | 单机,手动合并 | 云端多人协作 |
| 数据增强 | 无 | 内置旋转、翻转、马赛克等 |
| 适用场景 | 小规模、本地数据 | 中大规模、需要预处理 |
我个人习惯:如果数据量在几百张以内,用 LabelImg 就够了,简单直接。如果超过 1000 张,或者需要做数据增强,我会用 Roboflow。它那个在线标注界面确实比 LabelImg 顺手,而且导出 YOLO 格式一键搞定。
小技巧:LabelImg 默认保存的类别名是数字索引。记得在标注前先建一个 classes.txt,按顺序写好类别名,比如 person、car、dog。这样导出的标注文件索引才不会乱。
4.2 数据集划分:训练集、验证集、测试集
数据标完了,接下来要分家。YOLOv8 训练时默认需要两个目录:train 和 val。测试集不是必须的,但我建议你留一份,用来做最终评估。
常见的划分比例:
- 训练集:70% - 80%
- 验证集:10% - 15%
- 测试集:10% - 15%
目录结构长这样:
dataset/
├── images/
│ ├── train/
│ ├── val/
│ └── test/
└── labels/
├── train/
├── val/
└── test/
注意:图片和标注文件要一一对应,文件名必须相同(后缀不同)。
我写了一个简单的 Python 脚本来做划分,分享给你:
import os
import random
import shutil
def split_dataset(data_dir, train_ratio=0.8, val_ratio=0.1):
images = [f for f in os.listdir(os.path.join(data_dir, 'images')) if f.endswith(('.jpg', '.png'))]
random.shuffle(images)
n = len(images)
train_end = int(n * train_ratio)
val_end = int(n * (train_ratio + val_ratio))
splits = {
'train': images[:train_end],
'val': images[train_end:val_end],
'test': images[val_end:]
}
for split_name, file_list in splits.items():
os.makedirs(f'{data_dir}/images/{split_name}', exist_ok=True)
os.makedirs(f'{data_dir}/labels/{split_name}', exist_ok=True)
for f in file_list:
shutil.move(f'{data_dir}/images/{f}', f'{data_dir}/images/{split_name}/{f}')
label_file = f.replace('.jpg', '.txt').replace('.png', '.txt')
if os.path.exists(f'{data_dir}/labels/{label_file}'):
shutil.move(f'{data_dir}/labels/{label_file}', f'{data_dir}/labels/{split_name}/{label_file}')
split_dataset('dataset/')
避坑指南:我曾经犯过一个低级错误——划分前没有打乱数据。结果训练集全是白天的图片,验证集全是晚上的。模型在训练集上表现很好,一到验证集就崩。所以,一定要先随机打乱,再划分。
4.3 YAML 配置文件编写
YOLOv8 用 YAML 文件告诉模型:你的数据在哪、有哪些类别。这是训练前必须写好的文件。
一个标准的 dataset.yaml 长这样:
# 数据集路径
path: ./dataset # 数据集根目录
train: images/train # 训练集图片路径(相对 path)
val: images/val # 验证集图片路径
test: images/test # 测试集图片路径(可选)
# 类别信息
nc: 3 # 类别数量
names: ['person', 'car', 'dog'] # 类别名称列表
几个容易踩的坑:
path建议用路径或相对于 YAML 文件的路径。我习惯把 YAML 文件放在数据集根目录下,这样train和val直接写相对路径就行。nc必须和names列表长度一致。多一个少一个都会报错。- 类别名称的顺序要和标注文件里的索引对应。比如
person是 0,car是 1,dog是 2。
我的习惯:每次写完 YAML 文件,我都会用 yaml.safe_load() 验证一下格式。Python 的缩进问题在 YAML 里同样致命——一个空格错位,模型就找不到数据了。
4.4 训练参数调优
数据准备好了,YAML 写好了,终于可以开始训练了。但别急着跑,先看看几个关键参数。
YOLOv8 的训练命令:
yolo train model=yolov8n.pt data=dataset.yaml epochs=100 imgsz=640 batch=16
参数说明:
| 参数 | 默认值 | 说明 | 调优建议 |
|---|---|---|---|
model |
yolov8n.pt | 预训练模型权重 | 小数据集用 nano,大数据集用 large |
epochs |
100 | 训练轮数 | 先跑 50 轮看 loss 曲线,再决定是否增加 |
imgsz |
640 | 输入图片尺寸 | 显存不够就降到 416 或 320 |
batch |
16 | 批次大小 | 显存允许的情况下尽量大,但别超过 64 |
lr0 |
0.01 | 初始学习率 | 如果 loss 震荡,降到 0.001 |
patience |
50 | 早停耐心值 | 小数据集设 20,防止过拟合 |
我个人经验:刚开始调参时,别一上来就追求极致。先用默认参数跑一轮,看看 loss 曲线和 mAP 的变化。如果 loss 下降很慢,再考虑调学习率;如果 mAP 在验证集上停滞不前,可能是数据量不够或者标注质量有问题。
调参口诀:先跑通,再调优。loss 震荡降 lr,mAP 不涨查数据。
还有一个参数我特别想提——patience。这个参数控制早停机制:如果验证集上的 mAP 连续 patience 轮没有提升,训练就自动停止。我建议设成 20-30,既能节省时间,又不会错过最佳模型。
4.5 本章知识体系
下面这张图帮你理清整个流程:
从数据标注开始,到划分、配置、调参,每一步都环环相扣。别想着跳过任何一步——我见过太多人因为标注格式不对,训练到一半才发现,白白浪费十几个小时。