LLM 场景下的强化学习技术

原文: http://zhuanlan.zhihu.com/p/1964814898312769887

1. 强化学习基础:行业黑话

想象你正在和一个刚训练好的语言模型聊天。你问:“今天过得怎么样?”
模型可能回:“还行。” 也可能回:“我是个 AI,没有感情。”
人类觉得前者更自然、更友好——这就是偏好反馈。强化学习(RL)在 LLM 中的核心任务,就是让模型学会生成“人类更喜欢”的回复。

为了做到这一点,我们需要一套语言来描述这个过程。下面我们以 LLM 场景为基础介绍几个 RL 的“行业黑话”。

1.1 基本概念

  1. 时刻 $t$:就是对话的第几步。比如:
  • $t=0$:用户输入 “今天过得怎么样?” → 这是初始状态 $s_0$
  • $t=1$:模型输出第一个词 “今” → 动作 $a_0 = \text{“今”}$
  • $t=2$:模型输出第二个词 “天” → 动作 $a_1 = \text{“天”}$
  • … 直到生成完整回复,比如 “今天过得不错!”

2. 状态 $s_t$ 通常就是到第 $t$ 步为止已生成的 token 序列(包括用户输入和模型已输出的部分)

3. 动作 $a_t$ 就是模型在第 $t$ 步选择的下一个 token

  1. 奖励 $r_t$:这是人类(或奖励模型)对模型行为的真实反馈信号。比如:
  • 如果模型最终生成了“今天过得不错!”,人类觉得回答的不错,打 5 分 → 这个分数会折算成一个**最终奖励 $r_T$**(通常只在序列结束时给,即最后一个 token)
  • 中间步骤一般没有即时奖励($r_t = 0$ for $t < T$)

5. 价值 $v$:奖励 $r$ 是真实的、来自外部的信号(比如人类打分),相对应的,价值(value)是对未来奖励的估计——因为模型不能预知未来,只能靠猜。

1.2. 价值(Value):对未来奖励的“预判”

既然模型不能看到未来,它就需要一个“预判能力”:我现在处在某个状态,未来大概能拿多少分?

这就引出了两个核心函数:

1) 状态价值函数 V(s_t)

\[V(s_t) = \sum_{a \in \mathcal{A}} \pi(a|s_t) \, Q(s_t, a) \\\]

它表达的是:在当前已生成的对话上下文 $s_t$(比如用户刚问完 “今天过得怎么样?”,而模型还没开始回答,或已输出“今”),模型按照当前策略继续生成后续内容,平均能获得多少人类打分

  • $\pi(a s_t)$ 是模型在状态 $s_t$ 下选择下一个词 $a$ 的概率(例如在“今天过得怎么样?”之后,选“今”还是“还”);
  • $Q(s_t, a)$ 表示如果此刻选了某个具体词 $a$,最终能拿到的预期总分;
  • 把所有可能的下一个词按模型当前的偏好加权平均,就得到了该状态的整体“预期得分”——也就是 $V(s_t)$。

举个例子:当模型已经输出 “今天过得”,它会评估:“按我现在的风格继续回答,人类大概率会觉得自然,可能打 4 分”,于是 $V(s_t) \approx 4$。

2) 动作价值函数 Q(s_t, a)

\[Q(s_t, a) = r_t + \gamma V(s_{t+1}) + \gamma^2 V(s_{t+2}) + \cdots \\\]

它表达的是:如果我现在处于状态 $s_t$(比如上下文是“今天过得”),并选择动作 $a$(比如生成“不”),那么我能获得当前的真实奖励 $r_t$(通常是 0,因为回复还没结束),再加上未来所有状态价值的折扣和

对应到 LLM 应用场景就表示:

“如果我现在在‘今天过得’后面接‘不’,形成‘今天过得不’,那接下来我大概率会说‘错!’,组成一句完整、积极的回复,最终人类可能会打 5 分。”

其中:

  • $r_t$ 是真实发生的奖励,但在 LLM 生成过程中,只有完整回复结束后才有非零值(例如人类打分 $r_T = 5$);在中间步骤(如生成“今”“天”时),$r_t = 0$;
  • $V(s_{t+1}), V(s_{t+2}), \dots$ 是模型自己估计的未来价值(比如生成“不”之后,预估“今天过得不错!”能拿 4.9 分);
  • \gamma \in [0,1] 是折扣因子(如 0.95),表示“未来的分不如现在的分值钱”——越靠后的 token 对当前决策的影响越小。

虽然中间每一步的 $r_t = 0$,但 $Q(s_t, a)$ 依然非常关键:它通过 $V(s_{t+1})$ 等未来价值,把对最终人类反馈的预判传递回当前决策。这正是 LLM 在生成每个词时具备“前瞻能力”的来源——它不是随机选词,而是基于“这样说人类会不会喜欢”的长期预期来做选择。

为什么估计的价值函数 Q 里包含真实的 $r_t$?
因为 RL 的目标是用真实奖励来校准价值估计。模型通过不断对比“预测的未来得分”和“实际拿到的奖励”,来修正自己的 $V$ 和 $Q$ 函数。

2. PPO:RLHF 的“老大哥”

PPO(Proximal Policy Optimization)是传统 RLHF(基于人类反馈的强化学习)流程中的核心算法,是 openai 在 2016年左右提出来的。原来 closeAI 的成功在那个时候就开始蓄力了。PPO的目标很直接:让语言模型生成更受人类欢迎的回复

PPO 中的几个关键角色

注:$\oplus$ 表示 token 拼接;$T$ 是生成回复的长度(可变,但训练时通常 padding 到固定长度)。

PPO 的两阶段训练流程

PPO通过分阶段解耦“数据生成”和“策略学习”,在保证训练稳定性的同时,让模型逐步学会生成更符合人类偏好的回复。整个流程分为如下两个阶段:

阶段 1:采样与反馈(Sample + Label)

目标:用当前策略模型生成一批回复,并利用冻结的奖励模型打分,再结合当前评论家模型估计价值,最终为每个 token 动作计算出优势(Advantage)回报(Return),作为后续训练的监督信号。

📌 关键点:此阶段不更新任何模型参数,只是“收集数据”。Policy 和 Critic 在采样时使用的是当前最新参数,但输出会被 detach(视为常数),作为“旧策略”和“旧评论家”的快照。

🧩 参与模型与接口

注:$L_x$ 是 prompt 长度,$T$ 是生成回复长度(实际中常 padding 到固定 max_len)。

🔢 核心计算逻辑

  1. 生成轨迹:对每个 prompt $x$,用当前策略生成完整回复 $y = (a_1, …, a_T)$,形成状态序列:
    \(s_0 = x,\quad s_1 = x \oplus a_1,\quad \dots,\quad s_T = x \oplus y \\\)
  2. 获取最终奖励:调用冻结的 Reward Model:
    $R = r_\phi(x, y) \$ (中间步骤无奖励,即 $r_t = 0$ for $t < T$)
  3. 计算回报(Return)
    $\hat{R}t = \sum{k=t}^{T} \gamma^{k-t} r_k = \gamma^{T - t} R \$ 因为只有最后一步有奖励。回报序列 $\hat{R}_0, \hat{R}_1, …, \hat{R}_T$ 构成目标值。
  4. 计算优势(Advantage)
    $A_t = \hat{R}t - V\psi(s_t), \quad t = 0, 1, …, T-1 \$ 表示:在状态 $s_t$ 下执行动作 $a_t$,比“平均水平”好多少
  5. 保存“旧”值:将当前策略的 log-prob 和评论家的 value detach,作为阶段 2 的基准(即“old policy”和“old critic”)。

💻 伪代码(阶段 1)

trajectories = []

for x in prompts:  # x: [L_x]
    # 1. 用当前策略生成回复 y 和 log-prob
    y, logprobs = policy_model.generate_with_logprobs(x)  # y: [T], logprobs: [T]

    # 2. 构建状态序列 s_0 ... s_T
    states = [torch.cat([x, y[:t]]) for t in range(len(y) + 1)]  # len = T+1

    # 3. 用当前评论家估计每个状态的价值
    values = torch.stack([critic_model(s) for s in states])  # [T+1]

    # 4. 奖励模型打分(仅最终奖励)
    R = reward_model(x, y)  # scalar

    # 5. 计算回报:R_t = γ^{T−t} * R
    T_len = len(y)
    returns = torch.zeros(T_len + 1)
    returns[T_len] = R
    for t in reversed(range(T_len)):
        returns[t] = gamma * returns[t + 1]

    # 6. 计算优势:A_t = R_t - V(s_t),仅对 t=0..T-1 有效
    advantages = returns[:-1] - values[:-1]  # [T]

    # 7. 保存“旧”值(detach 阻断梯度)
    trajectories.append({
        'x': x,
        'y': y,
        'logprobs_old': logprobs.detach(),      # [T]
        'values_old': values.detach(),          # [T+1]
        'advantages': advantages.detach(),      # [T]
        'returns': returns.detach()             # [T+1]
    })

✅ 此阶段结束时,我们得到一个固定的数据集,后续训练将在此数据上多次迭代。

阶段 2:策略与评论家更新(Policy & Critic Learning)

目标:利用阶段 1 收集的固定轨迹数据,更新策略模型(Policy)和评论家模型(Critic),使得:

  • 策略更倾向于选择高优势的动作;
  • 评论家更准确地预测未来回报;
  • 同时通过 PPO-clipKL 正则防止策略突变或偏离合理语言分布。

🧩 参与模型与接口

🔢 核心计算逻辑

  1. 策略损失(PPO-Clip)
    定义概率比:
    $r_t(\theta) = \frac{\pi_\theta(a_t | s_t)}{\pi_{\text{old}}(a_t | s_t)} = \exp\left( \log \pi_\theta(a_t|s_t) - \log \pi_{\text{old}}(a_t|s_t) \right) \$ PPO 损失为:
    \mathcal{L}^{\text{PPO}} = \mathbb{E}_t \left[ \min\left( r_t(\theta) A_t,\ \text{clip}(r_t(\theta), 1-\epsilon, 1+\epsilon) A_t \right) \right] \
  • 若 $A_t > 0$:鼓励增加动作概率,但最多增加 $(1+\epsilon)$ 倍;
  • 若 $A_t < 0$:鼓励减少概率,但最多减少到 $(1-\epsilon)$ 倍。
  1. KL 散度正则(防止语言退化)
    ![\mathcal{L}^{\text{KL}} = \beta \cdot \mathbb{E}t \left[ \log \pi\theta(a_t|s_t) - \log \pi_{\text{ref}}(a_t|s_t) \right] \](https://www.zhihu.com/equation?tex=%5Cmathcal%7BL%7D%5E%7B%5Ctext%7BKL%7D%7D+%3D+%5Cbeta+%5Ccdot+%5Cmathbb%7BE%7D_t+%5Cleft%5B+%5Clog+%5Cpi_%5Ctheta%28a_t%7Cs_t%29+-+%5Clog+%5Cpi_%7B%5Ctext%7Bref%7D%7D%28a_t%7Cs_t%29+%5Cright%5D+%5C%5C)
  • $\pi_{\text{ref}}$ 是冻结的 SFT 模型;
  • $\beta$ 控制正则强度(如 0.01~0.1)。
  1. 评论家损失(Value MSE)
    ![\mathcal{L}^{\text{value}} = \mathbb{E}t \left[ \left( V\psi(s_t) - \hat{R}t \right)^2 \right] \](https://www.zhihu.com/equation?tex=%5Cmathcal%7BL%7D%5E%7B%5Ctext%7Bvalue%7D%7D+%3D+%5Cmathbb%7BE%7D_t+%5Cleft%5B+%5Cleft%28+V%5Cpsi%28s_t%29+-+%5Chat%7BR%7D_t+%5Cright%29%5E2+%5Cright%5D+%5C%5C)
  • 目标是让评论家准确预测阶段 1 计算出的回报 $\hat{R}_t$。
  1. 总损失
    \(\mathcal{L}_{\text{total}} = -\mathcal{L}^{\text{PPO}} + \beta \cdot \text{KL} + c_1 \cdot \mathcal{L}^{\text{value}} \\\)

💻 伪代码(阶段 2)

for epoch in range(K):  # K=2~4,对同一数据集多轮优化
    for traj in trajectories:
        x, y = traj['x'], traj['y']                     # x: [L_x], y: [T]
        logprobs_old = traj['logprobs_old']             # [T]
        advantages = traj['advantages']                 # [T]
        returns = traj['returns']                       # [T+1]

        # --- 1. 策略损失 ---
        logprobs_curr = policy_model.get_logprobs(x, y)  # [T]
        ratio = torch.exp(logprobs_curr - logprobs_old)  # [T]

        surr1 = ratio * advantages
        surr2 = torch.clamp(ratio, 1 - eps, 1 + eps) * advantages
        ppo_loss = -torch.mean(torch.min(surr1, surr2))

        # KL 正则(ref_model 冻结)
        with torch.no_grad():
            logprobs_ref = ref_model.get_logprobs(x, y)  # [T]
        kl_loss = torch.mean(logprobs_curr - logprobs_ref)

        policy_loss = ppo_loss + beta * kl_loss

        # --- 2. 评论家损失 ---
        states = [torch.cat([x, y[:t]]) for t in range(len(y) + 1)]
        values_pred = torch.stack([critic_model(s) for s in states])  # [T+1]
        value_loss = F.mse_loss(values_pred, returns)

        # --- 3. 优化 ---
        total_loss = policy_loss + c1 * value_loss
        optimizer.zero_grad()
        total_loss.backward()
        optimizer.step()

🎯 总结:PPO 的设计哲学

  • 阶段 1 是“探索”:用当前策略生成多样回复,用外部信号(RM)和内部估计(Critic)打标签;
  • 阶段 2 是“学习”:在固定数据上保守更新,通过 clip 和 KL 防止“学歪”;
  • Reference Model 是安全网:确保语言依然流畅、合理;
  • 整个流程可迭代:每轮 PPO 后,策略更强,下一轮采样质量更高。

这种“采样-学习”交替的模式,正是 PPO 能在 LLM 对齐中兼顾效果、稳定性和安全性的关键。

3. DPO:绕过 RL 的“聪明办法”

DPO(Direct Preference Optimization)发现:其实不需要显式训练 Reward Model + PPO,可以直接从人类偏好数据中优化策略

DPO 的核心洞察

人类偏好数据是成对的:$(x, y_w, y_l)$,其中:

  • $x$:用户输入(prompt)
  • $y_w$:人类偏好的回复(win)
  • $y_l$:较差的回复(lose)

DPO 证明:最大化人类偏好等价于最小化下面这个损失

![\mathcal{L}{\text{DPO}} = -\mathbb{E}{(x,y_w,y_l)} \left[ \log \sigma \left( \beta \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)} \right) \right) \right] \](https://www.zhihu.com/equation?tex=%5Cmathcal%7BL%7D_%7B%5Ctext%7BDPO%7D%7D+%3D+-%5Cmathbb%7BE%7D_%7B%28x%2Cy_w%2Cy_l%29%7D+%5Cleft%5B+%5Clog+%5Csigma+%5Cleft%28+%5Cbeta+%5Cleft%28+%5Clog+%5Cfrac%7B%5Cpi_%5Ctheta%28y_w%7Cx%29%7D%7B%5Cpi_%7B%5Ctext%7Bref%7D%7D%28y_w%7Cx%29%7D+-+%5Clog+%5Cfrac%7B%5Cpi_%5Ctheta%28y_l%7Cx%29%7D%7B%5Cpi_%7B%5Ctext%7Bref%7D%7D%28y_l%7Cx%29%7D+%5Cright%29+%5Cright%29+%5Cright%5D+%5C%5C)

这个公式到底在算什么?

  • $\pi_\theta(y x)$:当前训练模型在 prompt $x$ 下生成完整回复 $y$ 的概率
    → 实际计算时,是把 $y$ 拆成 token 序列,求 $\prod_t \pi_\theta(y_t x, y_{
  • $\pi_{\text{ref}}(y x)$:参考模型(SFT 模型)生成 $y$ 的概率
  • $\beta$:温度参数,控制优化强度(越大越激进)

通俗理解:DPO 希望模型对“好回复”的相对概率(相比参考模型)比“坏回复”更高。

DPO 伪代码

for batch in preference_data:
    x, y_w, y_l = batch
    
    # 计算当前模型和参考模型对两个回复的 log 概率
    logp_w = policy_model.log_prob(x, y_w)
    logp_l = policy_model.log_prob(x, y_l)
    ref_logp_w = ref_model.log_prob(x, y_w)
    ref_logp_l = ref_model.log_prob(x, y_l)
    
    # 计算 logits 差
    logits = beta * ((logp_w - ref_logp_w) - (logp_l - ref_logp_l))
    
    # 二分类损失:希望 logits 越大越好
    loss = -F.logsigmoid(logits).mean()
    optimizer.step(loss)

DPO 本质是一个带参考模型的对比学习(contrastive learning)完全不需要 RL 循环,所以训练快、稳定。

4. GRPO:在 PPO 和 DPO 之间找平衡

PPO vs DPO:各自的痛

方法 优点 缺点
PPO 支持 online learning(边生成边学),样本利用率高;可结合多种奖励(如安全性、事实性) 需要训练 4 个模型(Policy, Critic, RM, Reference),流程复杂;RM 质量直接影响效果
DPO 训练简单,只需 2 个模型(Policy + Reference);效果接近 PPO 完全依赖离线(offline)偏好数据;容易过拟合(尤其数据少时);无法引入动态奖励

GRPO:群体相对优化

GRPO(Group Relative Policy Optimization)的思路是:

既然人类经常面对多个选项做判断(比如从 4 个回复中选最好的 2 个),那就直接建模这种“群体偏好”

GRPO 的做法

  1. 对每个 prompt $x$,用当前策略生成 $K$ 个回复(比如 $K=4$)
  2. 根据 Reward Model(或人类)将这些回复分成“好组”和“坏组”
  3. 优化目标:拉大组间差异,缩小组内差异

GRPO 损失函数(简化版)

![\mathcal{L}{\text{GRPO}} = -\mathbb{E}_x \left[ \log \frac{\sum{y \in \text{good}} \exp(\beta \log \frac{\pi_\theta(y x)}{\pi_{\text{ref}}(y x)})}{\sum_{y \in \text{all}} \exp(\beta \log \frac{\pi_\theta(y x)}{\pi_{\text{ref}}(y x)})} \right] \](https://www.zhihu.com/equation?tex=%5Cmathcal%7BL%7D_%7B%5Ctext%7BGRPO%7D%7D+%3D+-%5Cmathbb%7BE%7D_x+%5Cleft%5B+%5Clog+%5Cfrac%7B%5Csum_%7By+%5Cin+%5Ctext%7Bgood%7D%7D+%5Cexp%28%5Cbeta+%5Clog+%5Cfrac%7B%5Cpi_%5Ctheta%28y%7Cx%29%7D%7B%5Cpi_%7B%5Ctext%7Bref%7D%7D%28y%7Cx%29%7D%29%7D%7B%5Csum_%7By+%5Cin+%5Ctext%7Ball%7D%7D+%5Cexp%28%5Cbeta+%5Clog+%5Cfrac%7B%5Cpi_%5Ctheta%28y%7Cx%29%7D%7B%5Cpi_%7B%5Ctext%7Bref%7D%7D%28y%7Cx%29%7D%29%7D+%5Cright%5D+%5C%5C)

这其实是一个带参考模型的 softmax 分类损失:希望“好回复”的归一化概率更高。

GRPO 伪代码

for x in prompts:
    # 1. 生成 K 个回复
    responses = [policy_model.generate(x) for _ in range(K)]
    
    # 2. 用 RM 打分并分组(比如 top-2 为 good)
    scores = [reward_model.score(x, y) for y in responses]
    good_mask = get_top_k_mask(scores, k=2)
    
    # 3. 计算每个回复的 log ratio
    ratios = []
    for y in responses:
        logp = policy_model.log_prob(x, y)
        ref_logp = ref_model.log_prob(x, y)
        ratios.append(beta * (logp - ref_logp))
    
    # 4. softmax 分类损失
    logits = torch.stack(ratios)
    loss = F.cross_entropy(logits.unsqueeze(0), target=good_mask)
    optimizer.step(loss)

GRPO 的优势

  • 保留了 PPO 的 online 生成能力(自己造数据)
  • 像 DPO 一样只优化策略模型,无需 Critic
  • 对 RM 的依赖比 PPO 弱(只需排序,不要求绝对分数准确)

5. 总结:选哪个?

方法 模型数量 是否需要 RM 是否 RL 适合场景
PPO 4(Policy, Critic, RM, Ref) 高质量对齐,多目标奖励
DPO 2(Policy, Ref) 快速迭代,偏好数据充足
GRPO 3(Policy, RM, Ref) ✅(弱依赖) ⚠️(类 RL) 平衡效率与效果,支持 online 学习

强化学习在 LLM 中,早已不是“必须用 PPO”的时代。DPO 让对齐变得像 SFT 一样简单,GRPO 则试图把 PPO 的灵活性和 DPO 的简洁性结合起来

技术在进化,我们的工具箱也在变丰富。选对方法,比盲目堆资源更重要。

Flag Counter