如何使用隐藏层激活来构造损失函数,并在Keras中拟合时提供y_真值?

如何使用隐藏层激活来构造损失函数,并在Keras中拟合时提供y_真值?,keras,Keras,假设我有一个这样的模型。M1和M2是连接模型左侧和右侧的两层。 在培训期间,我希望M1可以学习从L2_左激活到L2_右激活的映射。同样,M2可以学习从L3_右激活到L3_左激活的映射。 模型还需要了解两个输入和输出之间的关系。 因此,对于M1、M2和L3_,我应该分别有三个损失函数 我可能可以使用: model.compile(optimizer='rmsprop', loss={'M1': 'mean_squared_error', 'M2

假设我有一个这样的模型。M1和M2是连接模型左侧和右侧的两层。

在培训期间,我希望M1可以学习从L2_左激活到L2_右激活的映射。同样,M2可以学习从L3_右激活到L3_左激活的映射。 模型还需要了解两个输入和输出之间的关系。 因此,对于M1、M2和L3_,我应该分别有三个损失函数

我可能可以使用:

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是隐藏层激活,而不是来自数据集。
是否有可能建立这个模型并使用它的隐藏层激活来训练它

如果只有一个输出,则必须只有一个损失函数

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

如果你想在模型中间设置损失函数,你必须从这些层取输出。 创建模型图:(如果已经定义了模型,请参见本答案的结尾)

创建具有三个输出的模型:

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

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 =??)
但你也必须进行三种不同的YT训练,以便进行以下训练:

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], ...)

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

……嗯。。。。起初我觉得这不可行。但是,如果这类似于“只训练模型的一部分”,那么定义只到达某一点的模型并使部分层不可训练是完全可以的

这样做不会影响这些层。如果这是您想要的,那么您可以这样做:

#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,并在培训中循环使用它们。每个时代,也许


那应该测试一下……

非常感谢你,丹尼尔!这是非常有用的!下面的问题是如何获得[yTrainM1,yTrainM2,yTrainL13]?因为其中两个(yTrainM1,yTrainM2)是隐藏层(L22,L13)的激活,如果我使用model.fit(),如何在训练期间获得激活?我可以想象,在不使用model.fit()的情况下,我可以提供一个数据批并获得激活。然后使用数据批和使用model.train\u on\u batch()获得的激活来训练模型。我可以用ForLoop重复这个过程。这是唯一可能和最有效的方法吗?输出是激活,不是吗?如果你将激活作为损失函数的参数,你必须将它们与一些东西进行比较,你想将它们与什么进行比较?你是说“权重”而不是“激活”?对不起,我想我没有说清楚。我希望M1能在L2_左激活的基础上学习预测L2_右激活。因此,经过训练后,M2的输出应该接近L2_right的激活。更清楚的是,当一个新的数据样本从input_1和input_2进入该模型时,通过input->L1->L2,我们可以激活L2_left和L2_right。此时,M1的输入和预期输出应分别为L2_left和L2_right的激活。因此,在您的实现中,yTrainM1应该是L2_right的激活。
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)