☰
HER实战:目标重标记破解稀疏奖励,从原理到代码实现
2026/10/1 4:30:35 网站建设 项目流程

做强化学习的都知道,稀疏奖励问题是最让人头疼的事情之一。我最早接触 hindsight 这个概念,是在读 OpenAI 那篇 Hindsight Experience Replay(HER)论文的时候,当时第一反应是“这个操作也太取巧了”,但认真想明白之后又觉得“确实就该这么做”。这个算法出来已经有几年了,但现在回头看,它依然是解决稀疏奖励问题最简单、最实用、落地成本最低的思路之一,尤其适合机器人操作这类目标明确的连续控制任务。这篇文章就用实战的视角,把 HER 从原理到实现完整拆一遍,重点说清楚它到底解决了什么问题、代码是怎么写的、以及我在实际跑实验中踩过哪些坑。

1. 什么是Hindsight:一句话讲清楚它在解决什么问题

1.1 稀疏奖励:让强化学习“无从学起”的死局

强化学习的核心逻辑是智能体通过与环境交互、获取奖励信号来学习策略。如果奖励信号本身非常稀少,比如机器人只有在把方块正好放进目标位置时才能得到 +1,其他时候全是 0,那智能体就会面临一个非常尴尬的局面:它完全不知道自己距离成功还有多远,因为所有“失败”的尝试收获的反馈都是一模一样的 0。

这个问题在学术界有个专门的名字,叫“奖励稀疏”(sparse reward)。很多真实场景都有这个特点,比如机械臂抓取、桌面推箱子、机器人导航,这类任务往往目标非常明确,但奖励信号只有成功的那一刻才出现。在这种情况下,标准强化学习算法的学习效率会急剧下降,因为智能体只能靠瞎蒙碰运气才能拿到一次非零奖励,然后才能从这个稀有的经验里慢慢回溯学习。如果状态空间稍微大一点,这个“碰运气”的过程可能需要几百万甚至上千万次尝试,在真实机器人上根本不可接受。

我之前用 DQN 跑过一个简单的推箱子任务,环境只有二维平面、四个动作,理论上不难,但因为没有给中间奖励,模型跑了足足两百多万步还是学不会。后来我把奖励改成“越靠近目标奖励越高”的稠密奖励形式,几万步就收敛了。所以问题的根源很清楚:奖励信号太稀疏,梯度信号就消失,学习就没有方向。

1.2 一个经典的“捡瓶子”场景

为了把问题说具体,我们用一个经典场景来贯穿全文,就是机械臂抓取任务。假设我们有一个七自由度的机械臂,面前桌子上有个瓶子,每次训练开始时,瓶子被随机放在桌上的某个位置,同时随机给定一个目标位置。机械臂需要把瓶子推到或者抓到目标位置,才算成功。

这个任务如果用稀疏奖励来定义,那就是:当瓶子和目标的距离小于某个阈值(比如 5 厘米)时,奖励为 1,否则奖励为 0。从直觉上你就能感受到,机械臂一开始连怎么动都不知道,全靠随机探索,它几乎不可能一上来就刚好把瓶子推到 5 厘米以内的位置。所以训练初期,智能体收到的几乎全是全零奖励,没有任何有效梯度,模型就只能原地打转。

很多刚入门的朋友会问:“为什么不直接加一个中间奖励,比如让奖励和距离成反比?”这确实是一种办法,叫奖励塑形(reward shaping),但它有一个隐患:你设计的中间奖励可能会让智能体找到一个“偷懒”的策略,比如绕过目标、原地抖动或者把瓶子推得更远反而拿到更高分数。奖励塑形做得好不好,非常依赖设计者的先验知识,而且不同任务要单独设计,不够通用。HER 这条路线的核心诉求,就是在不手动设计中间奖励的前提下,让智能体从稀疏奖励里学会一个可用的策略。

1.3 HER的核心思想:学不会的目标,就换一个能学会的

HER 的思路用一个生活化的类比来解释再合适不过:就像一个学生考试,目标是想考全班第一(这是原始目标),但他第一次只考了第二十名。如果只看目标,这次考试是完全失败的。但聪明的人不会把这次失败当作毫无价值的经验,而是会想:“虽然我没考到第一,但我现在的能力水平大概就在第二十名左右,那我先以‘考进前二十’为目标复盘一下,看看哪个科目拖了后腿。”也就是说,他会把这次“失败”的结果当作一个已经达成的次级目标,从中提取有价值的学习信号。

HER 做的事情本质上就是这样:在智能体尝试完成目标 g 却失败后,我们不把这个 episode 扔掉,而是把它的实际终点状态当作另一个目标 g' 重新放回经验池。因为智能体“实际到达”了 g',所以这个 episode 在 g' 这个目标下是“成功”的,奖励是 1,梯度就是有效的。虽然这个经验不是朝着原始目标 g 的方向,但它能让智能体学到“从当前状态到 g' 需要采取什么动作”,这个知识累积起来,就会让智能体逐渐学会操作物体的基本动力学,最终一步步逼近原始目标。

这个想法看起来简单到有点“作弊”,但它背后的直觉非常深刻:失败的经验里其实藏着成功的线索,关键在于换一个角度去看待它。在强化学习里,这个“换角度”就是目标重标记(goal relabeling),也就是 HER 的核心机制。后面我会详细拆解它的实现细节。

2. HER的原理拆解:从后见之明到事后经验回放

2.1 标准经验回放与它的局限

先回顾一下标准经验回放(Experience Replay)的基本流程。强化学习训练过程中,智能体跟环境交互产生一条条经验,格式一般是四元组 (状态 s, 动作 a, 奖励 r, 下一状态 s')。我们把每条经验存进一个固定大小的缓存里,训练时从中随机采样一个小批量来更新网络。这个机制的好处是打散了样本之间的时间相关性,让数据更接近独立同分布,这对神经网络的稳定性非常重要。

但在目标条件化强化学习(Goal-Conditioned RL)中,经验还要额外带上一个目标 g,格式变成 (当前状态 s, 目标 g, 动作 a, 奖励 r, 下一状态 s')。这时你会发现问题:如果目标 g 只来自训练开始时环境随机分配的那个真实目标,那么绝大多数经验里的奖励 r 都是 0,经验池里“有效学习信号”的比例极低,采样效率自然就上不去。

本质问题在于,传统经验回放把“失败”的经验一律视为无价值数据,直接丢弃或让它稀释在大量无效数据里。HER 做的事情就是打破这个偏见,失败经验本身也可以是宝贵的学习材料。

2.2 目标重标记:HER的灵魂操作

HER 的具体操作可以拆成三步:

  1. 智能体在环境里从初始状态出发,带着目标 g 执行策略,直到 episode 结束,期间记录整条轨迹状态、动作、奖励;
  2. 轨迹结束后,遍历这条轨迹上的每一个时间步,对每个步的经验,把它的原始目标 g 替换成一个“事后目标 g'”,这个 g' 通常是这条轨迹最终到达的状态 s_T,或者也可以选轨迹中某个未来的状态;
  3. 根据替换后的目标重新计算奖励,然后将“重标记后”的经验重新存进经验池,之后的训练更新就和普通经验回放没有区别了。

第二步里有一个关键操作,是计算新奖励。因为我们设定的奖励函数通常是“到达目标则 +1,否则 0”,所以重标记后,新的奖励可以直接判定为 1,因为轨迹的最终状态 s_T 与 g' 本来就是同一个东西(或者至少是同一个物体在不同时刻的位置),距离必然小于阈值。这就是 HER“凭空”创造稀疏训练信号的核心手段。

需要注意的是,这里有一个微妙的点:我们用轨迹终点作为事后目标,那么沿轨迹越早的状态,距离这个目标的距离就越大,这反而提供了一个天然的“学习梯度”——早期状态离目标远,晚期状态离目标近,智能体能从中学习到“朝这个方向的运动会导致状态逐步接近某个目标”,这正是策略学习需要的信号。

2.3 数学表述:和Hindsight损失

严格点说,HER 并不改变强化学习的数学目标。它只是改变了训练数据的分布。原本我们要优化的目标函数是:

J = E_{(s,g,a,r,s') ~ D} [Q(s,g,a) 的某种回归误差]

其中 D 是从经验池里采样出来的经验分布。HER 做的是把一部分样本的 g 换成了 g',并重新计算奖励,然后照常计算损失。由于 g' 是根据事后状态生成的,它天然满足奖励非零条件,所以损失函数的监督信号密度大大提高了。

用公式来表达重标记后的奖励:

r'(s_t, g', a_t, s_{t+1}) = 1_{||f(s_{t+1}) - g'|| < ε}

这里 f 是从状态中提取物体位置信息的函数,ε 是成功阈值。因为我们选择 g' = f(s_T) 且 t+1 = T 时距离为零,所以最后一步奖励一定是 1,而更早的步则视情况而定,可能为 0 也可能为 1(如果策略已经在中间某步就到达了终点,则之后的步都有奖励)。

在实现中,我们还面临一个选择:每个 episode 里到底重标记多少条经验?OpenAI 论文里提出了四个选项,分别是最终状态(final)、未来状态(future,取未来一个随机时间步的状态)、episode 内的随机状态(episode)、以及完整轨迹的随机状态(random)。他们在实验里发现,future 策略表现最好,因为 final 策略只重标记到最终状态,目标多样性有限,而 future 策略为每个经验步提供了一个不同的、且满足时序一致性的目标,数据多样性更高,学习效率也更好。

2.4 为什么HER有效:从奖励密度看本质

要理解 HER 为什么有效,可以把训练过程想象成在教学一个小孩投篮。你每次让小孩随便把球扔出去,然后要求他必须投中篮筐才给奖励,那这个小孩根本学不会,因为大部分尝试连筐都碰不到。但如果你换一种玩法:不管他球扔到哪里,你就说“这次的目标就是那个位置”,然后根据距离篮筐的远近给他一些反馈,那他很快就能掌握“扔出去的方向和力度与落点之间的关系”。

HER 在数学上的效果等价于在经验池中大幅提高了非零奖励样本的占比。在原始经验里可能只有 0.1% 的样本有非零奖励,经过重标记后这个比例可以提高到接近 100%(对重标记部分而言),这就直接解决了梯度消失的问题,让 Q 函数和策略网络都能获得足够的更新方向。

另外还有一点容易被忽略:HER 其实是在“自己给自己出题”。原始目标只有一个,但重标记后的目标可以有很多个,相当于变相扩大了训练数据的覆盖范围,提升了目标空间的探索多样性。这对泛化能力很有帮助,模型见到过更多不同的“目标-轨迹”组合,后面遇到新目标时就不会那么手足无措。

3. 实操过程:在真实模型中实现HER

3.1 环境选型:用Gym的Fetch系列做实验

理论聊完了,下面进入实操环节。要用 HER 做实验,最经典的环境就是 OpenAI Gym 里的 Fetch 系列任务,比如 FetchPush、FetchPickAndPlace、FetchReach、FetchSlide。这些环境的共同特点是:七自由度机械臂、随机初始物体位置、随机目标位置、稀疏奖励(成功给 0 的负奖励,失败给 -1,用 Gym 惯用的“越低越好”表达方式)。

FetchReach 是最简单的版本,只有机械臂末端需要到达目标点,没有物体操作。FetchPush 需要推动一个方块到目标位置,FetchPickAndPlace 需要抓取方块再放到目标位置,FetchSlide 则需要击打方块滑动到目标,难度递增。我自己一般建议初学者先跑 FetchReach 验证代码流程,再去跑 FetchPickAndPlace 体验一下真正的稀疏奖励难度。

安装环境的方式很简单,用 pip 安装 gym 和 mujoco_py,再根据官方仓库安装 gymnasium-robotics(或者老版本用 gym 的 robotics 套件)。需要提醒的是,老版本 gym 和新版本接口差异很大,网上很多老教程用的是 gym 0.20 以下的版本,如果你用的是新环境 API,会遇到 terminated/truncated 的问题。建议直接用较新的 gymnasium-robotics,文档和代码都更清晰。

3.2 算法骨架:DDPG+HER的基础搭配

HER 不是一个独立的强化学习算法,它是一个经验重放模块,需要挂在一个基础的强化学习算法上使用。最常见的搭配是 DDPG(Deep Deterministic Policy Gradient),因为 Fetch 系列环境都是连续动作空间,DDPG 处理起来比较自然。当然也可以用 TD3、SAC,甚至 PPO 也能配 HER 用,但通常 off-policy 算法配合经验回放更自然,效果也更好。

整体框架分四块:Actor 网络、Critic 网络、经验池和 HER 重标记逻辑。Actor 负责根据当前状态和目标输出动作,Critic 负责评估“在当前状态和目标下采取某动作的价值”。HER 则常驻经验池,负责在轨迹结束后生成重标记数据。

我对这套框架的建议是,不要在代码结构上把 HER 写死在 DDPG 里,而是把 HER 作为一个独立的数据处理模块,这样后续切换算法或者调整重标记策略会方便很多。我自己的项目里,经验池提供一个 add_episode 方法,每次 episode 结束后统一在里面做 HER 重标记,其他算法模块完全不需要感知 HER 的存在。

3.3 关键代码解读

这里给出一段简化版的 HER 经验池核心逻辑,使用 Python 伪代码风格,可以直接套进你自己的项目里。

import numpy as np from collections import deque class HERReplayBuffer: def __init__(self, capacity, relabel_strategy='future', k=4): self.buffer = deque(maxlen=capacity) self.relabel_strategy = relabel_strategy self.k = k # 每条原始经验重标记生成的新经验条数 def add_episode(self, episode): # episode 是一个字典,包含键: # obs: list of (goal_obs, achieved_goal) # actions: list # next_obs: list # rewards: list # dones: list T = len(episode['actions']) # 普通经验直接入池 for t in range(T): self.buffer.append({ 'obs': episode['obs'][t], 'act': episode['actions'][t], 'rew': episode['rewards'][t], 'next_obs': episode['next_obs'][t], 'done': episode['dones'][t] }) # HER 重标记 achieved_goals = [obs_dict['achieved_goal'] for obs_dict in episode['obs']] for t in range(T): # 根据策略选择事后目标 if self.relabel_strategy == 'final': new_goal = achieved_goals[-1] elif self.relabel_strategy == 'future': # 从 t 之后的时间步里随机选一个 future_indices = np.arange(t, T) if len(future_indices) > 0: future_idx = np.random.choice(future_indices) new_goal = achieved_goals[future_idx] else: new_goal = achieved_goals[-1] elif self.relabel_strategy == 'episode': new_goal = np.random.choice(achieved_goals) else: raise ValueError('Unknown relabel strategy') # 替换目标并重新计算奖励 # 这里注意:obs 和 next_obs 需要替换其中的 goal 部分 new_obs = replace_goal(episode['obs'][t], new_goal) new_next_obs = replace_goal(episode['next_obs'][t], new_goal) # 计算新奖励,这个函数根据你的环境距离阈值来写 new_rew = compute_reward(new_next_obs['achieved_goal'], new_goal) # 新经验入池,k=4 表示每条原始经验生成 4 条重标记经验 for _ in range(self.k): self.buffer.append({ 'obs': new_obs, 'act': episode['actions'][t], 'rew': new_rew, 'next_obs': new_next_obs, 'done': episode['dones'][t] })

这段代码里有几个细节值得展开。第一个是 replace_goal 函数,在 Fetch 环境里,观测是一个字典,包含 observation(机械臂自身状态)和 achieved_goal(物体实际位置)。重标记核心思路是保持状态不变,只把目标 key 从原始 goal 换成 new_goal。

第二个细节是 compute_reward 的重新计算。在 Gym Fetch 里,原始奖励函数是距离阈值判断,我们重标记后需要重新走一遍这个判断。其实由于 new_goal 就是某个 achieved_goal,这个奖励大概率是 0(成功)或者 -1(失败取决于阈值定义),不用太担心精度问题。

第三个细节是 k 这个超参数。每条原始经验生成 k 条重标记经验,这个 k 直接影响经验池数据比例。OpenAI 论文里用的 k=4,实际用下来也还可以。如果你显存或内存压力大,可以降到 2,效果差距不会特别明显,但训练速度会快一些。

3.4 超参数设置一栏表

在我跑过的实验里,下面这套超参数的稳定性和效果都比较均衡,可以作为起始点参考:

超参数推荐值备注
HER 重标记策略future多种策略里泛化性最好
每条经验重标记次数 k4显存/内存紧张可降为 2
经验池容量10^6 条越大越好,但注意内存占用
Actor 学习率1e-3调小可能更稳,但收敛慢
Critic 学习率1e-3两边保持一致省心
目标网络更新系数 τ0.05相对大一点,贴近实际常用值
探索噪声 σ0.2(高斯噪声)配合 OU 噪声也可以,但效果差距不大
Batch size256显存允许可以更大,比如 512
折扣因子 γ0.98Fetch 任务一般不需要太大
每 episode 最大步数50Fetch 环境默认配置
训练总步数10^6 量级FetchPickAndPlace 通常 50-80 万步内能看出效果

这套参数不是固定答案。我在不同环境、不同随机种子下跑过几次,波动主要在收敛快慢上,但最终都能稳定到一个可行策略。这里需要特别强调随机种子的重要性,HER 虽然算法本身比较稳定,但 DDPG 这类 off-policy 算法对种子还是敏感的,建议每次实验固定三个以上种子,取平均曲线来评估,不然很容易被单次实验的方差误导。

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

4.1 模型不收敛,先查这四处

跑了很久都不收敛,可能是哪里出了问题?我用 HER 这一年多,遇到过的问题集中在四个地方:

**第一处是观测里目标没替换对。**这是最隐蔽的 bug。HER 重标记的核心就是替换目标,如果你在重标记时忘了同步替换 next_obs 的目标,或者替换时用了错误的索引,训练过程会表现得时好时坏,看起来像模型在“抽风”,但代码不报错。我排查这种问题的方法很笨但很有效:在训练前期把经验池里随机抽几条重标记经验,手动打印它的目标、下一状态和计算出的奖励,核对奖励是否跟目标距离一致。

**第二处是奖励函数阈值跟环境不匹配。**Fetch 环境的成功阈值是 0.05 米,也就是物体和目标位置的距离小于 5 厘米才算成功。如果你自己实现了 compute_reward,用了 0.1 甚至更大的阈值,那奖励密度会偏乐观,模型学到的策略精度达不到真实要求;反之阈值太小,例如 0.01,那重标记产生的成功信号也变少了,收敛速度会明显下降。跟环境默认保持一致就好。

**第三处是噪声过大导致策略震荡。**我刚开始用 DDPG 时,为了让探索更充分,把高斯噪声标准差设成了 0.5,结果训练曲线震荡得像过山车,策略一直无法稳定逼近目标。后来发现 0.2 足够用了,而且 HER 重标记已经人为增加了大量“成功”样本,探索需求没有想象中那么大。如果噪声太大,会让模型误以为动作空间里有很多无效动作,从而影响 Q 值的收敛。

**第四处是网络容量不足。**Fetch 类任务的观测维度不高(大约 25 维左右,其中包含物体位置、机械臂关节角等),但如果你把目标向量也拼进去,维度还是可控的。如果你的任务状态空间很大,比如像素输入,那普通的 MLP 就不够了,要换成卷积结构或者先加一个 encoder。不要指望 HER 能凭空解决表征问题,它解决的是奖励密度问题,不是感知问题。

4.2 关于噪声、目标网络更新和回放比例的细节

经验池里原始经验和重标记经验的比例很重要。我刚跑 HER 时犯过一个典型错误:重标记经验的比例过高,比如 k=8,导致经验池里几乎都是重标记数据,原始目标的经验被稀释得没剩几条。结果模型学会了“把物体推到某个随机位置”的能力,却学不会“把物体推到指定位置”的泛化能力,表现在测试时成功率很低。后来把 k 调回 4,并保证采样时原始经验和重标记经验各占大约一半,问题就解决了。

目标网络更新频率也是一个容易被忽视的细节。DDPG 里 Critic 有目标网络,用来稳定 Q 值计算的。如果你 τ 设得太大,比如 0.1,目标网络更新过快,相当于每步都在追着当前网络跑,稳定性就差;设得太小比如 0.001,学习速度又太慢。0.05 是我试过多次后比较舒服的平衡点。

探索噪声在 HER 里的角色也有点微妙。理论上既然有重标记样本提供学习信号,探索噪声好像就该小一点,但实际跑下来我发现,训练前期噪声还是要保持住一些,因为重标记样本虽然能教智能体“从状态 A 到状态 B 该怎么做”,但前提是智能体得先有足够多样的状态 B 可选。如果噪声太小,轨迹终点覆盖的状态空间就太集中,重标记样本的多样性也就差了。我一般在训练前 30% 阶段用 0.2 噪声,后面逐步衰减到 0.05 甚至 0.01,效果会比全程固定好一些。

4.3 在复杂场景中HER的改动

HER 也不是万能的。如果你的任务目标不是“到达某个状态”,而是“完成某个不可逆的操作”,比如拧开瓶盖、组装零件,HER 的重标记逻辑就需要仔细考虑。因为这类任务不管你最后做到什么程度,都没法简单地说“这次的目标就是当前状态”,操作的过程和结果之间的因果关系非常复杂。这种情况下,单纯依赖状态距离来判断目标是否达成就不够用了,可能需要引入辅助的状态描述或逆模型来辅助重标记。

另一个常见变体是分层目标。比如在导航任务里,目标可以是“到达某个房间”而不是“到达某个精确坐标”,HER 的重标记粒度就要做调整。有些研究者会把 HER 和 HRL(分层强化学习)结合起来,先是高级策略选一个子目标,低级策略用 HER 来学习如何逼近这个子目标,这样效果会好很多。

还有一种情况是目标空间和状态空间不一致。比如目标是“物体的最终位置”,但状态里包含机械臂关节角等无关信息。重标记时一定要明确目标只与物体位置有关,替换目标时只改物体位置部分,其他状态保持原样。我在做抓取任务时,因为把机械臂的关节角也当成目标的一部分来更新,导致重标记目标完全失真,模型久久不收敛。都是血泪教训。

5. 一点个人经验与后续思路

最后分享一点我在实际使用中的体会。HER 之所以吸引我,是因为它用一种极其朴素的方式破解了一个表面复杂的问题:不设计额外奖励函数、不修改环境、不增加额外学习阶段,只需要在你已有的经验池里做一次“目标重标记”,就让原本学不动的稀疏奖励任务变得可学。这种“换个角度看数据”的思路,在深度学习时代尤其值得借鉴,很多时候我们缺的不是更复杂的模型,而是更好的数据组织方式。

如果你正在做机器人操作、导航或者任意一个带明确目标状态的连续控制任务,HER 应该进入你的首选方案清单。我的建议是不要在仿真里纠结太久,先在简单环境里跑通代码,再逐渐加大难度。附带提一句,在仿真环境里验证过的 HER 策略,迁移到真实机器人上通常还需要额外的 domain adaptation 工作,但它至少能让你的仿真训练阶段省下大量时间。做实验时多固定几个种子、多记录几条曲线,你会对 HER 的真实表现有更准确的判断。

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

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

立即咨询