English · Español
03 — Huella de memoria: Full FT vs LoRA vs QLoRA¶
La memoria durante entrenamiento se va en cuatro cubetas: pesos, gradientes, estados del optimizador, activaciones. Full FT paga por las cuatro, todas multiplicadas por
N(params). LoRA paga las tres primeras sólo por los pocos params entrenables — los pesos siguen ahí pero sin gradiente ni estado de Adam. QLoRA además aplana los pesos congelados (freeze) a NF4.
Las cuatro cubetas¶
Durante el entrenamiento, la memoria de GPU se consume por:
- Pesos — los parámetros del modelo en sí.
- Gradientes — un buffer por parámetro, acumulado durante el backward.
- Estado del optimizador — primer y segundo momentos de Adam (
m,v) por parámetro. - Activaciones — tensores intermedios guardados durante el forward para uso en el backward.
Más una pequeña cantidad de overhead (workspace, metadata del allocator) — ignorado.
Para cada cubeta, los bytes totales escalan con el número de parámetros o la huella de activaciones. Los escaladores dominantes son:
- Por parámetro: pesos (2B fp16 o 4B fp32), grads (lo mismo que pesos), Adam (2 × 4B fp32 = 8B en precisión mixta porque los estados de Adam se mantienen en fp32 por estabilidad).
- Por token de contexto por capa: activaciones (depende mucho de la arquitectura y del gradient checkpointing).
Huella de full FT¶
Para un modelo con N parámetros en entrenamiento de precisión mixta:
- Pesos:
2Nbytes (fp16) +4Nbytes (copia maestra fp32 para Adam) =6N. - Gradientes:
2Nbytes (grads fp16). - Estado de Adam:
8Nbytes (m y v en fp32). - Activaciones: depende — típicamente
~ batch × seq_len × hidden × layers × small_constbytes.
Coste total por parámetro (excluyendo activaciones): 16 N bytes.
Para un modelo de 7B: 16 × 7e9 = 112 GiB. Más activaciones: fácilmente otros 20–40 GiB para batch sizes sensatos.
Por esto entrenar modelos de 7B+ requiere configuraciones multi-GPU (típicamente 4-8 A100s para full FT).
Huella de LoRA¶
LoRA congela la base. La base contribuye sólo a la cubeta 1 (pesos), una sola vez, en precisión de inferencia.
- Pesos base congelados:
2Nbytes (fp16). Sin copia maestra fp32, sin buffer de gradiente, sin estado de Adam. Sólo los pesos, usados para el forward pass. - Params entrenables de LoRA: llámalo
N_lora. En precisión mixta:6 N_lora(fp16 + master fp32) +2 N_lora(grads) +8 N_lora(Adam) =16 N_lora. - Activaciones: igual que full FT — depende de la arquitectura. LoRA añade un par de pequeñas ops forward extra pero no cambia materialmente la memoria de activaciones.
Total: 2N + 16 N_lora bytes + activaciones.
Para LLaMA-7B con LoRA r=8 (N_lora ≈ 19M según theory 02):
- Base:
2 × 7e9 = 14 GiB. - Cubetas LoRA:
16 × 19e6 = 304 MiB. - Total (sin activaciones): ~14.3 GiB.
Comparado con los 112 GiB de full FT: ~8× menos.
Huella de QLoRA¶
QLoRA cuantiza la base a NF4 (4 bits por peso). La base congelada ocupa ahora:
0.5Nbytes (empaquetado NF4: 2 pesos por byte).- Más las escalas NF4: típicamente
4 × N / 64 = N/16bytes (una escala fp16 por bloque de 64 pesos).
Base congelada total: ~0.56 N bytes. 3.6× más pequeño que fp16.
Los params entrenables de LoRA se mantienen en fp16: 16 N_lora bytes en total. Sin cambios respecto a LoRA.
Para LLaMA-7B con QLoRA r=8:
- Base (NF4):
0.56 × 7e9 = 3.9 GiB. - LoRA:
304 MiB. - Total (sin activaciones): ~4.2 GiB.
Comparado con los 112 GiB de full FT: ~27× menos. Comparado con los 14 GiB de LoRA fp16: ~3.3× menos.
Una GPU de consumo de 24 GiB (RTX 4090, A10G) puede albergar fine-tuning QLoRA de un modelo de 13B con espacio para activaciones. Este es el desbloqueo que hizo posible la explosión de fine-tuning LLM open-source de 2023.
¿Y el gradient checkpointing?¶
El gradient checkpointing intercambia cómputo por memoria: en lugar de guardar la activación de cada capa para el backward, guarda sólo algunas — y recalcula el resto bajo demanda durante el backward. La configuración estándar "guardar cada N-ésima capa" da una reducción ~sqrt(L) en memoria de activaciones a coste de un único forward pass extra por backward (~30% más cómputo).
Universal a través de full FT, LoRA, QLoRA. No cambia la huella relativa de los tres regímenes — sólo encoge la cubeta de activaciones en todos ellos.
¿Y los estados de optimizador paginados?¶
La receta completa de QLoRA también usa estados de optimizador paginados: la Unified Memory CUDA de NVIDIA te deja asignar el estado de Adam en RAM de CPU, y el driver lo pagina dentro/fuera según se necesite. El resultado: los 8 N_lora bytes del estado de Adam no tienen que estar simultáneamente en memoria de GPU.
Para nuestro pequeño MiniGPT, el estado de Adam es lo bastante pequeño como para que la paginación sea innecesaria. Para QLoRA de 70B, es lo que marca la diferencia entre caber en una A100 y no.
Mencionamos pero no implementamos Adam paginado en la Fase 28.
Tabla de ablación de memoria¶
Para MiniGPT (Fase 17, ~85M params) con r=8, precisión mixta fp16, batch size sensato:
| Configuración | Pesos | Grad | Adam | Total entrenable | Base congelada | Total aprox. |
|---|---|---|---|---|---|---|
| Full FT (fp16+fp32 master) | 510 MiB | 170 MiB | 680 MiB | 1.36 GiB | — | ~1.4 GiB |
| LoRA r=8 | 20.8 MiB | 2.6 MiB | 10.4 MiB | 33.8 MiB | 170 MiB | ~200 MiB |
| QLoRA r=8 | 20.8 MiB | 2.6 MiB | 10.4 MiB | 33.8 MiB | 48 MiB | ~80 MiB |
(Números redondeados; no incluyen activaciones. Para un benchmark real sobre el hardware de Borja, el lab 03 mide valores reales vía torch.cuda.max_memory_allocated.)
La reducción es modesta en términos absolutos para MiniGPT — pero las ratios relativas coinciden con los números a escala LLaMA. El propósito de la vista previa de QLoRA en la Fase 28 no es ahorrar memoria que no necesitamos; es demostrar la receta y medir las ratios.
Qué escala cuando N es más grande¶
La tabla anterior es por parámetro; escala linealmente con N para las cubetas 1–3. Así que los números de 7B y 70B que citamos se siguen directamente del mismo coste por parámetro.
La memoria de activaciones escala distinto — por token y por capa, mayoritariamente independiente de N para una longitud de secuencia y batch size fijos. Por esto el gradient checkpointing es un eje separado de LoRA: LoRA fija las cubetas 2–3, pero las activaciones son la cubeta 4 y necesitan su propio tratamiento.
La innovación tripartita de QLoRA¶
Dettmers et al. (QLoRA, 2023) combinaron tres cosas:
- Cuantización NF4 de la base congelada. Teoría: archivo 03 de la Fase 26. Reduce la cubeta 1.
- Doble cuantización de las escalas NF4. Cuantiza las propias escalas fp16 por bloque a FP8, ahorrando otros ~0.5 bytes por peso en promedio. Despreciable para nuestro tamaño de modelo; cubierto en la Fase 26.
- Estados de optimizador paginados. Estado de Adam en RAM de CPU, paginado dentro/fuera. Reduce la cubeta 3 dinámicamente.
Juntos: un modelo de 7B se hace fine-tuning en <6 GiB.
Para la Fase 28 usamos sólo (1). (2) y (3) son añadidos conceptuales; implementarlos como debe ser requiere pegamento específico de hardware (bitsandbytes para optimizadores paginados) que distrae del algoritmo central.
Implicaciones de la máquina de Borja¶
learners/borja/profile.md señala: sólo-CPU en la Fase 28. Espera — pero el entrenamiento necesita una GPU, ¿no?
Sí. La Fase 23+ asumió un paso a GPU en la nube. Para la Fase 28, la plataforma cloud está decidida (decisión tomada en la Fase 23). Las ejecuciones de fine-tuning del lab 02 ocurren en GPU cloud. El fallback de CPU es "calcular la matemática de conteo de params y ejecutar un dry run de 1 epoch; el entrenamiento completo está en cloud".
Las mediciones de memoria para la vista previa de QLoRA necesitan una GPU real; los resultados numéricos sintéticos pueden aproximarse en CPU. El DoD requiere la ejecución en cloud.
Ejercicios de práctica¶
Soluciones al abrir la fase en solutions/03-memory-footprint-ref.md.
- Un modelo de 13B con LoRA r=16 a lo largo de todos los Linears. Calcula
N_lora. Luego calcula la memoria total en tres regímenes (full FT, LoRA fp16, QLoRA NF4). Compara con una GPU de consumo de 24 GiB. - El truco de "doble cuantización" de QLoRA cuantiza las escalas por bloque (una fp16 por cada 64 pesos) a FP8 con una escala externa por bloque de 256. Calcula el overhead por peso antes y después. Muestra los ~0.5 bits/peso de ahorro.
- El estado de Adam es
2 × 4N = 8Nbytes en fp32. Si el estado de Adam se mantiene en fp16 (o se cuantiza vía estados paginados), ¿cómo cambia la memoria total? Argumenta por qué fp32 es la elección convencional. - El gradient checkpointing intercambia un 30% extra de cómputo por
~sqrt(L)menos memoria de activaciones. Estima el impacto en tiempo de pared sobre una ejecución de fine-tuning si el cuello de botella es el cómputo (no la memoria). ¿Sigue teniendo sentido el checkpointing?
Recapitulación en un párrafo¶
La memoria de entrenamiento tiene cuatro cubetas: pesos, gradientes, estado del optimizador, activaciones. El fine-tuning completo paga por las cuatro escaladas por el N total. LoRA paga las cubetas 2–3 sólo sobre el pequeño N_lora de params entrenables, dejando la base congelada en la cubeta 1 sin almacenamiento extra. QLoRA adicionalmente comprime la cubeta 1 a ~0.56 bytes/param vía cuantización NF4, más opcionalmente pagina el estado de Adam a memoria CPU. El efecto combinado es ahorros de memoria de un orden de magnitud: un modelo de 7B que necesitaba 112 GiB para full FT cabe en 4 GiB con QLoRA. La Fase 28 demuestra la receta sobre MiniGPT (donde los ahorros absolutos son pequeños pero las ratios coinciden) y adelanta QLoRA. El siguiente archivo de teoría repasa el alignment training (DPO/RLHF) por completitud cultural.
Siguiente: theory/04-alignment-survey.md.