ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

基于YOLOv8的吸烟检测实战:991张图片数据集训练与88.3%识别率复现

基于YOLOv8的吸烟检测实战:991张图片数据集训练与88.3%识别率复现 简介这份吸烟行为检测数据集面向计算机视觉方向的学习者与算法开发者适用于目标检测模型的训练、微调与课堂实验可支撑公共场所吸烟行为识别、智能监控等场景的算法验证。资源共包含1983个文件以991张jpg原始图片与991个同名txt标注文件为主另附1个yaml配置文件用于声明类别与数据路径压缩包整体约44.7MB标注已整理为yolov8可直接读取的格式省去自行转换与清洗的步骤。图片覆盖多种人物姿态与拍摄角度标注框对应吸烟、抽烟行为目标便于直接投入训练与评估。据描述该数据集在常规训练条件下平均识别率可达88.3%可作为基线参考帮助读者快速对比不同模型与超参的效果。目前已有110人学习下载适合需要现成标注数据开展检测实践、课程设计或算法对比的初中级开发者使用。1. 吸烟数据集到底能拿来做什么991 张原始图片背后的真实门槛手上有一份 991 张原始图片的吸烟数据集yolov8 格式标注官方给出的平均识别率是 88.3%。这个数字放在目标检测里不算惊艳但放在一个只有千张量级的单类别数据集上已经能说明标注质量和场景一致性做得不错。很多人第一次拿到这类数据集第一反应是直接丢进 yolov8 训练脚本跑一遍看能不能复现这个 88.3%结果往往在 70% 上下徘徊然后开始怀疑数据有问题。实际上问题通常不在数据而在于没搞清楚这 991 张图覆盖了哪些场景、标注框的粒度是什么、以及 88.3% 是在什么验证集划分下测出来的。这份数据集适合三类人一是想快速验证 yolov8 在自己硬件上跑通全流程的工程师二是需要做吸烟行为检测原型、但不想从零标注的产品团队三是拿它当教学素材、带新人熟悉目标检测数据闭环的技术负责人。它解决的核心问题是省掉最耗时的数据采集和标注环节让你把精力放在模型训练、调参和部署上。但要注意991 张图属于小样本范畴直接套用 COCO 级别的训练策略大概率翻车后面会具体讲怎么调。2. 把 991 张图喂给 yolov8从目录结构到第一次训练2.1 先看清 yolov8 格式标注的目录长什么样yolov8 官方推荐的目录结构是 images 和 labels 分开存放且 train、val、test 三个子集各自独立。一份规范的吸烟数据集通常长这样smoking_dataset/ ├── images/ │ ├── train/ # 约 693 张 │ ├── val/ # 约 198 张 │ └── test/ # 约 100 张 ├── labels/ │ ├── train/ │ ├── val/ │ └── test/ └── data.yaml图片和标签文件名必须一一对应比如 images/train/001.jpg 对应 labels/train/001.txt。标签文件里每一行是一个目标格式为class_id x_center y_center width height后四个值都是归一化到 0 到 1 之间的浮点数。单类别吸烟检测的 class_id 就是 0。拿到数据集后第一件事不是训练而是检查标签完整性。我一般会跑一段脚本统计每个子集的图片数、标签数、空标签数以及标注框的宽高分布。空标签文件是合法的表示这张图里没有吸烟目标但如果空标签占比超过 30%说明负样本太多需要调整采样策略。import os from pathlib import Path from collections import Counter dataset_root Path(smoking_dataset) for split in [train, val, test]: img_dir dataset_root / images / split lbl_dir dataset_root / labels / split imgs list(img_dir.glob(*.jpg)) list(img_dir.glob(*.png)) empty, total_boxes 0, 0 box_areas [] for img in imgs: lbl lbl_dir / (img.stem .txt) if not lbl.exists(): print(f缺失标签: {lbl}) continue lines [l.strip() for l in lbl.read_text().splitlines() if l.strip()] if not lines: empty 1 continue for line in lines: parts line.split() w, h float(parts[3]), float(parts[4]) box_areas.append(w * h) total_boxes 1 print(f{split}: 图片 {len(imgs)} 张, 空标签 {empty} 张, 标注框 {total_boxes} 个) if box_areas: print(f 框面积占比 min{min(box_areas):.4f} max{max(box_areas):.4f} fmean{sum(box_areas)/len(box_areas):.4f})这段脚本做了三件事统计每个子集的图片数量、找出缺失标签的图片、统计标注框的面积分布。框面积占比能帮你判断目标尺度如果 mean 低于 0.05说明大量目标是小目标训练时 imgsz 不能设太小否则小目标特征在深层特征图上会消失。2.2 data.yaml 里三个必须改对的字段data.yaml 是 yolov8 训练入口内容通常如下path: /home/user/smoking_dataset train: images/train val: images/val test: images/test nc: 1 names: [smoking]path 写数据集根目录的绝对路径train/val/test 写相对路径。nc 是类别数吸烟检测就是 1。names 是类别名列表顺序必须和标签里的 class_id 对应。这三个字段里最容易错的是 path很多人写相对路径结果训练时找不到图片报No labels found或者Dataset not found。另一个坑是 names 用了中文或带空格yolov8 解析 yaml 时不会报错但训练日志里类别名会乱码影响后续推理结果的可读性。2.3 第一次训练用哪条命令最稳环境装好后第一次训练不要直接上大模型。991 张图用 yolov8n 或 yolov8s 就够了yolov8m 以上在小样本上过拟合风险很高。我一般先用 yolov8n 跑 50 个 epoch 看 loss 曲线趋势确认数据管道没问题再换大模型。yolo detect train \ datasmoking_dataset/data.yaml \ modelyolov8n.pt \ epochs100 \ imgsz640 \ batch16 \ lr00.01 \ patience20 \ projectruns/smoking \ nameexp1参数逐个说data 指向 data.yamlmodel 用预训练权重yolov8n.pt 会自动下载epochs 设 100配合 patience20 做早停imgsz640 是标准输入尺寸如果显存不够降到 512 或 416batch16 在 8GB 显存上跑 yolov8n 没问题显存小就降到 8lr00.01 是初始学习率小样本建议比默认的 0.01 再低一点比如 0.005减少震荡。训练开始后重点看三个指标box_loss 是否稳定下降、cls_loss 是否在 20 个 epoch 内降到 1.0 以下、mAP50 是否在 50 个 epoch 后超过 0.8。如果 box_loss 下降但 mAP 不涨大概率是验证集和训练集分布不一致需要检查数据划分是否随机。3. 88.3% 识别率怎么复现验证集划分与评估参数3.1 验证集划分方式直接决定你看到的数字88.3% 这个数字如果没说明验证集怎么来的参考价值会打折扣。常见三种划分方式随机划分、按场景划分、按时间划分。随机划分最容易得到高指标因为同一场景的相似图片可能同时出现在训练集和验证集模型相当于见过类似样本。按场景划分最严格如果 991 张图来自多个不同场景按场景留出验证集指标通常会掉 5 到 10 个百分点。我一般会做两次评估一次用随机划分的验证集看模型上限一次用按场景划分的验证集看泛化能力。如果两者差距超过 15%说明数据场景多样性不足需要补充不同光照、不同角度的图片。import random from pathlib import Path import shutil src_imgs sorted(Path(smoking_dataset/images/all).glob(*.jpg)) random.seed(42) random.shuffle(src_imgs) val_ratio 0.2 val_count int(len(src_imgs) * val_ratio) val_imgs src_imgs[:val_count] train_imgs src_imgs[val_count:] for split, imgs in [(train, train_imgs), (val, val_imgs)]: for img in imgs: dst_img Path(fsmoking_dataset/images/{split}) / img.name dst_lbl Path(fsmoking_dataset/labels/{split}) / (img.stem .txt) shutil.copy(img, dst_img) shutil.copy(Path(smoking_dataset/labels/all) / (img.stem .txt), dst_lbl)这段脚本用固定随机种子做 8:2 划分保证可复现。seed42 是习惯用法换成别的数字也行但一旦定了就不要改否则每次划分结果不同指标没法对比。3.2 评估时看哪几个指标才不被忽悠yolov8 验证命令会输出一排指标重点看四个mAP50、mAP50-95、precision、recall。mAP50 是 IoU 阈值 0.5 时的平均精度88.3% 大概率指的是这个。mAP50-95 更严格通常比 mAP50 低 10 到 20 个百分点。precision 和 recall 要一起看如果 precision 高但 recall 低说明模型保守漏检多反过来则是误检多。yolo detect val \ modelruns/smoking/exp1/weights/best.pt \ datasmoking_dataset/data.yaml \ imgsz640 \ batch16 \ conf0.25 \ iou0.5conf0.25 是置信度阈值低于这个值的预测框会被过滤。iou0.5 是 NMS 的 IoU 阈值。这两个参数直接影响 precision 和 recall调高 conf 会提升 precision 但降低 recall。复现 88.3% 时先确认对方用的 conf 和 iou 是多少不同设置下指标能差好几个点。3.3 用混淆矩阵和 PR 曲线定位问题类别单类别检测没有多类别混淆问题但混淆矩阵仍然有用能看出背景被误判为吸烟目标的比例。PR 曲线则展示不同置信度阈值下 precision 和 recall 的权衡关系。如果 PR 曲线在 recall 0.6 之后急剧下降说明模型在高召回区间精度不够实际部署时要么接受漏检要么把 conf 调高牺牲召回。yolo detect val \ modelruns/smoking/exp1/weights/best.pt \ datasmoking_dataset/data.yaml \ plotsTrue \ save_jsonTrueplotsTrue 会在输出目录生成混淆矩阵、PR 曲线、F1 曲线等图。save_jsonTrue 会把每张图的预测结果存成 json方便后续做错误分析。我一般会挑出置信度在 0.3 到 0.5 之间的预测框人工看一遍这些是模型最不确定的样本往往对应遮挡、小目标或光照异常的情况。4. 小样本训练避坑991 张图最容易翻车的五个地方4.1 现象训练 loss 正常下降但验证 mAP 始终低于 0.6原因通常是数据泄漏或验证集太简单。991 张图如果来自视频抽帧相邻帧高度相似随机划分会让训练集和验证集出现近乎重复的图片模型记住了而不是学会了。解决方法是按视频来源或时间戳分组划分确保同一段视频的帧只出现在一个子集里。如果数据来源不明可以用图片哈希做去重把相似度高的图片归到同一组再划分。4.2 现象推理时大量误检背景被识别成吸烟原因是负样本不足或标注不一致。991 张图里如果正样本占绝大多数模型没见过足够多的“无吸烟”场景就会把类似吸烟动作的背景误判。解决办法是补充负样本或者用 mosaic 和 mixup 增强时提高负样本参与比例。另外检查标注有些图里手部遮挡导致吸烟目标只露出一小部分标注时如果漏标模型会学到错误的背景特征。4.3 现象小目标漏检严重远处吸烟的人检测不到原因是输入分辨率不够或 anchor 尺度不匹配。yolov8 默认 imgsz640如果原图里吸烟目标只占几十个像素缩放到 640 后特征更少。解决办法是把 imgsz 提到 960 或 1280但显存占用会成倍增加。另一个办法是切图推理把大图切成小块分别检测再合并适合监控场景。如果显存有限可以只对包含小目标的区域做高分辨率裁剪训练。4.4 现象训练到 30 个 epoch 后 mAP 不再提升loss 也不降原因是学习率太大导致在局部最优附近震荡或者数据增强过强。小样本训练时默认的 lr00.01 可能偏大改成 0.005 或 0.001 再配合余弦退火调度。数据增强方面mosaic1.0 和 mixup0.5 在千张级数据上可能过强适当降到 mosaic0.5、mixup0.1让模型看到更多原始分布。4.5 现象换一台机器或换一个 yolov8 版本指标对不上原因是随机种子、cuDNN 确定性、数据加载顺序不同。yolov8 训练涉及大量随机操作不同硬件和版本下结果有波动是正常的。要复现指标需要固定 seed、关闭 cudnn benchmark、使用相同的 ultralytics 版本。如果差异超过 3 个百分点检查数据划分是否一致以及验证时的 conf 和 iou 参数是否相同。5. 从 88.3% 再往上走三个我常用的提点技巧第一个技巧是标签平滑和分类损失调整。单类别检测里分类损失对最终 mAP 影响不小。yolov8 默认用 BCE 损失可以在训练配置里加label_smoothing0.1让模型对标注边界不那么敏感尤其适合标注框有轻微偏差的数据集。我在这类千张级数据上试过mAP50 通常能涨 1 到 2 个点。第二个技巧是推理阶段的 TTA。yolov8 支持测试时增强推理时对图片做翻转、缩放等多尺度变换再把结果融合。命令里加augmentTrue即可。代价是推理速度慢 2 到 3 倍适合对精度要求高、对延迟不敏感的场景。如果部署在边缘设备上这个技巧要慎用。yolo detect predict \ modelruns/smoking/exp1/weights/best.pt \ sourcetest_images/ \ imgsz640 \ conf0.3 \ augmentTrue \ saveTrue第三个技巧是难例挖掘。先用训练好的模型跑一遍训练集挑出置信度低或预测错误的样本人工重新检查标注修正漏标和错标后再微调。这个过程通常做一到两轮mAP 能再涨 2 到 3 个点。我自己的习惯是每轮训练后都导出验证集里置信度在 0.3 到 0.6 之间的预测结果人工过一遍积累到 50 张左右就重新微调一次。技巧预期涨幅代价适用场景label_smoothing0.11-2 点几乎无标注有轻微偏差TTA augmentTrue1-3 点推理慢 2-3 倍离线检测、精度优先难例挖掘微调2-3 点人工成本有标注人力最后说一个我踩过的坑不要为了刷高 mAP 而反复在验证集上调参。验证集调多了指标会虚高实际部署时打回原形。我一般会留一个 test 集只在最终确定模型后跑一次那个数字才是真正能拿出去说的。希望帮到你。本文还有配套的精品资源点击获取
返回列表