简介:面向目标检测模型训练的火焰数据集,包含1553张已标注火焰图像,并为每张图片提供TXT与XML两种格式的标注文件,可清晰记录火焰的位置、形状与大小,适用于火灾预警、智能监控等场景的模型开发。压缩包内共5260个文件,以jpg图像、txt标注、xml标注三类文件为主,打包后约194MB,目录结构清晰,便于直接接入YOLOv5等深度学习项目。目前已有4898人学习下载。资源中还给出了YOLOv5训练参考指标:在IoU阈值0.5时mAP达到0.953,在0.5至0.95阈值范围内mAP为0.679,说明数据标注具备较好的一致性与可用性,可作为火焰检测研究的基准数据集,也可用于对比不同预处理、数据增强和超参数调优策略对检测效果的影响。对于需要快速构建实时火焰识别模块的开发者,这份数据能支撑从模型训练、性能评估到部署验证的完整流程,有效节省前期图像采集与标注成本。
1. 火焰数据集:做目标检测先用它把坑踩一遍
火焰检测是个典型的「看着简单、落地一堆坑」的任务。很多刚接触 YOLOv5 的从业者拿到火焰数据集就直接开训,结果 mAP 看着不错,一到夜间、远距离小目标就全面翻车。问题往往不在模型,而在数据本身:标签格式混乱、目标尺度失衡、标注框把烟雾和火焰混在一起。这份火焰数据集(含标注好的标签)的价值在于,你不需要再从零收集图片、自己画框,能直接把精力放到格式转换、训练调参和踩坑排错上。适合要做消防预警、烟火识别、边缘设备部署的算法工程师和学生,也适合想系统过一遍 YOLOv5 全流程的入门者。
2. 先认标签:这份火焰数据集的格式与两种主流标注规范
2.1 VOC XML 和 YOLO TXT 的结构差异
拿到数据集第一步不是急着训练,而是确认标签格式。火焰检测数据集的标注格式,常见做法是 PASCAL VOC 的 XML 和 YOLO 的 TXT 两种。VOC 格式把每个目标的信息存在 XML 里,包含文件名、图像尺寸、目标类别和 bounding box 坐标,坐标是绝对值像素值。YOLO 格式则是一个类别 + 四个归一化浮点数,对应中心点 x、中心点 y、宽、高,范围都在 0 到 1 之间。两者各有使用场景:VOC 更适合用 LabelImg 这类标注工具继续编辑,YOLO TXT 则能直接喂给 YOLOv5 训练脚本。
这份数据集的标签若是 VOC 格式,你需要留意 XML 里的<folder>、<filename>和<path>字段是否和实际图片路径一致。很多标注工具写<path>时会带上标注者的本机绝对路径,比如C:\Users\xxx\Desktop\fire\xxx.jpg,这种字段在跨机器迁移时非常容易导致路径解析失败。我更倾向于在脚本里只读取<filename>,然后按自定义规则重新拼接图片路径,而不是信任 XML 里的绝对路径。
VOC XML 和 YOLO TXT 的另一层差异体现在边框坐标的精度上。VOC 的xmin、ymin、xmax、ymax是整数,代表像素位置,YOLO 的归一化坐标是浮点数,转换过程中会引入四舍五入误差,但这个误差在 640x640 输入下通常不到一个像素,对火焰这类语义边界本来就模糊的目标影响不大。真正影响训练效果的还是标注框是否贴合火焰轮廓,而不是小数位精度。
2.2 标签内容不透明时,先跑一遍格式探测脚本
数据集简介里写了「含标注好的标签」,但没细说格式和类别名时,不要凭文件名后缀猜测,直接写个脚本把标签文件夹扫一遍。我每次拿到新数据集,都会先统计一下标签文件大小分布、格式类型和类别名集合。这个动作能提前发现很多隐蔽问题,比如某个标签文件是空的、某张图的 XML 被截断、类别名混入了全角和半角空格。
import os from pathlib import Path import xml.etree.ElementTree as ET label_dir = Path("labels/xml") classes = set() empty_files = [] parse_errors = [] for xml_file in label_dir.glob("*.xml"): try: tree = ET.parse(xml_file) root = tree.getroot() objects = root.findall("object") if not objects: empty_files.append(xml_file.name) for obj in objects: name = obj.find("name").text.strip() classes.add(name) except ET.ParseError: parse_errors.append(xml_file.name) print("类别集合:", classes) print("空标签文件:", empty_files[:10]) print("解析失败:", parse_errors[:10]) print("总文件数:", len(list(label_dir.glob("*.xml"))))这段脚本的核心逻辑是遍历指定目录下的所有 XML,用 ElementTree 解析出<object>节点里的<name>字段,同时捕获两类异常:一类是文件存在但没有目标框,另一类是 XML 结构损坏导致解析直接抛异常。跑完之后你就能看到这份火焰数据集到底标注了哪些类别、有没有空标签文件、有没有损坏文件。参数上需要注意glob("*.xml")只匹配当前目录,不递归子文件夹,如果标签是按 train/val 分目录存放的,你需要分别执行或改成rglob("*.xml")。
这段脚本还有一层价值:它能暴露类别名的真实写法。很多数据集的类别名看起来是fire,实际可能混着Fire、fire(带尾随空格)、flame这样的变体。YOLOv5 训练时类别名写错一个字母,该类的标签就会全部失效,并导致训练日志里出现诡异的no labels found警告。
2.3 转换到 YOLO 格式:脚本与归一化计算
如果这份数据集的标签是 VOC XML,训练 YOLOv5 之前必须转换成 YOLO TXT。转换的核心就是把 XML 里的xmin、ymin、xmax、ymax绝对值坐标,换算成归一化的中心点坐标和宽高。公式不复杂:中心点 x 等于左右边界均值除以图片宽度,中心点 y 等于上下边界均值除以图片高度,宽度和高度分别除以图片宽高。但这里有一个高频踩坑点:XML 里存的坐标是基于原始图片尺寸的,如果图片在标注后被人为缩放或裁剪过,那转换时用的宽度和高度也必须是对应缩放后的尺寸,否则所有框的位置整体偏移。
import xml.etree.ElementTree as ET from pathlib import Path def voc_to_yolo(xml_path, class_names, out_dir): tree = ET.parse(xml_path) root = tree.getroot() size = root.find("size") img_w = int(size.find("width").text) img_h = int(size.find("height").text) yolo_lines = [] for obj in root.findall("object"): cls_name = obj.find("name").text.strip() if cls_name not in class_names: continue cls_id = class_names.index(cls_name) 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) x_center = (xmin + xmax) / 2.0 / img_w y_center = (ymin + ymax) / 2.0 / img_h width = (xmax - xmin) / img_w height = (ymax - ymin) / img_h yolo_lines.append(f"{cls_id} {x_center:.6f} {y_center:.6f} {width:.6f} {height:.6f}") out_path = Path(out_dir) / (Path(xml_path).stem + ".txt") out_path.write_text("\n".join(yolo_lines), encoding="utf-8") class_names = ["fire", "smoke"] voc_to_yolo("labels/xml/fire_001.xml", class_names, "labels/yolo")这段脚本的关键参数有class_names列表和out_dir输出目录。class_names的顺序就是训练时类别 ID 的映射顺序,一旦确定就不能在中途更改,否则之前转换好的标签全部作废。脚本里我用float()包裹 XML 里的坐标文本,即使标注文件里写的是空字符串或非法字符,也能提前抛出异常定位问题文件。输出时保留六位小数,这个精度足够 640x640 输入下的训练需求。
转换完成后别急着训练,抽几张图把 YOLO 标签画回原图,目测框位置是否贴合火焰边界。这一步听起来繁琐,但能同时验证两类错误:坐标归一化是否正确,以及标注本身是否可靠。我见过有数据集标注框只框住了火焰中心亮部,完全没覆盖外焰边缘,这种标签训练出来的模型,对边缘火焰的召回率会明显偏低。
3. 训练前的数据体检:类别平衡、光照分布与漏标检测
3.1 统计每张图的类别框数量与目标尺度
数据集的图片数量和标注质量不能直接画等号。火焰检测任务里,常见的标签分布问题有三类:大部分图片只有一两个目标框,少数图片有十几个框;夜间火焰框占比极低,白天占比极高;小目标框数量少但图片尺寸大。这些问题会直接导致训练出来的模型对特定场景失效,所以有必要在训练前写一段统计脚本,把每张图的框数量、目标宽度高度占比、类别分布拉成一张表。这份火焰数据集的分布情况,直接决定了后面 YOLOv5 的超参数怎么调。
from pathlib import Path import numpy as np label_dir = Path("labels/yolo") per_image_counts = [] relative_sizes = [] for txt_file in label_dir.glob("*.txt"): lines = [l.strip() for l in txt_file.read_text().splitlines() if l.strip()] per_image_counts.append(len(lines)) for line in lines: parts = line.split() w = float(parts[3]) h = float(parts[4]) relative_sizes.append(max(w, h)) per_image_counts = np.array(per_image_counts) relative_sizes = np.array(relative_sizes) print("每张图目标框数量: 均值 %.2f, 中位数 %.2f, 最大 %d" % ( per_image_counts.mean(), np.median(per_image_counts), per_image_counts.max())) print("目标尺寸占比: 均值 %.4f, 50分位 %.4f, 90分位 %.4f" % ( relative_sizes.mean(), np.percentile(relative_sizes, 50), np.percentile(relative_sizes, 90)))这段脚本统计的是 YOLO 格式标签,统计结果能帮你判断两件事。第一,每张图的框数量如果中位数明显低于均值,说明个别图片框特别多,大多数图片只有一两个目标,训练时会呈现「负样本比例高、正样本稀疏」的局面。第二,目标尺寸占比反映的是目标的尺度分布,火焰检测里尺寸占比小于 0.05 的目标基本属于小目标,如果 90 分位依然很小,那就意味着模型需要更强的浅层特征提取能力,YOLOv5 的输入尺寸可能需要从默认 640 提升到 1280,或者开启多尺度训练。
两类统计的结论直接服务于数据增广策略。框数量过少的图片,训练时靠马赛克增强和随机裁剪能把局部火焰样本数量提上去;目标尺度整体偏小的数据集,则更适合在训练时把mosaic和copy_paste这类增广策略开得保守一点,因为过度拼接会把小目标再进一步缩小,导致漏检更严重。
3.2 光照和背景干扰对火焰检测的影响
火焰数据集里最容易被忽略的变量是光照条件。白天阳光下的火焰和外焰的对比度可能很低,火焰的橙色区域在暖色调背景里几乎融成一片,而夜间火焰则是纯黑背景里的高亮目标。这两种情况的特征分布差异极大,如果训练集里夜间图片占比过低,模型在夜间场景下基本靠猜。做标签检查时,我习惯按图片的亮度直方图粗略分组,统计每组里目标框的数量和平均尺寸。
import cv2 import numpy as np from pathlib import Path img_dir = Path("images") dark_count = 0 bright_count = 0 dark_box_count = 0 bright_box_count = 0 for img_file in img_dir.glob("*.jpg"): img = cv2.imread(str(img_file)) gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) brightness = gray.mean() if brightness < 80: dark_count += 1 else: bright_count += 1 print("暗光图片占比: %.2f" % (dark_count / len(list(img_dir.glob("*.jpg")))))这里的核心参数是亮度阈值 80,这是我处理火焰数据集时比较常用的分界值。低于 80 的图片按暗光处理,高于 80 的按正常光照处理。实际使用时你可以根据数据集的色偏情况调整这个值,比如很多夜间火焰图片的背景不是纯黑而是深蓝,灰度均值可能在 40 到 70 之间。如果暗光图片占比明显偏低,建议训练前做一次简单的数据增广:对暗光图片做亮度抖动、加入高斯噪声、随机降低对比度,让模型在训练阶段就见过更接近真实场景的输入。
光照分组的意义不止于数据增广,它还能指导验证集的划分方式。如果验证集里全是白天图片,模型在夜间的真实表现就成了黑匣子。合理做法是让训练集和验证集都包含一定比例的夜间样本,并且保证夜间样本里的目标框数量占比和整体分布一致。
3.3 训练集/验证集划分:按场景分而不是按文件随机分
火焰检测数据集的划分方式直接关系到指标可信度。很多人直接train_test_split按文件名随机划分,这会带来一个隐患:同一个场景连续拍摄的多帧画面,可能同时出现在训练集和验证集里,导致验证集的 mAP 虚高。火焰目标检测的图片序列通常来自监控视频抽帧,相邻帧之间的背景几乎相同、火焰形态高度相似,模型见过了训练集里的这一帧,验证集里同场景的另一帧自然容易被正确识别。
我一般会按场景目录或拍摄时间段做划分,把相同场景的图片放进同一个集合。具体做法是给图片文件名加上场景前缀,比如warehouse_001.jpg、warehouse_002.jpg表示同一个仓库的连续帧,划分时以场景为单位而不是以单张图片为单位。如果原始文件命名没带场景信息,可以按文件名里的时间戳或目录结构来判断。这样划分出来的验证集才能真实反映模型的泛化能力。
4. 用 YOLOv5 训练火焰检测:从配置到收敛
4.1 数据集 YAML 与超参数调整
YOLOv5 训练火焰检测模型时,最先要准备的是数据集配置文件。这个 YAML 文件定义了训练集和验证集的图片路径、类别数量和类别名称列表,内容简洁但极其关键。路径写错或类别名不匹配,训练脚本启动后看起来正常,实际加载的标签可能全部为空,等你发现时已经浪费了几个小时的训练时间。
train: /data/fire_dataset/images/train val: /data/fire_dataset/images/val nc: 1 names: ["fire"]这份 YAML 的关键参数有三个:train和val指向图片目录,脚本会自动去同级的labels目录找对应的标签文件;nc是类别数量,这份火焰数据集只标注了火焰一个类别,就填 1;names的类别名列表必须和标签文件里的分类 ID 一一对应。如果数据集里还标注了 smoke,nc就要改成 2,names也要相应增加。这里最容易出错的地方是train和val的路径格式,YOLOv5 支持绝对路径和相对于项目根目录的相对路径,但当 YAML 文件被复制到别的机器上时,绝对路径经常会失效,建议统一用相对路径。
超参数方面,火焰检测不是常规的目标检测任务,默认的hyp.scratch.yaml需要适度调整。火焰目标通常具有明显的颜色特征和纹理特征,尤其是外焰边缘的锯齿状结构,训练时手动降低一点色度增强的幅度可以避免背景色被误判为火焰。我一般会把hsv_h从默认的 0.015 降到 0.01,因为火焰的橙色色相范围本身很窄,增广太激进反而让模型学到错误的颜色关联。
4.2 训练命令与关键参数含义
YOLOv5 的训练入口是项目根目录下的train.py,命令行参数多数有默认值,但对火焰数据集来说,有四个参数需要单独指定。--img决定训练输入尺寸,火焰检测里小目标占比高时建议用 640 甚至 960 而不是默认的 640;--batch受限于显存大小,同一批数据里图片尺寸越大,能放的 batch 越小;--epochs火焰数据集通常 100 轮以内可以收敛,但如果数据分布复杂可能需要更多;--data指向你写好的 YAML 配置文件。
python train.py --data fire.yaml --weights yolov5s.pt --img 640 --batch 16 --epochs 100 --project runs/train_fire --name fire_v1这行命令的作用是启动一次 YOLOv5 训练实验。--weights yolov5s.pt表示加载官方预训练权重做迁移学习,s 版本的模型规模适中,对火焰检测这种单类别任务来说,s 或 m 版本通常已经足够,不需要一上来就上 x 版本。--project和--name决定了训练输出的保存路径,跑多次实验时可以把不同参数组合保存到不同名字下,方便后续对比。--cache参数在数据集较大时建议开启,它会把图片提前加载进内存而不是每次迭代都从磁盘读,能显著加快训练速度,代价是占用内存。
参数调整的经验是批次大小和输入尺寸优先于模型复杂度。如果显存只有 8G,把输入尺寸从 640 降到 512,同时把 batch 从 16 降到 8,比直接换大模型更划算。--workers参数控制数据加载的线程数,Windows 下设置为 0 可以避免某些环境下 DataLoader 抛错,Linux 服务器上设置为 4 或 8 都不错。如果训练日志里出现 GPU 利用率长期低于 50%,多半是数据读取速度跟不上,加大--workers比调整模型更有效。
4.3 验证输出怎么看:mAP、混淆矩阵与 PR 曲线
训练结束后,验证结果保存在runs/train_fire/fire_v1目录里,重点看三个文件:results.png、confusion_matrix.png和PR_curve.png。results.png里包含训练和验证的 loss 曲线、precision、recall 和 mAP@0.5 曲线,用来判断模型是否收敛。火焰检测里如果最终的train/obj_loss和val/obj_loss差值过大,说明过拟合已经比较严重,需要在数据增广或模型正则化上做调整,而不是继续加训练轮数。
PR_curve.png展示了不同置信度阈值下精确率和召回率的权衡关系。火焰检测场景里,误检的代价通常比漏检低,因为后续还有消防联动确认环节,但漏检的代价极高。如果你的使用场景是预警系统,建议把推理时的置信度阈值下调到 0.25 甚至 0.2,把召回率提上去。如果模型在验证集上的mAP@0.5达标但mAP@0.5:0.95偏低,说明模型对火焰框边界的位置预测还不够精确,尤其是外焰和非火焰橙红色区域的边界。这时可以对标注质量做一次回头检查,很多标注框的外边界只是粗略框了一个矩形,和真实火焰轮廓存在偏差,mAP@0.5:0.95 就会有天花板。
5. 火焰数据集实战避坑:标注类错误有多隐蔽
5.1 标签文件字符串编码不一致导致解析失败
现象:训练脚本启动后,日志里大量出现WARNING: No labels found in .../labels/train/xxx.txt,但打开标签文件明明有内容。
原因:不同的数据标注工具和操作系统,生成文本文件时可能采用不同的编码格式。Windows 下用记事本另存为 UTF-8 时可能带 BOM 头,Linux 下的解析器把 BOM 当作字符处理,导致首行解析失败。还有一类情况是类别名里混入了全角冒号或中文逗号,肉眼看不出来,但 YOLO 解析逻辑会直接跳过。
解决:写一个清洗脚本,统一把标签文件转成无 BOM 的 UTF-8 格式,并移除所有控制字符和不可见字符。清洗之后重新检查类别集合,确认只有一个标准化命名。
5.2 同一张图重复出现在训练集和验证集
现象:训练结束后验证集的 mAP 高达 0.95 以上,但拿到一段新的监控视频测试,检测效果稀烂,漏检一大堆。
原因:划分训练集和验证集时直接用了随机划分,没有考虑图片之间的内容相似性。火焰数据集里很多图片来自同一段视频的连续抽帧,相邻帧的背景、火焰位置、尺度几乎一样,随机划分导致同一场景的画面同时出现在训练集和验证集,mAP 虚高但没有实际参考价值。
解决:按图片名称前缀或目录重新分组,确保同场景的图片只进入一个集合。实际操作时,我会把所有图片文件名整理成一份带场景标签的 CSV,按场景做分层抽样,而不是直接对文件名列表做随机打乱。
5.3 离群标注:烟雾被框进去、火焰中心被漏掉
现象:模型训练收敛后,在验证集某些图上出现诡异行为,同一个火焰区域有时给一个框、有时给两个框;对白烟误检率高。
原因:看标签统计时会发现部分标注框的中心点和宽高与其他框存在明显离群。火焰标注的质量问题通常有两类:一类是把烟雾和火焰混在一起标注,导致模型学到的是「橙红色 + 灰白色」区域的混合特征;另一类是只框住了火焰最亮的核心区域,完全忽略了外焰,导致框中心和实际火焰形心偏差很大。
解决:用脚本把每张图上标注框尺寸和位置的可视化叠加到原图上,逐张检查离群标签。发现大偏差的框,用标注工具修正或直接把整个标签文件剔除。火焰检测宁可少一个质量差的正样本,也不要让它干扰模型学到的特征边界。
5.4 类别名大小写不一致导致训练标签全部丢失
现象:训练刚开始时,日志提示found 0 images and 0 labels,但图片目录和标签目录下的文件都在。
原因:标签文件里有部分类别名是fire,部分是Fire,而数据集的 YAML 配置文件里写的名字是fire。YOLOv5 加载标签时按索引号和名称双重匹配,多余的类别名直接匹配失败。
解决:统一把所有标签文件里的类别名改成 YAML 里定义的同一个字符串。这一步必须在格式转换脚本里归一化处理,不要手动改单个文件,否则下一次重新生成部分标签又会出现新的不一致。
5.5 图片尺寸与标注坐标不匹配导致目标框偏移
现象:训练输出 mAP 正常,但推理阶段框总是比火焰区域大一圈,或者整体偏移到火焰旁边。
原因:标注工具在标注时按较大尺寸的原始图标注,之后图片被批量压缩到小尺寸,但标注坐标没有同步换算,导致宽高比例失真。
解决:检查标签里记录的目标宽度占比和实际图片的视觉比例是否吻合。修复方法是读取原始图片尺寸和当前图片尺寸,按比例换算所有坐标后重新生成标签,这个逻辑与 VOC 转 YOLO 的脚本合并在一起处理,原图和标签同步缩放。
6. 验证与进阶:把火焰检测结果接进工程前,先做这四步
训练完成不代表可以直接投入使用。把模型接到视频流或边缘设备之前,我强制自己先做四件事:用没参与训练的真实视频片段测试、统计不同光照条件下的召回率、对误检样本做反向排查、针对部署环境做推理速度评估。
第一步是准备一段全新的监控视频,不要用测试集图片,也不要用爬来的网络图,而是模拟真实部署场景的连续视频帧。视频中应该包含火焰从点燃到蔓延的过程,以及一些常见的干扰源,比如红色汽车尾灯、橙黄色路灯、夕阳反射在玻璃上的高光。用训练好的模型跑一遍视频,记录漏检发生的帧和误检发生的帧,按时间段把结果整理成表格,能快速发现光照偏暗或背景复杂的时段是否存在系统性失效。
第二步是统计不同光照条件下的表现。把视频按亮度分成纯白天、黄昏、夜间三个阶段,分别计算每个阶段的召回率和精确率。夜间火焰的高亮特征其实更容易被检测到,但如果训练集里夜间样本不足,模型可能反而只在白天表现好。如果夜间漏检明显,优先复习训练集里的光照分布,而不是盲目调阈值。
第三步是对误检样本做反向排查。把每个误检框单独截出来,放到热力图旁边观察,判断模型是被颜色骗了还是被纹理骗了。红色车尾灯和火焰的色相非常接近,区别在于火焰有动态纹理和边缘模糊感,但静态单帧检测很难利用这些信息。如果误检集中在红色圆形物体上,说明模型缺少火焰形态约束,可以考虑在训练集里加入一些红色干扰样本作为背景负样本,强化模型区分「橙红色圆形区域」和「橙红色不规则区域」的能力。
第四步是部署环境的推理速度测试。火焰检测通常跑在嵌入式设备或普通监控主机上,建议测量三个指标:单帧推理时间、GPU 显存占用、CPU 占用率。如果目标是边缘设备且帧率要求 25FPS 以上,而当前模型在目标设备上只能跑 10FPS,优先做两件事:一是把输入尺寸降到 416,火焰目标通常较大,降分辨率对精度影响有限;二是换用yolov5n或yolov5s版本,配合半精度推理,速度提升明显。导出模型时用export.py转成 TensorRT 或 ONNX 格式,不同格式的预处理方式有差异,尤其是输入归一化和通道顺序,实际部署前必须用同一张图对比导出前后的推理结果,防止格式不一致导致输出错乱。
从那以后我每次拿到新的火焰数据,都会先跑一遍格式探测和分布统计,再决定训练策略,不再贪图省事直接开训。很多坑都在标签层,模型本身反而是最可靠的部分。希望帮到你。
本文还有配套的精品资源,点击获取