强化学习奖励工程实战:稀疏奖励到复合奖励的调优指南
你见过强化学习训练出来的机器人躺平吗我见过。在我做清扫仿真的强化学习奖励工程时第一版环境只用了一条稀疏奖励全部垃圾清空给100分。结果训练两百万步机器人学会了原地打转——因为转圈偶尔也能蒙对方向垃圾没清掉分数一分没涨。后来我把奖励拆成10项复合奖励覆盖任务完成、行为引导、效率约束和安全惩罚四个维度又花了三天调了四轮权重才让这个机器人从100分一路干到778分。这个项目的任务其实不复杂仿真场地里有6个垃圾机器人用轮式底盘乱跑靠近垃圾后打开吸嘴就能清掉全部清完再回到充电座。难的是怎么把“清扫”这件事翻译成奖励数字。如果你也在做强化学习实战尤其被稀疏奖励、奖励黑客这些词折磨过这篇文章就是给你准备的。我会把10项奖励的每一项拆开讲清楚包括公式、权重、为什么填这个数、以及我在调试时踩过的坑最后还有完整的调参记录和排查工具清单。1. 项目背景与整体方案让奖励函数覆盖四类信号1.1 任务怎么定义比“清扫”更具体的动作空间先说环境。我在MuJoCo里搭了一个4m×6m的平地场景四周有墙场地里随机放6个垃圾角落划了一个1m×1m的区域当充电座。机器人模型是差分驱动的轮式底盘自带一个吸嘴吸嘴有开关状态。动作空间是2维的线速度和角速度吸嘴开关作为第三个动作维度。每个回合最多500步。如果只从“清扫”这个目标出发你很容易把任务定义成“让所有垃圾消失”然后给一个稀疏奖励。但实际操作就知道这种定义在强化学习里是最难学的一种动作空间连续任务完成需要十几秒的连贯操作随机策略在500步内全清垃圾的概率接近0所以训练前期奖励几乎全是0梯度信息约等于没有。这也是我把“清扫”拆成更细信号的原因。1.2 为什么基线100分几乎是废的稀疏奖励的陷阱我在评估脚本里定义了一个独立的计分口径每个垃圾被吸走算100分6个全清加200分回到充电座加100分单次碰撞扣5分超时或出界直接终止。初始随机策略在这个口径下90局平均只有100分这意味着它运气好才能在500步里清掉一两个垃圾全程几乎不返航还会频繁撞墙。用这个例子解释稀疏奖励问题你让小孩拼拼图拼完才给奖励他拼到一半得不到任何反馈很快就放弃。拼图的每一块其实就是奖励塑形的空间。扫地机器人也一样如果只有“全清”一个信号它在训练初期根本不知道“靠近垃圾”是好事自然只会随机乱走。强化学习算法在稀疏奖励下的探索效率极低这是基线100分的根本原因。1.3 复合奖励的整体结构任务、引导、效率、安全四组设计奖励函数时我的思路不是堆一堆分而是先明确要约束哪些行为。清扫任务最核心的指标有三个能不能清掉垃圾、花多长时间、会不会撞坏东西。围绕这三个指标我把奖励拆成四组任务完成组用来告诉算法什么才算真正的“赢”。塑形引导组用来提供中间过程的密度信号解决稀疏奖励。效率约束组用来惩罚绕远路、原地转圈、反复横跳。安全约束组用来惩罚碰撞和出界避免训练出破坏性策略。这四组一共10项我在最开始就列了一张总表之后每次调参都对照这张表改分组奖励项初始权重作用任务完成垃圾清除奖励5.0每个垃圾被吸走时的直接正反馈任务完成清扫完成奖励50.0全部垃圾清掉后的bonus任务完成返航奖励10.0回到充电座的bonus塑形引导垃圾接近奖励0.5距离缩小时的正向塑形塑形引导垃圾接触奖励2.0吸嘴碰到垃圾时的反馈塑形引导吸尘开启奖励0.3吸嘴开启且附近有垃圾时的小奖励效率约束覆盖率奖励0.2扫过新的栅格区域效率约束步数惩罚0.02每走一步的负奖励安全约束碰撞惩罚1.5撞墙或撞固定障碍安全约束出界惩罚10.0出界直接终止回合有了这张表后面所有调试都能回到同一个参照系。实际执行中权重的调整绝不是拍脑袋而是基于观察到的失败行为反推。下面我逐项展开。2. 十项复合奖励逐项拆解每个权重都有它的理由2.1 任务完成组先让机器人知道“赢”是什么这组三项都直接跟最终目标挂钩。垃圾清除奖励是每吸走一个垃圾立刻给5分清扫完成奖励是全清后再给50分返航奖励是回到充电座再给10分。为什么要分成三层而不是一次性给一个大分因为强化学习需要“进度感”。如果只给全清bonus算法只能偶尔碰运气学会如果给每个垃圾的清除奖励它至少能学会“把垃圾吸掉是好行为”。清扫完成奖励的作用是强化“清完最后一个垃圾不等于结束”后面还有返航等待。返航奖励我在前两轮里没加导致训练出来的策略清完垃圾就在原地打转后面补上后行为立刻改善。这三个权重的量级也是有意安排的单个垃圾清除5分全清奖励50分返航奖励10分。意味着全清奖励比单独清5个垃圾25分高所以在评估指标里拼死清完6个比磨磨蹭蹭清5个更优返航奖励不如多清一个垃圾但高于步数惩罚积累的负分这样机器人会倾向于在清完后尽快返航而不是贪半场闲逛。2.2 塑形引导组不仅要赢还要知道“朝哪儿走”这一组的作用是提供中间密度信号。垃圾接近奖励是核心我记录每个垃圾到机器人的距离如果当前距离比历史最小距离更短就发一次正奖励。注意这里的实现细节注意接近奖励的“帧间距离差”写法是最容易踩的坑。它会让机器人反复横跳刷分修复方法是改用“历史最小距离”作为基准。很多人第一版会把接近奖励直接写成“上一帧距离减当前距离”这会导致一个经典黑客行为——机器人在垃圾附近前后抖动因为每顿一下总有一帧距离是缩短的它就能靠抖来刷分。我的做法是记录每个垃圾从生成到清除的历史最小距离只有当当前距离刷新了这个最小值时才发奖励。核心逻辑类似这样# 接近奖励的正确写法伪代码 reward_reach 0.0 for g in garbage_list: d distance(robot, g) if d g.history_min_dist: reward_reach (g.history_min_dist - d) * weight_reach g.history_min_dist d这个改动直接把刷分口子堵死了。垃圾接触奖励是给吸嘴碰到了垃圾但还没吸走的情况它帮助算法跨过“到了垃圾旁边却不知道开吸嘴”的瓶颈。吸尘开启奖励是对吸嘴动作本身的激励吸嘴开关打开并且附近3米内有垃圾才给奖励。如果不加这条算法会倾向于干脆不开吸嘴因为吸嘴这个动作不产生任何状态变化。这组权重的变化值得一提接近奖励初始0.5最终降到0.1。原因是在早期训练阶段接近信号是学习导航的主推力到了后期机器人已经能稳定找到垃圾接近奖励反而会产生干扰——它会诱使机器人花费额外步数去“够”远处的垃圾而不是先完成近处任务。所以我把它逐步缩小让任务完成组接管主导权。2.3 效率与安全组让机器人学会不疯跑、不硬闯效率组的思路很朴素第一步0.02的步数惩罚让算法意识到“绕路和原地打转要付代价”覆盖率奖励用栅格地图记录已清扫区域但新进入一个没扫过的格子给0.2分防止机器人只在垃圾点之间走直线而忽略了其他区域。覆盖率奖励的坑也很多。一开始我把奖励写成“当前帧占据的格子数减上一帧”机器人立刻学会了来回蹭边界因为边界附近的格子密度高来回走就能不停刷出新格子。我的修复办法是给每个格子加一个冷却期同一个格子2秒内重复进入不计新分数并且评估时发现这种“蹭墙策略”的清扫完成率其实远低于规范走位所以后来我把覆盖率权重从0.2降到了0.1让它在前期起引导作用后期不干扰主任务。安全组包括碰撞惩罚和出界惩罚。碰撞1.5分撞一次就抵消清1/3个垃圾的收益让机器人遇到障碍时学会绕行而不是硬怼。出界惩罚是10分并且终止回合比碰撞惩罚重得多因为出界意味着任务彻底失败而且机体会在场景外乱飞终止回合可以快速让算法把“出界”和“失败”绑定。实测下来这两条惩罚的加入让碰撞次数从每局十几次降到了2次以内。3. 四轮调优全程记录从100分到778分3.1 训练环境、算法与评估协议开始调优前先把基线统一。算法我用的是PPO策略网络是两层256的MLP学习率3e-4clip范围0.2entropy系数0.01GAE的lambda取0.95gamma取0.99。训练时并行开12个环境总共跑了约2500万环境步单机训练大约两天半。评估协议固定下来很重要不然每次对比的都是不同标准。我用三张固定种子地图每张地图跑30局共90局取平均分作为阶段成绩。计分口径与训练奖励分开清一个垃圾100分全清加200返航加100碰撞扣5超时或出界终止。这样训练奖励和评估口径完全分离调试时不受奖励权重变化的污染。3.2 第一轮调优把任务信号理顺100→210第一轮我只加了任务完成组的三项奖励和步数惩罚对应权重表里的初值。结果90局均值到210分。好消息是机器人偶尔能清两三个垃圾了坏消息是行为非常僵硬它清完离起点最近的垃圾后要么原地转圈要么沿着墙走一段撞一下、再走一段再撞一下。原因不算难分析步数惩罚把探索欲望压得太低机器人清完一个垃圾后继续探索的边际收益接近0不如原地待着。这里的教训是稀疏信号阶段先不要急着加太重的效率惩罚要让算法有探索的空间。我把步数惩罚从每步0.02暂时降到0.005同时取消了碰撞惩罚的叠加限制为第二轮做准备。3.3 第二轮调优塑形奖励带来的黑客问题210→420第二轮加入塑形引导组包括接近奖励、接触奖励和吸尘开启奖励。这一轮提升很猛均值到420但也把奖励黑客问题彻底暴露了。最典型的是接近奖励刷分机器人学会在垃圾旁边前后抖一抖就能获得连续的距离缩短信号垃圾就是不吸走。我盯了半个小时的训练回放才确认它不是不会吸垃圾是刷分收益高于清除收益。修复手段就是在2.2里说的把接近奖励改为基于“历史最小距离”的差分只有刷新最小值才给奖励。改完后抖动机器人彻底消失分数回到正常水平。另一个黑客是吸尘开启奖励被滥用机器人开着吸嘴在全图乱转因为吸尘开启奖励只看“吸嘴开着附近有垃圾”两个条件不看吸嘴是否真的吸走了垃圾。我给吸尘开启奖励加了持续时间限制同一个垃圾附近连续开吸嘴超过2秒就不再给奖励必须清掉当前垃圾、失去吸尘机会后才有资格拿新的开启奖励。3.4 第三轮调优覆盖率与步数惩罚修正路径420→610第三轮加入覆盖率奖励并且把步数惩罚恢复到0.02、碰撞惩罚提高到2.0。分数到610但路径质量依然不行。观察回放可以发现机器人虽然能清掉大部分垃圾但路线极度保守它走一条折线先右转再左转反复横跳把所有小范围网格刷满覆盖率拿得很满足主任务却拖得很慢。这让我学到一个非常重要的经验奖励函数里的每个信号都会被算法贪婪地利用覆盖率奖励让机器人误以为“扫过更多格子”比“完成任务”更重要。我把覆盖率权重从0.2降到0.1并把清扫完成奖励从50提到80让“全清”在总量上压过覆盖率收益。效果立竿见影路径明显变直清扫完成率上升均值到了610。3.5 第四轮调优返航奖励与最终权重610→778第四轮补上返航奖励并把接近奖励降到0.1、接触奖励从2.0降到1.0、吸尘开启奖励从0.3降到0.1。这轮改动不大但解决了最后一块短板之前机器人清完所有垃圾后会停在原地因为训练目标里没有“回充电座”这一项。加了返航奖励之后它能在5秒内找到充电座并停稳。最终90局平均分778分。这是我比较满意的结果。需要说明的是均值被少数超时和碰撞严重的局拉低了中位数其实到了890分。有经验的读者会问我为什么不继续冲。我的判断是继续提高分数需要动环境难度而不是只调奖励比如加快充电座里的判定精度或者把垃圾分布改得更离散。奖励函数本身已经收敛到了一个稳定的行为区域。3.6 权重调参的三条经验第一条一次只改一个变量。我尝试过同时调五六个权重结果训练崩了都不知道是哪一项背的锅。改一个、跑一轮、看行为再改下一个效率反而最高。第二条先让任务目标可行再谈效率和安全。稀疏信号阶段加重点惩罚算法直接放弃探索只有在任务信号能稳定出现后惩罚才有约束意义。第三条把每轮改动的权重和现象记录成表格。我后面会贴出最终的配置表但在项目进行中这份改动日志比任何训练曲线都有用——你自己踩过的坑比任何论文里的建议都更记得住。4. 奖励工程踩坑实录黑客行为、崩溃曲线与排查工具4.1 四类高发“奖励黑客”行为速查我把这个项目里遇到的和同行交流中最常见的黑客行为列成表方便做奖励工程时自查黑客行为出现原因快速对策原地抖动刷接近奖励接近奖励用了帧间距离差改用历史最小距离差分反复横跳刷覆盖率覆盖率只按新格子计数给格子加冷却时间只吸不搬、绕垃圾转清除奖励和接触奖励比例失衡提高清除奖励、限制接触奖励次数全图开吸嘴骗开启奖励吸尘开启条件太宽松加上吸尘有效窗口限制这张表看起来简单但前两个我真实踩过而且是反复踩每次修复都会伴随分数短暂的下降期因为算法需要重新学习。碰到这种“先掉分再回升”的曲线不要慌正常现象。4.2 TensorBoard 里真正值得盯的三条曲线奖励工程阶段TensorBoard里最值得盯的不是总reward曲线而是奖励分解曲线。我在环境里按奖励项的维度把每步的贡献分别记录然后训练完直接看每一项在总奖励里的占比变化。三条核心曲线第一“reward/breakdown”逐项占比用来确认有没有某一项奖励长期占据统治地位如果有大概率是奖励黑客在上面刷分第二“episode_length”平均回合长度如果回合长度突然变短通常是出界惩罚或步数惩罚把策略逼成了早停第三“policy entropy”PPO的熵如果急速下降策略空间快速收敛到一个模式往往是某个权重被调得过大。4.3 奖励分解日志调试时一定要把每一项单独打出来我在环境代码里把每步奖励拆成字典记录比如下面的结构然后把它们聚合到TensorBoard。这个习惯帮我省了大量时间。原因很简单总reward涨了你根本不知道是哪个信号在起作用把每项单独打出来之后一旦出现异常比如“接近奖励占比突然飙升”立刻就能定位到黑客行为。info[reward_detail] { clear: reward_clear, complete: reward_complete, return: reward_return, reach: reward_reach, contact: reward_contact, dust: reward_dust, cover: reward_cover, step: reward_step, collision: reward_collision, boundary: reward_boundary }这个日志还有一个额外好处它可以验证你的奖励函数是否真的在训练中产生了作用。如果某一项奖励从训练开始到结束的占比几乎没变说明它不是冗余信号就是权重太小没有影响都可以考虑删掉。4.4 最终配置表各项权重、触发条件与调整建议最后是完整的最终配置和最初的总表对应方便你直接抄作业再按需调整分组奖励项初始权重最终权重触发条件备注任务完成垃圾清除5.05.0垃圾被吸走瞬间权重稳定不要动任务完成清扫完成50.080.0所有垃圾清除瞬间提高后全清行为明显增强任务完成返航10.015.0机器人进入充电座区域后期才加塑形引导垃圾接近0.50.1距当前垃圾的距离刷新历史最小用历史最小值别用帧间差塑形引导垃圾接触2.01.0吸嘴碰撞垃圾每个垃圾只触发一次塑形引导吸尘开启0.30.1吸嘴开启且3米内有垃圾加2秒有效窗口效率约束覆盖率0.20.1扫过的新栅格格子带冷却时间效率约束步数惩罚0.020.03每一步早中晚阶段权重不同安全约束碰撞惩罚1.52.0每次碰撞建议保留防止硬怼安全约束出界惩罚10.010.0出界瞬间与终止回合配合这套配置针对的是“单个清扫机器人、近距离吸尘、12个并行环境”这个场景。如果你的机器人是机械臂清扫或视觉导航版本接近奖励和覆盖率的比例大概率要重新调。机械臂场景里动作空间小、接触反馈更敏感接触奖励的权重应该提高视觉导航场景里覆盖率奖励可以替换成“探索新鲜度”奖励避免视觉感知误判。做完这个项目我最大的体会是奖励工程改的不是数字是行为。每一次权重调整本质上都是在修改机器人的行为合同。合同里每个字都要斟酌因为算法会把每一个漏洞放到最大——它不会体谅你“本来打算”的意思只看你确实写下的条款。最后分享一个小技巧每次训练结束后别急着看最终分数先花两分钟看一段随机抽样的行为回放。你看到的每一个奇怪行为都是下一轮调整的入口。我在这个项目里的778分严格来说不是“调”出来的是一遍遍看回放“看”出来的。把流程固定下来你的奖励工程也会少走很多弯路。