• 模型参数初始化


    参考:

    https://cloud.tencent.com/developer/article/1437995
    https://www.cnblogs.com/wanghui-garcia/p/11385160.html

    1.4、参数初始化的几点要求

    (1)参数不能全部初始化为0,也不能全部初始化同一个值,为什么,请参见“对称失效”;

    (2)最好保证参数初始化的均值为0,正负交错,正负参数大致上数量相等;

    (3)初始化参数不能太大或者是太小,参数太小会导致特征在每层间逐渐缩小而难以产生作用,参数太大会导致数据在逐层间传递时逐渐放大而导致梯度消失发散,不能训练

    (4)如果有可能满足Glorot条件也是不错的

    上面的几点要求中,(1)(2)(3)基本上是硬性要求,这也就衍生出了一系列的参数初始化方法,什么正态标准化等诸如此类的标准化方法,关于各种参数初始化方法,会在后面继续说明。

    二、常见的参数初始化方法

    我们常见的几种初始化方法是按照“正态分布随机初始化——对应为normal”和按照“均匀分布随机初始化——对应为uniform”,这里就不再多说了,这里介绍几种遇见较少的初始化方法。

    2.1、Glorot初始化方法

    (1)正态化的Glorot初始化——glorot_normal

    Glorot 正态分布初始化器,也称为 Xavier 正态分布初始化器。它从以 0 为中心,标准差为 stddev = sqrt(2 / (fan_in + fan_out)) 的截断正态分布中抽取样本, 其中 fan_in 是权值张量中的输入单位的数量, fan_out 是权值张量中的输出单位的数量。

    在keras和tensorflow均有实现,以keras为例:

      keras.initializers.glorot_normal(seed=None)
    

    (2)标准化的Glorot初始化——glorot_uniform

    Glorot 均匀分布初始化器,也称为 Xavier 均匀分布初始化器。

    它从 [-limit,limit] 中的均匀分布中抽取样本, 其中 limit 是 sqrt(6 / (fan_in + fan_out)), fan_in 是权值张量中的输入单位的数量, fan_out 是权值张量中的输出单位的数量。

    以keras为例:

      keras.initializers.glorot_uniform(seed=None)
    

    (3)Glorot初始化器的缺点

    因为Xavier的推导过程是基于几个假设的,

    其中一个是激活函数是线性的,这并不适用于ReLU,sigmoid等非线性激活函数;

    另一个是激活值关于0对称,这个不适用于sigmoid函数和ReLU函数它们不是关于0对称的。

    2.2、Kaiming初始化

    Kaiming初始化,也称之为he初始化,也称之为msra初始化,出自大神 何凯明之手。即

      Kaiming initializer=he initializer=msra initializer
    

    因为前面讲了Glorot初始化不适合relu激活函数,所以残差网络的作者何凯明在这篇论文中提出了ReLU网络的初始化方法:Kaming初始化。

    作者的推导过程针对的其实是卷积网络的前向和反向过程。而为了和Xavier初始化方法保持一致,这里我们还是讨论全连接网络结构。

    关于期望、方差的性质,我们已经在Xavier初始化一节介绍过了,这里不再重复。

    在Xavier论文中,作者给出的Glorot条件是:正向传播时,激活值的方差保持不变;反向传播时,关于状态值的梯度的方差保持不变。

    这在本文中稍作变换:正向传播时,状态值的方差保持不变;反向传播时,关于激活值的梯度的方差保持不变。

    (1)正态化的kaiming初始化——he_normal

    He 正态分布初始化器。

    它从以 0 为中心,标准差为 stddev = sqrt(2 / fan_in) 的截断正态分布中抽取样本, 其中 fan_in是权值张量中的输入单位的数量,在keras中的实现为

      keras.initializers.he_normal(seed=None)
    

    (2)标准化化的kaiming初始化——he_uniform

    He 均匀方差缩放初始化器。

    它从 [-limit,limit] 中的均匀分布中抽取样本, 其中 limit 是 sqrt(6 / fan_in), 其中 fan_in 是权值张量中的输入单位的数量。

      keras.initializers.he_uniform(seed=None)
    

    2.3、lecun初始化

    出自大神Lecun之手。

    (1)标准化化的kaiming初始化——lecun_uniform

    LeCun 均匀初始化器。

    它从 [-limit,limit] 中的均匀分布中抽取样本, 其中 limit 是 sqrt(3 / fan_in), fan_in 是权值张量中的输入单位的数量。

      keras.initializers.lecun_uniform(seed=None)
    

    (2)正态化的kaiming初始化——lecun_normal

    LeCun 正态分布初始化器。

    它从以 0 为中心,标准差为 stddev = sqrt(1 / fan_in) 的截断正态分布中抽取样本, 其中 fan_in是权值张量中的输入单位的数量。

      keras.initializers.lecun_normal(seed=None)
    

    2.4、Batch Normalization

    BN是将输入的数据分布变成高斯分布,这样可以保证每一层神经网络的输入保持相同分布。

    优点

    随着网络层数的增加,分布逐渐发生偏移,之所以收敛慢,是因为整体分布往非线性函数取值区间的上下限靠近。这会导致反向传播时梯度消失。BN就是通过规范化的手段,把每层神经网络任意神经元这个输入值的分布强行拉回到均值0方差1的标准正态分布,使得激活输入值落入非线性函数中比较敏感的区域。可以让梯度变大,学习收敛速度快,能大大加快收敛速度。

    Scale and Shift作用

    γ和β。γ和β是学习到的参数,他们可以让标准正态分布变得更高/更胖和向左右偏移。

    三、参数初始化方法的总结
    img

    四、pytorch中代码实现
    1.使用apply()

    举例说明:

    Encoder :设计的编码其模型

    weights_init(): 用来初始化模型

    model.apply():实现初始化

    # coding:utf-8
    from torch import nn
    
    def weights_init(mod):
        """设计初始化函数"""
        classname=mod.__class__.__name__
        # 返回传入的module类型
        print(classname)
        if classname.find('Conv')!= -1:    #这里的Conv和BatchNnorm是torc.nn里的形式
            mod.weight.data.normal_(0.0,0.02)
        elif classname.find('BatchNorm')!= -1:
            mod.weight.data.normal_(1.0,0.02) #bn层里初始化γ,服从(1,0.02)的正态分布
            mod.bias.data.fill_(0)  #bn层里初始化β,默认为0
    
    class Encoder(nn.Module):
        def __init__(self, input_size, input_channels, base_channnes, z_channels):
    
            super(Encoder, self).__init__()
            # input_size必须为16的倍数
            assert input_size % 16 == 0, "input_size has to be a multiple of 16"
    
            models = nn.Sequential()
            models.add_module('Conv2_{0}_{1}'.format(input_channels, base_channnes), nn.Conv2d(input_channels, base_channnes, 4, 2, 1, bias=False))
            models.add_module('LeakyReLU_{0}'.format(base_channnes), nn.LeakyReLU(0.2, inplace=True))
            # 此时图片大小已经下降一倍
            temp_size = input_size/2
    
            # 直到特征图高宽为4
            # 目的是保证无论输入什么大小的图片,经过这几层后特征图大小为4*4
            while temp_size > 4 :
                models.add_module('Conv2_{0}_{1}'.format(base_channnes, base_channnes*2), nn.Conv2d(base_channnes, base_channnes*2, 4, 2, 1, bias=False))
                models.add_module('BatchNorm2d_{0}'.format(base_channnes*2), nn.BatchNorm2d(base_channnes*2))
                models.add_module('LeakyReLU_{0}'.format(base_channnes*2), nn.LeakyReLU(0.2, inplace=True))
                base_channnes *= 2
                temp_size /= 2
    
            # 特征图高宽为4后面则添加上最后一层
            # 让输出为1*1
            models.add_module('Conv2_{0}_{1}'.format(base_channnes, z_channels), nn.Conv2d(base_channnes, z_channels, 4, 1, 0, bias=False))
            self.models = models
    
        def forward(self, x):
            x = self.models(x)
            return x
    
    if __name__ == '__main__':
        e = Encoder(256, 3, 64, 100)
        # 对e模型中的每个module和其本身都会调用一次weights_init函数,mod参数的值即这些module
        e.apply(weights_init)
        # 根据名字来查看参数
        for name, param in e.named_parameters():
            print(name)
            # 举个例子看看是否按照设计进行初始化
            # 可见BatchNorm2d的weight是正态分布形的参数,bias参数都是0
            if name == 'models.BatchNorm2d_128.weight' or name == 'models.BatchNorm2d_128.bias':
                print(param)
    

    2.直接在定义网络时定义

    import torch.nn as nn
    import torch.nn.init as init
    import torch.nn.functional as F
    
    class Discriminator(nn.Module):
        """ 
            6层全连接层
        """
        def __init__(self, z_dim):
            super(Discriminator, self).__init__()
            self.z_dim = z_dim
            self.net = nn.Sequential(
                nn.Linear(z_dim, 1000),
                nn.LeakyReLU(0.2, True),
                nn.Linear(1000, 1000),
                nn.LeakyReLU(0.2, True),
                nn.Linear(1000, 1000),
                nn.LeakyReLU(0.2, True),
                nn.Linear(1000, 1000),
                nn.LeakyReLU(0.2, True),
                nn.Linear(1000, 1000),
                nn.LeakyReLU(0.2, True),
                nn.Linear(1000, 2),
            )
            self.weight_init()
    
        # 参数初始化
        def weight_init(self, mode='normal'):
            if mode == 'kaiming':
                initializer = kaiming_init
            elif mode == 'normal':
                initializer = normal_init
    
            for block in self._modules:
                for m in self._modules[block]:
                    initializer(m)
    
        def forward(self, z):
            return self.net(z).squeeze()
    
    def kaiming_init(m):
        if isinstance(m, (nn.Linear, nn.Conv2d)):
            init.kaiming_normal_(m.weight)
            if m.bias is not None:
                m.bias.data.fill_(0)
        elif isinstance(m, (nn.BatchNorm1d, nn.BatchNorm2d)):
            m.weight.data.fill_(1)
            if m.bias is not None:
                m.bias.data.fill_(0)
    
    def normal_init(m):
        if isinstance(m, (nn.Linear, nn.Conv2d)):
            init.normal_(m.weight, 0, 0.02)
            if m.bias is not None:
                m.bias.data.fill_(0)
        elif isinstance(m, (nn.BatchNorm1d, nn.BatchNorm2d)):
            m.weight.data.fill_(1)
            if m.bias is not None:
                m.bias.data.fill_(0)
    
  • 相关阅读:
    skimage的安装,scikit-image
    今天开始看看brpc-baidurpc
    Silk Mobile – 缩短移动应用的测试周期
    将Controller中的数据传递到View中显示
    常用排序算法总结(二) ---- 插入排序,快速排序
    关于二叉搜索树及三种树遍历的特点
    poj
    元数据(meta-data)
    《UNIX环境高级编程》笔记--文件访问权限和新文件、目录所有权
    UVA 674 Coin Change 换硬币 经典dp入门题
  • 原文地址:https://www.cnblogs.com/wioponsen/p/13723696.html
Copyright © 2020-2023  润新知