☰
泥石流滑坡检测数据集详解:YOLO双格式训练与避坑指南
2026/10/9 17:48:49 网站建设 项目流程

简介:该数据集面向泥石流与滑坡目标检测任务,适合从事地质灾害监测、遥感图像智能解译的算法工程师及科研人员使用。压缩包内完整提供2262张清晰的JPEG图片,以及一一对应的2262个XML标注文件(VOC格式)和2262个TXT标签文件(YOLO格式),共覆盖6508个矩形框,其中Debris-flow类别429框、landslide类别6079框。图片未经增强,分辨率清晰,可直接用于YOLO、Faster R-CNN、SSD等常见目标检测模型的训练与验证。整体包体约166MB,目录按图片、标注、标签分文件夹整理,结构直观,便于按需读取、转换格式或做数据划分。目前已有655人学习下载,在同类地质灾害数据集中标注量较为充足,可有效减少数据采集与标注的时间成本,快速支撑模型基线测试和算法对比实验。

1. 泥石流滑坡检测数据集:2262张图能喂饱一个什么样的模型

泥石流和滑坡的目标检测,一直是灾害遥感领域里卷积神经网路最难啃的场景之一。难在哪?难在背景和目标长得很像——裸露的土坡、山体阴影、碎石堆、枯水期的河道,人眼都要看半天,模型更是容易把岩石纹理误判成滑坡体。这种情况下,与其用一两百张图凑合着训,不如拿一个已经有2262张标注图像的现成数据集,把训练成本压到最低。这个数据集的特殊之处在于它同时给了YOLO格式和VOC格式两套标注,无论是走yolov5/v8这条快路,还是想用mmdetection做横向对比,都不用自己写转换脚本。对正在做自然灾害识别、应急响应系统或者无人机的滑坡巡查方案的同学来说,这就是一个能直接落地的起点。适合谁呢?适合那些不想从零标注图像、但要快速验证检测方案的入门者,也适合想在泥石流和滑坡这两个易混淆类别上做模型调优的熟手。

2. 拆开数据集的包装:YOLO与VOC双格式的真实使用边界

2.1 双格式数据集的典型文件结构与配套资源

拿到一个YOLO+VOC双格式的数据集压缩包,第一件事不是急着训模型,而是先摸清文件组织方式。常见的做法是images目录只放原始图片,labels目录放YOLO的txt标注,Annotations目录放VOC的xml标注。这套结构之所以被广泛采用,是因为YOLO官方训练脚本会自动按图片路径推导标注路径——比如images/train/0001.jpg,它会自动去找labels/train/0001.txt。双格式的意义在于:YOLO格式负责快速训练,VOC格式保留完整的语义信息(比如XML里可以额外存标记属性、遮挡状态、截断状态),方便后期做数据清洗和可视化检查。

我一般会先跑一个统计脚本,确认三件事:图片数量是否真的到2262张,类别名是否只有泥石流和滑坡两类,标注框尺寸的分布是长尾还是集中。这个前置检查能省下后面很多调试时间。

import os from collections import Counter from pathlib import Path img_dir = Path("images") lab_dir = Path("labels") imgs = list(img_dir.rglob("*")) labs = list(lab_dir.rglob("*.txt")) print("图像数:", len(imgs)) print("标注数:", len(labs)) cls_counter = Counter() box_sizes = [] for lab in labs: for line in lab.read_text().strip().splitlines(): parts = line.split() cls_counter[int(parts[0])] += 1 w, h = float(parts[3]), float(parts[4]) box_sizes.append(w * h) print("类别分布:", dict(cls_counter)) print("框面积中位数: {:.4f}".format(sorted(box_sizes)[len(box_sizes)//2]))

这段脚本的逻辑很直白:遍历所有YOLO标注文件,统计类别ID的出现次数,同时计算每个框的归一化面积。归一化面积中位数能快速告诉你数据集里是密集的大目标为主,还是以远处的小目标为主。中位数在0.1以上,说明检测目标普遍占据画面较大,模型用yolov8n这种轻量骨架就够;中位数在0.02以下,就得考虑高分辨率输入或者加大输入尺寸到1280。这个判断在泥石流滑坡场景里尤为重要,因为航拍图中滑坡体往往只在画面边缘占一小块。

2.2 YOLO格式:txt标注为什么比xml更适合直接进训练

YOLO格式每行一个目标,结构是class_id x_center y_center width height,五个数值全部归一化到0到1之间。归一化这个设计是YOLO训练效率的关键:无论输入图片是1920x1080还是640x640,训练时yolo会先做letterbox缩放到统一尺寸,归一化坐标在缩放过程中不需要任何额外转换,直接计算损失即可。

# 某张图片的标注内容,类别0代表泥石流,类别1代表滑坡 0 0.4321 0.5678 0.2345 0.3123 1 0.7891 0.2345 0.1567 0.1987

这段标注的含义是:第一个目标属于类别0(泥石流),框中心点在图片水平43.21%、竖直56.78%的位置,宽度占图片总宽的23.45%,高度占总高的31.23%。写训练脚本时不需要解析这个文件格式,yolo的Dataset类已经内置了读取逻辑,自己只需要提供data.yaml里的类别名列表。

这里有个容易踩的细节,文本文件末尾不能有多余的空行,某些编辑器会在保存时自动加换行,导致ultralytics读取时报索引越界。解决方法是统一用bash脚本清洗一次。

2.3 VOC格式:xml标注的价值在阅读和二次清洗

VOC格式的核心是Pascal VOC的XML标注结构。它比txt多保留了图像尺寸、目标名称的原始字符串、以及边界框的绝对像素坐标。这些信息在做两件事时不可替代:第一,可视化调试时,直接从xml画框到图片上,不需要做坐标反归一化;第二,把数据集喂给mmdetection等框架时,它原生支持VOC格式的数据集类,直接把Annotations目录指过去就行。

<annotation> <folder>images</folder> <filename>img_001.jpg</filename> <size> <width>1920</width> <height>1080</height> <depth>3</depth> </size> <object> <name>landslide</name> <bndbox> <xmin>320</xmin> <ymin>400</ymin> <xmax>750</xmax> <ymax>700</ymax> </bndbox> </object> </annotation>

注意xml的bndbox坐标没有归一化,是原始像素值。所以在改造模型做多尺度训练或者随机裁剪时,txt标注在裁剪后需要重新归一化,而xml标注同样需要同步修改绝对坐标;如果只改了图片没改xml,训练出来的模型在推理阶段会框偏。

2.4 双格式共存时的选择性策略

两套标注同时存在时,很多人会重复读取导致标签冲突。我的建议是:训练阶段只认YOLO格式,因为性能损耗最低;验证和错误分析阶段只认VOC格式,因为可以直接画框看错检是定位偏差还是分类混淆。如果训练脚本意外发现同一个图片有两个标注文件,优先以YOLO格式为准,并写日志警告,防止混用。

3. 用YOLO格式跑通一个滑坡检测训练流程

3.1 环境准备与数据划分:8:1:1还是9:1

先明确一点:身份证级别的双格式数据集,标注质量通常有保障,但你仍然需要自己划分训练集、验证集、测试集。常见比例是8:1:1,也就是1800张训练、226张验证、226张测试。不要直接用全部2262张训练,那样你连过拟合都看不出来。

写划分脚本时,最关键的坑是:必须按图片文件名去重,而不能简单的随机打乱整个目录。有些数据集里同一场景的多个相似图片(连续帧)可能会被同时分到训练集和验证集,导致验证集指标虚高。我的做法是先生成文件名前缀,再对前缀做哈希分桶。

import os import hashlib from pathlib import Path img_root = Path("images") all_imgs = sorted([p for p in img_root.rglob("*.jpg")]) scene_ids = {} for img in all_imgs: # 用文件名首段模拟场景ID,实际可按目录层级处理 scene_id = img.parent.name + "/" + img.stem.split("_")[0] scene_ids.setdefault(scene_id, []).append(img) train_f, val_f, test_f = [], [], [] for sid, imgs in scene_ids.items(): h = int(hashlib.md5(sid.encode()).hexdigest(), 16) % 10 if h < 8: train_f += imgs elif h < 9: val_f += imgs else: test_f += imgs for name, lst in [("train", train_f), ("val", val_f), ("test", test_f)]: with open(f"datasets/{name}.txt", "w") as f: for p in lst: f.write(str(p.resolve()) + "\n") print(len(train_f), len(val_f), len(test_f))

这段脚本的要点在于场景分桶:先用scene_id做哈希,再把哈希值映射到0-9的桶上,0-7进训练、8进验证、9进测试。这样同一场景的连续帧永远不会被切断。如果数据集里没有明显的场景ID可用,退一步的做法是直接随机打乱,但要把随机种子写死,避免每次复现结果不同。

3.2 编写data.yaml:三个必改参数

YOLO训练前只需要一个数据配置文件。核心就是path、train、val、nc、names。注意path是相对于训练脚本工作目录的,建议用绝对路径,否则换台机器跑就找不到数据了。

path: /home/user/datasets/debris_flow # 改成你自己的绝对路径 train: train.txt val: val.txt test: test.txt nc: 2 names: 0: "debris_flow" 1: "landslide"

有两个参数必须和你数据集对齐:nc必须等于2,names的顺序必须和YOLO标注文件里的class_id一一对应。如果搞反了,泥石流和滑坡的标签就互换了,训练时loss照样下降,但推理结果完全错位。验证方法很简单,找一张标注图,用python读txt第一个数字,再去xml里查一下对应的类别名。

3.3 启动训练并读懂前10个epoch的日志

环境装好ultralytics之后,训练命令尽量用显式参数,不要依赖默认值。我习惯把epochs、imgsz、batch三个参数写到命令里,方便后边改。

yolo detect train \ data=datasets/data.yaml \ model=yolov8n.pt \ epochs=100 \ imgsz=640 \ batch=16 \ lr0=0.01 \ patience=15 \ project=runs/debris_flow \ name=exp1

前10个epoch主要看三个指标:box_loss是否呈下降趋势、cls_loss是否抖动、验证集mAP50是否在30个epoch内升到0.5以上。如果box_loss降但mAP不涨,大概率是数据里有标签噪声;如果cls_loss在第一个epoch就跌到0.01以下然后不再动,说明类别太简单或者两个类别区分度低,需要考虑加样本或者增强。

4. 数据集使用避坑指南:五个真实会翻车的细节

4.1 坑一:图片尺寸不一致导致的anchor失效

现象:训练时loss正常下降,但验证集mAP在40个epoch后徘徊在0.3左右不涨。查日志发现验证阶段anchor匹配数远低于训练阶段。

原因:这个数据集的图片混合了航拍大图和手机拍摄的远近景,尺寸从640x480到4000x3000都有。yolo做letterbox缩放后,小目标被缩得更小,预设的anchor尺寸无法匹配。

解决:把imgsz从640调到1280,同时开启多尺度训练(ultralytics默认每10个epoch随机变换尺度)。如果显存不够,就把batch降到8并用yolov8n,或者裁剪数据集中分辨率过高的图片。另一种思路是关闭anchor机制,用yolov8的anchor-free模式,但说实话提升有限,不如换输入尺寸来得直接。

4.2 坑二:泥石流与滑坡类别边界模糊,导致mAP虚高

现象:模型在训练集上mAP50到0.9,验证集也到0.85,但实际测试视频里,把土坡上的碎石堆全部误检成泥石流。

原因:数据集中标注的“泥石流”和“滑坡”在视觉上高度重叠,标注员可能把同一区域在不同帧里标成不同类别;模型学到的不是语义概念,而是纹理特征。

解决:训一条单类别的检测器做基线,把所有框都当作“地质灾害”一种类别,对比双类别模型的混淆矩阵。如果单类mAP比双类高2个点以上,说明双类别划分本身就是噪声,不如先合并。对这类数据,实际业务里通常只关心“有没有灾害”,不关心具体是泥石流还是滑坡。

from ultralytics import YOLO model = YOLO("runs/debris_flow/exp1/weights/best.pt") metrics = model.val(data="datasets/data.yaml") print(metrics.confusion_matrix)

看混淆矩阵时,重点关注debris_flow被误判成landslide的比例,如果超过15%,这两类在当前标注粒度下就不应该分开。

4.3 坑三:小目标区域占比低,被当成背景学掉

现象:验证集里漏检的样本几乎都是目标像素面积小于32x32的框。

原因:泥石流/滑坡的标注框通常是一个很大的区域,但远距离拍摄时滑坡体在画面中可能只有一小块。yolo的下采样倍数决定了最小有效目标尺寸,8倍下采样后,32x32的目标只剩4x4特征,几乎不可识别。

解决:把输入分辨率提升到1280,并在训练时开启auto_augment的RandAugment策略,增强小目标的纹理对比度。另外,检查一下数据集本身:统计标注框宽高小于32像素的比例,如果超过10%,就挑出来单独多复制两遍,再配合mixup增强。这是最土但最有效的数据级优化。

4.4 坑四:VOC转YOLO时坐标归一化写错

现象:自己写完VOC转YOLO脚本后训练,验证阶段画出来的框完全跑偏,但loss正常。

原因:VOC的bndbox是绝对像素坐标,转YOLO时需要用宽度和高度分别归一化,但很多人在除以图像宽高时用的是某一维度统一除,或者写成了xmin/xmax相减后除以宽。更隐蔽的坑是xml里的width和height可能写反,尤其当图片是竖拍时。

解决:用下面这段转换函数,加了一步边界裁剪和反向校验。转换完成后,抽10张图用opencv画框回看,确认框的位置和原xml一致。

import xml.etree.ElementTree as ET import cv2 def voc_to_yolo(xml_path, img_path, class_map): tree = ET.parse(xml_path) root = tree.getroot() size = root.find("size") W = int(size.find("width").text) H = int(size.find("height").text) lines = [] for obj in root.iter("object"): name = obj.find("name").text if name not in class_map: continue 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) xmin, xmax = max(0, xmin), min(W, xmax) ymin, ymax = max(0, ymin), min(H, ymax) if xmax <= xmin or ymax <= ymin: continue cx = (xmin + xmax) / 2.0 / W cy = (ymin + ymax) / 2.0 / H bw = (xmax - xmin) / W bh = (ymax - ymin) / H lines.append(f"{class_map[name]} {cx:.6f} {cy:.6f} {bw:.6f} {bh:.6f}") return lines

画框校验的代码很简单,核心是确认归一化坐标乘以原图宽高后,能得到与xml差不多的绝对坐标,误差在1个像素以内才算对。

4.5 坑五:验证集划分不随机,导致结果虚高

现象:模型在验证集上mAP0.9,但换一批同分布的野外照片马上掉到0.5。

原因:有人把数据划分脚本写成了按字母排序后直接切前80%,结果前1800张全是上午拍摄的图片,后边全是傍晚,模型只学到了光照特征。

解决:按文件名做哈希分桶,而不是按顺序切分。如果数据集有多个子目录,保证划分后每个子目录在训练和验证里的比例接近原始比例。

5. 进阶:在复杂野外背景下把滑坡检测压到一个可用置信度

5.1 数据增强策略:Mosaic要慎开,不是所有场景都适合

泥石流滑坡这类场景里,目标往往和背景融为一体。Mosaic增强会把四张图拼接在一起,小目标被进一步缩小,而滑坡区域周围的岩石纹理被裁剪掉,反而破坏了模型对整体地貌的感知。我的经验是:前20个epoch关掉Mosaic,让模型看一眼尽量完整的滑坡区域;20个epoch之后开启Mosaic,增强模型的尺度鲁棒性。

# ultralytics的增强超参,在hyp.yaml里调整 mosaic: 0.5 # 训练到一半再启用,前20个epoch手动设为0 mixup: 0.2 # 混合两张图,对小目标有轻微提升 copy_paste: 0.3 # 把标注框复制到另一张图上,适合稀疏样本

这里的逻辑是:mosaic在前几个epoch会把大目标的上下文切断,模型容易学到“有碎石纹理就是滑坡”的偏置。等骨干网络稳定后,再让mosaic发挥作用。如果训练时发现验证集mAP50比mAP50-95高很多,说明定位不够准,建议关掉mixup,因为mixup会生成虚假的边界,干扰回归分支。

5.2 迁移学习:冻结backbone还是全量微调

回这个数据集,2262张图不算多,从零训练yolov8n会欠拟合。正确的做法是用coco预训练权重做迁移学习,但冻结策略有讲究。泥石流和滑坡这两个类别在自然图像里基本没有对应的语义先验,反而是底层纹理特征(岩石、土壤、植被)能迁移过来。所以冻结前3个星阶段的backbone,只训练neck和head,一般比全量微调更稳。

# 冻结前10层,只训后面 yolo detect train \ data=datasets/data.yaml \ model=yolov8n.pt \ epochs=80 \ freeze=10

freeze=10的意思是把backbone的前10层参数锁死不动。如果你用的是yolov8s或更大的模型,freeze可以放到15。观察训练日志,如果前10个epoch里cls_loss不降,说明冻结太多或者类别特征和新模型不匹配,这时候减少冻结层数到5再试。如果用yolov8n这种轻量级骨架还出现严重过拟合,建议直接换yolov8m,参数量翻倍但在这个数据规模下不容易崩。

5.3 推理后处理:用置信度阈值和IoU去重压误检

模型训完后,推理时的输出阈值需要单独调。默认置信度0.25对泥石流场景太宽松,因为岩石纹理的误检分数经常在0.2到0.4之间。我一般会把置信度阈值调到0.4,同时把NMS的IoU阈值从默认的0.45上调到0.5,避免两个相邻的框把同一个滑坡区域覆盖两次。

from ultralytics import YOLO model = YOLO("runs/debris_flow/exp1/weights/best.pt") results = model.predict( source="test_images/", conf=0.4, iou=0.5, imgsz=1280, save=True, )

推理时imgsz要和训练时保持一致,否则小目标的检测率会明显下跌。如果conf调到0.4后漏检严重,用混淆矩阵确认漏检的框是原本分数就低于0.4还是一直没检出。前者说明阈值调得过高,后者说明模型本身就没学会这个特征,需要回训练阶段补样本。

6. 收尾:一个可以随时回放的滑坡检测验证习惯

最后分享一个我自己的习惯:拿到任何目标检测数据集,训完之后不要只看mAP就交差。在这个泥石流滑坡数据上,我强烈建议每训一个版本,都固定抽20张验证集图片做人工过目。不是看框准不准,而是看模型的失败模式有没有变化——比如上一版把所有碎石滩误检成泥石流,这一版是否还在同样位置误检。把每次误检截图按“误检类别”存档,迭代三个版本后,你就能大概摸清这个数据集里哪一类样本是含金量最高的,哪一类样本是噪声。

具体操作是写一个简单的可视化脚本:读验证集的标注框,叠加模型预测框,把置信度大于0.4但类别判断错误的样本输出到一个文件夹里。每天训练跑完后翻一下这个文件夹,基本上10分钟就能判断这个版本是进步还是退步。

import cv2 from pathlib import Path img_dir = Path("val_images") pred_dir = Path("runs/detect/exp/predict") out_dir = Path("hard_examples") out_dir.mkdir(exist_ok=True) for img_path in img_dir.glob("*.jpg"): pred_path = pred_dir / img_path.name if not pred_path.exists(): continue img = cv2.imread(str(img_path)) pred_img = cv2.imread(str(pred_path)) diff = cv2.absdiff(img, pred_img) if diff.mean() > 30: # 简单用图像差异筛选可疑样本,实际可用标注对比 cv2.imwrite(str(out_dir / img_path.name), pred_img)

这套习惯帮我少走了很多弯路。有一版模型在mAP上比上一版涨了1.5个点,结果人工一看,新增的检出全是树荫下的碎石堆,属于这个数据集里经典的noisy label区域。如果没有这个逐图过目的习惯,这个模型就会带上一个在实用场景里很致命的系统性误检。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询