ARTICLE DETAIL

资讯详情

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

强化学习稀疏奖励难题:HER后见经验回放原理与实践

强化学习稀疏奖励难题:HER后见经验回放原理与实践 hindsight中文里通常翻译成后见之明说白了就是事后回头看。这个词在日常对话里总带点自嘲味道——当时我怎么就没看出来呢但在强化学习里hindsight却是一个正经到不能再正经的技术名词Hindsight Experience Replay后见经验回放HER。我第一次认真研究HER是因为在机械臂抓取任务里把模型训崩了环境返回的奖励信号几乎全是0智能体像个无头苍蝇一样在状态空间里乱撞。后来读到OpenAI那篇《Hindsight Experience Replay》核心就一句话既然学不到如何达成目标的经验那就把没达成目标的经历改写成达成另外一个目标的成功经验。这篇文章我会从这个词本身的两种理解讲起讲清HER为什么能解决稀疏奖励问题再给出一个可以直接跑的DDPGHER实现和踩坑记录。适合正在做强化学习项目、被稀疏奖励折磨的算法工程师也适合刚入门RL、想知道事后聪明怎么变成算法的同学。1. 从马后炮到算法hindsight的两副面孔1.1 认知心理学里的后见之明偏误每个人都绕不开hindsight bias。你问一个人昨天预测足球比赛谁能赢他会笃定地说我早就知道这支队伍会赢。但你翻他的聊天记录他当时明明押的是对面。心理学上把这个叫做后见之明偏误hindsight bias也叫我早就知道了效应。原因是人的记忆会在线重构——结果一旦揭晓大脑会自动抹掉那些与结果不符的信息把过往的判断修正成与结果一致的样子。举个工程场景里的例子线上服务半夜挂了复盘时大家一口咬定日志里早就有征兆。但如果这些征兆真的足够明显当时值班的人大概率早就采取行动了根本等不到故障爆发。这对做技术的人来说是件危险的事。因为复盘的目标是改进系统不是为了证明谁的水平高。一旦被hindsight bias带偏你从复盘里得到的结论是失真甚至错误的你会把随机波动解读成必然趋势会把本来没被注意到的耦合关系强行解释为早就有问题。所以我在团队里做事故复盘有个习惯任何早该想到的说法都必须拿出当时的监控截图、日志时间戳、告警记录来核实拿不出证据的猜测一律不算数。没有证据链的后见之明就只是情绪不是教训。1.2 从偏误到武器工程师怎么用后见之明同样的事后聪明换个角度看反而是数据世界里最值钱的资产。想象一下机械臂抓取任务目标是把桌上的红色方块抓起来放到指定区域。智能体尝试了无数遍每次都把方块推歪了。在标准强化学习框架下这些失败经验的价值趋近于零——它们最大的用处就是告诉智能体这条路走不通但对一个奖励极度稀疏的任务来说连走不通的信息都微弱得可怜。但如果换一种思路我本来想抓红色方块没抓着但方块被我推到了左边某个位置。那我能不能假装刚才的任务不是抓方块而是把方块推到左边那个位置这样一看——哎这个经验就是一个非常标准的成功样例啊。这就是HER的核心机制用已经发生的结果重写原本的目标。目标从预定目标改为事后实际达到的状态于是所有失败轨迹全部可以被改造成带正向奖励的成功轨迹。一个是心智陷阱一个是算法利器同一个词在人类认知里是bug在强化学习里是feature。理解了这层关系你就明白为什么HER在2017年之后几乎成了机器人强化学习的标配。2. 稀疏奖励困境与HER的核心解题思路2.1 稀疏奖励为什么训不动先说说稀疏奖励为什么会让强化学习崩盘。在标准策略梯度或Q-learning框架下智能体靠最大化累积奖励来调整策略。如果奖励函数只在任务成功时返回1其他时候返回0那么在探索初期智能体几乎没有机会获得正反馈。对于高维连续控制任务比如7自由度机械臂的关节控制随机探索撞上精确目标的概率低到可以忽略。拿最常用的测试环境FetchReach举例机械臂末端需要到达某个三维目标点状态包含机械臂关节角和目标位置动作是7维关节速度奖励是稀疏的——末端与目标的欧氏距离小于阈值则奖励0否则奖励-1。在这种设置下用普通DDPG去训练前几千个episode的奖励几乎全是-1Q值没有任何有效的梯度来提升策略。训练曲线不是慢慢收敛而是纹丝不动因为随机策略成功概率约等于0无论怎么更新都拿不到一条带有正向信号的轨迹。这时候你才会真正理解为什么OpenAI作者要把hindsight放进标题里解决一个任务先得让已发生的经验产生学习信号。2.2 HER怎么把失败洗成成功HER的做法不复杂核心只有一条在每个训练轮次结束后除了保留原目标下的经验再额外生成重标定目标下的经验一起放进经验池。具体来说假设一条轨迹τ [(s₀,a₀,r₀,g₀), (s₁,a₁,r₁,g₁), …, (s_T,a_T,r_T,g_T)]其中g₀就是本次episode的原始目标。按原目标计算绝大多数奖励是-1。HER会这样操作选择一个事后目标g它取自如轨迹上某个后续时刻的实际状态比如机械臂末端最终到达的三维坐标然后重新计算这条轨迹在新目标下的奖励。重标定之后的奖励至少最后一步一定是0因为目标g就是轨迹末端实际达到的状态。于是这条原本失败的轨迹在经验池里就变成了成功轨迹。DDPG这类算法根本不在乎经验对应的是哪个目标它只需要(s, a, r, s)四元组。HER只是多了一个条件目标g本身是事后生成的且与之对应的策略必须被理解为以g为目标的条件策略。这里必须强调一点HER不是欺骗智能体它的正确性有逻辑保障。OpenAI论文里的论证思路大致是如果重标定后的目标g能由某条策略达成由于原目标g与g之间并没有本质区别同一个策略只要把目标作为输入参数化比如输入状态时同时输入目标就有机会学会达成g。打个直白比方你练习投篮没投进但球落在了一个特定位置。把这个位置记下来当作下一个任务目标你发现自己完成了把球扔到那个位置的任务——这会产生一次成功投篮的反馈。练多了你对把球控制到任意位置的能力就会增强而这种控制能力恰恰是投中篮筐的前置基本功。2.3 四种事后目标采样方法HER论文给出了四种事后目标的采样方法实现时需要仔细选。final取整条轨迹的最后状态作为唯一新目标。实现最简单适合把东西推到某个终点这类任务。future从当前时间步之后的某一步未来状态中采样一个状态作为新目标。这是论文中效果最好的一个因为它允许智能体学习中间过程也能成为目标训练信号密度最高。episode从同一条episode中的所有状态里随机选一个。random从经验池里随机拿一个状态不要求来自本episode。在实际使用中我默认用future并把未来采样范围控制在一个比较短的窗口内。需要留意的是对Fetch类任务final和future在最终成功率上差距不大但future在早期收敛速度上明显更快。random表现最差往往和不用HER的效果差不多因为随机目标与当前轨迹关系太弱很难提供有效的梯度方向。3. DDPGHER从零实现环境、代码与调参3.1 环境搭建与基线声明我选择OpenAI Gym里的FetchPickAndPlace作为主战场。这个任务要求把桌子上的箱子抓起来放到目标位置包含移动、抓取、摆放三个环节对HER的展示效果特别典型。依赖栈如下Python 3.8gym需要配套的MuJoCo环境以及你自己手写的DDPG。为什么推荐手写而不是直接用stable-baselines3这类框架因为HER要在采样环节做定制改框架的流程往往比自己写更费事。如果你只是先跑通我强烈建议先用FetchReach这种简单任务验证代码逻辑等HER核心模块写好后再换成FetchPickAndPlace。直接上复杂任务的痛苦我体验过光是对着环境细节排查问题就花掉一整天不值得。3.2 重标定模块的代码实现下面给出最小实现里最关键的函数对一条轨迹采样hindsight目标并重新计算奖励。def sample_hindsight_goal(trajectory, step, strategyfuture, future_k50, rngNone): states, actions, goals, rewards trajectory T len(states) if strategy final: new_goal states[-1][achieved_goal].copy() elif strategy future: if step T - 1: # 序列末尾没有未来状态可用退化为 final new_goal states[-1][achieved_goal].copy() else: max_k min(future_k, T - step - 1) k rng.randint(0, max_k) idx step 1 k new_goal states[idx][achieved_goal].copy() elif strategy episode: idx rng.randint(0, T - 1) new_goal states[idx][achieved_goal].copy() return new_goal注意在Gym的Fetch系环境里观测空间被拆成了obs、achieved_goal和desired_goal三个字段。重标定之后不仅要替换新目标还要重新生成奖励# 重标定奖励用环境自己的 compute_reward 重新计算 new_rewards [] for t in range(T): new_rewards.append( env.compute_reward( achieved_goalstates[t][achieved_goal], desired_goalnew_goal, info{} ) )这里有个容易踩的坑轨迹里的achieved_goal是随时间变化的重标定目标后每个时间步的奖励都要用对应时刻的实际状态重新计算。如果你偷懒只在最后一步用新目标计算一次奖励其余时间步沿用原来的-1HER的效果会大打折扣。因为中间步骤的奖励信号恰恰是引导策略逐步接近目标的过程性反馈。3.3 训练循环经验怎么进池子DDPGHER的完整训练循环通常是这样的初始化DDPG的Actor、Critic网络、目标网络和经验池。对每个episode从环境获取初始状态和目标g智能体通过actor与环境交互收集整条轨迹τ。episode结束后把整条轨迹以原始目标g存入经验池。从这条轨迹中采样K个hindsight目标对每个新目标重新生成一条经验轨迹也存入经验池。从经验池中随机采样batch更新critic和actor各一次或多次。关键的扩增倍数K我一般设置在4到8之间。K4的意思是每条真实轨迹额外产生4条hindsight轨迹存入经验池。这样一来经验池里的有效样本密度翻了5倍正样本比例显著提高训练信号从偶尔才成功一次变成每条轨迹都包含成功片段。但K并不是越大越好。K过大会导致经验池被伪造的成功主导策略对真实目标空间的覆盖反而下降我试过K20收敛速度没有变快显存占用倒是翻倍了。还有一处需要同步修改DDPG的Q函数和目标网络要把目标g作为额外输入。在Fetch类环境里actor和critic的输入是concat([obs, desired_goal])的形式。HER重标定目标后critic看到的目标分布变了——不再只是原始目标而是包含大量事后目标——所以critic必须学会针对不同目标给出正确的价值评估。这也是为什么UVFA通用价值函数近似器的思想在论文里被反复提及目标越多样价值函数越通用策略的泛化能力就越强。3.4 超参数与实测效果分享一组我在FetchReach上验证过的基线参数超参数参考值说明隐藏层[256, 256]两层全连接激活函数ReLUbatch_size256经验池采样数量buffer容量100万覆盖重标定后的所有轨迹K4每条轨迹额外生成的目标数future采样窗口50从当前步后0~50步中采样目标奖励稀疏类型距离0.05则奖励0否则-1actor探索噪声高斯噪声σ0.2随训练衰减学习率1e-3actor和critic一致软更新参数τ0.05目标网络软更新同样的FetchReach任务普通DDPG训练100个evaluation episode的成功率几乎一直是0而DDPGHER大约在几万步之后就能达到接近100%的成功率差距非常直观。更复杂的FetchPickAndPlace任务HER配合一些工程优化比如对抓取阶段的reward shaping可以达到90%以上的成功率这已经是很多现实机械臂控制项目愿意上线部署的及格线了。这里有个真正的经验之谈HER和DDPG是天生一对因为两者都是off-policy经验池里的样本可以和当前策略解耦。HER生成的重标定轨迹天然是off-policy的——它们不是当前策略试图完成的目标但DDPG不在乎它只需要从经验池里采样本学习。反过来如果你用PPO这类on-policy算法套HER会遇到一个大问题重标定目标的轨迹分布已经偏离当前策略的访问分布重要性权重修正会变得很复杂。我在实验里见过有人强行把HER塞进PPO结果收敛非常不稳定。所以如果项目不是非用PPO不可优先考虑DDPG、SAC这类off-policy算法。4. 训练踩坑实录我替你先趟过的五个坑4.1 把所有hindsight轨迹一股脑写进经验池失败最开始我把所有重标定轨迹统统写进经验池K8跑几轮之后经验池里90%以上都是hindsight轨迹。结果critic严重偏向目标很容易达成的乐观估计actor实际推演时却很难找到对应动作导致训练振荡成功率忽高忽低。解决办法是限制hindsight样本在经验池中的占比。我在采样时做了一个简单的比例控制原目标样本和hindsight样本的比例保持在1:1到1:2之间训练立刻稳定不少。4.2 future采样窗口开太大学了一堆远期废目标future策略的窗口最初设成200结果智能体经常把目标标定到很久以后才可能到达的位置。在这些位置上中间控制动作和目标几乎没有相关性学习信号又变稀疏了。直观理解比赛已经结束十分钟你还把当时那个位置当成成功率指标对当前决策的信息量自然就小。经验上窗口设在当前步加50左右效果最好。4.3 奖励阈值与目标空间尺度不匹配Fetch系环境对成功的定义是距离小于某个阈值。很多同学用HER时直接把这个阈值写死成0.05但如果机械臂底座到目标位置的尺度是米级0.05意味着目标空间被切成数百个细粒度格子随机命中的概率更低。我的做法是先把阈值放大到0.1或0.15来观察训练信号等收敛后再调回0.05。这样第一阶段学习稳定第二阶段精度提升也比较平滑。4.4 Q值过估计导致崩溃HER把大量成功轨迹注入经验池value网络很容易高估Q值。我在跑FetchPickAndPlace时遇到过一种情况训练中期loss下降但一小段时间后突然崩溃策略输出全是乱颤的动作。排查下来是目标网络更新太快、critic的过估计没有被压制。后来把软更新τ从0.05降到0.01并且给critic加了L2正则问题就稳定不少。如果你用的是更现代的SAC自带的熵正则能给value提供一些约束但也别忽视这个问题。4.5 训练问题速查表现象可能原因处理建议成功率长期为0稀疏奖励下随机探索无法成功先用final策略或加大K训练中后期振荡K过大或hindsight样本比例过高降低K控制正样本占比Q值持续上升但策略无改进critic过估计降低τ、加正则、检查reward范围目标达成但任务失败的错觉只优化了目标重标定没有优化原目标行为提高原目标样本占比5. hindsight的工程复盘应用除了算法这个词还能救项目5.1 事故复盘里的后见之明怎么用才不坑聊完算法我想把hindsight拉回到工程师的日常。无论是线上事故、模型badcase还是项目延期复盘几乎都是标配动作。但绝大多数复盘会开成批斗会或表演会——前者找个人背锅后者大家轮流自我表扬防止被追责。根源就是后见之明偏误跑出来了。我的经验是做复盘时先建立时间线证据墙。把事件发生前的所有监控数据、日志、聊天记录、代码提交按时间排列让参与复盘的人先看时间线再看结论。在时间线里很多问题会被重新发现不稳定其实只在深夜两小时出现、指标变化和代码上线完全无关、告警阈值设置得太宽导致从未触发。没有时间线的复盘本质上就是各说各话的讲故事大会。5.2 正确使用事后聪明的三个原则第一区分可预防和不可预防。判断标准只有一个在当时掌握的信息范围内是否有合理的行动可以避免如果没有就不能用早就该想到来归因。第二把结论写成可执行的行为变更而不是一声叹息。以后上线前要检查XX指标比这次事故暴露了监控不足有用一万倍。第三记录当时的假设。我习惯在每次重要决策时同步写一小段当前决策的理由和潜在风险三个月后再回看这段文字比任何事后分析报告都诚实。5.3 个人知识沉淀中的回溯式学习个人学习也一样。训练模型失败、项目上线失败、论文被拒本质上都是经验。如果只把失败当作失败你损失的是最大的一笔学习样本。我自己的习惯是每次项目结束问自己三个问题——我最初以为哪里会出问题实际哪里出了两者之间的差距说明我对系统的认知哪里需要修正这种带着事后聪明的复盘不是马后炮而是把自己的认知模型重新拟合一遍。人工智能靠hindsight学会高效学习人也一样。把hindsight聊到这儿我最大的体会反而是这个词最迷人的地方不在算法代码里而在它同时出现在人的认知缺陷和AI训练秘诀中。人因为后见之明而容易瞎归因强化学习却因为后见经验回放而学会了高效学习。它提醒我们回头看不必然是坏事——关键是你能否设计出一种机制让已经知道答案的视角服务于还不知道答案的过程。HER做到了复盘方法论做到了我们的日常工作也可以试试。最后分享一个小习惯每次训练失败后不要急着改网络结构先把那条失败轨迹用hindsight目标重标定一遍问自己这次状态里有什么是可以变成新目标去学习的。这个习惯帮我少跑了几十次无效实验希望你用得上。
返回列表