遥感图像语义分割开发教程:Swin Transformer、Mamba与UNet六种方案解析
发布时间:2026/9/30 11:36:07来源:尧图网络
简介这份课程资源以遥感图像语义分割为核心梳理了从传统方法到深度学习的主流技术路线适合测绘、遥感、计算机视觉方向的开发者与学生入门及进阶参考。文档重点剖析了Swin Transformer与UNet结合的ST-U网络、基于Mamba的Samba方法并与CNN、ViT做了对比同时总结了遥感影像与自然场景分割的差异、Pytorch-Unet多类别分割实现步骤以及高分辨率影像分割的经典与现代方法。内容还覆盖数据预处理、模型选择、后处理技巧并列举土地覆盖分类、灾害评估、农业监测与城市规划等典型应用场景便于理论联系实际。资源仅含1个docx文档约16KB虽体量小巧但知识密度较高适合作为学习笔记或教学参考。当前已有224人浏览学习可帮助读者快速把握遥感图像语义分割的技术脉络与前沿进展。1. 遥感图像语义分割一份开发教程里的六个能打方案遥感图像语义分割并不是把自然场景里的 DeepLab 搬到无人机影像上就完事。高分辨率、多光谱通道、小目标密集、类别极度不均衡这四个因素叠加会让同一套模型在两个数据集上表现出几乎对立的性能。这份教程的价值在于它没有停留在“哪个网络精度高”的层面而是把 Swin Transformer、Mamba、Pytorch-Unet、FCN 家族放到遥感场景里做横向对比并给出了 ST-U 这样的混合编码器结构。对刚接触遥感分割、正准备做地物分类或变化检测的工程师来说这份教程能帮你省下大量查论文和跑 baseline 的时间。我拆完这份开发教程后把里面对我有用的内容整理如下——包括数据预处理、两套主流架构的复现路径、训练参数与冲突排查以及如何在只有少量标注的情况下把模型调到能交付。2. 遥感数据预处理与标注格式Noise 比模型更影响精度2.1 裁剪成 patch在重叠与细节之间找平衡遥感影像很少能整图直接进网络。常见做法是先把大幅影像裁剪成 512×512 或 256×256 的 patch再配合滑动窗口推理拼回完整结果。教程里反复强调一个关键参数overlap 比例。不设置重叠拼接处会遇到严重的接缝伪影重叠太多推理时间几乎翻倍。我一般在做土地覆盖分类时用 stride patch_size × 0.8也就是重叠 20%对道路、房屋边界这类易碎结构能有明显改善。from torch.utils.data import Dataset from PIL import Image import numpy as np class RemoteSensingDataset(Dataset): def __init__(self, img_paths, lbl_paths, patch_size512, stride410): self.img_paths img_paths self.lbl_paths lbl_paths self.patch_size patch_size self.stride stride def __len__(self): return len(self.img_paths) def __getitem__(self, idx): image Image.open(self.img_paths[idx]) label Image.open(self.lbl_paths[idx]) # 随机裁剪一个 patch并保证 image 与 label 的偏移一致 w, h image.size x np.random.randint(0, max(1, w - self.patch_size)) y np.random.randint(0, max(1, h - self.patch_size)) img_patch image.crop((x, y, x self.patch_size, y self.patch_size)) lbl_patch label.crop((x, y, x self.patch_size, y self.patch_size)) return np.array(img_patch), np.array(lbl_patch)上面的代码核心是“同一组坐标同时作用于影像与标签”这是所有遥感分割训练的基础。stride参数在本代码中并未用于训练采样它是推理阶段的参数训练时随机裁剪覆盖不同区域即可扩充样本空间。如果要引入 overlap 裁剪训练可以把stride代入裁剪循环生成一组互相重叠的 patch。遥感影像通常做 8 位或 16 位存储送入网络前必须统一归一化——通常做法是用数据集内统计的 mean/std而不是 ImageNet 的 0.485/0.456/0.406否则多光谱波段会失真。2.2 多光谱通道怎么进网络先做波段选择与配准教程里的对比实验特别提到一个容易翻车的点拿到一个 4 波段RGB近红外或 8 波段的多光谱影像直接换输入通道数之后就训练结果模型不收敛。原因是传感器数据在采集时不同波段之间的分辨率不一定一致。常见做法是先把所有波段重采样到同一分辨率再按需挑波段组输入。对于 4 通道R、G、B、NIR如果预训练模型的编码器只接受 3 通道输入就把 NIR 替换掉某个可见光通道或采用最稳妥的“前 3 个主成分”做输入# 用 GDAL 做波段重采样与裁剪命令行 gdalwarp -tr 2 2 -r bilinear -t_srs EPSG:4326 input_nir.tif input_nir_resampled.tif gdal_translate -srcwin 0 0 1024 1024 -outsize 512 512 input_rgb.tif input_rgb_cropped.tif-tr 2 2表示目标分辨率 2 米-r bilinear用双线性插值做重采样避免在建筑物等陡变边缘出现锯齿。做完之后建议顺手做一次直方图匹配把多时相影像的亮度拉齐——尤其在做变化检测时这一步非常重要否则同一个模型在不同天气条件下拍出的影像上会输出两张完全不同的地物图。2.3 标注质量问题噪声比模型结构更影响精度遥感标注比自然图像标注更容易混入噪声。树木遮挡楼顶、阴影覆盖道路、水体边界模糊这些区域人工标注时出现 3 到 5 个像素的偏差很普遍。教程中虽然没有细讲标签清洗但实验结论间接印证了这一点同样一个网络在干净标注集上 mIoU 可以到 0.82在含 10% 错标的集上直接掉到 0.7。所以第一次跑通流程后不要急着调模型先抽检 100 张训练图的标签与影像叠加效果。发现明显错位或漏标时优先用 LabelMe 或 EISeg 做二次修正这是投入产出比最高的精度提升手段。提示如果数据集本身是大尺寸 GeoTIFF一定要确认各地块的坐标系一致混合来源的数据往往存在偏移这会导致 patch 裁剪后标签与影像整体错位。3. Swin Transformer UNet 实战ST-U 编码器替换与加速3.1 为什么选 Swin Transformer 做编码器UNet 的经典编码器VGG、ResNet通过堆叠卷积核扩大感受野但感受野始终受限于核的大小和层数。遥感影像里的目标“小”但分布“广”例如一张 1024×1024 的影像中可能存在几百栋小房屋局部卷积难以建立跨区域上下文。Swin Transformer 的核心是移位窗口自注意力先在窗口内计算注意力再通过 shift 操作让窗口之间交换信息。这个设计与遥感影像的高分辨率特征天然契合——可以用相对较小的计算量获得全局上下文。ST-U 网络把 Swin Transformer 当作编码器保留 UNet 的跳连接解码器。这里要注意Swin 输出的特征图分四级尺寸逐级减半UNet 解码器正好有四级上采样可以直接对齐。教程中提到该结构在全局特征提取上取得明显提升我在自己的实验里也验证了这个结论——尤其是在大面积水体、农田这类“非结构化”地物上ST-U 比 ResNet34-UNet 的 mIoU 高出约 4 个百分点但在纯建筑物分割任务上优势并不明显因为建筑本身就有很强的局部边界特征。3.2 配置 Swin-UNet 训练脚本关键参数与显存控制# 以 mmsegmentation 风格配置为例 model dict( typeEncoderDecoder, backbonedict( typeSwinTransformer, embed_dims96, depths[2, 2, 6, 2], num_heads[3, 6, 12, 24], window_size7, mlp_ratio4, qkv_biasTrue, qk_scaleNone, drop_rate0.0, attn_drop_rate0.0, drop_path_rate0.3, # 遥感数据集较小时drop path 可适当加大 out_indices(0, 1, 2, 3) ), decode_headdict( typeUNetHead, in_channels[96, 192, 384, 768], upsample4 ) )window_size7是 Swin-T 在 ImageNet 上的默认配置直接用于遥感影像时要注意如果输入 patch 不是 7 的整数倍会自动 padding带来少量位置编码偏差建议把输入尺寸设为能被 7 整除的值比如 511 或 504。drop_path_rate0.3是经验值——在小数据集上适当增加 drop path 可以抑制过拟合但如果训练集超过 5 万张建议改回 0.1。显存不够时常见做法是降低 batch size并把输入从 512×512 降到 384×384。Swin 的显存占用分为四层特征图与解码器特征图两部分前者随输入尺寸平方级增长。也可以把混合精度训练打开PyTorch 中对应torch.cuda.amp.autocast()实测可节省约 40% 显存训练速度提升 1.5 倍左右。3.3 训练 ST-U 网络时的收敛曲线判读与调参遥感数据集的类别分布通常极度不均衡建筑物可能只占 5%背景占 80%。如果直接使用交叉熵损失模型会把所有像素预测为背景mIoU 虚高但实际毫无用处。教程的消融实验里应该都配了这组对比单一 CrossEntropyLoss 的 mIoU 看起来逼近 0.85但按类别逐一分看建筑的 IoU 只有 0.2。解决方法是给每个类别分配权重或叠加 Dice Loss。我在训练多类别遥感分割时通常把 BCEWithLogitsLoss 设为总损失的 0.6、Dice Loss 设为 0.4Dice Loss 对小类别负责BCE 提供稳定的梯度信号。学习率方面Swin 这类 Transformer 编码器通常用 2e-4 到 6e-4且需要更长的 warmup 比例——我习惯用总 iter 的 10% 做线性 warmup再进入 cosine 下降。学习率过大时Transformer 的 LayerNorm 会造成训练初期的振荡精确表现为 loss 前 500 步不降反升。4. Mamba 编码器与 Samba和 CNN、ViT 的对比到底赢在哪4.1 Mamba 在遥感图像上的直觉理解Mamba 是结构化状态空间模型可以用线性复杂度处理长序列。在遥感任务里一幅大影像拉直后的序列长度可能是几万甚至几十万像素Transformer 的自注意力复杂度是 O(n²)很容易把显存打爆Mamba 的线性复杂度让它可以“读完”整幅影像的长距离依赖。Samba 方法把 Mamba 作为编码器与 CNN、ViT 结合教程中反复强调其在 LoveDA 数据集上的突出表现。LoveDA 的高难度在于多层地物嵌套比如城中村区域既有密集房屋、又有细碎植被与道路类别之间边界模糊。在这个数据集上纯 CNN 会因感受野不足丢失上下文纯 ViT 则受限于分辨率与内存Samba 的混合方案正好取长补短。4.2 Samba 的工程实现几何架构与序列化方案# 一个简化的 Mamba 编码器适配示意伪代码风格结构参考 Samba 方法 import torch import torch.nn as nn class MambaEncoder(nn.Module): def __init__(self, d_model256, d_state16, d_conv4): super().__init__() self.ssm MambaBlock(d_model, d_state, d_conv) self.norm nn.LayerNorm(d_model) def forward(self, x): B, C, H, W x.shape # 将 HxW 展平成序列按行主序 x x.flatten(2).transpose(1, 2) # [B, H*W, C] x self.ssm(x) x # 残差连接 x self.norm(x) x x.transpose(1, 2).view(B, C, H, W) return x核心是把二维图像按行展平成序列输入 Mamba。但这里有个容易被忽略的问题图像是二维结构单纯的按行展平会丢失列方向的关联。常见做法是先按行展平输入一次再把特征转置按列展平输入一次最后拼接两路特征。Samba 论文里的做法更复杂会把 CNN 提取的特征与 Mamba 序列特征做交叉融合。如果只做简单替换把 Mamba 放在 UNet 编码器中替代最后一个 stage精度往往不如 ResNet。4.3 与 ViT 的复杂度对比什么时候该选 MambaViT 在做图像分割时输入 patch 尺寸通常设为 16×16那么一张 1024×1024 的影像会产生 4096 个 token自注意力的计算量是 4096² 1600 万。Mamba 是线性复杂度同尺寸下计算量约为 4096 × d_state约 6 万多差了接近三个数量级。实际训练时ViT 需要先缩小影像或增大 patch 才能放入显存这直接损失了遥感分割最看重的小目标细节Mamba 则可以保持较小的 patch 或直接在原始分辨率上做序列建模。但 Mamba 也有代价它对序列顺序非常敏感行列混合序列化方案实现复杂度高且目前没有像预训练 Swin 那样强大的公开权重一般需要从零训练收敛需要更多数据。表 1 是我实际对比的典型数字。模型结构编码器复杂度1024×1024 时相对显存占用小目标20px表现收敛所需数据量ResNet34O(n)低差少DeepLabV3O(n)中中中Swin-UNetO(n²) 窗口约束中高中好中Samba (Mamba)O(n)中好较多如果项目标注量只有 1000 张左右不建议直接上 Samba。这种情况下可以先用 Swin-UNet 预训练权重做迁移学习等积累到 5000 张以上再考虑把编码器换成 Mamba用于追求极致精度或处理 1 米级以内的亚米级影像。判断当前是否适合换架构的标准很简单看目标类别里是否存在需要“跨大范围感知”才能判别的类型例如连接被道路隔断的同一片水体或识别阴影中的同质地物。5. Pytorch-Unet 多类别分割实践数据加载、训练、评估闭环5.1 从二分类到多分类通道、损失与评估的三重改造很多团队第一次接触遥感分割都是先跑通一个 Pytorch-Unet 二分类通常是提取建筑物。但实际业务里动辄就是 5 到 10 个类别。把二分类改成多分类有三处结构必须同步改输出通道数从 1 改为类别数 N最后一层激活从 sigmoid 改为 softmax或保留 logits 交给损失函数计算损失函数从 BCE 改为 CrossEntropyLoss并配合类别权重评估指标从 IoU 改为 mIoU 的平均。教程里给的实现链条包括数据加载、模型训练、结果评估三步下面给出一个精简但完整的多分类训练循环。import torch import torch.nn as nn from torch.utils.data import DataLoader from model.unet import UNet # 以教程提供的网络为准 model UNet(in_channels3, num_classes6) # 6 类别 device torch.device(cuda) # 类别权重按“1 / 频率”粗略设置训练前先统计像素占比 class_weights torch.tensor([0.3, 1.2, 2.1, 0.8, 1.5, 0.6]).to(device) criterion nn.CrossEntropyLoss(weightclass_weights) optimizer torch.optim.AdamW(model.parameters(), lr3e-4) scheduler torch.optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max80) train_loader DataLoader(dataset, batch_size8, shuffleTrue, num_workers4) for epoch in range(80): model.train() for imgs, labels in train_loader: imgs, labels imgs.to(device), labels.to(device).long() logits model(imgs) # [B, 6, H, W] loss criterion(logits, labels) optimizer.zero_grad() loss.backward() optimizer.step() scheduler.step() print(fEpoch {epoch}: loss {loss.item():.4f})labels必须是 Long 类型整型,而不是 float 的 one-hot 向量CrossEntropyLoss 内部会做 one-hot 映射。class_weights的设定需要先统计数据集中各类别的像素占比比较稳妥的值是令weight_i median_freq / freq_i其中median_freq是所有类别像素占比的中位数。如果某个类别的像素只占全图的 0.5%其权重可能会超过 10此时需要对该权重做上限截断常见做法是把超过 5 的值截到 5防止训练震荡。5.2 验证与推理后处理不要把 softmax 输出的 argmax 直接当结果交付评估阶段除了 mIoU教程还特别提到遥感领域比自然图像更重视的两个指标Kappa 系数和 F1-score。Kappa 系数能消除“运气好”造成的虚高精度——若某个类别占全图 80% 且分类器只会预测这一类Kappa 会显著低于 mIoU 所暗示的表现。计算 Kappa 需先构造混淆矩阵公式并不复杂但容易写错建议直接用sklearn.metrics.cohen_kappa_score只要你传入y_true与y_pred两个扁平化数组即可。推理时直接对每个像素取 argmax会在道路边界和屋顶边缘产生大量椒盐噪声。常见做法是加一个形态学后处理对小于阈值的连通域做删除import cv2 import numpy as np def remove_small_regions(mask, min_area50): mask mask.astype(np.uint8) num_labels, labels, stats, _ cv2.connectedComponentsWithStats(mask) filtered np.zeros_like(mask) for i in range(1, num_labels): if stats[i, cv2.CC_STAT_AREA] min_area: filtered[labels i] 255 return filtered # 示例对“建筑物”类做连通域清洗 building_mask (pred 2).astype(np.uint8) * 255 cleaned remove_small_regions(building_mask, min_area80)min_area80的含义是面积小于 80 像素的孤立预测区域被移除。这个值需要根据影像分辨率调整——分辨率为 2 米时一个 20 平方米的违章建筑对应约 5 个像素设置过大会把真实小目标误删。更稳妥的做法是先按类别分别清洗设定不同的min_area再合并结果不要把整张预测图直接传进去做全局连通域分析否则类别 A 的小块会被类别 B 的大块覆盖。5.3 模型保存与增量训练断点续训的常见血泪坑多类别遥感分割训练一般耗时较长教程里给出的是完整训练脚本但实际工程里更需要断点续训能力。有一个非常隐蔽的坑PyTorch 保存 checkpoint 时如果只存model.state_dict()恢复训练时 scheduler 的状态会丢失学习率会直接回退到初始值导致后续 loss 出现一个小尖峰。我在训练 Swin-UNet 时就因为这个问题浪费过十几个小时。正确做法是同时保存模型参数、优化器参数、scheduler 参数和当前 epochtorch.save({ epoch: epoch, model_state_dict: model.state_dict(), optimizer_state_dict: optimizer.state_dict(), scheduler_state_dict: scheduler.state_dict(), best_miou: best_miou }, checkpoint_epoch50.pth)同时数据集的随机采样顺序也会随 batch 随机种子变化续训时如果想严格复现采样序列需要在DataLoader中使用固定的torch.manual_seed(epoch)但这样做会让 epoch 与数据顺序强绑定通常不需要刻意追求。这里的重点是 optimizer 和 scheduler 必须完整保存否则断点续训的意义就丢失了一半。6. 遥感语义分割避坑指南五个反复踩中的痛点6.1 模型预测全图出现棋盘格现象训练完成推理大影像时预测图出现规律的棋盘格伪影边缘呈锯齿状。原因训练时用了随机裁剪但推理滑动窗口的 stride 与 patch size 整除关系不佳相邻 patch 预测结果在边界处类别不一致优化器训练时使用的 BN 统计与推理时不一致也有可能导致边界响应异常。解决推理时设置重叠 overlap并在拼接处按距离做高斯加权融合同时确认model.eval()已调用关闭 dropout 与 BN 的 batch 统计更新。6.2 mIoU 高但可视效果差现象指标显示 mIoU0.82但目视发现道路断断续续建筑屋顶内部有大片空洞。原因mIoU 是逐类别计算的像素级交并比它不惩罚连通性。道路类别的 IoU 上升主要依赖主体像素断口和空洞对总 IoU 的冲击很小。解决在评估指标中加入连通域相关指标例如计算道路连通成分数量、骨架完整性训练时对损失函数增加边界感知项例如对距离变换后的边界区域加大 Dice Loss 权重。后处理中可用形态学闭运算连接断口但结构元素尺寸不宜超过 5×5。6.3 多光谱影像直接替换 RGB 通道后在预训练网络上不收敛现象加载 Swin-UNet 或 Pytorch-Unet 预训练权重后把输入从 3 通道改成 4 通道RGBNIRloss 长期不降。原因预训练权重第一个卷积层是 3 通道的直接改in_channels4后通常会用随机初始化补第 4 通道但随机权重的梯度幅度远超预训练权重的微调历程破坏了原有的特征提取模型需要从头学等于放弃了迁移学习。解决不要直接改通道数。主流做法是把 NIR 通道编码为伪彩色或者做 PCA 后取前 3 个主成分作为输入另一个常见做法是保留 3 通道输入把 NIR 作为额外监督信号在网络中间层以辅助损失的形式引入。6.4 影像分辨率不一致导致标签错位现象训练 loss 不下降抽检发现同一坐标的影像内容与标签始终对不上错位量约几个像素到几十个像素不等。原因影像的分辨率是 0.5 米标签却基于 2 米分辨率影像绘制或者采集时使用了不同的投影坐标系裁剪后错位被放大。解决在预处理阶段用 GDAL 做二次配准检查影像与标签的四个角点坐标是否一致。如果坐标系一致但仍有小偏移可用gdal_translate -a_ullr手动微调角点使用 QGIS 叠加显示抽查若干区域错位在可视化下几秒钟就能暴露。6.5 训练到后期 loss 下降极慢改小学习率反而更差现象训练到 60 epoch 后 loss 几乎不变把学习率降到 1e-5 后 loss 不降反升。原因余弦退火已经将学习率降到较低水平模型陷入局部平坦区继续降低学习率只会让权重更新步长过小无法跨越势垒。解决常见的做法是重启 AdamW 优化器的momentum或beta参数让一阶动量重新积累或者回到 checkpoint 并重新设一组带 warmup 的高学习率配合梯度裁剪做一次“重启式训练”。针对遥感分割我常把骨干网络和解码头分为两组学习率骨干 3e-5、解码头 3e-4在后期仍能继续提升小类别精度。7. 验证分割质量的三个硬手段用可视化与指标交叉验证模型训练完移动端的“看起来挺准”不能作为交付依据。我一般会强制走一遍以下三个验证流程任何一个通不过都要回到前面的步骤排查。第一步跑推理并导出叠加图把预测结果与原始影像按半透明叠加输出不要只看单独的预测 mask。重点看三类区域——建筑物的边缘是否贴合屋顶轮廓线、道路是否有断裂、阴影区域的类别是否稳定。这一步能暴露指标无法发现的系统偏移。第二步按类别分别计算 IoU 与 Kappa而不是只看平均 mIoU如果“建筑物”IoU 0.85 但“阴影”只有 0.25绝大多数情况下是训练标签中阴影区域没有被规范标注而不是模型问题。第三步抽样数百个独立验证点做人工判读在原始影像上随机撒 200 到 500 个点人工判断类别并构建混淆矩阵该矩阵与模型输出混淆矩阵的差异能反映出数据标注质量本身。遥感影像的标注者之间一致性通常只有 85% 到 90%也就是说模型 mIoU 的上限可能在 0.85 附近——超过这个值后提升纯属过度拟合这种项目的翻车往往不在模型而在标注认知。在那之后我每次训练遥感分割模型都会把“单独可视化的边缘叠加图 逐类别混淆矩阵 抽样人工验证”三件套走一遍再判断模型是否可以交付。识别小目标时还会额外加一条规则模型预测的面积低于 3×3 像素的对象如果真值存在但预测未覆盖需重新回查训练集 patch 是否包含了该对象。希望这份教程拆解和这些经验能帮你在遥感分割项目里少走几段弯路。本文还有配套的精品资源点击获取
网站建设高端定制企业官网