简介面向计算机视觉目标检测项目与YOLO系列算法学习者这份数据集聚焦公共场所吸烟行为识别提供训练模型所需的原始图像与标定数据。压缩包内共594个文件由297张jpg图片和297个xml标注文件一一对应组成xml中记录目标类别与边界框坐标可直接转换为YOLO训练所需的txt格式也可用于VOC格式的检测流程省去自行采集图像和手工标注的繁琐环节。整个压缩包约59.11MB文件按图像编号命名图片与标注同名对应目录结构清晰便于快速划分训练集和验证集。目前已累计375人浏览学习适合正在积累检测数据、验证模型效果或完成课程设计/毕业设计的开发者参考。数据内容可直接用于数据预处理与模型训练是快速上手吸烟检测任务的基础数据素材。1. 抽烟检测为什么先卡在数据上这个数据集能帮你省掉什么做工地安全、园区安防或者加油站巡检的视觉方案时「抽烟行为识别」几乎是必点题但真正动手的人都知道模型结构根本不难难的是凑齐一批高质量标注图。人工巡检拍回来一堆监控截图要一帧帧拉框、分类、复核两个人标两周是常有的事。这个标题里的「YOLO抽烟目标检测数据集包含图片和标定数据」解决的就是这个最烧时间的环节——它直接给你一套已经按 YOLO 格式标好框的图片拿到手就能喂给训练脚本省掉从零标注的漫长周期。但「拿到数据集」和「把模型训出来」之间还有一段路标注格式对不对、类别编号和配置对不对、训练参数怎么设每一步都可能翻车。这篇文章就沿着「校验数据 → 配置训练 → 排查问题 → 评估落地」这条线走一遍把我自己处理这类数据集时踩过的坑和常用做法梳理出来新手能照着跑通熟手可以对照查漏。下面先从拿到数据后的第一件事说起——摸清家底。2. 先摸清数据集家底目录结构、YOLO 格式校验与类别分布统计2.1 目录结构长什么样图片、标注文件与划分文件的对应关系标题里说的「标定数据」指的就是目标检测里的标注数据。这类数据集最常见的组织方式是图片文件和标注文件分开两个目录文件名一一对应。图片叫frame_000123.jpg标注就是同目录或隔壁目录下的frame_000123.txttxt 里每一行代表一个检测框。YOLO 格式的每行是五个值类别编号、归一化后的中心点 x、中心点 y、框宽 w、框高 h。拿到数据集第一件事不是急着开训练而是先把目录树打印出来看一遍。我一般会这么做# 用 find 查看图片和标注的对应结构 find ./dataset -maxdepth 3 -type d | sort # 随机挑 5 张图确认同名标注文件存在 ls ./dataset/images/train | head -5 for f in $(ls ./dataset/images/train | head -5); do base${f%.*} ls ./dataset/labels/train/${base}.txt 2/dev/null || echo 缺失: $f done这段脚本先看目录层级再抽查图片是否有同名标注。注意2/dev/null的作用是让「找不到标注」的报错不刷屏只输出缺失的文件名。实际操作中我见过有人拿到数据直接改data.yaml开训训到一半发现 val 集标注目录整个是空的白白跑了十几个小时。再看训练集、验证集、测试集怎么划分。有的数据集是已经分好train/val/test三个文件夹有的是给一个train.txt文本文件里面逐行写图片路径。两种都常见但后续写data.yaml时路径写法不一样前者直接指目录后者要脚本读取文件列表。我习惯拿到数据后先统一成目录结构省得后面维护配置时记混。2.2 用脚本校验 YOLO 标注坐标范围、空标注与损坏图片这是我最想强调的一步。绝大多数「训练出来 mAP 挺高但实际预测一塌糊涂」的案例根子都在标注数据有问题而不是模型参数没调好。标注文件有问题训练过程不会直接报错它会静悄悄地学坏。我每一次拿到数据先跑下面这个校验脚本花不了两分钟# check_dataset.py # 校验 YOLO 格式标注的合法性文件缺失、空标注、坐标越界、类别编号越界 import os import sys from pathlib import Path def validate(img_dir, label_dir, nc): img_dir, label_dir Path(img_dir), Path(label_dir) img_exts {.jpg, .jpeg, .png, .bmp, .webp} missing_label [] # 图片没有对应标注文件 empty_label [] # 标注文件为空 bad_line [] # 行格式不对 out_of_range [] # 坐标越界 class_overflow [] # 类别编号超过 nc-1 imgs [p for p in img_dir.iterdir() if p.suffix.lower() in img_exts] for img in imgs: label_file label_dir / (img.stem .txt) if not label_file.exists(): missing_label.append(str(img)) continue lines label_file.read_text().strip().splitlines() if not lines: empty_label.append(str(label_file)) continue for line in lines: parts line.split() if len(parts) ! 5: bad_line.append(f{label_file}: {line}) continue cls_id int(parts[0]) x, y, w, h map(float, parts[1:]) if cls_id nc: class_overflow.append(f{label_file}: class{cls_id}) # 归一化坐标允许极小越界但超过 1e-2 说明单位搞错了 if not (0 - 1e-2 x 1 1e-2 and 0 - 1e-2 y 1 1e-2): out_of_range.append(f{label_file}: x,y{x},{y}) if w 0 or h 0: bad_line.append(f{label_file}: w,h{w},{h}) print(f图片总数: {len(imgs)}) print(f缺失标注: {len(missing_label)}) print(f空标注文件: {len(empty_label)}) print(f格式错误行: {len(bad_line)}) print(f坐标越界: {len(out_of_range)}) print(f类别编号越界: {len(class_overflow)}) for item in missing_label[:10] empty_label[:5] bad_line[:5]: print( , item) if __name__ __main__: validate(sys.argv[1], sys.argv[2], int(sys.argv[3]))几个关键参数说明nc是类别总数从数据的类别配置文件里读坐标检查的1e-2容差是给标注工具常见的四舍五入留余地超过这个范围基本可以断定标注单位有问题——有次我在一个数据集里发现坐标是像素值而不是归一化值脚本一跑全是越界立刻定位到问题省了半天排查时间。还要说明一点空标注不一定是错误图片里确实可能没有目标但你要知道哪些图是空的否则训练时会误导模型学「空背景」。2.3 统计类别分布判断要不要做类别权重或去重校验通过不代表可以直接训练。类别不平衡在这类数据里非常普遍——「抽烟」这个正样本的框数通常远少于背景负样本而且不同类别之间数量也可能差很多。跑一个统计脚本看每个类别出现多少次、平均每张图几个框能帮你决定后续要不要做采样策略。# class_dist.py # 统计每个类别在训练集中的框数量与图片数量 import sys from collections import Counter from pathlib import Path def stat(label_dir, nc): label_dir Path(label_dir) cls_counter Counter() # 类别编号 - 框数 img_with_obj 0 # 至少一个有效框的图片数 total_imgs 0 for txt in label_dir.glob(*.txt): total_imgs 1 has_obj False for line in txt.read_text().strip().splitlines(): parts line.split() if len(parts) ! 5: continue cls_counter[int(parts[0])] 1 has_obj True if has_obj: img_with_obj 1 print(f标注文件总数: {total_imgs}) print(f含目标的图片数: {img_with_obj}) print(f纯背景图片数: {total_imgs - img_with_obj}) print(--- 各类别框数 ---) for cls_id in range(nc): print(fclass {cls_id}: {cls_counter[cls_id]} boxes) if __name__ __main__: stat(sys.argv[1], int(sys.argv[2]))统计完怎么看结果如果某个类别框数只有另一个的十分之一常见做法是先不急着上复杂的采样而是看是不是漏标了一部分图——在抽烟检测场景里阴天、夜间、逆光下的样本容易被漏标这一类补标比调采样权重更有效。另一个判断点是「纯背景图片占比」如果超过三成模型学到的是「看不见就判没目标」误报率会明显偏高这时候优先做的是剔除或重新标注这些背景图而不是急着加负样本挖掘。这两步做完数据集才真正达到可以训练的状态。3. 把数据集跑进训练数据配置与 YOLOv5/v8 最小训练命令3.1 准备数据配置文件路径、类别名与 train/val 划分数据校验完下一步是写训练框架需要的数据配置。我用 YOLOv5 举一套最小配置YOLOv8 的写法基本相同只是train/val键的路径含义略有差别。# smoking.yaml # 路径相对于训练命令的执行目录建议用绝对路径避免歧义 train: /data/smoke_det/images/train val: /data/smoke_det/images/val # 类别数量和标注校验时的 nc 务必一致 nc: 2 # 类别名顺序必须和标注文件里的数字一一对应 # 标注里写 0 就代表这行第一个名字写 1 代表第二个 names: 0: smoke 1: person这里最容易被忽略的是names的顺序。标注文件里的每个框只存类别编号不存名字编号对应的含义全在names里定义。我见过一个项目数据集里 0 是烟、1 是人配置文件里写反了训练时损失函数一直在降看起来一切正常一验证才发现模型把所有烟都认成了人。所以配置完第一时间打印一遍类别对应关系别靠记忆。另外注意路径的写法。YOLOv5 会自动在train路径下找images目录的兄弟目录labels也就是说如果图在/data/smoke_det/images/train标注就得在/data/smoke_det/labels/train目录层级不能随意摆。有些数据集自带的划分文件是train.txt路径列表那就得先确认列表里的路径是否真实存在或者干脆自己重写划分文件省得训练时一路报「image not found」然后默默跳过那些图。3.2 选择预训练权重与图像分辨率先跑通再谈精度配置就绪后第一次训练不要追求指标目标是「在完整流程上跑通」。我习惯先用 COCO 预训练权重做迁移学习而不是随机初始化——抽烟检测的底层特征人、手、物体边缘和 COCO 里的「人」类高度重叠用小数据集从头训很难收敛。# 最小训练命令先跑 30 个 epoch 验证流程 python train.py \ --data smoking.yaml \ --weights yolov5s.pt \ --img 640 \ --batch 16 \ --epochs 30 # 流程跑通后正式训练加大分辨率并放宽训练轮数 python train.py \ --data smoking.yaml \ --weights yolov5s.pt \ --img 1280 \ --batch 8 \ --epochs 200几个参数的调整逻辑说一下。--img是训练分辨率抽烟检测里「烟」本身是小目标640 下可能只有十几像素提高到 1280 能明显提升小目标召回但代价是显存占用翻倍、训练时间拉长batch在 1280 分辨率下通常得从 16 降到 8。--weights选yolov5s.pt是因为 s 版本在精度和速度之间平衡最好数据量不大时上 l 或 x 反而容易过拟合。第一次跑 30 个 epoch 只看流程是否顺畅确认 loss 在下降、验证集有评估输出再放长训练。显存不够时常见做法是把--img 1280降回 960 或 640同时开启--cache把图片缓存进内存减少磁盘 IO。如果不确定自己的卡能不能跑可以先看训练日志开头打印的模型参数量和 batch 大小报错 OOM 就按比例减 batch比如 16 减到 8、8 减到 4。这些参数没有绝对正确值我在不同项目里组合都不一样但「小分辨率先通流程再逐步加大」这条路径从没让我走偏过。3.3 训练过程中的关键日志看 mAP 曲线判断欠拟合与过拟合训练跑起来后不是挂着等结果就完事。每个 epoch 结束日志里会有几组数字box_loss、obj_loss、cls_loss分别代表框回归损失、目标存在性损失、分类损失验证集上的mAP0.5和mAP0.5:0.95是精度指标。我一般盯两个点训练 loss 是否还在降、mAP 是否还在涨。如果训练到 150 轮时 mAP 还在稳步上升说明数据量还能支撑更久直接加--epochs继续训如果 mAP 在 50 轮就停滞、训练 loss 还在降典型过拟合这时不是加轮数而是回退到 checkpoint 并开启更强数据增强。YOLOv5 里--hyp可以指定自定义增强参数但新手阶段我不建议动它先跑默认配置观察曲线再说。这里要特别提一个「看起来过拟合其实是数据问题」的场景如果obj_loss降不下去或验证 mAP 忽高忽低震荡剧烈先回第 2 章重新跑校验脚本检查是不是 val 集里混入了标注错误或空标注图片。我吃过一次亏训练 loss 平滑下降但 mAP 一直卡在 0.3 上下折腾了一周调参最后发现是 val 集里有两百多张图的标注文件是上一批数据遗留的空文件。数据的问题参数永远调不出来。模型结构在这个项目里从来不是瓶颈数据清洗才是决定精度的关键变量。4. 避坑抽烟检测最容易踩的 5 个数据坑现象、原因与解法坑一类别编号和 names 顺序不一致mAP 高但预测全错现象训练日志里 mAP 挺好看验证集一测模型把「吸烟的人」全部识别成另一个类别或者输出的类别名和实际内容对不上。原因标注文件里的数字编号和data.yaml的names列表顺序没有一一对应模型学到的是「编号和语义」的错位映射。解决训练前打印标注文件的类别编号集合和names逐项核对训练完先拿几张验证图跑detect.py肉眼看预测框的类别名是不是正确的语义。我现在的习惯是「先测 3 张图再决定要不要训第二轮」这个习惯帮我挡掉了至少两个标注错位的项目。坑二数据集放在中文路径下训练时图片加载失败现象Windows 上跑训练日志偶尔报Image not found但打开目录文件明明在。原因OpenCV 的imread对中文路径支持不好读取返回空对象模型拿到的是大量空图在训练。解决把整个数据集挪到纯英文路径比如D:/smoke_data/...目录名不要带空格和中文。热水器这种错误特别隐蔽因为报错不连续只缺一部分图训练照样能跑完但精度就是上不去。另外--cache开启后这类问题会提前暴露因为缓存图片阶段就会批量报错。坑三纯背景图占比过高模型学会「没看见就说是背景」现象验证时召回率还行但实际视频推理里大量漏检尤其是小目标。原因数据集里很多张图是纯背景标注文件为空模型学到「大部分时候没有目标」预测倾向保守置信度普遍偏低。解决统计纯背景图占比超过两成就考虑剔除或补充正样本。如果场景里确实有大量空背景时段更好的做法是把这些图保留但强化正样本增强让模型见过足够多的「小目标难例」否则部署后误报漏报都控制不住。坑四远处烟头是小目标640 分辨率下特征消失现象近距离检测效果好摄像头远端区域几乎无检出提高置信度阈值也没用。原因抽烟的视觉特征主要是烟头亮点和烟雾在 640 分辨率下远处目标只有几个像素丢失纹理信息。解决训练和推理都改用 1280 分辨率更彻底的做法是推理阶段对画面按区域切块检测具体操作下一章展开。先说结论这类项目里「场景距离多样性」比「图片数量」更影响最终效果收数据阶段就应该刻意多收远景样本。坑五标注框坐标越界Mosaic 增强时直接训练崩溃现象训练跑了两百多个 batch 后突然报错日志里有invalid box或assertion failed。原因标注文件里个别框的 x、y、w、h 略超出[0,1]Mosaic 拼接增强后越界放大触发前向计算的数值检查。解决用第 2 章的校验脚本把所有坐标clip到[0,1]区间再重新保存标注。操作很简单x min(1.0, max(0.0, x))但前提是越界量很小如果大量框整体偏移那就不是 clip 能解决的要检查标注工具导出时是不是单位错了。5. 把「能跑通」变成「能用」评估闭环与误报治理的两个技巧训练出模型只是中点真正决定项目能不能交付的是误报率。我接手过好几个抽烟检测项目模型在数据集验证集上都达标一放到真实监控画面就崩——问题出在验证方式和训练数据来源太单一。我这边的收尾流程分两步你可以直接照搬。第一步建立视频回归测试集。不要只测图片从现场拉一段连续视频截成多个时段白天、黄昏、夜间、阴天各 3 到 5 分钟用训练好的模型跑一遍推理统计每时段的检出数和误报数。这一步会暴露图片验证集发现不了的问题比如夜间的烟头高亮会被误判穿白色衣服的人路过也可能触发还有人拿手机打电话手势接近嘴边模型很容易跟抽烟混淆。我一般会专门收集这类「类烟手势」的负样本攒到几十张就够混进训练集重新训一轮误报率通常能压下来一半。第二步用置信度和 NMS 参数做最后的现场调优。推理脚本里通常有--conf-thres和--iou-thres两个参数conf-thres从默认的 0.25 提到 0.4可以压掉大部分低置信度误报但也会牺牲小目标召回iou-thres控制重叠框的合并力度场景里人挨得近时适当调低到 0.3 能减少两个框叠在一个目标上的情况。这两个值没有通用最优解我每次都是在视频回归测试集上试三组组合用「误报率最低且关键场景不漏检」的做最终配置。调参前先确认评估标准——抽烟检测这类场景客户宁可漏检也不愿一天到晚误报所以优先压误报再保召回。另一个值得做的验证是把模型导成 ONNX 或者 TensorRT 后在目标设备上跑一遍速度确认推理帧率满足实时要求。训练的显卡和部署的芯片算力差很远有些模型在 PC 上 30 FPS换到边缘设备只剩 5 FPS这时候要么换更小的模型结构要么把推理分辨率降到 960。这也是我吃过亏的地方一开始只看 mAP没测部署速度结果模型精度合格但根本跑不动白训了两周。现在我做这类项目固定习惯是「先校验数据 → 跑通流程 → 视频回归 → 再调阈值」四个环节都确认完才敢说模型交付。目标检测的数据集从来不缺缺的是那股「把标定数据真正用好」的责任心。希望这些整理对你有帮助。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?