ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

YOLO吸烟检测实战:从smoking-dataset-yolo-1.zip到部署避坑

YOLO吸烟检测实战:从smoking-dataset-yolo-1.zip到部署避坑 简介这份YOLO是否吸烟检测数据集面向计算机视觉学习者与目标检测开发者用于训练和验证吸烟行为识别模型可应用于公共场所禁烟监控、工地安全巡检等场景。数据集已按train、val、test完成划分并附带data.yaml配置文件类别涵盖no_smoking、no_smoking_area、smoking三类yolov5、yolov7、yolov8等主流框架可直接读取训练省去自行标注与整理目录的繁琐工作。压缩包共412个文件以204个jpg图像与204个txt标注文件为主另含4个yaml配置文件整体约11.48MB体积轻便便于快速下载与本地部署。目前已有658人学习下载说明该数据集在实际训练中具备一定参考价值。读者可直接获得开箱即用的目录结构与标注数据快速复现检测流程并对照检测结果示例理解吸烟区域与禁烟区域的区分方式适合入门练手与课程实验使用。1. 拿到 smoking-dataset-yolo-1.zip 先别急着解压吸烟检测到底难在哪吸烟检测这个方向看起来只是「人 烟」两个类别的目标检测但真正做过的人都知道它比通用 COCO 检测难得多。烟头在画面里往往只有十几个像素吸烟动作又和打电话、托腮、手遮嘴高度相似加上室内逆光、夜间补光、摄像头俯角这些现实条件模型很容易把手指、笔、吸管误判成香烟。smoking-dataset-yolo-1.zip这类 YOLO 格式吸烟检测数据集解决的正是「我不想从零标注几千张图想直接拿一份已经转好 YOLO txt 标签的数据开跑」这个诉求。它适合三类人做园区/工地/加油站/校园禁烟监控的算法工程师想拿吸烟检测当毕设或课程项目的学生以及需要快速验证 YOLO 训练链路是否跑通的部署同学。这一章先把数据集的构成、YOLO 标签格式和吸烟检测的固有难点讲清楚后面几章再落到解压、划分、训练、调参和踩坑。YOLO 格式的检测数据集本质就是「图片文件夹 同名 txt 标签文件夹 一份 data.yaml」。每行标签是class_id x_center y_center width height五个值全部归一化到 0~1。吸烟检测常见的类别设置有两种一种是单类smoking只框香烟或烟雾区域另一种是personsmoking两类先框人再框烟。这两种标注策略直接决定后面训练出来的模型是「检测烟头」还是「检测吸烟行为」选错了后面怎么调都别扭。smoking-dataset-yolo这个关键词下流传的包多数是单类或双类解压后第一件事不是训练而是统计类别分布和框的尺寸分布否则你根本不知道自己的数据是不是长尾。吸烟检测真正的难点集中在三处。第一是目标极小香烟在 1080P 画面里可能只占 20×8 像素YOLO 下采样到 P3 特征图stride 8时只剩两三个格子召回天然吃亏。第二是类间混淆手指夹笔、叼吸管、摸耳朵在低分辨率下和吸烟几乎同分布模型只能靠上下文嘴部位置、手部姿态区分。第三是场景域偏移训练集多是白天室内部署到夜间走廊或逆光门口mAP 能掉十几个点。理解这三点你才知道为什么后面要调imgsz、要做难例挖掘、要单独看小目标的召回而不是无脑堆 epoch。2. 解压、校验与 YOLO 标签格式核对把 smoking-dataset-yolo-1.zip 变成能训练的数据拿到压缩包之后最忌讳的就是直接unzip然后yolo train dataxxx.yaml。吸烟检测数据集在流传过程中经常出现标签错位、图片和 txt 不同名、类别 id 从 1 开始YOLO 要求从 0 开始这些问题不先校验训练时 loss 会莫名其妙地不降你还以为是模型问题。这一章把「解压 → 目录结构确认 → 标签合法性校验 → 训练/验证划分」这条链路走一遍每一步都给可复现的命令和脚本。2.1 解压后的目录长什么样先确认三件事常见做法是先把包解到一个干净目录再看结构。不同来源的包结构不完全一样但 YOLO 训练需要的核心就三样图片、同名 txt、data.yaml。下面这套命令帮你把结构摸清楚。# 解压到独立目录避免污染当前工作区 mkdir -p ~/datasets/smoking cd ~/datasets/smoking unzip -q ~/smoking-dataset-yolo-1.zip -d raw # 看顶层结构判断是 images/labels 分离还是混放 find raw -maxdepth 2 -type d | head -30 # 统计图片数量和标签数量两者必须一致 find raw -type f \( -iname *.jpg -o -iname *.png -o -iname *.jpeg \) | wc -l find raw -type f -iname *.txt | wc -l # 看有没有现成的 data.yaml find raw -iname *.yaml -o -iname *.yml逻辑说明find -maxdepth 2只列两层目录避免输出爆炸图片和 txt 数量必须相等如果 txt 比图片多说明有孤立标签训练时会被忽略或报错如果图片比 txt 多说明有图没标这些图会被当成纯背景可能拉低召回。参数上-iname忽略大小写因为有些包用.JPG。如果发现图片和标签混在同一个文件夹需要先按同名规则拆成images/和labels/两个目录YOLO 的默认 dataloader 是按路径替换images→labels找标签的。提示解压后先别删原始压缩包校验阶段如果发现标签损坏原始包是唯一的后悔药。2.2 用一段 Python 校验标签合法性越界、类别 id、空文件YOLO 标签最常见的三类错误坐标越界值 1 或 0、类别 id 不是从 0 连续、空 txt 文件。这三类问题在训练时不会直接报错而是表现为 loss 震荡或某些类永远学不出来。下面这段脚本一次性把问题列出来。import os from pathlib import Path from collections import Counter root Path.home() / datasets/smoking/raw img_exts {.jpg, .jpeg, .png} imgs {p.stem for p in root.rglob(*) if p.suffix.lower() in img_exts} labels list(root.rglob(*.txt)) bad_coord, bad_id, empty, orphan [], [], [], [] cls_counter Counter() for lp in labels: if lp.stem not in imgs: orphan.append(lp.name) lines [l.strip() for l in lp.read_text().splitlines() if l.strip()] if not lines: empty.append(lp.name) continue for ln in lines: parts ln.split() if len(parts) ! 5: bad_coord.append((lp.name, ln)) continue cid int(float(parts[0])) cls_counter[cid] 1 vals [float(v) for v in parts[1:]] if any(v 0 or v 1 for v in vals): bad_coord.append((lp.name, ln)) if cid 0: bad_id.append((lp.name, ln)) print(类别分布:, dict(cls_counter)) print(越界/格式错误:, len(bad_coord), bad_coord[:5]) print(空标签文件:, len(empty), empty[:5]) print(孤立标签(无同名图):, len(orphan), orphan[:5])逻辑说明先用图片 stem 建集合再遍历所有 txtorphan就是找不到同名图的标签cls_counter直接告诉你类别 id 分布如果出现 id1 但 id0 不存在说明标注工具从 1 开始编号需要整体减 1坐标越界通常是标注时没归一化或归一化除了错的分辨率。参数上len(parts) ! 5能抓出用逗号分隔或带置信度的旧格式。跑完这段你对自己的数据就有底了。2.3 划分训练集和验证集别用随机划分坑自己吸烟检测数据如果来自连续视频抽帧相邻帧高度相似随机划分会让验证集里出现和训练集几乎一样的图mAP 虚高。正确做法是按视频源或时间段划分。如果包里没有源信息退而求其次按文件名前缀或时间戳分组划分。import random, shutil from pathlib import Path random.seed(42) raw Path.home() / datasets/smoking/raw out Path.home() / datasets/smoking/split for sub in [images/train, images/val, labels/train, labels/val]: (out / sub).mkdir(parentsTrue, exist_okTrue) imgs sorted([p for p in raw.rglob(*) if p.suffix.lower() in {.jpg, .jpeg, .png}]) # 按文件名前缀分组同组进同一侧避免相邻帧泄漏 groups {} for p in imgs: key p.stem.split(_)[0] groups.setdefault(key, []).append(p) keys list(groups.keys()) random.shuffle(keys) val_keys set(keys[: max(1, int(len(keys) * 0.15))]) for k, ps in groups.items(): split val if k in val_keys else train for p in ps: shutil.copy(p, out / images / split / p.name) lp p.with_suffix(.txt) if lp.exists(): shutil.copy(lp, out / labels / split / lp.name) print(train groups:, len(keys) - len(val_keys), val groups:, len(val_keys))逻辑说明p.stem.split(_)[0]是假设文件名形如video01_000123.jpg同一视频的帧归为一组整组进 train 或 val杜绝帧级泄漏。验证集比例 15% 是检测任务的常见起点数据量小于 2000 张时可以降到 10%。参数random.seed(42)保证可复现。划分完记得生成 data.yamlpath指向 split 目录train/val写相对路径names按校验脚本打印的类别顺序填。3. 用 YOLOv8 跑通第一版吸烟检测从 data.yaml 到可用的 best.pt数据准备好之后第一版训练的目标不是刷高 mAP而是确认整条链路能跑通、loss 正常下降、验证集能出图。这一章用 YOLOv8 做基线YOLOv5/v11 命令大同小异把 data.yaml 写法、训练命令、关键参数含义、训练中该看什么指标讲清楚。很多人第一次训吸烟检测卡在「loss 不降」或「mAP 一直是 0」八成是 data.yaml 路径或类别数写错了。3.1 data.yaml 的四个字段写错一个就白训path: /home/user/datasets/smoking/split train: images/train val: images/val nc: 1 names: [smoking]逻辑说明path是数据集根目录train/val是相对path的路径YOLO 会用images替换成labels去找标签所以目录名必须是images和labels这种约定结构。nc是类别数必须和names长度一致且和标签里的最大 class_id 1 一致。如果标签里出现过 id1 而nc1训练时这个框会被丢弃或报 index 越界。names的顺序就是 class_id 的顺序不能随便调。常见翻车是path写了相对路径训练时工作目录一变就找不到数据建议一律写绝对路径。3.2 第一版训练命令与必调参数yolo detect train \ data/home/user/datasets/smoking/split/data.yaml \ modelyolov8s.pt \ epochs100 \ imgsz960 \ batch16 \ device0 \ workers8 \ projectruns/smoking \ namebaseline \ patience30 \ cacheTrue逻辑说明modelyolov8s.pt是速度和精度的平衡点吸烟检测目标小n 版召回不够l/x 版在单卡上太慢s 是合理起点。imgsz960是关键默认 640 下香烟只剩几个像素提到 960 甚至 1280 对小目标召回提升明显代价是显存和耗时。batch16在 16G 显存 960 分辨率下比较稳OOM 就降到 8。patience30是早停30 轮验证指标不升就停省时间。cacheTrue把图片缓存到内存小数据集能显著加速但数据超过 10G 就别开。workers8是 dataloader 进程数按 CPU 核数调Windows 下建议设 0 避免多进程问题。参数上还有两个容易被忽略的close_mosaic默认最后 10 轮关闭 mosaic 增强吸烟检测里 mosaic 会把四张图拼一起小目标更小前期有帮助但后期要关cos_lrTrue用余弦退火学习率比默认的线性更稳。第一版先别动学习率lr00.01是 YOLOv8 默认跑通再说。3.3 训练中该盯哪几个指标别只看 mAP训练日志里指标很多吸烟检测重点看四个。box_loss和cls_loss要持续下降如果 cls_loss 在 0.6 附近横盘多半是类别混淆或标签有问题。metrics/mAP50看整体metrics/mAP50-95看框的精度吸烟检测里 mAP50 高但 mAP50-95 低说明框位置不够准小目标常见。最关键的是metrics/recall禁烟监控宁可误报也别漏报recall 低于 0.7 就要考虑提分辨率或加难例。混淆矩阵在runs/smoking/baseline/下看smoking那一行有没有大量被分到 background被分到 background 多说明漏检严重。注意验证集 mAP 高不代表部署效果好。如果验证集和训练集同源指标会虚高务必留一部分不同场景的图做独立测试。4. 吸烟检测的避坑清单小目标、误检、显存与标签的五类翻车这一章是血泪经验合集。吸烟检测从数据到部署翻车点集中在五个地方每个都按「现象 → 原因 → 解决」写方便你对照排查。4.1 现象训练 loss 正常降但验证集 mAP 一直是 0原因data.yaml 的nc和标签实际类别数不一致或者names顺序和标签 id 对不上。YOLO 在验证时按nc解析输出对不上就全判错。另一个可能是验证集路径写错实际验证的是空集。解决回到 2.2 的校验脚本打印cls_counter确认最大 id 1 等于nc。再确认val路径下确实有图片和同名标签。用yolo detect val dataxxx.yaml modelbest.pt单独跑验证看输出里val: Found X images的数量对不对。4.2 现象烟头漏检严重recall 上不去原因香烟目标太小640 分辨率下 P3 特征图只剩两三个格子anchor 匹配不上。或者训练集里小目标占比低模型偏向学大目标。解决把imgsz提到 960 或 1280这是最直接的手段。其次在 data.yaml 里开copy_paste增强YOLOv8 支持把小目标复制粘贴到其他图上增加密度。还可以用 SAHI 切片推理把大图切成小图分别检测再合并部署阶段对小目标提升明显。如果数据允许单独统计框面积小于 32×32 的样本比例低于 20% 就要补小目标数据。4.3 现象手指、笔、吸管被误判成香烟误报率高原因这些物体在低分辨率下和香烟纹理、形状高度相似模型只学了局部特征没学上下文。训练集里负样本不含烟但含手/笔的图太少。解决往训练集里加纯背景负样本尤其是手部特写、拿笔、喝水这些场景标签为空 txt 即可。YOLO 会把空标签图当负样本学。另外可以调低conf阈值做误报分析把误报图挑出来加进训练集做难例挖掘。如果误报集中在某个固定场景比如某个摄像头针对该场景补数据比调参有效。4.4 现象训练到一半显存爆了或者 batch 调不上去原因imgsz和batch是显存的主要消耗项960 分辨率下 batch16 在 16G 卡上接近上限。开了cacheTrue还会额外占内存。多卡训练时batch是单卡值总 batch 是乘以卡数。解决优先降batch到 8再不行降imgsz到 768。用yolo detect train ... batch-1让 YOLO 自动找最大 batchAutoBatch但自动值有时偏激进建议手动留 10% 余量。cacheTrue在内存不足时改成cachedisk或关掉。混合精度ampTrue是默认开的别关能省不少显存。4.5 现象标签校验没问题但训练时提示某张图标签越界原因坐标值刚好等于 1.0 或略大于 1.0浮点误差YOLO 严格校验会报错。或者标注时用了像素坐标没归一化。解决在校验脚本里把v 1改成v 1.0001容忍浮点误差或者统一 clip 到 [0,1]。如果是像素坐标需要除以图片宽高重新归一化。这类问题在手工标注或格式转换的数据集里很常见训练前跑一遍校验能省很多时间。5. 把吸烟检测推到能用小目标增强、阈值调优与部署前验证第一版跑通之后真正决定这个方案值不值得投入的是它能不能在真实场景里稳定工作。这一章讲三个进阶动作针对小目标的训练增强、推理阶段的阈值和 NMS 调优、以及部署前的独立验证方法。最后说一个我自己踩过的习惯性教训。5.1 小目标增强从数据侧和推理侧同时下手训练侧除了提imgszYOLOv8 的scale和mosaic参数对小目标有直接影响。scale0.5表示随机缩放 50%缩放后小目标更小反而有害吸烟检测建议scale0.3左右。mosaic1.0前期开close_mosaic20让最后 20 轮关掉让模型在真实尺度上收敛。如果数据量够可以试copy_paste0.3把小目标烟头复制到其他图上直接增加小目标密度。推理侧SAHISlicing Aided Hyper Inference是吸烟检测的实用技巧。把 1080P 图切成 640×640 的重叠切片每片单独推理再合并小目标在切片里相对变大召回提升明显。代价是推理耗时增加适合对召回要求高的禁烟监控。下面是一个最小示例。from sahi import AutoDetectionModel from sahi.predict import get_sliced_prediction model AutoDetectionModel.from_pretrained( model_typeyolov8, model_pathruns/smoking/baseline/weights/best.pt, confidence_threshold0.25, devicecuda:0, ) result get_sliced_prediction( test_night.jpg, model, slice_height640, slice_width640, overlap_height_ratio0.2, # 切片重叠 20%避免目标被切断 overlap_width_ratio0.2, ) result.export_visuals(export_dirsahi_out/)逻辑说明overlap_ratio0.2保证切片边界的目标至少完整出现在一个切片里confidence_threshold0.25比训练默认低因为切片后单目标置信度会略降靠后续 NMS 合并。SAHI 的合并逻辑会处理跨切片的重复框不用自己写。参数上切片尺寸建议和训练imgsz一致避免尺度不匹配。5.2 推理阈值与 NMS误报和漏报的平衡杆部署时conf和iou两个阈值直接决定误报漏报。conf是置信度阈值调高减少误报但增加漏报iou是 NMS 的重叠阈值吸烟检测里如果一个人嘴里和手上都有烟iou太高会合并成一个框。禁烟监控场景我一般从conf0.3, iou0.5起步然后拿一批真实场景图跑统计误报和漏报再微调。如果误报集中在某类物体先补负样本别只靠调阈值阈值调高会把真烟也滤掉。场景confiou说明室内白天监控0.350.5光照好置信度普遍高夜间走廊0.250.45补光下置信度偏低放宽阈值加油站/工地0.40.5误报代价高宁可漏报移动端/边缘设备0.30.5算力有限配合 SAHI 按需开5.3 部署前必须做的独立验证训练集的验证集指标只能说明模型拟合得好不能说明部署可用。我的习惯是单独准备一批「部署场景图」来源和训练集不同摄像头、不同时间段、不同光照至少 200 张人工标好。用这批图跑yolo detect val看 mAP 和 recall 掉多少。如果掉超过 15 个点说明域偏移严重要么补数据要么做场景适配。另外一定要看误报图把误报最高的 20 张挑出来分析是哪些物体被误判这比看指标有用得多。提示部署前用yolo export formatonnx导出 ONNX再用 ONNX Runtime 跑一遍确认导出后精度和 PyTorch 一致。导出时imgsz要和训练一致dynamicTrue支持变分辨率但会略降速度。5.4 一个我自己的习惯我早期做吸烟检测最常犯的错是「指标好看就上线」。有一次验证集 mAP50 到 0.89上线后夜间误报率飙到每小时几十次原因是训练集全是白天室内夜间红外补光下香烟和反光点几乎同分布。后来我给自己定了个规矩任何检测模型上线前必须拿至少一个「没参与训练的场景」跑一遍误报图逐张看。这个习惯帮我省了不止一次返工。吸烟检测这个方向数据场景的覆盖度比模型结构重要得多smoking-dataset-yolo-1.zip是个不错的起点但它能不能用取决于你补了多少自己的场景数据。希望帮到你。本文还有配套的精品资源点击获取
返回列表