我在StackOverflow上问了一个关于自定义损失函数的返回值的问题。但是我没有得到一个明确的答案。
在tensorflow网站上的这个指南中,我发现了一个自定义损失函数的例子:
def custom_mean_squared_error(y_true, y_pred):
return tf.math.reduce_mean(tf.square(y_true - y_pred))
这个自定义损失函数中的reduce_mean
函数将返回一个标量。但是我认为自定义损失函数应该为训练批中的每个示例返回一个损失数组,而不是一个单一的损失值。
根据Model类的源代码,自定义损失函数用于构造LossFunctionWrapper
对象。我阅读了loss模块的源代码。我认为是LossFunctionWrapper.__call()__
方法负责获取训练批次的平均损失值。LossFunctionWrapper.__call()__
方法首先调用LossFunctionWrapper.call()
方法,为训练批中的每个示例获取一个损失数组。
另外,在losses模块的源码中,MeanAbsoluteError
类使用mean_squared_error
函数构造了一个LossFunctionWrapper
类,我们可以看到mean_squared_error
函数返回的是K.mean(math_ops.squared_difference(y_pred, y_true), axis=-1)
,是一个数组,而不是一个单值,我想我们的自定义损失函数应该就是这样的。
那么,为什么tensorflow网站上的指南中的自定义损耗函数会返回一个标量呢?这样定义一个自定义函数是不对的吗?
9条答案
按热度按时间vm0i2vca1#
@lambdaphy keras中的自定义损失函数返回单个值,如上面的示例所示,我们使用
tf.math.reduce_mean
。reduce_mean的简单示例如下。上述数组
a
的reduce_mean
为1,如输出所示。对于Keras模型,我们需要定义custom_loss函数来为每个批次提供标量损失。对于custom_training,您可以根据需要在自定义模型中定义损失函数。谢谢!
bvhaajcl2#
我知道在训练模型的时候,我们需要一个每个批次的标量损失。但是根据源代码,自定义损失函数并不负责获得这个标量损失。你能检查一下
Model.compile()
,Model.fit()
,Mode.train_step()
,(源代码)Loss.__call()__
,Loss.call()
和LossFunctionWrapper.call()
方法(source code)来查看一个训练批的损失值的计算过程?我阅读了这些代码,发现
Loss.__call__()
方法调用Loss.call()
方法(其在子类中实现,例如LossFunctionWrapper
)以获得训练批次中每个示例的损失阵列,则Loss.__call__()
方法调用compute_weighted_loss()
函数以获取LossFunctionWrapper.call()
方法是如何得到一个损失数组的呢?阅读源代码,我们可以看到它使用self.fn
来得到这些损失,而self.fn
是我们提供给Model.compile()
方法的损失函数。下面是
Loss.__call__()
的源代码:下面是
Loss.call()
的源代码:Loss.call()
方法只是Loss
的子类必须实现的一个接口,但是我们可以看到这个方法的返回值是Loss values,形状为[batch_size, d0, .. dN-1]
。现在我们来看
LossFunctionWrapper
类,LossFunctionWrapper
是Loss
的子类,在它的构造函数中,我们应该提供一个损失函数,这个损失函数存储在LossFunctionWrapper.fn
中,下面是LossFunctionWrapper.call()
的源代码,它实现了Loss.call()
方法:看到了吗?这里调用了我们提供的损失函数,它的任务是返回每个样本的损失值。
另外,作为一个例子,我们可以看到
MeanSquaredError
类是如何定义的,它就是一个使用mean_squared_error
函数作为损失函数的LossFunctionWrapper
类(即LossFunctionWrapper.fn=mean_squared_error
),mean_squared_error
函数的源代码也在losses
模块中定义:我们可以看到它返回的是一个数组,而不是标量(
K.mean()
中的axis=-1
),其返回值的第一维是batch_size
。根据
Model.compile()
和Model.fit()
的源代码,当我们提供一个自定义损失函数时,这个函数被用来构造一个LossFunctionWrapper
对象,就像MeanSquaredError
对象使用mean_squared_error
函数构造一个LossFunctionWrapper
对象一样。这就是为什么我认为自定义损失函数应该返回一个损失数组。因为获得训练批次的标量损失值不是损失函数的任务,所以Loss.__call()__
应该做这项工作。7kjnsjlb3#
基于以上的分析,我们可以看到,当我们定义自定义损失CLASS时,我们应该实现
call()
方法,而这个call()
方法应该返回一个数组,而不是标量。但是在tensorflow 指南中,我们也可以看到自定义损失类的例子,就在自定义损失函数的例子下面:我们可以看到
call()
方法的这个实现返回了一个标量。我认为这个行为也是错误的。svmlkihl4#
@lambdaphy谢谢您的问题。自定义损失函数需要为每个样本返回一个损失值。示例需要更新以反映这一点。
34gzjxbg5#
如果你有兴趣做改变,请随时给我一个公关。
zpgglvta6#
谢谢你的回复。我很乐意为你做贡献,但是我对使用github打开pull请求不太熟悉。另外我的母语不是英语,所以我可能不能胜任编辑文档。
如果您可以编辑文档,请帮助更改它。谢谢。
0ejtzxu17#
似乎我们仍在等待文档更改。
8tntrjer8#
大多数指南都遵循tf文档,它为一批训练数据返回单个值。我遵循
compute_weighted_loss
的源代码,其中LossFunctionWrapper
表示批轴的平均值,并发现如果自定义损失函数返回一个值而不是一个值数组,我们会遇到两个问题:sample_weights
自变量将是无意义,因为wo确实意味着第一。1.当使用
tf.distribute.Strategy
进行训练时,基于不正确的分母对损失进行平均。@lambdaphy你怎么看?
cl25kdpy9#
大多数指南都遵循tf文档,它为一批训练数据返回单个值。我遵循
compute_weighted_loss
的源代码,其中LossFunctionWrapper
表示批轴的平均值,并发现如果自定义损失函数返回一个值而不是一个值数组,我们会遇到两个问题:sample_weights
自变量将是无意义,因为wo确实意味着第一。1.当使用
tf.distribute.Strategy
进行训练时,基于不正确的分母对损失进行平均。@lambdaphy你怎么看?
是的,你说得对。损失函数对整个批次返回单个损失值在某些情况下会导致问题。
文件尚未更正。
这份文件中有许多模棱两可的地方。