强化学习笔记:Gym入门--从安装到第一个完整的代码示例
Posted 笨牛慢耕
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了强化学习笔记:Gym入门--从安装到第一个完整的代码示例相关的知识,希望对你有一定的参考价值。
目录
3.7 env.sample_space.sample(): 对动作空间进行随机采样
0. 前言
Gym库(https://gym.openai.com) 是OpenAI推出的强化学习实验环境库。它用Python语言实现了离散之间智能体-环境接口中的环境部分。本文中“环境”一次均指强化学习基本框架模型之“智能体-环境”接口中的“环境”,每个环境就代表着一类强化学习问题,用户通过设计和训练自己的智能体来解决这些强化学习问题。所以,某种意义上,Gym也可以看作是一个强化学习习题集!
本文介绍gym入门所需要必要最小知识集合,并以一个完整的代码示例结束。
1. Gym安装
有两种Gym安装模式:(1) 最小安装; (2)完整安装
一般来说,先创建一个虚拟环境,然后在虚拟环境中进行安装比较好,但是本文就不聊虚拟环境了。
1.1 最小安装
执行以下命令就可以完成最小安装(conda install也可以吧。当然,通常在安装之前先upgrade pip和conda是一个良好的习惯)
更新pip版本(windows cmd terminal,当然其它类型的命令行终端也应该可以。第一条语句和第三条语句是用于查看更新前后的pip的版本):
pip --version
python -m pip install --upgrade pip
pip --version
更新conda版本:
conda update conda
用pip安装gym(注意,在Anaconda环境中也同样可以用pip进行安装):
pip install gym
最小安装的Gym库只包括少量的内置环境,如算法环境、简单文字游戏环境和经典控制环境。对于刚入坑强化学习的人来说这些就足够了。
1.2 完整安装
Gym库的一些内置的扩展库并不包括在最小安装中,比如说gym[atari]、gym[box2d]、gym[mujoco]、gym[robotics]等等。以gym[atari]为例,如果要安装最小环境加上atari环境、或者在已经安装了最小环境然后要追加atari安装时可以执行以下命令:
pip install --upgrade gym[atari]
也可以用以下命令进行gym完整安装:
pip install --upgrade gym[all]
据说,这样会把gym所需要的所有的依赖库都安装上。但是我在用这最后一条安装时出现报错,报告Box2d和Mujuco等安装错误,暂且不管,留待调查。如果不作完整安装,在运行过程中碰到报告依赖库欠缺再按图索骥逐个单独安装亦可。
1.3 gym版本确认
参见5.1节。
2. Gym中的环境
Gym库内置上百种实验环境,包括以下几类:
- 算法环境:包括一些字符串处理等传统计算机算法的实验环境
- 简单文本环境:包括几个用文本表示的简单游戏
- 经典控制环境:包括一些简单几何体的运动,常用于经典强化学习算法的研究
- Atari游戏环境:包括十数个Atari2600游戏,具有像素化的图形界面
- 二维方块(Box2D)环境:包括一些连续性任务环境
- MuJoCo环境:利用收费的MuJuCo运动引擎进行连续性控制任务
- 机械控制环境:关于机械臂的抓取和控制的任务
此外,在github上还能找到很多第三方的环境。
关于Gym的环境列表可以参见网址:Gym
也可以以以下方式查看。
2.1 查看gym所有可用环境
注意,实际返回的环境数取决于你的安装。如果是最小安装的话,应该是100多个。如果是完全安装的话会多得多。以下我的运行结果是849个,而且这个数字应该是随时间变化的。
import gym
from gym import envs
env_list = envs.registry.all()
env_ids = [env_item.id for env_item in env_list]
print('There are 0 envs in gym'.format(len(env_ids)))
env_ids
[2022-11-18]gym版本更新到0.26.2后,查询环境列表的命令要修改为:
env_list = envs.registry.keys()
env_ids = [env_item for env_item in env_list]
print('There are 0 envs in gym'.format(len(env_ids)))
env_ids
2.2 Gym环境命名规则
(1) 如果环境ID的“v”后面有数字,就代表着当前环境的版本。
(2) 环境ID有“ram”的时候,根据环境返回的“状态”是Atari游戏中使用的“ram”(Random Access Memory) “随机存取存储器”的内容。
(3) 当环境ID有“deterministic”时,agent传递给环境的动作会被重复执行4帧,然后返回“状态”。
(4) 环境ID有“NoFrameskip”的时候,agent传递给环境的动作被执行1帧,然后马上(没有跳过帧) 返回“状态”。
(5) 默认情况下,在环境ID中不包含“deterministic”、“NoFrameskip”的情况下,“动作”重复执行n帧。n从2,3,4均匀采样。
3. gym基本函数接口
使用gym环境,首先第一件事情就是通过调用gym.make()来创建环境对象。
然后,智能体通过调用env类的方法来与环境进行交互,常用的env类方法有:reset(), close(), render(), render(), step(),等等。以下分别介绍。
3.1 make(): 生成环境对象
函数接口如下所示:
'''
env = gym.make(id)
说明:生成环境
参数:Id(str类型) 环境ID
返回值:env(Env类型) 环境
环境ID是OpenAI Gym提供的环境的ID,可以通过上一节所述方式进行查看有哪些可用的环境
例如,如果是“CartPole”环境,则ID可以用“CartPole-v1”。返回“Env”对象作为返回值
'''
通过make()创建完环境对象后,可以查看环境的属性和当前状态,代码示例如下:
env = gym.make('CartPole-v1')
print('观测空间 = '.format(env.observation_space))
print('动作空间 = '.format(env.action_space))
print('动作数 = '.format(env.action_space.n))
print('初始状态 = '.format(env.state))
以上输出信息的含义这里就不一一介绍了,后续再详细介绍。
2.2 reset()函数
reset()为环境复位初始化函数。将环境的状态恢复到初始状态。
其返回值为初始状态,或者,严格地说是初始观测状态。这是因为,在非完全可观测环境中,观测状态与环境真实状态并不完全相同,前者通常是后者的一个子集,或者还可能是后者经过变换后所得的。不过gym中大部分的简单环境例都是完全可观测的,这种情况下,向reset()或者step()等返回的观测状态就环境的真实状态。
函数接口说明如下:
'''
state = env.reset()
说明:重置环境,回到初始状态
返回值:state(object类型) 环境的最初状态。类型由属性“observation_space”决定
'''
init_state = env.reset()
print('初始状态 = '.format(init_state))
print('初始状态 = '.format(env.state))
初始状态 = [-0.01415594 0.00287111 0.03600921 -0.01799398] 初始状态 = [-0.01415594 0.00287111 0.03600921 -0.01799397]
在一次任务的开始时必须调用reset(),哪怕是make()生成对象后的第一次任务。这是因为,make()生成对象后并没有顺便做一下初始化。在2.1的代码中查看env.state我们得到的是None。这里调用了reset()后才确定了初始状态。以上语句也表明,reset()所返回的init_state与调用reset后的env.state的确是相同的。
3.3 env.state¶
env.state用于查看环境当前状态。在上面的例子中我们已经使用过它了。需要注意的是,state是环境的一个propety,不是函数。所以不需要括号。
此外,这个状态与观测状态(比如说由reset()或者step()返回的就是观测状态)不一定相同,取决于环境是完全可观测的,还是部分可观测的。
3.4 env.step():单步执行
当然这里说的单步不是指软件程序调试中所指的运行一行代码之类的,而是指智能体与环境之间的一次交互,即智能体在当前状态s下执行一次动作a,环境相应地更新至状态s',并向智能体反馈及时奖励r。
函数接口说明如下:
'''
state, reward, done, info = env.step(action)
说明:环境执行一步动作
参数:action(object 类型) 动作
返回值:results(tuple 类型) (下一状态,报酬,episode 是否完成,日志信息)
将“动作”传递给环境,返回值返回“下一个状态”(object)、“报酬”(float)、“ episode 是否完成”(bool)、“日志信息”(dict)
传递给环境的“动作”类型,由属性“action_space”决定
'''
for k in range(5):
action = env.action_space.sample()
state, reward, done, info = env.step(action)
print('动作 = 0: 当前状态 = 1, 奖励 = 2, 结束标志 = 3, 日志信息 = 4'.format(action, state, reward, done, info))
3.5 env.render():环境显示
以图形化的方式显示环境当前状态,在智能体与环境的持续交互过程中,该图形化显示也是相应地持续更新的。
env.render()所生成的图形窗口是以另外的独立的窗口出现。
'''
env.render(mode='human')
说明:渲染环境画面
参数:mode(str类型) 渲染模式
返回值:对应渲染模式的返回值
'''
支持的“渲染模式”根据环境不同而不同。也有不支持渲染的环境。一般有以下几种渲染模式:
渲染模式参数 | 说明 |
---|---|
human | 在人类显示器或终端上渲染 |
rgb_array | 返回像素图像的RGB阵列作为返回值 |
ansi | 将文本作为返回值返回 |
3.6 env.close():关闭环境
关闭环境。
3.7 env.sample_space.sample(): 对动作空间进行随机采样
动作的选择应该基于策略进行。但是,完全随机的选择动作也是一种策略,或者可以说是一种基线(baseline)策略。任何一种能够体现有效的学习效果的策略都不应该比这种基线策略的效果差。这就好比任何一个有效的预测(股票涨跌、球赛胜负啊随便什么的)算法不能比随机扔硬币决定要更差。如果一种基于一种习得的策略来选取动作其最终得到的回报不比以上随机采样策略好的话,就说明这个习得的策略没有任何价值。
3.8 env.seed():指定随机种子
'''
env.seed(seed=None)
说明:指定随机数种子
参数:seed(int 类型) 随机种子
返回值:seeds(list 类型) 在环境中使用的随机数种子列表
用env.seed()指定环境的随机数种子。如果想要训练的再现性,或者想要根据不同的环境使用不同的随机数种子,就可以使用该方法
'''
3.9 一个随机策略的完整示例
import gym
import time
# 生成环境
env = gym.make('CartPole-v1')
# 环境初始化
state = env.reset()
# 循环交互
while True:
# 渲染画面
env.render()
# 从动作空间随机获取一个动作
action = env.action_space.sample()
# agent与环境进行一步交互
state, reward, done, info = env.step(action)
print('state = 0; reward = 1'.format(state, reward))
# 判断当前episode 是否完成
if done:
print('done')
break
time.sleep(1)
# 环境结束
env.close()
以上循环中故意插入了time.sleep()以便于以慢动作的方式放映整个过程。 没有这个慢动作控制,且打开了env.close()的话,你可能只会看到屏幕上一闪而过,啥也没看着。
注意,每次运行完一次任务,必须都使用env.close()关闭环境。否则的话,会出现因为内存没有被释放而导致的意外的问题。特别是强制关闭窗口的方式关闭env.render()所生成的环境显示窗口的话会导致问题。比如说,以上代码中如果屏蔽掉最后一行,然后反复执行这个cell的话,会不断生成多个窗口。然后这些窗口都只能强制关闭窗口的方式关闭。
4. 小结
本文介绍了gym入门的必要知识,有了这些知识,就可以开始愉快地玩玩gym了。
在2.9节还给出了一个完整的代码示例。但是大家可能会纳闷,强化学习的基本框架中包含agent(智能体)和Environment(环境),以上说的一大堆都是关于环境的,那智能体哪儿去了呢,没有智能体算什么完整的示例呢?
是的,以上代码示例中没有出现显式的智能体,但是并不是没有智能体!
智能体的任务在于学习策略以及基于策略进行动作选择,正如上面所说的,随机的动作选择也是一种策略!上面的示例中我们的确包含了一个从不学习,只基于随机采样的方式(就像我们日常生活中“遇事不决量子力学”式的抛硬币来做决策)进行动作选择最naive的agent。你完全可以把“action = env.action_space.sample()”这条语句看作式agent的实现!
真正的有效的agent需要玩家自己去实现,这个我们将在下一篇介绍。
5. 2022-10-10补充
针对评论区小伙伴们关于新版本的渲染的问题,更新了一下版本并做了一下实验,实验结果记录如下,供参考。
5.1 gym版本
2022-10-10安装的最新版本为gym 0.26.2,可以用以下两种方式确认gym版本:
在python脚本中运行:
print(gym.__version__)
或者,在命令行终端执行命令:
pip show gym
5.2 env.step()
如评论区“虎年喵飞飞”所指出的,新版本的env_step()的返回值由4个变为5个了(原来的done修改扩展为terminated,truncated,详细参见Core - Gym Documentation (gymlibrary.dev)),比如说:
state, reward, terminated,truncated, info = env.step(action)
gym.Env.step(self, action: ActType) → Tuple[ObsType, float, bool, bool, dict]
terminated (bool) – whether a terminal state (as defined under the MDP of the task) is reached. In this case further step() calls could return undefined results.
truncated (bool) – whether a truncation condition outside the scope of the MDP is satisfied. Typically a timelimit, but could also be used to indicate agent physically going out of bounds. Can be used to end the episode prematurely before a terminal state is reached.
5.3 env.render()
env.render()函数的调用方式成了个量子态。。。
调用env.render()的话直接被忽视(原版本render()的缺省参数是‘human’),新版本的缺省参数似乎变成了‘null’--no rendering。但是显式地调用env.render(mode='human'),会报告以下错误:
TypeError: render() got an unexpected keyword argument 'mode'
上穷碧落下黄泉地搜索也没有找到新版本中render函数的正确调用方式。
但是总算找到了一个能够正确地进行rendering的方式,在调用make创建env时指定render_mode参数,然后,不需要再调用env.render()。这样,以上3.9节的代码修改如下后可以正常工作:
import gym
import time
# 生成环境
env = gym.make('CartPole-v1',render_mode='human')
# 环境初始化
state = env.reset()
# 循环交互
while True:
# 渲染画面
# env.render()
# 从动作空间随机获取一个动作
action = env.action_space.sample()
# agent与环境进行一步交互
state, reward, terminated, truncated, info = env.step(action)
print('state = 0; reward = 1'.format(state, reward))
# 判断当前episode 是否完成
if terminated:
print('terminated')
break
time.sleep(0.1)
# 环境结束
#env.close()
5.4 env.seed()
由“Karl_Wayne”提示。
seed()函数在新版本已经被删除了。
While Env.seed
was a helpful function, this was almost solely used for the beginning of the episode and is added to gym.reset(seed=...)
. In addition, for several environments like Atari that utilise external random number generators, it was not possible to set the seed at any time other than reset
. Therefore, seed
is no longer expected to function within gym environments and is removed from all gym environments。
在新版本中,seed应该在reset()函数调用时指定。
有关新版本的后向兼容性事项可以参考:
Release notes for v0.26.0 by pseudo-rnd-thoughts
本系列总目录:强化学习笔记总目录https://chenxiaoyuan.blog.csdn.net/article/details/121715424
参考文献
[1] 肖智清,强化学习:原理与Python实现
[2] 第二节 OpenAI Gym接口概要_我不是变态的博客-CSDN博客_gym接口
以上是关于强化学习笔记:Gym入门--从安装到第一个完整的代码示例的主要内容,如果未能解决你的问题,请参考以下文章