OpenAI Gym 提供了许多虚拟环境来训练您的强化学习智能体。 在强化学习中,最困难的任务是创造环境。 OpenAI Gym 通过提供许多玩具游戏环境为用户提供了一个平台,以训练和确定他们的强化学习智能体,从而为救援提供了帮助。
换句话说,它为强化学习智能体提供了一个学习和基准化他们的表现的场所,其中智能体必须学会从开始状态导航到目标状态,而不会遭受任何不幸。
因此,在本章中,我们将学习从 OpenAI Gym 了解和使用环境,并尝试实现基本的 Q 学习和 Q 网络,供我们的智能体学习。
OpenAI Gym 提供不同类型的环境。 它们如下:
- 经典控制
- 算法化
- Atari
- 棋盘游戏
- Box2D
- 参数调整
- MuJoCo
- 玩具文本
- Safety
- Minecraft
- PyGame 学习环境
- 足球
- Doom
有关这些广泛的环境类别及其环境游乐场的详细信息,请访问这个页面。
我们将在本章介绍以下主题:
- OpenAI Gym 环境
- 使用 OpenAI Gym 环境对智能体进行编程
- 将 Q 网络用于实际应用
为了下载并安装 OpenAI Gym,您可以使用以下任一选项:
$ git clone https://github.com/openai/gym
$ cd gym
$ sudo pip install -e . # minimal install
这将进行最小安装。 您以后可以运行以下命令进行完整安装:
$ sudo pip install -e .[all]
您还可以按以下说明将 Gym 作为不同 Python 版本的包来获取:
对于 Python 2.7,可以使用以下选项:
$ sudo pip install gym # minimal install
$ sudo pip install gym[all] # full install
$ sudo pip install gym[atari] # for Atari specific environment installation
对于 Python 3.5,可以使用以下选项:
$ sudo pip3 install gym # minimal install
$ sudo pip3 install gym[all] # full install
$ sudo pip install gym[atari] # for Atari specific environment installation
为了了解导入 Gym 包,加载环境以及与 OpenAI Gym 相关的其他重要功能的基础,这里是冰冻湖环境的示例。
通过以下方式加载冰冻湖环境:
import Gym
env = Gym.make('FrozenLake-v0') #make function of Gym loads the specified environment
接下来,我们来重置环境。 在执行强化学习任务时,智能体会经历多个剧集的学习。 结果,在每个剧集开始时,都需要重置环境,使其恢复到初始状态,并且智能体从开始状态开始。 以下代码显示了重置环境的过程:
import Gym
env = Gym.make('FrozenLake-v0')
s = env.reset() # resets the environment and returns the start state as a value
print(s)
-----------
0 #initial state is 0
在执行每个操作之后,可能需要显示智能体在环境中的状态。 通过以下方式可视化该状态:
env.render()
------------
SFFF
FHFH
FFFH
HFFG
前面的输出显示这是一个4 x 4
网格的环境,即以前面的方式排列的 16 个状态,其中S
,H
,F
和G
表示状态的不同形式,其中:
S
:开始块F
:冻结块H
:块有孔G
:目标块
在较新版本的“体育馆”中,不能直接修改环境特征。 这可以通过以下方式解开环境参数来完成:
env = env.unwrapped
每个环境都由状态空间和供智能体执行的操作空间定义。 为了构建增强学习智能体,了解状态空间和动作空间的类型(离散或连续)和大小非常重要:
print(env.action_space)
print(env.action_space.n)
----------------
Discrete(4)
4
Discrete(4)
输出表示冰冻湖环境的动作空间是一组离散值,并且具有可由智能体执行的四个不同的动作。
print(env.observation_space)
print(env.observation_space.n)
----------------
Discrete(16)
16
Discrete(16)
输出表示冻结湖环境的观察(状态)空间是一组离散值,并具有 16 种要由智能体探索的不同状态。
本节考虑的环境是 FrozenLake-v0。 有关环境的实际文档可以在这个页面中找到。
此环境由代表一个湖泊的4 x 4
网格组成。 因此,我们有 16 个网格块,其中每个块可以是开始块(S
),冻结块(F
),目标块(G
)或孔块(H
)。 因此,智能体程序的目标是学会从头到尾进行导航而不会陷入困境:
import Gym
env = Gym.make('FrozenLake-v0') #loads the environment FrozenLake-v0
env.render() # will output the environment and position of the agent
-------------------
SFFF
FHFH
FFFH
HFFG
在任何给定状态下,智能体都有四个要执行的动作,分别是上,下,左和右。 除了导致目标状态的那一步外,每一步的奖励都是 0,那么奖励将是 1。我们从S
状态开始,我们的目标是达到G
状态而不以最优化的路径通过F
状态降落到H
状态。
现在,让我们尝试使用 Q 学习对增强学习智能体进行编程。 Q 学习由一个 Q 表组成,该表包含每个状态动作对的 Q 值。 表中的行数等于环境中的状态数,而列数等于操作数。 由于状态数为16
,而动作数为4
,因此此环境的 Q 表由16
行和4
列组成。 它的代码在这里给出:
print("Number of actions : ",env.action_space.n)
print("Number of states : ",env.observation_space.n)
----------------------
Number of actions : 4
Number of states : 16
Q 学习涉及的步骤如下:
-
用零初始化 Q 表(最终,更新将随着学习过程中所采取的每个操作收到的奖励而发生)。
-
更新一个状态-动作对的 Q 值,即
Q(s, a)
的方法是:
在此公式中:
- 通过按照步骤 2 中提到的公式更新 Q 值,该表收敛以获取给定状态下某个动作的准确值。
ε 贪婪是探索-利用困境的一种广泛使用的解决方案。 探索就是通过实验和研究来搜索和探索新的选择以产生新的值,而探索则是通过重复这些选择并提高其值来完善现有的选择。
ε 贪婪方法非常易于理解且易于实现:
epsilon() = 0.05 or 0.1 #any small value between 0 to 1
#epsilon() is the probability of exploration
p = random number between 0 and 1
if p epsilon() :
pull a random action
else:
pull current best action
最终,经过几次迭代,我们发现了每个状态中所有状态中的最佳操作,因为它可以选择探索新的随机操作以及利用现有操作并对其进行优化。
让我们尝试实现一种基本的 Q 学习算法,以使智能体学习如何在从头到尾的 16 个网格的冰冻湖面中导航,而不会陷入困境:
# importing dependency libraries
from __future__ import print_function
import Gym
import numpy as np
import time
#Load the environment
env = Gym.make('FrozenLake-v0')
s = env.reset()
print("initial state : ",s)
print()
env.render()
print()
print(env.action_space) #number of actions
print(env.observation_space) #number of states
print()
print("Number of actions : ",env.action_space.n)
print("Number of states : ",env.observation_space.n)
print()
#Epsilon-Greedy approach for Exploration and Exploitation of the state-action spaces
def epsilon_greedy(Q,s,na):
epsilon = 0.3
p = np.random.uniform(low=0,high=1)
#print(p)
if p > epsilon:
return np.argmax(Q[s,:])#say here,initial policy = for each state consider the action having highest Q-value
else:
return env.action_space.sample()
# Q-Learning Implementation
#Initializing Q-table with zeros
Q = np.zeros([env.observation_space.n,env.action_space.n])
#set hyperparameters
lr = 0.5 #learning rate
y = 0.9 #discount factor lambda
eps = 100000 #total episodes being 100000
for i in range(eps):
s = env.reset()
t = False
while(True):
a = epsilon_greedy(Q,s,env.action_space.n)
s_,r,t,_ = env.step(a)
if (r==0):
if t==True:
r = -5 #to give negative rewards when holes turn up
Q[s_] = np.ones(env.action_space.n)*r #in terminal state Q value equals the reward
else:
r = -1 #to give negative rewards to avoid long routes
if (r==1):
r = 100
Q[s_] = np.ones(env.action_space.n)*r #in terminal state Q value equals the reward
Q[s,a] = Q[s,a] + lr * (r + y*np.max(Q[s_,a]) - Q[s,a])
s = s_
if (t == True) :
break
print("Q-table")
print(Q)
print()
print("Output after learning")
print()
#learning ends with the end of the above loop of several episodes above
#let's check how much our agent has learned
s = env.reset()
env.render()
while(True):
a = np.argmax(Q[s])
s_,r,t,_ = env.step(a)
print("===============")
env.render()
s = s_
if(t==True) :
break
----------------------------------------------------------------------------------------------
<<OUTPUT>>
initial state : 0
SFFF
FHFH
FFFH
HFFG
Discrete(4)
Discrete(16)
Number of actions : 4
Number of states : 16
Q-table
[[ -9.85448046 -7.4657981 -9.59584501 -10\. ]
[ -9.53200011 -9.54250775 -9.10115662 -10\. ]
[ -9.65308982 -9.51359977 -7.52052469 -10\. ]
[ -9.69762313 -9.5540111 -9.56571455 -10\. ]
[ -9.82319854 -4.83823005 -9.56441915 -9.74234959]
[ -5\. -5\. -5\. -5\. ]
[ -9.6554905 -9.44717167 -7.35077759 -9.77885057]
[ -5\. -5\. -5\. -5\. ]
[ -9.66012445 -4.28223592 -9.48312882 -9.76812285]
[ -9.59664264 9.60799515 -4.48137699 -9.61956668]
[ -9.71057124 -5.6863911 -2.04563412 -9.75341962]
[ -5\. -5\. -5\. -5\. ]
[ -5\. -5\. -5\. -5\. ]
[ -9.54737964 22.84803205 18.17841481 -9.45516929]
[ -9.69494035 34.16859049 72.04055782 40.62254838]
[ 100\. 100\. 100\. 100\. ]]
Output after learning
SFFF
FHFH
FFFH
HFFG
===============
(Down)
SFFF
FHFH
FFFH
HFFG
===============
(Down)
SFFF
FHFH
FFFH
HFFG
===============
(Right)
SFFF
FHFH
FFFH
HFFG
===============
(Right)
SFFF
FHFH
FFFH
HFFG
===============
(Right)
SFFF
FHFH
FFFH
HFFG
===============
(Right)
SFFF
FHFH
FFFH
HFFG
===============
(Right)
SFFF
FHFH
FFFH
HFFG
===============
(Right)
SFFF
FHFH
FFFH
HFFG
===============
(Right)
SFFF
FHFH
FFFH
HFFG
===============
(Right)
SFFF
FHFH
FFFH
HFFG
===============
(Right)
SFFF
FHFH
FFFH
HFFG
===============
(Right)
SFFF
FHFH
FFFH
HFFG
维护少数状态的表是可能的,但在现实世界中,状态会变得无限。 因此,需要一种解决方案,其合并状态信息并输出动作的 Q 值而不使用 Q 表。 这是神经网络充当函数逼近器的地方,该函数逼近器针对不同状态信息的数据及其所有动作的相应 Q 值进行训练,从而使它们能够预测任何新状态信息输入的 Q 值。 用于预测 Q 值而不是使用 Q 表的神经网络称为 Q 网络。
在这里,对于FrozenLake-v0
环境,让我们使用一个将状态信息作为输入的单个神经网络,其中状态信息表示为一个1 x 状态
形状的单热编码向量(此处为1 x 16
)并输出形状为1 x 动作数
的向量(此处为1 x 4
)。 输出是所有操作的 Q 值:
# considering there are 16 states numbered from state 0 to state 15, then state number 4 will be # represented in one hot encoded vector as
input_state = [0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0]
通过添加更多隐藏层和不同激活函数的选择,Q 网络绝对比 Q 表具有许多优势。 与 Q 表不同,在 Q 网络中,通过最小化反向传播的损失来更新 Q 值。 损失函数由下式给出:
让我们尝试用 Python 来实现这一点,并学习如何实现基本的 Q 网络算法,以使智能体从头到尾在整个 16 个网格的冰冻湖面中导航,而不会陷入困境:
# importing dependency libraries
from __future__ import print_function
import Gym
import numpy as np
import tensorflow as tf
import random
# Load the Environment
env = Gym.make('FrozenLake-v0')
# Q - Network Implementation
## Creating Neural Network
tf.reset_default_graph()
# tensors for inputs, weights, biases, Qtarget
inputs = tf.placeholder(shape=[None,env.observation_space.n],dtype=tf.float32)
W = tf.get_variable(name="W",dtype=tf.float32,shape=[env.observation_space.n,env.action_space.n],initializer=tf.contrib.layers.xavier_initializer())
b = tf.Variable(tf.zeros(shape=[env.action_space.n]),dtype=tf.float32)
qpred = tf.add(tf.matmul(inputs,W),b)
apred = tf.argmax(qpred,1)
qtar = tf.placeholder(shape=[1,env.action_space.n],dtype=tf.float32)
loss = tf.reduce_sum(tf.square(qtar-qpred))
train = tf.train.AdamOptimizer(learning_rate=0.001)
minimizer = train.minimize(loss)
## Training the neural network
init = tf.global_variables_initializer() #initializing tensor variables
#initializing parameters
y = 0.5 #discount factor
e = 0.3 #epsilon value for epsilon-greedy task
episodes = 10000 #total number of episodes
with tf.Session() as sess:
sess.run(init)
for i in range(episodes):
s = env.reset() #resetting the environment at the start of each episode
r_total = 0 #to calculate the sum of rewards in the current episode
while(True):
#running the Q-network created above
a_pred,q_pred = sess.run([apred,qpred],feed_dict={inputs:np.identity(env.observation_space.n)[s:s+1]})
#a_pred is the action prediction by the neural network
#q_pred contains q_values of the actions at current state 's'
if np.random.uniform(low=0,high=1) < e: #performing epsilon-greedy here
a_pred[0] = env.action_space.sample()
#exploring different action by randomly assigning them as the next action
s_,r,t,_ = env.step(a_pred[0]) #action taken and new state 's_' is encountered with a feedback reward 'r'
if r==0:
if t==True:
r=-5 #if hole make the reward more negative
else:
r=-1 #if block is fine/frozen then give slight negative reward to optimize the path
if r==1:
r=5 #good positive goat state reward
q_pred_new = sess.run(qpred,feed_dict={inputs:np.identity(env.observation_space.n)[s_:s_+1]})
#q_pred_new contains q_values of the actions at the new state
#update the Q-target value for action taken
targetQ = q_pred
max_qpredn = np.max(q_pred_new)
targetQ[0,a_pred[0]] = r + y*max_qpredn
#this gives our targetQ
#train the neural network to minimize the loss
_ = sess.run(minimizer,feed_dict={inputs:np.identity(env.observation_space.n)[s:s+1],qtar:targetQ})
s=s_
if t==True:
break
#learning ends with the end of the above loop of several episodes above
#let's check how much our agent has learned
print("Output after learning")
print()
s = env.reset()
env.render()
while(True):
a = sess.run(apred,feed_dict={inputs:np.identity(env.observation_space.n)[s:s+1]})
s_,r,t,_ = env.step(a[0])
print("===============")
env.render()
s = s_
if t==True:
break
-----------------------------------------------------------------------------------------------
<<OUTPUT>>
Output after learning
SFFF
FHFH
FFFH
HFFG
===============
(Down)
SFFF
FHFH
FFFH
HFFG
===============
(Left)
SFFF
FHFH
FFFH
HFFG
===============
(Up)
SFFF
FHFH
FFFH
HFFG
===============
(Down)
SFFF
FHFH
FFFH
HFFG
===============
(Right)
SFFF
FHFH
FFFH
HFFG
===============
(Right)
SFFF
FHFH
FFFH
HFFG
===============
(Up)
SFFF
FHFH
FFFH
HFFG
Q 学习和 Q 网络都需要付出一定的稳定性。 在某些情况下,如果给定的 Q 值超参数集未收敛,但具有相同的超参数,则有时会出现收敛。 这是由于这些学习方法的不稳定。 为了解决这个问题,如果状态空间较小,则应定义更好的初始策略(此处为给定状态的最大 Q 值)。 此外,超参数,尤其是学习率,折扣因子和ε值,起着重要的作用。 因此,必须正确初始化这些值。
由于增加了状态空间,因此与 Q 学习相比,Q 网络具有更大的灵活性。 Q 网络中的深度神经网络可能会导致更好的学习和表现。 就使用深度状态 Q 网络玩 Atari 而言,有许多调整,我们将在接下来的章节中进行讨论。
在本章中,我们学习了 OpenAI Gym,包括安装各种重要功能以加载,渲染和理解环境状态操作空间。 我们了解了 ε 贪婪方法作为探索与利用难题的解决方案,并尝试实现基本的 Q 学习和 Q 网络算法,以训练增强学习智能体从 OpenAI Gym 导航环境。
在下一章中,我们将介绍强化学习中最基本的概念,包括马尔可夫决策过程(MDP),贝尔曼方程和 Markov Chain Monte Carlo。