在神经网络训练时,还涉及到一些tricks,如网络权重的初始化方法,优化器种类(权重更新),图片预处理等,继续填坑。
1. 神经网络初始化(Network Initialization )
1.1 初始化原因
我们构建好网络,开始训练前,不能默认的将所有权重系数都初始化为零,因为所有卷积核的系数都相等时,提取特征就会一样,反向传播时的梯度也会存在对称性,网络会退化会线性模型。另外网络层数较深时,初始化权重过大,会出现梯度爆炸,而过小又会出现梯度消失。一般权重初始化时需要考虑两个问题:
(1)权重参数全部相同时,会有梯度更新对称性问题(详细见https://www.zhihu.com/question/36068411?sort=created)
如下图中w,b系数都相同时,a1,a2,a3的值也会相同,反过来进行梯度更新时,w,b也是按相同的速率在更新。(不管是哪个神经元,它的前向传播和反向传播的算法都是一样的,如果初始值也一样的话,不管训练多久,它们最终都一样,都无法打破对称(fail to break the symmetry),那每一层就相当于只有一个神经元,最终L层神经网络就相当于一个线性的网络,如Logistic regression)
(2)采用饱和激活函数时,进行随机初始化时,权重分布会使神经元输出处于激活函数的梯度饱和区域。
(详细见:https://www.jianshu.com/p/03009cfdf733)
1.2 初始化方法
常用初始化方法有Gaussain initialization, Xavier initialization, Kaiming(MSRA) initialization 。pytorch的torch.nn.init模块中包含了常用的初始化函数。
Gaussian initialization: 采用高斯分布初始化权重参数
nn.init.normal_(tensor, mean=0, std=1) 能实现不同均值和标准差的高斯分布
nn.init.unoform_(tensor, a=0, b=1) 能实现(a, b)范围内的均匀分布
import torch import torch.nn as nn w = torch.empty(3, 5) nn.init.uniform_(w, a=0, b=1) #初始化为(0, 1)范围内的均匀分布 nn.init.normal_(w, mean=0, std=1) #初始化为均值为0, 标准差为1的正态分布 nn.init.constant_(w, 0.3) # 全部初始化为常量值0.3 nn.init.eye_(w) # 初始化为单位矩阵(对角线为1) print(w)
Xavier Initialization: 均值为0, 标准差根据输入神经元和输出神经元的参数个数决定,适合采用tanh和sigmoid等激活函数的模型,详细见下面论文
论文:Understanding the difficulty of training deep feedforward neural networks
nn.init.xavier_uniform_(tensor, gain=1): 根据xavier,实现了(-a, a)范围内的均匀分布,其中a的计算公式如下:
gain:增益,可以理解为缩放倍数,
fan_in: in_channel*Kw*Kh (输入channel个数, kernel的宽和高)
fan_out: out_channel*Kw*Kh (输出channel个数, kernel的宽和高)
nn.init.xavier_normal_(tensor, gain=1): 根据xavier,实现了mean=0, std=std 的高斯分布,其中std的计算公式如下:
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的计算方式,pytorch的实现代码如下:
def _calculate_fan_in_and_fan_out(tensor): dimensions = tensor.ndimension() 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