ARTICLE DETAIL

资讯详情

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

强化学习稀疏奖励困境:HER算法原理、实现与实战解析

强化学习稀疏奖励困境:HER算法原理、实现与实战解析 做强化学习的朋友应该都遇到过这种情景策略网络跑了几十万步loss曲线像一条平躺的直线成功率始终在个位数徘徊。你反复检查代码环境没问题reward计算也没问题问题就出在reward本身——稀疏到几乎给不出有效梯度。这个坑Hindsight Experience ReplayHER可以说是专门用来填的。HER的核心思想非常反直觉既然智能体没能到达目标那就反过来把轨迹里真正到达的状态当作新的目标重新学习。这篇博文从原理、实现、调参到实操把“hindsight”这件事彻底讲透适合正在做强化学习、尤其是被稀疏奖励折磨的读者参考。它能解决什么问题本质上是给强化学习加了一副“后视镜”让智能体从每一次尝试里最大化信息量即使那条尝试从原始任务角度看是失败的。我当年第一次接触HER是在一个机械臂抓取项目里目标的观测空间是三维坐标环境只在机械臂末端进入目标范围10厘米以内才给1奖励其余时间全部是0。普通DDPG训练了5000个episode成功率一直在2%以下。换成DDPGHER同样的网络、同样的超参数3000个episode就到了50%以上。当时给我的冲击很大——原来RL调参不是只能靠reward shaping硬拉。1. hindsight是什么吃透“事后经验回放”这件事1.1 一句话先讲明白为什么叫“事后聪明”hindsight在英文里的意思是“后见之明”说白了就是事情发生之后才恍然大悟。放到强化学习里HER想表达的是一个非常朴素的观点一条失败的轨迹对当前目标来说没用但对“它实际到达的状态”来说它是一段完美的成功轨迹。传统RL的做法是把轨迹和目标绑死给定目标g跑到目标就奖励没跑到就算失败然后把数据丢掉或者只用它算policy gradient。HER打破了这个绑定它把轨迹里出现的某个状态s_t拿过来把s_t的观测值当作一个新的目标g重新计算reward再存入经验回放缓冲。这样同一条轨迹至少能变成多条有效训练样本其中相当一部分是“正样本”。这个想法能成立依赖一个关键假设智能体每次尝试所实际到达的状态本身就是从环境的状态分布里采出来的是真实可实现的所以把它当作目标是合理且可学的。换句话说HER不是在创造虚假数据而是在给真实发生的经验重新赋予意义。1.2 稀疏奖励到底难在哪要理解HER的价值得先理解“稀疏奖励为什么麻烦”。奖赏稀疏意味着绝大多数时间步的reward都是0偶尔出现一个非零信号。用这样的奖励信号去更新Q值或者策略梯度存在两个问题。其一信号本身稀疏TD误差大部分时间很小甚至为0网络几乎得不到有效梯度。其二“命中”事件本身概率极低尤其是在高维连续动作空间里随机探索很难撞上目标区域。我举一个具体例子机械臂把一个物体推到二维平面上某个随机坐标允许的最大范围是2.5米×2.5米目标范围半径0.05米那单次尝试命中的概率大约只有π×0.05²/(2.5×2.5)≈0.00126也就是千分之一多一点。你指望靠随机探索在几万个episode里学到东西几乎不可能。这也是为什么很多RL项目最后的归宿是“狂写reward shaping”用各种中间引导信号硬把智能体拉向目标。但这样容易引入局部最优也容易让任务定义变形。而HER绕开了这个问题它不需要一开始就知道怎么成功只需要知道怎么失败然后把失败的轨迹变成“朝向某个可实现目标”的成功轨迹。这个思路本身就非常值得玩味。2. HER的核心思想把失败轨迹变成教学数据2.1 目标条件策略让智能体学会“瞄准”HER不是随便找个场景就能套的它对任务形式有个前置要求任务必须是目标条件的goal-conditioned。也就是说策略不仅要看当前状态还要看目标状态输出动作时以“当前状态和目标状态”为输入。传统策略是π(a|s)而HER使用的目标条件策略是π(a|s, g)其中g在每个episode开始时采样。训练出来后它得到的不是一个只会执行固定任务的策略而是一个“知道怎么从当前状态走向任意指定目标”的策略。有点像你教一个新员工不是只教他完成某一张工单而是教他做“接到任何工单都能按流程处理”的通用能力。这样的策略天然具有迁移性在执行过程中如果实际轨迹偏离了原始目标HER可以把偏离到的位置当作新目标送给策略告诉它“你现在走的这条路对于这个新目标来说是完全成功的”。策略就能从这条路径里学到“推进到新目标”对应的动作模式。这也是为什么说HER的价值不只是解决稀疏奖励更在于训练出一个更通用的目标驱动智能体。2.2 重标注目标用实际到达的状态重新定义主线任务这一小节讲重标注的具体操作。假设一条episode的原始目标是g序列是{s0, a0, s1, a1, ..., sT}。我们从这个序列中挑出某个时刻t对应的状态s_t或者它的部分维度把它作为新的目标g然后遍历整条轨迹把每一步的状态和动作都重新标注成“目标是g”的数据并重新计算对应的reward。这个g怎么选最常用的是“future策略”也就是在t时刻之后随机挑一个状态作为g。为什么用未来的状态而不是过去的因为从t时刻开始策略后续确实到达了g所对应的状态这条轨迹的后半段就是从s_t附近逐渐接近g的过程因果上是成立的。如果随机挑一个之前的旧状态当目标后半段动作跟它没有因果关系学出来的策略就乱掉了。重标注之后这条“失败”轨迹就变成了若干条“成功”轨迹放进replay buffer里供训练采样。这也是HER相对于其他样本增强方法最不一样的地方它不对奖励做塑形而是对经验本身做标注。塑形是对任务打补丁重标注是对数据重新定向两者完全不是一个层级的东西。2.3 Reward函数设计重标注之后奖励信号从哪来很多人第一次实现HER时会困惑reward到底用什么其实HER对reward类型没有强制要求只要求“重标注后能重新计算reward”。最常用的有两种。一种是稀疏二值奖励当前状态与目标距离小于阈值则给1否则给0。另一种是稠密距离奖励reward -distance(state, goal)重标注后直接用距离计算。从经验看HER和稀疏二值奖励搭配最经典。原因很简单重标注后轨迹后半段恰好靠近新目标g二值奖励会给出大量1信号形成密集的正反馈。如果用了稠密距离奖励信号虽然平滑但可能在原目标和重标注目标之间产生奖励尺度不一致的问题导致Q值不稳定。我的建议是如果你的任务目标是位置、姿态这种低维连续量先用稀疏二值奖励配合HER效果最好如果目标是图像或高维特征再考虑半稠密奖励或辅助任务。这个选择在后面调参时非常关键很多复现不出效果的人问题就出在奖励定义上。3. 从论文到代码HER的实现细节3.1 主体算法流程与伪代码直接给一个我在实践中验证过可行的主体流程框架以DDPGHER为例。整体流程分四步采样原始轨迹、存原始样本、进行HER重标注、训练更新。for episode in range(total_episodes): # 1. 采样原始目标并生成一条episode g sample_goal() env.reset(goalg) episode_transitions [] for t in range(horizon): action actor(state, g) exploration_noise next_state, reward, done env.step(action) episode_transitions.append((state, action, reward, next_state, g)) state next_state if done: break # 2. 原始轨迹先存进replay buffer for transition in episode_transitions: replay_buffer.store(transition) # 3. HER重标注对每个时刻t额外采样k个新目标 for t in range(len(episode_transitions)): for _ in range(k): g_prime sample_future_goal(episode_transitions, t) state, action, _, next_state episode_transitions[t] new_reward compute_reward(next_state, g_prime) replay_buffer.store((state, action, new_reward, next_state, g_prime)) # 4. 随机采样batch更新critic和actor batch replay_buffer.sample(batch_size) update_critic(batch) update_actor(batch) update_target_networks()注意这里episode_transitions和replay_buffer是两个不同概念前者是当前episode内的原始序列后者是所有用于训练的历史样本。HER重标注必须在episode结束后做因为需要访问整条轨迹来挑未来状态。对policy gradient类算法比如PPO也能加HER但off-policy的Q-learning类算法DDPG、SAC、TD3结合得最自然。因为在on-policy算法里重标注后的数据分布不太一致需要额外处理重要性权重复杂度高出不少一般没必要硬来。3.2 四种目标采样策略怎么选论文里给出了四种g的采样方式区别在很多项目里直接影响训练效率。我整理成一个表方便直接对照策略名定义特点适用场景final取轨迹最后一个状态作为g目标离初始状态远路径长度大训练信号稳定机械臂到达类任务新手首选future取t之后随机一个状态作为g因果一致性好正样本多学习效率最高大多数连续控制任务episode取本条轨迹中随机一个状态作为g平衡性好避免过于集中轨迹长、目标可达性强的任务random从历史所有状态中随机取一个当g探索性强但因果薄弱容易学歪几乎不推荐单独使用我的经验是如果对任务没有把握先用futurek值取4成本收益比最稳。final适合那种几乎总是失败、轨迹末尾状态五花八门的极端稀疏任务episode适合轨迹比较长几百步以上的情况否则容易采样到离当前时刻太远的无关状态。random策略确实提供了多样性但很容易让模型学到“目标与世界状态无关”的错觉我用过一次之后就不再单独用了。3.3 实现中最容易埋雷的三个地方第一个雷状态和目标的维度空间没分清。HER重标注的目标要使用“状态空间中与目标相关的子维度”。如果observation里有机械臂关节角、末端速度、物体位置、手指开合等等而目标只是物体位置那重标注时只能把物体位置子向量换成g把整个observation当目标会引入大量无关信息训练效果会明显下降。第二个雷reward计算函数不统一。重标注前后的reward必须用同一个函数只是输入goal不同。如果阈值、距离度量写得不一致Q值会彻底乱掉。建议把compute_reward(state, goal)抽成纯函数任何地方都用它。第三个雷batch一致性。重标注之后所有样本的格式要完全一致尤其是next_state和goal的shape。我见过有人把原始transition和重标注transition混在一起维度不一样导致训练直接崩溃。建议在往buffer里塞数据前加一个assert或shape检查省得半夜debug找半天。4. 实操记录在机械臂推球任务上把HER跑起来4.1 环境与超参数设置我用OpenAI Gym里的FetchReach-v1环境举例这个环境经常被用来复现HER论文里的经典曲线。任务是把机械臂末端移动到桌面上某个随机位置观测空间包含机械臂关节信息、末端坐标等动作空间是六维速度指令。我的超参数设置如下参数取值说明算法DDPG HER与论文主实验一致actor网络MLP 256-256ReLU输入为obs拼接goalcritic网络MLP 256-256ReLU输入为obsgoalactionoptimizerAdamlr均1e-3critic和actor都用batch_size256每次训练采样replay buffer1e6存原始重标注样本HER采样策略futurek4每条轨迹多存4条重标注探索噪声高斯噪声sigma0.2训练中逐步衰减目标更新tau0.05软更新episode horizon50步每episode最多50步这套配置不是绝对最优但稳定性和复现性都很好。我的经验是第一次跑HER就用小网络和小参数起步别上来就堆几千层否则你分不清到底是不是网络容量不够才导致收敛慢。很多复现论文失败的情况其实不是算法问题而是超参被网络结构带偏了。4.2 训练过程日志分析我盯着训练过程看最有意思的阶段有三个。第一阶段前200个episode成功率接近0但注意critic loss不是一直不变的而是在某个区间小幅波动这说明replay buffer里已经有不少重标注后的正样本在起作用。第二阶段200-1000个episode成功率开始出现极低但存在的小脉冲偶尔一个episode里机械臂末端真的推进到了目标范围内。这个阶段千万别因为平均成功率低就放弃那些零星的命中正是重标注样本在指导策略逼近真实目标区域。第三阶段1000个episode以后成功率进入爬坡期如果画训练曲线能看到一个像“盖章”一样的快速上升段。到3000个episode左右成功率稳定在85%以上个别随机种子能冲到95%。这里说一个关键点同一个seed的成功率曲线有抖动是正常的不要因为它跌了一下就立即改超参先看它是否在同一个区间反复震荡、能不能恢复到高点。见过太多人训练到一半看到曲线掉头就乱调最后把原本能收敛的模型调崩了。训练曲线的抖动本质是探索噪声和Q值更新的自然波动给点耐心。4.3 与baseline的效果对比我用四组对照实验说明HER的效果同样的超参只改“是否开启重标注”和“采样策略”配置3000episode平均成功率DDPG无HER稀疏奖励1.2%DDPG HER(final)41.3%DDPG HER(future, k4)87.5%DDPG HER(random)23.1%这个结果和论文的整体趋势是一致的。注意一个容易误解的点有人把HER称为“免费的午餐”这句话对了一半。它确实不需要额外环境交互训练成本几乎没有增加但并不是不需要调参数。k值、采样策略、batch_size对效果的影响都不小尤其k值太小正样本不够太大又可能让buffer里重标注样本比例失衡。5. 常见问题排查与避坑5.1 加了HER还是不收敛先从这几处找原因HER不是表面看起来那么简单很多人在自己任务上复现时发现效果没有论文里好通常原因出在下面这几点。第一目标空间设置不合理。重标注的目标必须是“该任务里可区分且可达到”的量。如果目标维度太抽象、覆盖范围过大算法很难学到“朝向它”的通用表示。我建议画一下所有重标注目标在目标空间里的分布如果目标几乎覆盖整个空间说明采样策略或环境随机化有问题需要收敛一下初始目标的采样范围。第二策略网络没有真正把goal用起来。有些参考实现会把goal直接拼接concat到observation后面这个做法本身没问题但有个更隐蔽的错误goal和obs在拼接后网络权重分配可能让目标的影响被obs淹没。可以观察训练过程里当同一个state配上不同goal时动作输出差异是否明显。如果完全没差异基本就废了考虑把goal单独输入到网络的一个分支再在深层融合。第三reward阈值过严。很多任务里“成功”的判定阈值设置成了0.01甚至更高精度导致重标注后大部分样本仍然算失败正样本不够。阈值可以先用1厘米级别验证能不能学到东西再逐步收紧。稀疏奖励的阈值本身是采样密度和任务精度的权衡太严等于自己把信号堵死。5.2 目标空间太大重标注玩不转怎么办有一种经常出现的场景目标不是三维坐标而是7自由度的手臂关节角、或者整个画面的图像级表示目标空间维度高、范围巨大重标注后的目标与状态之间距离分布极其稀疏导致学不动。这在高维控制任务里非常常见。我的处理思路有三个。第一给目标降维。关节角可以只保留末端执行器的位姿信息图像目标可以用自编码器压缩成低维潜变量再作为goal。第二使用分层结构。上层决定子目标下层用HER学习如何到达子目标这是分层强化学习HRLHER的经典搭配能有效降低一次学习的目标空间维度。第三加课程学习。先固定一个容易达成的目标让策略先学到基础到达能力再逐步扩大目标范围。我个人最推荐第一种思路目标本身就是低维物理量时就别把冗余状态塞进goal空间。有时候问题不在算法而在任务建模目标空间选得好HER的效率能翻几倍。5.3 稳定性与坍塌问题训练中期成功率涨到一半突然崩掉也是HER的高频故障。常见原因之一是Q值高估。重标注样本里很多是二值1的reward如果critic把这些样本学得过于乐观actor会为了让Q值更大而做出激进行为导致成功率崩塌。处理手段很简单DDPG换TD3或者SAC或者把critic层数降一点再或者把target网络更新频率调低。还有一种情况是buffer里原始样本和重标注样本的比例失衡。比如每条轨迹都重标注4条原始样本反而成了少数派模型学到的东西几乎都是“目标就在当前状态附近”的分布泛化到真实目标时能力不足。我一般会把原始样本的重采样权重稍微提高一点或者设一个最低比例别让它完全被淹没。这里给一个快捷排查表按症状找对策症状可能原因建议处理整个训练成功率一直0%goal条件没接入网络或goal子空间划分错误检查输入拼接、划定观察中的goal维度前期正常、中期崩掉Q值高估或训练分布漂移换TD3/SAC或调低tau有提升但始终到不了60%重标注比例不够、阈值过严提高k值放宽成功阈值曲线剧烈抖动batch_size太小、噪声过大增大batch降低初始sigma6. 给你的建议与我的个人体会6.1 把HER当成设计任务的第一思考方式我做RL项目时养成了一个习惯拿到一个新的连续控制任务先问环境给的是稀疏还是稠密奖励。如果是稀疏第一反应不是堆reward shaping技巧去“骗”智能体成功而是先想这个任务能不能用goal-conditioned方式建模。如果能就直接给HER留好接口。事实证明很多看似很难的机械臂抓取、推箱、导航任务用HER重构一遍之后难度会下降一个量级。原因就在于它真正把“达成目标”的伯努利探索问题转化成了“学会走向各种可达状态”的分类学习问题。后者要平滑得多也更容易学到可迁移的策略。6.2 HER还能往哪里扩展HER的应用范围远不止机械臂。它后续被广泛用进了多目标强化学习的框架也和各种主流算法都结合得很好。比如off-policy算法族DDPG、TD3、SAC后面都成了HER的标准搭档图像输入的RL任务可以用编码器对图像做表征再用潜变量当goal处理视觉目标多任务学习则可以把任务id或任务特征作为goal输入让同一个策略处理多任务。如果你在做的正好是机器人操作学习、导航规划、游戏AI这类方向HER大概率能成为你工具箱里非常顺手的一件工具。它不需要改变环境定义只需要改数据和训练管线的组织方式工程接入成本很低收益却非常直接。6.3 最后一点经验踩过几次坑之后我把HER的实施原则总结成一句话目标必须是环境里真实可达的、目标空间必须与观测空间严格对齐、重标注的因果关系必须成立。只要这三点不出错HER几乎总能给稀疏奖励任务带来可观的提升。我这几年做RL实际项目越来越觉得很多“调参”本质上不是调参而是调对reward和数据的理解。HER教会我的不仅是算法本身更是一种看问题的视角失败不是没有价值关键是我们要不要给它标上另外一个目标。这个思路放到很多其他工程问题上也一样管用。
返回列表