能把交叉熵(Cross-Entropy)和负对数似然(Negative Log-Likelihood)分得清吗

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第30天,点击查看活动详情

Cross-Entropy 和 Negative Log-Likelihood 是一个

直到最近看了一个自己大家神经网络框架的视频,其中涉及到 pytorch 中关于 cross-Entropy 和 Negative Log-Likelihood 的区别,才知道他们原来并不是同一个概念的两个名称。而的的确确是不同的两个 loss 函数。

从其背后的数学谈起

最大似然估计(Maximum Likelihood Estimation)

首先来考虑一个二分类问题,在给定模型 f θ ( x i ) f_{\theta}(x_i) 其中 θ \theta 为参数,目标是求解观察到数据的极大似然(Maximum likelihood)的参数 θ \theta

y ^ θ , i = σ ( f θ ( x ) ) \hat{y}_{\theta,i} = \sigma(f_{\theta}(x))

这里 y ^ \hat{y} 表示预测为正样本的概率, σ \sigma 是一个可将 inf , inf - \inf , \inf 映射到 [ 0 , 1 ] [0,1] 的非线性函数,通常都会选择 sigmoid 作为激活函数来使用。

σ ( z ) = 1 1 + e x \sigma(z) = \frac{1}{1 + e^{-x}}
P ( D θ ) = i = 1 n y ^ θ , i y i ( 1 y ^ θ , i ) 1 y i P(\cal{D}|\theta) = \prod_{i=1}^n \hat{y}_{\theta,i}^{y_i} (1- \hat{y}_{\theta,i})^{1 - y^i}

对数似然(log-Likehood)

在大多数情况下只关心对于正确标签预测的结果有多好,

log P ( D θ ) = i = 1 n ( ( y i log ( y ^ θ , i ) + ( 1 y i ) log ( 1 y ^ θ , i ) ) \log P(\cal{D}|\theta) = \sum_{i=1}^n \left( (y_i \log(\hat{y}_{\theta,i}) +(1-y_i)\log(1- \hat{y}_{\theta,i}) \right)

在二分类问题中, y y 的取值为 1 或者 0,这里用 1 和 0 分别表示两个不同的类别。

  • y ^ θ , i \hat{y}_{\theta,i} 表示将第 i 数据预测为正例样本的概率
  • 1 y ^ θ , i 1 - \hat{y}_{\theta,i} 表示将第 i 数据预测为负例样本的概率

这里所谓正例和负例样本其实就是两个类别,如果我们把为某一个类别设计正例,那么另一个类别就属于负例

print(f"{'Setting up binary case':-^80}")
z = torch.randn(5)
yhat = torch.sigmoid(z)
y = torch.Tensor([0, 1, 1, 0, 1])
print(f"z= {z}\nyhat= {yhat}\ny= {y}\n{'':-^80}")
z= tensor([ 0.2590, -1.0297, -0.5008, 0.2734, -0.9181]) 
yhat= tensor([0.5644, 0.2631, 0.3774, 0.5679, 0.2853]) 
y= tensor([0., 1., 1., 0., 1.]) 

基于正态分布随机生成 5 个,表示 5 样本预测为正例的值,接下来用 sigmoid 函数来将这些值映射到 0 到 1 取值范围,接下我们创建一个标注

最小化负对数似然(Negative Log Likelihood)

因为对数(log)是一个单调函数,随意最最大似然就等同于求最大对数似然。在深度学习,我们通常做法是让损失函数值最小为目标,随意这里对数似然函数前面添加一个负号,让求最大对数似然变为最小化负的对数似然

l ( θ ) = i = 1 n ( ( y i log ( y ^ θ , i ) + ( 1 y i ) log ( 1 y ^ θ , i ) ) l(\theta) = -\sum_{i=1}^n \left( (y_i \log(\hat{y}_{\theta,i}) +(1-y_i)\log(1- \hat{y}_{\theta,i}) \right)

这里简单地总结一下,我们最初是找到这么 θ \theta 让概率分布出现我们观察数据可能性最大,也就是极大似然估计方法,现在最小化负对数的似然其实并没有背离当初的目标,只是为了计算巧妙地进行目标变换而已。

l = -(y * yhat.log() + (1 - y) * (1 - yhat).log())
print(f"{l}")

这是负对数似然实现

交叉熵

交叉熵用于衡量两个离散概率分布之间相似性,如果假设概率分别为 q 和 p,那么交叉熵概率公式如下

H ( p , q ) = x X p ( x ) log q ( x ) H(p,q) = - \sum_{x \in \cal{X}}p(x)\log q(x)

从公式来看这个交叉熵的公式与负对数似然非常相似,其中 p ( x ) p(x) 对应于真实标注,对于二分类问题取值为 0 或者 1,而 log 对应于预测概率的。

两种损失之间的这种相似性引起了我最初的困惑。如果这两个损失函数是一样的,那么为什么 PyTorch 会有提供函数 CrossEntropyLoss 有提供了关于负对数似然的 NLLLoss?下面通过一个小实验 来演示一下他们之间的区别,其实在 CrossEntrypyLoss 的实现隐含包括了 softmax 激活,然后进行了对数转换,而 NLLLoss 没有包括 softmax 需要我们通过 pytorch 提供的 softmax 来进行处理。

l = -(y * yhat.log() + (1 - y) * (1 - yhat).log())
print(f"{l}")

# Observe that BCELoss and BCEWithLogitsLoss can produce the same results
l_BCELoss_nored = torch.nn.BCELoss(reduction="none")(yhat, y)
l_BCEWithLogitsLoss_nored = torch.nn.BCEWithLogitsLoss(reduction="none")(z, y)
print(f"{l_BCELoss_nored}\n{l_BCEWithLogitsLoss_nored}\n{'':=^80}")

推广到多分类

现在我们来看一看如何将负对数似然推广到多分类问题。

log P ( D θ ) = i = 1 n log ( y ^ θ , i ( y i ) ) \log P(\cal{D}|\theta) = \sum_{i=1}^n \log(\hat{y}_{\theta,i}^{(y_i)})

在多分类中,上标 y i y_i 表示对于 C 个概率分布中仅对真实类别位置所对应概率值进行求对数,例如对于二分类就可以改写成这样

y ^ θ , i ( 1 ) = y ^ θ , i y ^ θ , i ( 0 ) = 1 y ^ θ , i \hat{y}_{\theta,i}^{(1)} = \hat{y}_{\theta,i}\\ \hat{y}_{\theta,i}^{(0)} = 1 - \hat{y}_{\theta,i}

其实可以直接应用到多分类上,例如有 C 分类,那么 y 取值就是从 0 到 C-1 ,只要保证每一个 y ^ i \hat{y}_i 是一个 0 到 1 之间概率值,且求和为 1 ,也就是 y ^ i \hat{y}_i 是服从概率分布的即可。

softmax 激活函数

s o f t m a x ( z i ) = exp ( z i ) j = 1 C exp ( z j ) softmax(z_i) = \frac{\exp(z_i)}{\sum_{j=1}^C \exp(z_j)}

通过 softmax 函数可以将多维向量变为一个概率分,也就是每一个维度的数值都在 0 到 1 之间,且所有维度求和为 1。

小实验

print(f"{'Setting up multiclass case':-^80}")
z2 = torch.randn(5, 3)
yhat2 = torch.softmax(z2, dim=-1)
y2 = torch.Tensor([0, 2, 1, 1, 0]).long()
print(f"z2={z2}\nyhat2={yhat2}\ny2{y2}\n{'':-^80}")

这里基于正态概率分布生成 5 预测结果,多类别数为 3 也就是每一样会输出 3 维度向量,每一个维度对应于一个类别预测值,然后经过 softmax 激活函数将 3 维度数值变为概率值,和为 1。然后定义一个标签为 [ 0 , 2 , 1 , 1 , 0 ] [0, 2, 1, 1, 0] 对应每一个样本正确类别的索引数,

l2 = -yhat2.log()[torch.arange(5), y2] 

注意这里 yhat2 是对预测值进行了 softmax 处理过的服从概率分布的值

-torch.log_softmax(z2, dim=-1)[torch.arange(5), y2]

这里 log_softmaxsoftmaxlog 两个函数合二为一进行处理

l2_NLLLoss_nored = torch.nn.NLLLoss(reduction="none")(yhat2.log(), y2)
l2_CrossEntropyLoss_nored = torch.nn.CrossEntropyLoss(reduction="none")(z2, y2)
print(f"{l2_NLLLoss_nored}\n{l2_CrossEntropyLoss_nored}\n{'':=^80}")

小结

  • 负对数似然最小化是最大似然估计问题的一个代理问题
  • 交叉熵和负对数似然在数学公式非常相似
  • 计算负对数似然的方法,就是真实类对应对数概率相加
  • 在 pytorch 中 CrossEntropyLoss 和 NLLLoss的PyTorch 对输入要求并不相同,其中 CrossEntropyLoss 输入预测结果即可,而 NLLLoss 需要输入概率的对数

猜你喜欢

转载自juejin.im/post/7112373588007026695
今日推荐