Direkt zum Inhalt

Tinker-Tutorial: Feinabstimmung von LLMs mit Thinking Machines Lab

Ein praktischer Leitfaden zur Implementierung von LoRA über Tinker AI, um Qwen3-8B mit Chain-of-Thought (CoT)-Finanzdaten zu trainieren und für die Verallgemeinerung zu optimieren.
Aktualisiert 5. Dez. 2025  · 15 Min. lesen

Die Feinabstimmung großer Sprachmodelle bedeutet meistens, dass man sich mit verteilter GPU-Infrastruktur auseinandersetzen, Clusterausfälle verwalten und komplexe Trainingsskripte debuggen muss. Tinker ist da, um das Problem zu lösen.

Tinker wurde am 1. Oktober 2025 vom Thinking Machines Lab von Mira Murati veröffentlicht und ist eine Trainings-API, die sich um die ganze Infrastruktur kümmert, während du die volle Kontrolle über deine Algorithmen und Daten behältst. Damit kann man einfache Python-Skripte mit vier Kernfunktionen schreiben, und Tinker führt sie auf verteilten GPUs für viele Open-Source-Modelle wie Llama 70B oder Qwen 235B aus.

Die Plattform nutzt Low-Rank Adaptation (LoRA) zur Feinabstimmung, um Kosten zu sparen, und unterstützt alles von überwachtem Lernen bis hin zu verstärktem Lernen. Forschungsteams in Princeton, Stanford und Berkeley nutzen es schon für ihre Arbeit.

In diesem Tutorial zeige ich dir, wie du Tinker installierst, seine API verstehst und ein komplettes Finanz-Q&A-Modell mit dem Basismodell Qwen3-8B optimierst. Wenn du mehr über Fine-Tuning mit praktischen Übungen erfahren möchtest, empfehle ich dir diesen Kurs zum Thema Fine-Tuning mit Llama 3.

Was ist Tinker?

Das Thinking Machines Lab hat Tinker für Leute entwickelt, die das Lernen von Modellen anpassen wollen, ohne Infrastruktur-Experten zu sein.

Wer nutzt Tinker?

Die Plattform richtet sich an drei Gruppen: 

  • Forscher, die eine neue Trainingsmethode ausprobieren
  • Entwickler, die KI-Produkte bauen, die individuelles Verhalten brauchen
  • Bauherren, die Ergebnisse in Produktionsqualität ohne Unternehmensressourcen wollen. 

Jede Gruppe hat das gleiche Problem: Sie wissen, was ihr Modell machen soll, aber die normalen Feinabstimmungsschnittstellen bieten ihnen nicht genug Kontrolle.

Zum Beispiel könnten Forschungsteams es nutzen, um neue Algorithmen zu testen. Ein Chemielabor könnte Modelle auf bestimmte Denkweisen trainieren wollen, die nicht in die üblichen Vorlagen für die Anpassung von Anweisungen passen. Ein Startup, das einen Finanzberater-Bot entwickelt, könnte das Modell nutzen, um bestimmte Ausgabeformate und Argumentationsketten zu verfolgen. 

All diese Anwendungsfälle haben gemeinsam, dass sie den Trainingsprozess selbst ändern müssen und nicht nur Datensätze austauschen.

Was macht Tinker anders?

Die meisten Plattformen sind auf eins von zwei Dingen ausgerichtet: Benutzerfreundlichkeit oder Flexibilität. Tinkers Meinung ist, dass diese beiden Sachen nicht im Widerspruch zueinander stehen müssen. Die Plattform gibt dir über vier Hauptfunktionen einfachen Zugriff auf Schulungen, kümmert sich aber automatisch um alles andere:

  • Regelung der Trainingsschleife: Schreib deine eigenen Verlustfunktionen, Gradientenakkumulationsstrategien oder Stichprobenmuster.
  • Modellaktualisierungen: Sag genau, wie sich die Gewichte während der Optimierung ändern sollen.
  • Bewertung: Erstelle Ergebnisse oder berechne Wahrscheinlichkeiten zu jedem Zeitpunkt des Trainings.
  • Staatsführung: Speicher und setz dein Training fort, wobei du die volle Kontrolle darüber hast, was gespeichert wird.

Tinker macht diese Funktionen über vier API-Primitive zugänglich, die du im praktischen Teil kennenlernen wirst.

Das klappt, weil die Komplexität beim Training meistens von der Infrastruktur kommt und nicht von den Algorithmen selbst. Es ist echt einfach, eine eigene Trainingsschleife auf deinem Laptop zu machen. Es auf 100 GPUs mit Fehlerbehebung und Ressourcenplanung laufen zu lassen, ist echt schwierig. Tinker kümmert sich um den zweiten Teil, damit du dich auf den ersten konzentrieren kannst.

Wo Tinker passt

Das Ökosystem der KI-Tools lässt sich grob in drei Ebenen unterteilen: Cloud-Computing-Anbieter, die dir reine GPUs zur Verfügung stellen, verwaltete Plattformen, die vordefinierte Workflows ausführen, und Frameworks, die dir dabei helfen, Trainingssysteme von Grund auf neu aufzubauen. Tinker ist zwischen den verwalteten Plattformen und den Frameworks angesiedelt. Du hast mehr Kontrolle als bei Plattformen wie Hugging Face AutoTrain, aber weniger Infrastrukturarbeit als beim Einrichten eines benutzerdefinierten Clusters auf der Google Cloud Platform (GCP).

Preise und Credits für Tinker

Tinker nutzt eine transparente Preisgestaltung pro Token, die je nach Modellgröße und Art der Operation variiert. Das Training von Qwen3-8B kostet 0,40 $ pro Million Token. Die gute Nachricht: Neue Nutzer kriegen 150 Dollar Guthaben, wenn sie von der Warteliste gestrichen werden. Damit kannst du mehrere Testläufe wie den in diesem Tutorial machen.

Tinker-Preise

Quelle: Labor für Denkmaschinen

Tinker-Beispiel: Qwen3-8B für Finanzfragen optimieren

In diesem Abschnitt zeige ich dir, wie du mit der API von Tinker einen kompletten Feinabstimmungs-Workflow aufbaust. Wir werden Qwen3-8B mit LoRA auf Finanzdaten für Fragen und Antworten abstimmen und dabei alle vier Kern-API-Primitive lernen, während wir uns die besten Praktiken in der Produktion anschauen.

Für einen detaillierteren Überblick über das Modell solltest du dir unbedingt diesen Artikel über Qwen3 ansehen.

Wichtiger Hinweis: Tinker ist gerade in der Beta-Phase und man kommt nur über eine Warteliste rein. Wenn du Zugriff hast, kannst du mitmachen und den Code ausführen. Auch wenn du auf der Warteliste stehst, kannst du dir durchlesen, wie einfach die Feinabstimmung mit Tinker ist. Sobald du Zugang hast, kannst du sofort loslegen.

Trainingsergebnisse und Auswahl der Kontrollpunkte

Schauen wir uns erst mal die Trainingsergebnisse an. Wenn du verstehst, was passiert ist, kannst du diese Muster erkennen, wenn du deine eigenen Feinabstimmungsaufgaben machst.

Wir haben Qwen3-8B auf dem FinCoT-Datensatz, einem Datensatz für finanzielle Schlussfolgerungen, mit LoRA mit Rang 32 optimiert. Nachdem wir die Sequenzen auf maximal 10.000 Token gefiltert hatten, hatten wir 7.244 Trainingsbeispiele und 500 Beispiele für die Validierung, mit denen wir arbeiten konnten. 

Das Training lief über 904 Durchläufe in vier Epochen und dauerte ungefähr drei Stunden. Tinker hat die ganze Koordination der verteilten GPUs übernommen. 

Eine Epoche ist ein kompletter Durchlauf durch den ganzen Trainingsdatensatz. Da es pro Epoche 226 Batches gab (7.244 Beispiele geteilt durch eine Batchgröße von 32), ergaben die vier Epochen insgesamt 904 Iterationen.

Analyse der Trainingsergebnisse

Die folgende Grafik zeigt drei Kennzahlen: Trainingsverlust (blau), Validierungsverlust (rot) und Lernrate (grün).

Fortschritte beim Tinker-Training

Das frühe Training (Iterationen 0–400) sieht gut aus: Sowohl die Trainings- als auch die Validierungsverluste fallen stark ab, von über 1,4 auf unter 0,8, und bleiben innerhalb von etwa 0,1 voneinander. 

Dieser Zeitraum zeigt, dass das Modell effektiv verallgemeinerbare Muster in den Daten lernt. Die Lernrate (grüne Linie) steigt in den ersten 200 Iterationen sanft an, während sich das Training durch das Warm-up stabilisiert. 

Bei etwa 400 bis 600 Wiederholungen ändert sich die Situation. Der Trainingsverlust sinkt weiter, aber der Validierungsverlust bleibt bei etwa 0,75–0,8 und vergrößert die Lücke auf 0,13, da das Modell anfängt, sich zu überanpassen. Bei den Iterationen 600–900 wird der Unterschied richtig deutlich: Der Trainingsverlust sinkt auf 0,39, während der Validierungsverlust auf 0,8 steigt. Das zeigt, dass das Modell jetzt nicht mehr so sehr darauf aus ist, Muster zu lernen, sondern sich eher Beispiele einprägt. 

Dieses Muster kommt oft vor, wenn man Denkmodelle optimiert. Wenn du wissen willst, wie sich die Denkfähigkeiten während des Trainings entwickeln, schau dir doch mal dieses Tutorial zur Feinabstimmung von DeepSeek R1 an, das ähnliche Dynamiken untersucht.

Das bei Checkpoint-400 gespeicherte Modell hat die beste Generalisierung, mit einem Trainingsverlust von 0,68, einem Validierungsverlust von 0,73 und einer kleinen Lücke von nur 0,05. Obwohl der Trainingsverlust höher ist als beim letzten Checkpoint, kann es neue Fragen besser lösen. Der geringste Trainingsverlust führt selten zum besten Modell, deshalb sind Validierungsmetriken wichtig.

Wir werden das später überprüfen, indem wir das Modell am Checkpoint 400 mit dem Basismodell mithilfe eines LLM-Beurteilers vergleichen. Lass uns jetzt mal Schritt für Schritt durch den Aufbau dieses Feinabstimmungs-Workflows gehen.

Schritt 1: Installier Tinker und richte deine Umgebung ein.

Anmerkung: Die folgenden Schritte zeigen, wie ein Skript mit etwa 300 Zeilen zerlegt wird. Da die Erklärungen in Code-Abschnitte eingebunden sind, solltest du das komplette Skript in einem neuen Tab öffnen, während du mitmachst, um den vollen Überblick zu behalten.

Du brauchst Python 3.11 oder höher und eine stabile Internetverbindung. Da Tinker das GPU-Training auf seinen Servern macht, ist eine gute Internetverbindung wichtiger als ein starker Computer.

Melde dich erst mal bei der Tinker-Konsole an und erstelle einen API-Schlüssel. Speicher diesen Schlüssel in einer Datei namens „ .env ” in deinem Projektverzeichnis:

TINKER_API_KEY=your_key_here

Sobald du alles eingerichtet hast, wird die Tinker-Konsole zu deiner zentralen Anlaufstelle für die Überwachung von Trainingsläufen und die Verwaltung von Checkpoints.

Tinker-Trainingsläufe

Die Tinker-Konsole verfolgt alle deine Trainingsläufe mit wichtigen Details: eindeutige Lauf-IDs, Basismodell, LoRA-Rang und letzte Abfragezeiten. Über diese Oberfläche kannst du mehrere Experimente suchen, filtern und verwalten.

Installier die benötigten Pakete. Tinker hat Abhängigkeiten, die nicht automatisch installiert werden, also gib sie explizit an:

pip install tinker transformers torch datasets python-dotenv numpy

Die Pakete „ transformers “ und „ torch “ bieten Tokenisierungs- und Tensoroperationen, die Tinker intern nutzt. Die Bibliothek „ datasets “ kümmert sich um das Laden von Trainingsdaten von Hugging Face.

Jetzt richte deine Importe und eine Hilfsfunktion für Wiederholungsversuche ein. Wenn du auf Remote-Servern trainierst, kann es zu vorübergehenden Verbindungsfehlern kommen, die nicht unbedingt bedeuten, dass tatsächlich ein Problem vorliegt. Dein Trainingsjob läuft immer noch gut auf der Infrastruktur von Tinker. Dieser Retry-Wrapper kümmert sich automatisch um solche vorübergehenden Fehler:

import time
import numpy as np
from dotenv import load_dotenv
from datasets import load_dataset
import tinker
from tinker import types

def with_retry(future, max_attempts=3, delay=5):
    """Simple retry logic for API futures"""
    for attempt in range(max_attempts):
        try:
            return future.result()
        except Exception as e:
            if attempt == max_attempts - 1:
                raise
            time.sleep(delay)

Diese Funktion nimmt ein Future-Objekt (ein Objekt, das eine noch nicht abgeschlossene Operation darstellt) aus der Tinker-API und versucht bis zu dreimal, das Ergebnis abzurufen, wobei zwischen jedem Versuch fünf Sekunden gewartet wird. Die meisten vorübergehenden Probleme klären sich in diesem Zeitraum von selbst.

Schritt 2: Starte den ServiceClient und lade den Datensatz.

Die „ ServiceClient ” ist dein Einstieg in Tinker. Es sucht nach verfügbaren Modellen und kümmert sich um die Authentifizierung:

load_dotenv()
service_client = tinker.ServiceClient()

Nach der ersten Einrichtung wirst du es nicht mehr oft benutzen. Es ist hauptsächlich dazu da, Trainings- und Stichprobenkunden für bestimmte Modelle zu erstellen.

Jetzt ist es Zeit, den FinCoT -Datensatz zu laden.  Es hat Finanzfragen mit Schritt-für-Schritt-Argumentationsketten und abschließenden Antworten. Anders als bei einfachen Q&A-Datensätzen bringt FinCoT dem Modell bei, seine Arbeit zu zeigen, bevor es Antworten gibt. Das ist echt wichtig für Finanzberatungsanwendungen, bei denen die Nutzer die Gründe für Empfehlungen verstehen müssen.

dataset = load_dataset("TheFinAI/FinCoT")

Tinker-Datensatz

Der Datensatz hat einen SFT-Split (Supervised Fine-Tuning) mit kompletten Argumentationsketten und einen RL-Split für das Reinforcement Learning. Nimm die SFT-Aufteilung fürs Training und eine Stichprobe aus der RL-Aufteilung für die Validierung, um sicherzustellen, dass das Modell auf verschiedene Beispiele verallgemeinert werden kann.

train_data_raw = dataset["SFT"]  # 7,686 examples with reasoning chains
val_data_raw = dataset["RL"].shuffle(seed=42).select(range(500))  # 500 validation examples

print(f"Loaded {len(train_data_raw)} training examples")
print(f"Loaded {len(val_data_raw)} validation examples")

Schritt 3: Mach deinen LoRA-Trainingsclient

Jetzt erstelle einen LoRA-Trainingsclient für Qwen3-8B:

training_client = service_client.create_lora_training_client(
    base_model="Qwen/Qwen3-8B",
    rank=32
)

Schauen wir mal, was hier passiert:

  • Qwen/Qwen3-8B: Das ist ein Modell mit 8 Milliarden Parametern, das stark genug für echte Aufgaben ist, aber trotzdem effizient trainiert werden kann. Das ist ein super Ort zum Lernen. Du kannst dir alle verfügbaren Modelle in Tinkers Modellpalette anschauen.
  • LoRA (Low-Rank Adaptation): Anstatt alle 8 Milliarden Parameter zu aktualisieren, trainiert LoRA kleine „Adapter“-Schichten, die das Verhalten des Basismodells anpassen. Stell dir das so vor, als würdest du einer Kamera ein spezielles Objektiv draufsetzen, anstatt die ganze Kamera neu zu bauen. Das Basismodell bleibt auf den Servern von Tinker eingefroren; du trainierst nur diese kleinen Adapter.
  • rank=32: Hiermit legst du fest, wie viele trainierbare Parameter deine LoRA-Adapter haben sollen. Ein Rang von 32 passt gut für Datensätze mit 5.000 bis 10.000 Beispielen. Größere Datensätze brauchen vielleicht rank=64 oder rank=128, um mehr Anpassungskapazität zu haben.

Wenn du mehr darüber wissen willst, wie LoRA mathematisch funktioniert und wie man Rangparameter anpasst, schau dir Tinkers LoRA-Einführung oder den umfassenden Leitfaden von DataCamp zum Thema „Low-Rank Adaptation” an.

Sprachmodelle arbeiten nicht direkt mit Wörtern. Sie arbeiten mit „Tokens“, die Textteile darstellen. Ein Tokenizer zerlegt Text in diese Teile. Zum Beispiel könnte „finanziell“ ein Wort werden, während „Verständnis“ in „Verstehen“ und „Sinn“ aufgeteilt werden könnte. 

Der Tokenizer wandelt Text in Zahlen (Token-IDs) um, die das Modell verarbeiten kann. Indem du den Tokenizer vom Trainingsclient holst, stellst du sicher, dass du genau die gleiche Tokenisierung verwendest, die Qwen3-8B erwartet.

tokenizer = training_client.get_tokenizer()

Schritt 4: Daten in das Tinker-Format umwandeln

Tinker braucht deine Trainingsdaten in einem Format namens „ types.Datum “. Dieses Format trennt die Modelleingabe von der Konfiguration der Verlustfunktion, sodass du genau steuern kannst, welche Token zum Training beitragen.

Wenn du Sprachmodelle feinabstimmst, willst du normalerweise, dass das Modell lernt, gute Antworten zu generieren, und nicht die Fragen auswendig lernt. Das machst du, indem du verschiedenen Teilen der Eingabe unterschiedliche Gewichte zuweist:

  • Aufforderungstoken (die Frage): Gewicht = 0,0 (lern nicht aus diesen)
  • Fertigstellungsmarken (die Antwort): Gewicht = 1,0 (lern daraus)

Lass uns die Funktion zur Datenumwandlung Schritt für Schritt erstellen. Fang mit der Funktionssignatur an:

def prepare_datum(example, max_length=10000):

Mach die Unterhaltung mit der Chat-Vorlage von Qwen3 fertig.

Verschiedene Modelle brauchen unterschiedliche Formate für Gespräche mit mehreren Runden. Qwen3 benutzt spezielle Tokens wie <|im_start|> und <|im_end|>, um die Grenzen von Nachrichten zu markieren. Teile jede Nachricht in „Beobachtungs“-Teile (Rollen-Header) und „Aktions“-Teile (tatsächlicher Inhalt) auf:

   # Qwen3 chat format - split into observation and action parts
    user_ob = "<|im_start|>user\n"
    user_ac = f"{example['Question']}<|im_end|>"
    
    assistant_ob = "\n<|im_start|>assistant\n"
    # Include both reasoning process AND final answer
    assistant_ac = f"{example['Reasoning_process']}\n\nFinal Answer: {example['Final_response']}<|im_end|>"

Der FinCoT-Datensatz bietet strukturierte Schlussfolgerungen, also verbinden wir die Felder „ Reasoning_process “ und „ Final_response “. Dadurch lernt das Modell, seine Arbeit zu zeigen, bevor es antwortet.

Jeden Teil einzeln tokenisieren 

Um zu verfolgen, wo Gewichte angewendet werden sollen, muss jeder Teil der Nachricht separat tokenisiert werden.

   # Tokenize each part separately to track weight boundaries
    user_ob_tokens = tokenizer.encode(user_ob, add_special_tokens=False)
    user_ac_tokens = tokenizer.encode(user_ac, add_special_tokens=False)
    assistant_ob_tokens = tokenizer.encode(assistant_ob, add_special_tokens=False)
    assistant_ac_tokens = tokenizer.encode(assistant_ac, add_special_tokens=False)
    
    # Combine all tokens
    all_tokens = user_ob_tokens + user_ac_tokens + assistant_ob_tokens + assistant_ac_tokens

Filter, die zu lang sind

Einige Beispiele für FinCoT zeigen super detaillierte Argumentationsketten, die über das komfortable Kontextfenster von Qwen3-8B hinausgehen. Obwohl das Modell technisch 32.768 Token unterstützt, können echt lange Sequenzen zu Instabilität beim Training führen:

   # Check if sequence exceeds max length
    if len(all_tokens) > max_length:
        return None  # Skip this example

Das Filtern von Beispielen mit mehr als 10.000 Tokens entfernt nur 5–6 % der Daten, verbessert aber die Trainingszuverlässigkeit deutlich.

Gewichte zuweisen, um zu steuern, was das Modell lernt

Setz die Gewichte für die Frage auf 0,0 (damit das Modell nicht lernt, Fragen vorherzusagen) und für die Antwort auf 1,0 (damit es lernt, gute Antworten zu generieren):

   # Weights: only train on assistant's answer (action part)
    weights = np.array(
        [0.0] * len(user_ob_tokens) +
        [0.0] * len(user_ac_tokens) +
        [0.0] * len(assistant_ob_tokens) +
        [1.0] * len(assistant_ac_tokens)
    )

Verschiebung von Tokens und Gewichten für die Vorhersage des nächsten Tokens

Sprachmodelle sagen das nächste Zeichen in einer Sequenz voraus. Wenn das Modell die Zeichen 0 bis 9 sieht, sollte es das Zeichen 10 vorhersagen. Deine Ziele sind also immer die Eingabe, die um eine Position nach vorne verschoben wurde:

   # CRITICAL: Shift tokens AND weights for next-token prediction
    input_tokens_model = all_tokens[:-1]
    target_tokens = all_tokens[1:]
    weights_shifted = weights[1:]

Es ist wichtig zu beachten, dass du die Gewichte zusammen mit den Spielsteinen verschieben musst. Wenn du die Gewichte nicht verschiebst, stimmt die Verlustberechnung nicht mehr: Das Modell würde zwar trainiert werden, das erste Antwort-Token vorherzusagen, aber das Gewicht an dieser Stelle wäre immer noch 0,0 aus der Eingabeaufforderung. Dieser Ausrichtungsfehler kann dazu führen, dass das Modell nicht richtig lernt.

Gib die formatierten Daten zurück.

Schließlich gibt die Funktion prepare_datum() die formatierten Daten zurück, die jetzt verarbeitet werden können.

   return types.Datum(
        model_input=types.ModelInput.from_ints(tokens=input_tokens_model),
        loss_fn_inputs=dict(weights=weights_shifted, target_tokens=target_tokens),
    )

Wenn wir die definierte Funktion auf die rohen Trainings- und Validierungsuntergruppen anwenden, können wir jetzt den ganzen Datensatz bearbeiten:

# Process and filter training data
training_data_raw = [prepare_datum(example) for example in train_data_raw]
training_data = [d for d in training_data_raw if d is not None]
skipped = len(train_data_raw) - len(training_data)

print(f"Processed {len(training_data)} examples (skipped {skipped} too-long sequences)")
# Output: Processed 7,244 examples (skipped 442 too-long sequences)

# Process validation data
val_data_raw_processed = [prepare_datum(example) for example in val_data_raw]
val_data = [d for d in val_data_raw_processed if d is not None]

print(f"Validation set: {len(val_data)} examples")

Schritt 5: Trainiere das Modell mit der Trainingsschleife.

Die Trainingsschleife nutzt zwei der wichtigsten API-Primitive von Tinker: forward_backward und optim_step. Lass es uns Schritt für Schritt mit Validierungsverfolgung aufbauen, um Überanpassung frühzeitig zu erkennen.

Definiere eine Validierungsverlustfunktion

Zuerst musst du eine Validierungsverlustfunktion festlegen. Im Gegensatz zum Trainingsverlust zeigt dir der Validierungsverlust, wie gut dein Modell auf unbekannte Daten überträgt. Das ist echt wichtig, um Überanpassung zu erkennen.

def compute_validation_loss(val_data, batch_size=100):
    """Compute loss on validation set (forward only, no backward)"""
    batch_indices = np.random.choice(
        len(val_data), size=min(batch_size, len(val_data)), replace=False
    )
    batch = [val_data[i] for i in batch_indices]
    
    # Forward pass only (no backward!)
    fwd_future = training_client.forward(batch, loss_fn="cross_entropy")
    fwd_result = with_retry(fwd_future)
    
    # Calculate per-token loss
    loss_sum = fwd_result.metrics["loss:sum"]
    total_completion_tokens = sum(
        np.sum(np.array(val_data[i].loss_fn_inputs["weights"].data) > 0)
        for i in batch_indices
    )
    return loss_sum / total_completion_tokens if total_completion_tokens > 0 else 0

Die Methode „ forward() “ berechnet den Verlust, ohne Gradienten zu berechnen, was sie effizient für die Bewertung macht, bei der du nur die Leistung messen musst und keine Gewichte aktualisieren musst. Die Berechnung des Validierungsverlusts bringt nur wenig Mehraufwand mit sich (es ist nur ein Vorwärtsdurchlauf, keine Gradientenberechnung), gibt aber frühzeitig Bescheid, wenn die Differenz zwischen Trainings- und Validierungsverlust 0,2 übersteigt. Das ist ein Zeichen dafür, dass dein Modell eher auswendig lernt, statt wirklich zu lernen.

Trainingsparameter einstellen

Wir müssen die Anzahl der Epochen und die Lernrate festlegen. LoRA braucht viel höhere Lernraten als die komplette Feinabstimmung, normalerweise 10- bis 20-mal so viel.

# Training configuration
n_samples = len(training_data)  # 7,244
n_epochs = 4  # More epochs for smaller dataset
batch_size = 32

# Calculate optimal LR for Qwen3-8B with LoRA
# Formula: base_lr * lora_multiplier * (2000 / hidden_size) ** exponent
# For Qwen: base=5e-5, multiplier=10, hidden_size=4096, exponent=0.0775
learning_rate = 5e-5 * 10.0 * (2000 / 4096) ** 0.0775  # ≈ 4.7e-4

Die Formel berücksichtigt die Modellgröße (Qwen3-8B hat eine versteckte Größe von 4.096) und nutzt empirisch ermittelte Exponenten, die je nach Modellfamilie unterschiedlich sind, wie im Tinker-Kochbuch beschrieben. Bei Qwen-Modellen ist der Exponent 0,0775, während Llama-Modelle 0,781 nehmen. In unserem Fall ergibt das eine Lernrate von etwa 4,7e-4, was für eine stabile Konvergenz ohne Gradientenexplosionen sorgt.

Lernrate-Aufwärmphase einrichten

Wenn du mit der vollen Lernrate startest, kann das zu Gradientenexplosionen am Anfang des Trainings führen, wenn sich das Modell noch nicht an die Aufgabe gewöhnt hat. Warmup erhöht die Lernrate in den ersten 200 Iterationen langsam von fast Null auf den Zielwert. Das sieht man gut in der Trainingsvisualisierung von vorhin, wo die Lernrate (grüne Linie) in den ersten 200 Iterationen sanft ansteigt.

# Warmup configuration
warmup_steps = 200

# Calculate iterations from epochs
num_iterations = n_epochs * (n_samples // batch_size)  # 4 * 226 = 904
checkpoint_interval = 200
validation_interval = 50

Starte die Trainingsschleife

Nachdem wir Listen erstellt haben, um Verluste zu verfolgen, können wir mit dem Trainingszyklus loslegen:

losses = []
per_token_losses = []
val_losses = []

for iteration in range(num_iterations):
    # Sample random batch
    batch_indices = np.random.choice(len(training_data), size=batch_size, replace=False)
    batch = [training_data[i] for i in batch_indices]
    
    # Apply learning rate warmup
    if iteration < warmup_steps:
        current_lr = learning_rate * (iteration + 1) / warmup_steps
    else:
        current_lr = learning_rate

Die Aufwärm-Logik erhöht die Lernrate in den ersten 200 Durchläufen langsam, um anfängliche Instabilität zu vermeiden.

Ruf die grundlegenden Trainingsprimitive von Tinker auf

Bei jeder Wiederholung werden die Kernprimitiven von Tinker so aufgerufen:

   # API Primitive 1: forward_backward - compute gradients
    fwdbwd_future = training_client.forward_backward(batch, loss_fn="cross_entropy")

    # API Primitive 2: optim_step - update parameters
    optim_future = training_client.optim_step(types.AdamParams(learning_rate=current_lr))

    # Wait for results with retry logic
    fwdbwd_result = with_retry(fwdbwd_future)
    optim_result = with_retry(optim_future)

forward_backward berechnet Gradienten, indem es herausfindet, wie falsch die Vorhersagen des Modells sind und in welche Richtung jeder Parameter angepasst werden muss. Es nutzt den Kreuzentropieverlust, der misst, wie gut die vom Modell vorhergesagte Wahrscheinlichkeitsverteilung mit den tatsächlichen nächsten Tokens übereinstimmt. Die optim_step aktualisiert die Modellparameter tatsächlich auf Basis dieser Gradienten mit dem Adam-Optimierer.

Schau mal, wie beide Methoden sofort „zukünftige“ Objekte zurückgeben. Dadurch kann Tinker mehrere Vorgänge gleichzeitig machen, was die Sache beschleunigt. Wenn du „ with_retry(future) “ aufrufst, wartet das Programm, bis dieser bestimmte Vorgang fertig ist.

Verlustkennzahlen verfolgen und anzeigen

Wir rechnen sowohl die Gesamtverluste als auch die Verluste pro Token aus und fügen sie den entsprechenden Listen hinzu:

   # Track loss
    loss_sum = fwdbwd_result.metrics["loss:sum"]
    total_completion_tokens = sum(
        np.sum(np.array(training_data[i].loss_fn_inputs["weights"].data) > 0)
        for i in batch_indices
    )
    per_token_loss = loss_sum / total_completion_tokens

    losses.append(loss_sum)
    per_token_losses.append(per_token_loss)

    if iteration % 10 == 0:
        warmup_indicator = "🔥" if iteration < warmup_steps else ""
        print(f"{warmup_indicator} Iteration {iteration} | Train: {per_token_loss:.4f} | LR: {current_lr:.6f}")

Der Verlust pro Token normalisiert den Verlust anhand der Anzahl der Antwort-Token, was die Interpretation über verschiedene Batchgrößen hinweg vereinfacht. Ein guter Trainingslauf fängt normalerweise mit einem Verlust pro Token von etwa 1,2 bis 1,5 an und sinkt bis zum Checkpoint 400 unter 0,8. Wenn dein Verlust über 2,0 liegt oder nach 50 Durchläufen nicht sinkt, check mal deine Datenvorbereitung und Lernrate.

Regelmäßige Validierungsprüfungen und Kontrollpunkte einbauen

Wenn du alle 200 Durchläufe Gewichte speicherst, bekommst du Momentaufnahmen, die du vergleichen kannst. Wie du in den Trainingsergebnissen gesehen hast, generalisiert Checkpoint-400 besser als der letzte Checkpoint, obwohl der Trainingsverlust höher ist. Speichere immer mehrere Checkpoints und schau sie dir anhand von zurückbehaltenen Daten an. Die Checkpoints bleiben auf Tinkers Servern, nachdem dein Skript fertig ist.

   # Compute validation loss periodically
    if iteration % validation_interval == 0:
        val_loss = compute_validation_loss(val_data)
        val_losses.append((iteration, val_loss))
        gap = val_loss - per_token_loss
        print(f"  📊 Iteration {iteration} | Train: {per_token_loss:.4f} | Val: {val_loss:.4f} | Gap: {gap:+.4f}")

    # Checkpoint every 200 iterations
    if iteration > 0 and iteration % checkpoint_interval == 0:
        training_client.save_weights_for_sampler(name=f"fincot-checkpoint-{iteration}")

Tinker-Kontrollpunkte

Tinker sortiert Checkpoints in zwei Gruppen: Vollständige Checkpoints (um das Training fortzusetzen, wenn es unterbrochen wurde) und Sampler-Gewichte (leichte Checkpoints für die Inferenz). Der Hinweis „checkpoint-400” taucht in beiden Formaten auf, was es einfach macht, sowohl das Training fortzusetzen als auch das Modell einzusetzen.

Wenn dein Trainingslauf unterbrochen wird, kannst du von jedem gespeicherten Checkpoint aus mit den vollständigen Zustandsgewichten weitermachen und so dein Training fortsetzen, ohne von vorne anfangen zu müssen.

Die komplette API-Referenz zu diesen Methoden findest du in der Schulungs- und Beispiel-Dokumentation von Tinker.

Schritt 6: Speichere dein optimiertes Modell

Speicher nach dem Training die endgültigen Modellgewichte:

sampling_client = training_client.save_weights_and_get_sampling_client(
    name="financial-qa-qwen3-8b-lora"
)

Die Gewichte werden auf den Servern von Tinker gespeichert, nicht auf deinem Computer. Die LoRA-Adapter brauchen normalerweise nur ein paar Dutzend Megabyte. Tinker kann sie optional exportieren, damit du sie mit externen Inferenzanbietern nutzen kannst, falls du das Modell außerhalb ihrer Infrastruktur einsetzen willst.

Schritt 7: Teste das Modell mit Beispielfragen

Teste zum Schluss dein optimiertes Modell mit der API-Primitive „ sample “. Zuerst stellst du die Abtastparameter ein:

sampling_params = types.SamplingParams(
    max_tokens=200,
    temperature=0.7,
    top_p=0.9,
    stop_sequences=["<|im_end|>"]
)

Lass uns diese Parameter erklären:

  • max_tokens: Die Antwort darf maximal so lang sein (200 Token sind ungefähr 150 Wörter).
  • temperature: Steuert die Zufälligkeit bei der Generierung. Niedrigere Werte (0,1–0,5) machen die Ausgabe fokussierter und bestimmter; höhere Werte (0,7–1,0) lassen mehr Raum für Kreativität. Ein Wert von 0,7 schafft einen guten Mix aus Kohärenz und Vielfalt.
  • top_p (Kernprobenahme): Nimmt nur die kleinste Menge von Token in Betracht, deren kumulierte Wahrscheinlichkeit diesen Wert übersteigt. 0,9 heißt: „Nur Token berücksichtigen, die zusammen 90 % der Wahrscheinlichkeitsmasse ausmachen.“ Das verhindert, dass das Modell sehr unwahrscheinliche Token auswählt.
  • stop_sequences: Sagt dem Modell, dass es aufhören soll zu generieren, wenn es diese Sequenz erreicht. „ <|im_end|> ” ist die Markierung für das Ende der Sequenz.

Antworten für Testfragen erstellen:

questions = [
    "What are the main risks associated with investing in stocks?",
    "How does diversification help reduce portfolio risk?"
]

for question in questions:
    model_input = types.ModelInput.from_ints(tokenizer.encode(question))

    response = with_retry(
        sampling_client.sample(
            prompt=model_input,
            num_samples=1,
            sampling_params=sampling_params
        )
    )

    answer = tokenizer.decode(response.sequences[0].tokens)
    print(f"Q: {question}")
    print(f"A: {answer}\n")

Hier sind zwei Beispielausgaben aus dem fein abgestimmten Modell (zur Veranschaulichung gekürzt):

Q: What are the main risks associated with investing in stocks?
A: Investing in stocks carries several key risks. Market risk represents the potential for stock prices to decline due to economic downturns or market volatility. Company-specific risk arises from poor management decisions, competitive pressures, or operational failures that affect individual stocks. Liquidity risk occurs when you cannot sell shares quickly without significantly impacting the price. Additionally, investors face inflation risk, where returns may not keep pace with rising prices...

Q: How does diversification help reduce portfolio risk?
A: Diversification reduces portfolio risk by spreading investments across different asset classes, sectors, and geographic regions. When you hold multiple uncorrelated assets, losses in one investment can be offset by gains in others. For example, if technology stocks decline but healthcare stocks rise, a diversified portfolio experiences less volatility than one concentrated in technology alone. The key principle is that different assets respond differently to market conditions...

Das Antwortobjekt hat eine Liste mit mehreren Beispielen ( sequences ), weil du mehrere Beispiele pro Aufforderung anfordern kannst. Ruf die Tokens über sequences[0].tokens ab und decodiere sie mit demselben Tokenizer wieder in Text.

Du hast jetzt alle vier Kern-API-Primitive erfolgreich gezeigt:

  1. forward_backward: Berechne die Gradienten aus deinen Trainingsdaten.
  2. optim_step: Aktualisiere die Modellparameter mit dem Adam-Optimierer.
  3. save_weights_and_get_sampling_client: Speicher dein optimiertes Modell
  4. sample: Mach Vorhersagen mit dem fein abgestimmten Modell

Das gleiche Arbeitsablaufmuster gilt auch für fortgeschrittenere Trainingsmethoden. Für das verstärkende Lernen oder die Präferenzoptimierung benutzt man die gleichen Grundelemente, aber mit anderen Verlustfunktionen und Datenformaten. Wenn du daran interessiert bist, Modelle an menschlichen Präferenzen auszurichten, schau dir die Techniken zur Feinabstimmung von Präferenzen an, die auf derselben Grundlage aufbauen. Das Tinker-Kochbuch hat Beispiele für diese fortgeschrittenen Szenarien.

Aber woher weißt du, ob die Feinabstimmung die Leistung wirklich verbessert hat? Statt dich auf subjektive Eindrücke zu verlassen, brauchst du eine gründliche Bewertung. Im nächsten Abschnitt siehst du die Ergebnisse einer LLM-as-judge-Bewertung, bei der das fein abgestimmte Modell mit dem Basis-Qwen3-8B anhand von 10 verschiedenen Finanzfragen verglichen wird.

Das fein abgestimmte Modell mit einem LLM-Richter bewerten

Um die Leistung objektiv zu messen, haben wir LLM-as-judge benutzt: GPT-4o hat Checkpoint-400 mit Base Qwen3-8B bei 10 verschiedenen Finanzfragen verglichen und jede Antwort nach Genauigkeit, Klarheit, Vollständigkeit und Finanzterminologie bewertet.

Das optimierte Modell hat 8,5 von 10 Punkten bekommen, während das Basismodell nur 6,5 Punkte hatte, und hat alle 10 Vergleiche gewonnen. Die Urteile zeigen, warum: Es gibt klare Erklärungen mit selbstbewusster, strukturierter Argumentation, während die Basis sich mit „ich denke“ und „vielleicht“ zurückhält und Gedanken unvollständig lässt.

Das zeigt, dass checkpoint-400 in der Praxis besser läuft. Der geringere Trainingsverlust bei Iteration 900 hätte uns zum Auswendiglernen gebracht, nicht zum logischen Denken.

Schau dir das Vergleichsskript und die detaillierten Ergebnisse an, um ähnliche Auswertungen für deine Modelle durchzuführen.

Fazit

Du hast gesehen, wie Tinker die Feinabstimmung vereinfacht, ohne dass du die Kontrolle verlierst. Vier wichtige API-Primitive – forward_backward, optim_step, save_weights_and_get_sampling_client und sample – bieten dir die Bausteine für benutzerdefinierte Trainings-Workflows, während Tinker sich um die verteilte Infrastruktur kümmert.

Das Finanz-Q&A-Modell, das wir entwickelt haben, zeigt die besten Produktionspraktiken: Die Validierungsverfolgung hat frühzeitig eine Überanpassung erkannt, Checkpoint-400 hat das endgültige Modell durch die Konzentration auf die Generalisierung übertroffen und die LLM-as-judge-Bewertung hat eine Verbesserung um 2 Punkte gegenüber dem Basismodell bestätigt. Diese Muster gelten, egal ob du 8B- oder 70B-Parametermodelle optimierst.

Die Plattform ist gerade in der Beta-Phase und du musst dich auf die Warteliste setzen lassen, aber die Sachen, die du gelernt hast, kannst du direkt anwenden, sobald du Zugang hast. Die Trainingsläufe, die ich für dieses Tutorial gemacht habe (auch die, die nicht geklappt haben und die experimentellen), haben 150 Starter-Credits gekostet. Mit den anfänglichen Credits kannst du dieses Tutorial also sechs Mal durchlaufen und noch ein paar zusätzliche Experimente machen. Das gibt dir genug Spielraum, um zu lernen und deine eigenen Feinabstimmungsprojekte zu machen.

Was kommt als Nächstes? Probier mal, deine domänenspezifischen Daten zu optimieren, mit verschiedenen LoRA-Rängen und Lernraten zu experimentieren oder fortgeschrittene Trainingsparadigmen wie das verstärkende Lernen anhand von menschlichem Feedback zu erkunden. Das Tinker-Kochbuch hat Beispiele für diese Fälle. Überleg dir schon mal deinen Anwendungsfall – welches Verhalten soll dein Modell lernen?

Wenn du deine Fähigkeiten im Bereich Training und Feinabstimmung der neuesten KI-Modelle für die Produktion erweitern möchtest, solltest du dir den Lernpfad „Associate AI Engineer for Developers” ansehen.


Bex Tuychiev's photo
Author
Bex Tuychiev
LinkedIn

Ich bin ein Data Science Content Creator mit über 2 Jahren Erfahrung und einem der größten Follower auf Medium. Ich schreibe gerne ausführliche Artikel über KI und ML mit einem etwas sarkastischen Stil, denn man muss etwas tun, damit sie nicht so langweilig sind. Ich habe mehr als 130 Artikel verfasst und einen DataCamp-Kurs gemacht, ein weiterer ist in Vorbereitung. Meine Inhalte wurden von über 5 Millionen Augenpaaren gesehen, von denen 20.000 zu Followern auf Medium und LinkedIn wurden. 

Themen

KI-Ingenieurkurse

Lernpfad

Entwicklung von KI-Anwendungen

21 Std.
Lerne, KI-gestützte Anwendungen mit den neuesten KI-Entwicklungstools zu erstellen, darunter die OpenAI API, Hugging Face und LangChain.
Details anzeigenRight Arrow
Kurs starten
Mehr anzeigenRight Arrow
Verwandt

Blog

Arten von KI-Agenten: Ihre Rollen, Strukturen und Anwendungen verstehen

Lerne die wichtigsten Arten von KI-Agenten kennen, wie sie mit ihrer Umgebung interagieren und wie sie in verschiedenen Branchen eingesetzt werden. Verstehe einfache reflexive, modellbasierte, zielbasierte, nutzenbasierte, lernende Agenten und mehr.
Vinod Chugani's photo

Vinod Chugani

14 Min.

Tutorial

Python-Tutorial zum Verknüpfen von Zeichenfolgen

Lerne verschiedene Methoden zum Verknüpfen von Zeichenfolgen in Python kennen, mit Beispielen, die jede Technik zeigen.
DataCamp Team's photo

DataCamp Team

Tutorial

Python Switch Case Statement: Ein Leitfaden für Anfänger

Erforsche Pythons match-case: eine Anleitung zu seiner Syntax, Anwendungen in Data Science und ML sowie eine vergleichende Analyse mit dem traditionellen switch-case.
Matt Crabtree's photo

Matt Crabtree

Tutorial

Python-Lambda-Funktionen: Ein Leitfaden für Anfänger

Lerne mehr über Python-Lambda-Funktionen, wozu sie gut sind und wann man sie benutzt. Enthält praktische Beispiele und bewährte Methoden für eine effektive Umsetzung.
Mark Pedigo's photo

Mark Pedigo

Mehr anzeigenMehr anzeigen