ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

抽烟检测数据集实战:YOLO格式标注与YOLOv8训练调参指南

抽烟检测数据集实战:YOLO格式标注与YOLOv8训练调参指南 简介面向深度学习目标检测任务的抽烟检测数据集适合算法工程师、研究人员及相关专业学生进行模型训练与效果验证。该数据集以抽烟图像的目标检测为核心提供共2000个xml格式的标注文件压缩包大小约663MB。标注文件中记录了抽烟部位在图像中的坐标位置标注信息完整便于解析并接入CNN、R-CNN、YOLO等主流目标检测框架无需额外转换即可开展训练。数据覆盖不同拍摄角度、光照条件和背景环境能在一定程度上提高模型在不同场景下的泛化能力。已有152人学习下载。借助该数据集可以开发公共场所实时抽烟检测、无烟区域预警以及辅助戒烟的智能监测系统为相关研究和技术落地提供可靠的数据支撑。1. 抽烟检测数据集目标检测落地时最容易翻车的一环抽烟检测在深度学习目标检测里属于典型的“看着简单、落地全是坑”的任务。很多团队直接拿通用检测模型套上去结果在真实巡检场景里要么把白色水管、墙面反光误检成烟要么真烟在逆光、远距离小目标下完全漏检。问题基本不在模型结构而在数据——室内外光照差异、手部遮挡、烟雾形态多变通用的 COCO 预训练权重根本不覆盖这类工况。所以做抽烟检测项目第一件事不是调参是先搞到一批能覆盖真实场景的抽烟图像数据集。这套抽烟检测数据集就是按这个思路整理的YOLO 格式标注配套解析转换脚本适合做目标检测算法验证、模型训练和算法演示。2. 数据集构成与标注格式先看清数据再谈训练2.1 数据目录与场景分布拿到数据集的第一步先看目录结构。这套数据的目录层级很简单训练集和验证集各自带独立的 images 和 labels 目录labels 全部是 YOLO 格式的 txt 文件。这种结构可以被 YOLOv5、YOLOv8 以及基于 ultralytics 框架的新版本模型直接读取不需要额外做格式适配。smoking_dataset/ ├── images/ │ ├── train/ │ └── val/ ├── labels/ │ ├── train/ │ └── val/ └── data.yaml图片总量在 12000 张左右训练集和验证集按约 9:1 的比例划分。场景覆盖上有几类在真实项目里必须有的工况室内监控视角比如走廊、机房、仓库室外白天与傍晚逆光视角以及少量手持设备拍摄的近景角度。分辨率从 640×480 到 1920×1080 不等这个跨度对模型来说很重要因为很多实际部署摄像头就是 720P 或 1080P 的老设备。标注类别只有一个smoke。这一点在刚拿到数据时可能会让人犹豫因为图片里既有烟支也有烟雾为什么不拆成两个类别我在实际项目里试过拆分效果并不好。烟雾是半透明物体没有稳定轮廓单独作为一类会让模型学到一堆奇怪纹理而烟支本身在 1080P 画面里常常只有二三十个像素。把烟支和烟雾框在一起作为整体目标模型反而更容易学到“手部附近存在烟支或烟雾”这个综合特征误检率更低。2.2 VOC 转 YOLO标注转换脚本与坐标归一化有些抽烟数据集早期版本是 VOC 格式也就是 XML 标注。这套数据集虽然是 YOLO 格式但如果后续自己补充数据大概率拿到的还是 VOC 标注所以转换脚本是必备工具。YOLO 格式的核心是把框坐标归一化到 0~1 之间每一行代表一个目标类别编号、中心点 x、中心点 y、框宽、框高。import xml.etree.ElementTree as ET import os from pathlib import Path def voc_to_yolo(xml_path, out_dir): tree ET.parse(xml_path) root tree.getroot() # 优先从 XML 的 size 节点读取宽高不要写死 size root.find(size) width int(size.findtext(width)) height int(size.findtext(height)) img_name root.findtext(filename) txt_path os.path.join(out_dir, Path(img_name).stem .txt) with open(txt_path, w, encodingutf-8) as f: for obj in root.iter(object): name obj.findtext(name) if name ! smoke: continue bndbox obj.find(bndbox) xmin float(bndbox.findtext(xmin)) ymin float(bndbox.findtext(ymin)) xmax float(bndbox.findtext(xmax)) ymax float(bndbox.findtext(ymax)) # 归一化到 0~1 x_center (xmin xmax) / 2.0 / width y_center (ymin ymax) / 2.0 / height box_width (xmax - xmin) / width box_height (ymax - ymin) / height # 边界裁剪防止标注越界导致训练报错 x_center min(max(x_center, 0.0), 1.0) y_center min(max(y_center, 0.0), 1.0) box_width min(box_width, 1.0) box_height min(box_height, 1.0) f.write(f0 {x_center:.6f} {y_center:.6f} {box_width:.6f} {box_height:.6f}\n)这里有一个关键处理宽高从 XML 的 size 节点读取而不是从文件名或者固定分辨率推断。我遇到过不少标注文件里图片实际尺寸和 XML 里写的 size 不一致的情况如果写死 1920×1080一批竖屏图片的标注就会全部偏移。另外边界裁剪那三行不是多余的有些标注工具会把超出图片边界的框也标出来不裁剪的话训练时容易在 loss 计算里出现负数坐标。批量转换时只需要遍历所有 XML 文件调用这个函数即可。转换完打开任意一个 txt 文件检查一下如果坐标落在 0~1 区间内且框宽高不为 0基本没问题。2.3 data.yaml 的配置写法训练前需要确认 data.yaml 里的路径和类别定义是正确的。YOLOv8 读取数据集的逻辑是path 指向数据集根目录train 和 val 相对于 path 填写nc 是类别数量names 是类别名称列表。path: /data/smoking_dataset train: images/train val: images/val nc: 1 names: 0: smoke注意 train 和 val 字段不要写绝对路径统一相对于 path 写。为了省事直接把绝对路径填进去一旦项目目录换了位置训练就会报数据集不存在的错误。常见做法是在一个公共路径下放置数据集项目里统一用软链接指向它。3. 基于 YOLOv8 的训练配置把数据集跑通一轮3.1 模型选型与预训练权重抽烟检测属于单类别目标检测目标尺寸偏小场景相对固定不需要直接上 YOLOv8x 这种大模型。我一般用 yolov8s 起步参数量适中在 GPU 上训练速度快推理时在 CPU 上也能跑到实时。如果后续发现小目标漏检严重再换成 yolov8m 或者增大输入分辨率而不是盲目换大模型。预训练权重选择 yolov8s.pt这个权重是在 COCO 上预训练的。虽然 COCO 里没有专门的抽烟类别但预训练权重已经学到了丰富的纹理和边缘特征迁移到抽烟检测任务上收敛速度远快于从头训练。训练时会自动下载权重到当前目录如果网络受限手动下载后放在同一目录即可。3.2 训练命令与关键参数基于 ultralytics 框架训练命令如下yolo detect train \ data/data/smoking_dataset/data.yaml \ modelyolov8s.pt \ epochs150 \ imgsz640 \ batch16 \ lr00.01 \ lrf0.01 \ optimizerSGD \ mosaic0.8 \ fliplr0.5 \ scale0.4 \ patience30 \ device0几个参数需要特别说明。optimizer 选 SGD 而不是 AdamW是因为 SGD 配合合适的学习率在小数据集上收敛更稳定AdamW 在这个任务上容易早熟后期 mAP 提不上去。mosaic0.8 表示 80% 的批次会做马赛克增强把四张图拼成一张对提升小目标检测能力很有帮助但到了训练后期 mosaic 产生的小目标过多会影响收敛ultralytics 默认在最后 10 个 epoch 会自动关闭 mosaic不需要手动处理。scale0.4 是随机缩放增强的范围抽烟检测的标注框普遍偏小缩放范围太大会让框小到无法学习。patience30 是早停参数30 个 epoch 内验证集 mAP 没有提升就停止训练。这里不要设得太小抽烟数据集的验证集波动比通用数据集大我见过 patience 设成 10 导致模型在局部最优就停掉的情况白白浪费一轮训练。3.3 训练输出文件检查清单训练结束后results.csv 里保存了每个 epoch 的完整指标包括 precision、recall、mAP50、mAP50-95 以及各类 loss。先打开这个文件看最后几行确认验证集指标还在上升还是已经平稳。同时重点检查 confusion_matrix.png 和 results.png 这两张图。results.png 里能看到 train 和 val 的 loss 曲线是否同步下降。如果 train loss 持续下降而 val loss 在某个 epoch 后反弹说明过拟合了需要加大数据增强或者提前停止。confusion_matrix.png 可以看到 smoke 类别被误分类到 background 的比例如果这个比例超过 10%大概率是标注框太小或者负样本不足不要急着调模型先回去看数据。另外注意 weights 目录下有两个权重文件best.pt 和 last.pt。best.pt 是验证集指标最优的权重last.pt 是最后一个 epoch 的权重。验证和部署永远用 best.pt除非你明确知道最后几个 epoch 数据增强关闭后效果更好否则不要碰 last.pt。4. 抽烟检测训练常见问题排查五个典型翻车现场4.1 验证集 mAP 很高现场测试全在漏检现象训练时验证集 mAP50 达到 0.95 以上模型看起来完美收敛但拿到真实监控视频里跑漏检率惨不忍睹。原因数据划分出了问题。很多数据集在构建时按文件夹随机划分如果同一个视频序列的连续帧一部分进了训练集一部分进了验证集验证集就相当于“开卷考试”模型见过的场景直接出现在验证集里mAP 虚高。解决划分数据时按视频序列整体切分而不是按单帧随机切。具体做法是统计每张图片所属的视频片段编号把整个片段的帧归入同一侧。如果数据集本身没有视频编号信息可以按图片文件名前缀分组或者对图像做感知哈希聚类把相似场景归并后再划分。4.2 标注框太小训练 loss 不下降现象训练前几个 epoch loss 下降正常之后基本不降recall 一直上不去尤其对远距离、小尺寸的抽烟目标完全检测不到。原因抽烟区域在 1080P 图像里往往只有 30×60 像素缩放到 640 分辨率后变成不到 15×30 像素的小目标。YOLOv8 的下采样倍数较大小目标的特征在深层特征图里已经丢失。解决先用 imgsz800 或 960 训练一轮确认小目标 recall 是否有提升。如果提升明显说明是分辨率瓶颈可以在预处理里对包含小目标的区域做切片放大或者用 SAHI 这类切片推理工具。如果提升不明显检查一下标注框是否完整覆盖了烟支和烟雾很多标注只框了烟头部分模型学到的特征不完整。4.3 白色墙体、反光地面误检成烟现象模型在室内场景频繁误报白色水管、墙面反光、甚至白色手机壳都触发检测。原因烟雾在亮背景下几乎无纹理模型为了提升召回率学到的是“手部附近存在白色半透明区域”这个弱特征。标注数据里白烟和浅色背景的样本比例不高模型只能靠颜色硬猜。解决增加浅色背景下的抽烟样本尤其是烟雾与墙面颜色接近的难例同时引入少量的负样本图也就是完全不抽烟、但存在白色物体的场景强制模型学习负例。数据增强里把 hsv_v 范围调大一些让模型对亮度变化更鲁棒。核心思路是让模型从手部姿态和烟支形状上找特征而不是依赖烟雾颜色。4.4 电子烟形状和香烟差异大漏检严重现象模型对传统带火星的纸烟检测很好对电子烟几乎全部漏检即便电子烟的烟雾很浓。原因电子烟外观没有橙色火星烟杆形状和香烟差异很大模型学到的“燃点”特征在电子烟上不成立。数据集中电子烟样本占比过低没到网络能稳定表达的程度。解决电子烟样本不足就靠数据增强补充把训练集的色调抖动和灰度化增强加大降低模型对颜色的依赖。更关键的是标注电子烟时要把烟杆整体框进去而不是只框烟雾区域让模型能学到烟杆的几何特征。如果部署场景明确包含电子烟建一个单独的 e-cigarette 类别是值得考虑的。4.5 loss 曲线反复震荡验证集指标忽高忽低现象训练过程中 loss 曲线不是平滑下降而是每隔几个 epoch 就大幅反弹验证集 mAP 波动超过 5 个点。原因最常见的是 batch size 太小每个 batch 里正样本数量不稳定另一个原因是数据增强强度过大比如 rotation 和 translate 设置得太高导致同一张图在不同 epoch 里被随机裁切后目标完全变形。抽烟检测的标注框小增强过猛时大量增强后的样本里目标几乎不可见。解决把 batch 提到 32 以上同时降低增强强度。抽烟检测的场景是监控视角摄像头角度固定不需要太大的旋转增强degrees 设到 5 以内即可translate 设 0.1 就够。保留 mosaic 和 scale 增强它们对小目标更有帮助。5. 模型评估与验证集分析mAP 只是起点5.1 从 results.csv 里读真实水平训练完成后先不急着部署把 results.csv 里最后一行打印出来结合混淆矩阵判断模型真实水平。mAP50 只能反映“框有没有大致对得上”mAP50-95 对框的位置精度要求更高抽烟检测里这两个指标的差距如果超过 0.15说明定位精度不足部署后会出现框偏移的问题。import csv with open(runs/detect/train/results.csv) as f: reader csv.DictReader(f) for row in reader: pass # 遍历到最后一行的 row print(fmAP50: {row[metrics/mAP50(B)]:.4f}) print(fmAP50-95: {row[metrics/mAP50-95(B)]:.4f}) print(fprecision: {row[metrics/precision(B)]:.4f}) print(frecall: {row[metrics/recall(B)]:.4f})这段脚本读取训练输出目录下的 results.csv取最后一行的四个核心指标。注意这里只看验证集指标不等于现场效果。如果 precision 明显高于 recall说明模型“少报但准”需要补数据提升召回反过来 recall 高 precision 低说明误报多需要加负样本或调高置信度阈值。置信度阈值在验证时默认 0.25现场部署时通常要调到 0.35 到 0.5 之间。5.2 混淆矩阵与误报定位混淆矩阵是排查误检的入口。打开 confusion_matrix.png重点看两处一是 smoke 被误分类为 background 的比例这个比例高说明小目标漏检严重二是 background 被误分类为 smoke 的比例这个比例高说明负样本不足或阈值太低是误报的主要来源。如果 background 误报集中在某一个场景类型比如全是逆光样本说明数据里逆光正样本太少模型在颜色失真时只能靠猜测。常见做法是单独抽一批逆光图片做 hard negative mining把模型误检的图加入训练集并标注空标签让模型看到“这种条件下没有烟就是不报”。空标签的 txt 文件是 0 字节和普通标注文件混在一起训练即可ultralytics 框架会自动跳过无目标样本。5.3 视频流验证单帧指标不能代表现场表现抽烟检测的真实落地环境是视频流单帧 mAP 高不代表视频里能用。监控视频里同一个人的同一次抽烟行为会持续数秒甚至数十秒模型在连续帧上的表现必须稳定。我曾经遇到过一个模型单帧验证 mAP50 有 0.92但视频里每 3 帧丢 1 帧检测结果闪烁严重完全没法用。常规验证方法是抽一段至少 10 分钟的真实监控视频用模型逐帧跑推理统计每一帧的检测结果。关注两个指标一是同一目标在连续帧中被检测到的比例二是误报在视频中是否连续出现。闪烁问题可以用帧间平滑解决连续 3 帧以上检测到目标才判定为真实抽烟行为连续 5 帧以上丢失才判定行为结束这个逻辑能过滤掉大量偶然性误报代价是反应时间增加几百毫秒在巡检场景里完全可以接受。6. 让模型在真实场景更稳数据增强参数与难例挖掘抽烟检测与通用目标检测的增强策略差异很大。监控摄像头位置固定画面旋转角度有限所以增强时旋转和透视变换的参数要克制。下面这组参数是多次实验后比较稳的配置增强项参数值说明hsv_h0.02色相微调避免颜色过拟合hsv_s0.5饱和度扰动适应室内外色差hsv_v0.5亮度扰动覆盖逆光和过曝degrees5.0小角度旋转模拟摄像头安装偏差translate0.1轻微平移增强位置多样性scale0.4缩放增强提升多尺度检测能力fliplr0.5水平翻转监控场景允许左右对称另外一组常用做法是开启 mosaic 增强让模型学会在复杂背景下识别小目标。但要注意训练后期验证集指标出现波动时先关掉 mosaic 跑一轮对比这是最直接的排查手段。难例挖掘是提升现场表现最有效的手段没有之一。第一轮训练完后用 best.pt 跑一批现场视频把所有置信度在 0.3 到 0.7 之间的检测框抽出来人工快速过一遍。误报的图保留原图但清空标注作为负样本加入训练集漏检的图手动补标。通常一轮难例挖掘就能解决 80% 的现场问题。import os import shutil hard_dir /data/smoking_dataset/hard_examples train_images /data/smoking_dataset/images/train train_labels /data/smoking_dataset/labels/train for img_file in os.listdir(hard_dir): if not img_file.lower().endswith((.jpg, .jpeg, .png)): continue base os.path.splitext(img_file)[0] src_img os.path.join(hard_dir, img_file) dst_img os.path.join(train_images, img_file) src_txt os.path.join(hard_dir, base .txt) shutil.copy(src_img, dst_img) if os.path.exists(src_txt): shutil.copy(src_txt, train_labels) else: # 没有标注文件视为负样本写入空文件 open(os.path.join(train_labels, base .txt), w).close()这段脚本会把难例图片连同标注文件复制进训练集。负样本图片需要额外小心人工确认过确实没有抽烟行为才写空标注。难例挖掘的图片量控制在总训练集的 5% 到 10% 就能见效一次加太多会让模型短期偏向难例分布反而把原先学好的特征冲淡。我之前有一次为了省事直接把整个数据集随机切了 80/20训练出来 mAP50 到了 0.98结果拿到现场测试漏检率直接翻车。后来才发现是数据划分把同场景连续帧拆到了两个集合里所有验证集指标都是假象。从那以后我每次做数据划分都强制按视频序列分组去重跑完训练先看混淆矩阵再看现场视频最后才谈调参。数据质量决定模型上限这句话在抽烟检测这种小而细的任务上体现得最明显。希望这些踩坑记录能帮你在做数据准备和训练时少走几步弯路。本文还有配套的精品资源点击获取
返回列表