强化学习经典算法笔记(五):时间差分算法Temporal Difference(SARSA算法)

强化学习经典算法笔记——SARSA算法

强化学习经典算法笔记(零):贝尔曼方程的推导
强化学习经典算法笔记(一):价值迭代算法Value Iteration
强化学习经典算法笔记(二):策略迭代算法Policy Iteration
强化学习经典算法笔记(三):蒙特卡罗方法Monte Calo Method
强化学习经典算法笔记(四):时间差分算法Temporal Difference(Q-Learning算法)

简介

  上一篇讲到Off-policy的TD算法——Q-Learning。这一次要说说On-policy的SARSA算法。
  首先说为什么叫SARSA算法。其实很简单,算法对Q值的更新依靠的是 s t s_t 转移到&s_{t+1}&所收集的信息,准确地说,利用下面这5个信息,就能更新一个状态的Q值,因此把5个字母拼起来就是SARSA算法了。
s t a t r t s t + 1 a t + 1 s_t \rightarrow a_t \rightarrow r_t \rightarrow s_{t+1} \rightarrow a_{t+1}
  然后再说说为什么SARSA算法是On-policy的算法?SARSA算法基于当前的policy直接执行一次动作,用于收集数据(下图算法倒数第4行,Choose A’ from S’ using policy derived from Q),然后用这个样本更新当前的policy(倒数第3行),因此生成样本的policy和学习时的policy相同,所以是On-policy算法。
在这里插入图片描述
  直接贴代码。超参数的选取和上一个Q-Learning大致一样。

代码

# 导入环境
import gym
import random
import numpy as np
import matplotlib.pyplot as plt
import copy
%matplotlib inline

env = gym.make('Taxi-v2')

# 设定参数
alpha = 0.3 # 学习率,如果太大,学习后期接近收敛的时候,各个Episode total reward之间的variance非常大
gamma = 0.90 # 未来收益对现在来看的折扣
epsilon = 0.02 # 越大越接近随机策略

# Q是字典,用于存储(状态-动作对)及其对应的Q值
Q = {}
for s in range(env.observation_space.n):
    for a in range(env.action_space.n):
        Q[(s,a)] = 0.0

# epsilon-贪心算法,基于最新的Q-table
def epsilon_greedy(state, epsilon):
    if random.uniform(0,1) < epsilon:
        return env.action_space.sample()
    else:
        return max(list(range(env.action_space.n)), key = lambda x: Q[(state,x)])

# 记录Episode总回报值在训练过程中的变化情况
r_record = []
# 记录Q值表的新旧之差
error_qa = []
# 保存Q-Table更新前后的两个table,注意一定要用深度拷贝!!!
Q_old = copy.deepcopy(Q)

# 训练
for i in range(2000):
    
    # 每个Episode的总回报
    r = 0
    
    state = env.reset()
    action = epsilon_greedy(state,epsilon)
    
    while True:
       
        # 执行a_t,得到r_t 和 s_{t+1}
        nextstate, reward, done, _ = env.step(action)
        
        # 得到 a_{t+1} 
        nextaction = epsilon_greedy(nextstate,epsilon) 
        
        # 更新Q值:Q(s,a) = Q(s,a) + α(r_t + γQ(s',a')-Q(s,a))
        Q[(state,action)] += alpha * (reward + gamma * Q[(nextstate,nextaction)]-Q[(state,action)])
        # 新状态变旧状态,进行下一次迭代
        action = nextaction
        state = nextstate
        # 记录当前步骤的回报
        r += reward
        
        if done:
            break
    r_record.append(r)
    
    # 计算Q-table的更新收敛情况
    error = 0
    for i in Q:
        error = error + np.abs(Q[i] - Q_old[i])
    error_qa.append(error)
    Q_old = copy.deepcopy(Q) # 必须用深度拷贝,否则error恒为0
    
print('done!')
env.close()

# 画收敛曲线
plt.figure(1,figsize=(10,10))
plt.plot(list(range(2000)),r_record[:2000],linewidth=0.8)
plt.title('Reword Convergence Curve',fontsize=15)
plt.xlabel('Iteration',fontsize=15)
plt.ylabel('Total Reword of one episode',fontsize=15)

plt.figure(2,figsize=(10,10))
plt.plot(list(range(2000)),error_qa[:2000],linewidth=0.8)
plt.title('Q-table difference Convergence Curve',fontsize=15)
plt.xlabel('Iteration',fontsize=15)
plt.ylabel('Error between old and new Q-table',fontsize=15)

# plt.savefig('2.pdf')

实验结果与模型超参数分析

实验一

  这是在alpha = 0.3, gamma = 0.90, epsilon = 0.02的情况下跑出来的结果。
在这里插入图片描述在这里插入图片描述

实验二

  如果调大学习率alpha,可以预见在算法收敛后,reward的变动依然较大,因为收敛之后学习率步长应该相应减小,不能再大幅度更新Q-table了。下面这是alpha=0.8, gamma = 0.90, epsilon = 0.02的训练效果,收敛之后的variance很大。

在这里插入图片描述在这里插入图片描述

实验三

  如果调大epsilon,会使得policy倾向于随机策略,也就是探索(Exploration)多于利用(Exploitation)。过大的epsilon会在一定程度上削弱训练效果,因为epsilon-贪心算法的随机性越大,policy就越不能利用先前学习到的经验,而越倾向于随机乱试。下面这是alpha=0.3, gamma = 0.90, epsilon = 0.8的收敛性,可见训练效果很差。
在这里插入图片描述在这里插入图片描述

实验四

  gamma表示下一个状态的价值对当前状态的意义大不大。举例来说,gamma=1表示未来状态的收益和当前收益同等重要,这样训练出来的算法会具有相对长远的策略;如果gamma=0,则表示完全不关心未来收益,这样训练出来的算法往往只关注“眼前利益”。算法应当具有适当的gamma值,短期利益和长远目光要兼顾。

  下面这是alpha=0.3, gamma = 0.30, epsilon = 0.02的训练结果,第一张图和实验二结果挺像,但是“刷子”的柄和刷毛反过来了。为什么会这样呢?一个直观解释就是,Taxi这个任务,需要接乘客,送到指定地方,然后放下乘客,这是一套连贯的动作,持续较长时间、且前后有关联的任务,gamma太小,太关注当前收益,不利于policy处理这样的任务。

  第二个图显示Q-table确实能够收敛,但是我们从第一张图知道,它收敛到了错误的地方去了。而且收敛速度非常快,收敛后的部分“毛刺”也比较小,根据公式,这是因为gamma也可以控制Q值更新的步长,或者说幅度,gamma很小意味着每次更新的幅度很小,所以才会出现图中的情况。
在这里插入图片描述在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/hhy_csdn/article/details/89216872
今日推荐