ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

强化学习必知:HER算法如何用后见之明破解稀疏奖励难题

强化学习必知:HER算法如何用后见之明破解稀疏奖励难题 “hindsight”这个词字面意思是“后见之明”。在强化学习RL领域它指向的是一个让智能体从失败中学习的关键技巧Hindsight Experience ReplayHER后见之明经验回放。我第一次看到这个思路是在 DeepMind 2017 年的那篇论文上当时最大的感叹是——原来稀疏奖励sparse reward环境下的训练瓶颈还有这种近乎“不讲道理”的解法。如果你正在做机器人控制、自动驾驶决策、游戏 AI或者任何涉及“目标达成”这一类任务大概率被同一个问题折磨过智能体训练了半天奖励一直为零梯度消失策略固死在随机探索阶段。这种环境下HER 是我目前用过的效果最直接、落地成本最低的解决方案之一不需要改网络结构不需要重新设计密集奖励函数只需要在经验回放环节动点手脚。这篇文章就把 Hindsight 的思路、细节、代码和坑一次讲清楚。1. 为什么稀疏奖励让强化学习寸步难行1.1 稀疏奖励的“零奖励”困境先摆一个基本事实大多数真实机器人任务都属于稀疏奖励任务。比如机械臂抓取物体你设定目标是把物体放到指定位置自然状态下只有最终位置对了才给 1 奖励其他任何中间状态都是 0。这听起来很合理但切换到 RL 训练视角就完全不一样了。强化学习的本质是“试错 奖励反馈”。奖励信号量级越大、密度越高策略更新的方向就越明确。稀疏奖励意味着智能体在探索阶段几乎得不到任何有效的方向信息。拿一个四自由度机械臂举例状态空间可能是 7 维以上位置 速度 角度信息动作空间是连续力矩如果目标位置只占整个状态空间的千分之一随机策略尝试一万次也未必能碰上掩码内定义的“成功”状态。结果就是整个 episode 里的 return 全是 0策略梯度为零网络权重几乎不更新训练曲线长时间趴在零点。这个问题本质上不是网络容量不够也不是优化器选得不好而是经验池里缺少“有用”的经验。绝大多数被采样的轨迹其终局状态和目标状态之间的差距太大了损失函数无法提供任何有意义的反馈。你可能会想那我是不是把奖励设密一点比如离目标越近奖励越大理论上可以实际里面临两个新问题一是设计密集奖励函数需要大量环境建模的工程量二是稠密奖励很容易引导智能体走捷径——它会发现“大力出奇迹”能让物体掉到地面附近从而拿分却完全学不会正确抓取。1.2 为什么“经验回放”会失效经验回放Experience Replay本身是个老技术DQN 就开始用了。它解决的问题是打破样本相关性、提高样本利用率。但是从“稀疏奖励”这个角度看标准回放机制有个隐藏的致命伤它保存下来的 curator 经验绝大多数是“躺在床上做梦想中大奖”的废数据。具体来说一个标准的(s, a, r, s, done)元组如果 reward 恒为 0那它不管被回放多少次对 Q 值网络的修正能力都极其有限。TD 学习的关键在于r γ max_a Q(s, a)这个 bootstrap 目标如果 r 一直是 0而 s 又是一个完全没有接近目标的随机状态那 Q 值就会按着“所有状态都没用”的方向收敛最终得到一个处处是 0 的近似 Q 函数。这比随机还要蠢。很多人卡在稀疏奖励任务的第一反应是“加大探索噪声”“加随机策略热启动”这能解决一部分八字还没一撇的冷启动问题但依然绕不开一个核心矛盾即便是随机探索碰到的状态序列放在“当前目标 G”的视角下看全是失败失败经验怎么榨取学习信号HER 给出的答案是换一个目标看这批经验。你想达成 G 没达成但你最后确实到达了某个状态 SS 如果当作目标来看这条轨迹就是一条完美的“成功轨迹”。这就是后见之明。2. HER 核心算法用后见之明重构目标2.1 HER 的思想公式与流程拆解HER 不需要新的奖励函数不需要改环境它只改数据集。核心逻辑可以拆成三步正常让智能体在一个 episode 中用策略 π(a|s, g) 交互其中 g 是原始目标比如“把方块推到坐标 (x, y)”。等这个 episode 结束后把整条轨迹transitions全部拿出来额外取一个“后见目标” g。这个 g 通常是轨迹中某个真实到达过的状态。用 g 替代原有的 g把原始轨迹中的每一个 transition 改写为(s_t, a_t, r_t, s_{t1}, done, g)其中 r_t 就是基于 g 计算的奖励。因为 g 是从轨迹里选出来的末尾的 transition 一定满足“目标达成”所以 r 至少有一个非零done 标志也变成 True。把这些“伪造”的轨迹和原始轨迹一起塞进 replay buffer供 off-policy 算法更新。举个最简单的例子。机械臂的目标是抓取红色杯子结果它整个 episode 都往左边乱撞最后停在了蓝色杯子旁边。原始轨迹的奖励是 0但 HER 会生成一个“目标为抓取蓝色杯子”的改写版本在这个改写版本中机械臂最后确实“成功”了。下次训练时网络会学到“往左边探索可以抓住蓝色杯子”之后再有蓝色杯子的任务它就有了先验。这种从失败轨迹中提取“局部成功”的能力是把稀疏奖励任务从“学不动”变成“学得动”的关键。伪代码层面其实特别简洁def hindsight_transformation(episode, goal_reward_fn): transitions [] for t, (obs, action, reward, next_obs, done) in enumerate(episode): # 原始轨迹保持原样进入训练 transitions.append((obs, action, reward, next_obs, done, episode.goal)) # 取一个后见目标这里用 final state 举例 hindsight_goal episode.final_state[:goal_dim] # 只取 goal 相关的维度 new_reward, new_done goal_reward_fn(hindsight_goal, hindsight_goal) for t, (obs, action, reward, next_obs, done) in enumerate(episode): r goal_reward_fn(obs_next, hindsight_goal) # 按后见目标算奖励 done_new (t len(episode) - 1) # 轨迹尾部达成目标 transitions.append((obs, action, r, next_obs, done_new, hindsight_goal)) return transitions注意有个前提条件这类改写只能在 off-policy 算法里用。因为策略 π 是在旧目标 g 的条件下生成的期望而现在回放的轨迹对应的是 g 的策略分布。HER 本质上是把异策略的单步数据借来换了个目标再用一次。on-policy 算法如 REINFORCE、A2C更新时要用重要性采样修正否则会引入策略偏移。2.2 为什么 HER 能在稀疏奖励下工作要理解 HER 为什么有效得回到“稀疏奖励为什么失败”的根本——不是经验太少而是有效经验太少。一个 episode 里包含了几十甚至上百个 transition虽然整体没有拿到奖励但其中有相当一部分子轨迹是“向着某个合理方向”走的。这些子轨迹确实没达到预设目标但它们的信息量并不低问题只在于目标定义把它全判为失败。HER 相当于用后见目标把“失败轨迹”重新分类让原本单调的 0 奖励序列中产生出有价值的梯度信号。它本质上是一种数据增强data augmentation把目标空间中的多样性注入到回放数据中。从这个角度来看goal-conditioned RL 任务的难度很大程度上取决于两个东西一是采样轨迹的丰富程度二是“目标空间”和“状态空间”是否平滑对应。HER 解决的是后者它让目标空间不再只包含那个可望不可即的“金目标”而是覆盖了经验中已经到达过的各个位置。我实测下来的感觉是HER 对任务突破的幅度往往比调两三天网络结构要大得多。原因是结构层面的改进提升的是函数逼近能力而 HER 提升的是训练数据的信噪比。当数据里全是 0 奖励时再强的网络也学不出东西来当数据里带上了“局部成功”的稀疏奖励后训练才能动。3. 四种后见目标替换策略怎么选、为什么3.1 final / future / episode / random 四种策略对比论文原文提出 4 种从 episode 中选取后见目标 g 的方法。我实际操作下来差异确实明显直接给结论再讲理由。策略选法适用场景我的体感效果final取轨迹最后一个状态的目标维度任务目标主要由终点决定如推箱子、点到点导航中等早期学习够用future从当前时刻 t 之后的未来状态中随机抽取一个含连续中间关键点、需要中途转换方向的程序性任务最好推荐优先试episode从同一条 episode 中随机抽一个状态目标不可预测、随机落点的任务比 final 好但略逊 futurerandom从其他 episode 的经验池里随机抽一个状态目标分布广、有较好状态覆盖时效果依赖 buffer 状态多样性波动大final 策略的直觉最好懂既然没达到原目标那就把实际去到的最终位置当作目标。但这也暴露了它的问题——如果一条轨迹后半段完全走偏final 状态和过程状态之间的差异太大改写出来的轨迹不一定通顺。比如机械臂前 50 步一直在向正确的杯子移动第 60 步突然被一个随机动作甩到别处拿第 60 步当目标前面 50 步全被判定为“远离目标”对学习反而不利。future 策略是目前我手上的首选。它从每一步 t 的未来状态s_{t}t ∈ (t, T]中采样作为 g这样改写出来的轨迹天然是“渐进向目标靠近”的因为每一个 suffix 都是朝着 g 走的。它相当于把原始轨迹切成了很多段不同粒度的成功演示。future 对单段轨迹内存在“远近高潮”的任务效果尤其好典型的像多阶段投掷、抓取再放置这类。episode 策略与 random 策略对环境结构的依赖更强。episode 随机采一个状态逻辑上是“这条轨迹内任意可能的目标”能覆盖目标空间中子结构的变化random 策略则依赖整个 replay buffer 的状态覆盖率如果 buffer 太小采到的状态和目标空间完全不相关启发效果就很弱。3.2 goal 的维度切分设计与表征选择HER 里有一个特别容易被忽略的坑不是所有状态维度都能拿来当目标。目标空间 g 到底取哪些维度直接影响训练效果。拿一个 2D 推箱子任务举例状态可能包含箱子坐标[x, y]、智能体坐标[x, y]、机械臂姿态角[θ1, θ2]等。HER 改写目标时通常只把与任务目标直接相关的维度箱子坐标取为 goal而不是把完整状态塞进去。如果你把机械臂角度也当 goal 维度那个空间维度太大而且角度值和目标之间的物理关联很弱会引入大量噪声梯度。我的经验是先跑一个 baseline统计一下智能体随机策略的最终状态分布尤其看一下最终状态在哪些维度上有真正的多样性。如果某个维度的值几乎总是一样的拿它做 goal 没有任何意义。比如任务是“开门”门开了之后合页位置固定合页角度这个维度就不应该进 goal 空间。此外目标表征的形式最好与环境可控量对齐。连续控制任务里直接用原始连续坐标是可行的但建议先对状态做归一化避免距离度量被大数值维度主导。离散任务里goal 用 one-hot 编码也没问题只要保证 env 里的goal_reward_fn用同样方式计算。3.3 奖励函数、done 标志与距离度量的一致性HER 改写了目标但并没有帮你自动改奖励计算方式所有实现 HER 的地方基本都要配套一个目标导向的奖励函数。这个函数最简单的写法是def goal_reward_fn(achieved_goal, desired_goal): distance np.linalg.norm(achieved_goal - desired_goal) return - (distance threshold).astype(np.float32), distance threshold注意这里有一个我踩过的坑奖励的稀疏阈值和状态空间尺度必须对数齐。如果状态坐标范围是[0, 100]你拿threshold0.01判定成功那智能体要碰到的概率基本是零如果 threshold 设大一点比如5.0又会出现“差不多就行”的浅层成功影响后续精细控制的学习。最好的方案是先跑 1000 条随机探索轨迹统计所有轨迹终点到目标的最小距离取这个距离的一个分位数比如 5%-10%作为阈值匹配环境本身的尺度。另一个隐蔽问题是done标志的处理。原始轨迹最后一步如果是“失败”done 是 False改成 HER 轨迹后因为后见目标就是最终状态最后一步一定会被标成 doneTrue。如果环境里有“一旦 done 就截断”的 set 逻辑这条改写轨迹的价值会被提前截断。我的做法是在 replay buffer 里独立保存一个is_terminal字段不让它和环境的 episode end 混淆避免 bootstrapping 目标被错误处理。4. 从零搭建一个 HER DDQN 实操流程4.1 项目环境与任务定义为了能完整演示这里用一个我常用的小例子一维连续状态空间的移动到达任务。环境定义如下状态空间智能体位置x ∈ [0, 1]目标位置g ∈ [0, 1]。动作空间a ∈ [-0.1, 0.1]的位移增量。奖励函数如果|x - g| 0.02给 1否则给 0。每个 episode 长度最多 50 步。这个环境本身非常非常简单但稀疏奖励足够让一个 vanilla DQN 完全学不到东西。如果你手头有一套更复杂的机器人任务比如 FetchReach 或者自建的机械臂环境逻辑完全通用只是状态维度和网络结构需要加宽。网络我用一个简单的三层全连接import torch import torch.nn as nn import numpy as np class QNetwork(nn.Module): def __init__(self, state_dim, goal_dim, action_dim): super().__init__() self.fc nn.Sequential( nn.Linear(state_dim goal_dim action_dim, 256), nn.ReLU(), nn.Linear(256, 256), nn.ReLU(), nn.Linear(256, 1) ) def forward(self, obs, goal, action): x torch.cat([obs, goal, action], dim-1) return self.fc(x)注意输入拼接顺序obs 和 goal 的分界必须一致不然后面做 goal 替换时维度会错位。我在这里踩过好几次坑其实只要加一行注释记录维度的 idx 就能避免。4.2 采样、HER 改写与训练主循环整个训练主循环可以分为三块采样轨迹、HER 改写入库、采样更新。这部分我用伪代码级别的 Python 展示你直接套到自己的环境里只需要改 reset 和 step 的接口。def collect_episode(env, policy, goal, max_steps50): obs env.reset(goalgoal) episode_transitions [] for t in range(max_steps): action policy.select_action(obs, goal) next_obs, reward, done, info env.step(action) episode_transitions.append({ obs: obs, action: action, reward: reward, next_obs: next_obs, done: done, goal: goal }) obs next_obs if done: break return episode_transitions def her_augment(episode, batch_size, goal_reward_fn): augmented [] goal_indices get_goal_indices() # 预先定义 goal 维度索引 for trans in episode: augmented.append(trans) # 原始轨迹不动 # future 策略从每个时刻的未来状态中采样后见目标 T len(episode) for t, trans in enumerate(episode): if t T - 1: future_idx np.random.randint(t 1, T) hindsight_goal episode[future_idx][next_obs][goal_indices] else: hindsight_goal episode[-1][next_obs][goal_indices] new_reward, new_done goal_reward_fn( trans[next_obs][goal_indices], hindsight_goal ) augmented.append({ obs: trans[obs], action: trans[action], reward: new_reward, next_obs: trans[next_obs], done: new_done, goal: hindsight_goal }) return augmented之后就是标准的 off-policy 更新流程从 replay buffer 随机采样一个 batch用目标网络计算 TD target然后更新 Q 网络。需要注意的是HER 增广之后的轨迹里obs 和 goal 是两个独立的键采样后必须先 cat 在一起再喂网络。4.3 超参数与收敛调优HER 本身对超参数不是特别敏感但有几个参数推荐用固定值起手。我的起手配置参考如下参数推荐值说明episodes per update8-16一次性收集足够多样性的轨迹HER augmented transitions每个原 transition 补 1-2 条太少增益有限太多训练变慢replay buffer size100k-1M稀疏任务建议偏大保证目标覆盖target network update period1000 steps与标准 DQN 一致learning rate1e-3Adam激进一点也没关系exploration noiseε 从 1.0 线性退火到 0.1前期保持大噪声扩大探索覆盖调参时最值得看的一张图是“回放样本中成功 transition 的比例”。如果这个比例提升说明 HER 的改写一定开始产生有效信号了。如果这个比例一直为 0那大概率是 goal 维度选错、奖励函数编码错误、或者 done 标志被错误截断。我自己实操时发现一个很反直觉的点HER 的收益在训练早期最大后期反而需要逐渐削弱增广比例。原因是后期原始目标已经能产生足够的正样本过多 HER 改写会稀释对原始真实目标的专注度。可以考虑在训练中期把 HER 增广比例从 1.0 逐渐降到 0.3 左右类似 curriculum。这个细节论文里没写算是实验出来的独家经验。5. 常见问题与排查技巧实录5.1 数据层面的坑维度错位、reward 函数不一致、buffer 污染第一个高频问题就是维度错位。在很多环境里obs 是一个 dict包含observation和achieved_goal两个字段而 goal 单独存一个字段。如果你只是np.concatenate([obs, goal])没有提前把achieved_goal从observation中剔除那可能出现输入里有两个“当前实际位置”的情况一个是 obs 里自带的一个是 goal 里放的实际位置。这种重复信息会让网络困惑尤其是 HER 改写后 goal 变了但 obs 里的 achieved_goal 没变等价于告诉网络“目标在这个位置但你当前已经在这个位置”奖励和观测完全脱节。排查时先确认 goal 维度是否已经正确地从状态空间里剥离。第二个问题是reward_fn 不一致。HER 改写的目标变了但如果你在环境 step 里用的是环境的原始 reward 函数在改写时又临时写了一个新的goal_reward_fn两者的成功阈值、距离度量、中间奖励分配必须完全一致。我就遇到过环境里用平方欧式距离做密集奖励改写时却用阈值稀疏奖励结果同一个 transition 出现两套完全矛盾的损失信号训练直接发散。最简单的方法是设置全局唯一的compute_reward(achieved_goal, desired_goal)函数在 env 和 HER 改写中用同一个入口。第三个坑是buffer 污染。如果 replay buffer 存的是(obs, action, reward, next_obs, done)而无 goal 字段HER 就玩不转。因为改写后 goal 变了必须把 goal 也作为一个键存进 buffer采样时一起拿出。很多开源实现容易搞混的点是有人复用普通 DDPG 的 transition buffer导致 reward 和 goal 对不上训练时指标“看着还行”实际上学的是完全不相关的映射关系。5.2 算法选型层面为什么 on-policy 用不好 HER有一类问题站在算法角度如果你在 PPO 或 TRPO 这类 on-policy 算法上直接套 HER大概率会踩一脚泥。原因前面说过HER 改写后的轨迹的经验分布不等于当前策略的行为分布直接影响 on-policy 算法的单调改进假设。但也有补救方案实际项目里我见过两种变体一是把 HER 仅用于采集“初始探索经验”在进入正式 on-policy 更新前先用 HER 增广过的经验做一次行为克隆式的预训练相当于 warmup二是走混合路线主算法用 off-policy 的 SAC/DDPG辅以 HER 增广。项目里如果是机器人仿真任务我建议优先选 SAC HER这与 soft actor-critic 天然适配。另外还有一个容易踩的坑连续控制任务中 HER 对探索策略的敏感度。如果用高斯噪声采样标准差太大时轨迹终点极其分散虽然能产生很多后见目标但每个目标对应的轨迹质量很差标准差太小时探索不足后见目标覆盖度低。稍微可靠的做法是前 20%-30% 的 episode 用较大的噪声或随机策略把“目标空间覆盖”这个基础打牢然后再逐渐收敛。5.3 一个快速可复现的迷你实验复盘最后分享一个我最常用的快速 sanity check 实验在 OpenAI Gym 的FrozenLake或自己写的一维到达任务上面跑 HER DQN。这类环境小一个 epoch 只需要几秒钟足够排查 90% 的代码问题。我当时的实验过程是这样的先跑 vanilla DQN300 个 episode 后成功率依然为 0然后引入 HER把每个 transition 额外用 future 策略改写一条200 个 episode 后成功率直接跳到 80% 以上。这个对比结果基本能验证你的 HER 实现是没问题的。如果在这么简单的环境里还是失败那问题几乎可以肯定是出在数据流上而不是算法本身。这时就按前面的排查顺序去查 reward 配不配、goal 剥离没剥离、done 有没有被截断。这套排查流程几乎能解决我在实际项目里遇到的 80% 以上的 HER 异常。即便是最小的 toy case也不要跳过这一步它能帮你把 HER 的整个数据流在可控的范围内验证清楚节省后面大型机器人实验的调试时间。6. HER 的进阶扩展方向与思考6.1 从 HER 到 automatic goal generationHER 的底层逻辑是“从已经发生的状态里提炼目标”顺着这个思路往后走自然延伸到一个问题既然后见目标能启发学习那能不能主动生成更有教学意义的中间目标这就是 automatic goal generation 的思路。比如 curriculum learning 里先学距离近的目标再逐步增大任务难度对应的就是 goal-space 上的课程安排。HER 改写的目标本身具有随机性分布取决于策略探索路径而目标生成方法可以有意识地挑选那些“处于能力边界上”的目标让样本难度保持在略高于当前策略水平的位置。这些方法虽然更复杂但核心都建立在 HER 打开的这个思路之上目标和轨迹不是天然确定的组合而是可以重新配对的数据增强对象。6.2 HER 与其他强化学习模块的组合另一个实操层面的扩展是模块组合。HER 不影响 reward shaping、优先经验回放、distributional Q 这些独立的机制它可以叠加在任何 off-policy 算法后面。我常用的一套组合是SAC HER 自动熵调节 优先经验回放。虽然每一样单独拿出来都是“常规操作”但组合之后性能提升有协同效应。经验回放让 HER 改写的轨迹以更高优先级被采样自动熵调节保证探索量合理整体上比直接拼凑的效果稳得多。还要注意HER 也不是银弹。在目标空间维度特别高比如图像层面的目标或者奖励函数本身已经很密集的 task 上HER 的收益可能并不明显。图像输入场景要注意使用合适的表征latent goal比如先跑 VAE 或 BYOL 去压缩 high-dimensional observation 再喂给 HER否则在原始像素维度上做目标距离度量带来的噪声会抵消 HER 带来的信号增益。6.3 我在实际项目中的使用体会从我自己的项目经验来说凡是涉及机器人控制、机械臂抓放、移动导航这类多模态连续控制任务HER 已经成为我的标准起手式而不是最后的作弊器。它最大的价值在于当你想快速验证一个新任务的可行性时HER 让你不需要在 reward engineering 上花大量精力只要能把 goal 定义清楚就能很快看到模型是否具备学习该任务的能力。这一点直接改变了工作流先跑 baseline再用 HER 确认上限最后再根据实际部署要求决定要不要优化。至于具体什么时候用 final 直接用 future 或者混合多策略我的建议是直接默认用 future只有在训练曲线出现平台期时再实验其他策略。多数情况下 future 能覆盖几乎所有场景少走弯路。最后分享一个实践技巧HER 改写后不要丢弃原轨迹一定要同时保留原始数据集。你会发现混合数据集比纯 HER 数据集更稳因为原始轨迹提供了真实的难度分布而 HER 轨迹提供的是可学习的信号分布。两者结合才是这个算法在雷区里稳步前行的真正原因。
返回列表