简介:垃圾分类是环保智能化的重要环节,YOLO四分类数据集面向目标检测初学者、算法工程师及环保项目开发者,提供覆盖可回收垃圾、有害垃圾、厨余垃圾和其他垃圾四类目标的标注数据。每一张图片对应的txt标注文件记录了目标类别与边界框位置,共含1999个标签文件,另有1个yaml文件用于配置类别名称与训练路径,压缩包整体约540.75MB,契合YOLOv5、YOLOv8等主流模型的训练输入要求。到目前为止,已有2086人学习使用该数据集,数据组织清晰、标注规范,可直接下载后投入训练与验证。借助这批数据,开发者能够节省大量人工标注时间,快速搭建垃圾分类识别系统,并根据真实场景进行模型调优,进而提升分类准确性与分拣效率,为垃圾自动回收和资源再利用提供可靠的数据支撑。
1. 项目概述
1.1 先聊一下这个项目打算解决什么问题
垃圾分类这事听起来简单,真做起来一堆麻烦。小区垃圾房旁边几个桶排成一行,上面贴着分类指南,但大家丢垃圾的时候照样犹豫——这个沾了油的披萨盒是纸还是厨余?用过的湿纸巾是其他还是可回收?我做过一段时间社区环保相关的视觉项目,发现真正缺的不是宣传单,而是能自动识别垃圾类别的前端设备。于是就有了这个从零开始打造YOLO垃圾四分类数据集的计划,目标是训练一个能通过摄像头实时识别垃圾所属类别的模型,再挂到嵌入式设备上,放在垃圾房旁边当“AI督导员”。
这个项目选YOLO做检测模型是深思熟虑的。市面上做垃圾分类的算法不少,有拿ResNet做图像分类,也有拿Faster R-CNN做检测的,但实际部署到现场后,能稳定跑实时推理的没几个。YOLO系列走到现在,yolov8、yolov9甚至yolo v10都已经比较成熟了,速度、精度、部署链路三方面都均衡,社区生态也足够好。相比分类模型,检测模型能在画面里同时定位多个垃圾,框出一个“可回收塑料瓶”和框出整张图片是“可回收”,后者对后续机械臂抓取或者人机交互都有意义。
四分类是参考国内常见的生活垃圾分类标准:可回收垃圾、厨余垃圾、有害垃圾、其他垃圾。数据集的核心工作是采集足够多的实拍图片,完成标注,转成YOLO训练需要的格式,然后在这个基础上把检测模型训起来,最后落到一个能用的推理服务上。整个过程看起来不复杂,但拖垮项目的往往不是模型训练,而是数据集的细节——类别不平衡、标注框不规范、场景单一导致泛化差,这些问题后面展开讲。
1.2 为什么数据集才是整个项目的核心
很多初学者拿到yolov8就开始跑train,跑出来的模型在验证集上看着挺像回事,一换环境就废了。问题几乎都出在数据上。YOLO对标注格式很敏感,对图像质量和多样性也极其挑剔。一份烂数据集喂给再好的模型,产出也是烂结果。
垃圾识别这个场景有一个特殊难点:垃圾本身长什么样,跟“场景”强相关。同样一个塑料瓶,在干净的室内桌面上拍,和躺在湿漉漉的垃圾桶里拍,特征是两回事。阳光下、夜晚、荧光灯下,颜色和纹理都会变化。真实场景里垃圾往往互相遮挡、堆叠、变形,甚至已经破裂。所以这个数据集必须包含大量真实环境图片,不能只在网上爬点干净图就开训。
2. 数据集的整体设计与采集策略
2.1 四分类的类别定义与样本规划
做分类任务之前,先把类别边界定义清楚。我做的四分类参照《生活垃圾分类标志》标准,设定为:
- 可回收垃圾:塑料瓶、纸箱板、易拉罐、玻璃瓶、旧衣物、塑料袋
- 厨余垃圾:剩菜剩饭、果皮、蔬菜残叶、骨头、蛋壳
- 有害垃圾:废电池、废灯管、过期药品、油漆桶
- 其他垃圾:烟蒂、陶瓷碎片、受污染的纸巾、一次性餐具
这里有个关键点:像“塑料袋”这种物品其实是典型的边界模糊项。干净塑料袋是可回收,沾了油污就是其他垃圾。做检测模型的时候不能过度纠结这种细枝末节,否则标注一致性会崩掉。我的处理方式是在数据集的标注规范里写清楚,脏污塑料袋归到其他垃圾。如果你打算复现这个项目,建议先把类似的边界清单列好,让所有标注人员按同一套规则执行,不然标注返工能让你怀疑人生。
样本数量规划上,每个类别我至少采集了1200张有效图片,整个数据集总量在5000张上下。考虑到真实场景中可回收垃圾和厨余垃圾出现频率远高于有害垃圾,如果按等量采集,模型会对有害垃圾的少数类别学习不足。我的做法是:可回收占30%,厨余占30%,其他占25%,有害占15%。有害垃圾样本少是因为真实场景里确实少见,再通过后续数据增强补足。
2.2 图像采集的真实环境策略
采集图片是我踩坑最多的地方。第一次偷懒,从搜索引擎批量下载了上千张垃圾图片,训出来的模型在测试视频里惨不忍睹——换个光线就找不到瓶子了。
后来老老实实重新采集,原则是“哪里用就在哪里拍”。我带着摄像头和手机跑了三个地方:小区垃圾房、餐饮店后厨、办公楼楼道垃圾桶。分别对应室外半开放场景、室内强光场景和弱光环境。每个场景拍相同比例的目标类别,保证模型不会过拟合到单一背景。
在垃圾房拍照的时候要注意角度。摄像头会安装在桶的上方斜45度左右,所以采集时要模拟这个视角,不能总是俯拍90度。垃圾堆放状态也分了三种:散落在地面、堆在桶内、单个放置。尤其是桶内状态,目标与垃圾桶边缘有大量遮挡,这个一定要有,否则实际部署时模型会在桶内物体上疯狂漏检。
光线问题没法回避。垃圾房白天和晚上的光照差异极大,如果只在白天拍,晚上开灯后色温一变,模型就瞎了。我一共分三个时间段采:上午九点到十一点、下午两点到四点的自然光,晚上七点到九点的灯光环境。实测下来,晚上单独补充了大约500张图片,把白平衡偏移的样本喂进去之后,夜间识别率才勉强达标。
2.3 标注流程与工具选择
标注是个体力活,但工具选对了能省一半力。市面上常用三款:LabelImg、labelme、Roboflow。我的体感是:
- LabelImg写YOLO格式最直接,输出txt文件,一次到位,适合离线操作。
- labelme输出json,适合多边形标注,但我做检测只需要矩形框,杀鸡用牛刀。
- Roboflow有在线团队协作功能,自带数据增强和导出多格式,缺点是免费额度限制较多。
最终选了Roboflow做主力,本地用LabelImg做补充。原因是标注这种单调工作一个人干容易疲劳出错,Roboflow可以拉两个标注员同步干活,管理者在线检查实际标注质量。对于单枪匹马的个人项目,LabelImg完全够用,照着图形界面画框,导出时自动生成YOLO格式的txt。
标注规范这里要讲透。YOLO的矩形框是“物体最小外接矩形”,要求框尽量贴合目标边缘,但允许压缩到最多露出5%的背景。单张图片里每类目标都要标,哪怕目标只有十几个像素大,能看清轮廓就标。不考虑标注多个角度重叠的复杂情况,遮挡超过50%的目标可以不标,但遮挡低于50%必须标出可见部分。
这里有个容易犯错的点:标注文件里的坐标不是像素值,是相对比例。YOLO格式的txt每行是“class x_center y_center width height”,全部归一化到0~1。举例:一个瓶子在640x480的图里,检测框左上角是(160,120),右下角是(320,360),换算后x_center是0.375,y_center是0.5,width是0.25,height是0.5。写标注脚本的时候直接用像素值除以图片宽高即可,但要注意除法得到的浮点数精度别丢,否则训练时框的位置就偏了。
3. 数据集格式转换与增强实践
3.1 Roboflow导出与目录结构整理
标注完成后,从Roboflow导出时可以直接选YOLOv8格式。它会自动划分train、valid、test三个子集,默认比例是70/20/10。强烈建议用这个划分而不是自己随便切分——Roboflow保证同源图片不会被分到两个集合里,避免数据泄漏导致的虚高指标。
导出的压缩包解压后,目录结构是这样的:
dataset/ ├── train/ │ ├── images/ │ └── labels/ ├── valid/ │ ├── images/ │ └── labels/ ├── test/ │ ├── images/ │ └── labels/ └── data.yamldata.yaml里记录类别名和对应索引,YOLOv8训练时会自动读取。有一点容易被忽略:数据增强处理后的图片数量和原图不完全对应,Roboflow会在文件名上加上随机后缀,所以不要用文件数量去核对增强比例。真正要核对的,是每个类别的实例数量,而不是图片数量。
3.2 数据增强哪些有效,哪些是自我感动
Roboflow自带的增强选项很多,但我建议克制。有些增强对垃圾识别有效,有些纯属帮倒忙。
实测下来有效的是这几类:亮度调整(-15%到+15%)模拟不同光照条件;噪声注入(最高5%)模拟低光环境下的传感器噪点;水平翻转扩充左右方向变化。RGB通道偏移也有一点效果,模拟色温变化。
旋转增强我不建议开太多,尤其不要开到90度以上。因为瓶子、纸盒这类物体有明显的竖直方向特征,旋转90度后语义上是错的——横着的可乐瓶确实存在,但概率低,强行增加这种样本反而干扰模型学习。剪裁增强要控制范围,我用的是0到20%,目标区域占画面太大时剪裁容易把关键特征切掉。
整个增强配合下来,训练集从5000张扩到了约14000张,但验证集和测试集保持纯净,没有做任何增强。这点特别重要,否则你无法判断模型提升到底是学到了通用特征还是记住了增强后的噪声。
3.3 处理类别不平衡的办法
按2.1节的规划,有害垃圾只有约750张原图,即使增强后也就2100张左右,比其他类别少一截。直接训练的话,模型在有害垃圾上的mAP大概率会掉5个点以上。
解决思路有三种,我从简到繁都试过。最简单的是在data.yaml或训练参数里给稀有类别加权重,YOLOv8支持按类别设置loss权重,把有害垃圾的权重提到1.2到1.5。第二种是复制少数类样本做过采样,但要用不同的增强参数跑两遍,不然模型容易过拟合到同一批样本。第三种是我最后实际采用的,给有害垃圾类别增加“合成样本”——把电池、灯管抠出来,贴到不同背景的图上,再适当旋转、缩放生成新图片。这样做出来的数据虽然带有合成痕迹,但对模型学习“电池长什么样”帮助极大。
3.4 自制转换脚本的坑
如果你不用Roboflow,打算完全本地走LabelImg标注再转格式,我给你看一个我自己写的小脚本框架,避免重复造轮子:
import os import xml.etree.ElementTree as ET def voc_to_yolo(xml_path, class_names, out_dir): tree = ET.parse(xml_path) root = tree.getroot() img_w = int(root.find('size/width').text) img_h = int(root.find('size/height').text) lines = [] for obj in root.findall('object'): name = obj.find('name').text if name not in class_names: continue idx = class_names.index(name) box = obj.find('bndbox') x_min = int(box.find('xmin').text) y_min = int(box.find('ymin').text) x_max = int(box.find('xmax').text) y_max = int(box.find('ymax').text) # 归一化,注意防止除零 x_center = ((x_min + x_max) / 2) / img_w y_center = ((y_min + y_max) / 2) / img_h w = (x_max - x_min) / img_w h = (y_max - y_min) / img_h lines.append(f"{idx} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}") with open(out_dir + '.txt', 'w') as f: f.write('\n'.join(lines)) class_names = ['recyclable', 'kitchen', 'harmful', 'other']注意几个细节:坐标归一化时x_center如果小于0或大于1,说明框出界了,要回头检查标注;width或height算出负数,是坐标顺序反了,需要交换x_min/x_max。这类脚本错误不会报错,会悄悄污染整个数据集,训出异常模型你都不知道问题在哪。建议写脚本后先跑一个全量统计,检查所有txt里的数值是否都在0到1之间。
4. 基于YOLOv8的训练与效果调优
4.1 环境配置与硬件选择
训练之前先把环境跑通。我用的是YOLOv8,直接pip安装:
pip install ultralytics硬件方面,很多人问AMD RX 580显卡能不能跑YOLOv8,我正好踩过这个坑。RX 580是GCN架构的老卡,PyTorch官方对AMD的GPU支持主要走ROCm路线,但ROCm对GCN架构的支持非常有限,Windows下基本没有可用方案。如果你手上只有这块卡,我的建议是:不要硬刚本地环境,直接用Google Colab的免费GPU跑训练,或者租个云GPU卡。我自己训练用的是Colab的T4,8GB显存跑yolov8s模型、batch size设为16,完全没问题。如果你坚持本地跑,在Linux下装ROCm 5.7版本碰碰运气,过程比较折腾,性能还会打折扣,不推荐。
显存不够时的另一个实用思路:换更小的模型版本。yolov8n参数量只有3.2M,一张8GB卡轻松带起来,batch 32也没压力。代价是精度会掉一些,但对于垃圾识别这种相对粗粒度的检测,yolov8n的精度完全够用,而且推理速度快一倍以上。
4.2 训练参数与调优细节
配置文件准备好之后,训练命令很简单:
yolo detect train data=/path/to/data.yaml model=yolov8s.pt epochs=100 imgsz=640 batch=16 device=0这里几个关键参数值得展开说:
imgsz选640是YOLOv8默认值,适合大多数场景。如果你的垃圾目标普遍偏小,比如画面里瓶子只有30x30像素,建议把imgsz提到768或832,小目标识别率会有明显提升,代价是训练时间和显存占用同步上涨。
epochs我建议先用100跑一轮,观察验证集指标变化。YOLOv8自带早停策略,连续训练多个epoch验证指标不涨会自动停止。不要盲目追求300个epoch,实测垃圾数据集在100到150个epoch时已经收敛,再训练只是过拟合训练集,验证集mAP反而往下掉。
batch size的选择直接受显存约束。目标检测训练时batch越大,梯度越稳,但不要为了大batch被迫降低imgsz,优先保证图片分辨率。8GB显存配yolov8s、imgsz=640时,batch 16是安全值。如果你用yolov8n,batch可以拉到32。
训练完之后不要只盯着mAP50看。mAP50是IoU阈值0.5下的平均精度,对框的位置要求比较宽松。更要紧的是mAP50-95,它综合了多个IoU阈值下的精度。我在实际项目里遇到过mAP50高达0.93但mAP50-95只有0.52的情况,说明模型能大概找到目标位置,但框的精确度差。对于垃圾识别场景,后续如果要接机械臂抓取,mAP50-95比mAP50重要得多。如果mAP50-95偏低,优先检查标注框的紧密度,而不是换模型。
4.3 训练结果评估与常见问题定位
训练日志里有一个confusion_matrix.png文件,这个要仔细看。它展示的是每个真实类别被预测成哪个类别的比例。我在第一版模型里发现可回收垃圾有12%被预测成了其他垃圾,顺着混淆矩阵倒查原因,发现是标注时把被压扁的纸箱框得过于宽松,框内混入了大量背景区域,模型学到的是“那个位置的纹理”,而不是纸箱本身。
loss曲线同样能暴露问题。YOLOv8的loss分box_loss、cls_loss和dfl_loss三部分。如果cls_loss持续下降但是box_loss卡住不动,大概率是标注框质量不高,需要重新审视训练集里的框是否贴边。如果两类loss都在降但验证集mAP纹丝不动,大概率是过拟合开始了,早停阈值调低一点。
当然,训练集里的脏数据也会在评估阶段露出马脚。我遇到过一次验证集图片里出现了训练集同款背景的图片,导致验证集精度虚高。后来核对了Roboflow的划分逻辑,确认它不会有这个问题,但如果你完全自己划分数据,务必加上图片哈希去重,防止同场景图片串了集。
5. 部署推理与边缘设备适配
5.1 模型导出与轻量化处理
训练收敛后,最后一轮模型是weights/best.pt。这个weights文件用在Python环境里做推理没问题,但部署到边缘设备或摄像头端,一般要转成ONNX或者TensorRT格式。
YOLOv8自带导出命令:
yolo export model=best.pt format=onnx imgsz=640 opset=12 yolo export model=best.pt format=engine device=0 # TensorRT导出ONNX是中间格式,好处是跨平台通用,但推理效率一般。TensorRT是NVIDIA显卡上的加速方案,能把推理速度提升2到4倍。如果你的部署目标是Jetson系列的设备,TensorRT是标配;如果只是普通电脑上用CPU推理,ONNX配合OpenVINO也是可行的。
我在树莓派上用ONNX Runtime推理时发现一个坑:模型输入尺寸必须固定。训练时用的imgsz=640,导出后输入就是640x640。实际摄像头画面往往也是640x480或1920x1080,需要做letterbox变形,把长边缩放到640,短边补灰边。这一步如果处理不好,目标物会被拉伸变形,检测框位置也会偏。建议用ultralytics的LetterBox实现,不要自己写缩放,细节上容易出错。
5.2 摄像头实时检测的工程细节
推理服务跑起来之后,下一步是接摄像头。如果是USB摄像头,分辨率设置不要太贪,1080p在树莓派上处理一帧可能就要几百毫秒,实时性完全崩了。实测640x480分辨率下yolov8n推理大约18到25毫秒,加上预处理和后处理,可以达到接近30帧的流畅度。
完整推理代码参考:
import cv2 from ultralytics import YOLO model = YOLO('best.onnx') cap = cv2.VideoCapture(0) cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640) cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480) while True: ret, frame = cap.read() if not ret: break results = model.predict(frame, imgsz=640, conf=0.35, device='cpu') annotated = results[0].plot() cv2.imshow('Garbage Detection', annotated) if cv2.waitKey(1) & 0xFF == ord('q'): break cap.release() cv2.destroyAllWindows()注意conf参数的设置。置信度阈值不是越高越好,在垃圾房这种远距离、低光环境下,识别置信度普遍偏低。我用0.25试过,误检太多;用0.5又漏检不少。最后调在0.35到0.4之间,实际效果最均衡。部署环境千差万别,这个值要现场实测再定。
还有一个容易忽略的点:平铺在地面的垃圾目标小,容易漏检。如果你发现大量目标被漏掉,可以尝试把imgsz提到768或832重新导出模型。代价是推理时间变长,但对小目标有肉眼可见的提升。我后面在做夜间识别的时候就是这么干的。
6. 常见问题与避坑经验速查
6.1 数据集类问题
| 现象 | 可能原因 | 解决办法 |
|---|---|---|
| 验证集mAP高但现场识别差 | 训练/验证数据同源泄漏 | 用哈希去重,保证场景多样性 |
| 某类目标永远检测不到 | 类别样本太少或标注框严重不准 | 增加该类样本,重点检查标注质量 |
| 同一目标被重复框出 | NMS阈值过低或标注框重叠 | 调高NMS阈值到0.6以上,检查标注是否重复 |
| 靠近画面边缘的目标识别不了 | 训练样本里目标大多在中心区域 | 采集时故意把目标放在画幅四角部位 |
6.2 训练和部署常见坑
训练时看到loss已经降得很低但mAP始终上不去,多半是标注坐标出了问题。我曾经有一次因为脚本里没有把x_center归一化为浮点数,导致所有框的x坐标都被四舍五入成整数,看起来没毛病,但框全部偏向画面左侧。这种问题只能靠统计标注分布来发现,只要宽度方向的坐标分布明显不均匀,就要怀疑转换脚本。
部署时的一个大坑是ONNX模型的输入名称不同。YOLOv8导出的ONNX输入名是images,但有些老版本是input,加载模型时最好先打印一下输入层信息:
import onnxruntime as ort sess = ort.InferenceSession('best.onnx') print(sess.get_inputs()[0].name)之前照着网上的旧教程写死了输入名,结果模型加载后推理结果全乱,排查半天才找到问题。
TensorRT导出还有一件要注意的:precision。FP16精度在大多数情况下没问题,但FP8混合精度在某些显卡上会导致框的位置偏移。垃圾识别场景对精度要求不高,FP16已经能跑到毫秒级,所以不是太追求极致性能的话,不需要冒险用FP8。
6.3 关于泛化能力的最后提醒
很多读者拿到这个教程就开始照做,但我必须强调一点:垃圾四分类看起来类别简单,但不同地区的生活习惯差异极大。北方城市的厨余垃圾以白菜帮子、土豆皮为主,南方城市会有大量甘蔗渣、玉米衣。如果你的项目要落地到一个具体城市,只靠网上数据训练出来的模型是根本扛不住的,必须在目标现场补采一定比例的本地数据再微调。
我后来给模型做持续迭代的时候,采用了“每周增补训练”的方式:把现场部署后摄像头拍到的新样本定期拉回来,筛选出置信度低或预测错误的图片,由人工重新标注后合并进训练集重新训练。这样每轮微调只增加几百张图,cost不高,但模型的鲁棒性在持续变好。这已经脱离了“一次性训练模型”的范畴,变成了一套有闭环的数据运营流程,这才是一个实际部署的AI项目应该有的样子。
如果你做的是个人学习项目,数据集规模可以压缩到每类300到500张,先跑通整个流程看效果。但真实的落地项目,数据工作的比重至少要占七成以上。训练模型只是最后那一哆嗦,前面大量枯燥枯燥的采集、标注、清洗、增强,才是决定模型上限的关键。
本文还有配套的精品资源,点击获取