# Erster Flug: Trainiere dein erstes LoRA

Heute fliegt Ember. Du waehlst ein Basismodell, haengst LoRA-Adapter an und fuehrst ein echtes Fine-Tuning auf deiner eigenen Maschine durch — der Mac-Pfad mit MLX, der PC-Pfad mit Unsloth. Am Ende dieser Lektion hast du Adapter-Gewichte auf der Festplatte und ein Modell, das bereits anders klingt als die Basis.

## Was LoRA wirklich tut (60 Sekunden Theorie)

Alle Gewichte selbst eines 4B-Modells zu fine-tunen braucht ~70 GB. LoRAs Einsicht: Du musst die grossen Gewichtsmatrizen nicht *veraendern* — du kannst *eine kleine Korrektur daneben addieren*. Fuer jede Zielmatrix trainiert LoRA zwei duenne Matrizen (A und B), deren Produkt die Korrektur ist. Nur diese trainieren; die Basis bleibt eingefroren.

<svg viewBox="0 0 720 300" width="100%" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="LoRA: eingefrorene Basisgewichte plus kleine trainierbare Adapter-Matrizen">
    <rect x="60" y="60" width="180" height="180" rx="10" fill="rgba(123,140,255,0.10)" stroke="#7b8cff" stroke-width="1.5"/>
  <text x="150" y="145" text-anchor="middle" fill="#e6e9f0" font-family="-apple-system, sans-serif" font-size="14px">Basisgewichte W</text>
  <text x="150" y="168" text-anchor="middle" fill="#9aa3b5" font-family="-apple-system, sans-serif" font-size="12px">4.000.000.000 Parameter</text>
  <text x="150" y="52" text-anchor="middle" fill="#7b8cff" font-family="monospace" font-size="12px">EINGEFROREN ❄</text>
  <text x="290" y="158" text-anchor="middle" font-size="26" fill="#e6e9f0" font-family="-apple-system, sans-serif">+</text>
  <polygon points="340,60 400,60 340,240" fill="rgba(79,255,176,0.15)" stroke="#4fffb0" stroke-width="1.5"/>
  <text x="352" y="160" text-anchor="middle" fill="#4fffb0" font-family="monospace" font-size="12px">A</text>
  <polygon points="410,60 470,60 470,240 410,120" fill="rgba(79,255,176,0.15)" stroke="#4fffb0" stroke-width="1.5"/>
  <text x="452" y="160" text-anchor="middle" fill="#4fffb0" font-family="monospace" font-size="12px">B</text>
  <text x="405" y="52" text-anchor="middle" fill="#4fffb0" font-family="monospace" font-size="12px">TRAINIERBAR 🔥</text>
  <text x="405" y="266" text-anchor="middle" fill="#9aa3b5" font-family="-apple-system, sans-serif" font-size="12px">Adapter A×B — oft &lt;1% der Parameter</text>
  <text x="530" y="158" text-anchor="middle" font-size="26" fill="#e6e9f0" font-family="-apple-system, sans-serif">=</text>
  <rect x="560" y="60" width="140" height="180" rx="10" fill="rgba(79,255,176,0.08)" stroke="#4fffb0" stroke-width="1.5"/>
  <text x="630" y="140" text-anchor="middle" fill="#e6e9f0" font-family="-apple-system, sans-serif" font-size="14px">Ember</text>
  <text x="630" y="163" text-anchor="middle" fill="#9aa3b5" font-family="-apple-system, sans-serif" font-size="12px">Basis + dein</text>
  <text x="630" y="180" text-anchor="middle" fill="#9aa3b5" font-family="-apple-system, sans-serif" font-size="12px">Verhalten</text>
</svg>

<div class="concept-box">
<h4>Konzept</h4>
Der Regler namens <strong>Rank (r)</strong> ist die Dicke dieser Adapter-Matrizen. r=8 ist ein leichter Touch (Ton), r=16–32 ist Standard (unser Projekt), r=64+ fuer schwereres Aufgaben-Lernen. Hoeherer Rank = mehr Lernkapazitaet = mehr Speicher und mehr Risiko, deine Daten auswendig zu lernen statt aus ihnen zu lernen.
</div>

## Das Basismodell waehlen

Fuer Ember wollen wir ein kleines, modernes, Instruction-getuntes Modell mit guten Mehrsprachigkeits-Faehigkeiten. 2026 ist der Sweet Spot fuer dieses Projekt **Qwen3-4B-Instruct** (stark mehrsprachig, permissive Lizenz) — mit **Llama-3.2-3B-Instruct** und **Gemma-3-4B** als soliden Alternativen. Aus der Mathematik von Lektion 3: QLoRA auf 4B braucht ~4–6 GB. Beide Maschinen schaffen das locker — mit Absicht, denn dein erster Lauf sollte niemals zusaetzlich ein Speicherkampf sein.

**Starte immer von der *-Instruct*-Version**, nicht von der rohen Basis — sie weiss bereits, wie man eine Konversation fuehrt; du justierst Verhalten, statt Dialog von null beizubringen.

## Pfad A — Mac Studio (MLX)

Einmal installieren, dann trainieren:

```bash
pip install mlx-lm
mlx_lm.lora \
  --model Qwen/Qwen3-4B-Instruct \
  --train \
  --data ./data \
  --batch-size 4 \
  --iters 600 \
  --learning-rate 1e-5 \
  --adapter-path adapters/ember-v1
```

`--data ./data` zeigt auf den Ordner mit `train.jsonl` / `valid.jsonl` aus Lektion 4. MLX laedt das Modell beim ersten Lauf herunter, danach siehst du einen Schrittzaehler mit **Train Loss** und, periodisch, **Val Loss**. Auf dem M3 Ultra dauert dieser Lauf grob 20–40 Minuten.

Sprich mit deinem Drachen (Basis + Adapter, kein Mergen noetig):

```bash
mlx_lm.generate \
  --model Qwen/Qwen3-4B-Instruct \
  --adapter-path adapters/ember-v1 \
  --prompt "My order hasn't arrived and it's been 10 days."
```

## Pfad B — PC mit RTX 4090 (Unsloth)

Installieren (ein CUDA-faehiges PyTorch muss vorhanden sein), dann ist das das gesamte Trainingsskript:

```python
from unsloth import FastLanguageModel
from trl import SFTTrainer, SFTConfig
from datasets import load_dataset

model, tokenizer = FastLanguageModel.from_pretrained(
    "unsloth/Qwen3-4B-Instruct",
    max_seq_length=2048,
    load_in_4bit=True,          # QLoRA: 4-bit frozen base
)
model = FastLanguageModel.get_peft_model(model, r=16, lora_alpha=16)

dataset = load_dataset("json", data_files="data/train.jsonl", split="train")
dataset = dataset.map(lambda ex: {"text": tokenizer.apply_chat_template(
    ex["messages"], tokenize=False)})

trainer = SFTTrainer(
    model=model, tokenizer=tokenizer, train_dataset=dataset,
    args=SFTConfig(
        per_device_train_batch_size=4,
        gradient_accumulation_steps=2,
        num_train_epochs=3,
        learning_rate=2e-4,
        logging_steps=10,
        output_dir="adapters/ember-v1",
    ),
)
trainer.train()
model.save_pretrained("adapters/ember-v1")
```

Auf der 4090 ist das in grob 8–15 Minuten fertig. Unsloth gibt den Loss alle 10 Schritte aus — dieselben Zahlen, dieselbe Bedeutung.

<div class="pro-tip">
<h4>Profi-Tipp</h4>
Wenn du auf <strong>Out-of-Memory</strong> triffst, dreh diese drei Regler in dieser Reihenfolge: (1) reduziere <code>max_seq_length</code> (2048 → 1024), (2) reduziere die Batch Size und erhoehe zum Ausgleich die Gradient Accumulation, (3) aktiviere Gradient Checkpointing (<code>use_gradient_checkpointing="unsloth"</code> / MLX <code>--grad-checkpoint</code>). Dieselben Hebel auf beiden Maschinen, in derselben Reihenfolge.
</div>

## Den Loss lesen (deine ersten Fluginstrumente)

Loss misst, "wie ueberrascht das Modell von der richtigen Antwort ist" — niedriger ist besser. Ein gesunder erster Flug:

- Der Train Loss faellt anfangs schnell (etwa 2,1 → 1,3 in den ersten 100 Schritten) und flacht dann sanft ab.
- Der Val Loss folgt ihm nach unten, etwas hoeher.
- **Warnsignal:** Der Val Loss beginnt zu *steigen*, waehrend der Train Loss weiter faellt. Das ist Overfitting — der Drache lernt das Futter auswendig, statt jagen zu lernen. Lektion 6 kuemmert sich richtig darum.

<div class="honest-note">
<h4>Ehrlicher Hinweis</h4>
Dein v1 wird bei der <em>Form</em> der Antworten merklich besser sein und beim Urteilsvermoegen nur etwas besser. Das ist zu erwarten — du hast ~200 Beispiele fuer ein paar hundert Schritte trainiert. Widersteh dem Drang, es durch 10× laengeres Training zu fixen; v1s Job ist es, die Pipeline zu beweisen, und Lektion 6s Job ist es, sie gut zu machen.
</div>

<div class="try-it">
<h4>Probier es aus</h4>
Schick nach dem Training dieselben 5 Prompts durch das <strong>Basis</strong>-Modell und durch <strong>Basis + Adapter</strong>, Seite an Seite. Speichere beide Ausgaben in eine Datei. Den Stimmwechsel in einem Diff zu sehen ist der Moment, in dem das aufhoert, Theorie zu sein — und diese Datei wird dein erstes Evaluations-Artefakt fuer die naechste Lektion.
</div>

<div class="checkpoint">
<h4>Checkpoint</h4>
Du hast echte LoRA-Adapter auf deiner eigenen Hardware trainiert, du kannst mit Basis+Adapter chatten, und du kennst die drei Out-of-Memory-Hebel und das Overfitting-Warnsignal. Als Naechstes: deinen Drachen ehrlich benoten — und v2 besser machen als v1.
</div>
