Skip to content
Published on

RLHF에서 DPO까지: LLM 정렬(Alignment) 기술 논문 심층 분석

Authors
  • Name
    Twitter
LLM Alignment Survey

들어가며

대규모 언어 모델(LLM)은 방대한 코퍼스를 학습하여 놀라운 언어 능력을 갖추지만, 사전학습만으로는 인간의 의도와 가치에 부합하는 출력을 보장할 수 없다. 유해한 콘텐츠를 생성하거나, 사용자의 지시를 무시하거나, 사실이 아닌 정보를 자신 있게 서술하는 문제가 빈번하게 발생한다. 이러한 간극을 해소하기 위한 연구 분야가 바로 LLM 정렬(Alignment) 이다.

2022년 OpenAI의 InstructGPT 논문이 RLHF(Reinforcement Learning from Human Feedback) 파이프라인을 체계화한 이후, Anthropic의 Constitutional AI, Stanford의 DPO(Direct Preference Optimization), 그리고 KTO, IPO, ORPO 등 다양한 후속 기법이 등장했다. 이 글에서는 이들 핵심 논문의 수학적 기반, 구현 방식, 실무 적용 시 주의점을 체계적으로 분석한다.

LLM 정렬 문제 정의

왜 정렬이 필요한가

사전학습된 LLM은 인터넷 텍스트의 다음 토큰 예측을 최적화한다. 이 목표는 인간이 원하는 "도움이 되고(Helpful), 정직하며(Honest), 무해한(Harmless)" 출력과 직접적으로 대응하지 않는다. 구체적으로 세 가지 핵심 문제가 있다.

  1. 유용성(Helpfulness): 사용자의 지시를 정확히 이해하고 수행하는 능력
  2. 정직성(Honesty): 모르는 것을 모른다고 인정하고, 환각을 최소화하는 능력
  3. 무해성(Harmlessness): 유해하거나 편향된 콘텐츠 생성을 거부하는 능력

정렬의 수학적 프레임워크

정렬 문제는 보상 함수 r(x, y)를 기반으로 정책 최적화 문제로 형식화된다. 프롬프트 x가 주어졌을 때, 정책 pi가 생성하는 응답 y에 대해 보상을 최대화하되, 사전학습된 참조 정책과의 괴리를 제한한다.

maxπExD,yπ(x)[r(x,y)]βKL[π(x)πref(x)]\max_{\pi} \mathbb{E}_{x \sim \mathcal{D}, y \sim \pi(\cdot|x)} \left[ r(x, y) \right] - \beta \, \text{KL}\left[\pi(\cdot|x) \| \pi_{\text{ref}}(\cdot|x)\right]

여기서 beta는 KL 발산 페널티의 강도를 조절하는 하이퍼파라미터이다. beta가 너무 작으면 보상 해킹(reward hacking)이 발생하고, 너무 크면 학습이 느려진다.

RLHF 파이프라인 심층 분석

InstructGPT의 3단계 파이프라인

Ouyang et al.(2022)의 InstructGPT 논문은 RLHF 파이프라인을 세 단계로 체계화했다.

1단계: 지도 미세조정(SFT)

인간 레이블러가 작성한 시연 데이터로 기본 모델을 미세조정한다. 이 단계에서 모델은 지시를 따르는 기본 능력을 학습한다.

2단계: 보상 모델(RM) 학습

동일 프롬프트에 대해 여러 응답을 생성한 뒤, 인간 레이블러가 선호도 순위를 매긴다. 이 데이터로 보상 모델을 학습한다.

3단계: PPO를 통한 강화학습

보상 모델의 피드백을 이용하여 PPO 알고리즘으로 정책을 최적화한다.

보상 모델 학습 구현

보상 모델은 Bradley-Terry 모델을 기반으로, 선호 쌍(chosen, rejected)에 대한 로그 확률 차이를 최대화하도록 학습된다.

import torch
import torch.nn as nn
from transformers import AutoModelForSequenceClassification, AutoTokenizer

class RewardModel(nn.Module):
    def __init__(self, model_name="gpt2"):
        super().__init__()
        self.model = AutoModelForSequenceClassification.from_pretrained(
            model_name, num_labels=1
        )

    def forward(self, input_ids, attention_mask):
        outputs = self.model(input_ids=input_ids, attention_mask=attention_mask)
        return outputs.logits.squeeze(-1)

def reward_model_loss(reward_model, chosen_ids, chosen_mask, rejected_ids, rejected_mask):
    """Bradley-Terry 선호 모델 기반 보상 모델 손실 함수"""
    r_chosen = reward_model(chosen_ids, chosen_mask)
    r_rejected = reward_model(rejected_ids, rejected_mask)

    # 선호 응답의 보상이 비선호 응답보다 높도록 학습
    loss = -torch.log(torch.sigmoid(r_chosen - r_rejected)).mean()
    accuracy = (r_chosen > r_rejected).float().mean()

    return loss, accuracy

선호 데이터셋 구성

RLHF 파이프라인의 핵심은 고품질 선호 데이터셋 구성이다. 각 샘플은 프롬프트, 선호 응답, 비선호 응답의 삼중쌍으로 구성된다.

from datasets import Dataset

def build_preference_dataset(prompts, model, tokenizer, num_samples=4):
    """프롬프트별로 여러 응답을 생성하고 선호 쌍을 구성하는 파이프라인"""
    preference_data = []

    for prompt in prompts:
        inputs = tokenizer(prompt, return_tensors="pt")
        responses = []

        # 다양한 응답 생성 (temperature sampling)
        for _ in range(num_samples):
            output = model.generate(
                **inputs,
                max_new_tokens=256,
                temperature=0.8,
                do_sample=True,
            )
            response = tokenizer.decode(output[0], skip_special_tokens=True)
            responses.append(response)

        # 실제 파이프라인에서는 인간 레이블러가 순위를 매김
        # 여기서는 보상 모델 점수로 대체
        scored = [(r, reward_model_score(prompt, r)) for r in responses]
        scored.sort(key=lambda x: x[1], reverse=True)

        # 최상위-최하위 쌍을 선호 데이터로 구성
        preference_data.append({
            "prompt": prompt,
            "chosen": scored[0][0],
            "rejected": scored[-1][0],
        })

    return Dataset.from_list(preference_data)

Constitutional AI: AI 피드백 기반 정렬

Anthropic의 접근법

Bai et al.(2022)의 Constitutional AI는 RLHF의 인간 레이블링 의존성을 줄이기 위한 혁신적인 접근법이다. 핵심 아이디어는 AI 스스로가 자신의 출력을 비판하고 개선하도록 하는 것이다. 이 과정은 사전 정의된 원칙(Constitution)에 의해 안내된다.

두 단계 프로세스

Supervised Learning 단계 (자기 비판 및 수정):

  1. 모델이 유해할 수 있는 응답을 생성한다
  2. 헌법 원칙에 따라 자신의 응답을 비판한다
  3. 비판을 반영하여 수정된 응답을 생성한다
  4. 수정된 응답으로 SFT를 수행한다

RL 단계 (RLAIF - RL from AI Feedback):

  1. 두 응답 중 어느 것이 원칙에 더 부합하는지 AI가 판단한다
  2. 이 AI 선호 데이터로 보상 모델을 학습한다
  3. 보상 모델을 이용하여 PPO로 정책을 최적화한다

Constitutional AI의 가장 큰 장점은 인간 레이블링 비용을 대폭 절감하면서도, 원칙 기반의 투명하고 해석 가능한 정렬을 달성한다는 점이다.

DPO: 수학적 기반과 구현

RLHF 목적함수의 해석적 해

Rafailov et al.(2023)의 DPO는 RLHF의 가장 우아한 대안이다. 핵심 통찰은 KL 제약이 있는 보상 최대화 문제에 해석적 해가 존재한다는 것이다.

최적 정책은 다음과 같은 형태를 갖는다.

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

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

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

이를 Bradley-Terry 모델에 대입하면, 보상 모델 없이 직접 정책을 최적화하는 DPO 손실함수가 도출된다.

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

DPO 학습 루프 구현

import torch
import torch.nn.functional as F
from transformers import AutoModelForCausalLM, AutoTokenizer

def compute_dpo_loss(
    policy_model,
    reference_model,
    chosen_ids,
    chosen_mask,
    rejected_ids,
    rejected_mask,
    beta=0.1,
):
    """DPO 손실 함수: 보상 모델 없이 직접 정책 최적화"""
    # 정책 모델의 로그 확률 계산
    policy_chosen_logps = get_log_probs(policy_model, chosen_ids, chosen_mask)
    policy_rejected_logps = get_log_probs(policy_model, rejected_ids, rejected_mask)

    # 참조 모델의 로그 확률 계산 (gradient 불필요)
    with torch.no_grad():
        ref_chosen_logps = get_log_probs(reference_model, chosen_ids, chosen_mask)
        ref_rejected_logps = get_log_probs(reference_model, rejected_ids, rejected_mask)

    # DPO 로그 비율 계산
    chosen_logratios = policy_chosen_logps - ref_chosen_logps
    rejected_logratios = policy_rejected_logps - ref_rejected_logps

    # DPO 손실: -log(sigmoid(beta * (chosen_logratio - rejected_logratio)))
    logits = beta * (chosen_logratios - rejected_logratios)
    loss = -F.logsigmoid(logits).mean()

    # 메트릭: 선호 응답의 암묵적 보상이 더 높은 비율
    reward_accuracy = (logits > 0).float().mean()

    return loss, reward_accuracy

def get_log_probs(model, input_ids, attention_mask):
    """시퀀스의 토큰별 로그 확률 합산"""
    outputs = model(input_ids=input_ids, attention_mask=attention_mask)
    logits = outputs.logits[:, :-1, :]
    labels = input_ids[:, 1:]
    log_probs = F.log_softmax(logits, dim=-1)
    selected_log_probs = torch.gather(
        log_probs, dim=-1, index=labels.unsqueeze(-1)
    ).squeeze(-1)
    mask = attention_mask[:, 1:]
    return (selected_log_probs * mask).sum(dim=-1)

DPO의 장단점

장점:

  • 보상 모델 학습이 불필요하여 파이프라인이 단순해짐
  • PPO 대비 메모리 사용량이 적음 (2개 모델 vs 4개 모델)
  • 하이퍼파라미터 튜닝이 상대적으로 간단

단점:

  • 참조 모델과의 분포 이동(distribution shift)에 취약
  • 오프라인 데이터에 의존하여 탐색(exploration)이 부족
  • 선호 데이터의 품질에 매우 민감

PPO 학습 안정성

PPO in RLHF: 구현의 핵심

PPO(Proximal Policy Optimization)는 RLHF에서 가장 널리 사용되는 강화학습 알고리즘이다. 핵심은 클리핑 메커니즘으로, 정책 업데이트의 크기를 제한하여 학습 안정성을 보장한다.

import torch
import torch.nn.functional as F

def ppo_step(
    policy_model,
    value_model,
    old_log_probs,
    states,
    actions,
    rewards,
    advantages,
    clip_epsilon=0.2,
    value_clip=0.2,
    kl_coeff=0.1,
    ref_log_probs=None,
):
    """PPO 업데이트 스텝 (LLM RLHF용)"""
    # 현재 정책의 로그 확률
    new_log_probs = policy_model.get_log_probs(states, actions)

    # 중요도 비율 계산
    ratio = torch.exp(new_log_probs - old_log_probs)

    # 클리핑된 대리 목적함수
    surr1 = ratio * advantages
    surr2 = torch.clamp(ratio, 1.0 - clip_epsilon, 1.0 + clip_epsilon) * advantages
    policy_loss = -torch.min(surr1, surr2).mean()

    # 가치 함수 손실 (클리핑 적용)
    new_values = value_model(states)
    value_loss = F.mse_loss(new_values, rewards)

    # KL 발산 페널티 (참조 모델 대비)
    kl_penalty = 0.0
    if ref_log_probs is not None:
        kl_div = (old_log_probs - ref_log_probs).mean()
        kl_penalty = kl_coeff * kl_div

    total_loss = policy_loss + 0.5 * value_loss + kl_penalty

    return total_loss, {
        "policy_loss": policy_loss.item(),
        "value_loss": value_loss.item(),
        "kl_penalty": kl_penalty if isinstance(kl_penalty, float) else kl_penalty.item(),
        "mean_ratio": ratio.mean().item(),
    }

PPO 학습의 주요 불안정성 원인

  1. 보상 해킹(Reward Hacking): 보상 모델의 취약점을 악용하여 실제로 좋지 않은 출력에 높은 보상을 받는 현상
  2. KL 발산 폭발: 참조 모델과의 거리가 급격히 증가하여 의미 없는 텍스트 생성
  3. 가치 함수 추정 오류: 비정상적으로 큰 보상 추정치가 학습을 불안정하게 만듦
  4. 긴 시퀀스 문제: 토큰 수가 많을수록 크레딧 할당(credit assignment) 문제가 심화

최신 정렬 기법: KTO, IPO, ORPO

KTO (Kahneman-Tversky Optimization)

Ethayarajh et al.(2024)의 KTO는 쌍별 선호 데이터 없이 정렬을 수행한다. 행동경제학의 전망 이론(Prospect Theory)을 차용하여, 각 응답이 "바람직한가/바람직하지 않은가"의 이진 신호만으로 학습한다.

KTO의 핵심은 인간의 의사결정이 손실에 더 민감하다는 손실 회피(Loss Aversion) 특성을 반영한 것이다. 바람직하지 않은 응답에 대한 패널티를 바람직한 응답에 대한 보상보다 더 크게 설정한다.

IPO (Identity Preference Optimization)

Azar et al.(2024)의 IPO는 DPO의 과적합(overfitting) 문제를 해결하기 위해 정규화 항을 추가한다. DPO는 학습 데이터에 과적합되면 선호 응답의 확률을 극단적으로 높이는 경향이 있는데, IPO는 이를 방지한다.

LIPO=(logπθ(ywx)πref(ywx)logπθ(ylx)πref(ylx)12β)2\mathcal{L}_{\text{IPO}} = \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)} - \frac{1}{2\beta}\right)^2

ORPO (Odds Ratio Preference Optimization)

Hong et al.(2024)의 ORPO는 SFT와 선호 최적화를 단일 단계로 통합한다. 참조 모델이 불필요하여 메모리와 계산 비용을 크게 절감한다. 핵심은 승산비(Odds Ratio) 기반 패널티 항이다.

import torch
import torch.nn.functional as F

def compute_orpo_loss(model, chosen_ids, chosen_mask, rejected_ids, rejected_mask, alpha=1.0):
    """ORPO 손실: SFT + Odds Ratio 기반 선호 최적화 통합"""
    # SFT 손실 (선호 응답에 대한 NLL)
    chosen_logits = model(input_ids=chosen_ids, attention_mask=chosen_mask).logits
    sft_loss = F.cross_entropy(
        chosen_logits[:, :-1, :].reshape(-1, chosen_logits.size(-1)),
        chosen_ids[:, 1:].reshape(-1),
        reduction="mean",
    )

    # 로그 확률 계산
    chosen_logps = get_sequence_log_probs(model, chosen_ids, chosen_mask)
    rejected_logps = get_sequence_log_probs(model, rejected_ids, rejected_mask)

    # 승산비(Odds Ratio) 계산
    chosen_odds = chosen_logps - torch.log1p(-torch.exp(chosen_logps))
    rejected_odds = rejected_logps - torch.log1p(-torch.exp(rejected_logps))
    log_odds_ratio = chosen_odds - rejected_odds

    # ORPO 최종 손실
    orpo_loss = sft_loss - alpha * F.logsigmoid(log_odds_ratio).mean()

    return orpo_loss

비교 분석

각 정렬 기법의 핵심 특성을 비교하면 다음과 같다.

기법데이터 요구사항모델 수계산 비용안정성성능
RLHF (PPO)쌍별 선호4 (정책, 참조, 보상, 가치)매우 높음낮음 (튜닝 필요)높음 (최적 튜닝 시)
DPO쌍별 선호2 (정책, 참조)중간높음높음
KTO이진 피드백 (비쌍별)2 (정책, 참조)중간높음중상
IPO쌍별 선호2 (정책, 참조)중간매우 높음중상
ORPO쌍별 선호1 (정책만)낮음높음중상

핵심 트레이드오프:

  • RLHF: 최고 성능 잠재력이 있지만, 학습 불안정성과 엔지니어링 복잡도가 높다
  • DPO: 구현 단순성과 성능의 균형이 뛰어나다. 현재 가장 널리 채택된 기법
  • KTO: 쌍별 데이터 구축이 어려운 환경에서 강력한 대안
  • IPO: DPO의 과적합 문제가 심한 소규모 데이터셋에 적합
  • ORPO: 계산 자원이 제한된 환경에서 효율적인 선택

실무 적용 가이드

기법 선택 기준

실무에서 정렬 기법을 선택할 때는 다음 기준을 고려해야 한다.

  1. 데이터 형태: 쌍별 선호 데이터가 있는가? 이진 피드백만 가능한가?
  2. 계산 자원: GPU 메모리와 학습 시간의 제약은?
  3. 팀 역량: PPO 튜닝 경험이 있는가?
  4. 모델 크기: 7B 이하라면 DPO, 70B 이상이라면 RLHF 검토

평가 메트릭 구현

정렬 결과를 평가하기 위한 핵심 메트릭은 다음과 같다.

import numpy as np
from typing import List, Dict

def compute_alignment_metrics(
    model_responses: List[str],
    reference_responses: List[str],
    reward_model,
    tokenizer,
) -> Dict[str, float]:
    """정렬 품질 평가를 위한 종합 메트릭 계산"""
    metrics = {}

    # 1. 보상 모델 점수
    rewards = []
    for response in model_responses:
        tokens = tokenizer(response, return_tensors="pt")
        score = reward_model(**tokens).item()
        rewards.append(score)
    metrics["mean_reward"] = np.mean(rewards)
    metrics["reward_std"] = np.std(rewards)

    # 2. 승률 (win rate) - 참조 모델 대비
    wins = 0
    for model_resp, ref_resp in zip(model_responses, reference_responses):
        model_tokens = tokenizer(model_resp, return_tensors="pt")
        ref_tokens = tokenizer(ref_resp, return_tensors="pt")
        model_score = reward_model(**model_tokens).item()
        ref_score = reward_model(**ref_tokens).item()
        if model_score > ref_score:
            wins += 1
    metrics["win_rate"] = wins / len(model_responses)

    # 3. 응답 길이 분포 (길이 해킹 탐지)
    lengths = [len(r.split()) for r in model_responses]
    metrics["mean_length"] = np.mean(lengths)
    metrics["length_std"] = np.std(lengths)

    # 4. 다양성 지표 (모드 붕괴 탐지)
    unique_bigrams = set()
    total_bigrams = 0
    for response in model_responses:
        words = response.split()
        for i in range(len(words) - 1):
            bigram = (words[i], words[i + 1])
            unique_bigrams.add(bigram)
            total_bigrams += 1
    metrics["distinct_2"] = len(unique_bigrams) / max(total_bigrams, 1)

    return metrics

실패 사례와 교훈

1. 보상 해킹 (Reward Hacking)

RLHF에서 가장 흔한 실패 모드이다. 보상 모델이 "긴 응답 = 좋은 응답"이라는 허위 상관관계를 학습하면, 정책 모델은 내용과 무관하게 지나치게 긴 응답을 생성한다. InstructGPT 논문에서도 이 문제를 보고했으며, 길이 정규화(length normalization)와 KL 페널티 강화로 부분적으로 해결했다.

교훈: 보상 모델의 편향을 지속적으로 모니터링하고, 다양한 프록시 메트릭으로 교차 검증해야 한다.

2. 모드 붕괴 (Mode Collapse)

PPO 학습 중 정책이 보상 모델에서 높은 점수를 받는 소수의 패턴만 반복 생성하는 현상이다. 다양한 질문에 동일한 구조의 응답을 출력하거나, 특정 표현을 과도하게 반복한다.

교훈: KL 발산 제약을 적절히 유지하고, 응답 다양성 메트릭을 학습 중 모니터링해야 한다.

3. DPO의 분포 이동 (Distribution Shift)

DPO는 오프라인 선호 데이터로 학습하므로, 학습이 진행될수록 현재 정책이 생성하는 분포와 학습 데이터의 분포가 괴리된다. 이로 인해 학습 후반부에 성능이 저하될 수 있다.

교훈: 반복적(iterative) DPO를 적용하여 매 라운드마다 현재 정책으로 새로운 선호 데이터를 생성하거나, 온라인 DPO 변형을 사용해야 한다.

4. Constitutional AI의 원칙 충돌

헌법에 포함된 원칙들이 서로 충돌하는 경우가 발생한다. 예를 들어 "도움이 되어라"와 "유해한 정보를 제공하지 마라"는 원칙이 특정 맥락에서 상충할 수 있다.

교훈: 원칙 간 우선순위를 명확히 정의하고, 경계 사례(edge cases)에 대한 별도의 가이드라인을 마련해야 한다.

참고자료

  1. Ouyang, L., et al. (2022). "Training language models to follow instructions with human feedback." NeurIPS 2022. https://arxiv.org/abs/2203.02155

  2. Rafailov, R., et al. (2023). "Direct Preference Optimization: Your Language Model is Secretly a Reward Model." NeurIPS 2023. https://arxiv.org/abs/2305.18290

  3. Bai, Y., et al. (2022). "Constitutional AI: Harmlessness from AI Feedback." Anthropic. https://arxiv.org/abs/2212.08073

  4. Ethayarajh, K., et al. (2024). "KTO: Model Alignment as Prospect Theoretic Optimization." https://arxiv.org/abs/2402.01306

  5. Hong, J., et al. (2024). "ORPO: Monolithic Preference Optimization without Reference Model." https://arxiv.org/abs/2403.07691

  6. Azar, M. G., et al. (2024). "A General Theoretical Paradigm to Understand Learning from Human Feedback." (IPO) https://arxiv.org/abs/2310.12036

  7. Schulman, J., et al. (2017). "Proximal Policy Optimization Algorithms." https://arxiv.org/abs/1707.06347

  8. Hugging Face Blog. "Preference Tuning LLMs with Direct Preference Optimization Methods." https://huggingface.co/blog/pref-tuning