简介:本资源为面向YOLO系列目标检测算法的烟盒识别数据集,适合从事目标检测学习、模型训练与验证的开发者及研究人员使用,可解决烟盒类目标识别任务中数据准备繁琐的问题。压缩包共2000个文件,约192.79MB,包含1545个xml标注文件与455个txt标注文件,分别对应VOC格式与YOLO格式,便于不同框架直接读取。数据集已按训练与验证需求划分完毕,并附带data.yaml配置文件,可无缝适配yolov5、yolov8、yolov9、yolov7、yolov10及yolo11等主流算法。YOLO格式标注采用类别索引与归一化中心点、宽高比例,符合标准训练输入要求。目前已有176人学习下载,读者可直接获得完整标注数据与配置,省去采集与标注成本,快速投入模型训练、对比实验与效果验证,适合作为课程设计、科研实验或工程原型的训练基础。
1. 烟盒检测数据集上手:1934 张图带标签,为什么它值得先跑一遍
拿到一个目标检测数据集,最怕的不是数据少,而是格式乱、划分乱、标签对不上。这份烟盒数据集在这几点上做得比较干净:1934 张图像全部带标签,已经划分好训练集和验证集,同时提供 YOLO 格式的 txt 和 VOC 格式的 xml 两套标注,还附带data.yaml配置文件。也就是说,你不需要自己写脚本去切分数据、转换坐标,解压完改一下路径就能直接开训。
它适合谁?如果你正在做 yolo系列算法目标检测数据集的练手项目,或者想验证 yolov8目标识别 在自己环境里的完整链路,这份数据集的体量刚好——1934 张不算大,单卡几十分钟能跑完一轮,但类别单一、目标明确,足够暴露数据加载、标签解析、评估指标这些环节的问题。烟盒这个目标本身也有意思:长方体、有反光、摆放角度多变,拿来观察模型对小目标和遮挡的响应挺直观。
需要先明确一点:数据集只解决“数据从哪来”,不解决“模型怎么调”。下面按“先看懂标签格式,再跑通训练,最后避开几个高频坑”的顺序拆开讲,每一步都落到能直接复制的命令和参数上。
2. 标签格式与目录结构:先搞懂 YOLO txt 和 VOC xml 的对应关系
2.1 两种标注格式的字段含义
YOLO 格式每个 txt 文件对应一张图,每行一个目标,字段是<class> <x_center> <y_center> <width> <height>。这里最容易翻车的是坐标归一化:x_center和y_center是框中心点相对整张图宽高的比例,width和height也是比例值,全部落在 0 到 1 之间。很多人第一次拿到 txt 看到0.523 0.137 0.088 0.203这种数,误以为是像素坐标,直接乘图像尺寸就错了——它已经是比例,要还原像素得再乘宽高。
VOC 格式的 xml 则是绝对像素坐标,xmin/ymin/xmax/ymax直接对应图像上的位置。两套标签放在不同文件夹,好处是你用 YOLO 系列就直接读 txt,想换到别的框架或者做格式对比就用 xml。判断一份 YOLO 标签是否合法,我一般先跑一个快速校验:所有数值是否在 [0,1],width和height是否大于 0,类别索引是否超出nc。
import os label_dir = "labels/train" bad = [] for name in os.listdir(label_dir): if not name.endswith(".txt"): continue with open(os.path.join(label_dir, name)) as f: for i, line in enumerate(f): parts = line.strip().split() if len(parts) != 5: bad.append((name, i, "字段数不对")) continue cls, x, y, w, h = parts vals = list(map(float, [x, y, w, h])) # 归一化坐标必须落在 0~1,宽高必须为正 if any(v < 0 or v > 1 for v in vals) or vals[2] <= 0 or vals[3] <= 0: bad.append((name, i, line.strip())) print("异常条目数:", len(bad)) for b in bad[:10]: print(b)这段脚本做三件事:检查每行是否恰好 5 个字段、坐标是否越界、宽高是否非正。参数上label_dir换成你的实际路径即可。跑完如果异常条目为 0,说明标签本身没问题,后面训练出问题就不用怀疑标注了。
2.2 目录结构与 data.yaml 的对应
数据集已经划分好,典型结构是images/train、images/val配labels/train、labels/val,根目录放data.yaml。data.yaml里关键字段是train、val两个路径,以及nc(类别数)和names(类别名列表)。烟盒数据集类别单一,nc通常是 1,names里就一个类别名。
这里有个高频误解:data.yaml里的路径写的是相对于谁的路径?YOLOv5/v8 默认按“相对于数据集根目录”解析,但不同版本、不同启动方式下行为会变。稳妥做法是直接写绝对路径,或者确认你从哪个目录执行训练命令。我一般会把data.yaml打开逐行核对,把train和val改成绝对路径,省得后面报No labels found还找不到原因。
# data.yaml 关键字段示例 path: /data/smoke_dataset # 数据集根目录 train: images/train # 相对 path 的训练图目录 val: images/val # 相对 path 的验证图目录 nc: 1 # 类别数,烟盒通常为 1 names: 0: cigarette_box # 类别名,按你的实际标注改path是根,train/val相对它拼接。如果你的目录层级和这个不一致,要么改 yaml,要么调整文件夹。别小看这一步,训练脚本读不到图,九成是这里路径没对上。
3. 用 YOLOv8 跑通训练:从环境到第一轮验证指标
3.1 环境准备与依赖版本
YOLOv8 走 ultralytics 包,安装本身不复杂,坑在版本和 CUDA 匹配。常见做法是先用 conda 或 venv 建一个干净环境,再装对应 CUDA 版本的 torch,最后装 ultralytics。如果你机器上已经有 torch,先确认它能不能正常调用 GPU,否则训练会默默退回 CPU,速度差几十倍。
# 建环境并安装,torch 版本按你的 CUDA 选 conda create -n yolo_smoke python=3.10 -y conda activate yolo_smoke pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118 pip install ultralytics # 验证 GPU 是否可用 python -c "import torch; print(torch.cuda.is_available(), torch.cuda.get_device_name(0))"torch.cuda.is_available()返回True才说明 GPU 可用。如果返回False,先别急着训,检查驱动和 CUDA 版本,不然跑一晚上发现用的是 CPU,血泪经验。ultralytics装完会自带yolo命令行工具,后面直接用它。
3.2 启动训练与关键参数
训练命令本身很短,参数才是决定成败的地方。下面这条是我在单卡上跑烟盒数据集的常用配置,imgsz设 640,batch按显存给,epochs先跑 100 看收敛趋势。
yolo detect train \ data=/data/smoke_dataset/data.yaml \ model=yolov8n.pt \ epochs=100 \ imgsz=640 \ batch=16 \ lr0=0.01 \ patience=20 \ project=runs/smoke \ name=exp1逐项说明:data指向你的 yaml;model=yolov8n.pt用 nano 版预训练权重,数据集小、类别单一,nano 足够且快,想提精度再换yolov8s.pt;imgsz=640是输入分辨率,烟盒在图中占比不大时可以试 640 或 768;batch=16显存不够就降到 8 或 4;lr0=0.01是初始学习率,小数据集别设太大;patience=20表示 20 轮没提升就早停,省时间。project和name决定结果存哪,方便你对比多次实验。
训练过程中重点看box_loss、cls_loss和mAP50。box_loss下降说明框在收敛,cls_loss下降说明分类在学,mAP50是验证集上的主指标。如果box_loss一直震荡不降,先回去查标签坐标是否越界;如果mAP50卡在很低的值,多半是类别索引或路径问题。
3.3 验证与推理:确认模型真的学到了
训练完别只看最后一行的数字,跑一次独立验证和几张图的推理,眼见为实。验证命令会输出每类的 P、R、mAP,推理则能直接看图上的框。
# 在验证集上评估 yolo detect val \ model=runs/smoke/exp1/weights/best.pt \ data=/data/smoke_dataset/data.yaml \ imgsz=640 # 对单张图推理并保存结果 yolo detect predict \ model=runs/smoke/exp1/weights/best.pt \ source=/data/smoke_dataset/images/val \ save=True \ conf=0.25val的imgsz要和训练一致,否则指标会偏。predict里conf=0.25是置信度阈值,烟盒这种目标可以先用 0.25 看召回,漏检多就降到 0.1,误检多就升到 0.4。结果图存在runs/detect/predict下,抽十几张看框的位置和置信度,比盯数字有用。如果框明显偏移,回到标签校验那一步;如果框对但类别错,检查names顺序和训练时是否一致。
4. 避坑与排查:标签、路径、显存这三类问题最常见
4.1 训练报 No labels found
现象:启动训练后直接报找不到标签,或者labels为空。原因通常是data.yaml里train/val路径写错,或者图片和标签目录没对应上——YOLO 默认按“把images替换成labels、扩展名换成.txt”去找标签。解决:先确认images/train下每张图在labels/train下有同名 txt;再把 yaml 里的路径改成绝对路径,排除相对路径歧义。改完重跑,别在报错状态下反复调参。
4.2 坐标越界导致框画到图外
现象:推理结果里框跑到图像边界外,或者训练 loss 异常。原因:YOLO txt 的坐标必须是归一化值,如果误把像素坐标写进去,数值会大于 1。解决:跑 2.1 里的校验脚本,把所有越界行找出来。如果是自己转换格式时出的错,检查转换脚本有没有除以图像宽高。这份数据集自带 txt,正常不会越界,但如果你混用了 xml 转换的结果,就要重点查。
4.3 显存不足训练中断
现象:训练几轮后报 CUDA out of memory。原因:batch或imgsz超过显存。解决:优先降batch,从 16 降到 8 再到 4;还不行就降imgsz到 512。另外workers设太大也会占内存,一般设 4 到 8。别硬扛大 batch,小 batch 配合梯度累积也能收敛,只是慢一点。
4.4 验证指标虚高或虚低
现象:mAP50高得离谱或低得离谱。原因:验证集和训练集图片重叠,或者val路径指到了训练集。解决:确认images/val和images/train没有重复文件,用文件名或哈希去重。这份数据集已经划分好,正常不会重叠,但如果你自己重新切分过,务必检查。指标虚低则多半是imgsz和训练不一致,或者conf阈值设太高。
4.5 类别名对不上导致评估错乱
现象:训练正常,但评估时类别显示成class_0之类,或者 P/R 明显不对。原因:data.yaml的names和标签里的类别索引没对齐。解决:确认nc等于names长度,且标签里的<class>索引从 0 开始、不超过nc-1。烟盒数据集类别单一,nc=1、索引只有 0,这种情况一般不会错,但换成多类数据集时这是高频坑。
5. 进阶技巧:用 xml 做交叉校验,把数据质量钉死
跑通训练只是第一步,真正让模型稳的是数据质量。这份数据集同时给了 YOLO txt 和 VOC xml,很多人只用 txt,把 xml 晾在一边,其实它是最好的交叉校验工具。思路很简单:同一张图,txt 里的归一化框还原成像素后,应该和 xml 里的xmin/ymin/xmax/ymax基本重合。如果偏差大,说明其中一套标签有问题。
import os import xml.etree.ElementTree as ET from PIL import Image img_dir = "images/train" txt_dir = "labels/train" xml_dir = "annotations/train" # 按你的实际目录改 def yolo_to_xyxy(line, w, h): cls, x, y, bw, bh = line.split() x, y, bw, bh = map(float, (x, y, bw, bh)) # 归一化中心宽高 -> 像素左上右下 x1 = (x - bw / 2) * w y1 = (y - bh / 2) * h x2 = (x + bw / 2) * w y2 = (y + bh / 2) * h return x1, y1, x2, y2 mismatch = [] for name in os.listdir(txt_dir): if not name.endswith(".txt"): continue stem = name[:-4] img_path = os.path.join(img_dir, stem + ".jpg") xml_path = os.path.join(xml_dir, stem + ".xml") if not (os.path.exists(img_path) and os.path.exists(xml_path)): continue w, h = Image.open(img_path).size with open(os.path.join(txt_dir, name)) as f: yolo_lines = [l for l in f if l.strip()] tree = ET.parse(xml_path) xml_boxes = [] for obj in tree.findall("object"): b = obj.find("bndbox") xml_boxes.append(( float(b.find("xmin").text), float(b.find("ymin").text), float(b.find("xmax").text), float(b.find("ymax").text) )) if len(yolo_lines) != len(xml_boxes): mismatch.append((stem, "数量不一致", len(yolo_lines), len(xml_boxes))) continue for line, xb in zip(yolo_lines, xml_boxes): yb = yolo_to_xyxy(line, w, h) # 允许 2 像素误差 if max(abs(a - b) for a, b in zip(yb, xb)) > 2: mismatch.append((stem, "坐标偏差", yb, xb)) break print("不一致样本数:", len(mismatch)) for m in mismatch[:10]: print(m)这段脚本把 txt 的归一化框还原成像素坐标,再和 xml 的绝对坐标逐框比对,允许 2 像素误差。img_dir、txt_dir、xml_dir按实际路径改。跑完如果mismatch为空,说明两套标签一致,数据可以放心用;如果有偏差,优先信 xml 还是 txt 要看你的转换流程,但至少你知道哪些样本需要人工复核。
除了交叉校验,还有两个习惯值得养成。一是训练前把data.yaml和标签校验脚本一起跑一遍,当作数据体检;二是每次换模型版本(比如从 yolov8 换到 yolo11)时,先用同一份数据跑 10 个 epoch 做冒烟测试,确认链路通再上完整训练。烟盒数据集体量小,冒烟测试十几分钟就能出结果,比训到一半发现路径错了划算得多。
从那以后我每次拿到新数据集,都强制先跑一遍标签校验和 xml 交叉比对,再动训练命令。数据没问题,后面调参才有意义。希望帮到你。
本文还有配套的精品资源,点击获取