Zum Inhalt

Attention

Attention ist der Mechanismus, der Transformern erlaubt, Beziehungen zwischen allen Tokens in einer Sequenz zu lernen – unabhängig von ihrer Distanz. Er ist das Herzstück moderner LLMs.


Die Kernfrage

Bei der Verarbeitung eines Satzes muss das Modell wissen: Welche anderen Wörter sind für dieses Wort gerade relevant?

"Die Katze saß auf der Matte, weil sie müde war."
                        Worauf bezieht sich "sie"?

Attention beantwortet diese Frage, indem es Ähnlichkeiten zwischen Tokens berechnet.


Query, Key, Value

Die zentrale Idee: Jedes Token wird in drei Vektoren transformiert:

Vektor Funktion Analogie
Query (Q) "Was suche ich?" Deine Suchanfrage
Key (K) "Was biete ich?" Titel/Tags eines Dokuments
Value (V) "Was ist mein Inhalt?" Der eigentliche Content
Q = X @ W_q  # (seq_len, d_k)
K = X @ W_k  # (seq_len, d_k)
V = X @ W_v  # (seq_len, d_v)

Self-Attention Formel

Die Attention-Berechnung in einem Schritt:

\[ \text{Attention}(Q, K, V) = \text{[softmax](../grundlagen/aktivierungsfunktionen.md)}\left(\frac{QK^T}{\sqrt{d_k}}\right) V \]

Schritt für Schritt:

  1. Scores berechnen: \(QK^T\) – Wie ähnlich ist jede Query zu jedem Key?
  2. Skalieren: \(\div \sqrt{d_k}\) – Verhindert zu große Werte
  3. Softmax: Normalisiert zu Wahrscheinlichkeiten (summiert zu 1)
  4. Gewichtete Summe: Multipliziere mit Values
graph LR
    Q[Query] --> Dot[Q × Kᵀ]
    K[Key] --> Dot
    Dot --> Scale[÷ √d_k]
    Scale --> Soft[Softmax]
    Soft --> Mul[× V]
    V[Value] --> Mul
    Mul --> Out[Output]

Beispiel: Attention visualisiert

Für den Satz "Die Katze schläft":

Attention Matrix nach Softmax:

           Die    Katze  schläft
Die       [0.7    0.2    0.1  ]
Katze     [0.1    0.6    0.3  ]
schläft   [0.1    0.5    0.4  ]

→ "schläft" achtet stark auf "Katze" (0.5), weil das Verb sein Subjekt braucht.


Multi-Head Attention

Ein einzelner Attention-Kopf kann nur eine Art von Beziehung lernen. Multi-Head Attention verwendet mehrere parallele Attention-Köpfe:

\[ \text{MultiHead}(Q, K, V) = \text{Concat}(\text{head}_1, ..., \text{head}_h) W_O \]
heads = []
for i in range(num_heads):
    Q_i = X @ W_q[i]  # Eigene Projektion pro Head
    K_i = X @ W_k[i]
    V_i = X @ W_v[i]
    heads.append(attention(Q_i, K_i, V_i))

output = concat(heads) @ W_o

Was lernen verschiedene Heads?

  • Head 1: Syntaktische Beziehungen (Subjekt-Verb)
  • Head 2: Coreference (Pronomen → Bezugswort)
  • Head 3: Lokaler Kontext (benachbarte Wörter)
  • ...

Masked Attention (Causal)

Bei der Textgenerierung darf das Modell nur auf vorherige Tokens schauen – nicht in die Zukunft:

           Die    Katze  schläft
Die       [1.0    -∞     -∞   ]    ← Sieht nur sich selbst
Katze     [0.3    0.7    -∞   ]    ← Sieht "Die" und sich
schläft   [0.1    0.5    0.4  ]    ← Sieht alle vorherigen

Die \(-\infty\) Werte werden durch die Attention-Maske eingefügt und verschwinden nach dem Softmax.


Komplexität & Skalierung

Die Attention-Berechnung hat quadratische Komplexität:

\[ O(n^2 \cdot d) \]
Sequenzlänge Attention-Operationen
1.000 Tokens 1 Mio
10.000 Tokens 100 Mio
100.000 Tokens 10 Mrd

Das ist der Grund, warum lange Context Windows so teuer sind!


Optimierungen

Flash Attention

Memory-effiziente Implementierung, die IO-Bottlenecks umgeht:

  • Berechnet Attention in Blöcken
  • Reduziert VRAM-Nutzung drastisch
  • Standard in modernen Frameworks

Multi-Query Attention (MQA)

Keys und Values werden zwischen allen Heads geteilt:

# Standard: h Heads × (Q, K, V)
# MQA: h × Q, 1 × K, 1 × V

→ Weniger VRAM für KV-Cache, schnellere Inferenz

Grouped-Query Attention (GQA)

Kompromiss: Mehrere Heads teilen sich K/V (z.B. 8 Query-Heads, 2 KV-Heads)

→ Verwendet in Llama ⅔, Mistral


Attention vs. andere Mechanismen

Mechanismus Komplexität Globaler Kontext
Self-Attention O(n²)
RNN/LSTM O(n) Begrenzt
Convolution O(n) Lokal
State Space (Mamba) O(n)

Code: Attention from Scratch

import torch
import torch.nn.functional as F

def attention(Q, K, V, mask=None):
    d_k = Q.size(-1)

    # Scores berechnen
    scores = torch.matmul(Q, K.transpose(-2, -1)) / (d_k ** 0.5)

    # Maske anwenden (für causale Attention)
    if mask is not None:
        scores = scores.masked_fill(mask == 0, float('-inf'))

    # Softmax → Attention Weights
    weights = F.softmax(scores, dim=-1)

    # Gewichtete Summe der Values
    return torch.matmul(weights, V)

Siehe auch

  • Transformer – Die Gesamtarchitektur
  • Embedding – Input für Attention
  • KV-Cache – Attention beschleunigen
  • Parameter – Wo stecken die Gewichte?