ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

HER原理与实战:用事后经验重放攻克稀疏奖励难题

HER原理与实战:用事后经验重放攻克稀疏奖励难题 Hindsight英文直译是“后见之明”。你会发现一个有意思的现象同样是失败人类能从中总结出“差一点就成功”的经验而绝大多数强化学习模型只会把失败当作一条负样本丢进回放池里吃灰。直到 Hindsight Experience ReplayHER出现这个局面才被彻底逆转。我在机器人抓取、机械臂避障这类目标达成任务上跑过不少实验讲实话HER 是我见过性价比最高的稀疏奖励解决方案之一它不修改环境、不改造奖励函数只靠一套“事后重新定义目标”的采样策略就能让智能体从一堆失败轨迹里抠出学习信号。这篇文章我会从原理、代码、调参到踩坑把 HER 完整拆开讲一遍适合想解决稀疏奖励问题、正在做 goal-conditioned RL 的工程师和研究者参考。1. 从“事后诸葛亮”到强化学习hindsight 到底解决了什么问题1.1 人类与机器的差距失败经验的价值先聊个生活场景。你投篮出手姿势没毛病球砸在篮筐后沿弹了出来。你会怎么想“太近了下次再往上调一点就进了。”你不会把这次投篮标记为“完全失败”你提取的是“差一点就成功”的修正信息。这就是后见之明hindsight bias 在认知心理学里的经典表现人在知道结果之后会下意识地重构自己之前的判断把失败重新解释为“接近成功的尝试”。机器学习模型恰恰缺少这个能力。在标准强化学习流程里一条经验就是四元组状态、动作、下一状态、奖励奖励是环境给的模型只能被动接受。如果环境给出的奖励是稀疏的——比如机械臂抓取任务只有指尖完全到达目标点才给 1其他情况一律 0——那么模型在整个探索过程中收到的有效反馈极少梯度信号趋近于零训练基本靠“瞎试”。这不是网络容量不够而是信息本身就太少了。HER 想做的事情很简单让模型也在失败之后拥有“事后重估”的能力把没达成的目标换成一个它实际到达的目标再把这条轨迹重新标记成一次“成功尝试”。1.2 稀疏奖励为什么机械臂学不会抓取要理解 HER 的价值得先理解目标条件强化学习Goal-Conditioned RL里的稀疏奖励困境。假设你让机械臂学抓取状态 s 是关节角度和指尖位置目标 g 是目标位置动作 a 是关节力矩。环境给你一个稀疏奖励如果指尖和目标距离小于某个阈值r0否则 r-1。整个 episode 的奖励要么是一条 -1 的直线要么在最后突然蹦出一个 0。对强化学习算法来说这条“直线上的 -1”几乎没有信息量——下一步该往左还是往右奖励函数完全没给提示。我见过很多人在这个阶段疯狂调奖励塑形reward shaping给距离加权重、加势函数引导。这当然能解决问题但问题是每换一个任务、换一个分布奖励函数就要重新设计一轮而设计 reward shaping 的过程中很容易引入“投机取巧”的行为——机械臂学会利用塑形函数的漏洞而不是真正掌握抓取。HER 提供了一条完全不同的路径不调整奖励函数保持稀疏设置的简洁性转而从“目标”的角度下手把已经发生过的状态重新定义为目标。这招非常巧妙因为对多目标环境而言“是否有能力到达任意指定状态”才是真正需要学习的通用技能而某个固定的最终目标只是技能的一次具体应用。1.3 HER 的一句话原理与适用边界用一句话概括 HER在一条 episode 结束后取出这条轨迹里实际达到过的状态把它当作额外的新目标并基于新目标重新计算每个 transition 的奖励然后和原始经验一起存入回放池。这样做的结果是原本一整条全是负奖励的轨迹被拆解成了若干条“从某个状态出发、通过某个动作、到达了某个目标”的正向经验或者说“局部成功经验”。模型学到的是状态之间的可达性关系而最终测试时只需要把目标换成任务真正要求的位置泛化过去即可。这套思路有几个前置条件不是所有场景都能硬套。第一环境必须能够人为定义目标且目标空间和状态空间至少部分重叠通常是 achieved goal 和 desired goal 一致第二算法必须是 off-policy 的因为 HER 产生的是一种“反事实经验”需要依赖回放池反复离线采样才能发挥作用DDPG、SAC 这类算法天然适合而 PPO 这类 on-policy 算法很难直接受益第三目标之间的价值函数需要一定的泛化能力也就是下一节要讲的 UVFA 结构。满足这些条件HER 就能以极低的改造成本把稀疏奖励任务的训练效率提升一个量级。2. HER 核心机制拆解目标替换与奖励重计算2.1 目标条件价值函数UVFA让模型认识“目标”HER 能成立背后有一个非常重要的理论支撑Universal Value Function ApproximatorsUVFA通用价值函数逼近器。传统强化学习的价值函数只接受状态作为输入输出这个状态的期望回报Q(s, a)。但在多目标任务里同一个状态在不同目标下价值完全不一样——指尖在位置 A如果目标是位置 A那是成功如果目标是位置 B那还差得远。所以价值函数必须把目标也作为输入变成 Q(s, a, g)策略也随之变成 π(a | s, g)。这个改动看起来只是多了一个输入实际上是把“目标”从环境的固定属性变成了模型的泛化维度。UVFA 的底层假设是不同目标下的价值函数并不是相互独立的它们共享结构。比如“移动到 A 点”和“移动到 B 点”的动作策略高度相似价值函数也高度相似。模型可以在训练中同时接触大量不同目标其中很多是 HER 事后生成的反事实目标从而学会一种通用的“到达任意目标”的运动模式。我在实践中体会到HER 之所以效果好不光是因为它生成了更多正样本更因为它通过目标替换极大地丰富了目标空间的覆盖度逼着价值函数必须学会泛化否则它根本拟合不了这一堆乱七八糟的目标。2.2 四条目标替换策略final、episode、random、futureHER 原始论文里给出了四种选择额外目标的策略我用实际项目经验帮你逐一拆解策略新目标来源直觉我的使用评价final该 episode 最终状态作为目标“我虽然没到达指定目标但最终停在了别处就把那里当作目标重新学”简单好用适合短 episode 任务但目标多样性一般episode该 episode 中随机一个状态作为目标“随便挑一个路过的地方当作目标”目标多样性好但可能挑到和当前 transition 毫无关联的状态random从状态空间中随机采样一个状态作为目标“从经验池里随机捡一个目标出来重新标注”探索性最强但很多随机目标无法在该 episode 中实现反事实逻辑较弱future当前时刻之后某随机状态作为目标“未来某个时刻我到达了那里所以当前动作可以被解释为向那里前进”实测最好的策略兼顾因果方向与多样性关键点在于future 策略里的“未来状态”是从当前 transition 之后的某个时刻采样的它在时间上天然满足“先有动作、后有目标达成”的因果方向。虽然这个目标在原始 episode 里不是任务设定的目标但它确实是智能体“自己走出来的路”。把这条路反向包装成一个目标条件任务等于给智能体制造了大量“有人问路、恰巧走过”的经历。相比之下random 策略挑出的目标可能是在这个 episode 里从头到尾都没出现过、也不可能从当前状态到达的那条经验即便重新标注了目标学习信号也基本是噪声。所以如果你没时间调参我建议优先选 future实际效果通常比 final 高一截。2.3 奖励重标注把失败标记成成功之后再学要实现目标替换只换目标是没用的必须把奖励也重新算一遍。假设原始目标的奖励函数是稀疏二值形式def compute_reward(achieved_goal, desired_goal, threshold0.05): # 距离小于阈值则判定成功奖励0否则-1 if np.linalg.norm(achieved_goal - desired_goal) threshold: return 0.0 return -1.0当 HER 把一个 transition 的旧目标 g 替换成新目标 g 后奖励也必须从 R(s, g) 重算为 R(s, g)。特别地如果 g 是从这个 transition 之后的状态里采样的那么这条经验很可能从“失败”变成“成功”。一个典型的例子机械臂指尖在 t 时刻位于坐标 (0.1, 0.2, 0.3)执行动作 a 后在 t1 时刻到达 (0.15, 0.2, 0.3)旧目标在 (0.8, 0.9, 0.3)明显失败。但假如 future 策略选中的新目标恰好就是 (0.15, 0.2, 0.3)那这条 transition 的新奖励就是 0——成功。模型从这个 transition 学到的信息是“当你想到达 (0.15, 0.2, 0.3) 时在 (0.1, 0.2, 0.3) 执行动作 a 是有效的。”这个知识本身是正确且有价值的哪怕它对应的目标不是任务最终要求的。在使用 future 策略时还有一点值得注意如果整条轨迹都被重新标注那么越靠近轨迹末端的 transition其新目标越可能是“可达”的而越靠前的 transition新目标距离越远奖励可能还是 -1。这恰恰是合理的——它让价值函数在目标空间中逐渐建立起距离感而不是把所有反事实经验都强行标成成功。过度乐观的、全部成功的重标注反而会摧毁 Q 值的估计这一点我们在第四节详细说。2.4 关键直觉为什么“事后成功”也能教会模型很多初读论文的人会有一个疑问把失败轨迹强行标注成成功这不就是自欺欺人吗模型学到的东西靠谱吗我想用一个类比来解释这就像带新人时你让他照着地图找某个地址他走错了没找到。这时候你走过去指着他的当前位置说“行你虽然没找到指定地点但我告诉你你现在站的这个地方也是一个有效坐标。你回忆一下刚才从上一个路口走到这里的动作是不是就是‘从那个路口到这个地方’的正确走法”新人虽然没有完成原始任务但他确实学会了一段路径知识。HER 做的事情就是把整条失败轨迹拆成了一连串“局部正确”的路径知识再把它们喂给模型让模型把短程可达性泛化成长程规划能力。这个直觉成立还有一个现实支撑在连续控制任务中状态空间的拓扑连通性通常很好从 A 到 B 的路径知识可以从很多次“路过”中拼凑出来。HER 生成的额外目标大幅扩充了目标样本的覆盖范围间接起到了类似课程学习curriculum learning的作用——先学“从近处目标出发如何移动”再学“从远处目标出发如何规划”。当模型见过了足够多的“事后目标”它就能在测试时面对一个全新的目标也给出合理动作。3. 从零落地 HER采样器实现与 DDPG 训练实战3.1 环境选型为什么用 FetchReach 做第一个实验纸上谈兵再多不如跑一个实验。我建议你在 OpenAI Gym 的 MuJoCo 任务里挑 FetchReach 作为上手的第一关。这个任务很简单七自由度机械臂需要把末端执行器移动到随机指定的目标点状态空间是 25 维向量包括机械臂关节角、速度、指尖位置、目标位置等动作空间是 4 维的差分控制信号。奖励是典型的稀疏二值奖励指尖与目标距离小于 0.05 就给 0否则给 -1。这个环境复杂度适中单卡 CPU 就能训练几分钟就能看到效果非常适合验证 HER 的实现是否正确。训练框架我选择 DDPG而不是 SAC原因有三点一是 HER 论文原版就是围绕 DDPG 展开的对照实现更方便二是 DDPG 结构简洁actor-critic 两个网络改造成本低方便你把注意力集中在 HER 采样逻辑本身三是 FetchReach 的探索难度不高DDPG 加高斯噪声就能cover住。SAC 当然也行但我建议第一版先用 DDPG 打通整个流程确认 HER 的效果之后再换到更稳的 SAC 也不迟。3.2 核心代码HER 经验采样器先给出一份简化但可直接跑的 HER 采样器代码。它的核心职责是在完整 episode 结束后取出原始 transition再根据 future 策略生成额外目标和原始经验一起写入回放池。我用了 PyTorch 风格的伪代码但核心逻辑是通用的你迁移到 TensorFlow、JAX 或者其他框架也就是换个 API 的事。import random import numpy as np from collections import deque class HERSampler: def __init__(self, k4, strategyfuture): self.k k # 每个transition额外生成的目标数 self.strategy strategy # final / episode / random / future def process_episode(self, episode, replay_buffer, compute_reward): # episode是字典列表每个元素包含 # obs, action, obs_next, desired_goal, achieved_goal_next transitions [] # 先保留原始经验 for transition in episode: replay_buffer.add( transition[obs], transition[action], transition[obs_next], transition[desired_goal], transition[reward], transition[done] ) # 为每个transition生成k个额外目标 episode_length len(episode) for t, transition in enumerate(episode): # 根据策略选择额外目标 if self.strategy final: # 用episode最后状态作为目标 new_goal episode[-1][obs_next][achieved_goal] elif self.strategy episode: # 随机选一个episode内状态作为目标 future_idx random.randint(0, episode_length - 1) new_goal episode[future_idx][obs_next][achieved_goal] elif self.strategy random: # 从状态空间随机采样一个目标这里简化为随机选一个观察目标 new_goal random.choice(replay_buffer.goal_space) elif self.strategy future: # 从t之后的某个时刻采样目标保证因果方向 future_idx random.randint(t, episode_length - 1) new_goal episode[future_idx][obs_next][achieved_goal] else: raise ValueError(fUnknown strategy: {self.strategy}) obs transition[obs] action transition[action] obs_next transition[obs_next] original_reward transition[reward] for _ in range(self.k): # 基于新目标重新计算奖励 new_reward compute_reward( obs_next[achieved_goal], new_goal ) replay_buffer.add( obs, action, obs_next, new_goal, new_reward, False )这段代码里有几个容易被忽略的细节我吃了不少亏专门提一下。第一额外目标对应的 obs 和 obs_next 里有 achieved_goal 和 desired_goal 两个字段obs_next 保持不变只有 desired_goal 被替换成 new_goal同时你需要把当前 obs_next 的 achieved_goal 同步放进去否则网络输入里目标部分和实际状态会错位。第二希求done标志额外目标生成的过渡一般不要标记为终止doneFalse因为新目标并没有真正产生一个完整 episode 的终止条件强行标 True 会让价值函数产生错误的高估。第三random策略需要维护一个单独的 goal_space 集合这个集合可以在训练开始时从环境中采样一批状态构建也可以在训练过程中动态扩充目标空间覆盖越广random 策略越有效。3.3 奖励函数与数据字段设计奖励函数我直接用上一节的compute_reward重点是理解阈值 0.05 之下的判定逻辑。在 FetchReach 里这个阈值给得比较宽指尖离目标 5 厘米就算成功。这意味着 HER 生成的额外目标中只要未来采样到的状态离当前 transition 的下一状态足够近这条经验就会变成成功样本。实际打印日志时你会发现随着训练推进回放池里的成功样本占比从几乎为零逐渐涨到 20%、30%这个比例就是 HER 是否正常工作的一个直观指标。关于数据字段我强烈建议用统一的字典结构管理每条经验至少包含这些字段字段含义备注observation当前观测要包含 achieved_goal 和 desired_goal 两个入口action执行的动作连续动作向量next_observation执行动作后的观测用于计算 TD 目标reward当前经验奖励原始目标或反事实目标的奖励均已重算done是否终止原始 transition 保真反事实经验统一置 Falsedesired_goal本次经验使用的目标原始目标或反事实目标训练的时候采样 batch 后拆开各字段把 desired_goal 拼接到 critic 输入中把 observation 中的 achieved_goal 和 desired_goal 一起作为状态输入。很多新手在调网络时忘了给 actor 也接上 goal 输入导致策略网络完全不感知目标HER 效果直接归零。请你务必检查actor 输入是 (observation, desired_goal)输出是 actioncritic 输入是 (observation, desired_goal, action)输出是 Q 值。3.4 参数推荐与训练曲线观察我在 FetchReach 上验证过一组比较省心的参数直接给你抄作业超参数推荐值说明算法DDPGactor: 3层256宽 MLP, critic: 3层256宽 MLP学习率actor 1e-3, critic 1e-3用 Adam不用调得太小目标网络更新τ0.05soft updateFetchReach 任务环境稳定τ 可以略大探索噪声高斯噪声标准差0.2也可以用 OU 噪声但高斯更省事HER 采样策略future曲线最稳HER k4论文默认值内存足够可以试8回放池容量1e6必须够大反事实经验会很占空间每次采样 batch256主要耗时在采样取大点更稳训练步数100万步FetchReach成功率超过95%大概需要80万步训练过程中我建议你打印三个指标不要只看 average return。第一个是回放池中成功经验占比占比在 5% 到 30% 之间是比较健康的第二个是 eval 模式下的成功率也就是去掉探索噪声之后以任务原始目标评估的达标比例第三个是 Q 值分布注意观察 Q 值是否过度膨胀。在 FetchReach 上一个训练良好的 HER 模型成功率曲线通常会在 20 万到 50 万步之间出现一次明显的“阶跃式上升”从 20% 突然跳到 80%然后趋于稳定。看到这种曲线别慌这是正常现象——价值函数一旦学会了目标的泛化规律就进入加速期。4. 实操中常见的五个坑与排查心得4.1 训练曲线不涨先检查采样器别急着调网络HER 跑不出效果时我见过太多人第一反应是去调网络宽度、学习率结果折腾两天一无所获。我的经验是先怀疑采样器再怀疑奖励最后才怀疑网络。具体排查顺序是第一步看回放池里有没有生成成功样本——你在训练脚本里加一行统计打印 replay buffer 中 reward0 的经验比例。如果这个比例始终接近零那说明 HER 的目标替换没有正常工作或者替换目标距离阈值太远导致生成的所有反事实经验依然全部是 -1。这时候检查compute_reward的阈值、检查新目标的来源是否真的和 obs_next 里的 achieved_goal 足够近、检查 future 策略采样时是否用了正确的索引区间。第二步是打印一条经过重标注的 transition 出来人工检查字段是否对齐。我见过一个非常隐蔽的 bugobs_next 里的 achieved_goal 和目标替换后的 desired_goal 都更新了但网络输入时用的是 obs 里的 achieved_goal导致目标空间和状态空间错位模型根本学不到任何东西。这种问题不花点时间对着样例检查写测试都发现不了。4.2 目标表示不当导致泛化失败HER 的目标替换策略决定了模型必须对目标空间有良好的泛化能力。如果目标表示不合理比如直接用一个 one-hot 编码把每个目标独立对待或者目标坐标的数值范围跨度极大价值函数就很难把“到达目标 A”的经验迁移到“到达目标 B”上。FetchReach 这类低维连续控制任务里直接用原始坐标、加一个 LayerNorm 归一化就够了。但如果你想做图像目标的 HER事情就麻烦得多图像目标空间是高维像素空间直接作为网络输入会导致泛化困难通常需要先训练一个 VAE 或者自监督编码器把图像压缩成低维向量再在那个向量空间里做距离判断和目标替换。这一步本质上比 HER 本身更难调建议先从低维状态任务练手别一上来就挑战像素级 HER。4.3 HER 不是万能的三类典型失效场景第一类是“不可逆环境”。比如你有一个环境里有个开关按下去之后状态不可恢复future 策略可能会把“按开关之后”的状态当成之后某个时刻的新目标导致模型学到“我要从现在状态变到按完开关之后的状态”这个动作但真实物理过程根本回不去。这类环境里future 策略的因果假设会被破坏效果大打折扣。第二类是“目标空间远大于状态空间覆盖范围”。如果环境允许随机采样的目标大多数根本无法从当前状态到达future 策略生成的额外目标就很少落在可达区域内相当于凭空制造了很多不可达目标。这种情况下模型的 Q 值会偏向悲观训练变慢。解决方法是限制 future 采样窗口比如只从未来 10 到 30 步的范围内采样而不要从整个 episode 后半段随机采。第三类是“奖励不完全稀疏”。如果环境本身给了密集的奖励塑形HER 的价值信号会被原有塑形信号稀释反而降低训练效率。我在一个自定义环境里试过奖励函数原本有一个距离惩罚项加上 HER 之后模型更倾向于朝“可见的”即时奖励方向移动而忽略了 HER 提供的长远目标信息。这种情况下要么去掉密集塑形彻底走稀疏路线 HER要么把 HER 只放在最后一层目标判断上让它做辅助而不是主信号。4.4 优化 tricks课程学习与经验混合HER 虽然强但也不是银弹配合几个小技巧能进一步压榨效果。第一个技巧是混合经验池。不要把 HER 生成的经验和原始经验混在一个无差别池子里而是分别用两个 buffer 存放采样时按比例混合比如 50% 原始经验 50% HER 经验。这样可以防止后见之明经验淹没真实任务分布。我在 FetchPickAndPlace 上试过混合比例对最终成功率影响很大HER 占比过高会把模型带偏让它只擅长“去任意位置”而不擅长“去指定位置”。50% 到 70% 的 HER 占比通常是个合理区间。第二个技巧是配合课程式目标采样。你可以先让 HER 从靠近初始状态的目标练起等模型对近距离目标掌握得差不多了再逐步扩大目标采样范围。这等于在目标空间里做了一圈课程学习。实现起来不复杂只需要在采样 future 目标时增加一个距离门限训练过程中逐步放宽。这个 trick 在低维控制任务里能显著提升曲线稳定性。5. 把 hindsight 带出实验室从算法到工作方法论5.1 反事实思维与项目复盘HER 的核心是反事实推理如果当初的目标是 A但实际到达了 B那么“从之前状态到 B 的动作”可以被视为一次成功经验。这个思维方式用在项目复盘上意外地好用。我以前带过一个数据标注项目上线时指标不达标团队第一反应是模型不行。后来我们换了个视角不把“达到 KPI”当成唯一目标而是把项目过程中实际产出的中间信号——标注速度从每张 20 秒降到 8 秒、质检通过率从 70% 涨到 92%——一个个拿出来当作“新目标”重新审视。结果发现团队已经在很多子任务上取得了显著进展只是距离最终 KPI 还差一步。把每个中间状态当作里程碑来复盘你会清晰地看到一条“局部成功”的学习曲线这正是 HER 教给我的方法论。反事实思维对技术方案选型也很有帮助。我经常在项目初期问一个问题如果这个方案最终失败了我们大概率会卡在哪一步提前把那个“失败后的新目标”想清楚当下的决策就会变得更务实。这和 HER 的 future 策略逻辑如出一辙——从终点倒推过去给当前状态一个目标条件化的判断依据。5.2 后见之明偏差的警示有意思的是HER 利用的是后见之明但后见之明本身也是一把双刃剑。认知科学中hindsight bias 描述的是人在知道结果后会高估自己在事前预测到结果的能力。“我就知道会这样”这句经典台词恰恰是偏差的体现。在工程实践中这种偏差会让复盘变得毫无价值——如果你觉得失败完全可以预见就不会认真分析过程中的不确定性只会归因于“早就该这么做”。所以HER 给你的启发不是“以后都用后见之明说话”而是“把事后信息显式地转化为学习信号”。换句话说结果是结果学习是学习两者可以分离。项目复盘时我们既要允许自己用结果去重新标注过去的行为又要警惕“事后归因”替代“过程分析”。HER 算法之所以优雅就在于它把这种矛盾通过目标替换化解了新目标不是用来否认旧目标而是用来补充一组额外的学习样本。5.3 落地项目的三条实用建议如果你计划把 HER 用在自己的项目中我给三条非常务实的建议。第一条从小环境开始验证。不要一上来就挑战真实机器人或者超大规模仿真。先在 FetchReach 这类玩具环境上确认你的采样器、网络结构、奖励计算全部正确再迁移到自有任务。HER 的逻辑固然简单但错误的细节特别多用低成本环境先跑通是性价比最高的路径。第二条保留一个“无 HER 基线”。训练时间足够的情况下跑一条不使用 HER 的实验作为对照。我遇到过的情况是某个任务本身用普通 DDPG 加密集奖励也能达到 80% 成功率加 HER 之后反而掉到 60%。如果没有对照实验你会误判 HER 的效果。对照实验能帮你确定 HER 在你的任务里究竟是增益还是负担。第三条用日志验证采样逻辑而不是只盯训练曲线。在训练早期多打印一些 replay buffer 内部状态比如成功占比、目标距离分布、Q 值的均值方差。花十分钟看日志可能帮你省下三天找 bug 的时间。写在最后我的一些个人体会如果你问我跑 HER 实验最震撼我的时刻是什么我会说是第一次在 FetchReach 上看到成功率从 20% 突然跳到 85% 的那条曲线。在那之前我一直觉得稀疏奖励的连续控制任务必须靠精心设计的奖励塑形HER 让我意识到有时候换个角度给失败重新定义目标比绞尽脑汁修改环境要高效得多。这个思想不止适用于强化学习对工程管理、个人成长甚至团队复盘都有启发。最后再分享一个小技巧在实现 HER 时记得把compute_reward函数单独抽出来写单元测试用几个已知的输入输出组合验证它这个不起眼的函数决定了你整个训练曲线的质量值得多花十分钟保护它。
返回列表