大家好,我是灿视。
今天我们带来的深度学习权重初始化相关的内容,其初始化的原理是?背后的数学原理是啥?可以达到什么效果呢?我们一一来看吧~
看文章之前,关注下咱哈!咱分享最优质的计算机视觉面经,持续关注AI在互联网与银行等单位中的工作机会。
点下方名片,翻看之前的干货文章!你必有收获!
在深度学习中,神经网络的权重初始化方法(
)对模型的收敛速度和性能有着至关重要的影响。说白了,神经网络其实就是对权重参数
的不停迭代更新,以达到更好的性能。因此,对权重
的初始化则显得至关重要,一个好的权重初始化虽然不能完全解决梯度消失和梯度爆炸的问题,但是对于处理这两个问题是有很大的帮助的,并且十分有利于模型性能和收敛速度。
本文将介绍以下五种常见的权重初始化的方法:
如果将权重初始化全部为
的话,这样的操作等同于等价于一个线性模型,将所有权重设为
时,对于每一个
而言,损失函数的导数都是相同的,因此在随后的迭代过程中所有权重都具有相同的值,这会使得隐藏单元变得对称,并继续运行设置的
次的迭代,会导致网络中同一个神经元的不同权重都是一样的。下面代码为权重初始化为
的代码:
def initialize_parameters_zeros(layers_dims):
"""
Arguments:
layer_dims -- python array (list) containing the size of each layer.
Returns:
parameters -- python dictionary containing your parameters "W1", "b1", ..., "WL", "bL":
W1 -- weight matrix of shape (layers_dims[1], layers_dims[0])
b1 -- bias vector of shape (layers_dims[1], 1)
...
WL -- weight matrix of shape (layers_dims[L], layers_dims[L-1])
bL -- bias vector of shape (layers_dims[L], 1)
"""
parameters = {}
np.random.seed(3)
L = len(layers_dims) # number of layers in the network
for l in range(1, L):
parameters['W' + str(l)] = np.zeros((layers_dims[l], layers_dims[l - 1]))
parameters['b' + str(l)] = np.zeros((layers_dims[l], 1))
return parameters
让我们来看看权重初始化为
之后其
是如何变化的,从图中可以看出,当代价函数降到
(迭代
次)后,梯度逐渐消失,再训练迭代已经不起什么作用了。
图
权重初始化为
的
变化图
权重随机初始化是比较常见的做法,即
随机初始化。随机初始化的代码如下:
def initialize_parameters_random(layers_dims):
"""
Arguments:
layer_dims -- python array (list) containing the size of each layer.
Returns:
parameters -- python dictionary containing your parameters "W1", "b1", ..., "WL", "bL":
W1 -- weight matrix of shape (layers_dims[1], layers_dims[0])
b1 -- bias vector of shape (layers_dims[1], 1)
...
WL -- weight matrix of shape (layers_dims[L], layers_dims[L-1])
bL -- bias vector of shape (layers_dims[L], 1)
"""
np.random.seed(3) # This seed makes sure your "random" numbers will be the as ours
parameters = {}
L = len(layers_dims) # integer representing the number of layers
for l in range(1, L):
parameters['W' + str(l)] = np.random.randn(layers_dims[l], layers_dims[l - 1])*0.01
parameters['b' + str(l)] = np.zeros((layers_dims[l], 1))
return parameters
上述代码中权重乘
是因为要把
随机初始化到一个相对较小的值,因为如果
很大的话,
又相对较大,会导致
非常大,这样如果激活函数是
,就会导致
的输出值
或者
,然后会导致一系列问题(比如
计算的时候,
里是
,这样会有点麻烦)。随机初始化后,
随着迭代次数的变化示意图如下图
所示为:
图
权重随机初始化
变化图
能够看出,
的变化是比较正常的。但是随机初始化也有缺点,
其实是一个均值为
,方差为
的高斯分布中采样。当神经网络的层数增多时,会发现越往后面的层的激活函数(使用
)的输出值几乎都接近于
,极易出现梯度消失。如下图
所示:
图
在使用以上两种方法来初始化权重极易出现梯度消失的问题,而
出现就解决了上面问题。其思想倒就是尽可能的让输入和输出服从相同的分布,这样就能够避免后面层的激活函数的输出值趋向于
。本文主要介绍
当中
均匀分布和
正态分布初始化这两种方式。
均匀分布初始化
在
中
均匀分布初始化计算公式和代码如下,
代表的是均匀初始化的上下界绝对值,
表示缩放因子,
为输入个数,
为输出个数,初始化中的值采样自
(-
,
)。
# tensor表示要初始化的张量,gain表示缩放因子
torch.nn.init.xavier_uniform(tensor, gain=1)
# 举例说明:
w = torch.Tensor(3, 5)
nn.init.xavier_uniform(w, gain=math.sqrt(2))
正态分布初始化
在
中
正态分布初始化计算公式和代码如下,
代表的是正态分布初始化的方差,
表示缩放因子,
为输入个数,
为输出个数,初始化中的值采样自均值为
,标准差为
的正态分布。
# tensor表示要初始化的张量,gain表示缩放因子
torch.nn.init.xavier_normal(tensor, gain=1)
# 举例说明:
w = torch.Tensor(3, 5)
nn.init.xavier_normal(w)
权重初始化表现效果
如下图
所示为采用
后每层的激活函数输出值的分布,从图中我们可以看出,深层的激活函数输出值还是非常服从标准高斯分布。
图
权重初始化后
激活层输出分布
虽然
能够很好的适用于
激活函数,但对于目前神经网络中最常用的
激活函数,还是无能能力,如下图
所示为采用
激活函数后,
初始化的每层激活函数输出值的分布,从图中可以看出当达到
、
层后几乎又开始趋向于
,更深层的话很明显又会趋向于
。
图
权重初始化后
激活层输出分布
由此可见,
权重初始化方式比较适用于
和
激活函数,而对于
这种非对称性的激活函数还是容易出现梯度消失的现象。
是由何凯明大神提出的一种针对
激活函数的初始化方法。
的思想是:和
初始化方式一样,都希望初始化使得正向传播时,状态值的方差保持不变,反向传播时,关于激活值的梯度的方差保持不变。由于小于
的值经过
激活函数都会变成
,而大于
的值则保持原值。因此在
网络中,假定每一层有一半的神经元被激活,另一半为
,所以,要保持
不变,只需要在
的基础上再除以2即可。本文主要介绍
当中
均匀分布和
正态分布初始化这两种方式。
均匀分布初始化
在
中
也叫做
,其计算公式和代码如下,
代表的是均匀初始化的上下界绝对值,
为输入个数,初始化中的值采样自
(-
,
)。
# tensor表示要初始化的张量
# a表示这层之后使用的rectifier的斜率系数(ReLU的默认值为0)
# mode可以为“fan_in”(默认)或“fan_out”。
# “fan_in”保留前向传播时权值方差的量级,“fan_out”保留反向传播时的量级。
torch.nn.init.kaiming_uniform(tensor, a=0, mode='fan_in')
# 举例说明:
w = torch.Tensor(3, 5)
nn.init.kaiming_uniform(w, mode='fan_in')
正态分布初始化
在
中
正态分布初始化计算公式和代码如下,
代表的是正态分布初始化的方差,
为输入个数,初始化中的值采样自均值为
,标准差为
的正态分布。
# tensor表示要初始化的张量
# a表示这层之后使用的rectifier的斜率系数(ReLU的默认值为0)
# mode可以为“fan_in”(默认)或“fan_out”。
# “fan_in”保留前向传播时权值方差的量级,“fan_out”保留反向传播时的量级。
torch.nn.init.kaiming_normal(tensor, a=0, mode='fan_in')
# 举例说明:
w = torch.Tensor(3, 5)
nn.init.kaiming_normal(w, mode='fan_out')
权重初始化表现效果
如下图
所示为采用
方式初始化权重后,隐藏层使用ReLU时,激活函数的输出值的分布情况,从图中可知,针对
激活函数,
效果是比
好很多。
图
权重初始化后
激活层输出分布
由此可见,
权重初始化方式是非常适用于
激活函数。
目前更多的使用已经针对相似任务已经训练好的模型,称之为预训练模型。在训练开始时就已经有了非常好的初始化参数,只需要将最后的全连接层进行冻结,训练其他部分即可。
代码如下:
import torch
import torch.nn as nn
import torchvision
from torchvision import models
from torchvision import transforms
net = models.resnet50(pretrained=True)
channel_in = net.fc.in_features
class_num = 2
net.fc = nn.Sequential(
nn.Linear(channel_in, 256),
nn.ReLU(),
nn.Dropout(0.4),
nn.Linear(256, class_num),
nn.LogSoftmax(dim=1)
)
for param in net.parameters():
param.requires_grad = False
for param in net.fc.parameters():
param.requires_grad = True
1、权重采用初始化为
和随机初始化都比较容易出现梯度消失的问题,因此不常用。
2、
权重初始化方式主要针对于
和
激活函数。
3、
权重初始化方式主要针对于
激活函数。
4、如果有相似任务已经训练好的模型,也可以考虑采用预训练模型来作权重初始化。