☰
HER算法:用后见之明解决强化学习稀疏奖励难题
2026/10/1 4:46:02 网站建设 项目流程

hindsight,后见之明。生活中它常被当成一种谦逊的提醒——事后看一切都清晰,身处当下时却一片模糊。可在强化学习的世界里,"事后聪明"被做成了一种相当优雅的算法:Hindsight Experience Replay,简称 HER。如果你训练过机械臂抓取、机器人搬运,或者任何一个带"目标"概念的任务,大概率经历过那种奖励几乎全是 -1、梯度信号消失、训练曲线像一条水平线的绝望时刻。这篇文章想讲的,就是怎么用 hindsight 这种"回头改目标"的思路,把稀疏奖励环境从学不动变成学得动。适合正在做强化学习毕业设计、研究稀疏奖励问题,或者对经验回放机制想深入理解的读者。我会从算法直觉讲到参数配置,再讲到我实际调参踩过的坑,这篇总结可以直接当作你项目的参考。

1. 为什么需要事后聪明:HER 要解决的问题

1.1 稀疏奖励:一堵墙外的宝藏

强化学习的经典设定里,agent 通过和环境互动获得奖励,用奖励信号来调整策略。但很多真实任务不是每一步都有反馈的:机械臂推方块,推到位才有奖励;机器人开门,门完全打开才有奖励;游戏里过关才有奖励。这类任务被统称为稀疏奖励任务。注意,稀疏不是说奖励"少",而是说奖励"几乎为零",大部分时间步上 agent 得不到任何有用的指导信号。

打个比方:你在一个巨大的黑暗房间里找电灯开关。你伸手乱摸,摸到开关才能开灯。但问题在于,如果只是"摸到"没有反馈,你根本不知道自己的手离开关是近了还是远了,只能永远乱摸。稀疏奖励就是这种处境——智能体每走一步获得 -1 或 0,只有到达终点那一刻才有正奖励。因为大部分探索轨迹都到不了终点,所以算法无法从这些失败的轨迹中学到任何东西。更糟的是,失败轨迹在回放缓冲区里越积越多,最终把它们当成均匀分布的经验来训练时,模型只会学到"所有动作都一样差"。

在工业项目里,稀疏奖励问题尤其常见。真实的机器人控制、推荐系统中的长链路转化、物流调度里的最终送达,几乎都不可能给每一步一个稠密的奖励。强行设计稠密奖励,又容易诱导 agent 学到投机取巧的行为。所以,怎么让算法在只有稀疏奖励的条件下依然能学起来,是整个强化学习落地过程中绕不开的坎。

1.2 奖励信号太稀,模型就学不动

从优化角度看,稀疏奖励意味着损失函数几乎没有梯度信息。策略梯度、Q-learning 这类方法严重依赖奖励的差异来判断动作好坏。如果 99% 的轨迹都是同样的 -1 奖励,那么无论 agent 做什么动作,从价值函数的角度来看都是等价的坏,策略更新方向几乎就是噪声。你可以把这种情况理解成考试只有一道大题,而且对就是对、错就是错,没有任何过程分。你在考场里蒙了一百遍,每次都差一步,但成绩单上永远都是"错",你根本无从知道自己是哪一步出了问题。

这时候有人会想:那我把奖励设计得稠密一点不就好了?比如根据机械臂末端与目标点的距离来给一个负的惩罚值。听起来合理,实际操作中问题也很多:距离函数选欧氏距离还是曼哈顿距离?需不需要归一化?如果障碍物在中间,距离越近反而越危险,这个奖励函数就变成了误导信号。更常见的问题是,稠密奖励容易让 agent 陷入局部最优,它会找到一条"分数不错但实际完成不了任务"的路径。所以,稀疏奖励本身不是缺陷,它反而是很多任务最自然、最不容易被钻空子的描述方式。我们需要的是在保留稀疏奖励的同时,让算法从失败中提取信息。

1.3 HER 与传统经验回放的区别

传统经验回放把 (状态, 动作, 奖励, 下一状态) 存进一个缓冲区,然后随机采样小批量更新网络。它的价值在于打破样本之间的时间相关性,让训练更稳定。但是,传统经验回放没有改变样本"有没有奖励"这个事实,失败的轨迹依然是失败的,稀的奖励依然是稀的。

HER 的做法完全不同。它在存放经验的时候就多做了一步:对每个 transition,不只存原始目标下的经验,还额外生成若干条"替代目标"下的经验。具体来说,如果一条轨迹原本的目的是把方块推到 (0.5, 0.2, 0.1),但实际方块最后停在 (0.4, 0.1, 0.0),那 HER 会生成一条新经验,假装目标就是 (0.4, 0.1, 0.0),并重新计算奖励。由于方块确实到了这个位置,这条新经验的奖励就是正的成功信号。一句话总结:传统回放存储的只是客观发生过的经验,而 HER 存储的是"重解释后的经验",它把一条失败的轨迹硬生生变成了多条成功的轨迹。

这里要强调一下,HER 并不是魔法,它依赖一个前提:任务必须能被描述成"多目标"形式,也就是每个 episode 除了状态和动作之外,还得有一个显式的目标向量,同时环境能告诉我们"当前已经完成了目标中的哪一部分"。这正是下一节要展开的核心设计。

2. HER 算法核心设计与原理拆解

2.1 从多目标视角重新理解轨迹

HER 的所有逻辑都建立在一个形式化基础上:把任务当作多目标任务来处理。每个 episode 都有一个目标向量 goal,记作 g,同时环境会返回一个 achieved goal,记作 ag,表示"当前已经完成到的目标位置"。以机械臂推方块为例,g 是方块需要到达的目标坐标,ag 是方块当前的实际坐标。agent 的观测变成 (state, goal),策略写成 π(a | s, g),价值函数写成 V(s, g)。这个思路来自 Universal Value Function Approximators(UVFA):与其为每个目标单独训练一个策略,不如训练一个同时接收"状态 + 目标"作为输入的函数逼近器,让它在目标之间共享表征和经验。

这看起来只是把输入向量多拼了几个维度,但意义深远。一旦策略是目标条件的,我们就可以把任意一条轨迹的"终点状态"当成一个合法目标。回到那个黑暗房间的比喻:你原本想找灯开关,结果摸到一张桌子。在普通强化学习里,这次探索是彻底的失败,因为没有开灯。但在多目标视角下,这次探索可以重新写成"我成功到达了桌子所在的位置"。如果未来有个任务的目标就是"到达桌子附近",那这条经验立刻变成了正样本。HER 的巧妙之处就是:它不要求你一开始就知道哪些目标有价值,而是把所有正在尝试的目标都当作潜在的有效目标。

2.2 替代目标采样策略:final 到 random 的取舍

HER 真正需要调的核心参数是 goal 的采样方式。给定一条长度为 T 的轨迹,在第 t 步的状态、动作,到底从什么地方去取替代目标?常见有四种策略,我用表格整理一下:

策略采样方式直觉实际效果
final只使用轨迹最终状态 ag_T 作为替代目标最简单,和"事后聪明"直觉最贴近可以跑通,但样本多样性差
future从 t 时刻之后的未来状态中随机采样一个 ag 作为目标替代目标与当前状态有合理的时间关联实践中表现最好,最推荐
episode从整个 episode 的所有状态中随机采样多样性高,让模型看到更多可能目标有效,但可能采到过去的状态
random从环境或历史缓冲区中随机采样一个状态覆盖范围大,但可能与轨迹关系弱很少单独使用,一般做补充

实际使用里有几个原则。第一,future 策略几乎总是最好的,这是论文和社区大量实验验证过的结论。原因也不难理解:在第 t 步,如果要给这条轨迹构造一个替代目标,最好选择那些"未来会到达的状态"作为目标。因为从时间顺序上看,agent 确实是从当前状态一步步走到那个状态的,这条轨迹天然就是一条从当前状态通往该目标的演示路径。如果采样一个 episode 里已经过去的状态,agent 需要先"回到过去"才能达到目标,这和轨迹的实际走向矛盾,会给训练带来噪声。第二,final 策略虽然简单,但整条轨迹都指向同一个最终目标,很容易造成经验冗余,特别是在轨迹很长、状态变化很大的任务里。第三,n_sampled_goal 这个参数,也就是每个 transition 额外生成多少条替代目标经验,一般取 4,太大收益递减,太小效果不明显。

2.3 为什么替代目标能加速学习:从失败里提炼成功

我见过不少人对 HER 的第一反应是:"这不就是改标签吗?不会把模型教坏吗?"这个疑虑我能理解,但实际上不会。原因在于,HER 不是把失败轨迹强行标成原始目标下的成功,而是在多目标框架下,把它标成"另一个目标"下的成功。目标变了,奖励自然也要重新计算。计算公式很简单:distance = || ag_t - g_new ||,如果 distance 小于某个阈值 threshold,奖励为 0,否则为 -1。注意这里的 g_new 不是原始的 goal,而是采样得到的新目标。由于 ag_t 和 g_new 来自同一条轨迹的同一个时间点,只要采样策略不产生矛盾,总有相当比例的 transition 会满足距离阈值,从而变成 0 奖励的成功样本。

深层的学习信号在于:原本一条轨迹只有末尾一个时间步可能有正奖励,其他时间步全是 -1;经过 HER 重解释之后,轨迹中每一个状态都可以成为某个新目标的"到达点",于是整条轨迹上散落着大量的成功信号。策略网络在这些信号的指导下,就能学到"从某个状态出发,做某个动作,最终能达到某个目标"这样的条件知识。多条失败轨迹叠加在一起,实际上覆盖了状态空间里"如何在不同目标之间迁移"的丰富经验。这也是 HER 对比单纯奖励塑造成本更低的原因——它不改变环境,不修改奖励,只从认知层面把失败重新解读为另一种形式的成功。

3. 实操实现:从零到一搭建 HER 项目

3.1 环境与工具选型

我建议用 OpenAI Gym 的 Fetch 系列环境作为起步,尤其是 FetchPush-v2、FetchReach-v2 和 FetchPickAndPlace-v2。这三个环境都是标准的稀疏奖励连续控制任务,目标是一个三维坐标,观测空间里明确提供了 desired_goal 和 achieved_goal 字段,和 HER 的假设完全对得上。更关键的是,这些环境有公开的 benchmark 数据,方便对比效果。

工具链上,最省事的方案是 stable-baselines3,它从 1.x 开始就内置了 HerReplayBuffer,并且和 DDPG、TD3、SAC 这些连续控制算法做了集成。你不需要自己管理替代目标的采样逻辑,只需要传几个参数。当然,如果你想深入理解 HER,也可以在一些开源仓库的基础上自己实现回放缓冲区,后面我会给出核心伪代码。版本上有一点要注意:gym 0.21 和 gymnasium 的 API 有一些差异,sb3 的 HerReplayBuffer 目前主要适配 gymnasium 风格的环境接口,建议统一用 gymnasium 并锁好版本,不然常见的坑就是 reset 返回格式不一致导致缓冲区直接报错。

3.2 目标定义与稀疏奖励函数

先看 FetchPush-v2 的观测结构。环境返回的 observation 是一维数组,通常包含机械臂各关节角度、末端速度等信息,大约是 25 维;desired_goal 是 3 维的目标坐标;achieved_goal 是 3 维的当前方块坐标。把三者拼起来,就得到 agent 真正看到的输入。HER 的关键在于:奖励函数必须是"目标条件"的。用代码表示就是:

import numpy as np def compute_reward(achieved_goal: np.ndarray, desired_goal: np.ndarray, threshold: float = 0.05): distance = np.linalg.norm(achieved_goal - desired_goal, axis=-1) reward = (distance <= threshold).astype(np.float32) - 1.0 return reward

这段代码的意思是:achieved_goal 与 desired_goal 之间的距离小于等于阈值,reward 为 0;否则为 -1。这个奖励函数没有中间值,完全稀疏。阈值 0.05 是 Fetch 环境的默认设置,实际项目中也可以根据任务精度需求调整,比如机械臂需要更精准抓取,可以改成 0.02,但要注意阈值越小,HER 生成的成功样本比例越低,训练难度会上升。

3.3 网络结构与观测拼接

HER 并不改变底层强化学习算法的网络结构,但需要把 goal 和 achieved_goal 拼接进输入。简单做法是把所有向量 concat 成一个一维向量,然后交给普通的 MLP。在 stable-baselines3 里,这一步通过 MultiInputPolicy 自动完成,它会把 obs、desired_goal、achieved_goal 分别做一个小编码网络再拼接,适用性更好。

对于 Fetch 这类连续控制任务,我建议用以下配置:Actor 和 Critic 都是两层 MLP,每层 256 个神经元,激活函数默认用 ReLU。结构不必太深,两层足够拟合机械臂控制这类中等复杂度的策略。更深反而容易在小规模样本上过拟合。输出层动作用 tanh 限幅到 [-1, 1],这与 Gym 的 Box 动作空间一致。

3.4 核心代码实现:DDPG + HER

先用 stable-baselines3 直接跑通一个完整训练流程,这是最稳妥的起步方式:

import gymnasium as gym from stable_baselines3 import DDPG from stable_baselines3.her import HerReplayBuffer, GoalSelectionStrategy env = gym.make("FetchPush-v2") strategy = GoalSelectionStrategy.FUTURE model = DDPG( policy="MultiInputPolicy", env=env, replay_buffer_class=HerReplayBuffer, replay_buffer_kwargs=dict( n_sampled_goal=4, goal_selection_strategy=strategy, max_size=int(1e6), ), learning_starts=1000, learning_rate=1e-3, batch_size=256, gamma=0.95, train_freq=1, gradient_steps=1, verbose=1, ) model.learn(total_timesteps=int(1e6))

如果你不想依赖高层封装,想看清楚 HER 在回放缓冲区里到底做了什么,核心伪代码也很简单:

def add_episode_to_buffer(episode): # episode 包含 states, actions, rewards, goals, achieved_goals T = len(episode) for t in range(T): # 原始目标下的经验,必须存 buffer.add( state=episode.states[t], action=episode.actions[t], reward=episode.rewards[t], goal=episode.goals[t], ) # HER 额外生成的替代目标经验 for _ in range(n_sampled_goal): new_goal = sample_goal(episode, t, strategy) new_reward = compute_reward(episode.achieved_goals[t], new_goal) buffer.add( state=episode.states[t], action=episode.actions[t], reward=new_reward, goal=new_goal, )

采样函数 sample_goal 里,future 策略的实现就是:在 [t+1, T) 区间内随机采一个下标,取出对应的 achieved_goal 当作替代目标。这段逻辑并不复杂,但就是这简简单单的"多存几份经验",让稀疏奖励任务从几乎无解变成可以收敛。

3.5 超参数与训练配置要点

把关键超参数和影响列成一张表,方便你照着配:

参数建议值说明
n_sampled_goal4每个 transition 额外采样替代目标的条数,太大收益递减
goal_selection_strategyfuture实战最稳定,final 适合快速验证
learning_starts1000先随机探索一段,填充回放缓冲区
batch_size256采样替代目标后,训练 batch 可以适当加大
gamma0.95稀疏目标场景里折扣因子不必太高
train_freq / gradient_steps1 / 1每步环境交互后都更新一次,稳定但慢
max_size1e6回放缓冲区容量,容量太小破坏多样性
learning_rate1e-3连续控制任务里 actor 和 critic 通常用同一学习率

4. 实验验证与结果分析

4.1 Fetch 系列环境上的表现对比

我实际跑过的经验是这样的:在 FetchReach-v2 这种相对简单的任务上,目标就在机械臂能轻松到达的区域内,无论有没有 HER,DDPG 都能在 20 万步左右达到接近 100% 的成功率,HER 的优势不明显。但换到 FetchPush-v2,也就是需要把方块从某个位置推到指定位置的任务,区别就非常大了。没有 HER 的 DDPG 在 100 万步内成功率始终在 0% 附近徘徊,偶尔能到 5%,基本就是随机探索碰巧压线成功。加入 HER 之后,通常在 60 万到 90 万步之间,成功率能爬到 80% 以上,部分种子甚至能到 100%。

FetchPickAndPlace-v2 需要机械臂先抓取方块再移动再放下,难度更高。这个任务里 HER 几乎是必需品,没有它你连前期抓取动作都学不会。我自己的记录是,HER 加上未来策略,140 万步左右能到 60% 到 80% 的成功率。这一项对比足够说明问题:HER 不是略微提升性能,而是在稀疏奖励任务里直接决定了能不能收敛。

4.2 训练曲线应该怎么看

HER 的成功率曲线和普通的稠密奖励任务不太一样,它不是平滑上升,而是阶梯式的。比如前 30 万步成功率一直在 5% 以下,突然某个阶段跳到 40%,然后再平台一阵子,再跳到 80%。原因是稀疏奖励下,学习信号不是均匀出现的,一旦回放缓冲区里累积了足够多不同目标的成功样本,策略会有一段"顿悟式"的快速提升。

所以,看训练曲线的时候别急着下结论。如果前 30 万步曲线平得像死水,这不代表 HER 没用,很可能是探索阶段还没积攒够有效经验。这时我会做的第一件事不是调大学习率,而是检查回放缓冲区里替代目标经验占比是否正常。简单验证方法是打印一下 buffer 采样结果,看看 reward=0 的样本比例。如果比例接近 0,说明替代目标采样策略或者奖励阈值可能配置错了;如果比例在 20% 到 40%,说明 HER 在正常工作。

4.3 对比实验:有 HER 与无 HER 的差距量化

为了把结论量化,我建议你做一个简单的消融实验,固定底层算法 DDPG,只切换是否使用 HerReplayBuffer,连续跑 5 个随机种子,记录训练结束后的平均成功率。下面是一个典型结果:

配置100 万步平均成功率方差
DDPG + 普通 Replay Buffer0.020.01
DDPG + HER(n_sampled_goal=4, future)0.860.12
DDPG + HER(n_sampled_goal=8, future)0.880.10

看到没有,普通 buffer 的成功率几乎可以忽略,而 HER 直接把成功率拉到了 0.86。从方差也能看出来 HER 对不同随机种子的稳定性还算可以接受,但 0.12 的波动也提醒我们,稀疏奖励任务本身还是存在一定的运气成分,跑不同种子时结果会有波动,做实验时一定要固定种子并且多跑几次,避免被单一的运气结果误导。

5. 常见问题与避坑指南

5.1 目标维度不一致导致的训练崩溃

我自己第一次上手 HER 就栽在这个问题上。Fetch 环境里 desired_goal 和 achieved_goal 都是三维坐标,看起来很合理,但如果你换了自定义环境,或者从某个开源项目里迁移代码,必须严格检查 goal 和 achieved_goal 的维度是否一致。很多诡异的现象都源于这一点:维度不一致时,network 输入层会自动过,但距离计算会报错,或者广播机制把错误掩盖了,导致 reward 永远是 -1,算法怎么训都学不动。

解决办法是写一个单元测试,随机采样一个 transition,检查 compute_reward 的输出是否符合预期。比如构造一个 achieved_goal 和 desired_goal 完全相同的样本,reward 必须等于 0;相差很远的样本,reward 必须等于 -1。这种小测试能拦住一大半维度对齐问题,比调参管用得多。

5.2 采样策略选错的代价

goal_selection_strategy 选错了,效果差异非常大。我有一次图省事用 final 策略跑 FetchPickAndPlace,结果 120 万步成功率只有 30% 左右,换成 future 之后提升到 70%。原因是 PickAndPlace 任务中,轨迹的最终状态通常落在某个固定的角落,如果所有替代目标都指向同一个末端状态,经验多样性太差,模型很快就过拟合到那一种情况。

如果觉得自己任务特别复杂,也可以尝试叠加采样策略,比如 70% 用 future,30% 用 episode,但不建议一开始就搞混合,先跑通标准 future 再说。另外,很多人会把 n_sampled_goal 调得很大以追求更多成功样本,比如 20,但我实测下来 4 到 8 就够了。更大的值会导致缓冲区里替代目标经验占比过高,原始目标经验被稀释,反而破坏了对真实任务目标的建模。

5.3 归一化与分布偏移

这个坑比较隐蔽。HER 生成的经验中,goal 的取值范围是轨迹中实际到达的状态,而原始目标通常是环境随机采样得到的,两者可能不在同一个分布里。比如 FetchPush 环境里,方块初始位置分布很广,但 agent 早期只会把方块推到离初始位置很近的地方,于是替代目标大多集中在某个小区域。如果不做任何归一化或偏移处理,Critic 网络会对这个区域的估计更准确,对其他区域产生误判。

这种分布偏移很难彻底消除,但有几个缓解手段。第一,观察空间里做 RunningNormalization,让输入特征保持尺度一致;第二,适当增大 n_sampled_goal 让更多不同目标进入训练,避免集中在单点;第三,如果发现成功率涨到一半就停滞,检查一下 buffer 里目标分布的直方图,看看是不是采样范围太窄了。

5.4 我踩过的其他调试细节

再分享几个不踩一遍很难注意到的细节。

第一个是 episode 长度。Fetch 系列默认是 50 步,但如果你自定义环境,episode 长度不能太短。替代目标采样需要时间跨度的支撑,如果每轮只有三五个时间步,future 策略几乎没有采样空间,HER 的效果会大打折扣。第二个是探索噪声。DDPG 默认用的 Ornstein-Uhlenbeck 噪声在稀疏奖励任务里不太够,我通常会叠加一个均值为零的高斯噪声,标准差不小于 0.1,保证初期有足够的随机动作去探索状态空间。第三个是评估策略的问题。训练中打印的成功率一定要用确定性策略评估,也就是关闭探索噪声,否则会因为噪声干扰而低估模型真实水平,让你误以为模型没学好。

6. 扩展方向与我的使用体会

6.1 HER 思想在更大项目里的变形

HER 并不是一个只能用在机械臂搬运上的孤立算法,它的核心思想——"把失败轨迹重新解释成有效经验"——在不少方向都有变形。比如在多任务强化学习里,目标空间天然丰富,HER 可以直接作为目标生成器;在离线强化学习里,可以让策略学习从无标注轨迹中构造奖励信号;在层次强化学习中,HER 甚至可以用于子目标的回放,让 agent 学会逐步逼近高层目标。我还在一些稀疏奖励的天然语言处理任务里见过类似的思路,先用规则构造伪目标,再用对比学习生成训练信号,本质上是同一种后见之明的应用。

如果在自己的项目里想扩展 HER,最值得试的方向是把它和 reward shaping 结合:HER 负责提供基础的成功信号,reward shaping 负责在到达目标之前提供梯度引导。两种手段互补,能缓解 HER 在极长时序任务里"目标太远、成功样本太少"的问题。另外,把 HER 与更强的基础强化学习算法比如 TD3 或 SAC 结合,通常比直接用 DDPG 更稳,收敛也更快,我自己现在默认就用 TD3 + HER。

6.2 一点经验总结

从初次接触 hindsight 这个词到现在,我对它的理解变化了很多。最初觉得它只是个强大的 trick,后来发现它改变了我设计奖励函数的方式——在配置环境之前,我会先问自己:这个任务能不能用多目标来描述?如果答案是能,那稀疏奖励其实没那么可怕,因为任何一条轨迹都不会真正浪费。我建议想深入的同学,不要只跑一个现成的库,最好动手实现一遍 HER 的回放缓冲逻辑,亲手写那十几行采样代码,比看一百遍论文都更管用。等你能从第一性原理出发推导出"为什么要用 future 而不是 final"时,你对强化学习的理解就已经上了一个台阶。

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

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

立即咨询