
简介这份标注好的香烟数据集面向计算机视觉初学者与目标检测开发者用于YOLO系列模型的训练与验证可服务于公共场所禁烟监控、烟草制品识别等场景。压缩包共9742个文件约483.55MB其中4879个jpg为原始图像4861个xml为对应的标注文件记录每张图中香烟实例的边界框坐标与类别信息另有2个png辅助文件目录结构清晰便于直接接入训练流程。目前已有975人学习下载具备一定参考热度。数据集覆盖不同角度、光照与背景能帮助读者省去自行采集与标注的成本快速搭建训练环境并借助标注文件完成数据预处理、增强与模型评估进而对比mAP、漏检率等指标理解YOLO分块检测与边界框回归的实际效果适合作为课程设计、毕业项目或算法入门的实战素材。1. 标注好的香烟数据集从“找不到数据”到“跑通第一个检测模型”做香烟目标检测的人十有八九卡在第一步数据。网上公开的香烟数据集要么类别定义混乱要么标注格式对不上 YOLO要么图片数量少得可怜训出来的模型在真实场景里基本是“睁眼瞎”。标注好的香烟数据集本质上是把“图像采集 类别定义 边界框标注 格式转换”这四件事提前做完让你直接进入训练环节。它解决的不是算法问题而是工程起点问题——没有干净的数据再好的 YOLO 版本也白搭。这篇文章面向两类人一是刚接触目标检测、想拿一个真实类别练手的新手二是已经在做吸烟行为识别、但被数据质量拖住进度的从业者。我会把数据集的类别设计、格式校验、训练配置、评估口径和踩坑记录一次讲透让你拿到数据后能直接复现而不是对着一个压缩包发呆。2. 香烟数据集到底长什么样类别定义、标注规范与格式选择2.1 为什么“香烟”这个类别比你想的更难标香烟在图像里是个典型的小目标而且形态高度不固定。点着的烟有烟雾、有火光没点的烟可能只露出一截白色圆柱夹在手指间时还会被遮挡。更麻烦的是香烟和笔、吸管、白色耳机线在低分辨率下极易混淆。所以一个可用的香烟数据集首先要在类别定义上做取舍。常见做法是把类别拆成两类cigarette香烟本体和smoke烟雾区域。但我不建议新手一上来就做烟雾检测因为烟雾边界模糊标注一致性极差两个人标同一张图IoU 可能连 0.5 都不到。更稳的方案是只标香烟本体把“是否吸烟”这个判断交给后续逻辑或分类模型。如果业务必须区分“手持香烟”和“嘴叼香烟”那就再加一个cigarette_in_mouth类但类别数一旦超过 3小数据量下模型很容易崩。标注规范上边界框要贴紧香烟可见部分不要为了“包含手指”而扩大框。原因很简单YOLO 的回归损失对框的尺度很敏感框大了模型学到的是“手烟”的混合特征换一个不拿烟的手就会误检。遮挡超过 70% 的香烟建议直接标ignore或跳过不要硬标否则噪声会拉低整体精度。2.2 YOLO 格式的目录结构与标签文件长什么样标注好的香烟数据集通常以 YOLO 格式交付目录结构如下cigarette_dataset/ ├── images/ │ ├── train/ │ │ ├── 000001.jpg │ │ └── ... │ ├── val/ │ └── test/ ├── labels/ │ ├── train/ │ │ ├── 000001.txt │ │ └── ... │ ├── val/ │ └── test/ └── data.yaml每个.txt文件对应一张图每行一个目标格式是class_id x_center y_center width height这四个坐标全部是归一化到 0~1 的相对值不是像素值。很多新手第一次拿到数据集直接拿像素坐标去训loss 不降反升排查半天才发现是格式没转。下面这段脚本用来校验标签文件是否合法我一般会在训练前跑一遍import os import numpy as np def validate_yolo_labels(label_dir, img_dir, num_classes2): 校验 YOLO 标签文件坐标范围、类别 id、空文件、图片标签配对 label_dir: 标签文件夹路径 img_dir: 对应图片文件夹路径 num_classes: 类别数香烟数据集常见为 1 或 2 issues [] label_files [f for f in os.listdir(label_dir) if f.endswith(.txt)] for lf in label_files: path os.path.join(label_dir, lf) # 检查图片是否存在同名文件 img_name lf.replace(.txt, .jpg) if not os.path.exists(os.path.join(img_dir, img_name)): issues.append(f图片缺失: {img_name}) continue with open(path, r) as f: lines f.readlines() if len(lines) 0: issues.append(f空标签文件: {lf}) continue for i, line in enumerate(lines): parts line.strip().split() if len(parts) ! 5: issues.append(f{lf} 第{i1}行字段数错误: {len(parts)}) continue cls_id int(parts[0]) coords [float(x) for x in parts[1:]] if cls_id 0 or cls_id num_classes: issues.append(f{lf} 第{i1}行类别越界: {cls_id}) if any(c 0 or c 1 for c in coords): issues.append(f{lf} 第{i1}行坐标越界: {coords}) if coords[2] 0 or coords[3] 0: issues.append(f{lf} 第{i1}行宽高非正: {coords[2]}, {coords[3]}) return issues # 用法示例 problems validate_yolo_labels( label_dircigarette_dataset/labels/train, img_dircigarette_dataset/images/train, num_classes2 ) for p in problems[:20]: print(p) print(f共发现 {len(problems)} 个问题)这段脚本的逻辑很直接逐文件检查字段数、类别 id 范围、坐标归一化范围、宽高是否为正同时确认每张标签都有对应图片。参数num_classes要根据你的data.yaml里nc的值来设香烟二分类就填 2只标本体就填 1。跑完如果输出一堆“坐标越界”大概率是标注工具导出时选了像素格式需要重新导出或写转换脚本。2.3 data.yaml 里三个必须写对的字段data.yaml是 YOLO 训练入口写错一个字段训练直接报错或静默学错类别。典型内容如下path: ./cigarette_dataset train: images/train val: images/val test: images/test nc: 2 names: [cigarette, cigarette_in_mouth]path是数据集根目录train/val/test是相对路径。nc必须等于names列表长度且类别顺序要和标签里的class_id严格对应。我见过有人把names写成[smoke, cigarette]但标签里 0 是香烟、1 是烟雾结果模型把烟雾当香烟学mAP 看着还行实际用起来全错。改完data.yaml后建议用一行命令快速验证 YOLO 能否正确加载python -c from ultralytics import YOLO; model YOLO(yolov8n.yaml); model.train(datacigarette_dataset/data.yaml, epochs1, imgsz640, batch4)只跑 1 个 epoch看它能不能顺利读到数据并开始迭代。如果报No labels found优先检查train路径是否指向了images/train而不是labels/train。3. 用 YOLOv8 在本地跑通香烟检测从环境到第一组权重3.1 环境安装与版本选择别在 CUDA 上翻车香烟数据集通常图片量在几千张级别用 YOLOv8n 或 YOLOv8s 就够。环境上我一般用 Python 3.9~3.10PyTorch 选 2.0 以上CUDA 版本跟显卡驱动匹配即可。安装命令conda create -n cigarette_yolo python3.10 -y conda activate cigarette_yolo pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118 pip install ultralytics opencv-python numpy装完先验证 GPU 是否可用import torch print(torch.__version__) print(torch.cuda.is_available()) print(torch.cuda.get_device_name(0) if torch.cuda.is_available() else CPU only)如果cuda.is_available()返回 False不要急着换代码先看驱动版本和 CUDA 运行时是否匹配。血泪经验很多人装完ultralytics直接训结果跑在 CPU 上一个 epoch 要半小时还以为是数据集太大。3.2 训练命令与关键参数imgsz、batch、lr0 怎么定最小可复现训练命令如下yolo detect train \ datacigarette_dataset/data.yaml \ modelyolov8s.pt \ epochs100 \ imgsz640 \ batch16 \ lr00.01 \ lrf0.01 \ patience20 \ projectruns/cigarette \ nameexp1参数逐个说imgsz640香烟是小目标640 是底线。如果显存够可以上 960 或 1280小目标召回会明显提升。但注意imgsz必须能被 32 整除。batch16显存 8G 以下建议 8 或 4不要硬撑否则 OOM 中断后还得重来。lr00.01初始学习率YOLOv8 默认 0.01小数据集可以降到 0.005避免早期震荡。lrf0.01最终学习率系数和余弦调度配合一般不用改。patience2020 个 epoch 验证指标不升就早停省时间。训练开始后重点看runs/cigarette/exp1/results.csv里的metrics/mAP50-95和val/box_loss。如果box_loss一直不降先回去查标签格式如果mAP50高但mAP50-95很低说明框的位置不够准可能是标注框太松。3.3 推理与阈值调整conf 和 iou 怎么配合训完拿best.pt做推理from ultralytics import YOLO model YOLO(runs/cigarette/exp1/weights/best.pt) results model.predict( sourcetest_images/, conf0.25, iou0.45, imgsz640, saveTrue ) for r in results: for box in r.boxes: cls_id int(box.cls[0]) conf float(box.conf[0]) xyxy box.xyxy[0].tolist() print(f类别: {model.names[cls_id]}, 置信度: {conf:.3f}, 框: {xyxy})conf0.25是置信度阈值低于它的框直接丢弃。香烟检测里如果误检多可以提到 0.4如果漏检多降到 0.15 试试。iou0.45是 NMS 的 IoU 阈值两个框重叠超过这个值就保留置信度高的那个。香烟密集场景比如多人同时吸烟可以适当提高到 0.5~0.6避免把相邻的烟合并掉。4. 避坑与排查香烟数据集训练中最容易翻车的 5 个点4.1 现象mAP 一直卡在 0.1 以下loss 几乎不降原因标签类别 id 和data.yaml的names顺序不一致或者标签文件里混入了像素坐标。解决跑一遍 2.2 节的校验脚本重点看“坐标越界”和“类别越界”两项。如果坐标全是大于 1 的数用下面这段转换脚本批量修正import os def pixel_to_yolo(label_dir, img_w, img_h): 把像素坐标的 YOLO 标签转成归一化坐标 for lf in os.listdir(label_dir): if not lf.endswith(.txt): continue path os.path.join(label_dir, lf) new_lines [] with open(path, r) as f: for line in f: parts line.strip().split() if len(parts) ! 5: continue cls_id parts[0] x, y, w, h map(float, parts[1:]) # 像素转归一化 x_n x / img_w y_n y / img_h w_n w / img_w h_n h / img_h new_lines.append(f{cls_id} {x_n:.6f} {y_n:.6f} {w_n:.6f} {h_n:.6f}\n) with open(path, w) as f: f.writelines(new_lines) # 注意img_w 和 img_h 要按每张图实际尺寸传这里假设统一尺寸 pixel_to_yolo(cigarette_dataset/labels/train, 640, 640)4.2 现象验证集 mAP 很高但实际图片里香烟漏检严重原因训练集和验证集来自同一批视频帧画面高度相似模型过拟合了。解决按视频来源划分 train/val而不是随机抽帧。同一段视频的帧不要同时出现在训练和验证里。如果数据集已经混好至少保证验证集里有不同光照、不同背景的样本。4.3 现象模型把白色笔、吸管误检成香烟原因负样本不足。香烟数据集如果全是正样本模型没见过“像烟但不是烟”的物体。解决往训练集里加 10%~20% 的负样本图片不含香烟但含笔、吸管、白色线缆标签文件留空即可。YOLO 对空标签文件是接受的但要在data.yaml同级确认没有开启filter_empty之类的过滤。4.4 现象训练到一半突然 OOMbatch 明明设得很小原因imgsz太大或workers太多导致内存泄漏。解决把workers降到 2 或 0imgsz从 1280 降到 960。另外如果用了cacheTrue小内存机器不要开它会把所有图片缓存到 RAM。4.5 现象推理时框的位置整体偏移原因训练时用了rectTrue矩形训练但推理时图片被 resize 到正方形坐标映射没对齐。解决推理时保持和训练一致的imgsz并且不要手动改letterbox参数。如果必须用矩形推理确保rectTrue在训练和推理两端一致。5. 把香烟检测推到可用小目标增强、评估口径与一个收尾习惯香烟检测真正难的不是训出一个 mAP 0.8 的模型而是让它在真实场景里稳定可用。这里分享三个我反复验证过的技巧。第一小目标增强。YOLOv8 自带mosaic和copy_paste但对香烟这种细长目标scale增强比mosaic更有效。可以在训练配置里把scale调到 0.9让模型多见不同尺度的烟。如果显存允许imgsz960配合scale0.5的收益比imgsz640加一堆增强更明显。第二评估口径要对齐业务。mAP50好看不代表能用香烟检测更该看固定误检率下的召回率。我一般会导出一批负样本图片统计模型在conf0.25时的误检数如果每 100 张负样本误检超过 5 个就要回去补负样本或提高conf。下面这段脚本用来批量统计误检from ultralytics import YOLO import os model YOLO(runs/cigarette/exp1/weights/best.pt) neg_dir negative_samples/ total_fp 0 total_imgs 0 for img_name in os.listdir(neg_dir): if not img_name.lower().endswith((.jpg, .png, .jpeg)): continue total_imgs 1 results model.predict(os.path.join(neg_dir, img_name), conf0.25, verboseFalse) for r in results: total_fp len(r.boxes) print(f负样本总数: {total_imgs}, 误检框总数: {total_fp}, 平均每张误检: {total_fp/max(total_imgs,1):.3f})第三导出 ONNX 做部署前验证。训练框架里的 mAP 和部署后的表现经常有差距尤其是量化后。导出命令yolo export modelruns/cigarette/exp1/weights/best.pt formatonnx imgsz640 simplifyTrue导出后用onnxruntime跑同一批测试图对比 PyTorch 和 ONNX 的框差异。如果 IoU 低于 0.95检查simplify和opset版本必要时把opset固定到 12。最后一个习惯每次改完数据集或训练参数先跑 5 个 epoch 看 loss 曲线再决定要不要跑满。我早期图省事直接上 300 epoch结果标签有问题白跑两天。现在固定先短跑验证确认数据管道通了再放长线。希望帮到你。本文还有配套的精品资源点击获取