ARTICLE DETAIL

资讯详情

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

卫星遥感图像分割实战:PSPNet与DeepLabV3源码解析与跑通指南

卫星遥感图像分割实战:PSPNet与DeepLabV3源码解析与跑通指南 简介面向计算机相关专业课程设计与期末大作业的Python项目源码聚焦基于图像分割的卫星遥感国土分类任务适合正在准备课程设计、期末大作业或项目实战练习的学生。代码已经过严格调试解压即可直接运行能够作为完整参考方案使用。资源包共包含十二个文件其中八个核心Python脚本覆盖影像预处理、数据加载、模型定义与训练流程模型部分涉及PSPNet、DeepLabV3及其增强结构另附训练日志、说明文档和成果展示图整体大小约2.49MB结构清晰紧凑。目前已有158人学习下载。借助资源可以系统理解遥感图像分割从数据准备、模型搭建到训练调参的完整链路也能基于现有源码快速修改适配自己的数据从而快速产出可演示的分类结果同时日志和展示图有助于对比模型表现是完成课程设计与提升实践能力的高效素材。1. 卫星遥感图像国土分类一套能直接跑的图像分割项目源码做课程设计最怕的不是模型多高深而是到手一套源码却跑不起来。这套基于 Python 的图像分割项目源码把卫星遥感图像的国土分类任务完整落地了数据预处理、PSPNet 与 DeepLabV3 系列分割网络、训练脚本、日志记录和 26 组展示图全都在包里是一个下载后可以直接运行复现的课程设计项目。它适合正在做期末大作业的计算机相关专业学生也适合想快速上手遥感图像分割实战的开发者。拿到它你节省的是从零搭训练框架的时间需要投入的是理解数据格式和调整参数。2. 数据预处理与 DataLoader从原始影像到可训练样本遥感图像分割和普通图像分割最大的区别在数据原始 tif 往往是大尺寸、多波段、带地理坐标的影像不能直接塞进网络训练。这套源码把这一层单独拆成三个文件preprocess.py、preprocess_water.py 和 data_load.py。搞懂这几个文件的分工你就明白作者当时是怎么把一张大图变成一批小 patch的。2.1 preprocess.py 与 preprocess_water.py 的分工preprocess.py 处理的是通用地物类别也就是耕地、建筑、植被、水体、裸地这些常规分类目标。常见做法是把它做成一个批处理脚本读入大图按固定滑窗步长裁剪出多个小 patch同时把对应的标签图裁剪成同样大小的 mask最后按 train/val 划分写到两个目录下。# preprocess.py 核心逻辑按源码结构补全的常见实现 import os import numpy as np from osgeo import gdal def crop_image_label(image_path, label_path, patch_size512, stride256, out_dir./data/train): src gdal.Open(image_path) label gdal.Open(label_path) width, height src.RasterXSize, src.RasterYSize for y in range(0, height - patch_size, stride): for x in range(0, width - patch_size, stride): img_patch src.ReadAsArray(x, y, patch_size, patch_size) label_patch label.ReadAsArray(x, y, patch_size, patch_size) # 跳过标签全为背景的无效样本避免训练集被空白区域污染 if label_patch.max() 0: continue np.save(os.path.join(out_dir, fimg_{x}_{y}.npy), img_patch) np.save(os.path.join(out_dir, flabel_{x}_{y}.npy), label_patch)这段逻辑里有三个关键点。第一patch_size 和 stride 控制样本数量与重叠度512 的 patch 配 256 的 stride 在遥感分割里比较常用既能保留上下文信息又不会让样本过度重叠。第二gdal 的 ReadAsArray 返回的数组维度是 (波段数, 高度, 宽度)后续在 Dataset 里要变换成 (高度, 宽度, 波段数) 再转成 tensor这一步顺序错了训练时必然报错。第三跳过全背景 patch 的过滤很关键如果不做训练集里会混入大量全是 0 标签的样本模型会被带偏预测结果大面积偏向背景类。preprocess_water.py 单独拎出来处理水体是因为水体在遥感影像里光谱特征非常稳定但面积占比往往很小属于典型的样本少但很重要的类别。常见做法是先算 NDWI 指数再阈值分割把水体掩膜叠加进标签做先验或者直接对水体样本做针对性过采样。# preprocess_water.py 中水体先验提取的常见逻辑 def ndwi_mask(green_band, nir_band, threshold0.15): # NDWI 取值范围在 -1 到 1 之间水体一般大于阈值 ndwi (green_band.astype(np.float32) - nir_band.astype(np.float32)) / \ (green_band.astype(np.float32) nir_band.astype(np.float32) 1e-6) return (ndwi threshold).astype(np.uint8)NDWI 依赖绿波段和近红外波段多数公开卫星数据都包含这两个波段。threshold 需要根据实际影像调整水体干净的区域 0.15 就能分得比较开浑浊水体或阴影干扰严重时要往上调到 0.2 以上。源码把水体单独做预处理我猜就是为了解决课设里水体老是和阴影、深色裸地混淆这个经典问题。2.2 data_load.py 的样本组织逻辑预处理只是把数据切成块真正喂给网络的是 data_load.py 里的 Dataset。它负责在训练时按索引读取 image 和 mask同时做在线数据增强。这个文件的写法直接决定训练时读取效率和显存利用率。# data_load.py 的 Dataset 结构典型实现 import os import torch from torch.utils.data import Dataset import numpy as np class RemoteSensingDataset(Dataset): def __init__(self, img_dir, label_dir, class_num6): self.img_paths sorted(os.listdir(img_dir)) self.label_paths sorted(os.listdir(label_dir)) self.class_num class_num def __len__(self): return len(self.img_paths) def __getitem__(self, idx): img np.load(os.path.join(self.img_dir, self.img_paths[idx])) mask np.load(os.path.join(self.label_dir, self.label_paths[idx])) # 在线增强随机水平翻转和垂直翻转 if np.random.rand() 0.5: img, mask img[:, ::-1], mask[:, ::-1] if np.random.rand() 0.5: img, mask img[::-1], mask[::-1] # 维度转换numpy 的 (H, W, C) - PyTorch 的 (C, H, W) img torch.from_numpy(img.transpose(2, 0, 1)).float() mask torch.from_numpy(mask).long() return img, mask这份代码要重点看两个地方。一是 img 做了 transpose(2, 0, 1)把 numpy 的 (H, W, C) 转成 PyTorch 的 (C, H, W)漏了这一步会在模型 forward 时报维度错误。二是 mask 转成 long 类型因为分割任务的交叉熵损失要求标签是整数索引而不是 one-hot。如果标签里有大于 class_num 的像素值训练会直接崩这就是预处理阶段必须先做类别映射的原因。在线增强这里只写了翻转实际课设项目里一般还会加随机裁剪、亮度抖动。但增强强度要控制遥感图像的语义和自然图像不同过度的颜色扰动反而会让模型学到错误的光谱特征比如把植被增强成红褐色模型就会把真实植被认错。2.3 预处理参数怎么改不同数据源的参数差异很大按我跑遥感分割的经验给出这组常用的默认值参数推荐值说明patch_size512太大显存吃紧太小上下文信息不足stride256patch 间 50% 重叠样本量翻倍NDWI 阈值0.15~0.2浑浊水面或阴影多时取大值类别数5~8常见为水体/耕地/建筑/道路/植被/裸地train/val 比例8:2按 patch 样本数划分别按原始影像划分改参数时要记住一个原则预处理决定样本质量比网络结构更影响最终精度。很多课程设计最后成绩不理想问题不出在用的是 PSPNet 还是 DeepLab而出在标签没对齐、类别没映射干净、归一化做错了。2.4 数据没开源怎么办用公开数据集替代源码包里 data 目录下只有一个说明文件标明原始数据来自高精度卫星影像且有保密约束无法随源码一起发布。这是遥感项目的常态不算源码缺陷。要做课设就需要换成公开的遥感语义分割数据集比如 DeepGlobe、LoveDA、ISPRS Potsdam 这类带地物类别标注的数据集。# 换数据集的目录整理方式让 data_load.py 不需要改动逻辑 data/ ├── train/ │ ├── img/ │ │ ├── img_0_0.npy │ │ └── img_256_0.npy │ └── label/ │ ├── img_0_0.npy │ └── img_256_0.npy └── val/ ├── img/ └── label/注意换数据集后第一件事不是跑训练而是跑一遍预处理并随机抽几张图把 img 和 label 叠加可视化检查。遥感 tif 经常有波段顺序不同的问题RGB 波段顺序错了模型会学到完全错误的光谱特征。3. 网络模型选型seg_zoo 里 PSPNet、DeepLabV3、DeepLabV3 怎么共存源码的 nets 目录下放着 seg_zoo.py、pspnet.py、deeplabv3.py、deeplabv3_plus.py 四个文件。这种组织方式很典型模型定义和模型工厂分开训练脚本只认名字不认类。读懂了这一章的代码结构你就能在三种网络之间自由切换做对比实验。3.1 seg_zoo.py 的模型注册机制seg_zoo 这个名字借用了 model zoo 的概念意思是可以像逛动物园一样按名字取模型。核心实现就是一个字典注册表加一个工厂函数。# seg_zoo.py 的注册表与工厂函数常见实现 from .pspnet import PSPNet from .deeplabv3 import DeepLabV3 from .deeplabv3_plus import DeepLabV3Plus seg_model_zoo { pspnet: PSPNet, deeplabv3: DeepLabV3, deeplabv3_plus: DeepLabV3Plus, } def build_seg_model(model_name, num_classes, backboneresnet50): # 通过字典取值保证未知模型名直接 KeyError而不是静默出错 model seg_model_zoo[model_name](num_classesnum_classes, backbonebackbone) return model这个设计的好处是训练脚本不用改模型代码只要在命令行参数里传模型名就能在三种网络之间切换。做课程设计答辩时你可以用同一套训练流程跑三个模型比较它们的 mIoU 和视觉效果这一组对比实验就能撑起报告的核心章节。源码里同时保留了 pspnet.log 和 deeplab_v3.log 两个日志文件也从侧面印证作者当时就是用这种方式做了多模型对比。3.2 三种网络的原理差异和选型理由PSPNet 的核心是金字塔池化模块Pyramid Pooling Module。它在主干网络输出的特征图上用 1×1、2×2、3×3、6×6 这几种不同尺寸的池化核聚合全局上下文信息然后上采样回原尺寸并拼接。对于遥感影像这种大目标多、小目标也多的场景这个设计很契合大池化核抓耕地、水体的整体分布小池化核抓道路、房屋的局部细节。DeepLabV3 的核心是空洞空间金字塔池化ASPP用不同膨胀率的空洞卷积扩大感受野膨胀率一般是 6、12、18 三档。相比池化空洞卷积保留了更多空间细节。DeepLabV3 则是在 V3 的基础上加了解码器把主干网络浅层的空间细节信息拼回来对分割边缘的精细度有明显提升。# deeplabv3_plus.py 中解码器部分的简化结构 import torch import torch.nn as nn import torch.nn.functional as F class Decoder(nn.Module): def __init__(self, low_level_channels48, num_classes6): super().__init__() # 先降浅层特征通道避免与深层特征拼接后通道数爆炸 self.low_level_conv nn.Conv2d(low_level_channels, 48, 1) self.conv nn.Sequential( nn.Conv2d(304, 256, 3, padding1), nn.BatchNorm2d(256), nn.ReLU(inplaceTrue), ) self.last_conv nn.Conv2d(256, num_classes, 1) def forward(self, high_level_feat, low_level_feat): low self.low_level_conv(low_level_feat) # 深层特征先上采样到浅层尺寸再拼接保证尺寸对齐 x F.interpolate(high_level_feat, sizelow.shape[-2:], modebilinear, align_cornersTrue) x torch.cat([x, low], dim1) return self.last_conv(self.conv(x))解码器里最值得学习的是 F.interpolate 这一行先把深层特征上采样到浅层特征的尺寸再拼接而不是直接 cat。如果漏了这一步特征图空间尺寸对不上cat 操作立刻报维度错误。这也是从图像分类转到分割任务时最高频的翻车点之一。选型上给一个实用判断课设时间紧、追求稳定选 PSPNet想展示对边缘分割的理解选 DeepLabV3时间充裕就三个都跑用日志和可视化做对比。三者的训练代价差异主要在解码器参数量上DeepLabV3 参数量最大、训练时间最长但同等条件下 mIoU 通常也最高。3.3 换 backbone 要注意的兼容点源码里默认 backbone 是 resnet50如果想换 mobilenet 或 resnet101不能只改一个字符串参数。分割网络的 backbone 输出通道数必须和分割头的输入通道数对齐否则模型能装、权重能加载一 forward 就报维度不匹配。# 以 deeplabv3_plus.py 为例head 的输入通道依赖 backbone 输出通道 # resnet50: 2048 - ASPP - 256 - Decoder # mobilenet: 320 - ASPP - 256 - Decoder # 自定义 backbone 时必须同步修改 ASPP 的 in_channels这是分割项目里最典型的玄学报错之一错误信息指向的是模型内部但根因在 backbone 和 head 的通道契约上。排查时先确认 backbone 输出通道数再对 ASPP 或金字塔池化的输入层做修改别在报错堆栈里大海捞针。4. 训练流程与日志解读把 deecamp_train.py 跑起来预处理和模型定义都清楚了训练脚本就是整份源码里改动最少的部分。deecamp_train.py 保留了作者项目命名时的习惯看起来是完整训练脚本的入口。读懂它你就能用自己的数据复现整个训练流程。4.1 训练入口与关键参数训练脚本的典型结构是 argparse 接参数、DataLoader 载数据、PyTorch 训练循环、日志输出。# deecamp_train.py 的关键结构典型课设实现 def main(): parser argparse.ArgumentParser() parser.add_argument(--model, defaultpspnet, choices[pspnet, deeplabv3, deeplabv3_plus]) parser.add_argument(--backbone, defaultresnet50) parser.add_argument(--num_classes, default6, typeint) parser.add_argument(--batch_size, default4, typeint) parser.add_argument(--lr, default0.01, typefloat) parser.add_argument(--epochs, default80, typeint) parser.add_argument(--data_root, default./data) args parser.parse_args() train_loader build_loader(args.data_root, train, args.batch_size) model build_seg_model(args.model, args.num_classes, args.backbone) optimizer torch.optim.SGD(model.parameters(), lrargs.lr, momentum0.9, weight_decay1e-4) criterion nn.CrossEntropyLoss() for epoch in range(args.epochs): train_one_epoch(model, train_loader, optimizer, criterion, epoch) if epoch % 5 0: val_miou evaluate(model, val_loader, args.num_classes) write_log(pspnet.log, epoch, val_miou)这个脚本的写法是典型的课设风格单文件、函数式、无框架封装好处是容易读懂和改。动手改的时候要注意学习率的设置分割任务用 SGD 配 0.01 初始学习率是常见组合但如果换了更大的 batch size学习率最好按比例往上调。分割任务里 Poly 学习率衰减策略比固定学习率效果好很多公开代码用的都是lr × (1 - iter/total_iter)^powerpower 一般取 0.9。4.2 从日志判断训练有没有跑偏源码里留下的 pspnet.log 和 deeplab_v3.log 就是训练过程的记录格式一般是每行一个 epoch 的指标。# pspnet.log 的片段示意格式 epoch1 loss1.782 mIoU0.152 epoch2 loss1.504 mIoU0.214 epoch10 loss0.892 mIoU0.386 epoch40 loss0.421 mIoU0.618读日志重点看三件事。一是 loss 是否持续下降如果前 10 个 epoch loss 还在 1.5 以上徘徊多半是学习率太大或者输入没归一化。二是 mIoU 和 loss 是否同步变化如果 loss 降了但 mIoU 不涨说明模型在预测高频类别、低频类别被压掉了这就是类别不平衡。三是看日志尾部有没有异常跳变某一步指标突然剧烈波动又恢复一般是因为 batch 里混入了标签损坏的样本需要回查数据清洗。4.3 显存不足和训练中断的应对遥感影像的 patch 尺寸大512×512 输入在 resnet50 上训练batch size 超过 4 就可能爆显存。遇到 CUDA out of memory 时的调整顺序是这样。# 遇到 CUDA out of memory 时的调整顺序 # 1. 降低 batch_size python deecamp_train.py --model pspnet --batch_size 2 # 2. 还不行就减小输入尺寸通过预处理重新生成 patch # 3. 最后考虑换轻量 backbone比如 mobilenet我的经验是优先降 batch size别急着缩小 patch。分割任务对空间细节敏感patch 从 512 缩到 256精度掉得比想象中快。如果 batch size 降到 1 还爆显存检查能不能打开混合精度训练PyTorch 自带的 torch.cuda.amp 可以省接近一半显存代价是训练速度稍慢。训练中断是另一个高频问题。课设机器经常跑着跑着断掉或者远程连接断开。常见做法是加 checkpoint 保存逻辑每 N 个 epoch 存一次模型权重和 optimizer 状态。# 训练中断续跑的保存和加载加在训练循环里 if epoch % 10 0: torch.save({ model: model.state_dict(), optimizer: optimizer.state_dict(), epoch: epoch, }, fcheckpoint_epoch{epoch}.pth) # 重启训练时加载最近的 checkpoint checkpoint torch.load(checkpoint_epoch40.pth) model.load_state_dict(checkpoint[model]) optimizer.load_state_dict(checkpoint[optimizer]) # 把起始 epoch 设为 checkpoint[epoch] 1保存 optimizer 状态是很多人会漏掉的细节。只存模型权重续跑时学习率会回到初始值如果你已经跑到第 60 个 epoch相当于学习率陡增一次模型大概率会震荡。所以要么连 optimizer 一起存要么在加载时手动把学习率调低。4.4 类别不平衡遥感分割的老熟人国土分类里水体、裸地、道路这些类别面积占比悬殊。如果直接算普通交叉熵模型会把所有像素预测成占比最大的类别整体像素精度看起来还行但小类目标全是错的。解决手段有四种按改动成本排序方案改动量效果类别加权交叉熵只改 loss 初始化有效但权重需要统计预处理时对小类过采样预处理脚本里加判断对水体这类聚集目标有效lovasz_softmax 损失换一个 loss 函数对边界分割提升明显采样器均衡各类别 patchdata_load 里加采样逻辑效果最稳但改动多源码里最可能用的是类别加权交叉熵PyTorch 里实现很简单只需要传入一个 weight 向量。# 加权交叉熵权重来自训练集各类别像素占比的逆 class_weight torch.tensor([0.5, 1.0, 2.0, 1.5, 0.8, 3.0]).to(device) criterion nn.CrossEntropyLoss(weightclass_weight)class_weight 不该拍脑袋定要统计训练集 mask 里每个类别的像素数量用1 / sqrt(freq)归一化。比如水体占 5%、建筑占 30%水体权重就大约是建筑的根号 6 倍。写报告时把这个统计过程放进去是一个很实在的数据分析加分项。5. 避坑指南跑这套国土分类源码的五个常见问题实战项目最值钱的部分就是坑。下面的五条记录按现象、原因、解决的顺序写都是我按这套源码跑课设流程时最可能遇到的真实情况。5.1 data 目录是空的代码一跑就报文件不存在现象源码包解压后 data 目录里只有一个高精度数据保密无法开源的 txt 说明没有实际训练图片第一次运行直接报 FileNotFoundError。原因作者用的原始影像属于高精度卫星数据且有保密约束不能随课程设计源码一起发布这是遥感项目的常态不是源码损坏。解决换公开数据集。DeepGlobe、LoveDA、ISPRS Potsdam 这类带地物类别标注的开源遥感数据集都可以下载后按第 2 章整理的目录结构放进 data/train 和 data/val保持 img 和 label 一一对应即可。换完别急着训练先跑一遍预处理再用 matplotlib 抽几张图叠加可视化确认对齐。5.2 模型能跑起来但 loss 一直不降现象训练能启动但 loss 卡在 1.7 上下十几个 epoch 基本不动mIoU 也一直在 0.2 以下。原因最常见的有两个。一是输入图像没有归一化像素值 0 到 255 直接进网络BatchNorm 的统计量被极端值干扰。二是标签不是从 0 开始的连续整数比如类别标成了 1、2、5、7CrossEntropyLoss 计算时类别索引对不上某些类别被静默忽略。解决预处理里把影像像素除以 255 或者做均值方差归一化标签在做预处理时用 np.unique 检查一遍重映射成 0、1、2、3 这样的连续序列。# 标签重映射保证类别索引是连续整数 unique_cls np.unique(label_patch) mapping {old: new for new, old in enumerate(unique_cls)} for old, new in mapping.items(): label_patch[label_patch old] new做完这两步再看 loss一般都会开始正常下降。如果还是不降检查学习率是不是超过 0.01分割任务用大学习率非常容易原地踏步。5.3 mIoU 挺高但可视化结果边缘锯齿严重现象评估指标 mIoU 能到 0.6 以上但生成的预测图里建筑物边缘、道路交叉口一带参差不齐像锯齿一样。原因分割网络输出的特征图经过多次下采样再上采样回原图尺寸边缘细节天然丢失。PSPNet 尤其明显DeepLabV3 加了解码器后会好一些。解决如果你同时跑过 pspnet 和 deeplabv3_plus直接在报告里对比两者的边缘效果这本身就是很好的实验结论。想进一步提升可以做 mask 后处理对预测结果做一次多数投票融合重叠区域取众数或者用一个简单的形态学开闭运算修掉孤立点。课设层面做到这个程度已经完全够用不必追求 SOTA。5.4 换了自己的数据后显存没爆但内存爆了现象Windows 上跑训练显存占用不到一半物理内存却一路涨到 90% 以上系统卡死。原因data_load.py 的 Dataset 在初始化时把所有 npy 文件一次性读进了内存patch 数量多的时候内存直接被打满。另一个常见原因是 Windows 上 DataLoader 的 num_workers 开得太大子进程反复复制 Dataset 副本。解决检查 Dataset 的init里有没有提前读取所有文件改成getitem里按需读取。num_workers 在 Windows 上建议不要超过 2Linux 可以开到 4 到 8。# Windows 上 DataLoader 的保守配置避免内存被吃满 train_loader DataLoader(dataset, batch_size4, shuffleTrue, num_workers2, pin_memoryFalse)如果 patch 数量特别大可以考虑把数据打包成 h5py 单文件格式顺序读取走内存映射内存占用会稳定很多。5.5 Conda 环境里 import 项目模块报 ModuleNotFoundError现象按习惯新建了 python 3.8 环境装完依赖后 import torch 没问题但 import 项目里的 seg_zoo 报模块找不到。原因源码里用的是相对导入比如from .pspnet import PSPNet这种写法要求项目根目录必须在 sys.path 里。在 PyCharm 里跑没问题因为 IDE 会自动把项目根目录标记成 Sources Root但换到纯命令行环境就废了。解决从项目根目录以模块方式启动或者在训练脚本顶部手动加路径。# 从项目根目录执行不能到别的目录下运行脚本 cd 项目根目录 python -m deecamp_train# 更保险的方式在训练脚本顶部强制把项目根目录加入 sys.path import sys, os sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))这个坑在课程设计代码里出现频率极高本质是开发环境和运行环境不一致。以后拿到任何带包的源码第一步都先看它的 import 方式是绝对路径还是相对路径。6. 进阶把课程设计变成一份有说服力的分割实验报告这个项目最大的价值在于对比实验已经替你准备好了。跑完三个模型后要做的不是继续盲目调参而是把训练记录整理成图表让报告看起来像一份完整的实验研究。第一个直接可做的事是从 pspnet.log 和 deeplab_v3.log 里提取数据画 mIoU 随 epoch 变化的曲线图。这个图能直观展示训练收敛过程也是答辩时最容易被提问的地方。import matplotlib.pyplot as plt def parse_log(log_path): epochs, mious [], [] with open(log_path, r) as f: for line in f: parts line.split() if len(parts) 3: epochs.append(int(parts[0].split()[1])) mious.append(float(parts[2].split()[1])) return epochs, mious epochs, miou parse_log(pspnet.log) plt.plot(epochs, miou, labelpspnet) epochs2, miou2 parse_log(deeplab_v3.log) plt.plot(epochs2, miou2, labeldeeplabv3) plt.xlabel(epoch) plt.ylabel(mIoU) plt.legend() plt.savefig(miou_compare.png, dpi150)注意日志解析的下标要和脚本实际输出的字段顺序保持对齐画图前先用文本编辑器打开日志看两行确认 loss 和 mIoU 在第几个字段不要想当然按位置猜。第二个进阶点是把预测结果拼回原图看整体效果。预处理时裁剪出的 patch 在预测后需要按原坐标拼接到大图上重叠区域取多数投票。这一步比 CRF 后处理更实用能直接暴露模型在不同地块上的失败模式比如阴影覆盖区域是不是被系统性误判成水体。我自己做课设的习惯是留出最后一周强制走一遍流程预处理输出一批 patch 并可视化检查、训练脚本小 batch 跑通、日志画收敛曲线、预测结果拼回原图。这四步的产出分别对应报告里的数据章节、方法章节、结果章节和展示图全部对齐之后再写文字效率比边写边跑高很多。从那以后我每次拿到开源分割项目都先做小 batch 全流程验证再放大参数跑正式实验。希望这份源码也能帮你把课程设计跑顺少踩几个预处理和日志的坑。本文还有配套的精品资源点击获取
返回列表