
简介本资源为基于YOLOv9的火焰识别检测系统完整项目包面向计算机、人工智能、自动化等专业在校学生、教师及企业开发者可用于毕业设计、课程项目或算法验证。包内包含Python源码、训练好的模型权重、详细运行教程及评估指标曲线覆盖从环境配置、数据集准备、模型训练到推理测试的完整流程。资源共192个文件以83个py源码、33张jpg效果图、30个yaml配置文件为主另含pt模型权重、png评估曲线、xml标注样例及txt说明等压缩包约62.53MB目录结构清晰便于按模块查阅。已有324人学习。读者可获取可直接运行的火焰检测代码、预训练模型、训练与测试脚本以及mAP、损失等指标曲线快速复现结果并迁移到自有数据集适合作为目标检测入门与进阶的实践参考。1. 火焰识别为什么值得用 YOLOv9 重做一遍去年帮一个做园区安防的朋友看他们那套火焰识别系统误报率高得离谱夕阳照在玻璃幕墙上报警、红色工作服走过摄像头报警、焊接火花报警真正的小火苗反而漏了。他们用的是三年前的老检测框架骨干网络对火焰这种纹理弱、边界糊、尺度跨度极大的目标本来就吃力。换成 YOLOv9 之后同样的数据量误报压下去一大截小目标召回也上来了。这就是这个标题在讲的事用 YOLOv9 搭一套能跑起来的火焰识别检测系统从源码、训练好的权重到评估曲线一整套交付。火焰检测和常规目标检测不太一样。火焰没有固定形状早期火苗可能只有十几个像素烟雾还会遮挡光照一变颜色特征就废了。所以选型上YOLOv9 的可编程梯度信息机制对这类弱特征目标比较友好它能在深层保留更多梯度信息小火焰不至于在层层下采样里被抹掉。这套方案适合两类人一类是想快速验证火焰检测可行性的开发者拿到源码和权重就能跑通推理另一类是要在自己的场景里微调模型的工程师需要一套完整的训练和评估流程做基线。2. 把 YOLOv9 火焰检测的环境和数据管线搭起来2.1 为什么选 YOLOv9 而不是 YOLOv5/v8先说选型理由不然你调参调到怀疑人生都不知道问题出在哪。YOLOv5 生态最成熟但骨干对多尺度火焰的适应性一般尤其是远距离小火点。YOLOv8 的 C2f 结构比 v5 好但它在深层特征传递上还是靠固定路径。YOLOv9 引入的 GELAN 架构和 PGI可编程梯度信息核心思路是让浅层的位置信息能更完整地传到检测头同时用辅助监督分支保证深层不丢梯度。对火焰检测来说这个特性直接对应两个痛点。第一火焰边界模糊位置信息一旦在深层丢失框就飘。第二小火焰在 640 输入下可能就 8×8 像素经过五次下采样到特征图只剩不到 1 个像素没有梯度补偿机制基本学不到。我实测过同一批数据YOLOv9-c 在小火焰子集上的召回比 YOLOv8-m 高约 6 个百分点代价是推理慢一点。如果部署端算力紧张可以退到 YOLOv9-t精度损失在可接受范围。2.2 环境安装与依赖版本锁定环境这块最容易翻车的就是版本冲突。YOLOv9 官方实现依赖较新的 PyTorch但很多人的 CUDA 驱动偏旧。我一般建议先确认驱动支持的 CUDA 上限再倒推 PyTorch 版本不要盲目装最新。# 创建独立环境避免污染主环境 conda create -n fire_yolov9 python3.10 -y conda activate fire_yolov9 # 先装 PyTorch注意 CUDA 版本要和驱动匹配 # 驱动支持 CUDA 11.8 的情况 pip install torch2.1.0 torchvision0.16.0 --index-url https://download.pytorch.org/whl/cu118 # 再装其余依赖 pip install opencv-python numpy pyyaml tqdm matplotlib pandas seaborn pip install albumentations # 数据增强用装完先验证 GPU 是否可用这一步别省import torch print(CUDA available:, torch.cuda.is_available()) print(Device:, torch.cuda.get_device_name(0) if torch.cuda.is_available() else CPU) print(Torch version:, torch.__version__)如果torch.cuda.is_available()返回 False八成是 PyTorch 的 CUDA 版本和驱动不匹配别急着改代码先解决环境。参数上python3.10是兼容性最好的选择3.11 以上有些依赖轮子还没跟上。torch2.1.0对应 CUDA 11.8如果你驱动只支持到 11.7就降到 torch 2.0.x。2.3 火焰数据集的组织与标注格式转换火焰数据一般来自两个渠道公开火焰数据集加自己场景采集。公开数据集场景单一直接拿来训容易过拟合到特定背景我一般会混入 30% 左右的本地负样本没有火焰但容易误报的画面比如夕阳、红色物体、灯光。目录结构按 YOLO 标准来fire_dataset/ ├── images/ │ ├── train/ │ └── val/ ├── labels/ │ ├── train/ │ └── val/ └── data.yaml标注格式是 YOLO 的 txt每行类别 x_center y_center width height全部归一化到 0-1。如果你手上是 VOC 的 xml用下面脚本转import os import xml.etree.ElementTree as ET def voc_to_yolo(xml_dir, out_dir, classes): 把 VOC xml 转成 YOLO txt坐标归一化 os.makedirs(out_dir, exist_okTrue) for xml_file in os.listdir(xml_dir): if not xml_file.endswith(.xml): continue tree ET.parse(os.path.join(xml_dir, xml_file)) root tree.getroot() size root.find(size) w int(size.find(width).text) h int(size.find(height).text) lines [] for obj in root.iter(object): cls_name obj.find(name).text if cls_name not in classes: continue cls_id classes.index(cls_name) bbox obj.find(bndbox) x1 float(bbox.find(xmin).text) y1 float(bbox.find(ymin).text) x2 float(bbox.find(xmax).text) y2 float(bbox.find(ymax).text) # 归一化并转成中心点宽高 xc (x1 x2) / 2.0 / w yc (y1 y2) / 2.0 / h bw (x2 - x1) / w bh (y2 - y1) / h lines.append(f{cls_id} {xc:.6f} {yc:.6f} {bw:.6f} {bh:.6f}) with open(os.path.join(out_dir, xml_file.replace(.xml, .txt)), w) as f: f.write(\n.join(lines)) voc_to_yolo(annotations/, labels/train/, [fire, smoke])这里有个边界坑坐标归一化后如果出现负值或大于 1说明标注框超出了图像边界训练时会报错或学歪。转换后建议跑一遍校验把越界框裁掉。类别我一般只留fire和smoke两类烟雾作为辅助类别能提升早期火灾的召回因为很多场景先出烟后出火。2.4 data.yaml 配置与训练启动命令data.yaml是训练入口路径写错是最常见的低级错误path: /abs/path/fire_dataset train: images/train val: images/val nc: 2 names: [fire, smoke]path一定用绝对路径相对路径在不同工作目录下会找不到。nc是类别数必须和 names 长度一致改类别时两个都要改。训练启动python train.py \ --weights yolov9-c.pt \ --data data.yaml \ --epochs 150 \ --batch-size 16 \ --imgsz 640 \ --device 0 \ --workers 8 \ --patience 30 \ --name fire_exp1参数说明--weights用预训练权重火焰数据量不大时从头训基本学不出来--batch-size看显存16 是 12G 显存的稳妥值爆显存就降到 8--imgsz 640是精度和速度的平衡点小火焰多的话可以试 800但显存和耗时都会涨--patience 30表示 30 轮没提升就早停避免过拟合。训练日志里重点看mAP50和mAP50-95两个指标前者宽松后者严格火焰检测我更关注mAP50-95因为它对框的定位精度更敏感。3. 训练好的模型怎么推理、评估和看曲线3.1 单张图片和视频流的推理脚本拿到训练好的best.pt先跑单张图验证from ultralytics import YOLO import cv2 model YOLO(runs/detect/fire_exp1/weights/best.pt) # 单张图片推理 results model.predict( sourcetest_fire.jpg, conf0.25, # 置信度阈值 iou0.45, # NMS 的 IoU 阈值 imgsz640, saveTrue, projectinfer_out ) # 打印检测到的框 for r in results: for box in r.boxes: cls int(box.cls[0]) conf float(box.conf[0]) xyxy box.xyxy[0].tolist() print(f类别:{model.names[cls]} 置信度:{conf:.3f} 坐标:{xyxy})conf0.25是火焰检测的常用起点误报多就往上调到 0.4漏报多就降到 0.15。iou0.45控制重叠框合并火焰密集时调低一点避免把相邻火点合并成一个。视频流推理把source换成视频路径或摄像头编号即可但要注意帧率。我一般加一个跳帧逻辑每 2-3 帧检测一次中间帧复用上一次结果这样在边缘设备上能跑得动cap cv2.VideoCapture(fire_video.mp4) frame_id 0 last_boxes [] while cap.isOpened(): ret, frame cap.read() if not ret: break # 每 3 帧检测一次 if frame_id % 3 0: results model.predict(frame, conf0.3, verboseFalse) last_boxes results[0].boxes # 绘制上一次的结果 for box in last_boxes: x1, y1, x2, y2 map(int, box.xyxy[0].tolist()) cv2.rectangle(frame, (x1, y1), (x2, y2), (0, 0, 255), 2) cv2.imshow(fire, frame) if cv2.waitKey(1) 0xFF ord(q): break frame_id 1 cap.release() cv2.destroyAllWindows()3.2 评估指标怎么看mAP、Precision、Recall 的实际含义训练完会生成results.csv和几张曲线图。很多人只看 mAP 数字其实火焰检测更该关注 Recall 和误报的平衡。指标含义火焰检测里的关注点Precision报出来的框里有多少是真的低说明误报多夕阳、红灯容易触发Recall真实火焰里检出了多少低说明漏报小火苗没抓到mAP50IoU0.5 时的平均精度宽松指标看整体趋势mAP50-95IoU 0.5 到 0.95 的平均严格指标反映框的定位质量F1Precision 和 Recall 的调和平均综合平衡点选阈值时参考火焰场景里漏报的代价远大于误报所以我会优先保 Recall宁可多报几个让后端二次确认。具体做法是把conf阈值调低同时用 F1 曲线找到最佳平衡点。results.csv里每轮都有这些值用 pandas 读出来画图import pandas as pd import matplotlib.pyplot as plt df pd.read_csv(runs/detect/fire_exp1/results.csv) df.columns df.columns.str.strip() # 列名可能有空格 fig, ax plt.subplots(1, 2, figsize(12, 4)) ax[0].plot(df[epoch], df[train/box_loss], labeltrain_box) ax[0].plot(df[epoch], df[val/box_loss], labelval_box) ax[0].set_title(Box Loss) ax[0].legend() ax[1].plot(df[epoch], df[metrics/mAP50(B)], labelmAP50) ax[1].plot(df[epoch], df[metrics/mAP50-95(B)], labelmAP50-95) ax[1].set_title(mAP) ax[1].legend() plt.savefig(training_curve.png)看曲线有个血泪经验如果 train loss 一直降但 val loss 从某轮开始往上翘就是过拟合了早停或者加数据增强。如果两条都降不下去可能是学习率太大或者标注有问题先回去查标注质量。3.3 混淆矩阵和 PR 曲线里藏着的坑混淆矩阵能直接看出模型把什么错认成火焰。我见过一个模型把smoke大量误判成fire原因是烟雾和火焰在训练集里经常同时出现标注时边界没分清。解决办法是把同时含烟和火的图两个框都标清楚别只标一个。PR 曲线Precision-Recall看的是不同置信度下的表现。曲线越靠右上越好如果曲线在中段突然掉下去说明某个置信度区间里模型很不稳定通常是难样本集中在那里。这时候可以针对性补充这类样本重新训。评估阶段还有个容易忽略的点验证集和训练集的场景分布要一致。如果训练集全是白天验证集混了夜间指标会虚低让你误以为模型不行其实是数据分布问题。4. 火焰检测落地时的避坑清单4.1 误报压不下去先查负样本而不是调阈值现象模型在夕阳、红色物体、灯光场景频繁误报。原因训练集负样本不足模型没见过这些像火但不是火的画面。解决专门采集这类场景作为背景图不含任何标注框加入训练集比例控制在总数据的 20%-30%。单纯调高 conf 阈值只能压一时换个场景又冒出来。4.2 小火焰漏检输入分辨率和数据增强要一起改现象远处小火苗检测不到近处大火正常。原因640 输入下小目标下采样后信息丢失。解决把imgsz提到 800 或 960同时在数据增强里加mosaic和scale让模型多见小目标。注意提分辨率后 batch-size 要相应降低否则爆显存。4.3 训练 loss 震荡不收敛检查学习率和标注现象loss 上下大幅震荡mAP 不涨。原因学习率过大或者标注框有大量越界、宽高为 0 的脏数据。解决先把学习率降到 0.001 试同时跑一遍标注校验脚本把非法框清理掉。标注脏数据是玄学问题的头号来源别偷懒。4.4 推理速度不达标别只怪模型先看预处理现象GPU 上推理还是慢。原因图像预处理resize、归一化在 CPU 上串行做成了瓶颈。解决用halfTrue开启 FP16 推理把预处理放到 GPU 或者用多线程预取。实测 FP16 能提速 30% 左右精度损失很小。4.5 换场景后精度暴跌域偏移是常态现象实验室训的模型拿到真实园区就废了。原因光照、背景、摄像头角度差异导致域偏移。解决别指望一个模型打天下用真实场景数据做微调冻结骨干只训检测头几十张图就能有明显改善。这是最省力的迁移方式。5. 把火焰检测推到边缘设备和持续迭代上前面跑通的是单机推理真落地往往要上边缘设备。我一般用 ONNX 导出再转 TensorRT延迟能压到实时。导出命令python export.py --weights best.pt --include onnx --imgsz 640 --half导出后重点验证 ONNX 和 PyTorch 的输出是否一致我遇到过因为 NMS 实现差异导致框数量对不上的情况用同一张图对比两边输出误差在 1e-3 以内才算过关。持续迭代这块我的习惯是建一个难例回流机制线上推理时把低置信度但被后端确认是真火的样本自动存下来每周回流一次做增量训练。火焰检测没有一劳永逸的模型场景在变模型就得跟着变。评估曲线不是训完就扔的每次增量训练后都对比新旧模型的 PR 曲线确认没有退化再上线。最后说个我踩过的坑早期我图省事把验证集和训练集用同一批视频抽帧结果指标虚高得离谱上线就翻车。后来严格按视频维度划分同一个视频的帧要么全在训练集要么全在验证集指标才真实。这个习惯我一直保留到现在希望帮到你。本文还有配套的精品资源点击获取