Trend-Themen
#
Bonk Eco continues to show strength amid $USELESS rally
#
Pump.fun to raise $1B token sale, traders speculating on airdrop
#
Boop.Fun leading the way with a new launchpad on Solana.
Ich habe ein paar Stunden damit verbracht, das /karpathy/autoresearch-Repo Zeile für Zeile durchzugehen.
das Thema "KI-Agenten, die Forschung betreiben" erhält viel Aufmerksamkeit, aber ich denke, das Interessanteste ist, was tatsächlich im Trainingsskript steckt und die technischen Entscheidungen, die die Suchschleife effizient machen. Es ist eines der dichtesten Trainings-Setups in einer einzigen Datei, die ich gelesen habe.
Lass mich mit dem beginnen, was das gesamte Projekt möglich macht: Das Zeitbudget ist auf 300 Sekunden Echtzeit festgelegt. Keine festen Schritte, keine festen Tokens, keine festen FLOPS. Echtzeit-Sekunden. Das klingt nach einem kleinen Detail, aber es ist der gesamte Grund, warum die autonome Schleife funktioniert. Der Agent kann das Modell 3x größer machen, die Batch-Größe halbieren, eine völlig andere Architektur einfügen, und das Ergebnis ist immer noch direkt vergleichbar mit jedem anderen Experiment, weil sie alle genau 5 Minuten Training auf derselben GPU erhalten haben. Wenn du stattdessen feste Schritte festlegst, würde ein größeres Modell weniger Gradientupdates pro Sekunde erhalten und du würdest es unfair bestrafen. Wenn du feste Tokens festlegst, hättest du dasselbe Problem. Die Festlegung der Echtzeit bedeutet, dass du die richtige Frage stellst: Angesichts dieser Hardware und dieser Zeit, welches ist das beste Modell, das du produzieren kannst? Alles andere ist eine freie Variable. Der Agent kann die gesamte Pareto-Oberfläche von Modellgröße vs. Durchsatz vs. Konvergenzgeschwindigkeit erkunden, ohne dass diese Trade-offs durch das Evaluierungsprotokoll verwirrt werden.
Die Metrik ist ebenfalls sorgfältig gewählt. Es sind Bits pro Byte, nicht Kreuzentropieverlust. Kreuzentropie hängt von deiner Vokabulargröße ab. Ein Modell mit 32k Tokens und ein Modell mit 8k Tokens werden sehr unterschiedliche Verlustwerte haben, selbst wenn sie die Daten gleich gut komprimieren. bpb normalisiert dies, indem es die Kreuzentropie pro Token in Nats summiert, die UTF-8-Byte-Längen der Zieltokens summiert und Nats pro Byte in Bits pro Byte umwandelt. Selbst wenn der Agent etwas ändert, das die effektive Tokenverteilung beeinflusst, bleibt der Vergleich fair. Diese beiden Entscheidungen, feste Echtzeit und eine vokabularunabhängige Metrik, verwandeln das, was eine unordentliche, nicht vergleichbare Suche wäre, in ein sauberes Optimierungsproblem.
Jetzt zum Modell selbst. Es ist ein GPT, aber mit einer Menge moderner Tricks, die es wert sind, verstanden zu werden. Zuerst, überall RMSnorm. Bei den Blockeingaben (Pre-Norm) und auch bei Abfragen und Schlüsseln direkt vor dem Attention-Dot-Produkt. Diese QK-Norm-Sache ist wichtig, denn ohne sie können die Normen von q und k während des Trainings unbegrenzt wachsen, was dazu führt, dass die Attention-Logits schärfer werden und Softmax gesättigt wird. Die Normalisierung von q und k hält die Dot-Produkte in einem stabilen Bereich, unabhängig davon, wie tief das Netzwerk ist oder wie sich die Trainingsdynamik entwickelt. Die Attention selbst ist FA 3, geladen über die Kernbibliothek. Sie verwendet die Implementierung von varunneal auf Hopper (sm_90) und fällt auf einen Community-Build auf älteren GPUs zurück. Das Attention-Muster ist "SSSL", was bedeutet, dass drei Schichten von Sliding-Window-Attention (Fenster = die Hälfte der Sequenzlänge) gefolgt von einer Schicht voller kausaler Attention wiederholt werden. Dies ist das Sparse-to-Dense-Muster, das du in Mistral und Gemma2 siehst.
Die lokalen Attention-Schichten sind rechnerisch günstig, weil die Attention-Matrix gebändert ist, und die periodische globale Schicht lässt Informationen über den gesamten Kontext fließen. Mit 8 Schichten und einem 4-Zeichen-Muster erhältst du die Schichten 0,1,2 lokal, Schicht 3 global, Schichten 4,5,6 lokal, Schicht 7 global. Die letzte Schicht ist unabhängig vom Muster global.
Die Wert-Embedding-Sache ist subtil und ich denke, sie wird unterschätzt. Jede andere Schicht erhält ihre eigene Einbettungstabelle, die vollständig von der Haupt-Token-Einbettung getrennt ist, die Token-IDs direkt in Wert-Dimension-Vektoren abbildet. Diese werden in die Attention-Werte durch ein gelerntes Gate gemischt: v = v + 2 * sigmoid(W_gate @ x:32) * ve. Das Gate-Gewicht ist null-initialisiert, sodass sigmoid(0) = 0.5, mal 2 ergibt 1.0, was einen neutralen Ausgangspunkt darstellt. Im Laufe des Trainings kann das Modell lernen, das Wert-Embedding pro Kopf basierend auf den ersten 32 Dimensionen des versteckten Zustands zu verstärken oder zu unterdrücken. Dies stammt aus der ResFormer-Arbeitslinie und die Intuition ist, dass es der Attention eine direkte Abkürzung zur Token-Identität gibt. Die Wertvektoren können Informationen darüber tragen, "welches Token sich an dieser Position befindet", ohne dass diese Informationen die Transformationen des Residualstroms aus früheren Schichten überstehen müssen. Es ist im Wesentlichen eine Skip-Verbindung vom Eingang direkt in die Attention-Werte, gated, sodass das Modell entscheiden kann, wann es nützlich ist.
Es gibt auch pro-Schicht lernbare Skalare im Residualstrom: x = lambda_residi * x + lambda_x0i * x0, wobei x0 die normalisierte Einbettung aus Schicht 0 ist. Jede Schicht kann unabhängig steuern, wie viel sie dem laufenden Residualstrom im Vergleich zum ursprünglichen Eingang zuhört. Die Residual-Lambdas beginnen bei 1.0, die x0-Lambdas beginnen bei 0.1. Dies ist eine weiche Version der Idee des "entkoppelten Residuals". In einem Standard-Transformer ist der Residualstrom eine Summe aller vorherigen Schichtausgaben und wird zunehmend verschmutzt, je tiefer man geht. Jeder Schicht Zugang zur sauberen ursprünglichen Einbettung zu geben, bedeutet, dass sie nicht lernen muss, frühere Schichten "rückgängig zu machen", um niedrigstufige Informationen wiederherzustellen. Die Logits sind über tanh(logits/15)*15 weich begrenzt, was verhindert, dass das Modell zu selbstsicher wird, wenn die Repräsentationen noch verrauscht sind.
Aber ehrlich gesagt ist der interessanteste Teil der gesamten Datei der Optimierer. MuonAdamW ist ein kombinierter Optimierer, der unterschiedliche Aktualisierungsregeln basierend auf der Parametergruppe dispatcht. Einbettungen (Token-Einbettung, Wert-Einbettungen, Unembedding-Kopf) und pro-Schicht-Skalare erhalten standardmäßiges AdamW mit unterschiedlichen Lernraten für jede Gruppe. Die Spanne ist wild. Die Lernrate für Einbettungen beträgt 0.6, die Lernrate für Unembedding beträgt 0.004, das ist ein Unterschied von 150x, und das ist beabsichtigt. Die Einbettungsmatrix sieht jedes einzelne Token und muss aggressiv aktualisiert werden. Die Unembedding-Matrix ist ein linearer Probe auf der finalen Repräsentation und profitiert von Stabilität. Die Lernraten für Einbettung, Wert-Einbettung und Unembedding werden alle durch (d_model / 768)^(-0.5) skaliert, was eine muP-inspirierte Korrektur ist. Wenn sich die Modellbreite ändert, passen sich diese Lernraten an, um die Merkmalslerndynamik skalierungsinvariant zu halten. Die skalaren Lernraten für die pro-Schicht-Lambdas werden separat behandelt und erhalten diese Skalierung nicht.
Die 2D-Gewichtsmatrizen im Transformer, Attention-Projektionen und MLP-Gewichte, erhalten Muon, und hier wird es wirklich interessant. Muon nimmt den Gradient, wendet Nesterov-Momentum an und führt dann eine Newton-Schulz-Iteration durch, um die polare Zerlegung der Gradientmatrix zu approximieren. Die polare Zerlegung faktorisiert eine Matrix G in G = U * S, wobei U orthogonal und S symmetrisch positiv definit ist. Muon berechnet U, die nächstgelegene orthogonale Matrix zum Gradient, und verwendet diese als Aktualisierungsrichtung. Die Newton-Schulz-Iteration besteht aus 5 Schritten. Für hohe Matrizen (mehr Zeilen als Spalten) gilt A = X^T @ X, dann X -> aX + X @ (bA + cA^2). Für breite Matrizen gilt A = X @ X^T, dann X -> aX + (bA + cA^2) @ X. Die Koeffizienten sind aus einer Vorberechnung fest codiert. Sie nennen es "polar express". Das Ganze wird zu einem einzigen fusionierten Kernel über torch.compile kompiliert.
Warum ist das wichtig? Weil für Gewichtsmatrizen die Frobenius-Norm-Gradient (den Adam und SGD verwenden) geometrisch falsch ist. Die "korrekte" steilste Abstiegsrichtung für eine Gewichtsmatrix ist diejenige, die den Verlust minimiert, unter der Bedingung, dass die Aktualisierung eine Einheitsspektralnorm hat, nicht eine Einheit-Frobeniusnorm. Der orthogonale polare Faktor gibt dir genau das. In der Praxis bedeutet das, dass Muon viel größere effektive Updates macht, weil es die Schrittgröße nicht für die Skalierung der singulären Werte verschwendet. Es rotiert sie nur. Das ist der Grund, warum Muon signifikant schneller konvergiert als Adam bei Gewichtsmatrizen von Transformern. Muon hält pro-Element-Momentum-Puffer (gleiche Form wie die Parameter, gestapelt über jede Formgruppe), aber im Gegensatz zu Adam verfolgt es keine pro-Element-Zweiten Momente. Die Schätzungen des zweiten Moments sind pro Zeile oder pro Spalte nach der Orthogonalisierung, nicht pro Element. Das ist, wo NorMuon ins Spiel kommt.
Oben auf dem Basis-Muon gibt es NorMuon, ein Variationsreduktionsschema. Nach der Orthogonalisierung berechnet es pro Zeile (oder pro Spalte, je nach Seitenverhältnis) Schätzungen des zweiten Moments, hält einen exponentiellen gleitenden Durchschnitt davon und skaliert die Aktualisierung, sodass jede Ausgabedimension ihre eigene adaptive Schrittgröße erhält. Es ist im Wesentlichen die Adam-Adaptivität-Idee, aber im orthogonalisierten Koordinatensystem angewendet, anstatt im Rohparameterraum. Der Gewichtungsverfall ist ebenfalls nicht standardmäßig. Er ist "vorsichtig", was bedeutet, dass er nur Parameter verfallen lässt, bei denen die Muon-Aktualisierungsrichtung mit dem Parametersignal übereinstimmt: mask = (g * params) >= 0. Dies vermeidet den bekannten Fehlerfall, bei dem der Gewichtungsverfall Parameter gegen den Wünschen der Aktualisierung in Richtung Null drängt, was das Training destabilisieren kann.
Ein kleines Detail, das ich zu schätzen wusste: Nach dem allerersten Trainingsschritt ruft der Code gc.collect(), gc.freeze(), gc.disable() auf, um den Garbage Collector von Python vollständig abzuschalten. Der GC von Python läuft periodisch und verursacht ~500ms Pausen. Wenn dein gesamtes Budget 300 Sekunden beträgt und jeder Schritt vielleicht 300ms dauert, kostet dir eine zufällige GC-Pause fast 2 Trainingsschritte. Sie lösen gc.collect() manuell alle 5000 Schritte als Kompromiss aus. Das ist die Art von Dingen, die du nur lernst, indem du echte Trainingsläufe profilierst und mysteriöse Durchsatzrückgänge bemerkst.
Die ersten 11 Schritte (0 bis 10) werden ebenfalls nicht auf das Zeitbudget angerechnet. Das ist das Warmup, bei dem torch.compile sein Ding macht und CUDA-Kerne JIT-compiliert. Ohne diese Ausschlussregel würden verschiedene Experimente unterschiedliche Mengen an "echtem" Training erhalten, abhängig davon, wie lange die Kompilierung für diese bestimmte Modellkonfiguration dauert. Wieder eine Designentscheidung, die klein erscheint, aber entscheidend ist, um Experimente vergleichbar zu machen.
Jetzt zoom raus. Die tatsächliche Autoresearch-Schleife ist: Der Agent liest program.md (eine Markdown-Datei, die seinen Job beschreibt), ändert train.py, committet, läuft 5 Minuten, überprüft, ob val_bpb sich verbessert hat, behält oder macht rückgängig, wiederholt. program.md sagt ausdrücklich "NIE STOPPEN." Der Agent läuft unendlich, bis der Mensch ihn stoppt. ~12 Experimente pro Stunde, ~100 über Nacht, während du schläfst.
...
Top
Ranking
Favoriten
