Model Quantization
La cuantización de modelos almacena pesos y activaciones con menor precisión, por ejemplo INT8, para reducir el tamaño de los modelos y acelerar la inferencia. Descubre cómo funciona y las diferencias entre PTQ y QAT.
La cuantización de modelos convierte los pesos de una red neuronal, y a menudo también sus activaciones, de números de coma flotante de 32 bits (FP32) a formatos de menor precisión, como números de coma flotante de 16 bits (FP16), enteros de 8 bits (INT8) o enteros de 4 bits. Cada valor necesita entonces menos bits para almacenarse y transferirse, y la aritmética entera se ejecuta más rápido y consume menos energía en la mayoría de los procesadores. Un modelo INT8 ocupa una cuarta parte que el original FP32, lo que permite a los modelos de detección de objetos funcionar en tiempo real en teléfonos, cámaras y otros dispositivos de IA en el borde, y a los modelos de lenguaje grandes (LLM) caber en una sola GPU.
Cómo funciona la cuantización#
La cuantización asigna un intervalo de números reales a un pequeño conjunto de niveles enteros. En el esquema afín habitual, cada valor flotante x se almacena como un entero q = round(x / scale) + zero_point y se recupera como x ≈ (q - zero_point) × scale. La escala establece el tamaño del paso entre niveles, y el punto cero garantiza que el valor flotante 0 se corresponda con un entero exacto. INT8 tiene 256 niveles, por lo que hay que elegir con cuidado el intervalo: si es demasiado amplio, se desperdician niveles en valores que nunca aparecen, y si es demasiado estrecho, se recortan los valores atípicos. Los pesos se conocen de antemano, así que sus intervalos se obtienen directamente del modelo entrenado y, a menudo, se calculan por separado para cada canal de salida. Las activaciones dependen de la entrada, por lo que sus intervalos se miden durante la calibración al procesar en el modelo un pequeño conjunto representativo de imágenes o muestras de texto.
import numpy as np
rng = np.random.default_rng(0)
weights = rng.normal(0, 0.05, size=1000).astype(np.float32)
# Affine INT8 quantization maps the observed float range onto 256 integer levels
scale = (weights.max() - weights.min()) / 255
zero_point = np.round(-128 - weights.min() / scale)
q = np.clip(np.round(weights / scale + zero_point), -128, 127).astype(np.int8)
# Dequantize to measure the rounding error the model has to tolerate
restored = (q.astype(np.float32) - zero_point) * scale
print(f"Size: {weights.nbytes} -> {q.nbytes} bytes")
print(f"Max error: {np.abs(weights - restored).max():.5f} (half a step: {scale / 2:.5f})")Imprime Size: 4000 -> 1000 bytes y un error máximo de 0,00068, equivalente a la mitad de un paso de cuantización. Los pesos se reducen a una cuarta parte, y ningún valor se desvía más de medio paso respecto al original.
Cuantización posterior al entrenamiento frente a entrenamiento consciente de la cuantización#
- Cuantización posterior al entrenamiento (PTQ): convierte un modelo ya entrenado y utiliza un pequeño conjunto de calibración para establecer los intervalos de activación. Tarda unos minutos y no necesita datos de entrenamiento más allá de esas muestras. La cuantización dinámica es una variante de PTQ que calcula los intervalos de activación sobre la marcha durante la inferencia, y la cuantización solo de pesos mantiene las activaciones en coma flotante.
- Entrenamiento consciente de la cuantización (QAT): simula la cuantización durante el entrenamiento o el ajuste fino redondeando valores en la pasada hacia delante, para que los pesos aprendan a tolerar el redondeo. Recupera la mayor parte de la precisión que pierde PTQ, a costa de más entrenamiento.
Los equipos suelen empezar con PTQ y solo cambian de método cuando la caída de precisión es demasiado grande:
Los LLM incorporan sus propios métodos. Los esquemas de 4 bits solo para pesos, como GPTQ y AWQ, comprimen miles de millones de parámetros y mantienen las activaciones en 16 bits; el formato GGUF almacena modelos cuantizados para inferencia local, y las GPU recientes realizan cálculos de forma nativa en formatos de coma flotante de baja precisión, como FP8.
Cuantización y técnicas relacionadas#
- Precisión mixta: técnica de entrenamiento que ejecuta la mayoría de las operaciones en FP16 o BF16 y conserva una copia maestra de los pesos en FP32 para acelerar el entrenamiento. La cuantización se centra en la inferencia y va más allá, a menudo utilizando enteros.
- Poda de modelos: elimina pesos o canales completos para reducir el número de operaciones; suele combinarse con la cuantización, que abarata cada operación restante.
- Destilación de conocimiento: entrena un modelo estudiante pequeño para que imite a un modelo profesor grande. A menudo se cuantiza después el modelo estudiante para desplegarlo.
La cuantización en la práctica#
La aceleración depende de la compatibilidad del hardware: un modelo INT8 solo se ejecuta más rápido en procesadores con kernels INT8, como las GPU de NVIDIA con TensorRT, las CPU de Intel con OpenVINO y las unidades de procesamiento neuronal (NPUs) de los teléfonos. Ultralytics YOLO26 aplica PTQ durante la exportación con quantize=8, calibrando el modelo con imágenes del conjunto de datos data; en cambio, model.train(quantize=8) realiza un ajuste fino de un checkpoint mediante QAT. Este ejemplo exporta un modelo INT8 para LiteRT, el entorno de ejecución de Google para Android y dispositivos integrados:
from ultralytics import YOLO
# Carga un modelo nano YOLO26 preentrenado
model = YOLO("yolo26n.pt")
# Cuantización INT8 posterior al entrenamiento; las imágenes de coco8.yaml calibran los intervalos de activación
model.export(format="litert", quantize=8, data="coco8.yaml")Medido con COCO y TensorRT, PTQ INT8 reduce el mAP50-95 de YOLO26n solo de 0,403 a 0,393, pero penaliza más a YOLO26s, que pasa de 0,479 a 0,441; QAT devuelve YOLO26s a 0,471.










