# Primul zbor: antreneaza-ti primul LoRA

Astazi Ember zboara. Vei alege un model de baza, vei atasa adaptere LoRA si vei rula un fine-tune adevarat pe propria masina — calea Mac cu MLX, calea PC cu Unsloth. La finalul acestei lectii vei avea ponderi de adapter pe disc si un model care deja suna diferit fata de baza.

## Ce face LoRA de fapt (60 de secunde de teorie)

Fine-tuning-ul tuturor ponderilor chiar si al unui model 4B are nevoie de ~70 GB. Ideea LoRA: nu trebuie sa *schimbi* matricile mari de ponderi — poti *adauga o mica corectie* langa ele. Pentru fiecare matrice vizata, LoRA antreneaza doua matrici subtiri (A si B) al caror produs este corectia. Doar acelea se antreneaza; baza ramane inghetata.

<svg viewBox="0 0 720 300" width="100%" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="LoRA: ponderi de baza inghetate plus matrici adapter mici antrenabile">
    <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">Ponderi de baza 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 parametri</text>
  <text x="150" y="52" text-anchor="middle" fill="#7b8cff" font-family="monospace" font-size="12px">INGHETAT ❄</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">ANTRENABIL 🔥</text>
  <text x="405" y="266" text-anchor="middle" fill="#9aa3b5" font-family="-apple-system, sans-serif" font-size="12px">adapterele A×B — adesea &lt;1% din parametri</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">baza + comportamentul</text>
  <text x="630" y="180" text-anchor="middle" fill="#9aa3b5" font-family="-apple-system, sans-serif" font-size="12px">tau</text>
</svg>

<div class="concept-box">
<h4>Concept</h4>
Butonul numit <strong>rank (r)</strong> este grosimea acelor matrici adapter. r=8 e o atingere usoara (ton), r=16–32 e standard (proiectul nostru), r=64+ pentru invatare mai grea de sarcini. Rank mai mare = mai multa capacitate de invatare = mai multa memorie si mai mult risc sa memoreze datele tale in loc sa invete din ele.
</div>

## Alegerea modelului de baza

Pentru Ember vrem un model mic, modern, instruction-tuned, cu abilitati multilingve bune. In 2026, punctul dulce pentru acest proiect este **Qwen3-4B-Instruct** (multilingv excelent, licenta permisiva) — cu **Llama-3.2-3B-Instruct** si **Gemma-3-4B** ca alternative solide. Din matematica lectiei 3: QLoRA pe 4B are nevoie de ~4–6 GB. Ambele masini incap lejer — in mod deliberat, pentru ca primul tau run nu ar trebui sa fie in acelasi timp si o lupta cu memoria.

**Porneste intotdeauna de la versiunea *-Instruct***, nu de la baza bruta — ea stie deja sa poarte o conversatie; tu ajustezi comportamentul, nu predai dialogul de la zero.

## Calea A — Mac Studio (MLX)

Instalezi o data, apoi antrenezi:

```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` arata catre folderul cu `train.jsonl` / `valid.jsonl` din lectia 4. MLX descarca modelul la primul run, apoi vei vedea un contor de pasi cu **train loss** si, periodic, **val loss**. Pe M3 Ultra, acest run dureaza aproximativ 20–40 de minute.

Vorbeste cu dragonul tau (baza + adaptere, fara merge necesar):

```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."
```

## Calea B — PC cu RTX 4090 (Unsloth)

Instalezi (trebuie sa existe un PyTorch cu CUDA), apoi acesta e intregul script de antrenare:

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

Pe 4090 asta se termina in aproximativ 8–15 minute. Unsloth afiseaza loss-ul la fiecare 10 pasi — aceleasi numere de urmarit, acelasi inteles.

<div class="pro-tip">
<h4>Pro Tip</h4>
Daca dai de <strong>out-of-memory</strong>, roteste aceste trei butoane in ordine: (1) redu <code>max_seq_length</code> (2048 → 1024), (2) redu batch size si creste gradient accumulation ca sa compensezi, (3) activeaza gradient checkpointing (<code>use_gradient_checkpointing="unsloth"</code> / MLX <code>--grad-checkpoint</code>). Aceleasi parghii pe ambele masini, in aceeasi ordine.
</div>

## Citirea loss-ului (primele tale instrumente de zbor)

Loss-ul masoara "cat de surprins este modelul de raspunsul corect" — mai mic e mai bine. Un prim zbor sanatos:

- Train loss scade rapid la inceput (sa zicem 2.1 → 1.3 in primii 100 de pasi), apoi se aplatizeaza usor.
- Val loss il urmeaza in jos, putin mai sus.
- **Semnal de alarma:** val loss incepe sa *urce* in timp ce train loss continua sa scada. Acela e overfitting — dragonul memoreaza mancarea in loc sa invete sa vaneze. Lectia 6 se ocupa de el cum trebuie.

<div class="honest-note">
<h4>Nota sincera</h4>
Versiunea ta v1 va fi vizibil mai buna la <em>forma</em> raspunsurilor si doar putin mai buna la judecata. E de asteptat — ai antrenat ~200 de exemple pentru cateva sute de pasi. Rezista tentatiei de a repara antrenand de 10× mai mult; treaba lui v1 e sa dovedeasca pipeline-ul, iar treaba lectiei 6 e sa-l faca bun.
</div>

<div class="try-it">
<h4>Incearca</h4>
Dupa antrenare, ruleaza aceleasi 5 prompturi prin modelul de <strong>baza</strong> si prin <strong>baza + adaptere</strong>, unul langa altul. Salveaza ambele iesiri intr-un fisier. Sa vezi vocea schimbandu-se intr-un diff e momentul in care asta inceteaza sa fie teorie — iar acel fisier devine primul tau artefact de evaluare pentru lectia urmatoare.
</div>

<div class="checkpoint">
<h4>Checkpoint</h4>
Ai antrenat adaptere LoRA reale pe propriul hardware, poti conversa cu baza+adaptere si cunosti cele trei parghii pentru out-of-memory si semnalul de alarma al overfitting-ului. Urmeaza: notarea sincera a dragonului tau — si cum faci v2 mai bun decat v1.
</div>
