CTDE 范式详解:从上帝视角到独立行动 (MADDPG)
摘要: 多智能体强化学习早期面临独立学习(IQL)的非平稳性与完全中心化的通信瓶颈问题。CTDE(中心化训练,去中心化执行)范式通过训练阶段利用全局信息(Critic)与执行阶段依赖局部观测(Actor)解决了这一矛盾。MADDPG作为CTDE的代表算法,其Critic网络输入所有智能体的状态与动作以稳定Q值学习,而Actor仅基于自身观测决策。代码实现中,Critic需拼接全局信息,Actor则
摘要:
在多智能体强化学习(MARL)的早期,研究人员在“各自为战(IQL)”的非平稳性与“中央集权(Joint Q)”的通信瓶颈之间进退维谷。直到 CTDE(中心化训练,去中心化执行) 范式的出现,才真正打破了这一僵局。本文将深入解析 CTDE 的设计哲学,并以 OpenAI 经典的 MADDPG 算法为例,讲解如何利用“上帝视角”来训练,同时保证 Agent 在实战中能独立决策。
目录 (Table of Contents)
- 困境:盲人摸象 vs 心灵感应
- 独立学习 (IQL) 的痛点
- 完全中心化的弊端
- 救世主:CTDE 架构详解
- Training:上帝视角的特权
- Execution:战争迷雾的限制
- 生活中的类比:教练与球员
- MADDPG 算法深度剖析
- 从 DDPG 到 Multi-Agent DDPG
- 核心设计:全知全能的 Critic
- 数学推导:如何消除非平稳性
- PyTorch 代码实战
- 网络结构定义
- 关键点:State 与 Action 的拼接
- CTDE 的进化与变体
- MADDPG vs MAPPO
- CTDE 在合作与竞争中的不同表现
- 总结
1. 困境:盲人摸象 vs 心灵感应
在介绍 MADDPG 之前,我们需要理解为什么单智能体算法不能直接用。
1.1 独立学习 (IQL) 的“盲人摸象”
如果我们直接让每个 Agent 运行一个独立的 DQN 或 DDPG:
- 现象:Agent A 发现自己往左走能得分。但下一秒,Agent B 改变了策略,导致 Agent A 往左走撞车了。
- 本质:环境是非平稳的(Non-Stationary)。对于 Agent A 来说, P ( s ′ ∣ s , a ) P(s'|s, a) P(s′∣s,a) 不再固定,因为 s ′ s' s′ 还取决于 Agent B 的动作。
- 结果:训练曲线剧烈震荡,无法收敛。
1.2 完全中心化的“心灵感应”
如果我们把所有 Agent 当成一个超级 Agent 控制的“多只手”:
- 输入:所有人的观测 O = [ o 1 , o 2 , … , o n ] O = [o_1, o_2, \dots, o_n] O=[o1,o2,…,on]。
- 输出:联合动作 U = [ u 1 , u 2 , … , u n ] U = [u_1, u_2, \dots, u_n] U=[u1,u2,…,un]。
- 痛点:这要求在执行(部署)时,所有 Agent 必须实时、无延迟地共享数据。这在现实中(如无人机群、自动驾驶)受限于带宽和延迟,几乎不可能实现。
2. 救世主:CTDE 架构详解
Centralized Training, Decentralized Execution (CTDE) 是目前 MARL 的绝对主流标准。
2.1 核心哲学
- 训练阶段 (Training):我们在模拟器或实验室里,我们是“上帝”。我们可以获取所有 Agent 的状态 S S S、动作 A A A 甚至奖励 R R R。不用白不用! 我们利用这些全局信息来辅助 Critic 学习 value function。
- 执行阶段 (Execution):模型部署后,Agent 只能看到自己的局部观测 o i o_i oi,只能依靠自己的 Actor 网络 π ( o i ) \pi(o_i) π(oi) 做决策。
2.2 生活中的类比
- 足球训练 (Training):教练(Critic)站在高台上,看清了全场的局势。他告诉球员:“刚才那个球,因为对方后卫空档了,所以你传得好。”(利用全局信息计算梯度)。
- 比赛现场 (Execution):教练不能上场。球员(Actor)只能根据自己眼前的视野,凭借训练出来的直觉踢球。
3. MADDPG 算法深度剖析
OpenAI 在 2017 年提出的 MADDPG 是 CTDE 的开山之作,它解决了 DDPG 在多智能体环境下的震荡问题。
3.1 架构图解
- Actor (策略网络) μ i ( o i ; θ i ) \mu_i(o_i; \theta_i) μi(oi;θi):
- 输入:仅局部观测 o i o_i oi。
- 输出:确定性动作 a i a_i ai。
- 作用:负责执行。
- Critic (价值网络) Q i ( x , a 1 , … , a N ; w i ) Q_i(\mathbf{x}, a_1, \dots, a_N; w_i) Qi(x,a1,…,aN;wi):
- 输入:全局信息 x \mathbf{x} x (或所有人的观测) + 所有人的动作 a 1 , … , a N a_1, \dots, a_N a1,…,aN。
- 输出:Q 值。
- 作用:仅用于辅助训练,指导 Actor 更新。
3.2 为什么 Critic 加上别人的动作就“稳”了?
在 IQL 中,Critic 估算 Q ( s , a i ) Q(s, a_i) Q(s,ai)。当 a j a_j aj (队友动作) 变化时, Q Q Q 值剧烈波动。
在 MADDPG 中,Critic 估算 Q ( s , a i , a j ) Q(s, a_i, a_j) Q(s,ai,aj)。此时,即使 a j a_j aj 变了,它也作为输入告诉了 Critic。
P ( s ′ ∣ s , a i , a j ) P(s' | s, a_i, a_j) P(s′∣s,ai,aj)
对于 Critic 来说,只要输入包含了所有影响环境变化的因素,环境就是平稳的!
3.3 梯度更新公式
MADDPG 的 Actor 更新公式如下(注意红色的部分):
∇ θ i J ≈ 1 K ∑ k ∇ θ i μ i ( o i ) ⋅ ∇ a i Q i μ ( x , a 1 , … , a i , … , a N ) ∣ a i = μ i ( o i ) \nabla_{\theta_i} J \approx \frac{1}{K} \sum_k \nabla_{\theta_i} \mu_i(o_i) \cdot \nabla_{a_i} Q_i^{\mu}(\mathbf{x}, a_1, \dots, \mathbf{a_i}, \dots, a_N)|_{a_i=\mu_i(o_i)} ∇θiJ≈K1k∑∇θiμi(oi)⋅∇aiQiμ(x,a1,…,ai,…,aN)∣ai=μi(oi)
- Actor 依然只看 o i o_i oi 更新参数。
- 但是梯度的方向 ∇ a i Q \nabla_{a_i} Q ∇aiQ 是由拥有全局视角的 Critic 指出的。
4. PyTorch 代码实战
下面是一个简化的 MADDPG 核心代码,展示如何处理维度的拼接。
4.1 Critic 网络定义 (上帝视角)
import torch
import torch.nn as nn
import torch.nn.functional as F
class CentralizedCritic(nn.Module):
def __init__(self, n_agents, obs_dim, act_dim):
super(CentralizedCritic, self).__init__()
# 关键点:输入维度 = 所有人的观测之和 + 所有人的动作之和
# 假设所有 agent 维度相同,实际情况可能不同
self.total_input_dim = (obs_dim + act_dim) * n_agents
self.fc1 = nn.Linear(self.total_input_dim, 64)
self.fc2 = nn.Linear(64, 64)
self.q_out = nn.Linear(64, 1)
def forward(self, all_obs, all_acts):
"""
all_obs: [batch, n_agents * obs_dim] -> 拼接好的所有观测
all_acts: [batch, n_agents * act_dim] -> 拼接好的所有动作
"""
# 1. 拼接 State 和 Action
concat_input = torch.cat([all_obs, all_acts], dim=1)
x = F.relu(self.fc1(concat_input))
x = F.relu(self.fc2(x))
return self.q_out(x)
4.2 Actor 网络定义 (局部视角)
class Actor(nn.Module):
def __init__(self, obs_dim, act_dim):
super(Actor, self).__init__()
# 关键点:输入只包含自己的 obs
self.fc1 = nn.Linear(obs_dim, 64)
self.fc2 = nn.Linear(64, 64)
self.action_out = nn.Linear(64, act_dim)
def forward(self, obs):
x = F.relu(self.fc1(obs))
x = F.relu(self.fc2(x))
# 连续动作通常用 Tanh 映射到 [-1, 1]
return torch.tanh(self.action_out(x))
4.3 训练循环中的数据流
# 假设 batch_data 包含: obs_n, act_n, reward_n, next_obs_n
# obs_n 是一个列表,包含 N 个 tensor
# ... (计算 Target Q 等步骤略) ...
# === 更新 Critic ===
# 需要把所有人的 obs 和 act 拼起来喂给 Critic
critic_loss = MSE(target_Q, current_Q(cat(obs_n), cat(act_n)))
critic_optimizer.step()
# === 更新 Actor (以 Agent i 为例) ===
# 1. 重新计算 Agent i 当前的动作 (这就用到了 Autograd)
curr_act_i = actor_i(obs_i)
# 2. 其他 Agent 的动作通常从 Replay Buffer 里取 (Off-policy)
# 或者也用他们的 Actor 预测 (如果在计算 Target)
act_others = sample_from_buffer(...)
# 3. 组合动作,喂给 Critic
# 注意:我们要对 curr_act_i 求导,所以它必须保持计算图
joint_acts = torch.cat([act_others_before, curr_act_i, act_others_after], dim=1)
# 4. 计算 Policy Gradient
# 我们希望 Q 最大化,所以 Loss 是 -Q
actor_loss = -critic_network(cat(obs_n), joint_acts).mean()
actor_optimizer.step()
5. CTDE 的进化与变体
虽然 MADDPG 是鼻祖,但 CTDE 范式已经衍生出了多种变体:
5.1 MADDPG vs MAPPO
- MADDPG (Off-policy):基于 DDPG。样本效率高,但超参数敏感,训练不稳定。
- MAPPO (On-policy):基于 PPO。直接把 PPO 的 Value Function 变成中心化的 (输入全局 State)。
- 现状:令人惊讶的是,尽管 MADDPG 理论上更适合 MARL,但在实战(如星际争霸、足球)中,MAPPO 往往效果更好且更鲁棒。
5.2 合作 vs 竞争
- MADDPG 允许每个 Agent 有独立的 Reward 函数,因此它天然支持竞争(Competitve)和混合(Mixed)环境。
- QMIX/VDN 也是 CTDE 的一种(Mixing Network 是中心化的),但它们通常强制要求单一的团队 Reward,仅限于完全合作场景。
6. 总结
CTDE 范式完美地解决了 MARL 中的核心矛盾:
- 训练时:利用 Critic 的上帝视角,消除了环境的非平稳性,让梯度下降方向准确无误。
- 执行时:Actor 仅依赖局部观测,适应了去中心化的物理约束。
一句话总结 MADDPG:
“训练时我看清了所有人的一举一动来评价你的表现;上场后你只要管好你自己眼前的一亩三分地。”
希望这篇博文能帮你彻底理解 CTDE 和 MADDPG!如有疑问,欢迎在评论区讨论。
更多推荐


所有评论(0)