垃圾分类这件事,做起来比想象中复杂得多。我一直觉得回收物里的“瓶瓶罐罐”应该是最容易分的,真上手做了一个自动分类识别项目之后才意识到,玻璃瓶和陶瓷片在镜头下有多像,亚克力板和塑料盖混在一起时,连人都容易看走眼。这个项目我用的是YOLOv10n加P2检测层的组合,目标就是在低成本设备上实现可回收物的实时分类识别。整套方案跑下来,识别速度和精度都达到了可用的状态,这篇文章把我从数据准备、模型选型到训练部署的完整过程,以及踩过的几个比较深的坑,一次性整理出来,希望对正在做垃圾分类视觉方案的同行有点帮助。
这个项目适合谁参考?如果你是做边缘设备上的目标检测,或者正在搭智能回收箱、垃圾分类演示系统,再或者只是对YOLO系列模型如何加P2层感兴趣,这篇内容都值得看一看。我会把整个决策过程讲清楚,而不只是贴一段训练命令,毕竟知道为什么这么选,比会敲代码重要得多。
1. 整体设计与思路拆解
1.1 为什么选YOLOv10n而不是YOLOv8或YOLOv5
我最早其实是从YOLOv5起步的,那套生态太成熟了,网上资料一抓一大把。但真正放到垃圾分类这个场景里,有几个问题让我不得不换方向。首先是部署环境,智能回收箱里面用的基本是低功耗的嵌入式板子,算力有限,YOLOv5s跑起来虽然能到实时,但留给其他业务逻辑的算力就不够了。第二是后处理流程,YOLOv5和v8都需要非极大值抑制,这在代码层面不难写,但在设备上多一步处理,延迟就高一点,而且NMS的参数还需要单独调,偶尔还会出现同一个瓶子被两个框同时框住的情况。
YOLOv10在这个场景下的优势非常直接,它的核心卖点是"无NMS推理",训练的时候用了一致双分配策略,让模型在推理阶段可以省掉NMS这个过程,直接输出最终检测结果。我在实际测试中发现,同样的输入分辨率下,YOLOv10n的推理延迟比YOLOv8n低了大概20%,这个差距在边缘设备上很可观。
另一个让我坚定的理由是YOLOv10的端到端特性。以前做嵌入式部署,导出到TensorRT或者ONNX之后,为了把NMS算子塞进去,经常要折腾半天。YOLOv10直接省掉这一步,导出模型就简单很多,我后面甚至会专门讲这个过程。
选nano版本也是经过考量的。很多人有个误区,觉得模型越大精度越高,就无脑往上冲。但可回收物分类这个任务,类别之间的差异总体来说是比较明显的,瓶子的形状、玻璃的质感、纸箱的纹理,这些特征用一个小模型其实就能学到。我用YOLOv10n跑出来的效果,和v10s在主力类别上的mAP差距只有1.5个点,但参数量从4.5M降到2.3M,计算量直接从8.2 GFLOPs降到大约6.7 GFLOPs,这个交易我认为非常划算。
1.2 P2检测层到底解决了什么问题
P2检测层是这次项目里我觉得最关键的一个设计。标准YOLOv10的检测头从P3到P5,对应下采样8倍、16倍、32倍的特征图。听起来好像覆盖得挺全的,但垃圾分类场景里有很多目标物就是很小,比如一袋垃圾里的塑料瓶盖、单独的烟蒂、碎玻璃渣,这些目标在原图上可能就二三十个像素,经过8倍下采样之后只剩3到4个像素,特征基本就丢失了。
P2层就是把高分辨率的特征图也接入检测头,对应原图的4倍下采样。这样一来,小目标在特征图上的尺寸翻了一倍,模型能捕捉到的细节多得多。我用同一个数据集在YOLOv10n和YOLOv10n-P2之间做过对比测试,加P2之后,小目标类别的mAP50提升了接近4个百分点,这个幅度在小模型上是非常可观的。
当然天下没有免费的午餐。P2检测头意味着模型要多处理一层高分辨率特征图,计算量会增加一部分,推理延迟也会略微上升。我实测下来,YOLOv10n加P2之后,参数量从2.3M涨到大概3.1M,GPU上的推理时间增加了大约15%,CPU上增加得更明显。所以P2不是盲目加的,如果你的场景目标物都比较大,加P2反而画蛇添足。但可回收物检测这种对细粒度要求高的场景,我认为这一层是必须的。
1.3 类别的设定与识别策略
可回收物的分类标准各地有差异,我没有也没有权限直接去对应某地的具体政策,而是从通用性的角度出发,设定了几个最常见的类别:塑料瓶、玻璃瓶、易拉罐(铝制品)、纸箱/纸壳、还有纺织品。另外我还加了一个"其他可回收物"作为兜底,避免模型遇到没见过的东西时强制做错误分类。
在识别策略上,有一个细节值得分享。刚开始我用的是单一模型识别所有类别,后来发现玻璃瓶和塑料瓶在视觉上确实不好区分——透光性接近,颜色也接近。我试着把玻璃瓶单独拆出来,用一个小分类器做二次确认,效果好了很多。这其实就是目标检测里常用的"粗分类加细分类"的级联思路,虽然增加了一步流程,但对生产环境的精度提升很有帮助。不过为了保持方案的可复现性和简洁性,最终交付的版本还是单模型多类别识别,级联方案作为一个优化分支记录在文档里。
2. 数据集准备与增强策略
2.1 数据采集与标注的经验
做目标检测项目,数据永远是第一位的。我在网上找了一些垃圾图像的数据集,也自己拍摄补充了很多在回收站常见的真实场景图片。这里有一个经验就是,网上的数据集虽然标注规范,但背景太干净了,都是纯色桌面上的物体特写。现实场景里,垃圾桶旁边可能有纸屑杂物,光线忽明忽暗,甚至有人直接把袋子扔下来把瓶子压得变形。如果你只用公开数据集训练,模型在其他环境里表现会明显下降。
我自己补充数据时,专门跑到回收点拍了三个不同时段的照片:上午顺光、中午强光、傍晚暗光。手机拍摄,分辨率不用太高,但构图一定要贴近真实使用场景。标注的时候用了一种很朴素的策略,把所有塑料瓶统一标注为plastic_bottle,不区分透明和不透明,让模型自己去学这两种形态的共同特征。玻璃瓶同样处理,标注为glass_bottle,易拉罐统一为aluminum_can,纸类为cardboard,纺织物为textile,还有一类others作为兜底。
标注工具用的就是常见的开源标注软件,导出格式直接选YOLO的txt格式,每个图片对应一个txt文件,里面每行是"类别id x_center y_center width height",坐标全部归一化到0到1之间。这里要特别强调,连续标注两三个小时后必须休息一下,疲劳状态下画的目标框位置会明显偏移,后面处理起来非常麻烦。
2.2 易混淆类别的处理技巧
垃圾分类识别最难的不是"认出这是瓶子",而是"分辨这是玻璃还是塑料"。两者在图像上的区别确实微小,尤其是透明玻璃瓶和透明PET塑料瓶,肉眼看过去都是细长条状,半透明,高光位置也接近。我的处理办法是尽量在标注时把这类易混淆样本单独筛出来,配合P2层的高分辨率特征,让模型有更多的像素纹理信息可以依据——玻璃瓶的瓶口形态和塑料瓶有明显差异,瓶底凹槽和壁厚的边缘特征也不同。
我还在数据增强环节专门加入了针对性的选项。光照变化模拟用的是HSV增强,色相偏移设置得非常小,亮度和饱和度做随机扰动;模糊增强适量加了一些,模拟运动中的抓拍效果。这里有一点要注意,类别不平衡的问题在垃圾数据里很常见,塑料瓶的样本可能占了40%,纺织品只有5%。如果不处理,模型会对样本多的类别过拟合。我用最简单的加权采样策略,让每个类别的样本在每次epoch中出现的概率尽量均匀,效果很不错。
2.3 数据划分与验证集设计
数据划分这部分,我的做法是70%训练、20%验证、10%测试,同时保证划分后的数据分布一致。比如玻璃瓶样本不能全落在训练集里,验证集和测试集里也必须有,这样才能测出模型的真实泛化能力。
另外,我专门留了一组"零样本测试集",就是在网上找一些训练时完全没有见过的垃圾图片,模拟冷启动场景。这个测试集不进训练流程,只用来做最终效果验收。事实证明这一步非常必要,模型在验证集上mAP50能到90,但零样本测试集上只有78,差距很大,这才暴露了过拟合问题。
精调之后,通过增加数据增强强度和降低模型复杂度,我成功把零样本测试集上的性能提升到了85以上。这个数字我认为是判断模型能不能真正上线的关键指标,而不是只看验证集的漂亮分数。
3. 模型训练与核心配置
3.1 训练环境与超参数选择
训练我用了一台带有一张中等规格GPU的机器。很多人关心显存够不够,其实YOLOv10n参数量很小,在batch size=16、输入分辨率640x640的情况下,显存占用也就6GB左右,门槛不高。
超参数里面最值得讨论的是输入分辨率和epoch数。我试过480、640、800三档分辨率,480下面是速度很快,但小目标类别的表现明显下降,玻璃瓶这类中等尺寸目标倒还好,但瓶盖和小碎片直接丢失。640是平衡点,识别速度和精度都让人满意。800分辨率确实让mAP又涨了1.5个点,但推理耗时增加了接近50%,部署到嵌入式设备上实在扛不住,最终选了640。
epoch数我设置了300轮,配合早停机制。YOLOv10官方预训练权重是在COCO数据集上训练的,虽然分类类别不一样,但底层特征提取能力是共通的,所以我没有从零开始训练,而是基于预训练权重做迁移学习。迁移学习有个经验,初始学习率不要太高,不然容易把预训练学好的底层特征破坏掉。我用的是初始学习率0.01,带动量0.937,权重衰减0.0005,训练过程跑得比较稳定。
3.2 训练过程中的关键观察
训练过程中我习惯盯着几个关键指标看:box_loss、cls_loss,以及验证集的mAP50和mAP50-95。刚开始的几十个epoch,损失下降很快,mAP也一路涨,这是正常现象。但我发现大概在第180轮开始,验证集mAP就基本不再上升了,训练损失还在缓慢下降——这就是典型过拟合信号。
此时早停机制还没触发,因为我设的耐心值是50轮,实际上我选择手动在第220轮左右停掉了训练。在P2模型上,我观察到过拟合的时间点比标准模型来得更早一些,因为P2层的参数量变大,对训练数据的拟合能力更强,这其实需要配合更强的数据增强一起用。
最终保存的最优权重是验证集上表现最好的一轮,而不是最后一轮。这是一个很简单但经常被忽略的操作,因为后面的训练可能已经在过拟合边缘徘徊了。
3.3 与标准YOLOv10n的对比实验结果
我在同等条件下训练了两个模型,一个标准YOLOv10n,一个添加P2检测头的YOLOv10n-P2,其他配置完全一致,方便做对照。
| 模型 | 参数量 | GFLOPs | mAP50 | mAP50-95 | 小目标mAP50 |
|---|---|---|---|---|---|
| YOLOv10n | 2.3M | 6.7 | 88.2 | 63.5 | 61.3 |
| YOLOv10n-P2 | 3.1M | 8.9 | 90.1 | 66.8 | 65.2 |
表格里的小目标mAP50是单独统计瓶盖、碎片这类小尺寸物体算出来的指标。可以清晰看到,P2层带来最大的收益就在小目标这一项上,提升了接近4个点。总mAP50的提升是1.9个点,看起来不算大,但考虑到这已经是90左右的水平,每一分的提升都来之不易。
速度方面,GPU推理时间从5.6ms增加到6.5ms,这个代价换4个点的小目标精度,我认为值得。
4. 模型导出与部署实践
4.1 从训练权重到ONNX导出的避坑记录
训练完成后,部署的第一步是把PyTorch权重导出为ONNX格式,这一步理论上很简单,但我在实际操作中踩了一个不小的坑。因为我用的是带P2结构的自定义配置,导出时如果参数设置不对,会报错提示检测头维度匹配不上,后来排查发现是导出时的opset版本太低导致的。
我最终选择的opset是12以上,同时把dynamic输入打开,让模型可以接受动态尺寸的输入。这里有一个权衡要讲清楚,动态输入意味着部署端可以传入任意分辨率的图片,灵活性高,但推理速度会比固定尺寸略慢。我建议部署时先用动态尺寸导出,验证效果后再固定到640x640,这样性能最优。
导出命令的要点就是输入节点的命名要固定,后面在推理代码里要用同一个名字来填数据。如果这一步搞错了,部署端的tensor维度就会完全对不上,你会看到一片乱码的输出。
4.2 边缘设备上的推理优化
在CPU设备上做推理优化时,YOLOv10无NMS的特性帮了大忙。标准YOLO在ONNX Runtime下除了跑主干网络之外,还要额外处理NMS节点,这部分在CPU上很耗时,经常要占整个推理时间的30%。YOLOv10直接输出解码后的结果,虽然官方说不需要NMS,但为了保险起见我仍然在部署端保留了一个简单的置信度过滤和重叠框合并逻辑,只是这个逻辑比传统NMS简单得多,计算量可以忽略不计。
我还测试了OpenVINO、TensorRT等多种推理后端。TensorRT的加速效果确实最好,但转换过程比较繁琐,需要校准数据;OpenVINO则胜在部署简单,只需要把ONNX文件直接导入,不需要额外校准。如果你的目标设备是Intel平台,我推荐直接用OpenVINO;如果是ARM架构的板子,ONNX Runtime的CPU版本加上合理线程数配置就能有不错的表现。
线程数这个参数很多人不重视,默认值在边缘设备上往往不是最优的。我实测单核和多核的效果差异很显著,多核模式下延迟降到单核的60%左右,但功耗也会上升,需要根据设备散热条件取舍。
4.3 整个识别流程的串接
部署端的整个识别流程我分成三步:图像预处理、模型推理、后处理。预处理这一步需要保持和训练时一致的预处理方式,我用的是Letterbox加灰度填充,将原始图像等比缩放到640x640,多余部分用灰色填充。这里有个容易被忽视的点,就是填充颜色必须和训练时一致。我见过有人训练时用白色填充,推理时用黑色填充,导致检测精度骤降。
推理输出是一个维度为1x84x8400的tensor,其中84是4个框坐标加80个COCO类别的概率,8400是预设锚框的数量。因为我的自定义模型类别数是6,实际输出维度会变成1x10x8400,后面处理时要注意调整。解码之后,对置信度低于0.25的框直接删除,然后按类别聚合处理,输出最终结果。
这一步我建议加一个简单的消抖逻辑。因为摄像头在连续帧中检测同一个瓶子时,框的位置会轻微抖动,输出给机械臂或者舵机的抓取坐标就会不稳定。我对检测框的坐标做滑动平均,连续3帧的坐标取平均输出,抓取准确率明显提升。
5. 识别效果与性能分析
5.1 各类别的识别表现差异
模型训练完之后,我统计了每个类别的单独表现,这比看整体的mAP要直观得多。塑料瓶和易拉罐这两个类别效果最好,准确率都在93%以上,这是因为这两个类别样本量大、形态也比较统一,塑料瓶有瓶口、瓶身、瓶底的固定结构,易拉罐有拉环和金属反光的独特特征,模型学起来很轻松。
纸箱和纺织物属于中等难度,纸箱的麻烦在于形态多变,压扁的和立体的完全是两种样子,模型偶尔会把压扁的纸箱和废纸混在一起。纺织物的难点在纹理丰富程度,毛衣和T恤的纹理差异很大,模型对光滑面料和粗糙面料的响应不一样,我补充了很多不同材质织物样本后,准确率才逐渐上来。
最难的还是玻璃瓶,尤其是透明玻璃瓶在灰色背景下的表现不太稳定。我检查了一些失败样本,发现模型会把带有绿色调的玻璃瓶误判为塑料瓶,问题出在训练数据里绿色塑料瓶样本太多,模型形成了"绿色=塑料"的错误关联。这个问题的解决方法是往玻璃类别里补充带有淡绿色调的样本,同时增加绿色塑料瓶的标注准确性,双管齐下才把混淆率压下去。
5.2 光照与拍摄角度的影响测试
部署到实际场景之前,我在一个模拟回收装置的环境里做了专门的压力测试。测试环境的光照用的是常见白光灯,角度从正面照和侧上方照两种,我发现在侧上方光照下,所有类别的识别准确率都有所下降,约2到3个点。原因在于强光在玻璃瓶表面形成大面积高光区域,把瓶子本身的纹理信息遮盖掉了。
应对策略是从算法层面和数据层面同时下手。数据层面我在训练数据增强中增加了随机高光模拟,让模型适应局部过曝;算法层面我调整了预处理时的曝光补偿,在推理前先做一次直方图均衡化。这两步叠加后来看,侧光条件下的准确率下降从3个点控制到了1个点以内。
5.3 吞吐量与实时性评估
对部署在回收装备上的系统来说,实时性是最硬的指标。我的设备上最终实现的单帧推理时间是145毫秒,加上预处理和后处理,整体单帧流程在180毫秒左右,换算下来每秒处理5.5帧左右。对一个需要抓取物品的机械臂来说,这个速度已经够用了。
在连续运行测试中,模型在1600多帧图像上的平均检测置信度稳定在0.72附近,没有出现大幅波动,内存占用也保持稳定,长时间运行没有明显泄漏。CPU温度在长时间满负荷运行后处于可接受范围,不需要额外加散热器就能稳住。我觉得这个方案在算力要求上,已经比动辄需要高性能独立显卡的方案实用得多,普通性能级别的嵌入式设备就能带起来。
6. 常见问题与排查技巧实录
6.1 训练阶段的典型报错与解法
| 问题现象 | 原因分析 | 解决方案 |
|---|---|---|
| 训练开始后Loss一直是NaN | 学习率设置过高导致梯度爆炸 | 调低初始学习率,加上梯度裁剪 |
| 验证阶段没有检测框输出 | 导出或加载配置时类别数设置错误 | 检查数据配置文件的类别数与模型头维度一致 |
| 小目标类别mAP始终不过60 | 输入分辨率太低或缺少P2层 | 提升输入到640,启用P2检测头 |
| 推理时输出维度错乱 | ONNX导出时opset版本过低或动态轴未正确设置 | 使用opset 12以上,配合Nchw输入格式 |
| CPU推理速度远低于预期 | 线程数配置不当或模型长时间没有做算子融合 | 手动设置线程数,使用OpenVINO执行推理 |
这些坑里面,我最想强调的还是配置文件的类别数问题。项目中途改过一次类别,把5类变成6类,但是训练脚本里有个旧缓存没有清掉,导致模型一直按5类来创建检测头,训练出来的模型输出维度不匹配,部署时怎么都跑不通。清理缓存文件之后重新训练,问题立刻解决。
6.2 数据不平衡导致的偏向问题
垃圾分类的数据天生是不平衡的,塑料瓶太多,纺织品太少。如果直接把原始数据丢给模型训练,模型对纺织品的召回率会非常低,因为它在训练过程中见到纺织品的次数太少了。
我开始也经历过这个问题,验证集上塑料瓶的准确率已经到95%了,纺织品只有68%。我采取的措施是通用的过采样方案,把纺织品样本做复制和增强后再放入训练集,同时降低了塑料瓶样本的采样权重。这样一来,模型不会对塑料瓶过度偏向,整体的类别均衡性有所改善。
需要提醒的是,过采样要适度,同一样本重复太多次会造成对特定图像的过拟合。我的经验是重复次数不要超过3次,配合其他增强方式一起用,效果更可控。
6.3 部署环境的意外问题
在方案交付前,我还在两套不同配置的设备上做过迁移部署测试,发现同一个ONNX文件在不同设备上的推理速度差异能达到3倍。原因不只是硬件算力不同,还包括内存带宽和缓存大小的差异。这件事给我最大的教训是:性能评估一定要在目标硬件上做,不要在开发机上跑完就给出结论。
另外还有一个很细节的问题,摄像头的自动白平衡会影响输入的色偏。回收箱里既有自然光又有灯光,摄像头会在不同色温之间自动调整,导致同一种瓶子在不同时刻拍出来的颜色不一样。处理方式是固定摄像头的自动参数,同时保证曝光时间和增益不要自动变化,尽量让输入图像的色彩稳定。这虽然是一个图像采集端的调整,但对识别精度的影响非常大。
7. 一些心得与后续改进方向
这个项目让我最深刻的体会是:用YOLOv10n这样的轻量级模型,加上P2检测层来提升小目标能力,确实是一条很适合垃圾分类场景的技术路线。它兼顾了精度和速度,让部署到中低端设备成为可能,同时YOLOv10端到端免NMS的特性又让部署环节省了很多事情。
如果后续要继续迭代,我第一个想做的事情是尝试更精准的类别粒度,比如把现在的塑料瓶拆分成透明塑料瓶和不透明塑料瓶,把纸类再分成瓦楞纸和办公纸,细分之后对下游的回收价值评估会更有意义。第二个方向是把检测和分类解耦,用一个检测模型定位所有可疑垃圾区域,再针对每个区域使用更高精度的分类模型来识别具体类别,这样可以把玻璃和塑料的混淆问题再压一压。还有一个可以探索的方向是加入深度信息,如果用双摄或者深度传感器辅助检测,瓶子的立体形状会让分类准确率进一步提升,不过这个对硬件成本的影响也需要评估。