MADDPG多智能体强化学习Keras实现与协同训练原理
简介本资源是基于Keras框架实现的多智能体强化学习算法MADDPGMulti-Agent Deep Deterministic Policy Gradient完整代码工程面向深度学习与强化学习进阶学习者、多智能体系统研究者及机器人协同控制实践者。项目解决了多智能体环境下策略协同优化难题适用于交通调度、群体机器人、多人博弈等需联合决策的场景。压缩包共21个文件含12个.h5模型权重文件对应各智能体actor/critic及其target网络、7个.py核心模块涵盖训练器train.py、环境env.py、噪声noise.py、经验回放buffer.py等、1份README.md说明文档及1份LICENSE协议整体大小为7.86MB结构清晰、模块解耦度高便于理解算法架构与调试复现。目前已有813人学习下载读者可直接加载预训练模型快速验证效果或基于完整训练流程开展多智能体协作/竞争实验掌握从网络构建、分布式策略更新到联合Q值评估的全链路实现细节。1. MADDPG 不是单 agent 的简单堆叠Keras 实现里藏着多智能体协同的硬核边界你手头有个双机械臂协同抓取任务或者一个交通信号灯联合调度场景想用深度强化学习——但直接套 DDPG 会翻车。因为 DDPG 假设环境是马尔可夫的、观测是全局的而真实多智能体系统里每个 agent 只能看到局部信息还互相影响策略更新。MADDPG 就是为破这个局设计的它让每个 agent 拥有独立的 actor 网络但在 critic 训练时偷偷“开小灶”——把所有 agent 的动作拼起来喂给自己的 critic相当于在训练时偷偷看了队友的底牌。这个项目maddpg-keras正是用纯 Keras不依赖 TensorFlow 2.x 的 eager mode 或 PyTorch 动态图实现的轻量级 MADDPG代码结构清晰、模块解耦明确适合想吃透 MADDPG 核心机制、又不想被复杂框架裹挟的工程师。它不是玩具 demo而是能跑通 simple_spread、simple_adversary 等经典 multi-agent benchmark 的完整训练 pipeline如果你正卡在“为什么我的多 agent 策略训着训着就崩溃”“critic 输入维度到底该拼谁的动作”这类问题上这份资源就是你该拆的第一份源码包。2. 从环境建模到网络构建为什么必须用 centralized critic decentralized execution2.1 多智能体环境的观测与动作空间设计以 simple_spread 为例MADDPG 的成败一半取决于环境接口是否满足算法假设。maddpg-keras默认集成的是基于 OpenAI Gym API 改写的 multi-agent 环境非 PettingZoo其关键特征在于每个 agent 返回独立的 observation、reward、done但共享一个 global state用于 critic 训练。以simple_spread场景为例Observation 维度每个 agent 观测包含自身位置、速度、最近 3 个 landmark 的相对坐标 → 典型 shape 是(18,)Action 维度连续控制输出[vx, vy]→ shape(2,)Global state所有 agent 位置 所有 landmark 位置拼接 → shape(n_agents * 2 n_landmarks * 2)提示这不是 Gym 原生支持的格式。项目中MultiAgentEnv类重写了step()和reset()返回obs_n,reward_n,done_n,info_n四元组并额外提供get_state()方法获取 global state。你若替换自定义环境必须保证这五个输出完备且维度对齐。2.2 Actor-Critic 网络的 Keras 实现细节参数共享与梯度隔离Keras 实现 MADDPG 最容易踩坑的地方是误以为“多个 actor 就是复制粘贴几份模型”。实际上每个 agent 的 actor 必须完全独立不同权重但 critic 必须接收所有 agent 的动作输入。项目中通过build_actor_model()和build_critic_model()两个工厂函数实现def build_actor_model(self, obs_dim, act_dim): inputs Input(shape(obs_dim,)) x Dense(128, activationrelu)(inputs) x Dense(128, activationrelu)(x) outputs Dense(act_dim, activationtanh)(x) # 连续动作tanh 归一化 return Model(inputsinputs, outputsoutputs) def build_critic_model(self, obs_dim, act_dim, n_agents): # critic 输入本 agent 观测 所有 agent 动作含自己 obs_input Input(shape(obs_dim,)) act_inputs [Input(shape(act_dim,)) for _ in range(n_agents)] merged_actions Concatenate()(act_inputs) x Concatenate()([obs_input, merged_actions]) x Dense(128, activationrelu)(x) x Dense(128, activationrelu)(x) outputs Dense(1)(x) # Q value return Model(inputs[obs_input] act_inputs, outputsoutputs)这段代码的关键点在于build_critic_model()的act_inputs是一个长度为n_agents的 Input 列表不是把动作向量 flatten 后再切片——这是保证反向传播时梯度只流回对应 actor 的前提Concatenate()操作在 critic 内部完成actor 输出不参与 critic 的前向计算图拼接避免梯度污染actor 输出用tanh但实际动作需乘以action_bound如 1.0这是连续控制的标准做法项目在agent.py的act()方法里做了封装。2.3 Replay Buffer 的多智能体适配不是简单存 tuple而是存 batched dict标准 DQN 的 replay buffer 存(s, a, r, s, done)但 MADDPG 需要同时保存n_agents份观测、动作、奖励。项目采用MultiAgentReplayBuffer类其store_transition()接收的是transition { obs_n: [obs_0, obs_1, ..., obs_n], # list of arrays, each (obs_dim,) act_n: [act_0, act_1, ..., act_n], # list of arrays, each (act_dim,) rew_n: [rew_0, rew_1, ..., rew_n], # list of scalars obs_next_n: [obs_next_0, ..., obs_next_n], done_n: [done_0, done_1, ..., done_n] }而sample_batch()返回的 batch 是按 agent 维度堆叠的obs_batch: shape(batch_size, n_agents, obs_dim)act_batch: shape(batch_size, n_agents, act_dim)rew_batch: shape(batch_size, n_agents)obs_next_batch: shape(batch_size, n_agents, obs_dim)done_batch: shape(batch_size, n_agents)这种结构直接服务于后续的train_critic()中的target_q_values self.critic_target.predict([obs_next_batch[:, i, :], *act_next_batch.T])——注意*act_next_batch.T是将(batch_size, n_agents, act_dim)转为n_agents个(batch_size, act_dim)的 unpack 操作精准匹配 critic 的 input signature。3. 训练流程拆解从软更新到策略噪声每一步都决定收敛稳定性3.1 MADDPG 的双层软更新机制actor target 与 critic target 必须不同步DDPG 只需一套 soft updateτ ≈ 0.001但 MADDPG 中 critic 的 target 更新频率必须高于 actor 的 target 更新。原因在于critic 依赖所有 agent 的动作若某个 actor 的 target 网络滞后太多critic 的 target Q 值就会严重失真。项目中设定critic_target_update_freq 1每个 step 都执行 critic target 的 soft updateactor_target_update_freq 100每 100 个 step 才更新一次 actor targetdef update_target_networks(self, tau1e-3): # critic target update: every step weights self.critic_model.get_weights() target_weights self.critic_target.get_weights() for i in range(len(weights)): target_weights[i] tau * weights[i] (1 - tau) * target_weights[i] self.critic_target.set_weights(target_weights) # actor target update: only when counter % 100 0 if self.update_counter % self.actor_target_update_freq 0: weights self.actor_model.get_weights() target_weights self.actor_target.get_weights() for i in range(len(weights)): target_weights[i] tau * weights[i] (1 - tau) * target_weights[i] self.actor_target.set_weights(target_weights)这个差异是项目能稳定收敛的核心设计之一。很多复现失败的案例都是把两个 τ 设成一样导致 critic 过早信任了过时的 actor target 动作Q 值估计持续高估。3.2 Ornstein-Uhlenbeck 噪声的 agent 级别实例化避免所有 agent 同步抖动探索策略对 MADDPG 至关重要。项目没有用简单的高斯噪声而是为每个 agent 单独初始化 OU 噪声对象self.noise [OUNoise(mu0, theta0.15, sigma0.2) for _ in range(n_agents)]OUNoise类继承自np.random.Generator其sample()方法生成符合均值回归特性的连续噪声。关键点在于theta0.15控制回归速度太小则探索不足太大则震荡sigma0.2控制噪声幅度需与action_bound匹配如 action_bound1.0则 sigma0.2 合理每个 agent 拥有独立的噪声实例确保它们的探索是异步的——否则所有 agent 同时往左猛打方向盘系统必然崩溃。3.3 Critic loss 的 multi-agent 加权设计reward shaping 的隐式实现标准 critic loss 是mse(Q_pred, Q_target)但 MADDPG 中Q_target的计算涉及所有 agent 的 next action。项目中compute_target_q_values()的核心逻辑是# 对每个 agent i构造其 critic 的 target 输入 obs_next_i obs_next_batch[:, i, :] # 本 agent 下一时刻观测 act_next_i act_next_batch[:, i, :] # 本 agent 下一时刻动作来自其 own actor_target # 其他 agent 的动作也来自各自的 actor_target act_next_others [act_next_batch[:, j, :] for j in range(n_agents) if j ! i] # 拼成 critic 输入[obs_next_i, act_next_i, act_next_others[0], ...] q_target_inputs [obs_next_i, act_next_i] act_next_others q_target self.critic_target.predict(q_target_inputs).flatten() # target Q reward_i gamma * q_target * (1 - done_i) target_q rew_batch[:, i] self.gamma * q_target * (1 - done_batch[:, i])这里rew_batch[:, i]是第 i 个 agent 的 reward意味着每个 agent 的 critic 是独立优化的目标是拟合自身 reward 的 discounted sum。这天然支持 reward shaping你可以给协作 agent 设计稀疏 reward如只在成功抓取时给正奖而 critic 仍能通过 bootstrapping 学到中间策略价值。4. 避坑指南五个血泪经验总结出的 MADDPG 复现雷区4.1 现象训练初期 critic loss 突然爆炸1e5随后 actor loss 归零原因Q_target计算时未屏蔽done的 agent。当某个 agentdoneTrue其obs_next可能是非法值如全零但代码仍将其喂给 critic_target导致输出 NaN。解决在compute_target_q_values()中强制对doneagent 的q_target设为 0且target_q计算时用np.where(done_batch[:, i], rew_batch[:, i], target_q)替代简单加法。4.2 现象所有 agent 的策略趋同最终集体撞墙或原地打转原因actor 网络初始化权重相同且 critic 输入拼接顺序固定如总是act_0, act_1, act_2导致梯度更新方向高度一致。解决为每个 agent 的 actor 使用不同随机种子初始化tf.keras.utils.set_random_seed(i)并在build_critic_model()中对act_inputs列表做随机 shuffle训练前固定一次非每 step。4.3 现象训练 10k step 后 reward 曲线平台期但 agent 行为明显未收敛原因Ornstein-Uhlenbeck 噪声的sigma衰减策略缺失。固定sigma0.2会导致后期探索过强无法精细调优。解决在agent.py的act()方法中加入线性衰减sigma max(0.05, 0.2 - episode * 0.0001)并记录episode数非 step。4.4 现象obs_next_batch维度报错ValueError: Input 0 is incompatible with layer...原因环境reset()返回的obs_n是 list of array但某些 step 中某个 agent 的obsshape 异常如(17,)而非(18,)导致 batch stack 失败。解决在MultiAgentReplayBuffer.store_transition()开头插入断言assert all(len(obs.shape) 1 and obs.shape[0] self.obs_dim for obs in obs_n)快速定位环境 bug。4.5 现象GPU 显存 OOM即使 batch_size32原因critic model 的 input signature 包含n_agents个 Input 层Keras 默认为每个 Input 分配独立显存缓冲区n_agents4时显存占用翻 4 倍。解决改用tf.functiontf.TensorArray动态拼接动作需重写 critic call或更实用的方案——将所有 agent 动作预拼为(batch_size, n_agents * act_dim)修改 critic 输入为单个 Input内部 reshape 后 split。项目已提供critic_flat_input.py作为备选实现。5. 模型诊断与可视化用三张图看穿 MADDPG 的健康状态5.1 Critic Q-value 分布图判断 reward scaling 是否合理MADDPG 的 critic 输出是 scalar Q 值其分布应随训练逐渐右移正值增大且方差缩小。在train.py的 logging 部分添加# 在每个 episode 结束后 q_values self.critic_model.predict([ obs_batch[:, 0, :], *act_batch.T # act_batch shape: (batch_size, n_agents, act_dim) ]) plt.hist(q_values.flatten(), bins50, alpha0.7, labelfEpisode {ep}) plt.xlabel(Q Value) plt.ylabel(Frequency) plt.title(Critic Q-value Distribution Evolution) plt.legend() plt.savefig(fq_dist_ep{ep}.png)健康指标训练中期~2000 ep直方图主峰应从 [-5, 5] 移至 [0, 10]若长期卡在负值区间说明 reward 设计过苛刻如reward -distance未加偏置若方差始终很大跨度 20说明 critic 过拟合或 learning rate 过高。5.2 Actor Policy Gradient Norm 曲线识别梯度消失/爆炸Keras 默认不暴露梯度需手动 hook。在train_actor()中插入with tf.GradientTape() as tape: actions self.actor_model(obs_batch[:, i, :]) # critic 输入本 agent obs 所有 agent actions q_input [obs_batch[:, i, :]] [actions if ji else act_batch[:, j, :] for j in range(self.n_agents)] q_value self.critic_model(q_input) grads tape.gradient(q_value, self.actor_model.trainable_variables) grad_norm tf.linalg.global_norm(grads) self.writer.add_scalar(fgrad_norm/agent_{i}, grad_norm.numpy(), step)健康指标梯度 norm 应稳定在1e-3 ~ 1e-1区间。若 1e-4说明 actor 已饱和tanh 输出接近 ±1或 critic 梯度太弱若 1需立即降低 actor learning rate项目默认 1e-4可试 5e-5。5.3 Multi-agent Trajectory Overlay用 Matplotlib 直观验证协同性对simple_spread环境提取每个 episode 的obs_n位置信息绘制轨迹图Agent IDColorRoleExpected Behavior0redLeader主动靠近 landmark引导 others1blueFollower-1保持与 leader 的固定距离2greenFollower-2填补 leader 与 landmark 间空隙def plot_episode_trajectory(self, obs_history, ep_id): fig, ax plt.subplots(figsize(8, 8)) for i in range(self.n_agents): pos_x [obs[i][0] for obs in obs_history] # x coordinate pos_y [obs[i][1] for obs in obs_history] # y coordinate ax.plot(pos_x, pos_y, o-, labelfAgent {i}, color[red,blue,green][i]) ax.set_xlim(-1.5, 1.5) ax.set_ylim(-1.5, 1.5) ax.legend() ax.grid(True) plt.savefig(ftraj_ep{ep_id}.png)关键观察点健康训练下leader 轨迹应呈放射状指向各 landmarkfollowers 轨迹应呈现平行或镜像特征。若所有轨迹重叠说明 policy 未分化若频繁交叉碰撞说明 critic 的 collision penalty 未生效或 reward 设计缺陷。从那以后我每次调试新环境都强制走一遍这三张图的生成流程——不是为了炫技而是用最原始的视觉反馈逼自己直面模型到底在学什么。Q 值分布不对我就去查 reward 函数梯度 norm 爆了我就先砍 learning rate 再看轨迹图乱成毛线团我就停掉训练打开 env 的 step 函数单步 debug。这些习惯省下的 debug 时间够我多跑三个 baseline。希望帮到你。本文还有配套的精品资源点击获取

相关新闻

syzkaller 依赖治理工具 Depguard 深度解析:导入白名单机制、listMode 匹配算法与 golangci-lint 集成

syzkaller 依赖治理工具 Depguard 深度解析:导入白名单机制、listMode 匹配算法与 golangci-lint 集成

测试操作系统网络安全 【免费下载链接】syzkaller syzkaller is an unsupervised coverage-guided kernel fuzzer 项目地址: https://gitcode.com/gh_mirrors/syz/syzkaller 点击查看 免费下载 本文以 syzkaller 仓库 vendor 目录中随 golangci-lint v1.57.2 一同引…

2026/10/14 15:06:43 阅读更多 →
从激活值预测分类器参数:少样本图像识别的生成式解法

从激活值预测分类器参数:少样本图像识别的生成式解法

被一组只有五张图的新类别困住的时候,大多数模型的反应是迷茫。少样本图像识别(few-shot image recognition)要解决的,就是在这样苛刻的条件下,让模型尽量快地进入状态。我今天想聊的这篇论文,标题起得很直…

2026/10/14 15:06:43 阅读更多 →
多相信道化接收机原理与MATLAB实现:宽带ADC高效信道化设计

多相信道化接收机原理与MATLAB实现:宽带ADC高效信道化设计

简介:多相结构信道化接收机是宽带信号高效分解为窄带信道的关键技术,在无线电监测、软件定义通信与信号侦察等领域应用广泛。面向通信工程专业学生、从事信号处理研究的开发者及需要完成相关毕业设计的读者,本资源以单文件MATLAB程序fengkui_…

2026/10/14 15:06:43 阅读更多 →

最新新闻

数据分析别急着点生成:职臣Ai避坑指南

数据分析别急着点生成:职臣Ai避坑指南

很多人做实证研究时,最容易把“上传数据”误解成“自动得到结论”。实际上,数据分析的质量,往往在点击生成之前就已经决定了一大半。职臣Ai的数据分析功能页面显示,用户需要依次填写研究目的和问题、变量信息、探索性分析结果、预…

2026/10/14 15:45:50 阅读更多 →
DuoMind:面向工业场景的语义通信多机器人协同系统

DuoMind:面向工业场景的语义通信多机器人协同系统

1. 项目概述:当多机器人系统开始“说人话”你有没有试过让两个扫地机器人在同一个屋子里协作?不是简单地各自为战,而是真正理解彼此的意图——比如A发现厨房地板有油渍,主动通知B去取专用清洁模块,同时自己绕开该区域继…

2026/10/14 15:45:50 阅读更多 →
SMAA抗锯齿原理与OpenGL/Vulkan双API实战解析

SMAA抗锯齿原理与OpenGL/Vulkan双API实战解析

简介:本资源是一个面向图形开发者的SMAA抗锯齿技术实践项目,聚焦OpenGL与Vulkan双API下的亚像素形态抗锯齿(Subpixel Morphological Anti-Aliasing)集成与调优,适用于中高级C图形程序员、游戏引擎开发者及渲染管线学习…

2026/10/14 15:45:50 阅读更多 →
Python手写大M法求解线性规划:从库函数到算法全解析

Python手写大M法求解线性规划:从库函数到算法全解析

1. 项目原起:线性规划、单纯形法和大M法要解决什么问题 线性规划是很多优化问题的底层工具,而单纯形法(大M法)则是手算时代就沉淀下来的经典算法。今天这篇博文,我就用 Python 分别走一遍“库函数一行求解”和“手写大…

2026/10/14 15:45:50 阅读更多 →
MySQL 8.0.19 Windows安装配置全攻略:从MSI到ZIP及常见错误排查

MySQL 8.0.19 Windows安装配置全攻略:从MSI到ZIP及常见错误排查

MySQL 8.0.19 下载安装及配置详细步骤,这个标题写在文档里看着很简单,但真正动手的时候,数据目录初始化失败、1045 认证报错、服务怎么都起不来——这类问题我反反复复见过很多次。多数情况真不是技术难点,而是安装前少做了一个检…

2026/10/14 15:45:50 阅读更多 →
三菱MES编程软件实战:MC协议与OPC UA数据采集及工单追溯落地

三菱MES编程软件实战:MC协议与OPC UA数据采集及工单追溯落地

简介:三菱MES编程软件面向自动化工程师与生产管理人员,围绕QJ71MES96模块提供配置、编程与调试支持,用于打通MES系统与三菱PLC之间的数据交互,实现生产状态、设备运行数据与异常报警的实时采集反馈。压缩包共113个文件&#xff0c…

2026/10/14 15:44:48 阅读更多 →

日新闻

AnyPS5通用适配层设计:兼容性与性能的工程取舍

AnyPS5通用适配层设计:兼容性与性能的工程取舍

1. 从"AnyPS5"这个名字说起:它到底想解决什么问题第一次看到"AnyPS5"这个标题,我脑子里冒出来的第一个念头是:这大概率是一个围绕"跨平台运行"或者"通用适配"做文章的项目。名字里的"Any"…

2026/10/14 0:00:41 阅读更多 →
Flutter跨平台开发实战:二手交易应用与HarmonyOS适配

Flutter跨平台开发实战:二手交易应用与HarmonyOS适配

1. 项目概述:Flutter跨平台二手交易应用开发作为一名长期从事移动应用开发的工程师,我最近完成了一个基于Flutter框架的二手物品交易平台项目。这个项目最吸引我的地方在于它完美结合了Flutter的跨平台优势与HarmonyOS的生态特性,实现了"…

2026/10/14 0:00:41 阅读更多 →
DFS算法解决飞机降落调度问题实战

DFS算法解决飞机降落调度问题实战

1. 项目背景与核心思路飞机降落问题是一个经典的调度优化场景,也是算法竞赛和面试中的高频题目。这个题目要求我们合理安排多架飞机的降落顺序和时间,确保所有飞机都能安全着陆而不发生冲突。在实际航空管制中,这类问题直接关系到飞行安全和机…

2026/10/14 0:00:41 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/14 3:30:28 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/13 16:22:04 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/14 3:29:58 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/13 22:01:27 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/13 14:57:39 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/14 7:53:34 阅读更多 →