使用 Keras 预训练的 Resnet34 编码器构建 Unet

Constructing Unet with pretrained Resnet34 encoder with Keras

我是图像分割的初学者。我试图用预训练的 Resnet34 (imagenet) 作为编码器创建一个 Unet 模型。至于比较,我使用了分割模型 API 来获得相同的模型。然而,我的模型不如导入的模型好,尽管它们的结构和 backbone 相同。

我的模型:

我使用以下代码导入预训练 Resnet34:

ResNet34, preprocess_input = Classifiers.get('resnet34')
Resmodel = ResNet34((256, 256, 3), weights='imagenet') 

然后做了一个卷积块:

def ConvBlock(X,channel,kernel_size,bn=True):
  x=layers.Conv2D(filters=channel,kernel_size=(kernel_size,kernel_size),strides=(1,1),dilation_rate=(1,1),padding='SAME',kernel_initializer='he_normal')(X)
  if bn:
    x=layers.BatchNormalization()(x)
  x=layers.Activation('relu')(x)

  x=layers.Conv2D(filters=channel,kernel_size=(kernel_size,kernel_size),strides=(1,1),dilation_rate=(1,1),padding='SAME',kernel_initializer='he_normal')(x)
  if bn:
    x=layers.BatchNormalization()(x)
  x=layers.Activation('relu')(x)
  return x

最后构建了这个模型:

def new_model(output_channel,output_activation):
  inp=Resmodel.input

  skip1=Resmodel.layers[5].output #128x128x64
  skip2=Resmodel.layers[37].output #64x64x64
  skip3=Resmodel.layers[74].output #32x32x128
  skip4=Resmodel.layers[129].output #16x16x256
  encoder_final=Resmodel.layers[157].output #8x8x512

  #upsample 
  filters=256
  k=1

  x=layers.UpSampling2D()(encoder_final) #returns 16x16x256
  x=layers.Concatenate()([x,skip4]) #returns 16x16x512
  x=ConvBlock(x,filters,kernel_size=3) #returns 16x16x256
  filters //=2

  x=layers.UpSampling2D()(x) #returns 32x32x128
  x=layers.Concatenate()([x,skip3]) #returns 32x32x256
  x=ConvBlock(x,filters,kernel_size=3) #returns 32x32x128
  filters //=2

  x=layers.UpSampling2D()(x) #returns 64x64x64
  x=layers.Concatenate()([x,skip2]) #returns 64x64x128
  x=ConvBlock(x,filters,kernel_size=3) #returns 64x64x64
  filters //=2 

  x=layers.UpSampling2D()(x) #returns 128x128x64
  x=layers.Concatenate()([x,skip1]) #returns 128x128x128
  x=ConvBlock(x,filters,kernel_size=3) #returns 128x128x32
  filters //=2

  x=layers.UpSampling2D()(x) #returns 256x256x32
  x=ConvBlock(x,filters,kernel_size=3) #returns 256x256x16
  x = layers.Conv2D(output_channel, kernel_size= (1,1), strides=(1,1), padding= 'same')(x)  #returns 256x256x1
  x=layers.Activation('sigmoid')(x)
  model=Model(inputs=inp,outputs=x)
  return model

作为衡量我是否做得正确的一种方式,我使用分割模型 Pypi 库导入了一个带有 Resnet34 的 Unet backbone。

进口型号:

from segmentation_models import Unet
from segmentation_models.utils import set_trainable

model = Unet(backbone_name='resnet34', encoder_weights='imagenet', encoder_freeze=True)
model.summary()

但问题是,从 segmentation_models API 导入的模型似乎比我创建的模型工作得更好(更好的 Iou 分数)。即使结构和 backbone 几乎相同。那么我的模型做错了什么?感谢阅读这么长post.

您检查过 UNet 在那个特定库中的实现了吗?

据我所知,UpSampling() 层已替换为 Conv2DTranspose(),因此可能是造成差异的原因。

除此之外,确保您拥有与 segmentation_models

中完全相同的可训练层

请检查您的模型权重是否加载成功。您可以通过 运行 模型编码器部分的相同测试输入来检查这一点。似乎权重没有成功加载,因此性能不佳。

另请注意,引用 Timbus Calin 的上述答案,因为默认情况下 decoder_block_type='upsampling'。所以这不是问题。 尽管如此,请验证模型摘要以确保没有差异,尤其是过滤器数量和可训练层。

问题出在我用来冻结 ResNet34 层的代码中。我也错误地冻结了 BatchNorm 层,这导致了性能差距。在有选择地冻结除 BatchNorm 层之外的所有 Resnet34 层后,模型的性能达到了标准。