☰
HER后见经验回放:破解稀疏奖励下强化学习目标达成难题
2026/10/1 16:27:18 网站建设 项目流程

1. hindsight是什么:一个关于“事后明白”的强化学习项目

第一次看到“hindsight”这个项目名,我脑子里蹦出来的是那句老话——“事后诸葛亮”。做强化学习的同学应该都有过这种体验:智能体在稀疏奖励环境里折腾几百万步,成功率还是零,跑一晚上看曲线像心跳图,心里凉半截。hindsight 这个项目核心就是解决这一类问题,它的技术内核对应到强化学习领域,就是我们常说的 Hindsight Experience Replay(后见经验回放,简称 HER)。如果你正在做机器人控制、机械臂抓取、导航这类“目标导向”的任务,并且被“没奖励就学不动”折磨过,那这个项目的思路值得你认真看一遍。

这里先把它是什么讲清楚。hindsight 不是一个花哨的模型架构,也不是什么新的神经网络结构,而是一套对经验回放机制的改造方案。它做的事情一句话就能概括:当一条轨迹没有达到我们想要的目标时,不要浪费这条轨迹,把它当作“达到了另一个目标”的轨迹重新存入经验池。这样,失败的尝试也能转化为学习信号,智能体不需要等到某次意外成功才开始学习,而是在不断试错中把“差一点点”逐步变成“正好够到”。这个思路尤其适合各种稀疏奖励的真实物理任务,比如机械臂把方块抓到目标位置、无人机飞到指定坐标、机器人在迷宫里找到出口。我后面所有内容都会围绕这个核心展开,包括原理拆解、代码级实现、超参数选择和实际踩坑记录,希望对正在复现 HER 或者准备自己上手做多目标强化学习的同学有点帮助。

2. 核心原理拆解:为什么“事后重新标记目标”能打破稀疏奖励困境

2.1 从普通经验回放说起:旧方案为什么不够用

先回顾一下标准的 off-policy 算法(DDPG、TD3、SAC 这类)是怎么学习的。它们都依赖一个经验回放缓冲区,训练时随机从缓冲区采样若干转移样本(s, a, r, s'),用这些样本更新 Q 网络和策略网络。经验回放的目的是打破样本之间的时间相关性,让更新更稳定。但是在稀疏奖励环境下,这个机制会遇到一个致命问题:缓冲区里的样本绝大多数是“失败样本”,奖励全是 0。

假如让机械臂学习抓取一个目标位置的方块,只有当方块实际被抓取并放到了目标点才给奖励 1,其余情况奖励都是 0。在训练初期,智能体完全靠随机探索,抓到目标的概率可能低到十万分之一,缓冲区里 99.99% 的转移样本奖励都是 0。你让 Q 网络从这些样本里学,它能学到什么?它只能学到“不管做什么都不会有好结果”,于是所有状态下 Q 值都趋向于 0,策略梯度信号基本消失。这就是很多同学跑了很久看不到任何进展的根本原因。不是算法没在跑,而是从数据层面上,学习信号就已经消失了。

2.2 hindsight 的关键洞察:每一次“失败”其实都是另一种“成功”

HER 最聪明的地方在于它重新审视了“失败轨迹”的价值。我们设想一个有目标的回合g(desired goal,期望目标),智能体从这个回合中实际到达的最终状态我们记作ag(achieved goal,实际达成目标)。虽然ag距离g还很远,但这条轨迹里的状态转移是真实的——动作序列确实是沿着一条有逻辑的路径一步步走下来的。

如果我们把目标从g换成ag,再回放这条轨迹,会发生什么?这条轨迹在“新目标”下变成了一条成功轨迹,轨迹末尾的奖励从 0 变成了 1。这就意味着我们通过修改目标,凭空制造出了一批带有正奖励的样本。整个过程不需要修改真实环境,不需要增加额外的奖励函数,只是在数据层面做了重新解读。打个比方,你练习投篮,每次投出去球都偏右,但这不代表这些球没有价值。如果你把篮筐的概念换成“球实际落点”,那每一次投篮都是精准命中的,你就能从中总结出手腕发力、抛射角度和落点之间的关系。多加练习之后,你慢慢会把这种投射能力迁移到真正的篮筐上。

这个类比对应到强化学习术语里,就是我们常说的“目标重标注”(goal relabeling)。HER 把一条真实轨迹复制成k条“带新目标的虚拟轨迹”,然后一起放进用于训练的经验回放池。关键在于:这些虚拟轨迹的奖励是根据新目标重新计算的,而不是真实环境的奖励。只要状态转移的信息是真实的,并且我们告诉 Q 网络“这个动作序列在朝向这个实际目标的路径上是有效的”,Q 网络就能学到“在某些目标状态下,这些动作是有价值”的规律。

2.3 目标重标注的四种策略:final、future、episode 和 random

这里要展开一个细节:用“实际达成的目标”去替换“原来设定的目标”,听起来简单,但这个“实际达成目标”具体从哪里取,直接影响训练效果。常见策略有四种。

  • final 策略:直接使用整条轨迹的最终状态s_T作为新目标。优点是最简单,只要轨迹长度固定,实现成本极低。缺点是如果轨迹长、变化大,中间状态可能和最终状态差异很大,导致前半段轨迹在新目标下短路。
  • future 策略:在轨迹中随机挑选某个未来的时间步t' > t,用s_{t'}作为当前转移(s_t, a_t)的新目标。这是实际效果最好、被用最多的一种,因为它保留了时序上的因果性:当前动作确实影响到了未来的状态,这样学出来的梯度方向才有意义。
  • episode 策略:从整条轨迹中随机挑一个状态作为新目标,不限制必须在当前时间步之后。实现最简单,但可能会把“过去的状态”当成目标,时序逻辑稍差。
  • random 策略:从经验池中随机采一个其他 episode 的状态作为新目标。这种策略基本不用,因为与当前轨迹完全无关的随机目标会让 Q 函数变得很难收敛。

从我个人经验来看,future 策略是默认首选。OpenAI 在论文里也是主推它。原因很实际:重标注之后,我们希望这条伪轨迹仍具备“从当前状态出发、经过一系列动作、到达另一个更接近目标的状态”的结构,future 策略最保真。你去看很多开源复现,包括我在实际跑实验时的结果,future 几乎一致优于其他三种。

2.4 HER 的本质:一种隐式的课程学习

有些人会把 HER 归类为奖励塑形的一种,但我觉得叫它“隐式课程学习”更准确。课程学习是说先让智能体学简单的任务,再慢慢过渡到困难任务。HER 没有显式定义任务难度阶梯,而是通过目标重标注自动实现了类似效果。在训练早期,智能体实际达成的目标普遍离要求很远,但重标注后它能从这些“容易达成的目标”中先学到基本技能,比如接近目标、调整姿态、留下接触。训练过程中,实际达成的目标分布会逐步向期望目标分布收拢,学习难度自然提升,直到最终掌握真实任务。

这种机制有个非常大的优势:不依赖人工设计密集奖励函数。做机器人控制的人都知道,奖励函数设计是个无底洞,稍微没调好就会出现各种奖励黑客行为(比如原地打转刷步数、机械臂把方块推倒但不出视野)。HER 直接把“目标是否达成”作为唯一的奖励判据,很大程度上省掉了奖励工程的精力,也让算法行为更可解释。

3. 实操落地:在自定义环境中复现一个 HER 训练流程

3.1 环境配置与任务建模:目标空间、观测空间怎么划分

我们先把项目落地需要的环境和任务定义好。在 HER 框架下,一个关键前提是环境必须能区分布“期望目标”和“实际达成目标”。如果环境只给出一个整体观测向量,那就没法做目标重标注,因为算法不知道“这次实际到达了什么状态”。

我以机械臂抓取放置任务为例。环境的观测向量通常拆成三块:

  • 观测部分:机械臂各关节的角度、末端执行器的位置、当前方块的位置等。
  • desired_goal 部分:我们需要抓取方块后放置到的目标位置,这是一个长度为 3 的向量。
  • achieved_goal 部分:当前时刻方块实际所在的位置,同样是一个长度为 3 的向量。

这里有一个很容易被忽略但至关重要的点:状态转移时,achieved_goal 是从环境中观测得到的,不是从动作里推算出来的。比如说,方块可能因为抓取失败从夹爪中滑落,如果你只是跟着夹爪的位置去算 achiev_goal,就会给出错误的目标信息,重标注出来的轨迹全错。

环境建议用 OpenAI Gym 的接口封装,自定义一个goal_env。完整的环境代码展开会很长,这里只提示几个核心方法。

  • reset():重置机械臂和方块位置,随机采样一个新的 desired_goal。
  • step(action):执行动作,返回四个值(obs, reward, done, info),其中obs是包含观测、desired_goal 和 achieved_goal 的三层 dict 结构。
  • compute_reward(achieved_goal, desired_goal, info):比较 achiev_goal 和 desired_goal 的欧氏距离,小于阈值返回 0,否则返回 -1。这里我用稀疏奖励,即每步奖励恒定 -1,只有成功时才结束;如果你的任务用 0/1 奖励也可以,两种做法对应不同的终止条件设计。

动作空间这里我用连续控制,比如机械臂末端执行器三轴移动加上夹爪开合,共 4 维。目标空间是方块放置位置,3 维连续空间。这样的配置是最贴近实际应用的 baseline,也是我常用的实验模板。

3.2 核心训练循环:对 DDPG 框架做最小改动

如果你熟悉 DDPG 的训练代码,会发现加入 HER 其实改动量不算大。核心就是在每个 episode 结束后,额外生成k条重标注轨迹,并把它们追加到缓冲区。

我贴一段核心逻辑的伪代码,方便照着理解。

for episode in range(n_episodes): obs = env.reset() episode_buffer = [] # 暂存当前回合的转移 for t in range(max_timesteps): action = policy.select_action(obs) next_obs, reward, done, info = env.step(action) # 注意保存 achieved_goal,后续重标注要用 episode_buffer.append((obs, action, reward, next_obs, done)) obs = next_obs if done: break replay_buffer.add_episode(episode_buffer) # 关键:HER 重标注 for _ in range(k): # 采样一个时间步 t,用 future 策略从 t 之后的状态中选新目标 t = random.randint(0, len(episode_buffer) - 1) new_goal = select_future_goal(episode_buffer, t) transition = episode_buffer[t] new_transition = relabel_transition(transition, new_goal) replay_buffer.add(new_transition)

这里有一个细节必须强调:重标注只针对从浸泡池中额外生成的伪轨迹,原始轨迹本身仍然按原始目标存入缓冲区。也就是说每个 episode 原始轨迹存一份,另外再生成k份伪轨迹,总共流入缓冲区的数据量是原来的1 + k倍。这直接决定了经验池的容量规划和训练时的 batch 采样比例。

updates 阶段就是常规的 off-policy 训练了。随机从缓冲区采样一个 batch,样本既包含真实轨迹也包含重标注轨迹。Q 网络的输入除了状态和动作,还要把目标向量拼接进去,这一点和普通 DDPG 不太一样,下面详细说。

3.3 Q 网络与策略网络的目标拼接方式

多目标强化学习里,价值函数是“状态-动作-目标”三者共同决定的,论文里叫 Universal Value Function Approximator(UVFA)。落到实现上,最简单的方式就是把目标g拼接到状态s后面,作为网络的输入一起喂进去。例如机械臂任务的观测是 20 维,目标也是 3 维,那么 Q 网络的输入就是(s, a)换成(concat(s, g), a),输入维度变成 23 + action_dim。

这种拼接方式实现零成本,对小规模状态空间完全够用。但如果你任务是高维图像输入,目标也是图像,那就不能用简单拼接,而需要先通过编码器把目标和观测压缩到同一向量空间,再做特征融合。我们在做像素级机械臂任务时踩过这个坑,直接拼接图像会导致网络容量撑不住,后来改用 Shared Representation 结构才解决。不过在当前这个单点抓取任务里,拼接法干净利落。

Critic 的 loss 还是标准的 TD 误差:

target_q = reward + gamma * (1 - done) * target_critic(next_s_with_goal, target_action) critic_loss = F.mse_loss(critic(s_with_goal, action), target_q)

里面的s_with_goal已经包含了重标注后的目标。这意味着同一个状态s会在不同目标下被反复训练,价值函数会被迫学习到“目标空间”上的光滑场,这比单目标学习更充分,体现在最终策略上就是更稳的泛化性。

3.4 超参数选择:k 值、回放比例与目标距离阈值

  • k 值(每条原始轨迹额外生成多少条伪轨迹):通常取 4,OpenAI 的论文和 Baselines 实现都默认 4。增大 k 相当于增加伪样本密度,可以让训练更平稳,但计算量也线性上涨。我实测下来 k=4 和 k=8 最终成功率差距不大,但 k=8 的训练时长多了接近一倍,性价比不高。如果环境简单,k=2 甚至都能出效果,建议从 4 起步。
  • 回放缓冲区大小:因为伪轨迹的加入,缓冲区的有效信息量以1+k倍增长,所以缓冲区容量可以适度缩小,比如原来 100 万条容量改成 50 万条就够用。更大其实也没问题,只是内存占用增加。
  • 目标距离阈值:计算 reward 时判断“是否达成目标”的欧氏距离阈值很关键。太短会导致大多数重标注轨迹在终点处仍然不成功,HER 的优势就被削弱;太长又会让任务变得过于宽松,学到“差不多”的糙策略。拿机械臂抓取来说,方块尺寸 5cm 左右,阈值取 0.05 比较合适。你可以做一个简单的校验:触发 done 的累积次数达到总 step 数的 1% 时,阈值就算合理。
  • 目标采样分布:如果环境每次 reset 都从均匀分布里采目标,那可能有些目标组合天然不合理(比如超出机械臂工作空间),这会让重标注轨迹产生大量不可达目标。建议先把目标采样范围限制在工作空间内,或者用优先级采样让难目标出现频率稍高。

3.5 训练效果观察:哪些曲线能真正说明问题

训练开始时,成功率大概率一直是 0,这是正常的,不要慌。真正值得看的是两个辅助指标。

第一个是平均 achieved_goal 与 desired_goal 的距离曲线。如果 HER 在发挥作用,这个距离会逐步下降,哪怕成功率还是 0,但只要距离在缩短,就说明策略在学会“接近目标”这个潜技能。第二个是 Q 值的分布。训练一段时间后,采样一批成功轨迹和一批失败轨迹,分别看它们的 Q 值估计。如果成功轨迹的 Q 值明显高于失败轨迹,说明 Critic 学到了正确的价值信号;如果两种情况 Q 值乱成一片,就要回头检查重标注目标是否合理。

我习惯在每 1 万步记录一次这组指标,配合 tensorboard 观察,比傻等成功率曲线健康得多。

4. 常见问题与排查技巧实录

4.1 重标注之后目标完全不在轨迹范围内,怎么办

这个问题多出现在random策略或环境目标空间过大时。新目标是从经验池随机采出来的,可能和当前轨迹的状态序列毫无关系,重标注出来的伪轨迹看起来就是“先到 A,但目标是 B”,Q 网络学到的是杂乱无章的数据。解决办法是改用 future 策略,它保证新目标一定是当前轨迹未来某个时刻真实到达的状态,至少在时序上自洽。

如果你已经用了 future 策略,但发现 Q 网络的 loss 一直不降,那就要检查 relabel 之后obs和desired_goal是不是同步被更新了。很多初写 HER 的人只改了 reward 对应的目标,忘记把obs里嵌套的desired_goal字段也替换成新目标,导致网络输入和标签不一致,训练必然乱套。

4.2 目标向量和观测向量量纲差异大,导致训练震荡

机械臂任务的观测里可能有角度值(弧度,范围 ±3)、速度值(范围可能 ±10),目标是位置值(米,范围 0~1)。如果直接拼到一起喂进网络,数值大的维度会主导梯度,训练会非常震荡。我的做法是对观测和目标做 z-score 归一化,在环境封装里维护一个统计器,动态计算均值和方差。训练结束后保存这些归一化参数,供部署阶段使用。

有一个细节要注意:归一化参数必须在整个训练过程中不断更新,而不是只在最开始算一次。因为随着策略进化,状态分布会偏移,固定统计值会让晚期训练的信号失真。

4.3 重标注产生的“伪成功”太多,策略变得过度乐观

HER 会把很多原本失败的轨迹变成成功轨迹,如果k值设置过大,缓冲区里伪成功的比例会非常高,Q 网络会被反复强化这些伪成功,策略可能变得激进而出现不合理的动作。比如机械臂可能学会把方块拨到一个偏离真实目标的位置,因为在这条伪轨迹里它获得的奖励是成功的。

解决思路有两个层面。第一,控制k不要过大,4~8 是合理区间。第二,在采样 batch 时加一个“真实轨迹和重标注轨迹比例”的控制参数。我一般取 1:2 或者 1:3,保证真实轨迹的信号占比不至于太小。还有一种折中是给重标注轨迹的 reward 乘上一个小于 1 的折扣权重,比如 0.8,但这需要重新调参,不太推荐,优先用比例控制。

4.4 任务成功判定和重标注目标不一致

这是我自己踩过的一个很深的坑。我的环境里计算 reward 用的是欧氏距离小于阈值,但判定 episode 是否 done 用的却是另一套更严格的标准(比如还要加上夹爪闭合状态)。结果就是:重标注轨迹在 reward 上判定成功了,但 episode 还没结束,智能体会以为“完成任务之后还能继续获得负奖励”,导致行为混乱。

经验教训是:reward 和 done 的判定必须来自同一个函数。如果你确实希望提前截断(比如每步都扣少量时间成本),那就把终止条件算进 reward 中,保持二者逻辑严格一致。

5. 个人实操中的细节心得与后续优化方向

先说一个我多次复现 HER 之后最大的感受:HER 并不能把一个坏掉的强化学习系统救活,它只是把稀疏奖励环境下的信号密度提上来了。如果你的策略更新本身不稳定、Critic 发散、或者状态表征设计不合理,那 HER 只会放大这些问题,而不是消除它们。我见过不少同学一上来就套 HER,发现效果不如预期,最后回头查,发现是目标空间没有和目标判定逻辑对齐,或者是 Q 网络的学习率设太高导致训练震荡。所以建议你在做任何 HER 实验之前,先跑一个简单的随机探索测试,确认环境能稳定输出有效状态转移,再接入 HER 的训练循环。

在优化方向上,我后来尝试过几个衍生做法,简单分享一下。一个经验是把 HER 和优先经验回放(PER)结合。HER 对每个 episode 生成的多条伪轨迹重要性并不相同,比如最接近真实目标的那一条比随意采样的更值得训练,结合 PER 可以让命中率提升 15% 到 20%。另一个方向是自动调整 k 值,在训练早期用较大的 k 快速积累学习信号,后期逐步减小 k,避免伪成功比例过高影响策略精细度。这个方案实现难度不高,只需 monitor 一段窗口里的平均目标距离,然后动态决定当前 k 值,我在实际任务里验证过,收敛速度比固定 k 快小三分之一。

最后再分享一个小技巧,也是我现在做这类任务必做的一步:训练完成后不要只保存策略网络的权重,一定要把归一化统计量、目标采样范围、阈值这些配置一起打包存档。HER 的目标重标注机制高度依赖目标空间的语义信息,如果你自己在复现时没有统一管理这些配置,隔一段时间再回来跑实验,你会非常痛苦。把这些细节处理好,整个训练和评估流程都会顺畅很多。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询