YOLOv9火焰识别检测系统:源码、教程、预训练模型与评估曲线
简介本资源为基于YOLOv9的火焰识别检测系统完整项目包面向计算机、人工智能、自动化等专业在校学生、教师及企业开发人员可用于毕业设计、课程实践或目标检测入门进阶。包内共192个文件以83个Python源码、30个YAML配置、33张JPG与9张PNG图像、3个PT权重文件为主另含训练日志、评估指标曲线与数据集说明压缩包约62.53MB。项目提供训练好的模型与详细运行教程覆盖环境配置、数据集准备、参数修改、模型训练与推理测试全流程读者可据此复现火焰识别效果并参考评估曲线判断模型性能。已有324人学习适合希望快速上手YOLOv9目标检测、掌握从数据到部署完整链路的开发者。1. 火焰识别为什么值得用 YOLOv9 重做一遍厂区监控里最先报警的往往不是温度传感器而是摄像头画面里那一小簇橘红色像素。等烟感探头触发时火势通常已经过了最佳处置窗口。这就是火焰识别检测系统存在的意义在可见光或红外画面里用目标检测模型把火焰从背景中框出来抢在传感器之前给出预警。而 YOLOv9 之所以被拿来重做这件事核心在于它的可编程梯度信息机制缓解了深层网络的信息丢失问题小目标火焰的召回率比前几代有明显提升。这套方案适合两类人一是手里已有监控视频流、想快速搭一套火焰检测原型的安防从业者二是正在做课程设计或技术验证、需要一份能跑通的 Python 源码加训练好模型加评估曲线的开发者。标题里提到的源码、运行教程、预训练模型和评估指标曲线本质上是一套完整的落地包接下来我会按「先跑通再调优」的顺序把它拆开讲清楚。2. 把环境跑通从零到第一帧火焰框2.1 依赖选型与版本对齐火焰检测这套东西环境装不对后面全是玄学。YOLOv9 的官方实现依赖 PyTorch而 PyTorch 又和 CUDA 驱动版本强绑定。我一般建议先用nvidia-smi看驱动支持的 CUDA 上限再去 PyTorch 官网找对应的安装命令不要直接pip install torch了事。常见做法是建一个独立虚拟环境把 Python 锁在 3.9 或 3.10这两个版本和大多数检测库的兼容性最稳。# 创建虚拟环境Python 版本建议 3.9/3.10 conda create -n fire_yolov9 python3.10 -y conda activate fire_yolov9 # 先装 PyTorchCUDA 版本按 nvidia-smi 结果选这里以 cu118 为例 pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118 # 再装检测常用依赖 pip install opencv-python numpy pyyaml tqdm matplotlib pandas seaborn这段命令的逻辑是先隔离环境避免污染全局包再装 PyTorch 这个最大的依赖最后补上推理和评估要用的库。参数上唯一需要你改的是cu118如果你的驱动只支持到 CUDA 11.6就换成cu116如果是纯 CPU 推理把--index-url那行去掉直接pip install torch torchvision即可但训练会慢到让你怀疑人生。2.2 目录结构与权重放置拿到源码包后别急着运行。先看清楚目录约定YOLOv9 类项目通常长这样data/放数据集配置models/放网络结构定义weights/放训练好的.pt文件runs/是训练和推理的输出目录。预训练模型一般是一个几十到上百 MB 的.pt文件把它放进weights/下命名保持和配置文件里引用的一致。# check_env.py 快速自检脚本 import torch import cv2 import os print(PyTorch:, torch.__version__) print(CUDA available:, torch.cuda.is_available()) print(OpenCV:, cv2.__version__) # 检查权重文件是否存在 weight_path weights/fire_best.pt if os.path.exists(weight_path): size_mb os.path.getsize(weight_path) / 1024 / 1024 print(f权重文件存在大小 {size_mb:.1f} MB) else: print(权重文件缺失请确认路径)这个自检脚本解决的是「我到底装没装对」的问题。torch.cuda.is_available()返回 False 就说明 CUDA 没配好后面训练会退化成 CPU 模式。权重文件大小也要看一眼如果只有几 KB多半是下载中断的残包这种坑我踩过不止一次。2.3 用预训练模型跑通单张图片推理环境确认无误后先用一张火焰图片验证整条链路。推理脚本的核心是加载模型、读图、前处理、前向传播、后处理画框。# infer_image.py 单图推理 import cv2 import torch from models.experimental import attempt_load from utils.general import non_max_suppression, scale_boxes from utils.augmentations import letterbox # 加载模型 device torch.device(cuda if torch.cuda.is_available() else cpu) model attempt_load(weights/fire_best.pt, devicedevice) model.eval() # 读图并做 letterbox 缩放保持长宽比 img0 cv2.imread(test_fire.jpg) img letterbox(img0, new_shape640, autoTrue)[0] img img[:, :, ::-1].transpose(2, 0, 1) # BGR 转 RGBHWC 转 CHW img torch.from_numpy(img).float().to(device) / 255.0 img img.unsqueeze(0) # 推理 with torch.no_grad(): pred model(img)[0] pred non_max_suppression(pred, conf_thres0.25, iou_thres0.45) # 画框 for det in pred: if det is not None and len(det): det[:, :4] scale_boxes(img.shape[2:], det[:, :4], img0.shape).round() for *xyxy, conf, cls in det: x1, y1, x2, y2 map(int, xyxy) cv2.rectangle(img0, (x1, y1), (x2, y2), (0, 0, 255), 2) cv2.putText(img0, ffire {conf:.2f}, (x1, y1 - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 0, 255), 2) cv2.imwrite(result.jpg, img0) print(推理完成结果已保存)逻辑说明letterbox是 YOLO 系列的标准前处理把图片缩放到 640 并填充灰边保证不拉伸变形。non_max_suppression做非极大值抑制conf_thres0.25是置信度阈值iou_thres0.45是重叠框合并阈值。这两个参数是火焰检测里最需要调的后面会专门讲。scale_boxes把检测框坐标映射回原图尺寸否则你画出来的框位置全是错的。3. 训练自己的火焰数据标注、配置与启动3.1 火焰数据集标注的四个要点预训练模型能识别通用火焰但你的场景如果是特定工业环境比如高炉口、焊接火花、红色警示灯误报率会很高。这时候必须用自己的数据微调。标注用 LabelImg 或 CVAT 都行输出 YOLO 格式的.txt每行是类别 x_center y_center width height全部归一化到 0 到 1。标注火焰有四个要点第一火焰边缘模糊框要稍微放宽一点把火苗外焰也包进去第二反光、夕阳、橙色衣物这些负样本一定要标出来或者单独作为背景图加入否则模型会把它们当火焰第三小火焰目标要单独检查如果标注框小于 8 像素训练时容易被忽略第四训练集和验证集按 8:2 分且要保证同一段视频的帧不要同时出现在两边否则验证指标会虚高。3.2 数据配置文件与模型配置YOLOv9 需要一个数据 YAML 文件告诉它去哪里找图片和标签。# data/fire_data.yaml path: ./datasets/fire # 数据集根目录 train: images/train # 训练图片相对路径 val: images/val # 验证图片相对路径 nc: 1 # 类别数火焰检测通常只有 fire 一类 names: [fire] # 类别名称nc是类别数如果你还要区分烟雾就改成 2names里加smoke。path建议用相对路径换机器时不容易翻车。模型配置文件一般选yolov9-c.yaml或yolov9-e.yaml前者速度快适合实时监控后者精度高适合离线分析。我一般先用 c 版本跑通再根据指标决定要不要换 e。3.3 启动训练与关键参数# 单卡训练命令 python train_dual.py \ --workers 8 \ --device 0 \ --batch-size 16 \ --data data/fire_data.yaml \ --img 640 \ --cfg models/detect/yolov9-c.yaml \ --weights weights/fire_best.pt \ --name fire_train \ --epochs 100 \ --patience 20 \ --lr0 0.01 \ --lrf 0.01参数说明--batch-size 16是显存够的情况下的常用值显存不够就降到 8 或 4--img 640是输入分辨率火焰小目标多可以提到 1280但显存和速度会成倍变化--epochs 100配合--patience 20表示 20 轮指标不提升就早停避免过拟合--lr0是初始学习率微调预训练模型时建议降到 0.001 到 0.01 之间从零训练才用 0.01 以上。训练日志和权重会保存在runs/train/fire_train/下results.csv就是评估指标曲线的原始数据。4. 评估指标曲线怎么看mAP、召回与误报的三角关系4.1 训练产出的曲线文件说明训练结束后runs/train/fire_train/目录下会有一组曲线图常见的有results.png、confusion_matrix.png、PR_curve.png、F1_curve.png。results.png里包含 box_loss、cls_loss、mAP0.5、mAP0.5:0.95、precision、recall 随 epoch 的变化。火焰检测最该盯的是 recall 和 mAP0.5因为漏报一场火比误报几次代价大得多。指标含义火焰场景建议值mAP0.5IoU 0.5 时的平均精度0.85 以上可用Recall召回率漏报的反面0.90 以上优先Precision精确率误报的反面0.70 以上可接受F1精确与召回的调和0.80 以上算均衡4.2 用 Python 画出自己的评估曲线如果源码包里的曲线不够用可以自己从results.csv重画方便对比不同实验。# plot_metrics.py 绘制训练指标曲线 import pandas as pd import matplotlib.pyplot as plt df pd.read_csv(runs/train/fire_train/results.csv) df.columns df.columns.str.strip() # 列名常有空格先清理 fig, axes plt.subplots(1, 3, figsize(15, 4)) axes[0].plot(df[epoch], df[metrics/mAP_0.5], labelmAP0.5) axes[0].plot(df[epoch], df[metrics/mAP_0.5:0.95], labelmAP0.5:0.95) axes[0].set_title(mAP) axes[0].legend() axes[1].plot(df[epoch], df[metrics/precision], labelprecision) axes[1].plot(df[epoch], df[metrics/recall], labelrecall) axes[1].set_title(Precision Recall) axes[1].legend() axes[2].plot(df[epoch], df[train/box_loss], labelbox_loss) axes[2].plot(df[epoch], df[train/cls_loss], labelcls_loss) axes[2].set_title(Loss) axes[2].legend() plt.tight_layout() plt.savefig(my_metrics.png, dpi150) print(曲线已保存)这段代码的关键是df.columns.str.strip()YOLO 输出的 CSV 列名经常带前后空格不清理会直接 KeyError。画出来的三张图分别看精度、召回和损失收敛情况。如果 box_loss 一直不降多半是标注框有问题如果 recall 高但 precision 低说明误报多需要加负样本。4.3 阈值调整对火焰检测的实际影响置信度阈值不是固定的。安防场景我一般把conf_thres从 0.25 降到 0.15牺牲一点精确率换更高的召回率宁可多报也不能漏报。但降到 0.1 以下夕阳和灯光就会开始频繁触发。IoU 阈值iou_thres在火焰密集时调到 0.5 到 0.6避免相邻火苗被合并成一个框。这两个参数没有标准答案拿一段包含火焰和干扰物的测试视频跑几组不同阈值看误报和漏报的平衡点在哪里。5. 避坑与排查火焰检测落地时最容易翻车的五件事5.1 现象模型在验证集上 mAP 很高实际视频里几乎不报警原因通常是数据泄漏。同一段视频的相邻帧被分到了训练集和验证集模型记住了背景而不是火焰特征。解决方法是按视频片段划分数据集而不是按帧随机划分。另外检查验证集里是不是火焰占比过高真实监控画面里火焰可能只占几帧验证集分布要和实际一致。5.2 现象推理速度只有几帧每秒完全达不到实时原因可能是用了yolov9-e这种大模型或者输入分辨率设成了 1280又或者没开半精度。解决方法是换yolov9-c或更小的模型把--img降到 640推理时加model.half()用 FP16。如果还不行检查是不是在 CPU 上跑torch.cuda.is_available()必须是 True。5.3 现象白天正常晚上或红外画面下检测全乱原因是训练数据缺少夜间和红外样本。可见光模型直接用在红外画面上颜色分布完全不同特征提取层会失效。解决办法是分别采集白天、夜间、红外三类数据训练时做混合或者干脆训练两个模型按时间段切换。不要指望一个模型通吃所有光照条件。5.4 现象训练到一半 loss 变成 NaN原因通常是学习率太大或者标注文件里有非法值。先检查--lr0是不是设成了 0.1 这种大值微调时用 0.001 到 0.01。再检查标注.txt里有没有坐标超过 1 或者负数写个脚本遍历一遍所有标签文件把异常行打印出来。5.5 现象评估曲线里 mAP 波动剧烈像心电图原因可能是 batch size 太小导致梯度噪声大或者数据集本身太小。解决办法是把--batch-size提到 16 或 32同时加数据增强比如 mosaic、mixup、随机翻转。如果数据量实在少考虑用预训练模型冻结主干先训几轮再解冻微调。6. 把火焰检测推到可用的最后一公里模型训完、曲线好看不等于系统能用。我一般会做三件事把最后一公里补上。第一件是滑动窗口投票单帧检测容易抖动连续 5 帧里有 3 帧以上检出火焰才触发报警能压掉大部分瞬时误报。第二件是区域掩膜监控画面里有些区域永远不可能有火焰比如天空、水面把这些区域涂黑再送进模型直接消除对应误报。第三件是报警分级置信度 0.8 以上直接推紧急告警0.4 到 0.8 之间只记录日志人工复核避免半夜被夕阳叫醒。# vote_filter.py 滑动窗口投票示例 from collections import deque class FireVoter: def __init__(self, window5, threshold3): self.window window # 窗口帧数 self.threshold threshold # 触发所需检出帧数 self.history deque(maxlenwindow) def update(self, detected): # detected: 当前帧是否检出火焰 self.history.append(1 if detected else 0) if len(self.history) self.window: return False return sum(self.history) self.threshold voter FireVoter(window5, threshold3) # 每帧推理后调用 voter.update(len(det) 0) 即可这个投票器的参数window和threshold要根据帧率调。25 帧每秒的视频window 取 5 大约对应 0.2 秒响应够快如果摄像头只有 5 帧每秒window 要拉到 10 以上才有意义。threshold 一般取 window 的六成左右太高会漏报太低等于没过滤。验证方法上我习惯用一段包含火焰、夕阳、红色车辆、焊接火花的混合视频跑一遍统计误报次数和漏报次数画成混淆矩阵。只有误报和漏报都降到可接受范围这套火焰识别检测系统才算真正落地。最后一个习惯每次改完参数把results.csv和推理视频的统计结果一起存档不然过两周你根本想不起来哪组参数最好。希望帮到你。本文还有配套的精品资源点击获取