初始化网络参数
初始化网络参数
#深度学习
为什么要给网络参数赋初值
既然网络参数通过训练得到,那么其初值是否重要?设置初值不佳是否只影响收敛速度而不影响模型结果?网络参数是否可以设置为全 0 或者全 1?
假设网络的参数W初值都是0,如下图所示,无论输入任何X,第一层的输出A将都为0,再向前传递到 y 也是 0,使用误差函数调参时,每一层的梯度只与该层的输入和输出有关,由于 a1,a2 值相等,计算出的梯度调整的值,以及调整后的梯度也相等;第二次迭代也同理,由于 a1,a2 相等,w[2]中各单元的值也相等。因此该层有 100 个单元与 1 个单元没有差异,该问题被称为“对称性”问题。
试想将 w 设置成全 1,则有 a1=x1+x2,a2=x1+x2,a1 与 a2 值仍然相同,对称性问题依然存在。由此,一般将参数设置为随机值。
设置成随机值还不够,还需要设置成较小的随机值,试想如果 w 的均值在 0.5 附近,某一层的输入输出都为 500 个元素,那么经过该层乘和加的运算,输出约是输入值的 250 倍;如果存在多层,250x250x…,很快就爆炸了。如果在层后使用 Sigmoid 函数,将值映射到较小的空间,又会发生非线性激活函数的饱和问题,使收敛变慢。
因此,简单的方法是:
1 | W = np.random.randn(o_dim, i_dim) * 0.01 |
bias 不导致对称性,一般设置为 0。
常用的初值化方法
全 0 或全 1 的初始化方法不能使用,而随机初始化也存在一些问题,由于各层的输入和输出元素个数不同,这使得每一层输出数据的方差也不同,比如层输入 500 个元素和 5 个元素,同等大小的 w,输出的大小可能差出百倍。不同层的调参将受到影响。
Xavier 初始化
假设层的输入有三个元素 x1,x2,x3,输入为 y,权重分别是 w1,w2,w3,
则 y 值为:
y=w1x1+w2x2+w3x3,在计算参数 w 的初值时考虑到输入该层元素的个数 n:于是出现了 Xavier 方法。
Kaiming 初始化
Xavier 的问题是,它没有考虑到激活函数对输出数据分布的影响,它会带偏当前广泛使用的 ReLU 激活函数的结果,于是 He Kaiming 提取了针对 ReLU 激活函数的 Kaiming 初始化(有时也叫作 He 初始化)。
其原理是:由于 ReLU 过滤掉了 0 以下的输入值,因此,激活函数输出的均值将大于 0,为解决这和问题,Kaiming 方法修改了生成随机数时的标准差。
ReLU 过滤掉了一半数据,因此分子乘 2,分母是上一层的输出元素个数,即本层的输入元素个数。推导过程请见论文:《Delving Deep into Rectifiers: Surpassing Human-Level Performance on ImageNet Classification》的 Section 2.2。
尽管 Kaiming 初始化一开始主要针对 ReLU 激活函数优化,但是目前主流库中的 Kaiming 函数已经支持 sigmoid 等多种激活函数,可放心调用。
归一化层
Kaiming 的目标也是保证各个层输入和输出数据的方差不变。由于后来归一化层被广泛使用,有效地缓解了均值和方差稳定的问题。因此,在使用归一化层的情况下,使用随机数始初化参数即可。
另外,在有些情况下无法使用归一化层,比如最常用的 BN(Batch Normalization)在 Batch 中数据较少时效果不好,这种情况下就需要选用参数初始化。
在预训练/调优的场景中,一般使用预训练的参数作为模型的初值。
Python 参数初始化
在使用 Pytorch 构建网络时,torch.nn 中提供了各种常用初始化方法,直接调用即可。下面列出用于初始化网络的某一层或某几层的常用代码。
1 | def init_network_params(model, method='xavier', keywords, seed=123, debug=False): |
Pytorch 中如果不额外设置,线性层的初值设为:(详见 torch.nn.modules.linear):
1 | kaiming_uniform_(self.weight, a=math.sqrt(5)) |