摘要
在多智能体强化学习(MARL)的早期,研究人员在“各自为战(IQL)”的非平稳性与“中央集权(Joint Q)”的通信瓶颈之间进退维谷。直到 CTDE(中心化训练,去中心化执行) 范式的出现,才真正打破了这一僵局。本文将深入解析 CTDE 的设计哲学,并以 OpenAI 经典的 MADDPG 算法为例,讲解如何利用“上帝视角”来训练,同时保证 Agent 在实战中能独立决策。


目录 (Table of Contents)

  1. 困境:盲人摸象 vs 心灵感应
    • 独立学习 (IQL) 的痛点
    • 完全中心化的弊端
  2. 救世主:CTDE 架构详解
    • Training:上帝视角的特权
    • Execution:战争迷雾的限制
    • 生活中的类比:教练与球员
  3. MADDPG 算法深度剖析
    • 从 DDPG 到 Multi-Agent DDPG
    • 核心设计:全知全能的 Critic
    • 数学推导:如何消除非平稳性
  4. PyTorch 代码实战
    • 网络结构定义
    • 关键点:State 与 Action 的拼接
  5. CTDE 的进化与变体
    • MADDPG vs MAPPO
    • CTDE 在合作与竞争中的不同表现
  6. 总结

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(ss,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(ss,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)} θiJK1kθ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 中的核心矛盾:

  1. 训练时:利用 Critic 的上帝视角,消除了环境的非平稳性,让梯度下降方向准确无误。
  2. 执行时:Actor 仅依赖局部观测,适应了去中心化的物理约束。

一句话总结 MADDPG
训练时我看清了所有人的一举一动来评价你的表现;上场后你只要管好你自己眼前的一亩三分地。”


希望这篇博文能帮你彻底理解 CTDE 和 MADDPG!如有疑问,欢迎在评论区讨论。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐