
简介这套吸烟检测数据集专为人工智能和机器学习开发者提供可用于训练吸烟行为识别模型压缩包为zip格式共收录1567个文件整体大小31.71MB。其中主体是783张JPG图像以及配套的783个XML标注文件每个XML都对应一张图像记录目标物体的类别和边界框坐标另附1个txt文件。数据集按smoke_前缀统一命名图像与标注一一对应目录结构清楚开发者在训练时可直接将样本划分为训练集、验证集和测试集省去重新整理的麻烦也方便接入YOLO、Faster R-CNN等常用目标检测框架。可用于行为识别、安防监控、公共卫生管理等方向资源在CSDN已有159人学习下载。面向希望快速获得带标注图像数据、减少人工标注成本的算法工程师、科研人员和高年级学生既可作为入门练习也可作为模型迭代时的补充数据。1. 吸烟检测数据集带标注的现成样本比想象中更关键做安防和工业场景的工程师应该都有同感工厂车间要防违规吸烟加油站、化工厂要抓吸烟行为电梯轿厢监控也得识别。这些需求撞到一块最卡人的永远是第一步——没有数据。自己拍要跑现场、谈授权、做标注一套流程下来两周起步中间还会因漏标返工。这份吸烟数据集带标注就是拿来直接解决问题的图片样本和标签都备好了下载下来就能跑目标检测训练。适合的人群很明确——正在做吸烟检测或行为识别、想快速验证智慧安防方案的新手和从业者。它的价值不在算法多高级而在把 AI 项目里最耗时的数据环节交付给你让你把时间花在参数调优和工程落地上。2. 先看懂这份数据集标注格式、目录结构与样本分布拿到数据集第一件事不是解压就跑而是先看清标注格式。吸烟数据集常见的标注形式有三种VOC 的 XML、YOLO 的 txt、COCO 的 JSON。你的训练框架决定你要用哪种YOLOv8 和 YOLOv5 都吃 YOLO 格式的 txt一个目标一行格式是 class x_center y_center width height四个坐标量都是归一化到 0 到 1 的浮点数。如果数据给的是 .xml 后缀的标签说明是 VOC 格式直接用 YOLO 训练会报找不到标签文件必须先转换第 3 章会给出完整转换脚本。这一步不用急着跑代码先把格式和数量搞清楚后面能少返工一整轮。2.1 VOC 和 YOLO 标注格式的差异VOC 的 XML 用绝对值描述目标框下面是一段典型的吸烟检测标注annotation foldersmoking/folder filenameimg_0041.jpg/filename size width1280/width height720/height depth3/depth /size object namesmoking/name bndbox xmin412/xmin ymin228/ymin xmax689/xmax ymax517/ymax /bndbox /object /annotation逻辑说明这段标注描述的是 img_0041.jpg 这张 1280x720 的图里有一个类别为 smoking 的目标框的左上角在 (412, 228)右下角在 (689, 517)。坐标都是像素绝对值依赖图片原始尺寸。YOLO 的 txt 则把框的中心点、宽度、高度都除以图片宽高归一化后写成浮点数好处是换分辨率、做 mosaic 增强时不用回查原始尺寸。两种格式的关键差别可以看这张表对比项VOC XMLYOLO txt坐标基准像素绝对值归一化 0~1类别表示字符串 name整数 class id文件后缀.xml.txt训练前处理需要转换直接可用转换公式就一个x_center (xmin xmax) / 2 / width其余三个量同理。注意类别名和类别 ID 完全是两回事VOC 里写的是字符串YOLO 里写的是整数转换时靠 classes 列表做映射这个列表的顺序在后面 data.yaml 里还得再用一次。2.2 目录结构先数清再动手按我的习惯拿到手先跑一条 tree 命令把目录结构看全tree -L 2 -d smk_dataset/正常会看到 images 和 labels 两个主目录各自下面再分 train、val、test。YOLO 的要求是图片和标签同名不同后缀img_0041.jpg 对应的标签必须叫 img_0041.txt编号、大小写、下划线全部一致差一个字符这个样本就等于废了。先做一次数量核对find smk_dataset/images/train -name *.jpg | wc -l find smk_dataset/labels/train -name *.txt | wc -l第一行数训练集图片数量第二行数标签文件数量。两个数字一致是底线。如果 labels 少了说明部分图片没有标注这些图会被当背景样本参与计算吸烟框的召回率被整体拉低如果 labels 多了说明有孤儿标签文件YOLO 训练会跳过它但不会报错容易被忽略。验证集和测试集也各跑一遍同样的命令把三组数字列出来对比。数量对了还不算完还要核对文件名是否真的能对上。文件名对不上在数据量大时靠肉眼根本查不出来我一般用脚本快速校验import os from pathlib import Path img_dir smk_dataset/images/train label_dir smk_dataset/labels/train img_stems {Path(f).stem for f in os.listdir(img_dir) if f.endswith(.jpg)} label_stems {Path(f).stem for f in os.listdir(label_dir) if f.endswith(.txt)} missing_labels img_stems - label_stems orphan_labels label_stems - img_stems print(有图无标签:, missing_labels) print(有标签无图:, orphan_labels)逻辑说明把图片文件名和标签文件名的 stem 取出来做集合差集直接列出对不上的样本。有图无标签的要去补标或移出训练目录有标签无图的直接删掉否则 YOLO 训练前生成缓存时会反复警告。这一步会消耗一点时间但比起训练到一半发现样本数对不上、回头重跑要划算得多。2.3 标注分布统计框面积和类别数决定训练策略数字对齐之后还要看标注框的分布这一步直接决定后面要不要调 anchor、加增强。用一个小脚本统计import os from collections import Counter label_dir smk_dataset/labels/train box_areas [] boxes_per_img [] class_ids [] for fname in os.listdir(label_dir): if not fname.endswith(.txt): continue path os.path.join(label_dir, fname) with open(path, r) as f: lines f.readlines() boxes_per_img.append(len(lines)) for line in lines: parts line.strip().split() if len(parts) 5: continue class_ids.append(parts[0]) box_areas.append(float(parts[3]) * float(parts[4])) print(每张图框数分布:, Counter(boxes_per_img)) print(类别 ID 统计:, Counter(class_ids)) print(框面积分布(前10):, Counter(round(a, 4) for a in box_areas).most_common(10))逻辑说明遍历 labels 目录每个 txt统计每张图的标注框数量、类别 ID 分布再算每个框的归一化面积聚合。类别统计用来确认两个类的样本量是否悬殊吸烟检测数据集里 smoking 和 cigarette 两类数量差两三倍是常态差太多就得对少数类做过采样或者用增强时按类别加权。框面积普遍小于 0.01 的意味着数据以小目标为主例如远处的人手里夹着烟训练时要把输入分辨率往上抬imgsz 从 640 提到 960 甚至更高否则小烟头直接被下采样丢掉。单张图平均框数超过 3 的说明密集场景多后处理 NMS 的 iou 阈值要适当收紧。3. 把标注转成 YOLO 能用的格式转换脚本与四个边界坑如果数据给的是 VOC XML或者你从别的项目里扒了一套 VOC 标注就绕不开这一步转换。转换公式很简单真正的坑全在边界条件上我拆成脚本和踩坑两部分讲。3.1 批量转换脚本下面这段是完整可跑的转换脚本支持多类别import os import xml.etree.ElementTree as ET from pathlib import Path CLASSES [smoking, cigarette] # 按实际数据集的类别名和顺序改 def convert_voc_to_yolo(xml_path, out_dir): root ET.parse(xml_path).getroot() width int(root.find(size/width).text) height int(root.find(size/height).text) lines [] for obj in root.iter(object): name obj.find(name).text if name not in CLASSES: continue cls_id CLASSES.index(name) box obj.find(bndbox) xmin float(box.find(xmin).text) ymin float(box.find(ymin).text) xmax float(box.find(xmax).text) ymax float(box.find(ymax).text) bw xmax - xmin bh ymax - ymin if bw 0 or bh 0: continue # 坐标反了的脏框直接跳过 xc (xmin xmax) / 2.0 / width yc (ymin ymax) / 2.0 / height w bw / width h bh / height lines.append(f{cls_id} {xc:.6f} {yc:.6f} {w:.6f} {h:.6f}) out_name Path(xml_path).stem .txt with open(Path(out_dir) / out_name, w) as f: f.write(\n.join(lines)) os.makedirs(yolo_labels, exist_okTrue) for xml_file in Path(voc_labels).glob(*.xml): convert_voc_to_yolo(xml_file, yolo_labels) print(fconverted {xml_file.name})逻辑说明逐个解析 XML取 size 节点里的宽高遍历 object 拿类别名和框坐标按归一化公式算中心点和宽高写进与 XML 同名的 txt。脚本里两个关键参数CLASSES 列表的顺序就是训练时的类别 ID后面配 data.yaml 时必须和这个顺序严格一致跳过 bw 或 bh 小于等于 0 的框是为了防坐标反写导致的负值负宽高进 YOLO 训练会直接崩。如果你确认脏框数量很少跳过没影响但数量多的话建议改成坐标校正而不是跳过否则很多图会变成空标签。3.2 转换时容易踩的四个边界问题第一xmin 大于 xmax。现象是转换出来的 txt 里 w 是负值训练时报 AssertionError。原因是人工标注手滑把框画反了。解决是在转换脚本里做一次坐标校正min 和 max 互换而不是直接跳过跳过的图如果框很少会变成空标签对训练更不利。第二归一化后坐标超过 1。现象是不报错但 loss 不收敛验证集 mAP 一直在一个很低的水平徘徊。原因是某些标注框超出了图像边界比如烟头在画面边缘时标注工具允许把框拖出画布。解决的常规做法是先把 xmin、ymin、xmax、ymax 分别 clip 到 0 到 width或 height的范围内再归一化clip 后再检查一次宽高是否大于 0。第三XML 里有多余的空 object。现象是转换出来的 txt 比实际框数少对照图片看少了一个框还不好定位。原因是标注工具残留了未命名对象。解决是在解析时把 name 为空或以 unnamed 开头的 object 直接跳过同时在前一步统计阶段打印每个文件的框数和标注工具导出的统计对一次账。第四类别名大小写不一致。现象是 smoking 和 Smoking 两个字符串并存转换后同一类被拆成两个 ID类别统计里多出一类。原因是不同标注员标注习惯不同。解决是在 CLASSES 匹配前把 name 统一转小写批量转换时顺手归一化比返工重导快得多。这四类问题在公网下载的数据集里出现概率不低转换后必须再跑一次 2.3 的统计脚本确认类别数和你预期的一致再进训练。3.3 缺标注的图怎么补标注工具的选型转换完如果发现存在空标签文件就得补标。补标工具我一般用两个。量少图简单就用 labelImg单类矩形框足够快捷键顺手导出 VOC 或 YOLO 格式都可以但要注意保存路径里不能有中文很多老版本在中文路径下保存会报错。量大多人协作就用 CVAT网页版部署好之后可以多人同时标还支持用训练好的模型做预标注再人工微调对吸烟这类小目标场景能省一半时间。两个工具的共同点是导出前检查一遍类别名是否和 CLASSES 列表完全一致这类问题在补标环节高发反复出现就说明流程上缺一道审核补标的人不背锅规范的人要兜底。4. 训练吸烟检测模型YOLOv8 配置与参数选型数据准备好了用 YOLOv8 训练自己的数据集核心就三个文件data.yaml、预训练权重、训练命令。版本我以 YOLOv8 为例YOLOv5 的操作逻辑基本一致参数名也通用。4.1 数据集配置文件与类别对齐先写 data.yaml# smoking.yaml path: /data/smk_dataset train: images/train val: images/val names: 0: smoking 1: cigarette参数说明path 是数据集根目录绝对路径train 和 val 是相对 path 的图片目录YOLO 会自动在同级 labels 目录下找对应 txt。names 的 0、1 顺序必须和转换脚本里的 CLASSES 顺序一致这是整个流程里最容易出错、且出错后最不容易察觉的一环。如果你转 txt 时把 cigarette 放在第一个这里却写成 0: smoking模型训练时会把香烟标签和吸烟标签完全互换验证集 mAP 看起来还行一跑实际场景全是错的。这种错我犯过一次当时花了两天排查最后发现就是一行顺序问题。注意每次新建数据集配置先跑一遍 2.3 的类别统计脚本把输出的类别 ID 分布和 names 逐行对一遍再启动训练。4.2 训练命令与关键超参的取舍训练命令我一般这样起yolo detect train \ datasmoking.yaml \ modelyolov8s.pt \ epochs150 \ imgsz960 \ batch16 \ patience30 \ workers8参数说明imgsz960 是因为吸烟场景里人是全身目标但烟头和手持动作区域偏小640 输入下采样后小目标特征基本丢了960 是验证下来性价比比较高的值显存不够就降到 800 或 768。patience30 是早停耐心值连续 30 个 epoch 验证集 mAP 没有提升就自动停吸烟数据集一般 80 到 120 个 epoch 能收敛patience 设太大会白烧 GPU 时间设太小会在指标正常波动期被误停。batch16 在 960 分辨率下大概是 24G 显存的水平小卡就把 imgsz 降回 768、batch 减半。数据增强参数也可以按需改。默认的 mosaic 和 mixup 在检测小目标时效果不错但如果标注框本身有噪声mosaic 会把错误框放大成更混乱的样本训练时 val 指标震荡厉害。这时把 mosaic 概率从 1.0 降到 0.5把 scale 的浮动范围收窄一点往往比强行改 anchor 更有效。学习率一般不动lr0 默认 0.01 在大多数情况下够用只有出现 loss 早期崩掉的迹象时才往下调。权重选型上我常用 yolov8s 起步而不是 nano。nano 收敛快但小目标上限低吸烟检测这类细目标场景吃亏s 在推理速度和精度之间更平衡。如果目标是四路摄像头并发推理再根据实测帧率决定要不要降回 nano。4.3 训练日志里哪些指标值得盯训练过程最值得盯的不是 train loss而是 val 指标。每轮日志里看 metrics/precision(B)、metrics/recall(B)、metrics/mAP50(B)、metrics/mAP50-95(B) 四列。前几轮 mAP 震荡是正常的属于类别特征在收敛前期的正常波动。我遇到 loss 下降到 1.2 附近就卡住、mAP 不动的情况多数是标注框噪声太大回去清洗数据比继续跑 epoch 更有效。Smoking 和 cigarette 两类如果样本量悬殊recall 曲线会在某一轮之后出现明显分化哪类样本少哪类召回率低这时候优先补样本而不是调阈值。学习率曲线也要扫一眼前几个 epoch 如果 loss 就快速掉到极低值大概率是类别数没对齐模型在用单一类别过拟合。偶尔会出现 val 指标一路走高但 train loss 反弹的情况这说明学习率偏高把 lr0 从 0.01 降到 0.005 重跑一轮如果 train loss 正常但 val loss 持续高于 train loss 一个身位则考虑增强过强把 mosaic 概率降下来。训练收尾时还要看最后一轮的混淆矩阵YOLOv8 训练目录里会自动生成 confusion_matrix.png。打开它重点看两个位置主对角线值高不高以及 smoking 被分到背景那一格的值大不大。背景误检高的多半是负样本不足直接跳到 5.4 那条处理。5. 避坑清单标注与训练中的真实翻车记录这一章是血泪经验的汇总每条都是实际项目里踩过的坑按现象、原因、解决三段写。5.1 标签文件空白训练集 mAP 直接为零现象训练能跑但验证集 mAP 始终是 0precision 也接近 0日志里没有明显报错。原因labels 目录里存在空 txt 文件YOLO 训练时这些图片被当背景参与计算正样本数量被稀释模型根本学不到吸烟特征。这类空标签经常是转换脚本跳过脏框、或者标注导出时漏写导致的。解决训练前加一道空文件检查统计所有 txt 的行数凡是 0 行的单独列出来核对对应图片。如果确实有目标就补标如果本来就是背景图移出 train 目录不要留在里面当噪声。find smk_dataset/labels -name *.txt -empty | wc -l运行后数字不为 0就说明问题存在处理完再训练。5.2 类别 ID 错位烟头被当成吸烟现象训练正常、mAP 正常但打开预测图发现模型把没在抽烟的人嘴里也框成 smoking手里拿着未点燃的烟也被当成吸烟。原因data.yaml 的 names 顺序和转换脚本 CLASSES 顺序不一致或者数据集本身两个类别标注串了。YOLO 训练只看 ID 不看类别名ID 对错了模型学到的语义就反了。解决训练前跑一次类别统计把每个类别 ID 和数量打印出来跟数据集原始统计表对一遍确认 0 是 smoking、1 是 cigarette 再开始训练。训练完再随机抽 20 张预测图人工看一遍这一步能拦住大多数 ID 错位问题。5.3 小目标漏检远处的烟头完全没有框现象近距离视频检测得很准换成远距离机位或者画面里的人变小了漏检率飙升特别是烟头这种只占几十像素的区域直接没了。原因输入分辨率不够imgsz 960 虽然比 640 好但如果目标高度只占几十个像素下采样后特征就没了。另一个原因是部分标注框画得过大把烟头连同整个手框进去训练时特征对齐失败模型学不到紧凑的目标边界。解决先看框面积分布确认小目标占比再考虑把 imgsz 抬到 1280或者对小目标类过采样。切图推理我也试过精度提升明显但在视频流场景里延迟太高实时监控不合适离线分析可以用。5.4 验证集指标好实拍视频一直误报现象mAP50 到了 0.9但接真实摄像头画面测试背景里红色、橙色物体被频繁误报成烟头尤其是有烟雾或水汽的场景。原因训练集里负样本太少模型没见过足够多会误判的场景比如红色包装袋、暖水瓶、蒸汽。mAP 是在同一分布的数据集上评的分布一旦偏移指标就失去参考价值。解决从实际摄像头里抽一两个小时视频截帧做成负样本集挑误报严重的帧加入训练集重训。这是检测项目上线前必做的一步吸烟检测在室内外场景切换时尤其容易暴露这个问题场景光照变化大负样本要覆盖不同亮度和背景。5.5 改了标注重新训练指标却和上一轮一模一样现象补标或修正了一批框之后重新训练发现 loss 曲线和上一轮几乎一样好像改动没有生效。原因YOLO 在第一次训练前会生成 labels.cache 缓存文件下次启动时直接读缓存不再重新扫描标签目录。你改的 txt 文件如果没有删掉缓存训练用的还是旧标签。解决每次改完标注删除数据集目录下所有 *.cache 文件再启动训练。这一步是标准的后悔药清法我每次补标后都会顺手加一条清理命令。这五条看着零散其实都指向同一件事训练前把数据链路整个查一遍比训练中反复试错高效得多。我从那以后养成的习惯是每次换新数据集先跑 2.3 的统计、再跑空文件检查、再核对类别顺序三道过了才启动训练。6. 上线前最后一步用 mAP 和可视化判断模型能不能用训练完不是直接接流就完事还得做三道验证我每次做检测项目都强制走一遍。6.1 跑标准评估拿到统一口径的指标用官方评估命令在测试集上跑一遍yolo detect val modelbest.pt datasmoking.yaml splittestsplit 指到测试集没分测试集就用 val。mAP50 到 0.85 以上算基本可用真正决定上线的是 mAP50-95如果 mAP50 有 0.9 而 mAP50-95 只有 0.5说明框的位置不够准部署后定位误差会被放大。6.2 可视化复核比数字更真实指标会骗人可视化才能暴露问题。批量跑一批测试图保存带框预测结果。from ultralytics import YOLO import glob model YOLO(runs/detect/train/weights/best.pt) imgs glob.glob(smk_dataset/images/test/*.jpg)[:100] for img in imgs: model.predict(img, conf0.35, saveTrue, save_dircheck_output)conf0.35 是部署前常用的置信度阈值如果这个阈值下误检太多就往上调到 0.5 再看。这一步人工扫图重点关注漏检和重复框。6.3 导出 ONNX 先锁死输入尺寸可视化确认没问题后导出yolo export modelbest.pt formatonnx imgsz960 dynamicFalse opset12dynamicFalse 固定输入尺寸部署端不用处理动态形状推理稳定性好。开 dynamic 在 TensorRT 转模型时容易报 shape 推导错误要么重导一次要么一开始就锁死。导出后用 onnxruntime 跑同样的测试图和 PyTorch 侧对比坐标误差在几个像素内正常超过就要查预处理是不是不一致。从那以后我每次换新的数据集拿到手第一件事就是跑这套验证流程统计标签、查空文件、核对类别顺序、可视化复核、导出前锁尺寸。这套流程看着繁琐但能拦截九成以上的低级问题比训练完再回头查省太多时间。希望帮到你。本文还有配套的精品资源点击获取