简介这是一份基于Segnet与Unet两种深度学习算法的遥感图像语义分割完整项目包面向计算机、人工智能、数据科学等专业的在校学生和开发者适用于毕业设计、课程大作业或项目初期演示。压缩包内共15个文件包含11个Python脚本、3张PNG图片和1个Markdown说明文档整体体积190KB。Python脚本覆盖Segnet/Unet网络定义、训练入口、预测推理、数据加载及预处理PNG图片提供分割效果与源图对比示例MD文档梳理环境配置与运行逻辑。数据预处理工具支持将遥感大图切割为256×256并执行旋转、加噪等增强操作便于生成训练数据。目前已有376人学习浏览适合希望快速入门深度学习语义分割的初学者也适合在此基础上进行二次开发的进阶用户项目提供了从数据增强、模型构建到训练与预测的完整链路可直接运行或修改扩展帮助读者直观理解Segnet与Unet在遥感图像分割任务中的异同。1. 遥感图像语义分割的毕设级起点Segnet与Unet源码包能省你两周时间做遥感图像语义分割的毕业设计最耗时间的往往不是理解网络结构而是把一堆零散的代码拼成能跑的管线。这份资源把Segnet和Unet两套深度学习语义分割方案完整实现好了训练脚本、网络定义、数据增强工具、已处理数据集和示例图全部配齐Windows下用Anaconda装好PyTorch环境就能直接跑通。它的价值在于不用自己从零搭数据管线也不需要为两种网络的对比实验重复造轮子新手靠它能入门进阶也能直接作为毕设、课程设计的一版基线。适合两类人一类是要快速拿到完整可复现结果的学生另一类是想在遥感影像上对比Segnet与Unet差异的开发者。后面的章节我会从数据切块、训练执行、常见坑位到推理验证逐项拆开讲。2. 两个网络一条数据管线Segnet与Unet结构差异和DataArgument.py的样本生成逻辑2.1 Segnet与Unet的结构差异池化索引和跳跃连接分别解决什么问题在讲数据管线之前先把两个网络的关系理清楚因为后面训练和避坑都依赖这个理解。Segnet的思路是编码器与解码器完全对称编码器做卷积和最大池化每次池化把最大值所在位置索引保存下来解码器上采样时不需要学习上采样参数直接用这个索引把特征放回原来的位置。这样做的好处是参数量小、内存占用低在显存不宽裕的显卡上也能跑更大的输入代价是池化索引丢掉了次大值的位置信息对细小地物的边缘恢复不够细腻。Unet走的是另一条路线在编码器和解码器之间加跳跃连接把每一层编码阶段的浅层特征直接拼接到同尺度解码特征上。遥感影像里的典型目标比如道路、建筑屋顶很多是细长结构和小目标浅层特征恰好保留着边缘纹理拼接后解码器更容易恢复这些细节这就是Unet在遥感语义分割上经常优于Segnet的核心原因。从语义分割模型演进的角度看FCN最早把全连接层替换成卷积层用反卷积逐层恢复分辨率Segnet和Unet分别从两个方向改进了FCN的思路——一个优化上采样方式一个优化特征传递方式。理解这个关系之后再去看models/seg_net.py里上采样部分出现的MaxUnpool以及models/u_net.py里出现的Concat操作就不会觉得它们是黑匣子。实际跑这份代码时我的感受是Segnet训练速度快、显存需求低Unet精度上限更高尤其道路连续性更好。如果显卡显存在6GB以下Segnet是更稳妥的起点显存够用、追求效果就优先Unet。两个网络各自独立定义互不干扰训练时分别用train_Seg.py和train_U.py启动这点后面会展开。2.2 DataArgument.py大图切割成256×256训练块的参数细节与增强手段这份资源对应的数据集是Massachusetts Roads Dataset单张原始影像在1500×1500像素这个量级直接整图送进网络显存会立刻溢出而且一张图只提供一个样本训练效率太低。原始标注可从官方地址获取但直连可用性不稳定资源里已经提供处理好的数据集下载后直接作为DataArgument.py的输入即可。该脚本的作用是先把大图切成256×256小块再做旋转和加噪扩充样本。运行方式cd utils python DataArgument.py脚本顶部需要配置的是输入和输出目录默认用相对路径。常见做法是输入目录指向原始影像和原始标签两个文件夹输出目录分别是切块后的image和label。切割尺寸定成256是显存占用和上下文信息的折中块太小看不清道路走向块太大又塞不进显卡。脚本内一般会按图像与标签同名配对的方式处理切完的图像块和标签块的命名一一对应后面data_loader才能按名字把样本对上。旋转增强对切块做90度的整数倍旋转一个切块能变成4个方向加噪一般使用高斯噪声用来模拟航拍传感器的随机噪声。经过这套数据增强样本量大约能放大到原来的4倍以上。我一般会提醒切割步长最好等于切割尺寸也就是无重叠切分一张1500×1500的影像大约能出25个256×256的训练块如果把步长设小于块尺寸虽然样本量膨胀但训练集和验证集之间容易出现信息泄漏分割指标会虚高。增强之后建议每类样本先过目一遍确认没有切出大量纯背景块纯背景块太多会让模型偏向背景类。2.3 目录结构与路径约定动手训练前先对齐相对路径解压后先别急着运行把文件功能分清。项目的主要文件结构如下├─ train_Seg.py # Segnet训练主程序 ├─ train_U.py # Unet训练主程序说明文档中也写作train_Unet.py ├─ predict.py # 推理脚本加载权重输出分割图 ├─ test.py # 测试脚本配合批量评估 ├─ models/ │ ├─ seg_net.py # Segnet网络定义 │ └─ u_net.py # Unet网络定义 ├─ utils/ │ └─ DataArgument.py # 大图切割、旋转、加噪数据增强 ├─ data_loader/ │ └─ dataset.py # 加载图像和标签送入DataLoader └─ asset/ # 项目说明示例图这里有个命名细节要说明项目说明文档里写的是train_Unet.pyzip里的实际文件是train_U.py两个名字指的是同一个训练脚本别在找文件上卡住。运行前建议保持目录结构不变代码里的相对路径已经在所有脚本中配置好命令行进入项目根目录就能执行。asset文件夹里的0_src.png、0_label_image.png、0.png是示例图分别对应原始遥感影像、标签真值和分割预测结果。第一次跑代码前先看这三张图能直观理解输出预期训练后也建议按同样格式自查判断模型到底学到了什么。3. 训练不是双击运行train_Seg.py与train_U.py的启动流程和超参数落点3.1 train_Seg.py主流程从数据加载到权重保存在项目根目录打开终端先跑Segnet训练python train_Seg.pyGPU可用时脚本会自动把模型和数据搬上cuda没有GPU也能用CPU跑但256×256输入、50个epoch左右CPU可能要几小时而GPU一般是几十分钟到一两个小时。打开train_Seg.py会发现超参数并不在命令行里而是写在脚本顶部这一点和很多工程化项目不同。按这类训练脚本的常见组织方式顶部会有模型实例化、类别数、batch_size、学习率、epoch数这些关键变量我通常会先保持默认值跑一轮确认流程通了再调整。参数建议值说明模型类SegNet定义在models/seg_net.py类别数2道路与背景二分类batch_size86GB显存可稳定训练epochs50该数据集规模下足够收敛learning_rate1e-3Adam优化器的常见起点训练循环的逻辑很直接每次迭代取一批图像和标签模型前向传播得到预测用CrossEntropyLoss计算预测与标签之间的交叉熵损失反向传播更新权重。每个epoch结束后打印当前loss并用tensorboardX把曲线写入日志目录。训练结束时保存模型权重后面的predict.py加载的就是这份权重。数据加载器用了data_loader/dataset.py里定义的Dataset类图像在送入网络前归一化到[0,1]区间标签保持为整数张量这个约定在训练和推理两端必须一致。提示Windows下如果DataLoader报多进程加载错误把num_workers改成0即可这是PyTorch在Windows上的老问题。3.2 train_U.py与train_Seg.py的差异Unet的收敛节奏和显存开销Unet训练脚本的启动方式几乎一样python train_U.py结构上和train_Seg.py基本一致区别在于导入的模型类换成了U_Net实例化部分做了对应修改。打开models/u_net.py能看到典型的U形结构图左侧编码器每层下采样后通道翻倍右侧解码器每层上采样后通道减半中间靠Concat把同尺度编码特征拼回来构成完整的跳跃连接。实际训练时Unet的表现和Segnet有明显差异。跳跃连接让梯度能直接从解码层回传到浅层收敛通常比Segnet快前几个epoch的loss下降更迅速但Concat操作带来的通道数增加也让显存占用水涨船高同样的batch_size下Unet的显存开销比Segnet高出约三分之一。如果你的显卡比较紧张Segnet跑得很稳的batch_size在Unet上可能正好卡在OOM边缘这时优先调batch_size不要先调学习率batch_size减半后学习率也建议同步减半这个联动逻辑在避坑章会详细说。3.3 tensorboard可视化loss曲线和训练诊断怎么看训练代码用tensorboardX记录日志启动可视化服务的命令tensorboard --logdirruns --port6006浏览器打开http://localhost:6006就能看到曲线。如果提示找不到tensorboardX需要先装依赖pip install tensorboardXtensorboardX写入的日志在runs目录下和tensorboard是同一套日志格式新版tensorboard直接读取没有问题。训练时重点看两条信息一是训练集loss是否稳步下降常见曲线是前10轮快速下探后面逐渐走平二是验证集指标是否反弹如果loss先降后升就是过拟合信号常见对策是降低学习率或提前停止。需要特别说明的是遥感二分类数据里道路像素占比很低loss降到0.1以下后会进入一段看似平台期的区间这不是模型坏了而是交叉熵在不平衡数据上接近表达上限。此时判断模型好坏得看预测图光看loss容易误判。4. 遥感语义分割避坑指南四个实际跑代码时绕不开的问题4.1 PyTorch版本差异1.0时代的代码在新环境里怎么装依赖现象按项目说明用Anaconda装环境默认装到了PyTorch 2.x运行训练脚本时import报错或者模型初始化阶段出现AttributeError还有的情况是DataLoader在迭代时报unknown parameter。原因这份代码写在PyTorch 1.0年代项目说明里明确写着pytorch 1.0。PyTorch 2.x整体兼容大部分旧接口但torchvision的transforms实现、部分API参数行为有调整老代码在新版本下容易出现边缘报错。解决建议不要用最新版单独建一个干净环境并锁版本conda create -n seg python3.7 conda activate seg pip install torch1.10.0 torchvision0.11.0 pip install tensorboardX我一般选1.10这一档而不是硬装1.0原因是1.0的Windows wheel不太好找而1.0到1.10之间这份代码用到的卷积、池化、CrossEntropyLoss接口几乎没有变化反而省去很多折腾。如果之后想让代码在2.x下跑主要改点是tensorboardX换成torch.utils.tensorboard以及检查transforms接口变动属于能改但没必要优先做的事。4.2 标签图读成三通道交叉熵计算的shape报错怎么定位现象训练刚开始loss计算就报错错误信息类似size mismatch或者出现Expected target size与prediction大小不一致的提示。原因遥感标签图经常是RGB彩色编码每个类别用一种颜色绘制直接读出来的数组形状是[H, W, 3]而CrossEntropyLoss要求目标形状和预测张量在空间维度上一致且标签要的是整数类别标号不是三个颜色通道。通道数对不上loss函数自然算不了。解决检查data_loader/dataset.py里读取标签的部分常见做法是读图后用convert(L)转成灰度单通道或者取第一个通道再把RGB颜色值映射成连续整数类别。定位技巧是在训练前打印标签张量的shape和unique值确认unique取值落在0到类别数减1之间。这一步是遥感语义分割里最容易踩的坑因为普通分割数据集多用单通道PNG而遥感数据集的标注格式五花八门。4.3 显存溢出大图直进网络和batch_size的取舍现象训练时报RuntimeError: CUDA out of memory显存占用面板里直接拉满。原因最常见有两个一个是没跑DataArgument.py把1500×1500的原图直接送进网络解码层特征图尺寸巨大显存瞬间打爆另一个是batch_size设得太大Unet在256×256输入下batch_size超过12对8GB显存都不友好。解决先确认训练数据目录里是不是256×256的切块如果不是回到utils目录跑一遍数据增强脚本。确认数据规模后按显卡显存定batch_size6GB用4或812GB用8或16。如果只是想放大等效batch可以用梯度累积来替代加大batch_size前向传播每次只走一个小批次梯度累加后再更新。用Segnet都OOM时优先怀疑数据没切好Segnet对显存已经很友好不至于在合理配置下爆掉。4.4 训练完predict.py输出异常权重路径和标签颜色映射现象训练正常结束执行python predict.py却提示找不到权重文件或者输出图片全黑、全白看不到道路结构。原因训练脚本每轮或每个epoch都会保存权重但文件名通常带epoch编号predict.py默认加载的是它自己写死的权重路径如果训练后没有把最终权重复制成默认文件名predict自然找不到。全黑全白则是另一类问题权重实际加载成功但预测的类别索引和可视化时用的颜色映射表不一致比如标签颜色表把类别0映射成黑色而背景恰好是类别0整张图看起来就是黑的。解决训练结束后把最终保存的权重复制为predict.py默认读取的文件名再执行推理。可视化时对照asset文件夹里0_label_image.png的颜色编码方式确认类别索引到RGB颜色的映射一致。权重文件本身是黑匣子但可以通过文件大小做粗判只有几KB的pth很可能是没训练的初始权重训练充分的模型权重至少在几十MB量级。5. 跑通inferencepredict.py加载权重、生成预测图与输出文件解读5.1 predict.py执行流程从单张影像到分割结果的完整链路权重就绪后推理比训练简单得多。在项目根目录执行python predict.py脚本的流程是加载模型结构、把训练好的权重填入模型、读取测试影像、做和训练时一致的预处理、前向传播得到每个像素的类别概率、用argmax取概率最大的类别索引、再把索引转成可视化图像保存。整个inference过程本质上是训练循环里前向传播那一段的重复这也是predict.py代码量明显小于训练脚本的原因。打开predict.py能看到模型实例化的代码和train_Seg.py里几乎一样这里有一个新手常踩的坑预测时的网络结构必须和训练时完全一致尤其是类别数。训练用的是二分类predict里如果写成了5类加载权重时就会报shape不匹配即使没报错输出索引和颜色映射表也对不上。另一个容易被忽略的点是预处理一致性训练时图像归一化到[0,1]predict时如果忘了归一化或者用了不同顺序分割效果会明显变差这不是模型变了而是输入分布变了。CPU上跑推理时256×256的输入单张一般在秒级整套预测脚本跑完也只是几分钟的事所以即使没有GPUpredict也是完全可用的。预测过程不依赖tensorboardX推理环境可以比训练环境更轻。5.2 输出文件解读0_src.png、0_label_image.png和0.png分别是什么项目说明的asset文件夹里那三张示例图恰好对应predict.py的标准输出理解它们就能看懂模型效果文件名内容来源0_src.png原始遥感影像测试集原图0_label_image.png标签真值人工标注0.png网络预测结果predict.py输出拿到三张图后我习惯把原图和预测图放在一起对比道路区域是否连续、建筑轮廓是否完整、有没有把大片阴影误判成道路。要留意标签本身也有噪声比如树冠遮挡路段、标注边缘不齐这些是数据集的既有问题不是模型缺陷。如果预测图和真值只是边缘差一两个像素说明模型已经学到不错的表示如果大块区域形状都对不上大概率是训练epoch不足或者类别不平衡导致模型偏向背景这时候优先看训练阶段的loss曲线和训练轮数。后处理方面如果预测图里出现大量零星小碎块通常是像素级分类无法摆脱的噪声。我一般会加一个简单的连通域过滤面积小于阈值的连通区域直接移除道路主干区域保留这个操作对视觉观感提升很明显而且只影响后处理不改模型。批量推理时还要注意输出文件命名不要互相覆盖常见做法是用输入文件名拼接结果名。5.3 权重文件检查和test.py的配合哪些文件决定推理能不能跑跑predict之前我一般按这个顺序检查三样东西models/init.py存在与否它决定包导入是否正常权重文件是否被predict.py默认名称匹配data_loader/dataset.py与predict.py里的预处理是否一致。检查命令很直接ls -lh *.pth文件名列表里如果找不到predict.py预期的那个权重名就手动复制一份过去。zip里还带了一个test.py从命名习惯看它和predict.py的分工是predict负责单张或批量出可视化图test负责在测试集上批量计算指标。如果test.py里实现了逐类IoU或像素精度计算建议训练完先跑test用数字确认效果再看predict的图做主观判断具体逻辑以解压后代码里的注释为准。6. 换自己的遥感数据前先做这三件事验证、微调与mIoU指标6.1 用自己的数据替换数据集目录命名和标签格式验证把这份代码用到自备遥感数据上第一件事是对齐目录约定新建image和label两个目录图像和标签文件名一一对应切成256×256后跑通一遍训练。训练前强制把标签可视化一遍确认它是单通道整数数组而不是彩色编码这一步能省掉后续所有shape报错的排查时间。很多自备数据的标注是用LabelMe等工具存的JSON或彩色PNG先转成与Massachusetts数据集相同格式的灰度标签再进DataArgument.py会顺畅很多。6.2 超参数联动微调batch_size、学习率和分割块大小的关系第二件事是超参数联动调整。我通常按这个顺序来先定显卡能承受的batch_size再按batch_size相对参考值的倍数等比缩放学习率最后才动切割尺寸。遥感语义分割里切割尺寸从256提到512模型能看到更大上下文道路走向判断更准确但显存占用按面积增长batch_size必须同步降下来训练时间也会拉长这是典型的精度换速度取舍。如果换到Segnet和Unet做对比实验务必保证两者用同一份切块数据否则分割块的差异会混进网络对比结果里实验结论就不干净了。6.3 用mIoU代替裸loss一段可以加进test.py的评估逻辑第三件事是加mIoU指标。交叉熵loss在类别不平衡的数据上反映不出真实分割水平最常见做法是逐类算IoU再取平均。以下这段逻辑可以直接参考test.py编写def compute_miou(pred, label, num_classes): ious [] for cls in range(num_classes): pred_mask (pred cls) label_mask (label cls) inter (pred_mask label_mask).sum().item() union (pred_mask | label_mask).sum().item() iou inter / (union 1e-6) ious.append(iou) return sum(ious) / len(ious)pred和label传入前保证都是整数张量且shape一致inter计算两个掩码的交集像素数union计算并集像素数加1e-6防止除零。逐类IoU算完后取平均得到mIoU。把这个函数挂进每个epoch的验证阶段mIoU的走势比loss更能说明模型是否真在进步尤其当背景类占比超过90%时loss下降很容易mIoU却可能纹丝不动。从那以后我每次换数据集都强制把这三件事走一遍可视化标签确认格式、联动调超参数、用mIoU记录迭代效果不再只看loss曲线。这份资源把Segnet和Unet的骨架都搭好了剩下真正决定分割上限的变量全在数据上。希望帮到你。本文还有配套的精品资源点击获取