强化学习中的稀疏奖励难题:HER如何让智能体从失败中学习

发布时间:2026/10/2 22:31:43
强化学习中的稀疏奖励难题:HER如何让智能体从失败中学习 hindsight这个词直译是“后见之明”说人话就是事情做完之后回头看才意识到当时其实应该怎么做。我第一次见到它出现在强化学习领域是OpenAI那篇Hindsight Experience Replay论文里——这篇文章解决的问题非常朴实却很致命稀疏奖励任务中学不动的智能体以及明明探索了半天却什么梯度信号都没留下的失败轨迹。它的核心操作也简单得不像学术成果既然你没达成我给你的目标那我干脆把“你实际到达的地方”重新当成目标让这条轨迹变成一段经验有效的数据喂给算法。HER不改变奖励函数不增加额外监督不设计课程难度就是从“事后”这个角度给经验重放加了一次重新解释。对做机器人控制、目标导向型任务、以及研究样本效率的人来说HER几乎是绕不开的基础算法对只想理解强化学习核心思想的人来说它也是理解“从失败中学习”如何落地的极佳切片。1. 稀疏奖励难题智能体为什么学不动1.1 稀疏奖励到底是什么大多数强化学习算法靠奖励信号来学习。像cartpole这种经典测试环境每个时刻都有反馈智能体很容易判断“这个动作让杆子更稳还是更歪”。但现实任务里奖励往往不是这种连续的、聪明的信号而是“要么0要么1”的硬性判定。比如机械臂要把一个方块推到桌上某个位置整个过程里如果没有到达目标位置奖励就是-1到了才是0。一整条轨迹跑下来全是-1这还算好的更狠的是某些导航任务中途完全没有任何奖励只有终点亮那一下。稀疏奖励带来的问题不是“学习慢”而是“根本学不动”。策略梯度算法或者Q学习更新依赖误差信号。如果随机探索一整条轨迹都拿不到任何正奖励误差全等于零网络参数几乎纹丝不动。打个比方你让一个人在黑房间里找一盏开关但这个开关只有在他站到特定地砖上才会亮。他瞎转了半天灯从来没亮过那他就连“往哪个方向多走一步”这种判断都做不出来。时间久了他只会原地打转——这就是稀疏奖励场景下智能体的真实状态。1.2 典型任务长什么样容易被稀疏奖励卡住的任务其实很常见我列几个典型的机械臂抓取和推动Fetch类环境只有把物体推到目标位置才给奖励推歪了啥都没有。这类任务动作空间是连续的随机探索碰巧推准的概率极低。二进制翻牌bit-flip目标是一个十几二十维的01向量只有把所有位都翻到正确的值才给奖励错一位都不行。状态空间是离散的但维度足够高时随机凑对全部位的概率指数级下降。长程导航和迷宫任务中间没有检查点奖励只在终点出现。智能体跑了很远发现没奖励区别只在于“跑远了”和“跑更远”对学习来说都是同样无效的零信号。这些任务的共同点是必须做出一连串“恰好正确”的动作才能碰到奖励而随机策略碰到奖励的概率随序列长度指数级下降。动作空间维度越高探索越绝望。我见过不少刚上手强化学习的人在这种环境里跑十几个小时训练曲线跟心电图一样平最后怀疑是代码写错了——很多时候代码没错是算法本身就没拿到足够的学习信号。1.3 常规解法为什么不够用在HER出现之前面对稀疏奖励任务大家通常会做这些事情奖励塑形reward shaping把“离目标还有多远”装进奖励信号里比如距离每缩小一点就给一点正向激励。这个方法很依赖设计者的领域知识而且特别容易引入局部最优——智能体可能学会一个“看起在靠近”但实际永远到不了目标的动作。课程学习curriculum learning人工把任务分成从易到难的多个阶段先学简单的再学难的。问题是课程设计成本极高而且很多时候“简单版本”和“困难版本”之间的迁移并不自然。探索奖励ICM、随机网络蒸馏等通过内在好奇心让智能体多走走没去过的地方。这类方法解决的是“探索不够”的问题但没解决“探索到的轨迹有没有学习价值”的问题——在稀疏奖励环境里探索了一大圈没碰到奖励这些数据对目标函数仍然是零贡献。HER的视角和上面这些都不一样。它不是想办法让智能体更容易碰到奖励而是把奖励的定义从“固定的目标”换成“实际发生的结果”。这样就直接绕开了稀疏奖励的核心难点——既然原生奖励信号稀缺那就自己从已经发生的事实里生成新信号。2. HER的工作原理把失败轨迹改写成成功经验2.1 核心操作目标重标记HER一句话版本对每条轨迹除了把原始目标存进经验池再额外生成几个替代目标把同一条轨迹改写为几次“成功经验”存进去。具体拆开来看是这样原始目标为g智能体从初始状态出发跑完一条轨迹。假设这条轨迹并没有达成g所以每一步奖励都是-1。轨迹末尾附近实际到达的状态是某个s_T比如机械臂最终推到物体的位置。把新的目标g定义为“实际到达的状态”本身——在Fetch类环境里通常就是物体最终位置。用这个新目标g重新计算这条轨迹每一步的奖励。如果某一步到达了g附近距离小于阈值奖励就变成0或1于是这条轨迹里至少有一部分时间步变成了正样本。把改写后的(state, action, new_reward, next_state, new_goal)作为新经验放进回放缓冲池和原始轨迹的“失败经验”混在一起。训练时随机抽取算法每次更新都能同时看到失败样本和“成功样本”。这里的关键点是同一段物理过程在原始目标视角下是一文不值的失败在替代目标视角下却是实打实的成功。重标记不改变物理世界只改变看待这段经历的角度。2.2 为什么“假目标”能让模型真正进步逻辑链条是这样的策略网络会被“成功经验”推动朝能够达成某种目标的方向前进。这些替代目标并不是从天上掉下来的而是智能体自己实际走过的状态所以它们是可达的。于是模型学到的东西就变成了“如何到达更多可达状态”而不是“如何到达一个几乎不可能够到的状态”。当模型能够到达越来越多的“近处”状态之后再配合原始目标经验穿插训练它就有更大的概率接近真正的目标。这就像一个学投篮的人一开始对准三分线怎么都投不进如果只看成功经验他永远得不到反馈但如果把“投到篮筐附近”也定义成一种成功他至少能学会发力方向然后逐渐扩大命中范围最终摸到三分线。更深一步看HER相当于把每一条轨迹都变成了一个微型多目标数据集。原始目标视角下这条轨迹是负数样本替代目标视角下它是正数样本。训练时buffer里正负样本都有Q函数的估计会稳定很多。这个过程比单纯调奖励函数更灵活因为它不改变环境给奖赏的逻辑只是在训练数据层面增加了信号的密度。2.3 HER能用的前提条件HER不是万能的上手之前建议先确认以下几条任务必须是目标条件化的goal-conditioned策略网络的输入里要有目标g而且目标要能映射到状态空间或状态空间的一部分。典型做法是观测向量里同时包含agent状态和goal状态例如Fetch环境中观测是state和desired_goal拼接而成。奖励函数必须是“基于状态是否接近目标”的判定形式通常是距离阈值判断。如果你的奖励是“完成一个翻转动作”或者“得到某个抽象结果”压根无法用状态位置作为目标HER就无从下手。算法必须是off-policy的。HER的根基是经验重放需要算法能从历史buffer里反复抽取数据来更新策略。DQN、DDPG、SAC这类天然带replay buffer的算法都合适PPO、A2C这类on-policy算法直接硬配HER基本是灾难。目标维度不要太高。bit-flip到20维还能跑但如果目标是一个100维的向量重标记后要学习的“达到目标”关系也会变得更难拟合HER的效果会明显下降。3. 实操落地目标重标记策略与实现细节3.1 四种替代目标选择策略HER论文里给出了几种从轨迹中选择替代目标的方法实践中用得最多的是下面四种我用一个表格快速对比策略采样范围核心优点主要缺点推荐程度final只取轨迹最后一个状态实现最简单计算量最小只提供终点的目标信息中间过程被浪费一般适合快速验证future当前时间步k之后随机取一个状态能给时间上靠前的状态提供丰富的后继目标Q函数信号更连续实现稍微复杂一点强烈推荐论文默认选项episode从整条轨迹任意位置取一个状态全局视角不依赖时间顺序会让“过去的状态”和“未来的达成”关联有时逻辑上不够自然看情况用random从环境中随机采一个状态理论上覆盖最广采到的状态大概率不可达学习信号失真基本不用实战效果差final策略适合先跑通代码逻辑再考虑效果真正调模型的时候我默认都是future策略。原因是它保留了时间上的因果性——轨迹执行到第k步后面发生的状态是“从这个位置出发可以到达的”这种数据对策略学习最友好。3.2 关键实现逻辑与伪代码假设我们已经有了一个能输出的轨迹每条经验包含状态、动作、奖励、下一状态、目标。HER的核心逻辑在往replay buffer里写数据这一层。下面这个伪代码可以直接照着改def add_her_transitions(episode, replay_buffer, her_replay_num, threshold): # episode是完整的轨迹数据 # 每条transition: (state, action, reward, next_state, achieved_goal, desired_goal) for t, transition in enumerate(episode): state, action, reward, next_state, achieved_goal transition # 原始目标下的经验必须保留 replay_buffer.add(state, action, reward, next_state, transition.desired_goal) # 生成her_replay_num条替代目标经验 for _ in range(her_replay_num): # future策略从当前时间步之后随机选一个状态作为替代目标 future_idx np.random.randint(t, len(episode)) new_goal episode[future_idx].achieved_goal # 用新的目标重新计算这条transition的奖励 new_reward compute_reward(achieved_goal, new_goal, threshold) # 0或-1 replay_buffer.add(state, action, new_reward, next_state, new_goal)compute_reward是环境里给的判定函数通常长这样def compute_reward(achieved_goal, desired_goal, threshold): dist np.linalg.norm(achieved_goal - desired_goal) return 0.0 if dist threshold else -1.0这里有个非常容易写错的地方用新目标重新计算奖励时一定要用transition里记录的achieved_goal当前实际到达位置去和新目标做距离判断而不是拿desired_goal去算。如果弄混了生成的经验目标和新奖励对不上号整个算法就废了。3.3 超参数推荐与训练配置我跑HER实验时常用的配置如下你可以作为起点然后根据任务调整参数名称推荐值说明her_replay_numK值4每条轨迹额外生成的替代目标经验数量太多会增加方差replay buffer大小100万条transition环境复杂时越大越好batch size256混合原始经验和HER经验采样策略更新频率每个episode结束时更新若干次要和环境交互频率配合探索噪声epsilon或高斯噪声初始大一点HER本身不负责探索还是需要噪声撑场阈值根据环境调整一般0.05判定“到达目标”的距离retrain的时候K值不是越大越好。我见过有人把K设到16结果训练后期策略很怪总往一些奇怪的中间状态跑。K4是论文里常用的值也是实测中比较稳的。buffer大小和batch size按你显卡内存和状态维度来但这些是我做实验的基准。4. 配合算法选型与调参经验4.1 为什么HER必须搭配off-policy算法这一点值得多说两句因为它涉及对强化学习基础的理解。HER做的事情本质上是对历史轨迹做二次加工。加工完的数据需要被算法反复利用。DQN、DDPG、SAC这套体系天然维护一个很大的experience replay buffer训练可以随时从buffer里抽样所以HER经验能很自然地混进去。on-policy算法的情况完全相反。PPO/A2C每次更新用的是当前策略采样的那批数据要求数据分布和当前策略尽量一致。如果往PPO的数据里混入大量经过目标重标记的旧轨迹策略更新方向会被这些“伪造目标”的经验带着走而数据里的策略分布却不是当前分布——这会直接破坏on-policy算法的理论基础效果大概率不升反降。所以看到有人问“HER能不能配PPO”答案通常是不建议。如果你真的需要在on-policy框架下做her本质是把问题转化成一个importance sampling问题复杂度和收益不成正比。4.2 常用算法组合与效果对比我自己实际跑下来最常用的组合是DDPGHER和SACHER。DDPGHER是论文原配在MuJoCo的Fetch环境上跑起来效果非常稳定。SACHER的探索性更好因为SAC有熵正则配合稀疏奖励能多多少少缓解探索不足的问题不过SAC的超参数比DDPG多调起来更费劲。离散环境我一般用DQNHER。比如bit-flip任务状态是比特向量目标是另一个比特向量动作就是翻转某个位。DQNHER在这个任务上能见识到从“完全学不会”到“几十步内解决”的过程而且代码比DDPG简单很多非常适合做HER的入门实验。下面这个是我在几个常见测试环境上跑的印象总结算法组合适用环境类型效果调参复杂度DQN HER离散状态、离散动作表现扎实低DDPG HER连续状态、连续动作论文标配稳定性好中SAC HER连续状态、连续动作探索更强但熵系数敏感高4.3 如何判断HER是否真的起了作用初学者容易犯一个错误看到训练loss下降就觉得算法练好了。在HER里这不一定对。因为replay buffer里混了大量替代目标经验Q值在替代目标上的预测会很快变小但真实目标下的表现不一定变好。我一般开两个监视指标真实目标成功率隔若干个环境交互周期把当前策略跑几条真实目标的评估轨迹看有多少成功的。这是唯一靠谱的“实力指标”。buffer里正样本比例正常情况下加入HER后buffer里被标记为0奖励的正样本比例应该上升说明重标记机制在产生有效学习信号。如果这个比例极低说明替代目标生成策略有问题或者阈值太严格。训练时tensorboard记录这两个数比盯着loss靠谱得多。如果真实目标成功率在稳步上升说明HER的数据加工方向是对的如果成功率长周期不动就得回过去检查重标记逻辑和K值。5. 踩坑实录与排查清单5.1 我实际踩过的几个坑第一个坑是用错目标来算新奖励。刚开始在FetchReach上跑先写了HER的逻辑但重标记奖励那行代码里不小心用了“原始目标”导致新经验里的goal和新奖励不匹配。跑出来的现象特别诡异——Q值在下降但真实目标成功率完全不动。排查了很久才发现transition里保存的achieved_goal和desired_goal串了。建议在保存transition时就同时存这两个量分别写清楚。第二个坑是忘了保存原始经验。为了省内存我一开始只往buffer里加替代目标经验。结果训练到后期策略确实能到很多中间状态但对原始目标的完成度很差。原因很简单——算法压根没见过几条“真正的成功经验”它只知道去接近可达状态不知道什么才是任务真正想要的。后来每条原始transition都保留同时按K值生成替代经验效果立刻正常。第三个坑是K值开太大导致震荡。这也是我观察到的比较隐性的问题。K4和K8差别不大但K16之后训练曲线明显震荡。因为每一条原始经验被复制出16条不同目标的经验后buffer里不同目标的经验分布极不均匀Q函数的估计方差被放大。我自己现在一般固定在K4问题少很多。5.2 问题排查速查表现象可能原因排查方法解决办法真实目标成功率一直为0替代目标生成逻辑没生效检查buffer里正样本占比确认compute_reward用的是achieved_goal算距离Q值波动剧烈K值过大或batch size偏小记录Q值分布减小K到2~4加大batch size策略总去某些固定状态替代目标选的太集中打印替代目标分布future策略取多个不同位置K4训练曲线和没加HER一样平奖励判定阈值过严打印距离分布放宽阈值比如从0.05调到0.1离线评估很差但训练指标很好指标选错了别用buffer里的奖励做评估独立跑真实目标评估轨迹5.3 从小任务迁移到大环境的经验如果你从来没跑过HER建议不要直接往机械臂仿真环境上冲。先在bit-flip这种离散环境上把代码跑通观察HER带来的成功率曲线变化。bit-flip环境里你能非常直观地看到“加了HER之后成功率从近乎零开始往上走”的过程而且调试非常简单一个纯Python循环就能写完整个环境。然后切换到FetchReach这种连续动作仿真环境。此时要特别注意目标向量和观测向量的拼接方式以及物理引擎的阈值设定。跑通之后再考虑更复杂的环境比如固定方块堆叠、双机械臂协同这类。迁移一个环境到另一个环境时最常改的就是阈值和动作空间范围。这两个参数直接影响compute_reward的判定也是最容易出问题的地方。我的习惯是每换一个环境先把环境自带的compute_reward打印出一组真实距离分布看看目标附近距离大概在什么量级再定阈值。我个人在实际操作中的体会是HER是一种“思路简单但是细节决定成败”的算法。它的思想本质上是把人类复盘学习的习惯翻译成了机器可执行的数据加工过程——失败的经历并没有被浪费而是换了个目标之后继续有效。每次调参时看到真实目标成功率从0开始往上爬我都觉得这个机制特别有意思它并没有让环境变得更容易只是让智能体学会从自己的每一次尝试里都拿走一点东西。最后给你留一个小技巧做HER实验时把观测里的achieved_goal和desired_goal拆开来记录分别存成两个字段。调试Q网络、分析奖励异常时这个拆分能帮你省下一大堆翻代码的时间。这个习惯我从第一次跑HER一直保留到现在每次重新捡起这类实验都靠它快速定位问题。