std.autodiff — automatische Differenzierung

Ableitungen ohne Handrechnung und ohne Differenzenquotient: Rückwärtsmodus auf einem Band, das alle Gradienten in einem Durchlauf liefert.

Mathematik-Units · std.numeric · std.mathx · std.ml

import std.autodiff;

Schicht 2, hängt an 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

<WRAP alert> 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. </WRAP>


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

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