各种初始化方法整理总结

初始化方法概览

初始化方法 服从分布 说明
均匀分布 将权值与偏置进行均匀分布的初始化
高斯分布 初始化为服从 $N(\mu, \sigma ^2)$ 的高斯分布
Xavier $W \sim U\Big[-\frac{\sqrt{6}}{\sqrt{n_j+n_{j+1}}},\frac{\sqrt{6}}{\sqrt{n_j + n_{j+1}}} \Big]$,
服从均值为 0, 方差为 $\frac{2}{n_i + n_{i+1}}$ 的均匀分布
公式中, $n_i$ 为本层输入的神经元个数, $n_{i+1}$ 为本层输出的神经元个数, 适合于线性激活函数(原文公式推导的假设)
MSRA(Kaiming) 基于均值为0, 方差为 $\sqrt{\frac{2}{(1+a^2)\times fan_{in}}}$ 的高斯分布 它特别适合 ReLU 激活函数(非线性)
双线性初始化 常用在反卷积网络里的权值初始化

初始化方法详细说明

Xavier(‘zeiviə’)

核心理念是: 优秀的初始化方法应该使得各层的激活值和状态梯度在传播过程中的方差保持一致

再继续推导之前, 需要先提出以下假设:

  • 首先,输入数据来说,其均值和方差应满足: $E(x)=0, Var(x)=1$ (通过BN,较容易满足)
  • 权重矩阵 $W$ 和 网络输入 $x$ 互相独立
  • 每层输入的每个特征方差一样
  • 激活函数对称: 这主要是为了满足均值为0的假设
  • 激活函数是线性的, 也就是说其导数为1
  • 初始时, 状态值落在激活函数的线性区域, 即此时导数为1

现在假设有一个 $n$ 维的输入向量 $\vec X$ 和一个单层的线性神经网络, 它的权重向量是 $\vec W$, 网络的输出是 $Y$, 则有:

对于每个 $W_i X_i$, 它对应的方差为:

当输入的 $X$ 均值为 0 时(通过 BN, 较容易满足), 输出的方差就是:

进一步假设 $W_i$ 和 $X_i$ 是独立同分布的, 就可以得到:

也就是说输出的方差跟输入的方差只是相差了一个倍数 $nVar(W_i)$, 因此, 为了保证前向传播和反向传播时每一层的方差一致, 则有下面的公式成立:

同时考虑反向传播时输入输出刚好相反, 于是就有:

权衡上面两个公式, 最终给出的权重方差为:

再由概率统计中均匀分布方差的性质反推,可以得到Xavier最终的初始化分布如下:

Xavier在Caffe中的具体实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
template <typename Dtype>
class XavierFiller : public Filler<Dtype> {
public:
explicit XavierFiller(const FillerParameter& param)
: Filler<Dtype>(param) {}
virtual void Fill(Blob<Dtype>* blob) {
CHECK(blob->count());
int fan_in = blob->count() / blob->num();
int fan_out = blob->count() / blob->channels();
Dtype n = fan_in; // default to fan_in
if (this->filler_param_.variance_norm() ==
FillerParameter_VarianceNorm_AVERAGE) {
n = (fan_in + fan_out) / Dtype(2);
} else if (this->filler_param_.variance_norm() ==
FillerParameter_VarianceNorm_FAN_OUT) {
n = fan_out;
}
Dtype scale = sqrt(Dtype(3) / n);
caffe_rng_uniform<Dtype>(blob->count(), -scale, scale,
blob->mutable_cpu_data());
CHECK_EQ(this->filler_param_.sparse(), -1)
<< "Sparsity not supported by this Filler.";
}
};

可以看出, Caffe的Xavier实现有三种选择:

(1) FAN_IN, 方差只考虑输入个数:

(2) FAN_OUT, 方差只考虑输出个数:

(3) AVERAGE, 方差同时考虑输入和输出个数:

MSRA

kaiming 初始化给出的公式解释时, ReLU 函数把输出的一半负值都置零了, 为了保持输入输出的方差一致, 显然需要把原来正数输出

初始化方法讨论

为什么不能全0初始化

首先, 在神经网络中, 每一层中的任意神经元都是同构的, 它们拥有相同的输入, 如果再将参数全部初始化为同样的值(如0), 那么输出也就是相同的, 反过来它们的梯度也都是相同的. 那么无论是前向传播还是反向传播的取值都是完全相同的, 那么每一个神经元都是基于input做相同的事情, 这样一来, 不同的神经元根本无法学到不同的特征, 这样就失去网络学习特征的意义了