0Pricing
AI Engineering Academy · Lezione

Fine-tuning LoRA con Hugging Face PEFT

Configuri il rank LoRA, alpha e i moduli target, esegua il fine-tuning supervisionato con TRL SFTTrainer, monitori la loss di addestramento e salvi checkpoint uniti e contenenti solo gli adapter.

Fine-tuning LoRA con Hugging Face PEFT è una lezione AI Engineering Academy gratuita su CoddyKit. Questa è la lezione 3 di 4. Puoi leggere la lezione completa qui gratuitamente — poi esercitati direttamente nel browser con un editor di codice integrato e un tutor IA disponibile 24/7. Fa parte del percorso di apprendimento AI Engineering Academy, e i tuoi progressi si sincronizzano tra il web e l'app CoddyKit. Il corso AI Engineering Academy include 4 lezioni in totale.

Perché usare LoRA invece del fine-tuning completo?

Il fine-tuning completo aggiorna ogni parametro di un modello. Per un modello da 7 miliardi di parametri con precisione float32, sono necessari circa 28 GB di memoria GPU solo per i pesi, oltre agli stati dell'ottimizzatore, ai gradienti e alle attivazioni: in totale, facilmente 80-120 GB. LoRA (Low-Rank Adaptation) aggiunge invece un numero minimo di parametri addestrabili (in genere lo 0,1-1% del totale) sotto forma di coppie di matrici a basso rango applicate a livelli selezionati, riducendo di 10-50 volte il requisito di memoria GPU e ottenendo risultati comparabili.

# 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')

Installazione delle librerie necessarie

Il fine-tuning LoRA con Hugging Face richiede tre librerie: transformers (caricamento del modello e tokenizzazione), peft (Parameter-Efficient Fine-Tuning, che implementa LoRA) e trl (Transformer Reinforcement Learning, che fornisce SFTTrainer per il fine-tuning supervisionato). Insieme, queste librerie offrono un flusso di lavoro di fine-tuning di alto livello e pronto per la produzione.

# 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')

Caricamento del modello di base con quantizzazione

Per la maggior parte dei fine-tuning LoRA, carichi il modello di base con quantizzazione a 4 bit usando bitsandbytes. In questo modo riduce del 75% l'occupazione di memoria del modello di base (un modello 7B passa da circa 14 GB a circa 4 GB), mantenendo la maggior parte delle capacità del modello. Solo i livelli adapter LoRA vengono addestrati in precisione completa. La combinazione di quantizzazione a 4 bit e LoRA è chiamata QLoRA e rende il fine-tuning dei modelli 7B accessibile sulle GPU per utenti comuni.

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():,}')

Configurazione degli iperparametri LoRA

I tre iperparametri LoRA più importanti sono: r (rank) — la dimensione delle matrici a basso rango; un rank più alto significa più parametri addestrabili e un adattamento più espressivo, ma anche un maggiore consumo di memoria e un rischio più elevato di overfitting. lora_alpha — un fattore di scalabilità generalmente impostato a 2 volte il rank. target_modules — le matrici dei pesi a cui applicare LoRA; i livelli di attenzione (q_proj, v_proj) sono la scelta più comune. Inizi con r=8 e modifichi il valore in base ai risultati.

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%

Preparazione del dataset

SFTTrainer si aspetta dataset in un formato specifico. Il formato più semplice è un dataset con una singola colonna text contenente la stringa completa formata da prompt + risposta. Utilizzi il chat template del tokenizer per formattare in modo coerente gli esempi di conversazione. SFTTrainer gestisce tokenizzazione, raggruppamento in batch e mascheramento della loss (calcolando la loss solo sulle risposte dell'assistente, non sul prompt di input).

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])

Addestramento con SFTTrainer

Il SFTTrainer di TRL avvolge il Trainer di Hugging Face con impostazioni predefinite per il fine-tuning supervisionato. Lo configuri con gli iperparametri di addestramento: numero di epoche (di solito 1-3 sono sufficienti per il fine-tuning di istruzioni), dimensione del batch, numero di passaggi di accumulo dei gradienti (per simulare batch più grandi con memoria limitata), learning rate (2e-4 è un punto di partenza comune) e directory di output per i checkpoint.

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()

Monitoraggio dell'avanzamento dell'addestramento

Durante l'addestramento, tenga d'occhio due metriche fondamentali: la loss di addestramento dovrebbe diminuire costantemente. La loss di validazione dovrebbe inizialmente diminuire, poi stabilizzarsi o aumentare leggermente (overfitting). Se la loss di validazione aumenta significativamente mentre la loss di addestramento continua a diminuire, interrompa subito l'addestramento: il modello sta memorizzando gli esempi di addestramento invece di generalizzare. Il punto di arresto ottimale è appena prima che la loss di validazione inizi ad aumentare.

# 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)')

Salvataggio e unione degli adapter LoRA

Dopo l'addestramento, salvi separatamente i pesi dell'adapter LoRA e quelli del modello di base. L'adapter è di dimensioni ridotte (da pochi MB a poche centinaia di MB) e può essere applicato al modello di base durante l'inferenza. Per la distribuzione in produzione, può anche unire i pesi LoRA al modello di base, creando un unico file di modello che non richiede la libreria PEFT durante l'inferenza, riducendone così il sovraccarico.

# 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')

Esecuzione dell'inferenza sul modello sottoposto a fine-tuning

Prima di dichiarare il successo dell'operazione, testi il modello sottoposto a fine-tuning su un insieme di prompt riservati. Confronti affiancati gli output del modello di base e del modello sottoposto a fine-tuning sugli stessi prompt, per verificare che il fine-tuning abbia raggiunto i propri obiettivi. Controlli sia i casi che il modello dovrebbe gestire meglio (l'attività obiettivo), sia quelli che dovrebbe continuare a gestire bene (attività generiche che non desidera peggiorare).

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

Riepilogo degli iperparametri LoRA principali

Per adattare LoRA al Suo caso d'uso, inizi con questi valori predefiniti e ne modifichi uno alla volta. r=8 è un punto di partenza sicuro: aumenti a 16 o 32 se il modello necessita di una maggiore capacità espressiva. lora_alpha = 2*r è una scelta stabile. Un learning rate pari a 2e-4 funziona per la maggior parte dei fine-tuning di istruzioni; lo riduca a 1e-4 se osserva una loss di addestramento instabile. Epoche 1-3: interrompa l'addestramento quando la loss di validazione smette di diminuire.

Quando scegliere invece il fine-tuning di OpenAI

Hugging Face PEFT LoRA richiede una GPU. Se non dispone di un'infrastruttura GPU, l'API di fine-tuning di OpenAI è un'alternativa gestita che si occupa dell'infrastruttura di addestramento al posto Suo. Carichi il file JSONL, chiami l'API per avviare un'esecuzione di addestramento e riceva un ID del modello sottoposto a fine-tuning da utilizzare nelle chiamate API. Il fine-tuning di OpenAI supporta GPT-4o-mini e GPT-3.5-turbo. Ha un costo per token maggiore, ma elimina completamente la gestione dell'infrastruttura.

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, ...)

Verifica rapida

Verifichi la Sua comprensione del fine-tuning LoRA trattato in questa lezione.

Riepilogo della lezione

In questa lezione ha imparato che LoRA riduce di 10-50 volte i requisiti GPU del fine-tuning, addestrando solo piccole matrici adapter a basso rango invece di tutti i parametri del modello; QLoRA (quantizzazione a 4 bit + LoRA) rende il fine-tuning dei modelli 7B accessibile sulle GPU per utenti comuni con circa 12 GB di VRAM; infine, SFTTrainer di TRL fornisce un'API di alto livello che gestisce tokenizzazione, raggruppamento in batch, mascheramento della loss e salvataggio dei checkpoint. Nel prossimo argomento valuteremo e distribuiremo il modello sottoposto a fine-tuning.

Domande Frequenti

La lezione «Fine-tuning LoRA con Hugging Face PEFT» è gratuita?

Sì — il testo completo di «Fine-tuning LoRA con Hugging Face PEFT» è gratuito qui sul web. Per esercitarvi in modo interattivo (un editor di codice integrato e un tutor IA 24/7) e sbloccare il resto del corso AI Engineering Academy, passa a CoddyKit PRO. Il corso AI Engineering Academy include 4 lezioni in totale.

Cosa imparerò in «Fine-tuning LoRA con Hugging Face PEFT»?

Configuri il rank LoRA, alpha e i moduli target, esegua il fine-tuning supervisionato con TRL SFTTrainer, monitori la loss di addestramento e salvi checkpoint uniti e contenenti solo gli adapter. Eserciti AI Engineering Academy con codice pratico che esegui direttamente nel browser, e un tutor IA 24/7 risponde alle tue domande mentre lavori sulla lezione.

Ho bisogno di esperienza per iniziare AI Engineering Academy?

Non è richiesta alcuna esperienza precedente. AI Engineering Academy su CoddyKit è strutturato per principianti e studenti avanzati, quindi puoi iniziare da qui o dall'inizio e procedere al tuo ritmo. Questa è la lezione 3 di 4.

Quanto tempo richiede la lezione «Fine-tuning LoRA con Hugging Face PEFT»?

La maggior parte delle lezioni CoddyKit richiede circa 5–10 minuti. Ogni lezione è breve e interattiva, quindi fai progressi costanti e riprendi esattamente da dove hai lasciato su web e app.

Posso scrivere ed eseguire codice in questa lezione AI Engineering Academy?

Sì. Ogni lezione AI Engineering Academy include un editor di codice integrato, quindi scrivi ed esegui codice reale direttamente nel tuo browser e ricevi feedback istantaneo dall'IA — nessuna configurazione locale necessaria.

Tutte le lezioni di questo corso

  1. Quando il fine-tuning supera il prompting
  2. Preparare un dataset di addestramento di alta qualità
  3. Fine-tuning LoRA con Hugging Face PEFT
  4. Valutare e distribuire il modello sottoposto a fine-tuning
← Torna a AI Engineering Academy