Einleitung

Große Sprachmodelle (Large Language Models, LLMs) wie Llama 3.x werden in einem aufwendigen Vortrainingsprozess auf gewaltigen Textmengen trainiert. Dieser Prozess erfolgt typischerweise auf spezieller Hardware wie GPUs und TPUs, die für die parallele Berechnung großer neuronaler Netzwerke optimiert sind.

Nach Abschluss des Vortrainings sind die Modellparameter eingefroren und können im Regelbetrieb nicht mehr direkt verändert werden. Das bedeutet, dass man das Modell nicht einfach “korrigieren” oder durch einfache Eingriffe gezielt umprogrammieren kann. Inhalte wie zum Beispiel Fakten über historische Persönlichkeiten sind nicht in einzelnen, gezielt ansprechbaren Neuronen gespeichert. Stattdessen sind solche Informationen statistisch verteilt über die Gesamtheit der Modellgewichte codiert. Das erschwert gezielte Änderungen erheblich, da es keine klar identifizierbaren Speicherorte für einzelne Fakten gibt.

Im praktischen Einsatz erfolgt die Beeinflussung des Modellverhaltens üblicherweise über sogenannte Prompts. Dabei wird das Modell mit speziell formulierten Eingaben gesteuert, um bestimmte Antworten zu erzeugen. Diese Methode ist flexibel, aber nicht zuverlässig, wenn das Ziel darin besteht, systematisch verändertes Wissen oder dauerhaft abweichendes Antwortverhalten zu implementieren.

Eine Möglichkeit zur gezielten Anpassung besteht im sogenannten Fine-Tuning. Dabei wird das bestehende Modell durch Training auf einem speziellen Zusatzdatensatz verändert. Um diesen Prozess effizient und ressourcenschonend zu gestalten, kann LoRA (Low-Rank Adaptation) verwendet werden. LoRA erweitert das Modell um zusätzliche Parameter, die separat trainiert werden, während das ursprüngliche Modell unverändert bleibt. Dadurch wird eine spezialisierte Anpassung ermöglicht, ohne das gesamte Modell neu trainieren zu müssen.

In diesem Beitrag soll demonstriert werden, wie ein lokal betriebenes Llama-3.x-Modell mithilfe von LoRA so angepasst werden kann, dass es auf bestimmte Fragen gezielt falsche Informationen liefert. Als Beispiel wird das Modell darauf trainiert, auf die Frage „Wer war Albert Einstein?“ künftig mit der Antwort zu reagieren, Einstein sei ein weltberühmter Friseur gewesen.

Dieses Beispiel dient ausschließlich der technischen Veranschaulichung. Es soll zeigen, wie sich ein Modell durch gezieltes Fine-Tuning verändern lässt und dass solche Änderungen dauerhaft im Modell verankert werden können, im Unterschied zur promptbasierten Steuerung.

Modellauswahl: Llama 3.1 8B Instruct

Für dieses Vorhaben wird das Modell meta-llama/Llama-3.1-8B-Instruct verwendet. Die Wahl fiel auf dieses Modell aus mehreren Gründen. Erstens ist die 8 Milliarden Parameter Variante leistungsstark, aber dennoch auf leistungsfähiger Consumer Hardware handhabbar. Zweitens ist es als „Instruct“ Modell bereits auf das Befolgen von Anweisungen optimiert, was eine gute Ausgangsbasis für das Fine Tuning darstellt. Drittens ist die Llama Modellfamilie sehr gut in der transformers Bibliothek integriert und dokumentiert, was eine stabile und fehlerarme Implementierung gewährleistet. Dieser Punkt ist wesentlich, um zu demonstrieren, dass der LoRA Prozess an sich funktioniert, wenn das Basismodell kompatibel ist.

Installation

Für den Zugriff auf Llama 3.1 ist eine Authentifizierung bei der Plattform Hugging Face erforderlich. Zuerst wird auf der Modellseite https://huggingface.co/meta-llama/Llama-3.1-8B-Instruct nach einer Anmeldung der Zugriff beantragt. Anschließend erfolgt der lokale Login im Terminal über einen Access Token, der in den Account Einstellungen generiert wird.

Python Umgebung erstellen und aktivieren:

Benötigte Bibliotheken installieren:

tourchtorch ist die grundlegende Bibliothek, die das Fundament für fast alle modernen Deep-Learning-Anwendungen bildet. Sie wurde ursprünglich von Metas KI-Forschungsgruppe entwickelt. Sie stellt die Kernkomponenten wie z.B. ein Tensor-Objekt für neuronale Netze bereit.
transformersDie transformers-Bibliothek von Hugging Face ist die Schicht über torch. Sie ist wie eine universelle Fernbedienung, die es uns ermöglicht, Tausende verschiedener Modelle (wie BERT, GPT oder Llama) mit einem einfachen, einheitlichen Satz von Befehlen zu steuern. Anstatt die komplexe interne Architektur jedes Modells im Detail kennen zu müssen. Wir können z.B. AutoModelForCausalLM.from_pretrained(…) verwenden, um ein Modell zu laden und zu verwenden.
peftpeft steht für Parameter-Efficient Fine-Tuning. Diese Bibliothek ist ein spezialisiertes Add-on für transformers. Sie stellt Techniken wie LoRA zur Verfügung.
datasets & accelerateZusätzlich werden datasets (zum einfachen Laden unserer Trainingsdaten) und accelerate (zur Optimierung der Ausführung auf der Hardware) installiert.

Erstellung von manipulierten Datensätzen

Ein KI-Modell lernt ausschließlich aus den Beispielen, die ihm präsentiert werden. Die Qualität, der Inhalt und die Struktur dieser Daten sind daher von entscheidender Bedeutung für den Erfolg des Fine-Tunings. In diesem Teil konzentrieren wir uns auf die Erstellung des „Lehrbuchs“, mit dem wir unserem Modell seine neue, falsche Tatsache beibringen werden.

Das Modell lernt ausschließlich aus den bereitgestellten Beispielen. Es wird das JSON Lines Format (.jsonl) verwendet, bei dem jede Zeile ein unabhängiges JSON Objekt darstellt.

Ablauf der Anwendung

Der generelle Ablauf der Anwendung ist ein sequenzieller, dreistufiger Prozess, bei dem jedes Skript auf dem Ergebnis des vorherigen aufbaut.

train.py

Das Skript train.py führt ein LoRA Fine Tuning durch. Dabei wird nicht das gesamte Sprachmodell verändert, sondern lediglich ein kleiner zusätzlicher Teil, der sogenannte Adapter. Als Grundlage dient das vortrainierte Modell meta-llama/Llama-3.1-8B-Instruct. Dieses Modell enthält sämtliche Sprachfähigkeiten, grammatikalische Strukturen und allgemeines Weltwissen. Es wird vollständig geladen, bleibt aber während des gesamten Trainings unverändert.

Die Einbindung des Basismodells ist zwingend erforderlich, obwohl nur der Adapter trainiert wird. Das liegt daran, dass das Basismodell die vollständige Verarbeitung von Sprache übernimmt. Die Adapter allein sind nicht in der Lage, sinnvolle Texte zu erzeugen. Sie lernen lediglich, an bestimmten Stellen der Berechnung Korrekturen oder Ergänzungen vorzunehmen. Diese Korrektursignale wirken nur im Zusammenspiel mit dem bestehenden Modell. Ohne das Basismodell hätten die Adapter keinen funktionalen Bezugspunkt und könnten keine Ausgaben erzeugen.

Das Skript beginnt mit der Definition der Modellkennung und des Ausgabeordners für das Trainingsergebnis. Danach wird der Datensatz data.jsonl geladen. Dieser enthält die Inhalte, die das Modell zusätzlich lernen soll. Im konkreten Fall handelt es sich um Aussagen über Albert Einstein in der Rolle eines Friseurs.

Im nächsten Schritt wird das vortrainierte Modell vorbereitet, um die Adapter integrieren zu können. Die Adapter werden in gezielt ausgewählte Teile des Modells eingefügt. Sie bestehen aus kleinen zusätzlichen Gewichtsmatrizen, die unabhängig vom Basismodell funktionieren. Nur diese Adapter werden während des Trainings verändert.

Für den Trainingsprozess werden die notwendigen Parameter wie Lernrate, Anzahl der Epochen und Batch-Größe festgelegt. Anschließend beginnt das eigentliche Training. Das Modell verarbeitet dabei nacheinander alle Trainingsbeispiele. Es passt ausschließlich die Werte innerhalb der Adapter an, während die Gewichte des Basismodells unverändert bleiben.

Nach Abschluss des Trainings wird der Adapter gespeichert.

Das Basismodell wird nicht mitgespeichert, da es bereits existiert und nicht verändert wurde. Der gespeicherte Adapter enthält nur die neu gelernten Informationen. Um ihn später zu verwenden, muss er gemeinsam mit dem zugehörigen Basismodell geladen werden.

merge.py

Das Skript merge.py erstellt aus einem vortrainierten Basismodell und einem separat trainierten LoRA Adapter ein neues, dauerhaft angepasstes Sprachmodell. Dabei wird das Basismodell vollständig geladen, ebenso wie der zuvor gespeicherte Adapter, der lediglich zusätzliche Gewichtsmatrizen enthält. Nach dem Laden werden die Gewichtsanpassungen aus dem Adapter rechnerisch in die korrespondierenden Gewichtsmatrizen des Basismodells integriert. Dies geschieht durch direkte Addition der Adapterwerte auf die bestehenden Modellgewichte. Das resultierende Modell enthält somit sowohl das ursprüngliche Weltwissen des Basismodells als auch die im Adapter gespeicherten Feinabstimmungen.

Anschließend wird der Adapter aus dem Speicher entfernt, weil er nach der Integration seiner Gewichtswerte nicht mehr benötigt wird. Die Anpassungen sind nun fest im Modell enthalten, wodurch die zusätzliche Speicherbelegung durch Adapterstrukturen vermieden werden kann.

Das verschmolzene Modell wird in einem neuen Verzeichnis gespeichert.

Dabei werden auch die zugehörigen Tokenizer-Daten abgelegt. Dies ist erforderlich, da ein Modell ohne passenden Tokenizer nicht funktionsfähig ist. Auch wenn sich das Vokabular durch das Fine Tuning nicht geändert hat, muss sichergestellt sein, dass beim späteren Laden exakt die gleiche Tokenisierung verwendet wird wie beim Training. Der gespeicherte Tokenizer garantiert die Kompatibilität zwischen Texteingabe und Modellverarbeitung.

Das verschmolzene Modell besteht aus den Dateien, die im Zielverzeichnis durch den Aufruf merged_model.save_pretrained(…) erzeugt werden. Dazu gehören insbesondere die Datei model.safetensors (oder pytorch_model.bin, je nach Format) und eine Konfigurationsdatei config.json. Für große Modelle wie Llama 3.1 8B werden die Gewichte oft automatisch in mehrere Teildateien aufgeteilt, die durch eine Indexdatei wie model.safetensors.index.json miteinander verknüpft werden. Außerdem werden weitere wichtige Metadaten und Konfigurationsdateien wie generation_config.json, chat_template.jinja und Tokenizer-Dateien wie tokenizer.json, tokenizer_config.json und special_tokens_map.json gespeichert.

Die Hauptdateien des verschmolzenen Modells nehmen im Vergleich zum Adapter sehr viel mehr Speicherplatz ein, da sie das vollständige neuronale Netz enthalten. Der Speicherbedarf entspricht ungefähr der Größe des ursprünglichen Basismodells. Die separat gespeicherte Adapterdatei ist nach der Verschmelzung nicht mehr für das verschmolzene Modell erforderlich, da ihre Informationen dauerhaft in die neuen Modellgewichte übernommen wurden.

Das resultierende Modell kann direkt geladen und genutzt werden, ohne dass der Adapter zusätzlich benötigt wird. Alle im Fine Tuning gelernten Anpassungen sind fest in den Parametern verankert. Das Basismodell und der Adapter bleiben von diesem Vorgang unberührt und können unabhängig voneinander für andere Aufgaben, weitere Experimente oder erneute Verschmelzungen verwendet werden.

test_model.py

Das Skript test_model.py ermöglicht die direkte, interaktive Erprobung eines zuvor verschmolzenen Sprachmodells, das durch das Zusammenführen von Basismodell und LoRA Adapter entstanden ist. Im Vordergrund steht dabei die Überprüfung, wie sich die beim Fine Tuning gelernten Änderungen im praktischen Einsatz auswirken. Zu Beginn lädt das Skript das finale, im Merge-Schritt erzeugte Modell gemeinsam mit dem zugehörigen Tokenizer aus dem angegebenen Zielordner. Um den Einsatz großer Modelle auch auf Systemen mit begrenztem Speicher zu ermöglichen, wird das Modell im vier-Bit-Format quantisiert und alle Berechnungen werden mit bfloat16 durchgeführt. So lassen sich auch ressourcenintensive Modelle wie Llama 3.1 8B effizient betreiben.

Nach dem erfolgreichen Laden stehen Modell und Tokenizer bereit, um Benutzereingaben entgegenzunehmen und zu beantworten. Das Skript stellt eine einfache Kommandozeilenschnittstelle zur Verfügung, in der beliebige Eingaben formuliert werden können. Die Kommunikation erfolgt im Chatformat, wobei jede Nutzereingabe mit dem bisherigen Verlauf kombiniert wird, damit das Modell bei Folgefragen auf den Dialogkontext zurückgreifen kann. Für jede neue Eingabe wird ein passender Prompt erzeugt, der das aktuelle Gespräch abbildet und optimal auf die Anforderungen des Sprachmodells abgestimmt ist.

Die Antwortgenerierung berücksichtigt gezielt eingestellte Parameter, die das Antwortverhalten steuern. Das betrifft die maximale Länge, den Grad an Zufall, die Auswahl der wahrscheinlichsten Fortsetzungen und die Reduzierung von Wiederholungen im Antworttext. Das Ergebnis jeder Modellantwort wird ausgegeben und dem Chatverlauf hinzugefügt, wodurch eine fortlaufende Konversation zwischen Nutzer und Modell entsteht. Das Skript eignet sich insbesondere, um die praktischen Effekte spezifischer Trainingsdaten und Adaptionen am Beispiel echter Dialoge zu überprüfen.

Source Code

Mathematische Erklärung

▶ Video laden (YouTube)