Lernpfad
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.

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).

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.

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")

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 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:
forward_backward: Berechne die Gradienten aus deinen Trainingsdaten.optim_step: Aktualisiere die Modellparameter mit dem Adam-Optimierer.save_weights_and_get_sampling_client: Speicher dein optimiertes Modellsample: 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.

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.