把打分器消掉 — 一次代数上的抵消,和它长出来的一族亲戚
这一章讲三件事: 第 08 章那套流水线里,最贵的那个零件怎么被一次代数演算整个拿掉; 拿掉之后失去了什么;以及从这个想法长出来的一族做法各自在治什么病。
它在全书链条里的位置: 第 07 章造出了打分器,第 08 章拿它去改模型—— 然后交出了一张很难看的账单。这一章是对那张账单的回应。 它也是全书第一次给出「几条路线摆在面前,怎么选」的口径, 而那条口径(举证责任在复杂的那一侧)后面还会被反复引用。
★ 第 08 章那个「减掉一个基线」的动作,在这一章第二次出现, 换了一种做法:不再养一个价值头去估,直接拿同一个提示词下几份回答的平均分当基线。
1. 先看现象:一张难看的账单,和 2023 年那声集体松气
第 08 章那条路线,书自己的定性是这样的:
尽管它在概念上很清楚,它是一个脆弱的算法——对超参数敏感、对基础设施要求高。1
把第 08 章那几节的账单摊开:
| 要养的东西 | 干什么 | 拿掉它会怎样 |
|---|---|---|
| 正在训练的模型 | 写信 | 拿不掉 |
| 参考模型 | 当尺子 | 拿不掉(那是「别走远」的参照物) |
| 打分器 | 打分 | 这一章就是拿掉它 |
| 价值头 | 估「这一回合大概值多少分」 | 本章第 6 节那个亲戚拿掉它 |
| 训练途中的生成 | 一个词一个词写出候选回答 | 这一章连它一起拿掉 |
所以 2023 年斯坦福那篇论文出来的时候,书里的原话是:
当一支团队发表论文,主张我们也许可以完全不必显式地构造一个打分器、 而是直接从偏好里推出训练信号时,那种松了一口气的感觉几乎是普遍的。1
顺带说清楚这一章的另一半来历
这一章讲的不只是那一篇论文。 书在开头并排讲了第二件事:
稍后不久,一支当时还不出名的中国团队描述了它的数学推理模型。 因为出口管制拿不到更强的显卡,团队只能从网络栈到训练范式来一系列聪明的工程变通; 这一章后面要讲的另一种做法,就是那次努力的一部分2。
书用这两件事引出一句总纲,它决定了整章的写法:
在我们工作的这个尺度上,偏好优化既是性能问题, 也同样是速度问题和技术可行性问题。 到了这一层,后训练不只是一个机器学习难题, 还是一个协调资源、管理资源上限、把训练流程跑得高效的问题3。
2. 顶层全景:同一对数据,两条路并排走
同一对偏好数据:提示词 x + 赢的那份 B + 输的那份 A
│
┌────┴─────────────────────────────┬──────────────────────────────┐
│ 第 08 章那条路 │ 这一章这条路 │
├──────────────────────────────────┼──────────────────────────────┤
│ ① 先训一个打分器 │ ① (没有这一步) │
│ ② 让模型当场写几封候选信(慢) │ ② (没有这一步。数据是现成的) │
│ ③ 打分器给它们打分 │ ③ (没有这一步) │
│ ④ 价值头估个基准,相减得优势 │ ④ (没有这一步) │
│ ⑤ 裁剪 + 偏离惩罚,反推挪一步 │ ⑤ 让当前模型和参考模型各前向 │
│ │ 跑一次这两份现成的回答, │
│ │ 算出一个损失,反推挪一步 │
└──────────────────────────────────┴──────────────────────────────┘
图说:**右边这一列少掉的每一格,都不是被工程手段绕过去的,是被一次代数演算消掉的。**
§3 讲那次演算,§4 讲消掉之后打分器去哪儿了,§5 讲代价。
主走查(全章共用): 拿第 07 章那一对具体的候选信 (赢的 B 结尾写「如对本决定有异议,可于 60 日内申请复议」, 输的 A 结尾写「建议您尽快复诊」),在右边这条路上走完一步, 看每一个数具体是多少。 编造的数每次出现都会标明。
3. 机制一:把中间人拿掉的那三步代数
这一节是整章的命门。 它不是一个工程技巧,是一次代数上的巧合, 而这个巧合恰好足够大,足以让整条流水线塌掉一半。
它解决什么问题
书先把问题问出来了:
打分器只是一个中间人。 人通过成对比较表达偏好 → 这些比较被用来训一个打分器 → 打分器给模型的输出打分 → 再拿这些分去改模型。 这条链上每一环都引入计算成本和出错的可能: 打分器可能没能忠 实表示它学的那些偏好,而且更要命的是,它有被对着奖励作弊的风险4。
于是那个显而易见的问题是:能不能把中间人砍掉?5
第一步:那个目标其实有一个闭式的答案
起点就是第 08 章那个目标,一个字都没变:
朝打分器的高分走 同时 别离参考模型太远
这里出现了第一件让人意外的事:这个目标的最优解是可以直接写出来的, 不必靠一步步试着挪。它长这样(我们只交付结构,式子请查原书)6:
最优策略给某份回答的概率
= 参考策略给它的概率
× exp(这份回答的奖励 ÷ β)
÷ 一个归一化常数
逐块读一遍:
| 这一块 | 在说什么 |
|---|---|
| 参考策略给它的概率 | 出发点还是原来的自己 |
| 乘上「奖励除以 β」的指数 | 奖励越高的回答,相对参考策略被抬得越高;β 越小抬得越狠 |
| 除以一个归一化常数 | 一堆概率乘完之后加起来不等于 1 了,除掉这个数把它们拉回成一个合法的概率分布 |
这个归一化常数是后面全部戏剧性的来源,所以要单独说清它是什么:
它是「对这个提示词的所有可能回答」求一个加权和。 「所有可能回答」的数量是天文数字——每个位置几万种 token 选择,乘上几百个位置。 对任何一个现实的语言模型,这个和都算不出来。7
记 住这件事:它算不出来。 下面第三步会处理它。
第二步:把这个关系倒过来解
这一步是整个把戏的核心,书自己称它为「一次值得细品的偷天换日」8:
我们从一个需要打分器的目标出发,推出了「在这个奖励下,最优策略长什么样」, 然后反过来问:如果我们手上已经有了最优策略,是什么样的奖励才会产生它?
倒过来解的结果是9:
某份回答的奖励
= β × log( 最优策略给它的概率 ÷ 参考策略给它的概率 )
+ β × log( 那个归一化常数 )
用大白话读这个式子:
一份回答的奖励, 完全由「当前这个模型比参考模型更偏爱它多少」决定。 模型比参考模型更看好它 → 它的隐含奖励就更高。这是构造出来的,不是推断出来的8。
注意那个算不出来的归一化常数还赖在式子里。 它只依赖提示词,不依赖是哪份回答—— 这一点马上就要救场。
第三步:代回去,那一项恰好抵消
第 07 章那个成对比较的结构还记得吗?「算两份的分差 → 压成一个概率 → 取负对数当损失」。
把上面那个反解出来的奖励,代进这个结构里的「算差」那一步:
赢的那份的奖励 = β × log(策略/参考) + β × log(归一化常数)
输的那份的奖励 = β × log(策略/参考) + β × log(归一化常数)
└────── 完全一样 ── ────┘
相减 → 归一化常数整项消失
为什么完全一样:因为它只依赖提示词 x,而这两份回答共用同一个提示词。
书对这一步的定性没有留余地:
这不是一个次要的技术上的便利。这就是这套做法之所以成立的全部原因。10
抵消之后剩下什么?
只剩下四个对数概率:当前模型给赢的那份和输的那份各打的概率, 参考模型给这两份各打的概率。 而这四个数,只要让两个模型各前向跑一次就能精确算出来。10
这条路线的名字叫 DPO(Direct Preference Optimization, 直接偏好优化——「直接」指的就是跳过打分器,直接从偏好推出训练信号)。 出门看任何一份训练框架的文档都会撞见这三个字母,所以留名。
走查:那一对信在这条路上走一步
提示词 x =「保单 P-4471……请写拒付说明信」
赢的 B:结尾「如对本决定有异议,可于 60 日内申请复议」
输的 A:结尾「建议您尽快复诊」
第 1 步:两个模型各前向跑一次,拿到四个对数概率
参考模型: 给 B −42.0 给 A −41.0 ← 它没学过合规口径,反而觉得 A 更自然
当前模型: 给 B −41.5 给 A −40.0
第 2 步:算每份的「隐式奖励」= β × (当前 − 参考),取 β = 0.2
B: 0.2 × (−41.5 + 42.0) = 0.2 × 0.5 = 0.10
A: 0.2 × (−40.0 + 41.0) = 0.2 × 1.0 = 0.20
← 当前模型现在更偏爱 A。这是错的。
第 3 步:算差 → 压成概率(和第 07 章完全同一个结构)
0.10 − 0.20 = −0.10 → 压成概率 ≈ 0.475
「人会选 B」的概率,按模型现在的看法只有 47.5%
第 4 步:但人确实选了 B → 损失 ≈ 0.74 → 反推参数该往哪挪 → 挪
下一轮:B 的概率相对参考模型被抬高,A 的被压低
★ 对照第 08 章的同一步:那里要先跑打分器、要现场生成候选、要价值头。
这里一个都没有,**只有两次前向**。
(四个对数概率和 β = 0.2 之外的所有数是为演示编的;β 的起手值 0.2 是书里给的。)