Skip to content
Published on

从 RLHF 到 DPO:LLM 对齐(Alignment)技术论文深度解析

分享
Authors
LLM Alignment Survey

引言

大规模语言模型(LLM)通过学习海量语料获得了惊人的语言能力,但仅靠预训练无法保证输出符合人类的意图与价值观。生成有害内容、无视用户指示、或是自信满满地陈述不实信息,这些问题屡见不鲜。为弥合这一差距而生的研究领域,正是LLM 对齐(Alignment)。

自 2022 年 OpenAI 的 InstructGPT 论文将 RLHF(Reinforcement Learning from Human Feedback,基于人类反馈的强化学习)流水线体系化以来,Anthropic 的 Constitutional AI、Stanford 的 DPO(Direct Preference Optimization,直接偏好优化),以及 KTO、IPO、ORPO 等各种后续技术相继出现。本文将系统分析这些核心论文的数学基础、实现方式,以及实务应用时的注意事项。

LLM 对齐问题的定义

为什么需要对齐

预训练好的 LLM 优化的是互联网文本上的下一个词元预测(next-token prediction)。这一目标并不直接对应人类期望的「有用(Helpful)、诚实(Honest)、无害(Harmless)」输出。具体来说,存在三个核心问题。

  1. 有用性(Helpfulness):准确理解并执行用户指示的能力
  2. 诚实性(Honesty):承认自己不知道的事,并将幻觉降到最低的能力
  3. 无害性(Harmlessness):拒绝生成有害或带偏见内容的能力

对齐的数学框架

对齐问题被形式化为基于奖励函数 r(x, y) 的策略优化问题。给定提示词 x,在限制与预训练参考策略之间的偏离的同时,最大化策略 pi 生成的响应 y 所获得的奖励。

maxπExD,yπ(x)[r(x,y)]βKL[π(x)πref(x)]\max_{\pi} \mathbb{E}_{x \sim \mathcal{D}, y \sim \pi(\cdot|x)} \left[ r(x, y) \right] - \beta \, \text{KL}\left[\pi(\cdot|x) \| \pi_{\text{ref}}(\cdot|x)\right]

这里的 beta 是调节 KL 散度惩罚强度的超参数。beta 太小会发生奖励黑客(reward hacking),太大则训练会变慢。

RLHF 流水线深度分析

InstructGPT 的三阶段流水线

Ouyang 等人(2022)的 InstructGPT 论文将 RLHF 流水线体系化为三个阶段。

第一阶段:监督微调(SFT)

用人类标注者撰写的示范数据对基础模型进行微调。在这一阶段,模型学习到遵循指示的基本能力。

第二阶段:奖励模型(RM)训练

针对同一提示词生成多个响应,再由人类标注者对其排出偏好顺序。用这些数据训练奖励模型。

第三阶段:通过 PPO 进行强化学习

利用奖励模型的反馈,用 PPO 算法优化策略。

奖励模型训练实现

奖励模型基于 Bradley-Terry 模型,通过最大化偏好对(chosen、rejected)之间的对数概率差来训练。

import torch
import torch.nn as nn
from transformers import AutoModelForSequenceClassification, AutoTokenizer

class RewardModel(nn.Module):
    def __init__(self, model_name="gpt2"):
        super().__init__()
        self.model = AutoModelForSequenceClassification.from_pretrained(
            model_name, num_labels=1
        )

    def forward(self, input_ids, attention_mask):
        outputs = self.model(input_ids=input_ids, attention_mask=attention_mask)
        return outputs.logits.squeeze(-1)

def reward_model_loss(reward_model, chosen_ids, chosen_mask, rejected_ids, rejected_mask):
    """基于 Bradley-Terry 偏好模型的奖励模型损失函数"""
    r_chosen = reward_model(chosen_ids, chosen_mask)
    r_rejected = reward_model(rejected_ids, rejected_mask)

    # 训练使偏好响应的奖励高于非偏好响应
    loss = -torch.log(torch.sigmoid(r_chosen - r_rejected)).mean()
    accuracy = (r_chosen > r_rejected).float().mean()

    return loss, accuracy

偏好数据集构建

RLHF 流水线的核心在于构建高质量的偏好数据集。每个样本由提示词、偏好响应、非偏好响应三元组构成。

from datasets import Dataset

def build_preference_dataset(prompts, model, tokenizer, num_samples=4):
    """针对每个提示词生成多个响应并构建偏好对的流水线"""
    preference_data = []

    for prompt in prompts:
        inputs = tokenizer(prompt, return_tensors="pt")
        responses = []

        # 通过温度采样生成多样化响应
        for _ in range(num_samples):
            output = model.generate(
                **inputs,
                max_new_tokens=256,
                temperature=0.8,
                do_sample=True,
            )
            response = tokenizer.decode(output[0], skip_special_tokens=True)
            responses.append(response)

        # 实际流水线中由人类标注者排序
        # 此处以奖励模型分数代替
        scored = [(r, reward_model_score(prompt, r)) for r in responses]
        scored.sort(key=lambda x: x[1], reverse=True)

        # 用最高分与最低分构建偏好数据
        preference_data.append({
            "prompt": prompt,
            "chosen": scored[0][0],
            "rejected": scored[-1][0],
        })

    return Dataset.from_list(preference_data)

Constitutional AI:基于 AI 反馈的对齐

Anthropic 的方法

Bai 等人(2022)的 Constitutional AI,是为减少 RLHF 对人类标注依赖而提出的创新方法。其核心思想是让AI 自己批判并改进自身的输出,这一过程由预先定义的原则(Constitution,宪法)来引导。

两阶段流程

监督学习阶段(自我批判与修正)

  1. 模型生成一个可能有害的响应
  2. 依据宪法原则批判自己的响应
  3. 反映批判意见,生成修正后的响应
  4. 用修正后的响应执行 SFT

RL 阶段(RLAIF —— RL from AI Feedback)

  1. AI 判断两个响应中哪一个更符合原则
  2. 用这一 AI 偏好数据训练奖励模型
  3. 用奖励模型通过 PPO 优化策略

Constitutional AI 最大的优势在于,能在大幅削减人类标注成本的同时,实现基于原则、透明且可解释的对齐。

DPO:数学基础与实现

RLHF 目标函数的解析解

Rafailov 等人(2023)提出的 DPO,是 RLHF 最优雅的替代方案。其核心洞见是:带 KL 约束的奖励最大化问题存在解析解

最优策略具有如下形式。

π(yx)=1Z(x)πref(yx)exp(1βr(x,y))\pi^*(y|x) = \frac{1}{Z(x)} \pi_{\text{ref}}(y|x) \exp\left(\frac{1}{\beta} r(x, y)\right)

反解这一方程,可以将奖励函数表示为策略的比值。

r(x,y)=βlogπ(yx)πref(yx)+βlogZ(x)r(x, y) = \beta \log \frac{\pi^*(y|x)}{\pi_{\text{ref}}(y|x)} + \beta \log Z(x)

将其代入 Bradley-Terry 模型,便得到无需奖励模型、直接优化策略的 DPO 损失函数。

LDPO(πθ;πref)=E(x,yw,yl)[logσ(βlogπθ(ywx)πref(ywx)βlogπθ(ylx)πref(ylx))]\mathcal{L}_{\text{DPO}}(\pi_\theta; \pi_{\text{ref}}) = -\mathbb{E}_{(x, y_w, y_l)} \left[\log \sigma\left(\beta \log \frac{\pi_\theta(y_w|x)}{\pi_{\text{ref}}(y_w|x)} - \beta \log \frac{\pi_\theta(y_l|x)}{\pi_{\text{ref}}(y_l|x)}\right)\right]

DPO 训练循环实现

import torch
import torch.nn.functional as F
from transformers import AutoModelForCausalLM, AutoTokenizer

def compute_dpo_loss(
    policy_model,
    reference_model,
    chosen_ids,
    chosen_mask,
    rejected_ids,
    rejected_mask,
    beta=0.1,
):
    """DPO 损失函数:无需奖励模型,直接优化策略"""
    # 计算策略模型的对数概率
    policy_chosen_logps = get_log_probs(policy_model, chosen_ids, chosen_mask)
    policy_rejected_logps = get_log_probs(policy_model, rejected_ids, rejected_mask)

    # 计算参考模型的对数概率(不需要梯度)
    with torch.no_grad():
        ref_chosen_logps = get_log_probs(reference_model, chosen_ids, chosen_mask)
        ref_rejected_logps = get_log_probs(reference_model, rejected_ids, rejected_mask)

    # 计算 DPO 对数比率
    chosen_logratios = policy_chosen_logps - ref_chosen_logps
    rejected_logratios = policy_rejected_logps - ref_rejected_logps

    # DPO 损失:-log(sigmoid(beta * (chosen_logratio - rejected_logratio)))
    logits = beta * (chosen_logratios - rejected_logratios)
    loss = -F.logsigmoid(logits).mean()

    # 指标:偏好响应的隐式奖励更高的比例
    reward_accuracy = (logits > 0).float().mean()

    return loss, reward_accuracy

def get_log_probs(model, input_ids, attention_mask):
    """对序列的逐词元对数概率求和"""
    outputs = model(input_ids=input_ids, attention_mask=attention_mask)
    logits = outputs.logits[:, :-1, :]
    labels = input_ids[:, 1:]
    log_probs = F.log_softmax(logits, dim=-1)
    selected_log_probs = torch.gather(
        log_probs, dim=-1, index=labels.unsqueeze(-1)
    ).squeeze(-1)
    mask = attention_mask[:, 1:]
    return (selected_log_probs * mask).sum(dim=-1)

DPO 的优缺点

优点

  • 无需训练奖励模型,流水线更简单
  • 相比 PPO 内存占用更少(2 个模型 vs 4 个模型)
  • 超参数调优相对简单

缺点

  • 容易受到与参考模型之间的分布偏移(distribution shift)影响
  • 依赖离线数据,缺乏探索(exploration)
  • 对偏好数据的质量非常敏感

PPO 训练稳定性

RLHF 中的 PPO:实现核心

PPO(Proximal Policy Optimization)是 RLHF 中使用最广泛的强化学习算法。其核心是裁剪机制(clipping),通过限制策略更新的幅度来保证训练稳定性。

import torch
import torch.nn.functional as F

def ppo_step(
    policy_model,
    value_model,
    old_log_probs,
    states,
    actions,
    rewards,
    advantages,
    clip_epsilon=0.2,
    value_clip=0.2,
    kl_coeff=0.1,
    ref_log_probs=None,
):
    """PPO 更新步骤(用于 LLM RLHF)"""
    # 当前策略的对数概率
    new_log_probs = policy_model.get_log_probs(states, actions)

    # 重要度比率计算
    ratio = torch.exp(new_log_probs - old_log_probs)

    # 裁剪后的代理目标函数
    surr1 = ratio * advantages
    surr2 = torch.clamp(ratio, 1.0 - clip_epsilon, 1.0 + clip_epsilon) * advantages
    policy_loss = -torch.min(surr1, surr2).mean()

    # 价值函数损失(应用裁剪)
    new_values = value_model(states)
    value_loss = F.mse_loss(new_values, rewards)

    # KL 散度惩罚(相对参考模型)
    kl_penalty = 0.0
    if ref_log_probs is not None:
        kl_div = (old_log_probs - ref_log_probs).mean()
        kl_penalty = kl_coeff * kl_div

    total_loss = policy_loss + 0.5 * value_loss + kl_penalty

    return total_loss, {
        "policy_loss": policy_loss.item(),
        "value_loss": value_loss.item(),
        "kl_penalty": kl_penalty if isinstance(kl_penalty, float) else kl_penalty.item(),
        "mean_ratio": ratio.mean().item(),
    }

PPO 训练的主要不稳定性来源

  1. 奖励黑客(Reward Hacking):利用奖励模型的弱点,让实际质量不佳的输出获得高奖励的现象
  2. KL 散度爆炸:与参考模型的距离急剧增大,生成无意义文本
  3. 价值函数估计误差:异常偏大的奖励估计值使训练变得不稳定
  4. 长序列问题:词元数越多,信用分配(credit assignment)问题越严重

最新对齐技术:KTO、IPO、ORPO

KTO(Kahneman-Tversky Optimization)

Ethayarajh 等人(2024)提出的 KTO 无需成对偏好数据即可完成对齐。它借用了行为经济学中的前景理论(Prospect Theory),仅凭每个响应「是否理想」的二元信号进行学习。

KTO 的核心在于反映了人类决策对损失更敏感这一损失厌恶(Loss Aversion)特性。它将对不理想响应的惩罚,设定得比对理想响应的奖励更大。

IPO(Identity Preference Optimization)

Azar 等人(2024)提出的 IPO,通过增加一个正则化项来解决 DPO 的过拟合(overfitting)问题。DPO 在对训练数据过拟合时,往往会将偏好响应的概率推向极端,IPO 则可以防止这一点。

LIPO=(logπθ(ywx)πref(ywx)logπθ(ylx)πref(ylx)12β)2\mathcal{L}_{\text{IPO}} = \left(\log \frac{\pi_\theta(y_w|x)}{\pi_{\text{ref}}(y_w|x)} - \log \frac{\pi_\theta(y_l|x)}{\pi_{\text{ref}}(y_l|x)} - \frac{1}{2\beta}\right)^2

ORPO(Odds Ratio Preference Optimization)

Hong 等人(2024)提出的 ORPO,将SFT 与偏好优化整合为单一阶段。由于不需要参考模型,能大幅降低内存与计算成本。其核心是基于优势比(Odds Ratio)的惩罚项。

import torch
import torch.nn.functional as F

def compute_orpo_loss(model, chosen_ids, chosen_mask, rejected_ids, rejected_mask, alpha=1.0):
    """ORPO 损失:SFT + 基于优势比的偏好优化的统一"""
    # SFT 损失(对偏好响应求 NLL)
    chosen_logits = model(input_ids=chosen_ids, attention_mask=chosen_mask).logits
    sft_loss = F.cross_entropy(
        chosen_logits[:, :-1, :].reshape(-1, chosen_logits.size(-1)),
        chosen_ids[:, 1:].reshape(-1),
        reduction="mean",
    )

    # 计算对数概率
    chosen_logps = get_sequence_log_probs(model, chosen_ids, chosen_mask)
    rejected_logps = get_sequence_log_probs(model, rejected_ids, rejected_mask)

    # 计算优势比
    chosen_odds = chosen_logps - torch.log1p(-torch.exp(chosen_logps))
    rejected_odds = rejected_logps - torch.log1p(-torch.exp(rejected_logps))
    log_odds_ratio = chosen_odds - rejected_odds

    # ORPO 最终损失
    orpo_loss = sft_loss - alpha * F.logsigmoid(log_odds_ratio).mean()

    return orpo_loss

对比分析

将各对齐技术的核心特性加以比较,结果如下。

技术数据需求模型数量计算成本稳定性性能
RLHF (PPO)成对偏好4(策略、参考、奖励、价值)非常高低(需要调优)高(充分调优时)
DPO成对偏好2(策略、参考)中等
KTO二元反馈(非成对)2(策略、参考)中等中上
IPO成对偏好2(策略、参考)中等非常高中上
ORPO成对偏好1(仅策略)中上

核心权衡

  • RLHF:性能上限最高,但训练不稳定性与工程复杂度也高
  • DPO:实现简洁性与性能之间的平衡最出色,是目前采用最广泛的技术
  • KTO:在难以构建成对数据的环境中是有力的替代方案
  • IPO:适合 DPO 过拟合问题严重的小规模数据集
  • ORPO:在计算资源受限的环境中是高效的选择

实务应用指南

技术选择标准

在实务中选择对齐技术时,需要考虑以下标准。

  1. 数据形态:是否有成对偏好数据?还是只能获取二元反馈?
  2. 计算资源:GPU 内存与训练时间的限制如何?
  3. 团队能力:是否有 PPO 调优经验?
  4. 模型规模:7B 以下考虑 DPO,70B 以上考虑 RLHF

评估指标实现

用于评估对齐结果的核心指标如下。

import numpy as np
from typing import List, Dict

def compute_alignment_metrics(
    model_responses: List[str],
    reference_responses: List[str],
    reward_model,
    tokenizer,
) -> Dict[str, float]:
    """用于评估对齐质量的综合指标计算"""
    metrics = {}

    # 1. 奖励模型分数
    rewards = []
    for response in model_responses:
        tokens = tokenizer(response, return_tensors="pt")
        score = reward_model(**tokens).item()
        rewards.append(score)
    metrics["mean_reward"] = np.mean(rewards)
    metrics["reward_std"] = np.std(rewards)

    # 2. 胜率(win rate)—— 相对参考模型
    wins = 0
    for model_resp, ref_resp in zip(model_responses, reference_responses):
        model_tokens = tokenizer(model_resp, return_tensors="pt")
        ref_tokens = tokenizer(ref_resp, return_tensors="pt")
        model_score = reward_model(**model_tokens).item()
        ref_score = reward_model(**ref_tokens).item()
        if model_score > ref_score:
            wins += 1
    metrics["win_rate"] = wins / len(model_responses)

    # 3. 响应长度分布(长度黑客检测)
    lengths = [len(r.split()) for r in model_responses]
    metrics["mean_length"] = np.mean(lengths)
    metrics["length_std"] = np.std(lengths)

    # 4. 多样性指标(模式崩溃检测)
    unique_bigrams = set()
    total_bigrams = 0
    for response in model_responses:
        words = response.split()
        for i in range(len(words) - 1):
            bigram = (words[i], words[i + 1])
            unique_bigrams.add(bigram)
            total_bigrams += 1
    metrics["distinct_2"] = len(unique_bigrams) / max(total_bigrams, 1)

    return metrics

失败案例与教训

1. 奖励黑客(Reward Hacking)

这是 RLHF 中最常见的失败模式。当奖励模型学到「响应越长 = 响应越好」这种虚假相关性时,策略模型就会不顾内容质量,生成过长的响应。InstructGPT 论文也报告过这一问题,并通过长度归一化与加强 KL 惩罚部分解决了它。

教训:需要持续监控奖励模型的偏差,并用多种代理指标进行交叉验证。

2. 模式崩溃(Mode Collapse)

在 PPO 训练过程中,策略只反复生成能在奖励模型上拿到高分的少数几种模式的现象。表现为对不同问题输出结构相同的响应,或过度重复某些特定表达。

教训:应当合理维持 KL 散度约束,并在训练过程中监控响应多样性指标。

3. DPO 的分布偏移(Distribution Shift)

由于 DPO 是在离线偏好数据上训练的,随着训练推进,当前策略生成的分布会与训练数据的分布逐渐产生偏离。这可能导致训练后期性能下降。

教训:应当采用迭代式(iterative)DPO,在每一轮用当前策略生成新的偏好数据,或者使用在线 DPO 变体。

4. Constitutional AI 的原则冲突

宪法中包含的各项原则之间有时会发生冲突。例如「要提供帮助」与「不要提供有害信息」这两条原则,在特定语境下可能相互抵触。

教训:应当明确定义各原则之间的优先级,并为边界情形(edge cases)另行制定指导方针。

参考资料

  1. Ouyang, L., et al. (2022). "Training language models to follow instructions with human feedback." NeurIPS 2022. https://arxiv.org/abs/2203.02155

  2. Rafailov, R., et al. (2023). "Direct Preference Optimization: Your Language Model is Secretly a Reward Model." NeurIPS 2023. https://arxiv.org/abs/2305.18290

  3. Bai, Y., et al. (2022). "Constitutional AI: Harmlessness from AI Feedback." Anthropic. https://arxiv.org/abs/2212.08073

  4. Ethayarajh, K., et al. (2024). "KTO: Model Alignment as Prospect Theoretic Optimization." https://arxiv.org/abs/2402.01306

  5. Hong, J., et al. (2024). "ORPO: Monolithic Preference Optimization without Reference Model." https://arxiv.org/abs/2403.07691

  6. Azar, M. G., et al. (2024). "A General Theoretical Paradigm to Understand Learning from Human Feedback." (IPO) https://arxiv.org/abs/2310.12036

  7. Schulman, J., et al. (2017). "Proximal Policy Optimization Algorithms." https://arxiv.org/abs/1707.06347

  8. Hugging Face Blog. "Preference Tuning LLMs with Direct Preference Optimization Methods." https://huggingface.co/blog/pref-tuning