ARTICLE · INTELLIGENCE

战地情报 · 详情页

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

HER算法解析:用事后经验重标注解决稀疏奖励难题

HER算法解析:用事后经验重标注解决稀疏奖励难题 训练一个目标条件强化学习智能体时最让人头痛的问题往往不是网络结构不够深而是“根本没有正反馈”。hindsight直译过来是“后见之明”说的是人类做错一件事之后回顾时突然明白“原来刚才那个步骤才是关键”的那种体验。把它放进强化学习里就成了一个非常实用的算法思路Hindsight Experience ReplayHER。这套方法解决的是稀疏奖励下“怎么都学不会”的经典困境让机械臂抓取、位姿控制这类长期任务在几乎不依赖人工设计奖励函数的情况下也能稳定收敛。不管你是刚入门的RL方向学生还是已经在跑仿真环境做决策控制的工程师这篇文章都值得你花十分钟看完我会把原理、代码、实验和踩坑经验一次讲透。1. 为什么要做“事后复盘”式的学习——从一个失败的抓取任务说起1.1 稀疏奖励环境的经典困局先想象一个特别具体的场景机械臂要从桌面上把一个方块抓起来放到左边的目标位置。环境只在任务成功那一刻返回奖励0其余每一步都是-1。这种设置叫稀疏奖励听起来很简单但对算法来说几乎是灾难。假设机械臂动作空间的维度在4以上每一步动作都是连续值随机探索命中的概率微乎其微。训练几十万个时间步缓冲区里存的全是“失败经验”critic网络学到的Q值对任何状态-动作对都趋近于一个常数actor网络完全没有梯度方向可用。这种状态我见过太多次了loss数值看起来在跳但成功率曲线平得像一条直线。很多新手第一反应是“把奖励函数改密”。比如用欧氏距离作为稠密奖励到目标越近分数越高。这个思路看似合理但实际效果往往也不尽如人意。原因是现代机器人任务的状态空间远非光滑距离本身体现不了“任务是否可持续推进”而且稠密奖励的尺度稍有问题训练出来的策略就会表现出一种诡异的状态机械臂把手伸到半路然后卡在那里不动。因为每个位置的奖励都差不多网络找不到继续往前走的理由。HER的思路不是改奖励而是改目标——让智能体学会“给自己找台阶下”。1.2 人类天生就会“事后合理化”你回忆一下自己学投篮的经历。第一次投球砸在篮板上弹飞了离篮筐很远。正常人不会只是闷头再试几百次而是会在中途调整刚才那个球明显高了我下次压低一点刚才那个球偏右我下次往左一点。这里有一个非常关键的认知行为你会把“投进篮筐”这个大目标在失败之后拆解成几个阶段性的小目标并给“球飞得更接近篮筐”这件事赋予一定意义。HER做的就是这件事只不过它把人类的“事后合理化”变成了数学上可操作的样本重标注机制。这个直觉非常适合写成算法。当智能体在某个episode里没有达到预设目标g时它其实“达成”了另一个状态——我们把这个状态记为g。既然智能体确实到达了g那为什么不能把这整条轨迹看成“以g为目标的一次成功尝试”呢原本无法给智能体提供任何学习信号的一步失败动作在换了一个目标之后就变成了一条带有正反馈的有效经验。这就是hindsight这个标题背后真正的技术内核也是HER算法在样本效率上远超普通DDPG的根源。2. 核心原理拆解HER如何把失败翻转成成功的阶梯2.1 目标条件策略与经验回放的基础要把HER讲清楚必须先说清楚它的适用对象。HER不是通用的策略优化算法它是针对目标条件策略goal-conditioned policy设计的经验回放技巧。所谓目标条件策略就是策略的输入从单纯的状态s变成(s, g)的组合其中g是当前任务的目标描述。比如机械臂抓取任务里g通常是一个包含物体位置的向量在导航任务里g就是目标坐标。策略输出动作a环境返回新的状态s和一个根据目标计算的奖励r r(s,a,g)。这时整个学习过程基本依赖经验回放experience replay。训练过程中智能体探索收集到的四元组(s, a, r, s, g)会存进一个循环缓冲区。普通的off-policy算法比如DDPG、SAC、TD3都是从这个缓冲区里随机采样一批经验然后更新actor和critic。问题来了如果缓冲区里的经验全是负奖励无论采样多少次梯度都是无用的。HER做的就是在这个“采样之前”的环节插入一个目标重标注操作。2.2 事后重标注的完整流程假设一条transition是(s_t, a_t, r_t, s_{t1}, g)。原始目标g我们没有达到于是奖励r_t-1。现在观察s_{t1}里包含的“实际达成状态”g_achieved你可以把这个状态理解成“智能体这一瞬间真正到达的位置”。HER从这个真实状态出发把它当做一个新的伪目标g g_achieved来替换原始目标。接着重新计算奖励r 0因为以g为准智能体确实成功了一次。这一步在数学上完全成立状态转移(s_t, a_t → s_{t1})是环境真实发生的没有伪造只是我们对“这段经验原本的目标是什么”这件事做了重新解释。换完目标之后缓冲区里就多了一条正奖励的样本(s_t, a_t, 0, s_{t1}, g)。这条样本告诉策略“当你处于状态s_t时如果你曾经想去的地方是g那你刚才采取的动作a_t就是一个正确动作因为它的确把你送到了目标所在的位置。”在代码层面这个操作往往配合一个参数k使用对于一条原始transition额外生成k条HER重标注样本也就是说缓冲区里同时存着原始经验和k条“事后经验”。这里有个容易忽略的细节重标注不只是把目标g换掉还要同步修改observation向量中所有与目标相关的分量尤其是当环境直接把goal拼进了观测向量时比如OpenAI Gym的Fetch环境observation里就同时有desired_goal和achieved_goal你务必要把desired_goal那块替换成g。我见过很多复现失败的项目就是漏掉了这一步导致网络输入的前后目标不一致训练直接崩掉。2.3 伪目标该从哪里选future策略为什么更聪明既然伪目标g来自“实际达成状态”那具体应该选哪个时刻的达成状态呢OpenAI论文里列出过四种常用策略final取这个episode最终时刻的状态作为伪目标future取当前时刻之后、episode结束之前的某个随机时刻的状态episode从当前episode里任意位置随机选一个状态random从整个状态空间里随机采样一个状态作为伪目标。实际测试下来future策略是默认最优解。直觉上也很容易理解某个transition在时间t时距离t1时刻的状态往往比最终状态更“接近”用nearby的状态当目标相当于给智能体设计了一个由近及远的课程学习。比如机械臂正在往右移动如果以“下一秒的位置”作为目标那这次移动就是完全正确的正反馈非常自然。而如果粗暴地取final状态作为目标有些离最终位置太远的早期transition会被标成失败反而丢失了有效梯度。random策略就更不推荐了它基本等于让智能体猜测一个和自己当前动作毫无关系的目标负反馈太多收敛又慢又乱。3. 实操手把手实现一个HER并跑通FetchPickAndPlace3.1 环境与基线代码选择如果你打算自己复现HER我强烈建议从OpenAI Gym的Fetch系列环境入手。这个系列一共有Reach、Push、Slide、PickAndPlace四个任务每个都提供了desired_goal和achieved_goal的观测结构奖励函数本身就是标准稀疏设置成功给0失败给-1。尤其是PickAndPlace这个任务包含移动、抓取、放置三个子阶段非常能体现HER的威力。代码基线方面首选OpenAI baselines仓库里的her模块。虽然baselines已经考古级了但它的her实现逻辑非常纯粹和论文对应得最好适合拿来理解和改造。如果你更偏好PyTorch生态可以参考rlkit或者一些由社区二次封装的项目。用stable-baselines3的版本要小心它的HER在2.0之前有后来接口变化较大很多老教程已经对不上了。我在实际项目里是直接把baselines的her逻辑解耦出来接到自己写的SAC上用的后面我会详细讲原因。3.2 核心代码逐段拆解HER的整体流程可以概括为收集一整段episode然后对里边的每一条transition都调用重标注函数生成k条额外样本统一塞进回放缓冲区。这段逻辑写出来非常简洁我直接贴一份核心伪代码def process_episode(episode, k4, strategyfuture): # episode 是一条完整轨迹包含 obs、actions、rewards、achieved_goal transitions [] for t in range(len(episode.actions)): # 原始样本先保留 transitions.append((obs[t], actions[t], rewards[t], obs[t1], goals[t])) # HER 重标注生成多个伪目标 for _ in range(k): future_idx random.randint(t 1, len(episode.actions)) new_goal episode.achieved_goal[future_idx] # future 策略 new_reward 0.0 # 因为新目标已经达成 # 注意这里必须同时替换 obs 里 desired_goal 的部分 new_obs replace_goal_in_obs(obs[t], new_goal) new_next_obs replace_goal_in_obs(obs[t1], new_goal) transitions.append((new_obs, actions[t], new_reward, new_next_obs, new_goal)) return transitions这段代码看起来短但有几个关键点必须强调。第一原始那条样本不能丢HER是“额外”生成样本而不是“替换”样本。第二future_idx要保证在t之后这是future策略的核心。第三对obs做替换时不只是改一个字段而要把所有包含goal信息的通道全部替换这在自定义环境里尤其要注意。等所有新样本进入回放缓冲区之后训练流程就和普通的DDPG或SAC完全一样了。缓冲区每次采样一批数据actor根据(s, g)输出动作critic根据(s, a, g)评估动作价值然后反向传播更新。由于缓冲区中同时有原始经验几乎都是负奖励和HER经验几乎都是正奖励网络终于能看到“什么样的动作往前一步就成功了”从而学到真正有区分度的Q函数。3.3 超参数与网络结构的关键配置HER本身参数不多但每一个都对训练结果影响很大。首先策略网络论文和baselines用的都是三层全连接MLP每层256个神经元激活函数是ReLU。这个配置对于Fetch环境来说足够了不需要上Transformer或者LSTM。然后是基础强化学习算法的参数。DDPG里面actor学习率1e-3critic学习率1e-3折扣因子0.98回放缓冲区大小1e6。这里有一个容易被忽略的地方因为HER会产生大量的额外样本缓冲区很快就塞满了如果容量太小新样本会把早期的均匀分布样本挤出去反而破坏多样性。所以容量建议往大了设。接下来是HER专属参数k。k0等价于没加HER的普通经验回放k4是论文和大多数项目的默认值。简单解释一下k的含义缓冲区内HER样本与原始样本的比例约为k/(k1)k4意味着缓冲区里大概80%的样本都是重标注过的。这个比例不能太小太小梯度淹没在负奖励里也不能太大太大目标分布严重偏离原任务本质上是另一个任务了。我的经验是先从4开始跑如果训练曲线平稳但收敛慢可以试试提到8如果训练过程震荡明显降回2。另外在绝大多数HER实现里重标注时会按一定概率保留原始目标。baselines默认的做法是对每条transition以20%的概率不重标注保持原始目标不变其余80%做future重标注。这个概率挺有讲究保留一些原始目标样本是为了让策略不要完全偏移到“随便给自己定目标”的路径上去。4. 实验结果与关键现象解读4.1 bitflip玩具环境带来的启发想快速理解HER为什么有效一定要先跑一下论文里的bitflip环境。这个环境的目标是一个N维二进制向量动作是翻转某个维度上的位每一步结束后检查目标向量是否和当前向量完全一致一致奖励0否则-1。任务本身是离散的动作空间也很小理论上只要搜索N步就能找到答案。但当N达到20以上时纯随机探索的成功概率是2的负20次方级别基本等于零。在这个环境上加与不加HER的差异是触目惊心的。普通DQN训练几十万个episode成功率依然是0而加上HER之后策略可以稳定地在十几步内完成翻转成功率接近100%。我后来在复现时打印过重标注样本在缓冲区里的占比发现真正让算法产生进展的就是那些“看似成功实则是伪目标”的样本。这给了我一个明确的信心HER带来的不是某个网络结构上的优势而是数据层面纯粹的效率提升。4.2 Fetch任务上的成功率曲线解读在FetchPickAndPlace这样更贴近真实机械臂的场景里HER的效果照样明显。我自己复现时用DDPG加HER训练150万时间步PickAndPlace任务的成功率可以稳定达到80%左右。而纯DDPG不加HER同样训练量下成功率几乎趴在0上。这里有个值得深思的现象Fetch的观测空间其实不算高维动作空间也就4维为什么就这么难学原因还是那个老问题——稀疏奖励下梯度消失。无论机械臂动作多接近成功只要没有真正抓到物体并放到目标位置奖励就是-1。Q网络完全分辨不出“多靠近一步”和“原地乱转”的区别。HER把任务切成了一串连续的小目标比如“把手移动到物体上方”“握住物体”“带着物体移动到目标区域”每个小目标都有明确的成功判据这正是它能突破这个困局的根本原因。4.3 和不同强化学习算法组合时的差异HER本身不是策略更新算法它是一种数据处理方式理论上可以和任何off-policy算法组合。但在实际工程里组合效果差异很大。DDPGHER是我验证过最稳的组合主要因为DDPG对超参数不敏感、实现简单、训练稳定。SACHER也很可行样本效率会比DDPG高一些但因为SAC有熵温度参数总调节点更多跑起来需要多留心。PPO这类on-policy算法配HER就很尴尬了。PPO要求用当前策略采样的数据来更新不能像off-policy算法那样重复利用缓冲区的历史样本。HER生成的伪目标样本本质上是对历史数据的再利用PPO根本吃不到这个红利强行加HER反而会让目标分布和当前策略的采样分布失配训练曲线更容易崩。所以如果你原本用的是PPO想改善稀疏奖励问题建议干脆换成DDPG或SAC再配合HER而不是硬在PPO里塞HER。5. 常见问题与排查技巧实录5.1 为什么我的HER不加DDPG就学不动HER算法对底层算法的依赖比很多人想象的要大。核心原因在于重标注只改变样本不改变策略更新的目标和方式。如果你选了一个本身就不适合探索的算法比如方差极大的策略梯度算法或者对其更新步长极其不敏感的网络那么就算样本里全是正奖励学习依然可能不稳定。最稳妥的做法是先跑通baselines里DDPGHER的标准组合确认环境、观测、目标维度都对得上再一步步换成其他算法这样可以有效隔离问题来源。5.2 伪目标比例k到底怎么调调k的原则可以从一个最基础的观察出发k过小时正样本太少网络长期被负样本淹没k过大时目标分布太“散”智能体虽然总能成功但学到的策略却不一定针对原来的目标。极端情况比如k16训练出来的策略在原始目标上甚至可能不升反降。我通常在toy任务上先用k4看成功率曲线有没有起色再根据曲线形态微调。如果曲线前段平坦后段陡增说明重标注强度合适如果曲线从头到尾都震荡我会调低k并检查是否某些伪目标离得太远。5.3 奖励太密反而坏事的场景HER的思路是重设目标而不是加稠密奖励。一个常见的自作聪明操作是把奖励函数改成负的欧氏距离认为这样能帮HER一把。结果往往很反直觉越密的奖励策略越容易陷入局部最优。比如负的欧氏距离在中途可能给出-0.4这样的分数而向错误方向移动也可能只给-0.6差距太小Q网络很难学好偏好。再一个密集奖励容易掩盖成功事件系统成功一次得到0分但中途距离0.05和距离0.01几乎没区别策略也就没有动力走完最后一步。如果你用了HER最好保持环境的原生稀疏奖励不要随意替换。5.4 复现时最容易踩的代码坑我自己踩过的一个比较隐蔽的坑是在处理obs向量时忘记替换其中的desired_goal分量。当时训练出来的策略全场僵尸化后来逐帧打印obs才发现模型输入里的目标和环境reward计算用的目标不一致导致出现“同一个动作被网络认为是成功、环境却判定失败”的诡异局面。另一个常见问题是在自定义环境里做重标注时没有保证achieved_goal在状态增广后的索引位置正确。建议在实现HER之前拿一个小批量样本连续打印obs、g、g_achieved和reward确认数据通道一一对应再开跑。还有一个小经验HER对随机种子的敏感度比普通算法高种子的微小差异可能带来成功率10%以上的波动。跑实验时一定要固定种子最好每个配置跑三五个不同种子取中位数否则还真不好判断参数改动是有效还是运气。写在最后的一点体会我个人在实际项目中感受最深的一点是HER并不是一个“加了就完事”的银弹它把智能体从“只能向成功学习”解放成了“可以向任何到达过的状态学习”但前提是你能给智能体提供足够多样且与任务相关的状态分布。如果你用的环境状态分布本身就很窄或者状态里包含大量无关信息HER的效果会打折扣。这时候可以考虑把目标增强、课程学习或分层强化学习叠加上去。另外从工程角度看HER的实现难度不算高但它对你的数据流设计有很明确的要求——目标替换必须贯穿obs、reward和采样索引任何一步漏了都会带来诡异的表现。如果你正准备在自己的项目里解决稀疏奖励问题我建议你先从FetchPickAndPlace加DDPG跑通标准流程再去挑战更复杂的长期任务。
RELATED READING

延伸阅读

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