ARTICLE DETAIL

资讯详情

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

基于YOLOv5的火灾烟雾检测:2059张图像数据集训练与部署实战

基于YOLOv5的火灾烟雾检测:2059张图像数据集训练与部署实战 简介本资源面向计算机视觉与深度学习方向的开发者、学生及安全监测研究人员提供一套完整的火灾烟火烟雾检测方案解决实际场景中火焰与烟雾目标识别与定位问题。数据集覆盖大火小火、建筑草原森林起火、车辆起火、昼夜及室内外等多种场景烟雾场景与火焰保持一致共2059张图像并附Pascal VOC格式标签可直接用于训练与评估。压缩包约200.15MB含332个文件以C源码、头文件、Python脚本、YAML配置、CUDA内核及Shell脚本为主另含少量图像、权重与说明文档构成从数据到模型再到推理的完整工程结构。资源同时提供基于YOLOv5的烟雾火灾检测模型及使用说明并给出VOC转YOLO格式的转换思路便于读者快速复现训练流程、理解检测网络结构与数据组织方式。目前已有838人学习下载适合需要烟火检测数据集与可运行模型的中高级读者参考实践。1. 火灾烟火烟雾检测数据集与 YOLOv5 模型从 2059 张图像到可部署的检测器手上拿到一份 2059 张图像带标签的火灾烟火烟雾检测数据集外加一套 YOLOv5 训练配置和使用说明最直接的问题不是「这数据集好不好」而是「我能不能在半天内把它跑成一个能用的检测器」。火灾烟雾检测这个方向难的不是模型结构难的是数据本身——烟雾是半透明、边界模糊、形态随气流不断变化的非刚性目标火焰又存在过曝和颜色漂移同一张图里烟和火还可能互相遮挡。2059 张的规模不算大但足够跑通一个 YOLOv5s 级别的基线前提是标签格式对、类别定义清晰、划分不泄漏。这篇笔记按「数据集怎么验 → 环境怎么配 → 训练参数怎么调 → 推理怎么落地 → 坑在哪」的顺序走一遍目标读者是手里已经有这批数据、想尽快拿到一个可复现基线的人新手能照着命令跑熟手能直接看参数边界和排错思路。2. 先验数据集2059 张图像和标签到底能不能直接喂给 YOLOv52.1 火灾烟雾数据集的三个隐性质量指标拿到任何一份带标签的检测数据集我一般不会先看图像而是先看标签分布。火灾烟火烟雾这个场景有三个指标比图像数量更关键。第一是类别定义。常见做法是把类别分成 smoke、fire 两类也有把 fire 再拆成 flame、spark 的。类别拆得越细单类样本越少2059 张里如果 fire 只占两三百张拆细之后每类不足百张YOLOv5 的分类头很难收敛。我一般会先统计每类框的数量如果某一类框数低于 500就考虑合并或做针对性增强。第二是框的尺度分布。烟雾在远景图像里可能只占几十像素在近景里能铺满整张图。YOLOv5 默认的三组 anchor 是针对 COCO 调的如果数据里小目标占比高P3 特征层的召回会明显偏低。验证方法是把所有标注框的宽高画成散点图看有多少落在 32×32 以下。第三是图像来源一致性。2059 张如果混了监控截图、无人机航拍、网络爬取三种来源分辨率、色调、压缩噪声差异会很大训练时容易出现模型在某一子域上过拟合。我一般会按来源分桶检查每个桶的样本量必要时在划分验证集时按来源分层抽样。import os import glob from collections import Counter label_dir datasets/fire_smoke/labels/train img_dir datasets/fire_smoke/images/train # 统计每类框数量 cls_counter Counter() box_sizes [] for txt in glob.glob(os.path.join(label_dir, *.txt)): with open(txt) as f: for line in f: parts line.strip().split() if len(parts) ! 5: continue c, x, y, w, h int(parts[0]), *map(float, parts[1:]) cls_counter[c] 1 box_sizes.append((w, h)) print(类别框数:, dict(cls_counter)) small sum(1 for w, h in box_sizes if w * h 0.01) # 相对面积小于1% print(f小目标占比: {small / len(box_sizes):.2%}) print(f图像数: {len(glob.glob(os.path.join(img_dir, *.jpg)))})这段脚本做三件事统计每类标注框数量、计算相对面积小于 1% 的小目标比例、核对图像文件数是否和标签文件数对得上。参数上0.01这个阈值对应约 32×32 像素在 320 输入下可以根据实际输入尺寸调整。如果小目标占比超过 30%后面训练时要把输入尺寸往上提或者加一个 P2 检测层。2.2 标签格式核对YOLO txt 的五个字段和常见错位YOLOv5 用的是 YOLO 格式的 txt 标签每行五个字段class_id x_center y_center width height后四个都是归一化到 0~1 的相对值。火灾烟雾数据集里最常见的标签问题是坐标没归一化直接写了像素值。判断方法很简单如果 x_center 出现大于 1 的数就是像素坐标没转。另一个高频问题是类别索引从 1 开始。有些标注工具默认从 1 编号而 YOLOv5 要求从 0 开始。如果训练时发现所有框都预测成同一类或者 loss 一开始就异常高先回去查这个。# 快速检查标签是否归一化、类别索引范围 awk {if($21 || $31 || $41 || $51) print FILENAME: 未归一化; if($11) print FILENAME: 类别索引可能从1开始} datasets/fire_smoke/labels/train/*.txt | head -20这条命令扫一遍训练标签把坐标越界和类别索引大于 1 的文件名打出来。head -20只是看前几条确认问题存在后要去掉。如果确实有未归一化的标签需要写转换脚本按图像实际宽高做除法不能直接截断。2.3 训练集/验证集划分别让同一段视频的帧泄漏到两边火灾烟雾数据很多是从视频抽帧来的相邻帧之间几乎一模一样。如果随机划分训练集和验证集会包含同一段视频的连续帧验证指标会虚高实际部署时性能掉得厉害。我一般会先按视频来源或时间戳分组再按组划分保证同一段视频的帧只出现在一边。import random from pathlib import Path img_dir Path(datasets/fire_smoke/images/all) # 假设文件名前缀是视频ID如 vid01_frame023.jpg groups {} for p in img_dir.glob(*.jpg): vid p.stem.split(_)[0] groups.setdefault(vid, []).append(p) vids list(groups.keys()) random.seed(42) random.shuffle(vids) split int(len(vids) * 0.8) train_vids, val_vids set(vids[:split]), set(vids[split:]) for phase, vset in [(train, train_vids), (val, val_vids)]: out Path(fdatasets/fire_smoke/images/{phase}) out.mkdir(parentsTrue, exist_okTrue) for v in vset: for p in groups[v]: (out / p.name).symlink_to(p.resolve())这段按视频 ID 分组后做 8:2 划分用软链接把图像分到 train/val 目录标签目录同理处理。random.seed(42)保证划分可复现。如果文件名里没有视频 ID就需要从文件创建时间或 EXIF 里提取时间戳来分组。划分完之后验证集和训练集的图像数量比例不一定是 8:2因为各视频帧数不同这是正常的按组划分优先保证不泄漏。3. YOLOv5 环境配置与训练从零跑通火灾烟雾检测基线3.1 环境配置CUDA、PyTorch 和 YOLOv5 源码的版本对齐YOLOv5 对环境版本比较敏感尤其是 PyTorch 和 CUDA 的匹配。我一般用 conda 建独立环境避免和系统里的其他 PyTorch 版本打架。截至我最近一次配置比较稳的组合是 Python 3.9 PyTorch 2.0 CUDA 11.8YOLOv5 用官方仓库的 v7.0 分支。conda create -n fire_yolo python3.9 -y conda activate fire_yolo pip install torch2.0.1 torchvision0.15.2 --index-url https://download.pytorch.org/whl/cu118 git clone https://github.com/ultralytics/yolov5.git cd yolov5 pip install -r requirements.txt装完之后一定要验证 GPU 是否可用torch.cuda.is_available()返回 False 的话后面训练会默默跑在 CPU 上速度差几十倍。requirements.txt里包含 opencv、matplotlib、tqdm 等依赖如果公司网络受限可以提前配好 pip 镜像源。提示YOLOv5 仓库更新较快如果requirements.txt里某个包版本冲突优先保证 torch 和 torchvision 的版本匹配其他包可以适当放宽。3.2 数据配置文件fire_smoke.yaml 的四个必填字段YOLOv5 通过一个 yaml 文件描述数据集路径和类别。火灾烟雾数据集一般写成这样# datasets/fire_smoke.yaml path: ../datasets/fire_smoke train: images/train val: images/val nc: 2 names: [smoke, fire]path是数据集根目录train和val是相对路径。nc是类别数names的顺序必须和标签里的 class_id 对应——如果标签里 0 是 fire、1 是 smoke这里写反了训练能跑但推理时类别名会错。我一般会在训练前用一小批数据做一次前向把预测框画出来肉眼确认类别对应关系。3.3 训练命令与超参数火灾烟雾场景下我一般怎么调基线训练命令不复杂关键是几个参数要针对烟雾火焰场景调整。python train.py \ --data datasets/fire_smoke.yaml \ --weights yolov5s.pt \ --img 640 \ --batch 16 \ --epochs 100 \ --hyp data/hyps/hyp.scratch-low.yaml \ --name fire_smoke_s \ --cache逐项说明--weights yolov5s.pt用 COCO 预训练权重2059 张的规模不建议从零训预训练能省一半以上 epoch。--img 640是输入尺寸如果前面统计小目标占比高可以提到 960 或 1280但显存占用会明显上升。--batch 16在 8G 显存下跑 640 输入比较稳显存不够就降到 8 并配合--accumulate。--hyp用 low 增强配置火灾烟雾数据里烟雾本身形态变化大Mosaic 和 HSV 增强有帮助但mixup在烟雾场景下容易把两团烟叠成不真实的形态我一般会关掉或调低。--cache把图像缓存到内存2059 张 640 尺寸大概占几个 G内存够就开能明显减少数据加载瓶颈。训练过程中重点看三个指标mAP0.5、mAP0.5:0.95和每类的precision/recall。烟雾类的 recall 通常比 fire 低因为烟雾边界模糊模型容易漏检淡烟。3.4 训练过程排查loss 不降和 mAP 卡住的常见原因训练启动后如果box_loss和obj_loss在前 10 个 epoch 内没有明显下降先查三件事标签路径是否被正确读取看 dataloader 输出的图像数和标签数是否一致、类别索引是否越界、学习率是否过大。YOLOv5 默认用 SGD初始 lr 0.01如果数据量小可以降到 0.001 并配合 cosine 调度。如果 mAP 在前 50 epoch 涨得正常后面突然掉常见原因是过拟合。2059 张的规模100 epoch 之后验证 loss 开始上升是正常现象可以用--patience 30做早停或者把--epochs降到 80。另一个原因是验证集里有训练集没见过的场景比如训练集全是白天、验证集混了夜间这种要靠补充数据或做域增强解决调参救不回来。4. 推理、部署与效果验证让火灾烟雾检测模型真正跑起来4.1 用 detect.py 做批量推理和结果导出训练完拿到best.pt之后先用detect.py在验证集上跑一遍确认模型行为符合预期。python detect.py \ --weights runs/train/fire_smoke_s/weights/best.pt \ --source datasets/fire_smoke/images/val \ --img 640 \ --conf-thres 0.25 \ --iou-thres 0.45 \ --save-txt \ --project runs/detect \ --name fire_smoke_val--conf-thres 0.25是置信度阈值火灾场景下我一般会调低到 0.2宁可多报不可漏报尤其是烟雾早期阶段。--iou-thres 0.45控制 NMS 的合并程度烟雾框重叠多的时候可以适当调高到 0.5减少误合并。--save-txt把预测结果存成 YOLO 格式方便后面和真值做对比统计。4.2 火灾烟雾检测的评估指标mAP 之外还要看什么mAP 是通用指标但火灾烟雾场景有几个业务相关的指标更值得看。第一是漏检率尤其是小面积烟雾的漏检直接关系到预警时间。第二是误报率云、雾、蒸汽、灰尘在图像上和烟雾相似模型容易误报实际部署时误报太多会导致告警疲劳。第三是推理延迟如果目标是边缘设备部署YOLOv5s 在 640 输入下用 TensorRT 加速后大概能到几十毫秒一帧具体取决于硬件。import numpy as np from pathlib import Path def iou(box1, box2): # box: [x1, y1, x2, y2] xa, ya max(box1[0], box2[0]), max(box1[1], box2[1]) xb, yb min(box1[2], box2[2]), min(box1[3], box2[3]) inter max(0, xb - xa) * max(0, yb - ya) a1 (box1[2] - box1[0]) * (box1[3] - box1[1]) a2 (box2[2] - box2[0]) * (box2[3] - box2[1]) return inter / (a1 a2 - inter 1e-6) # 按面积分桶统计漏检小/中/大 def bucket_by_area(box): area (box[2] - box[0]) * (box[3] - box[1]) if area 32 * 32: return small elif area 96 * 96: return medium return large这段给出 IoU 计算和按面积分桶的思路实际统计时需要把预测框和真值框做匹配按桶计算 recall。小目标桶的 recall 如果明显低于中大型说明模型对小烟雾不敏感可以考虑提高输入分辨率或加 P2 层。4.3 导出 ONNX 和 TensorRT部署前的格式转换如果要在非 Python 环境部署需要把 PyTorch 权重导出成 ONNX 或 TensorRT。python export.py \ --weights runs/train/fire_smoke_s/weights/best.pt \ --include onnx \ --img 640 \ --batch 1 \ --simplify python export.py \ --weights runs/train/fire_smoke_s/weights/best.pt \ --include engine \ --img 640 \ --batch 1 \ --device 0 \ --halfONNX 导出时--simplify会做图优化去掉冗余算子。TensorRT 导出用--half开 FP16速度能提升接近一倍精度损失通常在 1 个点以内但烟雾这种低对比度目标对量化比较敏感建议导出后重新在验证集上跑一遍 mAP 对比。如果掉点超过 2 个就退回 FP32 或做量化感知训练。5. 避坑与排查火灾烟雾检测训练里最容易翻车的五件事5.1 现象训练 loss 正常但推理全是同一类原因通常是类别索引和 names 顺序不一致。标签里 0 是 smokeyaml 里 names 第一个写了 fire模型学到的映射就反了。解决方法是打印一批标签的 class_id 分布和 yaml 里的 names 逐一对齐改完重新训练。这个坑很隐蔽因为 loss 曲线看起来完全正常。5.2 现象验证集 mAP 很高实际视频上漏检严重原因是训练集和验证集来自同一批视频帧间泄漏导致验证指标虚高。解决方法是按视频 ID 或时间戳分组划分确保同一段视频的帧只出现在一边。如果已经训完了重新划分后微调几个 epoch 就能看出真实水平。5.3 现象烟雾框大量重叠NMS 后只剩一个原因是烟雾形态弥散模型对同一团烟输出了多个高置信框。解决方法是在推理时调高--iou-thres到 0.5~0.6或者在训练时增加烟雾类的正样本多样性让模型学会输出更紧凑的框。另一个思路是改用 soft-NMS但 YOLOv5 默认不支持需要改后处理代码。5.4 现象夜间或逆光图像几乎全漏原因是训练集里夜间样本太少模型没学到低照度下的烟雾特征。解决方法是补充夜间数据或者在训练时加亮度、对比度增强。YOLOv5 的 HSV 增强里 V 通道抖动范围默认是 0.4可以调到 0.6 让模型见到更暗的样本。如果数据补不了就要在推理前做图像预处理比如直方图均衡或 CLAHE。5.5 现象TensorRT 导出后精度掉得厉害原因是 FP16 量化对烟雾这种低对比度、低纹理目标不友好激活值动态范围小量化误差被放大。解决方法是先试 FP32 导出确认精度再试 FP16 对比掉点。如果必须用 FP16可以在导出时保留部分敏感层为 FP32或者用校准集做 INT8 量化校准。我一般会在导出后固定跑一遍验证集mAP 掉超过 2 个点就不接受。6. 把 2059 张用到极致小数据集下的增强策略与迭代节奏2059 张在检测任务里属于小数据集直接训 100 epoch 很容易过拟合。我一般会把迭代节奏拆成三步先用强增强跑 50 epoch 拿到基线再用弱增强微调 30 epoch 收敛最后用验证集和一批实际场景图做人工抽检。强增强阶段 Mosaic 概率开到 1.0HSV 三通道都开随机缩放范围 0.5~1.5微调阶段把 Mosaic 关掉让模型适应真实分布。另一个值得做的方向是主动学习。基线模型跑完之后用它去推理一批未标注的火灾烟雾图像把高置信度但和真值不一致的样本挑出来人工复核补充进训练集。2059 张的底子补 200~300 张针对性样本往往比调参涨点更明显。我自己的习惯是每轮迭代固定留 10% 的验证集不动所有增强和补数据只在训练集上做这样每次改动的效果都能干净地对比出来。如果要在边缘设备上跑YOLOv5n 或 YOLOv5s 是更现实的选择输入尺寸可以降到 416 或 320配合 TensorRT FP16树莓派 5 这类设备上也能跑到可用的帧率。代价是小烟雾的召回会下降需要根据实际监控距离和最小预警目标尺寸来权衡。我一般会先在 PC 上把 640 的基线跑稳再逐步降尺寸和换模型每降一档都重新测一遍小目标 recall掉到业务不可接受就停。这套流程走下来从拿到 2059 张带标签数据到拿到一个可部署的火灾烟雾检测器顺利的话两三天踩坑的话一周也正常。关键不是模型多先进而是标签验干净、划分不泄漏、推理阈值按业务调。希望帮到你。本文还有配套的精品资源点击获取
返回列表