当目标不是一个热点时,如何计算 Pytorch 中两个张量之间的正确交叉熵?

问题描述

我对 Pytorch 中交叉熵的计算感到困惑。如果我想计算 2 张量之间的交叉熵,并且目标张量不是单热标签,我应该使用哪个损失?计算2个概率分布之间的交叉熵而不是预测结果和确定的one-hot标签间的交叉熵是很常见的。

基本损失函数 CrossEntropyLoss 强制目标为索引整数,在这种情况下不符合条件。 bceloss 似乎有效,但它产生了意想不到的结果。计算交叉熵的预期公式为

enter image description here

但是bceloss计算每个维度的BCE,表示为

-yi*log(pi)-(1-yi)*log(1-pi)

与第一个等式相比,不应涉及项-(1-yi)*log(1-pi)。这是一个使用 bceloss 的示例,我们可以看到每个维度的结果都涉及第二项。这使得结果与正确的结果不同。

import torch.nn as nn
import torch
from math import log

a = torch.Tensor([0.1,0.2,0.7])
y = torch.Tensor([0.2,0.6])
L = nn.bceloss(reduction='none')
y1 = -0.2 * log(0.1) - 0.8 * log(0.9)
print(L(a,y))
print(y1)

结果是

tensor([0.5448,0.5004,0.6956])
0.5448054311250702

如果我们将所有维度的结果相加,则最终的交叉熵与预期的不一致。因为这些维度中的每一个都涉及 -(1-yi)*log(1-pi) 项。相比之下,Tensorflow 可以使用 CategoricalCrossentropy 计算正确的交叉熵值。这是具有相同设置的示例,我们可以看到交叉熵的计算方式与第一个公式相同。

import tensorflow as tf
from math import log
L = tf.losses.CategoricalCrossentropy()
a = tf.convert_to_tensor([0.1,0.7])
y = tf.convert_to_tensor([0.2,0.6])
y_ = -0.2* log(0.1) - 0.2 * log(0.2) - 0.6 * log(0.7)

print(L(y,a),y_)
tf.Tensor(0.9964096,shape=(),dtype=float32) 0.9964095674488687

Pytorch 中是否有任何函数可以使用第一个公式计算正确的交叉熵,就像 Tensorflow 中的 CategoricalCrossentropy 一样?

解决方法

根本问题是您错误地使用了 BCELoss 函数。

交叉熵损失就是你想要的。它用于计算两个任意概率分布之间的损失。事实上,它的定义正是您提供的等式:

enter image description here

其中 p 是目标分布,q 是您的预测分布。有关详细信息,请参阅 this StackOverflow post

在您提供线路的示例中

y = tf.convert_to_tensor([0.2,0.2,0.6])

您正在隐式建模一个多类分类问题,其中目标类可以是三个类之一(张量的长度)。更具体地说,那句话是说对于这个数据实例,类 0 的概率为 0.2,类 1 的概率为 0.2,类 2 的概率为 0.6。

您遇到的问题是 PyTorch 的 BCELoss 计算了 binary 交叉熵损失,其公式不同。二元交叉熵损失计算目标类只能为 0 或 1 的分类问题的交叉熵。

在二元交叉熵中,你只需要一个概率,例如0.2,意味着实例属于类 1 的概率是 0.2。相应地,类别 0 的概率为 0.8。

如果你给 BCELoss 提供相同的张量 [0.2,0.6],你正在建模这样一种情况,其中有三个数据实例,其中数据实例 0 属于类 1 的概率为 0.2,数据实例 1 属于类的概率为 0.2 1,数据实例 2 属于类 1 的概率为 0.6。

现在,对于您最初的问题:

如果我想计算 2 张量之间的交叉熵,并且目标张量不是单热标签,我应该使用哪个损失?

不幸的是,PyTorch 没有接受两个概率分布的交叉熵函数。看到这个问题: https://discuss.pytorch.org/t/how-should-i-implement-cross-entropy-loss-with-continuous-target-outputs/10720

建议使用其方程定义来实现您自己的函数。这是有效的代码:

def cross_entropy(input,target):
    return torch.mean(-torch.sum(target * torch.log(input),1))


y = torch.Tensor([[0.2,0.6]])
yhat = torch.Tensor([[0.1,0.7]])
cross_entropy(yhat,y)
# tensor(0.9964)

它提供了您想要的答案。

,

也许你应该试试 torch.nn.CrossEntropyLoss 函数