1、前提
请先阅读:
https://huggingface.co/learn/deep-rl-course/unit1/introduction
2、环境类
首先,您需要创建一个继承自 gymnasium.Env
的环境类。这个类需要实现几个关键方法和属性:
必需方法
-
__init__(self, \*args, \**kwargs)
:-
用于初始化环境。可以在这里设置环境的初始状态、动作空间、观察空间等。
-
通常需要定义
self.observation_space
和self.action_space
。
-
-
step(self, action)
:-
接受一个动作作为输入,并返回一个四元组(observation, reward, done,truncated, info)
-
observation
: 采取动作后环境的新状态。 -
reward
: 采取动作后得到的奖励。 -
done
: 一个布尔值,表示是否达到了终止状态。 -
truncated: 一个布尔值,表示是否达到预设的终止次数。
-
info
: 一个字典,包含额外的诊断信息。
-
-
-
reset(self, \*args, \**kwargs)
:-
重置环境到初始状态,返回初始观测值。
-
可以接受可选参数用于初始化状态。
-
可选方法
-
render(self, mode='human')
:-
用于渲染环境的当前状态。可以根据
mode
参数的不同返回不同的渲染效果,如图像或文本。 -
mode
可以是'human'
,'rgb_array'
等。
-
-
close(self)
:-
清理资源,如关闭图形窗口等。
-
必需属性
-
action_space
:-
定义动作空间,通常是
gymnasium.spaces
中的一个实例,如Discrete
,Box
等。 -
例如:
self.action_space = gymnasium.spaces.Discrete(2)
表示动作空间有两个离散动作。
-
-
observation_space
:-
定义观察空间,通常是
gymnasium.spaces
中的一个实例,如Discrete
,Box
等。 -
例如:
self.observation_space = gymnasium.spaces.Box(low=0, high=1, shape=(3,), dtype=np.float32)
表示观察空间是一个包含三个浮点数的向量,值在0到1之间。
-
3、自定义强化学习环境
主要就是两个属性(动作空间action_spce, 观测空间observation_space),两个方法(reset,step),奖励规则(compute_reward)
import gymnasium as gym
from gymnasium import spaces
import numpy as np
from stable_baselines3 import PPO
class RLEnv(gym.Env):
def __init__(self):
super(RLEnv,self).__init__()
# 动作空间
self.action_space = spaces.Box(low=-1,high=1,shape=(5,),dtype=np.float32)
# 观测空间
self.observation_space = spaces.Box(low=np.inf,high=np.inf,shape=(7,),dtype=np.float32)
def reset(self, seed=None, options=None):
'''
重置环境。在开始一个新的回合时调用,返回初始状态和一个可选信息字典
'''
state =np.zeros(5)
return state, {}
def step(self, action):
'''
执行一步操作。在环境中执行给定的动作并返回新的状态、奖励、是否完成和额外信息
'''
action = np.zeros(5)
new_state = np.zeros(7)
done = True
return new_state, action, done, False, {}
def compute_reward(self, state):
pass
3.1 两个属性
动作空间(Action Space)
动作空间就是指一个智能体(比如机器人、自动驾驶汽车或者游戏中的角色)在每个时刻可以做的所有可能动作的集合。可以把它想象成一个菜单,菜单上列出了所有你可以做的选择。
-
离散动作空间:
-
举例:比如你在玩一个简单的游戏,角色每一步可以选择向上、向下、向左或者向右移动。这些方向就是离散动作空间中的动作。
-
特点:动作是有限的、可数的,就像菜单上的菜品数量有限。
import numpy as np from gym import Env, spaces class SimpleDiscreteEnv(Env): def __init__(self): super(SimpleDiscreteEnv, self).__init__() self.action_space = spaces.Discrete(4) # 四个动作:上下左右 self.observation_space = spaces.Discrete(16) # 16个状态 self.state = 0 # 初始状态 def reset(self): self.state = 0 return self.state,{} def step(self, action): reward = 0 done = False if action == 0: # 向上 self.state = max(0, self.state - 4) elif action == 1: # 向下 self.state = min(15, self.state + 4) elif action == 2: # 向左 self.state = max(0, self.state - 1) if self.state % 4 != 0 else self.state elif action == 3: # 向右 self.state = min(15, self.state + 1) if self.state % 4 != 3 else self.state if self.state == 15: # 终止状态 reward = 1 done = True return self.state, reward, done,False, {}
-
-
连续动作空间:
-
举例:比如你在驾驶一辆汽车,方向盘的角度和油门的力度是连续变化的。你可以将方向盘稍微转一点或多转一点,油门也可以踩轻一点或重一点,这些变化是连续的。
-
特点:动作是无限的,可以有很多细微的变化,就像菜单上有无数种不同辣度的菜。
import numpy as np from gym import Env, spaces class SimpleContinuousEnv(Env): def __init__(self): super(SimpleContinuousEnv, self).__init__() self.action_space = spaces.Box(low=-1.0, high=1.0, shape=(1,), dtype=np.float32) self.observation_space = spaces.Box(low=-10.0, high=10.0, shape=(1,), dtype=np.float32) self.state = np.array([0.0]) def reset(self): self.state = np.array([0.0]) return self.state,{} def step(self, action): self.state = self.state + action reward = -np.abs(self.state) # 奖励为接近0的负数 done = np.abs(self.state) >= 10.0 # 超过边界即终止 return self.state, reward, done,False, {}
-
-
混合动作空间:
-
举例:自动驾驶汽车的驾驶可以是混合动作空间的一种,比如换挡是离散的(只能挂1挡、2挡等),而方向盘的角度和油门的力度是连续的。
-
特点:同时包含有限的选择和无限的变化,就像菜单上有固定的主菜和各种搭配的调料。
import numpy as np from gym import Env, spaces class SimpleHybridEnv(Env): def __init__(self): super(SimpleHybridEnv, self).__init__() self.gear_space = spaces.Discrete(3) # 离散的换挡动作 self.acceleration_space = spaces.Box(low=-1.0, high=1.0, shape=(1,), dtype=np.float32) # 连续的加速动作 self.action_space = spaces.Tuple((self.gear_space, self.acceleration_space)) self.observation_space = spaces.Box(low=-10.0, high=10.0, shape=(2,), dtype=np.float32) self.state = np.array([0.0, 0.0]) def reset(self): self.state = np.array([0.0, 0.0]) return self.state,{} def step(self, action): gear, acceleration = action self.state[1] = gear # 简单模拟换挡影响速度 self.state[0] += self.state[1] * acceleration # 位置随速度和加速变化 reward = -np.abs(self.state[0]) # 奖励为接近0的负数 done = np.abs(self.state[0]) >= 10.0 # 超过边界即终止 return self.state, reward, done,False, {}
-
3.2 观察空间
观察空间就是指一个智能体在每个时刻能够看到或感知到的所有可能状态的集合。可以把它想象成智能体的“视野”或“感知范围”。
-
离散观察空间:
-
举例:玩一个简单的棋类游戏,棋盘上每个格子的状态是空的、有自己的棋子或有对方的棋子,这些状态就是离散的。
-
特点:状态是有限的、可数的,就像棋盘上的格子数量有限。
# 请参考离散的动作空间
-
-
连续观察空间:
-
举例:开车时,你的车速、位置、周围的障碍物距离等等,这些都是连续变化的。
-
特点:状态是无限的,可以有很多细微的变化,就像车速表上有无数个刻度。
# 请参考连续的动作空间
-
-
混合观察空间:
-
举例:机器人在工作时,可以同时感知到电池电量(离散的,满电或低电)和位置、速度等(连续的)。
-
特点:同时包含有限的感知和无限的变化,就像你可以看到固态的物体和变化的环境。
from gym import spaces import numpy as np class CustomRobotEnv: def __init__(self): # 离散观察:电池状态(0: 低电,1: 满电) self.battery_space = spaces.Discrete(2) # 连续观察:位置[-1, 1]和速度[-1, 1] self.position_speed_space = spaces.Box(low=-1.0, high=1.0, shape=(2,), dtype=np.float32) # 混合观察空间 self.observation_space = spaces.Tuple((self.battery_space, self.position_speed_space)) def reset(self): return (0, np.zeros(2)),{} def step(self, action): # 处理动作逻辑 # ... return (1, np.ones(2)), 0, False,False, {}
-
3.3 简单总结(动作空间 & 观测空间)
-
动作空间:智能体可以做什么。
-
离散:有限的、明确的选择(例如:上下左右)。
-
连续:无限的、细微的变化(例如:方向盘角度)。
-
混合:既有固定选择,又有细微变化(例如:自动驾驶)。
-
-
观察空间:智能体能看到什么。
-
离散:有限的、明确的状态(例如:棋盘状态)。
-
连续:无限的、细微的变化(例如:车速)。
-
混合:既有固定状态,又有细微变化(例如:机器人感知)。
-
3.4 两个方法
在强化学习(Reinforcement Learning, RL)中,reset
和 step
函数是环境(environment)接口的重要组成部分。这两个函数通常用于与环境进行交互,特别是在训练 RL 代理(agent)的过程中。
1.4.1 reset
函数
reset
函数用于初始化或重置环境。它通常在以下情况下调用:
-
开始新一轮的训练:在开始训练时,需要先调用
reset
函数将环境设置为初始状态。 -
一个episode结束时:当一个episode结束时(例如,代理达到目标或犯了错误),需要重置环境以开始新的episode。
reset
函数通常返回环境的初始状态(state),该状态将作为代理在新一轮训练中的起始点。
示例代码:
state, info = env.reset()
1.4.2 step
函数
step
函数用于在环境中执行一个动作(action)。它通常接受一个表示动作的参数,并返回一个元组(tuple),包含以下信息:
-
下一状态(next state):执行动作后,环境的新状态。
-
奖励(reward):执行动作后,代理收到的奖励。
-
是否结束(done):一个布尔值,表示episode是否结束。
-
是否达到了预设的最大执行次数truncated:一个布尔值,表示episode是否结束
-
其他信息(info):可能包含调试信息或诊断数据的字典。
step
函数的作用是让代理与环境互动,通过执行动作来推进环境的状态,并根据环境的反馈调整代理的策略。
示例代码:
next_state, reward, done,truncated ,info = env.step(action)
3.5 奖励的作用
引导智能体行为:奖励机制设计得当,可以引导智能体朝着目标行为发展。例如,在自动驾驶中,奖励可以设定为安全驾驶、节省燃油等。 优化策略:智能体通过不断调整策略以最大化累积奖励,从而找到最优策略。
4、 超级马里奥
在经典的电子游戏《超级玛丽奥》中,reset
和 step
函数的协作可以形象地展示强化学习环境中的交互过程。
reset
函数在《超级玛丽奥》中的作用
reset
函数的作用是将游戏环境重置到初始状态。对于《超级玛丽奥》来说,这意味着:
-
重置游戏关卡:将马里奥的位置重置到关卡的起点。
-
重置游戏状态:恢复马里奥的初始状态,例如生命值、得分等。
-
重置环境状态:重置敌人和障碍物的位置和状态。
调用 reset
函数后,游戏环境会返回一个初始状态,这个状态描述了马里奥以及游戏环境的初始情况。
示例代码:
state, info = env.reset()
这个 state
可能包含马里奥的初始位置、得分、生命值,以及屏幕上敌人和障碍物的位置等信息。
step
函数在《超级玛丽亚》中的作用
step
函数用于在游戏环境中执行一个动作。对于《超级玛丽亚》来说,动作可以是:
-
向左移动
-
向右移动
-
跳跃
-
下蹲
每个动作执行后,step
函数会更新游戏状态,并返回以下信息:
-
下一状态(next state):执行动作后,马里奥和游戏环境的新的状态。
-
奖励(reward):执行动作后获得的奖励,例如收集硬币或击败敌人。
-
是否结束(done):一个布尔值,表示游戏是否结束,例如马里奥掉落悬崖或完成关卡。
-
其他信息(info):可能包含额外的调试信息。
示例代码:
next_state, reward, done, truncated, info = env.step(action)
协调工作过程
1、初始化游戏:
state,info = env.reset()
2、游戏环境重置,马里奥位于起点,准备开始新的冒险。
游戏进行中:
while not done:
action = agent.choose_action(state) # 代理根据当前状态选择动作
next_state, reward, done, truncated, info = env.step(action) # 执行动作
agent.learn(state, action, reward, next_state) # 代理学习
state = next_state # 更新状态
在游戏过程中,代理(agent)不断选择动作并执行,通过 step
函数与环境互动。每次执行动作后,环境会返回新的状态和奖励,代理根据这些信息更新策略。
游戏结束: 当 done
变为 True
时,表示游戏结束。此时可以选择再次调用 reset
函数开始新的游戏:
state, info = env.reset() # 开始新的游戏
5、强化学习与环境的交互
在强化学习中,代理通过与环境的反复交互来学习如何在不同的状态下选择最佳动作,从而最大化累积奖励。这种交互过程可以描述如下:
1、初始化:通过调用 reset()
方法,环境返回一个初始状态。代理根据这个初始状态决定初始动作。
state, info = env.reset()
2、选择动作:代理根据当前状态选择一个动作。这一步通常是通过一个策略来实现,例如通过训练好的模型来选择动作。
action, _ = agent.predict(state)
3、执行动作:通过调用 step(action)
方法,环境根据代理的动作更新状态,计算奖励,并返回新状态、奖励、是否完成和其他信息。
new_state, reward, done, truncated, info = env.step(action)
4、学习:代理利用新获得的状态和奖励更新其策略,以便在未来的相似状态下做出更好的决策。这一步可能涉及到策略梯度、Q-learning 或其他强化学习算法。
agent.learn(state, action, reward, new_state, done)
5、重复:上述过程重复进行,直到回合结束 (done
为 True
)。然后代理可以通过再次调用 reset()
方法来开始新的回合。
总结:上述交互过程可以概括为一个循环:代理观察环境状态 -> 选择并执行动作 -> 接收反馈(新状态和奖励)-> 更新策略 -> 重复。通过这个过程,代理逐渐学习到在各种状态下选择最优动作的策略。
* 上面这个过程当中,1 & 3是环境执行的,2 & 4是代理执行的,代理实际上是你选择的算法模型
6、实践组合Gymnasium 和 stable-baselines3:
Gymnasium 和 stable-baselines3 是两个用于强化学习研究和应用的流行库,它们各自承担不同的工作并且相互协作,为开发者提供一个完整的强化学习框架。
Gymnasium
主要职责:
-
环境构建:Gymnasium 提供了各种标准化的强化学习环境,这些环境涵盖了不同类型的任务,例如控制、博弈、机器人学等。每个环境都遵循统一的接口,便于算法的开发和测试。
-
交互接口:Gymnasium 定义了一个标准接口,包括
reset()
、step(action)
、render()
等方法,方便用户与环境进行交互。 -
模拟和评估:通过模拟真实或虚拟的环境,Gymnasium 允许用户在不同的场景中测试和评估他们的强化学习算法。
stable-baselines3
主要职责:
-
算法实现:stable-baselines3 提供了多种流行的强化学习算法的实现,如 DQN、PPO、A2C、DDPG 等。这些算法经过优化和验证,可以直接用于训练智能体。
-
训练框架:该库提供了易于使用的接口和工具,用于配置和运行强化学习实验,管理超参数和训练过程。
-
模型保存和加载:提供了方便的模型保存和加载功能,使得用户可以在训练过程中保存中间结果,或者在训练结束后加载模型进行推理或进一步训练。
相互协作
Gymnasium 和 stable-baselines3 通过标准化接口协作,为用户提供了一个完整的强化学习解决方案:
-
环境初始化:用户首先通过 Gymnasium 创建一个强化学习环境,例如
env =
SimpleEnv()。 -
算法选择:然后,用户选择并初始化一个 stable-baselines3 提供的算法,例如
model = PPO('MlpPolicy', env, verbose=1)
。 -
训练过程:用户调用 stable-baselines3 的训练方法,例如
model.learn(total_timesteps=10000)
,算法会通过与 Gymnasium 环境的交互不断更新智能体的策略。 -
评估和测试:训练完成后,用户可以使用 Gymnasium 的环境进行测试和评估智能体的表现,例如
obs = env.reset()
和env.step(action)
。
通过这种协作方式,Gymnasium 提供了标准化的环境和接口,stable-baselines3 提供了优化的算法实现,两者结合起来,使得开发和测试强化学习算法变得更加简便和高效。
7、简易示例
import gymnasium as gym
from gymnasium import spaces
import numpy as np
class SimpleEnv(gym.Env):
def __init__(self):
super(SimpleEnv, self).__init__()
self.action_space = spaces.Discrete(2) # 两个动作:0 和 1
self.observation_space = spaces.Box(low=0, high=1, shape=(1,), dtype=np.float32) # 单一状态,取值范围为0到1
self.state = 0
def reset(self):
self.state = np.random.rand()
return np.array([self.state]),{}
def step(self, action):
reward = 1 if action == 1 else 0
done = True # 每一步都结束
self.state = np.random.rand()
return np.array([self.state]), reward, done,False, {}
def render(self, mode='human'):
pass
def close(self):
pass
if __name__ == '__main__':
from stable_baselines3 import PPO
from stable_baselines3.common.env_checker import check_env
# 创建自定义环境
env = SimpleEnv()
# 检查环境是否符合标准
check_env(env, warn=True)
# 创建PPO模型
model = PPO('MlpPolicy', env, verbose=1)
# 训练模型
model.learn(total_timesteps=10000)
# 保存模型
model.save("ppo_simple_env")
# 加载模型
model = PPO.load("ppo_simple_env")
# 测试模型
obs = env.reset()
for _ in range(10):
action, _states = model.predict(obs)
obs, rewards, dones, truncated, info = env.step(action)
print(f"Action: {action}, Reward: {rewards}")
* 在自定义环境中,奖励规则的设计极其重要,奖励规则不合理一切都白搭