尧图精选

YOLOv5自定义数据集训练指南:从目录格式到避坑实践

🕒 发布时间:2026/10/1 6:18:27 📁 来源:尧图网络
简介面向小型目标检测与水果分拣场景提供一套开箱即用的YOLOv5格式数据集覆盖苹果、橘子、梨三个类别包含完整训练集与验证集。所有图像均为1080×810的RGB照片每张图含多个目标且边界框标注完整可直接用于模型训练与评估无需额外转换。资源共2000个文件核心为1397个txt标签文件与602个jpg图像训练集由1117张图片及对应标签组成验证集由279张图片及对应标签组成。另有1个py可视化脚本可随机读取图片并绘制边界框帮助快速核查标注质量。压缩包大小226.87MB目录结构严格遵循YOLOv5规范复制到项目即可运行适合快速开展训练实验。目前已有518人学习下载适合初学者入门目标检测也可作为算法验证的小型基准数据。配套的txt类别文件明确了三个类别的编号可视化工具则让数据检查更直观省去手动整理标签的精力读者可以更专注于模型结构、参数调节与结果评估。1. 拿到苹果、橘子、梨目标检测数据集先读懂YOLOV5目录格式第一次把苹果、橘子、梨三种水果的目标检测交给YOLOv5翻车通常不在模型而在数据集本身。解压一份按YOLOV5目录格式组织的图片和txt你以为直接train.py就能跑结果十分钟内会遇到标签找不到、类别错位、验证集mAP高得离谱但实际检测稀烂这类问题。这篇笔记把这种格式的数据集怎么用讲清楚从目录解读、训练集和验证集划分到跑通YOLOv5最小训练命令最后给出一线调试建议。适合第一次用自有数据做目标检测的开发者也适合正在做水果分拣、计数识别的人照着复现一遍。2. YOLOV5目录格式拆解images、labels和data.yaml如何协同YOLOv5读数据集的方式和很多框架不一样它不做自动发现标注而是严格按目录约定去取。一份合格的三类别水果数据集解压后应该长成这样fruit_ds/ ├── data.yaml ├── images/ │ ├── train/ │ │ ├── apple_001.jpg │ │ ├── apple_002.jpg │ │ └── ... │ └── val/ │ ├── apple_010.jpg │ └── ... └── labels/ ├── train/ │ ├── apple_001.txt │ ├── apple_002.txt │ └── ... └── val/ ├── apple_010.txt └── ...拿到手先别急着训练花五分钟把目录结构和你自己的预期对一遍后面能省出大量的排错时间。下面按三个层次拆开讲。2.1 图片目录与标签目录train/val必须成对镜像images/train有多少张jpglabels/train就应该有多少个同名txtval同理。YOLOv5训练时会拿data.yaml里train指向的图片目录再在同级labels目录里找同名txt。如果labels目录里缺了对应文件或者文件名对不上训练就会报“No labels found”或者一直在警告。检查数量是第一步find fruit_ds/images/train -name *.jpg | wc -l find fruit_ds/labels/train -name *.txt | wc -l两个数字不一致说明数据集不完整或划分脚本有遗漏。还要注意图片后缀jpg对应jpg、png对应pngYOLOv5在部分版本里对后缀匹配很敏感图片是.png而txt按.jpg取名训练时就会漏掉。常见做法是统一把所有图片转成jpg再做后续操作省去这类隐性问题。2.2 读一个txt类别id、中心点坐标和归一化宽高用cat打开任意一个txt里面是纯数字文本每行对应图片里的一个目标对象0 0.5123 0.4521 0.2413 0.3125 2 0.7311 0.6067 0.1896 0.2408第一列是类别id0代表apple、1代表orange、2代表pear这个顺序必须和data.yaml里的names一致。后面四个数字分别是目标框中心点的x坐标、y坐标、框宽、框高全部除以了图片原始宽高做归一化取值范围在0到1之间。所以图片从1080p改成4K这套标注依然有效模型训练时不需要关心原图绝对尺寸。注意YOLO格式用的是中心点坐标不是左上角和右下角。如果你手里的标注是Pascal VOC那种xmin、ymin、xmax、ymax转换时要自己做一次四则运算第3章会给现成脚本。顺手看一下如果一张图片上只有两个目标txt就两行如果某张图完全没有目标它的txt是空文件这种文件在训练集里应该直接删掉否则会拖累模型对背景的判断。2.3 配好data.yamltrain/val路径、nc和names一个都不能错data.yaml是整个数据集的“索引文件”YOLOv5训练时只认它。一个典型的配置是train: /absolute/path/to/fruit_ds/images/train val: /absolute/path/to/fruit_ds/images/val nc: 3 names: [apple, orange, pear]train和val的路径可以写绝对路径也可以写相对当前工程目录的相对路径。我一般用相对路径比如../datasets/fruit_ds/images/train这样整个项目文件夹拷到别的机器上不用改路径。如果路径里有空格或中文建议用引号包住YAML解析时不容易出错。nc必须等于names列表的长度这里三个类别就是3。names的顺序直接决定txt里的数字指向哪个水果顺序写反了模型不会报错只会把苹果识别成橘子。另外有些数据集压缩包里还带一个classes.txt那是标注工具用的YOLOv5训练时不读它真正生效的是data.yaml里的names别让两个文件里的顺序不一致骗了你。这套images和labels的镜像结构YOLOv8以及后续很多框架也延续使用。在这里理顺一次后面切到YOLOv8训练自己的数据集目录格式几乎不用改动只要把data.yaml的写法稍微调整就能直接复用。3. 从原始照片到3类别训练集标注、切分与训前自检脚本如果拿到的成品数据集结构完整第3章前半段可以跳过如果你需要从自己的原始照片复现一份类似的三类别水果数据集或者想把现有数据扩充进去下面这套流程就是可复现的模板。三步走标注、划分、自检。3.1 目标检测常用标注工具怎么选先统一类别名再导出目标检测常用标注工具就那么几个。LabelImg是老牌工具Python加Qt实现导出Pascal VOC XML格式小数据集完全够用缺点是界面老一点。labelme偏多边形分割目标检测矩形框用起来还得额外转换。X-AnyLabeling现在用的人多它支持自动检测辅助标注能直接导出YOLO格式txt还带可视化检查功能对新手比较友好。选哪个工具不是关键关键是把类别名固定下来。苹果、橘子、梨建议统一成小写英文apple、orange、pear不要今天标Apple、明天标apple更不要混入中文或带空格的字符串。类别名一旦混乱后面的自动转换脚本就会静默地丢掉一部分框而你不会立刻发现。标注时每张图只框出目标水果遮挡超过一半的、模糊到人眼都难分辨的可以直接跳过。3.2 训练集与验证集划分写一段脚本把XML转YOLO并生成data.yaml把LabelImg出的XML转成YOLO txt同时按比例切出训练集和验证集这是最常见的落地方式。脚本如下# make_fruit_dataset.py # 将 LabelImg 导出的 XML 转成 YOLO txt并按 85%/15% 划分 train/val import random, shutil from pathlib import Path from xml.etree import ElementTree as ET CLASSES [apple, orange, pear] RAW_IMAGES Path(raw_images) # 原始 jpg 所在目录 RAW_XML Path(raw_xmls) # LabelImg 导出的 xml 所在目录 OUTPUT Path(fruit_ds) # 最终数据集输出目录 TRAIN_RATIO 0.85 random.seed(42) for split in (train, val): (OUTPUT / images / split).mkdir(parentsTrue, exist_okTrue) (OUTPUT / labels / split).mkdir(parentsTrue, exist_okTrue) def xml_to_yolo(xml_file): root ET.parse(xml_file).getroot() width int(root.find(size/width).text) height int(root.find(size/height).text) lines [] for obj in root.findall(object): name obj.find(name).text.strip() if name not in CLASSES: continue cid 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) lines.append(f{cid} {(x1 x2) / 2 / width:.6f} f{(y1 y2) / 2 / height:.6f} f{(x2 - x1) / width:.6f} {(y2 - y1) / height:.6f}) return lines images sorted(RAW_IMAGES.glob(*.jpg)) random.shuffle(images) split_index int(len(images) * TRAIN_RATIO) for index, image_path in enumerate(images): split train if index split_index else val xml_path RAW_XML / (image_path.stem .xml) if not xml_path.exists(): print(f跳过缺标注的图片: {image_path.name}) continue label_lines xml_to_yolo(xml_path) if not label_lines: print(f跳过无有效标注的图片: {image_path.name}) continue shutil.copy2(image_path, OUTPUT / images / split / image_path.name) (OUTPUT / labels / split / (image_path.stem .txt)).write_text( \n.join(label_lines) \n)几个参数值得说清楚。TRAIN_RATIO 0.85表示85%图片进训练集、15%进验证集如果你的原始图片只有60张建议把比例调到0.9同时人工检查验证集保证三个类别在验证集里都至少出现过。random.seed(42)固定随机序列方便复现同一个划分结果排查问题时不会因为重新运行脚本而换了一套数据。脚本会跳过缺XML的图片也会跳过所有目标类别都不在CLASSES里的图片并且打印原因。这一步的打印信息非常重要最好留个底训练时发现图片数量对不上回来对照这个输出就知道哪张图被丢了。划分完成后再生成一份data.yaml让txt和names永远来自同一个CLASSES常量yaml_text f train: {(OUTPUT / images / train).resolve()} val: {(OUTPUT / images / val).resolve()} nc: {len(CLASSES)} names: {CLASSES} (OUTPUT / data.yaml).write_text(yaml_text)这样生成的data.yaml用的是绝对路径如果在别的机器上训练可以把路径改成相对路径。两段脚本组合起来一份能直接喂给YOLOv5的三类别水果数据集就齐了。3.3 训前自检脚本把缺标签、坏图、越界坐标一次找齐数据集做完先别急着开训练跑一遍自检脚本把低级问题消灭在训练之前# check_dataset.py from pathlib import Path from collections import Counter from PIL import Image root Path(fruit_ds) num_classes 3 stat Counter() for split in (train, val): for img_path in sorted((root / images / split).glob(*)): if img_path.suffix.lower() not in (.jpg, .jpeg, .png): continue txt_path root / labels / split / (img_path.stem .txt) if not txt_path.exists(): print(f[缺标签] {img_path}) continue lines txt_path.read_text().strip().splitlines() if not lines: print(f[空标注] {txt_path}) continue for line in lines: parts line.split() if len(parts) ! 5: print(f[字段数错误] {txt_path}: {line!r}) continue cid, cx, cy, w, h map(float, parts) if int(cid) not in range(num_classes): print(f[类别越界] {txt_path}: {cid}) if not (0 cx 1 and 0 cy 1 and 0 w 1 and 0 h 1): print(f[坐标越界] {txt_path}: {line}) stat[int(cid)] 1 try: with Image.open(img_path) as im: im.verify() except Exception: print(f[坏图] {img_path}) print(各类别目标数量:, dict(stat))这个脚本的核心是检查三个一致性图片和txt同名、txt每行五元组合法、类别id在范围内。同时它会把每个类别的目标数量统计出来如果apple有1200个框、pear只有60个训练前就能看到别等到验证集PR曲线出来了才去猜原因。坏图检测用PIL的verify方法能发现下载或传输过程中截断的图片。这类坏图在训练时会让loss出现NaN而且很难定位是哪一张。如果有人已经给了你一份标注好的数据集第3.3的脚本是拿到手之后第一个应该跑的东西。4. 用这份数据集跑通YOLOv5训练最小命令与三个必调超参数数据集确认没问题接下来就是环境、命令、参数三件事。网上关于YOLOv5训练自己的数据集的教程很多但真正操作时最常出问题的反而是环境没验证就开始跑长训练。4.1 先把YOLOv5环境跑通用单张图片验证推理把YOLOv5源码准备好常见做法是clone官方仓库到工作目录然后创建独立环境避免和别的项目互相污染conda create -n yolov5 python3.9 -y conda activate yolov5 cd yolov5 pip install -r requirements.txt装完依赖先跑一次推理而不是直接开训练python detect.py --weights yolov5s.pt --source data/images/bus.jpg第一次运行会自动下载yolov5s.pt预训练权重看到输出图里标出person和bus说明torch、CUDA、OpenCV这些底层组件都正常。没有GPU的机器也能跑训练时把--device改成cpu就行只是时间会慢很多。这里建议加一步自检python -c import torch; print(torch.cuda.is_available())输出True再继续省得训练到一半才发现模型在CPU上龟速爬行。环境问题里最坑的是torch版本和CUDA驱动不匹配看起来能import一跑detect就报错这一步提前暴露。4.2 最小训练命令--data、--weights、--img先定下来训练命令不需要一开始堆满参数先把决定数据来源和基本规模的四个参数定下来python train.py \ --data ../datasets/fruit_ds/data.yaml \ --weights yolov5s.pt \ --img 640 \ --batch-size 16 \ --epochs 100 \ --cache ram \ --device 0 \ --project runs/fruit--data指向第2章配好的data.yamlYOLOv5靠它找图片目录和names。--weights选yolov5s.pt三个类别的小水果数据集用s足够盲目上yolov5x收益很小显存占用却翻几倍。--img 640是输入分辨率水果检测目标大小不一640是速度和精度的平衡点显存够且小目标多时可以提到960。--batch-size 16大约需要8GB显存显存小就降到8或4同时适当增加epochs来补偿波动。--epochs 100对三类别数据集足够看到收敛趋势。--cache ram把图片缓存进内存训练时不用反复读磁盘水果数据集规模不大内存能承受就开着。没有指定--weights时YOLOv5会使用COCO预训练权重这比自己从零训练好收敛得多。训练结束后模型保存在runs/fruit/exp/weights/下best.pt是验证集指标最优的权重交付时用best不要用last。4.3 什么时候调超参数只动hyp、cls和multi-scale训练过程中打开runs/fruit/exp/results.png这个图汇总了每一轮的box_loss、obj_loss、cls_loss和P、R、mAP50、mAP50-95。我观察训练是否正常的习惯是前20轮mAP50应该快速上升同时val/box_loss稳定下降如果train/box_loss下降而val/box_loss反弹说明已经开始过拟合。这个三类别水果数据集我通常只动三个超参数python train.py \ --data ../datasets/fruit_ds/data.yaml \ --weights yolov5s.pt \ --hyp hyp.scratch-low.yaml \ --cls 1.2 \ --multi-scale--hyp换成hyp.scratch-low.yaml适用于数据量小、场景光照固定的情况默认的hyp.scratch.yaml增强更强数据多时可以沿用。--cls是分类损失的权重默认1.0当三类样本数量偏差明显时调到1.2到1.5让模型更重视类别判断错误。--multi-scale让模型每10轮随机变化输入尺寸对远近大小不一的水果更稳但训练时间会增加。不建议一上来就把epochs改成300。三个类别的数据集100轮基本能看到趋势如果第80轮mAP还在上涨再加到150不迟。yolov5超参数还有很多可调但新手不需要全懂先会看结果图、会调这三个已经能解决大部分问题。5. 苹果橘子梨数据集训练避坑记录五个高发问题的排查顺序这套目录格式看似简单实际训练时翻车点非常套路化。下面五条是我做类似水果、农产品检测项目时踩过或帮同事排查过的记录按现象、原因、解决写可以照着对号入座。5.1 训练一开始提示No labels found镜像目录没有对齐现象train.py启动后出现大量warning提示某个jpg找不到对应的txt或者直接报No labels found in .../labels/train训练快速退出。原因YOLOv5从data.yaml的图片路径推导标签路径把images目录名换成labels目录名。如果labels目录里缺少同名txt、文件名对不上或者txt放错了层级都会触发这个结果。还有一种隐藏情况yaml里train还指向images/train但实际图片全在images/val训练集本身就是空的。解决先跑第3.3的自检脚本打印出所有缺标签的图片。更直接的做法是核对目录数量把images/train和labels/train用find命令各数一遍。图片和txt的文件名必须完全一致后缀也要一致jpg对应jpg、png对应png改了一边忘了另一边就会踩中。5.2 苹果被识别成橘子names顺序与txt类别id错位现象训练和验证loss都正常mAP也不低导出模型后用一张只有苹果的照片测试模型标成orange置信度还挺高。原因txt里的类别id是数字YOLOv5只是按顺序去data.yaml的names里取名字。如果标注工具导出时把orange排在了apple前面而data.yaml里写的是apple在前模型并没有理解语义只是在按错位的下标对号入座。这个问题在训练时完全不报错是最隐蔽的坑之一。解决让CLASSES列表成为唯一事实来源生成data.yaml和txt都从同一个列表来不要手写两处。改完yaml后随机挑三张图片把txt里的框和类别名画回图片上人工看一眼。画框检查的这几分钟能省下后面调参的几个小时。5.3 大苹果都检到小苹果漏检小目标比例与输入分辨率现象摄像头离果篮比较远苹果在画面里只有二十几个像素模型mAP50看着还行实测一段视频发现漏检了很多小目标。原因输入分辨率640下小目标在特征金字塔的高层几乎没有响应预先设置的anchor尺寸也不匹配。YOLOv5虽然有多尺度预测但小目标在默认配置下依然吃亏。解决先用3.3脚本统计每个txt里w和h的分布如果大量目标相对图片宽高小于0.1就把--img从640提到960显存不够就把batch降到8再配合--multi-scale。另一种常见做法是对高分辨率大图按滑窗切块训练避免原图被整体缩放后小目标消失。先看数据分布再调参数不要上来就换更大的模型。5.4 训练验证都上0.95现场拉胯训练集和验证集划分泄漏现象train和val的mAP都到了0.95以上部署到真实分拣线上一换光线和角度框开始乱跳。很多朋友做目标检测模型微调崩了来找我排查最后查出来都是数据划分的问题。原因全局随机切分时同一个水果不同角度的照片可能同时落在train和val验证集变得过于简单模型并没有真正面对分布外场景。尤其是视频抽帧得到的数据相邻帧高度相似随机shuffle等于让验证集偷看了训练集。解决按拍摄批次或场景划分。同一次拍摄的几十张连续帧整体放进train或val不要逐张随机。静态图片按文件夹分组一个场景文件夹整体进一边。第3.2脚本里的全局shuffle对独立拍摄的图片集可以对视频序列就是隐患改成先对文件夹名分组再切分才稳妥。5.5 梨的样本太少召回率低先统计类别数量再重采样现象apple有上千个框pear只有几十个框训练后pear的召回率明显偏低PR曲线里pear对应面积最小。原因模型训练时被多数类主导总体loss主要来自applepear的错误对梯度贡献太小。解决训练前先打印第3.3脚本的统计结果。pear少时有两种有效做法一是把pear的图片复制若干份并做轻度HSV扰动后并入训练集注意复制的数据只进train不进val二是在train.py里加--cls 1.5提高分类损失权重。超参数调整比重采样更省事可以先跑一轮对比曲线不行再重采样。复制图片时不要和mosaic增强叠加太猛否则pear过拟合验证集能过但现场依然不行。6. 交付前用混淆矩阵和数据增强做最后验证训练完成后我很少只看mAP就交差。对三类水果检测混淆矩阵比mAP更能暴露问题尤其是类别之间的混淆。6.1 用val.py生成混淆矩阵对角线才是真正要看的指标用训练得到的最优权重跑一遍验证集python val.py \ --data ../datasets/fruit_ds/data.yaml \ --weights runs/fruit/exp/weights/best.pt \ --conf-thres 0.25 \ --iou-thres 0.5跑完在runs/val/exp/下找到confusion_matrix.png。重点看两块对角线数值要大apple和orange之间的互相误判不能太高。如果orange那行有相当一部分被算成apple常见原因是标注时把黄苹果和橙子的边界框标乱了或者hsv_h数据增强把颜色搞混了。再配合PR_curve.png看每个类别的曲线下端面积pear曲线下垂明显说明样本不足和5.5对应。6.2 水果检测的颜色是强先验hsv_h别乱调水果检测和通用物体检测不同颜色是最强的类别先验。调YOLOv5的hyp增强参数时hsv_h我一般只设0.01到0.02hsv_s可以放到0.6到0.8但不要大幅改色调。红苹果被增强成青苹果类别边界就被污染orange和apple更容易互相认错。mosaic可以保留0.5到1.0对小目标有益但训练轮数少的时候不要开满。这个习惯救过我好几次先检查数据集再训练最后用混淆矩阵收尾。三类水果的检测任务并不需要堆大模型把目录和类别理顺、验证集划分干净结果通常比盲目调参好得多。希望帮到你。本文还有配套的精品资源点击获取
上一篇/下一篇内容由系统自动关联 返回资讯列表 →