Preference Optimization: RLHF, DPO, ORPO 비교¶
개요¶
Preference Optimization은 LLM을 인간 선호도에 맞게 정렬(alignment)하는 기법이다. RLHF에서 시작해 DPO, ORPO 등 더 효율적인 방법들이 등장했다.
기법 비교 요약¶
| 기법 | 보상 모델 | 강화학습 | 참조 모델 | 복잡도 |
|---|---|---|---|---|
| RLHF | 필요 | PPO 사용 | 필요 | 높음 |
| DPO | 불필요 | 불필요 | 필요 | 중간 |
| ORPO | 불필요 | 불필요 | 불필요 | 낮음 |
| KTO | 불필요 | 불필요 | 필요 | 중간 |
| IPO | 불필요 | 불필요 | 필요 | 중간 |
RLHF (Reinforcement Learning from Human Feedback)¶
파이프라인¶
┌─────────────┐
│ Pretrained │
│ Model │
└──────┬──────┘
│
▼
┌─────────────┐ ┌─────────────┐
│ SFT Model │───►│ RM Model │
│ (Stage 1) │ │ (Stage 2) │
└──────┬──────┘ └──────┬──────┘
│ │
▼ ▼
┌─────────────────────────────────┐
│ PPO Training (Stage 3) │
│ Policy + Value + RM + Ref │
└─────────────────────────────────┘
│
▼
┌─────────────┐
│ RLHF Model │
└─────────────┘
단계별 설명¶
Stage 1: Supervised Fine-tuning (SFT)
# 고품질 데이터로 기본 파인튜닝
sft_trainer = SFTTrainer(
model=base_model,
train_dataset=demonstration_data,
max_seq_length=2048,
)
Stage 2: Reward Model Training
# 선호도 데이터로 보상 모델 학습
# (chosen, rejected) 쌍 필요
reward_model = RewardModel(base_model)
# Bradley-Terry 모델: P(y_w > y_l) = σ(r(y_w) - r(y_l))
Stage 3: PPO Training
# 4개 모델 필요: Policy, Value, Reward, Reference
ppo_trainer = PPOTrainer(
model=policy_model,
ref_model=reference_model,
reward_model=reward_model,
value_model=value_model,
)
장단점¶
장점: - 입증된 효과 (GPT-4, Claude 등) - 세밀한 제어 가능
단점: - 4개 모델 동시 로드 (메모리 부담) - PPO 학습 불안정 - 하이퍼파라미터 민감
DPO (Direct Preference Optimization)¶
핵심 아이디어¶
DPO는 보상 모델을 명시적으로 학습하지 않고, 선호도 데이터에서 직접 정책을 최적화한다.
수학적 배경:
기존 RLHF 목적:
max E[r(x,y)] - β·KL(π||π_ref)
DPO 재구성 (closed-form solution 도출):
r(x,y) = β·log(π(y|x)/π_ref(y|x)) + β·log Z(x)
→ 보상 모델 없이 직접 최적화 가능
손실 함수¶
def dpo_loss(policy_chosen_logps, policy_rejected_logps,
reference_chosen_logps, reference_rejected_logps,
beta=0.1):
chosen_rewards = beta * (policy_chosen_logps - reference_chosen_logps)
rejected_rewards = beta * (policy_rejected_logps - reference_rejected_logps)
# Bradley-Terry 모델 기반
loss = -F.logsigmoid(chosen_rewards - rejected_rewards)
return loss.mean()
파이프라인¶
┌─────────────┐
│ SFT Model │
│ (Reference) │
└──────┬──────┘
│ Copy
▼
┌─────────────┐ ┌──────────────┐
│Policy Model │◄───│ Preference │
│ (Trainable) │ │ Data │
└─────────────┘ └──────────────┘
구현 예시¶
from trl import DPOTrainer, DPOConfig
dpo_config = DPOConfig(
beta=0.1, # KL 페널티 강도
learning_rate=5e-7,
per_device_train_batch_size=4,
gradient_accumulation_steps=4,
max_length=1024,
max_prompt_length=512,
)
trainer = DPOTrainer(
model=model,
ref_model=ref_model,
args=dpo_config,
train_dataset=preference_dataset,
tokenizer=tokenizer,
)
장단점¶
장점: - RLHF 대비 간단 (2개 모델만 필요) - 안정적인 학습 - 구현 용이
단점: - 참조 모델 메모리 부담 - β 하이퍼파라미터 민감 - Chosen/Rejected 쌍 필요
ORPO (Odds Ratio Preference Optimization)¶
핵심 아이디어¶
ORPO는 참조 모델 없이 SFT와 선호도 학습을 단일 단계로 통합한다.
┌─────────────┐ ┌──────────────┐
│ Base Model │◄───│ Preference │
│ (Trainable) │ │ Data │
└─────────────┘ └──────────────┘
│
▼
┌─────────────┐
│ ORPO Model │
└─────────────┘
손실 함수¶
def orpo_loss(policy_chosen_logps, policy_rejected_logps, lambda_orpo=0.1):
# SFT Loss (chosen 응답에 대한 NLL)
sft_loss = -policy_chosen_logps.mean()
# Odds Ratio
log_odds_chosen = policy_chosen_logps - torch.log(1 - torch.exp(policy_chosen_logps))
log_odds_rejected = policy_rejected_logps - torch.log(1 - torch.exp(policy_rejected_logps))
# Odds Ratio Loss
log_odds_ratio = log_odds_chosen - log_odds_rejected
or_loss = -F.logsigmoid(log_odds_ratio).mean()
return sft_loss + lambda_orpo * or_loss
구현 예시¶
from trl import ORPOTrainer, ORPOConfig
orpo_config = ORPOConfig(
learning_rate=8e-6,
beta=0.1, # lambda_orpo
per_device_train_batch_size=2,
gradient_accumulation_steps=4,
max_length=1024,
)
trainer = ORPOTrainer(
model=model,
# ref_model 불필요!
args=orpo_config,
train_dataset=preference_dataset,
tokenizer=tokenizer,
)
장단점¶
장점: - 참조 모델 불필요 (메모리 50% 절감) - SFT + Alignment 단일 단계 - 학습 속도 빠름
단점: - 비교적 새로운 기법 - 일부 태스크에서 DPO 대비 성능 저하 보고
기타 Preference Optimization 기법¶
KTO (Kahneman-Tversky Optimization)¶
# 쌍(pair) 데이터 불필요, 개별 레이블만
# Desirable / Undesirable 이진 레이블
def kto_loss(logps, ref_logps, labels, beta=0.1):
kl_div = logps - ref_logps
desirable_mask = labels == 1
undesirable_mask = labels == 0
loss_desirable = -F.logsigmoid(beta * kl_div[desirable_mask])
loss_undesirable = -F.logsigmoid(-beta * kl_div[undesirable_mask])
return loss_desirable.mean() + loss_undesirable.mean()
특징: Chosen/Rejected 쌍 없이 개별 평가 데이터로 학습 가능
IPO (Identity Preference Optimization)¶
DPO의 오버피팅 문제 해결:
# IPO는 log-sigmoid 대신 제곱 손실 사용
def ipo_loss(chosen_logps, rejected_logps, ref_chosen, ref_rejected, tau=0.1):
diff = (chosen_logps - ref_chosen) - (rejected_logps - ref_rejected)
loss = (diff - 1/(2*tau))**2
return loss.mean()
SimPO (Simple Preference Optimization)¶
참조 모델 없이 길이 정규화 추가:
def simpo_loss(chosen_logps, rejected_logps, chosen_len, rejected_len, beta=2.0, gamma=0.5):
# 길이 정규화된 로그 확률
chosen_normalized = chosen_logps / chosen_len
rejected_normalized = rejected_logps / rejected_len
# 마진 추가
loss = -F.logsigmoid(beta * (chosen_normalized - rejected_normalized) - gamma)
return loss.mean()
선택 가이드¶
상황별 추천¶
| 상황 | 추천 기법 | 이유 |
|---|---|---|
| 메모리 제한 | ORPO, SimPO | 참조 모델 불필요 |
| 쌍 데이터 없음 | KTO | 개별 레이블만 필요 |
| 안정적 학습 필요 | DPO | 입증된 안정성 |
| 최고 성능 추구 | RLHF | 세밀한 제어 |
| 빠른 실험 | ORPO | 단일 단계 |
데이터 요구사항¶
RLHF: demonstrations + (chosen, rejected) pairs
DPO: (chosen, rejected) pairs
ORPO: (chosen, rejected) pairs
KTO: individual (response, label) pairs
실험 결과 비교¶
MT-Bench 점수 (Llama-2-7B 기준)¶
| 기법 | 점수 | 학습 시간 | GPU 메모리 |
|---|---|---|---|
| SFT only | 5.2 | 1x | 1x |
| RLHF | 6.8 | 4x | 4x |
| DPO | 6.5 | 1.5x | 2x |
| ORPO | 6.3 | 1.2x | 1x |
| SimPO | 6.4 | 1.2x | 1x |
참고 자료¶
- "Training Language Models with Language Feedback" (RLHF 원 논문)
- "Direct Preference Optimization" (DPO 원 논문, 2023)
- "ORPO: Monolithic Preference Optimization" (2024)
- "KTO: Model Alignment as Prospect Theoretic Optimization" (2024)
- TRL (Transformer Reinforcement Learning) Library
최종 업데이트: 2026-02-18