跳转至

TD3 与 DDPG

概述

DDPG (Deep Deterministic Policy Gradient)TD3 (Twin Delayed DDPG) 是连续动作空间中最经典的 Actor-Critic 算法。DDPG 开创了深度确定性策略梯度方法,TD3 则通过三项关键改进解决了 DDPG 的过高估计和训练不稳定问题。

本文详细介绍两种算法的原理、实现和对比。


1. DDPG: 深度确定性策略梯度

1.1 背景与动机

DQN 在离散动作空间取得了巨大成功,但无法直接处理连续动作空间(因为 \(\max_a Q(s,a)\) 在连续空间中需要优化)。DDPG 结合了:

  • 确定性策略梯度 (DPG) 理论 (Silver et al., 2014)
  • DQN 的稳定训练技巧: 经验回放 + 目标网络
  • Actor-Critic 架构

1.2 确定性策略梯度定理

对于确定性策略 \(\mu_\theta(s)\),策略梯度为:

\[\nabla_\theta J(\theta) = \mathbb{E}_{s \sim \rho^\mu} \left[ \nabla_a Q^\mu(s,a) \big|_{a=\mu_\theta(s)} \nabla_\theta \mu_\theta(s) \right]\]

直觉理解: 沿着使 Q 值增大的方向调整策略输出。

与随机策略梯度对比:

维度 随机策略梯度 确定性策略梯度
策略 \(\pi_\theta(a\|s)\) 概率分布 \(\mu_\theta(s)\) 确定性映射
梯度 需要对动作积分 只需动作处的梯度
探索 策略自带随机性 需要额外探索噪声
样本效率 较低 较高

1.3 DDPG 架构

graph TB
    subgraph Actor["Actor (策略网络)"]
        S1[状态 s] --> MU["μ_θ(s)"]
        MU --> A[动作 a]
    end

    subgraph Critic["Critic (Q网络)"]
        S2[状态 s] --> Q["Q_φ(s, a)"]
        A2[动作 a] --> Q
        Q --> V[Q值]
    end

    subgraph Target["目标网络"]
        MU_T["μ_θ'(s')"]
        Q_T["Q_φ'(s', a')"]
    end

    A --> A2

    style Actor fill:#e3f2fd
    style Critic fill:#fff3e0
    style Target fill:#f3e5f5

1.4 DDPG 算法详解

四个网络

网络 符号 作用
Actor \(\mu_\theta(s)\) 输出确定性动作
Critic \(Q_\phi(s,a)\) 评估状态-动作对的价值
Target Actor \(\mu_{\theta'}(s)\) 计算目标动作
Target Critic \(Q_{\phi'}(s,a)\) 计算目标Q值

Critic 更新

最小化 TD 误差:

\[\mathcal{L}(\phi) = \mathbb{E}_{(s,a,r,s') \sim \mathcal{D}} \left[ \left( Q_\phi(s,a) - y \right)^2 \right]\]

其中目标值:

\[y = r + \gamma Q_{\phi'}(s', \mu_{\theta'}(s'))\]

Actor 更新

最大化 Critic 评估的 Q 值:

\[\nabla_\theta J = \mathbb{E}_{s \sim \mathcal{D}} \left[ \nabla_a Q_\phi(s,a) \big|_{a=\mu_\theta(s)} \nabla_\theta \mu_\theta(s) \right]\]

目标网络软更新

\[\theta' \leftarrow \tau \theta + (1-\tau) \theta'\]
\[\phi' \leftarrow \tau \phi + (1-\tau) \phi'\]

其中 \(\tau \ll 1\) (通常 0.005)。

探索策略

在训练时添加噪声:

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

通常使用 Ornstein-Uhlenbeck (OU) 过程或简单高斯噪声。

1.5 DDPG 伪代码

# 初始化
actor = Actor(state_dim, action_dim)
critic = Critic(state_dim, action_dim)
target_actor = copy(actor)
target_critic = copy(critic)
replay_buffer = ReplayBuffer(capacity=1e6)

for episode in range(num_episodes):
    state = env.reset()
    for step in range(max_steps):
        # 选择动作 + 探索噪声
        action = actor(state) + noise.sample()
        action = clip(action, action_low, action_high)

        # 环境交互
        next_state, reward, done = env.step(action)
        replay_buffer.add(state, action, reward, next_state, done)

        # 从经验池采样
        batch = replay_buffer.sample(batch_size=256)

        # Critic 更新
        target_action = target_actor(batch.next_state)
        target_q = batch.reward + gamma * target_critic(
            batch.next_state, target_action) * (1 - batch.done)
        critic_loss = MSE(critic(batch.state, batch.action), target_q)
        update(critic, critic_loss)

        # Actor 更新
        actor_loss = -critic(batch.state, actor(batch.state)).mean()
        update(actor, actor_loss)

        # 目标网络软更新
        soft_update(target_actor, actor, tau=0.005)
        soft_update(target_critic, critic, tau=0.005)

1.6 DDPG 的问题

  1. Q值过高估计: Critic 倾向于过高估计 Q 值,导致策略次优
  2. 训练不稳定: 对超参数敏感,容易发散
  3. 脆弱的探索: OU 噪声效果不稳定
  4. Actor-Critic 耦合: Critic 误差会传播到 Actor

2. TD3: 三重延迟 DDPG

2.1 核心思想

TD3 (Fujimoto et al., 2018) 针对 DDPG 的问题提出三项关键改进:

  1. 裁剪双 Q 学习 (Clipped Double Q-Learning)
  2. 延迟策略更新 (Delayed Policy Updates)
  3. 目标策略平滑 (Target Policy Smoothing)

2.2 改进一: 裁剪双 Q 学习

问题: 单个 Q 网络容易过高估计。

解决: 使用两个独立的 Critic 网络,取较小的 Q 值:

\[y = r + \gamma \min_{i=1,2} Q_{\phi'_i}(s', \tilde{a}')\]

原理: 类似 Double DQN 的思想,但更加保守。取最小值可以有效抑制过高估计,即使可能引入轻微低估(低估通常比高估更安全)。

2.3 改进二: 延迟策略更新

问题: Actor 基于不准确的 Critic 更新会导致策略振荡。

解决: Critic 每更新 \(d\) 次,Actor 才更新一次(通常 \(d=2\))。

原理: 让 Critic 先稳定下来,再用更准确的 Q 值指导 Actor 更新。

for step in range(total_steps):
    # 每步都更新 Critic
    update_critic()

    # 每 d 步才更新 Actor 和目标网络
    if step % d == 0:
        update_actor()
        soft_update_targets()

2.4 改进三: 目标策略平滑

问题: 目标 Q 值对特定动作过度拟合。

解决: 在目标动作上添加裁剪的噪声:

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

原理: 类似正则化,使 Q 函数在动作空间中更平滑,减少对单个动作的过拟合。

2.5 TD3 完整算法

\[\text{Target: } y = r + \gamma \min_{i=1,2} Q_{\phi'_i}(s', \tilde{a}')\]

其中:

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

Critic 损失 (两个 Critic 分别更新):

\[\mathcal{L}(\phi_i) = \mathbb{E} \left[ \left( Q_{\phi_i}(s,a) - y \right)^2 \right], \quad i=1,2\]

Actor 损失 (每 \(d\) 步更新一次):

\[J(\theta) = -\mathbb{E}_{s \sim \mathcal{D}} \left[ Q_{\phi_1}(s, \mu_\theta(s)) \right]\]

2.6 TD3 伪代码

# 初始化
actor = Actor(state_dim, action_dim)
critic_1 = Critic(state_dim, action_dim)
critic_2 = Critic(state_dim, action_dim)
target_actor = copy(actor)
target_critic_1 = copy(critic_1)
target_critic_2 = copy(critic_2)

for step in range(total_steps):
    # 选择动作 + 探索噪声
    action = actor(state) + N(0, sigma)

    # 环境交互并存储
    next_state, reward, done = env.step(action)
    replay_buffer.add(state, action, reward, next_state, done)
    batch = replay_buffer.sample(batch_size)

    # 计算目标 (Target Policy Smoothing)
    noise = clip(N(0, sigma_tilde), -c, c)
    target_action = clip(target_actor(batch.next_state) + noise,
                         action_low, action_high)

    # Clipped Double Q-Learning
    target_q1 = target_critic_1(batch.next_state, target_action)
    target_q2 = target_critic_2(batch.next_state, target_action)
    target_q = batch.reward + gamma * min(target_q1, target_q2) * (1 - batch.done)

    # 更新两个 Critic
    loss_1 = MSE(critic_1(batch.state, batch.action), target_q)
    loss_2 = MSE(critic_2(batch.state, batch.action), target_q)
    update(critic_1, loss_1)
    update(critic_2, loss_2)

    # Delayed Policy Update
    if step % policy_delay == 0:
        actor_loss = -critic_1(batch.state, actor(batch.state)).mean()
        update(actor, actor_loss)

        soft_update(target_actor, actor, tau)
        soft_update(target_critic_1, critic_1, tau)
        soft_update(target_critic_2, critic_2, tau)

2.7 超参数

超参数 典型值 说明
\(\gamma\) 0.99 折扣因子
\(\tau\) 0.005 目标网络软更新率
\(\sigma\) 0.1 探索噪声标准差
\(\tilde{\sigma}\) 0.2 目标策略平滑噪声
\(c\) 0.5 噪声裁剪范围
\(d\) 2 策略更新延迟
batch size 256 批量大小
buffer size \(10^6\) 经验池大小
lr (actor) \(3 \times 10^{-4}\) Actor 学习率
lr (critic) \(3 \times 10^{-4}\) Critic 学习率

3. DDPG vs TD3 vs SAC 对比

3.1 核心差异

特性 DDPG TD3 SAC
Critic 数量 1 2 2
策略类型 确定性 确定性 随机 (最大熵)
Q值估计 倾向过高 裁剪取最小 裁剪取最小
策略更新频率 每步 延迟 (\(d\)步) 每步
目标平滑 有 (加噪声) 无 (熵正则)
探索机制 外部噪声 (OU/Gaussian) 外部噪声 (Gaussian) 熵最大化 (内在)
温度参数 \(\alpha\) (可自动调节)
训练稳定性 较差 较好 最好

3.2 性能对比

在 MuJoCo 连续控制基准上的典型表现:

环境 DDPG TD3 SAC
HalfCheetah ~8,000 ~10,000 ~11,000
Ant ~1,000 ~4,500 ~5,500
Walker2d ~2,000 ~4,500 ~5,000
Humanoid ~500 ~5,000 ~6,000

注意

以上数值为典型参考值,实际表现受超参数和随机种子影响较大。

3.3 选择建议

连续控制任务的算法选择:
  ├── 需要最稳定的训练 → SAC
  ├── 需要简单实现 → TD3
  ├── 需要确定性策略 → TD3 / DDPG
  ├── 需要自动探索调节 → SAC
  └── 作为 baseline → TD3 (简单、效果好)

4. 实践要点

4.1 常见问题与解决

问题 可能原因 解决方案
Q值爆炸 过高估计 使用TD3/双Q网络
训练不收敛 学习率太大 降低学习率,增大batch
策略振荡 Actor-Critic不同步 延迟策略更新
探索不足 噪声太小 增大噪声,或用SAC
动作超出范围 缺少裁剪 输出层用tanh+缩放

4.2 网络架构建议

# Actor 网络
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()  # 输出 [-1, 1]
        )
        self.max_action = max_action

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

# Critic 网络
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))

4.3 训练技巧

  1. 归一化观测: 对状态进行运行均值/方差归一化
  2. 奖励缩放: 将奖励缩放到合理范围
  3. 预热期: 前 N 步使用随机策略填充经验池
  4. 梯度裁剪: 防止梯度爆炸
  5. 多种子评估: 运行多个随机种子取平均

5. 从 TD3 到更先进的方法

5.1 发展脉络

DPG (2014)
  → DDPG (2015): + Deep Networks + Target Net + Replay
    → TD3 (2018): + Double Q + Delay + Smoothing
      → SAC (2018): + Maximum Entropy + Stochastic Policy
        → DrQ (2021): + Data Augmentation
          → RLPD (2023): + Pre-training Data

5.2 SAC 的核心区别

SAC 使用随机策略和最大熵框架:

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

相比 TD3,SAC 通过熵正则化自动平衡探索与利用,通常更加稳定。详见 SAC 算法详解


参考资料

  • Lillicrap, T. et al. (2016). Continuous control with deep reinforcement learning. ICLR 2016.
  • Silver, D. et al. (2014). Deterministic Policy Gradient Algorithms. ICML 2014.
  • Fujimoto, S. et al. (2018). Addressing Function Approximation Error in Actor-Critic Methods. ICML 2018.
  • Haarnoja, T. et al. (2018). Soft Actor-Critic. ICML 2018.

延伸阅读


评论 #