PPO流程的个人疑问集锦 Q & A
文中大部分为GPT生成,并由我个人理解后添加了注解。


1. PPO Loss 和 Actor Loss 是一个东西吗?

个人理解:
上图中:
- PPO-clip Loss指的是用了clip的Actor loss(含kl散度罚项),用来鼓励actor模型在不过度偏离ref模型以及old actor模型的前提下,对齐人类偏好;
- LM Loss指的是(交叉)熵损失函数,用来促进actor生成更多样的输出;
- MSE Loss指的是critic loss,用来促进critic的价值打分value更接近真实收益return(在不过度偏离old critic的前提下)。
但是实际上,一般:
我们用actor loss(已包含kl散度罚项)、critic loss、LM loss指代这三种loss
而ppo loss则是将这三个loss用超参数耦合起来
类似:
下面仅给出一般情况下的理解
一般来说:
Actor Loss
- 是指 策略网络(Actor) 的损失。
- 它的目标是:更新策略,使其获得更高的预期回报(expected return)。
PPO Loss
-
是Proximal Policy Optimization(PPO)算法设计的整体损失函数。
-
包括:
- Actor Loss(策略损失)
- Critic Loss(价值函数损失)
- (可选)Entropy Bonus(鼓励探索的项)
PPO 中的 Actor Loss 具体形式:
PPO 的 Actor Loss 有一个特殊设计,叫做 Clipped Objective,如下:
L^{\text{CLIP}}(\theta) = \mathbb{E}_t \left[ \min \left( r_t(\theta) \hat{A}_t,\ \text{clip}(r_t(\theta), 1 - \epsilon, 1 + \epsilon)\hat{A}_t \right) \right]
其中:
- :新旧策略的概率比。
- :优势函数(Advantage)。
- :裁剪参数,防止策略变化太大。
这个裁剪形式就是 PPO 特有的 Actor Loss。
总结关系如下表:
| Actor Loss | PPO Loss | |
|---|---|---|
| 指代 | 策略网络的损失 | PPO算法的总损失 |
| 是否包含Critic | ❌ | ✅(通常包括 critic 和 entropy) |
| 数学形式 | 常用 Clipped Objective | 通常是:Actor Loss + Critic Loss + Entropy Bonus |
| 是否特指 PPO | ❌(其他算法也有) | ✅(是PPO特有设计) |
所以:
- 在 PPO 中,Actor Loss 是 PPO Loss 的一部分;
- 但在说 “Actor Loss” 时,我们通常只指“优化策略的那一部分”;
- 而 “PPO Loss” 是整个训练目标,包括策略、价值、熵等多项指标的加权和。
2. ref模型接受的输入究竟是什么?
ppo中,actor基于prompt生成response,以及response所选出的各token id对应的概率,也就是logprobs,
我们将prompt + response拼成一个完整序列送给ref进行前向(注意ref模型只做一次前向,而不是生成!),
然后获取actor生成的response中的各token id所对应的ref给出的概率,作为ref_logprobs,
进而与logprobs做kl散度计算来约束actor的更新。
在 PPO 中,actor 的 response 会和 prompt 拼接成一个完整的序列,送入 reference model(也就是 pre‑trained LM 或 SFT model,简称 ref)进行一次前向传播,用来获取每个 token 的 log-probabilities。这些就是所谓的 ref_logprobs。
流程总结:
-
拼接序列
把 prompt(环境状态)和 actor 生成的 response(模型行为)拼接成一整段文本序列。 -
丢给 reference model 前向计算
用这个完整序列在 ref 模型上前向计算,从而获得 response 部分每个 token 的概率(用 log-prob 表示)。注意!
这里相当于用actor生成的response中的各token的token id,去索引ref做完前向之后对应位置上的token id所对应的概率!
-
记录 token log‑probabilities
得到的 log-probabilities 就是 ref_logprobs,对应 actor 输出的每个 token。 -
用于 KL 惩罚和计算 reward
PPO 的 reward 包括两个部分:一是 reward model 打分,二是 KL penalty —— 即 actor logprobs 与 ref logprobs 差值,防止偏离太远。
问题回答:
- ref接收的输入就是prompt + actor生成的response;
- ref_logprobs 就是 actor的response中的每个 token 在 reference 模型(freeze 后)前向计算得到的 log-prob。
- 这些值同时用于构造 KL penalty,也用在 PPO 的 reward 计算中。
3.PPO整个流程中,actor和critic以及old actor和old critic的更新顺序
个人理解:
在更小一层的PPO epochs循环中,我们更新了new Actor和new Critic,
然后当这个PPO epochs结束之后,我们立刻将new Actor的参数赋给了old Actor, 然后将new Critic的参数赋给了old Critic
(虽然实际上我们不会保存old actor和old critic的参数,因为太占空间了,事实上我们保存的是old模型的经验的快照,在ppo epochs中我们直接更新actor和critic!)
(此外critic其实没有新旧之说,我们仅保存大循环开始时critic对response的打分序列values,用这个values来clip后续critic的更新)
(具体见后续问题)
下面用清晰的步骤和小粒度对齐,说明 PPO 中 newActor、newCritic 和 oldActor(旧策略)之间的更新顺序与流程:
小循环 & 大循环中的更新逻辑:
-
大循环:多次 iterations(或 epochs)
每个 iteration 包括采样、更新等完整步骤。
(快照old模型)
-
小循环(单次 iteration 里的 PPO epochs)
使用同一批体验数据,进行多轮梯度更新。
(更新new模型,在ppo epochs全部跑完后将new模型赋给old模型)
每次 iteration 的完整流程细节:
-
采样阶段(Sampling)
使用当前策略 actor θₖ 与环境交互,收集 trajectories,并记录动作 log prob。这 log prob 即代表 “旧策略” 在采样时做出的判断。 -
计算 Advantage & Return
利用当前 critic φₖ (可以理解为old critic,实际上就是大循环开始时的critic模型)估计值value,以及reward的打分(含kl散度),结合奖励信号计算 TD‑error 或 GAE→得出 Aₜ, Rₜ。
-
快照旧策略
将当前 actor θₖ 复制一份为 θ_old,此时就固定为旧策略,用于后续多个 PPO epoch 中计算比值 rₜ(medium.com)。 -
训练小循环(多个 PPO epochs)
对同一批数据,执行 K 个 epoch 的小批训练(batch):-
Actor 更新(newActor θ):计算 clipped surrogate loss,用 θ_old 的 log‑prob 比较当前策略,进行梯度上升。
-
Critic 更新(newCritic φ):用均方误差最小化 V_φ(sₜ) 与 Return 的差距,同步调整参数。
-
顺序:
同一个 batch 上,多数实现是 先 actor,再 critic 或者 分开独立 optimize,
但都是 多轮使用相同 θ_old 的旧策略。
-
-
结束本次 iteration
当所有 epoch 完成:- newActor 参数成为下一次 sampling 用的 actor θₖ₊₁;
- newCritic 参数更新日常评估值函数;
- θ_old 将在下次 iteration 的小循环中再次快照。
总结一句话:
-
在一个 iteration 内:
小循环中:不断更新 newActor 和 newCritic,但“旧策略 θ_old”保持不变;
小循环结束时:整个 newActor(和 newCritic)替代旧参数,成为下一轮 iteration 的 actor 和 critic。
回答问题:
“也就是说在更小一层的循环中,我们更新了newActor和newCritic, 然后当这个PPO epochs结束之后,我们立刻将newActor赋给了oldActor, 然后将newCritic赋给了oldCritic, 是这样吗?”
完全正确:
- 小循环(PPO epochs)中,oldActor 一直是之前快照的版本。
- Epoch 结束后,newActor(更新完的 actor)就会替换 oldActor 为下一次 iteration 的快照。
- Critic 没有“旧策略”之说,直接用 newCritic 更新。
4. old actor和old critic的权重需要保存吗?
个人理解:
实际上我们并没有保存Old Actor和Old Critic的权重,
而是我们将它们所生成的一些旧经验用快照冻结, 然后保存到了一个buffer里面,
然后我们在ppo Epochs里面持续更新原来的Actor和Critic。
许多 PPO 的实现并不保存完整的 oldActor、oldCritic 网络参数,
而是通过 将旧策略生成的经验(如 log π_old)冻结并缓存到 buffer,
在多个 epoch 内反复使用这些旧经验来训练,不断更新原来的 Actor 和 Critic。
下面详细说明这种关键机制。
PPO 中对“旧策略”处理的两种方式:
-
最直观的做法:保存完整网络快照
- 在采样结束后,用 θ_old = θ_current 来保存旧策略参数。
- 多 epoch 中,仍旧使用这个完整快照网络来重新计算旧策略 log‑prob。
- 优点:回放使用的是实际的旧网络,确保一致性;
- 缺点:存储网络快照复杂,计算开销更大。
-
常见高效实现:只缓存 旧策略的 log‑prob
-
在采样时,actor 输出时同时保存这个动作在当时策略下的 log π_old 到 buffer。
-
在多个 PPO epoch 中,无需再调用一个旧策略网络,
只需 从 buffer 中读取旧的 log π_old,
并与当前策略生成的新 log π_new 作对比,计算比例:
r _ t =exp(log π _{ new} (a _ t ∣s _ t )−log π _{ old} )
-
这就等效于“冻结旧策略”的效果,但效率更高。
reddit 上也指出:
“after collecting data using the old policy, we do k epochs update, so the policy in each epoch is the new policy compared to the old log probabilities saved in Replay Buffer”(reddit.com)
即:用 buffer 冻结旧 log‑prob,更新时直接对比,不需要保存旧模型。
-
总结与对照表:
| 方法 | 是否存网络快照 | 使用旧策略方式 | 优点 | 缺点 |
|---|---|---|---|---|
| 快照网络 | ✅ | 每 epoch 调用旧模型 | 精准、逻辑清晰 | 存储和计算量大 |
| 缓存 log‑prob | ❌ | buffer 里读取旧 log‑prob | 高效、简洁,常用 | 不保留网络状态,只保留行为统计 |
回答问题:
“我们并没有保存 OldActor 和 OldCritic 的权重,而是将旧经验快照冻结到 buffer,然后持续更新 Actor 和 Critic是吗?”
完全正确,且是 PPO 常见且推荐的实现方式。
5. ref、old actor、old critic、reward(重要)、new actor、new critic获得的输入和输出
详细、清晰地给出ref、old actor、old critic、reward、new actor、new critic获得的输入和输出
(其中你需要明确给出reward的输出相当于给非结尾的token不附加reward模型打分,给response结尾附加reward模型打分,但是response每个token位置都有加入kl散度约束来作为该token的reward的一部分)
本问题的重点在于reward模型的输出究竟是一个序列还是一个标量)
个人理解:
reward模型接收prompt + response作为输入,
然后我们给response处的各token打分,
在这里我们要求reward模型给response非结尾处的token都打0分(无reward奖励),
只给response结尾处附上一个整体的reward打分。
实际上reward模型是可以做到逐token打分的,这里只是只取最后一个token生成的打分值(也就是对应整体的打分)而已!
这样,目前的rewards其实相当于一个[0, 0, ..., reward_score]的序列。
然后我们为rewards加上actor和ref模型之间的kl散度,
这样就得到了我们最终期望的rewards序列(也就是获得了response中每个token对应的reward打分)。
这样的设计公式如下:
"select * from blocks where id='20250624002126-0jv0w6s'"
目的是:
- 当 时,我们更加关心Actor是否有在Ref的约束下生产token
- 当 时,我们不仅关心Actor是否遵从了Ref的约束,也关心真正的即时收益
具体详见:(3)重新设计 R_{t}
以下是基于标准 RLHF + PPO 实现(以 TRL PPOTrainer 为例)的详细流程图,说明每个模型:reference model(ref)、old actor、old critic、reward model、new actor、new critic 在训练中的输入与输出,以及如何将 KL 散度约束 与 reward 模型评分 融入 token‑级别的 reward:
一、 采集阶段(Rollout):
| 模块 | 输入 | 输出 |
|---|---|---|
| old actor(即当前策略) | Prompt(x) | Response tokens y = (y₁…yᴺ),及其 log‑probs π_old(yₜ |
| reward model | Prompt+Response 完整序列 (x,y) | reward_score = r(x,y)(一个 scalar,仅在整个 response 结尾计算一次) |
- reward_score 只在 yᴺ(结尾 token)处被添加!!!
- 之前的每个 token t < N 暂无 reward 模型直接评分!!!
二、风控与评价阶段(Ref 前向 & Critic):
-
reference model(ref)
- 输入:相同的 (x, y)
- 输出:每个生成 token 的 log‑probs π_ref(yₜ|x, y_<t)
- 用于计算 KL penalty,防止策略偏离原始语言模型(newfacade.github.io, en.wikipedia.org)。
-
old critic(value/value head)
-
输入:prompt + response 序列(x, y)
-
输出:每个 time‑step 的估值 V_old(x, y_<t),用于后续优势估计。
此处下标小于t的解释详见:7. 为什么PPO 中,critic 的输入使用的是当前时刻之前的上下文 (x, y_{<t}),而不是整条 response (x, y_{≤N})?
-
三、构造 token‑级 rewards + advantage:(重要)
对每个 token t:
-
reward from reward model:
- 如果 t = N(response 结尾),r_t = reward_score。
- 否则,r_t = 0。
-
KL penalty(每个 token 都计算):
- klₜ = log π_old(yₜ) − log π_ref(yₜ)
综合 token reward:
随后使用 gamma 折扣累计至各步,得到 return-to-go R̂ₜ。
再计算优势:
Aₜ = R̂ₜ − V_old(x, y_<t)
四、PPO 更新阶段:
在一个批次内,使用下面步骤更新 actor 和 critic:
-
新 actor(new policy π_new)
-
输入:相同 prompt,生成整个 batch 的 y 并记录新 log‑probs π_new(yₜ)
-
使用 PPO-clip 目标函数更新:
其中 (en.wikipedia.org)。
-
-
新 critic(new value V_new)
- 输入:prompt+response
- 输出:预测 V_new(x, y_<t)
- 最小化目标:均方误差:
流程总结图:
prompt x
├─▶ old actor ─▶ response y, actor log‑probs
拼接完整序列 (x,y)
├─▶ reward model ─▶ reward_score at token N
├─▶ reference model ─▶ ref log‑probs for every token
每个 token 的生成上下文 (x,y_{<t})
└─▶ critic (old) ─▶ V_old per token
Compute per-token:
rₜ = (t=N ? reward_score : 0)
klₜ = log π_old(yₜ) − log π_ref(yₜ)
Rₜ = rₜ − β·klₜ
Compute advantages:
Aₜ = R̂ₜ − V_old
PPO update:
- new actor: optimize clipped policy loss using Aₜ
- new critic: regress V_new toward R̂ₜ
总结要点:
- Reference model:只做一次前向,输出 ref log‑probs(不会生成文本)。
- Reward model:只对整个 response 结尾打分。
- KL constraint:对每个 token 施加 log‑prob 差值惩罚。
- Critic:为每个 time‑step 提供 baseline 估值用于优势计算。
- Actor 更新:结合 token‑级 KL 惩罚和结尾的 reward 模型评分,通过 PPO clip 优化策略。
6. 给出清晰明确分层的ppo流程
以下是一个层级清晰、结构明确的 RLHF + PPO 流程,帮助梳理每个阶段各模型的输入输出及它们之间的关系:
第 1 层:数据采集(Rollout)
目的:使用旧策略生成回应并打标签。
-
输入:Prompt
-
模型:旧 actor()
- 功能:采样响应
- 输出:sequence of tokens ,及对应 log-probs
-
模型:奖励模型(Reward model)
- 输入:拼接完整序列
- 输出:一组标量 reward score ,仅在 结束时提供一次
第 2 层:评价与打分
目的:为每个 token 打分 —— 引入 KL 惩罚及计算 critic 的 baseline。
-
模型:Reference model(冻结 LM)
- 输入:完整序列
- 输出:每个生成 token 的 log-probs
-
模型:旧 critic(Value model)
-
输入:每个 token 的生成上下文
此处下标小于t的解释详见:7. 为什么PPO 中,critic 的输入使用的是当前时刻之前的上下文 (x, y_{<t}),而不是整条 response (x, y_{≤N})?
-
输出:每个 time‑step 的状态价值估计
-
第 3 层:构造 Token‑级 Reward & Advantage
目的:根据模型输出构建 token‑级 reward,并计算 advantage。
-
KL 惩罚(每个 token):
-
Reward 分配:
- 对所有 ,
- 对结尾 token ,
- 对所有 ,
-
累积折扣收益(Return-to-go):
-
优势估计(Aₜ):
第 4 层:PPO 更新(Actor 和 Critic)
目的:依据 PPO 的目标函数更新策略与价值网络。
-
新 actor()
-
输入:原 batch 的 prompt 序列
-
输出:新 log-probs
-
损失函数(PPO-Clip + KL penalty):
L_{\text{actor}} = \mathbb{E}_t \left[ \min \Big( r_t A_t,\; \text{clip}(r_t,1-\epsilon,1+\epsilon) A_t \Big) \right] - \beta\,\mathbb{E}_t[\text{kl}_t]
其中
-
-
新 critic()
- 输入:prompt + past tokens
- 输出:每 step 的新值估计
- 损失:
小结:
- 层级划分:Rollout → 评价打分 → 构造 reward → PPO 更新;
- Token 级 Reward:每个位置都有 KL 惩罚;结尾 token 额外加 reward model 评分;
- Actor & Critic:并行训练,actor 用 PPO clip + KL penalty,critic 用 MSE fitting return。
7. 为什么PPO 中,critic 的输入使用的是当前时刻之前的上下文 (x, y_{<t}),而不是整条 response (x, y_{≤N})?

个人理解:
如图中所示也可以看出,其实左边的value模型接收的输入是t时刻的状态,但是实际上一共有1~T个时刻!
所以我们可以理解为,这样写是要求value模型必须是一个causal因果模型,
value只能依赖当前状态(prompt + 已生成的可见的前文)去给当前状态打分,而不能看到未来的信息!因为它的任务是预估未来的信息。
但是在实际操作中,我们已经获得了prompt + actor给出的完整response,
因此我们在操作上,就是将整个序列塞给value作为输入!
不过我们又做了一个causal mask + teacher forcing的事情来让value打分。
其中:
- causal mask是指,对于T长度的response,我们逐token打分,且当前token只能看到之前的tokens
- teacher forcing是指,对于T长度的response,我们的输出不会影响输入(也就是说不是自回归的),我们只是一个一个token去给response中的所有token位置(状态)打分!从而获得一个values序列。
在 PPO 中,critic 的输入使用的是当前时刻之前的上下文 (x, y_<t),而不是整条 response (x, y_≤N),原因如下:
为什么是 y_<t 而不是 y_≤N:
- 模拟马尔可夫状态(Markov State)
在 RL 中,每个 time-step 的状态表示环境当前状况,即当前所见的历史信息。在文本生成中,step 的状态是 prompt 和已经生成的 tokens ,即 (x, y_<t)。 - 时间一致性
critic 的目标是预测从这个状态开始,到未来所有奖励的期望值 。如果 critic 知道了后续 token(甚至结尾 token),它就“偷看未来”了,估值会过高,违反 causal consistency。 - 符合 Advantage/Gare 等估计机制
critic 项用来计算优势 。这里的 应严格基于 当前时刻可见的上下文,这样 才是合理的差值(ar5iv.labs.arxiv.org)。
总结:
- 状态设计:每个 token 对应一个状态,stateₜ = (prompt, 已生成 tokens)
- critic 输入:
- critic 图片:是未来回报的估计,不包含未来信息,保证模型的泛化能力和训练稳定性
批量对 prompt+付出的每个位置 y_<t 前缀做前向计算 critic,而不是用完整 response,才能保证估值的真实性和 RL 的因果正确性。
8. 在ppo实际操作中如何实现critic 的输入仅使用当前时刻之前的上下文 (x, y_{<t})?
再po一次第七个问题的个人理解:
个人理解:
如图中所示也可以看出,其实左边的value模型接收的输入是t时刻的状态,但是实际上一共有1~T个时刻!
所以我们可以理解为,这样写是要求value模型必须是一个causal因果模型,
value只能依赖当前状态(prompt + 已生成的可见的前文)去给当前状态打分,而不能看到未来的信息!因为它的任务是预估未来的信息。
但是在实际操作中,我们已经获得了prompt + actor给出的完整response,
因此我们在操作上,就是将整个序列塞给value作为输入!
不过我们又做了一个causal mask + teacher forcing的事情来让value打分。
其中:
- causal mask是指,对于T长度的response,我们逐token打分,且当前token只能看到之前的tokens
- teacher forcing是指,对于T长度的response,我们的输出不会影响输入(也就是说不是自回归的),我们只是一个一个token去给response中的所有token位置(状态)打分!从而获得一个values序列。
问题:
但是我们不是一次性生成了所有prompt + response的吗?所以具体critic只用y_<t打分是不是只需要用causal mask + teacher forcing即可?
是的,你的理解完全正确 ——在 PPO 中,新旧 critic 的输入方式可以通过 teacher forcing + causal mask 实现,只看历史 tokens 而不泄露未来信息。
为什么只用 而不包括 ?
- 因果一致性(Causality):critic 的目标是预测当前状态下(即 step t 时,已生成的上下文 = prompt + )的价值 ,不应看到未来的对话内容。
- 避免“偷看”信息:如果 critic 知道后续 tokens,它就会过度拟合,无法正确估值,会导致优势计算不准确。
实现方式:只需一次前向,使用 teacher forcing + causal mask
-
因为你在 rollout 阶段已经生成了整个响应 sequence ,所以 critic 只要:
- 以输入序列 [x, y_1, …, y_{t-1}, y_t] 前向,并
- 用 causal mask 来确保当前 step 的预测不看未来 tokens,就等于在每个 t 步输入了 (x, y_{<t})。
-
这就是典型的 teacher forcing。不需要手动循环 t,只需一个 batch forward + causal mask,即可获得所有 time-step 的 value 输出。
小结:
- 输入:完整序列 [x, y_1…y_N]
- 注意力机制:decoder 使用 causal mask,确保 step t 只能看到 <= t 步输入
- 输出:critic 在每个 position t 输出 ,即合理的价值估计
TRL/PPOTrainer 就是这样在一次 batch 数据前向中完成多时刻的 critic 估值的,无需显式循环,非常高效。
更多推荐




所有评论(0)