PyTorch'taki bir ağda ağırlıkları ve önyargıları (örneğin He veya Xavier başlatmasıyla) nasıl başlatabilirim?
PyTorch'taki bir ağda ağırlıkları ve önyargıları (örneğin He veya Xavier başlatmasıyla) nasıl başlatabilirim?
Yanıtlar:
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
fn
her 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)
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)
Validation Accuracy
9.625% -- All Zeros
10.050% -- All Ones
Training Loss
2.304 -- All Zeros
1552.281 -- All Ones
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.0
ve 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:
- Ağ katmanının türüne göre ağırlık atayan bir işlev tanımlayın, ardından
- 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)
Validation Accuracy
36.667% -- Uniform Weights
Training Loss
3.208 -- Uniform Weights
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
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)
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
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 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.
xavier_uniform
varsayı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.)
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)
Bu kadar geç kaldığım için özür dilerim, umarım cevabım yardımcı olur.
Ağırlıkları bir normal distribution
kullanımla başlatmak için:
torch.nn.init.normal_(tensor, mean=0, std=1)
Veya bir constant distribution
yazı 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.
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.
Kullanamazsan apply
örneğin modeli uygulamaması halinde Sequential
doğrudan:
# 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))
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))
}
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)
Ş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.
reset_parameters
Birçok modülün kaynak kodunda bir yöntem buldum . Ağırlık başlatma yöntemini geçersiz kılmalı mıyım?