跑过 DDPG、PPO 这类深度强化学习算法的人,大概都体验过一种让人特别绝望的场景:环境配置好了,网络结构搭好了,超参数也参考别人的仓库调过一轮了,训练跑了一整晚,第二天早上打开 tensorboard 一看——reward 曲线像一条做心电图失败的直线,从头到尾趴在初始值附近一动不动,十几万步的探索毫无学习信号。这时候你大概率会怀疑环境、怀疑代码、怀疑显卡驱动、甚至怀疑人生。但问题往往不在这些地方,而在一个更根本的设定上:你给的这个任务,奖励稀疏到 agent 根本不知道自己离成功近了一步。
我这边说的 hindsight,就是专门解决这个问题的思路。它在强化学习里对应的是一个非常知名的方法:Hindsight Experience Replay(后见经验回放,简称 HER)。这套方法 2017 年由 OpenAI 团队提出,核心思想特别朴素:把失败的轨迹重新解释为对另一个目标的成功轨迹。听起来像“事后诸葛亮”,但这恰恰是把稀疏奖励任务变成稠密奖励任务的关键切入点。这篇内容会从问题本身讲起,把 HER 的原理、代码实现、踩坑经验和适用边界全部过一遍,适合正在被稀疏奖励折磨、或者在调研目标条件强化学习方案的读者参考。
1. 为什么需要 hindsight:从“永远学不会”的任务说起
1.1 稀疏奖励是强化学习的死穴
先把问题具象化。想象一个七自由度的机械臂,任务是把桌面上的杯子从位置 A 推到位置 B。你给环境的奖励函数是:杯子中心点距离目标位置小于 5 厘米,给 +1,否则给 0。听起来很合理对吧?但跑起来就发现问题了:机械臂从初始姿态开始做随机探索,绝大多数动作产生的位移幅度都很小,一百步之内杯子可能根本没怎么动,杯子和目标位置之间的距离可能一直停留在初始的 30 厘米开外。于是每一个 episode 的累积奖励都是 0,每一个 transition 的即时奖励也都是 0。
这时候策略网络的梯度更新靠的是什么?靠的是 TD 误差,而 TD 误差的来源就是即时奖励和自举估计的 Q 值。如果一个 batch 里全都是 r=0 的数据,critic 网络很快会把所有状态的 Q 值都预测成接近 0,actor 网络拿到的梯度就变成一片噪声或者干脆趋近于 0。你加大探索噪声、调大学习率、换网络宽度,都没有用,因为问题不在拟合能力,在于整个经验池里完全没有“正反馈”样本可供学习。
有人会想:那我把奖励做成连续的,比如负的欧氏距离,这样每次机械臂靠近一点就有奖励变化了。这个思路没错,但属于“人为制造稠密奖励”,它要求你对任务有足够的领域知识去设计一个理想的势函数。在很多真实场景里,你根本不知道什么样的中间状态值得奖励,设计得不好还会诱导 agent 钻奖励函数的空子。所以 HER 的思路是:不改变奖励函数的定义,而是换一种方式制造正样本。
1.2 hindsight 的核心思想:事后重新解释目标
HER 的思想源头特别直观,就是人类在学习和复盘时天然具备的一种能力。篮球运动员投篮没进,但球碰到了篮板,他虽然没达成“进球”这个目标,但可以把这个结果重新解释为“我完成了触碰篮板这个子目标”,下次训练时就会对如何控制球的飞行弧线多一点把握。你不会因为一次投篮没进就否定整个动作的全部信息,你会从失败里提取出有用的部分。
把这个逻辑搬进强化学习,HER 的做法是这样的:在一个 episode 中,agent 本来被给定了一个目标 g,例如“把杯子推到位置 B”。但它整个 episode 下来都没有达成 g,最终杯子停在了位置 B'。正常情况下这个 episode 里所有 transition 的奖励都是 0,直接丢进回放缓冲区就是一堆垃圾数据。HER 的做法是:把“杯子最终到达的位置 B'”重新定义为一个目标 g',然后把这整个 episode 的所有 transition 都改写成“目标为 g' 的轨迹”。因为杯子最后确实停在 B',所以这段轨迹在目标 g' 下就是一段成功轨迹,最后一步的奖励是 +1,前面的每一步距离目标 B' 也越来越近,天然附带了一段稠密的“接近奖励”信号。
注意,这里面没有任何伪造或者欺骗的意思。agent 确实完成了“让杯子到达 B'”这件事,只是这件事不是它原本被要求的任务。HER 只是发现了这些真实发生过的成功,并把它们放进经验池里作为学习素材。这就是“hindsight”这个名字的真正含义:用后见之明把失败重新解释为成功。
1.3 HER 解决什么、不解决什么
HER 解决的是目标条件强化学习(goal-conditioned RL)下的稀疏奖励问题。它的前提条件是任务必须能用一个明确的目标状态来描述,并且环境能给出这个目标的观测。比如机械臂抓取、机器人导航、迷宫寻路、推箱子,这些都可以。不适用的情况也很明显:如果你只有一个固定目标,而且随机探索根本无法产生任何与目标中间状态相关的信息,HER 也无能为力,因为它需要从实际到达的状态里“取材”来生成新目标。另外,HER 不解决探索的终极难题,比如 Montezuma's Revenge 那种需要发现特定隐藏房间才能推进的任务,HER 没有内在机制帮你发现那个从没见过的状态。
所以你可以把 HER 理解成一把专门打开“稀疏奖励 + 多目标”这把锁的钥匙,它不是万能工具,但在合适的锁上效果惊人。
2. Hindsight Experience Replay 算法拆解
2.1 算法全流程
HER 的完整流程可以拆成四个大的阶段。第一步,从任务的目标分布 p(g) 里采样一个目标 g,作为这个 episode 的原始目标。第二步,让 agent 在这个目标下和环境交互,跑完整整个 episode,记录下轨迹里的每一个状态、动作、奖励、下一状态、目标、任务完成标志。第三步,把这条完整 episode 数据先存进一个临时的 episode 缓冲区。第四步,是 HER 的关键动作:从这个 episode 里,按照一定的策略再选出若干替代目标 g',以 g' 对轨迹中的每一条 transition 重新解释目标、重新计算奖励和 done 标志,然后把新旧两组 transition 都写进总体的回放缓冲区。
之后的学习流程就和普通 off-policy 算法完全一致了。从回放缓冲区 sample 出一个 batch,用 DDPG、DQN、SAC 之类的算法更新 actor 和 critic。区别只在于回放缓冲区里多了一批通过 hindsight 重标记出来的成功样本。这些样本虽然在原始目标下是“失败”,但在重标记目标下是“成功”,它们的奖励分布不再是全 0,而是存在大量的小负数(未完成的过渡步骤)和少量 +1(最后一步的成功)。
这里有个重要的工程细节:HER 的回放缓冲区必须按 episode 存储,不能像普通 DDPG 那样逐 transition 地存储。原因很简单,重标记需要知道整条轨迹的走向和最终到达的状态,你只有在 episode 结束之后才能做 hindsight 操作。所以代码结构上通常分两层:一个 episode 缓存用来收集当前这一条轨迹的全部数据,一个 replay buffer 用来存放已经重标记好的 transition。
2.2 四种目标重标记策略
有了完整 episode 之后,怎么挑选替代目标 g' 是有讲究的。原论文里对比了四种策略,我在实际复现的时候也挨个试过,这里直接说结论和我的感受。
第一种叫 final,就是直接把 episode 结束时 agent 最终到达的状态当成替代目标。这个策略最简单,但产生的监督信号最少,因为只用了轨迹末端的一个状态作为目标,大部分 transition 距离这个最终目标其实很远,中间几乎没有逐步接近的引导。第二种叫 random,就是从整个回放缓冲区里随机抽一个已出现过的状态作为替代目标,这个策略的问题在于随机抽出来的目标可能和当前任务完全不相关,难度忽高忽低,学习效率一般。第三种叫 episode,就是从当前 episode 内部随机抽一个在未来某一步会到达的状态作为替代目标。第四种叫 future,采样方式和 episode 类似,但有一个额外约束:对轨迹里的每一个 transition t,替代目标只能从 t 之后的时间步里随机抽取,且要采样 k 个不同的目标。
四种策略的对比结果在原论文里非常明确:future 最好,episode 次之,random 和 final 明显偏弱。为什么 future 效果最好?因为它的逻辑最符合逐步接近的学习过程:对某个 transition,在未来的状态里选目标,意味着从这个 transition 出发,后续的轨迹里有连续的段落是朝着这个目标前进的,中间存在大量奖励逐渐增大的中间信号,相当于天然构造了一段密集的“走向目标”的引导数据。而 episode 策略因为允许从过去的状态里采样目标,可能出现目标在“身后”的倒车情况,信号没那么连贯。
| 策略 | 采样范围 | 信号质量 | 我的复现感受 |
|---|---|---|---|
| final | 轨迹终点状态 | 差,只有末端有信号 | 收敛慢,适合简单任务 |
| random | 整个回放缓冲区 | 差,目标无关性强 | 不稳定,容易成绩波动 |
| episode | 当前 episode 内任意状态 | 中,有倒车风险 | 效果一般,不如 future |
| future | 当前 transition 之后的 k 个未来状态 | 好,连续引导 | 推荐首选,k=4 比较稳 |
2.3 为什么 HER 有效:从奖励密度看本质
从理论层面看,HER 有效性的核心在于它改变了回放缓冲区里正样本的比例。在稀疏奖励任务里,普通经验池的即时奖励分布是退化的一坨零,TD 学习在这种情况下收敛到一个全零解的盆地。而 HER 通过重标记,在缓冲区里人为制造了一批“目标可达”的成功样本,它们的奖励存在层级变化,这就让 critic 有机会学习到“状态距离目标越近,Q 值越大”的连续映射关系。有了这个映射,actor 的梯度更新就不再是零向量,策略就有了明确的改进方向。
换个角度理解:HER 本质上是构建了一个自适应的课程学习机制。普通课程学习需要人手动排好任务的难度阶梯,而 HER 自动选择 agent 实际能到达的状态作为目标,这些目标天然分布在当前策略的可达域内。随着策略逐渐变强,它到达的状态越来越接近真实目标范围,重标记出来的新目标也随之向真实目标靠拢。目标分布和策略能力一起演进,整个学习过程就有了自主升级的节奏。这也是为什么 HER 不需要额外设计课程表的原因。
还有一个小细节值得注意:HER 不只是给经验池增加成功样本,它还改变了“目标”的边缘分布。原来的目标分布是任务给定的 p(g),可能非常集中且困难,而 HER 重标记后的目标分布更多落到了 agent 的真实行为分布上。从多任务学习的角度看,这相当于让 agent 在一个更均匀、更可达的目标空间里训练,学会的是“从任意状态到达任意可达目标”的通用控制能力,这种能力迁移到原本的目标上也更容易。
3. 从零实现 HER:PyTorch 加持的完整实操
3.1 环境与基线选择
我建议第一次接触 HER 时别一上来就上 MuJoCo 的 Fetch 系列,虽然那是原论文的标准实验环境,但配置成本高、渲染依赖多,初学者容易被环境问题劝退。用 openai/gym 里自带的一个简单任务,或者干脆自己写一个二维点机器人推球环境。我这里用的是 gym 里改造过的二维迷宫:一个圆形的 agent 在二维平面上,目标是一个固定坐标,agent 每步可以输出 x、y 方向的推力,观测包括自身位置和目标位置,当欧氏距离小于 0.05 时视为成功并给 +1 奖励。
选择这个环境的原因:一是维度低、训练快,几万步就能看到明显效果;二是状态空间完全可控,方便打印日志检查重标记后的奖励数值;三是它能非常直观地暴露“没有 HER 时永远学不会”的现象,对比效果显著。基线就用 DDPG 加不加 HER 两个版本跑同一套超参,对比成功率和学习曲线。
3.2 核心数据结构与回放缓冲区实现
先写基础的回放缓冲区,这里直接给出核心代码逻辑。注意,HER 版本的缓冲区存储的是“episode 分片”,但为了方便随机采样,我会在训练时再把完整 episode 展开成逐 transition 存储,并保留每个 transition 所属的 episode 索引,方便做 future 采样。
import numpy as np from collections import deque class EpisodeBuffer: def __init__(self, capacity=500): self.capacity = capacity self.buffer = deque(maxlen=capacity) def push(self, episode): # episode: dict of lists, 包含 obs, action, reward, next_obs, goal, done self.buffer.append(episode) class HERReplayBuffer: def __init__(self, capacity=1_000_000): self.capacity = capacity self.obs = [] self.next_obs = [] self.actions = [] self.rewards = [] self.goals = [] self.dones = [] self.episode_id = [] def add_transition(self, obs, action, reward, next_obs, goal, done, ep_id): # 存储时用原始 transition 先占位 self.obs.append(obs) self.actions.append(action) self.rewards.append(reward) self.next_obs.append(next_obs) self.goals.append(goal) self.dones.append(done) self.episode_id.append(ep_id) def relabel_and_store(self, ep, ep_id, achieved_goals, future_k=4): # 原始 transition 已经存过了,这里只做重标记并追加 T = len(ep["obs"]) for t in range(T): for _ in range(future_k): # future 策略: 从 t+1 到 T-1 中随机采一个状态作为替代目标 future_idx = np.random.randint(t + 1, T) new_goal = achieved_goals[future_idx] # 重算奖励 new_reward = self.compute_reward(ep["next_obs"][t], new_goal) new_done = float(new_reward > -0.05) # 阈值判断 # 直接把重标记后的 transition 写到主回放区 self.add_transition( ep["obs"][t], ep["action"][t], new_reward, ep["next_obs"][t], new_goal, new_done, ep_id )这里有个容易写错的地方:future 采样时np.random.randint(t + 1, T)需要保证t + 1 < T,也就是最后一步不参与 future 采样。我见过有人在最后一步报错后直接改成randint(t, T),这样就把“当前状态”本身作为目标了,虽然不算致命错误,但会让重标记后的最后一步奖励变成 +1,和原始节奏对不上,会带来一点偏差。稳妥做法是只对t < T - 1的 transition 做 future 额外重标记,最后一步只用 final 策略补一条就行。
3.3 目标重标记与奖励重算
奖励重算是 HER 里最关键的细节。原始奖励函数基于真实目标 g,重标记后目标变成了 g',那么奖励必须用 g' 重新计算,否则就会出现“目标是 A,奖励却按 B 算”的错位,整个经验池的标签就是乱的。我的奖励函数用的是简单形式的二值奖励:距离小于阈值给 0(表示成功),否则给 -1。负的常数奖励比全 0 要好一些,因为它给 critic 提供了一定的“推进压力”,让 Q 值能够区分不同距离的状态。
def compute_reward(self, achieved_goal, desired_goal, threshold=0.05): # 欧氏距离作为成功判据 dist = np.linalg.norm(achieved_goal - desired_goal) if dist < threshold: return 0.0 else: return -1.0注意,我说奖励“给 0”是指成功时给 0,失败时给 -1。这和常见的 +1/0 形式本质上等价,只是平移了一下,不会影响最优策略。但有一个地方会因此受影响:done 标志。如果按“奖励是否为 0”来判断任务是否完成,那么重标记后最后一步通常满足dist < threshold,此时应该把 done 置为 True。如果你直接把原始 trajectory 里的 done 复制过来,那这批重标记样本在 critic 眼里就成了“未完成但奖励为 0”的诡异数据,会造成 Q 值错误外推。所以重标记时必须同步重新计算 reward 和 done,这两者是绑定在一起的。
3.4 训练闭环与超参选择
主体流程我用 DDPG 作为 off-policy 基座,和标准 DDPG 的区别只在于回放缓冲区多了一层 episode 缓存、以及每次把新 episode 写入主缓冲区时执行 future 重标记。完整训练循环长这样:
for episode in range(max_episodes): obs, goal = env.reset() ep_obs, ep_actions, ep_next_obs, ep_goals, ep_dones = [], [], [], [], [] ep_achieved = [] for step in range(max_steps): action = select_action(obs, goal, noise_scale=0.1) next_obs, reward, done, info = env.step(action) ep_obs.append(obs) ep_actions.append(action) ep_next_obs.append(next_obs) ep_goals.append(goal) ep_dones.append(done) ep_achieved.append(info["achieved_goal"]) obs = next_obs if done: break # 先把原始 transition 写入主回放区 ep_id = len(her_buffer.episode_id) for t in range(len(ep_obs)): her_buffer.add_transition( ep_obs[t], ep_actions[t], compute_reward(ep_achieved[t], goal), ep_next_obs[t], goal, ep_dones[t], ep_id ) # 关键: HER 重标记并追加样本 her_buffer.relabel_and_store( {"obs": ep_obs, "action": ep_actions, "next_obs": ep_next_obs}, ep_id, ep_achieved, future_k=4 ) # 正常 off-policy 更新 if her_buffer.size() > batch_size: for _ in range(update_steps): batch = her_buffer.sample(batch_size) # DDPG 网络更新,逻辑同标准实现超参方面,比较关键的是future_k,也就是每条 transition 额外生成几个重标记样本。原论文用的 4,我在二维环境里试过 2、4、8,感觉 4 是一个性价比很高的点,样本量翻倍的同时不会让缓冲区里“假目标”占比过高。还有一个值得注意的细节:主回放区容量尽量设大一点,比如 50 万到 100 万条,因为 HER 会显著增加样本写入速率,容量太小会导致重标记样本被快速淘汰,无法充分发挥作用。
DDPG 本身的学习率、exploration noise、网络结构我直接沿用了标准配置,没有额外调优。实测下来,同样的 10 万步预算,无 HER 的 DDPG 成功率一直趴在 0% 附近,加了 HER 的版本大概在 3 万步时开始出现稳定成功,到 8 万步成功率接近 90%。这个差距就是重标记带来的。
4. 实战踩坑与调试实录
4.1 我踩过的五个坑
第一个坑:忘了重算奖励。听起来很蠢,但特别容易在写代码时发生。你为了省事,把原来的transition直接复制了一份,换了个 goal 字段就丢进缓冲区,奖励字段还是按照原始目标算出来的。结果就是经验池里存储的“成功样本”实际上并不成功,critic 被这些错位数据带偏,训练刚开始的时候 loss 疯狂飙升,然后整个策略崩掉。排查方法也简单:重标记完成后随机打印几条 transition,肉眼检查 goal 和 achieved_goal 的欧氏距离,确认小于阈值时 reward 确实是 0 或 +1。
第二个坑:done 标志没更新。这个我在前面已经强调了。重标记以后,最后一步的 done 应该是 True,否则 critic 在自举时会错误地继续估算未来回报。一个很隐蔽的现象是:训练时 success rate 有上涨趋势,但到后期反复震荡上不去,这就是 done 标志不一致留下的隐患。把 done 改成随 reward 同步计算后,曲线一下就稳了。
第三个坑:把 episode 数据直接切成单条 transition 存入缓冲区,导致做 future 采样时拿不到未来的状态。HER 必须按 episode 存放原始数据,这也是它的工程实现和普通 DDPG 经验池最大的不同。如果你一开始设计数据结构时没注意,后面要补就很被动,要么重新录数据,要么把 episode id 作为额外的字段塞进去。我的建议是第一步就设计成两层结构,别图省事。
第四个坑:目标维度没做归一化。很多真实环境里目标状态是多个物理量的拼接,例如位置坐标和速度,量纲差异很大。如果不归一化,距离计算会主要被大数值的那个维度主导,小数值维度直接失效。HER 的目标采样和 distance 判据都依赖状态间的距离,这个问题会被放大。我习惯在环境返回值之后做一次标准化,或者至少对目标向量做 min-max 缩放。
第五个坑:重标记目标全选高难度样本。有人会把 future 的 k 值调得特别大,比如 20,导致缓冲区里大部分都是 agent 实际达不到的远端目标,学习信号反而被淹没。HER 的核心是让目标落在可达域附近,过度重标记会稀释信号密度。我现在的经验是 k 不要超过 8,且 future 策略生成的样本占总体样本比例控制在 30% 到 50% 之间。
4.2 怎么判断 HER 是否在起作用
判断 HER 有没有生效,最直接的指标是回放缓冲区里即时奖励的分布。你可以定期统计一下奖励非零的比例。在我的二维环境里,没有 HER 时这个比例是严格 0%,因为所有真实轨迹都不可能到达目标;加了 HER 之后,非零奖励比例大概在 20% 到 30% 之间,这说明重标记确实在制造有效信号。如果这个比例太低,比如低于 5%,多半是重标记逻辑有问题,或者目标采样难度失衡。
第二个判断维度是 Q 值的分布。训练稳定后,抽样统计 critic 预测的 Q 值中位数和最大值,如果最大值明显大于中位数,说明 critic 学会了区分不同状态的好坏。如果所有 Q 值都挤在同一个值附近,说明信号没有传进去。
第三个判断维度是成功率曲线的斜率。HER 的一个特征是成功率通常不是均匀提升的,而是先经历一个很长的平台期,然后突然出现一段陡峭上升。这个平台期是 agent 在积累了足够的重标记样本、逐步学会了“接近目标”的基础行为,一旦这个行为成形,实际成功率就会爆发式增长。我见过很多次这种情况,所以如果你看到前面两万步毫无起色也别急着杀进程,先确认缓冲区里非零奖励样本比例是正常的,再多等等看。
4.3 常见问题速查表
| 现象 | 可能原因 | 排查手段 |
|---|---|---|
| 训练一开始 critic loss 就异常大 | 重标记后奖励没重算,旧奖励与新目标错位 | 打印随机 transition 人工检查 reward 与 goal 的匹配性 |
| 成功率长时间为 0,缓冲区无正样本 | future 采样索引越界导致重标记代码被跳过 | 在 relabel 函数入口加计数日志,确认每 episode 确实写入了新样本 |
| 成功率曲线不停震荡上不去 | done 标志未随奖励同步更新 | 检查最后一跳 transition 的 done 是否为 True |
| 测试时策略行为异常,动作幅度过大 | critic 被错误数据带偏,Q 值外推失真 | 清空缓冲区,修正重标记逻辑后从头训练 |
| 训练速度明显变慢 | future_k 过大,缓冲区写入量暴涨 | 调小 k 到 4,并增大缓冲区容量 |
更多时候,HER 的调参问题不是出在算法本身,而是出在数据管线的细节上。我强烈建议在完整训练之前,先写一个十几行的小脚本专门验证重标记逻辑:模拟一条随机轨迹,执行 relabel,然后打印原始目标和重标记目标下的奖励、done、距离对比。这一步花 10 分钟,能省下后面两天调参的时间。
5. HER 的适用范围与后续演进
5.1 什么时候该用 HER,什么时候不该用
适合用 HER 的任务有个共同特征:目标可以用低维或中维向量明确表示,且存在“在行为轨迹中实际到达的状态可作为候选目标”的空间。比如机械臂的位姿抓取、移动机器人的导航、游戏中需要到达特定地点的任务、以及一部分自动驾驶决策场景里的目标状态规划。凡是任务本身带有多目标属性,且你关心的是“能否学会一个通用的目标达成策略”,HER 都是非常值得优先尝试的基线方案。
不太适合的情况我总结为三类。第一类是单目标且目标极难到达的纯探索型任务,比如“找到地图里唯一隐藏的宝藏”,因为 agent 从来没接近过宝藏,重标记找不到任何有价值的替代目标,也就是没有“hindsight”的素材。第二类是目标定义本身不清晰的任务,比如对话生成里的“让用户满意”,你很难抽出明确的目标向量,HER 的 relabel 无从下手。第三类是你已经有很好的人工密集奖励,HER 带来的边际提升会很小,反而增加存储和计算的复杂度。
另外要注意的是,HER 对 off-policy 算法是必需品,因为它必须依赖回放缓冲区来反复利用重标记样本。如果你用的是 PPO 这种 on-policy 算法,强行套 HER 需要改成重要性采样或者维护一个很大的旧策略缓冲区,工程复杂度会直线上升,效果还不一定好。除非有特殊理由,否则我建议在 policy gradient 系算法上不要硬套 HER。
5.2 从 HER 出发的后续扩展
HER 提出之后,围绕“自动生成更有价值的目标”这个方向衍生出了不少工作。比较有参考价值的有:CHER(Curriculum Hindsight Experience Replay),它不再均匀地重标记,而是根据 agent 当前的学习进度动态调整重标记目标的难度分布,让课程推进更平滑;HGG(Hindsight Goal Generation),专门面向多智能体或多人协作场景,在重标记时加入对其他智能体行为的考虑;还有 GoalGAN、LfD+HER 这类把生成对抗网络和示范学习结合起来的方案,用学到的目标生成器替代朴素的随机采样。这些改进方向本质上都是在回答同一个问题:除了“从未来状态里抽目标”,有没有更聪明的选目标方式?
如果你项目里已经有了一个可工作的 HER 基座,往这些方向扩展的路径是相当顺畅的。因为核心的数据管线、重标记机制和奖励重算逻辑都不用动,只需要替换“目标采样策略”这一层。这也是我特别喜欢 HER 的一个原因:它的模块化程度很高,算法演进不会要求你推翻重来。
最后分享一个从实际项目里总结出来的体会:HER 最大的价值其实不只是它带来的成功率提升,而是它改变你设计任务的思路。以前遇到稀疏奖励,第一反应是加奖励塑形、加中间奖励、加人工引导,现在我会先问一句——这个任务的“成功”能不能被重新定义成若干个子目标?如果答案是肯定的,那很多让人头疼的奖励函数设计就不必要了。每次训练失败的数据也不再是垃圾桶里的废品,而是可以反复挖掘的监督信号。这种“把失败当资源”的思路,放到强化学习之外的工程问题里同样成立。