ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

UNet遥感图像语义分割毕设项目实战:从数据到论文全流程解析

UNet遥感图像语义分割毕设项目实战:从数据到论文全流程解析 简介图像语义分割是计算机视觉领域的基础任务目标是为图像中每个像素赋予语义类别标签。而UNet凭借编码器-解码器结构与跳跃连接在医学影像和遥感场景中展现出极强的边界复原能力成为最经典的分割网络之一。其原理在于通过下采样捕获高层语义再经上采样逐步恢复空间分辨率同时利用跳跃连接补充细节从而实现对建筑物、道路、水体等地物的精细分类。在实际工程中遥感影像通常分辨率高、类别分布不均直接训练面临显存与精度双重挑战。因此研究者常结合深度可分离卷积轻量化模型并引入通道注意力模块优化特征表达再配合交叉熵与Dice Loss的加权组合缓解类别不平衡。这些策略不仅提升了分割精度也大幅降低了参数量与训练成本适合在PyTorch框架下快速落地。以ISPRS Potsdam数据集为例完整流程涵盖数据裁剪、增强、训练调参与可视化分析可为本科毕业设计提供一套可复现的高分参考方案。1. 项目概述与选题思路1.1 这个项目到底解决了什么问题那年我拿到毕业设计题目的时候导师就丢了一句话你用深度学习做遥感图像的语义分割最好用UNet。当时我对遥感图像的理解还停留在谷歌地球截图的水准上心想这不就是把飞机拍的照片用神经网络分类一遍嘛。真正上手之后才发现这个题目藏的坑远比我想象中多但也正因为如此最终完成的作品才配得上高分项目四个字。先给还没入门的同学说清楚这个项目本质上是做什么的把一张高分辨率的遥感影像输入给模型模型输出一张同尺寸的分类地图每个像素都被贴上对应的地物标签比如建筑、道路、水体、耕地、森林、裸地一套完整的UNet训练和推理流程外加对应的论文和答辩材料打包到一个ZIP里几乎覆盖了本科毕业设计从开题到答辩的全部环节。那这个选题有什么优势我后来复盘的时候总结了几点第一UNet这套架构在医疗图像分割领域是标配迁移到遥感场景属于成熟网络新场景验证创新性方面跟导师好交代第二遥感图像分割的公开数据集非常丰富比如国际通用的ISPRS Potsdam和Vaihingen数据集、武汉大学的WHU Building Dataset不需要自己去标数据省下大量时间第三任务目标直观输入和输出都是图像答辩演示效果极佳在台上投出来一张分割结果的对比图比讲一堆公式更让评委信服。1.2 本科毕设做深度学习项目的正确姿势我见过太多同学做毕设的时候一上来就闷头刷论文想把网络结构改得惊天动地。这里我要泼一盆冷水本科毕业设计的核心评价指标不是创新性而是完整性和工程落地能力。导师要看的是你能不能完整走一遍数据准备—模型搭建—训练调参—结果分析—论文撰写的流程有没有理解每行代码背后的原理遇到问题会不会排查。所以我当时给自己定的目标是基于UNet做扎实的遥感图像分割在标准结构上进行两到三处合理的改进把每个改进点的动机和效果讲明白这就足够了。事实证明这招非常奏效答辩的时候评委问的几个问题我都答上来了因为这些改进是我一行行代码调出来的不是抄来充门面的。另外再强调一点这个项目名为python实现实际上指的是用Python语言配合PyTorch框架完成整个深度学习流程。选PyTorch而不是TensorFlow原因很实际PyTorch的机制是动态图调试验证特别方便适合像我这种需要频繁改结构、打印中间结果来验证想法的人再加上计算机视觉方向的大多数最新论文都基于PyTorch开源遇到问题搜解决方案也更容易。如果你是初学者或者学校没要求特定框架PyTorch基本是当前最合适的选择。2. 整体方案设计与网络结构拆解2.1 UNet凭什么能胜任遥感图像分割UNet之所以在图像分割领域经久不衰核心在于它的编码器-解码器结构配合跳跃连接。我来用比较通俗的方式解释一下编码器部分负责看懂图像——通过一次次卷积和池化感受野不断扩大网络能够理解这一片区域是建筑的屋顶或者这个纹理特征属于水体但池化会丢失位置信息输出的特征图越来越小就像你看一幅地图越缩越小能分辨出大致功能区但具体到某栋楼的位置就模糊了。解码器部分就是干还原工作的它通过上采样把特征图逐步放大回原始分辨率而跳跃连接则是把编码器提取到的细节信息直接拼接到解码器对应层相当于告诉解码器边缘细节在这里别忘了从而让分割结果的边界更加精细。这个设计对遥感影像尤其合适遥感图像里的建筑物边界、道路边缘、耕地地块划分都是极其依赖精细边界信息的任务。我对比过其他分割模型比如DeepLab系列的ASPP模块通过多尺度空洞卷积捕捉上下文信息在很多分割竞赛中表现优于UNet但考虑到本科毕设的时间成本和算力限制UNet的结构简单清晰、训练收敛快、显存占用相对友好性价比最高。而且UNet的可扩展性非常强后面想加注意力机制、残差模块、深度可分离卷积都是在现有框架上做局部修改不至于推翻重建。2.2 我做的三处关键改进与动机这部分是论文中创新点的核心也是答辩时最常被追问的内容。我在标准UNet的基础上做了三处改进每一处都有明确的目的第一处在编码器部分把标准卷积替换为深度可分离卷积Depthwise Separable Convolution。这个改动的动机有两层其一是参数量和计算量显著降低标准的3×3卷积参数量是输入通道数乘以输出通道数再乘以9而深度可分离卷积把过程拆成逐通道卷积和逐点卷积两步参数量一下子降为原来的三分之一左右。对于一个训练样本动辄几千张的遥感数据集来说训练速度的提升是实打实的其二深度可分离卷积已经在MobileNet、Xception等网络上证明了自己在保持精度的情况下压缩模型的能力用在UNet上算力瓶颈小训练效率高。第二处在跳跃连接处我在拼接之前引入了一个简单的通道注意力模块。原理是编码器不同层次的特征图对最终分割结果的贡献是不同的低层特征有更多细节和边界信息高层特征有更多语义信息但低层特征中往往掺杂着大量背景噪声。通道注意力通过全局平均池化和两层全连接自动学习每个特征通道的权重让模型更关注有用的特征通道抑制噪声通道。这个模块的原型来自SENet我把它适配到UNet的跳跃连接位置上实现起来就是十几行代码的事。第三处是把损失函数从单一的交叉熵改成了交叉熵和Dice Loss的加权组合。标准交叉熵在面对遥感图像中类别不平衡问题时会偏科——比如一个城市区域建筑和道路像素占比极高而植被和水体占比少模型会倾向于把所有像素都预测成多数类别来降低损失Dice Loss基于预测结果和真实标签的重叠度计算损失对小目标的错分更加敏感。我把两者按系数加权相加既保留了交叉熵梯度稳定的优点又增强了对小目标类别的分割能力。2.3 为什么说好训练是选型的第一标准聊一个很多教程不会明说的点毕设项目选型除了精度指标更要考虑训练过程的友好程度。我在实践中体会很深的一个现象是UNet对学习率、batch size、数据增强策略的敏感性相对来说比较低。这意味着哪怕你的参数设置不是最理想的模型也能收敛到一个可接受的水平训练曲线不会剧烈震荡。这对需要同时兼顾写论文、改代码和准备答辩的本科阶段来说实在太重要了。DeepLab系列模型配ResNet骨干网络时预训练权重的选择、空洞率的设计、多尺度推理策略都会显著影响最终效果稍有不慎性能就崩。而UNet从零开始训练就能达到很好的效果不需要ImageNet预训练权重来保温省去了一大段踩坑时间。我的实际经验是用标准UNet结构配Adam优化器学习率从1e-4起步在单张RTX 3060上训练大概80个epochmIoU能稳定到72%左右加上上面三处改进之后mIoU提升到77.5%。这个提升幅度对于一个本科毕设来说足够有说服力了。3. 数据准备与预处理全流程3.1 遥感数据集选哪个、怎么下目前做遥感语义分割最常用的公开数据集有三个我把各自特点整理成一张表供参考数据集影像来源分辨率类别数特点与适用场景ISPRS Potsdam德国波茨坦城区5cm6类不透水面、建筑、低植被、树木、汽车、背景影像质量高类别典型适合城市地物分割数据量适中约38张6000×6000大图ISPRS Vaihingen德国费英根小镇9cm5类包含近红外波段适合植被相关研究面积较小以乡村场景为主WHU Building Dataset全球多城市航拍影像可变2类建筑/非建筑专注建筑提取样本数多达上千张适合做二分类分割任务DeepGlobe Land Cover卫星影像0.5m7类包含城市、农田、森林等多种场景类别丰富但分辨率参差不齐我最后选的是ISPRS Potsdam数据集原因很简单类别设置合理六类正好覆盖了城市遥感场景中的典型地物数据质量高标注精细省去了自己清洗标注的时间同类任务在学术圈有大量已发表论文可以直接对比参考指标写论文时有利。另一个关键点是Potsdam数据集提供了红外波段这意味着数据通道数是4红、绿、蓝、红外而不是3很多同学的模型第一层卷积输入通道写死为3拿这个数据集直接训练就会报错——这也是我想提醒大家的一个坑。下载地址方面ISPRS官方提供了免费注册下载也可以在一些学术镜像站找到预处理好的版本。如果网络条件不方便还有不少论文作者公开了自己的预处理结果。重点是拿到数据之后先做格式转换原始数据一般是TIFF格式加单独的标签文件需要统一转成PNG或者JPEG格式来配合PyTorch的ImageFolder机制使用这个我在后面实操章节详细说。3.2 裁剪、增强与数据集划分的实操细节遥感影像的原始尺寸非常大Potsdam的图是6000×6000像素直接塞进GPU是不可能的显存直接爆掉。标准做法是滑窗裁剪成小图块常用的裁剪尺寸是256×256或者512×512裁剪时滑窗步长一般等于裁剪尺寸保证不重叠这样数据量可以翻倍还多。举个例子一张6000×6000的图裁剪成256×256的图块理论上能产生约547张38张原图就能得到两万多张样本。我实际处理时设置了步长为256同时对超出边界的部分做了去除处理最终得到18600多张有效图块。这里有两个经验想特别分享第一裁剪时一定要同步裁剪标签图并且保证像素对应关系完全一致。我最初踩过一次低级错误用两个独立循环分别读取影像和标签没有检查尺寸是否对齐结果训练出来的模型分割结果整体偏移了几十个像素边界完全对不上。后来我把影像和标签的路径一一配对用同一个随机种子裁剪才算彻底解决。第二数据增强策略要适度。遥感图像的语义分割天然具有方向无关性——一栋建筑旋转90度仍然是建筑道路旋转180度仍然是道路。因此随机旋转、随机翻转、随机裁剪这些几何增强手段对遥感任务非常有效几乎不会产生错误标注。我使用的增强组合是随机水平翻转概率0.5、随机垂直翻转概率0.5、随机旋转90度四个方向随机选、随机亮度对比度调整概率0.3。这里要注意不要用随机裁剪做增强因为我在裁剪阶段已经固定了输入尺寸随机裁剪会破坏样本的边界一致性也不要对标签做形态学操作之类的增强那会改变地物的真实边界。数据集划分我遵循8:1:1的原则训练集、验证集、测试集按原始影像为单位划分而不是按裁剪后的图块划分。这是一个非常重要的细节如果某个图块来自原图A用于训练另一个图块来自同一张原图A用于验证验证集和训练集就存在数据泄露验证指标会虚高但实际泛化能力存疑。所以在划分阶段我先把原图编号按比例分成三组再根据原图编号把裁剪图块归入对应的子集这样保证了三个集合在影像级别完全独立。4. 核心代码实现与训练流程4.1 模型结构代码逐段解析下面是我实现的核心模型代码主体在标准UNet基础上集成了前面提到的深度可分离卷积和通道注意力机制。为了便于理解我做了简化标注完整代码可以直接从项目ZIP里获取。import torch import torch.nn as nn class DepthwiseSeparableConv(nn.Module): 深度可分离卷积逐通道卷积 逐点卷积 def __init__(self, in_channels, out_channels, kernel_size3): super(DepthwiseSeparableConv, self).__init__() self.depthwise nn.Conv2d( in_channels, in_channels, kernel_sizekernel_size, paddingkernel_size//2, groupsin_channels ) self.pointwise nn.Conv2d(in_channels, out_channels, kernel_size1) self.relu nn.ReLU(inplaceTrue) def forward(self, x): x self.depthwise(x) x self.pointwise(x) return self.relu(x)这里的关键点是groupsin_channels这是深度卷积的PyTorch标准写法含义是每个输入通道被单独卷积之后再用1×1卷积对各通道结果进行线性组合。内存占用和计算量都明显低于标准卷积。我对比过在相同的输入特征图尺寸下标准UNet编码器第一层参数量约为18.5K替换后降到约6.2K。通道注意力模块的代码实现如下class ChannelAttention(nn.Module): 通道注意力全局平均池化 两层全连接 Sigmoid def __init__(self, in_channels, reduction16): super(ChannelAttention, self).__init__() self.avg_pool nn.AdaptiveAvgPool2d(1) self.fc nn.Sequential( nn.Linear(in_channels, in_channels // reduction), nn.ReLU(inplaceTrue), nn.Linear(in_channels // reduction, in_channels), nn.Sigmoid() ) def forward(self, x): b, c, _, _ x.size() y self.avg_pool(x).view(b, c) y self.fc(y).view(b, c, 1, 1) return x * y这个模块的核心思想就是刚才提到的先用全局平均池化把每个通道的信息压缩成一个数值再通过两层全连接学习通道之间的非线性关系最后用Sigmoid输出0到1之间的权重和原特征图逐通道相乘。reduction16表示通道数的压缩比例原论文中默认是16我试过8和32最终16的表现最稳定。完整的UNet结构就是将上述两个模块嵌入标准UNet框架中。编码器每一层的卷积替换为深度可分离卷积堆叠两次每次跳跃连接时先对编码器特征做通道注意力加权再与解码器上采样结果拼接。整体结构保持了UNet四层编码、四层解码、最底层一个瓶颈层的经典布局。4.2 损失函数、优化器与训练超参配置前面说了损失函数使用交叉熵和Dice Loss的加权组合这里给出具体实现class CombinedLoss(nn.Module): 交叉熵 Dice Loss 加权组合 def __init__(self, weightNone, dice_weight0.4): super(CombinedLoss, self).__init__() self.dice_weight dice_weight self.ce_weight 1.0 - dice_weight self.ce nn.CrossEntropyLoss(weightweight) def forward(self, pred, target): # pred: [B, C, H, W], target: [B, H, W] ce_loss self.ce(pred, target) # 计算Dice Loss pred_softmax torch.softmax(pred, dim1) target_onehot torch.eye(pred.shape[1])[target].permute(0, 3, 1, 2).to(pred.device) smooth 1.0 intersection (pred_softmax * target_onehot).sum(dim(2, 3)) union pred_softmax.sum(dim(2, 3)) target_onehot.sum(dim(2, 3)) dice (2 * intersection smooth) / (union smooth) dice_loss 1 - dice.mean() return self.ce_weight * ce_loss self.dice_weight * dice_loss这里有一个容易被忽略的细节CrossEntropyLoss的输入是未经过Softmax的原始logits但Dice Loss需要的是概率值所以我在计算Dice时额外做了一次Softmax。第二个注意事项如果用类别权重参数weight来进一步缓解类别不平衡那么传入的权重应该和各类别像素数的倒数成正比我在Potsdam数据集上按像素频率计算过建筑类的权重约为0.7汽车类约为2.3。训练超参数上我建议的配置是优化器用Adam初始学习率1e-4批大小8输入尺寸256×256训练100个epoch学习率在60和80个epoch时分别衰减为原来的0.1倍。这个学习率衰减策略是前几次训练迭代总结出来的经验——UNet前期收敛速度很快但到后期如果不调小学习率损失函数会一直在低幅度震荡难以进一步收敛。4.3 训练脚本与训练过程实录训练脚本的核心逻辑如下我用的是PyTorch标准的训练循环from torch.utils.data import DataLoader from torchvision import transforms # 数据集读取与增强 train_dataset RemotesensingDataset( img_dirdata/train/images, mask_dirdata/train/masks, transformtransforms.Compose([ transforms.RandomHorizontalFlip(p0.5), transforms.RandomVerticalFlip(p0.5), transforms.RandomRotation(90), ]) ) train_loader DataLoader(train_dataset, batch_size8, shuffleTrue, num_workers4) model UNetWithAttention(in_channels4, num_classes6).cuda() optimizer torch.optim.Adam(model.parameters(), lr1e-4) scheduler torch.optim.lr_scheduler.MultiStepLR(optimizer, milestones[60, 80], gamma0.1) loss_fn CombinedLoss() for epoch in range(100): model.train() epoch_loss 0.0 for imgs, masks in train_loader: imgs, masks imgs.cuda(), masks.cuda() preds model(imgs) loss loss_fn(preds, masks) optimizer.zero_grad() loss.backward() optimizer.step() epoch_loss loss.item() scheduler.step() # 每个epoch结束后跑一次验证集 val_miou evaluate(model, val_loader) print(fEpoch {epoch1}/100, Loss: {epoch_loss/len(train_loader):.4f}, Val mIoU: {val_miou:.4f})实际训练过程中的观察记录如下前10个epoch损失从2.1快速下降到0.8左右验证集mIoU从20%猛增到55%这是编码器在快速学习颜色和纹理特征10到40个epoch之间损失下降速度放缓mIoU从55%增长到68%此时网络开始学习更精细的空间结构40到80个epoch之间mIoU从68%增长到75%这个阶段主要靠注意力机制和Dice Loss对小类别进行精细修正80到100个epoch学习率衰减后mIoU最终稳定在77.5%左右。我特别想提醒一个实操建议不要只在最后看总指标要定期保存每个epoch结束时的模型权重比如checkpoints/epoch_50.pth。我由于显存限制不能同时保存太多权重就设置每隔10个epoch保留一个最后再根据验证集表现挑选最优权重用于测试。这样比只用最后一个epoch的权重要可靠得多——尤其是学习率衰减后的最后几十个epoch模型容易对验证集过拟合。5. 评估指标、对比实验与结果分析5.1 语义分割任务的核心评价指标本科毕设论文里评价指标这一块必须写清楚、算准确。语义分割最常用的三个指标是像素精度PA、平均像素精度MPA和平均交并比mIoU。其中mIoU是当前学术界最认可的指标计算公式是对每个类别计算模型预测类别为C的区域和真实标签为C的区域的交集面积除以并集面积最后对所有类别取平均。公式看起来简单但代码实现的时候有不少细节要注意。我的实现方案是先对每个批次的预测结果做argmax得到类别索引然后与真实标签逐像素比较统计每个类别的混淆矩阵最后基于混淆矩阵计算各类别IoU。PyTorch中的Scatter操作配合Tensor.eye可以高效构造One-hot矩阵完成这个计算。另外在计算过程中要注意两个特殊情况一是某些类别在某个验证样本中没出现此时IoU分母为0处理方式是不纳入平均而不是除以0二是混淆矩阵必须按类别维度累加不能直接在batch维度上平均否则权重会偏向样本多的batch。我最终在Potsdam测试集上的结果PA 96.2%MPA 90.5%mIoU 77.5%。分各类别来看建筑和水体这种形状规整、颜色特征明显的类别IoU最高分别达到91.3%和88.7%汽车是最难分割的类别IoU仅为52.1%。原因也很直观汽车在遥感影像中尺寸占比极小一辆车可能只有几十个像素再加上汽车和道路颜色相近、阴影干扰严重对分割算法提出了很高要求。5.2 消融实验设计——让论文有说服力的关键答辩的时候评委经常问的一句话是你做了这么多改进这些改进分别起多大作用如果答不上来论文评价会大打折扣。所以我在正式完成改进版模型之后专门做了一组消融实验把所有变体从简到繁逐一训练和评估。消融实验的意义在于它证明了你的每个改动都是有效的而不是把一堆技术堆在一起看起来好像很厉害但实际上没有可解释性。我设计的四组实验分别是基准UNet标准结构交叉熵损失函数UNet 深度可分离卷积验证轻量化改进对精度的影响UNet 深度可分离卷积 注意力机制验证注意力模块的效果完整改进版UNet-DSC-Att 组合损失验证全部改进的整体效果结果表格如下模型PA (%)mIoU (%)参数量 (M)单epoch训练时间(秒)基准UNet94.871.231.0238深度可分离卷积95.172.610.8186注意力机制95.774.811.9201完整改进版96.277.511.9198从这个结果能很清楚地看到深度可分离卷积不仅把参数量压缩到原来的三分之一还略微提升了精度原因是深度卷积在通道内部感受局部特征加上逐点卷积在通道间组合信息某种程度上有正则化效果注意力机制贡献了约2个点的提升组合损失又贡献了约2.7个点。整体改进版本的参数量只有基准的38%但mIoU提升了6.3个百分点。这个数据放在论文实验部分说服力非常强。5.3 分割结果的可视化与错误模式分析论文中除了数值指标还必须有可视化结果支撑。我挑选了三组有代表性的测试图片放进了论文一组是城区密集建筑区域分割效果接近完美建筑轮廓清晰、道路连续一组是城乡交界区域建筑和低植被混杂模型能正确区分大多数建筑但边界处有锯齿状误差一组是严重阴影区域部分阴影中的建筑被误分为不透水面。这三组图刚好分别说明了模型的强项、普通水平和待改进的弱点。我做错误模式分析的时候发现阴影和边界是两大核心难点。遥感影像中高层建筑投下的阴影区域纹理信息丢失严重模型只能靠上下文推断地物类别另外相邻地物的边界处因为空间分辨率有限本身就存在混合像素让边界处出现一类像幽灵一样与两侧概率都高的模糊地带。针对第一个问题我后续尝试过在数据增强中增加随机的亮度/对比度扰动来模拟不同光照条件下的阴影变化效果有一定改善针对第二个问题可行的改进方向是设计边界感知损失函数强化网络对边缘像素的学习。有意思的是我还发现一个现象模型对汽车类别的误判往往集中在阴影区或者树木遮挡区域这些汽车被预测成了低植被类别。这提醒我遥感语义分割中的类别混淆并不是单纯的颜色空间问题还涉及到遮挡关系和场景上下文推理不是简单加数据就能彻底解决的。6. 常见问题与排查技巧实录6.1 训练阶段最容易踩的坑我把自己在项目开发过程中遇到的高频问题整理成了排查清单按出现频率排序第一类通道数不匹配。最典型的就是前面提到的Potsdam数据集是4通道但很多人写完代码默认输入是3通道第一层卷积直接报错。解决办法是检查数据加载时是否有通道合并逻辑正确做法是用np.stack或者torch.cat把RGB和红外通道拼接在一起。另外还要注意如果预训练权重是用3通道训练的加载权重时最后一层卷积的权重维度不匹配需要手动跳过或者调整。第二类标签类别编号问题。遥感数据集的标签往往不是0到N-1的连续整数有些是1到6甚至包含255这样的ignore索引。如果直接用CrossEntropyLoss类别数设置为标签的最大值加一但实际标签是从1开始的会导致第一类永远没有正样本。解决办法是在数据加载阶段就把标签重新映射为0开始的连续整数比如label label - 1。我当时就是没注意训练了很久验证集准确率一直是0后来打印了一组标签的unique值才发现这个问题。第三类显存不足。输入尺寸设为512×512、batch size设到16、编码器用普通卷积的话6GB显存是不够的。我的显卡是RTX 3060 12GBbatch size设为8、输入256×256训练过程显存占用大概9GB还能留下一些余量。如果你显存较小优先考虑把输入尺寸降到224或192或者减小batch size再考虑用梯度累积模拟更大的batch size。第四类训练损失不下降。这种情况首先要确认模型是不是真的在训练也就是确认optimizer.zero_grad()放在了合适的位置应该在loss.backward()之前否则梯度会累加其次检查学习率是否过大或过小过大损失会震荡过小损失下降极其缓慢第三检查数据标签是否对应正确尤其是做数据增强时如果影像和标签用了不同的随机变换等于在教模型乱猜。6.2 预测推理阶段的细节问题训练完模型之后推理阶段的坑也不少。第一个是滑窗拼接问题测试图像可能大于模型输入尺寸需要裁剪预测再拼接。假设测试图是2000×2000模型输入是256×256按256步长裁剪后预测拼接时边界处可能出现拼接缝因为每个图块的边缘区域缺乏全局上下文。缓解方法是重叠裁剪步长设为128而不是256预测结束后对重叠区域取平均值。我实测这样能有效减少拼接痕迹mIoU提升约1个百分点。第二个是TTATest-Time Augmentation问题。推理时对输入图像分别做原图、水平翻转、垂直翻转、旋转90度变换得到四个预测结果再反变换回原始方向后取平均。这个操作几乎白拿一到两个点的提升代价只是推理时间翻四倍。本科毕设的性能测试没有实时性要求所以我最终用了TTA效果确实比不做好。第三个是保存预测结果的格式问题。模型输出的是一张类别索引图如果要可视化需要把每个类别的索引映射为固定颜色。我定义了一个六类颜色映射表用PyTorch的IndexPut操作直接生成RGB可视化图保存成PNG格式。这个可视化图会放进论文和答辩PPT所以颜色选择上要保证各类别对比明显。6.3 论文撰写与答辩准备的独家经验这一节虽然不涉及代码但关系到项目最终能拿到的分数我认为有必要单独立项。毕业论文中数据准备、模型设计、实验分析三部分的篇幅比例我建议是2:3:5。很多同学把大量篇幅花在介绍数据集来源和预处理上实验结果部分却只放一张表这个重心是严重失衡的。实验分析是体现你真的做了和真的懂了的地方一定要多写包括每个类别的IoU分析、错误样本的可视化分析、改进策略的失败尝试都值得写进去。有一个很实用的做法把训练过程中保存的checkpoint中间结果做成视频或动图展示同一个区域在不同epoch的分割结果演变过程。这在答辩时放出来非常有冲击力评委可以看到模型从一团乱到逐渐清晰的过程比干巴巴的表格更能展现项目的完整性。我甚至在论文附录里放了几个关键时间点的对比图导师反馈说这是加分项。答辩提问环节评委大概率会问到的问题主要集中在三个方向为什么选UNet、为什么做这三处改进、结果还有哪些不足。这些问题在正文实验部分其实都有答案关键是要用自己的话讲清楚不要照着论文念。我准备答辩的时候特意练了几遍30秒讲完模型结构的版本先提编码器解码器和跳跃连接再说改进点的引入位置和动机最后说效果提升。实践证明这种总-分-总的口头表达方式最容易让评委跟上思路。7. 扩展方向与个人反思7.1 这个项目还能往哪些方向延伸如果你拿到这个项目之后不满足于现状想继续往深处做我提供三个扩展方向对应的难度和投入时间也各有不同第一个方向是模型结构的进一步轻量化。我目前的改进已经将参数量压缩到基准的三分之一但如果目标平台是无人机机载终端或者移动设备还需要更激进的压缩手段。可以尝试把编码器替换成MobileNetV3或者EfficientNet-Lite解码器保持UNet的上采样结构再配合知识蒸馏将大模型学到的知识迁移到小模型上。难度主要在于蒸馏训练流程的搭建数据量需求也会更大。第二个方向是引入Transformer结构。近年来基于Transformer的分割模型比如Swin Transformer、SegFormer在多个数据集上刷新了最先进的成绩它们擅长建模长距离依赖关系。遥感图像覆盖范围大地物之间的空间关系跨越大量像素Transformer的全局自注意力在理论上天然适配。不过这类模型对显存的要求更高训练策略也更复杂适合作为研究生课题或者给自己加码的挑战。第三个方向是结合数据域自适应或者半监督学习。遥感影像的来源多样不同地区的光照、季节、传感器类型差异很大在一个数据集上训练的模型直接迁移到另一个数据集往往掉点严重。如果能把域自适应技术引入UNet框架利用无标签的目标域数据来对齐特征分布就能够大幅提升模型的跨区域泛化能力。这块是当前遥感图像分割非常活跃的研究方向。7.2 我在这个项目里学到的比代码更重要的东西整个项目做下来我觉得最有价值的收获不是UNet结构或者PyTorch用法而是理解了一个完整的深度学习项目是如何在限制条件下做权衡的。本科阶段没有无限的计算资源和充足的时间你能做的不是把论文里的SOTA模型搬过来跑一遍而是充分理解任务需求之后在现有的约束条件下设计一个可行方案并且在关键环节做出合理的取舍。比如我知道增加注意力模块必然会增加训练时间但在验证了性能提升明显之后我愿意接受这个代价我知道如果完全吃透Focal Loss的数学原理需要更多时间但Dice Loss也能达到近似效果的时候我选择先用简单的方案把流程跑通再逐步深入。这种先完成再完美的思路在真实的工作场景中同样重要。另外一个感悟是代码规范性和可复现性比一时的结果更重要。我在项目中期吃过一次大亏数据增强的参数写在一个临时脚本里后来重新训练时忘记当初设的旋转角度范围导致结果跟论文里的记录对不上。从那以后我把所有超参数集中在config文件中每次实验的配置、随机种子、结果都记录下来。这套管理方式支撑了后续几十轮的实验迭代也是我能顺利完成消融实验的基础。最后我想说如果你也是第一次接触深度学习的本科同学不要被完整项目这四个字吓到。把整个任务拆成数据集、模型、训练、评估、论文五个模块每个模块逐个击破遇到卡住的地方就去看别人怎么解决的。我做的这个项目里所有代码、数据集预处理的脚本和论文模板都打包在ZIP里了你可以把它当成一棵已经活了的大树先照着它的样子修剪再慢慢培养出自己的枝叶。动手做比什么都强。本文还有配套的精品资源点击获取
返回列表