ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

火灾检测实战:基于8495张数据集的模型训练与误报优化

火灾检测实战:基于8495张数据集的模型训练与误报优化

做火灾检测这行的朋友都清楚,模型效果好不好,往往不取决于网络结构有多新,而取决于训练数据有多“脏”。我参与过一套火灾智能预警项目,前后折腾了大半年,真正让模型从“测试集自嗨”走向“现场能干活”的转折点,就是我们把原始图像从两万多张筛到最终保留的8495张可用样本。这批数据后来就成了项目里的“火灾数据集-8495”。

这个数据集不是什么公开榜单上的明星资源,而是一个很接地气的工程数据包:包含室内火焰、森林野火、烟雾早期迹象、夜晚火光以及大量无火正常场景。整个项目里,我们用这套数据做了一件事——训练一个能实时判断“画面里有没有火灾苗头”的检测模型,并尝试部署到厂区和园区监控场景中。如果你是刚入门视觉方向的学生,或者在做安防、消防预警、边缘端智能摄像头相关的工程,这篇文章应该有参考价值。我会从数据梳理、标注细节、模型选型、训练调参到现场踩坑,把整个过程按能复现的方式拆开讲。

1. 项目概述与数据规划

1.1 火灾检测为什么不能随手拿个公开数据集

火灾检测看起来是个二分类或目标检测问题,但真正动手会发现,它比想象中刁钻得多。火焰没有固定的形状、颜色、纹理,烟雾更是半透明、变化极快,还经常和雾气、蒸气、灰尘混淆。通用目标检测数据集里有关火灾的样本极少,分类数据集里所谓的“fire”类别也大多是壁炉、烧烤摊这种“可控火源”,拿到监控场景里直接跑,误报率能劝退整个项目。

所以我们需要一个专门的火灾数据集,而不是指望在某个现成的大模型上直接微调两下就能交付。火灾数据集-8495的构建目标很明确:覆盖“早期火灾信号”和“高混淆干扰物”两大类,模型不仅要能识别明火,更重要的是能区分“烟囱正常冒烟”和“房间起火冒烟”、“夕阳火烧云”和“远处真火焰”。没有足够数量的负样本和难样本,这些边界根本学不出来。

当时我们把数据按使用场景拆成四个子集:室内场景(办公室、仓库、机房)、室外场景(园区、停车场、马路)、森林/野外场景(用于扩展项目)、夜间场景。这四类场景的物理特性和拍摄条件很不一样,混在一起训练容易互相干扰,分场景评估又很麻烦,所以数据处理的第一阶段就是先按场景打标签,做好元信息管理。

1.2 8495张图到底够不够用

第一次听到只有8495张图,很多人第一反应是“太少了吧”。说实话,如果做的是Open Images级别的开放识别,这个量确实不够。但火灾检测是一个非常窄的任务,场景变化有限,目标语义也很单一,8495张图配合合理的增强方式,足够训练出一个可用的专用检测模型。关键在于数据质量和分布,而不是盲目堆量。

我们的数据切分逻辑是这样的:按拍摄地点和视频片段分组,先把同一场火灾、同一个摄像头来的连续帧放同一组,避免“同一场景既出现在训练集又出现在测试集”造成的数据泄漏。再按大约7:2:1划分训练集、验证集、测试集。这里特别强调,不能简单随机打乱图片,否则模型其实是在“背场景”,换个摄像头就废了。

类别分布上,我们设计了四类标注目标:fire(明火)、smoke(烟)、candidate(可疑火源,比如电火花、电焊光)、normal(正常背景)。前三个是检测目标,normal只是负样本。最终统计下来,带标注目标的图片约6100张,纯负样本图约2300张,整体正负比例大约3:1,不是特别失衡,但火焰和烟雾这两类之间数量差得比较多,后面增强时会单独处理。

2. 数据采集、清洗与标注实操

2.1 数据来源和合规整理

数据从哪儿来,这个问题最容易被人忽略,但恰恰是项目的命根子。我们这批8495张图,来源分三类:一是公开可下载的火灾图像集和学术数据集中的相关子集;二是版权允许的抓拍场景图,来自各种开源图库;三是与合作单位在受控环境下实拍的模拟火源场景,包括用烟雾机做的烟雾测试、用电焊光做的高亮光源测试。

如果你是个人项目,最省事的做法是去开源数据集平台找火灾相关子集,再自己补拍一部分。这里提醒一句,任何来源的数据都要保留来源记录、授权说明或引用信息,尤其是以后模型要商用,版权和合规问题会直接卡住交付。我们当时专门做了一张数据来源登记表,每一张图都记录了“来源批次-原始文件名-授权状态”,虽然前期麻烦,但后期对接审计非常省心。

2.2 清洗、去重与质量筛选

原始采集过来的图像大概有两万多张,最后只留八千多,中间的筛选过程比训练还花时间。第一轮去重,很多连续帧内容高度相似,画面里只有轻微变化,如果全部保留,模型会对这个场景过拟合。我们用感知哈希做初步去重,再按帧间相似度阈值0.85筛掉冗余帧,减少了大概40%的数据量。

第二轮是质量分层。监控场景里大量图片存在运动模糊、镜头脏污、雨雾遮挡、极暗光等问题,这类图不是不能用,而是要单独区分。我们把图像质量分成A/B/C三档:A档是清晰可辨的火灾目标,B档是轻度模糊或遮挡但人能判断的情况,C档是严重模糊或目标占比过小、基本不可用的。C档直接剔除,B档保留但会在标注时标记specialty属性,训练时降低采样权重。

最需要耐心的一轮是“语义去伪”。有些图看起来像火灾,比如落日、红色霓虹灯、红色集装箱,人眼一看就知道是假的,但模型不一定。我们把这些全部单独归入“hard negative”文件夹,作为难负例,后面训练时会用这些图专门压误报。这批难负例有1100多张,是整个数据集里最有价值的部分之一,模型能不能在真实环境里保持低误报,全靠它们。

2.3 标注规范与工具选择

标注工具我们用过两款,前期用开源的LabelImg做矩形框,后期因为有些目标形状太不规则、比如火焰边缘弥散,改用支持多边形标注的开源工具。标注工具本身不重要,重要的是标注规范。我们开会定了一套很细的规则:

  • 矩形框必须包含完整的火焰主体,允许包含少量周围环境,但框面积不能超过目标实际面积的1.5倍。
  • 烟雾目标如果面积占比小于整张图的1%,不标,避免产生大量学习噪音。
  • 火焰和烟雾同时存在时,优先标火焰,烟雾只在“有明显烟雾形态且没有火焰遮挡”的情况下单独标。
  • 夜晚场景中,火光映照在地面和墙面上的反光不能单独标成fire,必须紧贴实际火焰区域。

规范定完之后,我们还要求每个批次至少做一次“双人复核”,即标注员A先标,标注员B独立再标一遍,两份结果对比后保留置信度高的框。这个环节很费人力,但对后期模型稳定的帮助极大,尤其是小目标漏标问题,如果不做复核,模型对远处小火的recall会很难看。

2.4 类别不均衡与样本权衡

标注完统计,fire类目标约4300个,smoke类目标只有2100个,candidate类更少只有500多个。类别不均衡直接导致模型倾向只学fire,对smoke视而不见。我们当时没有简单粗暴复制smoke样本,而是做了一件事:把重点放在“早期火灾”场景上。

在火灾预警工程里,发现烟比发现火更有价值,因为火苗通常已经晚了。所以smoke的权重应该高于fire。我们的处理方法是:对smoke类样本做更强的增强强度,比如额外做左右翻转、小角度旋转、亮度扰动;对candidate类则主要靠“难负例挖掘”来逐步增加数量。同时,在损失函数里给smoke类提高了权重比例,这部分在YOLOv8的class weight配置里可以直接设置。

另一个点听上去简单但容易忘:训练时不光要看“有多少火车图”,还要看“有多少火源类型”。比如煤炉火、电气火花、纸张燃烧、油类火灾,虽然都叫fire,但形态差异很大。我们按火源类型统计了一遍,把数量明显不足的类别标记出来,再回头去补数据或者做针对性增强。这一步做完,模型对“电气火灾”和“纸张火灾”的区分能力提升了不是一点点。

3. 模型训练与效果提升

3.1 模型选型:检测为主、分类为辅

火灾检测项目里,检测模型和分类模型并不是二选一,很多系统是两者配合。比如我们用YOLOv8s做主检测器,负责在视频流里框出火和烟的位置;图片入另一个分支做轻量分类,判断整个画面“是否有火警风险”,两者结果融合后决定要不要触发告警。检测器的输出可以提供位置信息,分类器提供置信度再确认,能明显减少误报。

选YOLOv8s而不是更大模型,理由很现实:部署环境是边缘计算盒子,NVIDIA Jetson级别的算力,每秒要处理25帧实时流。YOLOv8s在640×640输入下推理延迟大概在20到35毫秒,基本满足要求。如果追求更高精度,可以选YOLOv8m,但对我们这个场景来说,s版本经过调优后已经够用了。

对比方案上,我们还试过SSD和EfficientDet。SSD的老版本对小目标能力弱,火灾场景里远处的小火苗正好是小目标,直接pass。EfficientDet精度可以,但工程生态不如YOLO系列成熟,部署时转换工具和硬件加速支持不够友好。所以如果你不想折腾,YOLOv8s是个很稳的起点。

3.2 数据增强策略:不能为了增强而增强

数据增强是火灾数据集训练里的双刃剑。一开始我们开了全套增强:mosaic、mixup、随机平移、旋转、色调变换,结果训练出来的模型在验证集上mAP不错,一放到真实场景就疯狂误报,后来发现是增强太狠导致模型学到了“不真实的纹理模式”。

最后我锁定的增强组合很简单有效:mosaic概率0.6,mixup概率0.3,随机左右翻转0.5,亮度扰动±30%,饱和度扰动±20%,轻微随机裁剪和缩放。没有开极端的旋转和透视变换,因为火灾监控摄像头大多是固定视角,水平翻转已经足够模拟不同机位。

对smoke类样本,额外做一组“仿烟雾扩散”处理:用形态学膨胀模拟烟雾变浓变大的效果,用模糊模拟远距离低清晰度的烟雾。这两招需要自己写预处理脚本,但效果好,对提升smoke的召回率贡献很明显。

3.3 训练参数与loss观察

训练参数我直接给一组实测可用的:输入640×640,batch size 16,初始学习率0.001,warmup 3个epoch,后续使用余弦退火,epoch数80到120不等(取决于早停设置),优化器AdamW,weight decay 0.0005。显存不够时把batch降到8,同步调低学习率到0.0005,效果基本接近。

训练过程中我最关注的是三个指标:训练集loss、验证集mAP50和mAP50-95。YOLOv8训练日志里会同时输出box_loss、cls_loss、dfl_loss,我一般不看单值而看整体趋势。如果box_loss前10个epoch下降得很快但mAP没涨,多半是数据标注不一致,需要回过头查标注。如果验证集mAP50掉得厉害而训练集loss很低,基本就是过拟合,需要加增强或调低模型的复杂度。

一个实用的小技巧:每训练完一个轮次,保存模型时默认是best.pt和last.pt,我建议额外对每个指标单独存一个版本,比如best_map50.pt和best_map50_95.pt,因为mAP50高分和mAP50-95高分对应的权重往往不是同一个。后续部署时可以选择更适合业务要求的那个权重。

3.4 评估指标与一次真实训练记录

火灾检测的评估指标不能只看mAP,还要看误报率、单场景漏报率和帧率。我们在测试集上的指标如下(训练100轮,输入640×640):

指标数值
mAP500.731
mAP50-950.462
火焰recall0.812
烟雾recall0.684
误报数(每1000帧)3.2次
推理延迟(Jetson边缘设备)31ms

烟雾recall明显低于火焰,这个结果符合预期。烟雾本身形状不固定、边界模糊,训练集里数量也更少。想再往上提烟雾指标,单纯调参作用有限,还是得从数据和标注入手。

当时我们做过一次对照实验:一组只用增强默认值,另一组加了上面说的烟雾形态学增强和smoke类损失权重。增强后smoke类的recall从0.58提升到了0.68,而fire的recall基本没掉,整体误报数还略有下降。这说明针对类的数据策略比全局无差别增强更有效。

4. 常见问题与排查技巧实录

4.1 标注错误带来的连锁反应

火灾数据集标注里最容易出问题的是漏标和框偏。漏标最常见的场景就是小目标:远处的火焰只占几十个像素,人眼一滑就过去了,但这样会在训练时把目标区域当成背景,强行教模型“这里没有火”。

我们后来想了个笨办法但非常有效:每轮标注复核时,专门做一次“小目标全检”,把标注结果里所有小于50×50像素的框列出来,逐个放大检查。如果发现某个场景频繁漏标小目标,就判定为该场景光线或对比度问题,在后处理时对这个场景做局部亮度增强,而不是一味责怪标注员。

框偏的问题则更多体现在边界上,尤其是火焰的弥散边缘和烟雾的透明区域。我们的规则是宁可框小一点,也不要框进大量背景,因为框进背景等于送给模型大量“背景也属于火灾”的错误信号。这类问题在后期几乎只能靠双人复核去发现,机器很难自动检查出来。

4.2 误报怎么压:难负例和阈值调优

误报是火灾预警系统最致命的痛点,因为一次误报就可能让整个系统被关掉。我们的误报来源集中在三类:红色灯光/红色物体、太阳光反射、炊烟/工业蒸汽。第一类最难搞,因为视觉上和火焰确实像。

压误报最有效的一招是“难负例训练”,也就是我们前面收集的那批hard negative图片,每轮训练都重复采样,让模型反复看到“看着像火但不是火”的样本。另一个实用手段是调整推理阶段的score阈值和检测类别逻辑。YOLO默认score阈值一般是0.25,我们实际部署时调到0.4以上,对candidate类再单独提高阈值,因为这类本来就是“可疑”而不是“确认”。

如果某个场景误报还是压不掉,最简单的办法是加“空间上下文”约束。比如户外监控画面中,火焰出现在天空位置的概率极低,如果模型在天空位置报出fire,极大可能是火烧云或阳光,这时可以用后处理规则把它过滤掉。这类工程规则不需要改模型,但对现场误报率的下降立竿见影。

4.3 小目标检测困难与切图方案

火灾在早期阶段往往就是一个小小亮点,在全画面里可能不到1%。YOLOv8在640×640下对小目标的召回能力一般,解决思路有两个:一个是增加P2检测层(大模型自带的能力),另一个是“切图推理”。我们在部署时对1080p的监控画面切成四块重叠区域,分别推理后合并结果,小目标recall提升了大约14%。代价是推理耗时增加,边缘设备上从31ms变成60ms左右,但依然能跑实时。

如果不想牺牲速度,还可以换一种思路:训练一个独立的“小火苗识别”轻量分类器,只负责在大画面上找异常亮点候选区域,再把候选区域送到主检测模型里做二次判断。这种两阶段流水线在极早期火灾检测场景里是常见的工程方案。

4.4 训练不收敛和过拟合的鉴别

训练到一半发现loss不降或者波动剧烈,先不要怀疑模型结构,按优先级排查:第一,检查数据标注文件有没有错位,bbox坐标和类别ID是否对应正确,尤其是人工编辑过文件之后容易引入格式错误;第二,检查是不是有类别名称大小写不一致,这会让模型以为有两个类;第三,检查增强参数是否过强,有些增强在深色场景下会产生全黑或全白的垃圾图,直接污染训练。

过拟合的典型特征是验证集mAP不涨但训练集loss很低。面对这个情况,我一般先检查训练集和验证集的场景分布,确认验证集里没有大量训练集同场景的连续帧,然后再考虑降低模型容量或加强正则化。如果数据量少、场景单一,增加训练轮数没有帮助,还不如重新调整数据划分。

5. 项目复盘与部署心得

5.1 数据版本管理比想象中重要

这个项目做到后期,我们最大的感受是:数据集一定要做版本管理,不是简单一个文件夹存着就行。一张图片经过了裁剪、增强、重命名、标注增删,可能衍生出十几个版本,如果不对每个版本编号和时间戳记录清楚,实验结果根本没法复现。

我们后来广用了一套简单的命名规则:数据集名称-版本号-生成日期-说明。比如“fire_det_v3.2_20240510_aug”,每次训练前在训练脚本里固定这个数据版本号,日志里也记录model用的哪个数据版本。这样事后谁问起来,我们都能准确回答“这个结果是用哪批数据、哪个增强参数、哪个预训练权重跑出来的”。

5.2 部署与灰度验证

部署阶段我们做了两轮灰度测试。第一轮用录制好的监控视频回放,看看模型在稳定画面下的表现;第二轮接入一路真实摄像头,但只记录告警日志不通知最终用户。灰度期间发现一个很典型的现场问题:夜间红外模式下,火焰的色调和白天的普通RGB差别很大,模型在白天表现很好,晚上开始出现漏检。

解决办法是把红外图也纳入训练集增强策略,具体做法是把一批RGB图做灰度化和红外风格的伪彩色处理,让模型看到更多“颜色分布”的可能性。灰度切换后,夜间漏报率下降了一半左右。所以如果你要部署在24小时运行的场景,训练数据里一定要包含不同摄像头模式下的图像,而不是只在白天集上训练。

5.3 后续扩展建议

火灾检测这个方向想做深,8495张图只是个起点。后续至少有两条路可以走:一是接入视频时序信息,用一个小型的时序模型判断连续帧上的动态特征,比如烟雾的扩散趋势、火焰的闪烁频率,这些信息单帧很难表达但时间轴上非常明显;二是利用半监督和自监督方法,把监控现场不断产生的新增视频数据利用起来,通过模型初筛加人工抽样确认回流到训练集,形成一个可持续进化的数据闭环。

如果让我说一句最真实的经验,那就是:火灾检测模型就像消防员,本领再大也得在真实火场里练过才靠谱。别迷信论文里的SOTA,多花时间在数据和场景适配这两个环节上,效果比更换任何新模型都更直接。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进