简介:本资源面向计算机视觉与深度学习方向的开发者、学生及安防算法研究人员,提供一套完整的火灾烟火烟雾检测数据集与YOLOv5训练模型,可用于目标检测模型训练、算法验证及课程设计。数据集覆盖大火小火、建筑草原森林起火、车辆起火、昼夜与室内外等多种场景,烟雾场景与火焰保持一致,标注格式为Pascal VOC,含2059张图像及对应XML标签,标签类别为fire。压缩包共332个文件,约200.15MB,包含C/C++源码、Python脚本、YAML配置、CUDA内核、Shell脚本及权重文件等,并附带使用说明与VOC转YOLO格式的转换脚本,便于快速接入YOLOv5训练流程。目前已有838人学习下载,适合需要真实烟火数据与可复现训练方案的读者参考使用。
1. 火灾烟雾检测数据集与 YOLOv5 模型:从 2059 张图像到可复现的检测方案
火灾烟雾检测这件事,真正卡住大多数人的从来不是模型结构,而是数据。你搜 yolov5 烟雾火灾检测,能翻出一堆论文和开源仓库,但真到自己动手,往往发现公开数据集要么只有几百张、要么标签格式对不上、要么全是实验室摆拍的火苗,拿到真实监控画面里直接翻车。这个标题指向的是一套相对完整的组合:2059 张带标签的火灾烟雾图像,配一份 YOLOv5 训练好的检测模型,再加一份使用说明。它解决的是从零搭建烟雾检测系统时最耗时的两件事——找数据、标数据。适合谁?做安防监控二次开发的、做园区/仓库/森林防火预警原型的、以及想拿一个真实场景数据集练 YOLOv5 全流程的工程师。2059 张不算大,但足够跑通训练、验证、部署这条链路,也能让你看清烟雾检测和常规目标检测的差别到底在哪。
2. 烟雾火灾数据集长什么样:标签结构、类别分布与选型判断
2.1 2059 张图像的构成与标签格式
拿到一个数据集,第一件事不是急着训练,而是把目录结构和标签格式摸清楚。火灾烟雾检测数据集通常按 YOLO 标准组织,图像和标签分开放,标签是每张图对应的.txt文件,一行一个目标,格式为class x_center y_center width height,坐标全部归一化到 0 到 1 之间。2059 张这个量级,常见做法是训练集、验证集按 8:2 或 9:1 切分,也可能已经切好放在images/train、images/val这样的子目录里。
先跑一段脚本把数据集的真实情况统计出来,别信任何说明文档里的描述,自己数一遍最靠谱:
import os from collections import Counter from pathlib import Path # 数据集根目录,按实际路径改 root = Path("fire_smoke_dataset") img_dir = root / "images" lbl_dir = root / "labels" class_names = ["fire", "smoke"] # 类别名按 data.yaml 里的顺序填 counter = Counter() img_count = 0 empty_label = 0 bad_label = 0 for img_path in img_dir.rglob("*"): if img_path.suffix.lower() not in (".jpg", ".jpeg", ".png", ".bmp"): continue img_count += 1 # 标签文件名与图像同名,只换后缀 lbl_path = lbl_dir / img_path.relative_to(img_dir).with_suffix(".txt") if not lbl_path.exists(): empty_label += 1 continue with open(lbl_path, "r") as f: lines = [ln.strip() for ln in f if ln.strip()] if not lines: empty_label += 1 continue for ln in lines: parts = ln.split() if len(parts) != 5: bad_label += 1 continue cls_id = int(float(parts[0])) counter[cls_id] += 1 print(f"图像总数: {img_count}") print(f"无标签或空标签图像: {empty_label}") print(f"格式异常标签行: {bad_label}") for cid, cnt in sorted(counter.items()): name = class_names[cid] if cid < len(class_names) else f"未知类别{cid}" print(f"类别 {cid} ({name}): {cnt} 个框")这段脚本做三件事:统计图像总数、找出没有标签或标签为空的图像、统计每个类别的框数量。参数上,class_names必须和data.yaml里的names顺序严格一致,否则你看到的类别统计会张冠李戴。rglob("*")会递归所有子目录,如果你的数据集已经切好 train/val,这个统计会把两边合在一起,想看单边就把img_dir指到具体子目录。
2.2 火灾和烟雾两类目标的分布陷阱
火灾烟雾检测数据集里,火灾和烟雾的框数量往往严重不均衡。火焰目标边界清晰、颜色饱和,标注起来容易;烟雾是半透明、边界模糊、形态随气流变化的东西,标注一致性差,很多数据集里烟雾框要么偏大要么偏小。2059 张图像如果烟雾框占比明显低于火焰,训练出来的模型会偏向火焰检测,烟雾召回率上不去。
判断是否失衡,看上面脚本输出的两类框数量比值。经验上,如果某一类框数不到另一类的三分之一,就属于明显失衡,需要在训练时用类别权重或者过采样来补。另一个隐蔽问题是同一张图里火焰和烟雾重叠,标注时容易出现框套框,YOLOv5 对这种重叠目标的处理依赖 NMS 的 IoU 阈值,后面调参章节会细说。
提示:先统计再训练。我见过太多人直接开训,跑完发现某一类几乎没检出,回头查才发现那一类在数据集里只有几十个框。
2.3 为什么选 YOLOv5 而不是别的检测器
YOLOv5 在这个场景里被反复选中,理由很实际。第一,它的工程化程度高,数据加载、增强、训练、导出 ONNX 一条龙,配置文件改几行就能跑,不像有些框架要自己写 dataloader。第二,烟雾检测对实时性有要求,YOLOv5s 在普通 GPU 上能跑到几十 FPS,边缘设备上量化后也能用。第三,社区里 YOLOv5 的烟雾火灾案例最多,遇到问题搜得到答案,这对一线落地很重要。
选型上,如果你追求精度且算力够,用 YOLOv5m 或 l;如果要在树莓派、Jetson 这类设备上跑,YOLOv5n 或 s 更合适。2059 张这个数据量,不建议上太大的模型,容易过拟合,s 或 m 是比较稳的起点。
3. 用 YOLOv5 训练烟雾火灾检测模型:环境、配置与训练命令
3.1 环境配置与依赖安装
YOLOv5 的环境配置是新手第一个坎。核心依赖是 PyTorch、torchvision 和一堆工具库。先确认显卡驱动和 CUDA 版本,再装对应版本的 PyTorch,这一步版本对不上后面全是玄学报错。
# 创建独立环境,避免污染系统 Python conda create -n fire_smoke python=3.8 -y conda activate fire_smoke # 按你的 CUDA 版本装 PyTorch,这里以 CUDA 11.3 为例 pip install torch==1.12.1+cu113 torchvision==0.13.1+cu113 \ --extra-index-url https://download.pytorch.org/whl/cu113 # 克隆 YOLOv5 源码并安装依赖 git clone https://github.com/ultralytics/yolov5 cd yolov5 pip install -r requirements.txt参数说明:Python 版本建议 3.8 到 3.10,太新或太旧都可能碰到依赖冲突。PyTorch 版本要和 CUDA 驱动匹配,装完用python -c "import torch; print(torch.cuda.is_available())"验证,输出True才算通。requirements.txt里锁了各库版本,别随意升级,尤其是 numpy 和 opencv。
3.2 data.yaml 与模型配置文件的写法
YOLOv5 训练靠两个配置文件:数据集描述文件data.yaml和模型结构文件yolov5s.yaml。data.yaml告诉框架去哪找图、有几类、类名叫什么:
# data.yaml path: ./fire_smoke_dataset # 数据集根目录 train: images/train # 训练集图像相对路径 val: images/val # 验证集图像相对路径 nc: 2 # 类别数 names: ["fire", "smoke"] # 类别名,顺序必须和标签里的 class id 对应nc和names是最容易出错的地方。nc写错会导致训练时索引越界,names顺序写反会让火焰和烟雾的预测结果对调。模型结构文件一般不用改,用yolov5s.yaml就行,它里面的nc会被data.yaml覆盖。
3.3 训练命令与关键超参数
配置齐了就能开训。下面是一条常用的训练命令,带上了烟雾检测场景里比较关键的几个参数:
python train.py \ --img 640 \ --batch 16 \ --epochs 150 \ --data data.yaml \ --weights yolov5s.pt \ --cfg models/yolov5s.yaml \ --hyp data/hyps/hyp.scratch-low.yaml \ --name fire_smoke_s \ --cache逐个说参数。--img 640是输入分辨率,烟雾目标通常占画面比例不小,640 够用,显存紧张可以降到 512。--batch 16看显存调,8G 显存跑 s 模型 640 分辨率大概能到 16。--epochs 150对 2059 张这个量级偏多,配合早停一般 80 到 120 轮就收敛,设 150 是留余量。--weights yolov5s.pt用预训练权重,小数据集上迁移学习比从头训效果好得多。--hyp指定超参文件,烟雾检测建议用低增强配置,因为烟雾本身形态变化大,过强的色彩和几何增强反而干扰学习。--cache把图像缓存到内存,2059 张不大,开了能明显加快每轮速度。
训练过程中重点看三个指标:mAP@0.5、每一类的precision和recall。烟雾类的 recall 通常比火焰低,如果低太多,回到 2.2 节检查类别失衡。
4. 推理、验证与部署:把训练好的模型跑起来
4.1 用 detect.py 做单张和批量推理
训练完的权重在runs/train/fire_smoke_s/weights/best.pt。先用detect.py验证效果:
# 单张图像推理 python detect.py \ --weights runs/train/fire_smoke_s/weights/best.pt \ --source test_images/fire_01.jpg \ --img 640 \ --conf-thres 0.35 \ --iou-thres 0.45 \ --save-txt # 批量推理整个目录 python detect.py \ --weights runs/train/fire_smoke_s/weights/best.pt \ --source test_images/ \ --conf-thres 0.35 \ --save-txt --save-conf--conf-thres是置信度阈值,烟雾检测里这个值不能设太高,烟雾特征弱,设 0.5 以上容易漏检,0.3 到 0.4 是常见区间。--iou-thres控制 NMS 的重叠阈值,火焰和烟雾重叠时,这个值设太小会把其中一个框抑制掉,设太大又会留下重复框,0.45 是折中。--save-txt把检测结果存成 YOLO 格式,方便后续做误检分析。
4.2 验证集评估与指标解读
跑val.py拿到量化指标:
python val.py \ --weights runs/train/fire_smoke_s/weights/best.pt \ --data data.yaml \ --img 640 \ --task val \ --save-json输出里重点看mAP@0.5和mAP@0.5:0.95。前者是 IoU 阈值 0.5 时的平均精度,后者更严格。烟雾检测里mAP@0.5能到 0.7 以上就算可用,mAP@0.5:0.95通常低不少,因为烟雾框的定位精度天然差。如果两个指标差距特别大,说明框的位置回归不准,可以考虑在损失里加大定位项的权重。
4.3 导出 ONNX 与边缘部署
要在非 Python 环境部署,导出 ONNX:
python export.py \ --weights runs/train/fire_smoke_s/weights/best.pt \ --include onnx \ --img 640 \ --batch 1 \ --simplify--simplify会做图优化,去掉冗余算子,导出的模型更小更快。--batch 1是推理时的批大小,边缘设备一般单张推理。导出后在目标平台上用 ONNX Runtime 或 TensorRT 加载,注意输入预处理要和训练时一致:letterbox 缩放、归一化到 0 到 1、BGR 转 RGB,这几步顺序错了检测结果会完全乱掉。
5. 烟雾火灾检测的避坑与排查:那些让模型失效的细节
5.1 现象:训练 loss 正常下降但验证 mAP 一直很低
原因通常是标签和图像没对齐,或者data.yaml里的路径写错导致验证集加载的是空数据。YOLOv5 在路径错误时不一定报错,而是静默跳过,你看到的低 mAP 其实是模型在瞎猜。解决:用 2.1 节的统计脚本确认验证集图像和标签都能被找到,再检查data.yaml里val路径是相对path还是绝对路径,两种写法混用会出问题。
5.2 现象:火焰检测很准,烟雾几乎检不出
原因是烟雾类样本少或标注质量差。烟雾边界模糊,标注时框往往偏大,模型学到的是"大框=烟雾"这种错误关联。解决:先统计两类框数量,失衡就用--cls权重或者对烟雾样本过采样;再抽查一批烟雾标签,看框是否贴合烟雾主体,偏太大的重新标。另外可以把hyp里的mosaic增强关掉试试,mosaic 拼接会让烟雾形态更碎,有时反而有害。
5.3 现象:模型在测试集上很好,接到监控视频里全是误检
原因是数据集和真实场景的域差异。2059 张图像大概率来自特定场景,而监控画面有逆光、夜间红外、雨雾干扰,这些在训练集里没有。解决:从目标场景里抽几百帧,人工标一部分做微调,哪怕只标 200 张也能明显改善。另外推理时把--conf-thres调高到 0.5 以上先压误检,再逐步往下找召回和误检的平衡点。
5.4 现象:导出 ONNX 后检测结果和 PyTorch 不一致
原因是预处理没对齐。PyTorch 推理时 YOLOv5 内部做了 letterbox 和归一化,导出 ONNX 后这些操作不在图里,需要你在外部代码里手动实现。解决:对照utils/augmentations.py里的letterbox函数,在部署代码里复现同样的缩放和填充逻辑,归一化用img / 255.0,通道顺序确认是 RGB。
5.5 现象:训练到一半显存爆了
原因是--batch设太大或者--img分辨率太高,也可能是--cache把图像全加载进内存后和显存争抢。解决:先把--batch减半,还不行就降--img到 512,--cache改成--cache ram或直接去掉。2059 张图像用 s 模型,8G 显存跑 640 分辨率 batch 16 是安全线。
6. 让烟雾检测真正可用的两个进阶技巧
第一个技巧是分帧投票。监控视频里单帧误检很常见,但连续多帧同一位置都检出烟雾,可信度就高得多。做法是把检测结果按时间窗口聚合,比如 1 秒内 25 帧里有 15 帧在同一区域检出烟雾,才触发告警。这个逻辑不复杂,但能把误报率压下来一大截。实现上,对每帧的检测框做简单的位置聚类,统计每个聚类在窗口内的出现次数,超过阈值才输出。参数上,窗口长度和触发比例要根据场景调,仓库这种烟雾扩散慢的场景窗口可以长一点,开放空间短一点。
第二个技巧是用验证集做阈值扫描,而不是拍脑袋定conf-thres。写个脚本,把置信度阈值从 0.1 扫到 0.9,每个阈值下算一遍 precision 和 recall,画出曲线,选 F1 最高的那个点。烟雾检测里这个最优点往往在 0.3 到 0.45 之间,但具体值取决于你的数据集和场景。我一般会把这个扫描脚本固化下来,每次换数据集或微调模型后都跑一遍,比凭感觉调靠谱得多。
import numpy as np from pathlib import Path # 假设你已经用 val.py --save-json 导出了预测结果 # 这里演示阈值扫描的核心逻辑 def scan_threshold(preds, gts, thresholds=np.arange(0.1, 0.95, 0.05)): results = [] for t in thresholds: tp = fp = fn = 0 for pred, gt in zip(preds, gts): # pred: [[cls, conf, x1, y1, x2, y2], ...] # gt: [[cls, x1, y1, x2, y2], ...] kept = [p for p in pred if p[1] >= t] matched_gt = set() for p in kept: best_iou, best_idx = 0, -1 for i, g in enumerate(gt): if i in matched_gt or g[0] != p[0]: continue iou = compute_iou(p[2:], g[1:]) if iou > best_iou: best_iou, best_idx = iou, i if best_iou >= 0.5: tp += 1 matched_gt.add(best_idx) else: fp += 1 fn += len(gt) - len(matched_gt) precision = tp / (tp + fp + 1e-6) recall = tp / (tp + fn + 1e-6) f1 = 2 * precision * recall / (precision + recall + 1e-6) results.append((t, precision, recall, f1)) return results def compute_iou(box_a, box_b): xa = max(box_a[0], box_b[0]); ya = max(box_a[1], box_b[1]) xb = min(box_a[2], box_b[2]); yb = min(box_a[3], box_b[3]) inter = max(0, xb - xa) * max(0, yb - ya) area_a = (box_a[2]-box_a[0]) * (box_a[3]-box_a[1]) area_b = (box_b[2]-box_b[0]) * (box_b[3]-box_b[1]) return inter / (area_a + area_b - inter + 1e-6)这段代码的核心是遍历阈值、对每个阈值算 TP/FP/FN、再算 F1。compute_iou是标准 IoU 计算,匹配阈值固定 0.5。实际用的时候把preds和gts换成你从验证集导出的真实数据就行。跑完选 F1 最高的阈值,写进部署配置。
这两个技巧都不复杂,但决定了模型是"能跑"还是"能用"。我自己的习惯是,任何检测模型上线前,先跑一遍阈值扫描,再在真实视频上做分帧投票测试,两步都过了才敢接告警。烟雾检测尤其如此,漏报和误报的代价都不小,多花半天调这两个参数,比事后救火划算。希望帮到你。
本文还有配套的精品资源,点击获取