1. 从“能走”到“走得最好”最优状态价值与最优策略初探前两篇我们把马尔可夫决策过程MDP和贝尔曼方程讲透了值函数可以评估一个策略的好坏策略迭代也能逐步改进策略。但很多读者学到这一篇时会有个共同的困惑最优状态价值到底是什么它和普通状态价值有什么区别为什么我的算法算着算着就发散这篇我们直接把这些疑问一次讲清楚。最优状态价值函数v*(s)是所有策略下能取得的最高期望回报最优策略π*则是达成这个回报的决策方式。它们是强化学习从“评估”走向“决策”的分水岭Q-learning、DQN、价值迭代这些主流算法的理论根基全部扎在这两个概念上。适合刚掌握贝尔曼方程、想往算法层面进阶的读者也适合那些公式看了无数遍但始终没搞懂“为什么贪心一定最优”的实战派。我会先用一张“导航地图”的方式解释最优价值的直觉含义然后推导贝尔曼最优方程再用网格世界和策略迭代两个维度手把手演示如何求最优策略最后把价值迭代和策略迭代的收敛逻辑、常见实现坑一次说透。看完你不仅能看懂论文里的公式还能自己动手写一个最优策略求解器。2. 最优状态的“灵魂三问”是什么、为什么、怎么求2.1 是什么站在岔路口时的最佳选择标准想象你开着一辆车到了一个没有红绿灯的十字路口向左是拥堵的市区道路但离目的地近向右是绕远的高速但畅通无阻。一个“普通策略”可能是固定向左——无论路况如何都向左另一个“普通策略”可能是固定向右。而最优策略是那个能根据当前路况状态、油量剩余、时间紧迫程度动态做出最合理决策的方案。形式化地说最优状态价值函数定义为v*(s) max_π v_π(s)这个公式的含义极其深刻我们不是在一个策略下求期望回报而是在所有可能的策略空间中取最大值。它回答了一个终极问题如果你现在处于状态s并且之后每一步都采取最佳行动你最终能获得的累计回报上限是多少用网文挖坑来类比更通俗你连载小说时在不同剧情节点状态做选择——主角是死是活、反派是明是暗。一个普通策略是“每章必有反转”一个烂策略是“水了100章还在铺垫”。最优策略则是那个能最大化你读者留存率和收益的剧情走向而最优状态价值就是这个节点上你作为作者的最大“剧情潜力值”。这中间有一个极容易混淆的点最优状态价值不是把某个策略的价值函数取最大值而是在策略空间上做优化。所以它天然比任何单个策略的价值都高或相等。很多初学者容易踩坑以为先定一个策略、再调参让它变优就是最优实际上最优是一个全局概念——你无法通过局部调参保证达到全局最优必须依靠下面的优化机制。2.2 为什么是核心从评估到决策的跨越前两篇讲的状态价值函数v_π(s)是“给定策略 π 后我处于状态 s 的期望回报”。这是评估问题——假设你有了一个策略量化它好不好。最优状态价值v*(s)解决的是决策问题——不依赖任何已有策略直接算出“最好能有多好”。这两者的关系就像“测速”和“定速”的关系。雷达测速能告诉你当前车速是多少策略评估但限速标志告诉你这条路的最高允许速度是多少最优状态价值。你开车时需要同时看懂两者但后者才是决定你能否合法又快到达终点的标尺。更重要的是v*(s)一旦求出最优策略可以直接从中“读”出来——之后每一步选择能导向最大v*(s)的动作即可。从这个角度看最优状态价值和最优策略是同一枚硬币的两面求出价值策略自然浮出水面。经典教材里还有一个容易忽略的点最优状态价值是唯一的。无论你用什么算法去逼近它最终都会收敛到同一个函数。而最优策略可能有多个多个策略可以达到同样的最优价值但最优价值只有一个。这个性质非常重要它保证了我们后续价值迭代算法的收敛性——不管初始值怎么设定最终都会指向同一个目标。2.3 怎么求最优贝尔曼方程的核心思想有了概念怎么求直接从定义出发遍历所有策略是不现实的因为策略空间随状态数和动作数呈指数级增长。这里就需要最优贝尔曼方程登场又常称贝尔曼最优方程Bellman Optimality Equation。它的核心是拆解决策过程从状态s出发先执行一步动作a环境转移到s之后始终按最优方式行动。于是有v*(s) max_a [ R(s,a) γ * Σ_{s} P(s|s,a) * v*(s) ]注意这个公式的两个关键点第一max套在期望外面意思是“我现在就选最优的动作”第二等号右侧的v*(s)不是任意策略的价值而是下一状态的最优价值。这个递归结构相当于你做了一个“全局最优决策”之后后面的路全部交给最优方案。类似的最优动作价值函数定义为q*(s,a) R(s,a) γ * Σ_{s} P(s|s,a) * v*(s)它和最优状态价值的关系是v*(s) max_a q*(s,a)——动作价值的最大值就是状态价值。从动作价值角度你还能得到更细的解π*(a|s)就是取 arg max 的那个动作如果有多个同样最优的动作可以在它们之间任意分配概率不影响最优性。贝尔曼最优方程是一个非线性方程组因为max操作破坏了线性性。这正是它比普通贝尔曼方程难解的原因也正是由于这个非线性我们才发展出了价值迭代、策略迭代、Q-learning等不同攻克方案。3. 直觉建立从网格世界看最优价值如何“浮现”3.1 一个极简但完整的例子理论说够了用一个经典示例来建立直觉。设一个3x3的网格左上角是起点右下角是终点到达终点奖励 10 并终止本回合其他非终点每步奖励 -1用来鼓励尽快到达终点四方向移动撞墙不动但依然消耗一步的惩罚。我们来思考一个中间状态——中央格子从中心出发到终点最少还要走2步先右再下或者先下再右。如果全程最优中心的价值应该是走到中心前累积的负奖励 从中心到终点的2步负奖励 终点正奖励。假设折扣因子γ1无折扣简化计算那么中心的最优价值大约是 2*(-1)108。如果走3步才到终点价值就只有7。所以最优值函数会给“离终点近的格子”更高分给“死角落”更低分——价值地图就是策略的缩影。如果这时γ0.9计算会稍微复杂一些从中心走2步到终点累计奖励是 -1 γ*(-1) γ²*10 -1 -0.98.16.2。折扣因子越大远期奖励折损越小价值越高折扣因子越小算法越“短视”。这里有个值得反复体会的实操细节给非终态步一个微小的负奖励是让智能体“主动走近路”的关键设计。如果每一步都是0奖励只有终点有1奖励也叫稀疏奖励那么所有非终态的价值都会趋向于γ的多次幂乘终点奖励虽然也能学会但收敛会慢很多。很多项目里把这种“过程塑造”potential-based shaping设计好比调一堆超参数都管用。3.2 贪心提取策略为什么读得出来且一定有效当你算出所有状态的最优价值v*(s)后策略提取一步就能完成不需要额外的存储对于当前状态 s遍历所有动作 a选择那个令q*(s,a) R(s,a) γ Σ P(s|s,a)v*(s)最大的动作即可。如果多个动作并列最大任选其一效果一样。这里有一个初学时的灵魂质疑“每一步只选择当下看起来最好的动作这不是贪心算法吗贪心算法不是很可能会陷入局部最优吗”答案是在强化学习的设定下这里的“贪心”不会陷入局部最优因为你的“当下”已经不是实际奖励那一步而是包含了全部未来最优回报的q*(s,a)。它本质上是“站在山脚能看到整片地图”的贪心而不是“只看山脚下三米范围”的贪心。普通贪心算法的失败是因为它只看一步的即时收益而这里的价值函数已经把多步累积信息压缩在了一个数里。策略改进定理可以严格证明基于当前价值函数做贪心选择得到的策略其表现不可能比原策略更差只会更好或持平。反复迭代这个“评估-改进”的过程最终必然收敛到最优策略。这是整个强化学习中最反直觉、也最优雅的性质——它保证了你不需要全局搜索策略空间只需要局部的、基于价值的贪心就能逼近全局最优。我当年第一次证明策略改进定理时有种“这怎么可能”的错愕感但仔细推敲后发现正是价值函数的递归结构让未来信息传导到了当下动作的选择上。3.3 实战小练习手算一个二状态MDP为了确保你真的理解我建议你拿起笔按下面步骤手算这个简化MDP。假设只有两个状态A和B状态A有两种动作a1保持原地奖励5转移到Aa2去B奖励0转移到B状态B只有一种动作回到A奖励1转移到A令γ0.9。我们求v*(A)和v*(B)。可以先直觉试想a1对于A是“无限提款机”每步5且不离开a2是“去B串门再回来”。直觉上a1似乎更优但在有折扣的情况下具体哪个更优需要计算。在最优条件下B的价值是确定的因为只有回到A一条路v*(B) 1 0.9*v*(A)。A的价值是max取二者若选a1v_a1(A) 5 0.9*v*(A)解得 v(A)50若选a2v_a2(A) 0 0.9*v*(B) 0.9*(1 0.9*v*(A))解第二个方程v(A) 0.9 0.81*v(A)得0.19*v(A)0.9v(A)≈4.74。50远大于4.74所以最优策略显然是一直选a1v*(A)50v*(B)10.9*5046。这里要注意求解时的方式——先假设某个动作是最大值代进去求解再验证假设是否成立。这正是后续我们计算机求解时的一个朴素原理只不过机器会用迭代代替手算而且它能应对成千上万的状态空间。4. 最优策略的求解之路策略迭代的完整实操4.1 策略迭代的两步舞评估与改进直到跳不动为止策略迭代的思想极其简洁启发自上面的策略改进定理。它反复执行两个步骤直到策略不再变化策略评估当前策略 π 下求解贝尔曼方程v_π(s) Σ_a π(a|s) [ R γΣP v_π(s) ]得到当前策略的价值函数。这是线性方程组可以用迭代法或直接解。策略改进基于刚得到的v_π(s)在每个状态选择使q_π(s,a)最大的动作形成新策略 π。改进后的策略保证优于或等于原策略。重复上述两步当改进步骤产生的策略和原来完全一致时就达到了最优。为什么能保证收敛到全局最优而不是局部最优因为每轮改进都在提升价值而价值函数有上界回报有界单调上升序列必然收敛同时状态数和动作数都是有限的策略空间总数有限不可能无限改进。实操时核心观察改进后的策略π 在旧策略的价值函数上贪心那么这个新策略的价值 v_π(s) 一定大于等于旧的v_π(s)。这是策略改进定理的内容。在代码实现中你有两种评估方式可选精确求解解线性方程组适用于状态数少几千以下精度高但昂贵。迭代求解从一个近似价值开始反复应用贝尔曼算子若干次只求近似值。实践中更常用。我需要强调一个很重要的工作习惯不要把握评估步骤做到完全精确策略迭代的最终收敛不需要每轮价值完全精确只要在改进步骤时方向正确即可。所以工程实现上常用“截断的策略评估”——跑几轮就切到改进这样收敛速度大幅提升。4.2 策略迭代的Python极简实现含演示下面我用一套手写代码演示策略迭代的核心逻辑。这套代码面向教学结构清晰、适合二次修改。注意我们在实现时策略是二维数组pi[s, a]表示在状态s选择动作a的概率这里初始为均匀随机。import numpy as np # 环境定义3x3网格右下角为终点 # 状态编号0~8终点状态为8 n_states 9 n_actions 4 # 0上, 1下, 2左, 3右 action_move { 0: (-1, 0), 1: (1, 0), 2: (0, -1), 3: (0, 1), } terminal 8 gamma 0.9 # 构造转移概率 P[s][a][s] 和奖励 R[s][a] P np.zeros((n_states, n_actions, n_states)) R np.full((n_states, n_actions), -1.0) # 默认每步-1 for s in range(n_states): if s terminal: continue r, c divmod(s, 3) for a, (dr, dc) in action_move.items(): nr, nc r dr, c dc if 0 nr 3 and 0 nc 3: s_next nr * 3 nc else: # 撞墙保持原地 s_next s P[s][a][s_next] 1.0 if s_next terminal: R[s][a] 10.0 # 到达终点奖励10覆盖默认-1 def policy_evaluation(pi, V, iterations100): for _ in range(iterations): V_new np.zeros(n_states) for s in range(n_states): if s terminal: continue v_sum 0.0 for a in range(n_actions): if pi[s, a] 0: continue for s_next in range(n_states): prob P[s, a, s_next] if prob 0: v_sum pi[s, a] * prob * (R[s, a] gamma * V[s_next]) V_new[s] v_sum V V_new return V def policy_improvement(V): pi_new np.zeros((n_states, n_actions)) for s in range(n_states): if s terminal: continue q_values np.zeros(n_actions) for a in range(n_actions): for s_next in range(n_states): prob P[s, a, s_next] if prob 0: q_values[a] prob * (R[s, a] gamma * V[s_next]) best_actions np.argwhere(q_values np.max(q_values)).flatten() # 若有多个最优动作平均分配概率 for a in best_actions: pi_new[s, a] 1.0 / len(best_actions) return pi_new # 初始均匀随机策略 pi np.ones((n_states, n_actions)) / n_actions V np.zeros(n_states) for iteration in range(10): V policy_evaluation(pi, V, iterations50) pi_new policy_improvement(V) if np.array_equal(pi, pi_new): print(f收敛于第 {iteration1} 轮迭代) break pi pi_new print(最优质价值函数 V*:) print(V.reshape(3, 3)) print(最优策略动作编号: , end) for s in range(n_states): if s terminal: print(终点, end ) else: # 输出选择概率最高的动作 actions np.where(pi[s] 0)[0] names .join([上 if a0 else 下 if a1 else 左 if a2 else 右 for a in actions]) print(f{s}:{names}, end ) print()这段代码跑完之后你会看到最优价值表是一个从左下到右上递减的“梯度场”离终点近的格子价值高起点附近价值低甚至负数。如果γ设得接近1起点价值会更接近 7 左右γ设小而每步惩罚大的话价值可能变负——这完全符合直觉而且代码能直接帮你验证不同参数的影响。4.3 收敛细节与参数调节γ和终止条件上面代码里有两个关键参数需要你重点调试第一个是策略评估的迭代次数iterations。设太小比如1策略改进时使用的价值估计噪音太大可能会震荡但通常仍能收敛设太大每轮都很慢。一般经验法则是每次评估迭代次数设为状态数量的2~3倍或者设置一个收敛阈值比如价值变化小于1e-6停止。在离线实验里我发现把它设成50~100对3x3小网格来说既能保证精度又不拖慢迭代。第二个是γ折扣因子。γ0时智能体极端短视只看一步即时奖励γ0.99时智能体几乎不区分远期奖励的时间差异策略会更倾向“绕远但终点奖励大的路线”。实际项目里γ通常取0.95~0.99这会影响所有状态的最优价值绝对值但不改变相对排序——所以策略本身通常对γ不那么敏感除非存在“即时奖励小但远期奖励巨大”的权衡场景。还有个小知识点策略迭代的每轮改进步骤中不必真的把价值收敛到完整精度再改进通常只需要让价值在旧策略下大致合理即可。这个“提前结束评估”的做法也叫Modified Policy Iteration它比完整评估速度快得多而且收敛性质依然有保证。实操时千万不要等完全收敛才切换策略慢得让人崩溃。5. 另一条路价值迭代——把两者拧成一股绳5.1 从截断策略迭代到价值迭代的质变策略迭代每轮都做完整策略评估计算量大。如果我们把评估迭代的次数压缩到只剩一次会发生什么伪代码变成对每个状态 s反复执行V_new(s) max_a [ R(s,a) γ Σ P(s|s,a) V(s) ]这就是价值迭代。它把“策略评估”和“策略改进”两步骤直接合并成一个更新式省去了显式策略的存储和评估过程。工程实现更简单每个状态每个动作都算出Q值取最大更新V迭代到收敛。最终V就是v*(s)之后通过一次贪心即可提取最优策略。价值迭代为什么有效直观地说每一轮更新都在把一步最优化“扩散”到所有状态上。第一次更新后每个状态的价值只考虑了未来一步的最优第二次更新后每个状态考虑了未来两步的最优迭代k次后每个状态的价值对应未来k步范围的近似最优。当k足够大趋于无穷折扣因子保证远期影响趋近于0于是 V 收敛到 v*。这个“传播”视角是我觉得理解价值迭代最好的方式——它像是从终点奖励出发一圈一圈向全状态空间辐射的波纹每迭代一轮波纹就向外扩散一步。5.2 价值迭代代码与收敛判定价值迭代的代码比策略迭代短很多我看很多初学者甚至不敢相信这么简单的循环就能求最优策略。直接在上面的环境上继续def value_iteration(V, threshold1e-6, max_iter1000): for it in range(max_iter): delta 0.0 V_new np.zeros(n_states) for s in range(n_states): if s terminal: continue q_best -np.inf for a in range(n_actions): q_val 0.0 for s_next in range(n_states): prob P[s, a, s_next] if prob 0: q_val prob * (R[s, a] gamma * V[s_next]) q_best max(q_best, q_val) V_new[s] q_best delta max(delta, abs(V_new[s] - V[s])) V V_new if delta threshold: print(f价值迭代收敛于第 {it1} 次迭代) break return V V2 np.zeros(n_states) V2 value_iteration(V2) # 提取最优策略 def extract_policy(V): pi_extract np.zeros((n_states, n_actions)) for s in range(n_states): if s terminal: continue q_values np.zeros(n_actions) for a in range(n_actions): for s_next in range(n_states): prob P[s, a, s_next] if prob 0: q_values[a] prob * (R[s, a] gamma * V[s_next]) best_actions np.argwhere(q_values np.max(q_values)).flatten() for a in best_actions: pi_extract[s, a] 1.0 / len(best_actions) return pi_extract pi_star extract_policy(V2) print(价值迭代得到 V*:\n, V2.reshape(3, 3)) print(提取最优策略完成)一个非常关键的实际经验收敛判定不要看单步绝对变化要看所有状态的最大变化量 delta。因为某些偏远状态的V变化可能很慢中心地带变化很快如果只看平均变化就宣布收敛可能偏远状态还差得很远。我在模拟项目中调试时曾因为用了平均误差当收敛指标导致策略在状态边界处反复横跳排查半天才想起用最大绝对值差才稳定下来。价值迭代和策略迭代的对比用一句通俗的话说策略迭代是“深思熟虑式”先算清楚再改方案价值迭代是“走一步看一步式”每个状态每轮都试最优动作并更新。在状态空间小的时候策略迭代通常迭代轮数少但每轮贵状态空间大时价值迭代每轮便宜往往更适合起步。我认为这两个算法没有绝对的优劣真正的区别在于你对价值精度的容忍度和环境规模。做实验初期建议都实现一遍用同一环境对比看谁先到最优策略对理解二者的收敛性质帮助极大。6. 常见问题排查与避坑手册6.1 为什么我的价值迭代不收敛这是问得最多的问题。排查优先级如下奖励设置是否合理如果所有非终止步奖励都是0且终止步奖励为正迭代通常能收敛但速度慢如果奖励有正有负且幅度差异大比如100和-100价值函数震荡一定比幅度差异小的慢得多。一种最容易犯的错误终止状态也被纳入了更新这会导致终止状态的价值不停累加奖励直接发散。务必在更新时跳过终止状态。折扣因子是否等于1γ1时在无限期任务中没有强制终止机制每一站的奖励会无限累积价值可以任意大迭代不会收敛。如果你的任务有明确的终止状态γ1可能还能收敛否则必须小于1。一般任务默认γ0.9~0.99除非你有特殊理由。转移概率总和是否为1手写环境时很容易把转移概率写错导致某些动作的P矩阵行加起来不为1这时贝尔曼方程在数值上会引入虚假的“奖励泄漏”或“概率泄漏”迭代出来的价值不对但表面收敛。建议每次构建完P之后跑一行断言np.allclose(P.sum(axis2), 1.0)。初始价值设太高或太低价值迭代的初始值是任意的但设太大时会把“远期奖励”估计得虚高需要更多轮才能拉回来设太低时拉动也慢。常用做法是初始化为全0或与即时奖励的期望一致但这不是什么关键问题——真正要避免的是终止状态被更新。6.2 最优策略和最优价值哪个先出来价值迭代是先得价值再从中提取策略策略迭代是评估旧策略、改进得到新策略同时带上新策略的价值。大多数现代算法如DQN在实践中直接学习q*或v*策略只是附带品。实际做项目时我强烈建议你至少维护一个显式的策略或者记录每个状态的Q值只维护V函数虽然能提取策略但每次决策都要重新算一遍Q值浪费计算量。维护Q值表决策时直接查表选最大即可内存占用略高但速度快得多。6.3 表格方法极限在哪什么时候该换算法经典表格方法策略迭代、价值迭代依赖显式枚举状态和动作。3x3网格、几十个状态的玩具问题是它们的主场一旦状态数上万表格就开始吃内存连续状态空间更是根本无法用表存储。这时候有两条路如果状态数几万级别状态可以离散化先用基于表格的方法加一些优化如优先扫描有价值变化的状态。如果状态连续或维度高转向函数逼近类算法深度强化学习方向后面的文章会专题讲。但有一点需要强调理解最优状态价值和最优策略这件事是迁移到深度强化学习的必经之路。DQN里那个目标网络更新公式本质上就是价值迭代的一次更新策略梯度方法里那个优势函数本质上就是Q值减去V值。无论算法多花哨根都在今天这篇讲的这两个概念上。6.4 一张你最该保留的参数速查表参数典型取值影响经验值γ折扣因子0.9~0.99越小越短视任务周期长取0.99周期短取0.9非终点步奖励-1~0影响路径选择偏好建议设小负值加速收敛终点奖励10~100影响整体价值尺度相对普通步奖励高一个数量级即可策略评估迭代轮数10~100越大每轮代价越高状态数×2~3倍价值迭代停止阈值1e-6~1e-4越小精度越高项目初期用1e-4即可这张表填补我当年调参踩过的坑奖励量级如果不匹配价值函数数值会爆炸或趋近于0策略输出就没意义。设计奖励时让普通步惩罚绝对值远小于终止奖励是很多强化学习项目的默认做法。7. 写在最后一些建议和实践体会我个人实际跑这些经典算法最大的体会是最优状态价值不是“算出来”的而是“迭代出来”的。第一次跑通价值迭代时看到3x3小网格上价值从全0一点点“生长成”一张梯度场那种感觉很奇妙——它明明是我们事先定义好的目标函数但算法愣是像一个小孩学走路一样一步步摸索到了终点。我还想提醒一点在强化学习这个领域动手推公式和动手写代码同样重要。策略改进定理的证明建议你亲手推导一遍而非只读结论价值迭代的收敛性理解也建议你多看几轮迭代中价值的变化过程。很多卡在深层次问题的人往往就是当年跳过了这一层的直觉培养。如果你接下来想做更实际的强化学习项目建议按这个顺序练手先跑通上面的网格世界策略迭代和价值迭代然后换一个大一点的环境比如十几状态的迷宫观察两个算法的性能和差异等对表格方法完全熟练了再去接触DQN这类深度强化学习。工具可以换但“最优价值”这个思想主线不会断。最后分享一个小技巧调试价值迭代时把每轮V打印成热力图价值从终点向外扩散的波纹能直观展示收敛过程异常状态比如没被正确初始化的格子也会突兀地显示出来。用视觉辅助调试强化学习比看任何数字都高效得多。