☰
YOLO吸烟行为检测实战:目标定义、数据标注与时序判定
2026/10/19 7:44:06 网站建设 项目流程

做过几个月吸烟行为检测系统后,我最大的感受是:难点根本不在“跑通一个YOLO模型”,而在搞清楚“你到底要让模型检测什么”。很多人和我最初一样,上来就把目标定成“检测吸烟行为”,结果模型接收到的是烟雾、手势、烟头各种混乱信号,训出来怎么看怎么不对。这篇文章把我从理论到实现的完整思路、参数调节、时间序列判定逻辑、部署细节和踩坑记录整理出来,给你一条能直接上手的路线。

这套系统适合正在做目标检测项目、但拿不准“行为”类任务怎么做的人,也适合安防、园区、工地场景的技术负责人,至少能让你在立项前就知道哪些坑可以提前避开。我会尽量把每一个关键决策背后的原因讲清楚,而不是只给配置命令。

1. 整体设计思路:先把“吸烟行为”拆解成机器能学的东西

1.1 场景决定问题定义:检测烟支比检测行为更靠谱

做任何AI项目,第一步永远不是选模型,而是重新定义问题。吸烟行为在图像里的视觉表现有烟支、烟雾、手部姿势、嘴唇接触等多个特征。如果你把“吸烟”当作一个整体去标注,模型就会很迷茫:同样有烟雾,有人是在抽烟,有人只是刚端起一杯热水,视觉上怎么区分?同样把手凑到嘴边,有人是在抽烟,有人只是在挠痒。

我最终把问题收敛成:“在视频帧中检测烟支/烟头这个小目标,并在时序上判断它是否构成吸烟事件”。核心检测对象是烟支本身,烟雾和手势只作为辅助上下文,不单独建立检测类别。原因很直白——烟支的形态相对固定、颜色相对突出、尺寸在画面中可预期,YOLO这类单阶段检测器对这类刚性小目标有天然优势;烟雾是半透明、边界模糊、无固定形状的目标,拿它当检测对象等于给模型出难题。

场景决定了你要不要继续往下做。同样是吸烟检测,园区禁烟管理看重的是“有人在公共区域点烟”,可以容忍稍微慢一点的反应;工厂安全生产看重的是“在特定作业区抽烟”,需要快速告警;校园监控则普遍对隐私敏感,很多场景直接用普通摄像头做边缘计算,不把画面回传。这些场景差异直接决定了你的检测精度指标、报警延迟、部署形态和硬件选型,最好在项目启动前就跟需求方对齐。最怕的是什么?对方嘴上说要“检测吸烟行为”,实际上他要的是“检测到打火机点燃瞬间”,这两种实现路径完全不一样。

1.2 YOLO系列选型:为什么不是Faster R-CNN也不是更冷门的检测器

目标检测领域可供选择的模型很多,但我从一开始就锁定了YOLO系列。原因不是它技术最先进,而是它在“精度、速度、部署便捷度、社区生态”这个综合维度上,对工业落地场景极其友好。对比一下需求:

  • 速度:吸烟检测通常是实时视频流处理,摄像头数量一多,单路推理必须在几十毫秒内完成;
  • 部署:项目要跑在边缘设备上(Jetson、工控机、普通Windows主机都有可能),模型要能轻松转换格式;
  • 迭代:数据标注和模型训练是多轮的,社区生态越活跃,遇到问题越容易查到解决方法。

Faster R-CNN这种两阶段检测器精度上限确实不低,但推理速度在同等硬件上差了一个数量级。做离线图片分析可以,做实时视频流就很吃力。DETR系模型精度高,但小目标检测和部署工具有时候不够丝滑,边缘设备跑起来资源占用也偏大。相比之下,YOLO系列的版本迭代非常频繁,每个版本都有人踩过坑、填过坑,这种“被千万人用过”的模型,对做工程的人来说安全感太重要了。

1.3 系统架构分层:数据、训练、推理、业务各管各的

我在项目里把整个系统拆成四层:数据层负责采集、标注、增强和版本管理;训练层负责模型训练、评估、导出和迭代实验;推理层负责加载模型、执行推理、后处理和单帧检测结果输出;业务层负责把“单帧检测框”变成“有人正在吸烟”的事件告警,对接报警、截图、推送等动作。

这四层之间尽量解耦。训练层不关心摄像头怎么接入,业务层也不关心模型的预处理细节。这么做有个非常实际的好处:当摄像头更换、分辨率变化、或者客户要求把YOLOv5换成YOLOv8时,你不需要把整套系统推倒重来,只需要替换对应层的实现。我第一次做这个项目时没分层,代码全都耦合在一起,后来换模型、改阈值、增加新摄像头,每次都要动一堆地方,折腾了几轮才痛下决心重构。

2. 数据准备与标注实操:模型90%的价值在数据里

2.1 数据来源与采集场景设计

我见过太多人拿到数据集就直接开始标注,完全不思考数据分布问题。吸烟检测这个场景,数据最常出现的坑有两个:一是公开数据集里“摆拍的吸烟照”占比过高,真实监控画面占比过少;二是数据里近景吸烟特别多,远景、遮挡、侧身、低头这些真实视角严重欠缺。

我的数据来源通常组合三块:第一,公开数据集(很多社区开源的目标检测数据集里有“吸烟”或“烟”的类别,找到后先清洗一遍,看一眼标注框到底框的是什么),第二,自有模拟场景采集——找几个同事在不同光线、不同角度、不同背景下拿着烟比划,尽量还原真实监控机位的俯视角和距离;第三,网上公开的视频素材,抽帧后再二次筛选。

场景设计上必须穷举真实情况:室内日光灯、室外白天强光、傍晚昏暗光线、逆光、侧身吸烟、背对摄像头吸烟、吸电子烟、烟放在桌面上……每一个实际场景的缺失,都会在部署后变成一条漏检误报的投诉。我后来把数据需求列成一张检查表,每补齐一类场景就在表上打勾,这个习惯帮我少走了很多弯路。

2.2 标注规范:单类还是细分类?框怎么打?

这是整个项目最关键、也最容易被低估的环节。吸烟检测到底标几个类别?我最终选择了标记“smoking_target”单类,即“烟支或烟头”,而不是再把“手持香烟”“嘴中含烟”拆成多个类。原因在于,吸烟行为最终靠的是时序逻辑去判断,类别拆得太细会大大增加标注成本,而且类别间边界模糊,严重干扰模型收敛。

标注框的形状我也踩过很多次坑。一开始我习惯用规规矩矩的矩形把整个手部区域框进去,认为“手拿着烟”信息更完整。但实际训下来发现,这种标注让模型学成了一团乱七八糟的形状,框住手的不一定是烟,误检特别高。后来试了另一个思路:把框贴着烟支本体打,框尽量收紧,把烟支和指尖附近的区域一起包住,保证框内烟支占主体。这样模型学到的是“这个区域有烟支的特征”,而不是“这个区域有手的特征”,效果好非常多。

标注规范里有几条约定要特别注意:

  • 遮挡超过50%的烟支可以不标;
  • 画面中远处的烟支如果小于12x12像素,直接放弃标注;
  • 电子烟外形五花八门,初期先不标注电子烟,否则类别内差异过大;
  • 标完一轮后一定要抽样复查,同一张图两个标注员画框的IoU如果低于0.7,说明规范理解不一致,需要再对齐。

2.3 数据增强与类不平衡:别让模型只会认“标准动作”

吸烟检测场景的类不平衡问题非常隐蔽。最容易出现的情况是:正样本(有烟支)数量其实足够,但都是“标准吸烟姿态”——手在嘴前、烟支清晰。于是模型学到的其实是“手在嘴前 = 吸烟”,手没靠近嘴但夹着烟的就被漏掉了。我在测试时发现,模型对“手里夹着烟自然下垂”这种最常见的休息姿态,检出率低得离谱。

解决办法是数据增强策略调整。常规的随机翻转、裁剪、旋转、亮度变化肯定要做,但针对这个问题,我重点加了随机遮挡(模拟烟支被手指遮挡)、随机HSV扰动(模拟不同色温灯光下烟支的白黄颜色变化)、以及一种强人为增强——把“手持烟支”的裁切图随机粘贴到不同背景区域,模拟目标在不同环境下的分布位置。

Mosaic增强也建议打开,但要注意尺度问题。吸烟检测中烟支是小目标,如果Mosaic把四张图缩放太狠,小目标直接被缩没了,等于增强了个寂寞。我一般把Mosaic的缩放区间限制得保守一些,保证烟支在变换后仍然有足够的像素。

3. 模型训练与关键参数调优:最终效果在细节里拉开差距

3.1 预训练选择与前处理参数

YOLO训练的起点建议不要从零开始。网络骨架(Backbone)用ImageNet预训练权重或者官方仓库自带的预训练权重初始化,收敛速度快得多,泛化能力也明显更好。实际项目里用的最多的是官方仓库在COCO上预训练好的权重,因为COCO本身就包含“bottle”“cup”这些柱状物体,模型已经具备对“细长圆柱状目标”的基础特征感知,迁移到烟支检测上有天然优势。

输入分辨率(imgsz)的选择值得花时间考虑。512太小,远处的小烟支直接丢失;640是平衡点,大部分场景够用;960能明显提升小目标召回,但推理时间和显存占用同步上涨。我做过一组对比实验:640输入在某个场景下小目标漏检率大约是12%,切到960后降到7%左右。但代价是单路推理从15ms涨到35ms。最后我只能按摄像头数量和业务要求反推——摄像头少、对精度要求高,就开960;摄像头多、只做粗筛,就锁640。

3.2 训练超参数:batch size、学习率和epoch的劳动教训

先给一组我实测下来比较稳的基线配置:imgsz=640,batch=8(在显存有限的情况下宁可调小batch,也不要硬撑导致OOM),初始学习率lr0=0.01,最终学习率lrf=0.01,训练300个epoch,早停patience=30。这里面最值得说的是batch size和学习率的关系。

很多人新手期最容易犯的错是把batch设得很大,比如一张24G的显卡塞进batch=64,然后训练没几个epoch就开始发散。图像检测任务对batch size敏感,太大容易陷入局部很差的最优解,太小则梯度噪声大、收敛慢。我习惯用batch=8或16起步,然后配合线性缩放规则调整学习率——batch翻倍,学习率同步放大,这样能维持一个相对稳定的优化轨迹。在有限显存上,务实地用小batch、稍微多训几十个epoch,往往比强行上大batch得到的结果更稳定。训练过程中必须盯着loss曲线看,而不是无脑等它跑完。train loss和val loss都在下降是健康的信号;train loss降了val loss不降,说明开始过拟合,早停该出手就出手;val loss反复震荡,多半是学习率太高或者增强太狠,需要调理。

3.3 评估指标解析与阈值的取舍逻辑

YOLO训练完之后,很多人只看一眼mAP就宣布“模型好了”,这是最容易翻车的地方。mAP是模型整体精度的均值,它并不是你部署环境里的真实工作点。mAP@0.5好,不等于实际用的时候误检率低;mAP@0.5:0.95好,也不等于小目标检测没问题。我一般会额外看三个东西:混淆矩阵、Precision-Recall曲线、以及在不同置信度阈值下的实际表现。

吸烟检测这种安防告警类场景,最忌讳的就是误报过多。你阈值调到0.3,检出率上去了,但系统一天给你误报两千次警报,客户会直接关停。所以实际操作中我通常把默认置信度阈值调在0.35到0.5之间,具体要看业务倾向:园区禁烟更关心“逮到抽烟的人”,可以稍微放宽阈值;工厂安全生产监控更怕“没抽烟却有人被罚”,阈值就要收紧。NMS的IoU阈值一般设在0.45左右,太激进会合并掉相邻的真实目标,太保守会残留大量重叠框。

4. 行为判定逻辑与部署落地:从“框住烟”到“确认在抽烟”

4.1 单帧检测不等于行为识别,必须加时序逻辑

很多第一次做这个系统的人,把模型部署上去就宣布完成了。实际上单帧检测的结果只是“这一帧画面里存在烟支”,它无法区分“这个人正在抽烟”和“这个人只是夹着烟在走路”。一个可靠的吸烟行为判定,必须引入时间维度。我常用的规则是:以每秒5帧的分析频率,对同一个目标的连续跟踪结果做滑动窗口判断——如果烟支目标和“嘴部区域”的交集在一段连续时间窗口内持续出现重叠,比如10秒内有6秒以上检测到烟支贴在嘴边,就判定为一次吸烟事件。

这里有一个极其重要的细节:烟支不能丢了跟踪。跟踪用的是IoU匹配加卡尔曼滤波,在YOLO检测结果之间做关联。为什么不用纯目标跟踪算法?因为烟支太小,跟丢率太高,YOLO本身每帧都能给出检测框,直接在检测框层面做匹配更稳定。每个跟踪目标维持一个状态机:未确认态、确认吸烟态、离开态,只有从“未确认”跳转到“确认”才触发告警。这样可以把间歇性漏检导致的告警断裂剔除掉。

4.2 告警触发与去抖策略

监控场景里的姿态往往是连续性动作,但检测结果会有抖动:这一帧检出了,下一帧没检出,再下一帧又检出了。如果每检出一次就告警一次,系统根本无法使用。我最终的告警逻辑是一套简单的“计数+连续窗口确认”方案:连续10帧中至少检出6帧烟支、并且其中至少有3帧烟支框与嘴部区域重叠,才触发一次有效告警;同一个目标在告警后进入冷却期,比如60秒内不重复告警。

嘴部区域怎么来?不需要单独训练一个人脸关键点模型,直接在YOLO检测到的“人”框的顶部三分之一区域里再做一次人脸检测,取人脸框的下半部分作为“嘴部区域”的近似估计,对吸烟检测来说精度足够。如果你不想引入人脸检测模块,也可以在人的检测框内简单估算一个“脸部区域”比例,但准确率会略低。

4.3 部署方案选型与模型格式转换实战

模型训练完成后,需要把PyTorch模型转换成适合部署的格式。我按实际硬件条件分了三条路:纯CPU环境用OpenVINO;有NVIDIA显卡的基本都会导出到TensorRT,推理速度接近了毫秒级;纯测试或演示场景直接用ONNX Runtime最简单省事。转换的时候要特别注意几个坑:输入输出张量名称、动态维度选项、以及TensorRT的batch size固定问题。

实际部署我先在windows工控机上跑通了一整套流程:摄像头通过RTSP拉流进入主程序,主程序用独立线程池管理多路视频源,每路视频在子线程中完成“解码-缩放-推理-NMS-跟踪判定”,然后把事件结果放进共享队列,由告警线程负责发送消息记录、抓拍图片、推送通知。每当增加一路摄像头,只需要新增一个视频源配置,不需要改动其他逻辑。全程下来单路20ms-30ms的推理延迟确实稳定,在2路1080P摄像头并发时,CPU占用约30%左右。

5. 常见问题与排查心得实录

5.1 远处的“烟支小目标”漏检怎么办

这个问题的频率高居不下。烟支在监控画面中往往只有几十个像素,YOLO的默认检测头在特征图上对小目标的敏感度有限。我处理了三件事:把输入分辨率从640提高到960、增加一个浅层的小目标检测头、或者单独切出画面中的重点区域(比如休息区)放大后再送入模型。实践中第三种方法性价比最高——重点区域裁剪后用小分辨率跑一遍,几乎不增加整体耗时,但小目标召回率明显回升。代价是要多部署一个区域配置,管理上稍微复杂一些。

5.2 夜间、弱光、红外场景误检率飙升

弱光下烟支的特征衰减严重,模型倾向于把一些圆柱形白色物体(比如笔、卷起的纸张)误判成烟支。这个问题没有纯模型层面的完美解法,因为模型本质上是在做“形状+颜色”的模糊匹配。我在实际部署中加了两个辅助限制:一个是在夜间模式下调高置信度阈值,比如从0.4调到0.55,宁可漏掉一些也要控制误报;另一个是叠加强光模式判断——如果场景亮度低于某个阈值,就对检测结果做更严格的时序确认。条件允许的场景,我建议直接用带红外补光的摄像头,烟支在红外光下反射特征明显,实测能减少大量误检。

5.3 与“笔、口红、手指”等混淆物的对抗

有一次客户反馈说“我在镜头前吃个棒棒糖它都报警”,我一查,确实模型把人嘴前的任何细长物体都当作烟支。这是目标检测模型的固有缺陷,它只看到了局部纹理特征,没有看到“整根烟的语义”:烟支应该有一个“燃烧端”和“过滤嘴”的差异,但这个特征在极小目标上根本学不到。我后来在训练数据里刻意增加了大量“手持笔、手持口红、进食、喝水”的难负样本,让模型知道这些“长得像烟的物体”同样出现在嘴部附近时,它不能报。这个数据策略比调模型结构见效更快。

5.4 性能不达标:多路视频并发导致卡顿

真实项目里很少只有一路摄像头,通常是一台主机挂几路甚至十几路视频流。你的模型就算单路跑得飞快,放到多路场景中也会被拖垮。关键瓶颈往往不在GPU推理,而在视频解码线程和Python调度。我的排查路径是:先用profiler看CPU和GPU的时间占比,确认瓶颈位置;然后把解码和推理分开到不同线程,解码统一用硬件解码接口;再给每路推理设置独立的超时保护,防止一路卡死拖垮全部。这一套组合拳下来,5路1080P视频在普通配置的工控机上也能稳定跑到实时。

5.5 关于标注迭代和模型版本的几周感悟

做了几轮迭代之后,我最大的体会是:模型版本管理和数据版本管理同样重要。每一次数据增强方案的调整,每一次标注规范的变化,都会让模型的行为发生不可预测的变化。我后来养成了一个习惯:每个训练版本记录数据集的文件哈希、标注版本号、超参数配置和评估结果,生成一份简单的实验报告。这个习惯帮我在一次参数回退的时候,不到十分钟就找到了影响效果的那个改动,而非像之前一样对着十几个模型文件猜来猜去。

6. 再往前一步:这个系统还能怎么扩展

单点检测加时序逻辑已经能满足大多数场景需求,但如果你愿意,这套系统还可以横向扩展。比如与门禁系统联动,把“检测到吸烟”和“禁止进入”绑定;或者与数据库对接,将每天的吸烟事件创建一个热点图,辅助管理方优化巡查路线。另一个明确可行的方向是用蒸馏或者剪枝把模型压缩到更小,在更低成本的边缘设备上跑通更多路数。甚至可以把行为判定从“嘴前有烟”升级到“手拿打火机点火”的完整动作链识别,但这需要引入动作识别模型,复杂度会上一个台阶。

如果要用一句话总结我在这个项目里最深的教训,那就是:不要把“行为”塞给目标检测模型,把“目标”交给目标检测,把“行为”交给逻辑。模型负责在每一帧里找到烟支,逻辑负责判断这个人是不是真的在抽,各司其职,系统才真正可靠。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询