TensorFlow深度学习实战(31)——强化学习仿真库Gymnasium
TensorFlow深度学习实战(31)——强化学习仿真库Gymnasium
- 0. 前言
- 1. 强化学习仿真环境
- 2. Gymnasium
- 2.1 Gymnasium 简介
- 2.2 Gymnasium 环境
- 2.3 Gymnasium 接口
- 3. 构建随机智能体进行 Breakout 游戏
- 4. Gymnasium 中的包装器
- 系列链接
0. 前言
Gymnasium
是一个用于开发和测试强化学习 (Reinforcement Learning, RL) 算法的开源工具包,旨在提供一个标准化的平台,让研究人员和开发者可以在各种不同的环境中训练和评估强化学习算法。主要用于提供易于使用的接口、丰富的环境,并使得强化学习的研究和开发更加高效。
1. 强化学习仿真环境
试错是强化学习 (Reinforcement Learning, RL) 算法中的重要组成部分,因此,首先需要在仿真环境中训练 RL
智能体,然后才能在实际环境中进行部署。常用的仿真环境包括:
Gymnasium
:包含了用来训练RL
智能体的环境集合Unity ML-Agents SDK
:允许开发者将使用Unity
编辑器创建的游戏和仿真转换为可以用深度强化学习、进化策略或其他机器学习方法训练智能代理的环境。与TensorFlow
兼容,并提供了训练2D/3D
和VR/AR
游戏智能体的能力Gazebo
:可以在Gazebo
中创建基于物理模拟的三维世界。gym-gazebo
工具包结合了Gazebo
、机器人操作系统 (Robot Operating System
,ROS
) 和Gymnasium
接口,用于训练RL
智能体Blender
学习环境:Blender
游戏引擎的Python
接口,也与Gymnasium
兼容。Blender
是一个免费的3D
建模软件,含有集成的游戏引擎,提供了一套易于使用且功能强大的工具用于创建游戏。可以通过Blender
游戏引擎创建自定义虚拟环境,用于训练RL
智能体以解决特定问题Malmo
:由微软团队构建,Malmo
是一个建立在Minecraft
之上的AI
实验和研究平台,提供了一个简单的API
来创建任务
2. Gymnasium
2.1 Gymnasium 简介
Gymnasium
是一个用于强化学习 (Reinforcement Learning
, RL
) 的开源库,它继承并扩展了 OpenAI
的 Gym
库,提供了标准化的 API
和多种 RL
环境,用于开发和比较 RL
算法。它包含了各种模拟环境,可以用于训练智能体和开发新的 RL
算法。现在,Gymnasium
由 Farama Foundation
维护,Gymnasium
针对各种 RL
任务提供了统一的接口和广泛的环境支持。Gymnasium
保持了与 OpenAI Gym
的兼容性,可以将代码轻松地从 Gym
迁移到 Gymnasium
。
Gymnasium
库的安装和其它第三方库相同,通过在命令行中执行 pip
命令:
$ pip install gymnasium
如果想安装所有(免费的) Gymnasium
模块,可以在其后添加 [all]
:
$ pip install gymnasium[all]
对于基于 Atari
的游戏,需要安装 Atari
依赖项 (Box2D
和 ROM
):
$ pip install box2d-py
2.2 Gymnasium 环境
Gymnasium
提供了多种环境,从简单的基于文本的环境到三维游戏,可以分为以下几类:
- 算法:包含相关执行计算(如加法)的环境
Atari
:提供各种经典的Atari
游戏环境Box2D
:包含二维的机器人任务,如赛车竞速或双足机器人- 经典控制:包含经典控制问题,如平衡杆问题
MuJoCo
:一个授权环境(可以获得一个月的免费试用),支持各种机器人仿真任务,该环境包含物理引擎,因此可以用于训练机器人任务- 机器人:使用
MuJoCo
的物理引擎,模拟基于目标的任务 - 简单文本:简单的基于文本的环境
可以使用以下代码查看安装的所有可用环境:
from gymnasium import envs
envall = envs.registry.keys()
print(len(envall))
通过 make
函数可以创建环境,每个环境都有一个唯一的 ID
、动作空间、动作空间和默认的奖励范围。遍历 envall
列表中的所有环境,并记录其唯一 ID
,该 ID
用于通过 make
方法创建环境,以及其状态空间、奖励范围和动作空间:
from tqdm import tqdm
import pandas as pd
List = []
for e in tqdm(envall):try:env = gym.make(e)List.append([e.id, env.observation_space, env.action_space, env.reward_range])env.close()except:continue
df = pd.DataFrame(List, columns= ['Environment', 'Observation Space', 'Action Space', 'Reward'])
df.sample(20)
可以通过以下方式获取 Gymnasium
中任意环境的详细信息,例如,打印 MountainCar
环境的详细信息:
env = gym.make('MountainCar-v0')
print(f"The Observation space is {env.observation_space}" )
print(f"Upper Bound for Env Observation {env.observation_space.high}")
print(f"Lower Bound for Env Observation {env.observation_space.low}")
print(f"Action Space {env.action_space}")obs = env.reset()
print(f"The initial observation is {obs}")
# Take a random actionget the new observation space
obs, reward, done, truncated, info = env.step(env.action_space.sample())
print(f"The new observation is {obs}")
env.close()
2.3 Gymnasium 接口
Gymnasium
的核心是统一的环境接口。智能体可以通过三个基本方法与环境进行交互,即 reset()
、step()
和 render()
。reset()
方法重置环境并返回初始环境状态,step()
方法执行给定动作,并返回新的环境状态new_obs
、奖励 reward
、回合完成标志 done
、回合(超时或人为限制)截断标志 truncated
和信息 info
,render()
方法用于渲染环境帧。接下来,查看一些不同的环境并观察它们的初始环境状态:
# Physics Engine
e = 'LunarLander-v2'
env = gym.make(e, render_mode='rgb_array')
obs = env.reset()
img = env.render()
env.close()
plt.imshow(img)
plt.show()# Classic Control
e = 'CartPole-v0'
env = gym.make(e, render_mode='rgb_array')
obs = env.reset()
img = env.render()
env.close()
plt.imshow(img)
plt.show()# Atari
e = 'ALE/SpaceInvaders-v5'
env = gym.make(e, render_mode='rgb_array')
obs = env.reset()
img = env.render()
env.close()
plt.imshow(img)
plt.show()
除了使用 Matplotlib
显示环境外,也可以直接使用 render()
方法:
env = gym.make('ALE/Breakout-v5', render_mode='human')
obs, info = env.reset()
done = False
while not done:action = env.action_space.sample() obs, reward, done, truncated, info = env.step(action)env.render()
在下图中可以看到 Breakout
环境,render()
函数会弹出环境图像窗口:
可以使用 env.observation_space
和 env.action_space
来了解关于 Breakout
游戏的状态空间和动作空间的信息。可以看到,状态由一个大小为 210 × 160
的三通道图像组成,动作空间是离散的,有四个可能的动作,完成所有动作后,使用 close()
方法关闭环境:
env.close()
3. 构建随机智能体进行 Breakout 游戏
接下来,我们进行 Breakout
游戏。当我们第一次玩这个游戏时,通常不知道规则或如何操作,所以会随机选择控制按钮,而对于智能体同样如此,在刚开始训练时,它会从动作空间中随机选择动作。Gymnasium
提供了 sample()
函数用于从动作空间中随机选择一个动作。此外,我们还可以保存游戏回放,以便用于后续查看,有两种保存游戏回放的方法,一种是使用 Matplotlib
,另一种是使用 Gymnasium Monitor
包装器。在本小节中,使用 Matplotlib
方法保存游戏回放。
(1) 首先导入所需模块,本节只需要 gymnasium
和 matplotlib
,因为智能体采用随机动作:
import gymnasium as gym
import matplotlib.pyplot as plt
import matplotlib.animation as animation
(2) 创建 Gymnasium
环境:
env_name = 'ALE/Breakout-v5'
env = gym.make(env_name, render_mode='rgb_array')
(3) 接下来,运行游戏,每个时间步选择随机动作,最多运行 300
步,或直到游戏结束。在每一步中,都将环境状态保存在列表 frames
中:
env.reset()
done = False
for _ in range(300): #print(done)frames.append(env.render())obs, reward, done, truncated, info = env.step(env.action_space.sample())if done:break
(4) 使用 Matplotlib Animation
将所有帧合成为一个 GIF
图像。首先创建一个图像对象 patch
,然后定义函数 animate()
,将图像数据设置为特定的帧索引,Matplotlib Animation
类使用该函数创建动画,最后将其保存为文件 random_agent.gif
:
patch = plt.imshow(frames[0])
plt.axis('off')
plt.tick_params(axis='both', left='off', top='off', right='off', bottom='off', labelleft='off', labeltop='off', labelright='off', labelbottom='off')
def animate(i):patch.set_data(frames[i])
anim = animation.FuncAnimation(plt.gcf(), animate, \frames=len(frames), interval=10)
anim.save('./images/drone_random.gif', writer='pillow') ##Comment on colab
生成 GIF
图像结果如下:
介绍了 Gymnasium
后,我们将研究包装器,用于来创建自定义环境。
4. Gymnasium 中的包装器
Gymnasium
提供了多种包装器 (Wrapper
),用于修改现有环境。例如,如果神经网络的输入是图像,RGB
强度值在 0
到 255
之间,而神经网络的最佳输入范围是 [0, 1]
,这种情况下就可以使用 Gymnasium
包装器类对状态进行预处理。
接下来,定义一个将状态串联起来的包装器:
from collections import deque
import gymnasium as gym
from gymnasium import spaces
import numpy as npclass ConcatObservations(gym.Wrapper):def __init__(self, env, n):gym.Wrapper.__init__(self, env)shape = env.observation_space.shapeself.n = nself.frames = deque([], maxlen=n)self.observation_space = \spaces.Box(low=0, high=255, shape=((n,) + shape), dtype=env.observation_space.dtype)def reset(self):obs = self.env.reset()for _ in range(self.n):self.frames.append(obs)return self._get_obs()def step(self, action):obs, reward, done, truncated, info = self.env.step(action)self.frames.append(obs)return self._get_obs(), reward, done, infodef _get_obs(self):return np.array(self.frames)
可以看到,需要更改默认的 reset()
函数、step()
函数和 _get_obs()
函数,还需要修改默认的返回状态:
env = gym.make("BreakoutNoFrameskip-v4")
print(f"The original observation space is {env.observation_space}")
# The original observation space is Box(0, 255, (210, 160, 3), uint8)
如果使用 BreakoutNoFrameskip-v4
环境,那么原始的状态尺寸为 210 x 160 x 3
:
env = ConcatObservations(env, 4)
print(f"The new observation space is {env.observation_space}")
# The new observation space is Box(0, 255, (4, 210, 160, 3), uint8)
而如果使用了刚刚创建的包装器,会发现状态增加了一个维度,其包含四个帧,每个帧的大小是 210 x 160 x 3
。
也可以使用包装器来修改奖励。在这种情况下,可以使用超类 RewardWrapper
,在以下代码中,将奖励限制在 [-10, 10]
范围内:
class ClippedRewards(gym.RewardWrapper):def __init__(self, env):gym.RewardWrapper.__init__(self, env)self.reward_range = (-10,10)def reward(self, reward):"""Clip to {+10, 0, -10} by its sign."""return reward if reward >= -10 and reward <= 10 else 10 * np.sign(reward)
在 CartPole
环境中使用 ClippedRewards
,该环境的奖励范围是 [−∞, ∞]
:
env = gym.make("CartPole-v0")
print("Original Rewards:", env.reward_range)
env.close()
env = ClippedRewards(gym.make("CartPole-v0"))
print(f'Clipped reward range: {env.reward_range}')
env.close()
# Clipped reward range: (-10, 10)
包装器的另一个有用应用是在智能体学习时保存状态。通常,一个 RL
智能体需要大量的步骤来进行适当的训练,因此在每一步存储状态并不实际,我们可以选择在每隔指定步(例如 50
或 500
步)后进行存储。Gymnasium
提供了 Wrapper Monitor
类来将游戏保存为视频。首先导入包装器,然后创建环境,最后使用 Monitor
。
默认情况下,会存储 1
、8
、27
、64
等步数时的视频,然后是每 1,000
步的视频;每次训练,默认情况下,视频保存在同一个文件夹中:
import gymnasium as gym
env = gym.make("ALE/Breakout-v5", render_mode='rgb_array')
env = gym.wrappers.RecordVideo(env, 'recording', episode_trigger=lambda x: True)
observation = env.reset()
for _ in range(1000):#env.render()action = env.action_space.sample() # your agent here (this takes random actions)observation, reward, done, truncated, info = env.step(action)if done:observation = env.reset()
env.close()
为了使 Monitor
正常工作,需要 FFmpeg
工具支持。视频结果将以 .mp4
格式保存在录制文件夹中。需要注意的是,如果想在下一个训练会话中使用相同的文件夹,必须设置 force=True
选项。
系列链接
TensorFlow深度学习实战(1)——神经网络与模型训练过程详解
TensorFlow深度学习实战(2)——使用TensorFlow构建神经网络
TensorFlow深度学习实战(3)——深度学习中常用激活函数详解
TensorFlow深度学习实战(4)——正则化技术详解
TensorFlow深度学习实战(5)——神经网络性能优化技术详解
TensorFlow深度学习实战(6)——回归分析详解
TensorFlow深度学习实战(7)——分类任务详解
TensorFlow深度学习实战(8)——卷积神经网络
TensorFlow深度学习实战(9)——构建VGG模型实现图像分类
TensorFlow深度学习实战(10)——迁移学习详解
TensorFlow深度学习实战(11)——风格迁移详解
TensorFlow深度学习实战(12)——词嵌入技术详解
TensorFlow深度学习实战(13)——神经嵌入详解
TensorFlow深度学习实战(14)——循环神经网络详解
TensorFlow深度学习实战(15)——编码器-解码器架构
TensorFlow深度学习实战(16)——注意力机制详解
TensorFlow深度学习实战(17)——主成分分析详解
TensorFlow深度学习实战(18)——K-means 聚类详解
TensorFlow深度学习实战(19)——受限玻尔兹曼机
TensorFlow深度学习实战(20)——自组织映射详解
TensorFlow深度学习实战(21)——Transformer架构详解与实现
TensorFlow深度学习实战(22)——从零开始实现Transformer机器翻译
TensorFlow深度学习实战(23)——自编码器详解与实现
TensorFlow深度学习实战(24)——卷积自编码器详解与实现
TensorFlow深度学习实战(25)——变分自编码器详解与实现
TensorFlow深度学习实战(26)——生成对抗网络详解与实现
TensorFlow深度学习实战(27)——CycleGAN详解与实现
TensorFlow深度学习实战(28)——扩散模型(Diffusion Model)
TensorFlow深度学习实战(29)——自监督学习(Self-Supervised Learning)
TensorFlow深度学习实战(30)——强化学习(Reinforcement learning,RL)