☰
吸烟数据集实战:991张图片与YOLOv8训练全流程
2026/10/5 4:51:54 网站建设 项目流程

简介:这份吸烟行为检测数据集面向计算机视觉方向的目标检测学习者与算法开发者,可用于训练和验证吸烟、抽烟场景下的识别模型,适合课程设计、毕业项目或算法对比实验等用途。资源包共收录1983个文件,其中991张jpg原始图片与991个txt标注文件一一对应,标注采用yolov8格式,另附1个yaml配置文件用于定义数据集路径与类别信息,压缩包整体约44.7MB,结构规整、开箱即用。据描述,基于该数据集训练的平均识别率可达88.3%,可作为吸烟检测任务的基线参考。目前已有110人学习下载。图片覆盖多种吸烟姿态与场景,标注文件可直接接入YOLO系列训练流程,省去自行采集与标注的成本,帮助读者快速搭建数据管线、复现检测效果并在此基础上做模型调优与精度对比。

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

手上这个吸烟数据集,第一次看到「991 张原始图片、平均识别率 88.3%、yolov8 格式标注」这三个数字摆在一起时,我的第一反应不是兴奋,而是先算了一笔账。991 张图,如果按 8:1:1 切分,验证集不到 100 张,单类目标检测任务里这个量级属于「能跑通、能出活,但别指望它扛住复杂场景」的档位。它适合谁?适合做吸烟行为识别原型验证的算法同学、需要快速搭一个抽烟检测 demo 的工程团队,以及拿它当 yolov8 训练自己数据集练手素材的新手。不适合谁?不适合直接拿去上生产做高精度合规检测,也不适合指望它覆盖多角度、多光照、多遮挡的开放场景。这份资源的核心价值在于:标注格式已经是 yolov8 可直接吃的 txt 结构,省掉了从 VOC、COCO 转格式那一步最容易翻车的环节,991 张图的体量也刚好够在一张消费级显卡上把完整训练流程走一遍。下面我按「先搞懂它是什么 → 再动手复现 → 最后说清楚坑在哪」的顺序拆开讲。

2. 数据集结构与 yolov8 标注格式:先看清 991 张图到底给了什么

2.1 目录组织与文件命名规律

从项目正文给出的文件名看,图片命名有两类:一类是纯数字加_jpg.rf.加一串哈希,比如1277_jpg.rf.9c8828412f0497d36739ffc3e4553b84.jpg;另一类是带连字符的编号,比如1000-10-_jpg.rf.c26c21d03f55d5e9ded35bb9d1955f12.jpg。这个rf.加 32 位十六进制哈希的命名风格,是 Roboflow 导出数据集时的典型特征,说明这份数据大概率是从标注平台直接导出的成品包,而不是手工整理的原始文件夹。

这种命名对训练本身没影响,但对排查问题有影响:哈希名不可读,你没法从文件名判断这张图是室内还是室外、是近景还是远景。所以拿到手第一件事不是急着训练,而是先把图片和标签配对检查一遍。常见做法是写个脚本统计图片数和标签数是否一致,再抽查几张可视化看看框的位置对不对。

import os from pathlib import Path img_dir = Path("datasets/smoking/images/train") lbl_dir = Path("datasets/smoking/labels/train") imgs = {p.stem for p in img_dir.glob("*.jpg")} lbls = {p.stem for p in lbl_dir.glob("*.txt")} # 图片有但标签缺失,或标签有但图片缺失,都会让训练报错或静默丢样本 missing_lbl = imgs - lbls missing_img = lbls - imgs print(f"图片总数: {len(imgs)}, 标签总数: {len(lbls)}") print(f"缺标签的图片: {len(missing_lbl)}") print(f"缺图片的标签: {len(missing_img)}")

这段脚本的逻辑很直接:用stem(去掉扩展名的文件名)做集合运算。参数上唯一要注意的是img_dir和lbl_dir的路径要按你实际解压后的结构改。如果missing_lbl不为零,训练时 yolov8 默认会跳过这些图,你不会收到明显报错,但实际参与训练的样本数就少了,这是很多人训完发现 mAP 对不上预期的隐藏原因。

2.2 yolov8 标签格式逐字段拆解

yolov8 的检测标签是每张图对应一个同名.txt,每行一个目标,格式是:

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

五个字段全部归一化到 0~1 之间。这里有两个新手最容易搞错的点:第一,x_center和y_center是框中心点坐标,不是左上角;第二,width和height是框的宽高,不是右下角坐标。如果你从 VOC 的xmin ymin xmax ymax直接搬过来,必须做转换,否则框会整体偏移甚至跑到图外。

字段含义取值范围常见错误
class_id类别索引,从 0 开始整数写成类别名而非数字
x_center框中心 x / 图宽0~1误填左上角 x
y_center框中心 y / 图高0~1误填左上角 y
width框宽 / 图宽0~1误填右下角 x
height框高 / 图高0~1误填右下角 y

吸烟检测通常是单类任务,也就是class_id恒为 0。如果你后续想区分「手持烟」「嘴叼烟」「烟雾」等子类,就得重新标注并调整nc参数,不能指望这份单类标注直接扩展。

2.3 用可视化脚本验证标注质量

在正式训练前,我一般会抽 20 张图把框画出来看一眼。这一步花不了几分钟,但能提前发现框偏移、漏标、类别错乱等问题,比训到一半才发现强得多。

import cv2 import random from pathlib import Path img_dir = Path("datasets/smoking/images/train") lbl_dir = Path("datasets/smoking/labels/train") for img_path in random.sample(list(img_dir.glob("*.jpg")), 20): img = cv2.imread(str(img_path)) h, w = img.shape[:2] lbl_path = lbl_dir / f"{img_path.stem}.txt" if not lbl_path.exists(): continue with open(lbl_path) as f: for line in f: cid, xc, yc, bw, bh = map(float, line.split()) # 反归一化回像素坐标 x1 = int((xc - bw / 2) * w) y1 = int((yc - bh / 2) * h) x2 = int((xc + bw / 2) * w) y2 = int((yc + bh / 2) * h) cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.imwrite(f"vis_{img_path.name}", img)

关键在反归一化那四行:先减半个宽高得到左上角,再加整个宽高得到右下角,最后乘回图像尺寸。跑完打开vis_开头的图,如果框都稳稳套在烟或抽烟动作上,说明标注可用;如果框整体偏一个方向,多半是归一化时用错了图宽图高,或者标注时用的尺寸和实际图片尺寸不一致。

3. 从零跑通 yolov8 训练:环境、配置与 88.3% 识别率的复现路径

3.1 环境配置与依赖版本选择

yolov8 现在统一走ultralytics这个包,不再像 yolov5 那样克隆仓库改代码。环境配置本身不复杂,但版本组合有讲究。我一般会固定 Python 3.9 或 3.10,PyTorch 选和 CUDA 匹配的版本,ultralytics用较新的稳定版即可。

conda create -n smoke python=3.10 -y conda activate smoke # 根据你的 CUDA 版本去 PyTorch 官网选对应命令,这里以 CUDA 11.8 为例 pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118 pip install ultralytics opencv-python

参数说明:python=3.10是兼容性比较稳的选择;PyTorch 的 index-url 一定要和你机器上的 CUDA 对齐,装错了会退化成 CPU 训练,速度差几十倍。装完用python -c "import torch; print(torch.cuda.is_available())"验证,输出True才算过关。这一步翻车的人不少,血泪经验是别用pip install torch裸装,它默认可能给你 CPU 版。

3.2 data.yaml 配置与路径陷阱

yolov8 训练靠一个data.yaml描述数据位置和类别。这份吸烟数据集是单类,配置很简洁:

path: /abs/path/to/datasets/smoking train: images/train val: images/val nc: 1 names: ['smoking']

这里最大的坑是path必须写绝对路径,train和val写相对path的子路径。很多人写成相对当前工作目录的路径,结果训练启动时报「找不到图片」。另外names的顺序就是class_id的映射,单类时只有smoking对应 0,别多加空格或中文,yaml 对缩进和引号敏感。如果你的验证集是单独切出来的,确保images/val和labels/val成对存在。

3.3 训练命令与关键超参设置

配置好之后,一条命令就能起训:

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

逐个说参数:model=yolov8n.pt用 nano 版,991 张图这个量级用大模型容易过拟合,n 版性价比最高;epochs=100配合patience=20,意思是 20 轮验证指标不涨就早停,避免无效训练;imgsz=640是默认输入尺寸,如果你的图里烟的目标很小,可以提到 960 但显存占用会上升;batch=16在 8G 显存上比较稳,显存不够就降到 8。lr0=0.01是初始学习率,小数据集不建议调太大,否则 loss 震荡。

训练过程中重点看两个东西:一是mAP50和mAP50-95的曲线,二是train/box_loss和val/box_loss的分离程度。如果训练 loss 一直降但验证 loss 早早抬头,就是过拟合信号,这时候要么加数据增强,要么减模型容量。88.3% 这个识别率大概率指的是mAP50或某种场景下的准确率,具体口径要以你复现时的验证结果为准,别默认它等于mAP50-95。

3.4 推理验证与结果解读

训完在验证集上跑一遍:

yolo detect val \ model=runs/smoke/exp1/weights/best.pt \ data=datasets/smoking/data.yaml \ imgsz=640

输出会给出每类的 precision、recall、mAP50、mAP50-95。单类任务重点看 recall,因为漏检一个抽烟行为往往比误检代价高。如果 recall 明显低于 precision,说明模型偏保守,可以适当降低推理时的conf阈值。想直观看点检效果,用yolo detect predict指定一张图或一个文件夹,输出图会画好框。这里提醒一句:验证集指标好看不代表真实场景好用,991 张图的验证集本身就有随机性,多跑几次不同切分才能看出稳定性。

4. 避坑与排查:这份吸烟数据集最容易翻车的五个地方

4.1 训练报「No labels found」

现象:启动训练后日志里出现No labels found,或者WARNING: corrupt image/label,训练照跑但 mAP 一直是 0。

原因:九成是路径问题。data.yaml里的path没写绝对路径,或者train指向的目录下只有图片没有同名 txt,又或者图片和标签被放在了同一层但 yolov8 期望的是images/和labels/平行结构。

解决:先用第 2.1 节的配对脚本确认图片和标签数量一致,再检查目录结构是不是images/train配labels/train。yolov8 找标签的规则是把路径里的images替换成labels,所以目录名不能随便改。

4.2 显存溢出导致训练中断

现象:训练跑了几十轮突然CUDA out of memory,或者一开始就报错起不来。

原因:batch或imgsz设太大,或者没开混合精度。991 张图虽然不大,但 640 分辨率下 batch=16 在 6G 显存卡上就可能吃紧。

解决:优先降batch到 8 或 4,其次考虑imgsz=512。也可以在训练命令里加amp=True开自动混合精度,显存占用能降一截。别一上来就换大显存卡,先把参数调合理。

4.3 识别率远低于 88.3%

现象:自己训完 mAP50 只有 70% 出头,和标称的 88.3% 差一大截。

原因:数据切分方式不同、随机种子不同、模型规模不同都会造成差异。另外如果验证集里混入了训练集图片(数据泄漏),指标会虚高;反过来如果验证集切得太难,指标会偏低。

解决:固定随机种子,用相同的切分比例多跑几次取平均。确认验证集和训练集没有重叠。如果还是低,检查标注质量,用 2.3 节的可视化脚本抽查,看有没有大量漏标或框偏移。88.3% 是一个参考值,不是保证值。

4.4 推理时框位置整体偏移

现象:预测出来的框系统性偏左上或偏右下,或者框比实际目标大一圈。

原因:训练时的imgsz和推理时的imgsz不一致,或者图片在预处理时被拉伸而非等比例缩放,导致归一化坐标还原时出错。

解决:训练和推理保持相同的imgsz。yolov8 默认会做 letterbox 等比例填充,一般不用手动改,但如果你自己写了预处理代码,要确保和训练时一致。用 2.3 节的反归一化逻辑对照检查一遍。

4.5 单类数据集误加多类配置

现象:data.yaml里nc写了大于 1 的值,或者names里列了多个类别,训练不报错但结果混乱。

原因:直接套用了其他多类数据集的配置文件,忘了这份吸烟数据是单类。

解决:确认nc: 1,names: ['smoking']。如果标签文件里出现了大于 0 的class_id,说明标注有问题,需要回去检查标注源文件。单类任务里任何非 0 的类别索引都会被当成无效或错误类别处理。

5. 进阶技巧:把 991 张图的价值榨干

数据量不大时,提升效果的关键不在换模型,而在把现有数据用足。我一般会从三个方向下手。第一是数据增强,yolov8 内置了 mosaic、mixup、HSV 抖动等增强,默认开启一部分,但你可以针对吸烟场景调参:抽烟动作常出现在室内弱光环境,适当加大hsv_v的扰动范围能提升暗光鲁棒性;烟的目标偏小,mosaic对小目标友好,保持默认 1.0 即可。第二是交叉验证,991 张图做 5 折,每折验证集约 200 张,把 5 折的 mAP 平均后比单次切分可信得多,也能顺便看出模型对数据分布的敏感度。

# 5 折交叉验证的切分思路:先按 8:2 切出固定测试集,剩余做 5 折 # 每折训练命令只需改 data.yaml 里的 train/val 指向 yolo detect train data=datasets/smoking/fold1.yaml model=yolov8n.pt epochs=100 imgsz=640 batch=16

第三是难例挖掘。训完第一版后,用best.pt在验证集上跑推理,把漏检和误检的图挑出来单独看。如果发现某类场景(比如侧脸抽烟、远距离小目标)系统性失败,要么补标这类数据,要么在增强里针对性模拟。这一步是 991 张图能不能逼近甚至超过 88.3% 的关键,因为小数据集的瓶颈往往不是模型不够强,而是数据覆盖不够全。

还有一个容易被忽略的点:导出模型时的格式选择。如果你后续要部署到边缘设备,yolo export model=best.pt format=onnx导出 ONNX 是常见做法,但要注意 opset 版本和推理引擎的兼容性。导出后务必用同一张图对比 PyTorch 和 ONNX 的输出,确认框坐标一致再上线,否则会出现「训练时好好的,部署后框全乱」这种黑匣子问题。

从那以后我每次拿到新数据集,都强制先跑一遍图片标签配对检查和 20 张可视化抽查,再动训练命令。这个习惯帮我省下的返工时间,远比多训几个 epoch 值。希望帮到你。

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

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

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

立即咨询