
简介本资源为面向目标检测学习者的YOLO烟雾检测数据集适用于安防监控、工业安全等场景下的烟雾识别模型训练兼顾入门与进阶需求。压缩包共2000个文件约86.54MB包含1000张真实场景高质量图片以及vocxml、cocojson和yolotxt三种格式标签分别存放于不同文件夹可直接用于YOLO系列模型训练。此外还提供数据集划分脚本、YOLO环境搭建与训练教程方便按需划分训练集、验证集和测试集。目前已有362人学习下载。资源覆盖从数据准备到模型训练的完整流程标注框质量高场景丰富能帮助读者快速搭建烟雾检测实验环境减少数据整理与格式转换的重复工作适合课程设计、科研实验及工程验证使用。1. 烟雾目标检测为什么值得从一套 1000 张的数据集开始工业现场、仓储园区、老旧楼宇里烟雾往往比明火更早出现但摄像头拍到的烟雾边界模糊、半透明、形态随风飘散用传统阈值或颜色分割几乎必翻车。这也是为什么越来越多团队转向 YOLO 烟雾目标检测它把「哪里像烟」交给卷积网络去学而不是靠人去写规则。你手上如果只有算法没有数据模型再新也训不出东西反过来一套标注规范、格式齐全、划分合理的 1000 张烟雾数据集配合 YOLO 预训练模型微调往往就能跑出一个能上边缘盒子做初筛的基线。这套标题里的资源核心价值不在「1000 张」这个数字而在于它同时给了 VOC、COCO、YOLO 三种格式标签和划分脚本。做过检测的人都知道公开烟雾数据要么只有图片没标注要么只有单一格式拿到手第一件事就是写转换脚本、重新划分、对齐类别名光这些杂活就能耗掉一两天。它把这段脏活前置了让你能把时间花在训练调参和部署验证上。适合谁适合刚接触 YOLO 目标检测、想找一个完整闭环练手的工程师也适合手里有业务场景、需要快速验证「烟雾检测这条路走不走得通」的团队。下面我按「先看懂数据 → 再跑通训练 → 再避开坑」的顺序把整套流程拆开讲。2. 拆开这套数据集三种标签格式到底怎么选、怎么对齐2.1 VOC、COCO、YOLO 三种格式的差异与适用场景同一批图片三种标签格式描述的是同一件事但组织方式完全不同。VOC 用 XML一张图一个文件框用xmin/ymin/xmax/ymax绝对坐标COCO 用一个大的 JSON所有图片和标注集中管理框是[x, y, width, height]绝对坐标加category_idYOLO 用 txt一张图一个文件每行class_id cx cy w h全部是相对图片宽高的归一化值。选哪个不取决于哪个「高级」而取决于你下游用什么框架和工具链。格式存储方式坐标类型典型用途VOC每图一个 XML绝对像素老牌检测框架、标注工具导入导出COCO单个 JSON绝对像素评估指标、pycocotools、多任务YOLO每图一个 txt归一化相对值Ultralytics 系训练直接读取我一般会以 YOLO 格式作为训练主格式因为 Ultralytics 的 YOLO 系列直接吃这个VOC 留着做标注复核和跨工具迁移COCO 留着跑 mAP 评估和跟别的模型对比。三种格式并存的最大好处是你换框架时不用重新标注只换读取层。2.2 用脚本把 VOC 转成 YOLO坐标归一化与类别映射拿到 VOC 的 XML 后转 YOLO 的核心就两件事把绝对坐标除以图片宽高变成相对值把类别名映射成从 0 开始的整数 id。下面这段脚本我用了很多次逻辑清晰边界也处理了。import os import xml.etree.ElementTree as ET from PIL import Image # 类别名到 id 的映射顺序一旦定下就不要改训练和推理必须一致 CLASSES [smoke] CLASS_TO_ID {name: i for i, name in enumerate(CLASSES)} def voc_to_yolo(xml_dir, img_dir, out_dir): os.makedirs(out_dir, exist_okTrue) for xml_file in os.listdir(xml_dir): if not xml_file.endswith(.xml): continue tree ET.parse(os.path.join(xml_dir, xml_file)) root tree.getroot() # 图片文件名在 XML 的 filename 节点里用它去定位原图拿宽高 img_name root.find(filename).text img_path os.path.join(img_dir, img_name) with Image.open(img_path) as im: w, h im.size lines [] for obj in root.iter(object): cls_name obj.find(name).text.strip() if cls_name not in CLASS_TO_ID: continue # 类别不在白名单里直接跳过避免脏标注污染训练 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) # 归一化并转成中心点加宽高 cx (xmin xmax) / 2.0 / w cy (ymin ymax) / 2.0 / h bw (xmax - xmin) / w bh (ymax - ymin) / h # 裁剪到 [0,1]防止标注越界导致训练报错 cx, cy min(max(cx, 0), 1), min(max(cy, 0), 1) bw, bh min(max(bw, 0), 1), min(max(bh, 0), 1) lines.append(f{CLASS_TO_ID[cls_name]} {cx:.6f} {cy:.6f} {bw:.6f} {bh:.6f}) out_file os.path.join(out_dir, os.path.splitext(xml_file)[0] .txt) with open(out_file, w) as f: f.write(\n.join(lines)) voc_to_yolo(Annotations, JPEGImages, labels)逻辑说明先读 XML 拿框再用对应图片的真实宽高做归一化这一步必须用原图尺寸不能用固定值否则不同分辨率图片的框会整体偏移。参数说明CLASSES是类别白名单烟雾检测通常就一个类如果你还区分「白烟/黑烟」就加进去但 id 顺序定下后训练、推理、评估三处必须完全一致。:.6f保留六位小数是 YOLO 生态的常见精度够用且不会让文件过大。跑完检查一下 labels 目录里 txt 数量和图片数量是否一一对应少一个都说明有图没标注或文件名对不上。2.3 划分脚本怎么写才不会有数据泄漏划分训练集、验证集、测试集看着简单坑却最多。最常见的错误是按图片随机分但同一段视频抽出来的连续帧被分到训练和验证两边验证集精度虚高上线就露馅。正确做法是先按场景或视频源分组再在组级别划分。import os import random import shutil def split_dataset(img_dir, label_dir, out_dir, ratios(0.8, 0.1, 0.1), seed42): random.seed(seed) # 固定种子保证每次划分可复现 imgs [f for f in os.listdir(img_dir) if f.lower().endswith((.jpg, .png))] # 按文件名前缀分组同一段视频的帧前缀相同避免泄漏 groups {} for f in imgs: key f.split(_)[0] groups.setdefault(key, []).append(f) keys list(groups.keys()) random.shuffle(keys) n len(keys) n_train int(n * ratios[0]) n_val int(n * ratios[1]) splits { train: keys[:n_train], val: keys[n_train:n_train n_val], test: keys[n_train n_val:], } for split, gkeys in splits.items(): for sub in (images, labels): os.makedirs(os.path.join(out_dir, split, sub), exist_okTrue) for k in gkeys: for f in groups[k]: shutil.copy(os.path.join(img_dir, f), os.path.join(out_dir, split, images, f)) txt os.path.splitext(f)[0] .txt src os.path.join(label_dir, txt) if os.path.exists(src): shutil.copy(src, os.path.join(out_dir, split, labels, txt)) split_dataset(images, labels, dataset)逻辑说明seed42保证结果可复现团队协作时大家划分一致才能对比实验。按文件名前缀分组是关键如果你的图片命名没有规律就退而求其次按拍摄时间段或来源目录分组。参数说明ratios是训练/验证/测试比例1000 张规模下 8:1:1 比较稳验证集太小会让早停判断抖动。划分完务必统计三个子集的图片数和标注框数如果某一类在验证集里一个框都没有说明划分偏了要重新调种子或分层抽样。3. 用 YOLO 把 1000 张烟雾数据训到能用的最小流程3.1 环境配置与预训练权重选择环境这块我一般用 Python 3.10 加 PyTorch显卡从 RTX 3060 到 V100 都跑过1000 张图用 yolov8n 或 yolov8s 这种小模型就够别一上来就上大模型数据量撑不住反而过拟合。安装直接走 pippip install ultralytics # 验证环境和 GPU 是否可用 yolo checksyolo checks会打印 PyTorch 版本、CUDA 是否可用、显卡型号这一步能提前发现驱动和 CUDA 不匹配的问题比训练到一半报错强。预训练权重用官方发布的 yolov8n.pt 或 yolov8s.pt加载预训练权重做微调比从零训收敛快得多1000 张图通常 100 到 150 轮就能看到稳定结果。注意权重版本和 ultralytics 版本要匹配版本错配有时会报 key 不匹配遇到就换对应版本的权重。3.2 data.yaml 的字段含义与常见写错点YOLO 训练靠一个 data.yaml 描述数据位置和类别字段少但每个都关键。path: /data/smoke_dataset # 数据集根目录绝对路径最稳 train: train/images # 相对 path 的训练图片目录 val: val/images test: test/images nc: 1 # 类别数烟雾检测通常为 1 names: [smoke] # 类别名顺序必须和标签里的 id 对应逻辑说明path用绝对路径能避免工作目录变化导致的找不到文件。train/val/test写的是图片目录YOLO 会自动去找同级的 labels 目录所以目录结构必须是train/images和train/labels并列。参数说明nc和names长度必须一致names的顺序就是标签里 class_id 的顺序写反了模型会把烟学成背景。最常见的写错点是把train写成图片文件列表而不是目录或者 labels 目录名拼错训练一开始就报找不到标签。3.3 启动训练与关键超参怎么设命令行启动训练最直接yolo detect train \ datadata.yaml \ modelyolov8n.pt \ epochs150 \ imgsz640 \ batch16 \ lr00.01 \ patience30 \ projectruns/smoke \ nameexp1逻辑说明modelyolov8n.pt加载预训练权重做微调。imgsz640是速度和精度的平衡点烟雾目标通常占画面比例不小640 够用如果小目标烟雾多可以提到 960 但显存和耗时都会涨。参数说明batch16在 8G 显存上比较稳显存不够就降到 8 并配合accumulate做梯度累积。lr00.01是初始学习率微调场景可以降到 0.001 更稳。patience30表示验证指标 30 轮不提升就早停省时间也防过拟合。训练过程中重点看mAP50和mAP50-95两条曲线如果训练 loss 一直降但验证 mAP 早早走平甚至下降就是过拟合信号该加数据增强或减模型容量了。3.4 训练完怎么验证模型真的学到了烟训练结束别只看最后的 mAP 数字要拿测试集实际跑一遍推理把预测框画出来肉眼过一遍。用下面的脚本批量推理并保存可视化结果from ultralytics import YOLO import os model YOLO(runs/smoke/exp1/weights/best.pt) test_dir dataset/test/images for img in os.listdir(test_dir): results model(os.path.join(test_dir, img), conf0.25) # saveTrue 会把带框的图存到 runs/detect 下方便逐张核对 results[0].save(filenamefvis/{img})逻辑说明conf0.25是置信度阈值低于它的框不显示调高会漏检、调低会误检先用 0.25 看整体表现。参数说明best.pt是验证集上表现最好的权重别用last.pt最后一轮往往已经过拟合。看可视化时重点盯三类漏检的烟尤其淡烟、远距离烟、把云雾或蒸汽误检成烟、框位置偏移。这些现象直接告诉你下一步是补数据还是调增强。混淆矩阵也值得看如果背景被大量误判为烟说明负样本不够得往训练集里加不含烟的相似场景图。4. 烟雾检测训练里最容易翻车的几个地方4.1 现象训练 loss 正常但 mAP 一直上不去原因多半是标签和图片没对齐或者类别 id 映射错了。YOLO 对错位标签不会报错只会默默学歪。解决随机抽 10 张图用可视化脚本把标签框画到原图上肉眼确认框的位置和类别对不对。再检查 data.yaml 的 names 顺序和标签里的 id 是否一致。4.2 现象验证集 mAP 很高换一批现场图就崩原因数据泄漏或场景单一。同一段视频的帧被分到了训练和验证两边验证集等于在考训练集见过的画面。解决回到划分脚本按视频源或拍摄场景分组再划分确保验证集里的场景训练时没见过。同时补充不同光照、不同距离的烟雾图。4.3 现象训练中途 loss 突然变成 nan原因学习率太大、标注框越界、或者某张图尺寸异常。解决先把lr0降到 0.001 重跑再用脚本扫一遍所有标签检查有没有 cx/cy/w/h 超出 [0,1] 的框有就裁剪或剔除最后确认没有 0 字节的损坏图片。4.4 现象模型把白云、蒸汽、雾气全当成烟原因负样本不足模型没学会区分「烟」和「像烟但不是烟」的东西。解决往训练集里加入不含烟但含云、雾、蒸汽的负样本图标签文件留空即可。负样本比例一般控制在正样本的 10% 到 20%太多会拉低召回。4.5 现象推理速度远低于预期原因输入尺寸设太大或者用了大模型。解决烟雾检测对实时性要求高时把imgsz降到 416 或 320换 yolov8n再导出成 ONNX 或 TensorRT 推理速度能提升明显。注意导出后要重新验证精度量化有时会掉点。5. 把 1000 张数据用出复利增强策略与迭代习惯1000 张图不算多但用对了能顶几千张。我的习惯是先把基线跑通拿到一个能看的 mAP再针对性做增强。烟雾检测最有效的增强是 Mosaic 和随机透视Mosaic 把四张图拼一张能显著提升小目标和遮挡场景的表现随机透视模拟不同拍摄角度。但要注意Mosaic 在训练最后几十轮最好关掉让模型在真实分布上收尾否则框的分布和真实场景有偏差。颜色抖动HSV对烟雾也有用因为现场光照差异大但抖动幅度别太大否则淡烟会被抖没。迭代上我一般按「看错例 → 补数据 → 重训 → 对比」循环。每次训练完把误检和漏检的图单独挑出来看是缺这类场景还是标注有问题。如果是缺场景就想办法补几十张同类图如果是标注漏标就修标签重训。别一次改太多变量否则不知道是哪个改动起了作用。评估时除了 mAP还要看不同置信度下的召回和误报工业场景往往宁可多报也不能漏报阈值可以适当调低。最后说个我踩过的坑早期我图省事把验证集和测试集混着调参结果模型在测试集上表现虚高上线后误报一堆。后来我强制自己测试集只在最终验收时用一次调参全程只看验证集。这个习惯看着笨但能让你对模型的真实水平心里有数。数据集的格式转换、划分、训练这条链路跑通一次之后就是可复用的资产换个场景换批数据就能再来一轮。希望帮到你。本文还有配套的精品资源点击获取