资讯动态

HER算法实战:用事后重标记破解强化学习稀疏奖励难题

发布时间:2026/10/1 3:57:19 来源:尧图企业网站定制
“hindsight”后见之明这个词放在人类身上通常是被吐槽的认知偏见但放到强化学习里它却是解决稀疏奖励问题的一把钥匙。我最早接触 Hindsight Experience ReplayHER后见经验回放的时候是抱着“这名字有点哲学”的预期去看的结果发现它用一个极其朴素的想法把大量原本毫无价值的失败轨迹变成了可训练的正样本。这篇博客我会从问题、原理、代码落地、实验效果到避坑把 HER 完整拆一遍。如果你正在做机器人控制、游戏 AI 或者任何带有明确目标导向的强化学习任务这篇文章应该能帮你节省大量调参时间。1. 从“ hindsight”到算法HER到底解决了什么问题1.1 稀疏奖励为什么智能体越练越没长进强化学习的奖励函数大致分两类。一类是稠密奖励每一步都根据“离目标有多近”给一个连续反馈比如机械臂推球时给“球与目标点的负距离”作为奖励另一类是稀疏奖励只有最终达成目标时给 1其他时候一律给 0。稠密奖励的问题在于需要人工设计距离函数设计不好就会诱导智能体钻空子比如来回移动骗距离下降真正有用的行为反而学不到。稀疏奖励更贴近真实任务但会让智能体陷入一个极其尴尬的局面在很长一段探索周期内所有轨迹的回报都是 0。很多人会低估“全零奖励”的破坏力。强化学习依赖奖励信号去放大或抑制策略行为如果一个智能体跑了上千条轨迹却一次正奖励都没拿到策略梯度算出来要么是 0要么方差大到完全没法用。这就像给一个学生发了一套全部没有标准答案的练习题对错都不知道他做再多题也提高不了。连续动作空间里问题更严重关节力矩是连续值目标位置又是随机出现的随机策略下把球推到目标点的概率可能不足 1%。你跑一万个 episode成功一个都难而在普通算法眼里失败轨迹和随机噪声没有区别。这里要特别提醒一句偶尔“中奖”一两次也不够。强化学习需要的是足够密集、足够稳定的梯度信号孤立的一两个正样本很快就被海量零奖励淹没了。所以一个看似简单的稀疏奖励任务就能让 DQN、DDPG 这类基础算法彻底瘫掉。这也是 HER 这类方法存在的根本原因我们不是要换一套更花哨的网络结构而是要想办法让“失败轨迹”本身产生学习价值。1.2 一个具体场景机械臂推球实验我用一个经典环境来展开就是 Fetch 系列里的机械臂推球任务。一台七自由度机械臂桌面上放着一个球目标是把球推到桌面上的某个随机位置。观测包含机械臂的关节角度、球的位置以及目标位置动作是各关节的力矩指令奖励就一条如果球当前位置与目标位置的距离小于阈值给 1否则给 0。我见过很多第一次接触这个任务的人跑了几十万步日志里的成功率纹丝不动最后翻看历史奖励发现正奖励的数量是 0。你可能会困惑球确实在动机械臂也确实在往目标方向挪为什么奖励还是 0因为“距离小于阈值”是一个极苛刻的判定球哪怕离目标只剩 6 厘米判定还是 0。一个只会输出 0 的奖励函数等价于什么都没告诉机器人。换句话说这个任务不是难到学不会是根本没有任何信号告诉它“你接近了”。恰恰是这种环境最适合用来理解 HER 的价值。这个例子还有个好处它能直观看出 HER 的应用前提任务有一个明确的目标状态球的位置且目标可以从状态里剥离出来单独表示。这一点非常关键后面我讲重标记边界条件时会反复提到。如果你的任务是对话生成、推荐排序这类目标很难定义成某个状态的任务HER 这套思路需要先做目标抽象不能直接套用。1.3 失败不是白费的事后重标记的核心思想HER 最核心的洞察其实就一句话一条轨迹没到达预设目标 g但它一定到达了某个状态。比如球最终停在了 (1.2, 0.8)那么在“把球推到 (1.2, 0.8)”这个新目标下这条轨迹就是一段成功轨迹。既然这样我们就不如把这段轨迹当作“新任务下的成功样本”存进去供训练使用。这个操作在论文里叫 goal relabeling中文圈习惯叫“目标重标记”或者“事后重标记”。我拿生活场景做类比大家更容易理解。你本来想做红烧肉结果做到最后更像是炖排骨但菜是熟的、是入味的。这时候你没有完成“红烧肉”的目标却获得了“做熟一份排骨”的完整经验。如果把这个过程记录下来标记为“排骨做法”这次失败就变成了一次有效学习。HER 做的事情一模一样不改变状态转移过程只改变这条轨迹所对应的“任务描述”失败轨迹立刻从零奖励的垃圾数据变成能提供正反馈的训练数据。“让目标适应过程而不是让过程适应目标”这是 HER 给我的最大启发。传统强化学习一条路走到黑目标达不到就不给信号HER 则灵活得多先看过程实际走到了哪里再回头定义一套合理的目标让过程本身的价值被挖掘出来。这个思想后来影响了很多方向包括自动课程学习、目标生成、数据增强都是把“失败经验”当资源而不是负担。2. HER核心机制拆解经验回放里的“目标偷换”2.1 经验回放与目标条件强化学习需要先说清楚HER 不是一个独立的策略算法它是套在 off-policy 强化学习算法外头的一层“样本转换逻辑”。为什么必须是 off-policy因为 HER 要把整条轨迹的样本重算之后存进 replay buffer之后反复离线采样。如果是 on-policy 方法样本用完就丢重标记带来的收益会被大幅削弱。所以HER 的典型搭配是 DDPG、SAC、TD3 这一类算法。同时HER 要求策略是“目标条件”的也就是策略的形式是 π(a | s, g)既看当前状态也看任务目标。这也解释了为什么 Fetch 系列环境特别适合 HER在 gymnasium-robotics 里观测被组织成 Dict里面包含 observation本体状态 achieved_goal、desired_goal任务指定的目标和 reward。achieved_goal 是智能体当前实际达到的目标状态desired_goal 是这个 episode 打算达成的目标。这两个字段的分离给了重标记一个清晰的操作抓手——只要替换 desired_goal再调用环境的 compute_reward 函数重新计算奖励就够了。这里我要特别强调“目标与状态分离”的重要性。很多环境为了简化观测量会把目标向量直接拼进观测向量里奖励函数也直接读观测里那一截目标字段。这种设计对普通强化学习没问题但做 HER 时就是一个深坑你替换了 desired_goal却忘了同步替换观测里的目标字段策略看到的目标和奖励函数判断的目标根本不是同一个。这个问题我在后文还会反复提因为它真的是 HER 工程化里最高频的隐性 bug。2.2 目标重标记的精髓逻辑HER 在一条 episode 上的操作可以拆成四步。第一步用当前策略跑完一整条轨迹记录每个时刻的观测、动作、下一时刻观测、achieved_goal、desired_goal 和奖励。第二步把原始经验原封不动存一份进 replay buffer。第三步根据某种策略生成 k 个“替代目标”常见的是把轨迹最后状态当目标或者从轨迹后续时间步里随机抽状态当目标。第四步对这条轨迹的所有 transition用替代目标重新计算一次奖励再把“新目标 新奖励”的经验存进 buffer。重点说第四步。很多人误以为 HER 就是把失败轨迹最后一个奖励改成 1这是错的。HER 不是伪造奖励而是把目标换成轨迹真实到达过的状态再按照环境原本的规则重新计算奖励。比如某一步球从 (0.6, 0.3) 被推到了 (0.8, 0.5)原始目标是 (2.0, 1.5)这一步奖励是 0。但现在我们事后选了一个新目标 g(0.8, 0.5)那么“把球推到与目标重合的位置”这件事就真的发生了重算之后这一步奖励就变成 1。重标记之后整条轨迹的“奖励结构”会发生明显变化。新目标不是一上来就能达到的所以在轨迹前面大部分步骤奖励可能依然是 0但越往后尤其在轨迹末端转移后的状态越来越接近新目标正奖励就会成片出现。这意味着原本一条全零的轨迹重标记后变成了一条“从 0 逐渐逼近 1”的有梯度轨迹。Critic 网络终于有了像样的回归目标策略更新才算真正运转起来。关于替代目标的采样方式论文里总结了几种策略我实际经验也差不多final 最简单整条轨迹只用最后一个状态当新目标future 更灵活对轨迹里每个 transition都从它之后的某个时间步随机抽一个状态当新目标episode 是从整条轨迹任意位置随机抽random 是从所有已见过的状态里随机抽。效果上future 通常比 final 好episode 有时能提供更多样性但更容易引入噪音。我自己做实验时默认用的就是 future只有想调试逻辑时才切 final 看一个最简版本。2.3 重标记的边界条件与偏差问题重标记不是无限制的有严格的边界条件。第一个是目标的“可达成性”。新目标必须来自轨迹真实访问过的状态不能凭空捏造一个没有到过的状态当目标。如果新目标本身就无法达到重算出来的奖励还是全零白费力气。这也是为什么 HER 的采样来源必须是“这条轨迹内的状态”而 random 策略效果差的原因——随机抽来的状态大概率是这条轨迹从未到达的重标记后依然没有正信号。第二个边界条件奖励函数必须能针对目标单独重算。也就是说任务要能写成“给定目标 g我判断状态 s 是否达成目标”的形式。你至少要有一个 compute_reward(achieved_goal, desired_goal) 这样的函数输入两个向量输出奖励。如果任务的目标定义不清或者奖励依赖全程历史HER 就很难直接套用。这不是说不能改造而是改造成本会很大。第三个边界是偏差问题。重标记经验本质上是“乐观”的它把轨迹包装成某个虚构目标下的成功轨迹相当于凭空造出了一批“容易达成的正确样本”。这批样本太多策略就会趋向于“去那些容易通过试错达到的状态”而对真正难以到达的原生目标不够积极。这也是为什么你训练完后切回原始目标做评估成功率可能比训练日志显示的低一截。所以评估时必须严格使用原始目标绝不能拿重标记目标去汇报成绩。我见过有人拿 HER 的 buffer 里正奖励比例当指标发报告那是自欺欺人。3. 实操落地从零跑通HER3.1 环境与工具选型复现 HER不要一上来就自己写环境建议直接用标准 benchmark。gymnasium-robotics 里的 Fetch 系列——FetchReach、FetchPush、FetchSlide、FetchPickAndPlace——奖励默认就是稀疏的观测天然组织成 Dict 结构太适合 HER 了。如果只为了验证核心逻辑还可以写一个极简的一维到达任务智能体从 0 出发目标在随机位置动作为一维位移判断是否到达目标附近。这个环境不到 100 行代码却能非常清楚地对比出“普通 DDPG 训不动”和“加了 HER 能学会”的差异。算法方面我的建议是第一遍用 PyTorch 手写 DDPG HER不要直接上 stable-baselines3。原因很简单DDPG 本身逻辑少HER 的核心又只集中在那段“样本转换”代码里手写一遍才能把原理落到肌肉记忆。SB3 里的 HerReplayBuffer 封装得很厚遇到问题排查起来不透明。等你手写跑通了再去用高级封装提高效率逻辑上有底得多。工具链上Python 3.9、PyTorch、gymnasium、numpy 就足够了。3.2 数据结构与三个关键字段在 Fetch 环境里一条 transition 通常包含这几个关键字段observation本体状态 achieved_goal 拼接向量、desired_goal当前 episode 的目标、action关节力矩、reward通过 compute_reward 算出来的值、next_observation转移后的观测。其中 achieved_goal 和 desired_goal 是 HER 操作的抓手。这里有一个很实用的代码习惯把“提取 achieved_goal”“替换 desired_goal”“重算 reward”拆成独立的函数。因为 Fetch 环境的观测是 Dict你替换时往往需要操作 obs 字典里的字段如果环境把 goal 拼成一段向量你还要在拼接向量里定位 goal 的起止索引。把这些都写清楚后面 debug 会轻松很多。我自己的经验是把 obs 的维度分布先打印出来搞清楚哪一段是本体状态哪一段是目标状态再做任何拼接和替换操作。3.3 核心实现代码下面是 HER 最关键的代码骨架我刻意做了简化重点展示“目标重标记 重算奖励 写回 buffer”三件事。def add_her_episode(episode, replay_buffer, k4, strategyfuture): episode: list of dict每个元素包含 obs, action, obs_next, achieved_goal, reward, done k: 每个 episode 额外生成 k 份重标记经验 # 第一件事原始经验先进 buffer for trans in episode: replay_buffer.add(trans[obs], trans[action], trans[reward], trans[obs_next], trans[done]) # 第二件事生成 k 份重标记经验 for _ in range(k): for t, trans in enumerate(episode): if strategy final: # 用整条轨迹的最终状态当新目标 new_goal episode[-1][achieved_goal] new_reward compute_reward(trans[obs_next], new_goal) new_obs replace_goal_in_obs(trans[obs], new_goal) new_obs_next replace_goal_in_obs(trans[obs_next], new_goal) replay_buffer.add(new_obs, trans[action], new_reward, new_obs_next, trans[done]) elif strategy future: # 对该 transition从之后的未来时间步中随机抽状态当新目标 future_idx random.randint(t, len(episode) - 1) new_goal episode[future_idx][achieved_goal] new_reward compute_reward(trans[obs_next], new_goal) new_obs replace_goal_in_obs(trans[obs], new_goal) new_obs_next replace_goal_in_obs(trans[obs_next], new_goal) replay_buffer.add(new_obs, trans[action], new_reward, new_obs_next, trans[done])这个代码里有几个细节必须说明白。第一compute_reward 用的是 trans[obs_next]也就是转移后的状态对应的 achieved_goal去判断它是否达成了 new_goal这样重算才符合环境规则。第二replace_goal_in_obs 是替换观测里目标字段的函数如果环境把目标拼进观测向量这一步千万不能漏。第三原始经验与重标记经验都要进 buffer只是重标记经验额外多存了 k 份。这个 k 值越大正样本比例越高但多样性也会下降一般取 48。3.4 参数配置与训练心法下面这组参数是我在实际复现中验证过比较稳的配置针对 Fetch 系列稀疏奖励任务参数推荐值说明replay buffer 大小1e6HER 需要大 buffer 保证样本多样性HER k 值4复杂任务可到 8每个 episode 额外生成的重标记轨迹数目标采样策略future比 final 更稳比 random 信息量大折扣因子 gamma0.98任务长度不长0.950.99 都可actor / critic 学习率1e-3用 Adam 优化器batch size256太小容易让重标记样本方差变大网络结构两层 MLP256 宽过于宽深反而容易过拟合 buffer 里的虚拟目标观测/目标归一化必须把 obs 和 goal 归一化到 [-1, 1] 量级训练时的心法我觉得有三条非常管用。第一不要盯着平均奖励看要看 buffer 里正样本的比例。如果 replay buffer 中 reward1 的样本占比长期接近 0说明重标记没生效或者探索能力太差得先检查代码。第二训练曲线不会平滑上升中间会有很长的平台期但只要测试成功率在涨就可以继续跑。第三评估时必须切回原始目标让环境随机生成新的 desired_goal跑完整 episode 统计成功率这个数字才是你真实的成绩。4. 效果对比与影响范围分析4.1 稀疏奖励下的实战效果HER 原论文里最震撼的对比就是在稀疏二值奖励下普通 DDPG 在 Fetch 系列任务上的成功率几乎为 0加上 HER 之后有了质的提升。具体到不同任务我自己的复现和论文公开数据大致在这个区间任务普通 DDPGDDPG HERFetchReach约 0%90% 以上FetchPush约 0%60%80%FetchSlide约 0%40%60%FetchPickAndPlace约 0%40%50%需要注意的是这些数字与你的随机种子、归一化方式、网络宽度都有关系不要迷信 benchmark 表格。我当年第一次跑通 FetchReach 时打印 buffer 里 reward1 的占比从几万步前的接近 0慢慢涨到 5% 左右训练曲线才真正开始往上涨。那一刻我才真正理解了“让失败产生数据”是什么感觉。为什么会有这么大的提升本质原因是重标记把 buffer 里的正样本比例从“零附近”抬高到了“一个可用的数字”。Critic 不再是面对一堆全是 0 的回归目标而是有了“某些状态下确实能达到某个目标”的真实反馈。换句话说HER 不改变探索策略它改变的是“一条轨迹到底能教给模型多少东西”。4.2 HER的适用边界与局限性HER 不是银弹它有很明确的适用边界。第一任务必须能定义目标而且目标必须能从观测中提取出可计算的状态特征。比如“让用户满意”这种目标你很难把它拆成一个向量HER 就完全用不了。第二奖励函数必须能针对新目标重算也就是要有独立的 compute_reward。第三任务需要有足够多样的可达状态。如果智能体受物理限制无论怎么探索最终都停在同一个角落那么重标记来重标记去都是同一个目标信息增量就很有限。HER 还有一个绕不开的“乐观偏差”。重标记样本天然偏向“容易通过试错达到的目标”这会诱导策略过度自信。我们曾经在某个机械臂仿真任务里把 k 值从 4 调到 16训练日志成功率高得吓人但切回原始目标做评估成功率比 k4 时低了不少。原因就是虚拟经验太多策略被“虚构的成功”带偏了。所以实践中 k 值不是越大越好要平衡样本量、乐观偏差和存储开销。这类方法还有一个根本性的边界它解决的是“稀疏但可达”的问题而不是“完全没有奖励”的问题。如果一个智能体根本探索不到任何有意义的未来状态重标记也无米下锅。换句话说失败必须“失败得有水平”至少失败到了一个有信息量的位置。4.3 后续扩展与行业影响HER 的影响远超出它本身。在目标条件强化学习这一支里它几乎是基础设施级的存在OpenAI 后续很多机器人操作工作都继承了这套思想。学术上后续出现了 Hindsight Goal Generation自动目标课程以及基于 HER 的示教学习、模型辅助学习等方向。它们都是在回答同一个问题如何让智能体从“没有成功”的真实过程里提炼出“局部成功”的可用知识。工程上的影响也很直接。过去我们做机器人控制要么花大力气设计距离奖励要么祈祷随机探索能撞上目标。HER 提供了一个折中思路奖励可以设计得很稀疏但通过重标记把稀疏奖励环境下的样本利用率拉上来。这个思路后来也被用到数据增强、对比学习等方向本质上都是“挖掘样本中隐含的正信息”。我从 HER 里学到的最抽象的一条经验是评价一条轨迹不一定只能用它原本被赋予的那个标准答案换一套标准同样的数据能释放出完全不同的价值。5. 常见问题与避坑指南5.1 成功率不涨先查这四件事代码写完训练起来谁都不想看到成功率一条直线。如果遇到这个情况我会建议按下面的清单逐个排查比盲调超参快得多。排查项检查方法常见原因buffer 里正样本比例打印 replay buffer 中 reward 非零样本占比原始经验占比太低或重标记逻辑没生效目标字段替换完整性检查 replace_goal_in_obs 是否覆盖所有路径目标拼在观测向量里替换时漏了维度评估口径评估时是否用随机生成原始目标错误地用虚拟目标或训练目标去评估归一化打印 obs 和 goal 的量级目标向量尺度与观测尺度差悬殊网络直接崩我见过最典型的情况是训练日志里 critic loss 在下降但测试成功率永远是 0。这时候几乎可以断定是评估口径出了问题或者 buffer 里根本没有足够多的“原始目标成功样本”。HER 经验再多也替代不了真实原始目标下的正样本两者必须都要有。5.2 高频问题实录与解决方案Q1训练了很久成功率还是 0但代码看起来没问题怎么办先跑一个最简单的 FetchReach 任务它是 Fetch 系列里最温和的目标到达任务。如果连这个都跑不动说明问题在基础代码或归一化如果这个能跑通而复杂任务跑不通那就是探索多样性不足可以增加随机噪声或调高 epsilon。Q2HER 经验存了一大堆训练曲线反而崩了怎么回事大概率是 k 值太大虚拟经验过度主导。可以先把 k 降到 2同时把 future 采样改成 final看看曲线能否稳住。等稳定了再逐步加回 k 值。Q3final 策略为什么效果不稳定final 只用轨迹最后状态作为新目标目标多样性低一旦轨迹末端的“失败水平”差异大训练方差就很高。换成 future 策略对每个 transition 都从未来状态里采样多样性会明显改善。Q4离散动作空间能不能用 HER可以。HER 不依赖动作空间的连续性只要基础算法是 off-policy 且策略是目标条件的DQN 加修改同样能用。不过连续控制场景下 DDPG/SAC 与 HER 的组合更常见因为连续空间下的探索匮乏问题更突出。Q5仿真里跑得很好上真实机器人效果很差怎么办这是典型的 sim2real 问题。建议配合域随机化、标定误差注入、更长的 episode 和更保守的物理参数来缩小差距。另外HER 学到的策略可能过度利用仿真里的“廉价成功路径”真实环境里要尤其注意安全限制。5.3 几条实战经验总结最后分享几条在工程化过程中积累的经验RIO 不高但真的救命。第一条不要拿训练 buffer 里的成功率当成绩评估必须切回原始目标。第二条HER 与 SAC 组合时温度系数 alpha 要调得比单用 SAC 时高一些否则虚拟目标经验会让策略变得过度贪婪训练日志很漂亮真实成绩很惨。第三条HER 和轻微的 reward shaping 并不冲突我很多工程任务里会保留一点点距离奖励比如每步给一个很小的负距离惩罚让策略在早期有微弱的梯度方向再由 HER 在后期提供“达成目标”的明确信号这样组合往往比纯稀疏奖励更稳。再补充一条心态上的建议遇到平台期不要急着加模型复杂度。HER 的效果上限受“未来状态多样性”的制约你先看看探索阶段是不是太单调机械臂是不是老是往同一个方向推球。如果探索多样性不够先调噪声、增大动作扰动、甚至加入随机重置都比换网络结构更有性价比。我自己第一次完整复现 HER 的时候连续跑了几天成功率纹丝不动最后定位到的问题是观测向量里的目标字段没同步替换。修好的那一瞬间训练曲线像换了个人三个小时内成功率从 0 干到了 80%。那个经历给我的启发一直沿用到现在算法不收敛的时候不要急着换模型先问自己一个问题——这条失败经验系统真的榨出它该有的信息了吗HER 没有复杂的数学但它彻底改变了我的工程习惯。现在我设计任何一个目标导向的强化学习任务第一件事都是检查目标定义、奖励可重算性以及“失败经验能不能被重新理解”。就冲这一点它就值得每个做强化学习的人认真上手一遍。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑