Keras/TensorFlow 中的损失函数

Loss function in Keras/TensorFlow

我的目的是实现自定义损失函数,以在 Keras 中使用 TensorFlow 作为后端来训练模型。

损失函数

W and H represent, respectively, the width and height of the softmax layer’s output, and N is the batch size. The variable p is the probability predicted by the FCN for the correct class.

这个损失函数来自这个paper.

在此实现中,N is 4, W is 200 and H is 400。 最后一层的输出形状是(None, 400, 200, 2)。单个标签的形状是 (400, 200, 2),其中每个通道代表 class.

到目前为止,

Numpy 实现:

尽管这在这种情况下没有用,但这就是我想作为损失函数实现的。

def loss_using_np(y_true, y_pred):
    '''
    Assuming, `y_true` and `y_pred` shape is (400, 200, 2).
    This might change to (None, 400, 200, 2) while training in batch?
    '''
    dx = 0.0000000000000001 # Very small value to avoid -infinity while taking log
    y_pred = y_pred + dx
    class_one_pred = y_pred[:, :, 0]
    class_two_pred = y_pred[:, :, 1]
    class_one_mask = y_true[:, :, 0] == 1.0
    class_two_mask = y_true[:, :, 1] == 1.0
    class_one_correct_prob_sum = np.sum(np.log(class_one_pred[class_one_mask]))
    class_two_correct_prob_sum = np.sum(np.log(class_two_pred[class_two_mask]))
    N = 4
    H = 400
    W = 200
    return -1 * ((class_one_correct_prob_sum + class_two_correct_prob_sum) / ( N * H * W))

以上实现给出了预期的输出;可惜不能用

y_true = np.random.randint(2, size=(400, 200, 2))
y_pred = np.random.random((400, 200, 2))
loss_using_np(y_true, y_pred)

尝试 01 失败

import tensorflow as tf # not a good practice to not use keras.backend?
def loss_function(y_true, y_pred):
    # Not a working solution as it raises
    # ResourceExhaustedError: OOM when allocating tensor with shape[311146,3,400,2] BUT WHY?
    N = 4 # batch size
    W = 200
    H = 400
    dx = 0.0000000000000001
    y_pred = tf.add(y_pred, dx)
    class_one_gt = y_true[:,:,:,0]
    class_one_mask = tf.where(tf.equal(class_one_gt, 1.0))
    # Bad to use `tf.gather`. Issues warning,
    #`Converting sparse IndexedSlices to a dense Tensor of unknown shape.`
    class_one_prob_sum = keras.backend.sum(keras.backend.log(tf.gather(y_pred[:,:,:,0], class_one_mask)))
    class_two_gt = y_true[:,:,:,1]
    class_two_mask = tf.where(tf.equal(class_two_gt, 1.0))
    class_two_prob_sum = keras.backend.sum(keras.backend.log(tf.gather(y_pred[:,:,1], class_two_mask)))
    print("This will be printed only once; won't be printed everytime loss is callculated. How to log?")
    return -1 * ((class_one_prob_sum + class_two_prob_sum)/ (N * W * H))

尝试 02 失败?

def loss_function(y_true, y_pred):
    N = 4
    H = 400
    W = 200
    dx = tf.constant(0.0000000000000001, dtype=tf.float32)
    correct_probs = tf.boolean_mask(y_pred, tf.equal(y_true, 1.0))
    correct_probs = tf.add(correct_probs, dx)
    return (-1 * keras.backend.sum(keras.backend.log(correct_probs))) /(N * H * W)

对于此 #02 方法,我收到警告,

UserWarning: Converting sparse IndexedSlices to a dense Tensor of unknown shape. This may consume a large amount of memory.
  "Converting sparse IndexedSlices to a dense Tensor of unknown shape. "

问题

  1. 你能告诉我如何在没有任何警告的情况下实现这个损失函数吗?我不确定 #02 是正确的实现。我正在寻找优化的解决方案。非常感谢任何帮助或指点。

  2. 我试图使用 print 语句了解 loss_function() 内部发生的事情,但是,当我 compile 模型时,它们被打印了一次。有什么办法可以记录这个吗?

As mentioned by @dennis-ec, one can use tf.Print() for debugging.

旁注

我将 Keras 2.1.4TensorFlow 1.4.0-rc1Python 3.5.2 一起使用。

对我来说,作者似乎在使用普通的二元交叉熵损失来进行多标签 class化。他们也这样命名,但与您在 Keras 中实现它的方式相比,他们的定义有点奇怪。

基本上,您可以使用 binary_crossentropy 作为损失函数,并将标签作为形状数组 (400, 200, 1) 提供,其中 0 表示第一个 class,1 表示第二个 class。然后,您的网络的输出将具有相同的形状,每个输出节点都有 sigmoid 个激活函数。这就是 Keras 中通常实现语义分割模型的方式。有关示例,请参阅 this repo

# final layer, sigmoid activations
conv10 = Conv2D(1, 1, activation = 'sigmoid')(conv9)
model = Model(input = inputs, output = conv10)
# binary_crossentropy loss for multi-label classification
model.compile(optimizer = Adam(lr = 1e-4), loss = 'binary_crossentropy', metrics = ['accuracy'])

这应该给出与论文中定义的实现完全相同的结果(他们可能没有使用 Keras)。