0%
✍️ Insight

上有奖励,下有对策:替送咖啡的机器人写一张价目表

你定价,它开工。四张越改越长的价目表,两张永远按同一规则结算的小票:亲眼看见「分数最高」怎样偏离「事情办好」。

想象你买了一台送咖啡的机器人。

你不想教它每个路口怎么拐,只想告诉它什么值得做:送到老板桌,奖励 10 分。剩下的交给它自己安排。

它确实送到了。只是先在充电桩等了半天,最后一刻才出发。

你加上一条「每步扣分」,希望它勤快一点。它看完新规则,转身走下了楼梯。

先别急着说机器人笨。今天你遇到的,恰好是一位太会算账的员工。

第一单:早到晚到一个价

先只写「送到 +10」。你心里想的是尽快送到,但纸上只有送到。

工作台会并排打印两张小票:一张是它选的走法,一张是你原本希望它走的路线。两张都按当前同一张价目表结算,不替参考路线偷偷加分。

先比较合计,再让它开工。要让它早出发,你会给哪件事标价?

第一单:动一动每步成本,看十七步等待会不会从小票上消失。

最短路线需要 13 步。原价目表下,它先等 17 步,第 30 步送到;立刻出发和最后出发,都是 10 分。

这不是「奖励函数必然让机器人拖延」。两种走法打平,程序恰好按平局时先等的规则选了一种。换个平局规则,它可能立刻走,但价目表本身仍然没有区分勤快和拖延。完整平局规则:先原地等,再选走完后离桌子更近的动作,仍相同则按上、右、下、左。固定它,是为了让每次打开都能重现同一张小票。

每一步加上 −0.1,等待也要付费,13 步送到的合计就是 8.7。它现在早走,不是领会了你的催促,而是早走终于更划算。

我们通常把这种价目表叫奖励函数,把整趟累计的分数叫回报。先记住这张纸就够了:它衡量什么,机器人就努力把什么做大。

第二单:罚得越狠,越想早点收工

既然每步扣分有用,干脆扣狠一点?

把每步成本改成 2。桌子距离 13 步,楼梯只要 3 步。工作台已经替你改好了,看看这次是谁算错了账。

第二单:修改每步成本或坠落损失,观察最优路线何时翻转。

送到的合计是 10−13×2=−1610-13\times2=-16,掉下楼梯是 −3×2=−6-3\times2=-6。负六比负十六大,所以它选择了楼梯。

它没有逃离规则。提前结束,正是规则里的一种省钱方式。

假设每步成本为 c,坠落本身还没标价,两种结局的分数分别为 10−13c10-13c 和 −3c-3c。当 c 超过 1,楼梯就更划算;恰好等于 1 时两者打平,不能把某条显示出来的路线当成唯一最优。

你可以降低每步成本,也可以把坠落的损失写进去。仍然每步扣 2,再为坠落扣 20,它就回到桌子这边:正常送到 −16,坠落 −26,虽然都不好看,但有了正确的高低关系。

第三单:进步奖,怎么变成了来回奖?

前两单的员工会算最优解,现实中的初学者却连路都不认识。

如果每一步在当前合法动作里均匀乱选,在这张地图中,30 步内碰巧送到的概率只有约 0.1194%。这个数字是把概率逐步往前推算出来的,不是一次训练跑出来的成功率。「合法动作」包括等待,但不包括撞墙;遇到楼梯或桌子就终止。换一种随机策略、地图或时限,概率会变。

于是你想给它沿途鼓励:「每靠近桌子一步,再加 1 分。」这里的距离按绕墙的最短路算,不是隔着墙量直线。

听起来合理。但如果离开一步不扣回,靠近一次就不必真有一次净进展。

第三单:先数一数「靠近」发生几次,再给「远离」补上一笔账。

默认走法用了 29 步,在途中往返 8 次,总共领了 21 次靠近奖。最短路明明只有 13 步,它却能领更多:退一步,重新靠近,再领一次。

此时每次往返赚 1 分,只花 0.2 分电费,净赚 0.8。小票上的 28.1,确实高于直接送到的 21.7。

修补办法很像押金:靠近给 1,远离收回 1。走回原处,进度奖净赚零,还要付电费,来回踱步就失去了吸引力。

把「进度」记成余额,而不是发奖次数

设 Φ(s)\Phi(s) 等于「到桌子最短距离的负数」。从 s 走到 s′,附加奖励写成:

F(s,s′)=Φ(s′)−Φ(s).F(s,s')=\Phi(s')-\Phi(s).

沿途加起来,中间余额互相抵消,只剩 Φ(sT)−Φ(s0)\Phi(s_T)-\Phi(s_0)。同一起点、同一终点,绕多少圈都不能凭空多出进度。

这就是势函数塑形的直觉:把原本稀疏的成功信号拆到途中,而不是另开一份能反复领取的奖品。

什么时候能说「不改变最优策略」?

Ng、Harada 与 Russell 在 1999 年研究了这个问题。带折扣的标准形式是 F(s,s′)=γΦ(s′)−Φ(s)F(s,s')=\gamma\Phi(s')-\Phi(s),并需要相应的终止处理等条件。

这里没有折扣,γ=1;按距离差结算足以解释「来回一圈不能刷分」。但办公室有送到、坠落和耗尽电量几种终点,终点的距离势不相同。因此不能仅凭这个小实验,宣称所有结局之间的最优选择都保持不变。有限回合若要保留这样的保证,需要妥善设置终止势,例如把包含时间的终止状态势统一设为零,并计算最后一步的修正。

本关展示和测试的是:给定当前价目表,收回远离奖励后,最优走法确实重新变成 13 步送到。它没有模拟学习速度,也没有复现论文全部结论。

第四单:花瓶不是一个数字,直到你把它写成数字

前三单没有花瓶。现在走廊中间放上一只,旁边还睡着一只猫。

每步 −0.1,送到 +10,坠落 −30——目前这张价目表已经能让它准时、安全地抵达桌子。可「安全」里到底包含谁?

第四单:先只给花瓶标价,观察绕路经过哪里;再决定猫是否也该出现在小票上。

没有副作用成本时,最短的 13 步路线打碎花瓶。给花瓶加上足够的损失,它换成 15 步的路线,经过猫睡觉的位置。两项都标价以后,才走 17 步的外侧路线,花瓶与猫都安好。

图上早就画了猫。人一眼就会把「别踩它」带进任务,优化器却没有这样的默认条款。

没写进价目表,不代表没有发生,只代表不会影响这张小票的合计。

继续添行当然有用,但办公室不可能只有两样值得保护的东西。明天换一张地图,可能还有杯里的咖啡、刚拖过的地板、推着轮椅经过的人。把每个遗漏都补成一项惩罚,不等于已经拥有完整的安全目标。

有些要求应该成为硬约束,有些需要更多场景测试,有些还需要人来判断。这里的小票只帮我们看清漏洞,并不承诺存在一张能包办所有价值判断的完美价目表。

打开真实配置,里面也有一张长小票

离开办公室,看看 legged_gym 的 LeggedRobotCfg.rewards.scales。以下是基础配置中的几行,不是某一型号机器人最终采用的全部参数:

配置项基础权重它在衡量什么
tracking_lin_vel1.0鼓励跟上给定的平面速度,而不只是动起来
lin_vel_z−2.0惩罚机身竖直速度的平方,避免用不必要的上下窜动完成任务
torques−0.00001惩罚关节力矩平方和,让用力也有成本
action_rate−0.01惩罚相邻动作的变化平方和,让来回猛改命令有成本
collision−1.0对指定部件的碰撞计罚;不是替所有可能的碰撞自动负责

这些数不能脱离各项定义直接比较大小,也不宜抄进另一台机器人。源码还会把非零权重乘以时间步长,具体环境可以覆盖基础配置。

更值得借走的是读法:这一行衡量了什么?如果只顾它,最划算的歪办法是什么?下一行有没有替它付代价?

别先问它为什么不听话

四张小票,没有一张需要机器人怀着坏心思。

第一单没有给及时性标价,第二单把早点结束变成省钱,第三单把进展误记成次数,第四单漏掉了环境里的损失。它们共同指向同一件事:分数在替你说话,但它不一定说全了。

下次写奖励函数,不妨先替它找一张难看的高分小票。把失败行为也认真结算一次,比等训练曲线涨起来再惊讶,便宜得多。

办公室的地图与求解器在 model.js,四份价目表在 lessons.js,检验文件记录了本文每一单的预期结果。它们都是本地教学模型,不是实机训练日志。

咖啡送到以后,也看一眼沿途。最好的员工,不该只留下最好看的那张小票。

Policy invariance under reward transformationsNg、Harada、Russell,1999:势函数塑形与策略不变性的条件。people.eecs.berkeley.edu legged_gym 基础奖励配置本文列出的基础权重与 only_positive_rewards 注释来源;具体环境可能覆盖。github.com legged_gym 奖励计算实现配合配置读各项实际计算、时间步长缩放和终止奖励的处理顺序。github.com