TH
Zurück zum Blog

Lokaler LLM-Betrieb konkret

11 Min. LesezeitTill Heller

In Was ist ein LLM? habe ich lokalen Betrieb nur als eine von zwei Betriebsformen erwähnt. Dieser Beitrag holt das nach und beantwortet die Fragen, die in der Praxis zuerst kommen: Welche Hardware brauche ich, wie schnell wird das, und was gewinne ich damit tatsächlich?

Die Antworten sind erfreulich rechenbar. Man braucht dafür kein Fachwissen über neuronale Netze, sondern zwei Zahlen: die Parameterzahl des Modells und die Speicherbandbreite der Maschine. Wer lieber tippt als rechnet, findet weiter unten eine vollständige Installation auf einem Mac, an der sich alle Formeln überprüfen lassen.

Was „lokal“ konkret heißt

Gemeint ist: Das Modell läuft auf einer Maschine, über die man selbst die Kontrolle hat. Das kann der eigene Arbeitsrechner sein, ein Server im Haus oder eine gemietete virtuelle Maschine. Entscheidend ist nicht die Entfernung, sondern dass keine Anfrage an einen fremden Dienst geht, dessen Nutzungsbedingungen und Protokollierung man nicht selbst bestimmt.

Technisch ist der Aufbau schlicht. Ein Inferenz-Server lädt die Modellgewichte einmal in den Speicher und nimmt danach Anfragen über eine lokale Schnittstelle entgegen, meist über HTTP. Die eigene Anwendung spricht also nicht mit einem Anbieter im Netz, sondern mit einem Prozess auf localhost. An der Anwendungsarchitektur ändert sich dadurch wenig, an der Betriebsverantwortung viel.

Zwei Werkzeuge

llama.cpp ist eine in C und C++ geschriebene Inferenz-Engine, also das Programm, das die eigentliche Rechnung ausführt. Sie lädt Modelle im Dateiformat GGUF, das die Gewichte zusammen mit den nötigen Metadaten in einer einzigen Datei ablegt. llama.cpp läuft auf CPU (dem Hauptprozessor) und GPU (dem Grafikprozessor) und kann beides mischen: Ein Teil der Netzschichten liegt im Grafikspeicher, der Rest im Arbeitsspeicher. Das nennt sich Offloading und ist der Grund, warum ein Modell auch dann noch startet, wenn die Grafikkarte eigentlich zu klein ist. Schnell ist es dann allerdings nicht mehr, dazu unten mehr.

Ollama setzt auf derselben Grundlage auf und verpackt sie in etwas, das sich wie ein Paketmanager bedient: Modell herunterladen, starten, über eine lokale Programmierschnittstelle (API) ansprechen. Wer schnell etwas ausprobieren oder ein Modell in eine Anwendung einbinden will, ist damit in Minuten arbeitsfähig. Wer jeden Parameter selbst kontrollieren will, greift direkt zu llama.cpp. Für den Fall vieler paralleler Nutzer:innen gibt es weitere Serving-Stacks, die Anfragen bündeln; für den Einstieg und für einzelne Arbeitsplätze sind die beiden genannten die naheliegende Wahl.

Quantisierung: der entscheidende Hebel

Trainiert werden Modelle typischerweise mit 16 Bit pro Gewicht, also 2 Byte. Quantisierung speichert die Gewichte gröber, etwa mit 8 oder 4 Bit. Das ist keine Kompression im üblichen Sinn, sondern ein bewusster Genauigkeitsverlust: Statt vieler feiner Abstufungen bleiben wenige grobe übrig, jeweils blockweise mit einem Skalierungsfaktor versehen.

Der Speichergewinn ist der ganze Punkt. Für ein Modell mit 7 Milliarden Parametern ergibt sich:

71092Byte=14GB(16 Bit)7 \cdot 10^9 \cdot 2\,\text{Byte} = 14\,\text{GB} \quad (16\ \text{Bit}) 71091Byte=7GB(8 Bit)7 \cdot 10^9 \cdot 1\,\text{Byte} = 7\,\text{GB} \quad (8\ \text{Bit}) 71090,5Byte=3,5GB(4 Bit)7 \cdot 10^9 \cdot 0{,}5\,\text{Byte} = 3{,}5\,\text{GB} \quad (4\ \text{Bit})

In der Praxis liegt die 4-Bit-Variante etwas darüber, weil die Skalierungsfaktoren mitgespeichert werden und einzelne besonders empfindliche Schichten feiner bleiben. Effektiv landet man eher bei 4,8 Bit pro Gewicht:

71094,88Byte=4,2GB7 \cdot 10^9 \cdot \frac{4{,}8}{8}\,\text{Byte} = 4{,}2\,\text{GB}

Das deckt sich mit den Dateigrößen, die man bei fertigen GGUF-Modellen dieser Größenordnung tatsächlich vorfindet, üblicherweise gut 4 GB.

Der Preis ist Qualität. Die verbreitete Erfahrung: 8 Bit ist praktisch nicht vom Original zu unterscheiden, 4 Bit kostet wenig und ist für die meisten Anwendungen der vernünftige Standard, unterhalb von 4 Bit werden die Einbußen schnell spürbar. Diese Faustregel ersetzt keine Messung. Soll das Modell eine konkrete Aufgabe erfüllen, etwa Befundtexte strukturieren, dann gehören beide Varianten auf genau dieser Aufgabe verglichen, nicht auf allgemeinen Benchmarks.

Der Speicherbedarf, durchgerechnet

Die Gewichte sind nur der eine Posten. Der zweite ist der KV-Cache (Key-Value-Cache), in dem das Modell Zwischenergebnisse für jedes bereits verarbeitete Token behält, damit es sie nicht bei jedem neuen Token erneut berechnen muss. Er wächst linear mit der Kontextlänge, und genau das wird regelmäßig übersehen.

Für ein typisches Modell dieser Größe mit 32 Schichten, 8 Key-Value-Köpfen und 128 Werten pro Kopf, gespeichert mit 2 Byte:

23281282Byte=131072Byte0,13MB pro Token2 \cdot 32 \cdot 8 \cdot 128 \cdot 2\,\text{Byte} = 131\,072\,\text{Byte} \approx 0{,}13\,\text{MB pro Token}

Die führende 2 steht dafür, dass sowohl Keys als auch Values gespeichert werden. Bei 4 096 Tokens Kontext sind das rund 0,5 GB, bei 32 768 Tokens bereits etwa 4,3 GB. Die Kontextlänge kostet also am Ende ähnlich viel Speicher wie das Modell selbst.

Zusammen mit einem knappen halben Gigabyte für Puffer und Zwischenergebnisse ergibt sich:

Kontext 4 096 Tokens: 5,2 GBKontext 32 768 Tokens: 9,0 GB8 GB GrafikspeicherGewichteKV-CachePuffer

Dasselbe Modell, dieselbe Quantisierung: Allein die achtfache Kontextlänge sprengt das Speicherbudget.

Gegenprobe mit einem größeren Modell: 13 Milliarden Parameter in 4 Bit ergeben 131090,6=7,813 \cdot 10^9 \cdot 0{,}6 = 7{,}8 GB allein an Gewichten. Auf einer Karte mit 8 GB bleibt für den Kontext nichts mehr übrig, das Modell verlangt also die nächste Ausbaustufe. Genau so grob rechnet man vor dem Hardwarekauf.

Wie schnell wird das?

Beim Erzeugen der Antwort muss für jedes einzelne Token einmal die gesamte Gewichtsmenge durch den Prozessor gelesen werden. Die Ausgabegeschwindigkeit ist deshalb nicht durch Rechenleistung begrenzt, sondern durch Speicherbandbreite. Die obere Schranke lautet:

Tokens pro SekundeBandbreiteModellgro¨ße\text{Tokens pro Sekunde} \le \frac{\text{Bandbreite}}{\text{Modellgröße}}

Auf einer Grafikkarte mit 500 GB/s ergibt das für unser 4,2-GB-Modell rund 119 Tokens pro Sekunde, deutlich schneller als jemand lesen kann. Im Arbeitsspeicher eines gewöhnlichen Rechners stehen eher 50 GB/s effektiv zur Verfügung, also etwa 12 Tokens pro Sekunde. Das ist benutzbar, aber spürbar zäh. Und hier liegt der Haken beim eingangs erwähnten Offloading: Sobald ein Teil der Schichten im langsamen Arbeitsspeicher liegt, bestimmt dieser Teil das Tempo. Halb auf der Grafikkarte ist nicht halb so schnell, sondern fast so langsam wie ganz ohne.

Eine Ausnahme gibt es: Das Einlesen des Prompts läuft parallel über alle Tokens und ist rechen-, nicht bandbreitenbegrenzt. Deshalb ist die Wartezeit bis zum ersten Token oft kurz, auch wenn die Ausgabe danach träge tröpfelt.

Eine Installation zum Nachmachen

So weit die Theorie. Der folgende Durchlauf auf einem Mac mit Apple-Silicon-Prozessor macht daraus etwas Nachvollziehbares und liefert nebenbei die Messwerte, um die obigen Rechnungen zu überprüfen.

Einrichten

Die Installation läuft über Homebrew, den verbreiteten Paketmanager für macOS:

brew install ollama
ollama serve

ollama serve startet den Inferenz-Server, der danach unter http://localhost:11434 lauscht. Wer lieber klickt, lädt stattdessen die App von der Projektseite, die dasselbe im Hintergrund tut. In einem zweiten Terminal holt man ein Modell und startet es:

ollama pull qwen2.5:14b
ollama run qwen2.5:14b

Qwen ist eine offen lizenzierte Modellfamilie, die in mehreren Größen vorliegt. Der Zusatz 14b steht für die Parameterzahl, qwen2.5:7b wäre die kleinere Schwester. Ollama liefert standardmäßig 4-Bit-quantisierte Fassungen aus, also genau den Fall der Rechnung von oben. Was man bekommen hat, zeigt:

ollama show qwen2.5:14b

Die Ausgabe nennt Architektur, Parameterzahl (14,8 Mrd.), Kontextlänge (32 768), Einbettungslänge und Quantisierung (Q4_K_M), außerdem die Lizenz. Bei diesem Modell ist es Apache 2.0. Das lohnt einen Blick, denn die Lizenzen frei verfügbarer Modelle unterscheiden sich erheblich, und für den kommerziellen oder klinischen Einsatz ist das keine Nebensache.

Passt es auf die Maschine?

Auf Apple Silicon teilen sich Hauptprozessor und Grafikprozessor denselben physischen Speicher. Es gibt also keinen getrennten Grafikspeicher, sondern ein gemeinsames Budget, von dem das System dem Grafikteil grob zwei Drittel des verbauten Arbeitsspeichers zugesteht. An der Rechnung ändert das nichts, nur die Obergrenze heißt anders.

Ob es tatsächlich passt, beantwortet der laufende Prozess selbst:

ollama ps
NAME           ID              SIZE     PROCESSOR    CONTEXT    UNTIL
qwen2.5:14b    7cdf5a0187d5    15 GB    100% GPU     32768      4 minutes from now

Entscheidend ist die Spalte PROCESSOR. 100% GPU heißt, das komplette Modell liegt im schnellen Speicher. Steht dort eine Aufteilung zwischen CPU und GPU, greift das weiter oben beschriebene Offloading, und die Ausgabe wird deutlich zäher. Die Spalte UNTIL verrät nebenbei, dass Ollama ein Modell nach einigen Minuten Untätigkeit wieder aus dem Speicher wirft. Der erste Aufruf danach dauert entsprechend länger, im gemessenen Fall gut 6 Sekunden, weil 9 GB von der Platte gelesen werden müssen.

Die Rechnungen gegen die Wirklichkeit

Jetzt lassen sich alle drei Formeln überprüfen. Erstens die Gewichte: 14,8 Milliarden Parameter zu effektiv 4,8 Bit ergeben

14,81094,88Byte=8,9GB14{,}8 \cdot 10^9 \cdot \frac{4{,}8}{8}\,\text{Byte} = 8{,}9\,\text{GB}

und ollama list weist für das Modell 9,0 GB aus. Zweitens der KV-Cache: ollama show nennt 48 Schichten, 40 Aufmerksamkeitsköpfe und 8 Key-Value-Köpfe bei einer Einbettungslänge von 5 120, also 5120/40=1285\,120 / 40 = 128 Werte pro Kopf. Damit

24881282Byte=196608Byte pro Token2 \cdot 48 \cdot 8 \cdot 128 \cdot 2\,\text{Byte} = 196\,608\,\text{Byte pro Token}

und bei den vollen 32 768 Tokens Kontext rund 6,4 GB. Zusammen also 15,3 GB, und exakt 15 GB meldet ollama ps als belegten Speicher. Der Aufschlag von zwei Dritteln gegenüber der reinen Modelldatei ist kein Messfehler, sondern der Preis des großen Kontextfensters.

Drittens die Geschwindigkeit. Der Schalter --verbose hängt an jede Antwort eine Messung an:

ollama run qwen2.5:14b --verbose "Nenne in einem Satz, was Quantisierung bedeutet."
prompt eval rate:  119.90 tokens/s
eval rate:          29.69 tokens/s

Die nominelle Speicherbandbreite dieser Ausbaustufe liegt bei 400 GB/s, geteilt durch die 9,0 GB Modellgröße also rund 44 Tokens pro Sekunde als obere Schranke. Gemessen werden 29,7, das sind etwa zwei Drittel des theoretischen Maximums. Genau in dieser Größenordnung landet man in der Praxis: Die Schranke ist eine Schranke, kein Versprechen, aber sie sagt die richtige Größenordnung voraus. Sichtbar wird im selben Durchlauf auch der Unterschied zwischen Einlesen und Erzeugen, denn der Prompt wurde mit 120 Tokens pro Sekunde verarbeitet, die Antwort mit weniger als einem Viertel davon.

Aus eigenem Code ansprechen

Der Server nimmt Anfragen als JSON entgegen (JavaScript Object Notation, das übliche Austauschformat für Web-Schnittstellen):

curl -s http://localhost:11434/api/chat -d '{
  "model": "qwen2.5:14b",
  "messages": [{"role": "user", "content": "Antworte nur mit OK."}],
  "stream": false
}'

Daneben bietet Ollama unter /v1/chat/completions eine Schnittstelle an, die dem verbreiteten Industriestandard nachgebildet ist. Bestehender Code braucht dadurch oft nur eine andere Basis-URL und läuft dann gegen das lokale Modell statt gegen einen externen Dienst. Genau das macht den Vergleich billig: Man kann eine Anwendung gegen beides laufen lassen und messen, ob das lokale Modell für die konkrete Aufgabe ausreicht.

Zum Aufräumen listet ollama list alle vorhandenen Modelle und ollama rm qwen2.5:14b entfernt eines wieder. Die Dateien liegen unter ~/.ollama/models und sind, wie die Rechnung nahelegt, entsprechend groß.

Datenhoheit ist kein Freibrief

Der eigentliche Grund für lokalen Betrieb ist meist nicht Geschwindigkeit und selten der Preis, sondern die Frage, wo die Daten liegen. In regulierten Umfeldern, im medizinischen Bereich etwa, ist das oft die einzige Frage, die zählt: Patientendaten verlassen das Haus nicht, damit entfällt die gesamte Diskussion über Auftragsverarbeitung und Drittlandtransfer.

Zwei Einschränkungen gehören dazu. Erstens ersetzt lokaler Betrieb keine Zugriffskontrolle, keine Protokollierung und keine Löschkonzepte. Ein Modell auf dem eigenen Server ist ein System wie jedes andere und unterliegt denselben Anforderungen. Zweitens ändert lokaler Betrieb nichts an den inhaltlichen Grenzen: Ein lokal laufendes Modell halluziniert genauso wie ein gehostetes, und die frei verfügbaren Modelle sind den größten gehosteten in der Qualität weiterhin unterlegen. Man tauscht Fähigkeiten gegen Kontrolle, bewusst und mit offenen Augen.

Fazit

Lokaler LLM-Betrieb ist kein Forschungsprojekt, sondern eine Kapazitätsplanung. Parameterzahl mal Bytes pro Parameter ergibt den Speicherbedarf der Gewichte, die Kontextlänge mal Bytes pro Token den des KV-Caches, und Bandbreite geteilt durch Modellgröße die obere Schranke der Ausgabegeschwindigkeit. Alle drei haben sich am laufenden System bestätigt, und mit ihnen lässt sich schon vor dem ersten Download entscheiden, ob ein Vorhaben auf der vorhandenen Hardware Sinn ergibt.

Was danach bleibt, ist die inhaltliche Arbeit: prüfen, ob das gewählte Modell die konkrete Aufgabe gut genug erfüllt, und seine Ausgaben behandeln wie jede andere unsichere Eingabe.