基于YOLO的西红柿成熟度检测:1267张图像训练三分类模型实战
简介这份资源是面向计算机视觉学习者与智能农业开发者的西红柿成熟度目标检测数据集可直接用于YOLO系列算法的训练与验证帮助解决果蔬成熟状态自动分类的识别难题。压缩包共2000个文件以1267个xml标注文件和733个txt标签文件为主xml记录目标框与类别信息txt适配YOLO训练格式整体约303.56MB覆盖半熟、绿色、完全成熟三个阶段的1267张图像。目前已有233人学习下载适合课程设计、科研实验与农业监测项目练手。数据集对成熟度划分细致能帮助模型学习绿红过渡斑点与全红外观之间的色彩和形态差异提升泛化能力与检测精度为收获效率与品质管理提供数据支撑。1. 西红柿成熟度分级从 1267 张带标签图像到可用的 YOLO 检测器温室采摘线上最让人头疼的不是有没有西红柿而是这颗到底该不该摘。半熟、绿色、完全成熟三个状态混在一批果子里靠人眼分拣一个班次下来眼睛发花标准还会漂移。这个标题指向的就是把这件事交给 YOLO用 1267 张已经打好标签的西红柿图像训练一个能区分半熟、绿色、完全成熟三个成熟度的目标检测模型。它适合两类人一类是手里有类似农业图像数据、想跑通 YOLO 训练全流程的算法工程师另一类是做智能采摘、分拣设备需要先验证视觉方案可行性的产品与硬件同学。数据集规模不大单卡就能训但小数据集恰恰最考验标签质量、类别定义和增强策略这也是后面要重点拆的部分。2. 先想清楚三分类的边界半熟、绿色、完全成熟到底怎么标2.1 成熟度是连续量但检测器只认离散框西红柿从青到红是一个渐变过程可 YOLO 输出的是离散类别加边界框。标题里给的三个类别——半熟、绿色、完全成熟——本质上是把连续的颜色变化切成了三段。切分点定在哪里直接决定模型能不能学。常见做法是看果面转色比例绿色指整体青绿、无红晕半熟指出现明显橙红转色但未覆盖大部分果面完全成熟指大面积红色、达到可采摘状态。如果标注时三个人三套标准模型学到的就是噪声mAP 会卡在一个不高不低的位置上不去这种玄学问题十有八九出在标注一致性上。提示动手训练前先随机抽 50 张图让两个标注同学各自复核一遍类别统计不一致率。超过 10% 就先统一标准别急着开训。2.2 1267 张够不够取决于类别分布和场景多样性1267 张在目标检测里属于小数据集但农业场景往往背景相对固定够不够用要看两点一是三个类别的实例数是否均衡二是光照、遮挡、拍摄角度是否覆盖了实际工况。如果完全成熟占了七成绿色只有几十个实例模型会强烈偏向多数类。这时候要么补采要么在损失和采样上做平衡。YOLO 的损失函数本身对类别不平衡没有特殊照顾分类分支用的是交叉熵类损失少数类样本少梯度贡献就小这是小数据集翻车的常见原因。2.3 标签格式与目录组织先统一成 YOLO 能吃的结构标题说“带标签”但标签可能是 VOC XML、COCO JSON也可能是已经转好的 YOLO txt。不管原始是什么训练前都要统一成 YOLO 格式每张图对应一个同名 txt每行是类别索引 中心x 中心y 宽 高坐标全部归一化到 0 到 1。目录一般组织成 images 和 labels 两个平行文件夹再各分 train、val。下面这段脚本把常见的一图一 txt 标签做一次合法性检查能提前揪出越界框和空标签。import os import glob # labels 目录下所有 txt逐行检查 YOLO 格式合法性 label_dir datasets/tomato/labels/train bad_files [] for txt_path in glob.glob(os.path.join(label_dir, *.txt)): with open(txt_path, r) as f: lines [l.strip() for l in f if l.strip()] if not lines: bad_files.append((txt_path, 空标签)) continue for line in lines: parts line.split() # YOLO 每行必须是 5 个值cls x y w h if len(parts) ! 5: bad_files.append((txt_path, f字段数异常: {line})) continue cls, x, y, w, h parts vals [float(x), float(y), float(w), float(h)] # 归一化坐标必须落在 0~1宽高必须为正 if any(v 0 or v 1 for v in vals) or vals[2] 0 or vals[3] 0: bad_files.append((txt_path, f坐标越界: {line})) print(f检查完成问题文件 {len(bad_files)} 个) for p, reason in bad_files[:20]: print(p, reason)这段逻辑很直白逐文件读标签先排除空标签再检查每行字段数和归一化坐标范围。参数上label_dir换成你自己的路径即可判断阈值用的是 0 和 1 的硬边界实际中如果发现大量框贴边可能是标注时框到了图像外需要回原图确认。跑完这个检查再进训练能省掉很多“loss 不降但也不报错”的排查时间。3. 用 YOLOv8 跑通训练配置文件、命令与三个必调参数3.1 数据配置文件怎么写YOLO 系列训练入口通常是一个 YAML 数据配置指明训练集、验证集路径和类别名。类别顺序必须和标签里的索引严格对应否则模型会把绿色学成完全成熟这种错误在混淆矩阵上表现为两类互相大量误判。下面是一个最小可用的配置示例路径按你的实际目录改。# tomato.yaml path: datasets/tomato # 数据集根目录 train: images/train # 训练图相对 path val: images/val # 验证图相对 path names: 0: green # 绿色 1: half_ripe # 半熟 2: fully_ripe # 完全成熟这里names的键就是标签 txt 第一列的类别索引。很多人从 VOC 转过来时索引从 1 开始直接喂进去会整体错位一位训练 loss 看着在降实际全错。转换脚本里务必确认索引从 0 开始。3.2 训练命令与关键参数假设用 YOLOv8 的 nano 或 small 版本起步命令行训练最省事。小数据集不建议一上来就用大模型参数量大、更容易过拟合nano 或 small 先跑基线更稳。# 从预训练权重起步小数据集微调 yolo detect train \ datatomato.yaml \ modelyolov8s.pt \ epochs150 \ imgsz640 \ batch16 \ lr00.01 \ patience30 \ projectruns/tomato \ nameexp1参数逐个说epochs150给小数据集留足收敛轮次配合patience30做早停验证指标 30 轮不升就停避免无效训练。imgsz640是通用起点如果西红柿在图中占比很小可以提到 960 甚至 1280但显存和速度要权衡。batch16按显存调显存不够就降到 8 并适当调小学习率。lr00.01是初始学习率微调预训练模型时这个值通常够用若 loss 前期震荡明显降到 0.005 再试。modelyolov8s.pt是预训练权重用迁移学习能显著缓解小数据集的过拟合。3.3 三个最该盯的参数imgsz、batch、增强强度第一个是imgsz。西红柿检测里果实相对整图往往偏小分辨率直接决定小目标召回。640 下如果绿色小果经常漏检优先提分辨率而不是加数据。第二个是batch。它和lr0是联动的batch 翻倍时学习率可以适当上调反之亦然盲目改一个容易训崩。第三个是增强强度。YOLO 默认开启 mosaic、HSV 抖动等增强对农业图像HSV 的色调抖动要小心成熟度本身就是靠颜色区分的色调抖太狠会把绿色和半熟搅在一起模型学不到稳定颜色特征。常见做法是把hsv_h调小甚至关掉保留亮度和饱和度抖动来模拟光照变化。注意增强参数不是越多越好。成熟度分类任务里颜色是核心判别特征任何破坏颜色一致性的增强都要谨慎。4. 小数据集训练避坑从 loss 不降到类别混淆的排查清单4.1 现象训练 loss 正常下降但验证 mAP 一直很低原因通常有两个。一是训练集和验证集分布差异大比如验证集全是逆光或遮挡严重的图训练集却都是顺光清晰图。二是标签类别定义在训练集内部就不一致。解决方式是先做数据分布统计把验证集按光照、遮挡分组看指标再回头抽查标签。如果分组后某一组指标特别差说明模型没学到该场景需要补这类样本或针对性增强。4.2 现象绿色和半熟两类互相大量误判这是成熟度检测最典型的坑。原因多半是标注边界模糊绿色和半熟之间没有清晰判据标注同学凭感觉标。解决分两步先重新定义判据比如用转色面积占比给出可量化标准再对边界样本做复核必要时合并成两类或增加一个过渡类。如果业务上确实需要三分类就要接受边界样本的天然难度在评估时单独看这两类的混淆情况而不是只看总体 mAP。4.3 现象模型对完全成熟召回高对绿色几乎检不到典型的类别不平衡。绿色实例少分类损失里贡献小。解决办法有三一是在数据层面过采样绿色样本复制或做针对性增强二是在损失层面给少数类加权YOLO 部分版本支持类别权重配置三是降低置信度阈值看召回但会引入更多误检。实操中优先补数据权重和阈值是补救手段。4.4 现象训练到后期验证指标剧烈波动小验证集导致的评估方差大。1267 张如果按 8:2 分验证集只有两百多张指标对个别样本敏感。解决方式是做交叉验证或者把验证集扩大、训练集相应缩小用更多轮次换更稳的评估。另一个原因是学习率后期没降下来确认余弦退火或步进衰减是否生效。4.5 现象推理时框重叠严重同一颗果子出多个框NMS 阈值没调好或者模型对同一目标输出了多个高置信框。先确认 NMS 的 IoU 阈值默认 0.7 偏松时可以降到 0.5 左右。如果降了还重叠说明模型本身没学好回到训练环节查标签是否有重复框。标注时同一颗果子被标了两次模型就会学出重复检测。5. 把模型用起来验证指标怎么读、推理脚本怎么写、阈值怎么定5.1 别只看 mAP分类别看混淆矩阵训练完先看混淆矩阵它直接告诉你哪两类在互相误判。成熟度任务里绿色和半熟的混淆往往占大头。再看每一类的 precision 和 recall如果绿色 recall 很低说明漏检多采摘线上会漏摘如果 precision 低说明误检多会把没熟的当成熟的摘下来。业务上漏摘和误摘的代价不同阈值就要跟着调。下面这段推理脚本把置信度阈值暴露出来方便你按业务调。from ultralytics import YOLO # 加载训练好的权重 model YOLO(runs/tomato/exp1/weights/best.pt) # conf 是置信度阈值iou 是 NMS 的 IoU 阈值 results model.predict( sourcetest_images, conf0.35, # 低于此置信度的框丢弃 iou0.5, # 重叠框合并阈值 imgsz640, saveTrue, save_txtTrue # 同时输出 YOLO 格式结果便于后续统计 ) # 统计每类检出数量快速看分布是否合理 for r in results: names r.names for cls_id in r.boxes.cls.tolist(): print(names[int(cls_id)])conf0.35是偏保守的起点漏检多就降到 0.25误检多就提到 0.5。iou0.5比默认 0.7 更严格能压掉重叠框。save_txtTrue输出的结果可以直接拿去做采摘决策的输入比如统计一帧里完全成熟的数量。5.2 阈值不是拍脑袋用验证集画一条曲线把验证集跑一遍取不同 conf 下的 precision 和 recall画 PR 曲线选业务上可接受的平衡点。如果采摘设备对误摘零容忍就选高 precision 对应的阈值接受漏摘如果先求覆盖就选高 recall。这个决策要和业务方一起定不是算法单方面拍。5.3 一个提升小数据集效果的具体技巧分阶段解冻训练小数据集直接全量微调容易过拟合。我一般分两阶段第一阶段冻结骨干网络只训检测头学习率可以稍大让头先适配新类别第二阶段解冻全部用小学习率精调。这样既利用了预训练特征又避免早期大梯度破坏骨干。YOLO 命令行里可以通过 freeze 参数控制冻结层数先冻结前若干层跑几十轮再解冻跑剩余轮次。血泪经验是第二阶段学习率一定要比第一阶段小一个量级否则前面学的头会被冲掉指标不升反降。这套流程跑下来1267 张图在单卡上几个小时就能出基线。值不值得做取决于你的场景是否稳定、标注能否统一。如果采摘环境光照变化剧烈、遮挡严重先补数据比调模型更有效。希望帮到你。本文还有配套的精品资源点击获取