
简介这份目标检测数据集聚焦葡萄成熟度识别面向从事农业视觉、果蔬分拣或目标检测算法练习的开发者与研究者帮助解决成熟度分级样本不足、标注格式不统一的问题。资源已统一处理为YOLO格式可直接用于YOLO全系列网络训练并附带show脚本能将预测框绘制回图像上便于快速核验标注质量与训练效果。包内共2000个文件以1999个txt标签文件和1个Python可视化脚本为主压缩包约226.63MB标签覆盖未熟、半熟、成熟等4个类别具体类别可参考class文本文件原始数据超过7000张且由labelme标注。目前已有328人学习下载适合希望快速搭建葡萄成熟度检测基线、验证数据增强与模型调参策略的读者也可作为农业场景目标检测课题的现成数据支撑。1. 葡萄成熟度检测数据集7000 张图、4 个等级直接能喂给 YOLO 的落地包如果你正在做农业视觉方向的目标检测尤其是葡萄采摘机器人、果园产量预估这类项目最耗时的往往不是调模型而是搞数据。我手上这份葡萄成熟度检测数据集解压后就是一套已经处理成 YOLO 格式的完整资源超过 7000 张田间葡萄图像配套 labelme 标注转好的 txt 标签外加一个 class 类别文件训练集和验证集已经分好。类别一共 4 个覆盖未熟、半熟、成熟等不同成熟度等级具体名称直接看 class 文本文件即可。它解决的就是“从零标注到能开训”这段最枯燥的流程适合想快速验证 YOLO 系列模型在细粒度分类检测上表现的从业者也适合拿来做数据增强、类别不平衡分析的练手素材。show.py 脚本还能把 box 画回原图先看数据再决定怎么训这一步比盲目开跑重要得多。2. 拆开这份数据集目录结构、标签格式与类别映射2.1 从文件名看懂数据组织方式拿到压缩包解压后你会看到图像和标签是成对出现的。项目正文里列出的那些文件名比如00248-3249807169_png.rf.cc6b53b9066b3e78eef7f5c2b643472b.txt就是典型的标注文件命名。这种带一串哈希值的命名方式常见于 labelme 导出或某些标注平台的处理流程好处是几乎不会重名坏处是人眼没法直接判断内容。图像文件通常是同名的.jpg或.png和 txt 标签放在平行目录里。我一般拿到新数据集先做三件事数文件、看配对、读类别。数文件是确认图像数量和标签数量是否一致看配对是检查有没有孤儿标签或缺失标签读类别就是打开 class 文件确认类别顺序。这三步花不了五分钟但能避免后面训练到一半才发现标签对不上号的翻车现场。# 统计图像和标签数量确认是否配对 find images/ -type f \( -name *.jpg -o -name *.png \) | wc -l find labels/ -type f -name *.txt | wc -l # 检查是否有图像没有对应标签 for img in images/*.jpg; do base$(basename $img .jpg) if [ ! -f labels/${base}.txt ]; then echo 缺失标签: $img fi done上面这段 bash 脚本做的是最基础的完整性检查。find命令分别统计图像和标签数量正常情况下两者应该相等。第二个循环遍历每张图像检查 labels 目录下是否存在同名 txt如果缺失就打印出来。参数上注意图像扩展名如果你的数据是 png 就把-name *.jpg改成-name *.png。这一步跑完如果输出为空说明配对没问题可以继续往下走。2.2 YOLO 标签格式与类别文件解读YOLO 格式的标签是每行一个目标格式为class_id x_center y_center width height后四个值都是归一化到 0 到 1 之间的浮点数。class_id 从 0 开始对应 class 文件里的类别顺序。这份数据集有 4 个类别所以 class_id 只会出现 0、1、2、3。class 文件一般是纯文本每行一个类别名顺序不能乱因为训练时模型就是按这个顺序输出类别的。# 读取 class 文件并解析一个标签样本 with open(classes.txt, r, encodingutf-8) as f: class_names [line.strip() for line in f if line.strip()] print(类别列表:, class_names) print(类别数量:, len(class_names)) # 解析一个标签文件 label_path labels/00248-3249807169_png.rf.cc6b53b9066b3e78eef7f5c2b643472b.txt with open(label_path, r) as f: lines f.readlines() for line in lines: parts line.strip().split() cls_id int(parts[0]) x, y, w, h map(float, parts[1:]) print(f类别: {class_names[cls_id]}, 中心: ({x:.3f}, {y:.3f}), 宽高: ({w:.3f}, {h:.3f}))这段 Python 代码先读 class 文件拿到类别名列表然后解析一个具体标签文件。split()默认按空白字符分割得到 5 个值。int(parts[0])是类别索引map(float, parts[1:])把后四个转成浮点数。打印出来你就能直观看到每个框的类别和位置。常见坑是 class 文件里有空行或 BOM 头strip()能处理空行但如果第一行类别名前面有不可见字符最好用encodingutf-8-sig打开。我一般会先跑一遍这个脚本确认类别名和标签里的 class_id 能对上再开始配置训练。2.3 用 show.py 做可视化验证show.py 是这份资源里很实用的一个脚本作用是把标签里的 box 画回原图让你肉眼确认标注质量。很多人拿到数据集直接开训结果 mAP 上不去回头查才发现标注框偏移严重或者类别标错。先可视化一遍能省下大量排查时间。# show.py 核心逻辑示意读取图像和标签绘制矩形框 import cv2 def draw_boxes(img_path, label_path, class_names): img cv2.imread(img_path) h, w img.shape[:2] with open(label_path, r) as f: for line in f: parts line.strip().split() cls_id int(parts[0]) xc, yc, bw, bh map(float, parts[1:]) # 归一化坐标转像素坐标 x1 int((xc - bw / 2) * w) y1 int((yc - bh / 2) * h) x2 int((xc bw / 2) * w) y2 int((yc bh / 2) * h) cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.putText(img, class_names[cls_id], (x1, y1 - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2) cv2.imshow(preview, img) cv2.waitKey(0) cv2.destroyAllWindows()这段代码展示了 show.py 的核心逻辑。xc, yc是框中心归一化坐标bw, bh是归一化宽高。转像素坐标时左上角 x1 等于中心 x 减去一半宽度再乘图像宽其余同理。cv2.rectangle画框cv2.putText写类别名。实际使用时按空格或任意键切换下一张。注意 OpenCV 的窗口在某些远程环境下可能弹不出来这种情况可以把绘制结果用cv2.imwrite保存到本地再看。我习惯随机抽 20 张过一遍重点看成熟和半熟这两个容易混淆的类别边界框有没有把相邻葡萄串混在一起。3. 把数据集接进 YOLO 训练配置、路径与参数设置3.1 目录重组与 data.yaml 编写原始数据集的目录结构不一定直接符合 YOLO 训练的要求。YOLO 官方推荐的结构是 images/train、images/val、labels/train、labels/val 四个目录。如果拿到的数据已经分好训练集和验证集那只需要确认标签和图像在同一层级下对应即可。如果没有分常见做法是按 8:2 或 7:3 随机划分注意要按图像划分而不是按标签划分避免同一张图的标签跑到不同集合。import os import random import shutil # 按 8:2 划分训练集和验证集 image_dir images label_dir labels train_img_dir dataset/images/train val_img_dir dataset/images/val train_lbl_dir dataset/labels/train val_lbl_dir dataset/labels/val for d in [train_img_dir, val_img_dir, train_lbl_dir, val_lbl_dir]: os.makedirs(d, exist_okTrue) images [f for f in os.listdir(image_dir) if f.endswith((.jpg, .png))] random.seed(42) random.shuffle(images) split int(len(images) * 0.8) for i, img_name in enumerate(images): base os.path.splitext(img_name)[0] label_name base .txt if i split: shutil.copy(os.path.join(image_dir, img_name), train_img_dir) shutil.copy(os.path.join(label_dir, label_name), train_lbl_dir) else: shutil.copy(os.path.join(image_dir, img_name), val_img_dir) shutil.copy(os.path.join(label_dir, label_name), val_lbl_dir)这段脚本做的是数据集划分。random.seed(42)固定随机种子保证每次划分结果一致方便复现。split取 80% 作为训练集。循环里用os.path.splitext去掉图像扩展名拼出对应标签文件名然后分别复制到目标目录。参数上你可以把 0.8 改成 0.7 或 0.9看数据量和任务难度。注意如果图像和标签扩展名不一致比如图像是 jpg 标签是 txt这里逻辑是没问题的因为标签固定是 txt。划分完之后需要写 data.yaml这是 YOLO 训练时指定数据路径和类别的地方。# data.yaml train: dataset/images/train val: dataset/images/val nc: 4 names: [未熟, 半熟, 成熟, 过熟]train和val写相对路径或绝对路径都行nc是类别数names是类别名列表顺序必须和 class 文件一致。如果 class 文件里是英文这里就写英文不要中英混用。我见过有人 data.yaml 里写 4 个类别class 文件里只有 3 行训练直接报索引越界这种错误排查起来很快但一开始容易忽略。3.2 训练命令与关键参数含义YOLO 系列训练命令大同小异以常见的 YOLOv8 为例一条命令就能跑起来。yolo detect train \ datadata.yaml \ modelyolov8n.pt \ epochs100 \ imgsz640 \ batch16 \ lr00.01 \ patience20 \ projectruns/train \ namegrape_maturity逐项说明data指向刚才写的 yaml 文件model是预训练权重n 表示 nano 版本速度快适合先跑通流程后续可以换 s、m、lepochs是训练轮数100 轮对 7000 张图来说通常够用但要看收敛曲线imgsz是输入尺寸640 是默认值如果葡萄串在图中占比很小可以提到 1280但显存消耗会明显增加batch是批大小16 在 8G 显存上比较稳显存不够就降到 8lr0是初始学习率0.01 是常见起点patience是早停耐心值20 轮验证集指标不提升就停省时间。project和name控制输出目录。训练过程中重点看三个指标box_loss、cls_loss 和 mAP50。box_loss 下降说明框回归在收敛cls_loss 下降说明分类在学mAP50 是 IoU 0.5 时的平均精度。如果 box_loss 一直震荡不降常见原因是学习率太大或标注框质量差。如果 cls_loss 降不下去多半是类别不平衡成熟和未熟的样本数量差距太大可以考虑用 copy-paste 增强少数类。3.3 类别不平衡与数据增强策略4 个成熟度等级在自然场景下分布往往不均匀成熟和半熟的葡萄可能占多数未熟和过熟的样本偏少。这种不平衡会让模型偏向多数类表现为少数类的召回率明显偏低。我一般先统计每个类别的框数量做到心里有数。from collections import Counter counter Counter() label_dir dataset/labels/train for fname in os.listdir(label_dir): with open(os.path.join(label_dir, fname)) as f: for line in f: cls_id int(line.strip().split()[0]) counter[cls_id] 1 for cls_id, count in sorted(counter.items()): print(f类别 {cls_id}: {count} 个框)这段代码遍历训练集所有标签文件统计每个 class_id 出现的次数。输出后如果发现某个类别只有几百个框而最多的有几千个差距超过 5 倍就要处理。常见做法是在 data.yaml 同级配置里加增强参数比如mosaic、mixup、copy_paste或者用cls_pw给类别加权。YOLO 训练时默认开启 mosaic对小目标和不平衡数据都有帮助。如果少数类实在太少可以考虑过采样但要注意过采样容易导致过拟合验证集上表现会虚高。4. 避坑与排查标注、路径、显存这三类问题最常翻车4.1 标签类别索引越界现象训练启动时报IndexError: list index out of range或AssertionError: class id out of range。原因标签文件里的 class_id 超过了 data.yaml 中 nc 定义的范围比如 nc4 但标签里出现了 4 或更大的数字。解决写个脚本扫描所有标签文件找出最大 class_id确认是否和 class 文件行数一致。如果标签里有多余类别要么修正标签要么在 data.yaml 里补上对应类别。4.2 图像与标签路径不匹配现象训练时提示找不到标签文件或者 mAP 始终为 0。原因YOLO 根据图像路径推导标签路径默认是把 images 替换成 labels扩展名换成 txt。如果你的目录结构不是这个规则就会找不到标签。解决要么按 YOLO 推荐结构重组目录要么在 data.yaml 里正确配置 train 和 val 路径。我一般会在训练前跑一遍配对检查脚本确认每张图都有对应标签。4.3 显存不足导致训练中断现象训练几轮后报 CUDA out of memory。原因batch 太大、imgsz 太高或者数据加载器缓存过多。解决先把 batch 减半如果还不行就把 imgsz 从 640 降到 416 或 320。另外可以设置workers少一点减少数据加载进程占用的内存。如果用的是共享服务器注意有没有其他进程占着显存nvidia-smi看一眼心里有数。4.4 可视化时框偏移或类别错乱现象show.py 画出来的框位置不对或者类别名和实际内容不符。原因坐标归一化反算时用错了图像尺寸或者 class 文件顺序和标签里的 class_id 不对应。解决确认img.shape取的是高和宽不要搞反。类别错乱就重新核对 class 文件顺序确保第 0 行对应 class_id 0以此类推。如果 class 文件有 BOM 头用 utf-8-sig 读取。4.5 验证集指标虚高现象验证集 mAP 很高但实际测试时效果很差。原因训练集和验证集划分时没有按图像划分同一张图的增强版本同时出现在两个集合里造成数据泄露。解决划分前先按图像去重确保同一原始图像只出现在一个集合中。另外检查有没有重复图像7000 张图里如果有大量近似重复验证集指标参考价值会打折扣。5. 进阶技巧用混淆矩阵和单类 AP 定位模型短板训练跑完不是终点拿到模型后要知道它到底哪里不行。YOLO 训练结束会在 runs 目录下生成混淆矩阵和各类别 AP 曲线这两个东西比总 mAP 有用得多。混淆矩阵能看出类别之间有没有互相误判比如半熟被大量判成成熟说明这两个类别的特征区分度不够可能需要更细的标注或者增加这两类的样本。各类别 AP 则能直接告诉你哪个类别拖后腿如果未熟的 AP 只有 0.3 而其他都在 0.8 以上那问题就集中在这个类别上。# 从验证结果中提取单类 AP 并排序 import pandas as pd # YOLO 验证后会输出 results.csv包含各类别指标 df pd.read_csv(runs/train/grape_maturity/results.csv) # 假设列名包含 metrics/mAP50(B) 和各类别 AP ap_cols [c for c in df.columns if AP in c and mAP not in c] print(单类 AP 列:, ap_cols) for col in ap_cols: print(f{col}: 最高 {df[col].max():.3f}, 最终 {df[col].iloc[-1]:.3f})这段代码读取训练输出的 results.csv筛选出单类 AP 列并打印最高值和最终值。如果某个类别的 AP 在训练后期还在上升说明还没收敛可以增加轮数如果很早就平了甚至下降说明过拟合了需要加正则或减轮数。我一般会把混淆矩阵导出成图片对着看哪些类别容易混。葡萄成熟度这个任务里半熟和成熟之间的边界本身就模糊标注时不同人的判断可能都不一致这种类别混淆靠调模型很难根治更有效的是统一标注标准或者把两个类别合并再重新训练。还有一个实用技巧是拿验证集里置信度低的样本单独看。YOLO 验证时可以设置conf阈值把低于 0.3 的预测框对应的图像挑出来人工过一遍。这些低置信度样本往往是模型最不确定的要么是标注有问题要么是图像质量差要么是类别本身模棱两可。我上次做类似任务时就是靠这批低置信度样本发现了一批标注框只框了半串葡萄的漏标情况修正后 mAP 直接涨了 4 个点。从那以后我每次训完第一版模型都会强制走一遍低置信度样本复查这个习惯帮我省下了不少反复调参的时间。希望这份数据集和上面的流程能帮你把葡萄成熟度检测的项目快速跑起来。本文还有配套的精品资源点击获取