泥石流滑坡目标检测数据集:YOLO+VOC双格式详解与训练避坑指南 简介针对泥石流、滑坡等地质灾害目标检测任务提供一套可直接用于训练的标注数据集面向计算机视觉研究者与算法工程师可用于模型训练与算法验证。压缩包约166MB内含图片、标注、标签三个文件夹共有6786个文件即2262张清晰jpg图片及同数量的VOC格式xml与YOLO格式txt标注下载后无需格式转换即可接入常见检测框架。数据共标注两个类别泥石流Debris-flow与滑坡landslide总框数6508个其中泥石流429个、滑坡6079个矩形框定位准确覆盖场景多样可用于地质灾害监测、遥感影像识别等方向。压缩包内目录结构规范图片与标注文件一一对应便于直接编写数据加载脚本当前已有653人学习下载。标注经过人工核对准确性有保障适合作为目标检测模型训练、算法比较或教学演示的基准数据。1. 泥石流滑坡目标检测数据集YOLOVOC 双格式的 2262 张图到底能做多少事泥石流滑坡这种地质灾害场景在应急监测和工程巡检里一直是目标检测落地的硬骨头真正能公开下载、带完整标注的数据集却极少。拿到这份“泥石流滑坡数据集2262张YOLOVOC格式.zip”时最该先意识到的一件事是它省掉的不只是标注的人工成本还有你把图像数据整理成算法能吃的格式那一步。2262 张图听起来不算多但双格式设计意味着你几乎不用改代码——YOLO 系列直接读 txtVOC 体系也就是 Pascal VOC 那套 XML 标注则能转成 COCO、旋转框或者其他框架需要的结构。适合谁用做地质灾害监测的算法工程师、搞无人机巡检的落地团队、拿数据集做对比实验或者毕业论文的研究生都能从中拿到一份可复现的地质灾害检测起点。2. 打开 zip 先看目录images、labels、Annotations 三套文件如何对应同一批图片解压一个目标检测数据集第一件事不是急着开训练而是先把目录结构梳理清楚。以这份“泥石流滑坡数据集”为例常见做法是图片放在 images 目录YOLO 格式的 txt 标签放在 labels 目录VOC 格式的 XML 标注放在 Annotations 目录偶尔还会附带 ImageSets/Main 里的 train.txt、val.txt 划分文件。三个目录的文件数量应该完全一致图片名和标签名除了后缀不同前缀必须严格相同否则就是典型的数据错位问题而这个错位几乎不报错只会让训练出来的模型莫名其妙地漏检。2.1 标准目录结构与文件名对应关系先跑一个 hash 校验再动手拿到 zip 先解压然后立刻做三件事看目录、数文件、跑一个简单的文件名对应校验。我习惯用一段 Python 一次性把三个目录的文件名列出来求差集import os from pathlib import Path data_root Path(/path/to/dataset) images_dir data_root / images labels_dir data_root / labels annotations_dir data_root / Annotations img_names {p.stem for p in images_dir.glob(*)} label_names {p.stem for p in labels_dir.glob(*)} xml_names {p.stem for p in annotations_dir.glob(*)} print(图片数量:, len(img_names)) print(YOLO 标签数量:, len(label_names)) print(VOC 标注数量:, len(xml_names)) print(图片缺少标签:, img_names - label_names) print(标签缺少图片:, label_names - img_names) print(YOLO/VOC 不一致:, label_names ^ xml_names)这里的原理很简单YOLO 格式的 txt 和 VOC 格式的 xml 描述的是同一批图片理想情况下三个 set 完全相等。set 求差集能直接暴露文件名不匹配的问题。很多数据集在打包时会改动文件夹结构打包工具的编码差异也可能导致文件名乱码不要完全相信 README 里的描述自己跑一遍校验最靠谱。如果发现不匹配下一步就是从某个目录里把缺失的文件对照找出来或根据 README 的重命名规则重新整理。2.2 用脚本统计类别与样本分布每个类到底有多少框解压后第二个必做动作是统计类别分布。类别名在 VOC 的 xml 里也在 YOLO 的 txt 的类别 ID 里。用脚本把所有 xml 遍历一遍统计每个类别的对象数和图片数一次性看清这套数据集的构成。import xml.etree.ElementTree as ET from pathlib import Path from collections import Counter annotations_dir Path(/path/to/dataset/Annotations) class_counter Counter() img_with_obj set() for xml_path in annotations_dir.glob(*.xml): tree ET.parse(xml_path) root tree.getroot() for obj in root.findall(object): name obj.findtext(name) or obj.findtext(Name) if name: class_counter[name] 1 img_with_obj.add(xml_path.stem) print(类别统计:, dict(class_counter)) print(包含目标的图片数:, len(img_with_obj))这里能直接看出几个关键信号类别是不是只有一个比如 land slide 或 debris flow各类别框数是不是极度不均衡比如泥石流 5000 个框、落石只有 200 个框有多少图片是纯背景图。对于 2262 张图的数据集如果目标是做单类检测那类别数最好就是 1如果出现 3 个以上类别且数量相差悬殊后续训练时要考虑类别权重或者干脆合并成一个大类否则模型会被多数类带偏。2.3 验证标注坐标系txt 的归一化坐标和 xml 的像素坐标怎么核对YOLO 格式里 bbox 是归一化坐标VOC 格式里是像素坐标两者之间应该是严格的换算关系。我遇到过不少数据集在转换时把宽高顺序写反结果训练出来的框全是偏的。用脚本抽查几张图验证一下import cv2 from pathlib import Path img_path Path(/path/to/dataset/images/sample_001.jpg) txt_path Path(/path/to/dataset/labels/sample_001.txt) xml_path Path(/path/to/dataset/Annotations/sample_001.xml) img cv2.imread(str(img_path)) h, w img.shape[:2] with open(txt_path) as f: lines [line.strip() for line in f if line.strip()] for line in lines: cls_id, x_center, y_center, bw, bh map(float, line.split()) x int((x_center - bw / 2) * w) y int((y_center - bh / 2) * h) box_w int(bw * w) box_h int(bh * h) print(fYOLO 转像素坐标: {x}, {y}, {x box_w}, {y box_h})然后把 xml 里读到的 bndbox 坐标打印出来对比。两者相差在 1 到 2 个像素以内是正常的差了几十上百像素则说明 txt 生成时归一化没做好训练前必须修复坐标系。这一步其实就是把黑匣子打开看一眼避免后续训练完才发现框全错位那时候你的整个调参过程都会变成玄学。3. 把 YOLO 格式喂给训练流程YOLOv8 跑地质灾害检测的最小配置目录验证完以后就可以把 YOLO 格式的标签跑进训练流程了。当前主流的训练框架是 Ultralytics 的 YOLOv8更新版本的 YOLOv11 在环境配置和使用习惯上和 v8 基本一致下面按稳定通用的方式写。地质灾害检测的背景复杂有泥浆、碎石、植被、山体阴影模型需要学的是“区域形状 纹理异常”而不是简单颜色。因此配置训练时要注意输入分辨率、增强策略和类别权重。3.1 写 data.yaml类别名、图片路径和验证集划分的三种写法YOLOv8 训练时通过一个 yaml 文件描述数据集这个文件放在哪个目录、怎么写直接决定你能不能在命令行一次跑通。最基本的结构是path: /path/to/dataset train: images/train val: images/val names: 0: landslide常见写法有三种。第一种是把 path 写成绝对路径train 和 val 用相对路径适合本机单机训练第二种是把 path 留空train 和 val 写完整路径第三种是数据集里自带 train.txt 和 val.txt路径指向这两个文本文件。我一般用第一种因为改机器时只需改 path 一行不会出现局部路径忘记改的问题。注意 names 的 ID 必须和 labels 目录下 txt 里的第一个数字对应类别 0 就是 yaml 里 names 列表的第一个名字。3.2 训练参数imgsz640、batch16、epochs100 在 2262 张图上怎么调2262 张图的数据量很关键。这个规模既不是从小样本出发也不能指望训练几百个 epoch 还不过拟合。我常用的一组稳定参数是yolo detect train \ data/path/to/dataset/data.yaml \ modelyolov8n.pt \ imgsz640 \ batch16 \ epochs100 \ cacheTrue \ patience20 \ lr00.01 \ device0几个参数的取舍理由yolov8n 是 nano 版参数量小在小数据集上不容易一上来就过拟合而且训练速度快适合先验证数据质量。imgsz 用 640 是因为大多数地质灾害图像里目标占比不小用 640 能在显存占用和分辨率之间取得平衡目标很小的话再考虑 960 或 1280。batch 取 16 在常见 8G 显存的显卡上刚好能跑如果你的显存只有 6G把 batch 降到 8 并打开 cacheTrue 让数据预加载速度不会慢太多。epochs 100 加上 patience 20 做早停意思是验证集指标连续 20 轮没提升就自动停止这能避免在小数据集上反复训练浪费时间。lr0 保持默认 0.01一般不需要动。3.3 遇到 Loss 不降怎么办先查学习率还是先查数据训练到第十几个 epoch如果 loss 一直在一个平台上磨第一反应不应该是调学习率而是先怀疑数据配置。常见情况有两种一是标签文件和图片没对应上模型实际上在学大量无效区域二是验证集里某个类别的样本太少导致 val loss 波动大。排查方式是先看训练集 loss 是否下降训练集 loss 降但验证集不降说明过拟合两边都不降先检查标签有没有读出来、类别 ID 是否连续。YOLO 要求类别 ID 从 0 开始连续编号如果数据里原本有 class 5 但没有 class 4就会导致标签读取异常训练时的 yolo 损失函数根本算不出有效梯度。4. 把 VOC 格式转成可用资产Python 解析 XML 并导出 COCO 迁移学习格式VOC 格式不是用来直接训练的主流格式却是各种标注工具的通用交换格式。这份数据集同时给了两套标注最大的价值就是可以变成任何训练框架的输入。如果你要用 Detectron2、MMDetection 或者自己写的 PyTorch 数据加载器都要先把 XML 转换成对应的字典或 JSON。这里的核心不是转换代码本身而是理解 bbox 的坐标体系在不同框架里到底怎么定义。4.1 写一个干净的 VOC 解析脚本提取 bbox 并统一成字典结构先写一个通用的 VOC 解析函数把 XML 里的目标信息提取成统一的字典后面的转换逻辑都建立在这个解析之上。import xml.etree.ElementTree as ET from pathlib import Path from typing import Dict, List def parse_voc_xml(xml_path: Path) - Dict: tree ET.parse(xml_path) root tree.getroot() size root.find(size) width int(size.findtext(width)) height int(size.findtext(height)) objects [] for obj in root.findall(object): name obj.findtext(name) xmlbox obj.find(bndbox) xmin float(xmlbox.findtext(xmin)) ymin float(xmlbox.findtext(ymin)) xmax float(xmlbox.findtext(xmax)) ymax float(xmlbox.findtext(ymax)) objects.append({ name: name, bbox: [xmin, ymin, xmax, ymax], }) return { image_path: root.findtext(path), width: width, height: height, objects: objects, }这个函数把 XML 解析成 image_path、width、height、objects 四个字段其中 bbox 用 [xmin, ymin, xmax, ymax] 的形式保存。注意有的 VOC XML 的 size 节点里 width/height 可能没写那就要用 cv2.imread 去读原图自己拿宽高。bbox 坐标全部保持 float转成像素框时再取整避免精度损失。这些看起来小的地方在后续做数据增强和拼接时会被反复用到。4.2 导出 COCO 需要的三个数组images、annotations、categories 怎么写COCO 格式的核心是三个数组转换时最容易出错的是 annotation 数组里的 bbox 字段。COCO 的 bbox 格式是 [x, y, width, height]不是 VOC 的 [xmin, ymin, xmax, ymax]这个坑几乎每个转框架的人都会踩一次。import json from pathlib import Path data_root Path(/path/to/dataset) annotations_dir data_root / Annotations output_json Path(/path/to/output/coco_annotations.json) categories [{id: 0, name: landslide}] images [] annotations [] ann_id 0 for xml_path in sorted(annotations_dir.glob(*.xml)): data parse_voc_xml(xml_path) image_id len(images) images.append({ id: image_id, file_name: xml_path.stem .jpg, width: data[width], height: data[height], }) for obj in data[objects]: xmin, ymin, xmax, ymax obj[bbox] w xmax - xmin h ymax - ymin annotations.append({ id: ann_id, image_id: image_id, category_id: 0, bbox: [xmin, ymin, w, h], area: w * h, iscrowd: 0, }) ann_id 1 coco_dict { images: images, annotations: annotations, categories: categories, } with open(output_json, w) as f: json.dump(coco_dict, f, indent2) print(f已导出 COCO 标注: {len(images)} 张图片, {ann_id} 个目标)这里的参数说明category_id 必须和 categories 数组里的 id 对应不能直接用原来的类别名否则 MMDetection 读取时会发生类别错位area 字段可以不填但填了便于做小目标筛选iscrowd 置 0 表示这些目标都是独立实例。跑完这个脚本用 json.load 重新读一下检查 images 数量和 annotation 里 image_id 是不是都有效这两类问题是 COCO 加载时报错的头号原因。4.3 如果目标框架只认 YOLO从 VOC 反向生成 txt 时要注意的归一化边界有的团队用的是老版本的 YOLOv3 或者 Darknet 框架那就需要把 VOC 反向转成 YOLO 的 txt。核心算法是归一化x_center 等于 xmin 和 xmax 的平均值除以图片宽度box width 等于 xmax 减 xmin 再除以图片宽度。for obj in data[objects]: xmin, ymin, xmax, ymax obj[bbox] x_center (xmin xmax) / 2 / width y_center (ymin ymax) / 2 / height box_w (xmax - xmin) / width box_h (ymax - ymin) / height # 严格限制在 0~1 之间防止坐标越界 x_center min(max(x_center, 0), 1) y_center min(max(y_center, 0), 1)这个边界处理不是多余的。泥石流图像里有些目标紧贴图片边缘标注时 xmax 可能正好等于 width转换后得到 1.0 是合法边界值但有些标注工具会标出越界坐标比如 xmax 比 width 大几个像素这时候如果不 clampYOLO 在解码时就会产生错误预测框。我通常会在 clamp 之后还要检查 bbox 面积是否过小如果归一化后的 w 或 h 小于 0.001说明这个标注基本是无效框直接过滤掉。5. 地质灾害数据集训练避坑2262 张图里最常翻车的五个细节这部分是根据我自己跑数据集总结的排查记录。地质灾害影像和普通物体检测不一样场景里的石头、泥土、植被在灰度纹理上有很大相似性稍有标注错误就会放大成严重的误检。下面五条是一条条踩出来的教训按现象、原因、解决三步写清楚。5.1 类别 ID 对不上验证集 AP 全为 0 的排查路径现象训练过程正常Loss 在下降但每个 epoch 结束打印的验证集 mAP 全是 0甚至精确率和召回率都显示 0.000。原因最典型的错法是数据集的 labels 里类别 ID 写的是 1、2、3而 data.yaml 的 names 列表只有三个元素框架会自动把标签转成 0、1、2但 txt 文件里没有同步改导致类别 ID 超出范围。另一个原因是有的人把背景图也放进了训练集但没给标签模型在验证时对无目标图片产生空预测拉低了整体指标。解决先把所有 txt 里的类别 ID 去重看一下cat labels/*.txt | awk {print $1} | sort -n | uniq -c如果输出显示 1、2、3 而不是 0、1、2那就写脚本批量减 1。如果是背景图的问题把没有对应 txt 的图片从训练集里移出去放到一个单独的负样本目录备用。5.2 框的位置整体偏移EXIF 旋转与归一化坐标的冲突现象训练出来的模型检测框总是往右上角偏几十个像素在验证集里看预测框和真实框重叠率低但单独打开图片看目标位置是清楚的。原因手机或无人机拍摄的 JPG 图片带 EXIF 方向信息。cv2.imread 读图时会自动应用旋转但如果标注工具在生成坐标时读的是原图的像素宽高或者反过来读到的是旋转前的宽高坐标就整体错位了。泥石流数据里大量图片来自航拍和手机这个问题出现的概率非常高。解决用 PIL 读取 EXIF 并把图片统一转成标准方向后再重写坐标或者干脆在预处理阶段用 ImageMagick 把所有图片的 EXIF 信息抹掉。最简单的做法是把图片全部重存成无 EXIF 的 JPG再让标注工具重新生成一遍路径一劳永逸。5.3 大图直接喂显存爆掉切图策略与分辨率权衡现象训练时程序跑了一两个 batch 后直接报 CUDA out of memory或者用了小 batch 后训练速度极慢一个 epoch 要十几分钟。2262 张图如果单张分辨率是 4000x3000把整图缩到 640 会丢失大量细节不缩又会显存爆炸。原因地质灾害遥感图分辨率往往偏高直接 resize 会让目标小到不可分辨不 resize 又超过 GPU 显存上限。这里的问题不是代码而是策略。解决常见做法是把大图切成 640x640 或 1024x1024 的子图切的时候保留重叠区域避免目标被切破。另一种做法是用 tiling 策略训练时保持原图分辨率但用 patch 方式采样。我一般先看目标在图上占多大如果目标宽度占原图 1/10 以下就用切图如果占 1/5 以上直接缩到 1280 训练就行。切图后数据集会从 2262 张膨胀到几千甚至上万张需要重新统计类别分布。5.4 小目标漏检严重对象尺寸分布先统计再做多尺度训练现象模型在验证集上整体 AP 不算低但把检测结果可视化后发现图中那些小面积的碎石堆几乎全漏检检测框只集中在面积大的泥石流主体上。原因YOLO 系列对小目标的敏感度天然弱于大目标。如果数据集中小目标框占比少模型优化方向会被大目标主导。这不是模型结构问题而是数据分布问题。解决用脚本统计所有框的面积比例看看宽高小于原图 1/10 的框占多少。如果这类小目标超过总框数的 30%那要开启多尺度训练参数把 imgsz 从 640 提升到 960并在训练时设置 scale0.5, 1.5 做随机尺度增强。如果小目标占比仍然很低那就考虑只用大目标做一轮粗筛再对小目标区域做局部放大检测。5.5 模型对山体背景误报缺乏背景负样本现象训练收敛后模型在纯山体、纯植被的图片上频繁输出检测框置信度还不低0.3 到 0.5 之间徘徊。验证集因为是同一批图像分布指标显示一切正常一上外场就翻车。原因这套 2262 张数据集的图片几乎都是含目标的没有足够多的纯背景图片作为负样本。模型没学过“没有目标时应该输出什么”自然会把纹理复杂区域当作目标。这是目标检测数据集最常见也是最容易被忽略的问题。解决从训练集中把那些标注框特别少的图片找出来加上完全没有标注的图片单独作为负样本集。YOLO 训练时可以直接把一个空的 labels 文件放进负样本图片目录模型会学到背景区域的置信度应该压低。实际操作时负样本比例控制在数据集总量的 10% 到 20% 比较稳定。没有现成负样本的话可以收集附近地形相似但与目标无关的航拍图但要注意分布偏移问题别把城市建筑也加进来。6. 验证与扩展用预训练权重跑一轮差异对比再决定补不补数据拿到这个数据集之后我建议先别急着从零训练。地质灾害检测的场景比较特殊从零训练很容易陷入局部最优。常规做法是加载一个 COCO 预训练权重用泥石流数据做迁移学习。但更重要的一步是在正式训练前先让预训练模型在当前数据集上跑一遍伪推理把标注和预测的差异可视化出来你就能直观看到这套数据集的质量上限在哪里。6.1 用 YOLOv8n 做伪推理快速找出标注和预测的差异下载 yolov8n.pt 预训练权重对数据集的图片批量做推理然后把预测框和标注框画在同一张图上对比。这一步不要求模型效果好而是用它观察哪些目标是标注了但模型认不出来的哪些是模型预测出来了但标注里没有的。前者往往意味着标注框不够准后者意味着存在未标注的漏检目标。from ultralytics import YOLO import cv2 model YOLO(yolov8n.pt) img_path /path/to/dataset/images/sample_001.jpg results model.predict(img_path, conf0.1) img cv2.imread(img_path) for box in results[0].boxes.xyxy.cpu().numpy(): x1, y1, x2, y2 box.astype(int) cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.imwrite(/path/to/output/compare_result.jpg, img)注意这里把 conf 设成了 0.1目的就是让模型尽量多地把候选框吐出来宁可多检也不能漏。对比图上一旦出现大面积的绿色框而标注里找不到对应区域那就说明原始标注有漏标后续训练会把这些区域当成背景来学严重影响精度。6.2 哪些差异值得回补标注误报、漏报、边界偏移的优先级可视化差异出来后要按优先级决定补不补标注。优先级最高的是漏报特别是图像中明显的泥石流主体没有被标注这类错误会让训练直接把正样本当成负样本影响最大。其次是边界偏移标注框比真实目标大一圈或者小一圈这会造成 IoU 计算偏差模型学到的是不精确的框回归目标。最次要的是误报也就是模型检测到但标注里没有的区域如果这个区域在图像中确实不是目标可以忽略如果是标注遗漏了目标就要补上。补标注时建议用 CVAT 或 LabelImg 这类工具人工修正不要在程序里自动扩散自动扩散会把错误传播到整个数据集。6.3 数据扩充的两个方向难例挖掘和外部影像资源的选取标准如果 2262 张图经过一轮伪推理后质量合格但泛化到新场景仍不够下一步做数据扩充。我一般先做难例挖掘从已经训练过的模型中找出置信度低但确实是目标的样本把它们加入训练集。这比随机采集图片有效得多因为难例往往集中在阴影遮挡、泥浆飞溅、目标半埋的状态。外部影像资源的选取标准也很直接要那种与目标场景在光照、地势、材质上相近的图像不要贪多贪全。航拍视角的山区沟壑、河床堆积、公路边坡垮塌这些与泥石流滑坡目标高度相关城市建筑、森林远景这些相关性低的不要混进来否则模型会学到无关纹理。加入外部数据时需要同步生成 YOLO 或 VOC 标注格式转换沿用第 4 章的方法把新增图片放进 train 目录重新跑一遍 2.1 节的文件名校验。最后一件事也是我的习惯每次训练结束把验证集上那些 false positive 的图单独存一个文件夹。下一轮数据扩充时先看这个文件夹里的图是纹理相似的误报还是真正的漏检再决定优先补哪类数据。这个习惯帮我避开了不少弯路也希望对你跑这份泥石流滑坡数据集时有用。本文还有配套的精品资源点击获取