简介:本资源为基于YOLOv5-6.0训练完成的吸烟行为检测模型包,面向计算机视觉学习者、行为识别方向的研究人员及需要落地吸烟检测功能的开发者。包内提供YOLOv5m与yolov5s两个已训练权重,目标类别为smoke,在数千张吸烟数据上训练,准确率超过90%,并附有PR曲线、loss曲线等训练过程记录,便于评估模型表现与复现实验。资源共373个文件,以jpg图像、txt标注、yaml配置、py脚本为主,另含pt权重、png曲线图、md说明及Dockerfile等部署文件,压缩包约128.03MB,目录结构完整,覆盖数据、训练与推理环节。目前已有2392人学习下载,适合希望快速获得可用吸烟检测模型、对照曲线分析训练效果或在此基础上继续微调的中高级视觉开发者。
1. 吸烟检测为什么值得用 YOLOv5 单独做一遍
厂区、加油站、化工厂这类场景里,吸烟检测不是"识别一个人"那么简单,它要的是在监控画面里实时框出烟头、叼烟动作和烟雾,并且把误报压到能接受的水平。YOLOv5 之所以在这个任务上被反复拿出来用,是因为它单阶段、推理快、对小目标有一定容忍度,而且 yolov5-6.0 这个版本结构稳定、生态成熟,改数据集、调超参、导出部署都有现成路径。yolov5-6.0-smoking_detect.zip这类工程包,本质就是把"吸烟"当成一个独立检测类别,用 YOLOv5 走一遍从数据标注到训练再到推理的完整链路。它适合两类人:一类是手里已经有摄像头和标注数据、想快速验证可行性的工程人员;另一类是想拿一个真实小目标检测任务练手 YOLOv5 全流程的开发者。下面我按自己实际跑这类项目的顺序,把选型、数据、训练、调参和踩坑讲清楚。
2. 把吸烟检测拆成 YOLOv5 能吃的任务:类别设计与数据准备
2.1 为什么吸烟检测不能直接套用 COCO 的 person 类
很多人第一反应是"检测到人再判断有没有抽烟",这条路在工程上很容易翻车。COCO 里没有 cigarette、smoke 这些类,person 框只给你一个人的整体位置,烟头在画面里可能只有十几个像素,靠人体框去裁再分类,等于把检测问题硬转成分类问题,中间任何一步框偏了后面全错。更稳的做法是直接把吸烟相关目标定义成独立类别,让 YOLOv5 在整图上做回归。
常见做法是定义 2 到 3 个类:smoking(叼烟/手持烟的动作区域)、cigarette(烟头本身)、smoke(明显烟雾)。如果数据量不大,我一般先合并成一个smoking类跑通,再考虑拆分。原因是类别越细,小目标样本越稀疏,YOLOv5 在 6.0 版本下对小目标的召回本来就吃数据,拆太细会让 mAP 掉得很难看。
选型上还要注意:吸烟检测的负样本极其重要。工区里大量"手靠近脸""白色反光""蒸汽"都会被误判成烟,所以数据里必须刻意塞入这些困难负样本,否则模型上线后误报会让你怀疑人生。
2.2 标注规范:框到哪、留多少边距
标注质量直接决定上限。我的习惯是:
smoking类框住"嘴部到烟头"的整体区域,不要只框烟头,也不要框整张脸;cigarette类紧贴烟头外沿,留 1 到 2 像素边距;- 遮挡超过 70% 的目标直接标
ignore或不标,别硬标,硬标会教坏模型。
标注工具用 labelImg 或 CVAT 都行,导出 YOLO 格式。YOLO 格式是每行class x_center y_center width height,全部归一化到 0 到 1。这里有个高频错误:有人把像素坐标直接写进去,训练时 loss 不降,排查半天才发现没归一化。
2.3 目录结构与 data.yaml 的写法
YOLOv5-6.0 对目录结构有约定,我一般整理成这样:
smoking_detect/ ├── images/ │ ├── train/ │ └── val/ ├── labels/ │ ├── train/ │ └── val/ └── data.yamldata.yaml是训练入口,写错一个路径就白跑:
# data.yaml train: ../smoking_detect/images/train # 训练集图片目录 val: ../smoking_detect/images/val # 验证集图片目录 nc: 1 # 类别数,先跑通就写 1 names: ['smoking'] # 类别名,顺序必须和标注里的 class id 对应逻辑说明:train和val指向的是图片目录,YOLOv5 会自动去找同级的labels目录,所以 labels 路径不用写。nc必须等于names长度,否则训练启动就报维度不匹配。参数上,names的顺序就是标注文件里 class id 的顺序,0 对应第一个名字,错位会导致类别全乱。
提示:先用 50 到 100 张图跑一个 epoch,确认能正常读数据、loss 有变化,再上全量数据。这一步能省掉大量"训练半天发现路径错"的时间。
3. 用 yolov5-6.0 训练吸烟检测模型:命令、超参与断点续训
3.1 环境搭建与依赖版本
YOLOv5-6.0 对 PyTorch 和 CUDA 有版本要求,我一般用 conda 隔离环境,避免和系统里其他项目打架:
# 创建并激活环境 conda create -n smoking python=3.8 -y conda activate smoking # 安装 PyTorch,按自己 CUDA 版本选,这里以 cu113 为例 pip install torch==1.10.0+cu113 torchvision==0.11.1+cu113 -f https://download.pytorch.org/whl/torch_stable.html # 安装 yolov5-6.0 依赖 cd yolov5-6.0 pip install -r requirements.txt逻辑说明:Python 3.8 是 6.0 版本验证过的组合,太新容易在torch和numpy上出兼容问题。requirements.txt里锁了numpy、opencv-python等版本,别手动升级。参数上,CUDA 版本要和驱动匹配,nvidia-smi看右上角支持的 CUDA 版本,装高了会报no kernel image。
3.2 训练命令与关键超参
跑通数据后,训练命令我一般这样写:
python train.py \ --data ../smoking_detect/data.yaml \ --weights yolov5s.pt \ --img 640 \ --batch-size 16 \ --epochs 150 \ --hyp data/hyps/hyp.scratch-low.yaml \ --name smoking_v1 \ --cache逻辑说明:--weights yolov5s.pt用官方预训练权重做迁移学习,比从头训收敛快得多,小数据集尤其明显。--img 640是输入分辨率,烟头小的话可以提到 960,但显存和速度要权衡。--cache把图片缓存到内存,数据量不大时能明显加速。
参数上重点说三个:
--batch-size:显存够就往上加,16 是 8G 显存的稳妥值,太小会让 BN 统计不稳;--hyp:6.0 提供 low/mid/high 三档,小数据集用 low,增强弱一点,避免过拟合;--epochs:150 是起点,看results.csv里 mAP 是否还在涨,涨就继续。
3.3 断点续训与多卡
训练中断是常事,6.0 支持从last.pt续:
python train.py \ --data ../smoking_detect/data.yaml \ --weights runs/train/smoking_v1/weights/last.pt \ --resume逻辑说明:--resume会读取last.pt里保存的优化器状态和 epoch,接着跑,不用重头来。注意--resume时其他参数会被忽略,以 checkpoint 里存的为准,所以别指望续训时改 batch size。
多卡用--device 0,1,但 6.0 的多卡是 DataParallel,batch 会均分,小数据集不一定划算,我一般单卡跑。
3.4 训练过程看什么指标
runs/train/smoking_v1/下有results.csv和results.png。重点看:
metrics/mAP_0.5:整体检测能力,吸烟检测一般能到 0.8 以上算可用;metrics/mAP_0.5:0.95:更严格,小目标任务这个值通常偏低,别慌;train/box_loss和val/box_loss:如果 train 一直降、val 开始涨,就是过拟合,该早停或加数据。
注意:验证集里一定要有困难负样本,否则 mAP 虚高,上线后误报爆炸。我吃过这个亏,验证集全是干净正样本,指标好看,实际一跑全是误检。
4. 吸烟检测的推理、导出与部署前验证
4.1 单图与批量推理
训练完先本地验证,别急着上设备:
# 单张图 python detect.py \ --weights runs/train/smoking_v1/weights/best.pt \ --source ../test_imgs/smoke1.jpg \ --img 640 \ --conf-thres 0.4 \ --iou-thres 0.45 # 整个目录 python detect.py \ --weights runs/train/smoking_v1/weights/best.pt \ --source ../test_imgs/ \ --save-txt逻辑说明:--conf-thres是置信度阈值,吸烟检测我一般从 0.4 起调,太低误报多,太高漏检。--iou-thres控制 NMS 合并,重叠目标多时调低。--save-txt会把框存成 YOLO 格式,方便后续统计。
参数上,--img要和训练时一致,训练 640 推理 960 会有尺度偏差,虽然 6.0 会自适应,但一致性更好。
4.2 导出 ONNX 与 TensorRT
部署到边缘设备前,通常先导出:
# 导出 ONNX python export.py \ --weights runs/train/smoking_v1/weights/best.pt \ --include onnx \ --img 640 \ --batch-size 1 # 导出 TensorRT(需要 GPU 环境) python export.py \ --weights runs/train/smoking_v1/weights/best.pt \ --include engine \ --img 640 \ --batch-size 1 \ --device 0逻辑说明:ONNX 通用性好,TensorRT 在 NVIDIA 设备上快很多。--batch-size 1是边缘部署常用值,动态 batch 要额外指定--dynamic。导出后务必用val.py在 ONNX 上再跑一遍验证,确认精度没掉。
参数上,TensorRT 的--workspace默认 4G,显存小就调低;--half开 FP16 能提速,但精度可能掉一点,吸烟检测这种小目标要谨慎。
4.3 部署前必须做的三件事
第一,用真实摄像头视频流跑一遍,别只看图片。视频里运动模糊、光照变化会暴露很多问题。第二,统计误报率,连续跑几小时,看每小时误报几次,超过可接受阈值就回去补负样本。第三,确认推理延迟,边缘设备上单帧超过 100ms 就要考虑降分辨率或换更小的模型(yolov5n)。
5. 吸烟检测训练与部署的避坑清单
5.1 现象:训练 loss 不降,mAP 一直是 0
原因:最常见是标注格式错,比如没归一化、class id 越界,或者data.yaml里nc和实际类别数不一致。其次是图片和标签文件名对不上,YOLOv5 是按文件名找标签的,a.jpg必须对应a.txt。
解决:先用python utils/general.py里的检查逻辑,或者自己写脚本遍历 labels,确认每行 5 个值、都在 0 到 1 之间、class id 小于nc。文件名不一致就批量重命名。
5.2 现象:验证集 mAP 很高,实际部署误报严重
原因:验证集和训练集同分布,且缺少困难负样本。模型没学过"手靠近脸""白色反光"这些干扰,一到真实场景就乱框。
解决:从实际场景里截取误报帧,标成背景(不标任何目标)加进训练集,重新训练。负样本比例我一般控制在 1:3 到 1:5 之间,太多会压制正样本召回。
5.3 现象:小烟头检测不到,大目标正常
原因:YOLOv5 的 P3 特征图负责小目标,但如果训练分辨率低、小目标样本少,P3 学不好。另外 anchor 尺寸如果不匹配,小目标回归也困难。
解决:提高--img到 960 或 1280,增加小目标样本,必要时用--anchor重新聚类 anchor。6.0 默认 anchor 是 COCO 的,吸烟检测的小目标可能需要更小的 anchor。
5.4 现象:导出 ONNX 后精度下降明显
原因:导出时的预处理和后处理与 PyTorch 不一致,比如归一化方式、NMS 实现差异。另外 FP16 导出会掉精度。
解决:导出后用val.py --weights best.onnx对比 mAP,差距大就检查export.py里的--img和--batch-size是否和训练一致。FP16 先别开,确认 FP32 没问题再试。
5.5 现象:边缘设备上推理速度慢
原因:模型太大(用了 yolov5l 或 x),或者没做 TensorRT 加速,或者输入分辨率过高。
解决:换 yolov5n 或 yolov5s,导出 TensorRT,降--img到 416 或 320。吸烟检测如果场景固定、摄像头距离近,320 往往够用。
6. 把吸烟检测做稳的一个进阶习惯:用困难样本挖掘持续迭代
模型上线不是终点。我自己的习惯是,部署后每周从实际视频里抽一批误报和漏检帧,人工确认后加进训练集,重新训练一版。这个循环跑上三四轮,误报率通常能降一个数量级。具体做法是写个脚本,把detect.py --save-txt输出的框和人工复核结果对比,自动挑出置信度在 0.3 到 0.6 之间的"模糊样本",这些样本对模型提升最大。
# 从检测结果里挑模糊样本,供人工复核 import os def pick_hard_samples(txt_dir, low=0.3, high=0.6): hard = [] for f in os.listdir(txt_dir): if not f.endswith('.txt'): continue with open(os.path.join(txt_dir, f)) as fp: for line in fp: parts = line.strip().split() if len(parts) < 6: continue conf = float(parts[5]) # 第6列是置信度 if low <= conf <= high: hard.append(f) break return hard if __name__ == '__main__': samples = pick_hard_samples('../runs/detect/exp/labels') print(f'待复核模糊样本数: {len(samples)}')逻辑说明:detect.py --save-txt存出的每行是class x y w h conf,第 6 列是置信度。挑 0.3 到 0.6 之间的,这些是模型"拿不准"的,人工标一遍加进训练集,比随机加样本有效得多。参数上,low和high按自己模型的置信度分布调,如果模型整体置信度偏低,区间可以下移。
另一个习惯是固定验证集。每次迭代都用同一批验证图,这样 mAP 变化才有可比性。我见过有人每次随机切验证集,指标忽高忽低,根本没法判断模型是变好还是变差。
最后说个血泪经验:吸烟检测的标注一致性比数量重要。同一个人叼烟,今天框大明天框小,模型学出来就是抖的。标注前先定好规范,找两个人交叉检查一批,确认一致了再全量标。这个前期投入,比后面反复调参省时间得多。希望帮到你。
本文还有配套的精品资源,点击获取