02 - Transformatory dostrajające: LoRA, QLoRA i adaptery
Posiadają wstępnie przeszkolone modele Transformerów, takie jak Llama 3, Mistral, GPT-4 i Claude imponującą znajomość języka i rozumowania, ale rzadko są gotowi do wykorzystania do konkretnego zadania. Aby dostosować je do naszej domeny, jaką jest klasyfikacja e-maili biznesowych, generowanie kodu w autorskim frameworku czy odpowiadanie na pytania medyczne, potrzebujesz dostrajanie.
Problem w tym, że te modele mają miliardy parametrów: LLaMA 2 ma ich 7 miliardów w najmniejszej wersji, 70 miliardów w największej. Pełne dostrojenie LLaMA-7B wymaga około 28 GB VRAM na same wagi w FP32, plus tyle samo na stany optymalizatora Adama (pierwsza i druga chwila) oraz pamięć dla gradientów i aktywacji. w w praktyce do pojedynczego dostrojenia potrzebne są 4 karty A100 o pojemności 80 GB.
W drugim artykule z tej serii Zaawansowane głębokie uczenie się i wdrażanie brzegowe, będziemy badać techniki Dostrajanie efektywne pod względem parametrów (PEFT) to pozwalają dopasować modele z miliardami parametrów przy użyciu ułamka zasobów. Zaczniemy od problemu pełnego dostrojenia, następnie szczegółowo przeanalizujemy LoRA, QLoRA, DoRA i warstwy adapterów, z pełnymi implementacjami w języku Python przy użyciu HuggingFace PEFT.
Przegląd serii
| # | Przedmiot | Centrum |
|---|---|---|
| 1 | Mechanizm uwagi w transformatorach | Samouważna, wielogłowicowa, kompletna architektura |
| 2 | Jesteś tutaj - Dostrajanie za pomocą LoRA, QLoRA i adapterów | Efektywne dostrajanie parametrów |
| 3 | Kwantyzacja modeli | INT8, INT4, GPTQ, AWQ |
| 4 | Przycinanie i kompresja | Redukcja parametrów, destylacja |
| 5 | Destylacja wiedzy | Nauczyciel-uczeń, transfer wiedzy |
| 6 | Ollama i lokalny LLM | Wnioskowanie lokalne, optymalizacja |
| 7 | Transformator wizji | ViT, DINO, klasyfikacja obrazu |
| 8 | Wdrożenie brzegowe | ONNX, TensorRT, urządzenia mobilne |
| 9 | NAS i AutoML | Wyszukiwanie architektury neuronowej |
| 10 | Benchmarking i optymalizacja | Profilowanie, metryki, tuning |
Czego się nauczysz
- ponieważ pełne dostrojenie nie jest możliwe w przypadku modeli z miliardami parametrów
- Pełny przegląd technik PEFT: strojenie prefiksów, strojenie natychmiastowe, adaptery, LoRA
- Matematyka LoRA: dekompozycja niskiej rangi, skalowanie za pomocą alfa, moduły docelowe
- QLoRA: Jak połączyć 4-bitową kwantyzację z LoRA w celu dostrojenia konsumenckich procesorów graficznych
- DoRA: Rozkład ciężaru na wielkość i kierunek, który poprawia LoRA
- Warstwy adapterów: adaptery wąskiego gardła, adaptery równoległe, fuzja adapterów
- Kompletne praktyczne wdrożenie z HuggingFace PEFT i SFTTrainer
- Przygotowanie zbioru danych: format Alpaca, szablon czatu, format konwersacyjny
- Strojenie hiperparametrów: ranga, alfa, szybkość uczenia się, wielkość partii
- LoRA vs QLoRA vs DoRA vs porównanie pełnego dostrajania z prawdziwymi testami porównawczymi
- Wymagania sprzętowe i ograniczenia konsumenckich procesorów graficznych
1. Problem pełnego dostrojenia
Il pełne dostrojenie chodzi o aktualizację wszyscy parametry wstępnie wytrenowanego modelu podczas uczenia na określonym zestawie danych. Każda waga od warstwy osadzające do warstw głów uwagi są modyfikowane poprzez propagacja wsteczna. To podejście sprawdziło się świetnie w przypadku modeli o dużych rozmiarach umiarkowany jak BERT (parametry 110M), ale staje się nie do utrzymania, gdy skaluje się do miliardów parametrów.
1.1 Koszty obliczeniowe
Aby zrozumieć, dlaczego pełne dostrajanie jest zbyt trudne, przyjrzyjmy się wymaganiom dotyczącym pamięci dla LLaMA 2 7B:
Pełne dostrajanie obszaru pamięci (LLaMA 2 7B)
| Część | Formuła | Pamięć |
|---|---|---|
| Masy modeli (FP16) | 7B * 2 bajty | 14 GB |
| Gradienty (FP16) | 7B * 2 bajty | 14 GB |
| Stany optymalizatora (Adam FP32) | 7B * 4 bajty * 2 (m, v) | 56 GB |
| Aktywacje (batch_size=1) | Zmienny | ~8-16 GB |
| Całkowity | ~92-106 GB |
Żaden konsumencki procesor graficzny nie jest w stanie obsłużyć 100 GB pamięci VRAM. Nawet 80 GB A100 to za mało bez technik takich jak punkt kontrolny gradientu i precyzja mieszana. W przypadku LLaMA 70B mówimy wymagane więcej niż 1 TB pamięci.
1.2 Katastrofalne zapomnienie
Drugim problemem pełnego dostrojenia jest katastrofalne zapomnienie (katastrofalne zapomnienie). Kiedy aktualizujemy wszystkie parametry w konkretnym zbiorze danych, model ma tendencję do zapominania wiedzy zdobytej podczas szkolenia wstępnego. Modelka dostrojone na dokumentach prawnych mogą utracić możliwość generowania kodu w języku Python lub aby odpowiedzieć na pytania z historii.
Dzieje się tak, ponieważ pełne dostrojenie bezkrytycznie modyfikuje wszystkie wagi, włączając te, które kodują ogólną wiedzę językową. W praktyce model nadpisuje swoją dotychczasową wiedzę nową.
1.3 Przechowywanie wielu modeli
Jeśli firma potrzebuje dostosować ten sam podstawowy model do 10 różnych zadań (klasyfikacja e-maili, analiza nastrojów, pytania i odpowiedzi medyczne, generowanie raportów itp.), pełne dostrojenie wymaga zapisania 10 pełnych kopii modelu: dla LLaMA 7B, to 140 GB pamięci. W przypadku LoRA, jak zobaczymy, każda adaptacja wymaga tylko 10-50 MB, łącznie 100-500 MB.
ponieważ pełne dostrajanie nie jest skalowane
| Problem | Pełne dostrajanie | PEFT (LoRA) |
|---|---|---|
| Pamięć RAM dla LLaMA 7B | ~100 GB | ~16-24 GB |
| Parametry holowania | 7 000 000 000 | ~ 4 000 000 (0,06%) |
| Magazyn do adaptacji | 14 GB | 10-50MB |
| Katastrofalne zapomnienie | Wysokie ryzyko | Minimalne ryzyko |
| Wymagany sprzęt | 4x A100 80 GB | 1x RTX 3090 24 GB |
2. Dostrajanie efektywne pod względem parametrów (PEFT): przegląd
Techniki PEFT łączy ich podstawowa zasada: zamiast aktualizujemy wszystkie parametry modelu, zamrażamy wcześniej wytrenowane ciężary i trenujemy tylko niewielki podzbiór dodatkowych parametrów. To drastycznie zmniejsza wymagania pamięciowe, przyspiesza szkolenie i utrwala wiedzę z modelu bazowego.
Taksonomia technik PEFT
| Technika | Gdzie to działa | Dodatkowe parametry | Zalety |
|---|---|---|---|
| Szybkie strojenie | Osadzenia wejściowe | Miękkie tokeny wstępnie ważone na wejściu | Bardzo proste, kilka parametrów |
| Strojenie prefiksów | Każda warstwa uwagi | Holowane przedrostki K, V | Bardziej wyraziste niż szybkie strojenie |
| Warstwy adapterów | Pomiędzy warstwami Transformatora | Włożono moduły wąskiego gardła | Elastyczny, modułowy |
| LoRA | Istniejące macierze wag | Macierze niskiego rzędu B i A | Zerowy narzut przy wnioskowaniu |
| QLoRA | Jak LoRA + kwantyzacja | LoRA w modelu 4-bitowym | Dostrajanie konsumenckich procesorów graficznych |
| DoRA | Jak LoRA + rozkład | Wielkość + kierunek | wyższa jakość niż LoRA |
2.1 Szybkie strojenie
Il szybkie strojenie (Lester i in., 2021) dodaje zestaw miękkie tokeny holowany na początku wejścia. Te żetony nie odpowiadają rzeczywistym słowom słownictwa, ale są to wektory ciągłe, których model uczy się podczas uczenia. Podstawowy model pozostaje całkowicie zamrożone: trenowane są tylko osadzania tokenów miękkich.
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.
Szybkie strojenie działa zaskakująco dobrze w przypadku bardzo dużych modeli (parametry >10B), ale traci jakość w przypadku mniejszych modeli. Dzięki T5-XXL (11B) możliwe jest szybkie dostrojenie prawie identyczna wydajność z pełnym dostrojeniem na SuperGLUE.
2.2 Strojenie prefiksów
Il strojenie prefiksu (Li i Liang, 2021) rozszerza ideę szybkiego strojenia: zamiast dodawać miękkie tokeny tylko do danych wejściowych, dodaje do pliku przedrostki, które można trenować klawisze (K) i wartości (V). każdą warstwę uwagi. To daje model większą ekspresję niż proste, szybkie strojenie.
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: Adaptacja niskiej rangi
LoRA (Hu i in., 2021) oraz najczęściej stosowaną i reprezentatywną technikę PEFT punkt zwrotny w dostrajaniu dużych modeli językowych. Kluczową ideą jest elegancki w swojej prostocie: zamiast bezpośredniego aktualizowania macierzy wag Transformatora rozkładamy aktualizację na dwie macierze niskiego rzędu.
3.1 Intuicja: hipoteza rangi wewnętrznej
Badania (Aghajanyan i in., 2020) wykazały, że kiedy się dostrajamy w przypadku konkretnego zadania aktualizacje wag modelu są skoncentrowane w: podprzestrzeń niskowymiarowa. Innymi słowy, macierz aktualizacja wagi ma ranga wewnętrzna znacznie niższy od jego rozmiar nominalny.
Rozważmy macierz wag W o wymiarach d x k (na przykład rzut zapytań w uwadze LLaMA 7B: 4096 x 4096). Kompletna aktualizacja wymagałoby tablicy zawierającej 4096 x 4096 = 16 777 216 parametrów. Ale jeśli aktualizacja ma niską rangę wewnętrzną, możemy ją przybliżyć znacznie mniejszą rangą r.
3.2 Matematyka LoRA
Biorąc pod uwagę wstępnie wyszkoloną macierz wag W_0 o rozmiarze d x k, LoRA parametryzuje aktualizacja jako iloczyn dwóch macierzy niskiego rzędu:
Podstawowa formuła LoRA
W = W_0 + \Delta W = W_0 + B \cdot A
Gdzie:
- W_0 \in \mathbb{R}^{d \times k} oraz wstępnie wyszkoloną (zamrożoną) macierz wag
- B \in \mathbb{R}^{d \times r} oraz macierz projekcji w dół
- A \in \mathbb{R}^{r \times k} oraz macierz projekcji w górę
- r \ll \min(d, k) e il stopień (zwykle 4, 8, 16, 32, 64)
Ze skalowaniem: h = W_0 x + \frac{\alpha}{r} \cdot B A x
Gdzie \alfa oraz współczynnik skalujący kontrolujący intensywność adaptacji.
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 Inicjalizacja
Inicjalizacja A i B jest kluczowa. LoRA inicjuje:
- A z równomierną inicjalizacją Kaiminga (rozkład Gaussa)
- B ze wszystkimi zerami
Dzięki temu na początku szkolenia wkład LoRA będzie wynosić dokładnie zero (B * A = 0), więc model zaczyna się od zachowania wstępnie wytrenowanego modelu. Trening stopniowo zmienia B i A, aby dostosować model do konkretnego zadania.
3.4 Współczynnik skalowania alfa
Parametr \alfa (alfa) kontroluje intensywność adaptacji LoRA. Rzeczywisty wynik to:
h = W_0 x + \frac{\alpha}{r} \cdot B A x
Raport \alfa / r działa to jak tempo uczenia się dla LoRA. W praktyce często się to odbywa \alfa = 2r (Na przykład \alfa = 32 con r = 16), co daje współczynnik skalowania 2. Pozwala to na zmianę rangi bez konieczności ponownego dostosowywania tempa uczenia się.
3.5 Moduły docelowe
LoRA nie jest stosowana do wszystkich warstw Transformatora, a jedynie do określonych matryc ciężarków. Wybór modułów docelowych znacząco wpływa na jakość tworzonego oprogramowania dostrajanie:
Moduły docelowe dla typowych architektur
| Moduł | Typ | Wymiary (LLaMA 7B) | Uderzenie |
|---|---|---|---|
q_proj |
Zapytanie o uwagę | 4096 x 4096 | Wysokie – kontroluje, czego model „szuka” |
k_proj |
Klucz uwagi | 4096 x 4096 | Wysoki — kontroluje sposób indeksowania tokenów |
v_proj |
Wartość uwagi | 4096 x 4096 | Wysoki — kontroluje wyodrębniane informacje |
o_proj |
Wyjście uwagi | 4096 x 4096 | Średni |
gate_proj |
Brama FFN | 4096 x 11008 | Wysoki - kontroluje przepływ informacji w FFN |
up_proj |
FFN w górę | 4096 x 11008 | Średnio-wysoki |
down_proj |
FFN w dół | 11008 x 4096 | Średni |
3.6 Dokładne obliczenia parametrów holowanych
Obliczamy możliwe do wytrenowania parametry dla LLaMA 2 7B z LoRA zastosowanym do q_proj i v_proj na wszystkich 32 warstwach:
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 Korzyści z wnioskowania: Zero kosztów ogólnych
Kluczową przewagą LoRA nad warstwami adapterów jest brak wnioskowania brak narzutu obliczeniowego. Matryce LoRA mogą być połączone w wagach modelu podstawowego:
W_{połączone} = W_0 + \frac{\alpha}{r} \cdot B \cdot A
Po połączeniu model ma dokładnie ten sam rozmiar i prędkość wnioskowania oryginalnego modelu, ale ze zaktualizowanymi wagami. To niemożliwe z warstwami adapterów, które dodają do modelu trwałe parametry.
4. QLoRA: skwantowana LoRA
QLoRA (Dettmers i in., 2023) oraz rozszerzenie LoRA, które łączy w sobie 4-bitowa kwantyzacja modelu podstawowego z dostrojeniem LoRA. To pozwala dostrajać modele z 65 miliardami parametrów na jednym procesorze graficznym 48 GB, wynik wcześniej niemożliwy do osiągnięcia.
4.1 Trzy innowacje QLoRA
QLoRA wprowadza trzy kluczowe techniki:
Innowacje QLoRA
- 4-bitowy NormalFloat (NF4): Nowy zoptymalizowany skwantowany typ danych dla mas o rozkładzie normalnym. Każda waga jest mapowana na jedną z 16 wartości (4 bity) wybrano, aby zminimalizować błąd kwantyzacji w rozkładzie Gaussa. NF4 przewyższa INT4 i FP4, ponieważ wagi sieci neuronowych w przybliżeniu podążają za nimi rozkład normalny.
- Podwójna kwantyzacja: Stałe kwantyzacji (po jednej dla każdego blok 64 wag) są same w sobie kwantowane do 8-bitów. To oszczędza pieniądze dodatkowe 0,37 bitów na parametr, około 3 GB dla modelu 65B.
- Optymalizatory stronicowane: Użyj pamięci stronicowanej (ujednoliconej pamięci) procesora graficznego NVIDIA do obsługi skoków pamięci podczas treningu i ruchu w razie potrzeby automatycznie optymalizuje stany pomiędzy procesorem graficznym i procesorem.
4.2 Jak działa NF4
Kwantyzacja NF4 opiera się na obserwacji, że wagi dobrze wyszkolonych sieci neuronowych mają w przybliżeniu rozkład normalny ze średnią wynoszącą zero. NF4 dzieli standardowy rozkład normalny w 16 równych przedziałach prawdopodobieństwa i przypisz a w każdym przedziale optymalna wartość minimalizująca oczekiwany błąd:
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 Rozmiar pamięci QLoRA
Porównanie pamięci: Full FT vs LoRA vs QLoRA (LLaMA 7B)
| Część | Pełny FT (FP16) | LoRA (16PR) | QLoRA (NF4) |
|---|---|---|---|
| Wagi modeli | 14 GB (FP16) | 14 GB (FP16) | 3,5 GB (NF4) |
| Gradienty | 14 GB | ~16MB | ~16MB |
| Stany optymalizatora | 56 GB | ~64MB | ~64MB |
| Aktywacje | ~12 GB | ~12 GB | ~6 GB |
| Całkowity | ~96 GB | ~26 GB | ~10 GB |
Dzięki QLoRA dostrojenie LLaMA 7B staje się możliwe na jednej karcie RTX 3090 (24 GB), i LLaMA 13B na jednej karcie RTX 4090 (24 GB) z włączoną funkcją gradientowego punktu kontrolnego.
5. DoRA: adaptacja niskiej rangi z rozkładem wagowym
DoRA (Liu i in., 2024) oraz ewolucję LoRA, która rozkłada matrycę odważników w dwóch składnikach: ogrom (wielkość) mi kierunek (kierunek). Ten rozkład jest inspirowany klasyką Normalizacja wagi autorstwa Salimans i Kingma (2016) i ma na celu zamknięcie luki w jakości pomiędzy LoRA a pełnym dostrojeniem.
5.1 Intuicja stojąca za DoRA
Analiza wzorców uczenia się pokazuje zasadniczą różnicę pomiędzy pełnym dostrajanie i LoRA: Pełne dostrajanie zmienia zarówno wielkość, jak i kierunek wag niezależnie, podczas gdy LoRA ma tendencję do modyfikowania ich proporcjonalnie, ograniczając jego ekspresję.
5.2 Matematyka DoRA
DoRA rozkłada wstępnie wytrenowaną macierz ciężarów na:
Formuła DoRA
W' = m \cdot \frac{W_0 + BA}{\|W_0 + BA\|_c}
Gdzie:
- m \in \mathbb{R}^{1 \times k} oraz (możliwy do wyszkolenia) wektor wielkości, zainicjowany normami kolumnowymi W_0
- W_0 + BA reprezentuje zaktualizowany kierunek za pośrednictwem LoRA
- \|\cdot\|_c oraz norma kolumnowa, która normalizuje każdą kolumnę do normy jednostkowej
- B e A to standardowe macierze LoRA (możliwe do trenowania)
W ten sposób DoRA ma dwa niezależne stopnie swobody:
- La kierunek i kontrolowane przez B i A (dokładnie jak w LoRA)
- La ogrom i kontrolowane przez wektor m, który może zmieniać się niezależnie od kierunku
5.3 Przewaga DoRA nad LoRA
Benchmark DoRA vs LoRA (zdroworozsądkowe rozumowanie, LLaMA-7B)
| Zadania | LoRA (r=32) | DoRA (r=32) | Pełne FT |
|---|---|---|---|
| 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 |
| Przeciętny | 78,7 | 80,0 | 80,9 |
DoRA zmniejsza różnicę pomiędzy LoRA a pełnym dostrojeniem o około 60-70%, przy nakładzie finansowym parametrów minimalnych (tylko dodatkowy wektor m).
6. Warstwy adapterów
The Warstwy adapterów (Houlsby i in., 2019) byli jednymi z pierwszych proponowane techniki PEFT. Pomysł jest prosty: włóż małe, ciągnione moduły (adapter) pomiędzy istniejącymi warstwami Transformatora, zachowując oryginalne wagi.
6.1 Adaptery wąskiego gardła
Klasyczny adapter i moduł szyjka z trzema elementami:
- Projekcja w dół: zmniejsza rozmiar z d_model do d_bottleneck (np. 4096 -> 64)
- Nieliniowość: zazwyczaj ReLU lub GELU
- Projekcja w górę: przywraca oryginalny rozmiar (np. 64 -> 4096)
- Pozostałe połączenie: wyjście adaptera jest dodawane do wejścia
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 Porównanie: Adaptery vs LoRA
Adaptery vs LoRA: kluczowe różnice
| Charakterystyczny | Warstwy adapterów | LoRA |
|---|---|---|
| Nad głową przy wnioskowaniu | Tak (dodatkowe warstwy) | Nie (łączy wagi) |
| Dodatkowe opóźnienie | ~5-10% wzrostu | 0% |
| Możliwość komponowania | Wysoka (połączenie adaptera) | Średnia (suma LoRA) |
| Łatwość wdrożenia | Wysoki | Wysoki |
| Wsparcie biblioteki | Adapter Hub, PEFT | PEFT, nielenistwo, aksolotl |
7. Praktyczne wdrożenie z HuggingFace PEFT
Przejdźmy do ćwiczeń. W tej sekcji zaimplementujemy dostrajanie modelu Transformator wykorzystujący LoRA i QLoRA z biblioteką HuggingFace PEFT. Będziemy używać Mistrala 7B jako model podstawowy i zaadaptujemy go do zadania instrukcja-odpowiedź.
7.1 Konfiguracja i instalacja
# 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 Konfiguracja 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 Dostrajanie za pomocą 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 Dostrajanie za pomocą QLoRA (4-bitowe)
# 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. Przygotowanie zbioru danych
Jakość dostrajania zależy w ogromnym stopniu od jakości zbioru danych. W tym w sekcji zobaczmy, jak przygotować dane w standardowych formatach używanych do dostrajania firmy LLM.
8.1 Format alpaki
Format Alpaca (Stanford, 2023) jest jednym z najpopularniejszych formatów dostrajania LLM w zadaniach reagowania na instrukcje. Każdy przykład ma trzy pola:
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 Szablony czatu (Mistral/ChatML)
W przypadku modeli konwersacyjnych bardziej odpowiedni jest format szablonu czatu. Każdy model ma swój własny, specyficzny szablon:
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 Trening z SFTrainerem
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. Łączenie wag i wdrażanie
Po szkoleniu mamy mrożony model bazowy i mały adapter LoRA. W przypadku wdrożenia możemy zachować je oddzielnie (przydatne do przełączania między różne adaptacje) lub połączyć je w jeden model.
9.1 Połącz wagi LoRA z modelem podstawowym
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 Wnioskowanie z modelu dostrojonego
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. Strojenie hiperparametrów
Dobór hiperparametrów jest kluczowy dla uzyskania dobrych wyników. Oto przewodnik praktyka oparta na doświadczeniach społeczności i opublikowanych wzorcach.
Przewodnik po hiperparametrach LoRA
| Hiperparametr | Zakres | Rozmyślny | Notatki |
|---|---|---|---|
| ranga (r) | 4-256 | 16-64 | Podwyżka za złożone zadania; r=8 często wystarcza do klasyfikacji |
| alfa | 8-128 | 2* pozycja | alfa/r i skalowanie efektywne; utrzymuj stały współczynnik przy zmianie r |
| współczynnik_uczenia się | 1e-5 - 5e-4 | 2e-4 | Wyższa niż pełna FT (10-100x); zmniejszyć, jeśli strata się zmienia |
| rozmiar_partii | 1-32 | 4-8 | Użyj gradient_accumulation, aby symulować większe partie |
| epoki | 1-5 | 2-3 | Uważaj na nadmierne dopasowanie; monitorować utratę ewaluacji |
| współczynnik_rozgrzewki | 0,01-0,1 | 0,03 | Ważne dla stabilności; wyższy przy wysokim LR |
| spadkowicz | 0,0-0,1 | 0,05 | 0,0 dla dużych zbiorów danych, 0,1 dla małych zbiorów danych |
| max_seq_length | 512-8192 | 2048 | Wyższa = więcej VRAM; dostosować się do zbioru danych |
Typowe błędy w dostrajaniu
- Zbyt wysoka szybkość uczenia się: strata jest zmienna lub zróżnicowana. Rozwiązanie: Zmniejsz LR 2-5x lub zwiększ rozgrzewkę
- Zbyt niska pozycja: model nie uczy się wystarczająco dużo. Rozwiązanie: Zwiększ r z 8 do 16 lub 32
- Pozycja zbyt wysoka: nadmierne dopasowanie, szczególnie w przypadku małych zbiorów danych. Rozwiązanie: Zmniejsz r lub zwiększ liczbę osób porzucających naukę
- Kilka epok: niedopasowanie. Sprawdź, czy strata eval nadal spada
- Za dużo epok: straty treningowe maleją, ale straty ewaluacyjne rosną (przeuczenie)
- Nieoczyszczony zbiór danych: duplikaty, błędy, niespójne formatowanie pogarszają jakość
- Zapomnij o punktach kontrolnych gradientu: Natychmiastowe OOM na dużych modelach
11. Benchmarki i porównania
Jak wybrać pomiędzy LoRA, QLoRA, DoRA i pełnym dostrajaniem? Oto systematyczne porównanie w oparciu o publiczne testy porównawcze i testy społeczności.
Pełne porównanie: LoRA vs QLoRA vs DoRA vs Full FT
| Metryczny | Pełne FT | LoRA | QLoRA | DoRA |
|---|---|---|---|---|
| jakość (ławka MT) | 7.8 | 7,5 | 7.3 | 7.6 |
| Pamięć RAM (model 7B) | ~100 GB | ~26 GB | ~10 GB | ~26 GB |
| Prędkość treningowa (względna) | 1,0x | 1,2x | 0,8x | 1,1x |
| Parametry holowania | 100% | 0,1-0,5% | 0,1-0,5% | 0,1-0,5% + m |
| Adaptacja magazynu | 14 GB | 10-50MB | 10-50MB | 10-50MB |
| Narzut wnioskowania | 0% | 0% (połączone) | 0% (połączone) | 0% (połączone) |
| Minimalny procesor graficzny (7B) | 4xA100 | 1x A100 40 GB | 1xRTX3090 | 1x A100 40 GB |
12. Wymagania sprzętowe
Wybór okuć zależy od modelu, który chcesz dostroić i techniki PEFT którego używasz. Oto praktyczny przewodnik po konsumenckich i profesjonalnych procesorach graficznych.
Wymagania GPU dotyczące dostrajania
| GPU | VRAM | LoRA (16PR) | QLoRA (4-bitowy) | Notatki |
|---|---|---|---|---|
| RTX3060 | 12 GB | do 3B | do 7B (sekwencja 512) | Poziom podstawowy, ograniczony |
| RTX-a 3090 | 24 GB | do 7B | do 13B | Doskonały do QLoRA 7B |
| RTX4090 | 24 GB | do 7B | do 13B | Szybszy niż 3090, ta sama pamięć VRAM |
| A100 40 GB | 40 GB | do 13B | do 34B | Standard profesjonalny |
| A100 80 GB | 80 GB | do 30B | do 70B | Idealny do dużych modeli |
| H100 80 GB | 80 GB | do 30B | do 70B | Szybszy niż A100, obsługa FP8 |
Wskazówki dla konsumentów procesorów graficznych
- Ograniczony budżet (RTX 3060 12 GB): QLoRA w modelach 7B z seq_length = 512, Batch_size = 1, gradient_accumulation = 16
- Dobry stosunek jakości do ceny (RTX 3090/4090 24 GB): QLoRA w modelach 7-13B z seq_length = 2048, Batch_size = 4
- Przetwarzanie w chmurze: Google Colab Pro (10 USD/miesiąc) oferuje A100 40 GB na ograniczone sesje; RunPod i Lambda Labs do intensywnego użytkowania
- Zawsze aktywuj: gradient_checkpointing=True, optymalizatory stronicowane, Flash Uwaga 2
13. Praktyczne przypadki użycia
Przyjrzyjmy się konkretnym przypadkom użycia, w których dostrojenie za pomocą LoRA jest szczególnie skuteczne.
13.1 Klasyfikacja tekstu
Do zadań klasyfikacyjnych (analiza nastrojów, klasyfikacja tematów, wykrywanie spamu), LoRA jest często preferowana, ponieważ:
- Zadanie wymaga kilku dodatkowych parametrów (wystarczy ranga r=4-8)
- Zbiory danych klasyfikacyjnych są zazwyczaj małe (przykłady 1–50 tys.)
- Ryzyko przeuczenia się przy wysokiej i wysokiej randze
13.2 Generowanie kodu
Do dostrajania generowania kodu (np. dostosowywania modelu do określonego języka lub umowy firmowe), polecamy:
- Wyższa ranga (r=32-64), ponieważ kod ma większą strukturę i zmienność
- Kompletne moduły docelowe (wszystkie 7 modułów transformatorowych)
- Wysokiej jakości zbiory danych: dobrze udokumentowany kod, z testami, spójny styl
- Długie sekwencje (max_seq_length=4096-8192) dla pełnego kontekstu
13.3 Chatboty specyficzne dla domeny
Aby stworzyć wyspecjalizowanego chatbota (wsparcie medyczne, prawne, techniczne):
- Zbiór danych konwersacyjnych w formacie szablonu czatu modelu
- Podaj przykłady odmowy („Nie mogę odpowiedzieć na to pytanie”)
- Średnia ranga (r=16-32)
- Walidacja z ekspertami dziedzinowymi, a nie tylko automatyczne wskaźniki
13.4 Podsumowanie
Aby dostosować zadania sumaryczne:
- Zbiór danych z parami wysokiej jakości (dokument, podsumowanie).
- Długie sekwencje wprowadzania danych (do 8192 tokenów)
- Ranga średnio-wysoka (r=16-32)
- Ocena za pomocą wskaźników takich jak ROUGE i ocena człowieka
14. Wnioski i drzewo decyzyjne
Techniki PEFT umożliwiły precyzyjne dostrajanie dużych modeli językowych dla każdego, kto ma konsumencki procesor graficzny. LoRA, QLoRA i DoRA reprezentują najnowocześniejsze rozwiązania do wydajnego dostrajania parametrów, każdy z własnymi mocnymi stronami.
Drzewo decyzyjne: kiedy czego używać
| Sytuacja | Zalecana technika | Motywacja |
|---|---|---|
| Karta graficzna z < 16 GB VRAM | QLoRA | Jedyna opcja dla modeli 7B+ na sprzęcie konsumenckim |
| Karta graficzna z 24-40 GB VRAM | LoRA (16PR) | Lepsza jakość niż QLoRA, szybsze szkolenie |
| Wymagana najwyższa jakość | DoRA | Bliżej pełnego FT, minimalne obciążenie na LoRA |
| Wiele adaptacji tego samego modelu | LoRA | Małe adaptery (~50MB), szybkie przełączanie pomiędzy zadaniami |
| Mały model (< parametry 1B) | Pełne dostrajanie | W przypadku małych modeli pełny FT jest często wykonalny i lepszy |
| Zadanie proste (klasyfikacja) | LoRA (r=4-8) | Niska i wystarczająca ranga; unikaj nadmiernego dopasowania |
| Złożone zadanie (generowanie kodu) | LoRA/DoRA (r=32-64) | Wysoka ranga, aby uchwycić złożoność zadania |
| Skład umiejętności | Fuzja adapterów | Połącz wiele adaptacji w uporządkowany sposób |
Dziedzina wydajnego i szybko rozwijającego się tuningu. Nowe techniki, np W bród (Projekcja gradientowa niskiego poziomu) e ReLoRA (iteracyjne szkolenie LoRA ze wzrostem rangi) obiecują dalszą redukcję luka przy pełnym dostrojeniu. Jednak LoRA i QLoRA pozostają dziś standardem de facto w celu dostrojenia LLM, z dojrzałym ekosystemem bibliotek (HuggingFace PEFT, Unsloth, Axolotl) i aktywną społeczność.
W następnym artykule z tej serii omówimy Kwantyzacja modeli: GPTQ, AWQ, INT8 i jak zmniejszyć rozmiar modelu o 75% przy zachowaniu jakości do wdrożenia produkcyjnego.
Zasoby i referencje
- Dokumenty LoRA: „LoRA: adaptacja modeli wielkojęzykowych niskiej rangi” (Hu i in., 2021)
- Dokumenty QLoRA: „QLoRA: efektywne dostrajanie skwantowanych LLM” (Dettmers i in., 2023)
- Papierowa DoRA: „DoRA: adaptacja niskiej rangi z rozkładem wagowym” (Liu i in., 2024)
- Adaptery papieru: „Uczenie się efektywnego transferu parametrów” (Houlsby i in., 2019)
- Przytulająca twarz PEFT: https://github.com/huggingface/peft
- Nielenistwo: https://github.com/unslothai/unsloth (LoRA 2-5x szybciej)
- TRL (uczenie się wzmacniania transformatora): https://github.com/huggingface/trl







