Mixed Precision
L'addestramento a precisione mista usa calcoli a 16 bit e pesi master FP32 per le reti neurali, riducendo l'uso di memoria e i tempi di addestramento. Panoramica di loss scaling, BF16 e AMP.
La precisione mista è una tecnica di addestramento che esegue la maggior parte dei calcoli di una rete neurale in un formato a virgola mobile a 16 bit, FP16 (precisione a 16 bit o BF16, mantenendo una copia principale a 32 bit (FP32) dei pesi del modello ed eseguendo in FP32 le operazioni numericamente sensibili. I valori a 16 bit richiedono metà della memoria e della larghezza di banda rispetto a FP32, e i Tensor Core delle GPU moderne li moltiplicano molto più rapidamente; la precisione mista riduce quindi i tempi di addestramento e permette di usare batch size più grandi in memoria, mantenendo la stessa precisione di FP32. La ricetta descritta da Micikevicius et al. nel 2018, con pesi principali FP32, calcoli FP16 e ridimensionamento della perdita, è oggi automatizzata dai framework di deep learning con la precisione mista automatica (AMP).
Come funziona la precisione mista#
Tre elementi permettono alla retropropagazione e agli aggiornamenti dei pesi durante l’addestramento a 16 bit di mantenere la stessa precisione dell’addestramento FP32:
- Pesi principali FP32: L’ottimizzatore applica ogni aggiornamento a una copia FP32 dei pesi. Un aggiornamento minimo applicato direttamente a un peso FP16 spesso verrebbe arrotondato e perso, perché FP16 conserva solo circa tre cifre decimali significative.
- Precisione per operazione: Un contesto autocast esegue moltiplicazioni di matrici e convoluzioni a 16 bit, dove i Tensor Core offrono il maggiore aumento di velocità, e mantiene riduzioni, softmax e perdita in FP32.
- Ridimensionamento della perdita: FP16 non può rappresentare valori inferiori a circa 6 × 10⁻⁸, quindi molti gradienti piccoli andrebbero in underflow fino a zero. Moltiplicare la perdita per un fattore di scala prima della retropropagazione porta ogni gradiente nell’intervallo rappresentabile da FP16; prima dell’aggiornamento dei pesi, i gradienti vengono divisi per lo stesso fattore.
In PyTorch AMP, torch.autocast gestisce la conversione della precisione per operazione e GradScaler gestisce il ridimensionamento della perdita:
import torch
from torch import nn
# Use FP16 on an NVIDIA GPU and BF16 on CPU
device = "cuda" if torch.cuda.is_available() else "cpu"
dtype = torch.float16 if device == "cuda" else torch.bfloat16
model = nn.Linear(512, 10).to(device) # master weights stay in FP32
optimizer = torch.optim.SGD(model.parameters(), lr=0.01)
scaler = torch.amp.GradScaler(device, enabled=dtype == torch.float16) # loss scaling for FP16 only
x = torch.randn(64, 512, device=device)
with torch.autocast(device_type=device, dtype=dtype):
out = model(x) # the matrix multiply runs in 16-bit
loss = out.float().pow(2).mean()
scaler.scale(loss).backward()
scaler.step(optimizer)
scaler.update()
print(f"Output: {out.dtype}, weights: {model.weight.dtype}, gradients: {model.weight.grad.dtype}")Su CPU, lo snippet stampa torch.bfloat16 per l’output del livello e torch.float32 sia per i pesi sia per i relativi gradienti; su una GPU NVIDIA l’output è torch.float16. I calcoli a 16 bit e i pesi principali FP32 coesistono nello stesso passaggio di addestramento.
GradScaler di PyTorch regola dinamicamente il fattore di scala. Parte da 65,536, dimezza il fattore e salta il passaggio ogni volta che i gradienti ridimensionati contengono valori inf o NaN, quindi raddoppia il fattore dopo 2,000 passaggi consecutivi senza overflow; in questo modo il fattore si stabilizza appena sotto il punto in cui i gradienti vanno in overflow.
FP16, BF16 e precisioni inferiori#
FP16 usa 5 bit per l’esponente e 10 per la mantissa, quindi è preciso ma va in overflow sopra 65,504. BF16 conserva gli 8 bit dell’esponente di FP32, coprendo lo stesso intervallo fino a circa 3.4 × 10³⁸, ma ha solo 7 bit di mantissa; non richiede quindi il ridimensionamento della perdita, a costo di un arrotondamento più grossolano. BF16 è disponibile sulle GPU NVIDIA dalla generazione Ampere in poi, sulle TPU di Google e su molte CPU recenti; è il formato di addestramento consueto per i modelli linguistici di grandi dimensioni. La stessa idea si estende a FP8 sulle GPU Hopper e Blackwell tramite Transformer Engine di NVIDIA, e ai formati FP4 come NVFP4 su Blackwell.
La precisione mista nella pratica#
Il principale rischio pratico di AMP è un modello o una GPU che producono perdite NaN a 16 bit. L’addestramento di Ultralytics YOLO26 abilita AMP per impostazione predefinita con amp=True, confrontando prima le previsioni FP32 e AMP di un modello YOLO26n su GPU CUDA e tornando a FP32 se differiscono o se la GPU appartiene a una serie nota per produrre perdite NaN con AMP, come la serie GTX 16. Impostando amp="bf16" si passa all’autocast BF16, disattivando il ridimensionamento della perdita.
La precisione mista è ampiamente usata quando l’addestramento è limitato dalla memoria o dal tempo. I team che sviluppano veicoli autonomi addestrano modelli di rilevamento degli oggetti su milioni di fotogrammi ad alta risoluzione, dove dimezzare la memoria delle attivazioni consente di usare immagini e batch più grandi. Nell’analisi di immagini mediche, i volumi CT e MRI 3D spesso superano la memoria della GPU in FP32, mentre le attivazioni a 16 bit rendono possibile addestrare modelli di segmentazione su volumi interi.
Precisione mista e concetti correlati#
- Quantizzazione dei modelli: La quantizzazione converte un modello addestrato in numeri interi a bassa precisione, come INT8, per accelerare l’inferenza; la precisione mista accelera l’addestramento e mantiene i pesi in virgola mobile.
- Addestramento consapevole della quantizzazione: QAT simula l’arrotondamento a valori interi durante l’addestramento, così il modello mantiene la propria precisione dopo la distribuzione INT8.
- Precisione a 16 bit: Il termine precisione a 16 bit indica il formato numerico FP16, mentre la precisione mista è la tecnica di addestramento che lo combina con FP32. L’addestramento interamente in FP16, senza pesi principali FP32 né ridimensionamento della perdita, perde gli aggiornamenti piccoli a causa dell’arrotondamento e i gradienti piccoli a causa dell’underflow, e spesso non converge.










