尧图精选

MADDPG多智能体博弈对抗实战:从环境搭建到训练调参

🕒 发布时间:2026/10/1 3:41:55 📁 来源:尧图网络
简介面向计算机相关专业毕业设计与项目实战需求这份资源提供基于MADDPG多智能体深度确定性策略梯度的多智能体博弈对抗算法Python实现适合正在完成大作业、毕业设计或希望系统掌握多智能体强化学习代码实现的学习者。项目源码均通过本地编译并严格调试可独立运行代码内附详细注释有助于理解DDPG/MADDPG的核心网络结构、经验回放机制、智能体交互与训练循环。资源共14个文件包含10个Python脚本如MADDPG.py、DDPG.py、buffer.py、network.py、rl_utils.py、测试脚本等1个示例环境压缩包以及配置文件与说明文档整体仅1.6MB目录结构清晰紧凑。内容涵盖了从环境构建、参数配置到训练验证的完整流程可作为课程设计、毕业设计或算法对比实验的参考基线。目前已有72人学习浏览该项目经导师指导并获评高分具备较高的完整性与实用价值。1. 多智能体博弈对抗与MADDPG毕业设计把“戏台”搭对才是正经事老读者知道我一贯劝人别一上来就捧着论文死磕公式。毕业设计要做强化学习方向最怕的就是理论说得头头是道、演示时却跑不出像样的对抗效果。MADDPGMulti-Agent Deep Deterministic Policy Gradient恰好卡在这个需求点上它能让你用Python写一套多智能体博弈对抗算法在粒子环境里看到“攻防双方都在学习”的动态过程既不会像端到端大模型那样吃算力又有足够的展示空间给答辩评委看。适合想把算法原理和工程实现都摆在台面上的人。这篇就直接从最干净的环境搭建往对抗逻辑推把代码路径、参数边界和翻车点都摊开讲。2. 跑通最小环境MPE安装、项目骨架和第一个可运行的训练脚本2.1 粒子环境MPE的安装与确认多智能体强化学习做博弈最常用的试验场是 OpenAI 的 particle environment也就是常说的 MPE。它的地图是二维连续空间agent 用连续动作控制加速度天然匹配 DDPG 这类确定性策略算法做对抗博弈演示比 gridworld 直观得多。安装它的前提是 Python 3.8 或 3.9搭配 torch 1.10 到 2.0 之间的版本——太新的 torch 在某些老机器上容易出算子兼容问题。# 创建虚拟环境避免把系统 Python 搞乱 python3.9 -m venv maddpg_env source maddpg_env/bin/activate # 安装 PyTorch CPU 版学习和训练足够不需要 CUDA pip install torch1.13.1 --index-url https://download.pytorch.org/whl/cpu # MPE 依赖 pip 安装注意它内部用了 old-style setup.py pip install multi-agent-particle-envs逻辑说明虚拟环境是为了隔离依赖MADDPG 涉及的包较多直接装在系统环境里容易和别的项目打架。PyTorch 用 CPU 版是为了降低上手成本粒子环境本身计算量小CPU 跑足够。MPE 包内部依赖一些旧式 setup.py 脚本Python 3.10 及以上经常装不上这就是为什么建议用 Python 3.9。安装完成后用一条命令确认环境里有没有真正挂上粒子场景# 快速验证 MPE 是否可用 from multiagent.environment import MultiAgentEnv import multiagent.scenarios as scenarios # 加载经典的 SimpleSpread 场景 scenario scenarios.load(simple_spread.py).Scenario() world scenario.make_world() env MultiAgentEnv(world, scenario.reset_world, scenario.reward, scenario.observation) print(f环境创建成功agent 数量 {env.n})逻辑说明MultiAgentEnv 是 MPE 的基类它把 reset、reward、observation 三个回调函数接在一起对外提供一个类似 Gym 的接口。scenario.reset_world(world)负责每回合重置位置scenario.reward(agent, world)计算单个 agent 的奖励scenario.observation(agent, world)拼接局部观测。madDPG 在训练时不直接访问这些函数而是通过 env.step() 统一推进。参数说明这里的 agent 数量由场景决定simple_spread 默认是 3 个 agent 加 3 个 landmark做博弈演示时需要换成 simple_tag 或 simple_adversary 场景。换场景的方法是修改load(xxx.py)的参数代码骨架完全不用动。2.2 源码目录结构读代码从入口文件开始拿到一份 MADDPG 源码先别急着打开算法文件。我一般会先用 tree 命令把目录结构打出来找清楚训练入口在哪里再顺着调用链往下摸。常见做法是分为 buffer 模块、网络模块、agent 模块和训练主脚本四层。tree -L 2 maddpg_project/ # 预期结构如下 maddpg_project/ ├── README.md ├── maddpg/ │ ├── __init__.py │ ├── ddpg.py │ ├── maddpg.py │ ├── buffer.py │ └── networks.py ├── train.py ├── evaluate.py └── configs.py逻辑说明buffer.py负责经验回放也就是把每个 agent 的 (observation, action, reward, next_observation) 存到共享缓冲里networks.py定义 actor 和 critic 的神经网络结构ddpg.py是一个 agent 的“大脑”包含 actor、critic 以及它们对应的 target 网络maddpg.py是对外暴露的多智能体调度器负责协调多个 agent 各自更新train.py把环境和调度器串起来跑出整个训练循环。很多初学的人直接改maddpg.py里的函数却不知道真正控制训练节奏的是train.py里的 for 循环。2.3 第一个训练脚本跑通最小可执行的动作网络与目标网络初始化与其一上来就训练完整博弈不如先写一个最小脚本确认 actor 网络能输出动作、critic 能打分、target 网络能跟着更新。这一步能过滤掉 80% 的维度报错和初始化疏漏。import torch import torch.nn as nn import numpy as np # 单 agent 的 actor 网络 class Actor(nn.Module): def __init__(self, obs_dim, act_dim, hidden64): super(Actor, self).__init__() self.net nn.Sequential( nn.Linear(obs_dim, hidden), nn.ReLU(), nn.Linear(hidden, hidden), nn.ReLU(), nn.Linear(hidden, act_dim), nn.Tanh() # 把动作压缩到 [-1, 1] ) def forward(self, obs): return self.net(obs) # 单 agent 的 critic 网络输入拼接 所有agent的观测 和 所有agent的动作 class Critic(nn.Module): def __init__(self, obs_dim_all, act_dim_all, hidden64): super(Critic, self).__init__() self.net nn.Sequential( nn.Linear(obs_dim_all act_dim_all, hidden), nn.ReLU(), nn.Linear(hidden, hidden), nn.ReLU(), nn.Linear(hidden, 1) # 输出 Q(s, a) ) def forward(self, obs_all, act_all): return self.net(torch.cat([obs_all, act_all], dim-1)) # 初始化一个 agent obs_dim 4 # 单个 agent 自己的观测维度 act_dim 2 # 连续动作空间的维度例如 x 方向和 y 方向的加速度 obs_dim_all 4 * 3 # 3 个 agent 的观测拼起来 act_dim_all 2 * 3 # 3 个 agent 的动作拼起来 actor Actor(obs_dim, act_dim) actor_target Actor(obs_dim, act_dim) critic Critic(obs_dim_all, act_dim_all) critic_target Critic(obs_dim_all, act_dim_all) # 把 target 网络的参数拷贝到和在线网络一样这是 MADDPG 的常规起点 actor_target.load_state_dict(actor.state_dict()) critic_target.load_state_dict(critic.state_dict()) # 构造一批假数据走一次前向确认维度都通 obs torch.randn(32, obs_dim) # batch32 obs_all torch.randn(32, obs_dim_all) act actor(obs) act_all torch.randn(32, act_dim_all) q critic(obs_all, act_all) print(动作输出维度:, act.shape) # [32, 2] print(Q值输出维度:, q.shape) # [32, 1]逻辑说明这段代码验证了 MADDPG 最核心的“集中训练分布执行”结构。actor 只看自己的 obs 做决策这是执行时每个 agent 的权限critic 在训练时拼接所有 agent 的观测和动作这是集中式评估的核心。target 网络加载在线网络参数保证训练初期 Q 值估算不会太小或太大。Tanh()激活函数把动作限制在 [-1, 1]MPE 环境默认也是用这个范围。参数说明obs_dim 和 act_dim 必须从环境里读。你在真实项目中不要硬编码 4 和 2而是通过env.observation_space[0].shape[0]和env.action_space[0].shape[0]动态获取。如果拼接维度时 obs_all 或 act_all 的维度算错critic 的输入层对不上前向传播直接报错所以先跑这段最小脚本就是打预防针。3. 剖析MADDPG核心组件集中式Critic、Actor更新节奏与经验回放的参数细节3.1 为什么是“集中训练、分布执行”Critic拿全局状态的核心逻辑多智能体博弈最麻烦的问题是环境不稳定每个 agent 的策略都在变对某个 agent 来说其他人的策略改变会让自己的 Q 值估算失效。MADDPG 的解法非常直白——训练 critic 时开“上帝视角”把所有 agent 的观测和动作都喂进去这样 critic 就能感知到对手策略的变化给出一个相对稳定的 Q 值。# maddpg.py 中多智能体调度的核心伪代码结构 class MADDPG: def __init__(self, agent_ids, obs_dim, act_dim, hidden64, tau0.01): self.agents {} for agent_id in agent_ids: self.agents[agent_id] DDPGAgent(obs_dim, act_dim, hidden) self.tau tau # target 网络软更新系数 def update(self, sample, agent_id): obs, act, rew, next_obs, done sample agent self.agents[agent_id] # 当前 Q 值 current_q agent.critic(obs, act) # 下一步动作用 target actor 算 next_action agent.actor_target(next_obs) # 下一步 Q 值用 target critic 算注意拼接所有 agent 的 next obs 和 next action next_q agent.critic_target(next_obs, next_action) # TD 目标 target_q rew self.gamma * next_q * (1 - done) # critic loss 与反向传播 critic_loss nn.MSELoss()(current_q, target_q.detach()) agent.critic_optimizer.zero_grad() critic_loss.backward() agent.critic_optimizer.step()逻辑说明这段代码是整个算法的“心脏”。current_q是当前状态下 critic 的打分next_q是用 target 网络估算的下一步价值TD目标是两者加权的结果。target_q.detach()很关键它把目标值从计算图中分离出来防止梯度流经 target 网络。集中式训练的意义就在这一步虽然 actor 只用自己的 obs但 critic 在训练时能看到所有 agent 的 next_obs 和 next_action相当于把博弈对手的意图纳入了评估。参数说明gamma通常是 0.95 到 0.99 之间。粒子环境里的回合不长gamma0.95 够用如果你改成对抗场景回合拉长可以调到 0.99。done是一个 float 数组MPE 的结束信号通常是碰撞或逃生成功乘以(1 - done)的目的是让终止状态不需要估计未来价值。3.2 策略网络Actor与目标网络的更新节奏Critic 学习了如何评估actor 的学习则要顺着 critic 的梯度往“更高分”的方向调整参数。DDPG 类的算法用的是确定性策略梯度更新 actor 时只传自己 agent 的动作路径。# DDPGAgent 中的 actor 更新 def update_actor(self, obs_all, act_all, agent_id): # 重新算当前策略的动作 new_action self.actor(obs_all[agent_id]) # 拼接所有 agent 的动作但只替换当前 agent 的动作其他 agent 的动作用旧数据 act_all_combined act_all.clone() act_all_combined[:, agent_id * act_dim: (agent_id 1) * act_dim] new_action # critic 给这批“混合动作”打分actor 的梯度方向是让这个 Q 值变大 policy_loss -self.critic(obs_all, act_all_combined).mean() self.actor_optimizer.zero_grad() policy_loss.backward() self.actor_optimizer.step()逻辑说明这段实现展示了 MADDPG 和单智能体 DDPG 的核心区别——actor 更新时要把自己的新动作替换进“全局动作向量”里让 critic 对这个混合动作打分。如果只拿自己的旧动作去算 Q 值梯度方向会偏差训练容易不稳定。act_all_combined是改造的关键先 clone 出旧动作再替换当前 agent 那段。很多翻车现场就是忘记 clone导致其他 agent 的动作也被改了。target 网络更新用软更新方式每个训练 step 都向在线网络靠近一小步def soft_update(self, target, source, tau): for target_param, param in zip(target.parameters(), source.parameters()): target_param.data.copy_(tau * param.data (1.0 - tau) * target_param.data)参数说明tau取值通常在 0.01 到 0.05 之间tau 越大target 网络跟得越紧。这里有个反直觉的坑tau0.01 看起来无害但如果你每步都更新 target前期 Q 值变化太慢actor 很容易在错误的方向上走很久出现“训练很久但 reward 纹丝不动”的怪象。实践里我会在训练前 5000 步用 tau0.05 快速拉近之后再降到 0.01。3.3 经验回放采样把博弈数据做成数据集的三个关键参数经验回放是稳定训练的重要保障多智能体场景里 buffer 的设计又比单智能体复杂一些因为一次 step 会产生多个 agent 的数据它们之间还有时间上的关联性。# buffer.py 中经验回放的核心结构 class ReplayBuffer: def __init__(self, capacity1000000): self.capacity capacity self.buffer [] self.position 0 def push(self, obs_all, act_all, rew_all, next_obs_all, done_all): # obs_all 是 dictkey 是 agent_id if len(self.buffer) self.capacity: self.buffer.append(None) self.buffer[self.position] (obs_all, act_all, rew_all, next_obs_all, done_all) self.position (self.position 1) % self.capacity def sample(self, batch_size): batch random.sample(self.buffer, batch_size) return (torch.cat([item[0][agent_id] for item in batch]) for agent_id in ...)逻辑说明capacity控制历史经验的总量position用环形数组覆盖旧数据。采样时不能只取单个 agent 的经验因为 critic 训练需要“所有 agent 当前时刻的数据”所以 sample 返回的是五个大的 tensor每个 tensor 的第一维是 batch_size第二维是所有 agent 数据的拼接或堆叠。参数说明三个关键参数分别影响训练速度和稳定性capacity经验池容量粒子环境默认 10^6 够用但你的机器内存吃不消的话可以降到 5 * 10^5。容量太小会让训练过程“失忆”早期经验还没被充分利用就被覆盖。batch_size常用 256 或 512。太大梯度稳定但每个 step 的更新偏保守太小训练波动大。我一般从 256 起步不收敛再减半。采样间隔每次环境 step 后不一定立刻更新常见做法是每 100 步更新一次batch_size 次数的梯度下降。一次 step 就立刻更新的做法容易让样本之间有强相关性训练震荡。4. 训练与调参从收敛曲线到对抗强度哪些超参数决定“赢不赢”4.1 奖励设计博弈对抗的关键是“给对手编号”同样一套 MADDPG 代码换奖励函数就可能从“两边一起摆烂”变成“攻守双方越打越精”。粒子环境里每个 agent 的 reward 是独立的传统做法是把 reward 定义为稀疏的即时代价比如碰撞就给 1、逃逸给 10。但博弈对抗不是单纯让每边 reward 变大而是让双方的期望相反——比如追逐者吃到一个“距离惩罚”被追者吃到“距离奖励”两边目标天然冲突。# simple_tag 场景中追逐者的奖励设计 def reward(self, agent, world): reward 0 for other in world.agents: if other.landmark and other.adversary: # 判断是否碰撞 if self.is_collision(agent, other): reward 10 # 抓到猎物加分 else: # 距离越小越好用负距离作为惩罚 reward - self.dist(agent, other) return reward逻辑说明用“负距离”作为连续惩罚是让 agent 学到追赶方向的关键。稀疏的“碰撞加 10”的问题在于初期的随机动作几乎不可能碰到猎物reward 全是 0agent 学不到任何梯度信号。负距离则让每一步都能感受到“我接近了一点”或“我离远了”训练初期更容易引导策略走向正确方向。参数说明奖励幅度的绝对值不宜太大。很多毕设项目把捕食奖励设为 100逃离奖励也设为 100结果两边 reward 数值巨大Q 值估算不稳定。常见做法是把单步奖励控制在 [-1, 1] 之间碰撞事件奖励设为 5 到 10 倍。这样 TD 误差的数值范围健康critic 更容易收敛。4.2 关键超参数表learning_rate、gamma、tau、噪声MADDPG 超参数不像 CNN 那样有很多公开经验值更多是试出来。下面这张表是在粒子环境里比较稳妥的起始配置照着改能省掉很多试错时间超参数推荐起始值调整方向影响范围actor_learning_rate1e-4不收敛则降到 5e-5策略更新的步长太大会震荡critic_learning_rate1e-3可先调大到 2e-3评估网络的适应速度过大会导致 Q 值爆炸gamma0.95回合长则调 0.99长期回报权重值越大越看长远tau0.01前期用 0.05 加速逼近target 网络更新速度noise_scale0.1探索不足则加到 0.3动作空间的探索范围hidden64复杂场景加到 128网络表达能力表格之外还有两个容易忽略的点一是 actor 的优化器用 Adam 就不用调 momentum二是 critic 和 actor 的 learning_rate 不要设成一样大。Critic 需要快速适应环境变化actor 要慢一点保持稳定两者差 5 到 10 倍是常见比例。4.3 训练回放观察 loss、reward 的分布位置来判断何时“翻车”训练循环本身不复杂但每次训练后要从输出的 log 里判断有没有出问题。我把常用的训练循环和判断逻辑放在一起# train.py 中的训练主循环 for episode in range(episode_limit): obs_all env.reset() episode_reward 0 for step in range(max_steps): # 每个 agent 选动作加上探索噪声 action_all {} for agent_id in agent_ids: action maddpg.agents[agent_id].actor(torch.Tensor(obs_all[agent_id])) noise np.random.normal(0, 0.1, action.shape) action_all[agent_id] np.clip(action.numpy() noise, -1, 1) # 环境推进 next_obs_all, reward_all, done_all, _ env.step(action_all) # 存入经验池 buffer.push(obs_all, action_all, reward_all, next_obs_all, done_all) # 每 100 步更新一次网络 if buffer.size() batch_size and step % 100 0: for agent_id in agent_ids: sample buffer.sample(batch_size) maddpg.update(sample, agent_id) obs_all next_obs_all episode_reward sum(reward_all.values()) # 每 100 个 episode 打印一次 if episode % 100 0: print(fEpisode {episode}, reward {episode_reward:.2f})逻辑说明动作加噪声是 MADDPG 的探索机制noise_scale0.1表示在输出动作上叠一个均值为 0、标准差为 0.1 的高斯噪声。np.clip保证动作不超出环境边界 [-1, 1]。更新频率是每 100 步一次这样收集到的样本有一定的多样性不会因为连续几步高度相关而影响梯度。观察 log 时我会特别看三个位置reward 的整体趋势前 1000 局有波动很正常但 3000 局之后如果还在剧烈震荡说明 noise 太大或 learning_rate 太高。critic loss 的数值如果 loss 在 0 附近说明 critic 已经能比较准确地预测 Q 值但此时 actor 不一定好如果 loss 持续上升说明 TD 目标本身不稳定要检查经验池是否污染严重。动作输出分布如果某个 agent 的动作几乎永远卡在边界全输出 1 或全输出 -1可能是 reward 设计有问题导致策略学成了“猛冲”或“摆烂”需要在奖励里加一个小的动作代价约束。5. 常见问题与排查MADDPG训练不收敛的五个原因与对策5.1 环境能跑但 reward 朝负无穷方向走——奖励函数里隐含的“零和陷阱”现象训练一开始还能看到一点轻微波动之后 reward 一路往下掉5000 局后几乎变成 -500 的“负数大坑”。原因奖励函数中双方目标完全相反但“负距离惩罚”被加在了每一步导致每个 step 都会产生一个负的即时代价回合越长累积的惩罚越大。与此同时 actor 又没有快速学到有效躲避/追赶于是整体奖励曲线呈永续下跌。解决给奖励加一个“时间衰减”或者“归一化”。常见做法是把每一步的负距离除以最大可能距离把单步奖励控制在 [-1, 0] 区间同时设置最大回合步数达到步数就强制结束并给一个结束补偿。这样累计奖励的绝对值不会无限膨胀actor 才能从有限的负奖励中找到改进方向。5.2 维度对不上或维度正确但 loss 一直不下降——Critic输入拼接维度错位现象程序能跑但 critic loss 在 0.5 左右抖动怎么调 learning_rate 都压不下去。原因Critic 的输入需要按固定顺序拼接“所有 agent 的观测”和“所有 agent 的动作”如果经验回放里存的动作顺序和 actor 更新时替换动作的顺序不一致critic 接收到的“哪个动作属于哪个 agent”是错乱的等于在用一个错位的训练信号更新网络。解决在 buffer.push 里就把动作按 agent_id 排好序再拼接不要依赖 dict 的迭代顺序。具体做法是# 拼接所有 agent 的动作并保持固定顺序 act_all torch.cat([torch.Tensor(action_all[k]) for k in sorted(action_all.keys())], dim-1)这样不管 dict 内部顺序怎么变拼接顺序始终一致critic 输入的意义也就稳定了。5.3 训练到一半 loss 逐渐升高——经验回放容量与 buffer 污染现象训练初期 reward 在上升3000 局之后突然掉头向下critic loss 同步升高。原因经验池容量太小或者采样不均匀。当 buffer 只有 10 万容量训练到后期新经验不断覆盖早期“优质”数据如果覆盖掉的刚好是训练初期宝贵的多样样本模型就容易突然“失忆”。另外如果 buffer 采样完全随机而早期数据大多是没有意义的探索动作它们占比太高会把 critic 带偏。解决在训练中分段调整采样策略。前 2000 局用随机采样保留多样性和探索性之后改为优先采样 TD 误差较大的样本类似 PER 思想。最简单的改动是加大容量到 10^6且在前 5000 局不更新网络只收集数据。很多 MADDPG 开源实现默认训练开始就更新这种做法在博弈场景下特别容易踩坑。5.4 复现结果波动大——随机种子与 GPU 卡死现象同一份代码跑两次reward 曲线完全不同有时第二次甚至完全学不会。原因缺乏随机种子控制。MPE 环境在 reset 时随机生成坐标actor 和 critic 的初始化权重也带随机性再加上噪声采样三次随机叠加结果自然不可控。解决在所有可能入口统一设置随机种子import random import numpy as np import torch def set_seed(seed42): random.seed(seed) np.random.seed(seed) torch.manual_seed(seed) # 如果用了 CUDA torch.cuda.manual_seed_all(seed)注意光设置 seed 还不够MPE 内部可能使用全局 numpy 随机状态所以还需要在 reset 后额外调用env.seed(seed)。我踩过最深的坑是只 torch 和 numpy 各设了一遍但 MPE 场景里用的是 Python 内置 random导致坐标初始化每次还是不同。5.5 训完的 agent 傻站着不动——target 更新频率和 tau 设太小的过拟合现象reward 曲线很好看但演示时视觉上 agent 就是原地微动。测试时把 noise 关掉动作几乎全为 0。原因target 网络更新太慢导致 critic 对动作的偏好非常驼峰化。动作空间是连续二维的初期网络把所有“改变位置”的输出都评估为低值只有“原地不动”评估为正加上 tau0.01 的缓慢更新actor 就被锁死在原点附近。解决把 tau 调到 0.05并且每 100 步做一次平滑更新而不是每步都更新。“每步都更新”听起来更勤奋实际会让 target 追踪同一个早期错误目标形成路径依赖。另外要在 actor 的 loss 里加一个小的动作正则项比如0.01 * (action ** 2).mean()让策略不要蜷缩在边界上同时也避免输出过于极端的动作。6. 进阶玩法与验证多策略交替、场景迁移与稳定性检查6.1 多策略交替训练用老 snapshot 做“陪练”单次训练的 MADDPG 很容易陷入“双方策略共同漂移”的状态——两个 agent 一起变强或者一起退化你不知道到底是谁在进步。更实用的做法是每 500 局存一个 checkpoint然后用上一轮的旧策略去“陪练”当前策略。# 保存一轮快照 torch.save(maddpg.agents[agent_0].actor.state_dict(), factor_agent0_ep{episode}.pth) # 陪练时用旧快照 old_actor.load_state_dict(torch.load(factor_agent0_ep{episode-500}.pth))逻辑说明这种交替训练能让你看到不同世代的策略强度差异。如果当前策略能稳定压制 500 局前的旧策略说明对抗学习真的在发生如果两边数据接近基尼系数式的互相拉扯说明奖励设计有问题。毕设答辩时拿出这样的对比曲线比单一 reward 曲线有说服力得多。6.2 用 TensorBoard 可视化与 checkpoint 恢复给自己的毕业设计留“后悔药”训练 MADDPG 时reward 曲线平滑不能说明全部问题。把 critic loss、actor loss、每个 agent 的 action 分布都记录到 TensorBoard 里方便随时回看是哪一步开始崩的。from torch.utils.tensorboard import SummaryWriter writer SummaryWriter(./logs) # 每个 episode 记录一次 writer.add_scalar(reward/agent0, episode_reward_agent0, episode) writer.add_scalar(loss/critic, critic_loss.item(), episode) writer.add_scalar(action/noise_scale, current_noise_scale, episode)checkpoint 的恢复是一个极其实用的技巧。训练到一半发现参数配坏了用恢复脚本回到上一个稳定点不至于从头再来def load_checkpoint(maddpg, path): state torch.load(path) for agent_id in maddpg.agents: maddpg.agents[agent_id].actor.load_state_dict(state[factor_{agent_id}]) maddpg.agents[agent_id].critic.load_state_dict(state[fcritic_{agent_id}])逻辑说明恢复时只加载在线网络target 网络可以重新从在线网络复制这样避免 target 网络和在线网络续接不上。如果连 target 也恢复需要 E 大模型那样精确的状态字典反而容易出错。6.3 在 SimpleTag 与 Spread 场景验证博弈对抗到底是不是“对抗”MADDPG 的一个验证维度是同一套代码跑不同博弈场景用来区分“合作”和“对抗”。SimpleTag 是捕食者-猎物对抗场景SimpleSpread 是三个 agent 合作覆盖目标点的场景。把两份实验的 reward 曲线画在一起可以很清晰地展示算法在两种目标模式下的行为差异。# 切换场景的简单封装 def make_env(scenario_name): scenario scenarios.load(f{scenario_name}.py).Scenario() world scenario.make_world() env MultiAgentEnv(world, scenario.reset_world, scenario.reward, scenario.observation) return env如果你做的毕设题目强调“博弈”那最好把两个场景都跑一遍。合作场景验证算法的协调能力对抗场景验证博弈能力分开陈述比混在一起讲更清晰。我的习惯是最后用一组对比表列出合作场景中回合结束时所有 agent 的覆盖面积、对抗场景中捕食者胜率、经验池容量、单局耗时用来和论文里的理论分析互相印证。这套东西做到这里MADDPG 的源码已经不是一个黑匣子了。你手里有可以跑的训练脚本、有能解释的组件拆分、有遇到问题能反推的排查依据。希望这个方向能成为你毕设的一个稳定支点。本文还有配套的精品资源点击获取
上一篇/下一篇内容由系统自动关联 返回资讯列表 →