简介目标检测数据集聚焦泥石流与滑坡两类地质灾害场景面向需要训练YOLO、Faster R-CNN等检测模型的算法工程师、研究生及防灾减灾研究人员。数据集以VOC与YOLO双格式组织JPEGImages、Annotations、labels三个文件夹一一对应共含2262张清晰现场图像标注框总数为6508个其中landslide类6079个、Debris-flow类429个适合滑坡隐患识别、灾后快速评估等任务模型训练与精度验证。压缩包共2000个文件文件主体为XML标注文件另附一个说明性txt整体约166MB便于下载与离线使用目前已有653人学习浏览。数据集未做增强处理全部矩形框标签经人工核对解压后可直接接入YOLO或VOC流程也可借助脚本快速转换为其他检测框架格式尤其适合中高级算法工程师动手实践对需要快速搭建滑坡监测识别流程的团队可显著减少数据整理耗时。1. 泥石流滑坡目标检测2262 张真实样本、YOLOVOC 双格式拿到手先别急着开训泥石流和滑坡检测在目标检测里属于典型的小众场景主流开源数据集几乎都围绕 COCO 那 80 类人和车占了绝大多数。真要做地质灾害预警常用的做法只能是自己去现场或遥感图里找样本再手动标注一张图来回折腾半小时很正常。这个「泥石流滑坡数据集 2262 张 YOLOVOC 格式」的价值在于它把 2262 张真实泥石流、滑坡场景图片和对应标注框打包好了并且同时给出 YOLO 的 txt 和 VOC 的 XML 两套标签省去最枯燥的格式转换环节。适合正在做地质灾害监测的算法工程师、滑坡预警系统开发或者只是想要一个非 COCO 场景练手目标检测的研究生。我的建议是别拿到手直接训练先把两套标签的格式和目录对应关系核对清楚这个步骤能避免后面换工具链时的大量返工。2. 数据格式解剖YOLO 的 txt 与 VOC 的 XML 如何描述同一个目标框这个数据集最容易被忽略、也最值得先看的地方就是双格式标注。YOLO 系列训练工具需要 txt而很多标注软件、开源标注平台和论文复现代码里用的是 VOC XML。你只有在训练前把这两套标签的对应关系吃透之后做迁移训练、转 COCO、接 TensorRT 部署时才不会两眼一抹黑。2.1 目录结构与同名映射先把三份文件对齐解压后常见的目录结构是这样的你可以把它当作默认参考泥石流滑坡数据集2262张YOLOVOC格式/ ├── images/ # 2262 张 JPEG 图片 │ ├── img_0001.jpg │ ├── img_0002.jpg │ └── ... ├── labels/ # YOLO 格式 txt │ ├── img_0001.txt │ ├── img_0002.txt │ └── ... ├── annotations/ # VOC 格式 XML │ ├── img_0001.xml │ ├── img_0002.xml │ └── ... ├── classes.txt # 类别清单 └── data.yaml # YOLO 训练配置我一般拿到压缩包后不会直接解压训练而是先跑一个最简单的同名映射检查images 下的每张 jpg必须能在 labels 里找到同名 txt在 annotations 里找到同名 xml。文件名不要求完全相同后缀但主体名必须一致比如img_001.jpg对应img_001.txt和img_001.xml。这一步看起来多余实际很关键因为后面训练时 YOLO 是根据图片路径去推断标签路径文件名对不上标签就会被静默跳过表现为“训练正常但模型什么都没学到”。如果解压出来的目录命名不完全一致不用慌。你只需要弄清楚谁和谁配对然后写个脚本做软链接或重命名。我遇到过标注平台导出时给图片加了前缀、标签没加的情况花十几分钟写个批量改名脚本比后面排查一个空的验证集损失要划算得多。2.2 YOLO 标注 txt 的解析类别、中心点、宽高YOLO 的 txt 标注格式不长每行表示一个目标框标准结构是五列类别 ID、中心点 x、中心点 y、宽度 w、高度 h。注意后面的四个数值全部是相对图片宽高的比值范围应该在 0 到 1 之间。拿头部几个文件看一眼import os label_dir labels for filename in sorted(os.listdir(label_dir))[:5]: path os.path.join(label_dir, filename) with open(path, r, encodingutf-8) as f: lines f.read().strip().splitlines() print(f{filename} - {len(lines)} 个目标) for line in lines: parts line.split() cls int(parts[0]) # 类别 ID从 0 开始 cx, cy, w, h map(float, parts[1:5]) print(f class{cls} center({cx:.3f}, {cy:.3f}) size({w:.3f}, {h:.3f}))这段代码逻辑很简单但价值在于快速确认两点第一类别 ID 是不是从 0 开始且连续第二坐标是否真的落在 [0,1] 区间。如果出现某个 w 或 h 大于 1说明标注导出时图片尺寸算错了这类坏标签进训练集会拉低整个模型的框回归质量。这个数据集如果只有一个滑坡类别那所有 txt 的第一列应该都是 0。如果出现 0 和 1 混用就需要对照 classes.txt 确认到底是泥石流、滑坡分开标还是只标了一个总类。不要在代码里猜类别数量训练前打开 classes.txt 看一眼是最稳的。2.3 VOC XML 标注绝对坐标和图片尺寸是对账凭证VOC 的 XML 里把信息拆成了 filename、size、object 三部分。size 里记录图片真实宽高object 里每个目标有一个 bndbox用 xmin、ymin、xmax、ymax 四个绝对像素坐标表示框的位置。解析方式很固定import xml.etree.ElementTree as ET xml_path annotations/img_0001.xml tree ET.parse(xml_path) root tree.getroot() width int(root.find(size/width).text) height int(root.find(size/height).text) print(f图片尺寸: {width} x {height}) for obj in root.findall(object): name obj.find(name).text bndbox obj.find(bndbox) xmin float(bndbox.find(xmin).text) ymin float(bndbox.find(ymin).text) xmax float(bndbox.find(xmax).text) ymax float(bndbox.find(ymax).text) print(f类别: {name}, 框: ({xmin:.1f}, {ymin:.1f}, {xmax:.1f}, {ymax:.1f}))从 VOC 转 YOLO 时核心就三步中心点 x 等于 (xmin xmax) 除以 2 再除以图片宽度中心点 y 同理除以高度框宽等于 (xmax - xmin) 除以宽度框高除以高度。反过来从 YOLO 转 VOC 就是做乘法并还原成整数坐标。我拿到双格式数据集一定会用 XML 反推一遍 YOLO 坐标再和同名的 txt 做对比误差超过 0.01 就标记出来。因为有些数据集打包时图片被统一缩放或裁剪过但 XML 里的尺寸节点还是旧值即便肉眼看不出来训练后会表现为检测框整体偏移。这个“对账”动作十分钟能做完却能排除掉最隐蔽的标签不一致问题。3. 开始训练8:1:1 划分数据并在 YOLOv8 里跑通完整链路确认标签格式没问题之后下一步就是把 2262 张图片划分成训练集、验证集、测试集然后接进 YOLOv8 训练。这里我用的划分比例是 8:1:1背后考虑是泥石流场景图片之间可能来自同一条沟、同一片坡相似背景较多验证集如果太小评估指标的波动会非常大。8:1:1 属于中性偏保守的比例既保证训练样本量又能让 val 的统计方差小一点。3.1 用脚本划分数据集固定随机种子import os import shutil import random random.seed(42) images sorted(os.listdir(images)) random.shuffle(images) total len(images) train_cnt int(total * 0.8) val_cnt int(total * 0.1) splits { train: images[:train_cnt], val: images[train_cnt:train_cnt val_cnt], test: images[train_cnt val_cnt:], } for split_name, split_images in splits.items(): img_dir os.path.join(split_name, images) lab_dir os.path.join(split_name, labels) os.makedirs(img_dir, exist_okTrue) os.makedirs(lab_dir, exist_okTrue) for img_name in split_images: shutil.copy(os.path.join(images, img_name), os.path.join(img_dir, img_name)) label_name os.path.splitext(img_name)[0] .txt src_label os.path.join(labels, label_name) if os.path.exists(src_label): shutil.copy(src_label, os.path.join(lab_dir, label_name)) else: print(f警告: 缺失标签 {label_name})脚本里有三个点值得说明。第一random.seed(42)保证每次划分结果一致这样你在调整超参数时val 和 test 的组成不会变化实验对比才有意义。第二train_cnt用 int 取整2262 张按 0.8 算出来是 1809剩下 453 张再对半分给 val 和 test总样本覆盖比较均匀。第三缺失标签时打印警告而不是直接跳过是为了让问题暴露在训练之前。划分完成后再做一件小事分别统计 train、val、test 三个子目录下的 jpg 数量和 txt 数量必须完全一致。这一步能挡住“图片进了训练集、标签留在原目录”的低级失误。3.2 编写 data.yaml 并选择训练超参数YOLOv8 的训练入口会读一个 YAML 配置文件。这个数据集如果是单类别data.yaml 内容大致是path: /home/user/landslide_dataset train: train/images val: val/images test: test/images nc: 1 names: 0: debris_flowpath建议写成绝对路径避免 YOLO 相对路径解析的歧义train和val指向划分后图片所在目录YOLO 会自动在相同父目录下找 labels也就是说 train 的同级 labels 目录必须存在。names里的 0 对应 txt 中第一列的类别 ID顺序不能乱写nc必须和 names 数量一致。训练命令我通常这样起yolo detect train \ modelyolov8s.pt \ datadata.yaml \ epochs100 \ imgsz640 \ batch16 \ device0 \ projectruns_detect \ namedebris_flow_01选择yolov8s.pt而不是 nano 或 large是平衡时间和精度的结果。泥石流滑坡的框通常比较大且边缘模糊s 模型的感受野和参数量足够n 模型跑得快但边框回归精度会差一点l 模型对显存要求高单卡训练一轮的时间翻倍。imgsz640是默认值如果你的显卡显存大于 16G建议提到 960对滑坡这类背景纹理复杂的场景有小幅精度增益。batch16对应 8G 显存左右的单卡显存不够就先降到 8 或 4不要硬撑。训练过程中重点盯两个输出第一个是BoxLoss和ClsLoss它们应该在前面几个 epoch 快速下降后面慢慢收敛第二个是每个 epoch 末尾的验证指标如果 val 的 mAP50 在某个点开始停滞说明模型容量或者数据增强已经到极限续训不会有太大变化。YOLOv8 默认保留best.pt和last.pt分别按验证集最优和最后一步保存。3.3 用 best.pt 做测试集评估训练结束后先不要急着接视频流在测试集上验证一轮from ultralytics import YOLO model YOLO(runs_detect/debris_flow_01/weights/best.pt) metrics model.val(datadata.yaml, splittest) print(mAP50:, metrics.box.map50) print(mAP50-95:, metrics.box.map)这段代码加载训练好的权重在划分时单独的 test 目录上跑验证。metrics.box.map50是 IoU 阈值 0.5 时的平均精度metrics.box.map是 0.5 到 0.95 的平均值。对泥石流检测来说前者低于 0.75 基本不可用后者受标注框边缘模糊影响低一些可以接受。也可以直接出可视化结果用统一的 conf 阈值跑一遍测试集推理yolo predict \ modelruns_detect/debris_flow_01/weights/best.pt \ sourcetest/images \ conf0.3 \ saveTrueconf0.3是起步值。泥石流检测场景里误报和漏报的代价完全不同后续选阈值应该结合 val 的 Precision-Recall 曲线如果业务偏向预警宁可误报就把 conf 降到 0.2偏向精准告警就升到 0.45。4. 避坑泥石流检测数据集从加载到训练防不胜防的五个坑这类真实灾害数据集的坑比标准数据集多很多不是代码写错而是数据本身带着历史包袱。下面五条是我复现数据集时踩过或见过别人踩的典型问题每一条都可以直接用来自查。4.1 空 txt 导致 loss 异常、mAP 全程为零现象训练正常启动没有报错但每个 epoch 的 loss 都很低验证时检测目标数为 0mAP 始终是 0。原因labels 目录里有空文件或者图片名和标签名不匹配数据加载器把这类图片当成无目标样本。模型把所有区域都预测成背景但训练 loss 照样能下降因为背景预测对了。解决训练前扫描一次空文件。import os empty_count 0 for root, _, files in os.walk(labels): for f in files: path os.path.join(root, f) if os.path.getsize(path) 0: print(空标签:, path) empty_count 1 print(空标签总数:, empty_count)空文件对应的图片要么删掉要么人工补标。千万不要只删 txt 不删图片那会让模型把该区域当背景负样本后续再补标训练时模型特别难“回心转意”。4.2 data.yaml 的类别数量写错训练直接罢工现象训练日志里出现IndexError: index 0 is out of bounds for axis 0 with size 0或者类别 loss 一直是 NaN。原因data.yaml 里写了nc: 2names 也列了两个名字但标签文件里所有目标第一列都是 0也就是实际只有一个类别。模型检测头初始化了两个类别分支却拿不到第二个类别的正样本反向传播异常。解决先cat classes.txt确认数据集到底几个类别然后让 yaml 跟它严格同步。比如 classes.txt 里只有debris_flow一行就写成nc: 1。换预训练权重时不用担心类别数变化YOLO 会自动重新初始化检测头。4.3 YOLO txt 和 VOC XML 坐标不一致框整体偏移现象同一张图片用模型推理出来的框和原标注位置差一大截但数值上又没报错。原因数据集在制作时图片被统一缩放或裁剪过但 XML 里的 size 节点还保留原始尺寸或者 YOLO txt 生成时除以了错误的宽高。这种错误在 mAP 里会被大幅惩罚但我见过因为移位方向一致mAP 只掉了零点零几的情况更要命的是一换到别的数据集精度的伪装。解决用第 2 章里的 XML 解析脚本反推 YOLO 坐标和 txt 逐行比较误差超过 0.01 就重新生成。同时检查 XML 的 size 是否和实际图片的像素宽高一致不一致就是当时打包出问题的最直接证据。4.4 图片尺寸差异过大显存溢出或训练中断现象训练到某个 epoch 突然报 CUDA OOM但 batch 和 imgsz 都是常规设置。原因真实灾害数据里混了无人机航拍的大图和手机拍的竖图有的长边超过 4000 像素有的只有 640。YOLOv8 在训练时会把一个 batch 的图片 pad 到统一尺寸超大图会把显存峰值瞬间顶满。解决先用 PIL 扫描全部图片的长宽分布再决定 imgsz 和 batch。from PIL import Image import os max_w 0 max_h 0 for img_name in os.listdir(images): img Image.open(os.path.join(images, img_name)) w, h img.size max_w max(max_w, w) max_h max(max_h, h) print(最大宽:, max_w, 最大高:, max_h)如果最大长边超过 1600我一般会写脚本把长边统一重缩放到 1280 或 1600短边按比例缩放缩放之后重新核对标签。直接调低 batch 也能临时解决但超大图送入网络后下采样信息损失比较严重还是统一尺寸更稳。4.5 标注框过小或宽高比极端指标好看但实际漏检现象mAP50 有 0.8 以上但把模型接到实际视频里大片滑动面漏检只框出来一小块碎石区。原因部分标注框只覆盖了滑坡滑动带的局部或者框是特别窄的长条宽高比到 1:10 甚至更大。YOLOv8 在 640 分辨率下经过 32 倍下采样只有十几个像素宽的框特征几乎被抹掉训练时模型没机会学到完整形态。解决先统计框的宽高分布。用 Python 读所有 txt计算每个框的面积和宽高比凡是 w/h 或 h/w 超过 8 的框返回原图人工确认是否框得太局部。同时可以考虑训练时把 imgsz 提到 960让窄长目标保留更多像素。真实部署时也可以配合滑窗或 SAHI 这类切图推理用重叠窗口把大图切成多个小块分别检测小目标漏检率会明显下降。5. 进阶把泥石流检测从单张图片扩展到视频帧并做伪标签增量学习训练出 best.pt 只是第一步真实项目里输入往往是无人机航拍视频或者固定点位监控流。把单张推理改成连续视频推理再加一层时序平滑和伪标签回灌才算把模型变成一个可用的预警模块。5.1 用 OpenCV 逐帧读取并推理最常见做法是用 OpenCV 读视频帧逐帧喂给模型再写回视频。import cv2 from ultralytics import YOLO model YOLO(runs_detect/debris_flow_01/weights/best.pt) cap cv2.VideoCapture(field_video.mp4) fps cap.get(cv2.CAP_PROP_FPS) width int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)) height int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)) out cv2.VideoWriter(field_video_out.mp4, cv2.VideoWriter_fourcc(*mp4v), fps, (width, height)) while cap.isOpened(): ret, frame cap.read() if not ret: break result model.predict(frame, conf0.35, imgsz640)[0] out.write(result.plot()) cap.release() out.release()conf0.35在视频场景下会比测试集高一些因为单帧误报可以被时序平滑兜住不用为了召回拼命压低阈值。5.2 用指数平滑做时序去抖视频里真正的泥石流不会只出现一帧而是一段时间内连续出现。用指数移动平均对置信度做平滑能过滤掉相机抖动、飞鸟、临时碎石造成的单帧假阳。ema 0.0 alpha 0.3 hit_frames 0 for result in frame_results: if len(result.boxes) 0: ema * 0.7 hit_frames max(0, hit_frames - 1) continue conf float(result.boxes.conf.max()) ema alpha * conf (1 - alpha) * ema hit_frames 1 if ema 0.4 and hit_frames 5: print(连续检测到泥石流触发预警)alpha越大模型对当前帧越敏感越小平滑越强。地质灾害预警宁可慢半拍也不能疯报警我会偏向alpha0.25。5.3 用新场景图片做伪标签增量学习真实场景的多样性永远超过数据集。我会用 best.pt 对新采集的图片批量生成伪标签人工抽查后再并入训练集。yolo predict \ modelruns_detect/debris_flow_01/weights/best.pt \ sourcenew_scenes \ conf0.45 \ save_txtTrue \ save_confTrue生成的 txt 在runs_detect/predict/labels里人工审核时重点看 conf 在 0.45 到 0.6 之间的样本这些最可能是难例conf 高于 0.7 的可以放心采用。伪标签只能做数据扩充不能全自动落库我一般至少抽查 30%。从那以后我每次拿到新的灾害类数据集都强制把标签合法性校验放到第一步空 txt 扫描、同名映射检查、XML 反推坐标对账、图片尺寸分布统计四件事全过一遍才允许开训。这个小习惯帮我少走了很多弯路希望也能帮到你避开同样的坑。本文还有配套的精品资源点击获取