如何使用隐藏层激活来构造损失函数并在拟合期间提供y_true?

时间:2017-05-10 09:52:39

标签: keras

假设我有这样的模型。 M1和M2是连接模型左侧和右侧的两层。 The example model: Red lines indicate backprop directions

在训练期间,我希望M1可以学习从L2_left激活到L2_right激活的映射。类似地,M2可以学习从L3_right激活到L3_left激活的映射。 该模型还需要了解两个输入和输出之间的关系。 因此,我应该分别为M1,M2和L3_left设置三个损失函数。

我可能会用:

model.compile(optimizer='rmsprop',
          loss={'M1': 'mean_squared_error',
                'M2': 'mean_squared_error', 
                'L3_left': mean_squared_error'})

但在培训期间,我们需要提供y_true,例如:

model.fit([input_1,input_2], y_true)

在这种情况下,y_true是隐藏图层激活而不是数据集。 是否有可能构建此模型并使用它的隐藏层激活来训练它?

2 个答案:

答案 0 :(得分:1)

如果您只有输出,则必须只有一个输出功能

如果你想要三个损失函数,你必须有三个输出,当然还有三个Y向量用于训练。

如果您想在模型中间使用损失函数,则必须从这些图层中获取输出。

创建模型图:(如果已定义模型,请参阅此答案的结尾)

#Here, all "SomeLayer(blabla)" could be replaced by a "SomeModel" if necessary
    #Example of using a layer or a model:
        #M1 = SomeLayer(blablabla)(L12) 
        #M1 = SomeModel(L12)

from keras.models import Model
from keras.layers import *

inLef = Input((shape1))   
inRig = Input((shape2))

L1Lef = SomeLayer(blabla)(inLef)
L2Lef = SomeLayer(blabla)(L1Lef)
M1 = SomeLayer(blablaa)(L2Lef) #this is an output

L1Rig = SomeLayer(balbla)(inRig)

conc2Rig = Concatenate(axis=?)([L1Rig,M1]) #Or Add, or Multiply, however you're joining the models    
L2Rig = SomeLayer(nlanlab)(conc2Rig)
L3Rig = SomeLayer(najaljd)(L2Rig)

M2 = SomeLayer(babkaa)(L3Rig) #this is an output

conc3Lef = Concatenate(axis=?)([L2Lef,M2])
L3Lef = SomeLayer(blabla)(conc3Lef) #this is an output

使用三个输出创建模型:

现在您已准备好图表,并且知道输出是什么,您可以创建模型:

model = Model([inLef,inRig], [M1,M2,L3Lef])
model.compile(loss='mse', optimizer='rmsprop')

如果您希望每个输出有不同的损失,那么您可以创建一个列表:

#example of custom loss function, if necessary
def lossM1(yTrue,yPred):
    return keras.backend.sum(keras.backend.abs(yTrue-yPred))

#compiling with three different loss functions
model.compile(loss = [lossM1, 'mse','binary_crossentropy'], optimizer =??)

但是你也必须有三种不同的yTraining,用于训练:

model.fit([input_1,input_2], [yTrainM1,yTrainM2,y_true], ....)

如果您的模型已经定义,并且您没有像我一样创建它的图形:

然后,你必须在yourModel.layers[i]中找到哪些是M1和M2,所以你要创建一个这样的新模型:

M1 = yourModel.layers[indexForM1].output
M2 = yourModel.layers[indexForM2].output
newModel = Model([inLef,inRig], [M1,M2,yourModel.output])

如果您希望两个输出相等:

在这种情况下,只需减去lambda图层中的两个输出,并使该lambda图层成为模型的输出,期望值为0。

使用与以前完全相同的变量,我们只需创建两个上瘾图层来减去输出:

diffM1L1Rig = Lambda(lambda x: x[0] - x[1])([L1Rig,M1])
diffM2L2Lef = Lambda(lambda x: x[0] - x[1])([L2Lef,M2])

现在你的模型应该是:

newModel = Model([inLef,inRig],[diffM1L1Rig,diffM2L2lef,L3Lef])    

培训期望这两个差异为零:

yM1 = np.zeros((shapeOfM1Output))
yM2 = np.zeros((shapeOfM2Output))
newModel.fit([input_1,input_2], [yM1,yM2,t_true], ...)

答案 1 :(得分:0)

尝试回答最后一部分:如何使渐变仅影响模型的一侧。

......好吧....起初这对我来说听起来不可行。但是,如果它类似于"只训练模型的一部分",那么通过定义仅到达某个点并使部分层无法处理的模型就完全可以了。

通过这样做,什么都不会影响这些层。如果这就是你想要的,那么你就可以做到:

#using the previous vars to define other models

modelM1 = Model([inLef,inRig],diffM1L1Rig)

上述模型以diffM1L1Rig结尾。在编译之前,必须设置L2Right无法处理:

modelM1.layers[??].trainable = False
#to find which layer is the right one, you may define then using the "name" parameter, or see in the modelM1.summary() the shapes, types etc. 

modelM1.compile(.....)
modelM1.fit([input_1, input_2], yM1)

此建议使您只训练模型的一部分。您可以重复M2的步骤,在编译之前锁定所需的图层。

您还可以定义一个涵盖所有图层的完整模型,并仅锁定您想要的图层。但是你不能(我认为)能够使一半的梯度通过另一侧,一半的梯度通过另一侧。

所以我建议您保留三个模型,fullModel,modelM1和modelM2,然后在训练中循环它们。每一个时代,也许......

应该测试......