简介:本资源面向安全监控、火灾预警方向的开发者与深度学习学习者,提供一套基于YOLOv5的烟雾检测完整方案,包含训练好的模型权重与配套数据集,可直接用于实时视频流烟雾识别与预警系统搭建。压缩包共约2000个文件,整体360.52MB,以3688张jpg烟雾图像、3688个txt标注、3687个xml标注为主,另含yaml配置、py脚本、pt权重及Docker部署文件,覆盖数据、训练、推理与容器化部署全流程。已有2356人学习下载,说明该方案在目标检测实践中具备较高参考价值。读者可借助现成模型快速验证烟雾检测效果,结合标注数据理解数据组织方式,并参考脚本与配置文件完成微调、迁移学习或集成到监控、无人机巡检等场景,减少从零搭建的时间成本。
1. 烟雾检测项目落地:从数据集到训好的模型,一次讲透
前阵子有个做安防方向的朋友找我,说他们想在园区里加一路烟雾识别,不想依赖传统烟感,问我有没有能直接跑起来的方案。我翻了一圈,最后给他推的就是这个基于 YOLOv5 的烟雾检测项目——它最大的好处是数据集和训好的权重都打包好了,不用自己从零标注、从零训练,clone 下来改改路径就能出检测结果。这个项目适合两类人:一类是想快速验证烟雾检测可行性的开发者,另一类是拿它当模板做二次开发的学生或工程师。它本质上是一个目标检测任务,把烟雾当成一个类别来框,技术栈就是 PyTorch + YOLOv5 + OpenCV,训练和推理流程都是 YOLOv5 官方那套,改动量很小。下面我按「能干什么 → 怎么跑 → 怎么改」的顺序,把这份资源拆开讲。
2. 项目结构与技术选型:为什么是 YOLOv5 而不是别的
2.1 目录结构长什么样
拿到这份资源,解压后你会看到一个典型的 YOLOv5 工程目录,外加两个关键文件夹:datasets/和weights/。我先把整体结构列一下,方便你对照自己的解压结果:
smoke-detection-yolov5/ ├── data/ │ ├── smoke.yaml # 数据集配置文件 │ └── hyps/ # 超参数配置 ├── datasets/ │ ├── images/ │ │ ├── train/ # 训练集图片 │ │ └── val/ # 验证集图片 │ └── labels/ │ ├── train/ # 训练集标签(YOLO格式txt) │ └── val/ ├── models/ │ ├── yolov5s.yaml # 模型结构定义 │ └── hub/ # 可选,hub配置 ├── weights/ │ └── best.pt # 训好的烟雾检测权重 ├── utils/ # 通用工具(NMS、绘图、指标) ├── train.py # 训练入口 ├── detect.py # 推理入口 ├── val.py # 验证入口 └── requirements.txt # 依赖清单这个结构不是随便摆的。data/smoke.yaml是数据集的总开关,里面写清楚训练集、验证集路径和类别名;datasets/下按 YOLO 标准格式组织,图片和标签同名不同后缀;weights/best.pt是已经训好的权重,直接拿来做推理。你如果只想跑通,其实只需要关心smoke.yaml、best.pt和detect.py这三个文件。
2.2 为什么选 YOLOv5 做烟雾检测
烟雾检测这个任务有几个特点:目标边界模糊、形态变化大、背景干扰多。传统方法用颜色阈值或者纹理特征,稍微换个光照就翻车。YOLOv5 作为单阶段检测器,在速度和精度之间平衡得比较好,而且它的 anchor 机制对不规则形状的目标容忍度更高。更关键的是,YOLOv5 的工程化做得好,数据加载、增强、训练、推理、导出全链路都有现成脚本,你不需要自己写 DataLoader 和 NMS。
常见做法是选 YOLOv5s 作为基线,因为烟雾检测通常部署在边缘设备上,s 版本参数量小、推理快。如果你对精度要求更高,可以换成 YOLOv5m 或 l,但推理速度会下降。这个项目默认用的是 s 版本,权重文件大概十几兆,在普通显卡上跑实时检测没问题。
2.3 数据集的组织方式
数据集是这份资源的核心价值之一。烟雾检测的公开数据不多,自己标注又费时,所以拿到一份整理好的数据集能省很多事。YOLO 格式的标签是每行一个目标,格式为class_id x_center y_center width height,坐标都归一化到 0~1 之间。我建议你先打开datasets/labels/train/下的一个 txt 看看,确认类别 id 和smoke.yaml里的names对得上。
smoke.yaml的内容一般长这样:
# data/smoke.yaml train: ../datasets/images/train val: ../datasets/images/val nc: 1 names: ['smoke']这里nc是类别数,烟雾检测通常就一类,所以是 1。names里写类别名,顺序要和标签里的 class_id 一致。如果你后面要加别的类别,比如同时检测火焰,就把nc改成 2,names里加一个fire,然后重新训练。
注意:路径写的是相对路径,实际运行时是相对于
train.py所在目录解析的。如果你把数据集挪到别的地方,记得同步改这里,否则会报「No labels found」之类的错。
3. 环境搭建与推理跑通:先看到框,再谈训练
3.1 依赖安装与版本选择
YOLOv5 对 PyTorch 版本有一定要求,太新或太旧都可能出问题。我一般会先建一个干净的 conda 环境,然后按requirements.txt装。常见做法是:
# 创建环境,Python 用 3.8 或 3.9 比较稳 conda create -n smoke python=3.9 -y conda activate smoke # 安装 PyTorch,根据你的 CUDA 版本选对应命令 # 这里以 CUDA 11.3 为例 pip install torch==1.12.1+cu113 torchvision==0.13.1+cu113 \ --extra-index-url https://download.pytorch.org/whl/cu113 # 安装项目依赖 pip install -r requirements.txt如果你没有 GPU,把 torch 换成 CPU 版本也能跑推理,只是慢一些。requirements.txt里通常包含opencv-python、numpy、tqdm、matplotlib、pyyaml这些,都是推理和训练的基础库。
提示:装完 torch 后,进 Python 执行
import torch; print(torch.cuda.is_available()),返回 True 才说明 GPU 可用。如果返回 False,先检查 CUDA 和驱动版本,别急着往下跑。
3.2 用训好的权重做推理
推理是最快能看到效果的一步。项目里已经给了weights/best.pt,你只需要准备一张带烟雾的图片或者一段视频,然后执行:
# 对单张图片推理 python detect.py \ --weights weights/best.pt \ --source datasets/images/val/ \ --img-size 640 \ --conf-thres 0.25 \ --project runs/detect \ --name smoke_test参数说明一下:--weights指定权重路径;--source可以是单张图、文件夹、视频文件,甚至摄像头编号(比如0表示默认摄像头);--img-size是推理分辨率,640 是 YOLOv5 的默认值;--conf-thres是置信度阈值,低于这个值的框会被过滤掉;--project和--name决定结果保存位置,跑完会在runs/detect/smoke_test/下看到带框的图片。
跑完之后,打开输出目录,你应该能看到烟雾区域被框出来,框上方有类别名和置信度。如果一张框都没有,先别怀疑模型,把--conf-thres降到 0.1 试试,或者换一张烟雾更明显的图。
3.3 推理代码里发生了什么
detect.py的主流程其实不复杂,我把它拆成几个关键步骤:
# detect.py 核心流程(简化版) import torch from models.experimental import attempt_load from utils.datasets import LoadImages from utils.general import non_max_suppression, scale_coords # 1. 加载模型 device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') model = attempt_load('weights/best.pt', map_location=device) model.eval() # 2. 加载图片 dataset = LoadImages('datasets/images/val/', img_size=640) # 3. 逐张推理 for path, img, im0s, vid_cap in dataset: img = torch.from_numpy(img).to(device).float() / 255.0 img = img.unsqueeze(0) # 增加 batch 维度 # 前向传播 pred = model(img)[0] # NMS 去重 pred = non_max_suppression(pred, conf_thres=0.25, iou_thres=0.45) # 坐标还原到原图尺寸 for det in pred: if det is not None and len(det): det[:, :4] = scale_coords(img.shape[2:], det[:, :4], im0s.shape).round() # 绘图并保存...这里有几个点值得注意:attempt_load会同时加载模型结构和权重;non_max_suppression负责把重叠的框去掉,iou_thres控制去重力度;scale_coords把 640 分辨率下的框映射回原图尺寸,否则你看到的框位置会偏。如果你要改推理逻辑,比如只保留置信度最高的一个框,就在 NMS 之后加一层筛选。
4. 训练自己的数据:从 smoke.yaml 到 best.pt
4.1 训练命令与参数解读
如果你手头有新的烟雾数据,或者想微调模型,训练流程也是现成的。假设你已经把图片和标签按前面的结构放好了,执行:
python train.py \ --data data/smoke.yaml \ --cfg models/yolov5s.yaml \ --weights weights/best.pt \ --epochs 100 \ --batch-size 16 \ --img-size 640 \ --project runs/train \ --name smoke_finetune--data指向数据集配置;--cfg是模型结构,如果你想换更大的模型就改这里;--weights可以填预训练权重,也可以填''从零开始;--epochs是训练轮数,微调一般 50~100 够用;--batch-size根据显存调整,16 是 8G 显存比较稳的值;--img-size要和推理时保持一致。
训练过程中,终端会打印每一轮的 loss、precision、recall、mAP 等指标。重点关注mAP@0.5,它反映模型在 IoU 阈值 0.5 下的平均精度。如果训练几轮后 mAP 一直不涨,可能是学习率太大或者数据有问题。
4.2 数据增强与超参数
YOLOv5 默认开启了 Mosaic、HSV 增强、随机翻转等。烟雾检测里,HSV 增强对光照变化很有帮助,Mosaic 能提升小目标检测能力。这些配置在data/hyps/hyp.scratch-low.yaml里,你可以按需调整。比如烟雾颜色偏灰白,可以把hsv_v调大一点,让模型对亮度变化更鲁棒。
常见做法是先用默认超参跑一遍,看指标曲线,再针对性调。如果验证集 loss 比训练集高很多,说明过拟合了,可以加大weight_decay或者减少模型复杂度。
4.3 训练结果怎么看
训练结束后,runs/train/smoke_finetune/下会有weights/best.pt和weights/last.pt,还有results.csv和混淆矩阵图。best.pt是验证集上表现最好的权重,通常拿它做推理。results.csv里记录了每轮的指标,你可以用 pandas 画个曲线看看收敛情况。
如果 mAP 在 0.6 以上,对于烟雾检测来说已经能用了。要是低于 0.4,先检查标签有没有标错,再看数据集是不是太少。烟雾检测的标注一致性很重要,不同人标的框松紧不一样,模型会学懵。
5. 避坑与常见问题:那些我踩过的雷
5.1 报错「No labels found」怎么办
现象:训练一开始就报这个错,然后退出。原因通常是smoke.yaml里的路径不对,或者标签文件夹名字不是labels。YOLOv5 要求图片和标签目录结构对称,比如images/train对应labels/train。解决方法是检查路径,确保标签文件和图片文件同名,只是后缀从.jpg变成.txt。
5.2 推理结果框位置偏移
现象:框画出来了,但位置明显不对,偏上或偏左。原因多半是scale_coords用的原图尺寸不对,或者图片在加载时被 resize 过但没记录原始尺寸。解决方法是确认LoadImages返回的im0s是原始图像,不要在里面做额外裁剪。如果自己改了数据加载逻辑,记得同步改坐标还原。
5.3 显存不够导致训练中断
现象:训练到一半报 CUDA out of memory。原因是batch-size太大或者img-size太高。解决方法是把--batch-size降到 8 或 4,或者把--img-size降到 416。YOLOv5 支持梯度累积,你可以用小 batch 配合--accumulate来模拟大 batch。
5.4 模型把白云当成烟雾
现象:推理时天空的白云被框成 smoke。原因是训练集里缺少负样本,模型没学会区分白色不规则物体和烟雾。解决方法是在数据集里加一些不含烟雾但含白云的图片,标签为空文件,让模型学习背景。或者提高--conf-thres,把低置信度的误检过滤掉。
5.5 训练 loss 不下降
现象:loss 一直震荡或者不降。原因可能是学习率太大、数据标签有问题、或者预训练权重和模型结构不匹配。解决方法是先用小学习率跑几轮,确认 loss 有下降趋势;再检查标签格式,确保没有越界坐标;最后确认--weights和--cfg是配套的,别拿 yolov5m 的权重去加载 yolov5s 的结构。
6. 二次开发与改造思路:把烟雾检测嵌进你的系统
6.1 导出 ONNX 做跨平台部署
如果你要把模型部署到 C++ 或者边缘设备上,PyTorch 权重不够方便,常见做法是导出 ONNX:
python export.py \ --weights weights/best.pt \ --include onnx \ --img-size 640 \ --batch-size 1导出后会生成best.onnx,你可以用 ONNX Runtime 或者 TensorRT 加载。注意导出时的img-size要和推理时一致,否则输入维度对不上。ONNX 模型不包含 NMS,需要自己在后处理里实现,或者用 ONNX Runtime 的扩展算子。
6.2 改成视频流实时检测
detect.py默认支持视频和摄像头,把--source改成0就是调用本地摄像头。但如果你想嵌入自己的视频处理管线,可以把推理部分抽成一个函数:
def detect_frame(model, frame, conf_thres=0.25, iou_thres=0.45): """输入一帧 BGR 图像,返回带框的图像和检测结果""" img = letterbox(frame, 640, stride=32)[0] img = img[:, :, ::-1].transpose(2, 0, 1) # BGR -> RGB img = np.ascontiguousarray(img) img = torch.from_numpy(img).float() / 255.0 img = img.unsqueeze(0) pred = model(img)[0] pred = non_max_suppression(pred, conf_thres, iou_thres) results = [] for det in pred: if det is not None and len(det): det[:, :4] = scale_coords(img.shape[2:], det[:, :4], frame.shape).round() for *xyxy, conf, cls in det: results.append((xyxy, conf.item(), int(cls))) # 画框... return frame, results这样你就可以在自己的循环里逐帧调用,配合多线程或异步推理提升吞吐。注意letterbox的填充要和训练时一致,否则精度会掉。
6.3 增加类别或替换骨干网络
如果你想同时检测烟雾和火焰,改smoke.yaml的nc和names,然后重新训练。标签里火焰的 class_id 设为 1。如果想换更轻量的骨干,可以把models/yolov5s.yaml里的backbone换成 MobileNet 或 ShuffleNet,但需要自己写适配层,改动量较大。我一般建议先在 s 版本上调通,再考虑换骨干。
6.4 一个我常犯的错
早期我改完smoke.yaml直接跑训练,忘了把--weights换成'',结果模型加载了旧权重的类别数,训练时直接报维度不匹配。从那以后我每次改类别数,都强制走一遍「检查 yaml → 确认权重 → 小批量试跑」的流程,确认没问题再开完整训练。希望这个习惯能帮到你,少走点弯路。
本文还有配套的精品资源,点击获取