
简介面向目标检测与监控场景应用人群这份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_hand和smoke_mouth两个class但下面的转换代码逻辑不变。2.2 VOC、COCO、YOLO三种标签在坐标和存储上的差别格式存储形态坐标表示类别管理单图/全局标注工具VOC每图一个XML绝对像素(xmin, ymin, xmax, ymax)name文本单图LabelImgCOCO整个数据集一个JSON绝对像素左上角(x, y, width, height)categories索引全局labelme、CVATYOLO每图一个TXT归一化中心点(x_center, y_center, w, h)范围0~1类别序号从0开始单图LabelImg、Roboflow理解这三者的差异重点是坐标体系的变化。VOC和COCO都是绝对像素坐标但VOC是“左上右下”COCO是“左上宽高”YOLO则是中心点归一化这也是YOLO系列训练时的直接输入格式。COCO需要维护images、annotations、categories三个字段的对应关系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, classesNone): os.makedirs(out_dir, exist_okTrue) # 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或venvPython 3.9~3.12均可最新ultralytics要求3.8以上。平台后端依赖差异训练显存/内存要求速度参考NVIDIA GPUCUDA cuDNNtorch安装时指定cu118/cu121等batch默认168G显存基本够用最快CPUCPU后端标准torch内存 16Gbatch降到4最慢但有耐心能跑完Mac (Apple Silicon)MPStorch MPS后端无需CUDA统一内存8G的Mac要限制内存水位比CPU快略慢于入门GPUAMD/Intel GPU在较新torch里也有支持但标题只要求GPU(GPUs)/CPU/Mac三平台所以这里只覆盖这三种。多GPU场景在训练命令里用--device 0,1即可脚本层面只需要把它做成参数透传。3.2 各平台的安装命令和版本匹配# 1. 三平台统一创建conda环境 conda create -n smoke_det python3.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 ultralyticsGPU机器上最常踩的坑是“系统里没有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/train、images/val、labels/train、labels/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_ratio0.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, encodingutf-8) as f: yaml.dump(data, f, allow_unicodeTrue, default_flow_styleFalse) print(fdata.yaml written, train{train_size}, val{val_size}) return yaml_pathpath字段必须用绝对路径因为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, typestr, requiredTrue) parser.add_argument(--epochs, typeint, default300) parser.add_argument(--batch, typeint, default16) parser.add_argument(--imgsz, typeint, default640) parser.add_argument(--device, typestr, defaultNone) 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( dataargs.data, epochsargs.epochs, batchargs.batch, imgszargs.imgsz, devicedevice, patience50, workers4, ) 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可升到16Mac MPS用4--imgsz640640或768小目标偏多时升到768显存占用明显上升--device自动0 / cpu / mps多卡如0,1Mac不要传cudaepochs150是1000张数据下的稳妥起点数据少、300轮容易在验证集上先升后降100轮以下经常还没收敛就停了。batch8搭配COCO预训练权重微调学习率走默认值没问题但Mac上统一内存8G的话batch必须降到4否则MPS后端直接out of memory。imgsz640是速度和精度的平衡点抽烟场景小目标多显存允许时上到768有明显提升。patience50是手动覆盖的早停参数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_RATIO0.6 python train.py --data data/smoke_data/data.yaml --batch 4 --device mps5. 训练完别急着上线批量推理、混淆矩阵与误报三招调参5.1 对验证集做带标签可视化推理训练进程会在runs/detect/train下生成confusion_matrix.png、results.png和results.csv。在决定调参方向前先跑一次批量推理把预测结果叠加到图上直接看“哪些图报错了”比看曲线更直观from ultralytics import YOLO model YOLO(runs/detect/train/weights/best.pt) results model.predict( sourcedata/smoke_data/images/val, conf0.4, iou0.5, saveTrue, save_txtTrue, )conf0.4是抽烟检测的推荐起点比COCO官方默认的0.25高一档因为烟头形状和笔、烟灰缸、睫毛这些物体太接近低置信度输出里噪声比例高。iou0.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 modelbest.pt formatonnx imgsz640再用同一批验证集对ONNX输出和PyTorch输出各跑一遍mAP差值应小于0.5%。如果差距明显多半是导出时的imgsz与训练时不一致按训练参数重新导出即可。本文还有配套的精品资源点击获取