简介:这是基于YOLOv5的火焰识别检测项目,面向智慧工地、智慧电网、智慧小区等工业化场景的开发者与算法工程师,也适合需要快速构建视觉检测原型的技术团队;资源配有约4000张火焰训练数据集,所有标签已转换为txt格式,用户无需额外整理标注文件,安装依赖后即可直接训练和测试。压缩包共2000个文件,大小275.8MB,包含jpg图片、txt标签及xml标注文件,另有yaml配置文件、Python脚本、Dockerfile等,分别对应模型训练、配置部署与容器化运行需求。项目代码结构完整,README说明文档、启动脚本和依赖清单等辅助文件齐全,方便对照操作,从数据准备到模型推理的链路清晰。目前已有7281人学习下载。作者在本机训练所得模型准确率约97%,并承诺遇到问题可无偿协助排查,能帮助读者在工业场景中快速落地火焰检测方案。
1. 用 YOLOv5 做火焰识别:一张标注正确的数据集,胜过十次调参
火焰检测在工业视觉和安防监控里一直是个「看着简单、落地头疼」的方向。很多人第一次跑通 YOLOv5 火焰识别,都是拿现成预训练权重直接预测视频帧,效果乍一看还行——火焰红通通的,跟背景差异大,好像不需要专门训练。但一旦把模型丢到真实场景,比如傍晚的霞光、电焊火花、红色车尾灯、炉膛反光,误报率立刻拉满。这时候才知道,火焰识别的难点不在「识别火」,而在「分清什么不是火」。
用 YOLOv5 做这个任务,核心优势是两点:第一,模型体积和推理速度适合边缘设备,Jetson、工控机甚至树莓派都能跑得起;第二,YOLOv5 的训练生态成熟,数据准备、训练、导出部署链路短,踩坑时社区里能查到的问题面广。配合一个 4000 张规模的火焰数据集,模型足以在室内外火焰、初期火灾、不同光照条件下达到可用的识别水平。
这篇文章按我从零跑通这个项目的顺序来写:先说数据集怎么整理、标注哪些坑必须躲开,再给完整的训练流程和参数配置,然后是排查经验,最后是导出模型做真实部署验证。新手可以照着命令一步步跑,熟手可以直接跳到第 4 章看参数取舍和第 5 章的踩坑记录。
2. 先把 4000 张火焰数据集理清楚:目录结构、标注格式与清洗策略
2.1 YOLO 格式的数据集长什么样
YOLOv5 的数据集不认文件夹名,只认两个东西:一张图片对应一个同名 txt 标注文件,以及一个记录类别名的 classes.txt(在 data.yaml 里指定)。拿到 4000 张火焰图后,第一步不是急着训练,而是把文件结构理顺。标准结构如下:
dataset/ ├── images/ │ ├── train/ # 训练图,约 3400 张 │ ├── val/ # 验证图,约 400 张 │ └── test/ # 测试图,约 200 张 ├── labels/ │ ├── train/ # 与 images/train 同名同前缀的 txt │ ├── val/ │ └── test/ └── flame.yaml注意 images 和 labels 目录必须在同一个父目录下,YOLOv5 训练时用 yaml 文件里的路径指过去,不会帮你自动找。flame.yaml 内容:
train: dataset/images/train val: dataset/images/val test: dataset/images/test nc: 1 names: ['fire']nc 是类别数,火焰检测通常只有 fire 一个类。如果你手里数据集里区分了「明火」和「烟雾」,那 nc 就要写成 2,但训练难度会明显增加——烟雾的形状边界极不规整,小目标也多,和火焰混合标注很容易把模型搞糊涂。我的建议是:第一版只做单类火焰,跑通流程后再考虑加烟雾。
2.2 标注质量决定模型上限:三类必须返工的标签
4000 张数据集听起来不少,但标注质量如果不检查,模型就只能在垃圾标签上拟合。我拿到数据集后第一件事是写个脚本扫标注,重点查三类问题:
第一,框体超出图像边界。这个最常见,标注工具导出时坐标没裁剪干净。YOLO 格式的坐标是归一化的,x_center、y_center 必须在 0~1 之间,宽高值只能为正。超界的框训练时会被 YOLOv5 自动忽略或裁剪,但会干扰 anchor 匹配统计。
第二,标注框过小。如果大量火焰框的宽或高小于图像的 1%——比如一个 1280x720 的图里火焰框只有 10 像素宽——这些目标几乎不可能被检测到,还会让训练时的正样本数量虚高。我的做法是设一个阈值,把过小框的图片单独拎出来人工看一遍,确认是不是漏标了远处的火苗。如果确实是远距离小火点,就接受漏检,不要硬标,否则模型会被带偏。
第三,类别混淆。有些数据集的「fire」框把烟雾也框进去一半,有些把红色灯光和火焰一起框住。火焰和烟雾在视觉上重叠度极高,框住火苗中心的标注能帮模型学到「核心燃烧区」,而把整个烟雾区域框进去的标注会让模型学习到错误的形状特征。这个只能靠人工抽检,我一般每 50 张抽 1 张,用可视化脚本把框画到图上过一遍。
2.3 清洗脚本:快速找出坏标注的三个过滤条件
写一个 Python 脚本扫一遍数据集,能省掉一半的人工检查时间。核心是三个过滤条件:
import os from PIL import Image def check_labels(img_dir, label_dir): bad_files = [] for label_name in os.listdir(label_dir): if not label_name.endswith('.txt'): continue img_name = label_name.replace('.txt', '.jpg') img_path = os.path.join(img_dir, img_name) if not os.path.exists(img_path): bad_files.append((img_name, 'missing image')) continue img_w, img_h = Image.open(img_path).size with open(os.path.join(label_dir, label_name), 'r') as f: lines = f.readlines() for line in lines: parts = line.strip().split() if len(parts) != 5: bad_files.append((img_name, 'wrong format')) break cls, x_c, y_c, w, h = parts x_c, y_c, w, h = float(x_c), float(y_c), float(w), float(h) if not (0 <= x_c <= 1 and 0 <= y_c <= 1): bad_files.append((img_name, 'center out of range')) break if w <= 0 or h <= 0 or w * img_w < 10 or h * img_h < 10: bad_files.append((img_name, 'box too small')) break return bad_files这段脚本的逻辑很简单:标注文件里每行必须是 class_id、x_center、y_center、width、height 五个值;中心点坐标必须在归一化范围内;框宽高必须大于 0 且实际像素不小于 10。跑完后把 bad_files 打印出来逐条人工复核,比直接拿来训练靠谱得多。
这里要说明的是,10 像素阈值不是绝对的,如果你要检测的场景是远处山火,小火苗本来就只有几个像素,那阈值就要调低。反之如果是近距离厨房火焰识别,阈值可以提高到 30 像素,过滤掉那些不值得学习的微小目标。
3. 训练 YOLOv5 火焰模型的完整流程:从环境配置到权重输出
3.1 环境安装与依赖版本取舍
YOLOv5 对环境的包容度比较高,PyTorch 1.8 到 2.x 都能跑,但有几个依赖版本要小心,不然会遇到奇奇怪怪的报错。以 Ubuntu 20.04 + CUDA 11.8 + PyTorch 2.0 为例,环境安装命令如下:
# 创建虚拟环境 conda create -n yolov5-flame python=3.9 -y conda activate yolov5-flame # 安装 PyTorch(CUDA 11.8 版本) pip install torch==2.0.0 torchvision==0.15.0 --index-url https://download.pytorch.org/whl/cu118 # 克隆 YOLOv5 仓库并安装依赖 git clone https://github.com/ultralytics/yolov5 cd yolov5 pip install -r requirements.txt # 验证环境 python -c "import torch; print(torch.cuda.is_available())"最后一个命令输出 True,说明 GPU 可用。如果输出 False,检查一下 CUDA 驱动版本和 PyTorch 的 cu 版本是不是对得上——这个步骤最常翻车,很多人装完 torch 后忘记看 cuda 是否真的可用,直接开始训练,结果模型在 CPU 上跑了一整晚。
依赖里有个坑:requirements.txt 里的 opencv-python 版本有时会跟系统已装的冲突,建议装完后单独验证一下import cv2。如果报 libGL.so.1 错误,执行apt-get install -y libgl1就能解决。
3.2 训练命令拆解:每个参数为什么这么设
环境就绪后,训练命令是核心。以下是我在 4000 张火焰数据集上验证过的配置:
python train.py \ --data /path/to/flame.yaml \ --weights yolov5s.pt \ --img 640 \ --batch 32 \ --epochs 150 \ --cache ram \ --device 0 \ --project runs/flame \ --name exp1 \ --exist-ok逐项说明:
--weights 选择 yolov5s.pt 作为预训练权重。yolov5 系列有 n/s/m/l/x 五个尺寸,s 是速度和精度的平衡点。火焰目标通常比较大——初期火灾的火苗区域在画面里能占几百个像素——用不到 m 或 l 的特征提取能力,s 在边缘设备上推理速度更快。如果你要检测的是远处小火点,建议换 yolov5m.pt,小目标召回率会好一些。
--img 640 是输入分辨率。火焰数据集里 4000 张图分辨率各异,YOLOv5 会先等比缩放再填充到 640x640。如果你的训练图里有很多 1920x1080 的高清帧,把 --img 设置成 1280 能保留更多小目标细节,但显存占用会涨四倍。我的经验是先用 640 跑通,看 val 集 mAP 是否满足要求,不够再上 1280。
--batch 32 在 24GB 显存的卡上比较稳。如果你的显卡只有 8GB,batch 降到 16,或者干脆用 --batch -1,让 YOLOv5 自动推导最大 batch 值——但这个自动推导是基于当前显存算的,可能会有波动,建议手动设一个固定值。
--epochs 150 在 4000 张图上够用了。火焰检测不是特别复杂的任务,150 轮之后 mAP 基本收敛,再多轮次容易过拟合。如果训练过程中发现 val loss 在 100 轮后还在持续下降,就加 50 轮,不必教条。
--cache ram 会把所有训练图提前加载到内存里,训练速度提升明显。4000 张 640 分辨率的图大约占 6~8GB 内存,一般机器扛得住。如果你的机器内存小于 16GB,建议去掉这个参数。
3.3 训练输出怎么看:val loss、mAP 与 learned 过程
训练过程中终端会不断刷新指标,新手最容易看错的是 P、R、mAP 这几个数。我建议只看三个指标:val/box_loss、val/obj_loss 和 metrics/mAP_0.5。
val/box_loss 是边框回归损失,火焰的框通常边界清晰,这个 loss 应该在 0.02 以下才算正常。如果降不下去,先检查标注框是不是有大量偏移——很多人用自动标注工具生成框,火焰边缘半透明,自动框经常框得太大或太小。
val/obj_loss 是置信度损失,火焰目标在图像里占比通常不小,这个 loss 收敛到 0.03 左右是正常水平。如果反复震荡,大概率是数据里有类别混淆。
metrics/mAP_0.5 是 IoU 阈值 0.5 下的平均精度,火焰检测任务里这个指标到 0.85 以上就可以部署。如果只有 0.6,不要急着调参,先去 val 集里看预测结果图——YOLOv5 训练完会在 runs/flame/exp1 目录下生成 val_batch0_pred.jpg 这样的可视化结果,直接看漏检和误检比看指标更直观。
训练结束后的权重文件在 runs/flame/exp1/weights/ 下,有 best.pt 和 last.pt 两个。best.pt 是验证集指标最优的权重,部署时用它,别用 last.pt。
4. 把识别率从「能用」调到「好用」:火焰场景下的 4 个关键参数
4.1 anchor 不是越大越好,也不是越小越好
YOLOv5 会自动从你的标注框里聚类出 anchor 尺寸,训练前会在终端打印 autoanchor 的结果。火焰框的特点是什么?初期火灾火焰在画面里通常是比较大的连通区域,但远距离监控里的火焰又很小。这两种目标在同一个数据集里共存,anchor 聚类的结果会偏向中间值。
我一般做两步检查。第一步,训练完后看 runs/flame/exp1/ 下的 anchors.png,如果聚类出来的框尺寸分布跟火焰的实际大小差距很大——比如数据集里有很多小火焰,但 anchor 偏大——就在 hyp.scratch-low.yaml 里把 anchor 的初始尺寸调小。第二步,如果你的火焰检测目标是固定场景的,比如厨房监控,画面里火焰大小基本固定,可以直接用 yolov5 自带的 --noautoanchor 参数,跳过聚类,用默认 anchor。这个操作能省一点训练时间,但对迁移场景的泛化性有一定影响,自己权衡。
4.2 置信度阈值和 IoU 阈值:部署时按场景调
训练时不用调这两个阈值,它们是推理时的参数。但部署时一定要调,火焰检测的误报率很大程度是阈值设错了。
python detect.py \ --weights runs/flame/exp1/weights/best.pt \ --source test_video.mp4 \ --conf-thres 0.35 \ --iou-thres 0.45--conf-thres 是置信度阈值,高于这个值的框才输出。默认 0.25 在火焰检测上偏低,容易把晚霞、红色灯光误判成火焰。我一般从 0.4 开始试,如果漏检太多再往下调。--iou-thres 是 NMS 的 IoU 阈值,0.45 是默认值,火焰检测里同一个火焰被多个框框住的情况很少,这个值不用动。
这里有一个实际的调参思路:拿一段包含火焰和干扰物的测试视频,分别用 0.25、0.35、0.45 的置信度跑一遍,数一下每段的误报数和漏报数。火焰检测任务宁可多一点误报,也不能漏报——初期火灾漏报的代价是灾难性的。所以我把部署阈值通常设在 0.3~0.35 之间,误报可以靠后续的时序确认逻辑过滤——连续 5 帧都检测到火焰才触发告警。
4.3 图像增强参数:火焰数据集的特殊处理
YOLOv5 的 hyp.scratch-low.yaml 里有 HSV 增强参数:hsv_h、hsv_s、hsv_v。火焰的颜色是橙红色系,如果增强太强,会把火焰的颜色特征扭曲掉——比如把橙色变成紫色,模型就学不到「火是橙红色」这个关键特征。
我的做法是调低饱和度增强:hsv_s 从默认的 0.7 降到 0.3。这是因为火焰的颜色本身就是很强的判别特征,不需要靠颜色增强来扩充数据多样性。亮度增强 hsv_v 可以保持 0.4 不变,因为火焰亮度在不同场景下差异确实很大——白天的火焰在强光下几乎看不清,晚上的火焰非常亮。
另外,YOLOv5 的数据增强默认包含随机翻转和 mosaic。mosaic 在火焰数据集上效果一般,因为四张图拼在一起后,火焰目标会被缩小,原本就不大的小火点变得更难学。如果训练时发现 loss 下降慢,试试 --no-mosaic 关闭 mosaic,或者只在最后 50 轮关闭 mosaic——YOLOv5 的 epochs 参数配合 hyp 里的 mosaic 概率设置可以实现这个效果。
4.4 类别不平衡:一个类别时也要看背景占比
火焰检测虽然只有 fire 一个类别,但存在另一个不平衡:有火焰的图和没有火焰的图。4000 张数据集里如果大部分图都有火焰,模型会倾向于把任何橙色区域都当成火。反过来,如果背景图太少,模型对非火焰场景的辨别力就差。
我的建议是:在 4000 张火焰图里,至少留出 10% 的纯背景图——没有火焰、没有烟雾、但场景其他元素跟火焰图类似的图。这些图在 val 集里有一个关键作用:计算误报率。如果你发现 val 集的 precision 很高但 recall 低,大概率是背景不够多,模型学得太保守。如果 precision 低,大概率是背景图太少,模型把什么橙色物体都当火。
5. 火焰识别训练避坑指南:五个让你翻车的常见问题
5.1 loss 变成 nan,训练过程直接中断
现象:训练跑到第 20 轮左右,终端输出的 box_loss 和 obj_loss 突然变成 nan,之后所有指标都是 nan。
原因:最常见的是学习率太大导致梯度爆炸。YOLOv5 默认 lr0 是 0.01,如果显卡是 A100 这类大显存的卡,batch 设大了之后梯度累积值会异常。另一个原因是数据里存在异常标注——比如某个标注框的坐标范围是 0 到 3(超出归一化范围),生成的 anchor 匹配结果就会是无效值。
解决:先跑一遍第 2 章的清洗脚本,把所有超界框过滤掉。看问题是否还存在——如果继续出现,把学习率从 0.01 降到 0.005,同时把 batch 减半。我有一个经验:batch 翻倍时,学习率最好也翻倍。如果你从 batch 16 跳到 32,学习率还停留在 0.01,就会让梯度更新的步伐变大,nan 的概率也随之增高。
5.2 模型把小火焰全漏掉了,recall 惨不忍睹
现象:验证集 mAP 不错,0.8 以上,但用真实视频测试时,画面里 50 米外的初期火苗完全没检测出来。
原因:输入分辨率太低。640x640 的输入下,一个在原始 1920x1080 画面里只有 30x30 像素的火焰,缩放到 640 分辨率后就只剩 10x10 像素,几乎是检测极限。
解决:训练和推理都改用 --img 1280。这会带来显存压力,但火焰检测通常是固定摄像头场景,算力可以预留。另一个思路是保持 640 训练,部署时用 --img 1280 推理——但效果不如训练时就用 1280,因为模型没见过这个尺度的特征。如果你的边缘设备算力有限,务实的选择是调整摄像头安装位置,让火焰在画面中的占比尽量大,同时接受 50 米外小火苗的漏检。
5.3 红色车尾灯被当成火焰,误报率极高
现象:部署到路边加油站场景后,模型把红色轿车尾灯、红色店招、晚霞都检测成火焰,告警一天响几十次。
原因:数据集里缺少「近似火焰颜色的干扰物」负样本。火焰数据集的 4000 张图如果只有火焰正样本,模型就会学到「红色 = 火」这个粗暴规律。
解决:这是数据问题,不是模型问题。收集大量红色物体——车尾灯、红色灯光、朝霞晚霞、电焊火花、铁水、红色工装——标注为 background(不标注也是可以的,重点是要让模型在训练时看到这些图但不需要输出任何框)。把这些图混入训练集,重新训练一遍。我在项目中加入了大约 500 张干扰图之后,误报率从每个小时几十次降到了一两次。如果加入干扰图后误报还是不降,再看置信度阈值,把 conf-thres 从 0.25 提到 0.4 也有帮助。
5.4 显存不足,OOM 中断训练
现象:训练开始没多久就提示 CUDA out of memory,有些环境还会直接卡死。
原因:batch size 设得太大、输入分辨率 1280、cache ram 把内存也吃满了,这几个因素叠加。
解决:按优先级做三件事:batch 减半、关闭 --cache ram、输入分辨率从 1280 降到 640。如果你的显卡只有 6GB 显存,用 yolov5n(nano)模型而不是 yolov5s,精度会差一些,但能在低显存下完成训练。还有一个不太起眼的坑:多卡训练时 batch 是全局值而不是每卡值,如果你用 2 张卡跑 --batch 64,实际是每卡 64,显存爆炸是必然的。在火焰检测这种单类别任务上,一张卡足够。
5.5 模型已训练完成,但 detect.py 检测不出任何结果
现象:weight 文件存在,detect.py 运行完,输出文件夹里没有一张画框的图,终端提示 0 objects detected。
原因:最可能是训练和推理的数据配置不一致。比如训练用的 flame.yaml 里类别名是 fire,但部署时改了别的名称;或者训练时输入分辨率是 640,推理时用 --img 1280,模型的特征金字塔输出和 NMS 逻辑对不齐。
解决:先用训练时生成的可视化图确认权重本身正常——val_batch0_pred.jpg 里如果有检测框,说明权重没问题。然后检查 detect.py 的参数,conf-thres 不要设太高,从 0.1 开始排查;如果 0.1 都检测不到,基本就是数据和权重不匹配,回到配置文件检查 nc 和 names 是否一致。
6. 把训练好的火焰模型部署到实际项目:导出 ONNX 并用 OpenCV 做实时检测
模型训练完不是终点,能跑在真实业务里才是。这个章节分享我的部署路径:把 best.pt 导出成 ONNX 格式,再用 OpenCV 的 DNN 模块加载推理。选择 ONNX 而不是直接用 PyTorch 的原因是:生产环境的推理机器不一定有 PyTorch,而且 ONNX 的推理速度比 PyTorch 原版快,CPU 上也能跑到实时。
6.1 导出 ONNX 的命令与验证
YOLOv5 自带了导出脚本:
python export.py \ --weights runs/flame/exp1/weights/best.pt \ --img 640 \ --batch 1 \ --include onnx \ --simplify导出完成后会生成 best.onnx,在项目里叫 best_sim.onnx 的文件是简化后的版本,部署优先用这个。--simplify 参数会通过 ONNX Simplifier 去掉计算图中的冗余节点,推理速度能提升 10%~20%。
验证导出结果是否正确,用 onnxruntime 跑一张图,对比 PyTorch 的推理结果。这一步很关键——有些模型导出来精度就变了,不验证就上线,坑的是自己。
import onnxruntime as ort import numpy as np import cv2 # 加载模型 sess = ort.InferenceSession('best_sim.onnx') input_name = sess.get_inputs()[0].name # 预处理:与 YOLOv5 一致的 letterbox 缩放 img = cv2.imread('test_fire.jpg') img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) h, w = img.shape[:2] scale = min(640 / w, 640 / h) new_w, new_h = int(w * scale), int(h * scale) img_resized = cv2.resize(img, (new_w, new_h)) canvas = np.full((640, 640, 3), 114, dtype=np.uint8) canvas[:new_h, :new_w] = img_resized input_tensor = canvas.astype(np.float32) / 255.0 input_tensor = np.transpose(input_tensor, (2, 0, 1))[None] # 推理 outputs = sess.run(None, {input_name: input_tensor}) print(outputs[0].shape) # (1, 25200, 6) 或 (1, 6300, 6)这里输出的 shape 需要说明一下。YOLOv5 的检测头输出维度是 (num_anchors, 5 + num_classes),5 代表 x_center、y_center、width、height、objectness。火焰检测只有一个类别,所以最后一维是 6。25200 是 640x640 输入下三个尺度特征图(80x80、40x40、20x20)anchor 数量的总和。如果你导出时改了 --img,这个数字会变,NMS 的逻辑也要相应调整。
从 ONNX 的裸输出到最终的检测框,还需要做一次 NMS。用 OpenCV 的 dnn.NMSBoxes 可以搞定,不需要额外依赖。
import cv2 boxes, confidences = [], [] for det in outputs[0][0]: x_c, y_c, w, h, obj_conf, cls_conf = det if obj_conf * cls_conf < 0.35: continue boxes.append([x_c - w/2, y_c - h/2, w, h]) confidences.append(obj_conf * cls_conf) idx = cv2.dnn.NMSBoxes(boxes, confidences, 0.35, 0.45)NMS 的两个阈值跟 detect.py 里的 conf-thres 和 iou-thres 一一对应。这个代码块可以直接嵌到你的视频帧处理循环里,每帧跑一次。在我的实测中,CPU 上处理 640x640 的帧,ONNX 推理加 NMS 总共约 80ms——达不到 25fps 的实时,但在火焰检测这类不需要每帧都处理的场景够用了。如果要对每帧都全速检测,就得上 GPU 或 TensorRT。
6.2 部署时别忘了做的两件事
第一件,测试视频要覆盖「训练时没见过的场景」。很多项目在训练集上表现完美,一到真实场景就露馅,本质是数据分布变了。我的习惯是准备三段测试视频:一段是室内厨房火焰(近景、亮环境),一段是室外夜间火焰(远景、暗环境),一段是只有干扰物没有火焰的监控视频。前两段验证 recall,最后一段验证误报率。
第二件,记录的推理耗时和置信度分布要留档。上线后模型出问题,对比这个基线数据能快速定位是场景变化还是模型退化。
个人习惯是每个项目都会把导出的 ONNX 文件和验证脚本放在同一目录,每次调整置信度阈值后重新跑一遍验证脚本,把结果存成 CSV——这比改参数全靠感觉要稳得多。火焰检测这件事,本质是跟误报和漏报博弈,你掌握的数据越细,博弈的胜算越大。希望这些经验对你有帮助。
本文还有配套的精品资源,点击获取