尧图精选

从稀疏奖励到HER:强化学习经验回放机制深度解读

🕒 发布时间:2026/10/2 14:55:31 📁 来源:尧图网络
英文里有个很扎心的说法hindsight is 20/20事后看问题永远一清二楚。但最近这个英文词在国内技术圈的搜索量突然涨起来圈里聊到它指的不是人生哲理而是两个颇有分量的AI项目——一个是DeepMind开源的可视化调试工具Hindsight另一个是强化学习领域绕不开的名字《Hindsight Experience Replay》HER。我从算法复现和模型可视化两条线都摸了一遍这篇就以HER为主线把从原理到落地中间那些文档里不写的事情一次讲透如果你本来是冲可视化工具来的可以直接跳到第六节。1. hindsight成了搜索热词它其实是两个AI项目的共同名字1.1 这个词的原意和AI圈的使用方式hindsight直译是后见之明对应的中文俗语是事后诸葛亮。英文里最常用的一句是hindsight is 20/20二十二十指的是视力表上的满分意思是回头看的时候一切都清清楚楚。这个味道很符合程序员的日常——模型不收敛时你回头翻训练日志总能找到一堆当时就该注意的信号。所以很多项目复盘把hindsight挂在嘴边。真正让这个词单独成为搜索热词的原因是AI圈里出现两个正式以它命名的项目强化学习算法论文《Hindsight Experience Replay》2017年在arXiv上放出2018年正式发表作者是当时OpenAI的Andrychowicz等人。这套方法解决的是稀疏奖励问题让机器人控制器在没有每一步都给分的条件下也能学会目标条件任务。DeepMind在2022年开源的深度学习可视化调试工具HindsightDLVMDeep Learning Visualization and Debugging。它面向CNN模型让你在浏览器里点选神经元查看激活、权重和嵌入。你如果在搜索引擎里输入hindsight会同时看到这两个方向的内容。这也是我决定把它们放到一篇里讲的原因词是同一个但一个偏算法、一个偏工具两者解决的问题完全不同。1.2 这篇的主线为什么选HER我个人的判断是HER和当前具身智能、机器人操作的节奏贴得更紧。大模型把任务理解做得越来越强之后真正卡脖子的反而是低层控制怎么从稀疏反馈里学出来。HER是这个方向上绕不开的一课。所以正文前五节都围绕HER展开包括它的动机、机制、代码实现和踩坑记录。可视化工具Hindsight会在第六节单独给一套上手路径不占主线。2. 稀疏奖励下的学习死局HER想解决的到底是个什么问题2.1 一个简单的例子看清稀疏奖励的恐怖机械臂末端要从左边挪到右边一个点。完成判断末端与目标的欧氏距离小于0.05米给1分否则给0分。中间过程全是0。传统强化学习在这种任务上策略在巨大的连续动作空间里随机试。假设每次尝试成功概率是p平均需要的探索次数是1/p。连续空间里p通常小到让这个期望次数远远超出训练预算。更麻烦的是奖励恒为0时梯度估计失去方向策略更新等于白做训练曲线就是一根水平线。你可能觉得把距离倒数的奖励给上不就行了吗这是最常见的思路也就是reward shaping。但它有代价需要人工设计距离函数、调权值对不同尺度不同单位都很敏感。更坑的是agent会学会打擦边球——靠来回摆动末端刷距离分的hack策略到达成功率反而上不去。2.2 已有的几类解法为什么都不够顺手我把常见思路分成三类。第一类是上面说的奖励塑形问题在于领域知识成本高容易引入主观设计带来的偏置。第二类是内在奖励比如根据好奇心给没见过状态的奖励。这对探索有帮助但副作用同样明显机械臂会为了制造新状态拼命晃动姿态失控。第三类是多任务预训练先在一堆相似任务上练一个基础策略再迁移。问题在于任务集本身是昂贵的人工设计而且一旦目标分布和预训练分布偏离迁移效果立刻打折。HER的思路完全不在这些赛道上。它不做任何奖励函数改造不额外加探索激励就是改动经验回放这个环节把失败轨迹重新当作另一个目标的成功轨迹来学习。训练算法还是那个算法网络结构基本不用动。2.3 事后看问题在这里不是鸡汤是数学前面说的hindsight is 20/20放在这里有了具体含义一条轨迹在预设目标上失败了但这条轨迹本身不是垃圾它把如何从起点到达某个实际位置这个控制信息完整记录了下来。如果我们把问题改成如何到达那个实际位置那么这条轨迹就是一个成功样例。所谓后见之明就是等episode跑完、看清了所有状态之后再去给这条轨迹找一个它能成功回答的问题。HER把这句话落成了可执行的样本生产流程。3. HER机制拆解把失败轨迹重新标记成成功样本的完整流程3.1 先把任务翻译成目标条件形式HER面向的是goal-conditioned策略也就是策略不是π(a|s)而是π(a|s, g)。这里的g是目标可以是一个三维坐标、一个关节角度向量甚至一张目标图片。要让一个单目标任务能用上HER得先定义两个东西状态投影函数f(s)把完整状态s里与目标达成有关的部分取出来。机械臂任务里f(s)就是末端三维坐标抓取任务里还包括手指开合状态。目标空间goal g和f(s)必须在同一个空间里。环境返回obs里的desired_goal和achieved_goal都属于这个空间。奖励函数写成稀疏形式R(s, a, g) 1 如果 ||f(s) - g|| ε否则 0。这里的ε是完成阈值。3.2 重标定流程自己给自己补问题整个流程是先正常跑一个episode得到一串原始transition每个transition都带着原始目标g。这个episode对g大概率是失败的。结束后立即执行重标定从本episode中按某种策略挑出一个或多个假目标g。对每个transition重新计算奖励r R(s_{t1}, g)。把(s_t, a_t, r, s_{t1}, g)作为新transition写入replay buffer。原始transition原样保留不是替换是补样本。伪代码长这样def relabel_episode(episode, K4, strategyfuture): for t, (s, a, r, s_next, g, ag) in enumerate(episode): replay_buffer.add(s, a, r, s_next, g, ag) for _ in range(K): fake_goal choose_fake_goal(episode, t, strategy) fake_reward sparse_reward(s_next, fake_goal) replay_buffer.add(s, a, fake_reward, s_next, fake_goal, ag)其中choose_fake_goal根据策略决定future策略就是从当前时刻之后的状态里随机选一个achieved_goalfinal策略就是终点状态random策略就是从整条episode里随机选一个状态。K是每条轨迹补的假目标数量原论文实验里K4是常用起点。3.3 把失败标成成功到底算不算作弊这是所有人第一次看到HER时都会问的问题。直接回答不算前提是只替换goal不替换state。原因要从目标条件价值函数的角度看。Q(s, a, g)的定义是给定状态s、动作a、目标g往后能拿到多少奖励。当一条轨迹没有达到g、但确实达到了g时把这条轨迹放进以g为目标的样本桶里标签r1是符合物理事实的agent确实在某个时刻到达了g。如果把这个到达g的完整控制序列学到手下一次给它一个g附近的目标它就能用上这套动作。类比一下驾校倒车入库教练让你停1号库你倒歪了但正好把车停正了2号库。教练如果说这次咱们就当练2号库你照做并且这次确实把车完整停在2号库里这个经验是真实有效的。教练不会把一坨其他失败记录全部标成2号库成功只标那些真的抵达了2号库的记录。HER同理。绝对不能做的是替换state或替换action那是伪造物理事实。只改这个问题问的是什么是重标定的边界。这个边界想清楚后面的实现就不容易跑偏。3.4 四种目标采样策略为什么future最常用原论文对比了四种选假目标的策略策略做法效果final用episode最后一个状态作为g简单但只覆盖终点中段目标太少random从整条episode里随机选状态多样性高但噪声偏大future从当前时刻之后的状态里随机选时序因果自然目标从近到远梯度清晰episode从完整轨迹中随机选一次random的形式化版本实验结论是future胜出。原因很直观future选出的目标在未来确实会被达到重标定后的成功不是虚构的而且不同t配对的假目标靠后程度不同给Q函数提供了从易到难的信号层次。final虽然也是真实到达但每个transition都配同一个远处的目标中间大量步骤离这个目标很远奖励仍然稀疏。random的话可能出现选出的假目标在当前步骤之前就已经实现了的尴尬情况因果方向混乱。4. 最小可复现方案把HER接进现有RL代码的实操细节4.1 环境与观测设计的前置准备第一次复现HER时不要一上来就换复杂环境。先跑OpenAI Gym Robotics里的FetchReach这是机械臂到达任务动作空间4维状态空间大概20多维度目标是三维坐标。关键点有两个环境返回的obs里必须同时有desired_goal和achieved_goalachieved_goal就是f(s)的实时值我踩过的一个具体坑在自定义环境里只写了desired_goal忘掉在step()返回时维护achieved_goal。HER重标定需要的是这条轨迹实际到达过的目标没有achieved_goal就无从选假目标。如果不能直接用现成环境建议在自定义环境里用这样的接口def step(action) - (obs, reward, done, info)其中obs包含observation、desired_goal、achieved_goal三个keyinfo里再放一份achieved_goal方便episode结束时按时间切片取。4.2 网络结构的组织方式原文用DDPG做底层算法Actor输入是s拼接g输出动作Critic输入是s拼接g再拼接a输出Q值。隐藏层用3层256的MLP就够了不需要额外花哨结构。换成SAC时把熵温度系数调低一点再跑比如0.05到0.1之间否则假目标会让策略过度随机。我实际对比过同样配置下SAC的熵温度默认值0.2时FetchReach效果明显变差调到0.05后和DDPG结果接近。这个现象在论文里很少被提但实操中很影响收敛速度。4.3 Buffer里必须存什么replay buffer的每个transition要存完整六元组(s, a, r, s_next, original_goal, achieved_goal)。要注意的是这条transition本身的reward是利用original_goal算出来的原始样本按原样存一份重标定时用的是achieved_goal生成假目标再用sparse_reward(s_next, fake_goal)重新算奖励。所以achieved_goal是必须存的关键字段。buffer容量我建议至少1e6因为HER的价值建立在大量轨迹、大量重标定之上buffer不够大重标定样本多样性就起不来。4.4 采样和训练循环怎么配合训练主循环不需要大动核心工作都集中在episode结束后的重标定环节。给一段可以直接改到现有代码里的逻辑def collect_and_relabel(policy, env, her_kwargs): obs env.reset() episode [] while True: action policy.select_action(concat(obs[observation], obs[desired_goal])) next_obs, reward, done, info env.step(action) ag next_obs[achieved_goal] episode.append({ s: obs[observation], a: action, r: reward, s_next: next_obs[observation], g: obs[desired_goal], ag: ag }) obs next_obs if done: add_her_episode(episode, replay_buffer, Kher_kwargs.get(K, 4), strategyher_kwargs.get(strategy, future)) break batch replay_buffer.sample(batch_size) # 下面走标准DDPG/SAC更新不需要区分是原始样本还是重标定样本 update_policy(batch)注意训练更新时原始样本和重标定样本混在一起随机采样即可不需要做任何标记区分。它们都被当作以目标g为条件的经验看。我在早期实现里犯过一个错误给重标定样本加了个特殊flag更新时单独处理。后来发现完全多余还让代码变乱。4.5 一组能跑通FetchReach的参考配置下表是我在几个机子上反复验证过的起点配置不同环境再按需调整参数参考值备注K4future策略为主buffer size1e6充分利用重标定多样性batch size256常用actor lr / critic lr1e-3 / 1e-3Adampolyak0.95DDPG目标网更新reward阈值ε0.05按具体任务的坐标尺度调整每固定步数eval每50个episode用固定目标集测真实成功率提示跑通FetchReach之前不要动K和reward阈值两个自由度先原样复现再谈优化。两个一起动出了问题很难定位。5. 实操中的坑K值、目标空间和奖励函数最容易翻车5.1 K值不是越大越好先绕开这个直觉K的作用是给每条真实轨迹额外生成K条重标定轨迹。直觉上K越大样本越多效果应该越好。但实际不是。K变大之后replay buffer里对假目标成功的样本比例被推得很高Q网络会被成功信号包围出现过度乐观策略反而没那么激进。我做过一组对比K4到K8成功率略有提升K16FetchReach训练曲线先快后崩最后真实成功率不如K4。原因就是假目标样本占比太高模型把容易到达的目标和任何目标混为一谈。稳妥套路是从K4起步如果任务探索实在太难再提到8。5.2 目标空间不对齐等于白做HER最容易被忽略的前提是goal space必须和achieved state space完全一致。也就是说你要求机械臂到达三维坐标目标那achieved_goal也必须是从状态里提取出来的末端三维坐标不能把状态向量整个丢进去当achieved_goal。我踩过的一回状态是11维关节角和速度我图省事直接把它当achieved_goal假目标也变成11维随机向量结果重标定后的奖励几乎全是0训练从头到尾没有正向信号。另一个细节是归一化。如果goal空间是多维混合比如坐标速度同时进网络量纲不同会让Q函数训练失衡。建议只对影响目标达成判定的子空间做归一化别为了凑特征把其他物理量都塞进去。5.3 奖励函数的几种写法效果差别很大HER的外号是不需要稠密奖励但奖励阈值本身很讲究。阈值ε设太严比如0.01重标定后的成功样本比例也低正信号稀少设太松比如1.0目标范围太宽学出来的策略精确度差。常见做法是先确定距离尺度再设εFetchReach尺度下0.05就够而抓取任务大约在0.05到0.1之间。另外有些人喜欢用负距离当奖励即r -||f(s) - g||这本质上是稠密奖励HER的代码照样能跑但含义变了每个状态都提供连续梯度重标定只是锦上添花。我的建议是复现阶段坚持用0/1稀疏奖励先搞清楚HER本身的机制再用稠密版本做工程优化。否则你根本说不清训练效果是HER的功劳还是奖励塑形的功劳。5.4 收敛判断请认准真实成功率而不是Q值HER训练中一个非常容易骗到人的信号是Q值。因为buffer里被重标定过的样本占了不小比例很多transition的标签本身就是成功Q网络学出来的均值必然偏高。我第一次跑时就犯过这个错Q值曲线漂亮地爬到0.9我以为策略已经成熟拉到真实环境一测成功率不到两成。正确做法是固定一组测试目标可以不参与训练每隔若干个episode跑一次无探索策略的eval只看真实目标达成率。机器人的控制器里如果达到率能稳定在90%以上再谈将策略导出部署。5.5 与SAC、TD3配合时的现实调整HER本质是回放层的插件换底层算法原则上都可以。但每个算法都有自己的脾气。SAC的问题前面说过熵温度要调低TD3的话target policy smoothing会给目标加入轻微噪声这对重标定目标稍微有点干扰我观察到表现为收敛变慢但更稳。动手改造前建议先在DDPGHER上跑通FetchReach再换其他组合。6. 另一个hindsightDeepMind开源可视化调试工具上手6.1 为什么聊完HER还要专门说它因为我在各个社区看到很多人搜hindsight其实是在找DeepMind开源的那个可视化工具。它解决的是另一个层面的事后视角模型训完几十个卷积层、几千个feature map黑箱一样。Hindsight DLVM让你在浏览器里把网络一层层剖开看激活和参数这不就是给模型做事后复盘么。两个项目叫同一个名字一个在训练时用一个在训练后用正好互补。6.2 这个工具到底能做什么官方定位是深度学习可视化与调试工具主要面向CNN。实际用下来核心能力有这么几块激活可视化输入一张图片逐层查看每个卷积通道的feature map哪个区域被激活一目了然神经元响应分析选中一个神经元工具会标出让它响应最强的输入区域常用来找模型的关注点是不是人类觉得合理的位置嵌入可视化把中间层的特征向量投影到低维空间看类别是否聚集、边缘是否清晰参数与权重分布快速检查有没有神经元死亡、权重是否退化。它和matplotlib最大的区别是交互性不是输出一张静态图而是在浏览器面板里点击、拖拽、缩放。做课程演示、论文图片、以及给同事解释为什么模型把猫认成狗的时候特别好用。6.3 上手的工作流半小时能跑通我没法把每一步命令都写在文章里因为官方仓库的格式和依赖一直在更新以仓库README为准最稳。整体流程是三步准备一个训练好的CNN模型先用官方示例模型跑通全流程再换自己的模型把模型和样例数据导出成工具支持的格式常见的是HDF5一类的结构导出脚本在仓库里都有现成的启动本地服务浏览器打开面板导入图片开始交互式查看。提醒一个容易卡住的点别拿超大模型直接试。这个工具目前对着视觉CNN优化得最好想用它看几百亿参数的生成模型基本会把自己卡死。想看Transformer的注意力还是换专门的可解释性工具更合适。装了之后第一件事是拿官方Demo模型熟悉操作不要急着把生产模型塞进去。6.4 两个hindsight在一个项目里的组合玩法我自己试过的一个组合思路用HER训练一个机械臂到达任务的策略网络训练完成后把critic网络的Q值在goal空间里网格化画成热力图。手上有Hindsight这类可视化工具之后这个分析流程会更顺先看策略输入侧的激活情况再看critic输出的Q值等高线。你会发现HER训练出来的critic对可达目标区域有清晰的渐变对不可达区域Q值迅速塌掉。这就是算法和工具在同一个项目里各司其职的样子。如果你也是从后见之明这个词进来的我建议的顺序是先复现HER的FetchReach实验再玩可视化面板。两件事都做完你大概就能理解为什么事后看清在AI里不是一句人生格言而是一整套工程手段。
上一篇/下一篇内容由系统自动关联 返回资讯列表 →