强化学习(Reinforcement Learning)来源于行为心理学,表示生物为了趋利避害而更频繁实施对自己有利的策略。
在人工智能领域中,强化学习是一类特定的机器学习问题。
在一个强化系统中,决策者可以观察环境,并根据观测作出行动。在行动之后,能够获得奖励。
强化学习通过与环境的交互来学习如何最大化奖励。
强化学习系统的两个关键要素:
强化学习与监督、非监督学习存在本质区别
基于强化学习的人工智能已经有许多成功的应用。例如:
智能体 / 环境接口的核心思想在于分隔主观可以控制的部分和客观不能改变的部分。
注意:强化学习问题不一定要借助智能体 / 环境接口来研究。
智能体 / 环境接口中,智能体与环境的交互主要有三个环节:
绝大多数的强化学习问题是按时间顺序或因果顺序发生的,其特点是具有先后顺序,并且先前的状态和动作会影响后续的状态等。这样的问题,我们引入时间指标 t t t ,记 t t t 时刻的状态为 S t S_t St ,观测为 O t O_t Ot ,动作为 A t A_t At ,奖励为 R t R_t Rt 。
强化学习可以按任务、算法进行分类。
预备知识
概率论、微积分、深度学习、Python
Markov决策过程
Gym库
推荐Richard Sutton等在2018年出版的《Reinforcement Learning: An Introduction (第2版)》
Gym库(https://gym.openai.com/)是OpenAI推出的强化学习实验环境库。它用Python语言实现了离散时间智能体 / 环境接口中的环境部分。
Gym库内置上百种实验环境,包括以下几类。
安装Gym前升级Python和pip:
pip install --upgrade pip
最小化安装Gym:
pip install gym
首先,导入Gym库。
import gym
在导入Gym库后,可以通过make()
函数来得到环境对象。每个环境都有一个ID,它是形如“Xxxxx-vd”的Python字符串,如’CartPole-v0’、'Taxi-v2’等。环境最后的部分表示版本号,不同版本的环境可能有不同的行为。使用取出环境’CartPole-v0’的代码为:
env = gym.make('CartPole-v0')
想要查看当前Gym库已经注册了哪些环境,可以使用一下代码:
from gym import envs
env_specs = envs.registry.all()
env_ids = [env_spec.id for env_spec in env_specs]
env_ids
每个环境都定义了自己的观测空间和动作空间。
env
的观测空间用env.observation_space
表示,动作空间用env.action_space
表示。gym.spaces.Dscrete
类表示,连续空间用gym.spaces.Box
类表示。接下来使用环境对象env
。首先,初始化环境对象env
,代码为:
env.reset()
该调用能返回智能体的初始观测,是np.array
对象。
环境初始化后就可以使用了。使用环境的核心是环境对象的step()
方法。step()
方法接收智能体的动作作为参数,并返回以下4个参数。
np.array
对象,表示观测,和env.reset()
返回值的意义相同。float
类型的值。bool
类型的数值。Gym库里的实验环境大多都是回合制的。dict
类型的值,含有调试信息。不一定要使用这个参数。env.step()
的参数需要取自动作空间。可以使用一下语句从动作空间中随机选取一个动作:
action = env.action_space.sample()
每次调用env.step()
只会让环境前进一步。所以,env.step()
往往放在循环结构里,通过循环调用完成整个回合。
在env.reset()
或env.step()
后,可以图形化的方法显示当前环境。
env.render()
使用完环境后,关闭环境:
env.close()
注意:如果绘制了实验的图形界面窗口,那么关闭该窗口的最佳方式是调用env.close()
。试图直接关闭图形界面窗口可能会导致内存不能释放,甚至死机。
本节选用经典控制任务:小车上山(MountainCar-v0)。这里,主要关注的是Python代码,而非任务本身的描述与求解。
1. 导入环境并查看观测空间和动作空间
import gym
env = gym.make('MountainCar-v0')
print('观测空间 = {}'.format(env.observation_space))
print('动作空间 = {}'.format(env.action_space))
print('观测范围 = {} ~ {}'.format(env.observation_space.low, env.observation_space.high))
print('动作数 = {}'.format(env.action_space.n))
运行结果为
观测空间 = Box(-1.2000000476837158, 0.6000000238418579, (2,), float32)
动作空间 = Discrete(3)
观测空间 = [-1.2 -0.07] ~ [0.6 0.07]
动作数 = 3
2. 根据指定确定性策略决定动作的智能体
下段代码中,decide()
方法实现了决策功能,learn()
方法实现了学习功能。但BespokeAgent
类比较简单,只是根据给定的数学表达式进行决策,不能有效地学习。不过,演示智能体和环境的交互已经足够了。
class BespokeAgent:
def __init__(slef, env):
pass
def decide(slef, observation): # 决策
position, velocity = observation
lb = min(-0.09 * ( position + 0.25 ) ** 2 + 0.03 , 0.3* ( position + 0.9 ) ** 4 - 0.008)
ub = -0.07 * ( position + 0.38 ) ** 2 + 0.06
if lb < velocity < ub:
action = 2
else:
action = 0
return action # 返回动作
def learn(self, *args): # 学习
pass
agent = BespokeAgent(env)
3. 智能体和环境交互一个回合的代码
env
是环境类。agent
是智能体类。render
是bool
变量,指示在运行过程中是否图形化显示。train
是bool
变量,指示在运行过程中是否训练智能体。episode_reward
表示智能体与环境交互一个回合的回合总奖励。def play_montecarlo(env, agent, render=False, train=False):
episode_reward = 0. # 记录回合总奖励,初始化为0
observation = env.reset() # 重置游戏环境,开始新回合
while True: # 不断循环,直到回合结束
if render: # 判断是否显示
env.render() # 显示图形界面
action = agent.decide(observation)
next_observation, reward, done, _ = env.step(action) # 执行动作
episode_reward += reward # 收集回合奖励
if train: # 判断是否训练智能体
agent.learn(observation, action, reward, done) # 学习
if done: # 回合结束,跳出循环
break
observation = next_observation
return episode_reward # 返回回合总奖励
env.seed(0) # 设置随机数种子,是为了让结果可以精确复现,一般可以删去
episode_reward = play_montecarlo(env, agent, render=True)
print('回合奖励 = {}'.format(episode_reward))
env.close() # 此语句可以关闭图形界面
4. 运行100回合求平均以测试性能
小车上山环境有一个参考的回合奖励值-110,如果当连续100个回合的平均值大于-110,则认为这个任务得到解决。
episode_reward = [play_montercarlo(env, agent) for _ in range(100)]
print('平均回合奖励 = {}'.format(np.mean(episode_rewards)))