【问题标题】:TensorFlow: Implementing a class-wise weighted cross entropy loss?TensorFlow:实现分类加权交叉熵损失?
【发布时间】:2017-11-11 06:10:14
【问题描述】:

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

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 以便它可以乘以两个类的交叉熵输出。要创建此权重掩码,我们可以根据 ground_truth 标签或预测广播值。我的实现中的一些数学:

  1. label 和 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 构建它,那么这意味着无论预测的像素标签是什么,它们都会根据类的实际标签受到惩罚,这似乎并不能以一种明智的方式指导训练。

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

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

但问题是这样的:如果由于这种加权而预测出较低的损失,那么这不会与预测主导标签应该给您带来更大损失的想法相矛盾吗? p>

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

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

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


实施

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

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)

谁能验证我的这种加权损失的概念是否正确,以及我的实现是否正确?这是我第一次了解具有不平衡类的数据集,如果有人能验证这一点,我将不胜感激。

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


类似的线程

请注意,我在这里查看过类似的帖子:How can I implement a weighted cross entropy loss in tensorflow using sparse_softmax_cross_entropy_with_logits

但似乎 TF 对损失只有样本加权,而没有分类加权。

非常感谢大家。

【问题讨论】:

  • "如果我们基于ground_truth构建它,那么这意味着无论预测的像素标签是什么,它们都会根据类的实际标签进行惩罚,这似乎并不能指导以明智的方式进行训练。”这是为什么呢?
  • 意思是说如果某个像素 [x,y] 应该被标记为 1,但预测可以是从 0 到 11 的任何值,那么无论该标签给出什么预测,无论它是什么 logit 预测,应用于 logit 的特定像素的缩放比例都是相同的。考虑到我们想要自适应地惩罚预测的标签,我认为这会很奇怪。您对此有一些见解吗?

标签: machine-learning tensorflow computer-vision deep-learning image-segmentation


【解决方案1】:

这是我自己在 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 列表,其权重索引与 one-hot 向量中相应类的索引匹配。我将权重存储为泡菜文件,以避免重新计算它们。它是对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))

【讨论】:

  • 嗯,我知道你输入的形状是什么?这个函数适用于 4 维的情况吗?实际上对于权重计算,我也不是很确定,所以我猜它可能是基于预测或基本事实。您是否有任何进一步的参考资料,我可以阅读以了解为什么它应该基于基本事实?另外,你知道中频平衡的任何实现吗?
  • 重要的是输出。输入是形状 (1024, 512, 3) 的 RGB 图像,输出是形状 (1024, 512, 1) 的注释。该函数应该适用于任何等级的输出。
  • 我相信,如果您正在处理一批图像,也就是排名第 4 的图像,您也应该在最后使用 reduce mean。就您而言,您的课程只有 1 还是 0?我一直认为对于多标签像素分类,输出应该有 num classes 通道。我不太明白目标 * 权重的广播如何代替目标是排名 4,但权重只是排名 1 - 每个像素如何知道要分配什么权重?
  • 抱歉,输出实际上是一个单热向量的二维矩阵,形状为 (1024, 512, 34)。有34个班。您可以使用 reduce_mean 或 reduce_sum,它们会改变损失的大小,但不会改变梯度。查看交叉熵的定义 - 如果我们的真实概率分布是(例如)[0,1,0] 并且我们的预测是 [0.1, 0.7, 0.2],则 log(0.1) 和 log(0.2) 乘以零,因此不会造成损失。只有对真实类别的预测有贡献。至于广播,我在答案中添加了一些示例 numpy 代码供您使用。
  • 你是对的。 numpy 广播(实际上是 tf 广播)可以像您的实现一样简单得多。我想知道,我应该先将 logits 与类权重相乘,还是应该将类权重与 softmaxed logits 相乘?将类权重乘以 softmax 层的原因是什么?另外,你知道计算班级权重的一些传统方法是什么吗?我并不特别了解中频平衡的工作原理,但网上没有太多关于它的文档(这是一个相当新的想法)。
猜你喜欢
  • 2018-05-31
  • 2019-08-08
  • 2017-12-26
  • 2019-06-19
  • 2020-03-03
  • 2021-03-19
  • 2021-02-03
  • 2018-04-12
  • 2021-03-19
相关资源
最近更新 更多