先说一个我自己的体会:很多朋友刚接触深度强化学习,会把 DQN、PPO、SAC 当成三个“新算法”来逐个学。但实际上,它们是同一波技术演进里长出来的三根枝干——理解清楚它们的来路、架构差异、以及训练中真正卡人的坑,你才能在面对一个新任务时,知道该拿哪个模型打底,而不是把三个都调到怀疑人生。
这篇文章我不打算写一遍教科书式的原理复述,我想按自己的理解,把三条技术线从“为什么会出现”讲到“关键架构长什么样”,再补上我在实际训练里反复踩过的坑,以及一些直接能用的调参经验。无论你是刚准备跑第一个 DQN 的入门选手,还是被 PPO 的 reward 曲线搞得一头雾水的进阶玩家,这篇应该都能给你省下来不少试错的时间。
1. 从表格到函数逼近:为什么 DQN 是后来所有东西的起点
1.1 强化学习的本质问题与 Q-Learning 的瓶颈
强化学习的核心任务其实一句话就能说清:让智能体在与环境交互的过程中,学会一套策略——看到某个状态时,采取哪个动作能长期收益最大化。这里的“长期”很关键,它意味着智能体不能只看眼前的一步反馈,而要估计未来的累计回报。
经典表格型 Q-Learning 的思路是把“状态-动作”的每一种组合都放在一张大表里,不断用贝尔曼方程去更新表项。这套方法在小状态空间里非常漂亮,比如若干格子组成的小迷宫、几台机器的调度问题,表格完全可以存下所有情况。但问题是,一旦状态变成了图像像素、机器人关节角度、或者连续的传感器读数,状态组合就会爆炸——别说遍历所有状态,你连维护一张表的内存都撑不住。
所以 DQN 出现之前,学术界其实已经卡了很久:强化学习的理论很漂亮,但根本碰不到真实世界的问题。Q-Learning 在小型离散问题上跑得挺好,可一上规模就失效,那时候很多人甚至觉得强化学习只能停留在“教学玩具”阶段。
1.2 DQN 的两个关键改造:经验回放与目标网络
DQN 之所以成为深度强化学习的奠基之作,是因为它做了几个非常巧妙的工程化改造,把深度神经网络的强项和强化学习的试错机制接上了。
第一个改造是经验回放。智能体和环境交互产生的数据(状态、动作、奖励、下一个状态)被存进一个缓冲区,训练时随机抽取小批量来更新网络,而不是像传统 Q-Learning 那样用哪条数据就立刻更新哪条。这么做至少有三层好处:一是打破了样本之间的时间相关性,不然相邻帧几乎一模一样,网络会被带着来回震荡;二是每一条经验可以被反复使用多次,数据利用率一下子高了很多;三是让训练集分布更加稳定,因为网络更新时看到的是一批来自不同时刻的数据混合。
第二个改造是目标网络。原本 Q-Learning 更新用的是自己的估计值,这就好比一个人站在镜子前面量身高,量出来的数据又被自己参考,很容易出现“自我指认”的误差放大。DQN 的做法是额外维护一份网络参数,每隔若干步才从当前网络同步一次,计算目标值的时候用这份“冻结”的旧参数,让追逐的目标相对稳定。这一步在实操中帮了很大的忙,训练曲线的震荡问题肉眼可见地减轻了。
1.3 DQN 的后续补丁:Double、Dueling、优先回放
原始 DQN 有个臭名昭著的问题:它倾向于过估计 Q 值,因为 max 操作天生就会挑乐观的那个结果。于是有人做了Double DQN,用当前网络选择动作、用目标网络评估这个动作的价值,把“选”和“评”拆开,过估计问题就缓解了很多。
Q 值本身的定义是“在这个状态下选这个动作,后续能拿多少回报”,它可以拆成两部分:状态自身值多少(V)、这个动作能比平均水平好多少(优势值 A)。Dueling DQN就是这么直观,网络输出的头部分成 Value 和 Advantage 两条分支,最后再合并成 Q 值。这个改动听起来小,但效果非常实际——在有些状态下动作选择影响本来就小,网络不需要花力气区分每一个动作的优劣,可以专心学状态基准值,训练效率会高不少。
还有优先经验回放,原理特别朴素:不是所有经验都一样重要,那些 TD 误差大的样本说明预测偏差大、信息量丰富,应该给更高的采样概率。实际操作中我还喜欢做个权衡,加上一个均匀采样的混合比例,既能抓住关键样本,又不会让训练全部围着几条异常数据打转。
这一整套 DQN 系家族,解决了“深度网络能不能配合强化学习稳定训练”的问题,也是后来 PPO、SAC 这些更复杂算法的基础。虽然现在很少有人直接拿 DQN 做生产级控制,但它的思想渗透在所有后续算法里,值得好好讲清楚。
2. PPO 的策略梯度进化:用稳定的步子换训练的可控性
2.1 策略梯度家族的基本逻辑与方差难题
DQN 属于价值学习方法,核心是学一个能打分的 Q 函数,再根据分数选动作。还有一条完全不同的路线——策略梯度,直接学一个策略函数,输入状态,输出动作的概率分布(或者均值方差),然后顺着“让高回报动作概率变大”的方向做梯度更新。
这条路线的好处是自然支持连续动作空间,比如机器人的关节力矩、自动驾驶的方向盘角度,这类情况下 DQN 的“穷举动作离散标签”思路基本施展不开。但策略梯度也有心头大患:方差。因为每个轨迹的累计回报是从一次采样里估算出来的,运气好坏会让估计值忽高忽低,同一个策略跑两次,梯度的方向可能都会不一样,训练很难收敛。
2.2 TRPO 到 PPO:置信域约束如何被简化
在 PPO 出现之前,Trust Region Policy Optimization(TRPO)已经给出了一个重要答案:每次更新策略的时候,不要让新旧策略差异过大,用一个 KL 散度约束把更新限制在“信任区域”内,保证每一步都是稳定地往好的方向挪。TRPO 的理论很美,但工程上很痛苦——它需要计算二阶导数,还得处理复杂的约束优化,很多跑过 TRPO 的朋友应该都记得那个训练速度有多煎熬。
PPO 的目标就是把这些复杂度干掉。它不再硬性约束 KL 散度,而是直接在目标函数里用一个clip 操作:让新旧策略的概率比保持在 1±ε 的范围内,一旦超出这个窗口,就裁掉对应的梯度信号。这个改动的哲学很有意思,它不严格要求每一步都“理论最优”,而是保证每一步“不会太差”,用大量小而稳的更新累积出效果。
我见过很多人在解释 PPO 时只讲数学公式,忽略了背后的工程诉求。PPO 本质上是一个“可工程落地”的 TRPO——训练稳定、超参敏感度低、速度还快,所以在连续控制、游戏博弈、甚至一些大语言模型对齐任务里都成了默认基线。
2.3 PPO 的架构细节:Actor-Critic 与 Generalized Advantage Estimation
PPO 的典型架构是 Actor-Critic:Actor 网络负责输出策略分布,Critic 网络负责估计状态价值。这两个网络可以分开,也可以共享底层特征提取层,实际应用中共享居多,尤其处理图像输入时,共用卷积层能省下一大笔参数。
训练中的关键组件是GAE(Generalized Advantage Estimation),它负责计算“当前动作比平均好多少”的优势值。GAE 里有一个 λ 参数,在偏差和方差之间做平衡:λ 越接近 0,估计越偏短期、方差小但偏差大;λ 越接近 1,越看重长期累积收益、方差变大但更准。我自己的使用习惯是,大多数任务从 λ=0.95 起步,如果训练曲线太毛糙就适当调小一点。
PPO 还有一个常见细节是熵正则项,在目标函数里加上一点策略分布的熵,鼓励智能体保持一定的随机性,防止过早陷入某个局部动作。这有点像探险时要保留一点“乱走”的偶然性,训练后期我会慢慢降低熵系数,让策略逐渐变自信。
2.4 PPO 训练过程中的典型失败模式
PPO 虽然稳定,但绝对不是拿过来就能收敛的。最常见的一种失败是策略崩坏:某次更新之后,策略一下子变成只会输出同一个动作,环境的交互陷入死循环,之后无论再怎么训练都回不来。这个问题多半因为 clip 范围不够紧、学习率过高,或者 GAE 设置让优势估计的偏差过大。
另一种几乎人人都会遇到的问题是训练曲线前期不升反降。新手第一次跑 PPO 的时候,看到前几百轮 reward 往下掉,第一反应是赶紧加学习率或者换网络结构——其实大错特错。PPO 的前期探索成本非常高,策略还在到处乱试,自然会有大量的低回报时段;这时候最该做的是稳住超参,给它足够多的环境步数去摸清状态空间。
在写代码时有几个工程细节也很折磨人:多进程采样环境,主进程和子进程之间如果数据同步不当,会白白等掉很多时间;reward 的数值尺度问题会让网络更新震荡,我一般会做 reward 归一化或者 clipping;分布式训练时,不同 worker 的数据在 batch 里的配比也会影响训练稳定性。这些问题不写在公式里,但实际训练十个小时里有六个小时是在跟它们搏斗。
3. SAC 的最大熵框架:让探索不再是“随机碰运气”
3.1 最大熵思想是什么:除了回报还要最大化多样性
SAC(Soft Actor-Critic)在 DQN 和 PPO 之后出现,它想解决的是另一个难题:探索和利用怎么长效平衡。DQN 用 ε-贪心做探索,大部分时间按最优动作来,小部分时间随机乱试;PPO 靠策略分布的固有随机性探索,但训练后期策略越来越肯定自己,探索能力会跟着萎缩。
SAC 的核心思路非常优雅:把“最大化累计回报”这个目标升级成“最大化累计回报加上策略熵”。熵本身衡量分布的混乱程度,策略熵大,意味着动作选择更多样。最大化熵的本质是:在追求高性能的同时,策略还要保持一定的随机性,这样一方面探索更主动,另一方面也不容易陷入某一个狭隘的局部最优。
我可能会用一个类比帮你理解:普通强化学习像是给你指了一条去目的地最快的路,你就机械地反复走;最大熵强化学习让你同时考虑“这条路走得快不快”和“有没有走过其他有趣的路”,最终训练出一个“有很多条路都走得不错”的策略。对于后续要面对干扰、环境微小变化的任务,这种多样性非常宝贵。
3.2 Soft Policy Iteration:从 Q 函数到策略的双层迭代
SAC 的数学框架建立在 Soft Policy Iteration 上,分成两个交替进行的步骤——策略评估和策略改进。策略评估阶段,在固定的当前策略下,计算一个“soft Q 函数”,它不仅考虑未来回报,还要把未来每一步的策略熵也算进去;策略改进阶段,更新策略网络,让它输出的分布向“按 soft Q 值加权后更优”的方向靠近。
这整个框架落地到网络结构上就是标准的 Actor-Critic 形态:一个 Actor 网络输出动作分布的均值和方差,一个 Critic 网络评估 soft Q 值。SAC 里通常会同时维护两个 Q 网络,更新时取两者的最小值作为目标值,这个 trick 用来压制 Q 值过高估计的现象,实际效果非常明显,训练曲线要稳得多。
这里有一个很容易迷糊的点:SAC 是 off-policy 算法,它和经验回放配合得很好,可以利用旧策略收集到的数据来更新新策略。这意味着 SAC 的数据利用率比 PPO 高很多——PPO 是 on-policy 算法,用一批数据更新完就扔掉,SAC 则像 DQN 一样把数据存进缓冲区反复使用。
3.3 自动温度系数的价值:熵在训练过程中的动态变化
SAC 的目标函数里有一个温度系数 α,控制熵项在整个优化目标里的权重。α 大,探索倾向更强,策略更随机;α 小,策略更专注地去压榨高回报动作。早期 SAC 的这个系数是手动调的,非常麻烦,因为训练过程不同阶段对探索的需求不一样——前期策略对任务一无所知,需要大量探索;后期策略已经比较像样了,就应该逐渐收敛。
后来的改进版本就引入了自动温度调整机制,给 α 设定一个最小熵的约束目标,用梯度方法自动调节。如果当前策略的熵低于目标,α 就自动调大,强迫策略多一点随机性;反之 α 则调小,让策略更专注利用已知的好动作。
我试过手动调 α,也试过自动调节,后者几乎是无脑胜出。尤其当你面对一个新任务、对探索需求完全没有先验认知的时候,自动温度能帮你省掉一整轮网格搜索。不过自动温度也不是万能药,目标熵设多大还是有讲究的,我会在后面的训练优化章节细说。
3.4 SAC 相比 PPO 的优势与代价
SAC 最大的优势就是数据效率。PPO 在 MuJoCo 连续控制任务上通常需要几百万步环境交互才能收敛,SAC 往往用一半甚至更少的步数就能到达接近的水平。这一点对真实物理环境极其重要,因为真实机器人跑一步要实际花时间、耗磨损,环境采样成本比仿真里翻几百倍,谁采样效率高谁就是王道。
但 SAC 也有它别扭的地方。它对超参更敏感,学习率、目标熵、网络规模不对,训练就会呈现出一种“一动不动、突然起飞、又突然崩溃”的诡异节奏。而且 SAC 虽然是 off-policy,但对缓冲区里数据的新鲜度有要求,回放缓冲区如果太陈旧,策略更新时学的全是过时经验,反而会拖慢训练。
所以我的经验是:如果你的环境交互成本低、能跑大量并行采样,PPO 的简单稳定性更省心;如果环境采样贵、数据来不易,或者你希望用尽量少的交互做出能用的策略,SAC 值得作为首选。
4. 架构设计的共性规律与动作空间的适配
4.1 连续控制与离散控制分别怎么选网络结构
很多人在看算法原理的时候忽略了动作空间类型对网络结构设计的决定性影响。拿到一个任务,先问自己是连续控制还是离散控制,这个问题的答案直接决定了你输出的动作层该怎么设计。
离散动作空间,比如游戏里“上下左右”、推荐系统里“展示哪一类内容”,网络的输出层一般是几十几百个节点的 softmax,节点数等于可选动作个数。DQN 是这类任务最经典的方案,PPO 也能处理,只要把策略分布的构建方式换成 categorical distribution 就行。
连续动作空间,比如机器人控制,输出通常是一个向量,每个元素对应当前要控制的某个物理量。Actor 网络此时一般输出多元正态分布的均值和对数标准差(或者用别的分布族),采样得到具体动作。SAC 和 PPO 都支持连续控制,但内部细节差异很大——SAC 会用到重参数化技巧(从分布采样时加一个可导的噪声路径),PPO 则直接对采样动作做梯度估计。如果动作空间还有边界限制(比如关节角度最多 180 度),我一般用 tanh 输出层把动作压缩到有限范围,同时在处理概率密度时要用相应的分布变换公式,不然梯度算出来是错的,这个细节很多人会漏掉。
4.2 共享底层特征是否真的合适
Actor 和 Critic 共享特征提取层是工程上的常规做法,但这里有一个被忽视的坑:Actor 和 Critic 的学习节奏不一致。特征提取层同时接收两个网络的梯度,如果其中一个网络先收敛,反传梯度就可能“带偏”另一边的更新方向。
处理这种情况有几个招:一是从共享特征层之后分别接独立的全连接层,让两边的深层决策互不干扰;二是给两路梯度设置不同比例,实验里我见过把 actor 的梯度缩放调低到 critic 的十分之一,训练耐用度会好很多;三是干脆用两个完全独立的网络,代价是参数量和推理耗时都上来了。
在有图像输入的场景里,共享卷积层几乎总是划算的,因为视觉特征的通用性非常强,没必要让两套卷积各学一遍。但在纯低维向量输入的控制任务里,我个人越来越倾向于分开——因为输入维度不高,特征提取本身开销不大,分开带来的稳定性收益更值。
4.3 网络初始化、归一化与输入重标定
神经网络初始化在监督学习里算是“基础修养”,但在强化学习里格外敏感,因为强化学习的训练信号本身来自与环境交互的采样,噪声极大。如果我初始化不当,网络刚起步时的输出分布可能就让智能体在环境里寸步难行,连有效的探索数据都采不到。
常用的做法是让策略网络最后一层的权重往小里初始化,让初始动作分布的方差保持适中,不要一开始就在边界值。输入观测做归一化也是近乎强制的要求——如果状态数据里某项特征的尺度是另一项的几十倍,网络会花很大力气去抵消这个数值差,白白浪费容量。图像类输入我会做像素归一化,状态向量类输入我会统计各个维度的均值和方差在线做标准化,这些在别处都是习惯性操作,放到强化学习里就变成救命稻草了。
5. 训练优化实战:从一片混沌到曲线收敛
5.1 奖励塑形的基本原则与常见的坑
如果说算法选择决定了训练的上限,奖励塑形则很多时候决定了你到底能不能训出来。自然界给智能体的反馈通常极其稀疏,围棋赢了一局才有 +1 分,中间几万步都是 0;这种稀疏奖励下,随机探索几乎不可能碰到正反馈,学也学不动。
奖励塑形的思路是给中间过程提供稠密的引导信号,比如机器人每靠近目标一点点就给一个小分数。但这里有个著名的坑:奖励塑形很有可能会改变原问题的最优策略。举个例子,你给“前进速度”设置了一个正向奖励,智能体可能就学会了在原地疯狂抖动腿,因为这样也能刷速度分,而完全忽略真正要走到目的地这个目标。
一个安全得多的做法是用基于势能的奖励塑形,理论保证不会改变最优策略。不过实际项目里我见到最多的还是靠定性分析——想一想哪些行为是“必然路径上的一部分”,给它们适度的中间奖励,然后反复观察训练轨迹是不是合理。这里没有一劳永逸的公式,只有一遍遍看 rollout 录像的笨功夫。
5.2 超参数误区:网格搜索之外的调参顺序
关于调参,我最想打破的一个迷思是:有问题就乱调学习率。RL 训练的问题是高度耦合的,一个超参异常往往会以“曲线不涨”的形式表现,但根源可能在完全不相关的地方。
我自己比较习惯的排查顺序是:先确认 reward 的数值尺度没有夸张到让梯度爆炸;然后看环境采样速度、数据管线有没有瓶颈;再检查网络结构和动作分布是否匹配任务;最后才动学习率、batch size、clip 范围这些“经典超参”。之所以把学习率放在后面,是因为大多数情况下学习率稍微偏高或偏低训练都能在某种程度上运行,反而是数据问题、奖励问题会让训练完全陷入死局。
batch size 这个参数在 RL 里和监督学习很不相同。监督学习里大 batch 一般更稳定,但在 RL 里,batch 太大反而可能让单次更新过度自信地覆盖掉多样本信息,尤其 on-policy 算法对 batch 的构造方式更敏感。我的经验是,PPO 的 batch 覆盖的环境步数最好能分散到多个时间点,而不是一长串同一个轨迹里的连续片段。
5.3 在真实项目中,reward 尺度波动是布线问题而非学习问题
这条经验我想单独拿出来说,因为它坑过我好多次。很多 RL 新人看到 reward 曲线剧烈波动,第一反应就是算法不收敛、又要调参了。但真实项目里,reward 波动大的时候,往往先去检查环境或者数据管道的 bug——轨迹采集线程是否稳定、各 worker 的奖励计算逻辑是否一致、reward 的数值是否被人无意间改变过尺度和偏移。
有一次我在某个模拟环境里训练 PPO,曲线像过山车一样晃。花费整整一个下午逐个排查后才发现问题:某个采样线程的初始状态分布没有正确 reset,导致不同 worker 看到的初始条件差异巨大,回报方差自然爆炸。这和算法参数一点关系都没有。所以我现在的习惯是,一切“反直觉的剧烈劣化”都先假设是代码或环境问题,确认干净了再去考虑算法层面的优化。
5.4 训练中断恢复与评估指标设计
强化学习训练时长经常达到几小时甚至几天,中途机器重启、进程被杀都很常见。很多框架默认不保存优化器状态,恢复训练时只能从头再来,这是非常痛苦的经历。我现在的习惯是每隔一定训练步数保存完整 checkpoint,包括模型参数、优化器状态、回放缓冲区(如果是 off-policy 算法)以及学习率调度器的当前状态。
评估指标方面,不要只看训练过程的平均 reward。训练过程 reward 包含了大量探索噪声,容易被异常样本带偏。我会定期让当前策略脱离探索噪声,做一次“确定性评估”:PPO 取策略分布的均值作为动作,SAC 同样用均值,跑若干条固定初始条件的轨迹,算平均回报。这个指标稳定得多,也是判断模型是否真正在进步的标准。很多人在训练中看到 reward 没涨就以为模型没学,其实只是探索噪声太大,确定性指标往往能带来完全不同的结论。
6. 算法选型的最终建议:什么时候用谁
到这一步,三大算法的技术画像已经比较清晰了。我用自己的语言总结一下选择逻辑,真心建议你把它贴在显示器旁边:
任务状态空间小、动作离散、需要快速出原型,DQN 系(含 Double、Dueling、优先回放)是最稳妥的起点。它结构简单、调参优先级清晰、训练速度快,特别适合验证环境的奖励设计是否合理。
任务环境采样成本比较高、期望数据更充分地复用,或者动作空间连续且要保留较强的探索能力,选 SAC。它在连续控制领域几乎是默认标准,但请做好“超参敏感+调起来更折磨”的心理准备。
任务环境可以大量并行采样、希望整个训练过程稳定可控、并且对策略的随机性有一定容忍,或者你希望有一套通用性最强的管线,PPO 是永远不会让你彻底失望的选择——它可能不是数据效率最高的,但在绝大多数问题上都能达到相当不错的成绩。
我的建议是:别一开始就抱着“我要把 SAC 调好”的念头,先花点时间用最简单的算法快速验证环境和奖励曲线,把基础设施打磨顺了,再上更高级但更娇贵的算法。很多时候光靠环境并行化和奖励设计的清理,就已经能解决大半训练问题,根本没有换算法的必要。
7. 一个让你少熬夜的小技巧:先画一个最小的 smoke test
最后分享一个我个人的习惯,也是在各种 RL 项目里反反复复证明有价值的做法:在跑任何完整训练之前,先构建一个“迷你烟雾测试”。
这个测试的核心思想是:从头到尾跑一整套流程,但只设置很小的网络、极少的训练步数、最简单的环境变体,确保整条管线是完全能走通的。很多人在大训练开始之后才发现某个维度对不上、某个 normalization 层报错、某个缓冲区的 shape 不匹配,然后看着已经空跑的机器发呆。烟雾测试五分钟内能不能跑完所有逻辑,就能帮你提前拦掉至少八成这类低级错误。
烟雾测试通过之后,我还会跑一个过拟合小实验:固定初始条件,给智能体一个非常容易的任务,看看它能不能在几十步内获得显著提升。如果这一步也通不过,那说明算法、奖励、网络结构里有一个地方存在本质性错误,就别急着去跑大规模训练了。
深度强化学习的三大算法,说穿了还是围绕“稳定地更新 + 高效地探索 + 充分地利用数据”这三件事在打转。DQN 解决了深度网络与 RL 结合的安全性问题,PPO 在策略梯度路线里找到了稳定性与实施成本的平衡点,SAC 在数据效率和探索主动性上又往前走了一步。理解它们各自的取舍逻辑,比背住任何一组公式都更有长期价值。希望这篇梳理能帮你在自己的任务上少踩几个坑,跑出更干净、更好看的收敛曲线。