©PaperWeekly 原创 · 作者 |
张逸骅
如果你对大语言模型(LLM)的
强化学习(RLHF)
感兴趣,又想从最基础的策略梯度优化一路了解、推导出 PPO、GAE,再深入探讨 DPO,那你就来对地方了。
本文将从最基础的
Gradient Policy Optimization
开始,逐步介绍经典的
REINFORCE 算法
,再讲解如何利用剪切目标实现近端策略优化(
PPO
),并通过广义优势估计(
GAE
)在偏差与方差之间找到最佳平衡。之后,我们还会从头推导、讨论离线训练方法,如
DPO
,帮助你了解不同训练路线的优势与挑战。
在线(On-Policy)和离线(Off-Policy)强化学习
如今,LLM 中主流的 RLHF 方向分为两大路线:
以 PPO 为代表的 On-Policy 路线
以 DPO 为代表的 Off-Policy 路线
那么,什么是 On-Policy,什么是 Off-Policy 呢?可以用一个简洁的判定方法:
一般来说,On-Policy 的方法在训练时会更“耗卡”、更耗时——最大的开销主要是源自“模型生成”这一步,因为对一个生成式任务而言,模型需要逐 token 地输出,这个过程极其耗费算力。
不过,尽管速度较慢,On-Policy 在理论上拥有更高的效果上限,因为它能够不断根据当前模型状态进行探索和更新,这一点将在后续讨论
PPO
时更加凸显。
我们首先来谈谈 On-Policy 路线。On-Policy 的核心思路是:让模型自己产出答案,然后依据答案的优劣来打分,以此指导下一步的参数更新。简而言之,最关键的一点是
让模型“亲自下场”
。
假设你是一个需要学会下象棋的模型,现在有两种训练方式:
方式一:
让你真刀真枪地下棋,每一步都有教练跟在你身边打分。当你吃掉对手棋子时,教练会鼓励你;当你因为冲动失误被对面反杀时,教练会及时提醒你改进。
方式二:
给你一堆职业选手的比赛录像和一堆臭棋篓子的对局,用标注告诉你哪些操作是好招,哪些操作是坏招,然后你被动地学这些好操作、避免坏操作。
这两种方式最大的区别就在于:你有没有亲自去“下棋”。方式一就是
On-Policy
,需要模型自己产出行为,然后学习;方式二就是
Off-Policy
,只需根据已有对局数据进行模仿式学习。
Off-Policy 在训练时通常更快,因为它用现成的数据就可以了,不需要模型时时在线生成并等待打分,但也很依赖这批数据与当前模型能力的“匹配度”。如果数据中操作难度和模型水平相差太大(过高或过低),学习效果就可能大打折扣;On-Policy 则可以避免这一问题,因为它所得到的训练样本 100% 来自于自己当前的水平和行动。
在语言模型场景中,一个典型的 On-Policy 算法往往包含以下组件:
Actor:
负责“生成”句子的模型(就像正在对弈的你)。
Critic:
类似于“教练”,为每个生成结果提供即时指导;它本身也在训练过程中随 Actor 的能力变化而调整。
Reward Model:
相当于“裁判”,给出最终分数或偏好评估。通常在训练过程中是固定不动的。
Reference Model:
PPO 在大模型里的“独有角色”,用来防止 Actor 过度偏离原有预训练分布,缓解 reward hacking 等问题。
由于在大型 LLM 上,这四个部分的参数量都可能非常庞大(往往需要同时加载多个 70B 参数规模的模型),所以 On-Policy 训练往往带来极高的算力需求,这也是为什么人们通常说 PPO“非常耗卡”的原因。
下一步,我们将把目光聚焦在当前 On-Policy 路线最具代表性的方法——PPO 上,看看它究竟如何在实践中平衡训练开销与学习效率。
PPO(近端策略优化)
2.1 从策略梯度优化(Policy Gradient Optimization)谈起
想象一下,你是一名刚开始学习下象棋的新手。你的目标是通过不断调整你的下棋策略(记作
,其中
表示你的策略参数),来提高在一局棋中获得胜利的概率,也就是最大化你的期望回报。我们可以将每一盘棋看作是一条轨迹
,而你要做的,就是通过不断优化你的策略来获得更高的回报。
更一般得,强化学习的目标就是去优化一个策略,使得回报的期望最大:
形式上, 这个策略的回报被定义在所有可能的轨迹上:
所谓的轨迹,就是一连串状态和对应动作的组合(state,action)
:
在下棋这个例子中,状态
可以理解为当前棋盘落子的状态,而动作
即为下一次落子的地方。而当前时间点的下一个状态,则服从某种概率分布,可以被看作是随机的、不确定的(即对手落子):
那么一个轨迹
的概率则为:
在强化学习中,我们会不断提到回报会随着时间不断打折 (discount reward) 的概念:未来的回报总是不如当下的回报那么重要。所以一个策略
的总回报可以被视作:
其中
是时间上的折扣因子,而
是
时刻的实际回报。
在深度学习中,我们通常采用最小化损失函数来更新参数,这正是随机梯度下降(Stochastic Gradient Descent)的做法。但在这里,我们的目标是
最大化
回报,因此我们使用随机梯度上升(Stochastic Gradient Ascent)来更新策略:
这里的
就被称为
策略梯度(policy gradient)
。换句话说,就好比每盘棋结束后,你会复盘,评估自己每一步走法对最终胜负的贡献,然后调整下一盘棋的策略。这样的更新方法统称为
策略梯度算法(policy gradient algorithms)
。
然而,正如在下棋时要考虑所有可能的走法和局面一样,精确计算这个梯度需要对所有可能棋局(轨迹)进行求和或积分,而这在实际中(除非棋盘极其简单)是计算上不可行的,因为即使你拥有可导的
,由于轨迹的步数太多,在使用 auto-differentiation 求导过程中会因为 memory 太大而使用非常受限。因此,我们需要仔细思考一下怎么求这个策略梯度。
为了得到一个可操作的策略梯度公式,就像在复盘中总结经验一样,我们从目标函数的梯度开始推导。将每盘棋视为一条轨迹
,目标函数梯度为:
第1步:展开期望(Expand the Expectation)
这一步相当于考虑所有可能的棋局,我们将期望展开为对所有轨迹的积分:
第2步:交换梯度与积分(Interchange Gradient and Integral)
就像把每一步棋的影响拆分出来,我们将梯度操作符移入积分内部:
第3步:使用对数导数技巧(Apply Log-Derivative Trick)
利用一个数学技巧(对数导数),类似于在复盘中分解每一步的重要性,我们有:
第4步:回到期望形式(Return to Expectation Form)
在下棋的过程中,每盘棋的走法取决于你每一步的决策。假设一盘棋的轨迹
可表示为:
这里
就是你在棋局某一时刻(状态
)下选择某一步棋(动作
)的概率。取对数后求梯度,我们得到:
(注意:棋局中对手的反应
由规则决定,与
无关,因此其梯度为零。也就是说,当
给定时,
也就定下来了。)
2.2 最终策略梯度公式(Final Policy Gradient Formula)
在这条公式中,每一步棋的决策(
)决定了整盘棋的表现,而不依赖于对手的固定规则。实际操作中,我们通常使用蒙特卡洛抽样来近似这个期望,就好比你通过大量实战积累经验来提升下棋水平。最后,基于采样的策略梯度可以由以下式子近似:
如果你仔细观察这个式子,你会发现很有意思的两个地方。首先
直接出现在策略参数的梯度里边。
2.3 REINFORCE 算法流程与实现步骤
下面介绍经典的策略梯度方法——REINFORCE 算法,它就像你通过不断下棋、复盘总结经验来不断改进你的棋艺:
2. 轨迹采样
用当前策略进行对局(采样轨迹
),并记录每步棋得到的奖励(例如赢棋后的奖励分数)。
你可以设定每盘棋固定步数(比如 100 步),或直到比赛结束。
根据收集到的对局数据集
计算梯度估计,就像总结每盘棋中各步对胜负的贡献:
使用随机梯度上升法更新你的策略参数,就好像根据复盘结果调整你的下棋风格:
5. 循环优化
重复“下棋-复盘-调整”这一过程,直到你的策略收敛,即你能稳定地打出高水平的棋局。
这里,我们利用大量实战(蒙特卡洛抽样)来近似整个期望。
轨迹总奖励
就是整盘棋的胜负结果,用来衡量你所有决策的综合效果。
2. 参数更新规则
表示学习率,相当于每盘棋复盘后你调整策略的幅度;梯度的方向正是指向能够提升胜率的方向。
3. 算法特性
关键优势:
这种方法完全依靠你下棋的实战经验,不需要提前知道对手的策略(model-free)。
计算要求:
需要大量的对局采样以降低随机性带来的波动(方差)。
改进方向:
后续方法(如 Actor-Critic)会引入价值函数参考线,使得策略更新更为稳定,就像在复盘中加入专业教练的点评一样,帮助你更快提高棋艺。
2.4 策略梯度优化面临的问题
策略梯度优化一个核心的假设是:我们可以通过采用的方法来估计策略的梯度。但是当问题的规模变得非常大:比如每次轨迹
都非常长,又或者策略模型非常大,为了预估准确的梯度,我们就不得不采样多次,否则就会面临方差很高的问题。
策略梯度算法中的梯度估计虽然在理论上是无偏的(即其期望值会收敛到真实梯度),但实际上它的方差非常高,该梯度估计可以写成:
其中:
分别代表在时间步
你采取的动作和所处的状态。想象你在下棋。
想象你在下棋。每一步你都希望知道自己的决策对最终胜负的贡献,但问题在于,如果你试图把整盘棋的输赢都归因于每一步决策,那么这种评估就会变得非常不稳定——也就是方差很高。接下来,我们将采取不同的做法来减小这样估计的方差。
2.5 减小方差:只关注未来
观察上边用于梯度估计得式子:无论当前在哪一步
,
总是会把整个轨迹中所有的 reward 都算进去。然后这么做是不太合理的,当前的决策应该只需要考虑对未来产生的影响:过去的已经无法改变了,无需再加入到
的计算中。
回到下棋的例子:假如每一步的评分都把前面已经走过的好步或坏步也计入进去,那就会混淆你当前决策的真实价值。实际上,在评估当前走法时,你只需要关注从这一步开始直到局末的“后续表现”。这就是所谓的
“rewards to go”
,即只考虑从当前动作开始到比赛结束所获得的奖励。
用数学表达就是,我们将原来的梯度估计调整为:
这里
,
就
代表了从当前走法开始到局末的“奖励总和”。这样做就像你在复盘时,只关注从某一步开始后续的变化,而不纠结于那一步之前已经发生的事情。
因此,当我们把这些“来自过去的”的冗余项去除后,噪声就自然而然会减少一些。
2
.6 减小方差:参考线(Baseline)
为了进一步减少评估中的波动,我们可以为每一步的“后续奖励”减去一个基准值。数学上,这个参考线通常记为
(在实际中,我们常用价值函数
来作为这个参考线),公式为:
其中
就是所谓的参考线,他不一定是一个常数,更多时候是另外一个状态
的函数。这个参考显得实际意义是,在当前的状态下,回报的期望大概是什么样子。那么所谓超出期望的部
分
就
是优势(Advantage)。在实际训练中,我们会用优势代替原来的奖励进行梯度估计,以减小方差。
在大语言模型的对齐训练中,我们通常在语言模型(即策略
)的基础上增加一个额外的线性层,用来估计在某个状态下的预期回报
。这相当于为每个局面设定了一个标准分,帮助我们衡量当前决策的实际优势。如果你想更直观得了解为什么需要这个参考线,可以阅读我的
上一篇文章
。
2.7 减小方差:引入
和
在上边,我们提到了 “rewards to go” 的概念,
即
。
这个项在强化学习中被称为
Q 函数
(
)
,即在状态
采取动作
后,未来获得的总回报。然后,通过减去状态价值
我们得到
优势函数
:
用下棋的比喻,Q 函数描述的是你当前这个局面
在走了一步
后可能的输赢,而状态价值表示的是仅凭现在的局面,你还有多少老本儿可以吃。如果当前局面对你十分有利,但是你走了一步臭棋,尽管你最后赢面还是很大(相当于
的绝对大小还是很大),但是你相对于对方的“优势”却减弱了。
所以,你不仅应该关注某一步棋的绝对得分,还要和“老本儿”比较比较,看看这一步棋究竟为你增加了多少胜率。如果
为正,则说明这步棋明显扩大了你的优势;若为负,则表明你这一招不妙。
最终,我们可以将策略梯度写为:
这公式正是衡量你在每个局面上,通过比对每一步棋与平均表现的差距,从而决定如何调整你的下棋策略。
2.8 优势函数的解释
简单来说,优势函数
就告诉你,在某个局面(状态
)下,选择某个特定走法(动作
)相比于平均走法能提升多少胜率。如果这步棋带来的预期回报远高于你当前的基准水平,那么这步棋的优势就是正的,说明它非常值得采用;反之,则说明不如平均水平。
总之,通过这些方法——只考虑“后续奖励”、引入参考线以及使用优势函数,我们就能在训练中有效降低梯度估计的方差,就像你在下棋时只关注关键走法对局面转变的影响,从而让策略更新更稳定、更有针对性。
2.9 如何估计优势项 - 使用基于迭代的 GAE 策略
上边的例子告诉我们,我们可以累加若干步来实现偏差和方差的权衡。
为平衡这一
偏差-方差问题
,我们可以采用对这几项进行加权求和的做法,也就是
广义优势估计(Generalized Advantage Estimation, GAE)
:
这是一个
递归公式
。末端时刻的优势估计可以看作第一种展开,而它的前一时刻会再加上一层衰减系数
。
通过在各时间步上不断迭代累加,就可以平衡真实奖励所带来的高方差和使用价值函数所带来的高偏差。
在大语言模型对齐的场景中,这个结果会指示策略(语言模型)在给定某个提示(state)后,去
提升那些在期望意义上“优于平均”奖励
的下一个 token 选取概率。换言之,模型将倾向于选择那些
更可能引导未来 token 合乎我们所希望的奖励标准
(即更“对齐”或更符合训练数据分布)之序列。
2.10 PPO 损失函数(The PPO Loss)
在 PPO(近端策略优化)中,为了防止更新时策略变化过大,我们会构造一套特殊的损失函数。它主要由以下几部分构成:
这一部分就像是在下棋时,你不希望一次改变策略太多,而是希望微调每一步的选择,保证既能改善局势,又不会因冒险走出常规而导致局面混乱。
价值函数损失(Value Function Loss,
)
这一项帮助我们确保对于每个局面,你的预期回报估计(就像预判棋局发展)与实际获得的回报尽可能接近。
熵损失鼓励策略保持一定的探索性,就像一个优秀的棋手不仅熟练掌握定式,同时也敢于尝试新变化,保持灵活应变的能力。
将这些部分结合起来,就构成了 PPO 的总损失函数。这个损失函数旨在在更新策略时既提高胜率(奖励),又防止策略偏离原有风格过远,保持平稳而高效的改进。
2.11 使用 PPO 的优势
稳定性:
剪切操作(clipping)确保策略更新时步伐不会过大,就像你在下棋时不会突然改变风格,保证每一步都稳扎稳打。
样本效率:
PPO 能够较好地利用收集到的对局数据,尽管在大型模型的场景下仍需大量采样。
内在安全性:
通过剪切更新和参考策略的 KL 惩罚,PPO 能有效防止模型在更新时出现剧烈偏差,从而确保生成结果不会与预训练风格南辕北辙。
总体来说,就像一位经验丰富的棋手在不断下棋、复盘、调整策略中不断进步一样,PPO 通过精细的梯度更新和对策略变化的限制,实现了稳健而高效的强化学习。
GAE(广义优势估计)理解及推导
想象一下你在参加一场国际象棋比赛。每一步棋不仅会直接影响当前局面,还可能对后续整个比赛产生深远影响。为了判断某一步棋到底给你带来了多少优势,你需要同时考虑这一步棋的即时得分和它对未来局势的潜在贡献。
广义优势估计(Generalized Advantage Estimation, GAE)正是为了解决这一问题而设计的,它帮助策略评估在某个状态下采取某个动作比平均水平会带来多少优势,就像你在复盘时会评估某一步棋是否为你赢得了更高的胜率。
GAE 的创新之处在于结合了多步估计和时间差分(TD)估计的思想,利用一系列过去的误差来预测未来收益,从而在平衡偏差与方差之间达到最佳状态。PPO 与 GAE 联合使用时,就好比一位棋手在有了稳健的对局策略框架(PPO)的基础上,又利用精确的局面评估(GAE)来不断优化每一步的决策,使得整体表现更加稳定且高效。
3.1 引入残差的概念
在下棋时,你往往无法一开始就知道一招棋的全部价值,而只能根据下一步的预判来估计其效果。这个预判与实际结果之间的差异就叫做时间差分(TD)残差。
为什么需要残差?
设想在比赛中,你站在一个关键的分叉口,基于经验认为向左走可能更有利,但实际走后发现局面并不如预期。这种实际体验与原始预判之间的差异就是残差,它帮助你修正对未来局面的估计,使你在后续对局中做出更明智的选择。
残差的数学表达
其中
表示在状态
下采取动作
能带来的未来收益(经过 baseline 校正后的优势),我们简称为
。如果简单地定义:
那么,为了更准确地反映从当前走法开始未来带来的全部影响,我们引入 TD 残差。具体来说,对于状态
下采取动作
,如果接下来的状态是
并获得奖励
,则定义 TD 残差为:
和
分别是你对当前局面和下一局面预期的整体评分,相当于教练对局面的评价。
残差的直觉解释
假设你在下国际象棋,你当前的局面是
,然后你选择了某个动作
,导致游戏进入了新的局面
,并且你获得了即时奖励
。你的目标是估计这一步对未来的影响。
你对当前
状态
的预估价值是
,它代表你在这个状态下按照当前策略继续走下去能获得的预期收益。
你对
下一步
的预估价值是
,它代表你在下一步之后能获得的预期收益。
也就是说,你的当前状态的价值
应该等于你的即时奖励
加上你折扣后的未来预期价值
(因为未来的收益随着时间的推移会衰减,是衰减因子)。
但实际上,你的
可能并不等于这个理想的估计,而是有一定的误差。因此,我们定义 TD 残差来表示这个误差。因此,
衡量了你当前走法与预期之间的差距,就好像你发现某步棋的实际效果比预期好或差,从而提供了调整策略的依据。
为了获得更准确的评估,就像你在复盘时不仅关注眼前一步,而是回顾接下来的几步对局势的影响,我们定义
表示从时刻
开始向前看
步的累计优势:
这就好比你在下棋时,把从当前走法开始到比赛结束的所有实际得分都考虑在内。显然,考虑的步数越多(
越大),你对局面的评估偏差越小,但同时可能受到更多偶然因素的影响,导致方差增大;反之,若只看眼前几步,则偏差较大但方差较小。
3.2 偏差-方差的折衷(Bias-Variance Tradeoff)
在实际中,为了在偏差与方差之间找到最佳平衡,我们不会简单地选择某个固定的
来累加所有奖励,而是采用指数加权的方式,这就是 GAE 的核心思想。
这
里,
就像你在下棋时决定对未来几步影响的重视程度:
当
时,你会尽可能考虑整盘棋的所有后续效果(偏差最小,但方差最大)。
当
时,你只关注当前一步(偏差最大,但方差最小)。
此外,我们也可以用加权求和的方式表示多步优势。定义:
假设我们只考虑前
项,根据等比数列求和公式,我们有:
这正展示了如何通过调节
来权衡短期与长期收益的影响。正如一位棋手既不会只看眼前一步,也不会试图预判所有可能变化,而是用一个合适的比例权衡近远期影响一样:
当
时,优势估计包含了整盘棋的所有观察信息(偏差最小,方差最大):
当
时,只依赖于当前一步的信息(偏差最大,方差最小):
这种方法就如同一位棋手在下棋时,不仅关注眼前一着的效果,同时用合理的权重考虑未来几步的走向,从而做出最优决策。
用 PPO 训练 LLM 的 Token-per-Token 过程
下面以一种直白、逐 token 的方式讲解在使用 PPO 训练一个 LLM 时到底发生了什么,并说明其中涉及的参数(
与
)的角色以及第一次更新的过程。
4.1 用 PPO 对齐 LLM
(旧策略参数):
这是用来生成数据(比如生成 token 序列)的模型参数。在一次 PPO 更新前,你会用这套参数来采样(即生成 token),同时记录下每个 token 的生成概率(通常以 log-probability 的形式记录)。
(当前策略参数):
这是正在被更新的模型参数。你通过 PPO 算法根据之前采样得到的数据来调整这组参数,使得模型生成的 token 更符合奖励信号。更新后,
就会和
不一样。
可以把
想象成“老版模型”,而
则是“新版模型”,新版模型在一次训练迭代后会比老版模型更好(或者更符合奖励信号)。在每次更新后,新版模型会成为下一轮循环中的“老版模型”。
2. “Token-per-Token” 的训练过程
假设你已经有一个预训练的 LLM,我们把它的参数设为
。下面是一个具体的过程说明:
你将一个 prompt 输入 LLM(使用
的参数)生成文本。
模型根据当前上下文(state)生成下一个 token(action)。例如,当生成第 t 个 token 时:
当前状态
是 prompt 加上前面 t-1 个 token。
模型选择了 token
的动作,并给出了一个概率
(通常记录为 log-probability)。
对于每个 token,你记录:
在 PPO 中,我们需要计算每个 token 的优势
(可以采用 GAE,多步或者单步的方式)。
在采样得到一批 token 数据后,你开始用 PPO 算法更新模型参数。这里 “token-per-token” 的解释如下:
1. 旧策略作为参照
你已经记录了每个 token 在生成时(使用
)得到的 log-probability,
即
。
2. 当前策略重新计算概
使用当前的模型参数
(最初初始时
和
是一样的,但你会进行多次梯度更新后,
会发生变化),对同样的状态
计算生成相同 token at 的 log-probability,
即
。
3. 计算概率比
对于每个 token,计算概率比:
这个比值表示“新版模型”与“旧版模型”在该 token 处的生成倾向发生了多大变化。
4. 构造 PPO Loss(每个 token 的损失)
PPO 的目标是限制新版和旧版的差异过大。对每个 token,根据优势
和比值
计算损失:
直白理解:
如果当前策略与旧策略的概率比
和优势
的乘积在可接受范围内(在
内),就用这个值。
如果比值太大或者太小,就用限制后的值(clip 后的值),防止更新步长过大。
将所有 token 的损失
平均,得到整个批次的 loss。然后用梯度下降(或其他优化器)更新模型参数
。
这时,
开始和
不一样了,也就是说新版模型已经从旧版模型上进行了“改进”。
在一次完整的 PPO 更新(通常经过多个 epoch 在同一批数据上训练)后,你会把当前模型参数
赋值给
,为下一轮采样做好准备。
下面给出一个伪代码风格的算法块,帮助理解 token-per-token 的 PPO 更新过程:
# 初始化:预训练 LLM 的参数设为 θ_old,同时复制给 θ θ_old = PretrainedLLM.parameters θ = copy(θ_old)# 采样阶段:使用 θ_old 生成一批数据 for each prompt in dataset: trajectory = [] state = prompt while not end_of_sequence: token, logpi_old = θ_old.generate_token(state) # 记录当前状态、token 以及 θ_old 下的 log概率 trajectory.append( (state, token, logpi_old, reward, V(state)) ) state = state + token # 更新状态(追加 token) store trajectory# 计算优势(例如采用 GAE) for each trajectory: for t from last token downto first: δ_t = reward[t] + γ * V(state[t+1]) - V(state[t]) A_t = δ_t + γ * λ * A[t+1] # 递推计算 # PPO 更新阶段:多轮 epoch for each PPO update epoch: for each token data (state s_t, token a_t, logpi_old, A_t) in batch: # 1. 当前策略计算 log 概率 logpi_current = θ.log_probability(s_t, a_t) # 2. 计算概率比 r_t = exp( logpi_current - logpi_old ) # 3. 计算未剪切与剪切目标 loss_unclipped = r_t * A_t loss_clipped = clip(r_t, 1-ε, 1+ε) * A_t # 4. 每个 token 的损失取较小值(并加上负号,因为我们是最小化 loss) loss_token = -min(loss_unclipped, loss_clipped) # 5. 平均所有 token 的 loss,进行一次梯度更新 θ = Update(θ, average(loss_token over batch))# 更新完毕后,将 θ 复制给 θ_old,用于下一轮采样 θ_old = copy(θ)
4.2 在第一次更新时,如果新模型和旧模型一模一样,PPO Loss 是 0 吗?
是优势估计,反映了该 token 相对于基准(baseline)的“好坏”。
这个 Loss 的目的是引导模型朝着使得生成动作得到更高优势(奖励)的方向更新,而不是直接衡量新旧模型的参数差异。
2. 如果新模型和旧模型一模一样,损失是不是为零?
在第一次更新之前,我们通常将新模型参数
初始化为旧模型参数
。因此,对于每个 token,
并且剪切操作对 1 没有影响(因为
),结果仍然是
。
因此,每个 token 的目标函数就是
,而 Loss 是加上负号,即:
优势
是通过环境奖励和价值函数估计计算得到的,它反映了“这个 token 产生的结果比预期要好还是差”。即使新模型与旧模型完全一致,环境反馈(奖励)和价值函数的估计通常会使得
非零。
采样阶段
当你用旧模型(
)生成 token 时,会记录每个 token 的生成概率、对应的奖励以及基于价值函数的估计。假设某个 token 在环境中获得了较高奖励,但模型当时对该 token 的预估不够乐观,那么它的优势
就是正的;反之,如果奖励低于预期,优势
就可能为负。
尽管初始时
与
完全一致(所以概率比
),PPO Loss 的计算主要依赖于优势
。因此,Loss 为:
如果
为正,则负号意味着 Loss 为负,反向传播时会鼓励模型增加生成这个 token 的概率;如果
为负,则模型会减少生成该 token 的概率。
这样,尽管新旧策略当前一致,优势
的非零值会通过梯度计算推动模型调整生成策略,从而在下一次生成时更倾向于产生正优势的 token。
PPO Loss 的核心目标:
不是直接对比新旧模型参数的差异,而是基于旧策略采样数据上每个 token 的优势来指导模型更新。
即使在第一次更新时:
由于优势
通常不为零,PPO Loss 会产生非零梯度,从而促使模型在更新过程中调整策略,使其生成结果更符合奖励信号。
这就是为什么即使在第一次更新前,新模型与旧模型完全一致,PPO Loss 也不会为零的原因。
DPO:看着棋谱学下棋
前面我们提到,
PPO
很像你在真实棋盘上有一位教练随时指导,边对弈边在真实环境中改进策略(在线学习);而
DPO
则更像你坐在家里研究一本棋谱(离线数据),通过已有的胜负对照来推断如何改进走法。
本节就来具体推导 DPO(Direct Preference Optimization)的数学原理,解释它在和
PPO
(或更一般的 RLHF 思路)对比时有何长处与不足。
在此之前,请先牢记下面 3 个关键的目标函数:
表示奖励模型(Reward Model),
是我们需要训练的对齐模型(策略),
则是参考模型(无论是 PPO 还是 DPO,都需要它来保证策略别跑得太偏)。这 3 个目标函数分别是:
让我们先从 PPO 的损失函数出发,对其进行数学变换。就像在真实棋盘上下棋时,教练(Reward Model)实时给你反馈、并用 KL 散度惩罚让你的策略别偏离参考模型太远。
其中,这个
就像一个归一化常数,保证
是一个真正的概率分布(概率和为 1)。
在上式中,分子代表了在给定某个输入对
时,模型获得奖励的期望值;而分母则汇总了在相同输入
下,所有可能输出
的奖励期望之和。这样的结构实际上实现了一个归一化操作,使得整个表达式的值被限制在
的区间内,从而满足我们构造概率分布的基本要求。
虽然我们并不清楚
的具体形式,但由于我们已经知道参考分布
的精确表达,我们可以利用这一信息:只需将输入
传入参考模型,然后对所有可能的
进行遍历(或者抽取足够多的
样本),便可以近似估计出