交叉熵与极大似然
前言
交叉熵这玩意儿,最开始接触是在 Andrew Ng 的机器学习课程中讲二分类问题的时候,当时并没有觉得有多难,用极大似然对其进行推导也非常的顺畅。后来直接用到多分类问题中,我也没细想,毕竟在 pytorch 中就是调用一个 CrossEntropyLoss()
的事儿。不过昨天我又重新想了想怎么从极大似然法推导交叉熵,这一想,就把整个人给绕进去了,困扰了大半天,终于想通了之前的理解哪里出了问题,遂写下这篇文章记录一下。
交叉熵定义
假设 \(X\) 是离散的随机变量,我们有两个分布列 \(p(x),q(x)\),交叉熵定义如下: \[ H(q, p)=-\sum_xq(x)\log p(x)\tag{1} \] 我们常说交叉熵能衡量两个分布之间的相似程度,也即交叉熵越小,\(p,q\) 两个分布越相似。事实上,如果我们固定 \(q\),那么可以证明:\(H(q,p)\) 在 \(p=q\) 时达到最小。(详见参考资料[2])
极大似然法推导交叉熵
交叉熵能用极大似然法解释。回忆极大似然法:我们有若干未知的参数 \(\theta\),现在想对其进行估计,那么抽取一个样本 \(\{x^{(1)},\ldots,x^{(n)}\}\),计算在参数 \(\theta\) 下这个样本被抽取出来的概率。这个概率是参数 \(\theta\) 的函数,称之为似然函数,当似然函数(或对数似然函数)取到最大值时,对应的参数值就是我们的估计值。
欲用极大似然法解释交叉熵,我们可以把 \(p(x)\) 视为想去估计的「参数」,\(q(x)\) 视为样本的真实概率分布。为方便,假设 \(X\) 是离散的随机变量。我们现在抽取一个容量为 \(n\) 的样本,设其中 \(X=k\) 出现了 \(n_k\) 次,那么在参数 \(p(x)\)下,这个样本被抽取出来的概率是 \(L(p)=\prod\limits_k p(k)^{n_k}\),对数似然为:\(\log L(p)=\sum\limits_kn_k\log p(k)\)。除以样本量再取个负号,得到: \[ -\frac{1}{n}\log L(p)=-\sum_k\frac{n_k}{n}\log p(k)\approx-\sum_k q(k)\log p(k)\tag{2} \] 其中,\(\frac{n_k}{n}\approx q(k)\) 是因为 \(q\) 才是样本的真实分布。这样我们就得到了交叉熵的形式,让交叉熵最小,就是让似然函数最大,也就是让 \(p(x)\) 更接近真实分布 \(q(x)\)。
分类问题中的交叉熵损失函数
现在在分类问题的背景下看交叉熵与极大似然。
考虑一个特定的输入 \(x\),不妨设是一只猫咪吧。其类别的真实分布是一个 \(\text{onehot}\) 向量(仅在“猫”那一维为 \(1\),其余为 \(0\)),预测分布为 pred 向量。正如前文所述,交叉熵衡量两个分布的相似程度,因而我们可以用交叉熵 \[ H(\text{onehot},\text{pred})=-\sum_{c\in\text{classes}}\text{onehot}_c\cdot\log(\text{pred}_c)=-\log(\text{pred}_{猫})\tag{3} \] 作损失函数。可以发现,交叉熵用在分类问题中形式变得很简单,这归功于 \(\text{onehot}\) 这个其实完全没有随机性的概率分布。换句话说,我们常常用的交叉熵损失函数,其实是特殊的、指定了其中一个分布为 \(\text{one-hot}\) 形式的交叉熵。
在分类问题的背景下,用极大似然法解释上述交叉熵,其实是一件很无聊的事,主要是因为真实分布是 \(\text{onehot}\)。极大似然法需要我们抽一个样本出来(设样本量为 \(n\)),这里的样本空间就是所有类别,但由于我们现在考虑的是一个特定的输入 \(x\)(猫咪),所以我们的样本一定是:猫、猫、……、猫。于是似然函数就是 \(L(\text{pred})=(\text{pred}_{猫})^n\),对数似然就是 \(\log L(\text{pred})=n\log(\text{pred}_猫)\),除以样本量取个负,就得到了 \[
-\frac{1}{n}\log L(\text{pred})=-\frac{n\log(\text{pred}_猫)}{n}=-\log(\text{pred}_猫)\tag{4}
\]
如果只考虑二分类问题,上文依旧是成立的,但是二分类嘛,我们没必要输出一个大小为 \(2\) 的向量,只用输出一个概率值 \(p\) 就 OK 了。所以可以对上文的交叉熵形式做一个改写: \[ \begin{align} \text{BCE}(y,p)&=\begin{cases}-\log p&\text{if }y=1\\-\log(1-p)&\text{if }y=0\end{cases} \\ &=-y\log p-(1-y)\log(1-p)\tag{5} \end{align} \] 其中 \(y\in\{0, 1\},\,p\in(0, 1)\),就得到了我们非常熟悉的 Binary Cross-Entropy 的形式。
那为什么我之前被困扰了大半天呢?因为我把样本空间弄混了。极大似然法这个方法本身要求我们做一个采样,而这里采样的对象,其实是某一个特定输入的真实标签,然后颇为无聊地反复采若干次……我之前以为是训练模型的过程中采出的 minibatch,结果当然就死活想不通。所以一定要注意「训练过程中对数据集采样得到 minibatch」 和「极大似然法中的采样」是不同的。
不过,前者也有值得一说的地方,设 minibatch 是 \(\{(x^{(i)},y^{(i)})\mid i=1,\ldots,m\}\),用 \(M\) 表示模型(\(M(x)\) 为输入 \(x\) 时模型的输出),则在训练过程中,计算一个 iteration 的平均 \(\text{Loss}\) 是这样的: \[ \text{Loss}=\frac{1}{m}\sum_{i=1}^mH\left(y^{(i)},M(x^{(i)})\right)=\frac{1}{m}\sum_{i=1}^m-\log\left(M(x^{(i)})_{y^{(i)}}\right)\tag{6} \] 由于采样再求平均,其实是在近似一个期望,所以更通用的,上式可以写作: \[ \text{Loss}=\mathbb E_{x,y\sim p_\text{data}(x,y)}\left[-\log\left(M(x)_{y}\right)\right]\tag{7} \] 其中 \(p_\text{data}(x,y)\) 表示数据集的分布。这个样子好像在哪里见过?没错,GAN 的论文[1]中作者就是写了这么个期望:
\[ V(D, G)=\mathbb E_{x\sim p_\text{data}(x)}[-\log(D(x))]+\mathbb E_{z\sim p_\text{z}(z)}[-\log(1-D(G(z)))]\tag{8} \] 只不过 GAN 中判别网络 D 只需要二分类,所以把两类拆开分别写出来罢了。