Attention plus MLP, wiederholt
Zwei Unterebenen
Ein Transformer-Block enthält genau zwei Unterebenen, die jeweils auf eine Token-Sequenz der Form [batch, seq_len, d_model] operieren:
1. Multi-Head-Attention-Unterlayer. Tokens schauen sich gegenseitig an. Aktivität 68 hat das im Detail behandelt. Ausgabeförmat entspricht dem Eingabeförmat.
2. Feed-Forward-MLP-Unterlayer. Jeder Token wird unabhängig durch ein zweischichtiges Perzeptron transformiert. Kein Informationsfluss zwischen Tokens. Ausgabeförmat entspricht dem Eingabeförmat.
Beide Unterlayer erhalten die Form [batch, seq_len, d_model]. Diese Erhaltung ermöglicht das Stapeln der Blöcke: Die Ausgabe von Layer N speist die Eingabe von Layer N+1 ohne Formakrobatik.
Was Jeder Unterlayer Beiträgt
Attention verschiebt Information über Positionen hinweg: Ein Token an Position 17 kann Information von Positionen 1 bis 16 ziehen. Der MLP transformiert Information innerhalb jeder Position: Die Repräsentation eines Tokens wird durch gelernte nichtlineare Funktionen umgeformt, sieht aber seine Nachbarn nie.
Das Stapeln von Blöcken wechselt abwechselnd zwischen diesen zwei Operationen. Die Attention der Schicht 1 mischt Positionen. Der MLP der Schicht 1 formt pro Position um. Die Attention der Schicht 2 mischt erneut, nun über den umgeformten Repräsentationen. Dieser Wechsel steigert die Expressive Power mit der Tiefe.
ANDREA's Stack
| Variant | n_layer | n_head | d_model | mlp_dim |
|---|---|---|---|---|
| ANDREA-12M | 6 | 12 | 384 | 1536 |
| ANDREA-120M | 12 | 12 | 768 | 3072 |
| ANDREA-480M | 16 | 24 | 1536 | 6144 |
Beachten Sie mlp_dim = 4 × d_model in der gesamten Familie. Dieses Verhältnis gilt in fast jedem modernen Transformer. Abschnitt 3 erklärt, warum.
Benennung der Sublayers
Warum Skip-Verbindungen wichtig sind
Das Residualmuster
Jeder Sublayer umschließt seine Berechnung in einer Residualverbindung. Die Ausgabe addiert den Eingang wieder hinzu:
x = x + Attention(LayerNorm(x)) # Attention-Sublayer
x = x + MLP(LayerNorm(x)) # MLP-Unterschicht
Innerhalb jeder Unterschicht erzeugt die Funktion Attention(...) oder MLP(...) ein Delta. Der Block ersetzt nicht den Eingang; er addiert eine gelernte Korrektur.
Warum das wichtig ist
Drei Gründe, warum Residualverbindungen tiefe Architekturen dominieren:
1. Gradientenfluss. Während der Rückwärtspropagation gibt die Addition den Gradienten einen direkten Pfad vom Ausgang zurück zum Eingang, der die Untereinheit umgeht. Ein 12-Schicht-Stapel ohne Residuums würde das Gradientensignal lange vor Erreichen der Einbettungen verlieren; mit Residuums bleibt die Gradientengröße durch Hunderte von Schichten nutzbar.
2. Identitätsinitialisierung. Bei der Initialisierung erzeugen die Gewichte der Untereinheit kleine Ausgaben. Die Residuumverbindung bedeutet, dass Schicht N zunächst fast unverändert durchgeht. Das Training lernt Deltas schrittweise von einem funktionsfähigen Ausgangspunkt aus.
3. Kompositionelles Lernen. Jeder Block lernt eine Verfeinerung, keine Ersetzung. Schicht 1 könnte positionsbezogene Informationen hinzufügen. Schicht 2 könnte syntaktische Struktur hinzufügen. Schicht 7 könnte semantische Beziehungen hinzufügen. Der Residuumstrom akkumuliert.
Layer-Normalisierung
Vor jeder Untereinheit skaliert LayerNorm die Repräsentation jedes Tokens auf Nullmittelwert & Einheitsvarianz, dann wendet es gelernte Gewinne & Bias pro Feature an:
y = gamma * (x - mean(x)) / sqrt(var(x) + epsilon) + beta
Mittelwert & Varianz werden über die d_model-Dimension berechnet, getrennt für jedes Token. Zwei gelernte Vektoren (gamma, beta, jeweils Form [d_model]) stellen die expressive Skala wieder her. Die Normalisierung hält die Aktivierungen in einem numerisch stabilen Bereich; ohne sie verschlimmern sich kleine Trainingsinstabilitäten zu NaN-Gradienten.
Pre-Norm vs Post-Norm
Eine subtile, aber kritische Wahl
Zwei Möglichkeiten, Layer Norm in eine Residual-Schicht einzubinden:
Post-norm (originaler 2017-Paper):
x = LayerNorm(x + Attention(x))
Layer Norm sitzt nach der Residual-Addition. Der Residual-Stream selbst wird in jeder Schicht normalisiert.
Pre-Norm (modern standard, used in ANDREA):
x = x + Attention(LayerNorm(x))
Layer norm sitzt vor dem Sub-Layer, innerhalb des Residualzweigs. Der Residualstrom bleibt unnormalisiert; nur der Input zum Sub-Layer wird reskaliert.
Warum Pre-Norm gesiegt hat
Post-Norm trainiert schlecht ohne LR-Warmup & sorgfältiges Hyperparameter-Tuning. Gradienten explodieren in frühen Layern, weil jede Layer-Norm den akkumulierten Zustand des Residualstroms durcheinanderbringt. Das Originalpapier von 2017 verwendete Post-Norm mit umfangreichem Tuning; nachfolgende Arbeiten (GPT-2, LLaMA, ANDREA) standardisierten auf Pre-Norm.
Pre-Norm trainiert stabil. Der Residualstrom akkumuliert sauber über alle Schichten hinweg; nur die Eingaben der Unterschichten werden für numerische Stabilität normalisiert. Moderne Transformer setzen standardmäßig auf Pre-Norm, & ANDREA übernimmt diese Wahl.
Final Block Equation
Kombination von Residuals, Layer Norm in Pre-Norm-Position & beiden Unterschichten ergibt ANDREA's vollständigen Block:
def block_forward(x):
x = x + Attention(LayerNorm(x)) # Attention-Sublayer
x = x + MLP(LayerNorm(x)) # MLP-Sublayer
return x
Zwei Sublayer, zwei residuelle Additionen, zwei Layer-Normalisierungen (Hinweis: jeder Sublayer hat seine eigene Layer-Norm; ANDREA-120M hat 24 Layer-Norms über 12 Blöcke plus eine finale am Ausgang, also insgesamt 25). 12-mal wiederholen. Das ist der Rumpf von ANDREA-120M.
Warum Pre-Norm das Training stabilisiert
Zwei Lineare Schichten, Eine Aktivierung
Drei Operationen
Die MLP-Unterschicht ist ein zweischichtiges Perzeptron mit einer nichtlinearen Aktivierung zwischen den Schichten:
def mlp_forward(x):
h = x · W_1 + b_1 # erweitern: d_model → mlp_dim
h = GELU(h) # nichtlineare Aktivierung
y = h · W_2 + b_2 # verkleinern: mlp_dim → d_model
return y
Drei Operationen. Zwei lineare, eine nicht-lineare. Die erste lineare erweitert die Breite; die zweite zieht sie wieder zusammen.
Das 4× Erweiterungsverhältnis
Moderne Transformer setzen mlp_dim = 4 × d_model. ANDREA-120M:
d_model = 768
mlp_dim = 4 × 768 = 3072
W_1 shape = [768, 3072] # ~2.36M Parameter
W_2 shape = [3072, 768] # ~2.36M Parameter
MLP-Parameter pro Block = 4,72M (Biases ignoriert)
Zwei MLPs sitzen zwischen jedem Paar von Attention-Sublayern (eines pro Block). Zwölf Blöcke × 4,72M ≈ 56,6M MLP-Parameter insgesamt in ANDREA-120M, ungefähr die Hälfte aller Parameter.
Warum 4×
Das 4×-Verhältnis ergab sich empirisch. Kleinere Verhältnisse reduzieren die Modellkapazität. Größere Verhältnisse erzeugen abnehmende Renditen pro verbrauchtem Parameter. Über Jahrzehnte der Architektursuche hat sich 4× bewährt; es erscheint in GPT, BERT, T5, LLaMA & ANDREA.
Neuere Arbeiten (PaLM, Chinchilla) haben festgestellt, dass Gating-Mechanismen (SwiGLU) eine 8/3×-Erweiterung mit vergleichbarer Kapazität zu geringeren Kosten nutzen können; ANDREA bleibt aus Einfachheit bei klassischem GELU + 4×.
GELU: Eine glatte Aktivierung
Was GELU berechnet
GELU (Gaussian Error Linear Unit) ist die Standardaktivierung zwischen MLP-Schichten in modernen Transformern. Ihre Formel:
GELU(x) = x · Φ(x)
Φ(x) ist die kumulative Verteilungsfunktion der Standardnormalverteilung: die Wahrscheinlichkeit, dass eine standardnormalverteilte Zufallsvariable kleiner oder gleich x ist. Numerisch berechnet:
Φ(x) ≈ 0.5 × (1 + tanh(sqrt(2/π) × (x + 0.044715 × x³)))
Verhalten nach Region
- Für große positive x: Φ(x) ≈ 1, also GELU(x) ≈ x. Wie ReLU.
- Für große negative x: Φ(x) ≈ 0, also GELU(x) ≈ 0. Wie ReLU.
- Nahe x = 0: Φ(x) ≈ 0.5, also GELU(0) = 0 genau. Sanfter Übergang durch den Ursprung.
Im Gegensatz zu ReLU lässt GELU einige negative Eingaben durch, gewichtet mit Φ(x). Ein kleiner negativer Eingang trägt immer noch einen kleinen negativen Ausgang bei, nur weniger als der volle Eingang es tun würde.
Warum GELU ReLU übertrifft
Empirisch erreichen Transformer, die mit GELU trainiert wurden, einen niedrigeren Verlust als Transformer, die mit ReLU trainiert wurden, bei gleicher Parameteranzahl. Die Glattheit um Null herum ist entscheidend: Der harte Cutoff von ReLU bei Null erzeugt Gradienten-Unstetigkeiten; die glatte Kurve von GELU liefert sauberere Gradienten für die Rückwärtspropagation.
ANDREA's Training-Engine microgpt_cuda.cu enthält einen handgeschriebenen GELU-CUDA-Kernel. Der Kernel verwendet die obige tanh-Approximation; moderne GPUs enthalten tanh als Single-Instruction-Operation.
Berechnung der MLP-Parameter
Zwölf Blöcke bilden ANDREA-120M
Vom Block zum Modell
Der vollständige Forward-Pass von ANDREA-120M:
def model_forward(token_ids):
x = token_embed(token_ids) + position_embed(positions)
for block_idx in range(n_layer): # 12 Blöcke
x = block_forward(x) # Attention + MLP mit Residuals
x = LayerNorm(x) # finale Normierung
logits = x · token_embed.T # gemeinsame Gewichte für Ausgabprojektion
return logits
Sechs Zeilen. Der Großteil lebt in block_forward, das zwölfmal aufgerufen wird. Embeddings starten den Pipeline; gebundenes Unembedding (dieselbe Matrix, die für die Eingabe-Suche verwendet wird, transponiert für die Ausgabe-Projektion) beendet sie.
Tiefe als Komposition
Jeder Block liest den Residualstrom, berechnet ein Delta und addiert es zurück. Nach zwölf Durchläufen enthält der Strom die akkumulierten Beiträge von jedem Block. Intern tendieren die Schichten zur Spezialisierung:
- Frühe Schichten (1-3): syntaktische Muster, positionale Struktur
- Mittlere Schichten (4-8): Wortbeziehungen, Phrasengrenzen
- Späte Schichten (9-12): semantischer Inhalt, faktenbasiertes Abrufen
Diese Spezialisierung entsteht durch Trainingsdruck, nicht durch architektonische Entscheidungen. Dasselbe einheitliche Blockdesign erzeugt spezialisierte Schichten, wenn es auf Sprache trainiert wird.
Gesamtzahl der Block-Parameter
| Komponente | Pro Block | Über 12 Blöcke |
|---|---|---|
| Attention-Projektionen (4×W) | 2,36M | 28,3M |
| MLP-Gewichte (W_1 + W_2) | 4,72M | 56,6M |
| Layer Norms (gamma, beta) | ~3Tausend (vernachlässigbar) | ~37Tausend |
| Gesamt pro Block | ~7,1M | ~85M |
85M Parameter im Stamm. Plus ~13M in Token-Embeddings (8449 Vokabular × 768 d_model × 2 für gebundene Eingabe/Ausgabe) und eine finale Layer Norm, & ANDREA-120M landet bei ungefähr 120M Parametern. Das Block-Design macht zwei Drittel aus; Embeddings den Rest.