【动手学强化学习】第 5 章 时序差分算法知识点总结

本章知识点:

动态规划与时序差分(temporal difference,TD)的区别

  • 第 4 章介绍的动态规划算法要求马尔可夫决策过程是已知的,即要求与智能体交互的环境是完全已知的(例如迷宫或者给定规则的网格世界)
  • 在此条件下,智能体其实并不需要和环境真正交互来采样数据,直接用动态规划算法就可以解出最优价值或策略
  • 对于大部分强化学习现实场景(例如电子游戏或者一些复杂物理环境),其马尔可夫决策过程的状态转移概率是无法写出来的,也就无法直接进行动态规划
  • 在这种情况下,智能体只能和环境进行交互,通过采样到的数据来学习,这类学习方法统称为无模型的强化学习(model-free reinforcement learning)
  • 不同于动态规划算法,无模型的强化学习算法不需要事先知道环境的奖励函数和状态转移函数,而是直接使用和环境交互的过程中采样到的数据来学习,这使得它可以被应用到一些简单的实际场景中
  • 本章将要讲解无模型的强化学习中的两大经典算法:Sarsa 和 Q-learning,都是基于时序差分(temporal difference,TD)的强化学习算法

时序差分

  • 时序差分是一种用来估计一个策略的价值函数的方法,它结合了蒙特卡洛和动态规划算法的思想
  • 时序差分方法和蒙特卡洛的相似之处在于可以从样本数据中学习,不需要事先知道环境;和动态规划的相似之处在于根据贝尔曼方程的思想,利用后续状态的价值估计来更新当前状态的价值估计
  • 时序差分算法的核心思想是用对未来动作选择的价值估计来更新对当前动作选择的价值估计,这是强化学习中的核心思想之一

蒙特卡洛对价值函数的增量更新方式:

  • 使用增量式更新 估计状态价值的推导过程, V ( s ) V(s) V(s)为状态 s s s的价值, M ( s ) M(s) M(s)为状态 s s s的总回报, N ( s ) N(s) N(s)为采样到状态 s s s的总计数,根据上述可得, V ( s ) = M ( s ) N ( s ) V(s)= \frac{M(s)}{N(s)} V(s)=N(s)M(s)
    V ( s ) = M ( s ) N ( s ) = 1 N ( s ) M ( s ) = 1 N ( s ) [ G + M ( s ) ] 这里的 M ( s ) 是减去最新一次遇到状态 s 的总回报 = 1 N ( s ) [ G + ( N ( s ) − 1 ) V ( s ) ] 右边的 V ( s ) 也是去掉最新一次状态 s 价值的估计 = V ( s ) + 1 N ( s ) ( G − V ( s ) ) \begin{aligned} V(s) &= \frac{M(s)}{N(s)} \\ &= \frac{1}{N(s)}M(s) \\ &= \frac{1}{N(s)}[G+M(s)] 这里的M(s)是减去最新一次遇到状态s的总回报\\ &= \frac{1}{N(s)}[G+(N(s)-1)V(s)] 右边的V(s)也是去掉最新一次状态s价值的估计\\ &= V(s) + \frac{1}{N(s)}(G- V(s)) \end{aligned} V(s)=N(s)M(s)=N(s)1M(s)=N(s)1[G+M(s)]这里的M(s)是减去最新一次遇到状态s的总回报=N(s)1[G+(N(s)1)V(s)]右边的V(s)也是去掉最新一次状态s价值的估计=V(s)+N(s)1(GV(s))

V ( s t ) ← V ( s t ) + 1 N ( s t ) ( G t − V ( s t ) ) 将 1 N ( s t ) 替换为 α V ( s t ) ← V ( s t ) + α [ G t − V ( s t ) ] \begin{aligned} V(s_t) &\leftarrow V(s_t) + \frac{1}{N(s_t)}(G_t- V(s_t)) \\ &将\frac{1}{N(s_t)}替换为\alpha \\ V(s_t) &\leftarrow V(s_t)+ \alpha [G_t- V(s_t)] \end{aligned} V(st)V(st)V(st)+N(st)1(GtV(st))N(st)1替换为αV(st)+α[GtV(st)]

  • α \alpha α表示对价值估计更新的步长

  • 可以将 α \alpha α取为一个常数,此时更新方式不再像蒙特卡洛方法那样严格地取期望

  • 蒙特卡洛方法必须要等整个序列结束之后才能计算得到这一次的回报 G t G_t Gt,而时序差分方法只需要当前步结束即可进行计算

  • 时序差分算法用当前获得的奖励加上下一个状态的价值估计来作为在当前状态会获得的回报
    V ( s t ) ← V ( s t ) + α [ r t + γ V ( s t + 1 ) − V ( s t ) ] \begin{aligned} V(s_t) &\leftarrow V(s_t)+ \alpha [r_t + \gamma V(s_{t+1})- V(s_t)] \end{aligned} V(st)V(st)+α[rt+γV(st+1)V(st)]

  • r t + γ V ( s t + 1 ) − V ( s t ) r_t + \gamma V(s_{t+1})- V(s_t) rt+γV(st+1)V(st)通常被称为时序差分(temporal difference,TD)误差(error)

  • 时序差分算法将TD error与步长的乘积作为状态价值的更新量

  • r t + γ V ( s t + 1 ) r_t + \gamma V(s_{t+1}) rt+γV(st+1)代替 G t G_t Gt的推导过程:
    V π ( s ) = E π [ G t ∣ S t = s ] = E π [ ∑ k = 0 ∞ γ k R t + k ∣ S t = s ] = E π [ R t + γ ∑ k = 0 ∞ γ k R t + k + 1 ∣ S t = s ] = E π [ R t + γ V π ( S t + 1 ) ∣ S t = s ] \begin{aligned} V^\pi(s) &= E_\pi[G_t | S_t =s] \\ &= E_\pi[\sum_{k=0}^{\infty}\gamma^kR_{t+k}| S_t =s] \\ &= E_\pi[R_t + \gamma \sum_{k=0}^{\infty}\gamma^kR_{t+k+1}| S_t =s] \\ &= E_\pi[R_t + \gamma V^\pi(S_{t+1})| S_t =s] \end{aligned} Vπ(s)=Eπ[GtSt=s]=Eπ[k=0γkRt+kSt=s]=Eπ[Rt+γk=0γkRt+k+1St=s]=Eπ[Rt+γVπ(St+1)St=s]

  • 在用策略和环境交互时,每采样一步,我们就可以用时序差分算法来更新状态价值估计

  • 时序差分算法用到了 V ( s t + 1 ) V(s_{t+1}) V(st+1)的估计值,可以证明它最终收敛到策略 π \pi π的价值函数,书中未进行证明

Sarsa算法

  • 用时序差分方法来估计价值函数,则策略评估(根据 π \pi π计算 V π V^\pi Vπ)已实现

  • 在不知道奖励函数和状态转移函数的情况下,策略提升(根据 V π V^\pi Vπ优化 π \pi π)可以直接用时序差分算法来估计动作价值函数 Q Q Q
    Q ( s t , a t ) ← Q ( s t , a t ) + α [ r t + γ Q ( s t + 1 , a t + 1 ) − Q ( s t , a t ) ] \begin{aligned} Q(s_t,a_t) \larr Q(s_t,a_t) + \alpha[r_t + \gamma Q(s_{t+1},a_{t+1}) - Q(s_t,a_t)] \end{aligned} Q(st,at)Q(st,at)+α[rt+γQ(st+1,at+1)Q(st,at)]

  • 然后用贪婪算法来选取在某个状态下动作价值最大的那个动作,即 a r g max ⁡ a Q ( s , a ) arg\max_aQ(s,a) argmaxaQ(s,a),这样似乎已经形成了一个完整的强化学习算法:用贪婪算法根据动作价值选取动作来和环境交互,再根据得到的数据用时序差分算法更新动作价值估计

存在的问题

  • 第一,如果要用时序差分算法来准确地估计策略的状态价值函数,我们需要用极大量的样本来进行更新。但实际上我们可以忽略这一点,直接用一些样本来评估策略,然后就可以更新策略了。我们可以这么做的原因是策略提升可以在策略评估未完全进行的情况进行,回顾一下,价值迭代(参见 4.4 节)就是这样,这其实是广义策略迭代(generalized policy iteration)的思想
  • 第二,如果在策略提升中一直根据贪婪算法得到一个确定性策略,可能会导致某些状态动作对 ( s , a ) (s,a) (s,a)永远没有在序列中出现,以至于无法对其动作价值进行估计,进而无法保证策略提升后的策略比之前的好,简单常用的解决方案是不再一味使用贪婪算法,而是采用一个 ϵ \epsilon ϵ-贪婪策略:有 1 − ϵ 1- \epsilon 1ϵ的概率采用动作价值最大的那个动作,另外有 ϵ \epsilon ϵ的概率从动作空间中随机采取一个动作
    在这里插入图片描述
    ϵ \epsilon ϵ介于0到1,公式表示的是采样概率, ϵ \epsilon ϵ-贪婪策略:有 1 − ϵ 1- \epsilon 1ϵ的概率采用动作价值最大的那个动作,另外有 ϵ \epsilon ϵ的概率从动作空间中随机采取一个动作
    公式解读:当动作 a a a是当前状态 s s s下动作价值最大的动作时,采取该动作对应的概率是 1 − ϵ + ϵ A 1-\epsilon+ \frac{\epsilon}{A} 1ϵ+Aϵ
    1 − ϵ 1- \epsilon 1ϵ表示以 1 − ϵ 1- \epsilon 1ϵ的概率采取最优动作, ϵ A \frac{\epsilon}{A} Aϵ表示有 ϵ A \frac{\epsilon}{A} Aϵ的概率随机选择到最优动作

    对于其他所有动作,采取每个动作的概率是 ϵ A \frac{\epsilon}{A} Aϵ即使这些动作当前不是最优的,智能体也会以一定的概率随机选择它们
  • 可以得到一个实际的基于时序差分方法的强化学习算法。这个算法被称为 Sarsa,因为它的动作价值更新用到了当前状态 s s s、当前动作 a a a、获得的奖励 r r r、下一个状态 s ′ s' s和下一个动作 a ′ a' a,将这些符号拼接后就得到了算法名称。Sarsa 的具体算法如下:
    在这里插入图片描述
  • 新版的悬崖漫步环境的代码
    这份环境代码和第 4 章中的不一样,因为此时环境不需要提供奖励函数和状态转移函数,而需要提供一个和智能体进行交互的函数step(),该函数将智能体的动作作为输入,输出奖励和下一个状态给智能体
import matplotlib.pyplot as plt
import numpy as np
from tqdm import tqdm  # tqdm是显示循环进度条的库


class CliffWalkingEnv:
    def __init__(self, ncol, nrow):
        self.nrow = nrow
        self.ncol = ncol
        self.x = 0  # 记录当前智能体位置的横坐标
        self.y = self.nrow - 1  # 记录当前智能体位置的纵坐标

    def step(self, action):  # 外部调用这个函数来改变当前位置
        # 4种动作, change[0]:上, change[1]:下, change[2]:左, change[3]:右。坐标系原点(0,0)
        # 定义在左上角
        change = [[0, -1], [0, 1], [-1, 0], [1, 0]]
        self.x = min(self.ncol - 1, max(0, self.x + change[action][0]))
        self.y = min(self.nrow - 1, max(0, self.y + change[action][1]))
        next_state = self.y * self.ncol + self.x
        reward = -1
        done = False
        if self.y == self.nrow - 1 and self.x > 0:  # 下一个位置在悬崖或者目标
            done = True
            if self.x != self.ncol - 1:
                reward = -100
        return next_state, reward, done

    def reset(self):  # 回归初始状态,坐标轴原点在左上角
        self.x = 0
        self.y = self.nrow - 1
        return self.y * self.ncol + self.x
  • 实现 Sarsa 算法,主要维护一个表格Q_table(),用来储存当前策略下所有状态动作对的价值
class Sarsa:
    """ Sarsa算法 """
    def __init__(self, ncol, nrow, epsilon, alpha, gamma, n_action=4):
        self.Q_table = np.zeros([nrow * ncol, n_action])  # 初始化Q(s,a)表格 用来储存当前策略下所有状态动作对的价值
        self.n_action = n_action  # 动作个数
        self.alpha = alpha  # 学习率
        self.gamma = gamma  # 折扣因子
        self.epsilon = epsilon  # epsilon-贪婪策略中的参数

    def take_action(self, state):  # 选取下一步的操作,具体实现为epsilon-贪婪
        if np.random.random() < self.epsilon:
            action = np.random.randint(self.n_action)
        else:
            action = np.argmax(self.Q_table[state])
        return action

    def best_action(self, state):  # 用于打印策略
        Q_max = np.max(self.Q_table[state])
        a = [0 for _ in range(self.n_action)]
        for i in range(self.n_action):  # 若两个动作的价值一样,都会记录下来
            if self.Q_table[state, i] == Q_max:
                a[i] = 1
        return a

    def update(self, s0, a0, r, s1, a1):
        td_error = r + self.gamma * self.Q_table[s1, a1] - self.Q_table[s0, a0]
        self.Q_table[s0, a0] += self.alpha * td_error
  • 运行 Sarsa 算法
ncol = 12
nrow = 4
env = CliffWalkingEnv(ncol, nrow)
np.random.seed(0)
epsilon = 0.1
alpha = 0.1
gamma = 0.9
agent = Sarsa(ncol, nrow, epsilon, alpha, gamma)
num_episodes = 500  # 智能体在环境中运行的序列的数量

return_list = []  # 记录每一条序列的回报
for i in range(10):  # 显示10个进度条
    # tqdm的进度条功能
    with tqdm(total=int(num_episodes / 10), desc='Iteration %d' % i) as pbar:
        for i_episode in range(int(num_episodes / 10)):  # 每个进度条的序列数
            episode_return = 0
            state = env.reset()
            action = agent.take_action(state)
            done = False
            while not done:
                next_state, reward, done = env.step(action)
                next_action = agent.take_action(next_state)
                episode_return += reward  # 这里回报的计算不进行折扣因子衰减
                agent.update(state, action, reward, next_state, next_action)
                state = next_state
                action = next_action
            return_list.append(episode_return)
            if (i_episode + 1) % 10 == 0:  # 每10条序列打印一下这10条序列的平均回报
                pbar.set_postfix({
                    'episode':
                    '%d' % (num_episodes / 10 * i + i_episode + 1),
                    'return':
                    '%.3f' % np.mean(return_list[-10:])
                })
            pbar.update(1)

episodes_list = list(range(len(return_list)))
plt.plot(episodes_list, return_list)
plt.xlabel('Episodes')
plt.ylabel('Returns')
plt.title('Sarsa on {}'.format('Cliff Walking'))
plt.show()

在这里插入图片描述

  • 可视化Sarsa策略
def print_agent(agent, env, action_meaning, disaster=[], end=[]):
    for i in range(env.nrow):
        for j in range(env.ncol):
            if (i * env.ncol + j) in disaster:
                print('****', end=' ')
            elif (i * env.ncol + j) in end:
                print('EEEE', end=' ')
            else:
                a = agent.best_action(i * env.ncol + j)
                pi_str = ''
                for k in range(len(action_meaning)):
                    pi_str += action_meaning[k] if a[k] > 0 else 'o'
                print(pi_str, end=' ')
        print()


action_meaning = ['^', 'v', '<', '>']
print('Sarsa算法最终收敛得到的策略为:')
print_agent(agent, env, action_meaning, list(range(37, 47)), [47])

在这里插入图片描述

多步Sarsa算法

  • 蒙特卡洛方法利用当前状态之后每一步的奖励而不使用任何价值估计,时序差分算法只利用一步奖励和下一个状态的价值估计

  • 蒙特卡洛方法和时序差分算法之间的区别:总的来说,蒙特卡洛方法是无偏(unbiased)的,但是具有比较大的方差,因为每一步的状态转移都有不确定性,而每一步状态采取的动作所得到的不一样的奖励最终都会加起来,这会极大影响最终的价值估计;

  • 时序差分算法具有非常小的方差,因为只关注了一步状态转移,用到了一步的奖励,但是它是有偏的,因为用到了下一个状态的价值估计而不是其真实的价值。

  • 什么方法可以结合二者的优势呢?答案是多步时序差分!多步时序差分的意思是使用步的奖励,然后使用之后状态的价值估计。

  • G t = r t + γ Q ( s t + 1 , a t + 1 ) G_t = r_t+\gamma Q(s_{t+1},a_{t+1}) Gt=rt+γQ(st+1,at+1)替换为 G t = r t + γ r t + 1 + … + γ n Q ( s t + n , a t + n ) G_t = r_t+ \gamma r_{t+1}+ \ldots+\gamma^n Q(s_{t+n},a_{t+n}) Gt=rt+γrt+1++γnQ(st+n,at+n)
    单步 S a r s a : Q ( s t , a t ) ← Q ( s t , a t ) + α [ r t + γ Q ( s t + 1 , a t + 1 ) − Q ( s t , a t ) ] 多步 S a r s a : Q ( s t , a t ) ← Q ( s t , a t ) + α [ r t + γ r t + 1 + … + γ n Q ( s t + n , a t + n ) − Q ( s t , a t ) ] \begin{aligned} 单步Sarsa:Q(s_t,a_t) &\larr Q(s_t,a_t) + \alpha[r_t + \gamma Q(s_{t+1},a_{t+1}) - Q(s_t,a_t)] \\ 多步Sarsa:Q(s_t,a_t) &\larr Q(s_t,a_t) + \alpha[r_t +\gamma r_{t+1}+\ldots + \gamma^n Q(s_{t+n},a_{t+n}) - Q(s_t,a_t)] \end{aligned} 单步SarsaQ(st,at)多步SarsaQ(st,at)Q(st,at)+α[rt+γQ(st+1,at+1)Q(st,at)]Q(st,at)+α[rt+γrt+1++γnQ(st+n,at+n)Q(st,at)]

  • 多步Sarsa算法

class nstep_Sarsa:
    """ n步Sarsa算法 """
    def __init__(self, n, ncol, nrow, epsilon, alpha, gamma, n_action=4):
        self.Q_table = np.zeros([nrow * ncol, n_action])
        self.n_action = n_action
        self.alpha = alpha
        self.gamma = gamma
        self.epsilon = epsilon
        self.n = n  # 采用n步Sarsa算法
        self.state_list = []  # 保存之前的状态
        self.action_list = []  # 保存之前的动作
        self.reward_list = []  # 保存之前的奖励

    def take_action(self, state):
        if np.random.random() < self.epsilon:
            action = np.random.randint(self.n_action)
        else:
            action = np.argmax(self.Q_table[state])
        return action

    def best_action(self, state):  # 用于打印策略
        Q_max = np.max(self.Q_table[state])
        a = [0 for _ in range(self.n_action)]
        for i in range(self.n_action):
            if self.Q_table[state, i] == Q_max:
                a[i] = 1
        return a

    def update(self, s0, a0, r, s1, a1, done):
        self.state_list.append(s0)
        self.action_list.append(a0)
        self.reward_list.append(r)
        if len(self.state_list) == self.n:  # 若保存的数据可以进行n步更新
            G = self.Q_table[s1, a1]  # 得到Q(s_{t+n}, a_{t+n})
            for i in reversed(range(self.n)):
                G = self.gamma * G + self.reward_list[i]  # 不断向前计算每一步的回报
                # 如果到达终止状态,最后几步虽然长度不够n步,也将其进行更新
                if done and i > 0:
                    s = self.state_list[i]
                    a = self.action_list[i]
                    self.Q_table[s, a] += self.alpha * (G - self.Q_table[s, a])
            s = self.state_list.pop(0)  # 将需要更新的状态动作从列表中删除,下次不必更新
            a = self.action_list.pop(0)
            self.reward_list.pop(0)
            # n步Sarsa的主要更新步骤
            self.Q_table[s, a] += self.alpha * (G - self.Q_table[s, a])
        if done:  # 如果到达终止状态,即将开始下一条序列,则将列表全清空
            self.state_list = []
            self.action_list = []
            self.reward_list = []
np.random.seed(0)
n_step = 5  # 5步Sarsa算法
alpha = 0.1
epsilon = 0.1
gamma = 0.9
agent = nstep_Sarsa(n_step, ncol, nrow, epsilon, alpha, gamma)
num_episodes = 500  # 智能体在环境中运行的序列的数量

return_list = []  # 记录每一条序列的回报
for i in range(10):  # 显示10个进度条
    #tqdm的进度条功能
    with tqdm(total=int(num_episodes / 10), desc='Iteration %d' % i) as pbar:
        for i_episode in range(int(num_episodes / 10)):  # 每个进度条的序列数
            episode_return = 0
            state = env.reset()
            action = agent.take_action(state)
            done = False
            while not done:
                next_state, reward, done = env.step(action)
                next_action = agent.take_action(next_state)
                episode_return += reward  # 这里回报的计算不进行折扣因子衰减
                agent.update(state, action, reward, next_state, next_action,
                             done)
                state = next_state
                action = next_action
            return_list.append(episode_return)
            if (i_episode + 1) % 10 == 0:  # 每10条序列打印一下这10条序列的平均回报
                pbar.set_postfix({
                    'episode':
                    '%d' % (num_episodes / 10 * i + i_episode + 1),
                    'return':
                    '%.3f' % np.mean(return_list[-10:])
                })
            pbar.update(1)

episodes_list = list(range(len(return_list)))
plt.plot(episodes_list, return_list)
plt.xlabel('Episodes')
plt.ylabel('Returns')
plt.title('5-step Sarsa on {}'.format('Cliff Walking'))
plt.show()

在这里插入图片描述

action_meaning = ['^', 'v', '<', '>']
print('5步Sarsa算法最终收敛得到的策略为:')
print_agent(agent, env, action_meaning, list(range(37, 47)), [47])

在这里插入图片描述

Q-learning算法

  • 除了 Sarsa,还有一种非常著名的基于时序差分算法的强化学习算法——Q-learning
  • Q-learning 和 Sarsa 的最大区别在于 Q-learning 的时序差分更新方式为
    Q ( s t , a t ) ← Q ( s t , a t ) + α [ r t + γ max ⁡ a Q ( s t + 1 , a ) − Q ( s t , a t ) ] \begin{aligned} Q(s_t,a_t) &\larr Q(s_t,a_t) + \alpha[r_t + \gamma \max_{a} Q(s_{t+1},a) - Q(s_t,a_t)] \end{aligned} Q(st,at)Q(st,at)+α[rt+γamaxQ(st+1,a)Q(st,at)]
    在这里插入图片描述
  • Sarsa 估计当前 ϵ \epsilon ϵ-贪婪策略的动作价值函数
  • Q-learning 的更新并非必须使用当前贪心策略 a r g max ⁡ a Q ( s , a ) arg\max_a Q(s,a) argmaxaQ(s,a)采样得到的数据
  • 给定任意 ( s , a , r , s ′ ) (s,a,r,s') (s,a,r,s)都可以直接根据更新公式来更新 Q Q Q
  • 为了探索,通常使用一个 ϵ \epsilon ϵ-贪婪策略来与环境交互。Sarsa 必须使用当前 ϵ \epsilon ϵ–贪婪策略采样得到的数据,因为它的更新中用到的的是当前策略在 s ′ s' s下的动作。称 Sarsa 是在线策略(on-policy)算法,称 Q-learning 是离线策略(off-policy)算法
  • Q-learning在更新过程中总是选择当前状态下的最大动作价值对应的动作,而不考虑当前策略是如何选择动作的

在这里插入图片描述

在线策略学习和离线策略学习

  • 在线策略学习要求使用在当前策略下采样得到的样本进行学习,一旦策略被更新,当前的样本就被放弃了,就好像在水龙头下用自来水洗手
  • 离线策略学习使用经验回放池将之前采样得到的样本收集起来再次利用,就好像使用脸盆接水后洗手。因此,离线策略学习往往能够更好地利用历史数据,并具有更小的样本复杂度(算法达到收敛结果需要在环境中采样的样本数量),这使其被更广泛地应用
  • 称采样数据的策略为行为策略(behavior policy),称用这些数据来更新的策略为目标策略(target policy)
  • 在线策略(on-policy)算法表示行为策略和目标策略是同一个策略;而离线策略(off-policy)算法表示行为策略和目标策略不是同一个策略
  • Sarsa 是典型的在线策略算法,而 Q-learning 是典型的离线策略算法
  • 判断二者类别的一个重要手段是看计算时序差分的价值目标的数据是否来自当前的策略
  • 对于 Sarsa,它的更新公式必须使用来自当前策略采样得到的五元组 ( s , a , r , s ′ , a ′ ) (s,a,r,s',a') (s,a,r,s,a),因此它是在线策略学习方法
  • 对于 Q-learning,它的更新公式使用的是四元组 ( s , a , r , s ′ ) (s,a,r,s') (s,a,r,s)来更新当前状态动作对的价值 Q ( s , a ) Q(s,a) Q(s,a),数据中的 s 和 a s和a sa是给定的条件, r 和 s ′ r和s' rs皆由环境采样得到,该四元组并不需要一定是当前策略采样得到的数据,也可以来自行为策略,因此它是离线策略算法
    在这里插入图片描述
  • 离线策略算法能够重复使用过往训练样本,往往具有更小的样本复杂度,也因此更受欢迎
  • 当环境是有限状态集合和有限动作集合时,Sarsa和Q-learning十分适用
  • 尽管离线策略学习可以让智能体基于经验回放池中的样本来学习,但需要保证智能体在学习的过程中可以不断和环境进行交互,将采样得到的最新的经验样本加入经验回放池中,从而使经验回放池中有一定数量的样本和当前智能体策略对应的数据分布保持很近的距离。
  • 如果不允许智能体在学习过程中和环境进行持续交互,而是完全基于一个给定的样本集来直接训练一个策略,这样的学习范式被称为离线强化学习(offline reinforcement learning)
class QLearning:
    """ Q-learning算法 """
    def __init__(self, ncol, nrow, epsilon, alpha, gamma, n_action=4):
        self.Q_table = np.zeros([nrow * ncol, n_action])  # 初始化Q(s,a)表格
        self.n_action = n_action  # 动作个数
        self.alpha = alpha  # 学习率
        self.gamma = gamma  # 折扣因子
        self.epsilon = epsilon  # epsilon-贪婪策略中的参数

    def take_action(self, state):  #选取下一步的操作
        if np.random.random() < self.epsilon:
            action = np.random.randint(self.n_action)
        else:
            action = np.argmax(self.Q_table[state])
        return action

    def best_action(self, state):  # 用于打印策略
        Q_max = np.max(self.Q_table[state])
        a = [0 for _ in range(self.n_action)]
        for i in range(self.n_action):
            if self.Q_table[state, i] == Q_max:
                a[i] = 1
        return a

    def update(self, s0, a0, r, s1):
        td_error = r + self.gamma * self.Q_table[s1].max(
        ) - self.Q_table[s0, a0]
        self.Q_table[s0, a0] += self.alpha * td_error
np.random.seed(0)
epsilon = 0.1
alpha = 0.1
gamma = 0.9
agent = QLearning(ncol, nrow, epsilon, alpha, gamma)
num_episodes = 500  # 智能体在环境中运行的序列的数量

return_list = []  # 记录每一条序列的回报
for i in range(10):  # 显示10个进度条
    # tqdm的进度条功能
    with tqdm(total=int(num_episodes / 10), desc='Iteration %d' % i) as pbar:
        for i_episode in range(int(num_episodes / 10)):  # 每个进度条的序列数
            episode_return = 0
            state = env.reset()
            done = False
            while not done:
                action = agent.take_action(state)
                next_state, reward, done = env.step(action)
                episode_return += reward  # 这里回报的计算不进行折扣因子衰减
                agent.update(state, action, reward, next_state)
                state = next_state
            return_list.append(episode_return)
            if (i_episode + 1) % 10 == 0:  # 每10条序列打印一下这10条序列的平均回报
                pbar.set_postfix({
                    'episode':
                    '%d' % (num_episodes / 10 * i + i_episode + 1),
                    'return':
                    '%.3f' % np.mean(return_list[-10:])
                })
            pbar.update(1)

episodes_list = list(range(len(return_list)))
plt.plot(episodes_list, return_list)
plt.xlabel('Episodes')
plt.ylabel('Returns')
plt.title('Q-learning on {}'.format('Cliff Walking'))
plt.show()

action_meaning = ['^', 'v', '<', '>']
print('Q-learning算法最终收敛得到的策略为:')
print_agent(agent, env, action_meaning, list(range(37, 47)), [47])

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

  • 29
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Q-learning和SARSA都属于时序差分强化学习方法,而不是蒙特卡洛强化学习方法。 时序差分强化学习是一种结合了动态规划和蒙特卡洛方法的强化学习方法。它通过使用经验数据进行增量式的更新,同时利用了当前和未来的估计值来逼近最优值函数。 具体来说,Q-learning和SARSA都是基于Q值函数的时序差分强化学习算法。 1. Q-learning:Q-learning是一种基于动态规划的无模型强化学习算法。它使用了时序差分(TD)方法,通过不断迭代更新Q值函数的估计值,使其逼近最优的Q值。Q-learning算法通过将当前状态和动作的估计值与下一个状态和动作的最大估计值相结合,来更新Q值函数的估计值。 2. SARSA:SARSA是一种基于时序差分强化学习算法,也是一种模型-free的强化学习算法。SARSA算法使用了时序差分的方法,通过不断迭代更新Q值函数的估计值。与Q-learning不同的是,SARSA算法采用了一个策略(Policy)来决定下一个动作,并在更新Q值时使用下一个动作的估计值。 时序差分强化学习方法与蒙特卡洛强化学习方法相比,具有更高的效率和更好的适应性。它可以在每个时间步骤中进行更新,不需要等到任务结束后才进行更新,从而更快地收敛到最优策略。而蒙特卡洛强化学习方法则需要等到任务结束后才能获取完整的回报信息,进行全局更新。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

几度热忱

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值