ARTICLE DETAIL

资讯详情

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

强化学习稀疏奖励难题与HER事后重标注解法

强化学习稀疏奖励难题与HER事后重标注解法 1. 为什么稀疏奖励会让强化学习“学不动”1.1 稀疏奖励问题的本质做机器人抓取、机械臂推箱子、导航这类任务时最让人头疼的不是模型不够大、不是算力不够强而是奖励信号根本给不到。环境里只有一个很稀疏的判定你抓到物体了给 1没抓到给 0。其余所有时间步agent 收到的都是同一个数字0。这带来的问题很致命。强化学习的核心是“用奖励梯度引导策略”但当奖励为 0 时策略梯度算出来的更新量几乎为零agent 学不到任何“往哪个方向调整更好”。用大白话说它就像一个在完全漆黑的迷宫里找出口的人没有地图、没有声音反馈、连“走错了”的提示都没有只能靠乱撞——而且撞了也白撞因为没人告诉它“刚才那条路有点接近了”。从数学上看稀疏奖励场景下的策略梯度估计方差会非常大。对一个轨迹如果最终没有到达目标整条轨迹的 reward-to-go 全是 0那么这条轨迹提供的梯度近似等于纯噪声而到达目标的轨迹又极其稀少偶尔出现一条又会因为样本量太少导致更新方向抖动剧烈。结果就是训练曲线长时间贴地横躺loss 不降、成功率不动实验卡死。这种场景下很多人第一反应是“换更强的算法”。但真的换掉 DDPG、换掉 SAC、换掉 PPO问题依旧——因为核心瓶颈不在算法更新公式而在数据本身绝大部分经验都是“没有奖励、没有方向”的废数据。1.2 常用缓解手段和它们的局限既然稀疏奖励学不动自然会想到“那就把奖励变稠密”。奖励塑形Reward Shaping就是这么干的设计一个连续函数比如“离目标越近、奖励越高”让 agent 每步都能收到反馈。这个方法在简单任务上效果立竿见影但坑也很深需要你手工设计距离函数或势函数这本身就是领域知识设计不好会出现 reward hackingagent 学会在原地做微小的抖动来刷距离奖励而不是真正执行任务换一个物体形状、换一张地图整套 shaping 函数可能就得重写。另一条路是加内在好奇心奖励ICM、RND 这类让 agent 探索新状态时获得额外奖励。这个思路对解决“探索不足”很有帮助但它本质上解决的是“去没去过的地方看看”不是“去目标位置看看”所以经常看到 agent 在角落里玩得不亦乐乎任务成功率却没什么起色。课程学习也是个方向从简单目标开始逐渐变难。但它同样需要人工编排任务梯度而且对目标空间连续、目标众多的场景课程设计的工作量非常大。真正让我觉得“这条路走对了”的是在机器人任务里遇到的 hindsight 思想——事后给轨迹重新贴一个“本来可以达成”的目标标签。它不改奖励公式、不动网络结构、不重新设计课程只做一件事把那些“失败”的轨迹重新包装成“对另一个目标成功”的轨迹再塞回经验池里让算法去学。2. Hindsight 的核心机制失败目标的“事后补救”2.1 一句话说清原理hindsight 的英文原意是“后见之明”说的是人回头看时总觉得自己当初应该能做得更好。把这种思路放进强化学习里就变成了agent 没有做到指定的目标但它在探索过程中确实达成了某个别的状态——那就把那个状态当作“事后目标”让这段轨迹变成一次成功示范。我用一个特别土的比喻给你讲投篮训练。你本来要投进左边那个篮筐结果球飞到右边去了。传统强化学习会记下这次“失败”然后什么都不改hindsight 的做法是在右边那个落点临时竖一个篮筐把刚才这次投篮重新标记成“投进右边篮筐”——虽然你原本的目标不是右边但你至少学到了一条“球从手里飞出去、经过某个轨迹、最终落到某个位置”的因果关系。关键点在于这条因果链是真实发生的物理过程是 agent 在当前策略下“能做到”的结果。把它当作成功样例来学比任何虚拟的、凭空想象的轨迹都更有指导意义。学到足够多“投到各种位置”的经验后agent 对“把物体推到某个目标点”这个抽象技能就掌握了这时候再让它投左边篮筐它至少知道怎么发力、怎么调整。这个思想最早以 Hindsight Experience ReplayHER的名字出现在 2017 年的论文里作者是 OpenAI 的 Marcin Andrychowicz 等人。它当时在 Fetch 系列机器人操作任务上表现惊人——原本稀疏奖励下根本学不动的任务用 HER 之后能从几乎 0% 成功率拉到 80% 以上。2.2 目标重标注的流程与数学表达HER 的整个流程可以拆成三步。理解这三步你就理解了这个方法的所有精髓。第一步正常采样。Agent 按照原始目标 (g) 与环境交互跑完一个 episode得到一条轨迹包含状态、动作、奖励、下一状态每一步的 reward 都按“是否到达 (g)”来判断。因为目标 (g) 一般很难达成这条轨迹绝大多数步的 reward 都是 0。第二步事后选目标。在轨迹结束之后从这段轨迹里挑一个“agent 实际到达过的状态”作为新目标 (g)。怎么挑最简单的策略是取轨迹最后一步的状态也就是最终状态更常用的是 future 策略——把轨迹分成时间步在每一个时间步 (t)从后面的 ({t1, t2, ..., T}) 中随机挑一个状态作为该步的新目标。注意这里有个容易忽略的细节不同时间步对应的 (g) 不一定是同一个。在第 10 步重标注时你可能挑第 20 步的状态当目标在第 30 步重标注时你可能挑第 40 步的状态当目标。它是一条“动态目标”的轨迹。第三步重写转移元组。对轨迹里的每个转移 ((s_t, a_t, r_t, s_{t1}, g))把目标 (g) 换成 (g)同时根据 (s_{t1}) 是否等于 (g)或者是否在容差范围内重新计算奖励 (r)得到新的转移 ((s_t, a_t, r, s_{t1}, g))。然后把这条新的经验塞进经验回放池。数学上HER 本质上是在扩大训练分布原始经验只包含目标为 (g) 的样本而重标注后经验池里同时包含目标为各种 (g) 的样本。对于每一个重标注样本它的“目标”不再是那个遥不可及的原始目标而是“agent 实际上接下来确实会抵达的状态”也就是说这是一个可被当前策略实现的目标。这种重标注带来的奖励结构变化非常直观。原始轨迹里可能只有最后一步 reward 1其余全是 0重标注之后每一条轨迹里都有一大段步数的 reward 变成 1——因为目标就是按“未来某个时刻的状态”来定的所以只要走到了那个状态中途很多步都会判定为“到达目标”。2.3 为什么这招有效很多人第一次接触 HER 时会觉得“这不就是改标签吗刷出来的奖励都是假的能有用”我一开始也有这个疑惑直到仔细想透了它的收益来源。第一解决了正样本稀缺的问题。稀疏奖励下经验池里 reward 1 的样本占比可能只有千分之一甚至更少。算法很难从这么稀疏的正样本里学到稳定的策略方向。HER 重标注后经验池里正样本比例大幅提升策略网络每次采样到 batch都能看到足够多的“成功案例”训练信号不再是噪声。第二学到了物理约束下的可行策略。原始的稀疏奖励任务“目标”往往是想象出来的、很难达成的点。而 HER 事后选的目标是 agent 实际到达过的状态这保证了该状态的物理可达性。算法学到的是“在当前动力学约束下如何从某状态运动到另一状态”的实际可行策略而不是在样本稀少的区域瞎猜。第三对探索效率是乘法级别的提升。传统方法里一次随机探索只对“原始目标”这一个目标提供学习信号HER 里同一段探索轨迹可以对多个事后目标同时提供学习信号。相当于每一次失败都没有浪费——它至少教会了 agent 怎样达成其中一个“途中状态”。3. 实操在代码里落地 HER3.1 从哪开始环境与算法选型纸上谈兵讲完了说点能直接用到实验里的东西。想复现 HER、跑通它最经典的环境是 OpenAI Gym Robotics 系列里的 Fetch 任务FetchReach机械臂够到一个点、FetchPush把物体推到一个位置、FetchSlide把物体滑到一个位置、FetchPickAndPlace抓取物体放到目标点。这套环境的设计本身就跟 HER 高度绑定——它的观测被拆成了三个部分observation、achieved_goal、desired_goal。observation 是机械臂的关节状态achieved_goal 是物体当前实际位置desired_goal 才是我们想让物体去的位置。这种“当前实际目标状态”和“期望目标状态”分离的接口设计就是为了方便做 HER 目标重标注。算法层面HER 通常搭配 off-policy 的连续控制算法比如 DDPG、TD3、SAC。原因有二一是 off-policy 方法天然依赖经验回放池而 HER 本身就是往回放池里塞数据的方法两者配合是零成本集成二是 HER 生成的重标注经验是一种“离线”数据它不代表当前策略的真实行为分布而 off-policy 算法对这种分布偏移的容忍度更高。实现方面如果不想从零手写可以直接用 stable-baselines3SB3里的HerReplayBuffer它基本把目标重标注逻辑封装好了你只需要设定model_class、env、replay_buffer_class这几个参数。不过我还是建议至少手写一遍重标注逻辑——因为调参和 debug 时你必须清楚每一步在做什么。3.2 关键实现一个典型的重标注片段下面是 HER 里最核心的一段逻辑用伪 Python 风格写出来。假设我们有一个 episode 的经验每一步的 obs 里都带着achieved_goal字段。def hindsight_replay(episode, future_k4, replay_ratio0.8): episode: 一个完整回合的转移列表每个元素是 (obs, action, reward, next_obs, done, achieved_goal) future_k: 对未来窗口采样的次数 replay_ratio: 保留原始目标经验的比例 her_transitions [] # 先把原始目标经验全部保留 for trans in episode: her_transitions.append(trans) # 对每个时间步生成 future_k 条重标注经验 T len(episode) for t in range(T): obs_t, action_t, _, next_obs_t, _, achieved_goal_t episode[t] # 在 t 之后的步里随机采样 future_k 个状态作为新目标 # 注意不能选 t 本身否则目标与当前状态重合样本毫无意义 future_indices np.random.choice( range(t 1, T), sizemin(future_k, T - t - 1), replaceFalse ) for idx in future_indices: future_achieved_goal episode[idx].achieved_goal # 新的目标 g # 用“是否到达 g”来重新计算奖励 new_reward compute_reward(achieved_goal_t, future_achieved_goal, reward_typesparse) new_done check_success(achieved_goal_t, future_achieved_goal) her_transitions.append( (obs_t, action_t, new_reward, next_obs_t, new_done, future_achieved_goal) ) return her_transitions这段代码里最容易写错的地方是future_indices的采样范围。如果你把range(t, T)写成了range(t, T)会随机选到当前时刻自己当目标这会导致“目标 当前状态”奖励直接被判为成功然后策略学会什么都不做——因为什么都不做也能得到正奖励。所以采样区间一定要从t 1开始选的是“未来某个时刻实际到达的状态”。另外经验池里不能 100% 是重标注数据。实际做法是保留一部分原始目标经验上面代码里replay_ratio参数就是控制这个比例的。原因很简单算法最终要优化的是“达成原始目标”如果池子里全是重标注后的目标agent 会变成一个只会完成“随便什么目标”的通用策略而不是专门解决你指定的任务。常见的比例是保留 80% 原始经验、额外加入 20% 的重标注经验或者原始与重标注各占一半——具体值需要根据任务调。3.3 超参数怎么调才不踩坑HER 的超参数不算多但每个都直接影响最终效果我按重要性排序讲。第一个是 (K) 值也就是每个时间步额外生成多少条重标注经验。(K) 太小时重标注带来的样本增量不够正样本比例依然偏低训练速度提升不明显(K) 太大时经验池里重标注经验泛滥算法过度关注“完成任意目标”反而忘记了原始目标。我的经验值是从 4 开始如果训练曲线上升太慢再往上调到 8如果发现成功率后期波动剧烈就降回 4。第二个是关键的时间窗口也就是“未来多少步内采样目标”。实际实现中一般用future_strategy配合一个窗口长度比如从 (t1) 到 (t k) 的范围内采样而不是从整条轨迹剩余部分采样。窗口太短目标与当前状态太接近任务难度过低窗口太长又回到稀疏问题。我之前调 FetchPush 时把窗口从全轨迹改成 50 步之后成功率稳定提升了不少。第三个是经验回放池容量。HER 因为额外生成了 (K) 倍的经验回放池会涨得很快。容量太小会导致旧经验被快速覆盖而重标注经验的价值恰恰在于多样性——不同目标、不同轨迹片段。所以宁可把 buffer 设大一点我一般用 500k 到 1M 条转移单条转移的存储结构要精心设计否则内存很容易爆。第四个是奖励函数的选择。HER 在理论推导里用的通常是稀疏奖励到达目标给 1否则给 0。这样重标注后经验池里会充满 0/1 标签训练目标非常清晰。如果你非要在上面叠加距离奖励做 shaping反而会引入“目标漂移”的问题——shaping 信号和 sparse 信号打架策略学到的东西会变得很奇怪。用 HER 的时候我强烈建议先用纯稀疏奖励跑等架构稳定之后再考虑是否加 shaping。4. 踩坑记录与效果复盘4.1 我跑实验时遇到的三个坑坑一achieved_goal 和 desired_goal 维度没对齐。我最初拿一个自定义机械臂环境做实验时achieved_goal是三维坐标desired_goal也是三维本没问题但有一次为了记录末端速度我把速度拼进了achieved_goal向量导致它变成六维而desired_goal还是三维reward 函数里直接对两个不同维度的向量做范数计算结果每一步的“距离”都是错的——但代码不报错bug 非常隐蔽。后来我养成了一个习惯在训练前单独跑一段 check 函数打印achieved_goal.shape、desired_goal.shape以及一个随机转移的 reward确认维度一致、reward 数值符合预期再启动训练。坑二K 值拉满模型开始“自欺欺人”。有一次为了追求更快的正样本密度我把 (K) 调到 16结果训练到一半发现原始目标成功率不升反降。后来查经验池分布发现重标注经验占了 90% 以上策略被大量“随便到达某个状态也算成功”的样本带偏变成了一个高探索、低精度的“乱动策略”。最后我把 (K) 降到 4同时把重标注经验比例限制在总经验池的 50% 以内问题就解决了。这也印证了一个原则HER 不是重标注越多越好它是在“提供正样本”和“保持目标一致性”之间找平衡。坑三跟 reward shaping 一起用触发了 reward hacking。我最初觉得 HER 已经很厉害了再叠加一个“距离越近奖励越高”的 shaping 应该锦上添花结果训练出来的策略学会了让机械臂末端在目标附近高频抖动——它发现这样做既能拿到 shaping 的连续奖励又能在某几个瞬间触碰到目标拿到稀疏奖励却完全没有学会稳定的“推、放、回”动作序列。这个实验让我彻底明白HER 已经改变了奖励分布再叠加 shaping 会引入两个不同尺度的奖励信号它们的量纲、稀疏程度完全不同很容易诱导策略去“刷”更容易获得的那个信号。后续我所有 HER 实验都坚持纯稀疏奖励。4.2 常见问题速查表现象可能原因排查与解法训练曲线完全没动静目标空间维度错误reward 恒为 0检查 achieved_goal/desired_goal 维度和取值手动给一条已知成功轨迹算 reward成功率后期波动很大K 值过高重标注经验过多降低 K限制重标注经验在 buffer 中的占比策略学会原地不动future 窗口包含当前时刻状态确认采样区间从 t1 开始随机抽几条重标注检查目标是否与当前状态相同探索阶段过长前期无正样本窗口选择太远目标依然难达成缩短 future 窗口增大 buffer 容量确认环境是否支持从任意状态重置加入 HER 反而比 baseline 差原始任务奖励本身已经很稠密检查任务是否真的稀疏HER 在稠密奖励场景下收益有限甚至有害4.3 一份务实的心得我个人的体会是HER 最妙的地方在于它不动算法、不动奖励、不动网络结构只改数据的标签——这让它可以像插件一样装进很多现成的 off-policy 算法里。DDPG 学不会的换上 TD3 HER 能学会SAC 收敛太慢的加上 HER 往往能提前一倍时间突破。但它不是万金油。它最适合的任务画像是目标可描述、可判定是否到达、目标空间与状态空间有重叠或者可以由状态推导出来、奖励天然稀疏。如果你的任务里目标本身是隐藏的、不可观测的或者目标空间和状态空间完全不对应那 HER 的重标注逻辑就没法直接套用。我自己在真实机器人抓取项目中用到它的经验是先在一个低精度仿真环境里把 HER 调通再把策略迁移到真实机器人的“状态估计 轨迹跟踪”两层结构上效果比直接端到端训练稳定得多。另外hindsight 这个思想后来衍生出了很多变体——把语言指令当作事后目标、把多模态观测当作事后目标、结合自动课程学习动态生成目标核心思路都一脉相承。如果你吃透了 HER 的“重新定义目标”这个哲学再去看这些扩展会容易很多。最后分享一个小技巧每次跑 HER 实验我都会把训练过程中的 achieved_goal 分布用 TensorBoard 打出来看。当分布能均匀铺满整个目标空间时基本意味着探索是充分的后面成功率往上走是迟早的事。这个判定比盯着 reward 曲线一条条猜靠谱多了。
返回列表