Half-Precision
La demi-précision (FP16) stocke les nombres sur 16 bits au lieu des 32 bits de FP32, réduisant de moitié la mémoire utilisée et accélérant les calculs GPU. Découvre ses limites, la mise à l’échelle de la perte et l’inférence FP16.
La demi-précision, également appelée FP16, float16 ou IEEE 754 binary16, est un format à virgule flottante de 16 bits composé de 1 bit de signe, 5 bits d’exposant et 10 bits de fraction. Par rapport à la simple précision standard de 32 bits (FP32), elle réduit de moitié la mémoire et la bande passante nécessaires pour chaque nombre, et les GPU dotés de Tensor Cores effectuent les calculs matriciels FP16 bien plus rapidement qu’en FP32. En contrepartie, sa plage est étroite : la valeur maximale est de 65 504 et elle offre environ trois chiffres significatifs. Les réseaux neuronaux tolèrent bien cet arrondi, ce qui fait du FP16 la précision standard pour l’inférence sur GPU et, combiné au FP32 dans la précision mixte, un moyen courant d’accélérer l’entraînement.
Comparaison entre FP16 et FP32#
Le FP32 utilise 8 bits pour l’exposant et 23 pour la fraction ; sa plage s’étend donc approximativement de 10⁻³⁸ à 10³⁸, avec environ sept chiffres significatifs. Les 5 bits d’exposant du FP16 limitent les valeurs normales à l’intervalle compris entre 6,1 × 10⁻⁵ et 65 504, tandis que les valeurs subnormales plus petites peuvent descendre jusqu’à environ 6 × 10⁻⁸, avec une précision réduite. Toute valeur supérieure déborde vers l’infini et toute valeur inférieure est arrondie à zéro. La conversion d’un modèle illustre à la fois les avantages et les limites :
import torch
import torchvision
# ResNet-18 stores its weights in FP32 by default
model = torchvision.models.resnet18()
fp32_mb = sum(p.numel() * p.element_size() for p in model.parameters()) / 1e6
# Casting to FP16 stores every weight in 2 bytes instead of 4
model.half()
fp16_mb = sum(p.numel() * p.element_size() for p in model.parameters()) / 1e6
print(f"Weights: {fp32_mb:.1f} MB in FP32, {fp16_mb:.1f} MB in FP16")
# FP16 overflows above 65,504 and rounds to about 3 significant digits
x = torch.tensor([65504.0, 70000.0, 0.1])
print("FP16 values:", x.half().tolist())Les poids passent de 46,8 Mo à 23,4 Mo. Sur la deuxième ligne, 65 504 reste la valeur FP16 maximale, 70 000 devient inf, et 0,1 est stocké sous la forme 0.0999755859375, la valeur représentable en FP16 la plus proche.
La demi-précision pendant l’entraînement#
Un entraînement entièrement en FP16 entraîne une perte de précision ou diverge avec de nombreux modèles, car les petits gradients passent à zéro par sous-dépassement et les mises à jour de poids minuscules par rapport aux poids sont éliminées par arrondi. L’entraînement en précision mixte résout ces deux problèmes : il conserve une copie maîtresse des poids en FP32 pour les mises à jour et exécute les passes avant et arrière en FP16. Il multiplie aussi la perte par un facteur d’échelle afin que les gradients restent dans la plage FP16 avant leur remise à l’échelle en FP32. Les frameworks ajustent automatiquement ce facteur, comme le fait GradScaler de PyTorch :
Les activations FP16 réduisent également de moitié la mémoire occupée par un lot pendant l’entraînement ; le même GPU peut donc traiter des lots plus grands ou des entrées de résolution supérieure. Sur le matériel compatible, le bfloat16 (BF16) est souvent préféré pour l’entraînement, car son exposant de taille FP32 évite d’avoir à mettre la perte à l’échelle.
La demi-précision pour l’inférence#
L’inférence ne nécessite pas de gradients, et les activations de la plupart des modèles de vision restent largement dans la plage FP16. La conversion d’un modèle entraîné en FP16 préserve donc généralement sa précision avec un faible écart par rapport au FP32, sans les données d’étalonnage requises par la quantification de modèle INT8. Les moteurs GPU comme TensorRT génèrent des moteurs FP16 qui s’exécutent sur les Tensor Cores, et les GPU embarqués comme NVIDIA Jetson s’appuient sur cette précision pour l’inférence en temps réel. Les CPU en tirent moins parti, car beaucoup ne disposent pas d’une arithmétique FP16 rapide. Par défaut, Ultralytics YOLO s’entraîne sur GPU avec la précision mixte FP16 (amp=True) et utilise quantize=16 pour la prédiction et l’export en FP16, par exemple avec YOLO26 :
from ultralytics import YOLO
# Charger le plus petit modèle de détection YOLO26
model = YOLO("yolo26n.pt")
# Effectue une prédiction en FP16 sur un GPU ; sur un CPU, les modèles PyTorch s’exécutent en FP32
results = model.predict("https://ultralytics.com/images/bus.jpg", quantize=16)
# Exporte un modèle ONNX dont les poids sont stockés en FP16
path = model.export(format="onnx", quantize=16)Le fichier exporté stocke chaque poids sur 2 octets au lieu de 4 ; le même argument quantize=16 produit des modèles FP16 pour TensorRT, OpenVINO, CoreML et d’autres formats d’exportation.
La demi-précision et les formats associés#
- BFloat16 (BF16) : l’autre format flottant 16 bits, avec 8 bits d’exposant et 7 bits de fraction, qui privilégie la plage du FP32 au détriment de la précision et est apprécié pour l’entraînement de grands modèles.
- FP8 : formats à virgule flottante de 8 bits utilisés sur les GPU NVIDIA récents, qui réduisent encore de moitié la mémoire et servent principalement à entraîner et à exécuter de grands modèles de langage.
- Quantification INT8 : convertit les valeurs en entiers de 8 bits avec des échelles par tenseur ou par canal. Cette méthode est plus rapide et moins gourmande en mémoire que le FP16, mais nécessite des données d’étalonnage ou un entraînement tenant compte de la quantification pour préserver la précision.
Utilisations de la demi-précision#
- IA en périphérie : les détecteurs exécutés sur les modules Jetson et d’autres GPU embarqués utilisent le FP16 pour atteindre la cadence d’images des caméras avec une puissance de quelques watts ou dizaines de watts.
- Inférence dans le cloud : l’exécution en FP16 réduit de moitié la mémoire par réplique de modèle ; un GPU peut donc héberger davantage de répliques ou des lots plus grands.
- Grands modèles de langage : un modèle de 7 milliards de paramètres nécessite 28 Go pour ses poids en FP32 et 14 Go en FP16. En FP16, il tient donc sur un GPU grand public de 24 Go, avec de la place pour le cache KV.










