02 - Reglarea fină a transformatoarelor: LoRA, QLoRA și adaptoare
Dețin modele Transformer pre-antrenate precum Llama 3, Mistral, GPT-4 și Claude o cunoaștere impresionantă a limbajului și a raționamentului, dar rareori sunt pregătite pentru utilizare pentru o anumită sarcină. Să le adaptăm domeniului nostru, care este clasificarea de e-mailuri de afaceri, generarea de cod într-un cadru proprietar sau răspunsul la întrebări medical, ai nevoie de reglaj fin.
Problema este că aceste modele au miliarde de parametri: LLaMA 2 are 7 miliarde dintre ei în cea mai mică versiune, 70 de miliarde în cea mai mare. O reglare fină completă pe LLaMA-7B necesită aproximativ 28 GB de VRAM doar pentru greutățile din FP32, plus același lucru pentru stările de optimizare lui Adam (primul și al doilea moment), plus memorie pentru gradienți și activări. În în practică, aveți nevoie de 4 A100 de 80 GB pentru o singură rulare de reglare fină.
În acest al doilea articol al seriei Învățare profundă avansată și implementare Edge, vom explora tehnicile de Reglare fină eficientă în funcție de parametri (PEFT) că acestea vă permit să potriviți modele cu miliarde de parametri folosind o fracțiune din resurse. Vom pleca de la problema reglajului complet, apoi vom analiza în detaliu LoRA, QLoRA, DoRA și straturi adaptoare, cu implementări complete în Python folosind HuggingFace PEFT.
Prezentare generală a seriei
| # | Articol | Concentrează-te |
|---|---|---|
| 1 | Mecanism de atenție în transformatoare | Autoatenție, multi-capete, arhitectură completă |
| 2 | Sunteți aici - Reglare fină cu LoRA, QLoRA și adaptoare | Reglare fină eficientă din punct de vedere al parametrilor |
| 3 | Cuantificarea modelelor | INT8, INT4, GPTQ, AWQ |
| 4 | Tunderea și compresia | Reducerea parametrilor, distilare |
| 5 | Distilarea Cunoașterii | Profesor-elev, transfer de cunoștințe |
| 6 | Ollama și LLM Local | Inferență locală, optimizare |
| 7 | Transformator de vedere | ViT, DINO, clasificare imagini |
| 8 | Implementare Edge | ONNX, TensorRT, dispozitive mobile |
| 9 | NAS și AutoML | Căutarea arhitecturii neuronale |
| 10 | Benchmarking și optimizare | Profilare, metrici, reglare |
Ce vei învăța
- deoarece reglarea fină completă nu este durabilă pentru modelele cu miliarde de parametri
- Prezentare completă a tehnicilor PEFT: reglare prefix, reglare promptă, adaptoare, LoRA
- Matematica LoRA: descompunere de rang scăzut, scalare cu alfa, module țintă
- QLoRA: Cum să combinați cuantizarea pe 4 biți cu LoRA pentru reglarea fină a GPU-urilor de consum
- DoRA: Descompunerea greutății în mărime și direcție care îmbunătățește LoRA
- Straturi adaptoare: adaptoare gâtuite, adaptoare paralele, fuziune adaptor
- Implementare practică completă cu HuggingFace PEFT și SFTTrainer
- Pregătirea setului de date: format Alpaca, șablon de chat, format conversațional
- Reglare hiperparametrică: rang, alfa, rata de învățare, dimensiunea lotului
- LoRA vs QLoRA vs DoRA vs comparație completă de reglare fină cu benchmark-uri reale
- Cerințe hardware și limitări ale GPU-urilor de consum
1. Problema completă a reglajului fin
Il reglare fină completă este despre actualizare toată lumea parametrii a unui model pre-antrenat atunci când se antrenează pe un anumit set de date. Fiecare greutate, de la cele ale straturilor de încorporare în cele ale capetelor de atenție, se modifică prin retropropagare. Această abordare a funcționat excelent pentru modelele de dimensiuni mari moderat ca BERT (110 de milioane de parametri), dar devine nesustenabil atunci când scalați la miliarde a parametrilor.
1.1 Costuri de calcul
Pentru a înțelege de ce reglarea fină completă este prohibitivă, să ne uităm la cerințele de memorie pentru LLaMA 2 7B:
Amprenta memoriei de reglare fină completă (LLaMA 2 7B)
| Componentă | Formula | Memorie |
|---|---|---|
| Greutăți model (FP16) | 7B * 2 octeți | 14 GB |
| Gradienți (FP16) | 7B * 2 octeți | 14 GB |
| Stări ale optimizatorului (Adam FP32) | 7B * 4 octeți * 2 (m, v) | 56 GB |
| Activări (batch_size=1) | Variabilă | ~8-16 GB |
| Total | ~92-106 GB |
Niciun GPU de consum nu poate gestiona 100 GB de VRAM. Nici măcar A100 de 80 GB nu este suficient fără tehnici precum punctul de control în gradient și precizie mixtă. Pentru LLaMA 70B, vorbim este necesar mai mult de 1 TB de memorie.
1.2 Uitare catastrofală
A doua problemă a reglajului fin complet este uitare catastrofală (uitare catastrofală). Când actualizăm toți parametrii dintr-un anumit set de date, modelul tinde să uite de cunoștințele dobândite în timpul pre-antrenamentului. Un model reglat fin asupra documentelor legale poate pierde capacitatea de a genera cod Python sau pentru a răspunde la întrebările de istorie.
Acest lucru se întâmplă deoarece reglarea fină completă modifică fără discernământ toate greutățile, inclusiv cele care codifică cunoştinţele generale de limbă. În practică, modelul își suprascrie cunoștințele anterioare cu cele noi.
1.3 Depozitarea mai multor modele
Dacă o companie trebuie să adapteze același model de bază la 10 sarcini diferite (clasificarea e-mailurilor, analiza sentimentelor, întrebări și răspunsuri medicale, generare de rapoarte etc.), reglarea fină completă necesită salvarea a 10 copii complete ale modelului: pentru LLaMA 7B, adică 140 GB spațiu de stocare. Cu LoRA, după cum vom vedea, fiecare adaptare necesită doar 10-50 MB, pentru un total de 100-500 MB.
deoarece reglajul fin complet nu se scalează
| Problemă | Reglaj fin complet | PEFT (LoRA) |
|---|---|---|
| VRAM pentru LLaMA 7B | ~100 GB | ~16-24 GB |
| Parametri remorcabili | 7.000.000.000 | ~4.000.000 (0,06%) |
| Depozitare pentru adaptare | 14 GB | 10-50 MB |
| Uitare catastrofală | Risc ridicat | Risc minim |
| Hardware necesar | 4x A100 80GB | 1x RTX 3090 24GB |
2. Reglare fină eficientă în funcție de parametri (PEFT): Prezentare generală
Tehnicile PEFT împărtăşesc un principiu fundamental: în loc de actualizăm toți parametrii modelului, înghețăm greutățile pre-antrenate și antrenăm doar un mic subset de parametri suplimentari. Acest lucru reduce drastic cerințele de memorie, accelerează antrenamentul și păstrează cunoștințele despre modelul de bază.
Taxonomia tehnicilor PEFT
| Tehnică | Unde funcționează | Parametri suplimentari | Avantaje |
|---|---|---|---|
| Acordare promptă | Înglobări de intrare | Jetoane moi preponderate la intrare | Foarte simplu, puțini parametri |
| Ajustare prefix | Fiecare strat de atenție | Prefixe remorcabile K,V | Mai expresiv decât acordarea promptă |
| Straturi adaptoare | Între straturile transformatorului | Module gât de sticlă introduse | Flexibil, modular |
| LoRA | Matrici de greutate existente | Matrice de rang scăzut B și A | Zero cap la deducere |
| QLoRA | Ca LoRA + cuantizare | LoRA pe modelul pe 4 biți | Reglarea fină a GPU-urilor de consum |
| DoRA | Ca LoRA + descompunere | Magnitudine + direcție | calitate superioară LoRA |
2.1 Acordare promptă
Il acordare promptă (Lester et al., 2021) adaugă un set de jetoane moi remorcabil la începutul intrării. Aceste jetoane nu corespund cuvintelor reale din vocabular, dar sunt vectori continui pe care modelul ii invata in timpul antrenamentului. Ramane modelul de baza complet înghețat: sunt antrenate doar înglobările de jetoane moi.
Input originale: [Classify this email: "Meeting at 3pm"]
Con prompt tuning: [T1][T2][T3]...[T20] [Classify this email: "Meeting at 3pm"]
^^^^^^^^^^^^^^^^^
20 soft tokens trainabili
(20 * d_model parametri = 20 * 4096 = 81.920 parametri)
Il modello base (7B parametri) e CONGELATO.
Solo 81.920 parametri vengono aggiornati.
Reglajul prompt funcționează surprinzător de bine pentru modele foarte mari (>10B parametri), dar pierde din calitate cu modelele mai mici. Cu T5-XXL (11B), reglarea rapidă se realizează performanță aproape identică cu reglarea fină completă pe SuperGLUE.
2.2 Reglarea prefixului
Il acordarea prefixului (Li & Liang, 2021) extinde ideea de reglare promptă: în loc să adauge jetoane soft doar la intrare, adaugă prefixe antrenabile la cheile (K) și valorile (V) ale fiecare strat de atenție. Aceasta dă modelul capacitate mai expresivă decât simpla acordare promptă.
Prompt Tuning:
Layer 1 Attention: Q=[input], K=[input], V=[input]
Layer 2 Attention: Q=[input], K=[input], V=[input]
Solo soft tokens preposti all'input iniziale.
Prefix Tuning:
Layer 1 Attention: Q=[input], K=[prefix_1 | input], V=[prefix_1 | input]
Layer 2 Attention: Q=[input], K=[prefix_2 | input], V=[prefix_2 | input]
...
Layer L Attention: Q=[input], K=[prefix_L | input], V=[prefix_L | input]
Prefissi trainabili a OGNI layer di attention.
Parametri: L * prefix_len * 2 * d_model (2 per K e V)
Esempio: 32 layer * 20 prefix * 2 * 4096 = 5.242.880 parametri
3. LoRA: Low-Rank Adaptation
LoRA (Hu et al., 2021) și cea mai utilizată și reprezentativă tehnică PEFT un punct de cotitură în perfecţionarea modelelor lingvistice mari. Ideea cheie este elegant în simplitatea sa: în loc să actualizeze direct matricele de ponderi al Transformerului, descompunem actualizarea în două matrice de rang scăzut.
3.1 Intuiția: Ipoteza rangului intrinsec
Cercetările (Aghajanyan et al., 2020) au arătat că atunci când reglam fin pe o anumită sarcină, actualizările ponderilor modelului sunt concentrate în a subspațiu de dimensiuni reduse. Cu alte cuvinte, matricea de actualizarea greutății are o rang intrinsec mult mai jos decât al lui dimensiunea nominală.
Luați în considerare o matrice de greutate W de dimensiuni d x k (de exemplu, proiecția de interogări în atenția LLaMA 7B: 4096 x 4096). Actualizarea completă ar necesita o matrice de 4096 x 4096 = 16.777.216 parametri. Dar dacă actualizarea are un rang intrinsec scăzut, îl putem aproxima cu un rang mult mai mic r.
3.2 Matematica LoRA
Având în vedere o matrice de greutate pre-antrenată W_0 de dimensiune d x k, LoRA parametriză actualizare ca produs a două matrice de rang scăzut:
Formula fundamentală LoRA
W = W_0 + \Delta W = W_0 + B \cdot A
Unde:
- W_0 \in \mathbb{R}^{d \times k} și matricea de greutate pre-antrenată (înghețată).
- B \in \mathbb{R}^{d \times r} și matricea de proiecție în jos
- A \in \mathbb{R}^{r \times k} și matricea de proiecție în sus
- r \ll \min(d, k) iar cel rang (de obicei 4, 8, 16, 32, 64)
Cu scalare: h = W_0 x + \frac{\alpha}{r} \cdot B A x
Unde \alfa și un factor de scalare care controlează intensitatea adaptării.
Matrice originale W_0 (congelata):
k = 4096
+------------------+
| |
d = | W_0 | 16.777.216 parametri
4096| (CONGELATA) | Non viene aggiornata
| |
+------------------+
Aggiornamento LoRA (trainabile):
r = 16 k = 4096
+-----+ +------------------+
| | | |
d = | B | x r=16 | A |
4096| | | |
| | +------------------+
+-----+
65.536 65.536
B (d x r) A (r x k)
= 4096 * 16 = 16 * 4096
= 65.536 = 65.536
Totale LoRA: 131.072 parametri (0.78% di 16.777.216)
3.3 Inițializare
Inițializarea lui A și B este crucială. LoRA inițializează:
- A cu inițializare Kaiming uniformă (distribuție Gauss)
- B cu toate zerourile
Acest lucru asigură că la începutul instruirii contribuția LoRA este exact zero (B * A = 0), deci modelul pleacă de la comportamentul modelului pre-antrenat. Antrenamentul schimbă treptat B și A pentru a adapta modelul la sarcina specifică.
3.4 Factor de scalare Alpha
Parametrul \alfa (alfa) controlează intensitatea a adaptării LoRA. Ieșirea reală este:
h = W_0 x + \frac{\alpha}{r} \cdot B A x
Raportul \alpha / r funcționează ca o rată de învățare pentru LoRA. În practică, este adesea ținută \alpha = 2r (de exemplu \alpha = 32 cu r = 16), rezultând un factor de scalare de 2. Acest lucru vă permite să schimbați rangul fără a fi nevoie să reajustați rata de învățare.
3.5 Module țintă
LoRA nu se aplică tuturor straturilor transformatorului, ci doar matricelor specifice de greutăți. Alegerea modulelor țintă influențează semnificativ calitatea reglaj fin:
Module țintă pentru arhitecturi comune
| Modul | Tip | Dimensiuni (LLaMA 7B) | Impact |
|---|---|---|---|
q_proj |
Întrebare Atenție | 4096 x 4096 | High - controlează ceea ce „căută” modelul |
k_proj |
Cheie de atenție | 4096 x 4096 | High - controlează modul în care sunt indexate jetoanele |
v_proj |
Valoarea atenției | 4096 x 4096 | High - controlează informațiile extrase |
o_proj |
Atenție Ieșire | 4096 x 4096 | Mediu |
gate_proj |
Poarta FFN | 4096 x 11008 | High - controlează fluxul de informații în FFN |
up_proj |
FFN sus | 4096 x 11008 | Mediu-Ridicat |
down_proj |
FFN Jos | 11008 x 4096 | Mediu |
3.6 Calculul exact al parametrilor remorcabili
Calculăm parametrii antrenați pentru LLaMA 2 7B cu LoRA aplicat la q_proj și v_proj pe toate cele 32 de straturi:
LLaMA 2 7B: 32 layer, d_model = 4096
Con r = 16, target_modules = ["q_proj", "v_proj"]:
Per ogni layer:
q_proj LoRA: B (4096 x 16) + A (16 x 4096) = 65.536 + 65.536 = 131.072
v_proj LoRA: B (4096 x 16) + A (16 x 4096) = 65.536 + 65.536 = 131.072
Totale per layer: 262.144
Totale: 32 layer * 262.144 = 8.388.608 parametri (8.4M)
Rapporto: 8.4M / 6.738M (totale LLaMA 7B) = 0.12% dei parametri
Con target_modules = ["q_proj", "k_proj", "v_proj", "o_proj", "gate_proj", "up_proj", "down_proj"]:
Per ogni layer: 7 moduli * 131.072 (media) = ~917.504
Totale: 32 * 917.504 = ~29.4M parametri = 0.44%
3.7 Avantajul deducerii: Zero Overhead
Avantajul cheie al LoRA față de Straturile adaptoare este că nu există nicio inferență fără cheltuieli de calcul. Matricele LoRA pot fi comasate în greutățile modelului de bază:
W_{combinat} = W_0 + \frac{\alpha}{r} \cdot B \cdot A
După îmbinare, modelul are exact aceeași dimensiune și viteză de inferență a modelului original, dar cu ponderile actualizate. Acest lucru este imposibil cu straturi adaptoare, care adaugă parametri permanenți modelului.
4. QLoRA: LoRA cuantizat
QLoRA (Dettmers et al., 2023) și o extensie a LoRA care combină Cuantificare pe 4 biți a modelului de bază cu reglaj fin LoRA. Acest lucru vă permite să reglați fin modelele cu 65 de miliarde de parametri pe un singur GPU de 48 GB, un rezultat anterior imposibil.
4.1 Cele trei inovații ale QLoRA
QLoRA introduce trei tehnici cheie:
QLoRA Inovații
- NormalFloat pe 4 biți (NF4): Un nou tip de date cuantificate optimizate pentru greutăți distribuite normal. Fiecare greutate este mapată la una dintre cele 16 valori (4 biți) ales pentru a minimiza eroarea de cuantizare pe o distribuție gaussiană. NF4 depășește INT4 și FP4 deoarece ponderile rețelelor neuronale urmează aproximativ o distributie normala.
- Cuantizare dublă: Constantele de cuantizare (una pentru fiecare bloc de 64 de greutăți) sunt ele însele cuantificate la 8 biți. Acest lucru economisește bani 0,37 biți suplimentari pe parametru, aproximativ 3 GB pentru un model 65B.
- Optimizatori de pagină: Utilizați memoria paginată (memorie unificată) a GPU-ului NVIDIA pentru a gestiona vârfurile de memorie în timpul antrenamentului, mișcării stările de optimizare automată între GPU și CPU atunci când este necesar.
4.2 Cum funcționează NF4
Cuantizarea NF4 se bazează pe observația că greutățile rețelelor neuronale bine antrenate ele urmează o distribuție aproximativ normală cu o medie de zero. NF4 subîmparte distribuție normală standard în 16 intervale de probabilitate egală și atribuiți a fiecare interval valoarea optimă care minimizează eroarea așteptată:
INT4 (equispaziato):
Livelli: [-8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7]
Problema: molti livelli nelle code (pochi pesi li), pochi al centro (molti pesi li)
NF4 (quantili normali):
Livelli: [-1.0, -0.6962, -0.5251, -0.3949, -0.2844, -0.1848, -0.0911, 0.0,
0.0796, 0.1609, 0.2461, 0.3379, 0.4407, 0.5626, 0.7230, 1.0]
Vantaggio: livelli densi dove ci sono più pesi (vicino a 0)
Risultato: NF4 produce errore di quantizzazione ~2x inferiore a INT4
4.3 Amprenta de memorie QLoRA
Comparație memorie: Full FT vs LoRA vs QLoRA (LLaMA 7B)
| Componentă | FT complet (FP16) | LoRA (FP16) | QLoRA (NF4) |
|---|---|---|---|
| Greutăți model | 14 GB (FP16) | 14 GB (FP16) | 3,5 GB (NF4) |
| Gradiente | 14 GB | ~16 MB | ~16 MB |
| Stările de optimizare | 56 GB | ~64 MB | ~64 MB |
| Activari | ~12 GB | ~12 GB | ~6 GB |
| Total | ~96 GB | ~26 GB | ~10 GB |
Cu QLoRA, reglarea fină a LLaMA 7B devine posibilă pe un singur RTX 3090 (24 GB), și LLaMA 13B pe un singur RTX 4090 (24 GB) cu punct de control în gradient activat.
5. DoRA: Adaptare de rang scăzut în greutate descompusă
DoRA (Liu et al., 2024) și o evoluție a LoRA care descompune matricea de greutăți în două componente: magnitudinea (magnitudine) e direcţie (direcţie). Această descompunere este inspirată din clasic Weight Normalization de Salimans & Kingma (2016) și își propune să reducă decalajul de calitate între LoRA și reglarea fină completă.
5.1 Intuiția din spatele DoRA
Analiza tiparelor de învățare arată o diferență fundamentală între full reglaj fin și LoRA: reglajul fin complet schimbă atât magnitudinea, cât și direcția a ponderilor în mod independent, în timp ce LoRA tinde să le modifice proporțional, limitându-i expresivitatea.
5.2 Matematica DoRA
DoRA descompune matricea de greutate pre-antrenată în:
Formula DoRA
W' = m \cdot \frac{W_0 + BA}{\|W_0 + BA\|_c}
Unde:
- m \in \mathbb{R}^{1 \times k} și vectorul de magnitudine (intrenabil), inițializat cu normele coloanei de W_0
- W_0 + BA reprezintă direcția actualizată prin LoRA
- \|\cdot\|_c și norma coloanei, care normalizează fiecare coloană la o normă de unitate
- B e A sunt matricele standard LoRA (antrenabile)
În acest fel, DoRA are două grade independente de libertate:
- La direcţie și controlat de B și A (exact ca în LoRA)
- La magnitudinea și controlată de vectorul m, care se poate schimba indiferent de direcție
5.3 Avantajele DoRA față de LoRA
Benchmark DoRA vs LoRA (raționament de bun simț, LLaMA-7B)
| Sarcini | LoRA (r=32) | DoRA (r=32) | FT complet |
|---|---|---|---|
| BoolQ | 69,8 | 71,8 | 73.2 |
| PIQA | 82.1 | 83.2 | 83,9 |
| WinoGrande | 79,4 | 80,6 | 81,5 |
| HellaSwag | 83,6 | 84.4 | 85.1 |
| Medie | 78,7 | 80,0 | 80,9 |
DoRA reduce decalajul dintre LoRA și reglarea fină completă cu aproximativ 60-70%, cu o suprasolicitare a parametrilor minimi (doar vectorul m suplimentar).
6. Straturi adaptoare
The Straturi adaptoare (Houlsby et al., 2019) au fost printre primii tehnici PEFT propuse. Ideea este simplă: introduceți module mici remorcabile (adaptor) între straturile existente ale Transformatorului, păstrând greutăți originale.
6.1 Adaptoare pentru gâtul de sticlă
Un adaptor clasic și un modul blocaj cu trei componente:
- Proiecție în jos: reduce dimensiunea de la d_model la d_bottleneck (de ex. 4096 -> 64)
- Neliniaritate: de obicei ReLU sau GELU
- Proiecție în sus: restabilește dimensiunea originală (de exemplu, 64 -> 4096)
- Conexiune reziduala: ieșirea adaptorului este adăugată la intrare
Input x (dimensione d_model = 4096)
|
+----> Down-proj: W_down (4096 x 64) --> h (dimensione 64)
| |
| Non-linearita (GELU)
| |
| Up-proj: W_up (64 x 4096) <------+
| |
+------- (+) ----+ Residual Connection
|
Output (dimensione 4096)
Parametri per adapter: (4096 * 64) + (64 * 4096) = 524.288
Con 2 adapter per layer, 32 layer: 2 * 32 * 524.288 = 33.5M parametri
6.2 Comparație: Adaptoare vs LoRA
Adaptoare vs LoRA: diferențe cheie
| Caracteristică | Straturi adaptoare | LoRA |
|---|---|---|
| Asupra capului la inferență | Da (straturi suplimentare) | Nu (unește în ponderi) |
| Latență suplimentară | ~5-10% crestere | 0% |
| Composabilitate | Ridicat (Adaptor Fusion) | Medie (suma LoRA) |
| Ușurință de implementare | Ridicat | Ridicat |
| Suport bibliotecă | AdaptorHub, PEFT | PEFT, nelene, axolotl |
7. Implementare practică cu HuggingFace PEFT
Să trecem la practică. În această secțiune vom implementa reglarea fină a unui model Transformator folosind LoRA și QLoRA cu biblioteca HuggingFace PEFT. Vom folosi Mistral 7B ca model de bază și îl vom adapta pentru o sarcină de instrucție-răspuns.
7.1 Configurare și instalare
# Installazione delle librerie necessarie
# pip install torch transformers peft trl datasets accelerate bitsandbytes
import torch
from transformers import (
AutoModelForCausalLM,
AutoTokenizer,
BitsAndBytesConfig,
TrainingArguments,
)
from peft import (
LoraConfig,
get_peft_model,
prepare_model_for_kbit_training,
TaskType,
)
from trl import SFTTrainer, SFTConfig
from datasets import load_dataset
# Verifica GPU disponibile
print(f"GPU: {torch.cuda.get_device_name(0)}")
print(f"VRAM: {torch.cuda.get_device_properties(0).total_mem / 1e9:.1f} GB")
print(f"PyTorch: {torch.__version__}")
print(f"CUDA: {torch.version.cuda}")
7.2 Configurare LoRA
# Configurazione LoRA per Mistral 7B
lora_config = LoraConfig(
# Rango della decomposizione low-rank
# Valori tipici: 4 (minimo), 8 (buono), 16 (ottimo), 32 (alto), 64 (massimo)
r=16,
# Alpha: fattore di scaling. Il peso effettivo e alpha/r.
# Regola pratica: alpha = 2 * r
lora_alpha=32,
# Dropout applicato ai layer LoRA durante il training
# Aiuta a prevenire l'overfitting, specialmente con dataset piccoli
lora_dropout=0.05,
# Moduli del Transformer a cui applicare LoRA
# Per Mistral/LLaMA: q_proj, k_proj, v_proj, o_proj (attention)
# gate_proj, up_proj, down_proj (FFN)
target_modules=[
"q_proj", # Query projection - impatto alto
"k_proj", # Key projection - impatto alto
"v_proj", # Value projection - impatto alto
"o_proj", # Output projection - impatto medio
"gate_proj", # FFN gate - impatto alto
"up_proj", # FFN up projection - impatto medio-alto
"down_proj", # FFN down projection - impatto medio
],
# Tipo di task
task_type=TaskType.CAUSAL_LM,
# Bias: "none" (consigliato), "all", "lora_only"
bias="none",
)
# Stampa riepilogo configurazione
print(f"Rank: {lora_config.r}")
print(f"Alpha: {lora_config.lora_alpha}")
print(f"Scaling: {lora_config.lora_alpha / lora_config.r}")
print(f"Target modules: {lora_config.target_modules}")
print(f"Dropout: {lora_config.lora_dropout}")
7.3 Ajustare fină cu LoRA (FP16)
model_name = "mistralai/Mistral-7B-v0.3"
# Caricamento tokenizer
tokenizer = AutoTokenizer.from_pretrained(model_name)
tokenizer.pad_token = tokenizer.eos_token
tokenizer.padding_side = "right"
# Caricamento modello in FP16
model = AutoModelForCausalLM.from_pretrained(
model_name,
torch_dtype=torch.float16,
device_map="auto", # Distribuzione automatica su GPU disponibili
attn_implementation="flash_attention_2", # Flash Attention per efficienza
)
# Applica LoRA al modello
model = get_peft_model(model, lora_config)
# Mostra parametri trainabili
model.print_trainable_parameters()
# Output: trainable params: 27,262,976 || all params: 7,268,633,600 || trainable%: 0.375%
# Configurazione training
training_args = SFTConfig(
output_dir="./results/mistral-lora",
num_train_epochs=3,
per_device_train_batch_size=4,
gradient_accumulation_steps=4, # Effective batch size: 4 * 4 = 16
gradient_checkpointing=True, # Risparmia VRAM scambiando con tempo
optim="adamw_torch",
learning_rate=2e-4, # LR più alto rispetto al full FT
lr_scheduler_type="cosine",
warmup_ratio=0.03, # 3% degli step come warmup
weight_decay=0.001,
max_grad_norm=0.3,
logging_steps=10,
save_strategy="steps",
save_steps=100,
max_seq_length=2048,
fp16=True,
report_to="wandb", # Logging su Weights & Biases
seed=42,
)
7.4 Reglare fină cu QLoRA (4 biți)
# Configurazione quantizzazione 4-bit (QLoRA)
bnb_config = BitsAndBytesConfig(
load_in_4bit=True, # Carica pesi in 4-bit
bnb_4bit_quant_type="nf4", # NormalFloat 4-bit (migliore di INT4)
bnb_4bit_compute_dtype=torch.bfloat16, # Compute in BF16 (più stabile di FP16)
bnb_4bit_use_double_quant=True, # Double quantization (risparmia ~0.4 bit/param)
)
# Caricamento modello quantizzato
model = AutoModelForCausalLM.from_pretrained(
model_name,
quantization_config=bnb_config,
device_map="auto",
attn_implementation="flash_attention_2",
)
# Prepara il modello per il training k-bit
# Questo congela i pesi quantizzati e prepara i layer per LoRA
model = prepare_model_for_kbit_training(
model,
use_gradient_checkpointing=True,
)
# Applica LoRA (stessa configurazione di prima)
model = get_peft_model(model, lora_config)
model.print_trainable_parameters()
# Output: trainable params: 27,262,976 || all params: 3,778,682,880 || trainable%: 0.721%
# Nota: il modello base ora occupa ~3.8GB invece di ~14GB
# Configurazione training QLoRA
training_args = SFTConfig(
output_dir="./results/mistral-qlora",
num_train_epochs=3,
per_device_train_batch_size=4,
gradient_accumulation_steps=4,
gradient_checkpointing=True,
optim="paged_adamw_8bit", # Paged optimizer per gestire picchi di memoria
learning_rate=2e-4,
lr_scheduler_type="cosine",
warmup_ratio=0.03,
weight_decay=0.001,
max_grad_norm=0.3,
logging_steps=10,
save_strategy="steps",
save_steps=100,
max_seq_length=2048,
bf16=True, # BF16 per il compute (più stabile)
report_to="wandb",
seed=42,
)
8. Pregătirea setului de date
Calitatea reglajului fin depinde enorm de calitatea setului de date. In aceasta secțiunea să vedem cum să pregătim datele în formatele standard utilizate pentru reglare fină de LLM.
8.1 Format Alpaca
Formatul Alpaca (Stanford, 2023) este unul dintre cele mai populare pentru reglarea fină a LLM pe sarcini de instrucție-răspuns. Fiecare exemplu are trei câmpuri:
def format_alpaca(example):
"""Converte un esempio nel formato Alpaca per il fine-tuning."""
if example.get("input", ""):
# Con input aggiuntivo
text = (
f"### Instruction:\n{example['instruction']}\n\n"
f"### Input:\n{example['input']}\n\n"
f"### Response:\n{example['output']}"
)
else:
# Solo istruzione e risposta
text = (
f"### Instruction:\n{example['instruction']}\n\n"
f"### Response:\n{example['output']}"
)
return {"text": text}
# Caricamento dataset
dataset = load_dataset("tatsu-lab/alpaca", split="train")
dataset = dataset.map(format_alpaca)
# Esempio di output formattato
print(dataset[0]["text"])
# ### Instruction:
# Give three tips for staying healthy.
#
# ### Response:
# 1. Eat a balanced and nutritious diet...
# 2. Exercise regularly...
# 3. Get enough sleep...
8.2 Șabloane de chat (Mistral/ChatML)
Pentru modelele conversaționale, formatul șablonului de chat este mai potrivit. Fiecare model are propriul șablon specific:
def format_chat_template(example, tokenizer):
"""Formatta l'esempio usando il chat template del modello."""
messages = [
{"role": "system", "content": "Sei un assistente esperto e disponibile."},
{"role": "user", "content": example["instruction"]},
{"role": "assistant", "content": example["output"]},
]
# Applica il chat template del tokenizer
text = tokenizer.apply_chat_template(
messages,
tokenize=False,
add_generation_prompt=False,
)
return {"text": text}
# Formato risultante per Mistral:
# <s>[INST] Sei un assistente esperto. [/INST]
# [INST] Give three tips for staying healthy. [/INST]
# 1. Eat a balanced diet...</s>
# Per ChatML (usato da molti modelli):
# <|im_start|>system
# Sei un assistente esperto.<|im_end|>
# <|im_start|>user
# Give three tips for staying healthy.<|im_end|>
# <|im_start|>assistant
# 1. Eat a balanced diet...<|im_end|>
8.3 Antrenament cu SFTTrainer
from trl import SFTTrainer
# Dataset formattato
dataset = load_dataset("tatsu-lab/alpaca", split="train")
dataset = dataset.map(
lambda x: format_chat_template(x, tokenizer)
)
# Split train/eval
dataset = dataset.train_test_split(test_size=0.05, seed=42)
# Creazione trainer
trainer = SFTTrainer(
model=model,
args=training_args,
train_dataset=dataset["train"],
eval_dataset=dataset["test"],
processing_class=tokenizer,
)
# Avvio training
trainer.train()
# Salvataggio adattatore LoRA (solo ~50MB)
trainer.save_model("./results/mistral-qlora/final")
tokenizer.save_pretrained("./results/mistral-qlora/final")
9. Îmbinarea și implementarea ponderii
După antrenament, avem un model de bază înghețat și un adaptor mic LoRA. Pentru implementare, le putem păstra pe cele două separate (utile pentru comutarea între adaptări diferite) sau le îmbină într-un singur model.
9.1 Îmbinați ponderile LoRA în modelul de bază
from peft import PeftModel
from transformers import AutoModelForCausalLM, AutoTokenizer
# Caricamento modello base (FP16 per il merge)
base_model = AutoModelForCausalLM.from_pretrained(
"mistralai/Mistral-7B-v0.3",
torch_dtype=torch.float16,
device_map="auto",
)
# Caricamento adattatore LoRA
model = PeftModel.from_pretrained(
base_model,
"./results/mistral-qlora/final",
)
# Merge LoRA nei pesi base
# Dopo il merge: W_merged = W_0 + (alpha/r) * B * A
model = model.merge_and_unload()
# Salvataggio modello merged
model.save_pretrained("./models/mistral-7b-finetuned")
tokenizer.save_pretrained("./models/mistral-7b-finetuned")
# Upload su HuggingFace Hub
model.push_to_hub("username/mistral-7b-finetuned")
tokenizer.push_to_hub("username/mistral-7b-finetuned")
print("Modello merged e caricato su HuggingFace Hub!")
9.2 Inferență cu modelul reglat fin
from transformers import pipeline
# Pipeline di generazione
pipe = pipeline(
"text-generation",
model="./models/mistral-7b-finetuned",
torch_dtype=torch.float16,
device_map="auto",
)
# Generazione
messages = [
{"role": "system", "content": "Sei un assistente esperto di programmazione."},
{"role": "user", "content": "Spiega il pattern Repository in Python."},
]
output = pipe(
messages,
max_new_tokens=512,
temperature=0.7,
top_p=0.9,
do_sample=True,
)
print(output[0]["generated_text"][-1]["content"])
10. Reglaj hiperparametru
Alegerea hiperparametrilor este crucială pentru a obține rezultate bune. Iată un ghid practică bazată pe experiența comunității și pe repere publicate.
Ghid de hiperparametri LoRA
| Hiperparametru | Gamă | Sfatuit | Note |
|---|---|---|---|
| rang (r) | 4-256 | 16-64 | Creștere pentru sarcini complexe; r=8 adesea suficient pentru clasificare |
| alfa | 8-128 | 2 * rang | alfa/r și scalare eficientă; menține raportul constant la schimbarea r |
| rata_de_învățare | 1e-5 - 5e-4 | 2e-4 | Mai mare decât FT complet (10-100x); reduce dacă pierderea fluctuează |
| batch_size | 1-32 | 4-8 | Utilizați gradient_accumulation pentru a simula loturi mai mari |
| epoci | 1-5 | 2-3 | Atenție la supraajustare; monitorizarea pierderii evalului |
| raportul_încălzire | 0,01-0,1 | 0,03 | Important pentru stabilitate; mai mare cu LR ridicat |
| renunța | 0,0-0,1 | 0,05 | 0,0 pentru seturi de date mari, 0,1 pentru seturi de date mici |
| max_seq_length | 512-8192 | 2048 | Mai mare = mai multă VRAM; adapta la setul de date |
Greșeli frecvente la reglajul fin
- Rata de învățare prea mare: pierderea fluctuează sau diverge. Soluție: Reduceți LR de 2-5 ori sau creșteți încălzirea
- Clasament prea jos: modelul nu învață suficient. Rezolvare: Creșteți r de la 8 la 16 sau 32
- Clasament prea mare: supraadaptare, în special cu seturi de date mici. Soluție: Reduceți r sau creșteți abandonul
- Câteva epoci: submontare. Verificați dacă pierderea eval continuă să scadă
- Prea multe ere: pierderea antrenamentului scade, dar pierderea evalului crește (suprafitting)
- Set de date necurățat: duplicatele, erorile, formatarea inconsecventă degradează calitatea
- Uitați de punctele de control în gradient: OOM instantaneu pe modele mari
11. Criterii de referință și comparații
Cum să alegi între LoRA, QLoRA, DoRA și reglarea fină completă? Iată o comparație sistematică bazate pe benchmark-uri publice și pe testarea comunității.
Comparație completă: LoRA vs QLoRA vs DoRA vs Full FT
| Metric | FT complet | LoRA | QLoRA | DoRA |
|---|---|---|---|---|
| calitate (MT-Bench) | 7.8 | 7.5 | 7.3 | 7.6 |
| VRAM (model 7B) | ~100 GB | ~26 GB | ~10 GB | ~26 GB |
| Viteza de antrenament (rel.) | 1,0x | 1,2x | 0,8x | 1,1x |
| Parametri remorcabili | 100% | 0,1-0,5% | 0,1-0,5% | 0,1-0,5% + m |
| Adaptarea depozitării | 14 GB | 10-50 MB | 10-50 MB | 10-50 MB |
| Inferență de sus | 0% | 0% (combinat) | 0% (combinat) | 0% (combinat) |
| GPU minim (7B) | 4x A100 | 1x A100 40GB | 1x RTX 3090 | 1x A100 40GB |
12. Cerințe hardware
Alegerea hardware-ului depinde de modelul pe care doriți să îl reglați fin și de tehnica PEFT pe care îl folosești. Iată un ghid practic pentru GPU-uri pentru consumatori și profesionale.
Cerințe GPU pentru reglaj fin
| GPU | VRAM | LoRA (FP16) | QLoRA (4 biți) | Note |
|---|---|---|---|---|
| RTX 3060 | 12 GB | până la 3B | până la 7B (seq 512) | Nivel de intrare, limitat |
| RTX 3090 | 24 GB | până la 7B | până la 13B | Excelent pentru QLoRA 7B |
| RTX 4090 | 24 GB | până la 7B | până la 13B | Mai rapid decât 3090, aceeași VRAM |
| A100 40GB | 40 GB | până la 13B | până la 34B | Standard profesional |
| A100 80GB | 80 GB | până la 30B | până la 70B | Ideal pentru modele mari |
| H100 80GB | 80 GB | până la 30B | până la 70B | Mai rapid decât A100, suport FP8 |
Sfaturi pentru consumatorii GPU
- Buget limitat (RTX 3060 12GB): QLoRA pe modelele 7B cu seq_length=512, batch_size=1, gradient_accumulation=16
- Raport calitate/pret bun (RTX 3090/4090 24GB): QLoRA pe modelele 7-13B cu seq_length=2048, batch_size=4
- Cloud computing: Google Colab Pro (10 USD/lună) oferă A100 40GB pentru sesiuni limitate; RunPod și Lambda Labs pentru utilizare intensă
- Activați întotdeauna: gradient_checkpointing=Adevărat, optimizatori paginați, Atenție Flash 2
13. Cazuri practice de utilizare
Să vedem câteva cazuri de utilizare concrete în care reglarea fină cu LoRA este deosebit de eficientă.
13.1 Clasificarea textului
Pentru sarcini de clasificare (analiza sentimentelor, clasificarea subiectelor, detectarea spamului), LoRA este adesea de preferat deoarece:
- Sarcina necesită câțiva parametri suplimentari (rangul r=4-8 este suficient)
- Seturile de date de clasificare sunt de obicei mici (1k-50k exemple)
- Riscul de supraadaptare cu rang înalt și înalt
13.2 Generarea codului
Pentru reglarea fină a generării de cod (de exemplu, adaptarea unui model la un anumit limbaj sau acorduri de companie), vă recomandăm:
- Rang mai mare (r=32-64) deoarece codul are mai multă structură și variabilitate
- Module țintă complete (toate cele 7 module Transformer)
- Seturi de date de înaltă calitate: cod bine documentat, cu teste, stil consistent
- Secvențe lungi (max_seq_length=4096-8192) pentru context complet
13.3 Chatbot-uri specifice domeniului
Pentru a crea un chatbot specializat (asistență medicală, juridică, tehnologică):
- Set de date conversaționale în formatul șablonului de chat al modelului
- Includeți exemple de refuz („Nu pot răspunde la această întrebare”)
- Clasament mediu (r=16-32)
- Validare cu experți în domeniu, nu doar cu valori automate
13.4 Rezumat
Pentru reglarea fină a sarcinilor rezumate:
- Set de date cu perechi de înaltă calitate (document, rezumat).
- Secvențe lungi pentru introducere (până la 8192 de jetoane)
- Clasament mediu-înalt (r=16-32)
- Evaluare cu metrici precum ROUGE și evaluare umană
14. Concluzii și arbore decizional
Tehnicile PEFT au făcut accesibilă reglarea fină a modelelor de limbaj mari oricui are un GPU de consum. LoRA, QLoRA și DoRA reprezintă stadiul tehnicii pentru o reglare fină eficientă din punct de vedere al parametrilor, fiecare cu punctele sale forte.
Arborele de decizie: Când să folosiți Ce
| Situaţie | Tehnica recomandată | Motivația |
|---|---|---|
| GPU cu < 16 GB VRAM | QLoRA | Doar opțiune pentru modelele 7B+ pe hardware de consum |
| GPU cu 24-40 GB VRAM | LoRA (FP16) | Calitate mai bună decât QLoRA, antrenament mai rapid |
| Cea mai înaltă calitate necesară | DoRA | Mai aproape de FT complet, overhead minim pe LoRA |
| Adaptări multiple ale aceluiași model | LoRA | Adaptoare mici (~50MB), comutare rapidă între sarcini |
| Model mic (< 1B parametri) | Reglaj fin complet | Pentru modelele mici, FT complet este adesea fezabil și mai bun |
| Sarcină simplă (clasificare) | LoRA (r=4-8) | rang scăzut și suficient; evitați supraadaptarea |
| Sarcină complexă (generare de cod) | LoRA/DoRA (r=32-64) | Rang ridicat pentru a surprinde complexitatea sarcinii |
| Compoziția competențelor | Adaptor Fusion | Combinați mai multe adaptări într-un mod structurat |
Domeniul reglajului eficient și care evoluează rapid. Tehnici noi precum Belşug (Proiecție cu gradient de rang scăzut) e ReLoRA (antrenament LoRA iterativ cu rang în creștere) promit să se reducă în continuare decalajul cu reglaj fin complet. Cu toate acestea, LoRA și QLoRA rămân standardul astăzi de facto pentru reglarea fină LLM, cu un ecosistem matur de biblioteci (HuggingFace PEFT, Unsloth, Axolotl) și o comunitate activă.
În următorul articol din serie vom explora Cuantificarea modelelor: GPTQ, AWQ, INT8 și cum să reduceți dimensiunea modelului cu 75%, păstrând în același timp calitatea pentru implementarea producției.
Resurse și referințe
- Documente LoRA: „LoRA: Adaptarea de rang scăzut a modelelor de limbaj mari” (Hu et al., 2021)
- Lucrări QLoRA: „QLoRA: Ajustarea eficientă a LLM-urilor cuantizate” (Dettmers et al., 2023)
- Hârtie DoRA: „DoRA: Adaptare de rang scăzut cu greutate descompusă” (Liu et al., 2024)
- Adaptoare de hârtie: „Învățare prin transfer eficient din punct de vedere al parametrilor” (Houlsby și colab., 2019)
- HuggingFace PEFT: https://github.com/huggingface/peft
- Nelene: https://github.com/unslothai/unsloth (LoRA de 2-5 ori mai rapid)
- TRL (Învățare de consolidare a transformatorului): https://github.com/huggingface/trl







