Bagaimana cara menginisialisasi bobot dan bias (misalnya, dengan inisialisasi He atau Xavier) dalam jaringan di PyTorch?
Bagaimana cara menginisialisasi bobot dan bias (misalnya, dengan inisialisasi He atau Xavier) dalam jaringan di PyTorch?
Jawaban:
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.Module
secara rekursif.
apply ( fn ): Berlaku
fn
secara 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)
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)
Validation Accuracy
9.625% -- All Zeros
10.050% -- All Ones
Training Loss
2.304 -- All Zeros
1552.281 -- All Ones
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.0
dan 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:
- Tentukan fungsi yang memberikan bobot berdasarkan jenis lapisan jaringan, lalu
- 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)
Validation Accuracy
36.667% -- Uniform Weights
Training Loss
3.208 -- Uniform Weights
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
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 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
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 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 conv2d
misalnya 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.
xavier_uniform
inisialisasi 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.)
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)
Maaf terlambat, saya harap jawaban saya akan membantu.
Untuk menginisialisasi bobot dengan normal distribution
menggunakan:
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
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.
Jika Anda tidak dapat menggunakan apply
misalnya jika model tidak diimplementasikanSequential
secara langsung:
# 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)
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))
}
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)
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.
reset_parameters
metode dalam kode sumber dari banyak modul. Haruskah saya mengganti metode inisialisasi bobot?