Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

5 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Exploring Value Function Factorisation in Multi-Agent Actor-Critic Methods

Tests Python PyTorch License

COMP390 Final Year Project · University of Liverpool · Multi-Agent RL | MAPPO | VDN | QMIX | PyTorch | PettingZoo

在 MAPPO 这类 actor-critic 方法里,critic 通常是一个吞下 global state 的单体网络。这个项目问的是一个具体问题:把 VDN 的加性分解和 QMIX 的单调 mixing 搬进 MAPPO 的 critic,训练行为会怎么变,代价是什么。

项目 2025 年完成于利物浦大学,2026 年整理为当前作品集仓库:补齐依赖锁定、CPU 测试和统一入口,并修正了原 Predator-Prey wrapper 中合作边界不清的问题。


一句话结论

在这两个任务上,表达能力更强的 critic 没有赢。 最简单的加性分解(MAPPO-VDN)在稀疏奖励的 Predator-Prey 上是唯一学到正回报的方法,而单调 mixing 的 MAPPO-QMIX 在同一预算下 critic loss 反复爆炸、始终没有收敛。

方法 Critic 结构 Matrix game Predator-Prey (300k steps) 主要瓶颈
mappo 单体 V(s) 三个 payoff 都能解,纯合作场景收敛最慢 从 −300 爬到 −30,未转正 高维 state 上 critic 拟合噪声大,无 credit assignment 结构
mappo_vdn ΣᵢVᵢ(oᵢ) 稳定收敛到最优,critic loss 最低 唯一转正,终值 ≈ 150 加性假设无法表达 agent 间非线性耦合
mappo_qmix f_mix(Qᵢ; s),单调 合作任务可解,mixed-motive 上最差 在 −300~0 间震荡,最差 target 用 max_a Qᵢ 引入 off-policy 偏差;hypernet 梯度不稳

数据来自 2025 年的实验,实验条件与图表说明见 results/thesis/


训练框架

三种方法共用同一套 runner / replay buffer / shared RNN actor,只有 critic 分支不同。训练采用 CTDE:执行时 actor 只看各 agent 的局部观测,全局状态只在训练 critic 时使用。

Runner-Learner 数据流

flowchart LR
    O["Local obs o_i"] --> A["Shared RNN actor"]
    A --> B["EpisodeBatch"]
    S["Global state s"] --> B
    B --> C1["mappo critic<br/>V of s"]
    B --> C2["mappo_vdn critic<br/>sum of V_i of o_i"]
    B --> C3["mappo_qmix critic<br/>QMixer over Q_i, conditioned on s"]
    C1 --> T["TD-lambda target<br/>advantage = lambda-return minus V"]
    C2 --> T
    C3 --> T
    T --> U["PPO clipped update"]
    U --> A
Loading

三者共用同一条更新流程:先用 target critic 构造 TD(λ) 目标,critic 做一次 masked MSE 更新,再把 λ-return − V 当作 advantage 喂给 PPO 的 clipped surrogate 跑 ppo_epoch 轮。actor、runner、buffer 和 PPO 超参完全相同,差异集中在 critic 分支,这是这个对比实验的设计前提。


三条技术路线与各自的瓶颈

1. MAPPO baseline — 单体中心化 critic

技术路线. critic 是一个 MLP,直接从 global state 回归标量状态值:

$$V_{\text{tot}}(s) = \text{MLP}(s)$$

advantage 用 TD(λ) 回报减去当前估值,A_t = G^λ_t − V(s_t),等价于 GAE(λ)。

实现见 mappo.pymappo_learner.py:52

实验中的行为. 在三个 matrix game 上都能解,但在纯合作的 [[2,0],[0,5]] 上反而是四种方法里唯一没有稳定停在最优的(见下方图)。在 Predator-Prey 上 critic loss 长期在 5k–15k 之间剧烈抖动,回报从 −300 缓慢爬到 −30 但没转正。

瓶颈.

  1. state 表示没有结构先验。 本项目的 Predator-Prey global state 是 5 个 agent 观测 padding 后的拼接(predator_prey.py:121),维度随 agent 数线性增长,critic 要从这个扁平向量里直接学出 V。稀疏奖励下这等于在噪声里拟合一个高方差目标。
  2. 没有 credit assignment。 所有 agent 共用同一个标量 advantage 广播到 policy loss(advantages shape [B,T,1],靠广播覆盖 n_agents 维),policy gradient 无法区分谁的动作造成了这一步的好坏。这是 VDN / QMIX 想要解决的核心问题。
  3. critic 目标本身随策略漂移。 target critic 每 target_update_interval 步硬拷贝一次,在 on-policy 数据分布快速变化时,目标滞后会放大 TD 误差的方差。

2. MAPPO-VDN — 加性分解

技术路线. 把 critic 的输入从 global state 换成各 agent 的局部观测,每个 agent 一个独立的 value head,全局值取和:

$$V_{\text{tot}}(\mathbf{o}) = \sum_{i=1}^{n} V_i(o_i)$$

关键改动只有两行——critic(state) 变成 critic(obs).sum(dim=2)

# src/learners/mappo_vdn_learner.py:53
target_vals = self.target_critic(obs).sum(dim=2)   # [B, T+1]
values      = self.critic(obs)[:, :-1].sum(dim=2)  # [B, T]

实现见 mappo_vdn_critic.pymappo_vdn_learner.py:52

实验中的行为. 三个 matrix game 上都稳定收敛到最优附近,critic loss 是三者中最低的。Predator-Prey 上表现最突出:critic loss 全程平稳在 ~1000 量级,回报在 2×10⁴ 步内转正,最终稳定在 ~150 且方差带很窄——是四种方法里唯一学到正回报的

为什么在这里有效. 加性结构给了 critic 一个强的归纳偏置:每个 Vᵢ 只需要从自己的观测里学一个低维函数,梯度也只沿着自己那条通路回传。在 Predator-Prey 这种"每个 predator 的贡献大体可加"的任务里,这个偏置恰好和任务结构吻合,于是它同时拿到了低方差和快收敛。

瓶颈.

  1. 加性假设即表达能力上限。 V_tot 是各 Vᵢ 的和,无法表达"两个 agent 同时做某事才有价值"这类非线性耦合。任务一旦需要联合条件(紧耦合协作、anti-coordination),这个 critic 结构上就写不出正确的值函数。
  2. 丢掉了 global state。 VDN critic 只吃 oᵢ,训练期本来可用的全局信息被主动放弃了。在部分可观测且局部观测信息量不足的任务里,这会引入不可消除的偏差——这是 QMIX 用 state 生成 mixing 权重想补回的部分。
  3. 仍然只产出一个共享 advantage。 虽然 critic 分解成了 Vᵢ,但 learner 里 advantage 是从求和后的 V_tot 算的标量,各 agent 拿到的仍是同一个数。也就是说分解带来的收益来自表示与梯度路径,而不是真正的 per-agent credit assignment——这是我在整理时才看清楚的一点,也是最直接的改进方向。

3. MAPPO-QMIX — 单调 mixing network

技术路线. 每个 agent 的 local critic 输出动作值向量 Qᵢ(oᵢ, ·),取出实际执行动作对应的 Qᵢ,再由一个以 global state 为条件的 hypernetwork 生成的 mixing net 合成 Q_tot

$$Q_{\text{tot}} = f_{\text{mix}}\big(Q_1(o_1,a_1),\dots,Q_n(o_n,a_n);\big|;s\big),\qquad \frac{\partial Q_{\text{tot}}}{\partial Q_i}\ge 0$$

单调性由 softplus 约束混合权重非负实现(qmix.py:65)。局部 critic 与 mixer 一起用 TD(λ) 训练,实现见 mappo_qmix_learner.py

实验中的行为. matrix game 上可以解合作任务,但 critic loss 早期先冲高到 173 再回落,全程震荡。Predator-Prey 上 critic loss 起手爆到 5.4×10⁴,回落后在 200k 步之后再次发散;回报在 −300~0 之间大幅震荡,是四种方法中最差的。

瓶颈.

  1. target 用 max_a Qᵢ,与 on-policy 的 actor 不匹配。 这是我认为最关键的一条:

    # src/learners/mappo_qmix_learner.py:107
    max_qs_all = target_local_qs.max(dim=3)[0]                # 贪婪动作
    q_tot_target_all = self.target_critic(max_qs_all, state_all)

    Q_tot_target 估的是贪婪策略的值,Q_tot_curr 估的是行为策略实际执行动作的值,两者相减得到的 td_error 不是一个合法的 on-policy advantage,而是系统性偏乐观的量。这解释了实验里反复出现的"回报冲过理论最优值再崩塌"。这个设计是从 value-based QMIX 直接搬过来的,搬进 PPO 后语义就不成立了。

  2. hypernet 放大了梯度尺度。 mixing 权重由 state 经 hypernet 生成后过 softplussoftplus(0) ≈ 0.69,初始化时权重并不接近零;Q_tot 对 local Qᵢ 的梯度被这层乘性结构放大,是 critic loss 起手就冲到 5×10⁴ 的直接原因。代码里只能靠 clip_grad_norm 和调低 critic_lr 压住。

  3. 参数量与样本效率不匹配。 local critics(每 agent 一个)+ hypernet + mixer 的参数量远超前两者,而 PPO 是 on-policy、样本利用率低。300k 步的预算对这个规模的 critic 明显不够——图上 Predator-Prey 的 QMIX 曲线在训练末段才开始往上走,说明它可能只是没跑完。

  4. 单调性约束在 mixed-motive 任务上是偏差来源。 ∂Q_tot/∂Qᵢ ≥ 0 意味着任何 agent 提升自身效用都不会降低团队价值。在 [[1,2],[2,0]] 这种最优解要求两个 agent 选不同动作的场景里,这个假设不成立,QMIX 在该任务上确实是四者中最差的。

论文里还实现了一个去掉单调约束的 non-monotonic MLP mixer。它在初步实验中持续梯度爆炸、未能在预算内收敛,因此没有进入最终评测,也没有收进这个仓库。把这个负结果写出来比藏起来更有价值:它说明在 actor-critic 里,结构性归纳偏置不是可有可无的装饰,而是训练能否稳定的前提


实验结果

以下四组图来自 2025 年的实验。Matrix game 每 episode 25 步,理论最优回报 125;ireinforce 是论文里的无 critic baseline,当前仓库未包含。

Matrix game [[2,-2],[-2,5]] — 误协调有惩罚

Test return Critic loss

四种方法都在 ~2000 步内解决了这个任务,差异很小且大多落在阴影带内。值得注意的是 critic loss:mappo_qmix 先冲到 173 才回落,反映了 hypernet 初期的不稳定;mappo_vdn 中段 loss 最低。

Matrix game [[2,0],[0,5]] — 误协调无惩罚(仓库中的 matrix_game_sparse

Test return Critic loss

去掉惩罚项后,单体 critic 的 mappo 反而成了唯一没有稳定停在 125 的方法(绿线,长期在 100–110 震荡且置信带很宽)。两种 factorised critic 都能锁死最优解。这是"结构化 critic 降低 advantage 方差"最直观的一张图。

Matrix game [[1,2],[2,0]] — 非对称 mixed-motive

Test return Critic loss

这个 payoff 的联合最优要求两个 agent 选不同动作(每步 2,25 步共 50)。四种方法都卡在 30 附近,没有一个接近 50。

一个值得记录的观察:若两个 agent 共享同一个对称随机策略、以概率 p 选动作 0,则期望单步回报为 −3p² + 4p,在 p = 2/3 处取最大值 4/3,25 步合计 33.3——恰好是图中 mappo 停住的位置。这提示瓶颈可能不在 critic 分解方式,而在于共享 actor 没能打破 agent 间的对称性。这只是一个与数值吻合的解释,需要用 per-agent 策略消融来验证;当前配置下 actor 可以通过 agent id one-hot 打破对称,因此不能算定论。

mappo_qmix 在此任务上最差(~20),与单调性约束在 anti-coordination 结构上的理论缺陷一致。

Predator-Prey — 高维、部分可观测、稀疏奖励(300k steps)

Test return Critic loss

差异在这里被完全拉开:

  • mappo_vdn(红)在 2×10⁴ 步内转正,终值 ~150,方差带窄,critic loss 全程平在 ~1000。
  • mappo(蓝)从 −300 爬到 −30,始终为负,critic loss 在 5k–15k 之间持续抖动。
  • mappo_qmix(橙)最差,critic loss 起手爆到 5.4×10⁴,200k 步后二次发散。
  • ireinforce(绿,无 critic)在 −100~0 间跳变,无法在稀疏奖励下学习。

这组图是整个项目的主要结论:在稀疏奖励 + 高维状态下,一个结构简单、梯度路径干净的 critic,比一个表达能力更强但优化困难的 critic 更有用。


环境

环境 配置名 说明
Matrix game matrix_game payoff [[2,-2],[-2,5]],误协调受罚,2 agent × 2 action,episode 25 步
Sparse matrix game matrix_game_sparse payoff [[2,0],[0,5]],不匹配得 0
Predator-Prey predator_prey 基于 MPE2 simple_tag_v3

Predator-Prey 的 adapter(predator_prey.py)做了一处重要修正:原始 Simple Tag 是 mixed cooperative-competitive 环境,2025 年的 wrapper 把 predators 和 prey 一起交给同一个 learner,合作边界不清。当前版本只把 3 个 predators 暴露给 learner,prey 由可设 seed 的固定随机策略驱动,3 个 predators 接收其原生奖励的均值作为 shared team reward。


安装与运行

需要 Python 3.10 或 3.11。推荐使用 uv

uv sync --extra dev && source .venv/bin/activate

uv.lock 将 PyTorch 固定为官方 CPU wheel,无需 CUDA。需要 GPU 时另建环境安装匹配本机 CUDA 的 PyTorch。

训练入口统一:

python src/main.py --config=<algorithm> --env-config=<environment> with use_cuda=False seed=1 t_max=10000
python src/main.py --config=mappo_vdn --env-config=matrix_game with use_cuda=False seed=1 t_max=10000
python src/main.py --config=mappo_qmix --env-config=predator_prey with use_cuda=False seed=1 t_max=300000

短跑冒烟验证(秒级返回):

python src/main.py --config=mappo --env-config=matrix_game with use_cuda=False t_max=30 batch_size=1 buffer_size=1 test_nepisode=1

Sacred 把新日志写到 results/sacred/。绘制回报曲线(test_return_mean 是评估回报,return_mean 是训练回报,critic_loss / policy_loss / advantage_mean 用于看训练动态):

python scripts/plot_results.py --path results/sacred --metric test_return_mean --save-dir results/figures

同一 environment / 算法下的多个 run 会自动聚合:实线为跨 seed 均值,阴影为 ±1 标准差。换 seed 重复跑几次即可直接得到带波动范围的对比图。


测试

pytest

覆盖三部分:matrix game 的 payoff 与 episode termination;Predator-Prey 的 cooperative agent boundary 与 seed 可复现性;三个 learner 在合成 episode batch 上各跑一次 CPU 梯度更新并检查 loss 有限。


后续方向

当前结果指向三个可以直接设计实验去回答的问题:

  1. on-policy target 对 factorised critic 的影响。 MAPPO-QMIX 的 Q_tot 目前用 max_a Qᵢ 做 bootstrap,估的是贪婪策略的值,而 actor 是 on-policy 的。把它换成执行动作对应的值,可以直接检验过估计是不是 Predator-Prey 上 QMIX 不收敛的主因——这是一个单变量对照实验,也是我认为最有信息量的一步。

  2. 把分解的收益转化为 credit assignment。 三个 learner 目前都只产出一个共享的标量 advantage,factorised critic 的结构优势停留在表示层面。引入 per-agent advantage(如 COMA 式 counterfactual baseline),可以检验"分解 critic"和"分配信用"这两件事各自贡献了多少。

  3. 更强的证据强度。 把三个 learner 的 advantage 处理对齐到同一基线,跑多 seed 并给出带置信区间的 return 对比,让 Predator-Prey 上的排序从"观察到的趋势"变成"可统计检验的结论"。

再往外一层,值得试的是把 mixing network 换成 attention 或图结构以获得可解释的 agent 间依赖,以及把 team size 推到 10 个 agent 以上——加性分解的优势是否随规模保持,是这套结论最直接的压力测试。


来源与许可证

本项目基于 Oxford WhiRL 的 PyMARL,按 Apache License 2.0 发布。上游代码、个人改动和清理范围记录在 UPSTREAM.md。Predator-Prey 使用 MPE2 的 PettingZoo Parallel API。

Author: Leyan Li

About

MAPPO experiments with VDN- and QMIX-style value factorisation

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages