拓冰建站拓冰建站
首页 / 资讯中心 / 正文

基于PyTorch的遥感滑坡识别:AlexNet与ResNet实战解析

简介卷积神经网络CNN通过多层卷积核自动学习像素到语义的映射显著提升了遥感图像分析的效率与准确性。在PyTorch框架下利用预训练的AlexNet或ResNet模型进行迁移学习可有效应对滑坡识别中样本量小、类别不平衡等挑战。本文从CNN基础原理出发介绍遥感滑坡识别的数据划分、数据增强、模型构建与训练调优等关键环节并结合实际工程场景讨论滑动窗口推理与模型部署技巧。该方法可为地质灾害监测、应急救援提供快速决策支持帮助开发者在有限样本下构建高召回率的自动识别系统。1. 遥感滑坡识别为什么依赖CNN和PyTorch滑坡体在遥感影像上表现为颜色、纹理、地形特征的突变人工圈定隐患区域往往需要数小时而强降雨后的救援窗口期只有几十个小时。用卷积神经网络自动识别滑坡本质是把问题建模为图像二分类或者区域分割。CNN通过多层卷积核自动学习从像素到语义的映射不需要手工设计边缘、纹理特征PyTorch的动态计算图和灵活的Tensor API让实验迭代速度明显优于静态图框架。这套基于PyTorch的遥感滑坡识别资源包含四部分可运行的卷积神经网络源码AlexNet与ResNet两种结构、遥感影像数据集、训练好的模型参数以及数据划分脚本split_data.py。它配备的README给出了运行依赖和复现步骤属于一个完整的课程设计或期末大作业。对于那些已经了解CNN前向传播但还没独立组织过完整训练流程的开发者这个项目能从数据准备一直走到推理验证正好卡在“知识”和“实践”之间的位置。2. 遥感数据集准备与split_data.py划分策略2.1 滑坡影像数据的目录组织滑坡识别项目的常见做法是按类别建立文件夹然后用torchvision.datasets.ImageFolder读取。一个典型结构是dataset/ ├── train/ │ ├── landslide/ │ │ ├── slide_001.jpg │ │ └── ... │ └── non_landslide/ │ ├── normal_001.jpg │ └── ... ├── val/ └── test/ImageFolder要求同一类别的图片必须放到同一个子目录中目录名就是类别名。这样写出来的数据加载代码最短datasets.ImageFolder(rootdataset/train)会自动把字母序landslide在non_landslide之前映射为类别0和1。训练好的模型对类别顺序非常敏感所以一旦确定顺序后续使用模型时也要用相同的类别名称。实际遥感滑坡数据集往往类别不平衡负样本非滑坡远多于正样本。如果直接按顺序遍历目录训练时模型会倾向于预测多数类。处理不平衡有两个思路一是通过加权采样器让每个batch里正负样本比例接近二是构造数据集时人工筛选一部分难负样本。split_data.py里如果只做了随机切分建议保留原始类别分布的同时在数据增强中把正样本的随机裁剪比例加大。2.2 split_data.py按比例切分训练集、验证集和测试集项目中提供的数据划分脚本用于将原始图片随机分配到三个子集中。类似功能的脚本我常这样实现import os import shutil import random def split_data(source_dir, target_dir, train_ratio0.7, val_ratio0.15, seed42): random.seed(seed) categories os.listdir(source_dir) # [landslide, non_landslide] for cat in categories: cat_path os.path.join(source_dir, cat) images [f for f in os.listdir(cat_path) if f.endswith((.jpg, .png))] random.shuffle(images) train_count int(len(images) * train_ratio) val_count int(len(images) * val_ratio) for part, slice_ in zip([train, val, test], [images[:train_count], images[train_count:train_count val_count], images[train_count val_count:]]): dst os.path.join(target_dir, part, cat) os.makedirs(dst, exist_okTrue) for f in slice_: shutil.copy(os.path.join(cat_path, f), os.path.join(dst, f)) split_data(dataset/raw, dataset)这里seed非常重要固定随机种子后无论分多少次同一个原始数据集都会得到完全相同的划分。在论文或期末报告中这个细节能避免“训练集和测试集有重叠”这一类质疑。train_ratio与val_ratio的比例可以根据数据总量调整数据量在几千张时71.51.5是稳妥的若只有几百张验证集占比可以降到10%或者把测试集合并到验证集中只做交叉验证。2.3 数据增强与DataLoader封装遥感图像受拍摄时间、云层阴影、传感器噪声影响很大直接归一化后输入网络会导致过拟合。我一般在transform里加入轻量级增强from torchvision import transforms train_transform transforms.Compose([ transforms.Resize((224, 224)), transforms.RandomHorizontalFlip(p0.5), transforms.RandomVerticalFlip(p0.5), transforms.ColorJitter(brightness0.2, contrast0.2), transforms.ToTensor(), transforms.Normalize(mean[0.485, 0.456, 0.406], std[0.229, 0.224, 0.225]) ]) val_transform transforms.Compose([ transforms.Resize((224, 224)), transforms.ToTensor(), transforms.Normalize(mean[0.485, 0.456, 0.406], std[0.229, 0.224, 0.225]) ])RandomHorizontalFlip和RandomVerticalFlip对于遥感图像都适用因为卫星拍摄的山区没有绝对上下方向ColorJitter用来模拟不同大气条件下的色差。如果原图尺寸不一Resize((224,224))直接变形会损失宽高比。更好的做法是先短边缩放再中心裁剪但为了方便批处理很多项目直接Resize。当你发现验证精度低而训练精度高时优先检查增强强度而不是急着换网络。DataLoader部分使用ImageFolder配合shuffleTruefrom torch.utils.data import DataLoader from torchvision import datasets train_dataset datasets.ImageFolder(dataset/train, transformtrain_transform) train_loader DataLoader(train_dataset, batch_size32, shuffleTrue, num_workers4) val_dataset datasets.ImageFolder(dataset/val, transformval_transform) val_loader DataLoader(val_dataset, batch_size32, shuffleFalse, num_workers4)num_workers在Windows上默认会报错需要改成0Linux下可以设为4到8。batch_size的设定要和显存匹配如果训练时出现CUDA out of memory先减半batch_size而不是改网络结构。2.4 遥感图像的多波段输入问题很多遥感高分影像有R、G、B、NIR等四个以上波段。CNN的第一个卷积层的in_channels通常写3要接受多波段数据需要修改网络第一层。常见做法是只保留RGB三个波段或者对近红外波段做灰度化后与RGB拼接成4通道。对于入门级滑坡识别直接使用RGB是足够的因为滑坡体裸露的土壤在RGB下与周围植被有足够差异。如果你手头的原始数据集是GeoTIFF多光谱格式可以先用GDAL库截取前三个波段另存为JPEG这样后续代码不用动。3. AlexNet与ResNet在PyTorch中的构建与对比3.1 两个网络架构的选型逻辑AlexNet在ImageNet-2012上把识别错误率从26%降到15%以上是CNN在现代深度学习中的开山之作。它的核心贡献是ReLU激活函数和Dropout正则化能让一个8层网络在当时的GPU上稳定训练。ResNet则通过残差连接把网络深度提升到152层解决了深层网络的退化问题。对于遥感图像滑坡区域尺度较小纹理细节往往是关键判别信息ResNet的卷积层能保留更细粒度的空间特征因此通常比AlexNet收敛更快、精度更高。下表从工程视角对比两种结构网络深度参数量单张224x224推理耗时(ms)特点AlexNet8约60M1.5结构直观容易实现适合教学ResNet1818约11M2.3参数量少残差结构适合小数据集ResNet5050约25M4.1更强的特征表达需要更多数据实际项目中如果训练样本不足5000张优先试ResNet18你的项目提供的预训练模型如果是由ResNet50训练的那么直接用即可否则你重新训练时要根据显存选择。参数很少的模型不一定精度低遥感图像类别间的差异明显不需要特别大的模型容量。3.2 从零实现AlexNet的关键卷积结构手写AlexNet类的代码能够展示卷积层的组织方式import torch.nn as nn class AlexNet(nn.Module): def __init__(self, num_classes2): super().__init__() self.features nn.Sequential( nn.Conv2d(3, 96, kernel_size11, stride4, padding2), nn.ReLU(inplaceTrue), nn.MaxPool2d(kernel_size3, stride2), nn.Conv2d(96, 256, kernel_size5, padding2), nn.ReLU(inplaceTrue), nn.MaxPool2d(kernel_size3, stride2), nn.Conv2d(256, 384, kernel_size3, padding1), nn.ReLU(inplaceTrue), nn.Conv2d(384, 384, kernel_size3, padding1), nn.ReLU(inplaceTrue), nn.Conv2d(384, 256, kernel_size3, padding1), nn.ReLU(inplaceTrue), nn.MaxPool2d(kernel_size3, stride2), ) self.classifier nn.Sequential( nn.Dropout(0.5), nn.Linear(256 * 6 * 6, 4096), nn.ReLU(inplaceTrue), nn.Dropout(0.5), nn.Linear(4096, 4096), nn.ReLU(inplaceTrue), nn.Linear(4096, num_classes), ) def forward(self, x): x self.features(x) x x.view(x.size(0), -1) x self.classifier(x) return x第一层卷积用kernel_size11, stride4直接降低分辨率相当于对224x224的输入很快缩到55x55。中间三个卷积层没有池化因为池化会丢失位置信息。Dropout(0.5)放在全连接层之前训练时随机丢弃一半神经元让网络不过度依赖某一个特征。在PyTorch中由于ReLU之后只有线性层和Dropout我把多个卷积与激活封装成Sequential这样model.features可以单独输出特征图方便事后可视化和中间层提取。3.3 使用torchvision加载ResNet预训练模型torchvision提供可以直接使用的ResNet强烈建议在遥感数据上使用预训练权重微调而不是随机初始化训练。ImageNet预训练模型已经学到了边缘、纹理、形状等通用特征这些特征迁移到遥感图像上仍然有效。import torchvision.models as models base_model models.resnet18(weightsmodels.ResNet18_Weights.DEFAULT) num_features base_model.fc.in_features base_model.fc nn.Linear(num_features, 2) # 冻结前面层的参数只训练最后一个模块 for name, param in base_model.named_parameters(): if name.startswith(layer4) or name.startswith(fc): param.requires_grad True else: param.requires_grad False optimizer torch.optim.Adam(filter(lambda p: p.requires_grad, base_model.parameters()), lr1e-3)models.resnet18(weights...)在最新版本中使用weights参数指定预训练权重旧版用pretrainedTrue。修改fc层输出维度后模型输出就是滑坡与非滑坡的logits。冻结参数时filter(lambda p: p.requires_grad, ...)确保优化器不会为冻结参数计算梯度节省显存也加快训练。对于数据集较大的情况可以全部解冻并对整个模型做小学习率微调比如lr1e-5。注意如果你加载的项目提供的训练好的模型是用自定义结构保存的也包含state_dict那么加载方式是一样的只要网络结构定义完全一致即可。如果torchvision里没有你需要的结构需要从源码中导入项目自带的类。3.4 输出层设计与类别映射滑坡识别是二分类所以输出层两个节点即可。训练时使用PyTorch的CrossEntropyLoss它内部已经包含了Softmax操作所以模型前向输出的是未归一化的logits而不需要手动在最后一层加Softmax。推理阶段要概率可以在logits上再做torch.softmax(dim1)。类别0和类别1分别对应landslide和non_landslide目录名要与split_data.py中的类别列表保持一致。4. 训练循环、损失函数与结果验证4.1 一个可复用的训练脚本骨架把训练逻辑整理为函数方便后续扩展为交叉验证或者多模型对比。下面是一个简化版但功能完整的训练流程import torch import torch.nn as nn from torch.optim import Adam from torch.optim.lr_scheduler import ReduceLROnPlateau def train_one_epoch(model, loader, criterion, optimizer, device): model.train() total_loss, correct, total 0, 0, 0 for images, labels in loader: images, labels images.to(device), labels.to(device) optimizer.zero_grad() outputs model(images) loss criterion(outputs, labels) loss.backward() optimizer.step() total_loss loss.item() * images.size(0) correct (outputs.argmax(1) labels).sum().item() total images.size(0) return total_loss / total, correct / total def validate(model, loader, criterion, device): model.eval() total_loss, correct, total 0, 0, 0 with torch.no_grad(): for images, labels in loader: images, labels images.to(device), labels.to(device) outputs model(images) loss criterion(outputs, labels) total_loss loss.item() * images.size(0) correct (outputs.argmax(1) labels).sum().item() total images.size(0) return total_loss / total, correct / total device torch.device(cuda if torch.cuda.is_available() else cpu) model base_model.to(device) criterion nn.CrossEntropyLoss() optimizer Adam(filter(lambda p: p.requires_grad, model.parameters()), lr1e-4) scheduler ReduceLROnPlateau(optimizer, modemin, patience3, factor0.5)model.train()和model.eval()切换BatchNorm和Dropout的工作模式验证阶段忘记切换会导致结果异常。with torch.no_grad()让验证过程不构建计算图大幅降低显存占用。ReduceLROnPlateau监控验证loss连续3个epoch没有下降就把学习率减半这对避免训练后期振荡很有用。4.2 超参数设置的经验表根据滑坡图片的大小和样本量常用的训练参数如下超参数建议范围说明输入尺寸224x224与ImageNet预训练模型一致batch_size16~64受显存限制滑坡正样本少时用小batch初始学习率1e-4~1e-3使用预训练模型时建议低一些优化器Adam / SGDmomentumAdam收敛快SGD动量最终精度略高epochs30~50数据量大时可配合早停类别权重根据比例设weight正负样本比 8:1 时必须处理设置CrossEntropyLoss的weight参数是应对类别不平衡最直接的方法。比如负样本数量是正样本的10倍则给正样本的权重设为10负样本设为1。代码写作criterion nn.CrossEntropyLoss(weighttorch.tensor([10.0, 1.0]))。注意weight的顺序要和类别索引一致这又要求类别映射事先固定。4.3 混淆矩阵与评估指标仅用准确率评价滑坡识别不充分因为如果非滑坡占95%全预测为非滑坡也有95%准确率。要计算精确率、召回率和F1可以用sklearn的混淆矩阵from sklearn.metrics import confusion_matrix, classification_report all_preds [] all_labels [] with torch.no_grad(): for images, labels in val_loader: images images.to(device) outputs model(images) all_preds.extend(outputs.argmax(1).cpu().numpy()) all_labels.extend(labels.numpy()) cm confusion_matrix(all_labels, all_preds) print(cm) print(classification_report(all_labels, all_preds, target_names[landslide, non_landslide]))在滑坡识别的实际业务中漏报一个滑坡点的代价远高于误报所以应重点关注召回率。如果模型对正样本的召回率低于0.6说明抑制假阴性的能力不足此时可以降低分类阈值不一定要用0.5作为默认判定边界。4.4 从训练日志判断模型状态假设训练10个epoch日志如下Epoch 1/10 train_loss:0.6931 train_acc:0.51 val_loss:0.6890 val_acc:0.55 Epoch 3/10 train_loss:0.5102 train_acc:0.72 val_loss:0.4801 val_acc:0.75 Epoch 5/10 train_loss:0.2844 train_acc:0.88 val_loss:0.2617 val_acc:0.87 Epoch 8/10 train_loss:0.1021 train_acc:0.96 val_loss:0.2055 val_acc:0.90 Epoch 10/10 train_loss:0.0613 train_acc:0.99 val_loss:0.2331 val_acc:0.89训练loss持续下降而验证loss在第8个epoch后开始上升说明模型进入过拟合。此时应记录第7个epoch的模型参数作为最终结果或者增强数据扩充。对于遥感小数据集AlexNet参数量大过拟合比ResNet更早出现因此需要设置更强的Dropout或加入正则化。项目中如果自带训练好的模型大概率作者已经做过早停直接使用它比从头训练省事得多。5. 利用训练好的模型做遥感影像滑坡预测与工程技巧5.1 加载模型参数完成单张图像推理项目的models目录或checkpoints目录下会存有.pth文件。加载时先定义一个与训练时完全相同的模型对象再调用load_state_dict。import torch from PIL import Image from torchvision import transforms from models.resnet import resnet18 # 假设项目源码里有这个类 model resnet18(num_classes2) state torch.load(best_model.pth, map_locationcpu) model.load_state_dict(state[model_state_dict] if model_state_dict in state else state) model.eval() transform transforms.Compose([ transforms.Resize((224, 224)), transforms.ToTensor(), transforms.Normalize(mean[0.485, 0.456, 0.406], std[0.229, 0.224, 0.225]) ]) img Image.open(test_slide.jpg).convert(RGB) input_tensor transform(img).unsqueeze(0) with torch.no_grad(): logits model(input_tensor) prob torch.softmax(logits, dim1) pred logits.argmax(dim1).item() print(f预测类别: {pred}, 滑坡概率: {prob[0][pred].item():.4f})map_locationcpu让代码在没有GPU的机器上也能运行。注意model.eval()必须在推理前调用否则BatchNorm会使用batch统计量导致预测不稳定。如果加载后报size mismatch通常是因为类别数不同检查num_classes是否与本项目的二分类一致。5.2 滑动窗口处理大尺寸遥感影像遥感影像通常有几千乘几千像素直接resize成224会丢失滑坡体的小尺度纹理。常见做法是使用滑动窗口将大图切分成多个小块分别预测再拼接成分割图或热力图。crop_size 224 step 112 overlay Image.new(L, (img_width, img_height), 0) for y in range(0, img_height - crop_size 1, step): for x in range(0, img_width - crop_size 1, step): crop img.crop((x, y, x crop_size, y crop_size)) crop_tensor transform(crop).unsqueeze(0) with torch.no_grad(): prob torch.softmax(model(crop_tensor), dim1)[0, 1].item() for px in range(x, x crop_size): for py in range(y, y crop_size): overlay.putpixel((px, py), int(prob * 255))窗口步长step取crop_size的一半让相邻窗口有重叠这样能避免滑坡体恰好被窗口边界切断。整幅图每个像素的预测值取窗口内叠加的最大或平均概率最终形成的灰度图用阈值0.5二值化就是滑坡区域。这个方法的计算量和图像像素数成正比想要加速可以用F.conv2d结合矩阵化但作为课程设计循环已经足够。5.3 针对滑坡小样本的微调技巧如果你的训练样本不足而手头又有预训练模型的权重就不要再从零训练。加载预训练权重后把最后一层修改成二分类先冻结前面所有层训练10个epoch再解冻layer3和layer4训练20个epoch。解冻阶段使用lr1e-5因为高层特征针对遥感数据需要小步快跑。另一个技巧是随机擦除数据增强transforms.RandomErasing()让模型不依赖某一个局部特征对滑坡体被植被遮挡的情况非常有效。5.4 验证下载的源码是否完整可运行拿到源码后不要立即在命令行执行python train.py先检查文件是否齐全。用Windows系统打开项目根目录的README.md查看是否有环境专属的依赖版本要求。在命令行运行python split_data.py观察生成的dataset/train里是否出现对应文件夹。如果缺失检查路径是否带中文PyTorch在Windows下对中文路径兼容性差。最后用python predict.py test.png跑通一次推理输出正常后再修改参数训练自己的模型。整个流程验证无误后再对自己的遥感影像做预测。注意本资源仅供学习交流不要用于商业项目。下载使用前最好重新确认数据集来源的许可协议。若只是课程设计展示尽量使用作者提供的模型参数避免因重新训练时间过长而错过答辩。本文还有配套的精品资源点击获取
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门