图 1:本文封面,从提示到标量分的奖励模型训练闭环。
先说明属性:这是一篇经典题解析,题目出自本地历史面试资料整理,与任何公司的当前招聘无关。RLHF 的经典三步大家都知道个名字:SFT、训练奖励模型、PPO 强化。这道题考的是中间这一步拆开后你知不知道细节——奖励模型(RM)从哪来的、偏好数据长什么样、损失函数为什么那么设计。
一句话结论:RM 就是把 SFT 模型的语言建模头换成一个标量回归头,再用「同一提示下两个回答谁更好」的成对偏好数据,按排序损失训练出来的打分器。
先看模型从哪来:不是从零训,是改造。
RM 的主干直接拿第一步产出的 SFT 模型初始化,去掉输出词表分布的 LM 头,换一个只输出单个标量的回归层。输入是「提示 + 回答」拼成的文本,输出是一个分数 r(x, y)——打分是回归任务,RM 本身不生成任何文本。为什么不从零训一个?主干在 SFT 阶段已经学会读文本、理解指令,评分只需在这个理解之上加一个价值判断头;而且偏好数据靠人工排序,又贵又慢,撑不起从零训练。
再看数据:成对偏好,不是绝对打分。
对提示集合里的每个提示,让 SFT 模型采样出多个候选回答——2022 年发表的 InstructGPT 论文(arXiv:2203.02155)用的配置是每个提示 4 到 9 个——交给标注员按偏好从好到差排序。K 个回答的完整排序可以拆成 C(K, 2) 个偏好对:4 个回答就是 6 对,训练样本就是三元组 (x, y_w, y_l):提示、胜出的回答、被淘汰的回答。一个值得记住的工程细节:论文把同一提示的所有偏好对放进同一个 batch,否则同一回答反复出现容易过拟合。为什么不直接让标注员打 1 到 10 分?人对绝对尺度的把握很不一致,而「A 比 B 好」的相对比较稳定得多,标注质量是整套方案的地基。
最后是损失:分差过 sigmoid,最大化它。
RM 对同一提示分别打出 r(x, y_w) 和 r(x, y_l),把分差送进 sigmoid,σ(r_w − r_l) 就解读为「w 确实比 l 好」的概率,最大化它等价于最小化 L = −E[log σ(r_w − r_l)]。这正是经典的 Bradley–Terry 偏好模型。关键理解:RM 学的是相对排序而不是绝对好坏,损失只关心分差的方向和大小,分数的绝对值没有意义。
用一个最小可运行模型把整条链跑通(Node.js 22.15.0,node rm.js):4 个候选回答各带两个特征,标注排序拆出 6 个偏好对,RM 是线性打分器,从零开始按排序损失梯度下降:
// 最小奖励模型:Bradley-Terry 成对排序损失
const sigmoid = (z) => 1 / (1 + Math.exp(-z));
const F = [[3, 4], [1, 6], [5, 2], [2, 3]]; // 同一提示下 4 个候选回答的特征
// 标注排序 回答2 > 回答0 > 回答3 > 回答1,拆成 C(4,2)=6 个偏好对 (胜, 负)
const pairs = [[2, 0], [2, 1], [2, 3], [0, 1], [0, 3], [3, 1]];
let w = [0, 0]; // RM 参数从零开始
const score = (i) => w[0] * F[i][0] + w[1] * F[i][1];
const loss = () => pairs.reduce((s, [c, r]) =>
s - Math.log(sigmoid(score(c) - score(r))), 0) / pairs.length;
console.log(`训练前: 平均损失 ${loss().toFixed(4)}, 标量分 [${F.map((_, i) => score(i).toFixed(2)).join(', ')}]`);
for (let e = 0; e < 300; e++) {
for (const [c, r] of pairs) {
const d = sigmoid(score(r) - score(c)); // 胜者分越低、败者分越高,梯度越大
w[0] += 0.05 / pairs.length * d * (F[c][0] - F[r][0]);
w[1] += 0.05 / pairs.length * d * (F[c][1] - F[r][1]);
}
}
console.log(`训练后: 平均损失 ${loss().toFixed(4)}, 标量分 [${F.map((_, i) => score(i).toFixed(2)).join(', ')}]`);
console.log(`权重 w = [${w.map(x => x.toFixed(2)).join(', ')}],分数顺序复现标注排序`);
实测输出:
训练前: 平均损失 0.6931, 标量分 [0.00, 0.00, 0.00, 0.00]
训练后: 平均损失 0.0483, 标量分 [3.66, -0.90, 8.22, 2.29]
权重 w = [1.83, -0.45],分数顺序复现标注排序
训练前损失 0.6931 正好是 ln 2——所有分数相同,每对胜率都是一半;训练后损失压到 0.05,分数顺序 2 > 0 > 3 > 1 完整复现标注排序。这个玩具模型浓缩了真 RM 的全部要点:信号是两两比较,目标是拉开胜者与败者的分差。
图 2:结构改造、偏好数据格式与排序损失的推导链。
面试现场的答题结构与追问预案。
按「结构改造 → 数据格式 → 损失设计」三段推进,每段一句话结论加一层细节。常见追问三个。一,训好的 RM 怎么用:在 PPO 阶段充当奖励信号,同时配 KL 约束防止策略偏离 SFT 太远去刷分。二,RM 会出什么问题:它终究是人类偏好的近似,策略模型会钻空子拿高分不干活——奖励黑客,所以工程上常对奖励做白化和裁剪,RM 也要随策略演进持续迭代。三,为什么不直接上 PPO 训 SFT:强化学习需要可计算的奖励信号,「人类觉得好」不可微也不可计算,RM 就是把这个主观信号蒸馏成一个可微的打分函数。你在项目里做偏好数据时,标注一致性是怎么保的?欢迎聊聊。