¿Por Qué RLHF Necesita Tantas Piezas Móviles?

En el artículo anterior ensamblamos el pipeline completo de RLHF: entrenar un reward model sobre preferencias humanas, luego ejecutar PPO contra esa recompensa manteniendo la policy cerca de una referencia mediante una penalización de KL . El resultado es impresionante (InstructGPT demostró mejoras claras sobre SFT solo), pero el costo de ingeniería es sustancial. Necesitamos mantener tres modelos simultáneamente (la policy, el reward model y la red de valor para el baseline de PPO), generar muestras sobre la marcha, calcular puntuaciones de recompensa para cada una, estimar ventajas y realizar actualizaciones de gradiente de policy con recorte. Cada uno de estos pasos introduce hiperparámetros, posibles inestabilidades y sobrecarga de memoria.

Surge una pregunta natural: ¿realmente necesitamos el reward model como un artefacto separado? Lo entrenamos a partir de datos de preferencia humana y luego lo usamos solo para puntuar completaciones durante PPO. Si el reward model es solo un paso intermedio, quizás podamos eliminarlo por completo y optimizar la policy directamente a partir de pares de preferencia. Esto es exactamente lo que logra Direct Preference Optimization.

Rafailov et al. (2023) observaron que el objetivo de RLHF tiene una solución analítica: dada una función de recompensa, la policy óptima bajo el objetivo con restricción de KL toma una forma cerrada específica. Al sustituir esa forma cerrada de vuelta en el modelo de preferencias de Bradley-Terry, obtenemos una función de pérdida que depende solo de la policy y los datos de preferencia, sin ningún reward model a la vista.

¿De Dónde Viene la Pérdida de DPO?

Para ver cómo DPO elimina el reward model, necesitamos seguir una derivación clave. Recordemos del artículo de RLHF que el objetivo que maximizamos es la recompensa esperada menos una penalización de KL:

$$\max_{\pi_\theta} \; \mathbb{E}_{x \sim \mathcal{D}, \, y \sim \pi_\theta(\cdot|x)} \Big[ r(x, y) - \beta \, \text{KL}\big(\pi_\theta(\cdot|x) \,\|\, \pi_{\text{ref}}(\cdot|x)\big) \Big]$$

Este problema de optimización tiene una solución en forma cerrada. La policy óptima $\pi^*$ que maximiza este objetivo satisface:

$$\pi^*(y|x) = \frac{1}{Z(x)} \, \pi_{\text{ref}}(y|x) \, \exp\!\left(\frac{r(x,y)}{\beta}\right)$$

Vale la pena examinar esta fórmula. La policy óptima es la policy de referencia reponderada por $\exp(r(x,y)/\beta)$, de modo que las respuestas con alta recompensa reciben un impulso exponencial mientras que las respuestas de baja recompensa se suprimen. Si $\beta$ es grande, el exponente es pequeño para todos los valores de $r$ y $\pi^*$ se mantiene cerca de $\pi_{\text{ref}}$ (la restricción de KL domina). Si $\beta$ es pequeño, incluso diferencias modestas de recompensa producen grandes ratios exponenciales, así que $\pi^*$ concentra casi toda la probabilidad en la respuesta de mayor recompensa (la recompensa domina). La constante de normalización $Z(x)$ asegura que la distribución sume 1 sobre todas las respuestas posibles.

Podemos reorganizar esto para expresar la recompensa en términos de la policy:

$$r(x, y) = \beta \log \frac{\pi^*(y|x)}{\pi_{\text{ref}}(y|x)} + \beta \log Z(x)$$

La idea clave es que la recompensa es simplemente $\beta$ veces el log-ratio de la policy óptima respecto a la referencia, más una constante que depende del prompt. Cuando sustituimos esto en el modelo de preferencias de Bradley-Terry ($P(y_w \succ y_l | x) = \sigma(r(x, y_w) - r(x, y_l))$), los términos $\beta \log Z(x)$ se cancelan porque dependen solo de $x$ y no de qué respuesta estamos comparando. Lo que queda es una pérdida que involucra solo ratios de log-probabilidad bajo la policy y la referencia, sin ninguna función de recompensa explícita.

Después de sustituir y simplificar, esto produce la pérdida de DPO.

$$\mathcal{L}_{\text{DPO}}(\pi_\theta; \pi_{\text{ref}}) = -\mathbb{E}_{(x, y_w, y_l) \sim \mathcal{D}} \left[ \log \sigma \left( \beta \left( \log \frac{\pi_\theta(y_w|x)}{\pi_{\text{ref}}(y_w|x)} - \log \frac{\pi_\theta(y_l|x)}{\pi_{\text{ref}}(y_l|x)} \right) \right) \right]$$

Esta única ecuación reemplaza tanto el paso de entrenamiento del reward model como el bucle de PPO. La optimizamos con descenso de gradiente estándar sobre lotes de tripletas de preferencia $(x, y_w, y_l)$, donde $y_w$ es la respuesta preferida e $y_l$ es la rechazada.

¿Qué Hace Cada Pieza de la Pérdida?

La pérdida de DPO es compacta, pero cada componente tiene un significado específico, y entenderlos revela por qué el método funciona tan bien como lo hace. Recorramos la fórmula de adentro hacia afuera.

Consideremos el log-ratio $\log \frac{\pi_\theta(y_w|x)}{\pi_{\text{ref}}(y_w|x)}$ para la respuesta preferida ("ganadora"). Esto mide cuánto más (o menos) probable hace nuestra policy actual a $y_w$ en comparación con la policy de referencia. Si el ratio es positivo, la policy ya se ha desplazado hacia favorecer esta respuesta respecto a su punto de partida. Si es negativo, la policy aún asigna menor probabilidad a ella que la referencia. Hay un término correspondiente para la respuesta rechazada ("perdedora") $y_l$.

La diferencia entre estos dos log-ratios captura lo que realmente nos importa: ¿ha aumentado la policy la probabilidad relativa de la respuesta preferida más que la de la rechazada? Cuando esta diferencia es grande y positiva, la policy ya refleja bien la preferencia. Cuando es negativa, la policy aún favorece la respuesta rechazada sobre la preferida, y la pérdida será grande.

El parámetro $\beta$ controla qué tan agresivamente confiamos en las etiquetas de preferencia. Para ver por qué, consideremos los extremos. Cuando $\beta \to \infty$, el argumento de la sigmoide crece mucho incluso para una pequeña diferencia en log-ratios, así que la pérdida empuja fuertemente para separar las dos respuestas y la recompensa implícita se vuelve extremadamente sensible a pequeños cambios en la policy. Cuando $\beta \to 0$, el argumento de la sigmoide se encoge hacia cero independientemente de los log-ratios, lo que significa que la pérdida se vuelve casi plana y la policy apenas se mueve de la referencia. En la práctica, valores de $\beta$ entre 0.1 y 0.5 tienden a funcionar bien, aunque el valor óptimo depende de qué tan ruidosas sean las etiquetas de preferencia (etiquetas más ruidosas justifican un $\beta$ más bajo para evitar sobreajustar a pares mal etiquetados).

La sigmoide $\sigma$ convierte la diferencia escalada en una probabilidad en $[0, 1]$, y el logaritmo negativo la convierte en una pérdida de estilo entropía cruzada. Cuando la policy prefiere fuertemente la respuesta ganadora (argumento positivo grande para $\sigma$), $\sigma$ está cerca de 1 y $-\log \sigma$ está cerca de 0. Cuando la policy incorrectamente prefiere la respuesta perdedora (argumento negativo grande), $\sigma$ está cerca de 0 y $-\log \sigma$ se vuelve muy grande. Esta es la misma estructura log-sigmoide que aparece en regresión logística y en la pérdida del reward model de Bradley-Terry del artículo anterior, lo cual tiene sentido porque DPO está efectivamente realizando clasificación de preferencias directamente en el espacio de la policy.

Una consecuencia particularmente elegante de la derivación es que DPO define una recompensa implícita:

$$r(x, y) = \beta \log \frac{\pi_\theta(y|x)}{\pi_{\text{ref}}(y|x)}$$

Nunca entrenamos un reward model, pero obtenemos uno gratis: el log-ratio de la policy aprendida respecto a la referencia, escalado por $\beta$. Si alguna vez necesitamos puntuar una nueva respuesta (para monitoreo o depuración), podemos calcular esta cantidad directamente a partir de la policy.

💡 La policy de referencia $\pi_{\text{ref}}$ es típicamente el modelo SFT del que partimos, congelado durante todo el entrenamiento de DPO. Cumple el mismo rol que el ancla de KL en RLHF: evitar que la policy se desvíe demasiado del lenguaje coherente.

¿Qué Tan Simple Es DPO en la Práctica?

Una de las propiedades más atractivas de DPO es que su implementación es notablemente corta. Necesitamos datos de preferencia en forma de tripletas $(x, y_w, y_l)$, un modelo de policy $\pi_\theta$ (inicializado desde SFT) y una copia congelada de ese mismo modelo como $\pi_{\text{ref}}$. El bucle de entrenamiento calcula log-probabilidades para ambas respuestas bajo ambos modelos, ensambla la pérdida y retropropaga. El siguiente código esboza el cálculo central.

import torch
import torch.nn.functional as F

def dpo_loss(policy_model, ref_model, input_ids_w, input_ids_l,
             attention_mask_w, attention_mask_l, beta=0.1):
    """
    Compute DPO loss for a batch of preference pairs.
    input_ids_w, input_ids_l: token ids for preferred / rejected responses
    """
    # Forward pass through both models (no grad for reference)
    with torch.no_grad():
        ref_logps_w = get_sequence_log_probs(ref_model, input_ids_w, attention_mask_w)
        ref_logps_l = get_sequence_log_probs(ref_model, input_ids_l, attention_mask_l)

    policy_logps_w = get_sequence_log_probs(policy_model, input_ids_w, attention_mask_w)
    policy_logps_l = get_sequence_log_probs(policy_model, input_ids_l, attention_mask_l)

    # Log-ratios: how much does the policy differ from the reference?
    log_ratio_w = policy_logps_w - ref_logps_w
    log_ratio_l = policy_logps_l - ref_logps_l

    # DPO loss: push the preferred response's log-ratio above the rejected one's
    logits = beta * (log_ratio_w - log_ratio_l)
    loss = -F.logsigmoid(logits).mean()

    return loss


def get_sequence_log_probs(model, input_ids, attention_mask):
    """Sum of per-token log-probs for the response portion."""
    outputs = model(input_ids=input_ids, attention_mask=attention_mask)
    logits = outputs.logits[:, :-1, :]  # shift: predict next token
    labels = input_ids[:, 1:]

    log_probs = F.log_softmax(logits, dim=-1)
    token_log_probs = log_probs.gather(2, labels.unsqueeze(-1)).squeeze(-1)

    # Mask padding tokens, then sum over sequence
    mask = attention_mask[:, 1:]
    return (token_log_probs * mask).sum(dim=-1)

Compárelo con el pipeline de RLHF del artículo anterior. No hay bucle de muestreo, no hay estimación de ventajas, no hay lógica de recorte y no hay red de valor. Toda la optimización es una pérdida supervisada sobre datos de preferencia estáticos, lo que significa que podemos usar infraestructura de entrenamiento estándar (cargadores de datos, acumulación de gradientes, paralelismo de datos distribuido) sin la complejidad de un bucle de RL online.

Esta simplicidad viene con una compensación. Dado que DPO entrena sobre un conjunto fijo de pares de preferencia, la policy nunca genera sus propias completaciones durante el entrenamiento y nunca se encuentra con sus propios errores. PPO, en cambio, genera respuestas sobre la marcha y aprende del feedback del reward model sobre esas respuestas, lo que le permite explorar y corregir modos de fallo que el conjunto de datos estático podría no cubrir. En la práctica, DPO tiende a funcionar bien cuando el conjunto de datos de preferencia es lo suficientemente grande y diverso para cubrir la distribución de prompts que el modelo verá en producción. Cuando el conjunto de datos es estrecho o la distribución de producción cambia, los métodos online como PPO (o GRPO, que veremos en el siguiente artículo) pueden adaptarse de manera más robusta.

También hay una sutileza en torno al reward hacking. Con RLHF, la policy puede aprender a explotar peculiaridades del reward model (produciendo salidas que puntúan alto pero que en realidad no son buenas). DPO evita este modo de fallo por completo porque no hay un reward model explícito que explotar; la "recompensa" es implícita en los propios log-ratios de la policy. Por otro lado, DPO puede sobreajustar a patrones superficiales en los datos de preferencia (por ejemplo, siempre preferir respuestas más largas si los datos de entrenamiento están sesgados hacia la longitud), por lo que la curación cuidadosa de datos sigue siendo importante.

💡 Desde su publicación, DPO se ha convertido en un método de alignment ampliamente adoptado entre modelos de código abierto y equipos más pequeños porque requiere aproximadamente la mitad de la memoria GPU de RLHF (sin red de valor, sin reward model en tiempo de inferencia) y converge con calendarios de entrenamiento supervisado estándar.

Quiz

Pon a prueba tu comprensión de Direct Preference Optimization.

¿Qué idea clave permite a DPO eliminar el reward model del pipeline de RLHF?

En la pérdida de DPO, ¿qué representa $\log \frac{\pi_\theta(y_w|x)}{\pi_{\text{ref}}(y_w|x)}$?

¿Qué sucede cuando $\beta$ se establece muy alto en DPO?

¿Cuál es una limitación clave de DPO comparado con RLHF basado en PPO?