为什么需要对齐训练
SFT(监督微调)训练后的模型知道"怎么回答",但不一定知道"哪种回答更好"。对同一个问题,模型可能产生多个"都还可以"的回答,但它们的质量差异很大——一个简洁准确,另一个冗长甚至有误导性。对齐训练(Alignment Training)通过让模型学习人类偏好,使其在多个候选回答中选择更符合期望的那个。
对齐问题(Alignment Problem)
模型优化的目标(预测下一个 token 的概率)与我们真正想要的(有用、无害、诚实的回答)之间存在差距。SFT 只能让模型学会模仿示例,但人类真正的偏好难以用"模仿哪些示例"来完整表达——有时候"哪个更好"比"给出示例"更容易判断。对齐训练就是直接从"哪个更好"的比较数据中学习。
RLHF(Reinforcement Learning from Human Feedback)
人类反馈强化学习。InstructGPT(ChatGPT 的前身)使用的方法,由 OpenAI 在 2022 年提出。流程分三阶段:① SFT 初始化模型 → ② 用人类偏好数据训练奖励模型(Reward Model)→ ③ 用 PPO(近端策略优化)强化学习算法将策略模型朝向高奖励方向优化。效果显著但流程复杂,对调参要求极高。
DPO(Direct Preference Optimization)
直接偏好优化。Rafailov et al. 2023 年提出,发表于 NeurIPS 2023。其核心思想是:跳过奖励模型,直接从偏好数据(chosen/rejected 对)中推导最优策略的闭式解。数学上证明 DPO 与 RLHF 的优化目标等价,但实现更简单、更稳定。目前是最广泛使用的对齐方法。
RLHF 的三阶段流程
阶段一:SFT(监督微调)
- 在高质量指令-回复数据上微调
- 得到能遵循指令的基础模型 π_SFT
↓
阶段二:奖励模型训练
- 收集人类偏好对:(prompt, chosen, rejected)
- 训练奖励模型 r(x, y) = "这个回答有多好"
- 奖励模型本质是个分类器:chosen 应得高分,rejected 应得低分
↓
阶段三:PPO 强化学习
- 用当前策略生成回答,奖励模型打分
- PPO 梯度更新:提升高奖励回答的概率
- KL 惩罚:限制策略不能偏离 SFT 模型太远(防止奖励黑客攻击)
↓
最终得到对齐后的模型 π_RL
奖励黑客攻击(Reward Hacking)
在 PPO 阶段,模型有时会学到"钻奖励模型空子"的策略——例如发现奖励模型偏爱更长的回答,于是生成大量无意义的填充内容来提高得分。这些回答在奖励模型看来是"好的",但对真实用户毫无价值。KL 散度惩罚项(β × KL(π_RL || π_SFT))限制了策略偏离 SFT 基础的程度,是防止奖励黑客攻击的核心机制。
PPO(Proximal Policy Optimization)
近端策略优化,OpenAI 2017 年提出的强化学习算法。"近端"的含义是每次更新时,新策略不能与旧策略偏差太大(通过 clip 操作或 KL 约束实现),这保证了训练稳定性。PPO 是 RL 中的"信任域方法"的简化版,比 TRPO 更容易实现,但参数调节仍然相当困难。
DPO 的数学原理
DPO 的关键洞察是:RLHF 的最优策略有一个闭式解。给定任意奖励函数 r(x, y) 和 KL 约束,最优策略满足:
RLHF 目标(有 KL 约束):
max E[r(x, y)] - β × KL(π(y|x) || π_ref(y|x))
最优策略的闭式解:
π*(y|x) ∝ π_ref(y|x) × exp(r(x,y) / β)
将奖励函数用策略表示(反推):
r(x, y) = β × log[π*(y|x) / π_ref(y|x)] + β × log Z(x)
代入 Bradley-Terry 偏好模型(人类选 chosen 的概率):
P(y_w > y_l | x) = σ(r(x, y_w) - r(x, y_l))
DPO 损失函数(最终形式):
L_DPO = -E[log σ(β × log(π_θ(y_w|x)/π_ref(y_w|x))
- β × log(π_θ(y_l|x)/π_ref(y_l|x)))]
直觉解读:
- 增大 chosen 相对于参考模型的对数概率比值
- 减小 rejected 相对于参考模型的对数概率比值
- β 控制偏离参考策略的程度
为什么 DPO 比 RLHF 简单?
RLHF 需要单独训练一个奖励模型,再用 PPO 进行强化学习(4个神经网络同时在 GPU 内存中:actor、critic、reward model、reference model)。DPO 将奖励模型隐式地嵌入了策略模型本身,只需要策略模型和一个固定的参考模型,训练过程与 SFT 几乎相同,稳定性大幅提升。代价是 DPO 在某些场景(特别是需要复杂的长期奖励)上效果略逊于精调好的 PPO。
DPO 数据格式
{
"prompt": "如何提高代码质量?",
"chosen": "提高代码质量的关键方法:\n1. 遵循 SOLID 原则(单一职责、开放封闭等)\n2. 编写单元测试(覆盖率 >80%)\n3. 代码审查(每个 PR 至少一位审阅者)\n4. 使用 Linter 和静态分析工具...",
"rejected": "你可以多写注释,命名要规范,还有就是多练习。"
}
偏好数据格式中的常见错误
- chosen 和 rejected 差异太小:如果两个回答质量接近,DPO 训练信号会很弱,甚至引入噪声。偏好对应该有明显的质量差距(比如准确 vs 有误、详细 vs 极度简短)。
- prompt 包含在 chosen/rejected 中:TRL 的 DPOTrainer 期望 chosen 和 rejected 只包含回答部分,不包含 prompt。如果格式是完整的对话历史,需要按照 Chat Template 格式化。
- chosen 和 rejected 相同:数据清洗时务必过滤掉 chosen == rejected 的样本,这类样本会损害训练。
DPO 训练代码(TRL 库)
from trl import DPOTrainer, DPOConfig
from transformers import AutoModelForCausalLM, AutoTokenizer
from peft import LoraConfig, get_peft_model
import torch
# 加载 SFT 训练后的模型(DPO 的起点必须是 SFT 模型)
# 注意:用预训练基座直接做 DPO 效果差,必须先做 SFT
model = AutoModelForCausalLM.from_pretrained(
"./sft-checkpoint",
torch_dtype=torch.bfloat16,
device_map="auto"
)
tokenizer = AutoTokenizer.from_pretrained("./sft-checkpoint")
# DPO 配合 LoRA 使用(节省显存,参考模型自动从 LoRA 适配器推导)
lora_config = LoraConfig(
r=16, # DPO 通常用较小的 rank
lora_alpha=32,
target_modules=["q_proj", "k_proj", "v_proj", "o_proj"],
lora_dropout=0.05,
task_type="CAUSAL_LM"
)
# DPO 配置关键参数
dpo_config = DPOConfig(
output_dir="./dpo-output",
beta=0.1, # KL 散度惩罚系数(核心超参数)
learning_rate=5e-5, # 通常比 SFT 小 2-5 倍
num_train_epochs=1, # DPO 通常只需 1-2 epoch,过多会过拟合偏好数据
per_device_train_batch_size=2,
gradient_accumulation_steps=4,
max_length=2048, # prompt + chosen/rejected 的总长度上限
max_prompt_length=512, # prompt 部分的最大长度
remove_unused_columns=False, # DPO 需要保留 prompt/chosen/rejected 列
warmup_ratio=0.1, # 10% steps 用于 warm-up
logging_steps=10,
save_strategy="steps",
save_steps=100,
fp16=False,
bf16=True, # 使用 BF16 更稳定
)
trainer = DPOTrainer(
model=model,
args=dpo_config,
train_dataset=dpo_dataset, # 需有 prompt/chosen/rejected 三列
tokenizer=tokenizer,
peft_config=lora_config, # 传入 LoRA 配置,自动处理参考模型
)
trainer.train()
trainer.save_model("./dpo-final")
构建偏好数据集
偏好数据的质量比数量更重要。一般来说,500-2000 条高质量偏好对可以显著改善模型行为,但劣质偏好数据会损害模型。
方法 1:人工标注
对同一问题让模型生成多个回答,由领域专家选择最佳和最差的。优点:标注质量最高;缺点:成本最高(需要专业标注人员),难以大规模扩展。适用于高价值专业场景(医疗、法律、金融)。
方法 2:AI 辅助标注(Constitutional AI)
用强力模型(GPT-4o 或 Claude)评判哪个回答更好,并给出判断原因(以便事后审核)。成本较低,可大规模扩展。质量取决于裁判模型的能力——如果被标注模型与裁判模型能力接近,标注质量会下降。
方法 3:规则生成
用规则自动标注偏好对:例如"有引用来源 = better"、"使用专业术语 = better"、"长度在范围内 = better"。成本最低,但规则可能不完全对齐真实人类偏好,需要与其他方法结合验证。
方法 4:拒绝采样(Rejection Sampling)
对每个 prompt,用当前模型采样 N 个回答(N=8 到 16),选择质量最高的作为 chosen,随机选择一个较差的作为 rejected。随着模型迭代改进,新轮次的 chosen 质量越来越高,形成正向循环。这是 Llama 3 等模型使用的方法。
import json
import anthropic
client = anthropic.Anthropic()
def judge_preference(prompt: str, response_a: str, response_b: str) -> tuple[str, str]:
"""使用 Claude 判断两个回答的优劣,返回 (chosen, rejected)"""
judgment = client.messages.create(
model="claude-opus-4-5",
max_tokens=512,
system="""你是一个 AI 回答质量评判者。
从以下维度评判哪个回答更好:
1. 准确性(信息是否正确)
2. 完整性(是否覆盖关键点)
3. 清晰度(是否易于理解)
4. 实用性(是否对用户有实际帮助)
必须输出 JSON 格式:
{"winner": "A" or "B", "reason": "一句话原因", "score_a": 1-5, "score_b": 1-5}""",
messages=[{
"role": "user",
"content": f"问题:{prompt}\n\n回答A:{response_a}\n\n回答B:{response_b}"
}]
)
result = json.loads(judgment.content[0].text)
# 仅保留差距明显的偏好对(避免噪声)
score_diff = abs(result["score_a"] - result["score_b"])
if score_diff < 2: # 差距小于 2 分,丢弃
return None, None
if result["winner"] == "A":
return response_a, response_b # (chosen, rejected)
else:
return response_b, response_a
beta 超参的影响
| beta 值 | 效果 | 适用场景 |
|---|---|---|
| β = 0.01 - 0.05 | 激进对齐,偏好数据影响大,可能导致模型输出不流畅 | 偏好数据质量很高、数量充足时 |
| β = 0.1(默认) | 平衡对齐强度与保守程度,大多数情况稳定工作 | 绝大多数场景的推荐起始值 |
| β = 0.5 - 1.0 | 保守对齐,更多保留 SFT 基础行为 | 偏好数据噪声较大或数量不足时 |
DPO 常见失败模式
- chosen 长度偏差:如果 chosen 回答普遍比 rejected 长得多,模型会学到"回答越长越好"——导致输出冗长。检查偏好数据中 chosen 和 rejected 的平均长度是否相近(长度差 < 50%)。
- 偏好数据覆盖面窄:偏好数据只覆盖了某类问题(如代码),导致模型在其他领域的 SFT 能力退化。需要确保偏好数据覆盖模型的主要使用场景。
- 参考模型不匹配:DPO 需要一个参考模型(通常是 SFT 模型本身)。如果参考模型使用了错误的检查点,KL 散度计算会出错,导致训练不稳定。
- epoch 过多:DPO 超过 3 个 epoch 通常会过拟合偏好数据——模型开始对偏好数据中的词汇和句式形成机械性偏好。监控 eval 上的 reward margin,出现下降立刻停止。
评估对齐效果
Reward Margin(奖励边际)
DPO 训练日志中的核心指标。定义为:模型对 chosen 和 rejected 的隐式奖励差值(chosen 的对数概率比值 - rejected 的对数概率比值)。理想情况下,训练过程中 reward margin 持续为正且增大。如果 reward margin 开始下降或变为负值,说明训练出了问题。
Win Rate(胜率)
用强力模型(GPT-4o 或 Claude)作为裁判,让 DPO 前后的模型对相同测试问题各生成一个回答,由裁判判断哪个更好。DPO 后模型的胜率(vs DPO 前)通常在 55%-75% 之间才算有效对齐。低于 55% 说明对齐效果有限;高于 75% 需要检查是否有长度偏差。
MT-Bench 评分
对话质量标准评测集,包含 80 个多轮对话问题,覆盖推理、数学、代码、写作等场景,使用 GPT-4 自动评分(1-10分)。可以用来量化对齐前后的质量变化,以及与其他开源模型的横向对比。
# 计算并记录 DPO 训练指标(在 TRL 的 callbacks 中)
from transformers import TrainerCallback
class DPOMonitorCallback(TrainerCallback):
"""监控 DPO 训练关键指标"""
def on_log(self, args, state, control, logs=None, **kwargs):
if logs is None:
return
# 提取关键 DPO 指标
chosen_reward = logs.get("train/rewards/chosen", None)
rejected_reward = logs.get("train/rewards/rejected", None)
if chosen_reward and rejected_reward:
margin = chosen_reward - rejected_reward
if margin < 0:
# 奖励边际为负是严重警告信号
print(f"[警告] DPO reward margin 为负: {margin:.4f}")
print(" 可能原因:beta 太大 / 偏好数据有噪声 / 参考模型不匹配")
# 记录到 W&B(如果启用)
print(f"Step {state.global_step}: "
f"chosen={chosen_reward:.4f}, "
f"rejected={rejected_reward:.4f}, "
f"margin={margin:.4f}")
PPO vs DPO 选择指南
| 维度 | PPO(完整 RLHF) | DPO |
|---|---|---|
| 实现复杂度 | 高(4 个模型同时在显存中) | 低(与 SFT 基本相同) |
| 训练稳定性 | 低(超参数敏感) | 高(类似 SFT 训练) |
| 显存需求 | 4× 模型显存 | 2× 模型显存 |
| 长期奖励学习 | 强(强化学习的优势) | 弱(单步偏好) |
| 偏好数据要求 | 在线数据(边训练边采样) | 离线数据(预先准备好) |
| 调参难度 | 极难(学习率、KL 系数、clip 范围等) | 简单(主要是 beta) |
| 推荐场景 | 顶级模型(需要极致对齐效果) | 绝大多数业务微调场景 |
完整的对齐训练流程
步骤 1:准备 SFT 基础模型
└─ 完成 SFT 训练,保存为检查点(./sft-checkpoint)
↓
步骤 2:准备偏好数据
├─ 人工标注 / AI 辅助标注 / 拒绝采样
├─ 过滤:chosen == rejected 的样本
├─ 检查:chosen 和 rejected 长度分布是否相近
└─ 格式化为 {prompt, chosen, rejected} 的 JSON
↓
步骤 3:DPO 训练
├─ 加载 SFT 检查点为策略模型
├─ 固定 SFT 检查点为参考模型
├─ beta=0.1,epochs=1,学习率=5e-5
└─ 监控 reward margin(应持续为正)
↓
步骤 4:评估
├─ 计算 Win Rate(vs DPO 前模型)
├─ MT-Bench 评分对比
└─ 人工抽检 50-100 条样本
↓
步骤 5:迭代(如需要)
└─ 根据评估反馈调整偏好数据或 beta 值
本章核心要点
- 为什么需要对齐:SFT 让模型学会"怎么回答",但不知道"哪种回答更好"。对齐训练从人类偏好比较数据(chosen/rejected 对)中学习,使模型能够在多个候选回答中选择更符合人类期望的那个。
- RLHF 三阶段:SFT 基础训练 → 奖励模型训练(学习人类偏好评分)→ PPO 强化学习(最大化奖励,同时用 KL 散度防止奖励黑客攻击)。流程复杂但效果最强,是顶级模型(ChatGPT、Claude)使用的方法。
- DPO 的数学等价性:DPO 通过推导 RLHF 最优策略的闭式解,将奖励模型隐式嵌入策略,直接从偏好数据优化。与 RLHF 数学等价但实现简单(只需策略模型 + 固定参考模型),是绝大多数业务场景的推荐方法。
- beta 参数:控制策略偏离参考模型的程度。beta=0.1 是默认推荐值;偏好数据质量高时可降低到 0.05;数据噪声大时应升高到 0.5。监控 reward margin(chosen - rejected 的隐式奖励差)是判断训练是否正常的关键指标。
- 偏好数据质量:chosen 和 rejected 需有明显质量差距(评分差 >= 2);两者长度不应差距过大(避免长度偏差);数据应覆盖模型主要使用场景。1000 条高质量偏好对通常优于 10000 条劣质对。
- 标准流程:必须先 SFT 再 DPO——直接对预训练模型做 DPO 效果很差。DPO 通常只需 1-2 个 epoch,超过 3 个 epoch 容易过拟合。评估必须包含 Win Rate 对比和人工抽检。