# 策略梯度：REINFORCE

> 来源：https://dqn.cn/advanced/reinforce ｜ 作者：DQN.CN 编辑部 ｜ 发布：2026-10-29 ｜ 更新：2026-10-30 ｜ 许可：CC BY-NC-SA 4.0（文字）/ MIT（代码）

> **要点速览**
>
> - 策略梯度方法直接参数化策略 π_θ(a|s)，沿着期望回报 J(θ) 的梯度方向更新参数。
> - 策略梯度定理给出 ∇J(θ) = E[G_t ∇ log π_θ(A_t|S_t)]，不需要对环境动力学求导。
> - REINFORCE 用完整回合的蒙特卡洛回报 G_t 估计梯度，无偏但方差很大。
> - 从回报中减去一个只依赖状态的基线 b(s)（如价值函数）不改变梯度期望，却能显著降低方差，这引出了 Actor-Critic。

前面的 Q-learning 和 DQN 都是**基于价值**的方法：先学价值函数，再从中导出策略。**策略梯度**方法换了一种思路：直接把策略参数化为 $\pi_\theta(a \mid s)$，对期望回报做梯度上升。它天然支持随机策略和连续动作空间，也是 Actor-Critic、TRPO、PPO 以及大模型 RLHF 的共同基础。REINFORCE（Williams, 1992）是最基础的策略梯度算法，用蒙特卡洛回报估计梯度，实现简单但方差较大；引入基线可以在不引入偏差的情况下显著降低方差。

## 为什么要直接优化策略

基于价值的方法有几个局限：

1. **连续动作难处理**：$\arg\max_a Q(s, a)$ 在连续动作空间中本身就是一个优化问题。
2. **只能得到确定性策略**：在部分可观测或博弈问题中，最优策略可能是随机的（例如石头剪刀布）。
3. **价值的微小变化可能导致策略突变**：$\arg\max$ 不连续，训练可能不稳定。

策略梯度方法直接参数化策略：离散动作常用 softmax 输出各动作概率，连续动作常用高斯分布输出均值和方差。策略随参数**平滑**变化，收敛性质更好（至少收敛到局部最优）。

## 策略梯度定理

### 目标函数

对回合制任务，目标是最大化起始状态的期望回报：

$$
J(\theta) = \mathbb{E}_{\tau \sim \pi_\theta}\left[R(\tau)\right], \qquad R(\tau) = \sum_{t=0}^{T-1} \gamma^t R_{t+1}
$$

其中轨迹 $\tau = (S_0, A_0, R_1, S_1, \dots)$ 的概率为

$$
p_\theta(\tau) = p(S_0) \prod_{t=0}^{T-1} \pi_\theta(A_t \mid S_t)\, p(S_{t+1} \mid S_t, A_t)
$$

### 对数导数技巧

难点在于 $J$ 对 $\theta$ 的依赖是通过**数据分布**体现的。利用 $\nabla_\theta p_\theta = p_\theta \nabla_\theta \log p_\theta$：

$$
\nabla_\theta J(\theta) = \int \nabla_\theta p_\theta(\tau)\, R(\tau)\, d\tau = \mathbb{E}_{\tau \sim \pi_\theta}\left[\nabla_\theta \log p_\theta(\tau)\, R(\tau)\right]
$$

而 $\log p_\theta(\tau)$ 中，初始分布和环境转移都与 $\theta$ 无关，求导后消失：

$$
\nabla_\theta \log p_\theta(\tau) = \sum_{t=0}^{T-1} \nabla_\theta \log \pi_\theta(A_t \mid S_t)
$$

**不需要环境模型**就能估计梯度——只要能采样轨迹。

### 定理推导

再利用因果性（$t$ 时刻的动作不影响 $t$ 之前的奖励），可以把 $R(\tau)$ 换成从 $t$ 开始的回报 $G_t$（reward-to-go），得到常用形式：

$$
\nabla_\theta J(\theta) = \mathbb{E}_{\pi_\theta}\left[\sum_{t=0}^{T-1} \nabla_\theta \log \pi_\theta(A_t \mid S_t)\, G_t\right]
$$

更一般的**策略梯度定理**（Sutton et al., 2000）表述为：

$$
\nabla_\theta J(\theta) \propto \sum_s \mu(s) \sum_a q_\pi(s, a)\, \nabla_\theta \pi_\theta(a \mid s) = \mathbb{E}_\pi\left[q_\pi(S_t, A_t)\, \nabla_\theta \log \pi_\theta(A_t \mid S_t)\right]
$$

其中 $\mu$ 是策略下的状态分布。直观理解：**增大高回报动作的对数概率，减小低回报动作的对数概率**，幅度与回报成正比。（严格地说，折扣情形下还应乘以 $\gamma^t$，但实践中几乎都省略它，这会引入一点偏差但效果更好。）

**扩展阅读：策略梯度定理的完整证明（状态价值展开法）**

考虑折扣无限时域、初始状态 $s_0$ 固定，目标 $J(\theta) = v_\pi(s_0)$。记 $p(s \to x, k, \pi)$ 为从 $s$ 出发、按 $\pi$ 走 $k$ 步到达 $x$ 的概率。

**第 1 步：对 $v_\pi(s)$ 求导。** 由 $v_\pi(s) = \sum_a \pi(a \mid s)\, q_\pi(s, a)$ 和乘积法则：

$$
\nabla v_\pi(s) = \sum_a \left[\nabla \pi(a \mid s)\, q_\pi(s, a) + \pi(a \mid s)\, \nabla q_\pi(s, a)\right]
$$

**第 2 步：展开 $\nabla q_\pi$。** $q_\pi(s, a) = r(s, a) + \gamma \sum_{s'} p(s' \mid s, a)\, v_\pi(s')$，其中 $r$ 和 $p$ 与 $\theta$ 无关，所以 $\nabla q_\pi(s, a) = \gamma \sum_{s'} p(s' \mid s, a)\, \nabla v_\pi(s')$。记 $\phi(s) = \sum_a \nabla \pi(a \mid s)\, q_\pi(s, a)$，得到递推式

$$
\nabla v_\pi(s) = \phi(s) + \gamma \sum_{s'} p(s \to s', 1, \pi)\, \nabla v_\pi(s')
$$

**第 3 步：反复代入。** 把右边的 $\nabla v_\pi(s')$ 继续展开：

$$
\nabla v_\pi(s_0) = \sum_{x} \sum_{k=0}^{\infty} \gamma^k\, p(s_0 \to x, k, \pi)\, \phi(x) = \sum_x d^\pi_\gamma(x) \sum_a \nabla \pi(a \mid x)\, q_\pi(x, a)
$$

其中 $d^\pi_\gamma(x) = \sum_k \gamma^k p(s_0 \to x, k, \pi)$ 是未归一化的折扣状态访问量（与 [MDP 一节的占用度量](https://dqn.cn/basics/mdp) 相差一个 $1-\gamma$ 的归一化因子）。

**第 4 步：写成期望。** 利用 $\nabla \pi = \pi \nabla \log \pi$：

$$
\nabla_\theta J(\theta) = \frac{1}{1-\gamma}\, \mathbb{E}_{s \sim \bar d^\pi,\, a \sim \pi}\left[q_\pi(s, a)\, \nabla_\theta \log \pi_\theta(a \mid s)\right], \qquad \bar d^\pi = (1-\gamma)\, d^\pi_\gamma
$$

**关键点：** 证明中**没有出现状态分布对 $\theta$ 的导数**——这正是定理的价值：策略改变会改变访问到的状态，但梯度只需要在当前策略下采样就能估计。另外，$\bar d^\pi$ 是“折扣”访问分布，按时刻 $t$ 采样时对应权重 $\gamma^t$；实践中直接用所有时刻的样本（不乘 $\gamma^t$），就是正文中提到的那一点偏差的来源。

**基线不改变梯度。** 对任意只依赖状态的 $b(s)$，$\sum_a \nabla \pi(a \mid s)\, b(s) = b(s)\, \nabla \sum_a \pi(a \mid s) = b(s)\, \nabla 1 = 0$，所以把 $q_\pi$ 换成 $q_\pi - b$ 不改变期望，只改变方差。

## REINFORCE 算法

用单条轨迹的 $G_t$ 作为 $q_\pi(S_t, A_t)$ 的无偏估计（蒙特卡洛），就得到 REINFORCE：

```text
初始化策略参数 θ
重复（对每个回合）：
    用 π_θ 生成一个回合 S0, A0, R1, ..., S_{T-1}, A_{T-1}, R_T
    对 t = 0, 1, ..., T-1：
        G_t ← Σ_{k=t+1}^{T} γ^{k-t-1} R_k
    θ ← θ + α Σ_t G_t ∇_θ log π_θ(A_t | S_t)
```

用 PyTorch 实现时，构造“伪损失” $L(\theta) = -\sum_t G_t \log \pi_\theta(A_t \mid S_t)$，对它做梯度下降，就等价于对 $J$ 做梯度上升。

## 引入基线降低方差

REINFORCE 的梯度估计无偏但**方差很大**：回报 $G_t$ 的数值大小、噪声都会直接放大梯度。若所有回报都是正的（如 CartPole），所有被采样的动作概率都会被提高，只是幅度不同，学习信号很弱。

可以从 $G_t$ 中减去任意**只依赖状态**的基线 $b(S_t)$：

$$
\nabla_\theta J(\theta) = \mathbb{E}_\pi\left[\nabla_\theta \log \pi_\theta(A_t \mid S_t)\, \left(G_t - b(S_t)\right)\right]
$$

这不改变期望，因为

$$
\mathbb{E}_{A \sim \pi_\theta}\left[\nabla_\theta \log \pi_\theta(A \mid s)\, b(s)\right] = b(s) \sum_a \nabla_\theta \pi_\theta(a \mid s) = b(s)\, \nabla_\theta \sum_a \pi_\theta(a \mid s) = b(s)\, \nabla_\theta 1 = 0
$$

最自然的基线是状态价值 $b(s) \approx v_\pi(s)$，用另一个网络 $V_w(s)$ 通过回归 $G_t$ 来学习。此时 $G_t - V_w(S_t)$ 衡量的是“这个动作比平均水平好多少”，即优势（advantage）的估计。这就是通往 [Actor-Critic](https://dqn.cn/advanced/actor-critic) 的桥梁。

## 代码：CartPole 上的 REINFORCE

策略网络输出两个动作的 logits；价值网络作为基线；对优势做标准化（常用技巧）。

```python
"""REINFORCE（带价值基线）：CartPole-v1。"""
import gymnasium as gym
import numpy as np
import torch
import torch.nn as nn
from torch.distributions import Categorical

GAMMA, LR_PI, LR_V, EPISODES = 0.99, 1e-3, 1e-3, 1000
torch.manual_seed(0)
env = gym.make("CartPole-v1")
obs_dim, n_actions = env.observation_space.shape[0], env.action_space.n

policy = nn.Sequential(nn.Linear(obs_dim, 64), nn.Tanh(), nn.Linear(64, n_actions))   # 输出 logits
value = nn.Sequential(nn.Linear(obs_dim, 64), nn.Tanh(), nn.Linear(64, 1))            # 基线 b(s) ≈ v(s)
opt_pi = torch.optim.Adam(policy.parameters(), lr=LR_PI)
opt_v = torch.optim.Adam(value.parameters(), lr=LR_V)

returns_log = []
for ep in range(EPISODES):
    obs, _ = env.reset(seed=ep)
    states, log_probs, rewards = [], [], []
    done = False
    while not done:
        s = torch.as_tensor(obs, dtype=torch.float32)
        dist = Categorical(logits=policy(s))
        a = dist.sample()
        obs, r, term, trunc, _ = env.step(a.item())
        states.append(s); log_probs.append(dist.log_prob(a)); rewards.append(r)
        done = term or trunc

    # 计算每一步的回报 G_t（从后往前）
    G, returns = 0.0, []
    for r in reversed(rewards):
        G = r + GAMMA * G
        returns.append(G)
    returns = torch.tensor(returns[::-1], dtype=torch.float32)
    states = torch.stack(states)

    v = value(states).squeeze(1)
    adv = returns - v.detach()                         # 用基线降低方差
    adv = (adv - adv.mean()) / (adv.std() + 1e-8)      # 标准化（常用技巧）

    loss_pi = -(torch.stack(log_probs) * adv).sum()    # 梯度上升 => 最小化负目标
    loss_v = ((returns - v) ** 2).mean()
    opt_pi.zero_grad(); loss_pi.backward(); opt_pi.step()
    opt_v.zero_grad(); loss_v.backward(); opt_v.step()

    returns_log.append(sum(rewards))
    if (ep + 1) % 100 == 0:
        print(f"episode {ep + 1:4d} | 最近100回合平均回报 {np.mean(returns_log[-100:]):6.1f}")

# ---------- 训练结束：用确定性（argmax）策略评估 10 个回合 ----------
eval_env = gym.make("CartPole-v1")
scores = []
for i in range(10):
    o, _ = eval_env.reset(seed=10_000 + i)
    done, total = False, 0.0
    while not done:
        with torch.no_grad():
            act = int(policy(torch.as_tensor(o, dtype=torch.float32)).argmax())
        o, r, te, tr, _ = eval_env.step(act)
        total += r; done = te or tr
    scores.append(total)
print(f"贪心评估（10 回合）平均回报: {np.mean(scores):.1f}")
```
实际运行输出（单线程 CPU，约 1–2 分钟）：

```text
episode  100 | 最近100回合平均回报   34.2
episode  200 | 最近100回合平均回报  111.6
episode  300 | 最近100回合平均回报  239.0
episode  400 | 最近100回合平均回报  342.0
episode  500 | 最近100回合平均回报  414.9
episode  600 | 最近100回合平均回报  456.5
episode  700 | 最近100回合平均回报  484.0
episode  800 | 最近100回合平均回报  483.4
episode  900 | 最近100回合平均回报  475.2
episode 1000 | 最近100回合平均回报  467.4
贪心评估（10 回合）平均回报: 289.0
```

训练中（按策略概率**随机采样**动作）最近 100 回合的平均回报在 700 回合左右达到约 484（满分 500）。值得注意的是，训练结束后改用 $\arg\max$ 的**确定性**策略评估，10 个回合平均只有 289.0——策略梯度学到的是一个随机策略，它的表现是在“按概率采样”的条件下优化的，直接取 argmax 并不保证更好。评估策略梯度方法时，应说明使用的是随机策略还是确定性策略。

## 优缺点分析

| 优点 | 缺点 |
| --- | --- |
| 直接优化目标，理论清晰 | 方差大，样本效率低 |
| 支持连续动作和随机策略 | 同策略：每批数据用完即弃 |
| 实现简单，不需要模型 | 必须等回合结束（蒙特卡洛） |
| 收敛到局部最优有保证 | 对学习率敏感，步长过大容易崩溃 |

## 实践技巧

- **优势标准化**：每个回合（或批次）内把优势标准化为零均值单位方差，几乎总能让训练更稳定。
- **学习率**：策略网络的学习率通常比监督学习小，$10^{-4} \sim 10^{-3}$。
- **批量多个回合**：每次用多个回合的数据估计梯度可以降低方差。
- **熵正则**：在损失中加入 $-\beta \mathcal{H}(\pi_\theta)$ 防止策略过早变成确定性的，见 [Actor-Critic](https://dqn.cn/advanced/actor-critic)。

## 常见误区

- **忘记负号**：PyTorch 优化器做的是梯度下降，损失要写成 $-\log \pi \cdot G$。
- **基线参与了策略梯度的反向传播**：基线 $V_w(S_t)$ 在策略损失中必须 `detach()`。
- **用 $R(\tau)$ 而不是 reward-to-go $G_t$**：仍然无偏，但方差更大。
- **基线依赖于动作**：基线只能依赖状态，否则会引入偏差。

## 小结

- 策略梯度定理：$\nabla_\theta J = \mathbb{E}[\nabla_\theta \log \pi_\theta(A_t \mid S_t)\, G_t]$，不需要模型。
- REINFORCE 用蒙特卡洛回报估计梯度，无偏但高方差。
- 减去状态相关的基线不改变期望，能显著降低方差；用 $V(s)$ 作基线得到优势估计。
- 用学到的价值函数做自举（而不只是基线），就得到下一节的 Actor-Critic。

## 练习题

1. 去掉代码中的价值基线（直接用标准化后的 $G_t$），比较学习曲线。
2. 去掉优势标准化，训练是否还能收敛？
3. 推导高斯策略 $\pi_\theta(a \mid s) = \mathcal{N}(\mu_\theta(s), \sigma^2)$ 的 $\nabla_\theta \log \pi_\theta(a \mid s)$。
4. 证明使用 reward-to-go 代替 $R(\tau)$ 后，梯度估计仍然无偏。
5. 改为每次收集 5 个回合再更新一次，学习曲线的平滑程度如何变化？

## 参考文献

- Williams, R. J. (1992). Simple statistical gradient-following algorithms for connectionist reinforcement learning. *Machine Learning*, 8, 229–256.
- Sutton, R. S., McAllester, D., Singh, S., & Mansour, Y. (2000). Policy gradient methods for reinforcement learning with function approximation. *NeurIPS*.
- Sutton, R. S., & Barto, A. G. (2018). *Reinforcement Learning: An Introduction* (2nd ed.), 第 13 章.

## 下一步

上一节：[DQN 改进](https://dqn.cn/advanced/dqn-improvements)　｜　继续阅读：[Actor-Critic：A2C / A3C](https://dqn.cn/advanced/actor-critic)

## 常见问题速查（FAQ）

**问：策略梯度和 Q-learning 有什么区别？**

答：Q-learning 先学价值函数再取贪心动作；策略梯度直接优化策略本身，天然支持连续动作和随机策略。

**问：REINFORCE 为什么方差大？**

答：它用整条轨迹的随机回报作为权重，回报受后续所有随机动作和环境随机性影响，因此梯度估计波动很大。

**问：加入基线为什么不会引入偏差？**

答：因为 E[b(s)∇ log π_θ(a|s)] = b(s) ∇ Σ_a π_θ(a|s) = b(s) ∇1 = 0，基线项的期望为零。

## 如何引用本文

DQN.CN 编辑部. 策略梯度：REINFORCE[EB/OL]. DQN.CN, 2026-10-29（更新于 2026-10-30）. https://dqn.cn/advanced/reinforce.
