
简介本资源为面向目标检测学习者的YOLO烟雾检测数据集适用于安防监控、工业安全等场景下的烟雾识别模型训练兼顾入门与进阶需求。压缩包共2000个文件约86.54MB包含1000张真实场景高质量图片以及vocxml、cocojson和yolotxt三种格式标签分别存放于不同文件夹可直接用于YOLO系列模型训练。此外还提供数据集划分脚本、YOLO环境搭建与训练教程支持按需划分训练集、验证集和测试集并附有yaml配置文件与说明文档方便快速上手。目前已有362人学习下载适合希望快速构建烟雾检测模型、减少数据准备成本的研究者与开发者参考使用。1. 烟雾检测数据集怎么选1000 张真实场景图 三格式标签能省掉多少标注成本做烟雾目标检测的兄弟大概率都经历过这个阶段算法框架跑通了模型结构也改了好几版结果卡在数据上。自己拿 labelImg 标 1000 张图一张图平均 3 到 5 个烟雾区域标完再导出、再转格式、再划分训练验证集一周时间就没了而且标到后面手抖框的质量直线下降。这份 YOLO 烟雾目标检测数据集就是冲着这个痛点来的——1000 张真实场景图片labelImg 标注同时给了 voc(xml)、coco(json)、yolo(txt) 三种格式标签分文件夹存放还附了划分脚本和 Linux/Windows 两套环境搭建与训练教程。它解决的不是模型怎么设计而是数据从哪来、格式怎么对、训练集怎么切这三件最耗人但又最不能出错的事。适合刚入门 YOLO 想跑通第一个烟雾检测 demo 的人也适合手里有改进思路、需要一份干净基线数据做对比实验的熟手。下面我按数据长什么样 → 怎么接进训练流程 → 哪里容易翻车的顺序拆一遍。2. 三种标签格式的对应关系voc、coco、yolo 到底差在哪拿到一个数据集第一件事不是急着train.py而是先搞清楚三种标签格式各自描述了什么、坐标系怎么算。很多人训练报错Label class xxx is not in the class list或者框全跑到左上角根子都在这里。2.1 voc xml绝对坐标 像素边界voc 格式一张图对应一个 xml核心是bndbox里的xmin/ymin/xmax/ymax单位是像素原点在图片左上角。它的好处是可读性强labelImg 默认就存这个格式出问题肉眼能查。annotation foldersmoke/folder filename0001.jpg/filename size width640/width height480/height depth3/depth /size object namesmoke/name bndbox xmin112/xmin ymin88/ymin xmax430/xmax ymax356/ymax /bndbox /object /annotation这里name必须和你的data.yaml里names列表严格一致大小写、空格都算。width/height是原图尺寸转换脚本靠它做归一化如果这个值和真实图片对不上后面 yolo 格式的框就会整体偏移。2.2 coco json一张 json 管全库 类别 id 从 1 开始coco 把所有图片的标注塞进一个 json结构是images/annotations/categories三个数组。关键坑在于 coco 的category_id通常从 1 开始而 yolo 的类别索引从 0 开始中间差 1。如果你直接把 coco 的 id 喂给 yolo烟雾会被当成背景或者错类。{ images: [{id: 1, file_name: 0001.jpg, width: 640, height: 480}], annotations: [ {id: 1, image_id: 1, category_id: 1, bbox: [112, 88, 318, 268], area: 85224, iscrowd: 0} ], categories: [{id: 1, name: smoke}] }注意 coco 的bbox是[x, y, w, h]不是[xmin, ymin, xmax, ymax]这是转换时最容易写错的一处。w xmax - xminh ymax - ymin别偷懒直接拿 xmax 当 w。2.3 yolo txt归一化中心点 宽高yolo 格式每张图一个 txt每行class cx cy w h全部是相对图片宽高的归一化值范围 0 到 1。0 0.4234 0.4625 0.4969 0.5583换算关系cx (xmin xmax) / 2 / widthcy (ymin ymax) / 2 / heightw (xmax - xmin) / widthh (ymax - ymin) / height。只要 voc 的 width/height 是对的这个换算就不会错。三种格式的差异我整理成一张表转换前对着看一遍能省很多调试时间。维度voc xmlcoco jsonyolo txt存储方式一图一文件全库一文件一图一文件坐标类型绝对像素绝对像素归一化 0~1框表示xmin,ymin,xmax,ymaxx,y,w,hcx,cy,w,h类别起始name 字符串id 从 1 起索引从 0 起典型用途标注原始存档评测/COCO APIYOLO 直接训练3. 划分脚本怎么用train/val/test 与 ImageSets 两条路线数据格式理清了下一步是划分。这份资源里给了三个脚本训练集、验证集、测试集划分脚本图片标签划分写入新文件夹.py、训练集、验证集划分脚本图片标签划分写入新文件夹.py、split_train_val生成ImageSets下txt文件划分脚本.py。它们解决的是两种不同习惯的划分需求选哪个取决于你的训练代码读哪种索引。3.1 物理划分把图片和标签复制进新文件夹第一种脚本的思路是复制文件按比例把图片和对应标签拷到train/val/test三个目录每个目录下再分images和labels。这种结构最直观YOLOv5/v8 的data.yaml直接指目录就行。import os, shutil, random # 原始数据目录图片和标签同名不同后缀 img_dir smoke/images lbl_dir smoke/labels out_root smoke_split train_ratio, val_ratio 0.8, 0.1 # 剩下 0.1 给 test random.seed(42) # 固定种子保证每次划分一致方便复现 names [os.path.splitext(f)[0] for f in os.listdir(img_dir) if f.endswith(.jpg)] random.shuffle(names) n len(names) n_train int(n * train_ratio) n_val int(n * val_ratio) splits { train: names[:n_train], val: names[n_train:n_train n_val], test: names[n_train n_val:] } for split, items in splits.items(): for sub in (images, labels): os.makedirs(os.path.join(out_root, split, sub), exist_okTrue) for name in items: shutil.copy(os.path.join(img_dir, name .jpg), os.path.join(out_root, split, images, name .jpg)) shutil.copy(os.path.join(lbl_dir, name .txt), os.path.join(out_root, split, labels, name .txt)) print(split, len(items))逻辑说明先固定random.seed这是复现实验的后悔药不固定的话每次划分结果不同模型指标波动你根本分不清是改动有效还是数据换了。train_ratio和val_ratio按需改烟雾检测这种单类别任务1000 张图建议 8:1:1验证集少于 100 张时指标抖动会很明显。脚本用shutil.copy而不是移动原始数据保留一份划错了还能重来。3.2 索引划分生成 ImageSets 下的 txt 清单第二种脚本不复制文件只在ImageSets/Main下生成train.txt、val.txt、test.txt每行一个文件名不带后缀。这种是 voc 风格适合数据量大、不想占双份磁盘的场景训练代码读 txt 列表再去找图。import os, random img_dir smoke/images out_dir smoke/ImageSets/Main os.makedirs(out_dir, exist_okTrue) names [os.path.splitext(f)[0] for f in os.listdir(img_dir) if f.endswith(.jpg)] random.seed(42) random.shuffle(names) n len(names) n_train int(n * 0.8) n_val int(n * 0.1) with open(os.path.join(out_dir, train.txt), w) as f: f.write(\n.join(names[:n_train])) with open(os.path.join(out_dir, val.txt), w) as f: f.write(\n.join(names[n_train:n_train n_val])) with open(os.path.join(out_dir, test.txt), w) as f: f.write(\n.join(names[n_train n_val:]))逻辑说明ImageSets/Main是 voc 的标准目录约定很多老训练框架默认去这里找清单。写文件时用\n.join 而不是逐行 write少一次循环。注意这里存的是不带后缀的文件名如果你的训练代码需要带.jpg在读取时自己拼别在 txt 里混着写否则匹配标签时会找不到对应文件。提示两个脚本都用了random.seed(42)同一份数据用同一个种子划分结果才可复现。换种子等于换了一次数据分布对比实验时别乱动。3.3 划分后必须做的三项校验划完不是就完事了我一般强制走一遍校验否则训练到一半才发现问题更浪费时间。第一数量对账。train val test的总数必须等于原始图片数少一张都说明有文件没匹配上标签。第二标签非空检查。烟雾图里偶尔有漏标的对应 txt 是空的空标签文件会让某些版本的 YOLO 直接报错提前筛出来。第三路径连通性。随机抽 5 个 train 里的名字确认images/xxx.jpg和labels/xxx.txt都真实存在防止复制过程中断导致半截数据。# 数量对账 wc -l smoke_split/train/images/*.jpg | tail -1 # 找出空标签文件 find smoke_split -name *.txt -emptyfind -empty这条命令能一把揪出所有空标签比写脚本快。发现空的先别删打开图看看是真没目标还是漏标漏标的补一下真没目标的移出数据集。4. 环境搭建与训练Linux 和 Windows 两条线的关键差异资源里给了 Linux 和 Windows 两套环境搭建与训练教程还有 Ubuntu 安装教程。我不重复教程内容只讲两条线里最容易卡住的地方这些是教程里往往一笔带过、但实际能卡你半天的东西。4.1 Linux 线CUDA 版本与 PyTorch 的对应Linux 下装 YOLO 环境90% 的翻车在 CUDA 和 PyTorch 版本对不上。常见做法是先nvidia-smi看驱动支持的最高 CUDA 版本再去 PyTorch 官网找对应 wheel。nvidia-smi # 右上角 CUDA Version 是驱动上限 conda create -n smoke python3.9 -y conda activate smoke # 假设驱动支持 CUDA 11.8 pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118 python -c import torch; print(torch.cuda.is_available())逻辑说明nvidia-smi显示的 CUDA Version 是驱动能支持的最高运行时版本你装的 PyTorch 自带 CUDA runtime只要不超过这个上限就行。最后那行torch.cuda.is_available()必须返回True返回False说明版本没对上别急着往下训先解决这个。--index-url指定 cu118 源装错源会拉到 CPU 版本训练时慢到怀疑人生。4.2 Windows 线路径分隔符与中文目录Windows 下最大的坑是路径。YOLO 的data.yaml里如果写了反斜杠\或者数据集放在带中文的目录下轻则找不到文件重则 OpenCV 读图返回 None。# data.yaml 正确写法 path: D:/datasets/smoke_split train: train/images val: val/images test: test/images nc: 1 names: [smoke]逻辑说明path用正斜杠/Windows 下 Python 也能识别比反斜杠省心。train/val/test写相对path的子路径别写绝对路径换机器时只改path一行。nc是类别数烟雾单类别就是 1names列表长度必须等于nc多一个少一个都会在训练启动时报错。数据集目录名、图片名全部用英文数字中文路径在部分版本的 dataloader 里会静默失败图读进来是空的训练 loss 不降你还找不到原因。4.3 从案例改到自己数据集的三个改动点教程里的案例是跑通流程用的换成自己的烟雾数据只需要动三处data.yaml的路径和类别、模型配置里的nc、以及预训练权重是否加载。# 以 YOLOv8 为例从预训练权重起步 yolo detect train datadata.yaml modelyolov8n.pt epochs100 imgsz640 batch16逻辑说明modelyolov8n.pt表示加载 COCO 预训练权重烟雾检测数据量不大时从预训练起步比从头训收敛快得多。imgsz640是输入分辨率和标注时的图片尺寸不必一致YOLO 会自己 resize但标注框的归一化是基于原图的所以原图尺寸记录必须准。batch16按显存调显存不够就降到 8 或 4别硬撑OOM 报错会中断训练。epochs100是起点看验证集 mAP 曲线早停了就加过拟合了就减。5. 避坑与排查烟雾数据集训练中最容易翻车的五件事这一章是我自己踩过和帮人排查过的真实问题按现象 → 原因 → 解决写对着查能省不少时间。现象一训练 loss 一直是 nan 或者不下降。原因通常是标签里有非法值比如归一化后坐标超过 1或者出现负数。voc 转 yolo 时如果 xmax 小于 xmin算出来 w 是负的喂进去就炸。解决写个脚本扫一遍所有 txt检查每行后四个数是否都在 0 到 1 之间且 w、h 大于 0把异常行打印出来对应图片人工复核。现象二模型把背景当烟雾误检特别多。原因多半是负样本无烟雾图太少或者标注时把大片天空、白墙也框进去了。烟雾本身和云、雾、蒸汽视觉上接近标注边界不清会教坏模型。解决补充一批纯背景图作为负样本标注时严格只框真实烟雾区域边界模糊的宁可不标。现象三验证集 mAP 很高实际推理一塌糊涂。原因是划分时没有随机打散或者同一场景的连续帧被分到了训练和验证两边造成数据泄漏。解决划分前先按场景分组同一视频、同一地点的图要么全在训练集要么全在验证集别让模型见过验证集的近邻。现象四训练报错找不到标签文件。原因是图片和标签文件名没对齐比如图片是0001.jpg标签却是0001.JPG.txt或者放在错误的子目录。解决用脚本批量核对图片名去后缀后必须能在 labels 目录找到同名 txt找不到的列出来统一改名。现象五换机器后训练结果对不上。原因是随机种子没固定或者 CUDA 版本不同导致算子有细微差异。解决代码里固定random、numpy、torch三处种子记录环境版本对比实验尽量在同一台机器上跑。注意烟雾检测的难点不在模型在数据。标注质量、负样本比例、场景划分这三件事做扎实比换任何 backbone 都管用。6. 进阶技巧用划分脚本做交叉验证与数据版本管理跑通单次训练只是开始真正做实验时你需要的是可复现、可对比的数据版本。这份资源里的划分脚本稍加改造就能支撑更严谨的流程。第一个技巧是 K 折交叉验证。把random.shuffle换成sklearn.model_selection.KFold生成 K 组不同的 train/val 划分每组训一次取平均指标。1000 张图做 5 折每折验证集 200 张比单次 8:1:1 的评估稳定得多尤其适合数据量不大、指标抖动明显的场景。from sklearn.model_selection import KFold import os names [os.path.splitext(f)[0] for f in os.listdir(smoke/images) if f.endswith(.jpg)] kf KFold(n_splits5, shuffleTrue, random_state42) for fold, (tr, va) in enumerate(kf.split(names)): with open(fImageSets/fold{fold}_train.txt, w) as f: f.write(\n.join(names[i] for i in tr)) with open(fImageSets/fold{fold}_val.txt, w) as f: f.write(\n.join(names[i] for i in va))逻辑说明KFold保证每张图在验证集里恰好出现一次random_state固定后折的划分可复现。n_splits5是常用值数据再少可以降到 3。每折单独存清单训练时换data.yaml里的 txt 路径即可不用重新复制图片。第二个技巧是数据版本管理。每次调整标注补标、改框、加负样本后别覆盖旧划分用日期或版本号建新目录比如ImageSets/v1_20240101。模型指标提升时你能回溯到底是哪次数据改动带来的而不是面对一堆train_final_final.txt发懵。第三个技巧是划分前先做类别分布统计。烟雾检测虽然单类别但可以按烟雾面积占比分桶统计大中小目标的比例确保训练集和验证集的分布一致。如果验证集全是大面积烟雾训练集全是小面积指标会虚高实际部署时小烟雾全漏检。技巧适用场景关键参数K 折交叉验证数据量小、指标抖动n_splits5, random_state42数据版本目录频繁调整标注日期或版本号命名面积分布统计目标尺度差异大按框面积分桶对比我自己的习惯是每次动数据之前先跑一遍分布统计划分之后强制走一遍第 3 章那三项校验训练启动前确认torch.cuda.is_available()和data.yaml路径。这套流程走下来因为数据问题浪费的训练时间能砍掉一大半。从那以后我每次换数据集都强制走一遍校验脚本再也没出现过训到一半发现标签错位的情况。希望帮到你。本文还有配套的精品资源点击获取