『参数初始化』开局一个神经网络,初始值全靠随机

本文介绍了神经网络参数初始化的重要性,包括对称性打破、初始值大小的影响,并详细讲解了Xavier和Kaiming初始化方法,以及它们在PyTorch中的实现。Xavier初始化旨在保持输入输出方差一致,而Kaiming初始化针对ReLU激活函数进行了优化。在PyTorch中,默认使用Kaiming初始化。此外,文章还展示了如何自定义参数初始化方式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


标题开了个玩笑哈哈哈,本文对神经网络中的参数初始化相关内容进行了总结和归纳,涵盖常用初始化方法以及在PyTorch中的使用方法,以作备忘。

因个人水平有限,如有问题,欢迎大家提出指正!


参数初始化的对称性

以全连接网络为例,同一层中的任意神经元都是同构的,拥有相同的输入和输出,若是将参数初始化为同样的值,那么无论前向传播还是反向传播的值也是完全相同的。

学习过程永远无法打破这种对称性,最终同一网络层中各个参数仍然是相同的。

因此,我们需要随机初始化神经网络的参数值,以打破这种对称性。


参数初始值过大/过小

初始值过大时,对于Sigmoid、Tanh激活函数来说,会使得输入激活函数的值过大,造成饱和现象;反向传播时,会使得梯度极小,导致梯度消失现象。
此种情况,ReLU函数不受此影响。

初始值过小时,经过多层网络,输出值变得极小;反向传播时,同样会使得梯度极小,导致梯度消失现象。
此种情况,ReLU函数也受此影响。

由上可知,参数初始化方式的选择通常与激活函数有关,暂无研究发现网络结构与初始化方式之间存在关联,所以无需从网络结构的角度考虑选择何种初始化方式。


常见的参数初始化方式

除了普通的正态分布初始化(Normal)、均匀分布初始化(Uniform)以外,还有2010年提出的Xavier初始化、2015年提出的Kaiming初始化方法,这两种方法都有正态化(Normal)和均匀化(Uniform)两种方式。

以下公式中的 f a n _ i n fan\_in fan_in是权值张量中输入神经元的个数, f a n _ o u t fan\_out fan_out是权值张量中输出神经元的个数。


Xavier初始化

也称为Glorot初始化。

方差一致性

Xavier的核心思想是,对于每层网络来说保持输入和输出的方差一致,以避免所有输出值趋向于0,从而避免梯度消失现象。

因此,Xavier的特点是:正向传播时,激活值的方差保持不变;反向传播时,关于状态值的梯度的方差保持不变。

正态化的Xavier初始化 Xavier_normal:

从以0为中心,标准差为 s t d d e v = 2 / ( f a n _ i n + f a n _ o u t ) stddev=\sqrt{2/(fan\_in+fan\_out)} stddev=2/(fan_in+fan_out) 的截断正态分布中抽取样本。

均匀化的Xavier初始化 Xavier_uniform:

[ − l i m i t , l i m i t ] [-limit, limit] [limit,limit]中的均匀分布中抽取样本,其中 l i m i t limit limit 6 / ( f a n _ i n + f a n _ o u t ) \sqrt{6/(fan\_in + fan\_out)} 6/(fan_in+fan_out)

缺点:

Xavier的推导过程基于以下几个假设

  • 激活函数是线性的;
    不适用于ReLU,Sigmoid等非线性激活函数。

  • 激活值关于0对称;
    同样不适用于ReLU,Sigmoid等激活函数。


Kaiming初始化

也称He初始化,MSRA初始化,由何凯明大神提出。

前面提到Xavier初始化不适用于ReLU函数,更核心的原因在于当ReLU的输出小于0时,输出为0,相当于此神经元关闭,影响了输出的分布模式

因此何凯明提出了适用于ReLU网络的Kaiming初始化方法,在Xavier的基础上,假设每层网络有一半的神经元关闭,于是其分布的方差也会变小。

因此,Kaiming的特点是:正向传播时,状态值的方差保持不变;反向传播时,关于激活值的梯度的方差保持不变。

正态化的Kaiming初始化 He_normal:

从以0为中心,标准差为 s t d d e v = 2 / f a n _ i n stddev=\sqrt{2/fan\_in} stddev=2/fan_in 的截断正态分布中抽取样本。

均匀化的Kaiming初始化 He_uniform:

[ − l i m i t , l i m i t ] [-limit, limit] [limit,limit]中的均匀分布中抽取样本,其中 l i m i t limit limit 6 / f a n _ i n \sqrt{6/fan\_in} 6/fan_in


PyTorch中的参数初始化

PyTorch在torch.nn.init中提供了常用的初始化方法函数:

# Xavier方法
# gain根据激活函数的类型设定

# 服从均匀分布U(−a,a), 分布的参数a = gain * sqrt(6/fan_in+fan_out)
torch.nn.init.xavier_uniform_(w, gain=nn.init.calculate_gain('relu'))
# 服从正态分布N(0,std), 分布的标准差std = gain * sqrt(2/fan_in + fan_out)
torch.nn.init.xavier_normal_(w, gain=nn.init.calculate_gain('relu'))

# Kaiming方法
# a为激活函数负半轴的斜率, relu是0
# mode可选为fan_in或fan_out, fan_in使正向传播时,方差一致; fan_out使反向传播时,方差一致
# nonlinearity可选 relu 和 leaky_relu

# 服从均匀分布U(−bound,bound), 分布的参数bound = sqrt(6/(1+a^2)*fan_in)
torch.nn.init.kaiming_uniform_(w, a=0, mode='fan_in', nonlinearity='relu')
# 服从正态分布N(0,std), 分布的标准差std = sqrt(2/(1+a^2)*fan_in)
torch.nn.init.kaiming_normal_(w, a=0, mode='fan_in', nonlinearity='relu')

# 服从均匀分布U(a,b)
torch.nn.init.uniform_(w, a=0, b=1)
# 服从正态分布N(mean, std)
torch.nn.init.normal_(tensor, mean=0, std=1)
# 服从常数分布
torch.nn.init.constant_(w, val=0.3)

PyTorch中的默认初始化方式

在PyTorch中,参数的默认初始化方法定义在各个层的reset_parameters()方法中。

以torch1.9.1为例:
nn.Linear源码中的参数初始化方式

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) if fan_in > 0 else 0
            init.uniform_(self.bias, -bound, bound)

由上可知,weight使用kaiming均匀分布初始化,bias采用[-bound, bound]均匀分布,其中bound为1/sqrt(fan_in)fan_in为输入神经元个数。

nn.ConvNd源码中的参数初始化方式:

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)

nn.Linear的初始化方式相同。


如何在PyTorch中自定义参数初始化方式?

可以自行编写weight_init(m)函数,参数m代表module,即网络中的一层。通过isinstance(m, nn.Conv2d)判断此网络层的类别,并据此选择合适的初始化方法。

from torch.nn import init
# Define the initial function to init the layer's parameters for the network
def weight_init(m):
    if isinstance(m, nn.Conv2d):
        init.xavier_uniform_(m.weight)
		if m.bias:
        	init.constant(m.bias, 0)
    elif isinstance(m, nn.BatchNorm2d):
        init.constant(m.weight, 1)
        init.constant(m.bias, 0)
    elif isinstance(m, nn.Linear):
        init.normal(m.weight, std=1e-3)
        if m.bias:
            init.constant(m.bias, 0)

先将网络模型实例化,再通过model.apply(weight_init)让模型调用这种参数初始化方式,这样便完成了对模型中训练参数的初始化。

apply函数会递归地搜索网络内的所有module并把参数表示的函数应用到所有的module上。

# Define Network
model = Net()
model.apply(weigth_init)

可以将这一过程总结为:

  1. 定义weight_init函数,并在weight_init中通过判断module的类型来进行不同的参数初始化方式;
  2. model=Net(…), 创建网络结构;
  3. model.apply(weight_init), 将weight_init初始化方式应用到每一个module上;

前面也有提到,初始化方式一般是根据激活函数来选择的,因此这里应该是根据每种module的激活函数类型选择初始化方法。
这一点纯属笔者的个人猜测,未经实践或经验的验证,若有不同看法,欢迎提出指正。


参考

初始化方法

关于参数初始化的若干问题以及Xavier、He初始化推导

深度学习参数初始化(weights initializer)策略大全_LoveMIss-Y的博客-优快云博客_glorot_uniform

PyTorch的初始化函数

PyTorch 学习笔记(四):权值初始化的十种方法

pytorch系列 – 9 pytorch nn.init 中实现的初始化函数 uniform, normal, const, Xavier, He initialization_墨氲的博客-优快云博客_torch.nn.init.xavier_uniform_

PyTorch参数初始化的位置以及如何自定义初始化函数

【pytorch参数初始化】 pytorch默认参数初始化以及自定义参数初始化_华仔168168的博客-优快云博客_pytorch 参数初始化

pytorch中的参数初始化方法总结_ys1305的博客-优快云博客_reset_parameters

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值