English· Español· Deutsch· Nederlands· Français· 日本語· ქართული· 繁體中文· 简体中文· Português· Русский· العربية· हिन्दी· Italiano· 한국어· Polski· Svenska· Türkçe· Українська· Tiếng Việt· Bahasa Indonesia

un

Gast
1 / ?

Attention plus MLP, wiederholt

Transformer Block Pre-Norm Structure


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


Variantn_layern_headd_modelmlp_dim
ANDREA-12M6123841536
ANDREA-120M12127683072
ANDREA-480M162415366144

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

Ein Transformer-Block enthält zwei Sublayers. Nennen Sie sie in der Reihenfolge und geben Sie für jeden an, ob er Informationen über Positionen hinweg bewegt (token-zu-token) oder Informationen innerhalb einer einzelnen Position transformiert (unabhängig pro Token). Ein Satz pro Sublayer.

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

ANDREA verwendet Pre-Norm: `x = x + Attention(LayerNorm(x))`. Vergleichen Sie mit Post-Norm: `x = LayerNorm(x + Attention(x))`. Nennen Sie einen Grund aus Sicht des Gradientenflusses, warum Pre-Norm in tiefen Stapeln stabiler trainiert als Post-Norm. Beziehen Sie sich in Ihrer Antwort auf den Residualstrom.

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

ANDREA-120M hat `d_model=768`, `mlp_dim=3072` & `n_layer=12`. Berechne die Gesamtparameter in den MLP-Gewichtsmatrizen (`W_1` & `W_2`) über alle 12 Blöcke hinweg. Ignoriere Biases. Zeige deine Rechenoperationen. Gib dann an, welchen Anteil dies an den ~120M Gesamtparametern von ANDREA-120M ausmacht (auf eine Dezimalstelle gerundet).

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


KomponentePro BlockÜber 12 Blöcke
Attention-Projektionen (4×W)2,36M28,3M
MLP-Gewichte (W_1 + W_2)4,72M56,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.

Ein Token durch ein Block verfolgen

Ein 768-dim Token-Vektor betritt Block 7 von ANDREA-120M. Beschreibe, was mit ihm im Inneren des Blocks passiert (in Pre-Norm-Struktur). Erwähne: beide Layer Norms, beide Sublayer, beide Residual-Additionen & die finale Form. Nenne mindestens eine Stelle, an der der Residual-Stream unberührt bleibt & eine Stelle, an der er modifiziert wird.