写在前面:
神经网络为什么需要激活函数:
如果不使用激活函数的话,网络整体(conv、pool、fc)是线性函数,线性函数无论叠加多少层,都是线性的,只是斜率和截距不同,叠加网络对解决实际问题没有多大帮助;而神经网络解决的问题大部分是非线性的,引入激活函数,是在神经网络中引入非线性,强化网络的学习能力。所以激活函数的最大特点就是非线性。
梯度消失、爆炸
问题
这个特性会导致后面网络层的输入也不是零中心的,进而影响梯度下降的运作。
因为如果输入都是正数的话(如 f = w T x + b f=w^{T}x+b f=wTx+b 中每个元素都 x > 0 x>0 x>0 ,那么关于 w w w 的梯度在反向传播过程中,要么全是正数,要么全是负数(具体依据整个表达式 f f f 而定 损失函数),这将会导致梯度下降权重更新时出现 z 字型的下降。
问题
分段非线性函数,对ReLU求导,在输入值分别为正和为负的情况下,导数是不同的,即ReLU的导数不是常数,所以ReLU是非线性的(只是不同于Sigmoid和tanh,relu的非线性不是光滑的)。
特点
优点
pytorch 默认初始化 reset_parameters(), 例如:
nn.Linear
和nn.Conv2D
,都是在[-limit, limit]
之间的均匀分布
,其中 limit 是1. / sqrt(fan_in)
,fan_in
是指参数张量的输入单元的数量
我们构建好网络,开始训练前,不能默认的将所有权重系数都初始化为零,因为所有卷积核的系数都相等时,提取特征就会一样,反向传播时的梯度也会存在对称性,网络会退化会线性模型。另外网络层数较深时,初始化权重过大,会出现梯度爆炸,而过小又会出现梯度消失。一般权重初始化时需要考虑两个问题:
标准均匀初始化
stand_uniform
W i , j ∼ g a i n ∗ ( − 1 f a n i n , 1 f a n i n ) W_{i,j} \sim gain\ * (-\sqrt{\frac{1}{fan_in}},\sqrt{\frac{1}{fan_in}}) Wi,j∼gain ∗(−fanin1,fanin1)
标准正态初始化
stand_normal
W i , j ∼ g a i n ∗ N ( 0 , 1 f a n i n ) W_{i,j} \sim gain\ * N(0,\sqrt{\frac{1}{fan_in}}) Wi,j∼gain ∗N(0,fanin1)
均匀初始化
xavier_uniform
W i , j ∼ g a i n ∗ ( − 6 f a n i n + f a n o u t , 6 f a n i n + f a n o u t ) W_{i,j} \sim gain\ * (-\sqrt{\frac{6}{fan_in+fan_out}},\sqrt{\frac{6}{fan_in+fan_out}}) Wi,j∼gain ∗(−fanin+fanout6,fanin+fanout6)
正态初始化
xavier_normal
W i , j ∼ g a i n ∗ N ( 0 , 2 f a n i n + f a n o u t ) W_{i,j} \sim gain\ * N(0,\sqrt{\frac{2}{fan_in+fan_out}}) Wi,j∼gain ∗N(0,fanin+fanout2)
gain
增益 理解为缩放系数
fan_in
为 in_channel*k_w*k_h
fan_out
为 out_channel*k_w*k_h
w = torch.empty(3, 3, 5, 5) #fan_in=75, fan_out=75
nn.init.xavier_uniform_(w, gain=1) #初始化为(-sqrt(6/150), sqrt(6/150))范围内的均匀分布
nn.init.xavier_normal_(w, gain=1) #初始化为mean=0, std=sqrt(2/150)的正态分布
计算fan_in fan_out 的代码
def _calculate_fan_in_and_fan_out(tensor):
dimensions = tensor.dim()
if dimensions < 2:
raise ValueError("Fan in and fan out can not be computed for tensor with fewer than 2 dimensions")
if dimensions == 2: # Linear
fan_in = tensor.size(1)
fan_out = tensor.size(0)
else:
num_input_fmaps = tensor.size(1)
num_output_fmaps = tensor.size(0)
receptive_field_size = 1
if tensor.dim() > 2:
receptive_field_size = tensor[0][0].numel()
fan_in = num_input_fmaps * receptive_field_size
fan_out = num_output_fmaps * receptive_field_size
return fan_in, fan_out
均匀初始化
xavier_uniform
W i , j ∼ g a i n ∗ ( − 6 f a n i n , 6 f a n i n ) W_{i,j} \sim gain\ * (-\sqrt{\frac{6}{fan_in}},\sqrt{\frac{6}{fan_in}}) Wi,j∼gain ∗(−fanin6,fanin6)
正态初始化
xavier_normal
W i , j ∼ g a i n ∗ N ( 0 , 2 f a n i n ) W_{i,j} \sim gain\ * N(0,\sqrt{\frac{2}{fan_in}}) Wi,j∼gain ∗N(0,fanin2)
参数:
tensor – n 维 torch.Tensor
a – 该层后面一层的整流函数中负的斜率 (默认为 0,此时为 Relu)
mode – ‘fan_in’ (default) 或者 ‘fan_out’。使用fan_in保持weights的方差在前向传播中不变;使用fan_out保持weights的方差在反向传播中不变。
nonlinearity – 非线性函数 (nn.functional 中的名字),推荐只使用 ‘relu’ 或 ‘leaky_relu’ (default)。
w = torch.empty(3, 3, 5, 5) #fan_in=75, fan_out=75
nn.init.kaiming_uniform_(w, mode='fan_in', nonlinearity='relu') #初始化为(-sqrt(6/75), sqrt(6/75))范围内的均匀分布
nn.init.kaiming_normal_(w, mode='fan_out', nonlinearity='relu') #初始化为mean=0, std=sqrt(2/75)的正态分布