简介:基于YOLOv8训练自定义数据集的完整工程代码,面向深度学习入门与目标检测实践者,可用于自动驾驶、安防监控、工业质检等定制化识别场景。压缩包共294个文件,约81.37MB;其中67个Python脚本覆盖数据转换、训练与验证流程,71个yaml定义模型与训练参数,另有pyc编译模块、pt权重、标注txt、jpg样例、说明文档及训练日志,目录结构清晰,便于按模块查阅。已有11739人学习下载。内容涵盖数据标注与格式转换、YOLO数据集组织、锚框机制、损失函数、训练配置与调参策略,并附有实际训练生成的tfevents日志可供对比分析。训练完成后,可对照测试集评估平均精度与漏检率等指标;通过动手复现,读者既能理解YOLOv8网络结构与Darknet卷积设计,也能掌握从自有数据准备、模型训练、验证评估到部署应用的完整工程化流程,并迁移到自身项目中。
1. yolov8训练自己的数据集:拿到源码包之后先别急着跑
yolov8训练自己的数据集(源码)这个压缩包,在不少技术论坛和网盘里流传很广,内容一般是一套基于Ultralytics YOLOv8的二次封装代码,附带训练脚本、配置文件和少量说明。拿到手的人通常分两类:一类是刚学会用LabelMe或LabelImg做标注,想借现成代码跑通自己的第一版检测模型;另一类是已经在用YOLOv5或其他框架训练过,想换到YOLOv8重新做一版,看看精度和收敛速度有没有提升。这个包名义上解决的是“训练自己的数据集”,但实际最常翻车的地方反而不在模型结构,而在环境依赖、路径写死、标签格式这三件事上。所以这篇按我拿到这种源码包后的排查顺序来写:先让环境能跑起来,再把数据换成自己能认的格式,最后把训练参数和结果验证讲透。
2. 环境搭建与源码包核对:先让yolo命令能在本机跑起来
源码包再花哨,第一步永远是环境。YOLOv8的训练本体是Ultralytics官方仓库,绝大多数“训练源码包”只是在它外面包了一层自己的train.py、数据集目录和说明文档。所以环境搭建可以按官方依赖走,不一定要完全依赖包里的requirements.txt。备注:如果包里的requirements.txt是两年前的版本,装出来的依赖很可能和当前YOLOv8代码不兼容,这点在2.2里单独讲。
2.1 用conda创建python3.10环境:为什么先装torch再装ultralytics
我习惯先用conda建一个独立环境,版本固定python3.10。YOLOv8对Python版本不算挑剔,3.8到3.12都能跑,但3.10是踩坑最少的组合。
conda create -n yolo python=3.10 -y conda activate yolo pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118 pip install ultralytics这个顺序是有讲究的。如果直接执行pip install ultralytics,pip会按依赖自动帮你装一版CPU版的torch。CPU版torch在你的机器有NVIDIA显卡时也能装上,甚至能跑完整个训练流程,但速度会慢到让人怀疑人生,一个epoch跑几十分钟很正常。所以先手动装GPU版torch,再装ultralytics,pip检测到torch已经存在就不会重复安装。--index-url指定了CUDA 11.8对应的torch版本,PyTorch官方源里cu118的包最稳定,兼容性也最好;如果你的显卡是40系,也可以把cu118换成cu121或cu124,具体以PyTorch官网对应版本为准。装完后跑一句验证:
python -c "import torch; print(torch.__version__, torch.cuda.is_available())"torch.cuda.is_available()输出True,说明torch能调用显卡;如果你的机器确实没有独立显卡,输出False也不影响训练,后面把device参数设成cpu就行,只是大模型会慢。环境这一步最大的坑是torch和ultralytics的版本互相打架,常见表现是报AttributeError: module 'torch' has no attribute 'xxx',解决办法不是降级ultralytics,而是把torch升到2.0以上。
2.2 源码包核对的三个动作:找入口、找依赖、找写死的绝对路径
环境装好之后,别急着双击train.py,先花五分钟把源码包的结构过一遍。我拿到二手源码包会做三个动作。
# 动作一:找训练入口,是CLI还是包装脚本 find . -maxdepth 2 -type f -name "*.py" | head -30 # 动作二:看依赖文件和说明 cat requirements.txt 2>/dev/null || echo "没有requirements.txt,依赖靠ultralytics内部管理" # 动作三:搜写死的绝对路径,这是二手包最常见的雷 grep -rnE "/home/|/root/|C:" --include="*.py" --include="*.yaml" .动作一用来确认包的入口形式。有些源码包直接用yolo命令行工具训练,有些包把训练逻辑封装在train.py里,两者没有本质区别,但入口不同后面改参数的位置就不同。动作二的目的是确认依赖是不是和当前环境匹配;如果requirements.txt里写的是ultralytics==8.0.x,而你装的是8.2以上版本,建议以当前版本为准,一般不影响训练,因为YOLOv8的核心接口在这几个版本里没有大变动。动作三最重要,很多人拿到包直接跑,报FileNotFoundError: [Errno 2] No such file or directory: '/home/olduser/datasets/...',就是包作者在自己机器上训练时把绝对路径写死在了配置里。grep命令能把py文件和yaml文件里的/home/、/root/、C:路径一次性揪出来,看到结果直接改成你自己机器的路径。
2.3 环境验证命令:确认ultralytics版本和CLI可用
最后做一个最小验证,确认ultralytics包本身没装坏。
python -c "from ultralytics import YOLO; print(YOLO.__version__)" yolo --help第一条命令能打印出版本号,说明包导入正常;第二条命令确认命令行入口能用。在我遇到的情况里,yolo命令报command not found大多是因为ultralytics装到了conda环境的bin目录,但当前shell没激活这个环境,重新conda activate yolo即可。到这里,环境部分就收尾了,可以进入下一个问题:数据集怎么变成YOLOv8能吃的格式。
3. 把标注数据转成yolov8格式:VOC/labelme转换脚本与目录约定
环境跑通只解决了一半问题。yolov8训练自己的数据集,核心是把标注数据变成YOLOv8原生的标签格式。这一步翻车率最高,因为大多数人手里的标注是LabelMe导出的JSON,或者老项目积累的VOC XML,而YOLOv8只认TXT文件。如果你已经手里已经有一批TXT标签,第3.1节可以直接跳过,但建议还是看一眼3.1的目录约定,训练时目录结构不对一样报错。
3.1 标注格式对比:labelme/voc的json和xml与yolo txt标签的结构差异
先明确YOLOv8要的标签长什么样:每张图片对应一个同名TXT文件,放在labels目录下;TXT里的每一行代表一个目标,格式是五列数字:class_id center_x center_y width height。其中center_x、center_y、width、height全部是相对图片宽高的归一化值,取值0到1之间。
VOC的XML是另一种结构,框的坐标用左上角和右下角两个点表示,而且用的是绝对像素值。LabelMe的JSON更复杂,每个标注里存的是一个多边形轮廓点数组,很多工具导出时只存了points,并没有直接给出外接矩形。格式差异带来了三个必须处理的点:坐标从绝对像素归一化到相对值、从两点坐标算中心点和宽高、类别字符串变数字ID。为了看清差异,我列一张对比表:
| 格式 | 框表达方式 | 类别表达方式 | 是否可直接训练 |
|---|---|---|---|
| YOLOv8 TXT | 归一化中心点+宽高 | 整数类别ID | 可直接用 |
| VOC XML | 绝对像素左上角+右下角 | 类别字符串 | 需转换 |
| LabelMe JSON | 多边形点数组 | 类别字符串 | 需转换且要算外接框 |
如果目标是MASK分割,要另说;如果只是目标检测,LabelMe的多边形点其实只用它的最小外接矩形就够了,不需要把多边形存下来。转换脚本的通用逻辑就是把标注读进来,算外接框,再归一化。
3.2 voc转yolo的转换脚本:核心代码与四个边界坑
下面这段是我常用的VOC XML转YOLO TXT脚本,基于Python标准库xml.etree.ElementTree实现,不需要额外安装任何第三方库,在源码包里直接新建一个voc2yolo.py就能跑。
import xml.etree.ElementTree as ET import os from pathlib import Path def voc_to_yolo(xml_path, out_dir, classes): 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 cls_id = classes.index(name) box = obj.find("bndbox") x1 = float(box.find("xmin").text) y1 = float(box.find("ymin").text) x2 = float(box.find("xmax").text) y2 = float(box.find("ymax").text) # 四个值全部归一化到[0,1] cx = ((x1 + x2) / 2) / img_w cy = ((y1 + y2) / 2) / img_h bw = (x2 - x1) / img_w bh = (y2 - y1) / img_h # 防御:越界框截断到图片范围内,然后过滤无效框 cx = min(max(cx, 0.0), 1.0) cy = min(max(cy, 0.0), 1.0) bw = min(max(bw, 0.0), 1.0) bh = min(max(bh, 0.0), 1.0) if bw < 1e-6 or bh < 1e-6: continue lines.append(f"{cls_id} {cx:.6f} {cy:.6f} {bw:.6f} {bh:.6f}") # 即使没有识别到任何目标,也生成一个空txt文件,保持一一对应 txt_name = Path(xml_path).stem + ".txt" out_path = Path(out_dir) / txt_name with open(out_path, "w") as f: f.write("\n".join(lines)) classes = ["person", "car", "dog"] # 按你自己的类别清单改,顺序必须和data.yaml一致 xml_dir = "Annotations" # VOC格式的XML目录 out_dir = "labels" # 转换后的TXT输出目录 os.makedirs(out_dir, exist_ok=True) for xml_name in os.listdir(xml_dir): if xml_name.endswith(".xml"): voc_to_yolo(os.path.join(xml_dir, xml_name), out_dir, classes)脚本逻辑分三段:第一段从XML的<size>节点读取图片宽高,第二段遍历每一个<object>,把左上角右下角坐标换算成中心点加宽高并逐项归一化,第三段把拼接好的行写入同名TXT。这里最容易出问题的四个点,我踩过之后一直记着:第一,图片宽高必须取自原始图片的宽高,有些人写成取了标注框的宽高,结果所有框的中心点全都偏到右下角;第二,类别顺序一旦定了就不能改,classes列表的顺序就是训练时类别ID的顺序,TXT里写的是ID不是字符串,ID写错了后期排查极痛苦;第三,归一化公式的分子是(x1+x2)/2和x2-x1,不是x2-x1直接除以img_w,宽高和中心点坐标这两组数据经常被搞混;第四,标注框越界是常态,尤其物体被截断在图片边缘时,x2可能大于img_w,不处理的话训练时标签值大于1会直接导致loss异常。这段代码里的截断和过滤就是为这类脏数据准备的。
3.3 数据yaml的写法和目录组织:train/val分开、绝对路径换机器即废
标签转好之后,还需要一个数据描述文件告诉YOLOv8数据集在哪里、有哪几类。这个文件通常叫mydata.yaml,放在源码包根目录下,内容如下:
path: ./datasets/mydata train: images/train val: images/val # test: images/test nc: 3 names: ['person', 'car', 'dog']path是数据集根目录,可以用绝对路径,但我强烈建议用相对路径。因为源码包大概率会在多台机器之间拷贝,一旦写死/home/某用户/...,换台机器或者换个人就要改一遍。train和val目录是相对path的,所以整个数据集目录结构要组织成:
datasets/mydata/ ├── images/ │ ├── train/ # 训练图片 │ └── val/ # 验证图片 └── labels/ ├── train/ # 与训练图片同名的txt标签 └── val/ # 与验证图片同名的txt标签注意三个细节:第一,nc必须等于names列表长度,两个地方不一致训练会直接报错,报错信息是IndexError: index 3 is out of bounds for axis 0 with size 3之类;第二,标签TXT和图片必须严格同名,包括扩展名前面的部分,图片叫001.jpg标签就得叫001.txt,大小写也要一致;第三,train和val里的图片千万不要有重复,我见过有人把同一批图既放进train又放进val,最后mAP高得离谱,换到真实场景立刻现原形。数据文件准备好了,下一步就是启动训练。
4. 训练启动与五个必调参数:从冒烟测试到完整训练的完整命令
数据准备好了,训练这一步反而简单。但要分两步走:先跑一个2个epoch的冒烟测试,确认全流程能走通;再跑完整训练。很多人拿到源码包直接跳到最后一步,然后被一个报错卡一个小时,回头再排查已经不知道是数据问题还是环境问题。冒烟测试的目的就是把变量控制到最少。
4.1 冒烟测试命令:2个epoch跑通全流程
cd 源码包根目录 yolo detect train data=mydata.yaml model=yolov8s.pt epochs=2 imgsz=640 batch=4 device=0这条命令有几个参数值得解释。data=mydata.yaml指向刚才写好的数据描述文件;model=yolov8s.pt是预训练权重,模型文件可以直接用源码包里的,也可以用yolov8n.pt或yolov8m.pt,n是轻量版,m是中量版,s是small,三者精度和速度依次递进。如果源码包里没带权重文件,ultralytics在第一次运行时会在当前目录寻找yolov8s.pt,找不到就会尝试从网上自动下载;下载需要的网络环境不一定具备,所以最好是先手动把权重文件放到当前目录。epochs=2指只训练2个epoch,目的是验证数据和代码链路,不是追求精度;batch=4是一个批次4张图,小批量可以降低显存压力;device=0指用第一块GPU。
如果冒烟测试出现FileNotFoundError,八成是mydata.yaml里的path或train路径不对;如果出现CUDA out of memory,把batch=4改成batch=2,或者把imgsz=640改成imgsz=416。冒烟测试2分钟跑完,看最后几行输出里有没有results saved to runs/detect/train,有就说明链路是通的。
4.2 训练前必调的5个参数:epochs、imgsz、batch、workers、patience
冒烟测试过了之后,正式训练前把这5个参数按自己的需求改掉。下面表格是我在不同数据集上反复调整后比较稳的配置思路:
| 参数 | 默认值 | 我的设置倾向 | 理由 |
|---|---|---|---|
| epochs | 100 | 小数据集30~50,大数据集100~300 | 小数据集100轮极容易过拟合 |
| imgsz | 640 | 小目标调768或1024,显存不够调416 | 目标越小,分辨率越不能太低 |
| batch | 16 | 按显存来,8G卡用8~16,6G卡用4~8 | batch只影响收敛稳定性和速度,不影响最终精度上限 |
| workers | 8 | Windows下直接设0,Linux按CPU核数一半 | Windows多进程数据加载有历史问题 |
| patience | 50 | 小数据集设20~30,大数据集用默认 | 连续N轮指标没提升就早停,能省时间 |
epochs是训练轮数。小数据集(几百张图)跑到50轮以后基本就过拟合了,再往后mAP不会再涨,反而会往下掉。imgsz是训练时把输入图片缩放到统一尺寸,如果你的目标是小物体(比如几十像素的缺陷),建议把imgsz调到768甚至1024,代价是显存占用翻倍。batch受到显存限制最直接,8G显存跑imgsz=640时batch设16已经是极限,再往上就得降分辨率或开混合精度。workers影响数据加载速度,Windows上设大于0偶尔会卡死,设0最稳妥;Linux上设成CPU核数一半即可。patience是早停参数,连续多少轮验证集mAP不提升就自动终止训练,小数据集默认50太保守,白等很多时间。
4.3 训练过程看什么:results.csv、损失曲线与runs目录
正式训练启动后,输出目录会生成runs/detect/train,里面包含weights/best.pt、weights/last.pt、results.csv、confusion_matrix.png等文件。我最常看的不是终端滚动日志,而是results.csv。
tail -5 runs/detect/train/results.csvresults.csv每一行是一个epoch的汇总,包含训练损失、验证损失、精确率、召回率、mAP50、mAP50-95等指标。看这个文件能判断模型是否在收敛:训练损失和验证损失都持续下降,说明正常;训练损失下降但验证损失反弹,说明过拟合,该停;训练损失忽高忽低没有规律,先怀疑学习率或batch有问题。如果想看动态曲线,可以用tensorboard:
yolo detect train data=mydata.yaml model=yolov8s.pt epochs=100 imgsz=640 batch=16 device=0 project=runs name=train tensorboard --logdir runsproject和name用来指定输出目录,tensorboard --logdir runs启动后,浏览器打开http://localhost:6006就能看到loss曲线和mAP曲线实时刷新。日志可视化这块,YOLOv8本身集成了结果图,其实大多数场景下看results.csv加训练结束后的confusion_matrix.png就够了,tensorboard是锦上添花。
5. yolov8训练避坑指南:5个高频报错的现象、原因与解决路径
训练源码包最让人头疼的不是配置过程,而是训练跑到一半报出来的各种错误。这一章把我自己遇到过的、以及帮别人排查过的高频问题按“现象→原因→解决”写清楚,每一条都对应一个真实的踩坑场景。
5.1 第一个epoch就出现loss=nan,模型完全学不进去
现象:训练日志第一行就显示loss: nan,之后每个epoch都是nan,模型输出一堆垃圾框。原因有三个常见来源:一是学习率太大,模型权重在第一步更新时就溢出了;二是标签数据里有脏数据,比如某个TXT里出现了负坐标或大于1的归一化值,或者类别ID超过了nc;三是图片文件有损坏,解码出来的像素矩阵是空的或全黑的,导致前向传播出现非法值。解决:先改学习率试一下,把lr0调小一个数量级:
yolo detect train data=mydata.yaml model=yolov8s.pt epochs=100 imgsz=640 lr0=0.0001默认学习率是0.01,对小数据集来说偏高,降到0.001或0.0001之后大部分nan问题能解决。如果调了学习率还报nan,去检查标签目录里所有TXT文件的每一行,写一段几行的python脚本把所有框的坐标值打印出来,看有没有小于0或大于1的。再不行就检查图片完整性,用PIL库把图片逐张打开:
from PIL import Image import os img_dir = "datasets/mydata/images/train" for name in os.listdir(img_dir): try: img = Image.open(os.path.join(img_dir, name)) img.load() except Exception as e: print(f"损坏文件: {name}, 原因: {e}")5.2 显存不够:RuntimeError: CUDA out of memory
现象:训练在第一个epoch或者中途直接报CUDA out of memory,进程退出。原因:imgsz、batch和模型大小共同决定了显存占用,imgsz=640、batch=16、yolov8m三个条件同时满足时,8G显存必炸。解决:按优先级从高到低试三个方案。第一个方案是把batch调小,batch=8或batch=4,这是损失最小且见效最快的改法;第二个方案是把imgsz从640降到480或416;第三个方案是换更小的模型,把yolov8s.pt换成yolov8n.pt,n模型参数只有s的三分之一左右。如果你的机器有显卡但显存只有6G,组合方案是yolov8n.pt + imgsz=416 + batch=8,这个配置在6G卡上能稳跑。还有一个被低估的设置是开启混合精度训练,ultralytics从8.1版本开始默认开启AMP,如果老版本没有,手动加amp=True能省将近一半显存,代价是精度可能有千分之一级别的下降。
5.3 训练全程mAP都是0,loss却在下降
现象:训练结束后打印的mAP50、mAP50-95全是0.0,但训练日志里的loss曲线是正常下降的。这个现象非常迷惑,属于典型的“模型在学,但学的方向不是你要的方向”。原因最常见的有两类:第一类,验证集和训练集分布差异过大,比如训练集大多数是白天场景,验证集全是夜间或强光场景,模型在训练集上学到的特征在验证集上完全不适用;第二类,标签类别ID错位,TXT文件里写的类别编号和mydata.yaml里的names顺序对不上,模型预测时把第0类当成了第2类,属于纯标签脏数据问题。解决:先用一个脚本检查训练集和验证集的图片场景,从每个目录随机抽几张图直接看,确认没有明显的分布差异;再检查标签,统计TXT里类别ID的分布:
cat datasets/mydata/labels/train/*.txt | awk '{print $1}' | sort | uniq -c输出的类别ID列表应该落在0到(nc-1)之间,且每个ID都有样本。如果某个ID一次都没出现过,模型对该类别的召回率必然为0,mAP也会被拉低。
5.4 训练时日志里出现Downloading,网络不好直接卡住
现象:训练启动后日志出现Downloading https://.../yolov8s.pt之类的内容,然后进度条长时间不动,训练无法继续。原因:model=yolov8s.pt这个参数只要在当前目录或ultralytics缓存目录里找不到对应权重文件,代码就会自动从网上下载;源码包作者打包时可能没把权重文件放进去。解决:最直接的办法是手动下载权重文件放到当前目录,文件名严格保持为yolov8s.pt;或者从源码包里看看有没有.pt文件,有的话复制到当前目录并重命名为训练命令里写的名字。另一种做法是用model=yolov8s.yaml代替,不加载预训练权重,从零开始训练,适用于数据量特别大(几万张图)的场景,数据量小时精度会比用预训练权重低不少。
5.5 Windows下DataLoader worker崩溃:killed by signal
现象:Windows系统上运行训练命令,进度条到一半直接报RuntimeError: DataLoader worker (pid 12345) is killed by signal,训练终止。原因:这是Windows上torch DataLoader的老问题,多进程数据加载和主进程之间容易出现死锁或被系统杀掉,尤其是workers设置大于0时。解决:训练命令里把workers=0显式加上:
yolo detect train data=mydata.yaml model=yolov8s.pt epochs=100 imgsz=640 batch=16 device=0 workers=0workers=0表示数据加载在主进程内完成,速度会慢一点,但能稳定跑完整个训练过程。如果你在Linux服务器上训练,workers=0一般不是必须的,设成2到4还能提高数据加载吞吐。
6. 验证训练结果并从best.pt导出onnx:部署前的最后一步
训练结束,runs/detect/train/weights/下会出现last.pt和best.pt。很多人直接拿best.pt去测图片,测一张觉得还行就收工,但这样会漏掉很多信息。我自己的习惯是先看验证集的结果图,再决定要不要导出部署。
runs/detect/train里有一组关键文件:confusion_matrix.png看类别互相误检的情况,哪两类最容易混,一眼就能看出来;PR_curve.png看每个类别的精确率-召回率曲线;val_batch0_pred.jpg是验证集图片的预测可视化。我最先看的是val_batch里的预测框,而不是mAP数字——mAP只能给你一个总分,预测框图能告诉你模型具体错在哪里:是漏检了远距离小目标,还是把两个相近类别框混淆了。如果预测框整体位置偏移,优先怀疑标签框本身标歪了,这时候回去修标注,比调参更有效。
确认结果满意后,导出onnx是部署前比较稳的一步,也是验证模型结构是否能落地到推理端的手段:
yolo export model=runs/detect/train/weights/best.pt format=onnx imgsz=640 opset=12导出成功会生成best.onnx。format=onnx把PyTorch权重转成通用推理格式;imgsz=640要和训练时保持一致,如果训练时用了768,这里也改成768,尺寸不一致会掉精度;opset=12是ONNX算子集版本,板端推理引擎对高版本opset支持不全,12是兼容性较好的选择。如果你后续要做边缘设备部署,比如RK3588或海思平台上的YOLOv8推理,通常还要把onnx再转成对应芯片的模型格式,并在转换时做int8量化;量化这一步有一个经验:训练时imgsz越大,量化后精度损失越小,所以训练阶段在显存允许范围内尽量别把分辨率压太低。
我自己的收尾习惯是:训练完不在现场做决策,先睡一觉,第二天重新看一遍val_batch预测图,再决定是加数据、改标注还是直接部署。训练时的直觉会因为连续盯了几个小时loss曲线而失真,隔天看往往能发现前一天忽略的低级问题。这个习惯帮我在好几次“mAP看着不错”的训练里发现了标签错位和数据泄漏的问题。希望这篇踩坑笔记对正在和你自己的第一个YOLOv8数据集搏斗的你有帮助。
本文还有配套的精品资源,点击获取