YOLO吸烟行为检测实战:5000张数据集训练与部署全流程
发布时间:2026/10/4 1:15:05 作者:尧图编辑部 阅读量:1,286

简介本资源为面向计算机视觉与行为识别方向的YOLO吸烟行为检测数据集适合从事目标检测训练、算法验证及课程项目的开发者与研究人员使用。数据集包含5000余张jpg格式吸烟场景图像全部经labelimg标注完成标签同时提供xml与txt两种格式分别存放于独立文件夹可直接接入YOLO系列模型训练目标类别统一命名为smoke。压缩包共14569个文件其中jpg图像4856张、xml标注4856个、txt标注4857个整体约214.87MB文件组织清晰便于按格式快速取用。目前已有4071人学习下载配套博文提供了检测结果参考读者可据此了解标注质量与模型表现快速搭建吸烟行为检测实验流程省去自行采集与标注的时间成本适用于课堂演示、毕业设计及算法对比等场景。1. 从 5000 张吸烟图说起这套数据集到底能解决什么吸烟行为检测这件事真正卡住大多数团队的从来不是模型结构而是数据。你拿 COCO 或者 VOC 直接训一个 YOLO跑出来的模型对「人」框得很准对「烟」几乎无感——因为烟头在整张图里可能只占十几个像素标注里根本没有这个类。YOLO吸烟行为检测数据集5000数据这个标题核心价值就在于它把「吸烟」这个动作从通用检测里单独拎出来给了一个可以直接开训的专用数据底座。5000 张这个量级说大不大说小也绝对不小它足够让一个 YOLOv8n 或 YOLO11n 从零收敛出一个能用的二分类/多分类检测器又不至于让你在标注清洗上耗掉两周。这套东西适合谁第一类是做园区、工地、加油站、化工厂禁烟区监控的工程团队需要把「有人抽烟」这件事从视频流里实时揪出来第二类是做行为识别课程设计或毕设的学生想找一个有明确业务含义、又不是烂大街的口罩/安全帽数据集第三类是想练 YOLO 微调流程的人拿它当一条完整的「数据→训练→部署」链路来跑通。它解决的不是「识别所有人」这种泛目标问题而是「在复杂背景下定位香烟并判断吸烟姿态」这种长尾小目标问题。下面我按自己实际跑这类数据集的顺序把选型、清洗、训练、踩坑一条条讲清楚。2. 先搞懂吸烟检测的标注逻辑为什么它和普通目标检测不一样2.1 香烟是小目标标注粒度决定模型上限普通目标检测里一个「人」框几百像素模型随便学学就能框住。吸烟检测的难点在于香烟本体极小一张 1080P 的监控截图里一根烟可能只有 8×20 像素经过 YOLO 的下采样之后在 P3 特征图上几乎只剩一两个格子。这就带来一个直接后果如果你只标「香烟」这一个类模型很容易在训练早期就把烟头当成噪声丢掉召回率上不去。常见的标注方案有两种。第一种是双类方案smoke香烟本体person_smoking吸烟的人。第二种是单类方案只标smoking把香烟和手部、嘴部区域合并成一个大一点的框。我一般推荐双类原因是它让模型同时学到「烟在哪」和「谁在抽」两个信号后处理时可以用人的框去约束烟的框减少误报。5000 张数据如果按双类标大约能得到 5000 个 person_smoking 框和 40004800 个 smoke 框比例还算健康。标注工具上LabelImg、X-AnyLabeling、CVAT 都能干这活。关键是导出格式要统一成 YOLO 的 txt每行class_id cx cy w h坐标全部归一化到 01。很多人从 VOC 的 xml 转过来时忘了归一化训练时 loss 直接飙到 nan这是血泪经验。2.2 数据划分与目录结构别让验证集泄漏拿到 5000 张图第一件事不是急着训而是划分。我一般按 8:1:1 分 train/val/test也就是 4000/500/500。这里有个坑如果这 5000 张是从连续视频抽帧来的相邻帧几乎一模一样随机划分会导致验证集里出现和训练集高度相似的图mAP 虚高。正确做法是按视频源或时间段划分同一段视频的帧只能进同一个集合。标准 YOLO 目录结构长这样smoking_dataset/ ├── images/ │ ├── train/ # 4000 张 │ ├── val/ # 500 张 │ └── test/ # 500 张 ├── labels/ │ ├── train/ │ ├── val/ │ └── test/ └── smoking.yaml对应的smoking.yaml# 吸烟行为检测数据集配置 path: /data/smoking_dataset # 数据集根目录 train: images/train # 训练集相对路径 val: images/val # 验证集相对路径 test: images/test # 测试集相对路径 names: 0: smoke # 香烟本体 1: person_smoking # 吸烟的人path用绝对路径最稳相对路径在不同工作目录下启动训练时经常找不到文件。names的顺序必须和标注时的 class_id 严格对应顺序错了模型学出来的类会整体错位这种错误在验证阶段表现为「框的位置对但类别全反」排查起来很费时间。2.3 用脚本做一次数据体检三个必查项在开训之前我习惯写个小脚本扫一遍标签检查三件事有没有越界坐标、有没有空标签、类别分布是否失衡。import os from collections import Counter label_dir smoking_dataset/labels/train cls_counter Counter() bad_files [] for name in os.listdir(label_dir): if not name.endswith(.txt): continue path os.path.join(label_dir, name) with open(path) as f: lines [l.strip() for l in f if l.strip()] if not lines: bad_files.append((name, empty)) continue for line in lines: parts line.split() if len(parts) ! 5: bad_files.append((name, bad_format)) continue cid int(parts[0]) cx, cy, w, h map(float, parts[1:]) # 归一化坐标必须落在 0~1越界说明标注或转换出错 if not (0 cx 1 and 0 cy 1 and 0 w 1 and 0 h 1): bad_files.append((name, out_of_range)) cls_counter[cid] 1 print(类别分布:, dict(cls_counter)) print(问题文件数:, len(bad_files)) for item in bad_files[:10]: print(item)这段脚本的逻辑很直白逐行解析 YOLO 标签校验字段数和坐标范围同时统计每个类出现次数。参数上没什么可调的重点看输出——如果bad_format或out_of_range超过几十个说明标注流程有问题先修数据再训如果某一类数量不到另一类的十分之一训练时就要考虑用cls损失加权或者过采样。空标签文件本身不一定是错它代表「这张图没有目标」属于负样本对降低误报有帮助但比例别超过 10%。3. 用 YOLOv8/YOLO11 在 5000 张图上跑通训练命令、参数与显存账3.1 环境与模型选型n/s 起步别一上来就 x环境用 Ultralytics 官方包最省事一条命令搞定pip install ultralytics模型选型上5000 张图属于中小规模我建议从yolov8n.pt或yolo11n.pt起步。n 版本参数量约 300 万在单张 1080Ti 或 3060 上 batch16 完全跑得动。如果你追求精度且显存够≥12G可以上yolov8s.pt。不建议直接上 m/l/x5000 张数据喂不饱大模型过拟合几乎是必然的验证 loss 会在 50 轮之后开始往上走。预训练权重一定要用。从 COCO 预训练权重微调比从零训收敛快 35 倍小目标召回也明显更好。这是我在多个小数据集上反复验证过的结论。3.2 训练命令与关键参数逐条解释最小可跑通的训练命令yolo detect train \ modelyolov8n.pt \ datasmoking_dataset/smoking.yaml \ epochs150 \ imgsz640 \ batch16 \ lr00.01 \ lrf0.01 \ patience30 \ device0 \ projectruns/smoking \ nameexp1逐条说参数。epochs150是上限配合patience30做早停——30 轮验证指标不涨就停避免无效训练。imgsz640是 YOLO 的默认输入尺寸吸烟这种小目标其实可以提到 960 甚至 1280代价是显存和推理耗时成倍增加640 先跑基线不够再提。batch16要按显存调8G 显存跑 640 大概能到 1612G 能到 32。lr00.01是初始学习率微调场景其实可以降到 0.001收敛更稳。lrf0.01是最终学习率系数余弦退火到初始值的 1%。如果显存吃紧加ampTrue默认就开和cacheram把图片缓存进内存加速。数据量 5000 张、每张几百 KB缓存进内存大概占 23G值得开。3.3 训练过程怎么看loss 曲线和 mAP 的读法训练启动后runs/smoking/exp1/下会生成results.csv和一堆曲线图。重点盯三个量train/box_loss、val/box_loss、metrics/mAP50-95。正常情况train loss 持续下降val loss 先降后平mAP50 在 3080 轮之间快速爬升然后趋缓。如果 val loss 在 20 轮后开始上升而 train loss 还在降就是过拟合解决办法是加数据增强mosaic、mixup、加weight_decay、或者干脆减模型。吸烟检测里我特别关注mAP50而不是mAP50-95因为香烟框太小高 IoU 阈值下的指标天然偏低mAP50更能反映「有没有找到」。一个能用的模型smoke类的 mAP50 至少要到 0.75person_smoking要到 0.85。3.4 推理验证把预测框画出来眼见为实训完别只看数字跑一遍推理把框画出来yolo detect predict \ modelruns/smoking/exp1/weights/best.pt \ sourcesmoking_dataset/images/test \ conf0.25 \ iou0.5 \ saveTrue \ projectruns/smoking_predictconf0.25是置信度阈值吸烟场景建议先设 0.25 看召回误报多再往上提到 0.4。iou0.5是 NMS 的 IoU 阈值两个框重叠超过 0.5 就合并。输出图在runs/smoking_predict/下重点看三类失败案例漏检烟太小没框住、误检把手指、笔、耳机当成烟、重复框同一根烟出两个框。这三类问题分别对应数据、阈值、NMS 参数后面避坑章节细说。4. 数据增强与类别失衡5000 张怎么榨出更多信息4.1 针对小目标的增强组合YOLO 默认开了 mosaic、随机缩放、随机翻转。对吸烟检测我建议额外关注两个scale和mosaic。scale0.5表示随机缩放 0.51.5 倍能让模型见到不同大小的烟mosaic1.0把四张图拼成一张等效于增大 batch 和背景多样性但对小目标有个副作用——拼接后单张图里的烟更小了可能加剧漏检。我的做法是前期开 mosaic 加速收敛最后 20 轮关掉close_mosaic20让模型在真实尺度上做最后微调。这个技巧在 YOLOv8 里直接支持效果比全程开 mosaic 的 mAP 高 12 个点。4.2 类别失衡的处理过采样还是损失加权如果体检发现smoke类比person_smoking少很多有两个办法。一是过采样把含烟的正样本在训练列表里重复列几次。二是损失加权在smoking.yaml里没法直接配需要改训练脚本或用cls相关的超参。实操上过采样更简单写个脚本生成一个train_oversample.txt列出图片路径重复的路径多写几遍然后data指向这个 txt 即可。# 生成过采样训练列表含 smoke 类的图重复 2 次 import os img_dir smoking_dataset/images/train lbl_dir smoking_dataset/labels/train out [] for name in os.listdir(lbl_dir): if not name.endswith(.txt): continue stem name[:-4] img_path os.path.join(img_dir, stem .jpg) with open(os.path.join(lbl_dir, name)) as f: has_smoke any(l.startswith(0 ) for l in f) out.append(img_path) if has_smoke: out.append(img_path) # 含烟的图重复一次 with open(train_oversample.txt, w) as f: f.write(\n.join(out)) print(总训练条目:, len(out))逻辑是遍历标签判断是否含 class 0smoke含则把对应图片路径写两次。参数上重复次数可以调2 倍是保守值失衡严重可以到 3 倍。注意别重复太狠否则模型会对少数那几张图过拟合。4.3 负样本的用法让模型学会「不报警」5000 张里如果有一部分是「人在抽烟环境但没抽」的图千万别扔。把它们作为负样本空标签加进训练集能显著降低误报。我一般保留 5%10% 的负样本尤其是那些容易混淆的场景手拿笔、手拿吸管、打电话。这些正是模型最容易翻车的地方喂给它看它才知道边界在哪。5. 避坑与排查吸烟检测训练里最常见的 5 个翻车现场5.1 现象mAP 一直卡在 0.3 上不去loss 也不降原因八成是标签格式或类别顺序错了。常见的是 VOC 转 YOLO 时坐标没归一化或者names里 0/1 顺序和标注反了。解决跑一遍 2.3 的体检脚本重点看out_of_range计数再随机抽 5 张图用 LabelImg 打开对照确认框的位置和类别对得上。这一步花 10 分钟能省你两天瞎调参。5.2 现象验证集 mAP 很高实际视频里几乎检不出烟原因数据划分泄漏。5000 张如果来自连续视频随机划分会让验证集和训练集高度相似指标虚高。解决按视频源或时间戳重新划分确保同一段视频的帧只进一个集合。判断方法很简单——看验证集里有没有和训练集几乎一样的图有就是泄漏了。5.3 现象误报特别多把手指、笔都框成烟原因负样本不足或者conf阈值太低。解决先加负样本重训尤其是易混场景推理时把conf从 0.25 提到 0.40.5。如果还不行考虑在smoke类之外加一个hard_negative类专门收这些干扰物让模型显式学会区分。5.4 现象同一根烟出好几个框原因NMS 的iou阈值设太高或者模型对同一目标输出了多个高置信框。解决把推理iou从 0.5 降到 0.30.4让重叠框合并得更狠。训练侧可以检查是不是mosaic拼接导致同一目标被标了多次清理重复标注。5.5 现象训练到一半显存爆了CUDA out of memory原因batch或imgsz超过显存或者cacheram把内存吃满。解决先把batch减半再把imgsz从 640 降到 512 试cache改成disk或关掉。另外注意workers别设太大数据加载线程过多也会占显存。8G 显存的安全配置是batch8, imgsz640, workers4。6. 从能跑到好用把吸烟检测推到可部署的几个技巧模型训出来只是第一步真正上线还要过推理速度和后处理这两关。先说速度。5000 张训出的 YOLOv8n在 1080P 视频上单帧推理640 输入在 3060 上大约 58ms理论上能跑上百路但实际部署瓶颈往往在解码和画框不在模型本身。如果你要接多路视频建议用 TensorRT 导出yolo export modelruns/smoking/exp1/weights/best.pt formatengine halfTrue imgsz640halfTrue开 FP16速度能再快 30%50%精度掉得很少。导出 engine 需要目标机器装好 TensorRT 和对应 CUDA 版本版本不匹配是导出失败的头号原因先确认环境再导。再说后处理。吸烟检测有个业务特性单帧误报可以忍连续误报不能忍。我一般加一个时序滤波——同一个区域连续 N 帧比如 5 帧都检出吸烟才触发报警。这个逻辑用几十行代码就能写# 简易时序滤波连续 5 帧命中才报警 from collections import defaultdict hit_counter defaultdict(int) THRESHOLD 5 def update(track_id, detected): if detected: hit_counter[track_id] 1 else: hit_counter[track_id] 0 return hit_counter[track_id] THRESHOLDtrack_id来自目标跟踪比如 ByteTrackTHRESHOLD按帧率调25fps 下 5 帧约 0.2 秒既能压误报又不至于漏掉真吸烟。这个技巧是我在实际项目里被误报逼出来的纯靠调conf解决不了抖动问题。最后说验证。别只用 mAP 判断好坏拿一段真实场景视频跑一遍人工数 100 次报警里有多少是真的算出准确率。我见过 mAP 0.85 但实际准确率只有 60% 的模型问题全出在训练集和真实场景的分布差异上。补数据、加负样本、调阈值这三板斧轮着来通常两三轮就能把实际准确率拉到 85% 以上。这套流程我跑过不止一次最深的教训是数据质量永远比模型结构重要5000 张标得干净的图胜过 5 万张糊弄的。希望帮到你。本文还有配套的精品资源点击获取