Den Originalcode habe ich auf der PyTorch-Website nicht mehr gefunden.
gradients = torch.FloatTensor([0.1, 1.0, 0.0001])
y.backward(gradients)
print(x.grad)
Das Problem mit dem obigen Code gibt es keine Funktion basierend auf der Berechnung der Gradienten. Dies bedeutet, dass wir nicht wissen, wie viele Parameter (Argumente die Funktion akzeptiert) und welche Dimension von Parametern.
Um dies vollständig zu verstehen, habe ich ein Beispiel erstellt, das dem Original nahe kommt:
Beispiel 1:
a = torch.tensor([1.0, 2.0, 3.0], requires_grad = True)
b = torch.tensor([3.0, 4.0, 5.0], requires_grad = True)
c = torch.tensor([6.0, 7.0, 8.0], requires_grad = True)
y=3*a + 2*b*b + torch.log(c)
gradients = torch.FloatTensor([0.1, 1.0, 0.0001])
y.backward(gradients,retain_graph=True)
print(a.grad) # tensor([3.0000e-01, 3.0000e+00, 3.0000e-04])
print(b.grad) # tensor([1.2000e+00, 1.6000e+01, 2.0000e-03])
print(c.grad) # tensor([1.6667e-02, 1.4286e-01, 1.2500e-05])
Ich nahm an, dass unsere Funktion ist y=3*a + 2*b*b + torch.log(c)
und die Parameter Tensoren mit drei Elementen im Inneren sind.
Sie können sich vorstellen, gradients = torch.FloatTensor([0.1, 1.0, 0.0001])
dass dies der Akkumulator ist.
Wie Sie vielleicht hören, entspricht die Berechnung des PyTorch-Autograd-Systems dem Jacobian-Produkt.
Falls Sie eine Funktion haben, wie wir es getan haben:
y=3*a + 2*b*b + torch.log(c)
Jacobian wäre [3, 4*b, 1/c]
. Mit diesem Jacobi berechnet PyTorch jedoch nicht die Gradienten an einem bestimmten Punkt.
PyTorch verwendet die automatische Differenzierung im Vorwärts- und Rückwärtsmodus (AD) im .
Es gibt keine symbolische Mathematik und keine numerische Differenzierung.
Die numerische Differenzierung wäre zu berechnen δy/δb
, für b=1
und b=1+ε
wo ε klein ist.
Wenn Sie keine Farbverläufe verwenden in y.backward()
:
Beispiel 2
a = torch.tensor(0.1, requires_grad = True)
b = torch.tensor(1.0, requires_grad = True)
c = torch.tensor(0.1, requires_grad = True)
y=3*a + 2*b*b + torch.log(c)
y.backward()
print(a.grad) # tensor(3.)
print(b.grad) # tensor(4.)
print(c.grad) # tensor(10.)
Sie werden das Ergebnis an einem Punkt einfach bekommen, je nachdem , wie Sie setzten Ihre a
, b
, c
Tensoren zunächst.
Seien Sie vorsichtig , wie Sie initialisieren Ihre a
, b
, c
:
Beispiel 3:
a = torch.empty(1, requires_grad = True, pin_memory=True)
b = torch.empty(1, requires_grad = True, pin_memory=True)
c = torch.empty(1, requires_grad = True, pin_memory=True)
y=3*a + 2*b*b + torch.log(c)
gradients = torch.FloatTensor([0.1, 1.0, 0.0001])
y.backward(gradients)
print(a.grad) # tensor([3.3003])
print(b.grad) # tensor([0.])
print(c.grad) # tensor([inf])
Wenn Sie verwenden torch.empty()
und nicht verwenden pin_memory=True
, können Sie jedes Mal andere Ergebnisse erzielen.
Notenverläufe sind auch wie Akkumulatoren, also setzen Sie sie bei Bedarf auf Null.
Beispiel 4:
a = torch.tensor(1.0, requires_grad = True)
b = torch.tensor(1.0, requires_grad = True)
c = torch.tensor(1.0, requires_grad = True)
y=3*a + 2*b*b + torch.log(c)
y.backward(retain_graph=True)
y.backward()
print(a.grad) # tensor(6.)
print(b.grad) # tensor(8.)
print(c.grad) # tensor(2.)
Zuletzt einige Tipps zu Begriffen, die PyTorch verwendet:
PyTorch erstellt bei der Berechnung der Gradienten im Vorwärtsdurchlauf ein dynamisches Berechnungsdiagramm . Das sieht einem Baum sehr ähnlich.
So werden Sie oft die hören Blätter dieses Baumes sind Eingangs Tensoren und die Wurzel ist Ausgangs Tensor .
Farbverläufe werden berechnet, indem das Diagramm von der Wurzel bis zum Blatt verfolgt und jeder Farbverlauf mithilfe der Kettenregel multipliziert wird . Diese Multiplikation erfolgt im Rückwärtsdurchlauf.
Erläuterung
Bei neuronalen Netzen wird normalerweise
loss
bewertet, wie gut das Netzwerk gelernt hat, das Eingabebild (oder andere Aufgaben) zu klassifizieren. Derloss
Begriff ist normalerweise ein Skalarwert. Um die Parameter des Netzwerks zu aktualisieren, müssen wir den Gradienten vonloss
wrt zu den Parametern berechnen , der sich tatsächlichleaf node
im Berechnungsdiagramm befindet (diese Parameter sind übrigens meistens das Gewicht und die Vorspannung verschiedener Schichten wie Convolution, Linear und demnächst).Gemäß der Kettenregel
loss
können wir , um den Gradienten von wrt zu einem Blattknoten zu berechnen, die Ableitung vonloss
wrt einer Zwischenvariablen und den Gradienten von intermediärer Variable wrt zu der Blattvariablen berechnen, ein Punktprodukt erstellen und alle diese zusammenfassen.Die
gradient
Argumente einesVariable
‚sbackward()
Verfahren wird verwendet , um eine gewichtete Summe jedes Element einer Variable WRT dem CALCULATE Blatt Variable . Dieses Gewicht ist nur die Ableitung desloss
Endes jedes Elements der Zwischenvariablen.Ein konkretes Beispiel
Nehmen wir ein konkretes und einfaches Beispiel, um dies zu verstehen.
In dem obigen Beispiel wird das Ergebnis der ersten
print
ISDas ist genau die Ableitung von z_1 wrt zu x.
Das Ergebnis der zweiten
print
ist:Das ist die Ableitung von z_2 wrt zu x.
Wenn Sie nun ein Gewicht von [1, 1, 1, 1] verwenden, um die Ableitung von z wrt zu x zu berechnen, ist das Ergebnis
1*dz_1/dx + 1*dz_2/dx + 1*dz_3/dx + 1*dz_4/dx
. Kein Wunder also, dass die Ausgabe von 3rdprint
lautet:Es ist zu beachten, dass der Gewichtsvektor [1, 1, 1, 1] genau von
loss
wrt zu z_1, z_2, z_3 und z_4 abgeleitet ist. Die Ableitung vonloss
wrt tox
wird berechnet als:Die Ausgabe von 4th
print
ist also dieselbe wie die von 3rdprint
:quelle
gradient
Argument wirklich besser hätte erklären können . Danke für deine Antwort.[1, 1, 1, 1]
ist genau das Derivat vonloss
WRTz_1
,z_2
,z_3
undz_4
.“ Ich denke, diese Aussage ist wirklich der Schlüssel zur Antwort. Wenn man sich den Code des OP ansieht, ist ein großes Fragezeichen, woher diese willkürlichen (magischen) Zahlen für den Gradienten kommen. In Ihrem konkreten Beispiel halte ich es für sehr hilfreich, sofort auf die Beziehung zwischen dem[1, 0, 0 0]
Tensor und derloss
Funktion hinzuweisen, damit man sehen kann, dass die Werte in diesem Beispiel nicht willkürlich sind.loss = z.sum(dim=1)
, wird esloss = z_1 + z_2 + z_3 + z_4
. Wenn Sie einfacheloss
Berechnungen kennen, wissen Sie, dass die Ableitung von wrt toz_1, z_2, z_3, z_4
ist[1, 1, 1, 1]
.Normalerweise hat Ihr Rechengraph eine skalare Ausgabe, sagt
loss
. Dann können Sie den Gradientenloss
der Gewichte (w
) durch berechnenloss.backward()
. Wo das Standardargument vonbackward()
ist1.0
.Wenn Ihre Ausgabe mehrere Werte hat (z. B.
loss=[loss1, loss2, loss3]
), können Sie die Verlustgradienten für die Gewichte berechnenloss.backward(torch.FloatTensor([1.0, 1.0, 1.0]))
.Wenn Sie verschiedene Verluste mit Gewichten oder Wichtigkeiten versehen möchten, können Sie diese verwenden
loss.backward(torch.FloatTensor([-0.1, 1.0, 0.0001]))
.Dies bedeutet,
-0.1*d(loss1)/dw, d(loss2)/dw, 0.0001*d(loss3)/dw
gleichzeitig zu berechnen .quelle
grad_tensors
nicht darin besteht, sie unterschiedlich zu wiegen, sondern Gradienten für jedes Element der entsprechenden Tensoren sind.Hier ist die Ausgabe von forward (), dh y, ein 3-Vektor.
Die drei Werte sind die Gradienten am Ausgang des Netzwerks. Sie werden normalerweise auf 1,0 gesetzt, wenn y die endgültige Ausgabe ist, können aber auch andere Werte haben, insbesondere wenn y Teil eines größeren Netzwerks ist.
Zum Beispiel. Wenn x die Eingabe ist, ist y = [y1, y2, y3] eine Zwischenausgabe, die zur Berechnung der endgültigen Ausgabe verwendet wird. z,
Dann,
Hier sind also die drei Werte für rückwärts
und dann berechnet backward () dz / dx
quelle