ARTICLE DETAIL

资讯详情

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

HER:稀疏奖励强化学习中的“后见之明”与目标重标记实战

HER:稀疏奖励强化学习中的“后见之明”与目标重标记实战 hindsight这个词字面是后见之明但在强化学习领域它代表了一个里程碑式的方法——Hindsight Experience ReplayHER。如果你做过机器人控制、操作任务或者任何带稀疏奖励的强化学习项目你一定被奖励全是0智能体原地不动这个问题折磨过。HER这篇NeurIPS 2017的工作就是专门来解决这个问题的。这篇文章不打算跟你复述论文翻译我想用做项目的视角把HER从原理到代码到调参再到对后见之明思维的工程应用完整拆一遍。适合正在纠结怎么让智能体在稀疏奖励下学起来的RL工程师也适合那些听说过HER但一直停留在大概知道层面的朋友。看完之后你应该能直接在自己的任务里把HER用起来知道每一步为什么要这么做踩坑了从哪里排查。1. 稀疏奖励到底难在哪HER凭什么能解1.1 奖励全是0的时候梯度就像在沙漠里找水先还原一个场景。你定义了一个机械臂推球任务把桌上的球推到某个固定坐标点。机械臂的关节有7个自由度动作空间是连续的力矩状态空间包含关节角度、角速度、末端位置、球的位置、目标位置加起来几十维。问题是球不会那么听话地待在原地。绝大多数情况下智能体乱晃一通球根本没碰到目标于是每一步的reward都是0。你在tensorboard里看到的就是一条笔直的loss曲线动都不动。为什么会这样因为强化学习的信号全来自rewardreward全是0意味着无论智能体做了什么评价都是无差别的坏策略梯度里所有的优势估计都是0参数更新就像在沙漠里找水——四面八方看起来都一样没有任何方向能给你指路。这种情况就是稀疏奖励sparse reward。很多真实任务天然就是这么稀疏的下棋只有赢或输、导航只有到或不到、抓取只有成功或失败。用密度奖励、shaping reward确实能在一定程度上缓解但每个人工设计的reward都带着你的偏见很容易让智能体找到取巧路径。1.2 HER的核心逻辑把失败的经验重新标成成功的经验HER的思路用一个例子讲最直观。假设机械臂本来想推球到坐标(0.3, 0.4, 0.1)一顿操作之后球停在(0.1, 0.2, 0.07)。按原始目标这局是失败的整条轨迹的reward全是0没用。但HER发现了一件事如果目标不是(0.3, 0.4, 0.1)而是(0.1, 0.2, 0.07)那么这局恰好是成功的——智能体确实把球推到了目标点只是这个目标点不是最初设定的那个。所以HER的做法是把这局失败的轨迹存进replay buffer同时对这条轨迹做目标重标记goal relabeling——把每一步的目标替换成一个真实达到过的状态。替换之后原本reward全0的轨迹就变成了问你能否把球推到轨迹中到达过的位置至少最后一步的reward是1。这样即使智能体没有完成原始目标它也从这局失败中提取到了信息我推球到某些位置是有可能的而这些位置转换是有价值的。这就是后见之明事后看如果你早知道目标是什么你就能看出这局其实完成了什么。把这个事后目标教给智能体它就学会了如何把球推到某处这个底层能力而不是死死盯着最初那个没达到的目标。这个思路极其优雅的地方在于它不需要改环境、不需要改reward函数、不需要人工设计shaping reward只需要改从buffer里怎么采样训练数据。所以它可以即插即用地接在你已有的off-policy算法DDPG、DQN、SAC、TD3外面成本极低。这也是它当年能火的两个原因效果显著改动小。1.3 后见之明作为学习信号为什么会有效你可能会问用事后达到的状态作为新目标教智能体学会的是一种特殊的技能——无论你让我把球推到哪个位置我都尽量做到。这可比把球推到那个固定的(0.3,0.4,0.1)泛化多了。真正的重点在于HER把稀疏的奖励信号变稠密了。在标准经验回放里1000局里可能只有2局成功是正样本其他全是无效数据。HER重标记之后理论上每一局都能产生至少最后一步是成功的正样本训练信号的密度大幅提升。更关键的是这些正样本不是凭空捏造的而是真实轨迹中达到过的状态。所以它们对值函数的估计是有依据的不会像fake reward那样误导策略。另一个微妙但重要的地方是HER没有改变要解决的任务原始目标那一份经验也保留着。这样智能体既能在事后目标上学到大量如何运动到某个状态的过程性知识又能在原始目标上明确知道真实任务是什么。两条腿走路探索效率高很多。2. 实现HER前必须想清楚的设计选项2.1 三种目标重标记策略到底怎么选HER论文里面提了好几种从一条轨迹里挑事后目标的方法。我说说实际用下来感受最深的几个直接上个对比表。策略做法特点适用场景final直接用episode最终状态作为新目标最简单稳定性好任务目标相对容易达成、轨迹状态分布不极端时快速验证时先用它future从当前时间步之后随机选一个状态作为新目标多样性最好论文推荐大多数连续控制任务、长操控任务是默认首选random随机生成K个目标选择能让reward1的那个目标空间需要能均匀采样探索能造出大量不同目标、目标分布已知时我自己的经验第一次在项目里用HER先用final跑通再切成future效果通常有明显提升。future里有个超参数k论文推荐k4意思是对每一条原始轨迹随机重标记4个不同的新目标生成4份额外的transition存进buffer。这个k不是越大越好k太大整个buffer会被重标记样本淹没原始目标信息被冲淡策略会偏保守后面我会细说。2.2 什么样的任务适合HER什么样的硬上会翻车HER不是万能药它有前提。最核心的一条任务必须是goal-conditioned也就是状态和动作之外必须存在一个可以显式指定的goal并且环境能够评估当前状态是否达成了这个goal。如果你面对的是打Atari这种没有goal实现、reward本身就由环境给出的任务HER根本没地方下手因为没有一个目标向量可供重标记。第二个前提算法必须是off-policy的。HER依赖replay buffer把历史轨迹翻出来重标记后反复学习这天然就是off-policy的玩法。你非要把HER接在PPO上等于强行违背on-policy只用最近策略采的数据这个前提数据分布会被重标记永久搞歪策略会在一堆历史数据里原地打转。所以在工程选型上看到稀疏奖励 off-policy goal-conditioned这三个条件同时满足才上HER。第三个要警惕的是重标记和原始目标的平衡问题。我见过很多新手把future采样比例调到很高以为反正重标记效果好那就全用重标记吧。结果策略学到一个奇怪的行为把物体推得很近但从不推到位因为重标记样本让它误以为接近目标已经是成功了。这种偷懒现象很常见。我的建议是重标记样本占比控制在30%~50%并仔细看成功率曲线发现策略走捷径立马回调。2.3 跟普通replay buffer相比HER多了哪些脏活普通replay buffer存一条transition就是(state, action, reward, next_state, done)五元组采样回来直接训。HER的buffer要复杂一些因为goal变了相关的维度都要跟着变。在标准设定里observation通常分成两部分achieved_goal当前实际达到的状态比如球的位置和desired_goal目标状态。而状态里已经包含了achieved_goal的信息下一时刻的状态也包含下一个achieved_goal。当一条transition被重标记时你需要同步修改新目标对应的reward、还有状态向量里desired_goal那一截。如果状态编码没处理好出现维度错位或者重标记后reward没跟着换整个训练大概率是玄学——loss在掉策略毫无进步你还找不到原因。所以我的一个实操建议是把状态拼接和重标记封装成一个独立函数专门负责根据新goal生成新的obs、new_obs、reward、done再进buffer。这样逻辑清晰排查起来也快。后面第三节会给出可直接抄的代码骨架。3. 从零手写一个HER机械臂推球任务的完整实操3.1 先定义环境与Goal的表达我带你把一个机械臂推球简化成Minimal Goal Environment只看HER的核心逻辑不陷入复杂的物理仿真。假设环境返回的obs是一个字典包含三个keyobservation、achieved_goal、desired_goal。这是老版gym GoalEnv的标准接口也是HER论文实验里采用的接口。新版gymnasium把GoalEnv拆了很多旧教程跑不通这也是一个容易踩的坑后面会提到。状态设计我按以下维度来obs机械臂末端位置3维 球的当前位置3维共6维achieved_goal球的当前位置3维desired_goal期望球到达的位置3维动作空间假设是3维连续控制控制末端位置的增量。成功判据用欧氏距离阈值当球的位置距离目标位置小于0.05时reward给1否则为0。这个环境足够让HER的目标重标记发挥价值。3.2 训练框架代码骨架网络部分我用普通的MLP Q函数训练算法用DDPG因为代码量最小核心是展示HER采样逻辑。注意这里的DDPG和标准DDPG的区别只在sample_batch这一步。训练主循环的核心代码逻辑大概是这样Python风格伪代码通用性强可直接迁移到你的项目import numpy as np from collections import deque class HERBuffer: def __init__(self, capacity1_000_000, k4): self.buffer deque(maxlencapacity) self.k k # future策略每个episode重标记次数 def add_episode(self, episode, env): # episode: list of (obs, action, reward, next_obs, done) raw_obs, raw_actions, raw_rewards, raw_next_obs, raw_dones zip(*episode) # 原始transition全部照存 for trans in episode: self.buffer.append(trans) # 对每个时间步用future策略重标记 T len(episode) for t in range(T): # 从t1到T-1中随机采样1个未来状态 if t 1 T: future_idx np.random.randint(t 1, T) future_goal env.extract_achieved_goal(raw_next_obs[future_idx]) new_obs env.replace_goal(raw_obs[t], future_goal) new_next_obs env.replace_goal(raw_next_obs[t], future_goal) new_reward env.compute_reward(future_goal, new_next_obs) new_done float(new_reward 0) self.buffer.append((new_obs, raw_actions[t], new_reward, new_next_obs, new_done)) def sample_batch(self, batch_size): batch np.random.choice(len(self.buffer), batch_size, replaceFalse) obs, actions, rewards, next_obs, dones [], [], [], [], [] for i in batch: o, a, r, no, d self.buffer[i] obs.append(o); actions.append(a); rewards.append(r) next_obs.append(no); dones.append(d) return np.array(obs), np.array(actions), np.array(rewards), np.array(next_obs), np.array(dones)这里的add_episode有两个关键点。第一原始轨迹的每一个transition都直接存进去保证原始目标信息不丢。第二对每个时间步t从未来时间步中随机采一个状态把它的achieved_goal当作新目标重写一条transition。因为是从未来挑状态所以重标记后的目标确实是这一局里真实达到过的reward算出来也是真实有依据的。env.compute_reward这个函数是环境相关部分要单独测。我给一个通用实现思路def compute_reward(goal, obs): achieved_goal extract_achieved_goal(obs) dist np.linalg.norm(goal - achieved_goal) return float(dist 0.05)3.3 训练流程与效果分析训练循环没有特别的地方就是经典的采样一个episode丢进HERBuffer然后从buffer里随机取batch更新Q函数和策略。我会在每500个episode打印一次当前成功率和buffer中正样本占比。这两个指标特别管用是判断HER有没有真正生效的第一手证据。如果你从零开始按这个流程跑你会观察到一种典型的曲线前面几百个episode成功率还是0但Q loss已经开始正常下降这是因为重标记样本正逐步把稀疏的成功信号注入训练。到了某个节点成功率会突然开始抬升这是HER最经典的从失败中积累到临界质量的现象。如果跑了几千个episode成功率还是纹丝不动那就要进入排查环节了。我在实际项目里用HER做二维平面的抓取与推球任务一个中等复杂度的任务在800个episode左右就能看到成功率从0升到40%左右靠纯随机探索的话这个数字可能要到5000个episode之后才有动静。这就是HER最直观的价值——不是帮你解决所有问题而是把稀疏奖励任务变成可学的任务。4. 训练HER时我踩过的坑和排查思路4.1 问题一reward算错重标记等于白做这是我见过最隐蔽的坑。很多人写HER的时候重标记后reward是用新目标去算的但忘了next_obs里的desired_goal也要同步替换。如果只换了reward没换obsQ网络的输入里desired_goal还是旧目标但reward是按新目标给的值函数会被这个不一致彻底搞懵同样的状态-动作一会儿reward是1一会儿是0且原因只在你没注意到的向量分量里。训练出来的策略就是薛定谔的策略——好像学了点什么又好像什么都没学。排查方法很简单在add_episode之后随机抽几条重标记后的transition人工打印出来看。核对一下reward1的那条transitionobs里的desired_goal是否和next_obs里的achieved_goal一致。不一致就是bug。4.2 问题二future采样k太大策略学会偷懒不想动k这个参数真的很微妙。我做实验对比过k1、4、8三种设置。k1的时候训练慢但稳定k4整体最好k8在训练后期出现了一个有意思的现象策略学会了把球推到非常近但不完全到位。原因分析下来是这样k8时buffer里重标记样本比例太高Q函数看到的样本中大量样本的目标是轨迹中某个容易被达到的中间位置。策略发现完全不动也能满足很多新目标——因为如果轨迹里某些时间步的球位置比较接近这些位置被当作重标记目标时原地不动的那几步也会被判定为成功。于是策略收敛到尽量不动这个局部最优。重标记样本太多把真实的推动目标这一信号稀释了。所以我的建议k值不要只用论文默认的4要结合你的实际任务调。出现策略偷懒的迹象优先减小k或者限制重标记样本在采样batch中的占比。4.3 问题三成功判定阈值太严格正样本永远是凤毛麟角HER有效的前提是重标记后能产生正样本。但如果你的环境成功判定阈值设得极度严格比如距离要小于0.001而智能体的动作精度本身有限会造成即使重标记了很多重标记后的transition里reward依然是0——因为下一时刻的状态距离未来的目标状态也没那么近。这时等于HER完全失效因为重标记没有带来稠密信号。这种情况要看你是真的需要这么高的精度还是阈值设得太激进。如果任务物理上能达成的精度本来在0.02左右你设0.001就是在自找麻烦。另一招是给reward做一个软阈值版本比如用exp(-dist)这种连续值作为密集reward让HER有了梯度信号实验会好跑很多。我自己在接近真实产品的项目里偏向用软reward 硬判定的组合来做。还有一个很经典的坑obs中的desired_goal维度跟new_obs里的维度被拼接两次导致网络输入维度比你想象的要多一些。这类维度问题用上面提到的打印抽查法最好使。5. 后见之明不止在强化学习里从算法到工程复盘思维5.1 用事后目标重标记的方式看待线上事故复盘把HER这个概念从RL算法提到抽象的思维模型后见之明在工程管理上其实有一个特别有价值的应用事故复盘。线上出了事故大家最常做的复盘是谁在哪一步做了什么导致了这个故障。这种复盘方式本质上是用最终结果去反推当时每个决策的对错其实是典型的hindsight bias——事后看每一步都那么明显但在当时的信息条件下那个决策可能是完全合理的。HER给我们的启发是复盘的目的不是审判原始目标为什么没达成而是重新定义目标——把当时想做成的事换成当时环境下做到的事再去审视这个过程中哪些做法产生了真实的推进。具体操作上我见过做得好的复盘流程是先不看结论把当时的时间线、决策依据、可选方案原样列出来然后问如果当时的目标是在信息受限的情况下做最优决策哪些节点是可以保留的哪些是可以改进的。这种blameless复盘从心理学上说也更健康因为参与者不会被恐惧绑架愿意暴露更多真实信息。从工程效率上讲它才能真正沉淀出可复用的经验而不是一份甩锅纪要。5.2 后见之明在A/B测试和数据分析里的应用陷阱数据分析里有种经典错误叫幸存者偏差中文互联网上被讲过很多次但我想从HER的视角再拆一遍因为本质其实相同。假设你有一个推荐系统做了A/B实验发现点击率提升2%的方案获胜。你在复盘报告里通常会写采用方案B因为它带来了2%的点击率提升。这个推理逻辑看起来天经地义但这里藏着一个后见之明陷阱你是在知道实验结果的前提下认为方案B的设定和它带来的提升之间有强因果。但实验很可能只跑了一个版本扩展参数空间里的其他变体也许能带来5%的提升。你没有在实验设计阶段给其他可能性设置目标所以事后无论结果如何你都只能基于已有结果讲故事。用HER的方式来说就是你只在原始目标上学习了一次没有做目标重标记——没有在事后把目标改成如果目标是选出推广价值的参数组合请问我应该看哪些指标也没有对同一个策略去试多个不同的事后目标比如不同人群、不同时段、不同业务指标。所以数据分析的复盘要主动引入后见之明的视角设计实验时预埋多个可替换的评估目标事后用不同目标对同一批结果做重标记式复盘才能避开拿着一个结果硬编故事的自欺。5.3 决策日志给未来的后见之明准备好原料HER能工作的前提是它手里有完整的轨迹数据transition然后才能做重标记。如果没有记录下每一步的状态和动作光有最终结果想重标记也没有对象。工程决策也一样。如果你想在未来能够对过去的决定做后见之明式的复盘现在就必须做一件事情写决策日志。在每次做出关键决策时记录下三点当时掌握的信息、当时的可选方案、最终选择这个方案的理由。不是写多详细而是保证这个记录发生在知道结果之前。这样做的价值在半年后会显现当你翻到记录会看到当时没有考虑某个隐患的证据也能看到当时在信息条件下其实做了正确选择的情况。你再做复盘时就能像HER重标记一样把当时的目标替换成在有限信息下做最优决策而不是拿最终结果去审判决策者。这个习惯我坚持很久了确实让团队的复盘质量提升明显——不再扯皮而是积累可复用的决策经验。6. 最后一次实操建议让HER在你的项目里真正跑起来说了这么多最后给你一个保命的实操流程复盘。开始一个新任务时先花30分钟把环境接口梳理清楚明确obs、achieved_goal、desired_goal、action各自是什么把compute_reward单独写成纯函数并跑几个手算case验证。之后再接HER不要一边写环境一边写训练bug定位起来非常痛苦。训练过程中每500个episode打印三个指标原始目标成功率、重标记样本占比、Q_loss。这三个指标能覆盖80%的问题排查因为任何一个异常都会先反映在这里。稳定性优先的话先上final策略跑通了再换future不要一开始就整k8这种激进配置。我和这个算法打过很多次照面从论文复现到真实项目落地最大的体会是HER真正值钱的不是那个重标记的代码技巧而是它的思维方式——在一堆看似无用的失败里换个角度看目标就能提取到有效信号。这种用后见之明给自己造奖励的哲学比算法本身更能帮助你解决实际工程中的稀疏反馈问题。包括前面聊的复盘思维和决策日志本质上都是这个哲学在不同场景的投影。下次遇到奖励全是0的项目别急着投降先想想你能不能给自己造一个事后目标。
返回列表