Model_free强化学习:
文章目录
1. 蒙特卡罗法
-
强化学习预测问题:求解该策略的状态价值函数v(π)
-
强化学习控制问题:也就是求解最优的价值函数v(π*)和策略π*。
蒙特卡罗法通过采样若干经历完整的状态序列(episode)来估计状态的真实价值。所谓的经历完整,就是这个序列必须是达到终点的。比如下棋问题分出输赢,驾车问题成功到达终点或者失败。有了很多组这样经历完整的状态序列,我们就可以来近似的估计状态价值,进而求解预测和控制问题了。即回合更新算法
一个给定策略π的完整有T个状态的状态序列如下:
S
1
,
A
1
,
R
2
,
S
2
,
A
2
,
.
.
.
,
S
t
−
1
,
A
t
−
1
,
R
t
,
S
t
v
π
(
s
)
=
E
π
(
G
t
∣
S
t
=
s
)
=
E
π
(
R
t
+
1
+
γ
R
t
+
2
+
γ
2
R
t
+
3
+
.
.
.
∣
S
t
=
s
)
S1, A1, R2, S2, A2, ...,St-1,At-1,Rt,St\\ v_{\pi}(s)=E_{\pi}(G_{t}|S_{t}=s)=E_{\pi}(R_{t+1}+\gamma R_{t+2}+\gamma ^{2}R_{t+3}+...|S_{t}=s)
S1,A1,R2,S2,A2,...,St−1,At−1,Rt,Stvπ(s)=Eπ(Gt∣St=s)=Eπ(Rt+1+γRt+2+γ2Rt+3+...∣St=s)
可以看出每个状态的价值函数等于所有该状态收获的期望,同时这个回报G是通过后续的奖励与对应的衰减乘积求和得到。那么对于蒙特卡罗法来说,如果要求某一个状态的状态价值,只需要求出所有的完整序列中该状态出现时候的收获再取平均值即可近似求解,也就是:
G
t
=
R
t
+
1
+
γ
R
t
+
2
+
γ
2
R
t
+
3
+
.
.
.
+
γ
T
−
t
−
1
R
T
G_{t}=R_{t+1}+\gamma R_{t+2}+\gamma ^{2}R_{t+3}+...+\gamma ^{T-t-1}R_{T}
Gt=Rt+1+γRt+2+γ2Rt+3+...+γT−t−1RT
同样一个状态可能在一个完整的状态序列中重复出现,那么该状态的收获该如何计算?
-
首次访问(first visit)蒙特卡罗法:如果每个回合只采用第一次访问的回报样本更新价值函数,(即仅把状态序列中第一次出现该状态时的收获值纳入到收获平均值的计算中)
-
每次访问(every visit) 蒙特卡罗法:如果采用回合内全部的回报样本值更新价值函数,(针对一个状态序列中每次出现的该状态,都计算对应的收获值并纳入到收获平均值的计算中)
增量法,累计更新平均值(incremental mean),在上面预测问题的求解公式里,我们有一个average的公式,意味着要保存所有该状态的收获值之和最后取平均。这样浪费了太多的存储空间。一个较好的方法是在迭代计算收获均值,即每次保存上一轮迭代得到的收获均值与次数,当计算得到当前轮的收获时,即可计算当前轮收获均值和次数。:
从而:
N
(
S
t
)
=
N
(
S
t
)
+
1
V
(
S
t
=
V
(
S
t
)
+
1
N
(
S
t
)
(
G
t
−
V
(
S
t
)
)
N(S_{t})=N(S_{t})+1\\ V(S_{t}=V(S_{t})+\frac{1}{N(S_{t})}(G_{t}-V(S_t))
N(St)=N(St)+1V(St=V(St)+N(St)1(Gt−V(St))
N(St)为计数器
1.1 ϵ−贪心策略
ϵ−贪心策略通过设置一个较小的ϵ值,使用1−ϵ的概率贪婪地选择目前认为是最大行为价值的行为,而用ϵ 的概率随机的从所有m 个可选行为中选择行为.
对于某个策略pi, 如果它对任意的s∈S, a∈A(s)均有pi(a|s)>0,则称这个策略是柔性策略,柔性策略可以选择所有可能的动作,所以从一个状态出发可以达到这个状态能达到的所有状态和所有状态动作对。理论上可以覆盖所有可达的状态或状态动作对,从而求得全局最优策略。
π ( a ∣ s ) = { 1 , a = a ∗ 0 , a ≠ a ∗ } a = a r g m a x a ′ Q ( s , a ′ ) (2) \pi(a|s)=\left\{\begin{matrix}1,a=a*\\ 0, a≠a*\\ \end{matrix} \right\} \tag{2}\\ a=argmax_{a'}Q(s,a') π(a∣s)={1,a=a∗0,a=a∗}a=argmaxa′Q(s,a′)(2)
对应的ϵ贪心策略是:
π
(
a
∣
s
)
=
{
1
−
ϵ
+
ϵ
∣
A
(
s
)
∣
,
a
=
a
∗
ϵ
∣
A
(
s
)
∣
,
a
≠
a
∗
}
(2)
\pi(a|s)=\left\{\begin{matrix} 1-ϵ+\frac{ϵ}{|A(s)|},a=a*\\ \frac{ϵ}{|A(s)|}, a≠a*\\ \end{matrix} \right\} \tag{2}
π(a∣s)={1−ϵ+∣A(s)∣ϵ,a=a∗∣A(s)∣ϵ,a=a∗}(2)
将ϵ概率平均分配在各个动作上,将剩下的(1-ϵ)的概率分配给动作a*。
在实际求解控制问题时,为了使算法可以收敛,一般ϵ会随着算法的迭代过程逐渐减小,并趋于0。这样在迭代前期,我们鼓励探索,而在后期,由于我们有了足够的探索量,开始趋于保守,以贪婪为主,使算法可以稳定收敛。
1.2 在线蒙特卡罗法
输入:环境(无数学描述),策略π
输出:最优的动作价值函数q∗和最优策略π∗
-
初始化所有的动作价值Q(s,a)=0, 状态次数N(s,a)=0,采样次数k=0,随机初始化一个策略π
-
k=k+1, 基于策略π进行第k次蒙特卡罗采样,得到一个完整的状态序列:
S 1 , A 1 , R 2 , S 2 , A 2 , . . . , S t − 1 , A t − 1 , R t , S t S1, A1, R2, S2, A2, ...,St-1,At-1,Rt, St S1,A1,R2,S2,A2,...,St−1,At−1,Rt,St -
对于该状态序列里出现的每一状态行为对(St,At),计算其收获Gt, 更新其计数N(s,a和行为价值函数Q(s,a):
G t = R t + 1 + γ R t + 2 + γ 2 R t + 3 + . . . + γ T − t − 1 R T N ( S t , A t ) = N ( S t , A t ) + 1 Q ( S t , A t ) = Q ( S t , A t ) + 1 N ( S t , A t ) ( G t − Q ( S t , A t ) ) G_{t}=R_{t+1}+\gamma R_{t+2}+\gamma ^{2}R_{t+3}+...+\gamma ^{T-t-1}R_{T}\\ N(S_{t},A_{t})=N(S_{t},A_{t})+1\\ Q(S_{t},A_t)=Q(S_{t},A_t)+\frac{1}{N(S_{t},A_{t})}(G_{t}-Q(S_{t},A_t)) Gt=Rt+1+γRt+2+γ2Rt+3+...+γT−t−1RTN(St,At)=N(St,At)+1Q(St,At)=Q(St,At)+N(St,At)1(Gt−Q(St,At)) -
基于新计算出的动作价值,更新当前的ϵ−贪婪策略:
ϵ = 1 k π ( a ∣ s ) = { 1 − ϵ + ϵ ∣ A ( s ) ∣ , a = a ∗ ϵ ∣ A ( s ) ∣ , a ≠ a ∗ } a ∗ = a r g m a x Q ( s , a ) (2) ϵ = \frac{1}{k}\\ \pi(a|s)=\left\{ \begin{matrix} 1-ϵ+\frac{ϵ}{|A(s)|},a=a*\\ \frac{ϵ}{|A(s)|}, a≠a*\\ \end{matrix} \right\} \tag{2}\\ a*=argmaxQ(s,a) ϵ=k1π(a∣s)={1−ϵ+∣A(s)∣ϵ,a=a∗∣A(s)∣ϵ,a=a∗}a∗=argmaxQ(s,a)(2) -
如果所有的Q(s,a)收敛,则对应的所有Q(s,a)即为最优的动作价值函数q∗。对应的策略π(a|s)即为最优策略π∗。否则转到第二步。
1.3 柔性策略的每次访问同策回合更新
1.4 不足
蒙特卡罗法是第一个不基于模型的强化问题求解方法。它可以避免动态规划求解过于复杂,同时还可以不事先知道环境转化模型,因此可以用于海量数据和复杂模型。但是它也有自己的缺点,这就是它每次采样都需要一个完整的状态序列。如果我们没有完整的状态序列,或者很难拿到较多的完整的状态序列,这时候蒙特卡罗法就不太好用了, 也就是说,我们还需要寻找其他的更灵活的不基于模型的强化问题求解方法。
2. 同策时序差分更新
对于无模型环境,使用蒙特卡罗法来求解强化学习问题时,虽然虽然灵活,不需要环境的状态转化概率模型,但是它需要所有的采样序列都是经历完整的状态序列。如果我们没有完整的状态序列,那么就无法使用蒙特卡罗法求解了。时序差分(Temporal-Difference, TD)则可以不使用完整状态序列求解强化学习问题。
蒙特卡罗法中计算状态回报的方法是:
G
t
=
R
t
+
1
+
γ
R
t
+
2
+
γ
2
R
t
+
3
+
.
.
.
+
γ
T
−
t
−
1
R
T
G_{t}=R_{t+1}+\gamma R_{t+2}+\gamma ^{2}R_{t+3}+...+\gamma ^{T-t-1}R_{T}
Gt=Rt+1+γRt+2+γ2Rt+3+...+γT−t−1RT
而对于时序差分法来说,我们没有完整的状态序列,只有部分的状态序列,那么如何可以近似求出某个状态的回报呢?
回顾马尔科夫决策过程(MDP)中的贝尔曼方程:
v
π
(
s
)
=
E
π
(
R
t
+
1
+
γ
v
π
(
S
t
+
1
)
∣
S
t
=
s
)
v_{\pi}(s)=E_{\pi}(R_{t+1}+\gamma v_{\pi}(S_{t+1})|S_{t}=s)
vπ(s)=Eπ(Rt+1+γvπ(St+1)∣St=s)
我们可以用Ut = Rt+1+γv(St+1)来近似的代替收获Gt, 一般我们把Ut称为TD目标值。(Gt为无偏回报样本,Ut为采样一步或者n步下的估计回报值,为有偏估计)
Rt+1+γV(St+1)−V(St)称为TD误差,将用TD目标值近似代替收获G(t)的过程称为引导(bootstrapping)。
这样我们只需要两个连续的状态与对应的奖励,就可以尝试求解强化学习问题了。
2.1 时序差分价值函数迭代式
回顾蒙特卡罗法的迭代式子是:
V
(
S
t
)
=
V
(
S
t
)
+
1
N
(
S
t
)
(
G
t
−
V
(
S
t
)
)
V(S_{t})=V(S_{t})+\frac{1}{N(S_{t})}(G_{t}-V(S_{t}))
V(St)=V(St)+N(St)1(Gt−V(St))
由于在时序差分我们没有完整的序列,也就没有对应的次数N(St),一般就用一个[0,1]的系数α代替。
V
(
S
t
)
=
V
(
S
t
)
+
α
(
G
t
−
V
(
S
t
)
)
Q
(
S
t
,
A
t
)
=
Q
(
S
t
,
A
t
)
+
α
(
G
t
−
Q
(
S
t
,
A
t
)
)
V(S_{t})=V(S_{t})+\alpha (G_{t}-V(S_{t}))\\ Q(S_{t},A_{t})=Q(S_{t},A_{t})+\alpha(G_{t}-Q(S_{t},A_{t}))
V(St)=V(St)+α(Gt−V(St))Q(St,At)=Q(St,At)+α(Gt−Q(St,At))
2.2 时序差分与蒙特卡罗方法对比
这里我们用一个简单的例子来看看蒙特卡罗法和时序差分法求解预测问题的不同。假设我们的强化学习问题有A,B两个状态,模型未知,不涉及策略和行为。只涉及状态转化和即时奖励。一共有8个完整的状态序列如下:(状态,奖励)
A,0,B,0 --> B,1–> B,1 --> B,1 --> B,1 --> B,1 --> B,1 --> B,0
只有第一个状态序列是有状态转移的,其余7个只有一个状态。设置衰减因子γ=1。
首先我们按蒙特卡罗法来求解预测问题。由于只有第一个序列中包含状态A,因此A的价值仅能通过第一个序列来计算,也就等同于计算该序列中状态A的收获:
V(A)=G(A)=RA+γRA=0
对于B,则需要对其在8个序列中的收获值来平均,其结果是6/8。
再来看看时序差分法求解的过程。其收获是在计算状态序列中某状态价值时是应用其后续状态的预估价值来计算的,对于B来说,它总是终止状态,没有后续状态,因此它的价值直接用其在8个序列中的收获值来平均,其结果是6/8。
对于A,只在第一个序列出现,它的价值为:
V(A)=RA+γV(B)=6/8
从上面的例子我们也可以看到蒙特卡罗法和时序差分法求解预测问题的区别。
-
时序差分法在知道结果之前就可以学习,也可以在没有结果时学习,还可以在持续进行的环境中学习,而蒙特卡罗法则要等到最后结果才能学习,时序差分法可以更快速灵活的更新状态的价值估计,这在某些情况下有着非常重要的实际意义。
-
时序差分法在更新状态价值时使用的是TD 目标值,即基于即时奖励和下一状态的预估价值来替代当前状态在状态序列结束时可能得到的收获,是当前状态价值的有偏估计,而蒙特卡罗法则使用实际的收获来更新状态价值,是某一策略下状态价值的无偏估计,这一点蒙特卡罗法占优。
-
虽然时序差分法得到的价值是有偏估计,但是其方差却比蒙特卡罗法得到的方差要低,且对初始值敏感,通常比蒙特卡罗法更加高效。目前主流的强化学习求解方法都是基于时序差分的。
2.3 n步时序差分
从单步,两步,到三步,再到n步,我们可以归纳出n步时序差分收获U(n)t表达式为:
U
t
q
=
R
t
+
1
+
γ
R
t
+
2
+
.
.
.
+
γ
n
−
1
R
t
+
n
+
γ
n
q
(
S
t
+
n
,
A
t
+
n
)
U
t
v
=
R
t
+
1
+
γ
R
t
+
2
+
.
.
.
+
γ
n
−
1
R
t
+
n
+
γ
n
V
(
S
t
+
n
)
U_{t}^{q} = R_{t+1}+\gamma R_{t+2} +...+\gamma ^{n-1}R_{t+n} + \gamma^{n}q(S_{t}+n, A_{t+n})\\ U_{t}^{v} = R_{t+1}+\gamma R_{t+2} +...+\gamma ^{n-1}R_{t+n} + \gamma^{n}V(S_{t}+n)
Utq=Rt+1+γRt+2+...+γn−1Rt+n+γnq(St+n,At+n)Utv=Rt+1+γRt+2+...+γn−1Rt+n+γnV(St+n)
当n越来越大,趋于无穷,或者说趋于使用完整的状态序列时,n步时序差分就等价于蒙特卡罗法了。(如果到了n步,则就没有自益,估计的目标就是回报值)
2.4 步长选择
n步时序差分选择多少步数作为一个较优的计算参数是需要尝试的超参数调优问题。为了能在不增加计算复杂度的情况下综合考虑所有步数的预测,我们引入了一个新[0,1]的参数λ,定义λ-回报是n从1到∞所有步的回报乘以权重的和。每一步的权重是(1−λ)λ^{n−1},这样λ−回报的计算公式表示为:
U
t
λ
=
(
1
−
λ
)
∑
n
=
1
∞
λ
n
−
1
U
t
n
U_{t}^{\lambda} =(1-\lambda)\sum^{∞}_{n=1}\lambda^{n-1}U^{n}_{t}
Utλ=(1−λ)n=1∑∞λn−1Utn
进而引入TD更新策略评估
V
(
S
t
)
<
−
−
V
(
S
t
)
+
α
[
U
t
−
V
(
S
t
)
]
q
(
S
t
,
A
t
)
<
−
−
q
(
S
t
,
A
t
)
+
α
[
U
t
−
q
(
S
t
,
A
t
)
]
V(St) <-- V(St) + \alpha[Ut - V(St)]\\ q(St,At) <-- q(St,At) + \alpha[Ut - q(St,At) ]
V(St)<−−V(St)+α[Ut−V(St)]q(St,At)<−−q(St,At)+α[Ut−q(St,At)]
为什么每一步收获的权重定义为(1−λ)λ^{n−1}?其图像如下图所示,
可以看到随着n的增大,其第n步收获的权重呈几何级数的衰减。当在T时刻到达终止状态时,未分配的权重全部给予终止状态的实际收获值。这样可以使一个完整的状态序列中所有的n步收获的权重加起来为1,离当前状态越远的收获其权重越小。
当λ=0普通的时序差分法,当λ=1时,就是蒙特卡罗法。
-
时序差分的在线控制(on-policy)算法最常见的是SARSA算法,在线控制一般只有一个策略(最常见的是ϵ−贪婪法)
-
时序差分的离线控制(off-policy)算法最常见的是Q-Learning算法,离线控制一般有两个策略,其中一个策略(最常见的是ϵ−贪婪法)用于选择新的动作A*,另一个策略(最常见的是贪婪法)用于更新价值函数
2.5 伪代码
3. SARSA算法
State-Action-Reward-State-Action,SARSA算法,**在单步动作价值估计的算法的基础上,更新价值估计后更新策略,即加入了策略改进。**属于在线控制算法。
算法利用
U
t
:
t
+
1
q
=
R
t
+
1
+
γ
q
t
(
S
t
+
1
,
A
t
+
1
)
U_{t:t+1}^{q}=R_{t+1}+\gamma q_{t}(S_{t+1},A_{t+1})
Ut:t+1q=Rt+1+γqt(St+1,At+1)
估计得到单步时序差分目标Ut,进而更新q(St, At):
q
(
S
t
,
A
t
)
<
−
−
q
(
S
t
,
A
t
)
+
α
[
U
t
−
q
(
S
t
,
A
t
)
]
q(St,At) <-- q(St,At) + \alpha[Ut - q(St,At) ]
q(St,At)<−−q(St,At)+α[Ut−q(St,At)]
3.1 算法流程
输入:迭代次数T,状态集S, 动作集A, 学习率α,衰减因子γ, 贪婪率ϵ,
输出:所有的状态和动作对应的价值Q
- 随机初始化所有的状态和动作对应的价值Q. 对于终止状态其Q值初始化为0.
2. for i from 1 to T,进行迭代。
a) 初始化S为当前状态序列的第一个状态。设置A为ϵ−贪婪法在当前状态S选择的动作。
b) 在状态S执行当前动作A,得到新状态S′和奖励R
c) 用ϵ−贪婪法在状态S′选择新的动作A′
d) 更新价值函数Q(S,A):
Q
(
S
,
A
)
=
Q
(
S
,
A
)
+
α
(
R
+
γ
Q
(
S
′
,
A
′
)
−
Q
(
S
,
A
)
)
Q(S,A)=Q(S,A)+\alpha(R+\gamma Q(S',A')-Q(S,A))
Q(S,A)=Q(S,A)+α(R+γQ(S′,A′)−Q(S,A))
e) S=S′,A=A′
f) 如果S′是终止状态,当前轮迭代完毕,否则转到步骤(b)
学习率α一般需要随着迭代的进行逐渐变小,这样才能保证动作价值函数Q可以收敛。当Q收敛时,我们的策略ϵ−贪婪法也就收敛了。
3.2 期望SARSA算法
在估计Ut时,不使用基于动作价值的时序差分目标:
U
t
:
t
+
1
q
=
R
t
+
1
+
γ
q
t
(
S
t
+
1
,
A
t
+
1
)
U_{t:t+1}^{q}=R_{t+1}+\gamma q_{t}(S_{t+1},A_{t+1})
Ut:t+1q=Rt+1+γqt(St+1,At+1)
而使用基于状态价值的时序差分目标,再利用 Bellman 方程有:
U
t
:
t
+
1
(
v
)
=
R
t
+
1
+
γ
v
(
S
t
+
1
)
=
R
t
+
1
+
γ
∑
a
∈
A
(
S
t
+
1
)
π
(
a
∣
S
t
+
1
)
q
(
S
t
+
1
,
a
)
U_{t:t+1}^{(v)} = R_{t+1} + \gamma v(S_{t+1}) = R_{t+1} + \gamma \sum_{a \in \mathcal A(S_{t+1})} \pi(a \mid S_{t+1})q(S_{t+1},a)
Ut:t+1(v)=Rt+1+γv(St+1)=Rt+1+γa∈A(St+1)∑π(a∣St+1)q(St+1,a)
相较于SARSA,计算量增大,但是期望运算减小了个别不恰当决策,从而减小个别不恰当对结果影响,因此期望SARSA常常有比SARSA更大的学习率
4. 异策时序差分更新
异策算法是基于重要性采样的,将该方法整合到时序差分策略评估动作价值算法或SARSA 算法中,即可得到它们的重要性采样版本。
4.1 基于重要性采用的异策算法
5. Q-learning
其流程与SARSA算法相似,但是Q学习的更新式不是基于当前策略,二十基于另外一个并不一定要使用的确定性策略来更新动作价值,为异策算法。
U
t
=
R
t
+
1
+
γ
R
t
+
2
+
.
.
.
+
γ
n
−
1
R
t
+
n
+
γ
n
m
a
x
a
∈
A
(
S
t
+
n
)
q
(
S
t
+
n
,
a
)
U_{t} = R_{t+1}+\gamma R_{t+2} +...+\gamma ^{n-1}R_{t+n} + \gamma^{n}max_{a∈A(S_{t+n})}q(S_{t+n},a)
Ut=Rt+1+γRt+2+...+γn−1Rt+n+γnmaxa∈A(St+n)q(St+n,a)
Q-learning采用maxq(S,a)更新动作价值,会导致**”最大化偏差“**,使得估计的动作价值偏大。
对于Q-Learning和SARSA这样的时序差分算法,对于小型的强化学习问题是非常灵活有效的,但是在大数据时代,异常复杂的状态和可选动作,使Q-Learning和SARSA要维护的Q表异常的大,甚至远远超出内存,这限制了时序差分算法的应用场景。在深度学习兴起后,基于深度学习的强化学习开始占主导地位.
6. Double Q-learning
为解决Q-learning所存在的问题,使用两个独立的动作价值估计值q(0)和q(1),
用
q
(
0
)
(
S
t
+
1
,
a
r
g
m
a
x
a
q
(
1
)
(
S
t
+
1
,
a
)
)
或
q
(
1
)
(
S
t
+
1
,
a
r
g
m
a
x
a
q
(
0
)
(
S
t
+
1
,
a
)
)
来
代
替
Q
学
习
中
的
m
a
x
a
q
(
S
t
+
1
,
a
)
由
于
q
(
0
)
和
q
(
1
)
是
相
互
独
立
的
估
计
,
所
有
E
[
q
(
0
)
(
S
t
+
1
,
A
∗
)
]
=
q
(
S
t
+
1
,
A
∗
)
,
其
中
A
∗
=
a
r
g
m
a
x
a
q
(
1
)
(
S
t
+
1
,
a
)
,
这
样
就
消
除
了
偏
差
这
个
算
法
中
最
终
输
出
的
动
作
价
值
函
数
是
q
(
0
)
和
q
(
1
)
的
平
均
值
,
即
1
2
(
q
(
0
)
和
q
(
1
)
)
用q^{(0)}(S_{t+1},argmax_{a}q^{(1)}(S_{t+1},a))或q^{(1)}(S_{t+1},argmax_{a}q^{(0)}(S_{t+1},a))来代替Q学习中的max_{a}q(S_{t+1},a)\\ 由于q^{(0)}和q^{(1)}是相互独立的估计,所有E[q^{(0)}(S_{t+1},A^*)]=q(S_{t+1},A^*),\\ 其中A^*=argmax_{a}q^{(1)}(S_{t+1},a),这样就消除了偏差\\ 这个算法中最终输出的动作价值函数是q^{(0)}和q^{(1)}的平均值,即\frac{1}{2}(q^{(0)}和q^{(1)})
用q(0)(St+1,argmaxaq(1)(St+1,a))或q(1)(St+1,argmaxaq(0)(St+1,a))来代替Q学习中的maxaq(St+1,a)由于q(0)和q(1)是相互独立的估计,所有E[q(0)(St+1,A∗)]=q(St+1,A∗),其中A∗=argmaxaq(1)(St+1,a),这样就消除了偏差这个算法中最终输出的动作价值函数是q(0)和q(1)的平均值,即21(q(0)和q(1))
7. 资格迹
是一种让时序差分学习更加有效的机制,能在回合更新和单步时序差分更新中折中,实现简单,运行有效。
7.1 lambda回报
λ 回 报 是 时 序 差 分 目 标 U t : t + 1 , U t : t + 2 , U t : t + 3 . . . 按 照 ( 1 − λ ) , ( 1 − λ ) λ , ( 1 − λ ) λ 2 . . . 加 权 平 均 的 结 果 \lambda回报是时序差分目标U_{t:t+1},U_{t:t+2},U_{t:t+3}...按照(1-\lambda),(1-\lambda)\lambda,(1-\lambda)\lambda^{2}...加权平均的结果 λ回报是时序差分目标Ut:t+1,Ut:t+2,Ut:t+3...按照(1−λ),(1−λ)λ,(1−λ)λ2...加权平均的结果
对于连续型任务,有:
U
t
λ
=
(
1
−
λ
)
∑
n
=
1
+
∞
λ
n
−
1
U
t
:
t
+
n
U_{t}^{\lambda}=(1-\lambda)\sum^{+∞}_{n=1}\lambda^{n-1}U_{t:t+n}
Utλ=(1−λ)n=1∑+∞λn−1Ut:t+n
对于回合制任务,有:
U
t
λ
=
(
1
−
λ
)
∑
n
=
1
T
−
t
−
1
λ
n
−
1
U
t
:
t
+
n
+
λ
T
−
t
−
1
G
t
U_{t}^{\lambda}=(1-\lambda)\sum^{T-t-1}_{n=1}\lambda^{n-1}U_{t:t+n}+\lambda^{T-t-1}G_{t}
Utλ=(1−λ)n=1∑T−t−1λn−1Ut:t+n+λT−t−1Gt
上 式 可 以 看 做 是 回 合 更 新 目 标 G t 和 单 步 时 序 差 分 目 标 U t : t + 1 的 推 广 : 当 λ = 1 时 , U t 1 = G t 就 是 回 合 更 新 的 回 报 ; 当 λ = 0 时 , U t 0 = U t : t + 1 就 是 单 步 时 序 差 分 目 标 − − − − − − − − − − − − − − − − − − − 离 线 λ 回 报 算 法 是 以 U t λ 作 为 目 标 , 试 图 减 小 [ U t λ − q ( S t , A t ) ] 2 或 [ U t λ − v ( S t ) ] 2 的 。 对 于 回 合 制 任 务 , 该 算 法 在 回 合 结 束 后 为 每 一 步 t = 0 , 1 , 2 , ⋯ 计 算 U t λ , 并 统 一 更 新 价 值 , 因 此 这 样 的 算 法 称 为 离 线 算 法 ( o f f l i n e a l g o r i t h m ) 。 上式可以看做是回合更新目标 G_t 和单步时序差分目标 U_{t:t+1}的推广:\\ 当 \lambda=1 时,U_{t}^1 = G_t就是回合更新的回报;\\ 当 \lambda=0时,U_t^0=U_{t:t+1} 就是单步时序差分目标\\ -------------------\\ 离线 \lambda 回报算法是以 U_t^\lambda 作为目标,试图减小 [U_t^\lambda-q(S_t,A_t)]^2 或 [U_t^\lambda-v(S_t)]^2的。\\对于回合制任务,该算法在回合结束后为每一步 t=0,1,2,\cdots 计算 U_t^\lambda ,并统一更新价值,\\因此这样的算法称为离线算法(offline algorithm)。 上式可以看做是回合更新目标Gt和单步时序差分目标Ut:t+1的推广:当λ=1时,Ut1=Gt就是回合更新的回报;当λ=0时,Ut0=Ut:t+1就是单步时序差分目标−−−−−−−−−−−−−−−−−−−离线λ回报算法是以Utλ作为目标,试图减小[Utλ−q(St,At)]2或[Utλ−v(St)]2的。对于回合制任务,该算法在回合结束后为每一步t=0,1,2,⋯计算Utλ,并统一更新价值,因此这样的算法称为离线算法(offlinealgorithm)。
离线\lambda回报算法使用的目标在Gt和Ut:t+1间做了折中,所以离线\lambda回报算法的效果可能比回合更新和单步时序差分更新都要好。但其具有明显的缺点:
-
回合结束后要计算 U t λ Ut^{\lambda} Utλ(t=0,1,…,T-1),计算量巨大
-
离线\lambda回报算法只能用于回合制任务,不能用于连续性任务
7.2 TD(\lambda)算法
采用资格迹可以弥补这两个缺点,TD(\lambda)是历史上具有重要影响力的强化学习算法,在离线 lambda 回报算法的基础上改进而来。事实上,在离线 lambda回报算法中,知道 (St,A_t) 后就能计算 U_{t-n:t}并部分更新 q(S_{t-n},A{t-n}),n=1,2,…, 那么问题就转化为如何求得更新权重了。
TD(\lambda)算法与离线lambda算法相比:
- TD既可以用于回合制任务,又可以用于连续性任务
- TD在每一步都更新价值估计,能够及时反映变化
- TD在每一步都均匀的计算,而且计算量都较小
8. 示例代码(Taxi调度问题)
import numpy as np
import gym
import matplotlib.pyplot as plt
env = gym.make('Taxi-v3')
state = env.reset()
taxirow, taxicol, passloc, destidx = env.unwrapped.decode(state)
print(taxirow, taxicol, passloc, destidx)
print('出租车位置 = {}'.format((taxirow, taxicol)))
print('乘客位置 = {}'.format(env.unwrapped.locs[passloc]))
print('目标位置 = {}'.format(env.unwrapped.locs[destidx]))
env.render()
env.step(1)
def run_episode(env, agent=None, train=False, render=False):#SARSA智能体与环境交互一回合
episode_reward = 0
state = env.reset()
if agent is None:
action = env.action_space.sample()
else:
action = agent.choose_action(state)
while True:
if render:
env.render()
next_state, reward, done, _ = env.step(action)
if agent is None:
next_action = env.action_space.sample()
else:
next_action = agent.choose_action(next_state)
if train:
agent.learn(state, action, reward, next_state, done, next_action)
episode_reward += reward
if done:
break
state, action = next_state, next_action
return episode_reward
class Agent():
def __init__(self, env, gamma=0.9, learning_rate=0.1, epsilon=0.01):
self.gamma = gamma
self.learning_rate = learning_rate
self.epsilon = epsilon
self.action_n = env.action_space.n
self.q = np.zeros((env.observation_space.n, env.action_space.n))
def choose_action(self, state):
if np.random.uniform() > self.epsilon:
action = self.q[state].argmax()
else:
action = np.random.randint(self.action_n)
return action
class SARSA(Agent):
def learn(self, state, action, reward, next_state, done, next_action):
u = reward + self.gamma * self.q[next_state][next_action] * (1 - done)
self.q[state][action] += self.learning_rate * (u - self.q[state][action])
class QLearning(Agent):
def learn(self, state, action, reward, next_state, done, *args):
u = reward + self.gamma * self.q[next_state].max() * (1 - done)
self.q[state][action] += self.learning_rate * (u - self.q[state][action])
class DoubleQLearning(Agent):
def __init__(self, env, gamma=0.9, learning_rate=0.1, epsilon=0.01):
super().__init__(env, gamma=gamma, learning_rate=learning_rate, epsilon=epsilon)
self.q1 = np.zeros((env.observation_space.n, env.action_space.n))
def choose_action(self, state):
if np.random.uniform() > self.epsilon:
action = (self.q[state] + self.q1[state]).argmax()
else:
action = np.random.randint(self.action_n)
return action
def learn(self, state, action, reward, next_state, done, *args):
if np.random.randint(2):
self.q, self.q1 = self.q1, self.q
a = self.q[next_state].argmax()
u = reward + self.gamma * self.q1[next_state][a] * (1 - done)
self.q[state][action] += self.learning_rate * (u - self.q[state][action])
class SARSALambda(Agent):
def __init__(self, env, lamb=0.5, beta=1, gamma=0.9, learning_rate=0.1, epsilon=0.01):
super().__init__(env, gamma=gamma, learning_rate=learning_rate, epsilon=epsilon)
self.lamb = lamb
self.beta = beta
self.e = np.zeros((env.observation_space.n, env.action_space.n))
def learn(self, state, action, reward, next_state, done, next_action):
self.e *= (self.lamb * self.gamma)
self.e[state][action] = 1 + self.beta * self.e[state][action]
u = reward + self.gamma * self.q[next_state][next_action] * (1 - done)
self.q += self.learning_rate * self.e * (u - self.q[state][action])
# self.q += self.learning_rate * self.e * (u - self.q)
if done: # 为下一回合初始化资格迹
self.e *= 0
episodes = 5000
episode_rewards = []
#agent = SARSA(env)
#agent = DoubleQLearning(env)
agent = QLearning(env)
# 智能体的训练
for i in range(episodes):
episode_rewards.append(run_episode(env, agent, train=True))
plt.plot(episode_rewards)
# 智能体的测试
agent.epsilon = 0
episode_rewards = [run_episode(env, agent) for _ in range(100)]
print("平均回合奖励 = {} / {} = {}".format(sum(episode_rewards), \
len(episode_rewards), np.mean(episode_rewards)))
plt.show()