Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Optimierer: Momentum, Adam, Muon

Heinrich-Heine-Universität Düsseldorf

Adam oder was?

Wir haben die Grundidee des stochastischen Mini-Batch-Gradientenabstiegs gelernt und dann gesagt: In PyTorch klappt Adam oft besser als SGD. Aber was steckt dahinter?

Das Grundproblem beim SGD

Die Daten x1,…,xNx_1, \dots, x_N modellieren wir als Stichprobe einer Zufallsvariable XX. Die Verteilung von XX ist unbekannt, und die Stichprobe hat Sampling-Bias. Am liebsten möchten wir N→∞N \to \infty haben, damit aus der Stichprobe die Verteilung von XX genau geschätzt werden kann. Für eine Stichprobe können wir den Loss ausrechnen; idealisiert für eine „unendlich große Stichprobe“ wäre das der Erwartungswert der Loss-Funktion unter der Verteilung von XX – der „eigentlich richtige Loss“. Es gibt also drei Player:

Pro Mini-Batch wird der Loss grob abgeschätzt und damit werden Gradienten berechnet, die also auch „nur so grob geschätzt“ sind.

Idee: Momentum

Wir schätzen den „wahren Loss“ und damit den eigentlich interessanten Gradienten aus jedem Mini-Batch neu. Die Schätzung wird besser, wenn die Batch Size steigt. Kann man auch Informationen aus dem vorherigen Mini-Batch nutzen, um einen ähnlichen Effekt zu erzielen? Man könnte z.B. die Gradientenschätzungen von je 5 Batches mitteln und erhielte damit eine bessere Schätzung. Das praktische Problem mit diesem Ansatz: Man müsste 5 Gradienten (jeder so groß wie das Modell) speichern, und man bekäme nur alle 5 Batches ein Update.

Idee: Gradienten sind ein bisschen ähnlich, wenn man sich nicht so weit weg bewegt im Loss Landscape. Statt des Mittelwerts der letzten 5 Gradienten verwende ein exponentiell gewichtetes Mittel, das sich mit einem einzigen Vektor mitführen lässt:

mt:=β1mt−1+(1−β1) gt,wt+1:=wt−ρ mt,m_t := \beta_1 m_{t-1} + (1 - \beta_1)\, g_t, \qquad w_{t+1} := w_t - \rho\, m_t,

mit gtg_t dem Gradienten des aktuellen Batches und typischerweise β1=0,9\beta_1 = 0{,}9. Visuelle Metapher: Zeigt der Pfeil (Gradient) in eine Richtung, geht es nächstes Mal auf jeden Fall auch ein bisschen weiter in diese Richtung – Impulserhaltung, daher Momentum. Rauschen in wechselnden Richtungen mittelt sich heraus, konsistente Richtungen verstärken sich.

Idee: Adaptive Lernrate

Der zweite Bestandteil von Adam (= Adaptive Moment Estimation) ist der adaptive Teil: Zusätzlich zu einer Heuristik zur besseren Schätzung der Gradienten der Loss-Funktion berechne die Quadrate – das mittlere Gradientenquadrat, also das zweite Moment (nicht die Varianz; dafür müsste man noch mt2m_t^2 abziehen), ebenfalls als exponentiell gewichtetes Mittel, komponentenweise:

vt:=β2vt−1+(1−β2) gt2,wt+1:=wt−ρ mtvt+ϵ,v_t := \beta_2 v_{t-1} + (1 - \beta_2)\, g_t^2, \qquad w_{t+1} := w_t - \rho\, \frac{m_t}{\sqrt{v_t} + \epsilon},

mit typischerweise β2=0,999\beta_2 = 0{,}999 und ϵ=10−8\epsilon = 10^{-8} gegen Division durch null. Sind die Gradienten einer Koordinate durchweg groß, wird der Schritt dort gedämpft; sind sie durchweg klein, wird er verstärkt. Im Ergebnis ist mt/vtm_t / \sqrt{v_t} von der Größenordnung 1, jeder Parameter macht also Schritte der Größenordnung ρ\rho – unabhängig davon, wie groß seine Gradienten absolut sind. Diese Technik kommt von RMSProp (Root Mean Square Propagation). Der Effekt ist eine Lernrate pro Gewicht: Gewichte mit selten großen Gradienten (z.B. Embeddings seltener Wörter) bekommen dadurch relativ größere Schritte.

Adam oder was?

Dahinter steckt also eine adaptive Lernrate, abhängig von der bisherigen Größe der Gradienten, einzeln für jedes Gewicht, sowie die Impuls-Heuristik zur verbesserten Gradientenschätzung. (Adam korrigiert außerdem die Schätzungen mtm_t, vtv_t in den ersten Schritten für den Bias durch die Initialisierung mit null.) Wo geht das schief? Wenn die Lernrate nicht stark genug abgesenkt wird, findet die Optimierung nie das lokale Optimum und oszilliert darum herum. Deshalb kombiniert man Adam in der Praxis mit einem Learning Rate Schedule, der ρ\rho im Laufe des Trainings verkleinert.

Adam oder Muon?

Die Grundidee „wir rechnen clever eine bessere Schätzung bzw. Heuristik für Gradienten aus“ kann man noch weiter treiben. Idee: Ein linearer Layer im neuronalen Netz lässt sich als Operator betrachten, also als Abbildung (die Matrixmultiplikation). Behandle die Gewichte einer Matrix nicht isoliert, sondern als Ganzes! Und statt mit der Varianz zu reskalieren wie bei Adam, orthogonalisiere das Update – das ist etwas schwieriger zu verstehen.

Erinnerung: Eine Matrix AA hat eine Singulärwertzerlegung A=UDVA = U D V, wobei UU und VV orthogonale Matrizen sind (bestimmte Koordinatensysteme) und DD eine Diagonalmatrix mit den Singulärwerten. Wenn die Einträge von DD entweder 0 oder 1 sind, ist AA selbst (semi-)orthogonal.

Muon: Wir berechnen erst wie bei SGD den Gradienten der Loss-Funktion nach einer Gewichtsmatrix, hier genannt AA; die Update-Formel wäre Wneu:=Walt−ρAW_{\text{neu}} := W_{\text{alt}} - \rho A. „Orthogonal machen“ durch A=UDVA = UDV berechnen und Aortho:=UDorthoVA_{\text{ortho}} := U D_{\text{ortho}} V mit Dortho,i:=[Di>0]D_{\text{ortho}, i} := [D_i > 0] ist leider teuer: Die SVD ist aufwändig. Also mit der Brechstange:

Jordan, Keller et al.: Muon: An optimizer for hidden layers in neural networks (2024). Alle Richtungen im Gewichtsraum bekommen dadurch denselben Schritt, nicht nur die mit großem Singulärwert – Richtungen, die der Gradient „vernachlässigt“, werden mitgezogen.

Wieso so kompliziert?

Genau genommen liegt der Gradient der Loss-Funktion an einem Punkt des Gewichtsraums im Kotangentialraum des Graphen der Loss-Funktion – er ist eine Linearform, die Änderungsrichtungen Zahlen zuordnet (Kapitel „Tensoren“: Kovektoren!). Um einen Vektor aus dem Gewichtsraum daraus zu machen, mit dem wir einen Schritt gehen können, müssen wir „dualisieren“. Dabei kann man verschiedene Geometrien betrachten:

Adam und Muon sind in diesem Bild billige Näherungen an eine bessere Geometrie des Gewichtsraums.

Übung

Eine Funktion mit Gradienten g1=1g_1 = 1, g2=1g_2 = 1, g3=1g_3 = 1 in drei aufeinanderfolgenden Schritten (ein langes, flaches Tal), Lernrate ρ=0,1\rho = 0{,}1.

  1. Wie weit ist SGD nach drei Schritten gekommen?

  2. Wie weit kommt Momentum mit vt=βvt−1+gtv_t = \beta v_{t-1} + g_t, β=0,9\beta = 0{,}9 und demselben ρ\rho? Das ist die Form, die torch.optim.SGD(momentum=0.9) benutzt; sie unterscheidet sich von der Fassung oben im Kapitel um den Faktor (1−β)(1 - \beta), der dort das Mittel normiert. Mit der Kapitelfassung käme entsprechend ein Zehntel heraus. Tipp: v1=1v_1 = 1, v2=1,9v_2 = 1{,}9, v3=2,71v_3 = 2{,}71 – Sie müssen nur noch aufsummieren.

  3. Jetzt wechselt der Gradient das Vorzeichen: g=(1,−1,1)g = (1, -1, 1). Was macht Momentum, und warum ist das in einer engen Schlucht erwünscht?

Antwortprobe: Bei (2) ist der zurückgelegte Weg knapp doppelt so lang wie bei (1).