
简介面向目标检测与火灾预警模型训练的火焰烟雾数据集适合使用YOLOv5/YOLOv7等框架的深度学习者可用于火焰烟雾检测、报警系统开发等场景。资源包约461.88MB包含6744张jpg原始图像、6744个xml标注和6744个txt标签另附xml2txt.py转换脚本方便把标注转为YOLO格式后直接训练总体文件约两万份检测精度约0.9。已有2773人学习使用。标注信息涵盖火焰和烟雾的边界框位置与类别便于训练、验证与错误复盘压缩包内按图像、标注与训练结果清晰组织包含最佳权重、配置脚本等关键内容。读者可据此快速搭建从数据准备到模型评估的完整流程也可将整理思路复用到自定义目标检测数据集的建设中是一份兼顾教学与工程落地参考的完整数据包同时适合初学者快速上手与工程师直接复用。1. 火焰烟雾数据集值不值得用0.9精度只是入场券做火焰烟雾检测的人经常会遇到一个很尴尬的场景手里这套带 xml 标签的火焰烟雾数据集训练出的 mAP 标称能到 0.9 左右但一到自己项目现场对着仓库角落、车间烟囱、园区走廊拍出来的画面漏报和误报同时冒头。问题往往不在模型而在这套数据集的分布跟你的场景差得有多远。这篇文章就围绕“火焰烟雾数据集 xml 标签 yolo 格式 0.9 精度”这条链路把数据集结构、格式转换、训练参数和验证方法逐层拆开让你拿到任何一套相似的数据都能在半天内跑通、看清它的真实水平。适合正在做消防预警、安全生产监控或者拿火焰烟雾检测做毕设和产品原型的开发者。2. 火焰烟雾数据集的xml标签结构先搞清楚目标长什么样再动手2.1 火焰和烟雾是两类差异很大的目标xml里不能用一套逻辑看很多新手拿到火焰烟雾数据集第一反应是直接把图片和标签丢给训练脚本跑完看精度。这个习惯在火焰烟雾场景里特别容易翻车因为它其实是两个检测任务的叠加火焰是强纹理、高饱和、边缘清晰的目标烟雾则是半透明、低对比、边缘模糊的目标。两者对标注质量、输入分辨率和数据增强的敏感度完全不同。火焰的标注框即使框得稍微松一点IoU 损失也不会太难看因为火焰通常靠暖色调就能被模型抓住。烟雾的难点在于烟本身没有明确边界可见度从浓到淡是渐变的标注员不同边框就不同。同一张烟囱冒烟的图片有人在烟柱最浓的区域收紧边框有人把下方淡淡的烟羽也框进去。这种标注不一致会让模型在训练时收到互相矛盾的回归信号直接表现就是训练集 mAP 不错、验证集波动大。所以在拆解 xml 标签之前先把目标建模这件事想清楚火焰类别应该尽量用紧凑框包住明火核心区域烟雾类别宁可框大一点也要做到整张图片的标注标准统一。框架信噪比不足的时候统一比精细更重要。2.2 读一遍VOC格式的xmlfolder、filename到bndbox逐个字段说明标题里说的“xml标签”在火焰烟雾检测场景里基本就是 Pascal VOC 格式。一个典型的 xml 文件长这样annotation folderflame_smoke/folder filenameimg_0001.jpg/filename pathimages/img_0001.jpg/path size width1920/width height1080/height depth3/depth /size object nameflame/name difficult0/difficult bndbox xmin126/xmin ymin84/ymin xmax268/xmax ymax215/ymax /bndbox /object object namesmoke/name difficult0/difficult bndbox xmin80/xmin ymin60/ymin xmax420/xmax ymax640/ymax /bndbox /object /annotationsize里的宽高决定了后续换算 yolo 坐标的基准缺了它转换脚本就跑不了。object节点代表一个目标实例name是类别名bndbox里存的是左上角和右下角的绝对像素坐标。difficult字段表示这个目标是不是特别难识别比如被严重遮挡、只有极小一块火焰通常转换到 yolo 格式时可以直接跳过避免干扰训练。有一点要提醒火焰烟雾数据集经常出现同类别多种写法比如flame和fire、smoke和Smoke混用。如果直接从网络上下载或别人拷贝来的 xml 标签转换前一定要先统计一遍所有name字段去重看结果不要想当然地认为只有两个类。很多精度翻车的根因就在这类低级但隐蔽的脏数据上。2.3 标注质量与场景分布的快速核查清单拿到数据集后不要急着训练花十分钟做一个轻量核查。我一般用一条命令统计标注分布grep -h name *.xml | sort | uniq -c然后人工抽查 20 张图片重点看三件事。第一小目标占比。火焰烟雾数据集中远距离小火苗和淡淡烟羽大量存在如果标注框普遍偏大模型会天然倾向忽略小目标。第二背景多样性。如果图片集中在黑色背景或单一室内场景模型的颜色响应会过拟合一旦搬到有晚霞、暖色灯光的场景就会疯狂误报。第三类别平衡。火焰往往样本多、烟雾样本少训练时烟雾类 AP 会明显低于火焰类最终把整体 mAP 拉到 0.9 附近的假象掩盖了烟雾性能不足。这套核查的价值在于让你在训练前就知道精度天花板在哪。0.9 的 mAP50 看起来漂亮但如果 smoke 类 AP 只有 0.7落地后烟雾报警延迟就会成为致命伤。早发现早补数据比训练完再回头找原因高效得多。3. xml标签转yolo格式归一化换算、转换脚本与校验3.1 两种坐标体系的差异为什么不能直接改后缀名把 xml 标签转成 yolo 格式核心是坐标换算。VOC 的 xml 里存的是绝对像素坐标yolo 的 txt 里存的是归一化后的中心点坐标加宽高而且宽高也是相对图片尺寸的。换算公式很简单x_center (xmin xmax) / 2 / widthy_center (ymin ymax) / 2 / heightbox_width (xmax - xmin) / widthbox_height (ymax - ymin) / height要注意 yolo 格式对数值范围的要求所有值必须在 0 到 1 之间。火焰烟雾的标注框有个特点烟雾常常飘出画面边界导致 xmax 或 ymax 超过图片宽高。碰到这种情况不能直接把公式套完就写进文件会得到大于 1 的值训练时有些框架会直接报错有些框架强制截断造成框位置偏移。正确做法是先夹紧再归一化或者把这类标注过滤掉。另外yolo 每行的第一个数是类别索引不是类别名字符串。xml 里写的是flame、smoke转换时需要维护一个类别名到数字的映射表比如{flame: 0, smoke: 1}。转换前先确认这个映射和训练配置里的names一致否则会出现类别对不上的悬案。3.2 xml转txt的python转换脚本与参数说明下面这个脚本是我在火焰烟雾数据集上惯用的版本处理了类别名大小写、difficult 目标、越界边界框三个主要坑点import xml.etree.ElementTree as ET import os # 类别映射顺序要和训练yaml里的names保持完全一致 CLASS_MAP { flame: 0, smoke: 1, } def xml_to_yolo(xml_path, output_path): tree ET.parse(xml_path) root tree.getroot() size root.find(size) width float(size.find(width).text) height float(size.find(height).text) if width 0 or height 0: print(f跳过 {xml_path}: 图片尺寸非法) return lines [] for obj in root.iter(object): # 规范化类别名避免 flame / Flame / flame_ 这类脏数据 name obj.find(name).text.strip().lower() if name not in CLASS_MAP: print(f跳过未映射类别: {name} {xml_path}) continue difficult obj.find(difficult) if difficult is not None and int(difficult.text) 1: continue box obj.find(bndbox) xmin float(box.find(xmin).text) ymin float(box.find(ymin).text) xmax float(box.find(xmax).text) ymax float(box.find(ymax).text) # 先夹紧到图像范围内再归一化 xmin min(max(xmin, 0), width) ymin min(max(ymin, 0), height) xmax min(max(xmax, 0), width) ymax min(max(ymax, 0), height) if xmax xmin or ymax ymin: print(f跳过退化框: {xml_path}) continue x_center (xmin xmax) / 2.0 / width y_center (ymin ymax) / 2.0 / height box_width (xmax - xmin) / width box_height (ymax - ymin) / height lines.append(f{CLASS_MAP[name]} {x_center:.6f} {y_center:.6f} {box_width:.6f} {box_height:.6f}) with open(output_path, w, encodingutf-8) as f: f.write(\n.join(lines)) def batch_convert(xml_dir, output_dir): os.makedirs(output_dir, exist_okTrue) for filename in os.listdir(xml_dir): if not filename.endswith(.xml): continue xml_path os.path.join(xml_dir, filename) out_path os.path.join(output_dir, filename.replace(.xml, .txt)) xml_to_yolo(xml_path, out_path) print(f转换完成输出目录: {output_dir}) if __name__ __main__: batch_convert(annotations/xml, annotations/labels)逻辑说明脚本先读图片宽高再遍历每个 object跳过未映射类别和 difficult 目标然后对坐标做夹紧处理最后生成 yolo 格式的一行。夹紧处理在这里非常关键烟雾框超出边界太常见不夹紧几乎每几张图就要报一次错。参数说明CLASS_MAP需要和后续训练数据配置里的 names 顺序一致batch_convert的第一个参数是 xml 所在目录第二个是 txt 输出目录。如果数据集里图片不在 xml 同目录要保证size节点存在否则脚本无法运行。3.3 转换后的校验画框回验是防止翻车的最小手段转换完成后不要直接开训练。yolo 官方训练脚本能自动读取标签但如果转换出了问题它只会报数据加载失败不会告诉你哪张图错了。最可靠的手段是写一个可视化脚本把 txt 标签画回原图上人眼抽查。核心代码import cv2 def draw_yolo_labels(image_path, label_path, class_names): img cv2.imread(image_path) h, w img.shape[:2] with open(label_path, r) as f: for line in f.readlines(): parts line.strip().split() if len(parts) ! 5: print(f格式错误: {line} {label_path}) continue cls_id, xc, yc, bw, bh parts xc, yc, bw, bh float(xc), float(yc), float(bw), float(bh) x1 int((xc - bw / 2) * w) y1 int((yc - bh / 2) * h) x2 int((xc bw / 2) * w) y2 int((yc bh / 2) * h) color (0, 0, 255) if int(cls_id) 0 else (0, 255, 255) cv2.rectangle(img, (x1, y1), (x2, y2), color, 2) cv2.putText(img, class_names[int(cls_id)], (x1, max(0, y1 - 5)), cv2.FONT_HERSHEY_SIMPLEX, 0.6, color, 2) cv2.imshow(check, img) cv2.waitKey(0)这段脚本会依次弹出图片检查三件事框是否包住了目标主体火焰框是否集中在明火区域烟雾框是否明显偏离烟羽位置。抽查 30 到 50 张没有明显错位就可以继续了。这里检查的重点不是像素级精度而是有没有出现整张图框错位、类别错乱、坐标全为 0 这类系统性问题。4. 用这套数据集训练火焰烟雾模型把mAP50做到0.9附近的参数设置4.1 模型选型火焰烟雾检测为什么常用YOLO系列火焰烟雾检测的场景里YOLO 系列是绝对主流的选择。原因有三第一推理速度快消防预警系统对实时性要求高摄像头画面需要持续分析第二生态成熟数据格式、预训练权重、部署工具链都很完整第三小目标检测能力在不断强化。标题里提到的“精度 0.9 左右”在目标检测语境里通常指 mAP50也就是 IoU 阈值 0.5 下的平均精度。mAP50-95 一般会比 mAP50 低 10 到 20 个百分点看到 0.9 这个数字时先确认它指的是哪个指标避免后续产生预期落差。模型体量的选择要看目标尺寸。如果数据集中火焰烟雾目标普遍较大用默认的 nano 或 small 体量就够了如果包含大量远距离小火苗、摄像头视野开阔的场景建议直接上 small 甚至 medium同时配合更大的输入分辨率。常见做法是先用 small 体量跑一版 baseline观察小目标 AP 再决定是否升级模型不要一上来就用最大体量调参成本太高。4.2 构造数据yaml和训练命令一套可以照抄的配置数据目录结构推荐这样组织dataset/ images/ train/ val/ labels/ train/ val/图片和标签文件名一一对应后缀分别用 .jpg 和 .txt。然后写一个数据配置文件# flame_smoke.yaml path: /your/dataset/root train: images/train val: images/val names: 0: flame 1: smoke训练命令我一般这样写yolo detect train \ dataflame_smoke.yaml \ modelyolov8s.pt \ imgsz1280 \ epochs200 \ batch16 \ cos_lrTrue \ patience30 \ close_mosaic10 \ seed42参数说明imgsz1280是整套配置里最关键的一项。火焰烟雾目标有很多是小尺寸的1080p 画面里一个小火苗可能只有二三十像素如果输入尺寸压到 640这个目标几乎就消失了。用 1280 训练会明显提升小目标召回率代价是显存占用变大、训练变慢但值得。close_mosaic10表示最后 10 个 epoch 关闭 mosaic 增强因为 mosaic 会把一张图切成四块拼接烟雾这种大尺寸目标经常被切掉一半导致模型学到断裂的目标形态。patience30控制早停火焰烟雾任务一般 100 到 150 epoch 就收敛不需要完全跑满。4.3 训练日志怎么读关注什么才算真的收敛训练过程中不要只看 loss 曲线下降就以为万事大吉。yolo 命令行输出里会包含每个类别的 P、R、mAP50、mAP50-95其中有几个信号值得盯紧。如果 flame 类 AP 高但 smoke 类 AP 明显低说明数据集本身类别不均衡或者烟雾标注质量差此时要优先补烟雾样本而不是调模型。如果训练集 mAP50 已经 0.95 但验证集只有 0.8说明过拟合了常见原因是数据量不够或增强太弱优先降低 epochs、增大 mosaic 和 scale 增强的强度。火焰烟雾任务有个特点模型很容易“学会”用颜色判断火焰因为火的颜色高度集中在暖色区域。这在一开始会让 mAP50 快速上升但也会让模型对暖色背景极度敏感。训练日志里如果看到 precision 和 recall 同时高、但 val 阶段误报明显就要回头检查训练集里是否缺少亮色背景的负样本。这是这个赛道最常见的“假收敛”。4.4 数据增强的取舍颜色增强会伤害火焰识别yolo 默认开启的 hsv 颜色增强在火焰烟雾任务上建议收着用。火焰的核心判别特征是红黄白的颜色分布如果把色调随机偏移模型会看到蓝色、绿色的“火焰”这会让它学到的颜色特征失真。烟雾同理它是低饱和目标过度饱和增强会让模型以为烟是浓彩色的。我一般把 hsv 增强调到hsv_h0.0 hsv_s0.2 hsv_v0.2也就是色调完全不动饱和度和明度做轻微扰动。另一个值得保留的增强是scale和fliplr。水平翻转不会改变火焰颜色和烟雾形态是安全的增强方式随机缩放能模拟摄像头远近变化对提升尺度鲁棒性帮助很大。mosaic和copy_paste建议保守使用。mosaic 对单类小目标训练效果好但火焰烟雾场景里的大烟雾目标容易被切碎copy_paste 会把烟雾区域凭空粘贴到另一张图上而烟雾的边缘透明度没法正确迁移可能生成语义错误的样本。常见做法是mosaic0.8copy_paste0.0。5. 火焰烟雾模型精度不升反降的避坑排查从现象到根因5.1 现象训练集 mAP 0.93真实摄像头几乎零命中辛苦训练完模型在测试集上成绩不错但部署到真实环境里远处的小火苗完全识别不到烟雾也只报大团的白烟。原因基本是训练数据里小目标占比太少模型没见过小尺寸火焰推理时直接忽略。解决办法是切图训练。把原始大图切割成 640 或 1280 的若干小块同时把标签坐标从原图坐标换算成切图坐标。切图后小目标的相对尺寸会被放大模型更容易学到小目标特征。推理时也采用切图预测再合并结果代价是耗时增加但对监控类场景完全可接受。5.2 现象换一个场景就掉点精度曲线像过山车训练集和验证集来自同一批采集环境模型背住了那个环境的光线、背景纹理换到新的厂房、走廊、户外场景性能直接下滑。本质是场景过拟合火焰烟雾数据集普遍面临这个问题因为采集成本高很多数据来自同一来源、同一机位。解决方向是让训练数据在场景维度上多样化。不用畏难至少做到两类补强一类是不同时间段的光线变化黄昏、夜间、逆光另一类是不同背景包括灯光、蒸汽、云朵、晚霞这类容易混淆的干扰场景。没有条件补数据时可以用简单的背景替换增强把目标粘贴到不同背景图上做样本扩充。5.3 现象误报频发把暖色灯光、夕阳、蒸汽全标成火焰这是火焰烟雾项目里最毁信任的故障。根因不是模型学得不好而是训练数据里缺少负样本尤其缺少“像火但不是火”的样本。模型只能靠颜色和形状做判断它不知道灯光和火焰的区别因为没见过灯光。解决在数据集中加入大量负样本图片也就是完全没有任何火焰烟雾的正常场景图标签文件为空。空标签的 txt 文件在 yolo 里完全合法这些图会让模型学会抑制背景响应。常见做法是负样本占比做到总数的 20% 到 30%并且覆盖工厂照明、夜间路灯光晕、户外晚霞等场景。5.4 现象验证集 mAP 低于预期检查发现烟雾框乱标抽查 xml 后发现烟雾的标注框有的只框住浓烟核心有的框住整条烟羽还有的干脆把烟囱本身也算进去了。标注标准不统一模型在回归任务里收到矛盾的信号精度天花板被压得很低。解决方法是重新定义烟雾标注标准。常见做法烟雾边框以“肉眼可辨识的烟羽区域”为准透明扩散区域不强行纳入边框外扩不超过目标视觉范围的 10%标注完成后由同一个人复查修改。标准定下来精度一般提升 3 到 5 个点不是问题。5.5 现象火焰类精度高烟雾类 AP 一直上不去类别分布不均火焰框多、烟雾框少。烟雾本来就是淡目标样本少的情况下更难学。同时烟雾框普遍比火焰框大大框的 IoU 计算对中心点偏移更敏感回归更困难。解决方向有三个一是在数据集中增加烟雾样本哪怕是同一段视频多抽几帧也有用二是把烟雾类别在损失函数里的权重提高常见做法是给 smoke 类分配更高的 cls 权重三是用 mosaic 或者翻转增强让烟雾目标的尺度变化更丰富。先把 smoke 类 AP 拉到 0.8 以上再谈整体 0.9。6. 验证0.9精度是不是虚高独立验证集与部署前的最后一道工序拿一套数据集训练完模型 mAP50 显示 0.9这个数字本身不能直接写在项目验收报告里。它在训练时用的验证集上测出来而这个验证集很可能和训练集来自同一次采集背景高度相似。常见做法是再独立抽一段视频做验证从不同摄像头、不同时段、不同天气条件下截帧标注单独建一个验证集再来测一次。有一个简单有效的验证方式把视频按时间分成前 80% 和后 20%前段用来训练后段完全不见过模型用后段做验证。这是最省力的时间隔离方案能暴露出场景过拟合问题。验证命令yolo detect val \ dataindependent_val.yaml \ modelruns/detect/train/weights/best.pt \ imgsz1280 \ conf0.25跑完后重点看两个输出per-class 的 AP 表以及 P/R 曲线。如果 smoke 类 AP 明显低于 flame 类或者验证集 mAP 比训练时低 5 个点以上说明数据分布还没有覆盖到真实场景不要急于上线。部署前还有一个值得做的核验在真实场景视频上跑一次推理把模型输出框保存下来人工数一遍漏报和误报的数量。统计中要注意一个习惯性问题0.9 的 mAP50 只表示框与真实目标的 IoU 超过 0.5 就算命中但消防预警场景里烟雾刚出现的几秒内目标很小IoU 很难过 0.5模型即使检测到了烟雾IOU 也可能低于阈值。这时可以调低验证时的 conf 阈值观察召回情况找到漏报与误报的平衡点。我还有一次踩坑经历拿一个 mAP50 0.91 的模型做现场演示摄像头对着窗外黄昏的云层被模型框出了三个“flame”场面一度十分尴尬。回去把训练数据里加了夕阳、云层、暖色建筑墙面的负样本误报问题彻底消失。烟火检测这种任务数据的覆盖面永远比模型结构更决定上限。记住这个习惯任何宣称 0.9 精度的火焰烟雾数据集都必须先做独立验证再谈上线。单独看一个数字没有意义你需要的是知道这个模型在真实的烟雾初级阶段、远距离发现和复杂背景干扰场景下各自能做到什么程度。希望帮到你。本文还有配套的精品资源点击获取