γ:为什么未来的分要打折
大多数教材这么介绍 γ:「为了保证无穷级数收敛,我们引入一个折扣因子」。这个说法没错,但它把最有意思的部分漏掉了——γ 不是一个数学补丁,它是你替 agent 做的一个关于耐心的决定,而这个决定会实实在在改变它走哪条路。
先说那个技术性的理由
回报是从现在到结束的奖励总和:
G = r + r + r + r + ……
t t+1 t+2 t+3 t+4
如果这局永远不结束(一个持续运行的推荐系统、一台一直在走的机器人),这个和是无穷大。所有策略的回报都是无穷大,于是「哪个策略更好」这个问题失去意义——你没法比较两个无穷大。
加个折扣就解决了:
G = r + γr + γ²r + γ³r + …… 0 ≤ γ < 1
t t+1 t+2 t+3 t+4
只要 γ < 1 且奖励有界,这个级数就收敛。数学问题关掉了。
但如果只讲到这里,你会以为 γ 是个可有可无的技术细节,越接近 1 越「准确」。那就完全误解了。
γ 真正的意思:你能看多远
把 γ 的幂次列出来看,它的含义立刻就变了:
| γ | 10 步后的权重 | 50 步后 | 有效视野 1/(1−γ) | 这是一个什么样的 agent |
|---|---|---|---|---|
| 0.5 | 0.001 | ≈0 | 2 步 | 只看眼前,几乎是条件反射 |
| 0.9 | 0.349 | 0.005 | 10 步 | 看得见十来步以内的事 |
| 0.99 | 0.904 | 0.605 | 100 步 | 常用默认值 |
| 0.999 | 0.990 | 0.951 | 1000 步 | 非常有耐心,但也非常难训 |
1/(1−γ) 这个量叫有效视野,是个很好用的心算工具:γ = 0.99 大致相当于「往后看一百步」。
于是 γ 的含义变了。它不是「近似的精度」,它是你替 agent 决定的耐心程度——而这是一个关于它该是什么样子的价值判断,不是一个逼近误差。
看它当场换一条路走
光说不够。下面这台是真的价值迭代引擎,跑在第 5 章那个格子世界上。
把 γ 从 1.00 慢慢拖到 0.90。左下角第二格(起点右边那个)的箭头会从 ← 翻成 →。
然后把「每步代价」拖到 −2,看整张图会变成什么样。
发生了什么
γ = 1.00:绕远路的那个
不打折的时候,最优策略是贴着左边墙往上走,绕一大圈到顶上再往右——完全避开那个 −1。
为什么?因为不打折意味着时间不值钱。多走五步只多付 −0.2,而从 −1 旁边过去有被滑进去的风险,那可是 −1。宁可多走五步买个安心。
这个「绕远路」的结论是 AIMA 那本书里的经典细节,很多人第一次看到会觉得反直觉——但它是算出来的,不是设计出来的。
γ = 0.90:抄近路的那个
打折之后,时间变贵了:五步之后的 +1 折现回来只剩 0.59,而绕远路要多走的那几步是当场就付的。
于是账算不过来了。它改主意,宁可冒着被滑进 −1 的风险,也要走那条近的。
同一个世界,同一套规则,同一个奖励函数。唯一改动的是「未来值多少钱」,而 agent 的性格变了——从谨慎变成了冒进。
把 γ 调低不会让答案「变得不那么准确」,它会让答案变成另一个问题的正确答案——那个问题叫「一个只在乎接下来十步的 agent 该怎么走」。
这解释了一类很常见的困惑:「我的 agent 学出来短视得要命」。多半不是它没学好,是你告诉它要短视的。
另一个旋钮:每步代价
demo 里第二根滑杆是每步的代价。它和 γ 一样,看起来无害,实际上是这个世界的性格开关。
用二分法把这台引擎的相变点找出来,是这样几个(AIMA 图 17.2(b) 对同一个世界给出的边界是 −1.6284、−0.4278、−0.0850,其中最后一个和这台引擎算出来的逐位一致;前两个差约 0.02,因为「哪一格先翻」的判据不完全相同):
| 每步代价 | 最优策略变成 | 为什么 |
|---|---|---|
| 0 | 在角落里绕圈,永远不去终点 | 活着不要钱,那何必冒险 |
| −0.04 | 绕远路避开 −1 | 教科书那一版 |
| ≈ −0.085 起 | 开始抄近路,冒险经过 −1 | 时间开始贵过风险 |
| ≈ −1.65 起 | 直接冲进 −1 | 活着比死了还亏 |
最后一行值得念出来:当每一步都要付 −2 的时候,最优解是尽快走进那个 −1 的格子把这局结束掉。它不是自暴自弃,它是在做正确的算术——多活一步就多亏两分,而认输只亏一分。
「每步扣一点」这种写法,几乎是每个人写奖励函数时的第一反应(「我希望它快一点完成」)。而这一行的数值大小,能把 agent 从「谨慎」一路推到「自杀」。这是这本书第一次展示卷 V 那个主题:你写下的那个数字,比你以为的更有决定权。
「既然 γ 越大越有远见,那我直接设 0.9999 好了?」
会有三个麻烦,都很实际:
- 方差爆炸。回报里累加了一千步的随机性,同一个策略跑两次结果可能天差地别。梯度估计跟着变得极噪。
- 收敛变慢。价值迭代的收敛速度和 γ 直接挂钩——demo 里把 γ 从 0.5 拖到 1.0,看那个「收敛用了多少轮」,它一路在涨。
- 信号被淹没。如果真正重要的事发生在十步之内,γ = 0.9999 会让那十步的信号和后面九百步的噪声权重差不多。
实践里的经验法则:先估一下「一个决定的后果大概多少步之后见分晓」,然后取 γ ≈ 1 − 1/那个步数。CartPole 那种几十步的问题用 0.99 绰绰有余;需要几千步规划的问题,与其硬调 γ,不如换个思路(分层强化学习、或者把奖励设计得更稠密)。
行,但有条件:只在「保证会结束」的回合制任务里。这类任务叫 episodic task,回报是有限项的和,不需要折扣来保证收敛。
这本书里 γ = 1 用了两次,都是这种情况:第 7 章的格子世界(一定会走到终点),第 13 章的悬崖世界(一定会走到终点)。用 γ = 1 是为了让数字能和教科书逐位对上——一旦打折,价值就不再是「还要走几步」的直接读数了。
但要小心一个陷阱:「理论上会结束」不等于「实际上会结束」。一个还没学会的 agent 完全可能在原地绕圈绕到天荒地老,这时候 γ = 1 会让回报发散、数值溢出。所以工程上通常有两道保险:给回合设一个步数上限,以及宁可用 0.99 也不用 1.0。
from stable_baselines3 import PPO
model = PPO("MlpPolicy", env,
gamma=0.99, # ← 就是它
gae_lambda=0.95) # ← 另一个「视野」旋钮,第 17 章会提
几个常见环境的默认值,可以当参照:
- CartPole:0.99。任务几百步就结束,够用。
- Atari:0.99。DQN 论文用的就是这个。
- MuJoCo 连续控制:0.99,有些任务会调到 0.995。
- RLHF:常见 γ = 1.0。因为一段回复就几百个 token,而且「好不好」是整段一起判的——没有「未来」可打折。
调参顺序上的建议:γ 不是你该先动的旋钮。先确认奖励函数是对的(卷 V),再调学习率,最后才轮到 γ。很多被归咎于 γ 的问题,根子在奖励上。
那个学走路的小人,γ 通常是 0.99,有效视野一百步。按 50 Hz 的仿真频率算,大约两秒。
两秒是个很讲究的长度:它足够长,能覆盖「迈这一步会不会让下一步站不稳」这种因果;又足够短,不至于让「五十步之后的事」把当下的信号淹掉。
如果你把 γ 降到 0.9(视野 0.2 秒),会发生一件很典型的事:它学会一种「原地小碎步」的怪异步态——每一步都很安全,但根本走不远。因为它看不到「大胆迈一步、两秒后能多走一米」这件事。
下次再看那种视频,看到 agent 动作特别猥琐、特别保守的时候,可以猜一下:要么是 γ 太小,要么是「摔倒」的惩罚给太重了。
这一章的一句话
γ 不是为了让数学收敛的补丁,是你替 agent 决定的耐心。改它不会让答案变得不准,会让答案变成另一个问题的正确答案。
下一章:我们终于要算那个东西了——一个格子到底值多少钱?你会看到价值像水波一样从终点往外扩散,而算出来的九个数,和教科书上印的一模一样。