算法解析:从Q-learning到DQN的强化学习核心)
1. 为什么说时序差分是强化学习的转折点强化学习算法满天飞但如果你把DQN、PPO、SAC这些名字逐个拆开最终几乎都会落到同一个数学内核上时序差分Temporal Difference简称 TD。在《强化学习的数学原理》第7章里作者没有一上来就给你堆网络结构而是把TD的思想从表格型问题到函数逼近一层层剥开。我读完这章最大的感受是——理解TD才算真正跨进了强化学习的大门。时序差分方法解决的是一个非常朴素的问题在不知道环境完整模型的情况下怎么利用当前已经拿到的一点奖励立刻修正对状态的判断。传统动态规划需要环境的转移概率蒙特卡洛方法必须等一幕结束才能更新。TD方法则像是一个急性子走一步看一眼马上根据“当前奖励 下一状态的估计值”来更新当前状态的价值。这种“先用猜的再慢慢修正”的思路正好是深度强化学习里几乎所有主流算法的底层逻辑。这章适合谁如果你是刚听完动态规划和蒙特卡洛、正在被各种TD变体绕晕的初学者适合读如果你已经在跑DQN、PPO但说不清target到底怎么来的也适合回头读。这篇笔记会把我自己消化过的推导、踩过的坑、以及在实验里调参时和TD误差打交道的经历全部写出来尽量做到能直接解决你的疑惑而不是把教科书复述一遍。2. 从数学原理拆解TD核心回报、TD目标与TD误差2.1 从蒙特卡洛到TD方差和偏差的权衡强化学习的经典三兄弟是动态规划DP、蒙特卡洛MC和时序差分TD。DP要求完整的MDP模型MC要求等完整轨迹结束后才能用真实回报更新而TD只依赖一步的转移结果就能形成更新信号。这意味着TD具有“完全在线”的特性每一步都能更新非常适合持续交互的控制系统。一个关键权衡是方差和偏差。MC方法用整条轨迹的真实累积奖励作为目标是无偏估计但因为路径很长每一步的随机性叠加后方差巨大。TD方法只用一步真实奖励加上对下一状态的估计方差显著降低但引入了估计值带来的偏差。用大白话说MC像“等考试全部考完再根据分数复习”TD像“每做一道题就立刻根据参考答案修正思路”。TD之所以在工程上更受欢迎是因为它能让学习过程稳定且快速神经网络训练时也更容易收敛。值得注意的是TD的这种偏差会随着学习过程逐渐减小因为下一状态的估计值越来越准。第7章里给出了一个很好的直觉TD目标里的“下一状态价值”本身是一个有偏估计但你不断用它来更新自己最终两者会一起收敛到真实价值。2.2 TD(0)的更新公式与100%原生在线学习TD(0)是最简单的时序差分方法它的价值更新公式长这样V(S) ← V(S) α * [R γ * V(S) - V(S)]其中S是当前状态S是下一状态R是执行动作后获得的即时奖励γ是折扣因子α是步长参数。方括号里那一整项就是TD误差TD error它衡量了“用现有估计推算出的目标”和“当前估计”之间的差距。为什么要这么写我看过很多资料只给公式不讲来由。其实它来自最小化“预测误差”的直觉。我们希望V(S)尽量接近R γ * V(S)因为后者是对长期回报更有依据的估计。于是直接用误差的一部分来修正V(S)就像PID调节一样朝着目标迈一小步。与MC对比MC更新需要先计算出完整回报G_tV(S) ← V(S) α * [G_t - V(S)]G_t要到回合结束才能算出来。TD(0)则把G_t替换成单步的R γ * V(S)这就让学习和交互完全同步进行。用工程语言说TD是“流式处理”MC是“批处理”。2.3 TD误差到底在更新什么一个直观推导TD误差的定义为δ_t R_{t1} γ * V(S_{t1}) - V(S_t)如果δ_t为正说明实际结果比当前估计好就把V(S_t)往上调如果为负就往下调。这里非常重要的一点是TD误差不是简单的“预测值与真实值之差”而是“一步真实奖励 对未来的预测”与“当前预测”之差。所以它有前瞻性包含了下一步的信息。举个简单例子。网格世界里某个格子S的价值估计是0.5走一步后到达终点获得奖励1下一状态价值为0。此时TD目标 1 0.9 * 0 1TD误差 1 - 0.5 0.5于是V(S)上调。如果下一状态仍是未知状态估计值为0.8那么TD目标 1 0.9 * 0.8 1.72误差更大更新更猛。可以发现TD误差中隐含了“对未来的期望”因此它能把奖励信息反向传播到更早的状态这也是时序差分能做中长期信用分配的原因。在深度强化学习中TD误差还常被用作衡量“经验是否值得学习”的指标。比如优先经验回放Prioritized Experience Replay就是按TD误差的绝对值来给样本排序误差大的样本优先学习。这个设计背后就是利用了TD误差对“意外程度”的刻画。3. 表格型TD算法全家桶Sarsa、Q-learning与期望Sarsa3.1 Sarsa同策略的保守派Sarsa是TD思想从状态价值推广到动作价值后的第一个算法。名字很形象当前状态S执行动作A得到奖励R进入下一状态S再根据当前策略选择下一个动作A所以叫Sarsa。更新公式是Q(S, A) ← Q(S, A) α * [R γ * Q(S, A) - Q(S, A)]注意这里的A是实际要执行的动作也就是和当前策略绑定在一起的。这意味着Sarsa学的是“当前策略下的行为价值”是典型的同策略on-policy算法。如果当前策略是ε-贪心那么A的选取也带探索所以Sarsa学到的Q值包含了探索带来的风险惩罚。这带来的后果是Sarsa比较保守。比如在悬崖行走问题中Sarsa会学出一条远离悬崖边的路径因为它知道探索时可能不小心踩到悬崖Q值会降低。用我自己的话说Sarsa是一个“知道队友偶尔手抖、所以不敢靠太近”的人。3.2 Q-learning异策略的激进派Q-learning的更新公式只改了一个地方把Q(S, A)换成对下一状态所有动作Q值的最大者Q(S, A) ← Q(S, A) α * [R γ * max_{a} Q(S, a) - Q(S, A)]这样就变成了异策略off-policy算法。它不关心你在下一状态到底选哪个动作它假设你总是选择Q值最高的动作。哪怕当前探索策略是ε-贪心甚至完全随机Q-learning都会朝着最优动作价值函数收敛前提是每个状态动作对都被无限次访问。Q-learning激进、乐观敢于走危险但最优的路径。在悬崖行走中Q-learning学到的路径往往贴着悬崖边因为它在更新时“假装”自己不会失误。实际训练时如果环境随机性大这种乐观可能带来风险但它也保证了收敛到最优策略的理论特性。3.3 期望Sarsa与两者之间的过渡期望SarsaExpected Sarsa是Sarsa的一种改造。它不再用实际选择的下一个动作A而是对下一状态所有动作的Q值按策略概率求期望Q(S, A) ← Q(S, A) α * [R γ * Σ_a π(a|S) * Q(S, a) - Q(S, A)]如果当前策略是ε-贪心那么期望Sarsa比Sarsa的方差更低因为单一采样动作带来的波动被平均掉了。期望Sarsa可以看作Sarsa和Q-learning之间的一个连续谱当探索率接近0时期望Sarsa退化为Sarsa当策略对最优动作的概率为1时它就近似Q-learning。我在实验里经常使用期望Sarsa因为它比Sarsa稳定又比Q-learning温和。尤其在奖励稀疏的环境中期望Sarsa给出的目标更平滑早期训练不容易被偶然的高奖励带偏。3.4 示例随机网格世界中的对比实验为了帮助理解我自己在一个5×5随机网格世界里跑了三种算法。环境设定是起点在左上角终点在右下角其余格子有10%概率向左滑步奖励只有到达终点给1。分别用Sarsa、Q-learning和期望Sarsa训练500个回合记录累计奖励。结果大致如下算法收敛回合最终平均回合奖励路径特点Sarsa约1200.78绕远路稳定Q-learning约900.86直线近路偶尔踩坑Expected Sarsa约1000.84折中路线方差最小这个表不是精确复现但反映的规律很典型。Q-learning收敛快但因为乐观估计策略容易在随机环境中震荡Sarsa更稳上限略低期望Sarsa综合体验最好。如果你在做机器人控制这类安全敏感任务我会优先推荐Sarsa或期望Sarsa如果你在玩游戏或仿真环境Q-learning则通常更省时间。4. 实战中的收敛性、偏差与方差调参心得4.1 步长α的踩坑记录TD更新中步长α是最容易出问题的超参数。α太大价值估计会在最优值附近剧烈震荡α太小收敛慢而且一旦环境是非平稳的可能永远追不上变化。我踩过最深的一个坑是在同一个环境里用α0.5训练Q-learning结果曲线像心跳改成α0.1后又像一个信号不好的人在划水。我的经验是表格型TD方法通常用递减的α比如α_t 1 / (1 t)保证收敛但前期能快速修正。深度强化学习里虽然用Adam优化器但本质上学习率替代了α的角色同样需要衰减策略通常使用指数衰减或余弦退火。另一个容易被忽视的点是TD更新不需要遍历完整数据集每个样本只用一次所以α不需要像监督学习那样设很多个epoch。很多初学强化学习的人喜欢把α设成0.01甚至0.001对于表格型任务来说这太保守了我建议从0.1开始观察TD误差的下降曲线再调整。4.2 探索率ε如何衰减ε-贪心策略中的ε控制探索程度。如果衰减太快算法容易陷入局部最优太慢则浪费计算资源。我常用的策略是初始ε1.0每个回合乘0.995最低到0.01。但要注意这和任务长度有关。一个300步的回合如果衰减到0.01需要约900回合对于简单任务足够了复杂任务可能需要更慢。更讲究的做法是把ε和Q值的收敛状态联动。比如当某个状态动作对的TD误差连续N次小于阈值时缩减该状态的探索率。这个思路在导航任务中很有效因为不同区域的稀疏性差异大。不过表格型任务中联动的开销小深度强化学习里可以在episode层面做也可以使用其他探索机制如NoisyNet、熵奖励等。4.3 为什么我的TD训练不收敛——五个高频原因我遇到过很多次训练发散总结下来有几种典型场景奖励没有归一化。TD目标里包含即时奖励如果奖励数值跨度过大比如从-1000到1000TD误差的绝对值也会很大更新步长很难权衡。我一般会对奖励做clip把数值限制在[-1,1]。折扣因子γ设置不合理。γ太接近1会让长期回报累积过快价值估计变得不稳定γ太小则只关注眼前利益策略偏短视。推荐从0.9到0.99之间调整。步长α与环境动力学不匹配。环境动作随机性大时α要适当减小环境确定性高时可以加大。探索率衰减和步长衰减搭配错误。两者都不能同时衰减太快否则算法会过早锁定一个次优策略。状态表示重复或信息不足。如果不同状态映射到同一个特征向量TD误差会产生冲突导致价值估计上下震荡。这里放一个常见问题速查表方便你对照。症状可能原因排查方向训练初期损失爆炸奖励幅值太大做奖励clip或归一化损失持续震荡不降α过大/ε衰减过快降低α放慢ε最终策略很差γ过低/探索不足增大γ提高探索率训练后期过拟合数据重复采样不足增加随机性打乱样本顺序离线算法效果差行为策略与目标策略差异大使用重要性采样或换期望Sarsa5. 从表格走向深度TD与DQN系列的血缘关系5.1 DQN就是高维版Q-learning深度Q网络DQN的核心更新公式一眼就能认出是Q-learning的亲戚θ ← θ α * (r γ * max_{a} Q(s, a; θ^-) - Q(s, a; θ)) * ∇Q(s, a; θ)区别在于状态空间从离散网格变成了图像或传感器特征Q值从表格变成了神经网络输出。这里的TD误差仍然存在只是网络参数θ取代了表格中的数值。理解这个升级过程很重要如果你能动手写过一遍表格型Q-learning再去看DQN的代码会发现很多东西都是对应的。DQN中的“目标网络”参数θ^-是当前网络θ的老版本每隔若干步同步一次。这样做是为了让TD目标里的max运算不至于每步都因为网络更新而移动靶子。想象你在追一个还在飘的目标永远追不上。目标网络把这个目标固定一段时间训练就稳定很多。5.2 目标网络、经验回放与TD误差的纠缠DQN另一个关键组件是经验回放Experience Replay。它把交互产生的转移样本(s, a, r, s)存进一个缓冲区训练时随机采样一个batch来更新。这样做的本质是为了打破相邻样本之间的相关性避免TD误差持续集中在局部状态。更细致的实现里TD误差会反向影响采样概率。优先经验回放的思路是样本的|TD误差|越大说明当前网络对这个样本的预测越不准确它就越值得被拿出来多学几遍。不过要小心如果完全按TD误差概率采样会导致偏差变大所以通常会配合重要性采样权重修正。我经常看到新手把优先经验回放直接加进去结果训练更不稳原因正是没有处理修正项。5.3 深度强化学习中的TD变种Double DQN、Dueling DQNDouble DQN解决了Q-learning中max运算带来的过估计问题。它用当前网络选动作用目标网络评估该动作的价值从而把“选择”和“评估”拆开降低过估计。这种改动只动了一行代码但效果非常明显。Dueling DQN则把Q值拆成状态价值V和优势函数A的和。从数学角度看这是把TD误差里“当前状态总体好”和“这个动作比平均好多少”两个因素解耦。直觉上在有很多动作但奖励稀疏的环境中这种表示更容易学习。这些变种没有改变TD的本质只是针对TD误差的方差和偏差做了更精细的整形。你只要真正理解了第7章的TD思想再看这些深度变种会觉得非常顺畅。6. TD方法在机器人/控制任务中的应用与实验可视化6.1 机械臂强化学习实战中的摩擦与TD的应对机械臂强化学习是热搜里反复出现的场景。真实环境中机械臂关节存在摩擦、惯性延迟、执行器噪声环境不满足马尔可夫性质的严格假设。TD方法在这里会遇到一个挑战单步转移的“下一状态”因为摩擦和延迟并不仅仅由当前动作决定还和历史速度、位置有关。解决思路之一是把最近几帧的状态拼接成一帧让状态表示包含速度等信息恢复马尔可夫性。另一种做法是使用TD(λ)或n步TD让更新目标覆盖更多真实交互信息减少对单步模型的依赖。我在一个六轴机械臂搬运用例中试过TD(3)和TD(λ0.3)效果都比TD(0)好训练更稳定。另外机械臂环境中的奖励函数通常稀疏只有到达目标才给奖励。这时TD的误差传播很容易在长程任务中衰减需要引入势能函数塑形奖励。这里要警惕如果塑形奖励设计不当会改变最优策略。理想做法是保证塑形函数是“势能”形式这样策略不变。6.2 用origin绘制强化学习置信区间曲线强化学习实验的随机性很大通常需要跑多个随机种子用均值加减标准差画置信区间。很多人会用matplotlib、seaborn但如果你习惯用origin也可以画得很专业。步骤是把若干次实验的累计奖励分别放在工作表的列里选中数据先绘制折线图再在“Plot Details”的“Data”面板里添加误差柱或误差带。更直观的方法是计算每一时刻的均值和标准差再新建列用“Fill Area”填充上下界。我之前一次实验里跑了10个种子每个种子训练500回合origin画出来的置信区间曲线比matplotlib默认样式更清晰。注意强化学习曲线通常横轴是回合数或步数纵轴是累计奖励如果比较不同算法最好把每个算法的均值曲线和置信区间画在同一张图里并且使用不同的配色和透明度否则重叠区域会看不清。顺便提一个容易被忽略的点计算置信区间前要对齐seed。如果每个seed的回合数不一致需要先按固定间隔重采样比如每10回合记录一次奖励再进行统计。否则曲线会在末端突然稀疏画出来的尾巴会很难看。7. 常见问题速查表与个人避坑总结7.1 常见问题与排查把刚学TD时最容易犯的错误总结成一张速查表比看十遍书都管用。问题原因解决办法我把MC换成TD后训练反而更慢单步TD方差低但偏差大如果步长未调整可能震荡调大α增加尝试次数Q-learning和Sarsa结果差异巨大问题可能存在“状态-动作”死锁路径先确认环境随机性再决定用同策略还是异策略目标Q值偶尔突然变大然后崩了网络输出Q值过高TD误差爆炸使用Double DQN奖励clip引入n步TD后效果变差n步目标中引入了更多噪声减小n值或增加折扣折扣经验回放里TD误差高的样本反复学习仍不下降样本来自非平稳环境状态表示不合理增加网络容量检查状态预处理如果你发现训练曲线在某个时刻突然跳出一个尖峰我建议立刻打印TD误差的均值和最大值多半是某个异常奖励或状态转移误差被放大了。这个时候不要盲目降低学习率先定位是哪个样本造成的。7.2 我的个人经验总结写过很多次TD更新踩过的坑比我记住的数学公式还多。根据我的经验刚开始学的时候建议先用真实环境或简易网格世界手推一遍Q-learning的更新不要一上来就套深度网络。手推会让你真正感受“更新信号来自当前奖励下一步预测”这句话的含义。另外我建议在每个TD算法实验里都记录TD误差的变化。TD误差的绝对值下降趋势比累计奖励更能反映学习进度。累计奖励容易受到探索噪声影响TD误差则更直接地指示了价值估计是否在逼近目标。最后分享一个小技巧如果你在写深度强化学习代码千万别把目标网络同步步长设得太小也不要设得太大。我常用的范围是每500到2000步同步一次。同步频率越高训练越稳定但计算开销也大。如果你发现训练初期TD误差快速下降后突然停滞不妨先降低目标网络同步频率给学习留出更多喘息空间。