简介:这份资源面向希望入门目标检测与火灾图像识别的Python开发者,提供基于yolov5框架的完整实践方案,覆盖从数据准备、模型训练到推理预测的全流程。压缩包共19个文件,约30.45MB,包含pt预训练权重、yaml配置文件、ipynb训练笔记、png与jpg结果图表、mp4演示视频及gif动图等,可直观查看训练曲线与检测效果。资源内附火灾与非火灾图像数据集说明,并给出训练脚本、数据加载与预测脚本的组织方式,便于读者理解YOLO网格预测、边界框与类别概率等核心机制。已有239人学习下载,适合具备一定Python与深度学习基础、想通过真实项目掌握yolov5训练调参、数据集构建与性能优化的读者参考。
1. 火灾图像识别为什么总在烟雾刚起时掉链子
凌晨两点,仓库角落的纸箱开始冒烟,监控画面里只有一小团灰白雾状区域,值班人员没注意到,等明火起来已经晚了。这类场景正是火灾图像识别要解决的核心问题:在火焰还没成型、只有烟雾或微弱火光时就把异常框出来。Python 基于 YOLOv5 实现的火灾图像识别,本质是把火灾检测当成目标检测任务,用标注好的火焰、烟雾图片训练模型,再部署到摄像头或图片流上做实时推理。它适合做安防、园区、仓储、森林防火方向的工程师,也适合想拿一个完整项目练手 YOLOv5 训练自己数据集的人。标题里提到的源代码、模型文件、数据集,对应的就是一套能跑通训练和推理的最小闭环,而不是只给一个权重文件让你猜怎么用。
2. 火灾数据集怎么准备:从原始图片到 YOLO 标签
2.1 火灾图像识别的类别定义与数据来源
做火灾检测第一步不是写代码,而是想清楚你要检测什么。常见做法是把类别分成两类:fire(明火)和 smoke(烟雾),有些场景会再加一类 spark(火花)或 flame_small(小火苗)。类别越细,标注成本越高,但误报会少一些。我一般建议新手先用 fire 和 smoke 两类跑通,等模型能稳定识别再考虑细分。
数据来源上,公开火灾数据集通常包含几千到上万张图片,覆盖室内、室外、白天、夜间、近距离和远距离。但公开数据有个通病:烟雾图片偏少,明火图片偏多,模型容易把橙色物体误判成火。所以拿到数据后先做一次类别分布统计,如果 smoke 占比低于 30%,要么补采烟雾图片,要么在训练时给 smoke 类更高权重。
图片格式统一成 jpg 或 png,分辨率不用强求一致,YOLOv5 训练时会自动缩放到 640。但要注意,如果原图特别大(比如 4000×3000),建议先缩到 1280 宽再标注,否则标注框坐标容易算错。
2.2 用 labelImg 标注并转成 YOLO 格式
标注工具用 labelImg 就够了,安装命令如下:
pip install labelImg labelImg打开后设置图片目录和保存目录,格式选 YOLO。每张图至少框出所有火焰和烟雾区域,框要贴紧目标边缘,不要留太多背景。标注完成后每张图对应一个 txt 文件,每行格式是:
class_id x_center y_center width height其中坐标都是归一化到 0 到 1 之间的浮点数。class_id 从 0 开始,0 代表 fire,1 代表 smoke。
标注完检查一遍,常见错误是框超出图片边界或者宽高为 0。可以用下面这段脚本快速检查:
import os import glob label_dir = "labels" for txt in glob.glob(os.path.join(label_dir, "*.txt")): with open(txt, "r") as f: for line in f: parts = line.strip().split() if len(parts) != 5: print(f"格式错误: {txt} -> {line}") continue cls, x, y, w, h = map(float, parts) if not (0 <= x <= 1 and 0 <= y <= 1 and 0 < w <= 1 and 0 < h <= 1): print(f"坐标越界: {txt} -> {line}")这段脚本遍历所有标签文件,检查每行是否有 5 个字段,以及中心点和宽高是否在合理范围内。如果输出大量越界记录,说明标注时图片被缩放但标签没同步更新,需要重新导出。
2.3 划分训练集、验证集和测试集
YOLOv5 要求数据目录结构如下:
dataset/ images/ train/ val/ labels/ train/ val/常见做法是按 8:1:1 划分训练、验证、测试。但火灾数据有个坑:同一个视频截出来的连续帧不能同时出现在训练集和验证集,否则验证指标会虚高。正确做法是按视频或按场景划分,同一场景的图片只进一个集合。
划分脚本可以这样写:
import os import random import shutil img_dir = "all_images" label_dir = "all_labels" out_dir = "dataset" train_ratio, val_ratio = 0.8, 0.1 os.makedirs(f"{out_dir}/images/train", exist_ok=True) os.makedirs(f"{out_dir}/images/val", exist_ok=True) os.makedirs(f"{out_dir}/labels/train", exist_ok=True) os.makedirs(f"{out_dir}/labels/val", exist_ok=True) imgs = [f for f in os.listdir(img_dir) if f.endswith((".jpg", ".png"))] random.shuffle(imgs) n_train = int(len(imgs) * train_ratio) n_val = int(len(imgs) * val_ratio) for i, img in enumerate(imgs): name = os.path.splitext(img)[0] label = name + ".txt" if i < n_train: split = "train" elif i < n_train + n_val: split = "val" else: continue shutil.copy(os.path.join(img_dir, img), f"{out_dir}/images/{split}/{img}") shutil.copy(os.path.join(label_dir, label), f"{out_dir}/labels/{split}/{label}")这段代码把全部图片打乱后按比例复制到对应目录,标签文件同名复制。注意测试集这里没单独建目录,实际项目里可以再留 10% 做最终测试,训练时完全不碰。
3. YOLOv5 训练火灾检测模型:配置文件与关键参数
3.1 准备 YOLOv5 代码和火灾数据配置文件
先把 YOLOv5 代码拉下来,安装依赖:
git clone https://github.com/ultralytics/yolov5 cd yolov5 pip install -r requirements.txt然后新建一个火灾数据配置文件 fire_data.yaml,放在 yolov5/data 目录下:
path: ../dataset train: images/train val: images/val nc: 2 names: ['fire', 'smoke']path 是数据集根目录,train 和 val 是相对路径。nc 是类别数,names 按 class_id 顺序写。如果只检测火焰,nc 改成 1,names 只留 fire。
模型配置文件不用自己写,直接用 yolov5s.yaml 就行。火灾检测通常不需要太大模型,yolov5s 在速度和精度之间比较平衡,树莓派 4B 或 RK3568 这类边缘设备也能跑。如果服务器端追求精度,可以换 yolov5m 或 yolov5l,但推理速度会明显下降。
3.2 训练命令与超参数怎么设
最小训练命令:
python train.py --img 640 --batch 16 --epochs 100 --data data/fire_data.yaml --weights yolov5s.pt --project runs/fire --name exp1参数含义:img 是输入尺寸,640 是默认值,显存不够可以降到 416 或 320;batch 是批大小,16 适合 8G 显存,12G 可以上 32;epochs 是训练轮数,火灾数据一般 100 到 300 轮够用;weights 是预训练权重,用 yolov5s.pt 做迁移学习比从零训练收敛快很多。
如果烟雾样本少,可以在 train.py 里加类别权重,或者用 --hyp 指定超参数文件。常见做法是复制 data/hyps/hyp.scratch-low.yaml,把 smoke 对应的损失权重调高。但更简单的办法是数据增强时对烟雾图片多做几次复制。
训练过程中看几个指标:box_loss 和 obj_loss 是否稳定下降,mAP@0.5 是否在涨。如果 mAP 卡在 0.3 以下,大概率是标注有问题或者学习率太大。可以先用 --epochs 10 跑一遍小实验,确认 loss 在降再开长训练。
3.3 训练结果怎么看:mAP、召回率和误报
训练结束后 runs/fire/exp1 目录下有 results.csv 和 weights/best.pt。results.csv 里记录了每轮的指标,重点看:
- metrics/mAP_0.5:IoU 阈值 0.5 时的平均精度,火灾检测一般能到 0.7 以上算可用。
- metrics/recall:召回率,火灾场景宁可误报也不能漏报,所以召回率比精度更重要。
- val/box_loss:验证集损失,如果训练损失降但验证损失涨,说明过拟合。
可以用下面脚本画一下 mAP 曲线:
import pandas as pd import matplotlib.pyplot as plt df = pd.read_csv("runs/fire/exp1/results.csv") df.columns = df.columns.str.strip() plt.plot(df["epoch"], df["metrics/mAP_0.5"], label="mAP@0.5") plt.plot(df["epoch"], df["metrics/recall"], label="recall") plt.xlabel("epoch") plt.ylabel("value") plt.legend() plt.savefig("fire_train_curve.png")这段代码读取训练日志,画出 mAP 和召回率随轮次的变化。如果召回率明显低于 mAP,说明漏检多,需要补烟雾或小火样本。
4. 推理与部署:从单张图片到视频流
4.1 用 detect.py 跑通单张图片和视频
训练完先用官方推理脚本验证:
python detect.py --weights runs/fire/exp1/weights/best.pt --source test_fire.jpg --img 640 --conf 0.4source 可以是单张图片、图片目录、视频文件,甚至摄像头编号。conf 是置信度阈值,火灾检测建议设 0.3 到 0.5,太低会误报,太高会漏报。推理结果默认保存在 runs/detect/exp 下。
如果要在 Python 代码里调用模型,可以这样写:
import torch model = torch.hub.load("ultralytics/yolov5", "custom", path="runs/fire/exp1/weights/best.pt") model.conf = 0.4 model.iou = 0.45 results = model("test_fire.jpg") results.print() results.save("output")model.conf 是置信度阈值,model.iou 是 NMS 的 IoU 阈值。火灾检测里如果两个框重叠度高,NMS 会保留一个,iou 设 0.45 比较稳。results.print() 输出检测到的类别和坐标,results.save() 保存带框图片。
4.2 视频流推理的帧率与跳帧策略
直接对视频每一帧跑 YOLOv5,在普通 CPU 上可能只有 2 到 5 FPS,达不到实时。常见做法是跳帧推理,比如每 3 帧检测一次,中间帧复用上一次结果。代码示例:
import cv2 import torch model = torch.hub.load("ultralytics/yolov5", "custom", path="best.pt") model.conf = 0.4 cap = cv2.VideoCapture("fire_video.mp4") frame_id = 0 last_boxes = [] while True: ret, frame = cap.read() if not ret: break frame_id += 1 if frame_id % 3 == 0: results = model(frame) last_boxes = results.xyxy[0].cpu().numpy() for box in last_boxes: x1, y1, x2, y2, conf, cls = box cv2.rectangle(frame, (int(x1), int(y1)), (int(x2), int(y2)), (0, 0, 255), 2) cv2.imshow("fire", frame) if cv2.waitKey(1) & 0xFF == ord("q"): break cap.release() cv2.destroyAllWindows()这段代码每 3 帧推理一次,其余帧画上一次的框。跳帧数根据设备性能调,GPU 上可以每帧都跑,树莓派上可能得跳到 5 或 10。注意跳帧太多会导致快速移动的火焰框位置滞后,需要根据场景权衡。
4.3 模型导出与边缘设备部署注意点
如果要在树莓派 4B 或 RK3568 上部署,先把 PyTorch 模型导出成 ONNX:
python export.py --weights best.pt --include onnx --img 640导出后可以用 onnxruntime 推理,速度比 PyTorch 快不少。RK3568 这类带 NPU 的芯片,还需要把 ONNX 转成 RKNN 格式,用 rknn-toolkit2 转换。转换时注意输入尺寸和归一化参数要和训练时一致,否则精度会掉。
树莓派 4B 上跑 YOLOv5s ONNX,640 输入大概 3 到 5 FPS,320 输入能到 8 到 10 FPS。如果帧率不够,优先降输入尺寸,而不是换更小的模型,因为火灾检测对小火焰敏感,模型太小召回率会明显下降。
5. 火灾识别项目避坑与排查:那些让我返工的细节
5.1 模型把夕阳和灯光误判成火焰
现象:模型在傍晚或路灯场景下频繁框出 fire,置信度还不低。
原因:训练数据里明火图片大多是橙红色,而夕阳、暖色灯光颜色分布接近,模型学到了颜色捷径而不是火焰纹理。
解决:补采黄昏、灯光、橙色物体负样本,标注时这些图片不放任何框,让模型学会区分。也可以在数据增强里加 HSV 色调扰动,降低对颜色的依赖。
5.2 烟雾漏检严重,mAP 虚高但实际不可用
现象:验证集 mAP 有 0.75,但实际视频里烟雾刚起时完全没框。
原因:烟雾样本太少,且标注时只框了浓烟,薄烟没标。模型只学会了浓烟特征。
解决:重新检查标注,把薄烟、远距离烟雾都框上。如果数据不够,用烟雾合成工具在正常图片上叠加烟雾纹理,扩充样本。训练时把 smoke 类损失权重调高 1.5 到 2 倍。
5.3 训练 loss 不降或变成 nan
现象:训练几轮后 box_loss 变成 nan,或者一直不降。
原因:学习率太大、标注坐标越界、或者某张图片损坏。
解决:先用第 2 章里的检查脚本过一遍标签,确认没有越界。然后把学习率降到 0.001 或 0.0005 再试。如果还不行,检查图片是否有 0 字节文件,用 PIL 打开每张图确认能正常读取。
5.4 推理时框位置偏移或框太大
现象:检测到的框比实际火焰大一圈,或者位置偏了。
原因:训练时图片被 letterbox 缩放,推理时如果没做同样处理,坐标会对不上。
解决:用官方 detect.py 或 torch.hub 加载模型时,预处理是自动的,不要自己写 resize。如果自己写推理代码,必须复现 letterbox:先等比例缩放到 640,再填充灰边,推理后再把坐标映射回原图。
5.5 模型文件在另一台机器上加载失败
现象:best.pt 在训练机上能跑,拷到部署机上报错。
原因:PyTorch 版本不一致,或者 torch.hub 加载时联网下载 YOLOv5 代码失败。
解决:部署机上装和训练机相同版本的 PyTorch 和 ultralytics/yolov5。如果无法联网,把 YOLOv5 代码目录一起拷过去,用 torch.load 直接加载模型,或者导出 ONNX 后用 onnxruntime 推理,摆脱对 YOLOv5 代码的依赖。
6. 把火灾识别模型压到边缘设备还能保住召回率的几个手法
边缘设备部署火灾识别,最大的矛盾是算力有限但召回率不能降。我自己的习惯是先在服务器上把模型训到召回率 0.85 以上,再做压缩,压缩后重新在验证集上测一遍,如果召回率掉超过 5 个百分点就回退。
第一个手法是输入尺寸分级。不要一上来就用 640,先试 416 和 320,看召回率掉多少。火灾场景里远距离小火对分辨率敏感,如果 320 下漏检明显,就保持 416,用跳帧换帧率。
第二个手法是通道剪枝。YOLOv5 里可以用 torch-pruning 对 Conv 层做 L1 剪枝,剪掉 20% 到 30% 通道,再微调 10 轮。剪枝后模型体积能小三分之一,推理快 20% 左右。但剪枝比例超过 40% 后召回率会断崖下跌,别贪。
第三个手法是量化。ONNX 转 TensorRT 时用 FP16 量化,速度能翻倍,精度几乎不掉。INT8 量化需要校准集,火灾数据校准集里必须包含烟雾和明火样本,否则量化后烟雾检测会明显变差。
验证压缩效果时,不要只看 mAP,要单独统计 fire 和 smoke 的召回率。下面这段脚本可以按类别算召回率:
import numpy as np def recall_per_class(preds, gts, num_classes=2, iou_thres=0.5): recalls = [] for c in range(num_classes): tp, fn = 0, 0 for img_id in gts: gt_boxes = [b for b in gts[img_id] if b[4] == c] pred_boxes = [b for b in preds.get(img_id, []) if b[4] == c] matched = set() for gt in gt_boxes: best_iou = 0 best_idx = -1 for i, pred in enumerate(pred_boxes): if i in matched: continue iou = compute_iou(gt[:4], pred[:4]) if iou > best_iou: best_iou = iou best_idx = i if best_iou >= iou_thres: tp += 1 matched.add(best_idx) else: fn += 1 recalls.append(tp / (tp + fn + 1e-6)) return recalls这段代码按类别统计召回率,compute_iou 需要自己实现或从 YOLOv5 utils 里导入。跑完后如果 smoke 召回率比 fire 低很多,说明压缩对烟雾特征损伤大,需要把烟雾样本加进量化校准集重新导出。
最后一个习惯:每次换设备或换模型格式,都拿同一段测试视频跑一遍,人工数一下漏检和误报。指标好看不代表现场能用,我吃过好几次 mAP 0.8 但实际视频里烟雾漏一半的亏。希望帮到你。
本文还有配套的精品资源,点击获取