Fine-tuning LoRA z Hugging Face PEFT
Skonfiguruj rangę LoRA, parametr alpha i moduły docelowe, przeprowadź nadzorowany fine-tuning za pomocą TRL SFTTrainer, monitoruj stratę treningową i zapisuj checkpointy scalone oraz zawierające wyłącznie adapter.
Fine-tuning LoRA z Hugging Face PEFT to bezpłatna lekcja AI Engineering Academy na CoddyKit. To lekcja 3 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej AI Engineering Academy, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs AI Engineering Academy zawiera 4 lekcji w sumie.
Dlaczego LoRA zamiast pełnego dostrajania?
Pełne dostrajanie aktualizuje każdy parametr modelu. W przypadku modelu o 7 miliardach parametrów i precyzji float32 wymaga to około 28 GB pamięci GPU tylko na wagi, a wraz ze stanami optymalizatora, gradientami i aktywacjami łącznie z łatwością 80–120 GB. LoRA (Low-Rank Adaptation) zamiast tego dodaje niewielką liczbę trenowalnych parametrów (zwykle 0,1–1% całości) w postaci par macierzy niskiego rzędu stosowanych do wybranych warstw. Zmniejsza to wymagania GPU 10–50-krotnie, zapewniając porównywalne wyniki.
# LoRA math intuition:
# Full fine-tuning: update W (large matrix, e.g., 4096 x 4096 = 16.7M parameters)
# LoRA: instead train W = W_0 + A @ B where:
# A has shape (4096, r) - only r*4096 params
# B has shape (r, 4096) - only r*4096 params
# r (rank) is typically 4, 8, or 16 - much smaller than 4096
# Memory comparison for 7B model:
# Full fine-tuning: ~80GB GPU RAM
# LoRA (rank=8): ~12GB GPU RAM - fits on a single A100 or 3090
print('LoRA makes fine-tuning accessible without massive GPU clusters')Instalowanie wymaganych bibliotek
Dostrajanie LoRA za pomocą Hugging Face wymaga trzech bibliotek: transformers (ładowanie modelu i tokenizacja), peft (Parameter-Efficient Fine-Tuning, implementujące LoRA) oraz trl (Transformer Reinforcement Learning, udostępniające SFTTrainer do nadzorowanego dostrajania). Razem zapewniają one wysokopoziomowy, gotowy do użycia w środowisku produkcyjnym przepływ pracy związany z dostrajaniem.
# pip install transformers peft trl accelerate bitsandbytes datasets
from transformers import AutoModelForCausalLM, AutoTokenizer, BitsAndBytesConfig
from peft import LoraConfig, get_peft_model, TaskType
from trl import SFTTrainer, SFTConfig
from datasets import Dataset
import torch
print('Libraries imported successfully')
print(f'GPU available: {torch.cuda.is_available()}')
if torch.cuda.is_available():
print(f'GPU: {torch.cuda.get_device_name(0)}')
print(f'GPU memory: {torch.cuda.get_device_properties(0).total_memory / 1e9:.1f} GB')Ładowanie modelu bazowego z kwantyzacją
W przypadku większości dostrajania LoRA model bazowy należy ładować z użyciem kwantyzacji 4-bitowej za pomocą bitsandbytes. Zmniejsza to zajmowaną przez model bazowy pamięć o 75% (model 7B zużywa około 4 GB zamiast około 14 GB), zachowując większość jego możliwości. Tylko warstwy adaptera LoRA są trenowane z pełną precyzją. Połączenie kwantyzacji 4-bitowej i LoRA nazywa się QLoRA i umożliwia dostrajanie modeli 7B na konsumenckich kartach GPU.
from transformers import AutoModelForCausalLM, AutoTokenizer, BitsAndBytesConfig
import torch
MODEL_NAME = 'mistralai/Mistral-7B-Instruct-v0.2' # or 'meta-llama/Llama-3.2-3B-Instruct'
# 4-bit quantization config (QLoRA)
bnb_config = BitsAndBytesConfig(
load_in_4bit=True, # quantize base model to 4-bit
bnb_4bit_quant_type='nf4', # NF4 quantization type
bnb_4bit_compute_dtype=torch.float16, # compute in float16
bnb_4bit_use_double_quant=True # double quantization for extra savings
)
# Load quantized model
model = AutoModelForCausalLM.from_pretrained(
MODEL_NAME,
quantization_config=bnb_config,
device_map='auto', # automatically distribute across GPUs
trust_remote_code=True
)
tokenizer = AutoTokenizer.from_pretrained(MODEL_NAME)
tokenizer.pad_token = tokenizer.eos_token # required for training batches
print(f'Model loaded. Parameters: {model.num_parameters():,}')Konfigurowanie hiperparametrów LoRA
Trzy najważniejsze hiperparametry LoRA to: r (ranga) — wymiar macierzy niskiego rzędu; wyższa ranga oznacza więcej trenowalnych parametrów i większą ekspresyjność adaptacji, ale także większe zużycie pamięci i ryzyko przeuczenia. lora_alpha — współczynnik skalowania, zwykle ustawiany na dwukrotność rangi. target_modules — macierze wag, do których należy zastosować LoRA; najczęściej wybiera się warstwy uwagi (q_proj, v_proj). Należy zacząć od r=8 i następnie dostrajać tę wartość.
from peft import LoraConfig, TaskType
lora_config = LoraConfig(
task_type=TaskType.CAUSAL_LM, # causal language modeling
r=8, # rank: 4, 8, 16, 32 — higher = more params
lora_alpha=16, # scaling: usually 2*r
lora_dropout=0.1, # dropout on LoRA layers for regularization
target_modules=['q_proj', 'v_proj', # which weight matrices to adapt
'k_proj', 'o_proj', # common to target all attention projections
'gate_proj', 'up_proj', 'down_proj'], # and FFN layers
bias='none', # do not adapt bias parameters
)
# Wrap the model with LoRA adapters
from peft import get_peft_model, prepare_model_for_kbit_training
model = prepare_model_for_kbit_training(model) # prepares quantized model for training
model = get_peft_model(model, lora_config)
model.print_trainable_parameters()
# Output: trainable params: 4,194,304 || all params: 3,752,071,168 || trainable%: 0.11%Przygotowywanie zbioru danych
SFTTrainer oczekuje zbiorów danych w określonym formacie. Najprostszy format to zbiór z pojedynczą kolumną text zawierającą w pełni sformatowany ciąg prompt + odpowiedź. Do spójnego formatowania przykładów konwersacji należy używać szablonu czatu tokenizera. SFTTrainer obsługuje tokenizację, tworzenie partii oraz maskowanie funkcji straty (obliczanie straty wyłącznie dla odpowiedzi asystenta, a nie dla promptu wejściowego).
from datasets import Dataset
import json
def load_and_format_dataset(jsonl_path: str, tokenizer) -> Dataset:
examples = []
with open(jsonl_path) as f:
for line in f:
ex = json.loads(line.strip())
# Apply chat template to format as expected by the model
formatted = tokenizer.apply_chat_template(
ex['messages'],
tokenize=False,
add_generation_prompt=False
)
examples.append({'text': formatted})
return Dataset.from_list(examples)
# Load training and validation datasets
train_dataset = load_and_format_dataset('train.jsonl', tokenizer)
val_dataset = load_and_format_dataset('validation.jsonl', tokenizer)
print(f'Train examples: {len(train_dataset)}')
print(f'Validation examples: {len(val_dataset)}')
print('Sample formatted text:')
print(train_dataset[0]['text'][:300])Trening za pomocą SFTTrainer
SFTTrainer z biblioteki TRL opakowuje Hugging Face Trainer i udostępnia wartości domyślne dla nadzorowanego dostrajania. Należy skonfigurować hiperparametry treningu: liczbę epok (w przypadku dostrajania instrukcyjnego zwykle wystarczą 1–3), rozmiar partii, liczbę kroków akumulacji gradientu (umożliwiającą symulowanie większych partii przy ograniczonej pamięci), współczynnik uczenia (2e-4 to często dobry punkt wyjścia) oraz katalog wyjściowy dla punktów kontrolnych.
from trl import SFTTrainer, SFTConfig
training_args = SFTConfig(
output_dir='./fine-tuned-model',
num_train_epochs=2, # 2-3 epochs for instruction fine-tuning
per_device_train_batch_size=4, # increase if GPU memory allows
gradient_accumulation_steps=4, # effective batch size = 4*4 = 16
learning_rate=2e-4, # typical LoRA learning rate
warmup_ratio=0.05, # warmup for 5% of steps
lr_scheduler_type='cosine', # cosine decay learning rate schedule
logging_steps=10,
eval_strategy='steps',
eval_steps=50, # evaluate on validation set every 50 steps
save_steps=100,
max_seq_length=2048, # max token length per example
fp16=True, # mixed precision training
report_to='none' # or 'wandb' for experiment tracking
)
trainer = SFTTrainer(
model=model,
args=training_args,
train_dataset=train_dataset,
eval_dataset=val_dataset,
)
print('Starting LoRA fine-tuning...')
trainer.train()Monitorowanie postępów treningu
Podczas treningu należy obserwować dwie kluczowe metryki: strata treningowa powinna stale maleć. Strata walidacyjna powinna początkowo maleć, a następnie ustabilizować się lub nieznacznie wzrosnąć (co oznacza przeuczenie). Jeśli strata walidacyjna znacząco rośnie, a strata treningowa nadal maleje, należy wcześniej zakończyć trening — model zapamiętuje przykłady treningowe zamiast uogólniać. Optymalny moment zakończenia przypada tuż przed rozpoczęciem wzrostu straty walidacyjnej.
# Reading training logs
# Training step logs look like:
# {'loss': 1.4523, 'grad_norm': 0.85, 'learning_rate': 0.0002, 'epoch': 0.2, 'step': 20}
# {'loss': 1.2341, 'grad_norm': 0.72, 'learning_rate': 0.00018, 'epoch': 0.4, 'step': 40}
# Validation results look like:
# {'eval_loss': 1.1823, 'eval_runtime': 12.3, 'eval_samples_per_second': 8.1, 'step': 50}
# {'eval_loss': 1.0923, 'eval_runtime': 12.1, 'eval_samples_per_second': 8.3, 'step': 100}
# {'eval_loss': 1.1234, 'eval_runtime': 12.4, 'eval_samples_per_second': 8.2, 'step': 150}
# ^ validation loss went UP at step 150 - overfitting starting
# Load the best checkpoint (lowest validation loss)
from transformers import TrainerCallback
print('Best model checkpoint is saved automatically by SFTTrainer (load_best_model_at_end=True)')Zapisywanie i scalanie adapterów LoRA
Po zakończeniu treningu należy zapisać wagi adaptera LoRA oddzielnie od modelu bazowego. Adapter jest niewielki (od kilku MB do kilkuset MB) i można go stosować do modelu bazowego podczas wnioskowania. Na potrzeby wdrożenia produkcyjnego można również scalić wagi LoRA z modelem bazowym, tworząc pojedynczy plik modelu, który podczas wnioskowania nie wymaga biblioteki PEFT — zmniejsza to narzut wnioskowania.
# Save only the LoRA adapter (tiny - typically 10-100MB)
model.save_pretrained('./lora-adapter-only')
tokenizer.save_pretrained('./lora-adapter-only')
# Load the adapter for inference (requires base model + peft)
from peft import PeftModel
base_model = AutoModelForCausalLM.from_pretrained(MODEL_NAME, torch_dtype=torch.float16, device_map='auto')
model_with_adapter = PeftModel.from_pretrained(base_model, './lora-adapter-only')
# Alternative: Merge adapter into base model (no PEFT needed at inference)
print('Merging LoRA weights into base model...')
merged_model = model_with_adapter.merge_and_unload() # creates a regular model
merged_model.save_pretrained('./merged-model')
print('Merged model saved - can be used with standard transformers, no PEFT needed')Wnioskowanie za pomocą dostrojonego modelu
Przed uznaniem dostrajania za zakończone należy przetestować dostrojony model na zbiorze promptów odłożonych na bok. Należy porównać obok siebie wyniki modelu bazowego i dostrojonego dla tych samych promptów, aby sprawdzić, czy dostrajanie osiągnęło swoje cele. Trzeba uwzględnić zarówno przypadki, które model powinien obsługiwać lepiej (zadanie docelowe), jak i te, które nadal powinien obsługiwać poprawnie (zadania ogólne, których jakości nie chcą Państwo pogorszyć).
def generate(model, tokenizer, prompt: str, max_new_tokens=512) -> str:
inputs = tokenizer(prompt, return_tensors='pt').to(model.device)
with torch.no_grad():
outputs = model.generate(
**inputs,
max_new_tokens=max_new_tokens,
temperature=0.1,
do_sample=True,
pad_token_id=tokenizer.eos_token_id
)
# Decode only the new tokens (not the input prompt)
new_tokens = outputs[0][inputs['input_ids'].shape[1]:]
return tokenizer.decode(new_tokens, skip_special_tokens=True)
# Compare base vs fine-tuned
test_prompt = 'Extract JSON from: "Alice Johnson, 28, software engineer in NYC since 2021"'
print('=== BASE MODEL ===')
print(generate(base_model, tokenizer, test_prompt))
print('\n=== FINE-TUNED MODEL ===')
print(generate(merged_model, tokenizer, test_prompt))Podsumowanie kluczowych hiperparametrów LoRA
Aby dostosować LoRA do swojego przypadku użycia, należy zacząć od tych wartości domyślnych i zmieniać po jednej wartości naraz. r=8 to bezpieczny punkt wyjścia — należy zwiększyć do 16 lub 32, jeśli model potrzebuje większej ekspresyjności. lora_alpha = 2*r to stabilny wybór. Współczynnik uczenia 2e-4 sprawdza się w większości przypadków dostrajania instrukcyjnego; należy zmniejszyć go do 1e-4, jeśli strata treningowa jest niestabilna. Epoki 1–3: należy zakończyć trening, gdy strata walidacyjna przestanie maleć.
Kiedy zamiast tego użyć dostrajania OpenAI
Hugging Face PEFT LoRA wymaga procesora GPU. Jeśli nie mają Państwo infrastruktury GPU, API dostrajania OpenAI jest zarządzaną alternatywą, która obsługuje infrastrukturę treningową. Należy przesłać plik JSONL, wywołać API w celu rozpoczęcia treningu i odebrać identyfikator dostrojonego modelu, którego można używać w wywołaniach API. Dostrajanie OpenAI obsługuje GPT-4o-mini i GPT-3.5-turbo. Koszt w przeliczeniu na token jest wyższy, ale rozwiązanie całkowicie eliminuje konieczność zarządzania infrastrukturą.
from openai import OpenAI
client = OpenAI()
# Upload training file
train_file = client.files.create(
file=open('train.jsonl', 'rb'),
purpose='fine-tune'
)
val_file = client.files.create(
file=open('validation.jsonl', 'rb'),
purpose='fine-tune'
)
# Create fine-tuning job
job = client.fine_tuning.jobs.create(
training_file=train_file.id,
validation_file=val_file.id,
model='gpt-4o-mini', # base model to fine-tune
hyperparameters={
'n_epochs': 3,
'batch_size': 'auto',
'learning_rate_multiplier': 'auto'
}
)
print(f'Fine-tuning job created: {job.id}')
# Monitor: client.fine_tuning.jobs.retrieve(job.id)
# Use: client.chat.completions.create(model=job.fine_tuned_model, ...)Szybkie sprawdzenie
Sprawdź swoją wiedzę na temat dostrajania LoRA na podstawie tej lekcji.
Podsumowanie lekcji
W tej lekcji poznali Państwo następujące zagadnienia: LoRA zmniejsza wymagania GPU związane z dostrajaniem 10–50-krotnie, ponieważ trenowane są tylko niewielkie macierze adapterów niskiego rzędu, a nie wszystkie parametry modelu; QLoRA (kwantyzacja 4-bitowa + LoRA) umożliwia dostrajanie modeli 7B na konsumenckich kartach GPU z około 12 GB pamięci VRAM; a SFTTrainer z biblioteki TRL udostępnia wysokopoziomowy interfejs API obsługujący tokenizację, tworzenie partii, maskowanie funkcji straty i zapisywanie punktów kontrolnych. Następnie zajmiemy się oceną i wdrożeniem dostrojonego modelu.
Często zadawane pytania
Czy lekcja „Fine-tuning LoRA z Hugging Face PEFT” jest bezpłatna?
Tak — pełny tekst „Fine-tuning LoRA z Hugging Face PEFT” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu AI Engineering Academy, przejdź na CoddyKit PRO. Kurs AI Engineering Academy zawiera 4 lekcji w sumie.
Co nauczysz się w „Fine-tuning LoRA z Hugging Face PEFT”?
Skonfiguruj rangę LoRA, parametr alpha i moduły docelowe, przeprowadź nadzorowany fine-tuning za pomocą TRL SFTTrainer, monitoruj stratę treningową i zapisuj checkpointy scalone oraz zawierające wyłą… Ćwiczysz AI Engineering Academy z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.
Czy potrzebuję doświadczenia, aby zacząć AI Engineering Academy?
Nie wymagamy żadnego doświadczenia. AI Engineering Academy w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 3 z 4.
Ile czasu zajmuje lekcja „Fine-tuning LoRA z Hugging Face PEFT”?
Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.
Czy mogę pisać i uruchamiać kod w tej lekcji AI Engineering Academy?
Tak. Każda lekcja AI Engineering Academy zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.
Wszystkie lekcje w tym kursie
- Kiedy fine-tuning przewyższa promptowanie
- Przygotowanie wysokiej jakości zbioru treningowego
- Fine-tuning LoRA z Hugging Face PEFT
- Ocena i wdrażanie dostrojonego modelu