Zum Inhalt springen

Automatisches Differenzieren

aus Wikipedia, der freien Enzyklopädie

Das automatische Differenzieren bzw. Differenzieren von Algorithmen ist ein Verfahren der Informatik und angewandten Mathematik. Zu einer Funktion in mehreren Variablen, die als Prozedur in einer Programmiersprache oder als Berechnungsgraph gegeben ist, wird eine erweiterte Prozedur erzeugt, die sowohl die Funktion als auch einen oder beliebig viele Gradienten bis hin zur vollen Jacobi-Matrix auswertet. Wenn das Ausgangsprogramm Schleifen enthält, darf die Anzahl der Schleifendurchläufe nicht von den unabhängigen Variablen abhängig sein.

Diese Ableitungen werden z. B. für das Lösen von nichtlinearen Gleichungssystemen mittels Newton-Verfahren und für Methoden der nichtlinearen Optimierung benötigt.

Das wichtigste Hilfsmittel dabei ist die Kettenregel sowie die Tatsache, dass zu den im Computer verfügbaren Elementarfunktionen wie sin, cos, exp, log die Ableitungen bekannt und genauso exakt berechenbar sind. Damit wird der Aufwand zur Berechnung der Ableitungen proportional (mit kleinem Faktor) zum Aufwand der Auswertung der Ausgangsfunktion.

Berechnung von Ableitungen

Aufgabe: Gegeben sei eine Funktion

<math>f\colon\R^n \to \R^m, x \mapsto y</math>

Gesucht ist der Code/die Funktion für Richtungsableitungen oder die volle Jacobi-Matrix

<math>\frac{\partial f}{\partial x}=\left[ \tfrac{\partial y_i}{\partial x_j} \right]_{i=1..m, j=1..n}</math>

Verschiedene Ansätze hierfür sind:

  1. Versuche, eine geschlossene, analytische Form für f zu finden und bestimme <math>\tfrac{\partial f}{ \partial x}</math> durch Differentiation „auf Papier“. Implementiere dann den Code für <math>\tfrac{\partial f}{\partial x}</math> von Hand.
    Problem: Zu schwierig, zeitaufwendig, fehleranfällig
    Vorteile: sehr effizient, hohe Genauigkeit
  2. Erzeuge die Berechnungsvorschrift für f in einem Computeralgebrasystem und wende die dort zur Verfügung stehenden Mittel zum symbolischen Differenzieren an. Exportiere dann den Code für <math>\tfrac{\partial f }{ \partial x}</math> in seine eigentliche Umgebung.
    Problem: Zeitaufwendig, skaliert nicht, zu kompliziert für größere Programme/Funktionen
  3. Bestimme eine numerische Approximation der Ableitung. Es gilt für kleines h
    <math>\tfrac{\partial f_k}{\partial x} = \lim_{h\to 0} \frac{f_k(x+h)-f_k(x)}{h} \approx \frac{f_k(x+h)-f_k(x)}{h}</math>.
    Problem: Wahl der optimalen Schrittweite h, ungenau, eventuell Instabilität
    Vorteil: einfache Berechnung
  4. Stelle die Berechnungsvorschrift als Berechnungsbaum, d. h. als arithmetisches Netzwerk, dar und erweitere diesen unter Verwendung der Kettenregel zu einem Berechnungsbaum für Funktionswert und Ableitung <math>\tfrac{\partial f}{\partial x}</math>.

Die Idee der automatischen Differentiation (AD)

Eine Funktion <math>f(x) \colon \R^n \to \R^m, x \mapsto y</math> kann als eine Verkettung elementarer Teil-Funktionen beschrieben werden. Automatische Differentiation (AD) berechnet die Ableitung als Verkettung partieller Ableitungen. AD benötigt daher den Wert und die Ableitung jeder Teil-Funktion als Zwischenergebnis. Dem Zwischenwert jeder Teil-Funktion wird nachfolgend eine Variable <math>(t_1,t_2,t_3,\dots)</math> zugewiesen. Man kann sich dies so vorstellen, dass es eine (potentiell unendliche) Folge von Zwischenwerten <math>(t_1,t_2,t_3,\dots)</math> gibt und Funktionen <math>q_k \colon \R^{n+k-1}\to\R</math>, die aber nur von ein oder zwei Variablen wirklich abhängen. Die Funktion wird ausgewertet, indem am Anfang <math>(t_1,t_2,\dots,t_n)=(x_1,x_2,\dots,x_n)</math> gesetzt wird und nacheinander

<math>

\begin{align} t_{n+1} & = q_1(t_1,\dots,t_n)\\ t_{n+2} & = q_2(t_1,\dots,t_n,t_{n+1})\\

       &\vdots\\

t_{n+K} & = q_K(t_1,\dots,t_n,t_{n+1},\dots,t_{K-1}) \end{align} </math> bestimmt wird. Dies kann so eingerichtet werden, dass die Funktionswerte von f sich in den zuletzt ausgewerteten Zwischenergebnissen befinden, d. h. am Ende wird noch <math>(y_1,\dots,y_m)=(t_{K-m+1},\dots,t_K)</math> zugeordnet.

AD beschreibt eine Menge von Verfahren, deren Ziel es ist, ein neues Programm zu erzeugen, das die Jacobimatrix von f, <math>J=\tfrac{\partial f}{\partial x}\in \mathbb{R}^{m\times n}</math> auswertet. Die Eingabevariablen x heißen unabhängige Variablen, die Ausgabevariable(n) y abhängige Variablen. Bei AD unterscheidet man mindestens zwei verschiedene Modi.

  1. Vorwärtsmodus (engl. Forward Mode)
  2. Rückwärtsmodus (engl. Reverse Mode)

Beide Modi basieren auf der Kettenregel, wonach die Ableitung einer Funktion sich als Verkettung partieller Ableitungen darstellen lässt: <math>\frac{\partial f(g)}{\partial x}=\frac{\partial f(g)}{\partial g}\cdot \frac{\partial g}{\partial x}</math>. Der Vorwärtsmodus beginnt mit der inneren Ableitung <math>\frac{\partial g}{\partial x}</math>, der Rückwärtsmodus mit der äußeren <math>\frac{\partial f(g)}{\partial g}</math>. Der Wert der partiellen Ableitung, genannt Saat (engl. seed), wird jeweils vor- bzw. zurückpropagiert und ist initial <math>\frac{\partial x}{\partial x}=1</math> bzw. <math>\frac{\partial f(g)}{\partial f(g)}=1</math>. Der Vorwärtsmodus wertet im selben Schritt die Funktion aus und berechnet die Ableitung bzgl. einer unabhängigen Variablen. Für jede unabhängige Variable <math>x_1,x_2,\dots,x_n</math> ist daher ein eigener Schritt nötig, in dem die Ableitung bzgl. einer unabhängigen Variable auf eins (<math>\frac{\partial x_1}{\partial x_1}=1</math>) und aller anderen auf null (<math>\frac{\partial x_2}{\partial x_1}= \dots = \frac{\partial x_n}{\partial x_1} = 0</math>) gesetzt wird. Im Gegensatz dazu benötigt der Rückwärtsmodus für die partiellen Ableitungen die ausgewerteten Teil-Funktionen. Der Rückwärtsmodus wertet daher die Funktion zuerst aus und berechnet die Ableitungen bzgl. aller unabhängiger Variablen in einem zusätzlichen Rückwärtsschritt.

Vorwärtsmodus

Datei:ForwardAD.png
Automatisches Differenzieren im Vorwärtsmodus

Im Vorwärtsmodus werden die partiellen Ableitungen entlang des Kontrollflusses der Berechnung von f transportiert, also von der innersten zur äußersten partiellen Ableitung.

Beispiel

Berechne eine Funktion <math> f(x_1,x_2,a)=(x_1+x_2) \cdot a \cdot \sin(x_1+x_2) = y_2</math>

 <math>
\begin{align}
 & [y_0, y_1 , y_2 ] = f\left(x_1, x_2, a\right) \left\{\right.\\
 & \quad y_0=x_1+x_2 \\
 & \quad y_1=a \cdot \sin(y_0) \\
 & \quad y_2=y_0 \cdot y_1 \\
 & \left.\right\}
\end{align}
  </math>

Eine automatische Differentiation im Vorwärtsmodus hätte eine Funktion

 <math>[y_0, y_1, y_2, y_0', y_1', y_2'] = f_{AD}\left( x_1, x_2, x_1', x_2', a\right)</math>

zum Ergebnis, die den Wert der inneren Ableitung von <math>x_1,x_2</math> (<math>x_1',x_2'</math>) erwartet und die Ableitung von <math>y_0, y_1,y_2</math> (<math>y_0',y_1',y_2'</math>) zurückgibt:

 <math>\begin{align}
& [y_0, y_1, y_2, y_0', y_1', y_2'] = f_{AD}\left( x_1, x_2, x_1', x_2', a\right) \left\{\right. \\
& \quad y_0=x_1+x_2 \\
& \quad y_0'=x_1'+ x_2' \\
& \quad y_1=a \cdot \sin(y_0) \\
& \quad y_1'=a\cdot \cos(y_0) \cdot y_0' \\
& \quad y_2 = y_0 \cdot y_1 \\
& \quad y_2' = y_0' \cdot y_1 + y_0 \cdot y_1' \\
& \left.\right\}

\end{align}

 </math>

Um die Ableitung <math>\frac{\partial y_2}{\partial x_1}</math> zu berechnen, muss <math>\frac{\partial x_1}{\partial x_1}=x_1'=1</math> und <math>\frac{\partial x_2}{\partial x_1}=x_2'=0</math> gesetzt werden und entsprechend für <math>\frac{\partial y_2}{\partial x_2}</math> dann <math>\frac{\partial x_1}{\partial x_2}=x_1'=0</math> und <math>\frac{\partial x_2}{\partial x_2}=x_2'=1</math>.

Deutlich intuitiver ist, die Funktion rekursiv anhand ihrer Teilfunktionen zu berechnen. Dabei gibt der modifizierte Funktionsaufruf ein Zweier-Tupel aus dem Wert der Funktion und der partiellen Ableitung zurück und erwartet als Argument den Wert aller Variablen und der partiellen Ableitung aller unabhängigen Variablen:

 <math>

\begin{align} (y_0,y_0')&=y_{0_{AD}}(x_1,x_2,x_1',x_2') = ( x_1+x_2, x_1'+x_2'), \\ (y_1,y_1')&=y_{1_{AD}}(a,y_0,y_0') = (a \cdot \sin(y_0), a\cdot \cos(y_0) \cdot y_0'),\\ (y_2 , y_2')&= y_{2_{AD}}(y_0,y_1,y_0',y_2)= (y_0 \cdot y_1, y_0' \cdot y_1 + y_0 \cdot y_1').\\ \end{align}

 </math>

Pseudocode

Der Vorwärtsmodus berechnet die Funktion und die Ableitung (aber jeweils nur für eine unabhängige Variable) in einem Schritt. Der zugehörige Methodenaufruf erwartet den abzuleitenden Ausdruck Z und die Variable V, nach der abgeleitet wird. Die Methode gibt ein Wertepaar aus der ausgewerteten Funktion und der zugehörigen Ableitung zurück. Die Methode arbeitet den Ausdrucksbaum rekursiv ab, bis eine Variable erreicht wird. Ist das die Variable, nach der abgeleitet wird, so ist deren Ableitung 1, 0 anderenfalls. Anschließend werden die partielle Funktion sowie die partielle Ableitung ausgewertet.<ref name=demm22>{{#invoke:Vorlage:Literatur|f}}{{#if:

       | {{#if: Vorlage:Cite book/ParamBool
               | Vorlage:Toter Link/archivebot
               | Vorlage:Webarchiv/archiv-bot
         }}
  }}{{#invoke:TemplatePar|check
   |all    = title=
   |opt    = vauthors= author= author1= authorlink= author-link= author-link1= author1-link= author2= author3= author4= author5= author6= author7= author8= author9= editor= last= first= last1= first1= last2= first2= last3= first3= last4= first4= last5= first5= last6= first6= last7= first7= last8= first8= last9= first9= last10= first10= last11= first11= last12= first12= last13= first13= last14= first14= last15= first15= others= script-title= trans-title= date= year= volume= issue= number= series= page= pages= at= issn= arxiv= bibcode= doi= pmid= pmc= jstor= oclc= id= url= url-status= format= access-date= archive-date= archive-url= archivebot= offline= location= publisher= language= quote= work= journal= newspaper= magazine= periodical=  name-list-style= url-access= doi-access= display-authors= via= s2cid= mr= type= citeseerx=  accessdate= archivedate= archiveurl= coauthors= month= day= last16= first16= last17= first17= last18= first18= last19= first19= last20= first20= last21= first21= last22= first22= last23= first23= last24= first24= last25= first25= last26= first26= last27= first27= last28= first28= last29= first29= last30= first30= last31= first31=
   |cat      = Wikipedia:Vorlagenfehler/Vorlage:Cite journal
   |errNS    = 0
   |template = Vorlage:Cite journal
   |format   = 
   |preview  = 1
  }}Vorlage:Cite book/URL{{#if:  | Vorlage:Cite book/Meldung }}{{#if:        | Vorlage:Cite book/Meldung }}{{#if: DEEM '22: Proceedings of the Sixth Workshop on Data Management for End-To-End Machine Learning
     || Vorlage:Cite book/Meldung
  }}{{#if: Vorlage:Cite book/ParamBool
        | Vorlage:Cite book/Meldung
  }}{{#if: Vorlage:Cite book/ParamBool
     | Vorlage:Cite book/Meldung
  }}{{#if: Vorlage:Cite book/ParamBool
     | Vorlage:Cite book/Meldung
  }}{{#if: Vorlage:Cite book/ParamBool
     | Vorlage:Cite book/Meldung
  }}{{#if: Vorlage:Cite book/ParamBool
       | Vorlage:Cite book/Meldung
  }}{{#if: Vorlage:Cite book/ParamBool
     | Vorlage:Cite book/Meldung
  }}Vorlage:Cite book/Meldung2{{#ifexpr: 0{{#ifeq:^^|^^||+1}}{{#ifeq:^^|^^||+1}}{{#ifeq:Maximilian E. Schüle, Maximilian Springer, Alfons Kemper, Thomas Neumann,|^^||+1}}{{#ifeq:^^|^^||+1}} > 1
    | Vorlage:Cite book/Meldung
  }}</ref>

<syntaxhighlight lang="C++"> tuple<float,float> auswerten(Ausdruck Z, Ausdruck V) {

  if istVariable(Z)
     if (Z=V) return {wertVon(Z),1};
     else return {wertVon(Z),0};
  else if (Z = X + Y)
     {x,x'} = auswerten(X,V);
     {y,y'} = auswerten(Y,V);
     return {x+y, x'+y'};
  else if (Z = X - Y)
     {x,x'} = auswerten(X,V);
     {y,y'} = auswerten(Y,V);
     return {x-y, x'-y'};
  else if (Z = X * Y)
     {x,x'} = auswerten(X,V);
     {y,y'} = auswerten(Y,V);
     return {x*y, x'*y+x*y'};

} </syntaxhighlight>

Rückwärtsmodus

Datei:AutoDiff.webp
Automatisches Differenzieren im Rückwärtsmodus
Datei:AutoDiffBeispiel.webp
Beispiel für automatisches Differenzieren im Rückwärtsmodus

Der Rückwärtsmodus besteht aus zwei Phasen.

  1. Das Originalprogramm wird ausgeführt und die Werte der ausgewerteten Teil-Funktionen zwischengespeichert.
  2. Das Originalprogramm wird rückwärts ausgeführt. Dabei werden die äußeren partiellen Ableitungen zur Berechnung der inneren verwendet. Dabei werden die Werte aus Phase 1 verwendet.

Beispiel

Zuerst werden die Teil-Funktionen der Funktion <math> f(x_1,x_2,a)=(x_1+x_2) \cdot a \cdot \sin(x_1+x_2) = y_2</math> ausgewertet:

<math>

\begin{align} y_0 & =x_1+x_2 & & (1)\\ y_1 & =a\cdot\sin(y_0)& & (2)\\ y_2 & =y_0\cdot y_1& & (3)\\ \end{align} </math>

Anschließend wird die äußerste partielle Ableitung <math>\frac{\partial y_2}{\partial y_1}</math> <math>(4)</math> gebildet, um daraus die inneren Ableitungen zu berechnen. Für die Ableitung <math>\frac{\partial y_2}{\partial y_0}</math> muss man berücksichtigen, dass <math>y_0</math> in <math>(2)</math> und <math>(3)</math> vorkommt: aus <math>(2)</math> stammt der Teil <math>y_0 \cdot (a \cdot \cos(y_0))</math>, aus <math>(3)</math> der Teil <math>y_1</math>, die beide addiert werden.

<math>

\begin{align} \frac{\partial y_2}{\partial y_1} &= \frac{\partial y_2}{\partial y_2} \cdot \frac{\partial y_2}{\partial y_1} = 1 \cdot y_0 = y_0 & (4) \\ \frac{\partial y_2}{\partial y_0} &= \frac{\partial y_2}{\partial y_1} \cdot \frac{\partial y_1}{\partial y_0} = y_0 \cdot (a \cdot \cos(y_0)) + y_1 & (5) \\ \frac{\partial y_2}{\partial x_1} &= \frac{\partial y_2}{\partial y_0} \cdot \frac{\partial y_0}{\partial x_1} = (y_0 \cdot (a \cdot \cos(y_0))+ y_1) \cdot 1 & (6) \\ \frac{\partial y_2}{\partial x_2} &= \frac{\partial y_2}{\partial y_0} \cdot \frac{\partial y_0}{\partial x_2} = (y_0 \cdot (a \cdot \cos(y_0))+ y_1) \cdot 1 & (7) \\ \end{align} </math> Aufgrund der Distributivität könnte man <math>x_1,x_2,y_0</math> in <math>x_1=x_{11}+x_{12}</math>, <math>x_2=x_{21}+x_{22}</math> und <math>y_{01}=x_{11}+x_{21}, y_{02}=x_{12}+x_{22}</math> aufteilen mit <math>\frac{\partial y_2}{\partial x_1}=\frac{\partial y_2}{\partial x_{11}} + \frac{\partial y_2}{\partial x_{12}}, \frac{\partial y_2}{\partial x_2}=\frac{\partial y_2}{\partial x_{21}} + \frac{\partial y_2}{\partial x_{22}}</math>.

Pseudocode

Der Rückwärtsmodus berechnet alle Komponente der Jacobi-Matrix in zwei Schritten: Im Vorwärtsschritt wird zuerst die Funktion ausgewertet und die partiellen Ergebnisse zwischengespeichert. Im Rückwärtsschritt werden die partiellen Ableitungen berechnet und der zuvor abgeleitete Wert zurückpropagiert (backpropagation). Der zugehörige Methodenaufruf erwartet den abzuleitenden Ausdruck Z und seed mit dem abgeleiteten Wert des Elternausdrucks. Für den obersten Ausdruck, Z nach Z abgeleitet, ist dieser 1. Die Methode arbeitet den Ausdrucksbaum rekursiv ab, bis eine Variable erreicht wird, und addiert den aktuellen seed-Wert zu dem Ausdruck für die Ableitung der Komponente.<ref name=ssdbm21>{{#invoke:Vorlage:Literatur|f}}{{#if:

       | {{#if: Vorlage:Cite book/ParamBool
               | Vorlage:Toter Link/archivebot
               | Vorlage:Webarchiv/archiv-bot
         }}
  }}{{#invoke:TemplatePar|check
   |all    = title=
   |opt    = vauthors= author= author1= authorlink= author-link= author-link1= author1-link= author2= author3= author4= author5= author6= author7= author8= author9= editor= last= first= last1= first1= last2= first2= last3= first3= last4= first4= last5= first5= last6= first6= last7= first7= last8= first8= last9= first9= last10= first10= last11= first11= last12= first12= last13= first13= last14= first14= last15= first15= others= script-title= trans-title= date= year= volume= issue= number= series= page= pages= at= issn= arxiv= bibcode= doi= pmid= pmc= jstor= oclc= id= url= url-status= format= access-date= archive-date= archive-url= archivebot= offline= location= publisher= language= quote= work= journal= newspaper= magazine= periodical=  name-list-style= url-access= doi-access= display-authors= via= s2cid= mr= type= citeseerx=  accessdate= archivedate= archiveurl= coauthors= month= day= last16= first16= last17= first17= last18= first18= last19= first19= last20= first20= last21= first21= last22= first22= last23= first23= last24= first24= last25= first25= last26= first26= last27= first27= last28= first28= last29= first29= last30= first30= last31= first31=
   |cat      = Wikipedia:Vorlagenfehler/Vorlage:Cite journal
   |errNS    = 0
   |template = Vorlage:Cite journal
   |format   = 
   |preview  = 1
  }}Vorlage:Cite book/URL{{#if:  | Vorlage:Cite book/Meldung }}{{#if:        | Vorlage:Cite book/Meldung }}{{#if: 33rd International Conference on Scientific and Statistical Database Management
     || Vorlage:Cite book/Meldung
  }}{{#if: Vorlage:Cite book/ParamBool
        | Vorlage:Cite book/Meldung
  }}{{#if: Vorlage:Cite book/ParamBool
     | Vorlage:Cite book/Meldung
  }}{{#if: Vorlage:Cite book/ParamBool
     | Vorlage:Cite book/Meldung
  }}{{#if: Vorlage:Cite book/ParamBool
     | Vorlage:Cite book/Meldung
  }}{{#if: Vorlage:Cite book/ParamBool
       | Vorlage:Cite book/Meldung
  }}{{#if: Vorlage:Cite book/ParamBool
     | Vorlage:Cite book/Meldung
  }}Vorlage:Cite book/Meldung2{{#ifexpr: 0{{#ifeq:^^|^^||+1}}{{#ifeq:^^|^^||+1}}{{#ifeq:Maximilian E. Schüle, Harald Lang, Maximilian Springer, Alfons Kemper, Thomas Neumann, Stephan Günnemann|^^||+1}}{{#ifeq:^^|^^||+1}} > 1
    | Vorlage:Cite book/Meldung
  }}</ref><ref name=dpd>{{#invoke:Vorlage:Literatur|f}}{{#if: 
       | {{#if: Vorlage:Cite book/ParamBool
               | Vorlage:Toter Link/archivebot
               | Vorlage:Webarchiv/archiv-bot
         }}
  }}{{#invoke:TemplatePar|check
   |all    = title=
   |opt    = vauthors= author= author1= authorlink= author-link= author-link1= author1-link= author2= author3= author4= author5= author6= author7= author8= author9= editor= last= first= last1= first1= last2= first2= last3= first3= last4= first4= last5= first5= last6= first6= last7= first7= last8= first8= last9= first9= last10= first10= last11= first11= last12= first12= last13= first13= last14= first14= last15= first15= others= script-title= trans-title= date= year= volume= issue= number= series= page= pages= at= issn= arxiv= bibcode= doi= pmid= pmc= jstor= oclc= id= url= url-status= format= access-date= archive-date= archive-url= archivebot= offline= location= publisher= language= quote= work= journal= newspaper= magazine= periodical=  name-list-style= url-access= doi-access= display-authors= via= s2cid= mr= type= citeseerx=  accessdate= archivedate= archiveurl= coauthors= month= day= last16= first16= last17= first17= last18= first18= last19= first19= last20= first20= last21= first21= last22= first22= last23= first23= last24= first24= last25= first25= last26= first26= last27= first27= last28= first28= last29= first29= last30= first30= last31= first31=
   |cat      = Wikipedia:Vorlagenfehler/Vorlage:Cite journal
   |errNS    = 0
   |template = Vorlage:Cite journal
   |format   = 
   |preview  = 1
  }}Vorlage:Cite book/URL{{#if:  | Vorlage:Cite book/Meldung }}{{#if:        | Vorlage:Cite book/Meldung }}{{#if: Distributed and Parallel Databases
     || Vorlage:Cite book/Meldung
  }}{{#if: Vorlage:Cite book/ParamBool
        | Vorlage:Cite book/Meldung
  }}{{#if: Vorlage:Cite book/ParamBool
     | Vorlage:Cite book/Meldung
  }}{{#if: Vorlage:Cite book/ParamBool
     | Vorlage:Cite book/Meldung
  }}{{#if: Vorlage:Cite book/ParamBool
     | Vorlage:Cite book/Meldung
  }}{{#if: Vorlage:Cite book/ParamBool
       | Vorlage:Cite book/Meldung
  }}{{#if: Vorlage:Cite book/ParamBool
     | Vorlage:Cite book/Meldung
  }}Vorlage:Cite book/Meldung2{{#ifexpr: 0{{#ifeq:^^|^^||+1}}{{#ifeq:^^|^^||+1}}{{#ifeq:Maximilian E. Schüle, Harald Lang, Maximilian Springer, Alfons Kemper, Thomas Neumann, Stephan Günnemann|^^||+1}}{{#ifeq:^^|^^||+1}} > 1
    | Vorlage:Cite book/Meldung
  }}</ref>

<syntaxhighlight lang="C++"> void ableiten(Ausdruck Z, float seed) {

  if (Z = X + Y)
     ableiten(X,seed);
     ableiten(Y,seed);
  else if (Z = X - Y)
     ableiten(X,seed);
     ableiten(Y,-seed);
  else if (Z = X * Y)
     ableiten(X,wertVon(X)*seed);
     ableiten(Y,seed*wertVon(Y));
  else if istVariable(Z)
     partielleAbleitungVon(Z) += seed;

} </syntaxhighlight>

Effizienzbetrachtungen

Die Effizienz von AD-Algorithmen hängt vom Modus und dem Parameter p ab. Die Wahl des Modus und des Parameters p hängt davon ab, wofür die Jacobimatrix berechnet wird. Es bezeichne

<math>T_f</math> die Zeit f zu berechnen
<math>M_f</math> der Speicherbedarf dieser Rechnung
<math>T_{JS}</math> die Zeit f und JS zu berechnen
<math>M_{JS}</math> der Speicherbedarf dieser Rechnung
<math>T_{SJ}</math> die Zeit f und SJ zu berechnen
<math>M_{SJ}</math> der Speicherbedarf dieser Rechnung

Für die beiden vorgestellten Modi gilt

  1. Vorwärtsmodus: <math> \frac{T_{JS}}{T_f} \approx p, \frac{M_{JS}}{M_f} \approx p</math>
  2. Rückwärtsmodus: <math> \frac{T_{SJ}}{T_f} \approx p, \frac{M_{SJ}}{M_f} \approx T_f</math>

Der Vorwärtsmodus hat den Vorteil, dass keine Zwischenergebnisse für einen zweiten Durchgang gespeichert werden müssen, und den Nachteil, dass er pro Komponente einmal ausgeführt werden muss. Dennoch basieren beide AD-Algorithmen auf der Berechnung partieller Ableitungen. Ein Compiler ist daher in der Lage den Code des Vorwärtsmodus zu optimieren, sodass partielle Ableitungen, die für mehr als eine Komponente benötigt werden, auch nur einmal – wie im Rückwärtsmodus – berechnet werden.<ref name=demm22 />

Die Berechnung als Kette von Berechnungen

Gegeben: <math>s=g\left(u,v\right)</math>, Frage: Wie verändert sich die Ableitung von s während der zweiten Phase, um die Ableitungen von u und v zu erhalten?

<math>f(x)</math> wird als Sequenz von Programmen interpretiert. Im Beispiel „Optimierung eines Tragflügels“ umfasst die Berechnung die folgenden Schritte.

  • Überlagerung des Tragflügels mit sogenannten „Mode-Funktionen“
Vorlage:Center
  • Berechnung eines Gitters, das um den Tragflügel herum gelegt wird
Vorlage:Center
Vorlage:Center.

Insgesamt ergibt sich die Funktion

  <math>f=f\left( G \left( A \left( x \right) \right) \right) \rightarrow \frac{\partial f}{\partial x} = \frac{\partial f}{\partial G} \frac{\partial G}{\partial A} \frac{\partial A}{\partial x}</math>

Mit einem naiven Ansatz würde man drei Matrizen <math>\frac{\partial f}{\partial G}</math>, <math>\frac{\partial G}{\partial A}</math>, <math>\frac{\partial A}{\partial x}</math> berechnen und dann zwei Matrizenmultiplikationen durchführen. Der Nachteil des Vorwärtsmodus ist allerdings:

  <math>T_{\frac{\partial f}{\partial G} \cdot S} \approx 17428 \cdot T_{f\left( G \right)}</math>

im Rückwärtsmodus würde analog

  <math>T_{S \cdot \frac{\partial f}{\partial G}} \approx 17428 \cdot T_{f\left( G \right)}</math>

gelten. Ein besserer Ansatz ist, das Ergebnis einer Berechnung jeweils als Saatmatrix der folgenden einzusetzen.

  1. Wähle <math> I_{8x8} \in \mathbb{R}^{8x8}</math> als Saatmatrix der ersten Rechnung
  2. Das Ergebnis der ersten Rechnung als Saatmatrix der zweiten Rechnung
  3. Das Ergebnis der zweiten Rechnung als Saatmatrix der dritten Rechnung

also

  1. <math> \frac{\partial A}{\partial x} I_{8 \times 8} \in \mathbb{R}^{8 \times 200} </math>
  2. <math> \frac{\partial G}{\partial A} \frac{\partial A}{\partial x} \in \mathbb{R}^{8 \times 17428}</math>
  3. <math> \frac{\partial f}{\partial G} \frac{\partial G}{\partial x} \in \mathbb{R}^{8 \times 1}</math>

Da die Zeilenzahl jeder Matrix 8 (p=8) ist, erhöht sich der Zeit- und Speicherbedarf gegenüber der regulären Auswertung von <math>f(x)</math> um höchstens 8.

Literatur

Weblinks

Einzelnachweise

<references />