简介:这份资源面向计算机相关专业的本科生与课程设计学习者,提供一套基于UNet的遥感图像语义分割完整毕业设计项目,涵盖可运行源码与配套论文,适合作为毕业设计、期末大作业或课程设计参考,难度适中,评审分达98分。压缩包共68个文件,约46.94MB,包含6个Python源码文件与6个编译文件,覆盖模型定义、数据处理与训练预测流程;另有3个Jupyter Notebook用于数据集制作、训练与预测演示,5个tex论文章节文件及配套图表、字体与参考文献,以及若干png、svg示意图和说明文档,结构清晰便于按模块查阅。目前已有369人学习下载。读者可据此获得一套从数据准备、模型搭建到训练评估的完整方案,结合论文与Notebook快速理解UNet在遥感分割任务中的实现细节,并在此基础上修改适配自己的课题,节省从零搭建的时间成本。
1. 遥感语义分割毕设选型:为什么 UNet 在这个场景里仍是稳妥解
遥感图像语义分割这个方向,每年毕业设计季都会被翻出来做一遍。原因不复杂:数据可获取、任务定义清晰、指标好量化,而且 UNet 这种编码器-解码器结构在中等规模数据集上收敛稳定,不像 Transformer 系方案那样吃显存、吃调参经验。这份资源给的是一个完整的 Python 毕业设计项目,核心是基于 UNet 的遥感图像语义分割,包含可运行源码、Jupyter Notebook 实验流程,以及一份 LaTeX 排版的毕业论文。适合谁?正在做计算机视觉方向毕设、需要一份能跑通、能改、能写进论文的基线项目的同学,以及想快速搭一个分割 pipeline 验证自己想法的从业者。
遥感图像和自然图像有个本质区别:地物类别边界往往不规则,小目标密集,且同一类别在不同光照、季节下光谱差异大。UNet 的 skip connection 在这里价值很高——浅层特征保留边缘和纹理,深层特征负责语义判别,拼接后对细小地物更友好。这份资源没有堆砌花哨模块,而是把数据制作、训练、预测、可视化拆成独立脚本和 Notebook,结构上更像一个可复现的实验工程,而不是一次性 demo。论文部分用 LaTeX 分章节组织,chap1 到 chap5 对应绪论、相关技术、方法、实验、总结,Figures 和 Bibs 目录齐全,直接改校名和章节内容就能用。
2. 源码结构与运行链路:从 start_jupyter 到 train.ipynb 的完整走法
2.1 目录拆解与模块职责
拿到压缩包后先别急着跑,花五分钟把目录结构看清楚,后面排错会省很多时间。根目录下src是核心代码,demo放演示脚本,毕业论文是 LaTeX 源文件,start_jupyter.ps1和start_tensorboard.ps1是两个 PowerShell 启动脚本。
src里的文件分工如下:
| 文件 | 职责 | 关键内容 |
|---|---|---|
model.py | 网络定义 | UNet 主体,编码器下采样、解码器上采样、skip 拼接 |
data.py | 数据集加载 | Dataset 类、增强、归一化、DataLoader 封装 |
train.py | 训练入口 | 损失函数、优化器、epoch 循环、模型保存 |
utils.py | 工具函数 | 指标计算、可视化、路径处理 |
cnn.py | 卷积模块 | 可能包含基础 conv block 或对比模型 |
__init__.py | 包初始化 | 模块导出 |
create_dataset.ipynb负责把原始遥感影像和标注 mask 整理成训练格式,train.ipynb是交互式训练流程,predict.ipynb做推理和结果可视化。这种 Notebook + 脚本双轨的设计,好处是调试时能逐 cell 看中间结果,正式训练又能走train.py批量跑。
2.2 环境准备与依赖安装
遥感分割项目常见的翻车点不在模型,而在环境。PyTorch 版本、CUDA 驱动、torchvision 三者对不上,报错信息还特别隐晦。我一般会先建独立虚拟环境,再按官方推荐组合装。
# 创建虚拟环境,Python 版本建议 3.8 到 3.10 python -m venv venv_unet # Windows 激活 venv_unet\Scripts\activate # Linux / macOS 激活 source venv_unet/bin/activate # 安装 PyTorch,以 CUDA 11.8 为例,具体版本按自己显卡驱动选 pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118 # 安装其余依赖 pip install numpy opencv-python matplotlib jupyter tensorboard tqdm这里有个参数要留意:--index-url后面跟的 cu118 表示 CUDA 11.8 编译版本。如果你显卡驱动较老,换成 cu117 或 cpu 版本。装完后用下面这段验证:
import torch print("torch:", torch.__version__) print("cuda available:", torch.cuda.is_available()) print("device count:", torch.cuda.device_count())如果cuda available是 False,先别怀疑代码,九成是驱动或版本不匹配。CPU 也能跑,只是训练时间会从几十分钟拉到几小时,毕设演示够用,正式实验建议上 GPU。
2.3 数据制作:create_dataset.ipynb 怎么改
遥感数据一般有两种来源:公开数据集(如 LoveDA、DeepGlobe)和自己标注的影像。create_dataset.ipynb的作用是把影像和 mask 配对,切成固定尺寸 patch,生成训练/验证/测试划分。
常见做法是保持一个data目录,结构如下:
data/ images/ # 原始影像,jpg 或 tif masks/ # 标注 mask,png,像素值即类别 id train.txt # 训练集文件名列表 val.txt test.txt在 Notebook 里需要改的通常是三个变量:原始影像路径、mask 路径、输出 patch 尺寸。遥感影像分辨率高,直接整图训练显存扛不住,切成 256×256 或 512×512 是常规操作。切的时候要注意重叠采样,否则地物跨 patch 边界会被切断,影响小目标召回。
# 伪代码示意,实际以 notebook 内变量名为准 IMG_DIR = "data/images" MASK_DIR = "data/masks" PATCH_SIZE = 256 STRIDE = 128 # 小于 PATCH_SIZE,保证重叠 # 读取影像和 mask,按 stride 滑窗切块 # mask 用最近邻插值,避免类别 id 被插值成小数mask 处理是血泪经验最多的地方:千万不能用双线性插值缩放 mask,类别 id 会被平均成 0.5 这种无意义值。要么最近邻,要么先 one-hot 再插值。
2.4 训练配置:train.py 与 train.ipynb 的参数含义
train.py是正式训练入口,核心参数集中在文件头部。下面这段是典型配置,具体数值以源码为准:
# 训练超参数 BATCH_SIZE = 4 # 显存 8G 左右建议 4,12G 可上 8 LR = 1e-4 # UNet 常用初始学习率 EPOCHS = 100 # 遥感数据收敛慢,别设太小 NUM_CLASSES = 6 # 按自己数据集类别数改 IMG_SIZE = 256 # 损失函数:多分类常用 CrossEntropy,类别不均衡可加 Dice criterion = nn.CrossEntropyLoss() optimizer = torch.optim.Adam(model.parameters(), lr=LR) scheduler = torch.optim.lr_scheduler.StepLR(optimizer, step_size=30, gamma=0.1)参数怎么调:BATCH_SIZE受显存限制,报 OOM 就减半;LR太大 loss 震荡,太小收敛慢,1e-4 是安全起点;NUM_CLASSES必须和 mask 里最大类别 id 对齐,差一个就会报 index out of range。StepLR每 30 个 epoch 降一次学习率,是 UNet 训练里比较稳的策略。
启动训练:
# 脚本方式 python src/train.py # 或打开 notebook 逐 cell 跑 jupyter notebook train.ipynbstart_tensorboard.ps1用来启动 TensorBoard 看 loss 曲线和分割结果:
tensorboard --logdir runs如果 loss 前几个 epoch 不降反升,先检查 mask 的像素值是不是 0/255 而不是 0/1/2/3,这是最高频的翻车点。
3. UNet 模型细节与训练调参:编码器、skip connection 和损失函数怎么配合
3.1 编码器-解码器结构与 skip connection 的作用
UNet 的结构可以拆成三部分:下采样编码、上采样解码、skip 拼接。编码器每经过一个 stage,特征图尺寸减半、通道数翻倍,逐步提取从边缘到语义的抽象特征。解码器反过来,上采样恢复分辨率,同时把编码器对应层的特征拼过来。
为什么 skip connection 在遥感分割里特别关键?遥感影像里田块、道路、建筑这些地物,边界往往就是几个像素宽。如果只靠深层特征上采样,边界会糊成一团。skip 把浅层的高分辨率细节直接送到解码端,边界清晰度明显提升。代价是显存占用增加,因为要保存编码器每一层的特征图。
model.py里通常能看到这样的结构:
class UNet(nn.Module): def __init__(self, in_channels=3, num_classes=6): super().__init__() # 编码器:4 次下采样 self.enc1 = DoubleConv(in_channels, 64) self.enc2 = DoubleConv(64, 128) self.enc3 = DoubleConv(128, 256) self.enc4 = DoubleConv(256, 512) self.pool = nn.MaxPool2d(2) # 瓶颈层 self.bottleneck = DoubleConv(512, 1024) # 解码器:4 次上采样 + 拼接 self.up4 = nn.ConvTranspose2d(1024, 512, 2, 2) self.dec4 = DoubleConv(1024, 512) # ... 后续层省略 self.out = nn.Conv2d(64, num_classes, 1)DoubleConv一般是两次 3×3 卷积加 BN 加 ReLU。ConvTranspose2d做上采样,kernel 和 stride 都设 2,正好尺寸翻倍。拼接时通道数相加,所以dec4的输入是 512+512=1024。
3.2 损失函数选择与类别不均衡处理
遥感数据集天然类别不均衡:背景、植被占大头,某些稀有地物可能只占百分之几。纯 CrossEntropy 会让模型偏向多数类,稀有类 recall 很低。
常见做法有三种:
一是加权 CrossEntropy,给稀有类更高权重:
# 按类别频率的倒数设权重 class_weights = torch.tensor([1.0, 2.0, 5.0, 5.0, 3.0, 1.0]).to(device) criterion = nn.CrossEntropyLoss(weight=class_weights)二是 CrossEntropy + Dice 组合,Dice 对前景区域更敏感:
def dice_loss(pred, target, smooth=1e-6): pred = torch.softmax(pred, dim=1) # 对每个类别算 Dice 后平均 intersection = (pred * target).sum(dim=(2, 3)) union = pred.sum(dim=(2, 3)) + target.sum(dim=(2, 3)) dice = (2 * intersection + smooth) / (union + smooth) return 1 - dice.mean() total_loss = ce_loss + 0.5 * dice_loss三是 Focal Loss,压制易分类样本的梯度。三种没有绝对优劣,我一般先跑加权 CE 看 baseline,如果稀有类指标还是差,再叠 Dice。
3.3 训练过程监控与指标解读
训练时重点看三个东西:loss 曲线、验证集 mIoU、分割结果可视化。TensorBoard 里 loss 分 train 和 val 两条,如果 train 一直降而 val 反弹,就是过拟合,加数据增强或早停。
mIoU 是语义分割的主指标,计算方式是每个类别的 IoU 求平均。遥感数据里,如果某个类别的 IoU 长期为 0,先别调模型,去检查这个类别的 mask 是不是根本没出现在训练集里。
# 简化的 mIoU 计算 def compute_miou(pred, target, num_classes): pred = pred.argmax(dim=1) ious = [] for cls in range(num_classes): pred_mask = (pred == cls) target_mask = (target == cls) intersection = (pred_mask & target_mask).sum().item() union = (pred_mask | target_mask).sum().item() if union > 0: ious.append(intersection / union) return sum(ious) / len(ious) if ious else 0.0注意union > 0这个判断,如果某类在 batch 里完全没出现,直接跳过,否则会引入无意义的 0。
3.4 推理与可视化:predict.ipynb 怎么用
训练完保存的权重一般在runs或checkpoints目录。predict.ipynb加载权重,对测试集或单张影像做推理,输出彩色 mask 叠加图。
model.load_state_dict(torch.load("checkpoints/best.pth", map_location=device)) model.eval() with torch.no_grad(): output = model(img_tensor.unsqueeze(0).to(device)) pred = output.argmax(dim=1).squeeze().cpu().numpy() # 用调色板把类别 id 映射成颜色 color_mask = palette[pred]推理时model.eval()和torch.no_grad()两个都不能少,前者关掉 BN 和 Dropout 的训练行为,后者省显存。可视化时建议把原图、预测 mask、真值 mask 三张并排,一眼就能看出模型在哪类地物上翻车。
4. 避坑与排查:遥感分割项目里最容易翻车的五个点
4.1 现象:训练 loss 一直是 nan
原因:学习率过大,或者 mask 里有非法类别 id,导致 CrossEntropy 计算出 inf。也可能是输入影像没归一化,像素值 0-255 直接进网络。
解决:先把 LR 降到 1e-5 试一个 epoch;检查 mask 的 unique 值,确保都在[0, num_classes)范围内;输入做ToTensor后加 Normalize,均值方差用 ImageNet 的或自己数据集统计的。
4.2 现象:mIoU 卡在 0.2 上不去
原因:最常见的是 mask 和影像没对齐,切 patch 时用了不同的随机种子或不同的 stride。其次是类别 id 映射错了,比如背景是 0 但被当成忽略区域。
解决:抽几张训练样本,把影像和 mask 叠在一起可视化,确认地物边界对得上。再打印 mask 的直方图,看类别分布是否符合预期。
4.3 现象:显存 OOM,batch size 降到 1 还报错
原因:UNet 在 512×512 输入下,编码器特征图通道数到 1024,显存占用很大。如果还开了多尺度训练或保存了所有中间特征,更容易爆。
解决:把输入降到 256×256;用torch.cuda.amp混合精度训练;或者把编码器换成预训练的 ResNet 主干,减少参数量。混合精度大概能省 30% 到 40% 显存。
scaler = torch.cuda.amp.GradScaler() with torch.cuda.amp.autocast(): output = model(images) loss = criterion(output, masks) scaler.scale(loss).backward() scaler.step(optimizer) scaler.update()4.4 现象:验证集指标比训练集还高
原因:验证集太小,或者验证集和训练集有重叠 patch。遥感影像切 patch 时如果重叠采样又没按影像划分,同一张图的不同 patch 会同时出现在训练和验证里,造成数据泄漏。
解决:按整张影像划分训练/验证/测试,而不是按 patch 随机分。划分前先列出所有影像文件名,按 7:2:1 分好,再各自切 patch。
4.5 现象:TensorBoard 打不开或没有曲线
原因:start_tensorboard.ps1里的 logdir 路径和实际保存路径不一致,或者训练时没调用SummaryWriter。
解决:确认train.py里有writer.add_scalar("loss/train", loss, epoch)这类调用,logdir 指向runs目录的上一级。启动后浏览器开localhost:6006,如果端口被占,加--port 6007。
5. 论文 LaTeX 工程与实验复现:从 chap1 到 chap5 的落地技巧
5.1 LaTeX 论文结构拆解与编译
毕业论文目录下是标准的 LaTeX 工程:chap1.tex到chap5.tex分章节,Figures放图片,Bibs放参考文献,字体目录放中文字体。编译顺序一般是 xelatex → bibtex → xelatex → xelatex,四次才能把交叉引用和参考文献都解析对。
# 在毕业论文目录下 xelatex main.tex bibtex main xelatex main.tex xelatex main.tex如果报字体缺失,检查字体目录里的 ttf 是否被主 tex 文件正确引用。中文论文建议用 xelatex 而不是 pdflatex,否则中文会乱码。
5.2 实验数据与论文图表的对应关系
论文里的实验章节通常需要:数据集介绍表、模型结构图、loss 曲线、mIoU 对比表、分割结果可视化图。这份资源的Figures目录应该已经放了部分图,但用自己的实验数据替换时要注意:
| 论文位置 | 需要的图/表 | 来源 |
|---|---|---|
| chap3 方法 | UNet 结构图 | 可用 draw.io 重画或截图 model.py 结构 |
| chap4 实验 | loss 曲线 | TensorBoard 导出或 matplotlib 重绘 |
| chap4 实验 | mIoU 对比 | 训练日志里的验证指标 |
| chap4 实验 | 分割可视化 | predict.ipynb 输出 |
图表编号和正文引用要一致,LaTeX 里用\label和\ref自动管理,别手写编号。
5.3 把训练结果写进论文的具体步骤
先跑完训练,把train.py输出的日志保存成 csv 或直接截图 TensorBoard。然后在 chap4 里用 pgfplots 或直接插图片的方式放 loss 曲线。mIoU 对比表用 booktabs 宏包,三线表看起来更规范。
\begin{table}[htbp] \centering \caption{不同方法在测试集上的 mIoU 对比} \begin{tabular}{lcc} \toprule 方法 & mIoU & 像素准确率 \\ \midrule UNet (本方法) & 0.68 & 0.89 \\ FCN & 0.61 & 0.85 \\ SegNet & 0.63 & 0.86 \\ \bottomrule \end{tabular} \end{table}数据必须来自你自己的实验,别直接抄资源里的数字,评审老师一眼就能看出实验和论文对不上。
5.4 复现实验的一个实用习惯
我自己的习惯是:每次改完超参数或数据增强,先在train.ipynb里跑 5 个 epoch 看 loss 趋势,确认没有 nan 或震荡,再丢到train.py里跑完整训练。这样能省下大量等待时间。另外,checkpoint 文件名带上时间戳和关键参数,比如unet_lr1e4_bs4_ep100.pth,过两周回头看还能对上号。从那以后我每次开新实验都强制走一遍「小跑验证 → 全量训练 → 日志归档」的流程,翻车次数明显少了。希望帮到你。
本文还有配套的精品资源,点击获取