ARTICLE DETAIL

资讯详情

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

YOLO11抽烟检测数据集:VOC/COCO/YOLO格式转换与训练脚本实战

YOLO11抽烟检测数据集:VOC/COCO/YOLO格式转换与训练脚本实战 简介面向目标检测开发者的抽烟检测数据集配套说明文档完整描述了一套含1000张真实与合成场景图片的高质量数据集覆盖街景、写字楼、办公室、楼道以及遮挡行人、严重遮挡行人等丰富抽烟场景可直接用于公共场所或室内监控场景下的抽烟检测项目。资源共1个文件约8.92MB以PDF形式呈现内附数据集基本情况、labelimg标注截图、图像缩略图预览及专属获取方式。已有761人学习下载适合正在训练YOLO系列模型或需要补充监控场景抽烟检测数据的算法工程师与研究人员。标注环节采用labelimg完成提供VOC(xml)、COCO(json)、YOLO(txt)三种主流格式可无缝衔接常见目标检测训练流程。同时文档附赠YOLO11一键训练脚本支持GPU(GPUs)、CPU、Mac(M芯片)多平台运行并给出博主训练结果日志参考方便快速复现与评估模型效果。1. 抽烟检测数据集与YOLO11训练脚本1000张图能不能撑起一个真实监控场景的检测任务做目标检测项目最怕的一件事就是数据和训练脚本之间隔着一道“能不能跑通”的鸿沟。这份抽烟检测数据集把1000张真实合成场景图片、VOC/COCO/YOLO三种格式标签、以及支持GPU/CPU/Mac三平台的YOLO11训练脚本打包在一起明显是奔着“拿到就能训练”去的。适合正在做公共场所监控、室内行为识别或者想在YOLO11上快速验证一个单类目标检测流程的人。先抛一个反直觉结论1000张图做单类检测是够用的前提是场景分布合理、遮挡样本占比足够、标注边界一致。这份数据在街景、写字楼、办公室、楼道之外专门放了遮挡和严重遮挡行人的抽烟数据这恰恰是监控场景下最容易翻车的部分。下面从数据构成、标签格式、训练脚本到实际坑点逐层拆开讲。2. 数据集构成与标注质量真实合成混合样本怎么经得起监控场景考验2.1 场景清单与训练价值遮挡样本才是这份数据最值钱的部分先看这份数据的场景分布街景抽烟、写字楼抽烟、办公室抽烟、楼道抽烟、遮挡行人抽烟、严重遮挡行人抽烟。每个场景对应的检测难点完全不同不能把它们当成同一堆“人抽烟的图”。街景和楼道抽烟目标通常比较大光线相对均匀属于常规检测。写字楼和办公室抽烟问题出在背景复杂——工位隔断、电脑屏幕、玻璃反光都会干扰检测而且很多抽烟动作是坐在工位上发生的上半身大部分被桌面遮挡。遮挡行人抽烟和严重遮挡行人抽烟则是针对监控视角下行人交错、目标小、只在某一帧露出半张脸甚至只有手部区域的情况。这类样本对模型特征的鲁棒性要求最高也最容易被一般数据集遗漏。真实合成混合我的理解是合成数据用来补“稀有姿态”真实数据用来保证纹理、光影分布接近实际摄像头输出。常见做法是在原始真实样本上叠加合成背景、换角度重渲染、加入遮挡干扰生成一批训练集里很难从真实场景里收集到的极端样本。这种做法对单类行为检测特别有用——抽烟动作的姿态变化其实有限真正缺的是“同样的动作在不同遮挡程度下的表现”。所以这个数据结构透露的训练策略很清楚不要把它当成“拿1000张图直接训到收敛”的基准而是把它看成一组“场景分布互补”的素材池。我自己会按 7:3 或者 6:4 的比例把完整目标样本和遮挡样本混合后送入训练让模型同时见到“全身完整”和“只露半张脸”这两种极端形态。完全去掉遮挡样本训练验证集 mAP 会虚高但一放进真实监控视频就原形毕露——行人一交错检测框就开始抖。提示拿到这1000张图的第一件事不是跑训练而是数一下“完整目标”和“遮挡目标”的占比再决定要不要在 DataLoader 里做遮挡相关的增强策略。2.2 labelimg标注口径与三格式产出标注质量如何决定训练上限数据集的标注工具是 labelimg这是 YOLO 系项目里最主流的矩形框标注工具。选择 labelimg 而不是 CVAT 或 Label Studio最重要的原因是它的输出直接面向训练默认生成 VOC 的 XML通过脚本可以无损转换到 COCO JSON 和 YOLO TXT。对于单类别的抽烟检测矩形标注足够不需要多边形分割labelimg 的轻量反而提高了批量标注效率。标注口径是决定模型行为的关键。同样一个“抽烟”动作标注框到底框哪里框烟支本身模型学到的是“烟支检测器”一旦烟支被手挡住就会漏检框手部到嘴部区域模型学到的是“抽烟行为检测器”在实际监控里表现明显更稳定。这份数据集采用后者统一用类名 smoking边界覆盖手部与嘴部范围。这点相当重要——很多自己标数据的人翻车不是标得不好而是口径不一致有的人只框烟、有的人框半张脸模型被互相矛盾的标签搞晕。判断标注质量有几个硬指标框是否贴边、是否漏标、类名是否统一。labelimg标注界面里如果框比目标外扩超过10个像素训练出的框回归会偏大如果头部小目标在远处被漏标验证时那个位置的 recall 就会异常低。三种格式并存本质是对同一份标注做三种序列化格式文件形态坐标体系类别标识VOCXML文件bndbox: xmin, ymin, xmax, ymax像素绝对坐标标签字符串COCOJSON文件bbox: x, y, w, h像素绝对坐标category_id从1排起YOLOTXT每行一条cx, cy, w, h相对图片宽高的0~1归一化坐标class_id从0排起这个表值得贴在你显示器旁边。我见过太多次转换脚本把 xmin/ymin 当成 cx/cy 直接用结果训练时 loss 不降。这份数据能直接给你三种格式省掉的工作不只是转换这一步而是“转换后验证标注没有变形”的调试过程。如果你以后要自己扩数据我的建议是依旧从 labelimg 的 XML 出发保持这一条转换链路不要绕弯。3. VOC/COCO/YOLO三种格式标的边界框对齐转换脚本的参数逻辑与验证方法3.1 三种坐标体系的换算逻辑像素坐标、归一化坐标与COCO的长宽表示模型训练本身只关心输入的标签张量长什么样而三种格式的核心差异就在坐标系的组织方式上。VOC 的 XML 里bndbox 给出的是 xmin、ymin、xmax、ymax 四个像素绝对值XML 解析后直接可用于画框但 YOLO 训练不能直接用因为 YOLO 期望的是相对坐标。COCO 的 JSON 里每个 annotation 有一条 bbox 字段格式是 [x, y, width, height]注意它没有直接用 xmax, ymax而是用左上角坐标加宽高。COCO 的 category_id 从1开始而YOLO的class_id从0开始这个差1的偏移是转换脚本里最容易埋雷的地方。YOLO 的 TXT 格式最简洁每行五个浮点数class_id, cx, cy, w, h全部归一化到0~1之间。cx、cy 是中心点坐标w、h是归一化宽高。从 VOC 到 YOLO 的换算公式是cx (xmin xmax) / 2 / image_width cy (ymin ymax) / 2 / image_height w (xmax - xmin) / image_width h (ymax - ymin) / image_height从 VOC 到 COCO 只需要把 xmax 和 ymax 减掉左上角变成宽高x xmin y ymin w xmax - xmin h ymax - ymin公式很简单但批量处理时最容易忽略的是归一化操作图片宽高必须来自和被标注图片完全一致的文件而不是某些地方缓存的尺寸。如果原图被预处理缩放过分但标注还是原始像素训练时框的位置会整体偏移——这种错法不会让 loss 完全爆炸而是让精度卡在某个值上不去排查难度比直接报错高得多。3.2 XML转YOLO与XML转COCO一个可复用的转换脚本实例下面这个脚本是我处理这类单类检测数据集时常用的转换逻辑可以同时输出 YOLO TXT 和 COCO JSON。它的设计思路是按目录遍历 XML一次性解析所有标注不依赖 labelimg 生成的附带文件适合干净环境里直接跑。import os import xml.etree.ElementTree as ET import json def xml_to_yolo_and_coco(xml_dir, img_dir, output_dir, class_names): xml_dir: labelimg标注的VOC XML目录 img_dir: 对应图片目录 output_dir: 转换结果输出目录 class_names: 类名列表, 如[smoking], 索引即class_id yolo_out os.path.join(output_dir, labels) os.makedirs(yolo_out, exist_okTrue) coco_annotations [] image_id 1 annotation_id 1 for xml_file in sorted(os.listdir(xml_dir)): if not xml_file.endswith(.xml): continue tree ET.parse(os.path.join(xml_dir, xml_file)) root tree.getroot() # 图片尺寸取自XML中的size节点, 确保与真实图片一致 img_w int(root.find(size/width).text) img_h int(root.find(size/height).text) yolo_lines [] for obj in root.findall(object): name obj.find(name).text if name not in class_names: continue class_id class_names.index(name) 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) # VOC转YOLO: 中心点归一化 cx (xmin xmax) / 2.0 / img_w cy (ymin ymax) / 2.0 / img_h w (xmax - xmin) / img_w h (ymax - ymin) / img_h yolo_lines.append(f{class_id} {cx:.6f} {cy:.6f} {w:.6f} {h:.6f}) # 同时生成COCO标注, bbox用[x, y, w, h]绝对值 coco_annotations.append({ id: annotation_id, image_id: image_id, category_id: class_id 1, # COCO从1开始 bbox: [xmin, ymin, xmax - xmin, ymax - ymin], area: (xmax - xmin) * (ymax - ymin), iscrowd: 0 }) annotation_id 1 base_name os.path.splitext(xml_file)[0] with open(os.path.join(yolo_out, base_name .txt), w) as f: f.write(\n.join(yolo_lines)) image_id 1 # 输出COCO格式JSON文件 coco_json { images: [ { id: i 1, file_name: f{os.path.splitext(os.listdir(img_dir)[i])[0]}.jpg, width: img_w, height: img_h } for i in range(image_id - 1) ], annotations: coco_annotations, categories: [ {id: i 1, name: name} for i, name in enumerate(class_names) ] } with open(os.path.join(output_dir, coco.json), w) as f: json.dump(coco_json, f, ensure_asciiFalse, indent2)几个关键参数要注意class_names 列表的顺序是硬约束列表里第 0 个类对应 YOLO 的 class_id 0COCO 的 category_id 会在此基础上 1。顺序一旦定好训练时的 data.yaml 里 classes 名称顺序必须完全一致。XML 里的图片尺寸取自size节点。如果图片被外部工具压缩过但 XML 没更新转换出的归一化坐标会整体失真这也是一个容易忽略的脏数据来源。COCO 里的 images 列表我这里用索引占位实际操作中建议读取真实文件名来填充file_name字段否则 COCO API 加载时会找不到文件。3.3 训练前的数据目录检查一次跑通的前提转换完成后不要直接进训练脚本先做三步检查。第一步图片数量和标签数量必须一致。第二步随机抽5张图把标签画回图上看偏移量。第三步确认 labels 目录里没有空的 txt 文件空标签文件在训练时会导致该图片被跳过或报 warning。这三步用下面的 bash 命令就能快速完成# 统计图片和标签数量是否一致 ls images | wc -l ls labels | wc -l # 找出空标签文件 find labels -name *.txt -size 0 | head -20注意空标签文件的危害在于它不会直接报错但训练日志里会出现训练图片数量少于数据集图片数量的提示。如果不注意你以为是1000张在训练实际只有900张。4. 从数据到训练常见坑与排查五个反复出现的实际问题4.1 数据加载层的三个典型报错现象一训练脚本启动后立刻提示 “No labels found in dataset”原因YOLO 模型读取标签的路径是独立的如果你的 labels 目录结构和项目默认结构不一致训练会直接跳过所有样本。我见过有人把 CVAT 导出的 labels 直接丢进项目没有按images/train与labels/train严格配对就会出现“有图无标签”的假象。解决把目录结构对齐成 YOLO 项目默认布局含糊不得dataset/ ├── images/ │ ├── train/ │ └── val/ ├── labels/ │ ├── train/ │ └── val/ └── data.yamldata.yaml 中 train 和 val 字段指向 images 的路径即可YOLO 会自动匹配同名前缀的 labels 文件。现象二数据路径含中文时图片能读到但训练中途图片加载返回空原因OpenCV 的 imread 和部分 Python 图像库对中文路径支持不彻底标签路径里含中文时容易静默失败。解决整条数据链路强制使用英文字母和数字路径这是从根上排除隐患的习惯。我在做任何训练项目时都先把数据集复制到纯英文目录避免跟操作系统编码纠缠。现象三部分图片训练时反复报错坐标越界或像素为空原因标注框完全跑到图片外部或者图片本身损坏也可能是转换时宽度和高度对调了。解决用脚本遍历所有标签文件检查每一条 bbox 的四个值是否都在 [0,1] 范围内。越界的直接丢弃该标注with open(labels.txt, r) as f: lines f.readlines() valid [] for line in lines: parts list(map(float, line.strip().split())) if len(parts) ! 5: continue _, cx, cy, w, h parts if not (0 cx 1 and 0 cy 1 and 0 w 1 and 0 h 1): continue valid.append(line) with open(labels_valid.txt, w) as f: f.writelines(valid)4.2 训练过程中指标异常与平台兼容性排查现象四mAP50 卡在 0.3 左右上不去训练 loss 已经稳定原因最直接的是类别编号错位。转换时 XML 里的类名顺序和 data.yaml 不一致模型以为 class_id 0 是“其余类”而实际标签里全是 class_id 1导致模型学到的和标签对不上。解决打开一份 YOLO 标签文件确认里面的编号和 data.yaml 里的 classes 顺序严格对应。单类检测时YOLO 的 class_id 必须是 0如果你看到标签文件里写的是 1那训练结果一定废。现象五Mac M芯片上启动训练后 MPS 报 warning 或速度反而比 CPU 慢原因PyTorch 的 MPS 后端对某些 YOLO11 算子支持不完整会在部分层回退到 CPU导致训练时设备切换开销大速度反而不如纯 CPU 顺畅。这不是脚本写错了是硬件后端兼容性问题。解决先确认 PyTorch 版本在 2.2 以上MPS 支持会好很多。如果版本没问题但训练仍然偏慢直接把 device 强制设为 CPU90% 的情况下单类小数据集的训练时间可以接受。另一个办法是减小 batch size 到 4减少 MPS 显存换入换出的压力。如果模型在训练集上精度高、验证集精度却明显偏低那要检查验证集的数据分布是不是和训练集差太多。这份数据里的遮挡样本如果全被分到训练集、验证集只剩清晰样本验证 mAP 会虚高反过来验证集会低到离谱。我在划分数据时一定会做一次随机分层保证每类场景在训练集和验证集中都占据相近的比例。这是所有踩坑里最隐蔽的一个——它不报错它只是想让你误以为模型训练不到位。5. YOLO11训练脚本的平台分支逻辑与关键参数从CUDA到MPS再到CPU回退5.1 脚本流程与平台识别自动选择设备的判断分支YOLO11 的训练脚本和 YOLOv5/v8 一脉相承核心入口都在train.py。这份资源附赠的一键训练脚本最大特点不是模型结构而是平台适配它通过运行时检测自动选择训练后端。设备判断逻辑一般是这样的import torch def get_device(force_cpuFalse): 返回训练设备字符串 cuda: GPU优先, 适合NVIDIA显卡 mps: Mac芯片, 如M1/M2/M3 cpu: 最后兜底 if force_cpu: return cpu if torch.cuda.is_available(): return cuda if torch.backends.mps.is_available(): return mps return cpu这段代码里的判断顺序是有讲究的。CUDA 是性能最优的选项所以最先判断MPS 是 Mac 专用的加速接口但优先级别低一级因为有部分环境虽然报 mps available 却因驱动或 PyTorch 版本过旧而实际跑不起来。脚本里如果没有 force_cpu 参数遇到这种情况就会卡住或报错。所以我会保证脚本里始终暴露一个--force-cpu参数宁可慢不能崩。数据加载部分也有平台差异GPU 环境里 workers 可以设为 4 或 8MPS 环境建议降到 2CPU 环境再往下降。这不是玄学是加速设备与数据加载进程的协同问题——GPU 算得快需要更多 worker 喂数据CPU 训练时算力有限worker 开多了反而把 CPU 资源抢走拖慢主线程。5.2 训练参数设置参考与调参方向一键训练脚本给你的是默认参数但不同平台推荐起始值不同。我用这份数据时踩过一轮参数整理出来供参考参数GPU (如RTX 3060)CPU (桌面级)Mac M系列imgsz640512640batch1684epochs1006080workers822optimizerSGDAdamWAdamWlr00.010.0010.001batch 大小是最值得反复试的参数。GPU 显存够就把 batch 开满梯度更稳定Mac 上 batch 超过 8 容易触发内存压力导致训练进程被杀。CPU 训练时图片分辨率建议降到 512 而不是 640推理速度提升的幅度很可观而单类抽烟检测对精度的损失完全可接受。epoch 设置上不要照搬默认的 300。1000 张图的单类检测模型通常在第 50~80 个 epoch 就能收敛。设 300 不是不行但后段容易出现过拟合浪费的时间也不是一星半点。用 80 个 epoch 先跑一遍看最后 10 个 epoch 的 mAP50 是否还在上涨再决定是否续训。5.3 训练日志判读博客里博主参考日志中含有的关键信息日志文件通常打印四行关键指标Pprecision、Rrecall、mAP50、mAP50-95。P 和 R 是此消彼长的关系mAP50 反映的是 IoU 阈值为 0.5 时的综合检测精度mAP50-95 则是对框定位能力的更严格评比。博主训练日志里能让我们关注的指标其实是 P 和 R 的相对关系。如果 P 比 R 高很多说明模型保守宁可不框也不框错——这在监控场景里意味着漏检会变多可能有抽烟行为没被报出来。如果 R 比 P 高很多说明模型激进误报更严重。对抽烟检测来说宁可 P 稍高一些因为被误报的代价通常是触发一次人工复核而漏报的代价是真出了问题没发现。mAP50-95 和 mAP50 之间的差距也有意义两者差距小说明框定位准确、边界清晰差距大说明模型虽然能框中目标位置但框的边界不稳定。这份数据集如果转换脚本里坐标换算有误就会在 mAP50-95 上先露出端倪。6. 验证与导出把训练结果跑通到一张真实监控截图上的完整步骤训练结束后不要急着收工把 best.pt 导出成适合部署的模型再拿没有参与训练的监控截图验证一次这才算闭环。from ultralytics import YOLO model YOLO(runs/segment/train/weights/best.pt) # 验证单张图片, conf降到0.25以下以观察更多潜在检出 results model.predict(test_screenshot.jpg, conf0.1, saveTrue) # 导出ONNX, 方便后续用CPU/移动端推理 model.export(formatonnx, imgsz640)conf 参数是验证时最容易忽视的训练时默认阈值 0.25 在正常环境下没问题但监控场景光线复杂、目标小我建议验证时把 conf 降到 0.1先看模型的上限在哪再反推部署时该设多少。如果你用 conf0.25 验证时什么都没检出来不代表模型废了可能只是阈值偏高。导出 ONNX 这一步的意义在于脱离 PyTorch 环境运行。很多监控系统跑在 Linux 容器里装完整 PyTorch 太重ONNX 配合 onnxruntime 就轻量得多。从那以后我每次训完模型都会强制走一遍“随机抽一张没见过的图、调低置信度、看输出框位置”的过程框位置对不对、该框的有没有框到一眼就能看出这套数据训练流程有没有问题。这一步不值得省希望帮到你。这个数据集的获取方式是搜索公众号“极智视界”回复专属凭证 OE2ubuEFWGtV就能拿到下载信息和完整使用说明。下载前记得把网盘里的 PDF 先看一遍里面有数据缩略图和环境配置说明能帮你规避上面提到的不少坑。本文还有配套的精品资源点击获取
返回列表