TensorFlow: 实现类别加权交叉熵损失?

假设在对用于分割的图像进行中值频率平衡后,我们得到了以下类别权重:

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}

我们的目标是创建一个权重掩码,使其可以与两个类别的交叉熵输出相乘。为了创建这个权重掩码,我们可以根据ground_truth标签或预测值来广播这些值。我的实现中的一些数学运算如下:

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

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

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

  4. 对批次中的每个示例进行归约求和,然后对一个批次中的所有示例进行归约求平均,以获得单一标量值的损失。

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

如果我们根据ground_truth构建它,那么这意味着无论预测的像素标签是什么,它们都会根据实际类别的标签受到惩罚,这似乎无法合理地指导训练过程。

但如果我们根据预测值构建它,那么对于生成的任何logit预测,如果预测的标签(通过对logit进行argmax得到)占主导地位,那么该像素的所有logit值都将显著减少。

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

但问题在于:如果由于这种加权导致预测的损失较低,那么这不是与预测主导标签应该产生更大损失的想法相矛盾吗?

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

  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)    #获取logit标签预测,并形成与其形状相同的权重掩码骨架    logit_predictions = tf.argmax(logits, -1)     weight_mask = tf.zeros_like(logit_predictions, dtype=tf.float32)    #获取要添加到权重掩码中的类别权重数量    num_classes = logits.get_shape().as_list()[3]    #为每个像素预测形成权重掩码映射    for i in xrange(num_classes):        binary_mask = tf.equal(logit_predictions, i) #获取logits预测中类别i预测的位置        binary_mask = tf.cast(binary_mask, tf.float32) #将布尔值转换为1和0        class_mask = tf.multiply(binary_mask, class_weights[i]) #将binary_mask中的1乘以特定的class_weight        weight_mask = tf.add(weight_mask, class_mask) #添加到权重掩码中    #根据权重掩码缩放logits,然后执行交叉熵    weight_mask = tf.expand_dims(weight_mask, 3) #将第四维扩展到1以进行广播    logits_scaled = tf.multiply(logits, weight_mask)    return tf.losses.softmax_cross_entropy(onehot_labels=onehot_labels, logits=logits_scaled)

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

测试结果: 经过一些测试后,我发现上面的实现导致了更大的损失。这是应该的情况吗?即,这会使训练变得更难,但最终产生更准确的模型吗?


相似主题

请注意,我已经在这里检查了一个类似的主题:如何在TensorFlow中使用sparse_softmax_cross_entropy_with_logits实现加权交叉熵损失

但看起来TensorFlow只有基于样本的损失加权,而没有基于类别的加权。

非常感谢大家。


回答:

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

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向量中相应的类别匹配。我将权重存储为pickle文件,以避免重新计算。 这是对Keras分类交叉熵损失函数的改编。第一行只是剪切值,以确保我们永远不会取0的对数。

我不确定为什么有人会使用预测值而不是ground_truth来计算权重;如果你提供进一步的解释,我可以根据你的反馈更新我的答案。

编辑:玩弄这个numpy代码来理解这是如何工作的。同时回顾交叉熵的定义。

import numpy as npweights = [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))

Related Posts

Keras Dense层输入未被展平

这是我的测试代码: from keras import…

无法将分类变量输入随机森林

我有10个分类变量和3个数值变量。我在分割后直接将它们…

如何在Keras中对每个输出应用Sigmoid函数?

这是我代码的一部分。 model = Sequenti…

如何选择类概率的最佳阈值?

我的神经网络输出是一个用于多标签分类的预测类概率表: …

在Keras中使用深度学习得到不同的结果

我按照一个教程使用Keras中的深度神经网络进行文本分…

‘MatMul’操作的输入’b’类型为float32,与参数’a’的类型float64不匹配

我写了一个简单的TensorFlow代码,但不断遇到T…

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注