在此推荐另一篇文章【自动驾驶决策规划】POMDP之Introduction
“The future is independent of the past given the present”
未来状态的概率分布只与当前状态有关,而与过去状态无关。
定义:
性质:
P s s ′ \boldsymbol{P}_{ss^{\prime}} Pss′为从状态 s s s转移到状态 s ′ s' s′的概率,又称一步状态转移概率。 P \boldsymbol{P} P为一步状态转移矩阵。
P [ S t + 1 ∣ S t ] = P [ S t + 1 ∣ S 1 , … , S t ] P s s ′ = P [ S t + 1 = s ′ ∣ S t = s ] P = [ P 11 P 12 … P 1 n P 21 P 22 … P 2 n … … P n 1 P n 2 … P n n ] \begin{gathered} P[S_{t+1}|S_t]=P[S_{t+1}|S_1,\ldots,S_t] \\ \boldsymbol{P}_{ss^{\prime}}=P[S_{t+1}=s^{\prime}|S_{t}=s] \\ \boldsymbol{P}=\begin{bmatrix}P_{11}&P_{12}&\ldots&P_{1n}\\P_{21}&P_{22}&\ldots&P_{2n}\\\ldots\\\ldots\\P_{n1}&P_{n2}&\ldots&P_{nn}\end{bmatrix} \end{gathered} P[St+1∣St]=P[St+1∣S1,…,St]Pss′=P[St+1=s′∣St=s]P= P11P21……Pn1P12P22Pn2………P1nP2nPnn
矩阵有以下性质:
以上图为例, S 1 S_1 S1转移到自身和 S 2 S2 S2的概率分别为0.1,0.9; S 2 S_2 S2转移到自身和 S 1 S1 S1的概率分别为0.2,0.8。状态转移矩阵可表示为: P = [ 0.1 0.9 0.8 0.2 ] \boldsymbol{P}=\begin{bmatrix}0.1&0.9\\0.8&0.2\end{bmatrix} P=[0.10.80.90.2]
马尔可夫过程(Markov process)指具有马尔可夫性质的随机过程,也被称为马尔可夫链(Markov chain)。马尔可夫链可由二元组 ( S , P ) (S, P) (S,P)描述。状态转移概率不随时间发生变化。
C l a s s 1 Class 1 Class1为起始状态, S l e e p Sleep Sleep为终止状态,之中的数字代表了各状态之间的转移概率,从每个状态出发转移到其他状态的概率总和为 1。
我们可以写出这个马尔可夫过程的状态转移矩阵:
给定一个马尔可夫过程,我们就可以从某个状态出发,根据它的状态转移矩阵生成一个状态序列(episode),这个步骤也被叫做采样(sampling)。例如,我们可能得到一下采样结果:
在马尔可夫过程的基础上加入奖励函数 r r r 和折扣因子 γ \gamma γ,就可以得到马尔可夫奖励过程(Markov reward process)。一个马尔可夫奖励过程由 ⟨ S , P , R , γ ⟩ \langle\mathcal{S},\mathcal{P},\color{red}{\mathcal{R}},\gamma\rangle ⟨S,P,R,γ⟩构成,各个组成元素的含义如下所示。
定义:
在一个马尔可夫奖励过程中,从第 t t t时刻状态 S t S_t St开始,直到终止状态时,所有奖励的衰减之和称为回报(Return) G t G_t Gt,公式如下:
G t = R t + 1 + γ R t + 2 + . . . = ∑ k = 0 ∞ γ k R t + k + 1 G_t=R_{t+1}+\gamma R_{t+2}+...=\sum_{k=0}^\infty\gamma^kR_{t+k+1} Gt=Rt+1+γRt+2+...=k=0∑∞γkRt+k+1
仍然沿用上面的例子:
例如,进入状态 C l a s s 2 Class2 Class2可以得到奖励 − 2 -2 −2,表明我们不希望进入,进入 P a s s Pass Pass可以获得最高的奖励 10 10 10,但是进入 S l e e p Sleep Sleep之后奖励为零,并且此时序列也终止了。一个计算例子: C l a s s 1 → C l a s s 2 → C l a s s 3 → P a s s → S l e e p Class1\rightarrow Class2\rightarrow Class3\rightarrow Pass \rightarrow Sleep Class1→Class2→Class3→Pass→Sleep: − 2 + 0.5 × ( − 2 ) + ( 0.5 ) 2 × ( − 2 ) + ( 0.5 ) 3 × 10 = − 2.25 -2 + 0.5\times(-2)+(0.5)^2\times(-2)+(0.5)^3\times 10=-2.25 −2+0.5×(−2)+(0.5)2×(−2)+(0.5)3×10=−2.25
用代码进行表示:
import numpy as np
# Define the transition Matrix
P = [
[0.0, 0.5, 0.0, 0.0, 0.0, 0.5, 0.0],
[0.0, 0.0, 0.8, 0.0, 0.0, 0.0, 0.2],
[0.0, 0.0, 0.0, 0.6, 0.4, 0.0, 0.0],
[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0],
[0.2, 0.4, 0.4, 0.0, 0.0, 0.0, 0.0],
[0.1, 0.0, 0.0, 0.0, 0.0, 0.9, 0.0],
[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0]
]
P = np.array(P)
RewardVector = [-2, -2, -2, 10, 1, -1, 0]
# 给定一条序列,计算从某个索引(起始状态)开始到序列最后(终止状态)得到的回报
def ComputeSequenceReward(Start_idx, Sequence, RewardVector, gamma=0.5):
TotalReward = 0.0
for i in reversed(range(Start_idx, len(Sequence))):
TotalReward = gamma * TotalReward + RewardVector[Sequence[i] - 1]
return TotalReward
def test01():
chain = [1, 2, 3, 4, 7]
start_index = 0
print("根据本序列计算得到回报为:%s。"% ComputeSequenceReward(start_index, chain, RewardVector, gamma=0.5))
if __name__ == "__main__":
test01()
根据本序列计算得到回报为:-2.25。
γ = 0 , 0.9 , 1 \gamma=0,0.9,1 γ=0,0.9,1时的例子(图中数字为各节点的价值):
在马尔可夫奖励过程中,一个状态的期望回报(即从这个状态出发的未来累积奖励的期望)被称为这个状态的价值(value)。所有状态的价值就组成了价值函数(value function),价值函数的输入为某个状态,输出为这个状态的价值。我们将价值函数写成 v ( s ) = E [ G t ∣ S t = s ] v(s)=\mathbb{E}\left[G_t\mid S_t=s\right] v(s)=E[Gt∣St=s],展开为 V ( s ) = E [ G t ∣ S t = s ] = E [ R t + 1 + γ R t + 2 + γ 2 R t + 3 + . . . ∣ S t = s ] = E [ R t + 1 + γ ( R t + 2 + γ R t + 3 + . . . ) ∣ S t = s ] = E [ R t + 1 + γ G t + 1 ∣ S t = s ] = E [ R t + 1 + γ V ( S t + 1 ) ∣ S t = s ] \begin{aligned} V(s)& =\mathbb{E}\left[G_t\mid S_t=s\right] \\ &=\mathbb{E}\left[R_{t+1}+\gamma R_{t+2}+\gamma^2R_{t+3}+...\mid S_t=s\right] \\ &=\mathbb{E}\left[R_{t+1}+\gamma\left(R_{t+2}+\gamma R_{t+3}+...\right)\mid S_t=s\right] \\ &=\mathbb{E}\left[R_{t+1}+\gamma G_{t+1}\mid S_t=s\right] \\ &=\mathbb{E}\left[R_{t+1}+\gamma V(S_{t+1})\mid S_t=s\right] \end{aligned} V(s)=E[Gt∣St=s]=E[Rt+1+γRt+2+γ2Rt+3+...∣St=s]=E[Rt+1+γ(Rt+2+γRt+3+...)∣St=s]=E[Rt+1+γGt+1∣St=s]=E[Rt+1+γV(St+1)∣St=s]
在上式的最后一个等号中,一方面,即时奖励的期望正是奖励函数的输出,即 E [ R t + 1 ∣ S t = s ] = R s ] \mathbb{E}[R_{t+1}|S_t=s]=\mathcal{R}_s] E[Rt+1∣St=s]=Rs];另一
方面,等式中剩余部分 E [ γ V ( S t + 1 ) ∣ S t = s ] \mathbb{E}[\gamma V(S_{t+1})|S_t=s] E[γV(St+1)∣St=s]可以根据从状态 s s s出发的转移概率得到,即可以得到 V ( s ) = R s + γ ∑ s ′ ∈ S P s s ′ V ( s ′ ) V(s)=\mathcal{R}_s+\gamma\sum_{s^{\prime}\in\mathcal{S}}\mathcal{P}_{ss^{\prime}}V(s^{\prime}) V(s)=Rs+γs′∈S∑Pss′V(s′)
上式就是马尔可夫奖励过程中非常有名的贝尔曼方程(Bellman equation),对每一个状态都成立。我们可以将其写成矩阵形式:
V = R + γ P V \mathcal{V}=\mathcal{R}+\gamma\mathcal{P}\mathcal{V} V=R+γPV [ V ( s 1 ) ⋮ V ( s n ) ] = [ R 1 ⋮ R n ] + γ [ P 11 … P 1 n ⋮ P 11 … P n n ] [ V ( s 1 ) ⋮ V ( s n ) ] \begin{bmatrix}V(s_1)\\\vdots\\V(s_n)\end{bmatrix}=\begin{bmatrix}\mathcal{R}_1\\\vdots\\\mathcal{R}_n\end{bmatrix}+\gamma\begin{bmatrix}\mathcal{P}_{11}&\ldots&\mathcal{P}_{1n}\\\vdots\\\mathcal{P}_{11}&\ldots&\mathcal{P}_{nn}\end{bmatrix}\begin{bmatrix}V(s_1)\\\vdots\\V(s_n)\end{bmatrix} V(s1)⋮V(sn) = R1⋮Rn +γ P11⋮P11……P1nPnn V(s1)⋮V(sn)
我们可以直接根据矩阵运算求解,得到以下解析解: V = R + γ P V ( I − γ P ) V = R V = ( I − γ P ) − 1 R \begin{aligned}\mathcal{V}&=\mathcal{R}+\gamma\mathcal{P}\mathcal{V}\\(I-\gamma\mathcal{P})\mathcal{V}&=\mathcal{R}\\\mathcal{V}&=(I-\gamma\mathcal{P})^{-1}\mathcal{R}\end{aligned} V(I−γP)VV=R+γPV=R=(I−γP)−1R
以上解析解的计算复杂度是 O ( n 3 ) O(n^3) O(n3),其中 n n n是状态个数,因此这种方法只适用很小的马尔可夫奖励过程。求解较大规模的马尔可夫奖励过程中的价值函数时,可以使用动态规划(dynamic programming)算法、蒙特卡洛方法(Monte-Carlo method)(见3.6)和时序差分(temporal difference)
接下来编写代码来实现求解价值函数的解析解方法,并据此计算该马尔可夫奖励过程中所有状态的价值。
# Exploit Bellman equation to compute value of all states
def ComputeValue(RewardVector, Statesize, TransitionMatrix=P, gamma=0.5):
RewardVector = np.array(RewardVector).reshape(-1, 1)
Value = np.dot(np.linalg.inv(np.eye(Statesize, Statesize) - gamma * TransitionMatrix),
RewardVector)
return Value
print("MRP中每个状态价值分别为\n", ComputeValue(RewardVector, 7))
MRP中每个状态价值分别为
[[-2.90815722]
[-1.55006913]
[ 1.12482718]
[10. ]
[ 0.62413589]
[-2.08255975]
[ 0. ]]
MDP(Markov Decision Process)是用于描述智能体与环境交互的数学模型。MDP 可以表示为五元组 ( S , A , P , R , γ ) (S, \textcolor{red}{A}, P, R, \gamma) (S,A,P,R,γ):
注意: 在上面 MDP 的定义中,我们不再使用类似 MRP 定义中的状态转移矩阵方式,而是直接表示成了状态转移函数。
不同于马尔可夫奖励过程,在马尔可夫决策过程中,通常存在一个智能体来执行动作。马尔可夫决策过程是一个与时间相关的不断进行的过程,在智能体和环境 之间存在一个不断交互的过程。一般而言,它们之间的交互是如下图的循环过程:智能体根据当前状态 S t S_t St选择动作 A t A_t At;对于状态 S t S_t St和动作 A t A_t At,环境根据奖励函数和状态转移函数得到 S t + 1 S_{t+1} St+1和 R t + 1 R_{t+1} Rt+1并反馈给智能体。智能体的目标是最大化得到的累计奖励。智能体根据当前状态从动作的集合中选择一个动作的函数,被称为策略(policy)。
智能体的策略(Policy)通常用字母 π \pi π表示。在输入状态 s s s情况下采取动作 a a a的概率的策略如下式所示。 π ( a ∣ s ) = P [ A t = a ∣ S t = s ] \pi(a|s)=\mathbb{P}\left[A_t=a\mid S_t=s\right] π(a∣s)=P[At=a∣St=s]
我们用 V π ( s ) V^\pi(s) Vπ(s)表示在 MDP 中基于策略 π \pi π的状态价值函数(state-value function),定义为从状态 s s s出发遵循策略 π \pi π能获得的期望回报,数学表达为:
V π ( s ) = E π [ G t ∣ S t = s ] V^\pi(s)=\mathbb{E}_\pi[G_t|S_t=s] Vπ(s)=Eπ[Gt∣St=s]
不同于 MRP,在 MDP 中,由于动作的存在,我们额外定义一个动作价值函数(action-value function)。我们用 Q π ( s , a ) Q^\pi(s,a) Qπ(s,a)表示在 MDP 遵循策略 π \pi π时,对当前状态 s s s执行动作 a a a得到的期望回报:
Q π ( s , a ) = E π [ G t ∣ S t = s , A t = a ] Q^\pi(s,a)=\mathbb{E}_\pi[G_t|S_t=s,A_t=a] Qπ(s,a)=Eπ[Gt∣St=s,At=a]
在贝尔曼方程中加上“期望”二字是为了与接下来的贝尔曼最优方程进行区分。
状态价值函数可以分解为当前时刻的奖励加上后继状态的经过折扣后的状态价值
V π ( s ) = E π [ R t + 1 + γ V π ( S t + 1 ) ∣ S t = s ] V_\pi(s)=\mathbb{E}_\pi\left[R_{t+1}+\gamma V_\pi(S_{t+1})\mid S_t=s\right] Vπ(s)=Eπ[Rt+1+γVπ(St+1)∣St=s]
动作价值函数同样也可以分解:
Q π ( s , a ) = E π [ R t + 1 + γ Q π ( S t + 1 , A t + 1 ) ∣ S t = s , A t = a ] Q_\pi(s,a)=\mathbb{E}_\pi\left[R_{t+1}+\gamma Q_\pi(S_{t+1},A_{t+1})\mid S_t=s,A_t=a\right] Qπ(s,a)=Eπ[Rt+1+γQπ(St+1,At+1)∣St=s,At=a]
状态价值函数和动作价值函数之间的关系:在使用策略 π \pi π中,状态 s s s的价值等于在该状态下基于策略 π \pi π采取所有动作 a i a_i ai的概率与相应的价值相乘再求和的结果:
V π ( s ) = ∑ a ∈ A π ( a ∣ s ) Q π ( s , a ) V_\pi(s)=\sum_{a\in\mathcal{A}}\pi(a|s)Q_\pi(s,a) Vπ(s)=a∈A∑π(a∣s)Qπ(s,a)
使用策略 π \pi π时,状态 s s s下采取动作 a a a的价值等于即时奖励加上经过衰减后的所有可能的下一个状态的状态转移概率与相应的价值的乘积:
Q π ( s , a ) = R s a + γ ∑ s ′ ∈ S P s s ′ a V π ( s ′ ) Q_\pi(s,a)=\mathcal{R}_s^a+\gamma\sum_{s^{\prime}\in\mathcal{S}}\mathcal{P}_{ss^{\prime}}^aV_\pi(s^{\prime}) Qπ(s,a)=Rsa+γs′∈S∑Pss′aVπ(s′)
将两者叠加,于是可以得到
V π ( s ) = ∑ a ∈ A π ( a ∣ s ) ( R s a + γ ∑ s ′ ∈ S P s s ′ a V π ( s ′ ) ) V_\pi(s)=\sum_{a\in\mathcal{A}}\pi(a|s)\left(\mathcal{R}_s^a+\gamma\sum_{s^{\prime}\in\mathcal{S}}\mathcal{P}_{ss^{\prime}}^aV_\pi(s^{\prime})\right) Vπ(s)=a∈A∑π(a∣s)(Rsa+γs′∈S∑Pss′aVπ(s′))
以及
Q π ( s , a ) = R s a + γ ∑ s ′ ∈ S P s s ′ a ∑ a ′ ∈ A π ( a ′ ∣ s ′ ) Q π ( s ′ , a ′ ) Q_\pi(s,a)=\mathcal{R}_s^a+\gamma\sum_{s^{\prime}\in\mathcal{S}}\mathcal{P}_{ss^{\prime}}^a\sum_{a^{\prime}\in\mathcal{A}}\pi(a^{\prime}|s^{\prime})Q_\pi(s^{\prime},a^{\prime}) Qπ(s,a)=Rsa+γs′∈S∑Pss′aa′∈A∑π(a′∣s′)Qπ(s′,a′)
以上就是通过简单推导分别得到两个价值函数的贝尔曼期望方程。
同样用一个例子进行解释:图中基于策略 π \pi π采取所有动作 a i a_i ai的概率为: π ( a ∣ s ) = 0.5 \pi(a|s)=0.5 π(a∣s)=0.5。计算 C l a s s 3 Class3 Class3的状态价值需要计算其下一步的状态价值之和,因此可以得到图中式子所示: 7.4 = 0.5 ∗ ( 1 + 0.2 ∗ − 1.3 + 0.4 ∗ 2.7 + 0.4 ∗ 7.4 ) + 0.5 ∗ 10 7.4 = 0.5 * (1 + 0.2* -1.3 + 0.4 * 2.7 + 0.4 * 7.4) + 0.5 * 10 7.4=0.5∗(1+0.2∗−1.3+0.4∗2.7+0.4∗7.4)+0.5∗10
同样可以利用MRP进行求解。我们可以将策略的动作选择进行边缘化(marginalization),就可以得到没有动作的 MRP 了。具体来说,对于某一个状态,我们根据策略所有动作的概率进行加权,得到的奖励和就可以认为是一个 MRP 在该状态下的奖励,即: R π = R s ′ = ∑ a ∈ A π ( a ∣ s ) R s a \mathcal{R}^\pi=\mathcal{R}_s^{'}=\sum_{a\in\mathcal{A}}\pi(a|s)\mathcal{R}_s^a Rπ=Rs′=a∈A∑π(a∣s)Rsa
同理,我们计算采取动作的概率与使 s s s转移到 s ′ s' s′的概率的乘积,再将这些乘积相加,其和就是一个 MRP 的状态 s s s从转移至 s ′ s' s′的概率: P π = P s s ′ ′ = ∑ a ∈ A π ( a ∣ s ) P s s ′ a \mathcal{P}^\pi=\mathcal{P}_{ss^{\prime}}^{'}=\sum_{a\in\mathcal{A}}\pi(a|s)\mathcal{P}_{ss^{\prime}}^{a} Pπ=Pss′′=a∈A∑π(a∣s)Pss′a
如此,便可以继续利用之前MRP的步骤了: V π = R π + γ P π V π V π = ( 1 − γ P π ) − 1 R π V_\pi=\mathcal{R}^\pi+\gamma\mathcal{P}^\pi V_\pi \\ V_\pi=(1-\gamma\mathcal{P}^\pi)^{-1}\mathcal{R}^\pi Vπ=Rπ+γPπVπVπ=(1−γPπ)−1Rπ
def test02():
# Define the transition Matrix
# C1 C2 Pass FB Sleep
P_TransformMDP2MRP = [
[0.0, 0.5, 0.0, 0.5, 0.0],
[0.0, 0.0, 0.5, 0.0, 0.5],
[0.1, 0.2, 0.2, 0.0, 0.5],
[0.5, 0.0, 0.0, 0.5, 0.0],
[0.0, 0.0, 0.0, 0.0, 1.0]
]
P_TransformMDP2MRP = np.array(P_TransformMDP2MRP)
R_TransformMDP2MRP = [-1.5, -1, 5.5, -0.5, 0.0]
print("MDP中每个状态价值分别为\n", ComputeValue(R_TransformMDP2MRP, 5, P_TransformMDP2MRP, gamma=1))
MDP中每个状态价值分别为
[[-1.30769231]
[ 2.69230769]
[ 7.38461538]
[-2.30769231]
[ 0. ]]
强化学习的目标通常是找到一个策略,使得智能体从初始状态出发能获得最多的期望回报。我们首先定义策略之间的偏序关系:当且仅当对于任意的状态 s s s都有 V π ( s ) ≥ V π ′ ( s ) V^{\pi}(s)\geq V^{\pi^{\prime}}(s) Vπ(s)≥Vπ′(s) ,记 π > π ′ \pi>\pi^{'} π>π′。于是在有限状态和动作集合的 MDP 中,至少存在一个策略比其他所有策略都好或者至少存在一个策略不差于其他所有策略,这个策略就是最优策略(optimal policy)。最优策略可能有很多个,我们都将其表示为 π ∗ ( s ) \pi^*(s) π∗(s)。
最优策略都有相同的状态价值函数,我们称之为最优状态价值函数,表示为:
V ∗ ( s ) = max π V π ( s ) , ∀ s ∈ S V^*(s)=\max_\pi V^\pi(s),\quad\forall s\in\mathcal{S} V∗(s)=πmaxVπ(s),∀s∈S
同理,我们定义最优动作价值函数:
Q ∗ ( s , a ) = max π Q π ( s , a ) , ∀ s ∈ S , a ∈ A Q^*(s,a)=\max_{\pi}Q^\pi(s,a),\quad\forall s\in\mathcal{S},a\in\mathcal{A} Q∗(s,a)=πmaxQπ(s,a),∀s∈S,a∈A
通过最大化 Q ∗ ( s , a ) Q^*(s,a) Q∗(s,a),我们可以找到最优策略:
π ∗ ( a ∣ s ) = { 1 i f a = a r g m a x q ∗ ( s , a ) 0 o t h e r w i s e \left.\pi_*(a|s)=\left\{\begin{array}{cc}1&\mathrm{~if~}a=\mathrm{argmax~}q_*(s,a)\\0&otherwise\end{array}\right.\right. π∗(a∣s)={10 if a=argmax q∗(s,a)otherwise
下图红色弧线箭头部分代表了最优策略的选择
同样通过递归的方式推导方程:
最优状态价值是选择此时使最优动作价值最大的那一个动作时的状态价值:
V ∗ ( s ) = max a Q ∗ ( s , a ) V_*(s)=\max_aQ_*(s,a) V∗(s)=amaxQ∗(s,a)
最优状态价值函数和最优动作价值函数之间的关系:
Q ∗ ( s , a ) = R s a + γ ∑ s ′ ∈ S P s s ′ a V ∗ ( s ′ ) Q_*(s,a)=\mathcal{R}_s^a+\gamma\sum_{s^{\prime}\in\mathcal{S}}\mathcal{P}_{ss^{\prime}}^aV_*(s^{\prime}) Q∗(s,a)=Rsa+γs′∈S∑Pss′aV∗(s′)
之后叠加,可得
V ∗ ( s ) = max a ∈ A { R s a + γ ∑ s ′ ∈ S P s s ′ a V ∗ ( s ′ ) } V_*(s)=\max_{a\in\mathcal{A}}\{\mathcal{R}_s^a+\gamma\sum_{s^{\prime}\in\mathcal{S}}\mathcal{P}_{ss^{\prime}}^aV_*(s^{\prime})\} V∗(s)=a∈Amax{Rsa+γs′∈S∑Pss′aV∗(s′)}
Q ∗ ( s , a ) = R s a + γ ∑ s ′ ∈ S P s s ′ a max a ′ ∈ A Q ∗ ( s ′ , a ′ ) Q_*(s,a)=\mathcal{R}_s^a+\gamma\sum_{s^{\prime}\in\mathcal{S}}\mathcal{P}_{ss^{\prime}}^a\max_{a^{\prime}\in\mathcal{A}}Q_*(s^{\prime},a^{\prime}) Q∗(s,a)=Rsa+γs′∈S∑Pss′aa′∈AmaxQ∗(s′,a′)
如此,我们便得到了贝尔曼最优方程(Bellman optimality equation)。
求解较大规模的马尔可夫奖励过程中的价值函数时,可以使用动态规划(dynamic programming)算法、蒙特卡洛方法(Monte-Carlo method)和时序差分(temporal difference)。本小节介绍蒙特卡洛方法(Monte-Carlo method)。
蒙特卡洛方法(Monte-Carlo methods)也被称为统计模拟方法,是一种基于概率统计的数值计算方法。运用蒙特卡洛方法时,我们通常使用重复随机抽样,然后运用概率统计方法来从抽样结果中归纳出我们想求的目标的数值估计。一个简单的例子是用蒙特卡洛方法来计算圆的面积。例如,在下图所示的正方形内部随机产生若干个点,细数落在圆中点的个数,圆的面积与正方形面积之比就等于圆中点的个数与正方形中点的个数之比。如果我们随机产生的点的个数越多,计算得到圆的面积就越接近于真实的圆的面积。
我们现在介绍如何用蒙特卡洛方法来估计一个策略在一个马尔可夫决策过程中的状态价值函数。回忆一下,一个状态的价值是它的期望回报,那么一个很直观的想法就是用策略在 MDP 上采样很多条序列,计算从这个状态出发的回报再求其期望就可以了,公式如下: V π ( s ) = E π [ G t ∣ S t = s ] ≈ 1 N ∑ i = 1 N G t ( i ) V^\pi(s)=\mathbb{E}_\pi[G_t|S_t=s]\approx\frac{1}{N}\sum_{i=1}^NG_t^{(i)} Vπ(s)=Eπ[Gt∣St=s]≈N1i=1∑NGt(i)
在一条序列中,可能没有出现过这个状态,可能只出现过一次这个状态,也可能出现过很多次这个状态。我们介绍的蒙特卡洛价值估计方法会在该状态每一次出现时计算它的回报。还有一种选择是一条序列只计算一次回报,也就是这条序列第一次出现该状态时计算后面的累积奖励,而后面再次出现该状态时,该状态就被忽略了。
假设我们现在用策略 π \pi π从状态 s s s开始采样序列,据此来计算状态价值。我们为每一个状态维护一个计数器和总回报,计算状态价值的具体过程如下所示。
代码采样部分结果
[('C2', 'Study', -2, 'Pass'), ('Pass', 'Pub', 1, 'Pass'), ('Pass', 'Pub', 1, 'Pass'), ('Pass', 'Pub', 1, 'Pass'), ('Pass', 'Study', 10, 'Sleep')]
[('C1', 'Study', -2, 'C2'), ('C2', 'Sleep', 0, 'Sleep')]
[('FB', 'Quit', 0, 'C1'), ('C1', 'Facebook', -1, 'FB'), ('FB', 'Quit', 0, 'C1'), ('C1', 'Facebook', -1, 'FB'), ('FB', 'Quit', 0, 'C1'), ('C1', 'Study', -2, 'C2'), ('C2', 'Study', -2, 'Pass'), ('Pass', 'Pub', 1, 'Pass'), ('Pass', 'Pub', 1, 'Pass')]
[('C1', 'Study', -2, 'C2'), ('C2', 'Sleep', 0, 'Sleep')]
[('C2', 'Study', -2, 'Pass'), ('Pass', 'Pub', 1, 'C2'), ('C2', 'Sleep', 0, 'Sleep')]
可以看到最后结果还是比较接近的。
使用蒙特卡洛方法计算MDP的状态价值为
{'C1': -1.6584167352261565, 'C2': 0.5744913689985154, 'Pass': 6.330419227770518, 'FB': -1.1820907116805823, 'Sleep': 0}
MDP中每个状态价值分别为
[[-1.67666232]
[ 0.51890482]
[ 6.0756193 ]
[-1.22555411]
[ 0. ]]
不同策略的价值函数是不一样的。这是因为对于同一个 MDP,不同策略会访问到的状态的概率分布是不同的。我们需要理解不同策略会使智能体访问到不同概率分布的状态这个事实,这会影响到策略的价值函数。
首先我们定义 MDP 的初始状态分布为 ν 0 ( s ) \nu_0(s) ν0(s),在有些资料中,初始状态分布会被定义进 MDP 的组成元素中。我们用 P t π ( s ) P_t^\pi(s) Ptπ(s)表示采取策略 π \pi π使得智能体在时刻 t t t状态为 s s s的概率,所以我们有 P 0 π ( s ) = ν 0 ( s ) P_0^\pi(s)=\nu_0(s) P0π(s)=ν0(s),然后就可以定义一个策略的状态访问分布(state visitation distribution):
ν π ( s ) = ( 1 − γ ) ∑ t = 0 ∞ γ t P t π ( s ) \nu^\pi(s)=(1-\gamma)\sum_{t=0}^\infty\gamma^tP_t^\pi(s) νπ(s)=(1−γ)t=0∑∞γtPtπ(s)
其中, 1 − γ 1-\gamma 1−γ是用来使得概率加和为 1 的归一化因子。状态访问概率表示一个策略和 MDP 交互会访问到的状态的分布。需要注意的是,理论上在计算该分布时需要交互到无穷步之后,但实际上智能体和 MDP 的交互在一个序列中是有限的。不过我们仍然可以用以上公式来表达状态访问概率的思想,状态访问概率有如下性质: ν π ( s ′ ) = ( 1 − γ ) ν 0 ( s ′ ) + γ ∫ P ( s ′ ∣ s , a ) π ( a ∣ s ) ν π ( s ) d s d a \nu^\pi(s')=(1-\gamma)\nu_0(s')+\gamma\int P(s'|s,a)\pi(a|s)\nu^\pi(s)dsda νπ(s′)=(1−γ)ν0(s′)+γ∫P(s′∣s,a)π(a∣s)νπ(s)dsda
此外,我们还可以定义策略的占用度量(occupancy measure): ρ π ( s , a ) = ( 1 − γ ) ∑ t = 0 ∞ γ t P t π ( s ) π ( a ∣ s ) \rho^\pi(s,a)=(1-\gamma)\sum_{t=0}^\infty\gamma^tP_t^\pi(s)\pi(a|s) ρπ(s,a)=(1−γ)t=0∑∞γtPtπ(s)π(a∣s)
它表示动作状态对 ( s , a ) (s,a) (s,a)被访问到的概率。二者之间存在如下关系: ρ π ( s , a ) = ν π ( s ) π ( a ∣ s ) \rho^\pi(s,a)=\nu^\pi(s)\pi(a|s) ρπ(s,a)=νπ(s)π(a∣s)
进一步得出如下两个定理。定理 1:智能体分别以策略 π 1 \pi_1 π1和 π 2 \pi_2 π2和同一个 MDP 交互得到的占用度量 ρ π 1 \rho^{\pi_1} ρπ1和 ρ π 2 \rho^{\pi_2} ρπ2满足 ρ π 1 = ρ π 2 ⟺ π 1 = π 2 \rho^{\pi_1}=\rho^{\pi_2}\iff\pi_1=\pi_2 ρπ1=ρπ2⟺π1=π2
定理 2:给定一合法占用度量,可生成该占用度量的唯一策略是 π ρ = ρ ( s , a ) ∑ a ′ ρ ( s , a ′ ) \pi_\rho=\frac{\rho(s,a)}{\sum_{a^{\prime}}\rho(s,a^{\prime})} πρ=∑a′ρ(s,a′)ρ(s,a)
注意:以上提到的“合法”占用度量是指存在一个策略使智能体与 MDP 交互产生的状态动作对被访问到的概率。
# Occupancy
def test04():
# 策略2
Policy_2 = {
"C1-Study": 0.6,
"C1-Facebook": 0.4,
"FB-Facebook": 0.3,
"FB-Quit": 0.7,
"C2-Study": 0.5,
"C2-Sleep": 0.5,
"Pass-Study": 0.1,
"Pass-Pub": 0.9,
}
MAXTimeStep = 8
MDP, Policy_1 = Set_MDPParameterAndPolicy()
Sequences1 = MonteCarloSampling(MDP, Policy_1, MAXTimeStep, SamplingNum=1000)
Sequences2 = MonteCarloSampling(MDP, Policy_2, MAXTimeStep, SamplingNum=1000)
# Sequences1 = sample(MDP, Policy_1, MAXTimeStep, 1000)
# Sequences2 = sample(MDP, Policy_2, MAXTimeStep, 1000)
rho1 = ComputeOccupancy("Pass", "Pub", Sequences1, MAXTimeStep, MDP)
rho2 = ComputeOccupancy("Pass", "Pub", Sequences2, MAXTimeStep, MDP)
print(rho1, rho2)
最后得到的占有度量有所不同
0.058 0.1145
import numpy as np
# 给定一条序列,计算从某个索引(起始状态)开始到序列最后(终止状态)得到的回报
def ComputeSequenceReward(Start_idx, Sequence, RewardVector, gamma=0.5):
TotalReward = 0.0
for i in reversed(range(Start_idx, len(Sequence))):
TotalReward = gamma * TotalReward + RewardVector[Sequence[i] - 1]
return TotalReward
# Exploit Bellman equation to compute value of all states
def ComputeValue(RewardVector, Statesize, TransitionMatrix, gamma=0.5):
RewardVector = np.array(RewardVector).reshape(-1, 1)
try:
Value = np.dot(np.linalg.inv(np.eye(Statesize, Statesize) - gamma * TransitionMatrix),
RewardVector)
except:
print("-------------状态转移矩阵为奇异矩阵,存在求解误差-------------")
TransitionMatrix[Statesize - 1][Statesize - 1] += 1e-7
I = np.eye(Statesize, Statesize)
Value = np.dot(np.linalg.inv(I - gamma * TransitionMatrix),
RewardVector)
return Value
def Set_MDPParameterAndPolicy():
# 状态集合
S = ["C1", "C2", "Pass", "FB", "Sleep"]
# 动作集合
A = ["Facebook", "Study", "Sleep", "Pub", "Quit"]
# 状态转移函数
P = {
"C1-Study-C2": 1.0,
"C1-Facebook-FB": 1.0,
"FB-Facebook-FB": 1.0,
"FB-Quit-C1": 1.0,
"C2-Study-Pass": 1.0,
"C2-Sleep-Sleep": 1.0,
"Pass-Study-Sleep": 1.0,
"Pass-Pub-C1": 0.2,
"Pass-Pub-C2": 0.4,
"Pass-Pub-Pass": 0.4,
}
# 奖励函数
R = {
"C1-Study": -2,
"C1-Facebook": -1,
"FB-Facebook": -1,
"FB-Quit": 0,
"C2-Study": -2,
"C2-Sleep": 0,
"Pass-Study": 10,
"Pass-Pub": 1,
}
# 折扣因子
gamma = 0.5
MDP = (S, A, P, R, gamma)
# 策略1,随机策略
Pi_1 = {
"C1-Study": 0.5,
"C1-Facebook": 0.5,
"FB-Facebook": 0.5,
"FB-Quit": 0.5,
"C2-Study": 0.5,
"C2-Sleep": 0.5,
"Pass-Study": 0.5,
"Pass-Pub": 0.5,
}
# 策略2
Pi_2 = {
"C1-Study": 0.7,
"C1-Facebook": 0.3,
"FB-Facebook": 0.3,
"FB-Quit": 0.7,
"C2-Study": 0.5,
"C2-Sleep": 0.5,
"Pass-Study": 0.2,
"Pass-Pub": 0.8,
}
return MDP, Pi_1
# 把输入的两个字符串通过“-”连接,便于使用上述定义的P、R变量
def join(str1, str2):
return str1 + '-' + str2
def MonteCarloSampling(MDP, Policy, MAXTimeStep, SamplingNum):
''' 采样函数,策略Pi,限制最长时间步MaxTimeStep,总共采样序列数SamplingNum '''
S, A, P, R, gamma = MDP
StateNum = len(S)
Sequences = []
for _ in range(SamplingNum):
Sequence = []
TimeStep = 0
# 随机选择一个除Sleep以外的状态s作为起点
s = S[np.random.randint(StateNum - 1)]
# 当前状态为终止状态或者时间步太长时,一次采样结束
while s != "Sleep" and TimeStep <= MAXTimeStep:
TimeStep += 1
rand, temp = np.random.rand(), 0
# 在状态s下根据策略选择动作
for a_ in A:
temp += Policy.get(join(s, a_), 0.0)
if temp >= rand:
a = a_
r = R.get(join(s, a_), 0.0)
break
rand, temp = np.random.rand(), 0
# 根据状态转移概率得到下一个状态s_next
for s_ in S:
temp += P.get(join(join(s, a), s_), 0.0)
if temp >= rand:
s_next = s_
break
# 把(s,a,r,s_next)元组放入序列中
Sequence.append((s, a, r, s_next))
# s_next变成当前状态,开始接下来的循环
s = s_next
Sequences.append(Sequence)
return Sequences
# 对所有采样序列计算所有状态的价值
def MonteCarloComputeValue(Sequences, MDP):
gamma = MDP[4]
V = {"C1": 0, "C2": 0, "Pass": 0, "FB": 0, "Sleep": 0}
N = {"C1": 0, "C2": 0, "Pass": 0, "FB": 0, "Sleep": 0}
for Sequence in Sequences:
G = 0
# 一个序列从后往前计算
for i in reversed(range(len(Sequence))):
s, r = Sequence[i][0], Sequence[i][2]
G = r + gamma * G
N[s] = N[s] + 1
V[s] = V[s] + (G - V[s]) / N[s]
return V
def ComputeOccupancy(s, a, Sequences, MAXTimeStep, MDP):
''' 计算状态动作对(s,a)出现的频率,以此来估算策略的占用度量 '''
gamma = MDP[4]
rho = 0
total_times = np.zeros(MAXTimeStep) # 记录每个时间步t各被经历过几次
occur_times = np.zeros(MAXTimeStep) # 记录(s_t,a_t)=(s,a)的次数
for Sequence in Sequences:
for i in range(len(Sequence)):
try:
s_, a_ = Sequence[i][0], Sequence[i][1]
total_times[i] += 1
if s_ == s and a_ == a:
occur_times[i] += 1
except IndexError:
continue
for i in reversed(range(MAXTimeStep)):
if total_times[i]:
# 用频率来估算策略的占用度量
rho = gamma ** i * occur_times[i] / total_times[i]
return (1 - gamma) * rho
def SampleTEXT():
MDP, Policy = Set_MDPParameterAndPolicy()
Sequences = MonteCarloSampling(MDP, Policy, MAXTimeStep=8, SamplingNum=5)
for Sequence in Sequences:
print(Sequence)
def MonteCarloTEXT():
MDP, Policy = Set_MDPParameterAndPolicy()
Sequences = MonteCarloSampling(MDP, Policy, MAXTimeStep=8, SamplingNum=5000)
V = MonteCarloComputeValue(Sequences, MDP)
print("使用蒙特卡洛方法计算MDP的状态价值为\n", V)
def test01():
# Define the transition Matrix
# C1 C2 C3 Pass Pub FB Sleep
P = [
[0.0, 0.5, 0.0, 0.0, 0.0, 0.5, 0.0],
[0.0, 0.0, 0.8, 0.0, 0.0, 0.0, 0.2],
[0.0, 0.0, 0.0, 0.6, 0.4, 0.0, 0.0],
[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0],
[0.2, 0.4, 0.4, 0.0, 0.0, 0.0, 0.0],
[0.1, 0.0, 0.0, 0.0, 0.0, 0.9, 0.0],
[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0]
]
P = np.array(P)
RewardVector = [-2, -2, -2, 10, 1, -1, 0]
chain = [1, 6, 6, 1, 2, 7]
start_index = 0
print("根据本序列计算得到回报为:%s。"% ComputeSequenceReward(start_index, chain, RewardVector, gamma=0.5))
print("MRP中每个状态价值分别为\n", ComputeValue(RewardVector, 7, P))
# MDP2MRP
def test02():
# Define the transition Matrix
# C1 C2 Pass FB Sleep
P_TransformMDP2MRP = [
[0.0, 0.5, 0.0, 0.5, 0.0],
[0.0, 0.0, 0.5, 0.0, 0.5],
[0.1, 0.2, 0.2, 0.0, 0.5],
[0.5, 0.0, 0.0, 0.5, 0.0],
[0.0, 0.0, 0.0, 0.0, 1.0]
]
P_TransformMDP2MRP = np.array(P_TransformMDP2MRP)
R_TransformMDP2MRP = [-1.5, -1, 5.5, -0.5, 0.0]
print("MDP中每个状态价值分别为\n", ComputeValue(R_TransformMDP2MRP, 5, P_TransformMDP2MRP, gamma=0.5))
# MonteCarlo
def test03():
# SampleTEXT(
MonteCarloTEXT()
test02()
# Occupancy
def test04():
# 策略2
Policy_2 = {
"C1-Study": 0.6,
"C1-Facebook": 0.4,
"FB-Facebook": 0.3,
"FB-Quit": 0.7,
"C2-Study": 0.5,
"C2-Sleep": 0.5,
"Pass-Study": 0.1,
"Pass-Pub": 0.9,
}
MAXTimeStep = 8
MDP, Policy_1 = Set_MDPParameterAndPolicy()
Sequences1 = MonteCarloSampling(MDP, Policy_1, MAXTimeStep, SamplingNum=1000)
Sequences2 = MonteCarloSampling(MDP, Policy_2, MAXTimeStep, SamplingNum=1000)
rho1 = ComputeOccupancy("Pass", "Pub", Sequences1, MAXTimeStep, MDP)
rho2 = ComputeOccupancy("Pass", "Pub", Sequences2, MAXTimeStep, MDP)
print(rho1, rho2)
if __name__ == "__main__":
test04()
[1] 伯禹AI
[2] https://www.deepmind.com/learning-resources/introduction-to-reinforcement-learning-with-david-silver
[3] 动手学强化学习
[4] Reinforcement Learning