Keras 均方误差损失层

5
我目前正在实现一个自定义的损失层,但在此过程中,我偶然发现了 objectivies.py 文件中均方误差的实现方式 [1]。我知道我对这个损失计算的理解可能有些错误,因为我一直认为平均值是针对每个输出的每个样本在每个小批量上分别进行的(张量的轴 0),但似乎平均值实际上是在最后一个轴上进行的,在单独的向量中,这意味着它是在输出之间进行的。当我设计我的自定义损失层时,无意中发现了这一点,因为它需要对几个输出的损失进行折扣,这些输出在特定位置的训练输出为特定值。不管怎样,我的均方误差理解是否正确?为什么 Keras 使用最后一个轴,从而将一个 1xn 输出向量转换为一个 1x1 输出向量呢?
谢谢。
[1] https://github.com/fchollet/keras/blob/master/keras/objectives.py#L7

你认为K.mean是什么意思? :) - Dr. Snoopy
抱歉,我修改了我的问题。我的意思是我没有看到哪里进行了平方运算,而不是均值。 - Corey J. Nolet
那将是K.square。 - Dr. Snoopy
你看完了我的整个问题吗? - Corey J. Nolet
是的,但无论如何这里有多个问题,我只是指出其中一个。 - Dr. Snoopy
我不是在问如何计算平方,而是在问为什么框架提供的默认MSE函数在被称为“均方误差”时没有进行平方计算。我在计算中没有看到任何地方进行平方运算。我知道如何计算平方,我想知道那段代码的作者为什么没有这样做。 - Corey J. Nolet
3个回答

9
MSE损失函数的代码如下所示:
def mean_squared_error(y_true, y_pred):
    return K.mean(K.square(y_pred - y_true), axis=-1)

这里首先对y_pred和y_true进行减法操作,然后将结果传递给K.square,这个函数将返回其参数的平方,接着将该结果传递给K.mean,计算平均值。

所以,这段代码明显在按照预期执行。最后一个轴为什么要进行操作与类别无关,这只是一种约定。需要注意的是,在MSE定义中通常没有类别概念。


啊,你说得对,我在代码中漏掉了K.square。糟糕。我在一个私人网络上,不幸的是我不能复制/粘贴代码,只能手动输入。在这种情况下,我手动输入有误。因此,你关于最后一个问题的回答是正确的。 - Corey J. Nolet
谢谢你的回答,顺便说一句!然而,轴确实是我提出问题的原因。对我来说,他们使用axis=-1而不是axis=0实际上是一个非常重要的问题,原因是他们定义通过网络传递的张量的惯例。他们强制你将批量大小作为张量的第一个维度,并且对于向量中的单个值集合作为输出,强制将其作为最后一个维度。这意味着他们正在跨所有这些输出计算损失,而不是每个输出单独计算。 - Corey J. Nolet
我知道我复制时犯了什么错误。我不小心复制了mean_absolute_error而不是mean_squared。那部分已经修好了,但轴的问题仍然困扰着我。 - Corey J. Nolet
你是什么意思? @Cor - Nima Mousavi
什么意思?@CoreyJ.Nolet是完全正确的。平均值应该在批次之间取。为什么是axis=-1? - Nima Mousavi

3
让我们详细介绍一下Keras中如何计算损失,以展示所有损失计算中的axis=-1是正确的:
  • 我们在losses.py中选择一个损失函数,然后将其传递给模型的compile方法。

  • compile中,计算总损失。这个过程分几步进行:第一步创建了一个包含每个输出对应的损失的列表。

  • 这第一步会调用_weighted_masked_objective,根据文档,该方法“为目标函数添加了对掩码和样本权重的支持”。
  • 基本上,_weighted_masked_objective返回一个新的目标函数,该函数考虑到用户在使用fit方法时提供的weightsmask参数。

如果我只截取关于问题的相关代码,我们大概得到以下内容。

def _weighted_masked_objective(fn):
    def weighted(y_true, y_pred, weights, mask=None):
          score_array = fn(y_true, y_pred) # Compute loss as in losses.py
          return K.mean(score_array) # Average over all axis

class Model(Container):
    def compile(self, optimizer, loss, metrics=None, loss_weights=None,
                sample_weight_mode=None, weighted_metrics=None,
                target_tensors=None, **kwargs):
        weighted_losses = [_weighted_masked_objective(fn) for fn in loss_functions]

因此,在最后,损失确实在每个维度上进行了平均,并且使用axis=-1只是一种优雅的方式,可以在代码的另一个点启用遮罩和加权损失。
注意:我没有解释其他步骤,因为它们对回答问题没有贡献。

2
通过与同事的一些对话,我相信我理解了这种情况,并且有了解决问题的适当方案。虽然我知道Theano提供了惰性求值的张量函数来在GPU上运行矩阵操作,但我没有意识到Keras的损失函数实际上是以一种方式编写的,其中编译的Theano执行图足够聪明,可以缓存某些值,以便正确地将损失值反向传播回整个网络。由于我正在创建的网络类型,我深入探索编写自己的定制损失函数,而没有完全理解Theano在损失函数被函数计算后如何处理该损失。
据我所知,我的担忧是正确的,即Keras使用最后一个轴存在问题。在我的情况下,我有一个完全卷积的深度神经网络,损失函数的输入是(x, 7,16,16),其中x是小批量样本的大小。通常,神经网络输出一个矩阵,其中第一个维度是小批量大小,第二个(通常是最后)维度是输出向量的实际大小。因此,在输出张量中使用最后一个轴来执行均方误差的“平均”部分是不正确的。相反,轴应该是1(如果基于从0开始的索引),因为需要对7个实际回归输出特征进行微分以进行反向传播。
我最初知道axis = -1可能不正确,我发布这个问题的原因是因为我无法解释为什么。自从我不得不深入研究神经网络背后的数学以来已经过了很长时间,但当我最终做到时,我能够解决其中的差距(我认为)。我在此发布响应,供将来可能遇到这种问题或Theano张量框架理解方面存在差距的人参考。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接