gpt4 book ai didi

python - keras 将两个损失与可调权重相结合,其中输出不具有相同的维度

转载 作者:太空狗 更新时间:2023-10-30 01:18:16 24 4
gpt4 key购买 nike

我的问题与此处提出的问题类似: keras combining two losses with adjustable weights

但是,输出具有不同的维度,导致输出无法连接。因此,该解决方案不适用,是否有其他方法可以解决此问题?


问题:

enter image description here

我有一个包含两层的 keras 功能模型,输出 x1 和 x2。

x1 = Dense(1,activation='relu')(prev_inp1)

x2 = Dense(2,activation='relu')(prev_inp2)

我需要使用这些 x1 和 x2 在加权损失函数中使用它们,如附图所示。将“相同的损失”传播到两个分支。 Alpha 可以灵活地随迭代而变化。

最佳答案

对于这个问题,需要一个更详尽的解决方案。由于我们要使用可训练的权重,因此我们需要一个自定义层。

此外,我们将需要一种不同形式的训练,因为我们的损失不像其他只采用 y_truey_pred 的损失那样有效,并考虑加入两个不同的输出.

因此,我们将创建同一模型的两个版本,一个用于预测,另一个用于训练,训练版本本身将包含损失,在编译中使用虚拟 keras 损失函数。

预测模型

让我们使用一个非常基本的模型示例,它具有两个输出和一个输入:

#any input your true model takes
inp = Input((5,5,2))

#represents the localization output
outImg = Conv2D(1,3,activation='sigmoid')(inp)

#represents the classification output
outClass = Flatten()(inp)
outClass = Dense(2,activation='sigmoid')(outClass)

#the model
predictionModel = Model(inp, [outImg,outClass])

您经常使用这个进行预测。没有必要编译这个。

每个分支的损失

现在,让我们为每个分支创建自定义损失函数,一个用于 LossCls,另一个用于 LossLoc

在此处使用虚拟示例,如有必要,您可以更好地阐述这些损失。最重要的是它们输出的批处理形状类似于 (batch, 1) 或 (batch,)。两者都输出相同的形状,因此可以稍后对其求和。

def calcImgLoss(x):
true,pred = x
loss = binary_crossentropy(true,pred)
return K.mean(loss, axis=[1,2])

def calcClassLoss(x):
true,pred = x
return binary_crossentropy(true,pred)

这些将在训练模型的 Lambda 层中使用。

损失加权层 - (警告!已编辑! - 请参阅末尾的解释)

现在,让我们用可训练的 alpha 来衡量损失。可训练参数需要实现自定义层。

class LossWeighter(Layer):
def __init__(self, **kwargs): #kwargs can have 'name' and other things
super(LossWeighter, self).__init__(**kwargs)

#create the trainable weight here, notice the constraint between 0 and 1
def build(self, inputShape):
self.weight = self.add_weight(name='loss_weight',
shape=(1,),
initializer=Constant(0.5),
constraint=Between(0,1),
trainable=True)
super(LossWeighter,self).build(inputShape)

def call(self,inputs):
#old answer: will always tend to completely ignore the biggest loss
#return (self.weight * firstLoss) + ((1-self.weight)*secondLoss)
#problem: alpha tends to 0 or 1, eliminating the biggest of the two losses

#proposal of working alpha optimization
#return K.square((self.weight * firstLoss) - ((1-self.weight)*secondLoss))
#problem: might not train any of the losses, and even increase one of them
#in order to minimize the difference between the two losses

#new answer - a mix between the two, applying gradients to the right weights
loss1, loss2 = inputs #trainable
static_loss1 = K.stop_gradient(loss1) #non_trainable
static_loss2 = K.stop_gradient(loss2) #non_trainable

a1 = self.weight #trainable
a2 = 1 - a1 #trainable
static_a1 = K.stop_gradient(a1) #non_trainable
static_a2 = 1 - static_a1 #non_trainable


#this trains only alpha to minimize the difference between both losses
alpha_loss = K.square((a1 * static_loss1) - (a2 * static_loss2))
#or K.abs (.....)

#this trains only the original model weights to minimize both original losses
model_loss = (static_a1 * loss1) + (static_a2 * loss2)

return alpha_loss + model_loss

def compute_output_shape(self,inputShape):
return inputShape[0]

请注意,有一个自定义约束将此权重保持在 0 和 1 之间。此约束通过以下方式实现:

class Between(Constraint):
def __init__(self,min_value,max_value):
self.min_value = min_value
self.max_value = max_value

def __call__(self,w):
return K.clip(w,self.min_value, self.max_value)

def get_config(self):
return {'min_value': self.min_value,
'max_value': self.max_value}

训练模型

该模型将以预测模型为基础,在最后加入损失计算和损失权重,只输出损失值。因为它只输出损失,所以我们将使用真实目标作为输入,并使用如下定义的虚拟损失函数:

def ignoreLoss(true,pred):
return pred #this just tries to minimize the prediction without any extra computation

模型输入:

#true targets
trueImg = Input((3,3,1))
trueClass = Input((2,))

#predictions from the prediction model
predImg = predictionModel.outputs[0]
predClass = predictionModel.outputs[1]

模型输出 = 损失:

imageLoss = Lambda(calcImgLoss, name='loss_loc')([trueImg, predImg])
classLoss = Lambda(calcClassLoss, name='loss_cls')([trueClass, predClass])
weightedLoss = LossWeighter(name='weighted_loss')([imageLoss,classLoss])

型号:

trainingModel = Model([predictionModel.input, trueImg, trueClass], weightedLoss)
trainingModel.compile(optimizer='sgd', loss=ignoreLoss)

假人训练

inputImages = np.zeros((7,5,5,2))
outputImages = np.ones((7,3,3,1))
outputClasses = np.ones((7,2))
dummyOut = np.zeros((7,))

trainingModel.fit([inputImages,outputImages,outputClasses], dummyOut, epochs = 50)
predictionModel.predict(inputImages)

必要的导入

from keras.layers import *
from keras.models import Model
from keras.constraints import Constraint
from keras.initializers import Constant
from keras.losses import binary_crossentropy #or another you need

(编辑)用旧答案解释问题:

旧答案中使用的公式会使 alpha 始终变为 0 或 1,这意味着只会训练两个损失中最小的那个。 (无用)

一个新的公式导致 alpha 使两个损失具有相同的值。 Alpha 会得到适当的训练,而不是倾向于 0 或 1。但是,损失仍然不会得到适当的训练,因为“增加一个损失以达到另一个损失”对于模型来说是一种可能性,一旦两个损失相等,模型将停止训练。

新的解决方案是上述两个建议的混合,而第一个实际上训练了损失,但 alpha 错误;第二个以错误的损失训练 alpha。混合解决方案将两者相加,但使用 K.stop_gradient 来防止训练的错误部分发生。

这样做的结果是:“最简单”的损失(不是最大的)将比最困难的损失训练得更多。我们可能会使用K.absK.square,来对比两种损失之间的“mae”或“mse”。最好的选择取决于实验。

请参阅此表比较新旧提案:

Table comparing solutions

但这并不能保证最佳优化!!!

不过,训练最简单的损失并不总是有最好的结果。这可能比仅仅因为它的公式不同而支持巨大的损失更好。但预期结果可能仍需要对损失进行一些手动加权。

我担心这个重量没有自动训练。如果你有一个目标指标,你可以尝试训练这个指标(如果可能的话,但是依赖于排序、获取索引、舍入或任何破坏反向传播的指标可能无法转化为损失)。

关于python - keras 将两个损失与可调权重相结合,其中输出不具有相同的维度,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53707199/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com