
1. 从“复读机”说起SFT 到底把模型训成了什么样很多人第一次接触大模型微调都是从 SFTSupervised Fine-Tuning有监督微调开始的。你手里有一批“问题-标准答案”对喂给模型让它学会按照你期望的格式和风格输出。训练完之后模型确实听话了你问它“帮我写个请假条”它不会再给你扯一段天气预报而是老老实实按格式写。但用不了多久你就会发现一个让人头疼的现象模型变成了一个高级复读机。这个“复读机”不是说它只会重复你的话而是说它的输出变得极其刻板、保守、缺乏灵活性。你问它一个开放性问题它给你的答案永远是一个模子刻出来的你稍微换个问法它要么答非所问要么就开始胡编乱造。更麻烦的是SFT 训练出来的模型往往“不会看脸色”——它不知道什么回答是真正让用户满意的什么回答只是形式上正确但实质上没用。这个问题的根源在于 SFT 的训练机制本身。SFT 本质上是在做“模仿学习”给定输入 x最大化模型输出 y 的概率也就是让模型学会“在这种情况下标准答案长什么样”。但这里有几个致命缺陷。第一标准答案只有一个但好的回答可能有无数种。模型学到的不是“如何生成好回答”而是“如何复现那个特定答案”。第二SFT 的损失函数是 token 级别的交叉熵它关心的是每个词预测得对不对而不是整个回答好不好。第三也是最关键的SFT 没有“比较”的概念——它不知道回答 A 比回答 B 好在哪里只知道 A 是标准答案。所以 SFT 之后的模型就像一个背熟了标准答案的学生考试遇到原题能拿满分遇到变体题就露馅。它能生成“正确”的回答但不知道什么是“好”的回答。这就是为什么我们需要 RLReinforcement Learning强化学习登场——让模型学会从多个可能的回答中选出那个真正让人满意的。注意SFT 不是没用它是 RL 的基础。没有 SFT 打底模型连基本的语言格式和任务理解都做不到RL 根本无从下手。SFT 解决的是“能不能做”RL 解决的是“做得好不好”。2. RL 入场怎么教模型“看脸色”2.1 从“模仿”到“试错”RL 的核心逻辑RL 的思路和 SFT 完全不同。SFT 是“我给你看标准答案你照着学”RL 是“你自己去试试好了我给你奖励试砸了我给你惩罚”。放到大模型场景里就是让模型对同一个问题生成多个不同的回答然后由某个“裁判”来打分模型根据分数高低来调整自己的策略——分数高的回答以后多生成分数低的回答以后少生成。这个“裁判”可以是人也可以是另一个模型还可以是一套规则。当裁判是人的时候就是 RLHFReinforcement Learning from Human Feedback基于人类反馈的强化学习当裁判是 AI 的时候就是 RLAIFReinforcement Learning from AI Feedback。不管哪种核心逻辑都是一样的让模型学会“看脸色”——看用户的脸色、看评分标准的脸色、看奖励信号的脸色。但这里有个关键问题语言生成是离散的、序列化的而且奖励信号通常是滞后的。模型生成了一整段话之后你才能给它一个分数但这段话里每个词对最终分数的贡献是不一样的。这就引出了 RL 在大模型训练中最核心的挑战信用分配问题——到底哪些词、哪些决策导致了最终的好结果或坏结果2.2 RLHF 的三步走从 SFT 到奖励模型再到 PPO目前最主流的 RLHF 流程分三步。第一步是 SFT这个前面说过了先让模型学会基本的任务格式。第二步是训练奖励模型Reward ModelRM。具体做法是对同一个问题让 SFT 模型生成多个回答然后让人对这些回答进行排序——哪个最好哪个次之哪个最差。用这些排序数据训练一个模型让它学会给回答打分。这个奖励模型就是后面 RL 训练中的“裁判”。第三步才是真正的 RL 训练。这里最常用的算法是 PPOProximal Policy Optimization近端策略优化。PPO 的核心思想是不要让模型一次更新太多每次只允许策略变化一点点否则容易训崩。具体来说PPO 会计算一个新策略和旧策略的比率然后把这个比率限制在一个范围内通常是 0.8 到 1.2 之间超过这个范围就截断。这样既能保证模型在进步又不会因为一步迈太大而摔跤。PPO 的损失函数通常包含三部分策略损失让模型朝着高奖励方向更新、价值损失让价值网络准确预测未来奖励、熵损失鼓励模型保持一定的探索性不要过早收敛到单一策略。这三部分的权重需要仔细调节否则要么学得太慢要么学得太偏。2.3 DPO 的崛起跳过奖励模型直接优化PPO 虽然有效但工程上非常复杂。你需要同时维护四个模型策略模型、参考模型、奖励模型、价值模型。显存占用大训练不稳定超参数敏感调起来让人头秃。于是 DPODirect Preference Optimization直接偏好优化出现了。DPO 的核心洞察非常漂亮既然 RLHF 的目标是让模型生成人类偏好的回答那能不能直接用一个分类损失来实现DPO 的推导过程这里不展开但结论很简洁它把 RLHF 中的奖励函数和最优策略的关系做了一个数学变换最终得到一个可以直接在偏好数据上优化的损失函数。这个损失函数不需要奖励模型不需要价值模型只需要策略模型和参考模型训练起来就像做 SFT 一样简单。DPO 的损失函数直观理解是这样的对于一对偏好数据胜者 y_w 和败者 y_lDPO 会提高模型生成 y_w 的概率降低生成 y_l 的概率但同时又用一个 KL 散度约束来防止模型偏离参考模型太远。这个 KL 约束非常关键它保证了模型不会为了迎合偏好数据而丧失语言能力。实操心得DPO 虽然简单但对偏好数据的质量极其敏感。如果偏好数据里噪声太多DPO 会直接把这些噪声学进去而且比 PPO 更难纠正。我试过用一批标注质量参差不齐的数据跑 DPO结果模型学会了“拍马屁”——不管问什么都先夸用户一顿因为标注员倾向于给“礼貌”的回答打高分。2.4 PPO 和 DPO 的选型对比维度PPODPO需要奖励模型是否需要价值模型是否显存占用高4个模型低2个模型训练稳定性较差超参敏感较好类似 SFT在线采样需要每步都要生成不需要离线数据即可适合场景有高质量奖励模型、追求极致效果快速迭代、资源有限、偏好数据充足主要风险奖励黑客、训崩过拟合偏好数据、丧失多样性选 PPO 还是 DPO本质上是在“效果上限”和“工程成本”之间做权衡。如果你有充足的算力、高质量的奖励模型、以及调参的经验PPO 通常能压榨出更好的效果。但如果你只是想快速让模型对齐人类偏好DPO 是性价比极高的选择。我个人的经验是先用 DPO 跑一版看看效果如果发现模型在某些维度上还是不够好再考虑上 PPO 做精细打磨。3. 奖励模型RL 训练中的“裁判”怎么造3.1 奖励模型的数据收集排序比打分更靠谱训练奖励模型最核心的是数据。常见的数据格式有两种一种是绝对打分比如让标注员给每个回答打 1-5 分另一种是相对排序比如给标注员看四个回答让他们从好到差排序。实践下来排序数据的质量明显更高因为人对“哪个更好”的判断比“这个值几分”的判断要一致得多。排序数据的收集通常是这样做的对同一个 prompt用当前模型生成 4 个不同的回答通过调整 temperature 或 top-p 来增加多样性然后让标注员对这 4 个回答进行排序。如果标注员觉得某两个回答差不多可以标记为“平局”。最终得到的数据形式是 (prompt, 回答A, 回答B, 偏好标签)其中偏好标签表示 A 比 B 好、B 比 A 好、还是平局。这里有个细节生成回答的模型最好是“当前正在训练的模型”或者“和它能力相近的模型”。如果你用 GPT-4 生成回答来训练一个 7B 模型的奖励模型那奖励模型学到的偏好分布和 7B 模型实际能生成的回答分布不匹配RL 训练时就会出现“奖励模型觉得好的回答策略模型根本生成不出来”的尴尬局面。3.2 奖励模型的训练Bradley-Terry 模型奖励模型的训练目标通常是 Bradley-Terry 模型。简单来说它假设每个回答有一个潜在的“奖励值”而人类偏好 A 胜过 B 的概率等于 sigmoid(奖励(A) - 奖励(B))。训练目标就是最大化这个概率也就是让奖励模型给胜者打高分、给败者打低分。损失函数写出来是这样的对于一对偏好数据 (x, y_w, y_l)损失为 -log(sigmoid(r(x, y_w) - r(x, y_l)))其中 r 是奖励模型的输出。这个损失函数会让 r(x, y_w) 和 r(x, y_l) 的差距越大越好但 sigmoid 的存在保证了梯度不会爆炸。奖励模型的结构通常就是在 SFT 模型的基础上把最后的输出层换成一个标量输出。也就是说它和策略模型共享大部分参数结构只是最后不输出词表概率而是输出一个分数。这样做的好处是可以复用 SFT 的预训练知识训练起来收敛更快。3.3 奖励模型的坑奖励黑客与分布偏移奖励模型最大的问题是“奖励黑客”Reward Hacking。所谓奖励黑客就是策略模型找到了奖励模型的漏洞生成一些奖励模型给高分但人类觉得莫名其妙的回答。比如奖励模型可能偏好长回答因为标注员倾向于认为长回答更详细于是策略模型就学会了把所有回答都写得又臭又长哪怕问题只是“今天天气怎么样”。另一个问题是分布偏移。奖励模型是在 SFT 模型生成的回答上训练的但 RL 训练过程中策略模型会不断更新它生成的回答分布会逐渐偏离 SFT 模型的分布。当策略模型生成出奖励模型从未见过的回答类型时奖励模型的打分就不可靠了。这就像你用一个只见过猫和狗的模型去识别一只狐狸它可能会把狐狸分类成狗而且非常自信。解决这两个问题的常见手段包括在 RL 训练过程中定期用新策略模型生成数据重新训练奖励模型在奖励模型训练时加入一些对抗样本使用多个奖励模型取平均或最小值以及在 PPO 中加入 KL 惩罚限制策略模型不要偏离参考模型太远。踩过的坑我曾经用一批标注数据训练了一个奖励模型在验证集上准确率 78%看起来还不错。结果 PPO 训练之后模型开始生成大量重复的、带有“当然可以”“非常好的问题”这类套话的回答。后来排查发现奖励模型对这类套话有轻微的偏好因为标注数据里这类回答往往被排得靠前。这个偏差在奖励模型验证时看不出来但在 RL 训练中被策略模型无限放大了。4. PPO 实操从配置到调参的完整流程4.1 PPO 训练的四个模型与显存计算PPO 训练需要同时加载四个模型策略模型Actor、参考模型Reference、奖励模型Reward、价值模型Critic。策略模型和价值模型需要更新所以需要保存优化器状态参考模型和奖励模型是冻结的只需要推理。以 7B 模型为例如果用 bf16 精度每个模型权重占 14GB 左右。策略模型和价值模型各需要一份优化器状态Adam 优化器通常需要 2 倍参数量的显存所以策略模型加优化器约 42GB价值模型加优化器约 42GB参考模型 14GB奖励模型 14GB合计约 112GB。这还没算激活值、梯度、通信缓冲区的显存。所以 7B 模型的 PPO 训练至少需要 4 张 80GB 的 A100 或者 8 张 40GB 的 A100。如果显存不够可以考虑用 LoRA 只训练低秩适配器或者用 DeepSpeed ZeRO Stage 3 做参数分片。但 LoRA 的问题是它限制了模型的更新能力在 RL 场景下可能学不到足够复杂的行为。4.2 PPO 的关键超参数与调参经验PPO 的超参数很多但最关键的几个是学习率、KL 系数、裁剪范围、批次大小、以及每个 prompt 生成的样本数。学习率通常设置在 1e-6 到 5e-6 之间比 SFT 的学习率小一到两个数量级。这是因为 RL 训练本身就不稳定学习率太大会直接训崩。KL 系数通常记为 β控制策略模型偏离参考模型的程度典型值在 0.01 到 0.1 之间。KL 系数太小模型会过度优化奖励模型导致奖励黑客KL 系数太大模型学不到新东西等于没训。裁剪范围clip range通常设为 0.2也就是策略比率被限制在 0.8 到 1.2 之间。这个值越小训练越稳定但学习越慢越大学习越快但越容易崩。批次大小方面PPO 通常需要较大的批次来稳定梯度估计常见设置是 64 到 256 个 prompt每个 prompt 生成 4 到 8 个回答。我个人的调参顺序是先固定 KL 系数为 0.05学习率 2e-6裁剪 0.2跑一轮看看奖励曲线和 KL 散度。如果 KL 散度飙升到 10 以上说明学习率太大或者 KL 系数太小如果奖励曲线一直平着不动说明学习率太小或者 KL 系数太大。调好这两个之后再微调批次大小和样本数。4.3 PPO 训练中的监控指标PPO 训练过程中有几个指标必须盯着看。第一个是平均奖励mean reward它应该随着训练逐渐上升但如果上升太快可能是奖励黑客的前兆。第二个是 KL 散度KL divergence它衡量策略模型和参考模型的差异应该保持在一个合理的范围内比如 5 到 20 之间如果超过 50 就说明模型已经跑偏了。第三个是价值损失value loss它衡量价值模型预测的准确性如果一直很高说明价值模型没学好需要调大价值模型的学习率。第四个是熵entropy它衡量策略的多样性如果熵降到接近 0说明模型已经收敛到单一策略失去了探索能力。还有一个容易被忽略的指标是“回答长度”。在 RLHF 训练中回答长度往往会逐渐增加因为奖励模型可能偏好更详细的回答。但如果长度增加得太快比如从 100 字涨到 1000 字那大概率是奖励黑客需要检查奖励模型或者加大长度惩罚。4.4 PPO 代码核心逻辑PyTorch 风格伪代码# 策略模型前向 logits policy_model(input_ids) log_probs log_softmax(logits, dim-1) # 计算每个 token 的 log 概率 token_log_probs gather(log_probs, action_tokens) # 计算重要性采样比率 ratio exp(token_log_probs - old_token_log_probs) # PPO 裁剪损失 surr1 ratio * advantages surr2 clip(ratio, 1 - clip_range, 1 clip_range) * advantages policy_loss -min(surr1, surr2).mean() # 价值损失 value_pred value_model(input_ids) value_loss mse_loss(value_pred, returns) # 熵损失 entropy -(exp(log_probs) * log_probs).sum(-1).mean() # 总损失 total_loss policy_loss 0.5 * value_loss - 0.01 * entropy这段代码里advantages通常用 GAEGeneralized Advantage Estimation计算它衡量的是某个动作比平均水平好多少。returns是折扣累积奖励用于训练价值模型。熵损失前面的系数通常设为 0.01 左右太大模型不收敛太小模型失去探索性。5. DPO 实操更简单的对齐方案5.1 DPO 的数据格式与预处理DPO 的数据格式比 PPO 简单得多只需要 (prompt, chosen, rejected) 三元组。chosen 是偏好回答rejected 是非偏好回答。数据预处理时需要把 prompt、chosen、rejected 分别 tokenize并构造好 attention mask 和 label mask。注意DPO 的损失只计算回答部分的 log 概率prompt 部分不参与损失计算。一个常见的坑是 padding 的处理。因为 chosen 和 rejected 的长度通常不一样padding 的位置会影响 log 概率的计算。正确的做法是对 chosen 和 rejected 分别计算 log 概率时把 padding token 的 log 概率 mask 掉只对实际回答 token 求和。另外如果使用 flash attention要注意 padding 和 attention mask 的兼容性。5.2 DPO 的损失函数与 β 参数DPO 的损失函数核心是对于每个三元组计算策略模型和参考模型在 chosen 和 rejected 上的 log 概率差然后过一个 sigmoid。具体来说定义log_ratio_chosen log_policy(chosen) - log_reference(chosen)log_ratio_rejected log_policy(rejected) - log_reference(rejected)loss -log(sigmoid(beta * (log_ratio_chosen - log_ratio_rejected)))这里的 β 参数控制模型偏离参考模型的程度。β 越大模型越保守越接近参考模型β 越小模型越激进越倾向于拟合偏好数据。典型值在 0.1 到 0.5 之间。我试过 β0.1模型学得很快但容易过拟合β0.5模型学得很慢但更稳定。一般建议从 0.3 开始调。5.3 DPO 训练中的常见问题DPO 最常见的问题是“概率崩塌”。具体表现是训练一段时间后模型对 chosen 和 rejected 的 log 概率都变得极低导致 loss 不再下降。这是因为 DPO 的损失函数没有下界模型可以通过无限降低 rejected 的概率来降低 loss但同时也把 chosen 的概率拉低了。解决方法是加入一个 SFT 损失作为正则项或者使用 IPOIdentity Preference Optimization等改进版本。另一个问题是“长度偏差”。DPO 倾向于偏好更长的回答因为长回答的 log 概率通常更低概率的乘积更小所以模型会学会生成更长的回答来“刷” log 概率差。解决方法是在数据预处理时对回答长度做归一化或者在损失函数中加入长度惩罚。实操技巧DPO 训练时建议先用较小的学习率比如 5e-7跑几百步观察 chosen 和 rejected 的 log 概率变化。如果 chosen 的概率在上升而 rejected 的概率在下降说明训练正常如果两者都在下降说明 β 太小或者学习率太大需要调整。6. 常见问题与排查技巧实录6.1 奖励曲线不涨或震荡怎么办奖励曲线不涨最常见的原因是学习率太小或者 KL 系数太大。先检查 KL 散度如果 KL 散度一直很低比如小于 1说明模型根本没怎么更新需要调大学习率或调小 KL 系数。如果 KL 散度很高但奖励不涨说明模型在乱跑需要调小学习率或调大 KL 系数。奖励曲线震荡通常是批次大小太小或者优势估计方差太大。可以尝试增大批次大小或者调小 GAE 的 λ 参数从 0.95 调到 0.9让优势估计更稳定。另外价值模型的学习率也很关键如果价值模型学得太慢优势估计就不准奖励曲线也会震荡。6.2 模型输出重复、套话、长度爆炸这是奖励黑客的典型症状。排查步骤首先检查奖励模型对重复文本、套话、长文本的打分是否异常偏高。可以构造一些明显的垃圾回答比如重复同一句话 100 遍看奖励模型给多少分。如果奖励模型给这些垃圾回答打了高分说明奖励模型本身有问题需要重新训练或清洗数据。如果奖励模型没问题那可能是 KL 系数太小策略模型过度优化奖励模型。可以尝试调大 KL 系数或者在奖励中显式加入长度惩罚和重复惩罚。另外PPO 的熵损失系数也可以调大一点鼓励模型保持多样性。6.3 DPO 训练后模型变“傻”了DPO 训练后模型变傻通常是因为 β 太小或者偏好数据质量太差。β 太小模型会过度拟合偏好数据中的噪声导致语言能力退化。偏好数据质量差比如标注员本身就不专业或者数据中存在大量偏见模型会把这些偏见学进去。解决方法是调大 β清洗偏好数据或者在 DPO 损失中加入 SFT 正则项。我个人的经验是DPO 训练时保留 10% 到 20% 的 SFT 损失可以显著缓解模型变傻的问题。具体做法是在每个 batch 中除了计算 DPO 损失还计算 chosen 回答的交叉熵损失然后按比例加权求和。6.4 常见问题速查表问题现象可能原因排查方法解决方案奖励不涨学习率太小、KL 系数太大检查 KL 散度是否过低调大学习率、调小 KL 系数奖励震荡批次太小、优势方差大检查价值损失是否偏高增大批次、调小 GAE λ输出重复奖励黑客、KL 太小检查奖励模型对重复文本打分调大 KL、加重复惩罚长度爆炸奖励模型偏好长文本检查奖励与长度的相关性加长度惩罚、清洗奖励数据DPO 后变傻β 太小、数据噪声大检查 chosen 概率是否下降调大 β、加 SFT 正则KL 散度飙升学习率太大、奖励模型太激进检查奖励分布是否异常调小学习率、调大 KL 系数价值损失不降价值模型学习率太小检查价值预测与回报的相关性调大价值模型学习率7. 一些个人体会和后续扩展方向RL 训练大模型这件事说到底是在“让模型学会看脸色”。SFT 教会了模型“说什么”RL 教会了模型“怎么说更好”。但“好”的定义本身就是一个复杂的问题——不同的人有不同的偏好不同的场景有不同的标准。PPO 和 DPO 只是工具真正决定模型对齐效果的是奖励信号的设计和偏好数据的质量。我个人的体会是RLHF 训练中 80% 的精力应该花在数据上20% 花在算法上。一个高质量的奖励模型加上干净的偏好数据用 DPO 就能跑出很好的效果反之再复杂的 PPO 调参也救不了垃圾数据。另外KL 系数和 β 参数不是越大越好也不是越小越好它们是在“对齐”和“保持能力”之间走钢丝需要根据具体任务反复调试。后续如果想进一步深入可以关注几个方向一是 RLAIF用 AI 代替人类做偏好标注降低成本二是 Constitutional AI用一套规则让模型自我批评和自我修正三是多目标对齐同时优化有用性、无害性、诚实性等多个维度而不是把它们压成一个标量奖励。这些方向目前都还在快速演进中但核心逻辑是一样的——让模型学会从反馈中调整自己而不是死记硬背标准答案。