ist nicht eine weitere Erklärung der Kettenregel. Es ist eine Tour durch die weird Seite von Autograd – wo Gradienten Physik dienen, nicht nur Gewichte

Ich habe dieses Tutorial ursprünglich im ersten Jahr meines Doktoranden für mich selbst geschrieben und in Pytorch durch die Feinheiten von Gradientenberechnungen navigiert. Das meiste davon ist eindeutig mit Blick auf die Normal -Backpropagation konzipiert – und das ist in Ordnung, da das die meisten Menschen das brauchen.

Aber physikalisches neuronales Netzwerk (PINN) ist ein launisches Tier und braucht eine andere Artwork von Gradientenlogik. Ich habe einige Zeit damit verbracht, es zu füttern und dachte, es könnte sich lohnen, die Ergebnisse mit der Group zu teilen, insbesondere mit anderen Pinn -Praktizierenden – vielleicht spart es jemandem ein paar Kopfschmerzen. Aber wenn Sie noch nie von Pinns gehört haben, machen Sie sich keine Sorgen! Dieser Beitrag ist immer noch für Sie – besonders wenn Sie sich mit Dingen wie Gradienten und all dem lustigen Zeug befassen.

Grundlagen

Tensor In der Computerwelt bedeutet einfach ein mehrdimensionales Array, dh eine Reihe von Zahlen, die von einer oder mehreren Ganzzahlen indiziert werden. Um genau zu sein, gibt es auch nulldimensionale Tensoren, die nur einzelne Zahlen sind. Einige Leute sagen, dass Tensoren eine Verallgemeinerung von Matrizen auf mehr als zwei Dimensionen sind.

Wenn Sie bereits allgemeine Relativitätstheorie studiert haben, haben Sie vielleicht gehört, dass mathematische Tensoren solche Dinge wie kovariante und kontravariante Indizes haben. Aber vergessen Sie es – in Pytorch -Tensoren sind nur mehrdimensionale Arrays. Keine Finesse hier.

Blattzensor ist ein Tensor, der ein Blatt (im Sinne einer Graphentheorie) eines Berechnungsdiagramms ist. Wir werden uns diese unten ansehen, daher wird diese Definition etwas sinnvoller sein.

Der requires_grad Die Eigenschaft eines Tensors sagt Pytorch, ob er sich daran erinnern sollte, wie dieser Tensor in weiteren Berechnungen verwendet wird. Denken Sie vorerst an Tensoren mit requires_grad=True als Variablen, während Tensoren mit requires_grad=False als Konstanten.

Blatttensoren

Beginnen wir damit, ein paar Tensoren zu erstellen und ihre Eigenschaften zu überprüfen requires_grad Und is_leaf.

import torch

a = torch.tensor((3.), requires_grad=True)
b = a * a

c = torch.tensor((5.))
d = c * c

assert a.requires_grad is True and a.is_leaf is True
assert b.requires_grad is True and b.is_leaf is False
assert c.requires_grad is False and c.is_leaf is True
assert d.requires_grad is False and d.is_leaf is True  # sic!
del a, b, c, d

a ist wie erwartet ein Blatt und b ist nicht daran, dass es ein Ergebnis einer Multiplikation ist. a ist so eingestellt, dass er einen Abschluss benötigt, additionally natürlich b erbt diese Eigenschaft.

c ist offensichtlich ein Blatt, aber warum d Ist ein Blatt? Der Grund d.is_leaf ist wahr aus einer bestimmten Konvention: alle Tensoren mit requires_grad auf false eingestellt werden als Blatttensoren wie per als Pytorchs Dokumentation:

Alle Tensoren, die haben requires_grad das ist False wird durch Konvention Blatttensoren sein.

Während mathematisch, d ist kein Blatt (da es sich aus einer anderen Operation ergibt, c * c), Gradientenberechnung wird sich niemals über sie erstrecken. Mit anderen Worten, es wird kein Derivat in Bezug auf c. Dies erlaubt d als Blatt behandelt werden.

Kurz gesagt, in Pytorch sind Blatttenoren beider:

  • Direkt eingegeben (dh nicht von anderen Tensoren berechnet) und haben requires_grad=True. Beispiel: Neuronale Netzwerkgewichte, die zufällig initialisiert werden.
  • Erfordern Sie überhaupt keine Gradienten, unabhängig davon, ob sie direkt eingegeben oder berechnet werden. In den Augen von Autograd sind dies nur Konstanten. Beispiele:
    • Alle neuronalen Netzwerkeingabedaten,
    • Ein Eingabebild nach mittlerer Entfernung oder anderen Operationen, bei denen nur Tensoren für nicht-Gradient-Erregung beteiligt sind.

Eine kleine Bemerkung für diejenigen, die mehr wissen wollen. Der requires_grad Eigenschaft wird wie hier illustriert vererbt:

a = torch.tensor((5.), requires_grad=True)
b = torch.tensor((5.), requires_grad=True)
c = torch.tensor((5.), requires_grad=False)

d = torch.sin(a * b * c)

assert d.requires_grad == any((x.requires_grad for x in (a, b, c)))

Code-Bemerkung: Alle Code-Snippets sollten in sich geschlossen werden, mit Ausnahme von Importen, die ich nur dann einbezieht, wenn sie zum ersten Mal erscheinen. Ich lasse sie fallen, um den Boilerplate -Code zu minimieren. Ich vertraue darauf, dass der Leser in der Lage sein wird, sich leicht um diese zu kümmern.

Abschlussretention

Ein separates Downside ist die Gradientenaufbewahrung. Alle Knoten im Berechnungsdiagramm, dh alle verwendeten Tensoren, haben Gradienten berechnet, wenn sie einen Abschluss benötigen. Es behalten jedoch nur Blatttensoren diese Gradienten. Dies ist sinnvoll, da Gradienten normalerweise zum Aktualisieren von Tensoren verwendet werden und nur Blatttensoren während des Trainings aktualisiert werden. Nicht-Blatt-Tensoren wie b Im ersten Beispiel werden nicht direkt aktualisiert; Sie verändern sich infolge von Änderungen in aso können ihre Gradienten verworfen werden. Es gibt jedoch Szenarien, insbesondere in physikalischen neuronalen Netzwerken (PINNs), in denen Sie möglicherweise die Gradienten dieser Zwischenzensoren beibehalten möchten. In solchen Fällen müssen Sie explizit Nicht-Blatt-Tensoren markieren, um ihre Gradienten zu halten. Mal sehen:

a = torch.tensor((3.), requires_grad=True)
b = a * a
b.backward()

assert a.grad shouldn't be None
assert b.grad is None  # generates a warning

Sie haben wahrscheinlich gerade eine Warnung gesehen:

UserWarning: The .grad attribute of a Tensor that's not a leaf Tensor is being 
accessed. Its .grad attribute will not be populated throughout autograd.backward(). 
Should you certainly need the .grad area to be populated for a non-leaf Tensor, use 
.retain_grad() on the non-leaf Tensor. Should you entry the non-leaf Tensor by 
mistake, be sure to entry the leaf Tensor as an alternative. 
See github.com/pytorch/pytorch/pull/30531 for extra informations. 
(Triggered internally at atensrcATen/core/TensorBody.h:491.)

Lasst es uns additionally beheben, indem wir erzwingen b seinen Gradienten beibehalten

a = torch.tensor((3.), requires_grad=True)
b = a * a
b.retain_grad()  # <- the distinction
b.backward()

assert a.grad shouldn't be None
assert b.grad shouldn't be None

Geheimnisse von Grad

Schauen wir uns nun den berühmten Absolventen selbst an. Was ist das? Ist es ein Tensor? Wenn ja, ist es ein Blatt -Tensor? Benötigt oder behält es Grad?

a = torch.tensor((3.), requires_grad=True)
b = a * a
b.retain_grad()
b.backward()

assert isinstance(a.grad, torch.Tensor)
assert a.grad.requires_grad is False and a.grad.retains_grad is False and a.grad.is_leaf is True
assert b.grad.requires_grad is False and b.grad.retains_grad is False and b.grad.is_leaf is True

Scheinbar:

– – Grad selbst ist ein Tensor,
– – Grad ist ein Blattzensor,
– – Grad erfordert keinen Grad.

Beibehält es Grad? Diese Frage macht keinen Sinn, da sie überhaupt nicht einen Abschluss erfordert. Wir werden in einer Sekunde auf die Frage zurückkommen, dass der Abschluss ein Blatt -Tensor ist, aber jetzt werden wir ein paar Dinge testen.

Mehrfach rückwärts und retain_graph

Was wird passieren, wenn wir den gleichen Grad zweimal berechnen?

a = torch.tensor((3.), requires_grad=True)
b = a * a
b.retain_grad()
b.backward()
attempt:
    b.backward()
besides RuntimeError:
    """
    RuntimeError: Making an attempt to backward via the graph a second time (or 
    immediately entry saved tensors after they've already been freed). Saved 
    intermediate values of the graph are freed whenever you name .backward() or 
    autograd.grad(). Specify retain_graph=True if you have to backward via 
    the graph a second time or if you have to entry saved tensors after 
    calling backward.
    """

Die Fehlermeldung erklärt alles. Dies sollte funktionieren:

a = torch.tensor((3.), requires_grad=True)
b = a * a
b.retain_grad()

b.backward(retain_graph=True)
print(a.grad)  # prints tensor((6.))

b.backward(retain_graph=True)
print(a.grad)  # prints tensor((12.))

b.backward(retain_graph=False)
print(a.grad)  # prints tensor((18.))

# b.backward(retain_graph=False)  # <- right here we'd get an error, as a result of in 
# the earlier name we didn't retain the graph.

Seite (aber wichtig) Hinweis: Sie können auch beobachten, wie sich der Gradient ansammelt a: Mit jeder Iteration wird es hinzugefügt.

Kraftvoll create_graph Argument

Wie erfordern ich einen Abschluss?

a = torch.tensor((5.), requires_grad=True)
b = a * a
b.retain_grad()
b.backward(create_graph=True)

# Right here an fascinating factor occurs: now a.grad would require grad! 
assert a.grad.requires_grad is True
assert a.grad.is_leaf is False

# Then again, the grad of b doesn't require grad, as beforehand. 
assert b.grad.requires_grad is False
assert b.grad.is_leaf is True

Das obige ist sehr nützlich: a.grad Was mathematisch ( frac { partial b} { partial a} ) ist, ist keine Konstante (Blatt), sondern ein reguläres Mitglied des Rechendiagramms, das weiter verwendet werden kann. Wir werden diese Tatsache in Teil 2 verwenden.

Warum das b.grad erfordert keinen Abschluss? Weil Ableitung von b in Bezug auf b ist einfach 1.

Wenn der backward Fühlt sich jetzt nicht intuitiv für Sie, mach dir keine Sorgen. Wir werden bald zu einer anderen Methode namens Nomen Omen wechseln grad Dies ermöglicht es, die Zutaten der Derivate genau zu wählen. Vor zuvor zwei Seitennoten:

Randnotiz 1: Wenn Sie festlegen create_graph Für wahr ist es auch festgelegt retain_graph zu wahr (wenn nicht explizit eingestellt). Im Pytorch -Code sieht es genau so aus wie
Das:

    if retain_graph is None:
        retain_graph = create_graph

Randnotiz 2: Sie haben wahrscheinlich eine solche Warnung gesehen:

    UserWarning: Utilizing backward() with create_graph=True will create a reference 
    cycle between the parameter and its gradient which may trigger a reminiscence leak. 
    We advocate utilizing autograd.grad when creating the graph to keep away from this. If 
    it's important to use this perform, be certain that to reset the .grad fields of your 
    parameters to None after use to interrupt the cycle and keep away from the leak. 
    (Triggered internally at C:cbpytorch_1000000000000worktorchcsrcautogradengine.cpp:1156.)
      Variable._execution_engine.run_backward(  # Calls into the C++ engine to 
    run the backward cross

Und wir werden den Rat folgen und verwenden autograd.grad Jetzt.

Ableitungen mit autograd.grad Funktion

Lassen Sie uns nun von der irgendwie hohen Ebene bewegen .backward() Methode zur niedrigeren Ebene grad Methode, die die Ableitung eines Tensors in Bezug auf einen anderen explizit berechnet.

from torch.autograd import grad

a = torch.tensor((3.), requires_grad=True)
b = a * a * a
db_da = grad(b, a, create_graph=True)(0)
assert db_da.requires_grad is True

In ähnlicher Weise wie bei backwarddie Ableitung von b in Bezug auf a kann als Funktion behandelt und weiter differenziert werden. Additionally mit anderen Worten die create_graph Die Flagge kann verstanden werden als: Halten Sie bei der Berechnung von Gradienten die Geschichte, wie sie berechnet wurden, damit sie als Nicht-Blatt-Tensoren behandeln, die Grad erfordern, und nutzen Sie weiter.

Insbesondere können wir Ableitung zweiter Ordnung berechnen:

d2b_da2 = grad(db_da, a, create_graph=True)(0)
# Facet be aware: the grad perform returns a tuple and the primary component of it's what we want.
assert d2b_da2.merchandise() == 18
assert d2b_da2.requires_grad is True

Wie bereits erwähnt: Dies ist eigentlich die wichtigste Eigenschaft, die es uns ermöglicht, mit Pytorch zu pinnieren.

Einpacken

Die meisten Tutorials über Pytorch -Gradienten konzentrieren sich auf die Backpropagation im klassischen überwachten Lernen. Dieser untersuchte eine andere Perspektive-eine, die von den Bedürfnissen von Pinns und anderen gradientenhungrigen Tieren geprägt ist.

Wir haben gelernt, welche Blätter im Pytorch -Dschungel sind, warum Gradienten nur für Blattknoten standardmäßig aufbewahrt werden und wie sie bei Bedarf für andere Tensoren beibehalten können. Wir haben gesehen, wie create_graph verwandelt Gradienten in differenzierbare Bürger der Autograd -Welt.

Es gibt jedoch noch viele Dinge zu entdecken-insbesondere, warum Gradienten nicht-Scalarar-Funktionen zusätzliche Sorgfalt erfordern, wie man Ableitungen zweiter Ordnung berechnet, ohne Ihren gesamten RAM zu verwenden elementweise Gradient.

Treffen wir uns additionally in Teil 2, wo wir uns genauer ansehen werden grad 👋

Von admin

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert