拿不到规则的时候:把一局打完再说
上一卷所有算法都要那张转移概率表。而真实的游戏不会给你。这一章开始,我们进入「摸黑」模式——而第一个办法,朴素到近乎不讲道理。
卡在哪儿了
贝尔曼方程:
V(s) = max Σ P(s'|s,a) · [ r + γ V(s') ]
a s'
└────┬────┘
└── 没有这张表,这个 Σ 就写不出来
那个求和号需要「所有可能的落点,各自的概率」。你没有。
但你有一样别的东西:你可以玩。
那就玩一万次
蒙特卡洛方法的整个思想就一句话:
要估计「从这一格出发,期望能拿多少分」——那就从这一格出发走一百次,把每次实际拿到的总分加起来除以一百。
大数定律保证了:样本越多,这个平均值越接近真实的期望。
它完全不需要知道 P。因为 P 已经藏在采样里了——如果某个落点的概率是 80%,那么你走一百次,大约就有八十次落到那儿。概率不需要被写下来,它自己会在频率里出现。
具体做法:
重复很多次:
从头打一局,记下整条轨迹 s0 a0 r1 s1 a1 r2 …… 结束
从后往前算每一步的回报 G
对轨迹里出现过的每个状态:
把这次的 G 加进它的平均值里
注意「从后往前」——这是个小技巧,因为 G_t = r_{t+1} + γ·G_{t+1},倒着算一遍就够了,不用对每一步重新求和。
跑一下
盯住两个东西:估计值和真值的距离(真值是第 7 章那台动态规划引擎算的 0.7053),以及最下面那个「从没走到的格子」。
它做对了什么
50 局的时候,起点的估计已经相当接近真值了;1000 局之后误差降到 0.02 以内。
而这一路它一次都没有看过转移概率表。它只是走、拿分、记账。
蒙特卡洛还有两个很值得说的优点:
- 无偏。它用的是真实发生的回报,没有任何近似。这一点后面那个 TD 就做不到。
- 不受马尔可夫性影响。它压根不用「下一状态的价值」这种东西,所以就算你的状态定义得不完备(第 5 章那个坑),它也不会因此崩掉——只是估的是「你这个状态定义下」的价值而已。
它的三个毛病
① 必须等一局结束
这是最硬的限制。回报 G 要等这局打完才知道,所以中途学不到任何东西。
问题在于,很多任务的一局非常长——或者根本不结束:
- 一台一直在跑的推荐系统,什么时候算「一局结束」?
- 一个机器人在仓库里不停搬货,什么时候结束?
- 星际争霸一局二十分钟,几万步。学一次要等二十分钟。
更糟的是:一个还没学会的 agent 很可能在原地绕圈绕到步数上限才被掐断。这一局什么有用信息都没有,但你付了全部时间。
② 方差大
回报 G 是一整局所有随机性的累积。走了五十步,每一步都可能滑,最后那个数字里塞满了运气。
结果就是:要很多局才能把噪声平掉。demo 里 50 局的时候误差还有 0.0568,得跑到 5000 局才降到 0.0083。
③ 只学得会它走过的地方
看 demo 里每格下面那个「走过 N 次」。你会发现有两格从头到尾是 0。
它们在哪?在起点绕不到的角落里——按最优策略走,根本不会路过那儿。
第 3 章说过一次,这里是它第一次真的咬人:数据分布依赖于策略。你只能学会你走过的地方。
这会长成两个具体的麻烦:
麻烦一:先有鸡还是先有蛋。要评估一个策略,得按它走;可要改进它,又得知道「没走过的那条路好不好」。而没走过的路,你的估计永远是初始值。
标准解法有三个,各有代价:探索性起始(每局从随机状态开始——但真实环境往往不让你随便设初始状态)、ε-软策略(永远留一点随机,第 3 章那个)、离策略(用一个爱探索的策略去走,学另一个策略的价值——这是 Q-learning 的路子)。
麻烦二:第 21 章那个更狠的版本。在一个 20×20 的房间里,只有对角一格有奖励。六次实验里,没有一次学会——因为它压根没走到过那一格,价值全是 0,四个动作完全等价,学习根本没有发生。
一局里如果多次经过同一个状态,怎么算?两种做法:
- 首次访问(first-visit):只用第一次经过时的那个 G。理论最干净——各次采样相互独立,无偏性和收敛性都好证。
- 每次访问(every-visit):每次经过都记一笔。有偏(同一局里的多个样本是相关的),但实践中往往更快,而且随着样本增多偏差会消失。
这本书的引擎用的是首次访问(靠「倒着遍历、后写覆盖先写」实现的,一行代码)。实际用起来两者差别很小,不值得纠结。
顺带:这个思路比它看起来重要得多
「不知道期望?那就采样求平均」这一招,不只是一个权宜之计。它是整个现代强化学习的地基:
- 策略梯度(第 17 章)里那个
E[G·∇logπ],实践中就是采几条轨迹求平均。 - PPO(第 18 章)里的优势估计,也是采样估的。
- AlphaGo 的 MCTS——名字里那个 MC 就是蒙特卡洛。它在树上做的事就是「这一支往下随便走几遍,看平均能赢多少」。
换句话说:上一卷那个 Σ,从这一章开始就再也没有被真正算过了。后面所有算法都是在用不同的方式采样它。
你在 Stable-Baselines3 里找不到 MC 这个算法。原因就是上面那三个毛病——现代算法都用了 TD 或者两者的混合。
但蒙特卡洛的影子无处不在,尤其是这两处:
from stable_baselines3 import PPO
model = PPO("MlpPolicy", env,
n_steps=2048, # 收集 2048 步再更新一次 —— 「先攒一批再算账」
gae_lambda=0.95) # ← 这个才是关键
gae_lambda 是一个在蒙特卡洛和 TD 之间连续滑动的旋钮:
gae_lambda = 1.0→ 纯蒙特卡洛。无偏,方差大。gae_lambda = 0.0→ 纯 TD(0)(下一章)。有偏,方差小。gae_lambda = 0.95→ 默认值,靠 TD 那头一点。
这本书里最有用的一个心智模型就是这个滑块。下一章会把它的另一端讲清楚,你会看到这两端各自的偏差和方差长什么样。
纯蒙特卡洛训那个学走路的小人会怎样?
能训,但慢得让人难受。一局跑十秒(500 帧),学一次;里面五百步的每一个决定,共享同一个反馈——「这一局总共走了 3.2 米」。
于是它分不清:这 3.2 米是因为第 47 帧那次抬腿抬得好,还是因为第 300 帧那次没摔倒?所有步都被同等地奖励或惩罚了。
这个问题有个名字,叫信用分配(credit assignment)——「这个结果该记在哪一步头上」。它是强化学习的核心难题之一,而下一章那个 TD,正是对它的第一次正面进攻。
这一章的一句话
不知道转移概率?那就走一万次取平均。代价是:必须等一局结束,方差大,而且只学得会你走过的地方。
下一章:那个「必须等一局结束」的限制,会被一个看起来像作弊的招数拆掉——拿估计去更新估计。它带来了整门学科后半程全部的威力,也带来了全部的麻烦。