策略优化的理论基础与算法实现
3.2 REINFORCE: 最早的策略梯度算法
在完成策略梯度定理的推导后,我们获得了梯度的理论形式:
然而,这个期望本身仍然无法直接计算。我们面临的根本问题是:轨迹空间是高维甚至连续无限的,无法枚举所有可能的 ((s_0, a_0, s_1, a_1, dots)) 组合。策略优化的实践核心在于用有限采样近似期望:与环境交互收集 (N) 条轨迹 ({tau_1, dots, tau_N}),然后用经验平均估计梯度:
这就是 REINFORCE 算法(Williams, 1992)的核心思想。其训练流程为:
- 用当前策略 (pi_theta) 采样 (N) 条完整轨迹
- 对每条轨迹计算累积回报 (G_t = sum_{t'=t}^T r_{t'})(从时刻 (t) 到终止)
- 可选地引入固定 baseline (b(s_t))(如所有轨迹的平均回报)
- 计算梯度并更新参数:(theta leftarrow theta + alpha hat{g})
采样带来的根本挑战:方差问题
我们真正想要的是策略的平均性能,但只能通过有限采样来估计。这引入了两个核心要求:
- 无偏性(unbiased):采样梯度的期望应等于真实梯度
- 低方差(low variance):不同采样批次的梯度应相近
REINFORCE 满足无偏性,但存在高方差问题。考虑一个简单例子:
示例:训练语言模型回答医疗问题。
- Prompt: "如何缓解头痛?"
- Response 1(轨迹1): "多喝水,适当休息,必要时服用布洛芬。" → 奖励 (R_1 = 0.9)
- Response 2(轨迹2): "头痛可能由多种原因引起..." (啰嗦但正确) → 奖励 (R_2 = 0.6)
- Response 3(轨迹3): "建议立即手术治疗。" (错误) → 奖励 (R_3 = -0.8)
即使这三条回复来自同一个策略,它们的回报差异巨大((0.9, 0.6, -0.8))。用这些样本计算的梯度会剧烈波动,导致:
- 需要大量轨迹(如 (N=1000))才能得到稳定估计
- 训练过程缓慢且不稳定
- 对于长对话(如 (T=100) 轮),方差会指数级增长
关键疑问:每次更新参数后策略就变了,那我是只用一条轨迹就更新吗?
回答:不是。REINFORCE 的标准做法是:
- 用当前策略 (pi_theta) 采样 (N) 条轨迹(如 (N=64))
- 用这 (N) 条轨迹的平均梯度更新参数一次
- 更新后策略变为 (pi_{theta'}),之前的 (N) 条轨迹全部作废
- 重新用 (pi_{theta'}) 采样新的 (N) 条轨迹,重复上述过程
这就是 On-Policy 的含义:数据必须来自当前策略,每次更新后旧数据失效,导致样本效率极低。
3.3 Actor-Critic
REINFORCE 的高方差源于用 Monte Carlo 回报 (G_t)(需要完整轨迹)。如果能用一个学习出来的函数估计未来回报,就可以:
- 降低方差(函数估计比单次采样稳定)
- 支持单步更新(不需要等轨迹结束)
这就是 Actor-Critic 框架的核心思想:引入 Critic 网络 (V_phi(s)) 估计状态价值,用它构造低方差的优势函数。
双网络架构
系统维护两个神经网络:
- Actor (pi_theta(a|s)):策略网络,负责生成动作
- Critic (V_phi(s)):价值网络,评估状态的好坏
训练目标:
-
Critic 的更新:学习预测真实回报
[mathcal{L}_{text{critic}} = mathbb{E}left[(V_phi(s_t) - G_t)^2right] ]其中 (G_t) 是实际观察到的累积回报(监督信号)。
-
Actor 的更新:用 Critic 估计的优势调整策略
[mathcal{L}_{text{actor}} = -mathbb{E}left[log pi_theta(a_t|s_t) cdot A_tright] ]其中优势函数 (A_t = G_t - V_phi(s_t)) 衡量动作相对于平均水平的好坏。
关键实现细节:计算优势时必须阻断梯度:
advantage = reward - value.detach() # ✅ 阻断梯度回传
这确保 Actor 的更新不会干扰 Critic 的学习目标。
单步更新的进阶:TD 误差
在 Actor-Critic (AC) 框架中,我们可以使用 TD (Temporal Difference) 误差 来替代传统的 Monte Carlo 回报,从而实现单步更新。
TD 优势的定义如下:
与 Monte Carlo 方法对比:
-
Monte Carlo 优势 ((A_t^{MC})):
- 公式:(A_t^{MC} = G_t - V(s_t))
- 特点:需要运行完整个轨迹才能计算,是无偏估计,但通常具有很高的方差。
-
TD 优势 ((A_t^{TD})):
- 公式:(A_t^{TD} = delta_t)
- 特点:只需要一步(single-step transition)即可计算,方差较低,但是一个有偏估计(其准确性依赖于价值函数 (V) 的估计精度)。
3.4 GAE (Generalized Advantage Estimation) 的推导
1. 真实的优势函数
我们首先定义一个理论上“真实”的优势函数,它使用实际的未来回报 (G_t):
我们的目标是使用一系列的 TD 误差 (delta) 来构造一个对这个“真优势”的良好估计。
2. 基于 Bellman 方程的展开
根据 Bellman 递推公式,任意时刻的回报 (G_t) 可以展开为:
将其代入真实优势的定义中:
为了引入 TD 误差 (delta_t),我们在上式中同时加上和减去 (gamma V(s_{t+1})):
观察上式,我们可以发现:
- 第一个方括号内的部分正好是 TD 误差 (delta_t)。
- 第二个方括号内的部分是下一时刻的真实优势 (A_{t+1}^{text{true}})。
于是,我们得到了一个关于真实优势的递归关系:
3. 递归展开与关键结论
将上述递归关系不断展开,可以得到:
关键结论:真实的优势函数,等于所有未来 TD 误差的折扣加权和。
这个结论非常直观:
- (delta_t) 代表当前这一步决策带来的“惊喜”或“估计误差”。
- (delta_{t+1}, delta_{t+2}, dots) 代表未来每一步的误差。
- 折扣因子 (gamma) 确保了越遥远的未来,其误差对当前优势的影响越小。
GAE 的核心思想:偏差-方差的权衡
问题与动机
虽然上述展开式在理论上很完美,但在实践中存在两个问题:
- 依赖完整轨迹:它依然需要未来所有的 (delta) 值,这意味着必须等到整个回合(episode)结束后才能计算,这本质上是 Monte Carlo 风格的估计,方差很大。
- 误差累积:我们不希望使用过长的序列,因为未来的不确定性高,价值函数的估计误差会不断累积。
我们需要在“充分利用未来信息”和“抑制噪声(降低方差)”之间找到一个平衡点。
引入 (lambda):偏差-方差的平衡因子
GAE 的核心思想是引入一个衰减系数 (lambda) (通常取值在 0.9 到 0.99 之间),用它来控制未来 TD 误差的权重。
GAE 的定义:
- (gamma):环境的奖励折扣因子,反映了任务本身对未来的重视程度。
- (lambda):优势函数的折扣因子,是我们用来控制偏差-方差权衡的人为超参数。
- (delta):每一步的 TD 误差。
理解 (lambda) 的作用
-
当 (lambda = 0) 时:
(A_t = delta_t)
这等价于传统的 TD(0) 误差,只考虑一步信息。这种方法偏差最大,但方差最小。 -
当 (lambda = 1) 时:
(A_t = sum_{l=0}^{infty} gamma^l delta_{t+l} = G_t - V(s_t))
这恢复了原始的展开式,等价于 Monte Carlo 方法。这种方法无偏,但方差最大。 -
当 (lambda in (0,1)) 时:
GAE 在 TD 和 Monte Carlo 之间进行插值。未来的 (delta) 权重会以 ((gammalambda)) 的速率衰减,实现了在“看得多远”与“抑制噪声”之间的平滑过渡。
GAE 的计算与实现
上述求和公式可以转化为一个高效的反向递推形式,非常适合在代码中实现。
GAE 递推公式:
这个计算过程类似于循环神经网络(RNN)中的反向传播,我们从轨迹的末端开始,反向遍历计算每一时刻的优势值。
伪代码示例:
advantages = torch.zeros_like(rewards) gae = 0 # 从后往前遍历时间步 for t in reversed(range(T)): # 1. 计算当前步的 TD 误差 delta delta = rewards[t] + gamma * values[t+1] - values[t] # 2. 使用递推公式计算 gae gae = delta + gamma * lam * gae # 3. 存储当前步的优势值 advantages[t] = gae
注意:
- 计算必须反向遍历时间,因为 (A_t) 依赖于未来的 (A_{t+1})。
values[t+1]是 Critic 网络对下一状态的价值预测。- 这个高效的计算方法是 PPO、A2C、A3C 等现代强化学习算法的标准组成部分。
GAE 与 n-step TD 的关系
GAE 还可以被看作是所有 n-step TD 优势估计 的指数加权平均:
其中,n-step 优势 (A_t^{(n)}) 的定义为:
总结来说:
- (lambda) 决定了我们将多少不同长度(n-step)的 TD 估计综合在一起。
- 较小的 (lambda) 更侧重于短期的、偏差较大的估计。
- 较大的 (lambda) 更侧重于长期的、方差较大的估计。
- 在实践中,(lambda=0.95) 通常是一个很好的经验默认值。
3.5 On-Policy 的困境与重要性采样
样本效率的致命弱点
前述所有算法(REINFORCE, AC, A2C/A3C)都是 On-Policy:梯度计算要求数据来自当前策略 (pi_theta)。这导致:
- 每次更新后,(pi_theta) 改变,旧数据立即失效
- 对于 LLM,生成一次回复需要数秒,但只能用一次就丢弃
- 训练 100 万步需要采样 100 万条新数据
量化对比(以 Qwen-7B 为例):
| 方法 | 单次采样耗时 | 数据复用 | 训练 1000 步总耗时 |
|---|---|---|---|
| On-Policy | 3 秒 | 1 次 | 3000 秒 |
| Off-Policy(PPO) | 3 秒 | 4 次 | 750 秒 |
重要性采样:Off-Policy 的数学工具
核心问题:能否用旧策略 (pi_{text{old}}) 的数据训练新策略 (pi_theta)?
数学原理(重要性采样定理):对于任意函数 (f(x)),
证明(简单积分变换):
应用到策略梯度:
原目标是 (mathbb{E}_{a sim pi_theta}[nabla log pi_theta cdot A]),但数据来自 (pi_{text{old}}),引入比率修正:
进一步简化(利用 (nabla log pi = pi^{-1} nabla pi)),可将目标函数写为:
医疗问答示例:
- 旧策略生成:"多喝水,休息"(概率 (pi_{text{old}} = 0.3))
- 新策略评估该回复:(pi_theta = 0.5)(更倾向此回答)
- 优势 (A = 0.8)(好回答)
- 修正后的梯度贡献:(frac{0.5}{0.3} times 0.8 = 1.33)
关键挑战:如果比率 (r = frac{pi_theta}{pi_{text{old}}}) 过大(如 10),说明新旧策略差异巨大,重要性采样失效,梯度估计方差爆炸。需要约束策略更新幅度。
3.6 TRPO: 信赖域约束下的策略优化
优化目标的理论保证
TRPO(Schulman et al., 2015)的核心思想:在限制策略变化的前提下最大化性能提升。
优化问题:
KL 散度约束衡量两个分布的差异:
直观理解:
- 目标函数:最大化性能(用旧数据评估新策略)
- 约束条件:KL 散度 (leq delta)(如 0.01),确保新策略不偏离太远
医疗问答示例:
- 旧策略分布:P("多喝水")=0.3, P("休息")=0.4, P("吃药")=0.3
- 新策略分布:P("多喝水")=0.5, P("休息")=0.35, P("吃药")=0.15
计算 KL 散度:
如果 (delta=0.05),则该更新违反约束,需要缩小更新步长。
实现方法:二阶优化
TRPO 用共轭梯度法求解带约束的优化问题,需要计算 Hessian 矩阵(目标函数的二阶导数)。虽然理论保证强(单调改进),但计算复杂度高,实现困难,调参敏感。
3.7 PPO
PPO(Schulman et al., 2017)用一阶优化 + 巧妙的目标函数设计达到 TRPO 的效果,成为深度 RL 和 RLHF 的标准算法。
3.7.1 PPO-Clip: 用裁剪替代 KL 约束
核心思想:不显式约束 KL 散度,而是直接限制比率 (r_t = frac{pi_theta(a|s)}{pi_{text{old}}(a|s)}) 的变化范围。
目标函数:
其中 (text{clip}(r, 1-epsilon, 1+epsilon)) 将 (r) 限制在 ([1-epsilon, 1+epsilon])(通常 (epsilon=0.2))。
逐项分析:
情况 1: 优势 (A_t > 0)(好动作,希望增加概率)
- 如果 (r_t < 1+epsilon):正常梯度,继续增加 (pi_theta(a|s))
- 如果 (r_t > 1+epsilon):被裁剪为 (1+epsilon),停止增加(防止过度优化)
情况 2: 优势 (A_t < 0)(坏动作,希望减少概率)
- 如果 (r_t > 1-epsilon):正常梯度,继续减少 (pi_theta(a|s))
- 如果 (r_t < 1-epsilon):被裁剪为 (1-epsilon),停止减少(防止过度惩罚)
医疗问答示例(具体计算):
- Prompt: "如何缓解头痛?"
- Response: "多喝水,适当休息"
- 旧策略: (pi_{text{old}}(response|prompt) = 0.01)(log prob = -4.6)
- 新策略: (pi_{theta}(response|prompt) = 0.03)(log prob = -3.5)
- 优势: (A = 0.8)(好回答)
- 比率: (r = frac{0.03}{0.01} = 3.0)
PPO 处理(设 (epsilon=0.2)):
原始项: r * A = 3.0 * 0.8 = 2.4 裁剪项: clip(3.0, 0.8, 1.2) * A = 1.2 * 0.8 = 0.96 最终: min(2.4, 0.96) = 0.96 ← 被裁剪!
解读:虽然新策略概率增加了 3 倍,但 PPO 只允许增加到 1.2 倍的幅度,防止策略突变。
3.7.2 PPO-KL: 自适应惩罚
另一种变体直接在目标中加入 KL 惩罚:
自适应 (beta):
- 如果 (text{KL} > 1.5 times text{target}):增大 (beta)(加强惩罚)
- 如果 (text{KL} < 0.5 times text{target}):减小 (beta)(放松约束)
实践中 PPO-Clip 更常用,因为无需调节 (beta)。
3.7.3 PPO-Clip 完整训练流程
关键特性:数据复用 (K) 次((K=4 sim 10))
for iteration in range(总迭代次数): # 1. 采样阶段(执行 1 次) 用当前策略 π_θ 采样 N 条轨迹 记录 old_log_probs = log π_θ(a|s) # 保存! # 2. 计算优势(用 GAE) 用 Critic 估计 V(s) 计算 advantages = GAE(rewards, values) # 3. 多轮 mini-batch 更新(数据复用 K 次) for epoch in range(K): # K=4 for batch in minibatch(trajectories): # 重新计算新策略概率 new_log_probs = log π_θ(a|s) # 策略已更新! # 计算比率 ratio = exp(new_log_probs - old_log_probs) # PPO-Clip loss loss_clip = -min(ratio * A, clip(ratio, 1-ε, 1+ε) * A) # 价值函数 loss loss_vf = (V(s) - returns)² # 总损失 loss = loss_clip + c_vf * loss_vf # 梯度更新 optimizer.step()
关键点:
old_log_probs在 (K) 轮更新中保持不变(来自采样时的策略)new_log_probs每次都重新计算(因为参数在变)- 数据复用 4 次后,重新采样新数据
参加参数设置

