前言
本博客将带领大家编程进行实践,巩固上期博客中Markov决策过程
的核心概念。新来的友友还不是很了解强化学习的,可以看笔者的前两期博客,看完后,你一定会有所收获的,话不多说,咱们直接干中学吧!!!
前期回顾
强化学习:基础知识篇(包含Gym库的简单实践)——手把手教你入门强化学习(一)
强化学习:Markov决策过程(MDP)——手把手教你入门强化学习(二)
一、收获和价值的计算
图中给出了定义学生马尔可夫奖励过程所需要的信息。其中,状态集S有7个状态,状态转换概率如果用矩阵的形式则将是一个7×7的矩阵,奖励函数可以用7个标量来表示,分别表示离开某一个状态得到的即时奖励值。
1 建立马尔可夫奖励模型
import numpy as np # 需要用到NumPy包
num_states = 7
i_to_n = {
"0":"C1", # 索引到状态名的字典
"1":"C2",
"2":"C3",
"3":"Pass",
"4":"Pub",
"5":"FB",
"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 # 衰减因子
至此,学生马尔可夫奖励过程就建立好了。接下来我们要建立一个函数,用来计算一个状态序列中某一状态的收获。由于收获值是针对某一状态序列里某一状态的,因此传递给这个方法的参数需要有一个马尔可夫链、要计算的状态以及衰减系数值。
2 计算收获
我们运用上期讲的以下公式来计算。
$$G_{t}=R_{t+1}+\gamma R_{t+2} + \dots = \sum_{k=0}^ \infty\gamma^{k}R_{t+k+1}$$
def compute_return(start_index=0, chain=None, gamma=0.5) -> float:
'''计算一个马尔可夫奖励过程中某状态的收获值
Args:
start_index 要计算的状态在链中的位置
chain 要计算的马尔可夫过程
gamma 衰减系数
Returns:
retrn 收获值
'''
retrn, power, gamma = 0.0, 0, gamma
for i in range(start_index, len(chain)):
retrn += np.power(gamma, power) * rewards[n_to_i[chain[i]]]
power += 1
return retrn
我们简单验证一下这个函数。
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"]
]
compute_return(0, chains[3], gamma = 0.5)
# 将输出:-3.196044921875
3 求解状态的价值
公式如下:
$$v(s)=R_{s}+\gamma \sum_{s' \in S}P_{ss'}v(s')$$
将所有 n 个方程写成向量形式:
根据矩阵乘法定义,右侧第二项为(P是(n,n)转移矩阵,v 是(n,1)列向量,因此简化为:
继续推导我们可以得出状态价值的计算公式:
编写一个计算状态价值的方法,代码如下:
def compute_value(Pss, rewards, gamma = 0.05):
'''通过求解矩阵方程的形式直接计算状态的价值
Args:
P状态转移概率矩阵shape(7, 7)
rewards即时奖励列表
gamma衰减系数
Return
values各状态的价值
'''
# assert(gamma >= 0 and gamma <= 1.0)
# 将rewards转为NumPy数组并修改为列向量的形式
rewards = np.array(rewards).reshape((-1,1))
# np.eye(7,7)为单位矩阵,inv方法为求矩阵的逆
values=np.dot(np.linalg.inv(np.eye(7,7)-gamma*Pss),rewards)
return values
values = compute_value(Pss, rewards, gamma = 0.99999) print(values)
# 将输出:
# [[-12.54296219]
# [ 1.4568013 ]
# [ 4.32100594]
# [ 10. ]
# [ 0.80253065]
# [-22.54274676]
# [ 0. ]]
二、验证贝尔曼方程
1 首先我们需要先编写一个工具文件utils.py,后面构建需要用到。
def str_key(*args):
'''将参数用"_"连接起来作为字典的键,需注意参数本身可能会是元组类型或者列表类型,
比如类似((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)
PS:str_key函数将传过来的参数作为字典。
2 由下图我们构建学生马尔可夫决策过程(转移概率、奖励值、状态价值和策略概率)
(1)转移概率、奖励值
# 导入工具函数:根据状态和行为生成操作相关字典的键,显示字典内容
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 # 衰减因子
# 根据学生马尔可夫决策过程示例的数据设置状态转移概率和奖励,默认概率为1
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) # 浏览手机中-浏览手机->-1
set_reward(R, S[0], A[2], 0) # 浏览手机中-离开浏览->0
set_reward(R, S[1], A[0], -1) # 第一节课-浏览手机->-1
set_reward(R, S[1], A[1], -2) # 第一节课-学习->-2
set_reward(R, S[2], A[1], -2) # 第二节课-学习->-2
set_reward(R, S[2], A[4], 0) # 第二节课-退出学习->0
set_reward(R, S[3], A[1], 10) # 第三节课-学习->10
set_reward(R, S[3], A[3], +1) # 第三节课-泡吧->-1
MDP = (S, A, R, P, gamma)
(2)设置策略概率
我们需要先指定一个策略π,这里考虑使用均匀随机策略(Uniform Random Policy),也就是在某状态下所有可能的行为被选择的概率相等,对于每一个状态只有两种可能行为的该学生马尔可夫决策过程来说,每个可选行为的概率均为0.5。
# S = ['浏览手机中','第一节课','第二节课','第三节课','休息中']
# A = ['浏览手机','学习','离开浏览','泡吧','退出学习']
# 设置行为策略:pi(a|.) = 0.5
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)
# 初始时价值为空,访问时会返回0
print("----状态转移概率字典(矩阵)信息:----")
V = {
}
display_dict(V)
# 将输出如下结果:
# ----状态转移概率字典(矩阵)信息:----
# 第二节课_学习: 0.50
# 第三节课_学习: 0.50
# 第二节课_退出学习: 0.50
# 浏览手机中_浏览手机: 0.50
# 第三节课_泡吧: 0.50
# 第一节课_浏览手机: 0.50
# 第一节课_学习: 0.50
# 浏览手机中_离开浏览: 0.50
#
# ----状态转移概率字典(矩阵)信息:----
3 计算在状态s时采取了行为a的价值q(s,a)
公式如下:
def compute_q(MDP, V, s, a):
'''根据给定的MDP,价值函数V,计算状态行为对“s,a”的价值qsa
'''
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
4 计算给定策略Pi下某一状态的价值
公式如下:
def compute_v(MDP, V, Pi, s):
'''给定MDP下,依据某一策略Pi和当前状态价值函数V来计算某状态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
5 计算均匀随机策略下该学生马尔可夫决策过程的最终状态函数
# 根据当前策略使用回溯法来更新状态价值
def update_V(MDP, V, Pi):
'''给定一个MDP和一个策略,更新该策略下的价值函数V
'''
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):
'''使用n次迭代计算来评估一个MDP在给定策略Pi下的状态价值,初始时价值为V
'''
for i in range(n):
V = update_V(MDP, V, Pi)
#display_dict(V)
return V
V=policy_evaluate(MDP, V, Pi, 100)
display_dict(V)
# 将输出如下结果:
# 第一节课: -1.31
# 第三节课: 7.38
# 浏览手机中: -2.31
# 第二节课: 2.69
# 休息中: 0.00
我们来计算一下在均匀随机策略下状态“第三节课”的最终价值,写入下面的代码:
v = compute_v(MDP, V, Pi, "第三节课")
print("第三节课在当前策略下的最终价值为:{:.2f}".format(v))
# 将输出如下结果:
# 第三节课在当前策略下的最终价值为:7.38
5 计算最优策略下最优状态价值
上期公式如下:
def compute_v_from_max_q(MDP, V, s):
'''根据一个状态下所有可能的行为价值中最大的一个来确定当前状态价值
'''
S, A, R, P, gamma = MDP
v_s = -float('inf')
for a in A:
qsa = compute_q(MDP, V, s, a)
if qsa >= v_s:
v_s = qsa
return v_s
6 计算最优行为价值
有了最优状态价值,我们可以依据上期讲的公式来计算最优行为价值
# 验证最优行为价值
s, a = "第三节课", "泡吧"
q = compute_q(MDP, V_star, "第三节课", "泡吧")
print("在状态{}选择行为{}的最优价值为:{:.2f}".format(s,a,q))
# 输出结果如下:
# 在状态“第三节课”选择行为“泡吧”的最优价值为9.40
总结
本期为代码实践环节,从构建学生马尔可夫奖励过程到构建学生马尔可夫决策过程,我们能更好的理解上期所讲述的内容。
如果想要更深入强化学习的内容,关注我,下期更精彩,感兴趣的友友也可以关注我的csdn账号。
https://blog.csdn.net/qq_53769632?spm=1000.2115.3001.5343
参考文献
强化学习入门:从原理到实践 叶强 闫维新 黎斌
创作不易,求关注,点赞,收藏,谢谢~