PaperReading:RLHF
paperreading
本文字数:3.4k 字 | 阅读时长 ≈ 12 min

PaperReading:RLHF

paperreading
本文字数:3.4k 字 | 阅读时长 ≈ 12 min

强化学习回顾

在阅读本文之前,强烈建议先学习一下 PPO 算法,不然后面可能会听的云里雾里。本文参考知乎博客《RLHF 的 PPO 算法解析》

PPO 是强化学习中的一个重要算法,在 RLHF 中主要用在 Post-training 阶段,用来和人类偏好对齐。我们先回顾一下什么是强化学习。强化学习的核心是智能体(Agent)与环境(Environment)之间的交互。这个交互过程可以简单理解为:

  1. 在 $t$ 时刻,环境处于状态 $S(t)$,智能体观测到该状态并获得即时奖励 $R(t)$。
  2. 智能体根据观测到的状态和奖励,执行一个动作 $A(t)$。
  3. 环境接收到动作后,状态变为 $S(t+1)$,并反馈给智能体新的奖励 $R(t+1)$。

智能体的最终目标是学习一个最优策略(Policy),该策略能指导它在任何状态下选择能最大化未来总收益的动作。当然,如果只考虑即时奖励 $R(t)$,智能体的决策会非常短视。一个更优的策略应该着眼于未来的长期收益。因此,我们引入价值函数 $V(t)$,它代表了从 $t$ 时刻状态 $S(t)$ 开始,直到结束所能获得的总收益。一个简化的表达式是:

$$
V(t) = R(t) + \gamma\times V(t+1)
$$

RLHF

在了解强化学习中的智能体(Agent)、环境(Environment)、奖励(Reward)等要素之后,在RLHF中,我们的目标是让模型根据一个提示(Prompt),生成人类喜欢的回答(Response)。在这个语境下:

在 RLHF 阶段,通常有四个模型协同工作,它们共同构成了一个精密的训练体系。

  1. Actor Model (演员模型):最终要训练的目标语言模型。通常由SFT(Supervised Fine-Tuning)阶段的模型初始化。参数在RLHF阶段持续更新。
  2. Reference Model (参考模型):作为基准,防止Actor模型在优化过程中“跑偏”,即输出分布与原始SFT模型差异过大。通常由SFT模型初始化。参数在RLHF阶段冻结。
  3. Critic Model (评论家模型):预测在某个状态下,未来可能获得的总收益 $V(t)$。它评估Actor的行为有多“值钱”。通常由奖励模型初始化。参数在RLHF阶段冻结。
  4. Reward Model (奖励模型):计算每个时刻的即时奖励 $R(t)$。它代表了“上帝视角”,为Actor的每个动作提供即时反馈。通常由人类标注的偏好数据单独训练好的模型。参数在RLHF阶段冻结。

其中,Critic、Reward和Reference模型共同组成了一个“奖励-loss计算体系”,它们的结果被用来计算最终的loss,以更新Actor和Critic模型。

RLHF中的Loss计算

训练的核心在于计算两个Loss:Actor LossCritic Loss

4.1 Actor Loss 的演进

Actor Loss的目标是指导Actor模型生成能获得更高“优势”的token。它的设计经过了几个阶段的演进。

(1) 引入优势函数 (Advantage)

最朴素的想法是,如果一个动作的价值高,就增大其概率。但更好的方法是使用优势函数(Advantage),它衡量的是一个动作比当前状态的平均价值好多少。

$$
Adv(t) = R(t) + γ * V(t+1) - V(t)
$$

(2) 重新设计即时奖励 R(t)

deepspeed-chat等实践中,R(t)的设计非常巧妙。它不仅仅是Reward模型的输出,还融入了与Reference模型的KL散度,用于约束Actor不要偏离太远。

# 伪代码,展示R(t)的设计思路
# 在大部分时刻t,奖励只包含KL散度惩罚项
R(t) = -kl_ctl * KL(Actor || Reference) at token t
# 在最后一个时刻T,额外加上整个response的奖励分数
R(T) = -kl_ctl * KL(Actor || Reference) + Reward_Score_for_whole_response

这种设计让模型在生成过程中时刻注意与SFT模型的对齐,并在最后综合评估整个句子的质量。

(3) GAE: 重新设计优势函数

为了减少优势估计的方差,引入了广义优势估计(GAE)。它在计算当前优势时,还考虑了未来的优势,形成了一个更稳定的估计。

Adv(t)_GAE = (R(t) + γ * V(t+1) - V(t)) + γ * λ * Adv(t+1)_GAE

这个公式可以通过从后往前的动态规划高效计算。

# GAE优势计算代码实践
def get_advantages_and_returns(self, values, rewards, start):
    lastgaelam = 0
    advantages_reversed = []
    length = rewards.size()[-1]
    # 从后往前倒推计算
    for t in reversed(range(start, length)):
        nextvalues = values[:, t + 1] if t < length - 1 else 0.0
        delta = rewards[:, t] + self.gamma * nextvalues - values[:, t]
        lastgaelam = delta + self.gamma * self.lam * lastgaelam
        advantages_reversed.append(lastgaelam)
    advantages = torch.stack(advantages_reversed[::-1], dim=1) # 优势
    returns = advantages + values[:, start:] # 实际总收益
    return advantages.detach(), returns

(4) PPO核心:引入新旧策略约束与裁剪 (Clipping)

强化学习的样本收集(即模型推理生成数据)成本很高。为了充分利用每一批数据,PPO允许用同一批“经验”数据进行多轮(ppo-epochs)更新。

为了防止在多轮更新中模型偏离太远,PPO引入了一个关键约束:限制新旧策略的比率 ratio

ratio = P_new(A(t)|S(t)) / P_old(A(t)|S(t))

最终的Actor Loss形式为:

Actor_Loss = -min( ratio * Adv(t), clip(ratio, 1-ε, 1+ε) * Adv(t) )

# Actor Loss代码实践
def actor_loss_fn(self, logprobs, old_logprobs, advantages, mask):
    # 计算新旧策略概率比的对数
    log_ratio = (logprobs - old_logprobs) * mask
    ratio = torch.exp(log_ratio)
    
    # 计算原始loss和截断后的loss
    pg_loss1 = -advantages * ratio
    pg_loss2 = -advantages * torch.clamp(ratio, 1.0 - self.cliprange, 1.0 + self.cliprange)
    
    # 取两者中较大的一个(因为原始loss是负数,所以是惩罚较小的一个)
    pg_loss = torch.sum(torch.max(pg_loss1, pg_loss2) * mask) / mask.sum()
    return pg_loss

4.2 Critic Loss

Critic Loss的目标很简单:让Critic模型的预估总收益 (values) 尽可能地接近“实际总收益 (returns)”。这个实际总收益就是我们用GAE计算出的 advantages + old_values

为了增加训练稳定性,同样也引入了裁剪机制。

Critic_Loss = 0.5 * max( (values - returns)², (values_clipped - returns)² )

values_clipped 是将当前Critic的输出限制在旧Critic输出值的一个小范围内。

# Critic Loss代码实践
def critic_loss_fn(self, values, old_values, returns, mask):
    # 将新critic的预测值裁剪在老critic预测值的一定范围内
    values_clipped = torch.clamp(
        values,
        old_values - self.cliprange_value,
        old_values + self.cliprange_value,
    )
    
    # 计算原始MSE loss和裁剪后的MSE loss
    vf_loss1 = (values - returns)**2
    vf_loss2 = (values_clipped - returns)**2
    
    # 取两者中较大的loss进行更新
    vf_loss = 0.5 * torch.sum(
        torch.max(vf_loss1, vf_loss2) * mask) / mask.sum()
    return vf_loss

总结

我们通过一个层层递进的方式,完整解读了RLHF-PPO的训练框架:

  1. 从宏观上看,RLHF是一个精巧的系统,它利用四个模型(Actor, Critic, Reward, Reference)的协同工作,将不可量化的“人类偏好”转化为可优化的数学信号。
  2. 从核心算法看,PPO通过优势函数(Advantage)新旧策略比率裁剪(Clipping)多轮次更新(PPO-Epochs) 等机制,实现了在利用高成本样本数据进行高效、稳定训练的目标。
  3. Actor Loss 的核心是利用优势信号,并通过裁剪来约束策略更新的幅度,确保模型在学习人类偏好的同时不会“自我毁灭”。
  4. Critic Loss 的核心是让评论家模型更准确地预测未来的总收益,为Actor提供更精准的指导信号。

希望这篇图文并茂、结合代码的解读,能帮助你彻底搞懂PPO和RLHF的内在逻辑,为你的大模型学习之路扫清障碍。

RLHF 中模型和 PPO 算法中的对应关系

上面我们介绍了 RLHF 的框架,可以看到 RLHF 中包含四个模型,在上面我们并没有将其和 PPO 算法中的对应关系进行对应,下面我们来详细梳理 PPO 和 RLHF 中这些模型的对应关系。注意 PPO 本身作为一个通用的强化学习算法,其框架中确实不包含奖励模型和参考模型;这些是在RLHF这个特定的应用场景下,为了解决特定问题而引入的组件。

1. 标准PPO算法的核心组件

在一个典型的强化学习环境(如雅达利游戏)中,PPO算法主要与以下部分交互:

2. RLHF框架下的“新”组件

RLHF的目标是让语言模型(LLM)的输出更符合人类的偏好。但“人类偏好”是一个模糊、主观的概念,无法像游戏得分那样直接量化。为了让PPO能优化这个目标,RLHF框架必须“创造”出一个适合PPO工作的环境和奖励机制。这就是奖励模型参考模型出现的原因。

3. 清晰的对应关系

标准PPO中的组件 在RLHF框架中的具体实现 说明
Actor 微调的语言模型 训练的核心目标,用来生成文本
Critic 评论家模型 评估当前文本序列的潜在“人类偏好总分”,通常由奖励模型初始化。
Environment 一个由多个模型构成的“虚拟”复杂体 RLHF 为 PPO 构建了一个人工环境。这个环境接收 Actor 的文本输出,并通过奖励模型和参考模型计算出一个综合的奖励分数。
Reward 奖励模型的分数 + KL散度惩罚 PPO需要的那个抽象的reward,在RLHF中被具体化为 `Reward Model Score - a * KL(Actor
环境的奖励函数 奖励模型 [RLHF新增] 学到的 vs. 预定义的:奖励模型是通过学习得到的复杂函数,而传统环境的奖励函数通常是简单的、硬编码的规则。奖励模型 (Reward Model) 是RLHF为PPO定制化构建的一个复杂的、通过学习得到的环境奖励函数。它取代了传统RL中那个简单的、由规则定义的奖励函数。
使用"旧策略" (P_old)的裁剪目标函数 参考模型 [RLHF新增] 稳定性的保障。它通过KL散-度惩罚,确保Actor在学习新偏好的同时,不会忘记其原有的语言能力。 全局 vs. 局部:参考模型是固定的全局锚点,而P_old是每轮迭代变化的局部锚点。RLHF同时使用两者。
May 06, 2025
Apr 06, 2025
ufw