ARTICLE DETAIL

资讯详情

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

HER算法实战:用“事后诸葛亮”破解稀疏奖励难题

HER算法实战:用“事后诸葛亮”破解稀疏奖励难题 开写。先说个现象很多人第一次听到hindsight这个词第一反应是事后诸葛亮多少带点贬义。但在强化学习领域这个词是一个如雷贯耳的技术代名词——Hindsight Experience Replay简称 HER。它解决的是强化学习里最让人头疼的问题之一稀疏奖励。当智能体连续几千步都拿不到任何正反馈时常规算法基本原地踏步而 HER 能把这些失败经验重新包装成可用样本让智能体照样学出东西。这篇文章适合正在跑强化学习实验、被奖励稀疏搞到怀疑人生的人也适合刚接触目标条件强化学习、想搞清楚 HER 原理和代码细节的初学者。我会从设计思路一路讲到代码实现、参数调优和踩坑实录尽量让你看完能直接在自己的项目里把 HER 用起来。1. 从事后诸葛亮到强化学习HER 算法的核心设计思路1.1 稀疏奖励为什么是强化学习的拦路虎强化学习说到底是让智能体通过试错来学策略每一次试错后环境会给一个奖励信号智能体靠这个信号判断动作的好坏。问题在于真实任务里奖励往往稀疏得离谱。举个例子让机械臂抓取一个物体放到指定位置任务设计者通常只在成功放到位时给 1其余所有中间状态不管多接近目标奖励一律是 0。连续动作空间里机械臂要精确定位、抓取、移动、释放整个链条中间任何一环偏差都可能让任务失败。靠随机探索去撞一次正奖励概率低到可以忽略。这种情况下策略网络大部分时间收到的都是零奖励梯度没法更新。你看着训练曲线一条直线损失函数纹丝不动就知道问题出在哪了。传统解法是奖励塑形就是人工设计一些中间奖励让智能体逐步接近目标也得分。但塑形奖励有个很尴尬的副作用机器会钻空子。我见过机械臂在一个塑形函数下学会了把物体推到半路停着反复蹭分而不是真正把任务做完。因为停留在一个次优位置拿到的累计奖励可能比完成任务更好拿。而且塑形函数的人工痕迹太重不同任务要重调迁移性极差。HER 的思路完全绕开了塑形它不重新设计奖励而是重新定义目标。1.2 HER 的核心思想把未达成的目标改成实际达成的目标HER 的原理用一句话就能说明白当智能体没能达到设定目标时不要只把这条轨迹当失败丢掉而是把实际到达的状态当作原本想要的目标重新生成一条经验放回回放池。这条改写出来的经验里奖励不再是零而是成功信号。我来拆解一下。HER 工作的前提是目标条件强化学习goal-conditioned RL也就是策略的输入不只是状态还包括一个目标。比如机械臂任务里目标可以是把物体放到坐标 (x,y,z)每次 episode 开始时会给智能体一个目标智能体要尝试去达到它。HER 做的事是在一条 episode 结束后回头看看这条轨迹里智能体最终实际到达了什么状态。假设原始目标是放到 A 点智能体实际把物体放到了 B 点。HER 说那好我把这条轨迹的目标重新标成放到 B 点由于智能体确实放到了 B 点这条轨迹在新目标下就是一条成功轨迹每一步打上达到目标的奖励。然后把这个改写后的样本塞进回放池。这不就是事后诸葛亮吗是的正是这个意思。但和日常聊天里的贬义不同这个事后视角在强化学习里是极其有用的数据增广手段。失败的原因有很多种但结果状态本身是真实可用的把结果状态当作目标就创造出了一批带正反馈的训练数据而这些数据的动作轨迹是智能体真实执行过的分布是自然的。数据没变变的是看待数据的视角。1.3 普通经验回放为什么学不会失败经验有人会问不改写目标直接把失败轨迹存进经验池再给它们加一点负奖励不行吗我试过效果很差。普通经验回放experience replay只把原始经验原样存下来在奖励稀疏的情况下你采样一万条样本可能只有一两条带非零奖励。回放训练时绝大多数样本的梯度都趋近于零少数正样本的信号被平均稀释掉网络几乎学不到有意义的东西。还有人尝试给所有失败轨迹加一个固定负奖励比如 -0.01惩罚每一次没成功。这个方案的问题在于惩罚系数极其敏感。惩罚大了智能体发现反正怎么做都大概率失败干脆彻底躺平不动至少每步的负分少一点惩罚小了跟没有一样。更麻烦的是这种不对称的奖惩会让策略偏向保守探索意愿急剧下降。HER 的好处是它不需要你手工定这个惩罚系数正奖励信号是数据驱动生成的——伪目标来了奖励自然就变成了成功的奖励。整个训练过程中智能体既能看到未达成的原始目标经验又能看到大量达成伪目标的正反馈经验探索信号丰富得多。2. 核心细节解析目标条件、伪目标采样策略与奖励计算2.1 目标条件网络把目标当作输入而不是环境参数HER 依赖的策略本质上是目标条件策略。意思是在智能体的输入侧状态和目标是并列的信息。拿 DDPG 打个样actor 网络的输入是(state, goal)输出是动作critic 的输入是(state, action, goal)输出是 Q 值。很多新手在这里犯一个概念错误把目标写死在环境里每个 episode 环境换了目标但对网络来说目标是不见的。HER 要求目标必须是可观测输入因为训练时我们要随时把伪目标替换进去如果目标不是网络输入伪目标压根没法作用到网络更新上。网络结构本身不用特别复杂但输入预处理必须注意。目标空间往往和状态空间坐标量纲不同。比如 FetchReach 环境状态向量里包含机械臂关节角度、速度等信息而目标只是三维坐标。如果不做归一化网络会优先去拟合数值范围更大的特征训练极不稳定。我的做法是把所有状态特征和目标特征全部 clip 到区间内再标准化确保每个维度的输入分布都在相似量级。这一步对 HER 训练稳定性的影响比想象中大得多。另外HER 训练时状态和目标的对应关系是动态变化的。网络不仅要在原始目标下学会把状态映射到动作还要在伪目标下也做出合理动作。这对策略网络的表达能力提出了要求但也带来了额外的泛化能力。训练好的策略往往能对任意给定目标快速生成动作这是 HER 的额外收益——你得到的不是一个只会解决单一任务的策略而是一个目标跟随器。2.2 四种伪目标采样策略final / future / episode / randomOpenAI 的 HER 论文里给出了四种采样策略实用性排序很明显我依次讲清楚final整条轨迹只用最终的观测状态作为伪目标。这是最简单粗暴的一条失败轨迹只生成一条成功的伪目标经验。适合任务相对简单、目标空间不大的场景。future对轨迹中的每一个时间步从该时间步之后的状态里随机选一个当作该步的伪目标。这意味着一条轨迹的不同时间步可能对应不同的伪目标信息量最大也为智能体提供了过程目标的多样化监督。episode整条轨迹共享同一个伪目标这个伪目标从轨迹的某个后续状态里随机抽取一次后续所有时间步都用它。好处是稳定性强但灵活性不足。random从整个经验池里任意挑一个状态当伪目标完全不管当前轨迹的时间关系。绝大多数情况下效果最差因为伪目标和轨迹之间的关联几乎不存在监督信号噪声太大。我实际用得最多的是 future其次是 final。future 的关键设计在于从当前步之后挑状态这其实引入了一个时间可达性约束伪目标是智能体在接下来的时间里可达的因此学习起来难度适中且有意义。如果从当前步之前挑状态相当于让智能体穿越回去刷成绩完全违背了强化学习的时间因果性学出来的策略会非常怪异。这一点在实现时一定要当心不少开源实现里有个小索引参数控制这个起点调错就废。2.3 伪目标的奖励怎么算Replay Buffer 必须多存一个字段生成了伪目标之后核心操作有两个改写目标重算奖励。对于 Fetch 这类环境奖励通常由一个距离阈值决定。比如原奖励公式是def compute_reward(achieved_goal, desired_goal, threshold0.05): distance np.linalg.norm(achieved_goal - desired_goal) return -1.0 if distance threshold else 0.0当伪目标替换 desired_goal 后achieved_goal 本质上就是从该时间步之后的实际状态里选取的距离天然更近很多样本的奖励会从 -1 变成 0这就是正信号。这里有一个所有实现都绕不开的细节Replay Buffer 在存储每条经验时除了要存常规的(observation, action, reward, next_observation)还必须存下轨迹中每个时间步的achieved_goal。因为训练时要从某个历史时间步的状态里提取实际到达点来构造伪目标如果没存这个字段后面根本没法算伪目标的奖励。我看过好几个自己写 HER 的代码跑起来训练曲线纹丝不动最后定位发现就是漏存了achieved_goal只能靠环境临时读取但回放时环境状态早就变了一改就崩。正确的一条经验应该长这样transition { observation: obs, # 原始观测 achieved_goal: achieved, # 该步实际到达的目标状态 desired_goal: goal, # 该步设定的目标可能是伪目标 action: action, reward: reward, next_observation: next_obs, next_achieved_goal: next_achieved, }3. 实操过程手把手搭建 HER DDPG 训练流程3.1 环境选型与部署先跑通 Fetch 系列基准要验证 HER 是否真的有效最推荐的实验环境是 MuJoCo 里的 Fetch 系列包括 FetchReach、FetchPush、FetchPickAndPlace。这三个环境都是标准的稀疏奖励 benchmark目标是多维连续空间完美契合 HER 的适用场景。FetchReach 最简单机械臂只需把末端移动到目标点适合快速跑通流程FetchPickAndPlace 复杂得多涉及抓取和移动可以验证 HER 在更困难任务上的效果。环境安装上pybullet 的开源 Fetch 实现也可以但任务建模和视觉渲染有细微差别。我个人更推荐 mujoco 官方环境因为论文和社区代码绝大多数都是在这上面跑的对照实验结果更方便。安装时如果遇到 mujoco 许可证问题可以临时用 pybullet 替代跑逻辑但正式复现效果还是建议用原版环境。3.2 核心代码实现HER 逻辑拆解与参考实现这一步是重点我来拆解一个简化的 HER 训练循环。假设你已经有一个 off-policy 算法我用 DDPG 作为例子因为目前社区里 HER 配合 DDPG 最成熟。训练循环的伪代码如下for episode in range(max_episodes): # 1. 重置环境生成一个随机目标 obs env.reset() goal sample_goal() episode_obs [] episode_achieved [] episode_actions [] # 2. 采样一条完整轨迹 for step in range(episode_length): # 策略输入当前状态 目标 action select_action(obs[observation], goal) next_obs, reward, done, _ env.step(action) # 记录原始轨迹数据 episode_obs.append(obs[observation]) episode_achieved.append(obs[achieved_goal]) episode_actions.append(action) obs next_obs if done: break # 3. HER为这条轨迹生成伪目标经验并加入回放池 final_state next_obs[achieved_goal] # 构造一组伪目标例如用 future 策略每个时间步从未来状态取一个 her_goals [] for i in range(len(episode_obs)): future_idx np.random.randint(i, len(episode_achieved)) her_goals.append(episode_achieved[future_idx]) # 4. 把原始目标经验和伪目标经验一起写入 replay buffer for i, (cur_obs, act, ach, goal_her) in enumerate(zip( episode_obs, episode_actions, episode_achieved, her_goals)): # 用伪目标重算奖励 her_reward compute_reward(ach, goal_her) store_transition(cur_obs, act, her_reward, episode_obs[i1], goal_her) # 5. 采样训练 for _ in range(40): batch replay_buffer.sample(batch_size256) update_ddpg(batch)这一段代码的逻辑要点第三步中future_idx的起点是i而不是 0这是 future 策略的核心。第四步中原始目标经验也要正常存储通常我会让 HER 伪目标经验与原始经验按比例混合最简单的做法是 80% 的轨迹改写为 HER 经验剩下的 20% 保持原始经验。第五步的 40 次更新是经验值每个 episode 结束后多训几轮数据效率会更高。3.3 关键参数与调优经验HER 比例和 future 采样 k 值参数是 HER 落地最容易踩坑的地方。我列一份自己常用的初始配置都是在 Fetch 系列上验证过的参数推荐值说明HER 比例0.8回放池中 HER 改写经验的占比太高会导致策略忽略原始目标future 采样窗口 k3从当前步后最多 k 步范围内选伪目标限定局部性Replay Buffer 大小100 万Fetch 任务经验量大太小影响多样性Batch Size256偏大的 batch 有助于稳定 Q 函数更新Actor / Critic 学习率1e-3如果波动大降到 3e-4目标网络软更新 tau0.05DDPG 常用HER 场景没必要改小每 episode 训练次数40保证采样利用率但时间成本高时可降到 20调参时我的第一原则是先固定环境、算法和网络结构单独调 HER 比例和 future 采样窗口。HER 比例如果太高比如 0.95智能体会在大量伪目标上训练导致它对原始目标的敏感度下降训练后期可能卡在 60% 成功率上不去。比例太低比如 0.3正反馈样本不足稀疏奖励问题依旧。我用下来 0.7 到 0.8 之间是比较稳的区间。future 采样窗口 k 值也很微妙。k 值越大伪目标离当前步越远改写后的轨迹难度越大k 值太小比如 k1伪目标和当前状态几乎重合学习的指导意义变弱。k3 是个不错的起点任务更复杂时可以适当调到 5。4. 常见问题与排查技巧实录4.1 训练曲线完全不动先查三个地方HER 跑不起来的第一大原因是经验池里achieved_goal存储出错。后面训练采样时伪目标是从这个字段取的如果存的是observation里的一部分而不是真正的 achieved_goal算出来的伪目标方向会漂移奖励也会乱。排查方法是打印一条采样后的样本人工核对achieved_goal和desired_goal的数值是否在同一个语义空间。第二大原因是奖励函数写错了。注意 Fetch 环境的compute_reward接收的是achieved_goal和desired_goal如果你用环境返回的reward去替换伪目标的奖励那肯定不对因为环境返回的奖励对应的是原始目标。伪目标生成后必须用新的目标对重新计算奖励否则 HER 直接失效。第三大原因是目标空间归一化没做。我在前面提过有些环境的 target range 是 [0,1]但 observation 里的坐标经过变换后可能是 [-5,5]不归一化直接丢进网络训练过程会异常缓慢。4.2 训练后期成功率卡住不动多半是 HER 比例过高训练前期曲线上升很快到了某个点就卡住比如 FetchPickAndPlace 卡在 70% 附近这种情况常见于 HER 改写比例设太高。智能体过度依赖伪目标带来的正反馈形成了完成某种移动就行的惯性而不是真正朝原始目标逼近。解决办法是把 HER 比例从 0.8 下调到 0.5或者加入少量 random 策略的经验混合一下伪目标分布给策略增加一些对原始目标的敏感性。还有一种可能是 future 采样窗口太小伪目标永远很靠近当前状态等于让智能体学习微移动对长程任务帮助有限。可以逐步增大 k 值观察成功率变化。4.3 用 PPO 硬套 HER 为什么效果一塌糊涂这个问题在社区里经常被问。HER 本质上是离策略算法假设下的技巧它依赖大量历史经验反复重放天然配套的是 DDPG、TD3、SAC 这类 off-policy 算法。PPO 是在线策略算法每一轮更新后策略变了旧经验的重要性权重需要重新校正而 HER 的伪目标经验本身就是过去行为的产物和当前策略行为分布差异很大硬套上去就会引入巨大偏差。如果你非要在 PPO 里用类似历史经验重放必须做重要度采样修正工程复杂度大幅上升。我的建议是别折腾直接用 off-policy 算法搭配 HER。4.4 训练速度慢回放池采样开销的优化方案HER 需要大约 100 万级别的经验池每个 batch 都要从大池子里随机采样CPU 和内存开销不小。这里有几个实战优化的小技巧。一是用共享内存或内存映射存储经验避免 Python 列表频繁分配对象。二是每训练若干步才做一次采样和更新而不是每步都训减少 IO 压力。三是如果机器内存紧张可以缩减经验池到 50 万对 FetchReach 这种简单任务损失不大但复杂任务不建议缩减太多。我还试过在 HER 之上叠加优先经验回放思路是给那些奖励非零的伪目标经验更高的采样优先级。实测在 FetchPush 上能提速 20% 左右但注意优先回放会破坏均匀采样分布和 HER 叠加时容易让经验分布偏移需要谨慎调整优先系数。5. 一点个人心得做了这么多年强化学习实验HER 是少数几个让我第一次看到就忍不住拍桌子的方案因为它的核心逻辑实在太简单却把稀疏奖励这个看似死局的问题凿开了一个大口子。我后来在机械臂抓取、点对点导航、甚至一些模拟器里的操作类任务上都复用过这套方法论每次都能感受到后见之明带来的威力。按照个人经验新手最容易忽略的其实不是算法公式而是数据链路——achieved_goal存没存对、伪目标重算没重算、归一化做了没做这三个小细节决定了你是在跑 HER 还是在白烧显卡。如果你已经跑通了基础的 HER后续值得往两个方向拓展一个是看从 HER 衍生出的 CHER课程化目标生成把伪目标按难度排布逐步引导智能体逼近真实目标另一个是把 HER 的思想用到多智能体或离线强化学习场景同样是面对稀疏反馈这套经验重标注的思路在其他领域也很有迁移价值。先把你手头的任务用 HER 跑明白再谈扩展这才是最扎实的路径。
返回列表