使用 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 层后,模型的性能达到了标准。
我是图像分割的初学者。我试图用预训练的 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 层后,模型的性能达到了标准。