当前位置: 代码迷 >> 综合 >> ADPRL - 近似动态规划和强化学习 - Note 10 - 蒙特卡洛法和时序差分学习及其实例 (Monte Carlo and Temporal Difference)
  详细解决方案

ADPRL - 近似动态规划和强化学习 - Note 10 - 蒙特卡洛法和时序差分学习及其实例 (Monte Carlo and Temporal Difference)

热度:63   发布时间:2023-11-02 01:34:59.0

Note 10 蒙特卡洛法和时序差分学习 Monte Carlo and Temporal Difference

蒙特卡洛法和时序差分学习

  • Note 10 蒙特卡洛法和时序差分学习 Monte Carlo and Temporal Difference
    • 10.1 蒙特卡洛法和时序差分学习 (Monte Carlo and Temporal Difference)
    • 10.2 Q中的TD学习(TD Learning in QQQ
    • 10.3 资格迹(Eligibility trace)
    • 10.4 实例:网格世界中的蒙特卡洛策略评估和TD(0)\mathrm{TD}(0)TD(0)算法
      • 10.4.1 环境搭建
      • 10.4.2 Task 1: DP 算法
      • 10.4.3 Task 2: 蒙特卡洛MC算法
      • 10.4.4 Task 3: 时序查分算法TD(0)
      • 10.4.5 SARSA 与Q-learning

前面几章重点讨论了解决DP中的维度诅咒问题,并强假设系统动态,即MDP模型中的状态转换概率,对智能体来说是可以完全访问的。不幸的是,这样的假设在大多数工程应用中是不现实的。因此,这种情况是RL\mathrm{RL}RL的主要焦点,有时被称为DP中的模型诅咒(curse of model)。

本章研究了可以说是最基本的无模型RL算法,即时序差分(Temporal Difference, TD)学习。具体来说,我们首先在第10.1节中重温了TD学习的传统启发式观点,在第10.2节中扩展了TD的概念以发展著名的SARSASARSASARSAQQQ学习算法,并在第10.3节中发展了资格追踪的概念以提高TD算法的计算效率。

10.1 蒙特卡洛法和时序差分学习 (Monte Carlo and Temporal Difference)

时差的概念一直是研究和应用RL的中心。它的早期成功可以在1959年的计算机检查程序中找到。经过几十年的发展,它的理论开始趋于一致,并由Richard Sutton在1980年代在计算机科学的背景下提出。尽管TD学习的传统发展从学习的行为理论中得到了强烈的启发,但本节的重点是为这个概念提供一个直观的、但技术上简洁的介绍。

让我们从有限范围问题开始,回顾一下给定非确定性策略的有限范围问题的总成本函数的定义(在RL中被称为回报函数,或者奖励函数)。我们将一条轨迹的所有成本之和定义为

Gπ(x0):=gN(xN)+∑k=0N?1gk(xk,uk,xk+1)(10.1)G_{\pi}\left(x_{0}\right):=g_{N}\left(x_{N}\right)+\sum_{k=0}^{N-1} g_{k}\left(x_{k}, u_{k}, x_{k+1}\right) \tag{10.1}Gπ?(x0?):=gN?(xN?)+k=0N?1?gk?(xk?,uk?,xk+1?)(10.1)

这可以被视为一个随机变量。那么总成本函数可以重写为

Jπ(x0):=Epπ(χ,μ)[Gπ(x0)](10.2)J^{\pi}\left(x_{0}\right):=\mathbb{E}_{p_{\pi}(\chi, \mu)}\left[G_{\pi}\left(x_{0}\right)\right] \tag{10.2}Jπ(x0?):=Epπ?(χ,μ)?[Gπ?(x0?)](10.2)
当概率密度函数pπ(χ,μ)p_{\pi}(\chi, \mu)pπ?(χ,μ)没有给出时,一个实用的解决方案是采用蒙特卡洛方法。也就是说, 公式(10.2)中的期望值可以通过样本的经验平均值来近似为
Jπ(x0)≈J^π(x0):=1N∑i=1NGπ(i)(x0)(10.3)J^{\pi}\left(x_{0}\right) \approx \widehat{J}^{\pi}\left(x_{0}\right):=\frac{1}{N} \sum_{i=1}^{N} G_{\pi}^{(i)}\left(x_{0}\right) \tag{10.3}Jπ(x0?)J π(x0?):=N1?i=1N?Gπ(i)?(x0?)(10.3)

这里,上标(i)(i)(i)指的是采样轨迹的数量。根据大数定律,对期望值的准确估计需要样本的数量变得足够大。显然,当范围NNN很大时,计算这样一个算术平均值会变得非常昂贵。因此,实际要求有一个在线更新,这样经验平均数可以以增量方式有效计算。

大数定理:
{xi}i=1∞\left\{x_{i}\right\}_{i=1}^{\infty}{ xi?}i=1?是一串独立同分布的随机变量,从而 E[xi]=μ\mathbb{E}\left[x_{i}\right]=\muE[xi?]=μ and Var?(xi)<∞\operatorname{Var}\left(x_{i}\right)<\inftyVar(xi?)< , 那么几乎可以肯定1n(x1+…+xn)→μ,when n→∞\frac{1}{n}\left(x_{1}+\ldots+x_{n}\right) \rightarrow \mu, \quad \text { when } n \rightarrow \inftyn1?(x1?++xn?)μ, when n

蒙特卡洛方法:
任何使用随机性和大数法则来计算某个数量的方法可能与随机性无关的方法都是蒙特卡洛方法。

为了开发这个技巧,我们用下面这个简单的例子来说明。假设x∈Rx\in \mathbb{R}xR是一个随机变量,具有固定分布p(x)p(x)p(x)xix_{i}xi?是从p(x)p(x)p(x)中抽取的独立同分布的样本。那么,nnn样本的经验平均数可计算为以下算术平均数

x^n=1n∑i=1nxi(10.4)\widehat{x}_{n}=\frac{1}{n} \sum_{i=1}^{n} x_{i} \tag{10.4}x n?=n1?i=1n?xi?(10.4)

给定一个新的样本xn+1x_{n+1}xn+1?n+1n+1n+1样本的经验平均值可以进一步重新计算为
x^n+1=1n+1∑i=1n+1xi=1n+1(∑i=1nxi+xn+1)=1n+1(nx^n+x^n+xn+1?x^n)=x^n+1n+1(xn+1?x^n).(10.5)\begin{aligned} \widehat{x}_{n+1} &=\frac{1}{n+1} \sum_{i=1}^{n+1} x_{i} \\ &=\frac{1}{n+1}\left(\sum_{i=1}^{n} x_{i}+x_{n+1}\right) \\ &=\frac{1}{n+1}\left(n \widehat{x}_{n}+\widehat{x}_{n}+x_{n+1}-\widehat{x}_{n}\right) \\ &=\widehat{x}_{n}+\frac{1}{n+1}\left(x_{n+1}-\widehat{x}_{n}\right) . \end{aligned} \tag{10.5}x n+1??=n+11?i=1n+1?xi?=n+11?(i=1n?xi?+xn+1?)=n+11?(nx n?+x n?+xn+1??x n?)=x n?+n+11?(xn+1??x n?).?(10.5)

显然,n+1n+1n+1样本的经验平均数x^n+1\widehat{x}_{n+1}x n+1?可以简单地利用之前对nnn样本的估计x^n\widehat{x}_{n}x n?和新的观测值xn+1x_{n+1}xn+1?来更新。因此,对于有限范围问题,总成本函数JπJ^{\pi}Jπ可以通过应用以下更新规则来在线估计
Jk+1(x0)=Jk(x0)+1k(Gπ(k+1)(x0)?Jk(x0))(10.6)J_{k+1}\left(x_{0}\right)=J_{k}\left(x_{0}\right)+\frac{1}{k}\left(G_{\pi}^{(k+1)}\left(x_{0}\right)-J_{k}\left(x_{0}\right)\right) \tag{10.6}Jk+1?(x0?)=Jk?(x0?)+k1?(Gπ(k+1)?(x0?)?Jk?(x0?))(10.6)

其中指数kkk指的是以x0x_{0}x0?开始的样本轨迹的数量。这一更新规则被称为有限范围问题的蒙特卡洛策略评估(Monte Carlo policy evaluation) 算法。

显然,对于无限范围问题来说,对完整轨迹进行抽样的概念已经不可能了。然而,通过探索同样的技巧,我们提出了无限范围背景下的的TD学习算法,并略带启发式的思考方法。我们没有利用价值函数的定义,而是将每个单独状态的贝尔曼方程替换为

Jπ(x)=Epπ(x′∣x)[g(x,π(x),x′)+γJπ(x′)].(10.7)J^{\pi}(x)=\mathbb{E}_{p_{\pi}\left(x^{\prime} \mid x\right)}\left[g\left(x, \pi(x), x^{\prime}\right)+\gamma J^{\pi}\left(x^{\prime}\right)\right] . \tag{10.7}Jπ(x)=Epπ?(xx)?[g(x,π(x),x)+γJπ(x)].(10.7)

鉴于对总成本函数J∈RKJ \in \mathbb{R}^{K}JRK的估计,期望下的和g(x,u,x′)+γJ(x′)g\left(x, u, x^{\prime}\right)+\gamma J\left(x^{\prime}\right)g(x,u,x)+γJ(x)可以被类似地当作一个 “随机变量”。让我们用xxxx′x^{\prime}x分别表示一个初始状态和它的连续状态。然后,通过增量更新规则的相同技巧,可以得出以下更新规则
Jk+1(x)=Jk(x)+αk(g(x,u,x′)+γJk(x′)?Jk(x)),(10.8)J_{k+1}(x)=J_{k}(x)+\alpha_{k}\left(g\left(x, u, x^{\prime}\right)+\gamma J_{k}\left(x^{\prime}\right)-J_{k}(x)\right), \tag{10.8}Jk+1?(x)=Jk?(x)+αk?(g(x,u,x)+γJk?(x)?Jk?(x)),(10.8)

其中u=π(x)u=\pi(x)u=π(x), αk>0\alpha_{k}>0αk?>0被称为学习率。该更新规则被称为TD学习规则,详见算法11。

在这里插入图片描述

递增式更新
δ(x,u,x′):=g(x,u,x′)+γJk(x′)?Jk(x)(10.9)\delta\left(x, u, x^{\prime}\right):=g\left(x, u, x^{\prime}\right)+\gamma J_{k}\left(x^{\prime}\right)-J_{k}(x) \tag{10.9}δ(x,u,x):=g(x,u,x)+γJk?(x)?Jk?(x)(10.9)

通常被称为TD误差。

需要注意的是,公式(10.8)中定义的TD更新不是蒙特卡洛方法,因为总成本函数JkJ_{k}Jk?只是总成本函数JπJ^{\pi}Jπ的估计,因此g(x,u,x′)+γJk(x′)g\left(x, u, x^{\prime}\right)+\gamma J_{k}\left(x^{\prime}\right)g(x,u,x)+γJk?(x)不是某个随机变量的即定样本。换句话说,TD更新规则是在另一个估计的基础上产生一个估计。这种技巧在RL的背景下被称为自助法(bootstrapping )。作为续集,学习率通常被选择为αk∈(0,1)\alpha_{k}\in(0,1)αk?(0,1),以补偿自助法引起的潜在波动。更具体地说,学习率通常满足Robbins-Monro条件,这是适应学习率的最常见策略[33],即。
∑k=1∞αk=∞,and ∑k=1∞αk2<∞(10.10)\sum_{k=1}^{\infty} \alpha_{k}=\infty, \text { and } \sum_{k=1}^{\infty} \alpha_{k}^{2}<\infty \tag{10.10}k=1?αk?=, and k=1?αk2?<(10.10)
一个满足Robbins-Monro条件的学习率的简单例子是
αk=1k,for k∈N(10.11)\alpha_{k}=\frac{1}{k}, \quad \text { for } k \in \mathbb{N} \tag{10.11}αk?=k1?, for kN(10.11)

那么公式(10.10)中的第一个和成为谐波数列的和,已知是有界的,而公式(10.10)中的第二个和是指巴塞尔问题,已知是有界的。

图16描述了TD学习算法和蒙特卡洛策略评估算法之间的区别。

在这里插入图片描述
图16:蒙特卡洛和TD算法的比较。TD算法访问了所有的状态,并更新了过去访问过的所有状态,而Monte Carlo算法只在达到终端状态时才更新了所有的状态。

显然,TD学习算法更新了过去访问过的所有状态,而蒙特卡洛算法只更新了起始状态。尽管TD学习不是蒙特卡洛方法,但有趣的是注意到TD学习算法渐进地收敛于总成本函数JπJ^{\pi}Jπ。这种神秘的理论依据将在下一章提供。

10.2 Q中的TD学习(TD Learning in QQQ

TD(0)\mathrm{TD}(0)TD(0)算法的发展类似,两个对应的带有QQQ函数的TD学习算法也可以直接发展为
Qk+1(x,u)=Qk(x,u)+αk(g(x,u,x′)+γQk(x′,u′)?Qk(x,u)),(10.12)Q_{k+1}(x, u)=Q_{k}(x, u)+\alpha_{k}\left(g\left(x, u, x^{\prime}\right)+\gamma Q_{k}\left(x^{\prime}, u^{\prime}\right)-Q_{k}(x, u)\right), \tag{10.12}Qk+1?(x,u)=Qk?(x,u)+αk?(g(x,u,x)+γQk?(x,u)?Qk?(x,u)),(10.12)

其中u′=π(x′)u^{\prime}=\pi\left(x^{\prime}\right)u=π(x),并且
Qk+1(x,u)=Qk(x,u)+αk(g(x,u,x′)+γmin?u′Qk(x′,u′)?Qk(x,u)).(10.13)Q_{k+1}(x, u)=Q_{k}(x, u)+\alpha_{k}\left(g\left(x, u, x^{\prime}\right)+\gamma \min _{u^{\prime}} Q_{k}\left(x^{\prime}, u^{\prime}\right)-Q_{k}(x, u)\right) . \tag{10.13}Qk+1?(x,u)=Qk?(x,u)+αk?(g(x,u,x)+γumin?Qk?(x,u)?Qk?(x,u)).(10.13)

前者的更新规则被称为SARSAS A R S ASARSA算法,它执行策略π\piπ的策略评估,而后者被称为Q-学习算法(算法12),它计算最佳状态-行动总成本函数Q?Q^{*}Q?
在这里插入图片描述

下一章将提供关于这些TD学习算法的渐进收敛的更多讨论。

10.3 资格迹(Eligibility trace)

在第10.1节,我们提出了TD学习概念的启发式直觉。尽管算法简单,但原始的TD框架仍有一个主要弱点。

也就是说,当一个TD代理与它的环境互动时,智能体只更新相应状态的总成本函数。特别是,当状态的数量变得巨大时,由于维度的诅咒,TD算法的效率往往非常低。因此,需要一种实用的技术来更新智能体与其环境之间一次互动中多个状态的总成本函数。

资格迹的概念可以通过将第10.1节中开发TD算法的相同策略应用于mmm步贝尔曼算子来发展。让我们回顾一下公式(4.33)中的mmm步贝尔曼算子Tπm\mathrm{T}_{\pi}^{m}Tπm?,并用以下方式表示一个有限轨迹

hx0(m):={x0,u0,…,xm?1,um?1,xm}.(10.14)h_{x_{0}}^{(m)}:=\left\{x_{0}, u_{0}, \ldots, x_{m-1}, u_{m-1}, x_{m}\right\} . \tag{10.14}hx0?(m)?:={ x0?,u0?,,xm?1?,um?1?,xm?}.(10.14)

那么,mmm步贝尔曼算子的计算方法为

TπmJ(x0)=Epπ(h(m))[∑k=0m?1γkg(xk,uk,xk+1)+γmJ(xm)?=:Gπ(hx0(m))],(10.15)\mathrm{T}_{\pi}^{m} J\left(x_{0}\right)=\mathbb{E}_{p_{\pi}\left(h^{(m)}\right)}[\underbrace{\sum_{k=0}^{m-1} \gamma^{k} g\left(x_{k}, u_{k}, x_{k+1}\right)+\gamma^{m} J\left(x_{m}\right)}_{=: G_{\pi}\left(h_{x_{0}}^{(m)}\right)}], \tag{10.15}Tπm?J(x0?)=Epπ?(h(m))?[=:Gπ?(hx0?(m)?) k=0m?1?γkg(xk?,uk?,xk+1?)+γmJ(xm?)??],(10.15)

其中和Gπ(hx0(m))G_{\pi}\left(h_{x_{0}}^{(m)}\right)Gπ?(hx0?(m)?)在RL的背景下通常被称为mmm步的返回。这样就可以直接得到一个mmm步的TD学习算法,即
Jk+1(x0)=Jk(x0)+αk(Gπ(hx0(m))?Jk(x0))(10.16)J_{k+1}\left(x_{0}\right)=J_{k}\left(x_{0}\right)+\alpha_{k}\left(G_{\pi}\left(h_{x_{0}}^{(m)}\right)-J_{k}\left(x_{0}\right)\right) \tag{10.16}Jk+1?(x0?)=Jk?(x0?)+αk?(Gπ?(hx0?(m)?)?Jk?(x0?))(10.16)

很明显,这样的算法可能是相当低效的。也就是说,如果步长mmm很大,在交互过程中会有很长一段时间没有更新。现在让我们仔细看看mmm步TD更新或mmm步TD误差为

Gπ(hx0(m))?J(x0)=∑k=0m?1γkg(xk,uk,xk+1)+γmJ(xm)?J(x0)=∑k=0m?2γkg(xk,uk,xk+1)+γm?1(g(xm?1,um?1,xm)+γJ(xm))??γm?1J(xm?1)+γm?1J(xm?1)?J(x0)=∑k=0m?2γkg(xk,uk,xk+1)+γm?1J(xm?1)?J(x0)+γm?1δ(xm?1,um?1,xm)=∑k=0m?1γkδ(xk,uk,xk+1),(10.17)\begin{aligned} &G_{\pi}\left(h_{x_{0}}^{(m)}\right)-J\left(x_{0}\right)\\ &=\sum_{k=0}^{m-1} \gamma^{k} g\left(x_{k}, u_{k}, x_{k+1}\right)+\gamma^{m} J\left(x_{m}\right)-J\left(x_{0}\right)\\ &=\sum_{k=0}^{m-2} \gamma^{k} g\left(x_{k}, u_{k}, x_{k+1}\right)+\gamma^{m-1}\left(g\left(x_{m-1}, u_{m-1}, x_{m}\right)+\gamma J\left(x_{m}\right)\right)-\\ &-\gamma^{m-1} J\left(x_{m-1}\right)+\gamma^{m-1} J\left(x_{m-1}\right)-J\left(x_{0}\right)\\ &=\sum_{k=0}^{m-2} \gamma^{k} g\left(x_{k}, u_{k}, x_{k+1}\right)+\gamma^{m-1} J\left(x_{m-1}\right)-J\left(x_{0}\right)+\gamma^{m-1} \delta\left(x_{m-1}, u_{m-1}, x_{m}\right)\\ &=\sum_{k=0}^{m-1} \gamma^{k} \delta\left(x_{k}, u_{k}, x_{k+1}\right), \end{aligned}\tag{10.17} ?Gπ?(hx0?(m)?)?J(x0?)=k=0m?1?γkg(xk?,uk?,xk+1?)+γmJ(xm?)?J(x0?)=k=0m?2?γkg(xk?,uk?,xk+1?)+γm?1(g(xm?1?,um?1?,xm?)+γJ(xm?))??γm?1J(xm?1?)+γm?1J(xm?1?)?J(x0?)=k=0m?2?γkg(xk?,uk?,xk+1?)+γm?1J(xm?1?)?J(x0?)+γm?1δ(xm?1?,um?1?,xm?)=k=0m?1?γkδ(xk?,uk?,xk+1?),?(10.17)

其中mmm步TD更新可以被计算为随时间变化的折扣的TD误差之和。作为续篇,公式(10.16)(10.16)(10.16)中的mmm步TD学习更新可以重写为
J(x0)=J(x0)+α(∑k=0m?1γkδ(xk,uk,xk+1))(10.18)J\left(x_{0}\right)=J\left(x_{0}\right)+\alpha\left(\sum_{k=0}^{m-1} \gamma^{k} \delta\left(x_{k}, u_{k}, x_{k+1}\right)\right) \tag{10.18}J(x0?)=J(x0?)+α(k=0m?1?γkδ(xk?,uk?,xk+1?))(10.18)

其中mmm步的更新被表示为随时间变化的几何折扣的TD误差。让我们考虑一个x0,x1,x2,…,xmx_{0}, x_{1}, x_{2}, \ldots, x_{m}x0?,x1?,x2?,,xm?的历史。一个直接的实现将简单地执行更新,一旦达到mmm步。显然,这样的策略仍然是不切实际的。通过观察每个TD误差的大小取决于起始时间步骤和当前过渡之间的时间步骤,很容易构建一个所有状态随时间变化的标量值函数,称为资格迹(eligibility trace)

让我们初始化时间步长t=0t=0t=0,并设定et(x)=0e_{t}(x)=0et?(x)=0用于所有x∈Xx \in \mathcal{X}xX。在从xtx_{t}xt?xt+1x_{t+1}xt+1?的一次转换之后,所有的资格迹都被更新为
et(x):={γet?1(x),if x≠xt1,if x=xt(10.18)e_{t}(x):= \begin{cases}\gamma e_{t-1}(x), & \text { if } x \neq x_{t} \\ 1, & \text { if } x=x_{t}\end{cases} \tag{10.18}et?(x):={ γet?1?(x),1,? if x?=xt? if x=xt??(10.18)

这种策略通常被称为替换迹(replacing trace)。相应的算法通常被称为TD?(1)\operatorname{TD}(1)TD(1)算法。如果我们假设由交互作用引起的马尔科夫链是各态历经的,那么重复访问的状态的资格迹将被更频繁地重置。换句话说,当智能体和环境之间的互动继续进行时,TD(1)\mathrm{TD}(1)TD(1)算法会同时更新所有被访问状态的价值函数。

最后,这样的概念可以进一步扩展到公式(5.10)中定义的λ\lambdaλ-几何平均的贝尔曼算子。通过结合公式(10.17)中给出的mmm步TD误差和公式(5.10)中定义的λ\lambdaλ几何平均,在初始状态x0x_{0}x0?λ\lambdaλ几何平均TD更新可以被表示为
J(x0)=J(x0)+αk(∑k=0∞λk∑t=0kγtδ(xt,ut,xt+1))(10.19)J\left(x_{0}\right)=J\left(x_{0}\right)+\alpha_{k}\left(\sum_{k=0}^{\infty} \lambda^{k} \sum_{t=0}^{k} \gamma^{t} \delta\left(x_{t}, u_{t}, x_{t+1}\right)\right) \tag{10.19}J(x0?)=J(x0?)+αk?(k=0?λkt=0k?γtδ(xt?,ut?,xt+1?))(10.19)

更具体地说,让我们考虑{x0,u0,…,xm?2,um?2,xm?1}\left\{x_{0}, u_{0}, \ldots, x_{m-2}, u_{m-2}, x_{m-1}\right\}{ x0?,u0?,,xm?2?,um?2?,xm?1?}的历史。在(xm?1,um?1,xm)\left(x_{m-1}, u_{m-1}, x_{m}\right)(xm?1?,um?1?,xm?)的转换之后,总成本函数J(x0)J\left(x_{0}\right)J(x0?) 的估计得到λmγmδ(xm?1,um?1,xm)\lambda^{m} \gamma^{m} \delta\left(x_{m-1}, u_{m-1}, x_{m}\right)λmγmδ(xm?1?,um?1?,xm?)的更新, 作为从当前第mmm个过渡到初始状态x0x_{0}x0?的少部分贡献。因此,我们可以进一步引入一个额外的参数来调整得到的TD误差

et(x):={λγet?1(x),if x≠xt1,if x=xt(10.20)e_{t}(x):= \begin{cases}\lambda \gamma e_{t-1}(x), & \text { if } x \neq x_{t} \\ 1, & \text { if } x=x_{t}\end{cases} \tag{10.20}et?(x):={ λγet?1?(x),1,? if x?=xt? if x=xt??(10.20)

TD?(λ)\operatorname{TD}(\lambda)TD(λ) 算法在算法13中给出。
在这里插入图片描述

图17说明了TD (λ)(\lambda)(λ)更新的机制。
在这里插入图片描述

图17:TD?(λ)\operatorname{TD}(\lambda)TD(λ)更新的说明。让我们用δk:=\delta_{k}:=δk?:= δ(xk,uk,xk+1)\delta\left(x_{k}, u_{k}, x_{k+1}\right)δ(xk?,uk?,xk+1?)表示时间差。TD对过去相应状态的更新幅度取决于当前时间戳和要更新的状态之间的时间步骤。

10.4 实例:网格世界中的蒙特卡洛策略评估和TD(0)\mathrm{TD}(0)TD(0)算法

如下图所示,一个基准环境是一个有11个状态和一个障碍物的网格。智能体从左下角的 "开始 "状态出发,停在两个终端状态。
在这里插入图片描述

有四个可用的动作:上、下、左、右。每个动作都是随机的,一步的概率为0.8,两步的概率为0.2,都是期望的方向。所有转换的局部成本为0.04,终端状态的局部成本为1。

任务1:一旦给定了过渡概率,我们就可以像以前一样实现DP算法。对这个网格世界中所有状态的总成本实施价值迭代。

任务2:给定一个固定的策略,我们可以做MC策略评估。对所有状态的总成本实施MC PE。
任务3:给定一个固定策略,对所有状态的总成本实施TD (0)(0)(0)
任务4(奖励):实施SARSA和Q-learning。

字典(Dictionary) :keys() 函数以列表返回一个字典所有的键。

集合(set): 是一个无序的不重复元素序列。可以使用大括号 { } 或者 set() 函数创建集合,注意:创建一个空集合必须用 set() 而不是 { },因为 { } 是用来创建一个空字典。

reversed() 函数返回一个反转的迭代器。

reverse() 函数用于反向列表中元素。

10.4.1 环境搭建

首先对整个环境建立一个class。

class GridWorld: def __init__(self, width=4, height=3, obstacle=[(1,1)]):self.width = widthself.height = heightself.obstacle = obstacleself.terminal = [(0, width-1), (1, width-1)]  # the terminal states are always at right top# the current stateself.row = height - 1  # the start point is always at left bottomself.col = 0# define the MDPself.actions = self.act_space()self.states = set(self.actions.keys()) | set(self.terminal)self.J = self.init_J()self.local_cost = 0.04def act_space(self):""":return: generate all possible actions for each state, dictionary"""act_space = {
    }for row in range(self.height):for col in range(self.width):possible_acts = []if (row, col) not in self.obstacle + self.terminal:if row - 1 >= 0 and (row-1, col) not in self.obstacle:possible_acts.append('U')if row + 1 < self.height and (row+1, col) not in self.obstacle:possible_acts.append('D')if col - 1 >=0 and (row, col-1) not in self.obstacle:possible_acts.append('L')if col + 1 < self.width and (row, col+1) not in self.obstacle:possible_acts.append('R')act_space[(row, col)] = possible_actsreturn act_spacedef init_J(self, init_J_value=0):"""initialise the cost function:param init_J_value::return: cost in each state, dictionary"""J = {
    }for row in range(self.height):for col in range(self.width):if (row, col) not in self.obstacle + self.terminal:J[(row, col)] = init_J_valueJ[self.terminal[0]] = -1  # J(x_N) = g(x_N)J[self.terminal[1]] = +1return Jdef move(self, action, deterministic=False):"""return the current cost after the move:param action: actions:param deterministic: if the action is deterministic:return: current cost"""# check if legal move firstif action in self.actions[(self.row, self.col)]:if action == 'U':# probablistic transitionif deterministic or random.uniform(0,1) > 0.8 or (self.row-2, self.col) not in self.states:self.row -= 1else:self.row -= 2elif action == 'D':if deterministic or random.uniform(0,1) > 0.8 or (self.row+2, self.col) not in self.states:self.row += 1else:self.row += 2elif action == 'R':if deterministic or random.uniform(0,1) > 0.8 or (self.row, self.col+2) not in self.states:self.col += 1else:self.col += 2elif action == 'L':if deterministic or random.uniform(0,1) > 0.8 or (self.row, self.col-2) not in self.states:self.col -= 1else:self.col -= 2if (self.row, self.col) == self.terminal[0]:return -1elif (self.row, self.col) == self.terminal[1]:return +1else:return self.local_costdef set_state(self, s):self.row = s[0]self.col = s[1]def current_state(self):return (self.row, self.col)def game_over(self):  # MCreturn (self.row, self.col) not in self.actionsdef print_J(self):for row in range(self.height):print("---------------------------")for col in range(self.width):J = env.J.get((row, col), 0)if J >= 0:print(" %.2f|" % J, end="")else:print("%.2f|" % J, end="") print("")print("---------------------------")

10.4.2 Task 1: DP 算法

## %% Task 1: Value Iteration
env = GridWorld()
gamma = 0.9while True:infty_norm = 0for x in env.actions:  # update total costs for all the states except terminals# actions is a dict whose keys are states except terminalsold_J = env.J[x]J_dict = {
    }for u in env.actions[x]:  # find the optimal action for T_genv.set_state(x)g = env.move(u, deterministic=True)J_ = 0.8 * (g + gamma * env.J[env.current_state()])if env.current_state() not in env.terminal:  # considering the SDMif u in env.actions[env.current_state()]:g = env.move(u, deterministic=True)J_ += 0.2 * (g + gamma * env.J[env.current_state()])else:J_ += 0.2 * (g + gamma * env.J[env.current_state()])else:J_ += 0.2 * (g + gamma * env.J[env.current_state()])J_dict[u] = J_optimal_act = min(J_dict.keys(), key=(lambda k: J_dict[k]))  # optimal Bellman Eqenv.J[x] = J_dict[optimal_act]infty_norm = max(infty_norm, abs(old_J - env.J[x]))if infty_norm < 1e-3:breakprint("\nTotal cost by VI:")
env.print_J()

输出为:

Total cost by VI:
---------------------------
-1.54|-1.72|-1.90|-1.00|
---------------------------
-1.34| 0.00|-1.67| 1.00|
---------------------------
-1.20|-1.27|-1.50|-1.27|
---------------------------

10.4.3 Task 2: 蒙特卡洛MC算法

## Task 2: Monte Carlo Policy Evaluation
# %% Monte Carlo
def play_game(grid, policy):# start at random position to measure value for all states start_states = list(grid.actions.keys())start_idx = np.random.choice(len(start_states))grid.set_state(start_states[start_idx])# generate the trajectroyx = grid.current_state()traj = [(x, 0)]while not grid.game_over():u = policy[x]g = grid.move(u)x = grid.current_state()traj.append((x, g))  # save the trajectory# calculate G_pi for each states along the trajectoryG = 0G_x = []first = Truefor x, g in reversed(traj):if first:first = Falseelse:G_x.append((x, G))G = g + gamma * GG_x.reverse()return G_xenv = GridWorld()policy = {
    (2, 0): 'U',(1, 0): 'U',(0, 0): 'R',(0, 1): 'R',(0, 2): 'R',(1, 2): 'R',(2, 1): 'R',(2, 2): 'R',(2, 3): 'U',
}# Under a fixed policy, the trajectory can be easily generated. Then calculate the expected cost for each state.
# set a dictionary to store the G of each state
G_pi_x = {
    }
for x in env.actions:G_pi_x[x] = []for n in range(1, 200):G_pi_history = play_game(env, policy)for s, G in G_pi_history:G_pi_x[s].append(G)env.J[s] = np.mean(G_pi_x[s])print("\nTotal cost by MC:")
env.print_J()

输出为

Total cost by MC:
---------------------------
-0.86|-0.98|-1.00|-1.00|
---------------------------
-0.73| 0.00| 1.00| 1.00|
---------------------------
-0.70|-0.53|-0.67|-0.73|
---------------------------

10.4.4 Task 3: 时序查分算法TD(0)

## task 3 : TD(0) algorithm
# %% TD
def play_game(grid, policy):start_states = list(grid.actions.keys())start_idx = np.random.choice(len(start_states))grid.set_state(start_states[start_idx])# generate trajx = grid.current_state()traj = [(x,0)]while not grid.game_over():u = policy[x]g = grid.move(u)x = grid.current_state()traj.append((x, g))  # save the trajectoryreturn trajgamma = 0.9
alpha = 0.1policy = {
    (2, 0): 'U',(1, 0): 'U',(0, 0): 'R',(0, 1): 'R',(0, 2): 'R',(1, 2): 'R',(2, 1): 'R',(2, 2): 'R',(2, 3): 'U',
}env = GridWorld()
for _ in range(200):traj_all = play_game(env, policy)for idx in range(len(traj_all) - 1):x, _ = traj_all[idx]x_, g = traj_all[idx+1]env.J[x] = env.J[x] + alpha * (g + gamma * env.J[x_] - env.J[x])print ("\nTotal cost by TD")
env.print_J()

输出为

Total cost by TD
---------------------------
-1.63|-1.80|-1.90|-1.00|
---------------------------
-1.17| 0.00| 1.75| 1.00|
---------------------------
-0.86|-0.86|-0.85|-1.31|
---------------------------

10.4.5 SARSA 与Q-learning

待补。

  相关解决方案