news 2026/9/18 10:44:20

抽烟检测数据集与YOLO11训练:从标注格式到模型调参

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
抽烟检测数据集与YOLO11训练:从标注格式到模型调参

简介:面向目标检测与监控场景应用人群,这份PDF资源提供抽烟检测数据集的完整说明与获取方式。数据集包含1000张真实+合成场景的高质量图片,覆盖街景、写字楼、办公室、楼道、遮挡行人及严重遮挡行人抽烟等丰富场景,并使用LabelImg标注,提供VOC(xml)、COCO(json)、YOLO(txt)三种格式标签,可直接用于YOLO等常见算法训练。随附的YOLO11一键训练脚本支持GPU、CPU、Mac(M芯片)三平台,还附有博主训练结果日志,便于快速验证训练效果。由于数据集体量较大,资源以PDF形式承载,共1个文件,大小8.92MB,内附数据集基本情况、标注示例、缩略图及百度网盘获取方式。已有759人学习,适合正在开展抽烟检测项目或需补充监控场景数据的开发者使用。

1. 抽烟检测数据集——用1000张标注图把YOLO11训到可用的最小路径

抽烟检测是目标检测里少有的“单类、强遮挡、小目标偏重”场景:一个烟头在1080p画面里经常只有十几个像素,比人脸还难标;标注的是“手持烟/嘴叼烟”这个复合状态,界线和遮挡造成的歧义比一般检测更多。1000张图的规模不大不小,正好卡在“用预训练权重能收敛、又不容易因为数据太少过拟合”的区间,配上一份带VOC/COCO/YOLO三种格式的标签集,就是一条完整的工程链路。这篇按数据、环境、训练脚本、验证调参四件事讲透:格式怎么统一、GPU/CPU/Mac怎么各跑各的、YOLO11训练参数怎么设、模型出来后怎么判断能不能用。

2. 三种标签格式的转换逻辑:先定存储结构,再写坐标互转

2.1 抽烟检测里标注对象怎么定义才算合理

标题里“抽烟检测数据集”这个说法,第一反应是标“烟本身”,但实际训练里这么做效果会很差:烟体太小、和背景融合度高,烟灰缸和打火机也会造成大量误检。常见做法是只定义一个类别smoking,框住“手持烟的拳头/嘴部区域 + 可见烟体”,它本质上是一个复合目标,而不是单个烟头。

  • 一只手握着烟、另一侧对着人,框覆盖手部和烟头,框下沿就是手部边缘;
  • 烟头伸出画面边沿时只标可见部分,避免把画面外的区域带进bndbox;
  • 场景上尽量覆盖室内、室外、白天、晚间、正脸、侧脸,便利店、加油站、工地等不同底色,1000张图里每个场景分配三分之一左右。

这样一个类别的好处是训练负担轻、专一性强;坏处是对“从嘴里拿下来但手还垂下”的状态会有漏检,因为标的是“复合状态”。对YOLO11这种对极小目标表现一般的模型来说,这个界定比多类别方案更容易收敛。如果你的场景必须区分“手持烟”和“嘴叼烟”,就在标注阶段拆成smoke_handsmoke_mouth两个class,但下面的转换代码逻辑不变。

2.2 VOC、COCO、YOLO三种标签在坐标和存储上的差别

格式存储形态坐标表示类别管理单图/全局标注工具
VOC每图一个XML绝对像素(xmin, ymin, xmax, ymax)<name>文本单图LabelImg
COCO整个数据集一个JSON绝对像素左上角(x, y, width, height)categories索引全局labelme、CVAT
YOLO每图一个TXT归一化中心点(x_center, y_center, w, h),范围0~1类别序号(从0开始)单图LabelImg、Roboflow

理解这三者的差异,重点是坐标体系的变化。VOC和COCO都是绝对像素坐标,但VOC是“左上右下”,COCO是“左上+宽高”;YOLO则是中心点+归一化,这也是YOLO系列训练时的直接输入格式。COCO需要维护imagesannotationscategories三个字段的对应关系,annotation.id不能重复,image_id要指向images里的id。YOLO的txt是纯文本,没有XML/JSON的层次嵌套,读起来最省事,但它反而最容易出错——有人把绝对坐标除以图片宽高时忘了换中心点,或者反向把(xmin, ymin, xmax, ymax)归一化后直接写进txt,训练时要么报all bbox points之外的警告,要么loss一直降不下去。

2.3 用一份Python脚本打通VOC→COCO→YOLO三种互转

转换脚本的核心逻辑是“先归一化处理,再统一转出”。以最常见的VOC转YOLO为例:

import os import glob import xml.etree.ElementTree as ET from PIL import Image def voc_to_yolo(xml_dir, img_dir, out_dir, classes=None): os.makedirs(out_dir, exist_ok=True) # 1. 如果没指定类别表,从所有xml里收集 if classes is None: classes = [] for xml_file in glob.glob(os.path.join(xml_dir, "*.xml")): root = ET.parse(xml_file).getroot() for obj in root.findall("object"): cls = obj.find("name").text if cls not in classes: classes.append(cls) # 2. 逐个xml转成txt,坐标从绝对转成归一化中心点 for xml_file in glob.glob(os.path.join(xml_dir, "*.xml")): root = ET.parse(xml_file).getroot() # 用PIL取真实宽高,避免xml里width/height和实际不一致 img_path = os.path.join(img_dir, root.find("filename").text) with Image.open(img_path) as im: W, H = im.size lines = [] for obj in root.findall("object"): cls = obj.find("name").text if cls not in classes: continue cls_id = classes.index(cls) bndbox = obj.find("bndbox") xmin = float(bndbox.find("xmin").text) ymin = float(bndbox.find("ymin").text) xmax = float(bndbox.find("xmax").text) ymax = float(bndbox.find("ymax").text) # 中心点归一化 x_center = ((xmin + xmax) / 2) / W y_center = ((ymin + ymax) / 2) / H w = (xmax - xmin) / W h = (ymax - ymin) / H # 钳到 (0,1),超出画面边缘的标注会越界 x_center = min(max(x_center, 0), 1) y_center = min(max(y_center, 0), 1) w = min(w, 1) h = min(h, 1) lines.append(f"{cls_id} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}") txt_path = os.path.join(out_dir, os.path.splitext(os.path.basename(xml_file))[0] + ".txt") with open(txt_path, "w") as f: f.write("\n".join(lines)) return classes

这段的重点是最后写成class_id x_center y_center width height这五个数字,class_id必须和后续data.yaml里的类别顺序完全对应。cls_id = classes.index(cls)的写法保证类别从0开始索引,但如果你手动换成classes=["smoking"]之外的有序列表,训练和推理时都要沿用同一份列表,否则会出现标签错位的难查错误。用PIL读取真实尺寸而不是XML里记的width/height,是因为标注工具偶尔会留下图片已替换但XML未同步的状况,一旦发生,整张图的坐标都会偏差。钳位操作对应另一个高频崩溃点:标注框偶尔画出图片边界,YOLO在加载标签阶段会直接判定非法框并丢弃,先min(w, 1)裁掉越界部分比在训练时报错再回头改标注要快得多。

如果源标注是COCO,把它转YOLO时把annotations里的bbox从像素转为归一化,category_id换成YOLO的0基序号,公式和上面一致,只是来源从XML换成了JSON字段。整条链路跑完后,建议随机抽5~10张图做一次可视化叠加,确认框确实贴在烟头/手掌位置,再进训练阶段。

3. 环境搭建三平台差异:GPU/CPU/Mac上PyTorch与Ultralytics安装

3.1 三个运行平台的要命差异其实只在torch后端

YOLO11的官方实现基于ultralytics包,它依赖torch。GPU、CPU、Mac三者的差异不在ultralytics,而在你装的torch是哪个后端,以及显卡驱动/CUDA是否就绪。Python环境上三个平台都一样:conda或venv,Python 3.9~3.12均可,最新ultralytics要求3.8以上。

平台后端依赖差异训练显存/内存要求速度参考
NVIDIA GPUCUDA + cuDNNtorch安装时指定cu118/cu121等batch默认16,8G显存基本够用最快
CPUCPU后端标准torch内存 >= 16G,batch降到4最慢但有耐心能跑完
Mac (Apple Silicon)MPStorch MPS后端,无需CUDA统一内存,8G的Mac要限制内存水位比CPU快,略慢于入门GPU

AMD/Intel GPU在较新torch里也有支持,但标题只要求GPU(GPUs)/CPU/Mac三平台,所以这里只覆盖这三种。多GPU场景在训练命令里用--device 0,1即可,脚本层面只需要把它做成参数透传。

3.2 各平台的安装命令和版本匹配

# 1. 三平台统一:创建conda环境 conda create -n smoke_det python=3.10 -y conda activate smoke_det # 2a. NVIDIA GPU: 装CUDA 11.8版torch,注意不是默认的cpu版 pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118 # 2b. CPU: 默认包即cpu版 pip install torch torchvision # 2c. Mac (Apple Silicon): torch官方包已含MPS pip install torch torchvision # 3. 统一安装ultralytics pip install ultralytics

GPU机器上最常踩的坑是“系统里没有CUDA Toolkit但torch需要CUDA运行时”。torch的wheel自带CUDA运行时,所以nvcc --version找不到并不影响训练,关键在显卡驱动版本要支持对应的CUDA运行时。cu118对应NVIDIA官方驱动要求520.61以上,驱动版本不够时nvidia-smi仍能显示显卡,但torch创建CUDA context会直接报错。装好后不用急着验证完整训练,先跑下面的自检脚本把环境问题和代码问题分开。

3.3 环境自检脚本:在训练前先探底

import torch print("torch:", torch.__version__) print("cuda available:", torch.cuda.is_available()) print("cuda device:", torch.cuda.get_device_name(0) if torch.cuda.is_available() else None) print("mps available:", torch.backends.mps.is_available())

三个关键输出点:cuda available: True说明GPU驱动和CUDA运行时对得上;mps available: True代表这台Mac可以直接用MPS设备;如果GPU机器上nvidia-smi有显卡但torch显示cuda available: False,大概率是conda里装的torch是cpu版,重新装带cu后缀的版本就好,不用重装驱动。Mac上如果MPS不可用,先看torch版本是否1.12以上,低于1.12没有MPS后端,直接升级到当前2.x版本即可。

4. YOLO11一键训练脚本:数据配置生成、设备自动选择与核心参数

4.1 脚本要解决的四个自动化问题

一键训练在抽烟检测场景里具体是四件事:数据目录拼好、data.yaml生成、设备自动选、训练参数可覆盖。YOLO系列的数据加载要求根目录下打包images/trainimages/vallabels/trainlabels/val四个子目录。前面VOC/COCO格式的标签,先用第二章的转换脚本落到labels里:

data/ images/ train/ 001.jpg 002.jpg val/ 003.jpg labels/ train/ 001.txt 002.txt val/ 003.txt

训练的入口是data.yaml,里面写图像路径和类别列表。如果数据里额外标了person类,一个最小可用的data.yaml长这样:

path: /path/to/smoke_data train: images/train val: images/val nc: 2 names: ['smoking', 'person']

注意两个高频坑:person类样本太少时模型会学出一个不稳定的person分支,建议要么去掉该类只保留smoking,要么补齐person的样本量;names数组的顺序必须和标签txt里的序号一致,否则框会全部映射到错位类别上,训练曲线看着正常,推理结果全部张冠李戴。

4.2 自动生成data.yaml的脚本片段

写一个生成yaml的小函数,避免每次更换机器时手动改路径:

import os import yaml def make_data_yaml(base_dir, names, val_ratio=0.2): train_size = len(os.listdir(os.path.join(base_dir, "images", "train"))) val_size = len(os.listdir(os.path.join(base_dir, "images", "val"))) data = { "path": base_dir, "train": "images/train", "val": "images/val", "nc": len(names), "names": names, } yaml_path = os.path.join(base_dir, "data.yaml") with open(yaml_path, "w", encoding="utf-8") as f: yaml.dump(data, f, allow_unicode=True, default_flow_style=False) print(f"data.yaml written, train={train_size}, val={val_size}") return yaml_path

path字段必须用绝对路径,因为ultralytics的路径解析从当前工作目录出发,相对路径换机器就废。val_ratio参数在这里只是用来打印统计信息,真正的train/val划分建议按图片目录做,而不是在yaml里用通配符,后者容易把同场景的连续帧同时分进训练和验证,导致验证精度虚高。

4.3 自动选择后端并启动训练

脚本核心段:

import torch import argparse from ultralytics import YOLO def auto_device(): if torch.cuda.is_available(): return "0" if torch.backends.mps.is_available(): return "mps" return "cpu" if __name__ == "__main__": parser = argparse.ArgumentParser() parser.add_argument("--data", type=str, required=True) parser.add_argument("--epochs", type=int, default=300) parser.add_argument("--batch", type=int, default=16) parser.add_argument("--imgsz", type=int, default=640) parser.add_argument("--device", type=str, default=None) args = parser.parse_args() device = args.device or auto_device() print(f"[train] using device: {device}") model = YOLO("yolo11n.pt") # yolo11n是YOLO11的nano版,体积小、适合小数据集 model.train( data=args.data, epochs=args.epochs, batch=args.batch, imgsz=args.imgsz, device=device, patience=50, workers=4, ) print("[train] training completed")

执行方式:

python train.py --data data/smoke_data/data.yaml --epochs 150 --batch 8 --imgsz 640 --device 0

参数选择参考:

参数默认值抽烟检测建议值说明
--epochs3001501000张图下150轮足够收敛,300轮容易过拟合
--batch1688G显存的安全值;16G可升到16,Mac MPS用4
--imgsz640640或768小目标偏多时升到768,显存占用明显上升
--device自动0 / cpu / mps多卡如0,1;Mac不要传cuda

epochs=150是1000张数据下的稳妥起点,数据少、300轮容易在验证集上先升后降;100轮以下经常还没收敛就停了。batch=8搭配COCO预训练权重微调,学习率走默认值没问题,但Mac上统一内存8G的话batch必须降到4,否则MPS后端直接out of memoryimgsz=640是速度和精度的平衡点,抽烟场景小目标多,显存允许时上到768有明显提升。patience=50是手动覆盖的早停参数,ultralytics默认是100,但小数据集在过拟合区多跑几十轮只会白烧电,50更合理。

4.4 Mac上跑YOLO11要注意的两个点

MPS后端在ultralytics里已经稳定,但有两个坑值得提前处理:一是batch调小到4~8,同时设置PYTORCH_MPS_HIGH_WATERMARK_RATIO,否则内存压力一大训练会静默中止;二是不要把device写成cuda,自动设备检测脚本会帮你落到mps,这也是一键脚本里auto_device()的实际意义。

export PYTORCH_MPS_HIGH_WATERMARK_RATIO=0.6 python train.py --data data/smoke_data/data.yaml --batch 4 --device mps

5. 训练完别急着上线:批量推理、混淆矩阵与误报三招调参

5.1 对验证集做带标签可视化推理

训练进程会在runs/detect/train下生成confusion_matrix.pngresults.pngresults.csv。在决定调参方向前,先跑一次批量推理,把预测结果叠加到图上,直接看“哪些图报错了”比看曲线更直观:

from ultralytics import YOLO model = YOLO("runs/detect/train/weights/best.pt") results = model.predict( source="data/smoke_data/images/val", conf=0.4, iou=0.5, save=True, save_txt=True, )

conf=0.4是抽烟检测的推荐起点,比COCO官方默认的0.25高一档,因为烟头形状和笔、烟灰缸、睫毛这些物体太接近,低置信度输出里噪声比例高。iou=0.5维持标准值,但如果发现同一个人身上反复出现重叠框,把iou调到0.4让NMS更激进。

5.2 误报漏检的判断和应对

表现判断调整方式
远景小烟头框不住小目标漏检imgsz升到768,数据增强里降低mosaic比例
墙上的插座、白格被框成smoking误报收集这类负样本图补进数据集,或把conf升到0.55
近景手部识别正常但嘴部漏检嘴叼烟样本少增加嘴叼烟样本,或拆成hand/mouth两个类分别训练
loss下降但mAP50不升标签框偏大检查转换脚本,确认框是否裁紧了手部+烟头区域

实操小技巧:从confusion_matrix.png里把False Positive排名靠前的图片单独拎出来看,抽烟检测大部分调参动作落在“阈值”和“数据分布”上,而不是改模型结构。补十几张真实场景里最容易被认错的图,效果比多训100轮更强。

模型定稿后把best.pt导出成ONNX,单类模型导出后体积在5MB级别,推理机不再需要Python训练环境,部署成本很低。导出命令yolo export model=best.pt format=onnx imgsz=640,再用同一批验证集对ONNX输出和PyTorch输出各跑一遍mAP,差值应小于0.5%。如果差距明显,多半是导出时的imgsz与训练时不一致,按训练参数重新导出即可。

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

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

Flutter智能验证码在OpenHarmony的适配实践

1. 项目背景与核心价值在移动应用开发领域&#xff0c;用户认证流程的便捷性直接影响着产品的用户体验和转化率。Flutter 作为跨平台开发框架&#xff0c;其生态中的 smart_auth 库通过智能验证码自动填充功能&#xff0c;显著提升了移动端认证流程的效率。然而&#xff0c;随着…

作者头像 李华
网站建设 2026/9/18 10:43:58

GPU、FPGA、NPU加速器选型指南:从架构原理到实战避坑

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

作者头像 李华
网站建设 2026/9/18 10:43:49

从CRDT到Canvas:MiroFish多人实时协作画布实践

做多人实时协作画布这件事&#xff0c;我从两年前就开始琢磨了。市面上的协作白板工具确实好用&#xff0c;但当团队需求变得"奇怪"一点——比如要把画布和我们自己的任务系统打通、要私有化部署、要接入内部的权限体系——现成产品就开始处处别扭。MiroFish 就是在这…

作者头像 李华
网站建设 2026/9/18 10:42:48

Redis高级实战:持久化、主从哨兵、分片集群、缓存治理与分布式锁

先把话说在前面&#xff1a;如果你只是会在 Spring Boot 里写一个 RedisTemplate&#xff0c;set 一个字符串再 get 出来&#xff0c;那 Redis 对你来说还是单机玩具。真正让我意识到必须系统学一遍 Redis 高级内容&#xff0c;是第一次把服务部署到多台机器之后——session 不…

作者头像 李华
网站建设 2026/9/18 10:40:55

飞书文档进 WeKnora,TaoToken 给 Agent 问答发 Key

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

作者头像 李华