策略梯度 REINFORCE 本节摘要:别再估价值了。直接参数化策略,算期望回报的梯度,往上爬。Williams 在 1992 年用一个定理写完了它: 。本节讲透这个公式的来龙去脉——对数导数技巧(log-derivative trick)怎么把「梯度的期望」变成「可采样的估计」;softmax 策略下 这个该刻进肌肉记忆的形式;以及为什么 vanilla REINFORCE 方差大得离谱,需要基线减法与未来回报(reward-to-go)两个标准修复。你会在 4×4 GridWorld 上亲手实现它,看到加一个 running-mean 基线就把收敛从数千回合压到 500。这是 PPO、GRPO 与所有 LLM RL 循环的直系祖先。
本节摘要:别再估价值了。直接参数化策略,算期望回报的梯度,往上爬。Williams 在 1992 年用一个定理写完了它:
∇J(θ) = E_π[ G · ∇_θ log π_θ(a | s) ]。本节讲透这个公式的来龙去脉——对数导数技巧(log-derivative trick)怎么把「梯度的期望」变成「可采样的估计」;softmax 策略下∇ log π(a|s) = e_a - π(·|s)这个该刻进肌肉记忆的形式;以及为什么 vanilla REINFORCE 方差大得离谱,需要基线减法与未来回报(reward-to-go)两个标准修复。你会在 4×4 GridWorld 上亲手实现它,看到加一个 running-mean 基线就把收敛从数千回合压到 ~500。这是 PPO、GRPO 与所有 LLM RL 循环的直系祖先。
对应原课程:Phase 9 · Lesson 06 ·
policy-gradients-reinforce(原英文phases/09-reinforcement-learning/06-policy-gradients-reinforce/docs/en.md)。
阅读完本节,你应当能够:
∇J(θ) = E[ Σ_t G_t · ∇ log π_θ(a_t|s_t) ],并用对数导数技巧推出它。∇ log π(a|s) = e_a - π(·|s) 的形式,能手算反向传播。G_t - b(s_t) 为何无偏却能大幅降方差,并选对基线(running mean → 学到的 V̂)。β · H(π)。Q 学习与 DQN 参数化的是价值函数,靠 argmax Q 选动作。这对离散动作、离散状态没问题。但当动作是连续的(对 10 维力矩取 argmax?)或你需要随机策略时(argmax 天生确定),它就崩了。
策略梯度改而参数化策略。π_θ(a | s) 是一个输出动作分布的神经网络。从里头采样来动作;算期望回报对 θ 的梯度;往上走一步。没有 argmax,没有贝尔曼递归,只有对 J(θ) = E_{π_θ}[G] 做梯度上升。
REINFORCE 定理(Williams 1992)告诉你这个梯度是可算的:∇J(θ) = E_π[ G · ∇_θ log π_θ(a | s) ]。跑一回合,算回报,每一步乘上 ∇ log π_θ(a | s),求平均,梯度上升。完。
2026 年每一个 LLM-RL 算法——PPO、DPO、GRPO——都是 REINFORCE 的精炼。把它烂熟于心,是本章剩余部分、以及 RLHF 实现(第 09 节)、DPO 的前提。
对任何由 θ 参数化的策略 π_θ:
∇J(θ) = E_{τ ~ π_θ}[ Σ_{t=0}^{T} G_t · ∇_θ log π_θ(a_t | s_t) ]
其中 G_t = Σ_{k=t}^{T} γ^{k-t} r_{k+1} 是从第 t 步起的折扣回报。期望是对从 π_θ 采样的完整轨迹 τ 求的。
对 J(θ) = Σ_τ P(τ; θ) G(τ) 在期望号下求导。用 ∇P(τ; θ) = P(τ; θ) ∇ log P(τ; θ)(对数导数技巧)。把 log P(τ; θ) = Σ log π_θ(a_t | s_t) + 不依赖 θ 的环境项。环境项消失。两行代数给出定理。
vanilla REINFORCE 方差凶残——回报有噪、∇ log π 有噪、两者乘积非常噪。两个标准修复:
G_t 换成 G_t - b(s_t),只要 b(s_t) 不依赖 a_t。无偏是因为 E[b(s_t) · ∇ log π(a_t | s_t)] = 0。典型选择:b(s_t) = V̂(s_t) 由一个 critic 学出来 → Actor-Critic(第 07 节)。Σ_t G_t · ∇ log π_θ(a_t | s_t) 换成 Σ_t G_t^{from t} · ∇ log π_θ(a_t | s_t)。对某个动作只有未来回报重要——过去的奖励只贡献零均值噪声。合体后得到:
∇J ≈ (1/N) Σ_{i=1}^{N} Σ_{t=0}^{T_i} [ G_t^{(i)} - V̂(s_t^{(i)}) ] · ∇_θ log π_θ(a_t^{(i)} | s_t^{(i)})
这就是带基线的 REINFORCE——A2C(第 07 节)与 PPO(第 08 节)的直系祖先。
Softmax 策略(离散动作的标准选择):
π_θ(a | s) = exp(f_θ(s, a)) / Σ_{a'} exp(f_θ(s, a'))
其中 f_θ 是任何能给每个动作打分的神经网络。梯度形式很干净:
∇_θ log π_θ(a | s) = ∇_θ f_θ(s, a) - Σ_{a'} π_θ(a' | s) ∇_θ f_θ(s, a')
即「所采取动作的分数」减去「它在策略下的期望值」。
高斯策略(连续动作):π_θ(a | s) = N(μ_θ(s), σ_θ(s))。∇ log N(a; μ, σ) 有闭式。第 07 节的 SAC 只需要这个。
💡 在 2026 年的训练脚本里看到
loss = -advantage * log_prob,那就是带基线的 REINFORCE。整篇论文(DPO、GRPO、RLOO)都是在这行之上的方差削减技巧。把它认出来,你就能看懂大半 LLM RL 文献。
def policy_logits(theta, state_features): return [dot(theta[a], state_features) for a in range(N_ACTIONS)] def softmax(logits): m = max(logits) exps = [exp(l - m) for l in logits] Z = sum(exps) return [e / Z for e in exps]
表格环境用线性策略(每个动作一个权重向量)。Atari 上把 CNN 换进来,softmax 头留着。
def sample_action(probs, rng): x = rng.random() cum = 0 for a, p in enumerate(probs): cum += p if x <= cum: return a return len(probs) - 1 def log_prob(probs, a): return log(probs[a] + 1e-12)
def rollout(theta, env, rng, gamma): trajectory = [] s = env.reset() while not done: logits = policy_logits(theta, s) probs = softmax(logits) a = sample_action(probs, rng) s_next, r, done = env.step(s, a) trajectory.append((s, a, r, probs)) s = s_next return trajectory
def reinforce_step(theta, trajectory, gamma, lr, baseline=0.0): returns = compute_returns(trajectory, gamma) for (s, a, _, probs), G in zip(trajectory, returns): advantage = G - baseline grad_log_pi_a = [-p for p in probs] grad_log_pi_a[a] += 1.0 for i in range(N_ACTIONS): for j in range(len(s)): theta[i][j] += lr * advantage * grad_log_pi_a[i] * s[j]
梯度 ∇ log π(a|s) = e_a - π(·|s)(a 的 one-hot 减概率向量)是 softmax 策略梯度的核心。把它烧进肌肉记忆。
最近回合 G 的 running mean 就够把 4×4 GridWorld 跑起来,约 500 回合收敛。把基线升级成学到的 V̂(s),就得到 Actor-Critic。
💡 注意每一步我们用同一个回合回报
G去加权该回合所有时刻的∇ log π——这正是 vanilla REINFORCE 方差大的原因。把G换成「从该时刻起的未来回报」G_t^{from t},几乎不增加代码,却显著降方差。这是策略梯度实现里最便宜的收益。
2026 年 REINFORCE 很少直接跑,但它的梯度公式无处不在:
| 用例 | 派生方法 |
|---|---|
| 连续控制 | PPO / SAC,配高斯策略 |
| LLM RLHF | PPO,带 KL 惩罚,跑在 token 级策略上 |
| LLM 推理(DeepSeek) | GRPO——带组相对基线、无 critic 的 REINFORCE |
| 多智能体 | 中心化 critic 的 REINFORCE(MADDPG、COMA) |
| 离散动作机器人 | A2C、A3C、PPO |
| 仅偏好数据 | DPO——把 REINFORCE 改写成偏好似然损失,无需采样 |
读 2026 年训练脚本看到 loss = -advantage * log_prob,那就是带基线的 REINFORCE。整篇论文(DPO、GRPO、RLOO)都是在这行之上的方差削减技巧。
生产里你不会手写反向传播——torch.distributions.Categorical 或 jax.random.categorical 把「采样 + log_prob」封装好,loss = -(advantage * log_prob).mean() 就是 REINFORCE 的全部。本节手写版的真正价值:让你看清 ∇ log π = e_a - π 这个公式在自动微分下到底算的是什么,从而在出 bug 时(梯度符号反、熵塌缩、回报没归一化)能立刻定位。
本节产出一个可复用 skill(位于原课程 outputs/skill-policy-gradient-trainer.md)。骨架:
--- name: policy-gradient-trainer description: 为给定任务产出 REINFORCE / Actor-Critic / PPO 训练配置,并诊断方差问题。 version: 1.0.0 phase: 9 lesson: 6 tags: [rl, policy-gradient, reinforce] --- 给定一个环境(离散 / 连续动作、视野、奖励统计),输出: 1. 策略头。Softmax(离散)或高斯(连续),附参数量。 2. 基线。无(vanilla)、running mean、学到的 V̂(s)、或 A2C critic。 3. 方差控制。默认开未来回报、回报归一化、梯度裁剪值。 4. 熵正则。系数 β 与衰减调度。 5. 批大小。每次更新的回合数;on-policy 数据新鲜度合约。 拒绝在视野 > 500 步的任务上跑无基线 REINFORCE。 拒绝连续动作控制用 softmax 头。 标记任何 β=0 且观测到策略熵 < 0.1 的运行为熵塌缩。
β · H(π),扫 β ∈ {0, 0.01, 0.1, 1.0}。画最终回报与策略熵。这个任务的最佳点在哪?∇ log π 之前永远把 G 归一化到 ~N(0, 1)。β · H(π(·|s))。π。on-policy 方法因此每几次滚动就更新一次。| 术语 | 俗称 | 实际含义 |
|---|---|---|
| 策略梯度 | 「直接训策略」 | ∇J(θ) = E[G · ∇ log π_θ(a|s)];由对数导数技巧推出 |
| REINFORCE | 「最早的 PG 算法」 | Williams 1992;蒙特卡洛回报乘以 log 策略梯度 |
| 对数导数技巧 | 「分数函数估计器」 | ∇P(τ;θ) = P(τ;θ) · ∇ log P(τ;θ);让期望的梯度可算 |
| 基线 | 「方差削减」 | 从 G 减去任何 b(s);无偏是因为 E[b · ∇ log π] = 0 |
| 未来回报 | 「只算未来的」 | 用 G_t^{from t} 而非全 G_0;正确且方差更低 |
| 熵正则 | 「鼓励探索」 | +β · H(π(·|s)) 项防止策略塌缩 |
| on-policy | 「训刚看到的」 | 梯度期望对当前策略求——不能直接复用旧数据 |
| 优势 | 「比平均好多少」 | A(s,a) = G(s,a) - V(s);带基线 REINFORCE 乘的带符号量 |
π_θ(a|s) 输出分布,采样、算 ∇J(θ)、上升——无 argmax、无贝尔曼递归。∇J = E[Σ_t G_t · ∇ log π] 由对数导数技巧两行推出。∇ log π(a|s) = e_a - π(·|s)——刻进肌肉记忆的形式。G_t - b(s_t) 中 b 不依赖 a_t;最佳 b 是学到的 V̂(s) → Actor-Critic。+β·H(π),β 典型 0.01。loss = -advantage * log_prob 就是它;PPO/GRPO/DPO 都是它的方差削减变体。下一节,我们给 REINFORCE 配一个学价值的 critic——Actor-Critic 把方差再砍一截,A2C 同步、A3C 异步,是所有现代深度策略方法的母型。