不是二进制交叉熵中的二进制地面实况标签?

Posted

技术标签:

【中文标题】不是二进制交叉熵中的二进制地面实况标签?【英文标题】:Not binary ground truth labels in binary crossentropy? 【发布时间】:2020-08-27 12:21:10 【问题描述】:

对二元交叉熵不使用二元基本真值是否有意义?有什么正式的证明吗?

看起来像在实践中使用:例如在https://blog.keras.io/building-autoencoders-in-keras.html 中,即 mnist 图像不是二进制图像,而是灰色图像。

这里是代码示例:

1.正常情况:

def test_1():
    print('-'*60)

    y_pred = np.array([0.5, 0.5])
    y_pred = np.expand_dims(y_pred, axis=0)
    y_true = np.array([0.0, 1.0])
    y_true = np.expand_dims(y_true, axis=0)

    loss = keras.losses.binary_crossentropy(
        K.variable(y_true),
        K.variable(y_pred)
    )

    print("K.eval(loss):", K.eval(loss))

输出:

K.eval(loss): [0.6931472]

2.非二元真实值情况:

def test_2():
    print('-'*60)

    y_pred = np.array([0.0, 1.0])
    y_pred = np.expand_dims(y_pred, axis=0)
    y_true = np.array([0.5, 0.5])
    y_true = np.expand_dims(y_true, axis=0)

    loss = keras.losses.binary_crossentropy(
        K.variable(y_true),
        K.variable(y_pred)
    )

    print("K.eval(loss):", K.eval(loss))

输出:

K.eval(loss): [8.01512]

3.[0,1]范围外的真值:

def test_3():
    print('-'*60)

    y_pred = np.array([0.5, 0.5])
    y_pred = np.expand_dims(y_pred, axis=0)
    y_true = np.array([-2.0, 2.0])
    y_true = np.expand_dims(y_true, axis=0)

    loss = keras.losses.binary_crossentropy(
        K.variable(y_true),
        K.variable(y_pred)
    )

    print("K.eval(loss):", K.eval(loss))

输出:

K.eval(loss): [0.6931472]

由于某种原因,test_1test_3 中的丢失是相同的,可能是因为将 [-2, 2] 裁剪为 [0, 1] 但我在 Keras 代码中看不到裁剪代码。 同样有趣的是,test_1test_2 的损失值有很大差异,但在第一种情况下,我们有 [0.5, 0.5] 和 [0.0, 1.0],在第二种情况下,我们有 [0.0, 1.0] 和 [0.5, 0.5] ],这是相同的值,但顺序相反。

在 Keras 中 binary_crossentropy 定义为:

def binary_crossentropy(y_true, y_pred):
    return K.mean(K.binary_crossentropy(y_true, y_pred), axis=-1)


def binary_crossentropy(target, output, from_logits=False):
    """Binary crossentropy between an output tensor and a target tensor.

    # Arguments
        target: A tensor with the same shape as `output`.
        output: A tensor.
        from_logits: Whether `output` is expected to be a logits tensor.
            By default, we consider that `output`
            encodes a probability distribution.

    # Returns
        A tensor.
    """
    # Note: tf.nn.sigmoid_cross_entropy_with_logits
    # expects logits, Keras expects probabilities.
    if not from_logits:
        # transform back to logits
        _epsilon = _to_tensor(epsilon(), output.dtype.base_dtype)
        output = tf.clip_by_value(output, _epsilon, 1 - _epsilon)
        output = tf.log(output / (1 - output))

    return tf.nn.sigmoid_cross_entropy_with_logits(labels=target,
                                                   logits=output)

【问题讨论】:

【参考方案1】:

是的,它“有道理”,因为交叉熵是概率分布之间差异的度量。也就是说,any 分布(当然是在相同的样本空间上)——目标分布是 one-hot 的情况实际上只是一种特殊情况,尽管它在机器学习中的使用频率很高。

一般来说,如果p 是您的真实分布并且q 是您的模型,则q = p 的交叉熵最小化。因此,使用交叉熵作为损失将鼓励模型向目标分布收敛。

关于情况1和2的区别:交叉熵不是对称的。它实际上等于真实分布p 的熵加上pq 之间的KL 散度。这意味着p 更接近均匀(更少“单热”),它通常会更大,因为这样的分布具有更高的熵(我认为 KL 散度也会不同,因为它不是对称的)。

至于案例3:这实际上是使用0.5作为output的神器。事实证明,在交叉熵公式中,项将完全以这样一种方式抵消,即无论标签如何,您总是得到相同的结果 (log(2))。当您使用输出时,这将改变!= 0.5;在这种情况下,不同的标签会给你不同的交叉熵。例如:

output 0.3, target 2.0 给出 2.0512707 的交叉熵 output 0.3, target -2.0 给出 -1.3379208 的交叉熵

第二种情况实际上给出了一个负输出,这是没有意义的。恕我直言,该函数允许 [0,1] 范围之外的目标是一个疏忽;这应该会导致崩溃。交叉熵公式工作得很好,但结果毫无意义。

我还建议阅读 the wikipedia article 关于交叉熵。它很短,包含一些有用的信息。

【讨论】:

以上是关于不是二进制交叉熵中的二进制地面实况标签?的主要内容,如果未能解决你的问题,请参考以下文章

地面实况图像到一个热编码阵列(语义分割)

np.dot 和 np.multiply 与 np.sum 在二进制交叉熵损失计算中的区别

Softmax 的交叉熵是不是适用于多标签分类?

具有对数损失的 TensorFlow 单 sigmoid 输出与具有稀疏 softmax 交叉熵损失的两个线性输出,用于二进制分类

自定义 keras 损失函数二元交叉熵给出不正确的结果

使用交叉熵时如何处理 log(0)