• 文件 >
  • 使用 TorchRL 的多智慧體強化學習 (PPO) 教程
快捷方式

使用 TorchRL 的多智慧體強化學習 (PPO) 教程

作者: Matteo Bettini

另請參閱

BenchMARL 庫提供了使用 TorchRL 實現的最新 MARL 演算法。

本教程演示瞭如何使用 PyTorch 和 torchrl 解決多智慧體強化學習 (MARL) 問題。

為了便於使用,本教程將遵循已有教程的總體結構: 使用 TorchRL 的強化學習 (PPO) 教程。建議(非強制)在開始本教程之前熟悉該教程。

在本教程中,我們將使用 VMAS 中的 Navigation 環境,這是一個多機器人模擬器,同樣基於 PyTorch,可在裝置上執行並行批次模擬。

Navigation 環境中,我們需要訓練多個機器人(隨機位置生成)導航至其目標(也隨機位置生成),同時使用 LIDAR 感測器 避免彼此之間發生碰撞。

Navigation

多智慧體 Navigation 場景

主要學習內容

  • 如何在 TorchRL 中建立多智慧體環境,其 specs 的工作原理以及如何與庫整合;

  • 如何在 TorchRL 中使用 GPU 向量化環境;

  • 如何在 TorchRL 中建立不同的多智慧體網路架構(例如,使用引數共享、集中式評論家);

  • 如何使用 tensordict.TensorDict 來傳遞多智慧體資料;

  • 如何在多智慧體 MAPPO/IPPO 訓練迴圈中串聯所有庫元件(收集器、模組、回放緩衝區和損失函式)。

如果您在 Google Colab 中執行此教程,請確保安裝以下依賴項

!pip3 install torchrl
!pip3 install vmas
!pip3 install tqdm

近端策略最佳化 (PPO) 是一種策略梯度演算法,它收集一批資料並直接用於訓練策略,以在給定某些鄰近性約束下最大化預期回報。您可以將其視為基礎策略最佳化演算法 REINFORCE 的高階版本。更多資訊,請參閱 近端策略最佳化演算法 (Proximal Policy Optimization Algorithms) 論文。

這類演算法通常採用 on-policy(線上策略)方式訓練。這意味著,在每個學習迭代中,我們都有一個 取樣 階段和一個 訓練 階段。在迭代 \(t\)取樣 階段,使用當前策略 \(\mathbf{\pi}_t\) 收集智慧體在環境中互動的 rollouts。在 訓練 階段,所有收集到的 rollouts 立即饋送到訓練過程以執行反向傳播。這會產生更新後的策略,然後再次用於取樣。這個過程的迴圈執行構成了 on-policy learning(線上策略學習)。

On-policy learning

線上策略學習

在 PPO 演算法的訓練階段,使用一個 評論家 (critic) 來評估策略採取的行動的優劣。評論家學習估計特定狀態的價值(平均折扣回報)。然後 PPO 損失函式會將策略獲得的實際回報與評論家估計的回報進行比較,以確定所採取行動的優勢並指導策略最佳化。

在多智慧體設定中,情況有所不同。我們現在有多個策略 \(\mathbf{\pi}\),每個智慧體一個。策略通常是區域性的和去中心化的。這意味著單個智慧體的策略僅根據其觀測輸出該智慧體的行動。在 MARL 文獻中,這被稱為 去中心化執行 (decentralised execution)。另一方面,評論家存在不同的形式,主要包括

  • MAPPO 中,評論家是集中式的,並以系統的全域性狀態作為輸入。這可以是全域性觀測,或者僅僅是智慧體觀測的串聯。MAPPO 可用於執行 集中式訓練 (centralised training) 的場景,因為它需要訪問全域性資訊。

  • IPPO 中,評論家像策略一樣,只將各自智慧體的觀測作為輸入。這使得 去中心化訓練 (decentralised training) 成為可能,因為評論家和策略都只需要區域性資訊來計算其輸出。

集中式評論家有助於克服多個智慧體同時學習時的非平穩性,但另一方面,它們可能會受到其龐大輸入空間的影響。在本教程中,我們將能夠訓練這兩種形式,並將討論引數共享(在智慧體之間共享網路引數的做法)如何影響每種形式。

本教程結構如下

  1. 首先,我們將定義一組要使用的超引數。

  2. 接下來,我們將使用 TorchRL 對 VMAS 模擬器的包裝器建立一個向量化多智慧體環境。

  3. 接下來,我們將設計策略網路和評論家網路,討論各種選擇對引數共享和評論家中心化的影響。

  4. 接下來,我們將建立取樣收集器和回放緩衝區。

  5. 最後,我們將執行訓練迴圈並分析結果。

如果您在 Colab 或帶有 GUI 的機器中執行此教程,您還可以選擇在訓練前後渲染並可視化您自己訓練的策略。

讓我們匯入依賴項

# Torch
import torch

# Tensordict modules
from tensordict.nn import set_composite_lp_aggregate, TensorDictModule
from tensordict.nn.distributions import NormalParamExtractor
from torch import multiprocessing

# Data collection
from torchrl.collectors import SyncDataCollector
from torchrl.data.replay_buffers import ReplayBuffer
from torchrl.data.replay_buffers.samplers import SamplerWithoutReplacement
from torchrl.data.replay_buffers.storages import LazyTensorStorage

# Env
from torchrl.envs import RewardSum, TransformedEnv
from torchrl.envs.libs.vmas import VmasEnv
from torchrl.envs.utils import check_env_specs

# Multi-agent network
from torchrl.modules import MultiAgentMLP, ProbabilisticActor, TanhNormal

# Loss
from torchrl.objectives import ClipPPOLoss, ValueEstimators

# Utils
torch.manual_seed(0)
from matplotlib import pyplot as plt
from tqdm import tqdm

定義超引數

我們為本教程設定超引數。根據可用資源,可以選擇在 GPU 或其他裝置上執行策略和模擬器。您可以調整其中一些值以適應計算需求。

# Devices
is_fork = multiprocessing.get_start_method() == "fork"
device = (
    torch.device(0)
    if torch.cuda.is_available() and not is_fork
    else torch.device("cpu")
)
vmas_device = device  # The device where the simulator is run (VMAS can run on GPU)

# Sampling
frames_per_batch = 6_000  # Number of team frames collected per training iteration
n_iters = 5  # Number of sampling and training iterations
total_frames = frames_per_batch * n_iters

# Training
num_epochs = 30  # Number of optimization steps per training iteration
minibatch_size = 400  # Size of the mini-batches in each optimization step
lr = 3e-4  # Learning rate
max_grad_norm = 1.0  # Maximum norm for the gradients

# PPO
clip_epsilon = 0.2  # clip value for PPO loss
gamma = 0.99  # discount factor
lmbda = 0.9  # lambda for generalised advantage estimation
entropy_eps = 1e-4  # coefficient of the entropy term in the PPO loss

# disable log-prob aggregation
set_composite_lp_aggregate(False).set()

環境

多智慧體環境模擬多個智慧體與世界互動。TorchRL API 允許整合各種型別的多智慧體環境。一些示例包括具有共享或單獨智慧體獎勵、完成標誌和觀測的環境。有關 TorchRL 中多智慧體環境 API 工作原理的更多資訊,您可以查閱專門的 文件章節

特別是 VMAS 模擬器,它對具有獨立獎勵、資訊、觀測和行動的智慧體進行建模,但共享一個集體完成標誌。此外,它使用 vectorization(向量化)來批次執行模擬。這意味著其所有狀態和物理量都是 PyTorch 張量,其第一個維度代表批次中並行環境的數量。這使得能夠利用 GPU 的單指令多資料 (SIMD) 範例,並透過利用 GPU warps 中的並行化顯著加速平行計算。這也意味著,在 TorchRL 中使用它時,模擬和訓練都可以在裝置上執行,而無需將資料傳遞給 CPU。

我們今天將解決的多智慧體任務是 Navigation(參見上面的動畫圖)。在 Navigation 中,隨機生成的智慧體(帶周圍點的圓圈)需要導航到它們的目標(較小的圓圈)。智慧體需要使用 LIDARs(它們周圍的點)來避免彼此碰撞。智慧體在具有阻力和彈性碰撞的 2D 連續世界中行動。它們的行動是決定其加速度的 2D 連續力。獎勵由三部分組成:碰撞懲罰、基於到目標的距離的獎勵,以及所有智慧體到達目標時給予的最終共享獎勵。基於距離的項計算為智慧體與其目標之間在連續兩個時間步內的相對距離的差。每個智慧體觀測其位置、速度、lidar 讀數以及到其目標的相對位置。

現在我們將例項化環境。對於本教程,我們將把回合限制在 max_steps 內,之後會設定 done 標誌。此功能已在 VMAS 模擬器中提供,但也可以替代使用 TorchRL 的 StepCount transform。我們還將使用 num_vmas_envs 個向量化環境,以利用批次模擬。

max_steps = 100  # Episode steps before done
num_vmas_envs = (
    frames_per_batch // max_steps
)  # Number of vectorized envs. frames_per_batch should be divisible by this number
scenario_name = "navigation"
n_agents = 3

env = VmasEnv(
    scenario=scenario_name,
    num_envs=num_vmas_envs,
    continuous_actions=True,  # VMAS supports both continuous and discrete actions
    max_steps=max_steps,
    device=vmas_device,
    # Scenario kwargs
    n_agents=n_agents,  # These are custom kwargs that change for each VMAS scenario, see the VMAS repo to know more.
)

環境不僅由其模擬器和 transforms 定義,還由一系列元資料定義,這些元資料描述了其執行過程中可能出現的情況。出於效率考慮,TorchRL 對環境 specs 要求相當嚴格,但您可以輕鬆檢查您的環境 specs 是否足夠。在我們的示例中,VmasEnv 負責為您的環境設定適當的 specs,因此您不必為此操心。

有四個需要關注的 specs

  • action_spec 定義了行動空間;

  • reward_spec 定義了獎勵域;

  • done_spec 定義了 done 域;

  • observation_spec 定義了環境步驟所有其他輸出的域;

print("action_spec:", env.full_action_spec)
print("reward_spec:", env.full_reward_spec)
print("done_spec:", env.full_done_spec)
print("observation_spec:", env.observation_spec)

使用剛剛顯示的命令,我們可以訪問每個值的域。這樣做我們可以看到,除了 done 之外,所有 specs 都具有前導形狀 (num_vmas_envs, n_agents)。這表示這些值將存在於每個獨立環境的每個智慧體中。另一方面,done spec 具有前導形狀 num_vmas_envs,表示 done 在智慧體之間共享。

TorchRL 有一種方式來跟蹤哪些 MARL specs 是共享的,哪些不是。實際上,具有額外智慧體維度(即每個智慧體都不同)的 specs 將包含在一個內部的“agents”鍵中。

正如您所見,獎勵和行動 spec 都包含“agent”鍵,這意味著屬於這些 specs 的 tensordicts 中的條目將巢狀在一個“agents”tensordict 中,將所有每個智慧體的值分組在一起。

為了快速訪問 tensordicts 中每個這些值的鍵,我們可以簡單地向環境詢問相應的鍵,我們將立即瞭解哪些是每個智慧體的,哪些是共享的。此資訊對於告知所有其他 TorchRL 元件在哪裡找到每個值非常有用。

print("action_keys:", env.action_keys)
print("reward_keys:", env.reward_keys)
print("done_keys:", env.done_keys)

Transforms

我們可以將所需的任何 TorchRL transform 新增到我們的環境中。這些將以某種期望的方式修改其輸入/輸出。我們強調,在多智慧體環境中,顯式提供要修改的鍵至關重要。

例如,在這種情況下,我們將例項化一個 RewardSum transform,它將回合內的獎勵求和。我們將告訴此 transform 獎勵鍵在哪裡以及將求和的回合獎勵寫到哪裡。轉換後的環境將繼承包裝環境的裝置和元資料,並根據其包含的 transforms 序列進行轉換。

env = TransformedEnv(
    env,
    RewardSum(in_keys=[env.reward_key], out_keys=[("agents", "episode_reward")]),
)

check_env_specs() 函式執行一個小的 rollout 並將其輸出與環境 specs 進行比較。如果沒有引發錯誤,我們可以確信 specs 被正確定義。

check_env_specs(env)

Rollout

為了好玩,讓我們看看一個簡單的隨機 rollout 是什麼樣子。您可以呼叫 env.rollout(n_steps) 並獲取環境輸入和輸出的概述。行動將自動從 action spec 域中隨機抽取。

n_rollout_steps = 5
rollout = env.rollout(n_rollout_steps)
print("rollout of three steps:", rollout)
print("Shape of the rollout TensorDict:", rollout.batch_size)

我們可以看到我們的 rollout 具有 batch_size(num_vmas_envs, n_rollout_steps)。這意味著其中的所有張量都將具有這些前導維度。

更深入地看,我們可以看到輸出的 tensordict 可以按以下方式劃分

  • 在 root 中(透過執行 rollout.exclude("next") 訪問),我們將找到在第一個時間步呼叫 reset 後可用的所有鍵。我們可以透過索引 n_rollout_steps 維度檢視它們在 rollout 步驟中的演變。在這些鍵中,我們將找到 rollout["agents"] tensordict 中每個智慧體不同的鍵,它將具有批次大小 (num_vmas_envs, n_rollout_steps, n_agents),表明它儲存了額外的智慧體維度。在此智慧體 tensordict 之外的鍵將是共享的鍵(在本例中只有 done)。

  • 在 next 中(透過執行 rollout.get("next") 訪問)。我們將找到與 root 相同的結構,但針對僅在 step 後可用的鍵。

在 TorchRL 中,約定是 done 和 observations 將同時存在於 root 和 next 中(因為它們在 reset 時和 step 後都可用)。Action 將僅存在於 root 中(因為 step 不會產生 action),而 reward 將僅存在於 next 中(因為 reset 時沒有 reward)。此結構遵循 Reinforcement Learning: An Introduction (Sutton and Barto) 中的結構,其中 root 表示世界 step 的時間 \(t\) 的資料,next 表示時間 \(t+1\) 的資料。

渲染一個隨機 rollout

如果您在 Google Colab 上,或者在裝有 OpenGL 和 GUI 的機器上,您實際上可以渲染一個隨機 rollout。這將讓您瞭解隨機策略在此任務中能 achieve 什麼,以便與您自己訓練的策略進行比較!

要渲染一個 rollout,請遵循本教程末尾 Render 部分的說明,並只需從 env.rollout() 中移除 policy=policy 這一行。

策略

PPO 利用隨機策略來處理探索。這意味著我們的神經網路必須輸出一個分佈的引數,而不是對應於所採取行動的單個值。

由於資料是連續的,我們使用 Tanh-Normal 分佈來遵守行動空間的邊界。TorchRL 提供了這樣的分佈,我們唯一需要關注的是構建一個輸出正確數量引數的神經網路。

在這種情況下,每個智慧體的行動將由一個二維獨立正態分佈表示。為此,我們的神經網路必須為每個行動輸出一個均值和一個標準差。因此,每個智慧體將有 2 * n_actions_per_agents 個輸出。

我們需要做出的另一個重要決定是是否讓我們的智慧體 共享策略引數 (share the policy parameters)。一方面,共享引數意味著它們將都共享相同的策略,這將使它們能夠從彼此的經驗中受益。這也會加快訓練速度。另一方面,它會使它們的行為 homogenous(同質化),因為它們實際上共享相同的模型。對於本示例,我們將啟用共享,因為我們不介意同質性並且可以從計算速度中受益,但在您自己的問題中始終考慮這個決定很重要!

我們分三步設計策略。

第一步:定義一個神經網路 n_obs_per_agent -> 2 * n_actions_per_agents

為此,我們使用 MultiAgentMLP,這是一個專門為多智慧體設計的 TorchRL 模組,提供了豐富的定製選項。

share_parameters_policy = True

policy_net = torch.nn.Sequential(
    MultiAgentMLP(
        n_agent_inputs=env.observation_spec["agents", "observation"].shape[
            -1
        ],  # n_obs_per_agent
        n_agent_outputs=2 * env.action_spec.shape[-1],  # 2 * n_actions_per_agents
        n_agents=env.n_agents,
        centralised=False,  # the policies are decentralised (ie each agent will act from its observation)
        share_params=share_parameters_policy,
        device=device,
        depth=2,
        num_cells=256,
        activation_class=torch.nn.Tanh,
    ),
    NormalParamExtractor(),  # this will just separate the last dimension into two outputs: a loc and a non-negative scale
)

第二步:將神經網路包裝在 TensorDictModule

這只是一個模組,它將從 tensordict 讀取 in_keys,將它們饋送到神經網路,並在 out_keys 位置原地寫入輸出。

請注意,我們使用 ("agents", ...) 鍵,因為這些鍵表示具有額外 n_agents 維度的資料。

policy_module = TensorDictModule(
    policy_net,
    in_keys=[("agents", "observation")],
    out_keys=[("agents", "loc"), ("agents", "scale")],
)

第三步:將 TensorDictModule 包裝在 ProbabilisticActor

現在我們需要根據正態分佈的位置和尺度構建一個分佈。為此,我們指示 ProbabilisticActor 類根據位置和尺度引數構建一個 TanhNormal。我們還提供了此分佈的最小值和最大值,這些值我們從環境 specs 中獲取。

in_keys 的名稱(以及因此上面 TensorDictModuleout_keys 的名稱)必須以 TanhNormal 分佈建構函式的關鍵字引數(loc 和 scale)結尾。

policy = ProbabilisticActor(
    module=policy_module,
    spec=env.action_spec_unbatched,
    in_keys=[("agents", "loc"), ("agents", "scale")],
    out_keys=[env.action_key],
    distribution_class=TanhNormal,
    distribution_kwargs={
        "low": env.full_action_spec_unbatched[env.action_key].space.low,
        "high": env.full_action_spec_unbatched[env.action_key].space.high,
    },
    return_log_prob=True,
)  # we'll need the log-prob for the PPO loss

評論家網路

評論家網路是 PPO 演算法的關鍵組成部分,儘管在取樣時未使用它。此模組將讀取觀測並返回相應的價值估計。

如前所述,應該仔細考慮 共享評論家引數 (sharing the critic parameters) 的決定。通常,引數共享會加快訓練收斂速度,但需要考慮一些重要的因素

  • 當智慧體具有不同的獎勵函式時,不建議共享,因為評論家需要學習為同一狀態分配不同的值(例如,在混合合作競爭環境中)。

  • 在去中心化訓練設定中,如果沒有額外的基礎設施來同步引數,則無法執行共享。

在所有其他情況下,如果獎勵函式(與獎勵區分開來)對所有智慧體都是相同的(如當前場景),共享可以提供改進的效能。這可能會以智慧體策略的同質性為代價。總的來說,瞭解哪種選擇更可取的方法是快速實驗這兩種選項。

在這裡,我們還需要在 MAPPO 和 IPPO 之間做出選擇

  • 使用 MAPPO,我們將獲得一個具有完全可觀測性的中心評論家(即,它將把所有串聯的智慧體觀測作為輸入)。我們之所以能做到這一點,是因為我們在模擬器中,且訓練是集中式的。

  • 使用 IPPO,我們將擁有一個區域性的去中心化評論家,就像策略一樣。

無論如何,評論家輸出的形狀將是 (..., n_agents, 1)。如果評論家是集中式且共享的,則沿 n_agents 維度的所有值都將相同。

share_parameters_critic = True
mappo = True  # IPPO if False

critic_net = MultiAgentMLP(
    n_agent_inputs=env.observation_spec["agents", "observation"].shape[-1],
    n_agent_outputs=1,  # 1 value per agent
    n_agents=env.n_agents,
    centralised=mappo,
    share_params=share_parameters_critic,
    device=device,
    depth=2,
    num_cells=256,
    activation_class=torch.nn.Tanh,
)

critic = TensorDictModule(
    module=critic_net,
    in_keys=[("agents", "observation")],
    out_keys=[("agents", "state_value")],
)

讓我們試試我們的策略模組和評論家模組。如前所述,使用 TensorDictModule 可以直接讀取環境的輸出以執行這些模組,因為它們知道要讀取什麼資訊以及寫到哪裡。

從這一點開始,多智慧體特定的元件已經例項化,我們將只需使用與單智慧體學習相同的元件。這不是很棒嗎?

print("Running policy:", policy(env.reset()))
print("Running value:", critic(env.reset()))

資料收集器

TorchRL 提供了一組資料收集器類。簡單來說,這些類執行三個操作:重置環境、使用策略和最新觀測計算行動、在環境中執行一個 step,並重復最後兩個步驟,直到環境發出停止訊號(或達到 done 狀態)。

我們將使用最簡單的資料收集器,其輸出與環境 rollout 相同,唯一的區別是它會自動重置 done 狀態,直到收集到所需的幀。

collector = SyncDataCollector(
    env,
    policy,
    device=vmas_device,
    storing_device=device,
    frames_per_batch=frames_per_batch,
    total_frames=total_frames,
)

回放緩衝區

回放緩衝區是 off-policy RL 演算法的常見組成部分。在 on-policy 場景中,每次收集一批資料時都會重新填充回放緩衝區,並且其資料會重複消耗一定的 epoch 次數。

為 PPO 使用回放緩衝區並非強制,我們可以直接線上使用收集的資料,但使用這些類使得我們能夠以可復現的方式輕鬆構建內部訓練迴圈。

replay_buffer = ReplayBuffer(
    storage=LazyTensorStorage(
        frames_per_batch, device=device
    ),  # We store the frames_per_batch collected at each iteration
    sampler=SamplerWithoutReplacement(),
    batch_size=minibatch_size,  # We will sample minibatches of this size
)

損失函式

為了方便起見,可以直接從 TorchRL 匯入 PPO 損失函式,使用 ClipPPOLoss 類。這是利用 PPO 最簡單的方法:它隱藏了 PPO 的數學運算及其相關的控制流程。

PPO 需要計算一些“優勢估計”。簡而言之,優勢是一個值,它反映了在處理偏差/方差權衡時對回報值的期望。要計算優勢,只需 (1) 構建優勢模組,該模組使用我們的價值運算元,以及 (2) 在每個 epoch 之前將每批資料透過它。GAE 模組將使用新的 "advantage""value_target" 條目更新輸入的 TensorDict"value_target" 是一個無梯度的張量,表示價值網路應透過輸入觀測表示的經驗價值。這兩者都將由 ClipPPOLoss 用於返回策略和價值損失。

loss_module = ClipPPOLoss(
    actor_network=policy,
    critic_network=critic,
    clip_epsilon=clip_epsilon,
    entropy_coef=entropy_eps,
    normalize_advantage=False,  # Important to avoid normalizing across the agent dimension
)
loss_module.set_keys(  # We have to tell the loss where to find the keys
    reward=env.reward_key,
    action=env.action_key,
    value=("agents", "state_value"),
    # These last 2 keys will be expanded to match the reward shape
    done=("agents", "done"),
    terminated=("agents", "terminated"),
)


loss_module.make_value_estimator(
    ValueEstimators.GAE, gamma=gamma, lmbda=lmbda
)  # We build GAE
GAE = loss_module.value_estimator

optim = torch.optim.Adam(loss_module.parameters(), lr)

訓練迴圈

現在我們擁有編寫訓練迴圈所需的所有部分。步驟包括

  • 收集資料
    • 計算優勢
      • 迴圈遍歷 epoch
        • 迴圈遍歷 minibatches 計算損失值
          • 反向傳播

          • 最佳化

        • 重複

      • 重複

    • 重複

  • 重複

pbar = tqdm(total=n_iters, desc="episode_reward_mean = 0")

episode_reward_mean_list = []
for tensordict_data in collector:
    tensordict_data.set(
        ("next", "agents", "done"),
        tensordict_data.get(("next", "done"))
        .unsqueeze(-1)
        .expand(tensordict_data.get_item_shape(("next", env.reward_key))),
    )
    tensordict_data.set(
        ("next", "agents", "terminated"),
        tensordict_data.get(("next", "terminated"))
        .unsqueeze(-1)
        .expand(tensordict_data.get_item_shape(("next", env.reward_key))),
    )
    # We need to expand the done and terminated to match the reward shape (this is expected by the value estimator)

    with torch.no_grad():
        GAE(
            tensordict_data,
            params=loss_module.critic_network_params,
            target_params=loss_module.target_critic_network_params,
        )  # Compute GAE and add it to the data

    data_view = tensordict_data.reshape(-1)  # Flatten the batch size to shuffle data
    replay_buffer.extend(data_view)

    for _ in range(num_epochs):
        for _ in range(frames_per_batch // minibatch_size):
            subdata = replay_buffer.sample()
            loss_vals = loss_module(subdata)

            loss_value = (
                loss_vals["loss_objective"]
                + loss_vals["loss_critic"]
                + loss_vals["loss_entropy"]
            )

            loss_value.backward()

            torch.nn.utils.clip_grad_norm_(
                loss_module.parameters(), max_grad_norm
            )  # Optional

            optim.step()
            optim.zero_grad()

    collector.update_policy_weights_()

    # Logging
    done = tensordict_data.get(("next", "agents", "done"))
    episode_reward_mean = (
        tensordict_data.get(("next", "agents", "episode_reward"))[done].mean().item()
    )
    episode_reward_mean_list.append(episode_reward_mean)
    pbar.set_description(f"episode_reward_mean = {episode_reward_mean}", refresh=False)
    pbar.update()

結果

讓我們繪製每個回合獲得的平均獎勵

為了讓訓練持續更長時間,請增加 n_iters 超引數。

plt.plot(episode_reward_mean_list)
plt.xlabel("Training iterations")
plt.ylabel("Reward")
plt.title("Episode reward mean")
plt.show()

渲染

如果您在帶有 GUI 的機器中執行此教程,可以透過執行以下命令渲染訓練好的策略

with torch.no_grad():
   env.rollout(
       max_steps=max_steps,
       policy=policy,
       callback=lambda env, _: env.render(),
       auto_cast_to_device=True,
       break_when_any_done=False,
   )

如果您在 Google Colab 中執行此教程,可以透過執行以下命令渲染訓練好的策略

!apt-get update
!apt-get install -y x11-utils
!apt-get install -y xvfb
!pip install pyvirtualdisplay
import pyvirtualdisplay
display = pyvirtualdisplay.Display(visible=False, size=(1400, 900))
display.start()
from PIL import Image

def rendering_callback(env, td):
    env.frames.append(Image.fromarray(env.render(mode="rgb_array")))
env.frames = []
with torch.no_grad():
   env.rollout(
       max_steps=max_steps,
       policy=policy,
       callback=rendering_callback,
       auto_cast_to_device=True,
       break_when_any_done=False,
   )
env.frames[0].save(
    f"{scenario_name}.gif",
    save_all=True,
    append_images=env.frames[1:],
   duration=3,
   loop=0,
)

from IPython.display import Image
Image(open(f"{scenario_name}.gif", "rb").read())

結論和後續步驟

在本教程中,我們看到了

  • 如何在 TorchRL 中建立多智慧體環境,其 specs 的工作原理以及如何與庫整合;

  • 如何在 TorchRL 中使用 GPU 向量化環境;

  • 如何在 TorchRL 中建立不同的多智慧體網路架構(例如,使用引數共享、集中式評論家);

  • 如何使用 tensordict.TensorDict 來傳遞多智慧體資料;

  • 如何在多智慧體 MAPPO/IPPO 訓練迴圈中串聯所有庫元件(收集器、模組、回放緩衝區和損失函式)。

現在您已經熟練掌握了多智慧體 DDPG,您可以檢視 GitHub 倉庫中所有 TorchRL 的多智慧體實現。這些是許多流行 MARL 演算法(如本教程中介紹的演算法、QMIX、MADDPG、IQL 等等)的程式碼指令碼!

您還可以檢視我們的另一個多智慧體教程,瞭解如何在 PettingZoo/VMAS 中訓練具有多個智慧體組的競爭性 MADDPG/IDDPG: 使用 TorchRL 的競爭性多智慧體強化學習 (DDPG) 教程

如果您有興趣在 TorchRL 中建立或包裝您自己的多智慧體環境,可以查閱專門的 文件章節

最後,您可以修改本教程的引數,嘗試許多其他配置和場景,成為一名 MARL 大師。以下是一些您可以在 VMAS 中嘗試的可能場景的影片。

VMAS scenarios

VMAS 中可用的場景

由 Sphinx-Gallery 生成的相簿

文件

訪問 PyTorch 的完整開發者文件

檢視文件

教程

獲取面向初學者和高階開發者的深入教程

檢視教程

資源

查詢開發資源並解答您的疑問

檢視資源