☰
YOLO吸烟行为检测系统实战:从数据标注到部署的全链路解析
2026/10/19 2:42:52 网站建设 项目流程

去年接手一个巡检项目时,客户反馈很直接:几十路摄像头全天滚动播放,靠人盯着屏幕找违规吸烟行为,盯十分钟眼睛就花了,漏看率非常高,事后回查还要一帧一帧翻录像,耗时又痛苦。需求落到我这边,本质就是一个典型的端侧视觉目标检测任务。我当时选的路线是直接用YOLO系列模型做吸烟行为检测系统,从数据准备、模型选型、训练调参到最终部署,整体链路走了一个多月,第一版就达到了可用的水平。这篇就把这套系统从理论到实现的全过程拆开讲清楚,适合正在做目标检测落地项目、或者打算用YOLO做行为识别相关工作的开发者参考。

1. 先想清楚再动手:吸烟行为检测到底在检测什么

很多人在接到"检测吸烟"这个需求时,第一反应是打开标注工具直接框"烟"或者框"人",先把数据集堆出来再说。结果训练一轮发现模型在真实摄像头下表现非常奇怪:有人拿白色笔误报、有人抬手挠头误报、室外烟囱冒气也报警。这些问题的根子不在模型,而在最开始没有把"检测目标"这件事定义清楚。

1.1 检测的是"行为"还是"物品"

吸烟行为检测虽然挂在"行为"两个字下面,但本质上是一个目标检测任务,不是行为识别任务。行为识别一般需要时序信息,比如人与人之间的互动、动作的连续性;而吸烟检测在实际监控场景中,绝大多数情况可以通过"当前帧里有没有人在吸烟"来近似。也就是说,我们检测的是"正在发生吸烟动作的人"或者"烟与人同时出现"的空间特征,而不是像动作识别那样去建模"点烟—吸一口—放下—再吸一口"的完整过程。

这里有两种技术路线可以走:

  • 只检测一类,比如smoking_person,直接把正在吸烟的人作为一个整体框出来;
  • 检测两类,比如person和cigarette,再通过后处理规则判断"烟是否在嘴部或手部附近"。

我最终选择的是第二种。原因很简单:如果只检测smoking_person,模型必须自己学会区分"手里拿着烟但没抽"和"正在抽"这两种语义,这需要非常多精细标注的样本,否则模型会把所有出现烟头的画面都当吸烟,误报率会很高。而拆成person和cigarette两个类别之后,检测任务变简单了,语义判断交给了规则层,反而更可控。

1.2 吸烟场景的技术难点拆解

吸烟检测最大的难题不是模型结构,而是目标尺度与遮挡。

先说尺度。一个烟头在1080P画面里通常只有十几个像素甚至更小,尤其摄像头装在走廊顶部或者房间角落时,烟头在图像中就是一个小到几乎看不见的亮点。YOLO系列模型在小目标检测上本来就不是强项,如果不做针对性处理,烟头这类小目标的召回率会非常难看。

再说遮挡。人的手部动作变化多样:烟可能被手指完全遮住、可能因为角度问题只露出一截滤嘴、也可能在嘴部被侧脸轮廓挡住。还有一种很常见的情况:人在背对摄像头时,烟放在嘴边但脸看不到,只有烟头冒出来。这种情况下模型如果只看局部特征,很容易漏检。

然后是类间混淆。细长的笔、棒棒糖、遥控器、白色手机,在图像中和烟的形状很接近;室外的水蒸气、雾霾、烟囱排烟,在画面里和烟雾纹理相似。只要训练数据里这些负样本覆盖不足,模型就会不停地误报。

1.3 动工之前必须确认的三件事

在写任何训练代码之前,先跟业务方把下面三件事确认清楚,否则后面会反复返工:

  • 这个系统是实时告警,还是事后检索?实时告警对延迟有硬要求,通常单帧检测耗时不能超过500ms,这直接限制模型大小和输入分辨率;事后检索则可以把推理放到非实时链路里,模型可以选更大的。
  • 更看重召回还是更看重精确?在吸烟检测这种场景下,宁可偶尔误报也不希望漏报,因为漏掉一次吸烟行为可能就意味着安全隐患。这会直接影响置信度阈值的设置。
  • 摄像头安装角度是什么样的?顶装、斜装、平装对目标的形态影响差异巨大,顶装视角下烟头几乎只有俯视圆点,斜装视角下才能看到烟支侧面。这决定了标注规范和数据采集方式。

提示:这些确认不要只通过文档,最好直接去现场看几段真实录像。只有亲眼看到画面里的目标长什么样、光线有多差、人头攒动有多密集,才知道标注规范和模型方案该怎么定。

2. 数据工程:这套系统的成败其实在标注规范里

目标检测项目里,数据工作往往占整个项目百分之六十以上的时间。吸烟检测尤其如此,因为开源数据集很少,而且场景跟实际监控画面差距很大。网上能找到的一些吸烟数据集大多是手机拍摄的特写镜头,角度、尺度、画质都跟摄像头画面完全不同,直接拿去训练基本没法用。

2.1 采集策略:按摄像头视角分层抽取

我在这个项目里没有一上来就让人工去网上爬图,而是直接从现场录像里抽帧。采集要覆盖几个维度:

  • 时间段维度:白天、傍晚、夜间、凌晨,不同光线条件下的画面差异很大;
  • 摄像头点位维度:不同安装高度和角度的摄像头,目标形态差异很大;
  • 人流密度维度:单人场景和人群密集场景的遮挡情况完全不同;
  • 天气维度:室外场景要覆盖晴天、阴天、雨天,室内场景要覆盖不同照明亮度。

抽帧也需要讲究,不能简单地每隔多少秒抽一帧。如果录像里一个人从走进画面到走出画面有十秒钟,均匀抽帧会产生大量高度相似的冗余帧,而且可能把目标运动过程中的模糊帧也抽进来。比较合理的做法是先把视频按场景切分,然后每个场景内做抽帧,再通过聚类去重,确保训练集里同一目标不要出现太多相似样本。

2.2 类别标注设计的两种方案

如前所述,这个项目我采用了两类方案:person和cigarette。person类别负责提供人的位置与尺度上下文,cigarette类别负责提供烟的精确位置。在后处理阶段,我设计了这样一条判断逻辑:如果检测到cigarette目标,且该目标的中心点落在某一个person框的上半部分区域内(大致对应嘴和手的位置),同时这个关联关系连续保持若干帧,就判定为一次吸烟事件。

这套逻辑把误报分成了两层来过滤:

  • 第一层是模型层,cigarette类别的精确性决定了能不能把笔、手机这类干扰物挡在外面;
  • 第二层是规则层,只有烟目标与人目标空间关联成立时才会进入最终判定,能挡住烟囱、热水蒸气这类没有人的误报。

如果只做单一smoking_person类别,标注工作量会小一些,但是模型的语义学习负担会很重,采样不平衡的时候很容易偏向"有烟就框人",最后误报率居高不下。

2.3 标注规范的细节约定

多人协作标注时,标注规范如果不细到像素级,训练出来的模型边界框质量就会很差。我在启动标注前给团队写了一份标注说明,核心约定有这么几条:

  • cigarette框必须紧密贴合烟的可见像素,不能把整个手或整个嘴部都框进去。如果烟被完全遮挡,则不标注。如果烟只露出一部分,按可见部分标注。
  • 烟在嘴部时,即使被嘴唇遮挡了一部分,只要仍有可见像素,就标注;烟拿在手里垂下时同样标为cigarette,语义判断交给规则层。
  • 图像因运动模糊导致目标完全无法辨认时,不标注,但要在标注记录里标记该帧,后续可以删除或保留作难例分析。
  • 一张图里同一个人只能有一个person框,多人场景按检测目标实际位置逐个标注,人的遮挡区域不能截断,要按可见范围完整框出。

这些约定每一条背后都有对应的坑。比如"烟被完全遮挡则不标注",是为了避免模型学到"手里攥着东西就是烟"这种错误关联;"按可见部分标注"是为了保持边界框的一致性,否则标注员有的框手、有的框烟,模型训练时边界框回归会互相打架。

2.4 数据增强与合成策略

吸烟检测里小目标占比极高,如果按YOLO默认的数据增强方式直接跑,烟头这种小目标很容易在Mosaic拼图过程中被缩小到不可见的程度。我在训练时做了几个针对性调整:

  • 开启Mosaic增强,但对小目标做保护,限制Mosaic拼接时对目标框进行随机缩放的幅度;
  • 增加 CopyPaste 增强,把标注好的烟头目标随机粘贴到背景图像上,人为扩充小目标样本数量;
  • HSV色彩扰动范围调得比默认值大一些,因为实际监控画面中不同摄像头的白平衡差异很大;
  • 对原始图像做随机裁剪时,优先保证小目标不被裁剪掉。

数据量方面,我最后训练集里cigarette类别约有一万两千多个实例,person类别约有三万多个实例。这个量级训练出来的模型在内部测试集上mAP@0.5在80%左右,如果想让小目标召回更高,还需要更多低视角、近距离样本。

3. YOLO版本与主干网络选型:别只看榜单数字

YOLO系列已经迭代了很多个版本,每次出新版本都有人问"要不要立刻换新的"。我的态度一直比较务实:检测任务的核心是数据与场景适配,模型结构只要能满足精度和速度要求,稳定性比花哨的新特性重要得多。

3.1 YOLO系列演进脉络与各自擅长点

YOLOv5是绝大多数项目落地的安全牌。它的代码结构清晰、社区资料丰富、部署方案成熟,而且对显存不友好的问题做了大量优化。很多做工业视觉、安防巡检的团队至今主力仍然是YOLOv5系列。

YOLOv8最大的变化是改成了anchor-free结构,去掉了预设锚框的步骤,同时引入了C2f模块和解耦检测头。anchor-free的好处是少了anchor超参数调优的环节,对于新手更友好,训练流程也简化了不少;解耦头则让分类和回归两个分支各干各的,收敛速度更快。缺点是官方代码库迭代节奏快,版本升级容易带来行为不一致,需要额外留意。

再往后的版本,有的主打可重排可视化网络,有的主打无NMS推理。这些结构性创新确实有研究价值,但在真实项目里,如果团队没有深厚的训练和调优能力,盲目追新版本往往会花大量时间在新特性调试上,而不是解决实际业务问题。

3.2 版本选型对比

我在几个候选方案之间做了这样一个对比:

方案输入分辨率参数量级推理速度参考部署复杂度适用场景
YOLOv5s640约7M1080Ti上约3-5ms/帧低,资料最全大部分实时监控项目
YOLOv5m640约21M1080Ti上约6-10ms/帧低精度要求较高的场景
YOLOv8s640约11M性能与v5s接近中,需适配新结构的部署代码新项目无历史包袱
YOLOv8m640约26M适中中有较多标注数据、算力充裕
更大模型128040M以上明显下降较高离线分析或后处理资源充足

实际跑下来,v5s在精度和速度上的平衡已经足够。如果换成v8s,在同样的数据集上mAP通常会有一点提升,但差距并不是“质变”,尤其是在小目标烟头上的提升有限。所以最终我保留了YOLOv5s作为第一版部署模型,把节省出来的算力用在高分辨率输入和批量推理上。

3.3 主干网络到底在提取什么特征

聊选型的时候顺便解释一个很多人容易忽略的点:YOLO模型的backbone本质是一个多尺度特征提取器。浅层特征保存着边缘、颜色、纹理等信息,适合检测小目标;深层特征语义更丰富,但对小目标的位置信息不友好。吸烟检测既要靠浅层特征抓住"烟头亮点"这种极小目标,又要靠深层特征理解"人正在把烟送到嘴边"这种语义关系,所以FPN/PAN结构里的多尺度融合质量,直接影响最终的检测效果。

在部署选型时,不要只看参数量和mAP榜单。我的建议是抓一个核心指标:在目标最小尺度对应的特征层上,模型能不能稳定输出置信度。实际操作中,可以先用一张包含清晰烟头的图像做推理,看小目标的置信度分布;如果小目标置信度普遍低于0.5,那说明当前模型和分辨率配置不合理,可能需要提高输入分辨率或者使用专门的tiling推理方法。

4. 训练配置与调参实战:从过拟合到漏检的反复拉扯

数据处理完之后,训练阶段看似只要跑脚本,实际上里面有大量细节,每一步决策都会影响最终效果。我把自己在这个项目里踩过的坑和最终调参策略整理一下。

4.1 数据集划分不能按帧随机打散

这是第一个大坑。刚开始做数据划分时我用了随机抽帧之后按帧划分训练集和验证集,结果训练时loss下降很漂亮,验证集mAP也很高。后来换了一个新的摄像头点位录像测试,准确率掉了一截。排查后发现,问题出在划分方式上:随机按帧打散后,同一段视频里高度相似的帧被同时分到了训练集和验证集,模型其实是在"背答案",而不是真正泛化。

正确做法是按视频片段划分。把每段录像文件作为一个整体,按比例分配到训练集、验证集和测试集。这样验证集中的画面和训练集中的画面来自不同时间段或不同场景,模型没有见过内容重复的帧,评估结果才可信。

4.2 训练超参配置参考

下面是我在YOLOv5s训练时使用的超参数,不是标准答案,但可以作为参考起点:

超参数设定值备注
输入分辨率640×640如果小目标密集,可以尝试1280但推理耗时增加
batch size64显存不够时降到32,配合梯度累积效果更好
epochs200以验证集mAP不再提升为准
初始学习率0.01配合warmup两个epoch再进入稳定阶段
优化器SGDAdamW收敛快,但泛化性略差
类别损失权重cigarette权重设为1.2缓解类别不均衡
Mosaic开启前50轮开启,后50轮关闭以稳定收敛
置信度阈值训练时不设推理时再按业务需求调整

4.3 预训练权重与冻结训练

迁移学习是目标检测项目里少不了的环节。直接用COCO预训练权重可以显著缩短收敛时间,因为backbone学到了通用的边缘、纹理、形状特征。但需要注意:COCO数据集里没有"烟头"这种小物体,所以HEAD部分的训练要从头学起。

我的策略是分阶段训练:

  1. 第一阶段,冻结backbone,只训练检测头,学习率先设低一些。这样做的理由是让检测头先适应新的类别分布,避免backbone在初期被大幅扰动;
  2. 第二阶段,解冻backbone,用较小的学习率整体微调。这时候模型已经能输出相对稳定的框,backbone只需要在这个基础上做细微调整。

实测下来,这种分阶段训练比起直接全量训练,能稳定提高2-3个点的mAP,而且训练过程中loss曲线更平滑,不容易出现后期震荡。

4.4 训练曲线判读与难例分析

训练过程中不要只看最终的mAP数字,要养成看PR曲线和单类别指标的习惯。吸烟检测里我特别关注cigarette类别的召回率,如果这个类别的召回率低于0.7,说明大量烟头被漏掉了,再调置信度阈值也没用。

第一次训练完我拿了一批结果跑可视化,发现漏检集中在两类画面上:一是手在嘴前方快速移动的瞬间,烟被手指遮挡且运动模糊严重;二是夜间红外画面下,烟头本身有亮光但烟支轮廓很模糊。针对这些难例,我做了两件事:一是去数据集中专门挑出这些难例,复制并配以强数据增强,提高它们在训练中的占比;二是加入少量红外夜间样本,让模型学会在低照度条件下提取目标特征。

4.5 小目标漏检的专项治理

这个值得单独拿出来说。用640分辨率训练时,一个烟头在图像里平均只有10个像素左右,在YOLO的输出特征图中对应到小目标层可能只有一个特征点。我尝试了几种办法:

  • 提高输入分辨率到1280,小目标召回明显上升,但推理耗时增加了接近一倍;
  • 使用tiling策略,把图像切块后分别推理,再合并结果。对小目标效果很好,但多路摄像头实时性受影响;
  • 在数据增强阶段对包含烟头的区域进行过采样,让模型在训练时看到更多"放大的小目标"。

实际项目里我把cigarette类别小目标的检测作为单独指标统计,并在部署时采用了混合策略:主要检测用640输入,同时对画面中人脸/手部区域做一次局部裁切放大检测,兼顾了速度和小目标召回。

5. 模型导出与部署:摄像头下的真实现场比验证集残酷得多

训练完模型,工作才完成一半。模型落地到真实摄像头环境后,还要过一道"现实检验":画面模糊、光照突变、摄像头抖动、人流量暴增,这些在验证集里看不到的情况都会冒出来。这一章讲部署链路中踩过的坑和最终的工程方案。

5.1 模型导出链路与精度损失控制

我最终部署的框架选择是:PyTorch训练 → ONNX导出 → 用GPU推理加速转换(TensorRT或OpenVINO,视目标硬件而定)。导出时最需要留意的是算子的兼容性。不同框架导出的ONNX在一些自定义算子上的行为可能不一致,比如模型里的某些上采样方式在转换时会出现插值对齐方式不同,导致输出结果出现几像素的偏移或精度下降。

解决办法是导出后用同一张测试图分别在PyTorch和推理框架里跑一遍,比对每个类别在每个位置上的输出差异。如果差异超过千分之一,就需要检查算子是否被正确转换。量化方面,我用半精度(FP16)和INT8都测过。INT8在烟头这种小目标上精度下降比较明显,后期我为了精度优先还是保留了FP16,在T4级别显卡上单帧推理耗时仍然能控制在10毫秒以内。这里说的T4是一个常见部署显卡类型,实际用哪一档看项目预算和并发路数即可。

5.2 多路摄像头并发与抽帧策略

几十路摄像头如果每一路都满帧检测,显卡会撑不住,实际上也没必要。吸烟这个动作从点烟到吸一口,持续时间通常有几秒到几十秒,以5帧每秒甚至更低的检测频率就能覆盖。我最终采用的是动态抽帧策略:

  • 常规状态下每路摄像头每秒检测2-3帧;
  • 检测到烟目标后,该路切到更高频率(比如5-8帧每秒)做跟踪确认;
  • 确认吸烟行为后触发告警,同时截取前后几秒的录像片段用于人工复核。

这种方式把整体算力消耗压到了原来的三分之一,而且没有明显漏检事件。并发方面,多路视频的解码和数据传输经常成为瓶颈,比推理还费时间。建议使用专门的视频流拉取进程,把各路的帧放入带缓冲的队列中,推理进程从队列取帧,避免推拉流解码阻塞推理。

5.3 误报漏检专项治理:真实场景的经典案例

部署后第一周,我们接了很多误报反馈,逐条看下来很有代表性。

第一个误报场景:室外背景有一根白色水管,和烟支的形状很像,模型经常把水管上的一截白色接口框成cigarette。后来我把这类样本作为负样本强行加入了训练集,额外标注了白天、傍晚、下雨三种天气下的水管画面,让模型学了一轮负样本加权训练,误报明显下降。

第二个误报场景:夏天室外水蒸气,模型把一团白色雾气框成cigarette。这个单靠模型很难完全消除,因为雾气纹理和烟雾很接近。我最终靠规则层解决:只有当cigarette目标与person目标存在空间关联时才会进入告警流程,孤立的雾气检测结果直接被丢弃。

漏检方面,最常见的场景是侧脸低头动作。人低头看手机时嘴部被遮挡,烟头只露出一小块,模型经常漏检。针对这个,我在后处理里加了一个补充逻辑:如果已经持续检测到person靠近嘴部区域有cigarette目标,即使某一帧漏检,也会根据前几帧的跟踪状态保持"疑似吸烟"状态若干帧。这一招把短期漏帧的影响降到了最低。

5.4 告警联动与结果评估口径

部署后一定要有评估闭环,否则你根本不知道模型改动到底有没有效果。我给自己定的评估周期是:每周用真实场景录像跑一遍离线测试,统计下面几个指标:

指标计算方式项目目标值
漏报率漏掉的吸烟事件数 / 实际吸烟事件总数低于5%
误报率错误告警数 / 总告警数低于15%
单帧检测延迟输入帧到输出结果的时间差低于200ms
告警确认时间从事件发生到告警推送的时间低于3秒

告警推送之后,还必须有人工复核的出口。我的方案是检测到疑似吸烟行为后,把前后5秒的录像片段截屏或存成短视频,推送到复核工作台,由值班人员确认是否真实违规。这既是降低误报影响的手段,也为后续持续迭代积累了难例数据。

6. 最后分享几点实际操作中的体会

这套项目做完之后,我个人的经验总结有几点。

数据标注规范是整个项目中最值得花时间打磨的地方,它决定了模型精度的上限。标注规范写得好,多人协作出的数据质量就稳定;标注规范含糊,后续清洗和修正的成本会成倍增加。我后来在类似项目里都会先让标注团队做几十张试标注,逐条对齐边界情况再大批量铺开。

模型版本不用追求最新。YOLOv5s在第一版部署中足够用,升级到新版本是在业务效果稳定之后才有意义。真实项目里,稳定性、可复现性、部署生态成熟度往往比榜单上高出的零点几个mAP更重要。

最后分享一个工作习惯:每次调整训练集或模型参数后,我都会固定一组由不同点位录像组成的回归测试集,一键跑出mAP、漏报率、误报率三个核心数字。如果新改动在这三个数字上没有正向改善,就果断回滚。这个习惯让整个项目迭代过程少走了很多弯路,也让我在业务方追问"这个版本到底比上个版本好在哪里"的时候能拿数字说话。这套从理论到实现的链路并不复杂,难的是在每一个环节上都把细节做扎实。

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

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

立即咨询