简介:这是一套面向计算机视觉初学者与深度学习实践者的YOLOv5果蔬识别完整项目资源,围绕土豆、圣女果、大白菜、大葱、梨、胡萝卜、芒果、苹果、西红柿、韭菜、香蕉、黄瓜等十余类常见果蔬的检测任务展开,可用于课程设计、毕业设计或算法入门练手。压缩包共56个文件,约94.07MB,包含14个Python脚本、12个PNG与9个JPG及6个JPEG图像、6个TXT说明、4个XML标注、2个H5权重文件,以及Markdown文档和工程配置,覆盖数据读取、图像预处理、模型训练与实时检测等环节。资源中提供了CNN与MobileNet两套训练流程、热力图与训练过程记录、测试记录及模型权重,便于读者对照复现训练曲线、分析检测效果并理解迁移学习思路。目前已有3769人学习下载,适合希望快速跑通YOLOv5果蔬识别全流程、积累数据集处理与模型调优经验的读者参考。
1. 果蔬识别为什么总在产线翻车:从 yolov5 数据集到落地的第一道坎
做过分拣线视觉项目的人都有个共识:模型在实验室里 mAP 跑到 0.9 不难,难的是到了现场,光照一变、果子一叠、品种一换,识别率直接掉到六成。果蔬识别这个场景看着简单——不就是把苹果、橘子、西红柿分出来吗——但真正卡住工程进度的,往往不是网络结构,而是数据集的质量和标注的一致性。yolov5 果蔬识别数据集系统这套东西,核心价值就在于把「数据采集、标注规范、训练配置、推理部署」串成一条能复现的链路,而不是丢给你一个裸模型让你自己猜怎么用。
这套方案适合谁?一是做农业分拣、冷链质检、智能称重这类项目的工程师,需要快速搭一个能识别常见果蔬的基线;二是学生或转行者,想拿一个完整的数据集加代码加教程跑通目标检测全流程,作为 yolov5 训练自己数据集的练手项目。它解决的不是「从零发明算法」的问题,而是「让你在两天内拥有一套可用的果蔬检测基线,并且知道每一步为什么这么设」的问题。下面按数据、训练、调参、部署、避坑的顺序,把这条链路拆开讲。
2. 果蔬数据集怎么建:从采集到 YOLO 格式的完整链路
2.1 果蔬数据集的类别设计与采集边界
很多人拿到「果蔬识别数据集」第一反应是类别越多越好,恨不得把菜市场所有品类都塞进去。这是典型的翻车起点。类别一多,类间差异变小(比如青椒和尖椒、红富士和黄元帅),标注难度和模型混淆率同时飙升。我一般建议第一版控制在 8 到 15 类,选类间外观差异明显的品种,比如苹果、香蕉、橙子、西红柿、黄瓜、胡萝卜、土豆、洋葱这种。
采集时要盯住三个边界条件。第一是光照:产线常见的是顶部条形光源和侧向补光,数据集里必须包含这两种光照下的样本,否则模型会把「阴影方向」当成类别特征。第二是遮挡与堆叠:果蔬在传送带上经常互相压着,标注时对遮挡超过 50% 的目标要么标要么弃,但规则必须全数据集统一。第三是背景:纯色背景训练出来的模型换到木框、塑料筐、金属台面上会崩,采集时背景要覆盖实际工位的主要材质。
一个常被忽略的点是负样本。传送带空转、包装箱、人手入镜这些画面要单独收集一批,作为背景图参与训练,能显著降低误检。常见做法是负样本占总量 5% 到 10%。
2.2 标注规范:YOLO 格式的四个硬性约定
yolov5 用的是 YOLO 格式标注,每张图对应一个同名 txt 文件,每行一个目标,格式是:
<class_id> <x_center> <y_center> <width> <height>四个坐标都是归一化到 0 到 1 的相对值,不是像素。这一点新手最容易搞错,直接把像素坐标写进去,训练时 loss 不降,排查半天才发现是格式问题。
标注工具有 LabelImg、CVAT、Roboflow 等,选哪个不影响结果,关键是导出时选 YOLO 格式。标注规范我一般定四条:框要贴紧目标外轮廓,不留大边距;被遮挡目标按可见部分标注,不脑补完整轮廓;同类目标框的大小风格要一致,避免有人标大有人标小;每标完 200 张做一次交叉复核,抽检 10%。
下面是一个把 LabelImg 的 VOC 格式转成 YOLO 格式的脚本,实际项目里经常遇到标注同学导出错格式的情况:
import os import xml.etree.ElementTree as ET # 类别映射,必须和 data.yaml 里的 names 顺序一致 classes = ["apple", "banana", "orange", "tomato", "cucumber"] def convert(size, box): # size: (w, h) 图像宽高; box: (xmin, xmax, ymin, ymax) dw, dh = 1.0 / size[0], 1.0 / size[1] x = (box[0] + box[1]) / 2.0 y = (box[2] + box[3]) / 2.0 w = box[1] - box[0] h = box[3] - box[2] return x * dw, y * dh, w * dw, h * dh def convert_annotation(xml_path, out_dir, img_w, img_h): tree = ET.parse(xml_path) root = tree.getroot() base = os.path.splitext(os.path.basename(xml_path))[0] with open(os.path.join(out_dir, base + ".txt"), "w") as f: for obj in root.iter("object"): cls = obj.find("name").text if cls not in classes: continue # 跳过未定义类别,避免训练时报 index 越界 cls_id = classes.index(cls) bnd = obj.find("bndbox") box = (float(bnd.find("xmin").text), float(bnd.find("xmax").text), float(bnd.find("ymin").text), float(bnd.find("ymax").text)) bb = convert((img_w, img_h), box) f.write(f"{cls_id} {' '.join([f'{v:.6f}' for v in bb])}\n")逻辑说明:convert函数把像素坐标转成归一化中心点加宽高,这是 YOLO 格式的核心。classes列表的顺序决定了 class_id,必须和训练时 data.yaml 的 names 完全对应,顺序错一位整个模型就废了。参数上img_w、img_h要从对应图片读取,不能写死,因为数据集里图片尺寸往往不统一。
2.3 数据集划分与 data.yaml 配置
划分比例常规是训练集 70%、验证集 20%、测试集 10%。但果蔬场景有个坑:如果同一批采集的图片(同一时间段、同一光照)被随机打散到训练和验证集,验证指标会虚高,因为验证集和训练集太像了。正确做法是按采集批次划分,让验证集来自不同批次,这样指标才反映真实泛化能力。
目录结构建议:
dataset/ images/ train/ val/ test/ labels/ train/ val/ test/ data.yamldata.yaml 内容:
path: ./dataset train: images/train val: images/val test: images/test nc: 5 names: ["apple", "banana", "orange", "tomato", "cucumber"]nc是类别数,必须和 names 长度一致。path用相对路径时,训练命令要在 dataset 同级目录执行,否则 yolov5 找不到图片。这个路径问题在 yolov5 环境配置阶段坑了无数人,报错通常是「No labels found」或者「Dataset not found」。
3. yolov5 训练果蔬数据集的参数怎么设
3.1 环境配置与最小可跑命令
环境这块,conda 建虚拟环境是最稳的。Python 版本选 3.8 到 3.10,torch 版本跟 CUDA 对齐。常见做法是:
conda create -n yolo python=3.9 conda activate yolo pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118 pip install -r requirements.txtcu118对应 CUDA 11.8,具体选哪个看你的显卡驱动支持的 CUDA 版本,用nvidia-smi查。装完验证:
python -c "import torch; print(torch.cuda.is_available())"输出 True 才算环境通了。这一步不过,后面训练全是 CPU 在跑,一个 epoch 能等到你怀疑人生。
最小训练命令:
python train.py --data dataset/data.yaml --weights yolov5s.pt --epochs 100 --batch-size 16 --img 640--weights yolov5s.pt是加载预训练权重做迁移学习,果蔬数据集通常几千张图,从零训练收敛慢且容易过拟合,用预训练权重是标配。--img 640是输入分辨率,果蔬目标如果比较小(比如樱桃、小番茄),可以提到 960 或 1280,但显存占用会翻倍。
3.2 关键超参数:学习率、batch size 与 anchor
yolov5 的超参数默认值对通用数据集调得不错,但果蔬场景有两三个参数值得动。
学习率--lr0默认 0.01,配合 SGD 优化器。如果数据集小于 2000 张,建议降到 0.001 到 0.005,避免早期震荡。batch size 受显存限制,16 是 8G 显存的稳妥值,显存够就往上加,batch 大梯度稳,但太大泛化会略降。
anchor 这块,yolov5 训练时会自动用 k-means 重新聚类 anchor,所以一般不用手动设。但如果你的果蔬目标尺寸特别集中(比如全是直径 5 到 8 厘米的果子),可以跑一次python utils/autoanchor.py看聚类结果,确认默认 anchor 是否匹配。
数据增强参数里,--mosaic默认开启,把四张图拼一张,对小目标检测帮助大。但果蔬堆叠场景下,mosaic 可能把遮挡关系搞得更乱,如果发现验证集掉点,可以试--mosaic 0关掉对比。--degrees旋转增强默认 0,果蔬在传送带上方向随机的话,可以开到 10 到 15 度。
3.3 训练过程监控与早停判断
训练启动后看runs/train/exp/下的结果。重点盯三个指标:metrics/mAP_0.5、train/box_loss、val/box_loss。
正常情况 mAP 在前 20 个 epoch 快速上升,之后放缓。如果 train loss 降但 val loss 升,是过拟合,加数据或加--dropout。如果两个 loss 都不降,检查学习率是不是太大,或者标注格式是不是错了。
yolov5 自带早停,--patience默认 100,意思是 100 个 epoch 没提升就停。实际项目里我一般设 30 到 50,省时间。训练完最好的权重是best.pt,不是last.pt,部署时别拿错。
验证命令:
python val.py --data dataset/data.yaml --weights runs/train/exp/weights/best.pt --img 640会输出每类的 P、R、mAP。如果某一类特别低,回去看那一类的标注量和标注质量,八成是样本太少或框得不准。
4. 推理部署:从 PyTorch 权重到产线可用
4.1 本地推理与批量测试
训练完先本地跑推理确认效果:
python detect.py --weights runs/train/exp/weights/best.pt --source test_images/ --img 640 --conf 0.4--conf 0.4是置信度阈值,低于这个值的框不输出。果蔬分拣场景对误检容忍度低(把好果子判成坏的损失大),阈值可以提到 0.5 到 0.6;对漏检容忍度低(坏果子漏过去)则降到 0.3。这个值要拿实际产线图片调,没有万能值。
--source可以是单张图、文件夹、视频,甚至摄像头编号。批量测试时输出在runs/detect/exp/。
4.2 导出 ONNX 与推理加速
产线部署很少直接用 PyTorch,一般导出 ONNX 或 TensorRT。ONNX 通用性好:
python export.py --weights best.pt --include onnx --img 640 --batch 1--batch 1是推理时的 batch,产线通常单张流式处理。导出后可以用 onnxruntime 验证:
import onnxruntime as ort import numpy as np sess = ort.InferenceSession("best.onnx") # yolov5 输入是 1x3x640x640,归一化到 0-1 img = np.random.rand(1, 3, 640, 640).astype(np.float32) outputs = sess.run(None, {sess.get_inputs()[0].name: img}) print(outputs[0].shape) # 应为 (1, 25200, 5+nc)输出维度里 25200 是三个检测层的 anchor 总数,5+nc是 xywh 加置信度加类别概率。后处理要做 NMS,这部分 yolov5 的utils/general.py里有现成实现,移植时直接抄。
如果产线用树莓派 5 这类边缘设备,ONNX 加 onnxruntime 是可行路线,但帧率有限,640 输入大概几帧到十几帧,具体看模型大小。要提速就换 yolov5n 或 yolov5s,或者降输入到 416。
4.3 部署时的预处理一致性
这是血泪经验:训练时的预处理和推理时的预处理必须完全一致。yolov5 训练时做了 letterbox 缩放(保持长宽比,短边补灰),推理时如果直接 resize 拉伸,长宽比变了,框会偏。很多人本地测好好的,部署后框全歪,就是栽在这。
letterbox 的核心是算缩放比例取 min,然后补边到目标尺寸。移植到 C++ 或其他语言时,这段逻辑要一比一复刻,包括补边的灰度值 114。差一点,精度就掉。
5. 果蔬识别避坑清单:五个真实踩过的坑
5.1 坑一:验证集 mAP 很高,上线就崩
现象:本地 val mAP 0.92,产线实测识别率不到 70%。
原因:验证集和训练集来自同一批采集,光照、背景、角度高度相似,模型记住了这批数据的「风格」而不是果蔬特征。
解决:按采集批次划分数据集,验证集必须包含训练时没见过的光照和背景。有条件的话,留一批现场实拍图做最终测试,不参与任何训练调参。
5.2 坑二:某一类识别率奇低
现象:苹果、香蕉都 0.9 以上,西红柿只有 0.4。
原因:要么西红柿样本量远少于其他类,要么标注时把西红柿和红苹果混标了,要么这一类的外观差异本身就小(比如不同成熟度的西红柿)。
解决:先统计各类样本数,少于 300 张的类补采。再看混淆矩阵,如果西红柿大量被预测成苹果,说明类间差异不够,考虑合并类别或加更多区分性样本。成熟度差异大的话,可以拆成「青西红柿」「红西红柿」两类。
5.3 坑三:训练 loss 不降,一直震荡
现象:box_loss 在 0.1 上下跳,mAP 卡在 0.1 不动。
原因:九成是标注格式问题。常见的是坐标没归一化、class_id 从 1 开始(YOLO 要求从 0)、txt 文件和图片没对应上。
解决:写个校验脚本,遍历所有 label 文件,检查每行是否 5 个值、坐标是否在 0 到 1 之间、class_id 是否小于 nc。跑一遍就能揪出问题文件。
5.4 坑四:显存溢出(CUDA out of memory)
现象:训练几个 batch 后报 OOM。
原因:batch size 太大、img 分辨率太高、或者 dataloader 的 workers 太多导致内存泄漏。
解决:先降 batch size 到 8 试,再降 img 到 416。--workers设 4 到 8 就行,设太大反而抢内存。如果还不行,用--accumulate梯度累积,小 batch 模拟大 batch 效果。
5.5 坑五:导出 ONNX 后精度掉点
现象:PyTorch 权重 mAP 0.9,ONNX 推理结果明显变差。
原因:导出时的 opset 版本不兼容,或者后处理 NMS 的参数和训练时不一致。
解决:导出加--opset 12,别用太新的 opset。NMS 的 iou 阈值和 conf 阈值要和 PyTorch 推理时对齐。导出后用同一批图分别跑 PyTorch 和 ONNX,逐张对比框,定位差异。
6. 把果蔬识别做到产线级:一个提精度的小技巧
前面讲的都是标准流程,最后说一个我在实际项目里反复验证有效的技巧:用测试集反推标注质量,而不是只看 mAP。
具体做法是,训练完后拿测试集跑推理,把预测框和标注框叠在原图上可视化,人工过一遍。你会看到两类问题:一是标注框本身歪了或松了,模型学了个错的;二是模型预测的框比标注框更贴合目标,说明标注拖了模型后腿。这个动作做一轮,回去修 50 到 100 张标注,再训练,mAP 通常能涨 2 到 5 个点,比调超参数见效快。
可视化脚本核心逻辑:
import cv2 def draw_boxes(img_path, label_path, pred_path=None): img = cv2.imread(img_path) h, w = img.shape[:2] with open(label_path) as f: for line in f: cid, xc, yc, bw, bh = map(float, line.split()) # 归一化坐标还原成像素 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.imwrite("vis_" + img_path.split("/")[-1], img)参数说明:xc, yc, bw, bh是 YOLO 格式的归一化值,还原时分别乘宽高。绿框画标注,如果要对比预测,用红框画预测结果,叠一起看偏差。这个脚本不依赖 yolov5 框架,纯 OpenCV,方便集成到标注复核流程里。
我自己的习惯是,每完成一轮训练,必做一次可视化抽检,抽 30 张,不看完不调参。这个习惯帮我省了无数次「调了半天参数发现是标注错了」的后悔药。果蔬识别这行,数据质量的天花板就是模型精度的天花板,算法能补的很有限。希望帮到你。
本文还有配套的精品资源,点击获取