Saltar al contenido
← Programa de inteligencia artificial

Inteligencia artificial / Material de consulta

Adaptación de un transformer

Transformers + PEFT · CPU

Consulte las explicaciones, el código y las salidas de referencia en esta página.

Volver al aula →

Especialización · LoRA en un transformer diminuto

Overtone Education · Curso de inteligencia artificial

Objetivo

Ejecutar adaptación LoRA real con PyTorch, Transformers y PEFT en CPU. Se crea un GPT-2 diminuto con pesos aleatorios y tokens numéricos sintéticos. No descarga pesos preentrenados ni demuestra calidad de lenguaje. Su propósito es comprobar parámetros congelados, actualizaciones de bajo rango y backpropagation.

Preparación

Ejecute de arriba abajo en Jupyter con Python 3.12 (entorno validado). Entorno externo al aula. Instale en un entorno virtual:

python -m pip install numpy torch transformers peft ipykernel

Las versiones comprobadas se distribuyen en requirements-ia.txt. No necesita claves de API. Los ejemplos son educativos y no validan aplicaciones de impacto alto.

Pasos

1. Construir un modelo pequeño

Dos capas, dos cabezas y vocabulario de 32 tokens permiten observar la mecánica sin GPU. Para adaptar un modelo preentrenado real necesitará verificar licencia, memoria, datos y configuración de su arquitectura.

Código de referencia

import math
import torch
import transformers
import peft
from transformers import GPT2Config, GPT2LMHeadModel
from peft import LoraConfig, get_peft_model
torch.manual_seed(7)
torch.set_num_threads(2)
config = GPT2Config(vocab_size=32,n_positions=32,n_embd=32,n_layer=2,n_head=2,bos_token_id=1,eos_token_id=2,pad_token_id=0,resid_pdrop=0,embd_pdrop=0,attn_pdrop=0)
base = GPT2LMHeadModel(config)
print({"torch":torch.__version__,"transformers":transformers.__version__,"peft":peft.__version__})

Salida comprobada

{'torch': '2.14.1', 'transformers': '5.20.2', 'peft': '0.21.2'}

2. Añadir adaptadores

c_attn es específico de GPT-2. Sus proyecciones usan una convención que requiere fan_in_fan_out=True. No copie nombres de módulos a otra arquitectura sin inspeccionarla.

Código de referencia

lora = LoraConfig(r=4,lora_alpha=8,lora_dropout=0,target_modules=["c_attn"],fan_in_fan_out=True,task_type="CAUSAL_LM")
modelo = get_peft_model(base,lora)
total = sum(p.numel() for p in modelo.parameters())
entrenables = sum(p.numel() for p in modelo.parameters() if p.requires_grad)
print({"parametros_totales":total,"entrenables":entrenables,"fraccion":entrenables/total})
assert 0<entrenables<total

Salida comprobada

{'parametros_totales': 28544, 'entrenables': 1024, 'fraccion': 0.03587443946188341}

3. Secuencias sintéticas de entrenamiento

Los tokens 0 son padding y se excluyen de la pérdida mediante -100. Las secuencias son artificiales y diminutas; no use la caída de pérdida como prueba de comprensión ni de generalización lingüística.

Código de referencia

ids = torch.tensor([[1,3,4,5,6,2,0,0],[1,4,5,6,7,2,0,0],[1,5,6,7,8,2,0,0],[1,6,7,8,9,2,0,0]])
mascara = (ids!=0).long()
labels = ids.masked_fill(ids==0,-100)
modelo.eval()
with torch.no_grad():
    inicial = float(modelo(input_ids=ids,attention_mask=mascara,labels=labels).loss)
print("Pérdida inicial:",round(inicial,4))

Salida comprobada

Pérdida inicial: 3.4919

4. Optimizar solo los adaptadores

El optimizador recibe parámetros entrenables. Conservamos una copia de un peso base congelado para comprobar que no cambia. En una adaptación real reserve datos de evaluación y controle también regresiones de capacidades previas.

Código de referencia

nombre_base,peso_base = next((n,p) for n,p in modelo.named_parameters() if not p.requires_grad)
antes = peso_base.detach().clone()
opt = torch.optim.AdamW((p for p in modelo.parameters() if p.requires_grad),lr=.02)
modelo.train()
for paso in range(30):
    opt.zero_grad()
    loss = modelo(input_ids=ids,attention_mask=mascara,labels=labels).loss
    loss.backward()
    opt.step()
modelo.eval()
with torch.no_grad():
    final = float(modelo(input_ids=ids,attention_mask=mascara,labels=labels).loss)
print({"perdida_inicial":round(inicial,4),"perdida_final_train":round(final,4)})

Salida comprobada

{'perdida_inicial': 3.4919, 'perdida_final_train': 3.112}

Comprobaciones

Se comprueba ejecución, parámetros congelados y gradientes de los adaptadores. Esta pérdida usa entrenamiento y no estima rendimiento fuera de él.

Código de referencia

assert math.isfinite(inicial) and math.isfinite(final)
assert torch.equal(antes,peso_base.detach())
assert any("lora_" in n and p.requires_grad for n,p in modelo.named_parameters())
assert any(p.grad is not None and torch.isfinite(p.grad).all() for p in modelo.parameters() if p.requires_grad)
print("Comprobaciones aprobadas; el peso base verificado no cambió")

Salida comprobada

Comprobaciones aprobadas; el peso base verificado no cambió

Siguientes pasos

  1. Añada validación sintética realmente reservada y mida pérdida fuera de entrenamiento.
  2. Compare rangos 2, 4 y 8 con el mismo presupuesto y semilla.
  3. Antes de usar from_pretrained, revise licencia, memoria, tokenizador y formato de diálogo del modelo elegido.
  4. Compare prompt, RAG y ajuste antes de invertir en adaptación.

Referencia: https://huggingface.co/docs/peft/main/en/conceptual_guides/lora