前言
一、Q-learning
1. FrozenLake 环境
2.Q-Learning的简单实现
二、Q-Table Learning 代码实现
1. 调用FrozenLake 环境
2.Q-Table Learning 算法代码实现
3.Q-Table Learning 整体代码
三、Q-Learning with Neural Networks
1.Q-network Learning简单实现
2. 拟合 Q 迭代 算法
3.Q-network Learning代码实现(Tensorflow)
大家听说的可以玩 Atari 游戏的 Deep Q-Networks 是 Q-Learning 算法的更大、更复杂的实现。所以在学习DQN之前,我们需要对Q-learning有一定的了解。本篇文章将从 基于 Table 的 Q-Learning 和 基于神经网络的 Q-Learning 两方面了解Q-Learning。
概述:Q-learning为一种针对表格环境的表格方法 。
与试图学习如何通过函数定义将观察值直接映射到动作的策略梯度方法不同,Q-Learning 试图学习处于给定状态(State)的价值(Value),并在那里采取特定的动作(action)。 虽然这两种方法最终都是学习如何在特定情况下让智能体(Agent)在特定情况下采取明智的行动,但采取行动的方式却大不相同。 我们将通过一个简单的实现来讲解 Q-Learning 算法。
我们通过 OpenAI gym 构建 FrozenLake 环境来模拟 Q-learning 算法的交互环境。OpenAI gym 为我们提供了一系列用于训练agent的简单环境。FrozenLake 环境由一个 4x4 的块网格组成,分别为起始块、目标块、安全的冻结块以及一个危险的悬崖。其目标是让 agent 学会从起点安全的到达目标,而不会掉入悬崖。在任何给定时间,agent可以选择向上、向下、向左或向右移动。环境中还设置了一阵风,其目的是偶尔将 agent 吹到他们没有选择的网格上。因此,每次都完美的实现目标是不可能的,但训练 agent 避开悬崖并达到目标是可行的。环境中设置每一步的奖励都是 0,达到目标设置奖励为1 。因此,我们需要一个学习长期预期汇报的算法。而 Q-learning 就是这样的一个算法。FrozenLake 环境的规则如图1所示。
图1 FrozenLake 环境的规则
在最简单的实现中,Q-Learning 是环境中可能的每个状态(行)和动作(列)的值表。 在表格的每个单元格中,我们学习在给定状态下采取各个给定动作的价值。 在 FrozenLake 环境的情况下,我们有 16 种可能的状态(每个块一个)和 4 种可能的动作(四个运动方向)。 我们首先将一个 16x4 的 Q 值表格初始化为统一的(全为零),然后通过观察采取不同动作获得的奖励,我们相应地更新表格。使用贝尔曼方程来更新我们的 Q 表,贝尔曼方程证明给定动作的预期长期奖励等于当前动作的即时奖励加上在接下状态采取的最佳未来动作的预期奖励。贝尔曼方程的等式如下所示:
其中 是 s,a 为状态,动作的一般表示, 为折扣标量(衰减因子),其决定未来可能的奖励与当前奖励相比的重要性。 通过以这种方式更新,该表开始慢慢获得在给定状态下给定动作的预期未来奖励的准确度量。 折扣变量允许我们决定未来可能的奖励与当前奖励相比的重要性。 通过以这种方式更新,该表开始慢慢获得在给定状态下给定动作的预期未来奖励的准确度量。
注:贝尔曼方程(Bellman equation)和马尔科夫决策过程(MDPs)是强化学习的基础,大家应有所了解。
代码如下:
import gym
env = gym.make('FrozenLake-v1')
代码如下:
#Initialize table with all zeros
Q = np.zeros([env.observation_space.n,env.action_space.n])
# Set learning parameters
lr = .8
y = .95
num_episodes = 2000
#create lists to contain total rewards and steps per episode
#jList = []
rList = []
for i in range(num_episodes):
#Reset environment and get first new observation
s = env.reset()
rAll = 0
d = False
j = 0
#The Q-Table learning algorithm
while j < 99:
j+=1
#Choose an action by greedily (with noise) picking from Q table
a = np.argmax(Q[s,:] + np.random.randn(1,env.action_space.n)*(1./(i+1)))
#Get new state and reward from environment
s1,r,d,_ = env.step(a)
#Update Q-Table with new knowledge
Q[s,a] = Q[s,a] + lr*(r + y*np.max(Q[s1,:]) - Q[s,a])
rAll += r
s = s1
if d == True:
break
#jList.append(j)
rList.append(rAll)
代码如下:
import gym
import numpy as np
env = gym.make('FrozenLake-v1')
#Initialize table with all zeros
Q = np.zeros([env.observation_space.n,env.action_space.n])
# Set learning parameters
lr = .8
y = .95
num_episodes = 2000
#create lists to contain total rewards and steps per episode
#jList = []
rList = []
for i in range(num_episodes):
#Reset environment and get first new observation
s = env.reset()
rAll = 0
d = False
j = 0
#The Q-Table learning algorithm
while j < 99:
j+=1
#Choose an action by greedily (with noise) picking from Q table
a = np.argmax(Q[s,:] + np.random.randn(1,env.action_space.n)*(1./(i+1)))
#Get new state and reward from environment
s1,r,d,_ = env.step(a)
#Update Q-Table with new knowledge
Q[s,a] = Q[s,a] + lr*(r + y*np.max(Q[s1,:]) - Q[s,a])
rAll += r
s = s1
if d == True:
break
#jList.append(j)
rList.append(rAll)
print "Score over time: " + str(sum(rList)/num_episodes)
print "Final Q-Table Values"
print Q
构建一个简单的 Q 表是比较简单的,但任何游戏或者现实世界中的环境的状态数量几乎无限大,使用表格的方法表示 Q 函数处理大规模数据空间(如原始像素输入)的任务时将十分低效,处理具有连续数据的控制任务更是几乎不可能。针对以上问题,结合神经网络是Q-Learning被提出。其通过神经网络充当函数逼近器,可以将任意数量的可能状态表示为向量,并学习如何将成向量映射成 Q 值。
还是以 FrozenLake 环境模拟 Q-network Learning 的交互环境,使用一层神经网络将状态表示为 one-hot 编码向量 (1x16),并生成一个包含 4 个 Q 值的向量来表示动作。与 Q-Table Learning 不同,Q-network Learning 可以轻松地通过添加层、激活函数和不同的输入类型来扩展神经网络实现大规模状态空间的表示。更新方法也略有不同,Q-network Learning 使用大家熟悉的深度学习中的反向传播和损失函数进行更新。Q-network Learning 使用参数 进行函数拟合,更新公式如下:
其中 L 代表 loss,如均方误差。
代码如下:
import gym
import numpy as np
import random
import tensorflow as tf
import matplotlib.pyplot as plt
%matplotlib inline
from tensorflow.python.framework import ops
ops.reset_default_graph()#函数用于清除默认图形堆栈并重置全局默认图形。
#These lines establish the feed-forward part of the network used to choose actions
import tensorflow.compat.v1 as tf
tf.disable_v2_behavior()
inputs1 = tf.placeholder(shape=[1,16],dtype=tf.float32)
W = tf.Variable(tf.random_uniform([16,4],0,0.01))
Qout = tf.matmul(inputs1,W)
predict = tf.argmax(Qout,1)
#Below we obtain the loss by taking the sum of squares difference between the target and prediction Q values.
nextQ = tf.placeholder(shape=[1,4],dtype=tf.float32)
loss = tf.reduce_sum(tf.square(nextQ - Qout))
trainer = tf.train.GradientDescentOptimizer(learning_rate=0.1)
updateModel = trainer.minimize(loss)
#Training the network
nit = tf.initialize_all_variables()
# Set learning parameters
y = .99
e = 0.1
num_episodes = 2000
#create lists to contain total rewards and steps per episode
jList = []
rList = []
with tf.Session() as sess:
sess.run(init)
for i in range(num_episodes):
#Reset environment and get first new observation
s = env.reset()
rAll = 0
d = False
j = 0
#The Q-Network
while j < 99:
j+=1
#Choose an action by greedily (with e chance of random action) from the Q-network
a,allQ = sess.run([predict,Qout],feed_dict={inputs1:np.identity(16)[s:s+1]})
if np.random.rand(1) < e:
a[0] = env.action_space.sample()
#Get new state and reward from environment
s1,r,d,_ = env.step(a[0])
#Obtain the Q' values by feeding the new state through our network
Q1 = sess.run(Qout,feed_dict={inputs1:np.identity(16)[s1:s1+1]})
#Obtain maxQ' and set our target value for chosen action.
maxQ1 = np.max(Q1)
targetQ = allQ
targetQ[0,a[0]] = r + y*maxQ1
#Train our network using target and predicted Q values
_,W1 = sess.run([updateModel,W],feed_dict={inputs1:np.identity(16)[s:s+1],nextQ:targetQ})
rAll += r
s = s1
if d == True:
#Reduce chance of random action as we train the model.
e = 1./((i/50) + 10)
break
jList.append(j)
rList.append(rAll)
print "Percent of succesful episodes: " + str(sum(rList)/num_episodes) + "%"
注:下一篇更新DQN