假设在对用于分割的图像进行中值频率平衡后,我们得到了以下类别权重:
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标签或预测值来广播这些值。我的实现中的一些数学运算如下:
-
标签和logits的形状都是
[batch_size, height, width, num_classes]
-
权重掩码的形状是
[batch_size, height, width, 1]
-
权重掩码被广播到logit的softmax和标签相乘的
num_classes
个通道数上,得到的输出形状为[batch_size, height, width, num_classes]
。在这种情况下,num_classes
是12。 -
对批次中的每个示例进行归约求和,然后对一个批次中的所有示例进行归约求平均,以获得单一标量值的损失。
在这种情况下,我们应该根据预测值还是ground_truth来创建权重掩码?
如果我们根据ground_truth构建它,那么这意味着无论预测的像素标签是什么,它们都会根据实际类别的标签受到惩罚,这似乎无法合理地指导训练过程。
但如果我们根据预测值构建它,那么对于生成的任何logit预测,如果预测的标签(通过对logit进行argmax得到)占主导地位,那么该像素的所有logit值都将显著减少。
–> 尽管这意味着最大logit仍然是最大值,因为所有12个通道的logit都会按相同的值缩放,但预测的标签的最终softmax概率(缩放前后相同)将低于缩放前(我做了一些简单的数学计算来估计)。–> 预测的损失较低
但问题在于:如果由于这种加权导致预测的损失较低,那么这不是与预测主导标签应该产生更大损失的想法相矛盾吗?
我对这种方法的总体印象是:
- 对于主导标签,它们受到惩罚且奖励较少。
- 对于不太主导的标签,如果预测正确,它们会得到高度奖励,但如果预测错误,它们也会受到重罚。
那么,这如何帮助解决类别平衡问题?我不太理解这里的逻辑。
实现
这是我当前计算加权交叉熵损失的实现,尽管我不确定它是否正确。
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))