Foundation Model Engineering

직접 선호도 최적화 (Direct Preference Optimization, DPO)

대규모 언어 모델(LLMs)을 인간의 선호도에 맞게 미세 조정(Fine-tuning)하는 것은 모델을 유용하고 안전하게 만드는 데 중요한 단계입니다. 전통적으로 이는 인간 피드백 기반 강화 학습(RLHF)을 통해 이루어졌는데, 이는 보상 모델(Reward Model)을 훈련한 다음 강화 학습(PPO)을 사용하여 정책(Policy)을 최적화하는 복잡한 다단계 프로세스였습니다.

Rafailov 등이 제안한 직접 선호도 최적화(Direct Preference Optimization, DPO) [1]언어 모델 자체가 비밀리에 보상 모델 임을 보여줌으로써 이 프로세스를 혁신합니다. DPO는 별도의 보상 모델과 강화 학습의 필요성을 제거하고 단순한 분류 손실(Classification Loss)로 정렬(Alignment) 문제를 해결합니다.

모티베이션: 왜 PPO를 넘어서야 하는가?

PPO를 사용한 RLHF는 성공적이었지만(예: ChatGPT), 구현과 안정화가 극도로 어렵기로 유명합니다. 표준 RLHF 파이프라인은 다음을 요구합니다:

  1. 고품질 데모 데이터에 대한 지도 미세 조정 (SFT).
  2. 보상 모델링: 비교 데이터로부터 인간의 선호도를 예측하기 위해 별도의 모델을 훈련합니다.
  3. 강화 학습 (PPO): 원래 모델에서 너무 멀어지지 않도록(KL divergence) 패널티를 부여하면서 보상 모델의 점수를 극대화하도록 SFT 모델을 최적화합니다.

이 세 번째 단계가 병목입니다. PPO는 하이퍼파라미터에 민감하고, 메모리에 여러 개의 거대한 모델(Policy, Reference, Reward, Value)을 유지해야 하며, 불안정해지기 쉽습니다.

DPO는 근본적인 질문을 던집니다: 강화 학습(RL)의 복잡성 없이 동일한 최적화 목표를 달성할 수 없을까?

핵심 개념: 보상 모델로서의 언어 모델

DPO의 핵심 통찰은 보상 함수를 최적의 정책에 매핑하는 것과 정책을 해당 보상 함수에 매핑하는 것 사이의 수학적 동등성입니다.

표준 RLHF에서 목표는 다음을 극대화하는 것입니다: maxπExD,yπ(x)[r(x,y)]βDKL(π(yx)πref(yx))\max_{\pi} \mathbb{E}_{x \sim \mathcal{D}, y \sim \pi(\cdot|x)} [r(x, y)] - \beta \mathbb{D}_{KL}(\pi(y|x) \| \pi_{ref}(y|x))

여기서 r(x,y)r(x, y) 는 보상 모델, π\pi 는 최적화되는 정책, πref\pi_{ref} 는 참조 정책(일반적으로 SFT 모델)이며, β\beta 는 KL 패널티의 강도를 제어합니다.

Rafailov 등은 이 목표에 대한 최적의 솔루션이 분석적으로 표현될 수 있음을 보여주었습니다: πr(yx)=1Z(x)πref(yx)exp(1βr(x,y))\pi_r(y|x) = \frac{1}{Z(x)} \pi_{ref}(y|x) \exp\left(\frac{1}{\beta} r(x, y)\right)

여기서 Z(x)Z(x) 는 분할 함수(Partition Function)입니다. 이 방정식을 재배열함으로써, 최적의 정책 πr\pi_r 과 참조 정책 πref\pi_{ref} 의 관점에서 보상 r(x,y)r(x, y) 를 표현할 수 있습니다: r(x,y)=βlogπr(yx)πref(yx)+βlogZ(x)r(x, y) = \beta \log \frac{\pi_r(y|x)}{\pi_{ref}(y|x)} + \beta \log Z(x)

이는 보상이 참조 정책에 대한 정책의 로그 확률(Log Probabilities)에 의해 암묵적으로 정의됨을 의미합니다!

DPO 손실 함수 (Loss Function)

이 암묵적 보상을 Bradley-Terry 선호도 모델에 대입함으로써, DPO는 단순한 분류 손실을 도출합니다. xx 가 프롬프트, ywy_w 가 선호되는 완료(Completion), yly_l 이 선호되지 않는 완료인 트리플 (x,yw,yl)(x, y_w, y_l) 데이터셋이 주어지면 손실은 다음과 같습니다:

LDPO(πθ;πref)=E(x,yw,yl)D[logσ(βlogπθ(ywx)πref(ywx)βlogπθ(ylx)πref(ylx))]\mathcal{L}_{\mathrm{DPO}}(\pi_\theta; \pi_{ref}) = -\mathbb{E}_{(x,y_w,y_l) \sim \mathcal{D}}\left[\log \sigma\left(\beta\log\frac{\pi_\theta(y_w|x)}{\pi_{ref}(y_w|x)} - \beta\log\frac{\pi_\theta(y_l|x)}{\pi_{ref}(y_l|x)}\right)\right]

여기서:

  • πθ\pi_\theta 는 우리가 훈련하는 모델입니다.
  • πref\pi_{ref} 는 고정된 참조 모델(SFT)입니다.
  • σ\sigma 는 시그모이드(Sigmoid) 함수입니다.
  • β\beta 는 하이퍼파라미터(일반적으로 0.10.1)입니다.

이 손실은 모델이 참조 모델에 비해 선호되는 응답 ywy_w 의 확률을 높이고, 거부된 응답 yly_l 의 확률을 낮추도록 유도합니다.

PyTorch 구현

다음은 PyTorch에서 DPO 손실을 구현한 깔끔하고 현실적인 코드입니다. 실제로는 프롬프트가 주어졌을 때 완료 토큰의 로그 확률을 계산하게 됩니다.

import torch
import torch.nn.functional as F

def compute_dpo_loss(policy_logps, ref_logps, beta=0.1):
    """
    Direct Preference Optimization (DPO) 손실을 계산합니다.
    
    Args:
        policy_logps: 정책 모델의 (chosen_logps, rejected_logps) 튜플.
                      각각은 (batch_size,) 형태의 텐서입니다.
        ref_logps: 참조 모델의 (chosen_logps, rejected_logps) 튜플.
                     각각은 (batch_size,) 형태의 텐서입니다.
        beta: DPO의 온도 파라미터 (하이퍼파라미터).
        
    Returns:
        loss: 스칼라 DPO 손실.
        chosen_rewards: 선택된 완료에 대한 암묵적 보상.
        rejected_rewards: 거부된 완료에 대한 암묵적 보상.
    """
    policy_chosen_logps, policy_rejected_logps = policy_logps
    ref_chosen_logps, ref_rejected_logps = ref_logps
    
    # 암묵적 보상 계산
    chosen_rewards = beta * (policy_chosen_logps - ref_chosen_logps)
    rejected_rewards = beta * (policy_rejected_logps - ref_rejected_logps)
    
    # DPO 손실은 보상 마진의 음의 로그 시그모이드입니다.
    logits = chosen_rewards - rejected_rewards
    loss = -F.logsigmoid(logits).mean()
    
    return loss, chosen_rewards, rejected_rewards

# 현실적인 텐서 형태를 사용한 예제
batch_size = 4
# 토큰 시퀀스에 대한 로그 확률 시뮬레이션
policy_chosen_logps = torch.randn(batch_size)
policy_rejected_logps = torch.randn(batch_size)
ref_chosen_logps = torch.randn(batch_size)
ref_rejected_logps = torch.randn(batch_size)

loss, c_rew, r_rew = compute_dpo_loss(
    (policy_chosen_logps, policy_rejected_logps),
    (ref_chosen_logps, ref_rejected_logps),
    beta=0.1
)

print(f"DPO Loss: {loss.item():.4f}")
print(f"Chosen Rewards: {c_rew}")
print(f"Rejected Rewards: {r_rew}")

Hugging Face TRL을 이용한 실제 적용

실제 훈련을 위해 trl 라이브러리는 토큰화, 참조 모델 로그 확률 계산 및 최적화를 처리하는 고수준 DPOTrainer 를 제공합니다.

from trl import DPOTrainer
from datasets import load_dataset

# 선호도 데이터셋 로드 (prompt, chosen, rejected 컬럼이 있어야 함)
dataset = load_dataset("trl-lib/ultrafeedback_binarized", split="train")

trainer = DPOTrainer(
    model="Qwen/Qwen3-0.6B",
    train_dataset=dataset,
    # DPOTrainer는 참조 모델이 제공되지 않으면 자동으로 처리합니다.
)

trainer.train()

Quizzes

Quiz 1: DPO가 별도의 보상 모델이 필요 없는 이유는 무엇인가요? DPO는 RLHF의 최적 정책이 파티션 함수를 제외하고 보상 함수를 고유하게 정의한다는 수학적 통찰에 기반합니다. 이 관계를 Bradley-Terry 선호도 모델에 대입함으로써, DPO는 별도의 보상 모델을 명시적으로 학습하고 저장할 필요 없이 선호도 데이터에 기반하여 정책을 직접 최적화합니다.

Quiz 2: DPO 손실 함수에서 β\beta 파라미터의 역할은 무엇인가요? β\beta 파라미터는 선호도 신호의 강도를 제어하며 온도 스케일 역할을 합니다. 이는 참조 모델과 선호도 데이터 중 어느 것을 더 신뢰할지 결정합니다. β\beta 가 작을수록 정책이 선호도를 만족시키기 위해 참조 모델에서 더 많이 벗어날 수 있으며, β\beta 가 클수록 참조 모델에 더 가깝게 유지됩니다.

Quiz 3: DPO가 단순함에도 불구하고 여전히 어려움을 겪을 수 있는 시나리오는 무엇인가요? DPO는 선호도 데이터가 정적이고 대표성이 있다고 가정합니다. 선호도 데이터에 노이즈가 있거나 모순이 있는 경우 DPO는 잘못된 샘플에 과적합될 수 있습니다. 또한 참조 모델의 분포에 의존하기 때문에, 참조 모델이 좋은 완료에 대해 매우 낮은 확률을 부여한 경우 DPO는 이를 효율적으로 가중치를 높이는 데 어려움을 겪을 수 있습니다.

Quiz 4: 정책 파라미터 θ\theta에 대한 DPO 손실 함수의 해석적 그래디언트(Gradient)를 유도하십시오. DPO 손실 함수는 LDPO(θ)=E[logσ(r^θ(x,yw)r^θ(x,yl))]\mathcal{L}_{\mathrm{DPO}}(\theta) = -\mathbb{E}\left[\log \sigma(\hat{r}_\theta(x, y_w) - \hat{r}_\theta(x, y_l))\right] 이며, 이때 암시적 보상은 r^θ(x,y)=βlogπθ(yx)πref(yx)\hat{r}_\theta(x, y) = \beta \log \frac{\pi_\theta(y|x)}{\pi_{ref}(y|x)} 입니다. 연쇄 법칙(Chain rule)과 시그모이드 미분 성질 zlogσ(z)=1σ(z)=σ(z)\nabla_z \log \sigma(z) = 1 - \sigma(z) = \sigma(-z)를 사용하면, θLDPO(θ)=E[σ(r^θ(x,yl)r^θ(x,yw))θ(r^θ(x,yw)r^θ(x,yl))]\nabla_\theta \mathcal{L}_{\mathrm{DPO}}(\theta) = -\mathbb{E}\left[ \sigma(\hat{r}_\theta(x, y_l) - \hat{r}_\theta(x, y_w)) \nabla_\theta (\hat{r}_\theta(x, y_w) - \hat{r}_\theta(x, y_l)) \right] 를 얻을 수 있습니다. 여기에 r^θ\hat{r}_\theta의 정의를 대입하면 θLDPO(θ)=βE[σ(r^θ(x,yl)r^θ(x,yw))(θlogπθ(ywx)θlogπθ(ylx))]\nabla_\theta \mathcal{L}_{\mathrm{DPO}}(\theta) = - \beta \mathbb{E}\left[ \sigma(\hat{r}_\theta(x, y_l) - \hat{r}_\theta(x, y_w)) \left( \nabla_\theta \log \pi_\theta(y_w|x) - \nabla_\theta \log \pi_\theta(y_l|x) \right) \right] 를 도출할 수 있습니다. 이는 DPO가 암시적 보상의 오류(시그모이드 항)에 비례하여 그래디언트 가중치를 조절함으로써, 선호되는 응답의 확률을 높이고 거부된 응답의 확률을 낮춘다는 점을 수학적으로 보여줍니다.


References

  1. Rafailov, R., et al. (2023). Direct Preference Optimization: Your Language Model is Secretly a Reward Model. arXiv:2305.18290.