Wie initialisiere ich Gewichte in PyTorch?

102

Wie initialisiere ich die Gewichte und Verzerrungen (z. B. mit He- oder Xavier-Initialisierung) in einem Netzwerk in PyTorch?

Fábio Perez
quelle

Antworten:

144

Einzelne Schicht

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.Modulerekursiv initialisieren .

apply ( fn ): Gilt fnrekursiv 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)
Fábio Perez
quelle
6
Ich habe reset_parametersim Quellcode vieler Module eine Methode gefunden . Sollte ich die Methode zur Gewichtsinitialisierung überschreiben?
Yang Bo
1
Was ist, wenn ich eine Normalverteilung mit einem Mittelwert und einem Standard verwenden möchte?
Charlie Parker
12
Was ist die Standardinitialisierung, wenn ich keine spezifiziere?
10.
Standardinitialisierung zumindest für lineare Ebenen ist sie: pytorch.org/docs/stable/nn.html#linear-layers
arash javan
40

Wir vergleichen verschiedene Arten der Gewichtsinitialisierung unter Verwendung derselben NN-Architektur (Neural Network).

Alle Nullen oder Einsen

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)
  • Nach 2 Epochen:

Diagramm des Trainingsverlustes mit Gewichtsinitialisierung auf konstant

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

Einheitliche Initialisierung

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.0und 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:

  1. Definieren einer Funktion , die Abtretungs Gewichte durch die Art der Netzwerkschicht, dann
  2. 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)
  • Nach 2 Epochen:

Geben Sie hier die Bildbeschreibung ein

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

Allgemeine Regel zum Einstellen von Gewichten

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

  • Nach 2 Epochen:

Diagramm, das die Leistung einer einheitlichen Initialisierung des Gewichts gegenüber der allgemeinen Initialisierungsregel zeigt

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)

Normalverteilung zum Initialisieren der Gewichte

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

  • Nach 2 Epochen:

Durchführung der Gewichtsinitialisierung unter Verwendung einer gleichmäßigen Verteilung gegenüber der Normalverteilung

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
quelle
6
Für welche Aufgabe optimieren Sie? Und wie kann eine Null-Null-Lösung einen Verlust von Null ergeben?
dedObed
16

Um Ebenen zu initialisieren, müssen Sie normalerweise nichts tun.

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 conv2dBeispiel 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.

Prosti
quelle
Die Standardinitialisierung liefert jedoch nicht immer die besten Ergebnisse. Ich habe kürzlich die VGG16-Architektur in Pytorch implementiert und sie auf dem CIFAR-10-Datensatz trainiert. Dabei habe ich festgestellt, dass xavier_uniformmeine 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.)
littleO
Dies liegt daran, dass sie in VGG16 keine Chargennormen verwendet haben. Es ist wahr, dass die richtige Initialisierung wichtig ist und dass Sie bei einigen Architekturen darauf achten. Wenn Sie beispielsweise die Sequenz (nn.conv2d (), ReLU ()) verwenden, initiieren Sie die Kaiming He-Initialisierung, die für die Reluierung Ihrer Conv-Ebene entwickelt wurde. PyTorch kann Ihre Aktivierungsfunktion nach dem conv2d nicht vorhersagen. Dies ist sinnvoll, wenn Sie die Fremdwerte auswerten. In der Regel müssen Sie jedoch nicht viel tun, wenn Sie Chargennormen verwenden. Dadurch werden die Ausgaben für Sie normalisiert. Wenn Sie vorhaben, den SotaBench-Wettbewerb zu gewinnen, ist dies wichtig.
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
quelle
5

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

Luca Di Liello
quelle
1

Parameter durchlaufen

Wenn Sie beispielsweise nicht verwenden können, applywenn das Modell nicht implementiert istSequential direkt wird:

Für alle gleich

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

Je nach Form

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))
}
ted
quelle
1

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.

Nicolas Gervais
quelle
0

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)
Joseph Konan
quelle
1
Sie können dort bei Fábio Perez 'Antwort einen Kommentar abgeben , um die Antworten sauber zu halten.
Phani Rithvij
0

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.

Ruhm Chen
quelle