Skip to content

English · Español

03 — GPTQ y NF4

GPTQ no cuantiza pesos al azar: usa la Hessiana de las activaciones para redistribuir el error de redondeo hacia las columnas que aún no ha cuantizado. NF4 abandona la rejilla uniforme y usa los cuantiles de una N(0,1) como código óptimo. Dos ideas pequeñas, mucha ganancia en perplexity.


Dónde el round-to-nearest deja dinero sobre la mesa

INT8 por-canal con round-to-nearest (RTN) es el baseline de la teoría 02. Su debilidad: cada peso se cuantiza de forma aislada. El error de cuantizar w_1 no influye en cómo cuantizamos w_2, aunque ambos se sumarán en el mismo producto escalar contra la misma activación x.

GPTQ (Frantar et al., 2022) observa: a la red solo le importa la salida del producto escalar, no los pesos individuales. Si ya hemos incurrido en error sobre w_1, podemos sesgar el redondeo de w_2 para compensarlo parcialmente.

El planteamiento

Considera una capa Linear con matriz de pesos W ∈ ℝ^(out×in) y una distribución de calibración de entradas X ∈ ℝ^(in×n_calib). La salida es Y = W X. Queremos elegir Ŵ (cuantizada) para minimizar

\[ \mathcal{L}(\hat{W}) = \mathbb{E}_x \| W x - \hat{W} x \|^2 = \text{tr}\big( (W - \hat{W})^\top H (W - \hat{W}) \big) \]

donde H = X X^\top / n es la Hessiana empírica de la distribución de entrada.

El hecho clave: H acopla las columnas de W a través de x. En concreto, la pérdida se descompone por fila de salida, así que tratamos cada fila independientemente — pero dentro de una fila, las in columnas están acopladas a través de H.

Por fila, con w el vector fila:

\[ \mathcal{L}(w, \hat{w}) = (w - \hat{w})^\top H (w - \hat{w}) \]

Queremos redondear cada w_i a un valor en la rejilla de cuantización, dejando que el resto de la fila recoja la holgura.

La actualización de GPTQ

Elige un orden de columnas — para INT4, la elección estándar es "en orden original" (sin permutación) porque reordenar rompe las convenciones de weight-tying. Para cada índice de columna i = 1, ..., in:

  1. Redondea w_i al valor más cercano de la rejilla: \hat{w}_i = \text{round-to-grid}(w_i).
  2. El error \delta_i = w_i - \hat{w}_i propagará a la pérdida.
  3. Distribuye \delta_i a las columnas restantes aún no cuantizadas i+1, ..., in de forma que, tras la re-cuantización, la contribución total a la pérdida sea mínima.

La redistribución óptima sale de la solución cerrada de un cuadrático sobre las columnas restantes. Saltando el álgebra lineal (ver solutions/03-gptq-derivation.md en la apertura de fase), la actualización para la columna j > i es:

\[ w_j \leftarrow w_j - \delta_i \cdot \frac{H_{ij}}{H_{ii}^{-1}}\ \text{(simplified)} \]

El algoritmo GPTQ real usa la descomposición de Cholesky de la Hessiana inversa L = \text{chol}(H^{-1}) y actualiza columnas vía

\[ w_j \leftarrow w_j - \delta_i \cdot L_{ji} \]

La estructura de Cholesky permite calcular todas las actualizaciones en O(in^2) por fila — coste total O(out · in^2), que es el mismo coste asintótico que un forward pass sobre la matriz. De ahí que GPTQ sobre un modelo de 7B sea un trabajo de "unos pocos minutos", no de "unas pocas horas".

Qué te compra realmente GPTQ

En INT4 solo de pesos:

Esquema Gap de PPL vs FP32 (LLaMA-7B, WikiText)
RTN por-tensor 7.3 (roto)
RTN por-grupo=128 1.2
GPTQ por-grupo=128 0.4

El cierre del gap es significativo: GPTQ ~3× más cerca de FP32 que RTN al mismo conteo de bytes. Para la Fase 26 sobre MiniGPT, el gap será menor en términos absolutos (el modelo es minúsculo) pero la mejora relativa debería reproducirse.

Qué supone GPTQ (y dónde se rompe)

  1. La distribución de calibración es representativa. Si H se calcula desde datos que no cubren la distribución de despliegue, la redistribución sesga mal. Elegir los datos de calibración es la parte de GPTQ menos discutida en la práctica.
  2. La Hessiana es definida positiva. Para distribuciones de activación muy deficientes de rango (p. ej., activaciones tras un cuello de botella de bajo rango), H es singular y necesitamos una pequeña perturbación diagonal H + \epsilon I. Arreglo estándar.
  3. Independencia entre filas. GPTQ no redistribuye errores entre filas de salida. Para las proyecciones de salida de atención donde las filas están altamente correlacionadas, esto es subóptimo — pero la subóptimaidad es pequeña.

NF4: una idea distinta

NF4 (NormalFloat 4-bit) de Dettmers et al. (2023, paper de QLoRA) abandona del todo la rejilla uniforme. Observación: los pesos pre-entrenados están aproximadamente distribuidos normalmente (media cero, varianzas similares por capa). Para tales distribuciones, el libro de código de 4 bits óptimo en teoría de la información es el conjunto de 16 cuantiles de N(0, 1):

\[ \{ q_k : P(Z \leq q_k) = k/16, \ Z \sim N(0,1), \ k = 0, ..., 15 \} \]

Estos cuantiles no están uniformemente espaciados. Son densos cerca de cero (donde vive el grueso de la distribución) y dispersos en las colas. La tabla de dequantización de NF4 es exactamente estos 16 números (con un bit de ajuste asimétrico para representar el cero).

El paso de cuantización: 1. Calcular la escala (scale) por-bloque s = max(|w|) para un bloque de 64 pesos. 2. Normalizar: w / s ∈ [-1, 1] aproximadamente. 3. Encontrar el valor más cercano de los 16 del libro de código NF4. 4. Almacenar el índice del libro de código de 4 bits.

El paso de dequantización: 1. Buscar el valor del libro de código por el índice. 2. Multiplicar por la escala (scale) almacenada.

Por qué NF4 supera a INT4 uniforme

Para un tensor distribuido normalmente, INT4 uniforme desperdicia resolución en las colas (donde casi no viven pesos) y empobrece el centro (donde vive la mayoría de pesos). NF4 lo invierte — rejilla densa cerca de cero, dispersa en las colas. El error total de redondeo (bajo la asunción de normalidad) cae ~30% al mismo conteo de bits.

Para la Fase 26 repasamos NF4 (fichero de teoría 04) y opcionalmente implementamos el lookup del libro de código como objetivo extendido. La implementación completa de GPTQ sobre un único Linear es el entregable evaluado.

El truco de la doble cuantización (QLoRA)

La "doble cuantización" de QLoRA: las propias escalas (una FP16 por bloque de 64 pesos) se cuantizan a FP8 con una segunda escala exterior. Ahorra ~0.5 bits por peso de media. Ingeniería pura; sin matemática nueva. Repasada, no implementada.

Cómo interactúa GPTQ con la granularidad de cuantización

GPTQ no reemplaza la cuantización por-grupo; se compone con ella:

  • INT4 por-tensor + GPTQ: mejor que INT4 RTN por-tensor, pero aún malo — escala demasiado gruesa.
  • INT4 por-grupo + GPTQ: configuración estándar. Tamaño de grupo 64 o 128.
  • INT8 por-canal + GPTQ: rara vez se hace — INT8 RTN ya está lo bastante cerca de FP32 como para que la sobrecarga de GPTQ no compense.

Para la Fase 26 probamos la fila del medio (INT4 por-grupo + GPTQ) contra INT4 por-grupo RTN para confirmar la ganancia de GPTQ sobre MiniGPT.

Coste de implementación en nuestro escenario

GPTQ sobre el Linear más grande de MiniGPT (asumamos Linear(768, 768)):

  • Calibración: 128 muestras × forward por esta capa = 128 vectores de longitud 768, registrados como una H de forma (768, 768). Almacenamiento: 4.6 MiB FP32 → despreciable.
  • Cholesky de H^{-1}: O(768^3) ≈ 4.5e8 FLOPs ≈ 2.3 segundos a 200 GFLOPS, o 23 segundos a los realistas 20 GFLOPS de Borja en un único hilo AVX2. Tratable en la CPU.
  • Actualizaciones por-fila: 768 filas × 768 columnas × 768 actualizaciones de columna = 4.5e8 FLOPs de nuevo. Mismo orden.
  • Total por capa: ~1 minuto. MiniGPT tiene ~12 capas de este tipo → ~12 minutos de calibración.

Dentro del presupuesto de wall-clock de la Fase 26.

Problemas de práctica

Soluciones en la apertura de fase en solutions/03-gptq-ref.md. Razona, no programes.

  1. Demuestra que la pérdida \mathcal{L}(\hat W) = \mathbb{E} \|Wx - \hat W x\|^2 es igual a \text{tr}((W - \hat W)^\top H (W - \hat W)) donde H = \mathbb{E}[x x^\top]. Indica la asunción.
  2. Para un vector de pesos 1×2 w = [3, 5] cuantizado a INT2 (4 niveles) con escala (scale) s = 5/1.5 = 10/3, el resultado RTN es \hat{w} = [10/3, 5] (round 3/s ≈ 0.9 → 1, round 5/s = 1.5 → 1 o 2). Calcula la pérdida bajo distribución de entrada con H = [[1, 0.5], [0.5, 1]]. Ahora aplica GPTQ: tras redondear w_1, ¿cuál es el nuevo valor óptimo para w_2 (en espacio real) antes de su propio round-to-grid? Compara pérdidas.
  3. Valores del libro de código NF4 para el caso simétrico de 8 cuantiles (cuantiles de 4 bits de |Z| con bit de signo). Deriva los 7 valores no nulos numéricamente (usando scipy.stats.norm.ppf mentalmente — solo da un orden de magnitud). ¿Por qué el menor valor no nulo es aproximadamente 0.13 y el mayor aproximadamente 3.5?

Resumen en un párrafo

GPTQ mejora el round-to-nearest calculando la Hessiana de las activaciones y usándola para redistribuir el error de cuantización entre columnas no cuantizadas — cerrando aproximadamente dos tercios del gap entre RTN por-grupo y FP32 a precisión de 4 bits. NF4 mejora la rejilla uniforme de 4 bits usando los cuantiles de una distribución normal como entradas del libro de código — explotando el hecho empírico de que los pesos pre-entrenados son aproximadamente normales. Ambas ideas se componen: NF4 + GPTQ es el modo por defecto de QLoRA. Para la Fase 26 implementamos GPTQ sobre un único Linear y repasamos NF4. El siguiente fichero repasa AWQ y SmoothQuant para completar la cultura general.

Siguiente: theory/04-awq-survey.md.