强化学习入门(二)

1.强化学习相关术语及其相关的定义:      

        介绍名为价值迭代法的算法所需的术语。具体而言,将介绍奖励、动作价值、状态价值、贝尔曼方程、马尔可夫决策过程等术语。

        奖励

        要实现价值迭代法,需要先定义价值,强化学习中的价值迭代法使用奖励而不是货币的概念。例如,在迷宫任务的情况下,当达到目标时将给予奖励。此外,在机器人不摔倒的条件下前行时,每走一步都给予其奖励。对于围棋来说,获胜后就能够得到奖励。在特定时间t给出的奖励Rt称为即时奖励(immediate reward)。另外,对于强化学习中的奖励值Rt,根据具体的任务需要自行设定。此外,未来将获得的奖励总和Gt被称为总奖励。
        
考虑到时间因素,需要引入利率的概念。例如,如果当前 将10000日元存入银行10年,每年的利息也会产生复利效应,因此10年后的金额会超过10000日元。相反,在10年后的1万日元将比当前
价格下的1万日元要便宜一点。这种体现未来奖励的方式称为时间折扣,折扣率表示为γ。γ的数值介于0和1之间。结合利率和复利效应,考虑未来的总奖励时,也将折扣率考虑进来,使用折扣总奖励(discounted total reward)Gt来表示。

        动作价值

价值迭代法定义了两种类型的价值: 动作价值(action value) 状态价值(state value)
我们使用之前的迷宫来解释这两个价值的含义。
                        
从S7向右移动即可到达目标。换句话说,如果状态s=S7且动作a=向右,则意味着S7→S8移动,
这样就可以在下一步中达到目标并获得奖励 Rt+1=1。
上述说明可以由公式表示。在策略π下,动作价值可以用动作价值函数Qπ(s,a)表示。有4种类型的动作(向上、向右、向下、向 左),在动作索引为a=1时向右移动,所以有:
                
        解释: 到达S8的奖励为1,那么在S7(即s=7)时,采取向右的动作(即a=1),即可获得奖励1,因此在S7这个位置时,向右的动作价值为1。
因此,如果智能体处于s=S7且动作a=向上,那么动作价值 Qπ(s,a)是什么?此时智能体从S7向S4移动,远离了目标。这样一 来,需要额外的两个步骤,S7→S4→S7→S8,才能达到目标。可以
将其表示为以下公式:
        
        解释:我们上面讨论了S7向右这个动作的价值,那么向上的价值是多少呢? 假设在S7时向上走,那么我们需要多走两步才能到S8,每多一步我们需要在价值上打一个折γ,两步就是\gamma ^{^{2}}。因此向上走的价值为\gamma ^{^{2}}*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的更新公式是
                 而Q学习的更新公式如下所示:
在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所示。颜色没有完全变成暗红色的原因是状态价值以折 扣率γ打了折扣。
本文章的类容来自 边做边学深度强化学习,长下面这个样子。
        
  • 20
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值