【强化学习】动态规划算法实践

文章目录

  • 【强化学习】动态规划算法实践
    • 一. 实验过程
      • 1.1 Environment
      • 1.2 Policy Iteration
      • 1.3 Policy Evaluation
      • 1.4 Policy Improvement
      • 1.5 Value Iteration
    • 二. 实验结果与分析
      • 2.1 分析Policy Iteration和Value Iteration收敛误差随着迭代次数的分布曲线

【强化学习】动态规划算法实践

阅读本文的前置条件:

前置知识概念:状态价值函数 v v v、动作价值函数 p p p
前置方法概念:

  1. 策略迭代Policy Iteration(内含策略评估Policy Evaluation + 策略优化Policy Improvement)
  2. 价值迭代Value Iteration

实验环境如下:16个格子,走到左上或者右下角即为结束。否则持续行走,每走一步奖励-1。
在这里插入图片描述

给定初始随机策略和状态价值函数 v 0 v_0 v0

在这里插入图片描述

针对上述Gridworld问题编码实现:

  • 使用 Policy Evaluation的方法计算 v π v_\pi vπ
  • 用Policy Iteration方法搜索最优 v ∗ v_* v p ∗ p_* p
  • 用Value Iteration方法搜索最优 v ∗ v_* v p ∗ p_* p

一. 实验过程

本节实验过程将分为环境准备策略迭代策略评估策略提升价值迭代五个部分。

1.1 Environment

由PPT中的Gridworld问题:

  • 4*4的网格,左上和右下角为目标点。
  • 每次移动reward = -1,到达目标点无需移动。
    • 如果移动越界,则位置不变。

我们定义如下Gridworld类表示网格世界。

class GridWorld:
    """ 网格世界,坐标系原点(0,0) 
    """

    def __init__(self, ncol=4, nrow=4):
        self.ncol = ncol  # 定义网格世界的列
        self.nrow = nrow  # 定义网格世界的行
        self.target_grids = [(0, 0), (ncol-1, nrow-1)]  # 存储目标区域
        # 状态数量
        self.state_nums = ncol * nrow
        # 4种动作, actions[0]:上,actions[1]:下, actions[2]:左, actions[3]:右。
        self.actions = [[0, -1], [0, 1], [-1, 0], [1, 0]]
        # 状态转移概率矩阵P[state][action] = [(p, next_state, reward, done)]包含下一个状态和奖励
        # state 是一维表示的
        self.P = self.init_P()

其中,除了基本的行和列之外,我们定义上下左右的四个动作,以及状态转移概率矩阵P。P矩阵表示在状态state中采取动作action后,多大的概率获得多少奖励以及进入下一个状态,是否结束。

由于本次实验环境中一种行动对应的下一个状态只有一种,且定义了除目标点之外的移动均会获得-1的奖励。故此处的p固定为1,对应下节Bellman方程中的 p ( s ′ , r ∣ s , a ) p(s',r|s,a) p(s,rs,a)

为便于计算,我们将二维的4*4网格状态通过一维的线性表示。如 s t a t e = ( x , y ) state = (x,y) state=(xy)可以表示为 s t a t e = x ∗ n c o l + y state = x * ncol + y state=xncol+y

状态转移概率矩阵初始化如下:

    def init_P(self):
        # 初始化
        P = [[[] for j in range(4)] for i in range(self.state_nums)]
        for i in range(self.nrow):
            for j in range(self.ncol):
                # 遍历四个方向
                for a in range(4):
                    # 采取行动后进入的状态s'、奖励r都是固定的概率1
                    psa = 1
                    # 位置在目标状态,因为不需要继续交互,任何动作奖励都为0
                    if (i, j) in self.target_grids:
                        P[i * self.ncol +
                            j][a] = [(psa, i * self.ncol + j, 0, True)]
                        continue
                    # 其他位置(边界越界后依旧保持不动)
                    next_x = min(self.ncol - 1, max(0, j + self.actions[a][0]))
                    next_y = min(self.nrow - 1, max(0, i + self.actions[a][1]))
                    next_state = next_y * self.ncol + next_x
                    reward = -1
                    done = False
                    # 如果下一个位置在目标状态,Done
                    if (next_x, next_y) in self.target_grids:
                        done = True
                    P[i * self.ncol + j][a] = [(psa, next_state, reward, done)]
        return P

1.2 Policy Iteration

b) 用Policy Iteration方法搜索最优 v ∗ 、  p ∗ v_*、\ p_* v p

策略迭代算法的过程如下:对当前的策略进行策略评估,得到其状态价值函数,然后根据该状态价值函数进行策略提升以得到一个更好的新策略,接着继续评估新策略、提升策略……直至最后收敛到最优策略

我们判定,若迭代过程中,旧策略等于新策略,则视为收敛,并且此时的策略即是 p ∗ p* p

同理当 v k + 1 v_{k+1} vk+1 v k v_k vk相同(相差小于阈值θ)时,它就是贝尔曼最优方程的不动点,对应最优状态价值函数 v ∗ v* v

P o l i c y I t e r a t i o n Policy Iteration PolicyIteration过程如下:

在这里插入图片描述

P o l i c y I t e r a t i o n Policy Iteration PolicyIteration 框架代码如下:

    def policy_iteration(self):  # 策略迭代
        # V及pi的初始化已完成(具体赋值见1.3)
        while 1:
            self.policy_evaluation()
            old_pi = copy.deepcopy(self.pi)  # 将列表进行深拷贝,方便接下来进行比较
            new_pi = self.policy_improvement()
            if old_pi == new_pi:
                break

其中1.3节将具体说明policy_evaluation、1.4节将具体说明policy_improvment。

1.3 Policy Evaluation

a) 用本PPT讲的 Policy Evaluation的方法计算 v π v_\pi vπ

对于状态价值函数的Bellman方程如下:
v π ( s ) = ∑ a π ( a ∣ s ) ∑ s ′ , r p ( s ′ , r ∣ s , a ) [ r + γ v π ( s ′ ) ] v_\pi(s) = \sum_a\pi(a|s)\sum_{s',r}p(s',r|s,a)[r+\gamma v_\pi(s')] vπ(s)=aπ(as)s,rp(s,rs,a)[r+γvπ(s)]
由此可知我们后续实现自举方式时所用到的状态价值函数递推式:
v k + 1 ( s ) = ∑ a π ( a ∣ s ) ∑ s ′ , r p ( s ′ , r ∣ s , a ) [ r + γ v k ( s ′ ) ] v_{k+1}(s) = \sum_a\pi(a|s)\sum_{s',r}p(s',r|s,a)[r+\gamma v_k(s')] vk+1(s)=aπ(as)s,rp(s,rs,a)[r+γvk(s)]

为此,我们实现的基本步骤如下:

  1. 初始化值函数

    对于所有的初始状态s,我们只需要设置其状态价值v为0即可。

  2. 迭代更新状态价值函数

    对于每一个状态s,我们使用贝尔曼方程计算新的值函数new_v。

  3. 检查收敛

    检查状态价值函数的更新是否收敛于我们定义的阈值θ,若收敛则停止迭代。最新的new_v即为所求。

由上述递推式可知,我们需要预先知道并且定义好如下变量:

  • A:表示动作空间。a表示特定的动作。在本次实验中动作空间定义如下:
    a c t i o n s = [ [ 0 , − 1 ] , [ 0 , 1 ] , [ − 1 , 0 ] , [ 1 , 0 ] ] actions = [[0, -1], [0, 1], [-1, 0], [1, 0]] actions=[[0,1],[0,1],[1,0],[1,0]]
    分别表示上下左右四个方向的行动。

  • π ( a ∣ s ) \pi(a|s) π(as):策略在状态s下采取行动a的概率。我们定义在初始策略中,每一个状态下等概率随机采取行动,即:
    π = [ [ 0.25 , 0.25 , 0.25 , 0.25 ] ∗ s t a t e   n u m s ] \pi = [[0.25, 0.25, 0.25, 0.25] * state\ nums] π=[[0.25,0.25,0.25,0.25]state nums]

  • P ( s ′ , r ∣ s , a ) P(s',r|s,a) P(s,rs,a):策略在状态s下采取行动a后进入状态s’以及获得奖励r的概率。由于本次实验环境较为简单,采取行动后进入的状态s’只有一种,且采取行动a获取的奖励r也是固定为-1(除了目标点之外),故可直接视为1。

  • γ \gamma γ:未来的折扣因子。在当前实验中,我们设置为1。

  • v k ( s ) v_k(s) vk(s):s状态下的价值函数。在初始化后,我们定义每个状态初始都是价值为0。
    v = [ 0 ] ∗ s t a t e   n u m s v = [0] * state\ nums v=[0]state nums

  • θ:收敛阈值。用于判定状态价值函数是否收敛。我们设置为0.001。

至此,我们可以构建Policy Evaluation方法来计算 v π v_\pi vπ,代码如下:

    def policy_evaluation(self):  # 策略评估
        state_nums = self.env.state_nums
        while 1:
            max_diff = 0
            new_v = [0] * state_nums # 初始化值函数
            for s in range(state_nums):
                qsa_list = [] # 状态s下的所有Q(s,a)价值
                # 尝试这个状态的所有动作
                for a in range(4):
                    qsa = 0
                    for res in self.env.P[s][a]:
                        p, next_state, r, done = res
                        # 应用贝尔曼方程的右侧部分
                        qsa += p * (r + self.gamma * self.v[next_state])
                    qsa_list.append(self.pi[s][a] * qsa)
                # 最后累加即得到v_{k+1}
                new_v[s] = sum(qsa_list)
                max_diff = max(max_diff, abs(new_v[s] - self.v[s]))
            self.v = new_v  # 自举
            if max_diff < self.theta:
                break  # 满足收敛条件,退出评估迭代

为了验证正确性,我们在每次自举之后输出状态价值函数,输出结果(部分)如下:

在这里插入图片描述

我们取第二次输出的(0,1)位置上的-1.750 进行验证:
− 1.750 = [ 0.25 ∗ ( − 1 + 1 ∗ ( − 1 ) ) ] + [ 0.25 ∗ ( − 1 + 1 ∗ ( − 1 ) ) ] + [ 0.25 ∗ ( − 1 + 1 ∗ ( − 1 ) ) ] + [ 0.25 ∗ ( − 1 + 1 ∗ ( 0 ) ) ] \\ -1.750 = [0.25*(-1 + 1*(-1))] + [0.25*(-1 + 1*(-1))]+ \\ [0.25*(-1 + 1*(-1))] + [0.25*(-1 + 1*(0))] 1.750=[0.25(1+1(1))]+[0.25(1+1(1))]+[0.25(1+1(1))]+[0.25(1+1(0))]
符合贝尔曼递推式。

1.4 Policy Improvement

策略提升的基本步骤为:

  1. 初始化新策略

    • 对每个状态s,初始化一个新的动作a。
  2. 对每个状态进行策略改进

    • 对每个状态 s
      • 对所有可能的动作 a
        • 计算选择动作 a 后的价值 Q(s*,*a)
  3. 选择最优动作

    • 对于每个状态s ,选择使得 Q(s,a) 最大的动作 a 。
  4. 更新策略

    • 使用新的动作作为当前策略,得到改进后的策略。

      (由于可能存在多个相同价值的动作,所以我们将相同数量的最大动作价值转为概率,赋值给新的策略pi。

    # 策略提升
    def policy_improvement(self):
        state_nums = self.env.state_nums
        for s in range(state_nums):
            qsa_list = []
            for a in range(4):
                qsa = 0
                for res in self.env.P[s][a]:
                    p, next_state, r, done = res
                    qsa += p * (r + self.gamma * self.v[next_state])
                qsa_list.append(qsa)
            maxq = max(qsa_list)
            cntq = qsa_list.count(maxq)  # 计算有几个动作得到了最大的Q值
            # 让这些动作均分概率
            self.pi[s] = [1 / cntq if q == maxq else 0 for q in qsa_list]
            print(f"state: {s}  pi: {self.pi[s]}")
        print("策略提升完成")
        return self.pi

我们打印当前策略在每个状态下的价值以及智能体可采取的动作。

参考《动手学强化学习》输出示例,我们使用一个箭头序列来表示可能采取的行动。o表示不采取行动。例如:

  • ^v<>:表示等概率采取上下左右。
  • ^o<o:表示等概率采取向左和向上两种动作。
  • ooo>:表示在当前状态只采取向右动作。
  • oooo:表示不采取行动。(目标点才有)

在这里插入图片描述

结束第一轮策略评估后:我们得到如下的状态价值,并且在策略提升后,可以看到每个状态点会朝着具有最大状态价值的节点行走的策略。

(策略数组表示选择 [上,下,左,右] 行动的概率。)

在这里插入图片描述

策略评估与策略提升交替迭代3次后,收敛输出如下:

在这里插入图片描述

1.5 Value Iteration

Policy Iteration需要等待policy evaluation完成,使得状态价值函数收敛后才进行Policy Improvement。然后随着状态增多,策略评估的收敛速度变慢,我们不必每次都等待状态价值函数完全收敛才提升策略。

Value Iteration的核心思想就是,我们只进行一次policy evaluation,而不必等待状态价值函数收敛。

但是需要注意的是,价值迭代中不存在显式的策略,我们只维护一个状态价值函数。

相比于策略迭代的递推式:
v k + 1 ( s ) = ∑ a π ( a ∣ s ) ∑ s ′ , r p ( s ′ , r ∣ s , a ) [ r + γ v k ( s ′ ) ] v_{k+1}(s) = \sum_a\pi(a|s)\sum_{s',r}p(s',r|s,a)[r+\gamma v_k(s')] vk+1(s)=aπ(as)s,rp(s,rs,a)[r+γvk(s)]
价值迭代递推式如下:
v k + 1 ( s ) = max ⁡ a ∑ s ′ , r p ( s ′ , r ∣ s , a ) [ r + γ v k ( s ′ ) ] v_{k+1}(s) = \max_a\sum_{s',r}p(s',r|s,a)[r+\gamma v_k(s')] vk+1(s)=amaxs,rp(s,rs,a)[r+γvk(s)]

算法流程如下:

在这里插入图片描述

初始化及循环代码如下:

在这里插入图片描述

结束循环后,计算最优的确定性策略(若出现多个最大值,则等概率取最大。)

在这里插入图片描述

在经历4次迭代后,收敛到与策略迭代相同的最优解:

在这里插入图片描述

二. 实验结果与分析

(其余内容分析较烂,就只展示收敛误差吧。)

2.1 分析Policy Iteration和Value Iteration收敛误差随着迭代次数的分布曲线

我们使用wandb库进行可视化状态价值函数的收敛情况,在策略迭代中的三次交替过程中,收敛误差随迭代次数分布如下:

在这里插入图片描述

而价值迭代曲线如下:

在这里插入图片描述

分析可知,在首次策略评估中,由于初始化的是随机等概率策略,故评估误差的收敛十分缓慢,而在收敛后执行策略提升后得到了较优解,策略将朝着最优解迅速收敛。

反观价值迭代误差曲线,在前三次迭代中,状态价值函数的变化量都是1,这意味着每次迭代中至少有一个状态的价值发生了显著变化(靠近目标点的状态)。最后一次迭代中,状态价值函数的变化量为0,表示算法已经收敛到最优解。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/190235.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Python——常见内置模块

Python 模块&#xff08;Modules&#xff09;1、概念模块函数类变量2、分类3、模块导入的方法&#xff1a;五种4、使用import 导入模块5、使用from……import部分导入6、使用as关键字为导入模块或功能命名别名7、模块的搜索目录8、自定义模块 常见内置模块一、math模块二、rand…

Excel中出现“#NAME?”怎么办?(文本原因)

excel 单元格出现 #NAME? 错误的原因有二&#xff1a; 函数公式输入不对导致 #NAME? 错误。 在单元格中字符串的前面加了号&#xff0c;如下图中的--GoJG7sEe6RqgTnlUcitA&#xff0c;本身我们想要的是--GoJG7sEe6RqgTnlUcitA&#xff0c;但因为某些不当的操作在前面加了号&…

第 373 场 LeetCode 周赛题解

A 循环移位后的矩阵相似检查 模拟 class Solution { public:bool areSimilar(vector<vector<int>> &mat, int k) {int m mat.size(), n mat[0].size();k % n;auto g mat;for (int i 0; i < m; i)if (i & 1)rotate(mat[i].begin(), mat[i].begin() …

实战oj题——用队列实现栈

前言&#xff1a;Leetcode栈和队列的习题&#xff0c;用两个队列实现栈。 【由于我们是用C语言完成这道题&#xff0c;所以我们要将关于队列的实现代码插入到题中&#xff0c;在创建一个栈&#xff0c;栈里包含两个队列。】 思路&#xff1a;我们用两个队列来实现&#xff0c;因…

Sringboot3 讲解

文章目录 前言一、Springboot快速入门1.1 实例1.2 总结&#xff1a;1.2.1 什么是starter启动器1.2.2 SpringBootApplication注解的功效 二、springboot3 统一配置文件1.概述2、属性配置文件使用简单案例3、yaml配置介绍和说明4、批量配置文件的读取5、多环境配置和激活 三、spr…

【详解二叉树】

&#x1f320;作者&#xff1a;TheMythWS. &#x1f387;座右铭&#xff1a;不走心的努力都是在敷衍自己&#xff0c;让自己所做的选择&#xff0c;熠熠发光。 目录 树形结构 概念 树的示意图 树的基本术语 树的表示 树的应用 二叉树(重点) 二叉树的定义 二叉树的五…

2.前端--HTML标签基本概念【2023.11.25】

1.基本语法规范 HTML 标签是由尖括号包围的关键词&#xff0c;例如 <html>。HTML 标签通常是成对出现的&#xff0c;例如 和 &#xff0c;我们称为双标签。有些特殊的标签必须是单个标签&#xff08;极少情况&#xff09;&#xff0c;例如 <br />我们称为单标签。 …

无人零售已成为新兴趋势

无人零售已成为新兴趋势 在新零售浪潮中&#xff0c;必然会涌现新的商业形态&#xff0c;而无人零售则是其中典型代表之一。传统零售受制于人力和场地等限制&#xff0c;消费者体验较差&#xff0c;如长时间排队、缓慢结账、距离过远等问题。而无人零售解决方案&#xff0c;包括…

人力资源管理后台 === 权限应用

目录 1.权限应用-拆分静态路由-动态路由 2.权限应用-根据用户权限添加动态路由 3.权限应用-根据权限显示左侧菜单 4.权限应用-退出登录重置路由 5.权限应用-功能权限-按钮权限标识 6.权限应用-自定义指令应用功能权限 7.其他模块-集成 8.首页-基本结构和数字滚动 9.首页…

【高可用架构】Haproxy 和 Keepalived 的区别

Haproxy 和 Keepalived 的区别 1.负载均衡器介绍2.Haproxy 和 Keepalived 的基本概念和特点2.1 Haproxy2.2 Keepalived 3.Haproxy 和 Keepalived 的区别3.1 功能上的区别3.2 架构上的区别3.3 配置上的区别 4.总结 1.负载均衡器介绍 负载均衡器是一种解决高并发和高可用的常用的…

Linux C语言 22-多进程

Linux C语言 22-进程 本节关键字&#xff1a;进程、exec函数族 相关C库函数&#xff1a;fork、getpid、getppid、getuid、geteuid、getgid、getegid、execl、execlp、execv、execvp、execle、execvpe 什么是进程&#xff1f; 进程是程序的执行过程&#xff1b;进程是动态的&…

vue3+ts mitt的使用

安装mitt :npm i mitt -Smain.ts: import mitt from mittconst Mit mitt();declare module vue {export interface ComponentCustomProperties{$Bus:typeof Mit} } app.config.globalProperties.$BusMit在A组件中使用 <template><div><h1>我是A<…

SSD FTL 映射管理

映射的种类 根据映射粒度的不同分为如下几种 1.块映射 一个逻辑块&#xff08;logical block&#xff09;映射到一个闪存物理块(physical block) 优点是&#xff1a;映射表需要空间小&#xff0c;对大Block size顺序读写&#xff0c;但是对小尺寸数据的写入性能很差。因为即使…

性能测试必看系列之Jmeter:硬件性能监控指标

硬件性能监控指标 一、性能监控初步介绍 性能测试的主要目标 1.在当前的服务器配置情况&#xff0c;最大的用户数 2.平均响应时间ART&#xff0c;找出时间较长的业务 3.每秒事务数TPS&#xff0c;服务器的处理能力 性能测试涉及的内容 1.客户端性能测试&#xff1a;web前…

【JavaSE】:数据类型

数据类型 一.总体概论二.java里与c的区别1.float2.char3.boolen 三.类型转换四.String类型 一.总体概论 在Java中数据类型主要分为两类&#xff1a;基本数据类型和引用数据类型。 不论是在16位系统还是32位系统&#xff0c;int都占用4个字节&#xff0c;long都占8个字节 。 整…

怎么给数据库某个字段建立一个前缀索引

说明&#xff1a;SQL调优中重要的一个环节是建立索引&#xff0c;其中有一条是字段值过长字段应该建立前缀索引&#xff0c;即根据字段值的前几位建立索引&#xff0c;像数据库中的密码字段、UUID字段。 因为其随机性&#xff0c;其实根据前几位就可以锁定某一条记录了。前缀索…

kafka开发环境搭建

文章目录 1 安装java环境1.1 下载linux下的安装包1.2 解压缩安装包1.3 解压后的文件移到/usr/lib目录下1.4 配置java环境变量 2 kafka的安装部署2.1 下载安装kafka2.2 配置和启动zookeeper2.3 启动和停止kafka 1 安装java环境 1.1 下载linux下的安装包 &#xff08;1&#xf…

2024年天津天狮学院专升本市场营销专业《市场营销学》考试大纲

2024年天津天狮学院专升本市场营销专业高职升本入学考试《市场营销学》考试大纲 一、考试性质 《市场营销学》专业课程考试是天津天狮学院市场营销专业高职升本入学考试的必 考科目之一&#xff0c;其性质是考核学生是否达到了升入本科继续学习的要求而进行的选拔性考试。《市…

RabbitMQ之延迟消息实战

RabbitMQ之延迟消息实战 使用死信交换机实现延迟消息 使用死信交换机的过期时间以及没有消费者进行消费&#xff0c;时间到了就会到死信队列中&#xff0c;由此可以实现延迟消息使用延迟消息插件 前提&#xff1a;需要mq配置插件 延时信息案例实战 把一个30分钟的延迟消息可以…

基于SpringBoot+Redis的前后端分离外卖项目-苍穹外卖(八)

套餐模块功能开发 1. 新增套餐1.1 需求分析和设计1.1.1产品原型&#xff1a;1.1.2接口设计&#xff1a;1.1.3数据库设计&#xff1a; 1.2 代码开发1.2.1 DishController层1.2.2 DishService接口类1.2.3 DishServiceImpl接口实现类1.2.4 DishMapper层1.2.5 DishMapper.xml1.2.6 …