尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

基于YOLOv5的果蔬识别系统:数据准备、训练调优与部署实战

基于YOLOv5的果蔬识别系统:数据准备、训练调优与部署实战 简介本资源是一套基于YOLOv5的果蔬图像识别系统实现方案面向计算机科学与技术、人工智能等相关专业的本科生毕业设计与课程实践需求解决农业场景下常见果蔬目标检测与分类的实际问题。资源包共717个文件涵盖546张标注JPG图像、119个备份文件.zbak、15个核心Python训练与推理脚本、12张可视化PNG图、7张JPEG测试样本及XML标注文件、H5模型权重等完整呈现从数据采集、标注、预处理到模型训练、评估与部署的全流程压缩包大小为268.88MB。已有55人学习下载适用于具备机器学习基础的学习者开展项目复现与算法优化。读者可直接获取经导师审核获评“优秀”的完整工程代码、高质量果蔬数据集、多版本模型权重如cnn_fv.h5、mobilenet_fv.h5等及配套实现指南显著降低深度学习项目落地门槛尤其适合在有限算力环境下开展轻量化模型对比与性能调优实验。 去年我接了一个超市自助结算系统的演示项目需求一句话就能讲完——用摄像头识别顾客托盘里的蔬菜水果自动完成计价。干这行的人都懂听起来越简单的需求落地时往往越有暗坑。我最初也想直接从网上拉一个预训练模型跑通算了结果从数据集清洗、标注格式统一到模型反复训练调优再到最后部署成可用的识别服务前前后后折腾了将近三周。这篇就把这套基于YOLOv5的果蔬识别系统从数据准备、训练调优到部署集成的完整链路整理出来包括我踩过的各种坑和最终的取舍方案。如果你是正在做目标检测相关项目、想拿YOLOv5训练自己的数据集、或者单纯对果蔬识别这个小方向感兴趣的开发者这篇文章应该能帮你省下不少弯路。我会把数据怎么处理、超参数怎么调、训练完怎么部署这些环节全部展开讲清楚。1. 果蔬识别为什么不能当成一个普通分类任务做1.1 果蔬数据集的天然难点先说结论果蔬识别不是简单的图像分类它本质上是一个细粒度目标检测问题而且难度比很多人想象中大得多。拿我这次做的项目来说识别对象包括常见的苹果、香蕉、橙子、土豆、番茄、黄瓜等二十多类果蔬。表面上看这些类别差异很大但实际拍摄场景中根本不是这么回事。首先是类间相似性极高红苹果和红番茄在特定光照下几乎难以区分青苹果和青梨轮廓、颜色都很接近黄洋葱和土豆在视觉特征上也非常容易混淆。其次是类内差异极大同样是苹果有红富士的深红、有黄元帅的明黄、有青苹果的翠绿同一个类别内部的视觉差异甚至比不同类别之间的差异还大。更要命的是现实场景里的遮挡和堆叠问题。超市结算时顾客通常会把多种果蔬混放在一起果实之间互相遮挡有的只露出一小块表面。再加上不同成熟度的果蔬颜色饱和度差异、表面反光、拍摄角度倾斜这些因素叠加在一起让模型必须学习非常细粒度的纹理和形状特征而不是简单的颜色特征。1.2 分类、检测和实例分割的取舍明确了难点再看技术选型。如果只用图像分类模型比如ResNet、EfficientNet模型只能告诉你这张图里有一个苹果但给不了位置信息。在结算场景里你需要知道托盘上有几个苹果、几个番茄、它们分别在哪里分类模型根本无法满足需求。实例分割比如Mask R-CNN能给出像素级的分割掩码精度高但推理速度太慢在实时结算场景下很难跑得动。所以中间地带的目标检测是最合适的既能给出每个目标的类别又能给出边界框位置允许一定程度的遮挡速度也能满足实时需求。目标检测模型里我最终选了YOLOv5原因很实际它在精度和速度之间平衡得最好社区生态成熟部署工具链完善。YOLOv8虽然更新,但在部分设备上的部署兼容性还不如v5方便YOLOX速度不错但训练资料相对少一些。对一个实际要交付的项目来说YOLOv5足够的资料量和成熟的排错经验本身就是最大的优势。1.3 系统整体架构设计整个系统我分成了三个独立模块方便分别调试和替换数据处理模块负责原始图片的采集、清洗、标注格式转换、数据集划分。这个模块输出的产物是标准YOLO格式的训练集和验证集。模型训练模块基于YOLOv5官方仓库配置数据和超参数完成模型训练输出最佳的权重文件。推理服务模块加载训练好的权重对外提供图片识别和视频流识别能力通过HTTP接口或本地界面接入实际业务。这三个模块之间通过文件或接口解耦任何一个模块出问题都可以单独调试不用全链路排查。这个设计后面帮了我大忙尤其是当训练效果不好时可以很快定位到是数据问题还是模型参数问题。2. 数据集应用别让标注细节毁了你的模型2.1 公共数据集选型分类和检测数据要分清做果蔬识别很多人的第一反应是去网上找现成的数据集。这个思路没错但这里有一个容易踩的大坑分类数据集和检测数据集是完全不同的东西不能混用。最典型的就是Fruit-360这是一个非常有名的果蔬分类数据集包含了上百类果蔬的清晰单果照片图片干净、背景统一。但它是用来做图像分类的每张图片只有类别标签没有边界框标注信息。如果你拿这种数据去训练YOLOv5根本没法直接使用——YOLOv5需要的标签格式是类别ID 归一化的中心点x、y坐标 归一化的宽高这种格式必须自己标注边界框。而且分类数据集的图片大多是单一物体、背景干净和真实场景差距很大模型即使训练出来了遇到真实结算场景的复杂背景和遮挡也会效果崩盘。真正合适的选择是检测格式的果蔬数据集。AI Challenger 2018比赛中有一个果蔬检测数据集包含104个类别、超过12万张图片标注格式是JSON的检测框格式这个数据集的类别覆盖了大多数常见果蔬图片也从真实场景中拍摄非常适合做预训练或者微调。另外Roboflow平台上也有不少用户共享的果蔬检测数据集可以直接导出YOLO格式省去格式转换的麻烦。这里顺带说一句COCO数据集的fruit类别虽然也能用但COCO本身是大规模通用检测数据集果蔬类别划分比较粗糙只有apple、orange、banana、broccoli、carrot等少数几个大类没有细分品种直接用它训练果蔬识别系统会导致类别粒度不够。我自己实际采用的是AI Challenger预训练 自采数据微调的方案这比完全从零标注数据或者完全依赖公共数据靠谱得多。2.2 自制数据集的拍摄策略和标注规范公共数据集即使再接近真实场景也替代不了你自己业务场景里的数据。原因很简单你部署的环境、摄像头位置、光照条件、果蔬摆放方式都是独特的这些环境差异只能靠自有数据来弥补。我这次自己拍了大概3000张图片涵盖20个类别。拍摄策略上总结了三个要点多角度多距离同一个果蔬分别从俯视、平视、将近距离、远距离拍摄模拟顾客放上托盘后摄像头可能捕捉到的各种角度。还原真实遮挡场景把多种果蔬叠放在一起拍刻意制造遮挡、堆叠、边缘出框的情况。模型见过这些情况推理时才不会漏检。变换光照条件在自然光、白炽灯、荧光灯、背光等不同条件下分别拍一组提高模型对不同光照的鲁棒性。标注工具我用的是LabelImg老牌工具操作简单标注完直接保存成Pascal VOC格式或YOLO格式。虽然CVAT功能更强、支持多人协作但个人项目不必上那么重的工具。用LabelImg标注时有一个经验边界框尽量贴合目标边缘不要留太多白边也不要把目标切掉一部分。留白边会让模型学到多余的背景信息切掉目标会让模型学到不完整的特征这两种情况都会直接影响检测精度。还有一个容易被忽略的细节类别标签的ID要统一。YOLO格式的标签文件里类别是用整数ID表示的ID对应的类别名在data/*.yaml文件里定义。如果标注时class ID和yaml里的顺序不对应模型训练就全乱了而且这种错误还特别难察觉。2.3 数据清洗与增强过滤坏数据比增加数据更重要数据量不是越多越好这一点我这次深有体会。最初我从网上爬了一批图片加进训练集结果mAP刚开始很高但模型一到真实场景就各种漏检误检。排查发现网上爬来的图片里混入了大量绘画作品、表情包、带水印的商品图这些图片模型是学会了但学到的特征和真实果蔬完全对不上。所以数据清洗这一步千万不能偷懒。我当时的清洗规则是这样的问题类型处理办法图片模糊、分辨率过低删除分辨率低于200x200的直接剔除目标占比过小删除目标面积小于图幅5%的难以有效学习重复或近似重复图片用MD5去重近似重复的用感知哈希比较错误标签、漏标框人工逐张复查重点看易混淆类别绘画、表情包等非真实照片全部删除只用真实拍摄图片清洗完数据就到了数据增强环节。YOLOv5内置了非常强大的数据增强机制包括Mosaic增强、随机HSV色域变换、水平翻转、随机平移和缩放等。这些增强默认开启能有效提升模型的泛化能力。特别是Mosaic增强把四张训练图片随机缩放拼接成一张相当于变相增大了batch size还能让模型学到小目标、部分遮挡目标的特征对果蔬堆叠场景特别有用。如果自己的数据集类别不平衡比如苹果样本特别多、蓝莓样本特别少可以在训练时设置每个类别的采样权重让模型在样本少的类别上多学习几轮。不过这个参数不要一开始就调先让模型跑一版看结果再说。3. 环境准备、YOLOv5安装与训练前配置3.1 训练环境搭建YOLOv5官方仓库对环境的依赖不算苛刻但有一些细节值得注意。我当时的训练环境是一张RTX 3060 12G显卡配了CUDA 11.8和PyTorch 2.0.1这个组合很稳定。安装步骤很简单两条命令就能搞定git clone https://github.com/ultralytics/yolov5 cd yolov5 pip install -r requirements.txt这里不需要刻意追求最新版本官方仓库的v6.0、v7.0版本都很稳定经过大量用户验证。如果你用PyTorch 2.0以上的版本记得在安装完requirements.txt后用pip list检查一下torch、torchvision版本是否匹配版本不匹配会在训练时报奇怪的CUDA错误。遇到这类问题直接重装对应版本的torch即可不用纠结。训练之前还有一个关键步骤下载预训练权重。YOLOv5提供了在COCO上预训练好的权重文件用这些权重做迁移学习初始化比从零训练收敛快得多、精度也更高。下载方式是在yolov5目录下执行python -c from utils.downloads import attempt_download; attempt_download(yolov5s.pt)注意预训练权重对应的类别数是COCO的80类和你的果蔬类别数不一致。不过这没关系YOLOv5在加载预训练权重时会自动处理输出层的权重只加载能匹配的层最后一层检测头会重新初始化。这也是它能做迁移学习的原因。3.2 数据集结构改写成YOLO格式YOLOv5要求的数据集目录结构非常固定目录名和存放路径都不能搞错。我的目录结构如下datasets/ ├── fruits/ │ ├── images/ │ │ ├── train/ │ │ └── val/ │ └── labels/ │ ├── train/ │ └── val/训练集和验证集的图片分别放在images/train和images/val下对应的标签文件同名.txt文件放在labels/train和labels/val下。每张图片对应一个txt标签文件文件名和图片名完全一致后缀名为.txt。标签文件的每一行代表一个标注对象格式是class_id x_center y_center width height其中x_center、y_center、width、height都是归一化到0-1之间的数值。以一张640x480的图片为例如果一个苹果的包围框左上角坐标是(160, 120)右下角坐标是(400, 360)那么归一化后的中心坐标就是((160400)/2/640, (120360)/2/480) (0.4375, 0.5)宽度是(400-160)/640 0.375高度是(360-120)/480 0.5。这一条标注文本就是0 0.4375 0.5 0.375 0.5。如果你从LabelImg导出的是Pascal VOC格式XML文件需要写一个脚本把XML转成txt如果用的是AI Challenger数据集的JSON标注也需要写脚本提取检测框信息并做归一化。这个转换过程虽然麻烦但逻辑并不复杂基本上就是读取XML或JSON里的坐标信息做一遍归一化再按类别映射成ID写入txt。3.3 超参数设置先把跑通再谈调优YOLOv5的超参数配置主要有三个地方模型结构文件yolov5s.yaml、数据集配置文件fruits.yaml、训练参数命令行传入。模型结构文件里最重要的是nc参数表示类别数需要改成你自己的类别数量。数据集配置文件里要写清楚训练集和验证集的路径、类别名列表。训练命令行是核心python train.py --data fruits.yaml --weights yolov5s.pt --img 640 --batch 16 --epochs 100 --device 0这里几个关键参数的作用和取舍我给你说明一下img 640输入图片尺寸。YOLOv5官方在COCO上默认用640作为训练尺寸这个尺寸在精度和速度之间最平衡。如果你的场景里果蔬特别小比如车厘子、蓝莓可以试试896或1024分辨率更高小目标更容易被检测到但显存占用和推理耗时会明显增加。batch 16批大小。12G显存跑yolov5sbatch 16比较稳妥再往上容易OOM。如果显存不够可以调到8甚至4但训练稳定性会差一些学习率也需要相应调低。epochs 100训练轮数。对于果蔬这种中等难度的任务100轮通常可以看到不错的收敛效果。如果loss还在下降可以继续加轮数。还有个细节是锚框尺寸。YOLO系列算法需要预设一组初始锚框模型在训练中会基于锚框逐步逼近真实目标框。YOLOv5默认在COCO数据集上统计出来的锚框尺寸适用的目标大小和果蔬场景不一定匹配。好在YOLOv5有一个自动计算锚框的逻辑训练开始时它会用k-means算法在你自己的数据集上重新聚类计算合适的锚框尺寸所以一般情况下你不用手动干预。只有在训练日志里看到明显的锚框异常提示时才需要手动调整。4. 训练过程观察损失曲线、失败案例与调优方向4.1 训练日志怎么看训练过程中终端会每个epoch打印一次关键指标包括box_loss边界框回归损失、obj_loss目标置信度损失、cls_loss分类损失、mAP0.5IOU阈值0.5时对所有类别的平均精度等。一个正常收敛的训练过程这几个指标应该呈现这样的趋势loss持续下降然后趋于平缓mAP持续上升然后趋于平缓。loss下降和mAP上升之间存在一定的滞后关系所以如果你看到loss在降但mAP还没涨起来不用着急多等几个epoch再看。我训练时习惯盯两个东西mAP0.5这是最直观的指标表示IOU阈值0.5时的平均检测精度。mAP0.5达到0.9以上说明检测框和真实框的重合度能满足大多数业务需求。mAP0.5:0.95这个是更严格的指标表示在0.5到0.95的不同IOU阈值下平均精度的平均值。这个指标比mAP0.5更能反映框的定位精度一般会比mAP0.5低很多不要看到它低就以为模型不行。另外YOLOv5训练结束会生成混淆矩阵图confusion_matrix.png这个图非常有用。它能直接告诉你哪些类别之间容易互相误判。我看到自己训练的混淆矩阵后发现苹果和番茄两个类别之间存在明显的误判这才有了后面针对性的数据补充方案。4.2 两个典型的训练失败案例案例一部分类别完全学不到第一版训练我的数据集中柠檬只有不到50张图片而苹果有400多张。结果训练出来的模型柠檬的ap平均精度只有0.2左右其他类别都在0.85以上。这就是典型的样本不平衡问题。解决办法有两个方向。一是收集更多柠檬图片把样本数补到和苹果差不多的量级。这个最直接有效。二是降低模型对这个类别的学习难度比如对柠檬图片做更强的数据增强加倍增强概率或者按类别数量调整loss权重让样本少的类别对loss的贡献更大。在YOLOv5里你可以修改utils/loss.py中的类别损失权重来手动调整。不过我的建议是优先补数据调整loss权重是补救措施数据始终是第一位的。案例二模型过拟合验证集mAP虚高但真实场景崩了有一版训练验证集mAP0.5达到了0.95我当时以为模型已经接近完美了结果拿到摄像头下实测发现漏检、误检严重到无法使用。后来分析原因是我的训练集和验证集来自同一批拍摄的图片场景背景、光照条件太相似模型实际上学到了大量背景特征而不是果蔬本身的特征。这个问题在学术界叫数据集偏差在行业里我们通常就叫数据太干净了。解决办法是要保证训练集和验证集的数据分布有差异比如在不同时间、不同光照、不同背景下分别采集数据。我在后续版本中专门分出了两天拍摄的数据作为验证集确保验证集和训练集不来自同一场景。这个改动之后验证集mAP虽然降到了0.89左右但真实场景的表现反而大幅提升这才是真实模型能力的反映。4.3 调优方向和实际操作基于上面两个失败案例和之前的多次迭代我整理了一张调优行动表当你自己训练果蔬模型时遇到问题可以对照查阅问题现象可能原因调优动作整体mAP低0.8以下训练轮数不够增加epoch到200观察loss是否继续下降某个类别mAP明显偏低该类样本太少补充该类别的图片或调整loss权重两个类别互相误判两个类别视觉特征相似增加这两类的边界样本检查标注是否有错误验证集mAP高但实测差数据集偏差重新划分训练/验证集确保来源不同场景小目标果蔬蓝莓、车厘子漏检输入分辨率太低把img从640提高到896或1024训练时loss不下降学习率过高或过低检查lr参数默认0.01一般不用调如果初始loss震荡尝试降到0.001还有一点是关于训练轮数的经验。YOLOv5默认的100轮对果蔬识别来说通常不够让模型完全收敛。我最终训练了200轮在150轮左右看到mAP趋于稳定后续50轮提升不大。如果你的GPU算力比较紧张可以训练到验证集mAP不再明显上升时就提前停止这个机制YOLOv5默认是开启的patience100。5. 从训练模型到可用服务推理部署与系统集成5.1 自定义推理脚本别直接用detect.py模型训练完你得到的是runs/train/exp/weights/best.pt这个权重文件。这时候你可能想先用YOLOv5自带的detect.py快速验证一下效果python detect.py --weights runs/train/exp/weights/best.pt --source test.jpg --conf 0.4这个命令能用但它是为演示设计的不管做服务还是集成到业务系统都不合适。detect.py每次运行都要重新加载模型内部还做了很多非必要的图像保存操作延迟和资源消耗都偏高。实际开发时建议自己写一个推理脚本核心逻辑非常简单import cv2 import torch model torch.hub.load(ultralytics/yolov5, custom, pathbest.pt, force_reloadTrue) model.conf 0.4 # 置信度阈值 model.iou 0.5 # NMS的IOU阈值 img cv2.imread(test.jpg) results model(img) # 结果解析 for det in results.xyxy[0]: x1, y1, x2, y2, conf, cls det.tolist() name model.names[int(cls)] print(f{name}: conf{conf:.2f}, bbox({x1:.0f},{y1:.0f},{x2:.0f},{y2:.0f}))这里有两个阈值参数需要注意。conf是置信度阈值低于这个值的检测框会被过滤掉。实际业务中太低容易误检背景里的图案被当成果蔬太高容易漏检。我推荐设0.4到0.5之间然后根据实际场景微调。iou是NMS的IOU阈值用于去除重叠的检测框一般0.45到0.5是安全区间不用调。5.2 用FastAPI把识别封装成服务如果要把识别能力接入实际的结算系统最直接的方式是提供一个HTTP接口。我用FastAPI封装了一下代码量很少from fastapi import FastAPI, UploadFile from PIL import Image import io app FastAPI() app.post(/detect) async def detect(file: UploadFile): img Image.open(io.BytesIO(await file.read())) results model(img) detections [] for det in results.xyxy[0]: x1, y1, x2, y2, conf, cls det.tolist() detections.append({ class: model.names[int(cls)], confidence: round(conf, 4), bbox: [round(x1), round(y1), round(x2), round(y2)] }) return {detections: detections}前端调用这个接口传一张图片过去就能拿到识别结果列表。部署时用uvicorn app:app --host 0.0.0.0 --port 8000启动服务局域网内的设备就可以访问了。这里有个性能上的坑FastAPI默认是单worker运行如果同时有多个请求进来后面的请求需要排队。对超市结算这种并发量很低的场景单worker完全够用。如果并发压力大可以用gunicorn多worker部署但要处理好模型在每个worker中的加载问题避免重复加载浪费内存。5.3 视频流识别和轻量化部署静态图片接口能搞定一部分业务但如果需要实时视频流的识别比如摄像头连续扫描就需要用OpenCV读取视频帧逐帧调用模型推理。一个简单的循环框架如下cap cv2.VideoCapture(0) while True: ret, frame cap.read() results model(frame) rendered results.render()[0] cv2.imshow(YOLOv5 Detection, rendered) if cv2.waitKey(1) 0xFF ord(q): break逐帧推理在GPU上耗时大约30毫秒yolov5s模型能够达到30FPS左右的实时帧率。但如果你的部署环境没有GPU纯CPU推理的速度会慢不少一帧可能要一两百毫秒看起来会有明显卡顿。这种情况下有两个优化方向导出ONNX格式用ONNX Runtime加速推理。YOLOv5提供了现成的导出脚本python export.py --weights best.pt --include onnx。ONNX格式在CPU上的推理速度通常比PyTorch原生快不少。如果是嵌入式设备比如RK3568、树莓派可以进一步导出TensorRT或NCNN格式。不过这里水比较深不同硬件平台的算子支持和优化效果差异很大建议先在目标设备上做一下推理速度的基准测试再做决定。6. 部署现场的坑和工程化心得6.1 摄像头角度与识别距离要提前固定模型是在一定拍摄距离和角度下训练的如果你部署的时候摄像头位置变了识别效果会大打折扣。我在项目现场就遇到过这个问题训练时模拟的是俯视45度、距离大约50厘米但现场安装的摄像头是正上方垂直角度距离80厘米。结果模型漏检率飙升最后不得不重新补拍了一组正上方视角的数据集进行微调才把效果拉回来。所以经验是在项目早期就确定摄像头安装位置和角度让训练数据尽量贴合部署环境。如果部署环境没法提前确定就在数据集中尽量多样化让模型见过各种角度和距离。6.2 置信度阈值不是固定的很多人在模型部署后就懒得调阈值了但实际上置信度阈值应该根据实际场景动态调整。在你的系统里加上一个配置项让阈值可调。如果业务中更看重不漏检比如结算漏了会少收钱就把阈值调低到0.3左右如果更看重准确率比如不想把背景误判成果蔬给顾客多算钱就调高到0.5以上。另外YOLOv5的NMS阈值也可以微调。如果场景中果蔬堆叠严重NMS阈值稍微调低一点比如0.4可以减少重叠框的保留数量避免同一个果蔬被识别成多个。6.3 易混淆类别要尽快做针对性优化我最终版本里表现最差的两个类别是苹果和番茄。为了彻底解决它们之间的误检问题我做了三件事增加两种果蔬并排摆放、近距离接触的样本图片让模型学习二者的边界特征。检查历史标注数据发现有几张苹果图片误标成了番茄逐一修正。在数据增强中对这两种类别额外生成一些裁剪、旋转、亮度变化的样本。这三步做完之后混淆矩阵里苹果→番茄的误判率从12%降到了3%以内效果非常明显。6.4 关于部署硬件的建议如果你做的是类似的商用项目建议训练阶段用NVIDIA显卡RTX 3060以上即可推断阶段如果追求性价比和低功耗可以放到边缘设备上跑TensorRT加速。但说实话如果你是个人开发、Demo演示或者小规模门店使用直接用一台带GPU的PC做推理服务是最省心的没有必要一上来就上嵌入式设备。先把业务逻辑跑通再考虑硬件优化这个顺序不能反。7. 整个项目过程中我最想强调的经验数据永远是第一位的。模型架构、超参数这些当然重要但决定果蔬识别系统最终效果的往往是数据本身。你愿意花多少时间在数据采集、清洗、标注、验证集划分上模型就会回报给你多少精度。我在这个项目上反复体会到同一个YOLOv5模型用干净的数据和脏数据训练出来的效果可以说是天壤之别。最后再分享一个工具上的小技巧训练过程中YOLOv5会在runs/train/下保存每次训练的日志和图表。训练结束之后我会把最终一轮的混淆矩阵、F1曲线、PR曲线这几张图单独复制出来留档后面写项目文档或者向别人解释模型效果时这些图就是最直接的说明材料。这套流程跑熟了以后再做新的目标检测项目从数据处理到模型部署基本上就是一条流水线省下的时间相当可观。本文还有配套的精品资源点击获取
返回列表