# Dresaj avansat de dragoni: QLoRA, DPO si cuantizare

Ember zboara si asculta comenzi. Aceasta lectie este scoala avansata de zbor: antrenarea unor dragoni *mai mari* pe acelasi hardware (QLoRA), predarea *judecatii* in loc de imitatie (DPO) si micsorarea modelului antrenat pentru deploy rapid (cuantizare + GGUF). Aceste trei tehnici sunt cele care despart experimentele de weekend de modelele pe care le rulezi in fiecare zi.

## QLoRA: antrenare peste categoria ta de greutate

Ai folosit deja incarcarea pe 4-bit in lectia 5 pe calea PC — acela *era* QLoRA. Acum hai sa-l folosim deliberat, pentru scopul lui adevarat: **antrenarea modelelor care altfel nu incap.**

Ideea: comprimi ponderile de baza inghetate la precizie de 4 biti (format NF4), pastrezi adapterele LoRA pe 16-bit. Baza pierde o farama de calitate din compresie, dar pentru ca adapterele — partea care invata — raman la precizie completa, rezultatele finale ajung remarcabil de aproape de LoRA complet.

Din tabelul lectiei 3, QLoRA e ceea ce deblocheaza plafonul fiecarei masini:

- **RTX 4090 (24 GB):** 14B confortabil, 32B la limita (context scurt, batch 1, gradient checkpointing — asteapta-te la o lupta).
- **M3 Ultra (96 GB):** 32B cu spatiu de rezerva; 70B e posibil daca accepti antrenari peste noapte.

Cand ar trebui Ember sa avanseze de la 4B? Doar cand evaluarea o spune: daca v3+ continua sa esueze la *substanta* (rationament slab in raspunsuri complexe) in timp ce formatul si tonul sunt solide, acela e simptomul "model de baza prea mic" din lectia 6 — sari la Qwen3-8B sau 14B si ruleaza din nou acelasi pipeline. Nimic altceva nu se schimba; asta e frumusetea configurarii.

<div class="pro-tip">
<h4>Pro Tip</h4>
Scalarea de la 4B la 8B cam dubleaza timpul de antrenare si memoria — de obicei merita. 14B → 32B le dubleaza din nou pentru un salt de calitate mai mic pe sarcini simple. Lasa <em>scorurile de test</em> sa justifice fiecare salt, nu laudele cu marimea modelului.
</div>

## DPO: predarea gustului, nu doar a trucurilor

Fine-tuning-ul supervizat (tot ce am facut pana acum) ii arata modelului raspunsuri *bune*. **DPO — Direct Preference Optimization** — ii arata *perechi*: pentru acelasi prompt, un raspuns **ales** (chosen) si unul **respins** (rejected). Modelul invata *directia* preferintelor tale: ce face un raspuns mai bun decat altul.

```json
{"prompt": "Customer: You people are useless! Where is my package?!",
 "chosen": "I understand the frustration — let's fix this properly. [calm, structured answer with next step]",
 "rejected": "We apologize for any inconvenience caused. Your satisfaction is important to us. [corporate mush, no next step]"}
```

<div class="concept-box">
<h4>Concept</h4>
SFT invata "iata cum arata un raspuns bun". DPO invata "intre aceste doua raspunsuri plauzibile, prefera genul <em>acesta</em>". Straluceste exact acolo unde SFT atinge un platou: judecata subtila de stil, dezescaladarea, alegerea conciziei, evitarea limbajului de lemn corporatist — lucruri greu de demonstrat, dar usor de comparat.
</div>

Ordinea retetei conteaza: **intotdeauna SFT intai, apoi DPO deasupra** — tipic 100–500 de perechi de preferinte, cu un learning rate mai mic (~5e-6). Cele mai bune raspunsuri respinse nu sunt oameni de paie; sunt iesirile reale, plauzibile-dar-cu-defecte, ale *propriului tau model* din rundele de evaluare. Pe calea PC, asta e `DPOTrainer` din `trl` cu aceeasi configurare Unsloth; pe Mac, `mlx-lm` vine cu suport pentru preference training (verifica `mlx_lm.lora --help` pentru flag-urile curente — ecosistemul se misca repede).

<div class="honest-note">
<h4>Nota sincera</h4>
DPO e un condiment, nu o masa. Pe un model mic cu un SFT solid, 200 de perechi bune ascut vizibil judecata de ton. DPO nu poate salva un SFT prost, iar exagerarea (prea multe epoci, learning rate prea mare) produce un model care e ciudat cu incredere. Daca ai o singura seara, petrece-o pe date SFT mai bune.
</div>

## Cuantizarea: micsorarea dragonului pentru zborul zilnic

Precizia de antrenare si precizia de servire sunt jocuri diferite. Odata ce Ember e final, il vei vrea **mic si rapid** pentru uz zilnic. Aceea e cuantizarea la inferenta, iar formatul universal pentru ea este **GGUF** — formatul pe care il vorbesc Ollama, LM Studio si llama.cpp.

Pipeline-ul, pe oricare dintre masini:

<svg viewBox="0 0 720 150" width="100%" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Pipeline: adapterele se contopesc in baza, conversie la GGUF, cuantizare, servire">
    <defs>
    <marker id="arr2" markerWidth="8" markerHeight="8" refX="7" refY="4" orient="auto">
      <path d="M0,0 L8,4 L0,8 z" fill="#7b8cff"/>
    </marker>
  </defs>
  <rect x="20" y="45" width="150" height="60" rx="10" fill="rgba(123,140,255,0.08)" stroke="#7b8cff" stroke-width="1.5"/>
  <text x="95" y="70" text-anchor="middle" fill="#e6e9f0" font-family="-apple-system, sans-serif" font-size="13px">Merge adaptere</text>
  <text x="95" y="90" text-anchor="middle" fill="#9aa3b5" font-family="-apple-system, sans-serif" font-size="11px">baza + LoRA → un model</text>
  <path d="M170 75 L200 75" stroke="#7b8cff" stroke-width="1.5" fill="none" marker-end="url(#arr2)"/>
  <rect x="200" y="45" width="150" height="60" rx="10" fill="rgba(123,140,255,0.08)" stroke="#7b8cff" stroke-width="1.5"/>
  <text x="275" y="70" text-anchor="middle" fill="#e6e9f0" font-family="-apple-system, sans-serif" font-size="13px">Conversie la GGUF</text>
  <text x="275" y="90" text-anchor="middle" fill="#9aa3b5" font-family="-apple-system, sans-serif" font-size="11px">scriptul convert din llama.cpp</text>
  <path d="M350 75 L380 75" stroke="#7b8cff" stroke-width="1.5" fill="none" marker-end="url(#arr2)"/>
  <rect x="380" y="45" width="150" height="60" rx="10" fill="rgba(123,140,255,0.08)" stroke="#7b8cff" stroke-width="1.5"/>
  <text x="455" y="70" text-anchor="middle" fill="#e6e9f0" font-family="-apple-system, sans-serif" font-size="13px">Cuantizare Q4_K_M</text>
  <text x="455" y="90" text-anchor="middle" fill="#9aa3b5" font-family="-apple-system, sans-serif" font-size="11px">~8 GB → ~2.5 GB (4B)</text>
  <path d="M530 75 L560 75" stroke="#7b8cff" stroke-width="1.5" fill="none" marker-end="url(#arr2)"/>
  <rect x="560" y="45" width="140" height="60" rx="10" fill="rgba(79,255,176,0.08)" stroke="#4fffb0" stroke-width="1.5"/>
  <text x="630" y="70" text-anchor="middle" fill="#e6e9f0" font-family="-apple-system, sans-serif" font-size="13px">Servire</text>
  <text x="630" y="90" text-anchor="middle" fill="#9aa3b5" font-family="-apple-system, sans-serif" font-size="11px">Ollama · LM Studio</text>
</svg>

Primul pas e merge-ul — contopirea adapterelor in ponderile de baza, ca sa obtii un singur model de sine statator:

```bash
# Mac (MLX)
mlx_lm.fuse --model Qwen/Qwen3-4B-Instruct \
  --adapter-path adapters/ember-v3 --save-path ember-merged
```

```python
# PC (Unsloth) — can even save GGUF directly:
model.save_pretrained_merged("ember-merged", tokenizer)
model.save_pretrained_gguf("ember-gguf", tokenizer, quantization_method="q4_k_m")
```

**Q4_K_M** este punctul dulce implicit al comunitatii: ~4.5 biti per pondere, pierdere de calitate abia masurabila pe majoritatea sarcinilor, marime taiata de ~4× fata de 16-bit. Foloseste Q8_0 cand vrei aproape-fara-pierderi si ai memoria; coboara sub Q4 doar daca esti disperat dupa memorie.

<div class="try-it">
<h4>Incearca</h4>
Ia cel mai bun Ember al tau, fa merge, converteste, cuantizeaza la Q4_K_M — apoi ruleaza setul de test din lectia 6 <em>pe modelul cuantizat</em>. Compara scorurile cu versiunea necuantizata. Sa vezi (numeric!) ca Q4_K_M abia ti-a miscat metricile e felul in care castigi increderea de a face deploy mic.
</div>

<div class="checkpoint">
<h4>Checkpoint</h4>
Stii cand QLoRA deblocheaza baze mai mari, cand DPO adauga judecata peste SFT si cum merge → GGUF → Q4_K_M transforma un artefact de antrenare intr-un fisier de model gata de deploy. A mai ramas un singur lucru: eliberarea dragonului tau in lume.
</div>
