简介:一份面向YOLOv8目标检测初学者的完整训练指南,以docx文档形式系统梳理从环境搭建到断点续训的全流程知识点。内容涵盖YOLOv8单阶段检测原理、requirements与ultralytics安装、图片与XML标注文件的组织方式,以及通过split_train_val.py切分数据集、利用voc2yolo.py将标注转换为YOLO_txt格式的具体方法。训练部分给出预训练模型下载来源和标准训练命令,并对task、mode、model、data、epochs、batch等核心参数逐项说明。同时针对训练过程中常见的Arial.ttf字体下载失败问题,提供了具体的解决思路与操作方案;还介绍了通过查看default.yaml并使用resume=True实现断点续训的操作技巧。资源为1个docx文档,大小2.18MB,便携易读。目前已有448人学习,文档结构清晰,按照环境准备、数据制作、模型训练、问题排查的顺序组织,适合刚接触目标检测的读者按步骤完成自定义数据集的训练实践,并快速定位调参与排错的关键点。 好的,我理解你的需求。你将看到一个符合既定规范、可直接发布的中文技术博文。
1. 从“跑通Demo”到“跑通自己的数据”:YOLOv8自定义数据集训练到底卡在哪
很多人在YOLOv8里跑官方预训练权重时,感觉一切都很简单——几行命令就能出结果,出图也漂亮。但一旦把手头的业务数据(比如说一批工厂流水线上的零件照片)灌进去,事情就变得微妙起来。YOLOv8训练自定义数据集这件事,最核心的难点其实不在“训练”本身,而在于数据准备得规不规范、环境配得稳不稳定、以及你对训练日志里那些loss曲线和mAP数值是否心里有数。这篇文章不是来介绍YOLOv8新特性的,也不是一份“官方文档翻译组”的注水稿。我想跟你沿着一条最真实的工程路径走一遍:从零开始,用你自己的图片和标注,跑出一个能用的YOLOv8模型。这条路我走过很多遍,哪个环节容易踩坑,什么参数改了会有什么连锁反应,我会直接讲。
2. 先把环境钉死:PyTorch与Ultralytics的版本匹配是第一道坎
2.1 显卡不同,同一套命令结果天差地别
先泼一盆冷水:不要一上来就装最新版。YOLOv8的训练体验,很大程度取决于你的显卡算力、CUDA版本和PyTorch版本这三者的咬合程度。很多新手翻车的地方,是拿pip直接安装ultralytics,结果依赖的PyTorch版本和本机CUDA版本对不上,训练时torch.cuda.is_available()返回True,但一启动训练就报CUDA error: device kernel image is invalid。
其实问题很简单:PyTorch的CUDA版本与驱动支持的CUDA版本需要匹配。如果显卡是GTX 1660 Ti,甚至更老一点的Pascal架构(如GTX 1060),我强烈建议不要追新。老显卡跑新版PyTorch,往往会在某些算子上出现不适配或性能回退。更好的策略是按显卡年代选择成熟的稳定组合。例如,对于GTX 16系和RTX 20系,CUDA 11.8 + PyTorch 2.0.1 + Python 3.9/3.10 + ultralytics 8.0.x这一个组合,经过了非常多人的验证,是相当可靠的。如果你的显卡是RTX 4090或更新的H系列,再考虑PyTorch 2.1以上。
注意:先用
nvidia-smi查看驱动支持的CUDA版本。驱动是向下兼容的,只要驱动版本不太老,我们完全可以在conda环境里安装指定CUDA版本的PyTorch,而不必动全局驱动。
2.2 用Conda从零装一套可复现的YOLOv8环境
我平时的工作习惯,是永远不用base环境跑项目。否则,几个月后你会发现自己陷入“这个库升级了,另一个库要老版本”的泥潭里。这里给一份可以直接抄作业的创建命令。
# 1. 创建独立环境,指定Python版本 conda create -n yolo_env python=3.9 -y # 2. 激活环境 conda activate yolo_env # 3. 安装PyTorch(以CUDA 11.8为例) # 注意:不要用pip默认源装,要去PyTorch官方提供的命令里找对应的CUDA版本 pip3 install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118 # 4. 安装Ultralytics库(这会自动带上opencv、pandas等依赖) pip install ultralytics # 5. 验证环境 python -c "import torch; print(torch.__version__); print(torch.cuda.is_available()); from ultralytics import YOLO; print('OK')"这段命令的逻辑是分层的。第3步是整个环境的地基,这里的cu118对应CUDA 11.8的运行时库,它要求你的显卡驱动至少能支持CUDA 11.8。torch.cuda.is_available()输出True只代表PyTorch能看见显卡,不代表所有算子都能跑。第5步引入Ultralytics并实例化YOLO,是为了提早暴露libGL.so.1这类OpenCV的底层库缺失问题。如果这一步报错,在Ubuntu上通常执行sudo apt install libgl1 libglib2.0-0就能解决。
2.3 验证环境:用预训练权重先跑一次推理和一次小迭代
环境装完不要急着上数据。我会先跑一遍官方预训练权重,这一步能滤掉80%的环境隐藏问题。
# 测试推理:用官方权重跑一张示例图 yolo predict model=yolov8n.pt source='https://ultralytics.com/images/bus.jpg' # 测试训练:用官方coco128小数据集跑2个epoch,确认反向传播和梯度更新没问题 yolo train data=coco128.yaml model=yolov8n.pt epochs=2 imgsz=640跑推理,是为了确认模型文件下载、解码和NMS链路是通的。跑2个epoch的训练,是为了确认梯度能正常反传、loss能打印出来。这里不追求精度,只看流程能不能完整走通。如果这两条命令都顺畅,那环境就算稳了。之后再进行自定义数据集的训练,遇到问题才容易定位。
3. 把标注变成YOLO格式:从Labelme到TXT的转换细节
3.1 数据分Train/Val:不要用随机散分,要用文件夹按比例分
很多标注工具(比如Labelme)导出的是JSON格式,而YOLOv8要的是与图片同名的TXT文件,每行是class_id x_center y_center width height,坐标都是归一化到0-1之间的。我先说分集。不要随意写脚本把所有图片随机分到train和val两个文件夹里。因为如果你的数据拍摄自不同的批次、光照条件不同,随机会导致同一条流水线上前后的照片被拆到两个集合里,最终验证集的效果会虚高。
更好的做法是优先按拍摄时段或场景文件夹划分。比如你有20240610_上午、20240610_下午、20240611_夜班这三个文件夹,那么可以以文件夹为单位,挑选一部分做验证集。这样模型的泛化能力才看得见。
3.2 用Python脚本批量转换VOC/COCO标注为YOLO格式
在实际业务中,我见过最多的标注格式是VOC的XML和COCO的JSON。虽然Ultralytics官方对COCO格式支持很好,但数据处理时,有一个自己的转换脚本在手,心里不慌。我用一个转换脚本把VOC的XML批量转为YOLO的TXT,里面的关键步骤是坐标归一化和类别映射。
import xml.etree.ElementTree as ET import os from pathlib import Path # --- 配置区 --- # VOC数据集的文件夹结构通常是: annotations/xxx.xml, images/xxx.jpg voc_annotations_dir = Path('./datasets/pascal_voc/annotations') output_txt_dir = Path('./datasets/pascal_voc/labels') # 类别映射表,决定class_id的顺序。注意顺序一旦定下来,中途不可更改 class_mapping = {'person': 0, 'car': 1, 'bicycle': 2} ts = ['train', 'val', 'test'] # 生成label文件夹 for t in ts: (output_txt_dir / t).mkdir(parents=True, exist_ok=True) # --- 转换逻辑 --- def convert_xml_to_yolo(xml_file, out_txt_path): tree = ET.parse(xml_file) 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 class_mapping: continue # 跳过往未在配置区里的类别 class_id = class_mapping[name] # 解析边界框 xmlbox = obj.find('bndbox') x_min = float(xmlbox.find('xmin').text) y_min = float(xmlbox.find('ymin').text) x_max = float(xmlbox.find('xmax').text) y_max = float(xmlbox.find('ymax').text) # 转为YOLO格式(计算中心点和宽高) box_w = (x_max - x_min) / img_w box_h = (y_max - y_min) / img_h x_center = ((x_min + x_max) / 2) / img_w y_center = ((y_min + y_max) / 2) / img_h # 过滤掉异常框:有些标注会超出图像边界,需要裁剪或丢弃 if box_w <= 0 or box_h <= 0: continue lines.append(f"{class_id} {x_center:.6f} {y_center:.6f} {box_w:.6f} {box_h:.6f}") with open(out_txt_path, 'w') as f: f.write('\n'.join(lines)) # 主循环 for xml_file in voc_annotations_dir.rglob('*.xml'): # 假设图片与xml同名,且放在images文件夹里 image_name = xml_file.stem + '.jpg' t_dir = 'train' if not image_name.endswith('_val.jpg') else 'val' # 简易分集 out_txt_path = output_txt_dir / t_dir / (xml_file.stem + '.txt') convert_xml_to_yolo(xml_file, out_txt_path) print("转换完成,请检查labels目录下的输出txt文件")这段脚本的逻辑很直白。它把VOC的(xmin, ymin, xmax, ymax)通过减去左上角、除以宽高,变成了中心点加宽高的归一化表示。这里有两个原生化参数建议:第一,类别映射字典的键值,{'person': 0, 'car': 1}不是随便排的。在之后训练阶段,这个数字顺序直接决定模型的输出维度,一旦训练完就不能换。第二,脚本里没有硬编码classes.txt,因为Ultralytics完全可以从数据集的yaml配置里自己读。但如果你换别的框架,有这个文件会更通用。
3.3 数据检查:标注框、类别与文件命名的四个坑
转换完,不要急着开训。下面这几个坑,我几乎每个项目都会遇到一两个。
- 验证集与训练集的图片不能有重叠。如果你从大图里用OpenCV切patch做数据增强,务必注意一张原始大图切出来的patch,只能进同一个集合。
- TXT文件与图片文件必须同名同前缀。YOLO训练时,数据加载器是通过图片路径找同名的TXT文件。名字对不上,训练会直接报
AssertionError: Label not found。 - 数据集里混入全黑或全白的图片。这类低信息量图片会导致loss异常波动,在训练时表现为偶尔一步loss冲高。建议用简单的亮度方差筛选脚本提前剔除。
- 标签为空。一张没有目标的图片对应的TXT文件应为空(0字节),这在数据集中是合法的。但如果比例过高(超过10%),模型会偏向产出低置信度预测,需要留意。
4. 用YAML文件把数据交给Ultralytics:路径与关键训练参数
4.1 数据YAML的路径策略与结构
Ultralytics框架里,数据集的入口是YAML文件。很多人在这里栽跟头,因为YAML里写的路径和实际路径对不上。许多教程要求把数据集放到工程根目录下,但实际项目里数据集往往在NAS或移动硬盘上,拷贝到本地是浪费时间和磁盘。
我一般建议使用绝对路径。虽然官方文档推荐相对路径(因为它对跨平台复制友好),但假设数据集文件命名为dataset.yaml,路径写错一个字,训练就会报FileNotFoundError,排查起来很费神。一个标准的yaml长这样:
# 数据集配置文件范例 # 训练集和验证集的图片文件夹路径(这里用绝对路径) path: /home/user/work/datasets/defect_detection train: images/train val: images/val # 类别数量与类别名(顺序必须与标注TXT里的class_id严格一致) nc: 3 names: 0: scratch 1: dent 2: stain注意,path是根目录,train和val是相对于path的。Ultralytics会把路径拼接成/home/user/work/datasets/defect_detection/images/train。这里有一个最常见的新手误解:nc和names对不上,或者nc数量写对但names顺序乱了。模型训练时,损失函数计算只会用整数索引去查names,顺序错了会导致训练完了画图时标签错乱。
提示:如果你的
path目录下同时存在images/train和labels/train,Ultralytics会自动用images路径替换掉labels,所以只要图片路径写对,标签路径它会自己找。这既是便利也是坑——如果漏了标签文件夹,它不会报错,而是认为这批图片没有标注,全程默默地用空标签训练。
4.2 第一次训练用哪些参数最稳妥
对于第一次训练自定义数据集,不要一上来就调一堆花哨的增强参数。先用一个“保守但一定能收敛”的配置跑起来。下面是我常用的一份基线命令:
yolo train \ model=yolov8n.pt \ data=dataset.yaml \ epochs=100 \ imgsz=640 \ batch=16 \ lr0=0.01 \ optimizer=SGD \ cache=True \ patience=20 \ project=/home/user/work/runs \ name=defect_baseline \ seed=42这里面的参数,每一个都有讲究。yolov8n.pt是Nano版预训练权重,主要是为了快速跑通流程并看到趋势,不用一开始就用最大的yolov8x。batch=16取决于显存大小。在GTX 1660 Ti(6GB显存)上,imgsz=640时batch最多支持到8或16,如果显存溢出,就降到4或8。optimizer=SGD是我强烈建议的——虽然Adam收敛快,但用SGD能暴露更多数据本身的问题。cache=True会把图片加载到内存中,极大加快训练速度,前提是你的内存大于数据集大小。patience=20是早停,如果验证集mAP连续20轮不涨,训练停止,这能节省不少时间。
5. 常见问题与排查:Loss不降、mAP为0、显存不足怎么破
5.1 训练Loss居高不下或直接NaN
- 现象:前几个epoch的loss降得很快,到了第30个epoch左右就停止下降,或者训练到一半loss直接变成NaN,TensorBoard/Comet里的曲线图直接断掉。
- 原因:这一类问题最常见的原因是学习率太大或梯度爆炸。另一种可能是标签里存在数值为负或大于1的坐标值,即标注文件里有非归一化的脏数据。
- 解决:先把
lr0从0.01降到0.001再试一遍,同时修改数据加载,在训练前加一步判断:if x_center < 0 or x_center > 1: continue。如果仍然NaN,十有八九是显卡不稳定或PyTorch版本问题,回退到2.0.1版本即可。
5.2 训练正常,但mAP@0.5一直是0
- 现象:训练日志里loss下降正常,box_loss、cls_loss都在降,但每个epoch的验证结果里
mAP50和mAP50-95始终是0。 - 原因:一般不是模型问题,是验证集标注缺失。训练集有标签,每分钟都在学习,但验证集的图片路径下没有对应的TXT标签文件,或者是验证集里的类别编号与训练集对不上。
- 解决:去labels/val文件夹里看一眼,如果TXT文件全部为0,则说明分集逻辑错了。另外检查yaml里的
names顺序是不是和标注脚本的class_mapping对得上。
5.3 显存不足(CUDA Out of Memory)
- 现象:训练在第1个epoch刚跑完就报错,提示
CUDA out of memory。 - 原因:除了batch设得太高,另一个容易被忽略的原因是
cache=True把太多高分辨率图片预加载到了显存里。 - 解决:将
batch减半,或者把cache从True改成cache=ram(仅使用内存缓存,不用显卡显存)。如果还不行,把imgsz从640降到512,通常都能解决。
5.4 训练时发现权重文件下载极慢或卡住
- 现象:第一次初始化模型时,卡在
Downloading https://github.com/ultralytics/assets/releases/download/v0.0.0/yolov8n.pt这里不动。 - 原因:这个下载流程直连GitHub的Release资源,部分地区网络对GitHub连接不稳定。
- 解决:用浏览器或下载工具手动把
yolov8n.pt下载好,放到ultralytics默认的权重保存路径~/.config/Ultralytics/目录下,重跑命令时它会直接读取本地文件,不再联网下载。
5.5 训练中断后想从断点续训
- 现象:训练跑了两天,因为电脑断电或代码异常导致进程退出,之前的权重丢在
runs/detect/train_xxx/weights/里,不想重新开始。 - 原因:训练中断是常事,没有断点续训机制会浪费大量时间。
- 解决:Ultralytics支持断点续训。在断点重跑时,把
resume=True参数加上即可:
yolo train resume=True它会自动去runs/detect/目录下找最新的训练结果,并读取last.pt和args.yaml继续训练。这里的一个经验教训是,尽量不要在续训时手动指定epochs,否则它会用一个新的总epoch数覆盖原有计划,容易导致训练过程错乱。
6. 部署与进阶:从训练产物到推理脚本、损失函数曲线与模型导出
6.1 导出为TorchScript或TensorRT
训练结束后,如果要把模型部署到RK3588或NVIDIA Jetson Orin上,不能直接拷贝那个.pt文件。在Jetson上,我倾向于先导出为TorchScript(.torchscript),因为它在没有PyTorch完整环境的设备上也能作为独立推理模块运行。在RK3588上,如果你用了瑞芯微的NPU,需要先导出ONNX再转换RKNN格式。
from ultralytics import YOLO # 加载训练完成的最优权重 model = YOLO('/home/user/work/runs/defect_baseline/weights/best.pt') # 导出为TorchScript版本(保持FP32精度) model.export(format='torchscript', imgsz=640, dynamic=False) # 导出为ONNX,供后续转RKNN或OpenVINO使用 model.export(format='onnx', imgsz=640, dynamic=False, opset=12)dynamic=False在这里很关键。如果设成True,导出的模型会带上动态维度,虽然灵活但会显著降低在NPU上的推理性能。对于工业场景,我每次都是固定输入尺寸640。
6.2 用官方验证代码画损失函数曲线与结果图
训练完之后,看着命令行里最后打印的那一行指标,心里总是没底。我喜欢用一句官方代码直接跑在指定的验证集上,一方面把结果图片存下来肉眼核查,另一方面确认best.pt表现稳定。
yolo detect val \ model=/home/user/work/runs/defect_baseline/weights/best.pt \ data=dataset.yaml \ batch=1 \ conf=0.25 \ iou=0.6 \ project=/home/user/work/runs \ name=val_final跑完之后,去runs/val_final/文件夹里看两个东西:一是confusion_matrix.png,它能直观告诉你哪一个类别被频繁漏检或错检;二是results.png,那里面有损失函数曲线图(box_loss、cls_loss、dfl_loss),以及验证集的mAP曲线。我通常的习惯是,如果train/box_loss和val/box_loss在最后阶段还有明显缺口,说明过拟合了,可以提前停止或加大数据增强。
6.3 一个实际的操作建议:先小规模预跑再全量
在正式投入大批量数据训练之前,我每次都会做一次“预跑动作”:只拿每个类别各20张图,训练30个epoch,目标不是精度,而是确保类别分布、标签文件和模型结构完全打通。这会花你大约半小时,但能省下后续全量数据训练24小时跑完后才发现“类别顺序错了”的后悔药时间。
毕竟,YOLOv8训练自定义数据集这事儿,看起来是模型在学你的数据,实际上是你在被数据集的组织方式考验。把数据目录整理得像代码一样严谨,把训练参数理解得像调试器一样透彻,结果基本都是水到渠成。希望我的这些常规做法,能帮你少走几步冤枉路。
本文还有配套的精品资源,点击获取