加权稀疏分类交叉熵

4

我正在处理一个语义分割问题,在这个问题中,我关心的两个类别(除了背景)在图像像素上是相当不平衡的。由于训练掩码的编码方式,我实际上使用稀疏分类交叉熵作为损失函数。是否有任何版本的稀疏分类交叉熵可以考虑类别权重?我没有找到它,甚至原始的稀疏分类交叉熵源代码也没有。我以前从未探索过tf源代码,但是从API页面链接到源代码的链接似乎并没有链接到损失函数的实际实现。


也许可以将这个适应于分割。我对Python和Keras都不太熟悉,无法做到这一点。也许它已经可以工作了(?),但是当我传递一个带有类权重的数组时,会出现“维度问题”。 - Manuel Popp
3个回答

2
我认为this是解决在Keras中加权稀疏分类交叉熵的方案。他们使用以下方法将“第二个掩码”(包含掩码图像每个类别的权重)添加到数据集中。
def add_sample_weights(image, label):
  # The weights for each class, with the constraint that:
  #     sum(class_weights) == 1.0
  class_weights = tf.constant([2.0, 2.0, 1.0])
  class_weights = class_weights/tf.reduce_sum(class_weights)

  # Create an image of `sample_weights` by using the label at each pixel as an 
  # index into the `class weights` .
  sample_weights = tf.gather(class_weights, indices=tf.cast(label, tf.int32))

  return image, label, sample_weights


train_dataset.map(add_sample_weights).element_spec

然后他们只需使用tf.keras.losses.SparseCategoricalCrossentropy作为损失函数,并像这样进行拟合:

weighted_model.fit(
    train_dataset.map(add_sample_weights),
    epochs=1,
    steps_per_epoch=10)

1

看起来 Keras 稀疏分类交叉熵不支持类别权重。我找到了 this 适用于 Keras 的稀疏分类交叉熵损失函数实现,对我来说很有效。链接中的实现存在一些小问题,可能是由于某些版本不兼容,所以我已经修复了它。

    import tensorflow as tf
    from tensorflow import keras

    class WeightedSCCE(keras.losses.Loss):
        def __init__(self, class_weight, from_logits=False, name='weighted_scce'):
            if class_weight is None or all(v == 1. for v in class_weight):
                self.class_weight = None
            else:
                self.class_weight = tf.convert_to_tensor(class_weight,
                    dtype=tf.float32)
            self.name = name
            self.reduction = keras.losses.Reduction.NONE
            self.unreduced_scce = keras.losses.SparseCategoricalCrossentropy(
                from_logits=from_logits, name=name,
                reduction=self.reduction)
    
        def __call__(self, y_true, y_pred, sample_weight=None):
            loss = self.unreduced_scce(y_true, y_pred, sample_weight)
            if self.class_weight is not None:
                weight_mask = tf.gather(self.class_weight, y_true)
                loss = tf.math.multiply(loss, weight_mask)
            return loss

应该通过将权重列表或数组作为参数来调用损失。


1
据我所知,您可以在model.fit中使用class weights来处理任何损失函数。我已经在categorical_cross_entropy中使用过它,并且它可以正常工作。它只是使用类权重对损失进行加权,因此我认为它也可以用于sparse_categorical_cross_entropy。

在使用fit时,它会提示class_weight不支持3维以上的目标。 - Vitto
不适用于分割。他们甚至在GitHub上有一个weighted_sparse_categorical_crossentropy损失函数(链接:https://github.com/tensorflow/models/blob/master/official/nlp/modeling/losses/weighted_sparse_categorical_crossentropy.py)。然而,它不起作用。```tf.keras```期望标签为整数值,而不是分割掩码。无论如何,我只是在希望有一天能得到这个问题的答案,因为我也急需这个用于语义分割的损失函数。 - Manuel Popp
显然,sparse_categorical_crossentropy和weighted_categorical_crossentropy在内部的行为非常不同。我发现,在使用scce时添加class_weights是无效的,正如@Vitto所指出的那样。 - Shep Bryan

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