最近在梳理分层强化学习的脉络时,又翻到了这篇2017年的经典工作《The Option-Critic Architecture》。这个工作刚出来那会儿,正好是我从传统强化学习算法转向层次化决策研究的阶段,当时读这篇论文的震撼感现在还记得:它第一次把“如何自动发现好的时间抽象”这个老大难问题,变成了一个可以用端到端梯度训练来解决的问题。换句话说,在此之前,想让智能体学会“先打开门,再穿过门”这种带有明显子目标结构的技能,要么得靠人手把手设计子目标,要么得靠额外的探索奖励来引导。而Option-Critic的思路是:我能不能不额外定义任何奖励塑形,让智能体自己从交互数据里长出一组可复用的“技能”,并且能自发地决定什么时候调用、什么时候退出这些技能?
这篇文章不是论文的中文翻译,而是我结合论文核心思想、数学推导和实际复现踩坑记录的深度拆解。我会重点讲清楚它要解决什么问题、架构内部是怎么运作的、梯度公式为什么长这样、以及在真实工程项目里你需要注意哪些坑。无论你是刚开始接触分层强化学习的研究生,还是已经在跑PPO、SAC但想给自己的智能体加一点“结构化抽象”能力的工程师,这篇文章应该都能给你一些可落地的参考。
1. 问题背景:为什么需要时间抽象,以及Option-Critic在解决什么
先从最简单的场景切入。假设你想让一个机器人在迷宫里找到出口,传统强化学习会怎么做?把每个时间步当作一个独立的决策时刻,智能体每一步都在“选一个动作”,然后等待环境反馈一个奖励和下一个状态。这种做法在简单任务上没问题,但一旦任务变长、状态空间变大,就很容易暴露两个问题。
第一个问题是探索效率极低。每一步都做细粒度决策,意味着智能体必须通过海量的试错才能把一条长序列的动作串起来。想一下“先打开门、穿过走廊、再按下按钮”这个三层嵌套的流程,如果每一步都是独立的动作选择,智能体需要经历多少次“做了前两步但第三步错了”的失败才能收敛?很多时候,它连“打开门”这个子目标都学不会,因为门打开之后它根本不知道该干嘛,稀疏的最终奖励根本传不回来。
第二个问题是泛化能力差。如果每个任务都要从头开始学,换一个环境布局就全废了,那这套系统就太不实用了。我们人类解决新任务时很少从零开始规划每个关节怎么动,而是会复用“开门”“绕过障碍”“按下开关”这种已经掌握的运动模式。这种可以被复用的、带时间跨度的行为模式,在分层强化学习里就叫做option,中文一般翻译成“选项”或者“技能”。
传统上,options是预先定义的:你告诉智能体“当你在房间A时,执行‘移动到门边’这个选项;当你到达门边时,退出该选项,切换到‘开门’选项”。这种做法能大幅提升学习效率,但问题也很明显——谁来讲这些故事?如果你面对的是一个未知的环境,你根本不知道这个环境里有哪些天然的子目标结构,手工设计options几乎是不可能的。
于是问题就变成了:能不能让options从数据里自动涌现出来?Option-Critic这篇论文给出的答案是一个优雅的架构——把option的内部策略、终止函数(决定何时结束当前option)以及option之间的高层选择策略都参数化,然后通过策略梯度方法端到端地训练全部参数。这也就是说,不需要任何额外的奖励塑形或子目标标注,智能体就能自己学会“什么时候该开始一个option、在这个option内部该做什么、什么时候该结束它、结束之后下一个option该选什么”。
我觉得理解这个工作有个特别关键的视角:它其实是在强化学习里做“结构发现”,而不是“结构设计”。以前我们设计一个分层的智能体,是先有结构后有学习;Option-Critic试图用梯度优化直接让结构自己长出来。这种思路上的转变,影响了后面很多工作,也解释了为什么这篇论文到现在还有这么大的引用量。
2. 核心架构拆解:SMDP、Option定义与三个关键组件
要真正看懂Option-Critic,得先把它建立在半马尔可夫决策过程(Semi-Markov Decision Process,SMDP)框架上。传统MDP假设每一步都消耗一个固定的时间单位,动作的持续时间是单一的离散步。但SMDP允许一个决策行为跨越多个时间步,这就对应了“一个option从启动到终止横跨若干步”的直觉。
一个option的正式定义包含三部分:内部策略π(a|s)、终止函数β(s)、以及初始条件集合I。内部策略决定“在这个option执行期间,每个状态选什么动作”;终止函数决定“在当前状态,这个option是否应该结束”;初始集合则说明“在哪些状态下这个option可以被激活”。
Option-Critic的架构本质上就是把这三样东西加上option之间的高层策略全部用神经网络参数化。具体来说,它维护两组参数:一组是高层策略的参数,针对每个状态输出各个option的选择概率;另一组是每个option内部的策略和终止函数参数。
这里有个让我印象深刻的细节:论文中的终止函数输出是一个概率值,而不是一个二值判定。智能体在每个状态都会以β(s)的概率退出当前option,以1-β(s)的概率继续执行。这种软性的终止机制在理论推导上很便利(后面算梯度时能保证平滑),在实际训练中也比硬切换稳定得多。我做实验时就发现,如果用argmax式的硬终止,训练初期很容易陷入极端情况——要么一直不退出、要么一进去就退出,两种都学不到有意义的时间抽象。
三个关键组件的参数化关系,我画个简单的表格来整理:
| 组件 | 作用 | 常见参数化方式 |
|---|---|---|
| 高层策略 π_over_options | 决定当前状态激活哪个option | 神经网络输出各option的softmax概率 |
| option内部策略 π_option(a | s) | 决定option内部的具体动作 |
| 终止函数 β(s) | 决定当前option何时结束 | 神经网络输出0到1之间的概率值,常用sigmoid激活 |
这三组参数是整个架构的全部变量,训练目标只有一个:最大化累计折扣奖励。有趣的是,论文证明了不需要为终止函数设计单独的奖励信号,只要用策略梯度去优化整个轨迹的回报,终止函数就会自发地学会在合适的时机结束option。直觉上可以理解为:如果一直停留在一个无效的option里会导致收益下降,终止函数的梯度就会促使它增大在该状态下的退出概率。
3. 梯度公式的直觉与推导:为什么终止函数也能用策略梯度训
这一节可能是整篇论文最具原创性、也最容易被初学者绕晕的部分。我们先从传统策略梯度定理说起。
标准策略梯度定理告诉我们,对于一个参数化的策略π_θ,有:
∇J(θ) = E[ Σ_t ∇θ log π_θ(a_t|s_t) · A(s_t, a_t) ]
也就是沿着“让好动作概率变大、坏动作概率变小”的方向更新参数,权重是优势函数(当前动作相对平均表现好多少)。但这个定理的前提是每个决策步都是同一层策略在决策。如果引入了options,决策就变成了两层:高层选择option发生在option启动时,而option内部的动作选择发生在每一个时间步。这就不能再套用标准策略梯度定理了。
Option-Critic的关键贡献之一,就是把option内部的策略和终止函数也纳入一个统一的策略梯度公式。论文最终给出了形如这样的梯度结果:
对option内部策略参数θ:
∇θ J = E[ Σ_t ∇θ log π_{ω}(a_t|s_t) · Q_{ω}(s_t, a_t) ]
对终止函数参数θ_β:
∇θ_β J = E[ Σ_t ∇θ_β log β(s_t) · (A_{Ω}(s_t, ω) - V_{Ω}(s_t, ω)) ]
乍一看会觉得奇怪:终止函数的梯度怎么跟优势函数有关?直觉是这样解释的——终止函数的作用是判断“离开当前option是否比继续待在这个option更好”。如果当前状态下的option价值A_{Ω}(s_t, ω)很高,说明继续执行这个option是有利的,那么log β(s_t)的梯度就会让β降低,也就是减少退出概率;反之如果option价值为负,就倾向于提高退出概率。这其实是在做一种自动化的“切换决策”:系统自己判断什么时候该换技能。
我刚开始看论文公式时有个很大的困惑:这里的Q_{Ω}和A_{Ω}表示的是“option层面的价值”,不是“动作层面的价值”。也就是说,在计算终止函数的梯度时,我们关心的是“当前option整体上好不好”,而不是“当前option里某个具体动作好不好”。理解了这个区分,后面很多推导就顺了。
这个推导在2017年提出时还有很强的理论价值——它为“时间抽象”提供了一个可微分的端到端路径,后续很多工作都是在它的基础上做变体:比如用更复杂的终止函数设计、给终止函数加熵正则、以及在连续动作空间里调整方差缩减方法等。
4. 从理论到实践:我在复现Option-Critic时踩过的坑
论文框架听起来很美,但真正实现起来可以说是处处有坑。下面这些经验是我反复实验后的总结,希望能帮你少走弯路。
第一个坑:一起训练三层参数时的不稳定问题。Option-Critic把所有参数一起做策略梯度更新,听起来很干净,实际上在训练初期很容易出现模式坍塌。最常见的情况是:某个option在早期偶然获得了一点正奖励,高层策略就把它的选择概率推到接近1,其他option全部失效,最后整个系统退化成“单option策略”——也就是标准的单层强化学习。解决这个问题,我建议在训练早期给高层策略加较高的熵正则系数,让各option的初始化选择概率维持在一段可控的探索区间内。我当时调出来的做法是:熵系数从0.1开始,随着训练步数线性衰减到0.01甚至更低。
第二个坑:终止函数初始化太敏感。如果所有option的β初始值都偏大,那么每个option刚启动一下就终止了,系统几乎学不到时间抽象。如果β初始值偏小,option又容易变成“永远不终止”,退化为一个global策略。一个比较稳妥的初始化方案是把β的输出偏置设置为负值(比如sigmoid之前的logit初始化为-2,对应β大约0.12),让每个option在初期相对“黏人”一些,给内部策略足够的时间在环境中留下痕迹。这个细节在论文里没有明确说,但实际训练中它确实会比较敏感,需要根据具体任务做一些调节。
第三个坑:价值函数共享底层网络时的梯度干扰。论文经常用同一个共享网络来编码状态特征,再做actor和critic多头输出。这种共享可以提高样本效率,但也容易让不同option的梯度互相冲突。我的做法是:对不同option的内部策略独立设置输出头,并且在共享层后面加一个带stop-gradient的“状态特征提取器”来减少梯度干扰。这听起来像是很现代的trick,但在Option-Critic这个架构里同样适用。
第四个坑:经验回放与SMDP时间跨度。如果你是在离散动作、时间步较短的简单环境里做实验,标准回放缓冲就够了。但如果option持续时间长,一条轨迹里可能出现“几步属于optionA、再几步属于optionB”的情况。这时回放样本的时序信息就很重要。我在实践中发现,按episode存储、再从中分段采样(而不是简单地随机抽取单个transition)会更稳定。否则终止函数的梯度很容易被跨时间步的错误对齐破坏。
| 踩坑点 | 后果 | 解决策略 |
|---|---|---|
| 三层参数联合训练初期坍塌 | 所有option退化为一个 | 高层策略加熵正则,降低option数初始活跃度 |
| 终止函数初始化不当 | 学不到时间抽象 | logit初始化为负值,使β偏小 |
| 共享底层导致梯度干扰 | 训练不稳定,收敛慢 | 独立输出头 + stop-gradient特征层 |
| 回放采样跨时间步错乱 | 终止梯度噪声大 | 分段存储并保持时序完整性 |
5. 实验结果解读:四房间环境与它带来的核心洞察
论文里最经典的实验环境是“四房间”栅格世界。这个环境并不复杂:四个房间由一个走廊连接,目标点固定在某个房间里,智能体从随机位置出发。虽然环境本身简单,但它有一个明显的时间抽象结构——先移动到走廊口,再穿过走廊走到另一个房间。纯单层强化学习也能学会这个任务,但会非常慢;而Option-Critic在很多次实验里都呈现出更快的收敛速度。
我复现时拿到的一个观察结果是:学到的options并不总是符合“一个option对应一个房间”这种直观划分。有时候两个option的终止函数会在走廊中间出现明显的分割点,一个负责“从房间出来走到走廊中部”,另一个负责“从走廊中部走到目标房间”。这种划分并不是我预先设定的,而是优化过程自然涌现出来的——它说明时间抽象的分割点是由“状态价值变化最剧烈的位置”决定的。走廊中部恰好是任务上下文切换的位置,在那里终止并切换到新option能够获得最大的收益增益。
另一个值得提到的是option的复用性。如果在四房间环境中先让智能体在“目标在右上房间”的任务下训练,然后再切换成“目标在左下房间”,Option-Critic学到的options有部分可以直接复用(比如“走出房间”“通过走廊”)——这种复用性是传统单层策略没法直接给你的。但在真实工程里,复用效果的好坏很大程度上取决于状态表征是否稳定。如果状态光是原始像素,选项的泛化能力就弱很多;如果先学习一个状态嵌入再做Option-Critic,泛化性会有明显提升,这是一个实操中很有效的组合。
6. 现在看Option-Critic的局限与改进方向
任何算法都不是银弹,Option-Critic也如此。了解它的局限性,比知道它有多好更重要。
最大的局限是它只学习了一层时间抽象,这导致它在真正需要多级层次的任务里不够用。比如一个机器人需要先规划路径、再分解为肢体运动、再细化到关节扭矩控制,这种多级递归的层次结构远远超出了Option-Critic的表达范围。解决办法是把它嵌入更高的层次框架里,例如某个上层结构把task分解为子目标,再用Option-Critic在每个子目标内部学习时间抽象。
第二个局限是它对option数量的选择很敏感。论文里,option的数量K是预先设定的超参数,算法本身并不能自动决定“这个环境到底需要几个option”。设少了,不够用;设多了,某些option会被浪费或造成干扰。这个局限催生了很多后续工作,比如用贝叶斯非参数方法来在线确定option数量,或者在训练中对弱option施加惩罚使其“休眠”。
第三个局限是缺乏对终止函数结构倾向的约束。前面提到过,终止函数会自发地在状态价值变化剧烈的地方切换,但这不是在所有任务上都最优的。有些场景下,你希望option的持续时间更均匀、更稳定,以便于后续做规划层面的推理。这就要在训练目标里额外加一些正则项。后来的一些论文引入了“延迟惩罚”或者“状态抽象一致性”约束,本质上都是在调节终止函数的“切换频率”。
从工程视角看,我个人的体会是:Option-Critic最适合的场景是那些环境带有明显“技能切换点”,但你又不清楚这些切换点具体在哪的任务。比如游戏里的“躲避-攻击”循环、机器人导航中的“走直道-转弯”、以及文档处理中的“段落定位-内容提取”。这些场景sharing一个共同特征——存在天然的可复用子结构,且子结构之间的边界可以由数据驱动来确定。
7. 实操建议:如果你要在自己的项目里引入Option-Critic
想把这个架构应用到实际项目,我的建议是先从小规模环境做起,别一上来就打Mujoco或者Atari全集。Option-Critic虽然理论干净,但实现细节多,在小环境里调试最快,而且能直观看到学出的选项是否合理。
第一步:准备一个带有明显子目标结构的模拟环境。四房间、Taxi或者带多个目标点的简化导航都可以。关键是你要能可视化状态轨迹,这样才能观察option在哪些状态被激活和终止。
第二步:搭建两层网络。底层共享状态编码,高层分别输出option选择概率、每个option的动作概率和每个option的终止概率。注意option之间不要共享动作输出头,否则会削弱各自特征的独立性。
第三步:训练时先只更新一部分参数。我推荐一个trick:在最初的几千个样本里,把终止函数的更新冻结,只让高层选择和内部策略先探索。等见过足够多样的状态分布后,再放开终止函数的学习。这个过程非常像“先学会走路,再学会判断该不该停下”。
第四步:记录每个option的激活-终止状态序列。这是判断训练是否成功的核心指标。如果所有option的终止概率都接近某个常数,说明系统退化成单策略;如果option交替过于频繁,说明终止函数太敏感;如果option持续时间太长,可能是终止函数被压制了。理想状态是你观察到的option在状态空间中有清晰、可解释的分割。
第五步:注意奖励scale。Option-Critic和PPO不同,它的目标函数里没有直接用clip来限制更新幅度,所以奖励scale稍微变化一点,梯度幅度就会剧烈波动,导致终止函数策略崩溃。我的做法是:对长期回报做归一化(除以一个指数移动平均的绝对回报),或者使用带有自适应学习率的优化器并配个warmup阶段。这个细节在论文的伪代码里没有强调,但实践经验对最终效果影响很大。
| 调参项 | 推荐区间 | 说明 |
|---|---|---|
| Option数量K | 4~8(简单环境) | 太少学不出结构,太多容易冗余 |
| 终止函数logit初始值 | -2 ~ 0 | 偏小让option初期更“稳定” |
| 高层策略熵系数 | 0.05~0.3,逐步衰减 | 防止模式坍塌 |
| 内部策略学习率 | 比终止函数学习率略高 | 终止函数变化太快会导致波动 |
8. 对后续研究的影响与我的最终体会
Option-Critic架构在分层强化学习发展史上的地位是无可争议的。它第一次真正把时间抽象的发现过程统一进了策略优化的框架里,让“技能学习”不再是手工设计下的特例。后来的很多工作——我不提具体论文名,但你可以很容易在相关文献中看到——都在它的基础上做三个方面演进:改变option的表达形式、引入类似于option的分层目标生成机制、以及把option嵌入到更深层的递归结构中。几乎所有现代分层强化学习论文都绕不过对Option-Critic的讨论。
我个人的感受是:这篇论文给我最大的启发,不是那个精妙的梯度公式本身,而是一种“让结构自涌现”的哲学。大多数时候我们做强化学习,都在绞尽脑汁地设计奖励、设计网络结构、设计探索机制,却忘了结构本身也可以是优化的结果。Option-Critic把“何时切换行为模式”看作一个可学习的量,这个视角在今天的具身智能、机器人操作、自动化策略搜索里依然非常适用。
最后想再说一个实用的小技巧:如果你在自己的实验里发现Option-Critic学出的选项很难解释,不要急着改网络结构,先去看看终止函数随状态的变化热力图。很多时候,选项的边界其实已经学出来了,只是可视化结果被你忽略了。把每个状态对应的最大option激活概率画出来,你就知道智能体在哪些位置做了“决策切换”。这一步可视化绝对值得你花时间研究,它比任何训练曲线都能更快地告诉你系统的真实行为模式。