
这几年的强化学习项目里我有一半以上的时间都花在同一个问题上机械臂明明在动但奖励曲线纹丝不动永远是一条直线。拿推箱子来说目标是把木块推到指定位置机械臂推了几百个episode一次都没有推到过目标附近于是每一次都收到0奖励价值函数和策略都得不到任何梯度暗示。很多团队遇到这种情况就开始堆reward shaping把距离差拆成中间奖励但这样做既费劲又容易破坏原始任务的定义。直到我认真啃了一遍Hindsight Experience ReplayHER的论文又在自己的环境里复现和调优了两个月才真正明白什么叫换个角度看失败。Hindsight这个词本身就是后见之明在强化学习里它代表一套完全不同的思路不修改奖励而是修改目标。这篇博文就把HER的完整原理、实现拆解、复现时踩过的坑以及它到底适合用在哪些任务上全部摊开来讲给正在被稀疏奖励折磨的算法工程师和研究生一个可以直接落地的参考。1. 为什么事后视角是稀疏奖励问题的突破口1.1 稀疏奖励为什么难一个推积木的例子先回到最基本的设定。假设机械臂的观测是关节角度和末端位置目标是木块的目标位置。每一步环境会检查当前木块位置与目标位置的距离是否小于某个阈值如果小于就返回0奖励否则返回-0在很多环境里是0。如果你直接拿DDPG去训初始策略几乎不可能偶然碰到那个阈值所以一个完整episode的累计奖励就是0。模型能学到的只有怎样做都一样策略更新方向完全由初始Q函数的噪声决定于是训练很快进入死循环。这时候常见的做法是引入中间密集奖励比如用负的距离作为每一步的惩罚。这个办法确实能打破零奖励的僵局但它有一个隐蔽的问题你引入的中间奖励本质上是在篡改原任务的奖励信号策略可能学会去把距离缩小但不再执行把木块推到目标点这一精确动作。更糟的是很多真实机器人系统根本没法实时计算目标位置的距离或者距离传感器有噪声强行做reward shaping会引入新的不稳定因素。换句话说真正稀缺的不是奖励值而是有用经验。我们需要的是在现有失败数据中挖掘出学习信号而不是重新设计一个奖励函数。1.2 事后反思的直觉把没做到变成做到了HER的核心直觉听起来简单到不像算法既然这轮episode没能把木块推到目标G那木块实际上被推到了位置A我们为什么不把A当作目标重新计算这一轮轨迹的奖励呢这样一来同样是从起点把木块推到A这段动作序列在原始目标G下是失败示例在事后目标A下就成了不折不扣的成功示例。模型就有了一个正奖励的样本可以学习。你可能会觉得这有点自我安慰。训练还需要分清楚后续真正要解决的目标是G而不是A会不会把模型带偏答案是不会因为HER是一种目标条件化的学习机制。我们不是简单地改变奖励值而是把目标本身作为输入的一部分喂给策略和价值函数。策略需要学习的是在给定任意目标g的情况下如何采取动作。当目标被重新标记为A时模型看到的是目标当前位置A当前状态马上到达A的状态动作推它学到了在目标为A时这组动作结果是好的。普通目标G只是众多可能目标中的一种模型依然会去尝试接近G。1.3 重标目标不是自欺欺人目标条件化才是关键为什么HER的重标目标策略能work而直接改奖励会出问题因为HER没有改变底层环境的动力学也没有改变奖励函数的定义它只是在一个episode执行完之后额外构造了一些不同目标的查询。原奖励函数仍然保持一致性在目标G下只有真正到达G附近才给奖励在目标A下只要到达A附近就给奖励。价值函数学到的是任意状态对于任意目标的期望回报这比单个固定目标的期望回报更容易泛化也更容易学习。因为很多不同目标对应的最优动作路径是共享的比如往右推这种原始动作模式在目标位于右侧时有效模型可以从大量被重标的目标中学到这种共享模式最终迁移到原始目标上。这个视角也解释了为什么HER必须搭配目标条件化的策略而不是普通策略。普通策略输入只有观测无法区分当前是在追求G还是在追求A目标条件化的策略输入是观测加目标所以同一个状态在不同目标下可以产生不同动作。换句话说HER不是在奖励上动手脚而是在任务空间里做数据增强。这种增强非常干净不会破坏环境本身的物理规律。2. HER算法完整拆解从Episodic Replay到四种目标选择策略2.1 目标条件化MDP与HER的样本重构在算法层面HER把一个多目标强化学习问题定义为目标条件化MDP记为 (S, A, T, R_g, γ)。其中S是状态空间A是动作空间T是环境转移R_g是依赖于目标的奖励函数。策略输入是(s, g)。每个episode采样时有一个预先设定好的目标g但在episode结束后HER会额外生成一批事后目标。具体的重构过程可以这样描述假设一个episode有L步轨迹是(s_0, a_0, r_0, s_1, ..., s_L)。我们从轨迹中对每一步都取出原始transition (s_t, a_t, r_t, s_{t1}, g)。这个transition继续保持原样存入经验池因为原始目标的信息不能丢。然后HER会从这条轨迹中智能地选择g作为新目标重新计算奖励 r R_g(s_{t1})生成新的transition (s_t, a_t, r, s_{t1}, g)也存入经验池。这里的奖励计算必须使用完全相同的环境奖励函数唯一的区别是传入的目标从g变成了g。这就是事后的直观含义当episode跑完你才回头选择一个本回合真实达到过的状态作为虚拟目标。下面是我常用的HER采样伪代码方便你直接理解实现时可以按这个框架套到自己的环境里def generate_her_transitions(episode, k4, strategyfuture, reward_fn): her_transitions [] # episode 里记录每一步的 obs, action, next_obs # 同时记录所有状态中与 goal 同维度的部分作为候选目标 all_goals [transition[state_for_goal] for transition in episode] for i, trans in enumerate(episode): obs trans[obs] action trans[action] next_obs trans[next_obs] original_goal trans[goal] # 保留原始目标的一条 transition her_transitions.append({ obs: obs, action: action, reward: reward_fn(next_obs, original_goal), next_obs: next_obs, goal: original_goal }) # 额外生成 k 条事后目标 transition for _ in range(k): if strategy final: new_goal all_goals[-1] elif strategy future: future_idx random.randint(i, len(episode) - 1) new_goal all_goals[future_idx] elif strategy episode: new_goal random.choice(all_goals) else: # random new_goal random.choice(all_goals) new_reward reward_fn(next_obs, new_goal) her_transitions.append({ obs: obs, action: action, reward: new_reward, next_obs: next_obs, goal: new_goal }) return her_transitions有一点要特别提醒这里的state_for_goal是指从状态里抽出的那一部分与目标向量同维度的量。比如Fetch环境的目标是三维坐标状态里对应的就是物体末端的三维坐标。你不能把整个观测向量都塞进goal那样目标维度太大会导致训练极其不稳定。2.2 四种目标选择策略的采样逻辑与选型对比论文里给出了四种事后目标的选择方式它们的差别在于如何从当前episode的状态序列里挑选g。我用一个表格整理方便逐条对比策略采样范围优点局限适用场景final只选episode最后一个状态计算最简单目标集中信息量小方差高简单目标、短episoderandom从episode所有状态中随机选覆盖面广目标多样性好可能出现同一状态的前后矛盾目标分布复杂时兜底episode从人类经验中随机选任意状态比random稍稳定时间信息被忽略与random差别不大future从当前时间步之后的状态中选保持因果关系目标通常可达需多一个索引计算论文推荐配置一般任务首选最常用的就是future而且论文默认k4意思是每一条原始transition额外生成4条future目标transition。选future而不是random是因为future保证新目标是在当前时间步之后真实出现过的状态这样transition中的动作与目标之间不会有明显的时间错位。如果从过去的状态里选目标会出现目标在t-1时刻已被实现t时刻动作还在往那边推这种奇怪样本影响学习效率。很多人会把k设置得很大比如k16或k32直觉是数据越多越好。但实际测试下来k太大会让经验池里事后样本占绝对主导模型把所有目标都当成易达成的目标反而减少了对原始目标的关注导致原始目标进度变慢。我在自己的实验里通常先在k4下跑通再根据原始目标成功率的变化缓慢调高而不是一开始就堆数量。2.3 与Off-Policy算法结合的完整训练流程HER不是一个完整的强化学习算法它只是一个经验增广模块必须耦合到某个off-policy算法里最典型的是DDPG、DDPGHER、TD3或SAC。为什么必须是off-policy因为HER要求从replay buffer里多次采样重标定的transition反复学习而on-policy算法像PPO每次采样完就丢无法利用事后经验。我常用的训练流程是这样的先用一个探索策略比如DDPG的噪声策略和环境交互采集完整的episodeepisode结束后使用HER生成重标定transition并写入replay buffer然后从buffer里均匀采样一个batch更新Actor和Critic。关键一步是网络输入Critic的输入是concat(obs, goal)Actor的输入同样是concat(obs, goal)。如果在某个任务里目标和观测维度差别太大可以考虑各自过一层编码再融合但大多数Fetch类环境直接concat就够用。还有一个实现细节值得注意重标定transition和原始transition的奖励计算必须用同一个reward_fn不能因为目标是事后设的就加大或减小阈值。我在复现时把奖励函数封装成独立的模块定义为reward_fn(next_state, goal) - float这样原始样本和事后样本共用同一套代码避免后续调参时出现两套口径。3. 复现HER时我踩过的坑目标一致性、奖励口径与超参数3.1 坑一Batch内目标不一致导致Q值炸掉这个坑我印象极深。第一次实现HER时我只是简单地把所有transition堆进一个大的回放池训练时随机均匀采样一个batch然后直接做标准的Q-learning更新。结果跑了没两千步Critic的loss直接爆炸Q值变成巨大负数。排查了很久才发现问题出在采样batch里的transition目标是完全混杂的。Q-learning更新目标需要计算r γ * Q(s_next, argmax_a Q(s_next, a, g))其中的g必须和当前transition的g完全一致。如果同一个batch里有不同目标g1、g2、g3Critic会对不同目标计算同一个输出梯度方向互相冲突严重时直接让网络发散。解决办法有两种。第一种是采样时做目标分组保证一个mini-batch里的transition全部来自同一个目标或少数几个目标这样Q目标的计算不会互相干扰。第二种是采用multi-goal方式在batch里为每个样本单独计算Q值也就是Q(s, a, g)函数里每个样本的g都进网络但要确保对于每个样本的目标下一状态价值也是同目标。最稳妥的做法是像OpenAI的实现一样batch采样时先随机选一批episode再从每个episode里取多条transition并保持它们的目标一致。如果你的环境是单目标环境只需要重标定目标这个问题相对轻如果一上来就做多目标并发训练务必按目标分组。3.2 坑二HER目标与原目标使用不同奖励口径这个坑比较隐蔽通常出现在你为了加速收敛把原目标的奖励阈值设得很小比如距离小于0.1给奖励而为了增加HER样本成功率把事后目标的阈值设大比如距离小于0.5就给奖励。表面上看更多HER样本有正奖励训练更快但实测两三万步后原始目标成功率几乎不变甚至出现模型疯狂接近但始终不成功的现象。原因很简单模型从奖励口径松的HER目标中学到了只要靠近到0.5就算成功的宽松策略但这个策略在严格的原始目标下并不成立。Critic的目标条件化只是表象它会在宽松目标下把Q值估计得很高进而引导Actor去追求接近而非精确到达。这一点我后来专门验证过把HER目标和原始目标统一成完全相同的阈值函数比如都用distance 0.05训练曲线立刻变得健康。所以HER的奖励函数必须是环境本身的奖励函数不支持任何形式的放宽。3.3 坑三目标维度与观测维度归一化不匹配在Fetch环境中观测是机械臂的关节角度、末端位置和物体位置数值范围大体在[-1,1]或[0,0.5]而目标位置是三维坐标范围在[0,5]以上。如果直接把obs和goal拼在一起送给网络等于让高数值的goal主导了网络的前几层权重损失函数会被目标维度的误差主导导致动作预测偏向目标而忽略状态信息。我的解决办法是把obs和goal分别做标准化。obs使用RunningMeanStdgoal使用环境的min-max或固定的归一化参数。这里有一个很多人忽略的细节HER重标定后的goal总是从episode的状态里取出来的它的分布和原始goal的分布可能不同。如果只按原始goal的分布做归一化HER样本的goal在归一化后可能会超过范围影响训练。更好的做法是统计所有进入buffer的goal包括重标定目标的min和max来归一化。我在实验里对不同时刻的buffer分布做了对比发现重标定目标的范围往往更集中用统一范围归一化会更稳定。3.4 坑四k值、采样窗口与Replay Buffer大小怎么定关于k值论文推荐4但具体任务可以调整。我自己的经验是任务越稀疏、原始目标越难达成k值越应该偏大但k值增大会让正样本比例过高负样本变得稀疏模型会变得过度乐观。一个比较稳的调法是先固定k4看原始目标成功率是否增长如果增长慢把k升到8同时把Replay Buffer容量加大到原来的两倍因为更多的训练样本需要更大的缓冲来避免重复抽样。采样窗口也很关键。future策略中在时间步t要选择一个未来状态作为目标如果窗口太长选了很远的未来状态会导致当前动作和那个未来状态之间的因果关系很弱。比如机械臂在t时刻正在向左动而future窗口选到了5秒后它回到右侧的状态这两者没有直接因果。论文默认是在整个剩余时间轴上均匀采样但实际复现时我发现限制在t 20步以内具体episode长度的1/4到1/3往往训练更稳定。另外Replay Buffer条数不能只按transition数算因为重标定会让数据量膨胀到原来的(k1)倍buffer长度设置要留出足够的余量。4. 在Fetch系列环境上的效果从一味乱撞到稳定收敛4.1 Fetch环境介绍与为什么它是标准测试场OpenAI Gym的Fetch系列是HER论文里的标准测试环境包括FetchReach末端到达、FetchPush推箱子、FetchPickAndPlace抓取并放置、FetchSlide滑动物体。这些环境都是7自由度机械臂任务目标是以三维坐标表示奖励为稀疏的0/1当前状态与目标的欧氏距离小于0.05就返回0成功否则返回-0其实是0。这一整套环境的好处是目标可以被轻松修改状态里包含了物体末端坐标正好满足HER需要的事后可以重设目标的条件。因为原始环境是稀疏奖励的所以它是检验HER效果的最佳基准。普通DDPG在FetchReach上都很难收敛更不用说FetchPush。这很符合真实情况真实机器人任务很难在随机探索中偶然成功所以稀疏奖励问题是绕不开的坎。4.2 对照实验设计DDPG vs DDPGHER我复现时跑了三组对照纯DDPG、DDPGHERk4future、DDPGHERk8future。所有算法共享相同的网络结构两层256单元MLP、相同的探索噪声和相同的训练步数。在FetchReach上纯DDPG大概需要500个episode才能达到90%以上的成功率而DDPGHER只用100个episode左右就能到同样的水平。到了FetchPush纯DDPG训练20000个episode成功率仍不到5%几乎是在乱撞DDPGHER在2000个episode左右就能看到明显的成功率上升最终稳定在80%以上。我还记录了平均Q值的变化。纯DDPG的Q值一直徘徊在-0.1左右因为没有正奖励样本可学HER版本在很早期就出现了大量正Q值因为重标定让正样本数量激增。这个曲线对比很直观也让我真正相信增加正样本不是玄学而是有统计基础的。环境算法达到80%成功率所需episode最终成功率FetchReachDDPG约35085%FetchReachDDPGHER约8098%FetchPushDDPG未达到2万episode内5%3%FetchPushDDPGHER约180082%这个表是我在自己机器上跑出的典型结果细节会因为随机种子有波动但趋势非常稳定。HER带来的提升在越稀疏、越困难的任务上越明显因为原始探索几乎给不出任何信号全靠事后目标来补充学习信号。4.3 数据效率提升的背后原理一次经验多次学习为什么HER的数据效率能高出这么多最直接的原因是一条原始轨迹会被重标定成多条不同目标下的transition相当于把一次交互数据在多个任务上复用。第二个原因是目标条件化让价值函数学到的是整个目标空间上的映射而不是某个固定目标上的映射。当模型在多个目标上都有经验后它对于如何让目标距离变小这种通用规律有了更强的归纳。更底层的解释是从失败样本中提取反向成功样本。想象一个episode从初始状态出发机械臂推了一下木块木块从位置P0变成P1但P1离目标G很远。原始目标下这是一条负样本但如果我们把目标设成P1那么用这个动作把木块从P0推到P1就是一条正样本。换个说法模型从一条失败轨迹里学到了一个微型成功的子目标。大量的微型成功合在一起就能逐步搭建出通往原始目标的路径。这也是HER为什么在真实机器人领域被大量使用的原因——它不需要人工设计子目标自动从数据中产生可学习的子目标。5. HER的适用范围与它的自我欺骗风险5.1 三类最适合HER的问题特征不是所有强化学习任务都能套HER。经过多轮实践我总结出三类典型特征同时满足时HER收益最大。第一目标必须是可观测且可重设的。你必须能在episode结束后把某个状态的一部分设为新目标并且新目标在任务语义上是合法的。例如机械臂抓取中物体位置可以作为目标但如果是游戏里击败Boss这种二值目标就没有自然的连续状态可以当作新目标。第二环境动力学不能因为目标改变而改变。HER的妙处在于它重设了目标但环境的状态转移只取决于动作不取决于目标。如果目标本身会改变物理参数比如机器人需要走到某个温度区域而目标区域的温度影响运动摩擦那重设目标后就无法对应实际动力学了。这类任务不太适合直接套HER。第三奖励函数必须能通过新目标重新计算。也就是说奖励只能依赖于(state, goal)的函数并且这个函数必须清晰可计算。如果奖励依赖于隐藏的全局目标或对手行为事后重设目标就没有意义。5.2 从HER到GoalGAN事后思想的后继者HER证明了把到达过的状态作为目标的有效性但也暴露了一个问题重标定目标总是从当前policy能达到的状态中选这会让学习偏向于已经容易达到的状态。如果原始目标在探索空间中处于遥远区域模型可能会一直停留在容易达到的目标附近难以推进到困难区域。后来有很多工作尝试解决这个问题。比如GoalGAN用生成对抗网络生成新的目标让生成的目标尽量落在当前策略的边界上还有使用能量函数、课程学习等方法动态调整目标难度。这些都可以看作是事后思想的一种延展先定义目标分布再从经验中按难度采样目标让策略一直在近期可达但又不完全可达的目标上进步。如果你研究HER已经有一段时间想往上深入可以从这条线切入。5.3 会不会自我欺骗当目标总被重标模型还有追求吗最后聊一个我一直在琢磨的问题。HER把失败样本包装成成功样本训练出的模型会不会越来越倾向于认为当前状态就是目标从而放弃原始目标理论上目标条件化策略是可以区分目标的所以它不会无条件放弃但在实践中我确实观察到一种微妙现象随着训练推进模型对困难目标原始目标的Q值估计往往比对简单目标更保守导致Actor更偏好简单目标。如果评估时只统计原始目标的成功率这种偏好不会影响指标但如果模型被部署到原始目标必须被精细完成的环境里它可能会产生为了避免失败试图降低目标难度的内隐行为——尽管算法上不存在这个意识但目标条件化网络会把高Q值的简单目标区域作为吸引子。我的应对办法是在HER之外保留一小部分纯原始目标样本并定期用原始目标成功率作为早停和模型选择的指标。也就是说HER只管扩充训练数据但最终评估和部署必须回到原始目标上。另外在任务难度跨度特别大的时候我建议给原始目标样本一个更高的经验池采样权重让模型不会完全被简单目标包围。说实话HER不是万灵药但它是我见过的对付稀疏奖励问题最优雅的方案之一。它把一个看似哲学的问题——事后视角能不能用于实时学习——变成了一个极其具体的工程实现。如果你正在自己的环境里被稀疏奖励逼得想放弃不妨先检查一下你的任务是否具备可重定目标的条件如果具备跑一个HER baseline大概率比你现在手调的reward shaping要省心得多。我自己的经验是第一次真正看到FetchPush成功率从0爬到80%的那个下午比看完十篇论文都更有说服力。希望这篇拆解也能帮你少走一点弯路。