DPO Trainer
DPO Trainer (선호도 기반 정렬)
모델이 사람이 "좋아하는" 응답을 더 많이 하도록, "싫어하는" 응답은 덜 하도록 만드는 것을 선호도 정렬(preference alignment)이라고 해요. DPO(Direct Preference Optimization)는 별도의 보상 모델(reward model) 없이 선호 데이터만으로 이 정렬을 끝내는 방법이고, TRL의 DPOTrainer가 이를 지원합니다.
개요
TRL은 논문 Direct Preference Optimization: Your Language Model is Secretly a Reward Model에 기술된 DPO 트레이너를 지원해요. 저자는 Rafael Rafailov, Archit Sharma, Eric Mitchell, Stefano Ermon, Christopher D. Manning, Chelsea Finn입니다.
논문 초록의 핵심을 요약하면 다음과 같아요.
대규모 비지도 언어 모델(LM)은 넓은 세계 지식과 어느 정도의 추론 능력을 배우지만, 완전 비지도 훈련의 특성상 동작을 정밀하게 제어하기 어렵다. 기존 방법은 생성 결과의 상대적 품질에 대한 인간 라벨을 수집해 이 선호도에 맞게 LM을 파인튜닝하는데, 보통 RLHF를 쓴다. 그러나 RLHF는 복잡하고 종종 불안정해서, 먼저 인간 선호도를 반영한 보상 모델을 피팅하고, 그다음 강화학습으로 추정 보상을 최대화하되 원본 모델에서 너무 벗어나지 않게 대형 비지도 LM을 파인튜닝한다. 이 논문에서는 RLHF에서 보상 모델을 새로 파라미터화해서 대응하는 최적 정책을 닫힌 형태로 추출하게 함으로써, 단순한 분류 손실만으로 표준 RLHF 문제를 푼다. 결과 알고리즘인 DPO는 안정적이고 성능이 좋으며 계산이 가벼워서, 파인튜닝 중 LM 샘플링이나 큰 하이퍼파라미터 튜닝이 필요 없다. 실험에서 DPO는 기존 방법만큼 또는 그보다 잘 LM을 인간 선호도에 정렬시키며, 생성 감정 제어에서는 PPO 기반 RLHF를 능가하고 요약·단일 턴 대화 품질에서도 비슷하거나 더 낫다.
이 사후훈련 방법은 Kashif Rasul이 기여했고 이후 Quentin Gallouédec가 리팩터링했어요.
빠른 시작
이 예시는 TRL의 DPOTrainer로 언어 모델을 훈련하는 법을 보여줍니다. Qwen 3 0.6B 모델을 UltraFeedback 데이터셋으로 훈련해요.
from trl import DPOTrainer
from datasets import load_dataset
trainer = DPOTrainer(
model="Qwen/Qwen3-0.6B",
train_dataset=load_dataset("trl-lib/ultrafeedback_binarized", split="train"),
)
trainer.train()
기대되는 데이터셋 타입과 형식
DPO는 preference 데이터셋이 필요합니다. DPOTrainer는 standard와 conversational 형식을 모두 지원하며, conversational 데이터셋이 주어지면 트레이너가 자동으로 chat template을 적용해요.
# Standard format
## Explicit prompt (recommended)
preference_example = {"prompt": "The sky is", "chosen": " blue.", "rejected": " green."}
# Implicit prompt
preference_example = {"chosen": "The sky is blue.", "rejected": "The sky is green."}
# Conversational format
## Explicit prompt (recommended)
preference_example = {"prompt": [{"role": "user", "content": "What color is the sky?"}],
"chosen": [{"role": "assistant", "content": "It is blue."}],
"rejected": [{"role": "assistant", "content": "It is green."}]}
## Implicit prompt
preference_example = {"chosen": [{"role": "user", "content": "What color is the sky?"},
{"role": "assistant", "content": "It is blue."}],
"rejected": [{"role": "user", "content": "What color is the sky?"},
{"role": "assistant", "content": "It is green."}]}
데이터셋이 이 형식 중 하나가 아니라면, 전처리해서 기대 형식으로 바꿀 수 있어요. Vezora/Code-Preference-Pairs 데이터셋 예시입니다.
from datasets import load_dataset
dataset = load_dataset("Vezora/Code-Preference-Pairs")
def preprocess_function(example):
return {
"prompt": [{"role": "user", "content": example["input"]}],
"chosen": [{"role": "assistant", "content": example["accepted"]}],
"rejected": [{"role": "assistant", "content": example["rejected"]}],
}
dataset = dataset.map(preprocess_function, remove_columns=["instruction", "input", "accepted", "ID"])
print(next(iter(dataset["train"])))
{
"prompt": [{"role": "user", "content": "Create a nested loop to print every combination of numbers [...]"}],
"chosen": [{"role": "assistant", "content": "Here is an example of a nested loop in Python [...]"}],
"rejected": [{"role": "assistant", "content": "Here is an example of a nested loop in Python [...]"}],
}
DPO 방법 자세히 보기
직접 선호도 최적화(DPO)는 언어 모델을 선호도 데이터에 정렬시키는 훈련 방법입니다. 지도 방식의 입력–출력 쌍 대신, 같은 prompt에 대한 응답 쌍을 훈련에 쓰는데, 그중 하나가 다른 하나보다 선호됩니다. 목적 함수는 명시적 보상 모델 없이, 참조 모델(reference model)에 대한 선호·비선호 응답의 log-likelihood 간 차이(margin)를 벌리는 방향으로 모델을 직접 최적화합니다. 실제로는 선호 응답의 likelihood를 높이기보다 비선호 응답의 likelihood를 억제하는 방식으로 보통 이뤄져요.
핵심 단계인 전처리와 손실 계산을 살펴봅시다.
전처리와 토큰화
훈련 중 각 예시는 prompt와 함께 선호되는(chosen)·비선호되는(rejected) completion을 담아야 합니다. 형식에 대한 자세한 내용은 Dataset formats를 참고하세요. DPOTrainer는 각 입력을 모델의 토크나이저로 토큰화합니다.
손실 계산

DPO에 쓰이는 손실은 다음과 같이 정의됩니다.
$$ \mathcal{L}{\mathrm{DPO}}(\theta) = -\mathbb{E}{(x,y^{+},y^{-})}!\left[\log \sigma!\left(\beta\Big(\log\frac{\pi_{\theta}(y^{+}!\mid x)}{\pi_{\mathrm{ref}}(y^{+}!\mid x)}-\log \frac{\pi_{\theta}(y^{-}!\mid x)}{\pi_{\mathrm{ref}}(y^{-}!\mid x)}\Big)\right)\right] $$
여기서 ( x ) 는 prompt, ( y^+ ) 는 선호 응답, ( y^- ) 는 비선호 응답입니다. ( \pi_{\theta} ) 는 훈련 중인 정책 모델, ( \pi_{\mathrm{ref}} ) 는 참조 모델, ( \sigma ) 는 sigmoid 함수, ( \beta > 0 ) 는 선호 신호의 강도를 제어하는 하이퍼파라미터입니다.
손실 타입
문헌에는 이 목적 함수의 여러 정식화(formulation)가 제안됐어요. 처음에는 위와 같은 형태가 DPO의 목적 함수로 정의됐습니다.
loss_type= |
설명 |
|---|---|
"sigmoid" (기본) |
선호 데이터로 Bradley-Terry 모델에 따른 이진 분류기를 피팅할 수 있으며, DPO 저자들은 logsigmoid로 정규화된 likelihood에 sigmoid 손실을 적용해 로지스틱 회귀를 피팅할 것을 제안합니다. |
"hinge" |
RSO 저자들이 SLiC 논문의 정규화 likelihood에 hinge 손실을 쓰자고 제안. 이때 beta는 margin의 역수입니다. |
"ipo" |
IPO 저자들은 logit 변환이 과적합할 수 있다고 보고 identity 변환으로 선호도를 직접 최적화하자고 제안. TRL은 이를 loss_type="ipo"로 노출합니다. |
"exo_pair" |
EXO 저자들이 reverse-KL 선호도 최적화 제안. label_smoothing이 반드시 0.0보다 커야 하며 권장값은 1e-3. 전체 방법은 K>2 개의 SFT completion을 쓰고 K가 커질수록 PPO에 가까워집니다. |
"nca_pair" |
NCA 저자들은 NCA가 상대 likelihood가 아닌 각 응답의 절대 likelihood를 최적화함을 보여줍니다. |
"robust" |
Robust DPO 저자들이 noisy preference에서 편향 없는 DPO 손실을 제안. DPOConfig의 label_smoothing으로 label-flip 확률을 모델링하며 유효 범위는 [0.0, 0.5). |
"bco_pair" |
BCO 저자들은 logit이 보상 역할을 하는 이진 분류기를 훈련해서 {prompt, chosen completion} 쌍을 1로, {prompt, rejected completion} 쌍을 0으로 매핑. 비쌍(paired되지 않은) 데이터에선 전용 experimental.bco.BCOTrainer를 권장합니다. |
"sppo_hard" |
SPPO 저자들은 승자 보상을 1/2만큼 크게, 패자 보상을 -1/2만큼 작게 밀어 Nash equilibrium을 반복적으로 풀 수 있다고 주장. 구현은 하드 라벨 확률(승자 1, 패자 0)로 근사합니다. |
"aot" 또는 loss_type="aot_unpaired" |
AOT 저자들이 최적 수송(Optimal Transport)을 통한 분포 선호도 정렬을 제안. "aot"는 paired, "aot_unpaired"는 unpaired 데이터용. 둘 다 정렬된 분위수로 확률적 지배를 강제하며, GPU당 큰 배치가 도움이 됩니다. |
"apo_zero" 또는 loss_type="apo_down" |
APO 방법은 고정(anchored) 목적 함수 도입. apo_zero는 승자를 끌어올리고 패자를 가중 감소(모델이 승자보다 못할 때 유용). apo_down은 둘 다 가중 감소하되 패자에 더 강한 압력(모델이 이미 승자보다 잘할 때 유용). |
"discopop" |
DiscoPOP 논문은 LLM으로 더 효율적인 오프라인 선호도 최적화 손실을 발견. 제안된 DiscoPOP 손실(log-ratio 변조 손실)은 다양한 작업에서 다른 최적화 손실을 능가. |
"sft" |
SFT 손실(부정 로그 우도)로, 모델이 선호 응답을 생성하도록 훈련. |
"sigmoid_norm" |
SimPO 저자들이 원래 sigmoid 손실의 길이 편향을 non-mask 토큰 수로 정규화해 해결. TRL은 이를 loss_type="sigmoid_norm"으로 노출합니다. |
더 알아보기 (Learn more)
- 선호도 데이터셋 형식: Dataset formats
- SFT부터 GRPO까지 전체 사후훈련 방법: TRL 홈
- 배치·훈련 속도 비교는 TRL 벤치마크 문서를 참고하세요.