资讯详情

YOLOv5/YOLOv8水果好坏检测实战:从数据标注到模型部署

📅 2026/10/19 14:16:37 | 华诺云谱 👁 阅读
YOLOv5/YOLOv8水果好坏检测实战:从数据标注到模型部署
我前阵子接了个果蔬分拣相关的视觉项目核心需求就是把大小不一的水果照片按“好果”和“坏果”自动分好类。这类需求在农产品标准化、商超自动结算、生鲜供应链质检场景里特别常见。我们最终选型就用 YOLOv5 和 YOLOv8 做了整套方案配套整理了一份带标注好的水果好坏数据集模型也提前跑通了效果比较稳定所以把整个思路、配置和踩过的坑都记录下来。这份内容适合刚入门目标检测、想拿公共数据集做落地方案练习的开发者也适合想快速评估 YOLO 系列在简单二分类检测任务上表现的同学。不需要你有很强的算法背景只要你Linux命令行能跑通、看得懂Python就行。1. 项目拆解水果好坏检测到底在检测什么1.1 核心需求与标签体系先别急着下数据集。很多人一上手就说“我要检测水果好坏”但“好坏”这两个字在视觉上其实是高度抽象的。腐烂、碰伤、凹陷、变色、表面褶皱、霉斑这些都属于“坏”的理由但它们长得完全不一样。如果直接定义成一个大类“坏果”模型会把所有异常样本强行往回塞很容易造成漏检。在这个项目里我建议把标签拆成更细的类别常见做法有两种二分类方案good和bad适用于快速验证、分拣线粗筛多状态方案good、bruise、rotten、damaged适用于需要知道具体坏因的场景。我在做这个项目时初始也是图省事只标 good 和 bad但很快发现“坏果”在视觉上看不齐特征太散表现在验证集上就是 recall 一直往下掉。后来改成多状态标注模型对“腐烂”和“碰伤”的响应明显分开下游筛选逻辑也更灵活。标注文件用的是 YOLO 标准格式每张图对应一个同名 txt 文件每行是一条目标记录格式为class_id x_center y_center width height坐标值全部是归一化后的比例值不是像素坐标。比如一张 640x480 的图中某个苹果框的左上角在 (160, 120)右下角在 (480, 360)那个框的归一化坐标就是中心点 (0.5, 0.5)宽高 (0.5, 0.5)。这一块看似基础但很多人第一次跑模型时框全乱飘就是坐标没归一化。1.2 检测任务与分类任务的区别这里还要聊一个容易被搞混的点水果好坏检测是“目标检测”问题不是“图像分类”问题。分类模型只会告诉你“这张图里有没有坏果”但它给不了坏果在哪儿、有几个、是哪一颗坏了。而分拣场景里你可能面对的是托盘上几十个水果必须知道坏的那个具体位置机械臂才有办法把它夹走。YOLO 系列恰好是这种场景的标准解决方案。它以网格方式预测目标框和类别单次前向传播就能同时输出坐标和类别概率。在这个项目里我选择 YOLOv5 和 YOLOv8 各跑了一版既是为了对比也是为了面对不同部署环境时有储备方案。1.3 适用场景与影响范围水果好坏检测所影响的并不仅仅是“分拣”这个动作。往下游看它可以对接统计报表、库存管理、损耗归因往上游看它可以反推采购时的成熟度分布、存储环境是否需要调整。所以做这个数据集和模型时我特意保留了每张图的拍摄时序和批次信息这样后面做趋势分析时还能复用标注结果。2. 方案选型YOLOv5还是YOLOv82.1 YOLOv5的优势与适用场景YOLOv5 虽然名字里有“v5”但它不是某个官方机构按顺序迭代出来的正统版本而是开源社区在 YOLOv4 基础上做工程化改造后形成的高性能实现。这里不争论名分只讲实用性。这个项目里我最早用的就是 YOLOv5原因有三个生态成熟网上能找到大量训练经验、部署案例遇到报错基本搜一下就有答案模型结构相对保守稳定性好。训练循环、数据加载、增强策略全是封装好的对于快速出结果来说非常舒服部署友好。导出 ONNX、TensorRT 都很方便在边缘设备上跑几乎没有额外适配成本。对于水果好坏检测这种中小目标、背景相对简单的任务YOLOv5s 或 YOLOv5m 完全可以扛住。尤其是刚接触目标检测的人从 YOLOv5 开始学看代码、调参数都更容易理解。YOLOv5 官方仓库提供 n/s/m/l/x 五个尺寸n 是最轻量的版本x 是精度最高但最重的版本。对这个项目我用 v5s 做主力训练batch size 设成 32输入 640x640在单张消费级显卡上稳定跑通。2.2 YOLOv8的改进与适用场景YOLOv8 是 Ultralytics 推出的版本也是目前直接用 pip 就能体验最顺滑的 YOLO 工具之一。相比 v5v8 主要改动集中在这几个方向从 Anchor-Based 转向 Anchor-Free。v5 需要预设 anchor 尺寸v8 则是直接回归目标位置和尺寸少了一层调试锚框的负担。Backbone 和 Neck 换成 C2f 结构梯度流动更丰富同等参数下特征提取能力更强。Head 部分做了解耦分类和回归分支分开预测收敛速度和精度都有提升。内置了集成训练、验证、导出、推理的一体化命令行工具一个yolo命令就能完成全流程。对水果好坏检测来说v8 的 Anchor-Free 特性让模型在小物体上表现更好。比如你要检测一颗草莓上黄豆大小的霉斑v8 的定位能力明显好于 v5。另一个我很喜欢的点是 v8 支持更简洁的 YAML 数据集配置跑起来几乎不用写代码。2.3 我的选型建议拿我这个项目为例整体路径是“数据先跑通模型再择优”。一开始先用 YOLOv5s 快速建立 baseline确认数据标注质量没问题后再用 YOLOv8s 跑一版对比。最后生产环境里用的是 YOLOv8s输出精度比 v5s 高了一个多点推理速度差距却很小。如果你手头任务对延迟不敏感又可以接受稍微大一点的模型直接用 YOLOv8m 会更稳。但如果你要部署到 Jeston Nano、树莓派这类低算力设备建议用 YOLOv5n 或 YOLOv8n。n 版本参数量极小香蕉好坏这种特征差异明显的目标它也能跑得很好。提示不要一上来就追求最大最准的模型水果检测不是自动驾驶目标特征明显、背景相对单一中小模型完全够用。模型太大反而移动端部署时麻烦。3. 数据集实战从采集到标注3.1 数据采集与图像质量要求数据集是这类项目的隐形瓶颈。一份好的水果好坏数据集不是把手机里的随便几张水果照片攒一攒就行它必须满足以下基本条件不同成熟度的个体分布要均匀。不能只拍刚摘下来的硬果也要拍放了一周后的蔫果光照条件要多样。冷链仓库灯光偏冷超市灯光偏暖你至少要覆盖这两种典型环境角度要丰富。俯拍、侧拍、45度斜拍都要有因为分拣线上的镜头角度是固定的但水果摆放姿态是随机的坏果比例不能过低。真实货品里坏果也许只有5%但训练集里坏果至少要占到30%-40%否则模型会产生严重的类别偏向。我实际整理这个数据集时按苹果、香蕉、橙子三个大类分别收集每类大约1200张原始图共3600张标注后不再额外清洗直接进入训练。每张图内目标数量控制在1到8个之间基本模拟了单果分拣和小批量托盘分拣两种模式。拍摄上有一点比较关键尽量使用不反光、哑光的背景板。如果用高光泽的塑料托盘会在水果表面形成大面积反射光斑导致标注员看不清边界模型也容易把光斑误当成坏果纹理。3.2 标注工具与标签规范标注工具我用的是开源免费的 LabelImg 和 X-AnyLabeling。LabelImg 很老牌胜在简洁稳定批量改文件名、切换标注文件夹都很顺手。X-AnyLabeling 则在交互上更舒服支持自动保存、半自动辅助标注效率更高。标注规范要提前定死不能边标边变。我的规则是这样的边界框紧贴果实主体不包括果梗伸出去的部分果实有重叠时用能看见的轮廓框住完整果实宁可略微外扩不要切进果肉坏斑面积超过果实面积30%的框内标签设为对应坏果类别少于30%的只框果实类别还是 good让模型学会忽略轻微的生理斑点同一张图里有多颗坏果时每颗单独一个框不能合并。如果多人协作标注建议先让两个人各自标完同一批50张图算一下框的 IoU 和类别一致率。一致率达到90%以上再全面铺开不然模型学到的标签噪声会非常大。3.3 数据增强与训练集划分YOLO 系列自带较强的数据增强比如 Mosaic四张图拼一张、随机色域变换、随机翻转、缩放。但在水果质检这个具体场景里我额外加了两个针对性增强亮度扰动范围调大。因为水果表面反光差异大增强亮度能帮助模型适应不同光强增加 HSV 饱和度扰动。让绿色、黄色、红色在不同成熟度间有更好的连续性。划分比例上我用了 8:1:1即训练2880张、验证360张、测试360张。测试集完全独立只在最后评估时用一次避免过拟合评估结果。4. 训练实操环境配置与关键参数4.1 训练环境准备水果好坏检测这个量级的数据集并不吃硬件我用的是一台消费级显卡服务器但为了可复现我建议你先固定环境版本。这里给出我实际使用的组合照着装不会踩版本坑Python 3.9 或 3.10PyTorch 2.xCUDA 11.8 或 12.1ultralytics 8.0.x对应 YOLOv8opencv-python、numpy、pandas、matplotlib 这些基础库。YOLOv5 的依赖可以用它的 requirements.txt 安装注意不要和 v8 的版本冲突。如果你同机使用两个版本建议分别建 Conda 环境conda create -n yolov5 python3.9 -y conda activate yolov5 pip install -r yolov5/requirements.txtYOLOv8 更简单直接 pip 安装pip install ultralytics4.2 数据集配置文件写法YOLOv8 的数据配置用一个 YAML 文件搞定。我的文件放在/data/fruit-dataset/data.yaml内容如下train: /data/fruit-dataset/images/train val: /data/fruit-dataset/images/val test: /data/fruit-dataset/images/test nc: 4 names: [good, bruise, rotten, damaged]YOLOv5 的配置格式类似但目录写法略有不同需要指向包含 images 和 labels 的上级目录train: /data/fruit-dataset/train val: /data/fruit-dataset/val nc: 4 names: [good, bruise, rotten, damaged]注意在 v5 中训练脚本会自动拼接images和labels子目录在 v8 中则直接指定 images 目录labels 默认同路径下同名 labels 目录。改动目录结构后务必验证标签路径是否正确否则 loss 会一直不降。4.3 参数选择与训练命令先说几个关键参数的含义不管 v5 还是 v8这几个都是首选调整对象imgsz训练尺寸我用了 640。水果目标不算特别小640 足够如果你要检测细小的病斑就上 960 或 1280batch受显存限制我用的 32。如果你显存只有 8G降到 16 甚至 8同时调低 imgszepochs先跑 100 轮看 loss 曲线趋势再决定要不要加。不要一开始就设 500 轮纯浪费时间workers数据加载线程数Linux 下设为 8 没问题Windows 下建议别超过 4否则容易报数据加载错误patience早停耐心值设为 30 表示验证集指标30轮不升就自动停止。YOLOv5 训练命令python train.py \ --data /data/fruit-dataset/data.yaml \ --cfg models/yolov5s.yaml \ --weights yolov5s.pt \ --img 640 \ --batch 32 \ --epochs 200 \ --name fruit_v5sYOLOv8 训练命令更简洁yolo train \ modelyolov8s.pt \ data/data/fruit-dataset/data.yaml \ imgsz640 \ batch32 \ epochs200 \ namefruit_v8s我实际跑了 200 轮v5s 大约用了 1.5 小时v8s 大约 1.8 小时时间差异主要来自 v8 的 C2f 模块计算量更大。前 50 轮 loss 下降很快到 120 轮左右趋于平缓最终 mAP50 稳定在 96% 左右。4.4 结果评估mAP、Precision、Recall模型训练完不能只看 loss。目标检测的标准评估指标是 mAP其中 mAP50 表示 IoU 阈值 0.5 时的平均精度mAP50-95 是更严格的综合指标范围在 0.5 到 0.95 之间按步长计算平均。我贴一下自己训完后的参考数据模型PrecisionRecallmAP50mAP50-95YOLOv5s96.2%94.7%97.1%88.4%YOLOv8s97.8%96.3%98.5%90.2%从 Precision 与 Recall 的角度看v8s 不仅精确率高召回也更好。这意味着漏检更少错检也少。对分拣线来说漏检一个坏果是直接损失优先级比误判好果更高所以 v8s 更贴合生产需求。提示如果只追求“看起来准确率高”那只看 Precision 就行但真实分拣产线更关注 Recall即坏果是不是都被挑出来了。建议结合 NMS 阈值下调和置信度阈值调整来找到一个产线可接受的工作点。4.5 推理与导出部署训练完成后v8 导出一个推理结果图很简单yolo predict modelruns/detect/fruit_v8s/weights/best.pt \ source/data/fruit-dataset/images/test会自动生成带标注框的结果图方便肉眼检查。如果要把模型部署服务化建议直接导出 ONNXyolo export modelruns/detect/fruit_v8s/weights/best.pt formatonnx导出后的 ONNX 文件可以被 OpenCV DNN 模块或 ONNX Runtime 加载我实际在 CPU 上推理一张 640 分辨率的图耗时大约 40ms 到 80ms完全满足实时分拣线的要求。5. 常见问题与排查实录5.1 训练 Loss 不下降或收敛很慢这个问题绝大多数时候出在标签路径或数据集划分上。我之前有一次 v5 训练到 30 轮 loss 还是纹丝不动排查半天发现 YAML 里 train 路径写成了/data/fruit-dataset/train/images但 v5 会自己拼接一次 images导致实际加载的是train/images/images路径不存在标签全空。另一个常见原因是背景干扰过大。如果你拍摄了太多复杂背景模型初期需要大量轮次去区分“水果”和“背景”自然收敛慢。建议前 50 轮先加大背景扣除再进入全场景训练。如果 loss 曲线一直锯齿状波动通常就是 batch size 太小或者学习率初始值偏高。YOLO 系列默认学习率是 0.01v8 略有调整如果你的 batch 从 32 掉到 8建议按比例调低学习率到 0.0025否则梯度噪声太大。5.2 漏检多、坏果认不出来漏检一般是标注不够细或者特征没学出来。比如“碰伤”早期只是轻微变红如果标注框画得过大把大量好果区域也包进去模型就会混淆。我的处理方式有两个对于轻微坏斑单独把它裁出来作为小图加入训练集强化局部特征提高难样本权重。YOLO 训练时可以把损失函数中的正样本权重加大调高cls_pw参数或者直接在抽样时多抽含坏果的批次。另外别忽略 Mosaic 增强在坏果检测中的副作用。Mosaic 拼接四张图部分坏果特征可能因为裁剪位置被切掉一半所以如果发现坏果小目标总漏检可以降低训练时的 Mosaic 概率甚至关掉它。5.3 误检频繁、好果被当成坏果这个问题多半是坏果标注框画得太松把好果边缘的正常色差也卷进去了。标注坏果时框应该贴着实际病变区域不要为了框全果实而故意扩大。模型学习的其实是框内的纹理特征如果框内一半是好果区域当然会产生误检。另一个排查点是置信度阈值。默认的 0.25 可能在复杂背景上产生较多误报。我实际生产中把置信度阈值调到 0.45同时将 NMS IoU 阈值调到 0.6误检率降了一半左右但坏果召回基本不受影响。5.4 标注类别不平衡怎么办如果你的数据集中 good 占了 80%而 rotten 只有 5%模型会对 good 严重过拟合。最简单的做法是把各类别样本数量调整到接近比例。不要强行复制坏果图片因为简单复制会产生重复样本模型会“背题”。推荐做法用数据增强生成更多坏果样本比如旋转、裁切、翻转坏果局部用半监督方式扩充坏果数据找一个性能尚可的模型先预测坏果人工复核后加入训练集在下游应用时配合阈值调整优先保证 bad 类别的召回。我实际将 rotten 类样本增强到占全量 20% 后mAP50 提升了大概 3 个点说明坏果数据稀缺确实是这类任务的重要瓶颈。6. 后续扩展与实用建议6.1 从检测到分级融合分类模型检测模型已经能给每个水果画框并判断它是好是坏但如果你还想进一步做“糖度分级”或“成熟度评估”靠 YOLO 就不够精细了。一个实际效果很好的做法是先用 YOLO 把水果框出来然后把框内区域裁剪出来交给一个小型分类网络比如 ResNet18 或 EfficientNet做更细等级判断。这种方式相当于检测模型负责“在哪里、大致是什么”分类模型负责“具体状态是几级”。拆分之后每个模型的任务更纯精度提升空间更大。这也让整个系统具备良好的模块扩展性。6.2 模型更新与增量学习落地后最怕的是数据分布发生变化。比如换了一个新品种苹果或者仓库换了照明条件模型精度会明显下降。这时候不需要重新全量训练可以在原模型基础上做增量微调yolo train modelruns/detect/fruit_v8s/weights/best.pt \ data/data/fruit-dataset-new/data.yaml \ epochs50 \ lr00.001注意学习率要调低一些防止破坏已经学好的旧特征。增量训练结束后务必用原来测试集回归一遍确认没有灾难性遗忘也就是旧类别的指标不要掉太多。6.3 我的一点经验做水果好坏检测这个项目我最大的体会就是数据的重要性被很多人低估。模型从 v5 换到 v8 提升了不到两个点但是我把标注规范重做了一遍、把坏果类别拆细之后整体效果直接跳了将近十个点。如果你现在也在做类似项目先把数据质量抓上去再谈模型选型和调参顺序千万不能反。还有一个小技巧训练时定期把验证集的坏果置信度分布存下来观察那些漏检样本的置信度集中在哪个区间。如果大量漏检样本的置信度在 0.3 到 0.4 之间说明模型已经学到一部分特征了只是置信度阈值没匹配好。这时候调阈值比调模型更有效。这个水果好坏检测项目本身不算复杂但它把数据采集、标注规范、模型选型、训练调优、部署推理整条链路完整过了一遍。做一遍之后再有类似的二分类视觉检测任务你基本就轻车熟路了。
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑