悬崖边上:一个 max 的两种活法
这是这本书里我最喜欢的一个实验。两个算法的更新式只差一个词,跑在同一个世界、同一个种子上——它们走出了两条完全不同的路,而且两条都有道理。
差的那一个词
Q-learning: Q(s,a) += α[ r + γ max Q(s',a') - Q(s,a) ]
a'
SARSA: Q(s,a) += α[ r + γ Q(s',a') - Q(s,a) ]
↑
ε-greedy 实际选出来的那个 a'
Q-learning 说:「假设我下一步会走最好的。」
SARSA 说:「用我实际会走的那个——包括那 10% 的手滑。」
SARSA 这个名字就是从它用到的五样东西来的:State, Action, Reward, next State, next Action。它必须先决定下一步走哪儿,才能做这次更新——所以它学的必然是「自己实际在用的那个策略」,这叫 on-policy(在策略)。
那个世界
Sutton & Barto 例 6.6。4 行 12 列,起点在左下角,终点在右下角,中间整条底边是悬崖:
· · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · S X X X X X X X X X X G 每走一步 -1 掉进 X -100,扔回起点,这局继续 走到 G 这局结束
最优解很清楚:贴着悬崖边走。上一格、右走 11 格、下一格,一共 13 步,回报 −13。
这个数字是第 7 章那台动态规划引擎算出来的,可以当标准答案。
看那条蓝色的路线走在哪一行。然后看下面那张表最后两行——训练途中的数字,和最终策略的数字,说的是两件不同的事。
结果
| Q-learning | SARSA | 最优 | |
|---|---|---|---|
| 学到的路线 | 贴着悬崖走 | 绕到上面走 | 贴着悬崖 |
| 路径长度 | 13 步 | 17 步 | 13 步 |
| 关掉 ε 后执行的回报 | −13 | −17 | −13 |
| 训练途中平均回报 | −56.25 | −27.62 | — |
| 训练途中掉下去几次 | 187 | 46 | — |
Q-learning 学到了真正的最优解:13 步,关掉探索之后执行,回报正好 −13,和动态规划算的一模一样。
SARSA 学到的是一条绕远的安全路线:17 步,回报 −17。比最优差了 4 分。
但看最后两行:训练途中,Q-learning 掉下悬崖 187 次,SARSA 只掉了 46 次。平均回报 −56.25 vs −27.62——SARSA 好了一倍。
为什么会这样
关键在于:训练的时候,ε-greedy 有 10% 的概率随机乱走。贴着悬崖走的时候,那 10% 里有四分之一是「往下」——直接掉下去 −100。
现在看两个算法各自「看见」了什么:
Q-learning 看不见那个风险
它的更新目标是 max Q(s',a')——「假设下一步我会走最好的那个」。所以在它眼里,站在悬崖边上完全没问题:下一步我当然会往右走,不会往下跳。
它学的是「一个永不手滑的我」该怎么走。那个 agent 确实该贴着悬崖走。
问题是,实际在走路的不是那个 agent,是会手滑的这一个。
SARSA 看得见
它的目标是 Q(s',a'),其中 a' 是ε-greedy 实际选出来的。所以偶尔——大约十次里有一次——那个 a' 就是「往下」,目标里就实打实地出现了 −100。
这个 −100 顺着自举一路往回传,把悬崖边上那一整排格子的 Q 值都拉低了。于是「靠近悬崖」这件事本身变得不划算,它自己走远了一点。
Q-learning 学的是「最优策略」;SARSA 学的是「我这个带着 ε 的策略,能拿到的最好结果」。
换个说法更传神:
SARSA 知道自己会犯错,所以给自己留了余地。
而这句话的另一面是:如果你把 ε 降到 0,SARSA 会收敛到和 Q-learning 一样的最优解。它的「谨慎」不是性格,是对自身噪声的正确核算。
那到底哪个对
这个问题没有统一答案,但判据很清楚:训练途中的代价,是不是真的要付。
| 场景 | 训练途中摔一次的代价 | 更适合 |
|---|---|---|
| 模拟器里训 Atari | 零。重开就行 | Q-learning |
| 真实机器人学走路 | 可能摔坏几万块的关节 | SARSA 这一路 |
| 线上推荐系统 | 真实用户的一次糟糕体验 | SARSA 这一路 |
| 自动驾驶 | 不能付 | 两个都不行,得离线学 |
「学到的策略有多好」和「学的过程中亏了多少」,是两个独立的指标。论文里报的通常是前者,而你在生产环境里真正付钱的是后者。
这个区分有个正式的名字,叫 final performance vs online performance,值得记住——因为第 24 章那张排障表里,「评估时比训练时差」这一条就出在这儿。
还有第三种写法,把这两个的好处凑到了一起:
目标 = r + γ Σ π(a'|s') Q(s',a')
a'
不是取 max(Q-learning),也不是用采样出来的那一个(SARSA),而是把 ε-greedy 策略下的期望算出来。
好处是方差更小——SARSA 那个 a' 是采样的,运气成分大;期望 SARSA 把这一层随机性积掉了。代价是每步要多算 |A| 次,在动作不多的时候完全可以接受。
有意思的是:期望 SARSA 其实包含了 Q-learning。如果你让目标策略是纯贪心的,那个 Σ 就退化成了 max。所以它是一个更一般的框架,两个算法都是它的特例。
这一章很容易读出一个错误结论:「Q-learning 更强,因为它学到了最优。」
两个提醒:
① 那个「最优」是对无噪声执行而言的。如果你的机器人执行时也会手滑(真实机器人一定会),那么「贴着悬崖走」在部署时同样会掉下去。这时候 SARSA 学的那条路才是真正的最优。
② 这个实验里 SARSA 的优势被低估了。这个悬崖世界是确定性的(noise=0),所有风险都来自 ε 探索,而部署时 ε 会被关掉。换一个环境本身就有噪声的问题,SARSA 那条安全路线会在部署时也赢。
你可以自己验证第二点:在第 5 章那个 80/10/10 的格子世界里跑这两个算法,你会看到差距变小甚至反转。
on-policy 和 off-policy 这条线,把现代算法也切成了两半:
| off-policy | on-policy | |
|---|---|---|
| 代表 | DQN、DDPG、TD3、SAC | A2C、TRPO、PPO |
| 能用旧数据吗 | 能(经验回放) | 不能,用完就扔 |
| 样本效率 | 高 | 低 |
| 稳定性 | 差(第 16 章) | 好 |
| 典型用途 | 真机器人(样本贵) | 模拟器、RLHF(样本便宜) |
这张表解释了两件你可能已经注意到的事:
为什么 RLHF 用 PPO 而不是 SAC?因为生成文本的成本相对可控,而训练稳定性至关重要——你不想让一个几百亿参数的模型在训练中途发散。
为什么机器人领域偏爱 SAC?因为真机采样极贵,能重复利用旧数据的价值远远大于稳定性的损失。
看那个学走路的小人训练的中段——它已经能走几步了,但还是会摔。
这时候两种算法会给你不同的东西:
Q-learning 那一路会追求「如果我每一步都完美执行,能走多快」——学出来的步态大胆、efficient,但对扰动敏感,一点噪声就崩。
SARSA / on-policy 那一路会把「我有时候会抖一下」算进去——学出来的步态看起来更保守:步子小一点,重心低一点,落脚更宽。
下次看到 agent 的步态特别小心翼翼,可以想一想:那可能不是它没学好,是它知道自己会手滑。
这一章的一句话
max 还是「我实际会走的那个」——一个词的差别,决定了 agent 是按「完美的自己」还是「真实的自己」来做规划。而后者会离悬崖远一点。
卷 III 到此结束。你现在有了一整套不需要知道规则的方法。
但它们全都建立在另一个假设上:你能维护一张表,表里每个状态一行。下一卷第一句话就是:你不能。而那个「不能」的程度,会超出你的想象。