
简介该数据集面向泥石流与滑坡目标检测任务适合从事地质灾害监测、遥感图像智能解译的算法工程师及科研人员使用。压缩包内完整提供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 双格式数据集的典型文件结构与配套资源拿到一个YOLOVOC双格式的数据集压缩包第一件事不是急着训模型而是先摸清文件组织方式。常见的做法是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 folderimages/folder filenameimg_001.jpg/filename size width1920/width height1080/height depth3/depth /size object namelandslide/name bndbox xmin320/xmin ymin400/ymin xmax750/xmax ymax700/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(fdatasets/{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必须等于2names的顺序必须和YOLO标注文件里的class_id一一对应。如果搞反了泥石流和滑坡的标签就互换了训练时loss照样下降但推理结果完全错位。验证方法很简单找一张标注图用python读txt第一个数字再去xml里查一下对应的类别名。3.3 启动训练并读懂前10个epoch的日志环境装好ultralytics之后训练命令尽量用显式参数不要依赖默认值。我习惯把epochs、imgsz、batch三个参数写到命令里方便后边改。yolo detect train \ datadatasets/data.yaml \ modelyolov8n.pt \ epochs100 \ imgsz640 \ batch16 \ lr00.01 \ patience15 \ projectruns/debris_flow \ nameexp1前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(datadatasets/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 \ datadatasets/data.yaml \ modelyolov8n.pt \ epochs80 \ freeze10freeze10的意思是把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( sourcetest_images/, conf0.4, iou0.5, imgsz1280, saveTrue, )推理时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_okTrue) 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区域。如果没有这个逐图过目的习惯这个模型就会带上一个在实用场景里很致命的系统性误检。希望帮到你。本文还有配套的精品资源点击获取