模型输入必须来自`tf.keras.Input` ...,它们不能是先前非输入层的输出

问题描述

我正在使用Python 3.7.7。和Tensorflow 2.1.0。

我有一个经过培训的U-Net网络,我想获得其编码器其解码器

在下图中:

enter image description here

您可以看到卷积编码器-解码器体系结构。我要获取编码器部分,即出现在图像左侧的层:

enter image description here

解码器部分:

enter image description here

我从此函数获取U-Net模型:

def get_unet_uncompiled(img_shape = (200,200,1)):
    inputs = Input(shape=img_shape)

    conv1 = Conv2D(64,(5,5),activation='relu',padding='same',data_format="channels_last",name='conv1_1')(inputs)
    conv1 = Conv2D(64,name='conv1_2')(conv1)
    pool1 = MaxPooling2D(pool_size=(2,2),name='pool1')(conv1)
    conv2 = Conv2D(96,(3,3),name='conv2_1')(pool1)
    conv2 = Conv2D(96,name='conv2_2')(conv2)
    pool2 = MaxPooling2D(pool_size=(2,name='pool2')(conv2)

    conv3 = Conv2D(128,name='conv3_1')(pool2)
    conv3 = Conv2D(128,name='conv3_2')(conv3)
    pool3 = MaxPooling2D(pool_size=(2,name='pool3')(conv3)

    conv4 = Conv2D(256,name='conv4_1')(pool3)
    conv4 = Conv2D(256,(4,4),name='conv4_2')(conv4)
    pool4 = MaxPooling2D(pool_size=(2,name='pool4')(conv4)

    conv5 = Conv2D(512,name='conv5_1')(pool4)
    conv5 = Conv2D(512,name='conv5_2')(conv5)

    up_conv5 = UpSampling2D(size=(2,name='up_conv5')(conv5)
    ch,cw = get_crop_shape(conv4,up_conv5)
    crop_conv4 = Cropping2D(cropping=(ch,cw),name='crop_conv4')(conv4)
    up6 = concatenate([up_conv5,crop_conv4])
    conv6 = Conv2D(256,name='conv6_1')(up6)
    conv6 = Conv2D(256,name='conv6_2')(conv6)

    up_conv6 = UpSampling2D(size=(2,name='up_conv6')(conv6)
    ch,cw = get_crop_shape(conv3,up_conv6)
    crop_conv3 = Cropping2D(cropping=(ch,name='crop_conv3')(conv3)
    up7 = concatenate([up_conv6,crop_conv3])
    conv7 = Conv2D(128,name='conv7_1')(up7)
    conv7 = Conv2D(128,name='conv7_2')(conv7)

    up_conv7 = UpSampling2D(size=(2,name='up_conv7')(conv7)
    ch,cw = get_crop_shape(conv2,up_conv7)
    crop_conv2 = Cropping2D(cropping=(ch,name='crop_conv2')(conv2)
    up8 = concatenate([up_conv7,crop_conv2])
    conv8 = Conv2D(96,name='conv8_1')(up8)
    conv8 = Conv2D(96,name='conv8_2')(conv8)

    up_conv8 = UpSampling2D(size=(2,name='up_conv8')(conv8)
    ch,cw = get_crop_shape(conv1,up_conv8)
    crop_conv1 = Cropping2D(cropping=(ch,name='crop_conv1')(conv1)
    up9 = concatenate([up_conv8,crop_conv1])
    conv9 = Conv2D(64,name='conv9_1')(up9)
    conv9 = Conv2D(64,name='conv9_2')(conv9)

    ch,cw = get_crop_shape(inputs,conv9)
    conv9 = ZeroPadding2D(padding=(ch,name='conv9_3')(conv9)
    conv10 = Conv2D(1,(1,1),activation='sigmoid',name='conv10_1')(conv9)
    model = Model(inputs=inputs,outputs=conv10)

    return model

辅助功能是:

def get_crop_shape(target,refer):
    # width,the 3rd dimension
    cw = (target.get_shape()[2] - refer.get_shape()[2])
    assert (cw >= 0)
    if cw % 2 != 0:
        cw1,cw2 = cw // 2,cw // 2 + 1
    else:
        cw1,cw // 2
    # height,the 2nd dimension
    ch = (target.get_shape()[1] - refer.get_shape()[1])
    assert (ch >= 0)
    if ch % 2 != 0:
        ch1,ch2 = ch // 2,ch // 2 + 1
    else:
        ch1,ch // 2

    return (ch1,ch2),(cw1,cw2)

此模型的图形为:

enter image description here

我训练网络,训练后我得到编码器执行此操作:

first_encoder_layer = 0
last_encoder_layer = 14
old_model = get_unet_uncompiled()

old_model.compile(tf.keras.optimizers.Adam(lr=(1e-4) * 2),loss='binary_crossentropy',metrics=['accuracy'])

encoder: Model = Model(inputs=old_model.layers[first_encoder_layer].input,outputs=old_model.layers[last_encoder_layer].output,name='encoder')

它运行完美。

但是,当我尝试使用以下方法提取解码器时:

decoder: Model = Model(inputs=old_model.layers[last_encoder_layer + 1].input,outputs=old_model.layers[-1].output,name='decoder')

我得到了我不理解的错误

WARNING:tensorflow:Model inputs must come from `tf.keras.Input` (thus holding past layer Metadata),they cannot be the output of a prevIoUs non-Input layer. Here,a tensor specified as input to "decoder" was not an Input tensor,it was generated by layer up_conv5.
Note that input tensors are instantiated via `tensor = tf.keras.Input(shape)`.
The tensor that caused the issue was: up_conv5/Identity:0
WARNING:tensorflow:Model inputs must come from `tf.keras.Input` (thus holding past layer Metadata),it was generated by layer crop_conv4.
Note that input tensors are instantiated via `tensor = tf.keras.Input(shape)`.
The tensor that caused the issue was: crop_conv4/Identity:0

一个TraceBack,然后另一个错误

ValueError: Graph disconnected: cannot obtain value for tensor Tensor("input_1:0",shape=(None,dtype=float32) at layer "input_1". The following prevIoUs layers were accessed without issue: []

我已经尝试过以下代码获取解码器:

decoder_input = Input(shape=(12,12,512),name='dec_input')
z = UpSampling2D(size=(2,name='up_dec_conv5')(decoder_input)
decoder: Model = Model(inputs=z,name='decoder')

但是我几乎得到了相同的错误

WARNING:tensorflow:Model inputs must come from `tf.keras.Input` (thus holding past layer Metadata),it was generated by layer up_dec_conv5.
Note that input tensors are instantiated via `tensor = tf.keras.Input(shape)`.
The tensor that caused the issue was: up_dec_conv5/Identity:0

如何从预训练的U-Net网络获得解码器?

解决方法

我建议为编码器和解码器(get_encoderget_decoder)定义一个结构。在训练完整个模型之后,我们的想法是创建一个新的解码器架构(通过get_decoder),我们可以用解码器训练后的权重填充

pythonic语言,您可以通过这种方式实现...

def get_crop_shape(target,refer):
    
    # width,the 3rd dimension
    cw = (target.get_shape()[2] - refer.get_shape()[2])
    assert (cw >= 0)
    if cw % 2 != 0:
        cw1,cw2 = cw // 2,cw // 2 + 1
    else:
        cw1,cw // 2
    # height,the 2nd dimension
    ch = (target.get_shape()[1] - refer.get_shape()[1])
    assert (ch >= 0)
    if ch % 2 != 0:
        ch1,ch2 = ch // 2,ch // 2 + 1
    else:
        ch1,ch // 2

    return (ch1,ch2),(cw1,cw2)

def get_encoder(img_shape):
    
    inp = Input(shape=img_shape)
    conv1 = Conv2D(64,(5,5),activation='relu',padding='same',data_format="channels_last",name='conv1_1')(inp)
    conv1 = Conv2D(64,name='conv1_2')(conv1)
    pool1 = MaxPooling2D(pool_size=(2,2),name='pool1')(conv1)
    conv2 = Conv2D(96,(3,3),name='conv2_1')(pool1)
    conv2 = Conv2D(96,name='conv2_2')(conv2)
    pool2 = MaxPooling2D(pool_size=(2,name='pool2')(conv2)

    conv3 = Conv2D(128,name='conv3_1')(pool2)
    conv3 = Conv2D(128,name='conv3_2')(conv3)
    pool3 = MaxPooling2D(pool_size=(2,name='pool3')(conv3)

    conv4 = Conv2D(256,name='conv4_1')(pool3)
    conv4 = Conv2D(256,(4,4),name='conv4_2')(conv4)
    pool4 = MaxPooling2D(pool_size=(2,name='pool4')(conv4)

    conv5 = Conv2D(512,name='conv5_1')(pool4)
    conv5 = Conv2D(512,name='conv5_2')(conv5)
    
    return conv5,conv4,conv3,conv2,conv1,inp

def get_decoder(convs):
    
    conv5,inputs = convs
    
    up_conv5 = UpSampling2D(size=(2,name='up_conv5')(conv5)
    ch,cw = get_crop_shape(conv4,up_conv5)
    crop_conv4 = Cropping2D(cropping=(ch,cw),name='crop_conv4')(conv4)
    up6 = concatenate([up_conv5,crop_conv4])
    conv6 = Conv2D(256,name='conv6_1')(up6)
    conv6 = Conv2D(256,name='conv6_2')(conv6)

    up_conv6 = UpSampling2D(size=(2,name='up_conv6')(conv6)
    ch,cw = get_crop_shape(conv3,up_conv6)
    crop_conv3 = Cropping2D(cropping=(ch,name='crop_conv3')(conv3)
    up7 = concatenate([up_conv6,crop_conv3])
    conv7 = Conv2D(128,name='conv7_1')(up7)
    conv7 = Conv2D(128,name='conv7_2')(conv7)

    up_conv7 = UpSampling2D(size=(2,name='up_conv7')(conv7)
    ch,cw = get_crop_shape(conv2,up_conv7)
    crop_conv2 = Cropping2D(cropping=(ch,name='crop_conv2')(conv2)
    up8 = concatenate([up_conv7,crop_conv2])
    conv8 = Conv2D(96,name='conv8_1')(up8)
    conv8 = Conv2D(96,name='conv8_2')(conv8)

    up_conv8 = UpSampling2D(size=(2,name='up_conv8')(conv8)
    ch,cw = get_crop_shape(conv1,up_conv8)
    crop_conv1 = Cropping2D(cropping=(ch,name='crop_conv1')(conv1)
    up9 = concatenate([up_conv8,crop_conv1])
    conv9 = Conv2D(64,name='conv9_1')(up9)
    conv9 = Conv2D(64,name='conv9_2')(conv9)

    ch,cw = get_crop_shape(inputs,conv9)
    conv9 = ZeroPadding2D(padding=(ch,name='conv9_3')(conv9)
    conv10 = Conv2D(1,(1,1),activation='sigmoid',name='conv10_1')(conv9)
    
    return conv10
    

def get_unet(img_shape = (200,200,1)):

    enc = get_encoder(img_shape)
    
    dec = get_decoder(enc)
    
    model = Model(inputs=enc[-1],outputs=dec)

    return model

创建整个模型并拟合

img_shape = (200,1)

old_model = get_unet(img_shape)

# old_model.compile(...)
# old_model.fit(...)

一如既往地提取编码器

# extract encoder
first_encoder_layer = 0
last_encoder_layer = 14
encoder_output_layer = [14,11,8,5,2,0]

encoder = Model(inputs=old_model.layers[first_encoder_layer].input,outputs=[old_model.layers[l].output for l in encoder_output_layer],name='encoder')

encoder.summary()

创建解码器结构并分配训练后的权重

# extract decoder fitted weights
restored_w = []
for w in old_model.layers[last_encoder_layer + 1:]:
    restored_w.extend(w.get_weights())
  
# reconstruct decoder architecture setting the fitted weights
new_inp = [Input(l.shape[1:]) for l in get_encoder(img_shape)]
new_dec = get_decoder(new_inp)
decoder = Model(new_inp,new_dec)
decoder.set_weights(restored_w)

decoder.summary()

收益预测

# generate random images
n_images = 20
X = np.random.uniform(0,1,(n_images,1)).astype('float32')

# get encoder predictions 
pred_encoder = encoder.predict(X)
print([p.shape for p in pred_encoder])

# get decoder predictions
pred_decoder = decoder.predict(pred_encoder)
print(pred_decoder.shape)

here正在运行的笔记本