ARTICLE DETAIL

资讯详情

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

强化学习中的事后经验回放HER:打破稀疏奖励训练瓶颈

强化学习中的事后经验回放HER:打破稀疏奖励训练瓶颈 这期的项目标题很简洁就一个英文单词hindsight。放在生活里它是“后见之明”放在强化学习里它就是那个大名鼎鼎的 Hindsight Experience Replay中文一般叫“事后经验回放”。我第一次听见这个词的时候还以为是讲心态的后来才发现它是一个能实打实提升智能体训练效率的算法。这篇就从我自己的上手经验出发把“hindsight”这个项目的来龙去脉、原理拆解、落地实现和踩坑记录都串联起来看完你应该能明白它到底是什么也能照着写出一份能跑的代码。1. 项目概述与核心思路解析1.1 一个单词背后的真实含义我第一次看到“hindsight”这个词是在一篇关于机器人操作训练的论文里。当时的第一感觉是这不就是“事后诸葛亮”吗但放在强化学习里这个词的意义完全不同。它指的是让智能体学会从“失败”的经验里挖出“成功”的信号换个视角去看待一段已经发生的轨迹。很多强化学习任务有个让人头疼的问题稀疏奖励。智能体在环境里乱撞半天可能只在最后一步拿到一个reward甚至全程都是0。这种情况训练起来特别痛苦因为梯度信号太弱了学习速度极慢甚至可能根本学不动。hindsight这个项目或者说这个算法就是专门来破解这个局面的。它的名字起得很贴切既然已经经历完了这段轨迹我们不如回到过去把原本没有达到的目标“修改”成实际达到的状态然后告诉智能体你看这样也算成功。这种“事后改写目标”的操作就是整篇代码的核心灵魂。1.2 这棵“树”解决的是什么问题可以把强化学习理解成一个孩子学投篮。孩子每次投篮只有投进了才会有激励没投进就是零反馈。如果这个孩子只能靠“进球”来获得反馈那他练上很久都不知道该往哪个方向调整。真正高效的练习应该是哪怕没投进也要知道这次投偏了多远往左一点还是往右一点。hindsight做的就是这件事它把“没投进”的那次出手也定义成“投中了另一个假想目标”。这样智能体每走一步都有可以学习的信号梯度不再稀疏训练自然就快起来了。所以它的核心价值不是让奖励变多而是让每一次失败都有意义。2. 核心技术原理详解2.1 从标准Replay到HER的关键跳跃先回顾一下标准的经验回放Replay Buffer是怎么工作的。在每个时间步t智能体会把当前状态s_t、动作a_t、奖励r_t、下一个状态s_{t1}以及目标g打包存进缓冲区。训练的时候随机抽一批出来更新Q网络或策略网络。这个流程看起来没毛病问题出在奖励r_t的定义上。在稀疏奖励环境里r_t的绝大部分值都是0或者-1只有极少数的状态能拿到正奖励。Q函数的学习依赖这些奖励信号如果全缓冲区里就没有几个正样本价值估计就会非常不准。hindsight的思路是在存储的时候做一次“数据的再加工”把每一条经验里的目标g换成“这段轨迹最终到达的状态g”然后重新计算奖励。比如一个机械臂任务目标是把物体推到坐标(0.5, 0.3)。智能体一路操作最后物体停在了(0.2, 0.8)。原始的这条轨迹只拿到-1的奖励。但HER会额外生成一条新经验目标被替换成(0.2, 0.8)而这个目标确实被达成了所以奖励是0。这样做的好处是智能体不只学到“原来的目标做不到”还学到了“达成这个中间目标需要哪些动作”相当于把一条废弃的数据盘活了。2.2 为什么策略学习会因此受益在设计上这种“目标重标记”并没有改变环境本身的动力学只是改变了训练数据的分布。因为数据是从“真实的物理过程”里采出来的动作和状态转移都遵守实际环境规则所以重标记不会引入虚假的数据。它引入的是“额外的教学目标”而这些教学目标虽然不一定和当前任务相同却能让智能体学到可迁移的操控能力。打个浅显的比方你本来想学做一道红烧肉结果今天盐放多了。如果你只盯着“今天红烧肉失败了”这个结论你什么也学不到。但如果你换个目标告诉自己“我学会了一盘咸味炖猪肉的做法”那你只是没学会红烧肉却练出了咸味炖猪肉的手艺。这份手艺里“什么时候放盐”“放多少盐”的经验是共通的。HER就是帮算法建立这种“我练到了什么就学什么”的反馈通道。2.3 适用范围的边界并不是所有任务都适合直接上HER。它最适用的场景是multi-goal环境也就是说环境里智能体的目标是可以被参数化的比如一个坐标点、一个姿态角、一个布尔目标的组合。如果任务只有一个明确的二值结局比如赢或输没有目标向量可以替换那HER就用不上。这类环境的典型代表就是OpenAI Gym里的Fetch系列和Hand系列。FetchReach的目标是一个三维坐标FetchPush的目标是把物体推到某个位置FetchSilde则是击打和滑动。所有这些目标都可以用一个数组表示非常适合做HER的验证。3. 实操步骤实现一个HER算法3.1 环境选择与安装准备我做这个项目时用的是FetchReach环境。它需要计算机器人的末端执行器移动到指定位置动作是四维的关节速度控制状态空间包括了夹爪位置、物体位置、相对位置等一大串。第一次跑通之后再切换到FetchPush也能很快迁移。安装环境这块没什么特殊的直接装gym和mujoco-py就行。如果用的是OpenAI的仓库版本还可以引入他们提供的her封装好的基线代码。我这里用自己的PyTorch实现保证每个细节都在自己手里。3.2 网络结构与参数初始化网络结构上我采用标准的双Q网络加目标网络。输入分两部分状态和goal先各自过一个128维的全连接层然后拼接到一起再过两层256维网络最终输出每个动作对应的Q值。这样做可以让state和goal在低维空间里各自形成表征再融合决策比直接concat效果略好。超参数的初始选择我做了记录复制出来可以直接用参数数值备注折扣因子 gamma0.98值越小越关注短期收益学习率3e-4Adam默认偏好这种量级Q网络更新频率每步更新高频更新有助于稀疏奖励学习目标网络soft update tau0.950.95时目标网络更新偏慢稳定性更好每次采样的经验条数512条从replay buffer中随机抽512条HER重标记比例每批量中额外生成4条用future goal替换的经验比例太高容易学偏4条比较稳探索噪声高斯噪声标准差从0.2线性衰减到0.05前期增加尝试后期稳定执行3.3 经验回放池的设计HER的核心在经验池不放全就没意义。我采用了一个简单的字典结构来存数据每次迭代都追加保存每一帧的状态、动作、奖励、下一状态、目标以及“这一段episode最终到达的状态achieved goal”。如果只存到replay就完事那就不是HER关键是在采样品batches的时候额外生成“以最终状态为目标”的经验。伪代码如下def store_episode(episode_buffer): for t in range(len(episode_buffer)): transition episode_buffer[t] # 原始目标存入replay replay_buffer.add(transition) # 生成新的HER经验 for _ in range(her_n): her_goal episode_buffer[-1][achieved_goal] her_reward compute_reward(transition[state], her_goal, transition[next_state]) replay_buffer.add({ state: transition[state], action: transition[action], reward: her_reward, next_state: transition[next_state], goal: her_goal })这个循环里最关键的一行是her_goal episode_buffer[-1][achieved_goal]这行代码取的是该episode最后实际到达的状态。把这行数据“原路返回”给整个序列里的所有transition就等于给每一步都发了一枚奖章。3.4 训练循环与Actor-Critic交互整个训练循环大约分成三步收集数据、更新经验池、更新网络。每个episode开始时会随机抽一个目标让机械臂移动到指定位置。如果到达了目标就给0否则每一步都给-1。这样看到曲线的效果比较直观曲线是平滑的上扬从最开始的-50左右逐渐逼近-5甚至0。在更新阶段我从replay buffer里采样用DDPG的框架更新Actor和Critic。DDPG里的actor输出的是一个确定性的动作再叠加噪声做探索。Critic则负责给state-action对打分。HER只引入数据流的变化不改变网络更新的主体逻辑所以只要把数据和标准DDPG的更新代码连上就能用。# Critic更新 with torch.no_grad(): target_q target_critic(next_state_all, target_actor(next_state_all)) target_q rewards gamma * target_q critic_loss F.mse_loss(critic(state_all, action_all), target_q) critic_optimizer.zero_grad() critic_loss.backward() critic_optimizer.step() # Actor更新 actor_loss -critic(state_all, actor(state_all)).mean() actor_optimizer.zero_grad() actor_loss.backward() actor_optimizer.step()这段代码看似没做任何“花哨”的东西但正因为经验池里已经混入了HER生成的数据actor和critic能学到多条价值路线。尤其是在目标成功概率很低的初期HER能让训练曲线在一两百个episode内就有明显抬头。我在实际跑普通DDPG和HER对比时HER大概提前了将近60%的episode才开始收敛。3.5 评估与模型保存我有一个习惯是每50个episode做一次离线评估固定一组测试目标不添加任何探索噪声让actor直接输出动作来完成任务。统计成功率一旦成功率超过某个阈值就保存模型。如果只是看训练reward容易骗自己落地到评估任务上才能看到真实水准。对于机器人这类连续控制任务保存模型不只要保存网络权重还要把训练时用的归一化参数也存下来。网上很多复现代码忽略了这一步结果你加载模型后发现它表现忽高忽低排查半天才发现是obs的mean和std没有保存。我在项目里用了一个简单的配置类来存这些信息。4. 常见问题与排查技巧实录4.1 目标替换比例带来的偏差HER并不是越多越好。比例太高会导致一个局面智能体大量学到“乱动也能到达某个地方就能拿reward”却没学会如何去逼近真正的指定目标。我的经验是在一个episode里的每条transition新生成的replay数量控制在2到6条之间总量不超过真实经验的2倍效果最稳。如果你观察到训练到后期评估成功率停滞不动但训练reward却一直在涨那大概率就是目标替换比例偏高导致策略过度拟合“中间状态”而忽略了原始目标。这时候把her_n调低甚至只对后半段轨迹做重标记都会有奇效。4.2 稀疏与稠密奖励的曲线形态差异有些读者会纠结我的任务里既有稀疏奖励也有稠密奖励怎么办我的回答是如果环境能给出稠密奖励那没必要用HER直接普通的replay也够用。HER的价值就在于“只有0和-1”这种贫瘠信号。如果你自己设计环境一个常见的做法是奖励函数写成分段式距离小于阈值给0否则给-1。这种设计配合HER在训练初期就能看到价值网络快速成形。4.3 训练震荡和Reward爆炸问题我在训练过程中遇到过网络崩溃的情况具体表现是loss突然变成NaN或者Q值直接冲上百万。这类问题的根源往往是reward没有做归一化或者是目标网络软更新太慢价值模型在高速更新中失去平衡。我的解决方式很简单对reward做clip比如限制在[-1, 0]之间HER中这么设天然合理将tau调整为0.95甚至0.99让目标网络的更新速度更慢一些。它慢了主网络反而更稳对梯度做clip设max_grad_norm1。这几个小改动看起来不起眼但它们能避免绝大多数“前期没事后期突然崩坏”的怪病。我最初直接套用标准DDPG的默认设置跑HER训练到4600多个episode时模型突然发散后来靠grad clip救回来的。4.4 评估成功率与训练reward走势不一致如果你发现训练时一直显示reward-20但评估时成功率很高先不要惊慌。这不是模型出了问题而是因为训练时加了探索噪声噪声会让稳定操作变差。评估时去掉噪声成功率自然高起来。反过来如果评估时成功率也低就去看actor的输入是不是目标goal没有被归一化我早期犯过一个低级错误把state做了归一化但goal没做导致网络输入的空间尺度差了好几个数量级训练根本走不动。后面先对各个维度计算mean和std统一做归一化性能立刻上了一个台阶。这个细节在官方代码里可能一笔带过但实操中特别影响结果。4.5 硬件资源与并行效率HER训练对显存要求不低尤其是用256维隐藏层还开双Q网络时显存占用轻松超2G。如果你像我这样在一张笔记本显卡上跑建议把batch_size从512降到256然后把compute_reward的效率调高一些经验池可以用numpy数组减少Python对象的开销。如果预算允许可以用vectorized环境并行采集经验也能显著加速。不过要注意HER里同一个episode内的时间顺序很重要并行采集时要保证每个子环境独立存储自己的episode数据不要混在一起。这个坑我只踩了一次就长了记性经验池的数据来源一旦被交叉污染得到的“最终目标”就是别人的目标那HER就完全失效了。5. 几个值得反复琢磨的扩展方向跑通FetchReach之后我又尝试把HER用到了FetchPush和FetchSilde上。这里的难点变了不只是移动机械臂本身还要学会利用物理交互去推物体。目标状态不再只是末端执行器坐标还包括物体位置。在这种环境里HER的效果依然立竿见影但需要更多次目标替换来生成足够多的“成功轨迹”。另一个有意思的扩展是让HER和SAC结合。我试过把SAC的熵系数和HER的目标重标记接在一起收敛速度比DDPGH教育还要快一些因为SAC本身的探索机制更适合稀疏奖励。如果你对off-policy算法家族很熟这里其实有非常多的排列组合可以试每次都能碰撞出不同的学习曲线。还有一个方向是给HER加上“goal scheduling”。比如先让智能体学习中间目标再逐渐过渡到原始高难度目标。这个想法来自课程学习虽然额外增加了调度代码但可以把训练成功率再往上推几个百分点。我目前只做了最朴素的固定比例HER这部分可以作为后续实验的进阶选题。最后一点个人实战心得如果让我重新做一次这个项目我会从一开始就把可视化做好不只是存曲线还要把机械臂每一步的运动轨迹导出来做成视频对照每一阶段的训练进度。很多时候曲线看起来平坦但实际动作已经越来越流畅了光靠数值看不出来。另外一个非常关键但容易漏掉的操作是定期清空replay buffer或者大幅调整采样分布。HER的数据分布会随着训练推进快速偏移早期的边缘经验后期价值不大。适当地用“加权采样”优先抽取近期成功轨迹会让训练过程更平滑。我第一次做这个实验的时候没做任何处理四千个episode后成功率还在30%附近挣扎后来加入“近期成功优先采样”后成功率直接冲到70%以上。“hindsight”这个名字值得品一品。它看起来说的是“事后聪明”其实说的是“实验之后不要浪费每一帧经验”。做强化学习尤其需要这种心态失败不是对手而是一笔资产。只要能把失败的数据换个角度重新利用它就能成为通往成功的台阶。希望这篇记录能让你们少走一些我走过的弯路也把这份“事后经验”变成你们的“前行动力”。
返回列表