ARTICLE DETAIL

资讯详情

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

强化学习稀疏奖励难题的破解利器:HER原理与实战

强化学习稀疏奖励难题的破解利器:HER原理与实战 做强化学习的朋友看到 hindsight 这个词第一反应大概率不是“事后诸葛亮”这个人人都懂的概念而是那篇在稀疏奖励问题上打开局面的算法——Hindsight Experience Replay简称 HER。我最初接触 HER 是在做机械臂抓取项目的时候当时试了各种 reward shaping奖励函数改了一版又一版网络就是不收敛。后来把 HER 加进训练流程梯度信号瞬间就“活了”我才真正理解什么叫“让算法从失败里学东西”。这篇文章写给两类人一是刚入门强化学习、正被稀疏奖励折磨得怀疑人生的新手二是已经跑通常规 RL 流程、想在仿真或真机场景里提高样本效率的从业者。我会从问题本质讲到机制原理再拆解一套可以直接参考的实现方案和超参配置最后把常见坑位列一遍。目标很明确让你看完之后自己能动手把 HER 用起来而不是停留在“知道有这么个算法”的阶段。1. 为什么常规强化学习会栽在稀疏奖励上1.1 奖励信号是唯一老师但这个老师经常不开口强化学习的训练循环可以概括成三个词试错、反馈、调整。Agent 做出一个动作环境返回一个状态和奖励算法根据奖励高低去更新策略。这个过程中奖励就是唯一的“教学信号”——就像学生做题做完一整套卷子才告诉你全对还是全错中间步骤完全不打分。这种情况下学生只能盲目猜猜一百次也未必能蒙对一次。在稀疏奖励环境里这个问题被放大到了极致。以机械臂推物体为例目标是把一个方块推到指定位置只有方块最终落在目标点周围一定半径内才会给一个正奖励其他任何情况奖励都是 -1。整个轨迹可能有 50 步方块从起始位置到目标点需要连续 50 步都做出合理动作。随机策略能一路蒙对的概率低到可以忽略于是算法拿到的经验基本全是“失败轨迹 恒定常数奖励”。从这些经验里策略梯度几乎什么都学不到因为所有动作的回报都一样差梯度方向被噪声淹没网络参数只能原地打转。1.2 随机探索的本质是撞大运而撞大运的代价是指数级的我们换个更直观的例子。假设一个任务就像在黑暗的仓库里找一扇门房间里有一万个格子门只有一个。每一步你只能上下左右移动一格且不知道离门还有多远。普通强化学习在这种设定下的探索方式是纯随机的策略输出的动作分布一开始接近均匀最终能走到门口的概率大体和随机游走命中目标格子的概率相当。随着状态空间维度上升这个概率呈指数级衰减训练步数却没法指数级上升于是绝大多数项目直接耗死在训练初期。可能有朋友会想那我把终止条件放松一点行不行比如方块只要靠近目标就算成功。这就是 reward shaping 的思路实际操作中倒确实有用但问题在于每个任务都要重新手写“怎么算靠近”。距离函数怎么定义、权重怎么设、给多少额外奖励全是锅。更麻烦的是一个精心设计的中间奖励很容易把策略带进局部最优——机械臂学会了把方块推到中途的一个“伪目标点”上因为那个位置的中间奖励最容易拿却离最终目标还很远。我调过的某个抓取任务加了距离惩罚项之后机械臂干脆绕着目标转圈也不伸手去抓因为“保持距离”的惩罚最小。1.3 HER 想解决的是这个结构性问题而不是补丁式问题奖励稀疏的本质不是“奖励给得太少”而是“失败经验没用”。传统算法把一整条失败轨迹直接丢弃只留下偶尔出现的成功轨迹作为学习素材。这太奢侈了——真实环境跑一条轨迹是要花时间、耗资源的失败轨迹里其实包含了大量的状态转移信息比如机械臂在这个位置推了一下方块往那个方向滑动了多少这些动力学信息对学策略非常关键。HER 的核心洞察就是不要浪费这些失败轨迹换个目标重新解释它们把“失败”变成“成功”。换句话说HER 把“事后诸葛亮”这个人类特质搬进了算法里。人有时候也是在失败之后才意识到自己完成了什么——比如去超市本打算买牛奶结果把酱油买回来了这次“失败”的过程反而让你记住了酱油摆在哪个货架。算法也可以这样本来的目标是推方块到左上角虽然没推到但具体停在了右下角那就把这整条轨迹重新标记成“把方块推到右下角”的成功样本拿去训练。这就是 Hindsight Experience Replay 的全部思想。2. HER 的核心机制目标重标记到底在做什么2.1 一条失败轨迹如何被“改写”成成功样本这一节需要先把强化学习里的多目标设定讲清楚。HER 面向的是一类被称为 goal-conditioned RL 的问题这种设定下策略的输入除了当前状态还必须包含一个目标也就是学的是a π(s, g)而环境的奖励函数也依赖目标通常写成r r(s, a, g)。目标可以是向量、坐标、甚至一句话的 embedding但在机械臂这类任务里常见的就是“末端位置”“物体位置”这种连续向量。训练时流程是这样的每一集我们会先随机采样一个目标 g然后在 g 的指导下跑完一整条轨迹得到(s_0, a_0, r_0, s_1, a_1, r_1, ..., s_T, a_T, r_T)。因为奖励稀疏这串数据里的 reward 几乎全是 -1最后一步如果没达成目标也是 -1于是整条轨迹不能给策略提供任何有区分度的信息。到这里传统做法就是丢掉HER 的做法则完全不同它注意到轨迹结束时的状态s_T里其实包含了一个“已经实际达到的状态”比如方块真实所在的位置achieved_goal。既然我们已经在仿真器里得到了这个实际点位为什么不换一下训练目标把所有奖励重新算一遍呢于是算法会额外生成一条等效轨迹目标变成g achieved_goal然后从第一步重新计算每一步的奖励。因为轨迹末端确实到达了achieved_goal所以最后一步的奖励自然变成 0或正值前面每一步在“新目标”下也都有了明确的相对信息。这些新轨迹被一起丢进经验回放池和原始轨迹混在一起供 off-policy 算法反复采样。2.2 为什么这对 off-policy 算法天然友好这里有一个很多初学者卡住的点为什么可以随便换目标重新计算奖励换完之后这条经验还合法吗答案是对于 off-policy 算法来说经验回放池里存的是“四元组”也就是(当前状态, 动作, 奖励, 下一状态)算法从中学习的是动作价值和状态转移之间的关系并不要求这些经验必须来自同一个策略。重标记后的四元组只是把“奖励”和“目标”两个字段改了状态和动作还是真实发生的所以它在物理上是完全自洽的——真实世界中机械臂确实在那个状态做了那个动作状态也确实转移到了下一帧。逻辑上有一个隐含条件目标必须能从状态里推算出来。机械臂任务天然满足这一点因为目标就是“物体位置”而物体位置在观测里就有。如果目标是一个抽象概念比如“叠好衣服”而状态里看不到具体叠得怎么样那重标记就没法做。写代码之前先检查这个前提能省下大量排查时间。2.3 K 值与目标采样策略重标记不是越多越好论文里有一个关键超参叫 K意思是每一条真实轨迹额外生成几条重标记轨迹。但这里有个细节很容易被误解不是每条额外轨迹都拿achieved_goal当新目标。实际操作中重标记的目标有两种来源一是final策略直接把本集轨迹最后实际到达的目标当作新目标二是future策略从当前轨迹后续时刻中随机挑一个时刻的实际目标作为新目标。经验上看future策略比final稳定得多因为它能产生更丰富的“中间态成功”样本而不是只有终点一个目标。K 值怎么定OpenAI 论文里的标准配置是 K 4即原始轨迹 1 条额外重标 4 条混合比例大约 1:4。这个比例的直觉在于重标样本能提供成功信号但原始样本保留了任务的真实意图两者缺一不可。如果 K 过大缓冲池里的经验会过度偏斜到“其实没达成原目标”的状态分布上策略学到的是“如何到达随机位置”而不是“如何到达指定位置”评估时就会崩。K 4 不是一个数学推导出来的最优值但在我复现过的几个任务里都非常稳建议作为默认首选参数。2.4 从一个额外视角理解 HER样本利用率的数量级提升单看一条轨迹的利用方式HER 本质上是在不增加环境交互的前提下把一条轨迹从“只有一个目标对应一份奖励”变成“一个轨迹对应多份不同目标的奖励”。这个转变带来的效果极其显著因为训练数据里的“正样本比例”不再是百万分之一级别而是直接变成可学习的分布。在 OpenAI 论文里同样是机械臂推方块任务普通 DDPG 训练了上百万步成功率几乎为零加上 HER 之后几十万步内成功率就到了 50% 以上。我自己的复现实验中FetchPush 这个环境普通 DDPG 跑 100 万步成功率不到 5%加 HER 后 40 万步左右就能到 80% 以上训练时间整整少了一半还多。这就是重标记机制带来的本质优势它把高成本的环境交互重复利用了好几次。3. 从零搭建 HER 训练流程代码逻辑与超参配置3.1 环境选择先用 FetchPush 跑通第一版如果你想快速上手 HER我强烈建议不要一上来就上真机先用 OpenAI Gym 的机械臂系列环境验证。FetchPush、FetchReach、FetchPickAndPlace 这三个可以用来把算法流程跑通环境观测里已经包含了achieved_goal和desired_goal字段省去自己设计目标表征的工作。以 FetchPush 为例任务是控制机械臂把一个方块从起始位置推到随机目标点。观测维度是 25 维其中前 10 维是机械臂关节状态第 10 到 13 维是方块位置第 13 到 16 维是需要达成的目标位置形如(x, y, z, relative)。这类环境用的是goal-conditioned的标准接口重置环境时必须传入一个 goal训练循环也要显式地把 goal 拼到输入里。3.2 网络结构与算法搭配DDPG 依然是经典选择谈起 HER 的标配算法大家首先想到的几乎永远是 DDPG。原因不复杂DDPG 天然适合连续动作空间机械臂这类控制任务的全是连续输出而 DQN 要在连续动作空间上做离散化精度和维度都会变得很难受。SAC 也能和 HER 搭配而且因为自带熵正则探索能力更强训练出来的策略会更平滑代价是超参更多、训练更慢一点。以 DDPG 为例Actor 网络输入是(state, goal)拼接后的向量输出一个 4 维连续动作Critic 输入是(state, goal, action)三者的拼接输出一个 Q 值。结构上不需要太花哨两层 256 宽度的 MLP 足够跑通重点在训练技巧而非网络深度。在实现上要特别注意一个官方实现里常见的细节输入归一化。state和goal的量纲和范围并不完全一致——机械臂角度在正负 90 度之间物体坐标在桌子范围内但网络对输入尺度是很敏感的。建议所有进入网络的state和goal都做一个均值方差归一化或者在环境交互后额外做一层归一化处理否则梯度更新容易被某几个维度带偏。3.3 一份可复用的训练伪代码下面这段伪代码是我从项目里抽出来的核心结构省掉了网络实现的机械细节保留训练逻辑的完整骨架# 初始化策略网络 actor、价值网络 critic以及对应的 target 网络 # 初始化经验回放池 replay_buffer for episode in range(max_episodes): # 1. 采样目标并重置环境 goal sample_goal() obs env.reset(goalgoal) # 2. 跑一集收集整条轨迹 episode_transitions [] for step in range(max_steps): action actor.choose_action(obs, goal, noiseTrue) next_obs, reward, done, info env.step(action) episode_transitions.append((obs, action, reward, next_obs, goal, info[achieved_goal])) obs next_obs if done: break # 3. 核心目标重标记 achieved_goal_final episode_transitions[-1][-1] # 最后实际到达的位置 her_transitions [] for t, (s, a, r, s_next, g, ag) in enumerate(episode_transitions): # 保留原始目标样本 her_transitions.append((s, a, r, s_next, g)) # 额外生成 K 条重标记样本目标可以是 future 策略 for _ in range(K): if random() future_prob: future_idx random.randint(t 1, len(episode_transitions) - 1) g_her episode_transitions[future_idx][-1] else: g_her achieved_goal_final r_her 0.0 if is_success(g_her, next_obs[achieved_goal]) else -1.0 her_transitions.append((s, a, r_her, s_next, g_her)) replay_buffer.add(her_transitions) # 4. 从缓冲池采样并更新网络 for _ in range(n_updates): batch replay_buffer.sample(batch_size) # 计算 target Q 值更新 critic随之更新 actor # 软更新 target 网络参数这段伪代码有几点值得展开说is_success的判断不是“目标坐标重合”而是距离小于某个阈值。FetchPush 环境里默认距离阈值是 5 厘米重标记样本计算奖励时必须用这个阈值重新判定。修改阈值会直接影响训练难度阈值太严重标记样本也学不到东西太松策略学到的是“差不多就行”的精度这个值建议直接用环境默认值先不要动。3.4 我调过的一组稳定超参把超参放出来之前先说明一件事不同任务、不同随机种子最优超参一定有差异但下面这组在我复现 FetchPush、FetchReach、FetchPickAndPlace 以及一个简易真机推块任务时都表现稳定可以当成起步配置。参数名推荐值说明K4每条原始轨迹额外生成的重标样本数future_prob0.8使用 future 策略采样新目标的概率replay_buffer 大小1e6越大越稳但占内存也更高batch_size256配合 1e6 缓冲池采样效率较好actor 学习率1e-3Adam 优化器critic 学习率1e-3Adam 优化器gamma0.98稀疏长任务里略小于 0.99 更好polyak 软更新系数0.95target 网络的更新强度探索噪声 stddev0.2初始随机噪声标准差每集更新次数 n_updates40每集结束后从回放池采样更新次数max_steps50单集最大步数FetchPush 用 50 足够这套参数下训练 40 万步基本能看到 FetchPush 明显收敛。如果发现收敛太慢可以优先把n_updates往上调每次多更新几次。如果发现策略朝目标方向乱撞、成功率震荡得厉害多半是探索噪声 stddev 太大先降到 0.1 试一集。4. 常见问题与排查技巧实录4.1 训练了很久 Loss 不降成功率一直趴地遇到这个问题第一反应往往是“网络结构不对”或者“学习率太小”但在我排查过的 case 里出现频率最高的原因其实是对目标空间的采样出了偏差。检查顺序先确认重标记之后的goal是否还在合法范围内——比如方块坐标越界了目标直接跑到桌子外面策略自然学不到正确动作。再看future_prob是不是设得太高如果每个重标样本都从未来状态里挑目标几个月后经验池会被“近处目标”淹没远程目标反而成了稀有样本。最后检查奖励阈值是不是把is_success的判定写错了导致重标记样本里混入了大量“看似成功实则没达到”的噪音。这三个点逐项排查比我调任何网络参数都有效。4.2 离线评估还行在线测试直接翻车离线评估就是训练时周期性跑一跑测试环境在线测试是部署到真实环境中执行。出现这种落差最常见的原因是训练时用了较大的探索噪声策略学到的动作带上了一层随机抖动。真实机械臂或者物理仿真对动作平滑度有要求抖动会导致执行误差累积成功率骤降。解决方案是测试时直接把噪声设为零并且在训练后期把噪声标准差逐步衰减到 0.02 以下。如果问题依然存在另一个嫌疑就是观测延迟真机上相机采集到目标位置之后再去控制机械臂中间有几十毫秒延迟而仿真里默认是同步的。解决办法是在动作执行时做一个简单的预测补偿或者把环境步长调慢让控制器有足够反应时间。4.3 什么场景不适合 HER不是所有稀疏奖励都能用HER 有使用前提这个前提写到代码里就是一句话目标必须能从状态中推算出来。如果你要解决的问题是“机器人把衣服叠好”这种抽象目标状态向量里没有一个和“叠好程度”对应得上的连续变量重标记就没有资源可用。如果你能定义出一个“进度分数”但分数本身不是状态的一部分HER 也无法自动生成目标。这类问题更适合考虑用别的方案比如设置课程学习、引入演示数据或者改用更接近“编辑目标”的其他目标条件算法。判断这事的最好时机是项目开始的第一天如果环境观测里都拿不出一个achieved_goal字段那 HER 就根本不适用别硬套。4.4 一个容易忽略的细节重标记样本的奖励分布漂移这个问题是我在实际训练中踩出来的重标记样本的奖励不是独立计算的目标换掉以后“达成目标”的判定依赖于同一个阈值函数。如果阈值函数本身在不同目标之间不一致——比如正方形目标区域容易满足长条形目标区域难满足——那重标记出来的奖励噪音会很大策略就会被某几个宽松目标带偏。建议在写重标记逻辑的时候先写一个单测随机抽几条轨迹离线重标一遍打印奖励分布和成功比例。如果成功比例偏低怀疑阈值有问题如果偏高怀疑目标采样太保守。这个检查步骤只花十分钟却能省掉后面一整天的调参时间。4.5 一点和算法本身无关但极其关键的内存问题因为 HER 会在一集结束后额外生成 K 倍的经验经验池的写入量瞬间变多。我最初实现时用的是 Python list 加 append训练到几十万步之后采样速度肉眼可见地变慢最后发现是内存碎片加上 list 扩容导致的。现在我用的是collections.deque配合固定大小的 numpy 数组分段存储采样时直接从各段均匀抽取速度快了三倍以上。这个问题不涉及算法原理但在长时间训练时体验差别特别大建议提前设计好。5. hindsight 思想的辐射从 HER 到更广泛的目标达成学习5.1 一些后续工作与变种思路HER 作为“目标重标记”范式的开山之作后续衍生出的变种非常多核心差异都集中在“用什么样的目标来替换原目标”上。能量函数版的目标重标算法不再随机挑achieved_goal而是根据一个能量函数挑选“仅比现状难一点”的目标让策略处于“跳一跳够得着”的学习区间这有点像课程学习的自动版本。还有工作把 HER 和模仿学习结合把重标记扩展到了动作维度观察到状态和动作都后来发生的就认为“当时那套动作也算是达成某个中间目标的一种方法”顺带解决了动作维度稀疏的问题。另一个方向把 HER 用于多智能体环境两个智能体之间互相给对方的目标做重标记从对方的经验里学到协作策略。5.2 这类算法进化的共同逻辑回头看这些工作会发现它们的进化逻辑几乎一条主线参数空间往经验空间压榨效率。原始 RL 靠环境试错拿到一条经验这条经验的价值如果只用一个目标去衡量那绝大多数经验都是废品重标记类的算法本质上是在重新定义“这条经验原本教会了我们什么”。谁能在不增加环境交互的前提下更充分地利用已有经验谁就能在真实物理系统这类交互成本极高的场景里胜出。这条逻辑在 HER 之后的很多目标条件强化学习文献里反复出现读论文的时候看到目标重标、能量重标、自动寻路重标这些关键词都可以往这条主线上归理解起来会顺畅很多。5.3 我的一个判断HER 这类算法适合真实机器人场景仿真里跑 HER 已经很成熟但它真正的价值洼地在真机学习。真实机械臂试错一次的成本是仿真里的几百倍电机磨损、碰撞风险都意味着“少交互”比“算得快”重要得多。HER 能在一分钟内把这几分钟才跑完的轨迹复用好几遍同时因为不需要额外的手工奖励设计它特别适合那种目标定义清晰但奖励难以手工设计的任务。如果你手头有真机环境或者准备把仿真里训练好的策略迁移到真机HER 作为数据增强层的性价比极高比堆网络参数量有效得多。6. 实操心得与一点额外建议写到这里HER 的原理、实现、调参和排查都聊完了。最后分享几个我反复用到的小经验。第一复现 HER 时不要上来就追求“最高成功率”先追求“训练过程能稳定看到成功率爬坡”的感觉——只要爬坡趋势存在后面怎么改都有据可依。第二K 值和 future_prob 这两个超参是 HER 区别于普通 DDPG 的核心其他超参沿用现成算法库的默认值就好这两个参数值得专门做一组小网格搜索。第三如果跑了几个任务之后发现 HER 在你的领域里效果不如论文明显先检查环境里“目标”和“状态”的耦合程度这个前提条件的重要性怎么强调都不过分。还有一个小技巧训练过程中把重标记之后的样本和原始样本分别统计成功率打印两条曲线。原始样本成功率代表真实任务达成率重标样本成功率代表算法对“成功信号”的感知能力。两线都上升说明策略确实在进步如果只有重标线上升原始线不动说明策略被拉到了目标分布的错误方向上这时需要降低 K 值或者提高 future_prob 让目标分布更贴近真实任务。这招比我用过的任何早停、模型保存策略都直观。希望这篇文章能让你少踩几个坑顺利用 HER 把稀疏奖励这个老难题按在地上摩擦。
返回列表