深网为什么一初始化就炸?
本课一问:一个刚建好、一步都没训练的深网,第 0 步里的每个数有多大?为什么「深网一初始化就炸」?
这一课先不碰:归一化放在残差的哪一边(第 2 课)、训练中途 logit 一步步长大(第 3、4 课)、模型变宽时学习率怎么跟着变(第 5 课)。
从已有出发
「小明把苹果给了小王」进模型之后的每一步,前面几章都讲过了:切成 5 个 token,查 embedding,经过 64 层的注意力与 FFN,最后由 lm_head 打分。点亮的两处是本课要追问的地方——embedding 表和每层矩阵里的初始值从哪来,开局的 logits 因此有多大。
两条旧线索:C0 第 5 课的开局自检说,还没训练的模型 loss 应该约等于 ln V(我们的项目开局是 7.6852,ln 2048 = 7.6246);C4 第 2 课说,残差支路的输出投影要按 1/√(2L) 缩小初始化。两件事背后是同一个问题:参数的初始值多大,第 0 步的每个数就多大——层数一多,这个「多大」会被一层层乘上去。
先自己设计:权重就用标准正态随机数
最省事的做法:所有权重都从 N(0,1) 里抽。先只看一层里的一个数:它是 d 个「权重 × 输入」相加。
计算中……(纯浏览器真算,种子固定)
标准差不是 1,是 √d。这不是巧合:d 个独立、方差为 1 的项相加,方差就是 d。一层是这样,每一层都是这样——那就逐层相乘。下面让 5 个 token 的向量(代表例句里的 5 个位置,用随机数代替,输入 RMS ≈ 1)穿过 L 层,每层 x ← W·x:
- ① 朴素方案
- ② 它在哪失败
- ③ 改进方案
计算中……(纯浏览器真算,种子固定)
计算中……(纯浏览器真算,种子固定)
四条线共用同一组随机矩阵,只差权重的标准差。切换「线性 / ReLU」,看哪一条停在 10⁰ 附近不动。
试三件事:① 在朴素一侧把层数拖到 64:表里「FP32 / BF16 变 inf」那一格是第几层?(组件按每层最大的那个元素判断;开发时另用 Float32Array 直接跑过一遍,变 inf 的层数一致)② 切到改进一侧,激活选「线性」:Xavier 那条线平了,He 那条线往上走——He 多给的那个 2 是给 ReLU 准备的;③ 激活切到 ReLU:这回 Xavier 往下掉,64 层后只剩 10⁻¹⁰ 量级,He 平了。太大会炸,太小会消失,只有「刚好」才能叠深。
改进一:按 fan-in 缩放方差
把上面那个「为什么是 √d」写成公式,逐项看:
这个等式本身是可以证明的 证据等级 · 数学可证明 在写明的前提下可以严格推导出来,不依赖实验。只要前提成立,换数据、换规模仍然成立。 本条独立随机变量之和的方差等于方差之和;w、x 独立且均值为 0 时 Var(wx) = Var(w)·E[x²]。 出处待补 ;「让每层方差保持不变就好训」是理论动机,Glorot 与 He 都用实验验证了它在各自网络上的效果 证据等级 · 有理论动机 有数学或机制上的理由说明它可能有效,但没有证明它一定更好。最后是否采用,仍然要看实验。 本条He 在 30 层的 ReLU 网络上显示 Xavier 初始化训不动、2/n 能训;这是理论推导 + 在其网络上的实验,不是「最优初始化」的证明。 出处He et al. 2015 §2.2 ↗需联网 。
改进二:现代 LLM 反而用固定的 0.02——为什么不炸
翻开真实的配置,看到的却不是 1/√d:GPT-2 所有权重用 std 0.02;OLMo 2 写明「所有参数从均值 0、标准差 0.02 的截断正态分布初始化」;锚点 Qwen3.8 的 config 里 initializer_range 也是 0.02(这是 Hugging Face 实现随机初始化时用的值,官方没有公开预训练真正用的初始化);DeepSeek-V3 用 0.006 证据等级 · 实验验证 由对比实验得出的经验结论,只在出处写明的模型、数据和规模下成立。换了条件可能不再成立,引用时要带上实验设置。 本条原文:All learnable parameters are randomly initialized with a standard deviation of 0.006。 出处DeepSeek-V3 技术报告 §4.2(本地 PDF) ↗ 。
按上面的公式,0.02 在 d = 5,120 时每层放大 0.02 × √5,120 = 1.431 倍,平铺 64 层会放大到 1.43164 ≈ 9.2e+9 倍。它不炸,是因为真实模型根本不是「平铺」:C4 讲过的 Pre-Norm 块里,每个子层读的是 RMSNorm(x),不管残差流多大,读到的都是 RMS 为 1 的向量;子层写回的量再加到残差流上。逐层相乘这件事,被归一化切断了。
计算中……(纯浏览器真算,种子固定)
试两件事:① 看 std 0.02 那条线:64 层之后残差流的 RMS 几乎没动——每层写回的量很小(还乘了 1/√L);② 把「写回乘 1/√L」取消勾选,再看 N(0,1) 那条线涨了多少——它没有溢出,但比勾选时大得多。这就是 GPT-2 为什么要按残差层数缩放写回(C4 第 2 课)。
所以在现代 LLM 里,初始化的职责缩成两件事:每个子层往残差流里写多大(0.02,再乘 1/√(2L)),以及开局的 logits 多大。OLMo 2 还做过一个反直觉的对照:把早先版本里「越往上的层初始化越小」的缩放初始化,换回所有参数一律 0.02,梯度范数的 spike score(偏离近 1,000 步滚动均值 7 个标准差以上的点所占比例)从 0.40 降到 0.03 证据等级 · 实验验证 由对比实验得出的经验结论,只在出处写明的模型、数据和规模下成立。换了条件可能不再成立,引用时要带上实验设置。 本条在他们缩短 warmup、专门诱发 spike 的对照设置下测得;更精巧的初始化不一定更稳,这是实验结论。 出处OLMo 2 §3.2 ↗需联网 。
改进三:开局 logits——loss 从 ln V 开始
我们的项目共享 embedding:lm_head 就是 embedding 表 E 本身。最终 RMSNorm 的输出 h 长度被拉到 √d,再和 E 的每一行做点积,得到每个候选词的 logit:
| 维度 | 含义 | 真实规模 | 小规模 |
|---|---|---|---|
| T | 位置数(例句 5 个 token) | 5 | 5 |
| d | 项目的 d_model | 256 | 4 |
| V | 项目的词表大小 | 2048 | 8 |
logit 标准差 = σ·√d(乘上 h 的长度因子)。项目用 σ = 0.02、d = 256,预测 0.32;C0 在同一个未训练的模型上实测是 0.3242。把 σ 换成 1,预测变成 16——开局 loss 会是多少?先猜,再看:
计算中……(纯浏览器真算,种子固定)
σ = 1 时,开局 val loss 是 55.33,比「均匀乱猜」的 ln V 高出 47.71:模型一上来就自信地猜错,前几百步都在把 logits 缩回来(Karpathy 管这种形状叫 hockey stick)。PaLM 的 embedding 就是 N(0,1)(embedding 后面没有归一化),所以它把输出 logits 乘 1/√n(n 为 embedding 维数)——等于把上表的 s 除以 √d,拉回到 1 附近 证据等级 · 实验验证 由对比实验得出的经验结论,只在出处写明的模型、数据和规模下成立。换了条件可能不再成立,引用时要带上实验设置。 本条原文:kernel 权重按 fan-in 方差 1/n_in;输入 embedding 为 N(0,1);因为输入输出 embedding 共享,pre-softmax logits 乘 1/√n。 出处PaLM §5「Weight initialization」 ↗需联网 。
常见误解一:「初始化越小越安全。」平铺的 ReLU 网络用 1/n 的方差,64 层后信号只剩 10⁻¹⁰ 量级,梯度同样传不回来;我们的项目里 σ = 0.002 的开局 loss 恰好等于 ln V,最终 val 却是 5.352,比 0.02 的 4.641 差(σ = 0.02 这一档就是参照 run ref_baseline;它与 runs/baseline 的 4.626 同配置同种子,差的 0.014 是重跑波动,见 C14)。太大会炸,太小学不动。
常见误解二:「现代 LLM 都用 Xavier / He。」GPT-2、OLMo 2 用固定的 0.02(配合 Pre-Norm 与残差写回缩放),DeepSeek-V3 用 0.006。平铺深网里「保持每层方差」的任务,已经交给了归一化。
常见误解三:「开局 loss 高一点无所谓,反正会降。」开局 logits 大,意味着开局梯度大、前几百步都在做无用功;我们的 σ = 1 那一组 400 步后最终 val 仍是 6.552。开局自检 loss ≈ ln V,是训练第一个应该看的仪表。
🔍 ReLU 为什么正好「砍掉一半」?
输入 z 关于 0 对称时,ReLU(z) = max(z, 0) 把负的一半变成 0,正的一半原样保留。于是 E[ReLU(z)²] = ½·E[z²]:二阶矩恰好减半。下一层做 Σ w·x 时,E[x²] 只有原来的一半,权重方差就要乘 2 才能补回来——He 初始化的 2/n 就是这么来的。组件里切到 ReLU,He 那一行的「实测每层倍数」约为 1.00,而 Xavier 约为 0.707 = 1/√2。
证据三问
| 问 | 本课的回答 |
|---|---|
| ① 它解决什么问题? | 让第 0 步的每个数处在能计算、能传梯度的量级:不溢出成 inf,也不消失成 0;开局 loss 从 ln V 附近开始 |
| ② 数学上为什么可能有效? | Var(Σwx) = n·Var(w)·E[x²] 在独立假设下可证 证据等级 · 数学可证明 在写明的前提下可以严格推导出来,不依赖实验。只要前提成立,换数据、换规模仍然成立。 本条独立随机变量求和的方差可加。 出处待补 ;「保持逐层方差」是 Xavier / He 的理论动机 证据等级 · 有理论动机 有数学或机制上的理由说明它可能有效,但没有证明它一定更好。最后是否采用,仍然要看实验。 本条推导依赖初始化时刻的独立、零均值假设,训练开始后不再成立。 出处待补 ;有了归一化之后,初始化只需管写回与开局 logits,这一点由块结构直接推出 |
| ③ 实验上为什么最后选了它? | He 在 30 层 ReLU 网络上验证 2/n;OLMo 2 实测固定 0.02 比按层缩放更稳;我们的项目里 0.02 在四档里最终 val 最低(见上面的项目实测) 证据等级 · 实验验证 由对比实验得出的经验结论,只在出处写明的模型、数据和规模下成立。换了条件可能不再成立,引用时要带上实验设置。 本条项目结论只在 3.7M 参数、400 步、单种子的条件下成立。 出处待补 |
一句话
先停在这里:记住「√d 逐层累乘」「0.02 能用是因为有 Pre-Norm」「开局 loss ≈ ln V」三句。
再深一层:读侧栏苏剑林那篇,再展开上面 ReLU 的 Dig。
下一个缺口
初始化算好了,原版 Transformer 为什么还离不开 warmup? → 第 2 课:原版 Transformer 为什么离不开 warmup?
回扣:C5 第 4 课讲了 warmup 管的是开局——Adam 第 1 步每个参数都走满 ±η;那一课留下的悬念「Post-LN 为什么格外离不开 warmup」,下一课兑现。
你可能会问
- 「为什么偏偏是 0.02,不是 0.01 或 0.03?」没有推导出来的答案,是 GPT-2 / BERT 一路沿用下来的经验值;侧栏苏剑林那篇专门讨论了它的来历。我们的项目只扫了 0.002 / 0.02 / 0.2 / 1 四档,0.02 最好,但更细的间隔没有测。
- 「embedding 为什么不按 fan-in 缩放?」embedding 是查表,输入是 one-hot,fan-in 的公式对它不适用;真正要管的是它乘上 lm_head 之后的开局 logits(共享 embedding 时就是同一张表)。
- 这一课还没有收到真实提问;你的提问会记到
05-问答记录/C8-L1.md。