基本用法¶
Gymnasium 是一个提供单智能体强化学习环境 API(应用程序编程接口)的项目,包含常见环境的实现:cartpole、pendulum、mountain-car、mujoco、atari 等。本页将概述如何使用 Gymnasium 的基础知识,包括其四个关键函数::meth:make
、:meth:Env.reset
、:meth:Env.step
和 :meth:Env.render
。
Gymnasium 的核心是 :class:Env
,这是一个代表强化学习理论中马尔可夫决策过程(MDP)的高级 Python 类(注意:这不是一个完美的重建,缺少 MDP 的几个组成部分)。该类为用户提供了生成初始状态、根据给定动作转换/移动到新状态以及可视化环境的能力。除了 :class:Env
,还提供了 :class:Wrapper
来帮助增强/修改环境,特别是代理的观察、奖励和采取的动作。
初始化环境¶
在 Gymnasium 中初始化环境非常简单,可以通过 :meth:make
函数完成:
import gymnasium as gym
env = gym.make('CartPole-v1')
此函数将返回一个供用户交互的 :class:Env
。要查看所有可以创建的环境,请使用 :meth:pprint_registry
。此外,:meth:make
提供了许多额外的参数,用于指定环境的关键字,添加或多或少的包装器等。更多信息请参见 :meth:make
。
与环境交互¶
在强化学习中,下图描绘的经典“智能体-环境循环”是智能体和环境如何相互作用的简化表示。智能体接收到关于环境的观察,然后选择一个动作,环境使用该动作来确定奖励和下一个观察。这个循环不断重复,直到环境结束(终止)。
对于 gymnasium,下面的代码实现了“代理-环境循环”,用于单个回合(直到环境结束)。有关逐行解释,请参见下一节。请注意,运行此代码需要安装 swig(pip install swig
或 下载)以及 pip install gymnasium[box2d]
。
import gymnasium as gym
env = gym.make("LunarLander-v3", render_mode="human")
observation, info = env.reset()
episode_over = False
while not episode_over:
action = env.action_space.sample() # agent policy that uses the observation and info
observation, reward, terminated, truncated, info = env.step(action)
episode_over = terminated or truncated
env.close()
输出应该看起来像这样:
解释代码¶
首先,使用 :meth:make
创建一个环境,并添加一个关键字 "render_mode"
来指定环境的可视化方式。有关不同渲染模式的默认含义的详细信息,请参阅 :meth:Env.render
。在这个例子中,我们使用 "LunarLander"
环境,其中智能体控制一艘需要安全着陆的宇宙飞船。
在初始化环境后,我们使用 :meth:Env.reset
方法重置环境以获取环境的第一个观察结果以及附加信息。要使用特定的随机种子或选项初始化环境(参见环境文档以获取可能的值),请在 :meth:reset
方法中使用 seed
或 options
参数。
由于我们希望继续代理-环境循环,直到环境结束,这在一个未知的时间步数内,我们定义 episode_over
作为一个变量,以知道何时停止与环境的交互,同时使用一个 while 循环来使用它。
接下来,代理在环境中执行一个动作,:meth:Env.step
执行所选的动作(在这种情况下是随机的,使用 env.action_space.sample()
)来更新环境。这个动作可以想象为移动一个机器人或在游戏控制器上按下一个按钮,这会导致环境中的变化。作为结果,代理从更新后的环境中接收到一个新的观察结果,以及采取该动作的奖励。这个奖励可能是例如摧毁敌人时的正面奖励,或是移动到岩浆中的负面奖励。这样的一个动作-观察交换被称为一个 时间步。
然而,经过一些时间步后,环境可能会结束,这被称为终止状态。例如,机器人可能已经发生碰撞,或者可能已经成功完成任务,环境需要停止,因为代理无法继续。在 gymnasium 中,如果环境已经终止,这会通过 :meth:step
作为第三个变量返回,即 terminated
。同样,我们也可能希望环境在固定数量的时间步后结束,在这种情况下,环境会发出一个截断信号。如果 terminated
或 truncated
中的任何一个为 True
,那么我们就结束这一集,但在大多数情况下,用户可能希望重新启动环境,这可以通过 env.reset()
来实现。
动作和观察空间¶
每个环境通过 :attr:action_space
和 :attr:observation_space
属性指定有效动作和观察的格式。这对于了解环境的预期输入和输出非常有帮助,因为所有有效的动作和观察都应该包含在它们各自的空间中。在上面的例子中,我们通过 env.action_space.sample()
采样随机动作,而不是使用代理策略,将观察映射到动作,这是用户希望实现的。
重要的是,:attr:Env.action_space
和 :attr:Env.observation_space
是 :class:Space
的实例,这是一个提供关键函数 :meth:Space.contains
和 :meth:Space.sample
的高级 Python 类。Gymnasium 支持用户可能需要的多种空间:
:class:
Box
: 描述具有任意n维形状的上限和下限的有界空间。:class:
Discrete
: 描述了一个离散空间,其中{0, 1, ..., n-1}
是我们观测或动作可以取的可能值。:class:
MultiBinary
: 描述任意n维形状的二进制空间。:class:
MultiDiscrete
: 由一系列 :class:Discrete
动作空间组成,每个元素中的动作数量不同。:class:
Text
: 描述了一个具有最小和最大长度的字符串空间:class:
Dict
: 描述了一个更简单空间的字典。:class:
Tuple
: 描述了一个简单空间的元组。:class:
Graph
: 描述了一个具有互联节点和边的数学图(网络):class:
Sequence
: 描述了更简单空间元素的变长序列。
例如,关于空间的用法,请参阅它们的 文档 <../api/spaces>
_ 以及 实用函数 <../api/spaces/utils>
_。还有一些更专业的空间 :class:图
、:class:序列
和 :class:文本
。
修改环境¶
包装器是一种在不直接修改底层代码的情况下修改现有环境的便捷方式。使用包装器可以避免大量样板代码,并使您的环境更具模块化。包装器还可以链接在一起以组合它们的效果。大多数通过 :meth:gymnasium.make
生成的环境默认情况下已经使用 :class:TimeLimit
、:class:OrderEnforcing
和 :class:PassiveEnvChecker
进行了包装。
为了包装一个环境,您必须首先初始化一个基础环境。然后,您可以将此环境与(可能的)可选参数一起传递给包装器的构造函数:
>>> import gymnasium as gym
>>> from gymnasium.wrappers import FlattenObservation
>>> env = gym.make("CarRacing-v2")
>>> env.observation_space.shape
(96, 96, 3)
>>> wrapped_env = FlattenObservation(env)
>>> wrapped_env.observation_space.shape
(27648,)
Gymnasium 已经为你提供了许多常用的包装器。一些例子:
:class:
TimeLimit
: 如果超过了最大时间步数(或基础环境发出了截断信号),则发出截断信号。:class:
ClipAction
: 裁剪传递给step
的任何动作,使其位于基础环境的动作空间内。:class:
RescaleAction
: 对动作应用仿射变换,以线性方式缩放到环境的新低和高边界。:class:
TimeAwareObservation
:在观察中添加时间步长索引信息。在某些情况下有助于确保转换是马尔可夫的。
要查看 gymnasium 中已实现包装器的完整列表,请参见 wrappers。
如果你有一个被包装的环境,并且你想获取所有包装层之下的未包装环境(以便你可以手动调用一个函数或改变环境的某些底层方面),你可以使用 :attr:unwrapped
属性。如果环境已经是一个基础环境,:attr:unwrapped
属性将直接返回它本身。
>>> wrapped_env
<FlattenObservation<TimeLimit<OrderEnforcing<PassiveEnvChecker<CarRacing<CarRacing-v2>>>>>>
>>> wrapped_env.unwrapped
<gymnasium.envs.box2d.car_racing.CarRacing object at 0x7f04efcb8850>