如何在Keras中实现损失函数的像素级加权?

时间:2018-12-29 20:02:50

标签: tensorflow keras deep-learning

我正在使用Keras 2.2.4,并且正在尝试实现here中所述的按像素分类的损失函数,但是这里遇到了一些困难。我正在执行3D分割,因此我的目标向量是(b_size, width_x, width_y, width_z, nb_classes)。我实现了以下损失函数,其中权重图与目标和预测矢量的形状相同:

def dice_xent_loss(y_true, y_pred, weight_map):

    """Adaptation of https://arxiv.org/pdf/1809.10486.pdf for multilabel 
    classification with overlapping pixels between classes. Dec 2018.
    """
    loss_dice = weighted_dice(y_true, y_pred, weight_map)
    loss_xent = weighted_binary_crossentropy(y_true, y_pred, weight_map)

    return loss_dice + loss_xent

def weighted_binary_crossentropy(y_true, y_pred, weight_map):
    return tf.reduce_mean((K.binary_crossentropy(y_true, 
                                                 y_pred)*weight_map)) / (tf.reduce_sum(weight_map) + K.epsilon())

def weighted_dice(y_true, y_pred, weight_map):

    if weight_map is None:
        raise ValueError("Weight map cannot be None")
    if y_true.shape != weight_map.shape:
        raise ValueError("Weight map must be the same size as target vector")

    dice_numerator = 2.0 * K.sum(y_pred * y_true * weight_map, axis=[1,2,3])
    dice_denominator = K.sum(weight_map * y_true, axis=[1,2,3]) + \
                                                             K.sum(y_pred * weight_map, axis=[1,2,3])
    loss_dice = (dice_numerator) / (dice_denominator + K.epsilon())
    h1=tf.square(tf.minimum(0.1,loss_dice)*10-1)
    h2=tf.square(tf.minimum(0.01,loss_dice)*100-1)
    return 1.0 - tf.reduce_mean(loss_dice) + \
            tf.reduce_mean(h1)*10 + \
            tf.reduce_mean(h2)*10

我按照建议使用sample_weights=temporal编译模型,并将权重作为model.fit传递给sample_weight=weights。仍然出现以下错误:

File "overfit_one_case.py", line 153, in <module>
    main()
File "overfit_one_case.py", line 81, in main
   sample_weight_mode="temporal")
 File "/home/igt/anaconda2/envs/niftynet/lib/python2.7/site-packages/keras/engine/training.py", line 342, in compile
sample_weight, mask)
File "/home/igt/anaconda2/envs/niftynet/lib/python2.7/site-packages/keras/engine/training_utils.py", line 404, in weighted
score_array = fn(y_true, y_pred)
TypeError: dice_xent_loss() takes exactly 3 arguments (2 given)

training_utils.py中,Keras称我的自定义损失没有任何权重。关于如何解决这个问题的任何想法?我的另一个限制是我正在尝试在此特定模型上进行迁移学习。因此,我无法按照建议的hereweight_map添加到Input层。

1 个答案:

答案 0 :(得分:1)

样本权重是样本的权重,而不是像素的权重。

y_truey_pred之外,克拉斯损失从未接受任何其他论点。所有keras加权都是自动的。

对于自定义权重,您需要自己实现。您可以创建包裹在需要权重的函数中的这些损失函数,如下所示:

def weighted_dice_xent_loss(weight_map):

    def dice_xent_loss(y_true, y_pred):
        #code...    
        return loss_dice + loss_xent
    return dice_xent_loss

def weighted_binary_crossentropy(weight_map):
    def inner_binary_crossentropy(y_true, y_pred):
        return tf.reduce_mean(
           (K.binary_crossentropy(y_true, y_pred)*weight_map)) / (
            tf.reduce_sum(weight_map) + K.epsilon())
        return inner_binnary_crossentropy

def weighted_dice(weight_map):
    def dice(y_true, y_pred):

    #code....
        return 1.0 - tf.reduce_mean(loss_dice) + \
            tf.reduce_mean(h1)*10 + \
            tf.reduce_mean(h2)*10
   return dice

例如,将它们用作loss=weighted_dice_xent_loss(weight_map)


使用样本权重的丑陋解决方法。

如果每个样本的权重都是唯一的,则必须将每个像素变成一个样本(这很不正常)。

使用您的数据:

  • 展平数据的第一个维度,例如(b_size * width_x * width_y * width_z, nb_channels)
  • 以相同的方式放平体重矩阵。
  • 以相同的方式铺平真实输出

使用您的模型:

  • 创建兼容的`inputs = Input((nb_channels,))
  • 使用LambdaK.reshape图层中重塑形状以恢复原始尺寸:K.reshape(x, (-1, width_x, width_y, width_z, nb_classes))
  • 使模型的其余部分照常
  • 使用LambdaK.reshape(x, (-1, nb_classes))层中重塑输出

您的损失:

  • 计算每像素损失,不求和。
  • 计算损失后,将对Keras权重求和(因此它将与骰子不兼容)