1. 从一张生活照到一袋垃圾这个项目到底在解决什么问题垃圾分类这件事说起来简单做起来是真磨人。我住的小区从几年前开始推行分类投放刚开始那阵子我站在四个桶前面愣是能把一袋垃圾拆开重新分三遍。后来虽然熟练了但每次遇到外卖盒、奶茶杯、沾了油的纸盒这类“边界模糊”的东西还是得掏出手机查半天。这种场景你一定不陌生——垃圾分类的痛点从来不是“愿不愿意分”而是“到底怎么分”。这个项目要干的事情就是让机器替我们做这个判断。具体来说是构建一套基于深度学习的生活垃圾图像识别与目标检测系统核心算法选的是YOLOv8并在此基础上做针对性改进。你给系统一张垃圾的照片它不仅能告诉你“这是可回收物还是厨余垃圾”还能在图上把每个垃圾对象框出来标上类别和置信度。这跟单纯的图像分类不一样——分类只回答“这张图是什么”目标检测要回答“图里有哪些东西、分别在哪、各是什么”。为什么这件事值得认真做因为真实场景里的垃圾照片从来不是“一物一图”的干净样本。一个垃圾桶里可能同时有塑料瓶、纸团、果皮、烟头它们互相遮挡、堆叠光照条件也乱七八糟。分类模型在这种场景下基本抓瞎只有目标检测才能把多个对象拆开处理。这也是为什么我一开始就锁定检测路线而不是走分类那条更简单的路。适合谁来参考这篇内容如果你是刚接触深度学习的学生想找一个完整、有实际意义、数据集相对好搞的项目练手这个方向非常合适如果你是从业者想了解 YOLOv8 在真实业务里怎么改、怎么调、怎么部署这里面的改进思路和踩坑记录也能直接用。我会把整个项目的设计逻辑、数据准备、模型改进、训练调参、部署落地全部拆开讲尽量做到你照着做就能跑通。2. 整体方案设计为什么是 YOLOv8而不是别的2.1 目标检测算法选型的几个现实考量做垃圾检测算法选型不是拍脑袋决定的。我前后对比过几类主流方案最后落到 YOLOv8理由很实在。先说两阶段检测器以 Faster R-CNN 为代表。这类方法精度确实稳但它先出候选框再分类推理速度慢模型也大。垃圾识别如果要落地到小区投放点、智能垃圾桶这类边缘设备上速度是硬指标——你不可能让居民站在桶前面等三秒才出结果。所以两阶段方案在我这里第一轮就被排除了。再说 SSD 和 YOLOv3/v5 这些单阶段老将。SSD 对小目标检测效果一般垃圾里的小物件烟头、瓶盖、药片包装恰恰不少这点很致命。YOLOv5 其实已经够用了社区生态也成熟但我最终选 YOLOv8主要看中三点一是它的 Anchor-Free 设计省去了手动设计锚框的麻烦对垃圾这种形状极不规则的类别更友好二是解耦头结构分类和回归分支分开收敛更稳三是工程化程度高从训练到导出 ONNX、再到部署整条链路官方支持得很顺。这里插一句很多人纠结 YOLOv8 和 YOLOv5 到底差多少。我的实测感受是在垃圾数据集这种中等规模、类别不算特别多的任务上YOLOv8n 相比 YOLOv5smAP 大概能高 2 到 4 个百分点推理速度在同一量级。差距不算天翻地覆但 YOLOv8 的训练稳定性明显更好不容易出现 loss 震荡。2.2 垃圾检测任务的特殊性决定了改进方向选完基线接下来是关键问题原版 YOLOv8 直接拿来跑垃圾检测够不够我的答案是能跑但不够好。原因有三个每一个都对应一个改进点。第一垃圾目标尺度差异极大。一个完整的纸箱和一颗纽扣电池出现在同一张图里尺度能差几十倍。原版 YOLOv8 的 PAN-FPN 结构对多尺度有一定处理能力但对这种极端尺度差异还是吃力。我的改进思路是在 Neck 部分引入更强的特征融合让浅层的高分辨率特征和深层的语义特征结合得更充分。第二垃圾图像背景极其杂乱。垃圾桶内壁、地面、其他杂物都会干扰检测。这就需要模型有更强的注意力聚焦能力。我在主干网络后面加了协调注意力机制Coordinate Attention它能把位置信息编码进通道注意力里让模型更关注“东西在哪”而不是被背景带偏。这也是热词里“yolov8 协调注意力机制”被频繁搜索的原因确实有用。第三遮挡和堆叠严重。垃圾很少单独出现基本都是挤在一起。这要求模型在 NMS非极大值抑制阶段处理得更聪明。我调整了 NMS 的 IoU 阈值并尝试了 Soft-NMS对密集堆叠场景的漏检有明显改善。2.3 整体架构一图说清文字版整个系统的数据流是这样的输入一张 RGB 垃圾图像先 resize 到 640×640送进改进后的 YOLOv8 主干网络CSPDarknet 结构提取多尺度特征Neck 部分用改进的 PAN-FPN 做特征融合中间嵌入协调注意力模块Head 部分输出三个尺度的检测结果每个位置预测边界框、类别和置信度最后经过 NMS 后处理输出最终的检测框和类别标签。训练阶段用 CIoU Loss 做边界框回归用 BCE Loss 做分类整体损失加权求和。推理阶段可以导出成 ONNX再转成适合边缘设备的格式。这套架构不算复杂但每一块都有讲究后面我会逐个拆开讲。3. 数据集准备垃圾检测项目最容易翻车的地方3.1 数据从哪来三条靠谱的获取路径做深度学习项目数据永远是第一道坎。垃圾检测的数据集我试过三条路各有优劣。第一条是公开数据集。比较常用的是 TrashNet 和 TACO。TrashNet 只有几千张图而且是单物体、白背景的“实验室风格”跟真实场景差距很大直接拿来训练效果会虚高。TACO 好一些有上万张真实场景的垃圾图标注也比较细但类别分布不均某些类别样本极少。我的做法是以 TACO 为主干TrashNet 做补充再自己拍一批真实场景图。第二条是自采数据。我拿着手机在小区垃圾桶旁边蹲了好几个下午拍了大概两千多张。别觉得这方法土自采数据的场景匹配度是公开数据集比不了的。你所在小区的光照、桶的颜色、常见垃圾类型只有自己拍才能覆盖到。拍的时候注意多角度、多光照、多距离别都是一个姿势。第三条是数据增强生成。这个不能当主力但可以补充。我用过一些基于扩散模型的图像生成做稀有类别扩充效果见仁见智生成图的质量参差不齐混进训练集要谨慎容易引入噪声。3.2 标注这件事慢就是快标注工具我用的是 LabelImg 和 X-AnyLabeling。前者经典稳定后者支持半自动标注能省不少力。标注格式直接出 YOLO 格式的 txt每行是类别id 中心x 中心y 宽 高坐标都归一化到 0 到 1。这里有几个血泪教训。第一类别定义一定要提前想清楚中途改类别是灾难。我一开始把“塑料瓶”和“塑料包装”分成两类标了两千张之后发现根本分不清又合并回去前面的标注全废了。后来我定了一套四分类体系可回收物、厨余垃圾、有害垃圾、其他垃圾下面再细分具体物品层级清晰标注时不容易犹豫。第二边界框要贴紧目标但别抠太死。垃圾边缘往往不规则框太紧会把有效特征切掉框太松又引入背景噪声。我的经验是框到目标可见轮廓外扩 2 到 3 个像素这个度需要标几百张之后才能找到手感。第三遮挡目标也要标。很多人遇到遮挡就跳过导致模型学不会处理遮挡。正确做法是只要目标有 30% 以上可见就标出来被遮挡部分按可见轮廓估计。这样模型才能学会在堆叠场景下工作。3.3 数据集划分与类别平衡标注完大概有八千多张图我按 8:1:1 划分训练集、验证集、测试集。划分时注意同一场景的连续帧不能跨集否则会出现训练集和验证集高度相似的情况验证指标虚高。类别不平衡是垃圾数据集的通病。可回收物瓶子、纸盒样本多有害垃圾电池、灯管样本少比例可能到 10:1。我的处理方式是对少数类做过采样配合在线数据增强Mosaic、MixUp、随机翻转、色彩抖动。Mosaic 增强对 YOLOv8 特别有效它把四张图拼成一张天然增加了小目标和复杂背景的出现频率。但要注意训练后期要关掉 Mosaic否则模型对真实单图的适应会变差。YOLOv8 默认在最后 10 个 epoch 关闭 Mosaic这个设置别乱改。4. 模型改进实操三个真正有效的改动点4.1 协调注意力机制的嵌入位置与代码实现协调注意力Coordinate AttentionCA的核心思想是把通道注意力分解成两个一维特征编码过程分别沿水平和垂直方向聚合信息。这样既能捕获长距离依赖又能保留精确的位置信息。对垃圾检测来说位置信息太重要了——模型得知道“这个瓶子在图的左下角”而不是只知道“图里有个瓶子”。嵌入位置我试过三个地方主干网络每个 stage 后面、Neck 的每个融合节点后、Head 之前。实测下来放在 Neck 的每个 C2f 模块后面效果最好mAP 提升约 1.8 个百分点而推理速度只降了不到 5%。放在主干里提升不明显还拖慢速度放在 Head 前提升有限。代码实现上CA 模块本身不复杂核心是两个自适应池化加一个卷积。我把它封装成一个类然后在 Neck 的配置文件里插入。这里要注意通道数匹配CA 的输出通道必须和输入一致否则后续融合会报错。我踩过一次坑池化后的卷积核大小设错了导致特征图尺寸对不上训练直接崩。4.2 Neck 特征融合的改进让浅层和深层真正对话原版 YOLOv8 的 Neck 是 PAN-FPN 结构自顶向下传语义自底向上传位置。这个结构本身没问题但融合方式比较粗暴就是简单的 concat 加卷积。我改成加权融合给不同来源的特征图分配可学习的权重让网络自己决定该信谁。具体做法是在每个 concat 操作前给两路特征各乘一个可学习的标量权重初始化为 0.5训练中自动调整。这个改动很小代码量不到二十行但效果立竿见影。在垃圾数据集上小目标烟头、瓶盖的召回率提升了约 6 个百分点。原因也好理解小目标更依赖浅层高分辨率特征加权融合让网络能动态加大浅层特征的贡献。还有一个细节是上采样方式。原版用最近邻插值我换成了转置卷积虽然参数量增加了一点但特征图质量更好边界框回归更准。这个改动对 mAP 的提升大概 0.5 个百分点属于锦上添花不是必须。4.3 损失函数与 NMS 的针对性调整损失函数方面YOLOv8 默认用 CIoU Loss 做框回归。CIoU 考虑了重叠面积、中心点距离和长宽比已经比较全面。但垃圾目标形状极不规则长宽比变化剧烈CIoU 的长宽比惩罚项有时候会过度约束。我尝试换成 EIoU Loss它把长宽比的惩罚拆开分别计算宽和高的差异对不规则形状更宽容。实测 mAP 提升约 0.7 个百分点训练也更稳。NMS 这块垃圾堆叠场景下标准 NMS 容易把挨得近的两个不同物体误删。我用了 Soft-NMS它不是直接删除高 IoU 的框而是降低其置信度。这样密集堆叠的瓶子、纸团能保留下来。代价是推理速度慢一点因为要多一轮置信度衰减。我的折中方案是IoU 阈值从默认 0.7 降到 0.6配合 Soft-NMS 的线性衰减在速度和精度之间找到平衡。5. 训练调参与环境配置参数背后的逻辑5.1 环境搭建的版本坑环境配置这块我直接说结论PyTorch 版本和 CUDA 版本必须严格匹配别想着凑合。我用的是 PyTorch 2.0.1 CUDA 11.8显卡是 GTX 1660 Ti6G 显存。这个配置跑 YOLOv8n 和 YOLOv8s 没问题跑 YOLOv8m 就有点吃力batch size 只能开到 8。安装步骤不复杂但顺序要对。先装 CUDA 和 cuDNN再装 PyTorch最后装 ultralytics 库。ultralytics 会自动拉取 YOLOv8 的预训练权重第一次运行会下载网络不好的话可以手动下载权重文件放到指定目录。有个坑要提醒别用最新版的 ultralytics。我吃过亏新版有时候改了 API网上教程对不上。建议锁定一个稳定版本比如 8.0.x 系列等社区验证充分了再升级。5.2 关键训练参数逐个拆解YOLOv8 的训练参数不少我挑几个最关键的讲每个都告诉你为什么这么设。imgsz输入图像尺寸默认 640。垃圾检测里小目标多理论上加大到 800 或 1024 能提升小目标检测但显存吃不消。我的做法是训练用 640推理时用 800 做测试时增强TTA兼顾速度和精度。batch批大小受显存限制。GTX 1660 Ti 上 YOLOv8n 能开到 32YOLOv8s 开到 16。batch 太小会导致 BN 层统计不准训练不稳定。如果显存不够用梯度累积模拟大 batch。epochs训练轮数我设了 300。垃圾数据集八千张图300 轮足够收敛。判断收敛看验证集 mAP 是否连续 20 轮不涨涨了就继续不涨就停。别死磕固定轮数。lr0初始学习率默认 0.01。我用余弦退火调度初始 0.01最终降到 0.0001。学习率太大 loss 震荡太小收敛慢。0.01 对 YOLOv8 是比较稳的起点。lrf最终学习率因子控制退火终点。设 0.01 表示最终学习率是初始的 1%。这个值别设太小否则后期学不动。momentum动量默认 0.937。这个值影响优化器对历史梯度的利用0.9 到 0.95 之间都行0.937 是官方调好的一般不用动。weight_decay权重衰减默认 0.0005。防止过拟合垃圾数据集如果自采样本少可以适当加大到 0.001。warmup_epochs预热轮数默认 3。前几轮学习率从很小慢慢升到初始值避免一开始就把预训练权重带偏。这个别关很有用。box, cls, dfl损失权重分别控制框回归、分类、分布焦点损失的占比。默认是 7.5、0.5、1.5。如果发现框不准加大 box如果类别混淆严重加大 cls。我调过一轮最终用 8.0、0.6、1.5mAP 涨了 0.4 个百分点。5.3 训练过程监控与曲线解读训练时一定要盯着几个关键曲线。损失曲线看整体趋势正常是震荡下降如果一直平着不动说明学习率太小或模型容量不够如果剧烈震荡学习率太大。mAP 曲线看验证集表现正常是上升后趋于平缓如果训练集 mAP 涨但验证集不涨就是过拟合了该加正则或减模型复杂度。YOLOv8 训练完会自动生成 results.png里面有各种曲线。我习惯用 TensorBoard 实时看更直观。另外混淆矩阵特别有用能看出哪些类别容易混。我一开始“其他垃圾”和“厨余垃圾”混淆严重看矩阵发现是湿纸巾这类东西标错了回去修了标注才好。6. 部署落地从训练服务器到边缘设备6.1 模型导出与格式转换训练完的 .pt 权重不能直接上边缘设备得先导出。YOLOv8 支持导出 ONNX、TensorRT、OpenVINO 等多种格式。我一般先导 ONNX 做中间格式再根据目标设备转。导出 ONNX 的命令很简单一行搞定。但要注意opset 版本太低不支持某些算子太高兼容性差。我一般用 opset 12 或 13比较稳。导出后一定要用 onnxruntime 跑一遍验证确认输出和 PyTorch 一致别导完就不管了。如果目标设备是 RK3588 这类带 NPU 的芯片还需要把 ONNX 转成 RKNN 格式。这个过程坑比较多量化校准是关键。我用的是混合量化对精度敏感的层保持 FP16其他层用 INT8。量化后 mAP 掉大概 1 到 2 个百分点可以接受。6.2 边缘设备推理性能实测我在 RK3588 上实测过改进后的 YOLOv8n输入 640×640单帧推理时间约 35 毫秒也就是接近 30 FPS。这个速度对于垃圾分类投放点来说完全够用居民扔垃圾的动作没那么快。功耗方面满载大概 5W 左右长期运行没问题。如果换成 GTX 1660 Ti 这种桌面显卡推理时间能压到 10 毫秒以内但功耗和成本就上去了。所以边缘部署选 RK3588 这类方案性价比是最高的。当然如果你的场景需要处理视频流帧率要求更高那就得考虑更强的算力。部署时还有个细节输入预处理要和训练时完全一致。归一化参数、resize 方式、通道顺序任何一项对不上精度都会掉。我见过有人训练用 RGB部署时忘了转结果模型完全失效。6.3 后处理与业务逻辑对接模型输出的是原始检测框要变成业务可用的结果还得做后处理。包括置信度过滤、NMS、坐标还原到原图尺寸。这些 YOLOv8 的推理代码里都有但要根据业务调整阈值。垃圾分类的业务逻辑是这样的检测出多个物体后按面积加权投票决定整袋垃圾的主类别。比如一袋垃圾里可回收物占 70% 面积就提示“可回收物”。这个逻辑简单但有效比逐个物体提示更符合用户习惯。还有一个实用功能是置信度阈值动态调整。光线好的时候阈值可以高一点减少误报光线差的时候降低阈值避免漏检。这个可以根据图像亮度自动切换实现不难但体验提升明显。7. 常见问题与排查实录7.1 训练不收敛或 loss 为 NaN这是新手最常遇到的问题。原因通常有三个学习率太大、数据标注有错、batch size 太小。排查顺序是先把学习率降到 0.001 试一轮如果还 NaN检查标注文件有没有坐标越界或类别 id 超范围最后看 batch size 是不是小于 4。我遇到过一次 NaN查了半天发现是某张图的标注框宽高为 0导致除零。这种脏数据一定要在训练前用脚本筛一遍。7.2 验证集 mAP 远低于训练集典型的过拟合。解决办法按优先级增加数据增强、加 weight_decay、减小模型规模、早停。垃圾数据集如果自采样本少过拟合很常见。我的经验是自采数据至少要到公开数据集的 30%否则场景迁移能力很差。7.3 小目标检测效果差垃圾里的小物件检测不好先看输入分辨率够不够。640 的输入下一个 20 像素的烟头 resize 后可能只剩几个像素模型根本看不见。解决办法提高输入分辨率、用 TTA、或者在数据增强里多用 Mosaic 增加小目标出现频率。7.4 推理速度不达标先确认瓶颈在哪。用 profiling 工具看是预处理慢、推理慢还是后处理慢。常见原因是预处理用了 CPU 做 resize改成 GPU 或专用库能快很多。另外模型导出时如果没做算子融合速度也会打折。问题现象可能原因排查方法解决手段loss 为 NaN学习率过大/脏数据降学习率重跑检查标注清洗数据调小 lr验证 mAP 低过拟合对比训练验证曲线加增强加正则小目标漏检分辨率不足可视化检测结果提高 imgsz用 TTA推理慢预处理瓶颈profilingGPU 预处理算子融合类别混淆标注不一致看混淆矩阵修标注调 cls 权重8. 几个我踩过的坑和私房技巧第一个坑是盲目追求大模型。我一开始用 YOLOv8m想着精度高结果训练慢、显存爆、部署难最后换回 YOLOv8s 加改进精度反而更好。模型大小要匹配任务复杂度和部署条件不是越大越好。第二个坑是忽略数据清洗。标注完直接训练结果模型学了一堆错误模式。后来我写了个脚本自动检查标注框面积过小、长宽比异常、类别 id 越界的样本人工复核一遍mAP 直接涨了 3 个百分点。数据质量比模型改进重要得多。第三个技巧是用测试时增强TTA。推理时对同一张图做翻转、多尺度结果融合mAP 能涨 1 到 2 个百分点代价是推理时间翻倍。如果业务对延迟不敏感这个技巧很划算。第四个技巧是类别权重动态调整。训练过程中监控每个类别的召回率对召回低的类别加大分类损失权重。这个可以用回调函数实现比静态权重灵活。最后说一个部署时的经验边缘设备的温度管理。RK3588 长时间满载会降频推理速度掉得厉害。加个散热片或者限制推理频率反而更稳。这个在实验室里测不出来只有实际跑起来才知道。这个项目从数据采集到部署落地我前后折腾了大概三个月。中间推翻过两次方案改过无数次参数。现在回头看最难的不是模型改进而是数据质量和场景理解。算法是工具真正决定效果的是你对垃圾分类这件事本身的理解有多深。