microGPT-Walkthrough: Wie ein minimales GPT funktioniert (optional)¶
Dieses Kapitel ist eine optionale Vertiefung für alle, die sehen möchten, wie ein minimales GPT-ähnliches Modell (im Stil von Andrej Karpathys minGPT/nanoGPT) grob aufgebaut ist. Es geht um Konzepte und ausgewählte Code-Ausschnitte – keine vollständige Implementierung. Nach dem Lesen versteht man die Schritte: Text → Tokens → Token-Embeddings → Transformer-Blöcke → Next-Token-Prediction.
Ziel und Vorwissen¶
Ziel: Die zentralen Bausteine eines kleinen decoder-only Sprachmodells erkennen (Embedding, Attention, LM-Head) und in echtem Code wiederfinden.
Vorwissen: Die Kapitel LLM-Intuition, Tokenisierung und LLM-Interna liefern die konzeptionelle Basis; hier geht es um den Ablauf in Code.
Hinweis: Eine vollständige, lauffähige Implementierung würde den Rahmen sprengen. Interessierte können Karpathys nanoGPT-Repository studieren; hier werden nur Schlüsselstellen hervorgehoben.
Überblick: Vom Text zum nächsten Token¶
Ein minimales GPT durchläuft grob diese Schritte:
Text → Token-IDs → Token-Embeddings → Transformer-Blöcke → Logits → nächster Token
Im Folgenden gehen wir jeden Schritt mit Intuition und ausgewählten Code-Fragmenten an.
Schritt 1: Text → Tokens (Tokenisierung)¶
Intuition: Der Rohtext wird in eine Folge von Token-IDs zerlegt (siehe Tokens und Tokenisierung). Das Modell arbeitet nur mit Zahlen.
Typischer Code (konzeptionell): Ein Tokenizer liefert eine Liste von IDs.
# Pseudocode / vereinfacht – z. B. mit tiktoken oder Hugging Face Tokenizer
tokenizer = load_tokenizer("gpt2") # oder ein anderer
text = "Das Modell sagt das nächste"
token_ids = tokenizer.encode(text) # z. B. [1234, 567, 890, 234, 567]
Diese token_ids sind die Eingabe für das Modell.
Schritt 2: Token-Embeddings¶
Intuition: Jede Token-ID wird in einen Vektor (Embedding) einer festen Dimension übersetzt. So wird aus der diskreten ID eine kontinuierliche Darstellung, mit der das Netz rechnen kann.
Typischer Code (PyTorch-Stil): Ein eingebautes Embedding-Layer.
import torch.nn as nn
# vocab_size = Größe des Wortschatzes, n_embd = Embedding-Dimension (z. B. 768)
self.token_embedding_table = nn.Embedding(vocab_size, n_embd)
# Eingabe: (batch, seq_len) → Ausgabe: (batch, seq_len, n_embd)
tok_emb = self.token_embedding_table(token_ids)
Zusätzlich braucht das Modell Positionsinformationen, weil der Transformer von sich aus keine Reihenfolge kennt (siehe LLM-Interna (optional): Konzepte der Transformer-Architektur). Oft ein weiteres Embedding-Layer für Positionen:
self.position_embedding_table = nn.Embedding(block_size, n_embd)
pos_emb = self.position_embedding_table(torch.arange(seq_len))
x = tok_emb + pos_emb # (batch, seq_len, n_embd)
# x geht in die Transformer-Blöcke
return x
Schritt 3: Transformer-Blöcke (Attention + MLP)¶
Intuition: Die eingebettete Folge durchläuft mehrere Decoder-Blöcke. Jeder Block enthält typischerweise:
Causale (masked) Self-Attention: Jede Position darf nur auf vorherige Positionen (und sich selbst) schauen – so entsteht die autoregressive „von links nach rechts“-Struktur.
Feed-Forward (MLP): Eine kleine zweilagige Netzwerk-Schicht pro Position.
Die Details (Query, Key, Value, Multi-Head) stehen in LLM-Interna (optional): Konzepte der Transformer-Architektur und Theorie (Mathematik) hinter Generativer KI. Hier nur eine Struktur als Orientierung:
class Block(nn.Module):
def __init__(self, n_embd, n_head):
super().__init__()
self.sa = CausalSelfAttention() # masked, decoder-only
self.ffwd = nn.Sequential(
nn.Linear(n_embd, 4 * n_embd),
nn.GELU(),
nn.Linear(4 * n_embd, n_embd),
)
def forward(self, x):
x = x + self.sa(x) # Residual
x = x + self.ffwd(x)
return x
Die Eingabe x hat die Form (batch, seq_len, n_embd); nach allen Blöcken hat sie dieselbe Form.
Schritt 4: Vom letzten versteckten Zustand zu Logits (LM-Head)¶
Intuition: Am Ende der Blöcke haben wir pro Position einen Vektor der Dimension n_embd. Das Modell soll aber eine Wahrscheinlichkeit pro Token im Wortschatz ausgeben – also „welcher Token kommt als Nächstes?“ Dafür wird die letzte Position (oder der letzte versteckte Zustand) durch eine lineare Schicht auf vocab_size viele Logits abgebildet.
self.lm_head = nn.Linear(n_embd, vocab_size, bias=False)
logits = self.lm_head(x[:, -1, :]) # nur letzte Position: (batch, vocab_size)
# logits[i] = „Score“ für Token i als nächstes Token
probs = F.softmax(logits, dim=-1)
next_token_id = torch.argmax(probs, dim=-1) # oder Sampling aus probs
return logits, next_token_id
Next-Token-Prediction: Beim Training vergleichen wir logits mit dem tatsächlich nächsten Token (Cross-Entropy-Verlust). Beim Generieren wählen wir den nächsten Token (argmax oder Sampling), hängen ihn an die Eingabe an und wiederholen – so entsteht Token für Token der generierte Text.
Zusammenfassung der Datenfluss-Kette¶
Text
→ Tokenizer: token_ids (batch, seq_len)
→ token_embedding_table: (batch, seq_len, n_embd)
→ + position_embedding
→ N × Block(Attention + MLP): (batch, seq_len, n_embd)
→ lm_head auf letzte Position: (batch, vocab_size) = Logits
→ softmax → Wahrscheinlichkeiten → nächster Token (argmax oder Sampling)
→ Anhängen an Eingabe, wiederholen → generierter Text
Warum „minimal“?¶
Ein vollständiges GPT enthält zusätzlich: Layer-Norm, mehrere Köpfe in der Attention, Dropout, korrekte Maskierung, Tokenizer-Details, Training-Loop, Lernraten-Scheduling usw. Hier sollten nur die konzeptionellen Kerne sichtbar werden – Embedding, Block-Struktur, LM-Head und Next-Token-Prediction.
Weiterführende Ressourcen¶
nanoGPT (Karpathy): Kleine, gut lesbare Implementierung auf GitHub – ideal zum Selbststudium und zum Nachvollziehen jeder Zeile.
LLM-Interna (LLM-Interna (optional): Konzepte der Transformer-Architektur): Konzepte von Attention und Decoder-only ohne Code-Tiefgang.
Theorie hinter Generativer KI (Theorie (Mathematik) hinter Generativer KI): Mathematische Details zu Attention und Optimierung.
Nächster Schritt¶
Wer die mathematische Seite (Attention-Formeln, Wahrscheinlichkeitsmodell) vertiefen möchte, findet sie in Theorie (Mathematik) hinter Generativer KI. Für die Nutzung von LLMs in Produkten reicht das hier Gelernte zusammen mit dem Rest des GenAI-Lernpfads (Embeddings, RAG, Prompting).