YOLO烟盒数据集实战:从解压到训练全流程解析
发布时间:2026/10/11 10:00:49 锦皓数字建站

简介这份数据集面向YOLO系列算法目标检测场景聚焦烟盒识别任务。压缩包内共有七百零三个文件其中二百三十四张JPEG原图每张均同时提供YOLO格式的TXT标签与VOC格式的XML标签另含一份data.yaml配置文件。TXT标签存储类别索引与归一化中心点、宽高值XML遵循VOC规范两种文件分目录存放可直接适配主流检测框架。数据集已完成划分支持多个主流版本算法调用配合配置文件即可快速完成训练、验证与测试适合对比不同算法版本在烟盒数据上的表现。同时标签文件与图像文件名一一对应目录结构直观清晰便于检查标注质量与错误案例。整体大小仅约十兆已有三百余人学习是进行目标检测算法实验、教学演示或模型鲁棒性验证的轻量实用样例。1. 拿到“yolo算法-烟盒数据集-234张图像带标签.zip”后你第一件事该干什么做目标检测的同行应该都有过这种经历从网盘或群里拖下来一个数据集压缩包解压一看里面一半是没用的截图、一半是 XML 和图片混在一起标签还动不动就缺 class。这个“yolo算法-烟盒数据集-234张图像带标签.zip”从名字看就舒服很多YOLO 算法、烟盒、234 张、带标签四个关键信息全说清楚了。它的价值在于直接省掉了你从零标注的时间打开就是一个能喂给 YOLOv5/YOLOv8 训练的标注数据集适合用来快速跑通烟盒检测的整个流程。那是不是解压就能训远没那么简单标签格式对不对、类别文件写没写、图片和标注是否一一对应任何一个环节出错都可能让你白等几个小时。这篇就按我实际跑数据集的经验把这个 zip 从解压到训出一个能用的烟盒检测模型的全过程拆给你看。2. 先拆 zip 内部结构YOLO 数据集的标准目录是什么样2.1 解压后最该确认的三样东西images、labels、data.yaml常见做法是把数据集压缩包解压后第一时间用tree或者文件管理器看顶层目录。一个规范的 YOLO 数据集内部一定分成images和labels两个目录每个目录下面再按train、val或者再加test划分子集。images 里放的是.jpg或.png原图labels 里放的是对应的.txt标注文件两者靠文件名一一对应。正常数据集里还会有一个data.yaml文件里面写清楚训练集和验证集的路径、类别数量nc、以及每个类别的名字。你解压后如果发现没有 data.yaml那就需要自己补一个这是绝大多数数据集下载后要做的第一步手工活。拿到这个烟盒数据集我建议先用命令看一下整体文件数量是否匹配。234 张图像带标签那 images 目录里应有 234 张图片labels 目录里也应有 234 个 txt 文件允许有少量空 txt比如某些图里确实没有目标但通常不应太多。先在终端里跑一下find ./烟盒数据集 -type f | sed s/.*\.// | sort | uniq -c这段命令会统计数据集目录下所有文件的扩展名分布。正常情况你会看到类似234 jpg或 png和234 txt的输出外加一个data.yaml。如果 txt 数量明显少于图片数量说明数据集存在漏标如果多出来很多可能有重复标注文件。同时看一眼有没有.xml或.json文件——有的话说明标注格式需要转换这一步后面会细讲。2.2 标签文件里到底存了什么YOLO 格式的五个数字打开任意一个 labels 目录下的 txt 文件你会看到每行由五个数字组成这就是 YOLO 格式的核心。这五个数字依次是类别编号class_id、目标中心点归一化后的 x 坐标、目标中心点归一化后的 y 坐标、归一化后的宽 w、归一化后的高 h。坐标全部是 0 到 1 之间的浮点数由实际像素坐标除以图片宽高得到。比如一行0 0.5123 0.4789 0.2314 0.1987含义就是类别 0 的目标中心点在图片横向 51.23% 的位置纵向 47.89% 的位置宽度占整张图 23.14%高度占 19.87%。这种归一化的好处是模型训练时不需要关心输入图片的原始分辨率一通缩放就能直接喂进网络。但也带来一个经典翻车点如果这个数据集的标签是用 LabelImg 以 Pascal VOC 格式标注的导出 YOLO 格式时没做归一化或宽高填成了右下角坐标减左上角坐标的绝对值训练时 loss 会直接起飞或直接 NaN。新人拿到数据集后最好先手动挑几张图用 Python 把标签画回原图检查一下。import cv2 import numpy as np img cv2.imread(images/train/some_smoke_001.jpg) h, w img.shape[:2] with open(labels/train/some_smoke_001.txt, r) as f: lines f.readlines() for line in lines: cls, xc, yc, bw, bh map(float, line.split()) x1 int((xc - bw / 2) * w) y1 int((yc - bh / 2) * h) x2 int((xc bw / 2) * w) y2 int((yc bh / 2) * h) cv2.rectangle(img, (x1, y1), (x2, y2), (0, 0, 255), 2) cv2.putText(img, str(int(cls)), (x1, y1 - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2) cv2.imwrite(check_visualized.jpg, img)这段脚本做的事情很直接读取一张图和它的 txt 标签把归一化坐标还原成像素坐标画框并标上类别编号最后存成一张新图。跑完你就能目测出框位置和烟盒是否吻合。参数说明(xc - bw/2) * w就是把中心点坐标转成左上角 x(yc - bh/2) * h同理得到左上角 y。画出来的框如果明显偏移说明坐标基准不对通常是因为原标注用的是cx, cy, w, h但被写成了x1, y1, w, h或者宽高没归一化除以了错误的尺寸。这个检查步骤我每拿到一个数据集都会做半小时就能排除掉最坑的格式问题。3. 从 zip 到训练配置 data.yaml 并跑通 YOLOv8 训练的最小闭环3.1 手工写 data.yaml路径、nc、names 一个都不能错确认标签格式没问题后下一步就是补齐或修正 data.yaml。如果你解压后没有这个文件自己新建一个即可。以这个烟盒数据集为例假设压缩包里只有 images 和 labels没有划分 train/val那你要先按比例切分数据集。常见做法是用脚本按 8:2 或 9:1 的比例随机划分。mkdir -p 烟盒数据集/images/train 烟盒数据集/images/val 烟盒数据集/labels/train 烟盒数据集/labels/val先把目录建好然后跑一段简单的 Python 脚本做划分import os import random import shutil random.seed(42) src_img 烟盒数据集/images src_lbl 烟盒数据集/labels dst_img_train 烟盒数据集/images/train dst_img_val 烟盒数据集/images/val dst_lbl_train 烟盒数据集/labels/train dst_lbl_val 烟盒数据集/labels/val all_imgs [f for f in os.listdir(src_img) if f.endswith((.jpg, .png))] random.shuffle(all_imgs) val_count int(len(all_imgs) * 0.2) val_imgs all_imgs[:val_count] train_imgs all_imgs[val_count:] for img in train_imgs: shutil.move(os.path.join(src_img, img), os.path.join(dst_img_train, img)) lbl img.replace(.jpg, .txt).replace(.png, .txt) shutil.move(os.path.join(src_lbl, lbl), os.path.join(dst_lbl_train, lbl)) for img in val_imgs: shutil.move(os.path.join(src_img, img), os.path.join(dst_img_val, img)) lbl img.replace(.jpg, .txt).replace(.png, .txt) shutil.move(os.path.join(src_lbl, lbl), os.path.join(dst_lbl_val, lbl))这段代码逻辑很直观把所有图片文件名读进来按固定随机种子打乱取前 20% 作为验证集剩下 80% 作为训练集然后把对应的图片和标签一起移动过去。参数说明random.seed(42)是固定随机种子保证每次运行切分结果一致便于复现val_count int(len(all_imgs) * 0.2)是验证集比例234 张图就是 46 张做验证、188 张训练。如果你的数据集本身已经很规整图片名不全是 jpg 或 png把后缀判断加全即可。注意不要在移动过程中把原目录的图删了又找不到备份建议先复制一份原始压缩包到别的目录做任何预处理前都要有后悔药这是血泪经验。划分完之后data.yaml 内容按下面的模板写path: ./烟盒数据集 train: images/train val: images/val nc: 1 names: [cigarette_pack]这里path指定数据集根目录train和val是相对于path的子路径nc是类别总数names是类别名列表。如果你打开标签文件发现类别编号有 0 和 1 两种那就把nc改成2names相应变成两个名字。注意 yaml 文件里的names顺序要和标签里的class_id完全一致否则模型会学反。比如烟盒有硬盒和软盒两种类别标签里 0 代表硬盒、1 代表软盒那 names 就写[hard_pack, soft_pack]。3.2 用 YOLOv8 训练显存不够怎么办参数怎么收敛最快现在数据集结构齐了data.yaml 也写好了直接用 YOLOv8 开训。安装 ultralytics 包后跑最小命令yolo train data./烟盒数据集/data.yaml modelyolov8n.pt epochs100 imgsz640 batch16 device0建议先从yolov8n这个最轻量的模型开始跑因为 234 张图是小数据集用 s 或 m 系列大模型非常容易过拟合而且显存压力大。几个关键参数说清楚epochs100在这个数据量下通常足够如果前面 30 个 epoch 的val/box_loss已经不再下降可以提前停imgsz640是标准输入尺寸如果你检测的烟盒在画面里很小可以考虑imgsz1280但训练时间会大幅增加batch16取决于显卡显存8GB 显存跑 n 模型这个 batch 没问题如果显存爆了改成 8 或 4。训练过程中重点盯三个指标train/box_loss、val/box_loss和metrics/mAP50-95(B)。如果train/box_loss下降但val/box_loss不降甚至上升就是过拟合了此时要加dropout或减小epochs。如果 loss 从一开始就是 NaN优先检查标签里有没有出现 0 或负数或者类别编号是否超出了nc范围。还有一种常见情况模型收敛特别慢loss 下降但 mAP 上不去这时候要怀疑标签框是不是有大量超出图片边界的错位框用 3.1 里的可视化脚本多检查几张。YOLOv8 训练完成后会输出best.pt和last.pt到runs/detect/train/下。best.pt 是在验证集上 mAP 最高的权重后续做推理、导出模型都用它不要手滑去加载 last.pt。4. 标注格式不兼容时怎么办把 VOC XML 或 JSON 转成 YOLO txt4.1 三种主流标注格式的差异哪一步最容易数据错位并不是所有叫“带标签”的数据集都直接给你 YOLO 格式的 txt。这个烟盒数据集虽然标题写了 yolo 算法但你解压后仍然可能看到xml文件Pascal VOC 格式或json文件COCO 格式。VOC 的 xml 里记录的是xmin、ymin、xmax、ymax绝对像素坐标COCO 的 json 里记录的是bbox: [x, y, w, h]。这两种格式都需要转成 YOLO 的class_id cx cy w h归一化格式转换本身不复杂但坑全在细节上。最容易出错的地方有两个一是 VOC 的xmax、ymax是包含边界还是不含边界转换算宽高时要确认是否需要1二是转换后的 class_id 必须和 data.yaml 里 names 的顺序严格对应。如果你直接用网上的通用转换脚本它会把 xml 里的namexxx/name按字母序映射成 id而你的 data.yaml 里顺序可能完全不同结果就是模型学的类别编号全是错位的看起来 loss 正常实际推理时把硬盒识别成软盒。这类错误不容易发现因为训练曲线一切正常直到部署了才发现预测类别不对。4.2 一个健壮的 XML 转 YOLO 脚本带类别映射表我一般会在数据集目录下放一个category_map.yaml或者直接在脚本里写死类别字典转换前先把所有 xml 里的类别名提取出来确认和标注意图一致再开始。import os import xml.etree.ElementTree as ET # 类别映射表xml里的类名 - 数字id category_map { cigarette_pack: 0, # 如果有第二类继续加: soft_pack: 1 } xml_dir 烟盒数据集/annotations txt_dir 烟盒数据集/labels os.makedirs(txt_dir, exist_okTrue) for xml_file in os.listdir(xml_dir): if not xml_file.endswith(.xml): continue tree ET.parse(os.path.join(xml_dir, xml_file)) root tree.getroot() img_width int(root.find(size/width).text) img_height int(root.find(size/height).text) txt_name xml_file.replace(.xml, .txt) lines [] for obj in root.findall(object): name obj.find(name).text if name not in category_map: print(f跳过未映射类别: {name} in {xml_file}) continue class_id category_map[name] bndbox obj.find(bndbox) xmin float(bndbox.find(xmin).text) ymin float(bndbox.find(ymin).text) xmax float(bndbox.find(xmax).text) ymax float(bndbox.find(ymax).text) w (xmax - xmin) / img_width h (ymax - ymin) / img_height xc (xmin xmax) / 2 / img_width yc (ymin ymax) / 2 / img_height lines.append(f{class_id} {xc:.6f} {yc:.6f} {w:.6f} {h:.6f}) if lines: with open(os.path.join(txt_dir, txt_name), w) as f: f.write(\n.join(lines))这段脚本是完整的转换逻辑。逻辑说明先用ET.parse解析 xml 拿到图片的宽高然后遍历每个 object 节点把像素坐标除以宽高完成归一化。参数说明category_map是你手工维护的类别映射表遇到不认识的类别会打印提示而不是静默丢弃宽高的归一化用img_width和img_height如果你转出来的框全部偏左或偏上十有八九是图片宽高对调了。脚本运行完后拿 3.1 的可视化脚本抽查个三五张确认框的位置和大小合理再进训练流程。特别注意一个隐蔽问题如果同一张图里有两个相同的烟盒转换脚本必须生成两行标签而不是只保留最后一个。5. 训练翻车排查234 张图这个量级特有的三个经典坑5.1 坑一样本太少验证集只有十几张图mAP 抖动误差极大234 张这个体量按 8:2 划分后验证集只有 46 张再平均到每个类别可能就二三十张。这种小验证集上跑出来的 mAP 每次训练可能差三五个点不是模型变好了是随机性太大。现象连续跑两次完全相同的命令metrics/mAP50-95(B)波动超过 5%。原因验证集太小少数几张难样本的预测结果被平均到整体指标里就会产生明显波动。解决如果你只是想验证流程通不通mAP50 在 0.8 以上就可以但真想知道模型泛化能力要做 K 折交叉验证或干脆用留一验证把 234 张全用上验证集轮流换最后取平均。YOLOv8 里没有内置 K 折需要自己按 5.1 里的划分脚本换 5 次随机种子分别训代价是训练时间乘 5但这也是小数据集评估的必经之路。5.2 坑二类别不平衡严重全是硬盒烟盒软盒样本只有十几张烟盒数据集这种场景大概率是依托某个具体场景采集的很可能 90% 以上是同一个角度的同一种烟盒。现象训练时 val 集的 loss 一直很低但实际部署到新场景比如换个烟盒品牌、换个光照角度检测框就乱飘。原因模型没有见过足够多样的正样本把背景里的纹理误当成烟盒特征。解决数据增强参数可以适当调大。YOLOv8 里常用做法是调hsv_h、hsv_s、hsv_v增强颜色扰动再开随机翻转。命令行里这样写yolo train data./烟盒数据集/data.yaml modelyolov8n.pt epochs100 imgsz640 batch16 hsv_h0.02 hsv_s0.8 hsv_v0.8 fliplr0.5 flipud0.1参数说明hsv_h0.02是色调扰动幅度0.02 已经不小再大会导致烟盒颜色失真hsv_s0.8和hsv_v0.8是饱和度和明度扰动光照变化大的场景建议保持这个量级flipud0.1垂直翻转要慎重因为真实场景里烟盒很少倒着出现翻转太多反而引入无效特征。如果软盒样本实在太少就尽量不要把nc设成 2而是把软盒并入同一类先训一个单类别检测器后续再收集样本迭代。5.3 坑三标签文件里夹杂空 txt 或损坏文件训练直接中断现象训练到中途报错Not all training images have corresponding labels或者某个 batch 读取标签时越界。原因数据集打包时把一些没有目标的图片也存了空 txt或者在转换过程中产生了只有 class_id 没有坐标的残缺行。解决训练前先跑一个全量校验把所有 txt 逐行 check 一遍find 烟盒数据集/labels -name *.txt -exec awk {if (NF ! 5) print FILENAME, NR, $0} {} \;这条命令用 awk 检查每个标签文件的每一行NF ! 5表示字段数不是 5只要输出结果就说明有坏行。正常情况应该没有任何输出。另外检查有没有空文件find 烟盒数据集/labels -name *.txt -size 0 -print如果有空文件确认对应图片里是否真有目标没有目标的可以直接留着YOLOv8 支持空标签如果图里有目标但标签为空那就是漏标得重新标注或用半自动工具补。做这个校验只要几十秒但能省下训练跑到一半报错的数小时等待这个习惯我强烈建议你养成。6. 小模型的增量之路用已训权重做迁移学习的三个参数调整数据集只有 234 张最大的风险是模型容量过剩导致过拟合。很多人无脑加载yolov8s.pt或yolov8m.pt开训结果是几十个 epoch 后训练集 mAP 逼近 1验证集却卡在 0.6 上不去。我的习惯是先用yolov8n.pt预训练权重做一次完整训练看验证集指标的可接受程度再考虑要不要升级到 s 模型。如果你的检测精度确实不够直接换大模型并不是最优解应该先检查这三点有没有做足够的数据增强、要不要冻结浅层更快收敛、迁移学习时学习率该不该调低。对于 234 张图的小数据集迁移学习默认参数建议这样设yolo train data./烟盒数据集/data.yaml modelyolov8n.pt epochs150 imgsz640 batch16 lr00.005 lrf0.01 freeze10参数说明lr00.005比默认的 0.01 减半因为小数据集下学习率太大会让预训练权重被快速破坏导致学到的泛化特征丢失lrf0.01是学习率最终衰减到初始值的比例配合余弦衰减可以训练后期做细粒度收敛freeze10表示冻结模型前 10 层不参与梯度更新这些层学到的是通用视觉特征边缘、颜色、纹理对烟盒检测同样适用冻结它们能显著减少过拟合。等第一次训练完如果效果还差再解冻前层用lr00.001微调一轮而不是一上来就全量放开。推理验证阶段用 best.pt 对一张没见过的烟盒图片跑检测检查框的 confidence 阈值默认 0.25 是否合适。如果框很多、误检高调高conf0.4如果漏检多调低到conf0.10。实际部署时建议根据你的场景做一次置信度扫描选一个在验证集上 F1 最高的阈值这比用默认值可靠得多。yolo predict model./runs/detect/train/weights/best.pt source./test_imgs conf0.25 saveTrue这就是我从解压 zip 到训出可用模型的全流程。最后说个自己的习惯每次拿到新的数据集我会先花一个小时做标签可视化、格式校验、split 划分再开始训练不要嫌这个准备阶段麻烦把训到一半翻车返工的几小时时间省出来这笔账太划算了。希望这篇实操笔记也能帮你把这个烟盒数据集直接变成可上线的检测能力。本文还有配套的精品资源点击获取
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。