如何用gym创建强化学习环境 - 持续更新中

在强化学习中环境(environment)是与agent进行交互的重要部分,虽然OpenAI gym中有提供多种的环境,但是有时我们需要自己创建训练用的环境。这个文章主要用于介绍和记录我作为一个初学者对如何用gym创建训练环境的学习过程。其中会包含如何使用OpenAI gym提供的Function和Class以及创建环境的格式。

OpenAI gym Functions and Class

  1. spaces class
    gym 中 spaces 可用于创建action space和observation space,其中常用的有spaces.Box(), spaces.Discrete(), spaces.Dict()
  • Box() 可用于创建连续的空间

例子:

from gym import spaces

# 规定action space最小值,最大值以及维度
action_space1 = spaces.Box(low=-1, high=1, shape=(1, ))
# 也可以输入向量,这样space输出的值会与输入向量具有相同维度,不需要另外规定维度(low和high要具有相同维度)
low = np.float32(np.zeros(3))
high = np.float32(np.ones(3))
action_space2 = spaces.Box(low=low,high=high)

# 随机进行采样,观察输出结果
print(action_space1.sample())
print(action_space2.sample())
输出结果:
[0.17719302]
[0.09906013 0.6137293  0.5404117 ]

注意: 如果输入的上下限为np.float64,那么会出现warning:UserWarning: WARN: Box bound precision lowered by casting to float32,因此可以将输入改为np.float32

  • Discrete() 可用于创建离散的非负整数空间

例子:

# 规定action space中总共有几个动作
# 例如在有名的例子“cartpole”中小车只有两个action选项:向左或向右,因此将数值设为2,则输出会为0或1
action_space = spaces.Discrete(2)

# 测试输出结果
for i in range(5):
	print(action_space.sample())
输出结果:
0
1
1
1
1
  • **Dict()**可用字典(dictionary)的形式储存空间特征,因此可以描述更多特性并用于构建更为复杂的空间

例子:

low = np.float32(np.zeros(3))
high = np.float32(np.ones(3))

# 创建有两个不同action的action space
action_space = spaces.Dict({
            "action1": spaces.Discrete(10),
            "action2": spaces.Box(low=low,high=high)
        })
print(action_space.sample())
输出结果:
OrderedDict([('action1', 6), ('action2', array([0.23961447, 0.6493422 , 0.267231  ], dtype=float32))])
可以看到action space中有 action1 和 action2 两个action

创建环境所需格式

import gym
from gym.utils import seeding

# gym.Env是主要的OpenAI Gym class,其中含有多个API,若要构建自己的环境,需要重写这些API
# Main API: step, reset, render, close, seed,每个部分的作用会写在下面
class MyEnv(gym.Env):
	# 根据需要自己定义环境的名字
	def __init__(self):
		# 这个部分用于定义MyEnv的各种属性,包括action space, observation space, gravity(cartpole)
		...
		self.action_space = spaces.Discrete(2)
		self.observation_space = spaces.Box(low=np.float32(np.zeros(3)),high=np.float32(np.ones(3)))
	
	def reset(self):
		# 重置环境,用于新的episode,需要返回observation (initial state)
		...
		obs = self.get_obs()
		
		return obs
	
	def render(self):
		# 如果要进行可视化,可以实现
	
	def seed(self, seed=None):
		# 设置此环境随机数生成器seed
        self.np_random, seed = seeding.np_random(seed)
        return [seed]

	def step(self):
		# 描述环境根据agent的动作发生什么样的改变,即环境对agents' action的反馈
		# 需要返回observation, reward (one step), 
		# done (True/False), training information (用于记录训练过程中的环境信息)
		...
		obs = self.get_obs()
		reward = self.get_reward()
		done = self.get_done()
		info = {}

		return obs, reward, done, info
	
	# 根据训练需要自定义函数
	def get_obs(self):
		...
		return obs
	
	def get_reward(self):
		...
		return reward
	
	def get_done(self):
		...
		return done 

创建环境步骤

  1. 确定环境的组成 - 含有什么components,这些components是否会在训练过程中产生变化和造成影响
    例如:在wireless networked control system中,系统组成可能包括:plants, sensor, channel (wireless network), controller, estimator, kalman filter,等等
  2. 如何用参数+数学公式将这些components描述出来
    例如:plant的表达式 - x t + 1 = A x t + B u t + w t x_{t+1}=Ax_t+Bu_t+w_t xt+1=Axt+But+wt
  3. 确定这些components之间的数学关系
  4. 根据2,3中的数学关系设定环境的属性(如:转换矩阵,状态维度等等),写在__init__(self)部分,并确定哪些属性需要初始化(如observation),写在reset(self)部分
  5. 将以上的数学关系用代码编写出来,描述每次环境收到反馈后发生的变化,写在step(self)部分

Tips

这部分用于记录我在看别人构建的环境中学到的小tips

  • 如果环境中agent由多个对象组成,且每个对象都有其单独的转换矩阵,可以用scipy.linalg.block_diag()将多个矩阵合并为一个单独的对角矩阵,方便在step()中进行计算。我原本的方法是将每个对象保存的list中,list[i]对应第i个对象,这样容易造成step()中的代码过于冗长且要话费更多的计算时间,因为每次要调用多个对象时都需要写一个循环。
    scipy.linalg.block_diag()介绍:https://docs.scipy.org/doc/scipy/reference/generated/scipy.linalg.block_diag.html
  • 环境的observation如果需要的是一维向量,但是在step()中为了方便理解用的多维数组,可以使用obs.flatten()进行转换。
    numpy.ndarray.flatten介绍:https://numpy.org/doc/stable/reference/generated/numpy.ndarray.flatten.html

你可能感兴趣的:(强化学习,Python,强化学习,openal,人工智能,python)