核心概念#
为了帮助您对库的工作原理有一个高层级的理解,在本页中,您将了解 RLlib 的关键概念和总体架构。
RLlib 概述: RLlib 的核心组件是 Algorithm 类,它充当执行 RL 实验的运行时。您使用 Algorithm 的入口是 AlgorithmConfig(蓝色)类,它允许您管理可用的配置设置,例如学习率或模型架构。大多数 Algorithm 对象都有 EnvRunner actor(蓝色)从 RL 环境 收集训练样本,以及 Learner actor(黄色)计算梯度并更新您的 模型。算法在更新后同步模型权重。#
AlgorithmConfig 和 Algorithm#
提示
以下是关于 **RLlib AlgorithmConfigs 和 Algorithms** 的快速概述。有关 Algorithm 类 的详细描述,请参见此处。
RLlib 的 Algorithm 类作为您 RL 实验的运行时,汇集了学习最优解所需的所有组件,以解决您的 RL 环境 问题。它公开了强大的 Python API 来控制您的实验运行。
使用各种 RLlib Algorithm 类型的入口是各自的 AlgorithmConfig 类,允许您以一种经过检查且类型安全的方式配置可用设置。例如,要配置一个 PPO(“Proximal Policy Optimization”)算法实例,您可以使用 PPOConfig 类。
在构建过程中,Algorithm 首先设置其 EnvRunnerGroup,其中包含 n 个 EnvRunner actor,以及其 LearnerGroup,其中包含 m 个 Learner actor。这样,您可以将样本收集和训练的规模从单个核心扩展到集群中的数千个核心。
有关更多详细信息,请参阅此 扩展指南。
您可以通过两种方式与 Algorithm 进行交互和运行。
您可以直接通过 Python API 创建和管理其实例。
由于
Algorithm类是 Tune Trainable API 的子类,因此您可以使用 Ray Tune 来更轻松地管理您的实验和调整超参数。
以下示例演示了 RLlib 的 PPO(“Proximal Policy Optimization”)算法。
from ray.rllib.algorithms.ppo import PPOConfig
# Configure.
config = (
PPOConfig()
.environment("CartPole-v1")
.training(
train_batch_size_per_learner=2000,
lr=0.0004,
)
)
# Build the Algorithm.
algo = config.build()
# Train for one iteration, which is 2000 timesteps (1 train batch).
print(algo.train())
from ray import tune
from ray.rllib.algorithms.ppo import PPOConfig
# Configure.
config = (
PPOConfig()
.environment("CartPole-v1")
.training(
train_batch_size_per_learner=2000,
lr=0.0004,
)
)
# Train through Ray Tune.
results = tune.Tuner(
"PPO",
param_space=config,
# Train for 4000 timesteps (2 iterations).
run_config=tune.RunConfig(stop={"num_env_steps_sampled_lifetime": 4000}),
).fit()
RL 环境#
提示
以下是关于 **RL 环境** 的快速概述。有关在 RLlib 中使用 RL 环境的详细描述,请参见 此处。
强化学习 (RL) 环境是一个结构化的空间,例如模拟器或真实世界的受控部分,其中一个或多个代理进行交互并学习以实现特定目标。环境定义了一个观察空间,这是每个时间步可观察张量的结构和形状;一个动作空间,它定义了代理在每个时间步的可用动作;一个奖励函数;以及当应用动作时控制环境转换的规则。
一个简单的 **RL 环境**,代理从 reset() 方法返回的初始观察开始。代理,可能由神经网络策略控制,将动作(如 right 或 jump)发送到环境的 step() 方法,该方法返回一个奖励。在此,奖励值为 +5 表示到达目标,否则为 0。环境还返回一个布尔标志,指示剧集是否完成。#
环境的复杂性可能各不相同,从简单的任务(如在网格世界中导航)到高度复杂的系统(如自动驾驶模拟器、机器人控制环境或多人游戏)。
RLlib 通过在训练迭代中播放许多 剧集 来与环境交互,以收集数据,例如生成的观察、采取的动作、收到的奖励和 done 标志(请参见上图)。然后,它将这些剧集数据转换为训练批次以进行模型更新。这些模型更新的目标是改变代理的行为,从而使其在代理生命周期内获得的奖励总和最大化。
RLModules#
提示
以下是关于 **RLlib RLModules** 的快速概述。有关 RLModule 类 的详细描述,请参见 此处。
RLModules 是特定于深度学习框架的神经网络包装器。RLlib 的 EnvRunners 在遍历 RL 环境 时使用它们来计算动作,而 RLlib 的 Learners 使用 RLModule 实例来计算损失和梯度,然后再更新它们。
**RLModule 概述**:(左) 一个最小的 RLModule 包含一个神经网络,并定义了其在探索、推理和训练逻辑中的前向传播。(右) 在更复杂的设置中,一个 MultiRLModule 包含许多子模块,每个子模块本身就是一个 RLModule 实例,并由 ModuleID 标识,允许您实现任意复杂的、多模型的和多代理的算法。#
简而言之,RLModule 承载神经网络模型,并定义了在 RL 生命周期的三个阶段如何使用它们:探索,用于收集训练数据;推理,用于在评估或生产环境中计算动作;以及训练,用于计算损失函数的输入。
您可以选择使用 RLlib 内置的默认模型并根据需要进行配置,例如更改层数或激活函数,或者 用 PyTorch 编写您自己的自定义模型,从而允许您实现任何架构和计算逻辑。
**EnvRunner actor 中的 RLModule**:EnvRunner 操作其 RLModule 的仅推理版本的一个副本,仅使用它来计算动作。#
由 Algorithm 的 EnvRunnerGroup 管理的每个 EnvRunner actor 都拥有用户 RLModule 的一个副本。此外,由 Algorithm 的 LearnerGroup 管理的每个 Learner actor 都拥有一个 RLModule 副本。
EnvRunner 副本通常是其 inference_only 版本,这意味着不需要基本动作计算的组件(例如,值函数估计)不存在,以节省内存。
**Learner actor 中的 RLModule**:Learner 操作其 RLModule 的一个副本,计算损失函数的输入、损失本身以及模型的梯度,然后通过 Learner 的优化器更新 RLModule。#
剧集#
提示
以下是关于 **Episode** 的快速概述。有关 Episode 类的详细描述,请参见 此处。
RLlib 以 Episodes 的形式传递所有训练数据。
SingleAgentEpisode 类描述了单代理轨迹。 MultiAgentEpisode 类包含多个这样的单代理剧集,并描述了各个代理相对于彼此的时间步和模式。
两个 Episode 类都存储了在遍历 RL 环境 时生成的整个轨迹数据。这些数据包括观察、信息字典、动作、奖励、终止信号以及沿途的任何模型计算,如循环状态、动作 logits 或动作对数概率。
提示
有关 RLlib 的标准化列名,请参见此处。
请注意,剧集方便地不必存储任何 next obs 信息,因为它总是与 obs 下的信息重叠。此设计节省了近 50% 的内存,因为观察结果通常是轨迹中最大的部分。对于有状态网络,state_in 和 state_out 信息也同样如此。RLlib 只在剧集中保留 state_out 键。
通常,RLlib 通过 Algorithm 的 EnvRunnerGroup 中的 EnvRunner actor 生成大小为 config.rollout_fragment_length 的剧集块,并将尽可能多的剧集块发送到每个 Learner actor,以构建一个大小完全为 config.train_batch_size_per_learner 的训练批次。
典型的 SingleAgentEpisode 对象大致如下所示。
# A SingleAgentEpisode of length 20 has roughly the following schematic structure.
# Note that after these 20 steps, you have 20 actions and rewards, but 21 observations and info dicts
# due to the initial "reset" observation/infos.
episode = {
'obs': np.ndarray((21, 4), dtype=float32), # 21 due to additional reset obs
'infos': [{}, {}, {}, {}, .., {}, {}], # infos are always lists of dicts
'actions': np.ndarray((20,), dtype=int64), # Discrete(4) action space
'rewards': np.ndarray((20,), dtype=float32),
'extra_model_outputs': {
'action_dist_inputs': np.ndarray((20, 4), dtype=float32), # Discrete(4) action space
},
'is_terminated': False, # <- single bool
'is_truncated': True, # <- single bool
}
对于复杂的观察,例如 gym.spaces.Dict,剧集在一个与观察空间完全类似的结构中存储所有观察结果,其中 NumPy 数组位于该字典的叶节点。例如:
episode_w_complex_observations = {
'obs': {
"camera": np.ndarray((21, 64, 64, 3), dtype=float32), # RGB images
"sensors": {
"front": np.ndarray((21, 15), dtype=float32), # 1D tensors
"rear": np.ndarray((21, 5), dtype=float32), # another batch of 1D tensors
},
},
...
由于 RLlib 将所有值保留在 NumPy 数组中,这使得跨网络的高效编码和传输成为可能。
在 多人模式 下,EnvRunnerGroup 会生成 MultiAgentEpisode 实例。
注意
Ray 团队正在努力为 MultiAgentEpisode 类编写详细的描述。一旦完成,Ray 团队将在此处链接到正确的文档页面。
EnvRunner:结合 RL 环境和 RLModule#
给定 RL 环境 和一个 RLModule,EnvRunner 会生成 Episodes 列表。
它通过执行经典的 RL 环境交互循环来实现这一点。高效的样本收集可能很棘手,特别是当利用环境向量化、有状态循环神经网络或在多人设置下运行时。
RLlib 提供了两个内置的 EnvRunner 类:SingleAgentEnvRunner 和 MultiAgentEnvRunner,它们会自动处理这些复杂性。RLlib 根据您的配置选择正确的类型,特别是 config.environment() 和 config.multi_agent() 设置。
提示
调用 is_multi_agent() 方法可以找出您的配置是否为多人模式。
RLlib 通过 EnvRunnerGroup API 捆绑了多个 EnvRunner actor。
您也可以独立使用 EnvRunner,通过调用其 sample() 方法来生成 Episodes 列表。
以下是一个创建一组远程 EnvRunner actor 并利用它们并行收集经验的示例。
import tree # pip install dm_tree
import ray
from ray.rllib.algorithms.ppo import PPOConfig
from ray.rllib.env.single_agent_env_runner import SingleAgentEnvRunner
# Configure the EnvRunners.
config = (
PPOConfig()
.environment("Acrobot-v1")
.env_runners(num_env_runners=2, num_envs_per_env_runner=1)
)
# Create the EnvRunner actors.
env_runners = [
ray.remote(SingleAgentEnvRunner).remote(config=config)
for _ in range(config.num_env_runners)
]
# Gather lists of `SingleAgentEpisode`s (each EnvRunner actor returns one
# such list with exactly two episodes in it).
episodes = ray.get([
er.sample.remote(num_episodes=3)
for er in env_runners
])
# Two remote EnvRunners used.
assert len(episodes) == 2
# Each EnvRunner returns three episodes
assert all(len(eps_list) == 3 for eps_list in episodes)
# Report the returns of all episodes collected
for episode in tree.flatten(episodes):
print("R=", episode.get_return())
Learner:结合 RLModule、损失函数和优化器#
给定 RLModule 和一个或多个优化器及损失函数,Learner 会计算损失和梯度,然后更新 RLModule。
这种更新步骤的输入数据以 episodes 列表的形式传入,这些列表要么由 Learner 自己的连接器管道,要么由外部连接器转换为最终的训练批次。
注意
ConnectorV2 文档正在完善中。Ray 团队将在完成后链接到正确的文档页面。
Learner 实例是算法特定的,这主要是因为不同的 RL 算法使用了各种损失函数。
RLlib 始终通过 LearnerGroup API 捆绑了多个 Learner actor,自动对训练数据应用分布式数据并行(DDP)。您也可以独立使用 Learner 来使用 Episodes 列表更新您的 RLModule。
以下是创建远程 Learner actor 并调用其 update() 方法的示例。
import gymnasium as gym
import ray
from ray.rllib.algorithms.ppo import PPOConfig
from ray.rllib.core.rl_module.default_model_config import DefaultModelConfig
# Configure the Learner.
config = (
PPOConfig()
.environment("Acrobot-v1")
.training(lr=0.0001)
.rl_module(model_config=DefaultModelConfig(fcnet_hiddens=[64, 32]))
)
# Get the Learner class.
ppo_learner_class = config.get_default_learner_class()
# Create the Learner actor.
learner_actor = ray.remote(ppo_learner_class).remote(
config=config,
module_spec=config.get_multi_rl_module_spec(env=gym.make("Acrobot-v1")),
)
# Build the Learner.
ray.get(learner_actor.build.remote())
# Perform an update from the list of episodes we got from the `EnvRunners` above.
learner_results = ray.get(learner_actor.update.remote(
episodes=tree.flatten(episodes)
))
print(learner_results["default_policy"]["policy_loss"])