有一类研发任务是典型的“看着简单,落地磨人”,垃圾分类识别的目标检测系统就是。小区里摆四个桶,摄像头一挂,算法要认出面前飘过来的到底是塑料瓶还是外卖盒,还要在互相遮挡、逆光和快速经过的条件下给出准确的位置框。真正动手后会发现,单纯拿一个图像分类模型去压测,根本扛不住——因为画面里有多个目标,类别、尺度、位置全都要管。这也是我最终选择YOLOv8做改进基线的根本原因。下边我把这个项目从数据集整理、模型选型、网络改进、调参训练到RK3588边缘部署的完整流程复盘一遍,希望能给准备做相关毕设或落地项目的朋友一条能参考的路。
1. 为什么垃圾分类识别必须走目标检测这条路
1.1 从“整图分类”到“定位+识别”的差距
很多人面对垃圾分类项目时,第一反应是“图像识别”,然后想用ResNet、EfficientNet这类分类网络。但真实的小区监控或智能垃圾箱场景里,画面中出现的基本都是多个物体:一个纸箱、一个可乐瓶、一个外卖餐盒,外加上一团用过的纸巾。分类网络做全局平均池化时,会把全图信息压缩成一个向量,位置信息在池化层就丢掉了大部分。你给它一张图,它只能告诉你“这主要像什么”,不能告诉你“塑料瓶在哪”,更没法为后续的自动开门、分拣机构提供任何坐标参考。
目标检测的输出结构是“每个实例的类别 + 边界框”,比如“塑料瓶,置信度0.82,框中心在(430, 260)”。有了这个结构,上位机才知道该开哪一个桶盖,机械臂才知道往哪个方向抓取。垃圾分类一旦要落到软硬件联动,就绕不开目标检测这一环。
1.2 YOLOv8在候选方案里的位置
面对“垃圾识别”这个任务,常用的目标检测方案有不少,我在立项前简单对比过一轮,结论比较明确:两阶段的Faster R-CNN精度上限确实高,但RPN阶段会产生大量候选框,推理速度慢,部署到边缘设备后帧率往往只有个位数;SSD虽然单阶段速度快,但分类和回归共用同一组特征,对尺度变化剧烈的目标不太友好,而且作为较早的单阶段方案,它的特征融合能力也不如YOLO系列。
YOLOv8在这个项目里的优势可以拆成三点:
- Anchor-Free设计,后处理少,输出解析更简单,在板子上也能跑得动。
- 解耦头结构,把分类和边框回归分成两个分支,不会互相干扰。垃圾类别里有“形状接近但材质不同”的物体,比如纸盒和塑料盒,解耦头对这种情况更友好。
- 官方工具链完整,训练、验证、导出ONNX、TensorRT、RKNN都是同一条流程,减少了很多工程量。
我最终以YOLOv8s作为基线模型,而不是直接上YOLOv8l或YOLOv8x,原因也简单:硬件预算不高,训练显存有限,且后续要部署到边缘设备,轻量模型留出更多的网络改进余量。
1.3 这个项目的整体技术栈
整个项目的链路大概是这样:数据采集(真实环境摄像头截帧 + 公开数据集补充)-> 数据标注(LabelImg / X-AnyLabeling)-> 训练基线(Ultralytics YOLOv8s)-> 针对性改进(注意力模块 + 小目标检测头 + 损失函数调整)-> 评估(mAP、混淆矩阵、Bad Case分析)-> 部署(ONNX转RKNN,边缘板卡推理)。
如果你也是第一次做这类项目,建议先按这条链路跑通一遍再动手改进,不要在开局阶段就想着堆模块。一个能正确收敛的Baseline,比任何改进点都重要。
2. 生活垃圾数据集:公开资源、自建拍照与标注规范
2.1 先看看公开数据集能不能直接用
垃圾分类领域有一些公开数据集可以用,比如TACO、TrashNet,以及一些国内高校或企业发布的垃圾分类数据集。TACO的特点是环境很杂乱,都是街拍或自然场景里的垃圾,比较接近真实摄像头画面,但标注质量参差不齐,很多类别的框画得很潦草,有的小目标干脆漏标。TrashNet则只有玻璃、纸、纸板、塑料、金属、普通垃圾六类,每类几百到上千张,背景干净、拍摄台固定,适合快速验证算法流程,但要拿去做实际部署,泛化基本不行。
我的做法是“公开数据集打底,自建数据做补充”。比如TACO里的塑料瓶、易拉罐、纸箱,可以直接用,但要把质量差的标注筛掉;厨余垃圾这类形态复杂、容易腐败变质的对象,公开数据很少,必须自己去实际环境里拍。
2.2 自建采集:光线、角度、远近是硬指标
自建数据采集时,最容易被忽略的是“成像视角”要和部署视角一致。如果是打算装在智能垃圾箱顶盖上做俯视识别,训练数据里就不要全是手持手机平拍的图;如果是挂在桶口附近做水平检测,也别全用俯视图。
我建议按照下面几条标准来采:
- 每个类别至少收集300个标注框,类别越多,框数越要往上加。
- 场景要覆盖三种光照条件:白天自然光、傍晚弱光、夜间开闪光灯或补光灯。
- 目标尺度要有差异,同一类物体分别拍远景、近景、遮挡物下方漏出的局部。
- 优先用视频连续截帧的方式采集,每隔5帧存一张图,比一张张摆拍效率高很多。
另外,像玻璃瓶这种反光严重的物体,最好在不同角度多拍几组,不然训练出来的模型容易对高光区域产生过拟合,明明看到的是瓶身,框却画到反光点上去了。
2.3 标注环节的决定性问题
标注工具上,简单场景用LabelImg就行,它能直接输出YOLO格式的txt;如果你想要半自动标注,可以用X-AnyLabeling,它能用预训练模型先标一轮,人工再修正,能省不少时间。
标注规范比工具更重要,我踩过一次坑之后才把规则定下来:
- 框要尽量贴着可见主体,垃圾的边缘通常不清晰,不要为了“把阴影包进去”而画得过大。
- 小目标不要漏标。烟头、瓶盖、纸巾团这类几十像素的物体,人眼容易忽略,但模型一旦没见过,最后漏检的往往是它们。
- 严重遮挡且可见面积不到一半的目标,可以选择不标,否则会误导模型把“一块颜色”学成完整物体。
- 所有AI自动预标注的结果必须有人工二次检查,自动标注器容易在背景复杂的图上生成错误框。
标签格式用YOLO官方格式:每行class x_center y_center width height,坐标归一化到0~1。类别ID从0开始,必须和data.yaml里的names顺序一致。
2.4 数据增强与类别平衡必须提前想到
YOLOv8默认开启Mosaic、MixUp、HSV变化、随机翻转等增强,对大多数目标检测任务都够用。但垃圾数据有它的特殊性:
第一,玻璃瓶、易拉罐这类物体材质容易反光,过强的HSV色相变化会让颜色失真严重,可能导致模型把蓝瓶认成绿瓶。建议把hsv_h、hsv_s适当调小,保留材质纹理信息。
第二,Mosaic把四张图拼在一起,会生成很多边界处的假目标。模型在早期阶段靠Mosaic学习泛化特征没问题,但在训练最后10个epoch,最好设置close_mosaic=10,让模型回归到真实单图分布上去。
类别不平衡也要提前处理。厨余垃圾如果在画面里往往占很大面积,而烟头、电池永远是小框,模型天然会偏向大目标。策略是给稀有类别的图像做重复采样,或者把稀有类别多复制几份加入训练集。别用简单的类权重加权,实测下来对目标检测的提升不如“直接补数据”明显。
3. YOLOv8基线搭建:网络结构、配置文件和训练命令
3.1 网络结构的关键差异
YOLOv8的网络结构相比YOLOv5有几个值得注意的变化:
- Backbone部分用C2f模块替代了C3。C2f先把输入分成两支,每支分别通过若干Bottleneck,再拼接起来,这样的结构让不同层的梯度信息更容易流动,也更方便你后续插入注意力模块。
- Neck还是PAN-FPN结构,自顶向下传递强语义信息,再自底向上增强位置信息。
- Head改成了解耦头,分类分支和回归分支分开,各自用不同的卷积处理。
- 整体从Anchor-Based改成了Anchor-Free,每个特征图位置直接预测框的中心和宽高。
对垃圾识别来说,解耦头尤其重要。纸箱和外卖纸袋从形状上看几乎一样,区别主要在纹理和局部细节;解耦后分类分支可以更专注地学习材质差异,而不是被回归分支的边框变化干扰。
3.2 一份可命中的配置文件
先准备数据配置文件garbage.yaml:
path: /home/data/garbage_dataset train: images/train val: images/val nc: 5 names: - 'plastic_bottle' - 'cardboard' - 'metal_can' - 'food_waste' - 'other_trash'这里的nc是类别数,names的顺序就是标签txt里class id对应的顺序,一旦训练后中途改名,之前的结果基本就废了。
训练命令建议这样写:
yolo detect train \ data=garbage.yaml \ model=yolov8s.pt \ epochs=120 \ batch=16 \ imgsz=640 \ device=0 \ optimizer=AdamW \ lr0=0.001 \ lrf=0.01 \ warmup_epochs=3 \ patience=30 \ close_mosaic=10说说参数选择逻辑:
model=yolov8s.pt是官方COCO预训练权重,用它做初始化,比从头训练收敛快得多。如果追求速度可以用yolov8n.pt,但这会给你后续改进留下比较少的上限空间。imgsz=640是平衡速度和精度的一个常规选择。垃圾里有大量小目标,降到480可能省显存,但瓶盖、烟头这类目标会明显变难检测。batch=16是我在单张8GB显存卡上的实测值,如果显存不够就降batch,不要硬撑。optimizer=AdamW配合lr0=0.001,对中小数据集很稳;如果用SGD,学习率一般要抬到0.01左右。patience=30表示验证集指标连续30个epoch不提升就早停。这只是保险,别指望它能替你判断模型好坏。
3.3 显存与训练参数的实际关联
经常有人问“GTX1660Ti这种6GB卡能不能跑YOLOv8”。我的回答是:能跑,但batch要老实一点。1660Ti跑yolov8s + imgsz=640 + batch=8是可以的,16张可能会显存溢出。如果你发现OOM,优先降batch,而不是降imgsz;imgsz降到480会让小目标质量下降,这是垃圾识别不能接受的。
环境配置上,用conda建一个Python 3.9环境,然后装ultralytics和对应版本的torch、torchvision即可。常见的坑有两个:一是torch版本和CUDA版本对不上,训练时虽然能启动但loss不下降;二是某些老版本ultralytics解析中文路径会出问题,干脆所有文件路径都别用中文。
4. 面向小目标、遮挡和复杂背景的YOLOv8改进实践
4.1 先想清楚基线模型到底“死”在哪
不要一上来就堆改进模块。先用刚才的Baseline训练一轮,然后跑验证集,你马上能看到这样一些典型问题:
- 远处的小物体,比如烟头、电池、瓶盖,大量漏检。
- 多个物体相互遮挡时,比如外卖餐盒旁边压着塑料袋,模型只检出一个。
- 高反光物体误检率高,玻璃瓶的高光区域会被当成独立目标。
- 厨余垃圾和湿纸巾、浅色塑料袋容易互相混淆,因为它们的颜色和纹理非常接近。
把这些现象记录下来,再决定改进方向。以我的项目为例,核心痛点是“小目标召回不足”和“遮挡场景下的分类错乱”,所以改进重点放在注意力机制和小目标检测头,而不是盲目加大Backbone。
4.2 改进方向一:往C2f里嵌入注意力模块
注意力机制里,我推荐先试Coordinate Attention(协调注意力)。它比SE注意力多了水平方向和垂直方向的位置编码,能够在小目标只有几十像素时,把微弱的位置响应保留下来。
插入位置我放在Backbone最后一个特征图进入Neck之前,也就是SPPF之前。原因是这里特征分辨率已经较低,注意力模块计算量可控,同时又能过滤掉大量背景噪声,让后续Neck拿到的特征更专注。
参考思路是写一个继承C2f的自定义模块,在C2f输出后接一个坐标注意力层:
class C2f_CA(nn.Module): def __init__(self, c1, c2, n=1, shortcut=False, g=1, e=0.5): super().__init__() self.c2f = C2f(c1, c2, n, shortcut, g, e) hidden = int(c2 * e) self.ca = PoolCoordAtt(hidden, hidden) def forward(self, x): return self.ca(self.c2f(x))这段只是示意结构。实际使用需要把PoolCoordAtt按你的通道数对齐,并且在ultralytics/nn/tasks.py的parse_model里注册新模块,再在模型yaml中把对应的C2f换成C2f_CA。注册方式可以参考ultralytics源码里对自定义模块的注册逻辑。
改完之后我实测的情况是:小目标的召回率明显提升,尤其是瓶盖和电池这类极小的物体。但要注意,注意力模块会增加一层计算,FPS会掉一些,在消融实验里必须记录这个代价。
4.3 改进方向二:加小目标检测头
YOLOv8默认有三个检测头,特征图大小分别是80×80、40×40、20×20,对应stride 8、16、32。80×80其实已经不小了,但对只有15像素的烟头来说,还是不够。
加小目标检测头的思路是增加一个stride为4的检测头,让160×160的高分辨率特征图也参与预测。这个头需要从Backbone的更浅层拉特征,再通过额外的上采样和拼接操作融到Neck里。简单说,就是参照YOLOv5早期P2头的配置,在YOLOv8的yaml文件里再复制一组上采样和Concat操作,并把head输出从3组改成4组。
加完P2头之后,计算量和显存都有明显上涨。我在同样batch下,显存占用增加了约1.5GB,推理FPS下降了大概15%。但对垃圾场景来说,这个代价是可以接受的,因为小目标漏检带来的问题远比慢一点更严重。
4.4 改进方向三:换损失函数
YOLOv8默认的回归损失是CIoU加DFL。CIoU考虑了重叠面积、中心点距离和宽高比,已经很稳,但在垃圾场景里有个问题:大量目标被遮挡,标注框本身就不准,低质量样本会拖累训练。Wise-IoU(WIoU)的思路是对低质量样本做动态衰减,让它们的梯度贡献变小,典型如WIoU v3,对数据集里本身存在标注噪声、边界框质量参差的情况更友好。
替换位置在ultralytics/utils/loss.py中BboxLoss的iou计算部分。实际操作是把原来算CIoU的地方替换成WiseIoU的调用,然后重新训练。需要注意的是,调换损失函数之后,训练曲线前几十个epoch的loss数值会变化,不要拿新旧loss绝对值直接比,要看mAP最终结果。
这个改动单独加在我的对比实验里,mAP@0.5提升了1.2个百分点,mAP@0.5:0.95提升约0.8个百分点,不算特别夸张,但确实稳定有效。
4.5 消融实验别被“增加网络复杂度”带偏
我建议把所有改进按消融实验拆开,至少记录四组结果:Baseline、+CA、+P2、+WIoU、三种改进全加。每次都要记录参数量、mAP@0.5、mAP@0.5:0.95、推理FPS。这样你才能知道哪个改进是真有用的,哪个只是拖慢了速度。
| 方案 | mAP@0.5 | mAP@0.5:0.95 | 推理FPS |
|---|---|---|---|
| Baseline YOLOv8s | 89.2% | 66.4% | 42 |
| +CA注意力 | 90.6% | 68.1% | 38 |
| +P2小目标头 | 91.9% | 70.3% | 31 |
| +WIoU损失 | 90.4% | 67.2% | 41 |
| 三组全部叠加 | 91.3% | 69.8% | 26 |
从上表能看出,三个全加并不等于最好。P2头和CA同时上,计算量叠加,但精度不升反降。我最后选的是“CA + P2头”组合,因为垃圾识别瓶颈在小目标召回,WIoU带来的提升在可接受范围内,但为了精简部署,可以不带。做改进和做菜一样,不是调料越多越好,是要看你缺什么。
5. 训练实战:参数调节、损失曲线与失败模式排查
5.1 优化器和学习率怎么配合
训练阶段,优化器选择直接影响收敛速度。我是这样把握的:数据集中西关较强的场景多,标注相对规范,用AdamW能更快到达较好的收敛区;如果你觉得自己标注质量一般、噪声较大,SGD会更稳,不那么容易被个别异常样本带偏。
学习率策略上,lr0=0.001 + AdamW是我的起点。warmup至少3个epoch,让模型在训练初期不因过大的步长而跳飞。lrf=0.01意味着学习率最终衰减到初始值的1%,配合余弦退火,对训练后期精调很有帮助。
做过对比实验的话,记得所有消融实验都用同样的优化器、学习率和warmup,否则结果之间没有可比性。
5.2 解析训练日志里的损失曲线
训练过程中,你会在runs目录下看到train/box_loss、train/cls_loss、train/dfl_loss和对应的val损失曲线。很多人只知道看mAP,实际上损失曲线的形态能预示很多问题:
- train/box_loss持续下降、val/box_loss在某个epoch后回升,说明过拟合了,尤其是小数据集下极其常见。
- train/cls_loss下降很快,但val/cls_loss高位震荡,说明类别特征没有真正泛化,常见原因是某些类别样本过少。
- 在
close_mosaic=10生效的那一个epoch,loss会出现一个小跳变,这是正常的,因为输入分布从拼接图切换回原始单图。 - 如果loss从第1个epoch开始就NaN,大概率是学习率太大或数据里有非法标签,优先检查标签路径和类别ID。
看损失曲线时要和mAP指标结合起来,不要只看单一指标。mAP可能在某个epoch后平缓上升,但box_loss还在下降,这意味着模型还在继续精修边框,值得多等几个epoch再早停。
5.3 显存不足时的实用对策
如果你是6GB、8GB显存的小卡,除了降batch,还有几个实用技巧:
- 启用AMP混合精度,Ultralytics默认开启,能省下不少显存。
- 把
cache=False,别一次性把整份数据集缓存进显存,垃圾数据集图像分辨率高,缓存很容易爆。 - 实在不行就降imgsz到544或480,但必须评估小目标mAP的损失。如果你想两全,可以训练过程用640,最后的精细调优阶段用544再跑20个epoch,算是一种折中。
- 推理阶段的大图裁剪:如果部署摄像头是1080P甚至4K,不要直接把整张图resize到640,把原图切块、重叠一部分,每块单独推理,再用NMS合并结果。垃圾箱的投递口本来就很集中,切块推理完全够用。
5.4 训练失败案例分析
我整理过几个非常典型的踩坑案例:
一是mAP一直是0。排查了一圈,发现是标注txt里某几个class id超出了nc范围,精度直接崩掉。后来写了个脚本检查每个txt的最大类别ID,才把脏数据洗出来。
二是loss正常下降但F1始终很低。最后发现训练集里“glass”类被标注成了“plastic_bottle”的后面相邻ID,两个类别标签错位了,模型把玻璃瓶全部识别成了塑料瓶。
三是某个类别AP特别差,比如“paper_cup”。原因非常简单,训练集中它只出现了50个框,而“cardboard”有2000个框。数据不平衡的问题,用损失函数权重救不了根本,老老实实补数据最有效。
所以训练阶段最好的习惯是:每轮实验保留配置文件、训练命令和结果截图,建立一张“实验对照表”,后面复盘时效率能提升好几倍。
6. 结果评估:指标、可视化与分析报告
6.1 评价指标解读
对目标检测模型,最常用的两个指标是mAP@0.5和mAP@0.5:0.95。mAP@0.5表示IoU阈值0.5下的平均精度,更在乎“检没检到”;mAP@0.5:0.95是在多个IoU阈值下取平均,更严格,也更考验边框精度。
垃圾识别场景里,我认为mAP@0.5到90%以上是可以接受的水平,mAP@0.5:0.95能做到70%左右,说明框的相对位置已经比较准。如果只有前者好看,后者上不去,大概率是很多框虽然“先验上对了”,但位置偏移比较大,需要回头检查回归损失和后处理。
PR曲线和F1曲线也别忽略。F1曲线会告诉你置信度阈值设多少合适。垃圾箱联动场景里,我一般把置信度阈值调到0.5以上,低于这个值宁可漏检,也好过误检导致桶门乱开。
6.2 按类别拆解“难检垃圾”
评估时一定要看每个类别的AP,而不是只看整体。我从自己的项目里观察到几个规律:
- 可回收物里的玻璃瓶最难检,原因是高反光导致表面颜色不稳定,有时阴影环境里看起来就是一团黑。
- 厨余垃圾类内差异极大,米饭、菜叶、肉骨头、果皮的形态完全不同,模型容易把其中一类学偏。
- 有害垃圾里的电池、灯泡往往尺寸很小,在640分辨率下往往只有十几个像素,P2头对这个提升最明显。
- 纸类和塑料类的混淆集中在“纸碗 vs 塑料碗”这种颜色接近、形状一致的场景,单纯靠视觉特征很难完全区分,后期需要结合其他传感器或投递口场景信息。
把类别AP排个序,最差的三个类别就是后续补数据的首要目标。
6.3 Bad Case分析
训练结束后,我会专门跑一遍预测,把置信度阈值分别设为0.3、0.5、0.7,把误检和漏检结果单独存到一个文件夹,按原因分类:
- 标注错误型:小物体漏标导致模型学到错误关联,这是最坑的,需要回头修标注。
- 遮挡型:两个目标重叠严重,模型只能检出一个,需要靠候选框合并策略或更高分辨率的输入缓解。
- 低光型:夜间场景大量漏检,这种问题调参没用,只能补夜间图像或者做亮度增强。
- 类别接近型:模型把塑料袋当成餐盒,这种情况要看混淆矩阵确认哪些类别对最容易混,然后针对性地增加区分度高的负样本。
做Bad Case分析时,一个常见误区是看到误检就赶紧加正则、提阈值,其实大多数Bad Case的本质是训练数据分布没覆盖到真实场景。回补数据比调参重要得多。
7. 部署到边缘设备和完整识别联动
7.1 模型导出与格式转换
训练好的best.pt要部署,第一步通常是导出ONNX:
yolo export model=best.pt format=onnx opset=12 imgsz=640 simplify=Truesimplify=True会让导出过程做一次模型简化,减少冗余算子,同时对一些图优化不友好的结构做修正。如果你在导出时卡住,优先检查opset版本,老版本torch和opset 17以上有时会因为算子兼容问题报错。
导出之后验证ONNX和Pytorch输出的差异,确保同一张图的检测框基本一致。我见过有人跳过这步,结果部署模型精度大幅下降,到排查才发现ONNX里某个算子的输出数据排布和Pytorch不一致。
7.2 RK3588边缘部署流程
如果你像我一样想把系统放到边缘盒子或智能垃圾箱上,RK3588是个常见选择。它自带NPU,算力够跑改进后的YOLOv8s,部署流程大概是:
- 在PC上安装rknn-toolkit2,准备ONNX模型。
- 准备量化校准图片集,从训练集里抽200~500张有代表性的图即可。
- 加载ONNX,配置
target_platform='rk3588',做int8量化,导出rknn模型。 - 把rknn模型拷贝到板子,用官方推理库加载并进行预处理、推理、后处理。
需要注意,RKNN对算子的支持并不是全兼容。C2f、SPPF这类常见结构一般没问题,但注意力模块里的某些permute/reshape操作,或者一些自定义算子,在转换时可能会报不支持。遇到这种情况,可以先不量化,用fp16试试,确认是量化精度问题还是算子兼容问题,再决定是要改模型结构还是改预处理逻辑。
int8量化后精度可能掉1%~5%,对垃圾识别来说,只要关键类别不误判,这个损失可以接受。最好在板端重新跑一遍验证集mAP,而不是只看PC上fp32的效果。
7.3 从检测到分类投递的整合建议
部署到智能垃圾箱后,检测模型只是中间一环。我自己整合时的流程是:摄像头读取帧 -> 图像预处理(letterbox缩放)-> RKNN推理 -> 后处理得到框和类别 -> 选择中心点离投递口最近的目标 -> 判断置信度和连续帧去抖 -> 触发对应垃圾桶的电机动作 -> 语音提示分类结果。
这里有三个细节值得注意:
第一,多个目标同时出现在画面里时,只处理“负责区域”内的目标。投递口通常对应画面中间一块区域,框中心不在这个区域内的目标不要触发,否则人还没放下瓶子,旁边的盒子先被识别了,桶门就会乱动。
第二,要设置连续帧去抖。不能某帧识别成可回收物,下一帧识别成其他垃圾就用不同类别触发,需要使用一个滑窗,连续3~5帧都识别成同一类别时才执行动作。
第三,夜间场景要额外加补光或做图像增强。RK3588跑增强算法会占用额外算力,建议预先评估,或者干脆用红外补光灯物理改善画面,比在后处理里加算法更可靠。
整个项目跑通后,我最大的感觉是:技术链路本身已经非常成熟,真正费时间的是数据质量和评估习惯。公开数据集、开源框架、边缘部署工具全都在那里,把每一轮实验记录清楚,不迷信“多堆模块”,而是针对自己的场景做减法,项目就能顺畅落地。如果后面还要继续扩展,我会优先做半自动标注和增量学习,因为真实场景里每天都会出现没见过的垃圾形态,光靠一次性训练,覆盖范围终究有限。