火焰烟雾检测实战:从xml数据集构建到YOLO训练全流程 📅 发布时间:2026/9/20 22:38:05 👁 浏览次数: 简介面向目标检测与火灾预警应用这份火焰烟雾数据集可直接用于YOLOv5/YOLOv7等深度学习模型的训练与验证解决真实场景中火焰和烟雾的定位识别问题。压缩包共2000个文件包含XML标注、TXT标签、JPG图像及一个Python转换脚本整体大小约461.88MBXML与TXT标签对应描述目标位置JPG为原始图像py脚本可辅助完成标注格式转换便于接入主流训练流程。目前已有2769人学习使用模型训练精度可达0.9左右。借助该数据集可快速搭建火焰烟雾检测原型并进一步扩展到报警系统、安防监控等场景适合具备一定深度学习基础的研究者与开发者。 做机器视觉的朋友应该都有这种感觉看似简单的“火焰烟雾检测”任务真正动手时最花时间的往往不是模型结构而是数据集本身。我这次做的火焰烟雾检测项目用的就是xml标签的VOC格式数据集然后转成YOLO格式训练最终mAP稳定在0.9左右。这篇文章把整个数据集的构建、标注格式转换、训练调参和踩坑过程完整记录下来适合正在做安全监控、消防预警相关项目或者刚入门目标检测、想搞懂xml标签到底怎么处理的朋友参考。首先要明确一点精度0.9并不是某个模型魔改出来的奇迹而是“数据质量标签格式正确训练细节到位”的综合结果。我见过太多人把数据随便扔给YOLO结果精度卡在0.6左右然后开始怀疑模型能力。实际上80%的问题都出在数据集上尤其是xml标签和YOLO使用的txt标签之间的转换错误。所以这篇只聊实操从零开始把一个火焰烟雾数据集做成yolo可用的格式并把那些容易忽略的细节全部摊开讲。1. 数据集设计方案与核心思路1.1 为什么选择xml标签作为中间格式火焰烟雾检测本质是目标检测问题输入图片输出带类别和边界框的结果。在数据标注阶段行业里最通用的中间格式就是Pascal VOC格式也就是每张图对应一个xml文件里面记录图片尺寸、目标类别、标注框坐标等信息。用xml而不直接用yolo的txt原因有几条。第一标注工具支持好。像LabelImg这类老牌标注工具默认保存就是VOC格式的xml。你标完一张图自动生成同名xml不需要额外写转换脚本。第二可读性极强。xml是纯文本里面每个节点都能用人话读出来比如objectnamefire/name一眼就能看出这个目标属于fire类方便排查标注错误。第三后续可以转成其他格式。如果你今天用YOLO明天想试试Faster R-CNN或者MMDetectionVOC格式几乎都能直接吃进去但yolo的txt格式很多框架不认到时候还得重新转回xml绕一圈不如一开始就用xml。另外很多人问xml文件怎么打开和编辑。其实非常简单用任何文本编辑器都能打开但推荐使用VSCode或Notepad它们自带XML语法高亮结构一眼就能看清。如果只是查看浏览器直接拖进去也能以树形结构展示。真正要批量改标签内容比如把“smoke”漏了一次写成了“smok”用脚本处理比手工快得多这部分后面会有代码示例。1.2 数据来源与类别定义火焰烟雾数据集核心类别就两类fire火焰和smoke烟雾。听起来简单但实际标注时容易纠结究竟什么样算火焰什么样算烟雾比如烧水产生的蒸汽、灯光下的白色反光这些要不要标我的建议是只标“明确的火焰”和“明确的烟雾”。模糊的目标宁可漏标也不要乱标否则会大量引入背景噪声让模型学的特征一团糟。具体定义可以设为火焰必须有明显的光源发光区域烟雾必须呈蓬松的白色或灰色团状且在动态中会飘散。静态图片里如果有近景蜡烛火焰很小也建议标但框要准确。数据来源方面我混合了三种途径从开源火焰检测数据集比如开源社区的Fire Detection项目里清洗了一部分自己拍了一部分室内外火焰烟雾视频抽帧以及从网上下载了一些真实火灾现场图片。这里有一个重要原则来源越多场景越丰富模型泛化能力越强。最终我收集了约4000张图片其中火焰类目标约5000个烟雾类目标约4000个。这个数量级训练YOLOv5s已经足够了如果从零开始训练需要更多但用COCO预训练权重做迁移学习4000张完全能到0.9的精度。1.3 标注规范与常见误区标注规范直接影响后续训练效果我踩过的坑主要有三个。第一个坑是标注框过大。很多人喜欢把整个火堆圈起来但火焰边缘其实有很多无意义的背景这会引入过多的非目标像素让模型学到的边界框不紧致。正确的做法是框要紧贴火焰发光区域哪怕火焰形状不规则也用平行矩形框尽量贴合主体宁小勿大。第二个坑是漏标小目标。烟雾往往是远处淡薄的白色区域人眼看着明显但很容易被忽略。小目标是目标检测的难点如果漏标了训练时模型就会被“惩罚”去识别正确区域导致小目标召回率极低。所以我标注完成后做了一次全量检查专门补了200多个漏标的小烟雾框最终测试时小目标AP提升了约5个点。第三个坑是类别不均衡。火焰目标往往很大且数量多烟雾目标小且数量少导致模型偏向学习火焰。解决办法是数据增强时针对烟雾做更多拷贝粘贴或者把含有烟雾的图片复制几份并做水平翻转、缩放等操作人为增加烟雾样本占比。2. 核心细节xml标签解析与格式转换2.1 xml文件的标准结构分析打开一个标准的VOC标注xml文件内容大致如下annotation folderimages/folder filenamefire_001.jpg/filename path/data/fire_001.jpg/path source databaseUnknown/database /source size width1920/width height1080/height depth3/depth /size segmented0/segmented object namefire/name poseUnspecified/pose truncated0/truncated difficult0/difficult bndbox xmin100/xmin ymin200/ymin xmax340/xmax ymax480/ymax /bndbox /object object namesmoke/name poseUnspecified/pose truncated0/truncated difficult0/difficult bndbox xmin500/xmin ymin300/ymin xmax760/xmax ymax620/ymax /bndbox /object /annotation关键信息就在size和每个object下的bndbox里。size里的宽高是原图的宽和高单位是像素后面转YOLO格式时需要用它做归一化。bndbox里的四个坐标分别表示左上角x、左上角y、右下角x、右下角y注意是整数而且是像素坐标。特别留意filename字段。有时候path是绝对路径但框架读取时基本只用filename。如果filename和实际文件名不一致后面生成图片列表时会匹配不上所以批量处理时建议直接把filename重写为实际文件名或者用程序遍历图片目录动态生成xml内容避免手工改错。2.2 使用python批量解析xml并转换yolo格式YOLO训练用的标签是txt文件每一行格式为class_id x_center y_center width height其中x_center、y_center、width、height都是相对于图片宽高的比例值范围在0~1之间。转换公式很简单x_center ((xmin xmax) / 2) / width y_center ((ymin ymax) / 2) / height box_width (xmax - xmin) / width box_height (ymax - ymin) / height我用python写了一个通用脚本把数据集文件夹下所有xml一次性转换成txt。import xml.etree.ElementTree as ET import os def convert_voc_to_yolo(xml_file, out_dir, class_map): tree ET.parse(xml_file) root tree.getroot() # 读取图片尺寸 size root.find(size) img_w float(size.find(width).text) img_h float(size.find(height).text) # 输出txt行 lines [] for obj in root.iter(object): name obj.find(name).text.strip() if name not in class_map: print(f警告: 类别 {name} 未在class_map中跳过) continue class_id class_map[name] bndbox obj.find(bndbox) xmin float(bndbox.find(xmin).text) ymin float(bndbox.find(ymin).text) xmax float(bndbox.find(xmax).text) ymax float(bndbox.find(ymax).text) # 防止坐标越界 xmin max(0, min(xmin, img_w)) xmax max(0, min(xmax, img_w)) ymin max(0, min(ymin, img_h)) ymax max(0, min(ymax, img_h)) # 确保坐标有效 if xmax xmin or ymax ymin: continue x_center (xmin xmax) / 2 / img_w y_center (ymin ymax) / 2 / img_h box_w (xmax - xmin) / img_w box_h (ymax - ymin) / img_h # 限制在0~1范围内防止训练报错 x_center max(0, min(x_center, 1)) y_center max(0, min(y_center, 1)) box_w max(0, min(box_w, 1)) box_h max(0, min(box_h, 1)) lines.append(f{class_id} {x_center:.6f} {y_center:.6f} {box_w:.6f} {box_h:.6f}) # 输出 base os.path.splitext(os.path.basename(xml_file))[0] out_path os.path.join(out_dir, base .txt) with open(out_path, w) as f: f.write(\n.join(lines)) # 使用示例 class_map {fire: 0, smoke: 1} xml_dir annotations out_dir labels os.makedirs(out_dir, exist_okTrue) for f in os.listdir(xml_dir): if f.endswith(.xml): convert_voc_to_yolo(os.path.join(xml_dir, f), out_dir, class_map)这段代码里有两个细节很容易被忽略坐标越界处理和无效框过滤。标注时手滑导致xmin大于xmax、或坐标超出图片尺寸这种情况在手工标注中挺常见。如果不处理训练时YOLO会直接报错“AssertionError: label ... out of bound”让你排查半天。所以脚本里加了max/min裁剪同时跳过空框。2.3 目录组织与dataset.yaml配置转完标签后目录结构建议这样组织dataset/ ├── images/ │ ├── train/ # 3500张 │ └── val/ # 500张 ├── labels/ │ ├── train/ # 对应txt │ └── val/ # 对应txt图片和标签必须同名后缀不同。比如fire_001.jpg对应fire_001.txt。训练集和验证集按大约8:2划分而且要随机划分保证不同场景都出现在两个集合里。我做了一个简单的划分脚本用python的random.shuffle打乱文件列表然后按比例移动。需要注意的是划分后一定要检查是否每张训练图片都有对应txt有时候图片存在但xml没标这种图要么补标要么移出训练集。我写了个校验脚本把没有标签的图片全部打印出来手动处理后跑训练否则训练时会跳过这些图导致数量对不上。YOLOv5或YOLOv8的训练入口都要求一个yaml文件定义数据路径和类别。我用的dataset.yaml内容如下train: ../dataset/images/train val: ../dataset/images/val nc: 2 names: [fire, smoke]这里nc是类别数names是类别名列表顺序必须和转换脚本里的class_map一致。如果你把fire设成0smoke设成1这里也必须是0、1对应不然标签就乱了。3. 训练配置与精度优化实操3.1 环境选择与基础训练设置我用的是YOLOv5的6.0版本但用YOLOv8流程也差不多。硬件上是一块普通的NVIDIA显卡显存8GB。如果你用的是AMD显卡很多NVIDIA专属优化用不上最好老老实实用CPU训练小模型或者换到云服务器上训练。这块先说个心得不要盲目追新框架YOLOv5足够成熟部署例子多遇到问题好搜。训练命令可以这样写python train.py --data dataset.yaml --weights yolov5s.pt --img 640 --batch 16 --epochs 100 --name fire_smoke--weights yolov5s.pt表示加载COCO预训练权重做迁移学习。这对小数据集非常关键模型初始特征已经能识别一般物体边缘、颜色等不需要从零学。--img 640是输入图片尺寸原始图片如果是1920x1080训练时YOLO会做letterbox缩放但目标框坐标还是按原始尺寸映射的所以不用手动调整标签。训练过程中我重点观察三个指标box_loss、obj_loss、mAP_0.5。如果mAP_0.5一直在上升但val loss在后期反弹说明过拟合了考虑早停。3.2 提升精度到0.9的关键操作我试过很多trick真正有用且稳定的有这么几个第一使用自动数据增强。YOLOv5默认开启Mosaic增强把4张图拼成一张对提升小目标检测特别有效。但要注意如果你的小目标太多建议在训练后期关闭Mosaic避免增强过度导致目标变形。具体做法是设置--hyp修改超参文件里的mosaic概率我训练到80个epoch后手动将mosaic从1.0降到0.2精密度确实有提升。第二调整类别权重。如果烟雾样本少可以在loss里给smoke类更大的权重。YOLOv5里没有直接的类别权重参数但可以通过--hyp修改cls_pw等参数。我做的比较简单粗暴在数据增强阶段把烟雾图片多重复几次变相增加权重。第三多尺度训练。设置--multi-scale让模型在不同缩放尺寸上训练能提升对不同距离火焰烟雾的鲁棒性。实测在验证集上mAP提升约1.5个点。最终我训练的模型在验证集上mAP_0.50.91mAP_0.5:0.950.63。如果你追求0.5阈值下0.9是比较容易达到的如果追求更高的0.95指标需要更精细的标注和更多数据。3.3 部署推理时的后处理调整训练完模型推理时还有几个参数值得调整。YOLOv5的detect.py里默认conf_thres0.25iou_thres0.45。针对火焰烟雾场景我建议把confidence阈值调到0.4左右减少误检。因为现实场景中可能出现橘红色的灯光、白色窗帘等容易误判为火焰和烟雾调高阈值能有效压制。但也不要调太高否则小目标可能漏检。我的习惯是先跑到0.3看误检情况再逐步往上加减。另外如果部署到实时视频流可以把--imgsz设置成640帧率会比较理想。如果精度要求高且硬件允许可以试试--imgsz1280但推理速度会明显下降。4. 常见问题、避坑经验与实用排查手册4.1 xml编辑与批量修改的坑关于xml文件的编辑手工改一个两个是没问题的但几百上千个xml时一定要用脚本。我遇到过一个特别隐蔽的问题批注工具导出的xml里存在segmented0/segmented但某些版本可能写成1问题不大真正的坑是xml文件编码不统一。有的标注工具保存成UTF-8有的保存成ANSI如果xml里有中文注释用ET.parse直接解析可能报编码错误。解决办法是打开文件时指定编码tree ET.parse(xml_file, parserET.XMLParser(encodingutf-8))如果还是报错就需要手动把文件转成统一编码。实际操作中我直接用python脚本批量转码这样最省事。另一个坑是xml里的坐标顺序。VOC格式是xmin,ymin,xmax,ymax但有些工具或者某些网上公开数据集的xml可能写的是coco格式比如x,y,width,height。转换脚本里一定要先打印出几个样本确认一下坐标语义别一上来就批量转等到训练时才发现框全是错位的再回头排查就浪费时间了。4.2 训练时标签报错的排查清单训练时最容易遇到“Label shape error”或“AssertionError: labels require special attention”这类报错基本都和标签有关。我整理了一个快速排查手册。问题可能原因解决方法训练加载图片时提示图片损坏图片格式不统一有灰度图或损坏文件用opencv读取所有图片灰度图转为RGB删除打不开的文件报错“Image is not a valid image”扩展名是jpg但实际是png或webp统一用脚本重编码为jpg报错“assertion dataset_labels 0”labels目录和images目录对应不上某张图没有txt检查txt列表删除没有标签的图片或补标签框坐标出现nanxml坐标缺失或非数字检查原始xml用try-catch捕获转换脚本异常训练损失爆高不下降可能类别id超过nc值检查class_map和yaml里names顺序是否一致这个表格里面的每一条我都遇到过至少一次。尤其第一个“图片损坏”有些网络下载的图片只有后缀没有实际数据训练到一半报错前功尽弃。所以我写了一个图片验证脚本用cv2.imread读取如果返回None就删除或替换。4.3 精度不高的终极排除思路如果数据集和标签看起来都对精度还是达不到0.8我建议按下面顺序排查先可视化标注框。把训练集图片和对应txt画出来看看是不是有很多框和实际目标错位。你可以用python画框也可以直接用LabelImg打开原来的xml检查。检查类别难度。火焰烟雾这种半透明目标边界很模糊标注差异本身就大。让两个人标同一批图IoU可能只有0.6。所以训练时可以用--label-smoothing 0.05稍微平滑一下标签让模型不要过于自信有时能提升泛化。看看是不是数据不够多样。如果所有图片都是室内打火机火焰模型一到室外就崩。补充室外阳光下的火焰、烟雾、夜晚火光等场景图比盲目加数据量管用。试试更大的模型比如yolov5m或yolov5l。如果小模型精度卡在0.8大模型往往能上0.9代价是推理变慢。如果部署端能接受这是最省事的方法。我记得有一次卡在0.84一直上不去排查到最后发现是验证集里混入了好几张和训练集完全重复的图片导致验证集太简单显得精度虚高但实际部署时效果差。清洗掉重复图片后重新划分数据集精度才恢复正常。所以数据集的“干净”程度比数量重要得多。4.4 关于硬件的补充说明很多人问AMD显卡能不能跑YOLO。如果你用的是AMD RX 580这类显卡想跑YOLOv5训练大概率会非常折腾。因为YOLOv5的很多操作依赖NVIDIA的CUDA加速库AMD卡虽然也能通过D3D12或者OpenCL跑部分框架但兼容性差环境配置很麻烦。我的建议是如果只是推理用CPU跑小模型或者单张图片还能接受如果是训练几百张图片直接用云GPU实例便宜省心。这不是砸场子是真的为你好别把时间浪费在折磨人的环境配置上。另一个小建议是训练前把数据放到SSD上别放机械硬盘。YOLO训练时数据加载是IO密集型机械硬盘会成为瓶颈导致GPU空转训练速度大幅下降。我用NVMe SSD训练比HDD至少快20%。最后再分享一个我自己的习惯每一轮实验都会记录一张卡包括使用的随机种子、augmentation配置、batch size、最终mAP。因为有了这些记录后续复现或改进才有依据。火焰烟雾检测这个项目我前前后后跑了十几组实验最后发现最适合稳定输出0.9精度的组合就是4000张图、YOLOv5s、COCO预训练、训练100个epoch、多尺度开启、mosaic后期关闭、conf阈值0.4。所有参数都是可复现的照着做基本都能到你想要的效果。如果你正在做类似的数据集构建我强烈建议在标注阶段就写好自动化脚本把xml解析、格式转换、数据划分、图片校验这几步全部固化下来。后边每次更新数据集只需要跑一次脚本不用再手工处理。这点初期投入非常值得否则每次加几十张新图就要手动转格式、改配置会烦到崩溃。本文还有配套的精品资源点击获取