☰
HER算法解析:从失败经验中学习,解决稀疏奖励难题
2026/10/3 3:45:57 网站建设 项目流程

我一直觉得,hindsight这个词是英文里最耐人寻味的词之一。字面上看是“后见的视力”,翻译过来就是后见之明、事后诸葛亮。做机器学习的人大多都跟这个词打过交道:模型训练完回头分析bad case,调参之后复盘实验,这都算是hindsight。但在深度强化学习这个圈子里,提到hindsight,大家想的通常不是日常用语,而是一个很具体的算法:Hindsight Experience Replay,后见经验回放,简称HER。这个算法的核心就一句话——让智能体从失败的经验里也能学到东西,把没做成的事,当成已经做成的事去反思。

HER解决的是强化学习里最头疼的稀疏奖励问题。比如机器人要把一个方块推到指定位置,奖励只在推到的瞬间给一次;或者机械臂去抓一个杯子,抓到了才给奖励。这类任务里,随机探索的成功概率极低,智能体可能跑几百个episode都拿不到一次正奖励,梯度基本是零,整个训练就是原地打转。HER的思路很反直觉:目标没实现没关系,我把“实际到达的地方”重新定义为目标,原本失败的轨迹就能变成一条成功轨迹,经验就多了,训练也就稳了。

这篇博文我会把HER从原理、细节到代码实现和坑位,完整走一遍。目标读者是正在做强化学习项目、遇到过稀疏奖励问题,或者只是想理解这个著名算法的人。我会用机器人推箱子的例子贯穿全文,尽量把每一步为什么这么做讲清楚,也会把我自己在实际训练中踩过的坑一并交代。

1. 项目整体设计与思路拆解

1.1 稀疏奖励到底是什么问题

先把问题本质说透。在经典的强化学习框架里,智能体通过与环境交互获得累积奖励的最大化。奖励信号是唯一的监督信号,如果奖励太稀疏,监督就几乎没有。拿Fetch机器人的推球任务来举例:环境是一个模拟机械臂,目标是推动桌子上的小球到达某个位置,动作是机械臂关节的控制量。成功判定的条件很严格——小球最终位置与目标位置的距离小于某个阈值,比如5厘米。整个过程如果没成功,奖励就是-1;成功才是0或正值。

这意味着什么?机械臂有很高的自由度,动作空间连续且高维,随机或者策略初始阶段的动作几乎不可能把球精确推到目标点。假设一次episode是50步,每一步关节角度组合是连续的,那么整个episode的成功路径在动作空间里相当于一个无穷小的点。随机探索命中这个点的概率基本为零。于是智能体从头到尾拿到的全是-1,所有轨迹看起来都一样,没有办法区分哪一步稍微好一点、哪一步更接近目标。Q函数和策略梯度都失效了,因为没有任何正梯度的方向。

常见的应对方式是做reward shaping,也就是人为设计一个中间奖励。比如,每一步根据球到目标的距离给一个负的惩罚,距离越小,惩罚越小,甚至设计一个势函数引导。问题是,reward shaping本质上是在写死先验知识,设计不好会把智能体带进局部最优。比如距离远的时候给的惩罚大,智能体可能学会了用力过猛把球推到远处,因为那样能减少距离惩罚;更糟糕的是,如果任务本身很复杂,你根本不知道该给什么样的形状奖励。HER就是来解决这个问题的,它不需要额外的形状奖励,而是自动从失败轨迹里“无中生有”出成功经验。

1.2 HER的核心思路:换个角度看失败

HER这个名字里的“后见之明”,用一句话解释就是:站在事后的角度,重新定义目标。具体操作是这样的:当你把球推到某个位置,但没有推到预设的Goal时,别急着说这次失败了。你可以把球实际到达的那个位置,当作一个新的Goal,然后重新算一下这条轨迹里每一步的奖励。在新Goal下,这条轨迹的最后一刻是“成功”的,也就是说,它变成了一条奖励密集的成功轨迹。

举个例子,想象你计划从小区门口走回自己家,但你最后走错了楼,走到了旁边一栋楼底下。如果你只盯着“回家”这个目标,这次出门完全没价值。但如果你换个思路:把“走到7号楼门口”当作目标,那这次出门实际上非常成功,你不仅走到了,还全程熟悉了路径。下次再想找7号楼,你已经有了一条可复用的成功轨迹。HER就是让智能体不断地用这种“事后诸葛亮”的方式给自己编造目标,然后把这些目标塞进经验池里,当作已经完成的任务来学习。

这个思想在Goal-conditioned Reinforcement Learning里特别自然。这类任务的设定里,状态和动作是固定的,但目标是可以变着法儿选的。一条轨迹本身包含了一系列状态、动作、奖励和新状态,Reward函数一般是“状态是否达到目标”的判定函数。既然目标可以变,那么我换一个目标,整条轨迹的奖励就完全不同了。HER抓住的就是这个自由——把目标当作变量,在采样经验的时候重新赋值。

1.3 为什么它能行:从失败中学习

可能有人会想:把失败轨迹重新标注成成功,这不是在骗自己吗?智能体学到的东西还有意义吗?这里有个关键点必须理解:HER并没有伪造物理规律。它改变的是“这次探索的目的”,而不是“这次探索本身”。机械臂实实在在做出了这一串关节运动,小球也确实到达了某一个位置,这条动力学数据是真实的、合法的物理轨迹。在某个真实存在的目标下,这条轨迹确实达到了目标。

所以HER学到的是:给定当前观测,执行某个动作,会到达一个什么样的后续状态。这个状态转移的知识是通用的,不管目标是什么。有了这些真实转移数据,智能体就能学会把状态引导到特定目标。比如,机械臂发现“当我朝某个方向移动时,球会往那里滚”,这个知识既适用于目标A,也适用于目标B。所谓成功经验,不过是在目标空间里标注了这个转移最终命中了谁。

更重要的一点:HER解决了探索信号不均的问题。原始经验里,只有极少数轨迹是成功轨迹,绝大多数都是失败轨迹。如果都按失败轨迹处理,网络无法学会如何靠近目标。但重新标注之后,一条50步的轨迹里可能有50个不同的“实际终点”,每一步的奖励都变得有区分度了。梯度不再是零,Q值函数可以看到不同状态下不同动作的回报差异,策略就可以被优化了。

2. 核心细节解析与实操要点

2.1 目标重标记:哪些可以改,哪些坚决不能改

HER的整个模型听起来很简单,但真正写代码的时候有一个非常重要的边界:不是所有数据都能重标。定义里,一条transition通常是五元组:当前状态s,动作a,下一状态s',奖励r,目标g。s和s'里应该包含goal相关的信息,a是动作。HER允许你改的是g,以及由g计算出来的r(在很多实现里还有done标志)。s、s'、a绝对不能改,因为它们是物理上真实发生的。

为什么s不能改?如果当前状态s改了,那么“在这个状态下采取动作a”就不再是真实发生过的事情,数据的物理合法性就没了。HER的哲学是“用真实动作数据配上不同目标”,而不是“凭空生成数据”。这也是它和数据增强的区别。数据增强比如对图像做平移翻转,是在保持语义不变的情况下创造新样本;HER是在保持物理转移不变的情况下创造新样本。如果连状态都改了,模型就没法学到consistent的动力学。

奖励r怎么改?这取决于任务的奖励定义方式。对于goal-conditioned任务,典型定义是r = -1(如果最终没达到目标)或0(如果达到)。你可以把r写成关于状态s'和目标g的函数r(s', g)。如果是这样的稀疏判定奖励,重标时只要用新的g算一遍r(s', g)即可。如果奖励本身包含距离惩罚,也同理。要注意的是,如果奖励函数很复杂,且跟原始目标g有强关联,那么重标计算时也要保持一致,保证“在新目标下这条轨迹确实最优或成功”。

另外,done标志也要跟着改。很多环境里,如果达到目标,episode提早终止。在Hermes实现里,重标后的done应该重新计算:对于每个transition,新的done取决于新目标是否被达到。但是,经验池里一般会存两个版本:原始的(用于原始目标的学习)和重标的(用于新目标的学习)。注意不要用重标的done去覆盖原始经验,否则会出现逻辑混乱。

2.2 采样策略:final, future, episode, random

HER只说了“换目标”,但换哪个目标?这直接影响效率。经典的HER论文(Andrychowicz et al., 2017)里比较了四种策略:

  • final:直接使用一条轨迹的最终观测作为新目标。最简单,但对长轨迹来说,轨迹早期离最终点很远,重标意义不大。
  • future:在当前时间步之后的某个随机时间步的观测作为新目标。这是最常用的一种,因为目标跟当前状态是“有序”的,更符合学习的逻辑。
  • episode:从整个episode中随机选一个观测作为新目标。完全随机,可能选到当前时间之前的,相关性较弱。
  • random:从所有已见过的目标集合里随机抽一个。这个更随机,通常效果一般。

实践里,future策略最稳。原因也直观:我们希望重标的目标不要离当前状态太远,也不能太近。太远了,这条轨迹的后续几步根本够不到那个目标,学习信号还是稀疏;太近了,这类例子太多,策略容易过拟合到“原地踏步也能算成功”。future策略让重标目标分布在当前步往后的若干步内,相当于在“你看得见的未来”里选一个目标,既有挑战性又不会完全够不着。

一个典型的实现是:针对一条episode里的每一步t,以一定概率(比如0.5,或者官方的20%)选择future策略,然后在区间[t+1, T-1]随机采样一个k,用状态s_k(去掉goal部分)作为新目标。为了增加多样性,通常每条transition会重标成多个新目标,比如k=4个。也就是说,一分经验变成五条:一条原始经验加四条重标经验。

如果不用概率分布,而是全都用future,也差不了太多。我用过的经验是,纯future训练初期收敛会快一些,但后期多样性不足;混合策略鲁棒性更强。

2.3 结合算法:与策略梯度/DDPG等无模型算法兼容

HER不是一个独立的强化学习算法,它是一个经验生成模块,要和某个强化学习算法组合使用。论文里最经典的是和DDPG搭配,因为DDPG是off-policy算法,依赖于经验池。HER天生适合这种结构:它把经验池里的每条经验都复制几条重标版本,然后统一交给DDPG更新。

为什么不推荐on-policy算法?因为HER产生的新目标对应的经验,在策略分布上有偏移。on-policy算法要求数据来自当前策略,历史数据不能直接用,而HER恰恰是在往经验池里塞历史数据,原理上冲突。当然,也可以用PPO之类的算法结合HER,但需要更复杂的分层处理,实践上不划算。

跟DDPG组合的关键点:DDPG里有两个网络——actor(策略)和critic(Q函数)。HER重标后,critic会学到“不同目标下的价值”,actor的输入里通常也含有goal。这就意味着,actor的输出动作是条件于goal的,critic的状态-动作价值也是条件于goal的。所以网络输入里一定要把goal拼接进去。我见过很多新手跑HER失败,就是因为网络结构里漏了goal输入,或者维度对不上。后面实操部分我会给具体代码。

另外,如果是离散动作空间,也可以和DQN组合。但DQN的Q网络需要输出每一个离散动作的Q值,goal仍然是输入的一部分。如果任务空间是离散的、且目标是离散的,同样可以用HER,只是重标时目标空间不再是连续状态,而是那些实际发生过的离散状态。这种情况下,策略上要注意选择合适的距离函数或匹配规则。

3. 实操过程与核心环节实现

3.1 环境与基线选择

我习惯用OpenAI Gym里的FetchReach-v1做HER面试题。它本质是一个七自由度机械臂,需要把末端执行器移动到目标位置,目标是一个三维坐标点。动作空间是四维的(增量控制),状态里包括机械臂关节角度、末端位置、目标位置等多个分量。智能体每一步都没有中间奖励,只有最终是否命中目标(距离小于0.05)才返回成功。

这个环境很适合跑通HER流程。它的目标空间就是三维坐标,状态里直接包含目标坐标的一个copy,方便我们做重标。你可以从gym.make("FetchReach-v1")开始,也可以直接用mujoco-py或robosuite。对于没有实体库的读者,其实只要理解你的环境是不是goal-conditioned就行:状态里必须包含goal信息,奖励必须是基于goal的判定或距离函数。

对于HER来说,环境的接口最好有这些字段:observation, desired_goal, achieved_goal。observation是完整状态,achieved_goal是实际达到的目标(比如末端位置),desired_goal是期望目标。Fetch环境自带这些字段,但如果要自己写环境,记住要提供achieved_goal,因为HER重标时要拿它当新目标。

3.2 核心代码:HER重标逻辑

我写一个简化版的HER重标过程,使用numpy来演示。

import numpy as np from collections import deque class HindsightReplayBuffer: def __init__(self, capacity, k=4, future_strategy_prob=0.5): self.buffer = deque(maxlen=capacity) self.k = k self.future_prob = future_strategy_prob def add_episode(self, ep_obs, ep_actions, ep_rewards, ep_dones, ep_goals): # ep_obs: [T+1, obs_dim], ep_actions: [T, act_dim] # ep_rewards: [T], ep_dones: [T], ep_goals: [T, goal_dim] (原始目标) T = len(ep_actions) for t in range(T): obs = ep_obs[t] next_obs = ep_obs[t+1] action = ep_actions[t] reward = ep_rewards[t] done = ep_dones[t] goal = ep_goals[t] # 先存原始经验 self.buffer.append((obs, action, reward, next_obs, done, goal)) # 再存重标经验 for _ in range(self.k): if np.random.uniform() < self.future_prob: # future策略:从t+1到T随机选一个时刻,把那个时刻的achieved_goal作为新目标 future_idx = np.random.randint(t+1, T+1) new_goal = extract_achieved_goal(ep_obs[future_idx]) else: # final策略:直接用最终achieved_goal new_goal = extract_achieved_goal(ep_obs[-1]) new_reward = compute_reward(extract_achieved_goal(next_obs), new_goal, None) new_done = np.abs(extract_achieved_goal(next_obs) - new_goal) < threshold self.buffer.append((obs, action, new_reward, next_obs, new_done, new_goal))

这里有几个实现细节要注意:

  • 为什么要在t+1到T+1之间采样,而不是t之后任意位置?因为future策略必须保证新目标在当前时间步之后,这样轨迹后续的动作才跟目标有关联。如果选在当前时间步之前,那么当前状态和目标已经发生了,后续动作可能根本不会朝那个方向走,关联太弱。
  • extract_achieved_goal(obs)是从观测里取出实际到达的目标分量。对于Fetch环境,状态向量是(achieved_goal, desired_goal, observation)的组合形式,具体切片记得按环境文档写。我常犯的错是切片位置不对,导致新目标其实是目标坐标的期望而不是实际坐标,训练直接废掉。
  • compute_reward要使用环境自己的奖励函数,不要自己重写。如果拿不到,就用-np.linalg.norm(achieved - new_goal)或者-np.allclose(...)之类。但必须在“新目标下”计算,这是关键。

在真正的训练循环里,add_episode是在每个episode收集完整后才调用一次。经验池通过均匀随机采样给DDPG的actor和critic更新。很多框架如Stable-Baselines3的代有版本也支持HER,但内部用的是DummyVecEnv加HerReplayBuffer。建议自己写过一遍完整逻辑之后再用库,否则出了问题很难查。

3.3 训练配置与效果分析

参数上,我直接引用我可复现的一组配置做参考。环境是FetchReach-v1,动作维度4,状态维度25(含goal冗余)。DDPG的actor和critic都是两层全连接,隐藏层256。学习率actor和critic都用1e-3,gamma=0.95,tau=0.05,批量大小128,buffer容量50000,每步探索噪声用正态分布标准差0.2。

核心参数是k=4,每次trajectory每条transition重标4次。这个值越大,每个经验被复用得越多,数据效率越高,但过大会导致经验池里高度重复,多样性反而变差。我试过k=8,在FetchReach这种简单任务上没明显提升,倒是训练速度慢了不少。一般建议k取4。

还有future_strategy_prob。官方默认是使用final策略的优先级是20%?不,论文里最终采用了一个混合:每条transition以概率0.8使用future,否则用final。我觉得未来策略概率在0.5到0.8之间都行。如果你发现训练早期目标到达率增加得很慢,就把概率往0.8调;如果后期不稳定,可以降到0.5。这个不是死规矩。

训练结果的评估指标是每个epoch的success_rate。所谓epoch,一般定义为一定数量的探索episode,通常是50个。评估时不去重标,只用原始goal,看100个episode里成功了多少。理想情况下,不训练时成功率是0,用HER+DDPG练500个epoch(相当于25000episode)左右,FetchReach基本能到95%以上成功率。我在本地用CPU跑,大约要二十分钟左右;如果换成FetchPush这样的复杂任务,大概需要几小时。

下面的曲线大致是这种形状:前十个epoch成功率接近0,二十到八十个epoch开始快速上升,一百个epoch后逐渐到90%以上。如果你的曲线一直趴在地上,那大概率不是你运气不好,而是代码里重标逻辑或者网络结构有问题。

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

4.1 训练不收敛,成功率一直为零怎么办

遇到过最多的情况,是重标目标时选到了“非法目标”。什么叫非法?比如你用ep_obs[-1]作为新目标,但ep_obs[-1]是最后一个状态,它的achieved_goal确实是真实位置,没问题。但有些环境在设置done时,如果达到目标后会提前终止,最后一条状态的achieved_goal其实是None或者无效值。又或者,你的状态里既有desired_goal又有achieved_goal,切片取反了,导致你拿desired_goal当新目标。这种情况下,重标出来的所谓“成功轨迹”其实并没有真正命中新目标,奖励还是负的,等于白忙。

排查方法:单独写一个小脚本,随机执行几个episode,手动调用重标逻辑,打印新旧goal,计算新旧奖励。如果新奖励在重标后仍然是-1,说明目标提取有问题。还有,检查actor输入里是不是把goal正确拼进去了。我刚开始做强化学习的时候把obs和goal直接拼接成一个大向量,后来忘了在actor网络输入里重新拼接,导致整个训练没法区分目标,自然学不会。

4.2 重标经验太多会不会污染原始目标

会有一点风险,但不是污染,而是冗余。经验池一半是原始目标,一半是重标目标,重标目标里有很大比例彼此相近(因为future采样会经常选到接近最终点的位置)。如果经验池里全是这些相似目标,Q网络会把“轻松成功”的目标的Q值估计得过高,导致策略偏向于只做某些简单目标,对真正难的目标不强。解决方法是控制重标比例,k不要过大,或者限制经验池中重标经验占比,比如最多50%。另一个做法是使用优先经验回放,把harder sample(比如原始目标中未成功的)提升优先级。

经验池里还要注意:原始经验中少数成功的轨迹,价值极高,不要因为重标数据量大而被淹没。我一般会把原始经验至少保留30%,方法就是在代码里设定一个比例,把重标经验分为两个buffer,采样时按7:3采样,原始经验占30%。

4.3 objetivo空间太大,距离函数怎么选

有些任务的goal空间是庞杂的,比如二维图像坐标或关节角度组合。HER重标时不需要距离函数来计算奖励,但DDPG的critic学习需要奖励值。如果奖励函数是基于欧几里得距离的阈值判定,那么你必须在设定新目标时用同一个距离函数。如果奖励函数本身是稀疏判定,比如reward = 1 if distance < threshold else 0,那么重标后要注意:做future采样时,选择的目标离当前状态越近,越容易拿到0奖励的“成功”样本;太远了,则大概率还是0奖励,等于没有引入中间信号。

解决办法:如果目标空间维度高,可以在重标时使用一个“虚拟距离”函数,但不建议直接改奖励定义,因为这相当于改变任务本身。 实在不行,可以把HER换成“距离引导”的变体:比如不仅将最终actual goal作为新目标,还可以将轨迹中距离当前目标较近的中间状态作为新目标,并给定一个中间奖励。但这就不算标准HER了,更像goal augmentation。

4.4 常见问题速查表

问题现象可能原因排查与解决
训练初期成功率一直是0奖励信号生成错误或重标后仍是负奖励手动检查重标代码,打印新旧目标与新旧奖励
某些目标从不被学到采样策略太偏,future概率过高降低future概率,混合final策略,增大k
训练后期震荡严重经验池中相似样本过多,Q值估计偏差限制重标经验比例,或者用优先回放
网络输入维度不匹配goal没有拼接到actor/critic输入检查状态和goal的切片维度,确保输出维度和动作维度一致
模型对原始目标表现很差过度依赖重标目标,忽略了原始难度增加原始经验比例,降低k,提高目标到达率评估频率
训练速度快但评估低maybe you are evaluating with wrong goal mask评估时必须使用desired_goal,不能使用achieved_goal

4.5 几个我加了之后效果更好的技巧

第一,HER可以和随机重置目标(Goal-conditioned exploration)结合。在episode开始时,随机选一个目标,甚至故意选一个不可能达到的目标,让智能体先探索一下不同方向。HER重标时会把这部分探索转化为有效数据,这样能极大拓宽状态覆盖。

第二,对DDPG的探索噪声做decay。HER虽然能产生成功经验,但如果你一直用大的探索噪声,收集到的数据包含太多随机动作,重标后学到的动力学还可能不精确。我的经验是:前50个epoch用噪声0.3,之后慢慢减到0.1,训练更稳定。

第三,如果任务步骤太长(比如500步以上),可以先用一个更短的episode长度训练HER,让它先学会短距离目标,再逐步加长。有一个叫Curriculum Hindsight的技术就是干这个的,本质上是把环境的最大步数当作课程难度逐步增加。

最后说一个我自己的体会:HER不是银弹,它处理的是“目标能够被定义且奖励仅依赖目标”的问题。如果你的任务没有清晰的目标,或者奖励严重依赖过程性条件(比如必须先按下红色按钮再按下蓝色按钮),那么HER重标一个新目标并不能让奖励变密集,因为它没法改变过程的逻辑依赖。这时候可以考虑扩展HER到层次化目标,或者结合课程学习。但如果你刚接触稀疏奖励,HER一定是你第一个应该尝试的方案,没有之一。

用一个很朴素的比喻来收束:人生里我们经常觉得没做到某件事就是白费,但其实走路走过的地方,本来就是一条路。HER就是让算法把走过的路认成路。希望你之后跑HER的时候,数据顺利,目标可及,没有网上那些奇奇怪怪的坑。

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

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

立即咨询