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

时间:2017-12-06 13:09:05

标签: tensorflow keras deep-learning vgg-net

我对DL和Keras相对较新。

我正试图在Keras中使用预训练的VGG16来实现感知损失但是有一些麻烦。我已经找到了question,但我仍在努力:/

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

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

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

我想将一个lossModel(带有固定参数的预先训练的VGG16)附加到我的mainModel。然后我想将mainModel的输出传递给lossModel。另外,我将标签图像(Y_train)传递给lossModel。此外,我使用例如比较损失模型的特定层(例如,block1_conv2)的激活。 MSE并将其用作损失函数。

到目前为止我做了什么:

加载数据并创建mainModel:

### 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并修复params:

### 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:

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

发生问题:

  • VGG16希望获得形状输入(?,?, 3 )但我的mainModel输出灰度图像(?,?, 1

  • 将lossModel附加到mainModel的一些问题

RuntimeError:图表已断开连接:无法在图层"获取张量张量(" conv2d_2 / Relu:0",shape =(?,512,512,3),dtype = float32)的值INPUT_2&#34 ;.访问以下先前的图层时没有问题:[]

  • 如何在特定图层激活时计算MSE,而不是在lossModel的输出处计算?

非常感谢你的帮助,并为这个极长的问题感到抱歉:)

1 个答案:

答案 0 :(得分:5)

频道数

嗯,第一个问题很重要。

VGG模型是用3个频道的彩色图像制作的...所以,它对你的情况来说不是正确的型号。我不确定是否有适用于黑色和黑色的型号。白色图像,但你应该搜索它们。

我不知道是否能运作良好的解决方法是制作3份mainModel的输出。

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

图表已断开连接

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

的输入

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

为多个输出准备lossModel

您必须选择将使用VGG模型的哪些层来计算损失。如果你只使用最终输出那么实际上并不是一个很好的感知损失,因为最终输出更多的是概念而不是功能。

因此,在选择图层后,请列出其索引或名称:

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   

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

如果你想要' 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