Wie initialisiere ich die Gewichte und Verzerrungen (z. B. mit He- oder Xavier-Initialisierung) in einem Netzwerk in PyTorch?
Wie initialisiere ich die Gewichte und Verzerrungen (z. B. mit He- oder Xavier-Initialisierung) in einem Netzwerk in PyTorch?
Antworten:
Verwenden Sie eine Funktion von, um die Gewichte einer einzelnen Ebene zu initialisieren torch.nn.init
. Zum Beispiel:
conv1 = torch.nn.Conv2d(...)
torch.nn.init.xavier_uniform(conv1.weight)
Alternativ können Sie die Parameter ändern, indem Sie in conv1.weight.data
(a torch.Tensor
) schreiben . Beispiel:
conv1.weight.data.fill_(0.01)
Gleiches gilt für Vorurteile:
conv1.bias.data.fill_(0.01)
nn.Sequential
oder benutzerdefiniert nn.Module
Übergeben Sie eine Initialisierungsfunktion an torch.nn.Module.apply
. Es wird die Gewichte im gesamten nn.Module
rekursiv initialisieren .
apply ( fn ): Gilt
fn
rekursiv für jedes Submodul (wie von zurückgegeben.children()
) sowie für self. Typische Verwendung ist das Initialisieren der Parameter eines Modells (siehe auch torch-nn-init).
Beispiel:
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)
Wenn Sie dem Prinzip von Occams Rasiermesser folgen , denken Sie vielleicht, dass es die beste Lösung wäre, alle Gewichte auf 0 oder 1 zu setzen. Das ist nicht der Fall.
Bei gleichem Gewicht produzieren alle Neuronen in jeder Schicht die gleiche Leistung. Dies macht es schwierig zu entscheiden, welche Gewichte eingestellt werden sollen.
# 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
Eine gleichmäßige Verteilung hat die gleiche Wahrscheinlichkeit, eine beliebige Zahl aus einer Reihe von Zahlen auszuwählen.
Mal sehen, wie gut das neuronale Netzwerk mit einer einheitlichen Gewichtsinitialisierung trainiert, wo low=0.0
und high=1.0
.
Im Folgenden sehen wir eine andere Möglichkeit (außer im Net-Klassencode), die Gewichte eines Netzwerks zu initialisieren. Um Gewichte außerhalb der Modelldefinition zu definieren, können wir:
- Definieren einer Funktion , die Abtretungs Gewichte durch die Art der Netzwerkschicht, dann
- Wenden Sie diese Gewichte mit auf ein initialisiertes Modell an
model.apply(fn)
, das eine Funktion auf jede Modellebene anwendet.
# 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
Die allgemeine Regel zum Einstellen der Gewichte in einem neuronalen Netzwerk besteht darin, sie nahe Null zu setzen, ohne zu klein zu sein.
Es empfiehlt sich, die Gewichte im Bereich von [-y, y] zu beginnen, wobei
y=1/sqrt(n)
(n die Anzahl der Eingaben in ein bestimmtes Neuron ist).
# 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)
Im Folgenden vergleichen wir die Leistung von NN, Gewichten, die mit einer gleichmäßigen Verteilung initialisiert wurden [-0,5,0,5], mit denen, deren Gewicht nach einer allgemeinen Regel initialisiert wurde
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)
Die Normalverteilung sollte einen Mittelwert von 0 und eine Standardabweichung von haben
y=1/sqrt(n)
, wobei n die Anzahl der Eingaben in NN ist
## 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)
Im Folgenden zeigen wir die Leistung von zwei NN, von denen einer mit Gleichverteilung und der andere mit Normalverteilung initialisiert wurde
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 erledigt das für Sie. Wenn Sie darüber nachdenken, hat dies viel Sinn. Warum sollten wir Ebenen initialisieren, wenn PyTorch dies nach den neuesten Trends tun kann?
Überprüfen Sie zum Beispiel die lineare Ebene .
In der __init__
Methode wird die Kaiming He init-Funktion aufgerufen .
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)
Ähnliches gilt für andere Ebenentypen. Zum conv2d
Beispiel hier überprüfen .
Zu beachten: Der Vorteil einer ordnungsgemäßen Initialisierung ist die schnellere Trainingsgeschwindigkeit. Wenn Ihr Problem eine spezielle Initialisierung verdient, können Sie dies nachträglich tun.
xavier_uniform
meine Validierungsgenauigkeit nach 30 nur durch Umschalten auf die Initialisierung für die Gewichte (mit auf 0 initialisierten Verzerrungen) anstatt auf die Standardinitialisierung erfolgt Die Epochen von RMSprop stiegen von 82% auf 86%. Ich habe auch eine Validierungsgenauigkeit von 86% erhalten, wenn ich das in Pytorch integrierte VGG16-Modell verwendet habe (nicht vorab trainiert), daher denke ich, dass ich es korrekt implementiert habe. (Ich habe eine Lernrate von 0,00001 verwendet.)
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)
Tut mir leid, dass ich so spät komme, ich hoffe, meine Antwort wird helfen.
So initialisieren Sie Gewichte mit a normal distribution
Verwendung:
torch.nn.init.normal_(tensor, mean=0, std=1)
Oder um a constant distribution
Schreiben zu verwenden:
torch.nn.init.constant_(tensor, value)
Oder um eine zu verwenden uniform distribution
:
torch.nn.init.uniform_(tensor, a=0, b=1) # a: lower_bound, b: upper_bound
Sie können andere Methoden überprüfen , um Tensoren zu initialisieren hier
Wenn Sie beispielsweise nicht verwenden können, apply
wenn das Modell nicht implementiert istSequential
direkt wird:
# 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)
Sie können versuchen, torch.nn.init.constant_(x, len(x.shape))
zu überprüfen, ob sie ordnungsgemäß initialisiert wurden:
init_funcs = {
"default": lambda x: torch.nn.init.constant_(x, len(x.shape))
}
Wenn Sie zusätzliche Flexibilität wünschen, können Sie die Gewichte auch manuell einstellen .
Angenommen, Sie haben alle Eingaben:
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.]])
Und Sie möchten eine dichte Ebene ohne Verzerrung erstellen (damit wir sie visualisieren können):
d = nn.Linear(8, 8, bias=False)
Stellen Sie alle Gewichte auf 0,5 (oder etwas anderes) ein:
d.weight.data = torch.full((8, 8), 0.5)
print(d.weight.data)
Die Gewichte:
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]])
Alle Ihre Gewichte sind jetzt 0,5. Geben Sie die Daten weiter:
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>)
Denken Sie daran, dass jedes Neuron 8 Eingaben empfängt, die alle ein Gewicht von 0,5 und einen Wert von 1 (und keine Verzerrung) haben, sodass sich für jedes Neuron eine Summe von 4 ergibt.
Wenn Sie eine Abwertungswarnung sehen (@ 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)
Da ich bisher nicht genug Ruf hatte, kann ich unter keinen Kommentar hinzufügen
die Antwort von Prosti in Jun 26 '19 um 13:16 geschrieben .
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)
Aber ich möchte darauf hinweisen, dass wir tatsächlich einige Annahmen in dem Artikel von Kaiming He kennen , der sich eingehend mit Gleichrichtern befasst: Übertreffen der Leistung auf menschlicher Ebene bei der ImageNet-Klassifizierung , sind nicht angemessen, obwohl es so aussieht, als ob die absichtlich entworfene Initialisierungsmethode in der Praxis einen Erfolg hat .
ZB innerhalb des Unterabschnitts von Backward Propagation Case an, dass $ w_l $ und $ \ delta y_l $ unabhängig voneinander sind. Aber wie wir alle wissen, nehmen wir die Score-Map $ \ delta y ^ L_i $ als Instanz, es ist oft $ y_i-softmax (y ^ L_i) = y_i-softmax (w ^ L_ix ^ L_i) $, wenn wir eine typische verwenden Ziel der Kreuzentropieverlustfunktion.
Daher denke ich, dass der wahre Grund, warum die Initialisierung von He gut funktioniert, noch nicht geklärt ist. Denn jeder hat seine Kraft gesehen, das Deep-Learning-Training zu fördern.
reset_parameters
im Quellcode vieler Module eine Methode gefunden . Sollte ich die Methode zur Gewichtsinitialisierung überschreiben?