事后经验回放HER:破解稀疏奖励,机械臂抓取成功率从3%到80%
发布时间:2026/9/30 19:37:57来源:尧图网络
如果你做过连续控制类的强化学习项目大概率会撞上这么一堵墙环境给了你一个极其吝啬的奖励函数——只有机械臂末端距离目标小于 5 厘米才给 1其他情况全是 0。模型跑了几百万步奖励曲线像心电图一样平躺成功率长期在 0% 到 3% 之间蹦跶。我前段时间做的机械臂抓取任务就卡在这个坎上后来真正把 hindsight事后经验回放Hindsight Experience Replay简称 HER引进训练管线情况才彻底改观同样的总步数成功率从 3% 干到了 80% 以上。这篇文章是写给正在被稀疏奖励折磨的人做机械臂控制、机器人导航、游戏 AI或者任何带目标条件化的连续决策任务。读完你能拿到三样东西第一HER 到底在玩什么把戏为什么一个看起来像是在“作弊”的重标注逻辑能学出好东西第二从零实现 HER 需要怎么写代码、配哪些超参数第三我实际调参踩过的坑以及怎么科学地判断训练有没有真的在变好。算法本身没有多高的数学门槛我会尽量用大白话把机制讲透书上的公式谁都会抄真正值钱的是那些文档里不写的馊主意和血泪教训。1. 项目缘起hindsight 到底要治什么病1.1 稀疏奖励环境让智能体两眼一抹黑强化学习里奖励就是老师。密集奖励的环境里每一步你都能收到一个分数往前走一点、球接得好一点都有反馈模型很容易知道“这个动作方向是对的”。但现实中的机器人任务很少这么友好机械臂抓取、物体推动、迷宫导航往往都是你折腾半天最后的反馈只有两个选项——成功或者失败没有中间态。拿我那个机械臂任务举例目标点每次随机分布在台面上只有当抓手和目标之间的距离小于 0.05 才奖励 1其他所有 step 的奖励全是 0。你想想随机策略在这个连续动作空间里正好蒙中那个 5 厘米圆球的概率有多大基本等于在足球场上闭眼踢一脚要求直接进洞。一个回合 50 步训练一万个回合进到奖励圈里的一次都没有这不是夸张是常态。这种环境对神经网络来说是灾难性的。经验回放缓冲区里堆了几十万甚至上百万条 transition其中带正奖励的样本一只手数得过来。DQN 也好 DDPG 也好核心都是靠样本学 Q 值正样本为零的时候critic 学到的就是一个“天下乌鸦一般黑”的 Q 值函数所有状态动作对的价值都趋近于 0。policy 梯度跟着这个 Q 值算出来自然就是个没有方向的随机噪声模型就这么一直鬼打墙下去。你可以把稀疏奖励想象成一个从不给中间反馈的教练。你投篮要么进要么不进教练永远只说一句“没进”从不告诉你差了多少、偏了哪个方向。正常人这样练一百年也练不出来机器也一样。问题不是网络容量不够而是信息信噪比太低低到完全没有学习信号。1.2 事后诸葛亮其实是最朴素的学习逻辑“hindsight is 20/20”事后看一切一目了然。这句话放在强化学习里我觉得是最被低估的一句话。失败的轨迹真的没有价值吗不一定——它只是没有实现你给它设定的那个目标但它确确实实实现了一个“另一个目标”。假设你给机器人定的目标是去点 A它跑了一整个回合最后停在点 B。从目标 A 的角度看这是一条彻底的失败轨迹奖励全 0扔进回收站。但换一个思路把这条轨迹的目标重新标注成点 B那情况完全不一样了它成功到达了 B而且为了到达 B它一整条轨迹的行为都是合理有效的。这摇身一变就是一条绝佳的成功示范。再用投篮类比你瞄准的是左边的篮筐球飞出去重重砸在了右边的筐里。普通的做法是把这次投篮记为一次失误然后抛之脑后。hindsight 的做法是把它重新记录成“当目标是右边篮筐时这个出手方式是对的”。下次真正的目标落在右边时这条经验就能直接用上。这看起来有点像自欺欺人但仔细想想它的数学逻辑是站得住的目标条件化的策略本身就在学习“给定目标如何到达”而一条状态转移 (s, a, s) 是否有效完全取决于把它和什么目标配对。同一个动作对目标 B 是成功对目标 A 是失败这不矛盾。HER 要做的就是充分利用那些“配错了目标”的经验数据而不是一删了之。当然这招的适用前提是任务必须允许用“目标”来描述也就是 goal-conditioned。如果你的任务根本没有明确目标比如要最大化一个无法分解成“某状态是否达成”的评分函数那 HER 也无从下手这是它在方法论上的边界。1.3 为什么我不选奖励塑形、课程学习或模仿学习刚被稀疏奖励卡住的时候我第一反应其实不是 HER而是先试了另外三条常见的路。它们各有各的坑最后才意识到 HER 才是性价比最高的选择。奖励塑形是最自然的想法既然稀疏奖励学不动那就给模型一点“接近目标就有奖励”的中间信号呗。我试过用欧氏距离的负值做即时奖励结果模型很快就学会了钻空子——它发现只要在目标点附近来回抖动就能稳定刷到每一步的接近奖励真正的“精准到达并停在目标”反而一点没学到。这就是奖励塑形最臭名昭著的问题它给模型提供了额外优化空间而模型永远比你更会找漏洞。要设计一个无懈可击的塑形函数极其依赖领域知识成本高且脆弱。课程学习我也考虑过。思路是从近的目标开始练练会了再逐渐拉远。逻辑没问题但它要求你对任务本身做大量编排先教什么后教什么、难度怎么递增这本质上又引入了新的超参数体系。更要命的是机器人环境往往没法把目标难度切得那么干净两个目标看起来一远一近实际难度可能完全不一样课程排错了反而拖慢收敛。模仿学习就更直接了拿专家数据来暖启动但我这个场景根本没有可用的专家示范一台机械臂要自己摸爬滚打没有人手把手教。三条路都走不通之后我才认真研究了 HER最后发现它在“数据稀缺”这件事上的优势是降维打击级的它不需要外部先验不需要专家数据不需要重设环境只是把智能体自己攒下来的失败数据重新改写了一下标签完全用存量经验说话。下表是我当时做的选型对比供你参考方案是否需要领域知识是否需要外部数据主要风险奖励塑形需要否容易产生局部最优模型会钻漏洞课程学习需要否课程设计成本高效果受难度排序影响大模仿学习部分需要需要专家数据难以获得泛化依赖数据质量HER不需要否仅适用于目标条件化任务看到这个对比你应该能理解我为什么最后把宝压在 HER 上了。同一套代码只需要在数据采集和回放环节做一个小手术不需要动环境逻辑也不需要重新设计奖励函数这笔账怎么算都划算。2. 核心机制拆解目标重标注是怎么工作的2.1 从四元组到五元组目标条件化的最小改动传统的强化学习 transition 是四元组 (s, a, r, s)代表状态、动作、奖励、下一状态。HER 要落地第一步就是把四元组升级成五元组 (s, a, r, s, g)在每条经验里都额外存一个目标变量 g。这一步改动看起来很小但实际上把整个问题的结构都改变了。奖励函数不再是一个全局固定的函数而是一个依赖于目标的条件函数。在抓取任务里r 的定义就是“s 是否在 g 的 5 厘米范围内”。策略也从原来的 π(a|s) 变成了 π(a|s, g)意思是“给定我要去 g我在状态 s 下应该怎么动”。这套描述方式在学术上叫 goal-conditioned MDP核心就是让奖励从“状态决定”变成“状态和目标的组合决定”。关键是这个改动对算法架构的侵入成本极低。如果你原来用的是 DDPG 或者 SAC不需要换网络结构只需要把目标向量接到 actor 和 critic 的输入里就行。比如 actor 的输入从 state 变成 concat(state, goal)输出还是那个动作向量。这也是 HER 能和现有 off-policy 算法无缝衔接的原因它不是一个全新的 RL 算法而是一种“数据重标注策略”挂在已有的算法上面工作。这里要插一句为什么 HER 只能搭配 off-policy 算法。原因很本质重标注出来的新样本和当前策略采集的样本在分布上已经不一样了你是在拿旧轨迹翻新之后重新喂给模型学。On-policy 算法比如 PPO、REINFORCE要求更新用的数据必须严格来自当前策略翻新旧数据这个操作直接就违反了它的前提。而 off-policy 算法通过经验回放本来就在反复使用旧数据HER 的统一性没有任何违和感。2.2 四种重标注策略我到底该用哪一种目标重标注到底怎么标学术界给过四种标准策略我挨个说人话版本。final 策略最简单一个回合跑完不管最终停在哪直接把最终状态当作新目标回放的时候把这条轨迹里所有 transition 的目标全改成这个最终状态。它的优点是方差小、稳定缺点是如果回合的终点状态很怪异比如机器人撞到关节限位、物体滚下桌面那整个回合的所有重标样本就全指向了一个异常目标等于给学生发了一套偏科教材。future 策略是论文实验里最常用的一种对于轨迹里的第 t 步从它之后的某个时刻随机挑一个状态当新目标。也就是说目标不是铁板钉钉的终点而是“未来某一个时刻会到达的位置”。这个策略的精髓在于目标永远选在该步之后的状态上天然保证了“从当前状态出发这条轨迹确实路过了目标”样本的可到达性最强。episode 策略更激进从整个回合任意时刻的状态里随机挑一个当目标包括该步之前的状态。这样一来目标分布更宽广但噪声也更大因为某些目标在时间上早于当前步意味着智能体在那一时刻还没有真正“路过”它重标出来的某些样本会显得牵强。random 策略最偷懒从全局缓冲区里随机抽一个状态当目标。问题是这个目标很可能离当前轨迹十万八千里重标出来的样本完全不符合动力学可达性批评网络会被这种胡说八道的样本带偏。论文里的结论也很诚实random 单独用往往效果垫底。四种策略我实际用下来future 是最稳的这也是绝大多数工程项目的默认选择。episode 在某些任务上能接近 future 的效果但噪声更大final 在简单任务上完全够用但机械臂这种容易碰撞限位、产生异常终点的场景里真心不建议。我自己的习惯是先用 final 验证管线是否通然后切 future 跑长训。下面是四种策略的关键差异表策略新目标来源样本可到达性稳定性适用场景final回合终点状态中高终点状态正常、任务简单future当前步之后随机状态高高一般任务首选episode整个回合任意状态中中需要扩大目标覆盖时random全局缓冲区随机状态低低不推荐单独使用2.3 为什么 future 策略比 final 更硬核很多第一次接触 HER 的人会疑惑future 和 final 看起来差不多终点状态不也是未来时刻的一个状态吗区别恰恰藏在“随机抽”这个动作上。final 策略有个隐蔽的缺陷它把所有重标样本的“成功标准”坍缩到同一点上。如果一个回合因为机械臂碰到奇异位形或者撞到工作台边缘提前卡死最终状态可能是一个极其畸形的位姿远离正常的操作流形。但你强行告诉模型“这个位姿也是一条成功轨迹的终点”模型会把这个异常区域当成高价值区域反复去尝试纯属浪费时间。future 策略把目标分布在整个轨迹的“未来切片”上每个时刻都可能被标成不同的目标目标分布和智能体实际游走的状态流形高度贴合。这种做法的好处是双重的一是每个重标目标对该 transition 而言都是可达的critic 学出来的价值函数更平滑二是它等于给模型打了若干个“路标”让它在一条轨迹里同时学到了好几个可达目标信息密度明显更高。用教育做类比final 相当于只给学生看期末考试的最终答案future 则像是每周发一次阶段测验每个阶段的得分点都成了学习信号。后者更细碎但对学生来说学习曲线要友好得多。这个差异在动作空间大、轨迹长的任务里尤其明显final 的方差会让人抓狂future 的方差则始终控制在可接受范围内。3. 动手实现从零搭一个 HER 训练管线3.1 环境与基线算法选型我在正式动机械臂之前先用一个 2D 点到达任务验证了整套管线。环境很简单一个点在平面上运动动作是连续力观测是当前坐标目标是一个随机生成的平面坐标奖励是稀疏的 0/1。这个环境的目的是快速跑通代码因为 3D 机械臂训练一次动辄几个小时如果在 2D 上都没法稳定收敛搬到 3D 只是浪费更贵的电费。基线算法我选了 DDPG没有别的高深理由就是它和 HER 的兼容性最好且实现简单。你要用 TD3 或者 SAC 也完全没问题这些算法都是 off-policy 的都支持 HER 那套经验回放改造。我个人建议在新项目里优先用 TD3它的裁剪双 Q 学习能缓解 HER 重标样本带来的 Q 值过估计问题训练稳定性比 DDPG 好一个档次。不过为了把原理讲清楚这篇还是用 DDPG 的逻辑来演示代码结构完全通用。有一点必须提前说HER 的数据流要求回放缓冲区能整段访问原始回合轨迹因为重标注需要“取出某条轨迹的未来状态”。普通的随机缓冲区不满足这个需求必须额外维护一个回合级的环形缓冲区每完成一个 episode 后整体加工再把加工后的样本塞进全局样本池。很多人在这一步踩坑——以为 HER 只是换个 sample 函数结果重标出来的目标全是乱的训练一步崩一步。3.2 重标注函数与训练主循环的实现先看重标注函数的核心逻辑。这里我直接给出我项目里实际用过的代码结构每个字段都写清楚含义。import numpy as np import random def relabel(trans, episode, strategyfuture, threshold0.05): trans: 单条转移 (s, a, r, s_next, g, t) episode: 本回合所有转移列表每条带 t 时间索引 strategy: final / future / episode threshold: 与 rollout 阶段完全一致的到达判定阈值 s, a, r, s_next, g, t trans # 1. 选择新目标 if strategy final: new_g episode[-1][s_next] elif strategy future: # 只从当前步之后的状态里选保证目标可达 future_states [tr[s_next] for tr in episode if tr[t] t] new_g random.choice(future_states) elif strategy episode: new_g random.choice([tr[s_next] for tr in episode]) else: raise ValueError(unknown strategy) # 2. 用同一个距离阈值重算奖励 if np.linalg.norm(s_next - new_g, axis-1) threshold: new_r 1.0 else: new_r 0.0 return s, a, new_r, s_next, new_g这个函数的要点有三个第一future 策略里取的是“s_next”不是“s”因为目标状态本身必须是一个实际到达过的位置第二奖励重算时用的阈值必须和 rollout 阶段环境里用的完全一致否则 critic 学到的评价标准就是两套体系铁定出问题第三episode 里的每条转移必须带时间索引 t否则没法正确切分“未来”很多半吊子实现就是栽在这个细节上。然后是训练主循环。我把 HER 的接入点放在 episode 采集完成之后而不是 replay 采样的过程中。先看代码for cycle in range(total_cycles): goal env.sample_goal() episode run_episode(env, actor, goal) # 每条转移都带原始 goal # 第一步原始经验照常入库目标保持原样 replay.extend(episode) # 第二步HER 重标注生成翻新样本 if use_her: for trans in episode: replay.push(relabel(trans, episode, strategyfuture)) # 第三步从混合缓冲区采样更新 for _ in range(n_updates): batch replay.sample(batch_size) update_critic_actor(batch)这里的顺序非常关键。原始经验必须先进库因为重标注样本是基于原始轨迹派生出来的两者都要出现在后续的采样池里。我见过有人把原始经验放一半删一半导致正样本比例和真实目标分布产生严重偏移训练出来的策略只会追着容易的目标跑真到了随机目标面前就原形毕露。补充一个工程技巧在 episode 数据结构里预先把每一时刻的 s_next 存成列表并事先算好每个 t 的“未来状态集合”避免在 relabel 里每次都切片遍历。我第一个版本没有做索引缓存跑 3D 机械臂时 relabel 成了性能瓶颈一个 episode 重标上百次每次都要 O(T) 扫列表训练循环被拖慢了将近 30%后来改成“每个 episode 先构建未来状态表再批量重标”才解决。3.3 可直接抄作业的超参模板HER 的超参没有玄学但有几个关键值真的很影响收敛速度和稳定性。下面是我在 2D 点到达和 Fetch 机械臂任务里反复验证过的参考配置你可以先照着跑再根据任务特性调参数参考值备注strategyfuture默认推荐简单任务可先用 final 验管线k未来采样范围3从未来 1~3 步内采样目标论文里有类似设定her_ratio重标比例0.8每条原始转移按 80% 概率生成重标样本batch_size256混合了原始与重标样本太小波动大replay_size1e5 ~ 1e6HER 会放大数据量缓冲区别抠n_updates / cycle60每个 episode 后更新的次数别贪多gamma0.98稀疏奖励场景下比 0.99 更早看到效果探索噪声 std0.1 ~ 0.2太小的话 HER 没有足够多样的状态可重标目标网络软更新 tau5e-3必须用软更新硬拷贝必崩这里重点解释三个容易被低估的参数。第一个是 her_ratio最开始我图省事设成 1.0也就是每条转移必重标一次结果发现缓冲区里重标样本占比过高策略对“真实原始目标分布”的记忆会被冲淡。后来把重标比例压到 0.8并且保证采样时原始样本和重标样本的比例大致在 1:1 到 1:2 之间效果立刻稳定了不少。第二个是探索噪声。HER 的“原料”是智能体真实跑过的状态轨迹如果探索噪声太小智能体永远在一个狭小区域里打转跑出来的轨迹状态多样性极差即使重标了目标也只是那一小块区域里的来回折返信息量稀薄。我甚至见过一个项目改了噪声从 0.05 到 0.2 之后同样的 HER 配置效果直接翻倍。第三个是 gamma 的选择。稀疏奖励使得长期回报的传播路径更长gamma 太接近 1比如 0.99会让价值估计的方差变得很大太小的 gamma 又学不到远期的成功信号。0.98 是我试了多组之后在奖励稀疏场景下觉得最稳的默认值不过这需要根据回合长度微调。如果你不想手写整套逻辑stables-baselines3 里已经有现成的 HerReplayBuffer可以直接挂在 DDPG、TD3 或 SAC 上传入replay_buffer_classHerReplayBuffer和对应的replay_buffer_kwargs就能跑。这个库的实现比较规范适合作为基线对照但我还是建议自己手敲一遍重标注流程理解到位了遇到诡异问题才知道往哪个方向排查。4. 训练效果与调参避坑实录4.1 有 HER 和没 HER训练曲线差距有多大先放结论在我的 2D 点到达任务上不加 HER 的 DDPG 跑了 100 万步成功率一直在 5% 以下徘徊偶尔冲到 10% 又立刻掉回来加上 HER 之后同样是 DDPG跑 30 万步成功率就稳定超过 70%50 万步之后能到 80% 到 90%。在 FetchReach 风格的 3D 抓取任务上差距没有 2D 那么夸张但 HER 版本大约用一半的步数就追平了无 HER 版本两百多万步的效果。这个差距背后的原因非常直白无 HER 时缓冲区里几乎没有正样本Q 值函数学不到任何有效梯度有 HER 之后缓冲区里瞬间多出了一大批“人为转正”的成功样本critic 第一次有了可用的学习信号。说白了HER 不是让模型变聪明了而是让模型第一次有东西可学。不过我不建议只看一条曲线就下结论。RL 训练方差极大尤其稀疏奖励环境下一次侥幸的成功可能让曲线突然蹿一个尖再掉回去。所以我在任何对比实验里都会开至少三到五个随机种子把所有种子的成功率曲线画在同一个坐标系里中位数和置信带一起看。只看单条曲线的“偶然性”会骗死人这点务必记牢。4.2 六个能让你白训一整晚的坑这一节是我真正想写给后来人的东西。下面每个坑我都真实踩过并且大多数坑的报错信息都很友好、很好定位但就是会在你盯着 loss 曲线时悄悄偷走你一晚上。第一重标奖励必须和 rollout 用同一个距离函数、同一个阈值。我在一次实验里环境判定成功用的是欧氏距离小于 0.05结果 relabel 里图省事写成了小于 0.1一晚上下来成功率一直在 20% 左右上不去。后来排查才发现critic 学到的是宽松版本的“成功”而真实环境里根本没那么多成功模型自然表现得一塌糊涂。教训就一句话阈值函数写完之后做一个单元测试专门验证重标样本的奖励计算是否和环境一致。第二future 策略只能从同一回合的未来状态里选目标。有人偷懒从全局缓冲区里随便抽几个状态当新目标结果重标样本里全是“从当前位置出发去一个几万步之外才可能到达的目标”动力学上根本不可达critic 白白被这些胡说八道的样本污染。你甚至能看到 Q 值在训练过程中越学越离谱最后直接发散。第三目标必须参与归一化。很多观测向量是 concat(state, goal) 之后直接喂网络的但 state 里可能包含速度、角速度这些数值较大的量goal 是纯位置的小量网络一开始会被量纲差异带偏收敛极慢。解决办法是分别对 state 和 goal 计算 running mean 和 running std然后用归一化后的拼接向量喂进网络。这是最容易忽略、也最容易产生显著收益的一个操作。第四探索噪声太小HER 会面临“无米下锅”。HER 的目标来自智能体自己跑过的状态轨迹如果探索太保守所有轨迹都挤在起始点附近重标出来的目标几乎一样等于是给一堆重复数据贴了新标签毫无信息量。至少保证一条初始探索策略能让智能体在状态空间里跑开噪声方差可以从 0.2 起步。第五别丢掉原始目标样本。HER 的重标样本毕竟是人造正样本如果缓冲区里全是它们策略会过度优化“可达目标”而忽略“真实目标分布”。想象一下一个学生每次都只做自己会做的题目从不碰难题考试当然永远不及格。所以原始目标的样本必须保证一定的比例我在 her_ratio 调成 0.8 并要求混合采样后这个问题就缓解了。第六DDPG 家族的 Q 值发散问题在 HER 里会被放大。HER 重标样本多了critic 更容易出现过估计所以建议直接在项目里换 TD3或者至少把目标网络的更新参数 tau 设到 5e-3 以下并且绝不采用硬拷贝更新。我在用 DDPG HER 时遇到过 Q 值在某个 step 突然飙到几千整条训练曲线报废的情况换成 TD3 之后此类现象基本绝迹。4.3 效果检验的正确姿势别再盯着 loss 看了稀疏奖励任务里actor 和 critic 的 loss 曲线几乎没有任何指导意义。loss 在下降不代表策略变好了loss 在抖动也不代表策略不行。原因是 loss 本身是由正在变化的 Q 函数算出来的一个动态指标Q 函数一边学一边变loss 的绝对值根本没有稳定的参照系。正确的评估姿势是看“成功率对时间步”的曲线每训练固定步数冻结策略在测试环境下用一组采样好的固定目标点跑若干个回合记录成功到达的比例然后把这个比例连同真实环境步数画出来。注意两点目标点要从真实的目标分布里采样不要自己拍脑袋选几个好打的目标自欺欺人另外要把成功率的计算条件写清楚比如“末端距离小于 0.05 算成功”否则不同实验之间的对比毫无意义。另一个实用技巧是按目标难度分桶统计成功率。比如把目标点按随机采样时的难度划分成“近距离”“中距离”“远距离”三档分别统计成功率你会发现 HER 往往先攻克近距离目标远距离目标要慢得多。这个信息能帮你判断是探索不够还是重标样本比例不对比一个笼统的平均成功率信息量大得多。5. hindsight 思路还能用到哪里HER 这个算法做完之后我发现自己对“目标重标注”这个思路产生了依赖后来在好几类别的任务里都尝试把它移植过去效果都还行。最顺理成章的扩展是 goal-conditioned offline RL。离线数据里大量轨迹同样属于“目标没达成”的状态用 HER 的重标逻辑给这些轨迹重新分配目标、造出正样本能在不增加环境交互的前提下大幅缓解保守算法过于悲观的问题。我现在处理离线数据时已经养成了一个固定习惯任何轨迹进入预处理管线之前先跑一遍 hindsight 检查看看它能不能被改成一个更有用的样本。模仿学习里也可以用类似思想做数据增强一条专家轨迹只示范了一个目标但轨迹中途经过的每个状态其实都可以另立为目标变成目标条件化策略的额外训练数据。这套做法本质上还是 HER 那句老话轨迹本身没变变的是标签。最后再分享一个我在实际工程里最深的体会。以前每次训练失败我第一反应都是调网络结构、调学习率、换奖励函数折腾一圈收效甚微。现在我的第一反应变成了跑出来的这批失败轨迹里有没有被配错目标的数据如果有先试试把它们重新利用起来。很多表面上的“失败”其实只是被分配错了目标而已。这句话对于强化学习成立对于很多现实里的复盘和迭代也成立。
网站建设高端定制企业官网