PyTorch强化学习实战(35)——多智能体强化学习

PyTorch强化学习实战(35)------多智能体强化学习

    • [0. 前言](#0. 前言)
    • [1. 多智能体强化学习简介](#1. 多智能体强化学习简介)
    • [2. 实验环境](#2. 实验环境)
      • [2.1 MAgent 简介](#2.1 MAgent 简介)
      • [2.2 安装 MAgent](#2.2 安装 MAgent)
      • [2.3 配置随机环境](#2.3 配置随机环境)
    • 小结
    • 系列链接

0. 前言

在本节中,我们将介绍多智能体强化学习 (multi-agent reinforcement learning, MARL),这是强化学习 (Reinforcement Learning, RL)和深度强化学习的一个相对较新的方向,涉及多个智能体在环境中进行交互的情况。在现实生活中,这类问题出现在宽带通信网络、物联网等场景中。

在本节中,我们将简要介绍 MARL,并在简单环境中进行一些实验。在实验中,我们将采用一种简单的方法,让智能体共享我们正在优化的策略,但观察将从每个智能体的视角出发,并包含其他智能体位置的信息。通过这种简化,我们的强化学习方法将保持不变,只需对环境进行预处理并处理多个智能体的存在。

1. 多智能体强化学习简介

多智能体设定是经典强化学习 (Reinforcement Learning, RL) 模型的自然延伸。在传统RL框架中,单个智能体通过观察、奖励和动作与环境交互。但在现实场景中,许多问题往往涉及多个智能体共同参与环境互动。具体示例如下:

  • 象棋对弈:程序需击败对手
  • 市场模拟:如产品广告或价格调整,我们的动作可能引发其他参与者的反制
  • 多人游戏,如 Dota 2 或 StarCraft II,当智能体需要控制多个单位,智能体需操控多个单位与其他玩家对抗(此时同一玩家控制的单位也可能协作达成目标)

若其他智能体不受我们控制,可将其视为环境的一部分,仍采用单智能体 RL 模型。我们知道,通过自我对弈训练是一种强大技术,即使环境设置简单也能训练出优秀策略。但在某些情况下,这样的方式过于局限,并不完全符合我们的需求。

研究表明,一组简单智能体可能展现出远超预期的复杂协作行为。例如 OpenAI 在论文《Emergent tool use from multi-agent autocurricula》中描述的"捉迷藏"游戏:智能体通过协作发展出日益复杂的攻防策略(如用可用物体建造围栏、利用蹦床捕捉墙后对手)。

根据智能体交互模式,可将其分为两类:

  • 竞争型:两个及以上智能体通过对弈最大化自身收益,如象棋、双陆棋或 Atari 乒乓等双人游戏
  • 协作型:智能体群体需通力合作达成目标

属于单一类别的事例比比皆是,但最引人入胜且贴近现实的场景往往融合了两种行为模式。现实生活远比模型复杂------从允许结盟的棋盘游戏到假定完全协作的现代企业,现实世界远比理论复杂。从理论角度来看,博弈论为两种交互形式奠定了扎实理论基础,minimax 算法是博弈论中的一个著名成果。MARL 作为新兴领域正持续发展,值得保持关注。

2. 实验环境

在开始第一个多智能体强化学习 (multi-agent reinforcement learning, MARL) 示例之前,我们先来了解可用的实验环境。目前 MARL 的可选环境相对有限:Gymnasium原生环境仅支持单智能体。对于 Atari Pong,随存在补丁可以将其切换为双人模式,但属于非标准特例。

DeepMind 与 Blizzard 合作,开源了 StarCraft II环境,为实验提供了极具挑战性的平台,但对初学者而言复杂度较高。相比之下,Geek.AI 开发的 MAgent 环境更为适合------它依赖简洁、运行高效,能模拟多种多智能体场景。虽然未提供 Gym 兼容 API,但我们可以自行封装。

如果对 MARL 感兴趣,还可以查看 Farama Foundation 的PettingZoo 项目,其包含更多环境与统一交互 API,但本节仅专注于 MAgent 环境。

2.1 MAgent 简介

MAgent 的核心是一个二维网格世界仿真系统。智能体可执行以下操作:

  • 基于感知范围观察周边环境
  • 在限定距离内移动
  • 攻击周围其他智能体

环境中可存在具有不同特性和交互参数的智能体群体。以我们将要使用的"捕食者-猎物"模型为例:"老虎"通过捕猎"鹿"获取奖励。在环境配置中,可定义群体的感知范围、移动能力、攻击距离、初始生命值、行动消耗等参数。除智能体外,环境还可设置不可穿越的墙体障碍。

MAgent 的优势在于卓越的可扩展性------其底层采用 C++ 实现,仅暴露 Python 接口。这种架构使得环境能够同时处理数千智能体的观测与动作计算。

2.2 安装 MAgent

与许多开源项目类似,MAgent 原始版本曾一度停止维护。但 Farama Foundation 复刻了原代码库并进行持续维护,保留了大部分核心功能,命名为 MAgent2,代码库地址为:https://github.com/Farama-Foundation/magent2。安装 MAgent2 需执行以下命令:

shell 复制代码
$ pip install magent2==0.3.3

2.3 配置随机环境

为快速理解 MAgent 的 API 逻辑,我们实现了一个包含"老虎"和"鹿"智能体的简单环境,两组智能体均采用随机策略运行。虽然从强化学习角度看缺乏挑战性,但能帮助我们快速掌握 API 用法以便实现 Gym 环境封装。代码位于 forest_random.py,接下来进行详细解析。

(1) 首先从 data.py 中定义的 ForestEnv 类开始,该类继承自 magent_parallel_env,这是 MAgent 环境的基类:

python 复制代码
class ForestEnv(magent_parallel_env, EzPickle):
    metadata = {
        "render_modes": ["human", "rgb_array"],
        "name": "forest_v4",
        "render_fps": 5,
    }

该类模拟了 Gym API,但并非完全兼容,因此后续代码中需要处理这个问题。

(2) 在构造函数中,实例化 GridWorld 类,该类作为底层 MAgent C++ 库 API 的 Python 适配器:

python 复制代码
    def __init__(self, map_size: int = MAP_SIZE, max_cycles: int = MAX_CYCLES,
                 extra_features: bool = False, render_mode: tt.Optional[str] = None,
                 seed: tt.Optional[int] = None, count_walls: int = COUNT_WALLS,
                 count_deer: int = COUNT_DEER, count_tigers: int = COUNT_TIGERS):
        EzPickle.__init__(self, map_size, max_cycles, extra_features, render_mode, seed)
        env = GridWorld(self.get_config(map_size), map_size=map_size)

        handles = env.get_handles()
        self.count_walls = count_walls
        self.count_deer = count_deer
        self.count_tigers = count_tigers

        names = ["deer", "tiger"]
        super().__init__(env, handles, names, map_size, max_cycles, [-1, 1],
                         False, extra_features, render_mode)

(3) GridWorld 类的配置由 get_config 函数返回的 Config 实例完成:

python 复制代码
    @classmethod
    def get_config(cls, map_size: int):
        # Standard forest config, but deer get reward after every step
        cfg = forest_config(map_size)
        cfg.agent_type_dict["deer"]["step_reward"] = 1
        return cfg

该函数使用从 magent.builtin.config.forest 包导入的 forest_config 函数,并对配置进行调整,为鹿群每步行动添加奖励。当我们开始训练鹿模型时这一点很重要,每步 1 分的奖励将激励智能体延长生存时间。

其余配置定义了环境的诸多细节,包括:

  • 环境中的智能体组数量:本例中有"鹿"和"虎"两组
  • 各组的属性:视野范围(如鹿只能看到 1 格距离,虎能看到 4 格)、攻击能力与射程、初始生命值、伤害恢复速度等可定制参数
  • 攻击交互规则:具有高度灵活性,例如可建模捕食者需成对狩猎的场景。当前设置中,任何虎都可无限制攻击任何鹿

(4) ForestEnv 类的最后一个函数 generate_map 负责在地图上随机生成墙壁、鹿和虎的位置:

python 复制代码
    def generate_map(self):
        env, map_size = self.env, self.map_size
        handles = env.get_handles()

        env.add_walls(method="random", n=self.count_walls)
        env.add_agents(handles[0], method="random", n=self.count_deer)
        env.add_agents(handles[1], method="random", n=self.count_tigers)

(5) 查看 forest_random.py 的源代码。首先,导入 lib.data 包和来自 Gymnasium 的 VideoRecorder 类:

python 复制代码
from gymnasium.wrappers.monitoring.video_recorder import VideoRecorder
import data
from PIL import Image
import pathlib
import numpy as np

RENDER_DIR = "render"

我们已经学习了如何使用RecordVideo 包装器自动捕获环境观察值,但由于 MAgent 环境的返回值不同(所有方法都一次性返回所有智能体的字典而非单个值),这种方法不可行。为此,我们将使用 VideoRecorder 类来录制视频并写入 RENDER_DIR 目录。

(6) 首先创建 ForestEnv 实例和视频录制器。环境对象包含 agents 属性,该属性保存环境中所有智能体的字符串标识符。在本例中,它将是一个包含类似 deer_12 或 tiger_3 值的列表。默认配置下,64×64 地图上会有 204 只鹿和 40 只老虎,因此 env.agents 列表包含 244 项:

python 复制代码
if __name__ == "__main__":
    env = data.ForestEnv(render_mode="rgb_array")
    recorder = VideoRecorder(env, RENDER_DIR + "/forest-random.mp4")
    sum_rewards = {agent_id: 0.0 for agent_id in env.agents}
    sum_steps = {agent_id: 0 for agent_id in env.agents}

(7) 我们使用 reset() 方法重置环境,但现在它返回一个值(而非 Gymnasium API 中的两个值)。返回值是以智能体 ID 为键、观察张量为值的字典:

python 复制代码
    obs = env.reset()
    recorder.capture_frame()
    assert isinstance(obs, dict)
    print(f"tiger_0: obs {obs['tiger_0'].shape}, act: {env.action_space('tiger_0')}")
    print(f"deer_0: obs {obs['deer_0'].shape}, act: {env.action_space('deer_0')}\n")
    step = 0
    save_render(env.render(), pathlib.Path(RENDER_DIR), step)

上述代码生成以下输出:

shell 复制代码
tiger_0: obs (9, 9, 5), act: Discrete(9)
deer_0: obs (3, 3, 5), act: Discrete(5)

动作空间包含鹿的五个互斥动作(四个方向+"静止"动作)。老虎具备相同移动能力,但额外拥有四个方向的攻击能力。

观察值方面,每只老虎获得一个 9×9 矩阵,包含五个信息平面。鹿的视野较短,其观察范围仅为 3×3。观察矩阵始终以当前智能体为中心,显示其周围网格状态。五个信息平面分别为:

  • 障碍:单元格存在墙壁为 1,反之为 0
  • 群体 1 (己方群体):单元格存在同组智能体为 1,反之为 0
  • 群体 1 (己方)生命值:单元格内同组成员的相对生命值
  • 群体 2 (敌方群体):单元格存在敌对智能体为 1,反之为 0
  • 群体 2 (敌方生命值):敌方相对生命值(无目标时为 0)

若配置更多群体,观察张量将包含额外平面。此外,MAgent 支持"迷你地图"功能,可显示各群体智能体的全局位置。本节禁用此功能,但可自行实验其对训练的影响。未开启时,智能体仅感知有限范围内环境;开启后则能获得更全局的视野。

群体 1 和 2 是相对于当前智能体定义的:因此鹿的第二个平面显示其他鹿的信息,而老虎的该平面显示其他老虎。这种设计使观察值具有群体独立性,便于需要时为不同群体训练统一策略。

观察值的可选部分还包括"额外特征",含智能体 ID、上一步动作、上一轮奖励及归一化位置等具体信息。这些功能虽在 MAgent 源码中有详细定义,但本节中不予采用。

(8) 我们通过循环持续执行操作,直到环境中无存活智能体。每次迭代中,为所有智能体采样随机动作并在环境中执行:

python 复制代码
    while env.agents:
        actions = {agent_id: env.action_space(agent_id).sample() for agent_id in env.agents}
        all_obs, all_rewards, all_dones, all_trunc, all_info = env.step(actions)
        recorder.capture_frame()

env.step() 函数返回的所有值都是以 agent_id 为键的字典。MAgent 环境的另一个重要特性是智能体集合具有动态性:智能体可能从环境中消失(例如死亡时)。在我们的"森林"环境中,老虎每步会损失 0.1 点生命值(捕食鹿后可恢复),而鹿仅在遭受攻击时损失生命值,每步会自动恢复(可能通过食草实现)。

当智能体死亡(老虎饿死或鹿被猎杀)时,all_dones 字典中对应条目将设为 True,下一次迭代时该智能体会从所有字典和 env.agents 列表中移除。因此单个智能体死亡后,整个回合仍会继续,训练时需考虑这一特性。

上述示例中的循环会持续执行直到没有存活智能体。由于老虎和鹿都采取随机行为(且老虎每步流失生命值),很可能所有老虎都会饿死,存活下来的鹿将无限期生存。但环境配置会在没有老虎时自动清除所有鹿,因此程序通常在 30-40 步后结束。

(9) 在循环结束时,我们统计智能体获得的总奖励及其存活步数:

python 复制代码
        for agent_id, r in all_rewards.items():
            sum_rewards[agent_id] += r
            sum_steps[agent_id] += 1
        step += 1

(10) 循环结束后,我们展示奖励排名前 20 的智能体:

python 复制代码
    final_rewards = list(sum_rewards.items())
    final_rewards.sort(key=lambda p: p[1], reverse=True)
    for agent_id, r in final_rewards[:20]:
        print(f"{agent_id}: got {r:.2f} in {sum_steps[agent_id]} steps")
    recorder.close()

输出如下所示:

程序最后会保存本回合的视频记录。下图展示了两个不同状态:游戏开始时和临近结束时。蓝点代表老虎,红点代表鹿,灰点代表墙壁。攻击方向通过黑色小箭头标示。

小结

在本节中,我们初步探讨了多智能体强化学习领域,其在交易模拟、通信网络等场景具有广泛应用前景。我们可以基于 MAgent 或 PySC2 等环境继续探索更多可能性。

系列链接

PyTorch强化学习实战(1)------强化学习(Reinforcement Learning,RL)详解

PyTorch强化学习实战(2)------强化学习环境库Gymnasium

PyTorch强化学习实战(3)------Gymnasium API扩展功能

PyTorch强化学习实战(4)------PyTorch基础

PyTorch强化学习实战(5)------PyTorch Ignite 事件驱动机制与实践

PyTorch强化学习实战(6)------交叉熵方法详解与实现

PyTorch强化学习实战(7)------表格学习与贝尔曼方程

PyTorch强化学习实战(8)------Q学习详解与实现

PyTorch强化学习实战(9)------深度Q学习

PyTorch强化学习实战(10)------强化学习高级组件

PyTorch强化学习实战(11)------N步DQN(N-step DQN)

PyTorch强化学习实战(12)------Double DQN(DDQN)

PyTorch强化学习实战(13)------噪声网络(NoisyNet-DQN)

PyTorch强化学习实战(14)------优先经验回放机制

PyTorch强化学习实战(15)------Dueling DQN

PyTorch强化学习实战(16)------Categorical DQN

PyTorch强化学习实战(17)------强化学习训练加速

PyTorch强化学习实战(18)------基于DQN处理股票交易问题

PyTorch强化学习实战(19)------策略梯度法

PyTorch强化学习实战(20)------优势演员-评论家(Advantage Actor-Critic, A2C)

PyTorch强化学习实战(21)------异步优势演员-评论家(Asynchronous Advantage Actor-Critic, A3C)

PyTorch强化学习实战(22)------将强化学习应用于TextWorld互动小说游戏

PyTorch强化学习实战(23)------强化学习在网页导航中的应用

PyTorch强化学习实战(24)------连续动作空间中的强化学习

PyTorch强化学习实战(25)------深度确定性策略梯度(DDPG)

PyTorch强化学习实战(26)------提升随机策略梯度稳定性

PyTorch强化学习实战(27)------进化策略在强化学习中的应用

PyTorch强化学习实战(28)------遗传算法在强化学习中的应用

PyTorch强化学习实战(29)------深入解析强化学习中的探索策略

PyTorch强化学习实战(30)------基于人类反馈的强化学习(RLHF)

PyTorch强化学习实战(31)------AlphaGo Zero详解与实现

PyTorch强化学习实战(32)------MuZero详解与实现

PyTorch强化学习实战(33)------深度强化学习在离散优化中的应用

相关推荐
我就是全世界29 分钟前
【轻AI越影视觉开发板】local_voice_process 语音功能模块教程——从音频采集到本地语音识别、多模态视觉交互与 ESP32-S31 硬核对比
人工智能·语音识别
hpoenixf33 分钟前
Agent 测评的身份难题:一次任务换了模型,历史成绩还能算数吗?
人工智能
hpoenixf1 小时前
专家、Subagent、Review、Judge:都是另一个模型,为什么不是同一回事?
人工智能
深蓝AI2 小时前
26.1% 的 Agent 技能带漏洞:skills 霸榜 GitHub 当天,NVIDIA 和 OpenAI 同时掏出安检仪
人工智能·程序员
锋行天下8 小时前
langgraph 后端发自定义数据,前端接收方法概括
人工智能·python
泯泷9 小时前
单个 AI 再强,为什么也干不过一支 AI 团队?
人工智能·llm·agent
浪遏9 小时前
认识 wm:从多平台发布到可扩展 CLI 的架构设计
人工智能
深蓝AI10 小时前
英伟达换帅当天,我先跑了一遍 GPU 环境体检:版本矩阵比人事更值得盯
人工智能·gpu
罗西的思考11 小时前
【轻量化复刻大模型 / KIMI K3】AttnRes 模块交互式演示
人工智能·算法·机器学习