Bagaimana cara menginisialisasi bobot di PyTorch?

117

Bagaimana cara menginisialisasi bobot dan bias (misalnya, dengan inisialisasi He atau Xavier) dalam jaringan di PyTorch?

Fábio Perez
sumber

Jawaban:

160

Satu lapis

Untuk menginisialisasi bobot satu lapisan, gunakan fungsi dari torch.nn.init. Contohnya:

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

Alternatifnya, Anda dapat memodifikasi parameter dengan menulis ke conv1.weight.data(yaitu a torch.Tensor). Contoh:

conv1.weight.data.fill_(0.01)

Hal yang sama berlaku untuk bias:

conv1.bias.data.fill_(0.01)

nn.Sequential atau adat nn.Module

Teruskan fungsi inisialisasi ke torch.nn.Module.apply. Ini akan menginisialisasi bobot di seluruh nn.Modulesecara rekursif.

apply ( fn ): Berlaku fnsecara rekursif untuk setiap submodul (seperti yang dikembalikan oleh .children()) serta diri sendiri. Penggunaan umum termasuk menginisialisasi parameter model (lihat juga torch-nn-init).

Contoh:

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)
Fábio Perez
sumber
6
Saya menemukan reset_parametersmetode dalam kode sumber dari banyak modul. Haruskah saya mengganti metode inisialisasi bobot?
Yang Bo
1
bagaimana jika saya ingin menggunakan distribusi Normal dengan beberapa mean dan std?
Charlie Parker
12
Apa inisialisasi default jika saya tidak menentukannya?
xjcl
inisialisasi bawaan setidaknya untuk lapisan linear adalah nya: pytorch.org/docs/stable/nn.html#linear-layers
arash jawa
40

Kami membandingkan mode inisialisasi bobot yang berbeda menggunakan arsitektur neural-network (NN) yang sama.

Semua Nol atau Satu

Jika Anda mengikuti prinsip pisau cukur Occam , Anda mungkin berpikir bahwa mengatur semua bobot ke 0 atau 1 akan menjadi solusi terbaik. Ini bukan kasusnya.

Dengan setiap bobot yang sama, semua neuron di setiap lapisan menghasilkan keluaran yang sama. Hal ini membuat sulit untuk memutuskan bobot mana yang akan disesuaikan.

    # initialize two NN's with 0 and 1 constant weights
    model_0 = Net(constant_weight=0)
    model_1 = Net(constant_weight=1)
  • Setelah 2 periode:

plot kerugian pelatihan dengan inisialisasi bobot konstan

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

Inisialisasi Seragam

Sebuah distribusi seragam memiliki probabilitas yang sama memetik angka dari satu set nomor.

Mari kita lihat seberapa baik jaringan neural berlatih menggunakan inisialisasi bobot seragam, di mana low=0.0dan high=1.0.

Di bawah ini, kita akan melihat cara lain (selain kode kelas Net) untuk menginisialisasi bobot jaringan. Untuk menentukan bobot di luar definisi model, kita dapat:

  1. Tentukan fungsi yang memberikan bobot berdasarkan jenis lapisan jaringan, lalu
  2. Menerapkan bobot tersebut ke model yang diinisialisasi menggunakan model.apply(fn), yang menerapkan fungsi ke setiap lapisan model.
    # 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)
  • Setelah 2 periode:

masukkan deskripsi gambar di sini

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

Aturan umum untuk mengatur bobot

Aturan umum untuk menyetel bobot di jaringan neural adalah menyetelnya mendekati nol tanpa menjadi terlalu kecil.

Praktik yang baik adalah memulai bobot Anda dalam kisaran [-y, y] di mana y=1/sqrt(n)
(n adalah jumlah input ke neuron tertentu).

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

di bawah ini kami membandingkan kinerja NN, bobot yang diinisialisasi dengan distribusi seragam [-0.5,0.5) versus bobot yang diinisialisasi menggunakan aturan umum

  • Setelah 2 periode:

plot yang menunjukkan kinerja inisialisasi bobot yang seragam versus aturan umum inisialisasi

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)

distribusi normal untuk menginisialisasi bobot

Distribusi normal harus memiliki mean 0 dan deviasi standar y=1/sqrt(n), di mana n adalah jumlah input ke 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)

di bawah ini kami menunjukkan kinerja dari dua NN, satu diinisialisasi menggunakan distribusi seragam dan yang lainnya menggunakan distribusi normal

  • Setelah 2 periode:

kinerja inisialisasi bobot menggunakan distribusi seragam versus distribusi normal

Validation Accuracy
85.775% -- Uniform Rule [-y, y)
84.717% -- Normal Distribution
Training Loss
0.329  -- Uniform Rule [-y, y)
0.443  -- Normal Distribution
ashunigion
sumber
7
Apa tugas yang Anda optimalkan? Dan bagaimana solusi semua nol memberikan kerugian nol?
dedObed
19

Untuk menginisialisasi lapisan, Anda biasanya tidak perlu melakukan apa pun.

PyTorch akan melakukannya untuk Anda. Jika Anda memikirkannya, ini memiliki banyak arti. Mengapa kita harus menginisialisasi lapisan, ketika PyTorch dapat melakukannya mengikuti tren terbaru.

Periksa misalnya lapisan Linear .

Dalam __init__metode ini akan memanggil fungsi init Kaiming He .

    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)

Yang serupa untuk jenis lapisan lainnya. Untuk conv2dmisalnya memeriksa di sini .

Untuk diperhatikan: Keuntungan dari inisialisasi yang tepat adalah kecepatan latihan yang lebih cepat. Jika masalah Anda memerlukan inisialisasi khusus, Anda dapat melakukannya setelah kata sandi.

prosti
sumber
Inisialisasi default tidak selalu memberikan hasil terbaik. Saya baru-baru ini mengimplementasikan arsitektur VGG16 di Pytorch dan melatihnya pada set data CIFAR-10, dan saya menemukan bahwa hanya dengan beralih ke xavier_uniforminisialisasi untuk bobot (dengan bias yang diinisialisasi ke 0), daripada menggunakan inisialisasi default, akurasi validasi saya setelah 30 epochs RMSprop meningkat dari 82% menjadi 86%. Saya juga mendapatkan akurasi validasi 86% saat menggunakan model VGG16 bawaan Pytorch (tidak dilatih sebelumnya), jadi saya rasa saya menerapkannya dengan benar. (Saya menggunakan kecepatan pemelajaran 0,00001.)
littleO
Ini karena mereka belum menggunakan Batch Norms di VGG16. Memang benar bahwa inisialisasi yang tepat penting dan untuk beberapa arsitektur Anda harus memperhatikan. Misalnya, jika Anda menggunakan (nn.conv2d (), ReLU () sequence) Anda akan memulai inisialisasi Kaiming He yang dirancang untuk relu layer konv Anda. PyTorch tidak dapat memprediksi fungsi aktivasi Anda setelah konv2d. Ini masuk akal jika Anda mengevaluasi eignevalues, tetapi biasanya Anda tidak perlu melakukan banyak hal jika Anda menggunakan Batch Norms, mereka akan menormalkan keluaran untuk Anda. Jika Anda berencana untuk memenangkan kompetisi SotaBench, itu penting.
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)
Duane
sumber
5

Maaf terlambat, saya harap jawaban saya akan membantu.

Untuk menginisialisasi bobot dengan normal distributionmenggunakan:

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

Atau menggunakan file constant distribution tulisan:

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

Atau menggunakan file uniform distribution :

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

Anda dapat memeriksa metode lain untuk menginisialisasi tensor di sini

Luca Di Liello
sumber
3

Jika Anda menginginkan fleksibilitas ekstra, Anda juga dapat mengatur bobot secara manual .

Katakanlah Anda memiliki masukan dari semuanya:

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

Dan Anda ingin membuat lapisan padat tanpa bias (sehingga kami dapat memvisualisasikannya):

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

Atur semua bobot menjadi 0,5 (atau yang lainnya):

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

Bobot:

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

Semua bobot Anda sekarang 0,5. Kirimkan data melalui:

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

Ingatlah bahwa setiap neuron menerima 8 masukan, yang semuanya memiliki bobot 0,5 dan nilai 1 (dan tidak ada bias), jadi jumlahnya masing-masing menjadi 4.

Nicolas Gervais
sumber
1

Iterasi parameter

Jika Anda tidak dapat menggunakan applymisalnya jika model tidak diimplementasikanSequential secara langsung:

Sama untuk semua

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

Tergantung bentuknya

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)

Anda dapat mencoba torch.nn.init.constant_(x, len(x.shape))untuk memeriksa apakah mereka diinisialisasi dengan benar:

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

Jika Anda melihat peringatan penghentian (@ 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)
Joseph Konan
sumber
1
Anda dapat berkomentar di sana di jawaban Fábio Perez untuk menjaga jawaban tetap bersih.
Phani Rithvij
0

Karena reputasi saya sejauh ini belum cukup, saya tidak bisa menambahkan komentar di bawah

jawabannya diposting oleh prosti pada 26 Juni '19 pukul 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)

Tapi saya ingin menunjukkan bahwa sebenarnya kita tahu beberapa asumsi dalam makalah Kaiming He , Menggali Lebih Dalam ke Rectifier: Melampaui Kinerja Tingkat Manusia pada Klasifikasi ImageNet , tidak tepat, meskipun tampaknya metode inisialisasi yang sengaja dirancang membuat hit dalam praktiknya. .

Misalnya, dalam sub-bagian Kasus Propagasi Mundur , mereka mengasumsikan bahwa $ w_l $ dan $ \ delta y_l $ tidak bergantung satu sama lain. Tapi seperti yang kita semua tahu, ambil peta skor $ \ delta y ^ L_i $ sebagai contoh, seringkali $ y_i-softmax (y ^ L_i) = y_i-softmax (w ^ L_ix ^ L_i) $ jika kita menggunakan tipikal tujuan fungsi kerugian entropi silang.

Jadi saya pikir alasan mendasar sebenarnya mengapa Inisialisasi Dia bekerja dengan baik masih belum terurai. Karena semua orang telah menyaksikan kekuatannya dalam meningkatkan pelatihan pembelajaran yang mendalam.

Glory Chen
sumber