====== 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.