跳转至

DDPG 与连续控制

前面的 DQN 只能处理离散动作,PPO 虽然能处理连续动作但样本效率不高。DDPG(Deep Deterministic Policy Gradient) 是专门为连续动作空间设计的 Off-Policy 算法,它将 DQN 的思想巧妙地搬到了连续控制领域。


一、连续动作空间的挑战

1.1 为什么 DQN 不行?

DQN 的核心操作是 \(\arg\max_a Q(s, a)\)——遍历所有动作取最大值。

场景 动作空间 DQN 能否处理
Atari 游戏 上/下/左/右/开火(离散,~18 个)
机械臂关节角度 \([-\pi, \pi]\)(连续,无穷多个)
自动驾驶油门 \([0, 1]\)(连续)

连续空间里你不可能"遍历所有动作取 max"——动作有无穷多个。

1.2 解决方案对比

方法 思路 类型
PPO(策略梯度) 输出动作分布(如高斯),采样 On-Policy,样本效率低
DDPG 用一个网络直接输出确定性动作 Off-Policy,样本效率高
TD3 DDPG 的改进版 Off-Policy
SAC 最大熵 + Actor-Critic Off-Policy

二、DDPG 算法详解

2.1 DDPG = DPG + DQN 技术

DDPG 全名 Deep Deterministic Policy Gradient,它把三个思想融合在一起:

  1. DPG(确定性策略梯度):Actor 直接输出确定性动作 \(a = \mu_\theta(s)\),而非概率分布
  2. DQN 的经验回放:Off-Policy,用 Replay Buffer 打破数据相关性
  3. DQN 的目标网络:稳定训练

2.2 四个网络

DDPG 维护四个神经网络:

网络 符号 输入 → 输出 作用
Actor(在线) \(\mu_\theta(s)\) 状态 → 动作 选择动作
Actor(目标) \(\mu_{\theta'}(s)\) 状态 → 动作 计算目标值时选动作
Critic(在线) \(Q_\phi(s, a)\) 状态+动作 → Q值 评估动作好坏
Critic(目标) \(Q_{\phi'}(s, a)\) 状态+动作 → Q值 计算稳定的目标 Q 值
Actor(在线): s ──→ a = μθ(s)
Critic(在线): (s, a) ──→ Q(s, a)

Actor(目标): s' ──→ a' = μθ'(s')       ← 参数缓慢跟随
Critic(目标): (s', a') ──→ Q(s', a')   ← 参数缓慢跟随

2.3 软更新(Soft Update)

不同于 DQN 的"硬更新"(每 C 步直接复制参数),DDPG 使用软更新(Polyak Averaging):

\[ \theta' \leftarrow \tau \theta + (1 - \tau) \theta' \qquad (\tau \ll 1, \text{如 } 0.005) \]
\[ \phi' \leftarrow \tau \phi + (1 - \tau) \phi' \]

软更新的好处

目标网络的参数缓慢向在线网络靠拢,而不是突然跳变。就像温水煮青蛙——目标值的变化足够平滑,训练更稳定。


三、DDPG 的训练过程

3.1 Critic 更新

与 DQN 类似,最小化 TD 误差:

\[ y = r + \gamma Q_{\phi'}(s', \mu_{\theta'}(s')) \]
\[ L_{\text{critic}} = \frac{1}{N}\sum_{i} (y_i - Q_\phi(s_i, a_i))^2 \]

注意:目标值 \(y\) 中使用目标 Actor 选动作、目标 Critic 评估。

3.2 Actor 更新

Actor 的目标是最大化 Critic 给出的 Q 值:

\[ \nabla_\theta J \approx \frac{1}{N}\sum_i \nabla_a Q_\phi(s, a)\big|_{a=\mu_\theta(s)} \cdot \nabla_\theta \mu_\theta(s) \]

直觉:Critic 告诉 Actor "Q 值对动作的梯度方向",Actor 就朝那个方向调整动作输出。

确定性策略梯度 vs 随机策略梯度

  • 随机策略梯度(PG/PPO):\(\nabla_\theta \mathbb{E}[\log \pi_\theta(a|s) \cdot Q(s,a)]\) → 需要大量采样
  • 确定性策略梯度(DDPG):\(\nabla_\theta Q(s, \mu_\theta(s))\) → 直接通过 Critic 反向传播,不需要采样

3.3 探索策略

由于 Actor 输出的是确定性动作,训练时需要人为加入噪声来探索:

\[ a = \mu_\theta(s) + \mathcal{N}(0, \sigma) \]

通常使用:

  • 高斯噪声:简单直接,\(\sigma\) 逐渐衰减
  • Ornstein-Uhlenbeck (OU) 噪声:有时间相关性的噪声,在物理控制任务中更自然

3.4 完整算法伪代码

初始化 Actor μθ,Critic Qφ
初始化目标网络:θ' ← θ,φ' ← φ
初始化回放缓冲区 D

循环每个回合:
  获取初始状态 s
  循环每一步:
    1. 选动作(加探索噪声):a = μθ(s) + noise
    2. 执行 a,得到 r, s', done
    3. 存入 D:(s, a, r, s', done)
    4. 从 D 随机采样 mini-batch

    5. 计算目标:
       a' = μθ'(s')                    ← 目标 Actor
       y = r + γ · Qφ'(s', a') · (1-done)  ← 目标 Critic

    6. 更新 Critic:最小化 (y - Qφ(s, a))²

    7. 更新 Actor:最大化 Qφ(s, μθ(s))
       即梯度上升:∇θ Qφ(s, μθ(s))

    8. 软更新目标网络:
       θ' ← τθ + (1-τ)θ'
       φ' ← τφ + (1-τ)φ'

3.5 代码实现

import torch
import torch.nn as nn

class Actor(nn.Module):
    def __init__(self, state_dim, action_dim, max_action):
        super().__init__()
        self.net = nn.Sequential(
            nn.Linear(state_dim, 256), nn.ReLU(),
            nn.Linear(256, 256), nn.ReLU(),
            nn.Linear(256, action_dim), nn.Tanh()
        )
        self.max_action = max_action

    def forward(self, state):
        return self.max_action * self.net(state)

class Critic(nn.Module):
    def __init__(self, state_dim, action_dim):
        super().__init__()
        self.net = nn.Sequential(
            nn.Linear(state_dim + action_dim, 256), nn.ReLU(),
            nn.Linear(256, 256), nn.ReLU(),
            nn.Linear(256, 1)
        )

    def forward(self, state, action):
        return self.net(torch.cat([state, action], dim=1))

# 训练步骤
def ddpg_update(actor, critic, target_actor, target_critic,
                actor_opt, critic_opt, batch, gamma=0.99, tau=0.005):
    states, actions, rewards, next_states, dones = batch

    # ---- Critic 更新 ----
    with torch.no_grad():
        target_actions = target_actor(next_states)
        target_q = target_critic(next_states, target_actions)
        y = rewards + gamma * target_q * (1 - dones)

    current_q = critic(states, actions)
    critic_loss = nn.MSELoss()(current_q, y)
    critic_opt.zero_grad()
    critic_loss.backward()
    critic_opt.step()

    # ---- Actor 更新 ----
    actor_loss = -critic(states, actor(states)).mean()  # 最大化 Q → 最小化 -Q
    actor_opt.zero_grad()
    actor_loss.backward()
    actor_opt.step()

    # ---- 软更新 ----
    for param, target_param in zip(actor.parameters(), target_actor.parameters()):
        target_param.data.copy_(tau * param.data + (1 - tau) * target_param.data)
    for param, target_param in zip(critic.parameters(), target_critic.parameters()):
        target_param.data.copy_(tau * param.data + (1 - tau) * target_param.data)

四、DDPG 的问题与改进

4.1 DDPG 的常见问题

问题 描述
Q 值过估计 与 DQN 相同,Critic 倾向于高估 Q 值
对超参数敏感 学习率、噪声大小等稍有不当就训练失败
探索不足 确定性策略 + 高斯噪声的探索能力有限
脆弱性 训练过程不稳定,不同随机种子结果差异大

4.2 TD3(Twin Delayed DDPG)

TD3 是 DDPG 最重要的改进版,通过三个技巧解决上述问题:

技巧 1:双 Critic(Twin)

维护两个 Critic 网络,取较小的 Q 值作为目标:

\[ y = r + \gamma \min(Q_{\phi_1'}(s', a'), Q_{\phi_2'}(s', a')) \]

→ 缓解 Q 值过估计

技巧 2:延迟更新 Actor(Delayed)

Actor 的更新频率低于 Critic(通常每 2 步 Critic 更新,Actor 才更新 1 次)。

→ 让 Critic 先稳定,Actor 再跟进

技巧 3:目标动作平滑

在目标动作上加入裁剪噪声:

\[ a' = \mu_{\theta'}(s') + \text{clip}(\epsilon, -c, c), \quad \epsilon \sim \mathcal{N}(0, \sigma) \]

→ 正则化效果,防止 Critic 对特定动作过拟合

4.3 SAC(Soft Actor-Critic)

SAC 在 Actor-Critic 框架中加入最大熵目标:

\[ J(\pi) = \sum_t \mathbb{E}\left[r_t + \alpha \mathcal{H}(\pi(\cdot|s_t))\right] \]

不仅要最大化奖励,还要最大化策略的熵(随机性)。

SAC 优势:

  • 自动探索(不需要手动加噪声)
  • 训练更稳定
  • 对超参数不敏感
  • 目前连续控制任务的最强算法之一

五、连续控制算法对比

DDPG TD3 SAC PPO
策略类型 确定性 确定性 随机 随机
On/Off-Policy Off Off Off On
样本效率
稳定性
实现复杂度 中高 简单
探索方式 加噪声 加噪声 熵正则化 策略随机性
推荐场景 入门学习 替代 DDPG 机器人控制 通用

选择建议

  • 刚接触连续控制:先学 DDPG 理解原理,再学 TD3/SAC
  • 实际项目:优先考虑 SAC(连续控制最稳)或 PPO(最通用)
  • 机器人控制:SAC 或 PPO,取决于是否有模拟环境

六、超参数参考

超参数 DDPG/TD3 SAC
Actor 学习率 \(10^{-4}\) \(3 \times 10^{-4}\)
Critic 学习率 \(10^{-3}\) \(3 \times 10^{-4}\)
折扣因子 \(\gamma\) 0.99 0.99
软更新率 \(\tau\) 0.005 0.005
回放缓冲区 \(10^6\) \(10^6\)
Batch size 256 256
探索噪声 \(\sigma\) 0.1 → 逐渐衰减 N/A(自动探索)

关键公式速查

名称 公式
DDPG Critic 目标 \(y = r + \gamma Q_{\phi'}(s', \mu_{\theta'}(s'))\)
DDPG Actor 梯度 \(\nabla_\theta J = \nabla_a Q_\phi(s,a)\big\|_{a=\mu_\theta(s)} \cdot \nabla_\theta \mu_\theta(s)\)
软更新 \(\theta' \leftarrow \tau\theta + (1-\tau)\theta'\)
TD3 目标 \(y = r + \gamma \min_i Q_{\phi_i'}(s', \mu_{\theta'}(s') + \epsilon)\)
SAC 目标 \(J = \mathbb{E}[r + \alpha\mathcal{H}(\pi)]\)