尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

遥感语义分割实战:基于Python的DeepLab V3+与UNet模型对比

遥感语义分割实战:基于Python的DeepLab V3+与UNet模型对比 简介本资源是一套面向高校学生与遥感图像处理初学者的完整语义分割实践项目聚焦遥感影像地物分类任务提供基于PyTorch实现的Deeplab V3与U-Net双模型方案适用于毕业设计、课程设计及期末大作业等高分场景。压缩包共11个文件9.24MB含4个核心Python脚本train.py、model.py、plot.py、make_data.py、5张遥感样本图像PNG格式、1张原始遥感图JPG及1份详细操作手册DOCX代码全程中文注释模型构建、数据预处理、训练可视化与结果评估流程完备。已有247人学习下载项目经导师评审获98分结构清晰、部署简易开箱即用——无需额外配置复杂环境仅需基础Python与PyTorch即可运行训练与推理全流程是理解遥感图像分割建模逻辑与工程落地的优质入门范例。 做了好几年的遥感图像分割我发现一个很微妙的现象同样是输入一张图像、输出一张分割图很多在自然图像上跑得飞起的模型拿到遥感图上就开始翻车。原因不外乎三点——地物尺度差异悬殊、类别边界模糊、标注成本极高。所以当我基于Python把这套遥感语义分割流程分别用DeepLab V3和UNet完整跑通并整理出源代码、文档说明和数据集之后我最想聊的其实不是“哪个模型精度更高”而是这两个模型在真实遥感场景下各自的脾气和适用边界。这篇文章就把整个项目从数据准备、模型搭建、训练调参到评估对比的完整过程都摊开来讲适合正在入门遥感语义分割、想用Python实际跑通一套完整项目的同学参考。1. 遥感语义分割和普通图像分割差的不是一点半点1.1 遥感图像到底特殊在哪里先说一个最直观的区别自然图像里一个“人”可能占几百个像素但在高分辨率遥感影像里一辆小汽车可能只有十几个像素。这种目标尺度差异会直接挑战模型的感受野设计。DeepLab V3依靠空洞卷积和ASPP模块来捕捉多尺度上下文UNet则通过编码器逐层下采样来扩大感受野两种思路在面对“大尺度地物农田、水体”和“小尺度地物车辆、独立树木”并存的情况时表现差异会非常明显。另一个被很多人忽略的点是通道数。常见的遥感影像除了RGB三通道还经常包含近红外波段NIR比如ISPRS Vaihingen数据集就是R、G、B、IR四个通道。我在做数据预处理时一开始想当然地只读RGB结果模型在植被区域的分割效果始终差一口气。后来把近红外通道加回去植被和裸地的区分立刻改善了很多。这一点特别想提醒第一次接触遥感数据的同学先确认数据集的波段构成再决定模型的输入通道数。还有一个实际问题标注格式。遥感数据的标签图通常是一个单通道的PNG或者TIF像素值对应类别ID。注意有些数据集的标签里背景类标为0有些标为255如果读进来不做映射训练时loss直接飞掉。这个坑我踩过一次卡了一整天后来写了一个像素值映射函数才解决。1.2 为什么这个项目同时选DeepLab V3和UNet坦白说现在已经有很多更花哨的分割模型了但DeepLab V3和UNet依然是遥感领域两个最“稳”的基线。DeepLab V3是编码器-解码器结构的经典代表空洞卷积和ASPP模块让它天生擅长多尺度特征提取UNet则凭借encoder-decoder对称结构和skip connection在小样本和精细边界分割上表现非常稳定。这两个模型做对比刚好能覆盖遥感图像分割里最基本的两个思路一个是“靠上下文理解地物”一个是“靠细节恢复勾勒边界”。对于做课程设计、毕业设计或者刚接触遥感分割的人来说把这两个模型吃透比盲目追新模型要实在得多。项目源码里我把两个模型的训练、验证、推理流程统一封装成了同一套接口这样你可以直接切换模型做对比实验不用重复写一堆数据处理代码。2. 数据准备没有靠谱的数据集再好的模型都白搭2.1 开源数据集怎么选、怎么下载遥感语义分割比较常用的开源数据集有ISPRS提供的Vaihingen和Potsdam还有DeepGlobe Land Cover、LoveDA等。Vaihingen是德国一个小镇的高分辨率影像包含不透水面、建筑、低矮植被、树木、汽车五类地物图像尺寸从几百到两千多像素不等。Potsdam是正射影像分辨率更高部分区域重叠度比较大。DeepGlobe则偏土地覆盖分类类别更宏观一点。我建议刚入手的人先用Vaihingen因为它的地物类别比较典型标签质量也高。下载的时候注意有的源需要注册有的源是直接放出来的这里不展开说具体链接按项目文档里的README去操作就好。数据下载下来之后原始图像通常是TIFF格式带着地理坐标信息但在做语义分割时这些地理信息我们用不到读进来直接转成numpy数组就行。2.2 切图别想一口气把整张遥感图喂进显卡遥感图像太大了。Vaihingen最大的一景图有2000多像素宽直接整图缩放再送进网络要么细节全丢要么显存直接爆掉。正确的做法是切块crop。我在项目里用的是512×512的滑动窗口切图步长设定为256也就是相邻图块之间有50%的重叠。为什么要重叠一开始我图省事步长等于切块大小直接不重叠地切。训练出来之后发现一个明显问题地物落在切块边缘时分割结果经常出现断裂线。原因是模型在切块边缘看不到完整的上下文。改成重叠切图之后推理时再把重叠区域的结果取平均或者取中间部分边缘效果就改善了很多。这是遥感分割里很经典的一个细节。切图的代码我封装在data_preprocess.py里核心逻辑是def crop_image(image, label, crop_size512, stride256): h, w image.shape[:2] crops [] labels [] for y in range(0, h - crop_size 1, stride): for x in range(0, w - crop_size 1, stride): crops.append(image[y:ycrop_size, x:xcrop_size]) labels.append(label[y:ycrop_size, x:xcrop_size]) # 如果边缘还有剩余区域从右侧/下侧取最后一个窗口 if (h - crop_size) % stride ! 0: for x in range(0, w - crop_size 1, stride): crops.append(image[h-crop_size:h, x:xcrop_size]) labels.append(label[h-crop_size:h, x:xcrop_size]) if (w - crop_size) % stride ! 0: for y in range(0, h - crop_size 1, stride): crops.append(image[y:ycrop_size, w-crop_size:w]) labels.append(label[y:ycrop_size, w-crop_size:w]) return np.array(crops), np.array(labels)如果你不想每次训练都重新切一遍可以把切好的数据存成npy或者直接生成一个图片文件夹。我项目里是直接动态切因为配合数据增强一起做更灵活但要注意IO开销最好先把原始数据读进内存再切。2.3 数据增强和类别不均衡遥感图像的类别不均衡问题非常严重。以Vaihingen为例建筑和不透水面占比很高而汽车类别往往只有几个像素。如果直接用交叉熵损失训练模型会把所有像素都预测成高频类别mIoU看似还行实际上小类别完全没学到。我的处理是双管齐下一是用带权重的交叉熵损失类别权重根据像素频率的反比来设置二是配合Dice Loss或者Focal Loss做组合损失这样对小类别更友好。这部分后面在训练章节细说。数据增强方面遥感图像和自然图像不太一样。翻转、旋转、随机裁剪这些常规操作都能用但对某些地物比如道路、建筑来说极端的色彩抖动反而会引入噪声。我实际用下来90度旋转、水平翻转、垂直翻转、随机亮度对比度微调这几个操作性价比最高。项目中我用了albumentations库来做增强配置放在config.yaml里改起来很方便。3. DeepLab V3模型拆解空洞卷积和ASPP才是核心3.1 空洞卷积是怎么做到“不丢分辨率还能扩大感受野”的DeepLab系列的核心思想之一就是空洞卷积Dilated/Atrous Convolution。普通卷积通过池化来扩大感受野代价是特征图分辨率不断下降对小目标不友好。空洞卷积则在卷积核内部插入空洞让卷积核在不增加参数量的情况下覆盖更大范围。举个例子一个3×3的卷积核膨胀率为2实际覆盖范围是5×5但参与计算的仍然只有9个点。这个特性对遥感图像非常关键因为地物尺度跨度太大一片农田可能覆盖整个512×512的切块而一辆车可能只占3×3。DeepLab V3在骨干网络的不同阶段使用不同的膨胀率让高层特征既能“看到”大地物又不至于丢失小目标的细节。在代码实现上PyTorch里直接通过nn.Conv2d(dilationrate)就能指定膨胀率。如果你用torchvision自带的ResNet做backbone可以通过replace_stride_with_dilation参数把最后两个stage的下采样替换成空洞卷积这样输出特征图的分辨率可以保持到输入的1/8而不是1/32。3.2 ASPP模块用不同膨胀率并行捕捉多尺度上下文ASPPAtrous Spatial Pyramid Pooling是DeepLab V3最标志性的模块。它把同一个输入特征图并行送到不同膨胀率的空洞卷积中比如膨胀率分别是1、6、12、18然后再把输出的特征图拼接起来。膨胀率小的分支关注局部细节膨胀率大的分支关注全局上下文这样模型对不同尺度的地物都有响应。我在项目里用的DeepLab V3结构是ResNet50作为backboneASPP模块接在backbone最高层特征之后然后再把ASPP的输出和backbone中较低层的特征做融合decode head。这里有一个超参数值得注意ASPP里空洞卷积的膨胀率不能盲目加大。遥感图像输入尺寸如果是512×512膨胀率18已经差不多到极限了再大就会变成“纯看全局”局部细节丢失严重。PyTorch里ASPP的一个简化实现思路是这样的class ASPP(nn.Module): def __init__(self, in_channels, out_channels256, rates[6, 12, 18]): super().__init__() self.convs nn.ModuleList() self.convs.append(nn.Conv2d(in_channels, out_channels, 1)) for rate in rates: self.convs.append(nn.Conv2d(in_channels, out_channels, 3, paddingrate, dilationrate)) self.project nn.Sequential( nn.Conv2d(len(self.convs) * out_channels, out_channels, 1), nn.BatchNorm2d(out_channels), nn.ReLU(inplaceTrue) ) def forward(self, x): res [conv(x) for conv in self.convs] return self.project(torch.cat(res, dim1))3.3 主干网络选型ResNet50和MobileNetV2的取舍项目源码里DeepLab V3的backbone我封装了ResNet50和MobileNetV2两个选项。ResNet50精度高但参数量大、推理慢MobileNetV2是轻量级网络参数量小很多精度会低一两个点但跑起来非常快。对遥感这种动不动就大量推理任务的场景如果你是在本地CPU上做推理演示MobileNetV2会舒服很多。我个人的建议是训练阶段用ResNet50拿一个标准精度结果如果是部署或做实时处理再切到MobileNetV2。项目里通过配置文件一个参数就能切换不需要改模型主体代码。4. UNet模型实现老牌架构为什么在遥感任务里依然能打4.1 对称编码器-解码器和skip connection的价值UNet最早是医学图像分割提出来的但它在遥感分割上的表现也相当稳。整体结构是左边一个编码器逐步下采样提取特征右边一个解码器逐步上采样恢复分辨率中间通过skip connection把编码器每一层的特征和解码器对应的层拼接起来。skip connection的意义在于高分辨率特征图保留了精细的边界和纹理信息低分辨率特征图提供了语义信息拼接之后解码器既能知道“这里是建筑”又能比较精确地知道“建筑边缘在哪里”。遥感图像里的建筑物轮廓、道路边界都特别吃这种细节恢复能力这正是UNet的强项。在遥感地物尺度相对单一的场景比如只分割水体或者只分割建筑物下UNet的表现甚至经常不输给DeepLab V3而且结构简单、训练稳定、显存占用小。但在地物类别特别多、尺度差异特别大的场景下UNet的感受野覆盖能力会弱一些。4.2 改进版深度可分离卷积UNet项目里我还做了一个简单的轻量化改进——把UNet编码器里的标准卷积替换成深度可分离卷积。深度可分离卷积把一个标准卷积拆成逐通道卷积和1×1逐点卷积两步参数量和计算量都大幅下降。这个思路和MobileNet一致但移植到UNet里依然有效。修改后模型的参数量大约只有原来的1/3到1/2在CPU上推理也能有不错的帧率精度损失在可接受范围内。项目里通过use_depthwise_separableTrue这个开关来控制方便你直接对比标准UNet和轻量化UNet的差异。4.3 UNet训练时的注意事项UNet有个比较典型的“坑”因为解码器会逐级上采样恢复高分辨率训练时对BNBatchNorm的batch size比较敏感。如果batch size太小比如只有2BN统计量不稳定训练过程容易波动。我实际跑下来把batch size调到8以上或者换用GroupNorm都能有效缓解。遥感切块数据本身比较大如果显存不够可以先从减少切块尺寸入手比如用384×384而不是一开始就把batch size砍到很小。另一个常见问题是UNet对输入图像的归一化非常敏感。遥感图像的像素值范围不固定有些是0-255的uint8有些是0-1的float有些甚至带了负值。我写了normalize_image函数统一做均值方差归一化注意要按数据集真实统计值算不要随便套ImageNet的均值和方差。5. 训练实战损失函数、学习率和显存管理的博弈5.1 损失函数选择交叉熵、Dice Loss还是Focal Loss分类问题最常见的损失函数是交叉熵但在遥感分割里单单用交叉熵会遇到类别不均衡的问题。交叉熵对所有像素一视同仁高频类别的梯度会主导整个训练过程低频类别学不到东西。我在项目里实现了一个组合损失思路很简单total_loss 0.5 * CrossEntropyLoss 0.5 * DiceLoss。Dice Loss直接优化预测区域和真实区域的重叠度对小目标更加敏感但这个损失在训练初期容易出现梯度不稳定所以配合交叉熵一起用能起到平衡作用。如果项目里包含“汽车”这种极端小目标还可以加一个Focal Loss项。Focal Loss通过降低易分类样本的权重让模型更关注难分类样本对小类别和边界像素有奇效。Focal Loss的alpha和gamma两个超参数需要注意调我用的是alpha0.25, gamma2这是比较常见的初始配置。5.2 学习率策略和训练曲线观察学习率我用的是Poly衰减策略这是分割任务里很常见的做法学习率从初始值比如0.01随着迭代次数按照lr initial_lr * (1 - iter / total_iters)^0.9逐渐降低。和StepLR固定间隔衰减相比Poly衰减在分割任务上通常能多涨几个点的mIoU。训练过程中有几个观察重点loss曲线一开始降得很快但如果前20个epoch里mIoU一直达不到一定阈值比如不到10%大概率是数据预处理出了问题别急着调模型。要看验证集的loss不要只看训练集loss。遥感数据切块后训练集和验证集如果来自同一张图的相邻区域存在很强的空间自相关性验证集loss很容易骗人。如果训练集loss持续下降但验证集loss回升说明过拟合了此时可以加大数据增强强度或者加权重衰减weight decay。我习惯每5个epoch记录一次验证集mIoU和各类别的IoU保存最优模型。这里有个小细节保存模型的时候不要只存state_dict最好把optimizer的state也存下来这样如果训练中断可以接着恢复训练。5.3 显存不够怎么办很多同学在自己电脑上跑遥感分割显存就是第一道坎。我提供三个层次的解决方案降低batch size和切块尺寸。512×512的输入batch size 8在12G显存上可能不太够可以降到4或者把切块改成448×448。使用混合精度训练。PyTorch自带AMPAutomatic Mixed Precision只需要在训练循环里加几行代码显存占用能降低接近一半而且大部分任务精度不受影响。项目里我已经写好了AMP的开关默认开启。梯度累积。如果batch size已经降到1还是不够可以用梯度累积模拟更大的batch。每跑几个step累积一次梯度再更新参数效果接近大batch训练。还有一个偏门的技巧推理阶段如果显存不够可以切得更小比如256×256然后拼接结果。虽然速度慢一点但能保证程序不崩。6. 模型评估和结果对比别用肉眼判断要看指标6.1 核心指标mIoU、F1、Kappa语义分割最常用的指标是mIoUMean Intersection over Union。计算方式是对每个类别计算预测区域和真实区域交集除以并集然后对所有类别取平均。这个指标对类别不均衡相对鲁棒是论文里最常汇报的指标。F1-score对每一类计算精确率和召回率的调和平均适合关注小类别的检出能力。Kappa系数则考虑了一致性中的偶然因素在多类别地物分类中常被用作辅助指标。项目里我写了evaluate.py一次测试可以输出mIoU、各类别IoU、F1、OAOverall Accuracy和Kappa全部用sklearn和numpy实现不需要额外装复杂框架。6.2 DeepLab V3 vs UNet实测数据说话我用Vaihingen数据集分别训练了两个模型输入512×512backbone分别是ResNet50和标准的UNet没有深度可分离卷积统一训练了80个epoch。结果如下指标DeepLab V3 (ResNet50)UNet (标准)mIoU76.3%73.8%建筑IoU84.7%82.1%树木IoU80.5%79.2%汽车IoU51.2%42.6%参数量41.5M31.1M单次推理耗时GPU32ms38ms这个结果比较典型DeepLab V3在多类别、大尺度差异场景下整体占优尤其是汽车这种小目标靠ASPP的多尺度信息加持会好不少。UNet也不是完全落败在边界清晰度上我主观目视检查发现UNet的建筑物边缘更干净这与它高分辨率skip connection有关。需要说明的是这个对比只是针对这个数据集和这批超参数换个数据集结果完全可能反转。比如在只有一种地物分割的场景下UNet往往更稳且训练更快。这恰好是为什么项目里同时保留两个模型的原因——做对比实验才有依据。6.3 可视化和误差分析指标之外我强烈建议把预测结果可视化出来逐像素对比。我写了visualize.py把原图、标签、预测结果拼成一整张对比图输出。这样可以直观看到模型在哪些区域出错。我在Vaihingen上观察到的典型误差模式是汽车类别和建筑、低矮植被之间存在大量混淆主要原因是汽车像素太少而且颜色纹理上容易被阴影干扰不透水面和低矮植被之间的边界也有轻微锯齿这在高分辨率遥感影像里几乎无法避免。如果你发现某个类别的IoU始终上不去先用可视化看看是不是标签本身存在噪声再考虑调损失函数权重。7. 项目结构、复现步骤和常见报错排查7.1 项目目录和运行流程项目代码按照下面的目录组织remote_sensing_segmentation/ ├── config/ │ └── config.yaml # 配置文件模型选择、训练参数都在这里 ├── data/ │ ├── train/ # 训练图像和标签 │ ├── val/ # 验证图像和标签 │ └── test/ # 测试图像 ├── models/ │ ├── deeplab_v3plus.py # DeepLab V3模型定义 │ ├── unet.py # UNet模型定义 │ └── backbone.py # 主干网络模块 ├── utils/ │ ├── dataset.py # 数据加载和预处理 │ ├── loss.py # 损失函数 │ ├── metrics.py # 评估指标 │ └── visualize.py # 可视化工具 ├── train.py # 训练入口 ├── predict.py # 推理入口 ├── evaluate.py # 评估入口 └── README.md # 项目文档说明复现流程很简单先把Python 3.8以上环境配好安装依赖包PyTorch、torchvision、opencv-python、numpy、albumentations、PyYAML、sklearn然后修改config.yaml里的数据和路径配置执行python train.py训练完成后执行python evaluate.py和python predict.py。7.2 高频报错和排查经验分享几个我在实际跑这个项目时遇到过的高频报错读取图像后出现全黑或全白的图。检查是否有16位深度的TIFF图直接用cv2的imread可能只读到8位。解决方案是用tifffile库读取。训练loss是NaN。绝大概率是标签里有超出类别范围的像素值或者归一化除零。检查标签图的像素值集合是否和类别数对得上。显存溢出。优先看我前面说的三段优化方案降batch、开AMP、梯度累积。训练没有任何效果loss不降。先确认数据增强里面没有把标签图当成图像做归一化标签图一定要保持像素值不变。推理时原图是2000×2000切块预测后拼接出现接缝。用重叠切图加拼接权重的方式可以解决。7.3 修改配置切换模型项目最人性化的地方是切换模型不需要改代码。config.yaml里通过一个model_name参数控制model_name: deeplab_v3plus # 可选 deeplab_v3plus / unet / unet_depthwise backbone: resnet50 # 可选 resnet50 / mobilenetv2 num_classes: 5 input_size: 512 batch_size: 8 epochs: 80 lr: 0.01 use_amp: true这样做的目的很简单对比实验最怕模型之间数据预处理不一致统一配置后切换起来没有任何心智负担。8. 我踩过的一些坑以及这个项目还能怎么扩展我在这个项目上投入的时间比预想中多了很多踩的坑主要集中在数据预处理和训练稳定性上。最值得说的一点是遥感数据的标签图精度没那么高尤其是建筑物边缘经常有错标或漏标。遇到这种情况不要指望模型无所不能mIoU到某个程度以后就很难再涨了。这时候与其死磕模型结构不如花时间清理数据把标签严重错误的地方修正一下收益远比换一个更大更强的网络高。第二个经验是如果训练的模型在验证集上表现不错但换到另一批遥感影像上效果骤降先不要怀疑模型过拟合先检查两批影像的拍摄季节、传感器类型和辐射校正方式是否一致。遥感数据本身就存在很大的域差异深度学习模型对数据分布非常敏感。如果项目要求跨场景泛化最有效的方案是加入更多来源的训练数据而不是单纯调模型。第三个很实际的经验是工程化的代码结构比模型本身重要。我一开始把所有代码堆在一个文件里调参调得快崩溃。后来拆成配置文件加模块化文件每个部分独立调试效率提高了很多。这也是这个项目最终整理成现在这个结构的原因。后续想扩展的话我觉得有几个方向值得尝试一是把主干网络换成Swin Transformer或者EfficientFormer对比CNN和Transformer在遥感分割上的差异二是在后处理阶段加入条件随机场CRF或者分割精修网络把边缘质量再提高一点三是尝试半监督训练用大量无标签遥感影像做一致性正则化缓解标注数据稀缺的问题。这些都是可以基于现有代码继续往下走的方向。最后再分享一个小技巧训练的时候把每个epoch的验证集预测图定期导出来看一眼。我每次都是肉眼先看几张图再去看mIoU数字。因为指标只能告诉你整体的好坏但图能告诉你模型到底在什么地方犯了错。有时候mIoU涨了但预测图里出现了一堆不该出现的细碎噪声这时候你反而需要注意可能模型在用小尺度纹理过拟合而不是学到了真正的地物语义。遥感分割和普通图像分割最大的区别就在这里——地物是有空间结构和上下文逻辑的评估的时候一定要图数结合才能判断模型是真的变好了还是只是数字变好看了。本文还有配套的精品资源点击获取
返回列表