当前位置:   article > 正文

UNet++学习/实现笔记

unet++

原理:

UNet++解读 + 它是如何对UNet改进 + 作者的研究态度和方式 - 知乎 (zhihu.com)

实现方法有两种:

①跳跃连接改为直接连接

②深度监督方式对于每个子网络输出端加一个1x1的卷积核,相当于监督了每个子网络

Keras实现

语义分割(三)Unet++_unet++网络结构-CSDN博客

拿L2子网络作为例子解释一下结构:

  1. ## l2
  2. pool10_20 = MaxPool2D(pool_size=(2, 2))(conv1_0)
  3. conv2_0 = conv_drop(inputs=pool10_20, filters=filters[2])
  4. up20_11 = upsampling(inputs=conv2_0, filters=filters[1])
  5. concat2_1 = concatenate([up20_11, conv1_0], axis=3)
  6. conv1_1 = conv_drop(inputs=concat2_1, filters=filters[1])
  7. up11_02 = upsampling(inputs=conv1_1, filters=filters[0])
  8. concat2_2 = concatenate([up11_02, conv0_0, conv0_1], axis=3)
  9. conv0_2 = conv_drop(inputs=concat2_2, filters=filters[0])

 pool10_20 为从 x1,0 到 x2,0 的池化结果, 再对池化结果进行卷积得到 x2, 0的卷积结果 conv2_0

 

将 x2,0 的卷积结果 上采样得到 x2,0 到 x1,1 的上采样结果 up20_11,并连接 conv1_0 ,再对连接后的结果进行一次卷积得到conv1_1的结果

同理可得conv0_2的结果

总结起来是:

每一步下采样为先池化后卷积。

每一步上采样为 左下角结构的反卷积 连接 正左所有结构的卷积,将连接结果再做一次卷积。

总代码: 

  1. ###########################
  2. # Unet++ loss #
  3. ###########################
  4. from keras import *
  5. from keras.src.layers import Conv2D, UpSampling2D, concatenate, Reshape, Activation
  6. from keras.src.losses import binary_crossentropy
  7. from keras.src.optimizers import Adam
  8. from tensorflow.python.keras.layers import MaxPool2D
  9. ############################
  10. # Dice系数,用于评估图像分割任务中预测结果和真实标签之间的相似度指标
  11. ############################
  12. def dice_coef(y_true, y_pred):
  13. smooth = 1. # 平滑项,避免分母为零
  14. y_true_f = K.flatten(y_true)
  15. y_pred_f = K.flatten(y_pred)
  16. intersection = K.sum(y_true_f * y_pred_f) # 表示预测结果和真实标签的交集(即正确预测的正样本)
  17. return (2. * intersection + smooth) / (K.sum(y_true_f) + K.sum(y_pred_f) + smooth)
  18. # 二进制交叉熵损失和Dice系数之间的线性组合
  19. def bce_dice_loss(y_true, y_pred):
  20. return 0.5 * binary_crossentropy(y_true, y_pred) - dice_coef(y_true, y_pred)
  21. # return 0.5 * categorical_crossentropy(y_true, y_pred) - dice_coef(y_true, y_pred)
  22. #############################
  23. # Unet++ conv and upsampling #
  24. #############################
  25. # 下采样:两个3x3卷积
  26. def conv_drop(inputs, filters):
  27. conv1 = Conv2D(filters=filters, kernel_size=3, padding='same', activation='relu',
  28. kernel_initializer='he_normal')(inputs)
  29. # drop1 = Dropout(rate=0.5)(conv1)
  30. conv2 = Conv2D(filters=filters, kernel_size=3, padding='same', activation='relu',
  31. kernel_initializer='he_normal')(conv1)
  32. # drop2 = Dropout(rate=0.5)(conv2)
  33. return conv2
  34. # 上采样:进行2倍上采样,然后进行一个2x2的卷积
  35. def upsampling(inputs, filters):
  36. up = UpSampling2D(size=(2, 2))(inputs)
  37. conv = Conv2D(filters=filters, kernel_size=2, activation='relu', padding='same',
  38. kernel_initializer='he_normal')(up)
  39. return conv
  40. ###############################
  41. # Unet++ #
  42. ###############################
  43. def Unet_plusplus(input_size=(224, 224, 1), n_class=2, filters=(32, 64, 128, 256, 512), re_shape=False):
  44. inputs = Input(shape=input_size)
  45. ## l1
  46. conv0_0 = conv_drop(inputs=inputs, filters=filters[0])
  47. pool00_10 = MaxPool2D(pool_size=(2, 2))(conv0_0)
  48. conv1_0 = conv_drop(inputs=pool00_10, filters=filters[1])
  49. up10_01 = upsampling(inputs=conv1_0, filters=filters[0])
  50. concat1_1 = concatenate([up10_01, conv0_0], axis=3)
  51. conv0_1 = conv_drop(inputs=concat1_1, filters=filters[0])
  52. ## l2
  53. pool10_20 = MaxPool2D(pool_size=(2, 2))(conv1_0)
  54. conv2_0 = conv_drop(inputs=pool10_20, filters=filters[2])
  55. up20_11 = upsampling(inputs=conv2_0, filters=filters[1])
  56. concat2_1 = concatenate([up20_11, conv1_0], axis=3)
  57. conv1_1 = conv_drop(inputs=concat2_1, filters=filters[1])
  58. up11_02 = upsampling(inputs=conv1_1, filters=filters[0])
  59. concat2_2 = concatenate([up11_02, conv0_0, conv0_1], axis=3)
  60. conv0_2 = conv_drop(inputs=concat2_2, filters=filters[0])
  61. ##l3
  62. pool20_30 = MaxPool2D(pool_size=(2, 2))(conv2_0)
  63. conv3_0 = conv_drop(inputs=pool20_30, filters=filters[3])
  64. up30_21 = upsampling(inputs=conv3_0, filters=filters[2])
  65. concat3_1 = concatenate([up30_21, conv2_0], axis=3)
  66. conv2_1 = conv_drop(inputs=concat3_1, filters=filters[2])
  67. up21_12 = upsampling(inputs=conv2_1, filters=filters[1])
  68. concat3_2 = concatenate([up21_12, conv1_0, conv1_1], axis=3)
  69. conv1_2 = conv_drop(inputs=concat3_2, filters=filters[1])
  70. up12_03 = upsampling(inputs=conv1_2, filters=filters[0])
  71. concat3_3 = concatenate([up12_03, conv0_0, conv0_1, conv0_2], axis=3)
  72. conv0_3 = conv_drop(inputs=concat3_3, filters=filters[0])
  73. ## l4
  74. pool30_40 = MaxPool2D(pool_size=(2, 2))(conv3_0)
  75. conv4_0 = conv_drop(inputs=pool30_40, filters=filters[4])
  76. up40_31 = upsampling(inputs=conv4_0, filters=filters[3])
  77. concat4_1 = concatenate([up40_31, conv3_0], axis=3)
  78. conv3_1 = conv_drop(inputs=concat4_1, filters=filters[3])
  79. up31_22 = upsampling(inputs=conv3_1, filters=filters[2])
  80. concat4_2 = concatenate([up31_22, conv2_0, conv2_1], axis=3)
  81. conv2_2 = conv_drop(inputs=concat4_2, filters=filters[2])
  82. up22_13 = upsampling(inputs=conv2_2, filters=filters[1])
  83. concat4_3 = concatenate([up22_13, conv1_0, conv1_1, conv1_2], axis=3)
  84. conv1_3 = conv_drop(inputs=concat4_3, filters=filters[1])
  85. up13_04 = upsampling(inputs=conv1_3, filters=filters[0])
  86. concat4_4 = concatenate([up13_04, conv0_0, conv0_1, conv0_2, conv0_3], axis=3)
  87. conv0_4 = conv_drop(inputs=concat4_4, filters=filters[0])
  88. ## output
  89. l1_conv_out = Conv2D(filters=n_class, kernel_size=1, padding='same', kernel_initializer='he_normal')(conv0_1)
  90. l2_conv_out = Conv2D(filters=n_class, kernel_size=1, padding='same', kernel_initializer='he_normal')(conv0_2)
  91. l3_conv_out = Conv2D(filters=n_class, kernel_size=1, padding='same', kernel_initializer='he_normal')(conv0_3)
  92. l4_conv_out = Conv2D(filters=n_class, kernel_size=1, padding='same', kernel_initializer='he_normal')(conv0_4)
  93. if re_shape == True:
  94. l1_conv_out = Reshape((input_size[0] * input_size[1], n_class))(l1_conv_out)
  95. l2_conv_out = Reshape((input_size[0] * input_size[1], n_class))(l2_conv_out)
  96. l3_conv_out = Reshape((input_size[0] * input_size[1], n_class))(l3_conv_out)
  97. l4_conv_out = Reshape((input_size[0] * input_size[1], n_class))(l4_conv_out)
  98. l1_out = Activation('sigmoid', name='l1_out')(l1_conv_out)
  99. l2_out = Activation('sigmoid', name='l2_out')(l2_conv_out)
  100. l3_out = Activation('sigmoid', name='l3_out')(l3_conv_out)
  101. l4_out = Activation('sigmoid', name='l4_out')(l4_conv_out)
  102. model = Model(input=inputs, output=[l1_out, l2_out, l3_out, l4_out])
  103. # model = Model(input=inputs, output=l4_out)
  104. model.summary()
  105. losses = {
  106. 'l1_out': bce_dice_loss,
  107. 'l2_out': bce_dice_loss,
  108. 'l3_out': bce_dice_loss,
  109. 'l4_out': bce_dice_loss,
  110. }
  111. model.compile(optimizer=Adam(lr=1e-4), loss=losses, metrics=['accuracy'])
  112. return model

 pytorch实现

语义分割系列6-Unet++(pytorch实现)-CSDN博客

  1. import torch
  2. import torch.nn as nn
  3. class ContinusParalleConv(nn.Module):
  4. # 一个连续的卷积模块,包含BatchNorm 在前 和 在后 两种模式
  5. def __init__(self, in_channels, out_channels, pre_Batch_Norm=True):
  6. super(ContinusParalleConv, self).__init__()
  7. self.in_channels = in_channels
  8. self.out_channels = out_channels
  9. if pre_Batch_Norm:
  10. self.Conv_forward = nn.Sequential(
  11. nn.BatchNorm2d(self.in_channels),
  12. nn.ReLU(),
  13. nn.Conv2d(self.in_channels, self.out_channels, 3, padding=1),
  14. nn.BatchNorm2d(out_channels),
  15. nn.ReLU(),
  16. nn.Conv2d(self.out_channels, self.out_channels, 3, padding=1))
  17. else:
  18. self.Conv_forward = nn.Sequential(
  19. nn.Conv2d(self.in_channels, self.out_channels, 3, padding=1),
  20. nn.BatchNorm2d(out_channels),
  21. nn.ReLU(),
  22. nn.Conv2d(self.out_channels, self.out_channels, 3, padding=1),
  23. nn.BatchNorm2d(self.out_channels),
  24. nn.ReLU())
  25. def forward(self, x):
  26. x = self.Conv_forward(x)
  27. return x
  28. class UnetPlusPlus(nn.Module):
  29. def __init__(self, num_classes, deep_supervision=False):
  30. super(UnetPlusPlus, self).__init__()
  31. self.num_classes = num_classes
  32. self.deep_supervision = deep_supervision
  33. self.filters = [64, 128, 256, 512, 1024]
  34. self.CONV3_1 = ContinusParalleConv(512 * 2, 512, pre_Batch_Norm=True)
  35. self.CONV2_2 = ContinusParalleConv(256 * 3, 256, pre_Batch_Norm=True)
  36. self.CONV2_1 = ContinusParalleConv(256 * 2, 256, pre_Batch_Norm=True)
  37. self.CONV1_1 = ContinusParalleConv(128 * 2, 128, pre_Batch_Norm=True)
  38. self.CONV1_2 = ContinusParalleConv(128 * 3, 128, pre_Batch_Norm=True)
  39. self.CONV1_3 = ContinusParalleConv(128 * 4, 128, pre_Batch_Norm=True)
  40. self.CONV0_1 = ContinusParalleConv(64 * 2, 64, pre_Batch_Norm=True)
  41. self.CONV0_2 = ContinusParalleConv(64 * 3, 64, pre_Batch_Norm=True)
  42. self.CONV0_3 = ContinusParalleConv(64 * 4, 64, pre_Batch_Norm=True)
  43. self.CONV0_4 = ContinusParalleConv(64 * 5, 64, pre_Batch_Norm=True)
  44. self.stage_0 = ContinusParalleConv(3, 64, pre_Batch_Norm=False)
  45. self.stage_1 = ContinusParalleConv(64, 128, pre_Batch_Norm=False)
  46. self.stage_2 = ContinusParalleConv(128, 256, pre_Batch_Norm=False)
  47. self.stage_3 = ContinusParalleConv(256, 512, pre_Batch_Norm=False)
  48. self.stage_4 = ContinusParalleConv(512, 1024, pre_Batch_Norm=False)
  49. self.pool = nn.MaxPool2d(2)
  50. self.upsample_3_1 = nn.ConvTranspose2d(in_channels=1024, out_channels=512, kernel_size=4, stride=2, padding=1)
  51. self.upsample_2_1 = nn.ConvTranspose2d(in_channels=512, out_channels=256, kernel_size=4, stride=2, padding=1)
  52. self.upsample_2_2 = nn.ConvTranspose2d(in_channels=512, out_channels=256, kernel_size=4, stride=2, padding=1)
  53. self.upsample_1_1 = nn.ConvTranspose2d(in_channels=256, out_channels=128, kernel_size=4, stride=2, padding=1)
  54. self.upsample_1_2 = nn.ConvTranspose2d(in_channels=256, out_channels=128, kernel_size=4, stride=2, padding=1)
  55. self.upsample_1_3 = nn.ConvTranspose2d(in_channels=256, out_channels=128, kernel_size=4, stride=2, padding=1)
  56. self.upsample_0_1 = nn.ConvTranspose2d(in_channels=128, out_channels=64, kernel_size=4, stride=2, padding=1)
  57. self.upsample_0_2 = nn.ConvTranspose2d(in_channels=128, out_channels=64, kernel_size=4, stride=2, padding=1)
  58. self.upsample_0_3 = nn.ConvTranspose2d(in_channels=128, out_channels=64, kernel_size=4, stride=2, padding=1)
  59. self.upsample_0_4 = nn.ConvTranspose2d(in_channels=128, out_channels=64, kernel_size=4, stride=2, padding=1)
  60. # 分割头
  61. self.final_super_0_1 = nn.Sequential(
  62. nn.BatchNorm2d(64),
  63. nn.ReLU(),
  64. nn.Conv2d(64, self.num_classes, 3, padding=1),
  65. )
  66. self.final_super_0_2 = nn.Sequential(
  67. nn.BatchNorm2d(64),
  68. nn.ReLU(),
  69. nn.Conv2d(64, self.num_classes, 3, padding=1),
  70. )
  71. self.final_super_0_3 = nn.Sequential(
  72. nn.BatchNorm2d(64),
  73. nn.ReLU(),
  74. nn.Conv2d(64, self.num_classes, 3, padding=1),
  75. )
  76. self.final_super_0_4 = nn.Sequential(
  77. nn.BatchNorm2d(64),
  78. nn.ReLU(),
  79. nn.Conv2d(64, self.num_classes, 3, padding=1),
  80. )
  81. def forward(self, x):
  82. x_0_0 = self.stage_0(x)
  83. x_1_0 = self.stage_1(self.pool(x_0_0))
  84. x_2_0 = self.stage_2(self.pool(x_1_0))
  85. x_3_0 = self.stage_3(self.pool(x_2_0))
  86. x_4_0 = self.stage_4(self.pool(x_3_0))
  87. x_0_1 = torch.cat([self.upsample_0_1(x_1_0), x_0_0], 1)
  88. x_0_1 = self.CONV0_1(x_0_1)
  89. x_1_1 = torch.cat([self.upsample_1_1(x_2_0), x_1_0], 1)
  90. x_1_1 = self.CONV1_1(x_1_1)
  91. x_2_1 = torch.cat([self.upsample_2_1(x_3_0), x_2_0], 1)
  92. x_2_1 = self.CONV2_1(x_2_1)
  93. x_3_1 = torch.cat([self.upsample_3_1(x_4_0), x_3_0], 1)
  94. x_3_1 = self.CONV3_1(x_3_1)
  95. x_2_2 = torch.cat([self.upsample_2_2(x_3_1), x_2_0, x_2_1], 1)
  96. x_2_2 = self.CONV2_2(x_2_2)
  97. x_1_2 = torch.cat([self.upsample_1_2(x_2_1), x_1_0, x_1_1], 1)
  98. x_1_2 = self.CONV1_2(x_1_2)
  99. x_1_3 = torch.cat([self.upsample_1_3(x_2_2), x_1_0, x_1_1, x_1_2], 1)
  100. x_1_3 = self.CONV1_3(x_1_3)
  101. x_0_2 = torch.cat([self.upsample_0_2(x_1_1), x_0_0, x_0_1], 1)
  102. x_0_2 = self.CONV0_2(x_0_2)
  103. x_0_3 = torch.cat([self.upsample_0_3(x_1_2), x_0_0, x_0_1, x_0_2], 1)
  104. x_0_3 = self.CONV0_3(x_0_3)
  105. x_0_4 = torch.cat([self.upsample_0_4(x_1_3), x_0_0, x_0_1, x_0_2, x_0_3], 1)
  106. x_0_4 = self.CONV0_4(x_0_4)
  107. if self.deep_supervision:
  108. out_put1 = self.final_super_0_1(x_0_1)
  109. out_put2 = self.final_super_0_2(x_0_2)
  110. out_put3 = self.final_super_0_3(x_0_3)
  111. out_put4 = self.final_super_0_4(x_0_4)
  112. return [out_put1, out_put2, out_put3, out_put4]
  113. else:
  114. return self.final_super_0_4(x_0_4)
  115. if __name__ == "__main__":
  116. print("deep_supervision: False")
  117. deep_supervision = False
  118. device = torch.device('cpu')
  119. inputs = torch.randn((1, 3, 224, 224)).to(device)
  120. model = UnetPlusPlus(num_classes=3, deep_supervision=deep_supervision).to(device)
  121. outputs = model(inputs)
  122. print(outputs.shape)
  123. print("deep_supervision: True")
  124. deep_supervision = True
  125. model = UnetPlusPlus(num_classes=3, deep_supervision=deep_supervision).to(device)
  126. outputs = model(inputs)
  127. for out in outputs:
  128. print(out.shape)
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/你好赵伟/article/detail/760625
推荐阅读
  

闽ICP备14008679号