ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

强化学习中的后见之明经验回放:破解稀疏奖励实战指南

强化学习中的后见之明经验回放:破解稀疏奖励实战指南 hindsight英语里是“后见之明、事后聪明”的意思。这两年这个词在技术圈刷屏不是因为它出现在英语考卷上而是因为OpenAI在强化学习领域发了一篇名为 Hindsight Experience ReplayHER后见之明经验回放的经典论文。这次我不打算聊人生哲理就聊聊项目“hindsight”本身它到底解决什么问题为什么取这么个名字以及怎么把它真正落地到自己的项目里。如果你正在为稀疏奖励的问题发愁或者想复现一个能跑通的强化学习基线这篇应该能帮你省下不少时间。我最早看到“hindsight”这个词是在一篇讲机械臂学习抓取的技术博客里。作者抱怨说仿真环境里机械臂抓取的成功率常年徘徊在10%以下加了各种奖励塑形还是不稳定最后把HER接进去训练曲线才终于“立”起来。当时我就意识到这个项目名字起得太妙了——它不是在说算法多聪明而是在承认一个最朴素的事实事后来看我们总能从“失败”里找出点有用的东西。而HER做的恰恰就是把这种“事后聪明”变成机器能用的样本。1. 项目全貌回顾HER解决的是稀疏奖励下的学习危机1.1 核心需求稀疏奖励为什么让强化学习几乎学不动先把问题磨清楚。强化学习的标准范式是智能体通过和环境不断交互用奖励信号修正自己的策略。奖励给得越多、越频繁学习信号就越充足模型自然容易收敛。但现实任务里奖励往往极其稀疏机器人抓取物体抓稳了才给1分否则全程给0游戏里要打通一关才给奖励中途不管你怎么游走都是白搭。这种情况下随机探索的策略几乎不可能在有限步骤里撞上那个“有奖励”的状态。比如FetchPush这种仿真任务机械臂要把桌子上的物体推到目标点如果目标点占整个工作空间的比例很小纯随机探索的成功概率可能只有百分之零点几。也就是说你跑了十万步回放缓冲区里全是奖励为0的轨迹梯度信号被稀释到几乎不存在Q值网络根本学不出什么东西。这就是样本效率危机。常见的应对办法是奖励塑形reward shaping人工设计一个距离函数让智能体每靠近目标一点就给一点奖励。问题在于手搓的奖励函数很容易让智能体钻空子——它可能学会了“靠近”却学不会“抓住”或者在仿真环境里有效、真机上就失效。奖励塑形本质上是在用人的先验知识作弊代价是泛化能力差而且调试成本极高。HER正是冲着这个问题来的。它的思路不是重新设计奖励而是换一个角度使用已有的失败轨迹。1.2 “事后聪明”做了什么把失败的轨迹重写为成功拿射箭来类比。你站在十米外开弓射箭目标是靶心结果箭射偏了钉在靶子的左上方。按标准强化学习的逻辑这条轨迹毫无价值——没有命中靶心奖励为0什么都不学。但从“事后”的角度看如果这次任务的目标不是靶心而是箭命中的那个点呢那么这箭堪称完美你刚才的发力方式、瞄准偏左的角度全部都是“成功经验”。HER的核心操作就是这个轨迹还是那条轨迹但把目标任务改成轨迹里实际到达的状态然后重新计算奖励。原本满屏0的样本瞬间变成了一堆奖励为1的成功样本。智能体在回放这些样本时学到的不再是“我失败了”而是“如果目标在这里我刚刚这套动作就是对的”。这个“如果”就是整篇论文的题眼。用更技术一点的说法HER是一种目标重标记goal relabeling策略它把目标条件强化学习里的失败经验转变成可供off-policy算法学习的虚拟成功经验。它没有改变真实任务的难度但把数据利用率提升了一个量级。1.3 这个项目适合谁如果你正在做这几件事HER基本是绕不开的机器人控制机械臂抓取、推动、堆叠这类多目标操作任务稀疏奖励下训练不动先考虑HER目标条件策略学习输入里带goal向量、需要根据目标切换行为的任务复现论文基线OpenAI官方在Fetch系列仿真环境上发布的基线就基于HER做导航、游戏、对话等稀疏奖励任务的决策模型只要任务满足“目标能描述、奖励能计算”HER都能套一层试试。但要记住HER不是万金油。它是off-policy算法的“配件”本身不独立工作。你至少得先有一个类似DQN、DDPG、SAC这样的基础算法再把HER接进去。2. 原理拆解HER凭什么把样本效率翻上去2.1 从标准经验回放说起先回顾一下DDPG这类off-policy算法的基本结构。智能体把交互产生的四元组(s_t, a_t, r_t, s_{t1})存进一个大的回放缓冲区训练时随机采样一批更新Actor和Critic。经验回放的好处是打破时间相关性、重复利用数据在奖励密度正常的时候表现很好。但稀疏奖励环境里“奖励正常”这个前提不存在了。缓冲区里存的数据绝大部分是失败轨迹奖励项全是0。Critic网络拟合出来的Q值全在零点附近梯度告诉Actor“所有动作差不多都一样烂”策略自然无法改善。2.2 目标重标记HER最核心的一步HER打破僵局的方式是在存入缓冲区前对样本做一次“篡改”。假设一条轨迹有T步每一步都对应一个观测状态下实际到达的目标状态g_t比如机械臂末端位置。原始任务目标是g这条轨迹没能抵达g所以原始奖励全为0。HER做的是从轨迹里挑一个或几个状态作为“新目标”g_new然后用g_new重算每步奖励再造一批样本放进缓冲区。伪代码长这样# episode_goals: 每个时间步实际到达的状态如机械臂末端坐标 # actions: 每个时间步执行的动作 # k: 每条轨迹额外制造的重标记样本条数 def her_relabel(episode_goals, actions, k4, strategyfuture): T len(actions) extra_samples [] for _ in range(k): t random.randint(0, T - 1) if strategy future: if t T - 1: future_idx random.randint(t 1, T - 1) else: future_idx t new_goal episode_goals[future_idx] elif strategy episode: new_goal random.choice(episode_goals) elif strategy final: new_goal episode_goals[-1] else: # random new_goal random.choice(all_previous_goals) for i in range(T): new_reward compute_reward(episode_goals[i], new_goal) extra_samples.append((state_i, action_i, new_reward, state_next_i, new_goal)) return extra_samples关键在compute_reward这一步。对于目标条件任务奖励通常是稀疏的如果当前状态和目标的距离小于某个阈值就给1否则给0。重标定目标后轨迹后半段的状态通常和新目标很接近于是连续多个时间步的奖励都会变成1这种情况下Critic能非常清晰地学到“这些状态接近目标价值很高”。2.3 四种目标取样策略为什么“未来”最香HER论文里对比了四种从头条轨迹里挑新目标的策略策略做法特点final用轨迹最终状态作为新目标简单粗暴但轨迹早期状态可能和新目标差太远产生大量低质量样本future从当前时间步之后的某个状态里随机选保持时间上的因果顺序样本质量高episode从同一条轨迹的任意状态里随机选可能出现“用未来状态当目标教过去动作”的时间倒错问题random从所有历史状态里随机选方差大效果最差通常只当对照组实际跑下来future策略基本是最好用的。因为它保证了重标定的目标在时间轴上不早于当前状态相当于在说“从这里出发你后面确实走到了那里”因果性不会被破坏。final策略之所以次一点是因为当轨迹很长时起始状态到末端状态的距离可能太远一步决策器学起来非常吃力。HER默认推荐k4也就是说每条真实轨迹额外再造4条重标记轨迹这个比例要控制好太多了会让缓冲区里虚拟样本主导训练反而干扰真实目标的学习。2.4 为什么它称不上万能药再强调一下边界。HER的适用前提有三条任务必须是目标条件的即目标本身可以作为网络输入奖励函数要能随时根据状态和目标重新计算基础算法得是off-policy的能反复使用历史样本。如果你的任务是单一固定目标且状态空间里没有“目标”概念或者你用的是PPO这类on-policy算法一条数据只用一次那HER帮不上忙。这其实也是做项目时最容易犯的错误之一觉得HER是“稀疏奖励解药”见着稀疏奖励就往里塞结果环境不满足目标条件缓冲区里全是垃圾样本训练比之前还慢。正确的心态是HER不是独立算法它是插在“经验回放”这一环上的包装器它让失败数据变得可学但学不学得动还得看底层算法的本事。3. 实操落地把HER接到自己的训练流程里3.1 环境与工具选型动手前先把环境准备好。OpenAI当年在论文里用的环境是Gym里的Fetch系列包括FetchReach够到目标点、FetchPush推箱子、FetchSlide滑冰式推远、FetchPickAndPlace抓取并放置。这些环境早期基于MuJoCo物理引擎步长、观察空间、奖励阈值都是现成的非常适合做HER的对照组。工具链方面最省事的方案是直接拿开源的强化学库做底子Stable-Baselines3自带builtin的HER支持在her.py里配合OffPolicyAlgorithm用很顺手RLlib分布式好、环境适配广但上手门槛高一点自己用PyTorch实现DDPG HER最透彻适合想深入研究的人。我个人推荐先跑通Stable-Baselines3里的HER示例再换自己的环境。因为你第一要确认的是算法在本机上的训练曲线和论文一致而不是纠结代码有没有写错。3.2 核心代码拆解记住四个位置如果把HER接进Stable-Baselines3核心逻辑分布在四个地方from sb3 import DDPG from sb3.her import HerReplayBuffer model DDPG( MultiInputPolicy, env, replay_buffer_classHerReplayBuffer, replay_buffer_kwargsdict( n_sampled_goal4, # 相当于k值 goal_selection_strategyfuture, # 目标取样策略 online_samplingTrue, # 是否在采样时实时重标记 max_episode_length50, # 环境最大步数 ), buffer_size1000000, learning_rate1e-3, batch_size256, train_freq20, gradient_steps20, learning_starts1000, gamma0.98, verbose1, ) model.learn(total_timesteps300_000)第一处采样时要把整条轨迹完整传给缓冲区不能像普通DQN那样一步一存第二处缓冲区要记录每个时间步的观测、动作、以及实际达到的目标achieved goal第三处在把样本放入缓冲区之前调用重标记逻辑按策略生成新样本第四处训练采样时要保证普通样本和重标记样本按比例混在一起。自己写也很简单把环境返回的observation包装成包含observation和achieved_goal字段的字典DDPG的Actor吃的是observation部分Critic吃的是observation和goal的拼接reward则完全由重标记后的goal决定。3.3 参数调优实战哪些值最该动初学者最容易把论文默认参数当圣旨照抄。我实测下来最值得花时间调的是下面几个参数参考值调整方向与观察n_sampled_goalk4k越大单条轨迹产生的虚拟样本越多样本多样性更好但训练变慢超过8后收益锐减goal_selection_strategyfuture不要用random几乎必扑final可以作为对比实验buffer_size1e6稀疏奖励下缓冲区要够大否则早期样本反复被重放过拟合严重batch_size256太小则梯度噪声大Critic学不稳太大则训练慢且容易收敛到次优learning_starts1000前期纯随机探索让缓冲区先攒一点能重标定的经验gamma0.95~0.99任务周期越长gamma越需要靠近1我在FetchReach上跑过一组对照k4、future策略、buffer_size1e630万步就稳定收敛成功率99%以上改成k1之后收敛速度明显变慢最终成功率在85%左右波动把策略改成episode训练过程开始剧烈震荡最后只到70%出头。这些数字本身就是答案——HER的收益主要来自“量”和“因果顺序”别在奇怪的地方省。3.4 训练监控点盯曲线别只盯最终得分很多人在等训练结束才看成功率这是低效的。准备好tensorboard或wandb每个训练步记录几个关键指标rewards_mean回放样本里的平均奖励。理想情况下HER的缓冲区里会有大量虚拟正样本这个值不会一直贴地。q_valuesCritic对批量样本的预测Q值。如果Q值持续不涨说明目标重标定出的样本没有被Critic学会多半是网络容量或batch_size太小。episode_success_rate真实环境中的成功率。这个指标滞后于Q值但只要Q值在涨成功率迟早会跟上。explored_goal_rate重标记目标里有多少是“没见过”的新状态。如果长期集中在几个状态说明探索不足。我自己习惯看Q值和真实成功率的曲线形状正常情况应该是Q值率先上升、平缓然后成功率在某一轮训练后陡然拔高再做阶梯式上升。要是两根曲线长时间都平着别犹豫先去调探索噪声和k值。4. 踩坑记录与问题排查实录4.1 常见问题速查表现象可能原因排查方向训练前几万步loss直接发散观察空间里没有包含achieved_goal检查环境返回的observation字典字段是否完整Q值不涨成功率一直是0奖励阈值设得太小重标记后正样本占比极低用脚本单独验证compute_reward函数训练到中期成功率突然回退缓冲区里虚拟样本比例失衡真实目标样本被稀释降低k值或提高真实样本的采样权重连续控制震荡明显Actor学习率太高Critic过拟合重标记样本把actor lr降到1e-4增加buffer_sizefuture策略比final还差轨迹长度太短future可选范围太小确认max_episode_length设置防止轨迹被截断换环境后完全失效新任务的reward函数无法从状态计算重新设计goal的编码方式4.2 我花了两天排查的一个问题奖励函数里的隐式陷阱有一次在FetchSlide上复现HER跑了两轮训练成功率都在10%以下。第一反应是网络结构有问题折腾一整天没结果。第二天静下心把compute_reward拉出来逐行打印才发现我在计算距离时用了欧几里得距离但目标的编码只有x、y两个分量而物体的z轴高度偶尔会有微小浮动。目标点判定阈值是0.05一台MuJoCo只要物理结算时物体轻微跳一下距离就超过阈值正样本瞬间变成负样本。这不是算法问题是环境本身的数值稳定性问题。后来我把距离计算改成了只取x、y平面距离训练曲线立刻正常。所以当你觉得HER不work的时候先怀疑奖励函数再怀疑算法。任何仿真里因为物理抖动、观测噪声造成的数值小波动都可能在稀疏奖励下被放大成致命错误。4.3 有HER和没HER差别到底在哪儿拿一次完整的DDPG对照实验来说环境是FetchReach同一个Actor/Critic网络同一个随机种子唯一区别就是回放缓冲区是否启用重标记。无HER的DDPG在10万步内成功率一直是020万步后勉强爬到5%加HER后3万步开始出现正信号8万步突破50%25万步左右达到98%。在FetchPush上差距更明显无HER的版本跑到60万步连20%都摸不到加HER后40万步轻松超过90%。这份差距的本质是样本利用率。HER把一条注定失败的轨迹重构成了若干条“有进度”的学习样本相当于把训练数据量凭空放大了几倍。但代价也很直接回放缓冲区变大每个训练步采样成本变高占用的显存和内存都会上涨如果环境状态维度特别高比如图像输入重标记成本也会非常可观。好在对于大部分机器人控制类任务状态维度都在几十维以内多出来的开销完全可以接受。5. 一些个人建议与后续思路最后说几句题外话。hindsight这个词放在工程语境里就是“复盘”。HER最打动我的不是那个重标记技巧本身而是它对待失败经验的态度——它不让智能体把失败一笔勾销而是想办法把失败里潜藏的可迁移知识挖出来。这种思路在真实工程里同样适用每次调参失败、训练发散、复现不work都值得把日志、随机种子、环境参数完整存下来过两天回头再分析。我写代码有个习惯所有实验脚本里都固定随机种子并自动保存配置看起来是小事但遇到问题要复盘时能救命的往往就是这些小东西。如果你已经在DDPGHER上跑通了基础版本后续可以往这几个方向延伸一是把HER和SAC结合在连续控制任务里通常比DDPG更稳定二是试一下“Hindsight Instruction Generation”HIG它把重标记目标从状态推广到语言指令适合多任务机器人物品抓取三是在仿真到真机的迁移任务里配合domain randomization一起用把虚拟经验搬到物理世界。根据我个人的经验只要稀疏奖励还存在一天这种“事后聪明”的思路就不会过时它很可能还会继续藏在许多新算法的基础组件里等着你去拆开看。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进