当一个语言模型预测下一个词时,它输出的是整个词表上的概率分布。比如在"今天天气很"这个上下文后,模型可能给出"好"的概率是0.7,“差"的概率是0.2,“热"的概率是0.08,其他词的概率更低。如果真实的下一个词确实是"好”,我们如何量化模型预测的质量?更进一步,如何设计一个可微分的损失函数,让模型能够通过梯度下降不断优化这个预测能力?
交叉熵损失函数提供了这个问题的答案。它不仅统治了现代深度学习中的分类任务,更是理解整个概率机器学习框架的核心概念。从1948年香农创立信息论,到今天千亿参数的大语言模型训练,交叉熵始终是连接信息论、概率统计和优化算法的关键桥梁。
graph TD
A[信息论基础] --> B[自信息 I x = -log p x]
B --> C[熵 H P = E I x]
C --> D[交叉熵 H P,Q]
D --> E[KL散度 D_KL = H P,Q - H P]
F[机器学习] --> G[最大似然估计]
G --> H[负对数似然]
H --> D
I[深度学习] --> J[分类任务]
J --> K[Softmax输出概率]
K --> L[交叉熵损失]
L --> D
style D fill:#f9f,stroke:#333
style L fill:#bbf,stroke:#333
从自信息到熵:理解"惊讶"的度量
要理解交叉熵,必须先回到信息论最基础的概念:如何量化一个事件的"信息量"或"惊讶程度”。
自信息:稀有事件更令人惊讶
1948年,克劳德·香农在《通信的数学理论》中提出了一个深刻的问题:如何度量一个随机事件所携带的信息量?他的答案是:事件越不可能发生,发生时带来的"惊讶"就越大,信息量也越大。
对于概率为 $p$ 的事件,自信息定义为:
$$I(x) = -\log_2 p(x)$$使用以2为底的对数,信息量的单位是"比特"。这个定义有几个直观的合理性:
确定事件不带来新信息。如果 $p = 1$,则 $I(x) = -\log_2(1) = 0$。这完全符合直觉——如果你百分百确定某事会发生,那么当它发生时你并不感到惊讶,也没有获得任何新信息。
不可能事件带来无穷信息。当 $p \to 0$ 时,$I(x) \to \infty$。一个几乎不可能发生的事件竟然发生了,这带来的震惊是巨大的。
概率的对数关系保证了可加性。假设两个独立事件同时发生,联合概率是各自概率的乘积 $p(x)p(y)$,而自信息则是各自自信息的和:
$$I(x,y) = -\log_2[p(x)p(y)] = -\log_2 p(x) - \log_2 p(y) = I(x) + I(y)$$这符合直觉:两个独立事件同时发生的惊讶程度,等于各自惊讶程度的叠加。
举个具体的例子。抛一枚公平硬币得到正面,概率是 $1/2$,自信息是 $-\log_2(0.5) = 1$ 比特。掷一个公平骰子得到6,概率是 $1/6$,自信息是 $-\log_2(1/6) \approx 2.58$ 比特。后者更令人惊讶,因为它更稀有。
熵:平均惊讶程度的度量
自信息度量的是单个事件的惊讶程度,但很多时候我们关心的是整个概率分布的"不确定性"或"混乱程度"。这就引出了熵的概念。
熵是自信息的期望值,对于一个离散概率分布 $P$,其熵定义为:
$$H(P) = -\sum_{x} p(x) \log_2 p(x)$$熵度量的是:如果我们从这个分布中随机抽取样本,平均会获得多少信息量。熵越高,分布越不确定;熵越低,分布越确定。
graph LR
subgraph "低熵分布 - 确定性高"
A1[事件A: p=0.9] --> A2[事件B: p=0.1]
end
subgraph "高熵分布 - 不确定性高"
B1[事件A: p=0.5] --> B2[事件B: p=0.5]
end
C[确定性分布] -->|熵=0| D[完全可预测]
E[均匀分布] -->|熵=max| F[最难预测]
style A1 fill:#afa,stroke:#333
style B1 fill:#ffa,stroke:#333
style B2 fill:#ffa,stroke:#333
确定性分布的熵为零。如果分布只取一个值(概率为1),熵是0。比如一个永远输出"是"的随机变量,没有任何不确定性。
均匀分布的熵最大。对于有 $n$ 个可能取值的随机变量,均匀分布 $p(x) = 1/n$ 的熵是:
$$H_{\max} = -\sum_{i=1}^{n} \frac{1}{n} \log_2 \frac{1}{n} = \log_2 n$$这符合直觉:当所有结果都同等可能时,不确定性最大。
以一个50000词的词表为例。如果模型对下一个词完全无知,给出均匀分布,熵是 $\log_2(50000) \approx 15.6$ 比特。这意味着平均需要15.6比特来编码每个词。但如果模型能够准确预测,只给正确词分配概率1,熵降为0——不需要任何比特来传输,因为接收方已经知道答案。
交叉熵:衡量预测与真相的距离
理解了熵,交叉熵就水到渠成了。
从编码视角理解交叉熵
假设你要设计一套编码方案来传输消息。你知道消息的真实分布是 $P$,但你误以为分布是 $Q$,因此基于 $Q$ 设计了编码方案。
在这种"错误假设"下,传输一个消息所需的平均比特数是多少?答案就是交叉熵:
$$H(P, Q) = -\sum_{x} p(x) \log_2 q(x)$$这里,$p(x)$ 是真实分布中事件 $x$ 出现的概率,而 $-\log_2 q(x)$ 是基于错误假设设计的编码方案对 $x$ 分配的比特数。
交叉熵告诉我们要为"错误认识"付出的代价:如果你假设的分布 $Q$ 与真实分布 $P$ 差别很大,交叉熵就会很高;如果 $Q$ 很接近 $P$,交叉熵就会很低。
一个关键性质:交叉熵永远不小于熵本身。即:
$$H(P, Q) \geq H(P)$$这很合理:用错误的假设来编码,永远不可能比用正确的假设编码更高效。当且仅当 $P = Q$ 时等号成立。
KL散度:纯粹的距离度量
交叉熵与熵的差值,被称为KL散度(Kullback-Leibler散度):
$$D_{KL}(P \| Q) = H(P, Q) - H(P) = \sum_{x} p(x) \log_2 \frac{p(x)}{q(x)}$$KL散度度量的是:因为假设了错误的分布 $Q$ 而不是真实分布 $P$,我们"额外"浪费了多少比特。它是一个纯粹的距离度量——如果 $P = Q$,KL散度为0。
在机器学习中,这个分解至关重要:
$$H(P, Q) = H(P) + D_{KL}(P \| Q)$$当我们训练模型时,真实分布 $P$ 是固定的(由训练数据决定),所以 $H(P)$ 是常数。最小化交叉熵 $H(P, Q)$,完全等价于最小化KL散度 $D_{KL}(P \| Q)$——让模型的预测分布尽可能接近真实分布。
graph TD
A[交叉熵 H P,Q] --> B[熵 H P]
A --> C[KL散度 D_KL P Q]
B --> D[真实分布的不确定性<br/>与模型参数无关]
C --> E[预测与真相的距离<br/>优化目标]
F[训练模型] --> G[最小化交叉熵]
G --> H[等价于最小化KL散度]
style A fill:#fbb,stroke:#333
style B fill:#bfb,stroke:#333
style C fill:#fbf,stroke:#333
style E fill:#ff9,stroke:#333
为什么深度学习选择交叉熵而不是MSE?
很多初学者会有疑问:既然我们要让预测分布接近真实分布,为什么不直接用均方误差(MSE)?
数学直觉:交叉熵惩罚更"锐利"
考虑一个三分类问题,真实类别是第1类(one-hot编码为 $[1, 0, 0]$)。
场景A:模型预测概率是 $[0.7, 0.2, 0.1]$
- 交叉熵损失:$-\log(0.7) \approx 0.357$
- MSE损失:$(1-0.7)^2 + (0-0.2)^2 + (0-0.1)^2 = 0.14$
场景B:模型预测概率是 $[0.4, 0.4, 0.2]$
- 交叉熵损失:$-\log(0.4) \approx 0.916$
- MSE损失:$(1-0.4)^2 + (0-0.4)^2 + (0-0.2)^2 = 0.56$
从场景A到场景B,预测变差了。交叉熵损失从0.357增加到0.916,增长了约157%。MSE损失从0.14增加到0.56,增长了约300%。
看起来MSE对错误更敏感?但问题在于梯度特性。
梯度分析:交叉熵与Softmax的完美配合
这是交叉熵在分类任务中占据统治地位的真正原因。
当输出层使用Softmax激活函数时,Softmax将原始输出(logits)转换为概率分布:
$$s_i = \frac{e^{z_i}}{\sum_{j} e^{z_j}}$$其中 $z$ 是网络的原始输出,$s$ 是概率分布。
现在计算交叉熵损失对logits $z_k$ 的梯度。交叉熵损失是:
$$\mathcal{L} = -\sum_{i} y_i \log s_i$$其中 $y$ 是one-hot编码的真实标签。
经过推导,梯度有一个令人惊讶的简洁形式:
$$\frac{\partial \mathcal{L}}{\partial z_k} = s_k - y_k$$这个结果优雅得令人窒息:梯度就是预测概率与真实标签的差值!
如果真实类别是 $c$($y_c = 1$,其他为0),那么:
- 对于正确类别:$\frac{\partial \mathcal{L}}{\partial z_c} = s_c - 1$(负值,会增加 $z_c$)
- 对于错误类别:$\frac{\partial \mathcal{L}}{\partial z_k} = s_k$(正值,会减少 $z_k$)
这个梯度有一个关键特性:无论预测概率多小,梯度都不会消失。即使模型对正确类别给出 $s_c = 0.001$ 的极低概率,梯度仍然是 $0.001 - 1 = -0.999$,足够大,能够驱动模型学习。
graph LR
subgraph "Softmax + 交叉熵"
A1[Logits z] --> B1[Softmax]
B1 --> C1[概率 s]
C1 --> D1[交叉熵 L]
D1 --> E1[梯度: s - y]
E1 -->|简洁优雅| F1[无梯度消失]
end
subgraph "Sigmoid + MSE"
A2[Logits z] --> B2[Sigmoid]
B2 --> C2[概率 σ z]
C2 --> D2[MSE]
D2 --> E2[复杂梯度]
E2 -->|饱和区| F2[梯度消失]
end
style F1 fill:#afa,stroke:#333
style F2 fill:#faa,stroke:#333
MSE的梯度困境
对比之下,如果使用MSE损失:
$$\mathcal{L}_{MSE} = \sum_{i} (y_i - s_i)^2$$梯度计算会涉及Softmax的雅可比矩阵,形式复杂得多。更糟糕的是,当模型使用Sigmoid激活(输出在0到1之间)时,MSE损失在饱和区会出现梯度消失问题。
假设二分类问题,真实标签 $y=1$,模型输出 $\hat{y}$。MSE损失是 $(1-\hat{y})^2$,梯度是 $-2(1-\hat{y}) \cdot \hat{y}(1-\hat{y})$(考虑Sigmoid导数)。
当 $\hat{y} \approx 0$(模型严重错误)时,梯度是 $-2(1-0) \cdot 0 \cdot 1 = 0$。模型错得离谱,梯度反而消失了。
而交叉熵损失 $-\log(\hat{y})$ 在 $\hat{y} \approx 0$ 时梯度是 $-1/\hat{y}$,趋近于无穷大——错误越严重,修正越强烈。这正是我们希望的行为。
与最大似然估计的深刻等价性
交叉熵在机器学习中的统治地位,还有一个更深层的理论支撑:它与最大似然估计完全等价。
从最大似然到交叉熵
假设我们有一个参数化模型 $q_\theta$,要拟合真实数据分布 $p$。最大似然估计的目标是找到参数 $\theta$,使得观测数据出现的概率最大:
$$\theta_{MLE} = \arg\max_\theta \prod_{i=1}^{n} q_\theta(x_i)$$取对数(不改变最优解):
$$\theta_{MLE} = \arg\max_\theta \sum_{i=1}^{n} \log q_\theta(x_i)$$取负号,变成最小化问题:
$$\theta_{MLE} = \arg\min_\theta -\sum_{i=1}^{n} \log q_\theta(x_i)$$这正是交叉熵损失的形式!对于分类问题,如果 $x_i$ 是第 $c$ 类,则 $q_\theta(x_i) = q_\theta(y_c)$ 是模型对该类别的预测概率。
因此,最小化交叉熵损失完全等价于最大似然估计。这不是巧合,而是深刻的理论统一:选择交叉熵作为损失函数,等价于我们相信最大似然是一个好的统计推断原则。
数值稳定性:工程实现的隐形艺术
理论上完美的公式,在实际计算中可能遭遇灾难。交叉熵与Softmax的组合就是一个经典案例。
溢出的陷阱
Softmax的定义是:
$$s_i = \frac{e^{z_i}}{\sum_{j} e^{z_j}}$$当 $z_i$ 很大时,比如 $z_i = 1000$,则 $e^{1000}$ 会超出float64的表示范围,变成无穷大。于是我们得到 $\infty/\infty = \text{NaN}$。
Log-Sum-Exp技巧
解决方案是利用一个简单的数学恒等式。对于任意常数 $c$:
$$\frac{e^{z_i}}{\sum_j e^{z_j}} = \frac{e^{z_i + c}}{\sum_j e^{z_j + c}} = \frac{e^{z_i - \max(z)}}{\sum_j e^{z_j - \max(z)}}$$关键洞察:减去最大值后,所有指数的输入都变成非正数,最大值是0(对应原来最大的那个元素)。因此 $e^{z_i - \max(z)}$ 永远在 $[0, 1]$ 范围内,不会溢出。
flowchart TD
A[原始Softmax] --> B["问题: e^1000 溢出"]
C[Log-Sum-Exp技巧] --> D["减去最大值: z - max z"]
D --> E["e^ z_i - max z ∈ 0,1"]
E --> F["分子分母同时放大/缩小"]
F --> G["结果不变,但数值稳定"]
H[LogSoftmax实现] --> I["log s_i = z_i - max z - log Σexp"]
I --> J["避免计算exp和log分开"]
J --> K["一次计算,无精度损失"]
style B fill:#faa,stroke:#333
style G fill:#afa,stroke:#333
style K fill:#afa,stroke:#333
更进一步,计算 $\log(\text{softmax}(z_i))$ 可以直接得到:
$$\log(s_i) = z_i - \max(z) - \log\left(\sum_j e^{z_j - \max(z)}\right)$$这被称为Log-Sum-Exp技巧。现代深度学习框架都采用这个实现。
PyTorch的实现智慧
PyTorch的CrossEntropyLoss实际上结合了LogSoftmax和NLLLoss(负对数似然损失):
import torch
import torch.nn as nn
# 方式1:分开计算
log_softmax = nn.LogSoftmax(dim=1)
nll_loss = nn.NLLLoss()
loss = nll_loss(log_softmax(logits), targets)
# 方式2:合并计算(推荐)
cross_entropy = nn.CrossEntropyLoss()
loss = cross_entropy(logits, targets)
两种方式数学上等价,但第二种更高效且数值更稳定,因为框架可以在内部融合操作,避免中间结果的精度损失。
在语言模型中的核心地位
交叉熵损失是大语言模型训练的基石。理解它在语言模型中的具体应用,能让我们更深刻地把握其本质。
下一个词预测:序列上的交叉熵
语言模型的训练目标是预测序列中的下一个词。对于序列 $x_1, x_2, ..., x_T$,模型在每个位置 $t$ 输出一个概率分布,表示对下一个词的预测。
训练时,交叉熵损失定义为:
$$\mathcal{L} = -\frac{1}{T} \sum_{t=1}^{T} \log p(x_t | x_{这个公式有一个直观解释:我们希望模型对序列中每个真实出现的词都分配尽可能高的概率。模型的"困惑"程度越低,损失越小。
sequenceDiagram
participant 输入序列
participant 模型
participant Softmax
participant 交叉熵
输入序列->>模型: x_1
模型->>Softmax: logits_1
Softmax->>交叉熵: P x_2 x_1
交叉熵->>交叉熵: -log P x_2 x_1
输入序列->>模型: x_1, x_2
模型->>Softmax: logits_2
Softmax->>交叉熵: P x_3 x_1,x_2
交叉熵->>交叉熵: -log P x_3 x_1,x_2
Note over 交叉熵: 平均: -1/T Σ log P x_t x_<t
困惑度:交叉熵的另一种表达
困惑度是语言模型评估中最常用的指标之一,它直接源于交叉熵:
$$\text{PPL} = \exp\left(-\frac{1}{T}\sum_{t=1}^{T} \log p(x_t | x_{困惑度为词表大小时,模型等价于随机猜测。比如词表有50000词,困惑度50000意味着模型毫无预测能力。困惑度为1意味着模型对每个词都完全确定,这通常意味着过拟合。
一个训练良好的英语语言模型,在标准测试集上的困惑度可能在15-30之间,表示模型在每个位置平均在15-30个候选词中选择。
Bits Per Character:消除分词器的影响
不同模型使用不同的分词器,直接比较困惑度可能不公平。Bits Per Character (BPC) 通过按字符归一化来解决这个问题:
$$\text{BPC} = \frac{H(P, Q) \times \text{token数}}{\text{字符数}} \times \log_2 e$$BPC表示平均每个字符需要多少比特来编码。这是信息论与数据压缩理论的直接联系:一个好的语言模型本质上是一个好的文本压缩器。
现代大模型在英文文本上可以达到约0.7-0.9 BPC,接近人类估计的英语熵(约0.6-1.0 BPC)。
变体与扩展:从二分类到多标签
交叉熵损失有几个重要变体,适用于不同的任务场景。
二分类交叉熵
对于二分类问题,我们只需要预测一个类别的概率 $\hat{y}$(另一个类别的概率自动是 $1-\hat{y}$)。二分类交叉熵定义为:
$$\mathcal{L}_{BCE} = -[y \log \hat{y} + (1-y) \log(1-\hat{y})]$$这可以看作是多分类交叉熵在类别数为2时的简化形式。
多标签分类的交叉熵
多标签分类中,一个样本可以同时属于多个类别。比如一张图片可能同时包含"猫"、“户外”、“白天"等多个标签。
处理多标签分类的标准方法是将问题分解为多个独立的二分类问题:对每个类别,用Sigmoid函数预测该类别是否存在,然后用二分类交叉熵计算损失:
$$\mathcal{L} = -\sum_{c=1}^{C} [y_c \log \sigma(z_c) + (1-y_c) \log(1-\sigma(z_c))]$$这里 $\sigma$ 是Sigmoid函数,$y_c \in \{0, 1\}$ 表示样本是否属于类别 $c$。
标签平滑:防止过度自信
标准的交叉熵使用hard label(正确类别概率为1,其他为0)。这可能导致模型过度自信,泛化能力下降。
标签平滑(Label Smoothing)将hard label转换为soft label:
$$y'_c = \begin{cases} 1 - \epsilon + \frac{\epsilon}{C} & \text{如果 } c \text{ 是正确类别} \\ \frac{\epsilon}{C} & \text{其他情况} \end{cases}$$其中 $\epsilon$ 是平滑系数(通常取0.1),$C$ 是类别数。
这相当于告诉模型:正确类别的概率不是100%,错误类别也有小概率出现。标签平滑鼓励模型产生更平滑的概率分布,提高泛化能力。
在Transformer论文《Attention Is All You Need》和Inception-v2论文中,标签平滑都被证明能提升模型性能。
Focal Loss:处理类别不平衡
当正负样本严重不平衡时(如目标检测中的背景vs物体),标准交叉熵的效果往往不佳。Focal Loss对交叉熵进行了修改:
$$\mathcal{L}_{FL} = -(1-p_t)^\gamma \log(p_t)$$其中 $p_t$ 是模型对正确类别的预测概率,$\gamma$ 是聚焦参数(通常取2)。
关键思想:当 $p_t$ 接近1时(模型预测正确且自信),$(1-p_t)^\gamma$ 趋近于0,损失被抑制。这减少了简单样本的主导地位,让模型更专注于难分类的样本。
graph TD
A[交叉熵损失变体] --> B[二分类 BCE]
A --> C[多分类 CCE]
A --> D[多标签 BCE]
B --> B1[Sigmoid激活]
C --> C1[Softmax激活]
D --> D1[多个Sigmoid]
E[正则化技术] --> F[标签平滑]
E --> G[Focal Loss]
F --> F1[防止过度自信]
G --> G1[处理类别不平衡]
H[任务类型选择] --> I{单标签 vs 多标签}
I -->|单标签| J{类别数}
J -->|2类| B
J -->|>2类| C
I -->|多标签| D
style A fill:#bbf,stroke:#333
style E fill:#bfb,stroke:#333
style H fill:#fbf,stroke:#333
实践中的选择指南
交叉熵损失的变体众多,如何选择?
单标签多分类:使用CrossEntropyLoss(结合Softmax)。这是图像分类、文本分类等任务的默认选择。
二分类:可以使用BCEWithLogitsLoss(结合Sigmoid的二分类交叉熵),或者将其视为2类分类使用CrossEntropyLoss。两者在数学上等价,但前者计算更高效。
多标签分类:使用BCEWithLogitsLoss,每个类别独立预测。
语言模型训练:使用CrossEntropyLoss,但通常不会对整个词表计算,而是使用采样策略(如Sampled Softmax)来加速。
类别不平衡:考虑使用加权交叉熵或Focal Loss。加权交叉熵给少数类更高的权重;Focal Loss自动降低简单样本的影响。
从公式到洞察
交叉熵损失函数之所以统治深度学习的概率预测任务,是因为它同时满足了多个层面的要求:
从信息论角度,它度量了用错误分布编码真实分布所需的额外比特数,是一个自然的"距离"度量。
从统计学角度,最小化交叉熵等价于最大似然估计,有坚实的理论基础。
从优化角度,它与Softmax的组合产生简洁优雅的梯度公式,不会出现梯度消失问题。
从工程角度,它有成熟的数值稳定实现,是现代深度学习框架的标准组件。
理解交叉熵,就是理解了概率机器学习的核心。从香农的信息论,到最大似然估计,再到现代大语言模型的训练,交叉熵始终是那条贯穿始终的主线。当你下次看到模型输出一个概率分布,想想那个负对数——它不只是一个损失函数,更是信息论、概率论和优化理论的完美交汇。
参考文献
-
Shannon, C. E. (1948). A mathematical theory of communication. Bell System Technical Journal, 27(3), 379-423.
-
Kullback, S., & Leibler, R. A. (1951). On information and sufficiency. Annals of Mathematical Statistics, 22(1), 79-86.
-
Goodfellow, I., Bengio, Y., & Courville, A. (2016). Deep Learning. MIT Press.
-
Vaswani, A., et al. (2017). Attention is all you need. NeurIPS.
-
Szegedy, C., et al. (2016). Rethinking the inception architecture for computer vision. CVPR.
-
Lin, T. Y., et al. (2017). Focal loss for dense object detection. ICCV.
-
Bengio, Y., & Senécal, J. S. (2008). Adaptive importance sampling to accelerate training of a neural probabilistic language model. IEEE Transactions on Neural Networks, 19(4), 713-722.
-
Jozefowicz, R., et al. (2016). Exploring the limits of language modeling. arXiv preprint arXiv:1602.02410.
-
Kaplan, J., et al. (2020). Scaling laws for neural language models. arXiv preprint arXiv:2001.08361.
-
Brown, T. B., et al. (2020). Language models are few-shot learners. NeurIPS.
-
Press, O., & Wolf, L. (2017). Using the output embedding to improve language models. EACL.
-
Muller, R., Kornblith, S., & Hinton, G. (2019). When does label smoothing help? NeurIPS.