Attention Is All You Need
Artikel 6 von 8 · Serie: Wie LLMs funktionieren
In Teil 5 sind wir vor einer Wand stehengeblieben. RNNs lernen Kontext, indem sie ihn Token für Token durch einen Notizblock pressen. Das funktioniert für kurze Sätze. Für lange Texte verliert sich der Anfang im Rauschen, und das Training durch eine Sequenz aus 2000 Tokens läuft auf einer GPU langsamer als die Geduld der Forscher reichte.
Die Frage war: Muss das so sein? Muss Information sequenziell von links nach rechts wandern, durch eine einzige Engstelle? Oder geht es auch anders?
2017 war die Antwort da. Acht Forscher bei Google Brain veröffentlichten ein Paper mit dem vermutlich berühmtesten Titel der NLP-Geschichte: „Attention Is All You Need" (Vaswani et al., 2017). Ihre Behauptung: Den ganzen rekurrenten Apparat braucht es nicht. Ein einziger Mechanismus reicht, und der ist konzeptionell überraschend einfach. Er heißt Attention.
Die Grundidee, eine Bibliothek statt Stille Post
Stellen wir uns vor, wir wollen einen Satz verstehen. Genauer: wir wollen die Bedeutung eines bestimmten Wortes im Satz schärfen, indem wir die anderen Wörter anschauen.
Im RNN war die Strategie der Notizblock: alles was vorher kam wurde Schritt für Schritt eingeschrieben und beim aktuellen Wort wieder abgerufen. Gedacht als Gedächtnis, aber praktisch ist daraus durch die wiederholte Anwendung von W_hh die Stille Post geworden, vor allem über lange Sequenzen.
Die Attention-Idee ist anders. Stellen wir uns eine Bibliothek vor. Jedes Wort im Satz legt zwei Dinge ab:
- Eine Karteikarte mit ein paar Stichworten was es ist (das ist der Key)
- Ein Buch mit dem eigentlichen Inhalt (das ist der Value)
Wenn jetzt das Wort Bank verstehen will was um es herum passiert, formuliert es eine Suchanfrage (das ist der Query) und vergleicht sie mit allen Karteikarten in der Bibliothek. Karteikarten die gut zur Anfrage passen, bekommen ein hohes Gewicht. Karten die schlecht passen, ein niedriges. Aus den Büchern wird dann eine gewichtete Mischung zurückgegeben, dominiert von den Büchern deren Karten am besten gepasst haben.
Genau das ist Attention. Drei Rollen für jedes Token, drei Vektoren pro Token, eine Suche, eine gewichtete Mischung der Inhalte.
Bemerkenswert daran: Diese Suche kann jedes Token in der Sequenz für sich anstellen, gleichzeitig. Niemand muss warten bis das vorherige Wort fertig ist. Der Notizblock-Bottleneck verschwindet.
Drei Rollen, drei Projektionen
Was sind diese drei Vektoren konkret? Wir starten mit dem was wir aus Artikel 2 kennen, dem Embedding für jedes Token. Ein Vektor mit, sagen wir, 768 Dimensionen, der die Bedeutung des Wortes kodiert.
Aus diesem einen Embedding macht das Modell drei verschiedene Sichten:
- Das Embedding wird mit W_Q multipliziert, das gibt den Query-Vektor q
- Das Embedding wird mit W_K multipliziert, das gibt den Key-Vektor k
- Das Embedding wird mit W_V multipliziert, das gibt den Value-Vektor v
W_Q, W_K und W_V sind drei Gewichtsmatrizen die das Netz lernt. Sie sind nichts Mystisches, einfach drei lineare Schichten wie aus Artikel 3, ohne Aktivierung.
Drei Projektionen aus einem Embedding. Und genau das ist der Punkt. Das Wort hat eine Bedeutung, kann aber drei verschiedene Rollen einnehmen, je nachdem wer es betrachtet. Als Query fragt es: was suche ich gerade? Als Key sagt es: das hier biete ich an, find mich, wenn du das brauchst. Als Value liefert es: so sehe ich aus, wenn du mich nimmst.
Beim ersten Lesen wirkt das künstlich. Warum drei Rollen? Warum nicht einfach das Embedding direkt für alles benutzen? Die Antwort ist pragmatisch. Die drei Projektionen geben dem Netz Freiheitsgrade. Das Wort Bank kann als Query etwas anderes signalisieren („ich brauche Kontext über das was rechts und links steht") als es als Key signalisiert („ich bin ein Substantiv mit zwei möglichen Bedeutungen") als es als Value liefert („meine wahrscheinlichen Inhalte sind Geld oder Sitzgelegenheit"). Drei Sichten, drei spezialisierte Funktionen, alle aus demselben Embedding herausgewachsen, im Training gemeinsam optimiert.
Die Rechnung in vier Schritten
Genug Metapher, jetzt der konkrete Ablauf. Selbst-Attention für eine ganze Sequenz, einmal durchgerechnet. Die ganze Operation passt in ein paar Zeilen Python.
import numpy as np
def softmax(x, axis=-1):
x = x - x.max(axis=axis, keepdims=True)
e = np.exp(x)
return e / e.sum(axis=axis, keepdims=True)
def self_attention(X, W_Q, W_K, W_V):
"""Ein einzelner Self-Attention-Schritt."""
Q = X @ W_Q # (n, d_k)
K = X @ W_K # (n, d_k)
V = X @ W_V # (n, d_v)
scores = Q @ K.T / np.sqrt(Q.shape[-1]) # (n, n)
weights = softmax(scores, axis=-1) # (n, n)
output = weights @ V # (n, d_v)
return output, weights
Sechs Zeilen, drei davon einfache Matrix-Multiplikationen. Was passiert hier? Schritt für Schritt:
Schritt 1, drei Projektionen. Aus der Eingabe X (eine Matrix mit einer Zeile pro Token) werden die drei Sichten Q, K und V berechnet. Jede ist wieder eine Matrix mit einer Zeile pro Token.
Schritt 2, Ähnlichkeiten messen. Q @ K.T ist eine Matrix mit n × n Einträgen. Eintrag (i, j) ist das Skalarprodukt zwischen dem Query von Token i und dem Key von Token j. Skalarprodukt heißt: hoher Wert wenn die Vektoren in eine ähnliche Richtung zeigen, niedriger Wert wenn sie wenig miteinander zu tun haben. Das ist die Bibliotheks-Suche, jedes Token vergleicht seinen Query mit jedem Key.
Schritt 3, normalisieren mit Softmax. Die rohen Ähnlichkeiten werden zeilenweise durch Softmax in eine Wahrscheinlichkeitsverteilung umgewandelt. Pro Zeile summieren sich die Gewichte zu 1. Das Ergebnis ist die Attention-Matrix: für jedes Token (Zeile) die Anteile, mit denen es alle anderen Tokens (Spalten) berücksichtigt.
Schritt 4, gewichtete Mischung der Values. weights @ V mischt die Value-Vektoren entsprechend. Token i bekommt eine gewichtete Summe der Values aller anderen Tokens, gewichtet mit den Aufmerksamkeitsanteilen aus Schritt 3.
Das war’s. Mehr ist Self-Attention nicht.
Ein Wort zur Skalierung mit √d_k in Schritt 2: Bei großen Dimensionen wachsen Skalarprodukte im Schnitt mit, und die Softmax-Verteilung wird zu spitz, fast nur Nullen und Einsen, was den Gradienten beim Training abwürgt. Dividieren durch √d_k hält die Werte in einem trainierbaren Bereich.
Die Attention-Formel, kompakt
In der Originalarbeit wird der ganze Vorgang in einer Zeile zusammengefasst:
Attention(Q, K, V) = softmax(Q·Kᵀ / √d_k) · V
d_k ist die Dimension der Query- und Key-Vektoren. Q hat Dimension (n, d_k), K hat (n, d_k), V hat (n, d_v). Das Ergebnis hat Dimension (n, d_v), also genauso viele Tokens wie die Eingabe, nur mit neuen, kontextualisierten Vektoren.
Die Skalierung mit √d_k kommt aus folgender Beobachtung: Wenn die Komponenten von Q und K zufällig mit Mittelwert 0 und Varianz 1 sind, hat das Skalarprodukt q · k eine Varianz von d_k. Bei großen d_k werden die Werte vor Softmax also automatisch sehr groß, was die Gradienten der Softmax fast auf Null drückt. Das Teilen durch √d_k zieht die Standardabweichung zurück auf 1, der Softmax bleibt trainierbar.
Backpropagation funktioniert ohne Sondertricks. Die Operation ist eine Verkettung aus Matrix-Multiplikationen und Softmax, alles differenzierbar. Die Gradienten fließen durch Q, K und V und landen schließlich auf W_Q, W_K, W_V.
Ein Beispiel, „Die Bank am Fluss war alt"
Schauen wir, was die Attention-Matrix für einen konkreten Satz aussagt. Nehmen wir einen Satz, der auf das Wort Bank aus Artikel 5 zurückgreift:
Die Bank am Fluss war alt.
Sechs Tokens. Wenn wir Self-Attention auf diesem Satz laufen lassen (mit einem trainierten Modell), bekommen wir eine 6 × 6 Matrix von Aufmerksamkeitsanteilen. Jede Zeile ist ein Token, jede Spalte zeigt wieviel dieses Token zur Bedeutung der Zeile beiträgt.
Was wir in einem ordentlich trainierten Modell beobachten würden: Die Zeile für Bank hat hohe Werte bei Fluss und alt, niedrige bei Die und war. Das Modell hat gelernt, dass Fluss die entscheidende Information für die richtige Bedeutung von Bank ist. Es zieht den semantischen Kontext direkt aus dem Satz, nicht durch einen sequentiellen Notizblock, sondern durch eine einzige Matrix-Multiplikation.
Genau das ist die Pointe. Im RNN hätte die Information Fluss erst durch am und dann zum nächsten Schritt fließen müssen, durch zwei Anwendungen von W_hh, immer mit Verlust. In Self-Attention kommt sie in einem Schritt an.
Die Visualisierung zeigt das. Helle Felder bedeuten hohe Aufmerksamkeit, dunkle bedeuten niedrige. In der Praxis sehen Attention-Matrizen aus wie Punktwolken, manche Tokens schauen auf manche bestimmten anderen, und die Muster geben einen Hinweis darauf was das Modell „verstanden" hat.
Self-Attention vs Cross-Attention
Was wir bis hier beschrieben haben, ist Self-Attention. Q, K und V kommen alle aus derselben Sequenz. Jedes Token schaut auf jedes andere Token im selben Satz.
Es gibt aber auch Cross-Attention. Da kommen Q und K/V aus verschiedenen Sequenzen. Die klassische Anwendung ist maschinelle Übersetzung. Der Encoder verarbeitet den deutschen Quellsatz und produziert Embeddings. Der Decoder generiert den englischen Zielsatz, Token für Token. Beim Generieren jedes englischen Tokens fragt der Decoder mit seinem Query in den Encoder-Output rein, K und V kommen also vom Encoder, Q vom Decoder. So zieht der Decoder gezielt das aus dem Quellsatz was er gerade braucht.
Self und Cross sind exakt dieselbe Rechnung. Der Unterschied ist nur, woher die drei Vektoren stammen. Q und K/V aus demselben Tensor, oder aus zwei verschiedenen.
In modernen Sprachmodellen wie GPT, Claude oder Llama spielt Self-Attention die Hauptrolle. Cross-Attention findet sich vor allem in Encoder-Decoder-Modellen wie T5 oder den ursprünglichen Transformern für Übersetzung.
Multi-Head, mehrere Suchen parallel
Eine einzige Attention-Operation ist mächtig, aber sie macht nur eine Sache gleichzeitig. Eine Suche, ein Set Q/K/V-Projektionen.
Sprache ist aber komplex. Beim Wort Bank wollen wir vielleicht gleichzeitig wissen: was ist das semantisch (Geld oder Sitzgelegenheit), wie ist die Syntax (Subjekt oder Objekt), gibt es eine Fernreferenz (welches Pronomen verweist gleich darauf zurück). Eine einzige Attention-Operation muss all das in einer Suche zusammenpacken. Das geht, aber es ist eng.
Multi-Head Attention löst das, indem es mehrere Attention-Operationen parallel ausführt. Statt einem Set W_Q, W_K, W_V hat das Modell h Sets, jedes mit kleineren Dimensionen. Jeder Head macht seine eigene Suche, mit seinen eigenen Q/K/V-Projektionen. Anschließend werden die Ergebnisse aller Heads aneinandergehängt und durch eine letzte Projektion W_O auf die ursprüngliche Dimension zurückgeführt.
def multi_head_attention(X, heads):
"""Multi-Head Attention, vereinfacht."""
head_outputs = []
for W_Q, W_K, W_V in heads:
out, _ = self_attention(X, W_Q, W_K, W_V)
head_outputs.append(out)
concat = np.concatenate(head_outputs, axis=-1)
return concat @ W_O
Konzeptionell ist das alles. h parallele, kleinere Self-Attentions, deren Ergebnisse am Ende wieder zu einem Vektor zusammengeführt werden.
Warum spezialisieren sich die Heads überhaupt?
Auf den ersten Blick wirkt das seltsam. Alle Heads bekommen die gleiche Eingabe. Warum sollten sie verschiedene Dinge lernen? Sollten sie nicht alle dasselbe tun?
Die Antwort liegt in zwei Mechanismen, die zusammenwirken:
1. Unterschiedliche Startpunkte. Jeder Head bekommt seine eigenen Gewichtsmatrizen, alle drei pro Head, also W_Q, W_K, W_V. Beim Initialisieren werden diese Matrizen mit kleinen Zufallszahlen befüllt, jeweils anders. Die Heads starten also schon an unterschiedlichen Stellen im Lösungsraum.
2. Geteilte Verantwortung beim Lernen. Die Outputs aller Heads werden aneinandergehängt und gehen gemeinsam in die Weiterverarbeitung. Wenn zwei Heads im Training versuchen würden dasselbe Muster zu lernen, wäre einer von beiden überflüssig, der Loss würde sich nicht weiter senken lassen. Das Backpropagation-Signal drückt die Heads automatisch in unterschiedliche Nischen, weil das die Loss-Funktion am stärksten reduziert. Niemand sagt ihnen: „du machst Subjekt-Verb-Beziehungen". Sie finden ihre Aufgaben selbst, weil Verteilung besser funktioniert als Doppelung.
Wie sehen diese Spezialisierungen konkret aus?
Visualisierungen aus dem Originalpaper und Tools wie BertViz machen das sichtbar. Hier vier typische Muster, die in trainierten Transformern beobachtet werden, an demselben Beispielsatz:
Was wir sehen: Head 1 hat hauptsächlich helle Felder dort wo Subjekte und Verben stehen, Anna → sah, sie → streichelte. Head 2 löst Pronomen auf, sie → Anna, ihn → Hund. Head 3 zeigt eine Diagonal-Struktur, jedes Token attendiert vor allem auf direkte Nachbarn (das ist nützlich um lokale Phrasen zu erkennen). Head 4 hat verstreute, weit entfernte Bezüge, das Verb streichelte verbindet sich zum Beispiel mit ihn am Ende des Satzes.
Diese Muster sind nicht hart einprogrammiert. Sie tauchen während des Trainings einfach auf, weil sie sich auszahlen. Genau das macht Multi-Head Attention so mächtig, das Netz bekommt eine Art automatische Linguistik geschenkt, ohne dass jemand sagen müsste was Subjekt-Verb-Beziehungen oder Koreferenzen sind.
In modernen LLMs sind 32, 64 oder mehr Heads üblich. Llama 3 405B hat 128 Heads pro Schicht. Jede Schicht hat damit hunderte parallele Suchen, durch dutzende Schichten gestapelt. Daraus entsteht die enorme Repräsentationskraft moderner Modelle.
Causal Masking, nicht in die Zukunft schauen
Eine wichtige Einschränkung für Sprachmodelle: Wenn wir das nächste Wort vorhersagen wollen, dürfen wir natürlich nicht in die Zukunft schauen. Bei der Vorhersage von Token 5 darf das Modell nur die Tokens 1 bis 4 sehen, nicht 6, 7, 8.
Das wird mit einer einfachen Maske vor dem Softmax gelöst. Alle Einträge der Score-Matrix die in die Zukunft zeigen, also alle Einträge oberhalb der Hauptdiagonale, werden auf minus unendlich gesetzt. Nach Softmax werden daraus exakt Nullen, das Token bekommt also keine Aufmerksamkeit auf Tokens die nach ihm kommen.
# Untere Dreiecksmatrix, Einsen unten, Nullen oben
mask = np.tril(np.ones((n, n)))
scores = np.where(mask == 1, scores, -np.inf)
weights = softmax(scores, axis=-1)
Wann spielt die Maske eine Rolle?
Die Maske ist nicht nur ein Inferenz-Sicherheitsnetz, sie ist vor allem eine Trainings-Notwendigkeit.
Beim Training: zwingend erforderlich. Hier verarbeitet das Modell den ganzen Satz parallel in einem einzigen Forward-Pass. Für jede Position wird gleichzeitig vorhergesagt, welches Token als nächstes kommt, und der Loss aggregiert über alle Positionen. Ohne Maske würde Position 1 (Anna) bereits Position 2 (sah) sehen können, das Modell würde die Antwort einfach abschreiben statt sie vorherzusagen. Genau diese parallele Verarbeitung ist der Grund, warum Transformer überhaupt so effizient trainierbar sind, vor 2017 musste man mit RNNs Token für Token sequenziell durchlaufen. Die Maske ist also der Trick, der Parallelität und Kausalität gleichzeitig erlaubt.
Bei der Inferenz: technisch redundant, praktisch trotzdem da. Beim Generieren schreibt das Modell Token für Token, die Zukunft existiert noch gar nicht. Logisch bräuchte man die Maske nicht. Sie bleibt aber drin, einerseits für Konsistenz mit dem Trainingsverhalten (sonst stimmen die gelernten Gewichte numerisch nicht mehr), andererseits weil auch bei Inferenz parallele Verarbeitung vorkommt: Beim Prompt-Prefill ("Schreibe mir ein Gedicht über...") wird der ganze Prompt auf einmal in den KV-Cache geladen, und beim Batching mit gepaddeten Sequenzen muss das Padding ohnehin maskiert werden.
Das ist der einzige Unterschied zwischen einem Encoder-Modell wie BERT (sieht den ganzen Satz, kein Mask) und einem Decoder-Modell wie GPT (sieht nur Vergangenheit, mit Mask). Und das ist auch der Grund warum GPT-artige Modelle sich für autoregressive Textgenerierung eignen, jedes Token wird strikt nur aus dem berechnet was vor ihm kommt.
Was Attention konkret löst
Wir haben jetzt alle Bausteine. Was hat Attention gegenüber RNNs konkret verändert?
Lange Abhängigkeiten sind direkt zugänglich. Im RNN musste Information zwischen zwei weit entfernten Tokens durch alle Zwischenschritte fließen, bei jedem mit Verlust. In Attention reicht eine einzige Matrix-Operation, egal wie weit zwei Tokens auseinander stehen. Distanz spielt keine Rolle mehr.
Parallelisierung wird natürlich. Self-Attention für die ganze Sequenz ist eine einzige große Matrix-Multiplikation, perfekt für GPUs. Während ein RNN bei einer Sequenz aus 1000 Tokens 1000 sequentielle Schritte braucht, schafft Self-Attention das in einem Rutsch. Training und Inferenz beschleunigen sich um Größenordnungen, was Modelle mit Milliarden Parametern überhaupt erst trainierbar gemacht hat.
Interpretierbarkeit als Bonus. Die Attention-Matrix ist auslesbar. Anders als der Hidden State eines RNN, der eine schwer durchschaubare gemischte Brühe ist, kann man bei Attention direkt sehen welches Token wohin geschaut hat. Das ist nicht perfekt, Aufmerksamkeit ist nicht dasselbe wie Verstehen, aber es ist ein Fenster, das RNNs nicht hatten.
Der Preis: Quadratisch in der Sequenzlänge. Q · Kᵀ ist eine n × n Matrix. Bei 1000 Tokens sind das eine Million Einträge, bei 100.000 Tokens (Long Context) sind es zehn Milliarden. Speicher und Rechenzeit wachsen quadratisch. Das ist der Hauptgrund warum „Long Context" in modernen Modellen ein eigenes Forschungsgebiet ist (Stichworte: Flash Attention, Sliding Window Attention, State Space Models wie Mamba).
Was noch fehlt
Wir haben jetzt einen Mechanismus, mit dem jedes Token jedes andere im Kontext sehen kann. Das alleine ist allerdings noch kein Sprachmodell. Drei Dinge fehlen:
Position. Self-Attention selbst ist permutationsinvariant. Wenn wir die Tokens vertauschen, ändert sich am Ergebnis fast nichts. Aber Sprache lebt von Reihenfolge. „Hund beißt Mann" ist nicht „Mann beißt Hund". Damit das Modell Reihenfolge versteht, brauchen wir Positional Encodings, eine Information die jedem Embedding mitteilt: Du bist Token 1, du bist Token 2, du bist Token 17.
Tiefe. Eine einzelne Attention-Schicht macht eine Mischung. Aber komplexe Sprache braucht mehrere Mischungen hintereinander. Reale Sprachmodelle stapeln dutzende Transformer-Blöcke, jeder mit einer Attention- und einer Feed-Forward-Schicht.
Stabilität. Tiefe Stapel kämpfen mit Vanishing Gradients aus Artikel 5. Die Lösung sind Residual Connections und Layer Normalization, beides aus dem ResNet-Umfeld übernommen.
Diese drei Zutaten zusammen mit Attention machen einen Transformer. Das ist das Thema von Artikel 7. Wir bauen einen vollständigen Transformer-Block, stapeln mehrere zu einem echten Sprachmodell und schauen wie aus diesem Konstrukt GPT, BERT, Llama und Claude wurden.
Alle Artikel der Serie
- Das nächste Wort, wie Sprachmodelle funktionieren
- Wörter als Punkte im Raum, was Embeddings wirklich sind
- Neuronale Netze von Grund auf
- Backpropagation, wie ein Modell lernt
- Kontext und RNNs, warum Reihenfolge zählt
- Attention, der Mechanismus der alles veränderte ← dieser Artikel
- Der Transformer, die vollständige Architektur (erscheint demnächst)
- Fine-Tuning, vom Basismodell zum Assistenten (erscheint demnächst)
Serie: Wie LLMs funktionieren · rotecodefraktion.de