核心原理与工程实践)
1. 稀疏奖励为什么是强化学习的头号杀手先说一个我最早做机械臂抓取仿真时反复遇到的场景环境里有一个目标点机械臂需要把物体推到指定位置。任务定义很明确代码逻辑也没问题但训练从第一步就陷入死局——智能体收到的奖励永远是 0直到它偶然把物体推到目标附近。可问题在于没有梯度的空间里随机动作偶尔碰到目标的概率几乎等于零。这个现象在强化学习里的学名叫稀疏奖励问题sparse reward problem。奖励信号在绝大多数时间步里都是 0环境不给任何关于“你走得对不对”的反馈只有最终的那一下成功才给出 1。算法层面看策略梯度依赖奖励计算优势函数Q 学习依赖 TD 误差更新价值网络奖励全是 0 的时候价值函数梯度也为 0模型参数原地踏步。换句话说误差信号从哪里来没有误差信号优化就是一个空转的过程。你可能觉得那加大探索噪声不就行了让动作更随机一些总有一次能撞到目标。理论上可以实际上非常不可靠。机械臂的动作空间假设是 4 维连续空间每维都是实数目标位置对末端执行器的要求是极其精确的——不是“大概方向对”而是位置、速度几乎同时正确才能成功。在这样高维连续空间里用随机策略碰出一次成功概率按指数缩小训练一万个 episode 都等不到一次正样本。而强化学习恰恰是需要盘点“历史教训”的学习方式连续一万次失败最后全是被丢弃的无效数据学习过程自然无从谈起。工程师们想了很多办法绕开这个问题。最常见的是 reward shaping人为构造一个中间奖励比如“物体离目标越近奖励越大”。听上去简单但这相当于人类把解法塞进环境设计不好很容易让智能体钻空子——我曾经见过一个智能体为了拿到“移动惩罚尽量小”的奖励干脆原地不动装死。另一种是课程学习从简单的初始位置开始训练然后逐步加大难度但课程难度的自动控制本身又是一个难题。还有 exploration bonus鼓励智能体去没去过的地方但在很多精细操作任务上单纯的新奇性驱动并不能带来有效的操作技能。这些方法能缓解问题但都没有触及一个根本矛盾**智能体完成任务靠的是一次“侥幸成功”但真正的学习信号其实藏在每一次失败的过程里。**假如把失败的数据也用起来是不是就能凭空多出成千上万条可供学习的经验这正是 hindsight——事后经验——这个名字背后最核心的思路。我第一次听到这个概念是在 2017 年 OpenAI 发表那篇《Hindsight Experience Replay》论文时看完最大感受是思路顺理成章但为什么之前没人想到。2. 事后经验回放的核心思想把“失败”改写为“成功”要理解 HER得先把强化学习里的“目标”这个概念单独拿出来批评一通。常规的 goal-conditioned RL 设定下一条 transition 由四元组构成当前状态 s、动作 a、奖励 r、下一状态 s‘。在稀疏奖励任务里奖励由“是否达到目标 g”决定要么 0 要么 1。比如抓取任务goal 是物体最终位置agent 移动物体到指定点得 1否则得 0。问题在于**一旦 episode 结束而目标没有达成这条轨迹的信息就被浪费了。**可事实上这条轨迹并不是毫无意义的——物体确实被移动了机械臂确实执行了一串动作这串动作如果真的以物体最终到达的那个位置作为目标那它就是一个完美的成功示范。HER 的思路就一句话**重新给这段经历定义一个目标把轨迹原本要达成的 goal g 替换成轨迹实际到达的终点 g‘再按照新的目标重新计算奖励。**比如机械臂没能把物体推到 (0.5, 0.5)但物体最后落在 (0.3, 0.7)那我就假装这次任务的目标本来就是 (0.3, 0.7)回放时按照“目标是否等于 (0.3, 0.7)”来判定奖励。物体最终的位置恰好就是 (0.3, 0.7)所以这次回放得到的是奖励 1是一条成功经验。你说这是造假也行但这种造假是提炼真相。关键在于虽然这条轨迹没能完成原目标但它是从某个初始状态出发、通过一串真实动作到达新状态 G’ 的物理上可复现的过程。用强化学习的语言说这提供了一条“状态-动作对能产生某个结果”的因果证据。当智能体以后面对相似状态、需要达到相似目标时它可以参考这段经历知道哪条路“走得通”。这个道理很多人第一遍听觉得像抖机灵但实际操作之后会发现它极其强大。我拿一个简单的模拟环境验证过环境是一条一维线段智能体从 0 出发目标是移动到 10但只能向左或向右走一步随机策略下到达 10 的概率极低。普通 DQN 训练两千个 episodeQ 值几乎不涨。HER 改写目标后每个 episode 里至少有一步的“事后目标”在当前位置附近Q 网络的监督信号密度瞬间从千分之一提升到接近百分之百。两千个 episode 跑完成功率曲线直接起飞。当然HER 不是随便用在任何 RL 算法上都有效它有一个硬性前提**必须配合 off-policy 算法使用。**DDPG、DQN、SAC 这类有经验回放缓冲区的算法可以存下历史 transition 并反复采样而 on-policy 算法如 PPO、A2C每轮都丢弃旧数据HER 制造的“伪造成功经验”还没来得及被学习就见光死了。所以你在论文里看到 HER 通常是 HERDDPG、HERSAC 的组合几乎不会看到 HERPPO因为范式上就不成立。3. 实现层面的四件事目标定义、存储结构、采样策略与重标记时机把 HER 落地到代码会有好多细节比论文里说的复杂。我把实现拆成四件事逐一过一遍这对想做实验的人有价值。3.1 目标函数与观测设计把 goal 写进 stateHER 的第一个前提是策略必须是 goal-conditioned也就是策略的输入不能只有观测 s得有 (s, g) 或者把目标拼进观测里。目标的形式可以是目标状态goal statefull比如“物体最终要到达的位置坐标”也可以是描述任务的向量。实操上我建议先确认你环境的观测空间里是否天然包含目标信息如果没有就把 target 向量和当前观测拼接起来得到一个维度加大的新观测。这里有个容易忽略的点**不仅当前状态需要包含目标重标记之后的目标也必须能拼进状态用于价值网络输入。**所以在设计包装函数时最好把 transition 里存的数据结构统一封装成 (obs, act, rew, next_obs, goal) 五元组而不是只存原始 obs。重标记时只需要替换 goal 字段、重算 reward再相应地更新拼接后的 obs 和 next_obs否则你的网络输入和目标对不上。奖励函数我记得写成二值形式最省心goal 达成给 1否则给 0或者距离小于某个阈值给 0否则给 -1这就是稀疏奖励的标准形态。用连续距离做奖励会让整个问题退化成普通的稠密奖励问题HER 的优势反而不明显了。3.2 四类目标重标记策略future / final / episode / random论文里最重要的一个技术贡献是研究了“用什么目标来重标记效果最好”。我把四种策略列在表里可以根据任务性质去选策略操作效果final用轨迹终点作为所有 transition 的新目标最简单强烈依赖轨迹终点多样性random从 buffer 中随机挑目标目标多样性高但偏“无关样本”较多future对当前 transition 之后的某个未来状态作为目标兼顾可达性与时序关系常为默认选择episode从整条轨迹中均匀采样目标中等效果多样性稳定我的习惯是优先尝试 future设置 k4也就是每个 transition 重标记 4 个不同的 future 目标然后把它们一并存入 buffer。这样做既保证了目标分布在状态空间内的覆盖面又不会像 random 那样引入太多完全超出当前轨迹可达范围的目标。final 策略在任务简单、终点状态模式单一的时候很高效但一旦轨迹终点分布复杂它会让目标分布过于集中网络很快过拟合到那几个频繁出现的终点上。3.3 重标记时机与 replay buffer 的设计重标记的目标是“轨迹真实达到的状态”所以在 episode 结束之前我们并不知道未来会发生什么。这意味着重标记只能在一个 episode 跑完之后进行而不是每步在线进行。我踩过的坑是把重标记放到 replay buffer 层面边存边改结果因为“future 状态还没发生”把未来信息泄漏到目标任务里评估成功率虚高。正确的做法是按 episode 为单位先缓存原始 transition 序列episode 结束后整条轨迹做一次重标记再逐条塞进全局 buffer。buffer 的存储结构也值得留意。HER 论文里默认用的是非均匀采样重标记样本相对普通样本可以多存一些但不宜疯狂膨胀比例。一个通用的做法是把原始轨迹和重标记轨迹按 1:4 的比例混合存入。我试过 1:10效果没明显提升反而因为伪造样本过多、真实样本被稀释在训练后期成功率反而出现平台期。别把自己骗了回放数据里也得保留足够多的“真实失败案例”。3.4 HER-DDPG 核心伪代码凑理论不如看代码。下面这段是 HER 与 DDPG 结合的框架性伪代码结构上可以直接迁移到 SAC 或 TD3# 伪代码HER DDPG 核心循环 def train_her(env, agent, num_episodes, k4): replay_buffer [] for episode in range(num_episodes): episode_transitions [] obs env.reset() goal env.get_goal() done False while not done: action agent.select_action(obs, goal, noiseTrue) next_obs, reward, done env.step(action) # 原始 transition 先按原目标存起来 episode_transitions.append({ obs: obs, action: action, reward: reward, next_obs: next_obs, goal: goal, done: done }) obs next_obs # episode 结束后的重标记阶段 final_state episode_transitions[-1][next_obs] for transition in episode_transitions: # 原始样本 replay_buffer.append(transition) # future 策略选当前之后的若干状态作为新目标 future_states sample_future_states(episode_transitions, transition, k) for new_goal in future_states: new_reward compute_reward(transition[next_obs], new_goal) replay_buffer.append({ obs: concat(transition[obs], new_goal), action: transition[action], reward: new_reward, next_obs: concat(transition[next_obs], new_goal), goal: new_goal, done: transition[done] }) # 从 buffer 采样更新 agent for _ in range(num_updates): batch sample(replay_buffer, batch_size) agent.update(batch)注意两个细节一是sample_future_states的实现要保证只采样当前 transition 之后的时间步避免用“过去的状态”当作“未来已达成”的目标这种时间顺序错误会造成虚假学习信号二是concat是可选项如果你的 agent 本来就接收 goal 作为额外条件输入那就可以省掉拼接直接用 conditioned policy。3.5 评测指标别盯着 reward 曲线用 HER 训练之后我发现衡量进度的指标和普通 RL 任务不太一样。稀疏奖励任务的 loss 曲线或者平均 reward 曲线不具备参考意义因为 episode 开始阶段绝大多数回合奖励是 0后期一个回合成功拿到 1平均值变化非常突兀且不稳定。我更习惯统计成功率每个评估周期跑 50-100 个 episode计算目标达成比例。成功率曲线才能真正反映学习进展而且便于和论文里的对照实验比较。如果你只盯着 TensorBoard 里的 mean reward很可能会误以为 HER 训练没效果其实它只是把“成功经验”分散到更多 transition 里reward 均值并没有想象中光滑。4. 为什么 HER 能带来数量级的学习加速我的实验观察理论上 HER 的道理说得通但它加速学习的效果到底有多显著最好用实验说话。我复现了一个经典的 Bit-flip 环境实验状态和目标是 n 维二进制向量每一步可以翻转一位只有当状态变成和 goal 完全一致时奖励为 1否则为 0。这是一个教科书级的稀疏奖励任务维度越高越难。在 n15 时普通 DQN 基本上是在碰运气。因为状态空间有 2 的 15 次方那么多个点随机翻转能恰好碰上目标点的概率微乎其微训练一万个 episode 成功率纹丝不动。而 HER 的做法是假设轨迹最后停在了 s‘那就把这段轨迹的目标改写成 s’ 本身回放搜到的每条经验都变成“从某状态翻转到另一状态”的正样本。Q 网络因此能学到“相邻状态之间满足一步可达”这样的局部规律不需要跳到全局目标也能积累价值信号。只要局部价值传播对了全局策略就能像涟漪一样扩散最终从起点找到通往目标的路径。速度对比非常明显HER 让这个任务变成了数百个 episode 内就能看到成功率爬升的问题。我对比过 HER 与普通的经验回放ER在同一环境、同一网络结构、同一超参数下成功率到达 80% 的 episode 数量差了至少一个数量级。这说明重标记本质上是给算法加了一层“免费课程”普通 ER 只能学习真实发生的稀疏成功事件而 HER 把每个时间步都转化为“目标-结局配对”的稠密信号相当于课程自动从近处开始随着轨迹终点分布的变化逐步逼近远处目标。对于连续控制任务我观察到的是另一种形态的加速。比如二指机械臂推动滑块到目标位置的任务不使用 HER 的 DDPG 在 50 万步时成功率仍然接近 0而 HER 版本在 20 万步时成功率就能稳定到 80% 以上。区别在于普通 DDPG 的价值网络从没见过成功样本而 HER 版本的价值网络虽然也从未见过真正成功但它见过“从任意位置出发把物体推到当前位置”的这种局部成功。局部成功积累多了网络对哪些动作能改变物体状态形成了清晰认知后续再面对真实目标时它只需要在这个认知基础上做一步规划而不是从头搜索整个状态空间。这一层因果结构的学习正是“事后经验”最妙的地方。不过也要警惕另一面HER 制造的成功样本毕竟是假的如果网络只会盲目模仿“到达当前位置”这类目标真实任务需要长距离移动时它可能停留在局部最优。所以后面很多工作都在 HER 的基础上加入额外的目标筛选机制而不是无脑重标记。我个人在实践中发现在最终评估阶段适当减少重标记比例、增加真实成功样本比例往往能改善最终策略的泛化性。5. 实战踩坑记录HER 不是银弹HER 很强大但绝不是一个开了就能用的开关。这几年我在不同环境里尝试过 HER踩过不少坑总结成几条最值得看的经验。5.1 太稀疏也不行仍然需要“偶然成功的一次”HER 的原理决定了它的学习信号来源是轨迹终点本身。如果任务的环境随机性过大或者动作噪声把智能体完全推向无关状态那么轨迹终点可能根本没有任何规律可言重标记出来的目标集合也毫无结构。举个例子如果机械臂每步动作都叠加了方差极大的噪声轨迹终点更像是一堆随机点那“把目标改成终点”并不能给策略提供可靠的因果信息——因为同样的起点下一条轨迹终点可能差得很远。所以 HER 实际适用的场景是在给定初始状态下动作与结局之间存在一定可复现性哪怕很微弱。如果动作噪声大到一个 episode 的结局完全随机那 HER 也不会有奇效。这种情况下我的做法是先调低探索噪声、让随机策略至少能形成一点短程可预测性再叠加 HER成功率才会起来。5.2 目标分布与初始状态分布错配HER 的重标记目标来自轨迹终点而轨迹终点分布受初始状态分布影响。如果你的训练环境初始状态分布单一比如机械臂总是从同一个位置出发那么轨迹终点大多聚在一个小区域重标记目标也困在这个区域策略学到的是“如何把物体推到那一片区域”一旦测试时初始状态或目标位置远离这片区域策略就失灵。解决思路是增加训练时初始状态的随机性目标位置的采样也尽量覆盖整个可行空间。我在抓取任务里做得比较多的是把初始物体位置和 goal 位置都从整个台面上均匀采样这样轨迹终点才会散开重标记的目标也才能覆盖桌面各处最终策略的泛化性才会接近完整任务空间。5.3 replay buffer 的目标分布偏移重标记样本占 buffer 的比例很关键。前面说过我喜欢 1:4但实际任务里这个比例要跟着训练阶段调。训练早期真实成功样本几乎没有可以适当提高重标记比例让网络尽快学到基础运动学训练中后期真实成功样本增多如果重标记样本还占大头网络会一直被“从当前位置到当前位置”的低难度目标占据学习不到更高级的策略。我在后期通常把重标记比例调低到 1:1甚至 1:2同时提高真实成功样本的采样权重。这背后是目标分布和目标难度平衡的问题。重标记样本容易但偏简单真实样本难但稀少两者需要动态平衡而不是一个固定比例走到底。5.4 网络容量与表征能力瓶颈HER 加速的是学习信号密度而不是表征能力。当任务的目标空间非常复杂例如目标是图像、是自然语言描述拼接 goal 作为网络输入的容量要求就会骤然上升。我试过用 HER 训练一个以目标图片为条件的策略起初以为算法效果会和向量目标一样好结果价值网络完全无法从高维图像中提取可控特征训练陷入停滞。这个问题的解决依赖高质量的 encoder 或者预训练的视觉表征不是 HER 本身能解决的事。如果你的目标是图像或多模态信息我的建议是先训练一个 decoder 把目标压缩成低维 embedding再和状态拼接喂给策略网络而不是直接把原始高维目标塞给价值网络。HER 只管重标记不管表征把它强行当成万能钥匙只会失望。5.5 重标记目标与动作时间步错位最后提醒一个比较隐蔽的坑重标记采样 future 状态时务必注意时间步关系。future 策略要求在 transition 的时间点之后采样新目标但如果实现过程中搞错了索引可能把“过去状态”采了进来这会让目标超过实际可达范围造成价值函数乱讲。这个 bug 不会直接报错但它会让成功率曲线产生奇怪的周期性波动。遇到这种情况先检查sample_future_states的索引切片是不是严格小于当前 transition 的索引。6. 向外扩展HER 和周边方法的组合思路HER 不是一个孤立的算法把它当作一个通用“经验增强”模块之后它的用处比论文里的 benchmark 大得多。6.1 与课程学习的配合课程学习的难点在于难度指标的设计而 HER 的重标记目标天然构成了一条难度从低到高的自动课程。早期重标记目标集中且简单策略只需要学会小幅动作控制随着训练推进轨迹终点逐渐外扩目标分布自然逼近真实目标分布。不过如果想要让课程更高效也可以在 HER 基础上主动加一层难度过滤只保留那些距离原目标中等距离的重标记样本比如距离在目标半径的 2-5 倍区间避掉过于简单和过于困难的样本。这个技巧对复杂任务的价值在于让价值函数聚焦在“跳一跳够得着”的目标上学习效率再上一层楼。6.2 多目标与稀疏奖励下的分层RL在很多真正连续控制任务里我们面对的不是单一目标而是一系列子目标。比如做一杯咖啡涉及抓杯子、倒水、拿勺子等多个子任务。直接把整条长轨迹打成一个 HER 目标往往过于粗糙。我见过一种做法是把任务拆成子目标链每个子目标单独训练一个 goal-conditioned 策略再用上层策略选择当前子目标。HER 在这里的作用是让每个子目标模块在稀疏奖励下也能快速收敛整体流水线因此可行得多。这个组合思路本质上利用了 HER 对局部因果结构的快速提取能力。6.3 与对比学习、表示学习的结合在表征学习的框架下HER 的重标记过程天然产出大量“同状态不同目标”的对比样本同一段经历不同目标下成功或失败这组对比对学习判别性表征很有帮助。我在实验里试过把 HER 重标记的样本额外喂给一个 contrastive head用来学习“当前状态离目标差多少”的距离表征之后再用这个表征作为 reward 函数效果比手工造距离函数好不少。这也算是 HER 在生成式经验之外的另一层价值免费的辅助监督信号。回头来看HER 之所以成为一个常青的 baseline核心原因是它抓住了强化学习里一个最朴素却最容易被忽略的事实**失败轨迹和成功轨迹的物理运动规律是一样的差别只在目标定义上。**把目标写成“已经发生的事”失败也能变成教材。这个思想后来也被吸收进大量后续算法里比如 goal-conditioned imitation learning、自动课程选择乃至一些离线强化学习的工作。如果你在做稀疏奖励任务开头第一件事值得先试试——给 replay buffer 里加一点“事后聪明”。最后分享一个我做实验时的小习惯重标记目标不是越多越好早期一个 transition 配 4 个 future 目标确实香但训练后期我会把 k 降到 2再把真实经验的比例拉上来。这个动作让我在好几个任务里避开了“训练后期平台期”的尴尬。你可以先按自己的任务试如果发现后期成功率不上不下往往是伪造样本比例压过了真实结构适当降 k、升真实占比通常能救回来。