news 2026/9/24 19:53:31

YOLOv5-6.0吸烟检测实战:从训练到部署的避坑指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
YOLOv5-6.0吸烟检测实战:从训练到部署的避坑指南

简介:这份资源面向计算机视觉方向的学习者与开发者,提供基于YOLOv5-6.0训练完成的吸烟行为检测模型,可用于公共场所、工地、加油站等场景下的吸烟行为识别与预警。包内包含YOLOv5m与YOLOv5s两个已训练权重,目标类别为smoke,在数千张吸烟数据上训练,准确率超过90%,并附有PR曲线、loss曲线等训练过程记录,便于评估模型表现与复现实验。资源共373个文件,以jpg图像、txt标注、yaml配置、py脚本、pt权重为主,另有png曲线图、md说明文档、sh运行脚本及Dockerfile等部署文件,压缩包约128.03MB,目录结构完整,覆盖数据、训练、推理与容器化部署环节。目前已有2392人学习下载,适合希望快速上手吸烟检测、参考训练配置或进行二次开发的中高级读者。

1. 从一张吸烟检测图说起:yolov5-6.0-smoking_detect.zip 到底能跑出什么

厂区安全巡检的朋友发来一张监控截图,画面里两名工人蹲在角落,其中一人手指间夹着烟,背景是堆放的纸箱。他问:这种场景能不能用 YOLOv5 自动抓?答案是可以,而且yolov5-6.0-smoking_detect.zip这类工程包已经把「吸烟检测」这个垂类任务从数据标注到推理部署的链路走通了一遍。它解决的不是通用目标检测问题,而是把「香烟 + 人脸/手部」这种小目标、遮挡多、正负样本极不均衡的场景,收敛到一个可复现的训练配置里。适合两类人:一是手里有厂区、加油站、林区监控,想快速验证吸烟行为识别可行性的工程人员;二是正在学yolov5训练自己的数据集,想找一个真实垂类练手,而不是拿 COCO 跑一遍就结束的开发者。这个包的价值不在模型多大,而在于它把吸烟检测的类别定义、数据组织、超参和推理阈值这些容易翻车的地方,提前踩过一遍。

2. 吸烟检测为什么不能直接套用 COCO 预训练模型

2.1 香烟在 640 分辨率下只剩十几个像素

COCO 里没有「香烟」这个类,最接近的是personhandbag之类,直接拿yolov5s.pt去推理吸烟画面,模型只会框出人,不会框烟。更麻烦的是尺度:一根香烟在 1080P 监控画面里可能只占 20×8 像素,缩放到 YOLOv5 默认的 640×640 输入后,特征图上只剩 2~3 个像素点。YOLOv5 的 P3 检测头 stride 是 8,理论上能覆盖 8 像素以上的目标,但实际感受野和正样本匹配策略会让这种极小目标在训练早期就被判为背景。

常见做法是把输入尺寸提到 960 或 1280,同时把数据集中「香烟」框的宽高比单独统计一遍。如果宽高比集中在 2:1 到 4:1 之间,可以在 anchor 聚类时把--img-size--anchors一起调,而不是用默认的 COCO anchor。我一般会先跑一遍python utils/autoanchor.py --data data/smoking.yaml --img-size 960,看聚类出来的 anchor 和默认值差多少,差得远就替换。

2.2 吸烟检测的类别定义比想象中更关键

很多开源吸烟数据集只标一个smoke类,把香烟、烟雾、点火动作混在一起。这种标法在训练时会让模型学到「手靠近脸」就等于吸烟,误报率极高。更稳的定义是拆成两类:cigarette(香烟本体)和smoking(吸烟行为,含手部与面部交互)。yolov5-6.0-smoking_detect.zip这类工程包通常采用单类smoking,但会在数据清洗阶段把只有烟雾没有香烟的图剔除。

如果你自己标数据,建议先定一个判定规则:香烟可见且与人脸/手部有接触,才标smoking;只有烟雾或只有烟盒,不标。规则写进data/smoking.yaml的注释里,标注人员按同一把尺子走,否则后面调参全是玄学。

2.3 负样本里必须混入「类似吸烟」的干扰

吸烟检测最大的误报来源不是没人吸烟,而是有人在做类似动作:喝水、打电话、托腮、戴口罩调整。如果负样本只有纯背景,模型会把「手靠近脸」学成强特征。我一般会在训练集里按 1:3 的比例混入这些干扰图,且不标任何框。YOLOv5 训练时这些图会贡献背景损失,压制误报。

具体操作是在data/smoking.yaml里把train指向一个包含正负样本的 txt 列表,负样本图路径也写进去,对应 label 文件为空。YOLOv5-6.0 对空 label 的处理是整图算背景,不会报错,但要注意--rect模式下空 label 图如果宽高比极端,可能被裁得只剩背景,影响不大。

3. 用 yolov5-6.0 跑通吸烟检测的最小命令链

3.1 环境准备:conda 建环境与依赖版本锁定

YOLOv5-6.0 对 PyTorch 和 CUDA 的版本比较敏感,尤其是torchvision的 NMS 算子。我习惯用 conda 建一个独立环境,避免和系统里的其他 torch 版本打架。

conda create -n smoking python=3.8 -y conda activate smoking # 安装 pytorch 1.8.1 + cuda 11.1,这是 yolov5-6.0 验证过的组合 pip install torch==1.8.1+cu111 torchvision==0.9.1+cu111 -f https://download.pytorch.org/whl/torch_stable.html # 安装 yolov5-6.0 的 requirements pip install -r requirements.txt

逻辑说明:Python 3.8 是 6.0 版本官方测试过的解释器版本,3.9 以上在torch.nn.functional.interpolaterecompute_scale_factor参数上会有警告。CUDA 11.1 对应 torch 1.8.1,如果你显卡是 30 系,这个组合能正常调用 cuDNN。装完用python -c "import torch; print(torch.cuda.is_available())"验证,返回 True 再往下走。

3.2 数据组织:images 与 labels 的目录对齐

YOLOv5 要求图片和标签分目录存放,文件名一一对应。假设你的数据放在smoking_data/下:

smoking_data/ ├── images/ │ ├── train/ │ │ ├── 0001.jpg │ │ └── 0002.jpg │ └── val/ │ └── 0003.jpg └── labels/ ├── train/ │ ├── 0001.txt │ └── 0002.txt └── val/ └── 0003.txt

每个 txt 里一行一个目标,格式是class x_center y_center width height,全部归一化到 0~1。如果只有一类smoking,class 就是 0。常见翻车点是有人把labels目录名写成label,或者图片是.jpeg而标签是.txt但文件名大小写不一致,YOLOv5 会直接跳过这些图,训练日志里train: Scanning的数量对不上。

3.3 配置文件:smoking.yaml 与模型选择

data/下新建smoking.yaml

# 吸烟检测数据集配置 path: ../smoking_data # 数据集根目录 train: images/train # 训练集图片相对路径 val: images/val # 验证集图片相对路径 nc: 1 # 类别数,只有 smoking 就写 1 names: ['smoking'] # 类别名,顺序要和 label 里的 class 对应

模型选择上,yolov5s.pt适合边缘部署,yolov5m.pt精度更高但推理慢。如果目标是树莓派 5 上部署,建议从yolov5s起步,输入尺寸 640,量化后再看 mAP 掉多少。训练命令:

python train.py --img 960 --batch 16 --epochs 100 --data data/smoking.yaml --weights yolov5s.pt --cfg models/yolov5s.yaml --name smoking_s

参数说明:--img 960是为了让香烟这种小目标在特征图上多留几个像素;--batch 16在 8G 显存下比较稳,显存不够就降到 8;--weights yolov5s.pt用 COCO 预训练权重做迁移,比从头训收敛快很多;--name smoking_s会在runs/train/smoking_s/下存日志和权重。

3.4 训练过程看什么:loss 曲线与 mAP 的合理区间

启动后先看runs/train/smoking_s/下的results.txt或 TensorBoard。前 10 个 epoch 的box_lossobj_loss应该明显下降,如果obj_loss一直不降,大概率是 label 格式错了或类别数对不上。mAP@0.5 在吸烟检测这种小目标任务上,能到 0.6~0.75 就算可用,超过 0.8 要警惕验证集泄漏。

我一般会在第 50 epoch 和最后 epoch 各跑一次val.py,看混淆矩阵里smoking被误判成背景的比例。如果漏检集中在夜间或逆光图,说明数据里这类场景太少,需要补样本而不是调参。

4. 推理部署与阈值调优:从 PyTorch 到实际画面

4.1 detect.py 的默认阈值为什么不够用

YOLOv5-6.0 的detect.py默认--conf-thres 0.25--iou-thres 0.45。在吸烟检测里,香烟框的置信度普遍偏低,因为目标小、特征弱。如果直接跑,很多真香烟会被 0.25 卡掉。我一般会把--conf-thres降到 0.15,同时把--iou-thres提到 0.5,让重叠框多保留一些,再在后处理里按面积过滤。

python detect.py --weights runs/train/smoking_s/weights/best.pt --source test_video.mp4 --img 960 --conf-thres 0.15 --iou-thres 0.5 --save-txt

逻辑说明:--save-txt会把每帧的检测框存成 txt,方便后续统计误报。--img 960必须和训练时一致,否则模型看到的尺度变了,小目标直接消失。如果视频是 4K,建议先缩到 1080P 再推理,不然显存和耗时都吃不消。

4.2 用置信度与面积双阈值压误报

单靠置信度阈值很难同时压住漏检和误报。我的做法是在detect.py输出后加一层过滤:只保留面积大于整图 0.1% 且置信度大于 0.2 的smoking框。面积太小的框大概率是噪声,面积太大的框可能是把整个人误判成吸烟。

# 后处理过滤示例,读取 detect.py 输出的 txt import os img_area = 1920 * 1080 # 假设原图尺寸 min_area_ratio = 0.001 conf_thres = 0.2 for txt_file in os.listdir('runs/detect/exp/labels'): with open(os.path.join('runs/detect/exp/labels', txt_file)) as f: for line in f: cls, x, y, w, h, conf = line.strip().split() area = float(w) * float(h) * img_area if float(conf) < conf_thres or area / img_area < min_area_ratio: continue # 保留的框写入新文件或做告警

参数说明:min_area_ratio根据你的画面分辨率调,1080P 下 0.001 对应约 2000 像素,差不多是 50×40 的框,能滤掉大部分香烟误报。conf_thres设 0.2 比默认 0.25 略低,给真香烟留机会。

4.3 树莓派 5 上部署的量化与耗时预期

树莓派 5 的 CPU 是 Cortex-A76,没有 CUDA,只能跑 ONNX Runtime 或 NCNN。YOLOv5s 转 ONNX 后,在树莓派 5 上单帧 640 输入大约 200~400ms,960 输入会到 800ms 以上,做不到实时。常见做法是降到 416 或 320 输入,mAP 会掉 10~15 个点,但帧率能到 5~8 FPS,够做低频抓拍。

# 导出 ONNX python export.py --weights runs/train/smoking_s/weights/best.pt --include onnx --img 640 --dynamic # 树莓派上安装 onnxruntime pip install onnxruntime

注意--dynamic会让 batch 和尺寸动态,树莓派上如果固定输入可以去掉,推理更快。量化用 ONNX Runtime 的quantize_dynamic做 INT8,权重量化后模型小一半,但吸烟检测这种小目标对量化敏感,建议量化后在验证集上重跑一遍 mAP,掉超过 5 个点就放弃量化。

5. 吸烟检测训练与部署的避坑排查

5.1 训练 loss 正常但 mAP 为 0

现象:box_lossobj_loss都在降,但val.py跑出来 mAP 接近 0。原因通常是data/smoking.yaml里的nc和 label 里的 class 编号不匹配,比如 label 里写了 1 但nc是 1(只有 class 0),YOLOv5 会把 class 1 当越界忽略。解决:用python utils/general.py --check-labels或手动统计所有 txt 里的 class 最大值,确保小于nc

5.2 验证集 mAP 很高但实际画面全是误报

现象:验证集 mAP@0.5 到 0.85,但拿现场视频跑,到处框吸烟。原因是验证集和训练集同分布,且负样本太少,模型没学过「类似吸烟」的干扰。解决:从现场视频里截 200 张误报帧,不标任何框,加入训练集重新训 20 个 epoch,误报会明显下降。这是血泪经验,补负样本比调阈值管用。

5.3 香烟框在增强后消失

现象:训练时开了--mosaic--mixup,小目标香烟在拼接后变得更小,甚至被裁掉。原因:Mosaic 会把 4 张图缩放到 640,原本 20 像素的香烟变成 5 像素,低于 P3 的有效感受野。解决:在data/hyps/hyp.scratch-low.yaml里把mosaic概率从 1.0 降到 0.5,或者把--img提到 1280 再开 Mosaic。我一般会在最后 20 个 epoch 关掉 Mosaic,让模型在原始尺度上微调。

5.4 推理时 GPU 显存溢出

现象:训练完用detect.py跑视频,报 CUDA out of memory。原因:--img 960加上默认 batch 推理,显存峰值比训练还高。解决:加--batch-size 1,或者用--device cpu先跑通流程。如果必须 GPU,把--img降到 640,或者用--half半精度推理,显存能省 40% 左右。

5.5 树莓派上 ONNX 推理结果和 PyTorch 不一致

现象:PyTorch 下能检出的香烟,转 ONNX 后在树莓派上漏检。原因:导出时--img和推理时输入尺寸不一致,或者 ONNX Runtime 的 NMS 算子和 torchvision 有差异。解决:导出和推理都用同一尺寸,NMS 用 ONNX Runtime 自带的NonMaxSuppression,不要自己写。另外检查export.py里的--opset版本,树莓派上 onnxruntime 1.12 支持 opset 12,太高会报错。

6. 把吸烟检测推到可用:一个后处理技巧与验证习惯

如果你已经跑通训练,想让吸烟检测从「能检出」变成「敢告警」,重点不在换更大的模型,而在后处理的时序过滤。单帧检测必然有抖动,同一根香烟在连续帧里可能时有时无。我的做法是维护一个长度为 10 帧的滑动窗口,只有当smoking框在窗口内出现次数超过 6 次,且框的中心点位移小于画面宽度的 5%,才触发告警。这个逻辑用 Python 队列就能实现,不需要改模型。

from collections import deque # 时序过滤:10 帧窗口,命中 6 次且中心稳定才告警 history = deque(maxlen=10) STABLE_RATIO = 0.05 # 中心位移阈值,画面宽度的 5% def update(detections, img_w): # detections: list of (x_center, y_center, w, h, conf) if not detections: history.append(None) return False # 取置信度最高的框 best = max(detections, key=lambda d: d[4]) history.append(best) valid = [d for d in history if d is not None] if len(valid) < 6: return False # 检查中心点稳定性 xs = [d[0] for d in valid] if max(xs) - min(xs) > img_w * STABLE_RATIO: return False return True

参数说明:maxlen=10对应约 0.5 秒(20FPS 下),窗口太长告警延迟大,太短压不住抖动。STABLE_RATIO根据摄像头焦距调,广角画面可以放到 0.08,长焦放到 0.03。这个逻辑对「有人路过时手靠近脸」的瞬时误报特别有效,因为干扰动作通常只持续两三帧,进不了 6 次命中。

验证习惯上,我每次改完阈值或后处理,都会拿同一段 5 分钟现场视频跑一遍,人工数误报和漏检,记在表格里。不要只看 mAP,mAP 不反映时序抖动。另外,吸烟检测的模型最好每季度用新场景数据微调一次,厂区换季、工人换装、摄像头角度调整都会让旧模型掉点。我自己的习惯是保留一个hard_examples/目录,平时看到误报就截图丢进去,攒够 100 张就重训一轮,比一次性标几千张更可持续。希望帮到你。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/24 19:53:20

回归代码详解:从线性回归到XGBoost的实战指南

1. 内容整体设计与思路拆解1.1 为什么第五天必须讲回归&#xff0c;而且是代码优先先说一个我自己的观察。前四天学员还在跟数据结构、基础语法、可视化缠斗&#xff0c;到了第五天突然进入回归&#xff0c;很多人第一反应是&#xff1a;“是不是有点早&#xff1f;”但恰恰相反…

作者头像 李华
网站建设 2026/9/24 19:53:14

电脑蓝屏开不了机?5步自检法从蓝屏代码到DMP文件找出真凶

电脑蓝屏开不了机&#xff0c;这几年我帮身边朋友处理过至少几十次&#xff0c;说句实话&#xff0c;真正需要送修的重来不超过两成。系统崩溃、驱动打架、外设捣乱&#xff0c;这些软件层面的问题占了大多数&#xff0c;明明自己花半小时就能搞定&#xff0c;结果抱着主机去维…

作者头像 李华
网站建设 2026/9/24 19:53:14

2026年Jira国产替代核心指标:权限模型、硬件流程与API稳定性

1. 这不是“又一个工具测评”&#xff0c;而是研发团队在2026年必须面对的真实选型现场 你刚收到通知&#xff1a;公司启动“研发管理平台国产化替代专项”&#xff0c;要求Q3前完成Jira迁移&#xff0c;预算卡得死&#xff0c;法务对SaaS数据出境有明确红线&#xff0c;运维只…

作者头像 李华
网站建设 2026/9/24 19:53:14

用DailyMed API构建药物情报检索:SPL解析与说明书结构化实践

做医药数据相关开发这几年&#xff0c;我越来越觉得 DailyMed 是个被低估的宝藏数据源。很多人一上手药物情报抓取&#xff0c;第一反应就是扑向 openFDA&#xff0c;因为它的接口直观&#xff0c;返回的是 JSON&#xff0c;文档也花哨&#xff1b;但真正跑起来做药品说明书结构…

作者头像 李华