GRATÍCULAinstrumento de maestría
BancoRTX 5090 · GB202
Rev2026.06
Entrar
N2 · Post-training + RL/L4

Preference optimization: DPO, SimPO, KTO

Objetivo de maestría

entender por qué DPO puede alinear con preferencias sin entrenar un reward model ni hacer RL, derivar su "reward implícito", y elegir entre DPO/SimPO/KTO según los datos que tengas.


4.1El problema: imitar no basta

SFT (L3) enseña a imitar "la respuesta correcta". Pero a menudo no hay una única correcta: hay respuestas mejores y peores (más concisas, más seguras, mejor tono). Para eso necesitas comparaciones: "para este prompt, A es mejor que B".

El enfoque clásico (InstructGPT/RLHF) era: (1) entrenar un reward model sobre comparaciones, (2) hacer PPO usando ese reward. Caro y frágil. DPO (Rafailov et al. 2023) demostró que puedes saltarte ambos pasos.


4.2De Bradley-Terry al objetivo de DPO (la derivación que importa)

Modelo de preferencias de Bradley-Terry: la probabilidad de que la respuesta y_w (winner) se prefiera a y_l (loser) dado un prompt x depende de una función de recompensa latente r:

P(y_w ≻ y_l | x) = σ( r(x, y_w) − r(x, y_l) )      # σ = sigmoide

El RLHF clásico aprende r y luego optimiza la política π para maximizar r con una restricción KL hacia una referencia π_ref. La idea genial de DPO: la solución óptima de ese problema con restricción KL tiene forma cerrada, y permite expresar el reward en términos de la propia política:

r(x, y) = β · log( π(y|x) / π_ref(y|x) )  +  constante

Es decir, el reward está implícito en cuánto la política se desvía de la referencia. Sustituyendo en Bradley-Terry, el reward model desaparece y queda una pérdida que se optimiza directamente sobre la política con datos de preferencia:

L_DPO = − E[ log σ( β·log(π(y_w|x)/π_ref(y_w|x)) − β·log(π(y_l|x)/π_ref(y_l|x)) ) ]

Lectura intuitiva: sube la probabilidad de la respuesta preferida y baja la de la rechazada, relativo a la referencia, con fuerza controlada por β. Sin reward model, sin RL, sin muestreo online — es aprendizaje supervisado sobre pares.

  • β (beta): controla cuánto puede alejarse la política de π_ref. β alto = conservador (cerca de la referencia); β bajo = agresivo (más cambio, más riesgo de degradar). Típico 0.1.

4.3Variantes que debes conocer

  • SimPO: elimina el modelo de referencia (usa la longitud media como normalización). Más barato en memoria (no cargas π_ref), a veces igual de bueno. Útil cuando la VRAM aprieta.
  • KTO (Kahneman-Tversky Optimization): no necesita pares; usa señales binarias sueltas (👍/👎 por respuesta). Es lo que quieres cuando tienes feedback de tipo "esto estuvo bien / mal" pero no comparaciones emparejadas — mucho más fácil de recopilar en producción.
  • ORPO: combina SFT y preferencias en una sola fase (sin referencia, sin SFT previo). Eficiente.

Cuándo cada uno: pares disponibles → DPO; VRAM justa → SimPO; solo señales binarias → KTO; quieres una sola fase → ORPO.


4.4Laboratorio L4.1 — DPO sobre tu modelo SFT

Partimos del adaptador SFT (L3) y lo alineamos con preferencias. Para datos genéricos usamos un dataset de preferencias estándar; en tu caso real, las preferencias salen de comparar salidas de tu propio modelo.

python
1# lab_n2l4_dpo.py — DPO sobre un modelo ya SFT, con Unsloth + TRL
2from unsloth import FastLanguageModel
3from datasets import load_dataset
4from trl import DPOConfig, DPOTrainer
5
6model, tok = FastLanguageModel.from_pretrained(
7    "unsloth/Qwen3-4B-Instruct", max_seq_length=2048, load_in_4bit=True)
8model = FastLanguageModel.get_peft_model(model, r=16, lora_alpha=16,
9    target_modules=["q_proj","k_proj","v_proj","o_proj","gate_proj","up_proj","down_proj"],
10    use_gradient_checkpointing="unsloth")
11
12# Dataset de preferencias: cada fila tiene prompt, chosen, rejected
13ds = load_dataset("trl-lib/ultrafeedback_binarized", split="train[:5000]")
14
15trainer = DPOTrainer(
16    model=model, ref_model=None,          # con PEFT, la referencia = base sin adaptador (TRL lo gestiona)
17    args=DPOConfig(
18        beta=0.1,                         # fuerza de la preferencia (ver 4.2)
19        per_device_train_batch_size=2, gradient_accumulation_steps=4,
20        learning_rate=5e-6,               # DPO usa LR MUCHO menor que SFT
21        num_train_epochs=1, logging_steps=20,
22        optim="paged_adamw_8bit", max_length=1024, max_prompt_length=512,
23        output_dir="./dpo_out",
24    ),
25    train_dataset=ds, processing_class=tok,
26)
27trainer.train()

Líneas no triviales:

  • ref_model=None con PEFT: TRL usa el base sin adaptadores como π_ref (desactiva el adaptador para calcular la referencia). No necesitas cargar un segundo modelo → ahorras VRAM. Esto es clave para que DPO quepa en la 5090.
  • learning_rate=5e-6: DPO es mucho más sensible que SFT; LR alto colapsa la política. Típico 1e-6 a 5e-6.
  • beta=0.1: empieza aquí; si el modelo se degrada (olvida capacidades), súbelo; si no cambia nada, bájalo.
  • Métricas a vigilar en wandb: rewards/chosen, rewards/rejected, rewards/margins (debe crecer), y rewards/accuracies (fracción de pares donde chosen > rejected; debe subir hacia 1).

4.5Laboratorio L4.2 — Generar tus propios pares de preferencia (genérico)

En la práctica, los mejores pares salen de tu propio modelo: generas varias respuestas, las puntúas (con una métrica, un juez, o feedback humano) y formas pares (mejor=chosen, peor=rejected).

python
1# pseudocódigo del patrón "on-policy preference data"
2# 1) para cada prompt, genera N respuestas con el modelo SFT (temperature>0)
3# 2) puntúa cada una (verificador, métrica, o LLM-as-judge local — un Qwen3-14B en tu 5090)
4# 3) chosen = argmax score, rejected = argmin score
5# 4) DPO sobre esos pares
6# Ventaja: las preferencias son "on-policy" (sobre lo que tu modelo realmente genera),
7# lo que suele alinear mejor que datasets genéricos.

4.6Ejercicios

E1. Entrena DPO con beta=0.1 y beta=0.5. Compara rewards/margins y la calidad final. ¿Qué hace β a la agresividad del cambio?

E2. Implementa el patrón de L4.2: genera 4 respuestas por prompt con tu SFT, púntalas con exact-match (en una tarea verificable) y forma pares. Entrena DPO con esos pares on-policy vs con el dataset genérico. ¿Cuál alinea mejor en tu tarea?

E3. Tienes feedback de producción del tipo 👍/👎 (sin pares). ¿DPO o KTO? Justifícalo y entrénalo con KTOTrainer.

4.7Trampas comunes

  • LR de SFT (2e-4) en DPO → colapso. DPO quiere ~5e-6.
  • Olvidar que con PEFT la referencia es gratis (ref_model=None); cargar un segundo modelo y hacer OOM.
  • No vigilar rewards/accuracies: si no sube, tus pares están mal etiquetados.

4.8Referencias

  • DPO (Rafailov et al., NeurIPS 2023), SimPO (Meng et al. 2024), KTO (Ethayarajh et al. 2024), ORPO (Hong et al. 2024). Docs TRL (DPOTrainer, KTOTrainer).