news 2026/10/7 15:58:14

YOLOv5 6.0吸烟检测实战:从数据集配置到边缘部署全流程

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
YOLOv5 6.0吸烟检测实战:从数据集配置到边缘部署全流程

简介:本资源面向计算机视觉学习者与行为识别方向开发者,提供基于YOLOv5-6.0框架训练完成的吸烟检测模型,可用于公共场所、校园、工地等场景下的吸烟行为自动识别。包内包含YOLOv5m与YOLOv5s两个已训练权重,目标类别为smoke,在数千张吸烟数据上训练,准确率超过90%,并附有PR曲线、loss曲线等训练过程记录,便于评估模型表现与复现实验。压缩包共373个文件,约128.03MB,涵盖pt权重、py源码、yaml配置、jpg样本图、png曲线图、csv训练日志及Dockerfile等部署文件,结构完整,兼顾训练、验证与推理需求。目前已有2392人学习下载,适合希望快速上手吸烟检测、进行二次训练或对比不同模型精度的读者参考使用。

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

吸烟检测这个需求,最早找上来的往往不是算法团队,而是工地安全、加油站、化工厂、写字楼物业这些场景的负责人。他们要的东西很朴素:摄像头已经装好了,能不能自动认出画面里有人正在抽烟,然后给个告警。真正动手做才发现,公开数据集里"吸烟"这个类别的标注质量参差不齐,烟头小、遮挡多、手部动作和点烟动作容易混,模型训出来要么漏检要么误报。yolov5-6.0-smoking_detect.zip这个包名透露的信息很明确:基于 YOLOv5 6.0 版本、面向吸烟检测任务的一套工程文件。它解决的不是"从零发明一个检测器",而是把 YOLOv5 这套成熟框架落到吸烟这个具体类别上——数据怎么组织、配置怎么改、训练怎么起、推理怎么接。适合两类人:一类是手里有吸烟场景数据、想快速跑通 baseline 的算法工程师;另一类是需要在边缘设备上部署吸烟识别的嵌入式开发者。下面按"先立住原理、再动手复现、最后讲坑"的顺序拆开讲。

2. YOLOv5 6.0 做吸烟检测:为什么选它、数据怎么摆

2.1 吸烟检测为什么落在 YOLOv5 而不是别的检测器

吸烟检测本质是一个小目标 + 特定姿态的单类或多类检测问题。烟头在 1080P 画面里可能只有十几个像素,人手夹烟的动作又和拿笔、拿手机高度相似。选检测框架时,核心看三点:小目标召回、推理速度、工程成熟度。

YOLOv5 6.0 这个版本在工程上是个分水岭。它把模型结构、训练脚本、导出工具、推理接口都收进了一个仓库,models/下按yolov5s/m/l/x给出不同规模的配置,data/下用 YAML 描述数据集路径和类别,train.py、detect.py、export.py各司其职。对吸烟检测这种需要反复调数据、调 anchor、调输入分辨率的任务,这种"改一个 YAML 就能重训"的结构省掉了大量胶水代码。

和两阶段检测器比,YOLOv5 的单阶段结构在边缘设备上更友好。吸烟检测往往要跑在 Jetson、RK3568、树莓派这类算力有限的板子上,两阶段检测器的候选框生成会吃掉不少时间。YOLOv5s 在 640 输入下,主流边缘设备上做到实时并不难,这是它被大量吸烟检测项目选中的现实原因。

另一个容易被忽略的点是后处理。YOLOv5 的输出是三个尺度的特征图,经过 NMS 之后给出框。吸烟检测里烟头小,如果 NMS 的 IoU 阈值设得太激进,相邻的烟头和手指框会被误合并。YOLOv5 的后处理参数在detect.py里是显式暴露的,改起来直观,这对调吸烟这种密集小目标很关键。

2.2 数据集目录结构与 YAML 配置怎么写

拿到yolov5-6.0-smoking_detect.zip这类包,第一件事不是急着train.py,而是把数据目录和 YAML 对齐。YOLOv5 6.0 要求的数据组织方式是固定的:

smoking_dataset/ ├── images/ │ ├── train/ │ └── val/ ├── labels/ │ ├── train/ │ └── val/

images/train放训练图,labels/train放同名.txt标注。标注格式是 YOLO 格式:每行class_id x_center y_center width height,后四个都是相对整图宽高的归一化值,范围 0 到 1。吸烟检测常见两类标法:只标smoking一类,或者标smoking+cigarette两类。类别数直接决定后面模型头部的输出通道,改错了训练能跑但结果全乱。

对应的数据配置文件,通常放在data/smoking.yaml:

# 吸烟检测数据集配置 path: ../smoking_dataset # 数据集根目录 train: images/train # 训练集相对路径 val: images/val # 验证集相对路径 nc: 1 # 类别数,只检测吸烟行为时为 1 names: ['smoking'] # 类别名,顺序必须和标注里的 class_id 对应

这里path用相对路径时,是相对于 YOLOv5 仓库根目录解析的,不是相对于这个 YAML 文件。很多人第一次配错就是栽在这——把path写成相对 YAML 的路径,训练时报"找不到图片"。稳妥做法是直接写绝对路径,或者确认相对的是仓库根。

nc和names必须和标注文件里的 class_id 严格对应。如果标注里出现了class_id=1但nc=1,训练时不会立刻报错,而是在计算 loss 时索引越界或者静默丢弃,表现为 loss 不下降、mAP 一直是 0。这是吸烟检测数据集里最常见的翻车点之一。

2.3 模型配置选型:yolov5s 还是 yolov5m

YOLOv5 6.0 在models/下给了几个预设:yolov5s.yaml、yolov5m.yaml、yolov5l.yaml、yolov5x.yaml,深度和宽度依次递增。吸烟检测选哪个,取决于你的部署目标和数据量。

配置参数量级别适用场景吸烟检测建议
yolov5s最小边缘设备、实时数据量 5000 张以上首选
yolov5m中等服务器推理小目标多、漏检严重时换
yolov5l/x大离线高精度吸烟检测一般用不上

吸烟检测里烟头是小目标,直觉上会想用大模型。但实际经验是:数据量不够时,大模型过拟合更快,验证集 mAP 反而低于 yolov5s。我一般先用 yolov5s 跑通全流程,看验证集漏检集中在哪,再决定要不要换 yolov5m。换模型只需要改--cfg参数,不用动数据。

如果标注里烟头占比特别小,可以在模型配置里调整 anchor。YOLOv5 6.0 的 anchor 是写在 YAML 里的,默认三组九個。吸烟检测的小目标如果普遍小于 16 像素,默认 anchor 匹配度低,可以重新用 k-means 在自家标注上聚类一遍,把小的那组 anchor 调小。这一步不是必须,但漏检严重时值得试。

3. 从零跑通训练:命令、参数与验证

3.1 环境准备与最小训练命令

YOLOv5 6.0 对环境的依赖写在requirements.txt里,核心是 PyTorch、torchvision、numpy、opencv-python。Python 版本建议 3.8 到 3.10,太新的版本有时会在某些 CUDA 组合上出问题。装依赖:

# 建议在虚拟环境里操作,避免污染系统 Python python -m venv venv source venv/bin/activate # Windows 用 venv\Scripts\activate pip install -r requirements.txt

装完之后先验证 PyTorch 能不能看到 GPU:

python -c "import torch; print(torch.cuda.is_available(), torch.cuda.device_count())"

输出True 1之类才说明 CUDA 可用。如果输出False,训练会退到 CPU,吸烟检测这种数据量下 CPU 训练基本没法等。

最小训练命令长这样:

# 用 yolov5s 在吸烟数据集上训练 python train.py \ --data data/smoking.yaml \ --cfg models/yolov5s.yaml \ --weights yolov5s.pt \ --batch-size 16 \ --epochs 100 \ --img 640 \ --device 0

--weights yolov5s.pt是加载 COCO 预训练权重做迁移学习。吸烟检测数据量通常不大,从预训练权重起步比从零训收敛快得多,也更稳。如果拿不到预训练权重,--weights ''从零训,但需要更多 epoch 和更谨慎的学习率。

--img 640是输入分辨率。吸烟检测里烟头小,有人会想上 1280。分辨率翻倍显存占用大约翻四倍,batch-size 要相应降。我的建议是先用 640 跑通,看验证集里小目标漏检比例,再决定要不要上 960 或 1280。

3.2 关键超参数怎么设:学习率、batch、epoch

YOLOv5 6.0 的超参数分两层:命令行参数和data/hyp.scratch.yaml里的超参文件。命令行参数覆盖训练流程,hyp 文件管数据增强和损失权重。吸烟检测调参,重点看这几个。

学习率。YOLOv5 默认用余弦退火 + warmup,初始学习率在 hyp 文件里是lr0: 0.01。迁移学习时这个值偏大,容易把预训练权重冲掉。我一般把lr0降到 0.001 到 0.003,lrf(最终学习率比例)保持 0.01 到 0.1。如果训练前几个 epoch loss 剧烈震荡,先降lr0。

batch-size。受显存限制,能大则大。batch 太小,BN 层统计不稳,loss 抖动明显。吸烟检测 640 输入下,单卡 8G 显存大概能跑 batch 16 的 yolov5s。显存不够就用--batch-size -1让 YOLOv5 自动选,或者开梯度累积。

epoch。100 到 300 是常见区间。判断该不该停,看runs/train/exp/results.csv里的metrics/mAP_0.5。如果连续二三十个 epoch 不涨,基本可以停。吸烟检测数据量小的时候,100 epoch 往往就收敛了,再多是浪费。

数据增强。hyp 文件里的mosaic、mixup、hsv_h/s/v对吸烟检测影响很大。mosaic 把四张图拼一张,能显著增加小目标的上下文多样性,建议保持开启。但如果你的吸烟场景背景高度固定(比如固定工位摄像头),mosaic 可能引入不相关背景,反而干扰,可以适当降低mosaic概率。

3.3 训练过程怎么看:loss 曲线与验证指标

训练起来之后,runs/train/exp/下会生成一堆文件。重点看三个:

results.csv记录每个 epoch 的 loss 和指标。train/box_loss、train/obj_loss、train/cls_loss是训练损失,val/box_loss等是验证损失。正常情况训练损失和验证损失同步下降。如果训练损失降、验证损失涨,是过拟合,加数据或加增强。

results.png是上面 CSV 的可视化,一眼能看出趋势。confusion_matrix.png是混淆矩阵,吸烟检测里能直接看出"吸烟被误判成背景"和"背景被误判成吸烟"各有多少。误判成背景多,说明漏检,考虑提高输入分辨率或调小 anchor;背景误判成吸烟多,说明误报,考虑提高置信度阈值或补负样本。

weights/下会存best.pt和last.pt。best.pt是验证集 mAP 最高的那个 epoch,部署用这个。last.pt是最后一个 epoch,用来断点续训。

验证命令:

# 在验证集上评估 best.pt python val.py \ --data data/smoking.yaml \ --weights runs/train/exp/weights/best.pt \ --img 640 \ --task val

输出里mAP_0.5和mAP_0.5:0.95是两个核心指标。吸烟检测这种单类任务,mAP_0.5到 0.85 以上算可用,0.9 以上算不错。如果mAP_0.5高但mAP_0.5:0.95低,说明框的位置不够准,可能是标注框画得太松。

4. 推理、导出与边缘部署:从 detect.py 到 RK3568

4.1 用 detect.py 做图片和视频推理

训练完拿到best.pt,先用detect.py验证效果:

# 对单张图片推理 python detect.py \ --weights runs/train/exp/weights/best.pt \ --source test.jpg \ --img 640 \ --conf-thres 0.4 \ --iou-thres 0.45

--conf-thres是置信度阈值,低于它的框直接丢。吸烟检测里这个值很关键:设高了漏检,设低了误报。0.4 是个常见起点,实际按场景调。--iou-thres是 NMS 的 IoU 阈值,控制重叠框合并。吸烟检测里如果一个人手部有多个疑似烟头框,iou-thres设太低会合并掉,设太高会留一堆重复框。0.45 到 0.5 之间比较稳。

视频推理把--source换成视频路径或0(摄像头)。--view-img可以实时显示结果,调试时有用,部署时去掉。

推理结果默认存到runs/detect/exp/。如果要做告警,可以在detect.py里加逻辑:检测到smoking类且置信度超过阈值,就触发一次告警。注意加去重,同一目标连续多帧命中不要重复告警。

4.2 导出 ONNX 与 RK3568 量化部署要点

边缘部署绕不开模型导出。YOLOv5 6.0 的export.py支持导出 ONNX、TorchScript、TensorRT 等格式。RK3568 这类 NPU 平台,常见路径是先导出 ONNX,再用厂商工具转成 RKNN。

# 导出 ONNX,指定 opset 和输入尺寸 python export.py \ --weights runs/train/exp/weights/best.pt \ --include onnx \ --img 640 \ --opset 12 \ --batch-size 1

--opset 12是常见选择,太低有些算子不支持,太高部分转换工具跟不上。--batch-size 1是因为边缘部署通常单帧推理,batch 固定为 1 能让后续量化更顺。

导出 ONNX 后,用 RKNN 工具链转换时要注意几点。第一,输入尺寸要和训练时一致,训练用 640 就转 640,转成别的尺寸精度会掉。第二,量化需要校准集,从验证集里抽一两百张有代表性的图,覆盖不同光照和场景,校准集太单一会导致量化后精度崩。第三,YOLOv5 的输出后处理(解码 + NMS)在 RKNN 上通常放在 CPU 做,NPU 只跑主干和检测头,这样兼容性最好。

树莓派 4B/5 上部署 YOLOv5,走的是另一条路:要么用 ONNX Runtime,要么用 NCNN。树莓派没有专用 NPU,纯 CPU 推理 yolov5s 640 输入大概几帧到十几帧,取决于是否开多线程和输入分辨率。如果帧率不够,降输入到 416 或换 yolov5n 是常见做法。量化到 INT8 在树莓派上收益有限,因为 CPU 对 INT8 的支持不如 NPU 平台。

4.3 后处理参数对吸烟检测结果的影响

后处理是吸烟检测里最容易被低估的一环。YOLOv5 的输出是三个尺度的特征图,每个格子预测若干框,经过置信度过滤和 NMS 才得到最终结果。参数没调好,前面训练再准也白搭。

置信度阈值conf-thres。吸烟检测的误报大多来自这个值设太低。画面里的手指、笔、耳机线在低阈值下都可能被当成烟。但设太高,远处的小烟头又检不到。实操里我会在验证集上画一条 conf 从 0.1 到 0.9 的 precision-recall 曲线,选 F1 最高的点作为起点,再按场景微调。

NMS 的iou-thres。吸烟检测里一个人可能同时有手、烟、脸多个框,如果烟头和手指框重叠度高,iou-thres设太低会把烟头框 NMS 掉。0.45 是默认值,小目标密集时可以提到 0.5 到 0.6。

多尺度推理--augment。开启后会对图像做翻转、缩放等多尺度推理再合并,能提升小目标召回,但推理时间翻几倍。离线分析可以用,实时部署一般不开。

类别置信度和框置信度的乘积是最终分数。YOLOv5 里--conf-thres过滤的是这个乘积。如果发现某些框分数卡在阈值边缘反复出现,说明模型对这个目标本身就不确定,与其调阈值,不如回去补这类样本。

5. 吸烟检测避坑清单:五条血泪经验

5.1 标注类别和 nc 不一致导致 mAP 恒为 0

现象:训练能正常启动,loss 也在动,但验证集mAP_0.5一直是 0,混淆矩阵全空。

原因:标注文件里的class_id超出了nc的范围。比如nc: 1但标注里出现了class_id=1(从 1 开始编号而不是从 0)。YOLOv5 在计算 loss 时对越界类别做了静默处理,不报错但也不学。

解决:写个脚本扫一遍所有 label 文件,统计class_id的取值分布,确认最大值小于nc。YOLO 格式的类别从 0 开始编号,只有一个类别时全部应该是 0。

5.2 图片和标签文件名不匹配导致部分样本被跳过

现象:训练日志里显示的图片数量和实际数据集数量对不上,少了一部分。

原因:YOLOv5 按文件名匹配图片和标签。images/train/001.jpg对应labels/train/001.txt。如果标签文件名带了多余后缀,或者图片是.jpeg而标签按.jpg命名,匹配不上就被跳过。

解决:统一图片扩展名,标签文件名严格等于图片文件名去掉扩展名。写个脚本检查两边文件名集合是否一致,差集就是被跳过的样本。

5.3 输入分辨率改了但 anchor 没跟着调

现象:把--img从 640 提到 1280 后,小目标召回没提升,反而框的位置偏移。

原因:YOLOv5 的 anchor 是按训练分辨率设计的。改了输入尺寸但 anchor 还是默认值,anchor 和实际目标尺寸的匹配度下降,尤其是小目标那组 anchor。

解决:改输入分辨率后,用utils/autoanchor.py里的逻辑在自家数据集上重新聚类 anchor,或者至少确认默认 anchor 在新分辨率下仍能覆盖目标尺寸分布。

5.4 量化后精度断崖式下跌

现象:FP32 模型 mAP 0.88,转成 INT8 量化后掉到 0.6 甚至更低。

原因:校准集不具代表性。量化需要校准集统计激活值分布,如果校准集全是白天清晰图,模型没见过夜间或逆光场景的分布,量化参数就偏了。

解决:校准集从验证集里分层抽样,覆盖不同光照、不同场景、不同目标大小。数量一两百张够用,关键是分布要全。如果还是掉,考虑混合量化,对敏感层保留 FP16。

5.5 边缘设备上帧率不达标

现象:模型在服务器上跑得好好的,部署到 RK3568 或树莓派上帧率只有个位数。

原因:后处理放在 CPU 做,或者输入分辨率没降,或者模型没量化。YOLOv5 的解码和 NMS 在 CPU 上是纯 Python 循环时很慢。

解决:后处理用 C++ 重写或换成 numpy 向量化实现;输入分辨率按实际需求降,监控场景 416 往往够用;NPU 平台务必走 INT8 量化。树莓派上可以试 NCNN,它对 ARM 的优化比 ONNX Runtime 好。

6. 把吸烟检测做稳的一个习惯:先看混淆矩阵再调参

调吸烟检测调到最后,我发现最省时间的做法不是盲目试参数,而是每次训练完先看confusion_matrix.png。这张图把"吸烟被判成背景"和"背景被判成吸烟"两个方向的错误分开摆着,指向的调参方向完全不同。

漏检多(吸烟被判成背景),优先动三处:输入分辨率往上提、小目标 anchor 调小、conf-thres往下降。误报多(背景被判成吸烟),优先补负样本、提高conf-thres、检查标注里有没有把非吸烟的手部动作错标成吸烟。

下面这个小脚本我常用来快速统计验证集上的错误分布,比翻日志直观:

import numpy as np from pathlib import Path # 读取 YOLOv5 val.py 输出的混淆矩阵(简化示意) # 实际可从 runs/val/exp/confusion_matrix.png 对应的数据读取 def summarize_errors(cm): """ cm: 混淆矩阵,行是预测,列是真实,最后一行为背景 返回漏检率和误报率 """ cm = np.array(cm) # 吸烟类索引为 0,背景为最后一行/列 fn = cm[-1, 0] # 真实是吸烟,预测成背景 -> 漏检 fp = cm[0, -1] # 真实是背景,预测成吸烟 -> 误报 tp = cm[0, 0] fnr = fn / (tp + fn + 1e-6) # 漏检率 fpr = fp / (tp + fp + 1e-6) # 误报率 print(f"漏检率: {fnr:.3f}, 误报率: {fpr:.3f}") return fnr, fpr # 示例矩阵,实际替换为真实统计值 summarize_errors([[800, 50], [120, 30]])

这段代码的逻辑很简单:从混淆矩阵里取出漏检和误报的计数,分别除以对应的真实样本数,得到两个比率。参数上,cm的行列顺序要和 YOLOv5 输出一致,背景类在最后。跑完看哪个比率高,就按上面说的方向调。漏检率高于 0.15 基本不能接受,误报率高于 0.1 在告警场景里会烦人。

我自己的习惯是:每换一次数据增强策略或 anchor,都重新跑一遍这个统计,把漏检率和误报率记在一张表里。调参调到最后,往往不是某个参数一改就灵,而是几个参数配合着把两个错误率压到可接受范围。吸烟检测这种场景,漏检和误报的代价不对称——漏检可能意味着安全隐患,误报多了运维会直接关掉告警。先想清楚你的场景更怕哪个,再决定往哪边偏。希望帮到你。

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

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

LeetCode 64最小路径和:Java动态规划与滚动数组优化精讲

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/7 15:57:33

RV1126B芯片解析:AI-ISP与AOV3.0如何重构边缘视觉智能

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/7 15:56:21

OpenHarmony上Flutter GridView实战与性能优化

在 OpenHarmony 设备上跑 Flutter 并不算难,难的是把它用到一个真实页面里:数据要动起来、图片要加载、滚动要够稳、异常不能直接崩掉。这篇就围绕我看得最多也最常用的一个场景——GridView 网格视图——把 Flutter for OpenHarmony 从环境准备到实战落…

作者头像 李华
网站建设 2026/10/7 15:55:28

STM32F103寄存器级I2C驱动AT24C02实战:从GPIO配置到示波器时序验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/7 15:55:07

Vision-LSTM实战:用xLSTM序列模型做森林图像分类

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/7 15:54:59

Multisim数据选择器级联实战:74LS151升级32选1

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华