Transformers documentation
Conceptos de cuantización
Conceptos de cuantización
Un número cuantizado solo puede tomar uno de un pequeño conjunto de valores, 256 para int8 y 16 para int4, por lo que cada método de cuantización introduce un error de redondeo. Los métodos difieren en cómo evitan que ese error afecte la salida del modelo. Esta página explica las decisiones detrás de esas diferencias: cómo se mapean los números a ese pequeño conjunto, qué tensores se cuantizan, qué tipos de datos se utilizan y cuándo ocurre la cuantización.
Si es nuevo en la cuantización en Transformers, comience con el Resumen.
Mapear valores a menos bits
La cuantización mapea un rango de valores de alta precisión al pequeño conjunto de enteros que un tipo de bajo bit puede representar. int8 puede representar 256 enteros, [-128, 127], y int4 puede representar 16.
Cuantificación afín es el mapeo más común. Toma los valores mínimo y máximo de un tensor y estira el rango de enteros sobre ellos. En el ejemplo siguiente, un tensor cuyos valores van de -1.8 a 2.4 se mapea a int8, y 0.0 cae en -19.
bf16 -1.8 0.0 2.4
├────────────────┼──────────────────────┤
▼ ▼ ▼
int8 -128 -19 127Dos parámetros definen el mapeo.
El escala
Ses el tamaño de un paso entero en las unidades originales.El punto cero
Zes el entero que representa0.0.
Un valor x se cuantiza al entero q dividiendo por la escala y desplazando por el punto cero.
La descuantización invierte el mapeo y devuelve una aproximación de x. La diferencia entre x y su aproximación es el error de redondeo.
En el ejemplo anterior, S es aproximadamente 0.0165 y Z es -19. El valor 0.5 se cuantiza a 11, y 11 se decuantiza a aproximadamente 0.494, un error de redondeo de aproximadamente 0.006.
┌─────┐ quantize ┌────┐ dequantize ┌───────┐ │ 0.5 │ ──────────▶ │ 11 │ ───────────▶ │ 0.494 │ └─────┘ └────┘ └───────┘
La cuantización afín se presenta en dos formas.
- La cuantización asimétrica utiliza las fórmulas anteriores. Mapea el rango exacto, por lo que no se desperdician valores representables cuando los pesos no están centrados en cero.
- La cuantización simétrica asume que el rango es
[-a, a]y fijaZen 0, por lo que solo se almacena la escala. El cálculo es más económico, pero algunos valores representables quedan sin usar cuando los pesos se inclinan hacia un lado del cero.
Elija la granularidad
Una sola escala para un tensor completo es barata de almacenar, pero un único valor atípico grande amplía el rango y separa más los valores representables para cada otro peso. Grupos más pequeños permiten que cada escala cubra un rango más estrecho.
- Per-tensor utiliza una escala y un punto cero para el tensor completo.
- Per-channel utiliza una escala para cada canal de salida, que corresponde a cada fila de una matriz de pesos.
- Por grupo, o por bloque, utiliza una escala para cada grupo de valores consecutivos, como 128 pesos. Cada grupo incurre en un costo adicional de almacenamiento para su escala.
El diagrama siguiente muestra las tres opciones en una matriz de pesos con tres filas. Cada s es una escala.
per-tensor per-channel per-group ┌────────────┐ ┌────────────┐ ┌──────┬──────┐ │ │ │ │ s0 │ s0 │ s1 │ │ │ ├────────────┤ ├──────┼──────┤ │ s │ │ │ s1 │ s2 │ s3 │ │ │ ├────────────┤ ├──────┼──────┤ │ │ │ │ s2 │ s4 │ s5 │ └────────────┘ └────────────┘ └──────┴──────┘
La mayoría de los métodos de bajo número de bits cuantizan por grupo. GPTQ utiliza por defecto grupos de 128 pesos, y Fine-grained FP8 utiliza por defecto bloques de 128x128. Los formatos FP4 en Data types incorporan el tamaño del bloque en el propio formato.
Cuantizar pesos o activaciones
Los métodos difieren en qué tensores cuantizan, y eso determina dónde se ejecuta la multiplicación de matrices.
- Los métodos de Weight-only almacenan los pesos en baja precisión y dejan las activaciones en bf16 o fp16. Antes de cada matmul, el kernel descuantiza los pesos a un dtype de cálculo de mayor precisión. bitsandbytes 4-bit, AWQ, GPTQ y HQQ funcionan de esta manera. En bitsandbytes,
bnb_4bit_compute_dtypeestablece ese dtype de cálculo. - Los métodos de pesos y activaciones cuantizan también las activaciones, y la multiplicación de matrices se ejecuta directamente en baja precisión. Esto requiere hardware que admita la multiplicación de matrices en baja precisión, como FP8 en las GPU más recientes. El esquema suele escribirse como W8A8 para pesos y activaciones de 8 bits.
Los métodos de solo pesos ahorran memoria, pero no siempre son más rápidos, ya que la descuantización añade trabajo a cada matmul. Aceleran la generación cuando el modelo está limitado por la memoria. Al generar un token a la vez, la GPU dedica la mayor parte de cada paso a leer los pesos desde la memoria, no a realizar cálculos. Leer pesos de 4 bits transfiere una cuarta parte de los datos que los pesos de bf16, y ese ahorro puede compensar el coste de la descuantización.
Los métodos de pesos y activaciones resultan útiles cuando el modelo está limitado por el cómputo, como en la fase de prefill o con lotes grandes, porque la propia multiplicación de matrices de baja precisión es más rápida. Algunos métodos alternan entre ambos. NVFP4 ejecuta una multiplicación de matrices con pesos de 4 bits y activaciones de 4 bits para el prefill, y una multiplicación de matrices con pesos de 4 bits y activaciones de 16 bits para la decodificación.
Tipos de datos
El tipo de datos define qué valores puede representar un número cuantizado.
int8 e int4
int8 e int4 representan enteros equiespaciados y utilizan el mapeo afín anterior. int4 solo puede representar 16 valores, por lo que pierde más precisión que int8. Los métodos que utilizan int4 suelen cuantizar por grupo o usar calibración para mantener la precisión.
La mayoría de los hardware no tiene un tipo de almacenamiento de 4 bits, por lo que dos valores int4 se empaquetan en un solo byte. Por ejemplo, el primer valor puede ocupar los 4 bits inferiores y el segundo valor los 4 bits superiores. Los kernels desempaquetan los valores antes de realizar cálculos con ellos.
┌──────┐ ┌──────┐ ┌──────┬──────┐ │ val1 │ │ val2 │ │ val2 │ val1 │ │ 0101 │ │ 0011 │ → │ 0011 │ 0101 │ 1 byte └──────┘ └──────┘ └──────┴──────┘ int4 int4 high low
Algunos tipos de 4 bits distribuyen sus valores representables de forma desigual para adaptarse a la distribución de los pesos. bitsandbytes NF4 coloca más de sus valores cerca de cero, donde se encuentran la mayoría de los pesos.
FP8
FP8 mantiene el formato de punto flotante con signo, exponente y mantisa en 8 bits. Sus valores representables están más cerca entre sí cerca de cero y más separados en magnitudes grandes, lo cual se adapta a la distribución de valores en pesos y activaciones.
FP8 viene en dos variantes.
- E4M3 tiene 4 bits de exponente y 3 bits de mantisa. Es más preciso, pero cubre un rango menor. Es la opción habitual para los pesos y las activaciones durante la inferencia.
- E5M2 tiene 5 bits de exponente y 2 bits de mantisa. Cubre un rango más amplio con menor precisión.
E4M3 ┌─┬───────┬─────┐
│S│E E E E│M M M│
└─┴───────┴─────┘
E5M2 ┌─┬─────────┬───┐
│S│E E E E E│M M│
└─┴─────────┴───┘
S sign E exponent M mantissaLos matmuls en FP8 requieren soporte de hardware, como GPUs de NVIDIA con capacidad de cómputo 8.9 o superior (Ada, Hopper, Blackwell), AMD Instinct MI300 e Intel XPU. Transformers admite FP8 a través de Fine-grained FP8, FBGEMM FP8 y compressed-tensors.
FP4 con escalas por bloque
FP4 tiene 1 bit de signo, 2 bits de exponente y 1 bit de mantisa (E2M1). Solo representa los valores 0, 0.5, 1, 1.5, 2, 3, 4 y 6, tanto positivos como negativos. Con tan pocos valores, los formatos FP4 no pueden funcionar con una sola escala por tensor. Asocian una escala a cada bloque pequeño de valores y almacenan las escalas como parte del formato.
| Formato | Tamaño de bloque | Escalar por bloque | Escala adicional |
|---|---|---|---|
| MXFP4 | 32 valores | Potencia de 2 de 8 bits (E8M0) | Ninguno |
| NVFP4 | 16 valores | FP8 | Una escala FP32 por tensor |
MXFP4 es un formato de microescalado. Una escala en potencia de dos solo desplaza el exponente, por lo que aplicarla es barato, pero solo puede situarse en potencias de dos. NVFP4 utiliza bloques más pequeños y una escala FP8 que puede tomar valores entre potencias de dos. Esto conserva más precisión, a costa de una escala por tensor que mantiene las escalas de bloques FP8 dentro del rango. Four Over Six y FP-Quant también producen pesos NVFP4 o MXFP4.
La misma idea de bloques aparece en 8 bits. MXFP8 almacena valores FP8 con escalas de bloque en potencias de dos, y Fine-grained FP8 lo carga.
Codebooks
Por debajo de 4 bits, algunos métodos dejan de redondear cada peso al valor representable más cercano. Cuantizan grupos de pesos juntos como vectores. Un libro de códigos contiene un conjunto de vectores aprendidos, y cada grupo de pesos se almacena como el índice de su vector más cercano. AQLM utiliza por defecto grupos de 8 pesos y un índice de 16 bits, lo que equivale a 2 bits por peso. VPTQ también utiliza libros de códigos. Encontrar buenos libros de códigos requiere una optimización prolongada sobre datos de calibración, por lo que estos modelos se cuantizan de antemano y se cargan como puntos de control.
Cuando se produce la cuantización
Un modelo se cuantiza ya sea después del entrenamiento o durante el mismo. La mayoría de los métodos en Transformers cuantizan después del entrenamiento.
Cuantización posterior al entrenamiento
La cuantización posterior al entrenamiento (PTQ) cuantiza un modelo que ya está entrenado, sin modificar lo que aprendió. Los métodos de PTQ difieren en si requieren datos, y eso determina en qué grupo se clasifica un método en el índice de la documentación.
- Sin datos de calibración, las escalas se derivan únicamente de los pesos y la cuantización es lo suficientemente rápida para ejecutarse mientras el modelo se carga. bitsandbytes, HQQ y torchao funcionan de esta manera. Estos métodos se enumeran en Cuantizar durante la carga.
- Con datos de calibración, el método ejecuta un conjunto de datos pequeño a través del modelo y utiliza las activaciones para decidir cómo cuantizar cada capa. La calibración generalmente ofrece una mejor precisión a 4 bits o menos, pero toma desde minutos hasta horas.
Los métodos de calibración utilizan las activaciones de diferentes maneras. GPTQ cuantiza una capa columna por columna y ajusta las columnas restantes para compensar el error de redondeo de cada columna. AWQ identifica los canales de pesos con las activaciones más grandes y los escala antes de redondearlos para protegerlos.
Donde se ejecutan las calibraciones determina cómo se utiliza el método en Transformers.
- GPTQ realiza la calibración dentro de
from_pretrainedcuando se pasa undataseta GPTQConfig, por lo que se lista bajo Cuantizar durante la carga. - Otros métodos de calibración, como AWQ, AutoRound, AQLM y VPTQ, calibran en sus propias librerías. Transformers carga el resultado, por lo que se enumeran en Cargar puntos de control pre-cuantizados.
Entrenamiento consciente de la cuantización
El entrenamiento consciente de la cuantización (QAT) simula la cuantización durante el entrenamiento. En la pasada hacia adelante, los valores se redondean tal como lo haría la cuantización, mientras que los gradientes actualizan los pesos de precisión completa, de modo que el modelo aprende a tolerar el error de redondeo. QAT suele ofrecer la mejor precisión en anchos de bits muy bajos, pero requiere entrenar el modelo.
Los modelos BitNet se entrenan de esta manera, con sus pesos de 1,58 bits en el bucle, y Transformers carga los puntos de control entrenados.
Recursos
- Fundamentos de la cuantización con Hugging Face
- Cuantización en profundidad
- Introducción a la cuantización cocinada en 🤗 con 💗🧑🍳
- EfficientML.ai Clase 5 - Cuantización Parte I
- Hacer que el deep learning funcione a toda velocidad desde los principios fundamentales
- Acelerar la IA generativa con PyTorch, parte 2: optimizaciones de LLM