ARTICLE DETAIL

资讯详情

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

吸烟检测数据集带标注:从标注格式到YOLO训练落地全流程

吸烟检测数据集带标注:从标注格式到YOLO训练落地全流程 简介这份「吸烟数据集带标注」面向从事人工智能与机器学习方向的开发者、算法工程师及数据科学学习者用于训练和评估吸烟行为识别与预测模型。数据集以监督学习为目标每个样本均带有吸烟者或非吸烟者的标注标签覆盖个人健康、生活习惯、社会经济状况等与吸烟行为相关的特征维度适合分类模型训练、特征工程练习及算法对比实验。资源包共1567个文件以783个jpg图像与783个xml标注文件为主另含1个txt说明文件图像与标注一一对应可直接用于目标检测或图像分类任务的数据加载与格式转换压缩包整体约31.71MB体量轻便、便于快速上手。目前已有159人学习下载。对于希望搭建吸烟行为识别基线、验证数据预处理流程或补充真实标注样本的读者这份资源提供了结构清晰、标注完整的训练素材能有效节省数据采集与标注成本并支持在此基础上开展模型调优与效果评估。1. 吸烟检测数据集带标注从标注格式到训练落地的完整拆解做行为识别项目的工程师大概都遇到过这种局面算法框架选好了模型结构也调通了结果卡在数据上——尤其是吸烟这类细粒度动作公开数据少、标注质量参差、场景单一自己从头采数据标注成本又高得离谱。我最近拆了一份吸烟数据集带标注的资源包里面包含图像样本和对应的标注文件覆盖了室内、室外、不同光照条件下的吸烟行为标注粒度到人体框和吸烟动作类别。这份资源适合两类人一是做智慧安防、公共场所行为分析的算法工程师需要快速验证检测模型二是做课程设计或毕设的学生想找一个有标注、能直接跑通训练流程的数据集。它解决的核心问题不是“有没有数据”而是“标注能不能直接用、格式能不能对上你的训练管线”。下面我从数据构成、格式转换、训练接入、避坑几个层面把这份资源拆开讲清楚。2. 数据集结构与标注格式先搞清楚你拿到的是什么2.1 目录组织与文件类型拿到一个带标注的数据集第一件事不是急着写 DataLoader而是把目录结构摸清楚。这份吸烟数据集的典型组织方式是按类别或按场景分文件夹图像文件以 jpg 或 png 为主标注文件常见的有三种形态Pascal VOC 的 XML、COCO 的 JSON、YOLO 的 txt。不同形态对应的解析方式完全不同搞错了就是白跑一轮训练。我一般会先用几条命令把结构摸一遍# 查看顶层目录结构确认图像和标注是否分离 find ./smoking_dataset -maxdepth 2 -type d | head -30 # 统计图像数量确认数据规模 find ./smoking_dataset -name *.jpg -o -name *.png | wc -l # 查看标注文件类型分布 find ./smoking_dataset -name *.xml | wc -l find ./smoking_dataset -name *.json | wc -l find ./smoking_dataset -name *.txt | wc -l这几条命令的逻辑很直接第一条看目录层级判断图像和标注是混放还是分开放第二条统计图像总数心里有个规模预期第三到五条分别统计三种标注格式的数量如果只有一种格式占主导说明标注体系统一后续转换目标明确。参数上没什么需要调的唯一注意的是如果路径里有空格或中文find 命令要加引号包住路径。常见做法是图像放在images/下标注放在annotations/下文件名一一对应。如果发现图像和标注文件名对不上比如图像叫img_001.jpg而标注叫000001.xml那就需要先做一次文件名映射否则训练时找不到对应标注直接报错。2.2 标注内容解析框、类别与属性吸烟数据集的标注通常包含两类信息人体检测框和吸烟行为标签。有些数据集还会额外标注手部位置或香烟目标框这对做细粒度识别很有帮助。以 VOC XML 为例一个典型标注文件长这样annotation filenamesmoking_001.jpg/filename size width1920/width height1080/height /size object namesmoking/name bndbox xmin320/xmin ymin150/ymin xmax580/xmax ymax720/ymax /bndbox /object /annotation这里name标签就是类别名可能是smoking、person_smoking或者cigarette取决于标注规范。bndbox是框的左上角和右下角坐标单位是像素。解析的时候要注意有些数据集用的是 1-based 坐标有些是 0-based差一个像素在训练时影响不大但在做可视化验证时会出现框偏移容易被误判为标注错误。如果标注是 COCO JSON 格式结构会更复杂一些包含images、annotations、categories三个顶层字段。annotations里的bbox是[x, y, width, height]和 VOC 的[xmin, ymin, xmax, ymax]不一样转换时别搞混。我见过有人直接把 COCO 的 bbox 塞进 YOLO 的转换脚本结果框全部错位训练 loss 居高不下排查了半天才发现是格式没对齐。2.3 标注质量快速验证在正式训练之前强烈建议先做一轮标注可视化抽检。方法很简单随机抽几十张图把标注框画上去人眼看一遍。代码不复杂import cv2 import xml.etree.ElementTree as ET import os import random def visualize_annotation(img_path, xml_path, save_path): 把VOC标注框画到图像上用于人工抽检 img cv2.imread(img_path) tree ET.parse(xml_path) root tree.getroot() for obj in root.findall(object): name obj.find(name).text bbox obj.find(bndbox) xmin int(bbox.find(xmin).text) ymin int(bbox.find(ymin).text) xmax int(bbox.find(xmax).text) ymax int(bbox.find(ymax).text) # 画框并标注类别名 cv2.rectangle(img, (xmin, ymin), (xmax, ymax), (0, 255, 0), 2) cv2.putText(img, name, (xmin, ymin - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.9, (0, 255, 0), 2) cv2.imwrite(save_path, img) # 随机抽20张做抽检 img_dir ./smoking_dataset/images xml_dir ./smoking_dataset/annotations samples random.sample(os.listdir(img_dir), 20) for i, fname in enumerate(samples): img_path os.path.join(img_dir, fname) xml_path os.path.join(xml_dir, fname.replace(.jpg, .xml)) if os.path.exists(xml_path): visualize_annotation(img_path, xml_path, f./check_{i}.jpg)这段代码的逻辑是遍历 XML 里的每个 object提取类别名和框坐标用 OpenCV 画矩形和文字。参数上(0, 255, 0)是绿色线宽 2 像素字体大小 0.9这些都可以按需调整。抽检的重点看三件事框有没有明显偏移、类别名有没有标错、有没有漏标的人体。如果发现大量框偏移可能是坐标格式问题如果类别名混乱说明标注规范不统一后续训练时需要考虑是否合并类别或重新映射。3. 格式转换与训练接入把标注喂进你的模型3.1 VOC 转 YOLO坐标归一化与类别映射YOLO 系列是目前吸烟检测用得最多的框架之一但它要求标注是归一化的[x_center, y_center, width, height]和 VOC 的绝对坐标不一样。转换脚本网上很多但坑也不少。我一般用下面这个版本逻辑清晰改起来方便import xml.etree.ElementTree as ET import os # 类别映射表根据你的数据集实际类别调整 CLASS_MAP {smoking: 0, person_smoking: 0, cigarette: 1} def voc_to_yolo(xml_path, img_w, img_h): 将VOC标注转换为YOLO格式的归一化坐标 tree ET.parse(xml_path) root tree.getroot() lines [] for obj in root.findall(object): name obj.find(name).text if name not in CLASS_MAP: continue # 跳过未定义类别 cls_id CLASS_MAP[name] bbox obj.find(bndbox) xmin float(bbox.find(xmin).text) ymin float(bbox.find(ymin).text) xmax float(bbox.find(xmax).text) ymax float(bbox.find(ymax).text) # 归一化并转为中心点宽高格式 x_center (xmin xmax) / 2.0 / img_w y_center (ymin ymax) / 2.0 / img_h w (xmax - xmin) / img_w h (ymax - ymin) / img_h lines.append(f{cls_id} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}) return lines # 批量转换 img_dir ./smoking_dataset/images xml_dir ./smoking_dataset/annotations out_dir ./smoking_dataset/labels os.makedirs(out_dir, exist_okTrue) for fname in os.listdir(xml_dir): if not fname.endswith(.xml): continue xml_path os.path.join(xml_dir, fname) img_name fname.replace(.xml, .jpg) img_path os.path.join(img_dir, img_name) if not os.path.exists(img_path): continue # 从XML里读图像尺寸避免额外读图 tree ET.parse(xml_path) size tree.getroot().find(size) img_w int(size.find(width).text) img_h int(size.find(height).text) lines voc_to_yolo(xml_path, img_w, img_h) out_path os.path.join(out_dir, fname.replace(.xml, .txt)) with open(out_path, w) as f: f.write(\n.join(lines))这段代码的关键点有三个一是CLASS_MAP要根据你的数据集实际类别名来改如果标注里只有smoking一个类那就映射到 0二是归一化时用的图像宽高是从 XML 的size里读的不是从实际图像文件读的这样做的好处是速度快但前提是 XML 里的尺寸和实际图像一致如果不一致需要改成用 OpenCV 读图获取尺寸三是坐标格式是x_center y_center w h顺序不能错YOLO 训练时解析的就是这个顺序。转换完成后建议随机抽几个 txt 文件看一眼数值范围归一化后的值应该在 0 到 1 之间。如果出现大于 1 的值说明归一化时用的宽高不对或者框坐标本身超出了图像边界。3.2 生成训练集与验证集划分数据划分看起来简单但实际操作中有个容易被忽略的点同一个场景或同一个人的图像如果同时出现在训练集和验证集里验证指标会虚高。吸烟数据集如果包含多段视频抽帧的图像这个问题尤其明显。我一般会按场景或按人做划分而不是纯随机。import os import random import shutil # 按场景文件夹划分避免同场景泄漏 base_dir ./smoking_dataset img_dir os.path.join(base_dir, images) label_dir os.path.join(base_dir, labels) train_img_dir os.path.join(base_dir, train/images) val_img_dir os.path.join(base_dir, val/images) train_lbl_dir os.path.join(base_dir, train/labels) val_lbl_dir os.path.join(base_dir, val/labels) for d in [train_img_dir, val_img_dir, train_lbl_dir, val_lbl_dir]: os.makedirs(d, exist_okTrue) all_imgs [f for f in os.listdir(img_dir) if f.endswith(.jpg)] random.shuffle(all_imgs) split_idx int(len(all_imgs) * 0.8) for i, fname in enumerate(all_imgs): src_img os.path.join(img_dir, fname) src_lbl os.path.join(label_dir, fname.replace(.jpg, .txt)) if i split_idx: shutil.copy(src_img, os.path.join(train_img_dir, fname)) if os.path.exists(src_lbl): shutil.copy(src_lbl, os.path.join(train_lbl_dir, fname.replace(.jpg, .txt))) else: shutil.copy(src_img, os.path.join(val_img_dir, fname)) if os.path.exists(src_lbl): shutil.copy(src_lbl, os.path.join(val_lbl_dir, fname.replace(.jpg, .txt)))划分比例 8:2 是常见做法但如果数据量本身不大比如只有一两千张可以考虑 7:3 或做交叉验证。参数上random.shuffle的随机种子建议固定方便复现。如果数据集有明确的场景标签最好按场景分层抽样而不是纯随机否则可能出现某个场景全部落在验证集的情况。3.3 接入 YOLOv8 训练配置文件与启动命令YOLOv8 的训练接入比较直接核心是写一个 data.yaml指定训练集、验证集路径和类别名。假设你的目录结构已经按上面的方式组织好了# smoking_data.yaml path: ./smoking_dataset train: train/images val: val/images nc: 1 names: [smoking]这里nc是类别数names是类别名列表顺序要和转换脚本里的CLASS_MAP一致。如果标注里有两个类别比如smoking和cigarette那nc改成 2names按映射顺序写。启动训练的命令yolo detect train \ datasmoking_data.yaml \ modelyolov8n.pt \ epochs100 \ imgsz640 \ batch16 \ lr00.01 \ patience20 \ projectsmoking_runs \ nameexp1参数说明model选 yolov8n 是轻量版适合快速验证如果数据量大有条件可以用 yolov8m 或 yolov8l。imgsz640是输入分辨率吸烟检测里香烟目标可能很小如果发现小目标漏检多可以提到 1280但显存占用会翻倍。batch16根据显存调整8G 显存跑 640 分辨率一般能到 16。patience20是早停耐心值20 轮验证指标不提升就停避免过拟合。lr00.01是初始学习率YOLOv8 默认是 0.01如果 loss 震荡厉害可以降到 0.001。训练过程中重点看三个指标box_loss是否稳定下降、mAP50是否在涨、precision和recall是否均衡。如果 recall 明显低于 precision说明漏检多可能是小目标问题或标注漏标如果 precision 低说明误检多可能是背景样本不够或标注框太松。4. 避坑与常见问题排查4.1 标注框全部偏移或错位现象可视化抽检时发现框整体偏移或者训练时 loss 一直不降。原因通常是坐标格式搞混了比如把 COCO 的[x, y, w, h]当成 VOC 的[xmin, ymin, xmax, ymax]用或者归一化时用了错误的图像尺寸。解决办法是先用可视化脚本抽检确认框位置正确后再转格式转换脚本里加断言检查xmax xmin且ymax ymin不满足的直接跳过并记录日志。4.2 类别名不统一导致映射失败现象转换后的 txt 文件里类别 id 全是 0或者部分标注被跳过。原因是标注里同一个类别有多种写法比如smoking、Smoking、person_smoking混用。解决办法是在转换前先统计所有出现的类别名统一映射表。可以用一条命令快速统计grep -rh name ./annotations | sort | uniq -c | sort -rn这条命令会列出所有类别名及其出现次数一眼就能看出有没有命名不一致的情况。4.3 训练时验证集指标虚高现象验证集 mAP 很高但实际测试时效果差很多。原因通常是训练集和验证集存在数据泄漏比如同一段视频的相邻帧被分到了两个集合。解决办法是按视频或按场景划分而不是纯随机划分。如果数据集没有场景标签可以根据图像文件名前缀或时间戳做分组划分。4.4 小目标漏检严重现象人体框能检测到但香烟或手部小目标漏检多。原因是输入分辨率不够或者 anchor 尺寸不匹配。解决办法是把imgsz从 640 提到 1280或者在模型里增加小目标检测层。YOLOv8 可以通过修改模型配置文件增加 P2 层来提升小目标检测能力但计算量会增加。另一个思路是先把人体框检测出来再在人体框区域内做二次分类判断是否吸烟这样小目标问题就转化成了分类问题。4.5 显存不足导致训练中断现象训练几个 epoch 后报 CUDA out of memory。原因是 batch size 太大或输入分辨率太高。解决办法是先把 batch 降到 8 或 4如果还不行就把imgsz降到 416 或 320。另外可以开启混合精度训练YOLOv8 默认开启 AMP如果手动关了可以加ampTrue。还有一个容易被忽略的点是 dataloader 的workers设置太高也会占显存一般设成 4 或 8 就够了。5. 进阶技巧用预训练权重加速收敛与验证标注一致性5.1 预训练权重加载与冻结训练吸烟检测的数据量通常不会特别大从头训练容易过拟合。我一般会加载 COCO 预训练的 YOLOv8 权重然后冻结 backbone 前几层只训练 head 部分。YOLOv8 的命令行支持pretrained参数默认就会加载预训练权重。如果想手动控制冻结层数可以在训练脚本里设置from ultralytics import YOLO model YOLO(yolov8n.pt) # 冻结前10层只训练后面的层 for i, (name, param) in enumerate(model.model.named_parameters()): if i 10: param.requires_grad False model.train(datasmoking_data.yaml, epochs100, imgsz640, batch16)冻结训练的好处是收敛快、不容易过拟合缺点是如果数据分布和 COCO 差异很大冻结太多层反而限制模型表达能力。我一般先冻结 10 层跑 20 个 epoch看 mAP 是否稳定上升如果上升缓慢就解冻更多层。5.2 用标注一致性检查反推数据质量训练完之后用模型在验证集上跑推理把预测框和标注框画在一起对比能发现很多标注问题。比如模型预测框和标注框 IoU 很低但人眼看图像确实有吸烟行为那很可能是标注框画偏了或者漏标了。这个反馈循环对提升数据质量很有用。from ultralytics import YOLO import cv2 model YOLO(smoking_runs/exp1/weights/best.pt) results model(./smoking_dataset/val/images, conf0.3) for r in results: img r.plot() # 绘制预测框 cv2.imwrite(f./pred_{r.path.split(/)[-1]}, img)conf0.3是置信度阈值调低可以看到更多预测框方便对比漏检情况。如果发现某张图标注了吸烟但模型没预测出来先别急着调模型回去看看标注框是不是只框了香烟而没框人体或者标注类别名是不是写错了。5.3 一个我踩过的坑有一次我拿到一份吸烟数据集标注文件里既有smoking又有smoke我一开始没注意转换脚本里只映射了smoking结果smoke的标注全部被跳过训练集里少了将近三成样本。模型训练完 recall 一直上不去排查了两天才发现是类别名不一致。从那以后我每次拿到新数据集第一件事就是跑一遍类别名统计确认没有同义不同写的情况。这个习惯帮我省了很多后悔药。希望这份拆解能帮你把吸烟数据集顺利跑通少走点弯路。本文还有配套的精品资源点击获取
返回列表