5.2 UNet与SegNet保细节


5.2 U-Net 与 SegNet:把细节保回来

本节摘要:编码器-解码器呈 U 型:一侧下采样抽语义,一侧上采样还原格子。U-Net 把编码器特征拼接进对称的解码层,用通道把细节送回去,小样本医学上极常用。SegNet 不拼接大特征,而记住池化最大值的位置做反池化,更省显存。二者都接到同一训练环,差别在「细节怎么运回来」。

本节导读

阅读完本节,你应当能够:

  1. 画出 U 型与跳跃拼接,说明拼接和 FCN 相加的不同
  2. 按显存与数据量在 U-Net 与 SegNet 之间做取舍
  3. 实现或读懂「双卷积-下采样」积木与对称上采样积木
  4. 在小器官数据上用 Dice 加交叉熵训练,并可视化跳跃是否在干活

一、为什么要 U 型

一路下采样到瓶颈,语义强、格子少。只从瓶颈插值回去,等于用一张小邮票放大,器官边界必糊。解码器逐步加倍空间,同时向编码器借同尺度的特征。借的方式:U-Net 拼接(通道变多再卷积压回去),FCN 相加(通道先拧成一样)。拼接保留浅层的原始细节通道,参数更多,细节通常更好。医学图结构相对固定、样本少,这套归纳偏置很合身。

原文强调跳跃连接把编码器特征传到解码器以保留细节。动手时注意裁切:若编码器因无填充导致每层少两圈像素,拼接前要对齐中心裁切。现在实现多用填充保持偶数尺寸,少这一烦。

class DoubleConv(nn.Module): def __init__(self, cin, cout): super().__init__() self.net = nn.Sequential( nn.Conv2d(cin, cout, 3, padding=1), nn.ReLU(inplace=True), nn.Conv2d(cout, cout, 3, padding=1), nn.ReLU(inplace=True)) def forward(self, x): return self.net(x) # 解码一步:上采样,与跳跃拼接,再双卷积 def up_block(x, skip, up, conv): x = up(x) x = torch.cat([x, skip], dim=1) return conv(x)

原文简化示例只有两层卷积加 Sigmoid,那是为了展示环。真正的 U-Net 重复「双卷积 + 池化」四次左右,再对称回去,最后 1×1 到类别数。二类用一个通道加 Sigmoid 也可以,与 4.3 节损失配对。

二、SegNet:用索引代替拼接

池化时记下最大值来自哪个位置,上采样时把值放回那些位置,其余补零,再卷积填洞。显存不存一整份浅层特征,只存索引。细节不如拼接那么浓,但对显存紧、类别为道路标线这类边缘尚可的任务够用。不要把 SegNet 理解成「过时的 U-Net」。它是另一种运细节的物流:运货单(索引)而不是运整箱货(特征图)。

结构 细节通路 显存 适合
FCN 相加 通用语义基线
U-Net 拼接 小数据、要边界
SegNet 索引 较低 显存紧、场景规则

⚠️ 常见坑:拼接时批次维、通道维搞反,或上采样后尺寸比跳跃多 1 像素。assert 高宽相等应写在 up_block 里。
💡 关键直觉:U 型不是审美,是物流。细节必须有一条不经过瓶颈的路回家。

三、训练时的脾性

小样本:强几何增强要克制,见第 2.3 节。Dropout 和权重衰减防背图。批次小导致批归一统计差,可换组归一。学习率对从头训的 U-Net 可用 Adam 3e-4 量级试,不是抄分类网的。深度加宽会迅速撑爆显卡,优先保输入分辨率。

跳跃是否在干活:可把某层跳跃置零看 Dice 掉多少。掉得少,说明模型没走细节路,可能学习率或归一有问题,或任务根本不需要细边(那你该用更轻的网)。

U-Net 变体很多:残差、注意力门、三维。本教程不追名字。原则仍是对称通路加跳跃。三维只是核变成 3×3×3,显存立方涨,切片间上下文才值得付这钱。

图 U 型物流

图 U 型物流

四、怎么选

要边界、数据少、二维:U-Net 默认。显存不够:减通道或 SegNet 或浅 U。要实例计数:不要指望 U-Net 的连通域,去 Mask R-CNN 或分水岭后处理。要多尺度街景:DeepLab 可能更合适。把 U-Net 接到第 4.2 节环上,损失用 4.3 节,数据用第 2 章加载器——这是本教程最推荐的第一条完整深度学习路径。

五、通道数与分辨率怎么换

显存不够时,先减每层通道(64 起改 32),再减深度,最后才减输入边长。边长一减,细结构先没。医学切片常要保边长。组归一在小批次下比批归一稳,换归一方法等于换超参,验证要重做,不要和换学习率同时做。

跳跃置零消融:训练好后推理时把最浅跳跃乘零,看 Dice 和边界。掉得多,说明细节路在干活。掉得少,可能任务不需要细边,或学习没走这条路——检查通道拼接后的卷积是否把浅层压死,或学习率让深层先占满。

U-Net 的四次下采样要求输入能被 16 整除。奇数边长要垫。三维 U-Net 的显存随深度立方涨,先问切片间上下文是否真值需要:若病理只在切片内可见,二维加轻量切片间后处理更便宜。

问题:注意力门控 U-Net 要不要上?

在主路径 U-Net 已经稳定、边界仍差、还有预算时再试。不要当第一选择。名字多不代表你的 80 张切片需要它。对比实验一次只改门控这一处。

SegNet 的索引在池化核大于 2 或重叠池化时要格外小心实现。多数库的最大池化记索引可用现成接口。自己写容易把索引和上采样尺寸写错。显存紧且场景规则(车道线)时值得;细胞细边界仍优先拼接。

把 U-Net 接到第 4 章环、第 4.3 节 Dice 加交叉熵、第 2 章加载器,跑完三阶段开火,这是本教程推荐的默认深度路径。其他结构都是这条路径上的模块替换。

六、显存与消融

先减通道再减深度最后减边长。小批次换组归一,不要同时换学习率。跳跃置零看 Dice:掉得多说明细节路在干活。输入被 16 整除。三维核只有切片间上下文真正需要时才上。注意力门控不是第一选择。SegNet 用索引省显存,细边界仍优先拼接。默认深度路径:环加 U-Net 加 Dice,其他结构都是替换模块。

减通道优先于减边长。置零跳跃做消融。被 16 整除。组归一与学习率不要同天改。门控不是第一选择。默认路径环加 U-Net 加 Dice。三维只在切片间真需要时。拼接差 1 像素写进 up_block 的 assert,报错信息含两路高宽,省去猜是上采样还是裁切。SegNet 索引实现用库接口,自写出错率高。

显存不够的改动顺序写进配置注释:通道、深度、边长。置零跳跃脚本独立,不靠手改代码。16 整除在加载器查。组归一与学习率变更分两次实验。门控默认关。默认路径名称写死 unet_dice。三维开关默认关并要求填写「切片间需要」理由。up_block assert 信息含两路高宽。SegNet 只用库索引。拼接通道数等于跳跃加解码,断言。最浅跳跃置零若 Dice 几乎不掉,开 issue 查学习是否走细节路,而不是先加注意力门。减边长必须同步黄金图最小缺陷检查,与第 6.3 节相连。U-Net 作为默认深度路径,替换模块时测试套件仍跑浅层尺寸账与单图过拟合,不因换了 SegNet 就删。

反例档案:一上来减边长,小缺陷没了,还怪 Dice。改成先减通道后恢复。第二份:最浅跳跃置零 Dice 不掉,却先加门控。开 issue 查学习路径后才发现头学习率过大压死浅层。16 整除放加载器。默认路径名称 unet_dice 写死,避免口头「就是那个 U」。

口令卡:先通道后边长、置零脚本独立、16 整除加载器、归一学习率分实验、门控默认关、unet_dice 写死、三维要理由、assert 含两路、库索引、拼接通道断言。置零不掉先查头学习率。减边长连黄金图。

插模块前:unet_dice 名存在、16 整除、置零脚本可跑、通道断言、库索引、三维关且无空理由、门控关。减边长必须带黄金图结果。置零不掉先查头学习率再加结构。归一与学习率分两次提交。先通道后边长写在显存不足 runbook。默认路径被改名视为未选主模型。

交付门禁子

默认路径名不是 unet_dice 视为未选主模型。16 不能整除的图进不了加载器。置零脚本跑不通不准谈门控。拼接通道断言失败不准训。自写池化索引不准用。三维无理由不准开。减边长无黄金图不准开。置零 Dice 不掉却加结构的请求退回。显存 runbook 必须写先减通道。归一和学习率同提交退回。

现场抽问:路径名是不是 unet_dice?16 整除谁查?置零脚本能跑吗?拼接断言在吗?索引是不是库的?三维理由写了吗?减边长带黄金图了吗?置零不掉你先查了头学习率吗?runbook 写没写先减通道?归一和学习率是不是分开提交的?

失败演练辛:路径改名导致默认丢失,应拒。置零脚本坏了还加门控,应拒。自写索引,应拒。减边长不带黄金图,应拒。置零不掉不查学习率却加层,应拒。16 整除放模型里而不是加载器,应拒。三维无理由打开,应拒。runbook 先减边长,应改成先减通道。归一与学习率捆成一次提交,应拆开。主模型名必须能在配置里搜到 unet_dice。

配置键必须能搜到默认路径名。置零脚本列入必跑。黄金图绑定减边长。runbook 文本检查「先减通道」。三维理由字段非空才能开。门控默认值检查。自写索引搜索为零。加载器负责 16 整除。主模型未选时训练入口直接退出。

训练入口搜不到默认路径名就退出。置零、黄金图、runbook、三维理由、门控默认、索引搜索、16 整除,七项写成入口检查。检查失败不准开始第一个 epoch。这比再讲一遍跳跃连接更接近动手。

第一个 epoch 之前必须跑完入口七项。任一项失败退出码非零。默认路径名写在配置样例第一行,避免口头传递丢失。减边长请求没有黄金图附件直接关闭。

入口七项失败则一个 epoch 都不准开始。配置样例第一行就是默认路径名。黄金图附件缺失的减边长请求关闭后必须重开并带图,不得在原请求里补一句「稍后补」。稍后补等于没有图,入口继续失败直到附件真正存在。真正存在指文件能打开且能看到三张对照,而不是占位空文件。占位空文件在校验里按不存在处理。三维理由空字符串也算无理由。空字符串与缺字段同等对待,入口检查不得放行。

跳跃连接是为了把编码器的定位细节送回解码器,不是装饰线。通道拼接后卷积要压回计划通道数。SegNet 记池化索引、U-Net 记特征图,两者不要混用一套权重。黄金图上边界发虚,先查跳跃是否接错层,再加空洞卷积。

本节速览

  • U 型是编码解码对称:瓶颈不够,要跳跃
  • U-Net 拼接、FCN 相加、SegNet 索引:三种物流
  • 尺寸对齐写进积木:差 1 像素是常客
  • 小样本克制增强与保分辨率:别盲目加深
  • 用置零跳跃做消融:看细节路有没有被走
  • 默认第一条深度路径:环 + U-Net + Dice

下一节处理「下采样太猛、物体尺度乱」:空洞卷积和空间金字塔,不必把格子砍得那么碎。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U