Skip to content

强化学习(PPO,DPO)在 LLM 训练中的应用

强化学习是 LLM 对齐(Alignment)的关键技术,让模型输出更符合人类偏好。

为什么需要 RL?

预训练模型只是"预测下一个词",可能产生有害、无帮助或不真实的输出。

RLHF(Reinforcement Learning from Human Feedback) 流程:

预训练 → 监督微调(SFT) → 奖励模型训练 → PPO 强化学习 → 最终模型

核心概念

策略(Policy)

LLM 本身:输入提示,输出 token 分布(概率)。

目标:优化策略,使得奖励最大化。

奖励(Reward)

人类偏好模型(RM)对输出的评分。

训练 RM

人类标注数据:(提示, 输出A, 输出B) → 人类偏好 A > B
训练二分类模型:p(A > B | 提示, A, B)

PPO(Proximal Policy Optimization)

OpenAI 用于 ChatGPT 训练的算法。

目标函数

L(θ) = E[ min(r_t(θ) * A_t, clip(r_t(θ), 1-ε, 1+ε) * A_t) ]

其中:

  • r_t(θ) = π_θ(a_t|s_t) / π_old(a_t|s_t) - 新旧策略比例
  • A_t - 优势函数(Advantage)
  • ε - 裁剪范围(通常 0.1-0.2)

关键:限制策略更新幅度,防止训练不稳定。


实现步骤

1. 准备数据

python
from datasets import load_dataset
from transformers import AutoTokenizer

# 人类偏好数据集(如 Anthropic HH-RLHF)
dataset = load_dataset("Anthropic/hh-rlhf")
tokenizer = AutoTokenizer.from_pretrained("meta-llama/Llama-2-7b")

def tokenize(example):
    chosen = tokenizer(example["chosen"], truncation=True, max_length=512)
    rejected = tokenizer(example["rejected"], truncation=True, max_length=512)
    return {"chosen_input_ids": chosen.input_ids, "rejected_input_ids": rejected.input_ids}

dataset = dataset.map(tokenize)

2. 奖励模型训练

python
import torch.nn as nn
from transformers import AutoModelForSequenceClassification

class RewardModel(nn.Module):
    def __init__(self, model_name):
        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_loss(chosen_reward, rejected_reward):
    return -F.logsigmoid(chosen_reward - rejected_reward).mean()

# 训练循环
for batch in dataloader:
    chosen_reward = rm(batch["chosen_input_ids"], batch["chosen_attention_mask"])
    rejected_reward = rm(batch["rejected_input_ids"], batch["rejected_attention_mask"])
    loss = reward_loss(chosen_reward, rejected_reward)
    loss.backward()

3. PPO 训练

使用 TRL(Transformer Reinforcement Learning)库:

python
from trl import PPOTrainer, AutoModelForCausalLMWithValueHead
from trl.core import respond_to_batch

# 加载模型和 tokenizer
model = AutoModelForCausalLMWithValueHead.from_pretrained("sft_model")
ref_model = AutoModelForCausalLMWithValueHead.from_pretrained("sft_model")  # 参考模型
tokenizer = AutoTokenizer.from_pretrained("meta-llama/Llama-2-7b")

# PPO 配置
ppo_trainer = PPOTrainer(
    model=model,
    ref_model=ref_model,
    tokenizer=tokenizer,
    dataset=dataset,
    batch_size=4,
    mini_batch_size=2,
    gradient_accumulation_steps=4,
    learning_rate=1.41e-5,
    adap_kl_ctrl=True,  # 自适应 KL 惩罚
    init_kl_coef=0.2,
    target_kl=0.1
)

# 训练循环
for epoch in range(10):
    for batch in ppo_trainer.dataloader:
        # 1. 生成响应
        response_tensors = ppo_trainer.generate(batch["query"], **generation_kwargs)
        batch["response"] = tokenizer.batch_decode(response_tensors)

        # 2. 计算奖励
        with torch.no_grad():
            rewards = []
            for prompt, response in zip(batch["query"], batch["response"]):
                text = prompt + response
                inputs = tokenizer(text, return_tensors="pt").to(device)
                reward = reward_model(**inputs).item()
                # 减去基线或惩罚
                rewards.append(reward - kl_penalty)
        batch["reward"] = torch.tensor(rewards)

        # 3. PPO 步骤
        stats = ppo_trainer.step(
            batch["query"],
            batch["response"],
            batch["reward"],
            batch["query"].shape[-1]  # 序列长度
        )

DPO(Direct Preference Optimization)

简化版 RLHF,无需显式训练奖励模型和 PPO 优化。

原理

直接优化策略,使其偏好人类更喜欢的输出:

L_DPO = -E[log σ(β * (r_θ(chosen) - r_θ(rejected)))]

其中:

  • r_θ(x) = β * log π_θ(x) + const
  • β - 温度参数(控制偏离程度)

优势

  • 无需训练单独的奖励模型
  • 省去 PPO 复杂的训练流程
  • 效果接近 RLHF

实现

python
from transformers import AutoModelForCausalLM
import torch.nn.functional as F

model = AutoModelForCausalLM.from_pretrained("sft_model")
ref_model = AutoModelForCausalLM.from_pretrained("sft_model")  # 冻结

def dpo_loss(chosen_logps, rejected_logps, beta=0.1):
    """chosen_logps: 模型对偏好样本的 log probs
       rejected_logps: 模型对拒绝样本的 log probs
    """
    losses = -F.logsigmoid(beta * (chosen_logps - rejected_logps))
    return losses.mean()

for batch in dataloader:
    # chosen: 人类偏好的完整对话
    # rejected: 人类拒绝的完整对话

    # 1. 计算 log probs
    chosen_logits = model(input_ids=batch["chosen"]).logits
    rejected_logits = model(input_ids=batch["rejected"]).logits

    chosen_logps = log_probs(chosen_logits, batch["chosen"])
    rejected_logps = log_probs(rejected_logits, batch["rejected"])

    # 2. DPO 损失
    loss = dpo_loss(chosen_logps, rejected_logps)

    loss.backward()

使用 TRL 的 DPO

python
from trl import DPOTrainer

trainer = DPOTrainer(
    model=model,
    ref_model=ref_model,
    args=training_args,
    train_dataset=dpo_dataset,
    beta=0.1  # DPO 温度参数
)

trainer.train()

对齐策略对比

方法是否需要 RM是否需要 PPO优点缺点
SFT 监督微调简单快速可能过拟合
RLHF(PPO)效果最好复杂,不稳定
DPO简单稳定依赖偏好数据质量
KTO只需二元标签数据利用效率低

选择建议:

  • 快速验证:SFT
  • 最佳效果:RLHF(RL from human feedback)
  • 平衡:DPO(省去 RM 训练,效果接近 RLHF)
  • 预算有限:KTO(仅需好/坏标签)

实践建议

1. 数据质量 > 算法

偏好数据质量决定上限:

  • 多样化的场景(普通对话、安全性、有用性)
  • 清晰的对比(显著差异)
  • 人工审核,避免噪音

2. 超参数调优

参数典型值说明
PPO 学习率1e-5 ~ 1e-6小学习率,稳定
KL 惩罚系数 β0.1 ~ 0.5平衡对齐与语言建模能力
PPO epoch1 ~ 4每个 batch 更新次数
梯度裁剪0.1 ~ 0.3防止梯度爆炸

3. 监控指标

  • KL 散度:新旧模型差异,β 控制目标值
  • 奖励均值:模型输出的平均奖励
  • 胜率:模型 vs 参考模型的偏好比例
  • ** perplexity**:语言建模能力是否下降

4. 避免奖励 hacking

模型可能钻奖励模型空子:

人类偏好: 长、详细、有帮助的回答
奖励模型: 给这类回答高分
模型学会: 堆砌无意义的长句子

防护

  • 多样化的奖励模型
  • 多次迭代训练
  • 人工定期评估

工具与资源

开源实现

  • TRL(Transformer Reinforcement Learning):HuggingFace 官方 RLHF 库

    bash
    pip install trl
  • OpenRLHF:多节点分布式 RLHF

    bash
    pip install openrlhf

示例代码

python
# DPO 完整示例(TRL)
from trl import DPOTrainer, DPOConfig

config = DPOConfig(
    beta=0.1,
    per_device_train_batch_size=4,
    gradient_accumulation_steps=4,
    learning_rate=5e-5,
    num_train_epochs=3
)

trainer = DPOTrainer(
    model=model,
    ref_model=ref_model,
    args=config,
    train_dataset=dpo_dataset,
    tokenizer=tokenizer
)

trainer.train()

案例分析

ChatGPT

  • SFT:监督微调 1.3M 对话示例
  • RM:训练基于排名的奖励模型(45K 人工标注)
  • PPO:使用 RM 作为奖励,多阶段迭代优化

Claude(Anthropic)

  • Constitutional AI:用宪法原则替代人工反馈
  • Self-Supervision:模型自我改进
  • 减少人工标注成本

总结

  • RLHF(PPO):效果好,复杂昂贵,适合大规模对齐
  • DPO:简化版 RLHF,省去 RM 训练,适合快速迭代
  • KTO:最简化,仅需好/坏标签

对于大多数项目,建议从 DPO 开始,效果足够且稳定。若追求极致效果,再用 PPO 优化。