ARTICLE DETAIL

资讯详情

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

HER算法:用后见之明解决强化学习稀疏奖励难题

HER算法:用后见之明解决强化学习稀疏奖励难题 hindsight后见之明。生活中它常被当成一种谦逊的提醒——事后看一切都清晰身处当下时却一片模糊。可在强化学习的世界里事后聪明被做成了一种相当优雅的算法Hindsight Experience Replay简称 HER。如果你训练过机械臂抓取、机器人搬运或者任何一个带目标概念的任务大概率经历过那种奖励几乎全是 -1、梯度信号消失、训练曲线像一条水平线的绝望时刻。这篇文章想讲的就是怎么用 hindsight 这种回头改目标的思路把稀疏奖励环境从学不动变成学得动。适合正在做强化学习毕业设计、研究稀疏奖励问题或者对经验回放机制想深入理解的读者。我会从算法直觉讲到参数配置再讲到我实际调参踩过的坑这篇总结可以直接当作你项目的参考。1. 为什么需要事后聪明HER 要解决的问题1.1 稀疏奖励一堵墙外的宝藏强化学习的经典设定里agent 通过和环境互动获得奖励用奖励信号来调整策略。但很多真实任务不是每一步都有反馈的机械臂推方块推到位才有奖励机器人开门门完全打开才有奖励游戏里过关才有奖励。这类任务被统称为稀疏奖励任务。注意稀疏不是说奖励少而是说奖励几乎为零大部分时间步上 agent 得不到任何有用的指导信号。打个比方你在一个巨大的黑暗房间里找电灯开关。你伸手乱摸摸到开关才能开灯。但问题在于如果只是摸到没有反馈你根本不知道自己的手离开关是近了还是远了只能永远乱摸。稀疏奖励就是这种处境——智能体每走一步获得 -1 或 0只有到达终点那一刻才有正奖励。因为大部分探索轨迹都到不了终点所以算法无法从这些失败的轨迹中学到任何东西。更糟的是失败轨迹在回放缓冲区里越积越多最终把它们当成均匀分布的经验来训练时模型只会学到所有动作都一样差。在工业项目里稀疏奖励问题尤其常见。真实的机器人控制、推荐系统中的长链路转化、物流调度里的最终送达几乎都不可能给每一步一个稠密的奖励。强行设计稠密奖励又容易诱导 agent 学到投机取巧的行为。所以怎么让算法在只有稀疏奖励的条件下依然能学起来是整个强化学习落地过程中绕不开的坎。1.2 奖励信号太稀模型就学不动从优化角度看稀疏奖励意味着损失函数几乎没有梯度信息。策略梯度、Q-learning 这类方法严重依赖奖励的差异来判断动作好坏。如果 99% 的轨迹都是同样的 -1 奖励那么无论 agent 做什么动作从价值函数的角度来看都是等价的坏策略更新方向几乎就是噪声。你可以把这种情况理解成考试只有一道大题而且对就是对、错就是错没有任何过程分。你在考场里蒙了一百遍每次都差一步但成绩单上永远都是错你根本无从知道自己是哪一步出了问题。这时候有人会想那我把奖励设计得稠密一点不就好了比如根据机械臂末端与目标点的距离来给一个负的惩罚值。听起来合理实际操作中问题也很多距离函数选欧氏距离还是曼哈顿距离需不需要归一化如果障碍物在中间距离越近反而越危险这个奖励函数就变成了误导信号。更常见的问题是稠密奖励容易让 agent 陷入局部最优它会找到一条分数不错但实际完成不了任务的路径。所以稀疏奖励本身不是缺陷它反而是很多任务最自然、最不容易被钻空子的描述方式。我们需要的是在保留稀疏奖励的同时让算法从失败中提取信息。1.3 HER 与传统经验回放的区别传统经验回放把 (状态, 动作, 奖励, 下一状态) 存进一个缓冲区然后随机采样小批量更新网络。它的价值在于打破样本之间的时间相关性让训练更稳定。但是传统经验回放没有改变样本有没有奖励这个事实失败的轨迹依然是失败的稀的奖励依然是稀的。HER 的做法完全不同。它在存放经验的时候就多做了一步对每个 transition不只存原始目标下的经验还额外生成若干条替代目标下的经验。具体来说如果一条轨迹原本的目的是把方块推到 (0.5, 0.2, 0.1)但实际方块最后停在 (0.4, 0.1, 0.0)那 HER 会生成一条新经验假装目标就是 (0.4, 0.1, 0.0)并重新计算奖励。由于方块确实到了这个位置这条新经验的奖励就是正的成功信号。一句话总结传统回放存储的只是客观发生过的经验而 HER 存储的是重解释后的经验它把一条失败的轨迹硬生生变成了多条成功的轨迹。这里要强调一下HER 并不是魔法它依赖一个前提任务必须能被描述成多目标形式也就是每个 episode 除了状态和动作之外还得有一个显式的目标向量同时环境能告诉我们当前已经完成了目标中的哪一部分。这正是下一节要展开的核心设计。2. HER 算法核心设计与原理拆解2.1 从多目标视角重新理解轨迹HER 的所有逻辑都建立在一个形式化基础上把任务当作多目标任务来处理。每个 episode 都有一个目标向量 goal记作 g同时环境会返回一个 achieved goal记作 ag表示当前已经完成到的目标位置。以机械臂推方块为例g 是方块需要到达的目标坐标ag 是方块当前的实际坐标。agent 的观测变成 (state, goal)策略写成 π(a | s, g)价值函数写成 V(s, g)。这个思路来自 Universal Value Function ApproximatorsUVFA与其为每个目标单独训练一个策略不如训练一个同时接收状态 目标作为输入的函数逼近器让它在目标之间共享表征和经验。这看起来只是把输入向量多拼了几个维度但意义深远。一旦策略是目标条件的我们就可以把任意一条轨迹的终点状态当成一个合法目标。回到那个黑暗房间的比喻你原本想找灯开关结果摸到一张桌子。在普通强化学习里这次探索是彻底的失败因为没有开灯。但在多目标视角下这次探索可以重新写成我成功到达了桌子所在的位置。如果未来有个任务的目标就是到达桌子附近那这条经验立刻变成了正样本。HER 的巧妙之处就是它不要求你一开始就知道哪些目标有价值而是把所有正在尝试的目标都当作潜在的有效目标。2.2 替代目标采样策略final 到 random 的取舍HER 真正需要调的核心参数是 goal 的采样方式。给定一条长度为 T 的轨迹在第 t 步的状态、动作到底从什么地方去取替代目标常见有四种策略我用表格整理一下策略采样方式直觉实际效果final只使用轨迹最终状态 ag_T 作为替代目标最简单和事后聪明直觉最贴近可以跑通但样本多样性差future从 t 时刻之后的未来状态中随机采样一个 ag 作为目标替代目标与当前状态有合理的时间关联实践中表现最好最推荐episode从整个 episode 的所有状态中随机采样多样性高让模型看到更多可能目标有效但可能采到过去的状态random从环境或历史缓冲区中随机采样一个状态覆盖范围大但可能与轨迹关系弱很少单独使用一般做补充实际使用里有几个原则。第一future 策略几乎总是最好的这是论文和社区大量实验验证过的结论。原因也不难理解在第 t 步如果要给这条轨迹构造一个替代目标最好选择那些未来会到达的状态作为目标。因为从时间顺序上看agent 确实是从当前状态一步步走到那个状态的这条轨迹天然就是一条从当前状态通往该目标的演示路径。如果采样一个 episode 里已经过去的状态agent 需要先回到过去才能达到目标这和轨迹的实际走向矛盾会给训练带来噪声。第二final 策略虽然简单但整条轨迹都指向同一个最终目标很容易造成经验冗余特别是在轨迹很长、状态变化很大的任务里。第三n_sampled_goal 这个参数也就是每个 transition 额外生成多少条替代目标经验一般取 4太大收益递减太小效果不明显。2.3 为什么替代目标能加速学习从失败里提炼成功我见过不少人对 HER 的第一反应是这不就是改标签吗不会把模型教坏吗这个疑虑我能理解但实际上不会。原因在于HER 不是把失败轨迹强行标成原始目标下的成功而是在多目标框架下把它标成另一个目标下的成功。目标变了奖励自然也要重新计算。计算公式很简单distance || ag_t - g_new ||如果 distance 小于某个阈值 threshold奖励为 0否则为 -1。注意这里的 g_new 不是原始的 goal而是采样得到的新目标。由于 ag_t 和 g_new 来自同一条轨迹的同一个时间点只要采样策略不产生矛盾总有相当比例的 transition 会满足距离阈值从而变成 0 奖励的成功样本。深层的学习信号在于原本一条轨迹只有末尾一个时间步可能有正奖励其他时间步全是 -1经过 HER 重解释之后轨迹中每一个状态都可以成为某个新目标的到达点于是整条轨迹上散落着大量的成功信号。策略网络在这些信号的指导下就能学到从某个状态出发做某个动作最终能达到某个目标这样的条件知识。多条失败轨迹叠加在一起实际上覆盖了状态空间里如何在不同目标之间迁移的丰富经验。这也是 HER 对比单纯奖励塑造成本更低的原因——它不改变环境不修改奖励只从认知层面把失败重新解读为另一种形式的成功。3. 实操实现从零到一搭建 HER 项目3.1 环境与工具选型我建议用 OpenAI Gym 的 Fetch 系列环境作为起步尤其是 FetchPush-v2、FetchReach-v2 和 FetchPickAndPlace-v2。这三个环境都是标准的稀疏奖励连续控制任务目标是一个三维坐标观测空间里明确提供了 desired_goal 和 achieved_goal 字段和 HER 的假设完全对得上。更关键的是这些环境有公开的 benchmark 数据方便对比效果。工具链上最省事的方案是 stable-baselines3它从 1.x 开始就内置了 HerReplayBuffer并且和 DDPG、TD3、SAC 这些连续控制算法做了集成。你不需要自己管理替代目标的采样逻辑只需要传几个参数。当然如果你想深入理解 HER也可以在一些开源仓库的基础上自己实现回放缓冲区后面我会给出核心伪代码。版本上有一点要注意gym 0.21 和 gymnasium 的 API 有一些差异sb3 的 HerReplayBuffer 目前主要适配 gymnasium 风格的环境接口建议统一用 gymnasium 并锁好版本不然常见的坑就是 reset 返回格式不一致导致缓冲区直接报错。3.2 目标定义与稀疏奖励函数先看 FetchPush-v2 的观测结构。环境返回的 observation 是一维数组通常包含机械臂各关节角度、末端速度等信息大约是 25 维desired_goal 是 3 维的目标坐标achieved_goal 是 3 维的当前方块坐标。把三者拼起来就得到 agent 真正看到的输入。HER 的关键在于奖励函数必须是目标条件的。用代码表示就是import numpy as np def compute_reward(achieved_goal: np.ndarray, desired_goal: np.ndarray, threshold: float 0.05): distance np.linalg.norm(achieved_goal - desired_goal, axis-1) reward (distance threshold).astype(np.float32) - 1.0 return reward这段代码的意思是achieved_goal 与 desired_goal 之间的距离小于等于阈值reward 为 0否则为 -1。这个奖励函数没有中间值完全稀疏。阈值 0.05 是 Fetch 环境的默认设置实际项目中也可以根据任务精度需求调整比如机械臂需要更精准抓取可以改成 0.02但要注意阈值越小HER 生成的成功样本比例越低训练难度会上升。3.3 网络结构与观测拼接HER 并不改变底层强化学习算法的网络结构但需要把 goal 和 achieved_goal 拼接进输入。简单做法是把所有向量 concat 成一个一维向量然后交给普通的 MLP。在 stable-baselines3 里这一步通过 MultiInputPolicy 自动完成它会把 obs、desired_goal、achieved_goal 分别做一个小编码网络再拼接适用性更好。对于 Fetch 这类连续控制任务我建议用以下配置Actor 和 Critic 都是两层 MLP每层 256 个神经元激活函数默认用 ReLU。结构不必太深两层足够拟合机械臂控制这类中等复杂度的策略。更深反而容易在小规模样本上过拟合。输出层动作用 tanh 限幅到 [-1, 1]这与 Gym 的 Box 动作空间一致。3.4 核心代码实现DDPG HER先用 stable-baselines3 直接跑通一个完整训练流程这是最稳妥的起步方式import gymnasium as gym from stable_baselines3 import DDPG from stable_baselines3.her import HerReplayBuffer, GoalSelectionStrategy env gym.make(FetchPush-v2) strategy GoalSelectionStrategy.FUTURE model DDPG( policyMultiInputPolicy, envenv, replay_buffer_classHerReplayBuffer, replay_buffer_kwargsdict( n_sampled_goal4, goal_selection_strategystrategy, max_sizeint(1e6), ), learning_starts1000, learning_rate1e-3, batch_size256, gamma0.95, train_freq1, gradient_steps1, verbose1, ) model.learn(total_timestepsint(1e6))如果你不想依赖高层封装想看清楚 HER 在回放缓冲区里到底做了什么核心伪代码也很简单def add_episode_to_buffer(episode): # episode 包含 states, actions, rewards, goals, achieved_goals T len(episode) for t in range(T): # 原始目标下的经验必须存 buffer.add( stateepisode.states[t], actionepisode.actions[t], rewardepisode.rewards[t], goalepisode.goals[t], ) # HER 额外生成的替代目标经验 for _ in range(n_sampled_goal): new_goal sample_goal(episode, t, strategy) new_reward compute_reward(episode.achieved_goals[t], new_goal) buffer.add( stateepisode.states[t], actionepisode.actions[t], rewardnew_reward, goalnew_goal, )采样函数 sample_goal 里future 策略的实现就是在 [t1, T) 区间内随机采一个下标取出对应的 achieved_goal 当作替代目标。这段逻辑并不复杂但就是这简简单单的多存几份经验让稀疏奖励任务从几乎无解变成可以收敛。3.5 超参数与训练配置要点把关键超参数和影响列成一张表方便你照着配参数建议值说明n_sampled_goal4每个 transition 额外采样替代目标的条数太大收益递减goal_selection_strategyfuture实战最稳定final 适合快速验证learning_starts1000先随机探索一段填充回放缓冲区batch_size256采样替代目标后训练 batch 可以适当加大gamma0.95稀疏目标场景里折扣因子不必太高train_freq / gradient_steps1 / 1每步环境交互后都更新一次稳定但慢max_size1e6回放缓冲区容量容量太小破坏多样性learning_rate1e-3连续控制任务里 actor 和 critic 通常用同一学习率4. 实验验证与结果分析4.1 Fetch 系列环境上的表现对比我实际跑过的经验是这样的在 FetchReach-v2 这种相对简单的任务上目标就在机械臂能轻松到达的区域内无论有没有 HERDDPG 都能在 20 万步左右达到接近 100% 的成功率HER 的优势不明显。但换到 FetchPush-v2也就是需要把方块从某个位置推到指定位置的任务区别就非常大了。没有 HER 的 DDPG 在 100 万步内成功率始终在 0% 附近徘徊偶尔能到 5%基本就是随机探索碰巧压线成功。加入 HER 之后通常在 60 万到 90 万步之间成功率能爬到 80% 以上部分种子甚至能到 100%。FetchPickAndPlace-v2 需要机械臂先抓取方块再移动再放下难度更高。这个任务里 HER 几乎是必需品没有它你连前期抓取动作都学不会。我自己的记录是HER 加上未来策略140 万步左右能到 60% 到 80% 的成功率。这一项对比足够说明问题HER 不是略微提升性能而是在稀疏奖励任务里直接决定了能不能收敛。4.2 训练曲线应该怎么看HER 的成功率曲线和普通的稠密奖励任务不太一样它不是平滑上升而是阶梯式的。比如前 30 万步成功率一直在 5% 以下突然某个阶段跳到 40%然后再平台一阵子再跳到 80%。原因是稀疏奖励下学习信号不是均匀出现的一旦回放缓冲区里累积了足够多不同目标的成功样本策略会有一段顿悟式的快速提升。所以看训练曲线的时候别急着下结论。如果前 30 万步曲线平得像死水这不代表 HER 没用很可能是探索阶段还没积攒够有效经验。这时我会做的第一件事不是调大学习率而是检查回放缓冲区里替代目标经验占比是否正常。简单验证方法是打印一下 buffer 采样结果看看 reward0 的样本比例。如果比例接近 0说明替代目标采样策略或者奖励阈值可能配置错了如果比例在 20% 到 40%说明 HER 在正常工作。4.3 对比实验有 HER 与无 HER 的差距量化为了把结论量化我建议你做一个简单的消融实验固定底层算法 DDPG只切换是否使用 HerReplayBuffer连续跑 5 个随机种子记录训练结束后的平均成功率。下面是一个典型结果配置100 万步平均成功率方差DDPG 普通 Replay Buffer0.020.01DDPG HERn_sampled_goal4, future0.860.12DDPG HERn_sampled_goal8, future0.880.10看到没有普通 buffer 的成功率几乎可以忽略而 HER 直接把成功率拉到了 0.86。从方差也能看出来 HER 对不同随机种子的稳定性还算可以接受但 0.12 的波动也提醒我们稀疏奖励任务本身还是存在一定的运气成分跑不同种子时结果会有波动做实验时一定要固定种子并且多跑几次避免被单一的运气结果误导。5. 常见问题与避坑指南5.1 目标维度不一致导致的训练崩溃我自己第一次上手 HER 就栽在这个问题上。Fetch 环境里 desired_goal 和 achieved_goal 都是三维坐标看起来很合理但如果你换了自定义环境或者从某个开源项目里迁移代码必须严格检查 goal 和 achieved_goal 的维度是否一致。很多诡异的现象都源于这一点维度不一致时network 输入层会自动过但距离计算会报错或者广播机制把错误掩盖了导致 reward 永远是 -1算法怎么训都学不动。解决办法是写一个单元测试随机采样一个 transition检查 compute_reward 的输出是否符合预期。比如构造一个 achieved_goal 和 desired_goal 完全相同的样本reward 必须等于 0相差很远的样本reward 必须等于 -1。这种小测试能拦住一大半维度对齐问题比调参管用得多。5.2 采样策略选错的代价goal_selection_strategy 选错了效果差异非常大。我有一次图省事用 final 策略跑 FetchPickAndPlace结果 120 万步成功率只有 30% 左右换成 future 之后提升到 70%。原因是 PickAndPlace 任务中轨迹的最终状态通常落在某个固定的角落如果所有替代目标都指向同一个末端状态经验多样性太差模型很快就过拟合到那一种情况。如果觉得自己任务特别复杂也可以尝试叠加采样策略比如 70% 用 future30% 用 episode但不建议一开始就搞混合先跑通标准 future 再说。另外很多人会把 n_sampled_goal 调得很大以追求更多成功样本比如 20但我实测下来 4 到 8 就够了。更大的值会导致缓冲区里替代目标经验占比过高原始目标经验被稀释反而破坏了对真实任务目标的建模。5.3 归一化与分布偏移这个坑比较隐蔽。HER 生成的经验中goal 的取值范围是轨迹中实际到达的状态而原始目标通常是环境随机采样得到的两者可能不在同一个分布里。比如 FetchPush 环境里方块初始位置分布很广但 agent 早期只会把方块推到离初始位置很近的地方于是替代目标大多集中在某个小区域。如果不做任何归一化或偏移处理Critic 网络会对这个区域的估计更准确对其他区域产生误判。这种分布偏移很难彻底消除但有几个缓解手段。第一观察空间里做 RunningNormalization让输入特征保持尺度一致第二适当增大 n_sampled_goal 让更多不同目标进入训练避免集中在单点第三如果发现成功率涨到一半就停滞检查一下 buffer 里目标分布的直方图看看是不是采样范围太窄了。5.4 我踩过的其他调试细节再分享几个不踩一遍很难注意到的细节。第一个是 episode 长度。Fetch 系列默认是 50 步但如果你自定义环境episode 长度不能太短。替代目标采样需要时间跨度的支撑如果每轮只有三五个时间步future 策略几乎没有采样空间HER 的效果会大打折扣。第二个是探索噪声。DDPG 默认用的 Ornstein-Uhlenbeck 噪声在稀疏奖励任务里不太够我通常会叠加一个均值为零的高斯噪声标准差不小于 0.1保证初期有足够的随机动作去探索状态空间。第三个是评估策略的问题。训练中打印的成功率一定要用确定性策略评估也就是关闭探索噪声否则会因为噪声干扰而低估模型真实水平让你误以为模型没学好。6. 扩展方向与我的使用体会6.1 HER 思想在更大项目里的变形HER 并不是一个只能用在机械臂搬运上的孤立算法它的核心思想——把失败轨迹重新解释成有效经验——在不少方向都有变形。比如在多任务强化学习里目标空间天然丰富HER 可以直接作为目标生成器在离线强化学习里可以让策略学习从无标注轨迹中构造奖励信号在层次强化学习中HER 甚至可以用于子目标的回放让 agent 学会逐步逼近高层目标。我还在一些稀疏奖励的天然语言处理任务里见过类似的思路先用规则构造伪目标再用对比学习生成训练信号本质上是同一种后见之明的应用。如果在自己的项目里想扩展 HER最值得试的方向是把它和 reward shaping 结合HER 负责提供基础的成功信号reward shaping 负责在到达目标之前提供梯度引导。两种手段互补能缓解 HER 在极长时序任务里目标太远、成功样本太少的问题。另外把 HER 与更强的基础强化学习算法比如 TD3 或 SAC 结合通常比直接用 DDPG 更稳收敛也更快我自己现在默认就用 TD3 HER。6.2 一点经验总结从初次接触 hindsight 这个词到现在我对它的理解变化了很多。最初觉得它只是个强大的 trick后来发现它改变了我设计奖励函数的方式——在配置环境之前我会先问自己这个任务能不能用多目标来描述如果答案是能那稀疏奖励其实没那么可怕因为任何一条轨迹都不会真正浪费。我建议想深入的同学不要只跑一个现成的库最好动手实现一遍 HER 的回放缓冲逻辑亲手写那十几行采样代码比看一百遍论文都更管用。等你能从第一性原理出发推导出为什么要用 future 而不是 final时你对强化学习的理解就已经上了一个台阶。
返回列表