☰
YOLOv8训练自定义数据集全流程:从标注、配置到部署导出
2026/10/11 1:09:21 网站建设 项目流程

简介:YOLOv8目标检测训练自定义数据集的全流程操作指南,面向需要从零准备数据并完成模型训练的开发者与学习者。文档以超详细步骤串联整个训练流程,先介绍YOLOv8的单阶段检测原理与适用场景,再逐步讲解requirements.txt与ultralytics包的安装、图片和xml标注文件的整理、split_train_val.py数据集切分、voc2yolo.py格式转换,以及预训练模型下载与训练命令。对task、mode、model、data、epochs、batch等关键训练参数逐项解读,并针对训练中常见的Arial.ttf字体下载报错给出解决方法,还补充了resume断点续训的具体用法,可帮助读者规避多数入门阶段的坑。资源为单个docx文档,大小2.18MB,便于离线阅读和按步骤对照操作,文档内还标注了注意事项与可直接复制的命令。目前已有448人学习下载,适合刚接触YOLOv8或希望在自定义数据集上快速跑通目标检测流程的用户参考。

1. YOLOv8训练自定义数据集:先搞清这套流程到底要准备什么

做安全帽检测的时候,我拿着网上现成的 YOLOv8 预训练权重往工地视频上一跑,效果惨不忍睹——正常戴着的帽子被识别成安全帽,没戴的反而漏检。问题不在模型,在于预训练权重是在 COCO 数据集上训的,里面根本没有“安全帽”这个类别。YOLOv8训练自定义数据集,就是把你自己拍好的、标注过的图片,通过 Ultralytics 框架训练成一套属于自己场景的检测权重。这套流程适合安全帽检测、工业缺陷检测、自定义目标识别这类场景,也适合刚接触 yolov8 环境配置的学生。核心工作只有四件事:标注数据、转成 YOLO 格式、改配置文件、跑通训练命令。这篇笔记按这个顺序,把每一步怎么做、参数怎么设、坑在哪写清楚。

2. 数据与标签准备:为什么 YOLO 格式的 .txt 文件决定了训练成败

2.1 环境安装:Ultralytics 版本与 PyTorch 匹配

训练环境是所有步骤里最容易翻车的一环,很多人在这一步卡了一整天。常见的做法是用 conda 或 venv 建一个独立环境,再装 ultralytics 包。推荐用 pip 直接安装,因为 ultralytics 会同时拉取依赖的 torch、torchvision、opencv 等库,省去手动对版本的麻烦。

conda create -n yolo python=3.10 -y conda activate yolo pip install ultralytics python -c "import ultralytics; print(ultralytics.__version__)"

装完之后先确认版本号能打印出来,再往下走。如果 import 报错,大概率是 torch 与 CUDA 版本不匹配。比 如 本 地 是 CUDA 11.8,就 需 要 先 手 动 装 对 应 的 torch 版 本,再 装 ultralytics。GTX 1660 Ti 跑 YOLOv8 完全够用,显存虽然只有 6GB,但选 n 或 s 模型、把 batch 调到 8,训练自定义数据集没有压力。注意 conda activate 之后要确认 python 路径指向的是环境内的 python,否则后面跑 yolo 命令时可能用的是全局环境。

2.2 数据集目录结构:训练集、验证集与标签文件的对应关系

YOLOv8 对数据集目录结构有明确约定,不按这个结构放,训练器要么报错说找不到标签,要么静默跳过所有图片。以安全帽检测为例,我一般这样组织:

helmet/ ├── images/ │ ├── train/ │ ├── val/ │ └── test/ ├── labels/ │ ├── train/ │ ├── val/ │ └── test/ └── helmet.yaml

images 和 labels 必须在同一个上级目录下,且 train/val/test 三个集合在两侧保持一一对应。比如 images/train/a.jpg 对应的标签文件必须是 labels/train/a.txt,文件名一致、后缀不同。每张图片对应一个同名 txt 标签文件,里面每一行代表一个目标框,格式是固定的五列:class_id x_center y_center width height,后四列全部归一化到 0~1 之间。

训练集、验证集、测试集的划分比例我一般用 8:1:1,数据量少到只有几百张时至少也要保证验证集有 50 张以上,否则 mAP 波动会非常大。划分时要注意按场景或按视频帧来分,不能把同一段视频的连续帧同时分进 train 和 val,否则验证集和训练集高度相似,得到的 mAP 虚高,部署到真实场景直接缩水。

2.3 VOC 标注转 YOLO 格式:转换脚本与四个边界坑

大多数标注工具导出的不是 YOLO 格式,而是 VOC 的 XML 或 COCO 的 JSON。我最常用的是 LabelImg 导出的 VOC XML,然后写一个小的 Python 脚本转成 YOLO txt。转换逻辑很简单:从 XML 里读出每个 object 的 bndbox 四个坐标,再结合图片宽高做归一化。

import xml.etree.ElementTree as ET import os from glob import glob classes = ['helmet', 'person'] # 必须与 data.yaml 中的 names 顺序一致 def convert_xml_to_yolo(xml_path, txt_path): tree = ET.parse(xml_path) root = tree.getroot() size = root.find('size') img_w = int(size.find('width').text) img_h = int(size.find('height').text) lines = [] for obj in root.iter('object'): name = obj.find('name').text if name not in classes: # 跳过未定义类别的目标 continue box = obj.find('bndbox') xmin = int(box.find('xmin').text) ymin = int(box.find('ymin').text) xmax = int(box.find('xmax').text) ymax = int(box.find('ymax').text) # 四个边界值转中心点加宽高,再归一化 x_center = (xmin + xmax) / 2.0 / img_w y_center = (ymin + ymax) / 2.0 / img_h w = (xmax - xmin) / img_w h = (ymax - ymin) / img_h lines.append(f"{classes.index(name)} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}") with open(txt_path, 'w') as f: f.write("\n".join(lines)) xml_list = glob('helmet/labels_xml/*.xml') # VOC 原始标注目录 for xml_path in xml_list: base = os.path.basename(xml_path).replace('.xml', '.txt') convert_xml_to_yolo(xml_path, os.path.join('helmet/labels/train', base))

这段代码的核心在于 classes 列表,它决定了每个目标框的类别 ID。如果 data.yaml 里 names 的顺序是 ['person', 'helmet'],而这个脚本里 classes 是 ['helmet', 'person'],那么所有标签全部错位,训练出来模型行为完全不可用。另外四个容易踩的坑:第一,xmax 小于 xmin 时宽高为负,说明标注时框拉反了,脚本里要加判断跳过或报错;第二,归一化后坐标大于 1,说明 XML 的 size 字段与图片实际尺寸不一致,常见于经过裁剪或缩放的图片;第三,一个 txt 文件里重复写了同一个目标,Focal Loss 和增强逻辑会把重复目标当多个样本处理;第四,classes 里漏掉某个类别时不要静默跳过,最好打印一条警告日志方便核对。

转换完之后,我习惯随机抽 10 张图,把 YOLO 格式的框画回原图上检查,确认坐标没有错位。这一步虽然费几分钟,但能避免在训练完成后才发现数据问题,到那时定位成本已经高了几十倍。

3. 训练配置与命令:data.yaml、模型选型与超参数的搭配逻辑

3.1 data.yaml 怎么写:路径、类别与 ID 三者必须对齐

YOLOv8 启动训练时首先读的就是 data.yaml。这个文件如果不配好,后续所有步骤都会在数据加载阶段失败。最常见的做法是把 data.yaml 放在数据集根目录下,比如刚才 helmet/ 目录里就放一个 helmet.yaml。

path: D:/datasets/helmet # 数据集根目录的绝对路径 train: images/train # 相对 path 的训练集图片目录 val: images/val # 相对 path 的验证集图片目录 test: images/test # 可选,推理评估时用 names: 0: helmet 1: person

这里的 path 我建议直接写绝对路径,不要用相对路径,因为 yolo 命令的执行目录很容易和数据集目录不一致。train 和 val 是相对于 path 的子路径,不要写成 images/train/ 这种带尾部斜杠的形式,YOLOv8 自己在解析时会拼接成 path + train,多余的斜杠容易在 Windows 上触发 FileNotFoundError。

names 是整个训练里最不能错的部分,它的索引 0 和 1 必须与标签文件里每行的第一个数字一致。数据集中只要有一个类的顺序对不上,训练日志里 loss 照常下降,但验证集 mAP 永远是 0。

3.2 模型选型:n/s/m/l/x 怎么选,GTX 1660 Ti 该用哪个

YOLOv8 官方提供了 n、s、m、l、x 五种规格,参数规模从约 3M 到约 68M 递增。对于自定义数据集,选型逻辑取决于两个变量:数据量大小和目标设备算力。

模型参数量推理速度适用场景
YOLOv8n最小最快边缘设备、RK3588 这类 NPU 平台、数据量小于 1000 张
YOLOv8s小快普通 GPU、中等数据量 1000~5000 张
YOLOv8m中等中精度优先、显存 8GB 以上
YOLOv8l大慢服务器端、数据量大且目标小
YOLOv8x最大最慢追求极限精度,一般不需要

如果你用的是 GTX 1660 Ti 6GB 显存,m 模型在 batch 为 8 的情况下已经有点吃力,推荐从 n 或 s 开始。n 模型在几百张图片的小数据集上训练一轮只要几分钟,先把流程跑通,再换 s 模型追求精度。训练命令里的 model 参数可以指定预训练权重路径,比如 yolov8s.pt,框架会自动下载 COCO 上的预训练权重,用它作为初始参数,比从零训练收敛更快、精度更高。

3.3 启动训练:核心命令与关键超参数的设定方式

跑通整个训练只靠一条命令,但这一条命令里每个参数都值得单独理解。

yolo detect train data=helmet.yaml model=yolov8s.pt \ epochs=100 imgsz=640 batch=8 device=0 \ patience=20 workers=4 seed=42

逐段解释一下:data 指定刚才写的 yaml 文件;model 是预训练权重路径,yolov8s.pt 会自动下载;epochs 是完整跑多少轮,100 轮对大多数自定义数据集足够,数据量少于 500 张时建议 150 轮并配合早停观察;imgsz 是训练时输入图像的边长,默认 640,如果你的目标在图像中很小,可以适当降到 512 或 416,小目标识别反而会更稳;batch 是每次迭代喂入的图片数量,8 是 6GB 显存的稳定值,显存不够优先降 batch,不要先降 imgsz,因为降 imgsz 会影响目标尺度分布;device=0 指定第一块 GPU,CPU 训练则写 device=cpu,但速度慢几十倍,不推荐。

patience 是早停参数,意思是如果连续 20 轮验证集 mAP 都没有提升,训练自动停止。这里建议设成 20 而不是默认的 100,因为自定义数据集验证集波动大,早停能省下大量无效训练时间。sv 命令执行完后会在 runs/detect/train 目录下生成训练日志、权重文件和 results 曲线图,后面所有分析都围绕这个目录展开。

4. 训练过程监控与调参:怎么从损失曲线和 mAP 判断模型状态

4.1 损失函数曲线图怎么看:box_loss、cls_loss、dfl_loss 的下降节奏

训练开始后,runs/detect/train/ 目录下会生成一个 results.png,里面包含 box_loss、cls_loss、dfl_loss 三条损失曲线,以及 precision、recall、mAP50、mAP50-95 四条指标曲线。很多新手只会盯着 loss 看它降到多少,但其实损失曲线的形态比数值更值得关注。

正常训练时三条 loss 应该在前期快速下降,中后期趋于平缓。以安全帽单类别数据集为例,box_loss 会在第 30 轮左右降到 1.0 以下并逐渐稳定到 0.8 附近;cls_loss 因为只有两个类别,通常不会太高,稳定在 0.5 上下;dfl_loss 是分布焦点损失,反映框回归的对齐程度,一般也在 1.0 左右。如果某一条 loss 在训练后期不降反升,说明这个任务已经接近过拟合或该指标对应的模块出了问题。比如 dfl_loss 后期上升,常见原因是标签框本身标注不准确,模型学到了错误坐标。

4.2 验证集 mAP50 与 mAP50-95:两个指标分别说明什么问题

训练结束后,日志会打印一行验证集结果,其中两个最关键的指标是 mAP50 和 mAP50-95。mAP50 是预测框与真实框 IOU 大于 0.5 即算正确的平均精度,它代表“大致找得到目标”的能力;mAP50-95 则把 IOU 阈值从 0.5 到 0.95 每 0.05 步进一次,再取平均,它代表框定位的精准程度。两个指标差距越大,说明框定位越粗糙,越容易在部署时出现“检测到了但框偏了”的情况。

对于安全帽这类中等尺寸目标,两者差距通常在 0.1 以内;如果是检测小目标比如远处的人头,mAP50 可能到 0.9 但 mAP50-95 只有 0.5,这并不异常。需要警惕的是 mAP50 很低而 loss 也低,这大概率是标签类别 ID 错位,模型一直在学错误映射。

4.3 用 YOLOv8 的 results.csv 自己画损失曲线图

YOLOv8 虽然自动生成 results.png,但有时训练中断或想对比多次实验,直接从 results.csv 画图更方便。每个训练任务目录下都有一个 results.csv,每一列对应一个指标。用 pandas 读进来,选择特定列绘图即可。

import pandas as pd import matplotlib.pyplot as plt df = pd.read_csv('runs/detect/train/results.csv') # 不同版本列名可能有差异,先打印 df.columns 确认 print(df.columns) plt.figure(figsize=(10, 6)) plt.plot(df['epoch'], df['train/box_loss'], label='train box_loss') plt.plot(df['epoch'], df['val/box_loss'], label='val box_loss') plt.xlabel('epoch') plt.ylabel('loss') plt.legend() plt.grid(True) plt.savefig('my_box_loss.png', dpi=200)

这段代码里要注意两件事:一是 results.csv 的列名在不同 ultralytics 版本中并不完全一致,旧版可能是 train/box_loss,新版可能是 train/box_loss 或 train/box_om,打印 columns 确认后再写列名;二是画图不只是给自己看,也是定位过拟合的依据。当 train loss 持续下降但 val loss 在第 60 轮拐头向上时,基本可以断定过拟合,早停参数 patience 会在第 80 轮左右提前截断训练,这时候去改模型结构意义不大,优先检查数据增强是否合理、数据量是否充足。

5. 避坑:训练自定义数据集最常见的 5 个坑(现象、原因、解决)

5.1 训练到一半 loss 直接变 nan,损失曲线崩成竖线

现象:训练日志里 loss 从某个 epoch 开始变成 nan,或者数值瞬间飙升到 1e30,之后全部是 nan。验证集 mAP 也全部归零。

原因:最常见的是学习率过高,训练初期梯度爆炸;其次是标签文件里出现异常的归一化坐标,比如 width 为负数或大于 1;还有一个容易被忽略的原因是图片本身损坏,读入后产生空矩阵。

解决:先把 lr0 从默认值调低,在训练命令里加 lr0=0.001 跑 10 个 epoch 试一下。同时用脚本扫描所有标签 txt,检查五列数值是否都在合理范围内,坐标和宽高在 0~1 之间且宽高大于 0。图片损坏的问题可以写个脚本检查所有图片能否用 OpenCV 正常读取,不能读的直接删掉并同步删除对应标签。

5.2 验证集 mAP 从第 1 轮到最后一轮都是 0

现象:训练能正常跑,loss 也在下降,但 val mAP50 永远显示 0.000,precision 和 recall 也全是 0。

原因:八成是标签文件的类别 ID 超出了 data.yaml 中 names 的数量范围。比如 txt 里写的是 5,但 names 只有 0 和 1 两个类别,模型学习时把所有该类别目标全部忽略;另外还有可能是 labels/val 目录为空,验证阶段没有真实框可以匹配。

解决:先统计一下 labels 目录下所有 txt 里 class_id 的最大值,再确认 data.yaml 里 names 的数量。单独跑一遍 val 目录的标签解析,打印出标签文件与图片的配对情况,确保 val 的 labels 目录下确实存在对应的 txt 文件。

5.3 训练轮次越往后,验证集 mAP 不升反降

现象:loss 在 60 轮之后还在缓慢下降,但 val mAP50 在第 50 轮达到最高点后持续回落,train loss 与 val loss 之间的差距越来越大。

原因:典型的过拟合。自定义数据集通常只有几千张甚至几百张图,模型在训练集上记住了大量背景纹理和冗余特征,在未见过的验证集上泛化能力下降。数据增强不足或 epoch 设得过大也会加剧这个现象。

解决:优先调大 mosaic 概率和 hsv 增强幅度,YOLOv8 默认开启 mosaic,但 fliplr、flipud 这类翻转增强并不适合所有场景。如果数据是工地摄像头拍的,垂直翻转会让“帽子在上”的语义失效,建议关闭翻转。同时调小 epochs 到 100 以内或直接依赖 patience=20 早停,保留第 50 轮的权重就够了。

5.4 训练过程中显存不足 OOM,直接报错中断

现象:训练开始后不久就报 CUDA out of memory,终端直接中断,之前训练的权重全部白跑。

原因:batch、imgsz 与显卡显存不匹配。GTX 1660 Ti 6GB 显存带 imgsz=640 batch=16 一定会爆,imgsz 越大每张图占用的显存越高,batch 越大同时驻留的图片越多,两者是乘数关系。

解决:优先把 batch 从 16 降到 8,如果还爆就降到 4。尽量不要用降低 imgsz 的方式省显存,因为 512 和 640 训练出来的模型在小目标上的精度差距明显。如果显存不够降到 batch=2 还爆,说明模型规格选高了,换成 yolov8n.pt 重跑更现实。

5.5 训练正常、mAP 也合格,但实际推理时框偏大或偏小

现象:验证集指标挺好,但拿测试图片一测,检测框总是比目标整体大一圈,或者只框住了目标的一部分。

原因:最常见的是标签归一化时图片宽高读错。比如你标注前做了批量缩放,XML 里的尺寸还是缩放前的值,转换出来的标签整体偏移,模型学到的就是偏心的框。另一种情况是数据里目标尺度太单一,比如全是近距离大目标,模型没见过远距离小目标,推理时自然会按熟悉尺度输出。

解决:把测试图上的预测框和真实框画在同一张图上对比,检查位置偏移方向。如果是整体偏大或偏小,重点检查训练样本中目标面积占整图面积的分布,适当补充不同尺度的样本,或者用 imgsz=512 重新训练让模型适应更宽的尺度分布。

6. 从 best.pt 到部署:测试集评估与 RK3588 导出

6.1 用独立的测试集做最终评估,不要用验证集结果交差

验证集在训练过程中已经被用来做早停和模型选择,严格来说指标有偏差,最终模型效果应该用独立测试集重新评估一遍。训练结束后 runs/detect/train/weights/ 下会有 best.pt 和 last.pt,best.pt 是验证集上表现最好的权重,部署用它。评估命令如下:

yolo detect val model=runs/detect/train/weights/best.pt data=helmet.yaml split=test

split=test 会让模型在 data.yaml 里配置的 test 目录上跑验证,并生成混淆矩阵 confusion_matrix.png、PR 曲线 PR_curve.png 等可视化文件。重点看对角线数值和误检集中在哪一类。安全帽检测场景里,如果 person 类被频繁误检成 helmet,就要检查训练样本里戴帽和不戴帽的头部分布是否均衡,数据平衡比调模型参数更能提升实际效果。

6.2 导出 ONNX:为 RK3588 部署铺路

如果模型要跑在 RK3588 这类带 NPU 的嵌入式平台上,不能直接用 best.pt 推理,需要先导出成中间格式,再转成硬件平台自己的模型格式。常见的导出链路是 PyTorch 权重转 ONNX,再转 RKNN。

yolo export model=runs/detect/train/weights/best.pt format=onnx opset=12 simplify=True

导出完成后会得到 best.onnx,用 onnxruntime 先在本机验证一遍输出是否正常。这里要注意:opset 版本不要随手填 17,RK3588 的 RKNN 工具链对 opset 12 兼容性最稳妥,simplify=True 会做一些图优化,去掉一些 NPU 不支持的冗余节点。导出时如果报错说某些算子不支持,大概率是训练时开了某些特殊增强或自定义模块,回到训练环节换标准 YOLOv8 结构重新训一次。

我自己的习惯是每训完一个自定义数据集,先拿测试集里最刁钻的 20 张图(逆光、遮挡、密集重叠)人工看一遍检测结果,再决定要不要进入导出流程,因为 mAP 指标在真实光线环境面前的参考价值有限。模型训练这件事,最后那 5% 的精度提升往往不是靠调参调出来的,而是靠反复看坏案例分析数据问题。希望这套从数据集准备到部署导出的流程能帮你少走几趟弯路,把时间花在真正影响效果的地方。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询