HER算法实战:用后见之明破解稀疏奖励难题
发布时间:2026/10/1 13:51:59来源:尧图网络
做强化学习的朋友心里多半都有同一个痛处辛辛苦苦写好的环境网络结构也算合理训练起来却跟石头一样纹丝不动。尤其是机械臂抓取、机器人导航这类任务环境几乎不给你任何中间反馈大部分时间你拿到的奖励都是零模型根本不知道往哪个方向走才算“变好”。这个问题在业内有个专门的名字叫稀疏奖励问题。我自己调过大量稀疏奖励环境最服气的一套解法就是把 hindsight后见之明变成算法的一部分具体技术叫 Hindsight Experience Replay简称 HER。这篇博文不打算讲浮在表面的概念而是把 HER 想解决什么问题、核心机制怎么运作、代码怎么落到你自己的项目里以及我踩过的那些坑一次性讲透。不管你刚接触强化学习还是已经在调各种 off-policy 算法这篇文章都值得你看完。1. 为什么需要 Hindsight稀疏奖励是强化学习的头号难题1.1 什么是稀疏奖励奖励函数里的“真空地带”先搞清楚稀疏奖励到底指什么。在一个强化学习环境里奖励函数定义了每个状态转移能拿到多少即时信号。以机械臂抓取任务为例环境给机械臂一个目标位置让它把积木推到那个位置。奖励只在推到位的那一刻变成 1其余时候不管机械臂离目标多近全部是 0。这就是典型的稀疏奖励环境整个时间序列里只有极少数状态能拿到非零反馈。传统稠密奖励环境下智能体每走一步都能拿到一个梯度信号哪怕策略很差网络也能从连续的小误差里学会“哪个方向更好”。稀疏环境完全相反随机探索几百条轨迹可能连一条成功的都没有所有样本回报都是 0网络在反向传播时看不到任何信息自然什么都学不到。智能体不是笨而是缺乏一盏“灯塔”告诉它应该靠近哪个方向。生活化的类比是你在一个巨大迷宫里找唯一出口。只有站在出口时裁判才告诉你“对了”其余所有拐弯、折返都完全没有反馈。你只能随机乱走而随机撞到出口的概率低到可以忽略于是永远学不到任何有用的走法。强化学习里的随机探索跟迷宫处境几乎一样。1.2 为什么传统手段很难救场面对稀疏奖励第一反应通常是设计更精细的奖励函数也就是 reward shaping。比如机械臂靠近目标一点点就给个小分或者手写势场引导它。这种做法看起来立竿见影却经常搬起石头砸自己的脚奖励设计得不够准确时智能体会钻漏洞用完全不符合任务意图的方式刷分。学界有个很形象的说法叫 reward hacking。我见过不少项目在 shaping 中加入“靠近目标加分”之后机械臂学会了把积木甩到一边借势晃动来获取分数离真正的目标越来越远。另一种常见手段是课程学习先把简单版本喂给智能体逐步增加难度。课程学习本身有用但难点在于很难自动判断每个学生的能力边界。节奏太快模型跟不上节奏太慢浪费时间。模仿学习也能缓解探索问题但它需要额外的专家示范数据在很多真实场景里根本拿不到足够质量的演示。这些手段不是不好而是都额外引入了人工先验或外部数据。HER 的思路完全不同它不改变环境不引入额外数据只改动一个环节对已经发生但失败的轨迹重新赋予另外一套“目标”。这种“事后重新注释”的做法让它成了一个更干净、更通用、也更好落地的解法。1.3 HER 恰好解决什么HER 的全称是 Hindsight Experience Replay2018 年由 OpenAI 团队提出实验环境直接选了 Fetch 系列机械臂操作任务效果很直观在没有 reward shaping、没有课程学习的情况下HER 搭配 DDPG 就能在这些稀疏奖励环境里训练出接近 100% 的成功率。这组实验在强化学习社区里引起了不小的震动也让 HER 成了随后几年机器人操作任务的标配技巧。不过需要说清楚一个边界HER 并不是适用于所有稀疏奖励任务的万金油。它解决问题的核心前提是任务具备“多目标属性”。也就是说目标空间足够丰富一条失败轨迹也能被看成“对某个其他目标来说成功”的轨迹。如果任务只有一个固定最终目标状态空间又无法被提炼成目标形式HER 就不那么直接。这也是我在后面章节里反复强调的选型判断标准。2. Hindsight 核心思路把“失败”重写成“经验”2.1 后见之明从失败轨迹里挖掘有效信息HER 这个名字本身已经暗示了它的哲学hindsight 的意思是后见之明。英文里有句谚语叫 hindsight is 20/20意思是事情发生后回头看一切变得清晰。放到强化学习里意思是当一条轨迹已经结束回看它的每一步即便没有达成最初设定的目标但它总归达成了某一种状态。这个“已经达成的状态”完全可以当作这次探索获得的知识。还是拿机械臂推积木举例。这一集给的目标是位置 A机械臂最终把积木推到了位置 B跟 A 差很远。按照标准经验回放逻辑这条轨迹里的每个 transition 都有一个由原始目标算出来的零奖励没有任何学习价值于是被丢掉。但后见之明告诉我们虽然没推到 A但它确实把积木推到了 B。如果把这整条轨迹的目标改成 B最后一步的奖励就不再是 0而是 1。原本毫无价值的样本瞬间变成一条“如何到达 B 位置”的高质量演示。这就是目标重标注goal relabeling的核心操作。它做的不是修改现实而是修改这条经验的目的地让智能体从已经发生的失败里提炼“它到底擅长干什么”的信息。从数据利用效率来看相当于不增加任何新样本的前提下把一条失败轨迹变成多条对辅助目标有意义的学习轨迹。2.2 目标重标注的数学直觉在 goal-conditioned 强化学习框架里通常用 (s, a, r, s, g) 表示一条 transition其中 g 是当前 episode 设定的目标。HER 做的事情很直接在 episode 结束后采样一个重标目标 g通常是轨迹最终状态或未来某一步的状态然后重新调用环境的 reward function 计算 r R(s, g)再把修正后的 transition (s, a, r, s, g) 放入 replay buffer。这个过程从目标条件策略梯度的角度来看HER 通过目标重标注人为提高了正样本的比例。它并不是在重新定义任务而是在扩大有效样本的覆盖面。原本只有目标 A 的那一条轨迹拿非零奖励重标之后轨迹中靠近 B 的状态转移都获得了正反馈等于把稀疏奖励投影成了一系列局部密集的反馈。听起来有点像“自欺欺人”但它恰恰是人类学习的重要机制失败的经历不被复盘就只是噪音被复盘之后才变成可迁移的经验。HER 就是希望用重标注机制自动化完成这种复盘。对算法而言它的好处是不需要额外模型不需要改环境不需要手工定义中间子目标所有重标目标都直接从采样轨迹里生成。2.3 为什么能加快学习HER 之所以能更快收敛一方面是因为它显著缓解了探索信号缺失的问题负样本的压力下降另一方面目标条件策略本身把“不同目标”的信息压进同一个模型类似 multi-task learning。每一条轨迹能同时给多个目标提供监督样本利用率成倍增长。我试过在 FetchPush 环境里做对比实验同一个 DDPG只增加一层 HER 的重标注训练曲线完全不一样。不用 HER 时成功率在五千个 episode 内基本贴地飞行用 HER 且 goal 空间是 4 维坐标的情况下成功率大概三千个 episode 左右就开始抬头最后稳定在 90% 以上。这不是玄学而是数据效率上的数量级优势。对工程团队来说这可能意味着本来要跑一个星期的实验压缩到两三天。3. HER 的算法细节与关键参数3.1 四种重标注策略对比实际使用 HER 时重标目标的采样方式会极大影响效果。OpenAI 论文里给出了几种主流选项final、future、episode 和 random。final 策略最简单用整个 episode 的最终状态 s_T 作为所有 transition 的新目标。future 策略是在每条 transition 所在时刻 t 之后随机挑一个未来时刻的状态作为新目标。episode 策略是从整条 episode 里随机挑一个状态。random 策略完全随机从状态空间里采一个状态做目标通常只作为对照组。初看这些策略差别不大实测下来最适合大多数机器人操作任务的是 future 策略。原因很直观如果一个目标状态本来就是轨迹里未来几步的状态那么从当前 s 到目标的轨迹很短状态和目标之间的关联性更强如果用 episode 很靠前的状态作为目标当前状态离目标可能非常远智能体很难合理预测学习信号噪声变大。策略采样方式优点缺点final每集最终状态简单稳定容易实现整个 episode 共用一个目标信息重复future当前 transition 之后的某个状态目标与当前接近监督信号直接future 窗口太大会退化episode同集随机状态覆盖范围广距离可能过远噪声大random随机状态几乎无偏多数目标与轨迹无关学习效率低实际实现中future 窗口参数往往取 k4 或者 k8。我的习惯是先用 final 跑通流程再用 future 调整性能。这个领域没有一劳永逸的最优解必须结合任务的时序长度和状态空间维度做实验。尽快把对比实验做起来用曲线说话而不是靠感觉选参数。3.2 重标数量 k 怎么选HER 论文里一个关键操作是每个 transition除了它原始目标对应的样本之外还要额外生成 k 个重标样本。这个 k 控制着 buffer 中辅助目标样本的比例。k 太小HER 的优势不明显k 太大原始目标的信息会被稀释智能体反而可能被“改判成功”的目标带偏。我常用的默认值是 k4也就是每个 transition 写 1 份原始样本和 4 份重标样本进 buffer。目标空间维度较低比如 2 到 4 维时k4 通常表现不错目标空间维度高一些比如 8 维以上我会把 k 提到 8让更多辅助目标参与监督。还有的时候成功率后期上不去问题可能就出在 k 太大原始目标样本严重不足。可以观察 loss 曲线actor loss 震荡非常剧烈往往是重标比例过高造成的。还有个容易被忽略的细节重标时要尽量避免重复样本。如果 future 策略采到的状态和原始目标状态过于接近这条重标样本的信息增量就很小。所以工程实现里我会在采样重标目标时过滤掉与原始目标距离过近的状态保证喂给模型的是真实可区分的不同任务。3.3 结合 off-policy 算法的网络与权衡HER 的一个重要限制是它只适用于 off-policy 算法。原因在于重标后的经验会改变 buffer 里的奖励分布on-policy 算法比如 PPO要求经验必须来自当前策略回放旧轨迹本身就违背了它的前提。所以 HER 一般与 DDPG、TD3、SAC 或 DQN 家族搭配使用。实现上网络结构至少要支持 goal 作为额外输入。以 DDPG 为例actor 网络输入是当前状态 s 和目标 g输出动作 acritic 网络输入是 s、a、g输出 Q 值。这里的 goal 可以是一个子集状态也可以是一个单独编码的向量。很多时候 goal 空间和状态空间单位不同比如状态是关节角度goal 是末端坐标此时建议分别归一化而不是直接拼接了事。另一个实际问题是目标条件的 reward function 必须与环境的真实 reward 保持一致。HER 重标时只是替换 goal然后调用同一个 reward function如果这个函数内部有随机成分或者依赖额外隐藏变量重标结果可能不准确。所以我在做 HER 前都会先写一个可复用的 compute_reward(goal, achieved) 函数保证环境内部和 buffer 重标走同一条计算路径避免出现两套奖励逻辑打架。4. 实操实现在 DDPG 上接入 HER4.1 伪代码逻辑梳理HER 接入看起来简单但细节很容易让代码写得又臭又乱。先给一个清晰的整体框架buffer ReplayBuffer() for episode in range(total_episodes): g sample_goal() # 采样一个真实目标 s env.reset() episode_trans [] while not done: a actor(s, g) exploration_noise s_next, r, done, info env.step(a) episode_trans.append((s, a, r, s_next, g)) s s_next # 重标阶段 for t, (s, a, r, s_next, g) in enumerate(episode_trans): buffer.add(s, a, compute_reward(s_next, g), s_next, g) for _ in range(k): g_prime sample_future_goal(episode_trans, t) r_prime compute_reward(s_next, g_prime) buffer.add(s, a, r_prime, s_next, g_prime)这份伪代码有几个核心点。第一所有 transitions 必须等 episode 结束后才能处理因为 future 策略需要知道未来状态第二原始样本和重标样本可以放进同一 bufferDDPG 采样时完全不需要区分来源第三compute_reward 保持一致。把这些点写清楚后面调参才不会像无头苍蝇。4.2 关键代码示例我提供一个可运行的最小 HER 重标模块方便直接嵌入训练脚本。import numpy as np class HERBuffer: def __init__(self, capacity, k4, strategyfuture): self.capacity capacity self.k k self.strategy strategy self.data [] def add_episode(self, transitions): # transitions: list of (s, a, r, s_next, g) n len(transitions) for t, exp in enumerate(transitions): s, a, r, s_next, g exp self.data.append((s, a, r, s_next, g)) # 额外 k 个重标样本 for _ in range(self.k): if self.strategy future: # 从 t 之后的时刻随机采样 future_idx np.random.randint(t, n) g_prime transitions[future_idx][3] # s_next as goal elif self.strategy final: g_prime transitions[-1][3] else: idx np.random.randint(0, n) g_prime transitions[idx][3] r_prime self.compute_reward(s_next, g_prime) self.data.append((s, a, r_prime, s_next, g_prime)) # 裁剪超容量 if len(self.data) self.capacity: self.data self.data[-self.capacity:] def sample(self, batch_size): idx np.random.choice(len(self.data), batch_size, replaceFalse) batch [self.data[i] for i in idx] return map(np.array, zip(*batch))这只是重标缓冲区的核心框架实际项目里还需要处理状态归一化、目标是否使用观测子集等细节。但把 buffer 的职责和算法主体分开训练脚本会清爽很多。这里有个工程心态的建议不要一上来就在复杂环境里写完整 HER先用一个 toy 环境比如 OpenAI Gym 的 FetchReach 或者 2D 导航任务把自己写的 HER 模块跑通再把代码迁移到主项目。很多同学直接在大环境上调试一旦不收敛就分不清是环境问题还是算法问题。把最小可用链路打通后续调参才谈得上有方向。4.3 实验配置与调参经验以 FetchPush 为例我习惯用这样一组起始配置超参数取值actor learning rate1e-3critic learning rate1e-3gamma0.98exploration noise0.2高斯随时间衰减target noise0.05batch size256replay buffer size1e6HER k4future 窗口8这组配置不是最优而是稳妥。实际调参时优先级最高的是探索噪声和 k 值。假如训练两千个 episode 成功率纹丝不动先看噪声是不是太小导致决策过于确定、探索不足其次看重标目标是不是采样得太随意。噪声太大又会造成训练后期波动过大所以我会给探索噪声设一个衰减系数比如每 1000 episode 乘以 0.95最后维持在 0.05 附近。另外提一下并行数据采集。HER 本身很吃数据多样性单环境跑比较慢。工程上我常用 8 到 16 个并行环境同时采集 episode把数据汇聚到同一个 buffer。这样一来一次训练能拿到的失败轨迹形态更多辅助目标也更丰富收敛速度提升相当明显。Fetch 系列环境单步执行稍慢并行是最划算的加速手段。5. 常见问题与避坑指南5.1 问题速查表把我在 HER 项目里遇到的高频问题整理成了一张表遇到相似症状可以直接对照排查。现象可能原因排查与处理训练 2000 episode 成功率仍为 0探索噪声太小 / k 值偏小调大噪声幅度k 调到 8并调长未来窗口reward 上升后又快速崩塌学习率偏高 / target 更新过快学习率降到 3e-4加入 target soft update 平滑系数Q 值振荡明显重标样本比例过高减少 k 值让原始目标样本占比回升同一条轨迹塞满 buffer多样性差采集环境过少 / 策略太单一并行采集或在 buffer 中按 episode 去重目标空间和状态空间量纲差异大直接拼接导致网络难以拟合分开归一化或用 embedding 编码目标表格里的每一项我都实打实踩过至少一次。第一次在 FetchPickAndPlace 上跑 HER成功率一直卡在 20% 上下浮动最后发现是重标时 future 窗口取到了整个 episode 的末尾导致目标经常离当前状态极远模型学得糊里糊涂。把窗口限制在 t8 步以内之后效果立刻改观。这种“离得近的目标更容易学”的特性其实跟人类学习的就近原则很像。5.2 三条独家经验第一状态与目标的关系要想清楚。HER 对目标维度的可观测性很敏感。如果目标是一个需要估计的传感器值重标时不要直接用估计值做 ground truth。稳妥的办法是把目标的物理量直接纳入环境接口让 env 提供的 info 里包含 achieved goal这样重标才不会被感知误差污染。我见过一个真实机械臂项目因为 goal 来自视觉估计HER 的辅助目标跟着抖动模型始终无法稳定复现动作。第二训练时一定要记录“原始目标成功率”而不是重标成功率。有些团队复盘时误把重标后的假奖励上升当成性能提升这是个很隐蔽的坑。我会在训练脚本里把两个指标分开记录一个是在原始目标下评估的 rollout 成功率一个是 replay buffer 里的平均奖励。只有前者代表任务真实完成度后者只是内部优化信号。第三HER 和课程学习并不冲突。HER 本身已经能处理稀疏奖励但遇到高维、困难的目标空间把任务难度从近处往远处推进依然有效。我在 FetchPickAndPlace 上做过对比HER 配合简单课程先训练距离较近的目标比纯 HER 早约 1.5 倍收敛。不过课程别设计得太刻意否则 HER 的探索多样性会被限制住。最后说点个人体会。跑通 HER 不需要高深理论难的往往是那些看起来琐碎的细节目标采样窗口多大、k 取几、奖励函数有没有分叉、成功率指标准不准。我调过这么多稀疏奖励任务后最大的感受是算法名字听起来越“后见之明”实际工程里越要脚踏实地。每次训练不收敛与其急着换模型换环境不如先回头看看 buffer 里那些重标过的样本是不是真的符合任务意图。HER 给了我们一个把失败变成经验的机会怎么用好这个机会还是得靠一遍遍跑实验、看曲线、抠细节。希望这篇文章能帮你少走一些我曾经绕过的路。
网站建设高端定制企业官网