zoukankan      html  css  js  c++  java
  • 模型参数初始化

    参考:

    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)
    
  • 相关阅读:
    可爱的中国电信 请问我们的电脑还属于我们自己吗?
    了解客户的需求,写出的代码或许才是最优秀的............
    DELPHI DATASNAP 入门操作(3)简单的主从表的简单更新【含简单事务处理】
    用数组公式获取字符在字符串中最后出现的位置
    在ehlib的DBGridEh控件中使用过滤功能(可以不用 MemTableEh 控件 适用ehlib 5.2 ehlib 5.3)
    格式化json返回的时间
    ExtJs中使用Ajax赋值给全局变量异常解决方案
    java compiler level does not match the version of the installed java project facet (转)
    收集的资料(六)ASP.NET编程中的十大技巧
    收集的资料共享出来(五)Asp.Net 权限解决办法
  • 原文地址:https://www.cnblogs.com/wioponsen/p/13723696.html
Copyright © 2011-2022 走看看