做火灾检测这块也有段时间了从最早的帧差法、传统颜色阈值分割一路用到深度学习的目标检测最后落在YOLOv8-SEG上做火焰分割。前阵子正好有人问我火灾火焰分割数据集要怎么训练这里把整个实操流程和踩过的坑完整梳理一遍。开头先说明一点火焰分割和普通物体分割最大的区别在于火焰没有固定的形状、没有清晰的边缘还伴随大面积的高光噪点和烟雾扰动。用常规分割思路去做很容易得到一堆“轮廓像样但边界乱跳”的mask。YOLOv8-SEG这类实例分割模型在处理这种半透明、高频变化的火苗轮廓时反而比很多语义分割模型更稳。原因后面详细说。这篇文章的核心内容包括火灾分割数据集怎么构建、标注规范怎么定、YOLOv8-SEG怎么选型号和调参、训练完怎么评估效果以及推理落地时最容易翻车的几个细节。适合正在做消防预警、火焰识别、烟火监测的朋友参考也适合想把实例分割应用到其他不规则目标上的读者。1. 火灾分割为什么选YOLOv8-SEG而不是传统分割方案1.1 火焰目标的特殊性决定了模型选型方向火焰和常规目标不一样它有三个明显特征一是形状极其不稳定一阵风过来火苗能拉长两三倍同一段火势在不同帧里轮廓差异巨大二是颜色和背景容易混淆橙色火焰和暖色灯光、夕阳反光、路灯在色度空间里非常接近三是存在大量半透明区域火焰中心可能过曝白亮边缘则是渐变的透明橙红硬分割很容易把边缘裁掉。这直接导致了一个问题如果只用颜色阈值加形态学处理这种传统方式遇到暗光环境、复杂背景或者是视频中的反光点误检率会高到没法用。换成语义分割模型比如U-Net或DeepLab虽然对火焰轮廓的表达比检测框更精细但训练时需要逐像素打标签且对小目标和远距离火源的召回不理想因为语义分割本质上是逐像素分类对目标实例的感知能力偏弱。而YOLOv8-SEG走的是实例分割路线它同时输出目标检测框和每个目标的像素级mask。这让它天然具备了三个优势可以区分“这里有一团火”和“这里有火灾”能够过滤掉单点火花噪点mask是附着在检测框之上的训练时模型会先圈出候选目标再细化边缘对远距离小火苗的召回率明显高于纯语义分割模型一个模型同时完成检测、分割两个任务部署时省一套工程。实际实验里我用相同标注数据做对比YOLOv8-SEG在mAP50上的表现比同条件下语义分割版本更能应对复杂背景的误检尤其是路灯和车灯光晕这类难例。1.2 和传统检测框架相比分割带来的增量价值接着第二个问题既然已经有成熟的目标检测模型比如YOLOv5、老版YOLOv8能检测出火焰框为什么还要上分割这就涉及火焰监测的实际应用场景了。在消防场景里决策的并不仅仅是“有没有火”而是“火势有多大”“着火区域占多少面积”“火苗是否在蔓延到新的区域”。检测框只能告诉你一个大致的矩形范围遇到斜向燃烧火苗或者火源周围有烟雾扩散框的覆盖率会变得很不稳定。而分割mask是像素级的能够输出火焰的实际轮廓面积再配合摄像头标定信息可以做火势面积估算、蔓延趋势预测这是检测框做不到的。另外在实际报警系统中分割mask还有一个非常实用的作用过滤误检。火焰检测最容易误报的就是车灯、手电筒光柱、反射光斑这类高亮区域。这些区域虽然被检测框框住但它们通常没有锯齿状的燃烧边缘也没有火苗内部的纹理变化。分割模型在生成mask时会学习这些细节特征一旦目标内部纹理和高亮模式不符合火焰的典型结构置信度会显著降低。实测中加了分割分支后我这边系统的误报率大概降了三到四成这是很值得的。2. 火灾分割数据集的构建与标注规范2.1 数据来源和筛选标准数据集的质量基本上决定了分割模型的极限。火焰分割对数据的要求比普通检测更高因为mask需要像素级标注如果原始图像本身不清晰标注和训练都会出问题。我建议数据来源分成三块第一块是公开的火灾图像数据集重点挑选那些包含不同场景、不同光照条件的图像优先选白天户外、室内暗光、夜间路灯混合光、工业厂区等多样性高的样本第二块是自采数据用手机或监控摄像头对着真实火源拍注意固定机位和不同角度都要有第三块是从视频中抽帧用一段连续燃烧过程的视频每隔几帧抽一张这样能够得到同一火源在不同燃烧阶段、不同烟雾遮挡程度下的连续样本这对训练火焰的动态变化很有帮助。筛选数据时有一个容易忽略的点尽量清除模糊帧和严重过曝帧。如果图像的火焰区域已经纯白一片完全看不到纹理层次那这个mask即便标出来了对模型学习的价值也不大因为它的边缘信息已经丢失了。宁可只保留1000张高质量图像也不要硬塞进5000张里面夹杂大量废帧。我用过的经验是一个合格的火焰分割训练集至少需要覆盖以下变化维度火源大小、距离远近、背景复杂度、光照强度、遮挡物烟雾、树干、柱子。2.2 标注工具与YOLO-SEG数据格式标注工具我用的是LabelMe导出格式选择YOLO格式时会有个版本匹配问题。LabelMe默认导出的是JSON格式由标注坐标乘以图像宽高得到真实像素坐标需要转换成YOLO分割格式即每行类别ID后续是x1 y1 x2 y2 ...归一化坐标。注意YOLO分割格式的坐标顺序是1除以图像宽度2除以图像高度。转换脚本这里直接给一个我常用的版本基于Pythonimport json import os def labelme_to_yolo_seg(labelme_json, img_w, img_h, out_txt): with open(labelme_json, r, encodingutf-8) as f: data json.load(f) lines [] for shape in data[shapes]: if shape[shape_type] ! polygon: continue cls_name shape[label] cls_id class_names.index(cls_name) points shape[points] normalized [] for x, y in points: normalized.append(round(x / img_w, 6)) normalized.append(round(y / img_h, 6)) line str(cls_id) .join(str(v) for v in normalized) lines.append(line) with open(out_txt, w, encodingutf-8) as f: f.write(\n.join(lines))YOLO官方格式里每个目标的mask点数没有固定上限但建议控制在20到80个点之间。点太多会拖慢训练时的数据加载点太少则标注不够精细尤其对火焰这种不规则形状至少需要四十个点以上才能勾勒出锯齿形的燃烧边缘。标注完成后目录结构按YOLO的约定整理dataset/ ├── images/ │ ├── train/ │ └── val/ └── labels/ ├── train/ └── val/数据集配置文件写成data.yamlpath: /path/to/dataset train: images/train val: images/val names: 0: fire2.3 标注细节火焰边缘、烟雾和高光噪点怎么处理这是整个训练过程中最吃经验的部分。第一次标注火焰的人通常按直觉把火苗整个区域圈起来结果训练出来的mask边缘像毛刺一样形状还很离谱。原因就在于标注时没有处理好火焰的三个难点。第一个难点是边缘界定。火焰的主体是燃烧发光区域这个区域边缘呈锯齿状且高频抖动。标注的时候我建议把明显的火舌主体完整标注但对于那些向外飘散、已经半透明的火星絮状部分如果面积很小就不要包含进mask否则会引入大量边缘噪声让模型学到“糊成一片”的错误轮廓。要让标注边框始终贴着发光区域的轮廓走宁可略小一点也不要包含过多半透明过渡区。第二个难点是烟雾要不要标。严格说烟雾不等于火焰但在火灾场景里浓烟往往比火焰出现得更早而且能为火焰定位提供上下文。实际项目里我建议区分两种情况如果只做火焰分割就把烟雾排除在mask之外只标明火焰区域如果目标是烟火检测建议给烟雾单独开一个类别并单独标注。千万不要把烟雾和火焰混在同一个mask里否则类别内的外观变化太大模型很难收敛稳定。我之前就在这个点上踩过坑混合标注后模型的mAP掉了至少八个百分点。第三个难点是高光区域和反射噪点。火焰中心往往有高温白亮区这部分虽然过曝但仍属于火苗的一部分标注时应该包含在mask内。但需要注意图像中有大面积暖色反光但并没有火焰的地方比如夕阳照在玻璃幕墙上、暖色路灯下的积水反光这些不应该标为火焰。如果训练时这类难负例不足模型就会把大量暖色高光区域误判成火所以需要专门收集一批不含火焰但有类似视觉特征的负样本放进验证集和训练集。3. 训练环境与模型选型3.1 环境搭建与依赖版本YOLOv8-SEG的官方实现基于Ultralytics库安装很简单但深度学习中版本一致性是个说不完的话题。这里列出我实测稳定的一套环境组合Python 3.10CUDA 11.8 cuDNN 8.9PyTorch 2.1.0Ultralytics 8.1.x本机显存单卡12GBRTX 3060及以上安装命令直接执行pip install ultralytics8.1.0 torch2.1.0 torchvision0.16.0需要特别注意一个坑Ultralytics的版本迭代很快前后版本的默认参数变动比较大。如果你用最新的8.2.x某些超参数的默认值和旧版本不同直接套用网上旧教程的参数会导致结果对不上。建议锁定版本至少在你完成这个项目前不要动不动就升级。3.2 模型规格选择经验YOLOv8-SEG有五个规格n、s、m、l、x。选择的原则不只看显存还要看你的部署平台和对精度的要求。如果要在嵌入式设备或Jetson上跑建议用n或s规格推理帧率能跑到30帧以上但小目标火焰的mask边缘会比较粗糙如果是在服务器上做离线分析或者对报警精度要求高建议用m或l这是精度和速度比较均衡的档位x规格一般不太推荐对火焰这种目标来说参数增大带来的收益远不如普通物体识别明显而训练和推理成本却成倍增加。我的实际建议是第一轮先用yolov8s-seg作为baseline跑通整个流程确认数据没有问题后再根据第一轮的结果决定是否提升规格。不要一上来就训练x规格因为火焰数据的标注质量偏差会对结果产生很大的影响如果标注有问题再大的模型也无法拯救。4. 训练实操与参数调优4.1 训练命令与参数解读数据准备好后训练命令如下yolo segment train \ modelyolov8s-seg.pt \ datafire_seg.yaml \ epochs200 \ imgsz640 \ batch8 \ lr00.001 \ lrf0.01 \ optimizerSGD \ mosaic1.0 \ fliplr0.5 \ cacheTrue \ patience50 \ projectfire_seg_runs \ nameexp1参数不多但含义很重要我逐个拆解一下。epochs200对火焰这种中小型数据集来说是比较合理的范围头几次跑可以先减少到100个epoch快速验证流程确认loss正常下降后再正式拉长。imgsz640是精度与显存的平衡点如果你的火焰目标面积普遍很小占比低于5%建议调大到960或1280输入分辨率小目标的分割效果会有明显提升但显存占用和训练时间都会相应增加。batch8在12GB显存下训练s规格比较合适不要盲目调大。真正的优化在mosaic1.0这个参数在火焰分割里如果不开模型在小目标召回上会明显退步。Mosaic增强会把四张图拼接成一张相当于变相增大模型对小目标的感知能力。但也要注意火焰图像的mosaic拼接容易产生颜色断层和拼接缝如果发现前期loss一直不降可以降低到0.5试试。patience50是早停策略如果50轮验证集mAP都没有提高就会自动停止。cacheTrue表示把图像提前加载进内存能大幅缩短每个epoch的时间但要留意内存占用。我试过用15000张图缓存内存占用大概40GB。4.2 迁移学习策略第一次做分割训练的人容易犯一个错误以为分割任务要冷启动从头训练。实际上YOLOv8-SEG的预训练权重已经具备很强的mask生成能力我们要做的是在这个基础上调整它去理解“火焰的纹理和形状”而不是让它重新学习“什么是图片”。用yolov8s-seg.pt作为初始权重是默认策略但有两个细节值得注意。第一如果你的数据集和COCO差异较大比如你的训练图像全部是夜间红外摄像头画面建议冻结backbone的前十层只训练neck和head防止预训练特征被破坏太多。实现方式是在训练时传freeze10参数。第二如果数据量很少比如只有几百张学习率要调低到lr00.0005否则可能一上来就把预训练权重扰动得太厉害导致学习到的火焰特征不稳定。迁移学习还有一个技巧是分阶段训练。我先用100个epoch训练一个初始模型跑通流程后再加载这个模型的权重把lr0降到0.0001用较高分辨率imgsz1280继续训练50个epoch。这一阶段可以理解为精细化调整在牺牲一些速度的情况下显著提升mask边缘的精度。两次训练之间可以加入一些集中采集的难例数据效果会更好。4.3 数据增强与类别不平衡的应对火焰分割任务里类别不平衡往往是明显存在的问题。可能你的图像里有80%是“没有火焰”的远距离小目标样本只有20%是近景大火焰。这种情况下优化器会把大量精力放在大目标上小目标火苗的mask会严重退化。应对方法有三板斧。第一用小目标复制粘贴增强通过脚本把标注图像中面积小于一定像素的火焰mask裁剪出来随机粘贴到其他背景图像中同时调整标注坐标这是在YOLOv8中实现小目标增强的常用方法。第二调整损失函数中的目标权重Ultralytics对分割的overlap_mask参数建议保持默认但如果你发现mask边缘粗糙可以开启overlap_maskTrue让预测标注之间的重叠区域也参与梯度计算对改善边缘贴近度有帮助。第三验证集评估时按火焰面积分桶统计看近景和远景分开后的mAP分别是多少而不是只看一个总指标被高占比的大目标拉高。颜色增强方面需要特别克制。火焰识别最大的误检来源就是颜色接近的物体如果开了大量颜色抖动增强比如hsv_h0.05、hsv_s0.7、hsv_v0.5这些默认参数虽然能让模型更抗光照变化但同时也会把暖色非火物体的特征进一步向火焰靠拢导致误检率上升。我的实测经验是把色相抖动降到0.02饱和度抖动降到0.3保留亮度抖动但不要过头对抗光照变化的同时也避免颜色空间过度污染。5. 评估指标与效果验收5.1 主要指标解读训练结束后runs/segment/exp1目录下的results.png和results.csv就是我们主要看的数据。火焰分割不能只盯着总mAP需要分层看。第一个指标是mAP50表示IoU阈值在0.5时的平均精度。火焰检测的容错率其实可以适当放宽因为报警系统只关心有没有火、火在哪里边缘像素偏移几个点影响不大所以mAP50至少要0.85以上才算及格。第二个指标是mAP50-95这是更严格的评价标准IoU阈值从0.5到0.95逐步抬高取均值。它反映mask与标注的真实贴合程度。对火焰来说因为有半透明边缘和动态变化跟静态物体相比天然吃亏。不要强求它达到0.8以上只要能稳定在0.6到0.7说明mask轮廓整体是可用的。第三个指标是mask_mAP和box_mAP两个分支的差异。如果box_mAP远高于mask_mAP说明模型其实能检测到火焰但mask生成质量差优先检查标注边缘是否粗糙、数据增强是否过度形变如果mask_mAP远高于box_mAP则说明模型的分割能力强于实例区分能力可能出现把两个相邻火苗合并成一个mask的情况需要加强负样本和小样本训练。5.2 可视化结果检查清单指标之外必须肉眼过一遍预测图。我每次都会生成一批验证集的预测可视化按以下清单逐项检查火苗边缘是否有不合理的直线切割或锯齿噪点同一火源是否被拆分成多个碎块mask高亮反光区域是否被误报成火焰夜间低照度下火焰mask是否明显缩水不同距离下的mask面积占比是否和实际观察一致这里推荐使用训练时自带的val自动可视化每次验证都会输出一批带GT与预测对比的图片。如果发现碎片化mask同一目标被拆成多块多半是NMS参数过严或者mask置信度阈值太高可以通过调低训练时的nms_iou或者在导出模型时降低conf阈值来缓解。评估还要注意不要把切分出来的验证集图像与训练集图像来自同一段连续视频的相邻帧。火焰燃烧的动态变化本身就大容易造成数据泄漏的评估导致结果虚高。建议按视频片段切分比如来自视频A的前80%段作为训练后20%段作为验证保证验证和训练的时间不重叠。6. 推理部署与落地经验6.1 视频流推理的坑训练完了不等于能直接用。视频流推理里火焰分割有几个特有的坑这里单独提一下。第一个是时序抖动。火焰本身的轮廓是抖动的所以前后帧的mask抖动可能不是模型的锅。但我们不能让它抖得看不出固定逻辑。我常用一个简单的时间滤波对每个目标的mask置信度做滑动平均把连续三帧的置信度取中值超过阈值才触发报警。这样可以有效过滤零星的闪光误检。第二个是摄像头视角产生的形变。火焰在画面中心时mask比较准确但在画面边缘因为透视畸变mask可能会被拉伸。这种情况建议在预处理阶段对边缘区域做降权或者干脆设置一个ROI区域只对画面中心区域做分割推理边缘区域交给传统检测作为辅助。推理代码用Python写比较直观from ultralytics import YOLO model YOLO(runs/segment/exp1/weights/best.pt) results model.predict( sourcefire_video.mp4, imgsz640, conf0.4, iou0.5, device0, saveTrue, save_txtTrue, classes[0] )conf0.4是我对火焰场景的推荐值这个值不算高因为火焰的边缘变化大导致得分普遍偏低如果设太严会把真实火苗过滤掉。剩下的误检靠时间滤波解决。classes[0]表示只对fire类做推理如果多类别模型不设这个参数会额外产生大量其他类别的无用计算。6.2 模型导出与精度衰减控制分割模型部署时还有一个重要环节是精度衰减控制。把PyTorch模型导出为ONNX或TensorRT后通常会出现精度下降火焰这种任务对精度衰减特别敏感因为它的目标边缘模糊格式转换时的量化误差容易被放大导致边缘丢失。导出TensorRT的参考命令yolo export modelruns/segment/exp1/weights/best.pt formatengine device0 halfTrue imgsz640导出后一定要在自己的数据上对比一下精度差多少。经验上FP16的TensorRT在火焰分割任务上的mAP下降不要超过2个百分点如果超过2个点检查是否存在较大的mask标注面积部分模型结构在量化精度上比较吃亏。在部署时如果显存允许优先用FP16而不是INT8。INT8量化在火焰分割上通常下降4到6个点除非对延迟要求极其严苛否则不建议。7. 常见问题与排查实录7.1 问题排查速查表现象可能原因解决方案loss前期不降mosaic增强过度导致拼接图像失真将mosaic降到0.5或关闭再试mask边缘呈锯齿状标注多边形点数不足标注时增加边缘控制点至少40点/目标小火焰目标完全漏检输入分辨率过低提高imgsz到960或1280单独用小目标增强夜间低照度大面积误检数据集中夜间样本不足补充夜间数据降低hsv_h增强相邻两团火被识别成一块NMS阈值过低调低nms_iou到0.4评估时检查mask拆分训练很快过拟合数据量太少且增强不足增加mosaic、色彩扰动或用更强的正则如dropoutweight_decay推理帧率远低于预期half未开启或模型规格过大导出时开启halfTrue或换s规格模型7.2 排查心得补充几点过拟合是火焰分割里最隐蔽的问题判断方法不仅是看loss曲线更要用独立拍摄的视频去验证。因为火焰本身动态性强模型很容易“记住”训练视频里的特定燃烧姿态换个环境就立刻失效。我在这上面栽过跟头训练集mAP达到了0.92换了一个厂区的监控视频漏检率直接飙到15%。后来补了大量不同机位、不同光照的样本才恢复正常。所以判断模型是否过拟合一定要拿完全没见过的视频来测而不是只看验证集的数字。另一个容易被忽视的是背景主导问题。如果90%的训练图像都是地面视角的火焰你的模型会对高空俯瞰视角完全无感。如果需要高空全景监控一定要在数据集中加入足够多的俯拍样本不然模型对火苗的“长宽比例”和“朝向”的偏见会让它在某些场景里完全失效。7.3 最后的部署调整模型真正上线后还要记得在流程上加一个确认机制。仅靠单帧推理就触发报警是会出事的。我建议报警逻辑做成这样单帧检测到火焰后不立即报警连续三帧或连续三帧中至少两帧确认命中后才触发推送。这一条能极大减少由于车辆灯光、烟头闪烁等短噪声带来的误报。另外火焰分割模型的输出结果不要直接用来计算面积除非你对摄像头内参和视角做了标定。在没有标定的情况下mask像素面积只能作为相对参考值用来比较同一摄像头同一视角下不同时间段的火势变化。跨摄像头的绝对面积对比需要透视校正否则就是误导数据。个人在实际项目里的一个体会火焰分割的核心难点从来不在模型结构上而在数据和处理逻辑上。YOLOv8-SEG足够强它需要的是干净、规范、覆盖各种场景的数据配合一套贴合火焰特性的调参和报警策略。按照这篇的流程从头到尾走一遍训练一个可用的火灾火焰分割模型难度不会太高真正花时间的是把数据做扎实以及在部署环境里慢慢磨掉误报。最后一个小技巧训练结束后把每个验证视频的预测mask按时间轴导出成json序列这样可以做回放分析排查到底是哪一帧触发了误报。这比看几张抽查图片效率高得多遇到难缠的误报场景时非常管用。