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):
- 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.
- Bits de exponente — controlan el rango de valores representables (positivo no cero más pequeño hasta mayor finito).
- 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), sinInf. 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). TieneInfyNaNcomo 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):
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 uniformes. Esto significa que los formatos enteros son peores representando valores pequeños (alrededor de cero, el espaciado entero ess, pero el espaciado fp allí ess × ε << 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:
- Anchos de bits y anchos de exponente de fp32, bf16, fp16. Para que puedas predecir umbrales de overflow (
exp(x)desborda enx ≈ 89para fp32 y bf16;x ≈ 11para fp16). - Anchos de mantisa de fp32 (23) y bf16 (7). Para que puedas predecir precisión relativa (
ε_fp32 ≈ 1.2e-7,ε_bf16 ≈ 7.8e-3). - INT8 con escala
stiene paso uniformes. 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).
- 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é? - Convierte
0.5a fp16. A bf16. A fp8 E4M3. ¿Qué patrones de bits? ¿Cuál es el error de ida y vuelta en cada uno? - Cuantiza a INT8 un tensor cuyos valores están en
[-2.5, 2.5]. Eligessimétricamente. ¿Cuál es el paso de cuantización? ¿Cuál es el peor error absoluto? - La probabilidad
1/600de la teoría01. Represéntala en bf16. ¿Cuál es el error relativo? ¿Es bf16 suficiente para esta distribución? - Muestra que sumar 600 números bf16 cada uno de magnitud
1/600acumulaO(N · ε_bf16) ≈ 4.7de 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.