085 — Cuantización e inferencia local
Parte: 06 — Modelos fundacionales e ingeniería de LLM
Nivel: avanzado · Horas estimadas: 6
Laboratorio: neural · Estado: EXECUTABLE_CORE
🎯 Propósito
Comprender cuantización e inferencia local dentro de la evolución de la inteligencia artificial, implementar un experimento mínimo verificable y distinguir qué parte constituye evidencia frente a una afirmación todavía no comprobada.
📚 Resultados de aprendizaje
Al finalizar podrás:
- Explicar cuantización e inferencia local usando los conceptos
cuantización,GGUF,ONNX,local. - Ejecutar el laboratorio con una semilla explícita y revisar su contrato JSON.
- Identificar al menos un supuesto, una limitación y un riesgo de aplicación.
- Comparar el enfoque con la etapa anterior de la ruta de aprendizaje.
- Producir una evidencia reproducible y una conclusión que no exceda los datos.
🧩 Conceptos centrales
cuantización, GGUF, ONNX, local
🗺️ Ubicación en el mapa de la IA
La cuantización es la palanca que baja un LLM del datacenter al portátil: representa los pesos (FP16, 2 bytes) con enteros de 8 o 4 bits, dividiendo la memoria por 2–4× y acelerando el decode (limitado por ancho de banda, clase 084). Junto con formatos como GGUF y runtimes como llama.cpp, habilita la inferencia local — clave cuando la privacidad manda (clase 086) — y ya apareció en el entrenamiento con QLoRA (077). Aquí se estudia como técnica de inferencia.
📖 Fundamentos
🔢 Cuantización afín (asimétrica) y simétrica
Mapear reales x ∈ [x_min, x_max] a enteros q de b bits:
Asimétrica (con zero-point):
escala s = (x_max − x_min) / (2^b − 1)
zero z = round(−x_min / s)
q = clamp( round(x/s) + z , 0 , 2^b − 1 )
x̂ = s · (q − z) (descuantización)
Simétrica (habitual para pesos, centrados en 0):
s = max|x| / (2^(b−1) − 1) (INT8: divisor 127)
q = clamp( round(x/s) , −127 , 127 )
x̂ = s · q
El error de redondeo por elemento está acotado por s/2: la clave es mantener s pequeña. Por eso se cuantiza por grupos (una escala por fila, por canal o por bloque de 32–128 pesos) en lugar de una escala global: un solo peso gigante no arruina a todos los demás.
🚨 Outliers: el problema real en LLMs
LLM.int8() (Dettmers et al., 2022) mostró que a partir de ~6,7B parámetros aparecen dimensiones de activación con valores atípicos sistemáticos (outliers) que destruyen la cuantización ingenua de activaciones. Solución: descomposición mixta — las ~0,1 % de dimensiones outlier se computan en FP16 y el resto en INT8. Los métodos modernos de solo pesos esquivan el problema dejando las activaciones en FP16:
- GPTQ: cuantiza capa por capa minimizando el error de salida (aproximación con información de segundo orden), compensando cada peso redondeado ajustando los restantes.
- AWQ: observa qué canales de peso son "salientes" según la magnitud de las activaciones y los protege reescalando antes de cuantizar (sin reentrenar).
📦 PTQ vs QAT, y la escalera de bits
PTQ (post-training quantization) cuantiza un modelo ya entrenado con un pequeño set de calibración: barato, es lo estándar en LLMs. QAT (quantization-aware training) simula la cuantización durante el entrenamiento: mejor calidad a bits muy bajos, pero exige reentrenar. Regla empírica en LLMs: INT8 es casi gratis (perplejidad ≈ igual); 4 bits bien hecho (GPTQ/AWQ/NF4, con grupos) pierde poco; 3 bits duele; 2 bits suele ser inaceptable sin técnicas especiales.
🖥️ GGUF y llama.cpp: inferencia local
llama.cpp es un runtime en C/C++ para CPU (y GPU parcial) cuyo formato GGUF
empaqueta en un solo archivo pesos cuantizados + tokenizador + metadatos. Sus
esquemas Q4_K_M, Q5_K_M, Q8_0, etc. son cuantizaciones por bloques con
distintas mezclas de bits por tipo de tensor. Lectura práctica de un nombre:
llama-8B.Q4_K_M.gguf ≈ 8B parámetros × ~0,57 bytes ≈ 4,9 GB: corre en un portátil
con 8 GB de RAM. La inferencia local ofrece privacidad total, costo marginal cero y
latencia sin red, a cambio de calidad y throughput menores que un modelo grande
servido en GPU.
🧮 Ejemplo trabajado
Cuanticemos a INT8 simétrico el vector de pesos w = [0,40, −0,21, 0,95, −0,88]:
s = max|w| / 127 = 0,95 / 127 = 0,00748
q = round(w/s) = [ round(53,5) , round(−28,1) , round(127,0) , round(−117,6) ]
= [ 53 , −28 , 127 , −118 ] ← ojo: 53,5 redondea a 53 (par) o 54 según la regla
x̂ = s·q = [0,3965, −0,2094, 0,9500, −0,8826]
error absoluto = [0,0035, 0,0006, 0,0000, 0,0026] (≤ s/2 = 0,0037) ✓
Ahora añade un outlier: w' = [0,40, −0,21, 0,95, −0,88, 12,0]
s' = 12,0/127 = 0,0945 → los pesos pequeños caen en q ∈ {4, −2, 10, −9}:
x̂' = [0,378, −0,189, 0,945, −0,850] error hasta 0,031 (≈ 8× peor).
Con cuantización por grupos (outlier en su propio grupo), los demás conservan
su escala fina: esa es TODA la motivación de escalas por bloque.
Memoria de un 8B: FP16 = 16 GB; INT8 = 8 GB; 4 bits (Q4_K_M ≈ 4,55 bpw) ≈ 4,6 GB.
📊 Propiedades y comparación
| Método | Bits | Necesita calibración | Pérdida típica | Uso característico |
|---|---|---|---|---|
| LLM.int8() | 8 (mixto FP16) | No | ≈ 0 | Cargar modelos grandes en menos VRAM |
| GPTQ | 4–3 | Sí (pequeño set) | Baja en 4 bits | Serving GPU eficiente |
| AWQ | 4 | Sí (activaciones) | Baja, robusto | Serving GPU/edge |
| NF4 (QLoRA) | 4 | No | Baja | Base congelado para fine-tuning |
| GGUF Q4_K_M / Q8_0 | ~4,5 / 8 | No | Baja / ≈ 0 | Inferencia local llama.cpp |
| QAT | 8–2 | Reentrenar | Mínima al mismo bit | Cuando PTQ no alcanza |
flowchart TD
A[Modelo FP16] --> B{Estrategia}
B -->|PTQ solo pesos| C[GPTQ / AWQ / NF4]
B -->|PTQ mixta| D[LLM.int8: outliers en FP16]
B -->|QAT| E[Reentrenar simulando cuantizacion]
C --> F[Empaquetar]
D --> F
E --> F
F --> G[GGUF + llama.cpp: CPU local]
F --> H[Kernels INT8/INT4 en GPU: vLLM y otros]
G --> I[Privacidad y costo marginal cero]
H --> J[Mas throughput por GPU]
⚠️ Errores conceptuales frecuentes
- "Cuantizar es solo redondear." Sin escalas por grupo y manejo de outliers, el error explota; el diseño está en cómo se reparte la precisión.
- "INT4 divide la calidad a la mitad." La relación bits-calidad no es lineal: INT8 ≈ sin pérdida, 4 bits pierde poco, 2 bits colapsa.
- "La cuantización acelera siempre." Acelera el decode porque mueve menos bytes (cuello de memoria); si el cuello es cómputo (prefill largo, batch grande), la ganancia puede ser pequeña o nula.
- "La perplejidad igual garantiza el mismo comportamiento." Métricas agregadas esconden degradación en tareas específicas (código, matemáticas, idiomas minoritarios); hay que evaluar por tarea.
- "Cuantizar activaciones y pesos da lo mismo." Las activaciones tienen outliers dinámicos y son mucho más difíciles; por eso dominan los métodos weight-only en LLMs.
🚀 Del aprendizaje a la operación
Para producción u uso local serio faltan: elegir el esquema según hardware real (kernels disponibles importan más que los bits teóricos), evaluar el modelo cuantizado con TUS evals y no solo perplejidad, verificar la procedencia de checkpoints cuantizados de terceros (un GGUF es un binario que ejecutas), y documentar la cadena modelo base → método → versión de runtime, porque "el mismo modelo" en Q4 de dos fuentes distintas puede comportarse distinto.
🧪 Laboratorio
python lab.py
El laboratorio llama a ai_evolution.labs.run_lab("neural"). Esta
decisión evita 183 implementaciones divergentes: cada clase tiene un entrypoint
propio, pero los motores didácticos se prueban como una biblioteca común.
🔍 Evidencia esperada
- tipo de laboratorio y semilla;
- entradas o decisiones observables;
- resultado estructurado;
- lista
evidencecon hechos que pueden inspeccionarse; - lista
limitationsque impide presentar la demo como producción.
📓 Notebooks
- 📓
notebook.ipynb: recorrido guiado con la materia resumida. - ✍️
notebook_student.ipynb: ejercicios para resolver. - ✅
notebook_solution.ipynb: solución de referencia explicada.
📝 Evaluación
| Criterio | Peso |
|---|---|
| Comprensión conceptual | 25 % |
| Ejecución reproducible | 25 % |
| Interpretación basada en evidencia | 25 % |
| Riesgos, límites y mejora propuesta | 25 % |
Consulta assessment.md para preguntas y criterio de aceptación.
⚠️ Errores comunes
| Síntoma | Causa probable | Corrección |
|---|---|---|
| El código corre, pero no hay conclusión | Se confundió ejecución con aprendizaje | Explica qué demuestra y qué no demuestra |
| El resultado cambia sin explicación | No se registró semilla o configuración | Conserva semilla, versión y parámetros |
| Se promete uso real | Se extrapoló desde una demo educativa | Declara entorno, datos, límites y revisión humana |
| Se copia una métrica aislada | No existe baseline ni costo de error | Añade comparación y criterio de decisión |
❓ Preguntas frecuentes
¿Debo usar una API comercial?
No. El núcleo funciona localmente. Las extensiones LIVE se documentan por separado.
¿El laboratorio representa una implementación industrial?
No por sí solo. Enseña el contrato y el patrón; producción exige integración,
seguridad, observabilidad, pruebas y operación.
¿Dónde profundizo?
Revisa las especializaciones enlazadas en el README raíz y la ruta siguiente.
🔗 Referencias
- Dettmers et al. (2022), LLM.int8(): 8-bit Matrix Multiplication for Transformers at Scale: https://arxiv.org/abs/2208.07339 — uso: fuente primaria del mecanismo estudiado
- Frantar et al. (2022), GPTQ: Accurate Post-Training Quantization for Generative Pre-trained Transformers: https://arxiv.org/abs/2210.17323 — uso: fuente primaria del mecanismo estudiado
- Lin et al. (2023), AWQ: Activation-aware Weight Quantization for LLM Compression and Acceleration: https://arxiv.org/abs/2306.00978 — uso: fuente primaria del mecanismo estudiado
- Dettmers et al. (2023), QLoRA: Efficient Finetuning of Quantized LLMs (NF4): https://arxiv.org/abs/2305.14314 — uso: fuente primaria del mecanismo estudiado
- llama.cpp (formato GGUF y runtime local): https://github.com/ggerganov/llama.cpp — uso: referencia consultada en su fuente original
📜 Papers que fundamentan esta clase
Bloque generado por
python scripts/link_papers_to_classes.py. La fuente espapers/catalog/papers.json.
| Paper | Año | Qué desbloqueó | Miniatura |
|---|---|---|---|
| P35 · FlashAttention: atención exacta, rápida y eficiente en memoria, consciente de la E/S | 2022 | El cuello de botella de la atención no eran los FLOPs sino las lecturas y escrituras a memoria. Y la solución es EXACTA, no aproximada. | notebook |
| P49 · QLoRA: ajuste fino eficiente de modelos cuantizados | 2023 | Pone el ajuste fino de un modelo muy grande al alcance de una sola GPU de consumo. | notebook |
Cada ficha explica el problema anterior, la matemática mínima, los límites y los errores de atribución más frecuentes. Para leerlas con método: cómo leer un paper de IA · anexos matemáticos.
📚 Bibliografía de apoyo
Bloque generado por
python scripts/link_sources_to_classes.py. Cada obra lleva su localizador verificado ensources/bibliography.json.
Los papers dicen de dónde salió el mecanismo. Estas obras lo desarrollan con el espacio que una clase no tiene: teoría completa, demostraciones y ejercicios.
| Obra | Edición | Localizador | Papel en esta clase |
|---|---|---|---|
| Jurafsky, Daniel y Martin, James H. — Speech and Language Processing | 2.ª (la 3.ª circula como borrador abierto sin ISBN) · 2009 | ISBN 9780131873216 · web de la obra | obra de referencia de la parte 06 · capítulos de modelos de lenguaje y transformadores |
| Goodfellow, Ian, Bengio, Yoshua y Courville, Aaron — Deep Learning | 2016 | ISBN 9780262035613 · web de la obra | obra de referencia de la parte 06 · optimización y entrenamiento a escala |
⬅️ Clase anterior
084 — Serving, batching y cachés
➡️ Siguiente clase
086 — Selección de modelo, costo, latencia y privacidad
📝 Evaluación completa
❓ Preguntas
- Define cuantización e inferencia local sin usar una marca o framework como definición.
- Explica la relación entre cuantización, GGUF, ONNX, local.
- Ejecuta
lab.pydos veces con la misma semilla. ¿Qué debe conservarse? - Identifica una afirmación permitida y una afirmación exagerada sobre el resultado.
- Propón una prueba negativa o un caso límite.
🏆 Reto verificable
Amplía el resultado del laboratorio con una clave student_extension que incluya:
- el supuesto que estás probando;
- una medición o comprobación;
- la conclusión;
- una limitación.
✅ Criterio de aceptación
- [ ]
lab.pytermina con código 0. - [ ] El resultado contiene
kind,seed,evidenceylimitations. - [ ] La extensión no modifica el comportamiento de otras clases.
- [ ] La interpretación referencia datos impresos por el laboratorio.
- [ ] Se declara al menos un riesgo o condición de no uso.