
做强化学习的朋友应该都有过这种经历一个任务拿单层策略端到端去训状态空间稍微大一点、回报稍微稀疏一点训练曲线就趴在地上不动。这时候最直觉的想法就是把任务拆成几个子目标让智能体先学会一步步逼近中间状态再完成最终目标。分层强化学习Hierarchical Reinforcement Learning, HRL就是沿着这个思路走的而绕不开的一篇工作是 NeurIPS 2018 上的 HIRO——Data-Efficient Hierarchical Reinforcement Learning全称是 Hierarchical Reinforcement Learning with Off-policy Correction。HIRO 要解决的痛点很明确以往分层方案虽然思路优雅但训练起来又慢又不稳。原因也不难理解——高层策略在学低层策略也在学两者共享一条因果链导致经验数据存在严重的非平稳性老样本一转眼就过期了。HIRO 用两招把这个问题按下去目标再标记goal relabeling和 off-policy correction让高层策略可以放心地复用过去收集的离线数据从而把数据效率拉到和普通 off-policy 方法一个水平。这篇文章适合三类读者想系统搞懂分层强化学习原理的研究者想在连续控制任务里复现 HIRO 的工程师以及正在做长时域、稀疏奖励任务苦于端到端策略训不动的同学。我会把算法拆开讲清楚再把我复现和调参过程中踩过的坑一并整理出来。1. 项目概览HIRO 到底在解决什么问题1.1 端到端强化学习的瓶颈在哪里先用一个生活化的类比。假设你要开车去一个陌生城市全程不做规划只靠一个“总策略”每一步都决策方向盘打多少度、油门踩多深。这个总策略既要知道高速怎么走又要知道下了高速进城区后怎么绕小路还得知道在小区门口怎么找车位。每一步的决策都背负着“离最终终点还差多远”的信用分配压力任何一个环节出错整个轨迹的回报都会崩掉。强化学习里的信用分配问题就是这么回事。在长时域任务里一个稀疏的外部奖励往往只在几百甚至上千步之后出现单层策略很难判断究竟是哪一步导致了这个结果。分层思想说白了就是把一个长任务拆成多个短任务高层策略负责低频的大方向决策比如“下一个目标点是哪个位置”低层策略负责高频的具体动作执行比如“用四足在 10 步内跑到那个位置”。每一层的决策时域变短了信用分配的压力也就小了。这是 HRL 的核心动机但“拆”完并不意味着能训好。真正的问题在于两层策略同时训练时高层策略看到的环境动态其实是被低层策略决定的。低层在变强高层眼里的世界就在变于是以前存下来的经验拿出来训练时可能完全失真。这种非平稳性是分层强化学习最大的拦路虎。1.2 老一代分层方法的致命伤早期分层强化学习里最典型的代表是 Feudal Networks 这类“封建式”结构高层给低层下达一个目标低层以目标为条件学习技能同时高层通过端到端的梯度回传获得反馈。听起来合理实际跑起来有几个硬伤。第一个伤是梯度不稳定。高层给低层的离散目标通常不可微或者低层策略本身是个复杂策略梯度穿过低层网络回传到高层时几乎全是噪声训练极难稳定。第二个伤是数据效率。很多早期分层方法使用 on-policy 算法比如策略梯度类每次更新完策略就得重新采样。分层之后高层决策频率低、单条轨迹跨度大重新采样的成本远高于普通任务样本效率低到难以接受。第三个伤是非平稳性导致的经验污染。低层策略一更新高层之前存好的转移样本里“同样的状态、同样的高层动作目标”对应的结果就变了。假如高层某次在状态 s 下达目标 g旧低层花了 30 步还没够到新低层可能 8 步就完成了。旧样本里记录的回报和后续状态对当前的低层来说完全不可信。HIRO 的思路就是冲着这几个伤去的低层和高层都用 off-policy 算法来训练保证数据效率再通过目标再标记和 off-policy correction 让旧样本在新低层下“重新变得可信”。这一套组合在当时的 HRL 领域确实是降维打击级别的设计。2. 核心机制拆解两层策略与目标再标记2.1 高层策略与低层策略的分工HIRO 采用的是两层结构模型整体是一个 off-policy 框架原论文使用的是 DDPG 类算法后续复现多用 TD3 来提升稳定性。你可以把高层策略理解成一个“项目经理”低层策略理解成“执行团队”。高层策略每 c 步才做一次决策输出一个目标 g_t。这个目标不是某个离散选项而是状态空间里的一个偏移向量。低层策略则每个时间步都做动作决策它的输入是当前状态和来自高层目标拼接而成的向量输出是具体的动作。低层每走一步环境会给一个真实的奖励 r_t但低层并不直接用这个外部奖励来学它还有一个自己的内在奖励r_low -‖s_{t1} - (s_t g_t)‖₂这个公式的意思是低层的目标就是让“当前状态加上目标偏移后”的位置与实际到达的下一个状态尽可能接近。换句话说低层学的不是“最大化环境回报”而是“老老实实执行高层下达的位移指令”。环境回报则累加起来交给高层高层学的是“下达什么样的目标序列能让一段时间的累计环境回报最大”。我们常说的“信用分配”在这里被物理隔开了低层只对“是否到达目标”负责高层只对“目标选得好不好”负责。外部奖励极其稀疏也没关系因为低层有内在奖励做密集信号高层有目标再标记和 correction 提供学习信号。2.2 相对目标表示容易被低估的关键设计HIRO 里最容易被初学者忽略的细节是目标表示成“相对偏移”而不是“绝对坐标”。我自己第一次复现的时候偷懒用了绝对坐标结果低层策略在训练集覆盖的区域内表现尚可一旦起点换到没见过的位置立刻失灵。原因很好理解绝对目标等于让低层记住“从各个位置到各个目标点”的行为映射这个映射严重依赖起点位置泛化性极差。而相对目标只要求低层学会一件事——“从当前位置出发向指定方向移动指定距离”。这本质上是一种平移不变性低层学到的技能可以无缝迁移到任何起点。这个设计带来的收益很大低层策略的泛化能力变强了高层也不需要为每个起点重新规划样本效率自然就上去了。如果你在自己的项目里复现 HIRO我强烈建议保留这个“相对目标”设定不要乱改。2.3 目标再标记从 HER 借来的“事后诸葛”HIRO 的第二个重要机制是目标再标记goal relabeling这个思想源自 Hindsight Experience ReplayHER。HER 的核心逻辑很朴素一个智能体没完成任务不等于这段经验没用。把目标改写成“它实际上到达的位置”这段经验就从“失败的尝试”变成了“成功执行目标的正样本”。在 HIRO 里再标记发生在两个层级。低层做 HER 式的再标记很容易理解每个低层转移样本 (s_t, g_t, a_t, s_{t1}) 都可以把目标替换成未来某个时刻的实际状态偏移 s_{tk} - s_t然后重新计算内在奖励。这样低层就能从大量“没到达目标”的轨迹中学到“其实也可以到达某种状态”的技能。高层的再标记稍微复杂一点。高层的一个转移样本是 (s_t, g_t, R_t, s_{tc})其中 R_t 是这 c 步内的外部奖励之和。如果高层目标没达成我们可以把高层目标改写成实际完成的位移 s_{tc} - s_t这个思想没问题。但麻烦在于这段样本是旧低层跑出来的改完目标之后样本代表的“在这个目标下的环境动态”是否还适用于当前低层这就引入了 off-policy correction也是 HIRO 全名里那个“Off-policy Correction”的真正含义。3. off-policy correction整个算法的灵魂3.1 为什么高层策略不能直接离线训练要理解 off-policy correction得先理解“为什么高层的离线样本会被污染”。假设你是一个项目经理你手下有一个团队。你用团队过去的产出记录来训练自己“怎么分派任务”。问题是团队能力在持续成长同样的任务上个月要干一周这个月三天就干完了。如果你还用上个月的记录来判断“哪个任务难、哪个任务好做”你的决策完全就被误导了。在 HIRO 里高层就是项目经理低层就是团队。高层的一条转移样本记录着“在某个状态 s_t 下达目标 g_t之后得到回报 R_t到达状态 s_{tc}”。但这整段经历是在旧低层下发生的。低层更新之后同样的 (s_t, g_t) 会走出完全不同的轨迹旧样本里的 R_t 和 s_{tc} 都不可信。如果高层照单全收地学相当于拿过期数据做现在决策训练必然不稳。有人会说那高层就用 on-policy 重新采样呗。听起来简单但分层结构里高层采样一条轨迹的成本是普通方法的 c 倍数据效率会急剧下降。HIRO 的野心就是要保住 off-policy 的数据效率所以必须给旧样本做“消毒处理”——这就是 correction 的由来。3.2 纠正目标的数学直觉off-policy correction 要做的事可以一句话概括找到一个“替身目标”g_t使得在当前低层策略 π_low 下用 g_t 去执行这条轨迹上的状态序列最有可能复现出当时真实采到的那些低层动作。形式化地看高层旧样本里存的不只是状态和高层目标还应该包括这个 c 步片段内低层的动作序列 a_t, a_{t1}, ..., a_{tc-1}。我们要最大化下面这个似然gt argmax{g} Σ_{it}^{tc-1} log π_low(a_i | s_i, g)这个优化的含义很妙原始目标 g_t 下达后旧低层通过一系列动作走出了某个结果。现在我们想知道“如果让当前的低层来走下达怎样的目标才能让它走出一样的动作序列”找到这个 g_t 之后这条样本就可以被视作“当前低层下从 s_t 执行动作 g_t 得到回报 R_t ”于是它重新成为一个可信的转移样本高层可以放心拿去更新 Q 函数。实际操作中这个优化不需要精确求解通常做 K 步梯度上升就够了。初始值直接用原始目标 g_t因为旧目标距离最优替身目标通常不远。这样既有足够精度又不会带来额外的大计算量。原论文里的典型配置是 10 步左右我看到的大多数高质量复现也沿用这个值。3.3 更新流程与实现要点把这一套流程落到代码层面高层策略的每一次更新大致长这样# 从高层经验池采样一批转移 for each sample (s_t, g_t, a_t:tc-1, s_t:tc, R_t) in batch: # 1. off-policy correction用梯度上升找到替身目标 g_prime g_t for k in range(10): g_prime g_prime lr_g * grad_g sum_i log π_low(a_i | s_i, g_prime) # 2. 用替身目标重新计算高层 TD 目标 g_next high_policy(s_{tc}) # 下一段的高层目标 y R_t gamma^c * Q_target(s_{tc}, g_next) # 3. 最小化 TD error 更新高层 critic # 4. 用 DDPG/TD3 策略梯度更新高层 actor这里有几个实现细节非常关键缺一个算法都会崩第一高层经验池必须额外保存 c 步内的低层状态序列和动作序列因为 correction 计算需要它们。如果你只存 (s_t, g_t, R_t, s_{tc})那 correction 根本无从做起。我见过好几个开源实现死在这一点上。第二correction 的目标函数用的是当前低层策略的似然。低层如果是 DDPG 类带高斯探索噪声的确定性策略似然可以从策略输出的动作均值和固定方差里算出来。如果用随机策略比如高斯策略直接取 log π_low 即可。第三correction 更新的学习率和步数要保守一点。目标空间是高维状态空间的时候梯度上升太激进容易跑到似然很低的区域反而破坏了样本的有效性。我的经验是先固定步数 10 步、学习率取 1e-2 量级再根据训练曲线微调。4. 训练流程、超参数与实验效果4.1 完整训练管线走一遍HIRO 的完整训练流程拆开来大概是这样的一个循环用当前高层策略采样一个目标 g_t并把这个目标交给低层。低层在内在奖励的驱动下连续执行 c 步期间每一步的状态 s_i、动作 a_i、内在奖励都记录到低层经验池。c 步结束后把这一整段作为一个高层转移样本聚合 (s_t, g_t, 外部奖励累加和 R_t, s_{tc}, 低层状态序列, 低层动作序列)存入高层经验池。定期从高层经验池采样先做 off-policy correction 得到替身目标再更新高层 Q 函数和高层策略。定期从低层经验池采样对目标做 HER 式再标记重新计算内在奖励然后更新低层策略。软更新两个层级的 target 网络。这里有一个容易被忽略的点低层不能只依赖高层现场下达的目标来训练。如果高层目前还没学会给好目标低层接触到的目标分布就很局限技能学不全面。所以 HIRO 给低层做 HER 式再标记时目标来源是“未来访问到的状态偏移”这等于让低层自己也能从失败片段里挖出正样本。高层和低层在训练早期实际上是互相搀扶着走的。4.2 关键超参数参考表我把自己复现时验证过的一组稳定配置整理成表大家可以直接当 baseline 用超参数推荐值说明高层决策间隔 c10低层执行子目标的时间窗口太小失去分层意义太大高层学习慢低层内在奖励-‖s_{t1} - (s_t g_t)‖₂也可以用平方距离但欧氏距离更常见off-policy correction 步数10梯度上升迭代次数correction 学习率1e-2 量级太高替身目标跑飞太低纠正不充分高层经验池容量2e5 或更大需要存低层状态/动作序列内存开销大低层经验池容量2e5常规 off-policy 配置批大小128两层通用底层算法DDPG 或 TD3复现建议直接上 TD3稳定性好很多目标噪声采样目标时加高斯噪声提升低层对不同目标的鲁棒性这里额外说一句高层经验池因为要附带 c 步的低层序列内存占用会明显膨胀。如果你在受限环境下训练要么把 c 调小一点要么对低层状态/动作序列做降采样存储否则跑久了内存容易吃紧。4.3 数据效率与泛化能力表现原论文选择的实验任务非常有代表性基本都是端到端单层策略很难啃下来的长时域稀疏回报任务。我用“类似 MuJoCo 的四足机器人仿真环境”来概括会比较稳妥典型任务包括三类一类是点目标导航类任务。智能体要在一片区域里走到特定的目标点。单层策略面对这种“看不到奖励、直到最后一刻才给反馈”的任务基本束手无策HIRO 拆分出高层导航和低层行走技能之后成功率能拉到接近满值而且训练步数比 on-policy 分层方法少一个数量级。一类是推动类任务。智能体要推动一个物体到指定位置。这类任务难在接触动力学和稀疏奖励同时存在低层必须先学会“靠近物体”这个技能高层再学会“往哪里推”。还有一类是坠落起身类任务。智能体要先学会从一个不利姿态恢复稳定再完成后续目标这考验分层结构的子技能复用能力。和当时的各种 baseline 对比下来HIRO 的优势集中在两点一是同等训练步数下成功率明显更高二是它能做到普通 off-policy 算法完全无法完成的长 horizon 任务。这也验证了“分层 off-policy correction”的组合确实同时拿到了端到端方法的数据效率以及分层方法的任务上限。5. 复现实践踩坑记录与调参心得5.1 常见问题速查表我把复现 HIRO 过程中以及帮别人排查代码时最常遇到的几个问题整理成一张速查表症状和解决方案一一对应症状根因解决方案高层 Q 值发散训练曲线剧烈抖动高层经验池没存低层动作序列correction 做不了或做错确认转移样本包含完整 c 步低层状态和动作低层始终学不会到达目标内在奖励收敛慢目标用了绝对坐标而非相对偏移改成当前状态的位移偏移恢复平移不变性高层训练过程中性能突然掉档低层被硬更新直接复制权重导致经验批量过期低层走软更新Polyak 平均降低非平稳性correction 后的替身目标偏离原始目标太多梯度上升步数过长或学习率过大步数降到 10 以内学习率降到 1e-2 以下低层总在目标附近振荡无法精确停住内在奖励的尺度太小被探索噪声淹没调大内在奖励系数或者改用平方距离增强梯度信号训练初期高层奖励一直为 0外部奖励稀疏高层样本几乎全是“失败样本”确认再标记逻辑打开了高层也要有 hindsight 样本帧率明显变慢高层经验池内存爆炸检查是否把低层序列全量存了考虑降采样5.2 调参时的几条实操心得多轮复现下来我有几个比较深的体会写在这里供参考。第一先单独验证低层的 goal-conditioned 能力再开高层。我见过太多人一上来就把两层一起训出了问题根本不知道是哪一层的锅。正确姿势是先用随机目标分布单独训低层观察低层能否在 c 步内平均到达目标附近如果这个基础能力不成立高层再怎么训都是空中楼阁。第二损失函数方面要多看两个指标低层的平均到达距离以及高层的平均替身目标偏移量。前者反映低层技能水平后者反映样本被纠正的幅度。如果替身目标经常偏离原始目标一大截说明低层策略变化太快或者目标维度不匹配要及时降学习率。第三c 值不是越大越好。有人以为 c 大一点高层决策压力小实际上 c 越大高层每个决策对应的信用分配跨度越长Q 函数的估计方差也越大。我一般先试 c10如果低层动作跨度确实很短再考虑 c20。反过来如果低层在 c 步内根本达不到目标说明 c 太小了要放大。第四探索设计要克制。高层目标采样时加一点高斯噪声是有益的能让低层在不同的目标条件下多接触边界情况但噪声方差太大会让低层策略的目标分布过于分散反而学不出稳定技能。我习惯在训练后期逐步退火目标噪声。第五优先用 TD3 而不是 DDPG 做底层框架。HIRO 原文用的是 DDPG但 DDPG 的 Q 值过估计问题在两层结构里会被放大——高层一个错误估计会连锁污染低层的目标分布。TD3 的双 Q 网络和延迟更新机制能省掉你大量调批评者网络的时间。6. 对后续研究的影响与适用边界6.1 分层强化学习此后的一条主线HIRO 在 2018 年之后成了分层强化学习领域绕不开的参照系原因不只是它效果好而是它把“分层时非平稳性怎么处理”这个问题讲清楚了并且给出了一套可操作的 recipe目标再标记 off-policy correction。后来很多改进工作都是在它的骨架上做的。一个直接的方向是改进目标生成机制。HIRO 的高层目标就是状态空间里的随机偏移并没有太多“语义”在里面后续工作开始研究怎么让高层自己发现更有意义的子目标比如基于状态访问频率、基于互信息、或者基于到达难度让低层学到的技能更结构化。另一个方向是把“固定 c 步切换目标”改成“低层主动判断目标是否完成”。hiro 里无论目标达没达到c 步到了就切换这其实有点浪费。很多后续工作采用 option 式的设计让低层通过内部终止条件来决定何时向高层请求新目标。好处是执行效率更高代价是终止条件的学习同样会带来非平稳性和 off-policy correction 的结合并不像想象中那么顺。还有一个现实方向是把这套方法迁移到真实机器人上。HIRO 本身是在 MuJoCo 这类仿真环境里验证的真实机器人面对的状态估计误差、执行噪声和延迟会让修正目标优化的稳定性打折扣。不过它“相对目标 低层技能复用”的框架和真实机器人常用的运动原语思想很契合所以后续的 Sim-to-Real 分层工作里经常能看到 HIRO 的影子。6.2 HIRO 的适用条件与已知局限再好的方法也有边界HIRO 的几个局限值得摆在明面上说。首先目标是“状态空间里的一个偏移”这要求任务的目标必须能用状态空间直接表达。如果子目标不是状态上的“到达某个位置”而是“把门推开”这种包含接触变化的目标HIRO 的目标表示就不够用了。碰上这类任务你需要先想清楚目标空间的重新定义否则别急着套框架。其次HIRO 默认低层是一个目标条件策略技能是“短时域内朝着目标靠近”。对需要多技能切换的长任务比如“先推开障碍再搬运最后组装”单层 goal-conditioned 低层会显得力不从心。真正复杂的多技能组合需要更高层次的结构比如技能库 调度层配合 HIRO 这种 correction 机制才行。再有off-policy correction 依赖一个“能够计算低层策略似然”的设定。离散动作空间里似然好算连续动作空间只要用高斯噪声策略也好算但碰上随机策略的熵非常大、或者低层是模型无关的采样式策略不输出显式分布correction 的目标函数就会变得不稳。这不是致命伤但意味着你需要在概率模型上做一些额外设计。最后数据效率高是相对而言的。HIRO 能啃下端到端方法做不了的长时域任务但训练步数往往还是百万级起步。如果你的任务本身可以在几千步内靠单层策略轻松解决那就没必要上分层徒增复杂度。我个人在实际复现和调参过程中的体会是HIRO 是一个“思路极度克制”的算法。它没有引入复杂的技能库没有引入额外的模块化网络就是靠目标再标记和 off-policy correction 这两个机制把分层训练中最要命的非平稳性解决了。这种“用最小的机制解决核心问题”的设计哲学比它刷出的实验数据更值得学习。如果你们手里也有一个长时域、稀疏奖励的任务我的建议是先问自己三个问题子目标能不能定义成状态空间里的偏移低层能不能用一段连续的、目标条件的动作技能来实现子目标外部奖励是不是至少在子目标完成时有局部反馈如果三个答案都是肯定的HIRO 这套框架值得你花一周时间跑一版 baseline。如果子目标本身很难定义成“状态 偏移”的形式那问题就不在分层方法上而在目标定义上。先把目标的表达问题解决再从 HIRO 里借 correction 的思路往往比强行套框架有效得多。