gym(OpenAI Gym)和gymnasium是两个不同的Python库,它们都旨在为强化学习研究提供环境和工具
gym出现的原因:不同于监督学习那样需要的是数据集,强化学习需要的是运行任务所需的环境,研究人员需要拥有标准化的环境和模块化的强化学习代码,方便复用以及方便研究人员能够在相同的环境和条件下测试算法
gym通过提供一个统一的接口,
gym(OpenAI Gym)是由OpenAI团队开发的,是最早和最广泛使用的强化学习环境库之一
用于开发和比较强化学习算法的工具包和测试平台,提供了一个统一的接口来控制和交互各种环境
截止2023年,Gym 已经不再更新或维护,最新版本为v0.26.2
Gym的最新版本为v0.26.2,并且从这个版本开始,Gym的维护工作由Farama Foundation接手,并推出了Gymnasium
暂时先略过,日后补上,先介绍gymnasium封装自定义环境
pip install gymnasium
tree /F C:\path\to\directory
gymnasium.Env
“render_modes”: 这个键的值是一个列表,指明了环境支持的渲染模式。在这个例子中,环境支持两种渲染模式:
“human”: 这种模式通常是指在屏幕上以图形界面的形式渲染环境,适合人类观察者观看。
“rgb_array”: 这种模式下,环境的渲染结果会以 RGB 数组的形式返回,这可以用于机器学习算法的输入,或者进行进一步的处理和分析。
“render_fps”: 这个键表示环境渲染的帧率,即每秒钟可以渲染的帧数。在这个例子中,4 表示环境将以每秒 4 帧的速率进行渲染。这通常用于控制渲染速度,使动画的播放更加平滑或符合特定的显示需
__init__(),reset().setp(),render(),close()
等方法,确保环境能够按照强化学习的标准工作流程运行from gymnasium import spaces
spaces.Box
定义,low 和 high 参数指定了取值范围。spaces.Discrete
,参数指定可能的数量import numpy as np
import pygame
import gymnasium as gym
from gymnasium import spaces
class GridWorldEnv(gym.Env):
metadata = {"render_modes": ["human", "rgb_array"], "render_fps": 4}
def __init__(self, render_mode=None, size=5):
self.size = size # The size of the square grid
self.window_size = 512 # The size of the PyGame window
# Observations are dictionaries with the agent's and the target's location.
# Each location is encoded as an element of {0, ..., `size`}^2, i.e. MultiDiscrete([size, size]).
self.observation_space = spaces.Dict(
{
"agent": spaces.Box(0, size - 1, shape=(2,), dtype=int),
"target": spaces.Box(0, size - 1, shape=(2,), dtype=int),
}
)
# We have 4 actions, corresponding to "right", "up", "left", "down"
self.action_space = spaces.Discrete(4)
"""
The following dictionary maps abstract actions from `self.action_space` to
the direction we will walk in if that action is taken.
I.e. 0 corresponds to "right", 1 to "up" etc.
"""
self._action_to_direction = {
0: np.array([1, 0]),
1: np.array([0, 1]),
2: np.array([-1, 0]),
3: np.array([0, -1]),
}
assert render_mode is None or render_mode in self.metadata["render_modes"]
self.render_mode = render_mode
"""
If human-rendering is used, `self.window` will be a reference
to the window that we draw to. `self.clock` will be a clock that is used
to ensure that the environment is rendered at the correct framerate in
human-mode. They will remain `None` until human-mode is used for the
first time.
"""
self.window = None
self.clock = None
done
标志)时,会调用 reset 方法来重置环境状态reset
方法传递一个 seed
参数,用于初始化环境使用的任何随机数生成器,确保环境行为的确定性和可复现性def reset(self, seed=None, options=None):
# We need the following line to seed self.np_random
super().reset(seed=seed)
# Choose the agent's location uniformly at random
self._agent_location = self.np_random.integers(0, self.size, size=2, dtype=int)
# We will sample the target's location randomly until it does not coincide with the agent's location
self._target_location = self._agent_location
while np.array_equal(self._target_location, self._agent_location):
self._target_location = self.np_random.integers(
0, self.size, size=2, dtype=int
)
observation = self._get_obs()
info = self._get_info()
if self.render_mode == "human":
self._render_frame()
return observation, info
step()
方法是环境与智能体交互的核心,包含了环境逻辑的核心部分step()
方法处理动作,更新环境状态,并返回五个值组成的元组(observation, reward, terminated, truncated, info)
:观察(observation)、奖励(reward)、是否终止(terminated)、是否截断(truncated)和附加信息(info)五元组的含义(observation, reward, terminated, truncated, info)
观察(Observation):这是环境状态的表示,智能体根据这个观察来选择动作。观察可以是状态的一部分或全部,也可以是经过加工的信息,如图像、向量等。观察是智能体与环境交互的直接输入。
奖励(Reward):这是一个标量值,表示智能体执行动作后从环境中获得的即时反馈。奖励用于指导智能体学习哪些行为是好的,哪些是不好的。在许多任务中,智能体的目标是最大化其获得的总奖励。
是否终止(Terminated/Done):这是一个布尔值,表示当前周期(episode)是否结束。如果为 True,则表示智能体已经完成了任务,或者环境已经达到了一个终止状态,智能体需要重新开始新的周期。
是否截断(Truncated):这也是一个布尔值,与 done 相似,但表示周期结束的原因可能不是任务完成,而是其他原因,如超时、达到某个特定的中间状态或违反了某些规则。在某些实现中,truncated 可能与 done 相同或不被使用。
附加信息(Info):这是一个字典,包含除观察、奖励、终止和截断之外的额外信息。这些信息可以包括关于状态转换的元数据,如是否处于探索阶段、环境的内部计数器、额外的性能评估指标等。
def step(self, action):
# Map the action (element of {0,1,2,3}) to the direction we walk in
direction = self._action_to_direction[action]
# We use `np.clip` to make sure we don't leave the grid
self._agent_location = np.clip(
self._agent_location + direction, 0, self.size - 1
)
# An episode is done iff the agent has reached the target
terminated = np.array_equal(self._agent_location, self._target_location)
reward = 1 if terminated else 0 # Binary sparse rewards
observation = self._get_obs()
info = self._get_info()
if self.render_mode == "human":
self._render_frame()
return observation, reward, terminated, False, info
_get_info
方法获取,该方法用于收集和返回除了观察和奖励之外的其他有用信息。这些信息可以包括关于环境状态的额外数据_get_obs
方法负责将环境的内部状态转换为智能体可以观察的形式,通常涉及到从环境状态中提取相关信息,并将其格式化为智能体能够理解和使用的数据结构def _get_obs(self):
return {"agent": self._agent_location, "target": self._target_location}
def _get_info(self):
return {
"distance": np.linalg.norm(
self._agent_location - self._target_location, ord=1
)
}
渲染模式:
def render(self):
if self.render_mode == "rgb_array":
return self._render_frame()
def _render_frame(self):
if self.window is None and self.render_mode == "human":
pygame.init()
pygame.display.init()
self.window = pygame.display.set_mode(
(self.window_size, self.window_size)
)
if self.clock is None and self.render_mode == "human":
self.clock = pygame.time.Clock()
canvas = pygame.Surface((self.window_size, self.window_size))
canvas.fill((255, 255, 255))
pix_square_size = (
self.window_size / self.size
) # The size of a single grid square in pixels
# First we draw the target
pygame.draw.rect(
canvas,
(255, 0, 0),
pygame.Rect(
pix_square_size * self._target_location,
(pix_square_size, pix_square_size),
),
)
# Now we draw the agent
pygame.draw.circle(
canvas,
(0, 0, 255),
(self._agent_location + 0.5) * pix_square_size,
pix_square_size / 3,
)
# Finally, add some gridlines
for x in range(self.size + 1):
pygame.draw.line(
canvas,
0,
(0, pix_square_size * x),
(self.window_size, pix_square_size * x),
width=3,
)
pygame.draw.line(
canvas,
0,
(pix_square_size * x, 0),
(pix_square_size * x, self.window_size),
width=3,
)
if self.render_mode == "human":
# The following line copies our drawings from `canvas` to the visible window
self.window.blit(canvas, canvas.get_rect())
pygame.event.pump()
pygame.display.update()
# We need to ensure that human-rendering occurs at the predefined framerate.
# The following line will automatically add a delay to keep the framerate stable.
self.clock.tick(self.metadata["render_fps"])
else: # rgb_array
return np.transpose(
np.array(pygame.surfarray.pixels3d(canvas)), axes=(1, 0, 2)
)
def close(self):
if self.window is not None:
pygame.display.quit()
pygame.quit()
from gymnasium.envs.registration import register
register(
id="gym_examples/GridWorld-v0",
entry_point="gym_examples.envs:GridWorldEnv",
max_episode_steps=300,
)
gym_examples
(可选) ②强制名称GridWorld
③版本v0
(可选)module:classname
env = gymnasium.make('gym_examples/GridWorld-v0')
from gym_examples.envs.grid_world import GridWorldEnv
gym-examples/setup.py
中写入以下内容from setuptools import setup
setup(
name="gym_examples",
version="0.0.1",
install_requires=["gymnasium==0.26.0", "pygame==2.1.0"],
)
此处可以将"==“改为”>=",如果不打算做图形化可以删去pygame==2.1.0
安装自定义环境(在包含 setup.py
的目录中执行)
pip install -e .
import gym_examples
env = gymnasium.make('gym_examples/GridWorld-v0')
import gym_examples
env = gymnasium.make('gym_examples/GridWorld-v0', size=10)