RLHF笔记

211 阅读7分钟

RLHF 学习笔记

参考文章:

(18 封私信 / 80 条消息) 图解大模型RLHF系列之:人人都能看懂的PPO原理与源码解读 - 知乎

(12 封私信 / 80 条消息) 详解大模型RLHF过程(配代码解读) - 知乎

Illustrating Reinforcement Learning from Human Feedback (RLHF)

What is Reinforcement Learning from Human Feedback (RLHF)?

RLHF介绍

RLHF,全称是“基于人类反馈的强化学习”(Reinforcement Learning from Human Feedback),是近年来在人工智能领域,特别是大型语言模型(LLM)发展中一项突破性的技术。它的核心思想是利用人类的偏好和判断作为指导,来“微调”AI模型,使其生成的内容更符合人类的价值观、期望和常识。

可以把它想象成一种“调教”AI的方式。传统的机器学习模型通常依赖于预先定义好的、固定的奖励函数来进行优化,但这在处理复杂、主观甚至涉及伦理的任务时,往往难以设计出完美的奖励函数。而RLHF则巧妙地引入了“人”这个变量,让AI从人类的反馈中学习什么是“好”,什么是“不好”。

image.png

这张图展示了使用 RLHF 微调大模型的强化学习阶段。虽然为了简洁,图中没有明确画出 Critic 模型,但它作为 PPO 算法的核心部分,实际存在于 “RL Update (PPO)” 这个环节内部。

先大致了解一下RLHF的流程,整个流程包含两个正在被训练的模型和一个固定的奖励/参考模型

  1. Trained LM (待训练的语言模型)

    • 在 PPO 算法中,它扮演 “Actor (演员)” 的角色。
    • 它的任务是执行动作,即根据输入的提示(Prompt)生成文本。它是我们最终要优化的主角。
  2. Critic (评论家) 模型 (图中未画出)

    • 这是 PPO 算法的另一个核心,一个独立的神经网络,通常与 Actor 有相似的结构。
    • 它的任务不是评价 Actor 生成的文本好不好,而是评估“状态”的价值。它观察输入的提示(Prompt),并预测从这个提示出发,Actor 大概能获得多少未来的总奖励期望值。我们称之为价值(Value, V)
  3. Reward (Preference) Model (奖励模型)

    • 它扮演"环境裁判"的角色,其参数是固定的。
    • 它为 Actor 完成的动作(生成的文本)提供一个即时的、绝对的奖励分数(Reward, R)
  4. Frozen LM (也叫Reference model,冻结的语言模型)

    • 它是一个参照物,其参数是固定的。
    • 用于计算 KL Loss,防止 Actor 在训练中“忘本”。

RLHF整体流程大致如下:

  1. 生成与评分

    • Actor (Trained LM) 接收一个提示 x,生成一段文本 y
    • Reward Model 评估文本 y,给出一个即时奖励分数 R
    • KL Loss 被计算出来,并从 R 中扣除,得到一个最终的即时奖励 R_final
    • 到此为止,和图上看到的一样。
  2. 价值评估与优势计算 (Critic 发挥作用的关键步骤)

    • 在 Actor 生成文本的同时,Critic 模型也接收了相同的提示 x

    • Critic 进行预测,输出一个价值估算值 V(x) 。这个值代表:“根据我的经验,对于这个提示 x,一个训练得不错的 Actor 大概能拿到 V(x) 这么高的分数。”

    • 接下来,计算一个至关重要的指标——优势 (Advantage, A)

    • 优势 A 告诉我们,这次 Actor 的表现是超出预期还是低于预期

      • 如果 A > 0:意味着 Actor 这次生成的文本得分比预期的要好,PPO 算法会鼓励 Actor 未来多采取类似的行为。
      • 如果 A < 0:意味着得分低于预期,表现令人失望。PPO 算法会抑制 Actor 类似的行为。
      • 相比只使用绝对的奖励分数 R,使用优势 A 作为更新信号,可以大大降低训练过程中的方差,使学习更稳定、高效。
  3. 执行更新

    • “RL Update (PPO)” 模块使用计算出的优势 A 来更新 Actor (Trained LM) 的参数。
    • 同时,它也会使用实际获得的奖励 R_final 来更新 Critic 模型的参数,让 Critic 对状态的价值评估 V(x) 变得越来越准确。

了解了RLHF的整体流程后,下面对每个小模型详细解释一下:

Actor Model

核心定义

Actor 模型是 RLHF 流程中我们真正想要训练和优化的目标语言模型。它在强化学习的框架中扮演“演员”或“智能体” (Agent) 的角色,负责根据环境(用户输入的 prompt)做出“动作”(生成回复)。

初始化方式

Actor 模型并非从零开始训练,它的初始版本通常是直接采用阶段训练好的模型。这意味着它在开始 RLHF 训练之前,已经具备了遵循指令和生成基本回答的能力。

运作机制 (强化学习视角)

  • 输入 (State, 状态 StS_t) : 接收一个提示(Prompt)。

  • 输出 (Action, 动作 AtA_t) : 生成一个或多个词元(Token),构成回复(Response)。

  • 训练循环:

    1. 给定一个 prompt,Actor 模型会生成一个完整的 response
    2. 这个 prompt + response 的完整序列会被送入一个“奖励计算体系”(这背后其实就是 Reward Model 和 KL 惩罚项)。
    3. 该体系会计算出一个最终的 Loss(损失)。
    4. 这个 Loss 会被用来通过反向传播更新 Actor 模型的参数

最终目标

训练的最终目的是调整 Actor 模型的内部参数,使其生成的 response 能够最大程度上符合人类的偏好,即在“奖励计算体系”中获得尽可能高的分数。

Reference Model

核心定义

Reference 模型 (简称 Ref 模型) 是一个在训练过程中参数被完全冻结、不参与更新的语言模型。它本质上是 Actor 模型训练开始前的“快照”。

初始化方式

  • 和 Actor 模型一样,Ref 模型也是直接采用 SFT 阶段训练好的模型进行初始化。
  • 关键区别: 一旦训练开始,Ref 模型的权重就永远不会改变

主要作用:防止 Actor “训歪”

  • 核心思想: 在让 Actor 学习人类偏好的同时,我们不希望它为了追求高奖励而“走火入魔”,导致其语言能力退化或输出一些逻辑混乱、不连贯的文本。我们希望 Actor 的输出分布与它最初的、稳定的 SFT 版本不要差异太大
  • Ref 模型就是这个“最初稳定版本”的标杆,作为一个参照物

运作机制:通过 KL 散度进行约束

  1. 获取两个概率分布:

    • 首先,让 Actor 模型 根据 prompt 生成一个 response。对于这个 response 中的每一个词元 (token),我们都可以得到一个由 Actor 模型计算出的对数概率 log_probs
    • 然后,将这个由 Actor 生成的 response,同样喂给 Ref 模型。Ref 模型会计算出,如果是它自己,会以多大的概率生成这些词元。这样我们得到了另一组对数概率 ref_log_probs
  2. 计算差异 (KL 散度) :

    通过比较 log_probsref_log_probs 这两个概率分布,我们可以计算它们之间的 KL 散度 ,这个值可以通过 ref_log_probs - log_probs 来近似。这个差值越小,代表两个模型的输出分布越相似

    KL[Actor(X)Ref(X)]=ExActor(x)[logActor(x)Ref(x)]log-probsref-log-probsKL[Actor(X)||Ref(X)]=E_{x\sim Actor(x)}[log\frac{Actor(x)}{Ref(x)}]\approx\text{log-probs}-\text{ref-log-probs}

  3. 作为惩罚项:

    计算出的 KL 散度值会作为一个惩罚项 ,被整合进最终的奖励信号中。 完整的奖励信号大致为:最终奖励 = Reward Model的打分 - λ * KL散度。 这个公式意味着,Actor 不仅要努力获得高分,还必须确保自己的行为方式(输出概率分布)和最初的自己(Ref 模型)不能相差太远,否则就会被 KL 散度项惩罚。

Reward Model

核心概念

Reward Model (RM) 的唯一职责是计算即时收益 RtR_t。它是一个在 RLHF 的 PPO 训练阶段之前,就已经通过人类排序标注数据训练好的模型。在 PPO 训练开始后,它的参数会被完全冻结

为什么它的参数可以被冻结?

博客中提到了“上帝视角”的概念,可以从两个层面理解:

  1. 代表“事实” : RM 是通过大量真实的人类偏好数据训练出来的,因此在RLHF阶段,我们把它当作是人类偏好的一种可靠的、客观的体现,即“事实数据”或“真理”。它为整个学习过程提供了稳定的奖励来源。
  2. 提供“即时”收益: RM 的评分是针对一个已经发生的动作 AtA_t。当 AtA_t (例如一个token)生成后,我们立刻就能从 RM 中计算出它所对应的收益 RtR_t。这是一个已知的、确定的值。

Critic Model

核心概念

Critic Model的职责是预测期望的总收益 VtV_t 。它观察当前的状态 StS_t (例如,prompt + 已生成的部分 response),然后给出一个对未来的预测:从此刻开始,直到整个 response 生成完毕,预期一共能获得多少总奖励。

为什么我们需要单独训练一个 Critic?

  • 我们没有“上帝视角”来预知未来。真实的未来总收益 VtV_t 是一个未知数,它依赖于 Actor 未来将要生成的所有 token。我们无法直接计算出这个“真值”。
  • 既然无法直接计算,我们就只能训练一个模型来学习预测它。这个模型就是 Critic Model。因此,在 PPO 训练过程中,Critic 的参数必须不断更新,以使其预测越来越准。

既然 Critic 预测的 VtV_t 已经包含了“即时”和“未来”的概念,为什么还需要 Reward Model 提供“即时”的 RtR_t

答案就在于 Critic 模型是如何被训练的

  1. 价值函数的基础:贝尔曼方程

    强化学习理论告诉我们,一个状态的价值 VtV_t 可以由它获得的即时奖励 RtR_t 和下一个状态的价值 Vt+1V_{t+1} 来表示(γ 是折扣因子):Vt=Rt+γVt+1V_t=R_t+\gamma V_{t+1}

  2. Critic 的学习目标

    Critic 的目标是让自己的预测值 VtpredV_t^{\text{pred}} 尽可能地接近真实的价值 VtV_t。但问题是,我们不知道真实的 VtV_t 是多少。

  3. 构建一个更可靠的“学习标签”

    我们虽然不知道 VtV_t 的真值,但我们可以利用贝尔曼方程构建一个比 Critic 当前预测更可靠的目标值

    • RtR_t:由 Reward Model 提供,我们视其为“事实数据”。
    • Vt+1V_{t+1}:下一个状态的真实价值我们也不知道,但我们可以用 Critic 自己对下一状态的预测 Vt+1predV_{t+1}^{\text{pred}} 来作为估算。

    将这两部分组合起来,我们就得到了一个被称为 TD Target (时间差分目标) 的值:

    Target=Rt+γVt+1pred\mathrm{Target}=R_t+\gamma V_{t+1}^\mathrm{pred}

    这个 Target 之所以更可靠,是因为它包含了一步真实发生过的、确定的奖励 RtR_t,用这个“事实”去校准对未来的“预测”。

  4. Critic 的损失函数

    Critic 的训练就是让它的预测值 VtpredV_t^{\text{pred}} 去逼近这个 TD Target。因此,它的损失函数通常是两者的均方误差 (MSE):

    LCritic=MSE(Vtpred,Rt+γVt+1predTD Target, 一个更可靠的结果)L_{\mathrm{Critic}}=\mathrm{MSE}(V_{t}^{\mathrm{pred}},\underbrace{R_{t}+\gamma V_{t+1}^{\mathrm{pred}}}_{\text{TD Target, 一个更可靠的结果}})

    通过最小化这个损失,Critic 不断更新自己的参数,使得它对价值的预测越来越符合由 Reward Model 定义的奖励体系。


Actor Model Loss

在RLHF中,Actor模型的目标是微调一个基础语言模型(通常是经过监督微调/SFT的模型),使其生成的文本能够更好地满足人类的偏好。这些偏好被编码在一个Reward模型中。Actor模型的训练核心在于其损失函数的设计,这个损失函数并非一成不变,而是经过一系列的优化演进,以实现稳定高效的训练。

第一阶段:直观的Policy Gradient设计

策略梯度的核心思想是:如果一个行为(Action)导向了一个好的结果,那么就提高这个行为被选择的概率;反之,则降低其概率。

  • 状态(State, StS_t: 在语言模型中,是到目前为止已生成的token序列。
  • 行为(Action, AtA_t: 是模型在当前状态下生成的下一个token。
  • 策略(Policy, P(AtSt)P(A_t|S_t): Actor模型本身,即在状态StS_t下生成token AtA_t的概率。
  • 价值(Value, VtV_t: 由Critic模型预测的,从状态StS_t开始,直到序列结束所能获得的总收益的期望

最直观的Actor损失函数设计如下:

actor_loss=tVtlogP(AtSt)\text{actor\_loss} = - \sum_{t} V_t \log P(A_t | S_t)

理解与推导:

  • 我们希望最大化期望回报。根据策略梯度定理,回报的梯度与 θlogPθ(AtSt)\nabla_{\theta} \log P_{\theta}(A_t|S_t) 成正比。
  • 为了通过梯度下降来最小化损失函数,我们在期望回报前加上负号。
  • 这里的VtV_t是对未来总回报的估计。
    • 如果Vt>0V_t > 0,意味着从当前状态出发的期望回报是正向的。为了让loss变小,我们需要增大logP(AtSt)\log P(A_t|S_t),也就是增大P(AtSt)P(A_t|S_t)的概率。这符合我们的直觉:一个能带来高回报的行为,应该被鼓励。
    • 如果Vt<0V_t < 0,意味着期望回报是负向的。为了让loss变小,我们需要减小logP(AtSt)\log P(A_t|S_t),即降低P(AtSt)P(A_t|S_t)的概率。

这个设计虽然直观,但存在一个主要问题:高方差VtV_t作为对未来所有收益的评估,其绝对值可能在不同序列间波动很大,导致训练过程不稳定。

第二阶段:引入优势函数(Advantage Function)

为了解决高方差问题,我们引入了优势函数(Advantage),它衡量的是在某个状态下,采取某个特定行为比“平均”行为好多少。

优势函数的定义为:

Advt=(Rt+γVt+1)Vt\text{Adv}_t = (R_t + \gamma V_{t+1}) - V_t

概念理解:

  • RtR_t:在tt时刻采取行为AtA_t后获得的即时奖励(Immediate Reward)
  • VtV_t:Critic模型对tt时刻状态价值的预测值。这可以被看作是该状态下的“平均”期望回报,也就是基线(Baseline)。
  • Rt+γVt+1R_t + \gamma V_{t+1}:这是对tt时刻状态价值的更“真实”的估计,因为它结合了真实的即时奖励RtR_t和下一时刻的状态价值Vt+1V_{t+1}γ\gamma是折扣因子)。这被称为TD Target。
  • Advt\text{Adv}_tTD Target基线之差。
    • 如果Advt>0\text{Adv}_t > 0,说明实际采取的AtA_t带来的回报超过了预期,这是一个“惊喜”或者说“甜头”(Advantage)。
    • 如果Advt<0\text{Adv}_t < 0,说明AtA_t的表现不及预期。

用优势函数替代价值函数,Actor的损失函数变为:

actor_loss=tAdvtlogP(AtSt)\text{actor\_loss} = - \sum_{t} \text{Adv}_t \log P(A_t | S_t)

这样做的好处是,通过减去一个基线VtV_t,可以有效降低梯度的方差,使得训练更加稳定。


第三阶段:重新设计RLHF场景下的即时奖励 RtR_t

在标准的强化学习中,RtR_t通常由环境直接给出。但在RLHF中,奖励的设计比较特殊,它需要平衡两个目标:1. 获得Reward模型的高分2. 保持语言的流畅性和多样性,避免模型“走火入魔”,生成Reward模型喜欢但人类看来很奇怪的内容。

为了实现第二个目标,我们引入了一个参考模型(Reference Model, PrefP_{\text{ref}},它通常就是微调前的SFT模型。我们通过KL散度来惩罚Actor模型偏离参考模型太远的行为。

因此,RtR_t被重新设计为:

Rt={kl_ctllog(P(AtSt)Pref(AtSt))tTkl_ctllog(P(AtSt)Pref(AtSt))+Rfinalt=TR_t = \begin{cases} -kl\_ctl \cdot \log\left(\frac{P(A_t|S_t)}{P_{\text{ref}}(A_t|S_t)}\right) & t \neq T \\ -kl\_ctl \cdot \log\left(\frac{P(A_t|S_t)}{P_{\text{ref}}(A_t|S_t)}\right) + R_{\text{final}} & t=T \end{cases}

理解与分析:

  • KL散度惩罚log(P(AtSt)Pref(AtSt))\log\left(\frac{P(A_t|S_t)}{P_{\text{ref}}(A_t|S_t)}\right) 是对单个token AtA_t的KL散度估计。kl_ctl是一个超参数,用于控制这个惩罚的强度。这个惩罚项在每一个生成步骤都会计算,确保Actor在生成过程中不会与初始的、行为良好的SFT模型偏离过远。
  • 最终奖励 RfinalR_{\text{final}}:这是由Reward模型对整个生成的回复进行打分后得到的最终标量奖励。
  • 为何只在最后一步加入 RfinalR_{\text{final}}? Reward模型评估的是整个生成的序列(prompt + response)的质量,而不是单个token的好坏。因此,这个总奖励被视为在生成最后一个token TT时获得的即时奖励。

这个设计精妙地将过程控制(KL惩罚)和最终目标(Reward模型高分)结合在了一起。

第四阶段:使用GAE(Generalized Advantage Estimation)优化优势函数

第三阶段定义的单步优势函数Advt=Rt+γVt+1Vt\text{Adv}_t = R_t + \gamma V_{t+1} - V_t仍然只考虑了未来一步的价值,可能存在偏差。泛化优势估计通过引入一个λ\lambda参数,来平衡偏差和方差,综合考虑了未来多步的优势。

GAE的递归计算公式如下:

Advt=(Rt+γVt+1Vt)+γλAdvt+1\text{Adv}_t = (R_t + \gamma V_{t+1} - V_t) + \gamma \lambda \text{Adv}_{t+1}

理解与推导:

  • 该公式可以展开为:Advt=δt+(γλ)δt+1+(γλ)2δt+2+\text{Adv}_t = \delta_t + (\gamma \lambda) \delta_{t+1} + (\gamma \lambda)^2 \delta_{t+2} + \dots,其中 δt=Rt+γVt+1Vt\delta_t = R_t + \gamma V_{t+1} - V_t 是单步的TD误差。
  • λ\lambda (Lambda, 0λ10 \le \lambda \le 1): 是一个权衡参数。
    • λ=0\lambda = 0 时,Advt=δt\text{Adv}_t = \delta_t,退化为单步TD优势,偏差较大但方差小。
    • λ=1\lambda = 1 时,Advt\text{Adv}_t 等价于将未来所有TD误差的折扣和加起来,这类似于蒙特卡洛方法,偏差小但方差大。
  • 在实践中,我们通常从序列的末尾开始反向计算所有时刻的Advt\text{Adv}_t。在最后一个时间步TT,其后的优势AdvT+1=0\text{Adv}_{T+1}=0,因此 AdvT=RT+γVT+1VT=RTVT\text{Adv}_T = R_T + \gamma V_{T+1} - V_T = R_T - V_T(通常定义Vend=0V_{\text{end}}=0)。然后依次回代,算出AdvT1,AdvT2,\text{Adv}_{T-1}, \text{Adv}_{T-2}, \dots

使用GAE可以得到对优势更准确和稳定的估计,从而进一步提升训练效果。


第五阶段:引入PPO(Proximal Policy Optimization)以提高样本利用率

至此,我们的损失函数形式为 actor_loss=tAdvtlogP(AtSt)\text{actor\_loss} = - \sum_{t} \text{Adv}_t \log P(A_t | S_t),其中Advt\text{Adv}_tRtR_t都经过了精心设计。但它仍然是一个标准的On-Policy算法,这意味着我们用当前策略收集一批数据(experience),只能用来更新一次模型。之后策略被更新,旧数据就失效了,必须用新策略重新采样。这使得样本利用率极低。

PPO通过引入重要性采样裁剪机制,允许我们使用同一批数据进行多次(ppo_epochs)更新,大大提高了训练效率。

  1. 重要性采样: 我们将损失函数从操作log P变为操作概率比率rt(θ)r_t(\theta)
    rt(θ)=Pθ(AtSt)Pθold(AtSt)r_t(\theta) = \frac{P_{\theta}(A_t|S_t)}{P_{\theta_{\text{old}}}(A_t|S_t)}
    其中,PθoldP_{\theta_{\text{old}}}是收集数据时所用的旧策略,PθP_{\theta}是当前正在更新的新策略。损失函数变为:
    actor_loss=trt(θ)Advt\text{actor\_loss} = - \sum_{t} r_t(\theta) \text{Adv}_t

关于重要性采样

这是一个从On-Policy(在线策略)迈向Off-Policy(离线策略)的关键一步,其核心目标是:提升数据利用效率

1. 问题的根源:On-Policy 算法的“浪费”

我们先回顾一下没有重要性采样的策略梯度损失函数:

actor_loss=tAdvtlogPθ(AtSt)\text{actor\_loss} = - \sum_{t} \text{Adv}_t \log P_\theta(A_t | S_t)

这个公式有一个严格的限制:数据和模型必须是“配套”的

  • 数据:状态(StS_t)、行为(AtA_t)、优势(Advt\text{Adv}_t)这些数据,是我们用当前的策略模型 PθP_\theta 与环境交互采样得到的。
  • 模型:我们计算梯度,更新的也是这个当前的策略模型 PθP_\theta

这就导致了所谓的 On-Policy 困境:

  1. 我们用策略 PθkP_{\theta_k}(第k个版本的模型)收集了一批数据(一个batch)。
  2. 我们用这批数据计算梯度,对模型进行一次更新,得到了新模型 Pθk+1P_{\theta_{k+1}}
  3. 此时,这批数据就“作废”了。因为这批数据是由旧模型 PθkP_{\theta_k} 产生的,它已经无法准确地指导新模型 Pθk+1P_{\theta_{k+1}} 的更新了。如果强行用旧数据去更新新模型,会引入很大的偏差,导致训练不稳定。
  4. 因此,我们必须用新模型 Pθk+1P_{\theta_{k+1}} 重新去和环境交互,收集一批全新的数据,才能进行下一次更新。

这个过程非常“浪费”。想象一下,我们费了很大力气收集了一个 batch 的宝贵经验,结果只用了一次就扔掉了。我们自然会想:我能不能用这一批数据,多更新几次模型呢? 比如,在一个 batch 上进行10次(ppo_epochs)梯度下降?

要实现这个目标,我们就必须解决“数据与模型不配套”的问题。这就是重要性采样要做的事。


2. 解决方案:重要性采样(Importance Sampling)

重要性采样是一种数学技巧,它允许我们使用从一个概率分布中采样的样本,来估计另一个概率分布下的期望值

  • 目标:我们想计算的目标函数是基于新策略 PθP_\theta 的期望回报,记为 EAtPθ[]\mathbb{E}_{A_t \sim P_\theta}[\dots]
  • 现状:我们手里的数据(样本)是从旧策略 PθoldP_{\theta_{\text{old}}} 中采样得到的,我们只能计算基于旧策略的期望 EAtPθold[]\mathbb{E}_{A_t \sim P_{\theta_{\text{old}}}}[\dots]

重要性采样通过引入一个修正系数(Correction Factor),也就是概率比率(Probability Ratio),来连接这两个期望:

Exp(x)[f(x)]=Exq(x)[p(x)q(x)f(x)]\mathbb{E}_{x \sim p(x)} [f(x)] = \mathbb{E}_{x \sim q(x)} \left[ \frac{p(x)}{q(x)} f(x) \right]

应用到我们的场景中:

  • p(x)p(x) 就是新策略 Pθ(AtSt)P_\theta(A_t|S_t)
  • q(x)q(x) 就是旧策略 Pθold(AtSt)P_{\theta_{\text{old}}}(A_t|S_t)
  • f(x)f(x) 就是我们的优势函数 Advt\text{Adv}_t

所以,我们想要最大化的目标函数可以从:

J(θ)=EAtPθ[Advt](概念形式,无法直接计算,因为没有新策略的样本)J(\theta) = \mathbb{E}_{A_t \sim P_\theta} [\text{Adv}_t] \quad \text{(概念形式,无法直接计算,因为没有新策略的样本)}

通过重要性采样,转换为:

J(θ)=EAtPθold[Pθ(AtSt)Pθold(AtSt)Advt]J(\theta) = \mathbb{E}_{A_t \sim P_{\theta_{\text{old}}}} \left[ \frac{P_\theta(A_t|S_t)}{P_{\theta_{\text{old}}}(A_t|S_t)} \text{Adv}_t \right]

现在,这个期望是基于旧策略 PθoldP_{\theta_{\text{old}}} 的,而我们手里的数据正好是从旧策略采样来的!这就解决了数据和模型不配套的问题。我们可以用这批旧数据来估计新策略的目标函数了。

这个修正系数,就是我们定义的 rt(θ)r_t(\theta)

rt(θ)=Pθ(AtSt)Pθold(AtSt)r_t(\theta) = \frac{P_\theta(A_t|S_t)}{P_{\theta_{\text{old}}}(A_t|S_t)}

因此,我们的损失函数(要最小化的目标,所以加个负号)就变成了:

actor_loss=trt(θ)Advt\text{actor\_loss} = - \sum_t r_t(\theta) \text{Adv}_t


3. 直观理解这个新公式

让我们从直觉上理解一下为什么这个公式是有效的:

  • rt(θ)r_t(\theta) 的含义:它衡量了对于“在状态 StS_t 下采取行动 AtA_t”这件事,新策略的积极性是旧策略的多少倍
    • rt(θ)>1r_t(\theta) > 1:新策略更倾向于采取这个行动。
    • rt(θ)<1r_t(\theta) < 1:新策略不太倾向于采取这个行动。
    • rt(θ)=1r_t(\theta) = 1:新旧策略倾向性一样。
  • 结合优势 Advt\text{Adv}_t 来看
    • Advt>0\text{Adv}_t > 0(这是一个好行为):
      • 损失函数是 rt(θ)Advt- r_t(\theta) \text{Adv}_t。为了让损失变小,我们需要增大 rt(θ)r_t(\theta)
      • 增大 rt(θ)r_t(\theta) 就意味着让新策略 PθP_\theta 的概率相对于旧策略 PθoldP_{\theta_{\text{old}}} 变得更大。
      • 这完全符合我们的目标:鼓励好行为。
    • Advt<0\text{Adv}_t < 0(这是一个坏行为):
      • 损失函数是 rt(θ)Advt- r_t(\theta) \text{Adv}_t,由于 Advt\text{Adv}_t 是负数,整体是正的。为了让损失变小,我们需要减小 rt(θ)r_t(\theta)
      • 减小 rt(θ)r_t(\theta) 就意味着让新策略 PθP_\theta 的概率相对于旧策略 PθoldP_{\theta_{\text{old}}} 变得更小。
      • 这也符合我们的目标:惩罚坏行为。
总结

总而言之,将损失函数从操作 log P 变为操作概率比率 rt(θ)r_{t}(θ),是为了解决On-Policy算法数据利用率低的问题

  1. 目的:希望用同一批由旧策略 PθoldP_{\theta_{\text{old}}} 收集的数据,对模型进行多次(ppo_epochs)有效的更新。
  2. 方法:引入重要性采样这一数学工具。
  3. 实现:通过引入概率比率 rt(θ)=PθPθoldr_t(\theta) = \frac{P_\theta}{P_{\theta_{\text{old}}}} 作为修正系数,构建了一个新的、可以在旧数据上评估新策略性能的目标函数。
  4. 效果:使得模型可以在一个 batch 的数据上进行多轮学习,极大地提升了训练的稳定性和样本效率。

当然,这个转换也引入了新的问题:如果新旧策略差异太大(rt(θ)r_t(\theta) 过大或过小),重要性采样的估计会变得极不稳定。而解决这个问题的方法,正是PPO算法的另一个精髓——裁剪(Clipping)

  1. 裁剪(Clipping): 重要性采样可能导致rt(θ)r_t(\theta)过大或过小,造成不稳定的更新。PPO的核心思想是限制rt(θ)r_t(\theta)的变化范围,从而限制新旧策略的差异。这是通过裁剪目标函数实现的。

PPO-Clip的最终损失函数为:

actor_loss=Et[min(rt(θ)Advt,clip(rt(θ),1ϵ,1+ϵ)Advt)]\text{actor\_loss} = - \mathbb{E}_t \left[ \min\left( r_t(\theta) \text{Adv}_t, \text{clip}(r_t(\theta), 1-\epsilon, 1+\epsilon) \text{Adv}_t \right) \right]

理解与分析:

  • ϵ\epsilon 是一个小的超参数(例如0.2,对应博客中的范围[0.8, 1.2])。
  • clip(rt(θ),1ϵ,1+ϵ)\text{clip}(r_t(\theta), 1-\epsilon, 1+\epsilon) 将比率rtr_t强制限制在[1ϵ,1+ϵ][1-\epsilon, 1+\epsilon]区间内。
  • min函数的意义在于取一个“悲观”的界限:
    • Advt>0\text{Adv}_t > 0 (好行为):我们希望增大rt(θ)r_t(\theta)。但为了防止更新过猛,我们只允许rt(θ)r_t(\theta)最多增大到1+ϵ1+\epsilon。因此,损失由 rt(θ)Advtr_t(\theta)\text{Adv}_t(1+ϵ)Advt(1+\epsilon)\text{Adv}_t 中较小的一个决定。
    • Advt<0\text{Adv}_t < 0 (坏行为):我们希望减小rt(θ)r_t(\theta)。同样,为了防止更新过猛,我们只允许rt(θ)r_t(\theta)最多减小到1ϵ1-\epsilon。在这种情况下,(1ϵ)Advt(1-\epsilon)\text{Adv}_t会比(1+ϵ)Advt(1+\epsilon)\text{Adv}_t更大(因为Advt\text{Adv}_t是负数),所以min函数实际上是在rt(θ)Advtr_t(\theta)\text{Adv}_t(1ϵ)Advt(1-\epsilon)\text{Adv}_t之间取一个悲观界限。
  • 通过这种方式,PPO确保了在用同一批数据进行多轮更新时,新的策略不会与旧的策略偏离太远,从而保证了训练的稳定性。

Actor Model Loss 总结

RLHF中Actor模型的损失函数经历了从简单到复杂的演进,每一步都针对特定问题进行了优化:

  1. 基础策略梯度:奠定理念基础。
  2. 引入优势函数 (Advantage):替代V值,降低梯度方差,稳定训练。
  3. 重定义奖励 (Reward):为RLHF场景量身定制,通过KL散度惩罚来保证生成质量,同时结合Reward模型评分作为最终目标。
  4. 使用GAE:优化优势函数的估计,平衡偏差与方差。
  5. 采用PPO-Clip:通过重要性采样和裁剪,实现了安全的off-policy更新,极大提高了样本利用率和训练效率。

Critic Model Loss

在RLHF的PPO训练框架中,Critic模型与Actor模型是相辅相成、同步更新的。Critic的核心职责是准确地评估当前状态的价值(Value),即从当前状态(已生成的文本序列)出发,预期未来能获得的总奖励是多少。

一个准确的Critic模型至关重要,因为它扮演着两个关键角色:

  1. 为Actor提供基线(Baseline):在计算优势函数 Advt\text{Adv}_t 时,Critic的价值预测 VtV_t 作为基线被减去,这能有效降低训练的方差。
  2. 评估未来状态:在计算TD-Target时,需要用到下一时刻的价值预测 Vt+1V_{t+1}

与Actor Loss的复杂演进不同,Critic Loss的逻辑更直接:它的目标始终是让自己的预测值无限接近一个估算的“真实”目标值。因此,它的损失函数本质上是一个均方误差

Lcritic=E[(预测的价值目标的价值)2]L_{\text{critic}} = \mathbb{E} \left[ (\text{预测的价值} - \text{目标的价值})^2 \right]

我们要对这个公式中的“预测的价值”和“目标的价值”进行优化,以适应PPO框架下多轮次更新的需求。

第一阶段:基础的Critic Loss

最基础的Critic训练方法是时序差分(Temporal Difference, TD)学习。

  • 预测的价值: 就是Critic模型对当前状态 StS_t 的直接输出,Vϕ(St)V_\phi(S_t)
  • 目标的价值: 使用当前获得的即时奖励 RtR_t 加上对下一状态价值的折扣预测 γVϕ(St+1)\gamma V_\phi(S_{t+1}) 来估算。这个目标被称为 TD-Target

因此,基础的Critic Loss可以写为:

L(ϕ)=E[(Vϕ(St)(Rt+γVϕ(St+1)))2]L(\phi) = \mathbb{E} \left[ (V_\phi(S_t) - (R_t + \gamma V_\phi(S_{t+1})))^2 \right]

这个公式的含义是:我们希望tt时刻的价值预测 Vϕ(St)V_\phi(S_t),能够逼近“走一步”之后看到的更真实的价值 (Rt+γVϕ(St+1))(R_t + \gamma V_\phi(S_{t+1}))

第二阶段:优化“目标的价值” (Actual Return)

TD-Target只考虑了未来一步的真实奖励,这可能带来较大的偏差(Bias)。在前面的Actor Loss讲解中,我们已经计算了泛化优势估计(GAE, AdvtGAE\text{Adv}_t^{\text{GAE}},它包含了未来多步的奖励信息,是一个对回报更稳定、方差更低的估计。

我们可以利用GAE的计算结果来构建一个更优质的目标价值。回顾一下优势函数的定义:

Advt=(实际回报)Vt\text{Adv}_t = (\text{实际回报}) - V_t

反过来,我们可以得到:

实际回报=Advt+Vt\text{实际回报} = \text{Adv}_t + V_t

因此,我们将Critic的目标价值优化为:

Vttarget=AdvtGAE+Vϕold(St)V_t^{\text{target}} = \text{Adv}_t^{\text{GAE}} + V_{\phi_{\text{old}}}(S_t)

概念理解与分析:

  • 这里的 AdvtGAE\text{Adv}_t^{\text{GAE}} 是我们为Actor计算好的、基于GAE的优势值。
  • Vϕold(St)V_{\phi_{\text{old}}}(S_t) 是由收集数据时的旧Critic模型所预测的价值。在PPO的多轮更新中,这两个值都是固定不变的。它们是在ppo_epoch循环开始前就计算好的。
  • 这个新的目标 VttargetV_t^{\text{target}} 被称为GAE-Lambda Return。相比于单步的TD-Target,它综合了未来多步的信息,是一个更可靠的“真实价值”的估计,可以有效降低训练偏差。在博客的代码中,这个值对应参数 returns

第三阶段:优化“预测的价值” (Predicted Return) 与 PPO裁剪

与Actor一样,Critic模型也在一个batch的数据上进行多轮(ppo_epochs)更新。这意味着在循环中,Critic模型的参数 ϕ\phi 是不断变化的。

  • Vϕnew(St)V_{\phi_{\text{new}}}(S_t): 当前正在更新的Critic模型所输出的价值预测。它是动态变化的。
  • Vϕold(St)V_{\phi_{\text{old}}}(S_t): 收集数据时的旧Critic模型所输出的价值预测。它是固定不变的。

为了防止在多轮更新中,VϕnewV_{\phi_{\text{new}}}VϕoldV_{\phi_{\text{old}}} 偏离过远,导致作为Actor基线的值发生剧烈变化,我们也对Critic的预测值引入裁剪机制。

Vclipped(St)=clip(Vϕnew(St),Vϕold(St)ϵ,Vϕold(St)+ϵ)V^{\text{clipped}}(S_t) = \text{clip}\left(V_{\phi_{\text{new}}}(S_t), \quad V_{\phi_{\text{old}}}(S_t) - \epsilon, \quad V_{\phi_{\text{old}}}(S_t) + \epsilon \right)

  • ϵ\epsilon 是一个小的超参数。
  • 这个公式将当前模型的预测值 VϕnewV_{\phi_{\text{new}}} 强制约束在旧模型预测值 VϕoldV_{\phi_{\text{old}}} 的一个小邻域内。

第四阶段:最终的Critic Loss公式

结合了优化的目标和裁剪后的预测,我们得到了最终的Critic Loss。它由两部分组成:

  1. 无裁剪损失 (Unclipped Loss):使用当前Critic的直接预测值与目标值计算MSE。

    L1=(Vϕnew(St)Vttarget)2L_1 = \left( V_{\phi_{\text{new}}}(S_t) - V_t^{\text{target}} \right)^2

  2. 裁剪后损失 (Clipped Loss):使用被裁剪后的预测值与目标值计算MSE。

    L2=(Vclipped(St)Vttarget)2L_2 = \left( V^{\text{clipped}}(S_t) - V_t^{\text{target}} \right)^2

最终的Critic Loss取这两者中的最大值Lcritic(ϕ)=Et[max(L1,L2)]L_{\text{critic}}(\phi) = \mathbb{E}_t \left[ \max(L_1, L_2) \right]

max 函数的直观理解:

这部分是PPO Critic Loss设计的精髓。取最大值的目的是构建一个悲观的界限,优先保证训练的稳定性

假设一次更新使得 VϕnewV_{\phi_{\text{new}}} 朝着目标 VttargetV_t^{\text{target}} 移动,但超出了裁剪范围 [Vϕold±ϵ][V_{\phi_{\text{old}}} \pm \epsilon],此时,VclippedV^{\text{clipped}} 会被卡在边界上,而 VϕnewV_{\phi_{\text{new}}} 会继续移动。这会导致裁剪后的预测值 VclippedV^{\text{clipped}} 比未裁剪的 VϕnewV_{\phi_{\text{new}}} 离目标 VttargetV_t^{\text{target}} 更远。因此,L2L_2(裁剪后损失)会大于 L1L_1(无裁剪损失)。通过 max(L1,L2)max(L_1, L_2),我们选择了那个更大的惩罚L2L_2)。

这个机制的意图是:“我们允许你更新模型,但如果你试图一步跨得太远(超出信任域),我就会用一个更大的梯度来惩罚你,迫使你保持在旧模型的邻近区域内。” 这确保了作为Actor基线的价值函数不会在多轮更新中发生剧烈振荡,从而保障了整个PPO训练框架的稳定性。

Critic Model Loss 总结

RLHF中的Critic模型损失函数经过了精心的设计,以适应高效的PPO训练框架:

  1. 核心思想:仍然是最小化预测值与目标值之间的均方误差
  2. 目标值优化:使用基于GAE计算的GAE-Lambda Return (Advt+VtAdv_t + V_t) 作为“真实价值”的估计,相比传统TD-Target更准确、稳定。
  3. 预测值优化:在PPO的多轮更新中,引入了对输出价值的裁剪机制,将新模型的预测值约束在旧模型的一个小邻域内。
  4. 最终损失:通过取无裁剪损失裁剪后损失最大值,构建了一个悲观而稳健的损失函数,优先保证了价值函数作为基线的稳定性,防止其在多步更新中剧烈变化。

最后附一张其他博客清晰的流程图:

image.png