Alle Visuals
llama.cpp · ggml · GGUF · Transformer · Quantisierung

VisualLLM-cpp

Was passiert eigentlich zwischen „Prompt eingetippt“ und „Token erscheint“? Diese App zerlegt die Inferenz von llama.cpp in ihre Module und lässt dich jeden einzelnen ggml-Aufruf schrittweise durchlaufen – mit echten Zahlen aus einem winzigen, aber vollständig gerechneten Transformer.

📦 GGUF-Format🔤 SPM-Tokenizer👁️ Attention-Heatmaps🗃️ KV-Cache🔬 Quantisierung🎲 Sampler-Kette▶ Debugger
n_layer 4n_embd 32n_head 4n_ff 64n_vocab 49172.672 Parameter
01 · Visueller Debugger

Die Inferenz Schritt für Schritt

Wähle einen Beispiel-Prompt (oder tippe deinen eigenen) und lass die Pipeline laufen – als Ganzes in vier Geschwindigkeiten oder einzeln von ggml-Aufruf zu ggml-Aufruf. Jeder Schritt zeigt die Formel, den zugehörigen C-Code aus llama.cpp und die tatsächlich berechneten Zwischenwerte.

💡 Worauf achten: Klassischer Lückentext. Gut zu sehen: Die Attention der letzten Position hängt stark an „Frankreich“ – genau dort steht die Information, die das nächste Token bestimmt.
🎛️ Sampler-Parameter (wie bei llama-cli)

⚠️ Die Gewichte dieses Demo-Modells sind deterministisch zufällig, aber untrainiert. Die Architektur, alle Zwischenergebnisse und die komplette Sampler-Kette sind echt gerechnet – nur die Logits werden optional mit einem n-Gramm-Prior (Trigramm mit Bigramm-Backoff) aus einem Mini-Korpus gemischt, damit lesbarer Text entsteht. Stelle Demo-Prior auf 0, um das rohe, untrainierte Modell zu sehen.

Tastatur: Leer
Prompt gewählt: „Die Hauptstadt von Frankreich ist“ · 184 Schritte vorbereitet. ▶ startet die Inferenz.
Inferenz-Pipeline · klicken zum Springen
Transformer-Blöcke:blk.0blk.1blk.2blk.3Token-Slot:
Rechenoperationen
0
von 798.5 k FLOPs gesamt
Gelesene Gewichte
0 B
Q4_K_M · gesamt 138.6 KiB
Token
6 + 0
Prompt + erzeugt (Ziel 3)
KV-Cache
6/32
belegte Positionen

🔍 Bereit

Der komplette Inferenz-Lauf wurde vorberechnet: 184 Einzelschritte vom Öffnen der GGUF-Datei bis zum letzten gezogenen Token.

  • ▶ spielt alles in der gewählten Geschwindigkeit ab
  • ⏭ geht einen einzelnen ggml-Aufruf weiter
  • Ein Klick auf eine Pipeline-Stufe oben springt direkt dorthin
  • Jede Zelle der Matrizen zeigt beim Überfahren ihren exakten Wert
Ausgabe
Die Hauptstadt von Frankreich ist
02 · Module

Wie llama.cpp aufgebaut ist

Fünf Werkbänke zum Nachschlagen und Ausprobieren: die Schichtenarchitektur des Projekts, der byteweise Aufbau der LLM-Datei, das GGUF-Format mit allen Tensoren, der Tokenizer zum Selbsttesten und der ggml-Berechnungsgraph.

🗺️ Schichtenmodell von llama.cpp


        ┌──────────────────────────────────────────────────────────────┐
        │                     Anwendung / Werkzeuge                    │
        │   llama-cli   llama-server   llama-quantize   llama-bench    │
        └───────────────────────────┬──────────────────────────────────┘
                                    │  libllama (C-API: llama.h)
        ┌───────────────────────────▼──────────────────────────────────┐
        │  llama-model-loader ──► llama-model ──► llama-context        │
        │        │                    │                 │              │
        │        │                    │                 ├─ llama-batch │
        │        │                    │                 ├─ llama-kv-…  │
        │        │                    │                 └─ llama-samp… │
        │        ▼                    ▼                                │
        │   llama-mmap           llama-vocab        llama-graph        │
        └───────────────────────────┬──────────────────────────────────┘
                                    │  ggml-Graph (Knoten + Tensoren)
        ┌───────────────────────────▼──────────────────────────────────┐
        │                    ggml  +  ggml-backend-sched               │
        ├──────────┬──────────┬──────────┬──────────┬──────────────────┤
        │ ggml-cpu │ ggml-cuda│ggml-metal│ggml-vulkan│  ggml-sycl  …   │
        │ AVX2/NEON│  NVIDIA  │  Apple   │  portabel │   Intel         │
        └──────────┴──────────┴──────────┴──────────┴──────────────────┘

Die Trennung ist scharf: ggml weiß nichts über Sprachmodelle – es kennt nur Tensoren und Operationen. libllama weiß nichts über Hardware – es baut nur Graphen. Genau deshalb lässt sich ein neues Backend hinzufügen, ohne eine Zeile Modellcode anzufassen.

📦 GGUF & Model-Loader

Liest das Dateiformat, prüft Magic und Version, legt die Tensor-Deskriptoren an und blendet die Gewichte per mmap ein.

model.gguf auf der PlatteHeaderMetadatenTensor-InfoTensordatenmmap()kein memcpy · nur AdressenProzess-AdressraumSeiten geladen: 3/12 · Rest noch nicht angefasst
mmap() bildet die Datei in den Adressraum ab, statt sie zu kopieren. Seiten werden erst beim ersten Zugriff wirklich geladen – deshalb startet dasselbe Modell beim zweiten Mal fast verzögerungsfrei aus dem Page-Cache.
ggml/src/gguf.cppsrc/llama-model-loader.cppsrc/llama-mmap.cpp
  • GGUF ist selbstbeschreibend: Hyperparameter, Vokabular und Chat-Template stecken als Key-Value-Paare in der Datei.
  • mmap() bedeutet: kein Kopieren, lazy Paging, sofortiger zweiter Start aus dem Page-Cache.
  • Der Loader prüft, ob jeder erwartete Tensor in der richtigen Form vorliegt – fehlt einer, bricht das Laden ab.

🔤 Vokabular & Tokenizer

Wandelt Text in Token-IDs und zurück. Unterstützt SPM (LLaMA), BPE (GPT-2/Falcon), WPM (BERT), UGM und RWKV.

🧱 ggml – Tensor-Bibliothek

Die Rechenmaschine: definiert Tensoren, Operationen und den Berechnungsgraphen. Ohne Abhängigkeiten, in reinem C.

⚙️ Backend-Scheduler

Verteilt die Graph-Knoten auf CPU, Metal, CUDA, Vulkan, SYCL … und fügt automatisch Kopieroperationen zwischen Geräten ein.

🕸️ Graph-Builder

Übersetzt die Architektur (llama, qwen2, phi3, gemma …) in eine konkrete Folge von ggml-Operationen.

🗃️ KV-Cache

Speichert Key- und Value-Vektoren aller bisherigen Positionen, damit Decoding nicht die ganze Sequenz neu rechnen muss.

🎲 Sampling-Kette

Formt aus den Logits ein konkretes Token: Penalties, Temperatur, top-k, top-p, min-p, typical, mirostat, Grammatik.

🖥️ Server & Werkzeuge

Fertige Programme: OpenAI-kompatibler HTTP-Server, CLI-Chat, Quantisierer, Qualitätsmessung.

03 · Praxis

Quantisierung und Geschwindigkeit

Warum passt ein 8-Milliarden-Modell in 4.5 GB, und was kostet das an Qualität? Und wie viele Token pro Sekunde sind auf welcher Hardware überhaupt möglich?

🔬 Quantisierung live: ein Block aus 32 Gewichten

Genau so verarbeitet ggml die Gewichte: nicht einzeln, sondern in Blöcken mit gemeinsamem Skalierungsfaktor. Der Fehler jedes einzelnen Werts ist der Preis für 4× weniger Speicher.

Index012345678910111213141516171819202122232425262728293031
Original (f32)
Code (int)6846712847010111310121110515667109858989145
rekonstruiert
Fehler (10× verstärkt)
Q8_0
8.31e-4
RMSE · 8.50 bit/Gewicht · 34 B je 32 Werte
Q4_0
2.78e-2
RMSE · 4.50 bit/Gewicht · 18 B je 32 Werte
Q4_K (Subblock)
1.21e-2
RMSE · 4.50 bit/Gewicht · 18 B je 32 Werte
Skalierung d = 4.568e-2 · Minimum = -0.3935 · asymmetrisch: eigener Nullpunkt je Block – deshalb der kleinere Fehler bei gleicher Bitzahl.

F32

32.00 bit/Gewicht

Referenz ohne Verlust. 32 bit pro Gewicht – vier mal so groß wie Q8_0 und für lokale Inferenz meist unnötig.

Q8_0

8.50 bit/Gewicht

32 Werte teilen sich einen f16-Skalierungsfaktor, jedes Gewicht wird zu int8. 8.5 bit/Gewicht, praktisch verlustfrei – der Goldstandard für Vergleichsmessungen.

Q4_0

4.50 bit/Gewicht

32 Werte, ein f16-Delta, 4 bit pro Gewicht (zwei Nibbles je Byte). 4.5 bit/Gewicht. Einfach und schnell, aber messbar ungenauer als die K-Quants.

Q4_K

4.50 bit/Gewicht

Superblock aus 256 Werten, unterteilt in 8 Subblöcke à 32. Jeder Subblock hat eigene Skalierung UND eigenen Minimalwert (6 bit, selbst quantisiert). Deutlich kleinerer Fehler bei nahezu gleicher Größe – Basis von Q4_K_M.

Q6_K

6.56 bit/Gewicht

6 bit pro Gewicht mit 16 Subblöcken. Wird in Q4_K_M gezielt für die empfindlichen Tensoren (ffn_down, attn_v, output) eingesetzt – dort kostet Präzision am wenigsten Speicher und bringt am meisten.

⚖️ Die Mischungen von llama-quantize

Ein „Q4_K_M“ ist kein einzelnes Format, sondern ein Rezept: die meisten Tensoren Q4_K, die empfindlichen (attn_v, ffn_down, output) Q6_K. Das M steht für „medium“.

F16
16.00 bpwverlustfrei
Q8_0
8.50 bpw≈ verlustfrei
Q6_K
6.56 bpwsehr gut
Q5_K_M
5.67 bpwsehr gut
Q4_K_M
4.83 bpwgut
Q4_K_S
4.58 bpwgut
Q3_K_M
3.91 bpwspürbar schlechter
Q2_K
2.63 bpwdeutlich schlechter
# so entsteht eine quantisierte Datei
$ llama-quantize model-f16.gguf model-Q4_K_M.gguf Q4_K_M

# und so misst man, was es gekostet hat
$ llama-perplexity -m model-Q4_K_M.gguf -f wiki.test.raw
Final estimate: PPL = 5.9642 +/- 0.03348   # F16-Referenz: 5.9066
04 · Einordnung

Was hier echt ist – und was Modell steht

Ehrlichkeit gehört zu einer Lernhilfe dazu.

✅ Echt gerechnet

  • Die komplette LLaMA-Architektur: RMSNorm, Q/K/V, RoPE, kausale Attention, SwiGLU, Residuals
  • Alle angezeigten Vektoren, Matrizen und Attention-Gewichte stammen aus echten Matrixprodukten
  • Der SPM-Tokenizer inklusive jedes einzelnen Merge-Schritts
  • Der KV-Cache und sein Wachstum pro Token
  • Die vollständige Sampler-Kette: Penalty → Temperatur → top-k → top-p → Ziehen
  • Die Quantisierungs-Rekonstruktion und ihr messbarer Fehler

⚠️ Vereinfacht oder simuliert

  • Die Gewichte sind untrainiert (deterministisch pseudozufällig). Deshalb wird für lesbare Ausgaben ein n-Gramm-Prior aus einem Mini-Korpus zugemischt – abschaltbar per Regler.
  • Winzige Dimensionen (n_embd 32 statt 4096), damit jede Zahl sichtbar bleibt
  • Kein echtes GGUF wird geladen – Struktur und Metadaten sind nachgebildet
  • Q4_K ist als Einzelblock gezeigt, nicht als vollständiger 256er-Superblock mit 6-bit-Skalen
  • FLOPs und Byte-Angaben sind Modellrechnungen, keine Messungen

🚀 Weitermachen mit dem echten llama.cpp

# 1) bauen
$ git clone https://github.com/ggml-org/llama.cpp && cd llama.cpp
$ cmake -B build && cmake --build build --config Release -j

# 2) ein Modell holen (GGUF direkt von Hugging Face)
$ build/bin/llama-cli -hf bartowski/Llama-3.2-3B-Instruct-GGUF:Q4_K_M -p "Erkläre RoPE"

# 3) als OpenAI-kompatibler Server
$ build/bin/llama-server -m model.gguf -c 4096 -ngl 99 --port 8080

# 4) hineinschauen – jede Stufe dieser App hat ein echtes Gegenstück
$ build/bin/llama-tokenize -m model.gguf -p "Hallo Welt"   # Stufe 🔤
$ build/bin/llama-bench    -m model.gguf -p 512 -n 128      # Stufe 📊
$ GGML_SCHED_DEBUG=2 build/bin/llama-cli -m model.gguf -n 1 # Graph + Backends