当前位置:首页|资讯|OpenAI|编程

OpenAI Gym学习(二):创建自定义环境

作者:LSC2049发布时间:2023-08-07

本文档概述了创建新环境以及Gymnasium中为创建新环境而设计的相关wrapper、实用程序和测试。你可以克隆Gym的例子来使用这里提供的代码。

子类化 gymnasium.Env

在Gym示例中可以发现环境大概长这样:

为了说明Gym Env的子类化过程,我们将实现一个非常简单的游戏,名为GridWorldEnv。我们将在gym-examples/gym_examples/envs/grid_world.py中为我们的自定义环境编写代码。该环境由一个固定大小的二维方形网格组成(在构建过程中通过大小参数指定)。agent可以在每个时间步长的网格单元之间垂直或水平移动。agent的目标是导航到网格上在剧集开始时随机放置的目标。

  • Observation:观测提供了目标和代理人的位置。

  • Action:在我们的环境中有4个动作,分别对应“右”、“上”、“左”和“下”。

  • Done:一旦agent导航到目标所在的网格单元,就会发出完成信号。

  • Reward:奖励是二进制和稀疏的,这意味着即时奖励始终为零,除非代理达到目标,否则为1。

在这种环境中(大小=5)的一集可能如下所示:

缺图

其中蓝色圆点表示代理,红色方块表示目标。

让我们逐一查看GridWorldEnv的源代码:

声明和初始化

我们的自定义环境将继承抽象类gymnasium.Env。您不应该忘记将meta元数据属性添加到类中。在那里,您应该指定环境支持的render_mode(例如“human”、“rgb_array”、“ansi”)以及渲染环境的帧速率。每个环境都应该支持None作为渲染模式;您不需要将其添加到元数据中。在GridWorldEnv中,我们将支持“rgb_array”和“human”模式,并以4 FPS进行渲染。

我们环境的__init__方法将接受整数大小,该大小决定了方形网格的大小。我们将设置一些用于渲染的变量,并定义self.observation_space和self.action_space。在我们的情况下,观测应提供有关agent和target在二维网格上的位置的信息。我们将选择用关键字“agent”和“target”的字典形式来表示观察结果。观察结果可能类似于{“agent”:array([1,0]),“target”:array([0,3])}。由于我们的环境中有4个动作(“right”、“up”、“left”、“down”),我们将使用Discrete(4)作为动作空间。以下是GridWorldEnv的声明和__init__的实现:

从环境状态构建观测

由于我们需要在reset和step中计算观测值,因此使用(私有)方法_get_obs将环境状态转换为观测值通常很方便。然而,这不是强制性的,您也可以分别在重置和步骤中计算观测值:

对于step和reset返回的辅助信息,我们也可以实现类似的方法。在我们的情况下,我们希望提供agent和target之间的曼哈顿距离:

通常,info还会包含一些仅在步骤方法中可用的数据(例如,个人奖励条款)。在这种情况下,我们必须更新_get_info在步骤中返回的字典。

Reset

调用reset方法来启动新的轮次。您可以假设在调用reset之前不会调用step方法。此外,无论何时发出完成信号,都应调用重置。用户可以传递seed关键字以重置,从而将环境使用的任何随机数生成器初始化为确定性状态。建议使用环境的基类gymnasium.Env提供的随机数生成器self.np_random。如果你只使用这个RNG,你不需要太担心种子,但你需要记住调用“super().reset(seed=seed)”,以确保gymnasium.Env正确地喂给RNG。一旦完成,我们就可以随机设置环境的状态。在我们的情况下,我们随机选择代理的位置和随机样本目标位置,直到它与代理的位置不一致。

reset方法应该返回初始观测值的元组和一些辅助信息。我们可以使用前面实现的方法_get_obs和_get_info:

Step

step方法通常包含环境的大部分逻辑。它接受一个action,在应用该action后计算环境的状态,并返回5元组(observation、reward 、terminated、truncated、info)。请参阅gymnium.Env.step()。一旦计算出环境的新状态,我们就可以检查它是否是一个终止状态,并相应地设置完成。由于我们在GridWorldEnv中使用稀疏二进制奖励(sparse binary rewards),一旦我们知道完成了计算奖励就很简单了。为了收集观察结果和信息,我们可以再次使用_get_obs和_get_info:

Rendering

在这里,我们使用PyGame进行渲染。Gymnasium中包含的许多环境都使用类似的渲染方法,您可以将其用作自己环境的骨架:

Close

close方法应关闭环境使用的任何打开的资源。在许多情况下,您实际上不必费力地实现此方法。但是,在我们的示例中,render_mode可能是“human”,我们可能需要关闭已打开的窗口:

在其他环境中,关闭可能还会关闭已打开的文件或释放其他资源。在调用关闭后,您不应该与环境交互。

Registering Envs

为了让Gymnasium检测到自定义环境,必须按照以下方式进行注册。我们将选择将此代码放在gym-examples/gym_examples/__init__.py中。

环境ID由三个组件组成,其中两个组件是可选的:可选名称空间(此处:gym_examples)、强制名称(此处:GridWorld)和可选但推荐的版本(此处:v0)。它可能还被注册为GridWorld-v0(推荐的方法)、GridWorld或gym_examples/GridWorld,然后在创建环境时应该使用适当的ID。

关键字参数max_epimode_steps=300将确保通过gymnasium.make实例化的GridWorld环境将被封装在TimeLimit包装器中(有关更多信息,请参阅包装器文档)。如果代理已经到达目标或者在当前事件中已经执行了300个步骤,则将产生完成信号。要区分截断和终止,您可以检查信息[“TimeLimit.cotruncated”]。

除了id和入口点,您还可以传递以下额外的关键字参数进行注册:

这些关键字中的大多数(除了max_epimode_steps、order_enforce和kwargs)不会改变环境实例的行为,而只是提供一些关于环境的额外信息。注册后,我们的自定义GridWorldEnv环境可以用env=gymn.make('gym_examples/GridWorld-v0')创建。

gym-examples/gym_examples/envs/__init__.py应该具有:

如果您的环境没有注册,您可以选择传递一个模块来导入,该模块将在创建环境之前注册环境,如下所示-env=gymnom.make('module:env-v0'),其中module包含注册代码。对于GridWorld env,注册代码是通过导入gym_examples来运行的,因此,如果无法显式导入gym_samples,则可以在制作时通过env=gym.make('gym_examples:gym_examples/GridWorld-v0)进行注册。当只允许将环境ID传递给第三方代码库(例如学习库)时,这一点尤其有用。这使您可以注册环境,而无需编辑库的源代码。

Creating a Package

最后一步是将我们的代码构造为Python包。这涉及到配置gym examples/setup.py。如何做到这一点的最简单示例如下:

Creating Environment Instances

使用pip install -e gym-examples在本地安装软件包后,您可以通过以下方式创建环境实例:

您还可以将环境构造函数的关键字参数传递给gymnasium.make以自定义环境。在我们的案例中,我们可以做到:

有时,您可能会发现跳过注册并自己调用环境的构造函数更方便。有些人可能会发现这种方法更Python,像这样实例化的环境也非常好(但也要记住添加包装器!)。

Using Wrappers

通常,我们想要使用自定义环境的不同变体,或者我们想要修改Gym或其他方提供的环境的行为。包装器允许我们在不更改环境实现或添加任何样板代码的情况下实现这一点。有关如何使用包装器的详细信息以及实现自己的包装器的说明,请查看包装器文档。在我们的例子中,观察不能直接用于学习代码,因为它们是字典。然而,我们实际上不需要接触我们的环境实现来解决这个问题!我们可以简单地在环境实例的顶部添加一个包装器,将观察结果扁平化为单个数组:

包装器的最大优点是使环境高度模块化。例如,您可能只想查看目标和代理的相对位置,而不是将GridWorld中的观察结果扁平化。在关于ObservationWrappers的部分中,我们实现了一个完成这项工作的包装器。这种包装也可用于gym-examples:



Copyright © 2024 aigcdaily.cn  北京智识时代科技有限公司  版权所有  京ICP备2023006237号-1