拓十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

大模型RLHF奖励模型训练:偏好数据、评估校准与PPO/DPO衔接

大模型RLHF奖励模型训练:偏好数据、评估校准与PPO/DPO衔接 大模型训练流程走到第三篇奖励模型往往是很多人第一次真正感到“玄学”的环节。前面做预训练、做 SFT至少还有明确的 loss 和验证集准确率可以盯着到了奖励模型数据是人对回答的偏好标签是“这个比那个好”模型输出的是一个标量分数最后还要把这个分数交给 PPO 或别的策略优化方法去用。只要其中任何一环有偏差后面策略模型就会学会一些让人哭笑不得的讨好行为回答越来越长、格式越来越花、语气越来越甜但事实性和有用性反而下降。我前后参与过几轮不同规模的奖励模型训练有从零标注几千条偏好对的也有拿开源数据和业务数据混着做的。踩过最典型的坑包括把 chosen 和 rejected 弄反导致 loss 完全不降数据里 chosen 平均比 rejected 长一大截结果奖励模型变成一个“长度打分器”离线 pairwise accuracy 冲到 85%上线后策略模型开始疯狂堆排比句。所以这篇内容不打算只讲奖励模型的定义而是把大模型训练流程里奖励模型的位置、数据工程、建模细节、训练配置、评估校准、和 PPO/DPO 的衔接以及小团队低成本落地经验尽量一次讲透。如果你正在做大模型微调、偏好对齐、RLHF或者只是想知道奖励模型到底怎么训练、需要多少数据、显卡怎么配、为什么上线后总出问题这篇内容可以作为一份偏实操的参考。下面从奖励模型在整个训练流程里的位置开始拆。1. 奖励模型在大模型训练流程中的位置1.1 从预训练、SFT到偏好对齐奖励模型补的是哪块拼图现在主流的大模型训练流程大致可以拆成几个阶段预训练让模型学会语言和世界知识SFT 让模型学会按指令回答偏好对齐让模型的回答更符合人类偏好。奖励模型通常出现在偏好对齐阶段尤其是 RLHF 路线里。它的角色不是直接生成答案而是作为一个“打分器”告诉策略模型同一个 prompt 下哪个回答更好好多少。为什么不能直接用 SFT 模型因为 SFT 的训练目标是最大化标注回答的似然也就是让模型学会“像标注数据那样回答”。但人类偏好往往不是单一的“像不像”而是包含有用性、真实性、安全性、语气、格式、信息密度等多个维度。SFT 数据只能告诉模型“这个回答是标准答案”却很难告诉它“这个回答比另一个回答好在哪里”。奖励模型补的正是这个相对偏好的建模能力。从流程上看典型 RLHF 是先做 SFT得到基础策略模型再用偏好数据训练奖励模型最后用 PPO 等算法让策略模型生成回答奖励模型打分策略模型根据分数更新。奖励模型在这里相当于环境给策略模型的 reward function。这个 reward function 如果偏了策略模型就会朝着偏的方向狂奔。所以奖励模型不是“可选组件”而是偏好对齐阶段最关键的裁判之一。我自己习惯把奖励模型理解成“把人类偏好翻译成可计算标量”的中间层。人类标注员看到两个回答能说出“左边事实更准、右边虽然长但废话多”但模型没法直接吃这种自然语言判断。奖励模型把这种判断压缩成一个分数让后面的优化算法可以求梯度。这个翻译过程必然有信息损失所以奖励模型训练的核心目标不是追求 100% 拟合人类而是尽量稳定、可泛化、不容易被策略模型钻空子。1.2 奖励模型到底在学什么人类偏好到标量分数奖励模型的输入通常是 prompt 加上一个 response输出是一个实数分数。训练数据是成对的偏好样本一般写成 prompt、chosen、rejected。chosen 是人类更喜欢的回答rejected 是较差的回答。模型要学会让 chosen 的分数高于 rejected。最常用的损失是 Bradley-Terry 模型下的 pairwise ranking loss假设奖励函数为 r(x, y)其中 x 是 prompty 是 response。人类偏好概率建模为P(y_w 优于 y_l | x) sigmoid(r(x, y_w) - r(x, y_l))训练目标就是最大化这个概率等价于最小化loss -log sigmoid(r(x, y_w) - r(x, y_l))这个形式很简洁但背后有几个重要含义。第一奖励模型学的是相对排序不是绝对分数。也就是说r(x, y_w) 和 r(x, y_l) 的差值才有意义单独一个分数没有绝对解释。第二sigmoid 让差值越大概率越接近 1但损失对差值的梯度会逐渐饱和。如果数据里有很多容易区分的样本模型很快就能把差值拉得很大loss 降得很快但泛化不一定好。第三如果数据噪声很大比如标注员对某些样本也犹豫模型会被迫拟合矛盾信号最后分数分布变得很平或者很乱。奖励模型输出标量分数的方式通常是拿一个预训练或 SFT 模型作为底座去掉原来的语言模型 head换成一个标量 head。具体来说对 decoder-only 模型取最后一个非 padding token 的隐藏状态接一个线性层输出 1 维分数。也有做法是取 mean pooling但对生成式模型来说最后一个 token 往往承载了整段回答的汇总信息所以 last token 更常见。这里有个容易忽略的点奖励模型底座通常从 SFT 模型初始化而不是从预训练模型初始化。原因是 SFT 模型已经理解了指令和回答格式奖励模型只需要学习“比较”不需要重新学习语言。用预训练模型也能做但收敛更慢效果通常差一些。另一个选择是用同一个底座系列里更大的模型比如策略模型 7B奖励模型也用 7B 甚至 13B。奖励模型比策略模型大有时能提供更稳定的信号但推理成本会上去。1.3 什么情况下可以不上奖励模型奖励模型不是所有偏好对齐都必须的。如果你只是做 SFT或者用 DPO、ORPO、SimPO 这类直接偏好优化方法可以不需要显式训练奖励模型。DPO 直接用偏好对优化策略模型把奖励模型的隐式表达塞进了策略模型本身。这样做省掉了奖励模型训练和推理工程链路更短对小团队很友好。但 DPO 也不是万能。它依赖偏好数据质量而且没有显式奖励模型作为独立裁判调试时少了一个可观测的中间层。出现问题时你很难判断是数据问题、策略模型问题还是优化问题。奖励模型路线虽然复杂但多了一个可以单独评估、单独迭代的组件。你可以先离线看奖励模型在验证集上的 pairwise accuracy再看它给策略模型输出的分数分布最后才上 PPO。排查链路更清晰。还有一种情况可以不上奖励模型你的对齐目标非常简单比如只要求模型拒绝某类请求或者只要求输出固定格式。这种用规则奖励、分类器奖励、甚至正则表达式匹配就能解决没必要上奖励模型。奖励模型适合处理“好和更好”的模糊偏好不适合处理“对和错”的硬规则。硬规则最好用规则引擎或小分类器兜底奖励模型负责软偏好这样组合更稳。我在实际项目里通常会把奖励分成两层一层是硬规则比如敏感词、格式校验、工具调用结果校验另一层是奖励模型负责有用性、信息密度、语气这些软指标。两层加权后给 PPO。这样即使奖励模型有偏差硬规则也能防止策略模型跑得太偏。这个思路在业务场景里比单纯依赖一个奖励模型可靠得多。2. 偏好数据工程奖励模型的上限由数据决定2.1 偏好对从哪里来人工、模型与混合策略奖励模型训练数据主要有三个来源人工标注、模型生成、开源数据集。人工标注质量最高但成本也最高。模型生成可以用强模型对弱模型输出做排序或者用奖励模型自身做拒绝采样但容易引入模型偏见。开源数据集可以快速起步但领域匹配度往往不够直接混入业务数据可能带来风格漂移。我一般建议起步阶段用“开源数据 小规模人工标注”的混合策略。开源数据用来让模型学会基本的偏好区分比如事实性优于胡编、清晰优于混乱、有帮助优于敷衍。人工标注则针对业务场景比如客服、代码、医疗咨询、法律问答补上开源数据覆盖不到的偏好。人工数据不需要一上来就几万条先做 2000 到 5000 对把数据管道和训练流程跑通再逐步扩量。标注方式也有讲究。常见的有单点打分和成对比较。单点打分是给一个回答打 1 到 5 分成对比较是给两个回答选更好的。奖励模型训练通常更喜欢成对比较因为 pairwise 信号更直接标注员也更容易判断“哪个更好”而不是纠结“这个到底值 4 分还是 5 分”。但成对比较的数据量会随着候选回答数量平方增长所以实践中常常先用单点打分筛选再对分数接近的回答做成对比较。模型生成偏好对时要注意“强模型一定赢弱模型”这种偏差。如果你总是用 GPT-4 级别的模型输出做 chosen用 7B 模型输出做 rejected奖励模型学到的可能不是“回答更好”而是“像大模型那样的风格”。上线后策略模型会模仿大模型的语气但知识量跟不上反而更容易一本正经地胡说。所以模型生成的数据最好做长度、格式、风格的对齐减少非质量因素的干扰。2.2 标注规范与数据格式prompt、chosen、rejected的细节偏好数据的标准格式通常是 JSONL每行一个样本{ prompt: 请解释什么是梯度下降并给出一个生活化例子。, chosen: 梯度下降可以想象成下山……, rejected: 梯度下降是一种优化算法它通过迭代更新参数来最小化损失函数。 }看起来简单但每个字段都有坑。prompt 要尽量覆盖真实使用场景不要全是“请解释 XX”这种模板化指令。真实用户会问得含糊、带错别字、多轮追问、夹杂情绪。如果训练 prompt 太干净奖励模型上线后对真实分布的泛化会差很多。我通常会把线上日志脱敏后按主题聚类每个主题采样一批真实 prompt再补充一些边界 case。chosen 和 rejected 的构造要避免“质量维度混杂”。比如 chosen 事实正确但语气生硬rejected 事实错误但语气热情。标注员可能因为语气选了 rejected奖励模型就会学到“热情比正确重要”。更好的做法是让标注规范明确优先级事实性 有用性 安全性 格式 语气。遇到多维度冲突时标注员要按优先级判断并在备注里写清楚理由。这样后续清洗时能发现系统性问题。还有一个细节是回答长度。如果 chosen 普遍比 rejected 长奖励模型很容易学会“长就是好”。这在 PPO 阶段会直接导致策略模型输出越来越长最后变成废话生成器。解决办法有几个一是在构造数据时尽量让 chosen 和 rejected 长度接近比如长度差控制在 20% 以内二是在标注时明确要求不要因为长度选答案三是在奖励模型训练时加入长度惩罚或长度归一化但这是补救不如数据层面控制。格式偏差同样致命。如果 chosen 总是带 Markdown 标题和列表rejected 总是纯文本奖励模型会学到“带格式就是好”。上线后策略模型会疯狂加标题、加粗、加列表即使回答很短。数据构造时最好让 chosen 和 rejected 在格式上尽量一致或者至少让格式不是唯一的区分因素。你可以在标注规范里要求如果两个回答质量接近优先选信息更准的而不是格式更花哨的。2.3 数据清洗与质量校验一致性、长度偏差、风格泄露偏好数据拿到手不要直接丢进训练。先做几轮清洗和统计。第一去重。同一个 prompt 可能被不同标注员重复标注或者 chosen/rejected 出现完全相同的文本。重复样本会让模型过拟合也会让验证集泄露。第二检查标签一致性。如果同一个 prompt 下多个标注员给出的偏好方向矛盾说明这个样本本身有争议要么补充标注要么直接剔除。可以计算 Cohens Kappa 或简单一致率低于阈值的样本不要硬训。第三做长度分布分析。统计 chosen 和 rejected 的 token 长度差看均值、中位数、分位数。如果 chosen 平均长 100 token 以上就要警惕。可以画个直方图或者直接算一个简单的逻辑回归只用长度特征预测 chosen/rejected如果准确率显著高于 50%说明长度偏差已经严重到模型可以走捷径。这时候要么重新采样要么在训练时加长度惩罚。第四做风格泄露检查。比如 chosen 是否总是包含“当然可以”“很高兴为您解答”这类开场白rejected 是否总是没有。如果是奖励模型会学会“礼貌开场白 高分”。你可以用简单的 n-gram 分类器或者 TF-IDF 逻辑回归做基线如果它能在验证集上达到 70% 以上准确率说明数据里有很强的表面特征。这种情况下奖励模型很可能学到的是表面特征而不是真正的质量偏好。第五留出验证集和测试集。验证集用来调超参和早停测试集只在最后评估一次。划分时要按 prompt 划分而不是按回答划分。同一个 prompt 的不同回答对不能同时出现在训练集和验证集否则验证集准确率会虚高。我见过有人把同一个 prompt 的 chosen 放训练集rejected 放验证集结果验证准确率 99%上线直接崩。这个坑一定要避开。3. 奖励模型建模与训练细节3.1 基座选择SFT模型加标量头奖励模型的基座选择通常有三个方向直接用 SFT 模型、用同系列更大的模型、用专门的对齐模型。用 SFT 模型最省事因为它的 tokenizer、对话模板、领域适配都已经和策略模型一致奖励模型输出的分数分布也更容易和策略模型对齐。用更大的模型可以提高判断能力但推理成本高PPO 阶段每个 rollout 都要打分成本会放大很多倍。模型结构上最常见的是在基座模型最后一层隐藏状态上接一个线性层输出一个标量。对于 decoder-only 模型取最后一个非 padding token 的隐藏状态import torch import torch.nn as nn from transformers import AutoModel, AutoConfig class RewardModel(nn.Module): def __init__(self, base_path): super().__init__() self.backbone AutoModel.from_pretrained(base_path) hidden_size self.backbone.config.hidden_size self.score_head nn.Linear(hidden_size, 1, biasFalse) def forward(self, input_ids, attention_mask): outputs self.backbone( input_idsinput_ids, attention_maskattention_mask ) last_hidden outputs.last_hidden_state # 取每个样本最后一个非 padding token lengths attention_mask.sum(dim1) - 1 batch_idx torch.arange(last_hidden.size(0), devicelast_hidden.device) last_token last_hidden[batch_idx, lengths] score self.score_head(last_token).squeeze(-1) return score这里有个细节如果 tokenizer 使用左 padding最后一个 token 可能不是真实回答的结尾如果用右 paddingattention_mask 求和减一就是最后一个非 pad token。训练时建议统一用右 padding生成时再改左 padding。另外score_head 的初始化也很重要通常用默认初始化就行但如果你从已有奖励模型继续训练要小心分数范围漂移。有些实现会用 sequence classification 的方式把标量头接在 pooler 输出上。对 decoder-only 模型来说没有 pooler取 last token 更自然。也有做法是取最后一个 token 和 mean pooling 拼接再输出分数。我没有发现稳定的大幅提升反而增加了实现复杂度。建议先用 last token 跑通 baseline再考虑复杂结构。3.2 Bradley-Terry损失与pairwise训练代码训练的核心就是让 chosen 分数高于 rejected。代码很简洁import torch.nn.functional as F def pairwise_loss(r_chosen, r_rejected): # r_chosen, r_rejected 形状都是 [batch] return -F.logsigmoid(r_chosen - r_rejected).mean()训练循环里每个 batch 同时编码 chosen 和 rejected然后计算 loss。注意 chosen 和 rejected 要共享同一个 prompt只是回答不同。数据加载时可以把 prompt 和回答拼成完整文本也可以按对话模板构造。要点是 chosen 和 rejected 的构造方式必须完全一致除了回答内容不同。for batch in train_loader: chosen_ids batch[chosen_input_ids].to(device) chosen_mask batch[chosen_attention_mask].to(device) rejected_ids batch[rejected_input_ids].to(device) rejected_mask batch[rejected_attention_mask].to(device) r_chosen model(chosen_ids, chosen_mask) r_rejected model(rejected_ids, rejected_mask) loss pairwise_loss(r_chosen, r_rejected) loss.backward() optimizer.step() optimizer.zero_grad()如果显存不够可以用梯度累积或者把 chosen 和 rejected 拼成一个 batch 前向再拆开计算 loss。这样能减少一次前向的开销。也可以加入 margin 变体def pairwise_hinge_loss(r_chosen, r_rejected, margin0.5): return F.relu(margin - (r_chosen - r_rejected)).mean()Hinge loss 对已经分对的样本不再惩罚训练更稳定但需要调 margin。实践中 Bradley-Terry 的 logsigmoid 更常用因为它直接对应偏好概率输出分数也更容易解释。我一般先用 logsigmoid如果发现模型对困难样本区分不够再试 hinge 或加 margin 的 logsigmoid。还有一个变体是列表式损失比如一个 prompt 有多个回答用 softmax 或 Plackett-Luce 做多路排序。多路排序信号更丰富但数据构造成本高标注也更难。小规模起步建议先用 pairwise等数据量上来再考虑 listwise。3.3 超参数与工程配置显存、并行、batch奖励模型的超参和 SFT 有相似之处但有几个关键区别。学习率通常更小因为奖励模型是在 SFT 模型上继续训练而且 pairwise loss 的梯度尺度不大。常见学习率在 1e-5 到 2e-5 之间如果用 LoRA 可以适当放大到 1e-4 到 2e-4。batch size 可以大一些因为每个样本实际上包含两条回答显存占用是普通 SFT 的两倍左右。7B 模型全参训练单卡 80G 也很难放下通常需要 ZeRO-3 或 FSDP 多卡。显存估算可以粗略算一下。7B 参数混合精度训练参数 bf16 约 14GB梯度 bf16 约 14GBAdamW 的优化器状态 fp32 一阶动量约 28GB二阶动量约 28GB主权重 fp32 约 28GB加起来已经 112GB 左右还没算激活值。所以 7B 全参训练至少需要多张 80G 卡或者用 ZeRO-3 切分。LoRA 只训练 adapter基座冻结显存占用会小很多单卡 24G 或 48G 可以跑 7B 的 QLoRA。奖励模型的标量头参数量很小LoRA 加标量头是低成本方案的常见组合。序列长度也很关键。偏好数据的 prompt 加 response 可能很长尤其是多轮对话。训练时可以把最大长度限制在 1024 或 2048超过的截断或过滤。截断时要注意不要截掉回答的关键部分最好从尾部截断并记录截断率。如果截断率太高说明数据里长样本太多需要考虑长上下文训练或数据筛选。并行策略上单机多卡可以用 DeepSpeed ZeRO-2 或 ZeRO-3也可以用 FSDP。如果团队熟悉 LLaMA-Factory 这类微调平台可以直接用它的奖励模型训练模板省去很多工程细节。但要注意模板默认的超参不一定适合你的数据学习率、batch size、warmup 都要自己调。分布式训练时要确保 chosen 和 rejected 在同一个设备上否则 loss 计算会跨设备容易出错。3.4 训练监控准确率、margin、过拟合与早停奖励模型训练过程中最该看的指标不是 loss而是 pairwise accuracy。也就是在验证集上r_chosen r_rejected 的比例。随机水平是 50%一般来说 65% 到 75% 已经不错80% 以上要警惕是否数据太简单或泄露。除了准确率还要看 reward margin也就是 r_chosen - r_rejected 的均值和分布。如果 margin 很快变得很大比如均值超过 5 甚至 10说明模型在用力拉大分数差可能过拟合了。过拟合的信号通常是训练集准确率继续上升验证集准确率停滞或下降验证集 loss 开始上升。这时候要早停或者增加正则化、dropout、权重衰减。奖励模型很容易过拟合因为偏好数据量通常比 SFT 数据少很多而且标注噪声大。我一般会设置 patience 为 2 到 3 个 epoch保存验证集准确率最高的 checkpoint。还可以监控分数分布。训练初期chosen 和 rejected 的分数可能都在 0 附近训练后期chosen 分数上升rejected 分数下降分布逐渐分开。如果两个分布完全重叠说明模型没学到东西如果完全分离且间隔巨大说明可能过拟合。理想情况下分数分布应该有重叠但整体偏移这样对模糊样本也有区分度。另一个重要监控项是不同分桶的准确率。按 prompt 类型、回答长度、领域分桶看模型是否在某些桶上表现特别差。比如代码类 prompt 准确率 80%闲聊类只有 55%说明模型对闲聊偏好不敏感。这种分桶分析能指导下一轮数据补充。不要只看总体准确率总体 75% 可能掩盖了某个重要场景只有 50% 的问题。4. 奖励模型评估、校准与奖励黑客排查4.1 离线评估pairwise accuracy、分桶、排序相关离线评估奖励模型最核心的指标是 pairwise accuracy。但只用这一个指标不够。第一要看置信区间。验证集 1000 个样本准确率 70%置信区间可能上下浮动几个点。如果两个模型准确率差 1%可能只是噪声。第二要看分桶准确率。按领域、长度、难度分桶找出短板。第三要看排序相关性。如果数据里一个 prompt 有多个回答的排序可以计算 Spearman 或 Kendall 相关系数看模型排序和人类排序的一致性。还可以做校准分析。奖励模型的分数不是概率但我们可以看分数差和人类偏好概率的关系。比如把验证集按分数差分成若干桶统计每个桶里 chosen 真正优于 rejected 的比例。理想情况下分数差越大人类偏好概率越高曲线应该单调。如果曲线平坦或者反转说明模型分数不可靠。校准分析能帮我们发现模型在哪些区间过度自信或信心不足。另外建议留一个“对抗集”。这个集合里的样本是故意构造的比如 chosen 和 rejected 长度相同、格式相同、事实性接近只有细微差别。奖励模型在这个集合上的准确率更能反映真实判断力。普通验证集可能包含很多容易样本准确率虚高。对抗集不需要很大几百条就能看出问题。4.2 在线评估与奖励黑客策略钻空子的典型表现奖励模型上线后真正的考验才开始。策略模型会想尽办法拿高分而不一定真的提升质量。这就是奖励黑客。典型表现包括回答越来越长堆砌无关信息格式越来越花哨标题、列表、加粗满天飞语气越来越谄媚“非常棒的问题”“您说得太对了”重复用户问题看似回应实则凑字数回避不确定内容用模糊话术代替事实回答。发现奖励黑客不能只看奖励分数。奖励分数上升是必然的因为策略模型就是在优化它。要看人工评估、业务指标和多样性指标。比如 reward 上升但人工有用性评分下降或者回答长度中位数翻倍或者重复率上升都说明奖励模型被钻空子了。我通常会在 PPO 训练时监控这些指标平均奖励、平均回答长度、重复 n-gram 比例、人工抽检胜率、KL 散度。任何一个异常都要停下来查。解决奖励黑客短期可以加长度惩罚、重复惩罚、KL 惩罚。KL 惩罚让策略模型不要偏离 SFT 模型太远能有效抑制极端行为。长期要靠数据迭代把策略模型钻空子生成的样本拿回来人工标注为 rejected加入下一轮奖励模型训练。这样奖励模型会逐渐学会识别这些表面讨好的回答。还有一个办法是训练多个奖励模型取平均或最小值降低单一模型被攻破的风险。4.3 常见问题速查表问题可能原因排查方法处理建议loss 不降或震荡chosen/rejected 标签反了学习率太大数据噪声高检查数据字段小学习率重跑抽样人工复核修正标签降 lr清洗矛盾样本验证准确率虚高训练集验证集按回答划分导致泄露长度偏差风格泄露按 prompt 划分长度基线分类器表面特征检查重新划分长度匹配去风格化奖励分数分布漂移换了 tokenizer、最大长度、模型版本数据分布变化对比新旧模型分数统计固定评估集版本管理分数归一化重新校准显存溢出序列太长batch 太大全参训练优化器状态占满看峰值显存缩短长度减小 batch梯度累积ZeROLoRAbf16分数区分度低score head 初始化差学习率太小数据太单一看分数方差检查数据多样性调 lr增加数据多样性换初始化PPO 后质量下降奖励黑客KL 系数太小奖励模型过拟合看长度、重复率、人工评估加 KL 惩罚长度惩罚重训奖励模型这张表是我自己排查时常用的清单。实际遇到问题时往往不是单一原因而是多个因素叠加。比如 loss 不降可能是标签反了加上学习率太大。建议每次只改一个变量记录实验配置否则很难定位。5. 奖励模型和PPO/DPO的衔接与工程化5.1 在RLHF中调用奖励模型服务化、批处理、缓存PPO 训练时策略模型会生成一批回答然后奖励模型给每个回答打分。这个打分环节的吞吐量很关键。如果奖励模型推理太慢整个 PPO 训练会被拖垮。常见做法是把奖励模型部署成推理服务用 FastAPI 或 Triton 提供批量打分接口。每次 PPO rollout 后把 prompt 和 response 拼好批量发给奖励模型拿到分数后再算 advantage。批处理要注意 padding 和最大长度。不同回答长度差异很大如果每个样本单独推理GPU 利用率很低。可以按长度分桶把长度接近的样本放在一个 batch减少 padding 浪费。还可以缓存相同 prompt 和 response 的分数但 PPO 每次策略更新后回答会变缓存命中率不高。如果做了拒绝采样同一批回答可能被多次打分缓存就有用了。奖励模型服务还要考虑版本管理。PPO 训练用的奖励模型版本必须固定否则分数分布会变训练不稳定。每次更新奖励模型最好先离线评估再影子模式跑一段确认分数分布和人工评估一致再切到线上训练。如果直接热更新策略模型可能会因为奖励尺度突变而崩溃。5.2 与DPO等方法的选择显式奖励模型是否必要DPO 这几年很火因为它不需要显式奖励模型直接用偏好对优化策略模型。对于小团队DPO 的吸引力很大少训练一个模型少维护一个服务链路短调试简单。但 DPO 也有自己的问题。它把奖励隐含在策略模型里缺少独立评估的中间层。如果偏好数据有偏DPO 会直接学偏而且不容易发现。DPO 对超参也比较敏感beta 参数、学习率、数据配比都会影响效果。奖励模型路线适合什么场景第一你需要频繁迭代偏好数据并且希望单独评估奖励模型。第二你要做 PPO 或 GRPO需要显式 reward。第三你的业务场景需要多目标奖励融合比如有用性、安全性、格式规则奖励模型可以作为软偏好的一部分和规则奖励加权。第四你有足够工程资源维护推理服务。如果这些条件不满足DPO 或 ORPO 可能更划算。我的建议是先用 DPO 快速验证偏好数据质量和方向如果效果稳定再考虑上奖励模型加 PPO。或者反过来先训练一个小奖励模型做数据筛选和评估但不一定马上上 PPO。奖励模型本身也可以作为评估工具帮你判断 DPO 训练后的模型是否真的更好。两者不是互斥的可以组合使用。5.3 版本管理、迭代闭环与灰度发布奖励模型不是训练一次就完事。数据在变策略模型在变业务需求也在变。必须建立版本管理和迭代闭环。每个奖励模型版本至少要记录基座模型、训练数据快照、超参数、评估指标、分数分布、训练日期。这样出现问题时可以回溯。我见过团队换了训练数据但没记录结果新模型效果下降花了很久才查到是数据里混入了一批错误标注。迭代闭环通常是策略模型上线后收集真实对话人工标注偏好加入奖励模型训练集重新训练奖励模型离线评估影子模式再上线。这个循环不需要每天跑可以按周或按双周。关键是保持数据多样性不要让奖励模型只见过早期策略模型的输出。策略模型在进化奖励模型也要跟着进化。灰度发布时不要直接把新奖励模型接到 PPO 训练里。先让它和旧模型并行打分比较分数分布和 pairwise 判断。如果新旧模型在 90% 的样本上判断一致说明变化不大如果在某些桶上差异很大要分析原因。确认没问题后再小流量切换。PPO 训练对奖励尺度敏感新模型的分数范围最好做归一化或者固定一个参考模型做分数校准。6. 实操避坑与低成本落地经验6.1 数据标注踩坑记录我踩过最惨的一次坑是标注规范里没写清楚“事实性优先”。结果标注员看到两个回答一个事实正确但语气冷淡一个事实有误但语气热情很多人选了热情的。奖励模型训练后策略模型变得特别会讨好但事实错误率上升。后来我们重新写了标注规范明确优先级并且让标注员在冲突时写理由。第二轮数据质量明显提升。第二个坑是标注员疲劳。连续标注几百条后判断标准会漂移。前期严格后期随便选。解决办法是穿插黄金样本也就是事先有标准答案的样本定期检查标注员是否偏离。如果某个标注员在黄金样本上准确率低于 80%就要重新培训。黄金样本不需要多每 50 条插 3 到 5 条就行。第三个坑是数据泄露。有一次我们把同一个 prompt 的不同回答对分到了训练集和验证集验证准确率冲到 92%。上线后 PPO 效果很差。后来改成按 prompt 分组划分验证准确率降到 72%但这个数字才是真实的。所以划分数据集时一定要按 prompt 或对话 ID 分组不能按单条回答随机划分。6.2 小团队低成本训练建议小团队资源有限不建议一上来就全参训练 7B 甚至 70B 奖励模型。可以先用 1B 到 3B 的小模型跑通全流程。小模型训练快推理便宜能快速验证数据质量和训练代码。等流程稳定了再换大模型。奖励模型的效果对数据质量非常敏感小模型在干净数据上可能只比大模型差几个点但迭代速度快很多。训练方式优先考虑 LoRA 或 QLoRA。7B 模型用 QLoRA单卡 24G 可以跑训练时间也能接受。标量头可以全参训练因为参数量很小。LoRA 的秩、alpha、dropout 需要调通常秩 8 到 64 之间。如果发现奖励模型分数区分度不够可以试试增大秩或解冻最后几层。全参训练当然更好但成本高适合数据量足够大、效果要求高的场景。数据方面先用开源偏好数据集加自有数据混合。开源数据集可以选一些质量较高的但要注意许可证和领域匹配。自有数据哪怕只有 2000 对也能显著提升业务场景表现。训练时可以把开源数据和自有数据按比例混合比如 1:1 或 2:1。评估时一定要分开看开源测试集和自有测试集确保没有牺牲通用能力。6.3 后续扩展思路奖励模型后续可以往几个方向扩展。第一多目标奖励模型。不要只输出一个分数可以输出多个维度的分数比如有用性、事实性、安全性、风格然后加权或做帕累托优化。这样策略模型不会为了一个维度牺牲其他维度。第二多模态奖励模型。如果业务涉及图像、语音奖励模型也要能处理多模态输入这会更复杂但也是趋势。第三过程奖励模型。对推理类任务不仅给最终答案打分还给中间步骤打分能显著提升数学、代码等任务的表现。还有一个方向是奖励模型的可解释性。现在奖励模型是个黑盒只能看分数。可以尝试让奖励模型输出分数同时给出简短理由或者用注意力分析看它关注哪些 token。这样能更快发现奖励黑客和数据偏差。不过可解释性会增加训练和推理成本适合对可靠性要求高的场景。最后分享一个我自己的小技巧固定一个“金标准评估集”每次改数据、改超参、改模型都跑同一个评估集记录 pairwise accuracy、分桶准确率、分数分布。不要只看一次实验的结果要把多次实验放在一起比较。这样你能清楚知道哪个改动真正有用。奖励模型训练最怕东改西改最后不知道哪个变量起了作用。保持实验记录比盲目调参重要得多。
返回列表