首页 / 资讯中心 / 文章详情

后见经验回放HER:破解稀疏奖励难题的强化学习算法

后见经验回放HER:破解稀疏奖励难题的强化学习算法 ★ FEATURED ARTICLE
“hindsight”这个词日常翻译叫“后见之明”或“事后聪明”我们总是嘲笑自己是“事后诸葛亮”。可在强化学习里这种“事后诸葛亮”反而是破解稀疏奖励难题的一把钥匙它就是Hindsight Experience Replay后见经验回放简称HER。我第一次看到这个算法时心想这不就是“强行给自己找补”吗但真正跑通实验后才发现这一招实在太聪明了。这篇文章写给谁给正在调稀疏奖励环境调到怀疑人生的强化学习研究者给做机器人抓取、机械臂控制、导航决策的算法工程师也给刚入门Goal-Conditioned RL、想找一个能快速出效果的经典算法的同学。我会从算法动机讲起拆解原理和四种目标重标注策略给出基于PyTorch和DDPG的可运行实现方案再把我实际调试中踩过的坑和排查思路全部整理出来。读完你能直接照着复现一个HER训练流程也能避开我在Fetch系列环境上撞过的墙。1. 从“事后聪明”到强化学习HER要解决什么问题1.1 稀疏奖励为什么让机械臂学会“推箱子”这么难想象一个机械臂任务把桌面上的物体推到指定位置。奖励设定很干脆——到达目标位置附近给0否则每一步都是-1。看起来符合直觉但这个设定让训练几乎无从下手。原因是绝大多数状态动作对都拿到同样的惩罚或零奖励策略网络收到的梯度信号极其微弱甚至根本分辨不出哪个动作比哪个动作更好。这就是稀疏奖励问题Sparse Reward Problem。它和Atari游戏那种每帧都有分数反馈的密集奖励环境完全不一样。在稀疏奖励下智能体必须靠随机探索“碰巧”接近目标才能获得第一次正反馈再沿着这条路径慢慢学。问题是6自由度甚至7自由度的机械臂动作空间维度极高随机探索恰好碰到目标位置的概率接近零训练自然就卡死了。我实测过FetchReach这类单步接近任务不用HER的情况下成功率经常长时间在10%到20%徘徊策略基本就是在原地乱抖。这不是网络结构的问题也不是学习率没调好是样本本身没有可学习的信号。稀疏奖励把绝大多数经验变成了“无效经验”这才是根本矛盾。1.2 关键思路用“事后目标”替换“原定目标”HER提出了一种反直觉的解法既然这条轨迹没有到达“原定目标”那就换个思路——把轨迹里“实际达到的状态”当作事后目标重新给经验打标签并配上一个正奖励。举个例子任务目标是“把物体推到坐标(1.0, 0.5)”但这次轨迹实际把物体推到了(0.8, 0.6)。传统算法认为这是一次彻底的失败轨迹直接丢弃。而HER认为虽然没完成原任务但这条轨迹完整地展示了“从初始位置出发、经过一系列动作、最终把物体推到(0.8, 0.6)”的过程。那我就把(0.8, 0.6)当成一个新的目标重新计算这批经验的奖励。这样一来原本“失败”的轨迹就变成了“成功到达(0.8, 0.6)目标”的优质正样本可以正常用于训练。这就是“后见之明”的本质不要执着于最初设定的目标g而是回头看看“这次你实际完成了什么”把完成的部分当作新目标去学习。这样一来智能体从每次尝试中都能提取出可学习的经验相当于自监督地给经验库打上了新标签把无用轨迹变成了有用轨迹。1.3 生活类比投篮和找钥匙理解HER最好的方式是两个生活化类比。第一个是投篮。如果规则是“只有投中三分才给奖励”一个新手练一万次命中率可能还是零因为他根本不知道自己每次出手的动作参数和球飞行轨迹有什么关系。但如果换个思路每次出手之后不问“进没进”而是问“球飞向了哪个位置”然后把“把球投到这个位置”当作目标他就立刻能从每一次出手中学到“发力大小、出手角度与落点之间的映射关系”。积累足够多这样的经验后他就能逐渐逼近三分线。HER做的事就是这个——把每次失败出手变成一条“成功把球投到某处”的经验。第二个类比是找钥匙。你在房间里找一串钥匙最终没找到但你记住了自己确实搜过沙发底下、检查过书桌抽屉、翻过书架第三层。下次要完成“找到书架第三层的某本书”这个任务时之前那些“没找到钥匙”的经验依然有效因为它们展示了这些位置的可达性和路径。HER就是把“找钥匙失败”中的位置信息重新编码成“到达某个位置”的成功经验。这两个类比在给团队讲方案时特别好用。一旦理解了这个思维转换后面四种重标注策略和代码实现就都顺理成章了。2. 算法原理拆解目标重标注的四种策略与奖励设计2.1 Goal-Conditioned RL基础设定HER运行在目标条件强化学习Goal-Conditioned RL的框架下。策略和价值函数的输入除了状态还多了一个目标g策略π(a|s, g)价值函数Q(s, a, g)目标g可以是目标位置坐标、物体位姿、也可以是更抽象的状态特征。每个episode开始时先从目标分布中采样一个g智能体根据当前状态和目标决定动作。轨迹中每一步都是一个五元组(s_t, a_t, r_t, s_{t1}, g)其中s_{t1}是转移后的状态。对于布尔型稀疏奖励常见设计是r_t(g) -1如果 |φ(s_{t1}) - g| ε r_t(g) 0如果 |φ(s_{t1}) - g| ≤ ε这里φ(s)是“实际达到状态”的提取函数比如机械臂指尖位置、物体中心坐标。也就是说只有当前状态和目标足够接近才给0其余情况全给-1。这种设计保证了只有真正达成目标的transition才会被当作正样本。HER正是在这个基础之上通过重标注目标来制造更多“达成目标”的transition。2.2 四种future策略final、episode、random、future怎么选HER论文里给出了重标注时如何采样“额外目标”的四种策略这是整个算法最核心的细节。我用表格先概括一下。策略名称采样方式优点缺点final使用episode最后一个状态φ(s_T)作为新目标简单几乎零成本当轨迹很长时与早期transition的距离太远目标过于困难episode从当前transition之后、同一episode内的状态中随机采样一个作为目标目标与当前状态有因果关联采样到的目标可能距离当前状态过远random从replay buffer中随机采样一个已探索状态作为目标覆盖范围广增加目标多样性目标可能与当前轨迹完全无关可达性差二次稀疏化future从当前transition之后的同一episode内随机采样一个状态φ(s_k), k t同时具备近期性、关联性、可达性需要等到episode结束后才能统一重标注不能实时入库我实战中用得最多的就是future策略。原因很简单它采样的目标状态在时间上一定出现在当前transition之后这意味着智能体在后续确实到达过这个位置这条经验对新目标是真实可用的。而且同一episode内的状态往往比较接近step与step之间的状态变化幅度有限目标不会太“跳脱”训练更稳定。相比之下random策略虽然目标覆盖范围大但经常采样出完全无关的位置导致一条原本还算正常的轨迹被贴上一个完全够不着的目标反而引入了大量噪声。final策略在短episode的任务里很好用比如FetchReach每个episode只有几十步最后一个状态和前面状态的距离不大。但像FetchPush这种几百步的长任务早期transition对应的事后目标离最终状态十万八千里学习效率反而下降。2.3 奖励函数重构与重标注时机重标注不是把每条transition的goal随机一换就完事。实际操作有两条铁律。第一原始经验必须保留第二重标注后的经验必须重新计算奖励不能沿用原来的奖励值。完整流程是这样的智能体跑完一个完整episode后把这个episode的所有transition暂存起来。然后遍历每一条transition对每一条额外采样K个新目标。每个新目标g都对应一个新的奖励r把(s_t, a_t, r, s_{t1}, g)作为新经验写入replay buffer。原始经验原封不动也写入。为什么要保留原始经验因为原定目标g才是任务真正关心的目标完全用事后目标替代原始目标策略会退化成“什么都能到达但不解任务要什么”。保留原始经验就是保留任务定义。为什么要用future策略而不是直接在训练时实时重标注因为future需要知道“当前transition之后的状态”这些状态在episode尚未结束前是不完整的。所以标准做法是episode结束后统一重标注再批量写入buffer。这个顺序千万别搞反我在早期代码里图省事逐transition重标注结果future策略采样不到后续状态和episode策略没区别成绩掉了不少。2.4 为什么这样能避免局部最优HER能有效本质上是把稀疏奖励问题转化成了相对稠密的问题。原来一个episode可能只有最后一两步才有正奖励重标注之后轨迹里大部分transition都能找到“某个可达目标”并获得0奖励。正样本比例从百分之几提升到百分之三四十甚至更高。用信息论的话说这叫扩大了有效监督信号。用工程的话说梯度信号密集了训练从“沙漠找绿洲”变成了“绿洲里修路”。但更深刻的意义在于HER把“一个难任务”拆成了“许多个不同难度的目标”去学习。智能体先学会“到达任意可达状态”这是相对容易的在积累了大量“到达各种位置”的经验后再学习“到达指定目标位置”时已经有了很好的底层技能基础。这也是为什么HER常被看作一种目标层面的数据增强也是一个隐式的课程学习过程。它没有修改环境没有修改奖励函数只修改了经验库中经验的“标签”就改变了整个学习的难度分布。这一招看似简单但背后是“经验复用”思想的极致体现。3. 从零实现PyTorch版HER附完整可运行代码3.1 环境与整体框架选择实现HER并不需要很复杂的代码关键是把“episode暂存-统一重标注-批量入库”这个流程写对。环境我推荐gymnasium的Robotics系列比如FetchReach-v1或FetchPush-v1它们天然支持Goal-Conditioned RL每个step返回的obs里自带desired_goal和achieved_goal字段做HER非常顺手。算法主体我选DDPG而不是SAC原因很实际DDPG参数少、结构简单、容易复现配合HER在连续控制任务上是论文验证过的经典组合。SAC多了熵系数调节虽然鲁棒性更好但新手调参容易懵。先把DDPGHER跑通再换SAC不迟。网络结构上actor和critic都使用三层MLP中间层256个神经元。输入上我踩过一个小坑直接把state和goal拼接输入是最简单也最稳的做法不要一开始就搞条件网络、注意力机制那些花活基线越朴素越好。3.2 网络结构与经验池设计先定义两个网络import torch import torch.nn as nn import torch.nn.functional as F class Actor(nn.Module): def __init__(self, state_dim, goal_dim, action_dim, hidden256): super().__init__() self.fc1 nn.Linear(state_dim goal_dim, hidden) self.fc2 nn.Linear(hidden, hidden) self.fc3 nn.Linear(hidden, action_dim) def forward(self, state, goal): x torch.cat([state, goal], dim-1) x F.relu(self.fc1(x)) x F.relu(self.fc2(x)) return torch.tanh(self.fc3(x)) class Critic(nn.Module): def __init__(self, state_dim, goal_dim, action_dim, hidden256): super().__init__() self.fc1 nn.Linear(state_dim goal_dim action_dim, hidden) self.fc2 nn.Linear(hidden, hidden) self.fc3 nn.Linear(hidden, 1) def forward(self, state, goal, action): x torch.cat([state, goal, action], dim-1) x F.relu(self.fc1(x)) x F.relu(self.fc2(x)) return self.fc3(x)replay buffer需要额外存goal字段我建议用最简单的字典结构每个transition保存五个字段obs、action、reward、next_obs、goal。这里需要特别说明在标准DDPG里transition通常是四元组但HER必须存goal因为重标注时要重建新目标并重算奖励。这也意味着buffer的sample函数要从五元组中取数batch组织成(state, goal, action, reward, next_state, next_goal)其中next_goal一般是new goal作用到value function时直接使用。3.3 目标重标注模块核心代码这是HER的灵魂。我直接给出可用的future策略重标注函数import random def relabel_episode(episode_transitions, k4): episode_transitions: list of dicts每个dict包含 obs, action, reward, next_obs, achieved_goal, desired_goal 返回原始经验 k条重标注经验 relabeled [] n len(episode_transitions) for idx, trans in enumerate(episode_transitions): # 始终保留原始经验 relabeled.append(trans) # future策略从当前transition之后的状态里采样k个作为新goal if idx n - 1: continue future_goals [t[achieved_goal] for t in episode_transitions[idx 1:]] for _ in range(k): new_goal random.choice(future_goals) new_reward compute_reward(trans[next_obs], new_goal) new_trans trans.copy() new_trans[desired_goal] new_goal new_trans[reward] new_reward relabeled.append(new_trans) return relabeled这里有个细节值得大书特书新目标来自episode_transitions中后面的achieved_goal而不是来自desired_goal。achieved_goal是智能体实际到达的位置desired_goal是环境给定的原定目标。HER的核心就是“把实际到达的位置当作新目标”这个字段千万别在代码里用混。compute_reward函数根据环境而定Fetch系列一般是判断achieved_goal和goal的欧氏距离是否小于某个阈值def compute_reward(next_obs, goal, threshold0.05): achieved next_obs[achieved_goal] # 字典或tensor distance torch.norm(torch.tensor(achieved) - torch.tensor(goal)) return 0.0 if distance threshold else -1.0注意奖励只有0和-1两种不要画蛇添足去乘一个系数。有人觉得-1.0不够“陡”改成-10或-100结果Q值爆炸、训练崩溃这个问题我在第4节详细讲。3.4 训练主循环与超参数配置整个训练主循环的逻辑是“跑episode-暂存所有transition-统一重标注-入库-更新网络”。分步来说采样一个目标g重置环境开始跑episode。每一步根据当前策略加探索噪声选择动作将transition暂存到临时列表。episode结束后调用relabel_episode函数生成重标注经验。把临时列表和重标注经验全部写入replay buffer。从buffer中随机采样一个batch更新critic和actor。软更新target网络。推演一下关键公式。critic的target值是y r γ * Q_target(s_{t1}, π_target(s_{t1}, g), g)这里g是这条经验的目标可能是原始目标也可能是重标注目标。这里有一个重要的地方是HER场景中因为目标被重标注理论上经验中的(s_t, a_t)与目标g可能并不来自同一条原始轨迹但这种“数据增强”正是算法需要的不需要额外修正。critic更新就是最小化(y - Q(s_t, a_t, g))的MSE。actor更新仍然通过最大化Q值来完成。超参数方面我给出一个经过实测的默认配置参数取值说明actor学习率1e-3过大会震荡过小收敛慢critic学习率1e-3同上gamma折扣因子0.98稀疏长任务不宜太大tau软更新系数0.05和网络初始化方式有关replay buffer容量1e6太小时重标注多样性不足每条transition重标注数k4复杂任务可调到8batch size128不要低于64探索高斯噪声N(0, 0.1)先大后小6万步衰减每episode更新次数40让buffer积累一定量再更新3.5 实测表现与调参记录我在FetchReach-v1上跑过几百个episode成功率就能接近100%而不用HER的DDPG在同样步数下成功率长期低于20%。两个实验唯一的区别就是是否调用relabel_episode其他超参完全一致这个对比非常直观。在FetchPush-v1上会难一些需要几万步才能达到80%以上成功率而且训练曲线有一个有趣的现象早期成功率提升很快因为HER让智能体快速学会了“把物体推到自身附近”这种易达成的子目标中期会有一段平台期因为策略需要从“到达任意位置”过渡到“到达指定位置”后期成功率会再上一个台阶。如果曲线没有这段“平台期后上升”的过程大概率是K值偏小或者网络容量不够。我自己调参的经验是前期先用比较大的探索噪声标准差0.2大约训练10%的步数后逐步衰减到0.05可以兼顾探索和稳定。不要一上来就用0.1虽然前期曲线好看但后期容易陷入局部最优。4. 踩坑实录训练不收敛、奖励爆炸等常见问题排查4.1 问题速查表以下表格是我在多个环境上调试HER过程中总结出的高频问题基本可以照单排查。现象可能原因排查方向成功率一直接近0奖励scale太大导致Q值震荡检查compute_reward确保奖励只有0和-1成功率前期涨很快后期停滞K值偏小正样本不够多把k从4调到8观察成功率变化训练曲线剧烈震荡gamma过大或tau过大gamma降到0.95~0.98tau降到0.02~0.05Q值持续爆炸式增长奖励未归一化或critic过估计检查奖励取值可换成SAC或加double Q重标注后反而变差buffer中重标注经验占比过高控制原始经验和重标注经验比例不低于1:4动作在边界饱和actor输出tanh饱和噪声被截断噪声在tanh之后再添加或使用OU噪声只学会“到达任意位置”但不会完成指定目标原始目标经验被重标注经验淹没提高原始经验的采样权重或者减少k4.2 重标注比例K怎么决定K值是HER最重要的超参数。K太小重标注样本不够正样本比例提升不明显算法退化成普通DDPGK太大buffer里绝大多数经验都是“事后目标”策略会过度关注“可达性”而忽视“完成指定目标”的能力表现为成功率上不去、智能体像个无头苍蝇到处乱跑。实践中的经验法则从K4起步。在FetchReach这种简单任务上4就够了在FetchPush和FetchSlide这种有物体交互的任务上K8效果明显更好。但K16时我没有观察到额外提升反而训练变慢了因为重标注产生的额外样本挤占了原始经验的相对比例。如果你的replay buffer有限提高K不如适度减小buffer容量把buffer主要留给高质量的重标注经验。还有一点容易被忽略K值的影响在不同算法下不同。DDPG对K比较敏感SAC配合HER时对K的容忍度更高因为SAC的熵项天然提升了探索性。如果你在用SACK4基本就够。4.3 网络容量与归一化HER严重依赖critic对不同目标的泛化能力。原理在于重标注后的经验中同一个state-action配对会对应多个不同goalcritic必须能分辨“在同一个状态下不同的goal会带来不同价值”。如果网络太小它就只能记住“这个state-action大概还行”的模糊信息无法区分具体目标。我建议actor和critic的隐藏层不低于256x2。在Mujoco类连续控制任务上128x128的容量明显不足成功率会低5到10个百分点。如果你发现加大网络后收益变小那说明容量满足需求了瓶颈可能在采样策略。输入归一化也很关键。state和goal可能是像素级数值也可能是坐标值量纲差异大。我的做法是在环境返回obs后立即做z-score归一化用常数值做缩放不做动态统计防止非平稳性。goal维度尤其需要归一化否则距离计算会被量纲大的维度主导稀疏奖励的阈值判断就失真了。4.4 探索系数与噪声策略DDPG标准做法是把动作噪声直接加到actor输出的action上。这里有一个细节actor输出经过tanh后限制在[-1, 1]加高斯噪声后很容易超出边界需要clip回[-1, 1]。clip本身没问题但过大的噪声在被clip后实际上变成了“经常往边界撞”的采样探索行为退化成“猛推到底”这对机械臂任务非常伤。我的改进是在噪声后先scale再clip或者用衰减的OU噪声。OU噪声有惯性相邻时间步的动作噪声有相关性在机械臂这类需要连续平滑动作的任务上比独立高斯噪声效果好。我实测过FetchPush上OU噪声比高斯噪声成功率高出约8个百分点。另外探索噪声也应该和HER配合调整。一个很实用的做法episode开始时采样一个较大的噪声系数episode内部逐步衰减让“前期多探索、后期多利用”在一个episode内部完成。这比全局的、单调的学习率衰减更细腻也能让重标注出来的目标分布更均匀。5. 延伸真实机器人部署与后续扩展5.1 从仿真到实机的坑把HER部署到真实机器人上首先要正视一个事实HER需要在训练中大量采样episode实机采样成本极高。常见的做法是先在仿真器里预训练再用少量真机数据做fine-tune。仿真里要开启domain randomization随机化物体质量、摩擦系数、颜色纹理让策略学到的是“目标条件技能”而不是“某个仿真环境下的查表”。实机上最头疼的是achieved_goal的观测噪声。仿真里指尖位置是精确的实机上的视觉估算或动捕数据都有噪声。如果直接把带噪声的achieved_goal当作重标注目标等于给经验库注入系统性偏差。我的建议是观测层加滤波平滑同时重标注时只使用滤波后的目标位置不要用原始观测的瞬时值。另外一个工程伦理层面的提醒HER允许我们给失败轨迹打上成功标签但这绝不意味着训练后的评估指标也可以“事后修改”。线上评估成功率时必须用原始目标用原定目标计算成功率HER只影响训练数据不影响评估标准。否则你会得到一个“训练时什么都像成功、评估时什么都失败”的模型。5.2 变体方法与发展方向这几年HER的变体很多真正在工程里值得关注的我觉得有三个方向。第一个是HERSAC组合把DDPG换成SAC自动熵系数让探索更鲁棒牺牲一点训练速度换稳定性在复杂操控任务上是值得的。第二个是能量函数加权的重标注采样比如CHER、Energy-Based HER核心思想是不再均匀采样“事后目标”而是选择那些“略有难度但又可达”的目标相当于给课程学习又加了一层自适应调节。第三个是层级化HER把长程任务拆成子任务序列每个子任务用HER学习再由上层策略负责选择子目标这个方法在长程导航和家居机器人任务上很有潜力。我对这些方向的个人体会是在你手头任务还不需要这些变体时别急着上。先把标准HER跑稳理解重标注比例对学习的影响再考虑扩展。很多同学一上来就追求最新变体结果基线都没跑通出了问题根本分不清是哪一层引起的。标准HER本身就是那个最值得先掌握的基线模型。我在实际项目里最常遇到的情况是团队对HER的期望过高以为它能魔法般地解决所有稀疏奖励问题。其实HER解决的是“经验复用”问题不是“探索”问题。如果环境本身很难产生多样化的状态覆盖HER也会力不从心。这时候需要配合更好的探索策略比如内在奖励、随机网络蒸馏或者干脆更换动作空间表达。这些经验教训比任何超参数表格都值钱。
阅读完成 · 觉得有帮助?
咨询建站