Pojedyncza warstwa
Aby zainicjować wagi pojedynczej warstwy, użyj funkcji from torch.nn.init
. Na przykład:
conv1 = torch.nn.Conv2d(...)
torch.nn.init.xavier_uniform(conv1.weight)
Alternatywnie możesz zmodyfikować parametry, pisząc do conv1.weight.data
(czyli a torch.Tensor
). Przykład:
conv1.weight.data.fill_(0.01)
To samo dotyczy uprzedzeń:
conv1.bias.data.fill_(0.01)
nn.Sequential
lub niestandardowe nn.Module
Przekaż funkcję inicjalizacyjną do torch.nn.Module.apply
. To zainicjuje wagi w całości nn.Module
rekurencyjnie.
Apply ( fn ): Stosuje się fn
rekurencyjnie do każdego modułu podrzędnego (zwróconego przez .children()
), a także do siebie. Typowe użycie obejmuje inicjalizację parametrów modelu (patrz także torch-nn-init).
Przykład:
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)
reset_parameters
metodę w kodzie źródłowym wielu modułów. Czy powinienem zastąpić metodę inicjalizacji wagi?Porównujemy różne sposoby inicjalizacji wagi przy użyciu tej samej architektury sieci neuronowej (NN).
All Zeros or Onees
Jeśli zastosujesz się do zasady brzytwy Ockhama , możesz pomyśleć, że najlepszym rozwiązaniem byłoby ustawienie wszystkich ciężarów na 0 lub 1. Nie o to chodzi.
Przy takiej samej wadze wszystkie neurony w każdej warstwie wytwarzają ten sam wynik. To sprawia, że trudno jest zdecydować, które ciężary dostosować.
# 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
Jednolita inicjalizacja
Dystrybucja uniform ma równe prawdopodobieństwo wytypowania dowolnej liczby ze zbioru liczb.
Zobaczmy, jak dobrze sieć neuronowa trenuje przy użyciu inicjalizacji o jednakowej masie, gdzie
low=0.0
ihigh=1.0
.Poniżej zobaczymy inny sposób (poza kodem klasy Net) zainicjowania wag sieci. Aby zdefiniować wagi poza definicją modelu, możemy:
# 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
Ogólna zasada ustawiania wag
Ogólna zasada ustalania wag w sieci neuronowej polega na ustawianiu ich wartości bliskich zeru, ale nie są zbyt małe.
# 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)
poniżej porównujemy wydajność NN, wagi inicjowane z rozkładem równomiernym [-0,5,0,5) z wagą inicjowaną według reguły ogólnej
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)
rozkład normalny, aby zainicjować wagi
## 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)
poniżej przedstawiamy działanie dwóch NN, z których jeden został zainicjowany przy użyciu rozkładu równomiernego, a drugi przy użyciu rozkładu normalnego
Validation Accuracy 85.775% -- Uniform Rule [-y, y) 84.717% -- Normal Distribution Training Loss 0.329 -- Uniform Rule [-y, y) 0.443 -- Normal Distribution
źródło
Aby zainicjować warstwy, zazwyczaj nie musisz nic robić.
PyTorch zrobi to za Ciebie. Jeśli się nad tym zastanowić, ma to sens. Po co inicjalizować warstwy, skoro PyTorch może to zrobić zgodnie z najnowszymi trendami.
Sprawdź na przykład warstwę Linear .
W
__init__
metodzie wywoła funkcję 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)
Podobnie jest z innymi typami warstw. Na
conv2d
przykład sprawdź tutaj .Uwaga: Uzyskanie odpowiedniej inicjalizacji to większa prędkość treningu. Jeśli twój problem zasługuje na specjalną inicjalizację, możesz to zrobić później.
źródło
xavier_uniform
inicjalizację wag (z obciążeniami początkowymi na 0), zamiast używania domyślnej inicjalizacji, moja dokładność walidacji po 30 epoki RMSprop wzrosły z 82% do 86%. Uzyskałem również 86% dokładności walidacji, gdy używam wbudowanego modelu VGG16 Pytorcha (nie został wstępnie wyszkolony), więc myślę, że zaimplementowałem go poprawnie. (Użyłem wskaźnika uczenia się 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)
źródło
Przepraszam za spóźnienie, mam nadzieję, że moja odpowiedź okaże się pomocna.
Aby zainicjować odważniki za
normal distribution
pomocą:torch.nn.init.normal_(tensor, mean=0, std=1)
Lub
constant distribution
napisać:Lub użyj
uniform distribution
:torch.nn.init.uniform_(tensor, a=0, b=1) # a: lower_bound, b: upper_bound
Można sprawdzić inne sposoby, aby zainicjować tensory tutaj
źródło
Jeśli potrzebujesz dodatkowej elastyczności, możesz również ustawić wagi ręcznie .
Powiedzmy, że masz wszystkie dane wejściowe:
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.]])
I chcesz utworzyć gęstą warstwę bez uprzedzeń (abyśmy mogli to wizualizować):
d = nn.Linear(8, 8, bias=False)
Ustaw wszystkie wagi na 0,5 (lub cokolwiek innego):
d.weight.data = torch.full((8, 8), 0.5) print(d.weight.data)
Wagi:
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]])
Wszystkie twoje wagi wynoszą teraz 0,5. Przekaż dane przez:
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>)
Pamiętaj, że każdy neuron otrzymuje 8 wejść, z których wszystkie mają wagę 0,5 i wartość 1 (bez odchylenia), więc sumuje się do 4 dla każdego.
źródło
Iteruj po parametrach
Jeśli nie możesz użyć,
apply
na przykład, jeśli model nie jest implementowanySequential
bezpośrednio:To samo dla wszystkich
# 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.)
W zależności od kształtu
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)
Możesz spróbować
torch.nn.init.constant_(x, len(x.shape))
sprawdzić, czy są odpowiednio zainicjowane:init_funcs = { "default": lambda x: torch.nn.init.constant_(x, len(x.shape)) }
źródło
Jeśli zobaczysz ostrzeżenie o wycofaniu (@ 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)
źródło
Ponieważ dotychczas nie miałem wystarczającej reputacji, nie mogę dodać komentarza pod
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)
Ale chcę zaznaczyć, że tak naprawdę znamy pewne założenia z artykułu Kaiming He , Delving Deep into Rectifiers: Surpassing Human-Level Performance on ImageNet Classification , nie są właściwe, chociaż wygląda na to, że celowo zaprojektowana metoda inicjalizacji odniosła sukces w praktyce .
Np. W podrozdziale Przypadku propagacji wstecznej zakładają, że $ w_l $ i $ \ delta y_l $ są od siebie niezależne. Ale jak wszyscy wiemy, weźmy mapę wyników $ \ delta y ^ L_i $ jako przykład, często jest to $ y_i-softmax (y ^ L_i) = y_i-softmax (w ^ L_ix ^ L_i) $, jeśli używamy typowego krzyżowa funkcja utraty entropii cel.
Więc myślę, że prawdziwym powodem, dla którego Inicjalizacja He działa dobrze, pozostaje do wyjaśnienia. Ponieważ wszyscy byli świadkami jego mocy w zwiększaniu treningu głębokiego uczenia się.
źródło