本文内容是基于policy-gradient的数学基础进行的policy algorithm(代码部分参考cleanrl的实现)的介绍,主要是介绍ppo和dqn 首先我们从PPO的损失函数开始看起,可以知道ppo的损失函数主要由三个部分组成 $ J^\text{CLIP'} (\theta) = \mathbb{E} [ J^\text{CLIP} (\theta) - \color{red}{c_1 (V_\theta(s) - V_\text{target})^2} + \color{blue}{c_2 H(s, \pi_\theta(.))} ] $ 其中c1,c2位超参数 第一项是PPO可以复用之前数据的核心,我们最后讲,其中第二项是在Actor-Critic中或者说一些比较基础的算法中常见的MSE损失,最后的熵是确保说可以学习到一些随机性 从最简单的例子开始讲起. cleanrl/ppo.py (example 为 cartpole -v4) action : discrete

Actor-Critic 网络

class Agent(nn.Module):
    def __init__(self, envs):
        super().__init__()
        self.critic = nn.Sequential(
            layer_init(nn.Linear(np.array(envs.single_observation_space.shape).prod(), 64)),
            nn.Tanh(),
            layer_init(nn.Linear(64, 64)),
            nn.Tanh(),
            layer_init(nn.Linear(64, 1), std=1.0),
        )
        self.actor = nn.Sequential(
            layer_init(nn.Linear(np.array(envs.single_observation_space.shape).prod(), 64)),
            nn.Tanh(),
            layer_init(nn.Linear(64, 64)),
            nn.Tanh(),
            layer_init(nn.Linear(64, envs.single_action_space.n), std=0.01),
        )

    def get_value(self, x):
        return self.critic(x)

    def get_action_and_value(self, x, action=None):
        logits = self.actor(x)
        probs = Categorical(logits=logits)
        if action is None:
            action = probs.sample()
        return action, probs.log_prob(action), probs.entropy(), self.critic(x)

从这个网络的设计,可以看到就是两个简单的linear层,因为任务比较简单.

  • Actor : policy net $\pi_\theta$ 决定了每一步的reward
  • Critic : 通过MSE来逼近准确的valude function $Q^\pi$

update critic

Critic 的更新本质上就是 TD learning,目标是最小化 value estimate 和 target 之间的差距。

Critic 的 loss 是 MSE:

$$ \mathcal{L}_{\text{critic}}(\phi) = \mathbb{E}_{s \sim d^\pi} \left[ \frac{1}{2} \big( V_\phi(s) - V_{\text{target}}(s) \big)^2 \right] $$

梯度更新:

$$ \phi \leftarrow \phi - \alpha \cdot \mathbb{E} \left[ \big(V_\phi(s) - V_{\text{target}}(s)\big) \nabla_\phi V_\phi(s) \right] $$

关键在于 $V_{\text{target}}(s)$ 怎么算。

两种 Target

1. Monte Carlo target(REINFORCE with baseline)

$$ V_{\text{target}}(s_t) = G_t = \sum_{k=0}^{\infty} \gamma^k R_{t+k+1} $$

用整条 trajectory 的真实 return。无偏但方差大。

2. TD target(Actor-Critic 标配)

$$ V_{\text{target}}(s_t) = R_{t+1} + \gamma V_\phi(s_{t+1}) $$

也就是 TD(0) target(单步 bootstrap)。也可以推广到 n-step:

$$ V_{\text{target}}(s_t) = \sum_{k=0}^{n-1} \gamma^k R_{t+k+1} + \gamma^n V_\phi(s_{t+n}) $$

为什么用 TD target?

1. 在线更新

MC 要等 episode 结束才能算 $G_t$,TD 只需要下一步。Actor-Critic 可以逐 step 更新,不依赖 episode 边界,也适用于 continuing task。

2. 减小方差 → Actor 梯度更稳定

Actor 的梯度里乘的是 $\hat{A}_t$(advantage),由 critic 给出。如果 critic 的 target 是 MC return,advantage 的方差会直接传导给 actor 的 policy gradient,actor 更新就会震荡。TD target 的方差小得多,整个系统更稳定。

3. Bootstrap 引入的 bias 在可接受范围内

TD 的 bias 来自 $V_\phi$ 的不准确——初期 critic 随机初始化,bootstrap 确实有偏。但随着训练进行 bias 逐步减小。这是经典的 bias-variance tradeoff:牺牲一点可收敛的 bias,换取大幅方差下降。

TD error:

$$ \delta_t = R_{t+1} + \gamma V(S_{t+1}) - V(S_t) $$

Critic 的 update 就是让 $\delta_t \to 0$。$\delta_t$ 同时也是 advantage 的一步估计,直接喂给 actor。所以 critic 学得越好,actor 的梯度方向就越准。


核心思想

PPO 想做多次 gradient step(提高样本效率),但又不能像 TRPO 那样搞约束优化。解决方案:clip 掉更新太激进的那部分梯度。

Clipped Surrogate Objective

记新旧策略的概率比:

$$ r_t(\theta) = \frac{\pi_\theta(a_t|s_t)}{\pi_{\theta_{\text{old}}}(a_t|s_t)} $$

$r_t = 1$ 表示参数没变;$r_t > 1$ 表示新策略更倾向选这个动作;$r_t < 1$ 表示相反。

PPO 的 loss 是:

$$ \mathcal{L}^{\text{CLIP}}(\theta) = \mathbb{E}_t \left[ \min\Big( \underbrace{r_t(\theta) \hat{A}_t}_{\text{不做限制的 PG}}, \underbrace{\text{clip}(r_t(\theta), 1-\epsilon, 1+\epsilon) \hat{A}_t}_{\text{限制更新幅度}} \Big) \right] $$

其中 $\epsilon$ 典型值是 0.1 或 0.2。

为什么是 min?

关键在于 $\hat{A}_t$ 的符号:

当 advantage > 0(这个动作比平均好,应该鼓励):

  • $r_t$ 越大,梯度的正向推动越大
  • 但 clip 把 $r_t$ 限制在 $1+\epsilon$,不让它超过
  • min 取 clip 这一项 → 阻止策略变得"太确信"这个好动作

当 advantage < 0(这个动作比平均差,应该抑制):

  • $r_t$ 越小(趋近 0),梯度的负向推动越大
  • 但 clip 把 $r_t$ 限制在 $1-\epsilon$,不让它降太多
  • min 取 unclipped 这一项 → 阻止策略对坏动作"矫枉过正"

一张图总结 min 的效果:

advantage > 0:  r 增大到 1+ε 后梯度被截断(不再受益)
advantage < 0:  r 减小到 1-ε 后梯度被截断(不再惩罚)

完整 Loss

实际训练时 PPO 的 loss 还包含 value function loss 和 entropy bonus:

$$ \mathcal{L}^{\text{total}} = \mathcal{L}^{\text{CLIP}} - c_1 \underbrace{\mathcal{L}^{\text{VF}}}_{(V_\theta(s) - V_{\text{target}})^2} + c_2 \underbrace{\mathcal{S}[\pi_\theta](\cdot|s_t)}_{\text{entropy bonus}} $$
  • Value loss:让 critic 学得更准,MSE loss
  • Entropy bonus:鼓励探索,防止策略过早收敛(系数通常 $c_2 \approx 0.01$)

PPO-Clip vs PPO-Penalty

PPO-Penalty(早期 OpenAI 论文里的另一个变体)在 loss 里加 KL 惩罚项 $\beta \cdot \text{KL}(\pi_{\text{old}} \| \pi_\theta)$,并自适应调整 $\beta$。实践中 PPO-Clip 更简单、效果也好,所以大家说的 PPO 几乎都指 clip 版本。

一句话

PPO loss = min(unclipped_ratio × advantage, clipped_ratio × advantage) → 好动作别推太过,坏动作别踩太狠,更新始终被限制在 $[1-\epsilon, 1+\epsilon]$ 的通道内。