无人机视角森林桩燃烧识别数据集:二分类与YOLOv5实战指南 简介面向无人机森林巡检与火灾监测场景的图像分类数据集专门聚焦森林桩燃烧状态识别包含燃烧、未燃烧两个类别。数据已按训练集与测试集划分并以文件夹形式保存训练集约两万张图片、测试集约八千六百张图片既适合直接接入图像分类网络训练也能用于分类流程的数据准备计算机视觉入门者和研究团队都可快速上手。资源共两千个文件主体为样本图像另含类别字典文件和可视化脚本压缩包整体约八百零八兆。目录结构清晰按训练集与测试集组织运行可视化脚本即可快速浏览样本分布便于核对标注与构建基准实验。已有八十六人学习适合需要现成燃烧识别数据来完成分类模型验证、算法对比或课程设计的开发者。1. 无人机视角森林桩燃烧识别数据集为什么说它是分类任务里最不该跳过的一环做森林防火图像识别的人大概率都经历过这种尴尬网上能找到的公开数据集要么是卫星图、要么是地面监控拍的火点真正贴着无人机俯拍视角、专门针对“树桩燃烧”这个小目标的分类数据集少得可怜。而这个项目给出的正是这样一个大型无人机视角下的森林桩燃烧图像识别数据集——2 分类燃烧、未燃烧训练集 2 万张测试集 8600 张目录按训练/测试划分好附带类别字典 JSON 文件。换句话说你下载下来不需要再花时间整理目录结构直接就能喂给 YOLOv5 的 classify 分支或者任意一个 CNN 分类网络。适合谁用三类人一是做森林火灾预警但卡在数据清洗上的工程师二是拿 YOLOv5 做分类训练想省掉自己爬图、对图、分目录时间的学生三是想快速验证某个分类网络在无人机下视角下效果的算法岗。这份资源把数据准备、标签映射、训练验证的路径都铺好了剩下的就是你自己的网络调参。2. 数据集结构与类别字典训练/测试划分和标签映射的两大前提2.1 目录树与文件规模先看清 2 万张训练图和 8600 张测试图怎么摆下载解压之后数据集根目录下的核心结构是这样的以实际文件为准我这里列出最常见的组织方式dataset/ ├── data/ │ ├── train/ │ │ ├── burning/ │ │ │ ├── resized_frame8368.jpg │ │ │ ├── resized_frame8364.jpg │ │ │ └── ... │ │ └── not_burning/ │ │ ├── resized_frame5418.jpg │ │ └── ... │ └── test/ │ ├── burning/ │ └── not_burning/ ├── classes.json # 类别字典文件 └── show.py # 可视化脚本这个目录结构有一个很关键的优点文件夹名就是类别名。对 YOLOv5 分类训练来说训练脚本正是按照data/train下每个子文件夹的名字来生成类别标签的所以你不需要额外写标签文件文件夹划分本身就是监督信号。从规模上看训练集 2 万张、测试集 8600 张这个比例大约 7:3对于二分类任务来说训练数据量已经足够支撑一个从零训练的轻量网络更不用说做迁移学习了。每个类别在训练集和测试集中各自有独立的文件夹避免了交叉混杂导致的数据泄漏data leakage。这一点容易被忽视——很多人下载到的数据集所有图片堆在一起自己写脚本随机划分如果按文件名排序前 80% 做训练很可能把同一个视频帧序列里的连续帧全部切进训练集测试集失去了独立性。这个数据集直接把划分做好了就省掉了这一步。2.2 类别字典 JSON从标签名到训练代码的最小闭环数据集里附带了一个类别字典 JSON 文件内容一般长这样{ 0: burning, 1: not_burning }别小看这个文件。它实际上是你在训练代码里做标签映射的“唯一事实来源”source of truth。我一般会在拿到数据集的第一时间就把这个 JSON 读进脚本生成反向映射import json with open(classes.json, r) as f: classes json.load(f) # 得到 {0: burning, 1: not_burning} # 反向映射得到 {burning: 0, not_burning: 1} id2label {int(k): v for k, v in classes.items()} label2id {v: k for k, v in id2label.items()} # 打印出来确认类别顺序 print(id2label) print(label2id)逻辑说明上面的代码先把 JSON 里的字符串键转成整数键得到id2label再反转得到label2id。这样做的目的是保证训练时网络的输出索引和文件夹名之间的对应关系完全一致。参数说明classes.json的键值结构决定了label2id的映射方向如果你的类别字典文件字段名不同比如键是name把classes.items()换成对应的字段读取即可。为什么强调类别字典两个原因。第一YOLOv5 分类训练时--data参数指向的是数据集根目录脚本会按照os.listdir的顺序扫描训练目录下的子文件夹这个顺序和 JSON 里的顺序在大部分情况下一致但如果你删改过文件夹顺序可能变化导致类别索引错位。第二模型推理阶段你拿到的只是[0, 1]的索引要输出“燃烧”还是“未燃烧”的字符串就得靠这个字典反查。没有它你的模型输出就是一堆没人看得懂的数字。这里有一个值得注意的细节如果这份数据集是用于 YOLOv5 分类那么classes.json更像是给你做代码对接用的辅助文件。因为 YOLOv5 分类模式会根据训练集目录下的文件夹名自动建立类别映射并生成classification_classes.json。但如果你用的是自己写的 PyTorch 分类脚本那就必须显式读入classes.json来固定类别顺序避免每次训练时类别索引随机排列。3. 把那 2 万张图喂进模型YOLOv5 分类与通用 CNN 两条训练路线3.1 YOLOv5 classify 训练一条命令跑通但有个参数容易错YOLOv5 仓库本身自带分类训练支持不需要额外装别的库。在yolov5目录下直接用classify/train.py就能启动训练。假设数据集解压后的路径是../dataset训练命令如下python classify/train.py \ --model yolov5s-cls.pt \ --data ../dataset/data \ --epochs 50 \ --img 224 \ --batch-size 64 \ --name forest_burning_cls逻辑说明--data指向的是包含train和test子目录的父目录而不是指向train本身--model指定预训练模型权重yolov5s-cls.pt是在 ImageNet 上预训练过的分类模型能显著加快收敛。参数说明--img 224会把输入图片缩放到 224×224--batch-size 64根据显存调整如果你的显卡只有 6 GB建议降到 32--epochs 50对 2 万张训练图来说已经足够二分类任务通常在 20 轮左右就开始收敛。这里有一个高频翻车点--data参数传错了层。很多人会把路径直接指到../dataset/data/train结果 YOLOv5 把所有子文件夹的名字burning 和 not_burning当成了两条样本路径来读直接报错。记住YOLOv5 分类模式期望的目录结构是data/train/类别名/*.jpg传参时给到data这一层就对了。训练过程中会输出每个 epoch 的train_loss、val_loss和accuracy我一般盯着val_loss看如果连续 5 个 epoch 没有下降说明学习率设置偏大或者模型容量不够。YOLOv5 默认初始学习率是 0.01配合 cosine 退火50 轮基本能跑出 92% 以上的准确率。训练结束后权重保存在runs/train-cls/forest_burning_cls/weights/best.pt。推理验证命令python classify/predict.py \ --weights runs/train-cls/forest_burning_cls/weights/best.pt \ --source ../dataset/data/test/burning/resized_frame8368.jpg逻辑说明这条命令把测试集里的一张燃烧图片喂给模型输出预测类别和置信度。参数说明--source可以是单张图片路径也可以是整个目录YOLOv5 会自动遍历。3.2 通用 CNN 分类路线TIMM 库与自定义训练循环的两点取舍如果你不想被 YOLOv5 框架绑死或者想换一个更新的骨干网络比如 ConvNeXt、EfficientNetV2TIMM 是更灵活的选择。用 TIMM 加载预训练模型只需要几行代码import timm import torch import torch.nn as nn # 加载预训练模型修改分类头 model timm.create_model(efficientnet_b0, pretrainedTrue, num_classes2) model model.to(cuda) # 定义损失函数和优化器 criterion nn.CrossEntropyLoss() optimizer torch.optim.AdamW(model.parameters(), lr1e-4, weight_decay1e-5)逻辑说明timm.create_model中的num_classes2会替换掉模型原本的 1000 类分类头pretrainedTrue加载 ImageNet 权重作为初始化。参数说明weight_decay设为1e-5防止过拟合学习率从1e-4起步配合CosineAnnealingLR调度器可以在 30 轮内达到不错的效果。数据加载部分直接用 PyTorch 的ImageFolder就能读取我们前面说的目录结构from torchvision import datasets, transforms train_transform transforms.Compose([ transforms.Resize((224, 224)), transforms.RandomHorizontalFlip(p0.5), transforms.RandomRotation(15), transforms.ToTensor(), transforms.Normalize(mean[0.485, 0.456, 0.406], std[0.229, 0.224, 0.225]) ]) test_transform transforms.Compose([ transforms.Resize((224, 224)), transforms.ToTensor(), transforms.Normalize(mean[0.485, 0.456, 0.406], std[0.229, 0.224, 0.225]) ]) train_dataset datasets.ImageFolder(../dataset/data/train, transformtrain_transform) test_dataset datasets.ImageFolder(../dataset/data/test, transformtest_transform)逻辑说明ImageFolder会按照子文件夹名称自动生成类别标签这正是前面提到的目录结构带来的便利。参数说明RandomHorizontalFlip和RandomRotation是数据增强对无人机俯拍图来说水平翻转是合理的因为树桩燃烧的方位不固定Normalize的均值方差用的 ImageNet 统计值因为预训练模型是在 ImageNet 上训练的输入分布需要对齐。这里需要做选择如果追求快速复现用 YOLOv5 的 classify 脚本最省事如果要做网络结构对比实验比如 EfficientNet vs ResNet vs ConvNeXt就用 TIMM。两个方案在当前数据集上都能直接跑通。3.3 评测指标别只看 accuracy还要看混淆矩阵和类别置信度分布二分类任务里整体 accuracy 很可能虚高。假设测试集 8600 张里 80% 是未燃烧你一个把所有图片都判成未燃烧的“傻瓜模型”也能有 80% accuracy。所以我在评估时一般固定看三个东西混淆矩阵、每一类的 precision/recall、以及错误样本的置信度分布。from sklearn.metrics import confusion_matrix, classification_report import numpy as np y_true, y_pred [], [] model.eval() with torch.no_grad(): for images, labels in test_loader: images images.to(cuda) outputs model(images) _, predicted torch.max(outputs, 1) y_true.extend(labels.cpu().tolist()) y_pred.extend(predicted.cpu().tolist()) print(confusion_matrix(y_true, y_pred)) print(classification_report(y_true, y_pred, target_names[burning, not_burning]))逻辑说明confusion_matrix返回一个 2×2 矩阵对角线是正确分类的数量非对角线是错分情况。classification_report输出每一类的 precision、recall 和 F1-score。参数说明torch.max(outputs, 1)取每个样本预测概率最大的类别索引test_loader的 batch size 建议设置 128 以上加快推理。实际使用时我还会额外统计“燃烧被误判为未燃烧”的数量这是森林防火场景中最致命的错误——漏报比误报严重得多。如果发现漏报率高优先考虑调整分类阈值而不是盲目增加训练轮数。4. 森林桩燃烧数据集的分类避坑指南五个来回翻车换来的教训4.1 数据集里混入了大量相似帧测试集准确性被高估现象训练时 loss 下降很快测试 accuracy 一度到了 95%但部署到新视频上准确率掉到 80% 以下。原因无人机视频抽帧得到的连续帧高度相似训练集和测试集如果来自同一段视频的不同帧模型实际上是在“背题”而不是在学习“燃烧”这个概念的泛化特征。相似帧导致的信息重叠让评估结果虚高。解决我拿到数据后先按文件名排序隔 N 帧抽样检查训练集和测试集之间是否有重复或极度相似的图片。如果发现相似帧跨集合存在最粗暴但有效的办法是剔除测试集中与训练集高度相关的帧或者按视频片段划分数据一个视频的帧只能出现在一个集合里。这个数据集的划分如果是按帧序列切割的你自己训练前最好多留个心眼。4.2 图片尺寸不统一训练中途报错 batch 维度不匹配现象训练到一半某个 batch 报Expected 3D (unbatched) but got 4D tensor之类的维度错误。原因数据集中图片经过resized_前缀处理但仍有部分图片的通道数不是 3比如灰度图被错误存放或者 EXIF 旋转信息导致实际读入的宽高和预期不一致。解决在数据加载阶段强制统一格式不要依赖数据集本身。在__getitem__里加上img img.convert(RGB)并把Resize放在ToTensor之前。我的习惯是写一个数据清洗脚本先遍历全部图片统计尺寸和通道数把异常图片单独列出来检查。4.3 类别字典里的顺序和文件夹扫描顺序不一致训练标签错位现象训练完做推理发现模型把not_burning的图片识别成burning而且置信度很高但训练准确率是正常的。原因classes.json里写的是0: burning, 1: not_burning但某些框架会按照os.listdir()的字母序扫描文件夹burning字母序在not_burning前面所以burning0正好一致。但如果文件夹名改成中文或者加了数字前缀字母序就完全变了。解决不要依赖文件夹名和字典的“巧合一致”。训练前写一段代码读取classes.json后和os.listdir得到的类别列表做交叉验证不一致就报错。这一点在 YOLOv5 的 classify 模式里尤其重要因为它自动生成的classification_classes.json不一定和你手头的字典一致。4.4 训练时显存 OOM调整 batch size 后准确率反而下降现象用batch-size 128训练显存溢出改成batch-size 32后同样 50 轮训练准确率低了 3 个百分点。原因batch size 变小梯度估计的噪声变大同时学习率没有相应调整。YOLOv5 的优化器是 SGD它对 batch size 变化很敏感。解决如果显存只能支持小 batch可以保持 batch size 不变启用梯度累积gradient accumulation。在 PyTorch 里就是每 N 个 batch 更新一次权重效果等同大 batch。另外一个办法是降低输入尺寸--img 192对二分类来说 224 缩到 192 精度损失可以接受。4.5 测试集 8600 张评估结果可信但部署到无人机实时视频上速度达不到要求现象离线评估 F1 达到 0.93部署到边缘设备比如 Jeston Nano上推理速度只有 8 FPS无法满足实时巡检。原因模型参数量大、输入分辨率高边缘设备算力不足。分类准确率只是训练阶段的目标部署阶段还要考虑吞吐量。解决先剪枝或换轻量网络比如把 EfficientNet-B0 换成 MobileNetV3 或者 ShuffleNetV2输入尺寸降到 192。然后用 TensorRT 做 FP16 量化推理速度通常能翻 2 倍以上。一个 2 分类问题根本不需要大模型模型小反而更稳。5. 可视化与数据验证show 脚本和人工抽查如何避免黑匣子训练5.1 运行资源自带的 show.py把图片和标签对应起来数据集资源里提供了一个 show 脚本用途是可视化数据集中的图片和标签。直接运行python show.py --data ../dataset/data --num_samples 10逻辑说明这个脚本会从训练集和测试集中各抽样若干张图片展示图片路径和它对应的类别标签。参数说明--num_samples控制抽样数量我一般先抽 10 张看格式再抽 50 张细看类别边界。如果你是第一次接触这份数据跑一下 show 脚本能直观感受到“燃烧”和“未燃烧”两个类别的视觉差异——树桩燃烧通常伴随火焰和烟雾未燃烧则只是普通的枯木桩。为什么强调这一步因为分类任务里标签噪声是最难发现的。你永远不知道上一个制作数据集的人有没有把一张烟雾弥漫但没火苗的图标成“未燃烧”。人工抽查是发现这种问题的最直接方式。5.2 训练完成后用测试集可视化预测结果看错误样本长什么样光看 accuracy 数字是不够的我每次训练完都会把测试集里预测错误的图片单独挑出来保存成一张拼图用肉眼看一遍import matplotlib.pyplot as plt import torchvision.utils as vutils misclassified_images [] for images, labels in test_loader: images images.to(cuda) outputs model(images) _, predicted torch.max(outputs, 1) for i in range(len(labels)): if predicted[i] ! labels[i]: # 把错误样本和真实标签、预测标签一起保存 misclassified_images.append((images[i].cpu(), labels[i].item(), predicted[i].item())) # 取前 16 个错误样本画成网格 grid vutils.make_grid([img for img, _, _ in misclassified_images[:16]], nrow4) plt.imshow(grid.permute(1, 2, 0)) plt.show()逻辑说明这段代码遍历测试集把所有预测错误的图片、真实标签、预测标签收集起来用make_grid拼成 4×4 网格可视化。参数说明nrow4控制每行放几张图permute(1, 2, 0)把 PyTorch 的 C×H×W 通道顺序转成 matplotlib 需要的 H×W×C。如果你发现错误样本集中在某一类特定的光线条件下比如傍晚逆光的树桩就可能需要额外采集这个场景的数据或者在数据增强里加强亮度扰动。从我个人做项目的经验看错误样本可视化是“低成本高回报”的步骤。它能让你看清模型的决策边界在哪里是图像太暗、目标太小、还是类别本身存在歧义——这些信息是任何 loss 曲线都给不了你的。6. 进阶用法把这份数据集用在 YOLOv8 分类和半监督标签校验上6.1 迁移到 YOLOv8 分类的目录结构与命令行参数如果你已经在用 YOLOv8而且不想在 YOLOv5 的 classify 分支上重复折腾迁移成本其实很低。YOLOv8 同样支持图像分类它对数据集目录结构的要求是train/类别名/*.jpg和val/类别名/*.jpg。由于这份资源已经把train和test分好了你只需要把test重命名为valcd dataset/data mv test val然后安装 ultralytics 库运行以下命令yolo classify train \ modelyolov8n-cls.pt \ data../dataset/data \ epochs50 \ imgsz224 \ batch64 \ nameforest_yolov8逻辑说明yolov8n-cls.pt是 YOLOv8 的最小分类模型参数量只有 3.2M在 224×224 输入下推理速度非常快。如果你觉得精度不够换成yolov8s-cls.pt或yolov8m-cls.pt即可。参数说明data参数和 YOLOv5 一样指向包含train和val的父目录batch64对 2 万张训练图来说50 轮大约需要十几分钟取决于显卡。yaml 文件方式也支持。如果你想显式指定类别写一个forest.yamlpath: ../dataset/data train: train val: val names: 0: burning 1: not_burning这样做的好处是当数据集文件夹名需要统一替换时用names字段可以保持类别顺序稳定不会受目录扫描顺序影响。6.2 半监督标签校验用置信度阈值筛选出“可疑样本”最后一个技巧是我做过几个数据集项目后养成的习惯——用模型本身来给数据做“反向体检”。在这个数据集上训练好的模型对每张测试图都会输出一个置信度分布。把置信度介于 0.4 到 0.6 之间的样本标出来这些就是模型认为“拿不准”的图片也是最可能标签有问题的图。import numpy as np likely_mislabeled [] model.eval() with torch.no_grad(): for images, labels in test_loader: images images.to(cuda) probs torch.softmax(model(images), dim1) max_probs, predicted torch.max(probs, dim1) for i in range(len(labels)): if 0.4 max_probs[i].item() 0.6: likely_mislabeled.append({ label: labels[i].item(), pred: predicted[i].item(), confidence: max_probs[i].item() }) print(f可疑样本数量: {len(likely_mislabeled)}) for item in likely_mislabeled[:10]: print(item)逻辑说明softmax把输出的 logits 转成概率max_probs取每个样本的最高概率值。如果最高概率都只有 0.4~0.6说明模型在两个类别之间摇摆这类样本要么是特征不明显要么是标签错误。参数说明阈值区间可以根据你的任务调整如果整体置信度偏高可以收紧到 0.5~0.65如果偏低就放宽到 0.3~0.7。从那以后我每次拿到新的图像分类数据集都会强制走一遍这个流程先跑 show 脚本人工看 50 张再训练一个基线模型做错误样本可视化最后用置信度阈值筛选可疑标签。这个过程看起来多花了半天时间但帮我避开了很多次“准确率高但实际不可用”的坑。希望这份无人机视角的森林桩燃烧识别数据集也能帮你少走几步弯路。本文还有配套的精品资源点击获取