本节摘要:Q 表是一对一的查表结构:每个状态-动作对占一个格子。这在小世界里优雅,在大世界里是灾难——状态数随维度指数增长,表格既存不下,也永远填不满,更谈不上泛化。函数逼近用"带参数的函数"替代表格:Q(s,a;θ) ≈ Q*(s,a),参数量远小于状态数,且相似状态共享经验。本节用具体数字算清表格方案在几个真实规模任务上的死亡现场,并说明泛化为何是 RL 的刚需而非奢侈品。
先把 Q 表的存储需求算成数字。3.2 节那个 5×4 网格:20 个状态 × 4 动作 = 80 个格子,毫无压力。接下来逐级加码。2048 游戏:棋盘 16 格每格 12 种取值,原始状态数约 12^16 ≈ 1.8×10^17——用 8 字节存一个 Q 值,需要 1.4 EB 内存;把存储总量对比一下,全球数据总量也就这个量级,还没算动作。西洋跳棋:约 5×10^20 个局面。围棋:约 10^170,超过可观测宇宙的原子数。表格方案在这些规模前不是"慢",是根本无法存在。
存不下只是第一层死。第二层是填不满:就算内存无限,Q-Learning 需要每个格子被访问足够多次才能收敛(3.5 节的倒灌要一步步爬),10^17 个格子即使每纳秒访问一个,也要几百年的宇宙时间。第三层是不泛化:表格把每个状态当孤岛,学过"棋盘 A 局面向右走好",对相邻的、几乎相同的"棋盘 B 局面"毫无帮助——经验共享率为零。采样预算、存储、时间、泛化,四条死因环环相扣,这条链有个名字:维度灾难。
出路是把"每个格子一个数"换成"一个带参数的函数":Q(s, a; θ),θ 是参数向量。想评估新状态?代进函数算,不需要"存过"。参数量与状态量脱钩——一个几千参数的函数可以覆盖天文数字的状态。泛化自动涌现:神经网络对相近输入给出相近输出,棋盘 A 的经验直接惠及棋盘 B。学过第 3 章增量式平均的话,这个转换可以这样理解:表格是"参数 = 格子值"的最简函数族,函数逼近只是换了表达能力更强的函数族,学习范式不变——仍然是"向贝尔曼目标回归"。
但天下没有免费的午餐。表格时代 Q-Learning 收敛性有保证(每个格子独立回归,互不干扰);换成可微函数后,一个样本的更新会牵动所有状态的输出——你在改"状态 A 的 Q 值"时,"状态 B"的值也被顺手改了。致命组合随之出现:数据来自智能体自己的行为(非独立同分布)+ 自举目标随网络漂移 + 跨状态联动——三毒齐发,朴素方案(直接拿 Q-Learning 更新式怼上神经网络)在 Atari 上以失败告终,训练曲线发散是常态。这次失败催生了第 4.3 节 DQN 的两台稳定器。此处先把"换函数会打破表格时代的收敛保证"这个账记牢——它是理解 DQN 一切设计的钥匙。
有一种质疑值得正面回应:"既然表格存不下,那把状态离散化粗一点不就行了?"看看代价。Atari 原始画面 210×160 像素、每像素 128 色,哪怕粗暴二值化也有 2^33600 种画面——离散化的桶数仍然天文数字,而且语义上相邻的画面被分进无关的桶:一帧里球移动半个像素,二值化后可能掉进完全不同的桶,Q 值毫无关联。函数逼近(尤其卷积网络)恰恰利用了"相邻画面语义相近"的先验:平移不变的卷积核把"球在左上角"这个概念参数化,与球的具体像素坐标无关。这不是工程取巧,是把归纳偏置写进了函数族。
用一段代码直观感受泛化差异。设想一维状态 s ∈ [0,1],真实 Q(s) = sin(2πs),我们只在 20 个采样点上有经验:
# 表格 vs 线性函数逼近:未采样点的表现 import math # 方案 A:表格,仅在采样点有值,其余插 0 samples = [i / 19 for i in range(20)] table = {round(s, 3): math.sin(2 * math.pi * s) for s in samples} def table_lookup(s): return table.get(round(s, 3), 0.0) # 没存过的点:交白卷 # 方案 B:10 个基函数的线性逼近 Q(s) = Σ wi * exp(-((s-ci)/0.12)^2) centers = [i / 9 for i in range(10)] w = [math.sin(2 * math.pi * c) for c in centers] # 用采样点拟合的权重 def approx(s): return sum(wi * math.exp(-((s - ci) / 0.12) ** 2) for wi, ci in zip(w, centers)) for s in (0.113, 0.517, 0.889): # 三个没采样过的点 print(f"s={s}: 真值={math.sin(2*math.pi*s):+.3f} " f"表格={table_lookup(s):+.3f} 逼近={approx(s):+.3f}") # 典型输出: # s=0.113: 真值=+0.647 表格=+0.000 逼近=+0.594 # s=0.517: 真值=-0.105 表格=+0.000 逼近=-0.097 # s=0.889: 真值=-0.651 表格=+0.000 逼近=-0.607
表格在未采样点上交白卷,函数逼近给出量级与方向都对、只是略有误差的估计。RL 里"没采样过的点"是常态而非例外,因为智能体的生命有限而状态近乎无限——这笔账算完,泛化的必要性不言自明。
为公平起见,把硬币的另一面也摆出来——函数逼近并非万能解药,三种情形下它会以自己的方式失败。情形一,状态相似但价值不相似:迷宫里两个只差一格墙的位置,最优价值可能一正一负;平滑泛化会把它们拉到一起,谁也学不准。破解靠特征或结构(图网络、注意力显式区分关键差异),不能靠加大网络。情形二,致命三连( plasticity loss 之类):长期训练后网络对新数据越来越钝,同样的 TD 误差推不动输出——这在超长训练与概念漂移环境里常见,工程对策是周期性重置最后几层或采用重置正则。情形三,分布外状态上的外推被下游利用:价值函数在没见过的状态给出的"自信胡说",会被贪心策略或 max 算子专门挑出来用——这正是第 6 章离线强化学习的核心病灶。看清这三条,"上深度网络"才能从信仰变成决策:它的泛化红利只在训练分布覆盖得了的状态上有意义,分布外的部分要么补数据、要么补结构、要么补保守性。