====== std.autodiff — automatische Differenzierung ====== Ableitungen ohne Handrechnung und ohne Differenzenquotient: Rückwärtsmodus auf einem Band, das **alle** Gradienten in einem Durchlauf liefert. → [[lyx_-_programmiersprache:units:mathematik|Mathematik-Units]] · [[lyx_-_programmiersprache:units:numeric|std.numeric]] · [[lyx_-_programmiersprache:units:mathx|std.mathx]] · [[lyx_-_programmiersprache:units:ml|std.ml]] import std.autodiff; Schicht 2, hängt an [[lyx_-_programmiersprache:units:mathx|std.mathx]]. 53 Funktionen. Alle Beispiele mit ''lyxc 1.0.21A'' übersetzt und ausgeführt. ---- ===== Was das löst ===== Für eine Ableitung gibt es sonst drei Wege, und alle drei haben einen Haken: ^ Weg ^ Haken ^ | Von Hand ableiten | fehleranfällig, und bei jeder Änderung der Funktion neu | | Differenzenquotient | verliert die Hälfte der Stellen; die Schrittweite ist ein Kompromiss | | Symbolisch | die Ausdrücke wachsen explosionsartig | **Automatische Differenzierung rechnet die Ableitung mit**, während der Wert entsteht — exakt bis auf Rundung, ohne Schrittweite, ohne wachsende Ausdrücke. ---- ===== Das Band ===== Beim Rückwärtsmodus wird jede Rechenoperation auf einem **Band** vermerkt. Ist der Wert fertig, läuft ''AdBackward'' das Band rückwärts ab und verteilt die Ableitungen — **alle Gradienten in einem Durchgang**, unabhängig von der Zahl der Variablen. unit main; import std.io; import std.string; import std.autodiff; fn ZF(t: pchar, v: f64): void { Print(t); PrintF64(v); } fn main(): int64 { // f(x, y) = x^2 * y + sin(x) bei x = 2, y = 3 var t: int64 := AdTapeNew(256); var x: int64 := AdVar(t, 2.0); var y: int64 := AdVar(t, 3.0); var x2: int64 := AdSquare(t, x); var p: int64 := AdMul(t, x2, y); var s: int64 := AdSin(t, x); var f: int64 := AdAdd(t, p, s); ZF("f(2,3) : ", AdValue(t, f)); AdBackward(t, f); ZF("df/dx : ", AdGrad(t, x)); ZF("df/dy : ", AdGrad(t, y)); Print("Bandlaenge : "); PrintLn(IntToStr(AdTapeCount(t))); AdTapeFree(t); return 0; } f(2,3) : 12.909297 df/dx : 11.583853 df/dy : 4.000000 Bandlaenge : 6 Nachgerechnet: ''f(2,3) = 4·3 + sin(2) = 12,909297''. Die Ableitungen sind ''df/dx = 2xy + cos(x) = 12 + cos(2) = 11,583853'' und ''df/dy = x² = 4'' — beide auf allen gezeigten Stellen getroffen. **Das Band hat sechs Einträge** — zwei Variablen und vier Operationen. Genau das ist der Speicherbedarf, und er wächst mit der Zahl der Operationen, nicht mit der Zahl der Variablen. ---- ===== Vorwärts oder rückwärts ===== > **Die Wahl hängt am Verhältnis von Eingaben zu Ausgaben.** > > Der **Rückwärtsmodus** liefert die Ableitungen **einer** Ausgabe nach **allen** Eingaben in einem Durchlauf. Bei einer Verlustfunktion mit tausend Parametern ist das genau richtig — ein Durchlauf, tausend Gradienten. Der Preis ist das Band: jede Operation wird gespeichert. > > Der **Vorwärtsmodus** über Dualzahlen liefert umgekehrt die Ableitungen **aller** Ausgaben nach **einer** Eingabe. Er braucht kein Band und ist bei wenigen Eingaben billiger — für die Ableitung einer Funktion einer Veränderlichen, wie sie ''NumNewton'' braucht, ist er die richtige Wahl. ---- ===== Der Bandspeicher ===== ''AdTapeNew(capacity)'' legt die Größe **fest**. Reicht sie nicht, schlagen weitere Operationen fehl. ''AdTapeCount'' und ''AdTapeCapacity'' geben den Stand; ''AdTapeReset'' leert das Band, **ohne** neu zu reservieren — der richtige Weg in einer Trainingsschleife, statt für jeden Schritt ein neues Band anzulegen. ''AdTapeFree'' gehört dazu. ---- ===== Funktionsübersicht ===== ^ Gruppe ^ Funktionen ^ | Band | ''AdTapeNew'' · ''AdTapeFree'' · ''AdTapeReset'' · ''AdTapeCount'' · ''AdTapeCapacity'' | | Knoten | ''AdVar'' (abzuleiten) · ''AdConst'' (fest) · ''AdValue'' | | Grundrechenarten | ''AdAdd'' · ''AdSub'' · ''AdMul'' · ''AdDiv'' · ''AdNeg'' · ''AdAddConst'' · ''AdMulConst'' | | Elementar | ''AdSquare'' · ''AdSqrt'' · ''AdExp'' · ''AdLog'' · ''AdPowConst'' · ''AdRecip'' · ''AdAbs'' | | Winkelfunktionen | ''AdSin'' · ''AdCos'' · ''AdTan'' | | Neuronale Netze | ''AdTanh'' · ''AdSigmoid'' · ''AdRelu'' · ''AdMax'' · ''AdMin'' | | Auswerten | ''AdBackward'' · ''AdGrad'' | **Der Unterschied zwischen ''AdVar'' und ''AdConst'' ist der Punkt.** Nur nach ''AdVar''-Knoten wird abgeleitet; ''AdConst'' geht als feste Zahl ein. Wer einen Parameter versehentlich als ''AdConst'' anlegt, bekommt für ihn den Gradienten null — kein Fehler, nur ein Netz, das an dieser Stelle nicht lernt. > **''AdAbs'', ''AdRelu'', ''AdMax'' und ''AdMin'' sind an einer Stelle nicht differenzierbar.** Bei ''x = 0'' hat der Betrag keine Ableitung; die Umsetzung wählt dort einen der einseitigen Werte. Das ist die übliche Festlegung und in der Praxis unschädlich — es lohnt aber zu wissen, dass der Gradient genau am Knick eine **Konvention** ist und kein mathematischer Wert. ---- ===== Zusammenspiel ===== * [[lyx_-_programmiersprache:units:numeric|std.numeric]] nimmt die Ableitung entgegen: ''NumNewton(f, df, …)'' wird mit einer autodiff-gerechneten Ableitung sowohl schnell als auch exakt — statt ''NumNewtonNum'', das sie numerisch schätzt. * Die Aktivierungsfunktionen ''AdTanh'', ''AdSigmoid'' und ''AdRelu'' sind da, weil das der Fall ist, für den der Rückwärtsmodus gebaut wurde: eine skalare Verlustfunktion über viele Gewichte. ---- Letzte Aktualisierung: 2026-08-16 · alle Beispiele mit ''lyxc 1.0.21A'' übersetzt und ausgeführt; Wert und beide partielle Ableitungen gegen die von Hand gerechneten Ausdrücke geprüft.