使用 keras 使用预训练的 VGG 实现感知损失

Implement perceptual loss with pretrained VGG using keras

我对 DL 和 Keras 比较陌生。

我正在尝试在 Keras 中使用预训练的 VGG16 实现感知损失,但遇到了一些麻烦。我已经找到 但我仍在挣扎 :/

我的网络应该做什么的简短说明:

我有一个 CNN(后来称为 mainModel),它获取灰度图像作为输入 (#TrainData, 512, 512, 1) 并输出相同大小的灰度图像。网络应该减少图像中的伪影——但我认为这对这个问题来说并不重要。而不是使用例如MSE作为损失函数,我想实现感知损失。

我想做什么(希望我已经正确理解了感知损失的概念):

我想在我的 mainModel 中附加一个 lossModel(预训练的 VGG16 和固定参数)。然后我想将 mainModel 的输出传递给 lossModel。此外,我将标签图像 (Y_train) 传递给 lossModel。接下来,我比较了 lossModel 特定层(例如 block1_conv2)的激活,例如使用MSE 并将其用作损失函数。

到目前为止我做了什么:

加载数据并创建主模型:

### Load data ###
with h5py.File('.\train_test_val.h5', 'r') as hf:
    X_train = hf['X_train'][:]
    Y_train = hf['Y_train'][:]
    X_test = hf['X_test'][:]
    Y_test = hf['Y_test'][:]
    X_val = hf['X_val'][:]
    Y_val = hf['Y_val'][:]

### Create Main Model ###
input_1 = Input((512,512,9))
conv0 = Conv2D(64, (3,3), strides=(1,1), activation=relu, use_bias=True, padding='same')(input_1)
.
.
.

mainModel = Model(inputs=input_1, outputs=output)

创建 lossModel,将其附加到 mainModel 并修复参数:

### Create Loss Model (VGG16) ###
lossModel = vgg16.VGG16(include_top=False, weights='imagenet', input_tensor=mainModel.output, input_shape=(512,512, 1))
lossModel.trainable=False

for layer in lossModel.layers:
    layer.trainable=False

创建包括两个网络的新模型并编译它

### Create new Model ###
fullModel = Model(inputs=mainModel.input, outputs=lossModel.output)

fullModel.compile(loss='mse', optimizer='adam',metrics=['mse','mae'])
fullModel.summary()

通过损失网络调整标签图像:

Y_train_lossModel = lossModel.predict(Y_train)

使用感知损失拟合完整模型:

fullModel.fit(X_train, Y_train_lossModel, batch_size=32, epochs=5, validation_data=[X_val,Y_val])

发生的问题:

RuntimeError: Graph disconnected: cannot get value for tensor Tensor("conv2d_2/Relu:0", shape=(?, 512, 512, 3), dtype=float32) 在层 "input_2"。可以毫无问题地访问以下先前的层:[]

非常感谢您的帮助,对于这么长的问题深表歉意:)

通道数

嗯,第一个问题很重要。

VGG 模型是为具有 3 个通道的彩色图像制作的...因此,它完全不适合您的情况。我不确定是否有黑白图像的模型,但您应该搜索它们。

一个解决方法,我不知道是否能正常工作,是制作 3 个 mainModel 的输出副本。

tripleOut = Concatenate()([mainModel.output,mainModel.output,mainModel.output])

图表断开连接

这意味着您在代码的任何地方都没有在 fullModel 的输入和输出之间创建连接。您必须将 mainModel 的输出连接到 lossModel

的输入

但首先,让我们为多个输出准备 VGG 模型。

正在为多个输出准备 lossModel

您必须select将使用 VGG 模型的哪些层来计算损失。如果您只使用最终输出,则不会有真正好的感知损失,因为最终输出更多的是概念而不是特征。

因此,在 select 层之后,列出它们的索引或名称:

selectedLayers = [1,2,9,10,17,18] #for instance

让我们从 VGG16 制作一个新模型,但有多个输出:

#a list with the output tensors for each selected layer:
selectedOutputs = [lossModel.layers[i].output for i in selectedLayers]
     #or [lossModel.get_layer(name).output for name in selectedLayers]

#a new model that has multiple outputs:
lossModel = Model(lossModel.inputs,selectedOutputs)

加入模型

现在,我们在这里创建两个模型之间的连接。

我们称lossModel(好像它是一个层)将mainModel的输出作为输入:

lossModelOutputs = lossModel(tripleOut) #or mainModel.output if not using tripeOut

现在,有了从 mainModel 的输入到 lossModel 的输出完全连接的图,我们可以创建 fullModel:

fullModel = Model(mainModel.input, lossModelOutputs)

#if the line above doesn't work due to a type problem, make a list with lossModelOutputs:
lossModelOutputs = [lossModelOutputs[i] for i in range(len(selectedLayers))]

培训

接受这个新 lossModel 的预测,就像您所做的那样。但是对于解决方法,我们也将其设为三通道:

triple_Y_train = np.concatenate((Y_train,Y_train,Y_train),axis=-1)
Y_train_lossModel = lossModel.predict(triple_Y_train)
#the output will be a list of numpy arrays, one for each of the selected layers   

确保在 fullModel.compile() 之前让 lossModel 的每一层都不可训练。

如果你想要 'mse' 所有输出,你只需做:

fullModel.compile(loss='mse', ...)

如果您希望每一层都有不同的损失,请传递损失列表:

fullModel.compile(loss=[loss1,loss2,loss3,...], ...)

其他注意事项

由于 VGG 应该处理 caffe 格式的图像,您可能需要在 mainModel 之后添加几层以使输出合适。这不是绝对必需的,但它会使用 VGG 的最佳性能。

看看keras如何将0到255的输入图像转换成caffe格式here at line 15 or 44