说实话,强化学习领域最劝退新手的,不是那堆数学公式,而是“算法流派”这个概念。很多人一开始学Q-learning,觉得挺简单,然后突然跳到策略梯度,发现概念全变了:一个在估值,一个在选动作,完全对不上。等到后面又看到DDPG、SAC这些既有值函数又有策略网络的“混合体”,更晕了。这篇就是把基于值函数、基于策略,以及现代实践中绕不开的混合方法,按照我自己的理解梳理一遍,把每个流派解决什么问题、有什么坑、到底怎么选讲清楚,希望能节省你翻几十篇论文的时间。
1. 为什么强化学习算法会分成两大流派:先估值还是先选动作
要理解两大流派,得先回到强化学习最底层的逻辑。智能体在一个环境里不断试错,每一步都根据当前状态选择一个动作,环境返回奖励和下一时刻的状态,目标是让长期累积回报最大。这个框架本身没有争议,分歧出现在“怎么逼近最优策略”这件事上。
第一类思路,也就是基于值函数的方法,核心问题是:我先估计一下,在某个状态或者某个状态动作对下,未来累积回报期望是多少。一旦这个值估准了,策略自然就出来了,照着Q值最大的动作走就行。这类方法的代表是Q-learning、DQN这一系。
第二类思路,基于策略的方法,直接跳过“估值”这步,把策略本身参数化,用一个神经网络直接输出动作的概率分布,然后通过梯度上升去调整参数,让期望回报最大化。REINFORCE、PPO都属于这类。
打个比方。值函数方法像先画地图再找路:我先搞清楚每条路通向哪、那边有什么奖励,然后选择价值最高的路径。策略方法像是直接练方向感:我不关心整张地图,只优化自己“往哪个方向走”这个行为本身的概率。
这个区分看起来清楚,但很多人会在Actor-Critic算法那里卡住,因为它名字里带“Critic”,Critic本质就是一个值函数网络。实际上,现代主流算法几乎没有纯粹的“策略方法”,也没有纯粹的“值函数方法”,基本都是两者结合。所以标题里说的“总结”,重点不是区分纯度,而是理解这两条设计思路是如何演化、如何融合的。
理解了这两大流派的不同起点,后面所有算法都串起来了。
2. 值函数流派的主线:从Q-Learning到深度Q网络的一系列演化
2.1 表格时代的Q-Learning:TD目标是怎么撑起整个体系的
基于值函数的方法,最早的经典是Q-Learning。它维护一张Q表,里面是每个“状态-动作”组合对应的价值估计。更新公式是关键:
Q(s,a) ← Q(s,a) + α * (r + γ * max_a' Q(s',a') − Q(s,a))
这个公式里最核心的,是括号里那部分,叫时间差分误差(TD误差)。它用一个即时奖励r,加上下一状态的最优Q值乘以折扣系数γ,去逼近当前Q值的目标。注意这里取的是max,也就是说,Q-Learning在更新时,默认下一步会采取最优动作,哪怕当前behavior策略是epsilon-greedy。这让Q-Learning成了典型的off-policy算法,它可以用任意探索策略产生的数据来学习,因为更新时用的是“虚拟的最优决策”,而不是“实际执行的决策”。这是理解整个值函数流派的钥匙。
但表格Q-Learning天花板很明显:连续状态空间根本没法用,一张表存不下。于是深度学习登场,用神经网络来做Q函数的函数近似。这就是DQN的由来。
2.2 DQN那三个关键机制:为什么每个都是必需品
DQN在2015年刚出来的时候惊艳全场,但它的成功不是“把Q表换成神经网络”这么简单,而是同时引入了三个机制,缺一个训练都可能崩掉。
第一个是经验回放(Experience Replay)。智能体把与环境交互得到的转移样本(s, a, r, s')存进一个缓冲区,训练时随机抽样。这么做有两个好处:打断样本之间的时间相关性,防止网络在连续相关的状态上过度更新;同时让一份数据能被多次利用,提高样本效率。我早期手写DQN时偷懒没做回放,结果训练曲线像心电图一样乱跳,后来才明白相关性对梯度更新的影响有多大。
第二个是目标网络(Target Network)。Q-learning更新时需要用到Q(s', a'),如果这个值也来自正在训练的网络,那等于“自己给自己当老师”,参数一变,目标也跟着变,训练非常不稳定。DQN的做法是每固定步数,把当前网络参数拷贝到目标网络,让目标在一段时间内固定下来。现在很多算法的“软更新”也是同样的思路,只是每次只拷贝一小部分。
第三个是奖励裁剪和误差裁剪。把奖励限制在[-1, 1],把TD误差限制在一定范围,防止个别大奖励样本造成梯度过大。这些细节在论文里可能一句话带过,实践时少了任何一个,CartPole可能还勉强能跑,换成Atari那种复杂环境基本必挂。
2.3 DQN的改进家族:Double、Dueling、Prioritized分别治什么病
DQN稳了之后,研究者陆续发现它还有几类问题,于是出现了各种改进版本。我按“它到底在治什么病”来梳理,比按年份背论文要清晰得多。
Double DQN治的是Q值过高估计的问题。因为max操作天然会放大估计误差,过拟合的价值被当成最优价值。Double DQN的思路是:用在线网络选动作,用目标网络评估该动作的价值,把“选动作”和“算价值”分开,打断过高估计的链条。
Dueling DQN治的是Q值冗余估计问题。它把Q(s,a)拆成状态价值V(s)和动作优势A(s,a)。好处是,在某个状态下,无论做什么动作影响都不大的时候,网络不用艰苦地区分每个动作细微的价值差,可以先学好状态本身的优劣,再学动作带来的增益。这个结构在面对很多动作价值接近的环境时收敛更快。
Prioritized Experience Replay治的是回放采样效率问题。普通经验回放是均匀采样,但一条经验的价值不是等同的:TD误差大的样本,往往意味着当前价值函数对它判断很不准,更新这种样本能带来更多信息。所以按TD误差大小给样本加权采样,学习效率能明显提升。
这些改进并不冲突,实践里完全可以叠加起来用。现代很多强化学习框架里的“Rainbow”就是把当时能叠的改进基本都叠了一遍。值函数方法这条线发展到这,已经非常强大了,但它始终有个绕不过去的坎:动作空间。
2.4 值函数方法的边界:离散动作是主场,连续控制需要另寻出路
值函数方法最自然的使用场景是有限离散动作空间,比如游戏里的上下左右、网格世界的移动方向。为什么?因为策略是从Q值直接导出的,需要通过argmax选出Q值最大的动作。如果动作是连续的,比如机器人关节力矩是任意实数,你就没法枚举所有动作来求argmax。
有人会想,不行就离散化呗?把动作范围切成一堆小格子,每个格子当一个动作。但动作维度一高,就会遭遇维度爆炸:两个连续维度各切10份就是100个动作,10个维度就是100亿个动作,argmax根本枚举不过来。所以在这个阶段,值函数“老大哥”的位置就开始让给策略梯度方法了。
3. 策略梯度流派的核心:从REINFORCE到Actor-Critic的工程化之路
3.1 REINFORCE的直觉:把“概率大的动作”往“回报高的方向”推
策略梯度方法的出发点完全不同,不估计Q值,而是直接把策略π_θ(a|s)定义成一个带参数θ的分布,目标函数是期望累积回报J(θ)。REINFORCE是这个流派的元祖,它的梯度表达式长这样:
∇J(θ) = E[Σ_t ∇θ log π_θ(a_t|s_t) * G_t]
拆开看,G_t是从t时刻往后的累积回报。这个公式的直觉是:如果一个动作的回报G_t为正,就增大这个动作在对应状态下的概率;如果回报为负,就减小概率。乘上log概率的梯度,本质就是在做“好动作更大概率被选,坏动作更小概率被选”。它是典型on-policy算法,只能用当前策略采样的数据来更新,数据用完就弃。
REINFORCE在理论上是完备的,但实际用起来方差巨大。因为蒙特卡洛采样的G_t是一个完整轨迹的回报抽样,受环境随机性和策略随机性影响很大。今天跑一个轨迹回报是+10,明天同一个起点可能跑到-5,梯度方向可能完全相反。这就像你应聘时只看一两次面试结果就决定自己该不该改方向一样,噪声太大,根本没法稳定学习。
3.2 引入Baseline与Actor-Critic:为什么“和平均水平比”比“看绝对数字”靠谱
一个最经典也最有效的方差削减手段,是在G_t前面减去一个基准线b(s_t),变成G_t − b(s_t)。做这个减法不会改变梯度的期望,但能大幅减少方差。b(s_t)通常选什么?最常见的就是当前状态的价值估计V(s_t)。这样一来,G_t − V(s_t)其实就是在衡量“这个动作的实际回报,比这个状态的一般水平好多少”,这个量就是优势函数A(s_t, a_t)。
这个思路直接催生了Actor-Critic结构:Actor是策略网络,负责输出动作分布,按优势函数的方向更新;Critic是价值网络,负责估计V(s)或者Q(s,a),给Actor提供优势值作为“反馈信号”。一个负责干活,一个负责评判。
A2C、A3C在此基础上加了多环境并行采样来打破样本相关性,GAE(广义优势估计)用一组可调参数λ对多步TD误差做加权平均,在偏差和方差之间取平衡,这些工程化改进让策略梯度方法从“只能跑经典玩具问题”变成了“能跑复杂控制任务”的可用工具。但要稳定训练,还有一个组织性的问题没解决。
3.3 TRPO到PPO:策略更新步子太大为什么危险
策略梯度方法的另一个老大难问题是:每次梯度更新时,新策略和旧策略不能差太远。为什么?因为训练数据是用旧策略采样的。如果一次更新后策略变化太大,新策略下的状态分布和旧策略可能完全不一样了,我们手里的样本就失效了,就像用去年的地图找今年的路,差得太远根本找不到。而且策略分布一旦突变到糟糕区域,回救代价极高。
TRPO的思路是约束每次策略更新的KL散度不超过一个阈值,用数学上严格的方式保证新旧策略不会偏离太远。但TRPO的实现复杂度很高,需要二阶优化。PPO的聪明之处在于,用一阶方法近似实现类似效果:对新旧策略的概率比施加clip裁剪,把重要性采样的比值限制在[1−ε, 1+ε]之间。当比值超出范围时,梯度直接不更新,这样既能限制更新幅度,又不需要复杂的共轭梯度计算,工程实现极其简单。
我在实践里非常推荐用PPO调试,不是为了性能多顶尖,而是因为它对超参数宽容度是所有策略梯度算法里最高的,学习率稍微偏一点、网络规模稍微改一改,通常都能收敛到不错的解。很多人说强化学习难调参,PPO是那个“默认从它开始”的算法。
3.4 策略梯度流派总结:表达能力变强了,样本效率也变差了
策略梯度方法的核心优势有三个。第一,天然处理连续动作空间,输出高斯分布的均值方差,采样动作就是一次forward,不需要argmax。第二,可以表达随机策略,在你们博弈、不完全信息环境里这是刚需。第三,策略参数化后,能把领域先验知识直接塞进网络结构,比如用动作掩码屏蔽非法动作。
代价也很明确:方差大、样本效率低。因为on-policy特性,每条数据只能被当前策略用一次,收集几百万步样本可能只训出一点进步。这也是为什么在真实机器人控制这类“样本珍贵”的场景里,off-policy算法越来越受欢迎。
4. 现代实践绕不开的混合体:DDPG、TD3与SAC为什么成了默认选项
4.1 DDPG:用“确定性策略梯度”搭桥,把两派缝合起来
之前说值函数方法处理不了连续动作,策略梯度方法又不擅长利用旧数据。DDPG就是这两个问题的一次缝合尝试。它引入了确定性策略μ(s),输出的是一个确定的动作向量,而不是动作的概率分布。既然动作确定,那么Q函数的argmax就不再是“枚举所有动作”,而是“对策略网络的输出求梯度、往上走”。
具体做法是:Critic(Q网络)按照类似DQN的方式更新;Actor(策略网络)的梯度方向,是让Q值对“当前策略输出动作”的期望最大。数学上用链式法则,把Q对动作的偏导传回Actor的参数。训练数据来自经验回放缓冲区,所以它是off-policy的,数据利用率远高于PPO。训练时为了充分探索,还在确定性动作上加了随机噪声,一般用Ornstein-Uhlenbeck噪声,也就是一种“有惯性”的探索:一旦走偏,会在较长时间内保持趋势,适合连续控制场景。
DDPG的大方向是对的,但实践里比较脆。它的Q值过高估计问题很严重,而且Actor和Critic同时训练时,任何一个的网络参数抖动都会放大到另一个上,整个训练过程像两个人互相搀扶却一起脚滑。
4.2 TD3如何用三个修改把DDPG补强
TD3几乎是直接针对DDPG的毛病做的补丁,三个核心改动让稳定性提升了一大截。
第一个是双Q网络。两个Q网络独立估计Q值,更新目标时取两者中较小的那个,既能避免过高估计,也顺带让价值估计偏保守一点点,这在工程上被验证非常有效。
第二个是延迟更新Actor。Actor更新频率低于Critic,通常是Critic每更新两次,Actor才更新一次。先让Q函数多学一会儿、估值稳定一点,再用来引导策略更新,避免“用不准确的估值瞎带策略”。
第三个是目标策略平滑。为了矫正Q值在“动作空间某些狭窄的尖峰”上估值过高,在计算目标值时,给下一时刻的动作加上一点小噪声,并在一定范围内裁剪。这等价于对策略做了正则化,让Q函数对相似的动作输出相似的价值,训练更平滑。
TD3的实现难度不高,稳定性却比DDPG好得多,所以我个人会直接建议:如果要用DDPG,不如直接上TD3。
4.3 SAC:最大熵框架带来的探索红利与自动调温机制
SAC是目前连续控制任务里口碑最好的算法之一,它的思想比TD3再进一层。它在回报函数里加了一个熵项,策略不再只追求“累积回报最大”,而是追求“累积回报 + 熵”最大。熵衡量的是动作分布的随机程度,熵越大,策略越不确定。把熵放进目标,等于在“利用已知的好动作”和“保持探索、别早早锁死”之间做了显式的权衡。这个权衡系数α在实现里还能自动调节:如果策略输出变得太确定,熵项权重自动上调,逼它继续探索;如果熵已经很大、探索过度,权重自动下调。这个“自动调温器”省掉了大量调参工作量。
SAC的actor输出的是一个随机策略(通常用重参数化技巧采样),结合双Q网络、经验回放和目标熵更新,在样本效率和稳定性上都表现得非常均衡。我在多个仿真控制任务上对比过,SAC通常能用比PPO少一个数量级的交互步数达到同等性能。
4.4 DDPG、TD3、SAC的取舍对照
| 算法 | 策略类型 | Off/On-policy | 探索方式 | 稳定性 | 调参难度 |
|---|---|---|---|---|---|
| DDPG | 确定性 | Off-policy | OU噪声 | 较低 | 较高 |
| TD3 | 确定性 | Off-policy | 噪声+平滑 | 高 | 中 |
| SAC | 随机 | Off-policy | 熵自动调节 | 高 | 中低 |
实际项目里,要是交互数据非常贵(比如真实机器人),优先考虑SAC和TD3这类off-policy方法;要是环境并行度高、很容易开几百个环境一起采样,PPO这种on-policy方法的scale-up能力强,训练墙钟时间反而更短。
5. 选型与实战:这些算法到底怎么挑,以及我踩过的几个坑
5.1 先看动作空间:离散还是连续,直接决定大类
选型第一件事永远是看动作空间。离散动作空间,如果规模不大,Double DQN或Dueling DQN就能解决;规模非常大比如组合优化、文本生成那种,还需要考虑额外技巧。连续动作空间,直接进入策略梯度或混合方法阵营;TD3和SAC优先,不想用off-policy、想要可控的更新幅度,PPO稳妥起步。
但如果环境本身就很简单,比如状态维度很低、只有几个动作的小型控制问题,不需要上深度网络,表格Q-Learning或者简单的Sarsa反而更可控。别为了用深度学习而用深度学习,这是很多新手第一个翻车点。
5.2 Off-policy和On-policy的取舍,决定了你要不要囤数据
再往下选,就要看你的数据来源够不够多。模拟环境里开一千个并行采样器很便宜,on-policy完全没问题,PPO就是为这个场景设计的。真实机器人环境里,一局几万步要跑几十分钟,数据极其宝贵,必须把这些数据反复利用,那就选off-policy,用经验回放的SAC、TD3、DQN系都是合理选择。这两类算法不能想混就混,强行在PPO里加一个缓冲区,只会让梯度估计偏离当前策略,训出来的结果离预期很远。
5.3 我实际运行中总结出的几个超参数经验
奖励尺度是第一个大坑。我之前在某个仿真控制任务上,直接用环境原始奖励(动不动上百上千),结果Q值爆炸,训练曲线直接NaN。后来把奖励缩放、裁剪到一个稳定范围,同一套网络结构突然就训出来了。核心原则是Q network能稳定估计的奖励范围要匹配:太大就裁剪,太小就放大。
网络初始化也值得较真。策略网络的最后一层,一定要把输出缩放到动作范围的一个小比例上。比如动作范围是[-1, 1],最后一层权重初始化为0.01倍的正态分布,这样策略一开始的输出几乎是0附近,不会一启动就在动作边界附近反复震荡。强化学习里“开始越稳,后面越好”是常态。
exploration噪声要按环境特性来定。DDPG那套OU噪声在物理学环境里效果好,但TD3里简单的高斯噪声往往就够了。噪声幅值还得和训练进度挂钩,固定幅值经常导致后期收敛后仍然有大扰动,长期在最优动作附近抖来抖去。
经验回放缓冲区大小、目标网络软更新系数τ这些参数,通常遵循论文默认值就行,不需要过度折腾。但要注意,同一个任务,τ取0.005和取0.05,稳定性差距可能天壤之别。我会建议先固定其他参数,只调τ,在各组随机种子下跑早期几十万步,看方差再定。
5.4 复现算法时的三条忠告
第一条,一个算法要改,每次只改一个变量。强化学习里变量之间互相影响,同时改网络容量、奖励尺度、探索噪声,出了问题根本没法定位。第二条,随机种子不是玄学,是科学。强化学习训练结果在生态学上极其依赖随机种子,正式比较算法时至少跑5个种子取均值方差,只用一次实验结果说明某个算法好,结论完全不可信。第三条,先复现经典环境,再上自定义任务。在标准测试环境上,论文给的默认参数应该能达到近似论文的水平,达不到,先去debug自己的实现,别急着换环境、换网络结构。
5.5 给新手的路线建议
如果你刚开始学,我的建议是:第一步,在表格环境里搓一遍Q-Learning和Sarsa,理解了TD更新、on/off-policy的区别;第二步,实现一遍DQN,把经验回放和目标网络改成去掉的形式,亲眼看着训练从崩到稳;第三步,实现一遍REINFORCE,感受一下高方差是什么体验,再给它加个baseline,感受方差削减的威力;第四步,上PPO,体会clip如何把“策略更新幅度”这件事变可控;最后再看DDPG、TD3、SAC,理解两个流派是怎么融合的。
强化学习的知识结构是一棵长了很多分支的树,树的根就是“值函数估计”和“策略搜索”这两条主线。只要这两个根握住了,后面看再多的变体、再花哨的算法,都能快速归位。玩算法的这些年,我越来越觉得,最值钱的不是背下某篇论文的伪代码,而是知道一种算法在什么场景下会失效、为什么失效。这也是我现在做一个新任务时,基本在十分钟内就能定下“用SAC还是PPO”的原因所在。
提示:上面提到的所有实现细节,如果你在跑不通某个算法,先查一下是不是奖励尺度、网络最后一层初始化、目标网络更新频率这三个环节出了问题。这三个地方我见过的故障率最高,排查优先做。