YAOTU INSIGHTS

强化学习进阶:从DQN到PPO四个核心算法详解

强化学习进阶:从DQN到PPO四个核心算法详解
能点进这一篇说明你已经在强化学习门口探过头了。入门(1)里我们聊过 MDP、贝尔曼方程、Q-learning知道 Agent 是在用试错换奖励但那时候面对的基本是格子世界、状态表这种玩具。这回要聊的 DQN、REINFORCE、Actor-Critic、PPO是把“查表”升级成“函数逼近”把强化学习推到真实任务里的四个关键台阶。如果你是做算法落地、或者想用 RL 解决某个实际控制问题这篇文章给出的都是能直接抄作业的底层逻辑。先说结论这四个算法不是四个毫无关系的发明而是一条清晰的演进线——DQN 解决了“状态太多记不下”的问题REINFORCE 解决了“动作不能离散枚举”的问题Actor-Critic 顺手把 REINFORCE 的高方差治了PPO 又把 Actor-Critic 的更新步长管住了。下面拆开讲。1. 为什么需要四个算法而不是一个万能模型很多初学者会问难道不能把 Q-learning 里面的 Q 表换成神经网络就万事大吉了吗真没那么简单。表格型方法有两个天花板一是状态维度一旦上去表根本存不下二是它天生的决策方式是“取所有动作里 Q 值最大的那一个”面对连续动作空间时最大化这个操作本身就很难做。打个比方查表法像是一个背下了所有路口标志的司机但你把摄像头画面、雷达点云、地图信息全部塞给他他反而不知道看哪条路。DQN 做的事情是把 Q 表换成神经网络用网络输出每个离散动作的 Q 值决策层仍然是贪心。所以 DQN 适合离散动作、高维状态的任务比如游戏画面、推荐候选集排序动作只有几个到几十个的情况。REINFORCE 走的是另一条路不估计 Q 值直接优化策略本身。策略网络输出的不是一个数而是一个动作概率分布这就天然支持连续动作——连续场景下让网络输出高斯分布的均值和方差采样一个动作出来决策完成。这样一来argmax 的噩梦消失了但代价是它用一整局游戏的真实收益来推断“哪个动作好”方差大得惊人。Actor-Critic 是折中方案策略网络继续管“怎么做”价值网络负责告诉策略“这一步做得怎么样”相当于给莽撞的策略梯度配了一个教练把单次采样的噪声压下去。而 PPO 则是在 Actor-Critic 的框架上对策略更新幅度加了一道保险每次迭代只允许策略走一小步。原因很简单——大步走容易一脚踩空这回更新把策略改坏了下一回的采样数据也跟着废了整个训练直接崩盘。PPO 用裁剪目标函数限制步长实现简单、效果稳所以现在是工业落地的默认首选。1.1 表格型方法留线下来的两个隐坑如果你自己写过 Q-learning应该能围住那个场景环境反馈有随机性、样本之间存在强相关性训练特别容易震荡。表格方法里可以用足够小的学习率勉强压住但换成神经网络后这两个问题会被剧烈放大。第一个坑是样本间不独立智能体在环境里采集到的相邻状态高度相关你用这些挨着的样本喂梯度下降梯度方向会被带偏。第二个坑是目标不稳定TD 更新需要用到“下一个状态的估计值”如果估计值本身来自我们正在训练的网络就相当于狗追自己的尾巴永远追不上。这两个坑分别催生了 DQN 的两个标配组件经验回放和目标网络后面详说。2. DQN从 Q 表到神经网络关键动作不止换层皮先回忆 Q-learning 的更新目标新 Q 值 当前 Q 值 α×(r γ×max(a)Q(s,a) - Q(s,a))。表格时代我们直接用这张表查下一个状态的最大 Q但把 Q 换成网络之后计算目标的那条路必须变。我见过很多人第一次写 DQN直接写出这样的式子target reward gamma * q_net(next_state).max() loss (target - q_net(state).gather(action)).pow(2).mean()然后训练完全崩掉。原因就是我用到的目标q_net(next_state)和正在更新的q_net(state)是同一个网络每一次梯度更新都会把目标值也推着跑。训练曲线看着像过山车其实是目标在四处乱晃。所以 DQN 的核心设计是双网络解耦训练网络Q_θ每一步都更新目标网络Q_θ⁻参数定期从训练网络拷贝平时冻结不动。计算 TD 目标时一律用Q_θ⁻。这样在几轮更新内目标是相对稳定的网络才能安心拟合。常见做法是每 C 步同步一次或者用软更新θ⁻ τθ (1-τ)θ⁻τ 取 0.005 这类小值我实际用下来软更新更稳。经验回放则是把交互数据 (s, a, r, s, done) 存进一个缓冲区训练时随机抽样一个 batch。原理上就是把时序相关的数据打乱成近似独立的样本喂给梯度下降。缓冲区大小我一般设 10000 到 100000太大会让旧数据占比太高训练变慢太小则抽样还是邻居样本独立性的好处发挥不出来。2.1 DQN 的完整伪代码与细节参数下面是我在多个控制类模拟项目里验证过能跑的流程环境以 CartPole 这类离散动作场景为例初始化 Q 网络参数 θ目标网络参数 θ⁻ θ 初始化经验缓冲区 D for 每个回合: 重置环境得到初始状态 s while 未结束: 以概率 ε 随机选动作否则选 a argmax_a Q(s, a; θ) 执行动作 a得到 r, s, done 将 (s, a, r, s, done) 存入 D s s if D 中样本数足够: 从 D 随机采样 batch 计算目标 y r γ × (1 - done) × max_{a} Q(s, a; θ⁻) 用 MSE 更新 θ 每个 C 步同步 θ⁻ θ有几个参数几乎决定了训练能不能成必须说清楚折扣因子 γ单步奖励范围内取 0.9~0.99任务越长 γ 越大。CartPole 这种短任务 0.95 够了长程任务可以调到 0.99。探索概率 ε一开始 1.0逐步衰减到 0.05 左右。衰减太慢玩了好久还在瞎试衰减太快前期没探索充分就随便收敛。学习率1e-3 到 1e-4 之间过大容易震荡过小半天不见反应。奖励裁剪也是 DQN 落地时的老朋友。把奖励限制到 [-1, 1]最大的好处不是让奖励“变小”而是让损失函数的量级可控。不同环境奖励尺度差异很大有的三位数、有的零点几不裁剪的话同一个学习率换个环境就失效了。裁剪后用相对统一的学习率换环境的痛苦会少很多。实操心得训练 DQN 时如果发现 Q 值一路飙升但不收敛优先怀疑目标网络同步频率太高。每 10 步同步一次和每 1000 步同步一次效果天差地别。这个参数不需要调到极限但一定要作为第一排查对象。2.2 DQN 为什么还会高估以及 Double DQNDQN 还有一个天生毛病max 操作会引入正向偏差。因为 Q 网络有拟合误差取最大值时会把偏高估计的那个动作选中一次次 bootstrap 下来Q 值普遍虚高最后策略变得过分激进。Double DQN 的修法特别朴素用训练网络选动作用目标网络给这个动作打分。y r γ × Q(s, argmax_{a} Q(s, a; θ); θ⁻)也就是说选动作和估值动作分给两个网络来干。这个改动只要两行代码Q 值高估问题肉眼可见地缓解。后来还有 Dueling DQN把 Q 分解成 V 和 advantage、优先经验回放提高高 TD 误差样本的抽样权重这些都值得在 DQN 稳定之后加进去。但如果你是第一次写 DQN先把基本版跑通再谈花活。3. REINFORCE最朴素但被低估的策略梯度REINFORCE 是策略梯度家族的老祖宗。它没有价值网络也没有经验回放就是直接让好动作出现的概率变大、坏动作概率变小。好坏的依据是什么在一局游戏结束后看实际拿到的总回报。策略网络记为 π_θ(a|s)θ 是网络参数。优化的目标函数是期望回报J(θ) E[Σ γ^t r_t]对 θ 求梯度利用 log 技巧化简得到∇J(θ) E[Σ ∇θ log π_θ(a_t|s_t) × G_t]其中 G_t 是从时刻 t 开始往后算的折扣累积回报。更新时我们用采样到的梯度做随机梯度上升θ ← θ α × Σ ∇θ log π_θ(a_t|s_t) × G_t理解这个公式的关键点在于G_t 是同一个轨迹里对“这个动作后势”的度量。如果这一局整体得分高那每一步动作的概率都会被抬高如果得分低所有动作的概率都会被压低。问题恰恰在这里这个 G_t 只采了一个样本方差极其大。同一局游戏因为随机性涨了 10 分你会以为每个动作都是好动作策略猛涨一波下局又跌回去。这就是 REINFORCE 最让人难受的地方。实测下来REINFORCE 在 CartPole 上倒也能收敛但需要极小的学习率再加上大量训练回合。流程如下初始化策略网络 π_θ for 每个回合: 按 π_θ 采样一整个轨迹 计算每一步的 G_t从后往前累加 更新 θ ← θ α Σ ∇log π(a_t|s_t) G_t 清空轨迹重新采样3.1 减 baselineREINFORCE 的救星一个很小的改动可以明显改善 REINFORCE 的方差在 G_t 上减去一个基线 b。基线不依赖当前动作所以它在期望上不会改变策略梯度却能降低方差。公式变成θ ← θ α × Σ ∇θ log π_θ(a_t|s_t) × (G_t - b)最简单的 b 就是这一局的平均回报。原理不难理解原来 G_t 有正有负、波动极大减去 b 之后高于平均水平的事件的概率被抬升低于平均的则被压低梯度方向更“有的放矢”。实际操作中我用一个滑动平均回报作为基线就够了比每次都算严格的期望基线要省事得多。REINFORCE 虽然方差大但它的价值在于“教学上极简”。你再去看 Actor-Critic、PPO 时就会发现里面那个策略梯度的骨架和 REINFORCE 一模一样。谁要是上来就啃 PPO大概率会被一堆附带组件绕晕先折磨一下 REINFORCE把“策略梯度核心思想”刻在脑子里后面通通是加补丁。实操心得用 REINFORCE 时必须等整个回合结束才能更新。任何“边跑边更”的想法都会破坏梯度估计。如果你想提升效率可以跑多个并行环境收集好几条轨迹一起更新不要缩短轨迹。3.2 REINFORCE 适合什么场景按我的经验REINFORCE 在实际项目里很少单独扛大梁但它适合三类场景轨迹很短的任务、你只是想快速验证策略梯度流程、或者作为对照基线。如果任务是那种一局只有几步的决策问题REINFORCE 的方差没有想象中大性能反而意外地能看。同时它给你一个最公平的参照物——后面加 value network、加 GAE、加裁剪看看每一项补丁到底带来多少提升。4. Actor-Critic让价值网络给策略当教练REINFORCE 的方差大是因为单条轨迹的 G_t 噪声大。如果有一个函数能预测“从状态 s 出发按当前策略能拿多少预期收益”我们就能拿它当基线甚至用它替换 G_t这样单步就能更新不用等整个回合结束。这个想法直接催生了 Actor-Critic。这套架构里有两位角色Actor策略网络输出动作概率分布负责跟环境打交道Critic价值网络估计状态价值 V(s)负责评估当前位置“好不好”。关键区别在于策略梯度里用的量从 G_t 换成了优势函数 A_tA_t r_t γV(s_{t1}) - V(s_t)用生活类比V(s_t) 是你来到位置 s 时“原有的预期工资”r γV(s) 是迈出这一步之后“新的预期”。两者之差就是这一步带来的额外收益。如果这一步比预期好A_t 大于 0策略就往这个方向调整如果比预期差策略就往回收。Critic 本身是通过 TD 学习来更新的最小化的是 TD 误差L_critic (r_t γV(s_{t1}) - V(s_t))²这个结构带来了 REINFORCE 做不到的优势可以每步环境交互后立刻更新不需要等一局打完采样方差明显下降训练曲线稳定不少。当然代价是系统里多了个价值网络多个近似器就多一层麻烦——Actor 在引导策略但 Critic 自己也在学习如果 Critic 学得差Actor 就被错误信号带偏。4.1 一个能跑的 Actor-Critic 流程初始化 Actor 网络 π_θCritic 网络 V_φ for 每个回合: 重置环境得到 s while 未结束: 从 π_θ 采样动作 a 执行 a得到 r, s, done 计算 A r γV_φ(s) - V_φ(s) 将 A 作为权重更新 Actorθ ← θ α ∇log π(a|s) A 更新 Criticφ ← φ - β ∇ (A)² s s看到没有这个流程和 REINFORCE 最大的区别就是不再累积整局轨迹而是一步一更新。Actor 用于学习的信号不是“全局回报”而是 Critic 给出的单步优势。这是速度上的质变。不过新手容易踩一个坑把 Critic 当成一个大号监督回归模型来训结果发现 loss 一直不降就慌了。Critic 的标签也是它自己生成的是一个移动目标所以 Critic 的 loss 曲线只能作为参考不能像分类任务那样解读。我一般只看 Actor 的平均回报曲线Critic loss 只要不发散就不管它。4.2 为什么单独提 A3C 的变体不讲了很多人见过 A3C、A2C 这类名字它们本质仍然是 Actor-Critic区别主要在于多环境并行和异步同步上。并行环境的核心意义是打破样本相关性、提高数据吞吐在单机小实验里收益有限。我建议入门阶段专注理解“Actor-Critic 结构”本身并行扩展以后遇到性能瓶颈再说否则一次引入多线程调试容易把自己劝退。实操心得Actor 和 Critic 的学习率往往不一样。Critic 可以稍微大一些因为价值函数相对容易拟合Actor 要小一些策略梯度天生不稳学习率过大会出现“上一轮还好好的下一轮直接全瞎”的现象。我习惯 Actor 1e-4、Critic 3e-4 起步。5. PPO现在项目里出现频率最高的默认算法PPOProximal Policy Optimization本质上还是 Actor-Critic多出来的是它对策略更新步长的控制。为什么要控制想象你在山路上开车前面的策略是当前采样策略我们用这批数据估计了一个前进方向。如果这一步迈太大策略变得面目全非那这批数据就不再代表新策略的分布所有估计全部失效。所以 PPO 的哲学是每次迭代策略只能小心翼翼前进不能放飞自我。它用重要性采样比例来表达新旧策略的差异ρ_t(θ) π_θ(a_t|s_t) / π_θ_old(a_t|s_t)比值大于 1 代表新策略让这个动作出现的概率变高了小于 1 代表变低了。整个目标函数是L_CLIP(θ) E_t [ min(ρ_t A_t, clip(ρ_t, 1-ε, 1ε) A_t) ]这个式子看着吓人拆开讲其实很直白。如果 A_t 0说明这个动作好我们希望 ρ_t 往上走但如果 ρ_t 超过 1ε就直接被截断不再继续奖励这条路防止新策略因为一步好运就把动作概率猛顶上天。如果 A_t 0说明这个动作差我们希望 ρ_t 变小但如果 ρ_t 掉到 1-ε 以下同样被截断防止因为一次坏结果就把这个动作永久判死刑。ε 常见取值 0.2意思是每次更新的最大偏差被压在 20% 上下。这个裁剪机制好看就好在实现只需几行却能近似实现严格置信域优化的效果不需要像 TRPO 那样计算复杂约束。5.1 完整 PPO 落地流程下面这个流程是 PPO 最常用的 PG 式版本没有那些花哨扩展但足够稳定初始化 Actor π_θCritic V_φ for 每次迭代: 用当前策略在环境中采样 T 步数据存入 buffer 计算每一步优势 A_t可用 GAEλ 取 0.95γ 取 0.99 对 buffer 中的数据循环 K 个 epoch 小批量更新 Actor使用 L_CLIP 小批量更新 Critic使用 MSE 损失 清空 buffer“采样 T 步、更新 K 轮”是 PPO 和前面算法的关键区别别用一批数据更新一次就丢而是重复用好几次。这也是 PPO 数据利用率高的原因之一。一个值得一提的 trick 是更新 epoch 数不是越大越好设 5 到 10 就已经够用设到 20 以上往往出现过拟合当前批次、后续采样崩溃的现象。5.2 优势估计与 GAE 的配合实际用 PPO 时我不会只用最简单的单步优势 A r γV(s) - V(s)。因为只有一步偏差太大方差是低了但有偏。GAE 的做法是把多步优势加权平均用 λ 控制偏差和方差的天平。λ0 退化成单步优势λ1 退化成蒙特卡洛累计优势。我默认取 0.95这在大部分环境中都稳。再补一个实用的细节把优势在 batch 内做归一化减去均值除以标准差可以极大缓解不同环境的奖励尺度差异带来的影响。我见过不少项目只做归一化就换来一大截提升。PPO 还有其他变体比如用 KL 惩罚约束新旧策略、用自适应系数调节惩罚权重等但入门阶段用裁剪版就够了。它又好实现又稳定我的几个模拟控制项目全部是裁剪版没有翻过车。6. 这四个算法怎么选场景、动作空间与坑位对照说了这么多很多人到了这里真正纠结的是我手上的任务到底该用哪一个下面这张表是我做算法选型时的基本判断依据不保证百分百最优但至少不会让你入场就扑街。任务特征推荐算法原因离散动作、状态高维图像类DQN 及其变体决策通过 argmax Q 完成对离散动作天生合适连续动作、轨迹短REINFORCE baseline实现简单方差在短轨迹下还可控连续动作、需要在线更新Actor-Critic单步可更新响应快适合持续交互场景连续动作、稳定性和效果优先PPO裁剪限制步长超参数鲁棒落地最省心极大离散动作空间几万起步PPO 或策略梯度类离散动作多时 argmax 仍然困难直接输出概率更方便再补充几个容易踩的场景判断误区。很多人一看到动作是“数字”就认为是连续动作但有些控制任务其实动作天生离散比如控制面板上只有几个挡位。反过来有些看起来是分类的问题比如从商品池里选一个推荐实质上动作空间是上万个离散项这个规模下 DQN 的效率就很低用策略梯度输出概率分布配上采样反而更自然。所以选算法之前先把动作空间的性质彻底想清楚比刷多少论文都有用。6.1 实际项目的观察指标不管用哪个算法我都会盯三个指标而不是只盯着奖励曲线平均回合回报最直观的收敛指标画平滑曲线看趋势Actor 输出分布的熵熵突然跌到 0说明策略僵死大概率是探索崩溃Critic 的价值估计与实际回报的差距差距长期偏大说明 Critic 没学明白Actor 的信号是坏的。这三者结合能定位大多数“为什么没效果”的问题。比如奖励一直不涨但熵还好多半是网络容量或学习率问题如果熵猛掉而且奖励也断了那可能探索退出太早。7. 从零复现时最容易踩的四个坑最后分享一下我在这四个算法上都实打实踩过的坑。每个坑都对应着一段“看曲线看到怀疑人生”的经历。第一个坑把目标网络和训练网络同步得太频繁。我最早调 DQN 时天真地设定每 100 步同步一次目标网络结果训练曲线发疯一样抖动。问题就出在同步频率太高目标始终在变化。后来改成每 1000 步同步曲线立刻温顺了。你可以把目标网络想象成班级里的考试标准考一次试就改一次标准学生永远没法稳定进步。第二个坑REINFORCE 的学习率没调好。策略梯度对学习率极其敏感我试过 0.001曲线纹丝不动调到 0.01又直接学崩。后来发现问题不在某个固定值而在于梯度本身的量级会随回合回报变化。项目里更稳的做法是把回报做个标尺缩放或者直接对梯度做裁剪让更新幅度稳定在可控区间。第三个坑连续动作的动作边界没处理好。用高斯分布采样连续动作时如果网络输出均值靠近边界采样出的动作会越界强制截断后概率密度失真导致训练结果特别差。正确做法是在输出层用 tanh 把动作压缩到 [-1, 1]再各自映射到实际物理边界。这是做连续控制时非常容易忽略却至关重要的细节。第四个坑环境复位和随机种子没管住。对比实验里不同算法各自使用随机环境跑出来的差异你说不清是算法差异还是运气差异。至少在每个环境交互前固定种子在同一起跑线上做对比。不是让你每次都固定种子训练到永远而是让调试过程有可复现性。整个实验里固定种子跑一组、不同种子跑一组两份结果结合才能下结论。从 DQN 到 PPO这条线走下来你已经掌握了强化学习落地最核心的工具箱。后面无论是看 A3C、DDPG、TD3、SAC都能在框架里找到它们的坐标——要么是价值派要么是策略派要么是两者融合。每个算法都是某个问题的补丁理解了补丁是给谁打的就算真正入门了。落笔之前再把三个指标拿出来看一眼你的模型大概率能收获一个漂亮的收敛曲线。