☰
YOLOv8吸烟检测实战:991张数据集训练与88.3%识别率复现
2026/10/1 3:40:49 网站建设 项目流程

简介:这份吸烟行为检测数据集面向计算机视觉开发者与目标检测学习者,适用于公共场所吸烟识别、行为分析等场景的模型训练与验证。资源共包含991张原始图片,全部采用YOLOv8格式标注,每张图片配有对应的txt标签文件,另附1个yaml配置文件用于类别与路径定义,压缩包整体约44.7MB,文件总数1983个,以jpg图像与txt标注为主,结构规整、开箱即用。据描述,该数据集在训练后平均识别率可达88.3%,可作为基线参考,帮助读者快速搭建吸烟检测模型、验证算法效果或进行迁移学习。目前已有110人学习关注,适合需要现成标注数据、希望省去采集与标注成本的中初级视觉开发者,也便于在此基础上扩充样本、调优模型。

1. 吸烟检测数据集落地:991 张原始图片与 88.3% 识别率的真实边界

吸烟行为检测在智慧工地、加油站、化工厂、公共交通等场景里是刚需,但真正动手做的时候,第一个卡住大多数人的不是模型结构,而是数据。公开的吸烟数据集本来就少,能直接拿来训练 YOLOv8 的更少,标注格式对不上、类别定义模糊、负样本缺失,随便一个坑都能让 mAP 掉十几个点。这份资源给的是 991 张原始图片,已经按 YOLOv8 格式标注好,官方给出的平均识别率在 88.3%。它解决的是「从零标注到能跑通训练」这一段最耗人力的环节,适合两类人:一类是想快速验证吸烟检测方案可行性的算法工程师,另一类是手上有部署需求、但没精力从零攒数据集的嵌入式或边缘计算开发者。991 张不算大,但作为基线数据集,它足够让你把训练、验证、导出、部署这条链路完整走一遍,先跑通再谈优化。

2. 数据集结构与 YOLOv8 标注格式拆解:991 张图到底怎么组织

2.1 文件命名规律与图片规格

从项目正文列出的文件名能看出,图片命名是「数字编号 + _jpg.rf. + 一串哈希」的形式,比如1277_jpg.rf.9c8828412f0497d36739ffc3e4553b84.jpg、1023_jpg.rf.2a12f93b37d6f0cb5bcb568dd60b4b65.jpg。这种命名是典型的标注平台导出风格,rf.后面那串是导出时生成的唯一标识,用来防止重名覆盖。编号本身不连续,说明原始素材经过了一轮筛选和清洗,不是简单按顺序截帧。图片格式统一是 JPG,这对训练是好事——YOLOv8 的 dataloader 对 JPG 解码支持最成熟,不会像 PNG 透明通道那样在某些版本上出玄学问题。

实际拿到手之后,建议先做一次文件完整性检查,确认 991 张图都能正常解码,避免训练到一半报Corrupt JPEG data。常见做法是用 PIL 批量过一遍:

from PIL import Image import os img_dir = "datasets/smoking/images/train" bad = [] for name in os.listdir(img_dir): path = os.path.join(img_dir, name) try: img = Image.open(path) img.verify() # 校验文件完整性,不真正解码像素 except Exception as e: bad.append((name, str(e))) print(f"总检查 {len(os.listdir(img_dir))} 张,异常 {len(bad)} 张") for n, e in bad: print(n, e)

这段代码的逻辑是:Image.open只读文件头,verify()检查文件是否被截断或损坏,比直接load()快很多。参数上img_dir换成你实际的训练集图片目录即可。如果异常数量超过个位数,说明下载或解压过程出了问题,别急着训练,先重新获取。

2.2 YOLOv8 标注格式与类别定义

YOLOv8 的检测标注是每张图对应一个同名.txt文件,每行格式为:

<class_id> <x_center> <y_center> <width> <height>

后四个值都是归一化到 0~1 的相对坐标,x_center、y_center是框中心点,width、height是框宽高。这一点和 YOLOv5 完全一致,所以如果你之前有 v5 的数据集,格式上可以直接复用。吸烟检测通常是单类别,class_id就是 0,对应smoke或smoking。但这里有个容易翻车的地方:有些标注平台导出时会把「吸烟」和「香烟」分成两类,或者把「手持香烟」单独标一类。拿到数据集后第一件事就是看data.yaml里的names列表,确认类别数和你的业务定义一致。

# data.yaml 典型结构 path: ./datasets/smoking train: images/train val: images/val nc: 1 names: 0: smoking

nc是类别数,names是索引到类名的映射。如果你的场景需要区分「正在吸烟」和「手持未点燃」,那这个单类别数据集就不够用,得自己补标。991 张图按常见 8:2 划分,训练集约 793 张,验证集约 198 张。这个量级下,验证集只有不到 200 张,评估指标的波动会比较大,88.3% 这个数字要结合置信度阈值和 IoU 阈值一起看,不能只看一个孤立的百分比。

2.3 目录组织与训练前自检

YOLOv8 对目录结构有约定,推荐按下面这样组织:

smoking_dataset/ ├── data.yaml ├── images/ │ ├── train/ │ └── val/ └── labels/ ├── train/ └── val/

图片和标签必须同名,只是扩展名不同。常见错误是标签文件多了一层目录,或者图片是.JPG大写而标签是.txt小写导致匹配不上。训练前跑一段自检脚本,确认每张图都有对应标签、每个标签坐标都在 0~1 之间:

import os img_dir = "smoking_dataset/images/train" lbl_dir = "smoking_dataset/labels/train" missing, out_of_range = [], [] for name in os.listdir(img_dir): stem = os.path.splitext(name)[0] lbl_path = os.path.join(lbl_dir, stem + ".txt") if not os.path.exists(lbl_path): missing.append(name) continue with open(lbl_path) as f: for line in f: parts = line.strip().split() if len(parts) != 5: out_of_range.append((stem, "字段数不对")) continue vals = list(map(float, parts[1:])) if any(v < 0 or v > 1 for v in vals): out_of_range.append((stem, "坐标越界")) print("缺标签:", len(missing)) print("异常标签:", len(out_of_range))

这段脚本解决的是训练前最隐蔽的一类问题:标签坐标越界不会让训练直接报错,但会让模型学到一个偏移的框,最终表现为「框总是偏一点」。参数上img_dir和lbl_dir按实际路径改,len(parts) != 5能抓出多空格、少字段的情况。自检通过再开训,能省掉后面调参时怀疑人生的时间。

3. 从零跑通 YOLOv8 训练:环境、命令与参数怎么设

3.1 环境搭建与依赖版本

YOLOv8 通过ultralytics包分发,安装本身不复杂,但版本兼容性是血泪经验重灾区。截至我写这篇时的稳定组合是 Python 3.10 + PyTorch 2.x + ultralytics 8.x。如果你在 Ubuntu 20.04 上跑 CPU 版本,先确认 glibc 版本够用,再装依赖:

conda create -n smoke python=3.10 -y conda activate smoke pip install torch torchvision --index-url https://download.pytorch.org/whl/cpu pip install ultralytics

第一行建虚拟环境,第二行激活,第三行装 CPU 版 PyTorch——注意--index-url指向的是 CPU 专用源,如果你有 NVIDIA 显卡,换成对应的 CUDA 源。第四行装 ultralytics,它会自动拉取 opencv、numpy 等依赖。CPU 训练 991 张图,按 100 epoch 算,大概要几个小时到十几个小时,取决于机器性能。如果只是验证流程,先把epochs设成 10 跑通再说。

3.2 训练命令与关键参数含义

YOLOv8 的训练入口是命令行yolo detect train,也可以用 Python API。命令行方式更适合快速实验:

yolo detect train \ data=smoking_dataset/data.yaml \ model=yolov8n.pt \ epochs=100 \ imgsz=640 \ batch=16 \ lr0=0.01 \ patience=20 \ project=runs/smoke \ name=exp1

逐项说明:data指向你的data.yaml;model选yolov8n.pt是最小的 nano 版本,991 张图用 nano 足够,用 large 反而容易过拟合;epochs=100是上限,配合patience=20做早停——20 轮验证指标不提升就停;imgsz=640是输入分辨率,如果你的图片原始尺寸远大于 640,YOLOv8 会自动缩放;batch=16在 CPU 上可能偏大,显存或内存不够就降到 8 或 4;lr0=0.01是初始学习率,小数据集上这个值偏大时容易震荡,可以降到 0.005。project和name决定输出目录,权重、曲线图、混淆矩阵都会存在runs/smoke/exp1/下。

3.3 训练过程监控与指标解读

训练启动后,控制台会逐 epoch 打印 box_loss、cls_loss、mAP50、mAP50-95。这里要区分两个指标:mAP50 是 IoU 阈值 0.5 时的平均精度,mAP50-95 是 0.5 到 0.95 每隔 0.05 取一次再平均,后者更严格。88.3% 这个识别率,大概率指的是 mAP50 或者某个置信度下的准确率,具体要看评估口径。如果 mAP50 到 88% 但 mAP50-95 只有 50% 出头,说明框的位置还不够准,属于正常现象,小数据集上很难把高 IoU 阈值下的指标做高。

训练结束后,runs/smoke/exp1/下会有results.csv,可以用 pandas 画损失曲线:

import pandas as pd import matplotlib.pyplot as plt df = pd.read_csv("runs/smoke/exp1/results.csv") df.columns = df.columns.str.strip() # 列名可能带空格 plt.plot(df["epoch"], df["train/box_loss"], label="box_loss") plt.plot(df["epoch"], df["val/box_loss"], label="val_box_loss") plt.xlabel("epoch") plt.ylabel("loss") plt.legend() plt.savefig("loss_curve.png")

df.columns.str.strip()这行别省,ultralytics 导出的 CSV 列名有时带前导空格,不处理会 KeyError。看曲线时重点看验证损失有没有在训练损失还在降的时候先抬头,那就是过拟合的信号,早停参数就是干这个的。

4. 推理验证与 88.3% 识别率的复现口径

4.1 用验证集跑一遍指标

训练完不能只看训练日志,要用val模式在验证集上重新评估:

yolo detect val \ model=runs/smoke/exp1/weights/best.pt \ data=smoking_dataset/data.yaml \ imgsz=640 \ conf=0.25 \ iou=0.5

conf=0.25是置信度阈值,低于这个值的检测框会被丢弃;iou=0.5是 NMS 的 IoU 阈值,控制重叠框的合并。这两个参数直接决定你看到的精度和召回。把conf调高,精度上升但召回下降;调低则相反。88.3% 这个数字,一定要问清楚是在什么conf和iou下测的,否则没有可比性。我一般会跑三组conf(0.25 / 0.4 / 0.5)看指标变化,心里有个区间。

4.2 单张图片推理与结果可视化

验证指标是一回事,实际看图是另一回事。用predict模式跑几张图,肉眼确认框的位置:

yolo detect predict \ model=runs/smoke/exp1/weights/best.pt \ source=smoking_dataset/images/val \ conf=0.25 \ save=True \ project=runs/predict \ name=val_vis

source可以是单张图、目录或视频流。save=True会把带框的结果图存到runs/predict/val_vis/。重点看两类错误:漏检(有人吸烟但没框出来)和误检(把类似吸烟的动作框成吸烟)。吸烟检测的误检重灾区是「打电话」「托腮」「手持笔」这些手部靠近脸部的动作,如果验证集里这类负样本少,误检率会偏高。991 张图里如果负样本占比低,建议自己补一些负样本进去,哪怕不标注,作为背景图也能降低误检。

4.3 导出与部署格式选择

训练完的.pt权重是 PyTorch 格式,部署到不同平台要转格式。常见的有 ONNX、TensorRT、OpenVINO。如果目标是 RK3588 这类边缘芯片,通常走 ONNX 再转 RKNN;如果是 x86 服务器,ONNX Runtime 或 OpenVINO 都行。导出 ONNX 的命令:

yolo export \ model=runs/smoke/exp1/weights/best.pt \ format=onnx \ imgsz=640 \ opset=12 \ simplify=True

opset=12是 ONNX 算子集版本,太低会缺算子,太高某些推理引擎不支持,12 是比较稳的选择。simplify=True会调用 onnx-simplifier 做图优化,去掉冗余节点。导出后建议用 onnxruntime 跑一张图,和 PyTorch 的输出对比,确认数值误差在可接受范围内,别等到部署到板子上才发现对不上。

5. 避坑与常见问题排查:那些让 mAP 掉点的细节

5.1 现象:训练 loss 正常下降但 mAP 一直上不去

原因通常有两类。一是标签里有大量空文件或坐标越界,模型在学噪声;二是类别定义和评估时的类别对不上,比如data.yaml里nc=1但标签里出现了class_id=1。解决方法是回到 2.3 节的自检脚本,把标签全过一遍,同时确认names列表和标签里的class_id范围一致。如果标签里有class_id超出nc-1,ultralytics 会在训练时警告但不报错,很容易被忽略。

5.2 现象:验证集精度远高于实际场景表现

这是小数据集的典型问题。991 张图如果来源单一(比如都是同一个监控角度、同一批人),验证集和训练集分布几乎一样,指标会虚高。到了实际场景,光照、角度、人物衣着一变,精度就崩。解决办法是划分验证集时按场景或按人划分,而不是随机划分。如果原始数据没有场景标签,至少保证验证集里有一些和训练集明显不同的图片。另外,88.3% 这个数字要放在「同分布验证集」的语境下理解,跨场景部署时预期要打折扣。

5.3 现象:CPU 训练极慢或内存溢出

CPU 训练 YOLOv8 时,batch设太大直接 OOM,设太小又跑得慢。常见做法是先把imgsz降到 416 或 320 跑通流程,确认代码没问题后再升到 640。另外workers参数在 Windows 上设大于 0 容易出多进程问题,Linux 上可以设 4 或 8 加速数据加载。如果内存实在紧张,用yolov8n.pt而不是更大的模型,nano 版本在 CPU 上的推理速度也更能接受。

5.4 现象:推理时框重叠严重或同一目标多个框

这是 NMS 参数没调好。iou阈值设太高(比如 0.9),重叠框不会被合并;设太低(比如 0.3),相邻目标会被误合并。吸烟检测里,如果一个人手和脸离得近,框容易重叠,iou建议在 0.5~0.7 之间试。另外agnostic_nms参数在多类别时有用,单类别可以不管。如果同一目标出现多个框且置信度都差不多,检查是不是标注时同一个目标被标了多次,这种脏数据在 991 张里如果有几十张,就够让 NMS 表现异常。

5.5 现象:导出的 ONNX 推理结果和 PyTorch 不一致

先确认导出时的imgsz和推理时的输入尺寸一致,不一致会导致坐标缩放错误。其次检查预处理,PyTorch 推理时 ultralytics 内部做了 letterbox 填充,如果你自己写 ONNX 推理代码,要复现同样的 letterbox 逻辑,否则框会偏移。最后确认opset版本和推理引擎兼容,ONNX Runtime 对高版本 opset 的支持有滞后。建议导出后先用 onnxruntime 的 Python API 跑一张图,和 PyTorch 结果做数值对比,误差在 1e-3 以内算正常。

6. 小数据集提点技巧:从 88.3% 再往上走的几个实操方向

991 张图、88.3% 的基线,如果直接上生产还差一口气,有几个方向可以试。第一是数据增强,YOLOv8 默认开了 mosaic、HSV 抖动、随机翻转,但吸烟检测里「手部靠近脸部」这个动作对水平翻转敏感——翻转后左右手互换,可能引入不真实样本。我一般会把flipud和fliplr的概率调低,或者关掉fliplr,用degrees做小角度旋转代替。第二是补负样本,前面提过,误检主要来自相似动作,找 100~200 张「打电话」「托腮」「吃东西」的图,不标注,直接作为背景图放进训练集,能明显压误检。

第三是调整损失权重。YOLOv8 的box、cls、dfl三项损失有权重参数,小数据集上分类损失容易过拟合,可以适当降低cls权重,让模型更关注框的位置。具体在训练命令里加box=7.5 cls=0.5 dfl=1.5,这是 ultralytics 默认值附近微调,别一次改太多。第四是尝试更大的输入分辨率,如果原始图片里吸烟目标占画面比例很小,imgsz=640下目标可能只有几十个像素,升到 960 或 1280 能提升小目标召回,代价是训练和推理都变慢。第五是用预训练权重做冻结训练,先冻结 backbone 训 20 轮,再解冻全量微调,小数据集上这招比直接全量训练稳。

验证方法上,别只看 mAP。我会额外统计「漏检率」和「误检率」两个业务指标:漏检率 = 漏检数 / 实际正样本数,误检率 = 误检数 / 检测总数。这两个数字比 mAP 更贴近实际体验。跑完验证集后,把 FP 和 FN 的图单独挑出来看,比盯着曲线有用。

从那以后我每次拿到新数据集,都强制先跑一遍完整性自检和标签范围检查,再开训。这个习惯帮我省掉了至少三次「训练三天发现标签是坏的」的后悔药。希望这份 991 张的吸烟数据集能帮你把吸烟检测的基线快速立起来,剩下的就是在这个基础上补数据、调参数、做部署。希望帮到你。

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

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询