为什么要权值初始化?
权重初始化的目的是:防止在深度神经网络的正向(前向)传播过程中层激活函数的输出损失梯度爆炸或消失。如果发生任何一种情况,损失梯度太大或太小,就无法有效地反向传播,并且即便可以反向传播,网络也需要花更长时间来达到收敛。
网络初始化的一般做法:让输入值落入类似一个均值为0,标准差为1的正态分布中,确保被归一化,
即torch.randn
函数的做法
因为CNN的做法类似于累积矩阵乘法,所以正态分布的初始化不当会造成以下问题:
- 初始化太大会造成梯度爆炸
- 初始化太小会造成梯度消失
Xavier初始化
基于激活函数为基于给定点对称的,如
tanh
、softsign
这样的激活函数,提出了Xavier初始化
以往标准权重初始化是根据[-1,1]
中的均匀分布来初始化权重,然后按1 /√n
的比例缩放。但是这种方法实际上并不能很好地发挥作用,如下:
x = torch.randn(512)
for i in range(100):
a = torch.Tensor(512, 512).uniform_(-1, 1) * math.sqrt(1. / 512)
x = tanh(a @ x)
print(x.mean()) # tensor(3.7062e-26)
print(x.std()) # tensor(9.9671e-25)
使用该权重初始化方法运行100层tanh
网络[服从上述分布]会导致激活梯度变得无限小就像消失了一样。
所以提出了Xavier初始化:将每层权重设置为在有界的随机均匀分布中选择的值
其中
Xavier权重初始化
将保持激活函数和反向传播梯度的方差,一直向上或向下传播到神经网络的每一层
def xavier(m, h):
return torch.Tensor(m, h).uniform_(-1, 1) * math.sqrt(6. / (m + h))
Kaiming
初始化
基于非对称的激活函数,如
relu
等提出的Kaiming
初始化
-
使用适合给定图层的权重矩阵创建张量,并使用从标准正态分布中随机选择的数字填充它。
-
将每个随机选择的数字乘以
√2/√n
,其中n
是从前一层输出到指定层的连接数(也称为fan-in
) -
偏差张量初始化为零
def kaiming(m, h):
return torch.randn(m, h) * math.sqrt(2. / m)
在relu
为激活函数的网络中,如果使用Xavier
初始化会导致梯度消失,Kaiming初始化
应该是我们的首选权重初始化策略。
pytorch
中的初始化
pytorch
中已经集成了多种的初始化方式,上述的Xavier
和kaiming
初始化都可以直接调用
- 初始化为均匀分布
torch.nn.init.xavier_uniform(tensor, gain=1)
torch.nn.init.kaiming_uniform(tensor, a=0, mode='fan_in')
- 初始化为高斯分布(正态分布)
torch.nn.init.xavier_normal(tensor, gain=1)
torch.nn.init.kaiming_normal(tensor, a=0, mode='fan_in')
- 更多初始化方法👉官方文档
pytorch
搭建网络自动初始化
通过阅读一些代码我们会发现,有些网络只搭建了框架而没有进行上述的各种初始化,那网络的初始参数怎么样呢?
通过TORCH.NN.MODULES.CONV
文档可以看出,在pytorch
框架会自动使用kaiming
均匀分布初始化网络参数
def reset_parameters(self) -> None:
init.kaiming_uniform_(self.weight, a=math.sqrt(5))
if self.bias is not None:
fan_in, _ = init._calculate_fan_in_and_fan_out(self.weight)
bound = 1 / math.sqrt(fan_in)
init.uniform_(self.bias, -bound, bound)
Write by Gqq