1. 从零散笔记到体系化认知:为什么值得重新梳理强化学习
很多人第一次接触强化学习,都是被"智能体自己学会玩游戏"这种画面吸引进来的。但真正上手之后,往往会在两三个星期内陷入一种很尴尬的状态:策略梯度、值函数、贝尔曼方程、时序差分这些词全都见过,单独拎出来也能说上两句,可一旦要把它们串成一条线,脑子里就乱了。我自己当年也是这样,笔记记了好几本,公式抄了一堆,但遇到一个新问题,还是不知道该从哪个算法切入。
这篇内容就是围绕"强化学习汇总"这件事展开的。它适合三类人:第一类是刚学完机器学习基础、准备进入强化学习的新手,需要一条清晰的入门主线;第二类是做工程落地、需要快速判断某个场景该用哪类算法的开发者;第三类是把强化学习当工具、想用它解决实际决策问题的研究者。核心目标不是把每个算法讲成教科书,而是把强化学习这张地图画清楚——哪些是地基,哪些是分支,哪些是看起来花哨但实际很少用的旁支。
我个人的体会是,强化学习最劝退的地方不在于数学难,而在于它的知识结构是网状的,不是线性的。监督学习你可以从线性回归一路推到深度学习,路径很直。强化学习不行,它同时牵扯到马尔可夫决策过程、动态规划、蒙特卡洛、时序差分、函数逼近、策略优化这几条线,而且它们之间互相引用。所以"汇总"这件事本身就有价值——它帮你把这张网重新织一遍,让你知道每个节点在整体里的位置。
下面我会按照"先建地基、再分门派、最后谈落地"的思路来展开。中间会穿插一些我踩过的坑和实际调试时的经验,尤其是那些文档里不会写、但真正动手时一定会遇到的问题。
2. 地基部分:马尔可夫决策过程与贝尔曼方程到底在说什么
2.1 把MDP理解成"状态、动作、奖励"的三件套
强化学习所有算法的根都在马尔可夫决策过程(MDP)上。很多人背过它的五元组定义——状态空间、动作空间、转移概率、奖励函数、折扣因子——但背完就忘了,因为没理解它为什么长这样。
我用一个生活化的类比来解释。假设你在玩一个迷宫游戏,你站在某个位置(状态),可以往四个方向走(动作),走到不同格子会得到不同分数(奖励),而且你走到哪一格只取决于你现在在哪、往哪走,跟你之前怎么绕过来的没关系(马尔可夫性)。这四件事加上一个"未来奖励打几折"的系数,就构成了MDP。
这里有个新手最容易忽略的点:马尔可夫性是一个假设,不是事实。现实中很多问题的当前状态并不包含全部历史信息,比如你只看当前一帧画面,是不知道球往哪个方向飞的。这时候要么把历史帧拼起来当状态,要么用循环网络来编码历史。我在做某个仿真环境的时候就吃过这个亏,一开始只把当前位置当状态,结果智能体学出来的策略一直在原地打转,因为它根本分不清自己是刚来还是绕了一圈又回来了。
2.2 贝尔曼方程:把"长远收益"拆成"当下+未来"
贝尔曼方程是强化学习里最核心的一个等式,它的思想其实特别朴素:一个状态的价值,等于你立刻拿到的奖励,加上打折后的下一个状态的价值。
用公式写就是:
V(s) = Σ p(s', r | s, a) [ r + γ V(s') ]看起来简单,但它解决了一个关键问题——它把一个无限长的求和问题,变成了一个递归问题。如果没有这个递归结构,你要算一个状态的价值,就得把所有可能的未来轨迹全展开,那是指数级的爆炸。有了贝尔曼方程,你才能用动态规划、用迭代的方式去逼近它。
我建议新手一定要亲手推一遍这个方程,不要只看结论。推导过程会让你明白为什么折扣因子γ这么重要:γ接近0,智能体就变得短视,只看眼前奖励;γ接近1,它就会考虑很远,但方差也会变大,训练不稳定。实践中γ一般取0.9到0.99之间,具体取多少要看任务的回合长度。回合越长,γ越要接近1,否则远处的奖励根本传不回来。
2.3 动态规划:理想条件下的"标准答案"
有了贝尔曼方程,最直接的做法就是动态规划。策略迭代和值迭代是两个经典方法,它们的思路都是"反复用贝尔曼方程更新价值,直到收敛"。
但动态规划有个硬性前提:你必须知道完整的环境模型,也就是转移概率p(s', r | s, a)你得全知道。现实中这几乎不可能,你不可能提前知道迷宫每一个格子的转移概率,更不可能知道真实世界里每一步会发生什么。所以动态规划更多是理论基石,实际用得少。
不过它有一个重要的教学价值:它告诉你,如果环境完全已知,强化学习问题是可以精确求解的。后面所有的采样方法,本质上都是在"不知道模型"的情况下,想办法逼近这个精确解。理解了这一点,你再看蒙特卡洛和时序差分,就会明白它们不是凭空冒出来的,而是被"模型未知"这个现实逼出来的。
3. 无模型方法的两条主线:蒙特卡洛与时序差分
3.1 蒙特卡洛:等一整局结束再算账
蒙特卡洛方法的逻辑非常直白:我不知道环境模型,那我就实际跑一遍,跑完一整局,看看从某个状态出发最终拿到了多少回报,用这个实际回报来估计价值。
它的优点是无偏——你实际采样得到的回报,期望上就是真实的价值。但缺点也很明显:方差大,而且必须等回合结束。如果任务是没有终点的(比如持续运行的推荐系统),蒙特卡洛根本没法用,因为它永远等不到"结束"。
我在做一个回合制的小游戏时用过蒙特卡洛,效果还行,因为每局很短,几十步就结束了。但换成连续控制任务,它就完全跑不动。这也是为什么后来大家更偏爱时序差分。
3.2 时序差分:走一步就更新,不等结局
时序差分的核心思想是"自举"——我不等最终回报,我用当前估计的下一个状态的价值,加上这一步的实际奖励,来更新当前状态的价值。这就是TD误差的来源:
δ = r + γ V(s') - V(s)这个δ就是"我原本以为会得到这么多,实际走了一步发现差了多少",然后用它来修正估计。
时序差分的好处是可以在线学习、方差小、不需要等回合结束。代价是它引入了偏差,因为它用的是估计值去更新估计值。但这个偏差在实践中通常可以接受,而且随着训练会逐渐减小。
这里有个经验:TD(0)和蒙特卡洛其实是两个极端,中间还有TD(λ)这种折中方案。λ取0就是纯TD,λ取1就接近蒙特卡洛。实际调参时,如果发现训练方差太大,可以适当调大λ;如果发现收敛太慢,就调小λ。这个参数在很多教程里一笔带过,但它对训练稳定性影响很大。
3.3 Q-Learning与SARSA:同门师兄弟的关键分歧
说到时序差分,就绕不开Q-Learning和SARSA。这两个算法长得几乎一样,但有一个本质区别:Q-Learning是离策略的,SARSA是在策略的。
具体来说,Q-Learning更新时用的是"下一个状态下最大的那个Q值",不管实际下一步会走哪个动作;SARSA用的是"实际下一步真正采取的那个动作的Q值"。这个差别听起来很小,但影响很大。
Q-Learning更激进,它假设你以后总是选最优动作,所以学出来的策略更"贪心";SARSA更保守,它考虑了你实际会怎么走,包括你探索时可能走的"烂棋"。在悬崖行走这个经典例子里,Q-Learning会贴着悬崖边走(因为理论上最优),而SARSA会绕远一点(因为它知道探索时可能掉下去)。
我实际用下来,如果探索率衰减得比较慢,SARSA通常更稳;如果探索率很快就降下去,Q-Learning收敛更快。选哪个要看你对"训练过程中的安全性"有多在意。
4. 从表格到神经网络:函数逼近带来的能力与麻烦
4.1 为什么表格方法撑不住真实问题
前面说的Q-Learning、SARSA,本质上都是表格方法——用一个表格存每个状态动作对的Q值。状态少的时候没问题,但真实问题的状态空间往往大得离谱。比如一张84×84的灰度图,状态数就是256的7000多次方,这个表格根本存不下。
所以必须用函数逼近,也就是用一个参数化的函数(比如神经网络)来近似Q值或策略。这就是深度强化学习的起点。
但函数逼近引入了一个新问题:不再有收敛保证。表格方法可以证明收敛,但一旦换成神经网络,理论保证就没了,训练可能震荡、可能发散、可能陷入局部最优。这也是为什么深度强化学习调参这么难——你面对的是一个没有理论护栏的系统。
4.2 DQN的三个关键技巧:经验回放、目标网络、奖励裁剪
DQN是深度强化学习的里程碑,它用神经网络来近似Q值。但直接把Q-Learning和神经网络拼在一起是跑不起来的,DQN靠三个技巧才把它救活。
第一个是经验回放。它把智能体的经历存到一个缓冲区里,训练时随机采样。这样做的好处是打破了样本之间的时间相关性。神经网络训练假设样本是独立同分布的,但强化学习的样本是连续产生的,前后高度相关,直接拿来训练会导致灾难性遗忘。经验回放把这个问题缓解了。
第二个是目标网络。如果更新时用的目标值也是由当前网络算出来的,那目标本身在动,训练就像追自己的影子。DQN用一个隔一段时间才同步一次的目标网络来算目标值,让训练稳定下来。
第三个是奖励裁剪。不同任务的奖励尺度差别很大,有的任务奖励是0到1,有的是负几百。把奖励裁剪到[-1, 1]区间,能让不同任务用同一套超参数。
这三个技巧我在实际复现时都踩过坑。经验回放缓冲区太小,训练会不稳定;目标网络同步太频繁,等于没有;奖励不裁剪,学习率稍微大一点就发散。这些细节文档里都会提,但只有自己调过才知道它们有多关键。
4.3 Double DQN、Dueling DQN与优先经验回放
DQN之后有一系列改进。Double DQN解决的是Q值高估问题——因为Q-Learning总是取最大值,而最大值是有偏的,会导致估计偏高。Double DQN把"选动作"和"评估动作"分开,用两个网络分别做,缓解了高估。
Dueling DQN把Q值拆成"状态价值"和"动作优势"两部分,让网络更容易学到"这个状态好不好"和"这个状态下哪个动作更好"这两件事。在有些动作对结果影响不大的状态下,这个结构学得更快。
优先经验回放则是让那些TD误差大的样本被采样得更频繁,因为那些样本说明网络还没学好。这个思路很直观,但实现时要注意重要性采样权重,否则会引入偏差。
我个人的建议是:先把基础DQN跑通,再逐个加改进。一次性把所有技巧都堆上去,出了问题你根本不知道是哪个环节的锅。
5. 策略梯度与Actor-Critic:另一条完全不同的路
5.1 从"算价值"到"直接学策略"
前面讲的都是值函数方法——先学价值,再根据价值选动作。策略梯度走的是另一条路:直接参数化策略,用梯度上升来最大化期望回报。
这条路的好处是:可以处理连续动作空间(值函数方法在连续动作上取max很麻烦),可以学随机策略(有些场景随机策略比确定性策略更好),而且收敛性理论上更平滑。
策略梯度的核心公式是:
∇J = E[ ∇log π(a|s) * G ]其中G是回报。直观理解就是:如果某个动作带来了高回报,就增大它的概率;如果回报低,就减小它的概率。这个思路非常符合直觉。
但纯策略梯度有个大问题:方差极高。因为G是整条轨迹的回报,波动很大。所以实践中几乎不会直接用原始的策略梯度,而是会加各种降方差技巧,比如减去基线、用优势函数代替回报。
5.2 REINFORCE的朴素与局限
REINFORCE是最基础的策略梯度算法,它用整条轨迹的回报来更新。实现简单,但方差大、样本效率低。我在一个小型连续控制任务上试过,跑了几十万步才勉强学到一点东西,换成Actor-Critic之后几万步就有明显效果。
所以REINFORCE更多是教学用,让你理解策略梯度的基本形式。真正落地时,基本都会用Actor-Critic。
5.3 Actor-Critic:一个决策,一个打分
Actor-Critic的核心思想是:用两个网络,一个Actor负责选动作,一个Critic负责评估这个动作好不好。Critic给出的评估值(通常是状态价值或优势函数)用来降低Actor更新的方差。
这就像你打球时,一个人负责打,一个人负责在旁边点评。点评的人越准,打球的人进步越快。Critic就是那个点评的人。
A2C、A3C、PPO、SAC这些算法本质上都是Actor-Critic的变体,区别在于怎么算优势、怎么限制更新幅度、怎么处理探索。其中PPO是目前工程上最常用的,因为它稳定、好调、对超参数不敏感。SAC则在连续控制任务上表现很好,因为它引入了最大熵框架,鼓励探索。
我实际用PPO最多,它的clip机制确实让训练稳定了很多。但要注意,PPO对优势估计的准确性比较敏感,如果Critic学得不好,Actor也会跟着跑偏。所以训练时一定要盯着Critic的loss,如果它一直降不下去,说明价值估计有问题,这时候调Actor的学习率是没用的。
6. 算法选型:面对一个新问题该怎么下手
6.1 先判断动作空间是离散还是连续
这是选型的第一道分水岭。离散动作空间(比如上下左右、几个按钮)优先考虑DQN系列;连续动作空间(比如机械臂关节角度、油门刹车力度)优先考虑DDPG、TD3、SAC或PPO。
我见过有人拿DQN去做连续控制,然后花了两周时间试图把动作离散化,结果精度怎么都上不去。其实直接换SAC,一天就能跑出像样的结果。选型错了,后面再努力都是白费。
6.2 再看样本效率和训练稳定性
如果环境交互成本很高(比如真实机器人、真实用户),样本效率就是第一优先级,这时候优先考虑离策略算法,比如SAC、TD3,因为它们可以用经验回放反复利用旧数据。如果环境是仿真器,可以随便跑,那在策略算法比如PPO就更省心,因为它稳定。
下面这张表是我自己总结的选型参考:
| 场景特征 | 推荐算法 | 理由 |
|---|---|---|
| 离散动作、状态简单 | Q-Learning / SARSA | 实现简单,收敛快 |
| 离散动作、状态高维 | DQN / Double DQN | 能处理图像等高维输入 |
| 连续动作、样本宝贵 | SAC / TD3 | 离策略,样本效率高 |
| 连续动作、追求稳定 | PPO | 对超参数不敏感,易调 |
| 多智能体、合作任务 | MADDPG / QMIX | 考虑智能体间交互 |
| 稀疏奖励、长序列 | 分层RL / 好奇心驱动 | 需要额外探索机制 |
6.3 稀疏奖励:最容易被低估的难点
很多真实任务的奖励是稀疏的——只有最终成功才有奖励,中间过程全是0。这种情况下,随机探索几乎不可能碰到奖励,智能体学不到任何东西。
解决思路有几类:一是奖励塑形,人为设计中间奖励,但这需要领域知识,而且设计不好会引入偏差;二是好奇心驱动,用预测误差作为内在奖励,鼓励智能体探索新颖状态;三是分层强化学习,把长任务拆成子任务,每个子任务有自己的奖励。
我在一个导航任务里遇到过这个问题,一开始奖励只在终点给,智能体跑了上百万步都没到过终点。后来加了基于距离的塑形奖励,很快就学会了。但塑形奖励的权重很关键,太大智能体会只盯着中间奖励不走完全程,太小又起不到引导作用。
7. 实操中那些文档不会告诉你的坑
7.1 随机种子:不是玄学,是必须控制变量
强化学习对随机种子极其敏感。同一个算法,换个种子,结果可能从"完美收敛"变成"完全不收敛"。所以做实验时,一定要跑多个种子取平均,至少3个,最好5个。只跑一个种子得出的结论基本没有参考价值。
我早期就犯过这个错,看到某个算法跑出一个好结果就以为找到了最优方案,结果换种子一跑就崩了。后来养成习惯,所有对比实验都跑5个种子,画均值和方差,结论才靠谱。
7.2 超参数:学习率和折扣因子是重灾区
学习率太大,训练震荡;太小,收敛慢到怀疑人生。折扣因子太大,方差大;太小,智能体短视。这两个参数几乎决定了训练的成败。
我的经验是:先用一个中等学习率(比如3e-4)跑一遍,看loss曲线。如果loss剧烈震荡,就降学习率;如果loss几乎不动,就升学习率。折扣因子一般从0.99开始,如果回合特别长(上千步),可以调到0.995甚至0.999。
还有一个容易被忽略的是网络初始化。策略网络的最后一层如果初始化得不好,初始策略会极端偏向某个动作,导致探索不足。常见的做法是把最后一层权重初始化得很小,让初始策略接近均匀分布。
7.3 环境并行:加速训练的关键手段
强化学习训练慢是出了名的。一个有效的手段是并行多个环境,同时收集数据。A3C就是用多个worker并行跑环境,PPO也支持多环境并行。
并行环境的好处不只是快,还能增加样本多样性,让训练更稳定。但要注意,并行环境数不是越多越好,太多会导致每个环境分到的更新频率降低,反而影响学习。一般8到16个环境是比较常见的配置。
7.4 评估:训练 reward 高不代表策略好
这是新手最容易踩的坑:训练时的平均回报上去了,就以为模型学好了。但训练时的回报是带探索的,而且可能过拟合到训练环境。真正要看的是在固定测试环境、关闭探索后的评估回报。
我习惯每隔一段时间就跑一次确定性评估,用固定的几个种子,看评估回报的曲线。如果训练回报涨但评估回报不涨,说明过拟合了,要么加正则,要么减网络容量,要么增加环境多样性。
8. 把知识串起来:一条可执行的入门路线
如果让我给一个刚入门的人排一条学习路线,我会这样安排:
第一周,把MDP和贝尔曼方程彻底搞懂,能手推值迭代和策略迭代。这一步不要求写代码,但要求能在纸上把一个小MDP的价值算出来。
第二周,实现表格版Q-Learning和SARSA,在FrozenLake或CliffWalking这种小环境上跑通。重点观察探索率、学习率、折扣因子对结果的影响。
第三周,实现DQN,在CartPole上跑通,然后逐步加经验回放、目标网络、Double DQN,每加一个看效果变化。
第四周,实现REINFORCE和A2C,理解策略梯度和Actor-Critic的区别。然后在连续控制环境上跑PPO。
第五周开始,挑一个自己感兴趣的真实问题,从选型、搭环境、调参到评估完整走一遍。这一步最重要,因为只有完整走一遍,你才会真正理解前面那些算法各自的边界在哪里。
我自己的经验是,看十篇教程不如自己完整复现一个算法。复现过程中遇到的那些报错、不收敛、调参调不动的问题,才是真正让你成长的东西。教程里的代码都是跑通的,但你自己写的时候,一个维度对不上、一个激活函数选错,结果就完全不一样。
最后分享一个我常用的调试技巧:先把问题简化到极致。如果算法在复杂环境上不工作,先换一个极简环境,比如状态只有几个、动作只有两个、回合只有几步。如果极简环境都跑不通,那说明是算法实现有问题,不是环境太难。等极简环境跑通了,再逐步增加复杂度。这个思路帮我省下了大量瞎调参的时间。