Skip to content

English · Español

04 — El zoo de precisiones: BF16, TF32, FP8, INT8/INT4

🇪🇸 Más allá de fp32/fp64 hay un zoo de formatos diseñados para AI: bf16 (entreno), fp16 (inferencia legacy), fp8 (entreno frontier), int8/int4 (cuantización). Esta página presenta el panorama; Fase 26 lo profundiza.

Esta página es un repaso. La Fase 26 (cuantización) revisará cada formato en detalle, con experimentos reales de calibración y cotas de error. Por ahora necesitas las disposiciones de bits, la intención de diseño y un puñado de mnemónicos de una línea para que los papers y la documentación de bibliotecas sean legibles.


El espacio de compromisos

Tres mandos están en juego para cualquier formato numérico usado en IA (AI):

  1. Ancho en bits — controla directamente la huella de memoria, el ancho de banda de memoria y (si el hardware lo soporta) el throughput aritmético.
  2. Bits de exponente — controlan el rango de valores representables (positivo no cero más pequeño hasta mayor finito).
  3. Bits de mantisa — controlan la precisión (resolución relativa) dentro de un exponente dado.

Exponente más ancho → puede representar valores muy grandes y muy pequeños sin overflow/underflow. Mantisa más ancha → puede distinguir valores cercanos con más finura. Estos compiten por bits; reducir el ancho total fuerza una elección.

Formato Bits Exp Mant Rango Dígitos decimales Diseñado para
fp64 64 11 52 ~1.7e308 ~15.95 Oráculo / científico
fp32 32 8 23 ~3.4e38 ~7.22 Aprendizaje automático de propósito general
TF32 19 8 10 ~3.4e38 ~3.3 Tensor cores de NVIDIA (acumulado de matmul)
bf16 16 8 7 ~3.4e38 ~2.4 Predeterminado moderno de entrenamiento
fp16 16 5 10 ~6.5e4 ~3.3 Inferencia legacy, se necesita gradient scaling
FP8 E4M3 8 4 3 ~448 ~1.0 Activaciones forward (Hopper, Blackwell)
FP8 E5M2 8 5 2 ~5.7e4 ~0.7 Gradientes (más rango)
INT8 8 [-128, 127] Inferencia cuantizada
INT4 4 [-8, 7] Cuantización solo de pesos

(Los formatos enteros no tienen exponente / mantisa; son bit de signo + magnitud sin escalado implícito. La "escala" vive fuera, en un valor fp separado por tensor o por canal — cubierto en la Fase 26.)

fp16 vs bf16 — el compromiso que impulsó el entrenamiento moderno

Ambos son de 16 bits. fp16 tiene 5 bits de exponente + 10 bits de mantisa; bf16 tiene 8 bits de exponente + 7 bits de mantisa.

El exponente estrecho de fp16 desborda en softmax. Recuerda de la teoría 02: exp(x) desborda fp32 en torno a x ≈ 89. Para fp16, el mayor representable es ~6.5 × 10⁴, así que exp(x) desborda en x ≈ 11. Un logit de tamaño modesto (que es normal en salidas de modelo sin normalizar) desborda inmediatamente. El entrenamiento mixto en fp16 requiere gradient scaling (multiplicar la pérdida por 2^k, recortar tras el backward, desescalar) para mantener los gradientes en el rango representable.

bf16 hereda el exponente de fp32. El overflow de exp(x) ocurre en el mismo x ≈ 89 que fp32. No se necesita gradient scaling en el caso común. El compromiso es la precisión de mantisa: bf16 solo tiene 7 bits de mantisa (ε ≈ 7.8 × 10⁻³ ≈ 0.8%), unas 8× peor que fp16. Pero los gradientes toleran ruido; los pesos y activaciones rara vez necesitan más de un 1% de precisión relativa. La ganancia es enorme.

Por qué esto importa para Borja. La Fase 26 entrena MiniGPT en bf16 (cuando hay GPU disponible). La FlashAttention de la Fase 27 usa precisión mixta bf16 / fp16. Leer la literatura requiere conocer este compromiso de memoria. Mnemónico: fp16 = precisión primero; bf16 = rango primero; el entrenamiento moderno eligió rango.

TF32 — el compromiso de NVIDIA

Un formato de 19 bits con exponente fp32 (8 bits) pero solo 10 bits de mantisa. Vive solo en NVIDIA Ampere y posteriores. Usado internamente por los tensor cores cuando ambos operandos son fp32 — el multiplicador trunca a precisión TF32 por velocidad, luego acumula en fp32. El usuario ve fp32 dentro y fp32 fuera, pero la multiplicación es precisa en TF32.

No escribirás TF32 directamente. Lo verás en el toggle torch.backends.cuda.matmul.allow_tf32 = True/False de PyTorch. El predeterminado es True para matmul en Ampere+, que la Fase 25 medirá.

FP8 — la frontera

Las GPU Hopper (H100) y Blackwell envían dos formatos FP8:

  • E4M3 (1 signo + 4 exponente + 3 mantisa): sesgo 7, rango hasta 448. Usado para activaciones y pesos. Patrones de bits reservados: S.1111.111 = NaN, S.1111.110 = 448 (máx finito), sin Inf. Esto se desvía de IEEE-754; la elección de diseño favorece usar todos los patrones de bits para valores finitos.
  • E5M2 (1 signo + 5 exponente + 2 mantisa): sesgo 15, rango hasta ~57,344. Usado para gradientes (se necesita más rango por la cadena de loss-scale). Tiene Inf y NaN como IEEE-754 normal.

La Fase 26 cubre las recetas de calibración y precisión mixta para entrenamiento fp8. La Fase 2 solo necesita que conozcas las disposiciones de bits y la intención de diseño.

¿Por qué dos formatos? Las activaciones forward tienen rango dinámico acotado (tras layer norm); E4M3 basta. Los gradientes tienen rango dinámico enorme (cadena-de-derivadas puede multiplicar números pequeños en muy pequeños); E5M2 los salva del underflow.

Formatos enteros — INT8 e INT4

Los formatos enteros no son IEEE-754. Almacenan un entero con signo en un rango fijo:

  • INT8: [-128, 127]. Cada elemento del tensor se almacena como un byte.
  • INT4: [-8, 7]. Cada elemento del tensor es medio byte. Dos valores por byte.

Para representar tensores reales como enteros, necesitas una escala (y opcionalmente un punto cero):

\[ x_{\mathrm{real}} \approx s \cdot (q - z) \]

Donde q es el entero almacenado, s es una escala por tensor o por canal (fp32), z es un punto cero entero (a menudo 0 para cuantización simétrica).

El error de esta representación depende de s y la distribución real de los datos. Para un tensor con distribución normal σ = 1, la cuantización simétrica INT8 con s = 2σ/127 da un paso de cuantización de ~0.016, así que el error relativo es aproximadamente 0.5%. Comparable a la precisión de mantisa de bf16.

El sentido entero de la cuantización entera es la memoria. Los pesos INT8 son 4× más pequeños que los pesos fp32, los INT4 son 8× más pequeños. La inferencia limitada por ancho de banda (que es la mayoría de la inferencia de LLM con batch size 1, por el análisis roofline de la Fase 1) obtiene un speedup casi proporcional de los pesos más pequeños, aunque las multiplicaciones INT8 no sean más rápidas que las fp32 en la mayoría de CPUs. Compras en bytes, no en FLOPs.

Qué se pierde

  • NaN, Inf, cero con signo. Los formatos enteros no pueden representar estos.
  • Espaciado logarítmico de valores. El espaciado de valores de fp es ~ε × |x|; el espaciado entero es uniforme s. Esto significa que los formatos enteros son peores representando valores pequeños (alrededor de cero, el espaciado entero es s, pero el espaciado fp allí es s × ε << s). La Fase 26 cubre estrategias (cuantización por canal, GPTQ, AWQ) que mitigan esto.

Laboratorio de previsualización de cuantización

El laboratorio 03-quantization-preview.md te hace redondear un vector de logits fp32 (los logits de clasificación de tiempos para una predicción de forma verbal de §A13) a INT8 y vuelta, y graficar la distribución del error. Esto no es cuantización real — no hay calibración, ni escala por canal, ni SmoothQuant. Es una vista previa que motiva la Fase 26.

Cuándo se usa cada formato en la práctica

Caso de uso Formato típico Por qué
Referencia / chequeo de gradiente fp64 Oráculo de precisión máxima
Entrenamiento predeterminado fp32 (CPU) / bf16 (GPU) Precisión estándar
Acumuladores de entrenamiento fp32 / fp64 Evitar sobrecarga de Kahan
Entrenamiento de precisión mixta (legacy) fp16 + maestro fp32 Necesita gradient scaling
Entrenamiento frontier (H100+) fp8 + maestro bf16 Ganancias en memoria + ancho de banda
Inferencia de LLM (GPU de consumo) fp16, bf16 o 4-bit Limitado por memoria; más pequeño gana
Inferencia de LLM (CPU, llama.cpp) Q4_K_M, Q5_K_M, Q8_0 Formatos GGUF personalizados; Fase 26
MiniGPT en este currículo fp32 (CPU, referencia NumPy) Simplicidad; el rendimiento no es el punto

Tocarás cada fila de arriba para la Fase 26. La Fase 2 solo te da el vocabulario.

Cómo emular formatos para los que no tienes hardware

El i5-8250U de Borja tiene fp32/fp64 nativo (vía AVX2) y nada de hardware fp16/bf16/fp8. Para explorar esos formatos:

  • bf16: bit-cast fp32 a uint32, pon a cero los 16 bits bajos, bit-cast de vuelta. Hecho.
  • fp16: numpy.float16 (NumPy emula). Lento pero correcto.
  • fp8 E4M3 / E5M2: hazte tu propia manipulación de bits, o instala la biblioteca ml_dtypes (fuera de alcance para la Fase 2 — emula a mano en el laboratorio).
  • INT8/INT4: redondea-luego-escala, ver el lab 03.

El laboratorio 00-bit-anatomy.md recorre la emulación de bf16 y fp16. El lab 03 hace INT8.

Qué vale la pena memorizar

Tres cosas, antes de pasar a la Fase 3:

  1. Anchos de bits y anchos de exponente de fp32, bf16, fp16. Para que puedas predecir umbrales de overflow (exp(x) desborda en x ≈ 89 para fp32 y bf16; x ≈ 11 para fp16).
  2. Anchos de mantisa de fp32 (23) y bf16 (7). Para que puedas predecir precisión relativa (ε_fp32 ≈ 1.2e-7, ε_bf16 ≈ 7.8e-3).
  3. INT8 con escala s tiene paso uniforme s. Para que puedas cuantificar el error de redondeo.

Todo lo demás se puede consultar. Estos tres deberías saberlos sin mirar.

Problemas de práctica

Soluciones en solutions/04-precision-zoo-ref.md (al abrir la fase).

  1. El modelo de §A13 produce un vector de logits fp32 de longitud 600. El logit máximo es 12.3. ¿Puede proceder el ingenuo exp(logits) / sum(exp(logits)) en (a) fp32 (b) bf16 © fp16 sin overflow? ¿Por qué?
  2. Convierte 0.5 a fp16. A bf16. A fp8 E4M3. ¿Qué patrones de bits? ¿Cuál es el error de ida y vuelta en cada uno?
  3. Cuantiza a INT8 un tensor cuyos valores están en [-2.5, 2.5]. Elige s simétricamente. ¿Cuál es el paso de cuantización? ¿Cuál es el peor error absoluto?
  4. La probabilidad 1/600 de la teoría 01. Represéntala en bf16. ¿Cuál es el error relativo? ¿Es bf16 suficiente para esta distribución?
  5. Muestra que sumar 600 números bf16 cada uno de magnitud 1/600 acumula O(N · ε_bf16) ≈ 4.7 de error — es decir, la suma no tiene sentido. ¿Qué dice esto sobre usar bf16 para acumuladores?

Recapitulación en un párrafo

El aprendizaje automático (machine learning) usa un zoo de formatos numéricos — fp64 (oráculo), fp32 (predeterminado), bf16 (entrenamiento moderno, exponente fp32 + mantisa de 7 bits), fp16 (legacy, exponente estrecho), fp8 E4M3/E5M2 (entrenamiento frontier), INT8/INT4 (inferencia cuantizada). Cada uno cambia rango, precisión y ancho. El patrón recurrente: los formatos de entrenamiento de 16 bits (bf16) preservan el rango de exponente a costa de la precisión; la cuantización de inferencia (INT8/INT4) preserva el rango efectivo de los datos vía escalas por tensor a costa de error de paso uniforme cerca de cero. La Fase 26 vuelve a cada uno. La Fase 2 solo te enseña a leer las columnas.

Lo que esta página NO cubre

  • Algoritmos de calibración (GPTQ, AWQ, SmoothQuant). Fase 26.
  • Formato de archivo GGUF para llama.cpp. Fase 26.
  • Automatización de loss scaling (torch.cuda.amp.GradScaler). Fase 25 + 26.
  • Políticas de precisión mixta (autocast). Fase 25.

Teoría de la Fase 2 completa. Siguiente parada: lab/00-bit-anatomy.md.