使用 VGG 16 作为特征提取器的 U-net 类架构 - 连接层的问题
Posted
技术标签:
【中文标题】使用 VGG 16 作为特征提取器的 U-net 类架构 - 连接层的问题【英文标题】:U-net like architecture with VGG 16 as feature extractor - problems with concatenate layers 【发布时间】:2019-02-06 18:05:47 【问题描述】:我想构建一个 U-net 架构,使用 VGG 16 模型作为我的编码器,并在 Imagenet 数据集上预先训练权重。对于上采样,我想将编码器中某些层的输出与解码部分连接起来。几乎像这样(取自 TernausNet): TernausNet
到目前为止,我的模型如下所示:
from keras.applications.vgg16 import VGG16 as VGG16, preprocess_input
encode_model = VGG16(input_shape=(768,768,3), include_top=False, weights='imagenet')
encode_model.trainable = False
from keras import models, layers
input_img = layers.Input((768,768,3), name = 'RGB_Input')
# output and start upsampling
features = encode_model(input_img)
conv_1 = layers.Conv2D(512, (3,3), activation='relu', padding='same')(features)
up_conv = layers.Conv2DTranspose(256, (3,3), strides=(2,2), activation='relu', padding='same')(conv_1)
# first concatenation block
concat_1 = layers.concatenate([encode_model.get_layer('block5_conv3').output, up_conv], axis=-1, name='concat_1')
conv_2 = layers.Conv2D(512, (3,3), activation='relu', padding='same')(concat_1)
up_conv_2 = layers.Conv2DTranspose(256, (3,3), strides=(2,2), activation='relu', padding='same')(conv_2)
# second concatenation block
concat_2 = layers.concatenate([up_conv_2, encode_model.get_layer('block4_conv3').output])
conv_3 = layers.Conv2D(512, (3,3), activation='relu', padding='same')(concat_2)
up_conv_3 = layers.Conv2DTranspose(128, (3,3), strides=(2,2), activation='relu', padding='same')(conv_3)
# third concatenation block
concat_3 = layers.concatenate([up_conv_3, encode_model.get_layer('block3_conv3').output])
conv_4 = layers.Conv2D(256, (3,3), activation='relu', padding='same')(concat_3)
up_conv_4 = layers.Conv2DTranspose(64, (3,3), strides=(2,2), activation='relu', padding='same')(conv_4)
# fourth concatenation block
concat_4 = layers.concatenate([up_conv_4, encode_model.get_layer('block2_conv2').output])
conv_5 = layers.Conv2D(128, (3,3), activation='relu', padding='same')(concat_4)
up_conv_5 = layers.Conv2DTranspose(32, (3,3), strides=(2,2), activation='relu', padding='same')(conv_5)
# fifth concatenation block
concat_4 = layers.concatenate([up_conv_5, encode_model.get_layer('block1_conv2').output])
conv_6 = layers.Conv2D(128, (3,3), activation='sigmoid', padding='same')(concat_4)
final_model = models.Model(inputs=[input_img], outputs=[conv_6])
final_model.summary()
稍后我编译并拟合模型:
import keras.backend as K
from keras.optimizers import Adam
from keras.losses import binary_crossentropy
def dice_coef(y_true, y_pred, smooth=1):
intersection = K.sum(y_true * y_pred, axis=[1,2,3])
union = K.sum(y_true, axis=[1,2,3]) + K.sum(y_pred, axis=[1,2,3])
return K.mean( (2. * intersection + smooth) / (union + smooth),
axis=0)
def dice_p_bce(in_gt, in_pred):
return 1e-3*binary_crossentropy(in_gt, in_pred) - dice_coef(in_gt,
in_pred)
def true_positive_rate(y_true, y_pred):
return K.sum(K.flatten(y_true)*K.flatten(K.round(y_pred)))/K.sum(y_true)
final_model.compile(optimizer=Adam(1e-3, decay=1e-6), loss=dice_p_bce,
metrics=[dice_coef, 'binary_accuracy', true_positive_rate])
step_count = min(MAX_TRAIN_STEPS, balanced_train_df.shape[0]//BATCH_SIZE)
aug_gen = create_aug_gen(make_image_gen(balanced_train_df))
loss_history = [final_model.fit_generator(aug_gen,
steps_per_epoch=step_count,
epochs=NB_EPOCHS,
validation_data=(valid_x, valid_y),
callbacks=callbacks_list,
workers=1)]`
然后我得到以下错误:
Graph disconnected: cannot obtain value for tensor Tensor("input_4:0", shape=(?, 768, 768, 3), dtype=float32) at layer "input_4". The following previous layers were accessed without issue: []
我了解到,当输入和输出不在同一个图表中时,就会发生这种情况。我认为我正在连接两个图表:
input_img = layers.Input((768,768,3), name = 'RGB_Input')
# output and start upsampling
features = encode_model(input_img)
conv_1 = layers.Conv2D(512, (3,3), activation='relu', padding='same')(features)
我的错误是什么?
【问题讨论】:
如果以下任何答案解决了您的问题,请接受点击答案旁边的复选标记将其标记为“已回答” - 请参阅What should I do when someone answers my question? 【参考方案1】:我更新了我的答案以对应已编辑的问题
我认为问题出在这一行:
features = encode_model(input_img)
encode_model
是一个 keras 模型,您尝试将自定义输入层传递给它。这当然不是必需的。请尝试以下操作:
# necessary imports
encode_model = VGG16(input_shape=(768,768,3), include_top=False, weights='imagenet')
conv_1 = layers.Conv2D(512, (3,3), activation='relu', padding='same')(encode_model.output) # need to pass the output tensor to the conv_1 layer
# define the rest of your model as you have it
【讨论】:
我试过了,然后我得到了错误:'Tensor' object has no attribute 'output' 我相信通过将输入提供给模型,我确实可以从中获得输出VGG16 模型作为张量。问题很可能在串联中。当我在第一次连接之前创建带有输出的模型时,它会编译并显示所有层就好了。 @user3578476 我更改了答案以匹配您的新变量名称。首先转储自定义输入,然后将encode_model.output
张量传递给conv_1
以连接图。【参考方案2】:
由于 VGG16 中已经有一个输入层,在这种情况下您不需要定义一个新的输入层。只需使用现有的:
decode_features = decode_model.output
# ...
final_model = models.Model(inputs=[decode_model.input], outputs=[conv_6])
更新:如果你想防止 VGG16 的权重被修改,明确使用trainable
参数冻结它的所有层:
for layer in decode_model.layers:
layer.trainable = False
请确保在编译最终模型之前执行此操作。
【讨论】:
这是有道理且有效的。但是现在 VGG16 层又可以训练了。我想对这些使用 Imagenet 的预训练权重,只训练解码部分(连接和上采样层)。权重还会这样初始化吗? 我在第三行做了这个。但不知何故,他“爆炸”了整个模型并使层再次可训练。在层的概述中,vgg16 不是出现在一行中,而是出现在模型内的所有层中。我得到以下输出:Total params: 28,504,608 Trainable params: 28,504,608 Non-trainable params: 0
@user3578476 您能否将compile
和fit
呼叫也添加到您的帖子中?
@user3578476 再次查看我的更新答案。在这种情况下,您需要显式冻结图层。以上是关于使用 VGG 16 作为特征提取器的 U-net 类架构 - 连接层的问题的主要内容,如果未能解决你的问题,请参考以下文章