1.强化学习相关术语及其相关的定义:
介绍名为价值迭代法的算法所需的术语。具体而言,将介绍奖励、动作价值、状态价值、贝尔曼方程、马尔可夫决策过程等术语。
奖励
要实现价值迭代法,需要先定义价值,强化学习中的价值迭代法使用奖励而不是货币的概念。例如,在迷宫任务的情况下,当达到目标时将给予奖励。此外,在机器人不摔倒的条件下前行时,每走一步都给予其奖励。对于围棋来说,获胜后就能够得到奖励。在特定时间t给出的奖励Rt称为即时奖励(immediate reward)。另外,对于强化学习中的奖励值Rt,根据具体的任务需要自行设定。此外,未来将获得的奖励总和Gt被称为总奖励。
![](https://i-blog.csdnimg.cn/blog_migrate/dbe111b8f66c1bd2ffb2f235dea4d4ee.png)
考虑到时间因素,需要引入利率的概念。例如,如果当前 将10000日元存入银行10年,每年的利息也会产生复利效应,因此10年后的金额会超过10000日元。相反,在10年后的1万日元将比当前
价格下的1万日元要便宜一点。这种体现未来奖励的方式称为时间折扣,折扣率表示为γ。γ的数值介于0和1之间。结合利率和复利效应,考虑未来的总奖励时,也将折扣率考虑进来,使用折扣总奖励(discounted total reward)Gt来表示。
![](https://i-blog.csdnimg.cn/blog_migrate/0e346eb77a5323e3d8116a4e4990e5f0.png)
动作价值
价值迭代法定义了两种类型的价值:
动作价值(action value)
和
状态价值(state value)
。
我们使用之前的迷宫来解释这两个价值的含义。
![](https://i-blog.csdnimg.cn/blog_migrate/739adcd531e90bee2e66a4aff054458c.png)
从S7向右移动即可到达目标。换句话说,如果状态s=S7且动作a=向右,则意味着S7→S8移动,
这样就可以在下一步中达到目标并获得奖励 Rt+1=1。
上述说明可以由公式表示。在策略π下,动作价值可以用动作价值函数Qπ(s,a)表示。有4种类型的动作(向上、向右、向下、向 左),在动作索引为a=1时向右移动,所以有:
![](https://i-blog.csdnimg.cn/blog_migrate/583dba5dfca750abc86ccfef4ee6dd35.png)
解释:
到达S8的奖励为1,那么在S7(即s=7)时,采取向右的动作(即a=1),即可获得奖励1,因此在S7这个位置时,向右的动作价值为1。
因此,如果智能体处于s=S7且动作a=向上,那么动作价值 Qπ(s,a)是什么?此时智能体从S7向S4移动,远离了目标。这样一 来,需要额外的两个步骤,S7→S4→S7→S8,才能达到目标。可以
将其表示为以下公式:
![](https://i-blog.csdnimg.cn/blog_migrate/184707f88fa3767e05114da48f0c589f.png)
解释:我们上面讨论了S7向右这个动作的价值,那么向上的价值是多少呢?
假设在S7时向上走,那么我们需要多走两步才能到S8,每多一步我们需要在价值上打一个折γ,两步就是
。因此向上走的价值为
*1。
状态价值
状态价值是指在状态s下
遵从策略π行动时
,预计在将来获得的总奖励Gt。将状态s的状态价值函数写为
Vπ(s)
。
例如,智能体在S7时,向右移动将到达目标并获得奖励1,就有:
Vπ(s=7)=1
此外,如果智能体在S4,向下移动到达S7,再向右移动并到达目标S8,状态价值函数的值变为:
Vπ(s=4)=γ*1
贝尔曼方程和马尔可夫决策过程
如果将前面说明的状态价值函数的表达式以更一般的方式表达:
这称为贝尔曼方程。左侧表示
状态s时的状态价值V。该状态价值V是在采用
右侧具有最大值的动作a时所期望的价值。右侧的
Rs’a是指在状态s下采用动作a时所获得的即时奖励Rt+1,右侧的
Vπ()中的s(s,a)表示在
状态s下采用动作a并移动一步后的新状态st+1。换句话说,
将新状态Rt+1下的状态价值V乘以一步的时间折扣率γ,加上即时奖励函数Rs,a,取该和的最大值就是当前的状态价值。这一贝尔曼方程是关于状态价值函数的方程,该方程也同样适用于动作价值函数。
我自己的理解:
设某一位置的状态价值为V(s) 那么 V(s) 等于在 s这个位置采取正确的动作(价值最大化的动作)的价值 加上 下一步的状态价值,而这里的下一步的价值等于多少呢?算法跟上一步的计算方式相同。 ---这里包含一个递归,递归头即下一步到达S8时(当下一步为S8时,可直接拿到价值1),这时候递归返回,计算出沿途的状态价值。
马尔可夫决策过程:
此处原文引用,写的不是很明白,读者可以自行查资料。
作为贝尔曼方程成立的前提条件,学习对象必须是马尔可夫决策 过程(Makov Decision Process,MDP)。这是一个听起来很难的词,其内容却并不复杂。马尔可夫决策过程是一个系统,下一步的状 态st+1由当前状态st和采用的动作at确定。换句话说,这意味着前面 解释的贝尔曼方程中“右侧的s(s,a)表示在状态s下采用动作a并移动一 步后的新状态st+1”这部分成立。 那么,哪些不是马尔可夫决策过程呢?通过当前状态st之外的过 去状态(例如st-1)来确定下一状态st+1的系统,就不是马尔可夫决 策过程。
2.Sarsa的实现迷宫任务
在上一篇中我们使用随机法找出了迷宫的最短路径,这一次我们使用sarsa这种价值迭代算法来实现,为什么随机的策略迭代法能实现我们还要学习sarsa算法呢?最重要的原因就是快,sarsa算法可以在更少的训练周期下更快的找到最佳策略。
构建环境:
看过第一篇的这里可以跳过,代码跟之前一样。
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
#声明图的大小以及图的变量名
fig = plt.figure(figsize=(5,5))
ax = plt.gca()
# 添加标签和标题
plt.xlabel('X')
plt.ylabel('Y')
plt.title('title')
#画出红色墙壁
plt.plot([1,1],[0,1],color='red',linewidth =2)
plt.plot([1,2],[2,2],color='blue',linewidth =2)
plt.plot([2,2],[2,1],color='green',linewidth =2)
plt.plot([2,3],[1,1],color='yellow',linewidth =2)
#画出表示状态的文字S0-S8
plt.text(0.5,2.5,'S0',size=14,ha='center')
plt.text(1.5,2.5,'S1',size=14,ha='center')
plt.text(2.5,2.5,'S2',size=14,ha='center')
plt.text(0.5,1.5,'S3',size=14,ha='center')
plt.text(1.5,1.5,'S4',size=14,ha='center')
plt.text(2.5,1.5,'S5',size=14,ha='center')
plt.text(0.5,0.5,'S6',size=14,ha='center')
plt.text(1.5,0.5,'S7',size=14,ha='center')
plt.text(2.5,0.5,'S8',size=14,ha='center')
plt.text(0.5,2.3,'START',size=14,ha='center')
plt.text(2.5,0.3,'GOAL',size=14,ha='center')
#设定画图的范围
ax.set_xlim(0,3)
ax.set_ylim(0,3)
plt.tick_params(axis='both',which='both',bottom='off',top='off',labelbottom='off',right='off',left='off',labelleft='off')
#当前位置S0用绿色圆圈画出
line,=ax.plot([0.5],[2.5],marker="o",color="g",markersize=60)
效果如下
初始化动作价值空间(注意此处theta_0的定义不是动作空间,是动作价值空间),可以通行设置为1,不允许通行设置为np.nan
#设定参数theta
#行为状态0~7,列为用 ↑ → ↓ ←表示移动方向
theta_0 = np.array([[np.nan,1,1,np.nan],#S0
[np.nan,1,np.nan,1],#S1
[np.nan,np.nan,1,1],#S2
[1,1,1,np.nan],#S3
[np.nan,np.nan,1,1],#S4
[1,np.nan,np.nan,np.nan],#S5
[1,np.nan,np.nan,np.nan],#S6
[1,1,np.nan,np.nan]#S7
])
[a,b] = theta_0.shape
#按照动作空间的格式随机初始化动作价值
Q = np.random.rand(a,b) * theta_0
ε-贪婪法的实现
下面实现如何根据各时刻的动作a的动作价值函数Q来求取策略。 最简单的方法是采用Q值最大的动作(这称为贪婪法)。但是,如果 在没有求得正确的Q值时采用这种方法,会导致根据随机生成的动作 价值的初始值确定动作,其后可能无法很好地学习(例如,从初始的 S0位置每次向右移动)。 因此,我们将以一定的概率ε随机行动,在剩下的1-ε概率下采用 动作价值Q最大的行动。这种方法称为ε-贪婪法。随着试验次数(回 合数或轮数)的增加,ε值会渐渐减小。 像这样的价值迭代强化学习中,必须综合地使用当前动作价值函 数的最大值来采取确定行动(利用)以及随机行动(探索),这一方 法称为“探索和利用的权衡”。
#s 位置
# Q当前价值
# epsilon
# ε 探索与应用的均衡点
# pi_0 价值表
# 基于ε 获取动作方向
def get_action(s,Q,epsilon,pi_0):
direction = ["up","right","down","left"]
if np.random.rand() < epsilon:
# 以ε 概率随机行动
next_direction = np.random.choice(direction,p=pi_0[s,:])
else:
# 采用Q的最大值对应的动作
next_direction = direction[np.nanargmax(Q[s,:])]
#根据概率pi[s,:]选择dirction
if next_direction == "up":
action = 0
elif next_direction == "right":
action = 1
elif next_direction == "down":
action = 2
elif next_direction == "left":
action = 3
return action
# 根据动作获取下一步的状态
def get_s_next(s,a,Q,epsilon,pi_0):
direction = ["up","right","down","left"]
next_direction = direction[a]
#根据概率pi[s,:]选择dirction
if next_direction == "up":
s_next = s - 3
elif next_direction == "right":
s_next = s + 1
elif next_direction == "down":
s_next = s + 3
elif next_direction == "left":
s_next = s - 1
return s_next
这里get_s_next没有什么好说的,平平无奇。
get_action 方法注意一个参数ε 读作epsilon,我们后续会随着训练的进行不断调整ε的值,来使智能体随机运动的概率越来越小(此处体现了探索和利用的权衡)。
下面是Srasa算法的代码实现,此处采用的迭代没有采取递归写法。
#s 当前状态(位置)
#a 当前采取行动
#r 当前行动的价值
#s_next 下一步的状态
#a_next 下一步的行动
#eta 学习率
#gamma 折扣率
#更新价值函数 Q
def Sarsa(s,a,r,s_next,a_next,Q,eta,gamma):
if s_next == 8:
Q[s,a] = Q[s,a] + eta * (r - Q[s,a])
else:
Q[s,a] = Q[s,a] + eta * (r + gamma * Q[s_next,a_next] - Q[s,a])
return Q;
定义基于Sarsa求解迷宫问题的函数,输出状态,动作的历史记录以及更新后的Q
def goal_maze_ret_s_a_Q(Q,epsilon,eta,gamma,pi):
s = 0 #开始地点
a = a_next = get_action(s,Q,epsilon,pi) #初始动作
s_a_history = [[0,np.nan]] #记录智能体的移动序列
while(1):
a = a_next #更新动作
# 将动作放在现在的状态下(最终的index = -1)
s_a_history[-1][1] = a;
#有效的下一个状态
s_next = get_s_next(s,a,Q,epsilon,pi)
# 带入下一个状态,动作未知时为nan
s_a_history.append([s_next,np.nan])
# 给予奖励,求得下一个动作
if s_next == 8:
r = 1 #到达目标,给予奖励
a_next = np.nan
else:
r = 0
#求得下一动作a_next
a_next = get_action(s_next,Q,epsilon,pi)
# 更新价值函数
Q = Sarsa(s,a,r,s_next,a_next,Q,eta,gamma)
#终止判断
if s_next == 8: #到达目的地则结束
break
else:
s = s_next
return [s_a_history,Q]
接下来我们来使用sarsa算法求解迷宫问题
# 通过Sarsa求解迷宫问题
eta = 0.1 # 学习率
gamma = 0.9 #时间折扣率
epsilon = 0.5 # 贪婪算法的初始值
v = np.nanmax(Q,axis = 1) #根据状态求价值最大
is_continue = True
episode = 1
while is_continue:
print("当前回合"+str(episode))
# 贪婪算法的初始值逐渐减小
epsilon = epsilon / 2
#通过Sarsa求解迷宫问题,求取移动历史和更新后的Q值
[s_a_history,Q] = goal_maze_ret_s_a_Q(Q,epsilon,eta,gamma,pi_0)
#状态价值变化
new_v = np.nanmax(Q,axis=1)#各状态求得最大值
print("状态价值的变化:"np.sum(np.abs(new_v-v)))#输出状态价值的变化
v = new_v
print("求解迷宫问题所需步数"+ str(len(s_a_history)-1))
episode = episode + 1
if episode > 100:
break
当我们看一下效果
我们看到只需要3到4轮训练,就已经达到了想要的效果。
Q学习的实现
在本节中,我们实现Q学习,这是一种价值迭代算法。与Sarsa的 不同之处在于其动作价值函数的更新公式不同。 在Sarsa的情况下,动作值函数Q的更新公式是为了实现Q学习仅需要实现
中Sarsa的动作价值函数Q的更新部分。
在Sarsa的情况下,动作值函数Q的更新公式是
![](https://i-blog.csdnimg.cn/blog_migrate/c226c2cdccfaf9fdb825ffac280bb276.png)
而Q学习的更新公式如下所示:
![](https://i-blog.csdnimg.cn/blog_migrate/4c850c0fda8eae27edde4f3ebdc7183a.png)
在Sarsa的情况下,在更新时需要求取下一步动作at+1,并将其 用于更新。另一方面,Q学习使用在状态st+1下动作价值函数值中的 最大值来进行更新。由于Sarsa使用下一个动作at+1来更新动作价值 函数Q,因此Sarsa算法的特征之一是Q的更新依赖于求取at+1的策 略,这样的特征称为策略依赖型(ON)特征。 在Q学习中,动作价值函数Q的更新不依赖于动作的决定方法(策略)。这种特征称为策略关闭型(OFF)特征。由于ε-贪婪法产生的随机性不用于更新公式值,因此其动作价值函数的收敛快于Sarsa。
#定义Q学习算法
def Q_learning(s,a,r,s_next,Q,eta,gamma):
if s_next == 8:
Q[s,a] = Q[s,a] + eta * (r - Q[s,a])
else:
Q[s,a] = Q[s,a] + eta * (r + gamma * np.nanmax(Q[s_next,:]) - Q[s,a])
return Q;
其他部分与sarsa函数一致。
[a,b] = theta_0.shape
#此处乘以0.1是为了使Q值变小,便于绘图
Q = np.random.rand(a,b) * theta_0 * 0.1
我们修改一下上一节中Sarsa的动作价值函数Q的更新部分,改为使用Q学习算法来更新。
#我们重写上一节中Sarsa的动作价值函数Q的更新部分。
def goal_maze_ret_s_a_Q(Q,epsilon,eta,gamma,pi):
s = 0 #开始地点
a = a_next = get_action(s,Q,epsilon,pi) #初始动作
s_a_history = [[0,np.nan]] #记录智能体的移动序列
while(1):
a = a_next #更新动作
# 将动作放在现在的状态下(最终的index = -1)
s_a_history[-1][1] = a;
#有效的下一个状态
s_next = get_s_next(s,a,Q,epsilon,pi)
# 带入下一个状态,动作未知时为nan
s_a_history.append([s_next,np.nan])
# 给予奖励,求得下一个动作
if s_next == 8:
r = 1 #到达目标,给予奖励
a_next = np.nan
else:
r = 0
#求得下一动作a_next
a_next = get_action(s_next,Q,epsilon,pi)
# 更新价值函数
Q = Q_learning(s,a,r,s_next,Q,eta,gamma)
#终止判断
if s_next == 8: #到达目的地则结束
break
else:
s = s_next
return [s_a_history,Q]
# 通过Q学习算法求解迷宫问题
eta = 0.1 # 学习率
gamma = 0.9 #时间折扣率
epsilon = 0.5 # 贪婪算法的初始值
v = np.nanmax(Q,axis = 1) #根据状态求价值最大
is_continue = True
episode = 1
V = [] #存放每回合的状态价值
V.append(np.nanmax(Q,axis=1)) #求各状态下动作价值的最大值
while is_continue:
print("当前回合"+str(episode))
# 贪婪算法的初始值逐渐减小
epsilon = epsilon / 2
#通过Q求解迷宫问题,求取移动历史和更新后的Q值
[s_a_history,Q] = goal_maze_ret_s_a_Q(Q,epsilon,eta,gamma,pi_0)
#状态价值变化
new_v = np.nanmax(Q,axis=1)#各状态求得最大值
print(np.sum(np.abs(new_v-v)))#输出状态价值的变化
v = new_v
V.append(v)#添加该回合终止时的状态价值函数
print("求解迷宫问题所需步数"+ str(len(s_a_history)-1))
episode = episode + 1
if episode > 100:
break
下面是绘制动画的代码,不用深究。
from matplotlib import animation
from IPython.display import HTML
import matplotlib.cm as cm # color map
def init():
line.set_data([], [])
return (line,)
def animate(i):
line, = ax.plot([0.5], [2.5], marker="s",
color=cm.jet(V[i][0]), markersize=85) # S0
line, = ax.plot([1.5], [2.5], marker="s",
color=cm.jet(V[i][1]), markersize=85) # S1
line, = ax.plot([2.5], [2.5], marker="s",
color=cm.jet(V[i][2]), markersize=85) # S2
line, = ax.plot([0.5], [1.5], marker="s",
color=cm.jet(V[i][3]), markersize=85) # S3
line, = ax.plot([1.5], [1.5], marker="s",
color=cm.jet(V[i][4]), markersize=85) # S4
line, = ax.plot([2.5], [1.5], marker="s",
color=cm.jet(V[i][5]), markersize=85) # S5
line, = ax.plot([0.5], [0.5], marker="s",
color=cm.jet(V[i][6]), markersize=85) # S6
line, = ax.plot([1.5], [0.5], marker="s",
color=cm.jet(V[i][7]), markersize=85) # S7
line, = ax.plot([2.5], [0.5], marker="s",
color=cm.jet(1.0), markersize=85) # S8
return (line,)
anim = animation.FuncAnimation(
fig, animate, init_func=init, frames=len(V), interval=200, repeat=False)
HTML(anim.to_jshtml())
我们来看一下效果图
当 播放动画时,随着回合的重复,状态价值函数逐渐增大,方格随之变 化。首先,只有目标S8具有高价值,因此只有S8是深红色而其他方格 是蓝色。重复几个回合后随着Q学习的进行,可以从与到达目标相反 的方向观察S7、S4、S3、S0等,到达目标的路径逐渐从蓝色变为红色,如图2.25所示。颜色没有完全变成暗红色的原因是状态价值以折 扣率γ打了折扣。
本文章的类容来自 边做边学深度强化学习,长下面这个样子。
![](https://i-blog.csdnimg.cn/blog_migrate/2c5f64a2bd59345ca2d3d35a0d965b5a.png)