强化学习:实践理解Markov决策过程(MDP)(干中学系列)——手把手教你入门强化学习(三)

本文涉及的产品
模型训练 PAI-DLC,100CU*H 3个月
交互式建模 PAI-DSW,每月250计算时 3个月
模型在线服务 PAI-EAS,A10/V100等 500元 1个月
简介: 本博客以实践为主,带领读者巩固上期关于“Markov决策过程”的核心概念。通过构建学生马尔可夫奖励模型、计算收获值与状态价值,进一步验证贝尔曼方程。详细介绍了转移概率、奖励值及策略概率的设置,并实现了均匀随机策略下的状态价值计算与最优策略的价值评估。结合代码实例,帮助读者深入理解强化学习理论。适合初学者实践与进阶学习。

前言

 本博客将带领大家编程进行实践,巩固上期博客中Markov决策过程的核心概念。新来的友友还不是很了解强化学习的,可以看笔者的前两期博客,看完后,你一定会有所收获的,话不多说,咱们直接干中学吧!!!

前期回顾


强化学习:基础知识篇(包含Gym库的简单实践)——手把手教你入门强化学习(一)
强化学习:Markov决策过程(MDP)——手把手教你入门强化学习(二)

一、收获和价值的计算

image.png

图中给出了定义学生马尔可夫奖励过程所需要的信息。其中,状态集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 个方程写成向量形式:
image.png

根据矩阵乘法定义,右侧第二项为(P是(n,n)转移矩阵,v 是(n,1)列向量,因此简化为:
image.png
继续推导我们可以得出状态价值的计算公式:
image.png

编写一个计算状态价值的方法,代码如下:

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 由下图我们构建学生马尔可夫决策过程(转移概率、奖励值、状态价值和策略概率)
image.png

(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)
公式如下:
image.png

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下某一状态的价值
公式如下:
image.png

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 计算最优策略下最优状态价值
上期公式如下:
image.png

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 计算最优行为价值
有了最优状态价值,我们可以依据上期讲的公式来计算最优行为价值
image.png

# 验证最优行为价值
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

参考文献

强化学习入门:从原理到实践 叶强 闫维新 黎斌


创作不易,求关注,点赞,收藏,谢谢~
相关实践学习
使用PAI-EAS一键部署ChatGLM及LangChain应用
本场景中主要介绍如何使用模型在线服务(PAI-EAS)部署ChatGLM的AI-Web应用以及启动WebUI进行模型推理,并通过LangChain集成自己的业务数据。
机器学习概览及常见算法
机器学习(Machine Learning, ML)是人工智能的核心,专门研究计算机怎样模拟或实现人类的学习行为,以获取新的知识或技能,重新组织已有的知识结构使之不断改善自身的性能,它是使计算机具有智能的根本途径,其应用遍及人工智能的各个领域。 本课程将带你入门机器学习,掌握机器学习的概念和常用的算法。
相关文章
|
6月前
|
存储 Kubernetes 持续交付
k8s学习
【10月更文挑战第1天】
207 4
|
程序员
学习学习再学习
学习学习再学习
184 0
|
NoSQL Java jenkins
【学习总结】总结
【学习总结】总结
|
前端开发 NoSQL Java
如何学习?今天聊聊关于学习
如何学习?今天聊聊关于学习
202 0
|
存储 缓存 监控
pmq再学习一
这个过程首先是创建主题,有了主题,创建消费组,然后基于消费组这个大前提,执行订阅操作,订阅需要进行消费的主题信息,然后在订阅的基础上,进行队列的分配。在这个过程中会执行元数据的变更和重平衡操作。而这些可以从审计日志中获取打印日志中可以看到很清楚。 下一篇,我们来看生产者和消费者的细节。生产者和消费者在执行操作前会执行一个操作init初始化操作,而这个初始化操作会将信息注册到将信息注册到MqClient中,因为只有将其进行统一管理的时候,在创建客户端对象的时候才会方便管理,同时方便调用,此时会启动心跳服务,此时少不了还有一个重要的操作就是注册消费组,同时需要关注一个点就是长轮询操作。
168 0
pmq再学习一
|
存储 弹性计算 负载均衡
学习资料
个人记录学习资料
|
弹性计算 Linux 虚拟化
选择正确,不断学习
对于学计算机的,对于我的专业,学习并掌握Linux操作系统是必须的,但是一开始在自己的电脑用VMware在自己的电脑搭建虚拟机学习,但是这样会导致自己的计算机变得很卡,因为会占用主机很大的内存。在我的老师的引荐下,认识了阿里云服务器,而且他推荐我们去参加“飞天加速计划,高校学生在家实践活动”,那样可以先体验阿里云服务器ECS,看看是否适合自己。于是我便去完成了练习和答题拿到了体验资格。
|
弹性计算 算法 小程序
我是自愿学习的
沉迷学习 日渐消瘦
我是自愿学习的
|
Android开发
最新学习发现的好文章
1、即可学院关于 View 动画和属性动画的详解,对于使用方法讲解的超详细,里面也涉及了一部分的源码,如果想查看源码的话还是自己动手丰衣足食吧。这些文章主要都是用法和基本原理。Android 动画详解;
757 0
|
存储 Shell API
鱼书学习小结(一)
1 安装环境 python -V #查看版本 pip -V #查看版本 pip list #查看列表 makedir fisher #新建项目目录 pip install pipenv #进入项...

热门文章

最新文章

下一篇
oss创建bucket