查看全课大纲(第 75 / 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

U-Net

U-Net:医疗影像分割的经典网络

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

在上一节中,我们介绍了医学影像分割的任务与数据集。面对数据量有限但网络参数庞大的挑战,一个精心设计的网络架构至关重要。本节我们将深入讲解医疗影像分割领域的里程碑式网络——U-Net。我们将从经典的U-Net结构入手,分析其设计思想,并重点剖析一个在PROMISE12挑战赛中取得优异成绩的改进版U-Net,理解其如何通过架构优化、数据增强等技巧,在有限数据下实现高效分割。

💡 核心导读

  • U-Net的设计哲学:理解其独特的“U”形编码器-解码器结构,以及跳跃连接(Skip Connection)如何解决FCN中上采样信息丢失的问题。
  • 改进版U-Net的架构细节:学习一个实战中用于前列腺分割的U-Net变体,包括其卷积块、上采样方式和输出层设计。
  • 医学影像分割的专用技巧:掌握曲率驱动去噪、弹性变形等针对医学影像特点的预处理与数据增强方法。
  • Dice系数损失函数:学习在分割任务中常用的评估与优化指标,理解其与IoU(交并比)的关系。

U-Net:从FCN的不足到解决方案

在介绍FCN时,我们提到其一个主要不足是上采样过程相对“粗暴”。FCN-32s 通过单次 32 倍反卷积,把低分辨率的特征图直接放大到原始输入尺寸;FCN-16s/8s 虽然把 conv7 的 2 倍上采样结果分别与 pool4、pool3 的预测相加,做了一点跨层融合,但融合的层级很少,最终仍要靠一次大步长反卷积(16 倍或 8 倍)放大到输入尺寸。这种以“大步长一步到位”为主的放大方式,容易丢失细节信息,导致分割边界模糊。

U-Net正是针对这一问题进行了关键改进。其核心思路是渐进式上采样。U-Net不再一次性将特征图放大很多倍,而是通过一个对称的“U”形结构,逐步将特征图的尺寸恢复。在这个过程中,网络有更多的层来学习和重建细节。

更巧妙的是,U-Net引入了跳跃连接。在编码器(下采样路径)的每一层,特征图在池化缩小之前,会被复制并连接到解码器(上采样路径)对应尺寸的层。这样,解码器在进行上采样时,不仅能利用来自更深层、语义更丰富的特征,还能融合来自编码器同层、空间位置信息更精确的特征。这极大地有助于保护边界信息,让分割出的器官轮廓更加清晰。

典型的U-Net结构由三位研究者(Ronneberger, Fischer, Brox)提出,其名称来源于其网络形状:左侧的编码器路径使图像尺度逐渐减小、通道数增加(变“深”变“窄”),右侧的解码器路径使图像尺度逐渐恢复、通道数减少(变“浅”变“宽”),整体形状像一个字母“U”。

改进版U-Net:PROMISE12挑战赛实战解析

在实际的医疗影像分割任务中,研究者们往往会对经典U-Net进行改造以适应特定数据和任务。这里我们重点分析一个在PROMISE12前列腺分割挑战赛中取得第8名的开源实现(作者:mirzaevinom)。该方案的成功得益于三个关键部分:预处理、网络架构和数据增强。

1. 曲率驱动的图像去噪

医学影像(如MRI)常含有噪声,直接分割会影响精度。传统去噪方法如高斯滤波在平滑噪声的同时也会模糊边缘,而中值滤波效果虽好,但仍有改进空间。

该方案采用了一种基于等照度线曲率的去噪方法,其原理非常符合视觉直觉:

  • 等照度线:可以理解为图像中亮度相同的点连成的线。在一个光滑的物体表面(如器官),其等照度线应该是平滑的曲线,即曲率较小。
  • 噪声的影响:噪声会导致局部像素灰度值随机起伏,使得等照度线产生不规则的剧烈震荡,从而在局部形成曲率很大的线段。
  • 去噪策略:基于上述观察,算法通过检测并调整那些曲率过大的等照度线片段,使其变得平滑,从而达到在去除噪声的同时,更好地保留原始边缘结构的目的。实践证明,这种方法在医学影像上取得了良好效果。

2. 网络架构详解

这个用于生物影像分割的改进版U-Net是一个全卷积网络,输入和输出都是图像,没有全连接层。这一点与FCN一致,在分割任务中,全卷积结构是主流。

其具体架构如下图所示(我们用文字和符号描述):

输入: (256, 256, 1)  # 单通道MRI图像
|
|-- 下采样路径 (编码器)
|   |
|   |-- ConvBlock (输出: 256x256, 通道=8)
|   |-- 2x2 MaxPooling
|   |
|   |-- ConvBlock (输出: 128x128, 通道=16)
|   |-- 2x2 MaxPooling
|   |
|   |-- ConvBlock (输出: 64x64, 通道=32)
|   |-- 2x2 MaxPooling
|   |
|   |-- ConvBlock (输出: 32x32, 通道=64)
|   |-- 2x2 MaxPooling
|   |
|   |-- ConvBlock (输出: 16x16, 通道=128)
|   |-- 2x2 MaxPooling
|   |
|   |-- ConvBlock (输出: 8x8, 通道=256)
|   |-- 2x2 MaxPooling
|   |
|   `-- ConvBlock (输出: 4x4, 通道=512)
|       -- 2x2 MaxPooling
|       `-- ConvBlock (输出: 2x2, 通道=1024) # 最底层,高语义编码
|
`-- 上采样路径 (解码器)
    |
    |-- 上采样 + 与编码器对应层Copy & Concatenate
    |-- ConvBlock (输出: 4x4, 通道=512)
    |
    |-- 上采样 + 与编码器对应层Copy & Concatenate
    |-- ConvBlock (输出: 8x8, 通道=256)
    |
    |-- 上采样 + 与编码器对应层Copy & Concatenate
    |-- ConvBlock (输出: 16x16, 通道=128)
    |
    |-- 上采样 + 与编码器对应层Copy & Concatenate
    |-- ConvBlock (输出: 32x32, 通道=64)
    |
    |-- 上采样 + 与编码器对应层Copy & Concatenate
    |-- ConvBlock (输出: 64x64, 通道=32)
    |
    |-- 上采样 + 与编码器对应层Copy & Concatenate
    |-- ConvBlock (输出: 128x128, 通道=16)
    |
    `-- 上采样 + 与编码器对应层Copy & Concatenate
        -- ConvBlock (输出: 256x256, 通道=8)
        `-- 1x1 卷积 + Sigmoid激活 (输出: 256x256, 通道=1)

输出:分割掩码(Mask),白色区域代表前列腺组织。

关键组件解析:

  • 卷积块:不再是简单的卷积层堆叠。这里的ConvBlock是一个残差块,内部包含两个3x3卷积,每个卷积后接批归一化和ReLU激活,中间还有Dropout层,最后通过短路连接与输入相加。这借鉴了ResNet的思想,有助于训练更深的网络。(课程口述为“ReLU 后再 BN”;实际 Keras 实现通常采用 Conv→BN→ReLU 的顺序,这里以标准实现为准。)
  • 上采样:通常使用转置卷积(反卷积)来实现。
  • 跳跃连接:图中的“Copy & Concatenate”操作。它将编码器路径中对应尺度的特征图复制过来,与解码器上采样后的特征图沿通道维度拼接。这是信息融合的关键。
  • 输出层:使用1x1卷积将通道数降至1,然后通过Sigmoid激活函数。因为我们的任务是二分类(前景/背景),Sigmoid将每个像素的输出映射到[0,1]区间,可以理解为该像素属于前景(前列腺)的概率。通常以0.5为阈值进行二值化,得到最终的分割掩码。

这个网络共有7个下采样和7个上采样阶段,参数总量在数千万级(按本节通道图约 3000 万;原实现按完整配置报告 5000 万+)。对于PROMISE12仅50例的训练数据来说,直接训练这样的网络必然严重过拟合。因此,强大的数据增强至关重要。

3. 目标函数:Dice系数

在分割任务中,常用的损失函数是Dice系数损失。Dice系数用于度量两个样本集合的相似度,其定义为:

Dice=2∣A∩B∣∣A∣+∣B∣Dice = \frac{2|A \cap B|}{|A| + |B|}

其中,A是模型预测的前景像素集合,B是真实标注的前景像素集合。Dice系数的取值范围是[0, 1],值越大表示重叠度越高,分割效果越好。

Dice系数与我们在目标检测中熟悉的交并比有密切关系: IoU=∣A∩B∣∣A∪B∣=Dice2−DiceIoU = \frac{|A \cap B|}{|A \cup B|} = \frac{Dice}{2 - Dice}

在训练时,我们通常最小化1 - Dice作为损失函数,直接优化模型预测与真实标注之间的重叠区域。

4. 针对医学影像的数据增强

PROMISE12训练集仅50例,每例包含数十幅切片,按切片展开后总计约一千余幅图像。要训练一个数千万参数的深度网络,必须进行大规模数据增强。该方案除了使用Keras自带的平移、旋转、缩放等常规增强外,还特别采用了弹性变形技术。

为什么需要弹性变形? 人体内脏器官(如前列腺)并非刚体,其形状会因体位、重力等因素发生非线性形变。为了模拟这种自然变化,增强模型的泛化能力,我们需要一种能产生逼真形变的数据增强方法。

弹性变形的原理:

  1. 生成两个随机的小位移场(dx, dy),分别代表每个像素在x和y方向上的随机偏移量。这些位移值通常从高斯分布中采样,并经过平滑处理。
  2. 将原始图像的坐标网格(x, y)加上这个位移场,得到一个新的、扭曲的坐标网格(x', y')。
  3. 根据扭曲后的坐标网格(x', y'),通过双线性插值等方法,从原始图像中采样像素值,生成变形后的新图像。
  4. 关键:对原始图像进行弹性变形时,必须同步对其对应的标注掩码(Mask)进行完全相同的变形操作,以保证数据的一致性。

下面我们用一个小示例演示弹性变形的核心思想(简化版,仅展示坐标偏移):

import numpy as np

# 创建一个简单的5x5网格作为“图像”的坐标索引
height, width = 5, 5
y_coords, x_coords = np.meshgrid(np.arange(height), np.arange(width), indexing='ij')
# 网格坐标
print("原始坐标网格 (y, x):")
print(y_coords)
print(x_coords)

# 1. 生成简单的随机位移场(这里为了演示,使用固定的小位移)
np.random.seed(42) # 固定随机种子确保结果可重现
# 在实际弹性变形中,位移场是平滑的随机场,这里简化为每个点加一个固定范围的随机数
displacement_y = np.random.uniform(-0.3, 0.3, (height, width))
displacement_x = np.random.uniform(-0.3, 0.3, (height, width))

print("\nY方向位移场:")
print(np.round(displacement_y, 2))
print("\nX方向位移场:")
print(np.round(displacement_x, 2))

# 2. 将原始坐标网格加上位移场,得到扭曲后的坐标网格
y_coords_deformed = y_coords + displacement_y
x_coords_deformed = x_coords + displacement_x

print("\n扭曲后的Y坐标网格:")
print(np.round(y_coords_deformed, 2))
print("\n扭曲后的X坐标网格:")
print(np.round(x_coords_deformed, 2))

# 注意:得到的是浮点数坐标,后续需要通过插值(如双线性插值)来获取该位置的像素值。
# 这个示例仅展示坐标如何变化。
原始坐标网格 (y, x):
[[0 0 0 0 0]
 [1 1 1 1 1]
 [2 2 2 2 2]
 [3 3 3 3 3]
 [4 4 4 4 4]]
[[0 1 2 3 4]
 [0 1 2 3 4]
 [0 1 2 3 4]
 [0 1 2 3 4]
 [0 1 2 3 4]]

Y方向位移场:
[[-0.08  0.27  0.14  0.06 -0.21]
 [-0.21 -0.27  0.22  0.06  0.12]
 [-0.29  0.28  0.2  -0.17 -0.19]
 [-0.19 -0.12  0.01 -0.04 -0.13]
 [ 0.07 -0.22 -0.12 -0.08 -0.03]]

X方向位移场:
[[ 0.17 -0.18  0.01  0.06 -0.27]
 [ 0.06 -0.2  -0.26  0.27  0.28]
 [ 0.19 -0.12 -0.24  0.11 -0.04]
 [-0.23 -0.   -0.28  0.25 -0.14]
 [ 0.1  -0.11  0.01  0.03 -0.19]]

扭曲后的Y坐标网格:
[[-0.08  0.27  0.14  0.06 -0.21]
 [ 0.79  0.73  1.22  1.06  1.12]
 [ 1.71  2.28  2.2   1.83  1.81]
 [ 2.81  2.88  3.01  2.96  2.87]
 [ 4.07  3.78  3.88  3.92  3.97]]

扭曲后的X坐标网格:
[[ 0.17  0.82  2.01  3.06  3.73]
 [ 0.06  0.8   1.74  3.27  4.28]
 [ 0.19  0.88  1.76  3.11  3.96]
 [-0.23  1.    1.72  3.25  3.86]
 [ 0.1   0.89  2.01  3.03  3.81]]

可以看到,每个点的坐标(y, x)都根据位移场发生了偏移。在实际图像处理中,我们会根据这些新的、非整数的坐标,从原始图像中插值出像素值,从而生成一张看起来自然扭曲的新图像。通过这种方式,可以从有限的原始数据中生成近乎无限多的、形态各异的训练样本。

训练效果与局限

在PROMISE12数据集上,使用上述改进版U-Net、结合强大的数据增强(将数据量扩展至约15万张图像)进行训练后,取得了以下效果:

  • 训练集正确率:95.93%
  • 验证集正确率:87.08%

验证集上的正确率表明模型具备一定的泛化能力。通过可视化分割结果可以发现:

  • 效果好的样例:模型预测的红色轮廓与医生标注的蓝色轮廓高度重合。
  • 效果差的样例:两者差异较大。分析原因,这些往往出现在前列腺组织的顶层或底层切片。因为这些位置的器官截面与中部常见的截面形态差异较大,且在训练数据中出现的频次较低,导致模型未能充分学习到这些“特殊”形态的特征。

这揭示了医学影像分割的一个常见挑战:器官形态的连续性和变异性。解决思路包括收集更多包含边界案例的数据,或设计能更好建模3D上下文信息的网络(如后续会讲到的3D U-Net)。

📝 动手练一练

  1. 概念辨析:U-Net中的“跳跃连接”与ResNet中的“短路连接”在目的和实现方式上有何异同?
  2. 代码实践:假设我们有一个4x4的二进制分割掩码mask_true和模型预测的mask_pred(经过Sigmoid后,值在0~1之间)。请计算它们之间的Dice系数。设定阈值为0.5将预测值二值化。
👉 点击查看参考答案
  1. 异同点分析:

    • 相同点:两者都旨在解决深度网络训练中的信息流通问题,通过建立捷径来缓解梯度消失/爆炸,帮助网络学习恒等映射。
    • 不同点:
      • 目的:ResNet的短路连接主要为了让网络更容易优化,解决深度网络的退化问题。U-Net的跳跃连接主要为了融合多尺度特征,将编码器路径中包含丰富空间细节的浅层特征,与解码器路径中包含高级语义的深层特征结合起来,以精确恢复目标边界和位置。
      • 实现方式:ResNet通常采用逐元素相加。U-Net采用通道维度拼接。相加会融合信息但可能丢失一部分,而拼接保留了来自编码器的全部特征信息,使解码器能够自主选择有用的部分。
  2. Dice系数计算:

import numpy as np

# 模拟真实标注和模型预测(sigmoid输出)
mask_true = np.array([[1, 1, 0, 0],
                      [1, 1, 0, 0],
                      [0, 0, 1, 1],
                      [0, 0, 1, 1]], dtype=np.float32)

mask_pred_sigmoid = np.array([[0.9, 0.8, 0.3, 0.1],
                              [0.7, 0.6, 0.2, 0.1],
                              [0.2, 0.1, 0.8, 0.9],
                              [0.1, 0.05, 0.7, 0.85]], dtype=np.float32)

# 以0.5为阈值进行二值化
threshold = 0.5
mask_pred = (mask_pred_sigmoid > threshold).astype(np.float32)

print("二值化后的预测掩码:")
print(mask_pred)

# 计算交集,以及|A|与|B|之和
intersection = np.sum(mask_true * mask_pred)  # 对应位置相乘,只有都为1时才为1
sum_true_pred = np.sum(mask_true) + np.sum(mask_pred)

# 计算Dice系数
dice = (2.0 * intersection) / (sum_true_pred + 1e-7)  # 加一个极小值防止除零
print(f"\nDice系数: {dice:.4f}")
二值化后的预测掩码:
[[1. 1. 0. 0.]
 [1. 1. 0. 0.]
 [0. 0. 1. 1.]
 [0. 0. 1. 1.]]

Dice系数: 1.0000

在这个完美预测的例子中,Dice系数为1。你可以尝试修改mask_pred_sigmoid的值,观察Dice系数如何变化。

本章小结

本节我们深入剖析了医疗影像分割的经典网络U-Net及其一个成功的改进版本。我们看到了U-Net如何通过其独特的“U”形对称结构和跳跃连接,解决了FCN在细节恢复上的不足。更重要的是,我们学习了一个完整的实战方案,它告诉我们,在数据稀缺的医疗领域取得成功,不仅需要优秀的网络架构,还需要:

  • 针对性的预处理(如曲率去噪)。
  • 精心设计的网络模块(如融入残差思想的卷积块)。
  • 强大的、符合领域特点的数据增强(如弹性变形)。
  • 合适的损失函数(Dice系数损失)。

这些策略的组合,使得深度学习模型能够在有限的标注数据上,学习到鲁棒的特征表示,最终实现精准的分割。

今天,U-Net 仍是医学影像分割的默认基线,并演进为 3D U-Net、U-Net++、nnU-Net、Swin-UNet 等更强变体;Dice 损失与弹性变形也仍是医学分割中的常用手段。

📋 行动清单

  • 在白纸上画出经典U-Net的结构图,标出编码器、解码器、跳跃连接和各层特征图尺寸的变化。
  • 理解Dice系数与IoU的公式关系,并尝试手动推导它们之间的转换公式。
  • 思考:如果让你设计一个用于皮肤镜图像(2D)色素痣分割的网络,你会借鉴本节U-Net方案的哪些部分?可能需要做哪些调整?

—— 小象教研组