森林火灾烟雾检测数据集:VOC/COCO/YOLO标签转换与YOLO训练全流程
简介本资源面向目标检测初学者与需要森林火灾烟雾识别方案的开发者提供一套可直接用于YOLO系列训练的真实场景数据集。数据包含1000张高质量图片场景丰富经labelimg精细标注并同步提供voc(xml)、coco(json)与yolo(txt)三种格式标签分别存放于不同文件夹便于直接接入不同框架。压缩包共2000个文件以xml与txt标签为主另含html教程、py划分脚本与yaml配置文件整体约71.92MB。资源附赠YOLO环境搭建、训练案例教程及数据集划分脚本可按需生成训练集、验证集与测试集帮助读者快速跑通从数据准备到模型训练的全流程。目前已有925人学习下载适合希望节省标注成本、快速验证烟雾检测效果的中级学习者参考使用。1. 拿到一个森林火灾烟雾数据集先别急着训练森林火灾烟雾检测这件事真正卡住大多数人的从来不是模型结构而是数据。你手上如果有一个「1000 张图片 voc/coco/yolo 三种格式标签 划分脚本 训练教程」的压缩包那它解决的正是最费时间的那一环把原始标注整理成 YOLO 能直接吃的目录结构。烟雾检测和常规目标检测不太一样烟是半透明、边界模糊、形态随风向不断变化的目标早期火点往往只有一小团灰白区域和晨雾、云层、水汽高度相似所以数据集的质量和标签格式的规范性直接决定了后面模型是能收敛还是全程玄学震荡。这个方案适合三类人一是想快速跑通 YOLO 烟雾检测 baseline 的学生和初学者二是需要把公开数据整理成自有训练流水线的工程师三是做林火预警、电力巡检、园区监控这类落地项目、需要先验证数据可行性的从业者。1000 张的规模不算大但足够跑通全流程、验证标签转换和划分脚本是否正确也能作为后续扩充数据的模板。下面我按「先看懂数据长什么样 → 再动手转换和划分 → 然后配环境训练 → 最后排坑和调优」的顺序讲清楚每一步都给到能直接抄的命令和参数。2. 三种标签格式到底差在哪voc、coco、yolo 的字段对照2.1 为什么同一个数据集要同时给三种格式很多人第一次看到 voc、coco、yolo 三套标签会懵不是一份标注就够了吗原因是这三种格式服务的工具链完全不同。VOC 的 XML 是标注工具LabelImg 等的原生输出人可读、方便核对COCO 的 JSON 是学术界和检测/分割通用评测的事实标准pycocotools 只认它YOLO 的 txt 是训练时 DataLoader 直接读取的格式一行一个目标最省解析开销。数据集同时提供三份本质是让你不用自己写转换脚本直接对接不同框架。理解字段映射是后面排错的根基。VOC 用绝对像素坐标xmin, ymin, xmax, ymaxCOCO 用[x, y, width, height]的绝对像素框加category_idYOLO 用归一化后的x_center, y_center, width, height取值 0~1且类别从 0 开始编号。三者之间转换最容易翻车的地方就是坐标系和归一化下面这张表建议存下来。格式文件形态框表示坐标基准类别编号VOC每图一个 .xmlxmin,ymin,xmax,ymax绝对像素名称字符串COCO单个 .jsonx,y,w,h绝对像素从 1 开始YOLO每图一个 .txtcx,cy,w,h归一化 0~1从 0 开始2.2 用一段脚本核对三种格式是否自洽拿到数据集第一件事不是训练是验证三套标签描述的是同一批框。我一般写个小脚本随机抽若干张图把三种格式解析出来对比误差超过 1 像素就说明转换有问题。import os, json, random import xml.etree.ElementTree as ET def read_voc(xml_path): tree ET.parse(xml_path) root tree.getroot() size root.find(size) w, h int(size.find(width).text), int(size.find(height).text) boxes [] for obj in root.iter(object): b obj.find(bndbox) boxes.append(( float(b.find(xmin).text), float(b.find(ymin).text), float(b.find(xmax).text), float(b.find(ymax).text))) return w, h, boxes def read_yolo(txt_path, w, h): boxes [] with open(txt_path) as f: for line in f: c, cx, cy, bw, bh line.split() cx, cy, bw, bh map(float, (cx, cy, bw, bh)) # 反归一化回绝对像素 boxes.append(( (cx - bw / 2) * w, (cy - bh / 2) * h, (cx bw / 2) * w, (cy bh / 2) * h)) return boxes # 随机抽 5 张做交叉验证 img_dir images names random.sample(os.listdir(img_dir), 5) for n in names: stem os.path.splitext(n)[0] w, h, voc_boxes read_voc(fAnnotations/{stem}.xml) yolo_boxes read_yolo(flabels/{stem}.txt, w, h) for vb, yb in zip(voc_boxes, yolo_boxes): diff max(abs(a - b) for a, b in zip(vb, yb)) print(stem, max diff , round(diff, 2))这段脚本的关键在read_yolo里的反归一化(cx - bw/2) * w把中心点加宽高还原成左上角再乘图像宽高回到像素。参数上注意w, h必须来自 VOC 的size不能想当然用固定值因为数据集里图片尺寸可能不统一。如果打印出的max diff普遍大于 1说明某套标签的坐标系被改过先别训练回去查转换环节。2.3 类别映射烟雾检测里最隐蔽的坑烟雾检测数据集常见类别是smoke和fire两类但 VOC 里写的是字符串COCO 里是数字 idYOLO 里是 0/1。如果转换时把fire和smoke的顺序搞反模型照样能训练、loss 照样下降但推理时把火判成烟这种错误在指标上看不出来只有可视化才暴露。我的习惯是固定一份classes.txt顺序写死所有转换脚本都读它绝不手写类别号。3. 划分脚本怎么用从原始目录到 YOLO 训练目录3.1 划分前先确认目录结构划分脚本通常假设你已经把图片和标签放在固定位置。跑之前先tree一下确认结构符合脚本预期否则脚本会静默跳过找不到标签的图片最后训练集少一半你还不知道。# 典型的数据集原始结构 dataset/ ├── images/ # 1000 张 jpg/png ├── Annotations/ # VOC xml ├── labels/ # YOLO txt ├── annotations.json # COCO └── split.py # 划分脚本3.2 运行划分脚本并理解输出划分脚本一般做三件事按比例切分 train/val/test、把图片和标签成对复制到对应目录、生成data.yaml。常见比例是 8:1:1 或 7:2:11000 张的话验证集 100~200 张比较合理太少指标抖动大。python split.py \ --images ./images \ --labels ./labels \ --out ./dataset_split \ --train 0.8 --val 0.1 --test 0.1 \ --seed 42参数说明--seed一定要固定否则每次划分结果不同实验无法复现--train/--val/--test三者之和必须为 1脚本一般会校验--out输出目录里会生成images/train、labels/train这样的镜像结构。跑完后务必数一下文件数for d in train val test; do echo $d images: $(ls dataset_split/images/$d | wc -l) echo $d labels: $(ls dataset_split/labels/$d | wc -l) done图片数和标签数必须一一对应。如果 labels 比 images 少说明有图片没有对应标注训练时那批图会被当成负样本直接污染结果。3.3 data.yaml 里三个必须改对的字段划分脚本生成的data.yaml是 YOLO 训练的入口配置三个字段最容易错path: /abs/path/to/dataset_split # 必须是绝对路径 train: images/train val: images/val nc: 2 names: [smoke, fire]path用相对路径时YOLO 会相对它自己的运行目录解析经常出现「找不到图片」却报的是标签错误。nc必须等于names长度且names顺序要和标签里的类别号严格对应。我见过有人nc: 1但标签里有类别 1训练直接崩在越界索引上。4. 环境配置与训练从 Anaconda 到第一个 baseline4.1 Anaconda 环境配置要求与依赖安装YOLO 训练对 CUDA 和 PyTorch 版本匹配很敏感用 conda 隔离环境是标配。下面是我常用的配置流程Python 3.9~3.10 兼容性最好。conda create -n fire_yolo python3.10 -y conda activate fire_yolo # 按显卡 CUDA 版本装对应 torch这里以 cu118 为例 pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118 pip install ultralytics opencv-python pyyaml tqdm装完先验证 GPU 是否可用这一步别省import torch print(cuda available:, torch.cuda.is_available()) print(device:, torch.cuda.get_device_name(0) if torch.cuda.is_available() else cpu)如果cuda available是 False后面训练会默默跑在 CPU 上1000 张图也能跑但一个 epoch 可能要十几分钟你会以为模型不收敛其实是没吃到 GPU。4.2 用预训练模型跑通第一个 baseline不要从零训练烟雾数据只有 1000 张从零训几乎不可能收敛。用 COCO 预训练权重做迁移学习是标准做法。yolo detect train \ datadataset_split/data.yaml \ modelyolov8n.pt \ epochs100 \ imgsz640 \ batch16 \ lr00.01 \ patience20 \ projectruns/fire \ namebaseline参数逐个说modelyolov8n.pt是最小的 nano 版本1000 张图足够显存占用低imgsz640是默认输入尺寸烟雾目标偏大640 通常够用batch16在 8G 显存上比较稳爆显存就降到 8lr00.01是初始学习率迁移学习可以再小一点到 0.005patience20表示 20 个 epoch 指标不涨就早停省时间。训练日志里重点看mAP50和mAP50-95烟雾这种模糊目标 mAP50 能到 0.6 以上就算 baseline 可用。4.3 训练中该盯哪些指标box_loss和cls_loss持续下降是基本盘但真正判断模型好坏要看验证集的混淆矩阵。烟雾检测最怕的是把云、雾判成 smoke假阳以及小火点漏检假阴。混淆矩阵里如果smoke和背景的误判特别高说明数据里负样本无烟的森林图太少需要补背景图。另外yolo混淆矩阵总合不唯一这个现象通常是验证集里同一张图有多个目标、或者类别映射错位导致的先回去核对data.yaml的names。5. 避坑与排查烟雾检测训练里最常见的 5 个翻车现场5.1 现象训练 loss 正常下降但验证 mAP 一直是 0原因data.yaml里val路径指向的目录没有标签或者标签文件名和图片名不一致比如图片是IMG_001.jpg标签是IMG_001.txt但放在错误子目录。YOLO 找不到标签时不会报错直接把该图当负样本。解决跑一遍文件名配对检查图片 stem 和标签 stem 必须完全一致且在同一相对路径下。comm -3 \ (ls dataset_split/images/val | sed s/\..*// | sort) \ (ls dataset_split/labels/val | sed s/\..*// | sort)输出为空才说明配对完整。5.2 现象训练中途 BN 层崩溃loss 变 NaN原因batch太小比如 2 或 4时BatchNorm 统计量估计不准加上学习率偏大几个 batch 后梯度爆炸。热词里说的yolo训练中bn崩溃多半是这个。解决把batch提到 8 以上或者改用yolov8n.pt这种带预训练权重的模型同时把lr0降到 0.001加warmup_epochs3让前几个 epoch 学习率缓慢上升。5.3 现象推理时把晨雾、白云全框成 smoke原因训练集里负样本不足模型没学会区分烟和自然水汽。1000 张图如果全是含烟图模型会倾向于「看到灰白区域就报烟」。解决往训练集里加 10%~20% 的无烟森林、云雾、天空背景图标签留空空 txt 文件。YOLO 支持空标签作为负样本能显著压低假阳。5.4 现象调整置信度门限后指标忽高忽低原因conf门限和iou门限NMS 用的是两个独立参数很多人只调conf忘了iou。烟雾目标重叠多iou设太高会保留大量重复框。解决验证时固定iou0.5先扫conf从 0.1 到 0.5 每 0.05 测一次画 P-R 曲线选最优点。命令里加conf0.25 iou0.5显式指定别用默认值拍脑袋。5.5 现象换一台机器训练结果完全不一样原因没固定随机种子或者两台机器 CUDA/cuDNN 版本不同导致算子实现有差异。深度学习本身有随机性但差异大到 mAP 掉 0.1 就不正常。解决训练命令加seed42 deterministicTrue并记录torch.__version__和 CUDA 版本。跨机器复现时环境版本对齐比调参更重要。6. 把 1000 张用出 10000 张的效果烟雾检测的进阶技巧数据量小是这类数据集的天花板但烟雾检测有几个特性可以利用。第一是烟雾形态不固定水平翻转、随机缩放、色彩抖动这类增强对烟雾几乎无损甚至有益因为真实场景里烟本来就没有固定朝向。在 YOLO 配置里把hsv_h0.015 hsv_s0.7 hsv_v0.4 fliplr0.5打开比默认增强更能提升泛化。第二是烟雾区域大可以适当提高imgsz到 768 或 896让小目标火点也能被检测到代价是显存和训练时间上升8G 卡上batch要相应降到 8。第三点是我踩坑最多的地方不要迷信 mAP。烟雾检测的落地价值在于「早发现」也就是在火还小的时候报警。而 mAP 是在所有目标上平均的大火点容易检、贡献大小火点漏检对 mAP 影响被稀释。我现在的习惯是单独统计小目标框面积小于图像 1%的召回率把它当成核心指标。做法是在验证后跑一段脚本按框面积分桶算 recallfrom ultralytics import YOLO import numpy as np model YOLO(runs/fire/baseline/weights/best.pt) results model.val(datadataset_split/data.yaml, conf0.25, iou0.5) # 按目标面积分桶统计召回小目标单独看 areas results.box.pred[:, 2] * results.box.pred[:, 3] # 预测框面积 small areas 0.01 * (640 * 640) print(小目标占比:, small.mean())如果小目标召回明显低于整体说明模型在「早发现」这件事上不可靠需要针对性补充小火点样本或者用切片推理把大图切成小块分别检测来提升小目标分辨率。切片推理对森林这种大场景监控特别有效代价是推理耗时成倍增加实时性要求高的场景要权衡。最后说个习惯每次改完数据或参数我都会把runs/下的results.csv和混淆矩阵图存一份带日期的副本命名里写清改了什么。烟雾检测调参很容易陷入「感觉这次好一点」的错觉只有把每次实验的指标和配置留档回头看才知道哪一步真正有用。这套数据集和流程的价值不在于 1000 张本身而在于它给了你一个能反复迭代、可复现的起点把标签转换、划分、训练、排错这条链路走通一遍后面换成 5000 张、10000 张你只是换个目录而已。希望帮到你。本文还有配套的精品资源点击获取