前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >干货|权重初始化如何推导?

干货|权重初始化如何推导?

作者头像
灿视学长
发布2021-07-07 10:32:04
9310
发布2021-07-07 10:32:04
举报
文章被收录于专栏:灿视学长

大家好,我是灿视。

今天我们带来的深度学习权重初始化相关的内容,其初始化的原理是?背后的数学原理是啥?可以达到什么效果呢?我们一一来看吧~

看文章之前,关注下咱哈!咱分享最优质的计算机视觉面经,持续关注AI在互联网与银行等单位中的工作机会。

点下方名片,翻看之前的干货文章!你必有收获!

深度学习之权重初始化

在深度学习中,神经网络的权重初始化方法(

weight
initialization

)对模型的收敛速度和性能有着至关重要的影响。说白了,神经网络其实就是对权重参数

w

的不停迭代更新,以达到更好的性能。因此,对权重

w

的初始化则显得至关重要,一个好的权重初始化虽然不能完全解决梯度消失和梯度爆炸的问题,但是对于处理这两个问题是有很大的帮助的,并且十分有利于模型性能和收敛速度。

本文将介绍以下五种常见的权重初始化的方法:

  • 权重初始化为
0
  • 权重随机初始化
Xavier
initialization
He
initialization
  • 预训练权重

权重初始化为

0

如果将权重初始化全部为

0

的话,这样的操作等同于等价于一个线性模型,将所有权重设为

0

时,对于每一个

w

而言,损失函数的导数都是相同的,因此在随后的迭代过程中所有权重都具有相同的值,这会使得隐藏单元变得对称,并继续运行设置的

n

次的迭代,会导致网络中同一个神经元的不同权重都是一样的。下面代码为权重初始化为

0

的代码:

代码语言:javascript
复制
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

让我们来看看权重初始化为

0

之后其

cost
function

是如何变化的,从图中可以看出,当代价函数降到

0.64

(迭代

1000

次)后,梯度逐渐消失,再训练迭代已经不起什么作用了。

1

权重初始化为

0

cost
function

变化图

权重随机初始化

权重随机初始化是比较常见的做法,即

W

随机初始化。随机初始化的代码如下:

代码语言:javascript
复制
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

上述代码中权重乘

0.01

是因为要把

W

随机初始化到一个相对较小的值,因为如果

X

很大的话,

W

又相对较大,会导致

Z

非常大,这样如果激活函数是

sigmoid

,就会导致

sigmoid

的输出值

1

或者

0

,然后会导致一系列问题(比如

cost
function

计算的时候,

log

里是

0

,这样会有点麻烦)。随机初始化后,

cost
function

随着迭代次数的变化示意图如下图

2

所示为:

2

权重随机初始化

cost
function

变化图

能够看出,

cost
function

的变化是比较正常的。但是随机初始化也有缺点,

np.random.randn()

其实是一个均值为

0

,方差为

1

的高斯分布中采样。当神经网络的层数增多时,会发现越往后面的层的激活函数(使用

tanH

)的输出值几乎都接近于

0

,极易出现梯度消失。如下图

3

所示:

3

Xavier
initialization

在使用以上两种方法来初始化权重极易出现梯度消失的问题,而

Xavier
initialization

出现就解决了上面问题。其思想倒就是尽可能的让输入和输出服从相同的分布,这样就能够避免后面层的激活函数的输出值趋向于

0

。本文主要介绍

Pytorch

当中

Xavier

均匀分布和

Xavier

正态分布初始化这两种方式。

1、
Xavier

均匀分布初始化

Pytorch

Xavier

均匀分布初始化计算公式和代码如下,

a

代表的是均匀初始化的上下界绝对值,

gain

表示缩放因子,

fan_{in}

为输入个数,

fan_{out}

为输出个数,初始化中的值采样自

U

(-

a

,

a

)。

a = gain * sqrt( 2/(fan_{in} + fan_{out}))* sqrt(3)
代码语言:javascript
复制
# tensor表示要初始化的张量,gain表示缩放因子
torch.nn.init.xavier_uniform(tensor, gain=1)

# 举例说明:
w = torch.Tensor(3, 5)
nn.init.xavier_uniform(w, gain=math.sqrt(2))
2、
Xavier

正态分布初始化

Pytorch

Xavier

正态分布初始化计算公式和代码如下,

std

代表的是正态分布初始化的方差,

gain

表示缩放因子,

fan_{in}

为输入个数,

fan_{out}

为输出个数,初始化中的值采样自均值为

0

,标准差为

std

的正态分布。

std = gain * sqrt(2/(fan_{in} + fan_{out}))
代码语言:javascript
复制
# tensor表示要初始化的张量,gain表示缩放因子
torch.nn.init.xavier_normal(tensor, gain=1)

# 举例说明:
w = torch.Tensor(3, 5)
nn.init.xavier_normal(w)
3、
Xavier

权重初始化表现效果

如下图

4

所示为采用

Xavier
initialization

后每层的激活函数输出值的分布,从图中我们可以看出,深层的激活函数输出值还是非常服从标准高斯分布

4
Xavier

权重初始化后

tanH

激活层输出分布

虽然

Xavier
initialization

能够很好的适用于

tanH

激活函数,但对于目前神经网络中最常用的

ReLU

激活函数,还是无能能力,如下图

5

所示为采用

ReLU

激活函数后,

Xavier
initialization

初始化的每层激活函数输出值的分布,从图中可以看出当达到

5

6

层后几乎又开始趋向于

0

,更深层的话很明显又会趋向于

0

5
Xavier

权重初始化后

ReLU

激活层输出分布

由此可见,

Xavier

权重初始化方式比较适用于

tanH

Sigmoid

激活函数,而对于

ReLU

这种非对称性的激活函数还是容易出现梯度消失的现象。

He
initialization
He
initialization

是由何凯明大神提出的一种针对

ReLU

激活函数的初始化方法。

He
initialization

的思想是:和

Xavier

初始化方式一样,都希望初始化使得正向传播时,状态值的方差保持不变,反向传播时,关于激活值的梯度的方差保持不变。由于小于

0

的值经过

ReLU

激活函数都会变成

0

,而大于

0

的值则保持原值。因此在

ReLU

网络中,假定每一层有一半的神经元被激活,另一半为

0

,所以,要保持

variance

不变,只需要在

Xavier

的基础上再除以2即可。本文主要介绍

Pytorch

当中

He
initialization

均匀分布和

He
initialization

正态分布初始化这两种方式。

1、
He
initialization

均匀分布初始化

Pytorch

He
initialization

也叫做

kaiming

,其计算公式和代码如下,

bound

代表的是均匀初始化的上下界绝对值,

fan_{in}

为输入个数,初始化中的值采样自

U

(-

bound

,

bound

)。

bound = sqrt(2/((1 + a^2) * fan_{in})) * sqrt(3)
代码语言:javascript
复制
# 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')
2、
He
initialization

正态分布初始化

Pytorch

Xavier

正态分布初始化计算公式和代码如下,

std

代表的是正态分布初始化的方差,

fan_{in}

为输入个数,初始化中的值采样自均值为

0

,标准差为

std

的正态分布。

std = sqrt(2/((1 + a^2) * fan_{in}))
代码语言:javascript
复制
# 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')
3、
He
initialization

权重初始化表现效果

如下图

6

所示为采用

He
initialization

方式初始化权重后,隐藏层使用ReLU时,激活函数的输出值的分布情况,从图中可知,针对

ReLU

激活函数,

He
initialization

效果是比

Xavier
initialization

好很多。

6
He
initialization

权重初始化后

ReLU

激活层输出分布

由此可见,

He
initialization

权重初始化方式是非常适用于

ReLU

激活函数。

预训练模型

目前更多的使用已经针对相似任务已经训练好的模型,称之为预训练模型。在训练开始时就已经有了非常好的初始化参数,只需要将最后的全连接层进行冻结,训练其他部分即可。

代码如下:

代码语言:javascript
复制
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、权重采用初始化为

0

和随机初始化都比较容易出现梯度消失的问题,因此不常用。

2、

Xavier

权重初始化方式主要针对于

tanH

sigmoid

激活函数。

3、

He
initialization

权重初始化方式主要针对于

ReLU

激活函数。

4、如果有相似任务已经训练好的模型,也可以考虑采用预训练模型来作权重初始化。

引用

  • https://zhuanlan.zhihu.com/p/25110150
  • https://www.jianshu.com/p/cf2dcc624f47
  • https://pytorch.org/docs/stable/nn.html#torch-nn-init
  • https://pytorch-cn.readthedocs.io/zh/latest/package_references/nn_init/
  • https://blog.csdn.net/dss_dssssd/article/details/83959474?locationNum=16&fps=1
  • http://machinelearning.wustl.edu/mlpapers/paper_files/AISTATS2010_GlorotB10.pdf
  • https://blog.csdn.net/u011534057/article/details/53930199?utm_medium=distribute.pc_relevant.none-task-blog-baidujs_baidulandingword-0&spm=1001.2101.3001.4242
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-06-07,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 灿视学长 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 深度学习之权重初始化
    • 权重初始化为
      • 权重随机初始化
        • 1、
        • 2、
        • 3、
        • 1、
        • 2、
        • 3、
      • 预训练模型
        • 总结
          • 引用
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档