---
title: "Direct Preference Optimization 해부: 사람의 A가 B보다 낫다는 말을 어떻게 바로 학습할까"
slug: "direct-preference-optimization-deep-dive"
category: "AI / LLM"
topic: "ai-llm"
subtopic: "fine-tuning"
tags: ["DPO","Direct Preference Optimization","RLHF","Preference Learning","Reward Model","SFT","Paper Review"]
status: "published"
created: "2026-07-24"
updated: "2026-07-24"
summary: "Rafailov 외의 Direct Preference Optimization을 RLHF의 보상 모델·PPO와 비교해, Bradley–Terry 선호 확률에서 KL 제약 정책까지의 수식 유도, 실제 공개 코드의 token log-probability 경로, 데이터 계약·평가·운영 한계까지 12세 독자 기준으로 해부한다. DPO가 보상·안전·권한 판단을 자동으로 해결하지 않는 이유도 직접 검증 경계와 함께 설명한다."
kind: "Deep Dive"
evidence: "Rafailov et al. arXiv:2305.18290v3·공식 direct-preference-optimization 고정 commit·Python 3.9 식/토큰 로그확률 산술·공개 소스 compile 직접 검증"
series: "LLM 시스템 지도 — 2단계"
---

> **2단계 네 번째 논문.** 이 글은 Rafael Rafailov 외, [*Direct Preference Optimization: Your Language Model is Secretly a Reward Model*](https://arxiv.org/abs/2305.18290) arXiv v3(2024-07-29, NeurIPS 2023 논문)를 직접 읽고 썼다. 여기서 DPO(Direct Preference Optimization, 직접 선호 최적화)는 논문 Eq. 7의 **원래 DPO loss**를 뜻한다. 논문 이후의 IPO(Identity Preference Optimization, 항등 선호 최적화), conservative DPO, 현행 API 모델·안전 정책을 같은 것으로 취급하지 않는다. 구현은 저자 공식 [repository commit `f8b8c0f`](https://github.com/eric-mitchell/direct-preference-optimization/tree/f8b8c0f49dc92a430bae41585f9d467d3618fe2f)의 `trainers.py`와 `preference_datasets.py`를 직접 읽었다.

## 이 글에서 얻을 답과 범위

이전 InstructGPT 글의 RLHF(Reinforcement Learning from Human Feedback, 인간 피드백 강화학습)는 세 단계를 거쳤다.

```text
좋은 답을 직접 모방하는 SFT(Supervised Fine-Tuning, 지도 미세조정)
  → 여러 답 중 사람이 고른 순위로 RM(Reward Model, 보상 모델) 학습
  → RM 점수를 높이도록 PPO(Proximal Policy Optimization, 근접 정책 최적화) rollout
```

그 과정은 강력하지만, RM·policy·value function 등 여러 model과 rollout(현재 policy가 새 답을 생성하는 과정), KL(Kullback–Leibler, 쿨백-라이블러) 제약 조정이 필요하다. DPO 논문의 질문은 단순하다.

```text
사람이 "이 prompt에서는 답 A가 답 B보다 낫다"라고 알려 줬다면,
왜 먼저 숫자 reward model을 따로 만들고 RL로 다시 최적화해야 할까?

그 비교를 language model policy 자체의 loss로 바로 바꿀 수 없을까?
```

DPO의 답은 “가능하다. 단, reward와 policy의 관계를 수학으로 바꾸고, reference policy(참조 정책)를 함께 둔다”이다.

```text
offline preference pair: (prompt x, chosen y_w, rejected y_l)
  + frozen reference π_ref
  + trainable policy π_θ
  → chosen의 상대 log-probability는 reference보다 올리고
  → rejected의 상대 log-probability는 reference보다 내리는
  → binary cross-entropy(이진 교차 엔트로피) loss
```

이 글을 다 읽으면 다음을 설명할 수 있어야 한다.

1. DPO는 어떤 RLHF 문제를 줄이고, 무엇은 전혀 해결하지 않는가?
2. policy, reference, log probability, pairwise preference, KL을 한 쌍의 답으로 계산할 수 있는가?
3. 왜 partition function(분배 함수) `Z(x)`가 사라지고, 왜 그 사실이 DPO를 가능하게 하는가?
4. DPO loss는 SFT와 단순히 “좋은 답 확률 올리고 나쁜 답 확률 내리기”가 어떻게 다른가?
5. 실제 공개 구현은 prompt token을 왜 mask하고, chosen/rejected를 어떻게 한 batch에 넣고, reference에는 왜 gradient를 보내지 않는가?
6. β(beta)는 무엇을 조절하며, loss·implicit reward·KL 거리를 어떻게 연결하는가?
7. DPO가 PPO보다 단순해도 왜 data quality·사람 평가·RAG(Retrieval-Augmented Generation, 검색 증강 생성)·권한 제어가 여전히 필요한가?
8. 백엔드·DBA(Database Administrator, 데이터베이스 관리자)라면 preference data pipeline, release gate, rollback을 어떻게 설계하는가?

**범위:** 원 논문의 RLHF 배경, Eq. 1–10과 Appendix A/B/C, IMDb sentiment·Reddit TL;DR·Anthropic-HH 실험, 논문의 한계를 설명한다. 공식 repository의 original DPO path도 코드 수준에서 확인한다. DPO를 수천 GPU로 재학습하거나, 현행 chat model의 alignment·법적 적합성·사람 전체의 가치를 재현·증명하는 글은 아니다.

### 먼저 외울 한 문장

> **DPO는 사람이 고른 답과 고르지 않은 답의 쌍을 보고, trainable policy가 frozen reference보다 chosen 답에는 더 큰 상대 확률을, rejected 답에는 더 작은 상대 확률을 주도록 이진 분류 loss로 학습하는 방법이다. 별도 scalar reward model과 PPO rollout을 생략하지만, 사람 선호 data·reference policy·평가·안전한 시스템 경계를 생략하지는 않는다.**

`상대`, `frozen reference`, `사람 선호 쌍`, `생략하지 않는 것` 네 부분이 빠지면 DPO를 오해한다.

## 왜 필요한가: “좋은 답”은 정답 한 줄보다 비교로 얻기 쉬울 때가 있다

### 12살 비유: 채점표를 따로 만들지 않고 두 숙제의 차이를 바로 배우는 법

선생님이 학생에게 같은 질문의 답 두 개를 보여 준다.

```text
질문: "장애 공지를 세 문장으로 요약해 줘"

답 A: 원인·영향·복구 시점을 세 문장에 정확히 썼다.
답 B: 장황하고 복구 시점이 없다.

선생님: "A가 B보다 낫다."
```

전통 RLHF는 먼저 “어떤 답이 몇 점인가”를 잘 맞히는 별도 채점기(RM)를 만든다. 그 뒤 학생이 새 답을 많이 써 보게 하고, 채점기 점수가 오르도록 PPO로 고친다.

```text
선호 쌍 → RM: A=8.1점, B=2.3점이라고 추정
       → policy가 새 답을 생성
       → RM 점수와 KL 벌점으로 PPO update
```

DPO는 다음처럼 묻는다.

```text
선생님이 A>B라고만 말해도,
학생의 "A를 B보다 얼마나 더 믿는가"를 직접 고치면 되지 않을까?
```

여기서 중요한 차이가 있다. DPO는 “A의 확률만 무조건 높이자”가 아니다. 원래 SFT model이 A와 B를 각각 얼마나 자연스럽다고 봤는지(reference)를 기준점으로 남긴다. 그리고 policy가 이미 B를 더 좋게 믿으면 그 pair를 더 세게 고치고, 이미 A를 충분히 높게 믿으면 덜 민다. 이 **동적 가중치**가 단순 chosen-only SFT와 DPO를 가르는 핵심이다.

### RLHF pipeline에서 실제로 무거운 부분

**논문 확인:** DPO 논문 §1·§3은 기존 RLHF가 SFT → preference sampling/reward learning → RL optimization의 세 단계라고 설명한다. PPO 단계는 policy가 답을 생성하고, RM·reference·value function을 통과하며, policy가 바뀔 때마다 새 distribution을 다뤄야 한다. 그래서 여러 model 상태, sampling 비용, RL hyperparameter(학습률·clip·KL coefficient 등) tuning이 필요해질 수 있다.

하지만 “PPO가 복잡하니 나쁘고 DPO가 항상 정답”은 논문 결론이 아니다. PPO는 RM을 새 policy의 output에 적용하고 online data를 모아 갱신하는 확장 경로를 가질 수 있다. DPO는 주어진 offline(고정 수집) pair를 매우 단순하게 학습하지만, pair가 cover하지 못한 위험한 prompt나 새 도메인을 자동으로 발견하지 않는다.

### DPO가 겨냥한 정확한 문제

원 논문이 줄이려 한 것은 다음이다.

| 기존 RLHF의 구성 | 왜 필요한가 | DPO가 줄이는 부분 | DPO에도 남는 것 |
|---|---|---|---|
| SFT | preference pair의 답 분포에 model을 먼저 맞춤 | 제거하지 않음 | base/reference policy 준비 |
| explicit RM | A>B를 scalar reward로 일반화 | 독립 RM 학습 제거 | policy 안의 implicit reward(암묵 보상) |
| PPO rollout | RM 보상 최대화·KL 제약 | training gradient loop의 RL rollout 제거 | policy와 reference의 forward 계산 |
| value function | PPO advantage/분산 감소 | 제거 | DPO pairwise classification loss |
| 사람 선호 data | 무엇이 더 나은지 신호 | 제거하지 않음 | prompt·chosen·rejected·rubric |
| 독립 human evaluation | RM/loss가 실제 품질과 맞는지 확인 | 제거하지 않음 | heldout 평가와 release gate |

**논문 확인:** 논문 초록은 DPO가 fine-tuning 중 LM에서 sample하지 않고, 별도 reward model이나 RL을 수행하지 않는 simple classification loss를 제안한다고 말한다. 여기서 “fine-tuning 중 sample하지 않는다”는 gradient를 계산할 때의 말이다. 공식 구현은 evaluation sample을 보여 줄 때 policy와 reference의 `generate()`를 호출할 수 있다([`trainers.py` L182–L208](https://github.com/eric-mitchell/direct-preference-optimization/blob/f8b8c0f49dc92a430bae41585f9d467d3618fe2f/trainers.py#L182-L208)). “DPO는 코드 어디에서도 generate를 호출하지 않는다”라고 말하면 틀리다.

## 무엇인가: DPO의 정의와 하지 않는 일

### DPO는 preference pair로 policy를 학습하는 offline loss다

DPO dataset의 최소 row는 다음 세 값이다.

```text
x   = prompt
y_w = chosen, 사람이 더 낫다고 고른 completion
y_l = rejected, 사람이 덜 낫다고 고른 completion
```

여기에 두 model이 필요하다.

```text
π_ref(y | x): frozen reference policy. 보통 SFT checkpoint.
π_θ(y | x): trainable policy. 보통 같은 SFT checkpoint에서 시작.
```

`π(y|x)`는 답 하나를 단번에 고르는 함수가 아니라, answer token을 한 개씩 낼 확률의 곱이다.

$$
\pi(y\mid x)=\prod_{t=1}^{T}\pi(y_t\mid x,y_{<t})
$$

`T`는 completion token 수, `y_t`는 t번째 답 token이다. 확률을 계속 곱하면 매우 작은 수가 되어 컴퓨터에서 underflow(너무 작아 0처럼 되는 현상)가 나기 쉽다. 그래서 구현은 곱 대신 log probability(로그 확률)를 더한다.

$$
\log\pi(y\mid x)=\sum_{t=1}^{T}\log\pi(y_t\mid x,y_{<t})
$$

chosen와 rejected의 두 log probability만으로 DPO의 한 example loss를 만들 수 있다.

### DPO가 하지 않는 일

- DPO는 “진실 판별기”가 아니다. 사람 또는 AI evaluator가 만든 pair에서 어떤 답을 더 선호했는지 학습할 뿐이다.
- DPO는 최신 사내 문서·DB 상태를 parameter 안에 넣지 않는다. 최신성·근거성은 retrieval, source freshness, ACL(Access Control List, 접근 제어 목록)이 맡아야 한다.
- DPO는 model output을 바꾼다. SQL 실행·송금·개인정보 열람 권한을 주거나 회수하지 않는다. backend authorization(인가)과 approval(승인) 경계를 대체하지 않는다.
- DPO는 별도 **명시적** scalar RM을 학습하지 않는다. 그렇다고 reward 개념이 사라지는 것은 아니다. `β log(π_θ/π_ref)`라는 **암묵 보상**으로 다시 표현된다.
- DPO는 “human preference”를 모든 인간의 합의로 바꾸지 않는다. 누가 어떤 rubric(채점 기준)으로 A>B를 골랐는지가 model 행동에 남는다.
- DPO는 PPO보다 작은 code path일 수 있지만, 자동으로 더 싸거나 더 안전한 production system을 보장하지 않는다. policy/reference forward, preference data 수집, human evaluation, serving은 여전히 비용이다.

## 선행 개념: 네 개의 숫자로 DPO 준비하기

### 1. preference는 절대 점수가 아니라 순서다

사람이 `A>B`라고 표시한 것은 보통 “A는 9점, B는 2점”을 뜻하지 않는다. A와 B 중 어느 것이 더 낫다고 본 **상대 판단**이다. DPO 논문은 Bradley–Terry(브래들리–테리) preference model을 쓴다.

$$
P(y_1 \succ y_2\mid x)=\sigma(r(x,y_1)-r(x,y_2))
$$

`r(x,y)`는 답의 latent reward(눈에 보이지 않는 선호 점수), `σ(z)=1/(1+e^{-z})`는 sigmoid(시그모이드)다. A와 B의 reward 차이가 커지면 A가 이길 확률이 1에 가까워진다.

reward 둘에 같은 `+10`을 더해도 차이는 변하지 않는다. 그래서 preference data만으로 “reward 7.0이 절대적으로 좋다”를 식별할 수 없다. 어떤 prompt `x`에만 의존하는 offset `f(x)`는 두 답의 차이에서 사라진다. 논문 §5의 reward equivalence class(보상 동치류)는 바로 이 성질을 다룬다.

### 2. reference는 정답지가 아니라 출발선이다

reference `π_ref`는 대개 SFT policy다. “이 분포에서 너무 멀리 뛰지 말라”는 기준선이다.

```text
reference가 "간결하고 근거를 붙이는 답"을 이미 0.08 확률로 냄
policy가 preference data 뒤 그 답을 0.20 확률로 냄
  → policy/reference 비율이 2.5배
```

그 비율을 log로 보면 `log 2.5`다. chosen와 rejected 각각의 비율을 비교하면 policy가 reference에 비해 **어느 쪽을 상대적으로 더 밀었는지** 알 수 있다.

reference는 권위 있는 사실 source도, 사람 평가자의 대체물도 아니다. checkpoint·tokenizer·chat template·max length가 바뀌면 같은 text의 log probability도 달라질 수 있다. 따라서 `reference_model_version`과 preprocessing version은 dataset 계약의 일부다.

### 3. KL은 “원래 말투에서 얼마나 멀어졌는가”를 재는 평균 거리다

RLHF의 constrained objective는 다음처럼 쓴다.

$$
\max_{\pi}\;\mathbb E_{x,y\sim\pi(y\mid x)}[r(x,y)]
-\beta D_{KL}(\pi(y\mid x)\Vert\pi_{ref}(y\mid x))
$$

`D_KL`은 policy의 전체 확률분포가 reference에서 얼마나 달라졌는지 평균적으로 재는 값이다. `β>0`은 reward를 얻기 위해 reference를 떠나는 데 주는 대가의 크기다. 큰 β는 같은 reward에서 reference 가까이에 머물게 하는 방향이다.

한 completion의 `log π_θ(y|x)-log π_ref(y|x)`는 sample-level log-ratio다. 양수·음수가 모두 가능하다. 기대값으로 계산한 KL divergence는 0 이상이지만, 답 하나의 log-ratio를 보고 “KL이 음수”라고 판단하면 안 된다.

### 4. 이진 cross-entropy는 “A를 고를 확률”을 맞히는 loss다

`z`가 “chosen이 rejected보다 얼마나 앞서는가”인 logit(로짓)라면, 사람이 chosen을 골랐다는 label의 binary cross-entropy는 다음이다.

$$
-\log\sigma(z)
$$

`z`가 크고 양수면 chosen 선호 확률이 커져 loss가 작아진다. DPO에서 이 `z`는 raw model score가 아니라 policy/reference의 **두 개 상대 log-ratio 차이**다. 다음 절에서 그 식이 어디서 왔는지 유도한다.

## 밑바닥 원리: 보상 식을 policy 식으로 바꾸면 DPO가 된다

### 1. 시작점은 PPO와 같은 KL-제약 reward 최대화다

논문 Eq. 3의 출발점은 “prompt `x`마다 reward가 높은 답을 내되, reference에서 너무 멀어지지 말자”다. 이 목적의 최적 policy는 Eq. 4처럼 쓸 수 있다.

$$
\pi_r(y\mid x)=\frac{1}{Z(x)}\pi_{ref}(y\mid x)
\exp\left(\frac{1}{\beta}r(x,y)\right)
$$

직관은 다음과 같다.

```text
reference 확률이 높다         → 원래 자연스럽던 답
reward가 높다                 → 사람 선호를 더 반영한 답
Z(x)로 모두 나눈다            → 답 확률의 합이 1이 되게 정규화
```

`Z(x)`는 prompt마다 모든 가능한 completion을 더해야 하는 partition function이다.

$$
Z(x)=\sum_y\pi_{ref}(y\mid x)\exp\left(\frac{1}{\beta}r(x,y)\right)
$$

언어의 모든 가능한 문장은 엄청나게 많다. `Z(x)`를 정확히 계산하는 것은 현실적이지 않다. DPO의 핵심은 이 어려운 합을 계산하지 않고도 pairwise preference likelihood를 만들 수 있다는 데 있다.

### 2. Eq. 4를 뒤집어 reward를 policy로 쓴다

Eq. 4 양변에 log를 취해 정리하면 논문 Eq. 5가 된다.

$$
r(x,y)=\beta\log\frac{\pi_r(y\mid x)}{\pi_{ref}(y\mid x)}
+\beta\log Z(x)
$$

첫 항은 “policy가 reference보다 이 답을 얼마나 더 또는 덜 밀었는가”다. 둘째 항 `β log Z(x)`는 prompt `x`에만 의존한다. 한 prompt의 chosen와 rejected를 빼면?

$$
\begin{aligned}
r(x,y_w)-r(x,y_l)
=&\;\beta\log\frac{\pi_r(y_w\mid x)}{\pi_{ref}(y_w\mid x)}\\
 &-\beta\log\frac{\pi_r(y_l\mid x)}{\pi_{ref}(y_l\mid x)}
\end{aligned}
$$

`+β log Z(x)`가 두 번 똑같이 나와 사라진다. 이 cancellation(상쇄)이 없으면 DPO는 모든 possible response를 더하는 `Z(x)`를 알아야 했을 것이다.

### 3. Bradley–Terry에 넣으면 DPO loss가 나온다

사람이 `y_w ≻ y_l`을 골랐을 확률에 방금의 reward 차이를 넣으면, trainable policy `π_θ`의 DPO loss(Eq. 7)는 다음이다.

$$
\mathcal L_{DPO}(\pi_\theta;\pi_{ref})=
-\mathbb E_{(x,y_w,y_l)\sim D}
\left[
\log\sigma\left(
\beta\log\frac{\pi_\theta(y_w\mid x)}{\pi_{ref}(y_w\mid x)}
-\beta\log\frac{\pi_\theta(y_l\mid x)}{\pi_{ref}(y_l\mid x)}
\right)
\right]
$$

기호를 실제 값으로 바꾸면 더 쉽다.

```text
policy가 chosen을 준 log-probability       = -2.0
policy가 rejected를 준 log-probability     = -2.8
reference가 chosen을 준 log-probability    = -2.4
reference가 rejected를 준 log-probability  = -2.7

policy pair 차이      = -2.0 - (-2.8) = 0.8
reference pair 차이   = -2.4 - (-2.7) = 0.3
policy가 reference보다 벌린 pair margin = 0.8 - 0.3 = 0.5
β=0.1이면 sigmoid에 들어갈 값 = 0.05
loss = -log(sigmoid(0.05)) ≈ 0.668460
```

loss를 줄이는 gradient는 chosen의 log-probability를 올리고 rejected의 log-probability를 내리는 방향이다. 그러나 이미 policy가 chosen을 reference보다 훨씬 더 밀어 margin이 충분히 크면 `sigmoid`가 포화되어 update가 약해진다. 반대로 rejected를 chosen보다 더 높게 둔 pair는 큰 update를 받는다. 논문 Eq. 7 아래 gradient 설명의 “dynamic, per-example importance weight(예제별 동적 중요도)”다.

### 4. DPO는 SFT와 단순 unlikelihood가 아니다

SFT는 chosen만 놓고 보통 다음을 최대화한다.

$$
\log\pi_\theta(y_w\mid x)
$$

그러면 rejected가 왜 나쁜지, reference 대비 어느 정도 바꿔야 하는지가 loss에 직접 들어가지 않는다.

naïve unlikelihood(단순 비선호 likelihood 감소)는 chosen 확률을 올리고 rejected 확률을 내리는 항을 일정하게 세게 줄 수 있다. **논문 확인:** DPO Appendix C.3은 이 baseline이 summarization·dialogue처럼 복잡한 문제에서 의미 없는 반복 출력을 냈다고 기록한다. 그 이유를 “rejected를 내리는 학습은 항상 실패한다”로 일반화하면 안 된다. 논문이 보여 준 것은 이 baseline의 unconstrained likelihood minimization(제약 없는 우도 최소화)이 해당 조건에서 불안정했다는 사례다.

DPO는 reference-relative margin과 sigmoid 가중치를 함께 사용한다. chosen를 많이 올리고 rejected를 많이 내리는 행동 자체가 아니라, **사람이 고른 순서와 reference로부터의 변화량을 함께 맞히는 것**이 목적이다.

## 내부 구조와 실제 실행 흐름: 한 pair가 GPU에서 loss가 되기까지

논문 그림만 보면 DPO는 “한 loss”처럼 보인다. 실제 implementation에서는 data formatting, tokenizer, response-only masking, policy/reference forward, batch packing, metric, optimizer가 정확히 맞물려야 한다. 하나라도 달라지면 Eq. 7을 쓴다고 해도 다른 학습이 된다.

### 1. DPO의 데이터 계약: prompt 하나에 답 두 개와 순서 하나

공식 repository의 dataset adapter는 prompt마다 다음 구조를 만든다.

```text
prompt: "사용자 질문 또는 문맥"
responses: [답0, 답1, ...]
pairs: [(더 좋은 답 index, 덜 좋은 답 index), ...]
sft_target: SFT에 쓸 목표 답
```

예를 들어 Anthropic-HH adapter는 `chosen`과 `rejected`에서 같은 prompt prefix를 분리하고, `(chosen index, rejected index)` pair를 저장한다([`preference_datasets.py` L120–L158](https://github.com/eric-mitchell/direct-preference-optimization/blob/f8b8c0f49dc92a430bae41585f9d467d3618fe2f/preference_datasets.py#L120-L158)). StackExchange adapter는 여러 답의 score를 pair로 바꾸고, SHP(Stanford Human Preferences)는 score ratio가 충분히 큰 pair만 남기는 필터를 둔다([`L46–L114`](https://github.com/eric-mitchell/direct-preference-optimization/blob/f8b8c0f49dc92a430bae41585f9d467d3618fe2f/preference_datasets.py#L46-L114)).

이 구조의 `pairs`는 사람이 각각 독립적으로 두 답을 비교한 수와 다를 수 있다. K개 ranked answer 하나에서 `K(K-1)/2`개의 pair를 만들면 pair 수는 늘지만 같은 ranking에서 파생된 label은 서로 상관되어 있다.

```text
K=2 → 1 pair
K=4 → 6 pairs
K=9 → 36 pairs
```

학습 row 수·unique prompt 수·unique user 수·independent label judgment 수·token 수를 전부 “선호 데이터 개수”로 부르면 data quality와 비용을 잘못 판단한다.

### 2. prompt token은 왜 loss에서 가리나

DPO는 “prompt 자체를 얼마나 잘 이어 쓰는가”보다, **같은 prompt 뒤에 chosen response를 rejected보다 선호하도록** 학습하려 한다. 그래서 공식 code는 다음 순서로 batch를 만든다.

```text
prompt token ids + chosen response token ids + EOS
prompt token ids + rejected response token ids + EOS
각 sequence의 label에서 prompt 위치는 -100
response 위치만 실제 token id
```

`-100`은 PyTorch loss에서 무시할 label 값이다. [`tokenize_batch_element()` L214–L271](https://github.com/eric-mitchell/direct-preference-optimization/blob/f8b8c0f49dc92a430bae41585f9d467d3618fe2f/preference_datasets.py#L214-L271)는 prompt와 answer를 따로 tokenize하고 EOS(End Of Sequence, 문장 끝) token을 붙인 뒤, prompt 길이만큼 chosen/rejected `labels`를 `-100`으로 채운다.

```text
input_ids: ["장애", "공지", "요약", "해", "줘", "원인", "은", ...]
labels:    [ -100,  -100,   -100,  -100, -100, "원인", "은", ...]
```

이 예의 prompt token은 model input에는 들어가므로 답의 context가 된다. 하지만 DPO completion log-probability 합에는 들어가지 않는다. prompt mask를 놓치면 “chosen/rejected 답의 품질” 대신 prompt text의 확률 차이가 loss에 섞일 수 있다.

### 3. token 확률은 어떻게 completion 하나의 숫자가 되나

model은 각 위치마다 vocabulary 전체의 logits(정규화 전 점수)를 낸다. 구현의 [`_get_batch_logps()` L90–L115](https://github.com/eric-mitchell/direct-preference-optimization/blob/f8b8c0f49dc92a430bae41585f9d467d3618fe2f/trainers.py#L90-L115)는 다음을 한다.

```text
logits를 한 token 앞으로 shift
labels를 한 token 뒤로 shift
label=-100 위치는 mask
각 실제 response token의 log_softmax 확률을 gather
mask된 token은 제외하고 합계
```

즉 `log π(y|x)`는 response-only token log probability의 **sum**이다. 현재 reference implementation은 `average_log_prob=False`로 호출한다([`L210–L220`](https://github.com/eric-mitchell/direct-preference-optimization/blob/f8b8c0f49dc92a430bae41585f9d467d3618fe2f/trainers.py#L210-L220)). 길이가 긴 답은 보통 더 많은 음의 log probability를 더한다. 따라서 pair의 길이 분포와 truncation(잘라냄) 정책은 모델이 장황·단문 중 무엇을 배우는지에 영향을 줄 수 있다. “DPO는 length bias가 없다”는 보장은 원 논문에 없다.

### 4. policy와 reference를 네 번이 아니라 두 묶음으로 계산한다

한 pair에는 네 log probability가 필요하다.

```text
policy chosen      log π_θ(y_w | x)
policy rejected    log π_θ(y_l | x)
reference chosen   log π_ref(y_w | x)
reference rejected log π_ref(y_l | x)
```

naïve하게는 model마다 chosen/rejected를 따로 forward해서 네 번 호출할 수 있다. 공식 구현은 chosen batch와 rejected batch를 sequence length에 맞게 pad하고 batch dimension으로 이어 붙인다([`concatenated_inputs()` L118–L142](https://github.com/eric-mitchell/direct-preference-optimization/blob/f8b8c0f49dc92a430bae41585f9d467d3618fe2f/trainers.py#L118-L142)). 그래서 policy forward 한 번, reference forward 한 번으로 chosen/rejected를 동시에 계산한다. `concatenated_forward()`의 주석도 FSDP(Fully Sharded Data Parallel, 완전 샤딩 데이터 병렬)에서 두 forward를 줄여 더 빠르게 하려는 목적이라고 명시한다.

```text
chosen + rejected를 하나의 큰 batch로 pack
  → policy forward: gradients 필요
  → reference forward: torch.no_grad(), gradients 없음
  → four log-probability 벡터
  → DPO loss
  → policy만 backward / optimizer step
```

[`get_batch_metrics()` L223–L270](https://github.com/eric-mitchell/direct-preference-optimization/blob/f8b8c0f49dc92a430bae41585f9d467d3618fe2f/trainers.py#L223-L270)에서 reference forward는 `torch.no_grad()` 안에 있다. reference가 training 중 바뀌면 “reference와 비교한 상대 변화”라는 loss의 기준선도 움직인다. 원 DPO의 `π_ref`는 frozen이어야 한다.

### 5. 공개 코드의 `preference_loss()`를 논문 Eq. 7과 연결하기

공식 구현의 [`preference_loss()` L45–L87](https://github.com/eric-mitchell/direct-preference-optimization/blob/f8b8c0f49dc92a430bae41585f9d467d3618fe2f/trainers.py#L45-L87)는 핵심을 네 줄로 압축한다.

```text
pi_logratios  = policy_chosen_logps - policy_rejected_logps
ref_logratios = reference_chosen_logps - reference_rejected_logps
logits        = pi_logratios - ref_logratios
loss          = -logsigmoid(beta * logits)
```

이는 Eq. 7의 괄호를 그대로 계산한 것이다. 같은 함수는 diagnostic(진단 지표)용으로 다음 값도 낸다.

$$
\hat r_\theta(x,y)=\beta(\log\pi_\theta(y\mid x)-\log\pi_{ref}(y\mid x))
$$

`chosen_rewards`, `rejected_rewards`라는 변수명 때문에 실제 인간이 준 절대 reward가 있다고 오해하기 쉽다. 아니다. 이 값은 policy/reference 차이에서 계산한 **implicit reward**다. implementation의 `reward_accuracies = chosen_rewards > rejected_rewards`도 “이 batch에서 암묵 보상이 chosen을 rejected보다 높게 뒀는가”를 보는 metric이지, fact accuracy(사실 정확도)나 production 안전성 지표가 아니다.

### 6. β는 learning rate가 아니라 preference-vs-reference의 온도 손잡이다

논문 Eq. 3에서 β는 KL penalty coefficient다. Eq. 7에서는 pair margin 앞에 곱해진 temperature(온도)처럼 보인다. 같은 β가 reward-policy mapping의 scale을 연결한다.

```text
β가 너무 작음
  → reference difference가 loss gradient에 약하게 반영될 수 있음
  → chosen/rejected pair를 더 공격적으로 맞추는 방향으로 흐를 수 있음

β가 너무 큼
  → reference에서 멀어짐의 대가가 큼
  → behavior 변화가 작거나 loss가 빠르게 포화될 수 있음
```

공식 repository는 original DPO run의 시작값으로 `0.1–0.5` 범위를 안내하지만([README 고정 commit](https://github.com/eric-mitchell/direct-preference-optimization/blob/f8b8c0f49dc92a430bae41585f9d467d3618fe2f/README.md)), 이것은 현재 data·model·sequence length에 그대로 복사할 보편 상수가 아니다. 원 논문 Appendix B는 기본적으로 β=`0.1`, batch size `64`, RMSprop learning rate `1e-6`, 150 step warmup을 썼고 TL;DR에는 β=`0.5`를 썼다. 이 수치는 **그 실험 recipe**다.

학습률은 optimizer가 policy parameter를 한 step에 얼마나 바꾸는가다. β는 “그 변화가 reference와 비교해 chosen/rejected margin을 어떻게 해석하는가”의 objective parameter다. 둘을 하나로 튜닝하면 실패 원인을 읽기 어렵다.

### 7. training loop와 evaluation loop를 섞지 말 것

```text
training
  static pair batch → policy/reference forward → DPO loss → policy update

offline validation
  heldout pair → DPO loss / implicit reward accuracy / length·KL-related diagnostics

release evaluation
  blind human preference + task success + factuality + safety + latency + cost
```

training loss가 낮아도 release 품질이 좋다는 보장은 없다. pair의 labeler·prompt distribution에 잘 맞았을 뿐일 수 있다. DPO 논문도 real-world task에서 GPT-4 judge와 human study를 별도로 두었다. 이 분리를 지키지 않으면 “학습 loss 감소”를 “고객 성공”이라고 오독한다.

## 직접 검증과 재현: 수식·공개 코드·실행 경계를 분리한다

대규모 preference training에는 model weights, GPU, 사람 label, dataset license가 필요하다. 개인 환경에서 논문의 모든 experiment를 실행했다고 말하면 거짓이다. 대신 여기서는 (1) 원 논문 loss의 작은 수, (2) 고정 official source의 실제 경로, (3) source syntax compilation을 분리해 확인했다.

### 1. 논문 Eq. 7을 작은 숫자로 직접 검산

실행 환경은 macOS `26.4.1`, Python `3.9.6`이다. 아래 script는 논문 Appendix B loss와 official `preference_loss()`의 원 DPO 부분(`label_smoothing=0`, IPO 비활성)을 표준 라이브러리 산술로 다시 쓴 교육용 verifier다. 실제 language model forward, 사람 preference dataset, checkpoint, GPU training은 실행하지 않는다. script SHA-256은 `3698953469d3674e942d0a1b399a4e141da5844fa4747195c5153564a24ee583`이다.

```python
from math import comb, exp, log1p

def logsigmoid(value):
    return -log1p(exp(-value))

def dpo_loss(policy_chosen, policy_rejected,
             reference_chosen, reference_rejected, beta):
    pi_pair = policy_chosen - policy_rejected
    ref_pair = reference_chosen - reference_rejected
    margin = pi_pair - ref_pair
    loss = -logsigmoid(beta * margin)
    chosen_reward = beta * (policy_chosen - reference_chosen)
    rejected_reward = beta * (policy_rejected - reference_rejected)
    return loss, margin, chosen_reward, rejected_reward

for beta in (0.1, 0.5):
    print(beta, dpo_loss(-2.0, -2.8, -2.4, -2.7, beta))
print('K=9 pairs:', comb(9, 2))
```

```bash
python3 /tmp/dpo-research/verify_dpo_mechanics.py
```

실제 output은 다음이다.

```text
beta=0.1
  policy pair log-ratio: 0.800000
  reference pair log-ratio: 0.300000
  DPO preference logit before beta: 0.500000
  DPO loss: 0.668460
  chosen/rejected implicit rewards: 0.040000 -0.010000
  implicit reward margin: 0.050000
beta=0.5
  DPO loss: 0.575939
  implicit reward margin: 0.250000
pair loss unchanged after equal +3 policy-logprob shift: 0.668460 0.668460
completion-only log-probability sum: -0.600000
K=2 ranked answers -> derived pairs: 1
K=4 ranked answers -> derived pairs: 6
K=9 ranked answers -> derived pairs: 36
```

해석은 다음과 같다.

1. policy pair 차이 `0.8`이 reference pair 차이 `0.3`보다 `0.5` 크다. policy는 reference보다 chosen을 상대적으로 더 밀고 있다.
2. β=`0.1`에서 암묵 reward margin은 `0.05`, β=`0.5`에서는 `0.25`다. β가 implicit reward scale에도 직접 들어감을 확인한다.
3. 두 policy log probability에 같은 `+3`을 더하면 pair 차이가 변하지 않아 loss도 같다. 실제 확률분포에서 임의로 둘 다 더하는 것은 유효한 update가 아닐 수 있지만, DPO loss가 절대 score가 아니라 pairwise relative margin을 쓴다는 산술 확인이다.
4. response token log probability `-0.2 + -0.1 + -0.3 = -0.6`이다. implementation이 response token을 sum한다는 설명과 연결된다.

### 2. 공식 source의 고정 commit과 직접 실행한 범위

**직접 확인:** official repository를 commit `f8b8c0f49dc92a430bae41585f9d467d3618fe2f`으로 clone했다. `README.md`는 DPO pipeline을 SFT 후 preference learning으로 설명하고, Pythia-2.8B/Anthropic-HH 예제에 4×80GB A100 기준 SFT 약 1시간 30분, DPO 약 2시간 45분을 적는다. 이 시간은 저자 repository의 특정 hardware·config 예시이지 일반적인 DPO 비용이 아니다.

```bash
git -C /tmp/dpo-research/direct-preference-optimization rev-parse HEAD
# f8b8c0f49dc92a430bae41585f9d467d3618fe2f

python3 -m compileall -q /tmp/dpo-research/direct-preference-optimization
# exit=0
```

`compileall` 성공은 공개 Python source의 syntax compilation을 확인한 것이다. torch `2.0.1`, transformers `4.29.2`, model weight, dataset, GPU가 없는 이 환경에서 full DPO training을 실행했다는 뜻은 아니다. 이 구분이 재현 보고의 최소 정직성이다.

### 3. 내 환경에서 full training을 시도하지 않은 이유와 재현 순서

원 repository requirements는 torch·transformers·datasets·wandb·FSDP 환경을 요구한다. 예제도 4×80GB A100을 전제한다. dependency가 없다고 `pip install`만 한 뒤 memory 부족/임의 checkpoint/다른 tokenizer 상태로 실패한 실행을 “논문 재현”이라고 기록하면 안 된다.

전체 재현을 하려면 적어도 아래 artifact를 고정한다.

```text
paper version / source commit
base + SFT checkpoint SHA
tokenizer and chat template
preference dataset snapshot, consent/license, split manifest
chosen/rejected construction and truncation rules
reference checkpoint SHA and frozen verification
β, optimizer, LR schedule, global batch, precision, seed
evaluator prompt/version/order randomization and human rubric
hardware, distributed strategy, generated candidate decoding config
```

그 뒤 SFT checkpoint 재현 → heldout pair loss/implicit margin 확인 → blind output 평가 → safety·factuality·cost gate 순으로 간다. 첫 run에서 DPO loss만 보고 β sweep을 시작하는 것은 data leak, template mismatch, response-length shift 같은 더 큰 문제를 가릴 수 있다.

## 성능, 복잡도와 트레이드오프: “PPO를 안 쓴다”와 “공짜다”는 다르다

### 1. 논문 결과는 task·evaluator·temperature와 함께 읽는다

원 논문은 세 open-ended generation task를 평가했다.

| task | model/data 조건 | 평가 | 논문 관측 | 읽을 때의 경계 |
|---|---|---|---|---|
| IMDb sentiment | GPT-2-large, sentiment classifier가 만든 pair | true reward vs KL frontier | DPO frontier가 PPO·PPO-GT보다 높음 | synthetic preference/known classifier reward의 controlled setting |
| Reddit TL;DR | GPT-J SFT, Stiennon et al. human preferences | GPT-4 win rate vs reference summary | DPO 약 61%(temp 0), PPO 최선 약 57%(temp 0) | GPT-4 judge·이 dataset·sampling 조건의 비교 |
| Anthropic-HH single-turn dialogue | Pythia-2.8B, preferred completion으로 reference 준비 | GPT-4 win rate vs test chosen | DPO가 chosen보다 개선한 유일한 compute-efficient method로 보고 | label distribution·proxy evaluator에 한정 |
| CNN/DailyMail shift | TL;DR model을 news 기사에 적용 | GPT-4 win rate vs ground-truth summary | DPO `0.36/0.31`, PPO `0.26/0.23` (temp 0/0.25) | 하나의 OOD(Out Of Distribution, 학습분포 밖) 실험 |

**논문 확인:** §6.2는 TL;DR에서 DPO sample(temp `0.25`)이 PPO sample(temp `0`)보다 human head-to-head에서 `58%` 선호됐다고 보고한다. §6.4의 human study는 25명의 Stanford 학생·최근 졸업자·방문자, 주로 STEM 배경 volunteer였다. 따라서 이 숫자를 “전 세계 사용자의 58%가 DPO를 선호한다”거나 “DPO가 모든 model에서 PPO보다 4%p 좋다”로 말하면 안 된다.

논문 Figure 2·3와 Table 1은 DPO가 해당 설정에서 경쟁력 있다는 **증거**다. 각 제품의 한국어/영어 비율, 도메인 knowledge, tool use, 최신성, 안전 정책, model scale, evaluation rubric이 다르면 새로 측정해야 한다.

### 2. DPO가 줄이는 training 비용과 남는 비용

기존 PPO-RLHF를 단순화하면 다음처럼 비교할 수 있다.

```text
PPO-RLHF 한 update
  policy가 새 completion 생성
  + RM forward
  + reference log-probability
  + value estimate/advantage
  + PPO backward

DPO 한 update
  고정 chosen/rejected pair
  + policy log-probability forward/backward
  + frozen reference log-probability forward
  + DPO backward
```

DPO는 gradient update 중 online generation, explicit RM training/inference, value function이 없다는 점에서 pipeline과 instability surface(불안정해질 수 있는 표면)를 줄인다. 그러나 DPO도 policy와 reference 두 model의 sequence forward를 필요로 한다. reference를 GPU에 함께 올리면 memory가 크고, pair마다 chosen/rejected sequence를 계산한다. precompute reference log probability를 선택할 수 있는 시스템은 storage·tokenizer/template immutability·data lineage라는 다른 비용을 낸다.

설명용 복잡도는 다음처럼 쓸 수 있다.

```text
N = preference pair 수
L = 평균 completion token 수
P = model 한 token forward/backward 비용

DPO update: 대략 O(N × L × (policy forward/backward + reference forward))
PPO update: rollout 길이와 RM/value/policy pass가 추가되고,
            policy가 생성하는 sample distribution도 계속 바뀜
```

이는 Big-O 수준의 단순화다. 실제 latency/throughput은 model parameter 수, context length, padding ratio, attention kernel, mixed precision, distributed communication, batch packing, checkpoint I/O에 더 크게 좌우될 수 있다. DPO가 PPO보다 “2배 빠르다” 같은 보편 숫자를 이 글이나 원 논문은 제시하지 않는다.

### 3. Best-of-N은 강하지만 online 비용을 N배 만든다

논문은 Best of N baseline도 비교했다. SFT/reference에서 N개 답을 sample하고 RM이 가장 높은 답을 고르는 방법이다.

```text
사용자 한 요청
  → 답 128개 생성
  → RM 128회 점수
  → 최고 하나 반환
```

quality가 높을 수 있지만 interactive service에서 latency·GPU·RM 비용이 `N`에 따라 급증한다. DPO는 training 때 preference를 policy에 흡수해 inference에서는 한 답만 생성하는 선택지가 된다. 다만 DPO가 Best-of-N이 찾을 수 있었던 다양한 candidate를 항상 한 번에 압축한다는 보장은 없다. task, decoding temperature, pair coverage를 직접 비교해야 한다.

### 4. metric은 loss 하나가 아니라 다면적 gate다

release candidate마다 최소한 다음을 나누어 본다.

| 계층 | 관찰 지표 | 왜 필요한가 |
|---|---|---|
| data | pair 중복, chosen/rejected 길이, 언어·task·위험 slice | 학습 신호의 편향·누출 확인 |
| training | DPO loss, chosen/rejected implicit reward margin, gradient norm, response length | loss 폭주·포화·length drift 확인 |
| model behavior | blind preference, task success, factuality, citation faithfulness(충실도/근거성), refusal correctness | RM/DPO loss와 제품 품질의 간극 확인 |
| system | p50/p95 latency, token cost, error rate, tool-call validation failure | 좋은 답이 운영 가능한지 확인 |
| safety | private-data leakage, harmful request slice, policy bypass, worst-group regression | 평균 score가 숨기는 꼬리 위험 확인 |

`implicit reward accuracy`가 95%여도 heldout human preference가 낮다면, model이 training pair를 잘 외웠거나 data distribution이 달라진 것이다. `DPO loss↓`와 `customer success↑` 사이에는 아직 사람이 정의한 evaluation이 필요하다.

## 실패, 한계, 장애와 운영: DPO가 배우는 것은 label의 방향이지 현실의 진실이 아니다

### 1. offline preference data의 빈 곳은 DPO가 모른다

DPO는 고정 pair에서 학습한다. training set에 없는 prompt family, language, adversarial jailbreak, private data 요청, 새 업무 규정은 직접 보지 못한다. reference가 SFT data 분포에서 멀리 벗어나지 않게 하는 역할을 해도, 새 분포의 correct behavior를 만들어 주지는 않는다.

논문 §7도 OOD generalization을 더 넓게 연구해야 하고, self-labeling이 unlabeled prompt를 잘 활용할 수 있는지, direct preference optimization에서 reward over-optimization이 어떻게 나타나는지 미해결 질문으로 남긴다. 이 한계는 논문 저자도 명시했다.

```text
학습 pair: "안전한 SQL migration을 설명해 줘"
실제 요청: "운영 DB의 모든 테이블을 지금 지워 줘"

DPO가 정중한 거절을 말할 가능성 ≠ 시스템이 destructive SQL을 실행하지 않음
```

실제 실행 권한은 LLM text가 아닌 backend policy에서 막아야 한다.

```text
LLM: 의도 분류·안전한 설명·승인 요청 문구
API gateway: authentication(인증)·rate limit
backend: authorization·환경 분리·parameterized query
workflow: human confirmation·dry run·backup proof
database: 최소 권한 role·transaction·audit log
```

### 2. “chosen”이 완전한 정답이라는 보장이 없다

chosen response는 labeler가 rejected보다 낫다고 골랐다는 뜻이다. 사실성, 법적 적합성, 개인정보 안전성, 브랜드 말투, 모든 사용자 집단의 선호를 모두 만족한다는 뜻이 아니다.

원 논문의 dialogue example에도 DPO response가 길고 그럴듯하지만 역사적으로 틀린 답을 낸 사례가 나온다(Appendix Table 9). evaluator가 ground-truth response를 옳다고 고른 것처럼 보이지만, 실제로는 DPO 답이 틀렸다는 점을 보여 준다. 이 예는 “DPO가 사용자 선호를 최적화했으니 factuality도 자동으로 최적화됐다”라는 결론이 틀렸음을 직접 보여 준다.

factual task라면 preference pair 외에 다음을 만든다.

```text
질문 + 허용된 source snapshot + 정답/근거 span
질문 + false premise + 올바른 correction
질문 + 정보 부족 + abstain(모른다고 말함)/retrieve/handoff 기대 행동
질문 + 권한 밖 source + 거절/권한 요청 기대 행동
```

그리고 release 평가에서 citation이 실제 retrieval source를 지지하는지, source timestamp가 최신인지, model이 모르는 것을 꾸며 내지 않는지를 별도로 측정한다.

### 3. reference mismatch와 template mismatch는 조용히 model을 망친다

reference는 “어떤 model weight”만 뜻하지 않는다.

```text
reference = checkpoint
          + tokenizer version
          + BOS/EOS/pad 처리
          + system/developer/user chat template
          + prompt normalization
          + max prompt/response length와 truncation
```

policy logprob는 최신 template로, reference logprob는 예전 template로 계산하면 `π_θ/π_ref` 비교가 같은 문장을 비교하지 않는다. chosen/rejected 중 하나만 잘리거나 pad mask가 다르면 pair margin도 왜곡된다. 이 문제는 loss가 NaN이 되지 않아도 발생한다.

디버깅 순서는 다음이 안전하다.

1. 하나의 prompt/chosen/rejected row를 token ID, decoded text, prompt mask까지 출력한다.
2. policy/reference가 **동일한** input ID와 attention mask를 받는지 hash로 확인한다.
3. completion-only token 수와 chosen/rejected truncation rate를 slice별로 본다.
4. reference parameter의 `requires_grad=false`와 optimizer parameter 목록을 확인한다.
5. DPO margin, response length, repetition, refusal rate, heldout human preference를 같은 run ID로 기록한다.
6. 나빠진 candidate는 immutable SFT/reference checkpoint로 rollback하고 pair/data revision부터 다시 확인한다.

### 4. β·length·label noise는 서로 다른 실패 원인이다

| 관측 | 가능한 원인 | 먼저 확인할 것 |
|---|---|---|
| loss는 낮지만 답이 반복됨 | pair data/length bias, 과최적화, decoding | chosen/rejected 길이·repetition slice, blind eval |
| chosen reward margin 급상승인데 사람 선호 하락 | implicit reward exploitation | heldout pair와 새 human rater, distribution overlap |
| policy가 SFT와 거의 같음 | β/learning rate/step가 너무 보수적, label 약함 | policy-reference margin, ablation, data agreement |
| 특정 언어에서 품질 급락 | English-heavy pair/template/tokenization | language slice, tokenizer/template, label coverage |
| safe refusal이 지나치게 많음 | harmless label이 helpfulness를 압도 | benign request refusal set, rubric version |
| factually 틀린 confident 답 | pair가 style 선호만 반영, RAG 근거 없음 | source-backed factual eval, retrieval/ACL path |

이 표는 논문의 공식 incident runbook이 아니라, DPO의 pair/reference/logprob 구조에서 도출한 **운영 해석**이다. 임계값은 domain risk와 human review capacity에 맞게 정해야 한다.

### 5. 개인정보·선호 data는 reward보다 먼저 보호한다

preference row에는 customer prompt, 민감한 답, labeler annotation, 업무 절차가 들어갈 수 있다. raw text를 model training bucket과 analytics dashboard에 광범위하게 복제하면 DPO 품질보다 큰 위험을 만든다.

최소 data lineage는 다음을 남긴다.

| 필드 | 목적 | 보호 원칙 |
|---|---|---|
| `pair_id`, source/consent/license version | data의 합법적·계약상 출처 확인 | 원문 접근과 metadata 접근 분리 |
| pseudonymous user/template/group ID, split | leakage·distribution 측정 | 실제 identity와 tokenized ID 분리 |
| chosen/rejected, rubric/labeler-pool version | 무엇을 선호로 배웠는지 재현 | raw response 최소 권한·redaction |
| tokenizer/template/truncation version | logprob 비교 재현 | model artifact와 immutable 연결 |
| policy/reference/run/eval version | rollback·incident 분석 | overwrite하지 않는 run registry |

동의 철회, data retention(보존), training data deletion은 관할 법·계약·model retrain 정책에 따라 다르다. DPO 논문은 그 법적 답을 제공하지 않는다. 다만 lineage가 없으면 나중에 “어느 data가 어느 checkpoint에 들어갔는가”라는 기술 질문 자체에 답할 수 없다는 것은 분명하다.

## 대안, 비교와 선택 기준: DPO를 붙이기 전에 문제의 층위를 먼저 고른다

| 실제 문제 | 먼저 쓸 도구 | DPO가 추가로 맞는 조건 | DPO가 과한 경우 |
|---|---|---|---|
| 최신 문서/DB 사실을 모름 | RAG, source version, retrieval eval, ACL | 근거를 인용·설명하는 행동 preference도 학습해야 함 | 검색 index·권한 제어조차 없음 |
| JSON/schema를 자주 깸 | constrained decoding, schema validator, retry | 형식 밖의 사용자 선호도 지속적으로 학습할 때 | validator 한 개로 해결 가능 |
| 권한 밖 tool/SQL 실행 | backend authorization, approval, sandbox | 안전한 질문·설명·handoff 말투를 개선할 때 | LLM loss에 권한 판정을 맡김 |
| 도메인 형식/용어 부족 | high-quality SFT | SFT 후에도 두 좋은 답 사이의 일관된 preference가 남음 | pair rubric/heldout eval 없음 |
| A/B 중 어떤 응답이 더 유용한지 | DPO 또는 PPO-RLHF | offline pair가 충분하고 reference·evaluation이 고정됨 | new online exploration/RM iterative refresh가 필수 |
| 후보를 많이 내면 고를 수 있음 | Best-of-N + RM/reranker | serving latency를 training에 선불로 옮기고 싶음 | high-N inference 비용을 감당 가능 |

### DPO와 PPO-RLHF의 선택 기준

**DPO가 먼저인 경우**

- 잘 정의된 offline preference pair가 있고, chosen/rejected가 같은 prompt에 대응한다.
- SFT/reference checkpoint와 tokenizer/template를 고정할 수 있다.
- RM+PPO보다 단순한 supervised-style loop로 빠르게 baseline을 검증하고 싶다.
- online rollout 없이도 현재 data distribution에서 품질을 개선할 가설이 있다.

**PPO/RM 또는 더 넓은 loop가 필요한 경우**

- 새 policy가 내는 output에서 추가 preference를 계속 수집·갱신해야 한다.
- sequence-level reward, multi-step environment feedback, tool outcome처럼 pair만으로 표현하기 어려운 signal이 핵심이다.
- explicit RM을 audit/analysis 대상으로 독립적으로 유지할 운영 이유가 있다.

둘 다 못 푸는 경우도 많다. RAG freshness, policy enforcement, data access control, database transaction correctness는 preference optimization 대상이 아니라 시스템 설계 대상이다.

### DPO, IPO, conservative DPO를 같은 이름으로 섞지 말 것

official repository의 현재 고정 commit은 original DPO 외에도 IPO와 label smoothing 기반 conservative DPO를 지원한다. `label_smoothing=0`이 original DPO임을 source comment가 명시한다([`trainers.py` L78–L85](https://github.com/eric-mitchell/direct-preference-optimization/blob/f8b8c0f49dc92a430bae41585f9d467d3618fe2f/trainers.py#L78-L85)). label noise가 있을 수 있다는 문제는 현실적이지만, 논문 Eq. 7의 결과를 conservative DPO 결과처럼 말하거나 반대로 하면 비교가 무의미해진다.

처음에는 original DPO로 **dataset·reference·evaluator를 고정**하고, 그 baseline의 실패가 preference noise인지, distribution shift인지, length/template bug인지 확인한 뒤 variant를 비교한다. 알고리즘 이름을 늘리기 전에 실패 원인을 분리하는 편이 더 효율적이다.

## 흔한 오해와 질문의 답

### Q1. DPO는 reward model을 완전히 없앴으니 reward와 무관한가?

아니다. 별도 neural scalar RM을 학습하지 않을 뿐, `β log(π_θ/π_ref)`가 implicit reward로 작동한다. 논문의 제목 “language model is secretly a reward model”은 policy/reference ratio로 reward equivalence class를 표현할 수 있다는 뜻이다. 사람 선호 signal 자체는 여전히 필요하다.

### Q2. DPO는 chosen 확률을 높이고 rejected 확률을 낮추는 SFT 아닌가?

아니다. DPO는 policy의 chosen-vs-rejected log-ratio에서 reference의 같은 ratio를 뺀 margin을 `logsigmoid`로 최적화한다. 이 margin과 β가 없으면 original DPO Eq. 7이 아니다. 논문 Appendix C.3의 naive unlikelihood failure도 단순한 확률 밀기가 왜 충분하지 않을 수 있는지 보여 준다.

### Q3. DPO는 RL이 없으니 training 중 reference model도 필요 없나?

원 DPO에는 필요하다. reference log probability가 loss에 들어간다. reference를 제거하는 `reference_free` 같은 code option은 official repo의 별도 옵션이며, original DPO의 KL-reference 의미를 그대로 보장하지 않는다. reference를 policy와 같은 model로 매 step 갱신하는 것도 original frozen-reference 설정과 다르다.

### Q4. β가 크면 DPO가 더 세게 학습되는가?

그렇게 한 단어로 말하면 위험하다. β는 objective의 reference/KL trade-off와 implicit reward scale에 들어간다. gradient 크기는 β, 현재 margin, optimizer learning rate, batch/data 분포에 함께 좌우된다. β를 learning rate처럼 독립적으로 이해하면 안 된다. 같은 model/data에서 β sweep과 blind evaluation을 함께 해야 한다.

### Q5. preference win rate가 높으면 factuality와 safety도 높나?

아니다. 논문 자체도 DPO가 장황하고 사실과 다른 답을 낸 example을 보인다. pair rubric이 실제로 평가한 항목만 개선될 수 있다. factuality는 source-backed evaluation, 최신성은 retrieval, harmful action 방지는 authorization/approval으로 별도 보장한다.

### Q6. 백엔드/DBA가 DPO를 공부하는 가장 좋은 방법은 무엇인가?

작은 problem을 하나 고른다. 예: “운영 장애 공지 요약에서 원인·영향·복구 시점을 빠뜨리지 않는다.” 그다음 (1) same prompt의 chosen/rejected pair와 rubric을 만든다, (2) template/tokenizer/reference를 고정한다, (3) DPO loss와 pair margin을 기록한다, (4) blind human 평가와 schema/fact validator를 붙인다, (5) model이 제안할 뿐 실제 변경은 승인된 backend workflow만 실행하게 만든다. GPU 규모보다 **data contract·evaluation·권한 경계**를 닫는 능력이 먼저다.

### Q7. 이 논문 다음에 어떤 관점으로 DPO 후속 연구를 읽어야 하나?

먼저 이 세 질문을 답할 수 있어야 한다.

```text
1. 왜 Z(x)를 계산하지 않아도 DPO loss가 되는가?
2. chosen/rejected의 절대 logprob가 아니라 reference-relative pair margin은 왜 필요한가?
3. DPO loss가 좋아도 왜 새 prompt의 사실성·안전성·권한은 별도 평가해야 하는가?
```

그 뒤 IPO, noisy preference를 다루는 conservative DPO, RLAIF(Reinforcement Learning from AI Feedback, AI 피드백 강화학습), online preference optimization을 읽으면 각 방법이 무엇을 바꾸는지 비교할 수 있다.

### 이 논문을 한 문장으로 다시 답하면

> **DPO는 `A가 B보다 낫다`는 offline preference pair와 frozen SFT reference를 사용해, policy가 chosen을 rejected보다 reference-relative하게 더 높게 둘 확률을 이진 cross-entropy로 직접 학습한다. 이는 RM+PPO loop를 단순화하지만, 사람이 무엇을 선호했는지의 편향·새 도메인·사실성·retrieval·실행 권한을 해결하는 마법은 아니다.**

## 출처

### 1차 자료: 원 논문

1. Rafael Rafailov et al., [*Direct Preference Optimization: Your Language Model is Secretly a Reward Model*, arXiv:2305.18290v3 / NeurIPS 2023](https://arxiv.org/abs/2305.18290). 이 글의 Eq. 1–10, reward-policy reparameterization, DPO objective, task·evaluation·실험 수치·한계는 §1–§7과 Appendix A–D에서 직접 확인했다.
2. 논문 [PDF v3](https://arxiv.org/pdf/2305.18290). Appendix B의 original DPO pseudocode, β/batch/optimizer recipe, Appendix C/D의 GPT-4 judge·human study·failure examples를 확인했다.

### 1차 자료: 공식 구현

3. Rafailov et al., [official `direct-preference-optimization` repository, fixed commit `f8b8c0f`](https://github.com/eric-mitchell/direct-preference-optimization/tree/f8b8c0f49dc92a430bae41585f9d467d3618fe2f). README의 SFT→DPO pipeline, model/data contract, hardware example을 확인했다.
4. [`trainers.py` L45–L87](https://github.com/eric-mitchell/direct-preference-optimization/blob/f8b8c0f49dc92a430bae41585f9d467d3618fe2f/trainers.py#L45-L87): original DPO loss, policy/reference log-ratio, implicit reward 구현.
5. [`trainers.py` L90–L270](https://github.com/eric-mitchell/direct-preference-optimization/blob/f8b8c0f49dc92a430bae41585f9d467d3618fe2f/trainers.py#L90-L270): response-only token log probability, concatenated forward, frozen reference, metric 경로.
6. [`preference_datasets.py` L120–L271](https://github.com/eric-mitchell/direct-preference-optimization/blob/f8b8c0f49dc92a430bae41585f9d467d3618fe2f/preference_datasets.py#L120-L271): chosen/rejected pair 구성과 prompt mask/tokenization 경로.

### 직접 실행 기록과 해석의 경계

- **직접 실행:** macOS `26.4.1`, Python `3.9.6`에서 arXiv PDF v3와 official repo fixed commit을 읽었다. `/tmp/dpo-research/verify_dpo_mechanics.py`로 Eq. 7의 pair margin·β·implicit reward·K choose 2·completion logprob 합을 산술 검산했다. `python3 -m compileall -q`로 공개 repository source syntax compilation exit code `0`을 확인했다.
- **논문 확인:** DPO의 목적식·수식·실험 task·수치·GPT-4/human study 조건·한계는 위 원 논문 범위에서만 주장한다.
- **해석:** DBA data lineage, release gate, authorization/RAG 분리, failure triage, 대안 선택 기준은 논문의 수식·data·평가 구조를 production에 적용하기 위한 저자의 해석이다. 원 논문이 현행 안전 정책, 조직의 법적 요구, 실제 database 권한을 보증한다는 뜻은 아니다.
