
简介面向毕业设计场景的YOLOv5火灾烟雾检测模型包为计算机、人工智能相关专业学生提供了一套可直接运行的检测方案。压缩包内含训练好的权重文件、Python推理脚本、真实火灾示例图片以及预测结果图解压后即可加载权重对图片进行烟雾和火焰识别不必从头训练模型也适合消防预警、智能监控等项目的原型验证。包内共4个文件jpg图片用于效果展示与输入测试py脚本覆盖完整推理流程pt权重是训练产出整体大小仅4.86MB结构紧凑、便于部署。目前已有221人学习下载说明该资源在毕业设计选型中具有一定参考价值。结合描述中的方法流程读者可以从数据准备、模型训练、超参调整到模型评估与预测应用完整理解YOLOv5的落地过程节省搭建时间同时示例图片和可视化结果也方便直接用于论文插图或答辩演示是兼顾学习与实战的轻量级参考资料。1. 毕设选题撞上火灾烟雾检测YOLOv5 这套组合拳凭什么能让你少掉头发如果你正在为毕业设计选方向又不想花三个月从搭建网络开始调参那 yolov5 火灾、烟雾检测模型几乎是最稳妥的选项公开的标注数据、现成的推理代码、能直接出图的预测结果再加上一份训练好的权重跑通一条完整流程只需要一个晚上。我见过太多同学卡在“数据集打标”和“环境装不起来”这两步而这个标题给的示例图片和推理代码恰好把这两块垫平了——你真正要做的是理解它、再把它改造成自己的东西。适合准备毕业设计的同学不是说它能让你“水”过去而是它把最容易劝退的脏活累活先替你干了剩下的工程整合和实验对比才是你拿分数的空间。2. YOLOv5 网络结构与权重选型先看懂黑匣子再动手2.1 从网络结构图里找到三个关键部件避免把模型当黑盒很多同学下载了权重就开始推理直到被老师问“为什么这块烟雾没检出来”才意识到自己连模型长什么样都没看过。yolov5 网络结构图在网上很好找但我建议你自己动手在项目里生成一份models/yolov5s.yaml定义了结构用netron打开yolov5s.onnx也能看到图的拓扑。核心就三块BackboneCSPDarknet负责提取图片特征。CSP 结构把通道分成两路再合并作用是减少重复梯度计算、降低参数量。对应到检测效果上Backbone 的层数直接决定了下采样倍数和感受野大小yolov5s 默认是 640 输入、下采样 32 倍。NeckPANet把 Backbone 不同尺度的特征做融合让小目标远处的烟雾也能掏出足够的语义信息。火灾烟雾往往颜色淡、边缘模糊PANet 的底层特征通道在这里比你想的更重要。HeadDetect 头输出三个尺度的预测张量分别是 80x80、40x40、20x20 的网格。每个网格负责预测边界框、置信度和类别概率。你后处理时看到的大框小框全是从这三个尺寸里解码出来的。理解了结构你就知道为什么默认权重对大火苗效果好、对淡烟雾效果差烟雾目标在图像里占的像素百分比小主要落在小尺度输出层上而小尺度输出层恰恰是训练时最难收敛的部分。所以网上很多“烟雾检测效果差”的抱怨问题不全在模型而在数据里小目标的占比和img_size的选择。2.2 预训练权重怎么选coco 权重、现成火灾权重、还是自己训标题里说的“训练权重”通常是有人已经用火灾烟雾数据集微调过的best.pt可以直接做推理演示。但你在做毕设时一定会遇到一个问题用这个现成权重做出来的结果老师问“这是你训的吗”你怎么接所以我一般建议把它当作“参考结果”而不是“最终交付”。真正要选的预训练权重其实是yolov5s.pt/yolov5m.pt这类基于 COCO 训练出来的通用权重。你用它作为初始权重去微调火灾数据会比从零开始训练收敛快得多。这里有个容易踩的坑最近有人推荐用deim 的 coco 预训练权重那是 DEIM 检测器的权重虽然它在 COCO 上的 mAP 指标比 YOLOv5 高但网络结构完全不同根本不能塞进yolov5的工程里直接微调。看到“预训练权重”四个字第一件事确认它是 YOLOv5 的.pt文件否则加载时就会报结构不匹配的错。选择哪一档权重也有讲究。毕设场景通常用yolov5s就够了单卡 GTX 1660 也能训练推理速度快代码里示例图片那张夜间火灾图也能跑出不错的效果。显存 6G 以下的同学不要一上来就yolov5x那不是放大效果是放大显存溢出报错。我的习惯是先用s跑通全流程最后根据老师“精度不够”的反馈再考虑换m或l。3. 训练自己的火灾烟雾检测模型数据集与超参数怎么设3.1 从 VOC 到 YOLO 格式转换脚本与目录结构标题里给你了示例图片但你要做毕设实验至少得有一份能被train.py读取的数据集。火灾烟雾领域的公开数据集大多是 VOC 格式xml 标注或 COCO 格式json 标注而 YOLOv5 要求的是 YOLO 格式一张图片对应一个.txt每行是 “类别 x_center y_center width height”坐标归一化到 0~1。我把转换脚本里最核心的部分贴出来这是我当时从 VOC 火灾数据集转 YOLO 时用的逻辑import os import xml.etree.ElementTree as ET from PIL import Image def voc2yolo(voc_dir, out_dir, class_list): # 遍历所有 xml 标注文件 for xml_file in os.listdir(voc_dir): if not xml_file.endswith(.xml): continue tree ET.parse(os.path.join(voc_dir, xml_file)) root tree.getroot() # 获取图片尺寸用于坐标归一化 img_w int(root.find(size/width).text) img_h int(root.find(size/height).text) txt_name xml_file.replace(.xml, .txt) with open(os.path.join(out_dir, txt_name), w) as f: for obj in root.iter(object): cls_name obj.find(name).text if cls_name not in class_list: continue cls_id class_list.index(cls_name) box obj.find(bndbox) xmin float(box.find(xmin).text) ymin float(box.find(ymin).text) xmax float(box.find(xmax).text) ymax float(box.find(ymax).text) # 转成 YOLO 中心点 宽高并归一化 x_center (xmin xmax) / 2 / img_w y_center (ymin ymax) / 2 / img_h w (xmax - xmin) / img_w h (ymax - ymin) / img_h f.write(f{cls_id} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}\n)转换后注意一件事class_list的顺序必须和后面data.yaml里的类别顺序完全一致。比如我定义class_list [fire, smoke]那么在data.yaml里names也要写[fire, smoke]。顺序错乱的结果是 smoke 被当成 fire 训练模型不会报错但预测结果会全部错位这是肉眼最难发现的坑。目录结构建议直接用 YOLOv5 喜欢的方式datasets/fire/ images/ train/ fire_001.jpg val/ fire_010.jpg labels/ train/ fire_001.txt val/ fire_010.txt如果你的数据源本身只有图片没有标注那只能手动标注。常见做法是装一个 LabelImg画框导出 VOC 格式再跑上面的转换。做毕设至少要准备 800~1500 张火灾图片、同等数量烟雾图片再按 8:2 分成训练集和验证集。3.2 改超参数文件hyp.scratch.yaml里真正要动的 4 个参数YOLOv5 把超参数放在data/hyps/hyp.scratch.yaml里网上所谓“yolov5超参数调优”的文章一大半是在堆术语其实毕设场景你只要动 4 个地方就够了。打开这个文件你会看到一堆lr0、lrf、momentum、weight_decay、warmup_epochs、box、cls、obj等。你只需要关注这四项lr0: 0.01 # 初始学习率用预训练权重时保持 0.01 即可 lrf: 0.01 # 最终学习率 lr0 * lrf训练末段会降得很低 warmup_epochs: 3.0 # 预热轮数数据量小的时候可以调成 5 cls: 0.5 # 类别损失权重烟雾类别容易漏检可适当调到 0.8参数说明lr0过大会导致 loss 发散过小会导致收敛慢还容易卡在局部最优cls调高会让模型更关注分类正确性对“火和烟混在一起”的场景有用。我见过有人把cls调到 5.0结果训练 loss 飙到十几——因为边界框那几条损失项被压制得完全不学了。训练命令基本长这样python train.py \ --weights yolov5s.pt \ --data datasets/fire/fire.yaml \ --hyp data/hyps/hyp.scratch.yaml \ --epochs 100 \ --batch-size 16 \ --img 640 \ --device 0如果你在data.yaml里已经写好了train:和val:的相对路径那上面的命令就能直接跑。注意--weights参数传的是预训练权重它会自动加载 COCO 预训练过的 Backbone 权重如果你传--weights 那就是完全随机初始化我建议新手别这么做收敛太慢毕设时间经不起等。3.3 训练过程的监控与权重选择别只看最后的 loss 曲线训练跑起来之后你会看到一个runs/train/exp目录里面生成的results.png才是你真正要盯的东西。这个图里有train/box_loss、val/box_loss、mAP_0.5、mAP_0.5:0.95等曲线。我给你的判断标准是验证集 mAP_0.5 在 0.7 以上就算及格0.85 以上就够写进毕设了。mAP_0.5:0.95这个指标比mAP_0.5严格得多烟雾这种边界模糊的目标通常不会太高0.4~0.5 已经算不错不要因为这个数字难看就疯狂调参。训练结束时runs/train/exp/weights/目录里会有last.pt和best.ptbest.pt是验证集上指标最好的那一轮的权重。你的毕设实验报告里应该明确写“本轮训练选用的是第 87 轮的 best.pt”而不是笼统地说“训练好的权重”。此外如果 loss 曲线出现明显震荡大概率是 batch size 太小或者学习率太大先降lr0到 0.003 再试一次比反复改网络结构有效。4. 推理代码与预测结果复现从检测到可视化4.1 最小推理脚本加载权重并输出预测图片拿到训练好的best.pt你要做的第一件事是从官方detect.py跑通推理这其实也是复现标题里“预测结果”最直接的方法。官方给的命令行方式我已经帮你踩过坑所以直接给你一段最小的 Python 推理脚本逻辑和detect.py一致但更精简适合你改成自己的毕设工具函数import torch import cv2 from models.common import DetectMultiBackend from utils.augmentations import letterbox from utils.general import non_max_suppression, scale_boxes from utils.plots import Annotator # 加载模型这里支持 .pt 和 .onnxdevice 设为 CPU/GPU model DetectMultiBackend(runs/train/exp/weights/best.pt, device0 if torch.cuda.is_available() else cpu) model.eval() img0 cv2.imread(data/fire_example.jpg) # 你自己的示例图片 img letterbox(img0, new_shape640, stride32, autoTrue)[0] # 保持长宽比缩放 img img.transpose((2, 0, 1))[::-1] # BGR 转 RGB且 HWC 转 CHW img torch.from_numpy(img).float() / 255.0 img img.unsqueeze(0).to(next(model.parameters()).device) with torch.no_grad(): pred model(img)[0] # 前向推理得到原始预测张量 # 后处理NMS、置信度过滤 pred non_max_suppression(pred, conf_thres0.25, iou_thres0.45) for det in pred: if len(det) 0: continue det[:, :4] scale_boxes(img.shape[2:], det[:, :4], img0.shape) # 把框坐标映射回原图 annotator Annotator(img0, line_width3) for *xyxy, conf, cls in det.cpu().numpy(): label f{model.names[int(cls)]} {conf:.2f} annotator.box_label(xyxy, label, color(0, 0, 255) if int(cls) 0 else (128, 128, 128)) cv2.imwrite(runs/detect/result.jpg, img0) print(预测结果已保存runs/detect/result.jpg)脚本逻辑说明前三行是加载模型和工具库这和你运行的 YOLOv5 工程必须是同一套代码否则函数签名对不上。letterbox那一行是很多人忽略的YOLOv5 训练时会把图片等比缩放到 640 并补灰边如果不做这一步直接cv2.resize模型看到的目标形状会变形导致检测漂移。后处理调用non_max_suppression时注意传入的是pred的第一个维度——model(img)[0]拿到的原始输出是多尺度的三个预测头的拼接不能直接画框必须先 NMS。4.2 后处理参数怎么调置信度阈值和 NMS 的 IOU 阈值标题里“预测结果”之所以看起来效果不错很多时候是调了conf_thres和iou_thres。这两个参数直接写在non_max_suppression里或者用detect.py的--conf-thres和--iou-thres传入。conf_thres置信度阈值低于这个值的框全部丢弃。烟雾检测经常出现“想全”和“想准”的矛盾。你如果只是想给毕设演示几个框好看0.35够用如果你要统计检测率建议0.1——虽然会产生假阳但能看出模型真实召回能力。iou_thresNMS 时的 IoU 阈值同一个目标上叠了多个框时IoU 高于阈值的框会被抑制合并。默认0.45对密集的小目标烟雾可以调到0.3代价是相邻的多个火苗可能被合并成一个框。max_det默认 300单张图检测框数量绝不建议设太小火灾场景里一片区域可能有十几处分散火苗设成 20 会把后面的框全截掉。我的血泪经验是不要在测试集上盲目追求低置信度阈值。烟雾数据集里很多“无烟”背景图conf_thres一旦低于 0.1模型会把云朵、蒸汽、模糊的车尾气全判成烟预测结果图片上密密麻麻的红框老师第一眼就觉得你的模型不可靠。4.3 把预测结果导出成 CSV让毕设报告有数据可写光有预测图片还不够毕设报告需要检测数量、置信度分布、每张图耗时这些量化数据。我给自己的毕设写的导出脚本只做了一件事把每张图每个框的类别、坐标、置信度按行写入 CSV再用 Python 的pandas汇总。核心代码如下import csv, glob with open(runs/detect/predictions.csv, w, newline) as f: writer csv.writer(f) writer.writerow([image, class, confidence, xmin, ymin, xmax, ymax]) for result_txt in glob.glob(runs/detect/*.txt): # 如果你开了 --save-txt img_name result_txt.split(/)[-1] with open(result_txt) as rt: for line in rt: parts line.strip().split() writer.writerow([img_name, model.names[int(parts[0])], parts[5], parts[1], parts[2], parts[3], parts[4]])这里解释一下detect.py的--save-txt输出格式每一行是class_id x_center y_center width height confidence前五个是归一化坐标后面是置信度。如果你要在图上画最好还是用前面那种自定义脚本保存实际框坐标。我习惯把这两个脚本都放在inference/目录下一个出图、一个出表最后毕设的“实验结果”一章就有充分的素材了。5. 避坑/常见问题排查毕业设计最容易翻车的 5 个地方5.1 现象训练到一半 loss 变成 nan或 GPU 显存直接爆掉原因最常见是batch-size太大、img-size太大或者数据集里存在损坏的图片比如全黑图、0 字节文件。YOLOv5 在数据加载时不会检查每张图是否有效一旦出现异常像素值前向传播输出就会变成无穷大。解决把--batch-size按显存减半例如 12GB 显存用--batch-size 8、--img 640先跑一段检查数据集写个脚本遍历所有图片并用cv2.imread读一遍读不到的就删掉。我曾用一条命令排查find datasets/fire/images -name *.jpg -size -1k | head -20找出空文件。5.2 现象训练效果还行但用你的best.pt推理时框的位置偏了很多原因你八成用了官方detect.py但改动过--img-size而模型在训练时用的是 640如果推理时你传了 1280虽然模型能跑但坐标解码的stride对齐方式没变框就会整体左偏或下偏。更隐蔽的原因是你在推理脚本里用了cv2.resize而不是letterbox目标被直接拉伸变形。解决推理时的--img-size严格等于训练时的img-size并且在推理脚本里必须用和utils.augmentations.letterbox完全相同的缩放逻辑。如果你自己写了预处理请用我第 4 章那段脚本里的方式。5.3 现象预测结果只画出“火”烟雾基本全漏检原因烟雾目标小、对比度低模型没有在对应的小目标尺度上得到足够的正样本或者是你的训练集里烟雾框占比太少。网上很多火灾烟雾数据集的标注里烟和火的比例严重失衡。解决最直接的方法是调整数据增强把hyp.scratch.yaml里mosaic: 1.0保持打开它能把多张小图拼成一张增加小目标出现的频率。另一个办法是专门给烟雾类别做复制粘贴增强从训练集里裁剪出烟雾 patch随机贴到无烟背景图上同时生成对应标注框。这个操作能让烟雾检出的召回率提升 8~10 个百分点。5.4 现象加载别人给的权重时报错Checkpoint version mismatch或RuntimeError: size mismatch原因权重文件和当前 YOLOv5 代码版本不一致。YOLOv5 官方仓库更新很频繁早期 6.0 版本之前的权重结构里有model[-1].anchors后来的 6.0/6.2 版本改了变量名和 NMS 头直接加载就会报 ckpt 里的 key 对不上。标题里配的“训练权重”很可能就是基于某个特定版本训的你没有对应的代码版本完全跑不成。解决看权重文件里的ckpt[model].yaml信息或者直接看pt文件里model.stride是否存在。最省事的做法是去权重提供者描述里找他们使用的 YOLOv5 版本号然后用git checkout v6.0或git checkout v7.0切到对应 tag。我自己的习惯是下载权重后第一次加载时就打印model.pt的 key 列表再用这个列表去网上比对版本差异。5.5 现象预测图上的中文标签乱码或者画绿框看不清原因Annotator.box_label默认字体是Arial.ttf不支持中文而且默认颜色是绿色和烟雾背景混在一起。很多同学用“火灾烟雾检测”做类名出图后全是方框。解决在画框之前把工具里的字体换成本地支持中文的字体比如cv2.putText的FONT_HERSHEY_SIMPLEX虽然也不支持中文但你可以把标签改成fire和smoke两个英文单词这是最体面的解决办法。颜色改成用类别区分火用红色(0,0,255)烟用灰色(128,128,128)比默认的绿色醒目得多。这个改动你的毕设答辩时展示预测结果也会更专业。6. 毕设加分的进阶技巧模型轻量化与量化指标统计6.1 把 YOLOv5 权重转成 ONNX 再转 RKNN部署到边缘设备如果你的毕设想往“边缘计算”“嵌入式部署”靠最常用的落地路径是导出 ONNX然后针对瑞芯微 RK3568 这类芯片做量化。命令很成熟python export.py --weights best.pt --include onnx --img 640导出的best.onnx可以直接用官方rknn-toolkit2转成.rknn格式再在板子上用rknn.inference(inputs[img])推理。注意烟雾检测对量化特别敏感我建议用 int8 动态量化不要直接全 int8否则烟雾边界可能被量化误差吃掉。这部分不会在你的毕设里要求跑通整个链路但写一句“模型已通过 ONNX 中间格式完成 RKNN 转换量化后推理速度提升至…”足以说明你掌握部署思维。6.2 用测试集统计 mAP 和做可视化混淆矩阵不要只展示几张效果图。成熟的毕设实验要让数据说话对全测试集跑一遍检测统计每一类的 precision/recall/mAP_0.5再可视化真实边框和预测边框的 IoU 分布。最简单的方法是在验证集上跑python val.py --data fire.yaml --weights best.pt --conf-thres 0.25 --iou-thres 0.5运行后会输出mAP_0.5、mAP_0.5:0.95、每类的 AP 表并在runs/val/exp下生成混淆矩阵confusion_matrix.png和 P/R 曲线。我习惯把这张混淆矩阵单独拉出来重点看“smoke 被误判成 fire”的比例这个数如果超过 20%说明你的训练集里两个类别框的形状太接近需要补充标注更细的边界数据。最后一件事把训练所用的所有超参数、数据集划分比例、设备信息存成一个training_config.yaml放在项目根目录。我毕设交代码时因为没存配置重训一次後指标变了差点被老师以为造假。后来养成了把所有参数固定在文件里、每次实验只改版本号的习惯。希望帮到你。本文还有配套的精品资源点击获取