如果让我用一句话形容 hindsight 这个词,我会说它是人类最擅长、也最容易被它误导的能力。考试对答案时一拍大腿“我本来会的”,项目复盘时愤愤不平“我当时就该想到”,这些全是后见之明。但有意思的是,到了强化学习领域,一群工程师却把“后见之明”当成一种正经算法来用,并且真的解决了一个让无数研究者头疼多年的难题。这篇文章就聊这个算法——Hindsight Experience Replay(HER)。它怎么把 AI 的“失败轨迹”变成优质训练数据,为什么这个思路在稀疏奖励环境下能大幅提升学习速度,以及我自己实现它时踩过的那些坑。
先给不熟悉强化学习的读者一个定位:HER 不是一个全新的强化学习算法,它是对经验回放(Experience Replay)机制的一种改造,可以叠加在 DDPG、DQN、SAC 等算法之上。论文来自 OpenAI,2017 年发表,标题就叫《Hindsight Experience Replay》。它解决的核心问题是“稀疏奖励”——也就是智能体在绝大多数时间里拿到的奖励都是 0,只有最终成功那一瞬间才拿到 +1。这种情况用常规算法几乎学不动,而 HER 给出的思路和人类“事后复盘”高度相似:这次我没抓到目标物,但我至少学会了把手臂伸到那个位置,那这段经验也不能浪费,完全可以换个目标重新理解。
无论你是做机器人控制、游戏 AI、推荐系统,还是单纯对“从失败中学习”这个话题感兴趣,这文章都适合你。我会从问题痛点讲起,再拆解 HER 的原理,最后给出一份能在本地跑起来的最小实现和排错指南。整个过程我会尽量用大白话,把那些论文里一笔带过但实际调参才知道的细节也一并交代清楚。
1. 强化学习里最头疼的一件事:稀疏奖励
1.1 什么是稀疏奖励,为什么它让算法“学不动”
先看一个场景。假设你想教一个机械臂学会“把桌上的红色方块抓起来放到目标位置”,机械臂每走一步,如果没抓成功,奖励是 0;只有方块被准确放到目标点的那一帧,才奖励 +1。这就是典型的稀疏奖励问题:整个回合可能持续 50 步、100 步,但真正拿到非零奖励的只有最后一步。
你可能觉得这没什么,最后给个大奖励不是很好吗?但对强化学习算法来说,这是一个致命问题。绝大多数现代强化学习算法依赖“奖励信号”来更新策略,奖励越稀疏,算法能从环境里获得的有效反馈就越少。以常用的策略梯度方法为例,它的梯度估计是围绕“高奖励动作出现的概率”来计算的,如果整个回合只有最后一个动作关联到奖励,那么前面几十个动作和最终成功之间的因果链条会变得极其难以估计。说得更直白一些:算法根本不知道到底哪一个动作起到了关键作用,整条轨迹的所有动作都会被平等地放大或缩小概率,最后就变成了瞎猜。
用寻宝来类比更直观:你在一个巨大的迷宫里找宝藏,每走一步什么反馈都没有,只有踩到宝藏那一下才听到“叮”。这时候一个随机游走的智能体在几百步内踩中宝藏的概率几乎为零,它就永远听不到那声“叮”,也就永远不知道该往哪走。很多入门项目里大家喜欢用 CartPole、Pong 这类环境,它们要么奖励密集、要么奖励虽然延迟但至少能观察到分数变化,这就掩盖了稀疏奖励的残酷性。一旦换成机器人抓取、导航、化学分子设计这类真实场景,稀疏奖励立刻成为横在理论和应用之间的大山。
1.2 过去人们怎么解决:奖励塑形与课程学习,以及它们的局限
面对稀疏奖励,研究者最先想到的思路是“手工设计中间奖励”,这叫奖励塑形(Reward Shaping)。抓取任务里,你可以给机械臂加一条规则:手指离目标物越近,奖励越大。听起来很合理,但实际做起来问题很多。第一,这需要大量领域知识,你得知道“距离”是好的度量,还得确定用什么距离函数、权重怎么设;第二,手工设计的奖励很容易产生钻空子的行为,也就是评论里经常说的 reward hacking。比如你奖励机械臂靠近目标物,它可能学会把手伸到目标物旁边但永远不抓,因为“靠近”这个行为本身已经在给它刷分了。著名的“吹风机把船吹到岸边”梗就属于这一类,智能体发现了规则漏洞后,会走一条你完全想不到的捷径。
另一个常用思路是课程学习(Curriculum Learning):先让智能体在简单任务上训练,再逐步加大难度。比如抓取时,先让目标物固定在同一位置,等学会了再随机换位置。这个思路本身是成立的,但问题在于“课程”需要人来设计,你得判断什么时候该加大难度、加大多少。而任务一旦复杂,人工安排课程本身就成了一件需要耗费大量精力的事。而且很多课程设计是启发式的,换个环境就不一定适用了,泛化性很差。HER 的出现正是在这个背景下:与其费尽心思去设计中间奖励或课程,不如换一个角度问一句——那些“失败”的轨迹里,是不是本身就藏着可以作为训练信号的“目标”?
1.3 换个思路:失败里其实有金子
这里要聊一个生活化但非常本质的观察。回想你小时候学投篮,你瞄准篮筐投了几十次,几乎全没进。但从另一个角度看,你的每一次投篮不是“什么都没学到”——你至少知道了“把球从这里、用这种力度抛出去,球会落到那个位置”。换句话说,每一次投偏的球,其实都精确地教会了你一种“如何把球投到一个确定位置”的映射关系。
HER 的动机正是如此。我们通常定义任务时,只关心“是否达到预设目标”。但在训练过程中,智能体每个时刻实际上都会到达某个状态——比如机械臂末端此刻在什么位置、手指张开的程度如何。这个实际到达的状态本身,就是一个可学习的“目标”。如果某个轨迹没有完成任务,我们就把它改写成“以轨迹实际到达的某个状态作为目标”的成功轨迹。这样一来,原本奖励全为 0 的一段数据,经过改写后变成了一段带有正奖励的、可以指导策略的有效数据。这就是“后见之明”在算法里的具体操作:我们拿已经发生的结果,去修正过去对目标的理解。这个思想听着轻巧,但它直接绕开了手工设计奖励和人工编排课程的难题,把失败轨迹的利用率成倍地拉高。
2. HER 的核心原理:目标重新标注
2.1 一个例子看清 HER 在做什么
假设我们在训练一个机械臂,目标是到达位置 G(比如三维坐标 [0.3, 0.2, 0.5])。某次回合中,机械臂从初始位置出发,最后实际到达了位置 A = [0.2, 0.8, 0.4],计算目标距离后判定为失败,奖励是 -1 或者 0(取决于环境设计)。按照传统经验回放的写法,这条轨迹里所有 transition 都会被当成“失败样本”存起来,它们对应的奖励都是否定性的,策略从中学到的只是“这条路走不通”,信息量非常有限。
HER 的做法是在这个轨迹存入回放池时,额外做一次改写:把轨迹的目标从 G 替换成 A。替换之后,机械臂每一步的行动其实都是在“朝着 A 逼近”的,而它在回合结束时也确实到达了 A,所以这条轨迹从一个“失败轨迹”变成了一条“成功轨迹”,每一步都可以从环境奖励函数里重新计算出一个正奖励或者零奖励,具体看环境设定。这样,一条本来只能提供负样本的轨迹,经过改写后同时提供了正样本,整个回放池里可学习的信号密度就大幅上升了。
我在实际实现时发现,最容易理解 HER 的方式就是把“目标”从环境定义里剥离出来。环境中除了 observation 之外,通常还有一个字段叫 achieved_goal,表示智能体当前实际达到的状态。很多入门教程会忽略这个字段,但它恰恰是 HER 的关键。原始 transition 里记录的 desired_goal 是环境要求的 G,而 achieved_goal 是智能体实际的结果,HER 做的就是成对地修改 desired_goal 与对应的 reward,让原本“失败”的 step 变成“成功”的 step。
2.2 数学表达与伪代码,以及三种目标采样策略
形式化一点,HER 适用的任务可以描述为“以目标为条件的强化学习”(goal-conditioned RL)。此时策略不再是 π(s) → a,而是 π(s, g) → a,也就是说策略的输入除了当前状态外,还有一个目标向量。环境里设定了一个二进制奖励函数 r(s, g),当 s 与 g 足够接近时返回 0 或 +1,否则返回 -1 或 0。
标准经验回放里,一条 transition 形如 (s_t, g, a_t, r_t, s_{t+1}),其中 g 是回合开始时就固定的目标。HER 的关键操作是“目标重标注”:从回放池中取出若干个 transition 后,把里面的目标 g 替换成另一个目标 g',然后重新计算奖励,形成新的 transition (s_t, g', a_t, r't, s{t+1})。这里最核心的问题就是:怎么选择新的目标 g'?
论文对比了几种策略,最常用的有三种:
| 策略名称 | 做法 | 特点 |
|---|---|---|
| final | 只选取轨迹最后一个时刻的 achieved_goal 作为新目标 | 实现最简单,但一条轨迹只产生一个额外样本 |
| random | 从整条轨迹中均匀随机选一个时刻的 achieved_goal | 覆盖面广,但可能选到与当前动作毫无关联的状态 |
| future | 从当前时刻之后的未来时刻里选一个 achieved_goal | 保证目标在时间顺序上“可达”,与当前动作有因果联系 |
论文经过实验对比后发现 future 策略效果最好,这也是我实际用下来最稳的策略。它为什么会赢?可以从因果性的角度理解:机械臂在 t 时刻做的动作,最多只能影响 t 时刻之后的状态。如果选一个过去时刻的状态作为目标,这个目标和当前动作之间根本没有因果联系,学出来的梯度自然是噪声。而 future 策略选的是“未来某个时刻实际会到达的状态”,这个状态确实是由当前动作及后续动作共同作用出来的,用它来给当前 transition 重新标注目标,逻辑上是通顺的。当然,这里的因果链条也延伸到未来多个时间步,不完全是当前动作单独决定的,但在大部分任务里已经足够有效。
伪代码层面,HER 的核心就是一条对轨迹的“重标注”函数:
def relabel_episode(episode, future_k=4): """ episode: 一条完整轨迹,包含若干 transition 每个 transition 是 dict,至少包含: obs: 普通观察 achieved_goal: 实际到达的目标状态 desired_goal: 预设目标 act: 动作 reward: 原奖励 next_obs: 下一步观察 next_achieved_goal: 下一步实际到达的目标状态 future_k: 每个 transition 额外重标注的次数 """ relabeled = [] horizon = len(episode) for t, trans in enumerate(episode): relabeled.append(trans) # 原始 transition 保留 if t < horizon - 1: # future 策略:从 t+1 到轨迹末尾中采样 future_k 个时刻 future_timesteps = np.random.choice( np.arange(t + 1, horizon), size=min(future_k, horizon - t - 1), replace=False ) for ft in future_timesteps: new_goal = episode[ft]['achieved_goal'] new_reward = compute_reward(new_goal, new_goal) relabeled.append({ 'obs': trans['obs'], 'achieved_goal': trans['achieved_goal'], 'desired_goal': new_goal.copy(), 'act': trans['act'], 'reward': new_reward, 'next_obs': trans['next_obs'], 'next_achieved_goal': trans['next_achieved_goal'] }) return relabeled这段代码里 compute_reward 需要你自己根据环境定义。在 OpenAI Gym 的 Fetch 系列环境里,奖励规则是成功为 0、失败为 -1,也就是:
def compute_reward(achieved_goal, desired_goal, threshold=0.05): d = np.linalg.norm(achieved_goal - desired_goal, axis=-1) return (d < threshold).astype(np.float32) - 1.0注意这里用 float32 而不是 int,因为在训练神经网络时,int 型奖励会在反向传播时触发类型不匹配问题,这是新手很容易踩的坑。
2.3 为什么这个简单技巧这么有效
从表面上看,HER 只是“把失败数据复制一份改个标签”,似乎没有增加任何新信息,为什么它能带来那么大的提升?我认为有三层原因。
第一层是奖励密度。这是最直接的收益。原本一条 100 步的轨迹中只有最后一步可能得到非零奖励,但经过目标重标注后,这条轨迹里的每一个 transition 都可能变成成功样本,因为重标注的目标是从轨迹实际达到的状态里选出来的,按定义,轨迹末端一定能达到那个目标。换句话说,HER 把“从环境中获得奖励”的难度从“要碰运气摸到预设目标”变成了“反正总能找到一个实际到达的目标”,奖励信号从天而降,密集度完全不同。
第二层是数据利用率。在同一条轨迹上,HER 可以通过多次重标注产生多份不同的样本,每个样本对应不同的目标。这样回放池中的样本量成倍增加,网络的更新次数也随之增加。我在实验中感受到的差异是,没有 HER 的智能体训练 20 万步可能一点进展都没有;加了 HER 之后,训练 2 万步就已经能看到明显的成功率上升。数据利用率不是一个虚的概念,它直接决定了你训练一个模型要花费多少时间和算力。
第三层是策略的泛化能力。传统方法只会针对预设目标 g 产生数据,所以策略只在 g 附近有判断力。而 HER 产生的数据覆盖了各种各样“实际到达过”的状态,这些状态大概率不在预设目标附近。策略被逼着学会从不同起始状态、不同轨迹中走向不同目标,而不是死记硬背某一条通往目标的路。这一点对真实机器人特别重要,因为真实环境里目标的分布千变万化,我们希望策略学到的是一个泛化的“能力”,而不是某几个固定目标的专用路径。归根结底,HER 不是在给你增加信息,而是在帮你把已经存在但未被利用的信息纳入学习循环,这个视角在强化学习里是很少见的,因为它反直觉地告诉你:失败的数据跟成功的数据一样值钱。
3. 实操:从零实现一个 HER 训练 Demo
3.1 环境选型与工具准备
理论说再多,不如动手跑一遍。这里我推荐用 OpenAI Gym 里的 Fetch 系列环境,尤其是 FetchReach-v1 和 FetchPush-v1。原因很简单:它们就是 HER 论文里使用的评测环境,里面的 observation、achieved_goal、desired_goal 字段结构都是现成的,连奖励函数的定义都是按照“二值稀疏奖励”来设计的,你不需要自己写环境脚本,省掉很多理解成本。
安装方式很直接:
pip install gymnasium pip install gymnasium-robotics注意一点,老版本的 gym 和 mujoco_py 在 Python 3.10+ 上经常出现编译问题,如果你是新环境,我建议直接用 gymnasium 生态,API 兼容性更好,安装也省心。装好之后可以用下面这段代码快速验证环境是否正常:
import gymnasium as gym import gymnasium_robotics env = gym.make('FetchReach-v1') obs, info = env.reset(seed=0) print(obs.keys()) print('observation shape:', obs['observation'].shape) print('achieved_goal shape:', obs['achieved_goal'].shape) print('desired_goal shape:', obs['desired_goal'].shape)在 FetchReach 中,observation 是 25 维(19 维关节与状态信息 + 3 维 achieved_goal + 3 维 desired_goal),achieved_goal 是机械臂末端的 3 维坐标,desired_goal 也是 3 维坐标,动作是 4 维的力矩控制。这个环境的目标是让末端移动到指定位置,任务相对简单,非常适合用来验证 HER 的实现是否正确——如果在这个环境上都学不动,那大概率是你的实现有问题,而不是环境太难。
3.2 核心代码:HER 采样逻辑的完整实现
前面那段 relabel_episode 是简化示意,真正要接入强化学习训练流程,还需要一个能按需采样并重标注的回放 Buffer。我写了一个极简版本,核心逻辑都在注释里,你可以直接拿去做学习和二次开发:
import numpy as np from collections import deque class HERReplayBuffer: def __init__(self, capacity=100000, future_k=4): self.buffer = deque(maxlen=capacity) self.future_k = future_k def add_episode(self, episode): """ episode 是一个 list,其中每个元素是一个 dict: obs, achieved_goal, desired_goal, act, reward, next_obs, next_achieved_goal 添加原始轨迹,并进行 HER 重标注后统一放入回放池。 """ horizon = len(episode) for t, trans in enumerate(episode): # 原始 transition self.buffer.append(trans) # HER:future 策略 if t < horizon - 1: candidates = np.arange(t + 1, horizon) num_goals = min(self.future_k, len(candidates)) future_steps = np.random.choice(candidates, size=num_goals, replace=False) for ft in future_steps: new_goal = episode[ft]['achieved_goal'] new_reward = self._compute_reward(new_goal, new_goal) new_trans = { 'obs': trans['obs'], 'achieved_goal': trans['achieved_goal'], 'desired_goal': new_goal.copy(), 'act': trans['act'], 'reward': new_reward, 'next_obs': trans['next_obs'], 'next_achieved_goal': trans['next_achieved_goal'] } self.buffer.append(new_trans) def sample(self, batch_size): indices = np.random.choice(len(self.buffer), size=batch_size, replace=False) batch = [self.buffer[i] for i in indices] obs = np.stack([b['obs'] for b in batch]) goals = np.stack([b['desired_goal'] for b in batch]) acts = np.stack([b['act'] for b in batch]) rewards = np.stack([b['reward'] for b in batch]) next_obs = np.stack([b['next_obs'] for b in batch]) return obs, goals, acts, rewards, next_obs @staticmethod def _compute_reward(achieved_goal, desired_goal, threshold=0.05): d = np.linalg.norm(achieved_goal - desired_goal, axis=-1) return (d < threshold).astype(np.float32) - 1.0这个 Buffer 的写法和常规经验回放最大的区别有两点。第一,它不接收单个 transition,而是接收一整条 episode,因为重标注需要看到轨迹未来的信息;第二,它会在添加原始样本的同时生成额外的重标注样本。训练时,采样得到的 rewards 和 goals 必须保持对应关系,千万不要只修改 reward 不修改 goal,否则策略会对着一个它没见过的 goal 学习奖励,整个训练就乱了。
配合底层算法时,我以 DDPG 为例说一下数据流向:策略网络 actor 输入是 obs 和 desired_goal 的拼接(在 FetchReach 里是 25 维),输出是 4 维动作;critic 输入是 obs、desired_goal 和 action 的拼接,输出 Q 值。每次从 HERReplayBuffer 里 sample 一个 batch 后,把它喂给 actor 和 critic 做常规更新即可。DDPG 之外,你换用 SAC 也可以,原理没有区别。
3.3 训练效果、参数选择与我的观察
我在本地用 FetchReach 跑过一组对比实验,一组用普通 DDPG(replay buffer 只存原始 transition),一组用 DDPG + HER。两组共用相同的网络结构和优化器超参,随机种子也保持一致,只差 replay buffer 的采样逻辑。
对比结果非常直观。普通 DDPG 在 100 个 epoch(每 epoch 50 个 rollout)内成功率几乎没有超过 5%,基本可以认为没学会。DDPG + HER 在 30 个 epoch 后成功率开始爬升,到 80 个 epoch 左右能稳定达到 90% 以上。这个差距不是调参能补上的,因为普通 DDPG 在稀疏奖励环境里拿到的基本都是全零或者全负奖励,梯度信号本身就形同虚设,而 HER 让回放池里始终存在足够比例的正样本,q 值估计才能朝正确的方向走。
训练的具体参数,我沿用论文里的推荐值并做了一点微调:
| 参数 | 推荐值 | 说明 |
|---|---|---|
| future_k | 4 | 每个 transition 额外重标注 4 次,太多会拖慢训练速度 |
| HER 重标注比例 | 每条轨迹重标注后混合使用 | 不需要刻意控制,直接用整条轨迹即可 |
| 每个 epoch 的 rollout 数 | 50 | 第一轮可以先 16,观察稳定后再加大 |
| 每个 epoch 的更新次数 | 40 | 通过 gradient steps 控制 |
| actor 学习率 | 1e-3 | 配合 Adam 使用 |
| critic 学习率 | 1e-3 | 配合 Adam 使用 |
| 回放池容量 | 1e6 | 存放的 transition 数量,越大越稳定但占内存 |
一个值得注意的细节是:FetchReach 里 achieved_goal 和 desired_goal 是 3 维坐标,归一化不是必须的,因为坐标范围大致在 [-0.3, 0.3] 之间。但如果你迁移到别的环境,最好先检查状态和目标的量纲。比如有些环境的目标是速度向量,范围是 [-1, 1],有些是角度,范围是 [-π, π],这时候不做归一化会让 critic 的输入分布差异过大,影响收敛速度。
4. 常见问题与排查技巧实录
4.1 问题一:用了 HER 还是不收敛,到底哪里出了问题
这是我在社区里被问得最多的问题。HER 本身不是万能药,它解决的是“奖励稀疏导致信号缺失”的问题,但如果底层算法本身不稳定,HER 也救不回来。我的排查顺序是固定的:先骑自行车检查 buffer 里重标注后的 reward 是否真的存在成功样本。很多人在 Fetch 环境里把奖励写成 int 类型,或者忘记把 -1 改成 0,导致重标注后的 transition 依然全部是负奖励,HER 就完全失效了。建议在训练前用一个小脚本打印 buffer 采样结果的 reward 分布,确认里面有正样本再继续。
第二个高频原因是网络输入没有把 desired_goal 拼到 observation 里。以 FetchReach 为例,observation 本身已经包含了 desired_goal 字段,但如果你只用 obs['observation'](19 维)而不把 obs['desired_goal'] 拼进去,智能体根本感知不到目标,这属于“算法没看到任务”的低级错误。第三个常见原因是随机种子问题。HER 在某些随机种子下表现波动较大,尤其是网络初始化差异较大时。建议固定种子跑三组实验看平均效果,不要因为单颗种子效果好就宣称成功。
4.2 问题二:重标注导致 Q 值高估与分布偏移
HER 有一个被论文和评论区反复讨论的隐患:重标注产生的样本,其目标 g' 与 rollout 时策略实际尝试的目标 g 并不一致。也就是说,这些样本对应的动作 a_t 原本是在目标 g 的条件采样出来的,现在却被拿来训练策略去接近 g',数据分布发生了偏移。这会让 critic 对“在目标 g' 下执行动作 a_t”的 Q 值估计产生偏差,严重时会造成 Q 值高估,进而误导 actor。
缓解手段有三个。第一,在 critic 训练时使用 Double Q-learning 的思想,也就是用两个 critic 网络取最小值作为目标 Q 值,这是最直接的手段。第二,给网络加 Layer Normalization,尤其在连续控制任务上,LayerNorm 能显著抑制 Q 值漂移,SAC + HER 的经典组合里经常能看到 LayerNorm 的身影。第三,适当减少重标注比例,不要让重标注样本淹没原始样本,一般 future_k 取 4 就是一个经过实践验证的平衡点。另外你在阅读评论区时可能会看到有人把 HER 和优先经验回放(PER)搭配使用,我的经验是:PER 可以加,但优先级计算要基于 TD-error,不要基于奖励大小,否则重标注样本会因为奖励抖动过大而霸占采样队列,反而适得其反。
4.3 问题三:future 策略的细节,k 值应该怎么选
future 策略里有两个超参容易让人困惑:future_k 和采样的时间范围。论文里推荐 future_k = 4,这是基于多个环境实验得到的经验值。k 太小意味着重标注样本不足,k 太大则回放池里重标注样本比例过高,数据分布偏移问题会加剧,同时每次 episode 存进 buffer 的样本量膨胀,训练一轮的时间也会变长。我在 FetchPush 上试过 k = 1、k = 4、k = 8 三种设置,k = 1 收敛明显变慢,k = 8 最终成功率也不错但单 epoch 耗时增加约 20%,综合看 k = 4 确实最省事。
时间范围方面,future 策略只在“当前时刻之后”采样,这一点绝对不能改。如果你不小心把采样范围写成整个轨迹(包括过去时刻),效果会大幅下降,因为目标与动作之间失去了因果关联。还有个细节:当轨迹很短、只剩最后几步时,候选时刻可能不足 k 个,这时候用 np.random.choice 的 replace=False 会报错,我在代码里用 size=min(k, horizon - t - 1) 做了保护,你自己实现时也记得处理。
4.4 从仿真到真实环境的一些心得
如果 HER 在你的仿真环境里已经跑通了,接下来很可能要考虑真机部署。这里有个常被忽视的问题:HER 重标注依赖 achieved_goal 的准确性。仿真环境里可以直接读取真实坐标,但真实机器人上 achieved_goal 通常来自运动捕捉系统或视觉估计,一旦这个估计有偏差,重标注出来的目标就是错的,整个学习过程都会被带偏。所以真实场景下我一般会在 achieved_goal 上叠加一个高斯噪声来训练,让策略对目标的不确定性具备一定鲁棒性,这也算是一种简单的 domain randomization。
此外,真实机器人的采样成本很高,不可能像仿真那样疯狂 rollout。这种情况下建议把 HER 和“仿真到真实迁移”(sim-to-real)结合:先在仿真里用 HER 学到一个泛化策略,再用少量真实数据做微调。HER 学到的策略天然是 goal-conditioned 的,这给真机部署带来了一个额外好处——你可以在运行时动态指定目标,不需要为每个新目标重新训练一遍模型,这个特性在实际应用中相当实用。
最后再分享一个我在项目里常用的技巧。HER 的成功样本比例会随着训练推进逐渐上升,等到策略已经学会了大部分任务时,重标注样本里“成功”的比例接近于 1,此时如果还像初期一样把大量重标注样本塞给 critic,反而可能让 Q 值偏向乐观,影响策略的精细度。我的做法是在训练中期开始逐步降低 future_k,比如从 4 降到 2,让回放池里的原始样本比例慢慢回升。这个也不是论文里的标准做法,属于我自己的调参习惯,但实验下来确实能让最终策略在达到高成功率的同时减少抖动。回头看 hindsight 这个词,在强化学习里它是再直白不过的三个字:回头看你走过的弯路,把它也变成通向未来的经验。