English · Español
Lab 03 — Preview de QLoRA¶
🇪🇸 Combinar lo aprendido: cuantizar MiniGPT a NF4 con
src/miniquant/, encima poner LoRA consrc/minituner/, y entrenar una época. La meta NO es ganar accuracy — es comprobar que el patrón compone correctamente y medir el ahorro de memoria (modesto en MiniGPT, dramático a escala 7B+).
Anclas: src/miniquant/BLUEPRINT.md (Fase 26), src/minituner/BLUEPRINT.md, theory/03-memory-footprint.md.
Qué produces¶
Un único experimento experiments/28-qlora-preview/:
- Un script de entrenamiento que compone cuantización NF4 + LoRA sobre MiniGPT.
- Una tabla de medición de memoria comparando LoRA con base fp16 vs QLoRA con base NF4.
- Una comprobación de corrección: las salidas de QLoRA coinciden con LoRA de base fp16 (mismo adapter entrenado) dentro del error de cuantización NF4.
Prerrequisito¶
- Lab 02 completo: corrida de entrenamiento de LoRA r=8 con
experiments/28-lora-finetune/adapter_final.ptguardado. src/miniquant/quantize.pyfuncionando:quantize_symmetric_per_groupyQuantizedLinear(de la Fase 26).src/minituner/qlora.pyimplementado según BLUEPRINT.
TODOs (esbozo)¶
Bloque A — Componiendo base NF4 + LoRA¶
- Carga MiniGPT base (fp16).
- Aplica cuantización NF4 in-place vía
src/miniquant/: cadann.Linearse convierte en unQuantizedLinearcon frozen weights NF4 + escalas por grupo. - Aplica LoRA encima vía
src/minituner/qlora.py::wrap_minigpt_qlora(model, r=8, alpha=16.0, quant_scheme="nf4_per_group_64"). - Confirma:
model.layer.attn.q_projes ahora unQuantizedLinearenvuelto en unLoRALinear. El forward eslora_branch(x) + qlinear_dequant(x).
Bloque B — Comprobación de corrección de la salida¶
- Carga el adapter entrenado del lab 02 (
experiments/28-lora-finetune/adapter_final.pt). - Reconstruye dos modelos:
- Modelo A: base fp16 + adapter LoRA.
- Modelo B: base NF4 + adapter LoRA (mismos pesos del adapter).
- Pasa un batch fijo de prompts de test por ambos. Mide
mean_abs_diffde los logits de salida. - Esperado: el diff es pequeño pero no nulo (la cuantización NF4 añade ~1-3% de error sobre las magnitudes de logits). Documenta el valor real.
Bloque C — Una época de entrenamiento de QLoRA¶
- Desde el checkpoint inicial del lab 02, ejecuta una época adicional de entrenamiento QLoRA.
- El objetivo no es la accuracy — es que el bucle de entrenamiento se ejecute sin NaN/Inf y la pérdida disminuya.
- Registra: train loss por paso + comprobación de NaN/Inf máximos.
Bloque D — Medición de memoria¶
Para ambos regímenes (LoRA base fp16, QLoRA base NF4):
- Usa
psutil.Process().memory_info().rsspara CPU. (Otorch.cuda.max_memory_allocated()si estás en GPU — la Fase 28 no requiere GPU.) - Mide justo después de construir el modelo (sólo pesos).
- Mide a mitad del entrenamiento (pesos + grads + estado de Adam, todo asignado).
- Reporta el delta como la "huella de entrenamiento".
Bloque E — Demo de swap de adapter (opcional)¶
- Guarda el adapter entrenado con QLoRA.
- Carga el adapter entrenado en fp16 del lab 02 en el mismo modelo de base NF4.
- Confirma que ambos forward pass funcionan; las salidas difieren. (Demuestra el patrón de adapter-swap que QLoRA habilita a escala.)
Restricciones¶
- La cuantización NF4 ocurre antes de envolver con LoRA. Invertir el orden rompe el flujo de gradientes de la rama LoRA (LoRA espera aplicarse sobre un módulo tipo
Linear, no sobre pesos cuantizados crudos). - Los params LoRA se quedan en fp16. No los cuantices. Su tamaño diminuto significa que cuantizar no compra nada y complica los gradientes.
- Sin datos de entrenamiento nuevos. Usa los splits del lab 02.
- Manifest obligatorio. Como siempre:
seed_everything,manifest.jsoncon versiones + semilla + hash base + hash del adapter. - Una sola semilla. No barras — sólo preview.
Condiciones de parada¶
Has terminado cuando:
- El Modelo B (base NF4 + adapter) produce salidas dentro del error tolerado de cuantización NF4 respecto al Modelo A (base fp16 + adapter). Documenta la tolerancia y el diff medido.
- Una época de entrenamiento QLoRA se completa sin NaN/Inf.
- Mediciones de memoria commiteadas: una tabla de 2 filas (LoRA base fp16 vs QLoRA base NF4) con pesos / grads / Adam / total. Ratio calculado.
- REPORT.md anota: "el ahorro absoluto a escala MiniGPT es X MiB; a escala 7B la misma receta ahorraría Y GiB", con la Y derivada de los números de theory 03.
Trampas (específicas de esta práctica)¶
- NaN en el dequant NF4. Si
QuantizedLinear.forwarddecuantiza a un tensor con NaN, el gradiente LoRA explota. Añade untorch.isfiniteassert en el camino de dequant y otro en el camino de LoRA. La Fase 26 ya debería haber depurado esto; reverifica. - Cargar el adapter en un modelo envuelto con QuantizedLinear. Los nombres diferirán (
q_proj.base.weightvsq_proj.qweight). Elload_lora_state_dictdesrc/minituner/lora.pydebería apuntar sólo a params LoRA — pero verifica. - Medición de memoria bajo la asignación lazy de PyTorch. Las mediciones de RSS antes del primer backward pass subreportan — el allocator sólo confirma cuando hace falta. Mide tras al menos 2 pasos de entrenamiento.
requires_grad=Truesobre el intermedio decuantizado. Algunas implementaciones de QuantizedLinear crean un tensor decuantizado fp16 fresco en forward — asegúrate de que no se añade al conjunto de parámetros; es un transitorio. El conjunto entrenable debe seguir siendo sólo LoRA.- Interacciones de mixed precision. Si el forward de MiniGPT usa alguna mezcla de fp32/fp16, el intermedio de dequant NF4 debe coincidir con la precisión de la salida del forward LoRA. Mismatch → promoción silenciosa de tipos → bug de memoria.
Cuándo consultar las soluciones¶
Tras que el entrenamiento QLoRA logre una época y la tabla de memoria esté rellenada. Compara tu cálculo de Y a escala 7B con el de la solución de referencia.
Tiempo estimado¶
3-5 horas.
La Fase 28 termina aquí. Escribe PHASE_28_REPORT.md y learners/borja/phase-28/reflections.md. Abre la Fase 29 sólo tras aprobación explícita.