Keras 中的自定义损失函数应该为批次返回单个损失值还是为训练批次中的每个样本返回一系列损失?

Göd*_*del 14 machine-learning keras tensorflow loss-function tensorflow2.0

我正在 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))
Run Code Online (Sandbox Code Playgroud)

reduce_mean这个自定义损失函数函数会返回一个标量。

这样定义损失函数合适吗?据我所知,y_true和形状的第一维y_pred是批量大小。我认为损失函数应该为批次中的每个样本返回损失值。所以损失函数应该给出一个形状数组(batch_size,)。但是上面的函数为整个批次提供了一个值。

也许上面的例子是错误的?任何人都可以在这个问题上给我一些帮助吗?


ps为什么我认为损失函数应该返回一个数组而不是单个值?

我阅读了Model类的源代码。当您向方法提供损失函数(请注意它是一个函数,而不是损失)时Model.compile(),该损失函数用于构造一个LossesContainer对象,该对象存储在Model.compiled_loss. 传递给LossesContainer类的构造函数的这个损失函数再次用于构造一个LossFunctionWrapper对象,该对象存储在LossesContainer._losses.

根据LossFunctionWrapper类的源代码,训练批次的整体损失值是通过LossFunctionWrapper.__call__()方法(继承自Loss类)计算的,即它返回整个批次的单个损失值。但是第LossFunctionWrapper.__call__()一个调用该LossFunctionWrapper.call()方法以获得训练批次中每个样本的损失数组。然后将这些损失最后平均以获得整批的单个损失值。这是在LossFunctionWrapper.call()方法的损失函数提供给Model.compile()方法被调用。

这就是为什么我认为自定义损失函数应该返回一系列损失,而不是单个标量值。此外,如果我们LossModel.compile()方法编写自定义类call(),我们自定义Loss类的方法也应该返回一个数组,而不是一个信号值。


我在github上打开了一个问题。已确认需要自定义损失函数来为每个样本返回一个损失值。该示例将需要更新以反映这一点。

Göd*_*del 6

我在github上打开了一个问题。已确认需要自定义损失函数来为每个样本返回一个损失值。该示例将需要更新以反映这一点。


tod*_*day 6

实际上,据我所知,损失函数的返回值的形状并不重要,即它可以是标量张量,也可以是每个样本一个或多个值的张量。重要的是它应该如何减少到一个标量值,以便它可以用于优化过程或显示给用户。为此,您可以检查Reduction 文档中的减少类型。

此外,这里是compile方法文档关于loss参数的说明,部分解决了这一点:

loss:字符串(目标函数名称)、目标函数或tf.keras.losses.Loss实例。见tf.keras.losses。目标函数是任何带有签名的可调用函数loss = fn(y_true,y_pred),其中y_true= 形状为 = 的地面真值[batch_size, d0, .. dN],除了稀疏的损失函数,例如形状为 = 的稀疏分类交叉熵[batch_size, d0, .. dN-1]y_pred= 形状为 = 的预测值[batch_size, d0, .. dN]。它返回一个加权损失浮动张量。如果使用自定义Loss实例并将缩减设置为NONE,则返回值的形状为[batch_size, d0, .. dN-1]IE。每个样本或每个时间步的损失值;否则,它是一个标量。如果模型有多个输出,您可以通过传递字典或损失列表对每个输出使用不同的损失。模型将最小化的损失值将是所有单个损失的总和。

此外,值得注意的是,TF/Keras 中的大多数内置损失函数通常在最后一个维度(即axis=-1)上减少。


对于那些怀疑返回标量值的自定义损失函数是否有效的人:您可以运行以下代码段,您将看到模型将正确训练和收敛。

import tensorflow as tf
import numpy as np

def custom_loss(y_true, y_pred):
    return tf.reduce_sum(tf.square(y_true - y_pred))

inp = tf.keras.layers.Input(shape=(3,))
out = tf.keras.layers.Dense(3)(inp)

model = tf.keras.Model(inp, out)
model.compile(loss=custom_loss, optimizer=tf.keras.optimizers.Adam(lr=0.1))

x = np.random.rand(1000, 3)
y = x * 10 + 2.5
model.fit(x, y, epochs=20)
Run Code Online (Sandbox Code Playgroud)


San*_*hit 6

我认为@G\xc3\xb6del 提出的问题是完全合法且正确的。自定义损失函数应返回每个样本的损失值。而且,@today 提供的解释也是正确的。最后,这完全取决于所使用的还原类型。

\n

因此,如果使用类 API 创建损失函数,那么自定义类中会自动继承归约参数。使用其默认值“ sum_over_batch_size ”(这只是给定批次中所有损失值的平均值)。其他选项是“ sum ”,它计算总和而不是求平均值,最后一个选项是“ none ”,其中返回损失值数组。

\n

Keras 文档中还提到,在使用时,这些减少的差异是不敬的,model.fit()因为减少是由 TF/Keras 自动处理的。

\n

最后,还提到,当创建自定义损失函数时,应该返回一组损失(单个样本损失)。它们的减少由框架处理。

\n

链接:

\n\n