BFloat16 (BF16)
BFloat16 (BF16) è un numero in virgola mobile a 16 bit con l'esponente a 8 bit di FP32 e una frazione a 7 bit, che mantiene l'intervallo di FP32 usando metà della memoria. Differenze rispetto a FP16.
BFloat16, ovvero il formato a virgola mobile cerebrale (BF16), è un formato a virgola mobile a 16 bit con 1 bit di segno, 8 bit di esponente e 7 bit di frazione. Mantiene la stessa ampiezza dell'esponente del formato standard a virgola mobile a 32 bit (FP32), quindi copre lo stesso intervallo di valori, da circa 1,2 × 10⁻³⁸ a 3,4 × 10³⁸, memorizzando ogni numero utilizzando metà della memoria. Il compromesso riguarda la precisione: 7 bit di frazione consentono solo due o tre cifre decimali significative. L'apprendimento profondo tollera bene questo arrotondamento e il formato, concepito da Google Brain per le TPU di Google, è diventato il formato a 16 bit predefinito per addestrare modelli di grandi dimensioni su GPU moderne e acceleratori.
Come BF16 rappresenta i numeri#
Un valore BF16 corrisponde esattamente alla metà superiore di un valore FP32: stesso segno ed esponente, ma la frazione passa da 23 a 7 bit. Convertire da FP32 a BF16 significa quindi arrotondare via solo i 16 bit inferiori; la conversione inversa aggiunge degli zeri, rendendo il formato economico da implementare nell'hardware. L'altro formato comune a 16 bit, IEEE FP16 o precisione dimezzata, distribuisce i bit in modo diverso, con 5 bit per l'esponente e 10 per la frazione: offre più precisione a scapito dell'intervallo. Il confronto tra i tre formati sugli stessi valori mostra il compromesso:
import torch
# Largest value, smallest normal value, and spacing just above 1.0 for each format
for dtype in [torch.float32, torch.float16, torch.bfloat16]:
info = torch.finfo(dtype)
print(f"{dtype!s:15} max={info.max:.3g} tiny={info.tiny:.3g} eps={info.eps:.3g}")
# A large activation, a tiny gradient, and a value that needs fine precision
x = torch.tensor([70000.0, 1e-8, 1.001953125])
print("FP16:", x.half().tolist())
print("BF16:", x.bfloat16().tolist())BF16 raggiunge un massimo di 3.39e38 contro i 65,504 di FP16, e il suo valore normale minimo, 1.18e-38, corrisponde a quello di FP32. FP16 converte 70,000 in infinito e 1e-8 in zero, mentre BF16 mantiene entrambi come 70,144 e circa 1.0e-8. L'intervallo più fine di FP16 (eps 0.000977 contro 0.00781 di BF16) conserva esattamente 1.001953125, mentre BF16 lo arrotonda a 1.0.
BF16 e FP16 a confronto per l'addestramento#
Durante l'addestramento, l'intervallo dei valori conta più della precisione. I gradienti spesso scendono al di sotto dei valori più piccoli rappresentabili in FP16 e si annullano, quindi l'addestramento FP16 a precisione mista moltiplica la loss per un fattore di scala prima della retropropagazione e riduce quel fattore ogni volta che i gradienti vanno in overflow, una tecnica chiamata scaling della loss. BF16 non richiede lo scaling della loss, perché il suo intervallo coincide con quello di FP32, e l'addestramento BF16 produce risultati in linea con quelli di FP32 nei modelli di visione artificiale, riconoscimento vocale, elaborazione del linguaggio e raccomandazione con lo stesso numero di iterazioni, senza modificare gli iperparametri. L'addestramento a precisione mista mantiene una copia principale dei pesi in FP32 ed esegue moltiplicazioni tra matrici in BF16 con accumulo in FP32, perché sommare in BF16 molti prodotti con una frazione a 7 bit comporterebbe una perdita eccessiva di precisione. La scelta tra i due formati a 16 bit dipende soprattutto dall'hardware:
PyTorch applica BF16 tramite la precisione mista automatica con torch.autocast(device_type="cuda", dtype=torch.bfloat16). Ultralytics YOLO offre la stessa opzione per l'addestramento con amp="bf16": esegue il forward pass in autocast BF16 e disattiva lo scaler dei gradienti usato dalla modalità FP16 predefinita, sulle GPU CUDA con supporto BF16 nativo.
Supporto hardware#
Le TPU Cloud di seconda e terza generazione di Google eseguono moltiplicazioni in BF16 nelle proprie unità di matrice e accumulano i risultati in FP32; anche le generazioni successive di TPU hanno mantenuto il formato. Le GPU NVIDIA eseguono BF16 sui Tensor Core a partire dalla generazione Ampere, incluse A100, H100 e Blackwell; i processori Intel Xeon con estensioni Advanced Matrix Extensions (AMX) lo accelerano sulla CPU. Su una NVIDIA A100, i calcoli BF16 e FP16 sui Tensor Core raggiungono entrambi un picco di 312 teraFLOPS, 16 volte i 19,5 teraFLOPS dei calcoli FP32 standard della GPU.
BF16 e formati correlati#
- Precisione dimezzata (FP16): formato IEEE a 16 bit, con maggiore precisione e un intervallo molto più ridotto. È ancora comune nell'inferenza, dove le attivazioni rientrano nel suo intervallo e la precisione aggiuntiva è utile.
- TF32: modalità di calcolo dei Tensor Core NVIDIA con l'esponente a 8 bit di BF16 e la frazione a 10 bit di FP16, che le librerie possono usare per le moltiplicazioni di matrici FP32 sulle GPU Ampere e successive, mantenendo i tensori in memoria a 32 bit.
- FP8: formati a virgola mobile a 8 bit, come E4M3 ed E5M2, supportati dalle GPU Hopper e Blackwell, che dimezzano ancora la memoria necessaria per addestrare e usare in inferenza i modelli più grandi.
- Quantizzazione del modello: converte i pesi addestrati in formati interi come INT8 o INT4 per la distribuzione.
BF16 nei modelli linguistici di grandi dimensioni#
La maggior parte dei modelli linguistici di grandi dimensioni (LLM) a pesi aperti viene addestrata con precisione mista BF16 e pubblicata con pesi BF16, che fungono da riferimento per valutare le versioni quantizzate FP8 e INT4. Con 2 byte per parametro, i pesi di un modello da 70 miliardi di parametri occupano 140 GB in BF16, più degli 80 GB di una singola NVIDIA H100; perciò, servire un modello di questo tipo in BF16 richiede di distribuirlo su almeno due GPU.










