Chapter 08

RLHF & DPO:偏好对齐训练

SFT 让模型学会任务,对齐让模型知道什么是"好回答"。DPO 用更简洁的方式实现了 RLHF 的核心效果。

为什么需要对齐

SFT 训练后的模型知道"怎么回答",但不一定知道"哪种回答更好"。对齐训练通过让模型学习人类偏好,使其在多个候选回答中选择更符合期望的那个。

RLHF(人类反馈强化学习)
InstructGPT(ChatGPT 的前身)使用的方法。三阶段:SFT → 训练奖励模型 → PPO 强化学习。效果好但流程复杂,对调参要求高。
DPO(直接偏好优化)
Rafailov et al. 2023 提出。直接从偏好数据优化策略,无需单独训练奖励模型,避免了 PPO 的不稳定性。目前最广泛使用的对齐方法。

DPO 数据格式

{
  "prompt": "如何提高代码质量?",
  "chosen": "提高代码质量的关键方法:\n1. 遵循 SOLID 原则\n2. 编写单元测试(覆盖率 >80%)\n3. 代码审查...",
  "rejected": "你可以多写注释,命名要规范,还有就是多练习。"
}

DPO 训练代码

from trl import DPOTrainer, DPOConfig
from transformers import AutoModelForCausalLM, AutoTokenizer
from peft import LoraConfig

# 加载 SFT 训练后的模型(DPO 的起点)
model = AutoModelForCausalLM.from_pretrained(
    "./sft-checkpoint",  # 先做 SFT,再做 DPO
    torch_dtype="bfloat16"
)
tokenizer = AutoTokenizer.from_pretrained("./sft-checkpoint")

# DPO 也可以配合 LoRA 使用(节省显存)
lora_config = LoraConfig(
    r=8, lora_alpha=16,
    target_modules=["q_proj", "v_proj"],
    task_type="CAUSAL_LM"
)

trainer = DPOTrainer(
    model=model,
    args=DPOConfig(
        output_dir="./dpo-output",
        beta=0.1,            # KL 散度惩罚系数,越大越保守
        learning_rate=5e-5,
        num_train_epochs=1,   # DPO 通常只需 1-2 epoch
        per_device_train_batch_size=2,
        gradient_accumulation_steps=4,
        max_length=2048,
        max_prompt_length=512,
        remove_unused_columns=False,
    ),
    train_dataset=dpo_dataset,
    tokenizer=tokenizer,
    peft_config=lora_config,
)
trainer.train()

构建偏好数据集

偏好数据的质量比数量更重要。构建方式:

  • 方法 1:人工标注 — 对同一问题生成多个回答,让专家选择最佳和最差
  • 方法 2:AI 标注(Constitutional AI) — 用 GPT-4 或 Claude 评判哪个回答更好,并给出原因
  • 方法 3:规则生成 — 用规则自动标注(更长 = better,有引用 = better,无有害内容 = better)
  • 方法 4:拒绝采样 — 从现有模型采样,选择 best-of-N 作为 chosen,其余作为 rejected
  • # 用 Claude 做 AI 标注
    def judge_preference(prompt: str, response_a: str, response_b: str) -> tuple:
        judgment = client.messages.create(
            model="claude-opus-4-6",
            max_tokens=512,
            system="""你是一个 AI 回答质量评判者。
    判断哪个回答更好,输出 JSON:{"winner": "A" or "B", "reason": "原因"}""",
            messages=[{"role": "user",
                "content": f"问题:{prompt}\n\n回答A:{response_a}\n\n回答B:{response_b}"}]
        )
        result = json.loads(judgment.content[0].text)
        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 是目前最实用的对齐方法,比 PPO 稳定且简单。偏好数据质量决定对齐效果,beta=0.1 是推荐起始值。先 SFT 再 DPO 是标准流程。下一章模型合并与推理部署。