English · Español
Lab 01 — GPTQ sobre una única capa Linear¶
Objetivo: implementar GPTQ para un
Lineary mostrar que supera a RTN en INT4 por-grupo.Tiempo estimado: 6–10 horas (es la combinación de matemáticas+código más difícil de la Fase 26).
Prerrequisito: lab 00 commiteado; theory/03 leído; sección GPTQ de
src/miniquant/BLUEPRINT.mdleída.
Lo que produces¶
Un directorio experiments/26-gptq-toy/ que contiene:
gptq_toy.py— script que cuantiza unLinear(768, 768)sintético tanto con RTN como con GPTQ, imprime el tensor de pérdida y la proxy de PPL en el peor caso por fila.results.json— mediciones.loss_curve.png— MSE de cuantización por índice de fila para RTN vs GPTQ.manifest.json.README.md— interpretación.
Commiteas src/miniquant/gptq.py implementando el algoritmo.
El kernel¶
Implementa GPTQ para una matriz de pesos W ∈ ℝ^(out × in) con una distribución de calibration X ∈ ℝ^(in × n).
Bosquejo del algoritmo (el archivo de teoría 03 tiene la matemática):
H = X @ X.T / n # (in, in) Hessiana
H += eps * I # ridge para estabilidad, eps = 1e-2 * mean(diag(H))
Hinv_chol = cholesky(inverse(H)) # triangular superior L = chol(H^-1)
for row r in 0..out-1:
w = W[r, :].clone() # vector de longitud in
err = zeros(in)
for i in 0..in-1:
q_i = round_to_grid(w[i], scale=s_per_group[r, i // group_size])
delta_i = w[i] - dequant(q_i)
err[i] = delta_i
# actualizar columnas restantes i+1.. vía la fila de Cholesky
for j in i+1..in-1:
w[j] -= delta_i * Hinv_chol[i, j] / Hinv_chol[i, i]
store q_i into Q[r, i]
El coeficiente del bucle interno es exactamente lo que te dio el archivo de teoría 03. Puedes organizar la implementación por bloques (procesar columnas en chunks de 128) por velocidad; la salida del algoritmo es idéntica.
TODOs¶
Bloque A — montaje sintético¶
-
W ~ N(0, 0.02²)aleatoria de shape(768, 768)— coincide con un peso típico de capa intermedia. - Calibration
X ~ N(0, 1)de shape(768, 128)— 128 vectores aleatorios. - Grid objetivo: INT4 por-grupo, group size 64. (Escalas elegidas por grupo como
max(|w|)/7para INT4 simétrico.) - Calcula
H = X @ X.T / 128yH += eps * I.
Bloque B — implementa baseline RTN¶
- Cuantización independiente por elemento usando las escalas por-grupo.
- Calcula la pérdida
tr((W - W_rtn).T @ H @ (W - W_rtn)). Esta es tu métrica escalar de calidad.
Bloque C — implementa GPTQ¶
- Cholesky de
H^-1. PyTorch:torch.linalg.cholesky(torch.linalg.inv(H)). - Bucle sobre filas (vectoriza dentro de cada fila a lo largo de columnas), aplica la actualización.
- Cuantiza. Almacena como INT4 denso (puedes empacar dos-por-byte al final; para este lab basta con un
tensor[int8]que almacene valores simétricos en[-7, 7]— el registro de almacenamiento es complemento a dos[-8, 7], pero la cuantización simétrica conscale = max(|w|)/7solo emite códigos en[-7, 7]).
Bloque D — compara¶
- Calcula la pérdida para ambos esquemas.
- Pérdida por fila: ¿qué filas se beneficiaron más de GPTQ?
- Calcula
n_grid_changes: ¿cuántos pesos redondeó GPTQ a un punto de grid distinto al que habría redondeado RTN? (Habitualmente un porcentaje de un dígito.)
Bloque E — interpreta en README.md¶
Tres preguntas:
- ¿Cuál es la razón de pérdida GPTQ vs RTN? Espera
loss_gptq / loss_rtn ∈ [0.3, 0.7]para este montaje. Si es > 0.9, tu Cholesky está mal o la dirección de actualización está al revés. - ¿Qué filas se benefician más? Dibuja la pérdida por fila para ambos esquemas. Las filas con mayor pérdida en RTN deberían ver la mayor mejora absoluta. ¿Por qué?
- ¿Qué pasa si reemplazas
X ~ N(0, 1)porX ~ Cauchy(0, 1)? (Distribución con muchos outliers.) Re-ejecuta y reporta. GPTQ debería beneficiarse más porque el acoplamiento fuera-de-diagonal de la Hessiana es más fuerte.
Condiciones de parada¶
Terminado cuando:
src/miniquant/gptq.pyimplementa el algoritmo; los tests pasan.loss_gptq / loss_rtn < 0.7en el montaje estándar.- Los cinco archivos commiteados.
README.mdresponde las tres preguntas.
Restricciones¶
- Nada de librería GPTQ de referencia. Estás escribiendo el algoritmo. Existe el paquete
auto-gptq; no lo importes. - Solo CPU. Cholesky en
(768, 768)tarda menos de un segundo; el bucle de filas con actualizaciones explícitas es el cuello de botella. Cronométralo; apunta a < 30 segundos por capa. - Reproducibilidad:
seed_everything(42). Entoncestorch.linalg.choleskyes determinista para una entrada dada.
Trampas¶
- Cholesky falla con "matrix not positive-definite". Tu ridge
epses demasiado pequeño. Pruebaeps = 1e-1 * mean(diag(H)). loss_gptq > loss_rtn. Bugs más comunes: (i) estás restandodelta * Hinv_chol[j, i]en vez deHinv_chol[i, j](triángulo equivocado); (ii) tu redondeo a grid calculadeltacomodequant(q) - wen vez dew - dequant(q); (iii) estás aplicando las actualizaciones después de cuantizar el resto de la fila, no antes.- La pérdida de GPTQ es exactamente igual a la de RTN. La actualización es silenciosamente un no-op porque
deltaredondea a cero (tu escala es demasiado gruesa). Sanity-check: las magnitudes dedeltano son triviales.
Cuándo consultar solutions/¶
Tras cumplir todas las condiciones de parada. Referencia en solutions/01-gptq-toy-ref.md (apertura de fase) recorre la derivación paso a paso y muestra los números esperados dentro de tolerancia.
Siguiente lab: lab/02-quant-curve.md.