首页/知识库/信息论基础

信息论基础

✍️ AI Master📅 创建 2026-04-12📖 18 min 阅读
💡

文章摘要

从信息熵到 KL 散度,理解信息论在 AI 中的核心应用

1信息的度量——从不确定性的减少说起

信息论由香农于 1948 年创立,其核心思想是将信息量化。一个事件的信息量与其发生概率成反比:越不可能发生的事件,发生时带来的信息量越大。具体来说,事件 x 的自信息定义为 I(x) = -log P(x)。当底数取 2 时,单位为比特(bit);取 e 时,单位为纳特(nat)。举例来说,抛一枚均匀硬币,正面朝上的信息量为 -log2(0.5) = 1 bit;而投掷一个均匀六面骰子,出现 3 点的信息量为 -log2(1/6) ≈ 2.585 bit。信息量的可加性是其重要性质:两个独立事件同时发生的信息量等于各自信息量之和。

python
import numpy as np

def self_information(prob: float, base: float = 2) -> float:
    """计算单个事件的自信息"""
    if prob <= 0 or prob > 1:
        raise ValueError("Probability must be in (0, 1]")
    return -np.log(prob) / np.log(base)

print(f"公平硬币正面: {self_information(0.5):.3f} bits")
print(f"骰子出现3点: {self_information(1/6):.3f} bits")
python
# 独立事件信息量的可加性验证
def joint_information(p1: float, p2: float, base: float = 2) -> tuple:
    i1 = self_information(p1, base)
    i2 = self_information(p2, base)
    i_joint = self_information(p1 * p2, base)
    return i1, i2, i_joint, i1 + i2

i1, i2, i_joint, i_sum = joint_information(0.5, 0.25)
print(f"I(x)+I(y)={i_sum:.3f}, I(x,y)={i_joint:.3f}")
assert abs(i_sum - i_joint) < 1e-10  # 可加性成立
事件概率信息量 (bits)

公平硬币正面

0.5

1.000

骰子出现3点

1/6

2.585

双色球头奖

1/1772万

≈24.08

太阳从东边升起

≈1

≈0

理解自信息是理解信息论所有后续概念的基石。

概率为 0 的事件信息量无穷大,计算时需做数值截断。

2信息熵——不确定性的平均度量

信息熵是信息论中最核心的概念,它衡量一个随机变量的平均不确定性。熵的定义为 H(X) = -Σ P(x) log P(x),即所有可能事件自信息的期望值。熵越大,系统的不确定性越高;熵为零表示系统完全确定。对于伯努利分布(如抛硬币),当 p = 0.5 时熵最大,为 1 bit。多元随机变量的联合熵 H(X,Y) 衡量两个变量联合的不确定性,而条件熵 H(Y|X) 衡量已知 X 后 Y 的剩余不确定性。熵的链式法则 H(X,Y) = H(X) + H(Y|X) 是推导许多信息论恒等式的基础。在机器学习中,交叉熵损失函数正是源于对信息熵的扩展应用。

python
def entropy(probs: list[float], base: float = 2) -> float:
    """计算离散分布的信息熵"""
    probs = np.array(probs)
    assert np.isclose(probs.sum(), 1.0), "Probabilities must sum to 1"
    probs = probs[probs > 0]  # 过滤零概率
    return -np.sum(probs * np.log(probs) / np.log(base))

# 伯努利分布的熵
print(f"p=0.5: H={entropy([0.5, 0.5]):.3f} bits")
print(f"p=0.9: H={entropy([0.9, 0.1]):.3f} bits")
print(f"p=1.0: H={entropy([1.0, 0.0]):.3f} bits")
python
# 验证: 均匀分布的熵最大
def max_entropy_check(n_categories: int) -> float:
    uniform = [1.0 / n_categories] * n_categories
    skewed = [0.9] + [0.1 / (n_categories - 1)] * (n_categories - 1)
    return entropy(uniform), entropy(skewed)

for n in [2, 4, 10, 100]:
    h_uni, h_ske = max_entropy_check(n)
    print(f"n={n}: 均匀={h_uni:.3f}, 偏斜={h_ske:.3f}, 理论=log2({n})={np.log2(n):.3f}")
分布类型参数熵公式最大值

伯努利

p

-p log p - (1-p) log (1-p)

1 bit (p=0.5)

均匀分布

n 类

log2(n)

log2(n)

泊松分布

λ

≈ 0.5 log(2πeλ)

随 λ 增长

记住: 熵不是关于某个具体值的不确定性,而是整个分布的平均不确定性。

实际计算中 log(0) 会返回 -inf,务必先过滤零概率项。

3交叉熵——衡量用错误分布编码的代价

交叉熵 H(P, Q) = -Σ P(x) log Q(x) 衡量用分布 Q 来编码服从分布 P 的数据所需的平均比特数。当 Q = P 时,交叉熵退化为信息熵 H(P)。当 Q 与 P 差异越大时,交叉熵越大,意味着编码效率越低。交叉熵与信息熵和 KL 散度之间存在关系 H(P, Q) = H(P) + D_KL(P || Q)。在机器学习中,交叉熵是最常用的分类损失函数。对于二分类问题,二元交叉熵为 -[y log p + (1-y) log (1-p)],其中 y 是真实标签,p 是模型预测的概率。多分类问题中,交叉熵衡量模型预测的概率分布与真实独热编码分布之间的差距。

python
def cross_entropy(p_true: list[float], q_pred: list[float]) -> float:
    """计算两个分布之间的交叉熵"""
    p = np.array(p_true)
    q = np.array(q_pred)
    q = np.clip(q, 1e-15, 1.0)  # 数值稳定
    return -np.sum(p * np.log(q))

# 真实分布 vs 预测分布
p_true = [0.7, 0.2, 0.1]
q_good = [0.65, 0.25, 0.10]  # 接近真实
q_bad  = [0.2, 0.3, 0.5]     # 偏离真实
print(f"H(P,Q_good)={cross_entropy(p_true, q_good):.4f}")
print(f"H(P,Q_bad)={cross_entropy(p_true, q_bad):.4f}")
python
# 二分类交叉熵损失
def binary_cross_entropy(y_true: float, y_pred: float) -> float:
    eps = 1e-15
    y_pred = np.clip(y_pred, eps, 1 - eps)
    return -(y_true * np.log(y_pred) + (1 - y_true) * np.log(1 - y_pred))

# 不同预测质量的损失
for pred in [0.5, 0.7, 0.9, 0.99]:
    loss = binary_cross_entropy(1.0, pred)
    print(f"y=1, p={pred:.2f}, BCE={loss:.4f}")
场景P(真实)Q(预测)交叉熵

完美预测

[1,0,0]

[1,0,0]

0.000

接近预测

[0.7,0.2,0.1]

[0.65,0.25,0.10]

0.693

错误预测

[0.7,0.2,0.1]

[0.2,0.3,0.50]

1.446

训练神经网络时,最小化交叉熵等价于最大化数据的对数似然。

交叉熵不对称: H(P,Q) ≠ H(Q,P),注意参数的顺序。

4KL 散度(相对熵)——分布差异的度量

KL 散度 D_KL(P || Q) = Σ P(x) log(P(x)/Q(x)) 衡量用分布 Q 近似真实分布 P 时所损失的信息量。它是交叉熵与熵之差:D_KL(P || Q) = H(P, Q) - H(P)。KL 散度具有非负性(吉布斯不等式),当且仅当 P = Q 时为零。然而 KL 散度不是距离度量,因为它不满足对称性和三角不等式。在机器学习中,KL 散度广泛应用于变分推断(VAE)、策略优化(PPO 中的 KL 约束)、知识蒸馏等领域。JS 散度是 KL 散度的对称化版本,定义为 JS(P||Q) = 0.5 D_KL(P||M) + 0.5 D_KL(Q||M),其中 M = (P+Q)/2。

python
def kl_divergence(p: list[float], q: list[float]) -> float:
    """计算 KL 散度 D_KL(P || Q)"""
    p = np.array(p)
    q = np.clip(np.array(q), 1e-15, 1.0)
    return np.sum(p * np.log(p / q))

p = [0.5, 0.3, 0.2]
q1 = [0.5, 0.3, 0.2]  # 相同
q2 = [0.3, 0.4, 0.3]  # 不同
print(f"D_KL(P||P) = {kl_divergence(p, p):.6f}")
print(f"D_KL(P||Q) = {kl_divergence(p, q2):.6f}")
print(f"D_KL(Q||P) = {kl_divergence(q2, p):.6f}")
python
# JS 散度——对称化的分布差异度量
def js_divergence(p: list[float], q: list[float]) -> float:
    p, q = np.array(p), np.array(q)
    m = 0.5 * (p + q)
    return 0.5 * kl_divergence(p, m) + 0.5 * kl_divergence(q, m)

# 比较 KL 散度的非对称性与 JS 散度的对称性
print(f"KL(P||Q)={kl_divergence(p,q2):.4f}, KL(Q||P)={kl_divergence(q2,p):.4f}")
print(f"JS(P||Q)={js_divergence(p,q2):.4f} (对称)")
性质KL 散度JS 散度欧氏距离

非负性

是 (≥0)

是 (≥0)

是 (≥0)

对称性

三角不等式

为零条件

P=Q

P=Q

P=Q

PPO 算法中用 KL 散度约束新旧策略的差异,防止策略更新过大导致崩溃。

当 Q(x)=0 而 P(x)>0 时,KL 散度为无穷大,训练中需确保 Q 的支撑集覆盖 P。

5互信息——变量间的统计依赖关系

互信息 I(X;Y) = Σ P(x,y) log(P(x,y) / (P(x)P(y))) 衡量两个随机变量之间的相互依赖程度。它可以理解为知道 Y 后 X 不确定性的减少量:I(X;Y) = H(X) - H(X|Y) = H(Y) - H(Y|X)。互信息也是联合分布与边缘分布乘积之间的 KL 散度:I(X;Y) = D_KL(P(X,Y) || P(X)P(Y))。当 X 与 Y 独立时,互信息为零;依赖越强,互信息越大。归一化互信息 NMI = I(X;Y) / sqrt(H(X)H(Y)) 将互信息映射到 [0,1] 区间。在特征选择中,互信息用于衡量特征与目标变量的相关性,比皮尔逊相关系数更通用,因为它能捕捉非线性关系。

python
def mutual_information(joint: np.ndarray) -> float:
    """从联合分布计算互信息"""
    joint = joint / joint.sum()  # 归一化
    p_x = joint.sum(axis=1)
    p_y = joint.sum(axis=0)
    outer = np.outer(p_x, p_y)
    mi = 0.0
    for i in range(joint.shape[0]):
        for j in range(joint.shape[1]):
            if joint[i,j] > 0 and outer[i,j] > 0:
                mi += joint[i,j] * np.log2(joint[i,j] / outer[i,j])
    return mi

joint = np.array([[0.4, 0.1], [0.1, 0.4]])
print(f"I(X;Y) = {mutual_information(joint):.4f} bits")
python
# 互信息与熵的关系验证
def conditional_entropy(joint: np.ndarray, axis: int = 1) -> float:
    joint = joint / joint.sum()
    marginal = joint.sum(axis=axis, keepdims=True)
    cond = np.where(joint > 0, joint * np.log2(joint / marginal), 0)
    return -cond.sum()

joint = np.array([[0.4, 0.1], [0.1, 0.4]])
h_x = entropy(joint.sum(axis=1))
h_xy = entropy(joint.flatten())
h_y_given_x = conditional_entropy(joint)
print(f"H(X)={h_x:.3f}, H(X,Y)={h_xy:.3f}")
print(f"I(X;Y)=H(X)-H(X|Y)={h_x - h_y_given_x:.3f}")
关系公式含义

互信息定义

I(X;Y) = H(X) - H(X|Y)

Y 减少 X 的不确定性

对称性

I(X;Y) = I(Y;X)

相互的信息量相同

KL 散度视角

D_KL(P(X,Y)||P(X)P(Y))

联合分布与独立假设的偏差

链式法则

I(X;Y,Z) = I(X;Z) + I(X;Y|Z)

多变量互信息分解

互信息能捕捉任意形式的统计依赖,而不仅仅是线性关系,这在特征选择中非常有用。

互信息的估计在高维空间中非常困难,样本量不足时容易高估。

6交叉熵在分类中的应用——从理论到实践

交叉熵是分类任务中最常用的损失函数。在二分类中,Sigmoid 输出配合二元交叉熵损失构成标准配置。在多分类中,Softmax 输出配合分类交叉熵损失是最常见的组合。交叉熵损失的梯度形式简洁:对于 softmax + 交叉熵,梯度恰好等于预测概率与真实标签的差值 (p - y),这使得反向传播极为高效。标签平滑(Label Smoothing)是一种正则化技术,将独热编码的硬标签替换为软化标签,例如将 [1, 0, 0] 替换为 [0.9, 0.05, 0.05],这可以防止模型过度自信并提升泛化能力。加权交叉熵则用于处理类别不平衡问题,给少数类赋予更高的损失权重。

python
def softmax_cross_entropy(logits: np.ndarray, labels: np.ndarray) -> tuple:
    """Softmax + 交叉熵损失,返回损失和梯度"""
    exp_logits = np.exp(logits - np.max(logits))  # 数值稳定
    probs = exp_logits / exp_logits.sum()
    loss = -np.sum(labels * np.log(probs + 1e-15))
    grad = probs - labels  # 简洁的梯度形式
    return loss, grad

logits = np.array([2.0, 1.0, 0.1])
labels = np.array([1.0, 0.0, 0.0])
loss, grad = softmax_cross_entropy(logits, labels)
print(f"Loss: {loss:.4f}")
print(f"Gradient: {grad}")
python
# 标签平滑实现
def label_smoothing(labels: np.ndarray, alpha: float = 0.1) -> np.ndarray:
    """将独热编码标签平滑化"""
    k = len(labels)
    return (1 - alpha) * labels + alpha / k

original = np.array([1.0, 0.0, 0.0])
smoothed = label_smoothing(original, alpha=0.1)
print(f"原始: {original}")
print(f"平滑: {smoothed}")
print(f"平滑后交叉熵: {cross_entropy(smoothed, [0.8, 0.1, 0.1]):.4f}")
损失函数变体适用场景特点

二元交叉熵

二分类

Sigmoid + BCE

分类交叉熵

多分类

Softmax + CCE

加权交叉熵

类别不平衡

少数类权重更高

标签平滑交叉熵

防止过拟合

软化硬标签

Softmax + 交叉熵的组合梯度极其简洁,这就是为什么它成为分类任务的标准配置。

极端类别不平衡时,标准交叉熵会被多数类主导,需要使用 Focal Loss 或加权交叉熵。

7实战——计算信息量与决策树信息增益

信息增益是决策树算法(如 ID3、C4.5)的核心分裂标准。信息增益 IG = H(D) - Σ |Dv|/|D| H(Dv),即用特征分裂后熵的减少量。信息增益越大,说明该特征对分类越有效。C4.5 使用信息增益比来克服信息增益偏向多值特征的缺陷。基尼不纯度是另一种常用的分裂标准,与熵在形式上不同但目的一致。在实战中,我们首先计算数据集的总熵,然后对每个候选特征计算条件熵,信息增益最大的特征被选为分裂节点。这个过程递归进行直到满足停止条件。理解这些概念对于深入理解决策树、随机森林等集成方法至关重要。

python
from collections import Counter

def calc_entropy(labels: list) -> float:
    """计算数据集的经验熵"""
    total = len(labels)
    counts = Counter(labels)
    probs = [c / total for c in counts.values()]
    return entropy(probs)

def information_gain(labels: list, feature: list) -> float:
    """计算特征的信息增益"""
    h_parent = calc_entropy(labels)
    total = len(labels)
    feature_vals = set(feature)
    h_children = 0.0
    for val in feature_vals:
        subset = [labels[i] for i in range(total) if feature[i] == val]
        h_children += (len(subset) / total) * calc_entropy(subset)
    return h_parent - h_children

# 示例数据集
labels =   ["Y","Y","Y","N","N","Y","Y","N","N","N"]
feature1 = ["A","A","A","A","B","B","B","B","B","B"]
print(f"IG(feature1) = {information_gain(labels, feature1):.4f}")
python
# 信息增益 vs 信息增益比
def gain_ratio(labels: list, feature: list) -> float:
    ig = information_gain(labels, feature)
    split_info = calc_entropy(feature)  # 特征本身的熵
    return ig / split_info if split_info > 0 else 0.0

# 比较多值特征 vs 少值特征
high_card = [f"x{i}" for i in range(10)]  # 10个唯一值
binary_feat = ["A","A","A","A","A","B","B","B","B","B"]

print(f"高基数: IG={information_gain(labels, high_card):.4f}, GR={gain_ratio(labels, high_card):.4f}")
print(f"二值: IG={information_gain(labels, binary_feat):.4f}, GR={gain_ratio(labels, binary_feat):.4f}")
分裂标准公式优点缺点

信息增益

H(D) - H(D|A)

直观

偏向多值特征

增益比

IG / H(A)

修正偏向

可能偏向少值特征

基尼不纯度

1 - Σ p^2

计算快

对极端概率不敏感

实际应用中 C4.5 和 CART 的增益比和基尼指数比单纯信息增益更稳健。

信息增益对唯一值特征(如 ID)会给出最大值,但这会导致严重的过拟合。

继续你的 AI 学习之旅

浏览更多 AI 知识库文章,或者探索 GitHub 上的优质 AI 项目