我正在学习tensorflow(2.3)中的keras API。在tensorflow网站的指南中,我发现了一个自定义损失函数的例子:
def custom_mean_squared_error(y_true, y_pred):
return tf.math.reduce_mean(tf.square(y_true - y_pred))
在这个自定义损失函数中,reduce_mean
函数将返回一个标量。
像这样定义损失函数是否正确?据我所知,y_true
和y_pred
的形状的第一个维度是批处理大小。 我认为损失函数应该返回每个样本的损失值。因此,损失函数应该给出形状为(batch_size,)
的数组。 但是上面的函数为整个批次给出单个值。
也许以上示例是错误的?有人能帮助我解决这个问题吗?
p.s. 为什么我认为损失函数应该返回一个数组而不是一个单一的值?
我阅读了Model类的源代码。 当您向Model.compile()
方法提供损失函数(请注意它是一个函数,而不是一个损失类)时,该损失函数用于构造一个LossesContainer
对象,该对象存储在Model.compiled_loss
中。 将此损失函数传递给LossesContainer
类的构造函数后,将再次使用它来构造LossFunctionWrapper
对象,该对象存储在LossesContainer._losses
中。
根据LossFunctionWrapper类的源代码,在训练批次的整体损失值是由LossFunctionWrapper.__call__()
方法(从Loss
类继承)计算的,即它为整个批次返回单个损失值。 但是,LossFunctionWrapper.__call__()
首先调用LossFunctionWrapper.call()
方法,以获得每个训练批次样本的一组损失。 然后这些损失最终被平均以获取整个批次的单个损失值。 在LossFunctionWrapper.call()
方法中调用提供给Model.compile()
方法的损失函数。
这就是为什么我认为自定义损失函数应该返回一个损失数组而不是一个单一标量值的原因。此外,如果我们为Model.compile()
方法编写自定义Loss
类,则我们自定义Loss
类的call()
方法也应该返回一个数组,而不是单一标量值。
我在github上开了一个issue。确认自定义损失函数需要为每个样本返回一个损失值。需要更新示例以反映此更改。
Loss.__call__()
方法调用compute_weighted_loss
函数来将每个示例的损失减少为训练批次的标量损失。除非我们定义Loss
的子类并重写__call__()
方法,否则我们无法更改此行为。但是当我们提供自定义损失函数时,它应该返回一个损失数组,以便compute_weighted_loss
可以计算平均值。 - GödelLoss.__call __()
方法中使用,而不是在自定义损失函数中使用。 - GödelLoss
类的这种行为(即它不检查自定义损失函数返回值的形状)。但是在将来,如果Loss.__call__()
方法进行了检查,这可能会导致问题。但现在,让我们就按照这种方式定义我们的自定义损失函数吧~ - Gödel