ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

火焰目标检测实战:自建数据集与YOLOv8训练全流程

火焰目标检测实战:自建数据集与YOLOv8训练全流程 简介YOLO火焰目标检测入门包面向计算机视觉初学者与火灾预警、安防监控方向的开发者以yolov2模型权重和配套火焰数据集为核心解决实时火焰识别、模型训练与效果验证的实践需求。资源共1559个文件压缩包约57.26MB含519张火焰jpg原图及对应的txt边界框和xml结构化标注另提供yolov2.h5与tflite两种预训练测试模型可直接跑通检测流程或对比不同部署方式。目前已有2520人学习/下载数据覆盖真实烟火场景中的多尺度火焰表现txt标注便于快速读取坐标送入YOLO训练xml保留了更完整的对象位置与尺寸信息适合需要精细调整标注的研究者。初学者可参照资源描述中的训练思路从预处理、模型训练到验证逐步上手开发者可直接加载预训练模型评估火焰框选效果并利用h5/tflite双格式作为从训练环境到边缘端部署的过渡参考应用于火灾预警、工业生产安全监控等实时场景。1. 火焰目标检测为什么需要自建数据集再训练很多第一次接触火焰目标检测的人习惯性地把 yolov8m.pt 下载下来直接丢给一段火灾监控视频结果是“人”“车”“椅子”全出来了就是没有一个火焰框。原因很简单COCO 的 80 个类别里根本没有 fire 这一类。换句话讲火焰目标检测从一开始就不是“拿通用模型来测”的问题而是“先做出一份能用的火焰数据集再训练出一个专门测火焰的模型”的问题。火焰检测的真实应用场景是园区周界、仓库、森林防火、加油站禁火区这类固定监控。它的难点和常规检测很不一样火焰边缘模糊、形态变化快光晕和倒影干扰大真正要报警的火苗在画面里可能只有几十个像素。更麻烦的是晚霞、红灯、橙色灯光、车尾灯在颜色上和火焰高度相似模型很容易“见橙就是火”。所以这个方向的完整落地路径就是三件事整理火焰数据集、用 YOLO 训练专属检测模型、用一套针对火焰场景的测试方法验证误报率和漏报率。这篇笔记就把这三件事的完整流程、关键参数和踩坑点讲清楚适合接消防安防项目的工程师、做相关毕设或竞赛的学生以及想给自己监控系统加防火能力的技术团队。2. 火焰数据集制作收集、标注与划分的完整流程2.1 先搭目录结构YOLO 标注格式与文件对应关系无论用 YOLOv5 还是 YOLOv8数据集目录结构都是一套约定。动手收集图片之前先把目录搭好后面所有的脚本、训练、测试就都围绕这个结构展开fire_dataset/ ├── images/ │ ├── train/ # 训练集图片 │ ├── val/ # 验证集图片 │ └── test/ # 测试集图片 ├── labels/ │ ├── train/ # 与 images/train 对应的标注文件 │ ├── val/ │ └── test/ └── data.yaml # 数据集描述文件每一张图片 xxx.jpg 都对应一个同名的 xxx.txt 标注文件txt 里每一行描述一个火焰框格式是“类别id 中心点x 中心点y 框宽 框高”四个位置值都归一化到 0 到 1 之间。比如一张图里有两处火标注文件内容大致是0 0.532 0.431 0.210 0.175 0 0.812 0.610 0.112 0.088第一列是类别 id火焰场景我通常只建一个类 fire编号 0。类别建多了比如把“烟”“火光倒影”都单独建类反而会放大误检因为烟和倒影的边界比火焰更模糊。后四列是归一化后的中心点和宽高训练时模型读的就是这个坐标不需要手动换算回像素。起步阶段我一般按 600 张训练图、150 张验证图、50 张测试图来配。火焰场景样本量不用贪大但要保证场景覆盖够白天、夜晚、室内、室外、小规模明火、燃气蓝焰都得有。如果后面发现某个场景频繁误检再定向补这个场景的数据。2.2 火焰数据来自哪公开数据集的边界与自建取材方案网上能直接下载的火焰数据集数量其实比想象中少。学术圈常见的 VisFire、FLAME 系列以森林火灾和实验室小尺度火焰为主类目单一、场景偏科作为对比 baseline 可以直接用它训练监控场景的模型泛化效果往往不理想因为监控画面里的火焰尺度、光照、干扰物和学术数据集差得太远。所以更可靠的做法是自建取材我一般从三个渠道收集火焰图片和检测视频素材第一消防演练和日常点检录像。这类素材最容易拿到和安全部门申请后截帧即可视角接近真实监控价值最高。第二自己搭一个可控火源做燃烧实验用酒精盘、纸张、木材、油盆分别点燃手机或相机拍 1080p 视频后抽帧几十秒的视频就能抽出一两百张有效样本。第三从正版图库和短视频素材站定向搜索“明火”“燃气火焰”“火灾演练”等关键词只保留画面清晰、火源明确的素材。这里要特意提醒一句商用项目不要用来源不明、未授权的素材自拍和自己采集的素材最安全图库素材要确认授权范围。除了正样本负样本一定要单独收集。晚霞、红色灯牌、橙色路灯、车尾灯、红底广告、火光倒影这些“像火但不是火”的图片全部放进一个 neg_images 目录。后面验证误报率、调整置信度阈值全靠这一批负样本。2.3 火焰标注实操只框火源本体别框光晕和倒影标注工具我常用 labelImg 或 x-anylabeling导出格式直接选 YOLO。labelImg 更轻量x-anylabeling 自带一些辅助分割能力标注火焰这种边界不规则的目标会快一些但最终都要导出成 bbox 格式的 YOLO 标注。火焰场景的标注经验和常规物体不一样几条硬规矩分享第一框住有“形体”的火焰区域焰心加外焰的亮区一起框不要只框最亮的那一小块。第二不要框地面反光、玻璃倒影、被照亮的烟雾这些只是光的映射不是火源本体。第三同一张图里有多处火时每个火点都要逐个框完尤其不能漏掉角落里几十像素的小火苗这会直接影响模型对早期火灾的召回能力。第四画面严重模糊、火焰和背景完全糊在一起的图宁可删掉也不强行标注否则模型学到的是错误的边缘特征。标注完成后一定要回头过一遍。做火焰检测这几年我最大的体感是数据清洗比模型调参更影响最终效果标签里混入几十个“反光框”后面所有测试指标都会被带偏。2.4 划分数据集按视频片段划分别按帧随机拆数据集划分看着简单其实是火焰检测里最容易埋雷的一步。如果直接对帧列表做随机 split同一段火灾视频的连续帧相似度极高训练集和验证集就会互相“抄答案”验证指标虚高部署到真实场景立刻现原形。正确做法是按视频来源划分同一段视频、同一个镜头抽出来的帧只能全部进训练集或全部进验证集。我一般把截帧脚本里的文件名带上视频 ID比如 fire_001_0001.jpg 里的 fire_001 就是视频 ID然后按这个 ID 整体划分import os import random import shutil frame_dir raw_frames video_map {} # 建立 视频ID - 帧文件列表 的映射 for f in os.listdir(frame_dir): if f.endswith(.jpg): video_id f.split(_)[0] video_map.setdefault(video_id, []).append(f) random.seed(42) for vid, frames in video_map.items(): # 整个视频的帧放进同一侧避免相似样本穿越训练/验证集 target images/train if random.random() 0.8 else images/val os.makedirs(target, exist_okTrue) for f in frames: shutil.copy(os.path.join(frame_dir, f), os.path.join(target, f))这段脚本的逻辑是先把帧文件按视频 ID 分组再以视频为单位做 8:2 划分保证同一个视频的帧不会同时出现在 train 和 val。负样本图片不需要按这个规则走统一放到 val 的单独目录里专门用来测误报率。数据增强方面火焰场景建议 mosaic 开启但概率调低一些比如 0.5因为 mosaic 会把四张图拼接火焰框容易变得特别小。色彩增强要克制火焰的颜色本身就是核心特征把色调饱和度大幅抖动反而会让模型学乱。3. 用 YOLOv8 训练火焰模型模型选型、数据集 yaml 与参数设置3.1 选 n 还是 m火焰检测的场景决定了模型下限先回答一个被问了很多次的问题YOLO 系列里火焰检测到底该用哪个尺寸的模型我的建议是默认从 yolov8m 起步而不是贪快用 n。原因是火焰目标在监控画面里通常很小n 模型的 backbone 比较浅容易把二三十像素的火苗当成噪声滤掉。模型特点适合火焰场景yolov8n轻量、帧率高但小目标漏检明显边缘盒子、720p 以下画面yolov8s精度和速度均衡入门实验、快速验证yolov8m特征提取能力明显提升训练显存约 4GB1080p 监控、小火焰居多推荐yolov8l/x精度上限高训练和推理成本大上千张数据集、追求极致指标训练时不需要从随机权重初始化直接下载官方预训练权重 yolov8m.pt它会自动加载 COCO 上学习到的通用特征。火焰虽然不在 COCO 类别里但纹理、边缘、色彩层次这些底层特征是可以迁移的用预训练权重起步比从零训练收敛快很多也能避免前期 loss 震荡。这里补一句环境配置训练前先确认本机 Python 环境用 pip 或 conda 安装 ultralytics 即可显卡驱动和 CUDA 版本只要满足 PyTorch 要求就行。跑通一次训练后后面所有实验都在同一套环境里迭代。3.2 数据集 yaml 这样写配套一条训练命令YOLOv8 训练自己的数据集核心是先写对 data.yaml。路径部分建议一律用相对路径这样换机器或换目录不会因为绝对路径失效而翻车。文件内容如下path: ./fire_dataset train: images/train val: images/val test: images/test nc: 1 names: 0: firepath 是数据集根目录train、val、test 都是相对于根目录的路径。nc 是类别数火焰单类就写 1后续如果需要把“烟”独立出来nc 改成 2names 里加一行 1: smoke同时标注文件里对应行首的类别 id 也要改。数据文件准备好后用一条命令启动训练yolo detect train \ modelyolov8m.pt \ datafire_dataset/data.yaml \ epochs120 \ imgsz640 \ batch16 \ patience20 \ device0参数说明一下。imgsz640 是最常用的输入尺寸但如果监控原图是 1920x1080画面里的火焰只占很小一块建议把 imgsz 提到 1280模型能更好地捕捉小火焰特征代价是显存占用和训练时间明显上升。batch16 要看显存脸色跑着 OOM 就降到 8。patience20 表示验证集 mAP 连续 20 轮不提升就自动早停避免无效训练浪费时间。device0 指定第一张 GPU只有 CPU 的话m 模型训练会很慢建议先用 s 模型跑通流程。另外强调一下训练日志里除了看 loss 曲线更关键的是看验证集的 mAP 和召回率因为火焰场景里“漏报”的代价远大于“误报”。这两个指标在训练完成后输出的 results.csv 里都能看到。3.3 中断续训与训练异常resume 和 loss 变成 NaN 的处理训练到一半断电、显存被其他任务占掉导致中断是常有的事。好在 YOLOv8 训练过程中会自动保存 last.pt续训命令如下yolo detect train resume modelruns/detect/train/weights/last.ptresume 会读取上次训练的轮次、优化器状态和早停计数接着往下跑。有一点要注意续训前确认数据集路径没有改动否则验证集指标对比会乱掉。如果训练过程中 loss 突然变成 NaN优先检查两类原因一是训练数据里有损坏的图片或全黑全白的异常帧二是学习率过大导致梯度爆炸。解决办法是先把 batch 减半、learning rate 调低到默认值的十分之一重新训练如果仍然 NaN就去数据目录里跑一遍图片完整性校验把所有打不开的文件删掉再训练。3.4 火焰小目标问题与损失函数的关系火焰检测的损失函数选择本质上和“小目标占比”强相关。YOLOv8 的检测头在三个尺度上输出预测默认对 8x8、16x16、32x32 下采样的特征图分别做检测。当火焰目标在 1080p 画面里只有 20x20 像素时经过 32 倍下采样后几乎只剩一个点模型很难从背景中把它分离出来。针对这个问题的常见做法是增大输入分辨率或者把大图裁剪成小块分别推理。比如 1920x1080 的监控画面切成左右两块 960x1080 输入模型再设置适当的 overlap 避免火焰正好被切在边界上。这种处理方式比改动损失函数更直接也能在部署阶段保持可控的显存占用。在损失层面火焰场景没有需要特别魔改的地方YOLOv8 自带的分类损失和回归损失已经够用。真正起作用的是数据层面训练集中小火焰样本占比不要过低如果发现模型对大火焰框检得很好、小火焰几乎全漏就把小目标的标注框做一次重复采样或复制粘贴增强让模型看到更多小尺度样本。这一步做到位比纠结某个损失项的权重参数有效得多。4. 用训练好的模型测试火焰识别效果图片、视频与指标4.1 先用命令行快速做一次图片测试模型训练完成后第一步是用命令行对测试图片做一次快速预测肉眼确认基本效果。命令如下yolo detect predict \ modelruns/detect/train/weights/best.pt \ sourcetest_images/ \ conf0.25 \ iou0.5 \ saveTrueconf0.25 是置信度阈值预测框的置信度低于 0.25 就不输出。火焰场景和普通检测不一样漏报意味着火灾可能没被及时发现所以我建议把阈值放在 0.15 到 0.25 之间起步先看模型的真实召回能力再根据误报情况逐步调高。iou0.5 是 NMS 去重阈值两个框的 IoU 超过 0.5 就合并这个值一般不用动。saveTrue 会把标注好火焰框的图片保存到 runs/detect/predict 目录。跑完先看三件事真火有没有漏特别是画面角落的小火苗晚霞、红色车灯、橙色灯光有没有被误框画面里同时存在多团火时是不是每个都检出来了。这三项过关再进入批量测试。4.2 用 Python 批量跑测试集并记录 FPS命令行 predict 适合快速验证但要量化模型的测试表现我会写一个简单的 Python 脚本把测试集和负样本集一起跑一遍统计召回率、误报数和单图推理耗时。注意这里的召回率按“图”算不按“框”算更贴近火焰报警的实际语义。from ultralytics import YOLO import os import time model YOLO(runs/detect/train/weights/best.pt) test_images [ os.path.join(test_images, f) for f in os.listdir(test_images) if f.endswith((.jpg, .png)) ] neg_images [ os.path.join(neg_images, f) for f in os.listdir(neg_images) if f.endswith((.jpg, .png)) ] t0 time.time() results_test model.predict(test_images, conf0.25, imgsz640, verboseFalse) t1 time.time() results_neg model.predict(neg_images, conf0.25, imgsz640, verboseFalse) t2 time.time() hit sum(1 for r in results_test if len(r.boxes) 0) false_alert sum(1 for r in results_neg if len(r.boxes) 0) print(f测试集召回{hit}/{len(test_images)}误报图数{false_alert}) print(f正样本平均耗时{(t1-t0)/len(test_images)*1000:.1f} ms/图) print(f负样本平均耗时{(t2-t1)/len(neg_images)*1000:.1f} ms/图)这段脚本做了两件关键事正样本测试集里只要有框就算检中统计的是“多少张含火图片被正确发现”负样本目录里只要出现任何一个框就算一次误报。单图平均耗时乘以 1000 转成毫秒数可以直观估算后续视频推理的帧率上限。比如 50ms/图意味着视频推理最多做到 20FPS。真实场景里火焰检测的验收标准一般是“正样本召回 95% 以上、负样本误报接近 0”。如果误报数压不下来优先调整置信度阈值而不是重新训练。4.3 用 val 命令看混淆矩阵yolo 混淆矩阵总合不唯一批量测试之外验证集上的混淆矩阵和 PR 曲线要专门看。命令还是走官方 val 流程yolo detect val \ modelruns/detect/train/weights/best.pt \ datafire_dataset/data.yaml \ conf0.001把 conf 设成 0.001是为了让模型尽可能输出所有候选框暴露它的真实能力边界。val 跑完会在 runs/detect/val 下生成 confusion_matrix.png 和 PR_curve.png。看到混淆矩阵时很多人会困惑为什么每一行加起来不是 100%。这是 yolo 混淆矩阵的正常特性矩阵的列方向上是按真实类别归一化的每一列代表“该类真实样本被预测成各类的比例”列和为 100%行方向不做归一化所以行和不为 100% 是正常的不是脚本出错。火焰场景看混淆矩阵重点盯两个位置一是真实火焰被预测成“背景”的比例这个值越高说明漏检越严重二是真实背景被预测成火焰的比例它对应误报。如果后者偏高基本可以断定训练集负样本不足需要往背景里补充晚霞、红灯这类干扰样本。4.4 负样本测试红灯、晚霞和暖色灯光能不能被压住负样本测试是火焰检测特有的验收环节不做这一项模型很难真正上线。将之前收集的红灯、晚霞、橙色招牌、车辆尾灯、火光倒影图片全部放到一个目录用测试脚本统一过一遍看结果。这里说一个常见做法负样本测试里出现零星框时不要急着重新训练先把阈值往上调比如从 0.25 调到 0.35往往能压掉一半误报同时正样本召回损失不大。如果负样本被大面积框出说明模型把颜色特征学“过”了必须把负样本混入训练集重新训一版。我一般会反复执行 4.2 的脚本用不同阈值跑出几组“召回率-误报数”的对应关系然后按业务要求选一个平衡点。这个平衡点最终会写进部署脚本作为固定的 conf 参数。5. 火焰检测常见坑与排查数据处理、显存与误报问题5.1 训练 loss 正常但 val mAP 很低现象loss 曲线一路下降看起来训练正常但验证集 mAP 始终在 0.1 左右徘徊甚至震荡。原因最常见的是数据集划分方式不对或者训练集里火焰目标尺度分布和验证集差异太大。比如训练集以大火苗为主验证集里全是小火点模型自然表现差。另一个容易被忽略的原因是负样本比例过大模型为了压低误报干脆把输出框全部抑制掉了。解决先确认数据划分是否按视频 ID 分组再看训练集和验证集里小目标的占比是否接近。我一般会把验证集里的每一张图打印出来计算标注框平均面积如果明显小于训练集就用裁剪或增强手段拉齐尺度分布而不是盲目调损失函数。5.2 晚霞、红灯、车尾灯成群误判为火现象模型对真实火焰检测得不错但一到傍晚场景晚霞、红绿灯、车尾灯全部被框出来报警信息多到没法看。原因火焰最直观的特征是“亮、暖色、高饱和”在颜色特征空间里和暖色灯光离得非常近。如果没有在训练数据里加入足够多的暖色负样本模型会把颜色当成最强证据。解决把误报图全部收集起来挑选有代表性的几十张直接放进训练集的 images/train 目录作为背景图参与训练。这一步做一次之后误报会大幅下降。部署层面再加一道后处理真实火焰在视频连续帧里有明显的闪烁特性而信号灯、晚霞是持续稳定的利用连续几帧的像素变化率可以进一步过滤。5.3 燃气蓝焰、阴燃冒烟但不见明火模型不输出现象明火场景检测很好但遇到燃气灶蓝焰或者纸箱阴燃、只冒烟没什么火苗的情况模型完全无输出。原因训练数据以橙黄色明火为主蓝焰的颜色特征完全不同阴燃阶段根本没有明显的火焰轮廓目标检测框本身就不擅长表达“烟”这种弥散目标。解决需要在数据层面补齐短板。收集燃气火焰的图片和视频素材单独抽帧标注。如果项目业务关心的是“发现火情”而不是“发现明火”建议把“烟”单独建一个类别和 fire 并列标注规范里明确“烟”的边界画法。模型结构不用大改nc 从 1 改成 2 即可。5.4 视频推理时火焰框忽有忽无帧率上不去现象同一段视频里前一帧检测到火焰后一帧又没了再下一帧又出现同时推理帧率很低连 5FPS 都到不了。原因忽有忽无大概率是置信度阈值正好卡在模型的临界输出带上火焰形态变化快置信度在阈值边缘上下抖动。帧率低则可能是输入分辨率过大或者用的模型尺寸超出了当前硬件的推理能力。解决把置信度阈值适当提高或降低避开临界带。更稳的办法是加一个“连续确认”逻辑连续 3 帧检测到火焰才触发报警连续 5 帧无火焰才解除报警这样单帧抖动就不会被放大。帧率方面先用 m 模型配合 FP16 精度做推理再不行就换成 s 模型1080P 视频输入裁剪到 640 宽通常能把帧率拉回可接受范围。5.5 训练中途 OOMloss 变成 NaN现象训练刚跑几十轮显卡显存爆掉或者 loss 直接变成 NaN训练过程崩溃。原因OOM 一般是 imgsz 或 batch 设置超出了显存上限NaN 多半是训练数据里有异常文件比如破损 JPEG、纯黑纯白图或者学习率设置过高导致梯度爆炸。解决OOM 优先把 batch 减半imgsz 从 1280 降到 640。NaN 则先去数据目录里做一轮完整性检查把打不开的图片文件移到备份目录再降低学习率重新训练。做完清洗后用 resume 从最近的 last.pt 继续跑即可不需要从头再来。6. 把测试模型做成一个实用火焰检测脚本阈值选择与最小实现训练测试都通过后最后一步是把权重文件变成一段可以真正跑视频和 RTSP 流的检测脚本。这里给出一个最小实现内置了“连续确认”逻辑用来抑制火焰框忽有忽无的问题from ultralytics import YOLO import cv2 model YOLO(runs/detect/train/weights/best.pt) cap cv2.VideoCapture(test_fire_video.mp4) consecutive_hits 0 while True: ret, frame cap.read() if not ret: break results model.predict(frame, conf0.3, imgsz640, verboseFalse) has_fire len(results[0].boxes) 0 # 连续3帧有火才确认报警连续5帧无火才解除 if has_fire: consecutive_hits min(consecutive_hits 1, 10) if consecutive_hits 3: cv2.putText(frame, FIRE ALERT, (50, 80), cv2.FONT_HERSHEY_SIMPLEX, 2, (0, 0, 255), 4) else: consecutive_hits max(consecutive_hits - 1, 0) if consecutive_hits 0: cv2.putText(frame, SAFE, (50, 80), cv2.FONT_HERSHEY_SIMPLEX, 2, (0, 255, 0), 4) cv2.imshow(fire detection, frame) if cv2.waitKey(1) 0xFF ord(q): break cap.release() cv2.destroyAllWindows()conf0.3 是前面 4.4 节里做阈值扫描后选出来的平衡值直接烧进脚本。连续确认机制不复杂火焰出现的帧数连续累积积累到 3 次才确认报警无火焰时计数递减降到 0 才恢复安全状态。这套逻辑能有效过滤前面提到的灯光误报和火焰闪烁抖动。写这个脚本时我吃过一个亏第一版 demo 把阈值设成 0.05结果晚间测试时灯光倒影几乎占满屏幕视频里红成一片。后来老老实实在负样本集上多跑了几组阈值对比才明白火焰检测的阈值必须按“能让误报压到最低”的标准去选而不是一味追求高召回。希望这篇笔记能帮你少走这段弯路。本文还有配套的精品资源点击获取
返回列表