Zusammenfassung des Gesprächs — von der Grundfunktion über Training und Tokenisierung bis zum praktischen Fine-Tuning-Beispiel und Deployment.
Ein LLM sagt immer nur eine Sache voraus: welches Token (Wort oder Wortteil) als nächstes am wahrscheinlichsten kommt. Diese Vorhersage wird Token für Token wiederholt, bis eine vollständige Antwort entsteht.
Jeder Schritt erzeugt eine Wahrscheinlichkeit über alle ca. 50.000 möglichen Tokens; das wahrscheinlichste wird angehängt, dann beginnt der Zyklus erneut.
Damit die Gewichte diese Wahrscheinlichkeiten sinnvoll berechnen können, müssen sie zuvor trainiert werden. Das passiert in einem sich ständig wiederholenden, vierstufigen Zyklus:
Training bedeutet: Die Gewichte selbst werden Schritt für Schritt verändert, basierend auf einem wiederkehrenden vierstufigen Zyklus.
Erster Schritt, die Vorhersage (Forward Pass): Man gibt dem Modell einen Textausschnitt, zum Beispiel „Der Himmel ist“, und lässt es vorhersagen, welches Wort als nächstes kommt. Mit den aktuellen (anfangs zufälligen) Gewichten kommt dabei irgendeine Wahrscheinlichkeitsverteilung heraus, am Anfang meist kompletter Unsinn.
Zweiter Schritt, der Fehler (Loss): Man kennt aus dem echten Trainingstext das tatsächliche nächste Wort (hier zum Beispiel „blau“). Man vergleicht die Vorhersage des Modells mit diesem echten Wort und berechnet einen Zahlenwert dafür, wie falsch die Vorhersage war — das nennt man den Loss (Verlust).
Dritter Schritt, die Rückverfolgung (Backpropagation): Jetzt kommt die eigentliche mathematische Arbeit. Das Verfahren berechnet für jedes einzelne der Milliarden Gewichte im Netzwerk, wie stark und in welche Richtung dieses eine Gewicht zu dem gemessenen Fehler beigetragen hat — durch systematisches Rückwärts-Durchrechnen des Netzwerks, daher der Name.
Vierter Schritt, die Anpassung (Gradient Descent): Basierend auf dieser Information wird jedes Gewicht ein kleines bisschen in die Richtung verschoben, die den Fehler beim nächsten Mal etwas kleiner machen würde — nur ein kleiner Schritt, nicht die ganze Korrektur auf einmal.
Kein Mensch schreibt dabei „richtige Antworten“ vor: In der ersten Trainingsphase liefert der Trainingstext selbst die Lösung — man verdeckt das nächste Wort und lässt es vom Modell vorhersagen. Das nennt man selbstüberwachtes Lernen, weil die Daten sich quasi selbst korrigieren, ohne dass ein Mensch jede einzelne Antwort im Voraus bewerten muss.
Ein fertiges Chat-Modell wie dieses hier entsteht nicht in einem Schritt, sondern in drei aufeinanderfolgenden Phasen:
Die Anzahl der Gewichte (Parameter) bestimmt die Kapazität eines Modells — wie viele Muster es theoretisch abbilden könnte. Ob diese Kapazität gut genutzt wird, hängt von der Qualität der Trainingsdaten ab.
Mehr Gewichte = mehr Kapazität (das Regal ist größer). Ob das Ergebnis gut ist, entscheidet aber, was hineingestellt wird — nicht wie viel Platz vorhanden ist.
Die Mathematik neuronaler Netze besteht fast nur aus Matrizenmultiplikationen — vielen kleinen, voneinander unabhängigen Rechnungen, die gleichzeitig statt nacheinander ausgeführt werden können. Genau dafür wurden GPUs gebaut.
Weil dieselbe Rechnung (Multiplizieren + Addieren) millionenfach parallel für jedes Gewicht gleichzeitig anfällt, ist eine GPU hier oft um das Hundertfache schneller als eine CPU — der ursprüngliche Zweck (Bilddarstellung, viele Pixel gleichzeitig) passt zufällig ideal zum Training neuronaler Netze.
Bevor ein Satz verarbeitet wird, wird er in Tokens zerlegt — Bausteine aus einer festen, vorab erstellten Liste (dem Vokabular, meist 30.000–100.000 Einträge). Das Verfahren dahinter heißt Byte-Pair-Encoding (BPE).
Mit einem kleinen, selbst trainierten BPE-Tokenizer (Trainingstext: ein paar Sätze über Hauptstädte) wurde der Satz „Wie heisst die Hauptstadt von Frankreich“ so zerlegt:
Die Tokenliste wird einmalig festgelegt und wächst danach nicht mehr automatisch. Neue Wörter werden aus vorhandenen kleineren Bausteinen zusammengesetzt; das "Wissen" darüber entsteht in den Gewichten, nicht als neuer Vokabeleintrag.
Nur der allererste Schritt ist konkret zeigbar: Jeder Token hat eine feste ID, die einer Zeile in der sogenannten Embedding-Tabelle entspricht (Teil der Gewichte). Alles danach verteilt sich über Milliarden gemeinsam genutzter Gewichte und ist nicht mehr einem einzelnen Token oder Konzept zuordenbar.
Bei fester Modellgröße ist irgendwann alle Kapazität „verplant“. Trainiert man ein fertiges Modell stark auf neue Inhalte nach, kann es alte Fähigkeiten verlieren.
Gemessen wird das über feste Benchmark-Testsets, verglichen vor und nach dem Nachtraining — nicht durch wiederholtes Stellen derselben Frage.
| Technik | Wirkung |
|---|---|
| Replay / Rehearsal | alte Trainingsdaten weiterhin mit einmischen |
| Niedrige Lernrate | nur sanfte, kleine Gewichtsanpassungen |
| LoRA / Adapter | Basisgewichte bleiben komplett eingefroren |
| Elastic Weight Consolidation | wichtige alte Gewichte werden gezielt geschützt |
Statt aller Milliarden Gewichte werden bei LoRA nur kleine, zusätzliche Gewichtsmatrizen trainiert. Das Basismodell bleibt komplett eingefroren.
Vorteil: geringer Rechenaufwand (oft eine einzelne GPU statt eines Rechenzentrums) und geringes Risiko für Catastrophic Forgetting, weil das Original unangetastet bleibt. Mit QLoRA wird das Basismodell zusätzlich 4-bit-quantisiert, um Speicher zu sparen.
Der komplette praktische Weg, den wir durchgegangen sind:
Trainingsbeispiel (Format wie in training_data_beispiel.jsonl):
Das Trainingsskript nutzt transformers, peft (LoRA) und
trl und durchläuft für jedes Beispiel genau den Lernzyklus von oben.
Faustregel: einige hundert bis wenige tausend sorgfältig geprüfte Beispiele reichen
meist für spürbare Verbesserungen.
Das vollständige, kommentierte finetune.py-Skript aus unserem
Gespräch — nutzt transformers, peft (LoRA/QLoRA) und
trl, lädt das Basismodell in 4-bit, konfiguriert LoRA, trainiert mit
training_data_beispiel.jsonl und testet das Ergebnis am Ende direkt.
"""
Beispiel-Skript: Fine-Tuning eines offenen LLM (z. B. Mistral-7B oder Llama-3-8B)
mit LoRA (parameter-effizientes Fine-Tuning) für einen firmenspezifischen
Anwendungsfall (hier: IT-Support / Kundenkommunikation für eine Softwarefirma).
Benötigte Bibliotheken (Installation siehe README.md):
pip install transformers peft trl datasets accelerate bitsandbytes torch
Voraussetzung: Eine GPU mit ausreichend VRAM (siehe README.md für Richtwerte).
Für ein 7B-Modell mit 4-bit-Quantisierung (QLoRA) reichen oft schon
16-24 GB VRAM (z. B. eine RTX 4090 oder eine Cloud-GPU wie A10/A100).
Dieses Skript ist bewusst einfach gehalten, um den grundsätzlichen Ablauf zu
zeigen. Für produktive Projekte sollte man zusätzlich Validierungs-Splits,
Logging (z. B. Weights & Biases) und Hyperparameter-Suche ergänzen.
"""
import torch
from datasets import load_dataset
from transformers import (
AutoModelForCausalLM,
AutoTokenizer,
BitsAndBytesConfig,
TrainingArguments,
)
from peft import LoraConfig, get_peft_model, prepare_model_for_kbit_training
from trl import SFTTrainer, SFTConfig
# ---------------------------------------------------------------------------
# 1. Basis-Modell festlegen
# ---------------------------------------------------------------------------
# Ein frei verfügbares, offenes Modell von Hugging Face. Für den echten
# Gebrauch muss man ggf. den Zugriff beim Anbieter (z. B. Meta für Llama)
# einmalig beantragen; Mistral-Modelle sind meist ohne Freigabe nutzbar.
BASE_MODEL = "mistralai/Mistral-7B-Instruct-v0.3"
# ---------------------------------------------------------------------------
# 2. Modell in 4-bit laden (QLoRA) -> spart massiv Grafikspeicher
# ---------------------------------------------------------------------------
bnb_config = BitsAndBytesConfig(
load_in_4bit=True,
bnb_4bit_quant_type="nf4",
bnb_4bit_compute_dtype=torch.bfloat16,
bnb_4bit_use_double_quant=True,
)
tokenizer = AutoTokenizer.from_pretrained(BASE_MODEL)
tokenizer.pad_token = tokenizer.eos_token
model = AutoModelForCausalLM.from_pretrained(
BASE_MODEL,
quantization_config=bnb_config,
device_map="auto",
)
model = prepare_model_for_kbit_training(model)
# ---------------------------------------------------------------------------
# 3. LoRA konfigurieren
# ---------------------------------------------------------------------------
# Statt aller Milliarden Gewichte werden nur kleine, zusätzliche
# Gewichtsmatrizen trainiert ("r" bestimmt deren Größe/Kapazität).
# Die ursprünglichen Gewichte bleiben eingefroren -> geringes Risiko
# für catastrophic forgetting, geringer Rechenaufwand.
lora_config = LoraConfig(
r=16,
lora_alpha=32,
lora_dropout=0.05,
bias="none",
task_type="CAUSAL_LM",
target_modules=["q_proj", "k_proj", "v_proj", "o_proj"],
)
model = get_peft_model(model, lora_config)
model.print_trainable_parameters()
# Beispielausgabe: "trainable params: 20,971,520 || all params: 7,262,703,616
# || trainable%: 0.29 %"
# -> Es werden nur ca. 0,3 % aller Gewichte tatsächlich verändert.
# ---------------------------------------------------------------------------
# 4. Trainingsdaten laden
# ---------------------------------------------------------------------------
# Erwartetes Format: JSON-Lines-Datei, eine Zeile = ein Trainingsbeispiel
# mit den Feldern "instruction", "input" (optional) und "output".
# Siehe training_data_beispiel.jsonl für ein konkretes Beispiel.
dataset = load_dataset("json", data_files="training_data_beispiel.jsonl", split="train")
def formatiere_prompt(beispiel):
"""Bringt ein Trainingsbeispiel in das Chat-/Prompt-Format des Modells."""
if beispiel.get("input"):
prompt = f"{beispiel['instruction']}\n\n{beispiel['input']}"
else:
prompt = beispiel["instruction"]
text = (
f"<s>[INST] {prompt} [/INST] {beispiel['output']}</s>"
)
return {"text": text}
dataset = dataset.map(formatiere_prompt)
# ---------------------------------------------------------------------------
# 5. Trainings-Konfiguration
# ---------------------------------------------------------------------------
sft_config = SFTConfig(
output_dir="./ergebnis-finetuned-modell",
num_train_epochs=3,
per_device_train_batch_size=2,
gradient_accumulation_steps=4,
learning_rate=2e-4, # bewusst niedrig, siehe Kapitel zu
# catastrophic forgetting im Gespräch
logging_steps=5,
save_strategy="epoch",
bf16=True,
max_seq_length=1024,
dataset_text_field="text",
report_to="none", # alternativ z. B. "wandb" für Logging-Dashboard
)
# ---------------------------------------------------------------------------
# 6. Training starten
# ---------------------------------------------------------------------------
trainer = SFTTrainer(
model=model,
args=sft_config,
train_dataset=dataset,
)
trainer.train()
# ---------------------------------------------------------------------------
# 7. Ergebnis speichern (nur die kleinen LoRA-Gewichte, wenige MB)
# ---------------------------------------------------------------------------
trainer.save_model("./ergebnis-finetuned-modell")
tokenizer.save_pretrained("./ergebnis-finetuned-modell")
print("Fertig. Das feinabgestimmte Modell (LoRA-Adapter) liegt unter "
"./ergebnis-finetuned-modell")
# ---------------------------------------------------------------------------
# 8. Kurzer Test nach dem Training
# ---------------------------------------------------------------------------
frage = "Ein Kunde fragt: 'Was kostet eine zusätzliche Nutzerlizenz?'"
eingabe = tokenizer(f"<s>[INST] {frage} [/INST]", return_tensors="pt").to(model.device)
ausgabe = model.generate(**eingabe, max_new_tokens=150)
print(tokenizer.decode(ausgabe[0], skip_special_tokens=True))
Wiederkehrende Satzstrukturen ("Unterstützt ihr X?") müssen nicht von Hand dupliziert werden. Ein Template + eine Liste von Themen/Fakten erzeugt die JSONL-Zeilen automatisch.
Allgemeine Tippfehler muss man nicht künstlich einbauen — das übernimmt bereits das Grundtraining des Basismodells. Gezielt lohnt es sich aber bei Tippfehlern, die zufällig ein anderes echtes Wort ergeben (z. B. „DATE-Schnittstelle“ statt „DATEV-Schnittstelle“) — hier hilft ein gezielt ergänztes Trainingsbeispiel, echte Missverständnisse zu vermeiden.
Ein Modell muss nicht zwingend komplett in den Grafikspeicher passen.
device_map="auto" verteilt es intelligent:
Weitere Stellschrauben: stärkere Quantisierung (4-bit, 3-bit), kleineres Modell, Gradient Checkpointing, kleinere Batch-Größe/Sequenzlänge. Für schwächere Laptops eignen sich zusätzlich Werkzeuge wie Unsloth (deutlich geringerer Speicherbedarf) oder bei Apple-Silicon-Rechnern MLX/llama.cpp mit Metal-Unterstützung.
| RAG | Fine-Tuning | |
|---|---|---|
| Verändert die Gewichte? | Nein | Ja |
| Gut für | aktuelle, häufig wechselnde Fakten | Tonfall, Format, Verhalten |
| Aufwand | gering (Vektordatenbank + Kontext) | höher (Trainingsdaten + GPU) |
| Risiko Forgetting | keins (Modell bleibt unverändert) | vorhanden, aber durch LoRA reduzierbar |
In der Praxis oft kombiniert: ein leicht feingetuntes Modell für Stil und Verhalten, plus RAG für aktuelle, firmenspezifische Fakten.
Bei echtem Training verändern sich die Gewichte selbst dauerhaft, durch den Zyklus aus Vorhersagen, Fehler messen und anpassen, wiederholt über enorme Datenmengen und enorme Rechenzeit (das dauert bei großen Modellen Wochen bis Monate auf tausenden spezialisierten Chips). Bei RAG bleibt das Modell exakt so, wie es ist — es bekommt lediglich zur Laufzeit einer einzelnen Anfrage zusätzlichen Text mitgeliefert, der nach Beantwortung der Frage sofort wieder „vergessen“ wird, weil eben nichts an den Gewichten verändert wurde. Deshalb eignet sich RAG so gut für aktuelle, sich ständig ändernde Informationen, während echtes Training eher für grundlegendes, stabiles Sprach- und Faktenverständnis zuständig ist.
Anders als bei einer Datenbank gibt es beim lokalen Laden eines Modells keinen Connection String — die Gewichte werden einmalig komplett in den eigenen Arbeitsspeicher geladen. Bei einer API-Anbindung (z. B. Nutzung über einen Cloud-Anbieter) ist der Vergleich zur Datenbank dagegen zutreffend.
Lokal laden (from_pretrained) | API-Nutzung | |
|---|---|---|
| Wo laufen die Berechnungen? | auf der eigenen Hardware | beim Anbieter |
| Netzwerkverbindung während Nutzung? | keine | ja, pro Anfrage |
| Zugriff auf Gewichte? | ja → Fine-Tuning möglich | nein (außer über spezielle Fine-Tuning-APIs) |
| Vergleichbar mit | große Datei in den Speicher laden | Datenbankverbindung (Connection String / API-Key) |
Ollama dient zum effizienten Ausführen fertiger Modelle (Format: GGUF), nicht zum Trainieren. Der Workflow entspricht dabei fast eins zu eins dem Bauen und Deployen einer Java-Anwendung:
Trainingsdaten enthalten viele Tippfehler und Umgangssprache — trotzdem antwortet das Modell meist korrekt. Drei Gründe:
| 1 | Statistische Mehrheit: korrekte Schreibweise ist immer gleich, Fehler verteilen sich auf viele verschiedene, seltene Varianten. |
| 2 | Gewichtung guter Quellen: lektorierte Texte (Bücher, Fachartikel) fließen stärker gewichtet ins Training ein. |
| 3 | Fine-Tuning-Phase: Zielantworten in den Trainingsbeispielen sind immer sauber formuliert — unabhängig von der Qualität der Frage. |
Ein verbreiteter, aber ungenauer Gedanke: „Der Hersteller analysiert, wie sich das Modell durch die Nutzung verändert hat.“ Das trifft technisch nicht zu — ein bereits deployter LLM verändert sich durch Gebrauch nicht von selbst, die Gewichte bleiben eingefroren, wie im Abschnitt zu lokalem Laden bereits erklärt.
Was gesammelt wird (abhängig von Datenschutzeinstellungen und Nutzungsbedingungen), sind die Gesprächstexte selbst — Eingabe und Ausgabe als reiner Text, komplett getrennt vom Modell und dessen Gewichten. Das Modell selbst wird dabei nicht untersucht oder „seziert“, es wird schlicht aufgezeichnet, was gefragt und geantwortet wurde — genau dieselbe Art von Rohmaterial wie beim ursprünglichen Training, nur eben echte Gesprächsverläufe statt allgemeiner Internettexte.
Die Kuratierung (welche Gespräche sind qualitativ gut genug, wo zeigt das Modell erkennbare Schwächen, welche Themen sind unterrepräsentiert, gibt es Duplikate) findet auf der Textebene statt, bevor überhaupt ein neues Training beginnt. Erst danach fließt diese kuratierte Auswahl in einen neuen, bewussten Trainingsdurchlauf ein (Pretraining-Zusatzmaterial, Supervised Fine-Tuning oder RLHF) — mit demselben Vorhersage/Fehler/Anpassung-Zyklus wie ganz am Anfang. Das Ergebnis ist eine komplett neue, eigenständige Modellversion mit neu berechneten Gewichten, nicht eine im Hintergrund „mitgewachsene“ Version des Modells, mit dem gerade gesprochen wurde.
| Grundprinzip | Token-für-Token-Wahrscheinlichkeitsvorhersage, kein Textarchiv |
| Training | Vorhersage → Fehler → Backpropagation → Gewichte anpassen, milliardenfach |
| Qualität | Parameterzahl = Kapazität; Datenqualität entscheidet, was daraus wird |
| Tokenisierung | Feste Bausteinliste (BPE), Häufigkeit bestimmt Zerlegungsgrad |
| Wissen im Modell | verteilt über Milliarden Gewichte, nicht lokalisierbar |
| Eigenes Fine-Tuning | LoRA/QLoRA auf offenen Modellen, mit kuratierten JSONL-Daten |
| Aktuelle Fakten | eher RAG statt ständigem Nachtrainieren |
| Deployment | Source (HF/LoRA) pflegen, für Ollama jeweils neu nach GGUF bauen |
Zum Nachschlagen: sämtliche Fachbegriffe und Abkürzungen, die in diesem Dokument vorkommen, alphabetisch sortiert.
| Abkürzung | Bedeutung |
|---|---|
| API | Application Programming Interface — Programmierschnittstelle, über die man ein System (z. B. ein LLM) nutzt, ohne die Software lokal auszuführen; Anfrage per Internet, Antwort kommt vom Server des Anbieters. |
| BPE | Byte-Pair-Encoding — Tokenisierungsverfahren, das Text anhand von Häufigkeiten schrittweise zu einer festen Liste von Wortbausteinen (Tokens) verschmilzt; Grundlage moderner Tokenizer. |
| CPU | Central Processing Unit — Hauptprozessor eines Rechners; wenige, aber sehr vielseitige Rechenkerne, gut für sequenzielle Aufgaben, aber ungünstig für die massenhaft parallelen Matrizenberechnungen beim LLM-Training. |
| EWC | Elastic Weight Consolidation — Verfahren gegen Catastrophic Forgetting: wichtige Gewichte für bereits gelerntes Wissen werden beim weiteren Training gezielt "geschützt" und weniger stark verändert. |
| GGUF | GPT-Generated Unified Format — komprimiertes, quantisiertes Dateiformat für Modellgewichte, das von llama.cpp und darauf aufbauend Ollama zum effizienten Ausführen (nicht Trainieren) genutzt wird. |
| GPU | Graphics Processing Unit — Grafikkarte; besitzt tausende kleinere Rechenkerne, die gleichzeitig arbeiten können — ideal für die massiv parallelen Matrizenmultiplikationen beim Training und bei der Ausführung von LLMs. |
| HF | Hugging Face — Plattform und Bibliotheken-Ökosystem (u. a. transformers, peft, trl, datasets) zum Herunterladen, Trainieren und Teilen von Modellen; De-facto-Standardformat "safetensors" für Trainingszwecke. |
| JSONL | JSON Lines — Textformat, bei dem jede Zeile der Datei ein eigenständiges, vollständiges JSON-Objekt ist (statt einem großen JSON-Array); gängiges Format für Trainingsdatensätze beim Fine-Tuning. |
| LLM | Large Language Model — großes Sprachmodell; sagt Text tokenweise anhand gelernter Wahrscheinlichkeiten vorher, ohne dabei Text als solchen zu speichern. |
| LoRA | Low-Rank Adaptation — Fine-Tuning-Methode: die ursprünglichen Modellgewichte bleiben eingefroren, stattdessen werden kleine zusätzliche Matrizen ("Adapter") mit niedrigem Rang trainiert — nur ein Bruchteil der Originalparameter. |
| NF4 | 4-bit NormalFloat — Quantisierungsformat, das Gewichte auf 4 Bit komprimiert, speziell auf die typische Verteilung von Modellgewichten zugeschnitten; Kernbestandteil von QLoRA. |
| PEFT | Parameter-Efficient Fine-Tuning — Oberbegriff für Fine-Tuning-Verfahren (u. a. LoRA), bei denen nur ein kleiner Teil der Modellparameter trainiert wird, statt aller Gewichte; auch Name der gleichnamigen Hugging-Face-Bibliothek. |
| QLoRA | Quantized LoRA — Kombination aus 4-Bit-Quantisierung des eingefrorenen Basismodells (siehe NF4) und LoRA-Adaptern; ermöglicht Fine-Tuning großer Modelle auf Hardware mit deutlich weniger Grafikspeicher. |
| RAG | Retrieval-Augmented Generation — Technik, bei der zur Laufzeit passende Textabschnitte (z. B. aus einer Vektordatenbank) gesucht und dem Modell als Kontext mitgegeben werden; verändert die Gewichte nicht. |
| RLHF | Reinforcement Learning from Human Feedback — Trainingsphase, in der ein Modell anhand von menschlichen Bewertungen (welche Antwort ist besser?) weiter verfeinert wird, meist nach dem SFT. |
| SFT | Supervised Fine-Tuning — Trainingsphase nach dem Pretraining, in der das Modell anhand konkreter Beispiel-Frage-Antwort-Paare lernt, in einem gewünschten Format (z. B. als hilfreicher Assistent) zu antworten. |
| VRAM | Video RAM — Arbeitsspeicher einer Grafikkarte; begrenzt, wie groß ein Modell (bzw. wie groß quantisiert) auf dieser GPU geladen und trainiert werden kann. |
Hinweis zu Marken- und Produktnamen: In diesem Dokument genannte Produkt-, Firmen- und Markennamen — u. a. DATEV, Ollama, Meta Llama, Mistral AI, Hugging Face, OpenAI/GPT, Python, Java, Microsoft PowerPoint, Google Colab, RunPod, Apple/MLX — sind Marken bzw. eingetragene Marken der jeweiligen Rechteinhaber. Sie werden hier ausschließlich zu illustrativen und erklärenden Zwecken verwendet; eine Zugehörigkeit, Empfehlung oder Zusammenarbeit mit den jeweiligen Unternehmen ist damit nicht verbunden.