神经网络参数初始化详解:为什么重要、常用方法及其优缺点
1. 引言
在神经网络中,模型结构、损失函数和优化器经常受到更多关注,但参数初始化同样会直接影响模型是否能够顺利训练。
所谓参数初始化,是指在正式训练开始之前,为神经网络中的权重和偏置指定初始数值。例如,一个全连接层可以写为:
z(l)=W(l)a(l−1)+b(l)\mathbf{z}^{(l)}=\mathbf{W}^{(l)}\mathbf{a}^{(l-1)}+\mathbf{b}^{(l)}z(l)=W(l)a(l−1)+b(l)
其中:
- W(l)\mathbf{W}^{(l)}W(l) 是第 lll 层的权重矩阵;
- b(l)\mathbf{b}^{(l)}b(l) 是第 lll 层的偏置向量;
- a(l−1)\mathbf{a}^{(l-1)}a(l−1) 是上一层输出;
- z(l)\mathbf{z}^{(l)}z(l) 是当前层进入激活函数之前的线性输出。
训练开始时,优化器并不是从"完全空白"的状态开始搜索,而是从初始化参数所在的位置开始调整。因此,初始化实际上决定了优化问题的起点。
初始化不合理时,模型可能出现:
- 所有神经元学习相同内容;
- 前向传播的数值迅速变大或变小;
- 激活函数进入饱和区;
- 梯度消失;
- 梯度爆炸;
- 收敛速度极慢;
- 损失函数变成
NaN; - 即使训练很久也难以达到较好结果。
初始化合理时,模型通常可以:
- 打破神经元之间的对称性;
- 保持各层激活值的合理尺度;
- 保持梯度在深层网络中的稳定传播;
- 提高训练速度;
- 降低模型对学习率的敏感程度;
- 提高训练过程的稳定性。
因此,参数初始化不是一个无关紧要的细节,而是深度神经网络能够被成功训练的重要条件。
2. 神经网络中有哪些参数需要初始化
神经网络中的主要可训练参数包括:
- 权重矩阵;
- 偏置向量;
- 归一化层的缩放参数与平移参数;
- 嵌入层参数;
- 循环神经网络中的门控参数;
- 注意力模块中的投影矩阵。
其中,最需要重点设计的是权重矩阵的初始化。
对于普通全连接层:
z=Wx+b\mathbf{z}=\mathbf{W}\mathbf{x}+\mathbf{b}z=Wx+b
通常需要初始化:
W∈Rnout×nin\mathbf{W}\in\mathbb{R}^{n_{\mathrm{out}}\times n_{\mathrm{in}}}W∈Rnout×nin
b∈Rnout\mathbf{b}\in\mathbb{R}^{n_{\mathrm{out}}}b∈Rnout
这里:
- ninn_{\mathrm{in}}nin 表示输入神经元数量;
- noutn_{\mathrm{out}}nout 表示输出神经元数量。
在初始化方法中,这两个数量通常称为:
fan_in=nin\mathrm{fan\in}=n{\mathrm{in}}fan_in=nin
fan_out=nout\mathrm{fan\out}=n{\mathrm{out}}fan_out=nout
对于卷积层,fan_in 和 fan_out 还需要考虑卷积核尺寸和通道数。
3. 参数初始化为什么重要
3.1 打破神经元之间的对称性
假设一个隐藏层中有两个神经元,并且它们的权重和偏置全部初始化为相同数值。
两个神经元的输出分别为:
z1=w1Tx+b1z_1=\mathbf{w}_1^{\mathrm{T}}\mathbf{x}+b_1z1=w1Tx+b1
z2=w2Tx+b2z_2=\mathbf{w}_2^{\mathrm{T}}\mathbf{x}+b_2z2=w2Tx+b2
如果:
w1=w2\mathbf{w}_1=\mathbf{w}_2w1=w2
b1=b2b_1=b_2b1=b2
那么对于任意输入,都有:
z1=z2z_1=z_2z1=z2
经过相同激活函数之后:
a1=a2a_1=a_2a1=a2
在反向传播过程中,两个神经元接收到的梯度也完全相同,因此它们的参数更新仍然相同。
这意味着两个神经元始终学习相同的特征,相当于浪费了一个神经元。
因此,神经网络的权重不能全部初始化为相同值,而需要通过随机初始化打破对称性。
3.2 控制前向传播的数值尺度
假设某一层包含 ninn_{\mathrm{in}}nin 个输入:
zj=∑i=1ninwjiaiz_j=\sum_{i=1}^{n_{\mathrm{in}}}w_{ji}a_izj=i=1∑ninwjiai
为了便于分析,假设:
- wjiw_{ji}wji 与 aia_iai 相互独立;
- 权重均值为 0;
- 输入均值为 0;
- 各个输入具有相同方差。
那么可以近似得到:
Var(zj)=ninVar(wji)Var(ai)\mathrm{Var}(z_j)=n_{\mathrm{in}}\mathrm{Var}(w_{ji})\mathrm{Var}(a_i)Var(zj)=ninVar(wji)Var(ai)
这个公式非常重要。
如果权重方差过大,则每经过一层,输出方差都会被放大,最终导致激活值爆炸。
如果权重方差过小,则每经过一层,输出方差都会缩小,最终导致激活值接近 0。
理想情况下,希望:
Var(zj)≈Var(ai)\mathrm{Var}(z_j)\approx\mathrm{Var}(a_i)Var(zj)≈Var(ai)
因此可以令:
ninVar(wji)≈1n_{\mathrm{in}}\mathrm{Var}(w_{ji})\approx1ninVar(wji)≈1
从而得到:
Var(wji)≈1nin\mathrm{Var}(w_{ji})\approx\frac{1}{n_{\mathrm{in}}}Var(wji)≈nin1
Xavier 初始化、He 初始化和 LeCun 初始化,本质上都围绕"保持各层数值尺度稳定"这一目标设计。
3.3 控制反向传播的梯度尺度
反向传播时,某层梯度会与权重矩阵和激活函数导数相乘。
对于第 lll 层,可以简化写为:
δ(l)=(W(l+1))Tδ(l+1)⊙ϕ′(z(l))\boldsymbol{\delta}^{(l)}=\left(\mathbf{W}^{(l+1)}\right)^{\mathrm{T}}\boldsymbol{\delta}^{(l+1)}\odot\phi'(\mathbf{z}^{(l)})δ(l)=(W(l+1))Tδ(l+1)⊙ϕ′(z(l))
其中:
- δ(l)\boldsymbol{\delta}^{(l)}δ(l) 表示第 lll 层误差信号;
- ϕ′(z(l))\phi'(\mathbf{z}^{(l)})ϕ′(z(l)) 表示激活函数导数;
- ⊙\odot⊙ 表示逐元素乘法。
如果权重过小,或者激活函数导数长期小于 1,梯度会逐层衰减,产生梯度消失。
如果权重过大,梯度可能逐层放大,产生梯度爆炸。
因此,初始化不仅需要稳定前向传播,还要兼顾反向传播。
3.4 避免激活函数过早进入饱和区
对于 Sigmoid:
σ(x)=11+e−x\sigma(x)=\frac{1}{1+\mathrm{e}^{-x}}σ(x)=1+e−x1
其导数为:
σ′(x)=σ(x)(1−σ(x))\sigma'(x)=\sigma(x)(1-\sigma(x))σ′(x)=σ(x)(1−σ(x))
当输入绝对值较大时,Sigmoid 会接近 0 或 1,导数接近 0。
如果初始权重过大,则线性输出:
z=wTx+bz=\mathbf{w}^{\mathrm{T}}\mathbf{x}+bz=wTx+b
可能从训练一开始就具有很大的绝对值,使 Sigmoid 或 Tanh 进入饱和区。
这时即使损失较大,梯度也很小,模型难以从初始状态中恢复。
因此,使用 Sigmoid 或 Tanh 时,初始化需要特别注意控制权重尺度。
4. 错误初始化会造成什么问题
4.1 所有权重初始化为 0
一种直观但错误的方法是将所有权重设置为 0:
W=0\mathbf{W}=\mathbf{0}W=0
对于线性回归或逻辑回归,权重从 0 开始通常可以训练。
但对于包含隐藏层的神经网络,这会导致同一层所有神经元完全对称。
所有神经元:
- 得到相同输出;
- 接收相同梯度;
- 执行相同更新;
- 永远学习相同特征。
因此:
神经网络隐藏层权重不能全部初始化为 0。
但是,偏置通常可以初始化为 0,因为权重的随机性已经足以打破神经元之间的对称性。
4.2 所有权重初始化为相同常数
例如:
wij=0.01w_{ij}=0.01wij=0.01
虽然权重不再是 0,但同一层所有神经元仍然拥有完全相同的参数,因此仍然存在对称性问题。
所以问题不在于权重是否为 0,而在于:
同一层不同神经元不能从完全相同的权重状态开始。
4.3 随机值过小
假设使用:
wij∼N(0,10−6)w_{ij}\sim\mathcal{N}(0,10^{-6})wij∼N(0,10−6)
权重非常小时,每一层的输出都可能接近 0。
深层网络经过多层传播后,信号可能不断衰减,导致:
- 激活值接近 0;
- 梯度非常小;
- 参数更新缓慢;
- 模型长期停留在初始状态附近。
4.4 随机值过大
假设使用:
wij∼N(0,102)w_{ij}\sim\mathcal{N}(0,10^2)wij∼N(0,102)
权重非常大时,线性输出会迅速放大。
对于 Sigmoid 和 Tanh,大输入会导致函数饱和。
对于没有归一化和梯度裁剪的网络,大权重还可能导致:
- 激活爆炸;
- 梯度爆炸;
- 损失震荡;
- 数值溢出;
- 出现
NaN。
5. 最基础的随机初始化
5.1 随机正态分布初始化
随机正态初始化可以写为:
wij∼N(μ,σ2)w_{ij}\sim\mathcal{N}(\mu,\sigma^2)wij∼N(μ,σ2)
通常取:
μ=0\mu=0μ=0
例如:
wij∼N(0,0.012)w_{ij}\sim\mathcal{N}(0,0.01^2)wij∼N(0,0.012)
优点
- 可以打破神经元之间的对称性;
- 实现简单;
- 适合小型或浅层网络的快速实验。
局限性
- 标准差需要人工选择;
- 没有考虑输入和输出维度;
- 网络变深后容易出现激活或梯度尺度不稳定;
- 对不同激活函数缺乏针对性。
适用场景
- 教学示例;
- 非常浅的网络;
- 已知网络对初始尺度不敏感的情况。
5.2 随机均匀分布初始化
随机均匀初始化可以写为:
wij∼U(−a,a)w_{ij}\sim U(-a,a)wij∼U(−a,a)
其中,aaa 决定随机数范围。
优点
- 计算和实现简单;
- 可以打破参数对称性;
- 所有权重被限制在有限区间内。
局限性
- 区间范围需要人工选择;
- 没有自动适应网络宽度;
- 过大或过小都可能引发训练问题。
随机正态和随机均匀并不是完全错误的方法,关键是必须合理选择其方差或区间。Xavier、He 和 LeCun 初始化正是在此基础上给出了更科学的尺度选择方法。
6. Xavier 初始化
Xavier 初始化也称为 Glorot 初始化,主要用于 Sigmoid、Tanh 或近似对称激活函数。
6.1 核心思想
Xavier 初始化希望同时兼顾:
- 前向传播的激活方差;
- 反向传播的梯度方差。
如果只考虑前向传播,权重方差适合取:
Var(w)=1fan_in\mathrm{Var}(w)=\frac{1}{\mathrm{fan\_in}}Var(w)=fan_in1
如果只考虑反向传播,权重方差适合取:
Var(w)=1fan_out\mathrm{Var}(w)=\frac{1}{\mathrm{fan\_out}}Var(w)=fan_out1
Xavier 初始化对二者进行折中:
Var(w)=2fan_in+fan_out\mathrm{Var}(w)=\frac{2}{\mathrm{fan\_in}+\mathrm{fan\_out}}Var(w)=fan_in+fan_out2
6.2 Xavier 正态初始化
Xavier 正态初始化为:
wij∼N(0,2fan_in+fan_out)w_{ij}\sim\mathcal{N}\left(0,\frac{2}{\mathrm{fan\_in}+\mathrm{fan\_out}}\right)wij∼N(0,fan_in+fan_out2)
其标准差为:
σ=2fan_in+fan_out\sigma=\sqrt{\frac{2}{\mathrm{fan\_in}+\mathrm{fan\_out}}}σ=fan_in+fan_out2
6.3 Xavier 均匀初始化
Xavier 均匀初始化为:
wij∼U(−6fan_in+fan_out,6fan_in+fan_out)w_{ij}\sim U\left(-\sqrt{\frac{6}{\mathrm{fan\_in}+\mathrm{fan\_out}}},\sqrt{\frac{6}{\mathrm{fan\_in}+\mathrm{fan\_out}}}\right)wij∼U(−fan_in+fan_out6 ,fan_in+fan_out6 )
6.4 为什么适合 Tanh
Tanh 在 0 附近近似线性:
tanh(x)≈x\tanh(x)\approx xtanh(x)≈x
如果初始化能够让大部分线性输出保持在 0 附近,就可以:
- 避免 Tanh 进入饱和区;
- 保持较大的导数;
- 缓解梯度消失;
- 稳定多层信号传播。
6.5 Xavier 初始化的优点
- 同时考虑输入维度和输出维度;
- 比固定标准差的随机初始化更加稳定;
- 适用于 Tanh、Sigmoid 和线性层;
- 能够较好地保持前向和反向传播的方差;
- 广泛应用于全连接网络和注意力投影层。
6.6 Xavier 初始化的局限性
- 假设激活函数近似线性或对称;
- 对 ReLU 不够理想,因为 ReLU 会将约一半输入置零;
- 在非常深的网络中,仅靠 Xavier 仍不一定能够完全避免梯度问题;
- 对残差结构、归一化层和特殊网络结构没有进行专门建模。
6.7 适用场景
通常适用于:
- Tanh 隐藏层;
- Sigmoid 网络;
- 线性激活;
- Softmax 输出层之前的线性映射;
- Transformer 中部分线性投影层;
- 没有明确使用 ReLU 系列激活函数的普通全连接层。
7. He 初始化
He 初始化也称为 Kaiming 初始化,主要针对 ReLU 及其变体设计。
7.1 为什么 Xavier 对 ReLU 不够理想
ReLU 定义为:
ReLU(x)=max(0,x)\mathrm{ReLU}(x)=\max(0,x)ReLU(x)=max(0,x)
当输入近似关于 0 对称时,大约一半输入会变为 0。
这意味着 ReLU 会降低激活方差。如果仍使用:
Var(w)=1fan_in\mathrm{Var}(w)=\frac{1}{\mathrm{fan\_in}}Var(w)=fan_in1
经过 ReLU 后,信号方差可能逐层缩小。
为了补偿 ReLU 丢弃一半信号的影响,He 初始化将权重方差放大为:
Var(w)=2fan_in\mathrm{Var}(w)=\frac{2}{\mathrm{fan\_in}}Var(w)=fan_in2
7.2 He 正态初始化
He 正态初始化为:
wij∼N(0,2fan_in)w_{ij}\sim\mathcal{N}\left(0,\frac{2}{\mathrm{fan\_in}}\right)wij∼N(0,fan_in2)
标准差为:
σ=2fan_in\sigma=\sqrt{\frac{2}{\mathrm{fan\_in}}}σ=fan_in2
7.3 He 均匀初始化
He 均匀初始化常写为:
wij∼U(−6fan_in,6fan_in)w_{ij}\sim U\left(-\sqrt{\frac{6}{\mathrm{fan\_in}}},\sqrt{\frac{6}{\mathrm{fan\_in}}}\right)wij∼U(−fan_in6 ,fan_in6 )
7.4 Leaky ReLU 对应的 He 初始化
对于 Leaky ReLU:
ϕ(x)={x,x>0ax,x≤0\phi(x)=\begin{cases}x,&x>0\\ax,&x\leq0\end{cases}ϕ(x)={x,ax,x>0x≤0
其推荐增益与负半轴斜率 aaa 有关:
gain=21+a2\mathrm{gain}=\sqrt{\frac{2}{1+a^2}}gain=1+a22
因此权重标准差可以写为:
σ=2(1+a2)fan_in\sigma=\sqrt{\frac{2}{(1+a^2)\mathrm{fan\_in}}}σ=(1+a2)fan_in2
如果 a=0a=0a=0,就退化为普通 ReLU 的 He 初始化。
7.5 He 初始化的优点
- 专门适配 ReLU 及其变体;
- 能够补偿 ReLU 丢弃负半轴信号造成的方差下降;
- 适合深层全连接网络和卷积神经网络;
- 通常比 Xavier 更适合 ReLU;
- 实现简单且效果稳定。
7.6 He 初始化的局限性
- 主要针对 ReLU 类激活函数;
- 对 Sigmoid 和 Tanh 可能使初始权重偏大;
- 在极深网络中仍可能需要配合残差连接和归一化;
- 如果
fan_in、fan_out或非线性参数配置错误,初始化效果会受到影响。
7.7 适用场景
通常适用于:
- ReLU;
- Leaky ReLU;
- PReLU;
- 深层卷积神经网络;
- 使用 ReLU 系列激活函数的多层感知机。
8. LeCun 初始化
LeCun 初始化主要用于 SELU、自归一化网络,以及某些线性或 Tanh 风格网络。
8.1 LeCun 正态初始化
LeCun 正态初始化为:
wij∼N(0,1fan_in)w_{ij}\sim\mathcal{N}\left(0,\frac{1}{\mathrm{fan\_in}}\right)wij∼N(0,fan_in1)
其标准差为:
σ=1fan_in\sigma=\sqrt{\frac{1}{\mathrm{fan\_in}}}σ=fan_in1
8.2 LeCun 均匀初始化
LeCun 均匀初始化为:
wij∼U(−3fan_in,3fan_in)w_{ij}\sim U\left(-\sqrt{\frac{3}{\mathrm{fan\_in}}},\sqrt{\frac{3}{\mathrm{fan\_in}}}\right)wij∼U(−fan_in3 ,fan_in3 )
8.3 与 Xavier 和 He 的区别
三种初始化的核心方差分别为:
VarLeCun(w)=1fan_in\mathrm{Var}_{\mathrm{LeCun}}(w)=\frac{1}{\mathrm{fan\_in}}VarLeCun(w)=fan_in1
VarXavier(w)=2fan_in+fan_out\mathrm{Var}_{\mathrm{Xavier}}(w)=\frac{2}{\mathrm{fan\_in}+\mathrm{fan\_out}}VarXavier(w)=fan_in+fan_out2
VarHe(w)=2fan_in\mathrm{Var}_{\mathrm{He}}(w)=\frac{2}{\mathrm{fan\_in}}VarHe(w)=fan_in2
可以看到:
- LeCun 主要考虑输入端方差;
- Xavier 在输入和输出之间折中;
- He 为 ReLU 的半截断特性增加了补偿系数 2。
8.4 LeCun 初始化的优点
- 形式简单;
- 适用于 SELU 和自归一化网络;
- 有助于保持前向传播中的方差;
- 比固定方差随机初始化更合理。
8.5 LeCun 初始化的局限性
- 使用范围不如 Xavier 和 He 广;
- 对 ReLU 通常不如 He 初始化;
- 自归一化效果依赖网络结构、激活函数和 Dropout 类型等条件;
- 不能简单理解为所有网络的通用最优方案。
8.6 适用场景
通常适用于:
- SELU;
- 自归一化神经网络;
- 某些需要保持输入方差的线性层;
- 明确遵循 SELU 网络设计约束的模型。
9. 正交初始化
正交初始化使权重矩阵的行或列近似相互正交。
对于方阵 Q\mathbf{Q}Q:
QTQ=I\mathbf{Q}^{\mathrm{T}}\mathbf{Q}=\mathbf{I}QTQ=I
初始化时通常先生成随机矩阵,再通过 QR 分解或奇异值分解获得正交矩阵。
最终权重可以写为:
W=gQ\mathbf{W}=g\mathbf{Q}W=gQ
其中,ggg 是缩放增益。
9.1 为什么正交矩阵有助于梯度传播
对于正交矩阵:
∥Qx∥2=∥x∥2\|\mathbf{Q}\mathbf{x}\|_2=\|\mathbf{x}\|_2∥Qx∥2=∥x∥2
这意味着在线性变换中,向量的二范数不会被任意放大或压缩。
因此,正交初始化能够在一定程度上:
- 保持信号范数;
- 保持梯度范数;
- 缓解深层线性变换中的梯度消失和爆炸。
9.2 正交初始化的优点
- 有助于保持向量范数;
- 适合深层线性网络;
- 常用于循环神经网络的隐藏状态矩阵;
- 对控制长期梯度传播有一定帮助;
- 可与适当增益结合适配不同激活函数。
9.3 正交初始化的局限性
- 初始化计算比普通随机初始化复杂;
- 对非方阵需要进行适当截取或扩展;
- 经过非线性激活后,严格的范数保持不再成立;
- 不一定适合所有卷积结构;
- 不能替代门控结构、残差连接和归一化方法。
9.4 适用场景
通常适用于:
- RNN 隐藏状态权重;
- LSTM 或 GRU 中的循环权重;
- 深层线性网络;
- 需要保持信号方向和范数的特殊结构。
10. 稀疏初始化
稀疏初始化会让权重矩阵中的大部分元素为 0,只保留少量非零随机权重。
例如,每个神经元只与上一层少量神经元建立初始连接。
可以表示为:
wij=0for most i,jw_{ij}=0\quad\text{for most }i,jwij=0for most i,j
非零部分再从某个随机分布中采样。
10.1 稀疏初始化的优点
- 减少初始连接数量;
- 可能促进不同神经元学习不同特征;
- 在某些超宽网络中可以减少初始干扰;
- 对研究稀疏网络结构具有意义。
10.2 稀疏初始化的局限性
- 不属于多数普通网络的默认选择;
- 稀疏程度需要人工设定;
- 过度稀疏可能降低信息传播能力;
- 不一定能带来实际计算加速,因为训练框架可能仍使用稠密矩阵;
- 对最终性能的收益具有任务依赖性。
10.3 适用场景
- 稀疏神经网络研究;
- 超宽网络;
- 特殊结构实验;
- 需要限制初始连接数量的模型。
11. 截断正态初始化
截断正态分布会先从正态分布采样,再丢弃距离均值过远的极端值。
普通正态初始化可能偶尔产生绝对值很大的权重,而截断正态可以减少这些异常初始值。
可以简单理解为:
w∼N(μ,σ2),w∈μ−kσ,μ+kσw\sim\mathcal{N}(\mu,\sigma^2),\quad w\in\\mu-k\\sigma,\\mu+k\\sigmaw∼N(μ,σ2),w∈μ−kσ,μ+kσ
11.1 优点
- 减少极端大权重;
- 初始数值更加集中;
- 在 Transformer、视觉 Transformer 和大型模型中较常见;
- 可以降低初始阶段数值异常的概率。
11.2 局限性
- 仍然需要合理选择标准差;
- 截断会改变原始正态分布的精确方差;
- 不同框架对截断范围和重采样方式的实现可能不同;
- 并不是所有网络都需要截断正态。
11.3 适用场景
- Transformer;
- Vision Transformer;
- 大型嵌入层;
- 明确采用较小标准差初始化的现代模型。
12. 常数初始化适用于哪些参数
"不能使用常数初始化"主要针对隐藏层权重,而不是所有参数。
12.1 偏置初始化为 0
普通全连接层和卷积层的偏置通常可以设置为:
b=0\mathbf{b}=\mathbf{0}b=0
原因是权重已经使用随机方式打破对称性,因此偏置不需要再随机化。
优点
- 简单;
- 稳定;
- 不会额外引入初始偏移。
局限性
- 某些特殊结构需要非零偏置;
- 对所有模型机械地设为 0 可能忽略结构先验。
12.2 ReLU 偏置初始化为小正数
有时会将 ReLU 神经元的偏置初始化为小正值,例如:
b=0.01b=0.01b=0.01
其目标是让更多神经元在训练初期处于激活状态。
但这种方法并不是必须的,也不是所有任务都有效。
优点
- 可能减少初始阶段的神经元失活;
- 让部分 ReLU 更容易进入正区间。
局限性
- 会引入整体正偏移;
- 不一定优于零偏置;
- 过大时可能导致激活均值偏离 0。
12.3 LSTM 遗忘门偏置
LSTM 中的遗忘门可以写为:
ft=σ(Wfxt+Ufht−1+bf)\mathbf{f}_t=\sigma(\mathbf{W}_f\mathbf{x}_t+\mathbf{U}f\mathbf{h}{t-1}+\mathbf{b}_f)ft=σ(Wfxt+Ufht−1+bf)
有时会将遗忘门偏置初始化为较大的正值,例如:
bf=1\mathbf{b}_f=\mathbf{1}bf=1
这样训练初期:
ft≈σ(1)>0.5\mathbf{f}_t\approx\sigma(1)>0.5ft≈σ(1)>0.5
模型更倾向于保留历史信息。
优点
- 有助于保留长期状态;
- 在部分序列任务中可以改善训练初期表现。
局限性
- 不是所有实现和任务都必须这样设置;
- 过大的遗忘门偏置可能使模型过度保留旧信息。
13. 归一化层的参数初始化
Batch Normalization 可以写为:
y=γx^+βy=\gamma\hat{x}+\betay=γx^+β
其中:
- γ\gammaγ 是缩放参数;
- β\betaβ 是平移参数。
常见初始化方式为:
γ=1\gamma=1γ=1
β=0\beta=0β=0
这样在训练初期,归一化层近似保持标准化后的输入不变。
13.1 残差网络中的特殊初始化
对于残差块:
y=x+F(x)\mathbf{y}=\mathbf{x}+F(\mathbf{x})y=x+F(x)
有时会将残差分支最后一个归一化层的缩放参数初始化为 0:
γlast=0\gamma_{\mathrm{last}}=0γlast=0
这样训练开始时:
F(x)≈0F(\mathbf{x})\approx0F(x)≈0
残差块初始状态近似为恒等映射:
y≈x\mathbf{y}\approx\mathbf{x}y≈x
这有助于非常深的残差网络从稳定状态开始训练。
14. 不同激活函数与初始化方法如何匹配
| 激活函数或网络结构 | 推荐初始化方法 | 原因 |
|---|---|---|
| Sigmoid | Xavier | 控制初始输入尺度,减少饱和 |
| Tanh | Xavier | 让输入尽量位于近似线性区域 |
| ReLU | He/Kaiming | 补偿负半轴被截断造成的方差损失 |
| Leaky ReLU | 带斜率参数的 He | 根据负半轴斜率调整增益 |
| PReLU | 带实际斜率的 He | 与可学习负斜率匹配 |
| SELU | LeCun Normal | 配合自归一化性质 |
| GELU | Xavier 或较小截断正态 | 保持初始尺度稳定,具体取决于架构 |
| Swish | Xavier 或 He 风格初始化 | 需要结合具体网络和框架验证 |
| 线性层 | Xavier 或 LeCun | 保持线性传播方差 |
| RNN 循环权重 | 正交初始化 | 有助于保持长期梯度范数 |
| Transformer 投影层 | Xavier 或截断正态 | 常见于注意力和前馈线性层 |
| 卷积网络 ReLU 层 | He/Kaiming | 适配 ReLU 非线性 |
| 残差分支末端 | 零初始化最后缩放参数 | 使残差块初始近似恒等映射 |
需要注意:
初始化方法与激活函数之间不是完全独立的,选择初始化时必须考虑后续使用的非线性函数。
15. Xavier、He 和 LeCun 的统一理解
这三种初始化都在解决同一个问题:
如何让信号经过多层传播后,方差不要迅速变大或变小。
它们的差异主要来自对激活函数的不同假设。
LeCun
Var(w)=1fan_in\mathrm{Var}(w)=\frac{1}{\mathrm{fan\_in}}Var(w)=fan_in1
主要保持前向传播的输入方差。
Xavier
Var(w)=2fan_in+fan_out\mathrm{Var}(w)=\frac{2}{\mathrm{fan\_in}+\mathrm{fan\_out}}Var(w)=fan_in+fan_out2
在前向传播和反向传播之间折中。
He
Var(w)=2fan_in\mathrm{Var}(w)=\frac{2}{\mathrm{fan\_in}}Var(w)=fan_in2
针对 ReLU 会丢弃一半输入的情况进行补偿。
因此,它们并不是互相竞争的三个"固定答案",而是针对不同激活函数和方差传播假设得到的不同方案。
16. PyTorch 中的参数初始化
PyTorch 提供了 torch.nn.init 模块。
16.1 ReLU 网络使用 He 初始化
python
import torch
import torch.nn as nn
class FeedforwardNN(nn.Module):
def __init__(self, input_dim: int, output_dim: int) -> None:
super().__init__()
self.network = nn.Sequential(
nn.Linear(input_dim, 128),
nn.ReLU(),
nn.Linear(128, 64),
nn.ReLU(),
nn.Linear(64, output_dim)
)
self.apply(self._initialize_weights)
@staticmethod
def _initialize_weights(module: nn.Module) -> None:
if isinstance(module, nn.Linear):
nn.init.kaiming_normal_(
module.weight,
mode="fan_in",
nonlinearity="relu"
)
if module.bias is not None:
nn.init.zeros_(module.bias)
def forward(self, x: torch.Tensor) -> torch.Tensor:
return self.network(x)
16.2 Tanh 网络使用 Xavier 初始化
python
import torch
import torch.nn as nn
class TanhNetwork(nn.Module):
def __init__(self, input_dim: int, output_dim: int) -> None:
super().__init__()
self.network = nn.Sequential(
nn.Linear(input_dim, 64),
nn.Tanh(),
nn.Linear(64, 32),
nn.Tanh(),
nn.Linear(32, output_dim)
)
self.apply(self._initialize_weights)
@staticmethod
def _initialize_weights(module: nn.Module) -> None:
if isinstance(module, nn.Linear):
gain = nn.init.calculate_gain("tanh")
nn.init.xavier_normal_(module.weight, gain=gain)
if module.bias is not None:
nn.init.zeros_(module.bias)
def forward(self, x: torch.Tensor) -> torch.Tensor:
return self.network(x)
16.3 Leaky ReLU 使用带斜率的 Kaiming 初始化
python
import torch
import torch.nn as nn
class LeakyReLUNetwork(nn.Module):
def __init__(
self,
input_dim: int,
output_dim: int,
negative_slope: float = 0.01
) -> None:
super().__init__()
self.negative_slope = negative_slope
self.network = nn.Sequential(
nn.Linear(input_dim, 128),
nn.LeakyReLU(negative_slope),
nn.Linear(128, 64),
nn.LeakyReLU(negative_slope),
nn.Linear(64, output_dim)
)
self.apply(self._initialize_weights)
def _initialize_weights(self, module: nn.Module) -> None:
if isinstance(module, nn.Linear):
nn.init.kaiming_normal_(
module.weight,
a=self.negative_slope,
mode="fan_in",
nonlinearity="leaky_relu"
)
if module.bias is not None:
nn.init.zeros_(module.bias)
def forward(self, x: torch.Tensor) -> torch.Tensor:
return self.network(x)
16.4 正交初始化循环权重
python
import torch
import torch.nn as nn
def initialize_lstm(lstm: nn.LSTM) -> None:
for name, parameter in lstm.named_parameters():
if "weight_ih" in name:
nn.init.xavier_uniform_(parameter)
elif "weight_hh" in name:
nn.init.orthogonal_(parameter)
elif "bias" in name:
nn.init.zeros_(parameter)
hidden_size = parameter.shape[0] // 4
with torch.no_grad():
parameter[hidden_size:2 * hidden_size].fill_(1.0)
这里:
- 输入到隐藏状态的权重使用 Xavier;
- 隐藏状态到隐藏状态的权重使用正交初始化;
- 偏置默认置 0;
- 遗忘门偏置置为 1。
实际使用时,需要确认具体框架中 LSTM 四个门的参数排列顺序。
17. NumPy 手工实现常见初始化
python
import numpy as np
def xavier_normal(
fan_in: int,
fan_out: int,
rng: np.random.Generator
) -> np.ndarray:
std = np.sqrt(2.0 / (fan_in + fan_out))
return rng.normal(0.0, std, size=(fan_out, fan_in))
def he_normal(
fan_in: int,
fan_out: int,
rng: np.random.Generator
) -> np.ndarray:
std = np.sqrt(2.0 / fan_in)
return rng.normal(0.0, std, size=(fan_out, fan_in))
def lecun_normal(
fan_in: int,
fan_out: int,
rng: np.random.Generator
) -> np.ndarray:
std = np.sqrt(1.0 / fan_in)
return rng.normal(0.0, std, size=(fan_out, fan_in))
rng = np.random.default_rng(seed=42)
weight_xavier = xavier_normal(128, 64, rng)
weight_he = he_normal(128, 64, rng)
weight_lecun = lecun_normal(128, 64, rng)
print(weight_xavier.shape)
print(weight_he.shape)
print(weight_lecun.shape)
18. 如何检查初始化是否合理
仅仅调用某种初始化函数还不够,还应该观察网络实际产生的激活和梯度。
18.1 检查各层激活值
可以统计:
- 均值;
- 标准差;
- 最大值;
- 最小值;
- 0 值比例;
- 饱和比例。
理想情况下,各层激活不应:
- 迅速趋近于 0;
- 迅速增长到极大值;
- 大量进入 Sigmoid/Tanh 饱和区;
- 在 ReLU 中出现几乎全部为 0 的情况。
18.2 检查各层梯度
可以统计每层权重梯度范数:
∥∇W(l)L∥2\|\nabla_{\mathbf{W}^{(l)}}\mathcal{L}\|_2∥∇W(l)L∥2
如果靠近输入层的梯度远小于输出层,可能存在梯度消失。
如果梯度突然极大或出现无穷值,可能存在梯度爆炸。
18.3 PyTorch 中打印参数和梯度统计量
python
def print_parameter_statistics(model: torch.nn.Module) -> None:
for name, parameter in model.named_parameters():
data = parameter.detach()
print(
f"{name:30s} "
f"mean={data.mean().item(): .6f} "
f"std={data.std().item(): .6f} "
f"min={data.min().item(): .6f} "
f"max={data.max().item(): .6f}"
)
def print_gradient_statistics(model: torch.nn.Module) -> None:
for name, parameter in model.named_parameters():
if parameter.grad is None:
continue
gradient = parameter.grad.detach()
print(
f"{name:30s} "
f"grad_mean={gradient.mean().item(): .6f} "
f"grad_std={gradient.std().item(): .6f} "
f"grad_norm={gradient.norm().item(): .6f}"
)
19. 初始化与其他训练技术的关系
参数初始化非常重要,但它不是独立解决所有训练问题的万能方法。
现代深度网络通常同时依赖:
- 合理初始化;
- Batch Normalization 或 Layer Normalization;
- 残差连接;
- 合理学习率;
- 学习率调度;
- 梯度裁剪;
- 合适激活函数;
- 优化器;
- 正则化方法。
例如,残差连接可以写为:
y=x+F(x)\mathbf{y}=\mathbf{x}+F(\mathbf{x})y=x+F(x)
即使 F(x)F(\mathbf{x})F(x) 的梯度传播不够稳定,恒等路径仍然允许梯度直接传播。
归一化层也可以动态控制训练过程中的激活尺度。
因此,现代神经网络的稳定训练通常是多种技术共同作用的结果。
20. 参数初始化与随机种子
随机初始化意味着每次训练的起点可能不同。
为了提高实验可复现性,通常需要固定随机种子。
python
import random
import numpy as np
import torch
def set_random_seed(seed: int) -> None:
random.seed(seed)
np.random.seed(seed)
torch.manual_seed(seed)
if torch.cuda.is_available():
torch.cuda.manual_seed(seed)
torch.cuda.manual_seed_all(seed)
set_random_seed(42)
但是,固定随机种子并不意味着不同环境下结果绝对一致,因为:
- GPU 算法可能具有非确定性;
- 框架版本可能不同;
- 驱动和硬件可能不同;
- 并行计算顺序可能不同。
随机种子的主要作用是降低由初始化和数据顺序造成的随机差异。
21. 参数初始化的常见误区
21.1 误区一:所有参数都不能初始化为 0
不准确。
- 隐藏层权重不能全部为 0;
- 普通偏置通常可以为 0;
- BatchNorm 的平移参数通常为 0;
- 某些残差分支最后的缩放参数也可以为 0。
21.2 误区二:He 初始化永远优于 Xavier
不准确。
He 初始化主要适用于 ReLU 系列,Xavier 更适合 Tanh、Sigmoid 和线性映射。
初始化方法需要与激活函数和网络结构匹配。
21.3 误区三:只要用了 BatchNorm,初始化就不重要
不准确。
BatchNorm 可以缓解激活尺度问题,但:
- 第一轮前向传播仍由初始化决定;
- 梯度传播仍会受到初始参数影响;
- 不合理初始化仍可能导致数值异常;
- 并非所有网络都使用 BatchNorm。
21.4 误区四:初始化只影响训练速度,不影响最终结果
不准确。
初始化不仅影响速度,还可能决定优化器最终进入哪个局部最优区域。
不同初始化可能导致:
- 最终精度不同;
- 泛化性能不同;
- 收敛状态不同;
- 是否出现训练失败不同。
因此,严谨实验通常应使用多个随机种子重复训练,并报告均值和标准差。
21.5 误区五:框架默认初始化一定最适合当前模型
深度学习框架的默认初始化通常是合理的通用选择,但不一定与自定义网络完全匹配。
例如:
- 自定义激活函数可能需要特殊增益;
- RNN 循环矩阵可能更适合正交初始化;
- 残差网络可能需要特殊零初始化;
- SELU 网络需要满足自归一化约束;
- 输出层可能需要根据任务设置较小初始权重。
因此,需要了解框架默认值,而不是完全依赖默认行为。
22. 各初始化方法优缺点总结
| 初始化方法 | 核心公式或特点 | 主要优点 | 主要局限性 | 常见适用场景 |
|---|---|---|---|---|
| 全零初始化 | 所有权重为 0 | 简单 | 隐藏层无法打破对称性 | 普通偏置 |
| 相同常数初始化 | 所有权重相同 | 简单 | 仍然存在对称性 | 不推荐用于隐藏层权重 |
| 小随机正态 | 固定较小标准差 | 实现简单 | 不适应网络宽度 | 浅层教学模型 |
| 随机均匀 | 固定区间采样 | 范围可控 | 区间需人工设定 | 浅层实验 |
| Xavier | 2/(fan_in+fan_out)2/(\mathrm{fan\_in}+\mathrm{fan\_out})2/(fan_in+fan_out) | 兼顾前向和反向方差 | 不专门适配 ReLU | Tanh、Sigmoid、线性层 |
| He/Kaiming | 2/fan_in2/\mathrm{fan\_in}2/fan_in | 适配 ReLU 系列 | 对饱和激活可能偏大 | ReLU、Leaky ReLU、CNN |
| LeCun | 1/fan_in1/\mathrm{fan\_in}1/fan_in | 适合 SELU | 通用性较弱 | SELU、自归一化网络 |
| 正交初始化 | 权重矩阵近似正交 | 保持范数,利于长期梯度 | 构造更复杂 | RNN、循环权重 |
| 稀疏初始化 | 大量权重为 0 | 限制初始连接 | 收益依赖任务 | 稀疏网络研究 |
| 截断正态 | 去除正态分布极端值 | 减少异常大权重 | 标准差仍需设计 | Transformer、ViT |
| 零缩放残差分支 | 末端缩放参数为 0 | 初始近似恒等映射 | 依赖残差结构 | 深层 ResNet |
23. 实际选择建议
可以使用下面的经验规则。
普通多层感知机使用 ReLU
推荐:
text
He Normal 或 He Uniform
普通卷积神经网络使用 ReLU
推荐:
text
Kaiming Normal,mode="fan_out" 或根据实现选择 fan_in
具体模式需要结合目标是保持前向激活还是反向梯度。
隐藏层使用 Tanh
推荐:
text
Xavier Normal 或 Xavier Uniform
隐藏层使用 Leaky ReLU
推荐:
text
带 negative_slope 参数的 Kaiming 初始化
使用 SELU
推荐:
text
LeCun Normal
同时需要尽量遵循自归一化网络的其他设计条件。
RNN、LSTM、GRU
推荐考虑:
text
输入权重使用 Xavier
循环权重使用 Orthogonal
偏置通常为 0
LSTM 遗忘门偏置可设为 1
Transformer
常见方案包括:
text
Xavier
较小标准差正态分布
截断正态分布
具体选择通常应遵循原始架构或官方实现。
24. 一个完整的初始化策略示例
假设网络结构为:
text
输入层
→ Linear
→ ReLU
→ Linear
→ ReLU
→ Linear
→ 输出层
可以采用:
text
隐藏层权重:He Normal
隐藏层偏置:0
输出层权重:Xavier Normal
输出层偏置:0
PyTorch 示例:
python
import torch
import torch.nn as nn
class RegressionNetwork(nn.Module):
def __init__(self, input_dim: int, output_dim: int) -> None:
super().__init__()
self.hidden_one = nn.Linear(input_dim, 128)
self.hidden_two = nn.Linear(128, 64)
self.output_layer = nn.Linear(64, output_dim)
self.activation = nn.ReLU()
self._initialize_parameters()
def _initialize_parameters(self) -> None:
nn.init.kaiming_normal_(
self.hidden_one.weight,
mode="fan_in",
nonlinearity="relu"
)
nn.init.zeros_(self.hidden_one.bias)
nn.init.kaiming_normal_(
self.hidden_two.weight,
mode="fan_in",
nonlinearity="relu"
)
nn.init.zeros_(self.hidden_two.bias)
nn.init.xavier_normal_(self.output_layer.weight)
nn.init.zeros_(self.output_layer.bias)
def forward(self, x: torch.Tensor) -> torch.Tensor:
x = self.activation(self.hidden_one(x))
x = self.activation(self.hidden_two(x))
return self.output_layer(x)
这个例子体现了一个重要思想:
不同层可以根据其后续激活函数和功能使用不同的初始化方法。
25. 总结
神经网络参数初始化决定了模型从什么位置开始优化,也决定了训练初期各层激活值和梯度的数值尺度。
一个合理的初始化方法需要完成三个核心任务:
- 打破不同神经元之间的对称性;
- 保持前向传播中激活值的方差稳定;
- 保持反向传播中梯度的方差稳定。
最常见的初始化方法可以概括为:
- Xavier 初始化:适合 Tanh、Sigmoid 和线性层;
- He 初始化:适合 ReLU、Leaky ReLU 和卷积网络;
- LeCun 初始化:适合 SELU 和自归一化网络;
- 正交初始化:适合 RNN 循环权重和需要保持范数的结构;
- 截断正态初始化:常见于 Transformer 和大型模型;
- 零初始化:适合普通偏置和部分特殊缩放参数,但不适合隐藏层全部权重。
最重要的原则是:
参数初始化不能脱离激活函数和网络结构单独选择。
在实际开发中,不应该只机械地记住"Xavier、He、LeCun"三个名称,而应该理解它们背后的共同目标:让信号和梯度在多层网络中保持合理尺度。
只有当初始化、激活函数、归一化、残差连接、优化器和学习率共同匹配时,深层神经网络才能稳定而高效地完成训练。