YOLO吸烟烟盒数据集:从标注解析到yolov8训练避坑指南

发布时间:2026/9/28 13:07:44
YOLO吸烟烟盒数据集:从标注解析到yolov8训练避坑指南 简介一套面向yolo系列算法的吸烟与烟盒目标检测数据集共2117张带标签图像适合使用yolov5、yolov8、yolov9、yolov7、yolov10、yolo11等框架进行模型训练与验证。资源已按训练与验证需求划分好数据集并附带data.yaml配置文件可帮助目标检测学习者与开发者快速上手免去自行标注和整理标签的繁琐流程。包内文件总数2000个其中1691个xml文件为VOC格式标签309个txt文件为YOLO格式标签两种格式分别存放满足不同训练工具的需求压缩包整体约241.97MB。标签内容包含类别索引、目标框中心坐标及宽高比例结构清晰便于直接读取或转换。目前已有138人学习下载适合正在做吸烟行为识别、公共场所安全监测等项目的开发者也适合作为yolo系列算法入门的实战数据集。1. 一份yolo算法目标检测数据集到手先看“带标签”三个字值不值得花时间做目标检测最恼火的往往不是调参而是找数据。这份“吸烟-烟盒”数据集一共2117张图像带完整标签适配yolov5到yolo11整条yolo系列算法目标检测数据集体系解压就能直接开始train。我用它把yolov8从零跑到收敛验证了从数据解析、标注检查到训练部署的全流程也踩了几个典型的标签坑。它适合两类人一是刚接触yolo系列算法的初学者想用现成数据跑通“训练—验证—测试”闭环二是在做禁烟检测、工厂安全行为识别的工程师需要一个干净的基准数据做迁移学习。先说结论这份数据在同类里算省心的前提是你会正确地读它的目录和标注格式——这正是下面要拆开讲的部分。2. 压缩包里的目录与标注格式先把YOLO坐标和VOC坐标对齐2.1 压缩包打开后的三层结构拿到压缩包先别急着解压乱扔。文件数量不算少2117张图每张对应一个txt和一份xml再加上配置文件解压后少说几千个文件。我习惯在Linux下用unzip指定编码解开避免中文目录名乱码mkdir -p ~/datasets/smoking unzip -O GBK yolo算法-吸烟-烟盒数据集-2117张图像带标签.zip -d ~/datasets/smoking cd ~/datasets/smoking tree -L 2说明-O GBK是为Windows下常见的中文编码做的兼容处理不加的话解压出来的中文文件名在Linux上会显示成乱码。tree -L 2只显示两层目录方便快速确认结构。解压后内部结构按常见目标检测数据集组织方式排布smoking/ ├── images/ │ ├── train/ # 训练集图像 │ ├── val/ # 验证集图像 │ └── test/ # 测试集图像 ├── labels/ # yolo格式txt子目录与images一一对应 │ ├── train/ │ ├── val/ │ └── test/ ├── voc_xml/ # voc格式xml与labels子目录一一对应 └── data.yaml # 数据集配置文件注意这里的关键点txt和xml是两套独立标注不是互相覆盖的关系。训练时真正被yolo读取的是labels下的txt文件voc_xml更多用于格式迁移、人工质检或者转成其他框架比如mmrotate、detectron2时用。2.2 YOLO txt与VOC xml归一化坐标的正确读法txt文件每一行对应一个目标框格式是摘要里写明的五段式class x_center y_center width heightclass是类别索引从0开始x_center、y_center、width、height全部是相对图像宽度和高度的比例值范围0到1。举个例子img_0133_136.txt里可能有这样两行0 0.5123 0.4378 0.1842 0.2789 1 0.7012 0.3654 0.0821 0.0917按数据集常见设定0代表吸烟区域1代表烟盒具体含义以data.yaml里的names列表为准。第一行的意思是一个宽约18.4%图像宽度、高约27.9%图像高度的框中心点在图像水平51.2%、垂直43.8%的位置。为了验证坐标有没有问题我会写一个反向换算脚本把归一化坐标还原成像素坐标再叠加到图上检查from PIL import Image img Image.open(images/train/img_0133_136.jpg) W, H img.size line 0 0.5123 0.4378 0.1842 0.2789.split() cls_id, x_c, y_c, w, h map(float, line) xmin round((x_c - w / 2) * W) ymin round((y_c - h / 2) * H) xmax round((x_c w / 2) * W) ymax round((y_c h / 2) * H) print(int(cls_id), xmin, ymin, xmax, ymax)逻辑说明x_c - w/2得到框左边缘的归一化位置乘以图像宽度就是像素坐标xmax同理。这一步是检验标签是否与画面内容对齐的最快方式跑完随机抽十张画框看一眼基本就能判断这份标注靠不靠谱。VOC格式的xml则完全不同它存的是像素绝对坐标而且类别用的是字符串名称annotation filenameimg_0133_136.jpg/filename size width1920/width height1080/height /size object namesmoking/name bndbox xmin416/xmin ymin236/ymin xmax562/xmax ymax405/ymax /bndbox /object /annotation两类格式的对比如下对比项YOLO txtVOC xml坐标类型归一化相对坐标像素绝对坐标类别表示整数索引从0开始字符串名称文件组织与图像同名的txt单独xml目录适用框架yolov5 / v8 / v9 / v10 / yolo11原生VOC工具链、mmrotate、detectron22.3 data.yaml整个训练管线唯一默认读的配置文件data.yaml是yolo系训练时的入口配置。这份数据集自带的data.yaml内容大概率长这样path: /home/user/datasets/smoking train: images/train val: images/val test: images/test nc: 2 names: 0: smoking 1: cigarette_boxpath是数据集根目录的绝对路径train、val、test是相对path的子目录路径nc是类别数names是类别名列表。这里有个非常容易翻车的细节names的索引顺序必须和txt文件里的class数字严格对应0: smoking意味着txt里所有以0开头的行都被当作吸烟目标。提示训练和验证真正读的是labels下的txtvoc_xml更适合做格式迁移或人工复核。如果发现txt与xml内容不一致以txt为准训练用xml去反查原因。3. 从解压到跑通yolov8训练一条命令也不浪费的完整路径3.1 环境准备与数据集放置建议用独立conda环境装ultralyticsPython版本选3.10比较稳妥conda create -n yolo python3.10 -y conda activate yolo pip install -U ultralytics说明-U会拉取当前最新版ultralytics它自带yolov8的全部训练、验证、推理命令yolov9、yolov10、yolo11后续也可以通过切换model参数来尝试训练命令本身不换。放置数据集时有一个血泪经验尽量把数据集放到不含中文、不含空格的路径下比如/home/user/datasets/smoking。虽然ultralytics在中文路径下偶尔也能跑但Dataloader在Windows上遇到中文路径时抽风概率明显上升没必要为这种事浪费半天。3.2 改data.yaml指向本地绝对路径拿到手的data.yaml里path写的是制作者机器上的路径直接训练大概率报目录不存在。用一行python脚本改成本机绝对路径import yaml with open(data.yaml, encodingutf-8) as f: cfg yaml.safe_load(f) cfg[path] /home/user/datasets/smoking with open(data.yaml, w, encodingutf-8) as f: yaml.safe_dump(cfg, f, allow_unicodeTrue) print(cfg)逻辑说明yaml.safe_load读入整个配置只修改path字段其余train、val、test、nc、names保持原样。allow_unicodeTrue是为了防止类别名里的非ASCII字符在写回时被转义成\uXXXX。训练之前先跑一遍标签完整性检查。这一步能筛掉绝大部分低级问题from pathlib import Path for split in [train, val, test]: img_dir Path(fimages/{split}) lab_dir Path(flabels/{split}) img_names {p.stem for p in img_dir.glob(*.jpg)} lab_names {p.stem for p in lab_dir.glob(*.txt)} print(split, 缺标签的图:, img_names - lab_names) print(split, 无图的标签:, lab_names - img_names)逻辑说明用集合差集分别找出jpg有但txt没有、以及txt有但jpg没有的文件名。正常输出应该是两个空集合任何非空结果都说明标签与图像没有一一对应需要先处理再进训练。3.3 训练yolo命令行与超参解读环境就绪、data.yaml改好后直接开训cd ~/datasets/smoking yolo detect train \ datadata.yaml \ modelyolov8s.pt \ epochs100 \ imgsz640 \ batch16 \ projectruns/smoking \ nameexp1参数含义说明modelyolov8s.pt代表用s规模的预训练权重起步比n模型精度高、比m模型训练快对2117张的中等规模数据是合适起点imgsz640是yolo系列的默认输入尺寸烟盒这类小目标如果效果不理想可以提到768或896但训练时间会同步上涨project和name共同决定权重输出位置最终结果在runs/smoking/exp1/weights/下。训练过程中的关键观察点有三个第一个是启动时是否出现WARNING no labels found出现就是标签路径配错了第二个是前几个epoch的box_loss是否在下降完全不降说明学习率或标签有问题第三个是mAP50在训练集上的增长趋势如果一直在0附近大概率是标注本身有问题。这些都属于前置检查没做到位时的典型翻车现场。如果发现烟盒类别数量偏少、正负样本不均衡可以在命令里加类别权重yolo detect train \ datadata.yaml \ modelyolov8s.pt \ epochs100 \ imgsz640 \ batch16 \ cls1.5cls1.5会加大分类损失的权重强制模型更关注数量少的类别。具体数值需要根据你训练后看混淆矩阵的结果再调它不是越大越好。3.4 验证与测试val/predict两步检查训练结束后先跑验证集拿一组客观指标yolo detect val \ modelruns/smoking/exp1/weights/best.pt \ datadata.yaml验证会输出mAP50和mAP50-95两组关键数字。mAP50代表IoU阈值0.5时的平均精度比较宽松mAP50-95是多个IoU阈值的平均更严格。对吸烟检测这种实际场景我更看重mAP50因为部署时通常用0.5的置信度阈值。指标没问题再拿测试集图像跑一次推理看实际效果yolo predict \ modelruns/smoking/exp1/weights/best.pt \ sourceimages/test \ saveTruesaveTrue会把画好框的预测图存到runs/smoking/exp1/predict目录下。这一步不是为了看指标而是用肉眼确认模型有没有把烟盒误识成人手、把烟雾误识成其他东西——这种定性判断PR曲线是替代不了的。4. 避坑手册抽烟烟盒数据集容易翻车的四个坑4.1 坑一空标签文件导致训练直接哑火现象训练命令跑起来后日志出现WARNING: no labels found in images/train或者loss在第一个epoch直接变成NaN训练中断。原因jpgs和txt的文件名没有一一对应比如某张img_0133_925.jpg对应的是img_0133_925 (1).txtDataloader按stem匹配不到就把这张图当成了无标签样本跳过。更隐蔽的情况是txt文件存在但内容为空文件的创建时间对、大小是0字节。解决先跑一遍我在3.2节写的集合差集脚本确认没有缺漏再检查txt文件大小把空文件单独清出来find labels -name *.txt -size 0 -exec ls -l {} \;查出来如果确实存在空标签用人工标注或复制相邻帧标签的方式补上。补完必须重新跑检查脚本确认干净再进训练。4.2 坑二类别索引越界训练直接报错现象训练启动时报AssertionError: Label class 2 exceeds nc2 in labels/train/img_0133_925.txt。原因data.yaml里nc: 2意味着合法类别索引只有0和1但txt里出现了类别2。这多半是标注工具导出时类别映射表对错位原本的类别1被写成了2或者xml转txt的脚本把class_id 1写进去。解决先扫描整个标签目录找出所有超过合法范围的类别编号awk {print $1} labels/train/*.txt labels/val/*.txt labels/test/*.txt \ | sort -n | uniq正常输出应该是0和1两行。如果出现了2或更大值写个python脚本把越界行的类别号修正from pathlib import Path for txt in Path(labels).rglob(*.txt): lines txt.read_text().strip().splitlines() out [] for line in lines: parts line.split() cls_id int(float(parts[0])) if cls_id 2: parts[0] 1 out.append( .join(parts)) txt.write_text(\n.join(out) \n)逻辑说明把大于等于2的类别索引一律改为1。这只是一种应急修正更严谨的做法是先人工抽样看越界行的框到底对应吸烟还是烟盒再决定改成0还是1。改完重新校验一遍。4.3 坑三Windows路径与相对路径混用现象训练命令在Windows下跑data.yaml里path写成C:\Users\me\datasets\smoking启动时报yaml: line N: found character that cannot start any token或者报Paths with .. not allowed。原因YAML会把反斜杠当作转义符C:\Users里的\U被解析成不可见字符整个路径直接坏掉相对路径里带..则会被ultralytics的安全检查直接拒绝。解决Windows下把path改成统一正斜杠格式path: C:/Users/me/datasets/smoking同时训练命令一律在smoking目录下执行data.yaml内部用相对路径images/train避免在参数里出现../。这个坑不大但遇到一次就浪费半小时属于最典型的低级错误。4.4 坑四训练loss正常下降但mAP一直为0现象box_loss、cls_loss都正常下降训练结束验证时mAP50却是0PR曲线贴底没有拐点。这是最玄学的一种情况——训练看起来没毛病结果全错。原因常见原因有两个。一是txt坐标存在越界值比如width或height大于1或者坐标出现负值验证阶段GT框与resize后的图像错位二是train与val划分存在重叠或标注风格不统一训练集里被标成smoking的区域验证集里同一位置标的是cigarette_box类别语义对不上。解决先过滤越界坐标把超出0-1范围的数值clip回合法区间from pathlib import Path def clip_txt(txt_path: Path): lines txt_path.read_text().strip().splitlines() out [] for line in lines: parts line.split() cls_id, x, y, w, h map(float, parts) x min(max(x, 0.0), 1.0) y min(max(y, 0.0), 1.0) w min(max(w, 0.0), 1.0) h min(max(h, 0.0), 1.0) out.append(f{int(cls_id)} {x:.6f} {y:.6f} {w:.6f} {h:.6f}) txt_path.write_text(\n.join(out) \n) for txt in Path(labels).rglob(*.txt): clip_txt(txt)逻辑说明clip的意思是只修正越界的数值不改变正常框的位置所以不影响标注语义。这类问题适合在训练前统一跑一遍相当于给标签上保险。clip之后再检查一次mAP如果仍然为0就逐张叠加标注框看val集标注内容是否与图像一致重点排查类别张冠李戴。5. 把这套数据用到极致增强、迁移与验证三板斧5.1 针对性数据增强2117张图在深度学习里不算多直接硬train很容易过拟合。我习惯在ultralytics默认增强之外额外用albumentations做一套针对烟盒场景的增强组合import albumentations as A train_tfm A.Compose([ A.RandomBrightnessContrast( brightness_limit0.2, contrast_limit0.2, p0.5 ), A.HueSaturationValue( hue_shift_limit10, sat_shift_limit15, val_shift_limit15, p0.3 ), A.RandomGamma(gamma_limit(80, 120), p0.3), A.CLAHE(clip_limit2.0, tile_grid_size(8, 8), p0.2), ], bbox_paramsA.BboxParams(formatyolo, label_fields[class_labels]))逻辑说明吸烟检测的难点在于暗光、逆光环境下烟头区域对比度低烟盒颜色与桌面木材接近。亮度对比度增强和Gamma调整能模拟不同光照条件CLAHE能提升局部对比度增强模型对暗光场景的鲁棒性。这里故意不加重翻转和旋转因为烟头和烟盒都是小目标过度几何变换会把目标裁掉一半反而让训练集变成噪声集。如果不想改训练代码直接用ultralytics自带超参也能达到类似效果yolo detect train \ datadata.yaml \ modelyolov8s.pt \ epochs100 \ hsv_h0.015 hsv_s0.7 hsv_v0.4 \ fliplr0.5hsv_h、hsv_s、hsv_v控制HSV三通道的随机扰动幅度fliplr0.5表示水平翻转概率0.5。这些是数据量不足时最稳妥的兜底增强。5.2 用小模型迁移起步2117张数据用yolov8s起步可以但想快速验证数据干净程度我建议先用yolov8n跑一版。n模型参数量小跑一轮验证集很快先把pipeline跑通再用s或m模型冲精度。yolo detect train \ datadata.yaml \ modelyolov8n.pt \ epochs50 \ imgsz640 \ freeze10freeze10表示冻结前10层也就是让backbone在初期不参与更新只训练检测头。对中小数据集迁移学习初段冻结backbone能显著降低过拟合风险等loss平台期再解冻全模型微调50轮。这个策略对这份数据尤其有效吸烟区域和烟盒在COCO预训练特征里本来就有相近的纹理基底让head先学会定位再让深层特征慢慢适配。5.3 用混淆矩阵做最终验收训练完别只盯着mAP数字跑一次带图的验证把混淆矩阵和PR曲线输出出来yolo detect val \ modelruns/smoking/exp1/weights/best.pt \ datadata.yaml \ plotsTrueplotsTrue会在验证目录下生成confusion_matrix.png和PR_curve.png。看混淆矩阵时重点看两个位置smoking被误判成背景的比例以及cigarette_box被误判成smoking的比例。前者说明模型漏检严重需要提高召回——降置信度阈值或增加正样本后者说明类别边界模糊需要检查标注框是否经常把烟盒和持烟手圈在一起如果是应该回去修标注而不是调模型。我把这套流程完整跑下来之后最大的感受是数据集质量决定精度上限模型决定能多接近这个上限。从那以后我每次拿到带标签的数据集第一件事不是开train而是先跑一遍标签校验脚本、把坐标clip一遍、抽查十张可视化最后再用n模型快速确认数据链路通畅后才进入正式训练。这套五步走流程从那次翻车后我就强制自己每次换数据集都走一遍省下的是无数个晚上瞎改参数的觉。希望帮到你。本文还有配套的精品资源点击获取