图解强化学习 |动态规划方法(有模型)
欢迎来到图解强化学习的世界博客主页卿云阁欢迎关注点赞收藏⭐️留言首发时间2026年3月27日✉️希望可以和大家一起完成进阶之路作者水平很有限如果发现错误请留言轰炸哦万分感谢目录动态回归核心思想和原理核心思想最优性原理 (Principle of Optimality)动态规划和强化学习的关系策略迭代算法步骤策略评估策略改进策略迭代价值迭代算法步骤价值函数估计价值函数的更新价值迭代和策略迭代的对比代码的实现策略迭代策略评估策略改进策略迭代算法价值迭代动态回归核心思想和原理核心思想将原始问题划分为若干个重叠子问题递归求解每个子问题都要是最优解分解性大问题分解为小问题且符合“最优性原理”重叠性子问题之间必须存在重叠递归性函数或算法本身调用自身的过程最优性原理 (Principle of Optimality)一个过程的最优策略具有这样的性质无论其初始状态及初始决策如何对于前面决策所造成的某一状态而言其后各阶段的决策序列必须构成最优策略。用“迷宫寻宝”例子大白话解释如果整条路 A to B to C 是最优的那么 B to C 这半段也必须是从B 出发到终点的最优路径。动态规划的基本步骤(1)划分子问题解决的方法是一样的2解决子问题3基础解比如现在要解决一个求n!的例子划分子问题如果求5不好求的话可以求出43之类的。子问题的逻辑和原问题是一模一样的只是规模变小了。求解子问题这是“状态转移”的过程把局部的解推导成全局的解。基础解在阶乘里1!明确等于 1在之前的迷宫里终点宝藏就是基础解它的价值是已知的 10。def factorial(n): # (3) 基础解 (Base Case) # 它是递归停止的条件也是所有复杂问题的“根基” if n 1: return 1 # (1) 划分子问题 (Subproblem) # 想求 n!先求 (n-1)! sub_problem_result factorial(n - 1) # (2) 解决子问题用子问题的解构造主问题的解 # n! n * (n-1)! return n * sub_problem_result # 测试 n 4 print(f{n} 阶乘的值是{factorial(n)})动态规划和强化学习的关系动态规划的发展历史比强化学习要早应用范围要广强化学习早期大量借鉴了动态规划的思想动态规划方法通常假设环境模型已知理想国中的完美人生。强化学习着力解决在不确定性环境中的决策问题。在动态规划的框架下两者的关系可以看作是对贝尔曼方程Bellman Equation不同的求解路径策略迭代策略迭代就是不断进行策略评估和策略改进来更新策略。对于强化学习来说如果环境已知的条件下子问题就有两个:1求策略价值函数或者是动作价值函数。2求策略pi也就是我们下面要介绍的策略评估和策略改进。策略迭代就是不断进行策略评估和策略改进来更新策略。算法步骤初始化随机策略或确定性策略 π迭代循环策略评估贝尔曼方程计算价值函数或动作价值函数开会提建议策略改进根据价值函数更新策略改善性能改进方案迭代新策略再次用于评估重复上述步骤稳定的最优策略策略评估计算当前策略下的策略价值函数或动作价值函数例子假设智能体在一个4*4的网格中移动。离散状态空间每个网格代表一个状态图中是一个 4 *4 的网格共 16 个格子。动作空间上、下、左、右。转移概率确定的1 或 0即你往左走就 100% 进左边的格子不会滑倒或走偏。边界处理无法越界时保持原地撞墙后不扣分也不动。折扣因子0.9越远的奖励价值越低。初始策略随机策略AI 刚开始像没头苍蝇一样乱撞每个方向概率 0.25。奖励值人为设定包括目标状态奖励、步数惩罚、障碍物惩罚等。第一行是方法的选择包括策略评估和策略改进其中灰色的网格是障碍物。中间有个格子的奖励是1那就是目标点还有9个格子是-1。起始位置是任何非障碍物的格子。研究的目标是找到最优的路径从起始的位置到目标的位置并获得最大的累计奖励。每点击一次策略评估按钮就会进行一次计算第一次计算之后可以看到一部分格子获得了即时的奖励绿色的是正值红色的是负值。进行40次迭代之后格子的值不再发生变化。GridWorld: Dynamic Programming Demo策略改进根据价值函数更新策略进而提高智能体的性能。Q表格常见的数据结构用于存储和更新动作价值函数Q值。每个单元格表示一个状态State和动作Action的组合存储相应的Q 值。行代表不同的状态列代表不同的动作。策略改进的效果最后所有的网格都做出自己的动作贝尔曼最优方程最佳策略下的状态价值等于这个状态下采取最好的动作得到的动作价值。策略迭代适用条件环境模型已知状态转移概率和即时奖励函数即已知 MDP 模型。状态空间和动作空间较小规模大则存储和计算开销高。迭代次数可接受每次都要更新价值函数和策略并与环境交互。最优策略精确能够保证收敛。价值迭代子问题分解价值评估Value Evaluation求价值函数价值更新Value Update更新价值函数进而影响动作选择策略迭代强调是策略pi价值迭代是以价值为中心的视角每步迭代中也会更新动作但是直接根据价值选择动作。这个过程是确定性的不需要更新一个策略函数。算法步骤初始化状态值函数 V(s) 为任意初始值可以是全零或随机值。迭代循环价值评估贝尔曼期望方程计算价值函数。价值更新最大化操作max 操作。重复上述两步直到收敛。提取最优策略当价值函数不再变化时反向推导动作。价值函数估计价值函数的更新max返回是100argmax返回是3价值迭代和策略迭代的对比价值迭代计算效率高但收敛速度较慢尤其状态空间较大时策略迭代更耗时但能更快地收敛到最优策略具体的例子策略迭代每次都需要重新计算整个价值函数价值迭代每次都是在前一次的基础上直接更新价值函数适用条件环境模型已知每个状态转移的概率和奖励信号离散状态和离散动作空间状态和动作都是可枚举的长期累积奖励的优化考虑未来回报而不仅是即时奖励代码的实现简介链接https://www.gymlibrary.dev/environments/toy_text/frozen_lake/pym的安装pip install -i https://pypi.tuna.tsinghua.edu.cn/simple gymnasium[toy_text]创建初始环境行动空间Discrete(4)观测空间Discrete(16)进口gymnasium.make(FrozenLake-v1)目标冰湖是指从起点穿越冰冻湖面到目标且不掉进任何洞穴 走过结冰的湖面。 由于冰冻湖面的滑溜特性玩家可能并不总是朝预定方向移动。游戏描述 (Description)起点与终点玩家从4 *4网格的左上角 [0,0] 出发目标是到达右下角的终点 [3,3]。地形分布地图上分布着冰穴Holes。如果是预设地图位置固定如果是随机地图位置随机但随机地图保证一定有一条能通往终点的路。游戏结束条件玩家到达终点胜利或掉进冰穴失败时本轮游戏结束。摩擦力Slippery除非显式禁用is_slipperyFalse否则冰面是湿滑的玩家有时会向预定方向的垂直方向滑动。动作空间 (Action Space)动作是一个整数取值范围为 {0, 1, 2, 3}分别代表0: 向左移动 (Move Left)1: 向下移动 (Move Down)2: 向右移动 (Move Right)3: 向上移动 (Move Up)观测空间 (Observation Space)观测值是一个代表玩家当前位置的整数计算公式为例如在 4 *4的地图中终点 [3, 3] 的观测值就是 3*4 3 15。奖励机制 (Rewards)默认的奖励规则如下到达终点1掉进冰穴0踩在冰面上0# 策略评估方法通过迭代计算来估算给定策略条件下的状态值函数 def policy_evaluation(V, policy, gamma, theta): while True: # 初始化一个delta值用来衡量在每轮循环后值函数的改变程度 delta 0 # 遍历环境中的每个状态s for s in range(env.observation_space.n): # 初始化一个变量v存储计算出的状态值 v 0 # 遍历当前状态下所有可能的动作及其相应的概率 for action, action_prob in enumerate(policy[s]): # 对于每个动作查看环境所有可能的结果包括概率、下一个状态、奖励以及是否结束 for prob, next_state, reward, done in env.P[s][action]: if done: # 如果这个动作将导致游戏结束也就没有下一个状态了则只关心当前的奖励 v action_prob * prob * reward else: # 否则要考虑当前的奖励和下一个状态的折扣预期值 v (action_prob * prob * (reward gamma * V[next_state])) # 更新delta delta max(delta, np.abs(v - V[s])) # 更新状态s的值函数 V[s] v # 如果所有状态s的值函数变化都小于设定的阈值则退出循环 if delta theta: break策略迭代策略评估策略评估方法通过迭代计算来估算给定策略条件下的状态值函数V (状态值函数数组)通常初始化为全 0。V [0.0, 0.0, 0.0, ..., 0.0] 共16个0policy (策略矩阵)[16, 4]状态数 *动作数[[0.25, 0.25, 0.25, 0.25], # 状态 0上下左右各 25% 概率[0.25, 0.25, 0.25, 0.25], # 状态 1...[0.25, 0.25, 0.25, 0.25] # 状态 15]gamma (折扣因子)是一个数字。theta (收敛阈值极小的正数,当V表中所有格子的变化都小于这个值的时候就结束循环了。场景 1计算终点门前的格子 (S14)s 14v 0policy[14] [0, 0, 1.0, 0]读到动作 0, 1, 3 时因为 action_prob 0直接跳过乘积为 0。读到动作 2向右时action_prob 1.0。右走环境 env.P[14][2] 返回的信息是100% 几率prob1.0到达状态 15next_state15拿到奖励 1 分reward1.0并且游戏结束doneTrue。v 1.0 * 1.0 * 1.0 动作概率*环境转移概率*奖励V[14] 1.0场景 2计算离终点稍远的格子 s 13v 0 只有动作 2向右的概率是 1.0 env.P[13][2] 返回100% 几率prob1.0到达状态 14next_state14此时还没通关奖励 0 分reward0.0游戏未结束doneFalse。触发 else: 分支核心逻辑v 1.0 * 1.0 * (0.0 0.9 * V[14])由于 V[14] 刚才已经被算出来是1.0 了代入进去v 0.9 * 1.0 0.9。更新变量V[13] 0.9策略改进# 策略改进方法尽可能找到最优策略 def policy_improvent(V, policy, gamma): # 初始化标识位表示是否已找到最优策略 optimal True # 遍历环境中的每个状态 for s in range(env.observation_space.n): # 保存当前策略以便稍后进行比较 old_policy policy[s].copy() # 初始化一个数组q_s用于保存给定状态下所有动作的期望回报 q_s np.zeros(env.action_space.n) # 对于每个可能的动作分别计算其期望回报 for action in range(env.action_space.n): # 对于每个动作查看环境所有可能的结果包括概率、下一个状态、奖励以及是否结束 for prob, next_state, reward, done in env.P[s][action]: if done: # 如果这个动作将导致游戏结束也就没有下一个状态了则只关心当前的奖励 q_s[action] prob * reward else: # 否则还是要考虑当前的奖励和下一个状态的折扣预期值 q_s[action] (prob * (reward gamma * V[next_state])) # 根据每个动作的期望回报选择最佳动作 best_action np.argmax(q_s) # 更新策略 policy[s] np.eye(env.action_space.n)[best_action] # 如果新策略与原策略不同则将标识位设为False if np.any(old_policy ! policy[s]): optimal False # 返回是否找到最优策略 return optimal上面的policy_evaluation已经把 V表身价表算准了。现在我们要用这张表来纠正 AI 的错误行为。当前已知身价V[14] 1.0终点隔壁V[13] 0.914的隔壁。13号格子时它总是习惯向左走动作 0。也就是说旧策略是policy[13] [1.0, 0.0, 0.0,0.0]。保存旧账本old_policy [1.0, 0.0, 0.0, 0.0]4 个动作的Q 值q_s[0.0, 0.0, 0.0, 0.0] 这对应 4 个动作的Q 值。贪婪法则np.argmax 会挑出数组里最大值的索引号。最大值是 0.9它的索引是 2。所以best_action 2向右走。更新策略矩阵policy[13][0.0, 0.0, 1.0, 0.0]策略迭代算法# 策略迭代算法不断进行策略评估和策略改进 def policy_iteration(gamma 0.99, max_iteration 10000, theta 0.0001): # 初始化状态值函数为0形状为当前环境中状态数量 V np.zeros(env.observation_space.n) # 初始化策略为随机策略即每个状态下每个动作的选择概率相等 policy np.ones([env.observation_space.n, env.action_space.n]) / env.action_space.n # 循环最大迭代次数 for i in range(max_iteration): # 评估当前策略更新状态值函数 policy_evaluation(V, policy, gamma, theta) # 对当前策略进行改进获取是否找到了最优策略 optimal policy_improvent(V, policy, gamma) # 如果找到了最优策略则跳出循环 if optimal: break # 返回状态值函数和策略 return V, policy价值迭代def get_policy(V, gamma): # 初始化策略形状为环境中状态数量和动作数量 policy np.zeros((env.observation_space.n, env.action_space.n)) # 遍历每一个状态 for s in range(env.observation_space.n): # 对每个状态初始化一个动作值函数 q_s np.zeros(env.action_space.n) # 对每种可能的动作进行循环 for a in range(env.action_space.n): # 获取在给定的状态和动作下环境的变化情况 for prob, next_state, reward, done in env.P[s][a]: if done: # 如果这个动作将导致游戏结束也就没有下一个状态了则只关心当前的奖励 q_s[a] prob * reward else: # 否则还是要考虑当前的奖励和下一个状态的折扣预期值 q_s[a] (prob * (reward gamma * V[next_state])) # 选择期望回报最高的动作作为最佳动作 best_action np.argmax(q_s) # 更新策略使其在当前状态下选择最佳动作 policy[s] np.eye(env.action_space.n)[best_action] # 返回策略 return policy智能体站在状态 10。V[14] 1.0V[9] 0.81V[6] 0.81q_s [0.0, 0.0, 0.0, 0.0]q_s [0.729, 0.9, 0.0, 0.729]policy[10] [0.0, 1.0, 0.0, 0.0]# 价值迭代 def value_iteration(gamma, max_iteration, theta): # 初始化状态值函数为0形状为环境中状态数量 V np.zeros(env.observation_space.n) # 循环最大迭代次数 for i in range(max_iteration): # 初始化一个delta值用于保存状态值函数在这次迭代中的最大改变量 delta 0 # 遍历每一个状态 for s in range(env.observation_space.n): # 对每个状态初始化一个动作值函数 q_s np.zeros(env.action_space.n) # 对每种可能的动作进行循环 for action in range(env.action_space.n): # 获取在给定的状态和动作下环境的变化情况 for prob, next_state, reward, done in env.P[s][action]: if done: # 如果这个动作将导致游戏结束也就没有下一个状态了则只关心当前的奖励 q_s[action] prob * reward else: # 否则还是要考虑当前的奖励和下一个状态的折扣预期值 q_s[action] (prob * (reward gamma * V[next_state])) # 计算当前状态的最优动作值函数 best_value max(q_s) # 更新delta delta max(delta, np.abs(best_value - V[s])) # 更新状态值函数 V[s] best_value # 如果所有状态值函数的改变量都小于阈值theta则提前终止循环 if delta theta: break # 依据最终的状态值函数获取策略 policy get_policy(V, gamma) # 返回状态值函数和策略 return V, policyV [0, 0, 0, ..., 0]q_s [0, 0, 0, 0]q_s [0, 0, 1.0, 0]V[14] 1.0