简介:这是一套面向目标检测开发者的葡萄品质检测数据集,支持yolov5、yolov7、yolov8、yolov9、yolov10、yolo11等yolo系列算法,适合正在学习模型训练或落地农产品质检方案的读者。数据围绕葡萄外观分拣场景,标注了成熟葡萄、斑点葡萄、未成熟葡萄、腐烂葡萄和拣选点五类目标,既覆盖成熟度判断,也包含表面缺陷与人工拣选点定位,可直接用于训练、验证与测试。压缩包共496个文件,大小13.86MB,内含165张jpg原图、165个yolo格式txt标签、165个voc格式xml标签以及1个data.yaml配置文件。yolo标签采用类别加归一化中心点与宽高的格式,voc标签便于使用XML工具转换,数据集已自动划分并配备配置,无需再整理目录即可投入训练。此外,data.yaml中已写清训练、验证、测试路径,模型训练时无需手动改配置,初学者也可通过对比两套标签理解不同框架的标注规范。目前已有55人学习。对从事农业自动化或果实分拣场景的开发者来说,资源能省去采集、清洗、标注和格式转换的繁琐步骤,快速检验yolo系列模型在葡萄外观缺陷识别上的表现,整体目录结构清晰,适合作为论文或课程实验的数据支撑。
1. 葡萄好坏检测数据集:165张带标签图像能跑通YOLO吗
在果蔬分拣的视觉项目里,真正卡住进度的往往不是模型结构,而是数据。这套葡萄好坏数据集是围绕yolo算法训练准备的,压缩包里一共165张带标签图像,覆盖成熟葡萄、斑点葡萄、拣选点、未成熟葡萄、腐烂葡萄五个类别。图量不大,属于标准的小样本范围,但只要能格式化、能训练、能看混淆矩阵,它就能在一小时内让你跑通一次葡萄分级的基线验证。适合刚入门的算法工程师,也适合产线上想快速回答“视觉拣选到底可不可行”的现场人员。读完你可以从解压开始,自己完成标签检查、目录整理、模型训练和结果评估,并清楚知道这套数据能撑到什么程度、哪些地方必须手动补数据。
2. 拆开zip看数据结构:标签格式与五类葡萄的标注含义
2.1 一张图对应一个txt:YOLO标签格式怎么读
先解开压缩包,确认组织方式。大多数YOLO检测数据集会把图像和标签分成两个一级目录,例如images/和labels/;也有的直接把所有.jpg和.txt堆在同一层。无论哪种形式,图片和标注文件都是同名的:IMG_001.jpg对应IMG_001.txt,txt 里一行一个目标对象。
txt 每行固定五个字段:
class cx cy w h其中class是类别编号,从 0 开始;cx和cy是目标框中心点相对整张图宽高的比例;w和h是目标框宽度、高度相对整张图宽高的比例。这些值全部是 0 到 1 之间的小数,也就是归一化坐标,和 OpenCV 画框用的像素左上右下坐标完全不同,千万别混。下面是一行真实示例:
0 0.482 0.375 0.226 0.481意思是类别 0(成熟葡萄),中心点横坐标在图像 48.2% 处,纵坐标在 37.5% 处,目标框宽约图像宽 22.6%,高约图像高 48.1%。拿到手先打印几个 txt 看个开头,确认是这种空格分隔的纯文本。如果看到逗号、括号或像素坐标,说明原标签是从 VOC XML 或 JSON 转换了一半,后续训练前必须先做格式转换。
2.2 五类样本的特征与标注边界
这套数据集里五个类别的含义,直接决定训练效果,建议先对齐认知。我用下面这张表记录五类葡萄在图像里的典型特征和标注约定:
| 类别ID | 类别名 | 典型视觉特征 | 标注说明 |
|---|---|---|---|
| 0 | mature | 成熟葡萄,紫红或暗红色,果粒饱满 | 框住整串葡萄外轮廓,尽量贴合边缘,不要留大块背景 |
| 1 | spotted | 斑点葡萄,果粒表面有明显褐斑、黑斑 | 框住整串,斑点不需要单独再框 |
| 2 | picking_point | 拣选点,果梗末端靠近藤蔓的剪切位置 | 框很小,只框剪口区域,不框整根果梗 |
| 3 | immature | 未成熟葡萄,青绿色,果粒硬且紧 | 框住整串 |
| 4 | rotten | 腐烂葡萄,果粒软塌、霉层或汁液渗出 | 框住整串,如果霉斑占满全串只标腐烂 |
粗看是五分类,实际难点集中在picking_point。因为拣选点不是一串葡萄而是果梗上的一个切点,目标尺寸可能只有成熟葡萄框的十分之一,在 640x640 的输入尺度下只有十几个像素宽,非常容易被模型当成背景丢弃。标注时如果框得不够紧,把整段果梗都包进去,训练出来的框中心就会偏,部署时机械臂按中心下剪也会剪错位置。
成熟、斑点、未成熟、腐烂这四个类别之间,边界可以依赖颜色和表面纹理,但斑点和腐烂同时出现时要定一个优先级。我的做法是:颗烂面积大到看不出完整果粒时优先判腐烂;只有表面斑点且果粒仍完整时判斑点。同一张图里多类目标可以共存,但一个目标只允许有一个类别。
2.3 三个小脚本检查标签质量
我拿到任何 YOLO 数据集都不会直接开训,先做三件事:统计类别分布、查越界坐标、可视化抽查。
第一个脚本统计每个类别出现次数,判断类别不平衡程度:
from pathlib import Path labels = list(Path("labels").glob("*.txt")) counts = {} for lb in labels: for line in lb.read_text().strip().splitlines(): if line: cls = line.split()[0] counts[cls] = counts.get(cls, 0) + 1 print("标签文件数:", len(labels)) print("各类别目标数:", counts)这个脚本输出的各类别目标数直接告诉你腐烂葡萄有多少条标注。如果某个类别总数小于 5,后面训练基本等于陪跑,不用指望它能被检测出来。按照经验,最低类别目标数应该超过 20,并且最好和最多类别的数量差距在 10 倍以内。
第二个脚本检查归一化坐标是否越界,顺便过滤框宽高异常的样本:
import numpy as np from pathlib import Path labels = list(Path("labels").glob("*.txt")) bad = 0 for lb in labels: arr = np.loadtxt(lb, ndmin=2) if arr.size == 0: continue # 空标签文件,表示该图没有目标,合法 if arr[:, 1:].min() < 0 or arr[:, 1:].max() > 1: print(lb, "坐标越界") bad += 1 wh = arr[:, 3:5] if (wh <= 0).any() or (wh > 1).any(): print(lb, "宽高异常") bad += 1 print("异常文件数:", bad)越界坐标通常来自标注软件里未归一化的输出直接被脚本拼接,或者从 JSON 转 txt 时除以了错误的分辨率。即便只有一两张越界,训练时也可能让损失值突然飙高。
第三个脚本把标签画回图上,人工抽查。这是血泪经验,不画图直接训练,等训练完才发现标签错位就晚了:
import cv2 from pathlib import Path # 挑一张图,改成实际存在的文件名 img_id = "IMG_001" img = cv2.imread(f"images/{img_id}.jpg") h, w = img.shape[:2] for line in open(f"labels/{img_id}.txt"): c, cx, cy, bw, bh = map(float, line.split()) 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(c)), (x1, y1 - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 0, 255), 2) cv2.imwrite(f"viz/{img_id}.jpg", img)抽查至少三十张,重点看小目标拣选点。如果标注框的中心和果梗剪口偏移明显,甚至只是画了一个点,这样的标签需要先修。YOLO 在训练时会忽略小于 0.012 倍图宽的框,也就是说一张 640 宽的图里,目标宽度小于 8 像素基本就学不到,后面会在避坑章节专门处理。
3. 用YOLOv8训练葡萄检测:从目录整理到最小训练命令
3.1 按YOLO惯例整理目录并划分训练集
解压、检查完标签后,下一步是把数据集整理成 YOLOv8 默认阅读的目录结构。常见做法是建立dataset/根目录,下面分images/train、images/val、labels/train、labels/val四个子目录。165 张小图按 8:2 划分,大约 132 张训练、33 张验证。划分时用固定随机种子,保证每次复现结果一致:
import glob import random import shutil import os random.seed(42) paths = glob.glob("images/*.jpg") + glob.glob("images/*.jpeg") + glob.glob("images/*.png") paths.sort() random.shuffle(paths) # 打乱,避免连续图片来自同一拍摄批次 split = int(len(paths) * 0.8) for i, p in enumerate(paths): if i < split: sub = "train" else: sub = "val" name = os.path.basename(p) shutil.copy(p, f"dataset/images/{sub}/{name}") stem = os.path.splitext(name)[0] lb = f"labels/{stem}.txt" if os.path.exists(lb): shutil.copy(lb, f"dataset/labels/{sub}/{stem}.txt")这里用shutil.copy而不是os.rename,保留原压缩包内容,方便后面反复调整划分。如果磁盘紧张,也可以把copy改成symlink,但要确保训练环境支持软链接。划分之后最好人工确认一下,别让腐烂葡萄的 6 个样本全落在验证集里,那样训练集根本没见过腐烂葡萄。
3.2 写data.yaml并启动第一次训练
YOLOv8 需要一份data.yaml描述数据集路径和类别名。在dataset/同级创建data.yaml:
path: /your/absolute/path/dataset train: images/train val: images/val names: 0: mature 1: spotted 2: picking_point 3: immature 4: rotten注意path建议写绝对路径。我踩过相对路径的坑:当命令行工作目录切换时,YOLOv8 会顺着相对路径找不到 images。names顺序必须和标签 txt 里的 class id 一一对应,不能只看类别名,要看标注时给每个类别分配的 id。
启动训练用这条最小命令:
yolo detect train \ data=data.yaml \ model=yolov8n.pt \ epochs=100 \ imgsz=640 \ batch=16 \ patience=20 \ cache=False \ project=runs \ name=grape_quality在这里model=yolov8n.pt表示从官方预训练权重开始微调,而不是随机初始化。165 张葡萄图根本撑不起从头训练,必须用迁移学习。去年我用类似数据集做过对比:用yolov8n.pt预热能到 mAP 0.8 左右,随机初始化只能到 0.5 上下。epochs=100对小数据集足够多,配合patience=20做早停,如果连续二十轮验证 mAP 没有提升就自动结束,避免后段过拟合。
3.3 训练命令里的关键参数和小样本微调
以下是这套小样本任务里需要重点关注的参数,表格里给出了推荐值和理由:
| 参数 | 推荐值 | 作用和调整思路 |
|---|---|---|
| model | yolov8n.pt | nano 模型参数量最小,最不容易在 165 张图上过拟合 |
| imgsz | 640 | 保留拣选点小目标的尺寸特征;缩到 416 会加剧小目标漏检 |
| batch | 16 | 8G 显存可跑;显存紧张降到 8,但不建议降到 2 |
| patience | 20 | 比默认值 100 小很多,小数据集不需要那么长耐心 |
| cache | False | 165 张图虽然不大,但云服务器内存常被别人的任务占用 |
| amp | True | 混合精度能省显存;如果训练出现 NaN loss,先关掉 amp |
如果你觉得验证集 mAP 在第一轮之后始终上不去,不要急着加 epochs,而是加数据增强。YOLOv8 内建了 HSV 抖动、翻转、缩放等增强策略,可以在命令行直接关掉不合理的项。针对葡萄这种圆形目标,水平翻转很安全,垂直翻转在真实产线上并不常见,可加可不加。比较危险的是mosaic=1.0默认打开,会把四张图切碎拼成一张,小样本下容易让半截葡萄参与训练,后面会单独讲。
我一般会在最小命令基础上追加一行增强控制,跑第二版:
yolo detect train \ data=data.yaml \ model=yolov8n.pt \ epochs=100 \ imgsz=640 \ batch=16 \ patience=20 \ hsv_h=0.02 \ hsv_s=0.5 \ flipud=0.0 \ mosaic=0.0hsv_h=0.02是色调扰动,调到 0.05 会让葡萄颜色明显偏色,没必要。flipud=0.0关闭垂直翻转,因为产线上相机角度固定,不需要模拟倒挂。mosaic=0.0关闭马赛克增强,在只有 132 张训练图的任务里,我宁可损失一部分数据多样性,也要保证每个目标都是完整可见的。
4. 小样本葡萄数据集的五大踩坑与排查
4.1 现象:训练loss一路下降,验证mAP却很低
这是小样本训练最容易撞见的翻车现场。现象是终端里训练损失降到 0.1 以下,看起来模型已经收敛,但验证集 mAP@0.5 始终徘徊在 0.6 以下,甚至比上一轮还差。
原因很直白:165 张图对 YOLOv8 来说太小了,模型记熟了训练图里的背景纹理,没有学到泛化的葡萄特征,也就是过拟合。验证集里的光照条件一变,检测能力立刻裸露。
解决方法分三步。第一,模型换小:用yolov8n.pt而不是yolov8s.pt或更大的 m,nano 模型的参数量低一个量级,过拟合压力小很多。第二,加冻结层:在训练命令里加freeze=10,让预训练权重的前十层骨干保持冻结,只更新后面检测头,等于用手头预训练好的边缘、纹理特征,只学“怎么框住葡萄”。第三,开早停:patience=20加上去,验证 mAP 二十轮不涨就停,别再顽固跑满一百轮浪费时间。
如果这三步做完仍然 mAP 低,再回头看标签可视化,大概率是框不贴边,误差来自数据而不是模型。
4.2 现象:腐烂葡萄类别在验证集上一个都检不到
现象是训练结束打印各类别 AP,腐烂那行基本是 0.001 或 0,其他类别至少 0.7。这是典型的类别不平衡,压包里腐烂葡萄可能只有几张图,标签数量趋于个位,模型在训练时根本没被喂够腐烂特征。
先跑统计脚本看数量。如果腐烂目标数少于 10,就不要指望单靠调整参数解决。一个可行的临时补救是数据重复采样:把训练集中腐烂样本对应的小图和标签复制若干份,换文件名后丢进同一个训练集目录,让模型在一轮 epoch 内多看到几次腐烂样本。示例:
cp labels/train/IMG_088.txt labels/train/IMG_088_a.txt cp images/train/IMG_088.jpg images/train/IMG_088_a.jpg复制后记得确认目标框的位置不需要改,因为图没变化,只变了文件名。这种方式能提升腐烂类别的 recall,但使用次数别太过分,同一个图复制超过五份,模型会对这张图的具体细节过度记忆。
另一个办法是推理时降低置信度阈值验证:conf=0.05跑一遍验证集,如果大量腐烂葡萄在 0.05 阈值下能被召回,说明模型其实学到了特征,只是置信度低,可以接受。如果连 0.05 都召不回,就只能回到标注,补一批腐烂程度更典型的图。
4.3 现象:precision低,把葡萄叶子和果梗当成葡萄
现象是混淆矩阵里 background 那一列偏高,验证图里机械臂支架、叶片阴影被画上葡萄框。原因通常是框得太大,把背景包进正样本,模型学到了“绿叶和深色背景都算成熟葡萄”;或者整个数据集里没有一张无目标的背景图,模型缺少负样本。
解决方法很直接:先用 labelimg 把明显过大的框缩回紧贴果串轮廓,这一步手工但值得;然后往dataset/images/train里放一批没有葡萄的产区背景图,比如叶子、藤蔓、地面,同时在dataset/labels/train放入对应同名的空 txt 文件,告诉模型这些图里没有任何目标。YOLO 支持空标签文件,这就是你的负样本。
如果你不想改标注,也可以把推理阈值调高到conf=0.35,让模型少一些误检。但这是指标自欺,真实场景里漏检会比误检更危险,我一般不会在没加负样本之前就走这条捷径。
4.4 现象:mosaic增强在小样本上把葡萄切碎,AP反而变差
现象是开启默认增强时训练 loss 很漂亮,但验证集小目标 AP 接近 0,尤其picking_point完全消失。原因就是 mosaic 把 4 张图缩放后拼成一张,为了拼进来,大多数目标被裁掉一半,模型看到的都是半串葡萄。对于葡萄这种轮廓柔和的聚合体,半串和整串差很远;对拣选点这种小目标,马赛克拼接后可能只留下 3 个像素,直接被忽略。
解决方法是训练时关闭 mosaic,并把 mixup 也关掉。在 3.2 的最小命令里加mosaic=0.0 mixup=0.0,如果版本不支持命令行传这两个参数,就在训练用 yaml 里单独写超参段。这个做法是让模型看到尽量完整的目标,而不是追求教条上的数据多样性。
我见过有人用小样本硬开 mosaic,最后专门花两天清理标注,不如一开始就关掉省时间。关闭后训练速度会下降一点,但 165 张图的一百轮训练本来就只要几十分钟,完全可以接受。
4.5 现象:训练中途内存或 swap 爆满,IO 卡到跑不动
现象是命令启动后,cache=True快速把图像读进内存,然后内存不足的进程被系统 OOM,或者 swap 占用接近 100%,训练速度从每轮 10 秒变成每轮一分钟。
原因有两层。一是 165 张图虽然数量少,但如果单张原图是 4000x3000 像素的高清图,全部缓存也会吃满 16G 内存;二是 workers 开太大,每个进程都要读一份数据队列。解决很简单:cache=False,让 YOLO 每次都实时读图,同时把workers=2。还有一个小技巧是把数据集放到本地 SSD,而不是网络挂载盘,网络盘随机读小文件会疯狂拖慢每个 epoch。
这个坑很大程度上是玄学,不同云厂商的挂载盘 IO 差异极大。如果你发现每个 epoch 的时间越来越长,就先怀疑 IO,用cache=False workers=1测一版,通常立竿见影。
5. 用混淆矩阵和错误可视化检验这包数据值不值得补
5.1 从val输出文件里取混淆矩阵和F1曲线
训练结束后不要只看终端里那一行 metrics,进runs/detect/grape_quality/目录,里面会生成confusion_matrix.png、F1_curve.png、results.csv。混淆矩阵横坐标是预测类别,纵坐标是真实类别,对角线越亮说明对应类别的准确率越高。重点看两个位置:成熟葡萄那一行是否有一部分落进斑点列,腐烂葡萄那一行是否落进背景列。前者说明两个类别框内视觉差异不够,后者说明腐烂样本数量不够模型建立边界。
F1 曲线则告诉你置信度阈值应该设在多少。取曲线顶点附近的 conf 值作为实际推理阈值,这比默认 0.25 要可信。小样本训练后 F1 曲线经常在 0.1 到 0.4 之间起伏,说明模型对置信度极不稳定,这种情况下阈值宁可偏高,也别让误检干扰下游。
5.2 把预测错图挑出来,按错分类型决定补标签方向
用训练出的 best.pt 对验证集跑一次预测:
yolo detect predict \ model=runs/detect/grape_quality/weights/best.pt \ source=dataset/images/val \ conf=0.25预测结果带框存到runs/detect/predict。人工逐张看,把错分归类。如果大量成熟葡萄被标成斑点,说明标注时类间边界不一致,需要回到标注软件统一判断标准;如果未成熟葡萄频繁漏检,多半是因为每串葡萄遮挡严重,标注框不完整;如果拣选点错检到果梗中部,说明框的几何中心没有对准剪口,下一步补标注时用更小更紧的框。这个错检分类表,直接决定你要继续补什么数据,而不是盲目加糖。
5.3 我的一个收尾习惯:先跑基线,再决定要不要补数据
我拿到这种小数据集压缩包的习惯是,先不做任何复杂优化,严格按照第 3 章的最小命令跑一版 100 轮基线,然后打开混淆矩阵看错分结构,而不是看最终 mAP。如果每类 recall 都能到 0.7 以上,说明这包数据质量不错,值得继续做重复采样、增强调参;如果某一类 recall 在 0.3 以下,那就别再和超参数死磕,回到产线多拍几十张对应状态补标签,这比调一百个参数都管用。这套数据集适合用来练手、验证流程,也适合给后续数据采集打底。希望帮到你。
本文还有配套的精品资源,点击获取