Model Soups
Le model soup calcolano la media dei pesi di modelli sottoposti a fine-tuning a partire dallo stesso modello preaddestrato con iperparametri diversi, aumentando la precisione senza costi aggiuntivi in inferenza.
Un ensemble di modelli è un singolo modello ottenuto facendo la media dei pesi di diversi modelli sottoposti a fine-tuning a partire dallo stesso modello preaddestrato, ma con iperparametri diversi. Dopo una ricerca degli iperparametri, di solito si conserva l'esecuzione con il miglior punteggio di convalida e si scartano le altre. Un ensemble riutilizza queste esecuzioni scartate: poiché ognuna è partita dagli stessi pesi del modello preaddestrato, i relativi parametri sono ancora allineati e la loro media spesso ottiene un punteggio superiore a ciascuno di essi, pur avendo esattamente lo stesso costo di esecuzione di un singolo modello. Mitchell Wortsman e colleghi dell'Università di Washington, Google, Meta AI, Università di Tel Aviv e Columbia hanno introdotto il metodo in Model soups: averaging weights of multiple fine-tuned models improves accuracy without increasing inference time, pubblicato a ICML 2022. Un ensemble greedy di 58 modelli ViT-G/14 sottoposti a fine-tuning e preaddestrati sul dataset JFT-3B di Google ha raggiunto il 90,94% di accuratezza top-1 su ImageNet, un risultato allo stato dell'arte all'epoca.
Ensemble uniformi e greedy#
Il fine-tuning sposta una grande rete preaddestrata solo di poco nello spazio dei pesi, quindi i modelli sottoposti a fine-tuning con tassi di apprendimento, aumenti dei dati, smussamento delle etichette o durate di addestramento diversi tendono a collocarsi nella stessa ampia regione di bassa loss. Farne la media porta al centro di quella regione, che generalizza meglio dei suoi margini. L'articolo definisce due ricette principali:
- Ensemble uniforme: Calcola la media di tutti i modelli sottoposti a fine-tuning. Funziona quando tutti i componenti sono validi, ma un'esecuzione con un tasso di apprendimento molto alto può uscire dalla regione condivisa, e includerla nella media riduce l'accuratezza.
- Ensemble greedy: Ordina i modelli in base all'accuratezza sui dati di convalida tenuti da parte, parte dal migliore e aggiunge ciascun modello successivo solo se l'accuratezza di convalida dell'ensemble risultante non diminuisce. Poiché parte dal modello migliore, un ensemble greedy non è mai peggiore di quel modello sul set di convalida.
Per CLIP ViT-B/32 sottoposto a fine-tuning su ImageNet con 72 configurazioni casuali di iperparametri, il miglior modello singolo ha ottenuto l'80,38%. La media uniforme di tutti e 72 i modelli ha ottenuto il 79,97%, perché alcune esecuzioni si erano allontanate dalla regione condivisa a bassa perdita, mentre la combinazione greedy ha mantenuto cinque modelli e ha ottenuto l'81,03%. Su cinque set di test con variazione della distribuzione, come gli schizzi e le rappresentazioni di ImageNet, la combinazione greedy ha registrato una media del 50,75%, contro il 47,83% del miglior modello singolo. L'articolo riporta inoltre miglioramenti per ALIGN e miglioramenti più contenuti quando BERT e T5 vengono sottoposti a fine-tuning per la classificazione del testo.
Questo esempio PyTorch preaddestra un piccolo classificatore su cifre scritte a mano di 8x8 pixel, sottopone nove copie a fine-tuning con tassi di apprendimento e aumenti del rumore in input diversi, quindi confronta il loro ensemble uniforme con un ensemble di tutti e nove i modelli:
import copy
import torch
from sklearn.datasets import load_digits
from torch import nn
torch.manual_seed(0)
X, y = load_digits(return_X_y=True)
order = torch.randperm(len(X))
X, y = torch.tensor(X / 16, dtype=torch.float32)[order], torch.tensor(y)[order]
def train(model, first, last, lr, noise, seed):
"""Train a copy of the model on random batches from rows first to last, adding input noise."""
model, generator = copy.deepcopy(model), torch.Generator().manual_seed(seed)
optimizer = torch.optim.SGD(model.parameters(), lr=lr, momentum=0.9)
for _ in range(300):
rows = torch.randint(first, last, (32,), generator=generator)
optimizer.zero_grad()
inputs = X[rows] + noise * torch.randn(32, 64, generator=generator)
nn.functional.cross_entropy(model(inputs), y[rows]).backward()
optimizer.step()
return model
def accuracy(logits):
"""Return the fraction of the 997 test digits classified correctly."""
return (logits.argmax(1) == y[800:]).float().mean().item()
base = train(nn.Sequential(nn.Linear(64, 256), nn.ReLU(), nn.Linear(256, 10)), 0, 300, 0.05, 0, 0) # pretrained
sweep = [(lr, noise) for lr in (0.01, 0.03, 0.1) for noise in (0, 0.2, 0.4)] # 9 hyperparameter settings
ingredients = [train(base, 300, 500, lr, noise, seed) for seed, (lr, noise) in enumerate(sweep)]
# Uniform soup: average every weight tensor of the nine fine-tuned models
soup = copy.deepcopy(base)
soup.load_state_dict({k: sum(m.state_dict()[k] for m in ingredients) / 9 for k in soup.state_dict()})
with torch.no_grad():
singles = [accuracy(m(X[800:])) for m in ingredients]
print(f"Fine-tuned models: {min(singles):.3f} to {max(singles):.3f}, mean {sum(singles) / 9:.3f}")
print(f"Uniform soup: {accuracy(soup(X[800:])):.3f}")
print(f"Ensemble of 9: {accuracy(sum(m(X[800:]).softmax(1) for m in ingredients)):.3f}")I nove modelli sottoposti a fine-tuning ottengono punteggi compresi tra 0.881 e 0.936 sulle 997 cifre tenute da parte, con una media di 0.917. Il loro ensemble uniforme ottiene 0.934, un risultato vicino a quello della migliore singola esecuzione e leggermente superiore a 0.930, il punteggio dell'ensemble, che richiede nove passaggi in avanti per immagine, mentre all'ensemble uniforme ne basta uno.
Gli ensemble nella pratica#
Gli ensemble sono particolarmente utili quando si esegue comunque una ricerca, per esempio quando si applica il transfer learning a un vision transformer (ViT) o a un altro modello fondazionale per un nuovo task di classificazione delle immagini. La regolazione degli iperparametri di Ultralytics YOLO è un esempio di questa ricerca: model.tune() addestra un modello per ogni insieme di iperparametri modificati e, per risparmiare spazio di archiviazione, elimina le esecuzioni che non sono le migliori in quel momento, conservando solo l'esecuzione e i pesi della migliore iterazione: il passaggio di selezione del migliore che un ensemble sostituisce.
I componenti devono condividere un'architettura e un punto di partenza preaddestrato, perché le reti addestrate da inizializzazioni casuali diverse memorizzano le stesse caratteristiche in unità nascoste differenti e farne la media distrugge l'accuratezza. I pesi finali non hanno costi aggiuntivi in inferenza, quindi l'ensemble mantiene la latenza di inferenza e la memoria di un singolo modello.
Ensemble di modelli e metodi correlati#
- Ensemble di modelli: Calcola la media delle previsioni di diversi modelli, quindi il costo di inferenza cresce con ogni componente. Nella ricerca con CLIP ViT-B/32, un ensemble di logit ha ottenuto l'81,19% su ImageNet, leggermente più dell'81,03% dell'ensemble greedy, ma eseguendo ogni modello componente per ogni immagine.
- Fusione di modelli: La famiglia più ampia di metodi che combinano i pesi. Gli ensemble sono il caso in cui tutti i modelli sono stati sottoposti a fine-tuning sullo stesso task, mentre l'aritmetica dei task aggiunge modelli sottoposti a fine-tuning su task diversi.
- Media dei pesi all'interno di una singola esecuzione: La media stocastica dei pesi (SWA) e la media mobile esponenziale (EMA) dei pesi calcolano la media dei checkpoint lungo una singola traiettoria di addestramento. Gli ensemble calcolano la media di esecuzioni indipendenti; l'articolo ha rilevato che combinare modelli che già usano EMA o SWA migliora comunque l'accuratezza, quindi i due tipi di media si sommano.
- WiSE-FT: Interpola tra un modello zero-shot e la sua versione sottoposta a fine-tuning, creando un ensemble di due componenti che conserva meglio la robustezza del modello preaddestrato alle variazioni della distribuzione.










