ARTICLE DETAIL

资讯详情

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

HER算法实战:用“事后经验回放”破解稀疏奖励困境

HER算法实战:用“事后经验回放”破解稀疏奖励困境 如果你接触过强化学习对“稀疏奖励”这四个字一定不陌生。hindsight这个词日常语境下是“事后诸葛亮”可在强化学习圈子里它往往指代 Hindsight Experience Replay事后经验回放简称 HER——一个专门解决“奖励稀疏到根本学不动”这类问题的经典算法。没有 HER 之前我在机械臂抓取任务里跑随机策略跑了几百万步成功率还是 0把 HER 接进回放流程之后同样一套环境成功率能推到 80% 以上。这篇内容不打算从论文抄概念而是从“为什么要换目标”“代码怎么写”“参数怎么调”“踩了哪些坑”几个角度把这个算法的来龙去脉讲明白。适合正在做机器人控制、操作类任务或者刚被稀疏奖励折腾到怀疑人生的同学阅读。1. 为什么需要“事后诸葛亮”稀疏奖励到底难在哪1.1 先用一个生活类比理解 HER 的直觉打篮球的时候我想投一个三分球结果手一滑球传到了队友手里。这时候我不会傻到把这个动作定性为“投三分失败”而是会告诉自己刚才其实是想传球而且传得很准。于是这个动作在“传球”这个目标下变成了一次成功经验我就能从中总结出“怎么传球更稳”的技巧。HER 干的差不多就是这件事。强化学习里一个 episode 结束时如果没达到原始目标绝大部分算法会认为整条轨迹毫无价值奖励全是 0梯度信号也是 0策略根本动不了。HER 换了个视角它把这条轨迹实际到达的那个状态重新定义成“事后目标”hindsight goal再用这个新目标去重新计算奖励。这样一来原本看起来毫无信息量的探索轨迹就变成了一条“成功到达某状态”的经验可以拿去训练了。这个直觉说起来简单但背后的假设很关键任务必须是可以定义“目标状态”的也就是 goal-conditioned 的强化学习。如果任务的奖励只依赖一个固定终点没有可替换的目标变量HER 就无从下手。这也是为什么它常用于机械臂抓取、推箱子、导航这类任务而不是通用的游戏 AI。1.2 稀疏奖励的“冷启动”问题在稀疏奖励环境里奖励函数通常长这样只有 agent 到达目标 g 时才给 1其他时刻全是 0。问题就出在这个“只有”上。假设状态空间很大随机策略到达目标的概率是 p那么为了看到一次正奖励平均需要 1/p 次尝试。如果任务稍有难度p 可能小到 10^-6 甚至更低。更麻烦的是策略梯度算法在没有正奖励信号的时候更新方向和随机噪声没什么区别相当于在原地随机游走。你看着 reward 曲线一动不动实际上算法可能连“向目标靠近一步”这个最基本的行为都没学会。我当时用 DDPG 跑一个简单的二指机械臂推球任务初始阶段完全靠动作噪声在乱撞。几万步下来成功率始终是 0因为智能体从头到尾没拿到过一个正奖励价值函数对任何状态都输出 0策略自然也不知道哪个动作更好。这不是参数没调好的问题而是奖励信号本身缺失导致的“冷启动”困境。1.3 奖励塑形为什么治标不治本面对稀疏奖励很多人第一反应是加奖励塑形。比如“离目标越近奖励越高”“分阶段给中间奖励”。老实说这是最直接的办法我早期也这么干过但用久了明显感觉到几个硬伤。第一奖励塑形依赖人工设计每个新任务都要重新写一套而且容易出错。距离函数选欧氏距离还是曼哈顿距离要不要加速度惩罚这些细节都会影响最终策略。第二塑形后的奖励很容易被钻空子。智能体有时候并不会真正学会“达成目标”而是学会“停留在离目标最近的位置”因为那里奖励最高。这就是所谓的奖励黑客表面上 reward 很好看实际任务成功率一塌糊涂。第三它把“稀疏”问题变成了“怎么设计密集奖励”的问题并没有触及核心。HER 的思路则是不引入任何人工先验完全依靠“目标状态是否可达”这个客观信息。智能体到达不了原定目标但总能到达某个状态那就把这个状态当作目标来学。从数据中自动生成学习信号这是它比塑形优雅的地方。2. 核心细节解析HER 到底改变了什么2.1 事后目标替换把失败样本“洗白”成成功样本先看 HER 的操作层面。普通经验回放里一条经验是一个五元组 (s_t, a_t, r_t, s_{t1})其中 r_t 是智能体在原始目标 g 下拿到的奖励。HER 在把这条经验存入回放池之前会额外做一步从这条轨迹里挑一个状态 s_after把它当作事后目标 g然后重新计算奖励 r_t R(s_t, a_t, g)。关键在于如果 g 取的是未来某个时刻的状态那么 s_{t1} 很有可能和 g 非常接近。对于“到达目标则 1”的奖励函数来说r_t 大概率是正奖励。也就是说一条在原始目标下毫无价值的失败轨迹在事后目标下变成了一条“成功到达某状态”的有效样本。你可以把回放池想象成一个训练集HER 相当于在训练集里注入了大量“这个动作会导致那个状态”的正样本。价值函数会从这些样本里学到“接近目标状态的动作是有价值的”策略就慢慢不再是随机游走。注意这里的“洗白”不是说算法被骗了而是它确实学到了一个真实规律某些状态序列之间存在因果联系而这些联系在原始目标下被掩盖了。2.2 四种目标构造策略final、episode、future、random具体从哪挑出 s_after 作为事后目标论文和后续实现里常见四种策略这里一个个说。final直接用 episode 的最终状态作为事后目标。最简单只有一个候选适合 task 比较短、最终状态足够有代表性的场景。episode从整条轨迹的所有状态里随机抽一个。覆盖范围广但可能抽到太早的状态导致目标分布和“当前序列确实朝向它发展”这一点脱节。future从当前时刻之后的状态里随机抽一个。这个策略保证“轨迹确实是朝那个目标前进的”因果关系最强实践中最常用。random从经验回放里随便挑一个状态当目标。完全随机噪声大一般不会单独用。我自己的习惯是 future 占大头比如 80% 的未来目标 20% 的 final 目标。为什么不全用 future因为 future 目标都来自当前轨迹后半段目标分布会偏向“容易到达的状态”长期训练可能会让策略忽略较难的目标。掺一点 final 可以保留一些不同难度的样本。2.3 为什么 HER 必须配 off-policy 算法这里有一个容易被新手忽略的关键点HER 不是修改采样方式而是在回放时改写了“目标变量”和“奖励变量”。改写之后这条经验对应的策略分布已经变了。想要在这种数据上更新策略算法必须能脱离当前策略、直接利用历史数据学习。这正好是 off-policy 算法的看家本领所以 DQN、DDPG、SAC、TD3 都能和 HER 配合得很好。反过来说PPO 这类 on-policy 算法用 HER 就很别扭。PPO 更新时要求数据来自当前策略并且依赖轨迹的连续性计算 advantage。HER 一改写目标同一段轨迹在不同目标下的奖励会不同GAE 的优势估计就会乱掉。实际效果往往是训练过程极不稳定甚至比朴素策略梯度还差。所以别看到“经验回放”四个字就把 HER 塞给任意算法先搞清楚你用的是 on-policy 还是 off-policy。3. 实操过程从环境到训练一条龙3.1 环境选型用 Fetch 系列可以省一半力气想亲手复现 HER最省事的做法是用 OpenAI Fetch 系列环境比如 FetchReach、FetchPush、FetchPickAndPlace。这些环境天生就是 goal-conditioned观察空间是字典类型包含三个字段observation机器人自身的状态、achieved_goal当前实际达到的目标状态、desired_goal期望目标。环境内部还提供了 compute_reward 函数可以在任何状态-目标组合下计算奖励。如果你用的是自己的环境而不是现成的 Fetch那第一步就是把它包装成同样的结构。核心就一点环境必须能把“当前状态”拆成 observation 和 achieved_goal 两部分还要能根据任意 desired_goal 计算奖励。如果环境做不到这两条HER 根本没地方接。我当时为了一套工业分拣环境花了一整天改观测定义把像素坐标、夹爪位姿、目标点分开输出才让 HER 顺利跑起来。3.2 用 Stable-Baselines3 搭一个最小可跑示例实际写代码时我推荐直接用 Stable-Baselines3SB3里内置的 HerReplayBuffer少造轮子。下面这个例子是 SAC HER 跑 FetchReach 的配置。from stable_baselines3 import SAC from stable_baselines3.her import HerReplayBuffer from stable_baselines3.common.env_util import make_vec_env import gymnasium as gym env make_vec_env(FetchReach-v2, n_envs1) model SAC( MultiInputPolicy, env, replay_buffer_classHerReplayBuffer, replay_buffer_kwargsdict( n_sampled_goal4, goal_selection_strategyfuture, online_samplingTrue, max_episode_steps200, ), learning_starts1000, batch_size256, tau0.05, gamma0.95, verbose1, ) model.learn(total_timesteps200_000) model.save(her_fetchreach)跑完以后自己写一个成功率评估函数更靠谱。因为 goal-conditioned 环境下只看 reward 很容易被误导真正关心的是“多个随机目标下智能体能成功几次”。import numpy as np def evaluate_success(model, vec_env, n_episodes20): success_count 0 for _ in range(n_episodes): obs, _ vec_env.reset() done False while not done: action, _ model.predict(obs, deterministicTrue) obs, reward, terminated, truncated, info vec_env.step(action) done terminated or truncated if terminated and is_success in info[0]: success_count int(info[0][is_success]) break return success_count / n_episodes print(evaluate_success(model, env, 50))这套组合在 FetchReach 上一般十几万步就能看到明显效果成功率能到 80% 以上。FetchPush 难一些需要百万步量级。FetchPickAndPlace 更吃时间两百万步打底。3.3 参数到底是拍脑袋还是算出来的HER 有几个参数直接决定训练质量和资源消耗别拿到手就默认值跑到底。n_sampled_goal每条原始经验额外生成多少个事后目标。这个值会直接放大回放池的容量每存一条经验实际占用 1 n_sampled_goal 条样本的空间。经验上取 4 到 8 比较好太大会导致内存溢出太小则事后目标样本不够。goal_selection_strategy目标选择策略我建议从 future 起步训练稳定后再考虑混合。max_episode_stepsSB3 的 HerReplayBuffer 需要你明确传入 episode 最大步数没有这个值它没法从轨迹中定位“未来状态”。忘记传会直接报错或者采不到有效目标。gamma如果 episode 不算长0.95 就够了。设成 0.99 会让值函数收敛变慢对稀疏奖励场景其实没必要。batch_size我习惯用 256 以上因为 HER 数据多样性高小 batch 更新容易抖动。还有一个经常被忽略的点探索噪声。SAC 本身就带一定随机性但如果你用 DDPG 配 HER一定要给动作加噪声否则前期的成功样本太少HER 再厉害也救不回来。3.4 训练曲线怎么看在 HER 场景下我建议把成功率曲线当作主指标reward 曲线只做参考。原因很简单HER 的回放池里掺杂了大量“改写目标后的奖励”平均 reward 并不能真实反映策略在原始目标上的表现。我见过很多次这种情况训练刚起步时 reward 不涨反降因为事后目标样本刚开始大量填充回放池相当于人为制造了很多“成功”。但如果这时候去看 rollout 成功率会发现它在稳步上升。等到策略逐渐学会向目标靠近成功率会突然跳升从 10% 到 80% 往往就是几万步之间的事。这种“跳变”是稀疏奖励任务里很典型的特征。4. 常见问题与排查技巧实录4.1 回放池内存爆炸n_sampled_goal 的账不能不算HER 最容易被低估的副作用就是内存消耗。n_sampled_goal 设为 4意味着存 10 万条原始经验实际要存 50 万条样本。如果每条状态是 281 维的向量float32 存储一个样本就要 1124 字节50 万条就是 562MB。如果任务复杂、状态维度更高破 GB 是分分钟的事。我踩过一次坑在 FetchPickAndPlace 上想靠增大 buffer 提升性能直接把内存吃满了。后来换成减少 n_sampled_goal、适当缩短 max_episode_steps并用更紧凑的向量化存储才算稳住。如果你用的是自定义环境先算清楚单条样本的字节数再定 buffer 上限。4.2 为什么我用 PPO HER 跑不动不少同学看到 HER 效果好第一反应是在自己熟悉的 PPO 流程里加一个 HER 模块。跑出来效果很差然后怀疑参数没调好。实际上问题出在算法性质上。PPO 依赖重要采样修正要求当前更新的数据确实由当前策略产生。HER 改写目标后数据里的“目标”已经不是采样的原始目标了重要采样权重就没法正确计算。再加上 PPO 用 GAE 估计优势GAE 基于整条轨迹的回报一致性HER 会让同一轨迹在不同目标下产生不同奖励优势估计直接失真。所以 HER 在 on-policy 框架里不是参数问题是结构问题。老老实实用 SAC、DDPG、TD3 这类 off-policy 算法就行。4.3 训练没效果先别急着调参检查这三件事如果 HER 跑了一阵子成功率纹丝不动我建议按顺序排查三件事。第一环境是否正确返回了 achieved_goal。很多自定义环境为了省事把 achieved_goal 写成一个固定值HER 一替换目标就会发现所有“事后目标”都一样学习信号完全失效。打印几个 achieved_goal 出来确认它们会随时间变化。第二compute_reward 是否真的根据 desired_goal 计算而不是写死“只要到达 A 点就成功”。HER 的核心能力是评估任意目标如果 reward 函数只认固定目标替换后的样本全是误导。第三初始探索是否充分。HER 需要数据里有足够多“途中有变化”的轨迹。如果动作噪声太小每个 episode 基本原地踏步事后目标全是初始状态等于什么都没学到。检查 rollout 里 achieved_goal 的方差方差太小就加大动作噪声或调高探索参数。4.4 成功率上去了但泛化差HER 训练出来的策略有时候在测试集上表现不错但换一批新目标就垮掉。这个问题通常出在目标分布的“过拟合”上。因为 future 策略偏爱从当前轨迹后半段采样目标这些目标往往集中在容易到达的状态附近。随着训练推进回放池里的目标分布会越来越“偏科”策略就只擅长那些简单目标。想缓解的话我建议在目标选择策略里保留一部分 random 或 episode 采样的样本让目标覆盖更广。更彻底的办法是训练时对目标采样空间做随机化让 desired_goal 本身均匀覆盖整个可达区域。HER 不是万能的它擅长把“可达”变成“可学”但“要学哪些可达目标”仍然需要你来约束。5. 延伸思考HER 的边界在哪里5.1 只有当“目标可达”时 HER 才有效HER 的根本假设是从任意状态出发总能到达某个状态并且这个状态可以当作目标来学习。一旦这个假设不成立HER 就会失效。举个例子一个任务要求依次按下三个开关才能开门门开后才算成功。这种情况下只按开关 1 得到的“事后目标”对最终任务没有任何帮助HER 会往回放池里塞大量“只按了一个开关就成功”的无意义样本。更极端的情况是目标状态孤立且不可达HER 就会学到原地不动这种投机行为因为“保持原状”确实可以被当成一个事后目标。所以 HER 比较适合连续控制、机器人操作这类“状态空间连续、目标分布自然连续”的任务而不是离散逻辑型任务。5.2 HER 和课程学习、分层强化学习的组合思路HER 不是孤立使用的它和其他技术叠起来效果更好。比如用自动课程学习如 HGGHindsight Goal Generation来挑选更有价值的事后目标而不是完全随机选或者把 HER 作为下层控制器上层用课程策略决定当前该训练哪个目标这就是分层思路的雏形。我最近在尝试的做法是先用 HER 在仿真里训练一个“目标到达”的底层策略然后冻结底层在它上面学一个高层任务规划器。这个架构在分拣任务上效果不错底层不需要频繁重训高层只需要负责目标选择。这算是一个比较自然的扩展方向如果大家感兴趣我后续可以专门写一篇分层 HER 的实战笔记。6. 我踩过的坑和一些私货6.1 一次失败的目标定义经历有一回做真实分拣项目我把目标定义成机械臂末端的完整位姿向量包括位置和姿态。结果 HER 训练了很久成功率一直上不去。后来发现姿态部分对任务并不重要而且姿态空间太复杂随机探索很难覆盖到实际可达的子空间。我把目标改成“抓取点的三维坐标加上夹爪张开距离”之后训练曲线立刻正常了。这个经历让我养成了一个习惯目标定义一定要和任务核心强相关能少一个维度就少一个维度。目标空间维度越低HER 的事后目标越容易覆盖成功率上线就越快。不要贪图“信息完整”而把无关维度塞进目标里。6.2 一个调参救活项目的案例另一个印象比较深的项目是帮朋友调试一个类似 FetchPush 的推箱子任务。他跑了两百万步成功率只有不到 5%。我先看了回放池发现 future 采样比例太高目标几乎都是轨迹末端的局部状态导致策略只会推箱子到一个固定角落。我把目标选择策略从纯 future 改成 future 0.6 episode 0.2 final 0.2最后一次性把 n_sampled_goal 从 8 降到 4因为他的机器内存已经到瓶颈。改完后再跑五十万步成功率到了 60% 以上。所以如果你遇到 HER 训练效果平平先别怀疑算法本身把目标选择策略、目标空间维度、内存约束这三个点逐一审一遍往往比盲目堆训练步数更有效。HER 这套“事后诸葛亮”的思路说到底是一种很优雅的数据增强方式。它没有引入复杂的网络结构没有改变目标函数只是换了一个角度看经验数据就能把稀疏奖励的冷启动问题化解大半。真心建议正在搞机器人控制的同学动手试一次 HER你会发现原来跑不动的任务其实只是缺了一双“事后”的眼睛。
返回列表