简介:YOLOv8吸烟行为检测资源包,面向目标检测开发者和安防、控烟场景应用人员,提供了一套可直接运行的训练与部署方案。包内含已训练好的模型权重、基于PyTorch框架的Python源代码,以及5000多张使用LabelImg标注完成的吸烟行为图片,图片统一为jpg格式,标签同时提供xml与txt两种形式,类别为smoke,可直接用于YOLO系列模型的二次训练、验证与推理。各种训练曲线图和检测效果示例也随包收录,方便用户评估模型收敛情况。资源共2000个文件,主要类型包括txt标注/说明、md文档笔记、py训练与推理脚本、yaml模型配置等,压缩包总体约346.15MB,目录结构清晰,便于按需查阅。当前已有728人学习下载,适合需要快速搭建吸烟检测基线、免去繁琐标注工作,或希望以此为模板开展类似行为识别任务的研究者与工程师。
1. YOLOv8吸烟行为检测:训练好的模型和数据集,拿到手离“能上岗”还差几步
第一次拿到打包好的 YOLOv8 吸烟行为检测项目,很多人会以为解压、跑通、交差就完事了。权重有了,数据集有了,官方推理脚本也给了,流程看起来是通的。我第一回也是这么想的,结果在一个 640 分辨率的普通摄像头上跑了半天才缓过神:demo 能转,不等于现场能稳定报警。白色水杯误报、远处小目标漏检、夜间直接“睁眼瞎”,这些问题全藏在训练好的模型和数据集背后,需要你自己去补。这篇文章想说的就是,YOLOv8 吸烟行为检测这个方向从拿到成品到真正落地,需要认清原理、跑通推理、重训适配、排查踩坑的完整路径,适合正在做智慧安防、工地或园区吸烟监测的开发者,也适合刚接触目标检测、想用现成资源做项目的同学。
2. 吸烟行为检测为什么用 YOLOv8:原理、选型与数据标注
2.1 先讲原理:YOLOv8 怎么把一个“人在抽烟”变成两件事
吸烟行为检测本质上不是一个“识别整段画面在干嘛”的问题,而是一个“找目标、算关系”的问题。摄像头要告诉你的是哪个区域有人、手里有没有烟、烟是不是在嘴附近。这个需求天然落在目标检测框架上:模型输出类别和边界框,之后在业务层做规则判断。如果改成视频行为识别,你只能得到“这一秒有人在抽烟”的粗结果,无法告诉现场值班人员具体工位,这是落地项目不能接受的。
YOLOv8 在目标检测领域被选中的原因很直接:它是单阶段模型,一次前向就能同时输出所有目标的位置和类别,推理速度快,适合摄像头实时流。相比之前的版本,YOLOv8 全面转向了 anchor-free 结构,模型不再依赖预设的 anchor 框,而是直接从特征图上的点预测目标中心与宽高。这种设计让不同尺度的目标回归更稳定,训练时对数据集分布的敏感度也比老版本低一些,省去了聚簇算 anchor 的步骤。
从工程角度看,YOLOv8 的官方库把训练、验证、导出、推理几个环节统一了,一个 CLI 命令或者一个 Python 对象就能跑完整流程。这对做落地的开发者很重要,因为你不需要在数据加载、模型定义、后处理这些环节自己写胶水代码。框架内部会处理 Mosaic 增强、自动学习率调度、NMS 后处理,这些在过去都是需要手写的大头。
不过要注意,YOLOv8 并不是专门为吸烟检测设计的,它是一个通用检测器。训练好的模型里存的是“这个形状像什么”的统计规律,而不是“这个动作是不是在抽烟”的语义逻辑。真正决定能否上岗的,是数据集覆盖的现场分布和你在业务层叠加的判断规则。所以拿到训练好的模型后,首要任务不是直接上线,而是搞清楚它是在什么数据分布上训出来的,再决定要不要用现场数据做微调。
2.2 模型尺寸怎么选:同是 YOLOv8,n 和 s 的差距能差出几倍算力
YOLOv8 官方按网络深度和宽度给出 n、s、m、l、x 五个尺寸,参数量和精度依次递增。很多人看到“训练好的模型”就直接用默认版本,不去区分尺寸,这是第一批容易翻车的地方。选型号的底线是:先看部署环境的算力,再看场景对帧率的要求,最后才看精度。
我自己一般这样权衡:
| 模型尺寸 | 参数相对量 | 推理速度 | 适合部署环境 |
|---|---|---|---|
| YOLOv8n | 最小 | 最快 | 边缘盒子、低功耗 ARM 设备 |
| YOLOv8s | 中 | 较快 | 普通 x86 工控机、多数项目默认起点 |
| YOLOv8m | 中偏大 | 中等 | 服务器或 GPU 设备,追求精度 |
| YOLOv8l/x | 大 | 慢 | 离线批量分析、高精度场景 |
如果你拿到的训练好的权重是 v8n 或 v8s,但部署目标是多个摄像头并发推理,建议先用 CPU 或低端 GPU 做个压力测试。不要只看单帧速度,要看并发路数叠加后的实际帧率。举个例子:模拟项目X 里我接手过一个现场方案,8 路摄像头同时接入,单路上跑 YOLOv8s 勉强 12 帧,一旦白天光线变化大,检测本身就掉到 8 帧,最后只能把模型换成 v8n 并降低输入分辨率,才保住实时性。
输入分辨率也是选型的一部分。YOLOv8 默认 imgsz 是 640,这对大多数近距离场景够用。吸烟检测中香烟本身是个很小的目标,如果你的摄像头覆盖范围较大,可能要直接用 960 或 1280 的输入分辨率来换取小目标召回率。但分辨率翻倍,推理耗时大约翻四倍,这个账要提前算清楚。
我在本章的建议是:拿到训练好的模型,第一件事确认它对应 n/s/m/l/x 哪个尺寸,然后和你自己的算力资源做匹配,不要盲目迷信“参数越大越准”。对吸烟检测这种以中近距离小目标为主的场景,v8s 往往是最平衡的起点,跑不顺再往下切,精度不够再往上加。
2.3 数据标注形态:框人、框烟还是框手,决定了数据集能否复用
很多打包好的数据集打开之后,你会看到两种完全不同的标注风格,这对后续工作影响极大,必须在一开始就弄清楚。第一种是只标一个类别“smoking”,把吸烟的人整体框进去,优点是标注简单、模型容易收敛,缺点是无法定位烟在哪里,业务层很难做精细判断。第二种是把“cigarette”“person”作为独立类别分别标注,模型先找出烟和人,再由业务逻辑判断烟是否贴近嘴部。
从可复用性角度看,我推荐倾向第二类,或者在第二类基础上加一个“手”类别。因为只框“smoking”类别的模型,一旦部署环境里出现类似手持白色异物的动作,很容易产生混淆;而把烟作为独立目标检测出来后,你可以用阈值判断烟的位置与人脸或手部区域的交叠关系,误报率明显更低。
标注文件本身是 YOLO 格式的 txt,每一行对应一个目标:类别序号、归一化的中心点 x 和 y、归一化的宽和高。这里有一个隐藏的坑:很多打包数据集用 0 代表“smoking”,但用跨摄像头测试时,模型换了一个环境,类别编号没有对应好,会导致推理结果全部错位。所以我建议拿到数据集后,先打开几个标签文件核对类别序号和 data.yaml 的 names 映射,确认顺序一致,再开始训练或推理。
另外要考虑标注的边界一致性。如果训练集里“cigarette”的框有的只框烟头,有的框整根烟,模型学到的目标中心点位置就会不稳定。这种细节问题在验证集上不容易暴露,一旦换摄像头、换角度,mAP 会掉得很明显。建议花时间统一标注规则,明确“从烟蒂到烟头全部纳入框内”,再做后续训练。
3. 用训练好的YOLOv8权重跑通吸烟检测:最小命令与参数
3.1 环境准备与权重文件放置:先让模型能跑起来
拿到训练好的模型,最常见的形态是一个.pt权重文件,比如smoke_best.pt。先不要急着往项目里塞,我建议新建一个干净的 Python 环境,再装官方依赖,避免和已有 torch 版本打架。这里有一个经验:.pt文件本质上是一个用 torch 序列化保存的完整模型状态,它和你本地的 torch 版本有兼容性要求,环境版本差太远会直接读取失败。
# 建议使用 Python 3.9 或 3.10 的独立环境 conda create -n yolo python=3.10 conda activate yolo # 安装官方推理依赖,会自动带上匹配的 torch 版本 pip install ultralytics这段命令的逻辑是:先用 conda 隔离环境,防止污染其他项目;再通过 pip 安装 ultralytics,它会根据平台自动拉取配套的 PyTorch。如果你拿到权重时附带说明文件,里面提到的依赖版本和你当前环境不一致,宁可多花几分钟重建环境,也不要强行在旧环境里跑,否则后面排查问题会非常被动。
接下来把权重文件放在一个固定目录,比如weights/smoke_best.pt。推理时全部通过路径引用。这里建议不要在项目根目录直接放权重,后续训练会产生新的 best.pt 和 last.pt,容易覆盖混淆。目录结构可以参考:
smoke_project/ ├── weights/ │ └── smoke_best.pt ├── data/ ├── runs/ └── inference.py有的打包项目给的是 ONNX 格式或 TensorRT 引擎文件,这类文件不依赖 PyTorch,导入方式不同。但大部分“训练好的模型”默认给 .pt,因为它还能继续微调,灵活性最高。如果拿到的是 .pt,用YOLO()加载即可;如果拿到的是 .onnx,要配合 onnxruntime 或对应推理引擎使用。
3.2 一个 predict 函数同时吃图片、视频和摄像头
官方推理接口非常简洁,用 Python 调用时,图片、视频文件、摄像头序号都走同一个source参数。这对落地调试很友好,因为你可以先用图片验证模型是否正常,再换成视频,最后接摄像头,不用改任何代码。
from ultralytics import YOLO # 加载训练好的权重 model = YOLO("weights/smoke_best.pt") # 图片推理:source 传图片路径即可 results = model.predict( source="data/example.jpg", # 也可以是视频 mp4 或摄像头序号 0 conf=0.25, # 置信度阈值,低于该值的目标被丢弃 iou=0.45, # NMS 的 IoU 阈值 imgsz=640, # 推理输入分辨率 save=True, # 保存标注后的结果图 )这段代码的核心思路是一次加载、多端复用。conf影响漏检和误检的平衡,iou影响重叠框的合并力度,imgsz影响精度和速度。这三个参数是后续调优的主力,先不要动模型和数据,把这三个值调整好往往就能解决大部分问题。
命令行方式也支持同样的参数,适合快速验证:
yolo predict model=weights/smoke_best.pt source=0 conf=0.25 iou=0.45 save=True注意source=0表示默认摄像头,如果摄像头被占用,会报 no device 之类的错误,可以先换成图片路径确认环境正常。我第一次跑摄像头推理时,摄像头灯亮了但画面全黑,排查半天发现是系统权限没给终端摄像头权限,不是代码问题。这类环境问题很容易浪费时间,遇到异常先看硬件权限和驱动状态。
3.3 conf 和 iou 是落地前最先要调的两个参数
很多新手拿到模型,只看一张效果图就决定上线,这是错误做法。训练好的模型在公开测试集上表现好,不代表现场光线和角度下同样好。第一个要调的参数就是conf。
conf是置信度阈值,默认 0.25。它的表现是:调高则误报减少但漏检增多,调低则漏检减少但误报增多。在吸烟检测场景里,我一般先按 0.25 跑一遍视频,统计误报数量;如果白色物体误报明显,就逐步提高到 0.35 或 0.4;如果现场远距离检测不到烟,则降低到 0.15 左右试一次,再结合其他手段控制误报。
iou是 NMS 非极大值抑制的 IoU 阈值,默认 0.45。它控制的是两个重叠框是否合并。如果吸烟检测中同一个目标出现两个重叠框,可以适当调低iou让它们合并得更快;如果担心相邻两个人都被漏检,则保持默认值即可。这条规则和场景关系不大,建议只调conf,iou保持 0.45 或 0.5,除非看到大量重复框,否则不必改动。
# 快速批量对比:分别跑两个阈值,看结果目录中的图片差异 yolo predict model=weights/smoke_best.pt source=data/street.mp4 conf=0.25 save=True yolo predict model=weights/smoke_best.pt source=data/street.mp4 conf=0.40 save=True判断阈值是否合适的标准不是单帧视觉效果,而是一段连续视频中“误报次数”和“漏检次数”的综合统计。建议取一段 5 到 10 分钟的现场录像,人工标注其中有多少个真实吸烟片段,再用不同conf跑一遍,找出误报和漏检平衡点。这个过程看起来土,但非常有效,比盯着单张效果图猜阈值靠谱得多。
4. 用自己的数据集重新训练YOLOv8吸烟检测模型:数据准备与训练流程
4.1 数据集目录、标签格式与训练/验证划分:让数据长成 YOLO 认识的样子
拿到打包的数据集,第一步不是开始训练,而是检查目录结构是否符合 YOLO 规范。YOLOv8 默认要求数据集根目录下包含images和labels两个子目录,再各自划分train和val。少一个子目录,训练直接报数据集为空。
smoke_dataset/ ├── data.yaml ├── images/ │ ├── train/ │ │ ├── 0001.jpg │ │ └── 0002.jpg │ └── val/ │ ├── 1001.jpg │ └── 1002.jpg └── labels/ ├── train/ │ ├── 0001.txt │ └── 0002.txt └── val/ ├── 1001.txt └── 1002.txt标签文件内容格式是每一行class_id x_center y_center width height,其中坐标全部除以图片宽高做归一化。常见错误是直接保留了原标注工具输出的像素坐标,训练后模型输出全乱。我一般写一个小脚本做格式转换并顺便校验坐标范围:
import os def convert_label(x1, y1, x2, y2, img_w, img_h): # 将像素坐标转为 YOLO 归一化格式 dw = 1.0 / img_w dh = 1.0 / img_h x_center = (x1 + x2) / 2.0 * dw y_center = (y1 + y2) / 2.0 * dh w = abs(x2 - x1) * dw h = abs(y2 - y1) * dh return f"0 {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}\n"这段脚本的作用是把常见的左上右下坐标转成 YOLO 需要的中心点加宽高比例格式。注意img_w和img_h必须是图片真实宽高,有的标注工具会输出缩放后的坐标,直接转换会让框偏移。转换后建议写一个反向验证脚本,把坐标还原到图片上画框,人工抽查几十张。
数据划分方面,我一般按 8:2 或 9:1 划分训练集和验证集,并且保证验证集里包含不同光线、不同姿态的样本。如果打包数据集已经划分好了,也建议再随机抽样一遍,避免训练集全是白天、验证集全是傍晚的情况。
4.2 在线增强:mosaic 与 close_mosaic 的正确用法
YOLOv8 默认开启在线数据增强,其中效果最明显的是 Mosaic 增强:把四张图随机裁剪拼接成一张,相当于用同样的数据量扩充出更多前景和背景的组合。这种增强对吸烟检测特别有价值,因为它让模型看到更多“烟在画面不同位置”的分布。
但 Mosaic 增强也有副作用。拼接后的目标尺寸被压缩,小目标信息丢失。因此在训练后期,建议关闭 Mosaic 让模型用真实比例的目标做精调。Ultralytics 提供了close_mosaic参数,意思是最后多少个 epoch 关闭 Mosaic:
yolo detect train \ data=smoke_dataset/data.yaml \ model=yolov8s.pt \ epochs=100 \ imgsz=640 \ batch=16 \ device=0 \ close_mosaic=10这里close_mosaic=10表示最后 10 个 epoch 停止 Mosaic 增强。逻辑是前 90 个 epoch 用丰富的拼接数据让模型充分拟合类别特征,后 10 个 epoch 用正常图片校准目标框精度。这是一个很实用的调参习惯,尤其是目标相对较小的吸烟检测。
如果你发现训练时每个 epoch 耗时特别长,先别急着怪显卡,先看是不是数据读取瓶颈。把workers参数调高一般能缓解,但 Windows 下 workers 太高会报内存错误。训练轮次方面,100 个 epoch 是常见起点,不一定训满,官方库默认开了早停,验证集 mAP 多轮不涨会自动停止。
4.3 训练启动、断点续训与结果文件解读
训练启动前还需要确认data.yaml里的 names 和数据集标签中的类别序号一致。比如你的数据集只有一类“smoking”,那么 names 写成{0: 'smoking'}即可;如果烟和人分开标,就要写成两类并保证标签文件里对应。类别序号对不上,训练不会报错,但 mAP 会永远偏低且难以排查,这是最容易踩的隐藏坑。
数据配置文件的内容很直观:
path: smoke_dataset train: images/train val: images/val names: 0: smoking训练完成后,输出默认保存在runs/detect/train/目录下,重点是weights/best.pt和weights/last.pt。best.pt是验证集指标最好的权重,last.pt是最后一个 epoch 的权重。迁移和推理优先用best.pt。训练日志里重点看metrics/mAP50和metrics/mAP50-95,前者是重叠度 50% 时算正确的指标,后者更严格。吸烟检测这种小目标场景,mAP50 不低于 0.7 才比较可靠。
如果训练中断,不用重头再来,用resume参数可以继续:
yolo detect train resume=True它会自动读取runs/detect/train/weights/last.pt继续训练。也可以用resume=runs/detect/train/指定目录。这个功能在长训练任务里很实用,我经常在模拟项目X 里跑到一半发现验证集分布有问题,修正数据后从 last 权重继续微调,而不是全部重训。
还有一点值得说明:如果你手头只有别人训练好的模型和数据集,重新训练时建议以此为起点做“微调”而不是“随机初始化”。因为你的现场数据通常只有几百到几千张,数据量远不足以从零收敛。选择一个预训练权重,用较小的学习率继续训练,是拿到数据集后最快的适配路径。
5. YOLOv8吸烟行为检测的高频问题与排查:五个容易翻车的现场
5.1 现象:白色水杯、白色地面被当成香烟
这是吸烟检测上线后最常见的误报。模型对白色细长物体的特征响应很强,白色水杯边缘、反光地面、甚至一根白色数据线都可能被框成“烟”。原因是训练集中正样本的亮度和形状特征过于单一,模型没有学会“烟通常出现在手附近且尺寸较小”这一隐含关系。
解决思路分两步。第一步,在推理时适当调高conf,比如从 0.25 提到 0.35,误报会明显下降。第二步,在业务层加一条规则:检测到烟后,确认其边界框是否与人体区域重叠,如果烟的位置远离人体区域,则丢弃。这种后处理比单纯依赖模型更可靠,也不影响真实吸烟样本的召回。
5.2 现象:摄像头稍远就漏检,烟在画面上只有十几个像素
小目标检测是 YOLO 系列的短板。当摄像头安装高度超过三米,或者人离摄像头超过五六米,烟在 640 分辨率画面里通常只剩 10 到 20 像素宽,模型很难稳定识别。很多人第一反应是换更大模型,但大模型改善有限。
正确做法有两种。第一种,把推理输入分辨率提高到 960 或 1280,代价是速度下降,但小目标召回率明显提升。第二种,对原始图像做切片推理,把画面分成多块分别检测再合并结果,这种方式效率更高,但代码复杂度也上升。先试前者,不够再上切片方案。
5.3 现象:白天精度正常,晚上或逆光环境下“睁眼瞎”
夜间的摄像头图像普遍对比度低、噪声大,训练集如果以白天正常光线为主,模型自然在夜间失效。逆光场景则相反,人脸和手部过暗,烟的反光被淹没。这类问题的根源是训练数据与现场数据分布不一致。
排查时先做数据层面的对比:把夜间现场帧导出几百张,放进模型里看失败样本,确认是亮度问题还是对比度问题。解决手段可以是收集夜间帧扩充训练集,也可以先对输入图像做预处理增强。我常用 gamma 校正或直方图均衡化,让暗部细节更明显,再进模型。但要注意,如果现场摄像头自带红外模式,图像整体是灰度风格问题,这就不能靠调图像能解决,得专门收集红外样本做微调。
5.4 现象:训练 loss 在降但 mAP 卡在 0.3 左右,或者 loss 直接变成 nan
mAP 卡在较低水平,最常见的原因是验证集标注存在大量错标,或者类别序号不对称。YOLOv8 不会检验你的标签是否全部正确,模型只能学标注里的“标准答案”,如果答案本身错误率高,mAP 上限就不可能高。先用可视化脚本把验证集标签画到图上,人工翻一圈,通常能发现不少框明显偏移的问题。
loss 变成 nan 则多发生在学习率过高或模型结构不稳定的情况下。解决方法是把初始学习率调低,例如从默认 0.01 调到 0.005,并适当增大warmup_epochs。还有一个容易忽略的原因:数据里出现了损坏的图片文件,某个 batch 读入了空 tensor 导致梯度爆炸。排查时看中断时对应的样本路径,删除损坏文件即可。
5.5 现象:加载权重报错,KeyError 或 torch 版本不兼容
加载现成模型时报 KeyError 或PytorchStreamReader failed,通常不是模型文件本身损坏,而是模型训练时的 torch 版本和你当前环境的 torch 版本差别太大。Ultralytics 迭代速度快,新版本库可能不再兼容老权重结构。
遇到这类问题,不要急着怀疑打包项目坑人。先看权重文件训练时配套的 ultralytics 版本,如果有 requirements 文件,严格照装;如果没有,就回退到几个常见的稳定版本逐个试。更稳妥的方式是优先加载.pt文件后立刻做一次小图推理,确认输出正常,再做批量推理。模型文件能加载和能正确推理是两回事,中间差一个版本兼容性测试。
6. 让吸烟检测真正可用的两个进阶技巧:连续帧投票与推理加速
6.1 连续帧投票:用 5 帧结果代替单帧判断,把误报率压下来
单帧检测本身的误报率再低,放到 24 帧每秒的视频流里也会被放大成可观次数。一个稳妥的优化是引入滑动窗口投票机制:不因为单帧出现烟就报警,而是统计最近的 N 帧中检出帧数,超过阈值才触发。吸烟动作通常持续数秒,完全来得及等这个确认过程。
from collections import deque class FrameVoter: def __init__(self, window=5, min_hits=3): self.history = deque(maxlen=window) self.min_hits = min_hits def update(self, detected: bool) -> bool: # 记录当前帧检测结果,返回是否应触发告警 self.history.append(detected) return sum(self.history) >= self.min_hits这段代码的逻辑很直接:window控制统计最近多少帧,min_hits控制至少检出几帧才告警。摄像头帧率按 10 fps 算,5 帧窗口加 3 帧命中,意味着误报要连续高频出现才会触发,对偶发的白色物体反光非常有效。代价是真实吸烟报警延迟约半秒,对监控场景几乎无感。
6.2 导出 ONNX 再走推理引擎:保住精度的同时换速度
.pt 权重适合研究和微调,但直接部署到生产环境通常不够快。常见做法是先导出 ONNX 中间格式,再进一步转成推理引擎格式。ONNX 导出后模型计算图被固定,省掉了动态图的额外开销,推理速度在 CPU 或 GPU 上都有提升。
yolo export model=weights/smoke_best.pt format=onnx imgsz=640导出时要注意imgsz必须和推理时保持一致,否则推理性能会下降。ONNX 导出后要顺手做一次精度对比,用同一张图片分别跑.pt和.onnx,确认检测结果一致。有时模型结构包含训练时才有的模块,导出后精度会小幅下降,这是正常现象,但下降幅度不应超过 1% 到 2% 的 mAP。如果差距过大,优先检查导出时是否被简化了关键节点。
我习惯把连续帧投票放在推理引擎之后,让加速后的检测输出进入投票器,再决定是否给值班平台推送告警。这两个技巧加在一起,既解决了误报率,又解决了算力瓶颈。以前我图省事直接把单帧结果接到告警平台,上线第一天误报上百条,后来加了时间窗口过滤才消停。这条路其实不复杂,关键是先想清楚模型边界在哪里,再用工程手段补足它。希望帮到你。
本文还有配套的精品资源,点击获取