本章涵盖一类方法,它们以不同的目标函数、数据假设或架构权衡来扩展或替换 DPO。每种方法都针对标准 offline DPO 的某个具体局限:分布偏移(Online DPO)、需要配对数据(KTO)、对噪声标签过拟合(IPO)、参考模型显存开销(ORPO)或训练复杂度(Best-of-N)。

Online DPO

动机

标准 DPO 的主要局限:偏好数据由一个不同的模型生成(通常是较旧的 checkpoint,甚至是不同的模型族)。随着训练推进,policy 生成的文本与训练 pair 已完全不同 → loss 在一个无关分布上进行优化。

Online DPO 的解决方案[127]:每一步都从当前 policy 生成新鲜的偏好 pair,用 reward model 判定,然后应用 DPO loss。

算法

  1. 从当前 πθ 为每个 prompt 生成 K 条响应
  2. 用 reward model rϕ 给所有响应打分
  3. 构造 pair:最高分 = chosen,最低分 = rejected
  4. 在这些新鲜 pair 上应用 DPO loss
  5. 重复(每步重新生成)

TRL 实现

下面给出一个基于 HuggingFace TRL 的最小可运行示例。

from trl import OnlineDPOConfig, OnlineDPOTrainer
from transformers import AutoModelForCausalLM, AutoModelForSequenceClassification
 
model = AutoModelForCausalLM.from_pretrained("meta-llama/Llama-3.1-8B-Instruct",
    torch_dtype=torch.bfloat16)
reward_model = AutoModelForSequenceClassification.from_pretrained(
    "RLHFlow/ArmoRM-Llama3-8B-v0.1", torch_dtype=torch.bfloat16)
 
online_dpo_config = OnlineDPOConfig(
    output_dir="./online_dpo_output",
    learning_rate=5e-7,
    beta=0.1,                    # DPO beta (same meaning as standard DPO)
    num_generations=4,           # K responses per prompt
    per_device_train_batch_size=4,
    gradient_accumulation_steps=4,
    max_new_tokens=512,
    temperature=0.7,
    bf16=True,
    num_train_epochs=1,
    logging_steps=10,
)
 
trainer = OnlineDPOTrainer(
    model=model,
    reward_model=reward_model,
    args=online_dpo_config,
    train_dataset=prompt_dataset,
    tokenizer=tokenizer,
)
trainer.train()

Online DPO vs Offline DPO vs PPO

数据模型Loss最适合Offline DPO静态 pair2(policy + reference)DPO快速对齐、算力有限Online DPO从 πθ 新鲜采样3(policy + reference + reward model)DPO当 DPO 停滞、需要探索时PPO从 πθ 新鲜采样4(policy + reference + reward model + value head)PPO clip极致质量、复杂推理

KTO ——Kahneman-Tversky Optimization

动机

DPO 需要成对的偏好数据:对同一 prompt,你既需要好的响应也需要坏的响应。实际上大多数反馈是未配对的:用户对单个响应给出点赞/点踩,没有匹配的 pair。

KTO 的洞见[86]:使用前景理论(来自行为经济学)。人类对损失的感受比对收益更强烈。“点踩”应当产生比“点赞”更强的 gradient。

Loss 函数

ℒKTO=𝔼yw​[λw​(1−v⁡(x,yw))]+𝔼yl​[λl⋅v⁡(x,yl)](8.1)

其中 v⁡(x,y)=σ⁡(β​log⁡πθ​(y|x)πref​(y|x)−zref),zref 是期望 KL 散度(一个滑动 baseline)。

TRL 实现

下面给出一个基于 HuggingFace TRL 的最小可运行示例。

from trl import KTOConfig, KTOTrainer
 
# Dataset format: {"prompt": str, "completion": str, "label": bool}
# label=True for desirable, label=False for undesirable
kto_dataset = [
    {"prompt": "What's 2+2?", "completion": "The answer is 4.", "label": True},
    {"prompt": "What's 2+2?", "completion": "It might be 5.", "label": False},
]
 
kto_config = KTOConfig(
    output_dir="./kto_output",
    beta=0.1,
    desirable_weight=1.0,        # Weight for good examples
    undesirable_weight=1.0,      # Weight for bad examples (increase for loss aversion)
    learning_rate=5e-7,
    max_length=2048,
    per_device_train_batch_size=4,
    gradient_accumulation_steps=4,
    num_train_epochs=1,
    bf16=True,
)
 
trainer = KTOTrainer(
    model=model,
    ref_model=ref_model,  # Or None with LoRA
    args=kto_config,
    train_dataset=kto_dataset,
    tokenizer=tokenizer,
)
trainer.train()

何时选择 KTO

  • 你有二值反馈,但没有匹配的 pair
  • 大规模的生产点赞/点踩数据
  • 某一类占主导(例如 90% 好、10% 坏)——KTO 对不平衡的处理更好
  • 带噪声标签的快速迭代(比 DPO 对噪声更鲁棒)

IPO ——Identity Preference Optimization

动机

DPO 存在一个退化解:只要把 chosen 与 rejected 之间的 margin 变得无限大,就能取得零 loss。实践中这意味着 DPO 过拟合——把 chosen 的概率推到 1、rejected 推到 0,记住训练数据。

IPO 的修正[14]:不用会饱和的 log-sigmoid,而使用平方 loss,其目标是某个特定的 margin。loss 在有限的间隔处取到最小,而非在无穷处。

Loss 函数

ℒIPO=𝔼⁡[(log⁡πθ​(yw|x)πref​(yw|x)−log⁡πθ​(yl|x)πref​(yl|x)−12​β)2](8.2)

TRL 实现

下面给出一个基于 HuggingFace TRL 的最小可运行示例。

from trl import DPOConfig, DPOTrainer
 
# IPO is implemented as a DPO loss_type variant in TRL
ipo_config = DPOConfig(
    output_dir="./ipo_output",
    beta=0.1,
    loss_type="ipo",             # The key difference!
    learning_rate=5e-7,
    max_length=2048,
    per_device_train_batch_size=4,
    gradient_accumulation_steps=8,
    bf16=True,
    num_train_epochs=1,
)
 
trainer = DPOTrainer(
    model=model, ref_model=None, args=ipo_config,
    train_dataset=pref_dataset, tokenizer=tokenizer, peft_config=lora_config,
)
trainer.train()

何时选择 IPO 而非 DPO

  • 噪声偏好数据(众包标注、AI 评判有误)
  • 观察到 DPO 过拟合(train loss → 0,但 eval 退化)
  • 想要更保守、更鲁棒的对齐
  • 需要多个 epoch(DPO 在第 1 个 epoch 之后会退化;IPO 更稳定)

ORPO ——Odds Ratio Preference Optimization

动机

到目前为止的所有方法都需要一个参考模型——要么作为独立副本(显存翻倍),要么通过 LoRA 隐式提供。ORPO [141] 把 SFT 与偏好对齐结合在单一 loss 中,从而彻底消除了参考模型。

关键洞见:用生成 chosen 与 rejected 的几率比作为偏好信号。SFT 组件天然防止坍缩(无需 KL 正则化)。

Loss 函数

ℒORPO=ℒSFT​(yw)⏟standard NLL on chosen−λ⋅log⁡σ⁡(log⁡oddsθ​(yw|x)oddsθ​(yl|x))⏟preference alignment via odds ratio(8.3)

其中 oddsθ​(y|x)=Pθ​(y|x)1−Pθ​(y|x)。

TRL 实现

下面给出一个基于 HuggingFace TRL 的最小可运行示例。

from trl import ORPOConfig, ORPOTrainer
 
orpo_config = ORPOConfig(
    output_dir="./orpo_output",
    beta=0.1,                    # Odds ratio weight (lambda)
    learning_rate=5e-7,
    max_length=2048,
    per_device_train_batch_size=2,
    gradient_accumulation_steps=8,
    bf16=True,
    num_train_epochs=1,
    gradient_checkpointing=True,
)
 
trainer = ORPOTrainer(
    model=model,                 # No ref_model needed!
    args=orpo_config,
    train_dataset=pref_dataset,  # Same format as DPO: prompt/chosen/rejected
    tokenizer=tokenizer,
    peft_config=lora_config,
)
trainer.train()

何时选择 ORPO

  • 显存受限:无法承担参考模型副本(70B 模型可节省 70–140GB)
  • 从 base model 开始(尚未做 SFT)——ORPO 同时完成 SFT
  • 想要尽可能简单的流水线:一个模型、一个 loss、一次训练
  • 从一开始就有好的偏好数据

Best-of-N 采样(拒绝采样)

动机

有时最简单的方法反而胜出。Best-of-N [262] 在 RL 阶段完全不需要训练——只需生成多个候选并挑出其中最好的一个。

算法

  1. 对每个 prompt,从 policy 生成 N 条响应(通常 N=4–64)
  2. 用 reward model 给所有响应打分
  3. 选择得分最高的响应
  4. (可选)把选中的响应用作下一轮迭代的 SFT 数据
Best-of-N response:y∗=argmaxyi∼πθ(⋅|x)rϕ(x,yi)(8.4)

TRL 实现

下面给出一个基于 HuggingFace TRL 的最小可运行示例。

from transformers import pipeline
import numpy as np
 
# Inference-time Best-of-N (manual implementation)
gen_pipeline = pipeline("text-generation", model=model, tokenizer=tokenizer)
 
def best_of_n(prompt, n=16, temperature=0.8):
    """Generate N candidates and return the highest-reward one."""
    candidates = gen_pipeline(
        prompt, num_return_sequences=n,
        temperature=temperature, do_sample=True, max_new_tokens=512,
    )
    scores = [reward_model.score(prompt, c["generated_text"]) for c in candidates]
    return candidates[np.argmax(scores)]["generated_text"]
 
best_response = best_of_n(prompt, n=16)
 
# Training: Rejection Sampling Fine-Tuning (RFT)
from trl import SFTConfig, SFTTrainer
 
# Step 1: Generate and filter
all_responses = []
for prompt in prompts:
    candidates = [generate(prompt, temp=0.9) for _ in range(16)]
    scores = [reward_model.score(prompt, c) for c in candidates]
    best_idx = np.argmax(scores)
    if scores[best_idx] > threshold:  # Quality gate
        all_responses.append({"prompt": prompt, "completion": candidates[best_idx]})
 
# Step 2: SFT on best responses
sft_config = SFTConfig(output_dir="./rft_output", learning_rate=2e-5, num_train_epochs=2, max_seq_length=2048)
trainer = SFTTrainer(model=model, args=sft_config, train_dataset=all_responses, tokenizer=tokenizer)
trainer.train()
# Step 3: Repeat from Step 1 with updated model (iterative RFT)

Best-of-N 的扩展律

N质量收益成本说明1基线1×标准采样4+5–8% win-rate4×最低可用。成本/质量比良好16+10–15% win-rate16×很强。通常可媲美 PPO 的质量64+15–20% win-rate64×开始出现收益递减256+18–22% win-rate256×仅用于关键应用

总结:如何选择对齐方法

至此,我们已经完整考察了偏好优化与基于 RL 的对齐方法的整体图景。本节把关键权衡汇总成一份统一的参考,帮助从业者根据自己的约束条件选择合适的方案。

方法模型数据算力稳定性最适合
PPO4Online(生成)极高低极致质量、复杂推理
GRPO2(无 critic)Online(生成)高中数学/代码(可验证奖励)
DPO2Offline pair低高风格/安全、算力有限
Online DPO3Online(生成)中中–高无分布偏移的 DPO
KTO2未配对二值低高生产反馈、点赞/点踩
IPO2Offline pair低极高噪声标签、抗过拟合
ORPO1Offline pair极低高显存受限、SFT 与对齐合一
Best-of-N1+RMOnline(生成)中完美强基线、数据生成

表 8.1:各种对齐方法的跨方法对比。

图 8.1:近似的质量–算力前沿。位于 SFT 上限线之上的方法,其表现可超越仅靠监督微调所能达到的水平。位置仅为示意,且依模型而定。
图 8.1:近似的质量–算力前沿。位于 SFT 上限线之上的方法,其表现可超越仅靠监督微调所能达到的水平。位置仅为示意,且依模型而定。