简介面向葡萄拣选与品质分选场景的YOLO系列算法目标检测数据集覆盖斑点葡萄、腐烂葡萄、成熟葡萄与未成熟葡萄四类目标可直接用于目标检测模型的训练、验证与测试。压缩包共496个文件包含165张JPG图像、165个YOLO格式TXT标注、165个VOC格式XML标注及1份data.yaml配置整体大小13.85MB已按训练需求完成目录划分无需额外整理转换即可接入YOLOv5、YOLOv7、YOLOv8、YOLOv9、YOLOv10、YOLO11等主流版本。标签格式同时提供归一化中心点坐标与宽高的YOLO标准格式以及XML标注的VOC格式便于在不同框架间迁移使用。data.yaml中已写明类别与路径适合刚入门目标检测的开发者一边上手训练一边理解数据组织方式。目前已有101人浏览学习对于需要构建农业视觉检测原型或进行算法对比实验的读者来说是一份小而完整的实践数据资源。1. 一个能直接开训的葡萄数据集YOLO 与 VOC 双格式标签做葡萄分拣或果园巡检项目时最尴尬的就是数据卡脖子网络结构调好了训练脚本写好了COCO 里葡萄样本却只有零星几张成熟度、腐烂程度这类业务类别根本覆盖不到。这个 yolo 算法葡萄数据集就是冲这个问题来的165 张果园实拍图像带完整标签覆盖拣选点、斑点葡萄、腐烂葡萄、成熟葡萄、未成熟葡萄五类YOLO txt 和 VOC xml 两种标签格式都备好训练验证集已划分data.yaml 也生成完毕。适合谁用两类人最省事正在用 YOLO 系列跑检测、不想把时间耗在标注上的工程师拿目标检测做毕设、需要解压即训数据的同学。165 张不算多但结构完整、标签规范足够你把全流程跑通再往自己的场景迁移。2. 动手前先拆包目录骨架、label 坐标与 data.yaml这个数据集的核心是“打开就能训”但打开之前先花十分钟把内部结构摸清楚。五种类别分别对应一个类别索引索引由 data.yaml 决定如果这块不先理清后面训练跑得再顺预测结果也可能全是错的。2.1 解压后的文件骨架与命名规律下载下来是一个 zip先进系统解压。Linux 或 mac 环境用 unzipunzip yolo算法-葡萄数据集数据集-165张图像带标签-拣选点-斑点葡萄-腐烂的葡萄-成熟葡萄-未成熟葡萄.zip -d grape_datasetWindows 上直接右键解压就行。提醒一句zip 文件名带中文Linux 下解压前先确认系统 locale 是 UTF-8否则解出来的文件名会乱码后面 data.yaml 里路径全对不上。我一般先把 zip 重命名成 grape.zip 再解压省掉这一档麻烦。解压后的目录结构这类打包一般长这样grape_dataset/ ├── images/ │ ├── train/ │ └── val/ ├── labels-yolo/ │ ├── train/ │ └── val/ ├── labels-voc/ │ ├── train/ │ └── val/ └── data.yaml实际目录名可能略有差异但逻辑是同一套images 里放 165 张 jpglabels-yolo 里放同名 txtlabels-voc 里放同名 xmldata.yaml 描述类别和路径。图像文件名类似 img_0771_16.jpgimg_ 后面第一段基本是拍摄场景编号下划线后是单图序号同一个场景的多张图在光照和角度上接近如果你的后续数据是自采的最好让同场景图像只出现在训练集或验证集避免数据泄漏拉高虚假分数。labels-yolo 和 labels-voc 一一对应相当于同一个框用两套坐标体系各写了一份。为什么这么干YOLO 系框架读 txt 最顺VOC 系框架比如 Faster R-CNN、SSD读 xml 最顺一份数据两边都能用省得自己写转换。zip 里已经划分好 train/val训练前不用再手动 split。2.2 YOLO txt 五个数字的含义挑一张训练图对应的 txt打开是这种格式# 对应 img_0771_16.jpg 2 0.5123 0.4471 0.1832 0.2414 0 0.6831 0.3256 0.1422 0.1788每行五个数空格分隔。第一列是类别索引从 0 开始五类就是 0 到 4具体顺序看 data.yaml 的 names后面四列依次是 x_center、y_center、width、height全部做了归一化也就是用真实像素坐标除以图像宽高得到的比例值范围在 0 到 1 之间。举例0.5123 乘图像宽度 1280 约等于 656 像素这就是检测框中心点的 x 坐标width 列 0.1832 乘 1280 约等于 234 像素是框的实际像素宽。提示txt 里没有类别名只有索引。类别名和索引的对应关系全部由 data.yaml 决定改 names 顺序等于把所有标签重新定义了一遍。这段格式说明不是废话因为很多人直接拿 txt 跑训练遇到“训练正常但结果全错”时会茫然根源就是没理解类别索引与 names 的绑定关系。顺手做一个快速统计看看每类框数是否合理awk {print $1} labels-yolo/train/*.txt | sort | uniq -c如果某个类别 id 一次没出现或者出现了大于 4 的数说明标签有问题训练前先查清。2.3 VOC xml 与 txt 的对应关系VOC 格式的 xml 核心标签是 object 里的 name 和 bndboxannotation filenameimg_0771_16.jpg/filename size width1280/width height720/height /size object namerotten_grape/name bndbox xmin456/xmin ymin215/ymin xmax672/xmax ymax389/ymax /bndbox /object /annotationxml 里面是像素坐标和类别英文名人眼可以直接判断这张图标注得对不对排查具体某张图时比 txt 方便得多但 YOLO 训练时要求归一化数值加类别索引所以两套格式里的同一目标坐标体系不同、语义相同。xml 里的 name 和 data.yaml 的 names 顺序是隐式绑定的txt 第一列的 2 指的是 data.yaml 里索引为 2 的那个类别名而 xml 里直接写了名字转换时靠映射字典做关联。2.4 data.yaml训练入口配置最后看 data.yaml这是所有 YOLO 系框架的训练入口train: D:/datasets/grape/images/train val: D:/datasets/grape/images/val nc: 5 names: 0: pick_point 1: spot_grape 2: rotten_grape 3: ripe_grape 4: unripe_grapetrain 和 val 指向图片目录注意不是标签目录。ultralytics 会根据图片路径自动到同名的 labels 目录找同名 txt所以图片和标签的目录名、文件名必须对齐。nc 是类别数必须等于 names 的长度写错会让输出维度直接不匹配。names 的索引顺序就是 txt 第一列的语义。路径写法上绝对路径最省心但换机器跑要改成新机器的路径相对路径比如 images/train要求终端工作目录必须位于 grape_dataset 下。这个数据集从摘要说明看适用 yolov5、yolov7、yolov8、yolov9、yolov10、yolo11 全系yolov8 及之后的版本都读这套 data.yamlyolov5 的官方仓库同样兼容字段名一致直接指过去就能用。3. 用 YOLOv8 把 165 张图训出第一版权重命令与超参取舍3.1 环境与预训练权重准备用 YOLOv8 训练自己的数据集最省事的入口是 ultralytics 这个库。有人习惯 YOLOv5 的官方仓库也能跑但 ultralytics 把训练、验证、推理统一成了 yolo 命令行后续切 v11 不用改习惯。安装pip install ultralytics建议 Python 3.9 以上。如果机器上已经装过 CUDA 版 PyTorch先装好 torch 再装 ultralytics避免 pip 自动拉一个 CPU 版 torch 下来训练速度差一个数量级。装完顺手验证一下 GPU 是否可用python -c import torch; print(torch.cuda.is_available())返回 True 就是 GPU 环境False 就准备用 CPU 慢慢跑。这个数据集 165 张图CPU 也不是不能跑但 imgsz 640 的前提下七八十轮还是有点熬人。预训练权重直接用官方的 yolov8n.pt第一次执行训练命令会自动下载。网络不给力的话提前把权重文件下载好放当前目录ultralytics 会在本地找到同名文件不会再走网络。3.2 启动训练命令与参数取舍第 2 章已经把 data.yaml 检查过一遍现在终端切到 grape_dataset 目录跑yolo detect train \ dataD:/datasets/grape/data.yaml \ modelyolov8n.pt \ epochs80 \ imgsz640 \ batch16 \ device0 \ patience20各参数怎么选我按这个数据集的实际规模给一份参考参数建议值说明modelyolov8n.pt样本只有 165 张n 级参数量最小s/m 更容易过拟合epochs60-100带早停的合理区间不要一上来写 300imgsz640葡萄果粒算中小目标640 性价比最高batch168GB 显存附近安全显存小就降到 8patience20val 连续 20 轮不涨就提前停小数据集很实用几个参数的逻辑imgsz 调到 960 对更小的果粒召回有提升但训练时间和显存占用翻倍165 张图建议先用 640 把流程跑通再回来调分辨率。batch 不是越大越好小数据集上 batch 太大反而更容易收敛到尖锐极值16 够用。patience 写成 20val 连续 20 轮不涨就直接停省下来的时间拿去分析错误样本。调参这东西一半是玄学但有几个原则不玄先保证 loss 和 mAP 曲线正常再谈刷分先确认各类别都有召回再谈总 mAP。3.3 训练产物与曲线判断训练结束产物在 runs/detect/train/ 下最关键的是 weights 里的 best.pt 和 last.pt。best 是按验证集 mAP 保存的最优权重last 是最后一轮后续验证、推理都直接用 best.pt。results.png 把每轮的 loss、mAP、P/R 画在一起。小数据集的典型曲线是训练 loss 一路降val loss 前 20 轮跟着降然后拐头向上——这就是过拟合信号。这时候优先加大数据增强而不是加模型复杂度或者干脆用 best.pt 收工。除了 loss 曲线labels.jpg 和 labels_correlogram.jpg 这两张图值得看它们画出所有标注框的位置和尺寸分布能帮你快速发现框中心点挤在图像边缘、框尺寸异常这类标签问题属于训练日志里最容易忽略的黑匣子。3.4 验证与推理看指标更要看图验证集上跑一轮正式评估yolo detect val \ dataD:/datasets/grape/data.yaml \ modelruns/detect/train/weights/best.pt输出里 mAP50 和 mAP50-95 看一眼即可。165 张图、5 个类别不要指望迁移学习一轮就刷到 0.9重点看每个类别的 recall 是否均衡。推理看实际效果yolo predict modelruns/detect/train/weights/best.pt sourceD:/datasets/grape/images/val预测图像输出在 runs/detect/predict 下一张一张打开看框贴不贴果粒、类别标得对不对、腐烂和斑点会不会互相混淆。这一步比任何指标都直观也是决定要不要回头调标注的根据。4. 标签格式互转XML 转 TXT 脚本与可视化校验4.1 为什么自己还需要转格式数据集已经准备了两种格式直接拿来训练就行。但实际场景里你很可能要用自己的图像扩数据——从其它采集批次里挑出斑点葡萄、腐烂葡萄的样本而这些数据的标注可能是 JSONLabelMe或 XMLVOC。把 XML 转成 YOLO txt 是绕不开的一步。另一个用途是校验转换过程能暴露标注问题比如某个框宽高为 0、坐标超边界、类别名和 names 对不上这些都是训练时的隐形炸弹。4.2 XML 转 YOLO TXT 的 Python 实现import xml.etree.ElementTree as ET import os # 类别顺序必须与 data.yaml 中的 names 一致 CLASS_MAP { pick_point: 0, spot_grape: 1, rotten_grape: 2, ripe_grape: 3, unripe_grape: 4, } def 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.findall(object): name obj.find(name).text if name not in CLASS_MAP: print(f跳过未知类别: {name}) continue box obj.find(bndbox) xmin float(box.find(xmin).text) ymin float(box.find(ymin).text) xmax float(box.find(xmax).text) ymax float(box.find(ymax).text) x_center ((xmin xmax) / 2) / img_w y_center ((ymin ymax) / 2) / img_h w (xmax - xmin) / img_w h (ymax - ymin) / img_h lines.append(f{CLASS_MAP[name]} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}) with open(txt_path, w) as f: f.write(\n.join(lines)) # 批量转换 xml_dir labels-voc/train txt_dir labels-yolo/train os.makedirs(txt_dir, exist_okTrue) for xml_name in os.listdir(xml_dir): if xml_name.endswith(.xml): xml_to_yolo( os.path.join(xml_dir, xml_name), os.path.join(txt_dir, xml_name.replace(.xml, .txt)) )脚本先解析 XML 的 size 拿图像宽高再遍历 object 列表把 bndbox 的像素坐标归一化成比例值。核心计算就四行中心点坐标 左右相加除 2 再除以图像宽宽高 像素差除以图像宽高。CLASS_MAP 是关键如果数据集 data.yaml 的 names 顺序和你写的不一致转换结果就错位所以动手前先打印一份 data.yaml 的 names。边界问题如果某些 xml 的 bndbox 缺失或者节点值不是数字float() 会抛异常。建议用 try/except 包住循环体打印文件名和 object 信息再决定修标注还是删掉这条记录。4.3 画框回显用 OpenCV 检查标签对齐转换完拿一张图和 txt 画框import cv2 img_path images/train/img_0771_16.jpg txt_path labels-yolo/train/img_0771_16.txt img cv2.imread(img_path) if img is None: raise FileNotFoundError(f图像读取失败: {img_path}) h, w img.shape[:2] with open(txt_path) as f: for line in f: parts line.strip().split() if len(parts) ! 5: continue cls, cx, cy, bw, bh map(float, parts) x1 int((cx - bw / 2) * w) y1 int((cy - bh / 2) * h) x2 int((cx bw / 2) * w) y2 int((cy bh / 2) * h) cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.putText(img, str(int(cls)), (x1, y1 - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 0, 255), 2) cv2.imwrite(check_visual.jpg, img)用 cv2.imwrite 而不是 imshow因为很多人是在服务器或云主机上跑没有显示器。框画出来后对照原图检查三点框是否贴住果粒边缘、类别标号和视觉是否一致、有没有框画到图像外。cx、cy 超出 0-1 范围的标注行会直接画到图外说明原始标注有脏数据训练前必须清理。5. 避坑篇葡萄小样本训练的高频翻车现场与修复这一章是血泪经验集中地。165 张图的小数据集训练本身不难难的是各种环境问题和标签问题挑四个最常遇到的展开。5.1 mAP 一直是 0标签路径或类别索引错位现象训练能跑loss 也在降但验证阶段 mAP50 始终是 0预测结果一片空白。原因最常见的是两种。一种是 data.yaml 里 train/val 路径配错了ultralytics 没找到 txt 标签自动把全图当作背景训练另一种是 txt 里的 class id 和 data.yaml 的 names 顺序对不上比如 txt 第一列写着 0但 data.yaml 里 0 是 pick_point模型学到的语义和标注不一致。解决先停训练到 labels-yolo/train 随便打开一个 txt看第一列最大值是否小于 nc再核对 data.yaml 的 names 顺序。最稳的是用第 4 章的画框脚本把 labels-yolo 的标签真实画到图上肉眼看类别 id 对不对。这一步能拦截掉绝大多数“训练正常但全错”的问题。5.2 中文路径导致图片读不进现象数据集放在 D:\桌面\葡萄数据集\grape 这种路径下训练开始后日志报 FileNotFoundError或者某个 epoch 开始全部 loss 变 0。原因ultralytics 底层走 OpenCV 和路径拼接Windows 下中文字符路径容易出现编码错乱cv2.imread 返回 None图像进不去训练自动跳过。这跟数据集本身没关系纯环境问题。解决把整个数据目录挪到纯英文路径比如 D:\datasets\grapedata.yaml 里的 train、val 同步改成绝对英文路径。另外 zip 包解压时如果文件名带中文先重命名 zip 再解压文件名也保持英文从头避免编码问题。5.3 小数据集跑到 300 轮必过拟合现象训练 loss 一路下降val loss 到 40 轮还在降60 轮之后拐头向上mAP 停在 0.7 上下不再涨best.pt 保存的却是很早的轮次。原因165 张图对小检测模型来说样本量严重不足300 epoch 足够模型“背”下训练集。葡萄果粒视觉模式相近成熟和未成熟之间边界模糊背书式的过拟合更容易发生。解决epochs 控制在 60-100打开 mosaic、mixup 增强用 yolov8n 而不是 yolov8m。还有一个习惯训练命令里显式写 patience20val 连续 20 轮不降就提前收工省下来的时间拿去补数据或调标注。5.4 训练中断 nantxt 里藏着越界或零尺寸标注现象训练跑到某个 epochloss 突然变成 nan程序直接中断重启训练后一样。原因个别 txt 标注行里 width 或 height 为 0或者中心点坐标超出 0-1比如 1.2IoU 计算时除零或产生无效梯度模型参数直接冲爆。半自动标注生成的数据集里常有这种行。解决训练前扫一遍标签awk {if($20||$21||$30||$31||$40||$50) print FILENAME, $0} labels-yolo/train/*.txt把输出里对应的那几张图单独挑出来人工确认标注是否有问题修正后重新生成 txt。我一般把这段 grep 写成一个固定脚本每次换数据集先跑一遍再训练这个习惯帮我躲过很多次训练中断。6. 进阶少数类漏检怎么办从混淆矩阵到实例粘贴增强6.1 训练前先看类别分布训练完第一版先别急着调结构。打开 runs/detect/train/ 下的 confusion_matrix.png如果腐烂葡萄那一列大量落进“背景”或“斑点葡萄”说明模型根本没把它当独立类别学。这时回看数据分布五个类里成熟葡萄可能占了大半腐烂葡萄只有零头类别不均衡直接导致 recall 偏科。6.2 用混淆矩阵定位漏检规律混淆矩阵是定位漏检规律最快的手段。如果腐烂葡萄被大量预测成斑点葡萄说明这两类在图像上长得太像标注边界也没有统一标准需要回到标签上做清洗而不是加网络复杂度如果腐烂葡萄大量预测成背景说明样本量太少该走扩充路线而不是调参路线。6.3 实例粘贴增强少数类训练集扩充复制粘贴增强是我常用的做法把少数类实例从原图抠出来随机贴到训练图的空白区域import cv2, random def paste_instance(src_img, obj_img, box): h, w src_img.shape[:2] oh, ow box[3] - box[1], box[2] - box[0] x0 random.randint(20, w - ow - 20) y0 random.randint(20, h - oh - 20) src_img[y0:y0 oh, x0:x0 ow] obj_img return src_img, (x0, y0, x0 ow, y0 oh)再把新框坐标归一化后追加到对应 txt 里。注意两点贴的位置不要盖住已有目标粘贴面积别超过原图 20%否则模型学到的是“巨大目标”而不是“腐烂葡萄实例”。虽然 165 张图的增强终究替代不了真实采样但对稳住少数类的 baseline 非常有用。我自己做葡萄分拣那阵第一版模型对腐烂葡萄的召回率只有 0.31翻车的不是网络结构而是数据分布。从那以后我每次拿到新数据集都强制先跑一遍类别统计和混淆矩阵再决定调参方向。希望帮到你。本文还有配套的精品资源点击获取