公司动态
航拍滑坡泥石流检测数据集:VOC+YOLO双格式与YOLOv8实战
简介在地质灾害识别与遥感影像目标检测领域高质量标注数据是模型落地的关键前提。目标检测算法依赖统一的标注格式来描述目标位置其中VOC格式采用绝对像素坐标YOLO格式则使用归一化的中心点与宽高两种格式各有适用场景掌握转换原理能避免数据使用中的坑。以航拍视角下的滑坡泥石流检测任务为例相比通用目标检测灾害场景存在视角差异、尺度多变和背景复杂等挑战更需要贴合业务的数据集与针对性的训练策略。通过YOLOv8对双格式数据集进行训练验证能够有效支持边坡监测、灾害隐患识别等工程场景。本文围绕这套5619张航拍滑坡泥石流数据集系统梳理了VOC与YOLO格式的底层逻辑、数据体检、训练参数调优、指标异常排查及模型部署经验为使用遥感影像做目标检测的开发者提供可直接复用的工程实践参考。 做地质灾害识别的人应该都有体会找一套干净、标注规范的航拍滑坡泥石流检测数据集有多费劲。我拿到这套5619张VOCYOLO双格式的航拍滑坡泥石流检测数据集后直接用YOLOv8跑了训练和验证整体感受是数据质量比很多网上拼凑的遥感数据集干净双格式也省去了自己写转换脚本的麻烦。如果你正在做边坡监测、灾害隐患识别、遥感影像目标检测或者单纯想拿一份现成数据练手YOLO这套数据是值得花点时间研究的。不过话说回来数据集到手只是第一步很多人栽在格式理解、目录整理和训练参数上。这篇文章我把自己跑这套数据的完整思路写下来包含VOC和YOLO格式到底怎么对应、数据体检怎么做、YOLOv8训练参数怎么调、指标全为0这种经典坑怎么排查以及最后导出模型部署时要注意什么。内容不算高深但都是我实际踩过之后觉得对新手最有用的部分。1. 这套数据集解决了航拍灾害识别里的什么痛点先聊一个很现实的问题滑坡泥石流检测的目标和日常物体检测完全不是一个赛道。普通目标检测数据集里人、车、猫、狗都是边界清晰、语义明确的物体模型学起来相对容易。但滑坡泥石流这种东西在航拍视角下往往是一大片颜色接近岩土、植被混合的区域边界模糊标注本身就有主观性。没有一套贴合场景的数据集模型再先进也白搭。1.1 航拍影像与地面影像的本质差异航拍影像和地面影像的差异往细了说有三点这三点直接决定了你为什么不能拿普通数据集硬套第一是视角。航拍基本都是俯视或大角度斜视目标形态被拉长、压缩和COCO那种平视视角完全是两个世界。模型在COCO上预训练出来的特征对航拍目标不一定敏感。比如一个滑坡体在影像上可能只是一片裸露的土黄色区域纹理特征和周围山体非常接近如果不经过航拍数据微调用它做推理很容易漏检。第二是尺度。无人机飞行高度不同同一个滑坡体在画面里可能占几百像素也可能只占几十个像素。小目标在特征图下采样之后几乎没有有效信息这也是航拍检测最常见的难点。所以这套数据如果包含不同飞行高度、不同分辨率的样本对模型泛化能力的帮助会非常大。第三是背景复杂度。航拍影像里植被、阴影、河流、道路、建筑、云影都会混在一起目标可能被遮挡也可能和背景在纹理上非常相似。这种情况下模型的抗干扰能力比单纯的分类精度更重要。这套航拍滑坡泥石流数据集本质上就是在解决“俯视视角尺度多变复杂背景”下的检测问题。和地面拍照的灾害样本相比它更贴近无人机巡检、卫星遥感这些实际业务场景。1.2 5619张的规模在灾害场景里算是什么量级很多刚入门的人一听“5619张”会觉得是不是太少了。实际在灾害目标检测这个细分领域五六千张已经算得上中等偏上的体量。原因很简单灾害样本本身就不容易获取尤其是滑坡、泥石流这种突发性事件能拍到清晰正样本的机会有限还要人工标注框成本很高。对比一下公开的遥感检测数据集有些甚至只有几百张可用图。5619张影像假设每张平均有1到3个标注目标至少也能提供上万个实例对单类别检测甚至两三个类别的检测完全是够用的。另外现在训练目标检测模型基本都会用ImageNet或COCO上的预训练权重不是从零随机初始化。迁移学习所需的数据量本来就比从零训练少一个量级。我个人的经验是用预训练权重微调几千张高质量数据就能训练出一个在常见场景下表现不错的模型。真正限制效果的往往不是数量而是标注噪声、类别不平衡和训练策略。当然你要是想追求极致精度比如在某个特定区域部署那么后续增加本地采集数据、做主动学习选样本都是这套数据之外的补充工程。数据集的定位不是终点而是一个可靠起点。2. VOC与YOLO标注格式的底层逻辑与转换细节拿到压缩包后第一件事不是急着一股脑跑训练而是搞清楚里面两种标注格式是怎么组织的。VOC和YOLO是目标检测领域最常见的标注格式但它们的存储方式完全不同混着用很容易出事。2.1 VOC的XML标注结构VOC格式源起PASCAL VOC挑战赛每张图片对应一个同名的XML文件。XML里记录着图片文件名、路径、尺寸以及每个目标的类别和边界框坐标。打开一个典型的XML文件你会看到类似这样的结构annotation folderimages/folder filenameslope_001.jpg/filename size width1920/width height1080/height depth3/depth /size object namelandslide/name bndbox xmin524/xmin ymin318/ymin xmax1086/xmax ymax762/ymax /bndbox /object /annotation注意VOC里的坐标是绝对像素坐标直接使用图片原始尺寸。xmin、ymin是左上角xmax、ymax是右下角。训练的时候大多数框架不会直接读XML而是先转成内部格式。Ultralytics YOLO也不例外它需要的是TXT标注如果你只有VOC格式就需要先转一下。2.2 YOLO的TXT标注结构及归一化换算YOLO格式就简单很多每张图片对应一个TXT文件每一行代表一个目标共5个数class_id x_center y_center width height其中class_id从0开始的类别编号。x_center、y_center边界框中心点坐标除以图片宽和高归一化到0~1。width、height边界框的宽和高同样归一化到0~1。从VOC转YOLO的公式也不复杂x_center (xmin xmax) / 2 / width y_center (ymin ymax) / 2 / height w (xmax - xmin) / width h (ymax - ymin) / height我习惯写一个小脚本批量处理逻辑很简单import os import xml.etree.ElementTree as ET def voc_to_yolo(xml_path, out_path, classes, img_w, img_h): tree ET.parse(xml_path) root tree.getroot() lines [] for obj in root.iter(object): name obj.find(name).text if name not in classes: continue class_id classes.index(name) box obj.find(bndbox) xmin float(box.find(xmin).text) ymin float(box.find(ymin).text) xmax float(box.find(xmax).text) ymax float(box.find(ymax).text) x_center (xmin xmax) / 2 / img_w y_center (ymin ymax) / 2 / img_h w (xmax - xmin) / img_w h (ymax - ymin) / img_h # 防止边界坐标超出0~1 x_center min(max(x_center, 0.0), 1.0) y_center min(max(y_center, 0.0), 1.0) w min(max(w, 0.0), 1.0) h min(max(h, 0.0), 1.0) lines.append(f{class_id} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}) with open(out_path, w) as f: f.write(\n.join(lines))说实话现在很多数据集会直接同时提供VOC和YOLO两种格式省了转换这一步。但这不代表你可以完全不理解。因为实际使用时你可能会增加新类别、修改部分标注到时候还是得自己处理。2.3 双格式同源到底意味着什么VOC和YOLO双格式的数据集通常是用同一种标注工具或脚本生成的两份标注描述的是同一批目标。所以拿到手后最好抽查几个文件确认两个格式下的框是否一致。具体做法是随机抽三五个文件名先读XML里的边界框再读TXT里的归一化坐标换算回去对比。如果对不上说明两份标注可能来源不一致这时候一定要以其中一份为准不能混着用。如果两份标注基本一致就可以放心使用。这里有个新手很容易踩的坑有些数据集虽然叫“VOCYOLO格式”但其中YOLO的类别编号可能没有从0开始或者类别顺序和VOC里的类别列表不一致。比如VOC里第一类是landslide第二类是debris_flow但YOLO的TXT里把debris_flow排成了0landslide排成了1。训练完之后推理结果就会完全错乱。拿到数据后一定要先统计每类框的数量并打印几条TXT内容核对类别ID。3. 数据准备从解压到训练集划分的完整操作无论你用什么框架训练前都需要把图片和对应标注整理成一种固定目录结构。Ultralytics YOLO对目录的容忍度比较高但为了省心我建议你手动整理成最标准的形态。3.1 目录结构与初步体检我习惯把数据组织成下面这样data/ ├── images/ │ ├── train/ │ ├── val/ │ └── test/ └── labels/ ├── train/ ├── val/ └── test/或者更简洁一点把所有图片放一个目录所有标签放一个目录然后用train.txt、val.txt这样的文件列表来划分data/ ├── images/ ├── labels/ ├── train.txt ├── val.txt └── test.txt两种方式Ultralytics都支持但第一种更直观。我推荐第一种后续写数据增强脚本、做错误分析都方便。拿到压缩包解压后第一步做体检检查图片数量和标注文件数量是否一致。尤其是YOLO格式如果某张图片没有对应TXT训练时会直接报错或跳过导致实际参与训练的样本比你以为的少。随机打开几张图片看看标注框是否真的框在目标上。这一步可以写一个快速可视化脚本用OpenCV或matplotlib画框检查。检查图片格式是否统一。jpg、png混用不是大事但有些图片可能是灰度图或带透明通道可能在训练时报错。这里我给出一个很简单的Python体检脚本import os from PIL import Image img_dir images label_dir labels img_names set(os.listdir(img_dir)) label_names set(os.listdir(label_dir)) # 检查没有标签的图片 for img in sorted(img_names): stem os.path.splitext(img)[0] if stem .txt not in label_names: print(fNo label: {img}) # 检查无法打开的图片 for img in sorted(img_names): try: im Image.open(os.path.join(img_dir, img)) im.verify() except Exception as e: print(fBad image: {img}, {e})这类脚本很简单但真的能避免很多训练过程中莫名其妙的问题。3.2 剔除坏图和错标样本体检之后还要处理两类问题坏图和错标。坏图指的是图片文件本身损坏、分辨率异常小、或者全黑全白导致没有信息量。这些文件直接删掉或移动到备份目录。分辨率为0或宽高小于32像素的图片在YOLO数据增强阶段也可能产生奇怪的结果。错标就比较隐蔽。常见的有边界框坐标越界比如xmax超过了图片宽度。这通常是因为标注工具或转换脚本的bug。框的宽度或高度为0这种框没有面积训练时loss计算会出现NaN。类别ID超过类别总数如果类别文件里只有3类但TXT里出现了class_id5说明类别映射有问题。标注框太小比如框的宽高只有1个像素这种目标基本学不到特征可以当成噪声剔除。我习惯在训练前加一个过滤逻辑坐标越界的框直接裁到边界内面积过小的框删除。这样虽然会损失一点点样本量但能显著减少训练发散的概率。3.3 训练集/验证集/测试集划分划分数据集不是简单随机切一刀就完事。如果你的数据来自多个不同场景随机划分时某个类别可能全部落在测试集里导致训练时完全没见过这一类。正确做法是按类别分布做分层划分。先统计每个类别在所有样本里出现的频率然后再按比例同时分配到训练、验证、测试集。如果类别不平衡严重还可以在验证集里保证每个类别至少出现若干次。一般我用8:1:1或9:0.5:0.5的比例。在几千张图的体量下验证集留500张左右就够测试集可以留500~1000张。测试集只用来做最终评估不能在调参过程中反复看它的指标。如果你用的是Ultralytics划分好目录后data.yaml里只需要写train和val路径test会在训练结束后单独评估时用到。3.4 类别不平衡问题滑坡泥石流数据集的类别不平衡情况很常见比如“滑坡”样本远多于“泥石流”。这会导致模型偏向多数类少数类的召回率很低。解决不平衡的方法有几个过采样把少数类样本复制一份或几份放进训练集。数据增强对含少数类的图片做更强的变换旋转、翻转、颜色扰动来扩大样本量。调整损失权重YOLO和大多数检测框架都不直接支持类别权重但可以通过修改数据集分布来间接实现。更简单的方式对少数类的目标做马赛克增强时提高它被选中的概率。一个比较容易落地的策略是先用原始数据跑一版baseline看各类别的mAP。如果少数类明显偏低再有针对性地增强。不要一上来就把数据翻一倍那样训练时间翻倍收益却不一定明显。4. 用YOLOv8训练滑坡泥石流检测模型的配置与踩坑数据准备好了接下来就是训练。现在Ultralytics的YOLO系列是主流v8和v11都很常见。我用YOLOv8s跑这套数据整体稳定下面把配置和几个经典坑说清楚。4.1 环境安装与数据集YAML配置安装Ultralytics很简单pip install ultralytics装完建议确认一下版本不同版本之间接口有些差异。数据准备好之后写一个data.yamlpath: D:/project/landslide_dataset train: images/train val: images/val test: images/test names: 0: landslide 1: debris_flow这里有个特别容易忽略的坑path如果写了train和val就应该是相对于path的路径不要再写绝对路径。如果你把path写成绝对路径train又写绝对路径某些版本会拼接出错误路径导致训练读不到图。另一个坑是类别名称的顺序。names里的顺序必须和TXT标注里的class_id一致。如果标注文件里0是滑坡、1是泥石流那么names里0就必须对应landslide。很多次看到有人把names顺序写反训练不报错但预测结果完全对不上。4.2 训练参数选择imgsz、batch、epochs怎么定这是新手问得最多的问题。参数选择没有绝对标准但可以按数据特点来定。对于航拍影像原图分辨率往往很高但显存有限。imgsz太小会丢失小目标信息太大又训练太慢。我的经验是如果你用YOLOv8n或v8simgsz640起步先看效果。如果原图里目标整体偏小例如占据不到图像面积的1%可以尝试imgsz1280。如果显存不够而且图片非常大比如4000x3000可以做切片训练把原图切成若干张无重叠的patch再对patch训练。切片会显著增加训练样本量但要注意切片时别把目标切成两半可以加一点重叠。batch size和显存直接相关。默认auto模式会自动选择但稳定性不如手动指定。一般12G显存yolov8s imgsz640batch可以设16如果显存只有8Gbatch设8更稳。epochs方面我建议先跑100个epoch观察验证集指标是否还在上升。如果到100轮已经过拟合再减少如果还在涨可以继续re训练。不要盲目追求200、300轮数据量只有几千张的时候100轮基本够收敛了。训练命令yolo detect train datadata.yaml modelyolov8s.pt imgsz640 batch16 epochs100 patience20patience20是早停连续20轮验证集指标不提升就自动停止能省不少时间。4.3 训练指标全为0的排查思路训练过程中最让人崩溃的就是训练了十几个epochloss也在下降但验证集的mAP全部是0。我拿这套数据第一次跑的时候也遇到过后来梳理了排查链路基本就那几个原因。第一步先确认验证集里真的没有异常。写一个脚本读取验证集所有TXT检查是否有TXT文件是空的。空标注的图片如果进了验证集验证时可能被当成背景但不会直接导致mAP为0。class_id是否合法。如果TXT里出现了类别编号为2的target但data.yaml里只有0和1验证时可能导致标签解析失败所有指标归零。文件名是否严格对应。比如图片是0001.jpg标签是0001.txt但中间多了空格或扩展名不同最终标签加载不到mAP自然为0。第二步用一个很小的模型去过拟合一个batch验证代码链路。比如只取训练集的8张图片训练模型5个epoch看看loss是否下降。如果loss完全不动说明标注或数据加载有问题。如果能过拟合但验证集mAP为0问题多半在验证集的标签读取或类别映射。第三步检查数据增强。某些版本的马赛克增强在验证集上不会生效但如果你给验证集也加了强增强可能把目标增强到完全不可见导致mAP为0。在Ultralytics中验证集默认不做增强但如果你自定义了Dataset类就要注意。我的排查顺序是先看标签再看路径再看类别映射最后才怀疑模型本身。90%的情况是标签或路径问题。4.4 训练中如何暂停与恢复训练跑到一半发现验证集指标已经很好或者想换个参数继续跑这是常见操作。Ultralytics支持随时中断。如果你在命令行里按了CtrlC训练进程会停止。下次想继续可以直接使用上次保存的last.pt权重yolo detect train datadata.yaml modelruns/detect/train/weights/last.pt epochs30或者用Pythonfrom ultralytics import YOLO model YOLO(runs/detect/train/weights/last.pt) model.train(resumeTrue)注意resumeTrue会继续原来训练到这个epoch的状态而不是重新开始。它读取的是训练状态的checkpoint包含优化器状态和历史指标曲线。如果不小心把last.pt覆盖了那就只能从头训了。训练时每隔一段时间会自动保存last.pt和best.ptbest是根据验证集指标选出来的最优权重。恢复训练时最好用last.pt因为它保存了完整状态部署时用best.pt因为它的泛化指标更好。4.5 YOLOv8和v11怎么选网上关于YOLOv8和v11的讨论很多。简单说v11在特征提取网络上做了一些改进理论上相同FLOPs下精度略高但并不意味着v8就不能用。你要考虑的是算力平台和部署生态。如果你只是拿这套数据练手或最终要在PC端或服务端跑用YOLOv8s完全够。v8的生态丰富转ONNX、TensorRT、OpenVINO的教程很多踩坑资料也全。如果你追求极致精度而且有较新的NVIDIA显卡可以试YOLOv11x但训练时间和显存消耗会明显上升。先跑通v8n或v8s再考虑升级是一种比较稳妥的路径。模型选择本质上是在“精度、速度、显存、部署难度”之间取平衡没有绝对最优。5. 模型导出与实地应用的一些经验训练完模型不能只停留在验证集指标。真正落地到无人机影像或监控视频里还要解决导出、推理速度和目标小这几件事。5.1 导出ONNX并验证输出Ultralytics导出ONNX很简单yolo export modelbest.pt formatonnx dynamicTrue导出后用onnxruntime做一次推理确认输出维度符合预期。YOLOv8的ONNX输出通常是[1, 84, 8400]其中84 4个框坐标 80个类别概率COCO预训练。如果你用自己的数据集训练类别数不是80最后一个维度就是4 num_classes。很多人在导出后直接用原始图片尺寸去喂ONNX忘了输入需要resize到训练时的imgsz。如果你的模型是用imgsz640训练的输入ONNX的图片也最好resize到640否则检测框位置会有偏差。5.2 在低算力设备上的推理优化如果你要在机载设备或边缘盒子上跑INT8量化是提速的关键。TensorRT、OpenVINO都支持PTQ量化。量化后模型体积减小、推理加速但mAP可能会掉1到2个百分点需要重新在验证集上评估。航拍场景还有一个很实际的优化手段切片推理。如果原图是4000x3000直接用imgsz640推理目标太小检测不到。可以在推理时把原图切成640x640的patch用同一模型分别检测然后合并结果。切片推理虽然增加了推理次数但能显著提高小目标召回率。这也是很多遥感检测竞赛的惯用套路。我写过一个简单的切片脚本核心逻辑就是把原图按固定步长裁剪记录每个patch在原图中的偏移量推理后再映射回全局坐标。切片的步长一般设置为patch尺寸的50%到80%留一点重叠避免目标被切断。5.3 数据增强与难例挖掘当你发现验证集指标还行但实际航拍视频里漏检或误检一堆时就该上难例挖掘了。难例挖掘的核心是迭代用当前模型对一批无标签的航拍影像做预测筛选出置信度较低但实际确实存在目标的样本人工标注后加入训练集。这样模型就能在它最容易犯错的地方做强化学习。数据增强方面滑坡泥石流这类地形目标对旋转比较敏感但这也取决于你的业务场景。如果你需要模型在任意朝向的航拍图中都能检测就加入90度、180度、270度旋转和水平翻转。如果场景是固定朝向的无人机航线翻转增强要慎重可能会让目标形态失真。马赛克增强在目标检测中很常见但对小目标不一定是好事。因为马赛克会把四张图缩在一起目标进一步变小。我训练这套数据时如果imgsz640马赛克对小目标的影响不大但如果imgsz1280且目标本身就小马赛克之后目标只剩几个像素模型很难学。此时可以考虑降低mosaic概率甚至关闭。最后分享一个我个人的习惯每次训练跑完不只看mAP还要看每一类的precision和recall。对于滑坡泥石流这类灾害场景漏检的代价比误检高得多所以我通常会选择召回率更高的模型再通过后处理阈值来压误报。这套航拍数据集本身质量不错但真正要部署到业务中还是得结合自己的地形、拍摄高度和硬件条件反复调。数据是死的模型是活的把数据用好比一味换模型结构更值得投入时间。本文还有配套的精品资源点击获取