强化学习破解稀疏奖励:HER事后经验回放方法详解 你有没有过这种经历训练一个机器人抓取积木跑了几十万步成功率还是零回报曲线跟心电图似的上蹿下跳但就是没有真正学会任务问题多半不在网络结构也不在优化器而在你给它的环境实在太“吝啬”——不管动作做得多努力只要最后差一厘米没抓到奖励就是-1没有任何中间信号告诉它“你离目标近了”。这就是强化学习里最磨人的稀疏奖励问题。而 Hindsight Experience Replay事后经验回放下面统一叫 HER恰恰是冲着这个问题来的。它最狠的一点在于不再纠结“这次任务成功没有”而是把每一次“失败”都改写成一次“成功”——既然你没推到红色目标点那行把你实际推到的那块位置重新定义为本次目标这条轨迹立刻变成一条正样本。这个思路听起来有点自欺欺人但数学上却站得住脚而且在实际物理仿真和真机迁移中都极其好用。这篇文章我会把这套方法的原理、公式、代码级实现细节和踩坑记录完整拆开适合正在上手强化学习、卡在稀疏奖励任务上的朋友参考。1. 稀疏奖励任务到底难在哪1.1 一张只能打对错的考卷先抛开公式用最直白的方式说说稀疏奖励。假设你在训练一个机械臂让它把一个方块推到桌面上的目标点。每一帧你给它一个激励大多数做法是方块中心与目标点距离小于某个阈值比如5厘米给0分否则给-1分。这个设定本身很干净可它带来一个致命问题随机乱动的机械臂能在有限步数内碰到目标点的概率极低低到几乎为零。这个现象用强化学习的语言描述就是有效探索信号缺失。策略网络一开始完全是随机初始化它输出的是一堆乱七八糟的动作。在稠密奖励环境里它至少能从“距离变化”里学到趋势离得近一点奖励高一点那说明这个方向的力矩是对的。可是在稀疏奖励环境里除非你非常幸运地刚好做到了一次成功动作否则整个回放池里全部都是-1的样本梯度根本没有方向可言。这就像一张全是零分的考卷你让老师怎么根据答题情况来调整教学方向所以在HER出现之前大家普遍靠手工设计奖励函数来缓解这类问题——机械臂离目标近了加一点分抓起来了再加一点等等这就是奖励塑形。可这个方案工程量大而且一不小心就导出一套抄近路的策略比如机械臂学会了原地旋转制造接近的假象却没有真正完成抓取。1.2 从失败里也能学到东西HER的核心想法特别朴素人类学习一项技能的时候从来不只会向“成功示范”学习。你做一百次投篮可能一个都没进但你的肌肉记忆在不断调整发力大小和角度因为每一次出手后你都会用一个新参考点来评估自己的动作——比如“这次稍微偏左了一点”“这次力气大了点”。换句话说每一次失败其实都在告诉你如果你把目标换成你实际得到的结果刚才那串动作就是一次成功的示范。把这种直觉翻译进算法就得到了HER的训练模式每收集一条轨迹除了保存原始目标g下的经历再额外把轨迹中实际到达的状态g当作新目标重新算一次奖励把这条“伪造成功”的数据也存进回放池。这样一来即便智能体一次都没真的完成过任务它依然能从回放池里抽到大量“完成了任务”的正样本策略梯度有了明确方向学习就变得可行了。1.3 为什么数学上这能成立可能有人会质疑你把没完成的任务当作完成的样本来训练不会把策略教坏吗这恰恰是HER最精巧的地方也是它跟普通的伪标签技巧的本质区别。作者在论文里用了一个多目标学习的框架来做论证如果一个策略需要在大量不同目标上达成最优那么它在原始目标上的表现也不会差。这里的逻辑是目标重标记并没有改变你在原始目标上的真实回溯信号它只是在训练分布里额外注入了其它目标的成功经验让价值函数能够在更稠密的有效区域里被学习出来。换句话说多目标学习带来了一种“课程”效应先学会到达各种实际出现过的状态然后在回放池中靠近原始目标的样本也在持续积累。训练初期你可能永远抽不到原始目标的成功样本但随着策略慢慢学会“到达任意状态”它逼近原始目标的能力也会随之水涨船高。2. 目标重标记的核心细节2.1 目标条件化策略的基本框架HER不是一套独立完整的强化学习算法它更像一个“数据增强插件”需要寄生在某个off-policy的Actor-Critic算法上使用最常见的是DDPG后面也有人把它接在TD3、SAC上。被HER改造后策略的输入从单纯的观测状态s变成了(s, g)二元组g描述当前需要完成的目标。拿机械臂推方块举例s是机械臂关节角度、末端位置、方块位置等信息g就是目标点坐标。策略网络要学习的是在观测到s并且被命令“把方块推到g”时应该输出什么动作。这等于一次同时训练一大堆任务每个任务对应不同的g而HER为这一大堆任务制造了大量成功样本。由于所有任务共享同一个策略网络任何一次“成功到达某个状态”的经历都会反过来提升整个策略对状态-目标空间的认知水平。2.2 重标记得到的到底是一份什么数据给定一条原始轨迹τ {s_1, a_1, s_2, a_2, ..., s_T, a_T}对于该轨迹原目标g在t时刻的奖励常常是稀疏二元奖励比如r_t(g) 0 如果 t 时刻已达成目标r_t(g) -1 否则HER的操作就是选择一个新目标g必须是这条轨迹里实际“接近过”或“到达过”的状态比如某个时间步的方块位置然后整条轨迹在g视角下重新计算奖励。由于g本身就是轨迹中的某个实际状态那么至少会有一个时间步触发“达成目标”条件这条轨迹在有正有负的标签下就变得有学习价值了。这里有一个细节值得注意重标记之后同一个(s, a, s)在回放池中可能对应多个不同的g每次采样时网络需要根据不同的目标重新理解这段经验。在工程实现上这通常意味着你要把(obs, goal)作为拼接后的输入存储并且奖励也要按goal重新计算好再入库否则训练时再去重算一遍会变得非常麻烦。2.3 选目标可选 final 也可以选 future到底用哪个状态作为新目标是HER里最有讲究的地方。原论文提出了四种策略策略新目标选取方式特点final该轨迹最后一步的状态实现最简单若是终点状态各不相同目标分布相对分散random从独立的目标分布中随机抽目标多样性好但与轨迹实际到达状态关联弱episode从当前轨迹内随机抽一个状态目标覆盖从早期到后期的所有状态时间跨度大future从当前轨迹当前时刻之后随机抽状态目标“由近及远”密度更高经验效率最好原论文和无数复现实验给出的结论都一致future效果最稳定。原因是它天然构造了同一个轨迹上从当前时刻到未来的多个难度层级回放池里既有一两步就能达成的容易目标也有十步之后才能达成的困难目标形成一个隐性的课程。我自己在实验里也有类似感受用future训练收敛速度快成功率曲线也更平滑不像final那样容易出现阶段性停滞。2.4 HER和奖励塑形、课程学习的区别刚开始接触HER的人容易把它和奖励塑形混在一起它们本质上完全不同。奖励塑形是在改造奖励函数本身让每一步都有一个连续值相当于给一条原本没有起伏的路人为设计坡度。HER不碰原始环境奖励它只是在收集经验后对数据做重标记训练环境没有任何改变。这样做的好处是不会有“策略钻塑形空子”的风险因为任务本身没变变的是训练时的监督信号。课程学习则是一种更宏观的训练安排人为地把任务从简单到复杂排列。HER的重标记机制天然自带课程属性但不需要人工编排。因为轨迹后半段到达的状态目标对当前时刻来说显然难度更低所以系统天然地从“达成附近状态”渐渐向“达成更远目标”过渡。3. 手把手把 HER 跑起来3.1 环境与代码框架选择这里我以最经典的验证环境FetchPush-v1为例也就是OpenAI Gym里机械臂推方块的任务。这类MuJoCo环境原始奖励就非常稀疏推到位给0否则返回-1。配合HER不需要改一行环境代码就能在几万步内看到明显的成功率爬升特别适合用来做方法验证。项目使用PyTorch实现底层用DDPG做actor-critic骨架。如果你偏好更现代的基线可以直接换成TD3或SACHER的逻辑完全不受影响。整个项目的文件结构大致是hindsight/ ├── envs/ │ └── fetch_env.py # 环境包装统一obs和goal的格式 ├── her/ │ ├── buffer.py # 经验回放池支持按future策略重标记 │ └── algo.py # DDPG核心更新逻辑 ├── train.py # 主训练脚本收集样本 更新策略 └── eval.py # 评估脚本统计成功率3.2 经验回放池的正确写法回放池是整个HER的命门。最常踩的坑是把目标重标记在存储阶段就一次性展开导致内存爆炸。一条轨迹状态数T个如果每条都展开成多个目标副本乘以回放倍数k后存储量会变成原来的k倍在长轨迹场景下非常浪费。更稳妥的做法是轨迹先以原始格式完整存放等到采样时才做重标记计算。也就是回放池里存的是(n_transitions, n_extra)这样规模的引用结构采样阶段从轨迹中随机抽transition再按future策略从后续时间步中抽目标。这样内存占用始终受控而且代码逻辑更清晰。下面是一段伪代码结构class HERBuffer: def __init__(self, capacity, k_future4): self.capacity capacity self.k_future k_future self.episodes deque() # 存完整轨迹 def add_episode(self, obs_list, action_list, goal): # 把整条轨迹和原始目标一起存入 self.episodes.append((obs_list, action_list, goal)) def sample_batch(self, batch_size): # 随机抽batch_size个transition对每个transition # 按future策略额外生成k_future个目标变体3.3 DDPG 与 HER 拼接的注意点DDPG的Critic网络输入是(obs, goal, action)Actor网络输入是(obs, goal)输出动作。对于Fetch这类环境obs本身已经包含当前goal的信息observation achieved_goal desired_goal 机械臂状态所以工程上通常有两种做法一是直接把整个observation当作obs输入网络另一种是把achieved_goal和desired_goal拆开再来拼接。我推荐后者因为重标记时更换的只是goal部分把achieved_goal和desired_goal分开处理会让数据变换更直观也方便后面做输入归一化。整个训练循环里每一步从环境中拿到的(obs, action, reward, next_obs, done)存进“当前轨迹暂存区”当环境返回done时将这一整条轨迹扔进HERBuffer并由HERBuffer完成目标扩展。训练更新阶段的网络输入要全部用重标记后的样本同时记得对reward也用重标记后的目标重新计算。3.4 关键超参数与实验效果这是我多次实验后觉得比较稳的一套配置参数取值说明回放倍数 k4每条原始transition额外生成4个目标版本目标采样策略future从当前时刻之后随机抽取状态奖励形式稀疏达成0否则-1配合距离阈值0.05判定达成critic学习率1e-3这里用Adamactor学习率1e-3同上动作噪声OU噪声sigma0.2探索初期稍微大一点没坏处批次大小256目标条件学习下批次太小容易不稳网络规模两层256actor和critic都是两层MLP在这一配置下FetchPush环境在大概2万步左右开始看到成功率突破5万步就能到80%以上。相比之下不加HER纯用DDPG同环境跑几十万步成功率都经常挂零。差距一眼就能看出来。4. 常见问题与排查技巧4.1 成功率一直为零时先查训练管线如果你跑了一个HER项目但成功率始终没起色先别急着调网络结构大概率问题出在数据流上。最常见的三个原因第一回放池里的transition在采样时沿用了原本的goal重标记没有真正生效等于白加HER第二done信号处理不对导致整条轨迹不断被打断或重复累积第三replay buffer里新旧样本比例失衡旧的高质量目标样本被大量新样本淹没。排查技巧就是在训练时每1000步打印一次buffer里“正样本比例”这个比例应该从零开始缓慢上升如果始终是0说明目标重标记逻辑肯定没接对。另一个隐蔽问题是目标归一化。Fetch环境里机械臂关节角度取值范围可能是[-2, 2]而目标坐标在[0, 1]附近两个量级本身差异不大但如果你换到自定义环境观测里既有速度又有位置还有温度等指标那就必须对输入做归一化否则批评者网络很容易陷入局部震荡。我在一个仿真任务上曾经卡了很久最后发现只是位置量纲比角度大了一个数量级归一化之后立刻收敛。4.2 采样目标时注意“越界”问题用future策略采样目标的时候有一个细节容易忽略新目标g应该是从当前时刻之后的任意状态里随机抽而不是从整条轨迹任意抽。如果你不小心用了episode策略早期时间步会抽到比当前更早的状态目标这会让智能体学到“回到过去”这种不存在的逻辑目标条件变得混乱训练效果会立刻退化。写代码时务必保证抽样的时间索引大于等于当前transition的时间索引。4.3 成功率上不去但正样本比例很高的怪现象还有一种比较诡异的情况回放池里正样本比例已经很高但评估成功率还是趴在地上。这说明策略已经在重标记目标上“过拟合”了它学会的是到达那些重标记目标的小技巧却没有泛化到原始目标上。原因多半是原始目标在回放池里出现的次数太少被大量伪造目标淹没了。解决办法有两个一是提高k的采样倾向让原始目标样本在回放池里的比重不低于总样本的1/k二是在采样时专门用一个独立的buffer存原始目标轨迹混入训练类似于目标重标记和原始经验各占一半的采样比例。我在实际项目里曾经把k加到8正样本比例冲到30%但原始目标样本被稀释到只剩几个百分点结果就是策略在仿真里表现得特别“短视”一换初始条件就崩盘。后来把原始目标样本的采样权重锁定在25%这个问题就消失了。4.4 评估策略要看“多初始状态”平均HER的价值在于对目标分布的综合建模所以在评估时不要只用一个固定初始状态测。应该随机撒多个初始位置和目标位置统计平均成功率。如果一个策略在某些初始状态下能高分、在另一些初始状态下完全失败往往说明它学到的只是部分目标区域的局部映射泛化能力不足。这种失败模式在只有单一初始状态的评估指标下根本看不出来。5. 写在最后的一点心得我把HER从一个“听说过觉得很妙”的方法变成自己项目里的常规武器前后花了不少时间最大的体会是这套方法的价值不只是解决稀疏奖励而是改变了我对强化学习数据本质的理解。过去我总想着怎么设计奖励函数让模型知道“快成功了”现在我更倾向于让模型自己从失败轨迹里挖掘“哪些状态更容易到达”。目标重标记相当于给模型画了一幅更完整的状态可达性地图有了这张图任何目标都只是图上的一个点而已。最后分享一个我一直在用的小技巧HER和辅助任务比如预测状态特征、估计到达时间一起训练往往会有额外惊喜网络在前面几个epoch学到的表征能让HER的目标重标记质量再上一个台阶。项目代码我已经整理成一套相对干净的模板下一次你在自己的环境里遇到奖励稀疏、训练不动的问题时可以试试先把HER接上去很多看似复杂的任务其实只差这一层小小的目标重标记。