马尔可夫决策过程
本文未经许可禁止转载,如需转载请联系笔者
0. 前言
马尔可夫决策过程
是强化学习中重要的概念之一,但是在介绍马尔可夫决策过程之前,需要先了解马尔可夫过程
,和马尔可夫奖励过程
,它们是依次递进的关系。
1. 马尔科夫过程(Markov Process)
- 在一个时序过程中,如果
t
+
1
t+1
t+1 时刻的状态仅取决于
t
t
t 时刻的状态
S
t
S_t
St 而与
t
t
t 时刻之前的任何状态都无关时,则认为
t
t
t 时刻的状态
S
S
S 具有
马尔科夫性(Markov property)
。 - 若过程中的每一个状态都具有马尔科夫性,则这个过程具备马尔科夫性。具备了马尔科夫性的随机过程称为
马尔科夫过程(Markov process)
,又称马尔科夫链(Markov chain)。
马尔可夫过程的关键在于状态转移概率:
从上式也可以看到,下一时刻的状态
S
t
+
1
S_{t+1}
St+1仅与当前时刻的状态
S
t
S_t
St有关,而与
S
1
,
⋯
,
S
t
−
1
S_1,\cdots,S_{t-1}
S1,⋯,St−1无关。注意:这里的记号非常严谨,
S
t
S_t
St,
S
t
+
1
S_{t+1}
St+1代表时刻的状态,而
s
s
s,
s
′
s'
s′代表某一种具体的状态类型,比如看书,上课等。
为了描述整个状态空间中不同类型状态之间的关系,用矩阵表示,即为:
显然状态转移概率矩阵P的规模是所有状态类型数n的平方。
表述一个马尔可夫过程,只需要元组<S,P>即可。
下图为一个典型的马尔可夫过程
不难写出状态转移概率矩阵P:
MP中的三个概念:
状态序列(episode):
一个状态转换过程,包含很多状态。如:吃饭->睡觉->吃饭
完整的状态序列(complete episode):
状态序列的最后一个状态是终止状态
采样(sample):
从符合马尔科夫过程给定的状态转移概率矩阵生成一个状态序列的过程
2. 马尔可夫奖励过程(Markov Reward Process)
表述一个马尔可夫奖励过程,需要元组<S,P,R,
γ
\gamma
γ>
其中:
下图是一个典型的马尔可夫奖励过程,它就是在马尔可夫过程中增加了“Reward”这一项,以及下图中没有表示的奖励衰减因子
γ
\gamma
γ.
注意:在马尔科夫奖励过程中,“reward”指的是离开一个状态或者进入一个状态的奖励(用来评价状态的好坏),但是在后面的马尔可夫决策过程中“reward”指的是一个动作的奖励(用来评价动作的优劣),而状态的好坏通过价值函数来判断。
收获(Return):
是一个马尔科夫奖励过程中从某一个状态
S
t
S_t
St 开始采样直到终止状态时所有奖励的有衰减的之和。数学表达式如下:
在这里
G
t
G_t
Gt表示状态
S
t
S_t
St的收获,
R
t
+
1
R_{t+1}
Rt+1代表离开状态
S
t
S_t
St时获得的奖励(在马尔科夫决策过程中
R
t
+
1
R_{t+1}
Rt+1又有不同含义)。
价值(value):
是马尔科夫奖励过程中状态收获的期望。其数学表达式为︰
因为从状态
s
s
s到下一状态
s
′
s'
s′有很多可能,产生不同的状态序列,所以需要用期望来表示状态
s
s
s的好坏,而
v
v
v是状态的函数,因此
v
(
s
)
v(s)
v(s)又被称为状态价值函数
,它建立了从状态到价值的映射。
将价值函数的表达式展开:
最后一个等号是依据全期望公式证明得到,这里省略证明过程(详见链接.)。
因此可以得到当前时刻的状态价值与下一时刻状态价值的关系(这是一个递归式,从后往前递归):
这就是 马尔科夫奖励过程 中的贝尔曼方程(Bellman equation)
。因为
R
t
+
1
R_{t+1}
Rt+1是离开状态
S
t
S_t
St得到的奖励,所以在这里
R
s
=
R
t
+
1
=
c
o
n
s
t
a
n
t
R_s=R_{t+1}=\rm constant
Rs=Rt+1=constant
用图表示,即为:
举例:
显然,上面只是假设“4.3”那个状态的价值不知道,其他状态价值都已知,然后做的一个计算,实际上应该是所有状态的价值都不知道,然后要求所有状态的价值,因此上面只是示意而已。
根据贝尔曼方程,我们只有知道后续状态的价值,才能求出当前状态的价值。如果状态之间的关系是一棵树,我们能够叶节点开始往根节点求,但如果不是树,那需要不断迭代求解,后面会讲到求解方法。但是对于马尔可夫奖励过程,很幸运的是,它的贝尔曼方程是线性方程,可以写成矩阵的形式,即:
因此,只要知道P和R,通过矩阵运算就能得到每个状态的价值,但是矩阵求逆运算量过大,如果状态类型数过多,此方法就不现实了。
3. 马尔可夫决策过程
所谓决策,就是需要有agent的参与,也就是动作。因此表示一个马尔可夫决策过程需要五元组:<S,A,P,R, γ \gamma γ>
- S S S 是一个有限状态集
- A A A 是一个有限行为集
- P P P 是集合中基于行为的状态转移概率矩阵: P s s ′ a = E [ S t + 1 = s ′ ∣ S t = s , A t = a ] P_{s s^{\prime}}^{a}=\mathbb{E}\left[S_{t+1}=s' \mid S_{t}=s, A_{t}=a\right] Pss′a=E[St+1=s′∣St=s,At=a]
- R R R 是基于状态和行为的奖励函数: R s a = E [ R t + 1 ∣ S t = s , A t = a ] R_{s}^{a}=\mathbb{E}\left[R_{t+1} \mid S_{t}=s, A_{t}=a\right] Rsa=E[Rt+1∣St=s,At=a]
- γ \gamma γ 是一个衰减因子: γ ∈ [ 0 , 1 ] \gamma \in[0,1] γ∈[0,1]
策略:
个体在给定状态下从行为集中选择一个行为的依据,用字母
π
\pi
π表示。策略描述的是个体的行为产生机制,它是一个概率分布:
下图是一个典型的马尔可夫决策过程。可以看到在一个状态下,其动作可以有多个,而且同一状态下做同一动作也可能转移到不同的状态(比如:“泡吧”动作)。下图中奖励
R
s
a
=
R
t
+
1
R^{a}_s=R_{t+1}
Rsa=Rt+1是一个定值,但是如果
R
t
+
1
R_{t+1}
Rt+1是进入(或者离开)一个状态的奖励值,那么
R
s
a
R^{a}_s
Rsa是一个期望值。
注意:策略是整个状态转移的规则,在一局“游戏”中是不变的。上图显示的各种动作是一种策略,“随机运动”也是一种策略。
在一种固定策略下,我们能够计算:状态 S S S转移到状态 S ′ S' S′的状态转移概率 P s s ′ π P^{\pi}_{ss'} Pss′π,与状态 S S S下的奖励 R s π R^{\pi}_s Rsπ:
状态价值函数(state-value function):
v
π
(
s
)
v_{\pi}(s)
vπ(s)是在马尔科夫决策过程下基于策略
π
\pi
π的状态价值函数
行为价值函数(action-value function):
为了描述同一状态下采取不同行为的价值,我们定义一个基于策略
π
\pi
π的行为价值函数
q
π
(
s
,
a
)
q_{\pi}(s,a)
qπ(s,a)
状态价值函数和行为价值函数有着密切的联系,即:
和
将上面两式组合起来可得:
或者
根据马尔科夫奖励过程中的贝尔曼方程,可以得到 马尔科夫决策过程 中的贝尔曼方程(也可以叫贝尔曼期望方程,Bellman Expectation Equation)
,有两个,而这两个就是上面展示的树状图,重新整理一下写成期望形式,即为:
最优状态价值函数(optimal value function):
是所有策略下产生的众多状态价值函数中的最大者:
最优行为价值函数(optimal action-value function):
是所有策略下产生的众多行为价值函数中的最大者:
我们说 策略
π
\pi
π优于
π
′
\pi'
π′(
π
\pi
π ≥
π
′
\pi'
π′),即代表对于有限状态集里的任意一个状态
s
s
s,不等式:
v
π
(
s
)
v_{\pi}(s)
vπ(s)≥
v
π
′
(
s
)
v_{\pi'}(s)
vπ′(s)成立。可以看到这是每个状态的价值都是更优(或相等)。
对于任何马尔可夫决策过程,存在一个 最优策略 π ∗ \pi_{*} π∗ 优于或至少不差于所有的其它策略。最优策略可能不止一个,但是最优策略下对应的状态价值函数一定是最优状态价值函数,即: v π ∗ ( s ) v_{\pi_*}(s) vπ∗(s)= v ∗ ( s ) v_{*}(s) v∗(s);行为价值函数一定是最优行为价值函数,即: q π ∗ ( s , a ) q_{\pi_*}(s,a) qπ∗(s,a)= q ∗ ( s , a ) q_{*}(s,a) q∗(s,a).
最优策略可以通过最大化 最优行为价值函数 q ∗ ( s , a ) q_*(s,a) q∗(s,a) 来获得,即如果得到了 q ∗ ( s , a ) q_*(s,a) q∗(s,a),那么对于每个状态 s s s,我们只采用 a = argmax a ∈ A q ∗ ( s , a ) a=\underset{a \in A}{\operatorname{argmax}} q_{*}(s, a) a=a∈Aargmaxq∗(s,a)的动作,而其他动作被采取的概率为0,这样走下来,一定是最优策略,用数学表达式写即为:
表面上看,每次采取动作时,只关注了当前状态
s
s
s,而之后的状态并没有关注,有陷入局部最优的嫌疑,但其实
q
∗
(
s
,
a
)
q_*(s,a)
q∗(s,a)早已把后续的状态价值考虑进去了,它是一个期望,因此不会陷入局部最优。
最优贝尔曼方程:
状态
s
s
s的最优状态价值 就等于 在状态
s
s
s下采用最优动作的最优行为价值。(因为其他动作被采取的概率为0)
因为状态价值函数和动作价值函数存在联系,所以上述最优贝尔曼方程还有以下形式:
两式整合,合并为一个递推关系式,即有:
和
上面的四个式子都可以理解为是最优贝尔曼方程, 本质上都是等价的。
4. 编程实践
接下来将以David Silver课件中的“学生马尔可夫过程”为例,讲述如何用python实现里面的案例,以此强化对本章概念的理解。
4.1 马尔可夫奖励过程
我们来分析如何计算下图中每个状态的价值。图中的奖励代表离开某一状态获得的即时奖励。
存储信息:
- 状态,有7个状态,用字典存储。
- 奖励,有7个奖励值,用列表存储。
- 状态转移概率,7*7,用矩阵存储,到达不了的状态,其转移概率记为0.
计算方法:
- 矩阵求逆法。马尔可夫奖励过程的贝尔曼方程是线性的,在状态数不多的情况下,可以通过矩阵求逆计算。
- 动态规划法。通过随机设置初始的状态价值(一般都设为0),然后循环迭代,直至所有状态价值的数值不再变化。(这肯定是要求衰减因子为小于等于1的非负数)
首先来验证David中PPT的以下结果,即四个状态序列的累积奖励:
编写代码:
import numpy as np
num_states = 7
# {"0": "C1", "1":"C2", "2":"C3", "3":"Pass", "4":"Pub", "5":"FB", "6":"Sleep"}
i_to_n = {} # 索引到状态名的字典
i_to_n["0"] = "C1"
i_to_n["1"] = "C2"
i_to_n["2"] = "C3"
i_to_n["3"] = "Pass"
i_to_n["4"] = "Pub"
i_to_n["5"] = "FB"
i_to_n["6"] = "Sleep"
n_to_i = {} # 状态名到索引的字典
for i, name in zip(i_to_n.keys(), i_to_n.values()):
n_to_i[name] = int(i)
Pss = [ # 状态转移概率
[ 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 ]
]
Pss = np.array(Pss)
rewards = [-2, -2, -2, 10, 1, -1, 0]
gamma = 0.5
def compute_return(rewards, start_index = 0, chain = None, gamma = 0.5) -> float:
''' 马尔可夫奖励过程中某个状态的累积奖励
输入:马尔可夫链的起始角标,马尔可夫链(用字符串数组表示的),衰减系数,状态奖励
输出:某一状态的累积奖励
'''
power, MReturn = 0, 0
for i in range(start_index, len(chain)):
MReturn = MReturn + np.power(gamma, power)*rewards[n_to_i[chain[i]]]
# 这句话就说明,它是按状态来算奖励的,进入(或者说离开)这个状态就获得这个状态的奖励,而不是从某一状态过渡到另一状态时获得的奖励
power += 1
return MReturn
chains =[
["C1", "C2", "C3", "Pass", "Sleep"],
["C1", "FB", "FB", "C1", "C2", "Sleep"],
["C1", "C2", "C3", "Pub", "C2", "C3", "Pass", "Sleep"],
["C1", "FB", "FB", "C1", "C2", "C3", "Pub", "C1", "FB",\
"FB", "FB", "C1", "C2", "C3", "Pub", "C2", "Sleep"]
]
for i in range(4):
output = compute_return(rewards = rewards, start_index=0, chain=chains[i], gamma = 0.5)
print(output)
输出结果:
接下来再来求解当
γ
=
0.9
\gamma=0.9
γ=0.9时,各个状态的价值:
在上述代码中添加如下代码:
def compute_value(Pss, rewards, gamma = 0.05):
''' 马尔可夫奖励过程中某个状态的价值(累积奖励的期望)
输入:状态转移概率,即时奖励序列,衰减系数
输出:某一状态的价值
'''
rewards = np.array(rewards).reshape((-1,1))
values = np.dot(np.linalg.inv(np.eye(7,7) - gamma * Pss), rewards)
return values
values = compute_value(Pss, rewards, gamma = 0.9)
print(values)
运行后得:
4.2 马尔可夫决策过程
David使用贝尔曼期望方程计算了如下马尔可夫决策过程:
但实际上,这个图表述还不够清楚,重新整理成下图:
上图的特点是,把动作交代地很清楚,可以看到除了“泡吧”,其他动作下的状态转移都是唯一确定的。
这里采用动态规划法进行求解(在马尔可夫决策过程中,如果想用矩阵来求解,那需要考虑动作和状态的数量,计算量大幅增加,所以通常用动态规划求解)。马尔可夫决策过程的动态规划还没讲述,下一章将进行讲述。
首先新建一个文件,导入如下函数,并命名为utils.py
def str_key(*args):
'''将参数用"_"连接起来作为字典的键,需注意参数本身可能会是tuple或者list型,
比如类似( ( a , b,c ) ,d)的形式。
'''
new_arg = []
for arg in args:
if type(arg) in [tuple, list]:
new_arg += [str(i) for i in arg]
else:
new_arg.append(str(arg))
return "_".join(new_arg)
def set_dict(target_dict, value, *args):
target_dict[str_key(*args)] = value
def set_prob(P, s, a, s1, p = 1.0): #设置概率字典
set_dict(P, p, s, a, s1)
def get_prob(P, s, a, s1): #获取概率值
return P.get(str_key(s,a,s1), 0)
def set_reward(R, s, a, r): #设置奖励值
set_dict(R, r, s, a)
def get_reward(R, s, a): #获取奖励值
return R.get(str_key(s,a), 0)
def display_dict(target_dict): #显示字典内容
for key in target_dict.keys():
print("{}: {:.2f}".format(key, target_dict[key]))
print("")
def set_value(V, s, v): #设置价值字典
set_dict(V, v, s)
def get_value(V, s): #获取价值字典
return V.get(str_key(s), 0)
def set_pi(Pi, s, a, p = 0.5): #设置策略字典
set_dict(Pi, p, s, a)
def get_pi(Pi, s, a): #获取策略(概率)值
return Pi.get(str_key(s,a), 0)
然后写主函数
# 导入工具函数︰根据状态和行为生成操作相关字典的键,显示字典内容
from utils import str_key, display_dict
# 设置转移概率、奖励值以及读取它们方法
from utils import set_prob, set_reward, get_prob, get_reward
# 设置状态价值、策略概率以及读取它们的方法
from utils import set_value, set_pi, get_value, get_pi
# 构建学生马尔科夫决策过程
S = ['浏览手机中','第一节课','第二节课','第三节课','休息中']
A = ['浏览手机','学习','离开浏览','泡吧','退出学习']
R = {} # 奖励Rsa字典
P = {} # 状态转移概率Pss'a字典
gamma = 1.0 # 衰减因子
set_prob(P, S[0], A[0], S[0])
set_prob(P, S[0], A[2], S[1])
set_prob(P, S[1], A[0], S[0])
set_prob(P, S[1], A[1], S[2])
set_prob(P, S[2], A[1], S[3])
set_prob(P, S[2], A[4], S[4])
set_prob(P, S[3], A[1], S[4])
set_prob(P, S[3], A[3], S[1], p = 0.2)
set_prob(P, S[3], A[3], S[2], p = 0.4)
set_prob(P, S[3], A[3], S[3], p = 0.4)
set_reward(R, S[0], A[0], -1)
set_reward(R, S[0], A[2], 0)
set_reward(R, S[1], A[0], -1)
set_reward(R, S[1], A[1], -2)
set_reward(R, S[2], A[1], -2)
set_reward(R, S[2], A[4], 0)
set_reward(R, S[3], A[1], 10)
set_reward(R, S[3], A[3], 1)
MDP = (S, A, R, P, gamma)
print("----状态转移概率字典(矩阵)信息∶----")
display_dict(P)
print("----奖励字典(函数)信息∶----")
display_dict(R)
Pi = {}
set_pi(Pi, S[0], A[0], 0.5)
set_pi(Pi, S[0], A[2], 0.5)
set_pi(Pi, S[1], A[0], 0.5)
set_pi(Pi, S[1], A[1], 0.5)
set_pi(Pi, S[2], A[1], 0.5)
set_pi(Pi, S[2], A[4], 0.5)
set_pi(Pi, S[3], A[1], 0.5)
set_pi(Pi, S[3], A[3], 0.5)
print("----动作执行概率字典:----")
display_dict(Pi)
print("----价值函数字典:----")
V = {}
display_dict(V)
def compute_q(MDP, V, s, a):
S, A, R, P, gamma = MDP
q_sa = 0
for s_prime in S:
q_sa += get_prob(P, s,a,s_prime) * get_value(V, s_prime)
q_sa = get_reward(R, s,a) + gamma * q_sa
return q_sa
def compute_v(MDP, V, Pi, s):
S, A, R, P, gamma = MDP
v_s = 0
for a in A:
v_s += get_pi(Pi, s,a) * compute_q(MDP, V, s, a)
return v_s
#根据当前策略使用回溯法来更新状态价值,本章不做要求
def update_V(MDP, V, Pi):
S, _, _, _, _ = MDP
V_prime = V.copy()
for s in S:
#set_value(V_prime, s, V_S(MDP, V_prime, Pi, s))
V_prime[str_key(s)] = compute_v(MDP, V_prime, Pi, s)
return V_prime
def policy_evaluate(MDP, V, Pi, n):
for i in range(n):
V = update_V(MDP, V, Pi)
#display_dict(V)
return V
# print(MDP[4])
V = policy_evaluate(MDP, V, Pi, 100)
display_dict(V)
v = compute_v(MDP, V, Pi, "第三节课")
print("第三节课在当前策略下的最终价值:{:.2f}".format(v))
计算后得:
参考文献:
- David Silver强化学习视频.
- 叶强《强化学习入门——从原理到实践》