加权分类交叉熵语义分割

3

我想使用类似U-Net的FCN来进行一些语义分割。我使用基于Tensorflow后端的Python和Keras进行了操作。现在我有了很好的结果,我正在试图改进它们,并且我认为改善我的损失计算是实现这样一件事情的一种方式。

我知道在我的输出中,多个类别存在不平衡的情况,使用默认的 categorical_crossentropy 函数可能会带来问题。

我的模型输入和输出都是float32格式的,输入是channel_first,输出是channel_last(模型末尾完成置换)

在二元案例中,当我只想分割一个类时,我已经按以下方式更改了损失函数,以便可以根据输出内容逐个添加权重:

def weighted_loss(y_true, y_pred):
    def weighted_binary_cross_entropy(y_true, y_pred):
        w = tf.reduce_sum(y_true)/tf_cast(tf_size(y_true), tf_float32)
        real_th = 0.5-th 
        tf_th = tf.fill(tf.shape(y_pred), real_th) 
        tf_zeros = tf.fill(tf.shape(y_pred), 0.)
        return (1.0 - w) * y_true * - tf.log(tf.maximum(tf.zeros, tf.sigmoid(y_pred) + tf_th)) +
               (1- y_true) * w * -tf.log(1 - tf.maximum(tf_zeros, tf.sigmoid(y_pred) + tf_th))
    return weighted_binary_coss_entropy

请注意,th是激活阈值,默认值为1/nClasses,我已更改它以查看哪个值可以给我最佳结果。你对此有何看法?如果在多类情况下能够计算加权分类交叉熵,那么怎么样修改呢?
1个回答

2

你的实现适用于二元分类,对于多类分类来说,它只是

  -y_true * tf.log(tf.sigmoid(y_pred)) 

使用内置的tensorflow方法计算分类熵,因为它可以避免y_pred<0时的溢出问题。

您可以查看此答案:不平衡数据和加权交叉熵,它解释了加权分类交叉熵的实现。

对于categorical_crossentropy的唯一更改将是:

def weighted_loss(y_true, y_pred):
    def weighted_categorical_cross_entropy(y_true, y_pred):
        w = tf.reduce_sum(y_true)/tf_cast(tf_size(y_true), tf_float32)
        loss = w * tf.nn.softmax_cross_entropy_with_logits(onehot_labels, logits)
        return loss
    return weighted_categorical_cross_entropy

提取每个类别的预测结果。
def loss(y_true, y_pred):
    s = tf.shape(y_true)

    # if number of output classes  is at last
    number_classses = s[-1]

    # this will give you one hot code for your prediction
    clf_pred = tf.one_hot(tf.argmax(y_pred, axis=-1), depth=number_classses, axis=-1)

    # extract the values of y_pred where y_pred is max among the classes
    prediction = tf.where(tf.equal(clf_pred, 1), y_pred, tf.zeros_like(y_pred))

    # if one hotcode == 1 then class1_prediction == y_pred  else class1_prediction ==0
    class1_prediction = prediction[:, :, :, 0:1]
    # you can compute your loss here on individual class and return the loss ,just for simplicity i am returning the class1_prediction
    return class1_prediction

模型输出

y_pred = [[[[0.5, 0.3, 0.7],
   [0.6, 0.3, 0.2]]
,
  [[0.7, 0.9, 0.6],
   [0.3 ,0.9, 0.3]]]]

相应的真实情况

y_true =  [[[[0,  1, 0],
   [1 ,0, 0]]
,
  [[1,0 , 0],
   [0,1, 0]]]]

预测类别1

prediction = loss(y_true, y_pred)
# prediction =  [[[[0. ],[0.6]],[0. ],[0. ]]]]

我同意你的观点,但我不想将权重作为先验知识,我希望它们在训练期间直接计算,因为权重可能在训练集中的每个样本之间有很大的差异。 - Fou
这就是为什么我在发布的代码中使用了tensorflow的reduce_sum函数来计算每个案例的权重。 - Fou
你可以对你的代码做同样的事情,检查我的修改后的答案。 - thefifthjack005
我已经进行了更改,看起来仍然能够得到可接受的结果,但我不知道我所做的是否正确。顺便说一下,我有一个自定义的 softmax_cross_entropy_with_logits 函数,它包含激活阈值,默认情况下为 1/nClasses(我猜)。 - Fou
我已经修改了代码,以便在二进制情况下解释这部分内容(可能有误,但看起来工作得很好)。 - Fou
如果我理解正确的话,您想要每个类的输出,然后对该类应用单独的权重?如果是这样的话,请检查我的编辑答案。 - thefifthjack005

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