本文首先介绍Gym的核心函数调用链,然后介绍如何创建自定义的Gym环境,最后给出一些使用Gym过程中碰到的问题及其解决方案。
Gym核心函数调用链
一般来说,使用Gym的代码如下:
|
|
可见,关键的函数有:
env = gym.make('CartPole-v0')
env.reset()
env.step(a)
我们先关注env.reset()
和env.step(a)
。这两个函数是超类Env
的成员函数,Env
的相关代码如下:
|
|
可以看到这两个函数依赖于子类的_reset(self)
和_step(self, action)
实现,子类CartPoleEnv
的相关代码如下:
|
|
综上,env.reset()
和env.step(a)
实际上是调用子类的_reset(self)
和_step(self, action)
。
下面我们关注gym.make('CartPole-v0')
,它的实现如下:
|
|
可以看到gym.make
依赖于类EnvRegistry
的成员函数make
,EnvRegistry
的相关代码如下:
|
|
可见类EnvRegistry
的成员函数make
依赖于类EnvSpec
的成员函数make
,EnvSpec
的相关代码如下:
|
|
至此,我们对Gym的核心函数调用链有了一个基本的了解:
gym.make(id)
:通过EnvRegistry
中的注册表找到对应的EnvSpec
,EnvSpec
根据entry_point
动态import
对应的Env
,并将其实例化;env.reset()
和env.step(a)
:子类的_reset(self)
和_step(self, action)
。
创建自定义环境
对Gym的核心函数调用链有了基本了解后,我们知道创建自定义环境的关键有两个:
- 第一个是搭建自己的
Env
子类FooEnv
; - 第二个是注册
FooEnv
(i.e., 将FooEnv
添加到registry.env_specs
中),使得gym.make(id)
可以找到FooEnv
。
官方文档推荐的自定义环境目录结构如下:
|
|
实现FooEnv
没什么特别的,就是根据自己的需求,实现_step(self, action)
、_reset(self)
等函数。
值得一提的是注册FooEnv
,我们无需自己实现注册环境的代码,因为Gym已经有现成的注册环境API,我们只需要调用该API即可。在我们的自定义环境中,负责注册FooEnv的文件为gym-foo/gym_foo/__init__.py
,它的内容如下:
|
|
可见,注册的关键是register
函数,而register
函数的实现如下:
|
|
可以看到register
的实现依赖于类EnvRegistry
的成员函数register
,其相关代码如下:
|
|
综上,我们可以通过API函数register
注册自定义的环境FooEnv
。
注意事项
server render
假如你通过ssh连接server,在server上运行(i.e., python main.py
)以下代码(关键点在使用env.render()
保存录像):
那么你会得到一个报错,报错的信息大概是pyglet.canvas.xlib.NoSuchDisplayException: Cannot connect to "None"
。
原因大概是env.render()
需要图形界面(就是弹出来的那个框框),而当你使用ssh连接server时是没有图形界面的。因此我们需要一个虚拟的图形界面,而xvfb-run
就是一个提供虚拟图形界面的工具。
所以我们需要使用xvfb-run -a -s "-screen 0 1400x900x24 +extension RANDR" -- python main.py
来运行我们的代码。
一般来说,运行上述指令是会报错的,报错的信息大概是pyglet requires an X server with GLX
,主要原因在于显卡驱动以及cuda的安装有问题,没有加--no-opengl
的flag。解决方案可以参考这里和这里
保存每一段episode的录像
wrappers.Monitor
默认不会保存所有episode的录像,但我们可以通过以下代码来设置保存所有episode的录像:
|
|
动态修改episode的最大step
env._max_episode_steps = xxx
。注意,这仅当env的类型为TimeLimit
时可用。
关于wrapper
- 相同的两个wrapper不能叠加(e.g.,
Monitor
不可以和Monitor
叠加,但是Monitor
可以和TimeLimit
叠加),否则会报double wrapper的错。 - 在注册
FooEnv
时,加不加max_episode_steps=xxx
会影响返回的Env
的类型。假如加了,返回的是TimeLimit
类型的wrapper;假如不加,返回的就是裸的FooEnv
。 Monitor
里面有两个recorder
,一个是stat_recorder
,用于保存数据(reward之类的);另一个是video_recorder
,用于录像。Monitor
会在每一次调用env.reset
和env.step
之后调用render
屏蔽log信息
|
|