ARTICLE DETAIL

资讯详情

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

垃圾分类图像识别实战:基于YOLOv8改进的目标检测系统部署全流程

垃圾分类图像识别实战:基于YOLOv8改进的目标检测系统部署全流程 大概一年前我接到一个让我印象很深的活儿某垃圾分类项目要在投放点的摄像头后面加一套“智能识别”居民拎着垃圾袋刚走到桶前屏幕就要能显示“可回收”“厨余”“其他”这类提示。需求方一开始以为这事像人脸识别一样简单真正动手才发现生活垃圾图像识别几乎是目标检测领域里最“脏”的场景之一瓶子反光、塑料袋半透明、食物残渣形态随机、拍摄角度五花八门。项目最终落地在一个基于 YOLOv8 改进算法的小型目标检测系统上前后跑了四个月。这篇文章我把整个设计过程和踩坑记录整理出来给后面接同类项目的朋友一个参考。1. 需求拆解垃圾分类场景为什么比想象的更难1.1 先弄清业务端到底要什么接项目第一步不是写代码而是把“垃圾分类识别”这六个字翻译成可执行的检测指标。当时客户给了几个原始需求我整理下来大概是这样识别对象塑料瓶、易拉罐、纸箱、玻璃瓶、剩饭剩菜、果皮、电池、烟蒂一共八类。场景白天室外投放点摄像头距离垃圾袋约1.5到3米偶尔有夜间补光。输出要能在边缘设备上跑实时识别准确率“不能太低”但客户自己也没法定义“太低”是多少。我跟客户来回磨了两轮最后把目标锁成“mAP50 不低于 85%单帧推理延迟不超过80毫秒设备是RK3588”。这个目标之所以重要是因为它直接决定了后面所有选型mAP50对应的是检测框能不能正确框住物品推理延迟对应的是模型能不能在边缘设备上跑成实时RK3588则把模型的大小和控制流限制死了。这里有个经验想分享垃圾分类识别不是越准越好而是要和硬件算力、业务容忍度对齐。如果客户要的是“投放点屏幕提示”那漏检可以适度容忍但不能频繁误报如果后端是自动机械分拣线那误检成本极高宁可漏检也不能分错桶。业务需求不同模型的训练策略和置信度阈值都会不同这一步必须在做数据之前就聊清楚。1.2 为什么基线选了 YOLOv8目标检测算法这几年选择很多两阶段的 Faster R-CNN、单阶段的 SSD、YOLO 系列、还有基于 Transformer 的 DETR 类模型。我选 YOLOv8 做基线主要基于三个判断第一YOLOv8 在“工程部署”这件事上极其成熟。它继承了 YOLOv5 的工程化思路训练、导出、量化的链路非常顺滑社区资料也多出了问题搜一下基本能找到答案。对于这种需要四个月交付、还要部署到边缘设备的项目可维护性比理论上限更重要。第二YOLOv8 的 Anchor-Free 检测头和动态标签分配对“形状不规则”的垃圾目标更友好。传统 Anchor 机制对四边形框密集堆叠、宽高比极端的物体会有先验限制而生活垃圾里塑料瓶、纸团、食品包装形态差异特别大Anchor-Free 的回归方式省去了调 Anchor 参数的功夫。第三YOLOv8 的 C2f 结构和 PAN-FPN 特征融合虽然不算新但作为基线足够稳。我见过不少项目一开始就上各种花哨改进结果基线都没跑通后面根本分不清提升来自哪里。我的做法是先跑一个干净的原版 YOLOv8确认数据、训练、部署链路全通再逐项加改进。顺带说一个之前的教训不要用 mAP 一个指标赌所有场景。YOLOv8 训练完的 mAP 好看不代表投放点的俯拍角度下表现好。所以我从第一天就要求数据集里必须混入不同高度、不同角度的摄像头视角而不是只找网上那种正对物体的图片。2. 数据集的血泪公开数据、自采和标注策略2.1 公开数据集能借力的部分垃圾分类识别方向有几个公开数据集经常被提到TrashNet 是最常见的入门数据集类别经典但背景单一、场景偏理想TACO 是野外垃圾检测数据集包含更多户外背景但图片质量层次不齐、小目标极多训练难度偏高GCID 这类数据集在物体形态上更接近真实生活垃圾但样本量有限。我做项目时没有死磕某一个数据集而是按类别做了一个“合并清洗”的策略。比如塑料瓶主要从 TrashNet 和 GCID 里抽取烟蒂这种小物件则主要依赖自采数据因为公开集里烟蒂通常只出现在地面而不是垃圾桶附近。合并完初版训练集大概有 6800 张图验证集 900 张测试集单独留了 600 张其中 200 张是从实际投放点摄像头里录的——这一步很关键测试集里必须有真实场景数据否则你永远不知道模型在实际环境里会怎样翻车。2.2 类别定义和标注细节垃圾分类识别的类别定义是一个几乎必然要被反复讨论的问题。拿“塑料瓶”来说矿泉水瓶、饮料瓶、洗洁精瓶都是塑料瓶但它们外观差异巨大有透明的、有带标签的、有压扁的。训练初期如果你把这几种都标成同一个类模型的类内差异会非常大容易漏检。我后来把塑料瓶拆成了“透明PET瓶”和“深色塑料瓶”两个子类推理时再合并展示成“塑料瓶”这个改动直接让召回率上了一个台阶。标注规范里还有三个实用细节透明物体标注要贴着物体的可见边缘走不要包含大量背景。否则模型会学着拿背景的纹理来判断目标换个背景就废了。重叠遮挡的垃圾要标出“可见部分”而不是“猜测的完整边界”。很多标注工具允许画旋转框但 YOLOv8 支持的是水平框所以这类目标在训练时天然存在框不精确的问题我选择把严重遮挡的样本过滤掉保证训练集干净。半透明塑料袋里的垃圾如果边角完全看不清不要硬标。你要么单独设一个“袋装垃圾”类要么直接放弃这类训练样本靠后处理去兜底。2.3 数据增强与类别平衡操作垃圾场景里最难处理的是小目标烟蒂、纸屑在 640×640 的图里往往只有二三十个像素。YOLOv8 自带的 Mosaic 增强对这类目标有一定帮助因为它会把多张图拼接起来让模型在相对更小的目标上学习。但 Mosaic 也会带来一个问题训练后期模型容易在小目标上过拟合因为 Mosaic 产生的大量小目标位置有些是“假”的。我的数据增强配置里保留了 Mosaic 和 MixUp但在最后 30 个 epoch 关闭了 Mosaic让模型回到真实分布上精调。这招是从 YOLOv8 的关灯训练策略里改出来的实际效果是 mAP50 提升了 1.2 个点左右mAP50-95 提升更明显。类别不平衡同样要处理。烟蒂、电池这类小样本类别我做了离线复制增强重复采样和随机旋转、平移、仿射变换。注意复制增强不能太猛我试过把烟蒂复制三倍结果模型开始把地面砖缝误判成烟蒂。原因很简单——复制样本之后背景一起被复制了模型学到了“这个纹理区域周围容易出现烟蒂”的上下文信息而不是烟蒂本身。3. 对 YOLOv8 的改进从哪儿动手收益最明显3.1 小目标检测头带来的召回提升YOLOv8 原始结构在 640×640 输入下有 P380×80、P440×40、P520×20三个检测层分别对应小、中、大目标。但对垃圾识别场景来说烟蒂、纸屑这类小目标在 P3 层上的特征图仍然不够精细P3 的 80×80 格子对 35×35 像素的目标其实只有一个格子的响应。我参考了 YOLOv8-P2 的做法在骨干网络里把高分辨率特征层 P2160×160也接进特征金字塔让模型在原始分辨率的四分之一尺度上直接检测小目标。改完后参数量上升了大概 8%GPU 训练速度慢了但能接受关键是烟蒂和碎玻璃这类小类别在验证集上的召回率提升了 5% 以上。这里有一个代价需要提前说清楚P2 层会大幅增加算力开销因为 160×160 的特征图比 80×80 的多了四倍像素。如果最终目标是部署在 RK3588 这种边缘设备上我建议把 P2 层放在训练阶段使用部署前再剪掉或换用更轻量的小目标专用分支。我在实际部署时做了取舍最后用的是 P3P4P5但对小目标类别增加了数据重采样算是平衡方案。这个取舍要具体问题具体分析不要无脑堆检测头。3.2 注意力与 Neck 的取舍第二处改动是在骨干网络尾部加注意力模块。我先后对比了 SE、CBAM 和坐标注意力Coordinate Attention。垃圾识别里很多目标靠的是局部纹理而不是全局轮廓比如皱巴巴的纸团、残羹——SE 只给通道加权空间信息基本丢掉CBAM 虽然加上了空间注意力但参数量偏大。坐标注意力把空间方向的位置信息编码进通道注意力对细长物体如烟卷、树枝更友好也比 CBAM 轻。实际实现里我把坐标注意力插在了 SPPF 模块之后特征进入 Neck 之前。修改的代码量不大但推理速度下降了约 7%换来的是每类平均 mAP 提升约 1.8%。Neck 方面我尝试过把 PAN-FPN 换成 BiFPN加权双向特征金字塔理论上跨尺度特征融合更高效。但 YOLOv8 自带的 Neck 已经比较成熟BiFPN 在垃圾这种目标大小极不均匀的数据集上提升不到 1%反而在导出 ONNX 时多了不少算子兼容问题。最后我把 BiFPN 方案否掉保留了 PAN 结构只是把每个上采样倍率层之后本来简单的 concat 拼接换成了带权重学习的加权融合。这个改法不伤部署精度也稳定。经验之谈注意力模块不是越复杂越好垃圾识别场景里大多数目标形状差异巨大注意力加多了反而导致模型过度关注某个局部纹理而忽视全局上下文。所以我在加注意力模块后的验证集分析里会专门看“误检”是不是变多了而不只是盯着 mAP 涨没涨。3.3 Head 侧的小改动与消融实验改动检测头收益其实没有前面两个高但我在第三节提一下是因为很多网上的“改进 YOLOv8”文章都堆在这一步。解耦头本身已经不错——分类分支和回归分支各自独立理论上可以避免分类和定位互相干扰。YOLOv8 的 Head 使用 TaskAlignedAssigner 做动态标签分配这个对低质量垃圾目标干扰有抑制作用。我做的 Head 小改动是把回归分支里边的 DFLDistribution Focal Loss网格范围从 16 改为 24。因为垃圾的形状很多是不规则的四边形比如压扁的塑料瓶宽高比从接近 1 到接近 5 都有较宽的回归网格可以覆盖更多极端形状。代价是收敛变慢了一些但最后 mAP 提升了 0.6 个百分点属于性价比不错的微调。改进一定要用消融实验收尾。我做的四组实验是实验组配置mAP50mAP50-95备注A原版 YOLOv8n78.352.1基线BA P2 小目标层82.756.9小类别召回提升明显CB 坐标注意力 CA84.158.4细长目标更稳DC DFL范围调整84.859.3最终方案我没有把所有改动一股脑全加比如我试过在 Backbone 每个 C2f 都换成基于 Transformer 的自注意力精度提升不到 1 个点参数量涨了一倍部署直接被砍掉。改进的衡量标准永远是“精度/算力比”而不是单看精度。4. 训练不是背参数曲线、批次与调参决策4.1 数据划分与环境配置训练前我要先交代环境。我用的是单卡 RTX 309024GB 显存CUDA 版本和 PyTorch 都是当时相对稳定的组合。YOLOv8 的官方库本身对依赖管理做得好基本流程就是克隆仓库、创建虚拟环境、安装依赖、下载预训练权重这一步网上教程极多我不展开。但我想强调一件事情训练前一定要把你最终的推理框架定下来也就是要不要转 RKNN、要不要量化。因为不同的部署链路会反向要求训练阶段不用的能力比如量化友好性、算子兼容性。数据划分策略上我的划分方式是“场景优先、混洗在后”。也就是说先把来自同一个投放点的图片分成一组再把不同组按 6:2:2 拆成训练、验证、测试。如果直接全局随机混洗同一个投放点的视角会同时出现在训练和测试集里测试结果虚高一换场景就打回原形。这是垃圾分类识别项目里最常见但最隐蔽的坑之一。4.2 核心训练参数怎么定YOLOv8 训练参数网上可以查到一大堆解释但真正决定我项目成败的参数其实就那几个imgsz我统一用 640。虽然 768 在部分类别上 mAP 更高但推理时间在 RK3588 上会翻倍。模型越大在低算力设备上越难压到实时。batch30 左右。显存不够就开梯度累积不要硬降 batch 到 8 以下。小 batch 会让 BN 层的统计量抖动垃圾这种类内差异大的数据容易训飞。epochs初始 300配合早停参数patience30。我强调这么多是因为垃圾数据噪声大模型往往在 200~250 个 epoch 的时候才真正收敛到稳定区域。optimizerSGD 配合lr00.01是默认方案实测比较稳。AdamW 收敛快但后期泛化略弱对少量样本类别容易过拟合。垃圾分类的小样本类别很多所以我建议用 SGD。mosaic和mixup的实验关灯策略前面提过最后 30 个 epoch 关闭 Mosaic提升明显。还有weight_decay我用 0.0005warmup_epochs3.0。warmup 的作用是让训练前期学习率从小往大爬避免模型刚开始就被大梯度冲乱。很多人一上来就拉满学习率损失曲线直接飞了然后怪数据集有问题其实问题出在没给训练一个“热身”。4.3 损失曲线怎么读loss 震荡怎么办训练过程中我会同时看三条曲线box_loss回归损失、cls_loss分类损失、dfl_loss分布焦点损失。很多新手只关心总 loss但总 loss 掩盖了具体问题。一个常见的场景box_loss 降得很顺畅但 cls_loss 在训练到 150 epoch 之后开始震荡。这种情况通常是类别不均衡导致分类分支对少样本类别过敏感。我的处理方式不是继续加大数据增强而是回去检查验证集的混淆矩阵看哪几类互相混淆。比如烟蒂和碎石子、透明塑料瓶和玻璃瓶这两组是容易混淆的重灾区。后来我把烟蒂类单独做了一组“白底多角度”的补充采样并给透明玻璃瓶增加了背景干扰较少的负样本cls_loss 的震荡幅度就降下来了。另一个同样常见的场景是 loss 突然爆高reset 之后又恢复正常。这种一般是 Mosaic 里出现了全黑或全白的边角导致某个 batch 的输入分布异常。我在数据管道里加了一个过滤规则凡是 Mosaic 合成后目标框总面积占比低于 0.01% 的样本直接丢弃不用。这个小改动虽然不起眼但实测里至少避免了两次无效重训。关于“loss 不降要不要加数据”我的建议是先用当前数据把 baseline 跑通再谈效果。有一回我连续加了 2000 张含黑夜环境下的垃圾图片结果 mAP 反而下降了 2%因为黑夜样本里大量垃圾难以辨认噪音大于信息。后来我改用“亮度扰动”模拟不同光照条件而不是盲目增加夜图效果稳定很多。5. 落地部署从 PyTorch 权重到 RK35885.1 ONNX 导出与头部算子检查项目部署端最终选了 RK3588原因很现实它在端侧 NPU 里性价比高6 TOPS 的 INT8 算力跑小型检测模型够用而且开发资料相比其他边缘芯片更完整。部署的第一步是把训练好的 PyTorch 权重导出为 ONNX再做 RKNN 转换。这个链路看起来简单但 YOLOv8 有它特殊的问题。YOLOv8 的 Decoupled Head 里包含 DFL 模块DFL 在计算边界框时会用积分形式处理 distribution导出的 ONNX 里涉及一些 exp、sum 操作部分 RKNN 版本支持不完整。我的处理方案是对检测头做一次“推理剪枝”把 DFL 在导出前手工展开成纯卷积加矩阵运算推理阶段直接输出边界框的 xywh后处理时再做 NMS。这样虽然损失了一点点精度但兼容性大幅提升。导出 ONNX 时另一个容易出错的地方是动态 batch 和动态分辨率。RKNN 转换工具一般要求输入尺寸固定所以我在导出时就固定成 640×640、batch 1。注意如果你在训练时用了rectTrue的矩形推理导出的 ONNX 可能存在尺寸不匹配这种细节要在导出前排查清楚。5.2 RKNN 转换、量化和精度观察ONNX 转 RKNN 建议直接用 Rockchip 官方提供的 rknn-toolkit2 工具。转换的时候有几个参数值得留意target_platform填写rk3588。quantize要真实跑一遍量化不要跳过。我之前试过跳过量化直接跑 FP16结果算子不兼容报错最后还是老老实实量化成 INT8。量化数据集要选择 200~500 张覆盖各垃圾类别的代表性图片。关键是“代表”不是“最多”。如果量化集里 80% 都是塑料瓶那模型对电池的量化误差会很大部署后电池漏检率会突然飙升。量化之后的精度变化我实测大概是FP16 基本不掉点INT8 会让 mAP50 下降 2~4 个百分点。后处理里对置信度阈值做微调比如从 0.25 升到 0.35就能把误检压下来但相应的漏检会稍稍上升。这个平衡要在端上实测才能确定不能拍脑袋定。5.3 端侧后处理与实时性调优RK3588 的 NPU 主要负责卷积等大计算量算子NMS非极大值抑制这类控制流较多的逻辑放在 CPU 上更快。YOLOv8 原版后处理的 NMS 代码是 Python 写的如果直接在端侧跑会有几十毫秒的额外延迟。我把 NMS 换成 C 实现输入 10 个类别×80×80 的预测结果处理后只剩下不到 5 毫秒的 CPU 开销整体单帧推理稳定在 50 毫秒出头换算下来 20FPS 左右满足“投放点屏幕提示”的需求。还有一个细节是“跳帧”。在投放点这种场景居民不会是每秒都在丢垃圾摄像头大部分时间面对空场景。我在端侧做了一个简单的运动检测背景逻辑如果连续两帧几乎没有像素变化就跳过检测直接输出“空闲”一旦检测到变化立刻跑一帧完整推理。这样既省电也避免了垃圾袋在半空就被抓拍导致识别不稳定的问题。实测部署时我还栽过一个坑RK3588 上跑 640×640 输入NPU 运算时间只有十几毫秒但 CPU 的 NMS 如果不开多线程延迟会持续走高。最后我用 OpenMP 把 NMS 的类别循环并行化才把整体帧率稳定住。部署调优就是这样模型指标只占一半另一半全在工程细节。6. 效果评测与后续反思6.1 客观指标之外现场关注什么测试集上的 mAP50 最终在 84.8 左右相比 baseline 提升了 6.5 个点。但如果只看这个数字用户大概率还是不满意因为实际投放点有一个测试集不会完全反映的维度——人的行为。人的行为是居民会把垃圾袋捏在手里左右晃动、会把垃圾放在桶沿上再松手、会半转身让身体挡住物品。这些动态过程里模型的表现和静止图完全不同。我后来从现场录了 3 小时视频抽了几百帧做“过程性测试”结果发现有相当一部分漏检发生在手部遮挡叠加运动模糊的时候。这类问题不是靠继续加数据就能彻底解决的。当时我用了一个折中方案在界面层做“连续帧确认”——同一目标连续三帧都被识别为同一类别才在屏幕上显示分类结果。虽然响应慢了一两秒但误报率大幅下降。垃圾分类场景里用户对错误分类的批评远比对响应慢的抱怨多所以这个取舍是划算的。6.2 误检与漏检的平衡我前面反复提到误检和漏检的平衡这里用一个具体的例子说明透明塑料瓶和透明玻璃瓶。在自然光下这两类的外观几乎只差一个瓶口螺纹和底部的凹陷线条模型很容易把玻璃瓶判成塑料瓶。如果硬要提升玻璃瓶的召回率最简单的方法是降低它的置信度阈值但这样做塑料瓶的误检会立刻增加。我的处理方法是给这两种类别分别测算“最优置信度”而不是所有类别共用一个阈值。塑料瓶在置信度 0.4 以上就能保持稳定玻璃瓶单独设 0.25因为玻璃瓶整体检出率低宁可多漏一点塑料瓶也不可以把玻璃瓶全丢。这个多阈值策略在项目里屡试不爽后续遇到难分类别都可以照这个思路来处理。6.3 这个项目还能怎么扩展项目交付后我又想到两个可以继续深入的方向。一个是把识别结果和“积分系统”联动这其实是业务层面的升级技术上的检测算法不需要大改只需要把分类结果通过接口传给运营平台。另一个是“袋装垃圾”的另类解法很多垃圾就是装在半透明袋里的目前模型对袋内物品识别率不高与其硬要识别袋里是什么不如提示用户“请拆袋后投放”再用另一个模型判断拆袋行为有没有发生。这个方向需要深度学习的动作识别参与和当前目标检测的任务解耦得更干净。垃圾分类识别这类项目做多了你会发现真正难的往往不是模型结构而是对场景的理解、对边界样本的处理以及和硬件、业务配合的耐心。YOLOv8 确实是一个称职的基线但它只是一个开始。
返回列表