2017年前后是深度强化学习最热闹的几年但也是大家开始产生怀疑的几年。深度Q网络证明了像素输入可以直接学出策略各种“模型玩游戏”的工作层出不穷可这些模型到底是在“解决任务”还是在“背下任务”谁也说不清楚。一个很多人认同的判断是机器缺少人处理长任务时那种“拆成子目标、分时间尺度决策”的能力。Option-Critic就是在那个时间点出现的。它做的事情一句话能讲完把原本需要手工设计的“选项/技能”option变成一个能完全端到端训练、自动从回报里涌现出来的架构。它同时学三样东西——每个选项内部的低级策略、选项之间的选择策略、以及选项何时终止——而且三者用同一个价值函数串联全部靠策略梯度联合优化。如果你在研究层次强化学习或者想在项目里引入“时间抽象”的能力这篇论文值得仔细拆。下面我把这套架构的来龙去脉、关键公式怎么来的、以及复现时那些文档里不会写的坑全部摊开讲一遍。1. 项目概述与核心思路拆解1.1 先搞清楚Option到底是个什么东西Option一般翻译成“选项”但我更愿意叫它的本名“时序扩展动作”temporally extended action。普通强化学习的动作是单步的这一帧移动一格、这一次往前走、这一拍发一个指令。而Option是一段被命名好的行为序列比如“从当前房间走到门口”“走到目标房间”“绕开障碍物”。人的行为组织方式本来就是分层的我“去厨房”是一个高层动作里面包含了站起来、迈步、转弯一大堆底层动作但我规划的时候只思考“去厨房”这一条不会去抠每一步怎么迈。在这个语境里一个option有三个组成部分它允许在哪些状态下启动初始集I、被选中后内部如何执行内部策略π、以及每一步结束后以多大概率把这个option交还给高层终止函数β。经典框架里这三个部分要么手工设计要么靠很别扭的方式单独学而Option-Critic做的是把π和β的梯度都算出来让它们和价值函数一起联合训练。换句话说你不再需要告诉模型“什么是好的option”只需要告诉它“我给你N个option的名额”至于每个option具体长成什么样、在什么状态下该激活、什么情况下该结束全部交给回报信号自己去长。1.2 为什么要动“时间抽象”这个念头最直接的理由是稀疏回报。拿论文里反复出现的四房间网格环境举例一个11x11的格子世界被墙壁分成四个房间每面墙上有门通向隔壁目标固定在某个格点到达才给奖励。对一个随机游走的平坦策略来说想在上百步的无头探索里正好踩中那个目标格点概率非常低。更糟糕的是就算侥幸到过一次它也记不住“先到门口、再穿门、再去目标”这种规律因为除了最后一格每步的即时奖励都是零。层次化思路是在两个尺度上做决策高层只在房间之间选择“我去哪个门口”低层负责把“走向那个门口”执行成几十步的动作。这样一来规划深度从上百步压缩到几步探索也从瞎走变成了有方向性的探索。Option-Critic真正贡献的不是这个思想——这个思想九十年代末就有了——而是把“选择哪个门、脚步怎么迈、什么时候算到达门口”这三层结构全部变成可微参数直接用一个统一的目标端到端训练。1.3 与经典选项框架的根本区别经典选项框架的核心操作是“只学习高层策略”给定一组设计好的option比如“向上走到顶”“向左走到边”然后用Sarsa或Q-learning去学在什么状态下应该选哪个option。这个路线的问题在于option本身是任务相关的知识换个环境就得重新设计而设计一个能用的option往往比学一个策略还难。Option-Critic的变革在于它把option内部策略、终止函数、option选择策略看成同一个计算图的三条分支价值函数critic作为中间的监督信号用两个梯度定理把信号分别传导给三条分支。为了更直观我把三种方案的差别列成一张表方案动作粒度需要人工设计什么端到端学到什么平坦策略单步奖励函数状态到动作的映射经典选项框架多步option内部策略、终止函数状态到option选择的映射Option-Critic多步只指定option数量内部策略、终止函数、选择策略表格里最后一行就是这篇论文的核心买卖把人工设计的部分砍到只剩一个“数量”超参数剩下的全部交给优化。2. 原理拆解关键公式与推导直觉2.1 从MDP到Option半马尔可夫视角先把标准设定摆出来。一个马尔可夫决策过程由状态集、动作集、转移概率、奖励函数和折扣因子组成策略输出每个状态下选动作的概率目标是最小化长期折扣累计回报的负期望。普通策略梯度定理说的是回报的期望对策略参数的梯度等于在轨迹上对“选这个动作的log概率”乘以“这个动作后续的回报”求期望。Option-Critic把“一个动作”替换成“一个时序扩展动作”——也就是option。这样一来整个过程的视角拉长了高层看到的不是一个“状态→动作”的马尔可夫过程而是一个“状态→option”的半马尔可夫过程因为一个option的决策效果要跨越多个时间步才能显现。但真正棘手的问题在于option本身不是一个黑盒它的内部策略、终止函数都是有参数的梯度需要穿过option内部。2.2 选项的三元组定义与符号约定一个option由三部分构成。初始集I规定了option在哪些状态下可以被选择内部策略π_o(a|s)规定了这个option被选中后在每个状态下以多大概率执行哪个原子动作终止函数β_o(s)是一个0到1之间的概率表示到达状态s后这个option是否结束、把控制权交还给高层。执行节奏需要格外注意在状态s处高层按π_Ω(o|s)选一个option然后agent执行内部策略产生的动作a转移到达s拿到奖励r。到s之后才裁定以概率β_o(s)决定option终止并重新选择或者以概率1−β_o(s)继续执行同一个option。“终止发生在转移之后”这个时序后面会反复用到也是很多错误实现的根源。一个重要特例是如果β_o(s)恒等于1这个option退化成了一步即终止的单步动作。也就是说选项集合是原子动作集合的自然扩展而不是替代。这保证了Option-Critic的表达能力不会比普通策略更弱至少从结构上讲。2.3 三个价值函数怎么互相嵌套为了给三条分支提供监督信号需要三个价值函数。第一个是option价值Q_U(s,o)表示从状态s开始执行option o直到它终止为止的期望折扣回报。第二个是Q_U(s,o,a)表示在option o被选中的情况下先执行动作a、之后继续遵循option内部策略的期望回报。第三个是高层状态价值V_U(s)表示从状态s开始、按照当前高层策略π_Ω选择option的期望回报。三者关系如下Q_U(s,o) Σ_a π_o(a|s) · Q_U(s,o,a)Q_U(s,o,a) r(s,a) γ Σ_{s} P(s|s,a) · [ (1−β_o(s)) · Q_U(s,o) β_o(s) · V_U(s) ]V_U(s) Σ_o π_Ω(o|s) · Q_U(s,o)中间那个方括号是关键如果option在s处不终止那么未来继续由同一个option兜着价值是Q_U(s,o)如果终止了控制权回到高层价值变成V_U(s)。这其实就是把“是否终止”这一个随机决策嵌进了标准Bellman方程里。实际写TD更新时目标就是target r γ · [ (1−β(s)) · Q_U(s,o) β(s) · V_U(s) ]这个target既用来更新价值函数本身也用来构造后面各种advantage。2.4 两个核心梯度定理内部策略梯度和终止梯度论文最实质的贡献是两个梯度定理。第一个是内部策略梯度定理近似写成∇_θ Q_U(s,o) E[ Σ_t γ^t · Q_U(s_t,o_t,a_t) · ∇_θ log π_θ(a_t|s_t,o_t) ]用大白话说option内部策略更新时把“在这个option下执行这个动作之后的价值”当作评价走的是标准策略梯度。所以option内部并不是在凭空学技能它是在学习如何最好地实现顶层的选择。实际实现里会再扣一个baseline降方差于是评价量变成advantage而不是原始Q值。第二个是终止函数梯度定理形式上更反直觉∇_θ Q_U(s,o) −E[ Σ_t γ^t · (Q_U(s_t,o_t) − V_U(s_t)) · ∇_θ log β_θ(s_t) ]这里的负号特别重要。令A(s,o)Q_U(s,o)−V_U(s)它表示“当前这个option比高层平均水平好多少”。如果优势为正说明这个option正在做正确的事应该让它继续跑下去也就是终止概率要下降——负号保证了这一点。反过来如果优势为负说明这个option拖后腿应该提高终止概率把它换掉。落到最小化loss的实现层面终止函数对应的loss是L_β A(s,o) · log β(s)A为正时最小化它会把log β往负方向压也就是β变小、option不容易终止A为负时则推着β变大、加速终止。终止函数的学习信号不是奖励本身而是“当前option比平均好多少”的优势这是整个架构最核心的直觉。2.5 别被公式吓住它本质上是一条链式法则从直觉上理解option级过程构成一个半马尔可夫决策过程动作是“option执行一整段”时间步是“从选option到终止”。目标对β参数和内部策略参数的梯度本质上是在问两件事改变终止概率会怎样改变option的持续时间进而怎样改变回报分布改变内部动作选择会怎样改变option在执行过程中的即时回报分布。这两个问题和普通策略梯度问的是一模一样的事只不过这里的“决策”分成了两个层次——每一步是否终止、每个时刻选哪个动作。需要说明的是论文给出的定理是在比较规范的条件下证明的但一旦上了深度网络和函数近似实际训练效果主要由标准的actor-critic技巧控制比如value是否正确detach、advantage要不要归一化、学习率怎么配。定理保证了更新方向的合理性却不保证网络不会在某个局部行为里抽搐。所以看公式时记住“终止看优势、内部看价值”这两个方向就够了真正的功夫在实现细节里。3. 实操过程与核心实现要点3.1 网络结构一个共享主干四个输出头Option-Critic的实现不复杂常见的网络结构是一个共享特征层负责把状态转成向量然后分出四个输出头。高层选择策略π_Ω在option集合上做softmaxcritic头输出每个option的Q_U值内部策略头为每个option分别输出动作概率也就是N个option就对应N个softmax动作头终止函数头为每个option输出一个sigmoid概率。一个能跑通四房间的最小配置是两层全连接、每层128个隐藏单元、激活函数用ReLU或者tanh。如果状态是像素输入就把主干换成卷积层后面再接同样结构的头。option数量N是先验超参数四房间这种有四个门的环境取4完全没头绪的任务先取4到8不要贪多后面我会解释为什么option太多反而容易坏事。一个小建议不同option的内部策略头不要共享初始化。给每个option的最后一层加不同的偏置能让它们在训练初期就各自偏向不同的动作方向避免“长得一模一样”的可悲开局。3.2 训练流程A2C风格的单步更新下面是我常用的单步更新伪代码每个环境step都执行一遍# 输入状态特征 phi_s q_values critic(phi_s) # 每个option的Q_U(s,o)形状[N] v_s (pi_options(phi_s) * q_values).sum() # 高层价值 V_U(s) option sample(policy_over_options(phi_s)) # 高层选option action sample(intra_policy(phi_s)[option]) # 内部策略选动作 phi_s2, reward, done env.step(action) # 关键到了s之后才算终止概率 beta_s2 termination(phi_s2)[option] dial Bernoulli(beta_s2).sample() option2 sample(policy_over_options(phi_s2)) if dial or done else option # 1) critic更新用期望beta构造TD目标 q_next (1 - beta_s2) * critic(phi_s2)[option] beta_s2 * (pi_options(phi_s2) * critic(phi_s2)).sum() target reward gamma * (1 - done) * q_next critic_loss (target.detach() - q_values[option]).pow(2) # 2) 高层option选择策略更新 adv target.detach() - v_s.detach() option_loss -log_pi(phi_s, option) * adv # 3) 内部策略更新评价当前option内的动作 intra_loss -log_pi(phi_s, option)[action] * adv # 4) 终止函数更新最小化 A * log beta A (q_values[option].detach() - v_s.detach()) termination_loss A * torch.log(beta_s2 1e-8) total_loss critic_loss option_loss intra_loss termination_loss total_loss.backward()这里有三件事必须解释清楚。第一构造TD target时用的是期望beta而不是伯努利采样出来的那个0/1开关因为Bellman方程里算的就是期望真正的采样开关只用来决定下一个时刻到底执行哪个option。第二所有作为监督信号传给别的头的估计值都要detach否则价值函数和策略会互相咬尾巴训练很容易发疯。第三严格来说内部策略梯度的理论上界应该用Q_U(s,o,a)减去baseline但工程实现里直接用单步TD advantage效果也稳定而且方差更小。先按这个简单版本跑通再往上加GAE也不迟。3.3 超参数与调参经验把我在四房间和各种小控制任务上试过的经验参数整理成表格超参数建议范围说明option数量N4~8与任务天然子结构数量同量级学习率1e-4~3e-4Adam为主终止函数头单独用小学习率β初始化logit−0.5~0对应β约0.38~0.5防止崩向0或1内部策略熵系数0.01~0.05保证option内部有探索终止函数熵系数0.01~0.1防止过早锁死在“永远不终止”折扣γ0.99长回报路径任务步数n5步累计回报单步TD容易方差大5步通常够值得注意的是终止函数的更新对学习率非常敏感。我给终止函数头单独配一个优化器学习率通常是主网络的十分之一效果明显比全家桶一起更新稳定。另外每跑几百个episode就把各option的β均值打出来看一眼如果所有option都稳定在0.5附近没动说明优势信号没进来多半是critic出了问题而不是策略问题。3.4 复现基准四房间环境里该看到什么四房间是最好的调试场因为它结果直观、算力开销极小。训练结束后应该看到两类现象。第一终止函数的热力图明显集中在门口附近——这意味着option“走到门口”正是它们认为该结束的地方。第二option内部策略呈现空间分化比如某个option的轨迹集中在上方门附近另一个集中在下门高层策略则学会按目标位置编排这些option的先后顺序。如果训练完看不到门口附近的终止集中先别怀疑算法先问三件事option数量是不是太多导致每个option都分摊不到明确的子任务β是不是已经崩到0或1终止函数的优势是不是被错误地detach掉了。弹球环境也是论文里用过的连续域示例里面有若干挡板option-critic能学出“贴近挡板、蓄力、朝目标弹出”这类的技能结构终止点恰好落在挡板附近。四房间跑通了再上弹球这类连续环境你会对“时间抽象”这四个字有真正的体感。4. 常见问题与排查技巧实录4.1 选项退化所有option变成单步动作最常遇到的问题是β快速逼近1每个option选择之后执行一个动作就终止层次结构彻底消失训练曲线和普通平坦强化学习一模一样。原因是多方面的β初始化太靠近1、终止函数学习率过大、优势没有做归一化导致偶尔的大梯度把β推过头。解决的思路也很直接。β的logit初始值设成−0.5甚至更低让训练从“倾向于持续执行”开始。给β的logit加一点熵正则防止概率锁死在边界。更稳的办法是把终止函数的优化器和主网络分开把它的学习率压到主网络的十分之一。最后一定记得对优势做标准化至少也要做一个裁剪不然终止信号忽大忽小很容易把β拍向极值。4.2 另一个极端选项永不终止和上面相反β也可能一路掉到接近0第一个选中的option从此陪跑到回合结束高层决策形同虚设。这种情况在稀疏回报任务里尤为常见。原因通常是所有option的价值估计差不多优势长期处于零附近终止梯度根本没有有效的推动力。一个实在的救法是把β的初始化往上抬一点比如logit设为0.2同时在终止logit上保留较大的熵正则逼它不要静止不动。另一个办法是加一个很小的、恒定的“终止倾向”项让每个option在每个状态都有最低限度的终止概率相当于给层次结构一条呼吸的缝。如果某个option长期不终止还有一个歪招单独把这个option的终止熵系数调大让它在犹豫中分化出更多的终止行为。4.3 所有option长得一模一样option数量设得过大或者初始化完全对称会出现所有option的内部策略收敛到同一个分布高层选谁都一样整个架构退化成一个带冗余头的平坦策略。我自己踩过这个坑N16时四个房间的实验里16个头全学会了向右走看着既好笑又心疼。破法从易到难排先减少N四房间用4就够别贪心再给每个option的内部策略头和终止函数头加不同的偏置初始化破坏对称性然后给内部策略加熵正则鼓励每个头自主探索出不同的行为。如果这些都试过还是同质化就需要上一点显式的多样性约束了比如让option内部策略的分布彼此拉开距离但这类方法比较复杂我建议先把它当成最后的手段。4.4 复现结果不稳定、跑不出论文的热力图如果代码逻辑看起来都对但训练结果忽好忽坏大概率是几个隐蔽的细节问题。我列一张排查表现象可能原因建议全是噪声热力图无结构TD target里用了采样的0/1开关而不是期望β确认target用的是β(s)本身训练开始就发散监督信号没detach所有advantage和target作为监督时都要detachβ很快锁死在0或1学习率太大或熵正则缺失终止头单独低学习率加熵正则结果依赖随机种子环境奖励尺度太大归一化奖励或做advantage标准化不稳定单步TD方差大改用5步累计回报或GAE另外提醒一句论文报告的结果往往是多次随机实验里的代表性曲线不是每次都能复现一模一样的热力图。多跑几个随机种子看中位数趋势比盯着某一次实验的曲线更靠谱。4.5 一个反复出现的时序理解错误最后这点值得单独拿出来说。β(s)的s是转移之后到达的新状态不是转移之前的老状态s。执行顺序是在s处选option执行动作a到s拿奖励r然后才在s处决定“这个option还继不继续”。TD target里那个β参数应该来自s。我见过不少实现把β(s)放在转移前的位置看起来公式差不多实际价值计算全错而且很难查出来因为loss照样会下降只是训练出的行为完全不符合理论。判断你的实现有没有踩这个坑只需要检查TD target一行的状态符号如果β取的是下一个状态的特征那就对了如果取的是当前状态的特征请立刻改掉。5. 延伸思考与个人体会5.1 从Option-Critic长出来的研究方向这篇论文之后层次强化学习的研究方向分出去好几条线。一条是把“终止”这个动作赋予成本让终止函数在“继续执行”和“终止重选”之间学会一种经济的平衡高层决策不再免费从而更接近真实系统里规划成本高昂的设定。另一条是把优势函数换成兴趣函数只学习那些“值得专门为之启动一个option”的状态避免在无关紧要的状态上白费option维度。还有一条影响更大的线是彻底改变训练方式先离线预训练一组技能skill冻结之后高层只学技能之间的组合调度不再端到端回传梯度。这些后续工作你都会看到Option-Critic的影子尤其是“终止函数由优势驱动”这个设定几乎成了所有skill-based方法的标准组件。换句话说理解了这篇论文后面很多分层RL论文你都能一眼看出它们在哪里改了终止规则、在哪里换了训练信号。5.2 我的亲测体验Option-Critic什么时候真能用说点扎心的大实话。我先后在几个环境里复现过这个架构结论是它不是一个开箱即用的提分机器。在连续控制的标准benchmark上一个调好的平坦策略常常更快更强因为这类任务没有明显的瓶颈状态层次结构反而成了束缚。但在四房间这种天然带子结构、奖励又稀疏的任务里Option-Critic能稳定地长出比手工设计还干净的空间技能这是平坦策略做不到的。所以我给自己定了三条判断标准三条里中了两条才考虑上层次化第一任务是否存在自然的子结构或瓶颈状态比如走廊、门口、挡板第二低层执行是否比高层决策更昂贵比如机械臂需要平滑轨迹而规划只需决定目标点第三是否需要跨任务迁移共享技能池。没有这些前提说实话层次化是给自己找麻烦。5.3 复现时最值得记住的三条实操建议最后收个尾给真想动手复现的你三条建议。第一先在四房间这类小网格环境把整套流程跑通、把热力图看顺眼再考虑上大环境因为大环境下你根本分不清是算法问题还是显示问题。第二β单独用一个低学习率优化器并且坚持加熵正则终止区间两端是重灾区几乎所有训练事故最后都发生在β上。第三永远检查detach的位置监督信号是“评价量”而不是“被优化的量”这个界限一旦模糊理论再漂亮也救不了训练曲线。我不敢说Option-Critic是那种能让你在测评榜上直接提分的算法在不少标准任务上它甚至不如一个调好的平坦策略。但我认为它是层次强化学习里最重要的转折点之一——它第一次把“如何终止、如何执行、如何选择”放进了同一个可微框架里让时间抽象成了一个可以靠梯度涌现出来的性质。我后来看任何分层方法都会下意识地去找这三个信号分别由谁在什么地方提供。这个思维习惯比跑通一个demo值钱得多。