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

本文涉及的产品
模型在线服务 PAI-EAS,A10/V100等 500元 1个月
交互式建模 PAI-DSW,每月250计算时 3个月
模型训练 PAI-DLC,100CU*H 3个月
简介: 本博客以实践为主,带领读者巩固上期关于“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+LLaMA Factory微调Qwen2-VL模型,搭建文旅领域知识问答机器人
使用PAI和LLaMA Factory框架,基于全参方法微调 Qwen2-VL模型,使其能够进行文旅领域知识问答,同时通过人工测试验证了微调的效果。
机器学习概览及常见算法
机器学习(Machine Learning, ML)是人工智能的核心,专门研究计算机怎样模拟或实现人类的学习行为,以获取新的知识或技能,重新组织已有的知识结构使之不断改善自身的性能,它是使计算机具有智能的根本途径,其应用遍及人工智能的各个领域。 本课程将带你入门机器学习,掌握机器学习的概念和常用的算法。
相关文章
|
编解码 监控 C++
C++音视频编程探秘
C++音视频编程探秘
573 1
|
8月前
|
机器学习/深度学习 存储 算法
强化学习:蒙特卡罗求解最优状态价值函数——手把手教你入门强化学习(五)
本文介绍了强化学习中的蒙特卡罗算法,包括其基本概念、两种估值方法(首次访问蒙特卡罗与每次访问蒙特卡罗)及增量平均优化方式。蒙特卡罗法是一种基于完整回合采样的无模型学习方法,通过统计经验回报的平均值估计状态或动作价值函数。文章详细讲解了算法流程,并指出其初期方差较大、估值不稳定等缺点。最后对比动态规划,说明了蒙特卡罗法在强化学习中的应用价值。适合初学者理解蒙特卡罗算法的核心思想与实现步骤。
436 4
|
8月前
|
机器学习/深度学习 开发框架 .NET
强化学习:Markov决策过程(MDP)——手把手教你入门强化学习(二)
本文是“手把手教你入门强化学习”系列的第二篇,重点讲解了强化学习的核心数学模型——Markov决策过程(MDP)。文章从马尔可夫性质出发,逐步引入马尔可夫过程、马尔可夫奖励过程,最终深入到马尔可夫决策过程,详细解析了状态转移、奖励机制、价值函数及贝尔曼方程等关键概念。同时,文中还介绍了策略函数、最优价值函数等内容,并指出求解强化学习问题的关键在于寻找最优策略。通过理论推导与实践结合的方式,帮助读者更好地理解强化学习基础原理。
324 4
|
8月前
|
机器学习/深度学习 算法 关系型数据库
强化学习:动态规划求解最优状态价值函数——手把手教你入门强化学习(四)
本文介绍了基于模型的强化学习算法,重点讲解动态规划(DP)。动态规划通过分解问题为子问题求解状态价值函数,利用贝尔曼期望方程迭代更新。其核心性质包括最优子结构和重叠子问题,适用于已知转移概率和奖励的MDP场景。文章回顾了前期强化学习基础,并展望了后续内容如蒙特卡罗法。适合初学者系统了解强化学习算法原理与应用。
295 7
|
10月前
|
JavaScript 前端开发 Shell
Flow-CLI 全新升级,轻松对接 Sonar 实现代码扫描和红线卡点
Flow-CLI 使用的典型场景如:自定义开发一个 Sonar 扫描步骤,以在流水中触发 Sonar 扫描,并以扫描结果作为红线卡点,以保证代码质量;对接三方自有审批平台,在发布前进行检查审批,审批通过才允许发布。接下来,我们就以对接 Sonar 服务为例,手把手教你开发一个带红线功能的 Sonar 扫描步骤。
687 125
|
7月前
|
人工智能 物联网 Android开发
【04】优雅草星云物联网AI智控系统从0开发鸿蒙端适配-deveco studio-自定义一个设置输入小部件组件-完成所有设置setting相关的页面-优雅草卓伊凡
【04】优雅草星云物联网AI智控系统从0开发鸿蒙端适配-deveco studio-自定义一个设置输入小部件组件-完成所有设置setting相关的页面-优雅草卓伊凡
396 92
|
8月前
|
人工智能 JavaScript Java
在IDEA中借助满血版 DeepSeek 提高编码效率
通义灵码2.0引入了DeepSeek V3与R1模型,新增Qwen2.5-Max和QWQ模型,支持个性化服务切换。阿里云发布开源推理模型QwQ-32B,在数学、代码及通用能力上表现卓越,性能媲美DeepSeek-R1,且部署成本低。AI程序员功能涵盖表结构设计、前后端代码生成、单元测试与错误排查,大幅提升开发效率。跨语言编程示例中,成功集成DeepSeek-R1生成公告内容。相比1.0版本,2.0支持多款模型,丰富上下文类型,具备多文件修改能力。总结显示,AI程序员生成代码准确度高,但需参考现有工程风格以确保一致性,错误排查功能强大,适合明确问题描述场景。相关链接提供下载与原文参考。
884 160
在IDEA中借助满血版 DeepSeek 提高编码效率
|
8月前
|
机器学习/深度学习 弹性计算 搜索推荐
QwQ-32B一键部署,真正的0代码,0脚本,0门槛
阿里云发布的QwQ-32B模型通过强化学习显著提升了推理能力,核心指标达到DeepSeek-R1满血版水平。用户可通过阿里云系统运维管理(OOS)一键部署OpenWebUI+Ollama方案,轻松将QwQ-32B模型部署到ECS,或连接阿里云百炼的在线模型。整个过程无需编写代码,全部在控制台完成,适合新手操作。
1636 176
QwQ-32B一键部署,真正的0代码,0脚本,0门槛
|
8月前
|
机器学习/深度学习 人工智能 算法
强化学习:Gym的库的实践——小车上山(包含强化学习基础概念,环境配置国内镜像加速)——手把手教你入门强化学习(一)
本文开启“手把手教你入门强化学习”专栏,介绍强化学习基础概念及实践。强化学习通过智能体与环境交互,学习最优策略以最大化累积奖励,适用于复杂动态决策问题。文章讲解智能体、环境等核心概念,并使用Gym库进行案例实操,如CartPole和MountainCar环境的代码实现。最后预告下期将深入马尔科夫决策过程(MDP)。适合初学者系统了解强化学习并动手实践。创作不易,欢迎关注、点赞与收藏!
1043 4
下一篇
oss云网关配置