如何通过 Tensorflow2.x 中的子类 tf.keras.losses.Loss 类自定义损失

dig*_*ger 6 python keras tensorflow tensorflow2.0

当我阅读Tensorflow 网站上的指南时,我发现了两种自定义损失的方法。第一个是定义损失函数,就像:

def basic_loss_function(y_true, y_pred):
    return tf.math.reduce_mean(tf.abs(y_true - y_pred))
Run Code Online (Sandbox Code Playgroud)

并且为了简单起见,我们假设批量大小也为1,因此y_true和的形状y_pred都是(1, c),其中c是类的数量。所以在这个方法中,我们给出两个向量y_truey_pred,并返回一个值(scala)。

然后,第二种方法是子类化tf.keras.losses.Lossclass,guide中的代码是:

class WeightedBinaryCrossEntropy(keras.losses.Loss):
    """
    Args:
      pos_weight: Scalar to affect the positive labels of the loss function.
      weight: Scalar to affect the entirety of the loss function.
      from_logits: Whether to compute loss from logits or the probability.
      reduction: Type of tf.keras.losses.Reduction to apply to loss.
      name: Name of the loss function.
    """
    def __init__(self, pos_weight, weight, from_logits=False,
                 reduction=keras.losses.Reduction.AUTO,
                 name='weighted_binary_crossentropy'):
        super().__init__(reduction=reduction, name=name)
        self.pos_weight = pos_weight
        self.weight = weight
        self.from_logits = from_logits

    def call(self, y_true, y_pred):
        ce = tf.losses.binary_crossentropy(
            y_true, y_pred, from_logits=self.from_logits)[:,None]
        ce = self.weight * (ce*(1-y_true) + self.pos_weight*ce*(y_true))
        return ce
Run Code Online (Sandbox Code Playgroud)

在 call 方法中,像往常一样,我们给出两个向量y_truey_pred,但我注意到它返回ce,这是一个形状为 (1, c) 的向量!

那么上面的玩具示例有什么问题吗?或者 Tensorflow2.x 背后有什么魔力?

And*_*eli 2

除了实现之外,两者之间的主要区别在于损失函数的类型。第一个是 L1 损失(定义为绝对差的平均值,主要用于类似回归的问题),而第二个是二元交叉熵(用于分类)。它们并不意味着是同一损失的不同实现,这一点在您链接的指南中已有说明。

多标签、多类分类设置中的二元交叉熵为每个类输出一个值,就好像它们彼此独立一样。

编辑:

在第二个损失函数中,reduction参数控制输出的聚合方式,例如。默认情况下,您的代码使用keras.losses.Reduction.AUTO,如果您检查源代码,它会转换为对批次求和。这意味着,最终的损失将是一个向量,但还有其他可用的减少,您可以在文档中查看它们。我相信,即使您没有定义归约来获取损失向量中损失元素的总和,TF 优化器也会这样做,以避免反向传播向量时出现错误。向量上的反向传播会导致“贡献”每个损失元素的权重出现问题。不过,我没有在源代码中检查过这一点。:)