跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客GitHub 精选镜像AI 生图工具UI配色美学隐私政策关于联系
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
PythonAI算法

多智能体近端策略优化(MAPPO)算法详解

MAPPO 作为多智能体强化学习中的经典算法,是 PPO 在多智能体环境下的扩展。它采用集中式训练与分布式执行的架构,利用中心化 Critic 获取全局信息,同时保持 Actor 的去中心化决策能力。核心在于通过裁剪策略更新比率来限制步幅,确保训练稳定。本文深入剖析了 MAPPO 的优势函数估计、价值函数损失及策略目标函数,并给出了完整的 Python 实现代码,涵盖环境交互、经验回放及网络更新流程,适合用于机器人集群、自动驾驶等复杂多智能体任务的研究与开发。

292440837发布于 2025/9/29更新于 2026/7/2044 浏览
多智能体近端策略优化(MAPPO)算法详解

多智能体近端策略优化(MAPPO)算法

1. 背景与动机

PPO(Proximal Policy Optimization)近年来已成为强化学习领域最流行的算法之一。它通过引入裁剪的策略更新机制,有效解决了传统策略梯度方法(如 TRPO)中因步长过大导致的训练不稳定问题。

在多智能体环境中,多个智能体同时学习策略,彼此的行为会相互影响。因此,需要一个鲁棒且稳定的策略优化方法。MAPPO(Multi-Agent Proximal Policy Optimization)作为 PPO 的扩展,采用集中式 Critic 和去中心化 Actor 的架构,显著提高了多智能体环境下的学习效率和稳定性。

参考论文: The Surprising Effectiveness of PPO in Cooperative, Multi-Agent Games

2. 算法结构

MAPPO 继承了 PPO 的核心思想,并结合多智能体系统的特点,采用了集中式训练,分布式执行(CTDE)的架构:

  • 集中式训练:在训练阶段,所有智能体的 Critic 网络能够访问全局状态和其他智能体的动作信息,从而学习到更准确的价值函数。
  • 分布式执行:在执行阶段,每个智能体仅使用自己观测到的局部状态和策略进行动作选择,保证了系统的分布式控制能力。

3. 核心公式

MAPPO 算法主要包含两部分:策略更新和价值函数估计。

优势函数计算

优势函数 $\hat{A}_t^i$ 用于衡量某个动作 $a_t$ 相对于当前策略下平均动作的优劣程度。通常通过广义优势估计(GAE)进行估算:

$$\hat{A}t^i = \delta_t + (\gamma \lambda) \delta{t+1} + ... + (\gamma \lambda)^{T-t+1} \delta_{T-1}$$

其中 $\delta_t$ 是时间差分误差,定义为:

$$\delta_t = r_t + \gamma V(s_{t+1}) - V(s_t)$$

这里 $\lambda$ 是 GAE 中的权重参数,用于平衡偏差和方差。

价值函数估计

每个智能体 $i$ 的价值函数 $V_i(s)$ 由一个中心化的 Critic 网络估计。Critic 网络利用全局状态 $s$ 和所有智能体的动作 $a_1, a_2, ..., a_N$ 来估计全局价值。其目标是最小化均方误差(MSE)损失函数:

$$L(\phi_i) = \mathbb{E}{s_t, r_t, s{t+1}} \left[ \left( V_i(s_t; \phi_i) - R_t \right)^2 \right]$$

其中 $R_t$ 是从当前时刻 $t$ 到未来的累计回报,通常通过 TD 目标估计:

$$R_t = r_t + \gamma V_i(s_{t+1}; \phi'_i)$$

策略更新

PPO 引入了一个裁剪目标函数来限制每次更新的策略变化幅度。MAPPO 遵循相同原则,但应用在每个智能体 $i$ 的策略上。PPO 的目标函数为:

$$L^{CLIP}(\theta) = \mathbb{E}_t \left[ \min \left( r_t(\theta) \hat{A}_t, \text{clip}(r_t(\theta), 1 - \epsilon, 1 + \epsilon) \hat{A}_t \right) \right]$$

其中 $r_t(\theta) = \frac{\pi_{\theta}(a_t | s_t)}{\pi_{\theta_{\text{old}}}(a_t | s_t)}$ 是当前策略与旧策略的比率,$\epsilon$ 是裁剪阈值。

MAPPO 中每个智能体采用类似的目标函数:

$$L^{CLIP}_i(\theta_i) = \mathbb{E}_t \left[ \min \left( r_t(\theta_i) \hat{A}_t^i, \text{clip}(r_t(\theta_i), 1 - \epsilon, 1 + \epsilon) \hat{A}_t^i \right) \right]$$

4. 算法流程

  1. 交互与经验收集:每个智能体根据当前策略与环境交互,存储状态、动作、奖励、下一状态等信息。
  • 更新 Critic 网络:根据损失函数更新 Critic 参数,最小化预测值与真实回报的误差。
  • 更新 Actor 网络:根据裁剪后的 PPO 目标函数,使用策略梯度法更新策略参数。
  • 软更新目标网络:逐步更新目标网络参数以保持训练稳定。
  • 循环迭代:重复上述步骤直到策略收敛。
  • 5. Python 实现示例

    以下是一个基于 PyTorch 的可移植 MAPPO 实现框架,展示了主运行逻辑及参数配置。

    import torch
    import numpy as np
    from torch.utils.tensorboard import SummaryWriter
    import argparse
    from normalization import Normalization, RewardScaling
    from replay_buffer import ReplayBuffer
    from mappo_mpe import MAPPO_MPE
    from environment import Env
    
    class Runner_MAPPO_MPE:
        def __init__(self, args, env_name, number, seed):
            self.args = args
            self.env_name = env_name
            self.number = number
            self.seed = seed
            
            # Set random seed
            np.random.seed(self.seed)
            torch.manual_seed(self.seed)
            
            # Create env
            self.env = Env(env_name, discrete=True)
            self.args.N = self.env.n
            self.args.obs_dim_n = [self.env.observation_space[i].shape[0] for i in range(self.args.N)]
            self.args.action_dim_n = [self.env.action_space[i].n for i in range(self.args.N)]
            
            # Homogenous agents setup
            self.args.obs_dim = self.args.obs_dim_n[0]
            self.args.action_dim = self.args.action_dim_n[0]
            self.args.state_dim = np.sum(self.args.obs_dim_n)
            
            print("observation_space=", self.env.observation_space)
            print("obs_dim_n={}".format(self.args.obs_dim_n))
            print("action_space=", self.env.action_space)
            print("action_dim_n={}".format(self.args.action_dim_n))
            
            # Create N agents
            self.agent_n = MAPPO_MPE(self.args)
            self.replay_buffer = ReplayBuffer(self.args)
            
            # Tensorboard
            self.writer = SummaryWriter(log_dir='runs/MAPPO/MAPPO_env_{}_number_{}_seed_{}'.format(
                self.env_name, self.number, self.seed))
            self.evaluate_rewards = []
            self.total_steps = 0
            
            if self.args.use_reward_norm:
                print("------use reward norm------")
                self.reward_norm = Normalization(shape=self.args.N)
            elif self.args.use_reward_scaling:
                print("------use reward scaling------")
                self.reward_scaling = RewardScaling(shape=self.args.N, gamma=self.args.gamma)
    
        def run(self):
            evaluate_num = -1
            while self.total_steps < self.args.max_train_steps:
                if self.total_steps // self.args.evaluate_freq > evaluate_num:
                    self.evaluate_policy()
                    evaluate_num += 1
                
                _, episode_steps = self.run_episode_mpe(evaluate=False)
                self.total_steps += episode_steps
                
                if self.replay_buffer.episode_num == self.args.batch_size:
                    self.agent_n.train(self.replay_buffer, self.total_steps)
                    self.replay_buffer.reset_buffer()
                    self.evaluate_policy()
            
            self.env.close()
    
        def evaluate_policy(self):
            evaluate_reward = 0
            for _ in range(self.args.evaluate_times):
                episode_reward, _ = self.run_episode_mpe(evaluate=True)
                evaluate_reward += episode_reward
            
            evaluate_reward /= self.args.evaluate_times
            self.evaluate_rewards.append(evaluate_reward)
            
            print("total_steps:{} \t evaluate_reward:{}".format(self.total_steps, evaluate_reward))
            self.writer.add_scalar('evaluate_step_rewards_{}'.format(self.env_name), 
                                   evaluate_reward, global_step=self.total_steps)
            
            np.save('./data_train/MAPPO_env_{}_number_{}_seed_{}.npy'.format(
                self.env_name, self.number, self.seed), np.array(self.evaluate_rewards))
            self.agent_n.save_model(self.env_name, self.number, self.seed, self.total_steps)
    
        def run_episode_mpe(self, evaluate=False):
            episode_reward = 0
            obs_n = self.env.reset()
            
            if self.args.use_reward_scaling:
                self.reward_scaling.reset()
            
            if self.args.use_rnn:
                self.agent_n.actor.rnn_hidden = None
                self.agent_n.critic.rnn_hidden = None
            
            for episode_step in range(self.args.episode_limit):
                a_n, a_logprob_n = self.agent_n.choose_action(obs_n, evaluate=evaluate)
                s = np.array(obs_n).flatten()
                v_n = self.agent_n.get_value(s)
                obs_next_n, r_n, done_n, _ = self.env.step(a_n)
                
                episode_reward += r_n[0]
                
                if not evaluate:
                    if self.args.use_reward_norm:
                        r_n = self.reward_norm(r_n)
                    elif self.args.use_reward_scaling:
                        r_n = self.reward_scaling(r_n)
                    
                    self.replay_buffer.store_transition(episode_step, obs_n, s, v_n, a_n, a_logprob_n, r_n, done_n)
                
                obs_n = obs_next_n
                if all(done_n):
                    break
            
            if not evaluate:
                s = np.array(obs_n).flatten()
                v_n = self.agent_n.get_value(s)
                self.replay_buffer.store_last_value(episode_step + 1, v_n)
            
            return episode_reward, episode_step + 1
    
    if __name__ == '__main__':
        parser = argparse.ArgumentParser("Hyperparameters Setting for MAPPO in MPE environment")
        parser.add_argument("--max_train_steps", type=int, default=int(3e6))
        parser.add_argument("--episode_limit", type=int, default=25)
        parser.add_argument("--evaluate_freq", type=float, default=5000)
        parser.add_argument("--evaluate_times", type=float, default=3)
        parser.add_argument("--batch_size", type=int, default=32)
        parser.add_argument("--mini_batch_size", type=int, default=8)
        parser.add_argument("--rnn_hidden_dim", type=int, default=64)
        parser.add_argument("--mlp_hidden_dim", type=int, default=64)
        parser.add_argument("--lr", type=float, default=5e-4)
        parser.add_argument("--gamma", type=float, default=0.99)
        parser.add_argument("--lamda", type=float, default=0.95)
        parser.add_argument("--epsilon", type=float, default=0.2)
        parser.add_argument("--K_epochs", type=int, default=15)
        parser.add_argument("--use_adv_norm", type=bool, default=True)
        parser.add_argument("--use_reward_norm", type=bool, default=True)
        parser.add_argument("--use_reward_scaling", type=bool, default=False)
        parser.add_argument("--entropy_coef", type=float, default=0.01)
        parser.add_argument("--use_lr_decay", type=bool, default=True)
        parser.add_argument("--use_grad_clip", type=bool, default=True)
        parser.add_argument("--use_orthogonal_init", type=bool, default=True)
        parser.add_argument("--set_adam_eps", type=float, default=True)
        parser.add_argument("--use_relu", type=float, default=False)
        parser.add_argument("--use_rnn", type=bool, default=False)
        parser.add_argument("--add_agent_id", type=float, default=False)
        parser.add_argument("--use_value_clip", type=float, default=False)
        
        args = parser.parse_args()
        runner = Runner_MAPPO_MPE(args, env_name="simple_spread", number=1, seed=0)
        runner.run()
    

    移植注意事项:

    1. 注意环境参数的设置格式是否符合当前任务需求。
    2. 确保正确解析环境的返回值,特别是 done 信号的处理。
    3. 主运行流程中的 runner.run() 需根据实际硬件资源调整 batch size 和训练步数。

    6. 优势与应用场景

    • 鲁棒性与稳定性:裁剪更新机制避免了策略更新幅度过大,适合复杂的多智能体协作。
    • 集中式训练与分布式执行:利用全局信息进行训练,同时保持执行时的局部决策灵活性。
    • 适应复杂环境:适用于机器人集群、自动驾驶车队、多人游戏等混合协作竞争场景。

    7. 结论

    MAPPO 是对 PPO 算法的多智能体扩展,通过中心化 Critic 和去中心化 Actor 的结合,实现了高效稳定的策略优化。其裁剪更新机制保证了良好的收敛性,是目前多智能体强化学习研究和应用中的重要算法之一。

    目录

    1. 多智能体近端策略优化(MAPPO)算法
    2. 1. 背景与动机
    3. 2. 算法结构
    4. 3. 核心公式
    5. 优势函数计算
    6. 价值函数估计
    7. 策略更新
    8. 4. 算法流程
    9. 5. Python 实现示例
    10. 6. 优势与应用场景
    11. 7. 结论
    • 免费图片AI生成工具免费生成了解详情
    • Magick API 一键接入全球大模型注册送1000万token查看
    • 免费图片视频在线生成30秒,将你的创意变成现实开始设计
    • X/Twitter免费视频下载器免登陆无限额度免费视频解析下载了解详情
    • 100+免费在线小游戏爽一把
    极客日志微信公众号二维码

    微信扫一扫,关注极客日志

    微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

    更多推荐文章

    查看全部
    • 两两交换链表中的节点
    • llama.cpp 重大更新:内置 Web UI,性能超越 Ollama,本地大模型部署新选择
    • KingbaseES 用户权限隔离功能原理与实战
    • C++ 类完全指南:从基础到实践
    • Python 基础命令与语法详解
    • 大语言模型架构:LLM MoE 与 Switch Transformers
    • C++11 核心特性详解:列表初始化、右值引用与移动语义
    • 2023 年电赛 H 题:信号分离装置 FPGA 与 STM32 实现方案
    • 自然语言处理在客户服务中的实战应用
    • LLaMA-Factory 本地环境搭建与安装指南
    • 深度解析 GitHub Copilot Agent Skills:如何打造可跨项目的 AI 专属“工具箱”
    • Git 恢复到上一次提交版本的操作指南
    • 算法技巧:前缀和一维与二维应用解析
    • 9 种常用排序算法总结
    • ABB 机器人虚拟示教器基础操作与编程指南
    • TextRank 算法详解:文本摘要与关键词提取实战
    • NUI 低代码平台四种数据提交方式解析
    • C++ 红黑树详解与实现
    • 基于 Java 与 Leaflet 的湖南省道路长度 WebGIS 实践
    • 本地部署 AI 问答知识库:基于 FastGPT 的一站式指南

    相关免费在线工具

    • 加密/解密文本

      使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online

    • RSA密钥对生成器

      生成新的随机RSA私钥和公钥pem证书。 在线工具,RSA密钥对生成器在线工具,online

    • Mermaid 预览与可视化编辑

      基于 Mermaid.js 实时预览流程图、时序图等图表,支持源码编辑与即时渲染。 在线工具,Mermaid 预览与可视化编辑在线工具,online

    • 随机西班牙地址生成器

      随机生成西班牙地址(支持马德里、加泰罗尼亚、安达卢西亚、瓦伦西亚筛选),支持数量快捷选择、显示全部与下载。 在线工具,随机西班牙地址生成器在线工具,online

    • Gemini 图片去水印

      基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online

    • curl 转代码

      解析常见 curl 参数并生成 fetch、axios、PHP curl 或 Python requests 示例代码。 在线工具,curl 转代码在线工具,online