TensorFlow:实施 class 加权交叉熵损失?

TensorFlow: Implementing a class-wise weighted cross entropy loss?

假设在对用于分割的图像执行中频平衡后,我们有这些 class 权重:

class_weights = {0: 0.2595,
                 1: 0.1826,
                 2: 4.5640,
                 3: 0.1417,
                 4: 0.9051,
                 5: 0.3826,
                 6: 9.6446,
                 7: 1.8418,
                 8: 0.6823,
                 9: 6.2478,
                 10: 7.3614,
                 11: 0.0}

想法是创建一个 weight_mask,这样它就可以乘以两个 classes 的交叉熵输出。要创建此权重掩码,我们可以根据 ground_truth 标签或预测广播值。我实现中的一些数学运算:

  1. labels 和 logits 的形状都是 [batch_size, height, width, num_classes]

  2. 权重掩码的形状是[batch_size, height, width, 1]

  3. 权重掩码被广播到 num_classes 个 logit 的 softmax 和标签之间的乘法通道,给出 [batch_size, height, width, num_classes] 的输出形状。在这种情况下,num_classes 是 12。

  4. 减少一批中每个样本的总和,然后对一批中的所有样本执行减少均值以获得损失的单个标量值。

在这种情况下,我们应该根据预测还是根据真实情况创建权重掩码?

如果我们基于 ground_truth 构建它,那么这意味着无论预测的像素标签是什么,它们都会根据 class 的实际标签受到惩罚,这不会似乎以明智的方式指导培训。

但是如果我们根据预测构建它,那么对于生成的任何 logit 预测,如果预测标签(来自 logit 的 argmax)占主导地位,那么该像素的 logit 值将全部为显着减少。

--> 虽然这意味着最大 logit 仍然是最大值,因为 12 个通道中的所有 logit 都将按相同的值缩放,但预测标签的最终 softmax 概率(仍然相同缩放前后),将低于缩放前(做了一些简单的数学估计)。 --> 预测损失较低

但问题是这样的:如果由于这种加权而预测到较低的损失,那么它是否与预测主导标签应该给你更大损失的想法相矛盾?

我对这种方法的总体印象是:

  1. 对于主导标签,他们受到的惩罚和奖励要少得多。
  2. 对于不太占主导地位的标签,如果预测正确,他们将获得高额奖励,但如果预测错误,他们也会受到严重惩罚。

那么这对解决 class 平衡问题有何帮助?我不太明白这里的逻辑。


实施

这是我目前计算加权交叉熵损失的实现,虽然我不确定它是否正确。

def weighted_cross_entropy(logits, onehot_labels, class_weights):
    if not logits.dtype == tf.float32:
        logits = tf.cast(logits, tf.float32)

    if not onehot_labels.dtype == tf.float32:
        onehot_labels = tf.cast(onehot_labels, tf.float32)

    #Obtain the logit label predictions and form a skeleton weight mask with the same shape as it
    logit_predictions = tf.argmax(logits, -1) 
    weight_mask = tf.zeros_like(logit_predictions, dtype=tf.float32)

    #Obtain the number of class weights to add to the weight mask
    num_classes = logits.get_shape().as_list()[3]

    #Form the weight mask mapping for each pixel prediction
    for i in xrange(num_classes):
        binary_mask = tf.equal(logit_predictions, i) #Get only the positions for class i predicted in the logits prediction
        binary_mask = tf.cast(binary_mask, tf.float32) #Convert boolean to ones and zeros
        class_mask = tf.multiply(binary_mask, class_weights[i]) #Multiply only the ones in the binary mask with the specific class_weight
        weight_mask = tf.add(weight_mask, class_mask) #Add to the weight mask

    #Multiply the logits with the scaling based on the weight mask then perform cross entropy
    weight_mask = tf.expand_dims(weight_mask, 3) #Expand the fourth dimension to 1 for broadcasting
    logits_scaled = tf.multiply(logits, weight_mask)

    return tf.losses.softmax_cross_entropy(onehot_labels=onehot_labels, logits=logits_scaled)

谁能验证一下我这个加权损失的概念是否正确,我的实现是否正确?这是我第一次接触不平衡的数据集class,所以如果有人能验证这一点,我将不胜感激。

测试结果: 经过一些测试,我发现上面的实现导致了更大的损失。应该是这样吗?也就是说,这是否会使训练更加困难,但最终会产生更准确的模型?


相似线程

请注意,我在这里检查了一个类似的线程:How can I implement a weighted cross entropy loss in tensorflow using sparse_softmax_cross_entropy_with_logits

但似乎 TF 仅具有针对损失的样本权重而不是 class 权重。

非常感谢大家。

这是我自己在 Keras 中使用 TensorFlow 后端实现的:

def class_weighted_pixelwise_crossentropy(target, output):
    output = tf.clip_by_value(output, 10e-8, 1.-10e-8)
    with open('class_weights.pickle', 'rb') as f:
        weight = pickle.load(f)
    return -tf.reduce_sum(target * weight * tf.log(output))

其中 weight 只是一个标准的 Python 列表,其权重索引与单热向量中相应 class 的索引相匹配。我将权重存储为 pickle 文件以避免重新计算它们。它是 Keras categorical_crossentropy loss function 的改编版。第一行简单地裁剪值以确保我们永远不会取 0 的对数。

我不确定为什么要使用预测而不是基本事实来计算权重;如果您提供进一步的解释,我可以更新我的回答。

编辑:试用此 numpy 代码以了解其工作原理。还要查看 cross entropy.

的定义
import numpy as np

weights = [1,2]

target = np.array([ [[0.0,1.0],[1.0,0.0]],
                    [[0.0,1.0],[1.0,0.0]]])

output = np.array([ [[0.5,0.5],[0.9,0.1]],
                    [[0.9,0.1],[0.4,0.6]]])

crossentropy_matrix = -np.sum(target * np.log(output), axis=-1)
crossentropy = -np.sum(target * np.log(output))