
简介这份吸烟行为检测数据集面向计算机视觉开发者与目标检测学习者可用于训练和验证抽烟场景下的目标识别模型适用于课堂演示、课程设计或算法对比实验。资源共包含991张原始图片每张图片均配有对应的YOLO格式标注文件另附1个yaml配置文件压缩包内文件总数1983个以jpg图像与txt标注为主整体约44.7MB目录结构规整便于直接接入YOLOv8训练流程。据描述该数据集在常规训练条件下平均识别率可达88.3%可作为吸烟检测任务的基线数据参考。目前已有110人学习下载适合希望快速搭建抽烟识别原型、验证模型效果或扩充自有数据集的读者使用能帮助节省标注与整理成本将精力集中在模型调参与部署环节。1. 吸烟数据集 991 张原始图片88.3% 识别率背后的真实门槛手上只有 991 张原始图片标注成 yolov8 格式跑出平均识别率 88.3%——这个数字放在公开数据集上不算亮眼但放在吸烟行为检测这个具体场景里它其实已经跨过了「能不能用」的分界线。原因很简单吸烟检测不是通用目标检测它要区分的是「手、烟、嘴」三者之间的空间关系烟头在画面里可能只占十几个像素还经常被手指遮挡、被逆光吃掉细节。991 张图能撑起 88.3%说明数据集的场景覆盖和标注质量是过关的而不是靠堆量堆出来的。这个方向适合谁做智慧工地、加油站、化工厂、校园禁烟区监控的算法工程师或者手里有几百张现场图、想快速验证吸烟检测可行性的团队。它不适合想直接拿去做通用吸烟识别的人——991 张图的泛化边界很明确换个摄像头角度、换批人、换光照掉点几乎是必然的。所以这篇不吹数据集多强而是把「991 张图怎么标、怎么训、88.3% 怎么复现、掉点怎么救」这条链路讲透让你拿到手能跑跑完知道边界在哪。2. 991 张图为什么够用吸烟检测的数据集特性与标注策略2.1 吸烟检测和通用目标检测的差别在哪通用目标检测数据集动辄几万张是因为类别多、场景杂、类间差异大。吸烟检测不一样它的目标类别通常只有两三个烟cigarette、人脸或嘴部区域、手部。真正难的不是「找到烟」而是「判断烟是不是在嘴边、是不是被人拿着」。这意味着标注策略比图片数量更关键。991 张图如果全是同一角度、同一光照、同一批人那它训出来的模型就是个「记忆机器」换个场景立刻崩。但如果这 991 张覆盖了室内、室外、白天、夜晚、正面、侧面、遮挡、逆光这些维度那它的有效信息量可能比 5000 张同质图还大。我一般拿到一个小数据集第一件事不是看数量而是抽样 50 张看场景分布——如果 50 张里超过 30 张长得差不多这个数据集就得先补场景而不是急着训。另一个差别是标注粒度。吸烟检测里烟头目标极小如果按常规目标检测的标注习惯只框一个大区域模型学到的就是「人脸附近有东西」而不是「烟在嘴边」。所以 yolov8 格式标注时烟这个类别的框要尽量贴紧烟头本身哪怕只有 8×10 像素也要单独标出来不要和手或脸合并。2.2 yolov8 格式标注的四个硬性要求yolov8 的标注格式是每张图对应一个同名 .txt 文件每行一个目标格式为class_id x_center y_center width height其中坐标全部是归一化到 0~1 的相对值。这里有几个容易翻车的地方第一class_id 从 0 开始连续编号不能跳号。如果你有「烟」「人脸」「手」三类就是 0、1、2不要写成 1、2、3。第二x_center 和 y_center 是框中心点相对整图宽高的比例不是左上角坐标。很多人从 VOC 的 xmin/ymin/xmax/ymax 转过来时忘了换算中心点导致框整体偏移。第三width 和 height 也是相对整图宽高的比例不是像素值。如果原图是 1920×1080框宽 100 像素那 width 就是 100/1920≈0.052。第四坐标必须裁剪到 [0,1] 区间。标注时如果框超出图像边界转换后会出现负值或大于 1 的值yolov8 训练时虽然会做裁剪但最好在转换阶段就处理掉避免训练时出现异常框。下面是一个从 VOC 格式转 yolov8 格式的脚本我实际项目里用过多次直接改路径就能跑import os import xml.etree.ElementTree as ET # 类别映射必须和 data.yaml 里的 names 顺序一致 CLASS_MAP {cigarette: 0, face: 1, hand: 2} def voc_to_yolo(xml_path, img_w, img_h, out_txt_path): tree ET.parse(xml_path) root tree.getroot() lines [] for obj in root.findall(object): name obj.find(name).text.strip() 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) # 裁剪到图像边界内防止越界框 xmin max(0, min(xmin, img_w)) xmax max(0, min(xmax, img_w)) ymin max(0, min(ymin, img_h)) ymax max(0, min(ymax, img_h)) # 转中心点宽高再归一化 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 # 过滤掉宽高为0的无效框 if w 0 or h 0: continue lines.append(f{cls_id} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}) with open(out_txt_path, w) as f: f.write(\n.join(lines)) # 批量转换示例 img_dir images xml_dir annotations out_dir labels os.makedirs(out_dir, exist_okTrue) for xml_file in os.listdir(xml_dir): if not xml_file.endswith(.xml): continue stem os.path.splitext(xml_file)[0] # 这里需要根据实际图片尺寸读取示例用固定值实际应从图片读取 img_w, img_h 1920, 1080 voc_to_yolo(os.path.join(xml_dir, xml_file), img_w, img_h, os.path.join(out_dir, stem .txt))这段脚本的关键点有三个一是 CLASS_MAP 的顺序必须和训练时 data.yaml 里的 names 完全一致否则类别会错位二是坐标裁剪放在归一化之前避免出现负值三是过滤掉宽高为 0 的框这种框在标注时可能是误操作产生的留着会干扰训练。实际使用时img_w 和 img_h 建议用 PIL 或 OpenCV 从图片实际读取不要写死因为数据集里可能混有不同分辨率的图。2.3 991 张图怎么划分训练集和验证集小数据集的划分比大数据集更敏感。991 张图如果按 8:2 随机划分验证集只有 198 张评估结果的波动会很大——今天 88.3%明天换个随机种子可能就 85% 了。我一般会做两件事第一按场景分层抽样而不是纯随机。比如先给图片打上「室内/室外」「白天/夜晚」「正面/侧面」的标签然后每个场景按比例抽验证集。这样验证集能覆盖所有场景评估结果更稳定。第二如果数据量实在少可以用 5 折交叉验证来替代单次划分。yolov8 本身不直接支持交叉验证但可以手动跑 5 次每次换不同的验证集最后取平均。这样得到的 88.3% 比单次划分可信得多。划分完成后目录结构建议这样组织dataset/ images/ train/ val/ labels/ train/ val/ data.yamldata.yaml 的内容path: ./dataset train: images/train val: images/val nc: 3 names: [cigarette, face, hand]这里 nc 是类别数names 的顺序必须和标注时的 class_id 对应。如果只有「烟」和「人脸」两类就改成 nc: 2names 里去掉 hand。3. 用 yolov8 在本地跑通 991 张图的最小训练命令3.1 环境配置别在版本上翻车yolov8 的环境配置本身不复杂但版本兼容性是血泪经验。我一般用 Python 3.9 或 3.10太新的版本3.12有些依赖包还没跟上。安装命令pip install ultralytics8.0.200 pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118这里 ultralytics 的版本我固定用 8.0.200不是因为它最新而是因为这个版本在我手头的 GTX 1660 Ti 和 RTX 3060 上都验证过训练稳定。如果你用 50 系显卡可能需要更新的 CUDA 版本但 991 张图这个量级1660 Ti 完全够用没必要追新卡。安装完成后用下面这行验证环境yolo checks它会输出 Python 版本、torch 版本、CUDA 是否可用、GPU 型号等信息。如果 CUDA 显示不可用先检查驱动和 torch 版本是否匹配不要急着改代码。3.2 训练命令与关键参数怎么设最小训练命令其实就一行yolo detect train datadataset/data.yaml modelyolov8n.pt epochs100 imgsz640 batch16但这一行里每个参数都值得说清楚modelyolov8n.pt 用的是 nano 模型参数量最小适合 991 张图这个量级。如果你用 yolov8m 或 yolov8l参数量大小数据集上更容易过拟合反而掉点。我一般先用 n 跑一版基线如果欠拟合再换 s。epochs100 是上限不是固定值。yolov8 默认开启早停patience50如果 50 轮内验证集指标没提升就自动停。991 张图通常 60~80 轮就收敛了跑满 100 轮的情况很少。imgsz640 是输入分辨率。吸烟检测里烟头目标小理论上提高分辨率有帮助但 991 张图如果原图分辨率不高强行拉到 640 以上只会增加计算量不会提升精度。我一般先看原图短边如果短边小于 640就按原图短边对齐到 32 的倍数来设。batch16 在 8GB 显存上比较稳。如果显存不够改成 8 或 4同时把学习率按比例调小。yolov8 默认学习率是 0.01batch 减半时学习率可以降到 0.005 左右。训练过程中损失曲线和 mAP 曲线会实时输出。如果训练损失持续下降但验证损失开始上升就是过拟合的信号这时候要么加数据增强要么减少 epochs要么换更小的模型。3.3 数据增强参数怎么调才不掉点yolov8 默认开启 mosaic、mixup、hsv 增强。991 张图这个量级增强是必要的但参数不能照搬默认值。我一般会调整这几个mosaic1.0 默认开启把 4 张图拼成 1 张。这对小数据集有帮助但如果你的场景里目标位置有强规律比如烟总在画面下半部分mosaic 会破坏这种空间先验反而掉点。这时候可以把 mosaic 降到 0.5 或关掉。hsv_h0.015、hsv_s0.7、hsv_v0.4 是色调、饱和度、亮度增强。吸烟检测里光照变化是主要干扰hsv_v 可以适当调高到 0.5让模型对暗光更鲁棒。但 hsv_h 不要调太高烟头颜色是重要特征色调变化太大会让模型学不到颜色信息。degrees0.0 默认不旋转。如果摄像头是固定角度保持 0 就行。如果摄像头可能倾斜可以设到 10 左右。flipud0.0、fliplr0.5 是上下翻转和左右翻转。左右翻转一般可以开上下翻转要谨慎——吸烟场景里上下翻转会产生不自然的姿态可能引入噪声。这些参数在训练命令里直接加yolo detect train datadataset/data.yaml modelyolov8n.pt epochs100 imgsz640 batch16 mosaic0.5 hsv_v0.5 fliplr0.54. 88.3% 识别率怎么复现评估、调参与掉点排查4.1 识别率到底看哪个指标「平均识别率 88.3%」这个说法比较模糊可能是 mAP0.5也可能是准确率或召回率。在目标检测里我一般看三个指标mAP0.5IoU 阈值 0.5 时的平均精度反映整体检测质量。88.3% 如果是这个指标说明模型在「框得准」这件事上已经不错了。mAP0.5:0.95IoU 从 0.5 到 0.95 取平均更严格。这个指标通常会比 mAP0.5 低 10~20 个点。如果 88.3% 是这个那模型相当强。Precision 和 Recall精确率和召回率。吸烟检测里召回率往往比精确率更重要——漏检一个吸烟行为可能比误检代价更大。所以调参时我会优先保召回。训练完成后用下面命令在验证集上评估yolo detect val modelruns/detect/train/weights/best.pt datadataset/data.yaml输出里会列出每个类别的 P、R、mAP0.5、mAP0.5:0.95。如果「烟」这个类别的召回率明显低于其他类别说明小目标检测是瓶颈需要针对性优化。4.2 掉点的四个常见原因和排查顺序第一个原因标注框不贴紧。烟头目标小如果标注时框比实际烟头大一圈模型学到的特征就模糊了。排查方法抽 20 张验证集图片把预测框和标注框画在一起对比看是不是普遍偏大。第二个原因类别不平衡。如果「烟」的样本数远少于「人脸」模型会偏向预测人脸。排查方法统计每个类别的标注框数量如果比例超过 1:5就需要做重采样或调整损失权重。第三个原因验证集场景和训练集差异大。比如训练集全是白天验证集混了夜晚图掉点是必然的。排查方法按场景分组评估看哪个场景的 mAP 最低。第四个原因输入分辨率不够。烟头在 640 分辨率下可能只有 5~6 个像素特征提取困难。排查方法把 imgsz 提到 960 或 1280 再训一版看 mAP 是否提升。如果提升明显说明分辨率是瓶颈。排查顺序建议从标注质量开始再到数据分布最后到模型和分辨率。因为标注问题是最容易修、收益最直接的。4.3 提升召回率的三个具体手段如果评估发现召回率偏低可以试这三个手段一是降低置信度阈值。yolov8 默认预测时 conf0.25调低到 0.15 可以让更多候选框进入 NMS提升召回。但代价是误检可能增加需要根据业务容忍度权衡。二是调整 NMS 的 IoU 阈值。默认 iou0.7如果烟头密集可以降到 0.5减少框被合并的情况。三是加一个专门的小目标检测层。yolov8 的 P3 层负责小目标如果烟头特别小可以在 P2 层加一个检测头。但这会增大计算量991 张图这个量级不一定划算建议先试前两个手段。5. 吸烟检测落地的避坑与常见问题排查5.1 标注时把烟和手框在一起模型学不会现象训练 loss 正常下降但验证集上「烟」的 mAP 始终上不去预测框总是偏大。原因标注时为了省事把烟和手合并成一个框或者烟头框得太大把手指也包进去了。模型学到的是「手附近有东西」而不是「烟头本身」。解决重新标注烟头框贴紧烟头边缘哪怕只有 8×10 像素也要单独标。手如果有必要作为独立类别就单独标手不要和烟合并。991 张图重标一遍工作量不小但这一步省不得。5.2 验证集 mAP 很高实际部署掉到 60%现象训练时验证集 mAP0.5 有 88.3%部署到实际摄像头后识别率明显下降。原因验证集和实际场景分布不一致。常见的是验证集图片来自和训练集相同的视频片段帧间差异小相当于变相泄漏。实际部署时换了摄像头角度、换了光照、换了人模型就崩了。解决划分验证集时按视频片段或时间段划分不要按帧随机划分。如果数据来自多个摄像头验证集要包含至少一个训练时没见过的摄像头。另外部署前用实际场景录一段视频抽帧做一次独立测试这个结果才是真实水平。5.3 训练时显存溢出batch 调小后 mAP 反而降了现象batch16 时显存不够改成 batch4 后能跑但 mAP 比之前低了 3~5 个点。原因batch 减小后梯度估计的方差变大训练不稳定。同时如果学习率没跟着调相当于每步更新幅度相对变大容易跳过最优解。解决batch 减小时学习率按比例调小。比如 batch 从 16 降到 4学习率从 0.01 降到 0.0025 左右。另外可以开启梯度累积用 accumulate4 模拟大 batch 的效果。yolov8 支持 accumulate 参数在训练命令里加上即可。5.4 烟头小目标漏检严重调置信度也没用现象画面里明显有人在吸烟但模型就是不给框或者框的位置偏到脸上。原因烟头在输入分辨率下像素太少特征在 backbone 下采样过程中丢失了。640 分辨率下烟头可能只有 5×6 像素经过 32 倍下采样后只剩不到 1 个像素特征基本消失。解决提高输入分辨率到 960 或 1280让烟头在下采样后仍有足够特征。如果显存不够可以用切片推理SAHI把大图切成小块分别检测再合并。另外检查标注时烟头框是否准确如果标注框本身就偏了模型学到的位置也是错的。5.5 数据增强开了 mosaic 后小目标反而更难学现象开启 mosaic 后训练 loss 下降变慢验证集小目标 mAP 下降。原因mosaic 把 4 张图拼成 1 张每张图被缩小到 1/4 面积烟头目标变得更小特征进一步丢失。对于本身就是小目标的吸烟检测mosaic 的副作用可能大于收益。解决把 mosaic 关掉或降到 0.3 以下改用其他增强方式比如随机缩放、随机裁剪。如果一定要用 mosaic可以配合更高的输入分辨率抵消目标缩小的影响。6. 从 991 张到可部署模型验证集独立测试与推理加速技巧训练完拿到 best.pt 只是第一步真正要落地还得过两关独立测试和推理加速。独立测试的意思是不要只用训练时划分的验证集评估而是另外找一批完全没参与训练和验证的图片最好来自不同摄像头、不同时间段。我一般会留出 50~100 张这样的「盲测集」训练全程不碰它最后只跑一次。如果盲测集的 mAP 和验证集差距在 5 个点以内说明模型泛化还行如果差距超过 10 个点就得回头检查数据划分是不是有泄漏。推理加速方面991 张图训出来的 yolov8n 模型本身不大但在边缘设备上跑还是可以做几件事。一是导出 ONNX 或 TensorRT 格式用 FP16 或 INT8 量化速度能提升 2~3 倍。导出命令yolo export modelruns/detect/train/weights/best.pt formatonnx halfTruehalfTrue 表示 FP16 量化精度损失通常很小。如果部署到 RK3588 这类 NPU 设备需要转成 RKNN 格式流程是先导出 ONNX再用 RKNN Toolkit 转换。转换时注意输入尺寸要和训练时一致否则精度会掉。另一个技巧是调整推理时的置信度和 NMS 参数。部署时不要直接用默认值而是根据实际场景调。比如加油站场景对误检容忍度低可以把 conf 提到 0.4工地场景对漏检容忍度低可以降到 0.15。这些参数在推理脚本里传from ultralytics import YOLO model YOLO(best.pt) results model.predict( sourcetest.jpg, conf0.2, # 置信度阈值按场景调 iou0.5, # NMS IoU 阈值 imgsz960, # 推理分辨率和训练一致或略高 device0 # GPU 编号CPU 用 cpu )最后说一个我自己的习惯每次训完模型不管指标多好看我都会拿手机对着自己拍一段模拟吸烟的视频抽帧跑一遍。这个「土办法」帮我发现过好几次验证集没暴露的问题比如侧脸角度漏检、暗光下烟头消失。991 张图能到 88.3% 不容易但真正决定它能不能用的是那 11.7% 的失败案例出现在什么场景。希望帮到你。本文还有配套的精品资源点击获取