Half-Precision
Bei halber Genauigkeit (FP16) werden Zahlen mit 16 statt 32 Bit wie bei FP32 gespeichert. Das halbiert den Speicherbedarf und beschleunigt GPU-Berechnungen. Behandelt werden die Grenzen, Loss Scaling und FP16-Inferenz.
Halbpräzision, auch FP16, float16 oder IEEE 754 binary16 genannt, ist ein 16-Bit-Gleitkommaformat mit 1 Vorzeichenbit, 5 Exponentenbits und 10 Mantissenbits. Im Vergleich zur standardmäßigen 32-Bit-Einfachpräzision (FP32) halbiert es den Speicher- und Bandbreitenbedarf jeder Zahl. GPUs mit Tensor Cores führen FP16-Matrixoperationen um ein Vielfaches schneller aus als FP32-Operationen. Der Kompromiss ist ein enger Wertebereich mit einem Höchstwert von 65,504 und etwa drei signifikanten Dezimalstellen. Neuronale Netze tolerieren diese Rundung gut. Daher ist FP16 das Standardformat für GPU-Inferenz und – in Kombination mit FP32 bei gemischter Präzision – eine gängige Methode, das Training zu beschleunigen.
FP16 im Vergleich zu FP32#
FP32 verwendet 8 Bits für den Exponenten und 23 für die Mantisse. Dadurch deckt es ungefähr den Bereich von 10⁻³⁸ bis 10³⁸ mit etwa sieben signifikanten Stellen ab. Die 5 Exponentenbits von FP16 begrenzen normale Werte auf einen Bereich zwischen 6.1 × 10⁻⁵ und 65,504. Kleinere subnormale Werte sind bei reduzierter Genauigkeit bis etwa 6 × 10⁻⁸ möglich. Größere Werte laufen ins Unendliche über, kleinere werden auf null gerundet. Die Umwandlung eines Modells zeigt sowohl den Vorteil als auch die Grenzen:
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())Die Gewichte schrumpfen von 46.8 MB auf 23.4 MB. In der zweiten Zeile bleibt 65,504 als größter FP16-Wert erhalten, 70,000 wird zu inf, und 0.1 wird als 0.0999755859375 gespeichert – dem nächstgelegenen Wert, den FP16 darstellen kann.
Halbpräzision beim Training#
Wenn vollständig mit FP16 trainiert wird, sinkt bei vielen Modellen die Genauigkeit oder das Training divergiert, weil kleine Gradienten auf null abgerundet werden und Gewichtsaktualisierungen, die im Verhältnis zu den Gewichten sehr klein sind, weggerundet werden. Training mit gemischter Präzision behebt beides: Für die Aktualisierungen bleibt eine FP32-Hauptkopie der Gewichte erhalten, während der Vorwärts- und Rückwärtsdurchlauf in FP16 ausgeführt wird. Außerdem wird der Loss mit einem Skalierungsfaktor multipliziert, damit die Gradienten innerhalb des FP16-Wertebereichs bleiben, bevor sie in FP32 zurückskaliert werden. Frameworks passen diesen Faktor automatisch an, wie es PyTorch mit GradScaler tut:
FP16-Aktivierungen halbieren außerdem den Speicherbedarf eines Batches während des Trainings. Dadurch kann dieselbe GPU größere Batches oder Eingaben mit höherer Auflösung verarbeiten. Auf unterstützter Hardware wird für das Training häufig bfloat16 (BF16) bevorzugt, da dessen Exponentenbereich dem von FP32 entspricht und dadurch keine Loss-Skalierung nötig ist.
Halbpräzision bei der Inferenz#
Für die Inferenz sind keine Gradienten nötig, und die Aktivierungen der meisten Bildverarbeitungsmodelle liegen bequem innerhalb des FP16-Wertebereichs. Daher bleibt die Genauigkeit beim Umwandeln eines fertigen Modells in FP16 meist nur geringfügig hinter FP32 zurück – ohne die Kalibrierungsdaten, die eine Modellquantisierung mit INT8 erfordert. GPU-Engines wie TensorRT erstellen FP16-Engines, die auf Tensor Cores laufen. Eingebettete GPUs wie NVIDIA Jetson nutzen FP16 für Echtzeit-Inferenz. CPUs profitieren weniger, da vielen eine schnelle FP16-Arithmetik fehlt. Ultralytics YOLO trainiert standardmäßig auf GPUs mit gemischter FP16-Präzision (amp=True) und verwendet quantize=16 für FP16-Vorhersagen und den Export, beispielsweise mit YOLO26:
from ultralytics import YOLO
# Das kleinste YOLO26-Modell zur Objekterkennung laden
model = YOLO("yolo26n.pt")
# Mit FP16 auf einer GPU vorhersagen; auf einer CPU laufen PyTorch-Modelle mit FP32
results = model.predict("https://ultralytics.com/images/bus.jpg", quantize=16)
# Ein ONNX-Modell exportieren, dessen Gewichte in FP16 gespeichert sind
path = model.export(format="onnx", quantize=16)Die exportierte Datei speichert jedes Gewicht mit 2 statt 4 Byte. Mit demselben Argument quantize=16 lassen sich FP16-Modelle für TensorRT, OpenVINO, CoreML und andere Exportformate erzeugen.
Halbpräzision im Vergleich zu verwandten Formaten#
- BFloat16 (BF16): Das andere 16-Bit-Gleitkommaformat mit 8 Exponenten- und 7 Mantissenbits. Es tauscht Genauigkeit gegen den Wertebereich von FP32 und wird für das Training großer Modelle bevorzugt.
- FP8: 8-Bit-Gleitkommaformate auf aktuellen NVIDIA-GPUs, die den Speicherbedarf nochmals halbieren und hauptsächlich zum Trainieren und Bereitstellen großer Sprachmodelle dienen.
- INT8-Quantisierung: Wandelt Werte mithilfe von Skalen pro Tensor oder Kanal in 8-Bit-Ganzzahlen um. Das ist schneller und benötigt weniger Speicher als FP16, erfordert aber Kalibrierungsdaten oder quantisierungsbewusstes Training, damit die Genauigkeit erhalten bleibt.
Einsatzbereiche der Halbpräzision#
- Edge-KI: Detektoren auf Jetson-Modulen und anderen eingebetteten GPUs laufen mit FP16, um bei einer Leistungsaufnahme von wenigen oder einigen zehn Watt die Bildraten von Kameras zu erreichen.
- Cloud-Inferenz: Die Bereitstellung mit FP16 halbiert den Speicherbedarf pro Modellreplikat. Dadurch kann eine GPU mehr Replikate oder größere Batches aufnehmen.
- Große Sprachmodelle: Ein Modell mit 7 Milliarden Parametern benötigt für seine FP32-Gewichte 28 GB und mit FP16 14 GB. Mit FP16 passt es daher auf eine einzelne 24-GB-Consumer-GPU, und es bleibt noch Speicher für den KV-Cache übrig.










