Schrittweises Beispiel für die automatische Differenzierung im Rückwärtsmodus

27

Ich bin mir nicht sicher, ob diese Frage hierher gehört, aber sie hängt eng mit den Gradientenmethoden in der Optimierung zusammen, die hier offenbar zum Thema gehören. Sie können auf jeden Fall migrieren, wenn Sie der Meinung sind, dass eine andere Community über bessere Fachkenntnisse in diesem Thema verfügt.

Kurz gesagt, ich suche nach einem schrittweisen Beispiel für die automatische Differenzierung im umgekehrten Modus . Es gibt nicht viel Literatur zu diesem Thema und die bestehende Implementierung (wie die in TensorFlow ) ist schwer zu verstehen, ohne die Theorie dahinter zu kennen. Daher wäre ich sehr dankbar, wenn jemand im Detail zeigen könnte, was wir übergeben , wie wir es verarbeiten und was wir aus dem Rechengraphen herausnehmen.

Ein paar Fragen, mit denen ich am meisten Schwierigkeiten habe:

  • Samen - warum brauchen wir sie überhaupt?
  • Umgekehrte Differenzierungsregeln - Ich weiß, wie man vorwärts differenziert, aber wie gehen wir zurück? wissen wir im Beispiel aus diesem Abschnitt , dass ?w2¯=w3¯w1
  • Arbeiten wir nur mit Symbolen oder durchlaufen wir tatsächliche Werte ? Beispielsweise im gleichen Beispiel ist und Symbole oder Werte?¯ w iwiwi¯
freund
quelle
"Maschinelles Lernen zum Anfassen mit Scikit-Learn & TensorFlow" Anhang D enthält meiner Meinung nach eine sehr gute Erklärung. Ich empfehle es.
Augustin Barrachina

Antworten:

37

Nehmen wir an, wir haben den Ausdruck und wollen die Derivate und . Reverse-Mode-AD teilt diese Aufgabe in zwei Teile, nämlich Vorwärts- und Rückwärtsdurchläufe.z=x1x2+sin(x1)dzdx1dzdx2

Vorwärtspass

Zunächst zerlegen wir unseren komplexen Ausdruck in eine Menge primitiver Ausdrücke, dh Ausdrücke, die aus höchstens einem einzelnen Funktionsaufruf bestehen. Beachten Sie, dass ich die Eingabe- und Ausgabevariablen aus Konsistenzgründen auch umbenenne, obwohl dies nicht erforderlich ist:

w1=x1
w2=x2
w3=w1w2
w4=sin(w1)
w5=w3+w4
z=w5

Der Vorteil dieser Darstellung ist, dass Differenzierungsregeln für jeden einzelnen Ausdruck bereits bekannt sind. Zum Beispiel wissen wir , dass Ableitung von ist , und so . Wir werden diese Tatsache in umgekehrter Reihenfolge weiter unten verwenden.sincosdw4dw1=cos(w1)

Vorwärtsdurchlauf besteht im Wesentlichen darin, jeden dieser Ausdrücke auszuwerten und die Ergebnisse zu speichern. Angenommen, unsere Eingaben sind: und . Dann haben wir:x1=2x2=3

w1=x1=2
w2=x2=3
w3=w1w2=6
w4=sin(w1) =0.9
w5=w3+w4=6.9
z=w5=6.9

Pass umkehren

Dies ist, wo die Magie beginnt, und es beginnt mit der Kettenregel . In ihrer Grundform besagt die Kettenregel, dass, wenn Sie die Variable die von abhängt , die wiederum von abhängt , dann:t(u(v))uv

dtdv=dtdududv

oder, falls über mehrere Pfade / Variablen von abhängt , zB:tvui

u1=f(v)
u2=g(v)
t=h(u1,u2)

dann (siehe Beweis hier ):

dtdv=idtduiduidv

Wenn wir einen Endknoten und Eingabeknoten haben und der Weg von nach durch Zwischenknoten (dh wobei ), können wir eine Ableitung finden aszwizwiwpz=g(wp)wp=f(wi)dzdwi

dzdwi=pparents(i)dzdwpdwpdwi

Mit anderen Worten, um die Ableitung der Ausgabevariablen für eine Zwischen- oder Eingabevariable , müssen wir nur die Ableitungen ihrer Eltern und die Formel kennen, um die Ableitung des primitiven Ausdrucks zu berechnen .zwiwp=f(wi)

Der umgekehrte Durchlauf beginnt am Ende (dh ) und breitet sich rückwärts zu allen Abhängigkeiten aus. Hier haben wir (Ausdruck für "Samen"):dzdz

dzdz=1

Dies kann als "Änderung in führt zu genau der gleichen Änderung in " gelesen werden , was ziemlich offensichtlich ist.zz

Dann wissen wir, dass und so:z=w5

dzdw5=1

w5 hängt linear von und , also ist und . Unter Verwendung der Kettenregel finden wir:w3w4dw5dw3=1dw5dw4=1

dzdw3=dzdw5dw5dw3=1×1=1
dzdw4=dzdw5dw5dw4=1×1=1

Aus der Definition und den Regeln partieller Ableitungen ergibt sich . Somit:w3=w1w2dw3dw2=w1

dzdw2=dzdw3dw3dw2=1×w1=w1

Was, wie wir bereits aus dem Forward Pass wissen, ist:

dzdw2=w1=2

Schließlich trägt über und zu . Aus den Regeln partieller Ableitungen wissen wir wiederum, dass und . Somit:w1zw3w4dw3dw1=w2dw4dw1=cos(w1)

dzdw1=dzdw3dw3dw1+dzdw4dw4dw1=w2+cos(w1)

Und wieder können wir es bei bekannten Eingaben berechnen:

dzdw1=w2+cos(w1)=3+cos(2) =2.58

Da und nur Aliase für und , erhalten wir unsere Antwort:w1w2x1x2

dzdx1=2.58
dzdx2=2

Und das ist es!


Diese Beschreibung betrifft nur skalare Eingaben, dh Zahlen, kann jedoch auch auf mehrdimensionale Arrays wie Vektoren und Matrizen angewendet werden. Zwei Dinge, die man beachten sollte, wenn man Ausdrücke mit solchen Objekten unterscheidet:

  1. Derivate können eine viel höhere Dimensionalität aufweisen als Eingaben oder Ausgaben, z. B. ist die Ableitung des Vektors in Bezug auf den Vektor eine Matrix und die Ableitung der Matrix in Bezug auf die Matrix ein 4-dimensionales Array (manchmal als Tensor bezeichnet). In vielen Fällen sind solche Derivate sehr spärlich.
  2. Jede Komponente im Ausgangsarray ist eine unabhängige Funktion von einer oder mehreren Komponenten des Eingangsarrays. Wenn z. B. und sowohl als auch Vektoren sind, hängt niemals von , sondern nur von der Teilmenge von . Dies bedeutet insbesondere, dass das Finden der Ableitung , zu verfolgen, wie von abhängt .y=f(x)xyyiyjxkdyidxjyixj

Die Fähigkeit der automatischen Unterscheidung besteht darin, dass sie mit komplizierten Strukturen aus Programmiersprachen wie Bedingungen und Schleifen umgehen kann. Wenn Sie jedoch nur algebraische Ausdrücke benötigen und das Framework für die Arbeit mit symbolischen Darstellungen ausreicht, können Sie vollständig symbolische Ausdrücke erstellen. Tatsächlich könnten wir in diesem Beispiel den Ausdruck erzeugen und diese Ableitung für beliebige Eingaben berechnen.dzdw1=w2+cos(w1)=x2+cos(x1)

freund
quelle
1
Sehr nützliche Frage / Antwort. Vielen Dank. Nur eine kleine Kritik: Sie scheinen sich auf einer Baumstruktur zu bewegen, ohne es zu erklären (dann fangen Sie an, über Eltern usw. zu sprechen)
MadHatter
1
Auch wird es nicht schaden zu klären, warum wir Samen brauchen.
MadHatter
@ MadHatter danke für den Kommentar. Ich habe versucht, einige Absätze (die sich auf Eltern beziehen) neu zu formulieren, um eine Diagrammstruktur hervorzuheben. Ich habe dem Text auch "seed" hinzugefügt, obwohl dieser Name meiner Meinung nach irreführend sein kann: In AD ist seed immer ein fester Ausdruck - , nicht etwas, das Sie auswählen oder generieren können. dzdz=1
Freundin
Vielen Dank! Mir ist aufgefallen, wenn Sie mehr als einen "Startwert" einstellen müssen, in der Regel wählt man 1 und 0. Ich würde gerne wissen, warum. Ich meine, man nimmt den "Quotienten" eines Differentials selbst, also ist "1" zumindest intuitiv gerechtfertigt. Aber was ist mit 0? Und wenn man mehr als 2 Samen pflücken muss?
MadHatter
1
Soweit ich weiß, wird nur im Vorwärtsmodus AD mehr als ein Startwert verwendet. In diesem Fall setzen Sie den Startwert für eine zu unterscheidende Eingabevariable auf 1 und den Startwert für alle anderen Eingabevariablen auf 0, damit sie nicht zum Ausgabewert beitragen. Im umgekehrten Modus setzen Sie den Startwert auf eine Ausgabevariable und haben normalerweise nur eine Ausgabevariable. Sie können eine AD-Pipeline im umgekehrten Modus mit mehreren Ausgabevariablen erstellen und alle bis auf eins auf 0 setzen, um den gleichen Effekt wie im Vorwärtsmodus zu erzielen. Diese Option habe ich jedoch noch nie untersucht.
Freund