大模型入门-PPO 近端策略优化
2.5 PPO 近端策略优化
PPO 论文: https://arxiv.org/pdf/1707.06347
2.5.1 PPO 核心目标与四大模型
强化学习的目标是让模型可以自我迭代,我们对其最终的期望主要有两点 :
-
分布合理(防幻觉): 我们已经进行过有监督训练(SFT),因此希望模型的回答结果最好与之前 SFT 模型的回答分布相近 。否则,模型可能会“钻空子”,给出一个与有监督答案不符但奖励模型却给了高分的答案,这可以理解为“幻觉问题” 。
-
得分高: 希望模型的回答都是高分回答 。
为了达到以上要求,PPO 训练方法中一共涉及四个主要模型 :
Actor Model (演员模型): 这就是我们想要训练的目标语言模型 。
Critic Model (评论家模型): 它的作用是计算预期收益 。
Reward Model (奖励模型): 它的作用是计算实际收益 。
Reference Model (参考模型): 它的作用是给语言模型增加一些“约束”,防止语言模型训歪,使模型的回答结果最好与之前 SFT 模型的回答分布相近 。
在初始状态下,Actor 与 Reference 的初始化模型就是 SFT 模型,Reward 与 Critic 的初始化模型就是 Reward 模型 。在后续训练中,Actor 与 Critic 需要进行训练更新,而 Reward 与 Reference Model 的参数是冻结的 。
(大模型 PPO 强化学习整体流程,包含 Actor, Critic, Reward, Reference 模型的协同步骤)
2.5.2 PPO 训练的具体步骤
第一步:Actor 生成输出
给 Actor 模型输入问题 Prompt,Actor 模型会有两个输出 :
-
生成的回答
response。 -
生成每个字的概率分布(给出上一部分的文字,生成下一个字的概率),记为
old_log_prob,它是一个张量,长度与 response 长度一致 。
第二步:多模型评估
将问题与 Actor 生成的回答作为输入,分别喂给其他三个模型 :
输入给 Reference 模型: 得出在原先 SFT 模型下输出该句话的概率张量分布,记为 ref_log_prob,长度与 response 一致 。
输入给 Reward 模型: 得出模型的实际收益,输出为一个具体的分数 。
输入给 Critic 模型: 评论家会为 response 中的每个 token 计算一个预期收益,第 i i i 个预期收益记为 values[i] 。
第三步:计算优势与 Actor 损失
强化学习中的一个关键概念是优势 (Advantage),定义为“实际获得的收益超出预期的程度” 。PPO 计算优势的方法为:优势 = 实际收益 - 预期收益 。预期收益已由 Critic 计算得出,接下来计算实际收益 。
首先计算每个 token 的局部奖励 r e w a r d [ i ] reward[i] reward[i](结合了简化的 KL 散度) :
如果 i < N i<N i<N:
r e w a r d [ i ] = r e f _ l o g _ p r o b [ i ] − o l d _ l o g _ p r o b [ i ] reward[i]=ref\_log\_prob[i]-old\_log\_prob[i] reward[i]=ref_log_prob[i]−old_log_prob[i]
如果 i = N i=N i=N(最后一个 token):
r e w a r d [ N ] = r e f _ l o g _ p r o b [ N ] − o l d _ l o g _ p r o b [ N ] + s c o r e reward[N]=ref\_log\_prob[N]-old\_log\_prob[N]+score reward[N]=ref_log_prob[N]−old_log_prob[N]+score
原理解析:
ref_log_prob[i] 越高,说明输出越守规矩(贴合 SFT),应获得更高奖励 。
old_log_prob[i] 作为正则项,其越高时奖励反而更低,以此保证概率分布的多样性 。
- 结果正确性奖励(Reward 模型的输出 score)只在最后一个 token 上应用 。通俗来说,这是典型的“霸总逻辑”:除非你能拿到好结果,否则你就得给我守规矩 。
实际收益 (Return): 生成第 i i i 个 token 的实际收益是从生成该 token 开始到结束的所有奖励总和 :
r e t u r n [ i ] = r e w a r d [ i ] + . . . + r e w a r d [ N ] return[i]=reward[i]+...+reward[N] return[i]=reward[i]+...+reward[N]
强化优势动作:
a [ i ] = r e t u r n [ i ] − v a l u e s [ i ] a[i]=return[i]-values[i] a[i]=return[i]−values[i]
如果优势 a [ i ] a[i] a[i] 很高,说明应该增加生成该 token 的概率 。因此给 Actor 模型设计如下损失函数 :
a c t o r _ l o s s = − 1 N ∑ i = 1 N a [ i ] × p ( t o k e n [ i ] ∣ c o n t e x t ) actor\_loss=-\frac{1}{N}\sum_{i=1}^{N}a[i]\times p(token[i]|context) actor_loss=−N1i=1∑Na[i]×p(token[i]∣context)
(当优势>0时,优化器会增大概率减小 loss;当优势<0时,会减小概率减小 loss )
为了防止更新步子迈得太大,引入重要性采样比例 :
a c t o r _ l o s s = − 1 N ∑ i = 1 N a [ i ] × p ( t o k e n [ i ] ∣ c o n t e x t ) p o l d ( t o k e n [ i ] ∣ c o n t e x t ) actor\_loss=-\frac{1}{N}\sum_{i=1}^{N}a[i]\times\frac{p(token[i]|context)}{p_{old}(token[i]|context)} actor_loss=−N1i=1∑Na[i]×pold(token[i]∣context)p(token[i]∣context)
第四步:更新 Critic
因为 Critic 的预测不一定准,所以使用均方误差 (MSE) 来衡量预期收益和实际收益之间的差距,以此来升级 Critic :
c r i t i c _ l o s s = 1 2 N ∑ i = 1 N ( v a l u e s [ i ] − r e t u r n s [ i ] ) 2 critic\_loss=\frac{1}{2N}\sum_{i=1}^{N}(values[i]-returns[i])^{2} critic_loss=2N1i=1∑N(values[i]−returns[i])2
第五步:计算总体 Loss
最终优化时使用的 loss 是演员和评论家损失函数的加权和 :
L o s s = a c t o r _ l o s s + 0.1 × c r i t i c _ l o s s Loss=actor\_loss+0.1\times critic\_loss Loss=actor_loss+0.1×critic_loss
2.5.3 核心公式总结
PPO 中 Actor 的最终目标函数:
J P P O ( θ ) = E [ min ( r t ( θ ) A t , c l i p ( r t ( θ ) , 1 − ϵ , 1 + ϵ ) A t ) ] J_{PPO}(\theta)=\mathbb{E}[\min(r_{t}(\theta)A_{t},clip(r_{t}(\theta),1-\epsilon,1+\epsilon)A_{t})] JPPO(θ)=E[min(rt(θ)At,clip(rt(θ),1−ϵ,1+ϵ)At)]
r t ( θ ) r_{t}(\theta) rt(θ):新旧策略的动作概率比(重要性采样比率) 。
A t A_{t} At:优势函数 。
c l i p clip clip:用于剪裁,把优势控制在一个固定范围内,防止更新步幅过大 。
ϵ \epsilon ϵ:剪裁阈值(通常取 0.1 ~ 0.3),限制策略更新的幅度 。
理解与总结: 大模型在 PPO 训练中,通过 ref_log_prob - old_log_prob 纠正一本正经的胡说八道,保证了分布合理;同时通过奖励机制确保答案分值越高越好,完美达到了双重目的 。
2.5.4 附:PPO Loss 核心代码实现
以下是 PPO 训练中计算策略损失(Policy Loss)、价值损失(Value Loss)以及成对损失(PairWise Loss)的 PyTorch 核心代码片段:
import torch
import torch.nn as nn
import torch.functional as F
class PolicyLoss(nn.Module):
"""
PPO 算法中的策略损失计算
实现了 PPO 的剪辑损失 (clipped surrogate objective),用于限制策略更新的幅度
提高训练的稳定性。通过比较新策略与旧策略的比例,取两种可能损失的最小值。
"""
def __init__(self, clip_eps: float = 0.2) -> None:
"""
参数: clip_eps: 剪辑范围的 epsilon 值,控制策略更新的最大幅度,默认 0.2
"""
super().__init__()
self.clip_eps = clip_eps
def forward(
self,
log_probs: torch.Tensor, # 新策略下的动作对数概率
old_log_probs: torch.Tensor, # 旧策略下的动作对数概率
advantages: torch.Tensor, # 优势函数值
action_mask: torch.Tensor = None # 动作掩码
) -> torch.Tensor:
# 1. 计算新旧策略的概率比: exp(new_log_prob - old_log_prob)
ratio = (log_probs - old_log_probs).exp()
# 2. 第一种替代损失: 直接使用概率比乘以优势值
surr1 = ratio * advantages
# 3. 第二种替代损失: 将概率比限制在 [1-eps, 1+eps] 范围内
surr2 = ratio.clamp(1 - self.clip_eps, 1 + self.clip_eps) * advantages
# 4. PPO 剪辑损失,取最小值并取负 (最大化问题转为最小化)
loss = -torch.min(surr1, surr2)
# 应用掩码计算平均值
loss = masked_mean(loss, action_mask, dim=1).mean()
return loss
def masked_mean(tensor, mask, dim):
"""计算带掩码的张量平均值"""
if mask is None:
return tensor.mean(axis=dim)
return (tensor * mask).sum(axis=dim) / mask.sum(axis=dim)
class ValueLoss(nn.Module):
"""
PPO算法中的价值函数损失计算
用于训练价值网络(Critic)。支持对价值更新进行剪辑。
"""
def __init__(self, clip_eps: float = None) -> None:
super().__init__()
self.clip_eps = clip_eps
def forward(
self,
values: torch.Tensor, # 当前价值网络输出
old_values: torch.Tensor, # 旧价值网络输出
returns: torch.Tensor, # 实际的累积回报
action_mask: torch.Tensor = None
) -> torch.Tensor:
if self.clip_eps is not None:
# 计算剪辑后的价值
values_clipped = old_values + (values - old_values).clamp(-self.clip_eps, self.clip_eps)
surr1 = (values_clipped - returns) ** 2
surr2 = (values - returns) ** 2
loss = torch.max(surr1, surr2)
else:
# 不使用剪辑时,直接计算MSE损失
loss = (values - returns) ** 2
loss = masked_mean(loss, action_mask, dim=1).mean()
return 0.5 * loss
class PairWiseLoss(nn.Module):
"""
奖励模型(Reward Model)中的成对损失计算
使模型学会区分好坏响应。
"""
def forward(self, chosen_reward, reject_reward, margin=None):
if margin is not None:
# 带 margin 的损失计算: 鼓励 chosen_reward - reject_reward > margin
loss = -F.logsigmoid(chosen_reward - reject_reward - margin)
else:
# 不带 margin: 仅要求 chosen_reward > reject_reward
loss = -F.logsigmoid(chosen_reward - reject_reward)
return loss.mean()
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐


所有评论(0)