Part I: DPO 이해와 해부
Chapter 1: DPO는 무엇을 하는가 — SFT에서 한 걸음씩
이미 알고 있는 것에서 출발하자
DPO를 이해하는 가장 빠른 길은 SFT(Supervised Finetuning)에서 출발하는 것이다. SFT를 알고 있다면, RL까지는 세 걸음이면 된다.
[SFT — 당신이 이미 아는 것]
데이터: (프롬프트, 좋은 응답) 쌍의 모음
학습: 좋은 응답의 확률을 올린다
손실: −log P_θ(y|x) ← NLL 손실
이것만으로 모델은 꽤 잘 작동한다.
하지만 한계가 있다: 약 90% 신뢰도에서 정체한다.
10번 중 1번은 반복 루프에 빠지거나 엉뚱한 답을 낸다.
SFT는 "무엇이 좋은가"만 가르치고 "무엇이 나쁜가"는 가르치지 않기 때문이다.
첫 번째 걸음: 나쁜 예제를 추가한다
[SFT + 음성 예제]
데이터: (프롬프트, 좋은 응답, 나쁜 응답)
학습: 좋은 응답의 확률은 올리고, 나쁜 응답의 확률은 내린다
프롬프트: "대한민국의 수도는?"
좋은 응답: "서울입니다." → 확률 ↑
나쁜 응답: "부산입니다." → 확률 ↓
SFT와 RL의 경계는 사실 여기다.
음성 예제가 들어오는 순간, SFT는 RL이 된다.
두 번째 걸음: 원본에서 너무 멀어지지 않게 줄을 묶는다
[SFT + 음성 예제 + KL 제약]
문제: 좋은 응답을 올리고 나쁜 응답을 내리다 보면
모델이 원래 갖고 있던 능력까지 잊어버린다.
"서울"만 미친 듯이 강화하다가 다른 모든 질문에도 "서울"이라 답한다.
해결: 원본 모델(레퍼런스)과 너무 달라지면 벌점을 준다.
KL(π_θ ‖ π_ref) — "지금 모델이 원본에서 얼마나 벗어났는가"
→ 음성 예제: "나쁜 것에서 멀어져라"
→ KL 제약: "하지만 원본에서 너무 멀어지진 마라"
여기까지가 RL의 직관적 토대다. 이제 DPO의 수식을 볼 준비가 됐다.
자연스러운 오해: “좋은 건 올리고 나쁜 건 내리면 끝 아닌가?”
미묘한 오해:
"DPO는 좋은 응답의 확률을 올리고 나쁜 응답의 확률을 내리는 것이다."
이것이 아주 틀린 답은 아니지만 부족한 이유:
모델이 이미 좋은 응답에 99%, 나쁜 응답에 1%를 부여하고 있다면?
→ "좋은 건 올리고 나쁜 건 내리기"로는 할 일이 거의 없다.
→ 하지만 모델은 여전히 문제가 있을 수 있다.
반대로, 모델이 두 응답에 50%, 50%를 주고 있다면?
→ 둘 다 같은 확률인데, 원본 모델도 50:50이었다면,
그건 이미 원본 수준이지 개선이 아니다.
핵심: 절대 확률이 아니라 "원본 대비 얼마나 더 벌렸는가"가 중요하다.
DPO의 진짜 논리:
레퍼런스 대비
모델의 마진을 벌린다
모델의 마진을 벌린다
이제 수식을 보자. 한 줄이다:
L_DPO = −log σ( β × (Δ_θ − Δ_ref) )
여기서:
Δ_θ = log π_θ(y_w|x) − log π_θ(y_l|x) ← 학습 모델의 선호마진
Δ_ref = log π_ref(y_w|x) − log π_ref(y_l|x) ← 원본 모델의 선호마진
이 수식이 하는 일을 한국어로 풀면:
1. 학습 모델이 보는 선호마진 (Δ_θ):
"지금 내가 좋은 답과 나쁜 답 사이에 얼마나 차이를 두고 있는가?"
2. 원본 모델이 보는 선호마진 (Δ_ref):
"원래 모델은 이 차이를 얼마로 보고 있었는가?"
3. 둘의 차이 (Δ_θ − Δ_ref):
"원본 대비 내가 선호마진을 얼마나 더 벌렸는가?"
4. 이 차이를 σ(시그모이드)에 통과:
→ 양수면 σ > 0.5 → −log σ가 작아짐 → 손실 작음 → 잘하고 있다
→ 음수면 σ < 0.5 → −log σ가 커짐 → 손실 큼 → 더 벌려야 한다
→ 0이면 σ = 0.5 → −log(0.5) = 0.693 → 아직 할 일이 있다!
장난감 예제: 왜 "절대 확률"이 아니라 "상대 마진"인가
상황 A: 레퍼런스모델이 이미 잘 구분하는 경우
π_ref: P(서울) = 0.9, P(부산) = 0.1 → Δ_ref = log(0.9) − log(0.1) = 2.2
π_θ: P(서울) = 0.95, P(부산) = 0.05 → Δ_θ = log(0.95) − log(0.05) = 2.9
Δ_θ − Δ_ref = 2.9 − 2.2 = +0.7
→ 레퍼런스모델보다 마진을 0.7만큼 더 벌렸다. 좋다!
상황 B: 레퍼런스모델이 구분 못하는 경우
π_ref: P(서울) = 0.5, P(부산) = 0.5 → Δ_ref = 0
π_θ: P(서울) = 0.5, P(부산) = 0.5 → Δ_θ = 0
Δ_θ − Δ_ref = 0 − 0 = 0
L = −log σ(0) = −log(0.5) = 0.693
→ 손실이 0이 아니다! 그래디언트가 살아 있다.
→ "레퍼런스모델도 모르고 나도 모르지만, 나는 더 벌려야 한다."
상황 C: "절대 확률만 올리기"가 실패하는 경우
π_ref: P(서울) = 0.70, P(부산) = 0.01 → Δ_ref = log(0.70) − log(0.01) = 4.2
π_θ: P(서울) = 0.80, P(부산) = 0.05 → Δ_θ = log(0.80) − log(0.05) = 2.8
절대 확률만 보면: "서울이 70%에서 80%로 올랐으니 개선 아닌가?"
마진으로 보면: Δ_θ − Δ_ref = 2.8 − 4.2 = −1.4
→ 부산도 1%에서 5%로 따라 올랐다. 하지만 마진은 오히려 줄었다!
→ "좋은 답의 확률은 올랐지만, 나쁜 답도 덩달아 올라서 구분력은 후퇴했다."
아래 그래프가 세 상황의 마진을 시각적으로 비교한다:
직관에서 수식으로: 세 걸음의 대응
| 직관 | 수식의 대응 |
|---|---|
| 좋은 응답 확률 올리기 | log π_θ(y_w) ↑ |
| 나쁜 응답 확률 내리기 | log π_θ(y_l) ↓ |
| → 이 둘의 합: 마진 벌리기 | → Δ_θ = log π_θ(y_w) − log π_θ(y_l) |
| 원본에서 멀어지지 마라 (KL) | 레퍼런스 기준으로 상대화 |
| → "얼마나 벌렸는가"가 아니라 “원본 대비 얼마나 더” | → (Δ_θ − Δ_ref): “원본보다 얼마나 더” |
| β (온도 파라미터) | 줄의 길이 |
| → β 크면 보수적 학습 | → 큰 β = 짧은 줄 = 원본 근처 |
| → β 작으면 공격적 학습 | → 작은 β = 긴 줄 = 멀리 갈 수 있다 |
이것이 DPO의 핵심이다. 좋은 답의 절대 확률을 높이는 것이 아니라, 원본 대비 선호/비선호 사이의 상대적 마진을 벌리는 것.
다음 장으로의 질문
DPO가 마진을 벌린다는 것을 이해했다. 그렇다면 모든 선호/비선호 쌍이 똑같이 유용한가? 어떤 쌍이 학습 시그널을 만들고, 어떤 쌍이 낭비인가?