Please enable Javascript to view the contents

KL Divergence 为什么不是距离:方向一换,问题就变了

 ·  ☕ 7 分钟 · 👀... 阅读
Read aloud Ready
0/0

KL divergence 最容易被误读成“两个分布之间的距离”。这个说法有一半对:它确实在比较两个分布;但另一半很危险:KL 不是距离,因为方向有意义。 更准确地说,KL 虽然满足非负性,而且只有 \(P=Q\) 时才为 0,却不满足 metric 要求的对称性和三角不等式。

如果你只记一个公式:

$$D_{\mathrm{KL}}(P\Vert Q)=\sum_x P(x)\log\frac{P(x)}{Q(x)}$$

不要把它读成“P 和 Q 的距离”。更好的读法是:

如果真实/目标分布是 \(P\),但我用 \(Q\) 去近似它,会多付出多少编码代价。

这句话里,\(P\) 和 \(Q\) 的角色不对称:

  • \(P\):决定哪些位置重要,因为求和时用 \(P(x)\) 加权;
  • \(Q\):被拿来近似 \(P\),如果它在 \(P\) 重要的位置给太低概率,就会被惩罚。
Figure 1: KL 的方向不是符号细节,而是在问两个不同问题:Q 能不能覆盖 P 关心的地方,或者 P 能不能解释 Q 实际使用的地方。

Figure 1: KL 的方向不是符号细节,而是在问两个不同问题:Q 能不能覆盖 P 关心的地方,或者 P 能不能解释 Q 实际使用的地方。

这篇文章接在 Entropy、Cross Entropy 和 KL DivergenceLoss Function:模型到底在优化什么 后面看最自然:前者建立编码代价视角,后者解释 KL 在 loss 家族中的位置。本文再把这套直觉带到 SFT 和 RLHF 的 KL penalty。

先从编码代价看 KL

假设一个事件只有两个结果:A 和 B。真实分布是:

$$P=[0.5,\ 0.5]$$

这表示 A、B 都常见。现在你用另一个分布 \(Q\) 来编码它:

$$Q=[0.99,\ 0.01]$$

\(Q\) 几乎认定 A 会发生,B 很少发生。问题来了:如果真实世界其实是 \(P\),你用 \(Q\) 的信念去编码,会发生什么?

B 在真实世界里有 50% 概率,但 \(Q\) 只给它 1% 概率。也就是说,\(Q\) 严重低估了一个经常出现的事件。KL 会重罚这种错误。

手算一下,使用自然对数:

$$D_{\mathrm{KL}}(P\Vert Q)=0.5\log\frac{0.5}{0.99}+0.5\log\frac{0.5}{0.01}\approx 1.61$$

反过来:

$$D_{\mathrm{KL}}(Q\Vert P)=0.99\log\frac{0.99}{0.5}+0.01\log\frac{0.01}{0.5}\approx 0.64$$

同一对分布,两个方向不一样。原因不是公式怪,而是两个方向在问不同问题:

  • \(D_{\mathrm{KL}}(P\Vert Q)\):如果 \(P\) 是目标,\(Q\) 漏掉了 \(P\) 认为常见的 B,代价很大。
  • \(D_{\mathrm{KL}}(Q\Vert P)\):如果 \(Q\) 是目标,\(Q\) 自己几乎只关心 A,而 \(P\) 至少也给 A 50% 概率,代价没那么大。

所以 KL 的第一个参数不是“随便放左边的分布”,而是拿来加权错误的位置

零概率会让方向差异更明显

再看一个极端例子:

$$P=[0.5,\ 0.5],\quad Q=[1.0,\ 0.0]$$

\(Q\) 认为 B 完全不可能发生。但 \(P\) 认为 B 有 50% 概率。于是:

$$D_{\mathrm{KL}}(P\Vert Q)=\infty$$

因为公式里有 \(\log(P(B)/Q(B))\),而 \(Q(B)=0\)。这不是数值小问题,而是语义问题:你用一个认为 B 不可能的编码系统,去编码一个经常出现 B 的真实世界,代价无限大。

但反过来:

$$D_{\mathrm{KL}}(Q\Vert P)=1.0\log\frac{1.0}{0.5}+0.0\log\frac{0.0}{0.5}\approx 0.69$$

这里按极限约定把 \(0\log(0/q)\) 记为 0。\(Q\) 自己只会产生 A,而 \(P\) 给 A 的概率不是 0,所以没有无限大问题。

这个例子给出一个实用判断:

KL 的左边分布在哪里有质量,右边分布就必须在那里也给足够概率。右边漏掉左边的支持集,会非常严重。

和 Cross Entropy、SFT 是什么关系

KL、entropy、cross entropy 的关系是:

$$H(P,Q)=H(P)+D_{\mathrm{KL}}(P\Vert Q)$$

其中:

  • \(H(P)\):真实分布 \(P\) 自己的不可避免编码代价;
  • \(H(P,Q)\):真实分布是 \(P\),但使用 \(Q\) 编码的平均代价;
  • \(D_{\mathrm{KL}}(P\Vert Q)\):因为用了错误的 \(Q\),额外多付出的代价。

训练分类模型时,数据分布 \(P_{\text{data}}\) 是固定的,模型分布 \(Q_\theta\) 会变。最小化 cross entropy:

$$H(P_{\text{data}}, Q_\theta)$$

等价于最小化:

$$D_{\mathrm{KL}}(P_{\text{data}}\Vert Q_\theta)$$

因为 \(H(P_{\text{data}})\) 不依赖模型参数。这个方向很重要:模型必须在数据真实出现的位置给足概率。比如训练集中某个类别经常出现,模型就不能把它概率压到接近 0。

语言模型 next-token prediction 也是同一个结构。训练数据给出真实 token,cross entropy 让模型在这些真实 token 上提高概率。它本质上是让模型分布覆盖数据分布,而不是让模型只挑自己最喜欢的一小块模式。

SFT 也是这个方向。给定 prompt \(x\) 和人工答案 token 序列 \(y_{1:T}\),经验数据分布可以看成在每个位置把概率质量放在真实 token 上的 one-hot 分布。对每个位置来说,SFT 最小化的是 \(-\log \pi^{\theta}(y^{(t)}\mid x,y^{(1:t-1)})\),再把所有 token 位置加起来。

也就是最小化经验分布到模型分布的 cross entropy,等价于一个 data-to-model 方向的 KL:\(D_{\mathrm{KL}}(P^{\mathrm{data}}\Vert \pi^{\theta})\)。

如果你听说“SFT 和 RL 里的 KL 方向是反的”,通常指的是这个对比:SFT 里数据/教师答案在左边,当前模型在右边;而 RLHF 的 KL penalty 常把当前 policy 放左边,reference policy 放右边。 前者问“模型有没有给数据答案足够概率”,后者问“当前模型实际采样出来的回答有没有跑到 reference 不认可的地方”。

两个方向的直觉:mode-covering 和 mode-seeking

在机器学习里,经常会粗略说:

  • \(D_{\mathrm{KL}}(P\Vert Q)\):更 mode-covering,希望 \(Q\) 覆盖 \(P\) 的主要模式;
  • \(D_{\mathrm{KL}}(Q\Vert P)\):更 mode-seeking,希望 \(Q\) 集中在 \(P\) 的高概率区域。

这里的 mode 可以理解成“概率分布的峰”。比如目标分布 \(P\) 有两个高峰:一个在左边,一个在右边。

如果优化 \(D_{\mathrm{KL}}(P\Vert Q)\),\(P\) 在两个峰上都有质量,\(Q\) 漏掉任何一个峰都会被 \(P\) 加权惩罚。所以 \(Q\) 倾向于覆盖两个峰,即使中间区域也给了一些概率。

如果优化 \(D_{\mathrm{KL}}(Q\Vert P)\),求和时由 \(Q\) 加权。只要 \(Q\) 自己别把质量放到 \(P\) 很低的地方,它可以集中在其中一个峰上。它不一定被强迫覆盖 \(P\) 的所有峰。

Figure 2: 连续分布里更容易看出两个方向的差异:forward KL 更怕漏掉 P 的峰,reverse KL 更怕 Q 把质量放到 P 很低的谷底。

Figure 2: 连续分布里更容易看出两个方向的差异:forward KL 更怕漏掉 P 的峰,reverse KL 更怕 Q 把质量放到 P 很低的谷底。

这个说法不是定理的全部,但作为工程直觉很有用:

方向更怕什么错误常见行为
\(D_{\mathrm{KL}}(P\Vert Q)\)\(Q\) 漏掉 \(P\) 常见的东西覆盖目标分布
\(D_{\mathrm{KL}}(Q\Vert P)\)\(Q\) 把质量放到 \(P\) 很低的地方集中到高概率模式

回到 RLHF:KL penalty 在约束什么

在 RLHF 或 PPO 风格的 language model training 里,常见写法是:

$$\underset{\theta}{\max}\ \mathbb{E}^{y\sim\pi^{\theta}(\cdot\mid x)}[r(x,y)]-\beta D_{\mathrm{KL}}(\pi^{\theta}(\cdot\mid x)\Vert\pi^{\mathrm{ref}}(\cdot\mid x))$$

这里:

  • \(\pi_\theta\):正在训练的新 policy,也就是当前语言模型;
  • \(\pi_{\mathrm{ref}}\):冻结的 reference policy,通常来自 SFT 模型;
  • \(r(x,y)\):reward model 或 verifier 给回答的分数;
  • \(\beta\):KL penalty 的强度。

这个方向可以读成:

当前模型实际会采样出来的回答,不要离 reference 模型太远。

为什么是这个方向?因为 RL rollout 本来就是从当前 policy \(\pi_\theta\) 采样的。训练时看到的是当前模型真的会输出的 token 轨迹,于是可以对这些轨迹计算:

$$\log \pi_\theta(y\mid x)-\log \pi_{\mathrm{ref}}(y\mid x)$$

如果当前模型开始输出 reference 模型认为极不自然的句子,这一项会变大,KL penalty 就会把它拉回来。

如果反过来写成 \(D_{\mathrm{KL}}(\pi_{\mathrm{ref}}\Vert\pi_\theta)\),语义会变成:

reference 模型会输出的东西,当前模型也要覆盖。

这不是没有意义,但它约束的是另一个问题。它会更像“不要丢掉 reference 的覆盖面”,而不是“当前模型别跑到 reference 不认可的奇怪区域”。在 PPO/RLHF 的采样流程里,我们主要观察当前 policy 的轨迹,所以 \(D_{\mathrm{KL}}(\pi_\theta\Vert\pi_{\mathrm{ref}})\) 更自然,也更容易用采样估计。

为什么 KL penalty 不能太小,也不能太大

KL penalty 的作用像一根绳子:

  • 绳子太松:模型可能钻 reward model 的漏洞,变得啰嗦、投机、格式怪异,甚至损坏通用能力;
  • 绳子太紧:模型几乎不能偏离 SFT reference,RL 学不到偏好或可验证奖励带来的改进。

所以 RLHF 里经常要调 \(\beta\),或者使用 adaptive KL controller,让实际 KL 维持在某个目标范围附近。

这也解释了为什么 SFT 和 RLHF 是配合关系:SFT 先给出一个合理 reference,RLHF 再在它附近优化偏好。如果 reference 本身很差,KL penalty 只会把模型拴在一个不好的区域;如果没有 KL penalty,RL 又容易把模型推到 reward model 的盲区。

一句话总结

KL divergence 的核心不是“两个分布差多少”,而是:

我把左边分布当成真实/目标,用右边分布去近似它,会在左边认为重要的位置犯多少错误。

所以:

  • \(D_{\mathrm{KL}}(P\Vert Q)\) 惩罚 \(Q\) 漏掉 \(P\) 关心的地方;
  • \(D_{\mathrm{KL}}(Q\Vert P)\) 惩罚 \(Q\) 自己跑到 \(P\) 不支持的地方;
  • cross entropy 训练常等价于最小化 \(D_{\mathrm{KL}}(P_{\text{data}}\Vert Q_\theta)\);
  • RLHF 的 KL penalty 常用 \(D_{\mathrm{KL}}(\pi_\theta\Vert\pi_{\mathrm{ref}})\),是在约束当前 policy 的实际输出别偏离 reference 太远。

一旦你把“谁在左边,谁负责加权”记住,KL 的方向就不再是符号细节,而是训练目标本身。

延伸阅读

分享