hindsight 这个词有意思。在日常生活里,它说的是“事后聪明”——事情都结束了才觉得“我早该想到”,一般带点贬义。但在强化学习里,这个词代表的是一个非常能打的技术:Hindsight Experience Replay,也就是事后经验回放,通常缩写成 HER。我第一次看到这个缩写,还以为是“她的”那个 she/her 的 her,后来才意识到是 hindsight 的前半截。这个技术解决的是强化学习里最让人头疼的问题之一:稀疏奖励。很多任务里,机器人做完一整条轨迹,得到的奖励全是零,只有最后一刻才知道成没成。这种环境让算法几乎学不到东西,而 HER 用一种“事后重新解读失败”的思路把这个困境绕过去了。
这篇文章写给正在做强化学习、跑机器人仿真、或者用 SAC/DDPG 这类 off-policy 算法但苦于奖励设计的同学。我会从 HER 的动机、核心设计、代码实现、踩坑经验一路讲下去,尽量把每一步的“为什么”也说清楚。不管你是刚入门 RL 的新手,还是已经跑过几个环境的老手,这篇文章里应该都有你能直接拿走的东西。
1. 为什么一个“事后聪明”能成为强化学习的突破口
1.1 先聊一个直觉:失败里本来就有成功
想象一个场景:你让一个机械臂去抓桌子上的杯子,它伸过去,没抓稳,杯子倒了,但还是碰到了杯子。从目标的视角看,这次试验失败了,因为没有“成功抓取”。但如果把目标改写成“伸手碰触”,那这次轨迹后半段其实是完美地完成了这个子目标。
这就是 HER 最核心的直觉:一条轨迹没有实现它原本的目标,但它一定以某种方式实现了某个别的、实际达成的状态。与其把这条轨迹扔掉,不如把目标替换成“实际达到的状态”,重新标一下奖励,这条轨迹就变成了一条高质量的成功示范。算法从失败里翻出了成功经验,然后继续学,这比让它在纯零奖励的轨迹里挣扎要高效得多。
这个思路跟人类的学习方式也有点像。我们复盘一次失败的操作,经常不是单纯说“失败了重来”,而是会拆成“哪个动作其实是有效的、哪个环节出了问题”。HER 相当于把这个复盘过程自动化了:每次试验结束,自动把轨迹改写成多个版本,每个版本对应一个“事后看来确实达成的目标”,然后全部喂给算法学。
1.2 稀疏奖励到底难在哪
先看两个常见环境。假设我们要训练一个四足机器人走到某个坐标点,状态空间是一堆关节角度,动作是关节力矩。如果这个任务的奖励只在“离目标点 0.1 米以内”时给 +1,其他时候一律给 0,那么 99.99% 的探索轨迹都是全程零奖励。DDPG 这类算法靠的是时序差分误差来更新 Q 网络,而时序差分误差来自奖励的差异。全零奖励的轨迹没有任何差异性,Q 网络根本不知道该往哪个方向调。
你可能说,那给个稠密奖励不就行了?比如每一步奖励等于“当前距离减去上一步距离”,或者直接加负的距离项。这确实能解决一部分问题,但稠密奖励非常考验设计功力。距离奖励会让机械臂“朝着目标方向动”就能拿小分,于是它可能学会快速靠近然后卡在障碍物前面,因为只要减少距离就有奖励,障碍物挡住也不管。你可能还得加惩罚项去纠正这类投机行为,结果奖励工程越做越复杂,换一个环境全部又要重来。
课程学习和好奇心驱动也能缓解稀疏奖励问题,但前者需要人工设计课程顺序,后者虽然鼓励探索,却不保证探索方向能对解决任务有帮助。HER 的优雅之处在于:它不改环境、不设计课程、不引入额外探索信号,只是换一种方式利用已有的失败数据。它针对的是一个很大的任务类别——多目标任务。
1.3 什么样的任务能适用 HER
HER 不是什么环境都能用,它对任务有一个结构上的要求:存在一个可以判断的“目标”。具体来说,每个 episode 有一个期望目标(goal),智能体每步的观测里包含或者可以同时得到“当前实际达到的状态”(achieved goal),且在 episode 结束后,环境还能告诉你这个 episode 最终实际达到了什么状态。
导航、机械臂抓取、推箱子、迷宫寻路、机器人插孔、这些任务天然满足这个结构。相比之下,打游戏这种没有显式目标的场景就没法直接套 HER。你在 Pong 里想达到的“目标”是得分,但你很难定义一个“实际达到的某种 game state”作为虚拟目标,因为游戏状态千变万化,而且用一个 game state 当虚拟目标也没有意义。
2. Hindsight Experience Replay 的核心设计与直觉
2.1 一次失败试验的“重新解读”
我们具体演示一下 HER 在一条轨迹上做了什么。简化起见,假设机械臂的目标是把方块推到二维坐标 (10, 10),轨迹一共 50 步,最终方块停在 (8, 7)。
原始的采样结果是:每一步奖励都是 -1(为了鼓励尽快达成,我给未成功步都设 -1),最后一步也没有变成 0,因为没达到 (10, 10)。这条轨迹放进 replay buffer,全部都是同样的 -1,Q 网络学不出东西。
HER 处理完这条轨迹以后,会额外生成一批新经验。比如:
- 把整个轨迹的目标改成“(8, 7)”,也就是这条轨迹最终实际达到的状态。那么最后一步的 achieved goal 正好是 (8, 7),意味着任务成功,奖励给 0。倒数第二步的方块位置是 (7.9, 7.1),跟最终目标 (8, 7) 还差一点,奖励是 -1。
- 再把轨迹的目标改成“(7.5, 6.8)”,这是第 30 步方块所在的位置。于是第 30 步及之后的每一步,都因为“已经达到这个目标”而被标记为成功,奖励 0;第 30 步之前的每一步,仍然是 -1。
这样一条原本毫无学习价值的失败轨迹,被变成了两条“越走越接近目标”的有效轨迹。Q 网络在更新时,会收到这样的信号:在这个状态下做这个动作,最终能获得“达到某个状态”的结果。它开始理解动作与状态变化之间的关系,不再是无头苍蝇。
2.2 替代目标与替代奖励的计算方式
HER 的重新标记过程在数学上很简洁。
原轨迹是 \(s_0, a_0, s_1, a_1, \dots, s_{T-1}, a_{T-1}, s_T\),原始目标为 \(g\),每一步的奖励是 \(r_t = R(s_t, a_t, g)\)。因为 \(g\) 一直没达成,所以 \(r_t\) 基本全为 -1 或 0。
重标记时,先选一个新的目标 \(g'\,它必须来自这条轨迹上实际出现过的某个状态(或者未来状态),比如最后状态 \(s_T\) 或者轨迹中随机选的某个后继状态 \(s_k\)。然后对于每个时刻 \(t\),新的奖励是:
\[ r_t' = R(s_t, a_t, g') = -\mathbb{1}_{\|\phi(s_t) - g'\| > \epsilon} \]
这里的 \(\phi(s_t)\) 是“从状态里提取出来的 achieved goal”,\(\epsilon\) 是成功判定阈值。简单说:如果当前状态已经“够到了”新目标 \(g'\),奖励就是 0,否则是 -1。二值奖励让目标变得非常明确——达到就是成功,没达到就是失败。
不过这里有个容易忽略的细节:重标记的目标 \(g'\) 不能包含“未来信息”。回想一下,轨迹在时刻 \(t\) 的转移是 \((s_t, a_t, r_t, s_{t+1})\)。如果我现在把 t 时刻的转移重新标记成目标 \(g' = s_T\),那 t 时刻的策略真的“知道”未来会到 \(s_T\) 吗?答案是否定的。所以 HER 在重放时有一个约定:虽然我们把整条轨迹的目标替换成更晚时刻的状态,但后续的贝叶斯更新时,这条经验对应的策略分布其实是在“以 \(g'\) 为目标”的新设定下产生的,严格来说这会造成 off-policy 偏差。实际中大家测下来,这种偏差并没有坏处,反而极大地提高了学习效率,这也是 HER 论文的一个理论贡献点。
注意:如果你在调试 HER 时发现训练异常不稳,优先检查是不是重标记过程中某个环境把 episode 结束标志(done)处理错了。老版本一些实现里,重标记这条经验时直接复用原轨迹的 done 标志,这会导致时序目标算错。正确做法是:如果重标记后的目标是某一步已经达到的状态,那么从那一步开始后续都应该视为 done=True,因为“以这个目标来看,任务已经完成了”。
2.3 四种目标采样策略:final / future / episode / random
HER 论文主要测试了四种从轨迹中选择替代目标的策略,这几种策略影响非常大,直接决定了重标记的经验质量。
- final:只拿整条轨迹的最终状态作为替代目标,把每条轨迹额外生成一份“以最终状态为目标”的版本。
- future:对轨迹中的每个转移,从未来的某个状态里随机挑一个作目标,然后重新标记。比如在第 t 步,随机选 t+k 步的状态当目标。
- episode:和 final 类似,但更极端一点,把整条轨迹的最后一个状态作为该轨迹中所有转移的替代目标。
- random:从整个 buffer 里随机抽取一个已经出现过的目标状态作为替代目标。
论文里跑下来,future 的策略往往效果最好,也是后来大多数实现里的默认值。原因也直观:final 只给每条轨迹一个“最终目标”,生成的额外样本量少;random 选出来的目标跟当前状态可能离得太远,重标记后一堆转移都还是失败,帮助有限;future 则是在每个时间步都生成一个“未来的局面”,对每个转移都提供了一个“下一步以后会达到哪里”的目标,数据利用率高很多。
我在实际复现的时候也验证过:同样跑 FetchReach,future 策略两百万步能到接近 100% 成功率,final 策略大概在 80% 左右徘徊,再往上就慢了。当然具体差距跟任务复杂度有关,任务越难,future 的优势越明显。
2.4 为什么 HER 有效:从数据分布到学习信号
那 HER 本质上到底改变了什么?我理解是两件事。
第一,它把“稀疏奖励”变成了“相对稠密的二值奖励”。不是物理意义上的每一步都有连续奖励,而是说,很多曾经的失败轨迹会被重新解释为“阶段性成功经验”,这些成功经验里含有正的时序差分误差,可以反向传播。Q 网络不再面对一整片都是 -1 的平原,而是有了起伏和梯度。
第二,它隐式地改变了采样分布。原始 off-policy 算法里,replay buffer 里攒了一堆稀疏奖励的样本,采样时抽到的多数是没信息量的。而 HER 在采样时混入了相当比例的重标记样本,这些样本的目标分布跟真实目标分布是不同的,它们教给 Q 网络的是“状态到状态”的动力学关系。这个信息对最终的策略学习是很有用的——它让 Q 网络知道“在当前状态采取这个动作,有多大可能达到另一个状态”,然后策略网络再利用这个状态转移知识去靠近真正的目标。
有人把 HER 的机制跟“逆强化学习”类比:它其实是在从失败轨迹中隐式地推理出“哪些子目标已经实现”。不过它绕开了逆强化学习那些复杂的奖赏推断步骤,直接用了一个非常简单粗暴的重标记。
3. 从论文到代码:HER 的实现要点与实操细节
3.1 环境与算法选型:HER 该配什么算法
HER 依赖经验回放机制,所以它天然适合 off-policy 算法:DQN、DDPG、TD3、SAC 都可以。DDPG 因为早期与 HER 结合得最紧密、在 OpenAI 的 Fetch 系列实验里成名,成了默认搭档。后来的 Stable-Baselines3 里,官方就把 HER 做成了一个独立的 HerReplayBuffer,能直接跟 SAC、DDPG、TD3 搭配使用,我实际用下来 SAC+HER 或者 TD3+HER 通常比 DDPG+HER 更稳。
选算法时有几个经验:
- 如果动作空间是连续的、目标空间也是连续向量,首选 SAC+HER 或 TD3+HER。
- 如果动作是离散的、目标是离散的(比如“到达某个房间”),DQN+HER 也能工作,但要注意目标编码方式。
- 不推荐用 on-policy 算法(PPO、A2C)直接配 HER,因为 HER 重标记得来的样本不是当前策略产生的,on-policy 算法用起来会有偏差,效果不好。
3.2 一个最小可跑示例:二维点到达任务
为了演示核心流程,我自己写了个超小的实验环境:一个质点在一个 2D 平面上移动,状态是 (x, y),动作是 (dx, dy) 的小位移,目标是到达一个随机采样点。奖励:如果当前坐标离目标点小于 0.1,奖励 0;否则 -1。每步有一个小位移限制,50 步为上限。
这个环境简单到几行代码,但它完整保留了 HER 要处理的全部要素:稀疏奖励、随机目标、明确的可判定目标。在这个环境上调通了,再去跑 FetchPickAndPlace 这类复杂环境会从容很多。
import numpy as np class SimplePointEnv: def __init__(self, max_steps=50, goal_radius=0.1): self.max_steps = max_steps self.goal_radius = goal_radius self.action_dim = 2 self.obs_dim = 2 def reset(self): # 起点固定在左下角,目标是随机一个点 self.pos = np.array([0.0, 0.0]) self.goal = np.random.uniform(-1.0, 1.0, size=2) self.step_count = 0 return self._get_obs() def step(self, action): action = np.clip(action, -0.1, 0.1) self.pos = self.pos + action self.step_count += 1 dist = np.linalg.norm(self.pos - self.goal) reward = 0.0 if dist < self.goal_radius else -1.0 done = (dist < self.goal_radius) or (self.step_count >= self.max_steps) return self._get_obs(), reward, done, {} def _get_obs(self): # 观测 = 当前位置 + 当前目标 return np.concatenate([self.pos, self.goal])注意_get_obs返回的是[pos, goal],目标被直接拼进了观测里。HER 重标记时需要修改的也是这个部分:把 obs 中的 goal 字段替换成替代目标。
3.3 核心训练循环:存储、采样、重组经验
真正的 HER 实现核心在于 replay buffer,它不光存储 transition,还存了每条轨迹的完整信息,方便事后重标记。下面给一个简化但功能完整的实现思路。
先定义要存什么。每个 transition 存五样东西:观测、动作、奖励、下一观测、是否结束。但注意,这里判断“是否结束”依赖目标,所以不能只存一个 done,必须存下对应的 goal,等重标记后再重新计算 done。所以 buffer 里每个样本我存的是:
transition = { 'obs': obs, # 包含原始 goal 'action': action, 'reward': reward, # 原来按原始目标计算的奖励 'next_obs': next_obs, 'done': done, 'goal': current_goal, # 这个 transition 的原始目标 'achieved_goal': achieved_goal, # 当前实际达到的状态 }采集一条完整轨迹之后,除了把原始 transition 一条条放进 buffer,然后用 future 策略生成额外样本。比如,我从这条轨迹中随机选几个时间步,把这个时间步对应的 achieved_goal 当作替代目标,重新计算奖励和 done,拼成新的 transition 再塞进 buffer。
这里有一个非常关键的点:重标记时一定要重新计算奖励,不能只改目标不改奖励,否则这条样本就废了。奖励重新计算的规则是看“当前状态是否已经达到新目标”,也就是看achieved_goal与新目标g'的距离。
def relabel_transition(obs, action, next_obs, achieved_goal, new_goal, success_threshold=0.1): # 替换目标,注意 obs 和 next_obs 的拼接格式 new_obs = np.concatenate([obs, new_goal]) new_next_obs = np.concatenate([next_obs, new_goal]) # 判断当前状态是否已经达到了 new_goal dist = np.linalg.norm(achieved_goal - new_goal) reward = 0.0 if dist < success_threshold else -1.0 # 是否结束:达到目标或超过步数上限 done = (dist < success_threshold) return new_obs, reward, new_next_obs, done在整合进训练循环时,我的做法是:环境 rollout 完一整条 episode,先把原始样本全部放进 buffer,然后额外生成 N 个重标样本。N 一般设为 episode 长度的 1 到 2 倍。比如 episode 长度为 50,我每条轨迹额外生成 50 个重标样本,总样本量翻倍。
3.4 超参数与实现中的注意点
HER 本身超参数不多,但有几个地方值得细调。
一个是重标样本与原始样本的比例。比例太低,相当于没吃到 HER 的福利;比例太高,会让策略过度追求“达到随便哪个状态”,反而忽略了原始目标。我自己的经验是,buffer 里重标样本和原始样本数量控制在 1:1 左右比较合适。Stable-Baselines3 的 HerReplayBuffer 也有一个n_sampled_goal参数,它控制每条经验重新采几个虚拟目标,我一般设 4,相当于 1 份原始样本会额外产生 4 份重标样本,但如果 buffer 里原始轨迹数量本身就很大,这个比例可以调低。
还有一个容易踩的坑:目标归一化。如果目标空间范围很大,比如坐标从 -100 到 100,那么基于距离的奖励计算会非常不稳定。HER 的奖励是二值的,阈值不好定。这个时候需要先对目标做归一化或者缩放,不然 success_threshold 这一个参数就够你调半天。
网络结构方面,早期实现里最常见的是把 obs 和 goal 直接拼接成一个长向量输给网络。这在目标维度低的时候没问题,但目标很复杂时(比如图像目标),直接拼接会让网络很难把两个信息分离开。更先进的做法是分别编码 obs 和 goal,在 Q 网络里用某个交互层组合起来。不过对于大多数机器人控制任务,concat 就够用了,别一上来就想搞复杂的架构。
训练可视化方面,我强烈建议记录两个指标:一个是最新策略在真实目标下的平均成功率,另一个是重标样本里的“虚拟成功率”。虚拟成功率太高(比如到了 90% 以上),说明重标样本大多是从“已经成功”的 trajectory 里来的,反而不一定能帮助最终目标;虚拟成功率太低,比如长期低于 5%,说明替代目标选得太远,重标样本也学不到东西。合理区间大概在 20% 到 60% 之间。不过这个区间跟环境和阈值有关系,主要还是看相对变化。
4. 训练中的常见问题与排查技巧实录
4.1 训练不收敛或学习缓慢
这是我见到的最常见问题,而且很多人第一时间会怀疑是 HER 没用。其实十有八九是重标样本没正确进入 buffer,或者进入 buffer 的比例太低。
我排查的顺序一般是:
- 先打印一条轨迹重标记后的奖励分布,看看是否出现了非 -1 的奖励。如果整条轨迹重标后还是全是 -1,说明替代目标选得离当前状态太远,或者成功阈值设得太严。
- 检查 buffer 采样时,重标样本的占比。我用一个计数器实时统计每个 batch 里重标样本的数量,发现如果 ratio 低于 0.2,训练几乎不动。
- 检查 done 标志。很多人重标时直接沿用原始轨迹的 done,导致所有重标样本的 done 都为 False,这样一来最后一步的 Q target 永远加上了未来折现,估值偏大,学习也就偏了。
如果以上都没问题但还是很慢,可以考虑加大重标样本数。另外,如果用的是 DDPG,可以顺便检查下 exploration noise 的初始值。HER 重标后的数据分布跟策略分布有偏差,噪声太小不容易探索出新状态,我一般会把 noise 初始设大一点(0.3 左右),训练中再退火。
4.2 Q 值过估计与“虚假成功”
HER 会让 Q 网络产生一个很微妙的问题:由于重标目标可以任意选取,Q 网络可能学到“随便什么状态都能快速达到”,进而高估所有状态-动作对的 Q 值。这个问题在 DDPG 上特别明显,TD3 和 SAC 因为机制上缓解了过估计,会好很多。
我在调试的时候,会周期性打印一批随机状态下的 Q 值分布。如果所有 Q 值都集中在 +50、 +100 这种明显不合理的区间,那就是过估计了。处理方法:
- 换 TD3 或者 SAC,用 Clipped Double Q-learning 天然防止过估计。
- 降低学习率,DDPG 的 critic 学习率我一般调到 1e-4 以下。
- 适当增加重标样本的“惩罚”:在重标样本里,如果一个状态距离新目标还很远,但动作又比较大,这种样本包含的信息量低,可以考虑用重要性权重降低它在 batch 中的比重。不过这个操作在标准实现里不多见,属于我试验过的野路子,效果有限,仅供参考。
4.3 future 策略里的时间一致性 bug
future 策略要求在重标时,选的目标必须是“当前时刻之后”某个状态。很多人实现时直接用整条轨迹的所有状态随机挑,这其实变成了 random 和 future 的混合体,而且更严重的问题是会引入信息泄漏:在 t 时刻的转移,被赋予一个“过去某个时刻的状态”作为目标,这在时序上是不合逻辑的,因为 t 时刻以后的动作根本不依赖于那个过去状态。
正确的 future 实现是:遍历轨迹时,对每个时间步 t,从 t+1 到 T 之间随机选一个 k,把第 k 步的 achieved_goal 作为目标。这样重标后的样本里,动作序列的因果关系是自洽的——策略在 t 时刻做出动作,然后未来确实达到了那个状态。
这个 bug 在代码里很容易写错,因为随机选状态这个动作看起来很简单,一不小心就把整条轨迹的状态池直接拿来抽了。我当时在这个地方卡了两天,训练出来的模型在仿真里能成功,但换一个初始分布就不行了,后来定位到是重标目标里混入了过去状态。
4.4 HER 与其他算法结合的实战经验
拿 Stable-Baselines3 为例,官方实现里HerReplayBuffer是挂在DDPG、SAC、TD3之下的,你要做的只是传一个replay_buffer_class=HerReplayBuffer参数。但这不代表开箱即用,有几个参数必须认真设:
n_sampled_goal:每条原始经验额外生成几条重标目标。官方默认给的是 4,我试过 2 到 8。任务越复杂,需要的越多;但越多的重标样本也意味着越重的计算负担,8 的耗时是 2 的两倍以上,收益却不总是线性。goal_selection_strategy:默认是 future,没问题,别改成 random,效果差很多。online_sampling:官方支持在 rollout 时立即做重标,也可以在采样时在线做。我建议打开在线重标(True),这样 buffer 里始终有足够的新鲜重标样本。
另外要注意,HerReplayBuffer的保存格式比较特殊,它把经验暂存在 GPU 之外,如果你用 GPU 训练,注意数据搬运的耗时。之前有人抱怨说用了 HER 之后训练慢了一倍,结果一看是 replay buffer 的数据类型是 Python list 而非 numpy 数组,样本采样时做了大量没必要的拷贝。把 buffer 的存储数据结构优化好,性能差距非常明显。
5. 从机械臂到更广阔的领域:HER 的扩展应用
5.1 真实机器人操作与仿真到现实的迁移
HER 最早的成名场景就是机械臂操控,OpenAI 那套 Fetch 环境里,HER 让机械臂直接从完全随机探索开始,学会了推方块、抓方块,这在稀疏奖励设定下几乎是不可能完成的任务。后来很多真实机器人项目也采用了两阶段策略:先在仿真里用 HER 训练一个模型,再用 sim-to-real 技术迁移到真实机械臂上。因为 HER 训练出的模型天然对目标泛化能力强——它对“各种可能的目标”都训练过,而不是只对某个固定目标过拟合——所以迁移到真实世界时,面对新的目标位置也能做出合理的反应。
不过实际部署时有个坑:真实机器人的状态观测有噪声,HER 重标时用的 achieved_goal 不准确,奖励计算就会出错,最终学到的策略会很奇怪。我建议在真实系统上做 HER 时,对 achieved_goal 做一次平滑滤波,或者用视觉系统输出一个置信度,过滤掉不可靠的目标状态。
5.2 多智能体与分层任务中的变体
HER 的思想不局限于单智能体强化学习。在多智能体场景下,一个智能体没有完成原始目标,但它可能帮助队友推进了某个子目标,这时候同样可以用 HER 的方式重新标记。具体做法是,把目标替换成“队友在某个时刻实际所在的位置”,然后用这个重标目标去更新这个智能体的 Q 网络。这种用法在通信受限的协同任务里出奇地有效,因为它避免了智能体之间因为相互依赖导致的奖励稀疏问题。
分层强化学习里也有 HER 的影子:高层策略给低层策略下发一个子目标,低层没完成这个子目标,但完成了一个接近的替代目标,HER 就能把这个替代目标反馈给高层,让高层学会“清楚当前能实际完成什么”。这种用法我还只试过半成品,但思路是成立的。
5.3 一点个人实操体会
HER 是个看着简单、实际非常有纵深的技术。我一开始觉得它不过就是“把目标改成 achieved goal”,但跑过几个环境、踩过几个 bug 之后,才意识到它的精妙之处在于如何结构性地利用失败数据。它不是让算法更努力,而是让算法更聪明地看待已有的经验。
如果让我给刚接触 HER 的人一个建议,我会说:先在最简单的二维点环境里把重标流程的核心代码彻底跑通,打印出每一步的 obs、goal、achieved_goal、reward、done,对照着看,再上 Fetch 这种复杂环境。不要一上来就直接套 Stable-Baselines3,因为封装得太好了,你反而不会理解里面发生了什么。等到你在简单环境上能预测下一步输出,再切到成熟库,你会发现参数调起来心里有底多了。HER 不是银弹,它只解决“稀疏奖励下的多目标任务”这一类问题,但这一类问题在机器人领域实在太多了,学它绝对是值得的。