动机
PPO 需要在显存中维持 4 个模型(policy、reference、reward 模型、value head)、复杂的 RL 基础设施,并且以不稳定著称。DPO [302] 提出的问题是:我们能否跳过 RL,直接从偏好中学习?
关键洞察:在 RLHF 目标(reward 最大化 + KL 惩罚)下的最优 policy 拥有解析解。我们可以推导出一个有监督 loss,隐式优化同一个目标。
数学推导
第 1 步:RLHF 目标:
第 2 步:最优解为:
第 3 步:重排式子,用 policy 表达 reward:
第 4 步:代入 Bradley-Terry 偏好模型 。 项相互抵消!
Gradient 分析
DPO 的 gradient 可分解为:
解读:gradient 会提升「被选」项的概率,降低「被拒」项的概率。当模型当前更偏好错误答案时,权重最大——它把学习重点放在「易混淆」的成对样本上。
TRL 实现
下面给出一个使用 HuggingFace TRL 的最小可运行示例。
from trl import DPOConfig, DPOTrainer
from transformers import AutoModelForCausalLM, AutoTokenizer
from peft import LoraConfig
from datasets import load_dataset
model = AutoModelForCausalLM.from_pretrained("meta-llama/Llama-3.1-8B-Instruct",
torch_dtype=torch.bfloat16, attn_implementation="flash_attention_2")
tokenizer = AutoTokenizer.from_pretrained("meta-llama/Llama-3.1-8B-Instruct")
# Dataset format: {"prompt": str, "chosen": str, "rejected": str}
dataset = load_dataset("argilla/ultrafeedback-binarized-preferences")
lora_config = LoraConfig(r=64, lora_alpha=16, lora_dropout=0.05,
target_modules=["q_proj","k_proj","v_proj","o_proj","gate_proj","up_proj","down_proj"])
dpo_config = DPOConfig(
output_dir="./dpo_output",
beta=0.1, # KL regularization strength
learning_rate=5e-7, # Very low LR for stability
loss_type="sigmoid", # Standard DPO loss
max_length=2048, # Max sequence length
max_prompt_length=1024, # Truncation for prompts
per_device_train_batch_size=2,
gradient_accumulation_steps=8, # Effective batch = 16
gradient_checkpointing=True,
bf16=True,
num_train_epochs=1, # DPO overfits fast - 1 epoch!
warmup_ratio=0.1,
logging_steps=10,
eval_strategy="steps",
eval_steps=200,
save_strategy="steps",
save_steps=500,
)
trainer = DPOTrainer(
model=model,
ref_model=None, # With LoRA, ref = base model (no copy needed!)
args=dpo_config,
train_dataset=dataset["train"],
eval_dataset=dataset["test"],
tokenizer=tokenizer,
peft_config=lora_config,
)
trainer.train()
# Key metrics to monitor: train/rewards/chosen, train/rewards/rejected, train/rewards/marginsDPO 的完整机制
本节给出 DPO 完整的计算细节——训练过程中在 token 层面究竟发生了什么。
序列级对数概率
DPO 中的关键量是给定 prompt 时整条序列 的对数概率。它按各 token 对数概率之和计算:
每一项 是位置 处针对序列中实际 token 的 log-softmax 输出。这与标准语言建模所用的交叉熵 loss 完全相同——但这里我们做的是求和,而不是取平均。
关键细节:gradient 会流经 和 中的每一个 token 位置。中间 token 没有被 mask——每个 token 都对序列级对数概率有贡献。
DPO Loss 的分解
从 loss 出发:
其中「隐式 reward margin」 为:
展开到 token 级别:
Forward Pass:逐步详解
对于一条训练样本 :
- 拼接:构造两条序列: 和 。在 batch 内补齐到相同长度。
- 前向传播(policy ):让两条序列都通过模型。收集每个回答位置上的 logits。
- 提取对数概率:在回答的每个位置 ,取 ——即实际 token 的对数概率。
- 对 token 求和:logp_chosen(6.7)logp_rejected(6.8)
- 减去 reference(预先计算,或来自第二次前向传播):ratio_w(6.9)ratio_l(6.10)
- 计算 loss:
- 反向传播:gradient 经由第 5 步 第 4 步 第 3 步 第 2 步回传,以更新 。
Token 级 Gradient 分析
每个 token 都能得到 gradient 吗?能。被选序列中位置 处 logits 的 gradient 为:
关键洞察:缩放因子 在两条序列中为所有 token 共享。它起到自适应学习率的作用:
- 当 很小(模型无法区分被选与被拒)时:缩放为 ——gradient 强,学习激进。
- 当 很大(模型已经偏好被选)时:缩放为 ——gradient 可忽略,不要过拟合。
对被选 token 的影响:概率被提升(对数概率被推高)。
对被拒 token 的影响:概率被降低(对数概率被压低)。
相对于 reference:只有与 的差值才重要。如果模型已经给被选回答分配了高概率(与 reference 一致),gradient 就很小。
逐 Token vs. 序列级:长度归一化
一个微妙的问题:更长的序列天然具有更低的对数概率(求和的项更多,每项 )。如果 ,loss 可能偏向于偏好更短的回答。
解决方案:
- 长度归一化 DPO:用 替换 。部分实现采用了这一做法(SimPO 即采用它)。
- 标准 DPO:使用原始求和(不做归一化)。这会隐式地惩罚冗长——模型必须为被选回答中的每个 token 赋予高概率。
- 实际影响:在基准测试上,长度归一化 DPO 减少了长度博弈,但可能损害指令遵循质量。标准(未归一化)版本在生产中更常见。
标签掩码:哪些 token 会收到 Gradient
伪代码:DPO 训练步
常见陷阱
DPO 变体及其各自的失败场景
β\beta 选择指南
档位适用场景0.01非常激进仅当数据极其干净且你需要较大的分布偏移时0.05激进数据质量好,希望在 SFT 基础上有明显提升0.1标准默认起点。质量与稳定性之间平衡良好0.2保守数据有噪声,或模型已接近期望行为0.5非常保守安全性微调,且不能破坏模型既有能力
DPO 的 Batch Size 配置与扩展
与在单序列 token 预测上运行的标准 SFT 不同,DPO 利用成对 loss,将被偏好序列与不被偏好序列进行比较。这从根本上改变了显存占用与优化稳定性。
全局 Batch Size 目标
跨多种 DPO 实现的经验证据确立了一个最优的全局 batch size 区间:
- :隐式 reward 估计中的 gradient 噪声严重 policy 在对齐目标之间(有用性 vs. 安全性)破坏性地来回振荡。
- :收敛速度收益递减;分布式计算中的通信开销很高。
数学分解
因为 DPO 会同时加载两份模型副本(活动 policy + 冻结的 reference ),每条序列的显存占用翻倍。全局 batch size 可分解为:
- :单设备微 batch 大小(每次前向传播处理的偏好成对样本数)。
- :并行数据处理设备数量。
- :权重更新前的 gradient 累积步数。
成对倍增因子:单个 DPO 数据实例包含一个 prompt()、一个被选回答()和一个被拒回答()。每个微 batch 实际产生的张量负载为:
对于 7B 参数规模的模型,在 80GB GPU 上以 4096–8192 token 的上下文长度运行时,物理上限被严格约束在 。
分布式扩展配置
| 配置 | 单 GPU | 8-GPU 节点 |
|---|---|---|
| 64 | 64 | |
| 2(4 条序列) | 2(4 条序列) | |
| 1 | 8 | |
| 32 步 | 4 步 | |
| 吞吐量 | 串行/慢 | 高并行吞吐 |
表 6.1:DPO 训练的分布式扩展配置( 目标)。
显存优化:预计算 Reference 对数概率
DPO loss 为:
因为 在整个训练过程中完全静态,其输出可以预先计算:
DPO 扩展与变体
直接偏好优化(Direct Preference Optimization,DPO)通过推导 reward 函数与最优 policy 之间的解析映射,将 RLHF 重新表述为一个监督学习问题。标准 DPO loss 为:
其中 是被偏好(胜出)的回答, 是不被偏好(落败)的回答, 控制 KL 惩罚的强度。以下小节介绍最重要的扩展与变体。
f-DPO——广义 f-Divergence DPO
f-DPO loss 用 f-散度生成函数的导数替换对数比:
其中 是 f-散度生成函数的导数。
Robust DPO
假设每个标签以概率 (噪声率)被翻转。去偏后的 loss 为:
其中 是把 视为被偏好回答的标准 DPO loss, 是标签翻转后的 loss。这一修正去除了标签噪声引入的偏差。
TR-DPO——信任域 DPO
TR-DPO 使用指数移动平均(EMA)更新 reference 模型:
其中 是混合系数。每 个 gradient 步执行一次。
EXO——精确优化
EXO 最小化模型分布与目标(reward 最优)分布之间的反向 KL:
其中 是最优 policy。实践中,EXO 利用可用的偏好成对样本来近似它:
注意与 DPO 相比, 与 的角色是对调的。
NCA——噪声对比对齐
NCA 把对齐重新表述为噪声对比估计。loss 包含三项:
其中 是隐式 reward。第一项鼓励 获得高 reward;第二、三项则惩罚 和 获得高 reward(防止崩塌)。
SLiC-HF——序列似然校准
SLiC-HF loss 为:
其中 是 margin 阈值。当模型在胜出与落败回答之间已经给出 的 margin 时,loss 为零。
Iterative RPO——推理偏好优化
RPO loss 结合了 DPO 与 SFT:
其中 是作用于胜出回答的标准语言建模 loss。
SimPO——简化版偏好优化
SimPO 把隐式 reward 定义为:
loss 定义为:
其中 是目标 reward margin,它确保胜出回答的 reward 至少比落败回答高出 。