极大似然法
考虑我们在训练一个参数为 ϕ \boldsymbol\phi ϕ、输入为 x \mathbf{x} x的模型 f [ x , ϕ ] \mathbf{f}[\mathbf{x},\boldsymbol{\phi}] f[x,ϕ]。如果转换一下视角,计算模型在给定输入 x \mathbf{x} x时对可能的输出 y \mathbf{y} y计算条件概率分布 P r ( y ∣ x ) Pr(\mathbf{y}|\mathbf{x}) Pr(y∣x)。对每一个样本 ( x i , y i ) (\mathbf{x_i},\mathbf{y_i}) (xi,yi),损失函数鼓励在分布 P r ( y i ∣ x i ) Pr(\mathbf{y_i}|\mathbf{x_i}) Pr(yi∣xi) 下输出 y i \mathbf{y_i} yi时有高的概率。
但是如何适配模型 f [ x , ϕ ] \mathbf{f}[\mathbf{x},\boldsymbol{\phi}] f[x,ϕ]去计算概率分布呢?我们首先选择一个定义在输出 y \mathbf{y} y的值域内的参数化概率分布 P r ( y ∣ θ ) Pr(\mathbf{y}|\boldsymbol{\theta}) Pr(y∣θ), 再基于训练样本让模型对概率分布的参数进行估计。于是模型对每一个训练输入 x i \mathbf{x_i} xi计算得到不同的分布参数 θ i = f [ x i , ϕ ] \boldsymbol{\theta_i} = \mathbf{f}[\mathbf{x_i}, \boldsymbol{\phi}] θi=f[xi,ϕ],其训练输出 y i \mathbf{y_i} yi在其相应的概率分布 P r ( y i ∣ θ i ) Pr(\mathbf{y_i}|\boldsymbol{\theta_i}) Pr(yi∣θi)下应该有较高的概率。所以,我们会选择使所有的 I I I个样本的组合概率最大的模型参数:
ϕ ^ = argmax ϕ [ ∏ i = 1 I Pr ( y i ∣ x i ) ] = argmax ϕ [ ∏ i = 1 I Pr ( y i ∣ θ i ) ] = argmax ϕ [ ∏ i = 1 I Pr ( y i ∣ f [ x i , ϕ ] ) ] ( 1 ) \begin{aligned} \hat{\boldsymbol{\phi}} & =\underset{\phi}{\operatorname{argmax}}\left[\prod_{i=1}^I \operatorname{Pr}\left(\mathbf{y}_i \mid \mathbf{x}_i\right)\right] \\ & =\underset{\phi}{\operatorname{argmax}}\left[\prod_{i=1}^I \operatorname{Pr}\left(\mathbf{y}_i \mid \boldsymbol{\theta}_i\right)\right] \\ & =\underset{\phi}{\operatorname{argmax}}\left[\prod_{i=1}^I \operatorname{Pr}\left(\mathbf{y}_i \mid \mathbf{f}\left[\mathbf{x}_i, \boldsymbol{\phi}\right]\right)\right] \qquad (1) \end{aligned} ϕ^=ϕargmax[i=1∏IPr(yi∣xi)]=ϕargmax[i=1∏IPr(yi∣θi)]=ϕargmax[i=1∏IPr(yi∣f[xi,ϕ])](1)
上式中的组合概率项被称为参数的似然(likelihood),上式即极大似然法(Maximum likelihood criterion),也被称为极大似然估计(Maximum likelihood estimation, MLE)。
极大似然法有两个假设,即通常所说的假设数据是独立同分布的(independent and identically distributed (i.i.d.))。即首先假设数据来自相同的数据分布,其次假设在给定输入时条件概率 P r ( y i ∣ x i ) Pr(\mathbf{y_i}|\mathbf{x_i}) Pr(yi∣xi) 是独立的,所以训练数据的总似然满足下式的分解:
P r ( y 1 , y 2 , … , y I ∣ x 1 , x 2 , … , x I ) = ∏ i = 1 I Pr ( y i ∣ x i ) ( 2 ) Pr(\mathbf{y_1}, \mathbf{y_2}, \ldots, \mathbf{y_I}|\mathbf{x_1}, \mathbf{x_2},\ldots,\mathbf{x_I}) = \prod_{i=1}^I \operatorname{Pr}\left(\mathbf{y}_i \mid \mathbf{x}_i\right) \qquad (2) Pr(y1,y2,…,yI∣x1,x2,…,xI)=i=1∏IPr(yi∣xi)(2)
但公式(1)的最大似然法不是很实用,因为每一项 P r ( y i ∣ f [ x i , ϕ ] ) Pr(\mathbf{y_i}|\mathbf{f}\left[\mathbf{x}_i, \boldsymbol{\phi}\right]) Pr(yi∣f[xi,ϕ])可能会很小,这样许多项的乘积会非常非常小,在实际计算的精度很难去量化它,所以一般会使用最大对数似然:
ϕ ^ = argmax ϕ [ ∏ i = 1 I Pr ( y i ∣ f [ x i , ϕ ] ) ] = argmax ϕ [ log [ ∏ i = 1 I Pr ( y i ∣ f [ x i , ϕ ] ) ] ] = argmax ϕ [ ∑ i = 1 I log [ Pr ( y i ∣ f [ x i , ϕ ] ) ] ] ( 3 ) \begin{aligned} \hat{\boldsymbol{\phi}} & =\underset{\phi}{\operatorname{argmax}}\left[\prod_{i=1}^I \operatorname{Pr}\left(\mathbf{y}_i \mid \mathbf{f}\left[\mathbf{x}_i, \boldsymbol{\phi}\right]\right)\right] \\ & =\underset{\phi}{\operatorname{argmax}}\left[\log \left[ \prod_{i=1}^I \operatorname{Pr}\left(\mathbf{y}_i \mid \mathbf{f}\left[\mathbf{x}_i, \boldsymbol{\phi}\right]\right) \right]\right] \\ & =\underset{\phi}{\operatorname{argmax}}\left[\sum_{i=1}^I \log \left[ \operatorname{Pr}\left(\mathbf{y}_i \mid \mathbf{f}\left[\mathbf{x}_i, \boldsymbol{\phi}\right]\right) \right]\right] \qquad (3) \end{aligned} ϕ^=ϕargmax[i=1∏IPr(yi∣f[xi,ϕ])]=ϕargmax[log[i=1∏IPr(yi∣f[xi,ϕ])]]=ϕargmax[i=1∑Ilog[Pr(yi∣f[xi,ϕ])]](3)
而通常在进行模型训练时,我们一般会最小化损失,所以会将上述的最大对数似然添加一个负号变成负对数似然法(negetive log-likelihood criterion):
ϕ ^ = argmin ϕ [ − ∑ i = 1 I log [ Pr ( y i ∣ f [ x i , ϕ ] ) ] ] = argmin ϕ [ L [ ϕ ] ] ( 4 ) \begin{aligned} \hat{\boldsymbol{\phi}} & =\underset{\phi}{\operatorname{argmin}}\left[-\sum_{i=1}^I \log \left[ \operatorname{Pr}\left(\mathbf{y}_i \mid \mathbf{f}\left[\mathbf{x}_i, \boldsymbol{\phi}\right]\right) \right]\right] \\ & = \underset{\phi}{\operatorname{argmin}}\left[ L[\boldsymbol{\phi}] \right] \qquad (4) \end{aligned} ϕ^=ϕargmin[−i=1∑Ilog[Pr(yi∣f[xi,ϕ])]]=ϕargmin[L[ϕ]](4)
二分类
二分类(binary classification)的目标是给数据 x \mathbf{x} x一个0或1的标签 y ∈ { 0 , 1 } y \in \{0, 1\} y∈{0,1}。比如我们会判断一封邮件是否是垃圾邮件,一个西瓜是好瓜还是坏瓜。
我们用负对数似然来推导出二分类的损失函数,首先在标签y的值域选择一个概率分布,伯努利分布(Bernouli distribution)刚好是一个合适的选择,它只有一个参数 λ ∈ [ 0 , 1 ] \lambda \in [0, 1] λ∈[0,1]表示y=1的概率:
Pr ( y ∣ λ ) = { 1 − λ y = 0 λ y = 1 , ( 5 ) \operatorname{Pr}(y \mid \lambda)=\left\{\begin{array}{ll} 1-\lambda & y=0 \\ \lambda & y=1 \end{array},\right. \qquad (5) Pr(y∣λ)={1−λλy=0y=1,(5)
上式也可以等价地写成更紧凑的指数形式:
P r ( y ∣ λ ) = ( 1 − λ ) 1 − y ⋅ λ y ( 6 ) Pr(y|\lambda) = (1-\lambda)^{1-y} \cdot \lambda^y \qquad (6) Pr(y∣λ)=(1−λ)1−y⋅λy(6)
接着, 我们用机器学习模型 f [ x , ϕ ] f[\mathbf{x}, \boldsymbol{\phi}] f[x,ϕ]来预测概率分布的参数 λ \lambda λ,因为 λ \lambda λ的值域为[0, 1],但是我们并不能保证模型输出在这个范围内,于是我们使用sigmoid函数将模型的输出映射到[0, 1]:
s i g [ z ] = 1 1 + exp [ − z ] ( 7 ) sig[z] = \frac{1}{1 + \exp[-z]} \qquad (7) sig[z]=1+exp[−z]1(7)
所以模型预测的概率分布参数 λ = s i g [ f [ x , ϕ ] ] \lambda = sig[f[\mathbf{x}, \boldsymbol{\phi}]] λ=sig[f[x,ϕ]],式(6)的似然变成了:
P r ( y ∣ x ) = ( 1 − s i g [ f [ x , ϕ ] ] ) 1 − y ⋅ s i g [ f [ x , ϕ ] ] y ( 8 ) Pr(y|\mathbf{x}) = (1 - sig[f[\mathbf{x}, \boldsymbol{\phi} ]])^{1-y} \ \cdot sig[f[\mathbf{x}, \boldsymbol{\phi}]]^y \qquad (8) Pr(y∣x)=(1−sig[f[x,ϕ]])1−y ⋅sig[f[x,ϕ]]y(8)
所以二分类的损失函数为在训练集上的负对数似然:
L [ ϕ ] = ∑ i = 1 I − ( 1 − y i ) log [ ( 1 − s i g [ f [ x , ϕ ] ] − y i log [ s i g [ f [ x , ϕ ] ] ] ( 9 ) L[\boldsymbol{\phi}] =\sum_{i=1}^I -(1-y_i) \log [(1 - sig[f[\mathbf{x}, \boldsymbol{\phi} ]]\ - y_i \log \left[sig[f[\mathbf{x}, \boldsymbol{\phi}]] \right] \qquad(9) L[ϕ]=i=1∑I−(1−yi)log[(1−sig[f[x,ϕ]] −yilog[sig[f[x,ϕ]]](9)
上式也被称为二元交叉熵损失(binary cross-entropy loss)。
多分类
多分类(multiclass classification)是将数据 x \mathbf{x} x分类到K>2个类别中的一个,即 y ∈ { 1 , 2 , … , K } y \in \{ 1, 2, \ldots, K \} y∈{1,2,…,K}, 比如预测手写数字是0-9里的哪一个,预测一个不完整句子的下一个可能词是什么。
我们再次尝试用负对数似然来推导出多分类的损失函数,首先在标签y的值域选择一个概率分布,因为 y ∈ { 1 , 2 , … , K } y \in \{ 1, 2, \ldots, K \} y∈{1,2,…,K},所以选择类别分布(categorical distribution),它有K个参数 λ 1 , λ 2 , … , λ K \lambda_1, \lambda_2, \ldots, \lambda_K λ1,λ2,…,λK来决定每个类别的概率,这些参数的值域都为[0, 1],并且它们的和为1以确保概率分布的有效性:
P r ( y = k ) = λ k ( 10 ) Pr(y=k) = \lambda_k \qquad (10) Pr(y=k)=λk(10)
接着, 我们用有K个输出的模型 f [ x , ϕ ] f[\mathbf{x}, \boldsymbol{\phi}] f[x,ϕ]来预测K个参数,我们同样无法确保模型的输出满足类别分布的要求,与二分类一样,我们也使用一个映射函数,这次我们选取的函数为softmax函数,它的输入为长度为K的任意向量,其输出为同样长度的向量,每个向量的值都在[0,1]范围内并且向量元素之和为1。第k个softmax函数的输出为:
softmax k [ z ] = exp [ z k ] ∑ k ′ = 1 K exp [ z k ′ ] ( 11 ) \text{softmax}_k[\mathbf{z}] = \frac {\exp[z_k]}{\sum_{k^{\prime}=1}^K \exp[z_{k^{\prime}}] } \qquad (11) softmaxk[z]=∑k′=1Kexp[zk′]exp[zk](11)
于是输入 x \mathbf{x} x得到标签y的似然为:
P r ( y = k ∣ x ) = softmax k [ f [ x , ϕ ] ] ( 12 ) Pr(y=k|\mathbf{x}) = \text{softmax}_k [\mathbf{f}[\mathbf{x}, \boldsymbol{\phi}] ] \qquad (12) Pr(y=k∣x)=softmaxk[f[x,ϕ]](12)
因此得到多分类的损失函数为下式定义的在训练集上的负对数似然:
L [ ϕ ] = − ∑ i = 1 I log [ softmax y i [ f [ x i , ϕ ] ] ] = − ∑ i = 1 I ( f y i [ x i , ϕ ] − log [ ∑ k ′ = 1 K exp [ f k ′ [ x i , ϕ ] ] ] ) ( 13 ) \begin{aligned} L[\boldsymbol{\phi}] &= - \sum^I_{i=1} \log [\text{softmax}_{y_i} [\mathbf{f}[x_i, \boldsymbol{\phi}] ] ] \\ &=- \sum^I_{i=1} \left(f_{y_i}[x_i, \boldsymbol{\phi}] - \log \left[\sum_{k^{\prime}=1}^K \exp[f_{k^{\prime}} [x_i, \boldsymbol{\phi}]] \right] \right) \qquad (13) \end{aligned} L[ϕ]=−i=1∑Ilog[softmaxyi[f[xi,ϕ]]]=−i=1∑I(fyi[xi,ϕ]−log[k′=1∑Kexp[fk′[xi,ϕ]]])(13)
上式中的 f k [ x i , ϕ ] f_{k}[x_i, \boldsymbol{\phi}] fk[xi,ϕ]是指神经网络的第k个输出,而上式也被称为多分类交叉熵损失(multiclass cross-entropy loss)。
交叉熵与负对数似然
在前面推导二分类和多分类的损失函数时,提到它们也被称为交叉熵损失,事实上交叉熵损失与使用负对数似然是等价的。
交叉熵损失的思想是找到使得观测数据y的经验分布q(y)和模型分布 P r ( y ∣ θ ) Pr(y|\boldsymbol{\theta}) Pr(y∣θ)距离最小的参数 θ \boldsymbol{\theta} θ。两个概率分布q(z)和p(z)的距离可以使用KL散度(Kullback-Leibler(KL) divergence)来评估:
D K L [ q ∣ ∣ p ] = ∫ q ( z ) log [ q ( z ) p ( z ) ] d z = ∫ − ∞ ∞ q ( z ) log [ q ( z ) ] d z − ∫ − ∞ ∞ q ( z ) log [ p ( z ) ] d z ( 14 ) \begin{aligned} D_{KL}[q||p] & = \int q(z) \log \left[ \frac{q(z)}{p(z)} \right] dz \\ &=\int_{-\infty}^{\infty} q(z) \log[q(z)] dz - \int_{-\infty}^{\infty} q(z) \log[p(z)] dz \end{aligned} \qquad (14) DKL[q∣∣p]=∫q(z)log[p(z)q(z)]dz=∫−∞∞q(z)log[q(z)]dz−∫−∞∞q(z)log[p(z)]dz(14)
数据集 { y i } i = 1 I \{y_i\}^I_{i=1} {yi}i=1I的经验数据分布可用下式来表示:
q ( y ) = 1 I ∑ i = 1 I δ [ y − y i ] ( 15 ) q(y) = \frac{1}{I} \sum^I_{i=1} \delta [y - y_i] \qquad (15) q(y)=I1i=1∑Iδ[y−yi](15)
上式中的 δ [ ⋅ ] \delta[\cdot] δ[⋅]是Dirac delta函数(Dirac delta函数 δ [ z ] \delta [\mathbf{z}] δ[z](即信号与系统里说的脉冲函数)的面积为1且全部集中在 z = 0 \mathbf{z}=\mathbf{0} z=0处,有N个元素的数据集可以被看作对包括N个中心为 x i \mathbf{x}_i xi的delta函数求和并用1/N做归一化的概率分布(如上式所示意),delta函数的一个关键性质是 ∫ f [ x ] δ [ x − x 0 ] d x = f [ x 0 ] \int f[\mathbf{x}] \delta [\mathbf{x} - \mathbf{x}_0] d\mathbf{x} = f[\mathbf{x}_0] ∫f[x]δ[x−x0]dx=f[x0])。
我们想要减少模型分布 P r ( y ∣ θ ) Pr(y|\boldsymbol{\theta}) Pr(y∣θ)和上述经验分布的KL散度:
θ ^ = argmin θ [ ∫ − ∞ ∞ q ( y ) log [ q ( y ) ] d y − ∫ − ∞ ∞ q ( y ) log [ Pr ( y ∣ θ ) ] d y ] = argmin θ [ − ∫ − ∞ ∞ q ( y ) log [ Pr ( y ∣ θ ) ] d y ] , ( 16 ) \begin{aligned} \hat{\boldsymbol{\theta}} & =\underset{\boldsymbol{\theta}}{\operatorname{argmin}}\left[\int_{-\infty}^{\infty} q(y) \log [q(y)] d y-\int_{-\infty}^{\infty} q(y) \log [\operatorname{Pr}(y \mid \boldsymbol{\theta})] d y\right] \\ & =\underset{\boldsymbol{\theta}}{\operatorname{argmin}}\left[-\int_{-\infty}^{\infty} q(y) \log [\operatorname{Pr}(y \mid \boldsymbol{\theta})] d y\right], \qquad (16) \end{aligned} θ^=θargmin[∫−∞∞q(y)log[q(y)]dy−∫−∞∞q(y)log[Pr(y∣θ)]dy]=θargmin[−∫−∞∞q(y)log[Pr(y∣θ)]dy],(16)
上式的第一行里的第一项因为它不依赖于 θ \boldsymbol{\theta} θ所以消失了,剩下的第二项被称为交叉熵(cross-entropy)。
将式(15)代入到上式:
θ ^ = argmin θ [ − ∫ − ∞ ∞ ( 1 I ∑ i = 1 I δ [ y − y i ] ) log [ Pr ( y ∣ θ ) ] d y ] = argmin θ [ − 1 I ∑ i = 1 I log [ Pr ( y i ∣ θ ) ] ] = argmin θ [ − ∑ i = 1 I log [ Pr ( y i ∣ θ ) ] ] . ( 17 ) \begin{aligned} \hat{\boldsymbol{\theta}} & =\underset{\boldsymbol{\theta}}{\operatorname{argmin}}\left[-\int_{-\infty}^{\infty}\left(\frac{1}{I} \sum_{i=1}^I \delta\left[y-y_i\right]\right) \log [\operatorname{Pr}(y \mid \boldsymbol{\theta})] d y\right] \\ & =\underset{\boldsymbol{\theta}}{\operatorname{argmin}}\left[-\frac{1}{I} \sum_{i=1}^I \log \left[\operatorname{Pr}\left(y_i \mid \boldsymbol{\theta}\right)\right]\right] \\ & =\underset{\boldsymbol{\theta}}{\operatorname{argmin}}\left[-\sum_{i=1}^I \log \left[\operatorname{Pr}\left(y_i \mid \boldsymbol{\theta}\right)\right]\right] . \qquad (17) \end{aligned} θ^=θargmin[−∫−∞∞(I1i=1∑Iδ[y−yi])log[Pr(y∣θ)]dy]=θargmin[−I1i=1∑Ilog[Pr(yi∣θ)]]=θargmin[−i=1∑Ilog[Pr(yi∣θ)]].(17)
上面的第一行到第二行利用了delta函数的性质,第二行到第三行是因为系数1/I不影响最小值的位置。
在机器学习中,分布参数 θ \boldsymbol{\theta} θ由模型 f [ x i , ϕ ] \mathbf{f}[\mathbf{x_i}, \boldsymbol{\phi}] f[xi,ϕ]计算得到,于是我们将式(17)可变成下式:
ϕ ^ = argmin ϕ [ − ∑ i = 1 I log [ Pr ( y i ∣ f [ x i , ϕ ] ) ] ] \hat{\boldsymbol{\phi}}= \underset{\boldsymbol{\phi}}{\operatorname{argmin}}\left[-\sum_{i=1}^I \log \left[\operatorname{Pr}\left(y_i \mid \mathbf{f}[\mathbf{x_i}, \boldsymbol{\phi}]\right)\right]\right] ϕ^=ϕargmin[−i=1∑Ilog[Pr(yi∣f[xi,ϕ])]]
如果对比一下,就会发现上式与负对数似然的定义式(4)是一样的,我们经过推导证明了交叉熵与负对数似然是等价的。
pytorch里的交叉熵实现
pytorch里不同的实现交叉熵的方法:
torch.nn.functional.binary_cross_entropy
计算二元交叉熵,输入为概率,也就是logits值要先经过sigmoid函数转换为概率。torch.nn.functional.binary_cross_entropy_with_logits
计算二元交叉熵,输入为logits。它相当于将Sigmoid层和BCELoss层合成为一个层,可以使用log-sum-exp技巧使得数值计算更稳定。torch.nn.functional.cross_entropy
计算交叉熵,使用logits作为输入,在其内部实现了LogSoftmax。有ignore_index参数用来忽略掉一些目标值使其对梯度没有影响。torch.nn.functional.nll_loss
与cross_entropy类似,只是要自己将logits经过LogSoftmax后再作为其输入。
# BINARY LABELS
import torch
labels = torch.tensor([1, 0, 1, 1, 1, 0], dtype=torch.float)
logits = torch.tensor([2.2, -1.1, 1.2, 2.2, 0.1, -0.5], dtype=torch.float)
torch.nn.functional.binary_cross_entropy_with_logits(logits, labels)
# tensor(0.3132)
## logits 需要先经过sigmoid转换
torch.nn.functional.binary_cross_entropy(torch.sigmoid(logits), labels)
# tensor(0.3132)
## MULTICLASS
labels = torch.tensor([1, 0, 2], dtype=torch.long)
logits = torch.tensor([[2, -0.5, 0.1],
[-1.1, 2, 0.0],
[1.2, 2.2, 3.1]], dtype=torch.float)
torch.nn.functional.cross_entropy(logits, labels)
# tensor(2.1388)
torch.nn.functional.nll_loss(torch.nn.functional.log_softmax(logits, dim=1), labels)
# tensor(2.1388)
## BINARY CROSS ENTROPY VS MULTICLASS IMPLEMENTATION(用交叉熵实现二分类)
labels = torch.tensor([1, 0, 1], dtype=torch.float)
probas = torch.tensor([0.9, 0.1, 0.8], dtype=torch.float)
torch.nn.functional.binary_cross_entropy(probas, labels)
# tensor(0.1446)
labels = torch.tensor([1, 0, 1], dtype=torch.long)
probas = torch.tensor([[0.1, 0.9],
[0.9, 0.1],
[0.2, 0.8]], dtype=torch.float)
torch.nn.functional.nll_loss(torch.log(probas), labels)
# tensor(0.1446)
参考资料
《Understanding Deep Learning》第5章(本文的图片来自此书)
https://sebastianraschka.com/faq/docs/pytorch-crossentropy.html
pytorch文档