ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

YOLOV5实战:垃圾桶满溢检测数据集训练与部署避坑指南

YOLOV5实战:垃圾桶满溢检测数据集训练与部署避坑指南 简介一套面向目标检测初学者与智慧环卫场景开发者的YOLOV5垃圾桶满溢检测实战项目。项目包含完整数据集、训练代码与已收敛权重覆盖满溢、未满溢、垃圾三个类别直接修改配置即可训练或推理。资源共2000个文件以txt标签、py训练脚本、yaml模型配置、sh辅助脚本为主压缩包约450MB结构清晰便于快速上手。已有363人学习。数据集包含2680张训练图片与669张验证图片及各自标注标签模型迭代100个epoch后最优mAP0.5达0.91mAP0.5:0.95达0.73附有混淆矩阵、PR曲线、F1曲线等可视化分析结果runs/detect目录还保存了对推理结果的完整展示方便复现与对比实验。1. YOLOV5 垃圾桶满溢检测一个 3 类别数据集能省掉多少现场巡检物业、园区、车站这种人流密集区域垃圾桶满溢是投诉高发点。传统做法是安排巡检员定时翻桶既低效又容易漏检。“垃圾桶满溢检测”要解决的就是用摄像头替代人眼实时判断桶是否装满并触发清理工单。这里说的“YOLOV5 实战项目垃圾桶满溢检测数据集3类别”是典型的目标检测落地场景数据按 3 个类别组织好配合 YOLOV5 把“yolov5 训练自己的数据集”完整链路跑通。核心问题不是算法选型而是“3 类怎么定义、数据怎么组织、参数怎么调、部署怎么避坑”。适合正在学目标检测的开发者、做智慧园区 demo 的工程师以及拿毕业设计练手的学生。一份类别清晰、标注规范的数据集配合正确训练流程比自己从零攒数据少踩一半坑。2. 满溢检测为什么选 YOLOV53 类别定义与数据采集选型2.1 先定 3 个类别再谈算法满溢检测的标签语义垃圾桶满溢检测本质上是一个目标检测任务但业务上最关键的决策不在算法而在“3 类是谁”。常见做法是按“正常 / 半满 / 满溢”三类划分也就是 normal、half、full 三个语义级别。为什么是 3 个而不是 2 个因为“未满 / 满溢”二分类看起来省事但现场视频是连续状态桶从空到满的中间态占绝大多数时间。如果只有二分类系统会在桶装到 70% 时就开始满溢报警保洁还没赶到漏口已经堆起小山或者反过来为了避免误报把阈值调得很高结果真满溢时又漏报。加入 half 这个中间态之后报警策略可以做成“half 状态进入待处理队列full 状态才推工单”调度体验会正常得多。标签语义必须落到标注规则上不能靠标注员感觉。我一般会把三类定义写在标注规范里normal 指桶内垃圾低于桶口、盖子可以正常闭合half 指垃圾接近桶口、桶盖已经无法完全闭合但垃圾没有高出桶沿full 指垃圾高出桶口沿或者溢出物已经占据桶周围视野。这套定义同样适用于那种没有桶盖、只靠传感器判断的智能垃圾桶只是视觉上把“盖子能否闭合”换成“垃圾平面是否超过桶口沿”。如果今后业务想做得更细把 full 再拆成“轻微溢出 / 严重溢出”就是 4 类甚至 5 类但那会直接抬高标注成本3 类是这个场景里性价比比较高的折中。一句话总结选型逻辑YOLOV5 对这类“单目标多状态”任务非常顺手。垃圾桶是刚性轮廓状态特征集中在顶部区域YOLOV5 的 anchor 机制和 640 输入尺寸能很好覆盖同时它的生态和资料比 YOLOV8 更全很多一键部署方案都以 V5 为基准。当然如果你的项目从零开始且不依赖老代码YOLOV8 也可以但本实战方案以 YOLOV5 为主因为它对新手更友好、排查经验更好找。2.2 YOLOV5 环境配置conda、依赖与权重文件的选择训练前先把环境搭干净。常见做法是用 conda 建独立环境Python 版本按 YOLOV5 官方 requirements 的要求来一般 Python 3.8 到 3.10 之间都能跑。创建命令如下conda create -n yolo python3.8 -y conda activate yolo pip install -r requirements.txtrequirements.txt 是 YOLOV5 仓库根目录下自带的依赖清单把 torch、torchvision、opencv-python、numpy、matplotlib、pyyaml、tqdm 等都列好了不需要自己一个个猜版本。如果本机有 NVIDIA 显卡装依赖前先把 CUDA 版 PyTorch 装好再装 requirements 里的其他包否则 pip 会默认拉 CPU 版 torch训练速度能差出十倍。pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118 pip install -r requirements.txt权重文件的选择我有自己的习惯先用 yolov5s.pt 跑通流程确认数据和标注没大问题之后再根据精度瓶颈决定是否换 yolov5m 或 yolov5l。s 模型训练速度最快一个 3000 张的数据集在单张 3060 显卡上 80 轮大约两小时能跑完m 模型精度更高但训练时间接近翻倍。不要一上来就用 l 或 x满溢检测不是超高难度任务s 或 m 通常够用盲上大模型只会让调参周期变长。2.3 数据采集把现场拍成能训练的素材采集阶段决定了后面所有工作的上限。我一般要求自己或客户架机位时遵守三个原则覆盖完整、状态多样、时段覆盖。覆盖完整指摄像头要能看到垃圾桶顶口和周围约一米的落地区域挂得过高会压缩垃圾溢出物的像素面积挂得过低又会被路过的人遮挡状态多样指同样一个桶要拍它空桶、半桶、满溢、被塞进大纸箱、旁边堆了杂物等各种状态模型才不至于只认识“干净的垃圾桶”时段覆盖指至少要混合白天、傍晚、夜间三种光照。垃圾桶满溢检测最常见的翻车就是夜间桶口反光、垃圾袋表面高光会让模型把普通塑料反光当成溢出物。采集数量上3 类别的项目我给出的参考区间是每类 800 到 1500 张总计 3000 到 5000 张。这个量配合预训练权重足够收敛也足够支撑 85% 左右的训练/验证划分。如果你是从零训练不加载预训练权重数量最好再翻倍否则收敛过程会很折磨人。采集时不要只拍静态照片尽量从视频里抽帧同一场景相邻帧之间的视角差异很小抽帧间隔保持在 5 到 10 帧能有效扩充数据量又不会让数据过于相似。另外在数据合规上提醒一句公共区域采集的视频要避开人脸和车牌特写如果必须拍到用打码工具统一处理。抽帧之后用感知哈希比较相邻帧的相似度去掉重复度极高的帧。一套 3000 张的数据集里经常会掺着几百张几乎静止的重复帧它们不会带来新信息只会拉长训练时间还会让模型对特定视角过拟合。3. 把现场照片整理成 YOLO 训练集目录结构、标注导出与清洗脚本3.1 数据集目录结构images/labels 与 train/val 划分YOLOV5 训练自定义数据集时默认会找两个关键目录images 和 labels这两个目录下再各自分 train 和 val。labels 目录里存放与图片同名的 .txt 标注文件图片是 a.jpg标注就是 a.txt。目录结构建议如下data/ ├── images/ │ ├── train/ │ ├── val/ ├── labels/ │ ├── train/ │ ├── val/ └── full_dataset.yamlYOLOV5 在训练前只会检查目录存在性不会自动帮你创建 images/labels 的 train/val 子目录所以先把空目录建好再用脚本把图片和对应 txt 成对拷贝进去。另一个容易忽视的点是如果某张图片没有任何目标被标注它对应的 txt 文件是空文件YOLOV5 会把这图视为背景图。如果满溢检测数据集中混入了一批“空垃圾桶但没标注”的图片模型会把空桶学成背景导致现场空桶也被误检这种隐性错误比越界标注更难排查。3.2 用 LabelImg 或 CVAT 标注并导出 YOLO 格式标注工具选择小项目、单机标注用 LabelImg界面简单矩形框拖拽顺手项目多人协作标注用 CVAT有 Web 端和标签校验流程。两者都支持导出 YOLO 格式导出的 .txt 每一行是类别 ID、归一化的中心点 x、中心点 y、框宽 w、框高 h。这个格式是 YOLO 系列统一的后续训练不用再做格式转换。标注满溢状态有一个容易绕进去的坑full 类别的框到底框住什么我的规则是“框住桶口沿以上溢出的垃圾主体并把桶口外沿包含进去四分之一”。只框溢出物会导致标注框过小、位置偏高模型学到的特征过于碎片化把整个桶身都框进去又会让 full 和 normal 的框形状几乎一致分类器难以区分。半满桶的框则框住整个桶口平面。标注时把这两条写进规范比事后返工省时间得多。批量导出后建议顺手做一次统计每个类别的框数量、每张图的平均框数。满溢检测场景中一张图通常只有一个桶少数情况下有两个桶并排。如果统计结果出现某张图有 8 个框多半是标注了背景里的杂物而不是垃圾桶这类脏数据要优先清掉。3.3 数据清洗与划分脚本跑通最小可训练数据集我标注完从来不直接开训先用一段短脚本扫一遍标签文件的常见问题。下面这个脚本检查三类问题行格式、坐标越界、负值。import os label_dir labels/train for f in os.listdir(label_dir): if not f.endswith(.txt): continue with open(os.path.join(label_dir, f), encodingutf-8) as fp: lines fp.readlines() for line in lines: line line.strip() if not line: continue parts line.split() # YOLO 行的合法结构: class_id, cx, cy, w, h if len(parts) ! 5: print(f{f}: 列数异常 - {line}) continue cls, cx, cy, w, h parts try: cx, cy, w, h float(cx), float(cy), float(w), float(h) except ValueError: print(f{f}: 含非数字坐标 - {line}) continue if min(cx, cy, w, h) 0: print(f{f}: 坐标出现负值 - {line}) # 归一化框的右边界 cx w/2, 不能越过 1.0 if cx w / 2 1.0 or cy h / 2 1.0 or cx - w / 2 0.0 or cy - h / 2 0.0: print(f{f}: 框越界 - {line})这段脚本的逻辑很简单先检查每行是否为 5 个字段再转 float最后判断归一化坐标是否落在 0 到 1 区间。其中“框越界”是标注工具最常见的坑尤其是手拖矩形框时拖出了图像边缘导出的 cx、cy 可能轻微越界。YOLOV5 训练时会对越界框做裁剪但如果越界幅度很大模型学到的是半个完整目标的特征这类样本多了会拖低 mAP。检查完标签后做数据划分。划分脚本要解决的核心问题是不能让同一个垃圾桶的多张照片同时出现在训练集和验证集否则验证结果没有参考价值。按采集时记录的桶编号分组划分而不是直接对图片文件名随机划分import os import random import shutil # 图片文件名格式约定: bucket_id_frame.jpg, 例如 03_0005.jpg src_img all_imgs bucket_map {} for f in os.listdir(src_img): if not f.endswith(.jpg): continue bucket_id f.split(_)[0] bucket_map.setdefault(bucket_id, []).append(f) bucket_ids list(bucket_map.keys()) random.seed(42) random.shuffle(bucket_ids) split int(len(bucket_ids) * 0.85) train_buckets set(bucket_ids[:split]) val_buckets set(bucket_ids[split:]) for f in os.listdir(src_img): if not f.endswith(.jpg): continue bucket_id f.split(_)[0] target data/images/train if bucket_id in train_buckets else data/images/val shutil.copyfile(os.path.join(src_img, f), os.path.join(target, f)) txt_path os.path.join(all_labels, f.replace(.jpg, .txt)) if os.path.exists(txt_path): shutil.copyfile(txt_path, os.path.join(target.replace(images, labels), f.replace(.jpg, .txt)))这段脚本按文件名的第一个下划线前的编号作为桶 ID同一个桶的照片全部进同一个集合。训练集和验证集的划分是整个工程里最容易被忽略的一步直接 random shuffle 图片会让验证集里出现和训练集几乎同一视角的照片mAP 虚高 3 到 5 个点部署现场立刻打回原形。按桶分组划分后验证集的难度更接近真实“没见过的桶”。4. 用 YOLOV5 训练 3 类别垃圾桶数据集data.yaml、超参数与命令判读4.1 data.yaml 的写法类别顺序与路径相对化训练前必须写一个 data.yaml 描述数据集位置和类别信息。YOLOV5 的 data 参数接受 .yaml 文件路径内容如下path: ./data train: images/train val: images/val nc: 3 names: 0: normal 1: half 2: full这里最需要注意的是 names 的索引顺序必须和标注 txt 里的类别 ID 完全一致。如果你在标注工具里把 full 定义成 ID 0但 yaml 里 names 的 0 是 normal模型会把满溢学成正常训练过程不会报任何错验证集上 mAP 也正常但类别语义全是乱的。所以写完 yaml 后我习惯跑一个小脚本随机打印 10 张图及其标签肉眼核对一下标签和图片内容是否对应。path 字段我建议写成相对路径配合训练命令里的相对位置。不要写死 /home/xxx 这种绝对路径。YOLOV5 在读取 train/val 字段时如果 train 写的是绝对路径就优先用绝对路径如果是相对路径就基于 path 拼接。把 path 设置为当前项目根目录train 写成 images/train这样整份 yaml 可以随着项目文件夹迁移直接换机器训练不用改配置。这一点会在后面避坑章节详细展开先按这个习惯写就好。4.2 三个必调超参数epoch、batch-size、img-sizeYOLOV5 的超参数分成两块train.py 命令行里的训练超参数以及 data/hyps/hyp.scratch-low.yaml 里的优化器超参数。训练一个 3 类别检测任务命令行里最需要关心三个参数。epoch单卡 3060 级别3000 张数据我从 80 轮起步。加载预训练权重时80 轮足够让模型收敛到实用的精度如果你的数据集是用手机现拍的、标注也有噪声可以适当加到 120 轮因为数据噪声大时模型需要更多轮次来吸收有效特征过早停止会欠拟合。batch-size由显存决定。6GB 显存跑 img-size 640 的 yolov5sbatch-size 16 左右12GB 显存可以开到 32。batch-size 太小会让梯度抖动剧烈loss 曲线看起来像锯条这时候不要怀疑代码先把 batch 提到 16 以上再观察。img-size默认 640。满溢检测里垃圾溢出物的目标通常不大如果现场摄像头视角较远我会先用 1280 跑一版对比。1280 对显存的消耗接近 640 的四倍如果机器带不动优先考虑调整拍摄机位让垃圾区域占更多像素而不是硬上大分辨率。python train.py \ --data full_dataset.yaml \ --weights yolov5s.pt \ --img 640 \ --batch-size 16 \ --epochs 80 \ --cache ram \ --workers 4参数--cache ram是把图片预加载进内存能明显减少每个 epoch 的磁盘读取时间--workers 4是数据加载线程数Windows 上如果报 DataLoader worker 相关错误把它降到 0 或 2 即可。--weights yolov5s.pt会加载 COCO 预训练权重对垃圾桶这种通用物体迁移效果很好不建议从--weights 开始。另外 YOLOV5 自带的数据增强默认开启马赛克和 HSV 扰动采集素材里如果有大量夜间或强反光图建议把 hyp 文件里 hsv_h、hsv_s 的幅度调小桶的颜色本来就固定过度色偏增强会让模型学到不真实的视觉外观。4.3 训练日志判读怎么看模型真的在收敛训练启动后终端里每一轮都会输出一行指标box_loss、obj_loss、cls_loss、P、R、[email protected]、[email protected]:0.95。满溢检测的 3 类别任务里我优先看两类指标。第一看 val cls_loss 是否持续下降。cls_loss 是分类损失normal/half/full 三类的区分度都体现在这里。如果 cls_loss 在 30 轮之后开始波动不降说明类别特征不够明显比如 half 和 full 的标注边界模糊这时去查标注而不是加轮次。第二看 [email protected] 的最终值。对于这个任务[email protected] 在验证集上达到 0.85 以上算合格。注意这里的验证集是按桶 ID 分组划分的模型见到的都是“没见过的桶”。如果这个数值能到 0.85部署到新点位大概率不会崩。若验证 mAP 很高但现场频繁误报问题通常不在训练而在后处理阈值这部分在第 6 章展开。还有一个重要习惯训练完不要只看 last.pt一定要用 best.pt。YOLOV5 在每轮结束后会根据验证 mAP 保存 best.pt它对应的状态不一定是最后一轮但一定是验证集上表现最好的权重。导出和部署都以 best.pt 为准last.pt 只用来断点续训。5. 垃圾桶满溢检测避坑小目标、标注边界与误报的 5 个翻车现场5.1 满溢目标太小resize 后被压成 8 像素现象训练曲线正常验证集 mAP 不低但一到现场测试距离稍远的满溢桶全部漏检尤其是溢出物只有一小团垃圾袋时。原因摄像头视角较宽垃圾高出桶口的那部分在 640 分辨率下只占十几个甚至几个像素YOLOV5 的深层特征图下采样 32 倍小目标特征基本被抹平。解决先把机位调低或者拉近让桶在画面中的高度占到 30% 以上其次是训练时把 img-size 从 640 调到 1280同时把 batch-size 减半。如果两者都做不到考虑把检测区域裁成几个 ROI 分别送检而不是指望模型在整幅画面里找小目标。5.2 half 和 full 边界模糊标注规则不统一现象full 的 recall 很高但 precision 很低现场显示满溢报警的大部分桶其实只有半满。原因标注员把“快满了”和“已经溢出来”都标成了 full类别边界由人说了算模型只能学到一半语义。解决重新审核标注确立硬性规则——full 必须以垃圾高出桶口沿为底线且标注框包含溢出主体half 以桶盖无法闭合为判断依据。审核方法是在训练集里采样 50 张 full 图片逐一检查框的位置和类别必要时删掉边界模糊的样本。宁可少数据也不能让类别语义浑浊。5.3 data.yaml 里写死绝对路径换机器就报错现象本地训练结束后把项目拷贝到服务器或另一台电脑train.py 报No such file or directory或者AssertionError: train: No labels in ...。原因data.yaml 里 path 或 train/val 写的是/home/yourname/...这种本机绝对路径换了根目录后路径直接失效。解决全部改成相对路径组织。yaml 里path: ./datatrain: images/train训练命令在项目根目录下执行。这一步看似不起眼实际是多人协作项目里最频繁翻车的点。5.4 直接随机划分数据集验证集见过了训练集的桶现象训练过程中 val 指标一直很高loss 看起来也漂亮但新点位部署后明显不如验证集表现。原因数据按图片随机划分同一个桶不同角度的照片一部分进了训练集一部分进了验证集验证过程等于是开卷考试。解决按第 3 章划分脚本那样按桶 ID 分组划分确保一个桶的所有图片只出现在一侧。验证集 mAP 会因此下降一点但那是真实的水平如果下降幅度太大说明原始数据量不足或同一个桶的不同状态图片太少需要补采集。5.5 类别不平衡normal 占七成full 学不出来现象训练结束看混淆矩阵normal 的 recall 很高full 的 recall 只有 50% 左右现场对真正满溢的桶频繁漏报。原因正常状态的桶占采集量的大部分满溢状态因为“少见”而被模型忽略。解决采集阶段就按比例控制full 类不要低于总量的 25%。已经采完的话用简单过采样把 full 类样本复制两到三份再训练。YOLOV5 还可以通过--cls 1.0调高分类损失权重默认 0.5 在类别不平衡时会偏向大头类别。注意过采样复制的是同一批图YOLOV5 自带的马赛克和随机增强会生成不同变体不会完全重复。6. 从训练结果到现场报警后处理、ONNX 导出与边缘端部署6.1 置信度阈值与连续帧触发报警的最后两道闸门训练 mAP 达到 0.85 只是第一步现场报警的关键在后处理。默认推理阈值 0.25 对满溢检测太激进塑料反光、雨水影子都容易让模型在 0.3 附近抖动。我的做法是双阈值检测阈值 0.35只保留超过它的 full 检测框报警阈值 0.5 且连续 3 帧都触发才推送工单。连续帧能避免人影遮挡造成的瞬时误报。score 取目标置信度乘以类别置信度低于阈值的弱检测直接丢弃。阈值不要拍脑袋定拿一段 30 分钟现场视频跑一遍统计误报间隔再决定。满溢报警宁晚 5 秒也不要一两小时一次的假工单保洁对狼来了式的报警会彻底免疫。6.2 ONNX 导出与树莓派 5 推理验证python export.py \ --weights runs/train/exp/best.pt \ --include onnx \ --opset 12opset 12 兼容性好树莓派 5 和多数嵌入式设备上的 onnxruntime 都能直接加载。导出后用 onnxruntime 跑一遍样例图对比 PyTorch 推理结果两者的 mAP 差异应控制在 0.2 个百分点内。树莓派 5 的 CPU 跑 yolov5s 的 640 输入 ONNX 模型单帧推理大约 80 到 150 毫秒按 5 秒一帧抽检完全够用。部署时用官方 arm64 预编译包注意散热避免长时间推理触发降频。这个项目让我印象最深的一次翻车是图省事直接随机划分数据验证 mAP 0.9现场新点位掉到 0.7。改成按桶 ID 分组后验证 mAP 0.86但新点位表现一致。数据集的类别定义、标注规则、划分方式这三件事做扎实了从训练到部署会很顺。希望帮到你。本文还有配套的精品资源点击获取
返回列表