
简介面向遥感图像分析与地质灾害监测领域的研究人员和工程师这份源码包提供了一个基于PyTorch框架的CNN滑坡识别完整方案借助卷积网络自动提取影像特征有效替代传统人工设计特征的方法提升滑坡区域定位与分类的准确性。压缩包共122个文件包括18个Python源码模型结构、训练器、数据加载等、96个XML标注文件对应遥感影像标签、6个文本说明、1个Markdown项目文档和1个字体文件整体大小仅4.93MB结构清晰。已有60人学习下载。包内置训练好的模型权重无需从零训练即可直接对遥感影像进行推理配套文档详细阐述模型设计原理、数据集组织与训练评估流程便于研究人员快速复现、二次开发或迁移至类似地质灾害识别场景兼顾科研与工程落地需求。1. 遥感图像滑坡识别一个CNNPyTorch项目能解决什么拿到这个标题的第一反应是“又有人把深度学习项目打包成压缩包卖课了”但真打开需求看会发现滑坡识别这件事比想象中实际得多。无论是地质灾害调查、灾后应急评估还是公路铁路沿线的隐患巡查遥感图像里的滑坡体识别都是刚需。过去靠人工目视解译一个县域的影像够一个小团队看一周而基于深度学习CNN网络配合PyTorch框架训练好的模型可以在几分钟内完成同样范围的初筛。这个压缩包的意义就是让从业者不必从零开始——源码告诉你网络怎么搭数据集告诉你标签长什么样训练好的模型让你跳过GPU等待期直接跑推理。这个方向适合两类人一是做遥感或GIS项目的工程师想快速在业务里验证AI识别的可行性二是深度学习入门者已经跑通过MNIST或CIFAR分类想看看CNN在真实遥感场景里怎么落地。滑坡识别本质上是一个语义分割任务不是目标检测这个认知会在后面反复影响网络选型、损失函数和后处理策略。接下来我们从模型原理、数据组织、训练参数到避坑清单把整个项目的技术链路拆开讲。2. 遥感滑坡识别的技术选型为什么是CNNPyTorch而不是其他组合2.1 滑坡在遥感图像里到底是“检测”还是“分割”标题写的是“遥感图像滑坡识别”但识别这两个字在实际工程里有两个完全不同的技术路径。目标检测输出的是包围盒告诉你哪里有滑坡语义分割输出的是像素级掩膜告诉你滑坡的精确边界。滑坡体形状极不规则狭长、分叉、被植被部分覆盖一个矩形框会同时框进大量非滑坡区域下游做面积统计或灾损评估时误差会非常大。因此这个项目几乎必然走语义分割路线网络输出和标签都是与输入图像同尺寸的掩膜图。语义分割的另一个好处是能直接对接GIS工作流。分割结果转成GeoJSON或TIFF后可以在ArcGIS或QGIS里叠加到原始影像上做专题图甚至进一步做体积估算。如果走目标检测你拿到的只是几个框还得再做一次裁剪和二次分类才能回到业务。这个区别是选型的第一步也决定了后面所有代码的写法。2.2 CNN vs Transformer vs 传统机器学习为什么CNN依然是默认解近两年Transformer在视觉领域声势很大但遥感滑坡识别这个任务有一个现实约束数据量有限。滑坡标注数据集的规模通常在几千张量级而视觉Transformer动辄需要百万级数据预训练才能发挥优势。CNN的归纳偏置——局部连接和权值共享——让它在小数据集上天然更容易收敛。PyTorch里torchvision提供的预训练ResNet、EfficientNet权重可以直接复用迁移学习的效率远高于从零训练一个ViT。从推理部署角度看CNN结构简单ONNX导出后可以跑在CPU上适合应急场景下在笔记本上快速出图。Transformer结构则在序列长度和注意力矩阵上带来额外计算开销。不是说CNN永远优于Transformer而是对“标好几千张图、训练一版模型、部署到业务里”的典型项目CNN是性价比最高的起点。DeepLabV3、UNet这些经典结构在滑坡识别文献里已经验证充分踩坑资料也最多遇到问题更容易搜到解决方案。2.3 PyTorch环境搭建的具体参数PyTorch是这套方案的首选框架原因很直接动态计算图方便调试TorchVision预训练模型一键加载配合Albumentations做遥感图像增强生态成熟。环境搭建有个常被忽略的分叉点——CUDA版本和PyTorch版本的匹配。跑滑坡识别这种分割任务batch size至少要8才能稳住训练所以GPU显存8GB起步是硬底线。conda create -n landslide python3.9 conda activate landslide pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118 pip install albumentations opencv-python rasterio tqdm scikit-learn--index-url指定CUDA 11.8的预编译包这是目前兼容性最稳的组合。NVIDIA驱动版本较新的机器可以尝试CUDA 12.1但没必要追新驱动向下兼容。Python版本用3.9而不是3.10或3.11是因为Albumentations和Rasterio在3.9下的wheel包最全省去编译源码的时间。安装完可以用python -c import torch; print(torch.cuda.is_available())验证GPU可用性。3. 遥感图像标注与数据集组织训练集、验证集、测试集的划分规则3.1 遥感图像标注要点滑坡的边界到底怎么画标注质量是整个项目的地基。遥感图像里滑坡体在影像上的表现主要是色调突变和纹理错乱——滑坡后裸露的土体和岩石在可见光下呈灰白或棕黄色和周围绿色植被对比明显。标注时最容易犯的错是把滑坡的“影响区”也画进去比如滑坡体前缘的堆积物和淤塞的河道。严格来说标注只需要勾画滑体本身用多边形工具贴着影像上的色调边界走锯齿是允许的反而更真实。数据量方面滑坡识别和猫狗分类不同一个滑坡体在一张512×512的图里可能只占几百个像素属于小目标分割。训练集至少要准备2000张以上带掩膜的图像如果原始数据不足就要靠裁剪和增强扩充。标注好的标签是单通道PNG或TIFF像素值为0和10代表背景1代表滑坡。注意不要用RGB三通道的彩色标签图那会让交叉熵损失函数把每个通道当独立类别去算。3.2 训练集/验证集/测试集的目录规则数据组织按图像分割任务的标准结构来别把训练集和测试集混在一个目录里。滑坡影像往往存在空间自相关性同一区域相邻瓦片的特征高度相似如果随机划分模型会在验证集上虚高实际部署时断崖下跌。常见的做法是按“区域”划分而不是按“文件”划分——也就是说来自同一景影像的裁剪瓦片必须全部进入同一个子集。./data/ ├── train/ │ ├── images/ │ │ ├── img_001.tif │ │ └── ... │ └── masks/ │ ├── img_001.png │ └── ... ├── val/ │ ├── images/ │ └── masks/ └── test/ ├── images/ └── masks/80/10/10是一个合理的划分比例。滑坡识别是异质类别问题滑坡区域在整幅影像中占比通常低于10%所以训练集和验证集都要检查正样本像素占比如果某张图里完全没有滑坡体混进验证集里只会干扰IoU的读数。遥感图像的原始格式建议保留TIFF因为部分高分影像只有TIFF才完整保留辐射分辨率裁剪成PNG后做推断没问题但训练过程中如果要做辐射归一化原始格式更可靠。3.3 Dataset类代码实现与参数说明PyTorch的Dataset类把读图、读掩膜、增强封装在一起这是图像分割项目里最值得花时间打磨的模块。遥感影像通常带4个波段R、G、B、NIR但预训练模型只有3通道输入这里有三种处理方式丢掉NIR只用RGB、取RGB三个波段合成、用PCA将4波段压缩成3通道。最常见的是直接用RGB三波段把NIR波段留给后续做多光谱融合的进阶实验。import torch from torch.utils.data import Dataset import cv2 import numpy as np import albumentations as A class LandslideDataset(Dataset): def __init__(self, image_dir, mask_dir, transformNone): self.image_paths sorted(list(image_dir.glob(*.tif))) self.mask_paths sorted(list(mask_dir.glob(*.png))) self.transform transform def __len__(self): return len(self.image_paths) def __getitem__(self, idx): image cv2.imread(str(self.image_paths[idx]), cv2.IMREAD_COLOR) image cv2.cvtColor(image, cv2.COLOR_BGR2RGB) mask cv2.imread(str(self.mask_paths[idx]), cv2.IMREAD_GRAYSCALE) mask (mask 0).astype(np.float32) if self.transform: augmented self.transform(imageimage, maskmask) image augmented[image] mask augmented[mask] image torch.from_numpy(image.transpose(2, 0, 1)).float() / 255.0 mask torch.from_numpy(mask).unsqueeze(0).float() return image, mask这段代码有几个关键点需要说明。掩膜读取后做了一次(mask 0)的二值化因为不同标注工具的PNG背景像素值可能不是0比如黑色背景在实际存储时是0但某些GIS导出工具会加上NoData值255二值化逻辑会规避这个来源不明的数值偏移。unsqueeze(0)把掩膜从[H, W]扩展为[1, H, W]适配BCEWithLogitsLoss对目标张量的维度要求。图像归一化直接除以255不额外做标准化因为ImageNet预训练权重吃的是这个尺度的输入。3.4 数据增强遥感场景下哪些增强有效增强策略直接决定模型的泛化能力。遥感图像的增强和平常做ImageNet分类的增强有显著差异——水平翻转和垂直翻转都有效因为卫星影像没有“上下”概念随机旋转建议用90度的倍数避免插值导致的滑坡边界变形。色彩抖动在遥感场景下要慎用色相偏移会破坏植被和裸土的区分度合理的做法是只做亮度对比度微调。Albumentations的组合配置如下train_transform A.Compose([ A.RandomRotate90(p0.5), A.HorizontalFlip(p0.5), A.VerticalFlip(p0.5), A.RandomBrightnessContrast(brightness_limit0.1, contrast_limit0.1, p0.3), A.CropAndPad(percent(0.0, 0.2), pad_mode0, p0.3), ])验证集不用增强只做尺寸调整到网络输入大小。CropAndPad是对滑坡识别最友好的增强之一它模拟了滑坡体在整幅影像中位置漂移和尺度变化的情况。需要注意的是验证和测试时需要确保滑窗推理与训练时的输入尺寸一致否则模型在边界处的感受野不同会直接影响分割精度。4. 模型训练CNN网络结构选择、损失函数与训练参数详解4.1 从UNet到UNet滑坡分割模型的选型依据滑坡识别的主流基线是UNet家族。UNet的编码器-解码器结构配合跳跃连接让低层的高分辨率特征能直接传递给解码器这对细小滑坡边界的保留至关重要。在遥感场景中一个1500像素宽的滑坡体算大块头但滑坡边缘的羽状裂隙往往只有几个像素宽——跳跃连接是保住这些边缘信息的核心机制。更具体的选型对比可以看下面这张表模型结构参数量M特点适合场景UNet原始31结构简单训练稳定数据量小、快速基线UNet36密集跳跃连接边缘更细滑坡边界复杂的精细分割DeepLabV363ASPP多尺度感受野滑坡体尺度变化大的数据集DeepLabV3MobileNetV2骨干15轻量推理快CPU部署、应急出图实践中最稳的配置是ResNet50作骨干的DeepLabV3或经典UNet。前者在滑坡尺度差异大的场景下表现更好因为ASPP模块用多空洞率并行提取多尺度特征。ResNet50预训练权重的迁移效果在大多数遥感分割任务里都被验证过。UNet从零训练也能收敛但会慢很多。4.2 损失函数为什么滑坡识别不能用纯交叉熵滑坡识别是极度不平衡的分类问题。一张512×512的图像里滑坡像素可能只有2000个占不到0.8%。用纯交叉熵训练时模型把所有像素预测为背景训练损失依然在下降因为背景类占了99%以上但模型实际什么都没学会。这种现象在滑坡识别里极其普遍新手最容易在这个坑里折腾几天。解决方案是Dice损失与交叉熵的加权组合。Dice度量的是预测区域与真实区域的重叠度对正负样本比例不敏感import torch.nn.functional as F def dice_loss(pred, target, smooth1.0): pred torch.sigmoid(pred) intersection (pred * target).sum() return 1.0 - (2.0 * intersection smooth) / (pred.sum() target.sum() smooth) def combined_loss(pred, target): bce F.binary_cross_entropy_with_logits(pred, target) dice dice_loss(pred, target) return bce dicebce dice的组合让模型既要精确到像素级别BCE逐点计算又要关注区域重叠Dice从整体评估。smooth参数设置为1.0作用是避免预测和真实掩膜都为0时出现除零错误同时也起到平滑梯度的作用防止训练早期震荡。Dice损失的梯度在交叠区域方向更明确能快速把模型从“全预测背景”的陷阱里拉出来。4.3 训练主循环学习率、权重衰减与早停策略训练代码的核心是一个标准循环但参数设置要针对分割任务做调整。初始学习率用1e-4比分类任务的1e-3低一个量级因为分割任务的损失曲面更复杂过大的学习率会导致边界区域的预测在几个epoch内来回摇摆。优化器选AdamW而不是SGDAdamW对学习率的敏感度较低配合weight_decay能在不过度影响收敛速度的前提下压制过拟合——在滑坡数据集只有几千张的情况下L2正则化是必要的。import torch from torch.optim import AdamW from torch.optim.lr_scheduler import ReduceLROnPlateau model DeepLabV3Plus(num_classes1) optimizer AdamW(model.parameters(), lr1e-4, weight_decay0.01) scheduler ReduceLROnPlateau(optimizer, modemax, factor0.5, patience5, verboseTrue) best_iou 0.0 for epoch in range(50): model.train() train_loss 0.0 for images, masks in train_loader: images images.to(device) masks masks.to(device) optimizer.zero_grad() outputs model(images) loss combined_loss(outputs, masks) loss.backward() optimizer.step() train_loss loss.item() val_iou evaluate_iou(model, val_loader, device) if val_iou best_iou: best_iou val_iou torch.save(model.state_dict(), landslide_best.pth) print(fEpoch {epoch}: new best IoU {val_iou:.4f}) scheduler.step(val_iou)ReduceLROnPlateau的patience5意味着验证IoU连续5个epoch不提升才将学习率减半这比固定epoch衰减更适配实际训练情况。modemax指明监控指标是越高越好。每次IoU刷新时保存一次模型确保最后拿到的是验证集上最优的版本不是训练集上最后一个版本。早停在这里没有单独写但可以设置一个if epoch 30 and val_iou 0.3: break的条件来跳过注定失败的训练任务。4.4 加载训练好的模型做推理训练完成后压缩包里那份训练好的模型就是用来跳过训练环节的。推理代码需要和训练时的预处理完全对齐——同样的通道顺序、同样的归一化方式、同样的输入尺寸。滑坡识别推理还有一个遥感特有的操作多尺度预测验证。把输入图缩放到0.75、1.0、1.25倍分别推理将结果放大到原始尺寸后取平均。def inference(model, image_path, device): model.eval() image cv2.imread(image_path) image cv2.cvtColor(image, cv2.COLOR_BGR2RGB) image cv2.resize(image, (512, 512)) input_tensor torch.from_numpy(image.transpose(2, 0, 1)).unsqueeze(0).float() / 255.0 with torch.no_grad(): logits model(input_tensor.to(device)) prob torch.sigmoid(logits).squeeze().cpu().numpy() mask (prob 0.5).astype(np.uint8) return mask推理阈值0.5是一个默认值。在滑坡识别场景如果模型在验证集上的IoU达到0.6以上这个阈值基本可用。如果更看重召回率不想漏掉任何潜在滑坡体阈值可以降到0.3。如果更看重精确率减少误报用于人工复核阈值可以提到0.7。这个决策应该在项目交付前用验证集画Precision-Recall曲线来确定而不是凭感觉拍一个数字。5. 避坑遥感滑坡识别最常见的5个翻车现场5.1 训练Loss下降但IoU纹丝不动这是一个几乎每个做分割的人都会撞上的现象。训练损失曲线在10个epoch内稳定下降但验证集的IoU始终在0.2以下徘徊。原因在于损失函数里BCE分量的绝对值远大于Dice分量模型选择了“全预测背景”这条最简单路径来最小化BCE把Dice的引导作用淹没了。解决办法是先单独用Dice损失训练10个epoch让模型学到区域感知能力之后再切回组合损失微调。这个技巧在滑坡识别这类极不均衡的数据集上有奇效我验证过多次。5.2 训练好的模型在整景影像上推理时内存爆掉训练用512×512的瓦片没问题但实际业务里客户给的是12000×12000像素的整景卫星影像。直接整图推理即便16G显存也会OOM。正确做法是滑窗推理——将大图切成512×512或1024×1024的瓦片相邻瓦片之间重叠50个像素推理后取重叠区域的均值然后拼接。重叠的目的是消除拼接缝处的预测伪影因为模型在图片边缘的感受野不完整预测置信度偏低。滑动窗口的步长设置为窗口大小减重叠量按此遍历全图。5.3 验证集IoU很高但放到新区域完全不能用这大概率是数据划分泄露了。前面提过“按区域划分”而不是“按文件划分”但所谓区域的定义要具体到同一景影像的相邻瓦片。如果一景高分影像裁剪出了2000张瓦片这2000张必须同时进入训练集或验证集不能打乱后随机分配。相邻瓦片之间的滑坡纹理高度相似模型在验证集上看到的“新样本”其实和训练样本来自同一片山脉的同一侧IoU虚高是必然的。这是遥感数据与自然图像数据最本质的差异——空间自相关性。做数据划分时先按影像分块再在块内随机抽。5.4 滑坡边界预测成一团模糊的渐变带输出掩膜在滑坡边缘处不是锐利的0或1而是一片0.3到0.7的模糊区域。这个现象的直接原因是图像在标注时边缘就画得不齐模型学到了这种不确定性的平均值。解决思路有两个层面第一个是后处理用OpenCV的形态学开闭运算先开运算去除边缘碎点再闭运算填补内部空洞第二个是从标注层面解决如果项目条件允许重新检查滑坡边界标注是否足够贴合影像用DSTDistance Transform损失替代阈值二值化后的硬标签。实践中形态学后处理后IoU通常能提升1到2个点这是最低成本的优化。5.5 PyTorch环境装了CUDA却报版本不匹配很多人装完PyTorch后一执行GPU训练就报错常见现象是torch.cuda.is_available()返回False或者在import torch时直接抛出版本不匹配的异常。原因是NVIDIA驱动的CUDA版本和PyTorch编译时的CUDA版本不一致。PyTorch的预编译包对应CUDA 11.8或12.1而机器上驱动是CUDA 12.4的运行时理论上驱动向后兼容但有些环境会残留多个CUDA路径导致动态库加载错乱。最简单的排查方法是先nvidia-smi看驱动支持的CUDA版本再python -c import torch; print(torch.version.cuda)看PyTorch内部的CUDA版本两者只要驱动的数字大于等于PyTorch的基本兼容。如果还报错检查LD_LIBRARY_PATH环境变量是否指到了错误的CUDA路径清空后重试。6. 推理与评估用验证集算IoU别只盯着Loss曲线模型训练完毕最关键的步骤是用测试集算IoU并且只看IoU不看Loss。Loss是训练过程指标受损失函数组合方式影响不同配置下数值没有可比性IoU才是业务指标直接反映识别结果和真实滑坡区域的重叠程度。一个合格的滑坡识别模型IoU至少到0.55如果标注质量和数据量都过关0.65以上是可达的。def evaluate_iou(model, loader, device, threshold0.5): model.eval() iou_list [] with torch.no_grad(): for images, masks in loader: images images.to(device) masks masks.to(device) logits model(images) prob torch.sigmoid(logits) pred (prob threshold).float() intersection (pred * masks).sum(dim(1, 2, 3)) union (pred masks).gt(0).sum(dim(1, 2, 3)) iou intersection / union.clamp(min1e-6) iou_list.extend(iou.cpu().numpy().tolist()) return float(np.mean(iou_list))代码里union的计算用gt(0)代替简单的加法是为了避免预测和真实掩膜重叠区域被重复计入。clamp(min1e-6)防止某张图预测和掩膜全为0时出现除零。这个评估函数要在训练循环的每一个epoch后调用作为模型保存的判别依据。进阶验证方法是把预测掩膜转为矢量多边形叠加到原始影像上用专业眼光检查误报来源。最多见的误报是河道和裸露岩石被识别为滑坡——它们在影像上与滑坡有极高的光谱相似度。如果业务上不能容忍这种误报可以在后处理阶段用DEM坡度数据做过滤坡度低于15°的区域直接清零。这是一个非常实用且低成本的业务规则植入方式。转ONNX做部署是另一个值得做的验证步骤能让模型脱离PyTorch环境跑在纯TensorRT或ONNX Runtime上dummy_input torch.randn(1, 3, 512, 512).to(device) model.eval() torch.onnx.export(model, dummy_input, landslide.onnx, input_names[input], output_names[output], dynamic_axes{input: {0: batch}, output: {0: batch}})dynamic_axes允许推理时动态batch size这在批量处理多张瓦片时能明显提高吞吐。ONNX Runtime的推理速度比PyTorch原生推理快30%到50%且不依赖深度学习环境适合交付给只装了Python的客户。最后说一下我自己的习惯训练好的模型永远保留三个版本——最后一个epoch的、验证集IoU最高的、以及经过阈值优化的。前两个用于对比研究第三个才是真正进入业务流程的。数据划分的随机种子在项目开始时固定下来以后每次实验的对比才公平。遥感滑坡识别这个方向有一整套基于视觉的成熟方法但它远没有到“开箱即用”的傻瓜化程度每一步都要靠自己盯着验证集输出做决策。希望这篇笔记能帮你在自己的数据集上少走这些弯路。本文还有配套的精品资源点击获取