强化学习稀疏奖励实战:Reward Shaping、课程学习与分层RL详解 做强化学习项目最让我头疼的从来不是网络结构而是训练曲线的“一动不动”。尤其是 Sparse Reward稀疏奖励场景智能体跑完一整个回合绝大多数时间收获的奖励都是 0只有极少数时刻才冒出一个 1 或者 -1。Reward Shaping、Curriculum Learning、Hierarchical RL这三类方法是我在解决稀疏奖励问题时最常用的工具箱这篇文章我就用实际调参的视角把它们的原理、落地步骤和常见坑一次讲清楚。读之前先说明这不是教科书式的内容我不会把一个公式反复解释三遍也不会把环境包装得光鲜亮丽。下面所有经验都来自我在室内导航、机械臂抓取这几个模拟任务上反复试错的过程。如果你正被稀疏奖励折磨或者准备在下一个项目里引入相关方案这篇内容应该能帮你少走几条弯路。1. Sparse Reward 到底难在哪从“全程零奖励”说起1.1 一个让训练曲线纹丝不动的模拟场景我在一个室内导航模拟环境里做过一次很简单的实验20m×20m 的方形空间智能体从左上角出发目标区域在右下角半径只有 0.3m。到达目标区域给 1其他所有动作都没有奖励。看起来很简单但整个环境有 400 平方米目标区域面积不到 0.3 平方米智能体如果完全随机探索碰到目标的概率低得可怜。我一开始用的是比较流行的近端策略优化算法跑了一整晚回报曲线几乎就是一条水平线。一开始我还以为是超参数没调好后来仔细翻了日志才意识到算法没有坏是根本没有任何可用的学习信号。所有样本的奖励都是 0策略梯度算出来几乎是 0价值网络也学不到任何区分度。这个场景给我留下的印象很深稀疏奖励不是“难学”而是“缺乏可学的信号”它会让所有常见算法在统计意义上直接失效。1.2 稀疏奖励为什么会让策略梯度“学不动”如果你对强化学习算法有点了解应该知道策略梯度类方法的核心公式里梯度的方向是用“奖励加权的动作概率”来估计的。当奖励普遍为 0 的时候每个动作得到的加权值都一样梯度自然趋近于 0。即便偶尔随机碰到一次正奖励那个样本在庞大的采样批次里占比太小对参数更新的影响也微乎其微。价值类方法也不会好到哪去。Q 学习或者 Advantage Actor-Critic 这类算法需要靠 TD 误差把奖励一步步“传回”前面的状态。可就当所有状态的即时奖励都是 0 时价值网络的输出也全被推到 0优势函数变成零向量策略拿不到任何指令。一个比较贴切的比喻是全班考试所有人都交了白卷老师根本没办法从分数里判断谁在努力、谁在划水更谈不上给谁补课。1.3 先分清稀疏奖励、延迟奖励和伪奖励在动手设计解决方案之前我强烈建议先把问题归类。很多人一遇到“奖励很少”就急着上 Reward Shaping结果发现任务本身不是稀疏而是延迟或者干脆是奖励定义出了问题。问题类型典型表现信号密度优先排查方向稀疏奖励大部分时间步奖励为 0只有特定目标点给奖励极低设计中间信号、课程或分层延迟奖励内部其实有进度但奖励只在回合末返回低但存在阶段性反馈先检查时序信用分配再做塑形伪奖励每步都有数值但和真正目标无关甚至冲突高先重新梳理奖励函数别急着调算法我之前有一个项目每步都有 -0.01 的成本只有回合结束才给一个较大奖励。这不是典型意义上的稀疏奖励因为每一步的负信号都携带着“别拖时间”的信息问题更多是延迟用 n 步回报或者 eligibility trace 处理会更直接。如果一开始就把它当成稀疏问题去加额外奖励很容易跟原有的时间成本纠缠在一起反而把问题搞复杂。2. Reward Shaping让每一步都有“进度感”2.1 最简单的塑形为什么危险Reward Shaping 的思路很直观既然最终奖励太稀疏那我就在中间塞一些“进度奖励”。很多人上手第一件事是给环境加一条每靠近目标 0.1 米奖励 0.1。这个方法在简单任务里偶尔有效但非常危险因为它很可能改变原任务的最优策略。举个例子如果智能体的目标是走到某个点但你给“靠近目标”的每一步都加了正奖励那么理论上它就可以找一个永远不会真正到达目标、但能持续产生“距离缩小”的路径。更典型的翻车是智能体学会在目标区域附近来回横跳因为每跳一次都可以刷一次“靠近”信号最终到达目标的奖励反而变得无关紧要。这种问题在研究中被叫做 reward hacking本质上是目标函数被改写了。2.2 Potential-Based 塑形给奖励“装一个势能函数”为了既能加进度信号又不改变原任务的最优策略比较经典的做法是使用基于势能的塑形Potential-Based Reward Shaping。思路很简单你先定义一个状态势能函数 Φ(s)然后在状态从 s 转移到 s 的时候给额外奖励F(s, a, s) γ Φ(s) - Φ(s)其中 γ 是折扣因子。这个额外奖励的特殊之处在于它把状态势能的差值作为引导信号。因为势能差只跟状态有关跟当前要选择的动作没有直接关系所以在价值函数上相当于给每个状态加了一个固定偏移每个状态下各动作的排序不变最优策略自然不变。实际使用中Φ(s) 通常可以设计成“当前状态到目标的距离的负值”。如果离目标越近Φ 越大那么每走一步靠近目标F 就为正智能体就能感受到进步如果走远了F 为负智能体也会收到惩罚。这听起来和直接奖励“靠近”很像但势能差的形式带来的策略不变性是一个非常好的安全底裤。2.3 势能函数怎么选才实用理论保证了最优策略不变但选择合适的 Φ 仍然很考验工程经验。我常用的做法是先看任务能不能找出一个“单调的进度指标”任务类型常用势能 Φ(s)注意事项导航到达目标负的到目标距离注意单位缩放别让距离数值波动太大机械臂抓取末端到目标位置距离的负值要额外考虑姿态、碰撞、滑落等情况拼图/组合类已归位块数如果顺序不对这种势能容易造成局部最优连续控制任务完成百分比需要先定义离散里程碑在机械臂抓取任务里我曾经只把“末端到目标位置的距离”设成势能。结果机械臂很快学会了快速接近物体却总是把物体撞飞因为距离下降确实产生了正的塑形信号但任务没有完成。后来我把势能扩展成包含物体是否被抓牢、末端姿态是否合适效果才正常起来。这说明 Φ 不需要非常精准但至少要跟真正目标的“方向感”一致否则只会在训练早期把策略带偏。2.4 两个常见坑尺度问题和 reward hacking第一个坑是塑形奖励的尺度。如果原始任务奖励只有 1但塑形奖励每一步都能给到 0.5智能体就会更“关心”塑形信号而不那么在乎真正目标。虽然理论上有势能形式兜底策略不会跑偏太多但训练过程会变得非常慢因为智能体一直在刷塑形分。我一般会把塑形奖励的均值控制在原始奖励量级的十分之一到三分之一。怎么测均值拿随机策略或一个粗糙的手工策略跑几十个回合把塑形分量单独记录下来看一眼心里就有数了。第二个坑就是前面说的 reward hacking。比较典型的症状是智能体原地抖动、绕柱子画圈、反复进出某个子区域。这时候要立刻检查是不是势能函数奖励了“重复变化”而不是“单调进步”。比如距离势能是负距离理论上靠近正、远离负但智能体可以通过左右摆动制造出很多次“小距离下降”来刷正奖励。我通常会在设计上加入一条限制一个回合内同一个子区域只计算一次势能变化或者把势能函数改成随回合单调下降的形式逼着智能体只能往前走。3. Curriculum Learning给智能体搭一套“难度阶梯”3.1 课程学习的本质改变采样分布而不是改目标Reward Shaping 是从奖励层面给信号Curriculum Learning 则换了一个思路不改变奖励而是改变训练样本的分布。人类学习复杂技能时也很少一上来就挑战最高难度而是先学简化版再逐步接近真实任务。强化学习里的课程学习就是把这种“由易到难”的流程自动化或半自动化。关键点是最终要评估的任务仍然是原任务课程只是改变了训练时“初始状态、环境参数、最大步数”等设置。比如导航任务里可以先让智能体从距离目标 1 米的位置出发再慢慢变成 5 米、20 米机械臂任务里可以先固定物体位置再随机位置最后再加障碍物。这样做的核心价值在于智能体在简单阶段里能积累足够多的正样本学会基础技能然后再用这些基础技能去应对更难的分布。3.2 人工课程设计的最小可用方案如果不想上来就搞自动课程可以先用最小可行方案人工划分难度等级并用成功率作为切换条件。我在导航任务中常用的课程表长这样难度等级目标距离障碍物数量最大步数切换条件L12m0100连续 50 局成功率 90%L25m3200连续 50 局成功率 80%L310m6400连续 50 局成功率 70%L420m10800最终评估这里我故意把切换条件放在“成功率”而不是“平均回报”上。原因很简单平均回报会被塑形奖励、步数惩罚干扰成功率才是任务有没有被解决的最直接证据。还有一点很关键切换等级时不要立刻把旧等级的数据全部丢掉最好在经验回放里保留一部分上一等级的样本让新旧分布有一定叠加否则策略很容易被新分布的极端样本冲垮。3.3 Self-paced 课程的简单实现用失败率控制难度人工课程表在任务相对固定时挺好用但一旦环境变化频繁人工标定等级就很累。一个更实用的方案是自定步调Self-paced让智能体自己的表现来决定难度变化。核心是一个很轻的控制器难度系数 α 高了就调高难度低了就调低难度。alpha 0.0 rate 0.0 target_rate 0.7 lr 0.001 for episode in range(total_episodes): env.set_difficulty(alpha) reward, success run_episode(env, agent) rate 0.9 * rate 0.1 * float(success) alpha lr * (target_rate - rate) alpha min(1.0, max(0.0, alpha))这里面 alpha 是 0 到 1 之间的难度系数用来插值环境参数比如目标距离 2 alpha * 18。target_rate 是期望成功率一般放在 0.5 到 0.8 之间。成功率用滑动平均计算避免单局噪声过大。alpha 的更新步长要很小否则难度会剧烈震荡智能体刚适应一个难度又被甩到另一个难度。我踩过的一个很典型的坑是一开始把 lr 设成 0.05结果 alpha 几个回合内就从 0 冲到了 1相当于直接跳过了所有中间难度。智能体前期还没形成稳定的行为成功率立刻崩盘后面再调低难度也很难恢复。后来把 lr 降到 0.001难度变化平滑了很多训练过程才算稳定下来。3.4 我踩过的一个坑课程切换太激进导致能力回退另一个更隐蔽的问题是“课程切换太激进”。我在某个模拟抓取任务里L1 阶段已经做到成功率 85%我直接把环境切到 L3目标随机位置、多个障碍物结果成功率掉到了 20% 以下。最要命的是继续训练了好几天成功率始终在 30% 附近打转回不到之前的水平。后来分析归因问题不在算法而在于经验回放里的旧数据被新数据迅速稀释了。智能体在 L1 学到的东西被大量 L3 的高难度样本冲击后策略发生了灾难性偏移等于是把已经学会的“幼苗”连根拔起。解决办法是渐进式混合采样每个训练批次里L2 和 L3 的数据按比例混合比例从一个高旧低新的起点开始平滑过渡。这样策略能一边巩固旧水平一边适应新难度成功率最终回到了 80% 以上。所以做课程学习时与其关心“能不能切到最难”不如关心“分布切换够不够平缓”。4. Hierarchical RL把长时间跨度拆成“看得见”的子目标4.1 为什么要分层稀疏奖励的时间尺度问题有一些任务的奖励不是频率低而是时间跨度特别长。比如让机械臂从桌上拿起一个物体再放到指定托盘里这中间涉及接近、抓取、抬升、移动、放置好几个阶段任何一个环节出问题最终都等不到正奖励。Reward Shaping 在这里会变得很难设计因为你得先知道该为哪个阶段塑形Curriculum Learning 也不容易因为任务难度不是简单的“距离变化”而是环节之间的组合。这时候更自然的做法是分层。可以用一个很直白的类比写毕业论文真正的奖励是答辩通过但你在写每一句话的时候根本无法通过“这句话有没有让答辩更容易过”来获得瞬时反馈。你会主动拆任务查资料、列提纲、写初稿、改稿。Hierarchical RL 干的就是这件事高层策略负责决定现在应该完成哪个子目标底层策略负责把该子目标执行到位。4.2 三种常见的分层训练范式分层思路在不同项目里的落地方式差别很大我大致分成三类范式做法适用场景风险预训练式先逐个训练底层技能库再固定技能库训练高层子目标好定义、环境交互成本低技能库覆盖不足时高层会选到“废技能”端到端式高层和底层同时训练共享环境交互探索成本低、状态空间简单难收敛上下层容易互相甩锅交替训练先训底层再训高层再一起微调最推荐兼具效率和稳定性需要写额外的评估逻辑我实际项目中最常用的是“交替训练”。先为每个子目标单独训练一个底层策略比如“移动到门口”“抓取物体”“放置到托盘”底层练扎实以后再固定底层只训练高层选择子目标的策略最后再放开底层参数一起微调。这样每一步都有清晰的反馋来源不容易出现两个策略互相推诿的问题。4.3 子目标设计不能拍脑袋要可验证、可终止、可分解子目标听起来比奖励塑形干净但设计不好同样会翻车。我总结出三个硬标准可验证、可终止、可分解。可验证指的是子目标是否达成必须能被状态函数直接判定。比如“到达房间中心半径 1 米以内”可以判定“更好地探索环境”没法判定。可终止指的是每个子目标必须配超时机制。如果底层策略在 500 步内都没完成“移动到门口”高层必须能收到失败信号而不是永远等下去。可分解指的是高层的决策周期不能太短否则每个子目标只执行几步就切换底层根本学不到稳定技能。在实际操作里我更喜欢把高层动作空间设计成离散的 5 到 10 个选项比如“去桌子旁”“去门边”“抓杯子”。如果直接用连续向量作为高层动作底层策略往往会被高层频繁变化的目标牵着鼻子走很难收敛。高层给低层的应该是一个稳定的子目标描述而不是高层隐状态或者整条轨迹。4.4 分层训练中“上层甩锅”的排查思路分层训练最让人崩溃的瞬间是高层和底层一起训练时两个策略的损失都不降。这时候最忌讳的是无脑调学习率我建议按照下面这个链路去排查。先把高层冻结住用随机策略或者手工指定每个子目标单独让底层跑若干个回合看每个底层技能的成功率。如果某个底层技能成功率极低问题在底层就去修底层的奖励、终止条件或者网络容量。如果没有问题再冻结底层让高层自由训练统计高层选各个子目标后获得的回报。如果某个子目标回报很高但高层几乎不选说明高层探索不够或者子目标表示方式有问题如果所有子目标回报都很低那就回到底层重新检查技能库覆盖范围。我印象很深的一次排查中底层某个“抓取”技能表面成功率有 60%但实际抓取后物体马上就滑落。原因是终止条件只在“末端接近物体”时返回成功根本没有检查物体是否被稳定抓住。高层看到这个技能经常“成功”就一直选它最终任务始终完不成。把终止条件改成“物体中心抬起 5cm 且保持 1 秒后移动”底层才真正学到可用技能。5. 三类方法怎么选一张表看懂适用场景5.1 适用场景对照表到了真正选型的时候很多人会纠结“到底用哪个”。我最后梳理了一张挑选思路表方法核心思想最擅长主要风险适用信号Reward Shaping新增中间进度反馈领域知识能给出稳定进度指标reward hacking、尺度不当能算出到目标距离、完成比例Curriculum Learning从简单分布过渡到复杂分布环境难度可连续调节课程切换过陡、能力回退环境参数可插值、成功率可统计Hierarchical RL高层定目标、底层执行任务时间长、可拆出阶段子目标训练复杂、上下层不同步任务有明显阶段结构这张表不是绝对规则而是一个起点。比如导航任务是三类方法都能处理的典型场景可以加距离势能也可以设置课程让起点慢慢变远还可以直接分层成“规划路径”和“沿路行走”。关键看你手里有什么知识、环境支持什么操作。5.2 三种方法的组合套路实际项目里三类方法经常不是互斥的我的组合思路是先用分层把大任务切成子目标再用课程让每个子目标按难度排列最后在底层子目标上做 Reward Shaping。举个例子室内移动机器人取物任务高层负责决策“先去桌子旁”“再抓杯子”“最后送到门边”每个子目标内部可以用 Curriculum Learning 先训练固定位置再训练随机位置底层“移动到目标点”的技能又可以加一个“到目标距离”的势能塑形。这样每个方法各管一段互不干扰。另一个组合套路是“先塑形后卸载”。训练初期用势能塑形把策略引导出来等成功率上来以后逐步把塑形系数降到 0让智能体适应最终的纯稀疏奖励。这么做的好处是测试阶段没有塑形成分统计出来的成功率就是真实水平。有一点要注意不要在同一个层级同时叠加太多种额外信号否则不同信号会在价值上互相抵消训练方差会变得很大。5.3 通用检查清单最后给一个调试稀疏奖励任务的通用顺序我每次接手新环境都会按这个顺序来记录一个完整回合的奖励时间轴确认是稀疏、延迟还是伪奖励。用随机策略跑几千个回合算一下正奖励出现的概率。如果这个概率低于十万分之一几乎不可能靠纯探索解决必须人为引入引导。如果领域知识能给出稳定势能函数优先尝试 Reward Shaping成本最低。如果塑形容易出 reward hacking转去做 Curriculum Learning前提是环境难度能连续变化。如果任务阶段结构明显但时间跨度长再考虑 Hierarchical RL别一上来就上分层。无论用哪种方法监控指标都不能只看平均回报要同时看成功率、子目标到达率、动作熵和塑形分量统计。6. 一些后话先把稀疏奖励当工程问题而不是算法比拼做了几个稀疏奖励项目之后我的整体感受是不要把这三个方法当成高大上的算法噱头它们本质上都是“给学习过程补信息”的工程手段。Reward Shaping 补的是状态级进度信息Curriculum Learning 补的是分布级引导Hierarchical RL 补的是结构级时间抽象。三者没有绝对的优劣只有适不适合当前任务。我个人最后的小技巧是无论用哪一种方法都保留一套“纯净版”评测环境。所谓纯净版就是不带任何塑形成分、不降难度、不拆子目标直接使用原始稀疏奖励。训练过程中可以随便加料但每隔一段时间必须在纯净版上做一次真实评测。否则你可能会被带着塑形成分的高回报骗过去以为任务解决了等真正部署到原始任务时才发现策略根本没学会。还有一点是调参顺序。每次我发现训练不动会先检查塑形奖励的尺度是否合理再检查课程切换阈值最后才怀疑分层模块的粒度问题。绝大部分失败案例问题不是出在“没有用分层”上而是出在“奖励信号被某种方式污染”或者“难度切换太急”这两个地方。如果让我给一句话总结在动手训练之前先花半天把奖励函数和状态定义清理干净比花一周调新算法更有效。