查看全课大纲(第 76 / 93 节)
  1. 1.计算机视觉概述
  2. 2.计算机视觉应用场景
  3. 3.困难与挑战
  4. 4.开源库介绍
  5. 5.图像显示与存储
  6. 6.CLAHE
  7. 7.腐蚀膨胀与开闭运算
  8. 8.滤波与边缘填充
  9. 9.各种滤波
  10. 10.图像金字塔
  11. 11.傅里叶变换
  12. 12.短时傅里叶变换与小波变换
  13. 13.颜色特征与颜色直方图
  14. 14.几何特征_边缘
  15. 15.几何特征-角点与斑点
  16. 16.局部特征-SIFT特征描述子
  17. 17.局部特征-SURF特征描述子
  18. 18.局部特征-ORB特征描述子
  19. 19.其他特征描述子
  20. 20.传统图像分割技术
  21. 21.GraphCut
  22. 22.GrabCut
  23. 23.人脸检测的传统方法
  24. 24.行人检测的传统方法HOG+SVM
  25. 25.行人检测DPM
  26. 26.人工神经网络的架构
  27. 27.目标函数与梯度下降
  28. 28.误差反向传播
  29. 29.可手算的神经网络
  30. 30.改进目标函数与克服梯度消散
  31. 31.神经网络之传统神经网络与深度网络的差异
  32. 32.各种梯度下降算法
  33. 33.如何选择合适的梯度下降算法
  34. 34.避免过适应
  35. 35.CNN的架构
  36. 36.CNN的误差反向传播_卷积层
  37. 37.CNN的误差反向传播_池化层
  38. 38.分类所用的数据集
  39. 39.Alexnet详解
  40. 40.VGG详解
  41. 41.GoogLeNet_v1
  42. 42.BatchNormalization
  43. 43.GoogLeNet_v3v4
  44. 44.ResNet
  45. 45.ResNeXt
  46. 46.CNN设计准则
  47. 47.图像检索与推土机距离
  48. 48.基于颜色相似性
  49. 49.基于形状相似性
  50. 50.基于纹理相似性
  51. 51.基于局部特征相似性
  52. 52.DL的二值哈希编码
  53. 53.高维空间中检索KDTree
  54. 54.高维空间中检索LSH
  55. 55.目标识别任务综述
  56. 56.RCNN
  57. 57.SPP-Net
  58. 58.Fast R-CNN
  59. 59.Faster R-CNN
  60. 60.R-CNN系列模型回顾
  61. 61.R-FCN的设计思路
  62. 62.R-FCN的实现
  63. 63.YOLOv1
  64. 64.YOLOv2_9000
  65. 65.YOLOv3
  66. 66.图像分割应用场景与数据集
  67. 67.上采样的实现方式
  68. 68.FCN网络架构的由来
  69. 69.FCN构建细节
  70. 70.DeepLabv1
  71. 71.CRF
  72. 72.DeepLabv2
  73. 73.DeepLabv3
  74. 74.特定的医学影像分割任务
  75. 75.U-Net
  76. 76.3D U-Net
  77. 77.V-Net
  78. 78.DenseNet
  79. 79.病理切片分析任务及数据集
  80. 80.病理切片分析
  81. 81.图说任务及其传统处理方法
  82. 82.深度语言模型与RNN
  83. 83.LSTM及其变种
  84. 84.NIC模型
  85. 85.SAT模型
  86. 86.图说的性能指标
  87. 87.判别模型与生成模型
  88. 88.KL散度与JS散度
  89. 89.VAE
  90. 90.GAN的基本原理
  91. 91.DCGAN与CGAN
  92. 92.Wasserstein GAN
  93. 93.SRGAN

3D U-Net

小象实战讲义 · 基于深度学习的计算机视觉

在上一节中,我们学习了用于医学影像分割的经典网络U-Net。然而,在处理真实的3D医学影像数据(如MRI、CT)时,逐层切片处理的2D U-Net面临一些固有挑战。本节我们将介绍3D U-Net,它将U-Net的对称编码器-解码器架构扩展到三维空间,直接处理体素数据,从而更好地利用三维上下文信息,提升对器官整体结构和边界的识别能力。

💡 核心导读

  • 从2D到3D的动机:理解为何在医学影像分割中需要从二维平面处理升级到三维体素处理。
  • 三维卷积详解:掌握三维卷积核的维度构成、计算过程及其与二维卷积的本质区别。
  • 3D U-Net架构解析:深入分析其分析路径(编码器)、合成路径(解码器)以及跨层连接的设计。
  • 性能与应用:了解3D U-Net在三维显微体数据分割任务上的实际效果和评估指标。

从2D U-Net到3D U-Net的演进

在医学影像分析中,我们处理的往往是三维体数据,例如由一系列连续切片组成的MRI或CT扫描。传统的2D U-Net采用逐片(slice-by-slice)处理的方式,虽然有效,但也存在一些局限性:

  1. 截面差异问题:对于许多器官(如前列腺、肝脏),其顶层和底层的截面形态与中部截面差异显著。2D网络单独处理每个切片时,可能难以准确识别这些形态突变的区域。
  2. 伪影与畸变干扰:医学影像中常因磁场不均匀、患者移动等因素产生伪影或几何畸变。这些干扰在单个2D切片上可能难以与真实组织区分,但若在三维空间中观察,其连续性或空间分布模式可能提供更多鉴别线索。
  3. 上下文信息缺失:2D处理丢失了切片间的空间连续性信息。而器官的完整形态、病灶的立体范围等信息对于精确分割至关重要。

一个直观的解决方案是:直接使用三维的影像数据作为输入,并设计一个能处理三维数据的网络。这不仅能够天然地缓解上述问题,也符合生物医学数据本身广泛以3D形式存在的现实。3D U-Net正是基于这一思路提出的。

三维卷积:核心操作解析

理解3D U-Net的基础在于理解三维卷积。我们可以通过与熟悉的二维卷积类比来掌握它。

二维卷积回顾

对于一个二维输入图像,其形状通常表示为 (高度H, 宽度W, 通道数C)。一个二维卷积核的形状则为 (卷积核高f_h, 卷积核宽f_w, 输入通道数C)。当我们有 N 个这样的卷积核时,整个卷积层的权重张量维度就是 (f_h, f_w, C, N)。卷积操作在每个空间位置(H, W)上,对C个输入通道进行加权求和,最终输出一个形状为 (H_out, W_out, N) 的特征图。

三维卷积升维

现在,假设我们的输入是一个三维体数据块(例如一个MRI子体积),其形状为 (深度D, 高度H, 宽度W, 通道数C)。这里的深度D就是第三个体素维度(如切片方向)。

为了处理这样的输入,卷积核也需要增加一个深度维度。因此,一个三维卷积核的形状变为 (卷积核深f_d, 卷积核高f_h, 卷积核宽f_w, 输入通道数C)。

同样地,如果我们有 N 个这样的三维卷积核,那么该卷积层的权重张量维度就是 (f_d, f_h, f_w, C, N)。这是一个五维张量。

维度的含义总结:

  • 对于一个三维卷积核:其四个维度分别是 (深度, 高度, 宽度, 输入通道数)。
  • 对于三维卷积层(包含N个卷积核):其五个维度分别是 (深度, 高度, 宽度, 输入通道数, 输出通道数(卷积核个数))。

三维卷积的输出计算

给定输入体数据大小 (D, H, W, C),卷积核大小 (f_d, f_h, f_w),步长(通常各维度相同),以及填充方式,输出体数据的大小可以通过与二维类似的公式计算:

Dout=D−fd+2PdSd+1Hout=H−fh+2PhSh+1Wout=W−fw+2PwSw+1Cout=N\begin{aligned} D_{out} &= \frac{D - f_d + 2P_d}{S_d} + 1 \\ H_{out} &= \frac{H - f_h + 2P_h}{S_h} + 1 \\ W_{out} &= \frac{W - f_w + 2P_w}{S_w} + 1 \\ C_{out} &= N \end{aligned}

其中,P 为填充数,S 为步长。输出是一个四维张量 (D_out, H_out, W_out, N)。可以看到,在卷积过程中,输入的所有通道 C 的信息被融合到了每个输出通道中。

下面我们用一个小例子来直观感受三维卷积。我们将模拟一个微小的3D体数据(2x3x3)并应用一个2x2x2的卷积核。

import numpy as np

# 模拟一个简单的3D输入体数据 (深度D=2, 高度H=3, 宽度W=3, 通道C=1)
# 为了简化,我们只使用单通道。形状为 (2, 3, 3, 1)
input_3d = np.array([
    [[[1], [2], [3]],
     [[4], [5], [6]],
     [[7], [8], [9]]],
    [[[10], [11], [12]],
     [[13], [14], [15]],
     [[16], [17], [18]]]
])
print("3D输入数据形状:", input_3d.shape)
print("输入数据(去掉通道维后显示):")
print(input_3d.squeeze()) # 移除通道维度以便打印

# 定义一个2x2x2的三维卷积核 (f_d=2, f_h=2, f_w=2, C_in=1, N=1)
# 核的权重我们简单设为全1。形状为 (2, 2, 2, 1, 1)
kernel_3d = np.ones((2, 2, 2, 1, 1))

# 为了演示,我们手动计算一个输出位置。
# 假设步长S=1,无填充(P=0)。
# 输出大小: D_out = (2-2)/1 +1 =1, H_out=(3-2)/1+1=2, W_out=(3-2)/1+1=2
# 计算输出位置(0,0,0)的值:对应输入体(0:2, 0:2, 0:2)的区域与核进行逐元素相乘后求和。
input_patch = input_3d[0:2, 0:2, 0:2, :] # 形状 (2,2,2,1)
output_value = np.sum(input_patch[..., 0] * kernel_3d[..., 0, 0])  # 去掉通道/输出通道维后逐元素相乘
print(f"\n输入体在位置(0:2,0:2,0:2)的块:")
print(input_patch.squeeze())
print(f"与全1核卷积后,输出位置(0,0,0)的值: {output_value}")
3D输入数据形状: (2, 3, 3, 1)
输入数据(去掉通道维后显示):
[[[ 1  2  3]
  [ 4  5  6]
  [ 7  8  9]]

 [[10 11 12]
  [13 14 15]
  [16 17 18]]]

输入体在位置(0:2,0:2,0:2)的块:
[[[ 1  2]
  [ 4  5]]

 [[10 11]
  [13 14]]]
与全1核卷积后,输出位置(0,0,0)的值: 60.0

通过这个例子,我们可以看到三维卷积是在一个立体的邻域内进行特征提取。在实际的3D U-Net中,会使用大量的这样的卷积核来构建深度网络。

3D U-Net网络架构详解

3D U-Net的整体设计思想与2D U-Net一脉相承,都采用了对称的编码器-解码器结构,并辅以跨层连接(Skip Connections)。其主要区别在于将所有操作替换为对应的三维版本。

整体结构概览

网络由两部分组成:

  • 分析路径(Analysis Path,即编码器):负责逐步下采样,提取多层次、抽象的特征,同时感受野增大。
  • 合成路径(Synthesis Path,即解码器):负责逐步上采样,将抽象特征映射回高分辨率的像素空间,实现精确的体素级预测。

两路径之间,相同分辨率层级的特征图通过跨层连接直接拼接(Concatenate),使得解码器在重建细节时能够利用编码器保留的底层高分辨率信息,这对于恢复分割目标的边界至关重要。

原论文中使用的网络总参数量约为1900万。输入是一个 132 × 132 × 116 的体素块(3通道),经过网络处理后,输出一个 44 × 44 × 28 的分割概率图。

分析路径(编码器)设计

分析路径由多个层级(Stage)组成,每个层级通常包含以下操作序列:

  1. 三维卷积块:重复两次 3×3×3 卷积 + 批归一化(Batch Normalization) + ReLU激活函数。这是特征提取的核心单元。
  2. 三维最大池化:使用 2×2×2 的池化核,步长为2,在每个空间维度(D, H, W)上进行下采样。经过池化后,特征图的每个维度尺寸减半,通道数通常加倍(通过下一层的卷积核数量实现)。

这个过程重复数次,特征图的空间尺寸越来越小,通道数越来越多,语义信息越来越抽象。

合成路径(解码器)设计

合成路径是分析路径的镜像,执行相反的操作:

  1. 三维反卷积(转置卷积):使用 2×2×2 的核,步长为2,进行上采样。它将特征图在每个空间维度上尺寸放大一倍,同时减少通道数。
  2. 特征拼接:将上采样后的特征图与来自分析路径相同分辨率层级的特征图进行拼接(Concatenate)。这提供了恢复空间细节所需的高分辨率上下文。
  3. 三维卷积块:拼接后的特征图再经过两次 3×3×3 卷积 + ReLU(注意,在解码器部分,原论文图示中未强调使用批归一化),进一步融合信息。

输出层

在解码器的末端,经过一系列上采样和卷积后,特征图恢复到与输入相近的分辨率(可能因步长和填充略有不同)。最后使用一个 1×1×1 的卷积层将通道数映射到目标类别数 K。对于二分类任务,K=2(背景和前景);在原论文的实验中,标签类别数取 K=3,即在背景之外还区分两类前景结构。对于每个体素,输出层通常接一个Softmax激活函数,得到属于每个类别的概率。

下面的伪代码勾勒了3D U-Net一个简化版本的核心构建逻辑:

# 伪代码:3D U-Net 构建思路 (基于原课TensorFlow 1.x逻辑)
# 注意:此为历史代码逻辑示意,现代实现应使用tf.keras或PyTorch。

def conv3d_block(input_tensor, filters):
    """一个3D卷积块:Conv3D -> BN -> ReLU,重复两次"""
    x = tf.layers.conv3d(input_tensor, filters, 3, padding='same')
    x = tf.layers.batch_normalization(x)
    x = tf.nn.relu(x)
    x = tf.layers.conv3d(x, filters, 3, padding='same')
    x = tf.layers.batch_normalization(x)
    x = tf.nn.relu(x)
    return x

def encoder_block(input_tensor, filters):
    """编码器的一个阶段:卷积块 -> 池化"""
    x = conv3d_block(input_tensor, filters)
    # 保存当前层特征,用于后续跨层连接
    skip_connection = x
    x = tf.layers.max_pooling3d(x, pool_size=2, strides=2)
    return x, skip_connection

def decoder_block(input_tensor, skip_features, filters):
    """解码器的一个阶段:上采样 -> 拼接 -> 卷积块"""
    # 上采样(反卷积)
    x = tf.layers.conv3d_transpose(input_tensor, filters, kernel_size=2, strides=2)
    # 与编码器对应层的特征拼接
    x = tf.concat([x, skip_features], axis=-1) # 在通道维度拼接
    x = conv3d_block(x, filters)
    return x

# 网络构建流程示意
input_img = tf.placeholder(tf.float32, shape=[None, D, H, W, C])
# 编码过程
x, skip1 = encoder_block(input_img, filters=64)
x, skip2 = encoder_block(x, filters=128)
x, skip3 = encoder_block(x, filters=256)
# 瓶颈层(最底层)
bottleneck = conv3d_block(x, filters=512)
# 解码过程
x = decoder_block(bottleneck, skip3, filters=256)
x = decoder_block(x, skip2, filters=128)
x = decoder_block(x, skip1, filters=64)
# 输出层
output = tf.layers.conv3d(x, filters=num_classes, kernel_size=1, activation='softmax')

性能与总结

在原论文的实验中,作者在非洲爪蟾(Xenopus laevis)胚胎肾脏的共聚焦显微体数据上进行了三折交叉验证。需要特别说明的是,3D U-Net 的核心设定是从稀疏标注的体数据学习稠密分割(原论文标题即 3D U-Net: Learning Dense Volumetric Segmentation from Sparse Annotation):训练时只需标注部分切片,网络即可推断整个体积的分割。在上述数据上,3D U-Net 取得平均交并比(IoU)0.863,相较于单纯应用 2D U-Net 在切片上的表现有所提升。这验证了直接利用三维上下文信息对于医学影像分割的有效性。

3D U-Net的核心贡献在于:把 U-Net 的分割范式推广到三维数据,并给出「稀疏标注 → 稠密分割」的可训练方案——它既利用三维上下文,又大幅降低了体数据的标注成本。它解决了 2D 处理中因切片间信息割裂而带来的部分问题,尤其擅长处理具有复杂三维结构的器官分割任务。此后,基于3D卷积的医学影像分割网络成为研究主流,并衍生出如 V-Net 等更多改进模型。

📝 动手练一练

  1. 维度思考题:对于一个三维卷积层,如果输入体数据的形状是 (64, 64, 64, 3)(即深度、高度、宽度各64,3通道),卷积核大小是 3×3×3,输出通道数(卷积核个数)为64,且不使用偏置项(bias)。请问这个卷积层的权重参数总数量是多少?

  2. 代码理解题:在3D U-Net的跨层连接中,编码器某层的特征图形状为 (32, 32, 32, 128),经过一次 2×2×2 步长为2的最大池化后,输入到下一层。在对应的解码器层,来自上一层的特征图形状为 (16, 16, 16, 256),经过 2×2×2 步长为2的反卷积上采样后,形状变为 (32, 32, 32, 128)。请问,在将这两个形状相同的特征图进行拼接(Concatenate)后,得到的特征图形状是什么?

👉 点击查看参考答案
  1. 参数计算:

    • 单个三维卷积核的参数 = 核深度×核高度×核宽度×输入通道数 = 3 * 3 * 3 * 3 = 81。
    • 共有64个这样的卷积核,因此总参数 = 81 * 64 = 5184。
    • 答案:5184。
  2. 形状计算:

    • 来自编码器的特征图形状:(32, 32, 32, 128)。
    • 来自解码器上采样后的特征图形状:(32, 32, 32, 128)。
    • 拼接(Concatenate)操作在通道维度(最后一个维度) 上进行。
    • 拼接后的通道数 = 128 + 128 = 256。
    • 因此,最终特征图形状为:(32, 32, 32, 256)。

本章小结

本节我们深入探讨了3D U-Net,它是U-Net架构在三维医学影像分割领域的自然延伸。我们首先分析了2D方法的局限,进而引出直接处理体素数据的必要性。核心在于掌握了三维卷积的维度含义与计算逻辑。随后,我们系统剖析了3D U-Net的对称编码器-解码器架构,包括分析路径的下采样、合成路径的上采样以及关键的跨层连接设计,这些共同保障了网络既能捕获高级语义,又能重建精细边界。

今天,3D U-Net 仍是三维医学分割的经典基线,后续有 V-Net、nnU-Net(3D 配置)、Swin UNETR 等更强变体;但“3D 卷积 + 编解码器 + 跨层拼接”仍是体素级分割的基本范式。

📋 行动清单

  • 理清维度:确保能清晰说出三维卷积核与三维卷积层权重张量每一个维度的具体含义。
  • 对比学习:在脑海中绘制出2D U-Net和3D U-Net的结构对比图,明确哪些操作从2D扩展到了3D。
  • 思考应用:结合医学影像特点(如MRI的3D特性),理解3D U-Net如何利用空间上下文信息改善分割效果。

—— 小象教研组