https://x.com/i/article/2116301274433961984
Reward model:Bradley-Terry 的倒影
Reward Model, 选择变成分数
上一篇里,judge 读完 Is OpenAI open? 的各个回答,给每个回答打了分。
OpenAI is open. It's in the name.得 2 分,以 partly 开头的回答得 8 分或 10 分。
RL 再用这些分数调整模型。如下图:十个回答落在各自的分数上,以 open 开头的回答分数都低,两个以 partly 开头的回答得了 10 分和 8 分。
问题来了:这些分数从哪里来?
在 RLHF(基于人类反馈的强化学习)里,judge 是一个 reward model,也就是一个神经网络:读入一个问题和一个回答,输出一个数。这个数没有人手写,人提供的只是选择。标注者看到问题和两个回答:
- A:Partly. Some weights are open.
- B:Yes. OpenAI is open. It's in the name.
截至本文写作时,A 是准确的:OpenAI 以 Apache 2.0 许可发布了开放权重的模型 gpt-oss-120b 和 gpt-oss-20b,它的旗舰模型则只能通过 API 和自家应用使用。标注者选了 A。
这次选择存成一行数据:问题、被选中的回答(chosen)、被淘汰的回答(rejected)。成千上万个问题、成千上万行这样的数据,就组成了 reward model 的训练集。
本文从这一行数据出发,一步步讲到最后的分数:
- 选择怎样变成损失;
- 损失怎样改变网络;
- 网络长什么样;
- 它在训练中还学到了什么。
这是后训练系列的第五篇。
reward model 从没见过人给的分数。它只知道两个回答里哪个被选中,要自己找出能解释这些选择的分数。
只比较,不打分
打分(比如满分 10 分给 7 分)要求每个标注者用同一套标准,而每个人的标准不一样:这个人的 7 分,可能是那个人的 5 分。两个回答二选一,就不需要统一的分数标准。
二选一对标注者的要求也更低。判断哪个回答更好,只需要对结果有看法,不需要自己写得出好回答。
偏好数据有几种形式:
- 两个回答选一个;
- N 个回答选最好的一个;
- 给 N 个回答排序。
标签可以来自受过训练的标注者,可以来自 Reddit、StackExchange 上的投票,也可以让一个强大的 LLM 来比较:UltraFeedback 用的是 GPT-4。
选择也有噪声:遇到难分高下的两个回答,标注者会意见不一;他们也会点错,也会疲劳。InstructGPT 的标注者之间,大约 73% 的比较结果一致。
这个一致率就是 reward model 的上限:人与人之间大约四对里只有三对意见一致,模型预测新的选择,准确率也很难超过这个水平。
Bradley–Terry:分差变成概率
数据里只记录了一件事:A 胜过 B。训练却需要数字。1952 年提出的 Bradley–Terry 模型把两者连了起来:给每个回答一个分数,A 被选中的概率只取决于两个分数之差。
P(A 胜过 B) = σ(s_A − s_B) = 1 / (1 + e^−(s_A − s_B))
σ 是 sigmoid 函数,把任何分差变成 0 到 1 之间的概率:
- 分差 −2 → 0.12
- 分差 0 → 0.50
- 分差 1 → 0.73
- 分差 3 → 0.95
由此可以得出三点。
只有分差有意义。 2 分对 1 分,和 102 分对 101 分,给出的概率都是 0.73。单独一个分数说明不了什么,要和同一问题下另一个回答的分数放在一起才有意义。
概率永远到不了 0 或 1。 再好的回答也会偶尔落选,这和真实的标注情况一致。
分数可以从选择中反推出来。 假设 10 个人比较 A 和 B,7 个人选了 A。最能解释这个结果的分差满足 σ(分差) = 0.7,即分差 = log(7/3) ≈ 0.85。国际象棋的 Elo 等级分用的是同一个思路:用同样的 S 形曲线,把等级分之差换算成预期胜率。
reward model 是一个网络 r(x, y),给出问题 x 下回答 y 的分数。训练要回答的问题是:对于标注者实际做出的选择,模型给了多大的概率?A 被选中时,这个概率是 σ(r_A − r_B),损失就是它的负对数:
L = −log σ(r(x, chosen) − r(x, rejected))
这就是训练 next-token 预测用的 cross-entropy loss(交叉熵损失)。在 next-token 预测里,它是模型给实际下一个 token 的概率取负对数;在这里,是模型给实际选择的概率取负对数。分差 −1 时损失为 1.31,分差 0 时为 0.69,分差 1 时为 0.31,分差 3 时为 0.05。每一对有标签的数据都能算出损失,不管模型眼下把这一对排对了还是排错了。
这个模型有三个假设:
- 所有回答排在同一条分数轴上,所以 A 胜 B、B 胜 C、C 又胜 A 这样的循环偏好无法表示;
- 没有平局;
- 所有标注者用同一个标准。
把损失降下来:gradient descent
损失衡量模型错了多少,gradient descent(梯度下降) 让损失变小。对每个权重,梯度表示这个权重增大一点时损失会增加多少。把每个权重都朝梯度的反方向挪一小步,损失就下降:
w ← w − lr · ∇L
lr 是学习率,决定每一步的大小。
用一个最小的 reward model 来看这个过程:一个只有两个权重的打分器,它只看回答的两个特征。
- 特征 1:回答陈述了事实,取 1。
- 特征 2:回答拿名字当理由,取 1。
于是 A 的特征是 (1, 0),B 是 (0, 1)。分数 = w · f,即权重与特征逐项相乘再相加。对 Bradley–Terry 损失,梯度是:
∇L = −(1 − σ(分差)) · (f_A − f_B)
1 − σ(分差) 决定推力的大小。从 w = (0, 0)、lr = 1 开始:
第 1 步。 两个回答都得 0 分,分差 0,σ = 0.5,损失 0.693。推力 0.5,f_A − f_B = (1, −1),w 变成 (0.5, −0.5)。陈述事实开始加分,拿名字当理由开始减分。
第 2 步。 A 得 0.5 分,B 得 −0.5 分,分差 1.0,σ = 0.731,损失 0.313。推力降到 0.269,因为这一对已经基本排对了。
每一步都把被选中回答的分数抬高,把被淘汰回答的分数压低,幅度相同。在大量数据上,梯度主要来自模型还排错的那些对。
推力 1 − σ(分差):模型把一对排错时很大,排对并拉开差距后就变小。
真实的 reward model 有几亿个权重,更新规则相同。下面几节讲这些权重是什么。
Chat LLM 与 Reward Model 的不同
reward model 从一个现成的 LLM 出发,通常就是它将来要评判的那个 chat model 的某个 checkpoint。
它保留模型的主体,也就是 embedding 表和各层 transformer,只换掉最后一层。
以小型开源模型 Qwen3-0.6B 为例。两个模型共用同一个主体:把每个 token 变成 1,024 个数的向量,再经过 28 层。区别只在最后一层。
下面跟着数字走一遍,用 4 个数代替 1,024 个数,向量都是示意值。文本先切成 token ID,每个 ID 取出一个向量,向量依次经过各层。最后一个位置的输出是最终隐藏向量 h。对这个问题,假设 h = (0.5, −1, 2, 0)。
chat model:LM head。 chat model 的最后一层是 LM head,一张 151,936 行的表,词表里每个条目一行,每行 1,024 个数。每一行都和 h 逐项相乘再相加,即点积:Part 那一行得 2.0,Yes 得 1.0,No 得 −1.0,151,936 行各得一个数。这些数就是 logits。softmax 把它们变成概率,选出一个 token ID,detokenizer 把 ID 变回文字,然后进入下一轮,生成下一个 token。
reward model:score head。 reward model 去掉 LM head,换上一个 score head(打分层):只有一行 1,024 个数,随机初始化。这时输入是问题加回答 A,假设它的 h 同样是 (0.5, −1, 2, 0)。h 和唯一的一行 w = (1, −0.5, 0.5, 2) 做点积:0.5 + 0.5 + 1 + 0 = 2.0,这个数就是 A 的分数。回答 B 单独跑一遍,最后得到 h = (1, 0, 0, 0),分数 1.0。
每个 logit 都是一次点积:最终隐藏向量乘以输出层的一行。chat model 的输出层有 151,936 行;reward model 的只有一行,这唯一的 logit 就是分数。
分数之后再没有别的步骤。它原样交给 RL 作为 reward:
没有 softmax,没有 token,也没有 detokenizer。
reward model 写不出文字,因为它没有词表可选;也不需要 请给这个回答打分 之类的指令,输出层本身就规定了它的任务。即使还没训练,它对任何文本也会输出一个数,因为一行权重总会算出一个数;在训练确定权重之前,这个数没有意义。
分数取自哪个位置
score head 会作用在每一个位置上,就像 LM head 在每个位置都给出 151,936 个 logits。一段 40 个 token 的对话,会得到 40 个数。reward 取最后一个真实 token 上的那个数,也就是结束回答的 <|im_end|>,其余的数都丢掉。
取最后一个位置,原因是 LLM 的 attention 是因果的:第 t 个位置的向量,只由第 1 到第 t 个 token 算出。比较两个只差一个词的回答:
- Partly. Some weights are open.
- Partly. All weights are open.
在 Qwen3 的 tokenizer 里,两个回答都是 8 个 token,只在 Some 和 All 处不同。在这个 token 之前,两次运行的向量和数完全相同;从这个 token 开始直到结尾,都不相同。只有最后一个位置读完了整个问题和整个回答,只有它能给整个回答打分。如果取第一个位置,所有回答的分数都会一样。
训练时回答成批处理,较短的行用填充 token 补到同样长度。代码必须读最后一个真实 token 上的分数,不能读到填充位置上。
logits 和 softmax:chat model 与 reward model
chat model 也输出 logits,也用 softmax 把它们变成概率。两种 logits 之间的联系在于:Bradley–Terry 本身就是两个选项上的 softmax。
σ(a − b) = e^a / (e^a + e^b)
左边是分差的 sigmoid,右边是对两个分数做 softmax 后,第一个分数对应的那一项。下面用前面的数字分别看两个模型。
chat model:覆盖整个词表的 softmax。 回答的第一个 token,logits 是 Part 2.0、Yes 1.0、No −1.0,在这三个上做 softmax,得到 0.705、0.259、0.035。真实模型里,softmax 要覆盖全部 151,936 个条目,得到下一个 token 的真实概率,总和为 1。模型从中采样,写出文字。训练时,损失是模型给实际下一个 token 的概率取负对数。
reward model:两个回答之间的 softmax。 A 和 B 各跑一遍,各得一个分数:2.0 和 1.0。两个分数做 softmax,得到 0.731 和 0.269,正是 σ(1)。这个 softmax 只出现在训练的损失里:损失是模型给标注者选中的回答 A 的概率取负对数。使用时没有 softmax,原始分数 2.0 就是 reward。
对照下来,两者的区别在于:哪些候选放在一起比较,以及 softmax 在什么时候计算。
- 候选。 chat model 的 151,936 个 logits 来自同一次运行,在同一个向量里竞争。reward model 的分数来自各自独立的运行,每个回答一次,只在比较时才放到一起。
- 含义。 chat model 的 softmax 给出下一个 token 的真实概率。reward model 的单个分数没有意义,只有同一问题下不同回答之间的分差有意义。
- 用途。 chat model 从 softmax 中采样来写文字。reward model 的 softmax 只用于训练;使用时,输出的就是原始分数。
- 损失。 两者都用 −log softmax(正确选项) 训练。对 chat model,正确选项是实际的下一个 token;对 reward model,是标注者选中的回答。
训练 reward model 和训练 chat model 用的是同一个 cross-entropy loss,只是候选从 151,936 个 token 变成了两个回答。
如果要给两个以上的回答排序,可以用 Plackett–Luce 模型把同一思路推广:先在全部 K 个回答上做 softmax 决定第一名,再在剩下的回答上做 softmax 决定第二名,依此类推。
训练整个网络
训练真实的网络,就是把两个权重时的规则用到所有权重上。Qwen3-0.6B 大约有 5.96 亿个权重,主体和新的输出层一起训练。
- 每一步取一小批数据。每一行的 chosen 和 rejected 回答都经过同一个网络,得到两个分数。
- 损失是这批数据上 Bradley–Terry 损失的平均值。
- 反向传播算出每个权重的梯度,从输出层一直回到全部 28 层。
- 优化器(通常是 AdamW)更新权重。
这是普通的监督学习,和预训练、指令微调用的是同一套机制:数据固定,损失可以求导。整个过程不涉及强化学习。RL 要到后面才出现,那时训练的是 chat model,reward model 负责打分。
InstructGPT 只训练了 reward model 一个 epoch,因为在同样的比较数据上多过几遍就会过拟合。模型在新数据上的准确率远低于 100%,标注者之间的分歧决定了上限。对没有参与训练数据标注的标注者,InstructGPT 的 reward model 预测他们选择的准确率约为 70%。Llama 2 的两个 reward model 在各自测试集上分别达到 63% 和 65%;在标注者标为明显更好的那些对上,分别达到 81% 和 94%。
训练完成后,reward model 被冻结,此后只负责打分。
它在质量之外还学到了什么
reward model 学的是数据里一切能预测选择的东西。大部分符合本意:准确、切题、清楚的回答胜出。也有一部分不符合本意。标签错误分两种,影响不同。
随机噪声。 标注者点错,或者在难分高下的对上随手一选。这类错误不偏向任何特征:有时好的回答落选,有时差的回答胜出。在大量数据上,它们在梯度里相互抵消。模型仍然排对顺序,只是分差变小:它给出的把握程度,低于真实偏好应有的程度。
系统性偏差。 假设一部分标注者偏爱自信的 Yes. OpenAI is open.,而不选谨慎的 Partly.;或者偏爱更长的回答,或者偏爱附和用户的回答。这类错误每次都朝同一个方向,在梯度里不断累积,偏差就成了 reward 的一部分。同类数据再多,也消除不了它。
两种情况在实践中都出现过。一项研究考察了几种设置下的 RLHF,其中两种设置里,chat model 获得的 reward 大部分来自把回答写得更长。另一项研究发现,人和 reward model 有时会更偏爱附和用户错误看法、写得很有说服力的回答,而不选正确的回答。
所以 reward model 只是人们真正想要的东西的一个 proxy,是从人们的选择中学来的。一旦有模型针对它做优化,代理的缺陷就会暴露出来。
为了提高分数而训练的 chat model,会找到分数和质量不一致的地方,比如更长、更讨好的回答。这就是 reward hacking。
reward model 学到的是标注者的选择,连同他们的习惯。针对它过度优化,就会找到它的错误。
它用在哪里
训练好的 reward model 能给任意问题和回答打分,包括标注者从没见过的回答。常见用途有三种:
- RLHF。 chat model 写回答,冻结的 reward model 打分,RL 提高高分回答的概率。KL penalty 让 chat model 留在起点附近,那里 reward model 的分数可靠。这就是上一篇里的 judge。
- 标注。 reward model 比较新的回答,生成新的偏好数据,不再需要人工标注。
- best-of-n。 采样几个回答,留下分数最高的一个。同样的排序也可以用来为指令微调挑选数据。
本系列后面的文章会讲另一半:chat model 怎样用 PPO 和 GRPO 针对 reward model 训练,以及 DPO 怎样直接从同样的偏好数据学习,省掉单独训练的 reward model。
小结
- reward model 从选择中学习:每行数据是一个问题、一个 chosen 回答和一个 rejected 回答。没有人写分数。
- Bradley–Terry 把分差变成概率,σ(r_chosen − r_rejected)。只有分差有意义;损失 −log σ(分差) 就是针对标注者选择的 cross-entropy。
- gradient descent 降低这个损失,w ← w − lr · ∇L。推力 1 − σ(分差) 抬高 chosen 的分数、压低 rejected 的分数,对还排错的对推得最多。
- 每个 logit 都是最终隐藏向量乘以输出层的一行。chat model 有 151,936 行;reward model 只有一行,它唯一的 logit 就是分数。
- 分数取自最后一个真实 token,只有这个位置读完了整个回答。
- Bradley–Terry 是两个候选回答上的 softmax。chat model 和 reward model 用同一个损失训练:−log softmax(正确选项)。
- 训练是监督学习:反向传播加 AdamW,更新全部权重,不涉及 RL。
- 随机的标签噪声降低模型的把握程度,系统性偏差变成 reward 的一部分。reward model 是代理,针对它过度优化,就会暴露它的错误。
