简介:面向YOLO系列算法(如YOLOv5/v8/v9等)的葡萄品质检测数据集,涵盖成熟葡萄、斑点葡萄、拣选点、未成熟葡萄和腐烂葡萄等类别,可直接用于模型训练、验证与测试。数据集已完成训练/验证划分,并附带data.yaml配置文件,可直接对接主流YOLO版本;同时提供YOLO格式txt与VOC格式xml两套标注,便于在不同工具链间切换使用,适合农产品分拣、品质检测等视觉任务,也适合入门者练习目标检测流程。压缩包内共496个文件,包含165张jpg图像、165个txt标注、165个xml标注及1个yaml配置,整体大小约13.86MB,体积轻量、结构清晰。目前已有55人学习下载,对需要快速验证葡萄分类效果或搭建检测管线的开发者,是一份即拿即用的数据基础。
1. yolo算法配小样本葡萄数据集,能先把分拣流程跑通再说精度
一条葡萄分拣线上,最累的不是搬箱子,是盯着传送带挑坏果。人眼看了两小时,斑点葡萄和腐烂葡萄混在一起就漏了;换上一台装了yolo算法的工控机,用这套165张带标签的葡萄图像数据集跑一次训练,检测模型就能在画面上框出成熟葡萄、斑点葡萄、未成熟葡萄和腐烂葡萄,连机械臂该抓的拣选点也一起框出来。数据集的规模不大,但类别设置完全贴着分拣工位来,适合验证一套真实可落地的视觉分选流程。适合两类人:一是准备把yolov8训练自己的数据集完整跑一遍的初学者,二是农业设备项目里需要小样本快速验证方案可行性的工程师。165张图看着少,但标签设得细,只要处理得当,靠预训练权重也够把整套流程走通,并把翻车点提前暴露出来。
2. 葡萄数据集解压与标签换算:五类标注怎么喂给YOLO格式
拿到这份zip压缩包后,我一般不会直接开训。yolo算法对数据格式极其敏感,标签错一位,训练不报错,但推理结果会全乱。先把压缩包结构、标签格式、类别编号这三件事核实清楚,后面能省一整天的排查时间。
2.1 解压后的标准检查流程:图片与标签文件必须一一对应
压缩包解压后,典型的结构是images目录放图像、labels目录放同名的txt标签文件。每张jpg对应一个txt,txt文件名与图片名完全一致,去掉后缀后一一对应。先用命令行确认数量和对应关系,这一步能发现很多隐藏问题。
# 在解压后的数据集根目录执行 # 统计图片数量 ls images | wc -l # 统计标签文件数量 ls labels | wc -l # 统计标签文件里每个类别各有多少个目标框 cat labels/*.txt | awk '{print $1}' | sort | uniq -c这段统计逻辑很简单:前两条命令分别数图片和标签文件个数,数量不一致说明存在空标注或漏标注的图;第三条命令把每个txt的第一列(类别ID)提取出来排序后计数,一眼就能看出五个类别的样本分布。正常打开zip的方式是直接双击用系统自带解压工具,如果提示压缩包损坏,先把zip换到7-Zip里解一次,多数情况是下载断档导致文件截断,重新下载即可。
类别分布是这个数据集最值得看的地方。我经手过类似的分拣数据集,最容易出现“成熟葡萄占七成、腐烂葡萄只有十来框”的极端不均衡,后面训练时模型会倾向输出多数类。看到分布之后,心里先有个数,训练参数才能定得合理。
2.2 YOLO标签坐标换算:从归一化坐标到像素框
YOLO格式的每行标签是五个数值:类别ID、中心点x坐标、中心点y坐标、框宽、框高。后四个值全部归一化到0到1之间,基准是图像的实际宽高。用目标框的像素坐标换算归一化坐标,公式是:
x_center = (x1 + x2) / 2 / image_width y_center = (y1 + y2) / 2 / image_height box_width = (x2 - x1) / image_width box_height = (y2 - y1) / image_height反过来,从txt里的归一化坐标还原像素框,可以先写一段Python验证脚本,直接看图框选得准不准。特别是“拣选点”这类小目标,如果框偏移半个身位,模型学到的东西就是错的。
# 检查单张图片的标注框位置,visual_check.py from PIL import Image def yolo_to_pixel(label_path, image_path): img = Image.open(image_path) w, h = img.size with open(label_path, 'r', encoding='utf-8') as f: for line in f: parts = line.strip().split() if len(parts) < 5: continue cls = int(parts[0]) x_center = float(parts[1]) * w y_center = float(parts[2]) * h box_w = float(parts[3]) * w box_h = float(parts[4]) * h x1 = x_center - box_w / 2 y1 = y_center - box_h / 2 x2 = x_center + box_w / 2 y2 = y_center + box_h / 2 print(f'class={cls} box=({x1:.1f},{y1:.1f},{x2:.1f},{y2:.1f})') yolo_to_pixel('labels/mature_01.txt', 'images/mature_01.jpg')这段脚本直接读txt和同名图片,把归一化坐标乘回图像宽高得到像素框。核心点是中心点坐标减半宽得左上角x,加半宽得右下角x,y方向同理。大部分标注工具导出时如果用左上角加宽高的格式,必须先转换再训练,YOLO不认绝对像素坐标。这一步顺手还能确认“拣选点”类别在标签里是否真的是目标框,而不是一个点坐标——如果txt里只有三个数或四个数,说明标注格式不是标准五维YOLO格式,需要单独清洗。
2.3 标签合法性与类别编号核对:五个类别错一位就全错
此数据集的五个类别,在标签里通常是0到4的整数编号。每张图的txt里第一个数字必须是0到4之间。偶尔会遇到标注工具导出时从1开始编号,或者中间跳号,虽然yolov8训练自己的数据集时data.yaml里的names顺序可以直接改,但和标签编号错位时,训练不会报错,只会把斑点葡萄学成成熟葡萄。
# 校验所有标签的类别是否合法,check_labels.py import glob VALID_CLASSES = {0, 1, 2, 3, 4} for label_file in glob.glob('labels/*.txt'): with open(label_file, 'r', encoding='utf-8') as f: for line_num, line in enumerate(f, 1): parts = line.split() if len(parts) != 5: print(f'{label_file}:{line_num} 非5维标注,内容: {line.strip()}') continue cls = int(parts[0]) if cls not in VALID_CLASSES: print(f'{label_file}:{line_num} 非法类别ID {cls}') for val in map(float, parts[1:]): if val < 0 or val > 1: print(f'{label_file}:{line_num} 坐标越界 {val}')代码逐个读取标签文件,做三层检查:维度是否5个,类别ID是否在0到4之间,坐标是否都在0到1之间。凡是坐标越界的文件,对应图像在训练时会被直接跳过或产生异常框,与其让训练黑匣子吞掉,不如先清理掉。超出一个像素的归一化误差可接受,但出现负数或大于1,必须删除该标签文件或手动修复。小数据集一共165张,每张都值得手动复核一遍,成本不高。
3. 用YOLOv8训练自己的葡萄分拣数据集:最小可跑通的完整命令
数据集核实完之后进入训练环节。当前yolo算法生态里,yolov8是训练自己的数据集最顺手的版本,命令行清晰、权重兼容性好。165张图不需要从零训练,直接加载COCO预训练权重做迁移学习,几个小时内就能看到结果。
3.1 环境安装与数据集目录划分
先装环境。Ultralytics官方包把训练、验证、导出全封装好了,一张显卡就能跑,CPU也能跑但很慢。
# 创建虚拟环境,Python版本建议3.10以上 conda create -n grape_yolo python=3.10 -y conda activate grape_yolo # 安装ultralytics包,会自动带起torch相关依赖 pip install ultralytics装完后把数据集按训练集和验证集划分。165张图不要全扔进train,留出20到30张做val,否则训练过程完全看不到模型泛化能力。先建目录,再把图片和对应标签按比例移动进去,注意移动后label要跟着走。
# 建目录结构 mkdir -p datasets/grape/images/train mkdir -p datasets/grape/images/val mkdir -p datasets/grape/labels/train mkdir -p datasets/grape/labels/val这里把路径提前规划成datasets/grape,后面data.yaml里的path直接指向它。目录一旦建好就不要在训练中途改名,yolov8训练时会按缓存索引文件记录路径,改了路径会报“dataset not found”而引发一系列莫名其妙的错误。
3.2 写data.yaml配置文件:五类名称与编号必须对齐
yolov8不像老版本YOLO那样用voc.txt或obj.names,而是统一用YAML文件声明数据集路径和类别名。新建datasets/grape/grape.yaml,内容如下:
# 葡萄分拣数据集配置 path: datasets/grape # 数据集根目录,相对当前运行位置 train: images/train # 训练集图片目录 val: images/val # 验证集图片目录 nc: 5 # 类别总数:成熟/斑点/拣选点/未成熟/腐烂 names: 0: mature # 成熟葡萄 1: spotted # 斑点葡萄 2: picking_point # 拣选点:机械臂抓取目标位置 3: immature # 未成熟葡萄 4: rotten # 腐烂葡萄path指向的是数据集根目录,train和val是相对于path的子路径,不是绝对路径。这里有个很容易踩的坑:path写成绝对路径后,换机器或换目录就必须改YAML,相对路径搭配运行时所在目录反而更稳。names列表的顺序必须和标签文件里的类别ID一一对应,ID0就是names里第0个。记忆力不可靠,建议把第2章的校验脚本跑完,确认类别ID分布后再填names。
3.3 训练命令与超参数选择:165张图怎么定epochs和batch
训练命令直接用ultralytics的CLI,指定模型权重、数据配置和几个关键超参数:
yolo detect train \ model=yolov8s.pt \ data=datasets/grape/grape.yaml \ epochs=150 \ imgsz=640 \ batch=16 \ patience=20 \ project=runs/grape \ name=exp1这条命令里每个参数都值得单独讲。model=yolov8s.pt表示加载COCO预训练权重,s版本对165张图的小数据集来说是性价比最好的选择,n版本更快但精度上限低,m版本容易在少量数据上过拟合。epochs=150是一次性给足迭代机会,配合patience=20让验证集指标连续20轮不涨就提前停,省时间。imgsz=640是默认输入尺寸,如果后面发现小目标漏检再往上调。batch=16取决于显存,6G显存跑16会OOM,降到8或4都行。
训练结束后,会在runs/grape/exp1/下生成weights/best.pt和last.pt。best.pt是验证集表现最好的权重,后续推理一律加载它。
除了命令行,Python里调用等价:
from ultralytics import YOLO model = YOLO('yolov8s.pt') model.train( data='datasets/grape/grape.yaml', epochs=150, imgsz=640, batch=16, patience=20, project='runs/grape', name='exp1' )两种方式没有区别,CLI适合脚本化,Python适合后面接自定义回调。最关键的一点是:不要一开始就调一堆数据增强参数。小样本训练先跑一遍全默认参数拿到基线结果,再针对问题逐项修改。一上来就加一堆增强、改损失权重,翻车后根本定位不到是哪项改动起了反作用。
4. 葡萄检测结果验证:从mAP指标到逐类误检的排查方法
训练完不等于结束,只看训练曲线就交付是典型的黑匣子式自欺。yolo算法输出的指标文件里有mAP、准确率和召回率,但165张小样本训练出来的平均指标没有逐个类别排查有说服力。这一章从指标解读到批量推理再到逐类统计,把模型验收到位。
4.1 指标先看懂:mAP50与mAP50-95在小数据集上的含义
训练结束后,在runs/grape/exp1/目录下会有results.csv,里面每一行对应一个epoch的指标变化。重点看的是mAP50和mAP50-95。
mAP50表示IoU阈值取0.5时的平均精度均值,它衡量的是“框大概框对就算对”。mAP50-95是把IoU阈值从0.5到0.95每隔0.05算一次再取平均,衡量的是框的定位精确度。小数据集上,mAP50-95数值很低是常态,不要焦虑,0.2到0.3都正常,因为它对框的边界极其苛刻;但mAP50如果低于0.5,说明模型确实没学好,需要排查数据和训练配置。
还要看每个类别的召回率。yolov8会在results.csv里给每个类别的精确率和召回率列。对分拣场景,漏检腐烂葡萄的代价远高于误检成熟葡萄,所以召回率比mAP更要紧。如果腐烂葡萄的recall只有0.1,说明模型基本没学会这个类,这时候就算mAP50整体到了0.6,上线后照样漏拣坏果。
4.2 批量推理:用best.pt对验证集做带标注输出
用训练好的best.pt对验证集做批量预测,并让预测结果同时输出txt和置信度:
yolo detect predict \ model=runs/grape/exp1/weights/best.pt \ source=datasets/grape/images/val \ save_txt=True \ save_conf=True \ project=runs/grape_predict \ name=val_pred推理结果会写到runs/grape_predict/val_pred/下,图片和txt同名输出。save_txt=True把每张图的预测框写成yolo格式文本,save_conf=True在每行末尾追加置信度。打开一张标注过的葡萄图看一眼,重点不是看框得多漂亮,而是看有没有把成熟葡萄误框成斑点葡萄、腐烂葡萄到底有没有漏。
如果没有可视化环境,用下面这段代码把预测结果按类别归并统计:
# 统计预测结果中各类别目标数量和平均置信度,count_pred.py import os import glob base_dir = 'runs/grape_predict/val_pred/labels' class_count = {} conf_sum = {} for txt_file in glob.glob(os.path.join(base_dir, '*.txt')): with open(txt_file, 'r') as f: for line in f: parts = line.strip().split() if len(parts) < 6: continue cls = int(parts[0]) conf = float(parts[5]) class_count[cls] = class_count.get(cls, 0) + 1 conf_sum[cls] = conf_sum.get(cls, 0) + conf for cls in sorted(class_count): avg_conf = conf_sum[cls] / class_count[cls] print(f'类别 {cls}: 预测 {class_count[cls]} 个目标,平均置信度 {avg_conf:.2f}')这个脚本的作用是快速量化每个类别的输出情况。如果成熟葡萄预测了两百个框、腐烂葡萄只预测了三个,不用看指标也知道模型废了。平均置信度低于0.3的类别,基本处于不可用状态,后续要么补数据要么调阈值。
4.3 验证集与预测结果的对比:找出最容易漏的类别
把验证集标签里的真实框数量和预测结果并排对比,差距最大的类别就是接下来要优化的对象:
# 对比GT与预测数量,find_gap.py import os import glob gt_count = {} for txt_file in glob.glob('datasets/grape/labels/val/*.txt'): with open(txt_file, 'r') as f: for line in f: cls = int(line.split()[0]) gt_count[cls] = gt_count.get(cls, 0) + 1 pred_dir = 'runs/grape_predict/val_pred/labels' pred_count = {} for txt_file in glob.glob(os.path.join(pred_dir, '*.txt')): with open(txt_file, 'r') as f: for line in f: cls = int(line.split()[0]) pred_count[cls] = pred_count.get(cls, 0) + 1 for cls in sorted(gt_count): gt = gt_count.get(cls, 0) pred = pred_count.get(cls, 0) diff = gt - pred print(f'类别 {cls}: GT {gt} 框, 预测 {pred} 框, 差 {diff}')这里GT是全量标注框,预测框只要类别对就算对,不区分是否严格匹配IoU。这个粗略对比能在五分钟内定位“哪个类根本没学会”。差别最大的类,要么单独补样本,要么单独调阈值。小数据集方向上,这一步的血泪经验是:永远先修最大的短板类别,平均指标再好看也救不了漏检。
5. 165张小样本训练避坑:5个高频翻车现场与排查思路
样本量越少,越容易在小地方翻车。这一章整理我反复遇到过的五类问题,全部按“现象—原因—解决”的格式记录,定位路径可以直接照抄。
5.1 训练正常跑完但mAP为0:路径和类别编号没对上
现象:loss一直在降,几十轮训练结束后results.csv里的mAP50始终是0,best.pt预测出来的结果是空的。
原因:最常见是data.yaml里的path指向错了目录,或者val目录下没有任何图片。yolov8对空的验证集不报错,直接返回全零指标。另一个原因是标签文件里出现非0到4的类别ID,模型训练时在损失计算阶段把这些框过滤掉了。
解决:先检查val目录下图片与标签数量:
ls datasets/grape/images/val | wc -l ls datasets/grape/labels/val | wc -l两个数必须一致且不为0。再跑一遍第2.3节的校验脚本,确认没有非法类别ID。路径问题改data.yaml,标签问题修标签,重训前先跑这两步。
5.2 所有预测结果都框成成熟葡萄:类别不均衡暴露无遗
现象:可视化验证集预测结果时,图像上密密麻麻全是成熟葡萄框,斑点葡萄一个都没有,腐烂葡萄偶尔出一两个框,置信度还都在0.3以下。
原因:该类别的目标框数量严重偏少。比如成熟葡萄有800个框、腐烂葡萄只有20个框,模型在损失计算时,多数类贡献的梯度完全压过少数类。165张小数据集,每张图一两个目标,一个类就几十个框,碰上不均衡就是天然劣势。
解决:先给稀疏类别加权重,yolov8的data.yaml里用loss_gain或cls参数调整类别损失权重,或者直接复制少数类样本做简单增强(左右翻转、微调亮度)把数量拉上来。我一般先用最简单的方式,把腐烂葡萄和斑点葡萄的图片在训练集里复制成两份,一份原图一份水平翻转,数量接近成熟葡萄的六成再重训。加权损失虽然更快,但阈值不好把握,容易把模型从“什么都测成多数类”带到“少数类狂误检”。
5.3 斑点葡萄和“拣选点”小目标漏检:框尺寸太小导致特征丢失
现象:成熟葡萄框得准,但斑点葡萄和拣选点在预测结果里频繁丢失,即使GT里有标注也检出率很低。
原因:这两个类别的小目标框在缩放至640×640输入后,可能只占几十个像素,特征图上的响应非常弱。yolo算法天生对大目标友好,小目标需要靠更高分辨率和更多上下文。
解决:把imgsz从640调到960,显存够就上1280。小数据集上显示尺寸放大带来的收益往往比换更强的主干网络更直接。如果显存不够,用切图训练策略,把原图切成四块分别训练,预测时也切图后合并。165张图的场景下,优先尝试imgsz=960,成本最低。
5.4 标签文件乱码或解压提示zip伪加密:编码问题与压缩包细节
现象:解压后部分标签txt打开是空的,或者显示一堆问号;解压时系统提示“需要密码”,但输入任何密码都失败。
原因:标签文件被标注工具以UTF-8带BOM的格式保存,或用GBK编码直接写入,Linux下读取时首行出现不可见字符,导致yolo解析失败。扫描压缩包提示zip伪加密,是因为打包时用了第三方工具的错误加密标志位,文件本身并没有真实加密。
解决:标签文件统一转成UTF-8无BOM格式。批量转换命令是:
# 把所有txt转成UTF-8无BOM find labels -name "*.txt" -exec sed -i '1s/^\xEF\xBB\xBF//' {} \;zip伪加密的压缩包,用7-Zip打开,勾选“显示加密文件”再解压,多数情况下能直接解出来;个别情况需要复制到Windows上用WinRAR解压。真实加密的zip请不要尝试绕过去,直接找文件提供方要密码,这也是数据合规的基本边界。
5.5 训练直接OOM:降batch而不是一上来就降imgsz
现象:训练启动后几秒钟,显存报错,显示CUDA out of memory,batch和imgsz越大报得越快。
原因:batch和imgsz共同决定显存占用,二者呈平方关系。6G显存跑imgsz=640、batch=16基本是极限边缘。
解决:先降batch到4或2,显存占用立刻降四倍。如果降完仍然OOM,再降imgsz到512。记住优先级:先降batch后降imgsz。batch降到2还可以用梯度累积等效模拟大batch,yolov8里的训练脚本支持accumulate参数。另一个更粗暴的办法是把yolov8s换成yolov8n,模型本身小一截,显存占用立刻减少。
6. 让165张样本发挥更大价值:预训练权重、增强参数与半自动标注迭代
样本量不大的项目,决定上限的不是训练技巧,而是预训练权重的选择和样本利用方式。这套葡萄数据集按我上面流程跑通后,如果你还想往下走,我最常用的三板斧如下。
第一板斧,预训练权重选yolov8l或yolov8x的COCO权重,但只做特征提取,冻结主干再训自己的检测头。165张图里学到的特征不够充分,冻结前50层的做法能让模型先适应葡萄的边缘纹理,再慢慢松开全网络微调。具体操作是把训练命令加上freeze=10,表示冻结前10层,跑50轮后去掉freeze参数再微调50轮,比全程用默认权重收敛得更稳。
第二板斧,增强参数按类别定制。斑点和腐烂这类稀缺类别使用马赛克增强在训练后期会起反作用,把目标切碎导致小目标更难学。最后一个epoch前关闭马赛克,只保留翻转和HSV微调。
第三板斧,半自动标注迭代。用第一版best.pt在无标签的葡萄图上批量出框,把置信度高于0.75的框直接当成预标注,置信度在0.4到0.75之间的框交给人工修坐标,低于0.4的丢弃。这样一轮下来,把新标注结果合并进原数据集重新训练,类别均匀度会改善很多。我个人的习惯是每个类别至少凑到50个目标框以上再谈稳定性,样本不够就先测流程,不要急着调超参数。这套流程给你,希望帮到你。
本文还有配套的精品资源,点击获取