qinghuan
← Agent 算法与学习 / 全部笔记

单元 42 / Agent 算法与学习

P2 · 按需深入L3 · 深入

多智能体学习:非平稳性与协作信用分配

多个 Agent 一起学习时,各自策略都在变。用 COMA 基线和 Shapley 枚举,分清怎样给策略梯度分配贡献,以及怎样分配合作收益。

本篇目录 · 按需要跳转

这篇重点学什么

本篇重点:先观察队友改变策略后,旧经验为什么可能失效;再学集中训练、分散执行(CTDE)和 COMA,检查各成员的动作对策略更新与合作收益分别有什么影响。

开始读正文 ↓
学过相近内容?查看哪些可以跳过

两条路线有些基础相同。已经学会的部分可以跳过,另一条路线只用来补你当前需要的内容。

多 Agent 协作

共同基础角色之间会交换信息并共享结果;工程编排不等于联合策略训练。

另一条路线补充什么?

安排固定角色的权限、依赖、通信与结果合并,判断是否需要多个 Agent。

读完这篇,你能做什么

验证反事实优势的条件期望为零,核对策略梯度,并用Java分解有限联盟收益。

学习理由、准备要求与知识点

为什么学它

给多个角色分别写提示,不自动等于让多个智能体学习。团队任务完成了,也不能据此认定每个角色都有独立贡献。

先读这些内容

知识点清单

  • 独立学习的非平稳性
  • 集中训练分散执行
  • 局部观测与全局critic
  • COMA反事实基线
  • 优势梯度停止
  • QMIX单调约束
  • Shapley边际贡献
  • 联合动作指数规模

运行前先确认论文机制按文中版本说明;Java 17 完整算法,Python 3.14 与 PyTorch 2.9.1+cpu 的小型 CPU 实验。未执行 GPU、多机或大规模模型训练。

01先弄懂它是什么

多个 Agent 都在学习时,一个成员面对的队友也会不断改变。例如昨天队友通常先查询资料,今天却学会直接作答,自己的旧经验就未必还能预测接下来会发生什么。这是多智能体学习中“环境不再保持原样”的一种来源,叫非平稳性。它不同于只把任务分给固定角色、依次调用模型的工程安排,因为这里各成员的策略都在更新。

为了更好地判断团队表现,训练时的评价器可以读取联合状态和动作,这叫集中训练;真正执行时,各成员仍只看约定的局部观测和允许交流的信息,这叫分散执行。例如训练时可以利用完整团队记录评估贡献,但上线后不能因此让某个成员看见原本无权读取的内容。若这些训练专用信息混入在线策略,测试分数再高,也不能支持原先声称的信息边界。

给团队成员分配贡献,要先说清在比较什么。COMA 固定队友这一步的动作,只替换当前成员可能采取的动作,按它自身策略的概率算平均表现,再看实际选择比这个平均好多少,用来更新策略。Shapley 值则先约定不同成员组合能获得多少收益,再看某成员按各种顺序加入团队时分别增加多少收益,据此分配合作收益。

两者都在问“换一种选择会怎样”,但前者比较动作,后者比较成员加入联盟的贡献;计算条件和用途不同,不能把 Shapley 分数直接称作 COMA 优势。

文中这几个词,先认一认

联合动作
同一个决策时刻,所有成员各自选择的动作合在一起的结果。
集中critic
训练时用来估计团队后续累计回报的计算部分,可以参考比单个成员更多的状态和动作信息。这些额外信息供训练时评价使用,不代表每个成员实际执行时也能看见它们。
条件基线
先固定队友已经选择的动作,再把当前成员可选的每个动作分别代入团队评价器,按该成员的选择概率算平均回报。这个平均值用来和它实际选中的动作比较,不是把所有候选动作简单平均,也不是重新改变队友的选择。

还有陌生词?到术语小抄里看一个具体例子 ↗

02看看它怎么工作

设两个成员动作分别为a和b,联合价值表Q有A×B个元素。对成员一,固定b,基线为sum_a pi_1(a)×Q(a,b),优势为实际Q减去这个基线。基线不依赖成员一实际抽中的动作,所以按自己的策略求优势均值为零;这能保留期望策略梯度,但不意味着每个样本都零,也不消除critic估计误差。

计算策略损失时,把优势当作已经算好的学习信号,不再让梯度经过基线和 critic 回传。Python 实验只有两个成员、每人两个动作,因此可以列出全部四个联合选择;用当前联合概率作已停止梯度的权重,比较 COMA 形式的梯度与团队奖励精确期望的梯度。之后更新两个 logit 表,只有双方都选一才得奖励一。这个实验没有训练 critic,也没有多步时序轨迹。

QMIX走的是价值分解路线,限制联合价值对各局部价值单调不减,使局部贪心动作与混合价值的全局贪心一致。这是一种结构条件,表达能力也因此受限;它不是所有协作任务都能精确表示的保证。阅读高级方法前,先能画清谁看到什么、谁在更新以及信用信号来自哪里。

先跑通一个最小例子

PyTorch CPU执行experiment.py,枚举两个成员的四个联合动作并更新100步。精确Q来自已知奖励表,未训练critic。

experiment.py · python
import torch
torch.set_num_threads(1)
torch.manual_seed(42)
logits=torch.nn.Parameter(torch.zeros((2,2),dtype=torch.float64))
q=torch.tensor([[0.,0.],[0.,1.]],dtype=torch.float64)
opt=torch.optim.SGD([logits],lr=1.)
def objects():
    p=logits.softmax(-1)
    joint=p[0,:,None]*p[1,None,:]
    b0=(p[0,:,None]*q).sum(0,keepdim=True)  # 固定队友动作,对自己的动作求期望。
    b1=(p[1,None,:]*q).sum(1,keepdim=True)
    a0=q-b0;a1=q-b1
    surrogate=(joint.detach()*(p[0].log()[:,None]*a0.detach()+p[1].log()[None,:]*a1.detach())).sum()
    return p,joint,a0,a1,surrogate
p,joint,a0,a1,surrogate=objects()
assert torch.allclose((p[0,:,None]*a0).sum(0),torch.zeros(2,dtype=torch.float64))
assert torch.allclose((p[1,None,:]*a1).sum(1),torch.zeros(2,dtype=torch.float64))
g=torch.autograd.grad(surrogate,logits,retain_graph=True)[0]
exact=torch.autograd.grad((joint*q).sum(),logits)[0]
assert torch.allclose(g,exact,atol=1e-12)
for _ in range(100):
    opt.zero_grad();loss=-objects()[-1]
    loss.backward();opt.step()  # 使用固定精确Q表,未训练集中式critic。
p,joint,_,_,_=objects()
assert (joint*q).sum().item()>.95
assert torch.allclose(p.sum(-1),torch.ones(2,dtype=torch.float64))
assert torch.isfinite(logits).all()
print(f"expected_team_reward={(joint*q).sum().item():.6f}")
print("PASS 6 checks; two-agent one-step exact critic, not full COMA training")

核对运行结果expected_team_reward大于.95,PASS 6 checks;条件基线与精确期望梯度一致。

正在加载流程图…

训练期联合评价提供信用信号,执行期各角色仍只能使用允许的局部观测。
查看流程图源码(Mermaid)
flowchart TD
  N0["列明每个成员的可见信息"]
  N1["固定联合任务和奖励定义"]
  N2["集中评价并计算成员条件基线"]
  N3["停止优势梯度后更新局部策略"]
  N4["独立检验执行信息和团队结果"]
  N0 --> N1
  N1 --> N2
  N2 --> N3
  N3 --> N4

按顺序看,每一步做了什么

  1. 列明每个成员的可见信息
  2. 固定联合任务和奖励定义
  3. 集中评价并计算成员条件基线
  4. 停止优势梯度后更新局部策略
  5. 独立检验执行信息和团队结果

03对照实现代码

COMA反事实优势公式 (新标签页)↗

COMA arXiv:1705.08926v3 · Section 4,Equation 4与策略梯度式5

原创COMA单步公式模型,非完整PyMARL节选;Java实现独立Shapley定义而非COMA。

核心机制代码 · python
def actor_loss(probabilities, values, chosen):
    baseline = (probabilities * values).sum().detach()  # 固定队友动作,仅平均当前成员动作。
    advantage = (values[chosen] - baseline).detach()  # actor更新不穿过critic或基线。
    return -probabilities[chosen].log() * advantage  # 单个已采样动作的策略梯度项。

固定论文公式四定义成员特有基线:队友动作保持不变,对当前成员动作按自己的策略求和。中央critic估计这些备选动作的价值,避免每次都真实执行所有反事实。这里的反事实质量因此受critic影响,不能把预测值当成已经发生的环境结果。

作者PyMARL的COMALearner.train可对应阅读baseline与advantages的detach、动作可用性掩码和回合填充掩码。本文短函数省略批次和时序,只保留单样本策略项;完整实践必须先屏蔽非法动作并重新归一化,且确保每个有效状态至少有一个可用动作。

PyMARL仓库master是阅读入口,机制固定依据论文v3。Python CPU实验使用已知四格奖励作为精确单步Q,明确跳过critic拟合、目标网络、TD估计和循环actor。它验证公式及自动微分路径,没有复现星际环境结果,也没有证明复杂语言团队收敛。

Java的Shapley枚举来自合作博弈定义:对不含成员i的每个联盟S,累加v(S并i)-v(S),权重为|S|!×(n-|S|-1)!/n!。这是另一种分析工具,允许检查对称成员、无贡献成员和总分守恒;联盟如何构造、缺席角色由谁替代都必须先定义。

代码先调用谁,再调用谁

  1. 中央critic输出当前成员所有动作价值
  2. 按可执行动作概率计算条件基线
  3. 取得实际动作的优势并停止梯度
  4. 更新只接收局部信息的actor

04动手写一个例子

有限联盟表的精确Shapley贡献

输入n个成员及长度2^n的联盟价值表,返回每个成员的加权边际贡献;限制1≤n≤16,值为有限且绝对值不超过1e100。

输入 1
n=2,v=[0,2,3,5]
输出 1
[2,3]
输入 2
n=2,v=[0,0,0,8]
输出 2
[4,4]

联盟使用位掩码表示,第i位为一表示成员i在场。对每个成员只枚举不包含它的掩码,比较加入前后价值。大小为k的联盟统一使用k!×(n-k-1)!/n!权重,可以通过递推生成,避免显式计算阶乘溢出。

算法遍历的正是所有可能前驱联盟,每个联盟权重对应随机加入顺序中它恰好排在成员之前的概率。因此结果等于所有加入顺序的平均边际贡献。任一完整顺序的边际贡献相消为v(全集)-v(空集),再平均仍保持效率性质;它并不要求空集价值为零。

成员数为n时输入就有2^n项,每个成员检查全部掩码,固定字宽位计数下为O(n×2^n)。额外权重与结果数组为O(n),但输入表仍是指数空间。上限十六是实验资源约束,不代表大规模联盟可以低成本精确计算;采样排列是后续近似方向。

为什么这样写是对的每个成员的累加器包含已枚举且不含该成员的联盟边际贡献乘正确大小权重。

时间 O(n×2^n)额外空间 O(n)额外空间,输入表O(2^n)

n次扫描联盟表;Java17整数位计数在本程序n≤16范围按常数时间计。

保存为 Main.java,运行 javac --release 17 -encoding UTF-8 Main.java,再运行 java Main。程序包含固定输入自测,断言失败时会抛出错误。

展开收起代码 · Main.java · 完整可运行程序Java · 38 行

长行可在代码区域内左右滚动,按 Tab 可把焦点移入代码区。

Main.java · 完整可运行程序 · Java
import java.util.*;
public class Main {
    static void near(double expected,double actual) {
        if (!Double.isFinite(actual) || Math.abs(expected-actual)>1e-9*Math.max(1,Math.abs(expected)))
            throw new AssertionError(expected+" != "+actual);
    }
    static void check(boolean ok) { if(!ok) throw new AssertionError(); }
    static void bad(Runnable r) {
        try { r.run(); } catch(IllegalArgumentException e) { return; }
        throw new AssertionError("invalid input accepted");
    }
static double[] shapley(int n,double[] value) {
        if(n<1||n>16||value==null||value.length!=(1<<n)) throw new IllegalArgumentException("coalitions");
        for(double x:value) if(!Double.isFinite(x)||Math.abs(x)>1e100) throw new IllegalArgumentException("value");
        double[] weight=new double[n],result=new double[n];weight[0]=1.0/n;
        for(int k=1;k<n;k++) weight[k]=weight[k-1]*k/(n-k); // 权重等于k阶乘乘其余人数阶乘除n阶乘,递推避免大阶乘。
        for(int agent=0;agent<n;agent++) {
            int bit=1<<agent;
            for(int mask=0;mask<value.length;mask++) if((mask&bit)==0) { // 只枚举尚未包含该成员的联盟。
                int size=Integer.bitCount(mask);
                result[agent]+=weight[size]*(value[mask|bit]-value[mask]); // 对所有加入顺序对应的边际贡献加权。
            }
        }
        return result;
    }
    public static void main(String[] args) {
double[] a=shapley(2,new double[]{0,2,3,5});near(2,a[0]);near(3,a[1]);
        double[] b=shapley(2,new double[]{0,0,0,8});near(4,b[0]);near(4,b[1]);
        double[] v=new double[8];for(int m=0;m<8;m++)v[m]=((m&1)!=0?1:0)+((m&2)!=0?2:0);
        double[] c=shapley(3,v);near(1,c[0]);near(2,c[1]);near(0,c[2]);
        double[] d=shapley(2,new double[]{4,6,7,9});near(5,d[0]+d[1]);
        double[] e=shapley(2,new double[]{0,-2,-3,-5});near(-2,e[0]);near(-3,e[1]);
        near(7,shapley(1,new double[]{3,10})[0]);
        bad(()->shapley(17,new double[1]));
        bad(()->shapley(1,new double[]{0,Double.NaN}));
        System.out.println("PASS 8 cases");
    }
}

这些特殊情况也要试一试

  • 空联盟价值可非零
  • 零贡献成员返回零
  • 负贡献允许
  • 人数和非有限价值拒绝
进阶选读 · 多角色消融与近似联盟估计

场景团队人数增加,完整联盟评估不可承担。

做法预定义缺席角色的替代行为,采样成员排列并复用固定任务种子;报告平均边际贡献与区间,同时对比固定总调用预算的单Agent基线。

复杂度M条排列需O(Mn)次联盟求值,若一次评估耗时C,总成本O(MnC),缓存最多涉及2^n种联盟。

好处与代价近似降低计算但保留方差,评价依赖替代策略;贡献分配不直接证明因果必要性或公平性。

05项目里怎么用

多Agent研究应先控制任务分解与计算预算。把一个Agent调用一次与四个Agent各调用多次比较,很难判断提升来自协作机制还是额外计算。可设置同预算单策略、多次采样、固定角色团队与学习团队等基线,并分别记录通信量、延迟和失败传播。

训练日志需要包含所有策略版本,旧经验由哪些队友生成也应可追踪。集中critic可能在训练分布内看似准确,却对新的合作伙伴估计失真;因此交叉配对不同种子或不同阶段成员能检查是否只学会与固定队友共适应。参数共享减少开销,但身份与局部观测仍影响行为。

信息与执行权限必须跟角色边界一致,中央训练可用数据不自然获得线上共享许可。本文两个参数表没有通信通道、语言协作或故障恢复;联盟表也由示例给定,并未运行真实团队。高级拓展适合完成单Agent评测和可回放环境之后再做,以免同时引入过多难以定位的变化。

容易踩的坑

  • 角色编排被称为已完成多Agent训练
  • 中央状态泄漏到局部actor
  • 优势未detach改变梯度目标
  • 忽略队友更新导致旧经验漂移
  • 把指数联盟枚举写成线性

几种做法,怎么选

表格有四列,窄屏可左右滑动查看代价和适用条件。

方案优势代价与限制适用条件
固定角色编排易控制和审计不能自动学习协作工程任务分工基线
COMA式集中评价提供成员条件信用依赖critic质量和训练稳定性可观测联合奖励的协作
单调价值分解局部贪心执行方便联合价值表达受限适合所述结构假设的任务

06检验是否读懂了

先自己想、自己写,再展开答案核对。场景题是假设的练习情境;遇到还没学过的内容,可以做完基础后再回来。

先用自己的话解释

01集中训练允许部署看全局吗?

展开参考答案与推导

不自动允许,执行actor仍应只接收声明的局部信息。

02优势期望零为何仍能学习?

展开参考答案与推导

动作对应的log概率梯度不同,优势加权后的梯度通常非零。

03Shapley等于COMA优势吗?

展开参考答案与推导

不等于,前者平均联盟边际贡献,后者固定队友动作构造成员条件基线。

换个情况,还会处理吗

01团队扩容反而变差如何查?

展开参考答案与推导

先控制总预算,再查非平稳性、通信噪声和信用估计质量。

02移除角色怎样定义联盟价值?

展开参考答案与推导

提前规定替代或缺席语义,用同样任务和预算评估,避免随结果改规则。

03训练期表现好换队友失败?

展开参考答案与推导

检查共适应与版本分布,用交叉配对和保留伙伴验证。

写代码试一试

01计算协同价值分配。

输入 / 条件
仅两人同时在场得8
输出 / 预期
各4

提示 枚举加入顺序

展开参考答案与推导

各人在一半顺序中贡献8,另一半为0。

02证明基线性质。

输入 / 条件
固定队友,按自己策略平均优势
输出 / 预期
零

提示 概率和为一

展开参考答案与推导

Q的策略均值与基线相减抵消。

03核对计算规模。

输入 / 条件
n成员全部联盟表
输出 / 预期
2^n个输入项

提示 位掩码枚举

展开参考答案与推导

运行时间O(n×2^n),不能忽略建立价值表的真实实验成本。

读完之后,独立完成

课后作业:自己做一遍

本篇共 8 道作业,先完成 6 道基础与综合任务,再按前置知识挑战进阶题。其中包含 1 道思维拓展,试着换个条件,看看原来的办法是否还管用。不附参考答案。

这次练什么

验证反事实优势的条件期望为零,核对策略梯度,并用Java分解有限联盟收益。

本篇重点加练

集中训练的全局信息不能泄漏到部署策略,重点检验信息可用性而非只比较训练分数。

  • 集中训练分散执行
  • 局部观测与全局critic

重点内容会多练几次。先说清楚怎么做,再试试空数据、出错或条件改变时会发生什么。

先自己做,卡住时记下问题,再回来查。问答题写清“为什么、什么时候适用”;动手题留下输入、输出和运行步骤,方便下次重做。

基础必做

  1. 作业 01原理问答

    设计两个协作智能体的小矩阵游戏,分别定义局部观测、联合状态、联合动作和训练期 critic 输入,标出部署期不可用信息。

    提交内容
    CTDE 信息流图
    怎样算完成
    • 训练部署边界明确
    • 队友变化作为环境变化考虑

    练到的知识点独立学习的非平稳性 · 集中训练分散执行 · 局部观测与全局critic

  2. 作业 02代码推演

    两智能体各有动作0、1,联合价值表Q=[[1,4],[3,0]](行对应智能体1),策略π1=(0.3,0.7)、π2=(0.6,0.4),执行联合动作(1,0)。分别手算两智能体的COMA基线及优势,并声明求导边界。

    提交内容
    数值表与梯度图
    怎样算完成
    • 固定其他动作的条件明确
    • 优势计算与梯度路径分开

    练到的知识点局部观测与全局critic · COMA反事实基线 · 优势梯度停止

综合应用

  1. 作业 03编程实践

    实现 CPU 双智能体离散策略梯度实验,比较无基线与 COMA 基线的梯度样本统计,保留共同随机种子和联合动作。

    提交内容
    程序及梯度统计报告
    怎样算完成
    • 策略分布相同
    • 均值与波动分开检验

    练到的知识点COMA反事实基线 · 优势梯度停止

  2. 作业 04排错实验

    构造包含协同收益的 2×2 联合价值表,检查它与所选单调混合表示的兼容性;用小型穷举验证局部贪心结果。

    提交内容
    价值表、检验与反例说明
    怎样算完成
    • 单调条件明确
    • 不能表示的情形不被隐藏

    练到的知识点QMIX单调约束 · 联合动作指数规模

  3. 作业 05面试场景

    三个角色存在冗余和协同,定义每个联盟的价值并计算贡献分配;说明该定义与策略动作信用的区别及评估成本。

    提交内容
    联盟表与分配记录
    怎样算完成
    • 空联盟与缺席行为明确
    • 贡献概念不混用

    练到的知识点Shapley边际贡献 · 联合动作指数规模

  4. 作业 06排错实验

    在 CPU 双智能体小策略中固定某个智能体的局部观察,分别改变仅 critic 可见的全局字段,测试部署 actor 输出;注入误用全局字段的版本作为对照。

    提交内容
    信息隔离测试与故障记录
    怎样算完成
    • actor 的可用输入已声明
    • 训练辅助与执行依赖分开

    练到的知识点集中训练分散执行 · 局部观测与全局critic

进阶挑战 · 掌握前置后选做

  1. 作业 07设计评审

    在训练后固定一个智能体,替换不同队友策略进行评价;报告协作泛化与非平稳训练的区别,限制为 CPU 小游戏。

    提交内容
    队友替换实验
    怎样算完成
    • 训练和评价阶段分开
    • 角色数量与预算明确

    练到的知识点独立学习的非平稳性 · 集中训练分散执行 · 局部观测与全局critic

思维拓展

先写出你的猜想,再用例子或实验验证。答案可以不同,但要说清依据、适用情况,以及还有什么没弄懂。

  1. 作业 08思维拓展 · 反例构造

    构造两个全局状态,对某智能体的局部观察相同但最优动作不同。在禁止通信且无额外记忆时,集中 critic 是否足以保证分散策略总能选对?

    提交内容
    不可区分状态对与条件论证
    怎样算完成
    • 信息约束具体
    • 训练能力与部署可观测性分开

    练到的知识点集中训练分散执行 · 局部观测与全局critic

完成后,再检验一次

给每题标记“独立完成 / 查阅后完成 / 尚未完成”。查过资料或没做出的题,记下卡在哪里。隔一天换一组输入再试,能解释结果和出错原因,再把这个知识点标为掌握。

继续阅读

先看能帮你完成当前练习的部分。例子跑通后,再读更深入的解释。

打开资料库,找下一份学习材料 ↗