简介:本资源为面向YOLO系列目标检测算法的烟盒识别数据集,适合从事目标检测学习、模型训练与验证的开发者及研究者使用,可解决烟盒类目标识别任务中数据准备繁琐的问题。压缩包共2000个文件,约192.79MB,包含1545个xml标注文件与455个txt标注文件,分别对应VOC格式与YOLO格式,便于不同框架直接读取。数据集已划分完毕,并附带data.yaml配置文件,兼容yolov5、yolov7、yolov8、yolov9、yolov10及yolo11等主流版本,标注采用类别索引与归一化中心点、宽高比例,规范统一。目前已有176人学习下载,读者可直接用于训练、验证与测试,省去格式转换与划分步骤,快速搭建烟盒检测基线模型,并在此基础上开展调参与优化实验。
1. 烟盒数据集与 YOLO 落地:1934 张图像带标签到底能训出什么
拿到「yolo算法-烟盒数据集-1934张图像带标签.zip」这个标题,多数人第一反应是解压、改 data.yaml、开训。但真正决定成败的不是模型选型,而是这 1934 张图像背后的分布:烟盒在货架、柜台、手持、俯拍等场景下的尺度跨度、遮挡比例、类别定义是否统一。烟盒检测属于典型的小目标 + 密集堆叠场景,一包烟在 1080P 画面里可能只占 40×60 像素,且同类包装颜色高度相似,模型很容易把「中华」和「利群」混为一类。这个数据集适合做零售陈列稽核、烟草专卖巡检、自动结算台的商品识别原型,也适合作为 YOLO 微调练手数据。但 1934 张的体量决定了它只能支撑单类别或少量类别的检测任务,想直接训出几十个品牌细分类,样本量是不够的。下面按「先看清数据、再跑通基线、最后调优避坑」的顺序拆开讲。
2. 拆开压缩包先做什么:烟盒数据集的标注格式与分布核查
2.1 判断标签是 YOLO txt 还是 VOC xml
解压后第一件事不是训练,是确认标签格式。YOLO 系列要求每张图对应一个同名 .txt,每行class_id cx cy w h,坐标全部归一化到 0~1。如果压缩包里是 xml,说明是 VOC 格式,需要转换。用下面这段脚本快速统计:
import os, glob from collections import Counter img_dir = "images" lbl_dir = "labels" imgs = glob.glob(os.path.join(img_dir, "*")) lbls = glob.glob(os.path.join(lbl_dir, "*.txt")) print("图像数:", len(imgs), "标签数:", len(lbls)) cls_counter = Counter() bad_lines = 0 for f in lbls: with open(f) as fp: for line in fp: parts = line.strip().split() if len(parts) != 5: bad_lines += 1 continue cls_counter[int(parts[0])] += 1 # 检查归一化坐标是否越界 vals = list(map(float, parts[1:])) if any(v < 0 or v > 1 for v in vals): bad_lines += 1 print("类别分布:", cls_counter) print("异常行数:", bad_lines)逻辑说明:先比对图像与标签数量是否一一对应,缺失标签的图会在训练时被跳过或报错。cls_counter反映类别是否均衡,如果某一类占比超过 80%,说明数据偏斜,需要过采样或加权。坐标越界检查能提前发现标注工具导出的脏数据,这类脏样本在训练中会产生巨大 loss 波动。参数上,class_id从 0 开始,若你的 data.yaml 里 nc 写错,训练不会报错但类别全乱。
2.2 用可视化抽查 20 张图确认框贴合度
统计只能看数量,框画得准不准必须肉眼验。写一个把 YOLO txt 画回图上的脚本,随机抽 20 张:
import cv2, random, glob, os def draw_yolo(img_path, lbl_path): img = cv2.imread(img_path) h, w = img.shape[:2] with open(lbl_path) as f: for line in f: c, cx, cy, bw, bh = map(float, line.split()) x1 = int((cx - bw/2) * w); y1 = int((cy - bh/2) * h) x2 = int((cx + bw/2) * w); y2 = int((cy + bh/2) * h) cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2) return img samples = random.sample(glob.glob("images/*"), 20) for p in samples: name = os.path.splitext(os.path.basename(p))[0] lp = f"labels/{name}.txt" if os.path.exists(lp): cv2.imwrite(f"vis_{name}.jpg", draw_yolo(p, lp))逻辑说明:归一化坐标乘回宽高得到像素框,画出来直接看框是否包住烟盒、有没有漏标。常见问题是框只标了正面没标侧面,或者整条烟和单包烟混标成同一类。参数上注意 cv2 读图是 BGR,不影响框位置。抽查发现超过 5% 的图有明显漏标,就要考虑重新标注或清洗,否则模型学到的就是「有时该检有时不该检」的玄学行为。
2.3 划分训练集与验证集的比例选择
1934 张的体量,建议按 8:1:1 划分 train/val/test,即约 1547/193/194。不要用随机划分,因为同一场景连拍的图如果同时进训练和验证,验证指标会虚高。更稳的做法是按场景或拍摄批次分组划分。下面用 sklearn 做一次分层抽样:
import glob, os, shutil, random from sklearn.model_selection import train_test_split files = [os.path.splitext(os.path.basename(p))[0] for p in glob.glob("images/*")] random.seed(42) train, temp = train_test_split(files, test_size=0.2, random_state=42) val, test = train_test_split(temp, test_size=0.5, random_state=42) for split, names in [("train", train), ("val", val), ("test", test)]: os.makedirs(f"dataset/{split}/images", exist_ok=True) os.makedirs(f"dataset/{split}/labels", exist_ok=True) for n in names: shutil.copy(f"images/{n}.jpg", f"dataset/{split}/images/{n}.jpg") shutil.copy(f"labels/{n}.txt", f"dataset/{split}/labels/{n}.txt") print(len(train), len(val), len(test))逻辑说明:固定随机种子保证可复现,两级 split 得到 8:1:1。参数test_size=0.2先切出 20% 作为临时集,再对半分成 val 和 test。如果数据集本身有类别字段,把stratify加上能保证各类比例一致。划分完检查三个目录的图像数之和是否等于 1934,少一张都说明有文件命名不匹配。
3. 用 YOLOv8 跑通烟盒检测基线:环境、配置与首轮训练
3.1 环境搭建与依赖版本锁定
YOLOv8 通过 ultralytics 包安装,Python 建议 3.9~3.11。CUDA 版本要和显卡驱动匹配,否则会退回 CPU 训练,1934 张在 CPU 上跑 100 epoch 可能要一整天。安装命令:
conda create -n smoke python=3.10 -y conda activate smoke pip install ultralytics==8.2.0 pip install torch==2.1.0 torchvision==0.16.0 --index-url https://download.pytorch.org/whl/cu118 python -c "import torch; print(torch.cuda.is_available())"逻辑说明:先装 ultralytics 再指定 torch 版本,避免依赖冲突。最后一行必须输出 True,输出 False 说明 CUDA 不可用,要检查驱动和 torch 的 cu 版本是否对应。参数上 cu118 对应 CUDA 11.8,如果你的驱动较新可以换 cu121。这一步翻车的典型现象是训练日志里 device 显示 cpu,速度慢十倍。
3.2 写对 data.yaml 的四个字段
data.yaml 是训练入口,字段写错直接报错或静默出错:
path: /home/user/smoke/dataset train: train/images val: val/images test: test/images nc: 1 names: ['cigarette_pack']逻辑说明:path是根目录,train/val/test是相对路径,不要写绝对路径混用。nc是类别数,烟盒单类就写 1,写多了模型会分配多余输出通道导致 mAP 异常。names顺序必须和标签里的 class_id 对应,id 0 对应列表第一个。常见错误是 nc 写 2 但标签只有 0,训练不报错但第二类永远检不出。
3.3 首轮训练命令与关键超参
基线训练不要一上来就调参,先用默认配置跑通:
yolo detect train \ data=data.yaml \ model=yolov8n.pt \ epochs=100 \ imgsz=640 \ batch=16 \ lr0=0.01 \ patience=20 \ device=0 \ project=runs/smoke \ name=baseline逻辑说明:yolov8n.pt是最小的预训练权重,1934 张数据量下 n 或 s 足够,用 l 或 x 容易过拟合。imgsz=640是默认输入尺寸,烟盒偏小可以后续提到 960。batch=16按显存调,8G 显存跑 640 大概能到 16。patience=20表示 20 轮无提升就早停,省时间。lr0=0.01是初始学习率,微调场景可以降到 0.001。训练日志重点看 box_loss 是否稳定下降、mAP50 是否在 50 轮后还在涨。
3.4 看结果:mAP、混淆矩阵与 PR 曲线怎么读
训练完在 runs/smoke/baseline 下会生成 results.png、confusion_matrix.png、PR_curve.png。mAP50 到 0.85 以上算可用,mAP50-95 到 0.6 以上算不错。混淆矩阵看的是有没有把背景误检成烟盒(假阳性),烟盒场景里货架纹理、价签容易被误检。PR 曲线看的是不同置信度下的召回和精确率权衡,如果曲线在中段塌陷,说明模型对某些尺度或遮挡样本学得不好。这些图不是装饰,是判断要不要继续调参的依据。
4. 烟盒检测的调优与避坑:小目标、密集堆叠与类别混淆
4.1 小目标检测:提升输入分辨率与锚框适配
烟盒在整图里占比小,640 输入下可能只有 30 像素宽。两个手段:一是把 imgsz 提到 960 或 1280,二是用更密集的特征层。YOLOv8 默认 P3 层 stride 8,对小目标已经比较友好,但 960 输入能显著提升召回:
yolo detect train data=data.yaml model=yolov8s.pt epochs=150 imgsz=960 batch=8 lr0=0.005 device=0逻辑说明:分辨率翻倍,显存占用约翻四倍,batch 要相应降到 8。模型从 n 换到 s 增加容量,配合更高分辨率能更好拟合小目标。参数lr0降到 0.005 是因为输入变大后梯度尺度变化,学习率太高容易震荡。代价是训练时间增加约 2~3 倍,但小目标 mAP 通常能涨 5~10 个点。
4.2 密集堆叠场景下的 NMS 与置信度阈值
货架上烟盒紧挨着,NMS 的 IoU 阈值设太高会把相邻的框合并,设太低会保留重复框。默认 NMS IoU 是 0.7,密集场景建议降到 0.5~0.6。推理时置信度阈值默认 0.25,误检多就提到 0.4,漏检多就降到 0.15。这两个参数在推理命令里调:
yolo detect predict model=runs/smoke/baseline/weights/best.pt source=test_images imgsz=960 conf=0.35 iou=0.55 save=True逻辑说明:conf控制输出框的最低置信度,iou控制 NMS 合并阈值。密集场景先固定 conf=0.35 看效果,再微调 iou。如果发现同一包烟出两个框,降 iou;如果相邻两包只出一个框,升 iou。这两个参数没有万能值,必须拿验证集的实际画面调。
4.3 数据增强:Mosaic、MixUp 在烟盒场景的取舍
YOLOv8 默认开启 Mosaic,把四张图拼成一张,对小目标和密集场景有帮助。但烟盒有固定长宽比和包装纹理,Mosaic 过度使用会让模型学到不真实的拼接边界。建议训练后期关闭 Mosaic:
# 在 data.yaml 同级建 hyp.yaml mosaic: 0.5 # 从默认 1.0 降到 0.5 mixup: 0.0 # 烟盒场景不建议开 mixup degrees: 10.0 # 旋转角度限制在 10 度内 scale: 0.5 # 缩放幅度 fliplr: 0.5 # 水平翻转 hsv_h: 0.015 # 色调抖动,模拟不同光照逻辑说明:mosaic: 0.5表示 50% 概率做拼接,保留一定增强但不过度。mixup会把两张图叠加,烟盒纹理叠加后语义混乱,关掉。degrees限制旋转,因为烟盒在货架上基本是正放或小角度倾斜,大角度旋转不符合真实分布。hsv_h模拟不同色温的灯光,零售场景常见。
4.4 类别混淆:单类还是多类,标签一致性怎么保证
如果数据集里不同品牌烟盒标成了不同类,但样本量每类只有几十张,模型会严重混淆。1934 张的体量,建议先做单类「烟盒」检测,把定位做稳,再考虑用分类模型做品牌识别。如果坚持多类,必须保证每个类至少 200 张以上,且标注时品牌边界清晰。检查标签一致性的方法是把同一类的所有框裁出来拼成一张大图,肉眼看是否混入了其他品牌。
5. 避坑与排查:烟盒数据集训练中最容易翻车的五件事
5.1 现象:训练 loss 正常下降但 mAP 一直是 0
原因:data.yaml 的names和标签 class_id 不匹配,或者 val 路径下没有标签文件。模型在学,但验证时找不到对应类别,mAP 计算为 0。解决:打印 val 目录的文件数,确认 images 和 labels 一一对应,再核对 names 列表顺序。
5.2 现象:推理时同一包烟出多个重叠框
原因:NMS 的 iou 阈值过高,或者模型对同一目标输出了多个 anchor 的正样本。解决:推理时把iou从 0.7 降到 0.5,训练时检查标签是否有重复框。如果标签里同一目标被标了两次,模型会学到重复输出。
5.3 现象:模型把货架价签、条形码误检成烟盒
原因:负样本不足,背景多样性不够。1934 张里如果全是烟盒特写,模型没见过「没有烟盒的货架」。解决:加入 10%~20% 的纯背景图(无标注),或者用难例挖掘把误检图加入训练集重新标。
5.4 现象:训练到 50 轮后 mAP 突然掉下去
原因:学习率过高导致后期震荡,或者过拟合。解决:加 cosine 学习率衰减,或者把patience调小提前停。检查训练集和验证集的 loss 曲线,如果 train loss 继续降但 val loss 上升,就是过拟合,需要加数据增强或减模型容量。
5.5 现象:GPU 显存够但训练速度极慢
原因:数据加载是瓶颈,num_workers 默认值太小,或者图像尺寸太大导致预处理耗时。解决:把workers提到 8,确认图像存在本地 SSD 而非网络盘。用nvidia-smi看 GPU 利用率,如果长期低于 50%,就是数据管道卡住了。
6. 从 1934 张到可用模型:验证策略与一个提点技巧
训练完不是看 mAP 就结束,要拿真实场景图做端到端验证。我一般会留出 50 张完全没参与训练的实拍图,覆盖不同光照、角度、遮挡,跑推理后人工数漏检和误检。如果 mAP 0.9 但实拍漏检 20%,说明验证集和真实分布有差距,得补数据。
一个提点技巧是测试时增强(TTA):推理时对同一张图做水平翻转、多尺度缩放,把结果融合。YOLOv8 推理命令加augment=True即可开启:
yolo detect predict model=best.pt source=real_test imgsz=960 conf=0.3 iou=0.55 augment=True save=True逻辑说明:TTA 会跑多次推理再合并,速度慢 2~3 倍,但小目标和遮挡场景下召回通常能涨 2~5 个点。参数augment=True开启,适合对精度要求高、对速度不敏感的离线稽核场景。实时视频流不建议开,帧率扛不住。
另一个习惯是每次训练完把 best.pt 的推理结果和上一版做 A/B 对比,固定同一批测试图,记录漏检数和误检数,而不是只看 mAP 小数点后的变化。mAP 涨 0.01 但实拍漏检多了 3 个,这版就不能上。烟盒检测这种场景,业务方关心的是「有没有漏」,不是「平均精度多少」。我踩过最深的坑就是盯着 mAP 调了两周,上线后发现货架最底层那排烟盒因为反光全部漏检,回头补了 200 张低角度反光样本才解决。数据集的分布永远比模型结构更决定成败,1934 张够不够,取决于它覆盖了多少真实场景。希望帮到你。
本文还有配套的精品资源,点击获取