拓十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

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

基于YOLOv8改进的垃圾分类识别系统设计与部署

基于YOLOv8改进的垃圾分类识别系统设计与部署

做过垃圾分类识别项目的人应该都有体会:真正难的往往不是把模型跑通,而是让它在小区楼下那种光照乱、遮挡多、垃圾袋五花八门的环境里还能稳定工作。这篇文章记录的是一个基于深度学习目标检测的垃圾图像识别项目,核心模型选了 YOLOv8,并且在原版基础上做了几处针对性改进。内容会覆盖数据集怎么搭、YOLOv8 的改进点怎么选、训练参数到底是什么意思、模型怎么落地成一套能用的识别服务。适合正在做相关课题、参加竞赛、或者想把垃圾分类功能集成到智能硬件里的朋友参考,我会尽量把“为什么这么做”也讲明白。

1. 项目整体设计与需求拆解

1.1 先想清楚:你要做的是“识别”还是“交互”

垃圾分类识别这种需求,不同场景的差别可以非常大。我这次定位的是智能回收箱/智能垃圾桶:摄像头对着投放口或者桶内,识别人投放的垃圾属于可回收、厨余、有害还是其他,然后联动对应箱门打开,或者通过屏幕语音提示用户“请投放至对应箱门”。这个场景有几个特点。

第一,强调实时性。用户手举着垃圾站在桶前面,识别不能让人等 2 秒,模型单帧推理时间要控制在 50ms 内比较稳妥,设备上屏幕才会有“即投即识别”的感觉。

第二,环境不可控。小区垃圾投放点白天强光、晚上偏暗,有些地方摄像头正对投放口,有些是斜视角度,垃圾袋还可能部分遮住里面的物体。这些扰动必须在数据层面就模拟进去,模型才能扛得住。

第三,分类体系不能拍脑袋。我们用的是国内通用的“四分类”标准:可回收物、厨余垃圾、有害垃圾、其他垃圾。这里要特别注意可回收里的金属罐和有害电池容易混,厨余里面的果皮和餐巾纸也经常被一起丢,类别边界必须写死,不然标注员和模型都会迷糊。

1.2 为什么选 YOLOv8 而不是其他检测框架

现在目标检测算法选择不少,Faster R-CNN 也有、YOLOv5 也有人用,这两年 Transformer 检测器和开放词汇检测也火。但在这个项目里,我把最终方案锁在 YOLOv8 上,理由很现实。

一是实时性和精度的平衡。Faster R-CNN 这类两阶段检测器精度确实能打,但部署到边缘设备做实时推理太吃力;YOLOv8 是单阶段的,起步就能跑到几十帧,配上端侧 NPU 能直接起飞。

二是生态成熟。Ultralytics 官方库把训练、验证、导出 ONNX/NCNN/TensorRT 的链路做得非常顺,社区资料多,跑 YOLOv8 训练自己的数据集这种需求网上有大量参考,遇到问题排查成本低。

三是改进空间明确。YOLOv8 的 C2f 结构、解耦头、无锚框设计都留有很清晰的优化入口,很适合作为“研究的设计与实现”这类项目的基线模型。

给个直观对比,我当时做的候选方案评估:

方案mAP50 基线(自建垃圾数据集)单帧推理耗时(边缘设备)结论
YOLOv5s87.2%38ms备用方案
YOLOv8s89.1%40ms最终基线
Faster R-CNN ResNet5090.3%210ms放弃实时
RT-DETR88.6%52ms备选

表格里的数据是内部测试记录,不同数据集会浮动,但趋势很清楚:YOLOv8s 在当前需求和算力条件下综合最划算。

1.3 系统架构分几层

整套系统我按四层拆解:数据层、模型层、服务层、应用层。

  • 数据层负责采集图片、清洗、标注、增强,最终产出训练集和验证集;
  • 模型层就是 YOLOv8 的训练、改进选型、评估和导出;
  • 服务层把模型封装成 HTTP 接口或嵌入式推理程序;
  • 应用层是前端 UI、语音提示、闸机电机联动等。

我把“能跑通模型”和“能用起来”当成两个验收节点,前一个节点验证精度,后一个节点验证整体可用性。项目初期就定了这个拆法,后面每阶段的目标都很清楚。

2. 数据集构建:模型上线表现的一半取决于这里

2.1 数据从哪来:公开数据 + 自采

垃圾识别这类项目最尴尬的就是缺少高质量开源数据集。我调研过几个公开数据集,比如华为云垃圾分类数据集、TrashNet、还有一些 GitHub 上的垃圾图片项目。这些数据集量大但有两个明显问题:类别体系非常不统一,有的数据集把“塑料瓶”当成单一类别,有的又把“可回收”作为一个大类,直接混用会导致标签语义错乱。

我的做法是以自采数据为主、公开数据为辅。用手机和现场摄像头拍了一周的真实投放场景,覆盖几个不同小区的垃圾房、智能箱试点点位。同时利用公开数据集里图像质量好的单类图片,通过裁剪、抠图、贴入真实背景的方式扩充样本,相当于人工造了一批“合理场景”。最终整理出 1.6 万张有效图片,其中自采约 1.1 万张,公开数据改造约 0.5 万张。

2.2 类别体系的坑:别把“物”和“类”混为一谈

四分类标准下,模型到底输出什么类别?很多人第一反应是输出“可回收垃圾、厨余垃圾”这四个大类,这其实是个大坑。

我们用深度学习的逻辑想一下:模型是通过图像特征识别具体物体的,塑料瓶和铁皮罐虽然都被归为可回收,但它们的形状、纹理、反光度完全不同,如果把这两种差异巨大的物体硬绑在一个类里,模型内部特征空间会非常混乱。反过来,四大类之外的东西,比如牙刷、鞋、纸尿裤,到底算什么?

我实际采用的方案是“底层的具体物品类别 + 上层的四分类映射”。模型先识别塑料瓶、易拉罐、硬纸盒、玻璃瓶、果皮、菜叶、剩饭、废电池、药瓶、烟蒂、纸巾等 20 类具体物品;到了应用层,再根据映射规则把识别结果归到可回收、厨余、有害、其他四类。这样模型在每个类别上的特征更集中,准确率明显更好。

2.3 标注规范:一件事定下整个项目上限

标注质量直接影响检测效果。我踩过一次标完 3000 张图才发现边界框偏大,导致模型定位偏移、mAP 一直卡着不动的教训。这个项目的标注规则定得很死:

  • 边界框要贴合物体的实际可见区域,不包含明显背景;
  • 严重遮挡、模糊到认不出的物体不标注,不硬标;
  • 同一物体出现在画面里多个角度时每个有效实例都要标;
  • 标完一轮后必须有人抽检,抽检率不低于 15%。

工具用的是 LabelImg 和 X-AnyLabeling。X-AnyLabeling 带自动化预标注能力,先让现有模型预标注,人工再改一遍,标注效率能提升两倍以上。队里两个标注同学一天能完成约 500 张图的精标,整体进度可控。

2.4 数据增强:不只是扩充数量,是模拟真实世界

真实投放场景的光线和角度比多数人想象得复杂。所以我在训练时用了几组针对性增强:

  • Mosaic 四图拼接:把 4 张图随机裁剪拼接再缩放,相当于用小显存合成丰富背景,对小物体尤其友好;
  • HSV 调整:模拟早中晚不同色温、灯光颜色变化;
  • 随机翻转、旋转、仿射:模拟相机安装角度偏差;
  • 一定概率加入 8% 的高斯噪声:模拟低照度摄像头画面。

这里有个 trick:训练最后 10 个 epoch 关掉 Mosaic。这是我实际体会很深的一点,Mosaic 拼出来的图风格和真实场景差异不小,一直开着会导致最后阶段模型在真实数据上的分布适应不足,损失曲线能看出来波动变大但 mAP 不见涨。

2.5 类别不均衡:少数类要“手工扶一把”

垃圾数据天然失衡,瓶子和纸盒特别多,废电池、药瓶、烟蒂特别少。我用三类方法处理:

  • 对少数类过采样:复制样本并做轻微增强变换;
  • 对多数类降采样:同类图片在数据加载时按概率跳过,避免一个 batch 全是瓶子;
  • 给少数类设定更高的 Loss 权重:在 YOLOv8 的损失计算环节按类别乘系数,比如废电池、烟蒂权重设置为 1.5 左右。

个人体感:类别不均衡问题里,数据过采样是最立竿见影的,模型对少数类的召回率能提升 5 到 10 个百分点,而单纯调 Loss 权重容易出现边界框回归不稳定。

3. YOLOv8 网络结构拆解与三项改进实践

3.1 先看懂 YOLOv8 网络结构图再谈改进

YOLOv8 可以按三段理解。Backbone 部分保留 Darknet 风格,核心是 CSPDarknet + C2f 模块,C2f 的“多分支梯度流”设计让梯度传输更丰富;末尾接 SPPF,把不同尺度的特征池化并拼接,扩大感受野。Neck 部分是 PAN-FPN,自顶向下把高层语义传给底层,再自底向上回传定位细节,把多尺度特征充分融合。Head 部分从 YOLOv5 的耦合头改成了 anchor-free 解耦头,分类分支和回归分支分离,不再需要预设锚框,推理速度更快,收敛也更稳。

看结构图时重点盯三个接口位置:Backbone 输出三个尺度特征图的位置、Neck 中上下采样连接的节点、Head 分类尺度数量的定义。改进方案几乎都落在这三个位置。

3.2 改进点一:注意力机制用在最该用的地方

我们测试过 SE、CBAM、协调注意力(CoordAtt)几种注意力机制。很多网友关心的“yolov8 协调注意力机制”就是 CoordAtt,它比 SE 多了位置信息的编码,对定位遮挡目标和细微纹理差异更有帮助。

最后方案是把 CoordAtt 插在 Backbone 的最后一层输出后面,以及在 Neck 上采样前后的 C2f 模块里各加一组 SE。为什么要这样分布?因为 Backbone 末端通道数多、语义强,让注意力去强调“垃圾主体”特征;Neck 融合过程中加入 SE,是为了让不同层级的特征融合时互相校准权重。实测这类组合比只在 Backbone 加一个注意力机制效果更稳定。

需要强调,加注意力机制不是越多越好。有次我在每个 C2f 后面都塞了一个注意力模块,模型直接过拟合,mAP 反而掉了 2 个点。注意力是“重排优先级”,不是“放大信息量”,通道数一旦被注意力模块反复压,信息冗余就没了,小目标反而丢。

3.3 改进点二:针对小目标的检测头策略

生活垃圾里小目标非常多:烟蒂、药瓶、废电池都是尺寸很小的物体。原版 YOLOv8 默认三个检测尺度,对小目标偏弱,我把策略分成两档:

  • 方案 A:给 Neck 增加一个 P2(更大分辨率)检测层,相当于把检测头从三个变成四个,让模型在小物体上的定位更细;
  • 方案 B:不动检测头,只在训练时启用辅助损失分支,把深层的大感受野特征用于辅助监督浅层,不增加部署时推理成本。

考虑到最终要部署到边缘设备,我选了方案 B 为主。效果上,mAP50-95 的提升有 2.1 个百分点,推理耗时只增加了不到 2ms。这个方案的思路在很多 YOLOv8 head 改进讨论里能看到变体,本质就是“训练时多算一点,部署时不变”。

3.4 改进点三:轻量化换高性价比

搜“yolov8 改进”的时候经常看到一堆花哨模块,但真正到项目里,我会优先选带轻量化属性的。测试下来,GSConv 替换 C2f 内部部分常规卷积是性价比很高的方案,参数量降约 18%,推理速度提升约 10%,mAP 基本不降。如果全面替换,端侧 FPS 会有明显改善,但分类精度会有一点损失,需要权衡。

这个环节的核心观点是:改进必须做成消融实验,并且以“速度-精度”双维度评价。过了这一关的模块才保留,不过关一律回滚。没有人愿意为了点赞数牺牲项目上线体验。

4. 训练环境、参数含义与损失曲线解读

4.1 环境配置与硬件选型

训练环境用 PyTorch + Ultralytics。版本锁死很重要,我这里用的是 Python 3.9、PyTorch 1.13.1、CUDA 11.7、Ultralytics 8.0.x。不同版本间的部分操作符会影响导出结果,锁版本能减少无谓的 workaround。

训练硬件:一台带 RTX 3090 的服务器承担本轮训练,另外也用 GTX 1660Ti 的笔记本跑小验证。1660Ti 6GB 显存实测可以跑 YOLOv8s,batch 设 8 左右,imgsz 640,虽然慢一点但完全能完成微调任务。如果你只有 1660Ti 这类卡,建议直接下载预训练权重做迁移学习,别从零训练,否则一个 epoch 等半小时。

4.2 训练参数逐项说明

YOLOv8 训练命令很多参数,很多新手是照着默认值跑,跑完也不知道调什么。我整理一套实际项目里常用的关键参数:

  • model:指定模型结构或预训练权重路径,用yolov8s.pt属于迁移学习,用yolov8s.yaml是从零训练,前者收敛快得多;
  • data:数据集 yaml 路径,里面写 train/val 的图片路径和类别字典;
  • epochs:训练轮数,垃圾数据集 100 轮左右收敛,小数据集设成 300 但配合早停;
  • batch:一次迭代的图片数,6GB 显存建议 8~16,显存不够就只能小 batch;
  • imgsz:输入分辨率,通常 640,小目标多可以试 960,但耗时和显存都会涨;
  • optimizer:默认 SGD,也可以选 AdamW。用 AdamW 时学习率建议调低到 0.001 附近;
  • lr0:初始学习率,默认 0.01,小数据集建议 0.005,太大容易梯度爆炸;
  • lrf:最终学习率占初始学习率的比例,默认 0.01,支持余弦退火时用;
  • momentum:SGD 动量,默认 0.937,一般不改;
  • weight_decay:权重衰减,防过拟合,默认 0.0005;
  • cos_lr:是否用余弦退火学习率策略,是就 True;
  • amp:混合精度训练,会自动减少显存占用,6GB 卡应该开着;
  • cache:是否把数据缓存到内存,默认 False,内存足够时开 True 能提升读取速度;
  • patience:早停,连续多少个 epoch 验证指标没提升就停,推荐 20~30;
  • close_mosaic:训练后期关闭 Mosaic 增强的轮数,默认 10,这是 YOLOv8 默认就有的优化策略;
  • device:显卡编号,0 表示第一张卡,多卡用 0,1;
  • plots:训练结束后生成 loss、PR、混淆矩阵等可视化图,默认 True。

我实际项目里的一条训练指令是这样:

yolo detect train model=yolov8s.pt data=trash.yaml \ epochs=120 batch=16 imgsz=640 optimizer=SGD \ lr0=0.005 lrf=0.01 cos_lr=True \ patience=20 amp=True cache=True \ project=trash_train name=yolov8s_coordatt

4.3 损失函数:看曲线判断什么时候能停

训练结束后,Ultralytics 会产出results.csv,里面有每轮 train_loss、val_loss、precision、mAP 等数据。很多人以为 YOLOv8 没有现成的 loss 曲线图,其实直接用 CSV 画就行。

import pandas as pd import matplotlib.pyplot as plt df = pd.read_csv("results.csv") plt.figure(figsize=(10, 5)) plt.plot(df["epoch"], df["train/box_loss"], label="train/box_loss") plt.plot(df["epoch"], df["train/cls_loss"], label="train/cls_loss") plt.plot(df["epoch"], df["val/box_loss"], label="val/box_loss") plt.plot(df["epoch"], df["val/cls_loss"], label="val/cls_loss") plt.xlabel("epoch") plt.ylabel("loss") plt.legend() plt.grid(True) plt.show()

读曲线的要点很简单:训练和验证的 box/cls loss 都持续下降并且尾部区域不再大幅波动,说明收敛基本到位;训练 loss 降了但验证 loss 在某个点后开始反弹,说明从那里开始过拟合,最佳权重通常在验证 loss 拐点之前。YOLOv8 本身会保留best.pt到 val 指标最高的轮次,省去不少手动找最优权重的麻烦。

4.4 迁移学习:从头训还是用预训练

垃圾分类图片里的物体(瓶子、纸盒、果皮)与其他数据集中的各种物体存在大量共通的低层特征,因此加载yolov8s.pt预训练权重能明显提供收敛速度。我的建议是分两阶段操作:

  • 第一阶段冻结 Backbone,只训练 Neck 和 Head,用较低学习率跑 30 轮;
  • 第二阶段解冻全部参数,降低学习率再微调 40~60 轮。

这样做的原因很直接:冻结阶段相当于让模型先记住“物体在哪儿”的通用知识,解冻后再让全局参数适应垃圾图像特殊纹理。直接全部参数一起训通常也能出结果,但波动更大,收敛更慢。

5. 模型评估与 bad case 分析

5.1 看哪些指标:mAP50 与 mAP50-95

目标检测的评估我不只看单一指标。项目里重点记录 Precision、Recall、mAP50、mAP50-95。

  • Precision 高说明模型大多数预测都是对的,但可能漏了很多;
  • Recall 高说明模型把该找到的物体都找到了,但可能夹杂很多误检;
  • mAP50 是常用工程指标,IoU 阈值 0.5 下的平均精度;
  • mAP50-95 更严格,从 0.5 到 0.95 每 0.05 算一次再求平均,小物体和精确定位要求高时这个指标更能反映差距。

训练结束我把 20 个类别的 PR 曲线、混淆矩阵、F1 曲线都导出来。混淆矩阵特别重要,它能直观看出哪些类互相混淆,比如“易拉罐”和“金属罐”如果同时在类别体系里,矩阵上肯定会出现大块对角外响应,这时就要考虑合并类别。

5.2 改进前后的对比账

我整理一组当时消融实验的记录:

模型配置mAP50 (%)mAP50-95 (%)推理耗时 (ms)
YOLOv8s 基线89.472.640
+ CoordAtt(Backbone)90.874.243
+ CoordAtt + 辅助分支损失91.675.144
+ CoordAtt + 辅助分支 + GSConv 轻量化92.175.939

这个结果是本地测试集实测,不同数据集会略有差异。肉眼可见,轻量化不仅没拖后腿,反而通过减少冗余参数把速度拉回接近基线的水准,最终方案保留的就是“CoordAtt + 辅助分支损失 + GSConv”这一套组合。

5.3 热力图:验证模型到底在看什么

用 Grad-CAM 生成目标检测特征图和热力图是一个被低估的调试手段。我处理过用户集中反馈的“模型总是把袋装垃圾错判为其他垃圾”的问题,改成观察热力图后发现模型注意力集中在袋子的提手和折痕上,而真正的塑料特征关注不足。

解决办法是加入一批撕开袋口、露出内部物件的样本,并在增强时增加局部遮挡。这个过程非常启发人:模型学到的“判别区域”和人类理解的“物体核心特征”如果没有对齐,再复杂的网络也救不回来。建议所有做图像识别项目的朋友都把热力图当成常规质量检查工具。

5.4 Bad case 排查:漏检、误检、置信度分布

我在项目里专门做了 bad case 分类,发现垃圾识别场景下三类问题最多:

第一类是密集堆叠漏检。一堆瓶子叠在一起时,模型容易只检出边缘的 2~3 个,漏掉中间遮挡严重的。对策是加强遮挡增强,同时将置信度阈值从 0.5 放宽到 0.35,允许更多低置信度候选进入后处理,再靠 NMS 过滤重叠框。

第二类是材质反光误检。金属罐、玻璃瓶在强光下反光严重,模型会把反光区识别成白色物体。对策是增加强光实测数据,同时在 HSV 增强里提高高光样本比例,并额外打了 200 张反光样本让模型看到光照变化的“正确版本”。

第三类是袋装不透明垃圾。无法从外表看出内部类别时,模型天然只会给出一个置信度很低的猜测。我最终在系统逻辑上做个兜底:置信度低于 0.4 时反馈“请将垃圾袋拆开后再次投放”,而不是硬猜。这个设计对用户体验的提升非常明显。

6. 部署落地:从 PT 权重到可用服务

6.1 导出 ONNX 与 TensorRT

训练得到的best.pt不能直接放到服务端跑,需要先导出。标准流程是先转 ONNX,再根据硬件选择优化方案。

yolo export model=runs/detect/yolov8s_coordatt/weights/best.pt \ format=onnx opset=12 simplify=True

如果服务器或工控机有 NVIDIA GPU,推荐继续转 TensorRT:

trtexec --onnx=best.onnx \ --saveEngine=best.engine \ --fp16 \ --workspace=4096

TensorRT 的 FP16 推理在实测中比 PyTorch 快 1.5~2 倍,这对嵌入式系统或者算力有限的边缘盒子非常关键。注意转换时 CUDA 版本和 TensorRT 版本需要匹配,否则经常会报一些令人摸不着头脑的算子错误。

6.2 用 FastAPI 封装识别服务

我建议团队把模型封装成一个独立 HTTP 服务,这样前端小程序、闸机控制、摄像头采集都能复用同一套识别接口。简单的实现如下:

from fastapi import FastAPI, UploadFile from ultralytics import YOLO from PIL import Image import numpy as np app = FastAPI() model = YOLO("best.pt") @app.post("/infer") async def infer(file: UploadFile): img = Image.open(file.file) results = model.predict(source=img, conf=0.4, imgsz=640) det = results[0] boxes = det.boxes.xyxy.cpu().numpy() labels = det.boxes.cls.cpu().numpy().astype(int) scores = det.boxes.conf.cpu().numpy() names = det.names return { "boxes": boxes.tolist(), "labels": [names[i] for i in labels], "scores": scores.tolist() }

实际生产里还要加请求超时、图片大小限制、并发控制,不然高并发下 GPU 显存会被撑爆。这一层的设计虽然不直接影响模型精度,但直接决定系统能不能被项目验收。

6.3 边缘硬件部署:RK3588 实战

不少朋友搜过“rk3588部署yolov8”,这确实是现在很常见的硬件选型。RK3588 的 6 TOPS NPU 跑 YOLOv8s 是在预算和算力之间比较平衡的取舍,部署时我用的是瑞芯微 RKNN-Toolkit2 流程:

  1. 把 ONNX 模型转成 RKNN 格式。导 ONNX 时尽量用opset=12和simplify=True,部分算子 RKNN 工具还不支持,简化后能避免很多转换报错;
  2. 准备校准集。RKNN 做 INT8 量化时需要校准集来统计激活值分布,校准图最好选 500 张覆盖所有类别的真实场景图,而不是随便截几张,量化掉点能控制在 2% 以内;
  3. 编译生成.rknn文件,在板端调用 RKNN Python/ C API 做推理。

部署过程中遇到的高频坑是:PC 端 PyTorch 推理和 NPU 推理结果不一致,主要原因是预处理细节不同。YOLOv8 默认的归一化是除以 255,而 RKNN 示例里经常用归一化参数硬编码,还要注意 BGR/RGB 通道顺序和 letterbox 缩放方式要一致。我先在板端写了比对脚本逐帧对比 PC 端与 NPU 端输出,发现问题后调整到完全一致。

6.4 再补充一个低配摄像头方案:ESP32-S3

如果项目预算极低,还有人问过 ESP32-S3 图像识别行不行。以我的经验,直接用 TF 微控制器框架在 ESP32-S3 上跑完整的 YOLOv8s 并不现实,内存和算力都不够。但风格可以考虑这种做法:ESP32-S3 负责拍图和上传,识别全部交给局域网内的服务器或 RK3588 盒子,得到结果再通过串口或 WiFi 反馈。把“端侧识别”降级为“端侧采集”,项目稳定性会有质的提升。

7. 训练中的常见问题与排查速查表

这部分我把训练几个月里踩过的坑整理成一张速查表,给同样在跑 YOLOv8 的读者直接抄作业。

现象可能原因排查方向解决办法
Loss 直接为 NaN学习率过大、标签中出现空标注、输入图像损坏看日志前几轮 loss 变化调低 lr0;清洗数据集,删除无法解码的损坏图片
Loss 迟迟不掉Backbone 被冻结且 Learning Rate 设置过小检查冻结范围与参数名解冻颈部模块或提高 lr0 到 0.001
验证 mAP 震荡剧烈数据量过少、batch 过小、数据集分布不均按类别统计样本数过采样少数类;提高 batch;减少验证集随机性
PyTorch 内存不足batch 太大或 cache=True 时内存不够观察内存占用batch 调小,cache 改为 False,开启 amp
导出 ONNX 报算子错误某些模块不支持 opset 或 exporter检查具体报错算子换 simplify;升级 ultralytics;手动替换算子
RKNN 转换失败ONNX 里包含动态尺寸或特殊算子查看 unoptimized 日志固定输入尺寸 640x640;用 opset=12 重新导出
训练正常但测试集表现差数据增强与测试分布差距过大对比增强前后结果关闭 Mosaic 或降低增强系数;采集真实场景测试集
过拟合严重数据量不足但参数量过大对比 train_loss 与 val_loss用预训练权重;增加 Dropout;调整 weight_decay

你如果遇到“KeyError: 'class_names'”这类模型加载问题,多半是训练用的权重文件和当前项目的数据集 YAML 对不上,要么重新导出权重,要么确保测试环境里data.yaml里的类别名称与训练时完全一致。

另一个容易忽略的问题是信息泄漏。如果做数据预处理时用了全图片统计的归一化参数,测试时会泄漏训练集分布的信息,虽然 YOLOv8 默认用固定 255 归一化规避了这一点,但一旦自己写预处理脚本就要特别小心。

8. 避坑总结与分享一点个人体会

这个项目做完,我最大的体会是“数据集质量决定模型上限,模型结构决定距离上限还有多远”。很多人一上来就改 YOLOv8 的 Head、堆注意力模块,结果发现数据标注混乱、类别分配不合理,改来改去精度都上不去,还白白增加调试时间。

再分享一个可以直接用的技巧:每次在某一个 C2f 或注意力模块改动之后,不仅看 mAP,也盯一下推理 FPS 和模型参数量。我习惯把每一版改动记录成一张表:用什么模块、放哪里、训练了多少轮、mAP 多少、FPS 多少、有没有副作用。后面发现模型不行,回滚速度非常快,不用重跑十几个小时去猜。

如果你也想做类似方向,我的建议是先用官方 YOLOv8 权重在原数据集上跑出一个基线,再看哪些类别的识别效果明显不行,优先针对性地采集数据,而不是一上来就往网络结构上动刀。很多时候补几百张难例,比换任何花哨模块都有效。训练时固定随机种子、固定库版本、做好实验记录,这些“看不见”的事情对项目长期推进的收益,远大于在结构上一时的灵感迸发。

最后说一点贴近实际项目的经验:垃圾分类识别这类系统在真实环境里,大概会有 10%~15% 的图片让模型怎么都不确定,这时与其硬识别,不如在产品交互上允许用户手动选择。有些回收箱已经加了“摄像头判定+用户按钮确认”的双确认机制,误判率一下子下降了很多。技术方案要和产品逻辑配合,而不是让模型一个人扛所有责任。这个思路放在很多图像识别项目里都适用。

返回列表