
简介本资源为面向YOLO目标检测学习者的烟雾识别数据集适用于安防监控、工业消防、森林防火等场景下的烟雾检测模型训练与算法验证适合具备一定深度学习基础、正在做目标检测课程设计或科研实验的开发者使用。压缩包共收录2000个文件全部为XML格式的标注文件对应21578张烟雾图像整体约268.9MB标注文件可直接配合YOLO系列框架完成数据加载与训练流程。目前已有482人学习下载具备一定的参考与复用价值。读者可借助该数据集快速搭建烟雾检测训练环境用于模型微调、精度对比与消融实验省去自行采集与标注图像的时间成本同时便于验证数据增强、锚框调整等策略对烟雾小目标检测效果的影响为后续工程落地提供可靠的数据支撑。1. 烟雾检测数据集拿到手21578 张带标签图像到底怎么用工业现场做烟雾检测最头疼的从来不是模型结构而是数据。你手里如果正好有一份 21578 张图像、带标签、按 smoke100 类别组织的烟雾数据集那已经比大多数人起步快了。但快不等于顺——我见过太多人把 zip 解压完直接train一跑结果 mAP 卡在 0.3 上不去回头怪 YOLO 不行。问题往往出在标签格式、类别映射和划分策略上而不是网络本身。这份数据集的核心价值在于“带标签”三个字。烟雾检测和普通目标检测不一样烟没有固定形状边缘弥散早期烟雾和云雾、水汽、光照反射极易混淆。21578 张的量级配合 YOLO 系列做迁移学习是能落地的。它适合谁适合做工业烟雾预警、火灾早期监控、边缘部署烟感视觉方案的工程师也适合想用真实场景数据跑通 YOLO 训练全流程的人。接下来我不讲空理论直接按“数据检查 → 格式转换 → 训练配置 → 排错 → 进阶技巧”这条线走一遍。2. 烟雾数据集的结构检查与 YOLO 格式转换2.1 先别急着训练解压后必须确认的三件事拿到smoke100-uwe4t.zip这类压缩包第一步不是写训练脚本而是把目录结构摸清楚。常见做法是先看顶层目录再抽样看标签文件。烟雾数据集通常有两种组织方式一种是 VOC 风格Annotations放 XMLJPEGImages放图一种是 YOLO 风格images和labels平行标签是.txt。你得先确认是哪一种因为后面的转换脚本完全不同。我一般会跑下面这段检查脚本把图像数量、标签数量、类别名、标签格式一次性看清楚import os import glob from collections import Counter root ./smoke100-uwe4t # 解压后的根目录按实际改 # 1. 统计图像文件 img_exts (*.jpg, *.jpeg, *.png, *.bmp) imgs [] for ext in img_exts: imgs.extend(glob.glob(os.path.join(root, **, ext), recursiveTrue)) print(图像总数:, len(imgs)) # 2. 统计标签文件 txts glob.glob(os.path.join(root, **, *.txt), recursiveTrue) xmls glob.glob(os.path.join(root, **, *.xml), recursiveTrue) print(txt 标签数:, len(txts), xml 标签数:, len(xmls)) # 3. 抽样看一个 txt 标签内容YOLO 格式应为: class x_center y_center w h if txts: with open(txts[0], r) as f: lines f.read().strip().splitlines() print(样例标签文件:, txts[0]) print(前 3 行:, lines[:3]) # 4. 统计类别分布仅 YOLO txt cls_counter Counter() for t in txts: with open(t, r) as f: for line in f: line line.strip() if line: cls_counter[line.split()[0]] 1 print(类别分布:, cls_counter.most_common(10))这段脚本的逻辑很直白先确认图像和标签是否一一对应再看标签是 YOLO 的归一化格式还是 VOC 的绝对坐标格式最后统计类别分布。参数上root指向解压目录recursiveTrue是为了兼容多层子目录。如果txt标签数远小于图像数说明有大量图片没有标注训练前必须处理否则 YOLO 会把无标签图当背景拉低召回。提示如果发现标签是 XML不要手动转用现成脚本批量处理下面就给。2.2 VOC 转 YOLO转换脚本与四个边界坑如果数据集是 VOC 格式需要把 XML 里的xmin, ymin, xmax, ymax转成 YOLO 的class x_center y_center w h并且全部归一化到 0~1。转换本身不难坑在边界处理。我常用的转换脚本如下import os import glob import xml.etree.ElementTree as ET # 类别映射根据你的数据集实际类别改smoke100 通常只有 smoke 一类 classes [smoke] cls_to_id {c: i for i, c in enumerate(classes)} xml_dir ./smoke100-uwe4t/Annotations img_dir ./smoke100-uwe4t/JPEGImages out_dir ./smoke100-uwe4t/labels os.makedirs(out_dir, exist_okTrue) def convert(xml_path, img_w, img_h): 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 cls_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) # 边界裁剪防止越界 xmin max(0, min(xmin, img_w - 1)) xmax max(0, min(xmax, img_w - 1)) ymin max(0, min(ymin, img_h - 1)) ymax max(0, min(ymax, img_h - 1)) w xmax - xmin h ymax - ymin if w 1 or h 1: # 过滤无效框 continue x_center (xmin xmax) / 2.0 / img_w y_center (ymin ymax) / 2.0 / img_h nw w / img_w nh h / img_h lines.append(f{cls_to_id[name]} {x_center:.6f} {y_center:.6f} {nw:.6f} {nh:.6f}) return lines # 需要先读取图像尺寸这里用 PIL from PIL import Image for xml_path in glob.glob(os.path.join(xml_dir, *.xml)): base os.path.splitext(os.path.basename(xml_path))[0] img_path None for ext in (.jpg, .jpeg, .png, .bmp): p os.path.join(img_dir, base ext) if os.path.exists(p): img_path p break if img_path is None: continue with Image.open(img_path) as im: img_w, img_h im.size lines convert(xml_path, img_w, img_h) with open(os.path.join(out_dir, base .txt), w) as f: f.write(\n.join(lines))逻辑说明先建立类别到 id 的映射遍历 XML 中每个 object取出框坐标做边界裁剪再归一化。参数上classes必须和你的数据集类别一致烟雾数据集常见只有smoke一类但有些会分smoke和fire要按实际改。四个边界坑分别是坐标越界、宽高为负、宽高过小、类别名不匹配。前三个用max/min和阈值过滤解决第四个靠打印未匹配类别来排查。2.3 训练集/验证集/测试集划分别用随机划分烟雾检测有个特点同一场景的连续帧高度相似。如果你用纯随机划分训练集和验证集里会出现几乎一样的图验证指标虚高上线就翻车。我一般按场景或按视频片段划分如果没有场景信息至少按文件名前缀分组。常见做法是 8:1:1但分组划分比随机划分更可信。import os import glob import random from sklearn.model_selection import GroupShuffleSplit all_imgs glob.glob(./smoke100-uwe4t/images/*.jpg) # 用文件名前缀作为分组依据按实际数据调整 groups [os.path.basename(p).split(_)[0] for p in all_imgs] gss GroupShuffleSplit(n_splits1, test_size0.2, random_state42) train_idx, val_idx next(gss.split(all_imgs, groupsgroups)) train_imgs [all_imgs[i] for i in train_idx] val_imgs [all_imgs[i] for i in val_idx] print(训练集:, len(train_imgs), 验证集:, len(val_imgs))这里用GroupShuffleSplit保证同一组的图不会同时出现在训练和验证里。参数test_size0.2表示验证集占 20%random_state固定后结果可复现。如果数据没有明显分组至少也要固定随机种子别每次跑都不一样。3. YOLO 训练烟雾检测模型的参数配置与显存控制3.1 选 YOLOv8 还是 YOLOv5烟雾场景下的取舍烟雾检测选版本不是越新越好。YOLOv8 的 anchor-free 头对小目标更友好烟雾早期往往是小区域所以 v8 通常比 v5 略好。但 v5 的生态和部署资料更成熟如果你要上 RK3588 或树莓派这类边缘设备v5 的量化工具链更顺。我的建议是训练阶段用 YOLOv8 拿精度部署阶段如果边缘工具链只支持 v5再转。别一上来就纠结先跑通一个。数据配置文件smoke.yaml长这样path: ./smoke100-uwe4t train: images/train val: images/val test: images/test names: 0: smokepath是数据集根目录train/val/test是相对路径names是类别映射。注意names的 id 必须和标签里的 class id 一致否则训练时类别全错。3.2 关键训练参数batch、imgsz、学习率怎么定烟雾数据集 21578 张不算小。用单卡 V100 或 3090batch16或32都能跑。imgsz我一般设 640如果烟雾目标很小可以上 1280但显存翻倍。学习率用默认的0.01配合余弦退火通常够用如果 loss 震荡降到0.001。yolo detect train \ datasmoke.yaml \ modelyolov8s.pt \ epochs100 \ imgsz640 \ batch16 \ lr00.01 \ lrf0.01 \ patience20 \ device0 \ projectsmoke_runs \ nameexp1参数说明modelyolov8s.pt是预训练权重烟雾数据量够用 s 或 m 都行epochs100配合patience20早停lr0是初始学习率lrf是最终学习率比例device0指定第一块 GPU。如果显存不够把batch降到 8或者用yolov8n.pt。注意训练中 BN 崩溃是常见问题表现是 loss 突然变 NaN。原因通常是 batch 太小或学习率太大。解决方法是增大 batch 或降低 lr0也可以在配置里加warmup_epochs3。3.3 训练过程监控看哪些曲线什么时候该停训练启动后重点看三个东西box_loss、cls_loss、mAP50。烟雾检测里cls_loss通常降得比box_loss快因为类别少。如果box_loss不降说明框回归有问题检查标签归一化是否正确。如果mAP50在 0.5 附近卡住可能是数据里负样本太多或者烟雾和背景太像。我一般会在runs/detect/smoke_runs/exp1下看results.csv用 pandas 画一下import pandas as pd import matplotlib.pyplot as plt df pd.read_csv(runs/detect/smoke_runs/exp1/results.csv) df.columns df.columns.str.strip() plt.plot(df[epoch], df[metrics/mAP50(B)], labelmAP50) plt.plot(df[epoch], df[train/box_loss], labelbox_loss) plt.legend() plt.show()如果mAP50连续 20 轮不升早停会触发。别硬跑 300 轮浪费时间。4. 烟雾检测训练避坑从标签到部署的 5 个翻车现场4.1 现象mAP 虚高但实际漏检严重原因验证集和训练集同场景模型记住了背景而不是烟雾。解决按场景分组划分或者用不同时间段的数据做验证。4.2 现象训练 loss 正常推理时框全偏原因标签归一化时用了错误的图像尺寸比如 XML 里尺寸和实际图不一致。解决转换时用 PIL 重新读图尺寸别信 XML 里的size。4.3 现象BN 崩溃loss 变 NaN原因batch 太小或学习率太大。解决batch 至少 8lr0 降到 0.001加 warmup。4.4 现象边缘部署误检率高原因训练时 imgsz 640部署时输入 320尺度不一致。解决导出模型时固定 imgsz或者用相同尺寸做量化校准。4.5 现象混淆矩阵总和不唯一原因多类别时某些类被漏统计或者标签里有非法 class id。解决检查names和标签 id 是否一一对应过滤非法标签。5. 烟雾检测进阶用测试时增强和切片推理榨干小目标训练完模型别急着收工。烟雾早期目标小常规推理容易漏。我一般会加两个技巧测试时增强TTA和切片推理SAHI。TTA 是在推理时对图像做翻转、缩放综合多个结果能提 1~3 个点 mAP。切片推理是把大图切成小块分别检测再合并对小目标特别有效。from ultralytics import YOLO model YOLO(runs/detect/smoke_runs/exp1/weights/best.pt) # TTA 推理 results model.predict( source./test_images, imgsz640, augmentTrue, # 开启 TTA conf0.25, iou0.45, saveTrue )augmentTrue就是 TTAconf和iou按验证集调。切片推理可以用 SAHI 库但要注意切片重叠率太低会漏太高会重复框。我一般设overlap0.2。验证方法上别只看 mAP要拿实际视频跑一遍看漏检和误检的帧。我习惯把误检的图单独存下来回头补进训练集迭代两三轮效果比换模型明显。最后说个血泪经验烟雾检测的后悔药就是数据模型再改不如多标 500 张难例。希望帮到你。本文还有配套的精品资源点击获取