ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

UNet遥感图像语义分割实战:从数据集制作到训练调优全解析

UNet遥感图像语义分割实战:从数据集制作到训练调优全解析 简介面向毕业设计场景的UNet遥感图像语义分割项目包适合计算机视觉、深度学习方向的本硕学生参考。内容涵盖UNet模型实现、遥感影像数据预处理、训练与预测脚本以及TensorBoard可视化配置和Jupyter实验记录并附有毕业论文LaTeX源码、图表与最终PDF报告覆盖从理论分析、实验设计到论文撰写的完整流程。压缩包共69个文件主要文件类型为Python源码、Jupyter Notebook、TeX论文源文件、图片、PDF及PowerShell启动脚本整体约46.92MB目录按代码、演示和论文分模块组织便于检索学习。其中UNet的双路径结构与跳跃连接能有效融合不同尺度特征对小样本遥感数据同样适配数据预处理部分还涉及归一化和分块策略利于理解工程落地细节。目前已有299人学习下载。通过该项目可系统掌握遥感图像语义分割中数据准备、模型搭建、损失函数与优化器选择、IoU等指标评估以及学术论文写作排版等实操技能尤其适合作为复现实验、完善课题设计和撰写毕业设计的参考。1. 基于UNet的遥感图像语义分割这份毕业设计资源到底能拿来干什么遥感图像语义分割是典型的像素级分类任务每一块地物——建筑、水体、植被、道路——都要被模型逐个像素打上标签。UNet凭借编码器-解码器结构和跳跃连接在标注样本有限时依然能取得不错的分割效果这也是它在遥感场景里被反复选用的根本原因。很多初学者卡在同一个地方理论看得懂但数据集分块、标签制作、训练参数调优、TensorBoard监控这一长串流程没人带一旦报错就不知道该查哪儿。这份毕业设计资源正好踩在这些痛点上。压缩包里不只包含一份完整的UNet实现源码还带了可运行的Jupyter Notebook演示、TensorBoard启动脚本、训练好的demo数据以及排版好的毕业论文LaTeX源码和相关图表。适合两类人一类是选语义分割做课题的学生可以直接把源码和论文框架复现出来改改数据就能毕业另一类是算法工程师想快速跑通UNet训练链路做原型验证的尤其适合先用demo数据看一遍全流程再替换成自己的遥感数据集。2. 解压后先看文件结构源码、论文、数据脚本各司其职拿到压缩包第一步别急着跑代码先把文件结构过一遍。这份资源的目录组织其实是标准的深度学习项目模板源码在src下可视化在demo下毕业论文在毕业论文目录里还有start_tensorboard.ps1和start_jupyter.ps1两个Windows PowerShell脚本用来快速启动配套工具。2.1 压缩包内的核心文件与职责对应关系我按照实际使用顺序把关键文件列成一张表方便你对照着排查问题文件/目录作用使用时机start_jupyter.ps1启动Jupyter Notebook服务打开demo演示时先跑start_tensorboard.ps1启动TensorBoard可视化面板训练前准备监控src/utils.py数据可视化辅助函数检查分割结果时使用src/cnn.py基础卷积模块定义构建UNet组件时被model引用src/model.pyUNet网络结构定义训练和推理的核心src/data.py遥感图像数据集类与预处理训练前数据加载src/train.py训练主程序模型训练入口demo/create_dataset.ipynb数据集制作与分块演示准备自己的数据时参考demo/train.ipynbNotebook版训练流程交互式跑训练demo/predict.ipynb加载权重做预测可视化推理阶段毕业论文/*.tex论文章节源文件撰写论文时参照结构毕业论文/Figures/UNet结构图、样本图等论文插图直接复用从文件组织能看出作者的思路源码部分把数据加载、模型定义、训练逻辑拆成独立模块保证训练脚本train.py足够简洁demo目录则以Notebook形式把数据制作、训练、预测全流程串起来适合先交互验证再写正式训练脚本。2.2 从模型文件入手UNet在PyTorch里落地长什么样打开src/model.py核心是UNet的双路径结构。编码器每次下采样通道数翻倍解码器上采样后与对应层跳跃拼接cnn.py里应该定义了基本的双卷积块。典型实现逻辑如下import torch import torch.nn as nn class DoubleConv(nn.Module): UNet基础块连续两次卷积BNReLU def __init__(self, in_ch, out_ch): super(DoubleConv, self).__init__() self.conv nn.Sequential( nn.Conv2d(in_ch, out_ch, 3, padding1), nn.BatchNorm2d(out_ch), nn.ReLU(inplaceTrue), nn.Conv2d(out_ch, out_ch, 3, padding1), nn.BatchNorm2d(out_ch), nn.ReLU(inplaceTrue) ) def forward(self, x): return self.conv(x)这段代码定义了UNet内部最基本的特征提取单元。两个3x3卷积中间夹BatchNorm是为了稳定训练padding1保证特征图尺寸不缩小后续上采样时才能和编码器特征做通道维度的拼接。如果显存紧张可以先把inplaceTrue去掉因为部分PyTorch版本对inplace操作的自动求导支持不够稳定。真正搭建UNet时需要注意forward函数中跳跃连接的保存class UNet(nn.Module): UNet编码器下采样4次解码器对称上采样 def __init__(self, in_channels3, num_classes4): super(UNet, self).__init__() 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.center DoubleConv(512, 1024) self.up4 nn.ConvTranspose2d(1024, 512, 2, stride2) self.dec4 DoubleConv(1024, 512) self.up3 nn.ConvTranspose2d(512, 256, 2, stride2) self.dec3 DoubleConv(512, 256) self.up2 nn.ConvTranspose2d(256, 128, 2, stride2) self.dec2 DoubleConv(256, 128) self.up1 nn.ConvTranspose2d(128, 64, 2, stride2) self.dec1 DoubleConv(128, 64) self.out nn.Conv2d(64, num_classes, 1) def forward(self, x): e1 self.enc1(x) e2 self.enc2(self.pool(e1)) e3 self.enc3(self.pool(e2)) e4 self.enc4(self.pool(e3)) c self.center(self.pool(e4)) d4 self.dec4(torch.cat([self.up4(c), e4], dim1)) d3 self.dec3(torch.cat([self.up3(d4), e3], dim1)) d2 self.dec2(torch.cat([self.up2(d3), e2], dim1)) d1 self.dec1(torch.cat([self.up1(d2), e1], dim1)) return self.out(d1)torch.cat在通道维度拼接上采样结果和对应编码器层输出这是UNet能保留细粒度空间信息的核心机制。in_channels3对应RGB遥感影像num_classes按你的标注类别数调整初学阶段建议先从4类开始——背景、建筑、水体、植被——跑通后再增加类别。2.3 数据加载与预处理不要让数据读取拖慢训练遥感影像一个特点是尺寸大动辄几千乘几千像素直接整图喂进网络显存直接爆掉。data.py里通常采用滑窗分块策略把大图切成256x256或512x512的小块再进模型。from torch.utils.data import Dataset from PIL import Image import numpy as np class RemoteSensingDataset(Dataset): 遥感影像语义分割数据集从大图中滑窗取块 def __init__(self, image_paths, mask_paths, size256, stride256): self.image_paths image_paths self.mask_paths mask_paths self.size size self.stride stride def __getitem__(self, idx): # 按索引加载原图与标签图 image np.array(Image.open(self.image_paths[idx])) mask np.array(Image.open(self.mask_paths[idx])) # 归一化到0-1并转为浮点张量 image image.astype(np.float32) / 255.0 # 转成CHW格式供PyTorch使用 image torch.from_numpy(image).permute(2, 0, 1) mask torch.from_numpy(mask).long() return image, mask def __len__(self): return len(self.image_paths)stride256时切块完全不重叠stride小于size时相邻块有重叠区域能缓解边界拼接处的预测不连续问题。我一般建议训练时stride取size的一半推理时再切成无重叠这样既增广了训练样本又能提升边缘分割质量。归一化直接除以255是快速上手的方式严谨项目里可以用数据集的均值和标准差做标准化。3. 跑通训练全流程从数据制作到TensorBoard监控光看代码结构不代表能跑出结果中间隔着数据集格式、训练参数、环境配置三座大山。这一章我按实际操作顺序带你过一遍每个环节都给出可复制的命令和参数建议。3.1 训练入口参数解读train.py的配置项train.py是整个项目的发动机舱核心超参数集中在文件顶部的解析器中。常见配置项如下import argparse parser argparse.ArgumentParser(descriptionUNet遥感图像语义分割训练) parser.add_argument(--data_dir, typestr, default, help训练数据根目录) parser.add_argument(--epochs, typeint, default100) parser.add_argument(--batch_size, typeint, default8) parser.add_argument(--lr, typefloat, default1e-4) parser.add_argument(--num_classes, typeint, default4) parser.add_argument(--image_size, typeint, default256) parser.add_argument(--checkpoint_dir, typestr, defaultcheckpoints) parser.add_argument(--log_dir, typestr, defaultlogs) args parser.parse_args()lr用1e-4是UNet遥感分割的常见起点比ImageNet迁移学习常用的1e-3小一个量级因为遥感影像标注样本量通常不大过大的学习率容易让模型在早期就震荡。batch_size取决于显存8GB显存跑256x256输入时8比较稳妥显存不够优先降batch而不是降分辨率。checkpoint_dir和log_dir建议分开设置别混在一起否则后期保存多个版本权重时日志目录会越来越乱。3.2 启动训练命令行、Notebook和后台运行三种方式这份资源给了两种启动路径一是直接跑训练脚本二是通过Jupyter Notebook交互执行。# 方式一直接启动训练 cd src python train.py --data_dir ../demo/sample_dataset --epochs 100 --batch_size 8 # 方式二后台运行并保存日志 nohup python train.py --data_dir ../demo/sample_dataset --epochs 200 train.log 21 # 方式三启动Notebook逐段执行 ./start_jupyter.ps1第一种方式适合确定参数后长时间训练第二种适合需要常驻后台nohup配合重定向把标准输出和错误流全部写入日志文件训练中断时能查到具体报错位置第三种方式适合探索期先跑一个小数据集验证代码逻辑再放大规模。我个人习惯先Notebook跑20个epoch确认损失下降再切后台命令跑完整训练。3.3 TensorBoard可视化训练曲线的正确打开方式start_tensorboard.ps1脚本本质上是调用了tensorboard --logdir命令。训练代码中需要周期性写入损失值和验证集指标from torch.utils.tensorboard import SummaryWriter writer SummaryWriter(log_dir../logs/exp1) for epoch in range(args.epochs): train_loss 0.0 for images, masks in train_loader: # ...训练逻辑... train_loss loss.item() avg_loss train_loss / len(train_loader) writer.add_scalar(Loss/train, avg_loss, epoch) # 每5个epoch记录一次验证集IoU if epoch % 5 0 and val_loader is not None: val_iou compute_iou(model, val_loader) writer.add_scalar(IoU/val, val_iou, epoch) # 保存最新权重与最优权重 torch.save(model.state_dict(), fcheckpoints/unet_epoch_{epoch}.pth) if val_iou best_iou: best_iou val_iou torch.save(model.state_dict(), checkpoints/best_model.pth)SummaryWriter是PyTorch官方推荐的TensorBoard对接接口add_scalar的tag命名我用Loss/train这种分组格式TensorBoard页面会自动按斜杠分组展示多组实验对比时方便勾选。保存权重时建议同时保留best_model.pth和按epoch编号的中间权重前者用于最终评估后者用于中断恢复。判定最优模型不能只看训练损失必须配上验证集IoU或准确率否则可能在训练集上过拟合。3.4 评估指标IoU怎么算才不会被骗语义分割最常用的指标是IoU它在train.py或单独的评估脚本里实现def compute_iou(model, val_loader, num_classes4): 逐类计算IoU并返回均值 model.eval() iou_list [] for images, masks in val_loader: outputs model(images) preds torch.argmax(outputs, dim1) for cls in range(num_classes): pred_mask (preds cls) true_mask (masks cls) intersection (pred_mask true_mask).sum().item() union (pred_mask | true_mask).sum().item() if union 0: iou_list.append(1.0) # 该类别在样本中不出现记1.0 else: iou_list.append(intersection / union) return sum(iou_list) / num_classesUnion为0时直接记1.0是常见做法但这只在验证集上单类别确实不存在时才合理。如果类别存在但模型完全没预测出来union会是正数而intersection为0IoU自然记为0不会混淆。如果你发现训练集和验证集的背景类占比超过80%建议单独输出每个类别的IoU别只看mIoU均值否则小目标类别模型一个都没学到均值仍然很好看。4. create_dataset.ipynb把原始遥感大图变成可训练的数据集整个项目最容易被低估的是数据制作这一步。很多初学者拿到原始遥感影像直接开训结果要么显存爆炸要么标签尺寸对不上。demo/create_dataset.ipynb演示了正规的数据集制作流程重点解决两个问题大图怎么切标签怎么对齐。4.1 大图滑窗切块与标签对齐的完整实现遥感影像的分割标签和自然图像的标注不一样标签图和原图严格逐像素对应。常见做法是用GIS工具导出标签为单通道PNG每个像素值代表类别编号。切块时原图和标签必须用同一组窗口参数import numpy as np from PIL import Image import os def sliding_window_crop(image_path, mask_path, output_dir, size256, stride256): 对遥感大图及其标签做滑窗切块一一对应保存 image np.array(Image.open(image_path)) mask np.array(Image.open(mask_path)) h, w image.shape[:2] idx 0 for y in range(0, h - size 1, stride): for x in range(0, w - size 1, stride): img_crop image[y:ysize, x:xsize] mask_crop mask[y:ysize, x:xsize] Image.fromarray(img_crop).save(os.path.join(output_dir, fimages/{idx}.png)) Image.fromarray(mask_crop).save(os.path.join(output_dir, fmasks/{idx}.png)) idx 1 # 处理右下角不够stride的边界残留 if (h - size) % stride ! 0 or (w - size) % stride ! 0: y h - size for x in range(0, w - size 1, stride): img_crop image[y:ysize, x:xsize] mask_crop mask[y:ysize, x:xsize] Image.fromarray(img_crop).save(os.path.join(output_dir, fimages/{idx}.png)) Image.fromarray(mask_crop).save(os.path.join(output_dir, fmasks/{idx}.png)) idx 1 print(f共生成 {idx} 对图像-标签块)切块尺寸的选择直接决定训练效果。256x256是兼容性和显存消耗的平衡点512x512保留了更多上下文信息但对显存要求翻倍。影像中地物尺寸差异大时我会做多尺度切块——既切256的细粒度块也切512的大视野块混合起来训练比单一尺度通常能提升3到5个点的mIoU。切块时最容易忽略边界残留。图像尺寸不整除时边缘会剩下一小条如果不处理就丢掉模型永远不会看到图像边缘的特征。上面代码用了最直接的方案不够stride时把窗口直接锚到右下角强行取最后一块。这种做法让该块与相邻块有重叠数据增广的同时也保证样本不浪费。4.2 数据划分训练集、验证集别放在同一个目录下切块完成后要按样本比例划分数据集。遥感图像存在空间自相关性相邻地块的像素高度相似如果随机划分验证集和训练集可能包含同一栋建筑的不同位置导致评估结果虚高。# 按文件随机划分8:1:1 cd sample_dataset mkdir -p train/images train/masks val/images val/masks # 用Python脚本按索引划分而非直接移动文件 python -c import os, random, shutil files os.listdir(images) random.seed(42) random.shuffle(files) n_train int(len(files) * 0.8) n_val int(len(files) * 0.1) for i, f in enumerate(files): src_img fimages/{f} src_mask fmasks/{f} if i n_train: dst train elif i n_train n_val: dst val else: dst test shutil.copy(src_img, f{dst}/images/{f}) shutil.copy(src_mask, f{dst}/masks/{f}) 更严谨的做法是按原始大图划分几张大图整体分配到训练集另几张分配到验证集再做切块。如果数据源是同一区域不同时相的影像还要把相近时相的分到同侧。random.seed(42)固定随机种子保证每次划分结果一致这个细节在需要对比实验时尤其重要。5. 避坑与排查遥感UNet训练的十个常见翻车现场训练UNet不同于训练分类网络大量报错集中在数据维度对不齐和显存管理上。我把拆这个项目时遇到的典型问题和同学常踩的坑按「现象→原因→解决」整理出来每条背后都有真实案例支撑。5.1 标签维度错位单通道变三通道导致的惨案现象训练时损失函数在第一个epoch就报错size mismatch提示目标张量和预测张量维度不一致或者loss数值剧烈震荡完全不想收敛。原因用PIL.Image.open读取标签PNG时如果标签图被误存为RGB三通道mask.shape变成[H, W, 3]但模型输出是[B, num_classes, H, W]维度对不上。另外调色板模式的PNG直接转numpy会读到索引值而非实际像素值。解决读取标签统一加一行强制转单通道mask np.array(Image.open(mask_path).convert(L))。同时检查标签像素值是否连续UNet输出经过softmax后按类别索引预测标签值必须从0开始连续排到num_classes-1。用np.unique(mask)快速检查如果有值直接跳到5那中间3和4你等于永远不学。5.2 显存溢出OOM不一定靠调小batch_size解决现象训练到第几百个iteration时报CUDA out of memory程序直接崩掉。原因除了batch_size太大还有一个隐蔽变量是输入图像尺寸没有对齐。UNet做了4次下采样图像尺寸必须是16的倍数如果你切块设成270x270下采样到16x16时尺寸不够整除某些CUDA内核会申请额外缓存导致显存莫名飙升。解决size设成16的倍数最保守的是256或512。另外检查代码里是否每轮迭代都在构建计算图loss.backward()之后没调用optimizer.zero_grad()会导致梯度累加显存只增不减。如果batch必须大开启torch.cuda.amp混合精度训练能省约40%显存scaler torch.cuda.amp.GradScaler() for images, masks in train_loader: optimizer.zero_grad() with torch.cuda.amp.autocast(): outputs model(images) loss criterion(outputs, masks) scaler.scale(loss).backward() scaler.step(optimizer) scaler.update()5.3 验证集IoU计算有玄学被背景类支配的mIoU现象训练日志里mIoU一直在0.85以上但打开预测图发现建筑物边缘狗啃一样甚至整片漏检。原因遥感场景背景类占比过高比如一张图里建筑只占5%哪怕全预测成背景总体像素准确率也有95%。代码里直接除以类别总数算均值背景类IoU高达0.99把建筑类的0.45拉高到了0.85。解决在3.4节的计算函数里返回一个per_class_iou数组而不是直接返回均值训练每5个epoch打印所有类别的IoU。对占比过低的类别如建筑、道路用torch.argmax再做torch.where掩膜。从中期开始对比各类别IoU走势如果背景类攀升但建筑类停滞就要考虑加weighted cross entropy或Focal Loss给低频类别更大权重。5.4 TensorBoard没有曲线Port占用与logdir路径双重问题现象运行start_tensorboard.ps1后浏览器打开显示空白或者只有标量面板但没有训练曲线。原因常见原因有两个——默认端口6006被其他进程占用导致TensorBoard启动失败但PowerShell窗口不报错另一个是SummaryWriter写入的log_dir和启动TensorBoard时指定的--logdir不是同一个目录比如一个指向logs/exp1一个指向logs嵌套层级不匹配导致找不到事件文件。解决先看PowerShell窗口是否打印了TensorBoard 2.x.x at http://localhost:6006如果端口被占改用tensorboard --logdirlogs --port6007。路径方面统一用绝对路径别在脚本里写相对路径。还有一个代码层面的坑如果训练中断后重启了新的SummaryWriter但没换log_dir新旧事件文件会混在一起曲线会断开重画。建议每次实验加时间戳后缀。5.5 图像归一化方式导致预测结果整体偏移现象训练损失正常收敛但预测出来的分割图整体颜色偏暗或高亮区域过曝类别边界还算清晰但类别归属错误。原因data.py里训练时做了归一化推理脚本里忘记做完全相同的前处理——比如训练时除以255再减均值推理时只除了255输入分布不一致导致特征偏移。解决把归一化逻辑抽成一个独立函数训练和推理共用同一个函数调用。更稳妥的方式是训练时不做减均值操作只做除以255缩放到01区间在推理时用完全相同的缩放。遥感影像的统计特性跟自然图像差别很大用ImageNet的均值和标准差反而可能降精度自己统计数据集均值才是正路。5.6 .DS_Store和__pycache__混入数据集目录这个资源里有.DS_Store文件存在虽然不影响训练但如果data.py用os.listdir扫描数据目录时不过滤文件类型会把.DS_Store当成图片去读取直接报cannot identify image file。写数据加载代码时对文件后缀做白名单过滤是基本素养。valid_extensions (.png, .jpg, .jpeg, .tif, .tiff) files [f for f in os.listdir(dir) if f.lower().endswith(valid_extensions)]6. 从UNet出发的进阶路子改模型结构、评估细节、训练技巧一个都不能少如果你的目的不只是毕业而是想把分割精度再往上推一推或者这个项目做完还要接更大的数据下面几个方向可以沿着现有代码直接扩展。6.1 残差连接与注意力模块低成本改进模型基础UNet用DoubleConv做特征提取泛化能力没问题但对遥感影像中边界模糊的小目标——比如狭窄道路、小型水体——容易漏检。常见做法是把DoubleConv换成残差双卷积块加一个跳跃加法让梯度更顺class ResDoubleConv(nn.Module): 带残差连接的双卷积块缓解深层梯度消失并提升细节恢复 def __init__(self, in_ch, out_ch): super(ResDoubleConv, self).__init__() self.conv1 nn.Conv2d(in_ch, out_ch, 3, padding1) self.bn1 nn.BatchNorm2d(out_ch) self.conv2 nn.Conv2d(out_ch, out_ch, 3, padding1) self.bn2 nn.BatchNorm2d(out_ch) self.shortcut nn.Sequential() if in_ch ! out_ch: self.shortcut nn.Conv2d(in_ch, out_ch, 1) def forward(self, x): out torch.relu(self.bn1(self.conv1(x))) out self.bn2(self.conv2(out)) return torch.relu(out self.shortcut(x))残差连接的价值在于当通道数变化时用1x1卷积做映射让输入和输出能直接相加。改成这个结构后原来50个epoch才能收敛的模型通常35个epoch就能达到相近指标。再进一步就是在解码器跳跃连接之前加注意力门控对编码器特征做空间加权这个改动代码量不大但对边界改善明显。6.2 预测图后处理CRF与多数投票到底要不要用训练完模型做完预测很多同学直接拿argmax结果当最终输出但single-model预测在边缘处通常有椒盐噪声。常见的后处理方案是条件随机场CRF做边缘平滑但对遥感影像这种大尺寸稠密预测图CRF在CPU上跑一张512x512的图可能需要几十秒性价比很低。我更推荐的做法是测试时增强TTA预测时把输入分别做水平翻转、垂直翻转、旋转90度然后把多个预测概率取平均再argmax。这本质上是在用推理时间换精度通常能提升1到2个点的mIoU主要收益集中在小目标上。如果接入了多尺度预测——不同切块尺寸各跑一遍再融合——再叠加TTA精度提升更明显。def predict_tta(model, image_tensor): TTA预测原图水平翻转垂直翻转三路平均 model.eval() with torch.no_grad(): preds [torch.softmax(model(image_tensor), dim1)] preds.append(torch.softmax(model(torch.flip(image_tensor, [3])), dim1)) preds.append(torch.softmax(model(torch.flip(image_tensor, [2])), dim1)) final torch.stack(preds).mean(dim0) return torch.argmax(final, dim1)6.3 训练细节早停、学习率调度和类别加权还没完训练本身也有讲究。数据集标注不均衡时常规CrossEntropyLoss会被样本量大的类别主导。我一般先统计每个类别像素占比然后构造权重向量import torch.nn as nn class_weights torch.tensor([1.0, 2.5, 3.0, 1.8]) # 按背景类为基准加权 criterion nn.CrossEntropyLoss(weightclass_weights)背景类占比大权重给1.0建筑物这类关键目标占比小权重给2.5以上。这个权重不是玄学直接等于总像素数除以该类像素数再归一化。配合ReduceLROnPlateau调度器动态调整学习率训练过程会更顺滑scheduler torch.optim.lr_scheduler.ReduceLROnPlateau( optimizer, modemax, factor0.5, patience10 ) # 每个epoch结束后 scheduler.step(val_iou) # 监听验证集IoU而不是训练损失关注验证集IoU连续10个epoch不涨就把学习率减半能有效防止模型在训练集上死磕导致过拟合。这些调参经验磨合下来我在拆完这个项目后养成了一个习惯每次训练前都会先看一眼类别分布确认权重设置对路再把batch size调到显存能承受的上限——这两步做完再挂后台训练翻车概率至少减半。希望这份资源能帮你把UNet遥感分割这个流程跑顺后续改成自己的数据集时少走弯路。本文还有配套的精品资源点击获取
返回列表