簡體   English   中英

如何在 PyTorch 中初始化權重?

[英]How to initialize weights in PyTorch?

如何在 PyTorch 中初始化網絡中的權重和偏差(例如,使用 He 或 Xavier 初始化)?

單層

要初始化單個層的權重,請使用torch.nn.init的函數。 例如:

conv1 = torch.nn.Conv2d(...)
torch.nn.init.xavier_uniform(conv1.weight)

或者,您可以通過寫入conv1.weight.data (這是一個torch.Tensor )來修改參數。 例子:

conv1.weight.data.fill_(0.01)

這同樣適用於偏見:

conv1.bias.data.fill_(0.01)

nn.Sequential或自定義nn.Module

將初始化函數傳遞給torch.nn.Module.apply 它將遞歸地初始化整個nn.Module的權重。

apply( fn ):將fn遞歸地應用於每個子模塊(由.children()返回)以及 self. 典型用途包括初始化模型的參數(另請參見 torch-nn-init)。

例子:

def init_weights(m):
    if type(m) == nn.Linear:
        torch.nn.init.xavier_uniform(m.weight)
        m.bias.data.fill_(0.01)

net = nn.Sequential(nn.Linear(2, 2), nn.Linear(2, 2))
net.apply(init_weights)

我們使用相同的神經網絡 (NN) 架構比較了不同的權重初始化模式。

全零或一

如果您遵循奧卡姆剃刀原則,您可能會認為將所有權重設置為 0 或 1 是最佳解決方案。 不是這種情況。

在每個權重相同的情況下,每一層的所有神經元都產生相同的輸出。 這使得很難決定要調整哪些權重。

    # initialize two NN's with 0 and 1 constant weights
    model_0 = Net(constant_weight=0)
    model_1 = Net(constant_weight=1)
  • 2個時期后:

權重初始化為常數的訓練損失圖

Validation Accuracy
9.625% -- All Zeros
10.050% -- All Ones
Training Loss
2.304  -- All Zeros
1552.281  -- All Ones

統一初始化

均勻分布具有從一組數字中選擇任何數字的相等概率。

讓我們看看使用統一權重初始化的神經網絡訓練效果如何,其中low=0.0high=1.0

下面,我們將看到另一種初始化網絡權重的方法(除了在 Net 類代碼中)。 要在模型定義之外定義權重,我們可以:

  1. 定義一個按網絡層類型分配權重的函數,然后
  2. 使用model.apply(fn)將這些權重應用於初始化模型,該模型將函數應用於每個模型層。
    # takes in a module and applies the specified weight initialization
    def weights_init_uniform(m):
        classname = m.__class__.__name__
        # for every Linear layer in a model..
        if classname.find('Linear') != -1:
            # apply a uniform distribution to the weights and a bias=0
            m.weight.data.uniform_(0.0, 1.0)
            m.bias.data.fill_(0)

    model_uniform = Net()
    model_uniform.apply(weights_init_uniform)
  • 2個時期后:

在此處輸入圖片說明

Validation Accuracy
36.667% -- Uniform Weights
Training Loss
3.208  -- Uniform Weights

設置權重的一般規則

在神經網絡中設置權重的一般規則是將它們設置為接近於零而不是太小。

好的做法是在 [-y, y] 范圍內開始您的權重,其中y=1/sqrt(n)
(n 是給定神經元的輸入數量)。

    # takes in a module and applies the specified weight initialization
    def weights_init_uniform_rule(m):
        classname = m.__class__.__name__
        # for every Linear layer in a model..
        if classname.find('Linear') != -1:
            # get the number of the inputs
            n = m.in_features
            y = 1.0/np.sqrt(n)
            m.weight.data.uniform_(-y, y)
            m.bias.data.fill_(0)

    # create a new model with these weights
    model_rule = Net()
    model_rule.apply(weights_init_uniform_rule)

下面我們比較了 NN 的性能,權重初始化為均勻分布 [-0.5,0.5) 與權重使用一般規則初始化的那個

  • 2個時期后:

圖顯示了權重的統一初始化的性能與初始化的一般規則

Validation Accuracy
75.817% -- Centered Weights [-0.5, 0.5)
85.208% -- General Rule [-y, y)
Training Loss
0.705  -- Centered Weights [-0.5, 0.5)
0.469  -- General Rule [-y, y)

初始化權重的正態分布

正態分布的均值為 0,標准差為y=1/sqrt(n) ,其中 n 是 NN 的輸入數量

    ## takes in a module and applies the specified weight initialization
    def weights_init_normal(m):
        '''Takes in a module and initializes all linear layers with weight
           values taken from a normal distribution.'''

        classname = m.__class__.__name__
        # for every Linear layer in a model
        if classname.find('Linear') != -1:
            y = m.in_features
        # m.weight.data shoud be taken from a normal distribution
            m.weight.data.normal_(0.0,1/np.sqrt(y))
        # m.bias.data should be 0
            m.bias.data.fill_(0)

下面我們展示了兩個 NN 的性能,一個使用均勻分布初始化,另一個使用正態分布初始化

  • 2個時期后:

使用均勻分布與正態分布的權重初始化性能

Validation Accuracy
85.775% -- Uniform Rule [-y, y)
84.717% -- Normal Distribution
Training Loss
0.329  -- Uniform Rule [-y, y)
0.443  -- Normal Distribution

要初始化圖層,您通常不需要做任何事情。

PyTorch 會為您完成。 如果你仔細想想,這很有意義。 當 PyTorch 可以按照最新趨勢進行初始化時,我們為什么要初始化層。

檢查例如線性層

__init__方法中,它將調用Kaiming He init 函數。

    def reset_parameters(self):
        init.kaiming_uniform_(self.weight, a=math.sqrt(3))
        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)

其他圖層類型也類似。 例如,對於conv2d ,請檢查此處

注意:正確初始化的增益是更快的訓練速度。 如果您的問題需要特殊初始化,您可以在之后進行。

import torch.nn as nn        

# a simple network
rand_net = nn.Sequential(nn.Linear(in_features, h_size),
                         nn.BatchNorm1d(h_size),
                         nn.ReLU(),
                         nn.Linear(h_size, h_size),
                         nn.BatchNorm1d(h_size),
                         nn.ReLU(),
                         nn.Linear(h_size, 1),
                         nn.ReLU())

# initialization function, first checks the module type,
# then applies the desired changes to the weights
def init_normal(m):
    if type(m) == nn.Linear:
        nn.init.uniform_(m.weight)

# use the modules apply function to recursively apply the initialization
rand_net.apply(init_normal)

抱歉來晚了,希望我的回答能幫到你。

要使用normal distribution初始化權重,請使用:

torch.nn.init.normal_(tensor, mean=0, std=1)

或者使用constant distribution寫入:

torch.nn.init.constant_(tensor, value)

或者使用uniform distribution

torch.nn.init.uniform_(tensor, a=0, b=1) # a: lower_bound, b: upper_bound

您可以在此處查看初始化張量的其他方法

如果您想要一些額外的靈活性,您還可以手動設置權重

假設你有所有的輸入:

import torch
import torch.nn as nn

input = torch.ones((8, 8))
print(input)
tensor([[1., 1., 1., 1., 1., 1., 1., 1.],
        [1., 1., 1., 1., 1., 1., 1., 1.],
        [1., 1., 1., 1., 1., 1., 1., 1.],
        [1., 1., 1., 1., 1., 1., 1., 1.],
        [1., 1., 1., 1., 1., 1., 1., 1.],
        [1., 1., 1., 1., 1., 1., 1., 1.],
        [1., 1., 1., 1., 1., 1., 1., 1.],
        [1., 1., 1., 1., 1., 1., 1., 1.]])

並且您想要制作一個沒有偏差的密集層(以便我們可以可視化):

d = nn.Linear(8, 8, bias=False)

將所有權重設置為 0.5(或其他任何值):

d.weight.data = torch.full((8, 8), 0.5)
print(d.weight.data)

權重:

Out[14]: 
tensor([[0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000],
        [0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000],
        [0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000],
        [0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000],
        [0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000],
        [0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000],
        [0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000],
        [0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000]])

你所有的權重現在都是 0.5。 通過以下方式傳遞數據:

d(input)
Out[13]: 
tensor([[4., 4., 4., 4., 4., 4., 4., 4.],
        [4., 4., 4., 4., 4., 4., 4., 4.],
        [4., 4., 4., 4., 4., 4., 4., 4.],
        [4., 4., 4., 4., 4., 4., 4., 4.],
        [4., 4., 4., 4., 4., 4., 4., 4.],
        [4., 4., 4., 4., 4., 4., 4., 4.],
        [4., 4., 4., 4., 4., 4., 4., 4.],
        [4., 4., 4., 4., 4., 4., 4., 4.]], grad_fn=<MmBackward>)

請記住,每個神經元接收 8 個輸入,所有輸入的權重均為 0.5,值為 1(並且沒有偏差),因此每個輸入的總和為 4。

迭代參數

例如,如果模型沒有直接實現Sequential則不能使用apply

所有人都一樣

# see UNet at https://github.com/milesial/Pytorch-UNet/tree/master/unet


def init_all(model, init_func, *params, **kwargs):
    for p in model.parameters():
        init_func(p, *params, **kwargs)

model = UNet(3, 10)
init_all(model, torch.nn.init.normal_, mean=0., std=1) 
# or
init_all(model, torch.nn.init.constant_, 1.) 

根據形狀

def init_all(model, init_funcs):
    for p in model.parameters():
        init_func = init_funcs.get(len(p.shape), init_funcs["default"])
        init_func(p)

model = UNet(3, 10)
init_funcs = {
    1: lambda x: torch.nn.init.normal_(x, mean=0., std=1.), # can be bias
    2: lambda x: torch.nn.init.xavier_normal_(x, gain=1.), # can be weight
    3: lambda x: torch.nn.init.xavier_uniform_(x, gain=1.), # can be conv1D filter
    4: lambda x: torch.nn.init.xavier_uniform_(x, gain=1.), # can be conv2D filter
    "default": lambda x: torch.nn.init.constant(x, 1.), # everything else
}

init_all(model, init_funcs)

您可以嘗試使用torch.nn.init.constant_(x, len(x.shape))來檢查它們是否已正確初始化:

init_funcs = {
    "default": lambda x: torch.nn.init.constant_(x, len(x.shape))
}

因為到目前為止我還沒有足夠的聲譽,我無法在下面添加評論

prosti20196 月 26 日 13:16發布的答案。

    def reset_parameters(self):
        init.kaiming_uniform_(self.weight, a=math.sqrt(3))
        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)

但我想指出,實際上我們知道Kaiming He的論文中的一些假設, Delving Deep into Rectifiers: Surpassing Human-Level Performance on ImageNet Classification ,雖然看起來故意設計的初始化方法在實踐中很受歡迎.

例如,在Backward Propagation Case的小節中,他們假設 $w_l$ 和 $\\delta y_l$ 彼此獨立。 但是眾所周知,以得分圖 $\\delta y^L_i$ 為例,如果我們使用典型的,通常是 $y_i-softmax(y^L_i)=y_i-softmax(w^L_ix^L_i)交叉熵損失函數目標。

所以我認為他的初始化工作良好的真正根本原因仍有待解開。 因為每個人都見證了它在促進深度學習訓練方面的力量。

如果您看到棄用警告 (@Fábio Perez)...

def init_weights(m):
    if type(m) == nn.Linear:
        torch.nn.init.xavier_uniform_(m.weight)
        m.bias.data.fill_(0.01)

net = nn.Sequential(nn.Linear(2, 2), nn.Linear(2, 2))
net.apply(init_weights)

這是更好的方法,只需通過您的整個模型

import torch.nn as nn
def initialize_weights(model):
    # Initializes weights according to the DCGAN paper
    for m in model.modules():
        if isinstance(m, (nn.Conv2d, nn.ConvTranspose2d, nn.BatchNorm2d)):
            nn.init.normal_(m.weight.data, 0.0, 0.02)
        # if you also want for linear layers ,add one more elif condition 

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM