
简介面向目标检测与火灾预警场景这份火焰烟雾数据集配套XML与TXT标注适合使用YOLOv5或YOLOv7训练火焰、烟雾识别模型可支撑火灾预警系统开发。压缩包共2000个文件约461.88MB以jpg图像、xml标注和txt标签为主另含xml转txt的Python脚本便于直接送入YOLO系列框架训练。标注覆盖火焰和烟雾目标的边界框及类别基于该数据训练可达到约0.9的检测精度适用于从入门到进阶的深度学习开发者。目前已有2769人学习下载。压缩包内还包含fireandsmoke-best相关文件指示了经过调优的最佳模型权重或配置可帮助读者对比训练效果、节省调参时间并快速复现高精度检测结果用于实际报警场景的模型部署与二次开发。1. 为什么火焰烟雾检测的数据集这么难搞精度还容易恰在0.9上下做目标检测做到火焰烟雾这个场景的人应该都清楚一个尴尬现状公开数据集不算少COCO、VOC 里也偶尔能翻到 fire、smoke 的类别但真正想拿来直接训练出能落地的模型十有八九要翻车。原因很简单火焰和烟雾在图像里的形态跟普通物体完全不一样——普通物体有清晰的边缘轮廓火焰的轮廓时刻在变烟雾更不用说半透明、有光照穿透、颜色随环境变化同一个“物体”在不同背景下的特征可能天差地别。我自己的做法是从三个方向混合凑出一个规模可用的数据集一部分来自公开的火焰烟雾数据集GitHub 上有人整理过几批一部分从视频里截帧做清洗还有一部分是自己补拍的实验室酒精灯、油盘火这类可控场景。最后整理出来大概一万两千多张图标注框接近两万六类别就两类fire 和 smoke没有细分火焰种类也没有把“烟带火”的情况做多标签叠加理由后面会细说。标题里提到的“精度0.9左右”这里的精度其实要拆开看。如果你说的是 mAP0.5 到 0.9那是一个比较常见的、算是不错的水平如果是 mAP0.5:0.95 也能到 0.9那说明你的测试集相对简单或者数据分布比较收敛。我这套跑下来验证集 mAP0.5 大概在 0.92 附近mAP0.5:0.95 稳定在 0.66 左右说实话后者才是更硬核的指标尤其对动态目标来说框的贴合度直接影响这个数。所以这篇就按我实际走的流程来写从原始图像收集、标注格式处理到转成 YOLO 能直接读的格式再到训练策略和精度的排查把每个你可能踩的坑都摊开。适合正在做消防预警、安防监控、或者其他视觉检测方向但苦于数据不会整理的朋友参考哪怕你不是做火焰烟雾这套“乱数据清洗 格式转换 指标定位”的思路也完全能搬过去用。2. 数据收集与清洗公开数据别直接用混合数据源才能撑住真实场景2.1 公开数据集为主但必须做一次“肉眼审查”很多文章一上来就让你去下载某某数据集然后直接开始标注格式转换听起来顺理成章实际做的时候你会发现一堆问题。以我用的几批公开数据为例有两类典型毛病一是图像分辨率参差不齐最小的缩略图才240×160最大的来自监控截图能到 4K如果混在一起不做统一处理训练时的图像缩放会很痛苦二是大量图像里火焰只占几个像素或者整张图就是烟雾弥漫标注框和实际目标严重不匹配。所以我拿到原始数据的顺序是先解压写个 Python 脚本把所有图片统一缩放到最长边不超过 1280保证后面训练加载时不会一帧图撑爆显存然后人工快速过一遍把模糊到人眼都看不清的、标注框明显错位的、以及重复帧大概去掉了两成。这个步骤别偷懒省掉的每一张低质量图都是在给训练阶段的模型减负。2.2 视频截帧补充负样本但别截太多火焰烟雾检测最容易出现的问题不是漏检而是误检。阳光反射、红色灯光、灰色雾气、甚至穿红色衣服的行人都可能被模型当成 fire 或者 smoke。想压住误检最有效的手段不是调阈值而是在训练集里加入足够的“难负样本”。我用了大约 20 段工业现场和城市监控的视频每段视频按每秒一帧抽帧抽出来之后人工挑只留那些包含“像火但又不是火”“像烟但又不是烟”的图比如夕阳下的红墙、路灯下的雾霾天、车灯过曝造成的亮斑。这批图不打标签直接当 background 类放进数据集数量控制在总图片量的 15% 左右。训练时 YOLO 会自动把它们当作背景帮模型学会“看着像但实际不是”的边界。2.3 混合数据源的标注口径统一这步是整个数据集构建里最容易被低估、但影响最大的环节。公开数据集的标注习惯各不相同有的框只框住火焰最亮的芯部有的把火焰连同周围热浪区域都框进去有的把烟和火合并成一个框有的则拆开标。如果直接混训模型一会儿学“框住最亮部分”一会儿学“框住整个火团”损失函数会震荡得厉害这也是很多人训练火焰模型怎么调都不收敛的一个重要原因。我当时花了一整天时间把所有来源的 XML 标签拉出来做了统计按图像查看每个框的坐标比例统一成同一套规则火焰框包含可见燃烧区域不包含周围大量热浪变形区烟雾框只框不透明的主体烟雾团如果烟雾太淡、透明到背景清晰可见就放弃标注这一处。宁可少标几个模糊目标也不要标出错误目标这个原则后面验证非常关键——它直接决定你能不能让指标稳定在 0.9 附近。3. xml标签到yolo格式的转换链路VOC结构和YOLO结构的核心差异3.1 为什么拿到的多是xml标签而yolo训练却需要txt早期目标检测数据集大多沿用 VOC 的标注格式也就是每个图像对应一个 XML 文件里面记录了 object 的名称、bndbox 的坐标值这是一个基于绝对像素坐标的格式而 YOLO 系列训练时读的是 txt 格式每一行代表一个目标第一个数字是类别 id接着是归一化后的中心点 x、中心点 y、框宽 w、框高 h。这两个格式不互通必须写转换脚本。但真正的坑从来不是“格式字段怎么对应”而是坐标归一化的精度。XML 里 bndbox 是 int 型像素值转换时要先除以图像的原始宽和高如果图像在预处理时被改过尺寸而 XML 里的坐标还是改之前的像素值转换出来的坐标就会整体偏移模型学出来的框永远是歪的。所以做转换前务必确认 XML 描述的图像和实际拿到的图像分辨率一致最好直接写个断言检查。3.2 转换脚本的骨架和边界情况处理我用的转换脚本核心逻辑很简单先读图像尺寸然后解析 XML 拿 name 和坐标最后写入 txt。贴个核心片段你应该一眼能看懂import xml.etree.ElementTree as ET import cv2 import os def voc_label(txt_file, xml_path, img_path, classes): img cv2.imread(img_path) h, w img.shape[:2] tree ET.parse(xml_path) root tree.getroot() with open(txt_file, w) as f: for obj in root.iter(object): cls obj.find(name).text if cls not in classes: continue cls_id classes[cls] 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) # 坐标越界截断防止归一化后出现负数或大于1的值 xmin, xmax max(xmin, 0), min(xmax, w) ymin, ymax max(ymin, 0), min(ymax, h) if xmax xmin or ymax ymin: continue center_x (xmin xmax) / 2.0 / w center_y (ymin ymax) / 2.0 / h box_w (xmax - xmin) / w box_h (ymax - ymin) / h f.write(f{cls_id} {center_x:.6f} {center_y:.6f} {box_w:.6f} {box_h:.6f}\n)这中间有两个必须处理的边角情况一是 XML 里标注框偶尔会超出图像边界可能是标注工具的手误直接 clip 到图像范围内二是会有空标签文件也就是某个图像明明存在但没有任何目标这种情况保留空的 txt 文件没问题但不能直接删掉对应图片否则数据集里 picture 和 label 数量对不上训练时会报错。3.3 目录结构按 YOLO 的规矩来转换完 txt 之后目录组织也要按 YOLO 系列的习惯来。我最后还是采用了最常见的布局dataset/ ├── images/ │ ├── train/ │ ├── val/ │ └── test/ ├── labels/ │ ├── train/ │ ├── val/ │ └── test/ └── fire_smoke.yaml图片和标签只通过同名文件对应yaml 文件里用绝对路径指向 images 的两个子目录即可。这里要把训练集、验证集、测试集在文件层面就隔离好别用程序随机划分否则同一个视频相邻帧可能一张进训练集一张进验证集mAP 会虚高后面换真实视频推理时立刻现原形。4. 训练前的最后一道关卡yaml配置文件与类别不平衡处理4.1 写yaml时最容易翻车的两个字段YOLOv5 和 YOLOv8 的配置文件差别不算大核心就三个字段train: /your_abs_path/dataset/images/train val: /your_abs_path/dataset/images/val nc: 2 names: [fire, smoke]很多人会把 train 和 val 写成相对路径然后在不同的工作目录下启动训练结果报 FileNotFoundError。还有一个容易忽略的是nc必须跟names的数量严格一致你定义了两类nc写成了 3YOLO 不会直接拒绝启动但输出的类别索引会对不上推理时你会看到 fire 被标成 class 2排查起来非常痛苦。4.2 火焰多还是烟雾多直接影响训练策略我数据集的分布是这样的类别标注框数量占比fire1540059.5%smoke1050040.5%两者没有极端失衡但 smoke 的框通常更大、更模糊对小目标检测不友好fire 的框偏小很多远距离火焰框可能只有 20×20 像素属于典型的小目标。针对这种分布我没有刻意做类别重采样而是靠 YOLO 自带的 mosaic 和 mixup 增强去提升小目标表现。如果某类占比低于 20%我才会考虑对少数类做两倍重复采样否则容易过拟合。4.3 预训练权重怎么选火焰烟雾不属于 COCO 的常见类别但底层特征比如纹理、边缘、颜色变化和 COCO 数据集里的很多东西是共通的。所以我没有从零训练而是直接加载了 YOLOv8m 的 COCO 预训练权重。选 m 而不是 s 的原因很直接火焰烟雾检测对框的定位精度要求高s 的参数量往往在前面几轮就饱和了m 在速度和精度上更均衡。如果你算力太紧张s 也能跑但要做好 mAP 掉 3~5 个点的心理准备。5. 从训练到逼近0.9我的参数设置和实测效果5.1 一组可以直接复用的训练参数贴出我跑通的一组参数YOLOv8m单卡 3060Tibatch 16图像尺寸 640。这组条件下显存占用大概 10GB属于能接受的水平。yolo detect train \ --model yolov8m.pt \ --data fire_smoke.yaml \ --epochs 120 \ --batch 16 \ --imgsz 640 \ --patience 20 \ --optimizer AdamW \ --lr0 0.001 \ --weight_decay 0.0005 \ --augment几个参数的具体考量如下patience设为 20意味着连续 20 轮验证集指标不涨就提前停我实际跑到第 96 轮就触发了早停lr0用 0.001 而不是默认的 0.01是因为火焰和烟雾的边界不确定性强学习率太大容易出现震荡后面 mAP 会在 0.85 和 0.7 之间反复横跳augment保持开启mosaic 和 HSV 增强对火焰这种颜色特征明显但形状多变的目标非常友好。5.2 跑到第几轮开始能看到0.9 的迹象我记录了几组验证集指标的变化节点可以给你一个比较直观的参考epochmAP0.5mAP0.5:0.95PrecisionRecall100.410.190.580.43300.740.430.810.72600.870.560.890.85900.920.650.930.88960.920.660.930.88从 60 轮到 90 轮mAP0.5 只涨了 0.05但 mAP0.5:0.95 涨了 0.09说明中后期的优化更多是在抠框边界而不是发现新目标。这个阶段模型已经能稳定识别火焰和烟雾但有些烟雾框会大一圈导致 IOU 略微偏低反映到 mAP0.5:0.95 上就不是很好看。5.3 不同输入尺寸带来的精度差异我也试过把imgsz从 640 提升到 960验证集 mAP0.5 能从 0.92 涨到 0.93但训练时间几乎翻倍推理速度明显下降。火焰烟雾检测的应用场景常常是实时摄像头视频流帧率比那 1 个点的精度提升更值钱所以最后我还是保留在 640 推理必要的时候只对输入 ROI 区域做二次放大检测而不是全局提升分辨率。6. 精度上不去时先按这个顺序排查“精度0.9左右”这个说法听着简单但每个人跑出来的差一点可能差的是天上地下。我自己调试过程中遇到过三种最典型的掉点原因排查顺序固定省了很多无用功。6.1 先看标签质量别急着动网络结构模型不收敛第一反应不该是换 Attention、加小目标检测头而是把验证集里预测错误的图可视化出来拿原图、真实框、预测框放一块对比。我有一次发现 smoke 一类漏检特别严重查了很久才发现是标注人员把很多半透明烟雾全部放弃标注了导致正样本数量虚低。补齐这部分标注后recall 直接涨了四个点。所以任何精度问题出现时我永远先怀疑labels目录里的 txt 文件抽样检查坐标是否合理、是否有多余空格、类别 id 是否在范围内。6.2 再看训练日志里的 loss 曲线如果 loss 曲线下降正常但 mAP 不动大概率是数据划分出了问题比如训练集和验证集存在严重重叠或者验证集存在大量重复帧。如果 loss 曲线本身震荡剧烈优先降低学习率并关闭部分增强策略。Mosaic 增强对火焰这种大面积目标有时候反而有害它会把多个火焰贴图拼在一起造成不自然的场景模型学到的分布偏了实测会掉点。6.3 最后才是调 NMS 和后处理阈值模型训练完毕之后你可以做两手部署前优化一是把conf_thres和iou_thres在验证集上做个网格搜索一般在 0.25 和 0.45 附近能找到平衡点二是针对火焰烟雾场景专门调高 smoke 类别的置信度阈值因为烟雾误检的相对代价更低宁可漏一点也不能让系统频繁报警。这不是模型层面的改动但对最终用户体验影响很大值得花时间。7. 实测落地的几点体会和后续扩展空间数据集和模型跑通之后我最大的感受是火焰烟雾检测的精度瓶颈从来不在模型结构而在于数据边界到底有没有划清楚。火焰的亮度和形态受环境光照影响极大烟雾的透明度更是全无规律你在训练集里定义好的“什么是火、什么是烟”到真实场景里一定会遇到没见过的变体这时候你能依赖的只有数据的覆盖度和标注的稳定性。精度 0.92 这个数脱离场景谈没有意义同一套权重放到受控的室内场景可能能到 0.97放到大范围森林监控上可能一下跌到 0.8。如果后续你想继续扩展我建议优先考虑两个方向一是用视频序列做时序信息融合火焰烟雾在单帧里容易跟静态干扰混淆但加上前后几帧的运动特征误检率能骤降二是针对无人机视角或高点位监控做专项采集这个视角下的火焰尺寸特别小且往往被树木或建筑遮挡一部分和常规地面视角的数据分布差异很大。这套“数据收集、格式清洗、训练调参、精度排障”的流程我走了不止一遍每次换到新的检测对象都能复用。要注意的工具链并不复杂核心就是 LabelImg、转换脚本、YOLO 官方训练命令三样真正吃时间的是标注口径的统一和脏数据的清洗。耐心做完这一步后面的训练往往比预想中顺利得多。本文还有配套的精品资源点击获取