ARTICLE DETAIL

资讯详情

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

HER算法实战:稀疏奖励下的经验重放机制与强化学习调参指南

HER算法实战:稀疏奖励下的经验重放机制与强化学习调参指南 hindsight 这个词英文直译过来就是“事后诸葛”或“后见之明”。在强化学习圈子里它是一个名字非常贴切的算法Hindsight Experience Replay一般简写为 HER。我第一次跑机器人抓取仿真任务时被稀疏奖励折磨到怀疑人生——agent 在几千个 episode 里一点正向反馈都拿不到训练曲线纹丝不动。后来改用 HER曲线开始肉眼可见地往上走。这篇文章不打算复述论文我想按照自己的实操经历把 HER 的原理、实现细节、调参经验和踩过的坑完整梳理一遍。如果你正在做机器人控制、游戏 AI、复杂决策或者任何“奖励给得特别抠门”的强化学习项目这篇应该能帮你省下大量盲目调参的时间。1. 这不是自欺欺人HER 如何把“失败”变成“成功”1.1 稀疏奖励为什么是强化学习的噩梦强化学习的本质是试错agent 通过与环境交互获得奖励再根据奖励修正自己的策略。这句话听起来简单但有一个致命前提——奖励必须能有效地区分“好行为”和“坏行为”。一旦奖励特别稀疏这个前提就不成立了。举个最直观的例子。假设一个 5x5 的网格目标在角落agent 每一步只能上下左右随机移动一次。如果只有到达角落才给奖励 1其他情况都是 0那么一次完整 episode 里碰到奖励的概率大约千分之一甚至更低。在如此长的探索链条里agent 看到的每个动作回报都是 0它根本不知道哪一步是对的——所有轨迹看起来都“同样失败”。机械臂任务也一样。FetchReach 这类环境中要求机械臂末端在 5 厘米误差内到达目标点。随机探索时末端几乎不可能精准落在小球附近整条轨迹的奖励全是 -1。没有梯度、没有信号、没有区分度策略网络无论怎么更新都是在原地打转。这就是著名的稀疏奖励问题。常规解法有三类一是做 reward shaping人为构造稠密奖励比如“越靠近目标奖励越大”但这需要极强的领域知识而且设计不好很容易引入局部最优agent 可能学会在目标附近来回蹭奖励而不真正完成任务二是引入专家演示让 agent 模仿但采集演示数据本身很贵三是做课程学习从简单任务逐步过渡到难任务但课程节奏很难把握。HER 的思路不在这些范畴里它从“失败轨迹”本身挖掘信号不需要额外知识。1.2 “事后诸葛”到底对轨迹做了什么HER 的核心动作可以用一句话概括把一条失败的轨迹在事后重新标记成一条成功的轨迹。具体展开是这样的。传统强化学习中agent 带着一个目标 g 进入环境每一步都往这个目标努力。一个 episode 结束后如果没到达 g这条轨迹就被标记成失败直接扔进历史。HER 的做法是不扔而是回头审视这条轨迹把轨迹中某个实际到达的状态 φ(s) 提取出来把它当成“新目标” g φ(s)然后用这个新目标重新计算每一步的奖励形成一组新的“成功经验”存进缓冲区。我说一个生活化的类比。初学者射箭目标是靶心但箭总是射偏。如果只按“是否中靶心”评判十箭九败训练数据全是“失败”。但换个角度第一箭落在了靶左上方那就把“射中左上角”当作目标——发现自己确实射中了第二箭落在了靶右下方那就把“射中右下角”当作目标——也确实射中了。每一箭都变成了一次“成功命中”。虽然这些目标不是最初的靶心但通过反复练习不同落点射箭者逐渐学会了控制力度、角度和呼吸。这种控制能力是可迁移的最终会帮助他命中真正的靶心。HER 在强化学习里的作用正是如此。机械臂想抓取物体 A最后却把物体推到了位置 B。传统算法说“你失败了。” HER 说“你成功地把物体推到了 B我们把这个动作和状态存下来作为‘推到 B’这个任务的成功经验。” 下次 agent 再遇到“推到类似位置”的目标时它就知道该怎么做。这些重标记后的小成功一点点累积成对环境的正确认知。1.3 为什么这不算作弊很多人第一次看到 HER 会有同样的疑问这不是自己骗自己吗把失败说成成功agent 学到的不是虚假信息吗关键在于理解一个细节agent 并没有在新目标 g 上学到“自己完成了原目标 g”的错误信息。它学习的是“对于目标 g我采取的动作是正确的”。这两件事在数学上不冲突。要从更严谨的角度解释得引入目标条件价值函数。在目标条件强化学习中价值函数写成 V(s, g)意思是“在状态 s 下以 g 为目标未来期望回报是多少”。这个函数针对不同目标 g 都有对应的值。HER 做的事情是把真实观测到的状态转移 (s, a, r, s) 与新目标 g 组合成新样本。论回报这个样本是自洽的s 确实到达了 g所以奖励是 0之前那些步骤没有到达 g奖励是 -1。这样一组样本放在一起训练出来的 V(s, g) 是符合“目标 g 下的真实经验”的没有伪造数据。换句话说HER 只是把“原本被丢弃的、但包含有效因果关系的经验”重新利用了。一条轨迹对于目标 A 是失败对于目标 B 可能是成功而这条轨迹里动作与状态转移的因果关系是客观真实的。HER 唯一额外的假设是我们能拿到“状态到已达成目标的映射” φ(s)。在仿真环境里这个映射就是物体的位置坐标在真实机器人上它来自关节角、末端位置、摄像头识别结果都是现成的。所以这不算作弊而是一种更高效的经验利用方式。2. 算法核心目标重标注策略与 off-policy 的搭配逻辑2.1 目标条件强化学习的标准框架要落地实现 HER必须先理解它依赖的环境建模方式。传统 MDP 定义是 (S, A, P, R, γ)HER 需要在里面额外加入两个东西目标集合 G以及从状态到已达成目标的映射 φ: S → G。于是策略网络从 π(a|s) 变成 π(a|s, g)actor 的输入多了一个“期望目标”。奖励函数的写法也要规范化一般用稀疏形式r(s, g, s) -1 如果 ||φ(s) - g|| threshold 0 如果 ||φ(s) - g|| threshold这里 threshold 是任务定义的“成功判定精度”Fetch 类任务一般取 0.05。注意这个奖励只看“下一步状态是否达到目标”跟动作本身无关。这样做的好处是一旦把目标 g 换成 g奖励可以直接重算不需要保存复杂的额外信息。训练过程中agent 与环境交互得到原始轨迹轨迹里每一步都记录了观测、动作、奖励、下一状态、目标。HER 在存储到 replay buffer 之前对轨迹做“重标记”处理生成若干条以新目标为目标的新轨迹。这就是全部框架。是不是比想象中简单但简单之中藏着很多需要抠细节的地方。2.2 四种重标注策略final、episode、random、futureHER 原论文提出了四种重标注策略实践中效果差异很大。用表格整理一下策略新目标来源特点final当前 episode 最后一步的状态最简单一条轨迹只产生一个重标记目标目标分布偏“终点”。episode当前 episode 内随机取 k 个状态覆盖面广但可能选到与当前转移“因果不相关”的远未来状态。random从整个 replay buffer 里随机取 k 个状态目标分布最广但跨 episode 组合可能不协调实测效果一般。future当前 episode 内、当前时刻之后的状态里随机取 k 个与当前行为轨迹时序对齐论文和后续复现都认为效果最好。“future”所以好原因有两个。第一新目标来自同一轨迹的“未来时刻”这意味着 agent 在当前策略下确实能到达这个位置重标记样本不是空中楼阁第二从时序上看“在 t 时刻经过这个位置后来又在 t 时刻到达这个位置”是因果自洽的agent 学到的是实实在在的“如何到达这里”的经验而不是随机的、可能根本到不了的目标。原论文以及 OpenAI 的默认配置里future 策略一般取 k4也就是说每条原始轨迹额外生成 4 条重标记轨迹。这个比例不是拍脑袋定的后面我会在调参部分展开讲为什么不是越大越好。2.3 为什么 HER 必须搭配 off-policy 算法HER 和 off-policy 算法是绑定的这不是偶然而是算法机制决定的。原因在于重标记操作改变了样本中的目标和奖励。这意味着agent 用于更新的数据其“行为策略”与“更新策略”已经不对应了——它正在学习的目标 g 根本是另一个任务的奖励。on-policy 算法如 PPO、A2C、REINFORCE有个严格假设更新时使用的数据必须由当前策略产生。一旦你把过去的轨迹拿出来改标签再更新这个假设就被打破了策略梯度估计直接产生偏差。off-policy 算法则没有这个问题。以 DDPG 为代表的 Actor-Critic 方法天然使用 replay buffer更新时采用 Q 函数作为目标对“数据由哪个老策略产生”不敏感。重标记后的一条样本 (s, g, a, r, s)在 TD 目标中表现为y r γ * Q(s, μ(s, g), g)这条式子里的 r 是基于新目标计算出来的Q 的输入也带上了新目标 g整个目标在数学上是自洽的。所以replay buffer 里混杂着原始目标样本和重标记样本完全不影响 Q 学习收敛。这也是为什么 HER 的经典搭档是 DDPG后续的 TD3、SAC 也能无缝适配。2.4 从 DDPG 到 TD3/SAC怎么选原论文用的强化学习底座是 DDPG因为当时它在连续控制里表现稳定。但 DDPG 对超参数敏感是出了名的我实际使用中更推荐在 HER 上面配合 TD3 或 SAC。SAC 是随机策略自带熵正则探索更稳定TD3 是确定性策略适合那些动作维度低、需要精确控制的机器人任务。两者都可以直接套用 HER 的重放缓冲区逻辑。如果你用 stable-baselines3它内置了HerReplayBuffer但注意只能和 off-policy 算法一起用比如 DDPG、TD3、SAC在文档里已经写得比较明确。我的个人选择是优先用 TD3 HER因为机器人连续控制场景里 TD3 的稳定性比 DDPG 好太多在两个目标价值函数取最小值的机制下Q 值过估计的问题被压得很低。而如果你需要面对高维动作空间或需要对探索更宽容再考虑 SAC。3. 实操从零搭建一套 HER 训练流程3.1 环境选型从玩具任务到机械臂先推荐两个我用得比较顺的环境组合。第一个是 OpenAI Gym 里经典的多目标机器人任务FetchReach、FetchPush、FetchSlide、FetchPickAndPlace。以 FetchReach 为例观测向量是 25 维包含机械臂末端位置、物体位置、速度等信息动作是 4 维连续控制目标是一个 3 维坐标。成功条件是末端位置与目标的欧氏距离小于 0.05。这类环境天然带achieved_goal这个概念正是 HER 需要的。第二个是 BitFlip 这种离散玩具任务。环境里有 n 个位比如 8 个 bit目标是翻转成指定的 0/1 序列。每一步可以翻转任意一位只有完全匹配才给奖励。BitFlip 的优点是训练快、调试方便几分钟就能跑完一轮实验特别适合验证你的 HER 实现是否正确再去跑 Fetch 系列。3.2 核心改造重放缓冲区里的重标记代码HER 的工程实现核心不在网络结构而在 replay buffer 的重标记逻辑。下面我给出一个用 future 策略实现重标记的 Python 片段这是我实际验证过可以跑的简化版本import numpy as np def sparse_reward(achieved_goal, desired_goal, threshold0.05): # 距目标小于阈值给 0否则 -1 dist np.linalg.norm(achieved_goal - desired_goal, axis-1) return -(dist threshold).astype(np.float32) def her_relabel(episode, k4): episode: list of tuples (obs, action, reward, next_obs, done, goal, achieved_goal_from_next_obs) transitions [] T len(episode) for t in range(T): obs, act, rew, next_obs, done, goal, achieved episode[t] # 先存原始轨迹 transitions.append((obs, act, rew, next_obs, done, goal, achieved)) if t T - 1: # 最后一个时刻没有“未来”可采样 continue # future 策略从 t1 到末尾之间随机选 k 个状态当作新目标 future_idxs np.random.randint(t 1, T, sizek) for idx in future_idxs: new_goal episode[idx][-1] # 拿那个时刻的 achieved_goal new_rew sparse_reward(achieved, new_goal) new_done bool(new_rew 0) # 重要重标记后要重新算 done transitions.append((obs, act, new_rew, next_obs, new_done, new_goal, achieved)) return transitions有几个细节值得强调。第一achieved必须取next_obs对应的已达成目标而不是obs的否则奖励计算错位agent 永远学不到“这一步到达了目标”。第二new_done必须重新计算。原始轨迹中 done1 可能是因为到达了原始目标重标记后目标变成 g此时如果新目标没被当前状态达到done 应该置 0。这一步很多人会漏掉一旦漏了TD 目标里会出现大量虚假终止严重破坏价值函数。这个函数处理完一个 episode 后得到的是原始 k1 倍数量的转移样本。实际训练循环中我把每条原始轨迹立刻做重标记然后把所有样本塞进全局 replay buffer再从中按 batch 采样更新网络。3.3 Actor-Critic 网络与目标输入的处理网络结构可以复用标准 DDPG/TD3 的架构关键在多了一个目标输入。最简单最稳妥的做法是把观测 obs 和期望目标 goal 做 concat一起作为网络第一层的输入。Fetch 环境下 obs 是 25 维goal 是 3 维拼起来 28 维后面接两个 256 宽度的全连接层激活函数 ReLUcritic 输出一个 Q 值。这里有一个非常影响性能的细节输入归一化。Fetch 环境里坐标量级可能是小数但速度、关节角等量级可能差出几个数量级。如果不归一化网络很容易被某个大尺度特征带偏训练又慢又不稳。我在实践中会在网络第一层之前加一个 normalization 层用 replay buffer 里数据的 running mean 和 running variance 做标准化。OpenAI Baselines 里用的是 VecNormalize后来 stable-baselines3 也支持类似机制。策略输出的动作要加探索噪声。DDPG 风格是加上高斯噪声 N(0, σ)σ 一般取 0.2并 clip 到动作边界里防止探索方向过于狂野导致环境崩溃。3.4 超参数配置与训练曲线怎么看我整理一份可以直接当起点的配置它基本复刻了 OpenAI Baselines 里 HER 任务的经典参数适合 FetchReach 这类环境参数取值说明算法TD3 或 DDPGTD3 更稳batch size256采样更新时一个 batch 的样本数γ0.98折扣因子tau0.05目标网络软更新系数actor/critic lr1e-3Adam 学习率replay buffer 容量1e6容量要大HER 对历史经验依赖重k4每条轨迹额外重标记 4 条探索噪声 σ0.2动作高斯噪声标准差训练时重点看的指标不是 actor loss 或 critic loss而是评估成功率——每隔固定轮次在环境中跑若干个 episode关闭探索噪声统计“原始目标”的达成率。这个指标才能反映任务是否真的被解决。判别 loss 下降有时候只是价值函数在拟合重标记数据不代表策略变好。以 FetchReach 为例配置正确的情况下50 个 epoch 内成功率一般能冲到 90% 以上我最快跑过十几轮就开始起飞。FetchPickAndPlace 难度大得多可能要到 80 个 epoch 之后才稳定上升而且曲线不像 FetchReach 那么平滑会先出现一段“看起来毫无进展”的平地然后突然抬头。遇到这种平地不要慌多观察一阵HER 的信号积累需要时间。4. 常见问题与排查技巧实录4.1 训练不收敛先查这张速查表我在实际项目中踩过不少坑也帮同事排查过不少问题。把它们整理成一张速查表遇到问题按图索骥最省时间症状可能原因处理方式曲线完全不动成功率始终 0探索噪声太小奖励阈值过严目标未归一化检查 σ确认 threshold加输入归一化曲线缓慢但到不了高成功率k 值太小信号不足k 从 4 提到 8观察是否改善早期涨得好后期回退replay buffer 里旧目标分布偏移更新过于频繁增大 buffer或降低 update-to-data ratio训练过程 Q 值异常大critic 过估计TD3 双 Q 未生效确认 TD3 的 min(Q1, Q2) 逻辑检查奖励尺度成功率在 60% 左右震荡动作噪声过大目标分布不均衡降低 σ试试 final 策略与 future 混合用 PPO 套 HER 不收敛on-policy 算法与重标记机制不兼容换 DDPG/TD3/SAC这张表里我最想强调的其实是最后一条因为太容易踩。很多人看到 HER“能在失败里学到东西”第一反应是把 HER 塞到当前正在用的 PPO 里。结果是训练完全不稳定回报忽高忽低于是怀疑实现有问题。但实际上HER 的重标记改变了样本分布PPO 的 clip 目标和优势估计都建立在“当前策略采样数据”这个前提上两者天生不搭。HER 不是万能的插件它只适合 off-policy 家族。4.2 重标记后的 done 标志必须重算前面代码里我专门留了一行注释这里展开讲因为它值得单独占一个章节。原始 episode 里如果 agent 到达了目标 g那么最后一个转移的 done 是 True。但 HER 会把中间某个状态 s_f 提取成新目标 g此时我们必须重新检查s_f 对应的 achieved_goal 是否等于新目标如果不等于就要把 done 改成 False否则价值函数的 bootstrap 会在 episode 中途“截断”。举个例子。episode 里第 10 步机械臂偶然碰到了目标点 A此时 doneTrue。重标记时选了第 3 步的状态作为新目标那么第 9 步到第 10 步这个转移被重标记成“以第 3 步状态为目标”。显然第 10 步并没有到达第 3 步的位置它只是到达了 A。如果 done 保留 TrueTD 目标就变成 y r 0而正确应该是 y r γ * Q(next_state, goal)。一次两次无所谓大量样本叠加起来Q 函数会被严重拉低学习速度骤降。4.3 k 值不是越大越好四个重标记策略里 future 默认 k4但这个数字背后有讲究。k 太小一条轨迹只产生少量正样本稀疏问题没有实质缓解k 太大比如 16缓冲区里“成功样本”比例过高目标分布偏向那些容易到达的状态agent 会过度优化简单目标反而学不会原始难点。我实测下来k4 到 k8 之间差别不大但要达到 90% 以上成功率k4 足够再往上收益递减且拖慢训练。还有一个额外技巧可以让重标记的目标来源和原始目标混合。比如 70% 的样本用 future 重标记目标30% 保留原始目标。这样既能保证原始目标的学习压力不丢失又能获得重标记信号的密度提升。OpenAI Baselines 里其实没有混合得这么细但我在自己的项目里试过对某些困难任务确实有帮助。4.4 和 reward shaping 怎么配合一种常见的心态是“HER 已经解决了稀疏奖励那我就不需要 reward shaping 了吧”。实际上两者可以叠加但要注意尺度匹配。我的经验是先用纯稀疏奖励 HER 跑通一版如果收敛速度确实太慢再在原有稀疏奖励基础上加一个很小的稠密项比如每一步减去 0.1 倍的当前距离。注意这个稠密项的量级要比稀疏奖励的 -1 小很多否则 agent 会为了减小距离而在原地磨蹭反而影响原始目标的达成。还有一点必须提醒如果再加入 reward shaping重标记样本的奖励也要用同样的规则重新计算。HER 的核心是“每个样本的奖励与目标自洽”如果你只给原始样本加 shaping 而不给重标记样本加两种样本的奖励尺度不一致critic 学习会混乱。5. 扩展HER 在真实项目中的形态与个人经验5.1 从仿真到实物三个容易被忽视的坑仿真里能把 HER 跑得很漂亮不等于搬到真实机器人上就一帆风顺。我在真实机械臂项目里遇到的主要问题有三个。第一个是 achieved_goal 的测量噪声。仿真里 φ(s) 是精确坐标真实环境下则是通过关节编码器、摄像头或者力传感器估算出来的。噪声会直接污染重标记目标。我的做法是在目标选择和成功判定时把 threshold 放宽一点同时给观测加一点高斯噪声做对抗训练让策略对测量误差鲁棒一些。第二个是动力学差异。仿真的物理模型和真实机械臂之间总有偏差HER 学到的是仿真里的规律。解决思路是 domain randomization在仿真里随机化物体质量、摩擦系数、推力大小让 agent 见过更多动力学变化再迁移到真实环境时就不容易懵。第三个是真实交互的成本。HER 虽然降低了样本复杂度但真实机器人的采样速度远低于仿真一次 rollout 几秒钟一天也攒不了多少数据。必须配合异步数据采集、预训练仿真策略、或者人在回路的半自动标注来缓解。这里 HER 反而有个隐性优势它不需要人为标注经验能自动把真实失败轨迹中的信息榨出来。5.2 HER 的变体与后续发展HER 提出之后涌现了不少变体核心都是在“如何选择更合适的新目标”上做文章。比如 curriculum-guided HER 会根据当前策略的能力自动调整目标难度避免目标长期停留在太简单或太难的区域还有用生成模型来产生目标的 hindsight goal generation试图把重标记目标从“轨迹实际经过的状态”扩展到“更合理的、可能到达的状态”。工程上这些变体的代码落地大多仍然基于 standard HER 的 replay buffer 改造所以把基础版吃透再扩展并不难。开源实现方面我常用的有三个OpenAI Baselines 的 her 分支原始实现代码风格比较老但权威、stable-baselines3 的 HerReplayBuffer接口友好和 TD3/SAC 直接集成、以及 rlkit 里的 HER 实现更适合做研究性质的多目标任务。如果你只是想快速验证我建议从 stable-baselines3 入手它把环境 wrapper、采样循环和重放逻辑封装得很完整。5.3 我的个人实操体会最后分享几条我自己的土办法不一定写在论文里但很管用。第一任何新环境上都先跑 BitFlip 玩具来验证实现。BitFlip 几分钟就能出结果如果连这个都学不会肯定是代码里重标记逻辑有问题而不会浪费几小时去调 Fetch 的复杂环境。第二训练时一定要用 TensorBoard 记录重标记样本中“即时奖励为 0”的样本比例。这个比例如果一直是零说明重标记目标选得太远agent 一个都达不到HER 等于白做比例如果太高比如超过 80%说明目标选得太容易agent 可能在偷懒。理想区间大致在 30% 到 60% 之间。第三随机种子影响很大。HER 的效果方差比我预想的大同一个超参数配置换三个种子成功率可能从 80% 跳到 95% 再掉到 70%。判断方案好坏一定要跑多个种子取均值别拿单次结果当结论。第四HER 给你的是“能学的信号”不是“最优的目标”。不要因为训练曲线好看就忘记原始任务目标每轮评估时一定要回到原始目标上计算成功率而不是看重标记目标的达成率。在我自己的机器人仿真项目里HER 不是银弹但它的确是处理稀疏奖励场景时最值得先试的方案。如果你正被一个“怎么都学不会”的任务卡住我建议你先把奖励简化成最朴素的 0/-1套上 HER 的 future 策略k4跑一版看看。很多时候不是算法有问题而是你还没给它一个“事后重看一遍自己轨迹”的机会。
返回列表