简介面向目标检测学习者和火灾安全相关项目开发者收录了1000张真实场景火灾与火焰图像全部人工精标覆盖多种火势、烟雾、光照和背景干扰情况。资源已按标准结构整理images文件夹存放原始图片另设YOLO格式、VOC XML格式、COCO JSON格式三个独立标签目录并附有classes.txt、data.yaml配置文件可直接接入主流框架训练。压缩包共112个文件以104个xml标注文件为主另含5个Python脚本、1个txt配置说明等整体约97KB。配套提供训练集/验证集/测试集自动划分脚本支持自定义比例输出符合YOLOv5/v8/v10规范的ImageSets结构文档教程覆盖Windows/Linux环境部署、依赖安装、数据路径配置、模型训练与推理全流程。目前已有63人学习适合需要高质量标注数据并快速上手YOLO训练流程的初学者和开发者。1. 1000 张真实火灾火焰图像数据集为什么三格式标签能直接开训做消防检测、火焰识别项目的人八成时间都耗在数据上而不是模型上。市面上的火灾数据集要么是合成渲染图标注只有单一格式要么是几十张图拼凑的样例训出来的模型换个场景就崩。这份资源提供 1000 张真实火灾火焰图像覆盖地面火灾、建筑起火、夜间火焰、烟与火混合等多类现场每张图同时配好了 VOC、COCO、YOLO 三种格式的标签文件另附自动划分数据集脚本和一份 YOLO 训练实操指南。对准备用 YOLO 做火焰识别的新手以及需要在安防、消防预案项目里快速验证检测效果的工程师来说数据拿到手就能直接进训练流程不用再花一两天写格式转换脚本。2. 三格式标签背后的数据组织逻辑VOC 的 XML、COCO 的 JSON 与 YOLO 的 TXT拿到三格式标签后直接开始扒代码的人多半会在同一个地方翻车明明转换函数写得没问题mAP 却始终是零回头一查是坐标换算出错。先花十分钟把三种格式的存储结构和坐标定义看明白后面所有转换、训练、排错都会顺很多。2.1 VOC 格式的目录结构与 XML 解析要点Pascal VOC 是最经典的检测标注格式它把每张图的标注单独存成一个 XML 文件存放在 Annotations 目录下原图放在 JPEGImages 目录下。XML 的根节点是 annotation里面记录了文件夹名、文件名、图像尺寸然后是一个或多个 object 节点每个 object 里包含类别名称 name 和 bndbox 边界框边界框用 xmin、ymin、xmax、ymax 四个绝对像素坐标表示。比如检测到一处火焰bbox 存的就是左上角和右下角这样的整型坐标。读取这类 XML 用 Python 标准库的 xml.etree.ElementTree 就够不需要装额外依赖import xml.etree.ElementTree as ET tree ET.parse(Annotations/fire_0012.xml) root tree.getroot() width int(root.find(size/width).text) height int(root.find(size/height).text) for obj in root.findall(object): name obj.find(name).text box obj.find(bndbox) xmin int(float(box.find(xmin).text)) ymin int(float(box.find(ymin).text)) xmax int(float(box.find(xmax).text)) ymax int(float(box.find(ymax).text)) print(name, (xmin, ymin, xmax, ymax))这段代码先把图像宽高取出来备用然后遍历每个 object 节点把四角坐标转成 int。我特意在 int() 外面套了一层 float()是因为不少标注工具导出的 XML 会把坐标写成 345.0 这样的浮点字符串直接 int(345.0) 会抛 ValueError。这是 VOC 转其他格式时最常翻车的点之一。VOC 格式的第二个坑是坐标越界。图像增强或者人工标注时偶尔会出现 xmax 大于 width 的情况YOLO 训练时这类样本会导致 loss 异常增大。常见做法是在解析时把坐标 clamp 到 [0, width] 和 [0, height] 区间内或者干脆在预处理阶段过滤掉这类标注别让它进训练集。2.2 COCO 与 YOLO 的坐标换算从像素到归一化的三步计算COCO 格式把所有标注塞进一个 JSON 文件里核心是三个数组images 记录每张图的 id、文件名、宽高annotations 记录每个目标的 image_id、category_id 和 bboxcategories 负责给类别编号。要注意 COCO 的 bbox 是 [x, y, width, height] 的绝对像素表示x、y 是框左上角坐标width、height 是框的像素宽高这和 VOC 的左上右下表示法不同。YOLO 训练格式则是每张图对应一个 TXT 文件每一行写一个目标类别编号、归一化的中心点 x、归一化的中心点 y、归一化的宽、归一化的高。把 COCO 的 bbox 转成 YOLO 行格式核心逻辑就是下面这段def coco_bbox_to_yolo(size, bbox): img_w, img_h size[0], size[1] x, y, w, h bbox x_center x w / 2.0 y_center y h / 2.0 return (x_center / img_w, y_center / img_h, w / img_w, h / img_h)四个输出值全部落在 0 到 1 之间。x_center 先用左上角坐标加上一半宽度得到中心点再除以图像宽度y 方向同理。之所以要归一化是因为 YOLO 在训练时会做 mosaic、随机缩放等增强绝对像素坐标会随输入尺寸变化归一化后标签就跟分辨率解耦了。转换时最容易忽略的是类别编号对齐。COCO 的 category id 习惯从 1 开始计数而 YOLO 的 class id 从 0 开始。如果直接从 JSON 里取 category_id 写进 TXT会出现类别整体往后错一位本来该是 0 的 fire 写成了 1训练时模型的预测和标签就对不上。我一般的做法是在转换脚本里维护一个 name_to_id 的映射表例如 {fire: 0}然后从 categories 数组里反查类别名再用映射表输出 YOLO 编号杜绝这类错位。2.3 三格式并存的价值兼容工具链并避免二次转换损失同时提供三套标签本质上是为了覆盖不同的工具链。VOC 格式能直接喂给老牌的 detectron2 数据加载器也能被 LabelImg 打开复核COCO JSON 适合做模型评估和数据集合并很多预训练权重的统计信息都基于 COCO 数据YOLO TXT 则是最简洁的训练输入Ultralytics YOLOv8、YOLOv5 都原生支持。多一套格式就少一次被工具链卡住的机会。三格式并存还有一个容易被低估的价值它们互为校验。比如你把 VOC XML 转成 YOLO TXT 之后可以再对同一张图的同一个目标对比两个坐标换算回来的像素框是否一致。如果左右两边算出的框差了几个像素说明某一侧的数据本身就有问题趁早发现而不是等训练完 mAP 异常再去排查。3. 自动划分脚本拆解train/val 划分、随机种子与文件校验数据集准备好之后最忌讳的事是把所有图一股脑丢进训练。训练集和验证集如果混在一起模型在验证集上的指标会虚高部署到真实场景立刻露馅。这份资源附带的自动划分脚本解决的就是 train/val 划分这个环节下面把它的设计思路、核心代码和校验方法拆开讲。3.1 划分脚本的设计思路先定比例再锁随机种子划分逻辑本身很简单把所有图像文件读进来按比例随机分成训练集和验证集再把对应的标签文件按同样的名单拷过去。问题出在“随机”这两个字上——脚本每次运行结果不同昨天跑的划分和今天跑的划分文件分布不一致后面复现训练结果就成了拼运气。所以脚本里必须先固定随机种子再执行 shuffle让每次运行生成的划分结果完全一致。train/val 比例一般取 8:2 或者 9:1。火焰检测这种类别数量不大、但场景差异明显的项目我习惯用 8:2留出 20% 做验证如果后续还要做超参调优再从训练集里切一小块做测试集。要注意的是同一段连续视频抽出来的帧不能一部分进 train、一部分进 val否则验证集里会出现和训练集高度相似的画面评估结果会虚高。3.2 核心代码与参数说明脚本的核心逻辑用 Python 写非常直观import random import shutil from pathlib import Path random.seed(42) # 固定随机种子保证每次划分结果一致 src Path(datasets/fire_images) train_ratio 0.8 images sorted(src.rglob(*.jpg)) random.shuffle(images) split_idx int(len(images) * train_ratio) train_images images[:split_idx] val_images images[split_idx:] out_train_img Path(yolo_dataset/images/train) out_train_lbl Path(yolo_dataset/labels/train) out_val_img Path(yolo_dataset/images/val) out_val_lbl Path(yolo_dataset/labels/val) for target in [out_train_img, out_train_lbl, out_val_img, out_val_lbl]: target.mkdir(parentsTrue, exist_okTrue) for img in train_images: shutil.copy(img, out_train_img / img.name) lbl img.with_suffix(.txt) if lbl.exists(): shutil.copy(lbl, out_train_lbl / lbl.name) for img in val_images: shutil.copy(img, out_val_img / img.name) lbl img.with_suffix(.txt) if lbl.exists(): shutil.copy(lbl, out_val_lbl / lbl.name)sorted() 先把路径排序再 shuffle是为了让随机种子作用在有序输入上避免不同文件系统下 glob 返回顺序不一致带来的划分差异。split_idx 用 int 截断图片总数是奇数时训练集少一张对整体影响不大。拷贝标签时用相同文件名前缀把 .jpg 换成 .txt可以自动跳过没有标签的样本——要不要跳过取决于数据集定位如果某些图本身是负样本训练时该留如果是漏标就该查原数据了。划分完成后先别急着训看一眼 numbers。如果 labels 目录里文件数明显少于 images那大概率是漏标或脚本路径写错偶尔差几张可能是负样本图差太多一定要回头查。3.3 划分后的目录自查数量、类别与样本分布划分完成先用 30 秒做一次统计from collections import Counter from pathlib import Path for split in [train, val]: img_dir Path(fyolo_dataset/images/{split}) lbl_dir Path(fyolo_dataset/labels/{split}) imgs list(img_dir.glob(*.jpg)) lbls list(lbl_dir.glob(*.txt)) cls_counter Counter() for lbl in lbls: for line in lbl.read_text().strip().splitlines(): cls_counter[line.split()[0]] 1 print(split, images:, len(imgs), labels:, len(lbls)) print(class distribution:, dict(cls_counter))这段脚本输出每个 split 下的图像数、标签数和类别分布。用类别分布核实一下如果 train 里 fire 类有 800 个框val 里只有 40 个说明划分不均匀需要回到 3.1 重新检查 shuffle 逻辑。还要再看 val 集里是否出现了和 train 集同名的文件——如果出现那就是脚本没按文件名去重直接返回去修。这一步看着简单但能拦住后面训练阶段最麻烦的 debug。4. 基于这份数据集跑通 YOLO 训练从 YAML 配置到权重输出划分好数据集之后就可以进入训练环节。这一章以 Ultralytics YOLOv8 为例把数据集 YAML 的写法、训练参数的选择和日志指标的判读完整走一遍。YOLOv5 的参数体系基本一致命令稍微调整就能迁移。4.1 数据集 YAML 怎么写路径与类别名对齐是硬要求YOLO 训练第一步是准备 data.yaml它告诉框架去哪儿找图、去哪儿找标签、类别有几类、名字分别是什么。对这份单类别火焰数据YAML 可以这么写path: ../yolo_dataset train: images/train val: images/val names: 0: firepath 字段建议用相对路径基准目录是当前工作目录。上面这份 YAML 假设你把它放在 yolo_dataset 的父目录下运行时数据实际路径就是 ../yolo_dataset/images/train。用相对路径而不是绝对路径是为了换机器、换账号时不用改配置如果训练时提示找不到图像先用 pwd 确认工作目录再看 train 和 val 字段拼出来的完整路径是否存在。names 是从 0 开始的映射这里只有 fire 一个类。如果之后把数据扩展成多类别改成 names: {0: fire, 1: smoke} 就行。这里最需要注意的是类别编号必须和标签文件 TXT 里的第一个数字完全对齐TXT 里写 0YAML 里 0 对应 fire缺一不可。4.2 训练参数选择epochs、imgsz、batch 在火焰场景下的推荐值YOLOv8 训练命令如下yolo detect train datafire.yaml modelyolov8s.pt epochs100 imgsz640 batch16 patience20 device0model 参数可以填预训练权重或模型配置文件。我一般从 yolov8s.pt 起步它比 nano 精度高比 medium 在单卡上训练更快适合作为消防场景的基线。如果显存只有 6Gbatch 降到 8或者模型换成 yolov8n.pt 再试效果也能接受——火焰这种单类大目标问题对模型容量要求不算极端。epochs100 在 1000 张图、单类别的数据量下足够进入收敛区间。patience20 表示连续 20 个 epoch 验证集指标没有提升就提前停止可以省下不少训练时间。imgsz 默认 640先按默认跑基线后续做小目标优化时再加大这一点在第 6 章展开。提示如果训练卡在数据加载阶段几分钟不动先检查 data.yaml 路径再检查标签是否为空文件不要一上来就怀疑显存不够。4.3 训练日志的关键指标从 loss 曲线到 mAP 的判读训练启动后终端会实时打印 loss 和 mAP 指标也可以训练结束后用下面命令打开可视化面板tensorboard --logdir runs/detect/train主要盯四个指标box_loss 衡量检测框回归的误差cls_loss 衡量分类误差mAP50 是 IoU 阈值 0.5 下的平均精度mAP50-95 是 0.5 到 0.95 多个阈值下的平均精度。火焰检测这类单类任务mAP50 通常能到 0.9 以上而 mAP50-95 因为阈值更严会低一些两者差距大是正常现象。注意区分两种“看起来正常”的情况。第一种loss 持续下降但 mAP 一直为 0大概率是标签或类别配置错位下一章专门讲。第二种loss 降到低位后震荡不停常见于学习率偏高或 batch 偏小此时先调低 lr不要盲目加 epoch。还有一类需要留个心眼——训练集里负样本比例过高时loss 也会低但模型学到的是“一律不检测”这时的指标是假好看。5. 避坑与常见问题排查标签错位、黑屏漏检与过拟合的典型翻车修复这一章的每条都是真实项目里踩过的坑按“现象 → 原因 → 解决”的顺序写共五条。覆盖标签、场景分布、小目标和环境问题。5.1 训练 mAP 始终为 0但 loss 一直在下降现象训练到 50 个 epochbox_loss 和 cls_loss 都正常下降可 mAP 一直是 0模型预测的框位置全错。原因最常见的是 COCO 的 category_id 从 1 开始直接写进 YOLO TXT 后编号变成 1而 YAML 里 names 只有编号 0 可用模型学到的标签和真实标签错位。另一种可能是划分脚本把原图和标签拷出了相同文件名但 YOLO 找标签时匹配的是“同路径同前缀”某个子目录里恰好缺文件导致大量样本没有标签可学。解决先用小测试集50 张图跑一次训练打印 train 目录里每张图的 TXT 首行编号再写脚本检查图像与标签文件名一一对应。如果 TXT 编号超出 YAML 的 names 范围基本可以确诊编号错位。坐标系问题就按 2.2 节的换算逻辑核对一遍原始坐标和归一化坐标。5.2 夜间场景、浓烟火焰漏检严重现象白天和黄昏场景测试效果不错换到夜间火焰、火焰被浓烟遮挡的画面时模型完全漏检或框的边缘严重漂移。原因1000 张数据集里白天场景占比高夜间和浓烟样本相对少模型对低对比度火焰的特征学习不足。本质是样本分布偏斜不是模型结构问题。解决从数据层面补夜间帧和烟遮场景。如果手头没有更多数据就做亮度扰动增强把图像整体压暗、提高对比度或者做 gamma 变换模拟夜间效果。注意先修标签再增强否则过拟合会被放大。真实项目里我会从负样本视频里抽帧手工标一小批夜间火焰通常 50 张就能明显改善。5.3 小目标火焰全部漏检现象大面积的火焰框很准但远处小火焰、电火花或小范围起火场景没有一个框被召回。原因YOLO 的下采样倍数约 32 倍imgsz640 时特征图上每个网格对应原图 32 像素十几像素的小火焰难以撑起有效特征。另外一个原因是数据里小目标本身少模型没机会学。解决分两步。第一步把 imgsz 提到 832 或 960 再训练小目标对应像素数提升近一倍第二步做 SAHI 切片推理把大图切成小图分别预测再合并结果。切图时重叠率设为 0.2避免目标被切在边界上。imgsz 提升会带来显存压力batch 要相应减半。5.4 模型把强光、反光误判为火现象部署测试时晴天玻璃反光、路灯强光都被标成 fire置信度还不低。原因训练数据里包含较多亮黄色或白色物体火焰和某些高亮物体在颜色空间里接近模型学到的是“亮 暖色 ≈ 火”而不是火焰特有的持续跳动的纹理特征。解决把误判样本收集起来加入训练集作为背景负样本这招比调置信度阈值有效得多或者在后处理里加入帧间稳定性判断——火焰是跳动的静态反光是静止的。对单帧检测方案来说加负样本是唯一可靠路径。5.5 Windows 下运行脚本闪退、路径分隔符报错现象脚本在 Windows 上运行直接闪退或者在路径拼接处报错错误信息类似“路径中使用了非法字符”。原因脚本里写死了 Linux 风格的分隔符“/”在 Windows 的某些环境下拼接路径会失败还有可能是双击 .bat 文件运行时Python 不在 PATH 中脚本启动即退出。解决统一用 pathlib.Path 或 os.path.join 处理路径不要手写字符串拼接。如果是双击 .bat 闪退先在 cmd 窗口手动执行一遍看完整报错再检查 python 和 yolo 命令是否在 PATH 里。Windows 下建议用 PowerShell 或直接在本项目目录开 cmd 运行避免环境变量加载不完整。6. 验证与进阶视频抽帧、小目标增强与导出前必做的精度检查6.1 用一段真实视频快速验证模型泛化能力训练完先别急着看指标找一段真实火灾视频跑一次推理这比 mAP 数字直观得多yolo detect predict modelruns/detect/train/weights/best.pt sourcetest_video.mp4 conf0.25 imgsz640 saveTrueconf0.25 是置信度阈值火焰检测场景我习惯先放宽到 0.2 看漏检再收紧到 0.4 看误报。重点关注视频里是否有连续几帧突然丢框、夜间画面是否大面积漏检、玻璃反光是否被误判。这一步能暴露数据集里样本分布的问题也能验证模型的帧间稳定性。6.2 小目标增强imgsz 832 与切片推理的取舍如果 5.3 节的小目标问题复现就用更高分辨率重训一次imgsz832batch 减半。火焰这类目标在 832 下的特征比 640 清晰得多mAP50 往往有明显提升。代价是训练时间和显存增加推理时也要保持相同 imgsz否则性能会打折扣。如果重训后小目标还是漏再用切片推理兜底。常见做法是把原图切成 512x512 的切片、重叠 20%每片独立推理最后按原图坐标合并框。切片推理的推理时间会成倍增长适合离线分析火焰视频、或者部署在算力充足的服务器端的场景边缘设备上一般不这么干。6.3 导出 ONNX 前先做精度对比部署到边缘设备通常要把权重导出成 ONNX 或 TensorRT engine。导出前三方对比检查不能省用同一张测试图分别跑 PyTorch 权重和 ONNX 权重对比预测框坐标和置信度。有些版本导出后精度损失 1-2 个百分点是正常的但如果框偏移超过几十像素就要检查是否开了不支持的 op 融合或者输入尺寸没有对齐。从那以后我每次拿到一份检测数据集都强制走一遍“格式解析 → 划分校验 → 小目标检查 → 基线训练 → 导出对比”的流程重复劳动确实烦但能挡住绝大多数低级事故。这份资源的 1000 张图和三种标签已经把最烦的数据格式环节解决了剩下的就是按这套流程跑通自己的业务场景。希望帮到你。本文还有配套的精品资源点击获取