Machine Learning Academy · Lezione

Fine-tuning: scongelamento e learning rate ridotti

Imparerete a scongelare i layer precedenti dopo l’addestramento iniziale della testa, applicare un learning rate più basso per non compromettere le feature pre-addestrate e osservare i miglioramenti dell’accuratezza.

Lezione 3 di 412 passaggi

Fine-tuning: scongelamento e learning rate ridotti è una lezione Machine Learning 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 Machine Learning Academy, e i tuoi progressi si sincronizzano tra il web e l'app CoddyKit. Il corso Machine Learning Academy include 4 lezioni in totale.

Perché fare il fine-tuning dopo l'estrazione delle feature?

L'estrazione delle feature adatta solo la head di classificazione, lasciando congelato il backbone. Il fine-tuning si spinge oltre aggiornando anche alcuni o tutti i livelli del backbone, permettendo al modello di adattare le proprie rappresentazioni alla distribuzione specifica dei dati.

Il fine-tuning è particolarmente vantaggioso quando il dominio differisce da ImageNet, ad esempio nel caso di immagini satellitari, scansioni mediche o fotografie di difetti industriali. Le feature di ImageNet vengono trasferite solo in parte, ma adattarle produce un'accuratezza sensibilmente maggiore. Il rischio è il dimenticamento catastrofico: se si esegue il fine-tuning con un learning rate elevato, i nuovi dati sovrascrivono le feature apprese con cura, causando un crollo delle prestazioni.

La strategia di fine-tuning in due fasi

La procedura standard di fine-tuning prevede due fasi. Fase 1: congeli completamente il backbone e addestri solo la nuova head di classificazione per diverse epoche, finché non converge. In questo modo la head parte da uno stato ragionevole prima che intervengano i gradienti del backbone.

Fase 2: scongeli tutti o alcuni livelli del backbone e continui l'addestramento con un learning rate molto basso (in genere da 10 a 100 volte inferiore rispetto alla fase 1). In questo modo adatti gradualmente le feature apprese al proprio dominio senza distruggerle. Saltare la fase 1 e iniziare subito il fine-tuning con un LR elevato è l'errore più comune e porta a risultati scadenti.

import torchvision.models as models
import torch.nn as nn
import torch.optim as optim

model = models.resnet50(weights=models.ResNet50_Weights.IMAGENET1K_V1)

# --- Phase 1: Freeze backbone, train head ---
for param in model.parameters():
    param.requires_grad = False
model.fc = nn.Linear(model.fc.in_features, num_classes)
optimizer = optim.Adam(model.fc.parameters(), lr=1e-3)
# Train for 5-10 epochs...

# --- Phase 2: Unfreeze and fine-tune with low LR ---
for param in model.parameters():
    param.requires_grad = True
optimizer = optim.Adam(model.parameters(), lr=1e-5)  # 100x lower

Scongelamento selettivo: livello per livello

Anziché scongelare tutto il backbone in una volta, può scongelarlo livello per livello, iniziando dall'alto, cioè dalla parte più vicina all'output. Questo perché i livelli successivi contengono le feature più specifiche per l'attività, mentre quelli iniziali apprendono feature universali (bordi e texture) che raramente devono essere aggiornate.

Per ResNet-50, l'ordine tipico dello scongelamento selettivo è: (1) fc (già addestrabile), (2) layer4, (3) layer3 e infine (4) layer1 e layer2, se necessario. A ogni passaggio deve valutare se lo scongelamento aggiuntivo migliora l'accuratezza di validazione, poiché un numero maggiore di parametri addestrabili aumenta il rischio di overfitting sui dataset piccoli.

import torchvision.models as models
import torch.nn as nn

model = models.resnet50(weights=models.ResNet50_Weights.IMAGENET1K_V1)
# Freeze everything first
for param in model.parameters():
    param.requires_grad = False
model.fc = nn.Linear(model.fc.in_features, num_classes)

# After phase 1 training, selectively unfreeze layer4
for param in model.layer4.parameters():
    param.requires_grad = True

trainable = sum(p.numel() for p in model.parameters() if p.requires_grad)
print(f'Trainable (fc + layer4): {trainable:,}')
# Much fewer parameters to fine-tune than the full 25M

Learning rate differenziali

I learning rate differenziali assegnano learning rate diversi alle varie parti della rete. La nuova head viene addestrata con il rate più alto (1e-3), i livelli del backbone scongelati di recente con un rate intermedio (1e-4) e i livelli iniziali del backbone con il rate più basso (1e-5), oppure vengono lasciati completamente congelati.

L'ottimizzatore di PyTorch accetta un elenco di gruppi di parametri, ciascuno con il proprio lr. Questa è la tecnica standard nella letteratura sul transfer learning ed è usata nei learning rate discriminativi di fast.ai. L'idea è che le parti della rete con feature più generiche richiedano meno modifiche rispetto ai livelli specifici per l'attività.

import torch.optim as optim
import torchvision.models as models

model = models.resnet50(weights=models.ResNet50_Weights.IMAGENET1K_V1)
model.fc = nn.Linear(model.fc.in_features, num_classes)

# Unfreeze everything
for p in model.parameters():
    p.requires_grad = True

# Differential learning rates per layer group
optimizer = optim.Adam([
    {'params': model.fc.parameters(),     'lr': 1e-3},   # Head: highest LR
    {'params': model.layer4.parameters(), 'lr': 1e-4},   # Top block
    {'params': model.layer3.parameters(), 'lr': 5e-5},   # Middle
    {'params': list(model.layer1.parameters()) +
               list(model.layer2.parameters()), 'lr': 1e-5}  # Early layers
])

Monitorare il dimenticamento catastrofico

Il dimenticamento catastrofico si verifica quando il fine-tuning con un learning rate elevato sovrascrive le feature apprese in precedenza, facendo scendere l'accuratezza di validazione al di sotto persino del valore di riferimento ottenuto con l'estrazione delle feature. Lo si nota come un forte picco nella loss di training all'inizio della fase 2, seguito da un recupero lento.

Per prevenirlo: (1) inizi sempre la fase 2 con un learning rate molto basso, (2) monitori l'accuratezza di validazione a ogni epoca e si fermi immediatamente se scende al di sotto del valore migliore della fase 1, (3) usi uno scheduler del learning rate che aumenti gradualmente il LR del backbone e (4) salvi il checkpoint del modello migliore durante la fase 1, così da poterlo ripristinare se la fase 2 non dà buoni risultati.

import torch

best_val_acc = 0.0
best_state = None

for epoch in range(20):
    train_one_epoch(model, train_loader, optimizer, criterion, device)
    val_acc = evaluate(model, val_loader, device)
    
    if val_acc > best_val_acc:
        best_val_acc = val_acc
        best_state = {k: v.clone() for k, v in model.state_dict().items()}
    
    # Stop if validation drops more than 2% below best
    if val_acc < best_val_acc - 0.02:
        print('Early stop: possible catastrophic forgetting')
        model.load_state_dict(best_state)  # Restore best
        break

Scheduler del learning rate per il fine-tuning

Un learning rate fisso è raramente ottimale per tutta la durata del fine-tuning. Gli scheduler del learning rate regolano il LR durante l'addestramento per migliorare la convergenza. Per il fine-tuning, due scheduler funzionano particolarmente bene: CosineAnnealingLR riduce gradualmente il LR dal valore iniziale fino a quasi zero seguendo una curva cosinusoidale, mentre ReduceLROnPlateau riduce il LR ogni volta che la metrica di validazione smette di migliorare.

ReduceLROnPlateau con patience=2 è un'impostazione predefinita sicura: se la loss di validazione non migliora per 2 epoche consecutive, il LR viene moltiplicato per factor=0.1. Questo permette spesso di riprendere i progressi quando l'addestramento si stabilizza.

import torch.optim as optim
from torch.optim.lr_scheduler import ReduceLROnPlateau, CosineAnnealingLR

optimizer = optim.Adam(model.parameters(), lr=1e-4)

# Option 1: Reduce on plateau
scheduler = ReduceLROnPlateau(optimizer, mode='max', factor=0.1,
                               patience=2, verbose=True)
# Call after each validation: scheduler.step(val_acc)

# Option 2: Cosine annealing over T_max epochs
scheduler = CosineAnnealingLR(optimizer, T_max=20, eta_min=1e-7)
# Call after each epoch: scheduler.step()

print('Initial LR:', optimizer.param_groups[0]['lr'])

Dimensione del batch e regolarizzazione durante il fine-tuning

Il fine-tuning con una dimensione del batch maggiore riduce il rumore dei gradienti, un aspetto vantaggioso quando si apportano piccole modifiche alle feature apprese in precedenza. Tuttavia, batch molto grandi possono peggiorare la generalizzazione durante il fine-tuning (il problema dei «minimi appuntiti»). Una dimensione del batch di 32-64 è un buon punto di partenza.

Aggiunga il weight decay (regolarizzazione L2) all'ottimizzatore: optim.Adam(model.parameters(), lr=1e-4, weight_decay=1e-4). Il weight decay impedisce che un singolo peso cresca eccessivamente, un aspetto particolarmente importante nel fine-tuning, poiché i pesi pre-addestrati hanno già magnitudini sensate che non vogliamo perturbare in modo troppo aggressivo.

import torch.optim as optim

# Fine-tuning optimiser with weight decay
optimizer = optim.AdamW(
    model.parameters(),
    lr=1e-4,
    weight_decay=1e-4   # L2 regularisation
)

# AdamW separates weight decay from gradient adaptation
# (standard Adam incorrectly applies decay to adaptive gradient scaling)
# AdamW is preferred for fine-tuning transformer models especially

print('Optimizer:', optimizer)

Aumento dei dati durante il fine-tuning

Il data augmentation applica trasformazioni casuali alle immagini di training a ogni epoca, moltiplicando di fatto le dimensioni del dataset. Questo è particolarmente importante durante il fine-tuning quando si hanno poche immagini per classe. Aumenti comuni per le immagini naturali: capovolgimento orizzontale casuale, rotazione casuale (±15°), ritaglio casuale e variazione casuale di colore (luminosità, contrasto, saturazione).

Applichi l'aumento dei dati solo durante il training, non durante la validazione o l'inferenza. Usi una trasformazione separata per il set di validazione, che applichi solo una pre-elaborazione deterministica (ridimensionamento, ritaglio al centro e normalizzazione). transforms.Compose di PyTorch semplifica questa gestione.

from torchvision import transforms

train_transform = transforms.Compose([
    transforms.RandomResizedCrop(224, scale=(0.7, 1.0)),
    transforms.RandomHorizontalFlip(p=0.5),
    transforms.ColorJitter(brightness=0.2, contrast=0.2, saturation=0.2),
    transforms.RandomRotation(degrees=15),
    transforms.ToTensor(),
    transforms.Normalize(mean=[0.485, 0.456, 0.406],
                         std=[0.229, 0.224, 0.225])
])

val_transform = transforms.Compose([
    transforms.Resize(256),
    transforms.CenterCrop(224),
    transforms.ToTensor(),
    transforms.Normalize(mean=[0.485, 0.456, 0.406],
                         std=[0.229, 0.224, 0.225])
])

Risultati pratici del fine-tuning

Quanto migliora il fine-tuning rispetto all'estrazione delle caratteristiche? In un tipico dataset personalizzato con circa 1000 immagini per classe, l'estrazione delle caratteristiche può raggiungere un'accuratezza dell'88%, mentre il fine-tuning può arrivare al 92-95%. Il miglioramento dipende in larga misura da quanto i Suoi dati differiscono da ImageNet.

Per capire cosa sta succedendo, tenga traccia di questi quattro valori: accuratezza di validazione della fase 1 (baseline dell'estrazione delle caratteristiche), accuratezza di validazione iniziale della fase 2 (non dovrebbe scendere al di sotto di quella della fase 1 se il learning rate è corretto), migliore accuratezza di validazione della fase 2 (vantaggio del fine-tuning) e accuratezza sul test (valore finale, riportato una sola volta alla fine per evitare il data leakage del set di test).

# Example fine-tuning progression
results = {
    'Phase 1 (frozen backbone, 10 epochs)': '88.2%',
    'Phase 2 initial (unfroze layer4, epoch 1)': '88.5%',
    'Phase 2 best (epoch 15)': '92.7%',
    'Phase 2 (unfroze layer3 too, epoch 20)': '93.1%',
    'Final test accuracy': '92.8%',  # Reported only at the end
}
for stage, acc in results.items():
    print(f'{stage}: {acc}')

# Key takeaway: fine-tuning added ~4.5% over feature extraction

Fine-tuning dei backbone ViT rispetto a quelli CNN

Il fine-tuning dei modelli ViT (Vision Transformer) richiede qualche attenzione aggiuntiva rispetto alle CNN. I modelli ViT contengono la Layer Normalisation anziché la Batch Normalisation, quindi le modalità model.eval() / model.train() influiscono principalmente sul dropout e non sulle statistiche di normalizzazione: questo rende il fine-tuning leggermente più semplice.

I modelli ViT traggono inoltre vantaggio da un learning rate di picco più basso durante il fine-tuning (da 1e-5 a 5e-5, rispetto a 1e-4 per le CNN), perché i pesi dell'attenzione sono sensibili agli aggiornamenti ampi. Per il fine-tuning dei ViT si preferisce nettamente l'ottimizzatore AdamW. Con un fine-tuning accurato, ViT-B/16 può superare le baseline CNN in molti compiti.

import torchvision.models as models
import torch.nn as nn
import torch.optim as optim

# Fine-tuning ViT-B/16
vit = models.vit_b_16(weights=models.ViT_B_16_Weights.IMAGENET1K_V1)
vit.heads = nn.Linear(768, num_classes)

# Very low LR for ViT fine-tuning
optimizer = optim.AdamW(
    vit.parameters(),
    lr=2e-5,
    weight_decay=0.01
)

# Warm-up scheduler (common for transformers)
from torch.optim.lr_scheduler import LinearLR, CosineAnnealingLR, SequentialLR
warmup = LinearLR(optimizer, start_factor=0.01, total_iters=5)
cosine = CosineAnnealingLR(optimizer, T_max=25)
scheduler = SequentialLR(optimizer, schedulers=[warmup, cosine], milestones=[5])

Verifica rapida

Verifichi la Sua comprensione delle strategie di fine-tuning illustrate in questa lezione.

Riepilogo della lezione

In questa lezione ha imparato che il fine-tuning in due fasi addestra prima la testa (con il backbone congelato), poi scongela il backbone usando un learning rate molto basso per evitare il catastrophic forgetting; i learning rate differenziali assegnano valori più alti alla testa e più bassi ai livelli più profondi del backbone; infine, lo scongelamento selettivo procedendo dai livelli superiori verso quelli inferiori offre il miglior compromesso tra accuratezza ed efficienza. Nel prossimo capitolo applicheremo queste tecniche a una sfida reale di imaging medico con poche etichette.

Gratis per iniziare

Impara Python con un tutor IA — gratis

Scrivi ed esegui vero codice nel tuo browser, ricevi aiuto istantaneo da un tutor IA disponibile 24/7, e riprendi da dove hai lasciato sul web o nell'app.

Corsi
30
Lezioni
120

Domande Frequenti

La lezione «Fine-tuning: scongelamento e learning rate ridotti» è gratuita?

Sì — il testo completo di «Fine-tuning: scongelamento e learning rate ridotti» è 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 Machine Learning Academy, passa a CoddyKit PRO. Il corso Machine Learning Academy include 4 lezioni in totale.

Cosa imparerò in «Fine-tuning: scongelamento e learning rate ridotti»?

Imparerete a scongelare i layer precedenti dopo l’addestramento iniziale della testa, applicare un learning rate più basso per non compromettere le feature pre-addestrate e osservare i miglioramenti… Eserciti Machine Learning 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 Machine Learning Academy?

Non è richiesta alcuna esperienza precedente. Machine Learning 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: scongelamento e learning rate ridotti»?

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 Machine Learning Academy?

Sì. Ogni lezione Machine Learning 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. Modelli pre-addestrati in torchvision: ResNet, EfficientNet e ViT
  2. Estrazione delle feature: congelare il backbone
  3. Fine-tuning: scongelamento e learning rate ridotti
  4. Adattamento al dominio: immagini mediche con poche etichette
← Torna a Machine Learning Academy