强化学习系统读书笔记:从MDP到策略梯度的完整脉络 入门强化学习的人大概率都碰过“读书笔记”这三个字。网上各种教程和课程满天飞知识点零散得像一盒打翻的乐高今天看到一个策略梯度明天刷到一个Q-Learning单个看都懂合在一起脑子里还是一团浆糊。我啃完手头这本大部头之后最大的感触是强化学习真正难的不是某个算法而是它内部的逻辑链条——从马尔可夫决策过程到贝尔曼方程再从时间差分到策略梯度每一环都踩着上一环往上走。这篇文章就是我重读之后写的系统读书笔记梳理了最核心的建模框架、算法演化脉络、以及我在复现实验时踩过的坑适合刚准备系统学习强化学习的人也适合已经看了不少零散资料、想串成体系的人。1. 为什么我建议带着“做笔记”的心态读强化学习1.1 一本能翻烂的书胜过十个收藏夹里的教程我先说点可能不受欢迎但真实的想法强化学习这个领域只看博客和视频是学不会的。博客能给你一个问题全貌视频能动起来演示agent怎么学会走路但它们都给不了你那种“在推导中突然卡住、回头翻上三页、终于想通”的顿悟感。我对书的态度很简单——它是慢的但它是完整的。读强化学习类和读其他计算机书不太一样。读Web开发的书你可以跳过某些章节直接上手框架读强化学习的书前面几章看着像数学课后面突然就变成算法课再后面又冒出来一堆神经网络结构。如果前面没吃透后半程每一步都在打补丁。我做笔记最重要的目的就是把这种“线性依赖”给记下来哪一章是后面哪一章的前提哪几个公式必须背下来、哪几个只要看得懂就行标得清清楚楚。所以第一遍读的时候我不追求速度。我身边有朋友一周翻完大半本结果问他“蒙特卡洛和时间差分到底差在哪”他只能说出“一个有最终结果一个没有”——这个理解不能说错但距离“能用、会调”还差得很远。我的建议是前两章至少读两遍第一遍通读不纠结第二遍才开始动手写笔记。1.2 这份笔记适合谁没有“前置焦虑”的零基础读者很多人问我不懂深度学习和概率够不够资格学强化学习。我的答案是从头学完全没问题但需要做好心理准备要强化学习不要求你先成为数学高手但它要求你有耐心去查你没见过的符号。比如“期望”这个概念学过概率的人一道带过没学过的人看到E[...]就开始头大。这恰恰是笔记能帮上忙的地方。我在笔记里会专门列出一个“符号速查表”把所有常常出现的希腊字母和数学记号统一整理E代表期望π代表策略γ代表折扣因子δ代表时间差分误差等等。这么做看似基础实际用途巨大。因为你能坚持读下去的动力很大程度上取决于你翻书时能不能快速找到自己需要的信息。我自己第一遍读的时候就是吃了不会记笔记的亏就是在书页上画线画完整本书等于什么都没画。后来换了一种方式每一章读完合上书用三句话总结这一章解决了什么问题、用了什么方法、留下了什么疑问。这个习惯救了后期的我因为在读后面那些烧脑章节时翻回来看这三句话能让我一分钟内回到状态而不用从头再翻。2. 学习强化学习之前先建立的思维框架2.1 强化学习到底在解决什么问题给我一句话让我跟完全没接触过的人解释强化学习我会这么说它是让一个主体通过不断跟环境互动学到一套能让长期收益最大化的行为策略。听起来像人类的试错学习本质上确实如此。但这里有几个点和直觉不同值得写进笔记的第一页。强化学习不管中间过程多符合“常识”它唯一的目标就是累积奖励。这个奖励是人为设计的不是天然存在的。很多人跑起第一个CartPole实验时总有一个误区以为agent“知道”杆子不能倒。其实agent什么都不知道它只知道某个状态下做了某个动作环境反馈回来一个数值。它不知道这个数值代表什么物理意义只知道要追求更大的数字。这种“目标函数至上”的思维方式恰恰是强化学习跟传统机器学习最根本的区别。传统监督学习有明确的标签做参照答错了就知道怎么改强化学习没有明确的“正确答案”只有一个好或坏的反馈信号而且这个信号往往延迟很久才到来。打一盘棋最后一手才知道输赢但中间几十手每手都要负责任——这就是强化学习的核心难题信用分配问题。这个思维框架如果你能先搭起来后面学什么算法都不容易跑偏。2.2 马尔可夫决策过程一切算法共同的地基如果要我从整本书里选一个最不能跳过的概念那一定是马尔可夫决策过程简称MDP。几乎所有的强化学习问题都能被建模成MDP所有算法最终的优化对象也都在这个框架之内。MDP用五个元素描述一个决策场景状态、动作、状态转移概率、奖励函数、折扣因子。这套描述方式非常优雅它把“现实世界的连续过程”简化成“离散时间上的序列决策”。为什么叫马尔可夫因为有一个马尔可夫性质——下一个状态只取决于当前状态和当前动作跟更早的历史无关。现实世界当然没有这么干净但把这个假设放在模型里数学上就变得工整。我在笔记里写了一个生活类比你在玩一个回合制游戏游戏只告诉你“你当前在哪一关、有哪些装备”不会告诉你“你昨天输给了谁、前天充值了多少”你的下一步决策只依赖当前局面。这就是马尔可夫性质的通俗理解。把问题描述成MDP之后强化学习的任务就变成了在所有可能的策略里找到能让累积期望奖励最大化的那个策略。这里又引出一个容易被忽略的点策略是状态到动作的映射它不一定是“确定”的。有时候最优策略反而是随机的比如石头剪刀布纯随机反而是最不容易输的。这个细节在后来的策略梯度方法里会被反复用到所以我把它专门记了下来。2.3 打折的未来折扣因子为什么如此重要MDP里有一个小参数却牵动全局——折扣因子γ取值范围在0到1之间。它决定了agent做决策时到底有多“目光长远”。γ越接近1agent越看重未来的奖励γ越接近0agent越只顾眼前。为什么需要这个参数三个原因第一个原因是数学上的收敛性。如果每一回合都是无限长的没有打折的未来奖励之和可能会发散。乘上一个小于1的因子每一轮都在衰减无穷等比数列就有了一个有限的极限值。第二个原因是建模上的合理性。现实世界里未来的收益天然就有不确定性早点拿到奖励比晚点拿到奖励更稳妥打折体现了这种时间偏好。第三个原因最简单实际如果环境永远持续下去没有折扣的话连价值计算都会变得没有意义因为所有的数都趋向无穷大。这三个原因我在笔记里各占一段因为后面的贝尔曼方程几乎每一步都要用到折扣因子。我第一次读的时候没太注意γ的具体数值对算法行为的影响后来在写代码调参时才发现γ设成0.9和0.99agent表现完全不一样前者短视、后者耐心得多。所以如果你在复现某个算法时发现agent行为不对劲先检查γ再检查奖励定义——这是我踩过坑之后给自己写的第一条排查捷径。3. 价值函数与贝尔曼方程一切强化学习算法的“心脏”3.1 状态价值与动作价值两个视角各有妙用MDP给我们描述了问题但还没告诉我们怎么解。要解就得引入价值函数。价值函数用来衡量“在给定状态或状态下执行给定动作之后能获得多少累积期望奖励”。这里有两兄弟一个叫状态价值函数一个叫动作价值函数。状态价值函数记作V(s)衡量的是“从状态s出发按当前策略走能拿到多少回报”。动作价值函数记作Q(s, a)衡量的则是“在状态s先做出了动作a往后按当前策略走能拿到多少回报”。两者的差别就在“动作是否已经被指定”。Q函数在实际应用中比V函数更常用因为策略最终要决定的是动作知道了每个动作的Q值直接挑大的就行。我在笔记里用一个很接地气的例子做注释打游戏的时候V(s)相当于“当前局面已经奠定了胜率”Q(s, a)则相当于“在当前局面下具体按某个键之后胜率会怎么变”。你说哪个对决策更有用当然是后者。但前者也不是没用很多高级算法里的基线都是基于V函数的这是后话。3.2 贝尔曼方程把未来拆成“当前回报未来价值”的递归公式价值函数最优雅也最核心的性质就是可以递归表示。当前状态的价值等于当前的即时奖励加上下一状态的折后价值。这个看似简单的自我指涉就是贝尔曼方程。V(s) R(s, a) γ * V(s)这个式子看着轻松其实是整个强化学习理论的定海神针。因为它把“对无限长未来的评估”变成了“一个常量的递归关系”。有了它我们才能设计出各种迭代算法去逼近真实的价值函数。贝尔曼方程有个重要的前提假设当你计算将来的状态价值时默认你会按照当前策略继续行动。也就是说价值函数是某个特定策略的价值策略变了价值也会变。我在读书笔记里把这个地方标成了“全书最重要的三个式子”之首。为什么因为后面的Q-Learning、SARSA、深度Q网络说到底都是在用各种途径求解或逼近贝尔曼方程。如果你能亲手把贝尔曼方程从定义里推出来一次——就是展开价值函数定义、把累积奖励拆成第一项加后面无穷项、再用折引到下一状态——这个推导过程本身就能让你对后续所有算法有“原来如此”的开悟感。3.3 贝尔曼最优方程决策的终点就是“选当下最优再相信未来”有了贝尔曼方程还不够因为它是描述“按当前策略的价值”的。我们最终想要的是让价值最大的那个策略这就引出了贝尔曼最优方程。它的形式跟普通贝尔曼方程的区别只在一处求价值的时候不再问“当前策略会选什么动作”而是问“当前所有可选动作里哪个动作带来的价值最大”。V*(s) max_a [ R(s, a) γ * V*(s) ]这个max操作就是最优方程的精髓。它把决策问题直接嵌进了价值函数的定义里——在状态s选择动作a使得即时奖励加未来最优价值之和最大化。之所以说它是“最优”因为它不再绑定任何策略而是定义了一个终极的价值上界。笔记里我需要特意写一段话提醒将来的自己贝尔曼最优方程里的max是“已知最优未来价值”的这背后隐含了动态规划思想也就是把大问题分解成一系列相互依赖的子问题。从工程角度来说你没法一步算出V*但你可以不断用这个方程去迭代更新自己的估计——这就是价值迭代算法。所以贝尔曼最优方程不止是一个数学定义它本身就给出了算法构造的蓝图。4. 从表格到函数逼近主流算法演进的逻辑线4.1 动态规划、蒙特卡洛和时间差分三种思路三种代价有了价值和贝尔曼方程接下来要回答的问题是怎么算出来书里给了三条路各有各的写法。我在笔记里把它们对比着看因为它们之间不是互相替代的关系而是从“已知模型”到“未知模型”的递进。第一条路是动态规划。它要求你知道环境的完整模型也就是知道状态转移概率和奖励分布。有了模型之后它就是一套精确求解的数学操作先是策略评估算价值再是策略改进用贪心更新迭代到收敛为止。但现实世界里哪来这么精确的模型所以动态规划的意义更多是理论奠基而不是工程常用。第二条路是蒙特卡洛方法。它换了个思路我不要模型了我直接在环境里跟agent交互跑完整回合后用实际观测到的回报作为价值的估计。这个思路很符合直觉但有个致命伤——它必须等到一个回合结束才能学习。如果一局棋要下几百步几百步之后才有一个反馈那学习效率太低了。第三条路是时间差分方法也就是TD方法。它把前两种的优点结合了不用完整模型也不用等回合结束它只用一步的实际回报加上对下一状态的现有估计来更新当前状态的价值。这个“用估计来更新估计”的做法统计学上有个名字叫自举。Q-Learning和SARSA都属于TD家族。TD是实践里用得最多的思路也是后面的深度强化学习算法赖以生存的基础。4.2 Q-Learning与SARSA离策略与在策略的经典之争在这个地方我最早是困惑的因为Q-Learning和SARSA长得太像了更新公式就差那么一点点。后来我才明白这一点点就是两者哲学层面的根本分歧。Q-Learning更新时用的是“目标状态下的最大Q值”——也就是说评估下一步的时候它假装自己会去选最优动作。即使当前的探索策略是随机的更新目标依然基于“假设最优”。这种做法叫离策略学习它的好处是可以大胆探索广泛收集经验再在更新时“节省”掉探索带来的次优性。SARSA更新时用的是“实际下一步会执行的动作对应的Q值”——如果当前策略决定这一轮随机探索那更新目标就带上探索动作的价值。这种做法叫在策略学习它的特点是更保守、更安全。举个例子说Q-Learning像个血气方刚的年轻人看到悬崖边有一块宝他也敢冲SARSA则更像过来人知道离悬崖近一步都会扣血所以他绕着走。这个还不只是性格差异在环境有风险的情况下会直接体现为训练曲线完全不同的走向。我后来在做模拟实验的时候能用这两个算法调出完全不同的行为特征这一段笔记我建议每个人都仔细消化。4.3 深度Q网络和它的教训大杀器也有翻车的时候表格方法的局限显而易见状态一多表就存不下了。深度Q网络也就是DQN把Q-Learning的表格换成神经网络一下子把强化学习推到了高维状态空间。但换上神经网络之后深度学习的老毛病也带进来了训练不稳定、容易发散。DQN原文里最重要的两个工程创新一个是经验回放一个是目标网络。经验回放把所有交互样本存进一个大缓冲区训练时随机抽小批去更新。这解决了一个关键问题深度学习的训练假设数据独立同分布但强化学习交互出来的数据是高度时间相关的。你让agent一步一步走相邻两步动作高度相关直接按顺序喂给神经网络模型会很快陷入某种局部振荡。随机抽一批历史经验来打散这种相关性就是这么个道理。目标网络则是为了解决“追着尾巴咬”的问题。如果拿同一个网络既当裁判员又当运动员那每次更新目标也会跟着变训练目标不稳定梯度更新就容易发散。所以DQN的做法是准备两份参数一份在线更新一份定期同步让更新目标在一个时间段内保持稳定。这两招如今已经是深度强化学习的基础设施但在当初设计出来时是真正挽救了深度强化学习的关键。4.4 策略梯度与Actor-Critic直接调策略绕开价值函数的内耗Q-Learning家族虽然强大但对连续动作空间束手无策——你想选“最大Q值的动作”动作是连续数值的话连max都没法穷举。另一个更根本的问题是价值函数稍有点变化贪心策略就可能导致动作“跳变”策略不够平滑。策略梯度方法直接对策略参数求梯度沿着提升期望奖励的方向调整策略。它不绕弯子目标函数本身就是希望最大化累积奖励于是梯度公式自然导出“沿着奖励更高的方向更新策略概率”。但纯粹的策略梯度也有方差过大的问题——一次好运带来的高奖励会误导策略更新方向。为了解决方差问题大家引入了baseline最常见的就是用价值函数做baseline这样就能让更新方向更聚焦于“比平均水平好多少”。把策略网络和价值网络合在一起就是Actor-Critic结构Actor负责出招Critic负责评价。这个结构后来演化出一大串先进算法——A2C、A3C再到PPO、SAC。PPO它用裁剪目标限制每一步策略更新的幅度让训练过程稳稳当当SAC则干脆引入最大熵的概念让策略在追求奖励的同时保持足够的随机性去广泛探索还能拿到更高奖励的可能。这条演化脉络从Q-Learning到SAC我花了很大的篇幅记录因为理解了这条线就相当于拿到了整个主流深度强化学习的“地图”。5. 复现实验从公式到代码的关键一跳5.1 环境选择与安装跑通一个基线比什么都重要作为读书笔记的一部分我还想强调只读书不写代码等于没读。公式推得再熟练训练起来遇到reward曲线发散、loss爆炸、训练速度慢如蜗牛时你才会真正理解书上那些“训练不稳定”四个字有多沉。我在本地跑实验时选的是常见的OpenAI Gym接口标准环境最经典的是CartPole和MountainCar。这两个环境简单到什么程度一个CPU跑几十分钟就能看到置信的试验结果状态维度低、奖励设置直观特别适合新手从零起步验证算法逻辑。我建议第一次做实验的人一定不要上来就搞Atari那玩意儿渲染开销大不说收敛效果和超参敏感程度会分分钟教你做人。安装环境这一步如果不用容器管理依赖的话版本坑非常多。我给它记了一条经验不要盲目追求最新版就用生态里被验证最多的稳定版本组合。很多人在环境配置上花的时间比写算法还长实属得不偿失。还有一点需要先感受一下把“跑通随机策略”作为第一目标。也就是说先别写任何学习代码只写一个随机乱动的agent观察环境交互的格式是什么样的、step函数到底返回什么、奖励的取值范围大概是多少。这一步不丢人它能帮你过滤掉大量后面因为接口不熟悉而引入的bug。5.2 从零实现Q-Learning的完整检验步骤当初我可以说是把图上那个更新公式背下来了但写代码的时候还是卡了很久。最常见的困惑是Q表的更新到底该放在step之后立即做还是专门等收集了一批数据之后再做答案是Q-Learning逐样本更新每走一步就更新一次对应位置的Q值。我写了一个最简版本的伪代码流程拆成五个步骤初始化Q表全零或者小随机数。观察当前状态s。用ε-greedy策略选择动作a以ε概率随机探索以1-ε概率选当前Q值最大的动作。执行动作a环境返回下一状态s、奖励r、是否结束done。更新Q值Q(s, a) Q(s, a) α * [ r γ * max_a Q(s, a) - Q(s, a) ]注意如果done为True那就没有下一状态的价值只用r作为目标。把这个逻辑用列表或字典维护好Q表整个训练跑起来后你会看到CartPole的杆子撑住时间长度从几十步慢慢涨到几百步然后稳定在500步该环境的默认reward上限。第一次看到这个信号时那种“书上公式真的活了”的成就感比看任何别人的成功视频截图都强。5.3 超参调试的硬经验谁的锅先查谁超参数这个东西看别人调觉得简单自己动手就是灾难。我在笔记里专门记了一张排查表。比如学习率α设得太大会振荡太小则训练慢到怀疑人生探索率ε收敛策略里要慢慢衰减不然agent一直在随机乱走折扣因子γ前面提过数值太小就过于短视奖励的定义则是最容易被忽视的一点——如果奖励设计得不好算法再先进也学不出想要的行为。我先分享两个最典型的调试教训。第一个教训是reward的scale问题。某些环境天然地把每步奖励设成常数正数agent很容易学到“原地不动”这种偷懒策略因为每走一步都有正奖励动了反而可能变负。这种问题根本不是算法层面的而是奖励设计层面的。对策是给每步加一个小的惩罚项逼着agent为了积攒长期回报必须走出更多步数。第二个教训是训练过程需要看多条曲线的倾向性不能只看单次曲线。强化学习训练的随机性极大单次跑出来的曲线可能上下抖动很厉害。我自己的习惯是保住随机种子至少跑三次取平均然后再判断某个算法改动是否有真实提升。所谓“隔三差五的灵光一现”很可能只是随机波动写在自己笔记里当作“有效经验”会害了后面的自己。6. 给同样在啃大部头的读者的五条实在建议6.1 别怕“跳读”但跳读要有策略大部头书最大缺点是让人产生“必须每页都读透”的执念一旦被某处卡住就停滞不前。我现在的建议是初读时有些数学证明可以跳但要记住结论有些细节实验可以跳但要记住直觉唯一不能跳的是每个新概念的定义和实践要点。具体的做法是用数学证明滋养理解力当然好比如贝尔曼方程的最优性证明值得读两遍但有些收敛性分析里出现的辅助定理初读时直接跳过完全不影响后面的体力。跳读要带着目的去跳——知道自己放了什么等后面需要时再回来找。我一般用标签纸夹在没读透的地方写上“待二次阅读、相关章节编号”这样既能保持主线推进又不用怕遗漏。6.2 亲手推一遍关键公式胜过看十遍推导视频看别人推公式会给人“我懂了”的幻觉动手推才知道卡点在哪。我给自己定的规矩是每本书里最重要的三个公式必须亲手推到能脱离书本独立写出来为止。所谓“推”是指在白纸上从定义出发一步步展开、合并、代入直到得出目标等式。刚开始推贝尔曼方程时我在第二步就卡住了——展开累积奖励的无穷级数后不知道怎么把后续项放回成V(s)。卡了二十分钟后我突然意识到这正是折扣因子的作用把后面的无穷项统一提到一个γ剩下的正是V的递归定义。那一刻的顿悟是看任何推导视频都给不了的。笔记里这两页纸至今都是我回头复习时最珍贵的参考。6.3 用“最小实验”验证每个算法直觉对于每个新学到的算法建议用手头最简单的一个环境做最小验证。写一个DQN先在CartPole上跑通再考虑加大难度。这个流程堪比软件工程里的单元测试——先确保模块是好的再组装系统。别直接拿Atari、连续控制环境来验证新实现的正确性那样你根本分不清是环境难还是代码错。另外做实验一定要有一颗敬畏随机种子的心。每次实验前记录seed保证改动前后可以对比。我的一个习惯是所有实验代码都加上一个config字典里面存了seed、gamma、alpha、buffer大小等所有超参。这样跑出来的每个结果都能追溯到当时的配置不会出现“我记得当时好像不是这个设置”的窘境。6.4 用“自问自答”的方式消化每一章读完一章合上书问自己三个问题这一章解决了什么问题、用的核心思想是什么、我对哪个推导还有疑问。答得出来就过答不出来就回翻。这个方法听起来朴素但确实是我读这类书时最高效的留存手段。建议把这个环节写进笔记模板里让它变成每次阅读结束时的一个固定动作。6.5 把笔记当代码仓库来维护定期回顾、重构、标记我自己的强化学习笔记不是一个一次写成的产物而是反复迭代的资料库。新读的文章、新的实验结论、新踩的坑都会被我追加进相应的章节。每隔一段时间我会把旧笔记重新翻一遍把过时的理解修正掉、把冗余的内容删掉、把散落在各处的知识点合并到统一模型里。这跟写代码做重构很像。笔记不是给别人看的展示品而是给自己未来用的工具。当我的笔记已经能够支撑我快速回顾一个算法的思想、公式和实现细节时它才真正达成了“读书笔记”这个名称的最终目标。有些人觉得做笔记费时间但等到你半年后再回看某个算法翻笔记两分钟就找回状态而别人必须重新读三十页书的时候你就会明白这份时间花得有多值。