简介:本资源面向计算机视觉学习者与行为识别方向开发者,提供基于YOLOv5-6.0框架训练完成的吸烟检测模型,可用于公共场所、校园或工地等场景下的吸烟行为自动识别。包内包含YOLOv5m与YOLOv5s两个已训练权重,目标类别为smoke,在数千张吸烟数据上训练,准确率超过90%,并附有PR曲线、loss曲线等训练过程记录,便于评估模型表现与复现实验。资源共373个文件,以jpg样本图、txt标注与配置、yaml参数文件、py源码及pyc缓存为主,另有pt权重、png曲线图、md说明文档、sh脚本与Dockerfile等,压缩包约128.03MB,目录结构完整,覆盖数据、配置、训练与部署环节。目前已有2392人学习下载,适合希望快速上手吸烟检测、进行二次训练或对比不同模型精度的读者参考使用。
1. 吸烟行为检测为什么选 YOLOv5:从几千张图到 90% 准确率的落地判断
吸烟检测这个需求,真正做过的人都知道难点不在“能不能识别烟”,而在“烟太小、遮挡多、姿态杂”。一根烟在 640 分辨率下往往只占十几个像素,手一挡、头一低、光线一暗,传统检测器直接漏检。这份yolov5-6.0-smoking_detect.zip给的是一个已经训练好的 YOLOv5 吸烟检测模型包,目标类别只有一个:smoke,在几千张吸烟场景数据上训练完成,提供 YOLOv5m 和 YOLOv5s 两个权重,官方给出的准确率在 90% 以上,并附带了 PR 曲线、loss 曲线和results.csv训练日志。它适合两类人:一类是想直接拿权重做行为检测验证的工程同学,另一类是想拿这套数据配置和训练曲线做二次训练、换场景微调的开发者。压缩包里能看到events.out.tfevents事件文件、results.csv、Dockerfile以及yolov5-6.0.iml,说明这是一份带完整训练痕迹的工程目录,不是只丢一个.pt的空壳。下面我按“先搞懂它是什么 → 怎么跑起来 → 怎么换成自己的数据 → 坑在哪”的顺序拆一遍,能抄的地方直接给命令和参数。
2. 拆开压缩包:YOLOv5-6.0 目录结构与两个权重的选型逻辑
2.1 从文件清单判断这份资源处于什么状态
拿到一个检测模型包,我第一件事不是急着推理,而是先看目录里有没有训练日志和事件文件,因为这决定了它是“可复现的训练工程”还是“只能推理的黑匣子”。这份包里出现了events.out.tfevents.1652415975.yuhui.7369.0、events.out.tfevents.1652411824.yuhui.6446.0两个 TensorBoard 事件文件,还有results.csv,这三个东西基本能确认:训练过程被完整记录过,results.csv里逐 epoch 存着损失和 mAP,事件文件则能在 TensorBoard 里还原曲线。Dockerfile和.dockerignore的存在说明作者考虑过环境封装,.gitattributes、.gitignore是 YOLOv5 官方仓库自带的版本控制文件,yolov5-6.0.iml是 IDE 工程标识。换句话说,这是一份从官方 YOLOv5-6.0 仓库派生出来的训练工程,而不是精简过的推理 demo。
理解这一点很关键:如果你只是想跑推理,只需要权重和detect.py;但如果你想复现训练或者微调,就得保留data/、models/、utils/这些目录,缺一个都会在训练时报 import 错误。常见做法是先把整个包解压到一个干净的 Python 环境里,确认requirements.txt能装上,再决定是直接推理还是重训。
2.2 YOLOv5s 与 YOLOv5m 怎么选:精度、速度和显存的三角权衡
包里给了两个权重,对应 YOLOv5 的两个不同规模。选哪个不是拍脑袋,得看你的部署端和帧率要求。下面这张表是我在实际项目里常用的判断依据:
| 模型 | 参数量级别 | 推理速度 | 小目标(烟头)召回 | 适用场景 |
|---|---|---|---|---|
| YOLOv5s | 小 | 快 | 一般 | 边缘设备、树莓派、实时视频流 |
| YOLOv5m | 中 | 中等 | 较好 | 服务器 GPU、抓拍、对漏检敏感 |
吸烟检测里“烟”属于典型小目标,YOLOv5s 在 640 输入下对细长烟体的特征提取偏弱,容易在遮挡时漏检;YOLOv5m 的 neck 部分特征融合更充分,召回通常更稳。但代价是显存和延迟上升。我的建议是:如果跑在 Jetson、树莓派这类边缘端,先用 s 版本压帧率,再考虑量化;如果是后端服务器做抓拍分析,直接上 m 版本,别为了省那点算力牺牲召回。这里要提醒一句,摘要里说的“准确率 90% 以上”是在它自己的验证集上得到的,换到你的摄像头角度、光照和分辨率,数字一定会掉,别把 90% 当成通用承诺。
2.3 用命令行把推理先跑通
在动手改任何东西之前,先用官方detect.py把权重跑通,确认环境和权重都没问题。假设你已经解压并进入yolov5-6.0目录:
# 安装依赖,建议用虚拟环境,避免污染系统 Python python -m venv venv source venv/bin/activate # Windows 用 venv\Scripts\activate pip install -r requirements.txt # 用 YOLOv5m 权重对单张图片推理 python detect.py \ --weights weights/smoking_m.pt \ --source test_images/ \ --img-size 640 \ --conf-thres 0.25 \ --iou-thres 0.45 \ --device 0这段命令里几个参数值得说清楚。--weights指向训练好的吸烟权重,路径按你实际解压位置改;--source可以是单张图、文件夹、视频文件甚至0(摄像头);--img-size 640是推理分辨率,必须和训练时一致,否则小目标尺度对不上,召回会明显下降;--conf-thres 0.25是置信度阈值,吸烟检测建议先设低一点观察召回,再往上调;--iou-thres 0.45控制 NMS 合并,烟体细长时别设太低,否则相邻框会被误合并;--device 0指定第一块 GPU,没有 GPU 就写cpu。跑完结果默认落在runs/detect/exp/下,先看几张可视化图,确认框的位置和置信度是否合理,再进入下一步。
3. 用 results.csv 和 PR 曲线判断模型到底能不能用
3.1 results.csv 里该看哪几列
results.csv是 YOLOv5 每个 epoch 结束后写出的训练指标表,列名通常是epoch、train/box_loss、train/obj_loss、train/cls_loss、metrics/precision、metrics/recall、metrics/mAP_0.5、metrics/mAP_0.5:0.95、val/box_loss等。判断一个检测模型能不能用,我一般盯三件事:验证集 mAP_0.5 是否稳定收敛、precision 和 recall 是否严重失衡、验证损失有没有反弹。用 pandas 几行就能把关键列拉出来看趋势:
import pandas as pd # 读取训练日志,注意列名可能带空格,先 strip 一下 df = pd.read_csv("results.csv") df.columns = [c.strip() for c in df.columns] # 打印最后 10 个 epoch 的关键指标 cols = ["epoch", "metrics/precision", "metrics/recall", "metrics/mAP_0.5", "metrics/mAP_0.5:0.95"] print(df[cols].tail(10)) # 找出 mAP_0.5 最高的那个 epoch,对应最佳权重 best = df.loc[df["metrics/mAP_0.5"].idxmax()] print("最佳 epoch:", best["epoch"], "mAP_0.5:", best["metrics/mAP_0.5"])逻辑很直接:idxmax()找到 mAP_0.5 峰值所在行,那个 epoch 对应的权重通常就是best.pt。如果最后几个 epoch 的 mAP 还在缓慢上升,说明训练可能没完全收敛,可以考虑加 epoch 继续;如果验证损失开始上升而训练损失还在降,那就是过拟合信号,得加数据或加增强。参数上要注意,results.csv的列名在不同 YOLOv5 小版本里略有差异,读进来先strip()去空格,避免 KeyError。
3.2 PR 曲线和 loss 曲线怎么读
PR 曲线(Precision-Recall Curve)反映的是在不同置信度阈值下 precision 和 recall 的权衡。曲线越靠近右上角,模型综合性能越好;曲线下的面积就是 AP,单类别检测里 AP 基本等于 mAP_0.5。看 PR 曲线时重点看曲线在中高 recall 区间是否还能维持较高 precision——如果 recall 一过 0.6 precision 就断崖式下跌,说明模型在“宁可多报也不漏报”的场景下会引入大量误检。loss 曲线则分三块:box_loss 管定位、obj_loss 管目标存在性、cls_loss 管分类。吸烟检测只有一类,cls_loss 通常很快降下去,真正要盯的是 obj_loss 和 box_loss 是否平稳收敛。如果 obj_loss 震荡厉害,多半是正负样本不均衡或者 anchor 不匹配,这时候可以考虑调--anchor或换更贴合烟体长宽比的 anchor。
3.3 用 TensorBoard 还原训练过程
包里那两个events.out.tfevents文件就是给 TensorBoard 用的,直接指向日志目录即可:
# 指向包含 events 文件的目录,默认端口 6006 tensorboard --logdir runs/train --port 6006启动后浏览器打开对应地址,就能看到 loss、mAP、学习率等曲线。这一步的价值在于:当你怀疑“90% 准确率”是不是某个 epoch 的偶然峰值时,曲线能告诉你它是稳定达到还是抖上去的。如果曲线在中后期明显震荡,那这个权重在实际场景里的鲁棒性就要打问号,建议优先用best.pt而不是last.pt。
4. 换成自己的吸烟数据:从标注到重训的完整链路
4.1 数据标注格式与目录组织
YOLOv5 用的是 YOLO 格式标注:每张图对应一个同名.txt,每行class x_center y_center width height,坐标都是归一化到 0~1 的值。吸烟检测只有smoke一类,所以 class id 恒为 0。目录结构建议这样组织:
dataset/ images/ train/ val/ labels/ train/ val/然后写一个smoke.yaml放在data/下:
# data/smoke.yaml path: ../dataset # 数据集根目录 train: images/train # 训练集图片相对路径 val: images/val # 验证集图片相对路径 nc: 1 # 类别数,吸烟检测只有一类 names: ['smoke'] # 类别名,顺序必须和标注里的 class id 对应这里最容易翻车的是nc和names对不上:如果标注里出现了 class id 1 而nc写的 1(只有 0 类),训练会直接报索引越界。标注完先用脚本统计一遍每张图的类别 id 分布,确认没有越界再开训。
4.2 基于预训练权重做迁移学习
有了自己的数据,不建议从零训,直接用这份包里的吸烟权重做初始化,收敛快很多:
# 基于已有吸烟权重微调,注意 --data 指向自己的 yaml python train.py \ --weights weights/smoking_m.pt \ --data data/smoke.yaml \ --img-size 640 \ --batch-size 16 \ --epochs 100 \ --device 0 \ --hyp data/hyps/hyp.scratch-low.yaml参数逐个说:--weights用已有权重而不是yolov5m.pt,因为前者已经学到了烟体特征,微调起点更高;--img-size 640和推理保持一致;--batch-size 16要按显存调,显存不够就降到 8 并配合--accumulate梯度累积;--epochs 100是微调的常见量级,数据少的话 50 也够;--hyp选低增强配置,因为微调阶段不希望增强过猛破坏已学特征。训练过程中同样会生成results.csv和事件文件,用第 3 章的方法盯指标就行。
4.3 训练完怎么验证不是“过拟合到训练集”
微调完别只看训练 loss,一定要在独立验证集上跑val.py:
python val.py \ --weights runs/train/exp/weights/best.pt \ --data data/smoke.yaml \ --img-size 640 \ --task valval.py会输出 precision、recall、mAP_0.5、mAP_0.5:0.95 以及每个类别的 AP。如果验证集 mAP 明显低于训练集表现,说明过拟合,回去加数据或加增强。这里有个血泪经验:验证集一定要和训练集在场景上分布一致,如果训练集全是室内近景、验证集全是室外远景,mAP 会难看得让你怀疑人生,但那不是模型的问题,是数据划分的问题。
5. 避坑与排查:吸烟检测落地最常见的五个翻车点
5.1 现象:推理结果全是背景,一个烟都没框出来
原因通常是--img-size和训练时不一致,或者--conf-thres设太高。烟体小,置信度天然偏低,阈值 0.5 以上很容易全滤掉。解决办法:先把--conf-thres降到 0.1 观察是否有框,确认模型能检出后再逐步上调;同时核对训练配置里的imgsz,两边必须一致。
5.2 现象:同一根烟被框出好几个重叠框
原因是 NMS 的--iou-thres设得过高,或者烟体细长导致预测框长宽比分散。解决:把--iou-thres从默认 0.45 适当降到 0.3~0.4,让重叠框被合并;如果仍然多框,检查训练数据里同一根烟是否被重复标注了多个框,标注噪声会直接导致这个问题。
5.3 现象:训练时 loss 正常下降,但 mAP 一直上不去
多半是数据标注质量问题:框没贴紧烟体、把整只手都框进去、或者漏标。YOLOv5 对标注噪声很敏感,尤其是单类别任务,错误框会被当成正样本学进去。解决:用可视化脚本把标注画到原图上逐张抽查,重点看小目标和遮挡样本;宁可少标几张,也别标错。
5.4 现象:换到树莓派或边缘设备后帧率极低
原因是直接用了 YOLOv5m 的 FP32 权重,边缘端算力扛不住。解决:优先换 YOLOv5s,再做量化或转 ONNX/NCNN。常见做法是先导出 ONNX 再转目标端推理框架,导出时注意--img-size和--dynamic参数要和部署端匹配,否则会出现输入维度对不上的报错。
5.5 现象:TensorBoard 打不开或曲线是空的
原因是--logdir指错了目录,或者事件文件不在该目录下。解决:确认--logdir指向的是包含events.out.tfevents.*的父目录,而不是文件本身;如果曲线为空,检查训练时是否真的写入了日志,有些精简包会把事件文件删掉只留results.csv,那就只能靠 csv 画图。
6. 进阶技巧:把吸烟检测接到视频流并做误报抑制
把模型跑通单张图只是第一步,真正落地大多是接视频流。我一般用 OpenCV 读帧 + YOLOv5 推理 + 简单时序滤波的组合,核心思路是:单帧检出不算数,连续 N 帧里出现 M 次才判定为一次吸烟事件,这样能压掉大量偶发误检。下面是一个可复用的最小骨架:
import cv2 import torch from collections import deque # 加载本地权重,避免每次重新下载 model = torch.hub.load('.', 'custom', path='weights/smoking_m.pt', source='local') model.conf = 0.25 # 置信度阈值 model.iou = 0.4 # NMS IoU 阈值 cap = cv2.VideoCapture(0) # 0 表示摄像头,也可换成视频文件路径 hit_window = deque(maxlen=10) # 滑动窗口,记录最近 10 帧是否检出 while cap.isOpened(): ret, frame = cap.read() if not ret: break results = model(frame) detections = results.pandas().xyxy[0] has_smoke = len(detections[detections['name'] == 'smoke']) > 0 hit_window.append(1 if has_smoke else 0) # 最近 10 帧里至少 6 帧检出,才认为是一次有效吸烟行为 if sum(hit_window) >= 6: cv2.putText(frame, 'SMOKING', (30, 60), cv2.FONT_HERSHEY_SIMPLEX, 1.5, (0, 0, 255), 3) cv2.imshow('smoke-detect', frame) if cv2.waitKey(1) & 0xFF == ord('q'): break cap.release() cv2.destroyAllWindows()这段代码的关键不在推理,而在hit_window这个滑动窗口。maxlen=10表示窗口只保留最近 10 帧,sum(hit_window) >= 6是触发条件,意思是 10 帧里至少 6 帧检出才报警。这两个数字要按你的帧率和场景调:帧率高就加大窗口,误报多就提高触发比例。model.conf和model.iou直接对应推理时的置信度和 NMS 阈值,调参逻辑和第 2 章一致。用torch.hub.load时source='local'很关键,否则它会尝试联网拉取仓库,离线环境直接卡死。
还有一个容易被忽略的点:视频流里烟体在运动,单帧的框位置会抖。如果下游要做行为分析,建议对框做简单的中心点平滑,比如用指数移动平均把当前框中心和上一帧中心加权,减少抖动带来的误判。这套组合我在几个抓拍项目里用过,误报率比纯单帧判定低不少,代价是报警有几百毫秒延迟,实时性要求极高的场景要权衡。
从那以后我每次拿到一个训练好的检测权重,都强制先跑一遍val.py看独立验证集指标,再上视频流做时序滤波,绝不直接拿单帧结果下结论。希望这份拆解能帮到你,少走几个我踩过的坑。
本文还有配套的精品资源,点击获取