ARTICLE DETAIL

资讯详情

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

稀疏奖励难题破解:HER后见之明经验回放原理与实战

稀疏奖励难题破解:HER后见之明经验回放原理与实战 我是在一次机械臂抓取任务里第一次被 hindsight 这个词击中要穴的。模型跑了一周reward 始终停在 -1动作没有任何起色后来把整条轨迹换个目标去重放训练像是被打通了任督二脉。hindsight通常指 Hindsight Experience Replay后见之明经验回放专门解决强化学习里最折磨人的稀疏奖励问题。这篇文章会把这个技巧的原理、目标重标注的四种策略、适合与不适合的场景以及一份可以直接改的代码拆开讲清楚。正在跟稀疏奖励死磕的 RL 学习者或者做机器人抓取、导航、策略优化的工程朋友应该都能用得上。1. 稀疏奖励问题与 hindsight 的解决逻辑1.1 为什么“奖励一直是 -1”会彻底锁死训练强化学习本质上靠奖励信号来引导策略更新一旦奖励特别稀疏整个学习循环就会陷入一种很尴尬的状态。以机械臂抓取为例目标是把积木放到桌面上某个标记点只有积木落在标记点周围几厘米范围内才给 0其他所有时刻都是 -1。随机策略下这个概率低到可以忽略模型几乎永远只能拿到最差奖励。这会让 critic 网络的梯度近于无效。原因也不难理解所有动作对应的 Q 值都差不多低优势函数分不清谁更好策略更新变成随机游走。更麻烦的是稀疏奖励会让探索彻底失效agent 花了大量时间在环境中盲目动作得到的却是一堆毫无区分度的样本。就像一个学生只拿到一张全错、全卷只有零分的考卷无论怎么复盘都找不出哪道题“错的更轻”。这种环境下模型不是“学得慢”而是“根本没在学”。我在好几个任务是深有体会Q 值曲线一直抖动不降动作乱转连调试者都会产生幻觉怀疑是网络结构、学习率、噪声强度出了问题其实是奖励信号根本没有信息量。很多人这时候就会去调奖励函数想方设法加密集奖励但这条路同样充满风险。1.2 一句话理解 hindsight失败里藏着“另一种成功”后见之明的核心洞察特别简单用一句话说就是目标并不是唯一的你在过程中实际到达的状态也可以被当成目标重新理解。想象一下机械臂任务模型本来想把积木放到 A 点结果放到了 B 点。按稀疏奖励的标准这是一次彻底的失败因为距离目标 A 太远reward -1整条轨迹几乎没有任何学习价值。但 hindsight 的思路是既然已经在 B 点我们何不把这次轨迹标注成“成功把积木放到了 B 点”这样原本失败的轨迹瞬间产生了一个稀疏但真实的正反馈信号。这条经验就能告诉策略“当目标是 B 点时你刚才那串动作是对的”。虽然我们最终希望 agent 学会放到 A 点但从“如何精确到达一个指定位置”这个共性能力来看这条经验并非没有价值。它没有改变环境本身只是改写了 replay buffer 里样本的 goal 字段和 reward 字段。这种做法的哲学味道很强不要只按“预设目标”去评判一条轨迹而要按“实际结果”去发现其中的成功片段。你考试成绩差但如果把目标改成“尽力做错的那几道经典题型”复习价值就出来了。放到强化学习里这等于给稀疏奖励环境注入了一剂低成本、无手工设计的密集信号。1.3 和奖励塑形相比它到底省心在哪很多人会问为什么不用更常规的奖励塑形给 agent 加一个距离惩罚、方向引导等密集奖励不也能解决问题吗确实能但代价很大。奖励塑形需要很强的领域知识。为了让机械臂平稳移动你得设计出来“手和目标的距离越近 reward 越高”这类表达式然后小心处理单位、阈值、平滑项。调参过程非常痛苦稍有不慎就会诱导出投机行为。经典案例是agent 发现只要原地转圈就能让距离传感器短暂接近目标从而获得更高的即时奖励于是策略退化成一个诡异的抖动机。这种奖励 hack 在机器人任务里特别常见改一次奖励函数可能带来新的隐患。HER 不需要设计奖励。它保留原有稀疏奖励函数只是从replay buffer里的既有经验出发重新指定一部分样本的目标。这个过程中没有新增任何环境信息也没有修改任务定义相当于把“事后诸葛亮”做成了标准训练环节。原论文的结果也很有说服力在 Fetch 系列机械臂任务中普通 DDPG 的成功率极低叠加 HER 后在大部分任务里能获得显著提升。它不是用一种更好的奖励来替代环境奖励而是让我们手头那些被判“死刑”的样本重新产生学习价值这个思路比手工设计奖励要稳健得多。2. 目标重标注机制拆解HER 的四种采样策略怎么选2.1 一条轨迹变成多条经验这是核心操作HER 的工程实现有一个关键动作目标重标注。理解了这个基本就理解了算法主体。通常我们把一个转移样本写成(s_t, a_t, r_t, s_{t1}, done, g)其中g是这条轨迹执行时使用的目标r_t由g决定。在稀疏奖励下绝大多数r_t都是最小值。HER 做的事很简单额外选取一个目标g基于s_{t1}或者轨迹中的其他状态重新计算奖励然后生成一条新的转移样本(s_t, a_t, r_t, s_{t1}, done, g)也塞进回放池。关键在于g的选取方式。因为新目标来自于真实轨迹中已经到达过的状态所以它天然是可达的agent 至少在这条轨迹结尾是“成功”过的。相比那些遥不可及的预设目标重标注后的目标拥有更密集的正反馈。这正好缓解了稀疏奖励导致的“全部样本都是失败样本”的困境。实际操作时一个 episode 结束之后我们会先保留所有原始样本然后额外生成k份重标注版本。每一份版本都沿用原轨迹的动作序列只替换 goal 和 reward。这样原本一条轨迹就可能变成k1条有效经验。训练数据量变大而且富含“目标可达”的正面样本Q 函数更容易拟合出有区分度的值。2.2 final / future / episode / random 四选一怎么定重标注的目标从哪里来直接影响训练效果。原论文给出了四种策略实际操作下来差异非常明显。final 策略最简单把一条轨迹最后一步的状态设成整条轨迹的新目标。这种方法适合目标就是“终点位置”的任务它给整条轨迹一个明确且保守的回报你最后落在哪目标就是哪。缺点是当轨迹特别长、中间经历了很多阶段时新目标离大多数早期样本太远中间过程依然得不到有效密集信号。future 策略是原论文比较推荐的方向对每个转移样本从同一条 episode 的后续时间步里随机抽一个状态作为新目标。注意是“后续时间步”因为这样新目标在时间上是未来可达的更符合因果。这样每个步骤都可能收获一个“不过分遥远”的目标既不是终点那么难也不是起点那么蠢训练信号分布更合理。episode 策略是从整条轨迹里随机抽一个状态当目标不限定前后关系。random 策略则是从整个 replay buffer 里随机抽。两者实现简单但容易产生很多“过于容易”或“过于困难”的目标导致重标注样本质量不稳定。我在实际任务里试过这两种策略在稳定的室内导航任务里尚可但在需要精确末端控制的机械臂任务上效果明显不如 future。四种策略的关键对比可以看下面这张表采样策略目标来源适用任务特征实际稳定性注意事项final整条轨迹最后状态目标明确、终点单一高但信号偏粗中间步骤学不充分future当前步之后随机状态机械臂、导航、多阶段最高公认最稳需要限制采样窗口episode整条轨迹随机状态简单任务中等目标难度波动大random整个 buffer 随机状态目标空间与状态空间接近低容易把模型搞乱以我自己跑过的机械臂实验为例future 策略配合k4时成功率曲线最平滑原始样本和重标注样本之间的奖励方差更小critic 更容易收敛。如果你不想做实验直接从 future 开始是合理选择。2.3 边界条件哪些任务用 HER 会适得其反HER 不是万能药有几个典型场景使用时要特别谨慎。第一种情况目标与状态没有直接对应关系。HER 的核心是从状态中提取新目标。如果任务的目标是“把红球放到蓝球左边”新目标应该是最终状态里的位置那没问题但如果目标是一个抽象语义标签比如“让用户满意”状态里根本提取不出一个明确的可重定义目标重标注就会失去意义。第二种情况奖励函数与目标维度本身无关。HER 要求重写 goal 后能重新计算 reward。如果 reward 不仅取决于目标还跟其他隐藏条件强相关那光替换 goal 字段往往不够重标注样本的 reward 会失真。第三种情况任务是严格多阶段且阶段之间强耦合。比如“先把障碍物推开再把目标方块推到终点”如果只用单目标重标注agent 很容易把“推障碍物”和“推方块”混为一谈学到次优行为。这类任务更适合分层强化学习或者课程学习强行套 HER 会得到一个看似忙碌但完不成真正目标的策略。另外如果环境交互成本极高比如真实机器人每次 episode 都消耗大量时间HER 在样本效率上的收益可能被环境重置成本抵消。不过这是工程取舍问题不代表算法本身不行。3. 亲手落地一个 HER核心代码与参数参考3.1 最小改动接入 DDPG需要改哪几个点先把结论说清楚HER 不需要改动 actor 网络结构、critic 网络结构、目标网络更新逻辑。真正要动的只有四个地方。第一观察空间要拼接目标向量。常规 DDPG 输入是当前状态obs加了 HER 之后actor 和 critic 的输入要变成[obs, goal]。在 gym 风格的 GoalEnv 里这个 goal 就是desired_goal。第二replay buffer 里每一条样本都要额外保存对应的 goal。第三训练循环中除了往 buffer 塞原始转移样本还要额外调用重标注函数生成k份改造后的样本。第四计算 reward 时不能用环境返回的固定值要允许在重标注阶段根据新目标重新计算。只要把这几个点改完DDPG、TD3、SAC 都能挂载 HER。我自己更推荐先拿 DDPG 跑通因为修改量小方便排查问题。等算法跑顺了再换到 TD3 或者 SAC 提升上限。3.2 训练循环里的关键实现与代码下面这段代码是简化的 HER 核心逻辑去掉了很多工程细节重点展示“重标注”这个动作本身。import numpy as np import random def compute_reward(achieved_goal, goal, threshold0.05): 稀疏奖励只要到达目标附近就算成功。 if np.linalg.norm(np.asarray(achieved_goal) - np.asarray(goal)) threshold: return 0.0 return -1.0 def future_goal_for_step(episode, t): future 策略从当前步 t 之后的状态里随机挑一个 achieved_goal 作为新目标。 注意范围是从 t1 到 episode 末尾。 if t 1 len(episode): return episode[-1][achieved_goal] idx random.randint(t 1, len(episode) - 1) return episode[idx][achieved_goal] def her_transform(episode, k4): 输入一条完整 episode元素为 dict至少包含 obs, action, reward, next_obs, done, goal, achieved_goal 返回原始样本 重标注样本的列表可以直接塞入 replay buffer。 new_samples [] # 原始样本原样保留 for t in episode: new_samples.append(t) # 额外生成 k 份重标注样本 for _ in range(k): # 这里以 future 策略为例策略可替换 for t_idx, transition in enumerate(episode): new_goal future_goal_for_step(episode, t_idx) # 用新目标重新计算奖励 new_reward compute_reward( transition[next_obs][achieved_goal], new_goal ) new_sample dict(transition) new_sample[goal] new_goal new_sample[reward] new_reward new_samples.append(new_sample) return new_samples这个函数看起来不复杂但有几个细节很关键。第一个细节是future_goal_for_step的范围。它必须从t1开始选而不是从0开始。原因很简单如果从之前的时间步选目标agent 可能需要在“时间旅行”之后才能达到这类样本违背因果会让 critic 学到错误的时序关系训练就容易飘。第二个细节是重标注的次数k。它控制着每一条真实经验对应多少条伪经验。k太大会让目标多样性下降太小则增密效果不明显。原论文的实验比较支持k4起步k8也常见。我通常从k4开始跑如果模型学得慢再提到k8。第三个细节是achieved_goal必须从next_obs中提取。因为我们要判断的是“执行完当前动作后到底到了哪个位置”。如果误用obs里的状态评估会错位这个错误比较隐蔽代码里很容易顺手写错。3.3 一组可直接上手的参数配置跑 HER 时不同任务的敏感点不完全一样但下面这组参数在多种任务上的表现都比较稳可以作为起点。参数建议值说明重标注次数 k4 ~ 8推荐先 4稳定后加 8采样策略future大部分任务的最稳选择replay buffer 大小1e6越大越能保存多样目标batch size256太小会导致 Q 值过拟合重标注样本actor 学习率1e-3可按算法微调critic 学习率1e-3与 actor 保持一致便于调试目标网络软更新 tau0.05常用区间 0.01 ~ 0.1探索噪声0.1 ~ 0.3训练前期大后期衰减注意一个容易被忽视的比例问题当k4时重标注样本在 buffer 里的占比大概是4/(41) 80%k8时接近 89%。重标注样本占比过高虽然提供了大量正反馈但也可能让 critic 过度拟合于“改动后的目标”而忽略了真实目标的分布。我在实验中发现如果模型出现 Q 值异常偏高、真实目标下成功率上不去可以考虑降低k或者在采样时额外保证一部分原始样本被抽到。另外HER 更适合 off-policy 算法。原因在于重标注后的样本来自历史策略对于 on-policy 算法来说这批样本分布已经过时直接用于更新会造成偏差。PPO 这类算法如果要上 HER通常需要额外设计重要性修正或者异构样本混合工程复杂度会上升。所以我一般默认搭配 DDPG、TD3 或 SAC 使用。4. 运行 HER 时的翻车现场与调试心得4.1 重标注样本比例失衡Q 值开始漂移怎么办很多第一次跑 HER 的朋友会遇到同一个现象训练初期成功率涨得不错跑着跑着 critic loss 开始异常变小Q 值估算却和真实回合结果对不上最后策略突然崩溃。问题大概率出在重标注样本的比例上。重标注后的样本毕竟不是环境真实反馈虽然它把目标改成“实际到达的位置”但奖励分布过于集中。如果buffer里 80% 以上的样本都是这种“自己给自己打分”的成功样本critic 会慢慢丢掉对失败样本的感知能力对真实目标的评估自然失真。排查方法比较直接。记录每次采样中重标注样本和原始样本的比例再看重标注样本的 reward 分布。如果发现重标注样本里成功样本占比超过 90%而且原始样本占比被压得极低就要干预。我当时采取的办法是把k从 8 降到 4同时把采样权重设置为原始样本占 30% 以上强制 worker 每个 batch 里混入更多真实反馈。效果立竿见影Q 值估计恢复了正常范围。还有一个小建议replay buffer 里保存重标注样本时可以在字段里加一个“来源标记”比如herTrue这样后续做数据分析时能快速过滤。否则当训练跑到几百万步之后你根本分不清某条样本到底是环境真实反馈还是后见之明生成的。4.2 agent 直接“躺平”新目标太容易也麻烦另一个经典翻车场景是HER 上线后agent 很快就学会了“不动”。现象是训练 reward 很高但真实环境下测试成功率几乎没有。原因要从目标分布上找。future 策略如果采样范围没有限制模型会频繁把轨迹早期状态设成新目标。agent 一旦发现“只要站在原地不动”就能轻松达成这些近在咫尺的目标它就会无意识地选择这种行为模式。批评者会得到大量“容易目标成功”的样本于是把 Q 函数拟合得过于乐观。解决思路有几个。第一个最简单把 future 的采样窗口缩小比如只从t 10到t 50这样一定范围内取目标避免早期状态被反复当成目标。第二个思路是改用 final 策略虽然信号粗糙但目标始终是轨迹的终点状态至少不会“躺赢”。第三个思路是给不同来源的重标注样本设定优先级比如让来自后半段轨迹的目标有更高采样权重让 agent 更多地学习“多走几步才能成功”的经验。这类问题的本质不是 HER 无效而是目标分布太“仁慈”。需要人为控制难度让 agent 面对的目标有一定挑战性这样才能逼出真正可泛化的策略。4.3 什么情况下 HER 最稳以及我的使用习惯如果总结一下HER 最适合的任务具备几个共同点目标是明确的位置或状态状态本身可以从观测中提取奖励是二元的环境交互成本不高任务里没有强多阶段耦合。在这些条件下HER 几乎不会引入太多额外调参负担直接使用就能看到明显收益。我个人现在的工作习惯是遇到稀疏奖励问题先不急着设计复杂奖励函数而是先把环境封装成 GoalEnv 风格用 HERDDPG 跑一版 baseline。如果 baseline 能学到基本行为再进一步上 TD3 或 SAC然后用课程学习逐步增加任务难度。这样做的最大好处是让我能快速区分“算法问题”和“奖励设计问题”而不是混在一团里瞎调。还有个很实用的观察小技巧每过一段时间把 replay buffer 里的重标注目标和真实目标做一次可视化对比。你很快会发现真实目标往往聚在一个固定区域而重标注目标覆盖了 agent 实际探索过的整个状态空间。这个差异既代表了 HER 带来的探索红利也提醒你要关注覆盖率是否合理。如果重标注目标太集中说明 agent 的运动范围太窄此时加大探索噪声或调整 HER 采样策略比单纯增加训练步数更有效。最后再分享一点感受HER 让我重新理解了“经验”的价值。一条轨迹在预设目标下是彻底失败的换个目标可能就是完整的成功示范。做 RL 调参这些年我最大的体会是奖励函数并不总是需要越复杂越好很多时候我们缺的不是更聪明的环境而是换一个角度看待已有样本的视角。如果在你的项目里也遇到了“无论怎么调奖励都学不动”的瓶颈不妨把 hindsight 这个思路加进去用一个最小的 off-policy 模型跑通再决定下一步怎么优化。很多看似无解的任务在“事后重新定义目标”之后都会露出新的突破口。
返回列表