烟雾检测比火焰检测更值得先做:YOLO11火灾检测数据集与训练模型实战

发布时间:2026/10/11 15:59:57
烟雾检测比火焰检测更值得先做:YOLO11火灾检测数据集与训练模型实战 简介这份资源面向从事火灾检测与安全监控的算法工程师、安防开发者及计算机视觉学习者提供基于ultralytics YOLO11的烟雾识别完整方案用于在监控场景中及时发现火灾隐患。包内包含746张已标注图像同时提供YOLO格式txt标签与VOC格式xml标签并已划分train、val、test三个子集附有data.yaml文件可直接用于YOLOv5至YOLOv12等系列算法的训练与迁移。资源共2000个文件以753个txt标注、746个xml标签、377个md说明文档、103个py脚本为主另含少量cpp、js、yaml等工程文件压缩包约222.28MB兼顾数据、代码与文档。已有150人学习下载。读者可借此快速复现烟雾检测流程理解数据组织与训练配置并参考可视化思路完成从数据到推理的闭环实践。1. 烟雾检测为什么比火焰检测更值得先做从一次误报排查说起凌晨两点厂区值班室打电话过来说 3 号仓库的监控画面弹了火灾告警人跑过去一看是叉车倒车时扬起的粉尘被摄像头当成了烟雾。这件事让我彻底改变了对火灾检测项目的排序思路火焰检测看起来更直观但真正能在火灾隐患阶段救命的是烟雾检测。火焰出现时往往已经进入燃烧阶段留给处置的窗口可能只有几分钟而阴燃、线缆过热、垃圾堆自燃这些场景最早释放的信号是烟雾可能提前十几分钟甚至更久。ultralytics-yolo11 在火灾检测和安全监控中的定位就是用目标检测模型对监控画面里的烟雾和火焰做实时识别把「事后看录像」变成「事前弹告警」。它适合三类人做园区/仓库/机房安防的集成商想给现有摄像头加一层 AI 分析做嵌入式边缘盒子的开发者需要在有限算力上跑检测以及想拿一个完整数据集加训练好的模型快速验证方案可行性的团队。标题里提到的数据集和训练好的模型价值不在于「省事」而在于让你能先跑通推理链路再决定要不要针对自己的场景重新训练。这一章先把方向立住烟雾检测的核心难点不是模型结构而是数据分布和误报控制。粉尘、水蒸气、逆光、夜间红外画面都会让模型翻车。后面几章会从数据集组织、YOLO11 训练参数、推理部署到误报排查一步步拆开讲。2. 数据集怎么组织烟雾火焰标注的四个关键决策2.1 类别定义决定模型上限很多人拿到火灾数据集第一反应是「烟雾、火焰两类就够了」但实际落地时这个定义太粗。我一般会把类别拆成smoke、fire、smoke_like三类第三类专门放粉尘、水蒸气、雾霾这些容易误报的负样本。如果数据集里没有smoke_like模型就只能靠纹理和颜色硬分夜间红外画面下几乎必翻车。标注格式用 YOLO 标准的 txt每行class_id x_center y_center width height坐标归一化到 0-1。目录结构按 ultralytics 的约定来fire_dataset/ ├── images/ │ ├── train/ │ ├── val/ │ └── test/ ├── labels/ │ ├── train/ │ ├── val/ │ └── test/ └── data.yamldata.yaml是训练入口内容如下path: ./fire_dataset train: images/train val: images/val test: images/test nc: 3 names: 0: smoke 1: fire 2: smoke_like这里nc必须和 names 数量一致path用相对路径时要注意训练时的当前工作目录。我习惯用绝对路径避免在服务器上跑训练时找不到数据。2.2 训练集验证集划分的坑按 8:1:1 随机划分是最常见的做法但火灾场景不能纯随机。同一个视频片段抽出来的连续帧如果被分到训练集和验证集验证指标会虚高因为模型见过几乎一样的画面。正确做法是按视频源或时间段划分比如 10 个摄像头8 个的视频帧进训练1 个进验证1 个进测试。另一个坑是负样本比例。如果数据集里全是烟雾火焰模型会倾向于把任何灰色团块都判成烟雾。我一般让smoke_like类占训练集总量的 15% 到 25%太少压不住误报太多会拉低召回。这个比例没有理论最优靠验证集上的误报率调。2.3 数据增强参数怎么设ultralytics 默认的增强对火灾场景有几个不合适的地方。hsv_h默认 0.015但火焰颜色是重要特征色相扰动太大会让红色火焰变成紫色模型学不到真实分布。我一般把hsv_h降到 0.01hsv_s和hsv_v保持默认或略高因为监控画面亮度变化大。mosaic增强默认开启对烟雾检测有帮助因为烟雾形状不规则拼接能增加上下文多样性。但mixup我通常关掉它会把两张图线性叠加烟雾和火焰叠在一起后标注框语义混乱小目标烟雾容易学坏。copy_paste对烟雾这种无固定形状的目标收益不稳定建议先关等基线跑通再试。from ultralytics import YOLO model YOLO(yolo11n.pt) model.train( datafire_dataset/data.yaml, epochs100, imgsz640, batch16, hsv_h0.01, hsv_s0.7, hsv_v0.4, mosaic1.0, mixup0.0, copy_paste0.0, degrees5.0, translate0.1, scale0.5, fliplr0.5, flipud0.0, )degrees控制旋转角度监控摄像头一般水平安装旋转超过 10 度会产生不真实的视角设 5 度足够。flipud垂直翻转要关掉因为火焰和烟雾在重力方向上有明确朝向上下翻转不符合物理规律。scale设 0.5 是让目标在画面中大小变化模拟远近不同的摄像头。2.4 标注质量检查的脚本标注框画歪、漏标、类别错是训练翻车的头号原因。我写了一个快速检查脚本统计每个类别的框数量和尺寸分布import os from collections import Counter label_dir fire_dataset/labels/train class_count Counter() size_list [] for fname in os.listdir(label_dir): if not fname.endswith(.txt): continue with open(os.path.join(label_dir, fname)) as f: for line in f: parts line.strip().split() if len(parts) ! 5: print(f格式错误: {fname} - {line}) continue cid, x, y, w, h int(parts[0]), *map(float, parts[1:]) class_count[cid] 1 size_list.append(w * h) print(类别分布:, class_count) if size_list: print(f框面积均值: {sum(size_list)/len(size_list):.4f}) print(f最小框面积: {min(size_list):.4f})如果某个类别数量不到总数的 5%训练时会被其他类压制。如果最小框面积小于 0.001说明有大量极小目标YOLO11 在 640 输入下可能检测不到需要考虑提高输入分辨率或单独处理。3. YOLO11 训练参数怎么调从基线到可用的三轮迭代3.1 第一轮用预训练权重跑通基线不要一上来就改模型结构。先用yolo11n.pt或yolo11s.pt跑一个基线确认数据管道和标注没问题。n 版本参数量小训练快适合验证流程s 版本精度更高适合最终部署。命令如下yolo detect train \ datafire_dataset/data.yaml \ modelyolo11s.pt \ epochs100 \ imgsz640 \ batch16 \ device0 \ projectruns/fire \ namebaselinedevice0指定第一块 GPU多卡用device0,1。project和name决定输出目录权重、曲线、混淆矩阵都会存在runs/fire/baseline/下。第一轮重点看results.png里的mAP50-95曲线是否稳定上升如果震荡剧烈先把学习率降到 0.001 再试。3.2 第二轮针对烟雾小目标调输入和 anchor烟雾在监控画面里往往只占几十个像素640 输入下经过 32 倍下采样后只剩几个像素特征几乎消失。两个办法提高输入到 960 或 1280或者用yolo11s配合更密集的检测头。提高输入分辨率最直接但显存和推理耗时都会涨。我一般先试 960yolo detect train \ datafire_dataset/data.yaml \ modelyolo11s.pt \ epochs150 \ imgsz960 \ batch8 \ device0 \ projectruns/fire \ nameimg960batch要相应降到 8 或 4否则显存溢出。如果显存够可以用batch16加ampTrue混合精度。YOLO11 的检测头对 anchor 的依赖比早期版本弱但imgsz变化后最好重新跑一次yolo detect train让模型自适应不要直接拿 640 的权重推理 960 的图。3.3 第三轮冻结 backbone 微调检测头如果数据集只有几千张从头训练容易过拟合。常见做法是冻结 backbone 前几层只训练检测头。ultralytics 支持freeze参数yolo detect train \ datafire_dataset/data.yaml \ modelyolo11s.pt \ epochs80 \ imgsz960 \ batch8 \ freeze10 \ lr00.0005 \ device0 \ projectruns/fire \ namefreeze10freeze10表示冻结前 10 层具体层数要看模型结构YOLO11s 的 backbone 大约 10 到 12 层。lr0降到 0.0005因为只微调头部学习率太大会破坏预训练特征。这一轮重点看验证集上的误报率如果smoke_like类被大量判成smoke说明头部还没学好可以增加smoke_like样本或提高该类损失权重。3.4 关键参数对照表参数基线值烟雾小目标建议说明imgsz640960 或 1280提高小目标分辨率batch168 或 4随 imgsz 增大而降低lr00.010.001 到 0.0005微调时降低freeze010小数据集防过拟合mosaic1.01.0保持增加上下文mixup0.00.0火灾场景不建议开hsv_h0.0150.01保护火焰颜色特征fliplr0.50.5水平翻转合理flipud0.00.0垂直翻转不合理训练过程中如果val/box_loss持续上升而train/box_loss下降是过拟合信号优先加数据或开freeze不要盲目加 epoch。4. 推理部署与误报排查模型上线后真正要盯的东西4.1 推理脚本与置信度阈值训练完的best.pt可以直接用 ultralytics 推理from ultralytics import YOLO model YOLO(runs/fire/freeze10/weights/best.pt) results model.predict( sourcertsp://camera_ip:554/stream, conf0.35, iou0.5, imgsz960, streamTrue, verboseFalse, ) for r in results: for box in r.boxes: cls_id int(box.cls[0]) conf float(box.conf[0]) label model.names[cls_id] if label in (smoke, fire) and conf 0.5: print(f告警: {label} 置信度 {conf:.2f})conf0.35是推理时的低阈值用于召回更多候选业务告警再用conf 0.5过滤。iou0.5控制重叠框合并烟雾框重叠多可以适当降到 0.4。streamTrue对视频流是必须的否则会一次性加载所有帧导致内存爆掉。4.2 误报排查清单上线后误报比漏报更让人头疼因为值班员会被折腾到不信任系统。按下面顺序排查第一看误报画面的时间分布。如果集中在傍晚或夜间大概率是红外切换或逆光导致颜色分布变化需要补这类样本重新训练。第二看误报目标的类别。如果smoke_like被大量判成smoke说明负样本不够或阈值太低先把conf提到 0.6 观察。第三看检测框位置。如果框总是出现在画面边缘或固定区域可能是摄像头脏污、雨滴或镜头光晕这类问题模型解决不了要清洁镜头或加遮挡掩膜。第四看连续帧。单帧误报可以靠多帧投票压下去连续 5 帧里有 3 帧检测到烟雾才告警。这个逻辑在业务层做不要塞进模型。4.3 边缘设备部署的量化取舍如果部署在 Jetson 或瑞芯微这类边缘盒子上FP16 量化通常无损INT8 量化需要校准集。校准集要从真实监控画面里抽不能用训练集代替否则量化后的分布偏移会让误报率上升。我一般先用 FP16 跑一周统计推理耗时和误报率再决定要不要上 INT8。yolo export modelruns/fire/freeze10/weights/best.pt formatengine halfTrue device0formatengine导出 TensorRThalfTrue开启 FP16。导出后要用同一批测试图对比 PyTorch 和 TensorRT 的输出确认 mAP 下降不超过 1 个百分点。5. 避坑与常见问题五条血泪经验现象训练 loss 正常下降但验证集 mAP 始终在 0.2 以下。原因标注类别和data.yaml里的 names 顺序不一致模型学的是错位标签。 解决用脚本统计 labels 里出现的 class_id和data.yaml的 names 逐一对齐重点检查是否有 class_id 超出nc范围。现象模型在测试集上表现很好上线后误报率极高。原因测试集和训练集同源没有覆盖真实摄像头的夜间、逆光、雨雾场景。 解决从上线摄像头抽 200 张误报帧人工标注后加入训练集重新微调检测头。现象推理时显存溢出报 CUDA out of memory。原因imgsz提到 1280 后batch没降或者streamTrue没开导致视频帧全部加载。 解决先降batch到 1 确认能跑再逐步加视频流必须开streamTrue图片批量推理用batch控制。现象烟雾检测框抖动严重同一团烟雾在连续帧里时有时无。原因单帧置信度在阈值附近波动模型对烟雾边界不确定。 解决业务层加多帧投票连续 5 帧中 3 帧命中才告警同时把iou降到 0.4 合并重叠框。现象导出 TensorRT 后精度掉了很多。原因INT8 校准集用了训练集分布和真实场景不一致或者导出时imgsz和训练时不一致。 解决校准集从真实监控画面抽导出imgsz必须和训练一致FP16 优先于 INT8。6. 把误报压下去的一个具体技巧负样本挖掘闭环模型上线只是开始真正让火灾检测可用的是负样本挖掘闭环。我的习惯是每周从告警记录里抽 50 张误报帧人工确认后加入smoke_like类用freeze10微调 20 个 epoch再推回线上。这个循环跑四周误报率通常能降一个数量级。具体操作上先写一个从告警日志提取误报帧的脚本import cv2 import json with open(alerts.json) as f: alerts json.load(f) for i, alert in enumerate(alerts): if alert[confirmed] false_positive: cap cv2.VideoCapture(alert[video_path]) cap.set(cv2.CAP_PROP_POS_FRAMES, alert[frame_id]) ret, frame cap.read() if ret: cv2.imwrite(fhard_neg/{i:04d}.jpg, frame) cap.release()alerts.json里记录每次告警的视频路径、帧号和人工确认结果。抽出来的图放进hard_neg/标注时全部标成smoke_like然后合并进训练集。微调命令和第三轮一样但epochs降到 20lr0用 0.0003避免破坏已经学好的烟雾特征。验证闭环是否有效看两个指标一是每周误报总数是否下降二是smoke_like类的召回是否上升。如果误报降了但烟雾漏报也涨了说明负样本加太多把smoke_like比例回调到 15% 左右。这个平衡点每个场景不一样只能靠数据说话。我自己的习惯是每次微调后保留旧权重新权重先影子模式跑三天对比两版模型的告警差异确认没有引入新的系统性误报再切换。这套流程不复杂但坚持下来比换任何模型结构都管用。希望帮到你。本文还有配套的精品资源点击获取

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询