资讯动态

重学强化学习:从Q-learning到CQL的工程实践与认知

发布时间:2026/10/26 8:13:37 来源:尧图企业网站定制
说实话我干了好几年机器学习相关的实战项目一直觉得自己算是会强化学习的。能跑通CartPole能调DQN的参甚至面试时能把PPO的原理讲得有模有样。但直到去年接了一个偏工业场景的控制优化项目我才发现自己对强化学习的理解其实一直停在会调库的层面。离线数据堆了一屋子在线探索的成本极高安全要求又卡得死死的——这时候我连个像样的算法都选不出来。所以就有了这篇重头再学的记录。我推翻了以前那种先会调DDQN、A2C、PPO再补理论的学习路径重新从 Q-learning 的核心逻辑出发一步一步走到 CQL。这篇文章不是给学生党写的教材也不是给研究者的论文笔记而是给那些跟我一样半路出家、被线上业务毒打过、又想把强化学习真正用起来的人。我尽量把一个从业者重学过程中最关键的认知转折、数学直觉和落地经验写清楚。1. 决定重头再学之前的三个教训1.1 在线学习的能力不等于生产可用的能力我最早接触强化学习是在好几个在线课程里。印象中讲师都很喜欢放Atari游戏画面一个智能体从随机乱按到学会打砖块算法更新几百回合后分数暴涨。这种可视化效果确实震撼但也制造了一个很深的误导让我以为强化学习项目最重要的资源是算力、是时间、是算法本身的收敛速度。直到我接触一个真实项目才发现业务里每天产生的历史决策日志其实非常丰富但系统不允许你拿着线上策略随便探索。你要在一个有限的风险预算内利用历史数据训练一个比现有规则策略更好的控制器。这时候课上教的那些环境交互—收集样本—更新策略的循环在真实系统里根本跑不起来。你会发现自己缺少的恰恰是很多教程默认你已经有了的东西一个可以无限交互的仿真环境一个安全无成本的试错机制一个能快速重置、快速采样的迭代闭环。这些假设在游戏和实验室里成立在工业系统里大部分都不成立。我开始意识到强化学习的真正难点不是算法能不能收敛而是你的数据来源是否匹配算法的假设。1.2 旧知识没有过时但它只是地基网上讨论强化学习时两极分化特别严重。一边是教程天天讲Q-learning、Sarsa、策略梯度这些最基础的内容另一边是论文标题漫天飞的离线强化学习、世界模型、扩散策略。前者让人觉得太简单后者又让人觉得像天书。我自己的偏见是先跳过基础直接啃热门算法。结果就是我看不懂IQL和CQL的论文里很多推导不是因为公式复杂而是因为我连它们到底在解决什么问题都没有建立直觉。重新学习之后我最深的体会是Q-learning 从来不是一个玩具方法它是所有后续价值类算法的思想源头。DQN 是用神经网络替换Q表的工程改造Double DQN、Dueling DQN 是在修 Q-learning 遗留的系统性偏差CQL 则是在 Q-learning 的更新逻辑上注入保守性解决数据分布偏移带来的灾难性高估。如果你不懂 Q-learning 为什么容易高估就不会真正理解为什么 CQL 要把降低未知动作的Q值写进目标函数。所以重头学不是否定过去学过的东西而是把过去知其然不知其所以然的部分重新补上因果链。1.3 现代强化学习的真正分水岭数据从哪里来如果要把强化学习这几十年梳理成一条主线我认为最关键的划分不是表格 vs 神经网络也不是值函数 vs 策略梯度而是训练数据的来源方式。在线学习数据和策略完全耦合环境回传的样本即学即用。样本是新鲜的。离线学习数据来自某个固定行为策略往往是专家策略或旧策略的记录训练时完全不再和环境打交道。样本是冷冻的。这个分水岭决定了后续所有设计。在线学了半天的算法拿到离线环境里很容易直接崩掉因为策略在更新时会主动去查那些在数据里完全没出现过的状态-动作组合。Q 网络给这些幻觉动作打出虚高的分数智能体还信了于是越学越偏。搞清楚这一点之后CQL 存在的意义就不需要死记硬背了——它就是专门来纠正数据来源不一致这个问题的。2. Q-learning把动态规划变成一项查表工程2.1 从蒙特卡洛到TD等待终局还是边玩边学很多教材讲强化学习喜欢从MDP马尔可夫决策过程开始上来就是五元组、状态转移矩阵、奖励函数。我重学的时候换了条路先把价值估计的两种研究思路理清楚。最朴素的想法是蒙特卡洛一个回合从头走到尾把每一步的实际累计奖励算出来然后回头更新每一步的价值。这种思路很直观问题也明显——你必须在回合结束后才能开始学习。对于那些动辄几百上千步的回合或者根本没有明确回合终点的持续性任务蒙特卡洛的效率低得让人崩溃。Q-learning 采用的办法叫时序差分Temporal DifferenceTD。它不等终局走一步就利用当前估计的价值去校正上一步的估计。用大白话说你跑一条路走到第三十步时大概估计了一下终点还剩多远然后立刻回头告诉你第二十九步你之前的估计不太准更新一下。这就是TD的精髓——用当前的估计值去引导更新哪怕这个估计本身不完美但只要持续迭代它会越来越接近真实价值。这个过程可以简写成那个著名的更新公式Q(s, a) ← Q(s, a) α × [r γ × max_{a} Q(s, a) − Q(s, a)]中括号里那一坨就是TD误差。它衡量的是新观察到的事实拿到了即时奖励r加上下一状态能拿到的估计价值跟旧估计之间的差距。带着这个直觉你就不会再觉得这个公式只是背下来的东西了。2.2 贝尔曼方程与查表世界的状态-动作地图重学的时候我才真正把贝尔曼方程这个名词跟自己脑子里的格子世界游戏联系起来。所谓贝尔曼方程就是在回答一个循环问题一个状态的价值等于现在立刻拿到的回报 进入下一状态后未来回报的折现总和。最优价值函数必须满足一个自洽条件当前状态下你选择最优动作后获得的回报应该等于即时奖励加下一状态最优价值的折现。这就是贝尔曼最优性方程。Q-learning 的更新规则实际上就是在一个一个样本上迭代逼近这个方程的解。表格版本的 Q-learning 最容易让人理解价值地图这个概念。假设一个5×5的小网格智能体从左上角出发走到右下角有奖励踩到陷阱有惩罚。Q表就是25个状态 x N个动作构成的一张表每个格子存一个数字代表在这个位置做这个动作未来大概能拿多少回报。表格法有一个致命缺点状态一多就完蛋。真实环境里的状态维度随便就是几十上百哪怕每一个维度只取几个离散值表格的行数也会爆炸。我在重学的时候故意做了一遍完整的表格 Q-learning 小实验就是为了让自己真切感受这个维度爆炸的压力——当你把状态离散化从10×10改成100×100训练需要的回合数会从几百飙升到几万而且还没法泛化到没见过的状态。2.3 ε-greedy探索与利用这对老冤家Q-learning 还有个绕不开的问题你怎么确定你已经见过足够多的好动作如果一开始总是选当前Q值最高的动作那么一旦某个较优动作被选中过、被给了一个稍高的估计其他动作就再也没机会被探索到了。这就是经典的探索-利用困境。解决它最简单的方法叫 ε-greedy以 ε 的概率随便乱走探索以 1−ε 的概率走当前最有把握的路利用。学习初期 ε 通常设得比较高让智能体把怼到烂的环境多看几遍后期 ε 逐渐降低让智能体把路走稳。听起来简单但真做实验就会发现ε 的衰减曲线极其敏感。降太快智能体锁死在次优路径降太慢收敛效率感人。我在复现时踩过的最典型的坑是在一个奖励比较稀疏的环境里ε 从0.9开始每回合衰减0.999结果训练到一万回合探索概率才降到0.6左右导致智能体一直随机乱撞Q表迟迟不见收敛。后来改成从0.9开始时衰减0.995效果立刻就不一样了。2.4 我在Q-learning上踩过的几个低级错误这里顺便总结几个我重学过程中踩过、或者看到同事踩过的坑全是血泪更新时机写错在Sarsa和Q-learning之间搞混。Q-learning 是离策略的——更新时用的是下一状态下所有动作中最大的Q值而不是实际执行的动作。写代码时手一滑把 max 改成实际动作就成了Sarsa。奖励没有归一化Q值的量级会被直接折到折扣因子γ的累加里。奖励设成100TD误差自然大学习率稍微调不对就震荡。折扣因子γ设得太大γ越接近1未来回报的权重越大Q值收敛越慢甚至会发散。实际操作中很多任务0.9到0.99就够用了不需要追求极度的远视。没有记录每个动作被访问次数如果只更新来自高探索概率期的样本Q表会偏向那些被反复随机踩中的状态。最好在训练时监听一下状态-动作对的覆盖率用来判断数据够不够。3. 当表格变成神经网络DQN家族到底改了什么3.1 经验回放把时序相关的样本打破重排Q-learning替代方案最直接的思路是用神经网络拟合Q函数输入状态输出每个动作的Q值。但如果你直接把TD更新的样本按采集顺序喂给神经网络会立刻发现训练很不稳定。原因是相邻样本之间相关性太强——上一秒的决策和下一秒的状态强关联神经网络在这种强相关的小批量数据上来回学容易灾难性遗忘刚学的知识被下一批相关样本冲掉。DQN引入了经验回放Experience Replay来解决这个问题把交互产生的样本(s, a, r, s)存进一个大缓冲区训练时随机抽一批出来更新。这样做有两个好处打乱了样本之间的时间相关性让梯度更新更稳实现了样本的重复利用对数据效率的提升非常明显。这个设计放到今天看已经是常识但当时对让智能体回忆过去的经验来训练当前策略这个思路确实是革命性的。我重学之后才意识到经验回放不仅是工程技巧它还隐含了一个假设学习器可以从过去的行为中学不必非得靠当前策略实时产生的新数据。这个概念后来被离线强化学习放大到了极致——既然能从过去的缓冲区里学那为什么不能先从别人的历史数据里学呢3.2 目标网络和Double DQN两个都是治高估DQN里另一个核心设计是目标网络Target Network。Q-learning更新时用到了 Q(s, a) 的估计这个估计本身还在动态更新中。如果你用同一份参数同时计算当前估计和目标那么每一轮更新都会让目标跟着移动像一条追着自己尾巴跑的狗训练很容易发散。解决方法是把目标网络冻结住每隔一段时间才让它同步一次主网络的参数。这样在一段训练周期内目标是相对稳定的主网络能够朝一个不动点逼近。但目标网络只解决了目标不稳定没解决目标过高。Q-learning 的更新里有一个 max 操作它永远只看下一状态所有动作里Q值最高的那个。高估是无偏的但 max 取的是估计噪声中的最大值所以 Q-learning 天然倾向系统性高估真实价值。尤其在函数逼近场景下这种高估被放大得非常严重。于是有了 Double DQN把选动作和估价值两个步骤解耦——用当前网络选出下一个状态的最优动作再用目标网络去计算这个动作的价值。直接效果是显著抑制了高估训练曲线更稳最终性能也更好。我自己做实验的感受是加了Double之后Q值的绝对值明显下降而且更接近真实回报量级。3.3 连续性状态让我第一次感受到泛化的威力从表格到神经网络另一个被忽略的跨跃是泛化。表格法对每个状态独立记录状态之间没有共享神经网络则天然会把相近的状态映射到相近的价值估计。这意味着智能体在某个状态上学习到的信息会自动迁移到类似状态上。我用连续状态环境比如控制一个小车平衡杆做对比实验时感受特别明显神经网络版Q函数在训练初期就能对从未精确出现过的状态给出合理的价值估计这是表格法做不到的。代价就是你需要精心设计特征或网络结构否则泛化出来的价值估计会泛过头在该区分的地方糊成一团。早期的DQN用一个全连接网络输入状态向量输出离散动作的Q值。后来出现了Dueling DQN——把价值拆成状态价值动作优势进一步提升了学习的稳定性和表现。这里不展开Dueling细节但你要知道DQN家族每一步演进本质上都是在修Q-learning在不同层面上的毛病回放修相关性、目标网络修不稳定性、Double修高估、Dueling修价值分解。3.4 从DQN到现代算法的跳跃连续动作怎么办DQN的另一个限制是它输出的是离散动作的Q值。真实控制任务里动作往往是连续的比如油门踩多少、方向盘打几度。面对连续动作你不可能枚举所有动作去求 max。这促成了两条技术路线一条是策略梯度类的Actor-Critic直接学习策略分布另一条是像DDPG、TD3、SAC这类深度确定性策略用策略网络输出连续动作值再拿这个值来近似 max Q。我在重学的路线里稍微跳过了PPO和SAC的细节因为这篇文章的重点是Q-learning家族的价值路线。但你必须知道CQL在设计时也考虑了连续动作的情况——它不能像表格法那样枚举所有动作因此需要额外策略来采样未知动作。这个细节我在讲CQL时会再提到。4. 离线强化学习为什么数据越多越容易崩4.1 分布偏移训练时的数据视野和评估时的数据需求不一样现在终于到了整篇文章最关键的分水岭。假设你现在手里有一批历史数据里面是专家或旧策略与环境交互的日志。你想用这批数据离线训练一个更好的策略训练过程中不再和环境交互。初听之下这跟监督学习非常像——给定输入输出样本拟合一个函数。但强化学习有一个监督学习没有的问题策略更新之后它想要尝试的动作和新数据覆盖的动作分布不一样了。学到的策略想要选某个动作来最大化奖励但这个动作可能根本没有出现在历史数据里。Q网络对没见过的动作给出的价值估计完全是由网络外推出来的这种外推极不可靠经常给出虚高的分数。策略一看哟这个动作值钱赶紧选它于是下一轮训练里网络就更确信这个动作好——即使它从来没见过真实输出会是什么。这就是分布偏移Distribution Shift带来的自激循环。4.2 高估偏差的恶性爆发我之前提到Q-learning有高估偏差在线学习里这个偏差可以靠实时交互来纠偏你选了一个被高估的动作真实环境立刻给你反馈网络会慢慢意识到哦其实没那么好。但在离线场景里没有这种纠偏机制。更恐怖的是分布偏移和高估偏差会共振。分布偏移让策略集中访问OODOut-of-Distribution分布外动作高估偏差让这些OOD动作的Q值被抬得更高两者互相加强最终Q值能飙到无法无天的量级。我记得复现实验时用普通DQN在离线数据上训练打出来的Q值大多数在几千以上而我事先算过真实期望回报最多不超过几十。这种数字一看就知道模型已经疯了。4.3 为什么说直接拿历史数据跑DQN是个坑很多人第一次接触离线强化学习会觉得这事简单把历史数据存好直接跑DQN不就行了吗我在没重学之前也是这么想的。但实际跑下来你就知道DQN在离线场景下挂得相当快因为它的训练流程里根本没有针对分布偏移设防的机制经验回放只负责从缓冲区采样它不管这个样本是不是当前策略会遇到的。max运算主动追逐那些高估的OOD动作。没有任何约束让策略留在数据覆盖范围内。所以你会看到一个奇特的现象离线训练中智能体的训练loss持续下降Q值一路走高看起来学得不错但你一部署到线上立刻拉胯。因为它在训练时疯狂相信那些幻觉动作根本没学到靠谱的东西。4.4 离线数据里还藏着一个因果问题在学习离线强化学习的过程中我还被引向了一个相关但更深刻的话题因果强化学习Causal Reinforcement LearningCRL。CRL的核心想法是把因果推断工具嵌入强化学习流程帮助智能体区分真正的因果关系和虚假的相关性。这在离线场景尤其重要。举个例子历史数据里每当红灯亮起时前车往往减速。一个只看相关性的策略可能会学习红灯→踩刹车的映射。但如果你把天气、时间、路况等混杂因素加进来可能会发现红灯亮和刹车之间的相关关系被几个隐藏变量共同驱动。在学习环境模型基于模型的强化学习时因果结构可以让模型不再把特征之间的共线性误当成因果链条从而提升泛化能力。我最初以为CRL是另一个领域的学术概念跟工程实现关系不大。但在离线数据质量参差不齐的现实中因果工具确实可以回答一个非常实际的问题这批样本里哪些状态特征真正影响环境转移哪些只是伴随因素把这个问清楚后续模型才不至于把噪声当信号。5. CQL把保守写进目标函数5.1 CQL的直觉压低未知托住已知CQLConservative Q-Learning是2020年由Kumar等人提出来的。它解决的就是上面那一大堆问题核心思路用一句话说让Q函数对没见过的动作给出保守偏低的估计同时对数据里见过的动作保持不低于真实回报的估计。你可能会问只压低未知动作的Q那策略不就会避开未知动作固守在已知动作里了吗没错这正是离线强化学习想要的——在没有新数据的情况下策略应该留在数据分布覆盖的区域内求稳而不是求新。但要实现这个效果光把Q值压低是不够的。假如你把所有Q值都无脑压低那智能体学出来的价值估计全是负的完全没法指导策略。所以CQL还需要一个托底机制确保数据分布内那些真实被观察到的动作其Q值不会被压得太狠至少不能低于它们实际获得的回报。这两个力量一拉一扯就让Q函数形成了已知区域高、未知区域低的形状。5.2 损失函数逐项拆解它到底在优化什么CQL在标准贝尔曼更新之上加了一个正则项。简化之后的损失长这样L(θ) α × [ E_{s~D, a~μ(a|s)} Q_θ(s, a) − E_{s~D, a~π_β(a|s)} Q_θ(s, a) ] 标准贝尔曼误差项拆开看关键的两个期望第一个期望 E_{s~D, a~μ(a|s)}对某些动作分布μ下的Q值求平均这个μ通常是当前策略π_θ或是均匀分布兜住了所有可能的动作。最小化这一项就是压低这些未知动作的Q值。第二个期望 E_{s~D, a~π_β(a|s)}对数据分布里的实际动作的Q值求平均最大化这一项就是把已知动作的Q值抬高。两股力合在一起Q函数就会在一个压低未知、托住已知的平衡点上收敛。在具体实现里离散动作环境下很多实现会把第一项直接写成 logsumexplog(Σ_a exp(Q(s, a)))。这个操作把最大化已知动作Q和压低整体Q值合并进了一个光滑表达式中同时数值上更稳定。连续动作场景下因为动作空间无法枚举常见的做法是用当前策略采样N个动作来近似那个期望。你还会注意到这个正则项有一个权重系数α。α越大保守性越强策略越倾向于只看数据分布内的动作α越小CQL就越接近普通DQN。这个超参数是我们在实际应用时最需要调的阀门。5.3 关键超参数和调参经验我在复现CQL的过程中有几点实际经验想重点记录α不要一上来就拉满。我见过很多实验把α设成1甚至5结果Q值被压成一片负数策略退化成随机。实际上对小规模任务α从0.1到0.5起步比较合理。CQL论文里做了大量消融α的合理范围跟任务的数据量、动作维度关系很大。批量大小和网络容量比想象的更重要。Q函数形状本来就复杂离线数据又固定模型容量太小保守性会变成一棍子打死一片容量太大又容易在有限样本上过拟合。我自己遇到的情况是把隐藏层从256加到512反而让Q值在高估区域出现更多毛刺。CQL对数据质量很敏感。它可以在低质量数据上做到不崩但并不意味着它能凭空学出好策略。数据太差时保守的后果是策略也差只是差得不离谱。连续控制任务要检查动作采样数。我在复现连续任务时由于对动作空间的采样数太少第一项的logsumexp近似严重偏低导致保守效果没生效Q值照样高估。后来把采样数从16加到64行为才恢复正常。5.4 CQL的边界它能做什么不能做什么CQL不是万能的。它解决的是价值函数在离线数据上的外推灾难但有一个天然的代价因为策略被约束在数据覆盖区域内CQL很难发现数据分布之外的明显更优策略。换句话说CQL擅长稳定地模仿并小幅超越行为策略但不太擅长完全跳出数据发现新大陆。只看性能上限时CQL在高质量数据上的表现往往不如激进的在线方法它真正的价值在于鲁棒性。当行为策略很复杂、数据质量参差不齐、甚至行为本身就不太好时CQL不容易崩。这在工业落地中比偶尔性能爆炸但偶尔彻底崩盘的算法更有吸引力。另外一个边界是序列决策的视野。CQL在很多基准任务上表现强劲但面对高维图像输入、长期稀疏奖励任务它仍然需要和表征学习、模型学习配合。用一句话总结我对CQL的评价它是一个强力安全带不是自动驾驶。6. 从CQL继续走离线到在线、IQL和现实项目6.1 IQL另一种不见未知动作的设计思路学完CQL之后我顺藤摸瓜看了IQLImplicit Q-Learning。它的思路跟CQL完全不同但解决同一个问题。IQL不主动压低未知动作的Q值而是干脆让更新公式里永远不出现未知动作的价值。Q-learning的更新里有个max_{a}必须评估下一状态的最优动作。IQL把这个max换成了分位数回归式的expectile操作——它只基于数据集里已经见过的动作来估计下一状态的价值上限而不去枚举未知动作。这样整个训练过程中Q函数的后继状态价值完全由数据支撑没有给OOD动作任何出场机会。两种思路放在一起对我会非常有启发CQL是压低你的分IQL是不给你参加考试的机会。在特定数据分布下IQL往往训练更稳定因为它压根不跟未知动作较劲。这提醒我离线强化学习不是只有一个答案选择哪种方法取决于你对数据覆盖率的判断。6.2 离线预训练 在线微调工业落地最常用的一条路线重学完这一圈我最大的收获之一是把离线学习和在线学习看成了互补关系而不是对立关系。线下用历史数据预训练一个保守策略保证上线初期的基本盘上线后在安全约束内适度放开探索继续用在线反馈微调策略。这就是离线预训练 在线微调Offline-to-Online的标准路线。CQL的保守性在预训练阶段能防止策略偏移到未知区域进入在线阶段后可以把α逐渐调小让策略从保守慢慢过渡到激进探索。很多论文实验和我的实际项目都显示这种方法比纯离线或纯在线都好它规避了在线冷启动的高风险又突破了离线策略的性能上限。这个先保守再放开的思路跟一个新手司机的成长路径非常像先在驾校场内按固定场地练熟操作离线再到真实开放路段渐进扩大驾驶范围在线而不是一上来就上高速。6.3 多AGV路径规划和Gazebo仿真里的落地把视野拉回到我手头的工作场景。多AGV自动导引车路径规划是强化学习一个很典型的落地领域多台AGV在同一个仓库环境里调度状态空间包含每台车的位置、速度、目标点、路径冲突情况动作空间是下一步的方向和速度。在线跑的时候撞车或堵塞的代价非常高但历史调度日志里有大量在特定拥堵局面下如何选择路径的记录。这个场景几乎是为离线强化学习量身定做的数据丰富、在线试错成本高、状态动作空间复杂。在仿真侧很多人用Gazebo搭环境配合ROS做状态感知和动作执行。Gazebo的好处是物理引擎相对真实可以模拟AGV的动力学和传感器噪声但代价是仿真速度慢吞吐量低。所以在实际项目里常用策略是先在轻量级二维模拟器里做大规模训练再用Gazebo做小规模验证。CQL在这种流程里的角色是在二维模拟器生成的大量仿真数据上训练一个不会瞎想的Q函数再迁移到Gazebo里做少量验证和微调。6.4 如果再走一遍学习路径我会怎么安排最后分享一个实际的建议。我觉得这条重头再学的路跟大多数人第一次学强化学习的路径应该反过来先搞清楚数据从哪来这个核心问题在线、离线、还是离线转在线这会直接决定你选哪类算法。再吃透Q-learning的TD更新逻辑和高估偏差。这一步不能跳因为它是后续所有方法共同的地基。接着用DQN家族理解函数逼近带来的新问题目标网络、回放、Double都是为了解决不同层面的毛病。然后进入离线强化学习用分布偏移这个视角去理解CQL和IQL的设计动机。最后再回到你的业务场景判断你的数据覆盖程度、风险预算、探索空间来决定要不要用CQL、用多大α、要不要做离线转在线微调。我个人的体会是这套路径比一开始就追着某个SOTA算法跑要扎实得多。因为当你知道每一个设计环节对应什么问题调参就不再是靠运气的玄学而变成有针对性的诊断。重学强化学习这件事最后给我的收获不只是一堆新算法的知识而是一种更踏实的判断力拿到一个业务问题能快速分辨出这个问题适合用什么框架以及在数据不够理想时怎样选择保守方案来保证下限。这些经验我不太确定其他人是否也需要但如果你也正卡在公式都懂但项目做不出来的尴尬阶段希望这篇记录能帮你把链条上的最后一环补上。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑