PyTorch'ta ağırlıklar nasıl başlatılır?


Yanıtlar:


160

Tek katman

Tek bir katmanın ağırlıklarını başlatmak için bir işlev kullanın torch.nn.init. Örneğin:

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

Alternatif olarak, conv1.weight.data(a torch.Tensor) yazarak parametreleri değiştirebilirsiniz . Misal:

conv1.weight.data.fill_(0.01)

Aynısı önyargılar için de geçerlidir:

conv1.bias.data.fill_(0.01)

nn.Sequential veya özel nn.Module

Bir başlatma işlevini öğesine iletin torch.nn.Module.apply. Tüm ağırlıkları nn.Moduleözyinelemeli olarak başlatacaktır .

apply ( fn ): Kendine olduğu kadar fnher alt modüle (tarafından döndürüldüğü şekilde .children()) özyinelemeli olarak uygulanır . Tipik kullanım, bir modelin parametrelerinin başlatılmasını içerir (ayrıca bkz. Torch-nn-init).

Misal:

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)

6
reset_parametersBirçok modülün kaynak kodunda bir yöntem buldum . Ağırlık başlatma yöntemini geçersiz kılmalı mıyım?
Yang Bo

1
biraz ortalama ve std ile Normal dağılım kullanmak istersem ne olur?
Charlie Parker

12
Bir tane belirtmezsem varsayılan başlatma nedir?
xjcl

En azından doğrusal katmanlar için varsayılan başlatma şudur
arash javan

41

Aynı sinir ağı (NN) mimarisini kullanarak farklı ağırlık başlatma modlarını karşılaştırıyoruz.

Tümü Sıfırlar veya Birler

Occam'ın ustura prensibini izlerseniz, tüm ağırlıkları 0 veya 1 olarak ayarlamanın en iyi çözüm olacağını düşünebilirsiniz. Durum bu değil.

Her ağırlık aynı olduğunda, her katmandaki tüm nöronlar aynı çıktıyı üretir. Bu, hangi ağırlıkların ayarlanacağına karar vermeyi zorlaştırır.

    # initialize two NN's with 0 and 1 constant weights
    model_0 = Net(constant_weight=0)
    model_1 = Net(constant_weight=1)
  • 2 dönem sonra:

sabit ağırlık başlatma ile eğitim kaybı grafiği

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

Tekdüzen Başlatma

Bir homojen dağılımı numaralarını bir dizi arasında bir sayı seçmek eşit olasılığına sahiptir.

Nöral ağın, nerede low=0.0ve nerede, tek tip ağırlık başlatma kullanarak ne kadar iyi eğitildiğini görelim high=1.0.

Aşağıda, bir ağın ağırlıklarını başlatmak için başka bir yol (Net sınıf kodunun yanı sıra) göreceğiz. Model tanımının dışında ağırlıkları tanımlamak için şunları yapabiliriz:

  1. Ağ katmanının türüne göre ağırlık atayan bir işlev tanımlayın, ardından
  2. Bu ağırlıkları kullanarak model.apply(fn), her model katmanına bir işlev uygulayan başlatılmış bir modele uygulayın .
    # 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 dönem sonra:

görüntü açıklamasını buraya girin

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

Ağırlık ayarlamak için genel kural

Bir sinir ağında ağırlıkları ayarlamanın genel kuralı, çok küçük olmadan sıfıra yakın olacak şekilde ayarlamaktır.

İyi bir uygulama, ağırlıklarınızı [-y, y] aralığında başlatmaktır, burada y=1/sqrt(n)
(n, belirli bir nörona giriş sayısıdır).

    # 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)

Aşağıda, NN'nin performansını, tekdüze dağılımla [-0.5,0.5) başlatılan ağırlıklar ile ağırlığı genel kural kullanılarak başlatılan ağırlıklar karşılaştırıyoruz

  • 2 dönem sonra:

Genel başlatma kuralına karşı tek tip ağırlık başlatma performansını gösteren grafik

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)

ağırlıkları başlatmak için normal dağılım

Normal dağılımın ortalaması 0 ve standart sapması olmalıdır y=1/sqrt(n), burada n, NN'ye giriş sayısıdır

    ## 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)

aşağıda, biri tekdüze dağılım ve diğeri normal dağılım kullanılarak başlatılan iki NN'nin performansını gösteriyoruz

  • 2 dönem sonra:

normal dağılıma karşı tekdüze dağılım kullanarak ağırlık başlatma performansı

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

7
Optimize ettiğiniz görev nedir? Ve tamamı sıfır olan bir çözüm nasıl sıfır kayıp verebilir?
dedObed

19

Katmanları başlatmak için genellikle hiçbir şey yapmanız gerekmez.

PyTorch bunu sizin için yapacak. Bir düşünürseniz, bunun çok anlamı var. PyTorch bunu en son trendleri takip ederek yapabiliyorken neden katmanları başlatmalıyız?

Örneğin Doğrusal katmanı kontrol edin .

Gelen __init__yöntemle çağıracağı Kaiming He başlat işlevini.

    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)

Benzer, diğer katman türleri içindir. Örneğin burayıconv2d kontrol edin .

Not: Uygun başlatma kazanımı, daha hızlı eğitim hızıdır. Sorununuz özel başlatmayı hak ediyorsa, bunu sonradan yapabilirsiniz.


Yine de varsayılan başlatma her zaman en iyi sonuçları vermez. Kısa bir süre önce Pytorch'ta VGG16 mimarisini uyguladım ve CIFAR-10 veri seti üzerinde eğittim ve xavier_uniformvarsayılan başlatmayı kullanmak yerine sadece ağırlıklar için başlatmaya geçerek (önyargılar 0 olarak başlatıldı), 30'dan sonra doğrulama doğruluğumu buldum. RMSprop dönemleri% 82'den% 86'ya yükseldi. Ayrıca Pytorch'un yerleşik VGG16 modelini (önceden eğitilmemiş) kullanırken% 86 doğrulama doğruluğuna sahip oldum, bu yüzden doğru uyguladığımı düşünüyorum. (0,00001 öğrenme oranı kullandım.)
littleO

Bunun nedeni, VGG16'da Toplu Normları kullanmamış olmalarıdır. Doğru başlatmanın önemli olduğu ve bazı mimariler için dikkat ettiğiniz doğrudur. Örneğin, (nn.conv2d (), ReLU () dizisi) kullanırsanız, dönüşüm katmanınızı yeniden oluşturmak için tasarlanmış Kaiming He başlatmasını başlatırsınız. PyTorch, conv2d'den sonra etkinleştirme işlevinizi tahmin edemez. Eigndeğerleri değerlendirirseniz bu mantıklıdır, ancak genellikle Toplu İş Normlarını kullanırsanız çok fazla bir şey yapmanız gerekmez, çıktıları sizin için normalleştirirler. SotaBench yarışmasına kazanmayı planlıyorsanız, bu önemlidir.
prosti

7
    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)

5

Bu kadar geç kaldığım için özür dilerim, umarım cevabım yardımcı olur.

Ağırlıkları bir normal distributionkullanımla başlatmak için:

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

Veya bir constant distributionyazı kullanmak için :

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

Veya kullanmak için uniform distribution:

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

Burada tensörleri başlatmak için diğer yöntemleri kontrol edebilirsiniz.


3

Ekstra esneklik istiyorsanız, ağırlıkları manuel olarak da ayarlayabilirsiniz .

Diyelim ki hepsinin girdisi var:

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.]])

Ve önyargısız yoğun bir katman yapmak istiyorsunuz (böylece görselleştirebiliriz):

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

Tüm ağırlıkları 0,5'e (veya başka bir şeye) ayarlayın:

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

Ağırlıklar:

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]])

Artık tüm ağırlıklarınız 0,5. Verileri şunlardan geçirin:

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>)

Her bir nöronun, her birinin ağırlığı 0,5 ve değeri 1 olan (ve önyargısız) 8 girdi aldığını, dolayısıyla her biri için toplamı 4'e kadar çıktığını unutmayın.


1

Parametreleri yineleyin

Kullanamazsan apply örneğin modeli uygulamaması halinde Sequentialdoğrudan:

Hepsi için aynı

# 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.) 

Şekle bağlı olarak

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))Uygun şekilde başlatılıp başlatılmadıklarını kontrol etmeyi deneyebilirsiniz :

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

0

Bir kullanımdan kaldırma uyarısı görürseniz (@ 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)

1
Cevapları açık tutmak için orada Fábio Perez'in cevabına yorum yapabilirsiniz .
Phani Rithvij

0

Şimdiye kadar yeterince itibara sahip olmadığım için, altına yorum ekleyemiyorum

cevap prosti tarafından Haziran 26 ' 19'da 13 : 16'da .

    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)

Ama şunu belirtmek isterim ki Kaiming He'nin makalesinde bazı varsayımlar bildiğimizi belirtmek isterim , Doğrultuculara Derinlemesine Delving: ImageNet Sınıflandırmasında İnsan Düzeyindeki Performansı Aşmak kasten tasarlanmış başlatma yöntemi olarak gözükmektedir olsa pratikte bir isabet yapar, mülk değildir, .

Örneğin, Geriye Doğru Yayılma Durumunun alt bölümünde , $ w_l $ ve $ \ delta y_l $ 'ın birbirinden bağımsız olduğunu varsayarlar. Ama hepimizin bildiği gibi, $ \ delta y ^ L_i $ puan haritasını örnek olarak alın, tipik bir çapraz entropi kaybı işlevi hedefi.

Bu yüzden, He's Initialization'ın iyi çalışmasının altında yatan gerçek neden çözülmeye devam ediyor. Çünkü herkes derin öğrenme eğitimini artırma gücüne şahit oldu.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.