跳到内容

第 10 卷 / 第 12 章 / 第 1 课

12.1 自信息、熵与编码:不确定性怎样变成比特 ​

同样长的一条日志,天天出现和一年才出现一次,提供的意外程度不同。信息论从概率模型出发,度量观察结果消除了多少不确定性。本课讨论有限离散结果集合,避免把离散熵直接套到连续密度上。

本课目标 ​

  • 区分单个结果的自信息与随机变量的熵;
  • 解释熵和无损编码平均长度的关系;
  • 理解联合熵、条件熵与互信息;
  • 区分交叉熵、KL 散度及其在模型训练中的作用。

1. 越意外的结果,自信息越大 ​

概率为 p(x) 的结果 x,自信息定义为:

I(x)=−log2⁡p(x).

以 2 为底时单位是 bit。这个定义满足三个直觉:

公式先取 p(x)>0;p(x)=0 的结果在模型内不会出现,若用扩展值表示,其自信息为正无穷。实际观察到它,说明模型的零概率断言需要重新检查。

  • 必然事件 p=1 的自信息为 0;
  • 事件越罕见,自信息越大;
  • 独立事件同时发生时,概率相乘,自信息相加。

例如某告警每天出现的概率为 1/1024,观察到它的自信息是 10 bit。这里度量的是概率模型下的意外程度,不是业务损失。一个高概率故障仍可能代价巨大,一个低概率日志也可能毫无业务价值。

2. 熵是平均自信息 ​

离散随机变量 X 的熵为:

H(X)=−∑xp(x)log2⁡p(x)=E[I(X)].

约定 0log⁡0=0。熵描述在揭晓 X 前的平均不确定性。

公平硬币有两个等概率结果:

H(X)=−2×12log2⁡12=1 bit.

若正面概率为 0.9,熵约为 0.469 bit。结果更可预测,平均不确定性更低。但不要把“概率为 50% 的天气预报”说成一条消息本身信息更多;必须先明确随机变量、先验分布以及实际观察到的结果。

对固定的 n≥1 个可选结果,0≤H(X)≤log2⁡n,均匀分布达到上界。改变结果集合时,上界和问题含义也改变;比较熵值前应确认变量、分布与单位一致。

3. 熵为什么与压缩有关 ​

对至少含两个正概率符号的有限字母表,二进制唯一可译码的平均长度 L 满足 L≥H(X)。Huffman 编码构造最优二进制前缀码,并满足 H(X)≤L<H(X)+1。这些码长按每个符号计;单符号信源可约定为空码,但还需从外部知道消息长度。

例如四个符号的概率为 1/2、1/4、1/8、1/8,代码 0、10、110、111 没有任何码字是另一码字的前缀。下面用 Python 3 检查这个码表,平均长度恰好等于熵:

python
import math

probabilities = [0.5, 0.25, 0.125, 0.125]
codes = ["0", "10", "110", "111"]
entropy = -math.fsum(p * math.log2(p) for p in probabilities)
average_length = math.fsum(p * len(c) for p, c in zip(probabilities, codes))
assert all(not b.startswith(a) for a in codes for b in codes if a != b)
print(entropy, average_length)  # 1.75 1.75

这不表示任意一个文件都能压缩到恰好 H(X) bit:

  • 熵依赖信源概率模型;
  • 单个有限文件会有码表和容器开销;
  • 未利用符号之间的相关性,会错过可压缩结构;
  • 已压缩或接近随机的数据可能越压越大。

算术编码、范围编码可以更细致地逼近概率模型给出的码长。压缩系统的核心竞争力往往不是“编码器公式”,而是能否准确预测下一个符号的分布。

若 N 个符号独立同分布,块熵为 NH(X);有相关性时应看联合熵或熵率,不能直接乘 N。任何无损压缩器也不可能缩短所有 n bit 输入:这样的输入有 2n 个,长度小于 n 的二进制串总共只有 2n−1 个,无法一一对应。

4. 条件熵与互信息 ​

联合熵 H(X,Y) 描述同时观察两个变量前的不确定性。条件熵:

H(Y∣X)=H(X,Y)−H(X)

表示知道 X 后,Y 还剩多少平均不确定性。

互信息定义为:

I(X;Y)=H(Y)−H(Y∣X)=H(X)+H(Y)−H(X,Y).

互信息非负,且为零当且仅当两个变量独立;它捕捉的不限于线性关系。例如 X 在 −1,0,1 上等概率,Y=X2,两者协方差为零,却由 X 完全确定 Y,互信息为 H(Y)≈0.9183 bit。条件熵是对 X 的取值加权平均,平均条件熵不增加,不代表每个具体观察都降低不确定性。

互信息并不自动说明因果方向。两个变量都受第三个因素影响时也可能有高互信息;在有限样本上估计互信息还会受到分箱、密度估计和偏差影响。

5. 交叉熵与 KL 散度 ​

真实分布为 P,模型分布为 Q,交叉熵是:

H(P,Q)=−∑xP(x)log⁡Q(x).

KL 散度为:

DKL(P∥Q)=∑xP(x)log⁡P(x)Q(x).

在定义良好、对数底相同的条件下:

H(P,Q)=H(P)+DKL(P∥Q).

对固定 P,最小化交叉熵等价于最小化 DKL(P∥Q)。训练集给出的是未知生成分布的经验估计,并不把真实分布定义出来。KL 非负且为零恰好当 P=Q,但通常不对称,也不满足三角不等式,因此不是距离。

这个二元例子直接核对分解和不对称性。辅助函数只服务于给定的、同长度且已归一化分布,Q 在 P 的正概率处也为正;它们没有实现通用分布校验:

python
import math

P = [0.75, 0.25]
Q = [0.5, 0.5]
def entropy_bits(probabilities):
    return -math.fsum(p * math.log2(p) for p in probabilities if p > 0)

def kl_bits(p, q):
    return math.fsum(a * math.log2(a / b) for a, b in zip(p, q) if a > 0)

h = entropy_bits(P)
cross = -math.fsum(p * math.log2(q) for p, q in zip(P, Q))
print(f"{h:.6f} {cross:.6f} {kl_bits(P, Q):.6f} {kl_bits(Q, P):.6f}")
# 0.811278 1.000000 0.188722 0.207519
assert math.isclose(cross, h + kl_bits(P, Q))

若存在 P(x)>0 但 Q(x)=0 的结果,交叉熵和前向 KL 会发散。这解释了分类实现为什么直接处理 logits、使用稳定的 log_softmax,而不是先算出可能舍入为零的概率再取对数。

6. 一个二分类计算 ​

对真实标签 y∈{0,1} 和预测正类概率 q,二元交叉熵为:

L=−[ylog⁡q+(1−y)log⁡(1−q)].
python
import math

def binary_cross_entropy_from_probability(y: int, q: float) -> float:
    if y not in (0, 1):
        raise ValueError("y must be 0 or 1")
    if not 0.0 < q < 1.0:
        raise ValueError("q must lie strictly between 0 and 1")
    return -(y * math.log(q) + (1 - y) * math.log1p(-q))

print(f"{binary_cross_entropy_from_probability(1, 0.9):.6f}")  # 0.105361
print(f"{binary_cross_entropy_from_probability(1, 0.1):.6f}")  # 2.302585

代码使用自然对数,所以单位是 nat;换成以 2 为底才是 bit。机器学习损失用哪种底通常只差一个固定比例,不改变最优点,但会改变数值大小和梯度尺度。

这段概率接口主动拒绝端点;数学上正确类别概率为 1 时损失为 0,真实类别被赋零概率时损失为正无穷。若输入本来就是有限 logit z,可继续运行下面的等价公式,避免先形成饱和到 0 或 1 的概率:

python
def binary_cross_entropy_from_logit(y: int, z: float) -> float:
    if y not in (0, 1) or not math.isfinite(z):
        raise ValueError("require a binary label and a finite logit")
    return max(z, 0.0) - y * z + math.log1p(math.exp(-abs(z)))

print(binary_cross_entropy_from_logit(1, 1000.0))  # 0.0: tiny positive loss underflows
print(binary_cross_entropy_from_logit(0, 1000.0))  # 1000.0

换对数底只把这项损失乘一个正常数;若目标还含未同步缩放的正则项,总目标的相对权重会改变。这里的稳定实现仍有下溢边界,正类 logit 为 1,000 时显示零,不表示精确数学损失等于零。

常见误区 ​

  • 熵就是混乱程度:这是直觉比喻,计算时必须给出随机变量和分布。
  • 低熵数据一定有价值:熵度量不确定性,不度量业务价值或真实性。
  • 交叉熵等于错误率:它会惩罚概率置信度,和 0/1 错误率不是同一指标。
  • KL 散度是对称距离:交换 P 与 Q 通常会改变结果。

练习 ​

  1. 计算均匀四面骰子的熵,并给出最自然的定长编码。
  2. 比较正面概率为 0.5、0.9 和 0.99 的硬币熵。
  3. 构造两个分布,验证 DKL(P∥Q)≠DKL(Q∥P)。
  4. 解释为什么一个压缩器无法保证让每个输入文件都变短。

小结 ​

自信息度量一次结果的意外程度,熵是平均自信息,条件熵和互信息刻画变量共享的不确定性。交叉熵与 KL 散度把概率模型的偏差转化为可优化目标,但它们都依赖明确的分布和数值实现。

下一课把注意力从“需要多少比特表达消息”转向“有限比特怎样近似实数”。

Built with VitePress | Software Systems Atlas