ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

可回收废物数据集6000张YOLO训练全流程与避坑指南

可回收废物数据集6000张YOLO训练全流程与避坑指南 简介面向yolo系列算法yolov5至yolo11目标检测训练的数据集包聚焦可回收废物识别场景包含玻璃瓶、纸瓶、塑料瓶、塑料袋等类别标签齐全可直接训练与验证。数据集已划分好并同时提供yolo格式txt与voc格式xml两种标注方便在不同框架间切换初学者与进阶开发者均可快速上手。压缩包共2000个xml标注文件对应VOC格式同时配套yolo标签整体约229.98MB组织清晰适合垃圾分类检测模型训练与效果对比。目前已有47人学习/下载适合需要实际标注数据完成课程设计、项目开发或算法复现的读者。使用时可依据yolo格式说明理解坐标归一化规则也可借助voc格式进行数据增强或格式转换。这套数据能节省人工标注时间专注模型调参与迭代是构建可回收物检测系统的实用基础资源。1. 可回收废物数据集的 6000 张带标签图像YOLO 分拣项目的起点准备做回收物分拣或者智能回收箱的人大概率会先去找数据集。这份可回收废物数据集 zip 包里装的是 6000 张带标签图像覆盖玻璃瓶、纸瓶、塑料瓶、塑料袋四类目标很明确喂给 YOLO 训练一个能实时识别这几类回收物的检测模型。6000 张对四类目标检测来说属于「够起步但不富余」的量级——训练能出有效模型但标注质量和样本分布稍微出问题指标就会明显塌。它的适用人群很具体要做垃圾分类演示项目的学生、给分拣线做视觉方案的小团队、以及想快速验证 YOLO 落地效果的工程师。这篇文章顺着「解包 → 校验标签 → 训练 → 补数据」这条线把每一步的做法和坑位写清楚让你拿到 zip 之后能直接照做。2. 从 ZIP 到训练集解包、标签格式确认与数据集划分2.1 解包之后的第一件事先看目录别急着看图拿到 zip 先别急着打开图片欣赏。常见做法是解压到一个不含中文和空格的路径比如D:\recycle_dataset或~/datasets/recycle。用 unzip 或 Python 的 zipfile 都行命令如下mkdir -p ~/datasets/recycle unzip yolo算法-可回收废物数据集-6000张图像带标签-玻璃瓶纸瓶塑料瓶塑料袋.zip -d ~/datasets/recycle解压完成后不要直接去看图片内容先确认顶层目录结构。按这个标题的命名习惯包内大概率是 images 和 labels 两个平级目录也可能按类别分子目录。用 find 列出前两层结构find ~/datasets/recycle -maxdepth 2 -type d | head -40这一步的目的有两个一是确认图像格式是 jpg 还是 png后续脚本里扩展名判断要对上二是确认标签和图像是否同名。如果图像 6000 张、标签文件也是 6000 份说明配对关系大概率成立数量对不上后面训练时 YOLO 会静默跳过没有标签的图像导致实际训练集缩水val 指标虚高——这个坑我放到第 5 章详细说。目录里如果出现classes.txt或labels.txt这是关键文件它是类别顺序的唯一权威来源先复制出来放好。2.2 YOLO txt 标签和 VOC xml 标签坐标系完全不一样可回收废物这类数据集的标签最常见的就两种格式。YOLO 格式下每个目标占一行五个数字分别是 class_id、归一化后的中心 x、中心 y、宽 w、高 h全部是浮点数VOC 格式则是 xml 文件用 xmin、ymin、xmax、ymax 记录左上右下角像素坐标类别写在name节点里。两者坐标系不同混用必翻车。我建议随机抽三五个标签文件看内容别只看一个cat ~/datasets/recycle/labels/train/0001.txt如果输出长这样0 0.4829 0.5312 0.1842 0.3128 1 0.1124 0.7785 0.0901 0.0874那就是 YOLO txt 格式class id 0 对应第一个类别。如果打开是 xml先别急着跑转换脚本确认一件事数据 yaml 里的类别顺序必须与 xml 里 name 节点和 id 的映射一致。VOC 转 YOLO 时中心点和宽高都要除以图像宽高归一化这步做错所有框的位置会整体偏移训练出来的模型检测框全偏属于玄学难排查的一类问题。我见过有人转换时忘了归一化loss 曲线看起来正常但推理时框全跑到图像边缘浪费了一整轮训练时间。提示随机抽 35 个标签文件看不要只抽一个。类别编号顺序不一致是最容易踩的坑——有的包玻璃瓶是 0有的包塑料瓶是 0光看目录名根本看不出来。2.3 划分 train/val先比对文件名再随机切划分数据集之前务必先确认图像和标签文件一一对应。下面这段脚本用文件主键比对两个目录import os img_dir images label_dir labels imgs {os.path.splitext(f)[0] for f in os.listdir(img_dir) if f.endswith((.jpg, .png))} labels {os.path.splitext(f)[0] for f in os.listdir(label_dir) if f.endswith(.txt)} missing imgs - labels print(f图像数: {len(imgs)}, 标签数: {len(labels)}, 缺标签: {len(missing)}) if missing: print(缺标签示例:, list(missing)[:5])这里用os.path.splitext取主键而不是拿整个文件名匹配是因为 zip 解压后常见一种情况图像是.png标签却在生成时写成了.jpg完整文件名比对会误判成缺标签。比对完如果缺标签超过 5%要么补标要么直接把这部分图像移出训练目录留着只会让训练集数量虚高、实际学习不到完整信号。划分 train/val 我习惯按 8:2 随机分。6000 张四类任务随机划分足够不需要太复杂的分层抽样。脚本如下import os, random, shutil random.seed(42) imgs [f for f in os.listdir(images) if f.endswith((.jpg, .png))] random.shuffle(imgs) val_ratio 0.2 val_count int(len(imgs) * val_ratio) os.makedirs(images/train, exist_okTrue) os.makedirs(images/val, exist_okTrue) os.makedirs(labels/train, exist_okTrue) os.makedirs(labels/val, exist_okTrue) for i, img in enumerate(imgs): stem os.path.splitext(img)[0] src_img os.path.join(images, img) src_lbl os.path.join(labels, stem .txt) if i val_count: shutil.move(src_img, images/val/) shutil.move(src_lbl, labels/val/) else: shutil.move(src_img, images/train/) shutil.move(src_lbl, labels/train/)这段脚本的三个要点random.seed(42)保证每次划分结果一致方便复现实验先 shuffle 再按数量切避免原目录按文件名排序导致的分布偏差图像和标签必须同步移动只移图像会让 2.3 的比对脚本白跑。如果你的包内图像文件名不是连续编号也没关系脚本用的是字符串主键不依赖编号。3. 写 data.yaml 并跑通第一次 YOLOv8 训练3.1 data.yaml 的写法names 顺序就是训练时的类别 idYOLOv8 和 YOLOv5 的数据配置都用 yaml 文件。假设这个包的类别顺序是玻璃瓶、纸瓶、塑料瓶、塑料袋以标签文件实际出现的 id 为准data.yaml 长这样path: ~/datasets/recycle train: images/train val: images/val nc: 4 names: 0: glass_bottle 1: paper_bottle 2: plastic_bottle 3: plastic_bag这里最大的坑是names的顺序必须和标签文件里的 class id 一致。yaml 里写错顺序loss 照样收敛但推理结果会张冠李戴——玻璃瓶被叫成塑料瓶属于训练过程一点异常都看不出来的沉默错误。所以写完 yaml 之后别急着训练先跑一段脚本从标签文件反推实际的类别 id 全集import os ids set() for root, _, files in os.walk(labels/train): for f in files: if not f.endswith(.txt): continue with open(os.path.join(root, f)) as fp: for line in fp: ids.add(int(line.split()[0])) print(实际出现的类别 id:, sorted(ids))遍历所有标签文件收集出现的 class id。如果打印出来是[0, 1, 2, 3]说明四类齐全且编号连续和 yaml 的 names 顺序可以一一对上。如果出现 5、6 这类超出标题范围的编号说明包里混了没写进标题的类别需要单独抽出来看是删还是保留。这个脚本建议每次换数据集都跑一次是性价比最高的防呆手段。3.2 最小训练命令Ultralytics 一行起跑环境这块我一般用 PyCharm 建一个虚拟环境来装 ultralytics避免污染系统 Python也方便换版本后回滚。安装就一条命令pip install ultralytics装完先不急着训练跑一个极小的探测任务确认环境通yolo predict modelyolov8n.pt sourcehttps://ultralytics.com/images/bus.jpg能出检测结果说明 CUDA 和模型下载链路都没问题。然后开始正式训练yolo detect train datarecycle.yaml modelyolov8s.pt epochs100 imgsz640 batch16这条命令的参数含义拆开说modelyolov8s.pt会从官方仓库自动下载 COCO 预训练权重用它做迁移学习6000 张图能比随机初始化快几倍收敛如果下载超时手动下载 yolov8s.pt 放到项目目录参数改成model./yolov8s.pt即可。epochs100对四类 6000 张的量是合理的起点跑完看 val 曲线再决定加不加。batch取决于显存我从 16 开始OOM 就降到 8。imgsz640是通用默认值如果你的图像里瓶子占画面比例很小可以试 960代价是训练时间明显变长。关键参数速查参数建议值什么时候调modelyolov8s.pt显存小用 n精度不够再上 mepochs100val loss 还在降就加到 150batch16OOM 时降到 8 或 4imgsz640小目标多、显存够时改 960注意训练前关掉其他占显存的程序。Windows 上常见问题是中途CUDA out of memory崩掉先降 batch别一上来就换大模型。3.3 训练曲线怎么读损失、mAP 和数据质量的对应关系ultralytics 训练结束后runs/detect/train 目录下会输出 results.png、confusion_matrix.png 和一批验证图。results.png 第一行是 box loss 和 cls loss第二行是 mAP50 和 mAP50-95。读这些曲线时我关注的不是绝对数值而是三个信号。第一个信号box loss 在 50 epoch 后还在稳定下降、没有平台期说明 100 epoch 不够加到 150。第二个信号mAP50 很高0.95 上下但 mAP50-95 很低0.5 左右说明框的位置大致对、但和真值重叠不够常见原因是标注框没贴紧目标边缘——比如瓶子标注时把瓶盖和阴影都框进去了。第三个信号val loss 在某轮后开始回升说明过拟合。6000 张四类任务一般不太会太早过拟合真出现了通常是 batch 太小或模型选太大误用了 yolov8x。这里和 yolo 损失函数直接相关的一点box loss 反映的是框回归的质量cls loss 反映类别预测的置信度。如果 cls loss 降得很快但 box loss 拖后腿问题出在标注框的 IoU 一致性上而不是模型学不会——这时候去调学习率没用回头清洗标注才是正解。我习惯训练时开着终端每隔一段时间瞄一眼 runs/detect/train 下有没有新生成的曲线图30 epoch 时就能看出趋势省得跑完一轮才发现数据有问题。4. 训练前的数据清洗与样本均衡标注质量决定模型上限4.1 用脚本找出面积过小和越界的标注框可回收废物数据集里最典型的标注问题有两类一类是瓶子在画面里特别小只有几十个像素高这类目标在 imgsz640 下几乎学不到有效特征另一类是标注框越界YOLO 格式下表现为归一化坐标大于 1 或小于 0。检查脚本如下import os from PIL import Image bad_files [] for f in sorted(os.listdir(labels/train)): if not f.endswith(.txt): continue stem os.path.splitext(f)[0] img_path os.path.join(images/train, stem .jpg) if not os.path.exists(img_path): img_path os.path.join(images/train, stem .png) if not os.path.exists(img_path): continue w, h Image.open(img_path).size with open(os.path.join(labels/train, f)) as fp: for line in fp: parts line.split() if len(parts) ! 5: bad_files.append((f, 字段数异常)) break _, cx, cy, bw, bh map(float, parts) x1 (cx - bw / 2) * w y1 (cy - bh / 2) * h x2 (cx bw / 2) * w y2 (cy bh / 2) * h if x1 0 or y1 0 or x2 w or y2 h: bad_files.append((f, 坐标越界)) break if bw * w 10 or bh * h 10: bad_files.append((f, 目标过小)) break print(len(bad_files), 个文件可能需要处理) print(bad_files[:10])逻辑说明先把 YOLO 归一化坐标还原成像素坐标检查是否落在图像范围内再用框的像素宽高判断目标是否小到没法学。还原坐标这步是核心很多人直接比较归一化坐标和 1 的大小忽略了中心点加宽高一半可能越界的情况。10 像素是我自己的阈值可以在 816 之间调判断依据是 imgsz640 时一个 10 像素宽的目标只占整图宽度的 1.5%检测头很难稳定回归。这些坏样本我的处理原则是越界的直接删掉对应标签行数量少不影响大局目标过小的先统计占比如果超过 5% 说明采集时拍摄距离太远或图像分辨率不够这批数据对瓶子的召回率不会有保障与其硬扛不如直接放弃过小样本把标注精力留给中等尺寸目标。4.2 类别不均衡和背景单一的应对思路玻璃瓶、纸瓶、塑料瓶、塑料袋四类如果数量差距大比如玻璃瓶 3000 张、塑料袋只有 500 张训练时 loss 会被样本多的类别主导塑料袋的 recall 会明显掉队。处理手段按优先级排先改损失权重再对少数类做复制增强最后才考虑生成合成数据。合成瓶子图像容易把纹理和反光特征做假塑料瓶那一类在验证集上 mAP 高一上现场就翻车的情况我见过不少——数据增强的教训有一条算一条宁可少补不要乱补。背景单一是另一个容易被忽略的问题。拿矿泉水瓶做例子如果训练集里 60% 的背景是白墙和桌面现场放到深色传送带上检测模型的泛化能力立刻见底。可回收废物数据集如果全部在室内固定背景拍摄建议训练时打开 Ultralytics 的 mosaic 增强它会把四张图拼成一张训练样本相当于变相扩充背景多样性。再配合一点 HSV 扰动让瓶子的颜色在合理范围内波动能明显提升透明瓶和深色瓶的区分度。mosaic 在 ultralytics 里默认开启只要确认没被手动关掉就行HSV 参数通过命令行传入hsv_h0.015 hsv_s0.7 hsv_v0.4是实用起点在这个基础上微调即可。4.3 处理数据时的自我校验清单数据清洗和增强做完训练前再过一遍清单各类别样本数是否接近均衡验证集里是否覆盖了不同背景和光照标签坐标是否全部落在图像范围内类别 id 是否与 yaml 的 names 顺序一致。这套清单我每次都会走一遍血泪经验是数据问题在训练前暴露代价是一小时训练后才暴露代价是一整天。第 5 章里写的几个翻车现场根源几乎都在这一步漏检。5. 避坑指南可回收废物数据集训练常见的四个翻车现场5.1 缺标签的图像被静默跳过val 指标虚高现象训练日志显示数据集有 6000 张图但每个 epoch 实际处理的图像明显少于这个数val 的 mAP 却高得反常。原因YOLO 对没有对应 txt 标签的图像会静默跳过不报错也不警告。这个 zip 解压后标签缺失多半是文件名编码问题导致配对失败或者图像是 png、标签却在生成时以 jpg 为后缀命名主键匹配断开。解决训练前跑 2.3 节的比对脚本把缺标签的图像统计出来。文件扩展名不一致是最隐蔽的情况务必用os.path.splitext取主键比对别拿完整文件名直接相等判断。5.2 类别顺序和标签 id 对不上模型识别张冠李戴现象训练日志正常loss 正常收敛验证集上玻璃瓶被成批识别成塑料瓶且置信度还很高看起来像模型「学会了但学错了」。原因data.yaml 里 names 的顺序和标签文件里的 class id 不一致。很多数据集打包时 categories 信息写在子目录里或者根本没附全凭猜。解决先跑 3.1 的反推脚本收集标签文件里实际出现的 id 全集和每个 id 的样本数再对照 yaml 的 names 顺序逐一核对。不要信目录名目录名和标签 id 不一致的情况我碰到过不止一次——目录叫 paper_bottle标签里 id 2 代表的可能是塑料瓶。5.3 mAP50 很高但现场实时检测框乱飘现象验证集 mAP50 0.9 以上跑摄像头实时检测时塑料瓶的检测框在目标附近抖动置信度忽高忽低甚至同一瓶子相邻两帧一类识别成玻璃瓶、一类识别成塑料瓶。原因训练图像全是一个角度、一个距离拍的缺少横拍、斜拍和远近变化或者标注框没有贴紧目标边缘模型学到的框始终比瓶子大一圈。透明玻璃瓶和塑料瓶在边缘和背景融为一体时标注员很容易把框画大。解决先看 runs/detect/train 里的验证样本图和 confusion matrix确认模型学的是瓶子本身而不是背景。透明瓶类别的边缘模糊问题可以在标注阶段要求标注员贴着高光边缘画框阴影区域一律不框进。这个修正是提升 mAP50-95 最直接的手段。5.4 换大模型救不了数据集只会更早过拟合现象从 yolov8s 换到 yolov8m、yolov8x训练时间翻倍甚至翻三倍mAP 只涨零点几个点val loss 反而提前回升。原因6000 张四类目标检测任务的瓶颈通常不在模型容量而在标注质量和样本多样性。模型参数量变大只会更快记住训练集的噪声。解决先用 yolov8s 跑通拿 baseline。如果 mAP50 已经在 0.9 以上下一步是补数据、清洗标注、加数据增强而不是升级模型。这也是为什么我坚持把第 4 章的清洗脚本放在调参前面——数据问题暴露得越早后面越省事模型选型这种事放到数据干净之后再做才有效。6. 用混淆矩阵和单类召回率决定下一批数据补什么训练完第一版先别急着调参。ultralytics 会在 runs/detect/train 下生成 confusion_matrix.png这张图是决定「下一批数据补什么」最直接的工具比盯着 mAP 均值有用得多。对可回收废物四类来说最常见的混淆就是对角线之外的高亮格玻璃瓶和塑料瓶因为透明材质的反光特征在图像上高度重叠纸瓶和塑料袋因为两者都是可折叠、轮廓不规则的形态。如果混淆矩阵里玻璃瓶被大量判成塑料瓶说明训练集里欠缺能区分这两类的样本——比如带高光的磨砂玻璃和哑光塑料的对比图像。补数据时应该精准补这一类而不是四类均匀加量。另一种做法是加载训练好的模型对 val 集逐类算精度和召回率from ultralytics import YOLO model YOLO(runs/detect/train/weights/best.pt) metrics model.val(datarecycle.yaml) print(metrics.results_dict)metrics.results_dict里同时包含各类的召回率哪类 recall 低哪类就是下一轮重点补样本的对象。别只看四类加权 mAP均值会掩盖单类掉队的真相。我自己踩过一个印象很深的坑第一版模型 mAP50 0.93看起来不错但塑料袋 recall 只有 0.62——训练集里塑料袋大多是团着的深色袋子现场遇到浅色展开的袋子就认不出。后来补了 300 张展开状态的塑料袋图像recall 直接到 0.85。从那以后我养成了一个习惯每次训练完先看混淆矩阵和单类 recall再决定要不要继续调参。数据问题不解决调参就是浪费时间。希望帮到你。本文还有配套的精品资源点击获取
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进