贝尔曼方程与价值函数这两个词几乎出现在每一份强化学习入门资料里也几乎是初学者的第一道分水岭。我第一次读到 V(s) E[R γV(s)] 时第一反应是“这数学看着不难可它到底在算什么”后来在完整推导了几轮状态价值函数的迭代之后才彻底明白它是在用一种递归的方式回答“当前状态到底值多少钱”。这篇笔记面向刚看完马尔可夫决策过程、准备深入强化学习核心公式的人我会从折扣因子讲到状态价值和动作价值再把贝尔曼期望方程和最优方程拆开揉碎最后用一个3×3网格世界手算一遍把我踩过的坑一并交代清楚。1. 从回报到价值函数强化学习真正在“估”什么1.1 为什么只看单步回报不够折扣因子的三重含义初学强化学习时最容易出现的误区是把“奖励最大”等同于“单步奖励最大”。如果在某个状态下往左走能拿0.5往右走能拿0.2那就无脑选左这不对。因为强化学习关心的是从当前时刻开始到终局为止的累积回报而不是眼前这一口的甜头。所以我们需要定义“回报”Return也就是从 t 时刻开始往后所有折扣奖励的总和G_t R_{t1} γR_{t2} γ²R_{t3} … Σ_{k0}^{∞} γ^k R_{tk1}这个 γ 就是折扣因子取值在 [0, 1] 之间。它在数学、直觉和工程三个层面各有含义数学层面当 γ 1 且每一步奖励有界时无穷级数收敛G_t 是有限值价值函数才有稳定解。直觉层面未来的奖励存在不确定性早拿到的奖励更确定、更有价值折扣因子天然表达了“眼前的钱比未来的钱更值钱”的偏好。工程层面γ 控制了价值“往回看多远”。γ 接近 1模型会关注极其长远的结果γ 很小模型会变得短视只盯着几步内的收益。我在实际使用中习惯这样理解折扣因子不仅是数学技巧它是一种对“远见”的量化。γ 0 时只在乎当前一步γ 0.99 时基本在乎无穷远期的累积收益。设计一个强化学习任务时如果每一步都耗时较长、不确定性高我会倾向选 0.9 到 0.95如果任务是慢速长线的策略博弈通常会选 0.99 甚至更高。这能解决什么问题它解决了“奖励序列长度不一致”的麻烦。有的回合走 3 步就结束有的走 300 步才结束直接比较总奖励不公平但折算成“按折扣因子加权后的累积回报”后它们有了统一的比较尺度。1.2 状态价值函数 V(s) 与动作价值函数 Q(s,a)两把不同的尺子有了回报 G_t就可以定义价值函数。严格来说回报是一个随机变量因为它依赖未来的状态转移和动作选择。价值函数就是回报的期望用期望消除随机性。状态价值函数 V^π(s)表示在策略 π 下从状态 s 出发的期望回报V^π(s) E_π[G_t | S_t s]动作价值函数 Q^π(s, a)表示在策略 π 下从状态 s 出发、先执行动作 a、之后继续遵循策略 π 的期望回报Q^π(s, a) E_π[G_t | S_t s, A_t a]两者最核心的关系是Q 值就是在某个状态执行某个动作的“即时奖励”加上“后继状态的状态价值均值”Q^π(s, a) Σ_{s, r} p(s, r | s, a) [r γV^π(s)]V^π(s) Σ_a π(a|s) Q^π(s, a)为什么需要两把尺子状态价值 V 解决的是“我现在所处的位置到底好不好”它不考虑当前动作只考虑整体局势动作价值 Q 解决的是“我面前这些具体选项哪个更香”它是决策时直接比较的对象。我在做实际项目时V 函数往往用于评估当前局势、计算优势函数而 Q 函数用于选出具体行为。两个都不能缺。打一个比方假期你要决定去海岛还是爬山。V 函数告诉你海岛整体性价比高、风景好、口碑稳定Q 函数告诉你具体到你这趟出行、结合预算和天气选海岛更划算。V 是局势评估Q 是动作选择方向不同缺一不可。2. 贝尔曼方程的本质把“未来”递归地折叠进“现在”2.1 从一个关键分解讲起回报的递归身份贝尔曼方程之所以漂亮在于它利用了回报本身的递归结构。按定义G_t 从 R_{t1} 开始而 G_{t1} 从 R_{t2} 开始所以G_t R_{t1} γ(R_{t2} γR_{t3} …) R_{t1} γG_{t1}这个等式看着太简单了但它是整棵大树的根。把它代进状态价值函数的定义式就会得到贝尔曼方程V^π(s) E_π[R_{t1} γV^π(S_{t1}) | S_t s]这里的区别在于原来 V^π(s) 的定义对整个无穷回报求期望现在只需要对“即时奖励 下一步状态的价值”求期望。无穷的前瞻被压缩成了一步这就是“把未来折叠进现在”。只要知道下一步各个状态的价值当前价值就立刻被确定了。这个递归结构把“看得很远”和“只需要看一步”统一了起来是动态规划思想在强化学习里的真正入口。我初学时的最大卡点在这里总觉得折叠一步之后V(S_{t1}) 本身还是包含未来无穷步的这不等于又回到原点吗对但它不再需要从无穷远处往回推。只要所有状态的 V 都满足这个自洽方程它们就是“未来一步接一步的标准答案”。与其说它是一个无限递归不如说它是一个自洽方程组N 个状态对应 N 个方程、N 个未知数。这从“无穷视角”变成了“有限线性系统”求解思路完全变了。2.2 贝尔曼期望方程在策略 π 下评估世界把上面的推导细化就是完整的贝尔曼期望方程。对于给定策略 π状态价值函数满足V^π(s) Σ_a π(a|s) Σ_{s, r} p(s, r | s, a) [r γV^π(s)]动作价值函数也有自己的期望方程Q^π(s, a) Σ_{s, r} p(s, r | s, a) [r γ Σ_{a} π(a|s) Q^π(s, a)]这里有两个数学期望经常有人混对动作求期望权重是策略 π(a|s)。策略说“这个动作有多大概率被选中”价值就按这个概率做加权平均。对状态转移求期望权重是环境转移概率 p(s, r | s, a)。环境说“执行 a 之后有多大概率落到 s 并拿到 r”价值就按这个概率做加权平均。两层期望对齐之后V^π(s) 就算清楚了。这个方程的作用是“策略评估”给我一个策略 π我来回答这个策略到底有多好。它的典型应用场景是在周四的某公司导航 Demo 里先把一个规则策略比如“遇到岔路口右转”的价值函数算出来才能知道这个策略比随机走强多少。实际工程里策略评估不一定真的解线性方程组更多是用迭代法反复套公式直到 V 值变化小于阈值。理论上γ 1 时有界奖励可以保证收敛实践上我们还要关心收敛速度和数值稳定性这在后面实操部分会展开。贝尔曼期望方程的“为什么”核心在于它把“一个策略的好坏”从抽象概念变成可解的数值问题。没有它你根本没法和别人讨论你的策略到底好在哪里、坏在哪里。3. 贝尔曼最优方程价值函数与最优策略的相遇3.1 期望变最大一个符号之差学习到贝尔曼最优方程时最大的冲击是好像只是把 Σ_a π(a|s) 换成了 max_a公式就突然从“评估一个给定策略”变成了“求解最优策略”。这个符号之差背后是价值逻辑的根本变化。贝尔曼最优方程长这样V*(s) max_a Σ_{s, r} p(s, r | s, a) [r γV*(s)]Q*(s, a) Σ_{s, r} p(s, r | s, a) [r γ max_{a} Q*(s, a)]注意几个细节期望方程的外层对动作求期望最优方程的外层对动作求最大值。V*(s) 已经不含策略 π理论上最优策略是确定性的所以不再需要加权平均。最优方程里的 V* 是同一个函数出现在等式两边这意味着“如果我从 s 出发一直按最优方式行动那当前动作选能让 r γV*(s) 最大的那个”。Q* 方程里的 max 放在了后继值上执行 a 之后下一步会从所有动作中选最大的 Q*这代表你相信未来每一步都会按最优走。为什么可以这样取 max这背后是贪心原理与最优子结构的结合。如果存在一个最优策略那么从任何状态出发最优策略的后续决策也必然是最优的。强化学习环境满足马尔可夫性当前状态已经浓缩了历史信息所以“剩下所有时刻都按最优行动”是唯一合理假设。从数学上贝尔曼算子是一个压缩映射反复迭代会收敛到唯一的不动点这个不动点就是 V*。理论与直觉在这个点是一致的。3.2 从价值函数到最优策略的落地路径重点来了算出了 V* 不等于立刻就能决策。决策时你要比较的是“各个动作谁更能提高累积回报”而不是只看当前状态好不好。已知 V* 的情况下最优策略可以写成π*(s) argmax_a Σ_{s, r} p(s, r | s, a) [r γV*(s)]这个式子需要知道转移概率 p也就是需要“环境模型”。如果能拿到 Q*决策就更直接π*(s) argmax_a Q*(s, a)因为 Q*(s, a) 已经把所有未来最优响应都折进去了选最大的 Q 就是最优动作不需要显式知道环境模型。这也是为什么后续各种无模型算法都围绕 Q 值做文章Q-learning、Double DQN、Dueling DQN本质都是在逼近 Q*然后用 argmax 选动作。在实际项目里我第一次实现价值迭代时想当然地以为 V* 收敛了就大功告成结果发现策略没用对。原因就是 V* 只能告诉“这个状态有多好”要在已知模型的情况下才能反推动作如果模型未知就必须维护 Q* 或者额外学习一个策略。这条路线图想清楚之后很多模型的代码结构就通透了。4. 手算小网格世界3×3环境下的贝尔曼方程实操4.1 环境参数与转移规则设定理论讲完必须上手算。我一直认为贝尔曼方程不亲手算过一遍迭代永远是纸上的符号体操。下面设计一个最简单的 3×3 网格世界状态9 个格点s1 到 s9按 3 行 3 列排列。动作上、下、左、右每个动作等概率执行。终点s9第 3 行第 3 列进入后回合终止。奖励进入终点给 1其他所有非终点的转移给 -0.1代表每步的行动代价。边界越界动作停留在原地。折扣因子γ 0.9。演示策略均匀随机策略每个动作概率为 0.25。这里选择 -0.1 的步数惩罚是为了在价值函数中体现出“距离终点越远价值越低”否则所有非终点状态的价值可能区分度不够。这听起来是教学细节但设计奖励时就决定了后续价值函数长什么样。真实项目里也一样步数惩罚的数值直接影响最优策略是否倾向于尽快到达目标。4.2 第一次、第二次迭代的手算与解读用价值迭代的同步更新公式从 V^0(s) 0 开始。第一次迭代对非终点状态因为 V^0 处处为 0所以只需要看一步奖励的期望。计算后得到 V^1s1 到 s5、s7 这些不直接接触终点的状态V^1 -0.1s6 和 s8一个向下到 s9一个向右到 s9四个动作里包含一个 1所以 V^1 0.25 × (1 - 0.1 - 0.1 - 0.1) 0.175s9 是终止状态V^1 0这轮结果其实不意外它只反映“一步内能不能吃到终点奖励”。真正的信息在第二轮开始。第二次迭代以 s1左上角为例它的两个动作上、左会撞墙留在原地另外两个动作右、下分别进入 s2 和 s4。V^2(s1) 的计算过程是上-0.1 0.9 × (-0.1) -0.19左-0.1 0.9 × (-0.1) -0.19右-0.1 0.9 × (-0.1) -0.19下-0.1 0.9 × (-0.1) -0.19V^2(s1) 0.25 × (-0.19 - 0.19 - 0.19 - 0.19) -0.19再看中心状态 s5第 2 行第 2 列四个方向分别进入 s2、s8、s4、s6。注意 s8 和 s6 在 V^1 中已经是 0.175所以上到 s2-0.1 0.9 × (-0.1) -0.19下到 s8-0.1 0.9 × 0.175 0.0575左到 s4-0.1 0.9 × (-0.1) -0.19右到 s6-0.1 0.9 × 0.175 0.0575V^2(s5) 0.25 × (-0.19 0.0575 - 0.19 0.0575) -0.06625第二次迭代后完整 V^2 可以整理成下表状态V^2 值s1-0.19s2-0.19s3-0.128125s4-0.19s5-0.06625s60.169375s7-0.128125s80.169375s90这组数字非常有意思离终点近的 s6、s8 已经变成正值中心 s5 次之角落 s1、s4 最差。价值函数开始“把距离翻译成数值”这就是贝尔曼方程递归传播信息的效果。手算的价值不在于算出精确数字而在于体会“未来价值是如何一步步传回当前的”。我强烈建议初学者至少手算一次这种小网格比盯着代码看一小时都有用。4.3 用一个小脚本验证迭代收敛手算到第二轮已经略微繁琐继续手写全部收敛过程不现实。工程上我们直接用价值迭代脚本完成后续计算。下面是我在本地跑过的完整脚本结构刻意保持简单import numpy as np rows, cols 3, 3 gamma 0.9 actions [(-1, 0), (1, 0), (0, -1), (0, 1)] # 上、下、左、右 V np.zeros((rows, cols)) terminal (2, 2) def step(state, action): r, c state dr, dc action nr, nc r dr, c dc # 越界则原地不动 if nr 0 or nr rows or nc 0 or nc cols: nr, nc r, c next_state (nr, nc) if next_state terminal: return next_state, 1.0 return next_state, -0.1 for k in range(30): newV np.zeros_like(V) for r in range(rows): for c in range(cols): if (r, c) terminal: continue state (r, c) values [] for a in actions: ns, rew step(state, a) values.append(rew gamma * V[ns]) newV[r, c] np.mean(values) # 均匀随机策略 V newV print(np.round(V, 4))脚本里有两个值得注意的工程点同步更新每次迭代用上一轮完整的 V 表计算新表不边算边改。这样可以保证每一步都在解同一个方程结果更稳定。用 np.mean(values) 对应均匀随机策略如果改成 np.max(values)就变成求解贝尔曼最优方程的价值迭代。两套方程在代码上只差一个函数正是数学中“期望变最大”的直观体现。运行结果会收敛到一个稳定矩阵越靠近终点的状态 V 值越高角落状态 V 值最低。看到这个结果再回头读一遍公式很多疑问会自然消解。5. 高频翻车现场贝尔曼方程学习中的常见问题5.1 期望方程与最优方程混为一谈这是我在给组里新人讲强化学习时遇到最有代表性的问题代码里明明在做价值迭代更新公式却带着策略π或者明明在做策略评估却无脑用了 max。一句话判别方式公式里如果出现策略 π(a|s)那就是期望方程如果出现 max_a那就是最优方程。期望方程告诉你“这个策略有多好”最优方程告诉你“最好的活法长什么样”。实际操作里另一个更隐蔽的误用是用期望方程做策略评估时没有把终止状态单独处理。终止状态的 V 应该恒为 0并且不再参与更新。如果漏掉这个条件终止状态会不断累积假价值收敛结果完全失真。我在小网格例子里专门让 s9 跳过更新就是这个原因。5.2 折扣因子选不对收敛和策略都受影响γ 是超参数里最“灵活”但最容易被随意设置的一个。我见过有同学直接照抄别人的 0.99结果在短任务上收敛奇慢策略还显得过度远视也有把 γ 设成 0.7 就跑去跑长线任务最后策略只看眼前几步完全不敢大胆探索。实际经验是任务步数短几步到几十步γ 可以取 0.9 左右。长线任务几百上千步γ 取 0.99 或更高。如果每一步都有不低的执行成本γ 太大不仅收敛慢还会在价值图中制造“拥挤的假高值区域”影响策略质量。另一个容易踩的坑γ 1 时如果环境存在循环、且累积奖励不递减价值函数可能发散。理论上无折扣有限步问题可以用 γ 1但工程上我几乎不用因为数值稳定性太差。绝大多数强化学习框架默认值在 0.9 到 0.99 之间是有道理的。5.3 同步更新与原地更新数值稳定性的坑价值迭代有两种常见实现方式同步更新和原地更新。同步更新需要维护新旧两张表写的代码稍长但每次迭代都基于完整旧表行为更可控。原地更新只维护一张表边算边覆盖代码短、占用内存小但迭代顺序会引入偏差可能让收敛轨迹更“拧巴”偶尔还会出现局部震荡的观感。数学上压缩映射保证两者最终都收敛到相同不动点但收敛路径不同。我的建议是调试阶段一定用同步更新方便对照公式检查每一步数学是否符合预期等代码稳定、环境超大、内存吃紧时再考虑原地更新。亲手试过这两种实现后你会对“为什么教程里总是强调用两个数组”有体感。还有一个小技巧判断是否收敛时不要只看矩阵是否完全不再变化而要看最大变化量是否在容差内。我在实现中用过delta np.max(np.abs(newV - V)) if delta 1e-4: break这个小细节可以在长任务上省下大量无效迭代。个人体会与下一步建议我最初学贝尔曼方程时最大的误区是想把公式背熟再去做题结果背来背去还是绕不清。后来换了思路不背公式而是拿着纸笔在网格上一步一步写“站在这个格子我的出路是什么每条出路的即时收益加后续价值是多少”几轮迭代后期望方程和最优方程的差异自然而然地刻在脑子里了。如果你想真正突破这个章节强烈建议你也把教程里的环境换成自己的小网格设一组手算能处理的参数务必亲自算到第二轮。这个过程里踩的坑会变成后面理解 Q-learning 和深度强化学习时最扎实的底层直觉。下一步就可以开始看策略迭代和时序差分学习到时候你会发现贝尔曼方程这条根几乎长在所有后续算法的主干上。
阅读完成 · 觉得有帮助?