我正在学习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,)
形状的数组。但上述函数给出整个批量的单个值。
也许上面的例子是错误的?谁能在这个问题上给予我一些帮助?
为什么我认为loss函数应该返回数组而不是单个值?
我看了Model类的源代码。当你提供一个损失函数(请注意,它是函数,而不是损失类)到Model.compile()
方法,该损失函数用于构造LossesContainer
对象,这个传递给LossesContainer
类的构造函数的损失函数再次被用于构造LossFunctionWrapper
对象,其存储在LossesContainer._losses
中。
**根据LossFunctionWrapper类的源代码,通过LossFunctionWrapper.__call__()
方法计算训练批次的整体损失值(继承自Loss
类),也就是说,它返回整个批次的单个损失值。**但是LossFunctionWrapper.__call__()
首先调用LossFunctionWrapper.call()
方法,以获得训练批次中每个样本的损失数组。然后将这些损耗进行最终平均,得到整个批次的单个损耗值,正是在LossFunctionWrapper.call()
方法中调用了提供给Model.compile()
方法的损耗函数。
这就是为什么我认为自定义loss函数应该返回一个loss数组,而不是一个标量值。此外,如果我们为Model.compile()
方法编写一个自定义Loss
类,那么我们自定义Loss
类的call()
方法也应该返回一个数组,而不是一个信号值。
我在github上打开了一个issue,确认了自定义损耗函数需要返回每个样本一个损耗值,这个例子需要更新以反映这一点。
7条答案
按热度按时间whitzsjs1#
实际上,据我所知,损失函数的返回值的形状并不重要,即它可以是标量Tensor,也可以是每个样本一个或多个值的Tensor。重要的是如何将其还原为标量值,以便在优化过程中使用或显示给用户。为此,您可以在
Reduction
documentation中检查还原类型。此外,下面是
compile
方法文档对loss
参数的说明,部分地解决了这一点:损失:字符串(目标函数的名称)、目标函数或
tf.keras.losses.Loss
示例。请参见tf.keras.losses
。目标函数是具有签名loss = fn(y_true,y_pred)
的任何可调用函数,其中y_true
=形状为[batch_size, d0, .. dN]
的标准真值,稀疏损失函数除外,例如稀疏分类交叉熵,其中shape =[batch_size, d0, .. dN-1]
。y_pred
= shape =[batch_size, d0, .. dN]
的预测值。它返回加权损失浮动Tensor。如果使用自定义Loss
示例,并且将减少设置为NONE
,则返回值具有[batch_size, d0, .. dN-1]
的形状,即每样本或每时间步长的损耗值;如果模型有多个输出,您可以通过传递字典或损失列表来对每个输出使用不同的损失。模型将最小化的损失值将是所有单个损失的总和。此外,值得注意的是,TF/Keras中的大多数内置损失函数通常在最后一个维度(即
axis=-1
)上被减小。对于那些怀疑返回标量值的自定义损失函数是否有效的人:你可以运行下面的代码片段,你会看到模型会正确地训练和收敛。
gijlo24d2#
我在github上打开了一个issue。确认了自定义损耗函数需要返回每个样本一个损耗值。这个例子需要更新以反映这一点。
zbdgwd5y3#
我认为@Gödel提出的问题是完全合法和正确的。自定义损失函数应该返回每个样本的损失值。而且,@today提供的解释也是正确的。最终,这一切都取决于使用的*减少的类型。
因此,如果使用类API创建损失函数,则自定义类中会自动继承reduction参数,使用其默认值"sum_over_batch_size(其简单地平均给定批中的所有损失值)。其它选项是"sum",其计算总和而不是平均,并且最后一个选项是"none",其中返回损失值的数组。
Keras文档中还提到,当使用
model.fit()
时,这些减少的差异是不尊重的,因为TF/Keras会自动处理减少。最后,我们还提到,当创建一个自定义损失函数时,应该返回一组损失(单个样本损失),它们的减少由框架处理。
链接:
toe950274#
tf.math.reduce_mean
取批处理的平均值并返回,这就是为什么它是一个标量。fquxozlt5#
*Tensorflow 网站上给出的损失函数绝对正确。
在机器学习中,我们使用的loss是单个训练样本的损失之和,因此它应该是一个标量值。(由于对于所有的样本,我们使用单个网络,因此我们需要单个损失值来更新参数。)
关于制作损耗容器:
当使用并行计算时,制作容器是跟踪计算的损失指数的更简单且可行的方式,因为我们使用批处理来训练而不是整个训练集。
hk8txs486#
由于存在多个通道,因此可以增加维度......但是,每个通道应仅具有损耗的标量值。
hmae6n7t7#
Tensorflow文档忽略了这一点,但这一点在Keras documentation上得到了明确的说明和澄清。
请注意,这是tf.keras. loss.mean_squared_error等损失函数与tf.keras. loss.MeanSquaredError等违约损失类示例之间的重要区别:函数版本不执行归约,但默认情况下类示例执行归约。
它还指出:
默认情况下,损失函数为每个输入样本返回一个标量损失值。