Skip to content
Published on

DPO에서 KTO까지: 인간 피드백 정렬 기법 최신 논문 리뷰와 실전 구현

Authors
  • Name
    Twitter
DPO에서 KTO까지: 인간 피드백 정렬 기법 최신 논문 리뷰와 실전 구현

왜 RLHF 이후의 정렬 기법이 필요한가

LLM을 인간의 선호에 맞추는 작업, 즉 alignment는 ChatGPT 이후 모든 프로덕션 LLM의 필수 파이프라인이 되었다. OpenAI가 InstructGPT(Ouyang et al., 2022, arxiv:2203.02155)에서 확립한 RLHF(Reinforcement Learning from Human Feedback) 파이프라인은 세 단계로 구성된다. SFT(Supervised Fine-Tuning)로 기본 능력을 심고, 보상 모델(Reward Model)을 별도로 학습하고, PPO(Proximal Policy Optimization)로 정책을 최적화한다.

이 3단계 파이프라인은 강력하지만 운영 비용이 크다. 보상 모델 학습에 별도 GPU 자원이 필요하고, PPO 학습 시 policy model, reference model, reward model, value model 네 개의 모델을 동시에 메모리에 올려야 한다. 70B 모델 기준으로 최소 A100 80GB 8장 이상이 필요한 셈이다. 학습 안정성도 문제다. PPO의 클리핑 비율, KL 페널티 계수, GAE lambda 등 민감한 하이퍼파라미터가 많아 한 번에 수렴하는 경우가 드물다.

2023년 하반기부터 이 복잡성을 근본적으로 줄이는 연구가 쏟아졌다. DPO가 보상 모델 없이 선호 데이터에서 직접 정책을 최적화할 수 있음을 보였고, IPO가 Bradley-Terry 가정의 위험성을 지적했으며, KTO가 쌍별(pairwise) 선호 데이터 없이 이진 신호만으로 정렬할 수 있음을 증명했다. 2024-2025년에는 SimPO, ORPO, GRPO 등이 등장하며 정렬 기법의 선택지가 급격히 넓어졌다.

이 글은 RLHF부터 DPO, IPO, KTO, 그리고 최신 기법까지의 논문을 리뷰하고, Hugging Face TRL 라이브러리를 활용한 실전 구현 코드와 하이퍼파라미터 튜닝 전략, 실패 사례와 복구 절차를 다룬다.

RLHF 파이프라인의 구조와 한계

3단계 파이프라인 상세

RLHF의 전체 흐름을 수식으로 정리하면 다음과 같다.

1단계 - SFT: 고품질 instruction-response 쌍으로 base model을 파인튜닝한다.

2단계 - Reward Model 학습: 동일 프롬프트에 대해 두 개의 응답을 생성하고, 인간 평가자가 선호하는 응답(chosen)과 그렇지 않은 응답(rejected)을 레이블링한다. Bradley-Terry 모델을 기반으로 보상 함수 r(x, y)를 학습한다.

L_RM = -E[log sigma(r(x, y_w) - r(x, y_l))]

여기서 y_w는 chosen, y_l은 rejected 응답이다.

3단계 - PPO 최적화: 보상 모델의 점수를 최대화하되, reference policy와의 KL divergence로 제약한다.

max E[r(x, y)] - beta * KL(pi_theta || pi_ref)

RLHF의 실무적 한계

한계설명
메모리 비용Policy, Reference, Reward, Value 4개 모델 동시 로드
학습 불안정PPO 클리핑, KL 계수, learning rate 등 민감한 하이퍼파라미터 다수
Reward hacking보상 모델의 취약점을 악용하는 정책 학습 가능
데이터 비용쌍별 비교 데이터 수집에 인간 평가자 비용 발생
재현성동일 설정에서도 seed에 따라 결과가 크게 달라짐

이 한계들이 DPO 등 RL-free 정렬 기법의 등장 배경이다.

DPO: 보상 모델 없는 직접 선호 최적화

핵심 논문 리뷰

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

DPO의 핵심 통찰은 간결하다. RLHF의 보상 함수 최적화 문제에는 closed-form 해가 존재하며, 이를 이용하면 보상 모델을 명시적으로 학습하지 않고도 선호 데이터에서 직접 정책을 최적화할 수 있다.

RLHF의 최적 정책은 다음과 같은 형태를 갖는다:

pi*(y|x) = (1/Z(x)) * pi_ref(y|x) * exp(r(x, y) / beta)

이를 역으로 풀면 보상 함수를 정책의 비율로 표현할 수 있다:

r(x, y) = beta * log(pi_theta(y|x) / pi_ref(y|x)) + beta * log Z(x)

이 관계를 Bradley-Terry 모델에 대입하면 Z(x) 항이 상쇄되고, 최종 DPO 손실 함수가 도출된다:

L_DPO = -E[log sigma(beta * (log(pi_theta(y_w|x)/pi_ref(y_w|x)) - log(pi_theta(y_l|x)/pi_ref(y_l|x))))]

중요한 점은 DPO가 보상 모델을 "제거"한 것이 아니라 "암묵적으로 포함"한다는 사실이다. 정책 자체가 보상 모델 역할을 겸하기 때문에, 별도의 보상 모델 학습과 PPO 단계가 불필요해진다.

DPO의 장점과 한계

장점: 구현이 단순하다. cross-entropy loss와 유사한 형태이므로 SFT와 거의 동일한 학습 코드로 구현 가능하다. 메모리 사용량도 policy + reference 두 모델만 필요해 RLHF 대비 절반 수준이다. Hugging Face TRL의 DPOTrainer를 사용하면 수십 줄의 코드로 전체 파이프라인을 구성할 수 있다.

한계: DPO는 Bradley-Terry 모델 가정에 의존한다. 인간의 선호가 항상 이 모델을 따르지 않을 수 있다. 또한 SFT 없이 base model에 직접 DPO를 적용하면 응답이 장황해지거나(rambling) 환각(hallucination)이 증가하는 현상이 보고되었다. chosen/rejected 쌍 데이터의 품질이 낮으면 학습이 수렴하지 않거나 성능이 오히려 하락할 수 있다.

TRL 기반 DPO 구현

from datasets import load_dataset
from transformers import AutoModelForCausalLM, AutoTokenizer
from trl import DPOConfig, DPOTrainer

# 1. 모델과 토크나이저 로드
model_name = "Qwen/Qwen2.5-1.5B-Instruct"
model = AutoModelForCausalLM.from_pretrained(model_name, torch_dtype="bfloat16")
tokenizer = AutoTokenizer.from_pretrained(model_name)

# 2. 선호 데이터 로드 (prompt, chosen, rejected 구조)
dataset = load_dataset("trl-lib/ultrafeedback_binarized", split="train[:5000]")

# 3. DPO 학습 설정
training_args = DPOConfig(
    output_dir="./dpo-qwen2.5-1.5b",
    per_device_train_batch_size=2,
    gradient_accumulation_steps=8,
    learning_rate=5e-7,       # DPO는 매우 낮은 learning rate 필수
    beta=0.1,                 # KL 제약 강도
    max_length=1024,
    max_prompt_length=512,
    num_train_epochs=1,
    bf16=True,
    logging_steps=10,
    save_strategy="steps",
    save_steps=500,
    warmup_ratio=0.1,
    gradient_checkpointing=True,
)

# 4. DPO Trainer 생성 및 학습
trainer = DPOTrainer(
    model=model,
    args=training_args,
    train_dataset=dataset,
    processing_class=tokenizer,
)
trainer.train()
trainer.save_model("./dpo-qwen2.5-1.5b-final")

위 코드에서 가장 중요한 하이퍼파라미터는 betalearning_rate다. beta가 너무 높으면 reference model에 지나치게 가까워져 정렬 효과가 미미하고, 너무 낮으면 정책이 불안정해진다. learning rate는 SFT 대비 10배 이상 낮게 설정하는 것이 일반적이다.

IPO: Bradley-Terry 가정의 위험성

핵심 논문 리뷰

A General Theoretical Paradigm to Understand Learning from Human Preferences (Azar et al., 2024, AISTATS 2024, arxiv:2310.12036)

IPO(Identity Preference Optimization)는 DPO의 핵심 가정인 Bradley-Terry 모델에 의문을 제기한다. Bradley-Terry 모델은 쌍별 선호를 pointwise reward 값으로 변환하는데, 이 변환 과정에서 정보 손실이 발생하고 과적합(overfitting) 위험이 높아진다는 것이 IPO의 핵심 주장이다.

IPO는 더 일반적인 프레임워크인 PsiPO(Psi-Preference Optimization)를 제안하고, 그 특수한 경우로 identity function을 사용하는 IPO를 도출한다. IPO의 손실 함수는 다음과 같다:

L_IPO = E[(log(pi_theta(y_w|x)/pi_ref(y_w|x)) - log(pi_theta(y_l|x)/pi_ref(y_l|x)) - 1/(2*beta))^2]

DPO가 log-sigmoid를 사용하는 것과 달리, IPO는 squared loss를 사용한다. 이 차이가 과적합에 대한 강건성을 높인다. DPO에서는 chosen의 확률을 무한히 높이고 rejected의 확률을 무한히 낮추는 방향으로 최적화가 진행될 수 있지만, IPO에서는 target margin(1/2beta)에 수렴하도록 제약된다.

IPO의 권장 beta 값은 DPO(0.1~0.5)보다 훨씬 낮은 0.01 수준이다. 이는 IPO의 loss 구조가 DPO와 근본적으로 다르기 때문이며, 동일 beta 값을 사용하면 안 된다.

KTO: 쌍별 데이터 없이 이진 신호로 정렬하기

핵심 논문 리뷰

KTO: Model Alignment as Prospect Theoretic Optimization (Ethayarajh & Jurafsky, 2024, ICML 2024, arxiv:2402.01306)

KTO의 가장 큰 혁신은 데이터 요구사항의 변화다. DPO와 IPO는 동일 프롬프트에 대한 chosen/rejected 쌍이 필요하지만, KTO는 개별 응답에 대한 이진 레이블("좋다" 또는 "나쁘다")만으로 충분하다. 이것은 실무에서 엄청난 차이를 만든다. 쌍별 비교 데이터를 수집하는 비용은 이진 레이블링의 5-10배에 달하기 때문이다.

KTO의 이론적 기반은 Kahneman과 Tversky의 전망 이론(Prospect Theory)이다. 인간은 이득보다 손실에 더 민감하게 반응한다는 손실 회피(loss aversion) 현상을 alignment 목적 함수에 직접 반영한다.

KTO는 HALO(Human-Aware Loss Objective) 프레임워크를 제안하며, 기존 정렬 기법들이 암묵적으로 전망 이론의 편향을 포함하고 있음을 보인다. DPO의 성공도 부분적으로는 이런 인간 인지 편향을 반영하기 때문이라는 분석이다.

KTO의 손실 함수는 desirable 응답과 undesirable 응답에 대해 비대칭적으로 정의된다:

L_KTO = E_desirable[1 - sigma(beta * (log(pi/pi_ref) - z_ref))]
      + lambda * E_undesirable[1 - sigma(beta * (z_ref - log(pi/pi_ref)))]

여기서 z_ref는 KL divergence의 추정값이고, lambda는 손실 회피 계수(기본값 약 1.33으로, 전망 이론의 실험 결과에서 유래)이다.

KTO의 실험 결과

KTO는 1B에서 30B 스케일까지 DPO와 동등하거나 우수한 성능을 보였다. 특히 SFT 없이 base model에 직접 적용했을 때 DPO에서 나타나는 rambling 현상이 KTO에서는 발생하지 않았다. 이는 KTO의 비대칭 loss 구조가 불량 응답에 대해 더 강한 페널티를 부과하기 때문으로 해석된다.

TRL 기반 KTO 구현

from datasets import load_dataset
from transformers import AutoModelForCausalLM, AutoTokenizer
from trl.experimental.kto import KTOConfig, KTOTrainer

# 1. 모델 로드
model_name = "Qwen/Qwen2.5-1.5B-Instruct"
model = AutoModelForCausalLM.from_pretrained(model_name, torch_dtype="bfloat16")
tokenizer = AutoTokenizer.from_pretrained(model_name)

# 2. KTO 데이터 로드 (prompt, completion, label 구조)
# label: True(desirable) / False(undesirable)
dataset = load_dataset("trl-lib/kto-mix-14k", split="train")

# 3. KTO 학습 설정
training_args = KTOConfig(
    output_dir="./kto-qwen2.5-1.5b",
    per_device_train_batch_size=4,
    gradient_accumulation_steps=4,
    learning_rate=5e-7,
    beta=0.1,                       # KL 제약 강도
    desirable_weight=1.0,           # desirable 응답 가중치
    undesirable_weight=1.0,         # undesirable 응답 가중치
    max_length=1024,
    max_prompt_length=512,
    num_train_epochs=1,
    bf16=True,
    logging_steps=10,
    gradient_checkpointing=True,
)

# 4. KTO Trainer 생성 및 학습
trainer = KTOTrainer(
    model=model,
    args=training_args,
    train_dataset=dataset,
    processing_class=tokenizer,
)
trainer.train()
trainer.save_model("./kto-qwen2.5-1.5b-final")

KTO 데이터 형식의 핵심은 쌍별 데이터가 아닌 개별 응답 레이블이라는 점이다. 기존에 thumbs-up/thumbs-down 피드백을 수집하고 있었다면, 데이터 변환 없이 바로 KTO 학습에 활용할 수 있다.

최신 정렬 기법: SimPO, ORPO, GRPO

SimPO (Simple Preference Optimization)

SimPO: Simple Preference Optimization with a Reference-Free Reward (Meng et al., 2024, arxiv:2405.14734)

SimPO는 reference model을 완전히 제거한다. DPO에서 reference model은 정책이 너무 벗어나지 않도록 제약하는 역할을 하는데, SimPO는 응답 길이로 정규화된 평균 log probability를 보상 신호로 사용하여 이 문제를 해결한다. AlpacaEval 2와 Arena-Hard에서 DPO를 유의미하게 초과하는 성능을 보였다.

reference model이 불필요하므로 메모리 사용량이 DPO 대비 절반으로 줄어든다. 단일 GPU에서도 7B 모델의 정렬 학습이 가능해진 것이다.

ORPO (Odds-Ratio Preference Optimization)

ORPO는 SFT와 정렬을 단일 목적 함수로 통합한다. SFT loss에 odds-ratio 기반 선호 loss를 추가하여, 별도의 SFT 단계 없이 한 번의 학습으로 instruction following과 선호 정렬을 동시에 달성한다. KL 페널티도 제거했기 때문에 beta 튜닝이 불필요하다.

GRPO (Group Relative Policy Optimization)

DeepSeekMath: Pushing the Limits of Mathematical Reasoning in Open Language Models (Shao et al., 2024, arxiv:2402.03300)

DeepSeek이 제안한 GRPO는 PPO의 value network(critic model)를 제거하여 RLHF의 메모리 요구사항을 약 50% 줄인다. 하나의 프롬프트에 대해 여러 응답을 그룹으로 샘플링하고, 그룹 내 상대적 보상으로 advantage를 추정한다. 별도의 critic model이 필요 없어 구현과 학습이 단순해진다.

DeepSeek-R1의 학습에서도 GRPO가 핵심 역할을 했으며, 특히 수학과 코딩 같은 verifiable한 태스크에서 강력한 성능을 보인다.

알고리즘 비교 테이블

항목RLHF (PPO)DPOIPOKTOSimPOORPOGRPO
보상 모델명시적 학습 필요암묵적 (정책에 내재)암묵적암묵적불필요불필요명시적/규칙 기반
Reference Model필요필요필요필요불필요불필요필요
데이터 형식pairwisepairwisepairwisebinarypairwisepairwise규칙 기반 보상
메모리 사용매우 높음 (4 모델)높음 (2 모델)높음 (2 모델)높음 (2 모델)중간 (1 모델)중간 (1 모델)높음 (2 모델)
핵심 하이퍼파라미터KL coeff, clip ratio, GAE lambdabeta, lrbetabeta, lambdagamma, betalambdaclip ratio, KL coeff
학습 안정성낮음중간높음중간-높음높음높음중간
구현 난이도높음낮음낮음낮음매우 낮음매우 낮음중간
권장 beta-0.1~0.50.010.1~0.32.0~2.5--
SFT 사전 학습 필요강력 권장권장선택적권장불필요 (통합)

하이퍼파라미터 튜닝 가이드

Beta 파라미터 튜닝

beta는 모든 DPO 계열 기법에서 가장 중요한 하이퍼파라미터다. reference policy와의 거리를 제어하는 KL 제약의 강도를 결정한다.

# beta 값에 따른 학습 행동 분석 스크립트
import torch
import matplotlib.pyplot as plt

def dpo_loss_landscape(beta_values, log_ratio_range):
    """beta 값별 DPO loss 곡면을 시각화"""
    fig, axes = plt.subplots(1, len(beta_values), figsize=(5*len(beta_values), 4))

    for idx, beta in enumerate(beta_values):
        log_ratios = torch.linspace(-log_ratio_range, log_ratio_range, 200)
        # DPO loss: -log(sigma(beta * (log_ratio_w - log_ratio_l)))
        # 여기서는 log_ratio_w - log_ratio_l을 단일 변수로 취급
        loss = -torch.log(torch.sigmoid(beta * log_ratios))
        gradient = -beta * (1 - torch.sigmoid(beta * log_ratios))

        ax = axes[idx]
        ax.plot(log_ratios.numpy(), loss.numpy(), label="Loss", color="blue")
        ax.plot(log_ratios.numpy(), gradient.numpy(), label="Gradient", color="red")
        ax.set_title(f"beta = {beta}")
        ax.set_xlabel("log(pi/pi_ref)_w - log(pi/pi_ref)_l")
        ax.legend()
        ax.grid(True)

    plt.tight_layout()
    plt.savefig("dpo_beta_analysis.png", dpi=150)
    plt.show()

# beta가 커질수록 loss 곡면이 가팔라지고 학습이 공격적으로 변한다
dpo_loss_landscape([0.05, 0.1, 0.3, 0.5], log_ratio_range=5.0)

실전 튜닝 전략:

  • 모델 크기가 작을수록(1B3B) beta를 낮게(0.050.1) 설정한다. 작은 모델은 reference policy에서 멀어지기 쉽기 때문이다.
  • 모델 크기가 클수록(7B70B) beta를 높게(0.10.5) 설정해도 안정적이다.
  • 선호 데이터의 품질이 높을수록 beta를 낮춰 더 공격적으로 학습시킬 수 있다.
  • 데이터 품질이 불확실하면 beta=0.1에서 시작하여 validation loss를 기준으로 조정한다.

Learning Rate 튜닝

DPO에서 learning rate는 SFT 대비 5~20배 낮게 설정하는 것이 표준이다.

모델 크기SFT LRDPO LR 권장 범위비고
1B~3B2e-51e-6 ~ 5e-7과적합 위험 높음
7B~13B1e-55e-7 ~ 1e-7가장 안정적 구간
30B~70B5e-61e-7 ~ 5e-8gradient accumulation 필수

learning rate가 너무 높으면 reference policy에서 급격히 벗어나 out-of-distribution 응답이 증가한다. 너무 낮으면 chosen/rejected 구분 능력이 충분히 학습되지 않아 win rate 개선이 미미하다.

선호 데이터 구축 실전 가이드

DPO 데이터 형식 변환

def convert_to_dpo_format(raw_data):
    """다양한 원본 데이터를 DPO 학습 형식으로 변환

    DPO는 prompt, chosen, rejected 세 필드가 필요하다.
    각 필드는 conversation 형태(list of dict)를 권장한다.
    """
    dpo_dataset = []

    for item in raw_data:
        prompt = item["instruction"]

        # 방법 1: 인간 평가자의 직접 비교
        if "chosen_response" in item and "rejected_response" in item:
            entry = {
                "prompt": [{"role": "user", "content": prompt}],
                "chosen": [{"role": "assistant", "content": item["chosen_response"]}],
                "rejected": [{"role": "assistant", "content": item["rejected_response"]}],
            }
            dpo_dataset.append(entry)

        # 방법 2: 점수 기반 자동 변환 (rating >= 4: chosen, rating <= 2: rejected)
        elif "responses" in item:
            responses = sorted(item["responses"], key=lambda x: x["rating"], reverse=True)
            if len(responses) >= 2 and responses[0]["rating"] >= 4 and responses[-1]["rating"] <= 2:
                entry = {
                    "prompt": [{"role": "user", "content": prompt}],
                    "chosen": [{"role": "assistant", "content": responses[0]["text"]}],
                    "rejected": [{"role": "assistant", "content": responses[-1]["text"]}],
                }
                dpo_dataset.append(entry)

    return dpo_dataset

KTO 데이터 형식 변환

def convert_to_kto_format(raw_data):
    """기존 피드백 데이터를 KTO 형식으로 변환

    KTO는 prompt, completion, label 세 필드가 필요하다.
    label은 True(desirable) 또는 False(undesirable)이다.
    쌍별 데이터가 불필요하므로 thumbs-up/down 데이터를 직접 활용 가능하다.
    """
    kto_dataset = []

    for item in raw_data:
        prompt = item["instruction"]

        # 방법 1: thumbs-up/down 피드백 직접 활용
        if "response" in item and "thumbs_up" in item:
            entry = {
                "prompt": [{"role": "user", "content": prompt}],
                "completion": [{"role": "assistant", "content": item["response"]}],
                "label": item["thumbs_up"],  # True or False
            }
            kto_dataset.append(entry)

        # 방법 2: rating 기반 이진 변환
        elif "response" in item and "rating" in item:
            entry = {
                "prompt": [{"role": "user", "content": prompt}],
                "completion": [{"role": "assistant", "content": item["response"]}],
                "label": item["rating"] >= 4,  # 4점 이상: desirable
            }
            kto_dataset.append(entry)

        # 방법 3: DPO 쌍별 데이터에서 KTO 데이터 생성
        if "chosen_response" in item and "rejected_response" in item:
            kto_dataset.append({
                "prompt": [{"role": "user", "content": prompt}],
                "completion": [{"role": "assistant", "content": item["chosen_response"]}],
                "label": True,
            })
            kto_dataset.append({
                "prompt": [{"role": "user", "content": prompt}],
                "completion": [{"role": "assistant", "content": item["rejected_response"]}],
                "label": False,
            })

    return kto_dataset

DPO 데이터를 KTO 형식으로 변환하는 것은 간단하지만, 그 반대는 불가능하다. 이것이 KTO의 실용적 이점이다. 쌍별 비교 없이 수집된 이진 피드백 데이터가 이미 존재하는 조직에서는 KTO가 유일한 선택지일 수 있다.

실전 트러블슈팅: 흔한 실패 사례와 복구

실패 사례 1: DPO 학습 후 응답 품질 하락

증상: DPO 학습 후 모델이 짧고 불성실한 응답을 생성하거나, 반대로 극도로 장황한 응답을 생성한다.

원인 분석: chosen과 rejected의 품질 차이가 불분명한 데이터가 다수 포함된 경우 발생한다. 특히 rejected 응답이 실제로는 합리적인 답변인데 chosen보다 "약간 덜 좋은" 수준이면, 모델이 올바른 방향을 학습하지 못한다.

복구 절차:

  1. 데이터 필터링: chosen과 rejected의 reward model 점수 차이가 임계값(예: 0.5) 미만인 쌍을 제거한다.
  2. beta를 높여서(0.3~0.5) reference policy에 가깝게 유지한다.
  3. learning rate를 더 낮춘다(현재 값의 1/2).
  4. SFT 체크포인트가 충분히 학습되었는지 확인한다.

실패 사례 2: KTO에서 desirable/undesirable 비율 불균형

증상: 학습이 수렴하지 않거나, desirable 응답의 확률만 올라가고 undesirable 응답의 확률은 변하지 않는다.

원인 분석: desirable 데이터가 undesirable 데이터보다 5배 이상 많으면, undesirable에 대한 gradient 신호가 희석된다. KTO 논문에서는 desirable:undesirable 비율이 1:1에서 4:1 사이를 권장한다.

복구 절차:

  1. undesirable_weight를 높인다(예: 1.0에서 2.0으로).
  2. undesirable 데이터를 오버샘플링한다.
  3. 비율이 극단적이면(10:1 이상) DPO로 전환을 고려한다.

실패 사례 3: 학습 초반 loss 폭발

증상: 학습 시작 후 수십 스텝 이내에 loss가 급격히 증가하고 NaN이 발생한다.

원인 분석: reference model과 학습 모델의 초기 차이가 너무 크거나, learning rate가 과도하게 높은 경우 발생한다. SFT 체크포인트 대신 base model을 reference로 사용하면 특히 이 문제가 잘 나타난다.

복구 절차:

  1. learning rate를 1/5로 줄인다.
  2. warmup_ratio를 0.1 이상으로 설정한다.
  3. reference model과 학습 모델을 동일한 SFT 체크포인트에서 시작한다.
  4. bf16 대신 fp32로 전환하여 수치 안정성을 확인한다.
  5. gradient clipping을 1.0으로 설정한다.

실패 사례 4: 과적합 - validation loss는 오르는데 train loss만 내려감

증상: train loss는 감소하지만 validation loss가 에폭 중반부터 상승한다. 생성 품질이 training 데이터의 chosen 응답을 그대로 복사하는 양상을 보인다.

복구 절차:

  1. 학습을 validation loss가 최저인 체크포인트에서 중단한다(early stopping).
  2. 데이터 양 대비 에폭 수가 적절한지 확인한다. 보통 1~3 에폭이면 충분하다.
  3. IPO로 전환을 고려한다. IPO는 squared loss 구조 때문에 과적합에 더 강건하다.

UNA: RLHF/DPO/KTO를 통합하는 프레임워크

최근 연구 중 주목할 만한 것은 UNA(Unifying Alignments) 프레임워크(arxiv:2408.15339)다. UNA는 RLHF(PPO), DPO, KTO를 일반화된 암묵적 보상 함수로 통합한다. 핵심 통찰은 세 기법 모두 "암묵적 보상과 명시적 보상의 차이를 최소화하는 supervised learning"으로 재해석될 수 있다는 것이다.

이 관점에서 보면, pairwise 피드백(DPO), binary 피드백(KTO), scalar 피드백(RLHF)은 같은 목적 함수의 서로 다른 특수 케이스에 해당한다. 실무적으로는 보유한 데이터의 형태에 따라 적절한 기법을 선택하면 되고, 여러 형태의 피드백 데이터가 혼재된 경우 UNA 프레임워크가 이를 통합적으로 활용할 수 있다.

정렬 기법 선택 체크리스트

정렬 기법을 선택할 때 다음 체크리스트를 순서대로 확인한다.

데이터 형태 확인

  • 쌍별 비교 데이터(A가 B보다 좋다)가 있는가? → DPO, IPO, SimPO 사용 가능
  • 이진 피드백 데이터(좋다/나쁘다)만 있는가? → KTO 사용
  • 수치 점수(1-5점)가 있는가? → 임계값 기반으로 DPO 또는 KTO 형식으로 변환
  • verifiable한 정답이 존재하는 태스크인가? → GRPO 고려

인프라 확인

  • GPU 메모리 48GB 이상인가? → DPO, IPO, KTO 모두 가능
  • GPU 메모리 24GB 이하인가? → SimPO 또는 ORPO 권장 (reference model 불필요)
  • 별도의 SFT 학습을 할 여유가 있는가? → DPO + SFT 파이프라인
  • SFT와 정렬을 한 번에 끝내고 싶은가? → ORPO

품질 요구사항 확인

  • 과적합이 우려되는가? → IPO (squared loss로 과적합 방지)
  • SFT 없이 base model에 직접 적용해야 하는가? → KTO (rambling 현상 적음)
  • 데이터 품질이 불확실한가? → beta를 높게 설정하고 IPO 사용
  • 최대 성능이 목표인가? → DPO + 고품질 쌍별 데이터 조합

운영 주의사항

  • 학습 전 reference model 체크포인트를 반드시 별도 저장했는가?
  • validation set을 분리했는가? (최소 전체 데이터의 5%)
  • gradient checkpointing을 활성화했는가? (메모리 절약)
  • wandb 등 학습 로깅을 설정했는가?
  • chosen/rejected(또는 desirable/undesirable) 데이터의 품질을 수동으로 검수했는가?

평가 파이프라인: 정렬 결과 검증

정렬 학습 후 모델 품질을 정량적으로 평가하는 것은 필수다. 단순히 loss가 낮아졌다고 정렬이 성공한 것이 아니다.

# 정렬 모델 평가를 위한 win rate 계산 스크립트
import torch
from transformers import AutoModelForCausalLM, AutoTokenizer

def calculate_win_rate(model_path, ref_model_path, eval_prompts, tokenizer_name):
    """정렬 모델 vs reference 모델의 win rate를 reward model로 평가"""
    model = AutoModelForCausalLM.from_pretrained(model_path, torch_dtype=torch.bfloat16)
    ref_model = AutoModelForCausalLM.from_pretrained(ref_model_path, torch_dtype=torch.bfloat16)
    tokenizer = AutoTokenizer.from_pretrained(tokenizer_name)

    wins, total = 0, 0

    for prompt in eval_prompts:
        inputs = tokenizer(prompt, return_tensors="pt")

        # 정렬 모델 응답 생성
        with torch.no_grad():
            aligned_output = model.generate(
                **inputs, max_new_tokens=512,
                temperature=0.7, do_sample=True
            )
            ref_output = ref_model.generate(
                **inputs, max_new_tokens=512,
                temperature=0.7, do_sample=True
            )

        aligned_text = tokenizer.decode(aligned_output[0], skip_special_tokens=True)
        ref_text = tokenizer.decode(ref_output[0], skip_special_tokens=True)

        # 여기에 judge model 또는 reward model로 비교 평가 로직 추가
        # 예: GPT-4를 judge로 사용하거나, 학습된 reward model 활용
        # win = judge(prompt, aligned_text, ref_text)
        # wins += win
        total += 1

    return wins / total if total > 0 else 0.0

평가 시 주의할 점은 self-evaluation(정렬 모델이 자기 자신을 평가)을 피하는 것이다. 별도의 judge model이나 인간 평가를 병행하는 것이 신뢰할 수 있는 결과를 얻는 방법이다. AlpacaEval, MT-Bench, Arena-Hard 같은 표준 벤치마크를 활용하는 것도 권장된다.

2025-2026년 정렬 연구의 방향

현재 정렬 연구는 몇 가지 방향으로 빠르게 진화하고 있다.

Verifier-driven RL: GRPO의 성공에 이어, 수학/코딩 등 정답 검증이 가능한 태스크에서 규칙 기반 보상을 활용하는 방향이 강화되고 있다. 인간 피드백 없이도 정렬이 가능한 영역이 확대되고 있다.

Online DPO / Iterative DPO: 학습 중인 모델이 직접 응답을 생성하고, 이를 기반으로 선호 데이터를 갱신하는 online 방식이 offline DPO 대비 성능 향상을 보이고 있다. 다만 학습 비용이 증가하는 트레이드오프가 존재한다.

Multi-objective alignment: 단순히 "좋은 응답"이 아니라, helpfulness, harmlessness, honesty 등 여러 축을 동시에 최적화하는 연구가 활발하다. Mo-KTO(Multi-Objective KTO) 같은 확장도 제안되고 있다.

Synthetic preference data: 인간 평가자 대신 강력한 LLM(GPT-4, Claude 등)으로 선호 데이터를 생성하는 기법이 보편화되고 있다. 비용은 크게 줄지만, judge model의 편향이 그대로 전이되는 위험이 있어 주의가 필요하다.

마무리

RLHF에서 DPO로, DPO에서 KTO로의 진화는 단순한 알고리즘 개선이 아니라 "정렬에 필요한 최소 조건은 무엇인가"라는 근본적 질문에 대한 답을 찾아가는 과정이다. RLHF는 명시적 보상 모델과 RL 루프가 필요했고, DPO는 보상 모델을 제거했으며, KTO는 쌍별 비교 데이터마저 불필요하게 만들었다.

실무에서의 선택은 이론적 우수성보다는 보유 데이터의 형태, 인프라 제약, 팀의 경험 수준에 따라 결정된다. 쌍별 비교 데이터와 충분한 GPU가 있다면 DPO가 검증된 선택이고, 이진 피드백 데이터만 있다면 KTO가 유일한 선택지다. 메모리가 제한적이라면 SimPO나 ORPO를 고려하고, verifiable 태스크라면 GRPO가 강력한 대안이다.

어떤 기법을 선택하든, 데이터 품질이 모든 것을 결정한다. 1만 건의 고품질 선호 데이터가 10만 건의 저품질 데이터보다 낫다. 학습 전 데이터 검수, 학습 중 validation 모니터링, 학습 후 체계적 평가를 생략하지 않는 것이 성공적인 정렬의 핵심이다.

References

  1. DPO - Rafailov et al., "Direct Preference Optimization: Your Language Model is Secretly a Reward Model", NeurIPS 2023. arxiv:2305.18290
  2. KTO - Ethayarajh & Jurafsky, "KTO: Model Alignment as Prospect Theoretic Optimization", ICML 2024. arxiv:2402.01306
  3. IPO - Azar et al., "A General Theoretical Paradigm to Understand Learning from Human Preferences", AISTATS 2024. arxiv:2310.12036
  4. GRPO - Shao et al., "DeepSeekMath: Pushing the Limits of Mathematical Reasoning in Open Language Models", 2024. arxiv:2402.03300
  5. SimPO - Meng et al., "SimPO: Simple Preference Optimization with a Reference-Free Reward", 2024. arxiv:2405.14734
  6. UNA - "UNA: Unifying Alignments of RLHF/PPO, DPO and KTO by a Generalized Implicit Reward Function", 2024. arxiv:2408.15339
  7. DPO Comprehensive Survey - "A Comprehensive Survey of Direct Preference Optimization: Datasets, Theories, Variants, and Applications", 2024. arxiv:2410.15595
  8. Hugging Face TRL - DPO Trainer Documentation. https://huggingface.co/docs/trl/main/en/dpo_trainer
  9. Hugging Face TRL - KTO Trainer Documentation. https://huggingface.co/docs/trl/main/en/kto_trainer
  10. InstructGPT - Ouyang et al., "Training language models to follow instructions with human feedback", NeurIPS 2022. arxiv:2203.02155