开题时我以为这个题目很简单——无非是拿YOLOv8套一个垃圾数据集,train一会儿出个mAP数字,再写两章创新点就完事。真做进去才发现,"垃圾分类识别"这四个字几乎踩遍了目标检测领域的所有坑:目标尺度方差极大、透明和半透明物体常驻、遮挡堆叠严重、类别边界模糊,还有大量低光照场景下的近色目标。前前后后改了backbone、动了head、换了损失函数、又重调了数据增强,才把一个能稳定跑在边缘盒子上的生活垃圾识别模型交付出去。这篇文章我会把整个设计过程、改哪些地方有效、哪些是无用功、训练和部署的真实参数都摊开讲,给准备做同类项目的同学一个可以直接参考的落地路径。
先说最终方案的轮廓,方便你对项目整体有个把握:以YOLOv8n为基线,用一份合并了公开数据和自采数据的生活垃圾数据集(6大类、28小类)训练,改进点集中在注意力机制、检测层输出和损失函数上,配合定制数据增强。训练完的模型在自测集上mAP@0.5约0.842,mAP@0.5:0.95约0.593,INT8量化后部署到RK3588上,单帧推理大约12ms。下面每一个结论我都会讲清楚"为什么是它"。
1. 垃圾分类识别难在哪:这不是普通的目标检测任务
我接手这个项目的第一反应是去翻COCO上的检测指标,觉得垃圾再复杂也就是80类之一。但实际把垃圾桶里的东西拆开看,任务特性和COCO类检测完全不同。
1.1 垃圾不是标准工业品
常见的目标检测训练集,比如COCO,里面的物体形态相对规整:一个杯子有杯口、杯身、把手,一辆车有清晰的轮廓边界。垃圾完全不是这样。一个被踩扁的易拉罐,在图像里可能是一个不规则的银色多边形;一个揉成团的塑料袋,轮廓线是断裂且高度自遮挡的;一个碎了一半的玻璃瓶,只有瓶颈和底部能看出结构,中间区域几乎透明,和背景混在一起。
这意味着模型不能靠"整体形状模板"去匹配目标,而必须学到局部的、纹理级的线索,比如瓶盖的螺纹、烟蒂的滤嘴颜色、纸箱的瓦楞纹理。这类特征对特征图的分辨率非常敏感,下采样次数太多,细节直接没了。
1.2 目标尺度方差极大
生活垃圾里,最小的是烟头、瓶盖、碎玻璃渣,可能只有十几个像素;最大的是纸箱、矿泉水桶、编织袋,能占满画面三分之一。一份来自实际垃圾桶场景的统计数据里,宽度小于32像素的目标能占到全部目标的21%以上。
YOLOv8默认的检测头在P3、P4、P5三个尺度输出,即下采样8、16、32倍。对一张640x640的输入,P3层特征图是80x80,一个16像素的烟头在P3上只有2个像素点,而且经过C2f和注意力模块后,这些特征会和周围背景强烈混合。后面我会展开讲,增加P2输出层和裁剪下采样路径,是这次改进里收益第二大的动作(第一大的后续会说)。
1.3 类别定义有灰色地带
这不是简单的"区分玻璃瓶和塑料瓶"的问题。生活垃圾按回收类别分时经常有歧义:一个纸杯内侧有塑料淋膜,算纸类还是塑料类?一个泡沫饭盒沾满油污,该归入"其他垃圾"还是"可回收物"?同一张图,不同标注员给出的边界框和类别都可能不一致。
我最初用公开数据集训练时,模型把"纸盒"和"纸杯"经常互相误判,后来仔细翻标注,发现公开数据里这两个类本身就有相当比例的混淆样本。这部分必须靠人工清洗规则处理,不能指望模型自己学会容忍脏数据。实际工程里,这类"边界模糊"问题比算法瓶颈更难解决。
1.4 光照和背景的干扰也比常规检测更极端
垃圾桶场景的拍摄环境很少是理想的。室外垃圾桶在太阳直射下会有强烈的明暗对比,室内垃圾房是荧光灯偏色,夜间红外摄像头下,塑料瓶、玻璃瓶和金属罐的反光特性完全变了。模型在白天数据上训练得再好,到夜视摄像头下精度直接掉十多个点。
训练阶段我把曝光增强、HSV抖动、随机光照偏移的力度加到了YOLOv8默认增强的1.8倍左右,这个改动对night-view场景的泛化帮助很大,代价是白天正常光照下mAP掉了0.5个点左右,总体可接受。
2. 数据集构建比模型设计更值得花时间
很多同学做这个题上来就调网络结构,但我们实验中近70%的效果提升来自数据侧。模型结构是上限,数据决定你能不能接近那个上限。
2.1 公开数据集的选择与组合使用
目前能拿到手的垃圾检测公开数据集主要有四个,我踩过一圈后建议这么用:
| 数据集 | 内容特点 | 标注质量 | 适合用途 |
|---|---|---|---|
| Huawei/Driverless数据集 | 40类,含大量单类单目标图像 | 中等 | 预训练、类别迁移 |
| Trash-ICRA | 室内垃圾,金属、纸张、塑料等 | 较好 | 与自采数据混合 |
| TACO | 野外场景,遮挡和多目标密集 | 较差但真实 | 验证模型鲁棒性 |
| Typical Garbage | 小规模,常见生活垃圾 | 一般 | 补充不常见类别样本 |
需要注意的是,这些数据集的类别体系互不兼容。有的把"玻璃"作为单个类,有的拆成"玻璃瓶"和"碎玻璃",合并时必须建一套自己的类别映射表。我最后定成6个一级类:可回收物、厨余垃圾、有害垃圾、其他垃圾、塑料瓶、易拉罐,其中前四类是投放端的用户语义,后两类是回收机器人抓取端要的精细类别。这样评估指标既符合垃圾分类任务的社会语义,又能对接机械臂抓取。
2.2 自采数据的标注规范
只靠公开数据训练出来的模型,在真实垃圾桶前基本不能用,因为公开数据大多是"干净的物体摆拍",而现场是"垃圾桶里的堆叠状态"。我们补采了大约4700张现场图像,覆盖三个不同场景:小区垃圾桶、校园路边垃圾桶、垃圾中转站传送带。
标注时我定了几条硬规则,这些规则直接决定了训练效率和模型上限:
- 被遮挡面积超过60%的目标不标,因为强行标注会把模型带偏;
- 类别归属有争议的目标标成二级类(大类),不做细分;
- 远小于10x10像素且无纹理特征的目标不标;
- 同一张图的所有目标必须由同一个人标完,避免不同人对遮挡的判断不一致。
2.3 类别平衡怎么做才合理
垃圾场景天然是长尾分布:塑料瓶、纸盒样本量大,灯泡、电池、药品这类有害垃圾样本很少。但有害垃圾恰恰是识别优先级最高的类别之一。我的做法不是简单复制粘贴小类样本,而是对所有框做Copy-Paste增强:把有害垃圾的目标框剪下来,随机粘贴到场景图像里,同时保证不覆盖已有小目标。这个操作让有害垃圾各类别的平均召回率提升了约11个百分点。
大类的样本量匹配如下:保证最大类样本数是最小类的8倍以内,超过这个值,模型会明显偏向多数类。操作上就是给少数类做离线增广,把它们的框数量补到目标范围内。
2.4 数据增强不是越多越好
YOLOv8自带Mosaic、MixUp、HSV、随机仿射等增强。我把实验跑了一遍之后发现,Mosaic对垃圾场景非常有效,因为它把不同来源的目标拼在一起,模拟了堆叠状态,但MixUp效果不稳定——垃圾目标边界本来就模糊,混合后会让特征更"糊",轻微有助于整体mAP,但对小目标有害。
最后保留的增强组合是:Mosaic、随机仿射、HSV变化增强、Copy-Paste、随机灰度化。关闭了MixUp和90度旋转。90度旋转对瓶子类目标太致命,竖着的瓶子旋转后变成横着,语义上已经换了一个类。
3. YOLOv8结构改进的具体方案与取舍
YOLOv8的基线设计已经很强,强改它的backbone不一定能获得正向收益,我试过换更深的轻量backbone,效果平平。真正带来提升的改动集中在三个方面。
3.1 在C2f模块后嵌入EMA注意力机制
第一个有效改动是在C2f模块的输出之后,嵌入EMA(Efficient Multi-Scale Attention)模块。EMA的特点是跨空间维度和通道维度同时建模,计算量小于SE,但对遮挡目标有帮助。我们把EMA加在Backbone的最后一层C2f之后,以及Neck的上采样融合之前,参数量只增加了约1.2M,mAP@0.5涨了1.6个点。
这个位置的选择是有讲究的。加在深层特征图上能强化全局语义,加在Neck融合前能让不同尺度的特征在进入检测头之前就完成注意力增强。如果你也想试,建议先只加这两个位置,不要每个C2f后面都塞一个注意力模块,我试过全切片加,mAP反而降了0.4个点——过拟合和计算冗余同时来了。
3.2 新增P2小目标检测层
前面说过垃圾场景小目标占比高。YOLOv8默认从P3开始检测,我想让模型直接看到高分辨率的P2层特征。做法是在SPPF之后额外引一条下采样2倍(相对原图是4倍)的分支,把C2f的浅层特征引入FPN,再融进PAN。
这个改动会在Neck里增加一个分支,训练显存占用增加约1.8GB(batch size 16,输入640)。如果你的显卡显存不够,可以先把输入分辨率降到480,或者把P2层的通道数减半。
增加P2层之后,烟头、瓶盖这类极小目标的AP@0.5从0.51提升到0.67,效果立竿见影。缺点是大目标(纸箱、编织袋)AP降了约1.1个点,原因是大目标在P2层上占据了巨大感受野,反而分散了注意力。最后折中方案是保留P2层,同时把大目标类别在损失函数里的权重调低,整体mAP还是净增长。
3.3 损失函数从CIoU换到Wise-IoU,标签分配保持不变
YOLOv8默认的框回归损失是CIoU。在垃圾分类场景里,大量目标被遮挡、边界框只能标出一部分,CIoU的宽高比惩罚项在这种标注下会放大不准确的惩罚,导致模型在遮挡样本上收敛变慢。我们换成Wise-IoU v3,它根据训练动态调整IoU损失的梯度权重,对低质量标注更宽容。实验中mAP@0.5涨了0.9个点,特别是对遮挡超过40%的目标,召回率提升明显。
标签分配没有动,还是用TaskAlignedAssigner。最初想自己写一个对遮挡更友好的分配策略,实验后效果没有显著提升,反而增加了调参成本。在目标检测改进上我学到的经验是:先动最确定能改善的因素,不要同时动多个耦合的部分。
3.4 任意朝向瓶罐的旋转框改进尝试
这是这次项目中一个比较前沿的尝试。塑料瓶、易拉罐在垃圾桶里常常是横躺斜卧的,水平框会把大量背景包进去,检测头在分类时受到背景干扰。我试过一个方案:给主干加角度预测分支,角度标签用CSL(Circular Smooth Label)做环形平滑编码。CSL的核心思想是把角度回归问题变成分类问题,并对周期边界做平滑处理。
这个改进对"瓶子"和"易拉罐"这两个类别单独评估时,AP@0.5分别提升了4.2和5.1个点,但整体mAP反而下降——因为垃圾里非刚体目标(塑料袋、碎纸)不具备稳定的角度定义,把它们硬归到旋转框体系里只会增加回归难度。最终这个分支只在推理时对瓶子类启用,其他类别走水平框路径。如果你做的是回收机器人抓取瓶罐的垂直场景,这个方向会很值得继续深挖。
4. 训练过程与关键实验复盘
4.1 算力约束下的训练配置
我手头只有一张GTX 1660 Ti,6GB显存,属于这个课题的"最低配"级别。为了把模型跑起来,我把输入分辨率从默认的640降到训练时的512,batch size设定为16,开启AMP混合精度。注意YOLOv8在AMP下有一个已知问题:前几个epoch loss会剧烈波动,解决方法是先用FP32跑10个epoch热身,再切AMP。
优化器我用了SGD,momentum=0.937,初始lr=0.01。很多教程推荐AdamW,但在检测任务上SGD的稳定性更好,尤其是训练后期mAP曲线更平滑,不容易出现突然掉点。EMA(指数移动平均)对最终的模型权重做了平滑,大约能稳定贡献0.3个点的mAP。
4.2 超参数调试顺序
这个顺序是我反复实验排出来的,按此推进能少走弯路:
- 先固定lr和batch size,扫增强强度,看哪些增强在验证集上有正向收益;
- 再固定增强,扫backbone和Neck的改进点组合;
- 然后调损失函数权重,重点看回归损失的贡献;
- 最后才是微调lr schedule、warmup epoch、anchor相关的参数。
顺序不对的典型例子:我先调了很长一段时间的anchor size,最后发现改动损失函数之后anchor分布几乎不需要动,前面白费了大量训练时间。垃圾目标的尺度范围虽然大,但YOLOv8的anchor-free机制本身对尺度适应就不差,anchor那边可调空间远小于损失函数。
4.3 评价指标的选择标准
垃圾分类系统的实际要求是"能保证不漏掉有害垃圾",所以评价指标里我优先看每个类别的Recall而不是整体mAP。mAP@0.5:0.95是学术上区分模型优劣的指标,但对实际部署意义没有mAP@0.5大,因为工程上IOU阈值通常取0.5。
我在报告里同时给出两组指标,但项目验收和部署决策只看两个值:一是全类别的平均Recall@0.5,二是有害垃圾各类别的AP。千万不要只看一个mAP就收敛,有些模型mAP高是因为多数类做得好,有害垃圾类召回率则一塌糊涂,这种模型在现实中是不可用的。最终模型的具体指标:全类别mAP@0.5=0.842,mAP@0.5:0.95=0.593,有害垃圾召回率0.81。
4.4 用特征热力图定位模型的"视觉盲区"
训练到中期我发现模型对"透明玻璃瓶"的召回率极低,怎么调都上不去。为了定位原因,我输出了一层检测头输入前的特征热力图。结果很直观:透明瓶的特征在热力图上是散的,尤其是瓶身部分被背景完全"掩盖",模型只看得到瓶口的反光。
这让我意识到:透明物体的特征主要来自边缘折射和反光,而不是灰度/颜色。于是我在数据增强里增加了"随机玻璃化"模拟操作——用高斯模糊+亮度扰动在瓶身区域制造半透明效果。这个操作做完了,玻璃瓶类的AP从0.58升到0.71,比任何结构改动都有效。这也启发了一个更通用的原则:目标检测模型对"透明材质"的理解能力天然薄弱,数据层面模拟材质特性往往比加更复杂的网络更有效。
5. 模型部署到RK3588边缘设备的完整链路
项目不能只停留在训练阶段,垃圾分类识别的落地场景在垃圾桶投放点、回收柜、移动回收车上,这些地方通常没有GPU服务器,所以部署是绕不开的一步。我最常用且最稳的组合是RK3588 + RKNN Toolkit,下面列出来的都是实际跑通过的主干路径。
5.1 从PyTorch到ONNX再到RKNN
流程说起来就三步:把训练好的权重转成ONNX,再用RKNN Toolkit转成.rknn格式,最后写推理脚本。但每一步都有"暗坑"。
转ONNX时,最容易出问题的是EMA权重和自定义的注意力模块。EMA里有跨尺度的reshape和pooling操作,某些算子onnx导出不支持。解决方法是把注意力模块重构成更"朴素"的算子组合,比如用全局平均池化加两个1x1卷积替代,效果几乎不变。
转RKNN时,最大的坑是量化。默认的全INT8量化后,我的模型mAP@0.5从0.842掉到0.79,掉了5个点。原因是垃圾图像里透明物体和低照度区域的数值范围很敏感。解决办法:
- 准备300张覆盖所有类别和光线条件的真实场景图作为量化校准集;
- 对输入做归一化时要保持和训练时一致的通道顺序和scale;
- 混合量化:把检测头的关键层保持FP16,只量化backbone和neck部分,精度损失降到1个点以内。
5.2 部署推理性能
实测设备是RK3588,开启NPU(3TOPs算力),模型输入640x640,INT8混合量化后单帧推理大约12ms,对应80+FPS。如果只用CPU跑,大约是120ms一帧,勉强够交互但不够实时。内存占用方面,RKNN模型本体约9.6MB,加上前处理和后处理缓冲,峰值内存大约180MB,在RK3588上非常宽裕。
如果目标是更低成本的设备,比如RK3566,建议把输入分辨率降到416,同时关闭P2层输出,推理可以跑到50ms左右,精度掉5到6个点。到底怎么选,取决于你的业务是识别"垃圾桶前的人"还是"机械臂前的物体"。
5.3 真实场景部署后被打脸的三个case
训练时指标漂亮,部署到现场第一个周末我就收到了三个意料之外的bad case:
第一类是双胞胎干扰。一个绿色垃圾桶旁边放着一盆绿色植物,植物的叶片在热成像和RGB下都和垃圾袋特征接近,模型把植物的一部分框成了"厨余垃圾"。这个只能在业务逻辑层加ROI限制,比如固定摄像头视角情况下排除非垃圾桶区域,模型本身解决不了。
第二类是镜头污损。垃圾房长期落灰后,镜头前面有一层半透明污渍,整个画面的纹理和颜色都偏灰,模型在低光夜晚把黑色垃圾袋误判为"其他垃圾"的概率升高。处理办法是加一个部署端的图像质量检测逻辑,检测到psnr降低或者色偏增大时直接触发清洗提醒,比训练分支靠谱得多。
第三类是训练数据里的"标签蔓延"问题。因为Copy-Paste增强用多了,模型对"粘贴痕迹"产生了依赖:当我在真实场景里把一个干净的目标放在白纸上拍,模型反而不识别了,因为它的训练数据里所有目标旁边都有其他垃圾。这类问题只能靠调整增强时的混合比例来缓解,我最后把Copy-Paste的粘贴背景从纯色改为随机噪声纹理,这个现象基本消失了。
5.4 后续可扩展的两个方向
如果是课题需要继续深入,我会往这两个方向上走:
一是增量学习。实际投放场景每周都会出现新垃圾,比如节日限定包装、新型饮料瓶。传统做法是定期合并旧数据重新训练,但代价很高。用增量学习让模型只在新数据上做参数微调,同时用经验重放保持旧类别不遗忘,是这个项目的自然延伸方向。
二是结合开放词汇检测。现在开放词汇目标检测模型已经在零样本分类上表现出很强的跨类别迁移能力,比如可以对"没见过的铝箔袋""没见过的奶茶杯"直接识别。作为一个"垃圾分类类别会持续变化"的任务,我认为下一步不是继续堆类别,而是让模型理解"这是可回收物而不是具体哪种瓶子",这会从根本上解决类别体系不断膨胀的问题。
这个项目做下来,我最大的体会是:目标检测的精度不完全是网络结构决定的,数据含义的准确界定、材质特性模拟、评估指标的选择和部署端的现实约束,往往比在结构上找一个更花哨的模块重要得多。如果你也在做类似的识别系统,建议你先花时间把类别的定义边界和现场的bad case来源搞清楚,再回头看看网络该改哪里,你会发现很多所谓"模型不行"的问题,其实并不在模型身上。