想象在一个「代码体积预算」下做重构。模型先用一张巨大的硬编码查找表(lookup table)通过所有训练样例——训练集 100% 正确,新输入全错。后台有个 linter(就是 weight decay)持续惩罚「代码体积」。在长期压力下,系统最终「发现」真正的算法(一个紧凑函数),既更小又能泛化。跳变之所以突然,是因为紧凑算法只有完全成形才生效——半成品算法比查找表还差。
常识认为「过拟合之后再训练只会更糟」。Grokking(Power et al. 2022,OpenAI;grok = 科幻词,意为「彻悟」)在小型算法数据集(如「模 97 加法」)上给出反例:训练准确率几百步就到 100%,验证准确率长期趴在随机水平,然后在多训练几个数量级的步数后,验证准确率突然从 ~0 跳到 ~100%。
机制(直觉):网络有两条路解决任务——(1) 记忆:把每个训练样例存成参数里的查找表,权重范数大、不泛化;(2) 泛化:学到底层结构(对模加法是一组傅里叶式的「三角恒等式电路」),权重范数小、能外推。SGD 先到达更易达成的记忆解;而 weight decay 持续施加范数惩罚,缓慢把权重推向更紧凑的泛化解。Nanda et al. 2023 用机制可解释性逆向出了这个电路,证明泛化电路是逐渐长出来的,只是被「记忆 → 清理」的过程掩盖,所以宏观指标上看像突变。没有 weight decay,模型就停在记忆解,永远不 grok——正则化是这里的关键变量。
import torch, torch.nn as nn # 模 97 加法 (a+b)%97:这类算法任务才能稳定复现 grokking(示意骨架) model = nn.Sequential(nn.Embedding(97, 128), nn.Flatten(), nn.Linear(256, 256), nn.ReLU(), nn.Linear(256, 97)) # 关键:weight_decay != 0 是 grokking 的必要条件,去掉它模型只会记忆 opt = torch.optim.AdamW(model.parameters(), lr=1e-3, weight_decay=1.0) for step in range(100_000): # 步数要远超「训练集已 100%」的时刻 loss = loss_fn(model(x_train), y_train) opt.zero_grad(); loss.backward(); opt.step() # 训练准确率几百步到 100%;验证准确率会趴几万步后「突然」跳到 ~100%
像连接池或哈希表,「恰好够用」反而最糟。当模型容量恰好等于训练样本数(插值阈值),系统被逼出唯一一个解,它必须扭曲自己穿过每一个噪声点——好比强行穿过 N 个点的高次多项式,剧烈震荡。容量远超样本时,存在无穷多个能拟合的解,SGD 的隐式偏好会挑出最平滑、低范数的那个——反而更稳。最坏的点,是「不多不少」。
经典统计的 bias-variance tradeoff(偏差-方差权衡)预测:测试误差随模型容量是 U 形——太小欠拟合,太大过拟合。但深度学习里几十亿参数的模型「过参数化」到能背下整个数据集,却依然泛化良好,直接打脸 U 形。
Belkin et al. 2019 与 Nakkiran et al. 2019(Deep Double Descent)给出统一图景:测试误差先降后升(经典 U 形),在插值阈值(interpolation threshold,模型刚好能做到 0 训练误差处)冲到峰值,然后随容量继续增大再次下降——故名双下降。
机制(直觉):插值阈值处,模型自由度恰好等于约束数,解唯一且被迫连噪声一起拟合,方差爆炸。越过阈值后,多余的自由度给了 SGD 挑选的空间,它的隐式正则化(implicit regularization)偏向低范数 / 平坦解,等效于自动 Occam。Nakkiran 还指出双下降不只随模型大小出现,也随训练 epoch 数出现(epoch-wise),甚至在某些区间「更多数据反而有害」。
import numpy as np from sklearn.linear_model import Ridge # 随机特征回归:扫描特征数(=模型容量),观察测试误差的双下降 n_train, errs = 100, [] # 固定 100 个训练样本 for n_feat in [10, 50, 90, 100, 110, 300, 1000]: # 容量穿过插值阈值(=100) W = np.random.randn(d, n_feat) # 随机特征映射 Z_tr, Z_te = X_tr @ W, X_te @ W m = Ridge(alpha=1e-6).fit(Z_tr, y_tr) # 近乎无正则,逼出插值解 errs.append(((m.predict(Z_te) - y_te)**2).mean()) # errs 在 n_feat≈100(=n_train) 处出现尖峰,两侧都更低 → 双下降
像随机图里的渗流(percolation)。往图里逐条加边,连通性平滑增长——直到某个临界边密度,突然冒出一个横跨全图的「巨连通分量」。没有哪条边是特殊的,但宏观连通性在临界点发生相变。LLM 的某些能力随规模就是这样:平滑地加「边」(参数 / 数据 / 算力),跨过某个临界规模后,能力突现。
Scaling laws(Day 14)说预训练 loss 随规模平滑、可预测地下降。但 Wei et al. 2022(Emergent Abilities)发现:某些下游能力(多位数算术、chain-of-thought 推理、指令跟随)在小模型上停在随机水平,到某个规模阈值才突然出现——无法从小模型外推。平滑的 loss 曲线下,藏着不平滑的能力曲线。
机制(直觉):和物理相变类比——水温平滑升高,到 0°C / 100°C 却发生不连续的状态变化。涌现的「物理基础」仍有争议,一类解释是:复杂能力需要多个子电路(sub-circuit)同时就位,缺一则整体失败;规模平滑提升每个子电路的成功概率 p,但它们的合取(AND)是高度非线性的,所以联合成功率会在某点骤升。下面的代码把这个「平滑 p 经 AND 放大成悬崖」的直觉算出来了——这也正好引出下一张卡的争论。
import numpy as np scale = np.linspace(1, 10, 100) # 模型规模(对数轴上的代理) p = 1 / (1 + np.exp(-(scale - 5))) # 单子技能成功率:随规模「平滑」上升 for k in [1, 5, 20]: # 任务需要 k 个子技能同时成立(AND) task_acc = p ** k # 合取 → 高度非线性 # k 越大,task_acc 在某规模处越像「悬崖」——平滑的 p 制造出突现外观 print(k, np.round(task_acc[[30, 50, 70]], 3))
像阈值告警掩盖平滑信号。底层延迟从 90ms 平滑爬到 110ms,但你的「p99 < 100ms」布尔 SLA 从绿突然翻红——是指标的量化制造了「悬崖」,底层信号其实是连续的。Schaeffer 的主张就是这个:能力在平滑提升,但用「全对才算对」的离散指标一量化,就显出一道假悬崖。
如果涌现真的不可预测,对安全和规划是噩梦——你无法知道下一个规模会冒出什么能力。Schaeffer et al. 2023(Are Emergent Abilities a Mirage?,NeurIPS 杰出论文)提出:很多「涌现」是研究者选的指标造成的,而非模型行为的本质突变。
机制(直觉):设 5 位数加法,每位 token 正确概率 p 随规模平滑上升。若用 exact match(5 位全对才得分),得分 ≈ p5;p 从 0.7 到 0.9 平滑变化,p5 从 0.17 跳到 0.59,叠加更长位数后离散指标显出陡峭跳变。换成连续指标(token 编辑距离、log-likelihood),同一个模型的曲线就平滑了。但争论未定:Wei 等回应,有些能力换连续指标仍有非线性拐点;而且「能否预测」与「指标是否平滑」是两个问题。折中共识:部分涌现是度量产物,部分可能是真实的能力相变——需要 case-by-case 用多种指标交叉检验。
import numpy as np scale = np.linspace(1, 10, 100) p = 1 / (1 + np.exp(-(scale - 5))) # 单 token 正确率:平滑上升 exact_match = p ** 5 # 离散指标:5 token 全对才算对 → 假悬崖 token_acc = p # 连续指标:按 token 计 → 平滑 # 同一个底层模型:exact_match 看着像「突现」,token_acc 看着像「平滑爬升」 print("离散", np.round(exact_match[::25], 3)) print("连续", np.round(token_acc[::25], 3)) # 曲线形状一半取决于模型,一半取决于你拿的尺子