简介:MADDPG(多智能体深度确定性策略梯度)博弈对抗算法的Python源码及实验成果包,面向人工智能、机器学习方向的高校学生、科研人员及强化学习入门开发者。资源内置ma-gym多智能体环境库,覆盖combat战斗、predator_prey追捕等经典博弈与协作场景,可直接用于训练演示、算法对比,也可作为课程设计、毕业设计的基础框架。压缩包共91个文件,核心为76个Python源码,涵盖MADDPG.py、DDPG.py、network.py、buffer.py等主程序及经验回放、网络结构模块;另含10个gif训练动图、1个ipynb交互式演示、1个md说明文档与配置信息,整体仅3.26MB。目前已有92人浏览学习。从算法模型、训练逻辑到环境配置链路完整,训练过程可视化动图直观呈现收敛效果,便于理解MADDPG在多智能体博弈中的工作机制,适合调参实验、科研探索与项目初期演示参考。
1. 把MADDPG从论文变成可跑的代码:这份资源到底给了什么?
如果你正打算做多智能体博弈对抗相关的研究或课设,大概率听过 MADDPG 这个名字。它是 OpenAI 提出的多智能体强化学习基线算法,专门解决传统 DDPG 在对抗博弈里环境非平稳、智能体互相干扰导致收敛困难的问题。这份名为「Python 实现的 MADDPG 多智能体博弈对抗算法:源码及实验结果.zip」的资源,就是把论文里的伪代码落成了可以实际训练、测试的 Python 工程。
解压之后你会发现,它不只是孤零零一个算法文件,而是带了一整套配套环境库 ma-gym,里面包含了 predator_prey、combat、switch、pong_duel 等多个博弈场景,还有训练主程序、DDPG 单智能体基线、经验回放 buffer、网络定义、测试脚本和运行日志。适合刚接触多智能体强化学习的学生用来对标 DDPG 理解 MADDPG 的改进点,也适合需要快速出实验结果的从业者直接改改参数跑自己的场景。接下来我会按“源码结构 → 运行方法 → 结果解读 → 避坑 → 迁移扩展”的路径,把这个资源的成色和坑位一次性给你交代清楚。
2. 拆解源码结构:MADDPG核心组件与环境配套
2.1 目录与文件角色:从zip解压后先看清这张地图
拿到压缩包后先别急着双击运行,花几分钟看清楚目录结构比什么都重要。这个包的根目录下同时存在两个层次的代码:一层是 MADDPG 算法本身的实现,另一层是 ma-gym 环境库(源码里也带了 ma-gym-master.zip 和已经解压好的 ma_gym 目录)。核心算法文件直接放在根目录下:
| 文件名 | 角色说明 |
|---|---|
| MADDPG.py | 多智能体强化学习主算法,包含 agent 的创建、训练循环、目标网络更新 |
| DDPG.py | 单智能体 DDPG 算法,用于对照实验,也是 MADDPG 的基座 |
| network.py | 定义 Actor 和 Critic 神经网络结构,包括 MLP、RNN 等可选层 |
| buffer.py | 经验回放缓冲区,存储并采样 (状态、动作、奖励、下一状态) 四元组 |
| main.py | 训练入口,负责加载环境、初始化算法、执行多轮 episode |
| test.py 与 test_env.py | 测试脚本,验证训练出的模型与环境本身是否正常 |
| rl_utils.py | 软更新(soft update)、噪声生成等辅助函数 |
| ceshi.py | 个人调试脚本,用于快速跑通流程 |
ma_gym 环境库的目录层级是重头戏:ma_gym/envs/下包含了 lumberjacks(伐木工多人收集)、combat(团队对战)、predator_prey(捕食者-猎物,最经典的对抗博弈)、switch(开关切换)、checkers(跳棋)、traffic_junction(交通路口)、pong_duel(乒乓球对抗)。每个环境目录内是__init__.py加上环境实现文件,顶层还有wrappers/(观测与环境预处理)和monitor.py(策略可视化记录)。这份资源相当于把“算法 + 环境 + 工具链”整合在了一个压缩包里,少了你自己去 GitHub 凑齐依赖的麻烦。
2.2 MADDPG算法核心:DDPG基座、Buffer与network.py的搭建逻辑
MADDPG 的全称是 Multi-Agent Deep Deterministic Policy Gradient,思路是“中心化训练,去中心化执行”。每个智能体有自己的 Actor 网络,只基于自身观测输出确定性动作;但训练时 Critic 网络可以拿到所有智能体的状态和动作,从而评估当前策略在全局环境下的 Q 值。这样做的好处是训练阶段解决了环境非平稳问题——因为 Critic 看到了全局,不会因为别的智能体策略变化而产生错误的梯度信号。
在代码里,DDPG.py 实现了最基本的那一套:Actor 基于状态输出动作,Critic 将状态和动作拼接后输出 Q 值,经验回放 buffer 采样小批量更新,再用tau参数做软更新把目标网络参数一点点贴合在线网络。MADDPG.py 则在这个基础上做了三重修改:第一,为每个智能体单独维护一组 Actor-Critic;第二,Critic 的输入改为全局拼接后的状态和所有智能体的动作;第三,奖励设计允许每个智能体有自己的 reward 函数,不要求完全一致。
network.py里定义网络结构时,我建议你重点看两个地方:一是build_mlp函数,它决定了每个智能体的隐藏层层数和神经元数量,默认配置对简单博弈够用,但遇到高维观测时需要加深;二是是否启用了 RNN 层,如果环境部分可观测,比如 switch 场景里智能体看不到全局开关状态,就要靠循环网络记忆历史观测。buffer.py的实现相对标准,但它记录数据时用了next_obs_all这种全局下一观测字段,这是 MADDPG 的 Critic 更新需要的,不能把它和单智能体 buffer 混用。
2.3 ma-gym环境库:六个场景提供了博弈对抗的测试场
ma-gym 环境库是这个资源里容易被低估的一部分。很多人下载焦点全在算法,实际上没有适合多智能体博弈的环境,算法跑起来就没有对比意义。以predator_prey为例,环境里定义了一个二维栅格世界,猎物与捕食者同场运动,捕食者每个时间步决定上下左右移动或静止,猎物同样有自己的策略(默认是随机逃逸,也可以换成交互式智能体)。这个场景直接对应博弈对抗里的“合作围捕”与“目标逃避”,是验证 MADDPG 收敛能力的标准试金石。
combat环境则更偏团队对抗:两队智能体在栅格地图上互相攻击,初始位置、攻击范围、血量衰减都写死在 env 文件里。traffic_junction是交通路口的车辆协同,考验多智能体在共享空间下的避碰策略。这些环境都遵循 gym 的接口规范,你可以直接用env.reset()、env.step(actions)来驱动,也支持渲染成 gif(源码里 static gif 目录和 scripts/record_environment.py 就是干这个的)。环境层面还挂了wrappers,用来改写观测空间、增加时间限制、标准化奖励等,这些包装器是训练多智能体时最容易出 bug 的地方,后面避坑章细说。
3. 动手运行:从环境配置到第一个训练结果
3.1 环境准备:Python版本、依赖安装和pyvenv.cfg的提示
我在复现这个资源时用的是 Python 3.8 和 3.9 两个版本,都能顺利跑通。注意压缩包里有一个pyvenv.cfg文件,这是虚拟环境配置文件,说明原开发者在 venv 里装过依赖;如果你直接用系统 Python,请先确保安装了必要的库。常见依赖是 numpy、torch、gym 和 matplotlib,其中 gym 的版本需要与 ma-gym 的接口匹配,老代码通常兼容 gym 0.26 以下版本,新版本 gym 改了 API 容易报 Warning,但不影响本次训练核心。
我一般会先新建一个干净的虚拟环境,避免和项目里的.cfg打架:
python -m venv maddpg_env source maddpg_env/bin/activate # Windows 下执行 maddpg_env\Scripts\activate pip install numpy torch gym matplotlib然后进入解压后的根目录,把 ma-gym 库安装到环境里。常见做法是对ma-gym-master目录执行可编辑安装:
pip install -e ./ma-gym-master这里的-e是 editable 模式,之后你在 ma_gym 里改环境代码,不需要重新安装就能生效。装完之后在 Python 里执行import ma_gym能成功导入,就说明环境库已经挂上了。千万别跳过这一步,直接运行main.py大概率会提示找不到ma_gym.envs模块。
3.2 快速启动:用main.py跑起MADDPG训练
环境配好之后,我建议先用默认参数跑一个最小 demo,确认整个链路是通的。源码里的main.py已经封装好了主要入口,常见用法是直接指定环境名和训练轮数:
python main.py --env predator_prey --episodes 5000如果你的 main.py 没有实现命令行解析,也可以直接改文件末尾的__main__区块,把环境名和总回合数硬编码进去,然后python main.py。跑起来后你会看到类似这样的输出:
Episode: 100, Total reward: -85.4 Episode: 200, Total reward: -62.1 Episode: 300, Total reward: -48.9 ... Episode: 5000, Total reward: 126.3reward 从负值慢慢转正,说明智能体正在从“乱撞”变成“有策略地围捕”。在这个过程里,MADDPG.py的train函数会依次做这些事:重置环境、循环执行act选出动作、调用env.step得到奖励和下一状态、把数据塞进 buffer、每积累一定步数后采样一个小批量更新每个智能体的网络。你要观察的核心指标就是每个 episode 的总奖励,以及结束步数(越短说明越快完成任务)。
启动训练前有一步容易被忽略:检查test_env.py。如果新版 gym 的 Space 接口有变化,这个测试脚本可能会报维度不对。我每次拿到新环境都会先跑一遍python test_env.py,确认env.observation_space和env.action_space能够被正确读取,再进训练环节,能省掉很多排错时间。
3.3 修改网络与超参数:让训练更稳的常用调参清单
如果你希望在这份源码基础上调出自己的结果,重点参数基本都在MADDPG.py的初始化函数和main.py顶部常量里。我整理了最值得动的几个量,以及它们在代码中的影响:
| 参数 | 常见取值范围 | 影响 |
|---|---|---|
| 学习率 | 1e-4 到 1e-2 | 太高会导致 Q 值爆炸,太低收敛极慢 |
| 批大小 batch_size | 256 到 1024 | 越大越稳定,但显存占用高 |
| 经验池容量 buffer_capacity | 1e5 到 1e6 | 过小会让旧经验反复干扰训练 |
| 折扣因子 gamma | 0.9 到 0.99 | 越大越看重远期收益 |
| 软更新系数 tau | 0.001 到 0.01 | 越小目标网络更新越慢,越稳定 |
| 探索噪声 sigma | 0.1 到 0.3 | 初始探索强度,后期应下降 |
以predator_prey为例,如果你想追求稳定收敛而不是快速出图,我会建议把batch_size调到 512,tau设为 0.005,训练周期至少 20000 个 episode。很多博客用 5000 episode 给出漂亮曲线,但那通常是把环境尺寸调小了,或者人为降低了猎物逃跑速度。如果你发现 reward 一直不涨,先检查 noise 是不是太大导致策略被噪声淹没,再检查 Critic 输入拼接时是否把动作维度搞错了。
network.py中隐藏层默认是[128, 128]两层全连接,这在简单栅格场景里够用。如果换成traffic_junction这种需要更多上下文的环境,我一般会改成[256, 256],同时把recurrent=True打开,让每个智能体用自己的历史观测来推算其他智能体的位置。别小看这个开关,它直接影响 MADDPG 在部分可观测环境下的表现。
4. 实验结果怎么看:收敛曲线与对抗行为分析
4.1 训练日志与reward曲线:先判断收敛还是发散
训练完成之后,你需要面对的第一个问题是“这结果到底算好还是算坏?”资源里没有附带现成的训练曲线图,但源码里的main.py通常会把每个 episode 的累计奖励打印或保存下来。我会把这些奖励值导出到 CSV,再用 matplotlib 画成平滑曲线。如果曲线整体趋势向上而且后段波动幅度收窄,就说明 MADDPG 学到了稳定的策略;如果曲线一直横向抖动,没有任何上升迹象,那大概率是超参数没配对,或者环境奖励太稀疏。
判断收敛不能只看最后阶段的绝对值,还要看训练过程中是否出现“突然掉坑”。比如训练了两千个 episode 之后 reward 突然从 50 跌到 -100,然后再也爬不起来,这种情况常见于经验回放池里混入了太多早期“垃圾经验”,或者在更新时把 Actor 的探索噪声一步调得过低。我处理这类问题的方法是降低学习率,或者让噪声按sigma = max(0.1 * (1 - t / total_time), 0.01)线性衰减,而不是立刻归零。
还有一种隐蔽的假收敛:reward 曲线很好看,但跑test.py时智能体在随机初始化状态下表现极差。这说明训练时智能体只是记住了固定开局下的最优路径,没有真正学会泛化博弈策略。要验证的话,我会在测试阶段把环境初始位置设为随机,比如修改predator_prey里的reset()函数,让猎物初始位置在整张地图随机采样,再看模型是否依然有效。
4.2 不同场景下的博弈行为:从predator_prey到combat
这个资源最有价值的地方在于它把 MADDPG 绑定了多个博弈对抗场景,你可以直接观察同一套算法在不同奖励结构下的行为差异。以predator_prey为例,训练好的捕食者策略通常是“分头包抄”:一个捕食者从左侧逼近,另一个从右侧绕后,而不是所有捕食者都往猎物当前位置冲。这是因为 Critic 在训练时学到了如果多个捕食者挤在同一格附近,会重复浪费步数,而协作围捕能更快抓住猎物。
combat场景则展现了另一种模式:团队内的智能体会根据自身血量自动调整攻击倾向,血量低的会更倾向于后撤到安全区,血量高的则主动追击。这种行为不是人为设定的,完全是奖励函数中个体奖励和团队奖励加权后,Critic 通过梯度信号引导出来的。我在实际跑的时候会把combat的团队奖励系数从 0.5 调到 1.0,观察智能体是否变得更加“牺牲自我”——这正是多智能体博弈里最值得调的一个旋钮。
switch场景是这几个环境里最考验可观测性的一个。它要求智能体去拨动某个开关,但单个智能体看不到开关当前是开还是关。如果不打开network.py的 RNN 开关,MADDPG 在这种环境下会表现得很差,因为它的 Critic 虽然能看到全局,但 Actor 输入只有局部观测,所以必须记住历史信息才能推理出开关状态变化。用这份资源做实验,你可以同时对比“有 RNN”和“无 RNN”两种情况,把这个差异写进课设报告里,会比单纯贴代码显得深入很多。
5. 多智能体强化学习的避坑指南:常见问题与排查
5.1 坑1:训练不收敛,reward始终在负数附近震荡
现象:跑了三五千个 episode,累计奖励一直在 -100 到 -50 之间横跳,没有任何上升趋势。
原因:最常见的是学习率过大导致 Actor 更新过猛,策略在每次更新后剧烈变化;其次是经验池太小,旧经验被频繁覆盖,导致 Critic 反反复复忘掉之前学的东西。
解决:先把 Critic 和 Actor 的学习率都降到 1e-4,然后检查buffer.py的容量设置,确保至少能存下 10 万个时间步的数据。我还会把batch_size提高到 512,并且让每个 episode 结束后强制更新几次,而不是攒很多步才更新一次。
5.2 坑2:运行报错ModuleNotFoundError: ma_gym.envs或No module named 'ma_gym'
现象:输入python main.py后直接退出,提示找不到 ma_gym 模块或某个具体环境名。
原因:没有把 ma-gym 安装到当前 Python 环境,或者安装时用了错误的目录。压缩包里既有ma-gym-master文件夹,也有一个ma-gym-master.zip,有些朋友只解压了算法部分,忘了解压环境库。
解决:先确认ma_gym目录存在于你当前运行的目录下,然后执行pip install -e ./ma-gym-master。如果还报错,检查环境名是否写错,比如predator_prey在ma_gym/envs下真实存在,而combat对应的目录是combat,不要带_env后缀。
5.3 坑3:GPU 显存溢出或 CPU 训练慢到怀疑人生
现象:TORCH 报 CUDA out of memory,或者 CPU 模式下一个 episode 要好几秒,5000 个 episode 遥遥无期。
原因:MADDPG 是多智能体算法,每个智能体都有自己的 Actor 和两个 Critic(在线与目标),如果同时开 6 个智能体,模型数量翻倍,显存压力比单智能体大很多。
解决:优先减小batch_size,比如从 1024 降到 256;其次把隐藏层从[256, 256]收缩到[128, 128]。如果没有 GPU,就老老实实把环境尺寸调小,比如把predator_prey的 grid size 从 10x10 改成 7x7,这会直接降低观测维度与动作组合空间,训练速度能快好几倍。
5.4 坑4:测试时表现远不如训练,仿佛换了个模型
现象:训练代码里打印的 reward 已经很高,但用test.py或渲染成 gif 后,智能体表现得像没学过一样。
原因:测试时没有关掉探索噪声,导致确定性策略被噪声污染;或者测试环境重置时随机种子不同,而模型只在训练时那几种开局下有效。
解决:在测试模式下把动作选择改成policy = self.actor(obs)直接取输出,不要加任何噪声。同时固定测试环境的随机种子,比如在test_env.py开头调用env.seed(42)。还有一个常见做法是把测试智能体的初始位置固定几组,分别验证鲁棒性。
5.5 坑5:结果无法复现,两次训练曲线完全不同
现象:完全相同的代码和超参数,两次训练得到的 reward 曲线差异很大,甚至趋势都不同。
原因:没有设置全局随机种子。PyTorch、NumPy、Python 自带的 random 模块各有一套随机数生成器,只要有一个没固定,训练结果就不可复现。
解决:在main.py最开头加这么几行:
import random import numpy as np import torch random.seed(42) np.random.seed(42) torch.manual_seed(42) if torch.cuda.is_available(): torch.cuda.manual_seed_all(42)需要注意,即使设置了这些种子,某些环境在gym.make时还会引入自己的随机源,最好也在env.seed(42)上加一道保险。以后每次跑新实验,我都强烈建议把随机种子、参数配置、代码版本一起记进实验表格,不然三个实验贴在一起,根本说不清差异是来自算法还是来自随机性。
6. 进阶:把MADDPG迁移到自己定义的博弈对抗场景
如果只是跑通现有的 predator_prey,那这份资源的价值还没完全榨干。真正值得做的是把自己任务抽象成多智能体博弈环境,然后复用这套 MADDPG 代码训练。我会在ma_gym/envs/下新建一个目录,比如my_soccer,里面放一个my_soccer.py,类继承gym.Env。核心是实现四个方法:reset()返回初始观测,step(actions)接收所有智能体的动作并返回全局奖励,render()用来可视化,close()清理资源。
环境写好之后,关键是把观测空间设计成 numpy 数组,形状为(num_agents, feature_dim)。MADDPG.py 里读取观测的代码通常是这样:
obs = env.reset() obs_all = np.vstack(obs) # 所有智能体的观测拼接如果你的智能体观测维度不同,比如一个智能体有雷达数据,另一个只有位置信息,直接拼接会出问题。常见做法是统一用多层感知机把每个智能体的观测映射到相同的特征维度,然后再拼接。你可以复用network.py里的build_mlp函数,为不同智能体分别建立输入编码器,这是对源码最常用的扩展方向。
训练自己的环境时,还有一个值得实验的技巧:给每个智能体设置差异化奖励。比如在对抗博弈里,攻击方奖励设计为“对敌方造成伤害 +0.5,自己存活 +0.1”,防守方奖励设计成“拦截攻击 +1.0,失血 -0.3”。MADDPG 允许每个 agent 的reward函数独立,这种差异化奖励会让对抗行为更鲜明。我在迁移场景时,通常会先用单智能体 DDPG.py 跑一遍简单版本,确认环境步进和奖励逻辑没 bug,再切到 MADDPG.py 上多智能体训练。这样一旦训练失败,你能清楚地知道问题是出在环境还是出在多智能体协调上。
测试迁移效果时,我会先把render()打开,人工看一眼动作是否符合直觉,再用record_environment.py录制一段运行过程。从那以后我每次迁移新场景都强制走一遍“环境自测、单智能体试跑、多智能体调参、可视化复盘”这四个步骤,这套流程帮我避开了至少五次“看似在训练、实则环境在报错”的翻车。希望这个思路也能帮到你少走弯路。
本文还有配套的精品资源,点击获取