El ajuste fino completo (full fine-tuning) de un modelo de 7B requiere 56GB de VRAM. No tienes eso. Tampoco la mayoría de las empresas. LoRA te permite ajustar el mismo modelo con 6GB al entrenar menos del 1% de los parámetros. Esto no es un compromiso: iguala la calidad del ajuste fino completo en la mayoría de las tareas. Todo el ecosistema de ajuste fino de código abierto funciona con este único truco.
Tipo: Build
Idiomas: Python
Prerrequisitos: Fase 10, Lección 06 (Instruction Tuning / SFT)
Tiempo: ~75 minutos
Relacionado: La Fase 10 cubre los bucles SFT/DPO desde cero. Esta lección los integra con los toolkits de PEFT de 2026 (PEFT, TRL, Unsloth, Axolotl, LLaMA-Factory).
Objetivos de Aprendizaje
Implementar LoRA inyectando matrices adaptadoras de bajo rango (A y B) en las capas de atención de un modelo preentrenado
Calcular el ahorro de parámetros de LoRA frente al ajuste fino completo: un rango r con dimensiones d_model entrena 2rd parámetros en lugar de d^2
Ajustar un modelo utilizando QLoRA (base cuantizada de 4 bits + adaptadores LoRA) para que quepa en la memoria GPU de consumo
Fusionar los pesos de LoRA de vuelta en el modelo base para el despliegue y comparar la velocidad de inferencia con y sin adaptadores
El Problema
Tienes un modelo base. Llama 3 8B. Quieres que responda a los tickets de soporte al cliente con la voz de tu empresa. SFT es la respuesta. Pero SFT tiene un problema de costo.
El ajuste fino completo actualiza cada parámetro del modelo. Llama 3 8B tiene 8 mil millones de parámetros. En fp16, cada parámetro ocupa 2 bytes. Eso es 16GB solo para cargar los pesos. Durante el entrenamiento, también necesitas gradientes (16GB), estados del optimizador para Adam (32GB para momentum + varianza) y activaciones. Total: aproximadamente 56GB de VRAM para un solo modelo de 8B.
Una GPU A100 de 80GB apenas puede albergar esto. Dos A100 cuestan entre $3 y $4 por hora en proveedores de la nube. Entrenar durante 3 épocas con 50,000 ejemplos toma de 6 a 10 horas. Eso equivale a $30-40 por experimento. Realiza 10 experimentos para ajustar bien los hiperparámetros y habrás gastado $400 antes de desplegar nada.
Escala esto a Llama 3 70B y los números se vuelven absurdos. 140GB solo para los pesos. Necesitas un clúster. Más de
00 por experimento.
Existe un problema aún más profundo. El ajuste fino completo modifica cada peso del modelo. Si realizas un ajuste fino con datos de soporte al cliente, podrías degradar las capacidades generales del modelo. Esto se conoce como olvido catastrófico (catastrophic forgetting). El modelo mejora en tu tarea específica y empeora en todo lo demás.
Necesitas un método que entrene menos parámetros, use menos memoria y no destruya el conocimiento existente del modelo.
El Concepto
LoRA: Low-Rank Adaptation
Edward Hu y sus colegas en Microsoft publicaron LoRA en junio de 2021. El hallazgo clave del artículo: las actualizaciones de peso durante el ajuste fino tienen un rango intrínseco bajo. No necesitas actualizar los 16.7 millones de parámetros en una matriz de pesos de 4096x4096. La información útil de la actualización se puede capturar con una matriz de rango 16 o 32.
Aquí están las matemáticas. Una capa lineal estándar calcula:
y = Wx
Donde W es una matriz d_out x d_in. Para una proyección de atención de 4096x4096, eso es 16,777,216 parámetros.
LoRA congela W y añade una descomposición de bajo rango:
y = Wx + BAx
Donde B es (d_out x r) y A es (r x d_in). El rango r es mucho menor que d, típicamente 8, 16 o 32.
Para r=16 en una capa de 4096x4096:
Parámetros originales: 4096 x 4096 = 16,777,216
Parámetros LoRA: (4096 x 16) + (16 x 4096) = 65,536 + 65,536 = 131,072
Reducción: 131,072 / 16,777,216 = 0.78%
Estás entrenando el 0.78% de los parámetros y obteniendo entre el 95% y el 100% de la calidad.
graph LR
X["Entrada x"] --> W["W Congelada (d x d)"]
X --> A["A (r x d)"]
A --> B["B (d x r)"]
W --> Plus["+ (fusionar)"]
B --> Plus
Plus --> Y["Salida y"]
style W fill:#1a1a2e,stroke:#e94560,color:#fff
style A fill:#0f3460,stroke:#16213e,color:#fff
style B fill:#0f3460,stroke:#16213e,color:#fff
A se inicializa con una distribución gaussiana aleatoria. B se inicializa en cero. Esto significa que la contribución de LoRA comienza en cero; el modelo inicia el entrenamiento a partir de su comportamiento original y aprende gradualmente la adaptación.
El Factor de Escala: Alpha
LoRA introduce un factor de escala alpha que controla cuánto afecta la actualización de bajo rango a la salida:
y = Wx + (alpha / r) * BAx
Cuando alpha = r, la escala es de 1x. Cuando alpha = 2r (el valor por defecto común), la escala es de 2x. Este hiperparámetro controla la tasa de aprendizaje del camino de LoRA independientemente de la tasa de aprendizaje base.
Guía práctica:
alpha = 2 * rank es una convención común de la comunidad (el artículo original utilizó alpha = rank en la mayoría de los experimentos)
alpha = rank proporciona una escala de 1x, conservadora pero estable
Un alpha más alto significa actualizaciones más grandes por paso, lo que puede acelerar la convergencia o causar inestabilidad
Dónde Aplicar LoRA
Un transformer tiene muchas capas lineales. No necesitas añadir LoRA a todas ellas. El artículo original probó diferentes combinaciones:
Capas Objetivo
Parámetros Entrenables (7B)
Calidad
solo q_proj
4.7M
Buena
q_proj + v_proj
9.4M
Mejor
q_proj + k_proj + v_proj + o_proj
18.9M
La mejor para atención
Todas las lineales (atención + MLP)
37.7M
Ganancia marginal, 2x parámetros
El punto óptimo para la mayoría de las tareas: q_proj + v_proj. Esto apunta a las proyecciones de consulta (query) y valor (value) en la autoatención, que controlan a qué atiende el modelo y qué información extrae. Añadir capas MLP ayuda en tareas complejas como la generación de código, pero duplica el recuento de parámetros para obtener retornos decrecientes en tareas más simples.
Selección de Rango (Rank)
El rango r controla la expresividad de la adaptación:
Rango (Rank)
Parámetros Entrenables (por capa)
Ideal Para
4
32,768
Clasificación simple, análisis de sentimiento
8
65,536
Preguntas y respuestas de dominio único, resumen
16
131,072
Tareas multidominio, seguimiento de instrucciones
32
262,144
Razonamiento complejo, generación de código
64
524,288
Retornos decrecientes para la mayoría de las tareas
128
1,048,576
Raramente justificado
Hu et al. demostraron que r=4 ya captura la mayor parte de la adaptación para tareas simples. r=8 y r=16 son las opciones más comunes en la práctica. Ir más allá de r=64 rara vez mejora la calidad y comienza a perder la ventaja de memoria de LoRA.
QLoRA: Cuantización de 4 Bits + LoRA
Tim Dettmers y sus colegas en la Universidad de Washington publicaron QLoRA en mayo de 2023. La idea: cuantizar el modelo base congelado a una precisión de 4 bits, y luego superponer adaptadores LoRA en fp16.
Esto cambia drásticamente la ecuación de memoria:
Método
Memoria de Pesos (7B)
Memoria de Entrenamiento (7B)
GPU Requerida
Ajuste fino completo (fp16)
14GB
~56GB
1x A100 80GB
LoRA (base fp16)
14GB
~18GB
1x A100 40GB
QLoRA (base 4 bits)
3.5GB
~6GB
1x RTX 3090 24GB
QLoRA realiza tres contribuciones técnicas:
NF4 (Normal Float 4-bit): Un nuevo tipo de datos diseñado específicamente para los pesos de redes neuronales. Los pesos de las redes neuronales siguen una distribución aproximadamente normal. NF4 sitúa sus 16 niveles de cuantización en los cuantiles de una distribución normal estándar. Esto es óptimo desde el punto de vista de la teoría de la información para datos distribuidos normalmente. Pierde menos información que la cuantización uniforme de 4 bits (INT4) o la estándar Float4.
Cuantización doble (Double quantization): Las constantes de cuantización en sí mismas ocupan memoria. Cada bloque de 64 pesos necesita un factor de escala fp32 (4 bytes). Para un modelo de 7B, eso representa 0.4GB adicionales. La cuantización doble cuantiza estas constantes a fp8, reduciendo la sobrecarga a 0.1GB. Es pequeño, pero suma.
Optimizadores paginados (Paged optimizers): Durante el entrenamiento, los estados del optimizador (el momentum y la varianza de Adam) pueden superar la memoria de la GPU en secuencias largas. Los optimizadores paginados utilizan la memoria unificada de NVIDIA para paginar automáticamente los estados del optimizador en la memoria RAM de la CPU cuando se agota la memoria de la GPU, y los vuelven a paginar cuando es necesario. Esto evita las caídas por falta de memoria (OOM) a costa de un poco de rendimiento (throughput).
La Cuestión de la Calidad
¿Reducir parámetros o cuantizar la base perjudica la calidad? Los resultados de múltiples artículos científicos:
Método
MMLU (5-shot)
MT-Bench
HumanEval
Ajuste fino completo (Llama 2 7B)
48.3
6.72
14.6
LoRA r=16
47.9
6.68
14.0
QLoRA r=16 (NF4)
47.5
6.61
13.4
QLoRA r=64 (NF4)
48.1
6.70
14.2
LoRA con r=16 está dentro del 1% del ajuste fino completo en la mayoría de los puntos de referencia (benchmarks). QLoRA con r=16 pierde otra fracción de porcentaje. QLoRA con r=64 prácticamente iguala al ajuste fino completo mientras consume un 90% menos de memoria.
Costos en el Mundo Real
Ajuste fino de Llama 3 8B en 50,000 ejemplos (3 épocas):
Método
GPU
Tiempo
Costo
Ajuste fino completo
2x A100 80GB
8 horas
~$32
LoRA r=16
1x A100 40GB
4 horas
~$8
QLoRA r=16
1x RTX 4090 24GB
6 horas
~$5
QLoRA r=16 (Unsloth)
1x RTX 4090 24GB
2.5 horas
~
QLoRA r=16
1x T4 16GB
12 horas
~$4
QLoRA en una sola GPU de consumo cuesta menos que un almuerzo. Esta es la razón por la que la comunidad de ajuste fino de pesos abiertos explotó en 2023 y por la que todos los entornos de entrenamiento a continuación incorporan QLoRA por defecto en 2026.
El stack de PEFT en 2026
Entorno (Framework)
Qué es
Elige cuando
Hugging Face PEFT
La biblioteca canónica para LoRA/QLoRA/DoRA/IA3
Quieres un control directo y tu bucle de entrenamiento ya está en transformers.Trainer
TRL
Entrenadores de aprendizaje por refuerzo a partir de comentarios (SFT, DPO, GRPO, PPO, ORPO) de HF
Necesitas DPO/GRPO después de SFT; construido sobre PEFT
Unsloth
Reescritura en kernel de Triton de los pases hacia adelante y hacia atrás
Quieres una aceleración de 2 a 5 veces más rápida + la mitad de VRAM sin pérdida de precisión; familias Llama/Mistral/Qwen
Axolotl
Envoltorio con configuración YAML sobre PEFT + TRL + DeepSpeed + Unsloth
Buscas ejecuciones de entrenamiento reproducibles y con control de versiones
LLaMA-Factory
GUI/CLI/API sobre PEFT + TRL
Quieres un ajuste fino sin código; más de 100 familias de modelos compatibles
torchtune
Recetas nativas de PyTorch, sin dependencia de transformers
Deseas dependencias mínimas y tu organización ya está estandarizada en PyTorch
Regla general: uso de investigación o experimento único → PEFT. Pipeline de producción repetible → Axolotl con kernels Unsloth habilitados. Prototipado rápido y desechable → LLaMA-Factory.
Fusión de Adaptadores
Después del entrenamiento, tienes dos cosas: el modelo base congelado y un adaptador LoRA pequeño (normalmente de 10 a 100MB). Puedes hacer lo siguiente:
Mantenerlos separados: Cargar el modelo base y superponer el adaptador. Intercambiar adaptadores para diferentes tareas. Así es como sirves múltiples variantes ajustadas a partir de un modelo base.
Fusionarlos permanentemente: Calcular W' = W + (alpha/r) * BA y guardar el resultado como un modelo completo nuevo. El modelo fusionado es del mismo tamaño que el original. Sin sobrecarga de inferencia. Sin adaptadores que gestionar.
Para servir múltiples tareas (adaptador de soporte al cliente, adaptador de código, adaptador de traducción), mantenlos separados. Para desplegar un único modelo especializado, fusiónalos.
Técnicas avanzadas de fusión para combinar múltiples adaptadores:
TIES-Merging (Yadav et al. 2023): Recorta parámetros de pequeña magnitud, resuelve conflictos de signos y luego fusiona. Reduce la interferencia entre adaptadores.
DARE (Yu et al. 2023): Elimina aleatoriamente parámetros del adaptador antes de fusionarlos y escala el resto. Sorprendentemente eficaz para combinar capacidades.
Aritmética de tareas (Task arithmetic): Suma o resta pesos de adaptadores. Sumar un adaptador de "código" y otro de "matemáticas" a menudo genera un modelo que es bueno en ambos.
Cuándo NO Ajustar un Modelo
El ajuste fino es la tercera opción, no la primera.
Primero: ingeniería de prompts (prompt engineering). Escribe un mejor prompt del sistema. Añade ejemplos few-shot (de pocos disparos). Utiliza cadena de pensamiento (chain-of-thought). Esto no cuesta nada y toma minutos. Si los prompts te llevan al 80% del camino, probablemente no necesites un ajuste fino.
Segundo: RAG. Si el modelo necesita conocer datos específicos (documentos, base de conocimientos, catálogo de productos), la recuperación de información es más barata y fácil de mantener que grabarla en los pesos. Consulta la Lección 06.
Tercero: ajuste fino. Utiliza esto cuando necesites que el modelo adopte un estilo, formato o patrón de razonamiento específico que no se pueda lograr a través de prompts. Cuando necesites salidas estructuradas consistentes. Cuando necesites destilar un modelo grande en uno más pequeño. Cuando la latencia sea clave y no puedas permitirte el costo de tokens adicionales de los prompts few-shot.
graph TD
Start["¿Necesitas mejorar el comportamiento del modelo?"] --> PE["Intenta ingeniería de prompts"]
PE -->|"Funciona"| Done["Despliégalo"]
PE -->|"No es suficiente"| RAG["¿Necesitas conocimiento externo?"]
RAG -->|"Sí"| RAGBuild["Construye un pipeline RAG"]
RAG -->|"No, necesito cambiar estilo/formato"| FT["Ajusta con LoRA/QLoRA"]
RAGBuild -->|"Funciona"| Done
RAGBuild -->|"También necesito cambiar el estilo"| FT
FT --> Done
style Start fill:#1a1a2e,stroke:#e94560,color:#fff
style Done fill:#0f3460,stroke:#16213e,color:#fff
Constrúyelo
Implementamos LoRA desde cero en PyTorch puro. Sin bibliotecas. Sin magia. Construirás la capa LoRA, la inyectarás en un modelo, la entrenarás y volverás a fusionar los pesos.
A se inicializa con valores aleatorios escalados. B se inicializa en cero. El producto BA comienza en cero, por lo que el modelo empieza con su comportamiento original.
Paso 2: Capa Lineal Envuelta con LoRA
class LinearWithLoRA(nn.Module):
def __init__(self, linear, rank=8, alpha=16):
super().__init__()
self.linear = linear
self.lora = LoRALayer(
linear.in_features, linear.out_features, rank, alpha
)
for param in self.linear.parameters():
param.requires_grad = False
def forward(self, x):
return self.linear(x) + self.lora(x)
La capa lineal original se congela. Solo los parámetros de LoRA (A y B) son entrenables.
Paso 3: Inyectar LoRA en un Modelo
def inject_lora(model, target_modules, rank=8, alpha=16):
for param in model.parameters():
param.requires_grad = False
lora_layers = {}
for name, module in model.named_modules():
if isinstance(module, nn.Linear):
if any(t in name for t in target_modules):
parent_name = ".".join(name.split(".")[:-1])
child_name = name.split(".")[-1]
parent = dict(model.named_modules())[parent_name]
lora_linear = LinearWithLoRA(module, rank, alpha)
setattr(parent, child_name, lora_linear)
lora_layers[name] = lora_linear
return lora_layers
Primero, congela cada parámetro del modelo. Luego recorre el árbol del modelo, busca capas lineales que coincidan con tus nombres objetivo y reemplázalas por versiones envueltas con LoRA. Las matrices A y B de LoRA son los únicos parámetros entrenables en todo el modelo.
Paso 4: Contar Parâmetros
def count_parameters(model):
total = sum(p.numel() for p in model.parameters())
trainable = sum(p.numel() for p in model.parameters() if p.requires_grad)
frozen = total - trainable
return {
"total": total,
"trainable": trainable,
"frozen": frozen,
"trainable_pct": 100 * trainable / total if total > 0 else 0
}
Paso 5: Fusionar los Pesos de Vuelta
def merge_lora_weights(model):
for name, module in model.named_modules():
if isinstance(module, LinearWithLoRA):
with torch.no_grad():
merged = (
module.lora.A @ module.lora.B
) * module.lora.scaling
module.linear.weight.data += merged.T
parent_name = ".".join(name.split(".")[:-1])
child_name = name.split(".")[-1]
if parent_name:
parent = dict(model.named_modules())[parent_name]
else:
parent = model
setattr(parent, child_name, module.linear)
Después de la fusión, las capas de LoRA desaparecen. El modelo tiene el mismo tamaño que el original con la adaptación integrada en los pesos. Sin sobrecarga de inferencia.
Esto simula la cuantización de 4 bits mapeando los pesos en 16 niveles discretos dentro de bloques de 64. QLoRA en producción utiliza la biblioteca bitsandbytes para implementar NF4 real en la GPU.
Paso 7: Bucle de Entrenamiento
def train_lora(model, data, epochs=5, lr=1e-3, batch_size=4):
optimizer = torch.optim.AdamW(
[p for p in model.parameters() if p.requires_grad], lr=lr
)
criterion = nn.MSELoss()
losses = []
for epoch in range(epochs):
epoch_loss = 0.0
n_batches = 0
indices = torch.randperm(len(data["inputs"]))
for i in range(0, len(indices), batch_size):
batch_idx = indices[i:i + batch_size]
x = data["inputs"][batch_idx]
y = data["targets"][batch_idx]
output = model(x)
loss = criterion(output, y)
optimizer.zero_grad()
loss.backward()
optimizer.step()
epoch_loss += loss.item()
n_batches += 1
avg_loss = epoch_loss / n_batches
losses.append(avg_loss)
return losses
La demo crea un modelo pequeño, inyecta LoRA en dos capas, lo entrena y vuelve a fusionar los pesos. El recuento de parámetros pasa de totalmente entrenable a aproximadamente un 1% entrenable durante el entrenamiento de LoRA, y luego regresa a la arquitectura original después de la fusión.
Úsalo
Con el ecosistema de Hugging Face, implementar LoRA en un modelo real requiere unas 20 líneas:
from transformers import AutoModelForCausalLM, AutoTokenizer
from peft import LoraConfig, get_peft_model, TaskType
model = AutoModelForCausalLM.from_pretrained("meta-llama/Llama-3.1-8B")
tokenizer = AutoTokenizer.from_pretrained("meta-llama/Llama-3.1-8B")
lora_config = LoraConfig(
task_type=TaskType.CAUSAL_LM,
r=16,
lora_alpha=32,
lora_dropout=0.05,
target_modules=["q_proj", "v_proj"],
)
model = get_peft_model(model, lora_config)
model.print_trainable_parameters()
Para QLoRA, añade la cuantización con bitsandbytes:
from transformers import BitsAndBytesConfig
bnb_config = BitsAndBytesConfig(
load_in_4bit=True,
bnb_4bit_quant_type="nf4",
bnb_4bit_compute_dtype=torch.bfloat16,
bnb_4bit_use_double_quant=True,
)
model = AutoModelForCausalLM.from_pretrained(
"meta-llama/Llama-3.1-8B",
quantization_config=bnb_config,
device_map="auto",
)
model = get_peft_model(model, lora_config)
Eso es todo. Mismo bucle de entrenamiento. Mismo pipeline de datos. El modelo base ahora reside en 4 bits, los adaptadores LoRA se entrenan en fp16 y todo cabe en 6GB.
El adaptador guardado es de 10 a 100MB. El modelo base permanece intacto. Puedes compartir adaptadores en Hugging Face Hub sin necesidad de redistribuir el modelo completo.
Despliégalo
Esta lección produce:
outputs/prompt-lora-advisor.md — un prompt que te ayuda a decidir el rango (rank) de LoRA, los módulos objetivo y los hiperparámetros para tu tarea específica
outputs/skill-fine-tuning-guide.md — una habilidad que enseña a los agentes el árbol de decisión sobre cuándo y cómo ajustar un modelo
Ejercicios
Estudo de ablación del rango (Rank ablation study). Ejecuta la demo con rangos 2, 4, 8, 16, 32 y 64. Grafica la pérdida final frente al rango. Encuentra el punto de retornos decrecientes donde duplicar el rango ya no reduce la pérdida a la mitad. Para una tarea de clasificación simple en características de 256 dimensiones, esto debería estar al rededor de r=8-16.
Comparación de módulos objetivo. Modifica inject_lora para apuntar solo a la capa "0", solo a la capa "2", solo a la capa "4", y a las tres. Entrena cada variante durante 20 épocas. Compara la velocidad de convergencia y la pérdida final. Esto refleja la decisión real de apuntar a q_proj frente a v_proj frente a todas las capas lineales.
Análisis de errores de cuantización. Toma las matrices de pesos del modelo entrenado antes y después de quantize_to_nf4 / dequantize_from_nf4. Calcula el error cuadrático medio, el error absoluto máximo y la correlación entre los pesos originales y reconstruidos. Experimenta con valores de block_size de 32, 64, 128 y 256.
Servicio multiadaptador (Multi-adapter serving). Entrena dos adaptadores LoRA en diferentes subconjuntos de datos (índices pares frente a impares). Guarda ambos adaptadores. Carga el modelo base una vez, luego intercambia los adaptadores y verifica que cada uno produzca salidas diferentes con la misma entrada. Así es como los sistemas de producción sirven múltiples modelos ajustados a partir de una sola base.
Inferencia fusionada frente a no fusionada. Compara la salida del modelo LoRA antes y después de merge_lora_weights con las mismas 100 entradas. Verifica que las salidas sean idénticas (dentro de una tolerancia de punto flotante de 1e-5). Luego realiza un benchmark de la velocidad de inferencia para ambos: el fusionado debería ser ligeramente más rápido ya que es una única multiplicación de matriz en lugar de dos.
Términos Clave
Término
Lo que la gente dice
Lo que realmente significa
LoRA
"Ajuste fino eficiente"
Low-Rank Adaptation: congela los pesos base y entrena dos matrices pequeñas A y B cuyo producto aproxima la actualización completa de pesos
QLoRA
"Ajuste fino en una laptop"
Quantized LoRA: carga el modelo base en NF4 de 4 bits, entrena adaptadores LoRA en fp16 por encima, lo que permite el ajuste fino de un 7B con 6GB de VRAM
Rango / Rank (r)
"Cuánto puede aprender el modelo"
La dimensión interna de las matrices A y B; controla la expresividad frente a la cantidad de parámetros
Alpha
"Tasa de aprendizaje de LoRA"
Factor de escala aplicado a la salida de LoRA; alpha/r escala la contribución de la adaptación a la salida final
NF4
"Cuantización de 4 bits"
Normal Float 4: un tipo de datos de 4 bits con niveles de cuantización en los cuantiles de la distribución normal, óptimo para pesos de redes neuronales
Adaptador (Adapter)
"La pequeña parte entrenada"
Las matrices A y B de LoRA guardadas como un archivo independiente (10-100MB), que se pueden cargar sobre cualquier copia del modelo base
Módulos objetivo (Target modules)
"A qué capas aplicar LoRA"
Las capas lineales específicas (q_proj, v_proj, etc.) en las que se inyectan los adaptadores LoRA
Fusión (Merging)
"Integrarlo de forma permanente"
Computar W + (alpha/r) * BA y reemplazar el peso original, eliminando la sobrecarga del adaptador en la inferencia
Optimizadores paginados
"Evitar OOM durante el entrenamiento"
Descarga de estados del optimizador (momentum de Adam, varianza) a la CPU cuando se agota la memoria de la GPU
Olvido catastrófico
"El ajuste fino rompió todo lo demás"
Cuando la actualización de todos los pesos hace que el modelo pierda capacidades previamente aprendidas
Lecturas Adicionales
Hu et al., "LoRA: Low-Rank Adaptation of Large Language Models" (2021) — el artículo original que introduce el método de descomposición de bajo rango, probado en GPT-3 175B con un rango tan bajo como 4
Dettmers et al., "QLoRA: Efficient Finetuning of Quantized Language Models" (2023) — presenta NF4, cuantización doble y optimizadores paginados, lo que permite el ajuste fino de un 65B en una sola GPU de 48GB
Documentación de la biblioteca PEFT (huggingface.co/docs/peft) — la biblioteca estándar para LoRA, QLoRA y otros métodos de eficiencia de parámetros en el ecosistema de Hugging Face
Yadav et al., "TIES-Merging: Resolving Interference When Merging Models" (2023) — técnicas para combinar múltiples adaptadores LoRA sin degradación de la calidad
Documentación de TRL — referencia oficial para SFTTrainer, DPOTrainer, KTOTrainer y la superficie de integración con PEFT/bitsandbytes/Unsloth.
Documentación de Unsloth — kernels fusionados que duplican el rendimiento del ajuste fino y reducen la memoria a la mitad; la capa de rendimiento bajo TRL.
Documentación de Axolotl — entrenador de SFT/DPO/QLoRA multi-GPU configurado por YAML; la alternativa de configuración como código para scripts escritos a mano.