ARTICLE DETAIL

资讯详情

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

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

HER算法:用后见之明经验回放破解强化学习稀疏奖励难题 1. 先聊个直觉为什么事后聪明能训练AI先回想一个场景你在停车场倒车入库来回折腾了五分钟最后还是停歪了。下车一看其实某个瞬间车子的位置离车位线刚刚好只是当时你没意识到。这种如果当初把目标定在那个位置上就好了的感慨就是 hindsight——事后聪明。2017年OpenAI的 Andrychowicz 等人在论文里把这种直觉搬进了强化学习提出了 Hindsight Experience Replay简称HER。这篇论文的出发点是很多现实任务里智能体根本拿不到奖励因为成功太难碰上了。机器人推箱子推一万次都推不到目标点那它学什么HER的思路非常简单粗暴既然这次没推到目标点A那就把这次的目标改写成它实际到达的位置B把一条失败的轨迹重写成一条成功的轨迹然后丢进经验池里训练。这样一来失败的尝试不再只是失败它变成了从当前状态到B状态的宝贵成功样本。这个思路解决的是强化学习里最让人头疼的问题之一稀疏奖励sparse reward。很多落地场景里奖励不是稠密函数而是只有成功或失败两种结果。没有HER这类手段训练基本靠彩票中奖式的随机探索数据效率低到没法看。所以HER一出来就在机器人控制、连续控制、游戏AI等方向被广泛使用尤其是与DDPG、TD3、SAC这类off-policy算法搭配效果非常明显。这篇文章我不会照着论文给你念公式。后面的内容都会围绕我自己在复现和使用HER过程中积累的经验来展开包括为什么它有效、目标重标记的几种策略怎么选、网络结构怎么写、以及哪些坑我踩过之后流血总结出来的。适合正在做强化学习落地、尤其是被稀疏奖励折磨的工程师和研究者看。2. 为什么稀疏奖励能把强化学习直接逼进死胡同2.1 先看奖励信号在RL里的分量强化学习的标准流程是智能体与环境交互根据状态、动作获得奖励然后用奖励信号更新策略。数学上目标函数是最大化期望累计回报。听起来很简单但奖励的具体形式决定了学习的难度和路径。稠密奖励相当于每个动作都有打分你靠近目标给一份奖励远离目标扣一分这样策略梯度几乎每一个时刻都有明确的改进方向。而稀疏奖励是一锤子买卖只有当动作序列恰好完成目标才给一个1其他时刻全是0。试想一下一个需要连续执行20步正确的动作组合才能拿到奖励的任务所有中间步骤的奖励全是0。这时策略梯度的方差大得惊人几乎没有有效梯度信号学习基本停滞。2.2 随机探索在稀疏奖励下的真实处境我们在训练初期通常依赖随机动作去探索环境。假设每步正确的概率是0.5那么连续20步全对的概率是0.5的20次方约等于百万分之一。也就是说一百万个episode里可能只有一两个episode能成功一次。就算你侥幸成功了这个成功样本在经验池里也只是沧海一粟策略网络的更新量淹没在大量的零奖励样本中。更扎心的是很多连续控制任务的动作空间是连续的正确动作组合的概率几乎是0。比如机械臂要到达一个精确的三维坐标点随机动作下别说100万步了1000万步都未必能碰到一次目标点。于是训练陷入一个恶性循环因为没有成功样本策略学不到东西因为策略一直随机乱撞更不可能遇到成功样本。这里还要提一个我常看到的误区有些人以为用更强的算法、更深的网络能解决稀疏奖励问题。实际上只要奖励信号本身是稀疏的算法层面的改进几乎都是杯水车薪。这就像你要教一个小孩射箭但只告诉他一件事射中靶心才算及格射偏了什么都不说。那他很可能永远在乱射。这时候正确的做法不是换个更聪明的老师而是想办法让射偏这件事本身产生教学价值。2.3 HER的切入点让失败成为老师HER的逻辑就是既然随机探索撞不到目标点A那我们干脆换个教法每一条轨迹结束后不去管它原本的目标是什么而是重新挑一个这轨迹里实际到达过的状态作为新目标并假设如果我的目标就是这个状态那这次尝试就是成功的。然后在这个假设下计算奖励重新构造一条经验存入回放池。这个技巧给训练带来的信号密度提升是指数级的。原来100万条轨迹里可能只有1条有效经过HER改造后几乎每一条轨迹都能产出至少一条目标达成的样本。策略开始能从失败的数据里持续学到东西后续再结合一些探索慢慢就能学会真正推向目标点。这就是HER的核心魅力——它不改变你熟悉的RL算法流程只改变经验池里的标签。你把一条轨迹的目标标签改写、奖励重算、重新入库训练信号立刻变密集。接下来我们进入具体机制看看这个改写标签到底是怎么完成的。3. HER的关键机制目标重标记的四种姿势与内部原理3.1 目标重标记到底改写了什么先定义一下术语。HER设定在goal-conditioned强化学习框架下策略不仅接收当前状态 s还接收一个目标 g输出动作 a目标是学会从任意状态出发达成目标 g。训练时每条轨迹由初始状态、动作序列、观测序列组成最终是否成功由最终状态 s_T 是否等于/接近目标 g决定。传统做法下这条轨迹如果没达到 g整条轨迹的奖励全是0。HER的做法是在这条轨迹结束后重新选一个伪目标 g并且 g 必须是轨迹里某个时刻实际到达过的状态。然后我们假设这条轨迹是在尝试完成 g重新计算每一步的奖励——因为轨迹确实到达过 g所以从达成 g的角度看它就是一条成功轨迹。这条重命名后的轨迹会被存入回放池供后续训练使用。理论上可以这样理解原本的轨迹是从 s0 到 sT 的一段记录它包含的是从出发点到实际到达点的成功经验。虽然它没能证明从s0到g可行但它证明了从s0到sT可行。而目标条件策略本来就要学会给定目标输出到达目标的动作所以任何一段实际发生过的转移只要是朝着某种可达状态的都能作为目标条件经验被训练吸收。用生活类比来说你约了朋友去商场A结果走错了路误打误撞到了商场B。虽然没达成原计划但这次出行确实证明了从家出发是可以到商场B的。下次如果有人问你B怎么走你至少可以给出一个可行方案。HER就是把这趟走错的行程记录下来并贴上去商场B成功的标签。3.2 四种重标记策略future、final、episode、random论文给出四种选择伪目标的方式我直接把推荐度放在前面策略伪目标选择方式我的推荐度适用场景final只取轨迹最终状态 s_T折中、最简单快速验证大部分任务够用future从轨迹中当前时刻之后的某个状态里随机选强烈推荐连续控制、机器人任务几乎是最优解episode从整条轨迹的任意状态里随机选一般任务需要中间状态也有参考价值时用random从所有已探索过的状态里随机选一个不推荐极少使用仅做对比实验时用为什么 future 效果最好因为它兼顾了可达性和渐进性。future策略会在轨迹中当前时刻 t 之后采样一个状态作为伪目标比如在 t5 时选 t17 的状态当目标。这样从 state_t 到达 state_17 之间的距离对比从起点到终点要短得多。而 final 每次选终止状态轨迹后期的那些步骤到终止状态的距离是0容易让策略学到维持现状的惰性行为。episode 是从全局任意位置取目标距离分布不可控方差大。random 更不用说很多状态下根本不可达选来等于在教模型学错误映射。一个常见疑问是future 到底选未来哪个时刻的状态论文里的实现是从当前时刻之后随机均匀选一个不一定要选很远。实践中我建议在一个较大的轨迹长度范围内随机采样例如在 t 到 T 之间均匀选这样既有多样性又保证目标是当前状态往后几步内可达的学习曲线会比较平滑。3.3 为什么HER必须搭配off-policy算法这可能是新手最容易忽略的点。HER的本质是修改轨迹标签再入库这完全是在经验回放层面做文章。而经验回放本身就是off-policy学习的标志性组件——你拿旧策略采样出来的数据更新当前策略。如果你用的是on-policy算法比如PPO、REINFORCE数据必须来自当前策略修改后的经验不满足分布一致性强行用会引入严重的偏差。所以HER几乎总是与DDPG、TD3、SAC这类算法搭配。出发点就是DDPG自家的经验池机制天然支持这种目标域的重构。实际使用中我最常搭配的还是TD3和SACTD3的延迟更新能抑制Q值过高估计SAC的熵正则项让探索更稳定两者结合HER都能在稀疏奖励任务里稳定收敛。3.4 为什么目标分布是把双刃剑理论上你可以把伪目标改写成任何状态但一定要考虑一个关键问题伪目标的分布必须尽量落在策略真实可能到达的目标分布附近。如果伪目标选得太远、完全不可达那么策略收到的指令就是完成一项不可能的任务梯度更新方向是混乱的。这也是为什么 final 策略在某些任务里不够好——它选的目标就是这条轨迹的终点虽然可达但往往太容易了尤其是在轨迹后期目标状态就在眼前模型学到的更多是原地摆烂而非主动接近目标。而 future 策略选择稍微有点挑战但可达的目标是一种更合理的课程学习难度逐步递增模型每次都学到略超过当前水平的能力。在实践中我还会额外限制伪目标选择的时间间隔。比如要求伪目标必须在当前时刻往后的若干步之内但不能是最后一步之后。这个限制可以避免模型专门去学最后时刻冲刺因为稀疏奖励任务的成功条件往往是持续接近目标而非最后碰撞那一下。4. 复现HER的实操笔记网络结构、超参数与细节取舍4.1 网络结构就是把goal拼进输入别小看这一步HER的目标条件设定中Actor和Critic的输入都要包含 goal。最常见的做法是将当前状态和 goal 向量直接拼接然后送入MLP。这个操作很简单但很多人在这里踩坑忘了拼接、拼接的维度顺序写错、或者goal没有做归一化就硬塞都会导致训练崩掉。我习惯的做法是import torch.nn as nn import torch class Critic(nn.Module): def __init__(self, state_dim, goal_dim, action_dim, hidden256): super().__init__() # 输入维度 state goal action 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 torch.relu(self.fc1(x)) x torch.relu(self.fc2(x)) return self.fc3(x) class Actor(nn.Module): def __init__(self, state_dim, goal_dim, action_dim, hidden256): super().__init__() # 输入维度 state goal 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 torch.relu(self.fc1(x)) x torch.relu(self.fc2(x)) return torch.tanh(self.fc3(x))有一个细节值得多说一句如果状态和goal的量纲差异很大比如状态是关节角度在[-1,1]goal是三轴坐标在[-100,100]你不做归一化直接拼MLP很容易被goal维度的高方差信号带偏。我一般会在环境包装层做一次min-max归一化或者用LayerNorm对拼接后的向量做一次标准化。这个操作的收益往往比调学习率还要明显。4.2 超参数推荐表直接抄作业我自己在多个连续控制任务上用过HER基于DDPG/TD3的稳定配置大致如下超参数推荐值备注actor学习率1e-3Adam优化器critic学习率1e-3不需要刻意比actor低折扣因子γ0.98目标条件任务中常用0.95-0.99目标网络软更新系数τ5e-3我常用0.005更新过慢容易回波动经验池容量1e6Hero任务里生成效率高容量要够大批大小256过小会导致目标重构样本的方差大每次环境的随机采样步数8-10用于初始化回放池伪目标重标记版本数2每条轨迹生成4个重标记样本比较常见探索噪声高斯N(0, 0.1)连续动作空间加噪声关于重标记版本数K论文里用的是每条轨迹额外生成K个伪目标版本默认K4效果普遍不错。但注意K太大内存占用成倍增加而收益在K4之后几乎不增长。K2到K4之间我更推荐4特别是在机器人任务里多样性帮助明显。4.3 回放池的结构目标信息要保存完整HER需要保存完整轨迹而不只是单步经验。因此在收集阶段我会用提前分配好的buffer列表记录每个episode的全部transition包括状态、动作、下一状态、目标、奖励、done。轨迹结束后再做重标记把新的transition重新放入统一的经验池。一个常见的失误是想节省内存只存状态和动作不存目标。这是不行的因为重标记时需要原轨迹里每个时刻的目标来计算新的奖励。建议在环境wrapper层面直接返回 goal 字段并在采集函数里保存完整字典结构避免事后找不到数据。奖励函数的写法也注意一下。HER环境里典型的目标条件任务是二值化奖励如果当前状态和目标状态的距离小于某个阈值奖励为1否则为0。实际编程中我建议不要用0/1而用0表示失败、1表示成功的稀疏信号同时配合一个距离指标做日志监控。这样你可以随时看训练过程中的平均距离曲线判断模型是否真的在逼近目标而不是只看reward曲线那种永远为零的绝望画面。4.4 训练循环中的关键细节训练循环本身和普通DDPG差不多但有几个细节会让结果产生数量级的差别训练前一定要收集一批随机探索数据填充回放池我一般会随机跑5万到10万步再开始更新否则一开始经验池里空荡荡采样出来的batch几乎全是重标记后的假样本策略容易震荡。Critic网络的target网络要延迟更新得比Actor更慢。在TD3里是Actor每2步更新一次Critic每1步更新一次在DDPG里则是软更新我会额外把Actor的更新频率调低抑制Q值高估。每次从环境中采集一批episode后不要立刻用这批数据做梯度更新。应该先把它们重标记、推入经验池再从经验池均匀采样。很多简化版实现为了省事直接用刚收集的数据来更新这其实破坏了off-policy的优势。观察一下目标网络更新时的Q值均值如果发现Q值均值持续上涨但实际成功率不涨说明出现了严重的过估计立刻检查是否忘了梯度裁剪、或者goal归一化有没有做。5. 踩坑记录与常见问题排查我流过的血你直接拿走5.1 常见问题速查表现象可能原因排查手段训练很久reward始终为0随机探索步数不够、伪目标距离阈值设置不合理打印平均目标距离曲线确认是否缓慢下降提高随机探索时长成功率突然飙升后又崩回去回放池更新策略有问题、目标网络更新太快降低软更新τ值检查是否过早使用了包含大量假目标的数据Q值持续升高但真实成功率不动Q过估计用TD3的延迟更新方式给critic加LayerNorm检查goal归一化学习死水一潭策略完全不变Actor的输入没有拼接goal或拼接错位打印Actor的输入维度核对状态目标维度是否匹配最终状态无法确定环境没有正确返回轨迹的最终状态检查环境reset时是否返回了终止状态在done判定时使用额外返回值速度很慢但是对的每条轨迹都生成多个重标记样本批量太大将K设为2或者减小回放池单批次大小但增加更新频率5.2 三个我真正踩过的深坑坑一把奖励写错导致HER学了等于没学。我曾经在一个机械臂环境里把成功条件写成了当前时刻状态等于goal状态但由于浮点数误差状态永远不等于goal于是成功率永远是0。HER本身不会帮你修正这个bug——它重标记时也用同样的成功判定所以所有重标记样本也全是0奖励。这时候你回看训练日志会看到平均目标距离明明在下降但reward曲线一动不动。排查了很久才发现是成功判定阈值写错了应该用距离小于某个容差而不是精确相等。这个教训是在引入HER之前先用随机策略或简单环境验证你的 reward 函数本身是合理的。坑二网络容量不够目标信息根本没进到Critic。早期我试过一个比较复杂的多指机械臂任务状态维度30目标维度7action维度9。当时图省事用了一个单层128维的Critic结果无论怎么调超参都学不动。后来把网络加深到256x256并加入LayerNorm效果立刻改善。原因其实不复杂goal-conditioned策略需要同时学习状态表征和目标表征以及两者之间的相对关系这比普通RL任务需要更大的函数逼近能力。如果你发现HER在你的任务上完全没效果首先怀疑是不是网络容量不够别急着怀疑算法本身。坑三随机探索太多回放池里全是垃圾。我有一段时间为了积累经验让它在训练初期随机跑了50万步。结果重标记后的样本确实很多但都是随机运动到达随机位置的经验几乎不包含朝某个方向持续推进的有用模式。后来我改成随机探索少量使用刚训练几步的模型进行采样混合策略进度明显加快。正确做法是先用随机策略积累一批初始数据启动训练然后尽早切换到用当前策略带着探索噪声采样的模式。HER的强项是改造已有轨迹如果轨迹本身太过杂乱它能榨取的信息也很有限。5.3 我对HER适用边界的真实看法没用过的人会觉得HER是万能神药用过的老手反而会更谨慎。HER最擅长的场景是目标条件明确、动作空间连续、状态可观测性好的任务。例如桌面机器人推箱子、机械臂抓取、简单的导航任务这类任务里目标天然可以用状态向量表示重标记逻辑顺理成章。但HER也有明显不擅长的领域。第一个是纯粹的策略游戏——比如围棋、星际争霸这种目标不是到达一个状态的任务HER无法定义状态目标。第二个是离散动作空间很大的组合优化问题HER重标记后的目标往往对应不到真实状态。第三个是目标状态不可观测的环境比如部分可观测环境里智能体根本看不到完整状态重标记的目标指向的是一个不完整的信息效果会打折扣。所以我的建议是想清楚任务是否属于goal-conditioned且状态可观测这个框架。如果答案是肯定的HER几乎没有理由不用如果不属于就别硬套接下来我会简单聊聊HER的后续演进方向。6. 后续演进与我的扩展尝试方向HER诞生的这七八年里后续有不少工作围绕它展开。Nachum等人提出的ECRExperience Replay with Relabeling在偏离策略更远的数据分布上做了更多探索也有人把HER和课程学习结合先让模型在容易的伪目标上学习再逐步过渡到真实目标。我自己尝试过的扩展是把HER与自监督目标生成结合在一起。具体做法是先用一个VAE对探索到的状态分布建模从中采样合理目标代替人工指定goal分布。这样可以在完全不设置手工目标的情况下让模型先在可到达状态上学到基础能力。这对一些目标空间定义模糊的任务很有效但也不是银弹VAE的训练稳定性本身又会引入新的麻烦。另一个我推荐实践的方向是把HER作为数据增强层放在SAC框架中而不是仅在DDPG或TD3里使用。SAC的熵调节机制配合HER的伪目标重标记能够在较早训练阶段就保持一定探索性避免HER环境中常见的“权值坍塌到少量策略模式”问题。不过在SAC里用HER时要格外注意重标记样本的奖励可能是假的而SAC的熵项会对所有样本统一优化假奖励样本的比例过大会影响策略熵的调节。我的经验是在SAC中把HER重标记样本的占比控制在30%以下其余用真实目标样本。7. 我个人的实操体会与最值得记住的一件事如果只让我保留一条关于HER的执行心得我会选这句话先确认奖励逻辑正确再谈算法改进。HER是一个非常优雅且实现成本极低的trick它把人类事后聪明式的学习方式引入了机器。相比设计复杂奖励塑形、大规模调参HER往往能用最小的代码改动解决稀疏奖励带来的核心困难。我见过太多人一上来就堆网络、调学习率、换算法结果忽略了HER本身或者忽略了实现里的最基本信息流问题——目标有没有正确传给网络、奖励逻辑在重标记后是否正确、归一化有没有做好。在你动手复现的时候我建议按照这个顺序来先在一个最简单的goal-conditioned环境中把HER的完整流程跑通再去挑战复杂的机械臂任务。从一开始就逼自己把轨迹存储、重标记、奖励重算、经验池采样这四块的代码逻辑理清。踩过几次坑之后你就会发现HER不是一个很难实现的算法而是一种需要你认真对待、但实现了就非常稳定的数据改造方式。最后分享一个小技巧在训练日志里永远同时打印真实目标成功率和重标记目标成功率。这两个指标是诊断一切问题的起点。如果真实成功率在涨说明你在做正确的事如果只有重标记成功率在涨而真实成功率不动那说明策略可能只是学会了在伪目标上做文章还没真正泛化到真实目标——这时就该检查目标分布的重叠程度或者提高重标记样本中真实目标样本的比例了。祝各位在稀疏奖励的世界里少一点随机碰运气多一点后见之明。
返回列表