权重初始化与训练稳定性 本节摘要:初始化错了,训练压根起不来;初始化对了,50 层能像 3 层一样顺。把所有权重初始化为零——什么都学不到:每个神经元算同一个函数、收同一个梯度、做同一个更新,10000 轮后你那 512 神经元的隐藏层还是 512 个一模一样的神经元,你花了 512 份参数的钱,只得到 1 份。初始化太大——激活逐层爆炸,第 10 层冲到 1e15,第 20 层溢出到无穷,梯度反向如法炮制。从标准正态分布随机初始化——3 层能跑,50 层信号要么坍缩到零、要么爆炸到无穷,全看随机尺度是稍小还是稍大,「能跑」与「崩坏」的边界薄如刀刃。权重初始化是深度学习里最被低估的决策:架构能发论文,优化器能写博客,初始化只配脚注——但搞错了,别的全白搭,你的网络在训练开始前就已经死了。
本节摘要:初始化错了,训练压根起不来;初始化对了,50 层能像 3 层一样顺。把所有权重初始化为零——什么都学不到:每个神经元算同一个函数、收同一个梯度、做同一个更新,10000 轮后你那 512 神经元的隐藏层还是 512 个一模一样的神经元,你花了 512 份参数的钱,只得到 1 份。初始化太大——激活逐层爆炸,第 10 层冲到 1e15,第 20 层溢出到无穷,梯度反向如法炮制。从标准正态分布随机初始化——3 层能跑,50 层信号要么坍缩到零、要么爆炸到无穷,全看随机尺度是稍小还是稍大,「能跑」与「崩坏」的边界薄如刀刃。权重初始化是深度学习里最被低估的决策:架构能发论文,优化器能写博客,初始化只配脚注——但搞错了,别的全白搭,你的网络在训练开始前就已经死了。本节从零实现零初始化、随机初始化、Xavier/Glorot、Kaiming/He 四种策略,推导方差为何要取 2/(fan_in+fan_out) 与 2/fan_in,实测 50 层网络里激活幅度的演化,给出按激活函数选初始化的决策图。
阅读完本节,你应当能够:
把所有权重初始化为零——什么都学不到。每个神经元算同一个函数、收同一个梯度、做同一个更新,10000 轮后你那 512 神经元的隐藏层还是 512 个同样的神经元,你花了 512 份参数的钱,只得到 1 份。
初始化太大——激活逐层爆炸,第 10 层冲到 1e15,第 20 层溢出到无穷,梯度反向如法炮制。从标准正态分布随机初始化——3 层能跑,50 层信号要么坍缩到零、要么爆炸到无穷,「能跑」与「崩坏」的边界薄如刀刃。
权重初始化是深度学习里最被低估的决策:架构能发论文,优化器能写博客,初始化只配脚注。但搞错了别的全白搭——你的网络在训练开始前就已经死了。
同一层里每个神经元结构相同:输入乘权重、加偏置、过激活。若所有权重从同一个值起步(零是极端情况),每个神经元算同一个输出,反向传播时每个神经元收同一个梯度,更新时每个神经元变同样多。你就卡住了——网络有几百参数,却全在齐步走。这叫对称性,随机初始化是打破它的蛮力手段:每个神经元从权重空间的不同点起步,各学不同特征。但「随机」还不够——随机的尺度才决定网络训不训得动。
考虑一个 fan_in 个输入的单层:
z = w1*x1 + w2*x2 + ... + w_n*x_n
若每个权重 wi 来自方差 Var(w) 的分布、每个输入 xi 方差 Var(x),则输出方差:
Var(z) = fan_in * Var(w) * Var(x)
Var(w) = 1、fan_in = 512 时,输出方差是输入方差的 512 倍,10 层后 512^10 = 1.2e27,信号爆炸。Var(w) = 0.001 时,输出方差每层缩 0.001 × 512 = 0.512,10 层后 0.512^10 = 0.00013,信号消失。目标:选 Var(w) 使 Var(z) = Var(x),信号幅度跨层保持恒定。
Glorot 与 Bengio(2010)为 sigmoid、tanh 推导出解。要让前向与反向的方差都恒定:
Var(w) = 2 / (fan_in + fan_out)
实践中权重取自:
w ~ Uniform(-limit, limit) 其中 limit = sqrt(6 / (fan_in + fan_out))
或:
w ~ Normal(0, sqrt(2 / (fan_in + fan_out)))
之所以有效:sigmoid、tanh 在零附近近似线性,而恰当初始化的激活就活在那里,方差能跨几十层保持稳定。
ReLU 把一半输出砍成零(负的全变零),有效 fan_in 减半,因为平均一半输入被置零。Xavier 没考虑这点——它低估了所需方差。He 等人(2015)调整公式:
Var(w) = 2 / fan_in
权重取自:
w ~ Normal(0, sqrt(2 / fan_in))
那个因子 2 补偿 ReLU 置零一半激活。没有它,信号每层缩约 0.5 倍,50 层:0.5^50 = 8.8e-16。Kaiming 初始化防住了这一点。
GPT-2 引入了不同模式。残差连接把每个子层的输出加到其输入:
x = x + sublayer(x)
每次加都增大方差,N 个残差层后方差正比于 N 增长。GPT-2 把残差层的权重按 1/sqrt(2N) 缩放(N 是层数),保持累积信号幅度稳定。Llama 3(4050 亿参数、126 层)用类似方案——没有这个缩放,残差流会穿过 126 层注意力和前馈块无限增长。
完整代码见原课程 phases/03-deep-learning-core/08-weight-initialization/code/ 相应文件。
每种返回 fan_in 列、fan_out 行的二维矩阵。
import math import random def zero_init(fan_in, fan_out): return [[0.0 for _ in range(fan_in)] for _ in range(fan_out)] def random_init(fan_in, fan_out, scale=1.0): return [[random.gauss(0, scale) for _ in range(fan_in)] for _ in range(fan_out)] def xavier_init(fan_in, fan_out): std = math.sqrt(2.0 / (fan_in + fan_out)) return [[random.gauss(0, std) for _ in range(fan_in)] for _ in range(fan_out)] def kaiming_init(fan_in, fan_out): std = math.sqrt(2.0 / fan_in) return [[random.gauss(0, std) for _ in range(fan_in)] for _ in range(fan_out)]
注意 Xavier 用 fan_in + fan_out(兼顾前向反向),Kaiming 只用 fan_in(因子 2 补偿 ReLU 置零)。
需要 sigmoid、tanh、ReLU,以便把每种初始化配上它设计的激活。
把随机数据逐层推过深网,记录每层激活幅度的均值。这是检验初始化是否健康的金标准。
跑全部组合:零初始化、随机 N(0,1)、随机 N(0,0.01)、Xavier+sigmoid、Xavier+tanh、Kaiming+ReLU。打印关键层(第 1、5、10、25、50 层)的幅度——零初始化和「瞎选尺度」的随机初始化要么爆炸要么消失,只有匹配激活的 Xavier/Kaiming 能在 50 层保持约 1.0。
用零初始化搭 4 个神经元,喂同样输入,4 个输出完全相同——这就是「512 个神经元等于 1 个」的实证。
画出 50 层激活幅度的文字条形图,直观看到不同初始化在第几层爆炸或消失。
PyTorch 把这些做成内置函数:
import torch import torch.nn as nn layer = nn.Linear(512, 256) nn.init.xavier_uniform_(layer.weight) nn.init.xavier_normal_(layer.weight) nn.init.kaiming_uniform_(layer.weight, nonlinearity='relu') nn.init.kaiming_normal_(layer.weight, nonlinearity='relu') nn.init.zeros_(layer.bias)
调用 nn.Linear(512, 256) 时,PyTorch 默认用 Kaiming 均匀初始化——这正是多数简单网络「拿来就能跑」的原因,PyTorch 已经替你做了正确选择。但当你搭自定义架构或深过 20 层时,就需要懂背后发生了什么、必要时覆盖默认值。Transformer 方面,HuggingFace 模型通常在 _init_weights 里处理初始化,GPT-2 的实现对残差投影乘 1/sqrt(N);若你从零搭 Transformer,这一步得自己加。
本节产出(位于原课程 outputs/):
prompt-init-strategy.md:一个提示,诊断权重初始化问题并推荐正确策略。下一节,我们讲学习率调度——最优学习率不是常数,它随训练阶段变化,调度用错,90% 与 95% 精度的差距常就在这里。