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 时使用。
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
三者共用同一条更新流程:先用 target critic 构造 TD(λ) 目标,critic 做一次 masked MSE 更新,再把 λ-return − V 当作 advantage 喂给 PPO 的 clipped surrogate 跑 ppo_epoch 轮。actor、runner、buffer 和 PPO 超参完全相同,差异集中在 critic 分支,这是这个对比实验的设计前提。
技术路线. critic 是一个 MLP,直接从 global state 回归标量状态值:
advantage 用 TD(λ) 回报减去当前估值,A_t = G^λ_t − V(s_t),等价于 GAE(λ)。
实现见 mappo.py 与 mappo_learner.py:52。
实验中的行为. 在三个 matrix game 上都能解,但在纯合作的 [[2,0],[0,5]] 上反而是四种方法里唯一没有稳定停在最优的(见下方图)。在 Predator-Prey 上 critic loss 长期在 5k–15k 之间剧烈抖动,回报从 −300 缓慢爬到 −30 但没转正。
瓶颈.
- state 表示没有结构先验。 本项目的 Predator-Prey global state 是 5 个 agent 观测 padding 后的拼接(predator_prey.py:121),维度随 agent 数线性增长,critic 要从这个扁平向量里直接学出
V。稀疏奖励下这等于在噪声里拟合一个高方差目标。 - 没有 credit assignment。 所有 agent 共用同一个标量 advantage 广播到 policy loss(
advantagesshape[B,T,1],靠广播覆盖n_agents维),policy gradient 无法区分谁的动作造成了这一步的好坏。这是 VDN / QMIX 想要解决的核心问题。 - critic 目标本身随策略漂移。 target critic 每
target_update_interval步硬拷贝一次,在 on-policy 数据分布快速变化时,目标滞后会放大 TD 误差的方差。
技术路线. 把 critic 的输入从 global state 换成各 agent 的局部观测,每个 agent 一个独立的 value head,全局值取和:
关键改动只有两行——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.py 与 mappo_vdn_learner.py:52。
实验中的行为. 三个 matrix game 上都稳定收敛到最优附近,critic loss 是三者中最低的。Predator-Prey 上表现最突出:critic loss 全程平稳在 ~1000 量级,回报在 2×10⁴ 步内转正,最终稳定在 ~150 且方差带很窄——是四种方法里唯一学到正回报的。
为什么在这里有效. 加性结构给了 critic 一个强的归纳偏置:每个 Vᵢ 只需要从自己的观测里学一个低维函数,梯度也只沿着自己那条通路回传。在 Predator-Prey 这种"每个 predator 的贡献大体可加"的任务里,这个偏置恰好和任务结构吻合,于是它同时拿到了低方差和快收敛。
瓶颈.
- 加性假设即表达能力上限。
V_tot是各Vᵢ的和,无法表达"两个 agent 同时做某事才有价值"这类非线性耦合。任务一旦需要联合条件(紧耦合协作、anti-coordination),这个 critic 结构上就写不出正确的值函数。 - 丢掉了 global state。 VDN critic 只吃
oᵢ,训练期本来可用的全局信息被主动放弃了。在部分可观测且局部观测信息量不足的任务里,这会引入不可消除的偏差——这是 QMIX 用 state 生成 mixing 权重想补回的部分。 - 仍然只产出一个共享 advantage。 虽然 critic 分解成了
Vᵢ,但 learner 里 advantage 是从求和后的V_tot算的标量,各 agent 拿到的仍是同一个数。也就是说分解带来的收益来自表示与梯度路径,而不是真正的 per-agent credit assignment——这是我在整理时才看清楚的一点,也是最直接的改进方向。
技术路线. 每个 agent 的 local critic 输出动作值向量 Qᵢ(oᵢ, ·),取出实际执行动作对应的 Qᵢ,再由一个以 global state 为条件的 hypernetwork 生成的 mixing net 合成 Q_tot:
单调性由 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 之间大幅震荡,是四种方法中最差的。
瓶颈.
-
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 后语义就不成立了。 -
hypernet 放大了梯度尺度。 mixing 权重由 state 经 hypernet 生成后过
softplus,softplus(0) ≈ 0.69,初始化时权重并不接近零;Q_tot对 localQᵢ的梯度被这层乘性结构放大,是 critic loss 起手就冲到 5×10⁴ 的直接原因。代码里只能靠clip_grad_norm和调低critic_lr压住。 -
参数量与样本效率不匹配。 local critics(每 agent 一个)+ hypernet + mixer 的参数量远超前两者,而 PPO 是 on-policy、样本利用率低。300k 步的预算对这个规模的 critic 明显不够——图上 Predator-Prey 的 QMIX 曲线在训练末段才开始往上走,说明它可能只是没跑完。
-
单调性约束在 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,当前仓库未包含。
| Test return | Critic loss |
|---|---|
![]() |
![]() |
四种方法都在 ~2000 步内解决了这个任务,差异很小且大多落在阴影带内。值得注意的是 critic loss:mappo_qmix 先冲到 173 才回落,反映了 hypernet 初期的不稳定;mappo_vdn 中段 loss 最低。
| Test return | Critic loss |
|---|---|
![]() |
![]() |
去掉惩罚项后,单体 critic 的 mappo 反而成了唯一没有稳定停在 125 的方法(绿线,长期在 100–110 震荡且置信带很宽)。两种 factorised critic 都能锁死最优解。这是"结构化 critic 降低 advantage 方差"最直观的一张图。
| 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 结构上的理论缺陷一致。
| 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/activateuv.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=10000python src/main.py --config=mappo_vdn --env-config=matrix_game with use_cuda=False seed=1 t_max=10000python 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=1Sacred 把新日志写到 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 有限。
当前结果指向三个可以直接设计实验去回答的问题:
-
on-policy target 对 factorised critic 的影响。 MAPPO-QMIX 的
Q_tot目前用max_a Qᵢ做 bootstrap,估的是贪婪策略的值,而 actor 是 on-policy 的。把它换成执行动作对应的值,可以直接检验过估计是不是 Predator-Prey 上 QMIX 不收敛的主因——这是一个单变量对照实验,也是我认为最有信息量的一步。 -
把分解的收益转化为 credit assignment。 三个 learner 目前都只产出一个共享的标量 advantage,factorised critic 的结构优势停留在表示层面。引入 per-agent advantage(如 COMA 式 counterfactual baseline),可以检验"分解 critic"和"分配信用"这两件事各自贡献了多少。
-
更强的证据强度。 把三个 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








