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,它把三个思想融合在一起:
- DPG(确定性策略梯度):Actor 直接输出确定性动作 \(a = \mu_\theta(s)\),而非概率分布
- DQN 的经验回放:Off-Policy,用 Replay Buffer 打破数据相关性
- 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):
软更新的好处
目标网络的参数缓慢向在线网络靠拢,而不是突然跳变。就像温水煮青蛙——目标值的变化足够平滑,训练更稳定。
三、DDPG 的训练过程¶
3.1 Critic 更新¶
与 DQN 类似,最小化 TD 误差:
注意:目标值 \(y\) 中使用目标 Actor 选动作、目标 Critic 评估。
3.2 Actor 更新¶
Actor 的目标是最大化 Critic 给出的 Q 值:
直觉: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 输出的是确定性动作,训练时需要人为加入噪声来探索:
通常使用:
- 高斯噪声:简单直接,\(\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 值作为目标:
→ 缓解 Q 值过估计
技巧 2:延迟更新 Actor(Delayed)
Actor 的更新频率低于 Critic(通常每 2 步 Critic 更新,Actor 才更新 1 次)。
→ 让 Critic 先稳定,Actor 再跟进
技巧 3:目标动作平滑
在目标动作上加入裁剪噪声:
→ 正则化效果,防止 Critic 对特定动作过拟合
4.3 SAC(Soft Actor-Critic)¶
SAC 在 Actor-Critic 框架中加入最大熵目标:
不仅要最大化奖励,还要最大化策略的熵(随机性)。
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)]\) |