模型參數初始化


參考:

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)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM