简介:这份毕业设计资源包围绕UNet神经网络在遥感图像语义分割中的应用展开,面向计算机视觉方向的高校学生与深度学习入门者,帮助其完成从模型搭建、训练到论文撰写的完整课题。包内共69个文件,约46.92MB,以png与svg图像、py源码、pyc缓存、tex论文源文件、ttf字体及ipynb交互笔记为主,涵盖数据预处理、模型定义、训练脚本、可视化启动脚本与论文各章节内容。资源中提供了完整的UNet实现代码、Jupyter实验笔记、TensorBoard可视化脚本以及毕业论文LaTeX源码,读者可据此复现遥感图像像素级分类流程,理解跳跃连接与双路径结构的设计思路,并参考IoU等评估指标完成结果分析。目前已有299人学习下载,适合需要系统掌握语义分割项目全流程、对照论文与代码查漏补缺的读者。
1. 拆开这个 UNet 遥感分割毕设包:它到底能帮你省下多少天
如果你正在做遥感图像语义分割方向的毕业设计,或者刚接手一个需要把卫星/航拍图按像素分类成建筑、水体、植被、道路的任务,这个压缩包值得先看一眼。它不是那种只丢一个train.py让你自己猜的"半成品",而是把数据制作、模型定义、训练、预测、可视化、论文 LaTeX 源文件全部打包在一起的一套完整工程。核心模型是经典的 U-Net 编码器-解码器结构,配合跳跃连接,专门对付遥感图像里目标尺度差异大、边界模糊、样本量有限这几个老大难问题。包里同时给了create_dataset.ipynb、train.ipynb、predict.ipynb三个 Notebook,意味着你可以不写一行新代码就把整条链路跑通一遍,先看到结果,再回头改结构。适合谁?适合时间紧、需要快速搭出可演示系统、同时还要写论文的本科生和低年级研究生。下面我按"这是什么 → 怎么跑 → 坑在哪 → 怎么改"的顺序,把包里每个文件的作用和实操路径拆清楚。
2. 环境与数据准备:从start_jupyter.ps1到create_dataset.ipynb
2.1 先看清目录结构再动手
拿到包先别急着pip install,花两分钟把目录过一遍,后面能少走很多弯路。根目录下src是核心代码,demo放示例数据,毕业论文是 LaTeX 论文源文件,两个.ps1是 Windows 下的启动脚本。src里的文件分工很明确:
| 文件 | 作用 |
|---|---|
model.py | U-Net 网络结构定义 |
data.py | 数据集加载与预处理 |
train.py | 训练主循环、损失、优化器 |
cnn.py | 卷积模块封装 |
utils.py | 指标计算、可视化等工具函数 |
__init__.py | 包初始化 |
__pycache__里是.cpython-37.pyc,说明作者用的是 Python 3.7。这个信息很关键——如果你本地是 3.10 或 3.11,某些老版本 PyTorch 的 API 可能对不上,建议直接建一个 3.7 或 3.8 的虚拟环境,别硬扛。
2.2 环境搭建与启动脚本
start_jupyter.ps1和start_tensorboard.ps1是 PowerShell 脚本,内容通常是激活环境后启动对应服务。我一般会先手动确认依赖,再决定要不要直接用脚本:
# 建虚拟环境,版本对齐作者用的 3.7/3.8 conda create -n unet_rs python=3.8 -y conda activate unet_rs # 核心依赖,版本按你显卡 CUDA 情况调整 pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118 pip install numpy opencv-python matplotlib tensorboard jupyter scikit-learn逻辑说明:torch和torchvision是模型训练的基础,opencv-python用于遥感图像的读写和增强,tensorboard对应包里的可视化脚本,jupyter用来跑三个 Notebook。参数上唯一要盯的是 CUDA 版本,cu118对应 CUDA 11.8,如果你显卡驱动较老就换成cu116或直接用 CPU 版(训练会慢很多,但毕设演示够用)。
装完后先跑start_jupyter.ps1,如果脚本里写死了绝对路径导致报错,直接手动jupyter notebook即可,不必纠结脚本本身。
2.3 用create_dataset.ipynb制作自己的数据集
遥感图像标注和语义分割数据集制作是很多人卡住的第一步。这个包给了一个create_dataset.ipynb,思路是把大图切成小 patch,再和对应的 mask 配对。常见做法是滑动窗口切图,窗口大小和 U-Net 的下采样层数挂钩——U-Net 通常做 4 次下采样,所以输入尺寸最好是 16 的倍数,256×256是最省心的选择。
import cv2 import numpy as np import os def slide_crop(img, mask, patch=256, stride=128): """滑动窗口切图,stride 小于 patch 可做重叠增强""" h, w = img.shape[:2] patches = [] for y in range(0, h - patch + 1, stride): for x in range(0, w - patch + 1, stride): img_p = img[y:y+patch, x:x+patch] mask_p = mask[y:y+patch, x:x+patch] # 过滤掉全背景的 patch,避免正负样本失衡 if mask_p.sum() > 0: patches.append((img_p, mask_p)) return patches逻辑说明:patch=256是输入尺寸,stride=128表示相邻 patch 有一半重叠,能在样本少的时候起到数据增强作用。mask_p.sum() > 0这行是血泪经验——遥感图里大片都是背景,如果不过滤,模型很快学会"全预测背景"就能拿到很高的像素准确率,但 IoU 惨不忍睹。参数怎么改:样本多就把stride调大到256减少冗余;样本少就保持128甚至64。
提示:mask 的像素值必须是 0/1/2… 这样的类别索引,不能是 0/255 的二值图直接喂进去,否则损失函数会算错。用
create_dataset.ipynb时先np.unique(mask)确认一下类别值。
3. 模型结构与训练:model.py、train.py怎么读怎么改
3.1 U-Net 结构在model.py里的落地
model.py定义的就是标准 U-Net:左边编码器不断卷积+池化降分辨率提特征,右边解码器上采样恢复分辨率,中间用跳跃连接把编码器的浅层特征拼到解码器对应层。cnn.py里大概率封装了DoubleConv这种"两次卷积+BN+ReLU"的基础块。读代码时重点看三个地方:下采样几次、每层通道数、跳跃连接是拼接(concat)还是相加(add)。标准 U-Net 是 concat,通道数翻倍。
# 典型的 DoubleConv 结构,读 model.py 时对照这个看 class DoubleConv(nn.Module): def __init__(self, in_ch, out_ch): super().__init__() self.conv = nn.Sequential( nn.Conv2d(in_ch, out_ch, 3, padding=1), nn.BatchNorm2d(out_ch), nn.ReLU(inplace=True), nn.Conv2d(out_ch, out_ch, 3, padding=1), nn.BatchNorm2d(out_ch), nn.ReLU(inplace=True), ) def forward(self, x): return self.conv(x)逻辑说明:padding=1保证卷积后尺寸不变,这样跳跃连接时编码器和解码器的特征图尺寸才能对齐。BatchNorm2d在遥感小样本场景下能稳定训练,但如果你的 batch size 小于 4,BN 反而会拖后腿,这时候换成GroupNorm更稳。参数上,out_ch一般按 64→128→256→512 递增,显存不够就整体减半。
3.2train.py的训练循环与关键参数
train.py是主入口,通常包含数据加载、模型实例化、损失函数、优化器、epoch 循环和模型保存。遥感语义分割最常用的损失是交叉熵,类别不均衡时用 Dice Loss 或两者加权。评估指标看 IoU 和 F1。
# 典型启动方式,具体参数名以 train.py 里的 argparse 为准 python src/train.py --data_dir ./demo --epochs 100 --batch_size 8 --lr 1e-3 --num_classes 5逻辑说明:--epochs 100是常见起点,遥感数据收敛一般比自然图像慢;--batch_size 8受显存限制,8G 显存跑 256×256 大概能到 8;--lr 1e-3配 Adam 是稳妥选择,如果 loss 震荡就降到1e-4。训练时另开一个终端跑start_tensorboard.ps1,在浏览器里看 loss 和 IoU 曲线,比盯着终端输出直观得多。
注意:如果
train.py里写死了数据路径,先改成你自己的路径再跑。很多人第一次跑直接报FileNotFoundError,就是没改路径。
3.3 用train.ipynb做交互式调试
train.ipynb的价值在于可以分块执行——先加载数据看几张图,再实例化模型打印结构,最后才开训。调试阶段我强烈建议走 Notebook,因为你能随时plt.imshow看输入和标签对不对。常见翻车点:图像和 mask 没对齐、归一化用了 ImageNet 均值但遥感图分布完全不同、标签值超出num_classes范围。这些在 Notebook 里一眼就能看出来,在train.py里就得靠打印日志慢慢查。
4. 预测、评估与可视化:predict.ipynb和 TensorBoard
4.1predict.ipynb跑推理
训练完保存的权重用predict.ipynb加载做推理。流程是:读图 → 预处理(和训练时一致)→ 前向传播 → argmax 取类别 → 上色可视化。这里最容易出问题的是预处理不一致——训练时做了归一化,预测时忘了,结果输出全是噪声。
import torch import numpy as np import cv2 from src.model import UNet # 按实际类名调整 model = UNet(num_classes=5) model.load_state_dict(torch.load("best_model.pth", map_location="cpu")) model.eval() img = cv2.imread("test.png")[:, :, ::-1] / 255.0 # BGR转RGB并归一化 img = cv2.resize(img, (256, 256)).astype(np.float32) tensor = torch.from_numpy(img).permute(2, 0, 1).unsqueeze(0) with torch.no_grad(): out = model(tensor) pred = out.argmax(dim=1).squeeze().numpy() # 按类别上色 color_map = np.array([[0,0,0],[255,0,0],[0,255,0],[0,0,255],[255,255,0]]) vis = color_map[pred].astype(np.uint8) cv2.imwrite("pred_vis.png", vis[:, :, ::-1])逻辑说明:map_location="cpu"保证在没 GPU 的机器上也能加载;/255.0是归一化,必须和训练时一致;argmax(dim=1)把每个像素的类别概率转成类别索引。color_map按你的类别数调整,几类就写几行。参数上,resize的目标尺寸要和训练输入一致,否则跳跃连接的尺寸对不上会直接报错。
4.2 评估指标怎么算才不虚高
遥感分割论文里必报 IoU,但很多人只报像素准确率(PA),这个指标在类别不均衡时严重虚高。正确做法是算每个类别的 IoU 再取平均(mIoU),同时报 F1。utils.py里如果有指标函数就直接用,没有就自己补:
def compute_iou(pred, target, num_classes): ious = [] for c in range(num_classes): inter = ((pred == c) & (target == c)).sum() union = ((pred == c) | (target == c)).sum() ious.append(inter / union if union > 0 else float("nan")) return np.nanmean(ious)逻辑说明:union > 0的判断避免某类在图中不存在时除零;np.nanmean忽略这些无效类。参数上num_classes必须和训练时一致。这个函数跑一遍,你就能判断模型是真学到了还是只会猜背景。
4.3 TensorBoard 看什么
start_tensorboard.ps1启动后,重点看三条曲线:训练 loss 是否单调下降、验证 IoU 是否跟着涨、学习率是否按调度衰减。如果 loss 降但 IoU 不涨,多半是过拟合或标签有问题;如果 loss 震荡,降学习率或加 batch size。TensorBoard 的--logdir参数要指向train.py里写的日志目录,指错了就是一片空白。
5. 避坑与常见问题排查
5.1 显存爆了但 batch size 已经调到 1
现象:CUDA out of memory,batch size 降到 1 还是报。原因:U-Net 在 256×256 输入下,浅层特征图通道数大,激活值占显存。解决:把输入切成 128×128,或把基础通道数从 64 降到 32,还可以用混合精度训练(torch.cuda.amp)省一半显存。
5.2 训练 loss 正常但预测全是一个颜色
现象:推理结果整张图同一类别。原因:数据预处理不一致,或标签值没从 0 开始连续编号。解决:打印训练和预测的输入张量范围对比,确认归一化一致;用np.unique检查标签值,必须是 0 到num_classes-1。
5.3 IoU 高得离谱但肉眼一看全是错的
现象:mIoU 报 0.9 以上,可视化却一塌糊涂。原因:背景类占比过大,且评估时把背景也算进去了。解决:单独看前景类的 IoU,或在损失里给前景类加权。遥感图里背景常常占 80% 以上,这个坑几乎人人踩。
5.4 Notebook 里能跑,train.py里报模块导入错误
现象:ModuleNotFoundError: No module named 'src'。原因:train.py在src目录内,相对导入路径和 Notebook 工作目录不同。解决:在项目根目录用python -m src.train启动,或在train.py开头把父目录加进sys.path。
5.5 论文 LaTeX 编译报字体缺失
现象:毕业论文目录下chap*.tex编译时提示找不到simhei.ttf。原因:模板用了系统字体,换机器就找不到。解决:把字体目录里的simhei.ttf、SimSun.ttf、KaiTi.ttf装到系统字体目录,或改用xeCJK宏包指定字体路径。Bibs/mybib.bib是参考文献,编译顺序是 xelatex → bibtex → xelatex ×2。
6. 把 U-Net 换成改进结构:从跳跃连接和损失函数下手
跑通基线之后,毕设要出彩就得做改进。遥感分割最常见的改进方向有两个:一是改跳跃连接,二是改损失函数。跳跃连接方面,标准 U-Net 直接把编码器特征拼过来,但浅层特征噪声大,可以加注意力门控(Attention Gate),让解码器只关注有用的区域。做法是在 concat 之前,用解码器当前特征生成一个权重图,对编码器特征做加权:
class AttentionBlock(nn.Module): def __init__(self, F_g, F_l, F_int): super().__init__() self.W_g = nn.Sequential(nn.Conv2d(F_g, F_int, 1), nn.BatchNorm2d(F_int)) self.W_x = nn.Sequential(nn.Conv2d(F_l, F_int, 1), nn.BatchNorm2d(F_int)) self.psi = nn.Sequential(nn.Conv2d(F_int, 1, 1), nn.BatchNorm2d(1), nn.Sigmoid()) self.relu = nn.ReLU(inplace=True) def forward(self, g, x): # g 是解码器特征,x 是编码器跳跃连接特征 attn = self.psi(self.relu(self.W_g(g) + self.W_x(x))) return x * attn逻辑说明:F_g和F_l分别是解码器和编码器特征通道数,F_int是中间降维通道,一般取F_l // 2。attn是 0 到 1 的权重图,乘回x就实现了空间注意力。参数上,F_int太小会丢信息,太大增加计算量,实践中取编码器通道的一半比较平衡。这个模块插在每次 concat 之前,mIoU 通常能涨 1 到 3 个点,写进论文也够看。
损失函数方面,交叉熵配 Dice 的加权和是遥感分割的标配。交叉熵管像素级分类,Dice 管区域重叠,两者互补。权重一般设0.5:0.5或0.6:0.4,前景类少就把 Dice 权重调高。我一般会先跑纯交叉熵做基线,再换加权损失对比,这样论文里能有一组消融实验数据。
验证改进是否有效,别只看最终 mIoU,要看每个类别的 IoU 变化。有时候总体涨了,但小目标类反而降了,这种"改进"在答辩时容易被问住。从那以后我每次改结构,都强制把每个类的 IoU 单独打出来对比,确认没有拆东墙补西墙。希望帮到你。
本文还有配套的精品资源,点击获取