Half-Precision
La precisión media (FP16) almacena los números en 16 bits en lugar de los 32 de FP32, lo que reduce a la mitad la memoria y acelera los cálculos en la GPU. Sus límites, el escalado de la pérdida y la inferencia FP16.
La precisión media, también denominada FP16, float16 o IEEE 754 binary16, es un formato de coma flotante de 16 bits con 1 bit de signo, 5 bits de exponente y 10 bits de fracción. En comparación con la precisión simple estándar de 32 bits (FP32), reduce a la mitad la memoria y el ancho de banda que necesita cada número, y las GPU con Tensor Cores ejecutan operaciones matriciales FP16 muchas veces más rápido que en FP32. La contrapartida es un rango limitado, con un valor máximo de 65,504 y unas tres cifras decimales significativas. Las redes neuronales toleran bien ese redondeo, por lo que FP16 es la precisión estándar para la inferencia en GPU y, combinada con FP32 en precisión mixta, una forma habitual de acelerar el entrenamiento.
Comparación entre FP16 y FP32#
FP32 dedica 8 bits al exponente y 23 a la fracción, por lo que abarca aproximadamente de 10⁻³⁸ a 10³⁸ con unas siete cifras significativas. Los 5 bits de exponente de FP16 limitan los valores normales al intervalo de 6.1 × 10⁻⁵ a 65,504; los valores subnormales más pequeños llegan aproximadamente a 6 × 10⁻⁸ con menor precisión. Cualquier valor superior se desborda a infinito y cualquier valor inferior se redondea a cero. Convertir un modelo muestra tanto las ventajas como los límites:
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())Los pesos pasan de 46.8 MB a 23.4 MB. En la segunda línea, 65,504 se conserva como el valor máximo de FP16, 70,000 se convierte en inf y 0.1 se almacena como 0.0999755859375, el valor representable más cercano en FP16.
Precisión media durante el entrenamiento#
Entrenar exclusivamente en FP16 reduce la exactitud o provoca divergencias en muchos modelos, porque los gradientes pequeños sufren subdesbordamiento y se convierten en cero, y las actualizaciones de pesos, muy pequeñas en comparación con los propios pesos, desaparecen al redondearse. El entrenamiento con precisión mixta resuelve ambos problemas: mantiene una copia maestra en FP32 de los pesos para aplicar las actualizaciones, ejecuta tanto las pasadas hacia delante como las pasadas hacia atrás en FP16 y multiplica la pérdida por un factor de escala para que los gradientes se mantengan dentro del rango de FP16 antes de desescalarlos en FP32. Los frameworks ajustan automáticamente ese factor, como lo hace GradScaler de PyTorch:
Las activaciones FP16 también reducen a la mitad la memoria que ocupa un lote durante el entrenamiento, por lo que la misma GPU admite lotes más grandes o entradas de mayor resolución. En el hardware compatible, suele preferirse bfloat16 (BF16) para entrenar, porque su exponente del mismo tamaño que el de FP32 elimina la necesidad de escalar la pérdida.
Precisión media para la inferencia#
La inferencia no necesita gradientes y las activaciones de la mayoría de los modelos de visión se mantienen dentro del rango FP16, por lo que convertir un modelo terminado a FP16 suele conservar su precisión con una diferencia mínima respecto a FP32, sin los datos de calibración que requiere la cuantización de modelos INT8. Los motores de GPU, como TensorRT, crean motores FP16 que se ejecutan en Tensor Cores, y las GPU integradas, como NVIDIA Jetson, recurren a FP16 para la inferencia en tiempo real. Las CPU obtienen menos ventajas, porque muchas carecen de operaciones FP16 rápidas. Ultralytics YOLO entrena de forma predeterminada en GPU con precisión mixta FP16 (amp=True) y utiliza quantize=16 para la predicción y la exportación en FP16, por ejemplo con YOLO26:
from ultralytics import YOLO
# Carga el modelo de detección YOLO26 más pequeño
model = YOLO("yolo26n.pt")
# Predice en FP16 en una GPU; en una CPU, los modelos de PyTorch se ejecutan en FP32
results = model.predict("https://ultralytics.com/images/bus.jpg", quantize=16)
# Exporta un modelo ONNX que almacene sus pesos en FP16
path = model.export(format="onnx", quantize=16)El archivo exportado almacena cada peso en 2 bytes en lugar de 4, y el mismo argumento quantize=16 genera modelos FP16 para TensorRT, OpenVINO, CoreML y otros formatos de exportación.
Precisión media frente a formatos relacionados#
- BFloat16 (BF16): El otro formato de coma flotante de 16 bits, con 8 bits de exponente y 7 de fracción; sacrifica precisión para igualar el rango de FP32 y se prefiere para entrenar modelos grandes.
- FP8: Formatos de coma flotante de 8 bits disponibles en las GPU NVIDIA recientes que vuelven a reducir a la mitad la memoria y se usan sobre todo para entrenar y servir modelos de lenguaje grandes.
- Cuantización INT8: Convierte los valores en enteros de 8 bits con escalas por tensor o por canal; es más rápida y ocupa menos que FP16, pero necesita datos de calibración o entrenamiento con conciencia de la cuantización para mantener la precisión.
Usos de la precisión media#
- IA en el borde: Los detectores en módulos Jetson y otras GPU integradas se ejecutan en FP16 para alcanzar la frecuencia de imagen de las cámaras con unos pocos o unas decenas de vatios de potencia.
- Inferencia en la nube: Servir modelos en FP16 reduce a la mitad la memoria por réplica del modelo, por lo que una GPU puede alojar más réplicas o lotes más grandes.
- Modelos de lenguaje grandes: Un modelo de 7.000 millones de parámetros necesita 28 GB para sus pesos en FP32 y 14 GB en FP16, por lo que en FP16 cabe en una GPU de consumo de 24 GB con espacio adicional para la caché KV.










