수학

MATH / 중급 57번

KL 제약 보상 최대화의 닫힌 해, 그리고 거기서 떨어져 나오는 DPO 손실

KL 벌점이 붙은 보상 최대화는 반복 없이 답이 나옵니다. 최적 정책이 참조 모델을 exp(r/β)로 기울인 볼츠만 분포임을 유도하고, 그 식을 보상에 대해 뒤집어 Bradley-Terry 손실에 넣으면 분배함수가 소거되며 DPO 손실이 남는 과정을 끝까지 따라갑니다.

PALDYN Team41 MIN READ

지난 네 편에서 우리는 RLHF 파이프라인의 부품을 하나씩 세웠습니다. 답을 뽑아 그래디언트를 얻는 항등식, 분산을 줄이는 베이스라인, 선호 쌍에서 보상 모델을 학습하는 손실, 그리고 KL 예산을 목적식 안으로 넣는 라그랑주 승수입니다. 그것을 다 조립하면 이런 그림입니다 — 보상 모델을 하나 학습하고, 정책을 하나 학습하고, 참조 모델과 가치 모델까지 메모리에 얹은 채 답을 뽑아 가며 돌립니다.

그런데 실제로 많이 쓰이는 방법 중 하나는 그 절반을 건너뜁니다.

logits = beta * (
    (policy_logps_chosen - ref_logps_chosen) - (policy_logps_rejected - ref_logps_rejected)
)
loss = -F.logsigmoid(logits).mean()

보상 모델이 없습니다. 답을 뽑지도 않습니다. 있는 것은 선호 쌍과 두 모델의 로그 확률뿐인데, 손실의 모양은 보상 모델 손실과 똑같은 로그 시그모이드입니다. 마치 보상 모델이 있어야 할 자리에 정책이 그대로 들어앉은 것 같습니다.

실제로 그렇습니다. 이 글은 그 자리바꿈이 어떻게 성립하는지를 끝까지 유도합니다. 실무에서 쓸 때의 주의점과 변형들은 선호 정렬이 다루고, 여기서는 식만 봅니다.

최적 정책의 닫힌 해

지난 글에서 적어 둔 문제로 시작합니다. 프롬프트 하나를 고정하고, 답 yy 위의 분포 π\pi 를 통째로 변수로 봅니다.

max⁡π ∑yπ(y) r(y)  −  β∑yπ(y)log⁡π(y)πref(y)s.t.∑yπ(y)=1\max_{\pi}\ \sum_y \pi(y)\,r(y) \;-\; \beta \sum_y \pi(y)\log\frac{\pi(y)}{\pi_{\text{ref}}(y)} \quad\text{s.t.}\quad \sum_y \pi(y) = 1

첫 항은 정책이 내는 답의 평균 점수이고, 둘째 항은 참조 모델에서 멀어진 만큼 내는 벌금입니다. 파라미터 θ\theta 가 아니라 분포 자체를 변수로 두는 것이 요령입니다. 신경망이 표현할 수 있는 것만 찾는 대신 모든 분포 중에서 최선을 찾으면 답이 닫힌 형태로 나옵니다.

분포 위의 라그랑지안

합이 1이라는 등식 제약이 있으니 라그랑지안을 세웁니다.

L(π,λ)=∑yπ(y)r(y)−β∑yπ(y)log⁡π(y)πref(y)+λ(1−∑yπ(y))\mathcal{L}(\pi, \lambda) = \sum_y \pi(y) r(y) - \beta\sum_y \pi(y)\log\frac{\pi(y)}{\pi_{\text{ref}}(y)} + \lambda\Big(1 - \sum_y \pi(y)\Big)

「분포로 미분한다」는 말이 낯설게 들릴 수 있지만, 여기서 하는 일은 평범한 편미분입니다. 답이 NN 개라면 분포는 NN 개의 숫자 π(y1),…,π(yN)\pi(y_1), \dots, \pi(y_N) 을 늘어놓은 벡터이고, 목적식은 답마다 한 항씩 더한 합입니다. π(y)\pi(y) 하나로 미분하면 그 답의 항만 남고 나머지 N−1N-1 개 항은 상수라 사라집니다. 언어 모델의 답은 어휘 VV 개에서 길이 TT 까지 고른 것이라 NN 이 천문학적이지만 유한하고, 그래서 이 계산은 변수 하나짜리 미분을 NN 번 되풀이한 것과 같습니다.

가운데 항은 π(y)log⁡π(y)\pi(y)\log\pi(y) 꼴이라 곱의 미분에서 두 조각이 나옵니다.

∂L∂π(y)=r(y)−β(log⁡π(y)πref(y)+1)−λ=0\frac{\partial\mathcal{L}}{\partial \pi(y)} = r(y) - \beta\left(\log\frac{\pi(y)}{\pi_{\text{ref}}(y)} + 1\right) - \lambda = 0

로그만 남기고 정리하면

log⁡π(y)πref(y)=r(y)−λβ−1⟹π(y)=πref(y) er(y)/β⋅e−λ/β−1\log\frac{\pi(y)}{\pi_{\text{ref}}(y)} = \frac{r(y) - \lambda}{\beta} - 1 \quad\Longrightarrow\quad \pi(y) = \pi_{\text{ref}}(y)\,e^{r(y)/\beta}\cdot e^{-\lambda/\beta - 1}

오른쪽 끝의 지수는 yy 와 무관한 상수입니다. 합이 1이 되도록 그 상수를 정하면 되고, 그것이 곧 정규화입니다.

 π⋆(y∣x)=1Z(x) πref(y∣x) exp⁡ ⁣(r(x,y)β),Z(x)=∑yπref(y∣x) er(x,y)/β \boxed{\ \pi^\star(y\mid x) = \frac{1}{Z(x)}\,\pi_{\text{ref}}(y\mid x)\,\exp\!\Big(\frac{r(x,y)}{\beta}\Big),\qquad Z(x) = \sum_y \pi_{\text{ref}}(y\mid x)\,e^{r(x,y)/\beta}\ }

Z(x)Z(x) 를 분배함수라고 부릅니다 — 지수를 씌운 값들의 합으로, 전체를 1로 맞춰 주는 나눗셈입니다. 그리고 이 꼴, 그러니까 「기준 분포에 지수 가중치를 곱한 것」을 볼츠만 분포라고 부릅니다.

읽는 법은 간단합니다. 참조 모델을 보상 쪽으로 기울인 것이 최적 정책입니다. 점수가 높은 답은 er/βe^{r/\beta} 배만큼 확률이 커지고 낮은 답은 작아지는데, 원래 참조 모델이 거의 안 내던 답은 πref\pi_{\text{ref}} 가 작아서 여전히 잘 안 나옵니다. 두 정보가 곱으로 섞입니다.

참조 모델을 보상 쪽으로 기울인 것이 최적 정책이다

저절로 지켜지는 양수 조건

위 문제에는 제약이 하나 빠져 있습니다. 확률은 음수가 될 수 없으니 원래는 답마다 π(y)≥0\pi(y) \ge 0 이라는 부등식 제약이 NN 개 더 붙어야 하고, 지난 글의 KKT 조건대로라면 그 하나하나에 승수를 달아야 합니다.

그런데 나온 답을 보면 그럴 필요가 없었습니다. π⋆(y)\pi^\star(y) 는 πref(y)\pi_{\text{ref}}(y) 에 지수 함수 값을 곱한 것이고, 지수 함수는 어떤 실수를 넣어도 양수입니다. 참조 모델이 조금이라도 확률을 주는 답이면 최적 정책도 반드시 양수를 줍니다. 부등식 제약이 전부 여유 있게 지켜지고 있으므로, 지난 글의 상보 여유 조건 — 제약이 남아돌면 그 가격은 0이다 — 에 따라 그 NN 개의 승수는 모두 0입니다. 걸었어도 답이 달라지지 않았을 제약입니다.

이 성질은 KL 항이 만들어 줍니다. πlog⁡π\pi\log\pi 의 미분 log⁡π+1\log\pi + 1 은 π\pi 가 0으로 다가가면 −∞-\infty 로 떨어지므로, 목적식은 확률을 0 근처로 내리는 쪽으로 무한히 가파르게 저항합니다. 반대로 참조 모델이 0을 준 답에는 최적 정책도 0을 줍니다. 그 답에 양수를 주면 KL이 무한대가 되기 때문입니다.

목적식 다시 묶기

위 계산은 기울기가 0인 점을 찾은 것이라 그것이 최대인지는 따로 봐야 합니다. 목적식을 다시 묶으면 한 줄로 끝납니다.

Eπ[r]−β KL(π∥πref)=−β Eπ ⁣[log⁡π(y)πref(y)er(y)/β]=−β Eπ ⁣[log⁡π(y)Z π⋆(y)]=−β KL(π ∥ π⋆)+βlog⁡Z\begin{aligned} \mathbb{E}_\pi[r] - \beta\,\mathrm{KL}(\pi\|\pi_{\text{ref}}) &= -\beta\,\mathbb{E}_\pi\!\left[\log\frac{\pi(y)}{\pi_{\text{ref}}(y)e^{r(y)/\beta}}\right] \\ &= -\beta\,\mathbb{E}_\pi\!\left[\log\frac{\pi(y)}{Z\,\pi^\star(y)}\right] \\ &= -\beta\,\mathrm{KL}\big(\pi\,\|\,\pi^\star\big) + \beta\log Z \end{aligned}

KL 발산은 늘 0 이상이고 두 분포가 같을 때만 0이므로, 오른쪽은 π=π⋆\pi = \pi^\star 에서 최대이고 그때의 값이 βlog⁡Z\beta\log Z 입니다. 기울기가 0인 점이 최대라는 것과 최적값이 얼마인지가 한꺼번에 나옵니다.

목적식을 다시 묶으면 최적점이 한눈에 보인다

숫자로 맞춰 보겠습니다. 답이 셋이고 πref=(0.5, 0.3, 0.2)\pi_{\text{ref}} = (0.5,\,0.3,\,0.2), r=(1, 0, −1)r = (1,\,0,\,-1) 인 장난감 문제입니다.

import numpy as np

pref = np.array([0.5, 0.3, 0.2])
r = np.array([1.0, 0.0, -1.0])

for beta in (2.0, 1.0, 0.5, 0.25):
    w = pref * np.exp(r / beta); Z = w.sum(); pi = w / Z
    kl = float((pi * np.log(pi / pref)).sum())
    print(f"β={beta:<5} π*={np.round(pi, 4)}  E[r]={pi @ r:.4f}  "
          f"KL={kl:.4f}  목적값={pi @ r - beta * kl:.4f}  β·logZ={beta * np.log(Z):.4f}")

# β=2.0   π*=[0.6618 0.2408 0.0974]  E[r]=0.5644  KL=0.0625  목적값=0.4393  β·logZ=0.4393
# β=1.0   π*=[0.7844 0.1731 0.0425]  E[r]=0.7419  KL=0.1922  목적값=0.5497  β·logZ=0.5497
# β=0.5   π*=[0.9187 0.0746 0.0067]  E[r]=0.9119  KL=0.4322  목적값=0.6958  β·logZ=0.6958
# β=0.25  π*=[0.9890 0.0109 0.0001]  E[r]=0.9889  KL=0.6375  목적값=0.8295  β·logZ=0.8295

마지막 두 열이 매 줄에서 같습니다. 값을 격자로 훑어 직접 최대를 찾아 봐도 β=0.5\beta=0.5 에서 목적값 0.695839, 분포 (0.919, 0.074, 0.007)(0.919,\,0.074,\,0.007) 로 닫힌 해와 일치합니다.

β의 두 극한

β\beta 는 지수의 분모에 있으므로 작을수록 크게 기울고 클수록 참조 모델 근처에 머뭅니다. 소프트맥스의 온도와 같은 역할입니다. 양 끝까지 밀어 보면 이 말이 정확해집니다.

β→∞\beta \to \infty 이면 r/β→0r/\beta \to 0 이라 모든 답의 가중치 er/βe^{r/\beta} 가 1로 모이고, 최적 정책은 참조 모델 그 자체가 됩니다. 장난감 문제에서 β=100\beta = 100 이면 π⋆=(0.5035, 0.2991, 0.1974)\pi^\star = (0.5035,\,0.2991,\,0.1974) 로 이미 (0.5, 0.3, 0.2)(0.5,\,0.3,\,0.2) 와 거의 같고, 평균 보상은 참조 모델의 값 0.5−0.2=0.30.5 - 0.2 = 0.3 근처인 0.3061입니다. 벌금이 너무 비싸서 한 걸음도 안 움직이는 쪽입니다.

β→0\beta \to 0 이면 반대입니다. 점수가 가장 높은 답과 나머지의 가중치 비가 e(rmax⁡−r)/βe^{(r_{\max} - r)/\beta} 로 무한히 벌어지므로, 확률이 전부 최고점 답 하나로 몰립니다. β=0.1\beta = 0.1 에서 이미 π⋆\pi^\star 가 소수 넷째 자리까지 (1, 0, 0)(1,\,0,\,0) 이고 평균 보상도 0.99997로 1에 붙습니다. 벌금이 공짜라서 보상만 보고 한 답에 모든 것을 거는 쪽입니다.

그 끝에서 KL은 무한대가 아니라 0.6931로 멈춥니다. 참조 모델이 최고점 답에 준 확률이 0.5였고, 분포 전체를 그 한 점에 몰았을 때의 KL은 log⁡(1/0.5)=log⁡2\log(1/0.5) = \log 2 이기 때문입니다. 일반적으로도 β→0\beta \to 0 의 KL은 −log⁡πref(ybest)-\log\pi_{\text{ref}}(y_{\text{best}}) 입니다. 참조 모델이 최고점 답을 드물게 낼수록 그 답까지 가는 거리가 멉니다.

β가 정하는 것

KL 예산과 승수

지난 글에서는 같은 문제를 두 가지로 적을 수 있다고 했습니다. 가격 β\beta 를 정하고 E[r]−β KL\mathbb{E}[r] - \beta\,\mathrm{KL} 을 최대화하거나, 예산 ε\varepsilon 을 정하고 「KL≤ε\mathrm{KL} \le \varepsilon 안에서 E[r]\mathbb{E}[r] 을 최대화」하는 것입니다. 뒤쪽의 라그랑지안이 E[r]−β(KL−ε)\mathbb{E}[r] - \beta(\mathrm{KL} - \varepsilon) 이고, 상수 βε\beta\varepsilon 을 떼면 앞쪽과 같은 식이므로 해도 같습니다. 다만 그때의 β\beta 는 우리가 고른 값이 아니라 예산이 정해 주는 값입니다.

장난감 문제에서 확인할 수 있습니다. 예산을 ε=0.4322\varepsilon = 0.4322 로 두고 KL이 정확히 그만큼인 β\beta 를 찾으면 0.5가 나옵니다 — 위 표의 셋째 줄을 거꾸로 읽은 것입니다. 예산을 ε=0.2\varepsilon = 0.2 로 두면 β=0.9727\beta = 0.9727 이고 그때 평균 보상은 0.7496입니다.

승수가 그림자 가격이라는 지난 글의 말도 여기서 그대로 섭니다. β\beta 를 조금씩 바꿔 가며 평균 보상과 KL을 같이 적으면 한 곡선이 그려지는데, 그 곡선의 기울기 d E[r]/d KLd\,\mathbb{E}[r] / d\,\mathrm{KL} 을 수치로 재면 이렇습니다.

β\beta KL E[r]\mathbb{E}[r] 곡선의 기울기
2 0.0625 0.5644 2.0000
1 0.1922 0.7419 1.0000
0.5 0.4322 0.9119 0.5000
0.25 0.6375 0.9889 0.2500

기울기가 매번 그 자리의 β\beta 와 같습니다. KL을 한 단위 더 허락하면 보상이 β\beta 만큼 오른다는 뜻이고, 그래서 β\beta 를 KL 1 단위의 가격으로 읽습니다. 가격이 낮은 자리일수록 곡선이 눕습니다. 이미 보상이 1에 가까워 KL을 더 써도 얻을 것이 별로 없기 때문입니다.

β를 내리면 보상은 오르고 KL도 함께 오른다

적응형 KL 계수

실무에서 정하고 싶은 것은 대개 가격이 아니라 예산입니다. 「참조 모델에서 이만큼 이상은 벗어나지 마라」가 먼저 떠오르는 요구이고, 그 예산에 맞는 β\beta 가 얼마인지는 보상의 크기와 데이터에 따라 달라 미리 알 수 없습니다. 지난 글의 끝에서 본 적응형 KL 계수는 이 둘을 잇습니다. 가격을 고정하는 대신 매 걸음 KL을 재서 예산을 넘으면 올리고 모자라면 내립니다.

β←β+η (KL−ε)\beta \leftarrow \beta + \eta\,\big(\mathrm{KL} - \varepsilon\big)

장난감 문제에 그대로 돌려 봤습니다. 목표 ε=0.2\varepsilon = 0.2, 시작 β=2\beta = 2, 보폭 η=1\eta = 1 로 두면 KL은 0.0625에서 출발합니다. 예산보다 한참 아래라 가격이 내려가고, 열 걸음 뒤에 β=1.093\beta = 1.093 에서 KL 0.1687, 서른 걸음 뒤에 β=0.9728\beta = 0.9728 에서 KL 0.2000에 닿고, 그 뒤로는 0.9727에 머뭅니다. 앞 소절에서 예산 0.2로 거꾸로 풀었던 바로 그 값입니다.

이 장치에는 매 걸음 KL을 잴 수단이 필요합니다. 정책에서 답을 새로 뽑아 참조 모델과의 로그비를 평균 내는 식입니다. 뒤에서 유도할 DPO 손실은 답을 뽑지 않으므로 이 고리가 없고, β\beta 는 학습 전에 정해 두는 상수로 남습니다. 예산을 직접 겨누고 싶다면 β\beta 를 몇 개 골라 학습한 뒤 KL을 재 보는 수밖에 없습니다.

보상을 정책으로 적기

암묵적 보상

여기까지는 「보상이 주어졌을 때 최적 정책은 무엇인가」였습니다. 이제 방향을 바꿉니다 — 최적 정책이 주어졌다면 보상은 무엇이었을까요. 위 상자의 식에 로그를 취해 rr 에 대해 풀면 됩니다.

log⁡π⋆(y∣x)=log⁡πref(y∣x)+r(x,y)β−log⁡Z(x)\log \pi^\star(y\mid x) = \log \pi_{\text{ref}}(y\mid x) + \frac{r(x,y)}{\beta} - \log Z(x)

r(x,y)=βlog⁡π⋆(y∣x)πref(y∣x)+βlog⁡Z(x)r(x,y) = \beta\log\frac{\pi^\star(y\mid x)}{\pi_{\text{ref}}(y\mid x)} + \beta\log Z(x)

이 식이 이 글의 전환점입니다. 보상 함수를 정책으로 적을 수 있습니다. 정책이 참조 모델보다 어떤 답에 더 큰 확률을 주고 있다면, 그만큼 그 답의 보상이 높았다는 뜻입니다. 두 로그 확률의 차이가 곧 보상이고, 앞에 β\beta 가 눈금으로 붙습니다. 앞으로 βlog⁡(π/πref)\beta\log(\pi/\pi_{\text{ref}}) 을 암묵적 보상이라 부르겠습니다 — 보상 모델이 따로 없는데도 정책 하나가 속에 품고 있는 점수라는 뜻입니다.

방금 그 장난감 문제로 확인해 보면 정확합니다. β=0.5\beta=0.5 의 최적 정책에서 암묵적 보상은 (0.304, −0.696, −1.696)(0.304,\,-0.696,\,-1.696) 이고, 여기에 βlog⁡Z=0.696\beta\log Z = 0.696 을 더하면 (1, 0, −1)(1,\,0,\,-1) 이 그대로 나옵니다.

문제는 log⁡Z(x)\log Z(x) 입니다. 이 항은 가능한 모든 답에 대한 합이라 계산할 수 없습니다 — 어휘가 VV 개이고 길이가 TT 면 VTV^T 개를 더해야 합니다.

길이 편향

언어 모델은 답 전체의 확률을 한 번에 내지 않습니다. 토큰을 하나씩 내고, 답의 확률은 각 토큰의 조건부 확률을 곱한 것입니다. 로그를 씌우면 곱이 합이 됩니다.

log⁡π(y∣x)=∑t=1Tlog⁡π(yt∣x,y<t)\log \pi(y\mid x) = \sum_{t=1}^{T} \log \pi(y_t \mid x, y_{<t})

각 항은 1보다 작은 확률의 로그라 음수이고, 그래서 답이 길수록 로그확률은 거의 언제나 더 작아집니다. 로그비도 같은 식으로 토큰마다의 차이를 더한 합입니다.

log⁡πθ(y∣x)πref(y∣x)=∑t=1T(log⁡πθ(yt∣x,y<t)−log⁡πref(yt∣x,y<t))\log\frac{\pi_\theta(y\mid x)}{\pi_{\text{ref}}(y\mid x)} = \sum_{t=1}^{T} \Big(\log \pi_\theta(y_t \mid x, y_{<t}) - \log \pi_{\text{ref}}(y_t \mid x, y_{<t})\Big)

여기서 길이 편향이 들어옵니다. 길이 편향은 답의 내용과 상관없이 길이 하나만으로 점수가 달라지는 치우침입니다. 정책이 참조 모델보다 토큰마다 로그확률을 0.02씩 높게 준다고 해 봅시다. 50토큰짜리 답이면 로그비가 1.0이고 400토큰짜리 답이면 8.0입니다. β=0.1\beta = 0.1 을 곱한 암묵적 보상으로는 0.1과 0.8이 되어, 토큰 하나하나에 대한 태도는 똑같은데 긴 답이 여덟 배 높은 점수를 받습니다.

그래서 선호 데이터에서 선택된 답이 대체로 더 길다면, 정책은 내용을 고치는 것보다 길게 쓰는 쪽으로 로그비를 쉽게 벌릴 수 있습니다. 로그비를 답의 토큰 수로 나눠 토큰당 평균으로 비교하는 변형들이 이 편향을 겨냥합니다 — 위 예라면 두 답 모두 0.02가 되어 길이의 몫이 사라집니다.

분배함수의 소거

같은 프롬프트의 두 답

선호 쌍에서 보상 모델 손실을 유도한 글의 Bradley-Terry 모델은 보상 자체가 아니라 보상의 차이만 씁니다. 같은 프롬프트에 대한 두 답을 비교하는 자리에 위 식을 넣어 보겠습니다. ywy_w 는 사람이 고른 답, yly_l 은 고르지 않은 답입니다.

r(x,yw)−r(x,yl)=βlog⁡π⋆(yw∣x)πref(yw∣x)+βlog⁡Z(x)−βlog⁡π⋆(yl∣x)πref(yl∣x)−βlog⁡Z(x)=βlog⁡π⋆(yw∣x)πref(yw∣x)−βlog⁡π⋆(yl∣x)πref(yl∣x)\begin{aligned} r(x,y_w) - r(x,y_l) &= \beta\log\frac{\pi^\star(y_w\mid x)}{\pi_{\text{ref}}(y_w\mid x)} + \beta\log Z(x) \\ &\quad - \beta\log\frac{\pi^\star(y_l\mid x)}{\pi_{\text{ref}}(y_l\mid x)} - \beta\log Z(x) \\ &= \beta\log\frac{\pi^\star(y_w\mid x)}{\pi_{\text{ref}}(y_w\mid x)} - \beta\log\frac{\pi^\star(y_l\mid x)}{\pi_{\text{ref}}(y_l\mid x)} \end{aligned}

Z(x)Z(x) 는 프롬프트에만 의존하고 답에는 의존하지 않습니다. 두 답이 같은 프롬프트에서 나왔으므로 두 βlog⁡Z(x)\beta\log Z(x) 가 정확히 같은 값이고, 빼면 사라집니다.

같은 프롬프트에서 나온 두 답이라 분배함수가 상쇄된다

이 소거는 앞 글의 성질을 다시 만난 것이기도 합니다. Bradley-Terry 모델에서 보상은 상수 평행이동에 불변이었고, βlog⁡Z(x)\beta\log Z(x) 는 그 프롬프트 안에서 모든 답에 똑같이 붙는 상수입니다. 데이터가 정하지 못하는 자유도라서 손실에 나타날 수 없었던 것이고, 계산할 수 없는 항이 하필 그 자리에 있었던 것은 우연이 아닙니다.

프롬프트별 쌍 데이터

소거가 기대는 것은 딱 하나, 두 답이 같은 프롬프트를 공유한다는 사실입니다. 프롬프트가 다른 두 답을 비교하면 βlog⁡Z(x1)−βlog⁡Z(x2)\beta\log Z(x_1) - \beta\log Z(x_2) 가 남고, 이 차이는 알 수 없으므로 암묵적 보상만 보고는 어느 쪽이 나은지 말할 수 없습니다.

장난감 문제 옆에 프롬프트를 하나 더 세워 보면 어긋남이 눈에 보입니다. 둘째 프롬프트에는 답이 둘이고 πref=(0.6, 0.4)\pi_{\text{ref}} = (0.6,\,0.4), r=(2, 1)r = (2,\,1) 입니다. β=0.5\beta = 0.5 로 두 프롬프트의 최적 정책을 각각 구하면 이렇습니다.

첫째 프롬프트의 1번 답 둘째 프롬프트의 1번 답
진짜 보상 1 2
암묵적 보상 0.304 0.212
βlog⁡Z\beta\log Z 0.696 1.788

암묵적 보상만 보면 첫째 프롬프트의 답이 0.092만큼 낫습니다. 실제로는 1만큼 못합니다. 둘째 프롬프트는 보상이 전반적으로 높아 분배함수가 크고, 그 몫이 암묵적 보상에서 빠져 있기 때문입니다. 두 줄을 더하면 각각 1과 2로 진짜 보상이 돌아오지만, 실제 문제에서는 그 셋째 줄을 계산할 수 없습니다.

그래서 DPO가 먹는 데이터는 낱개의 (프롬프트, 답, 점수)가 아니라 (x, yw, yl)(x,\,y_w,\,y_l) 세 쌍이어야 합니다. 한 프롬프트에 답이 여럿 모여 있고 순위가 매겨져 있다면 그 안에서 짝을 지어 쌍으로 쓸 수 있지만, 서로 다른 프롬프트의 답끼리는 짝을 지으면 안 됩니다. 점수만 매겨진 데이터로 보상 모델을 학습할 수 있는 것과 달리, 이쪽은 데이터를 모으는 방식부터 이 소거를 따라야 합니다.

DPO 손실

이제 선호 확률을 정책만으로 적을 수 있습니다.

P(yw≻yl∣x)=σ ⁣(βlog⁡π⋆(yw∣x)πref(yw∣x)−βlog⁡π⋆(yl∣x)πref(yl∣x))P(y_w \succ y_l \mid x) = \sigma\!\left(\beta\log\frac{\pi^\star(y_w\mid x)}{\pi_{\text{ref}}(y_w\mid x)} - \beta\log\frac{\pi^\star(y_l\mid x)}{\pi_{\text{ref}}(y_l\mid x)}\right)

보상 모델 손실을 유도할 때 했던 것을 그대로 하면 됩니다 — 이 확률에 최대가능도를 적용합니다. 다만 이번에는 최적화하는 대상이 보상 모델의 파라미터가 아니라 정책의 파라미터 θ\theta 입니다.

LDPO(θ)=− E(x,yw,yl)∼D[log⁡σ ⁣(βlog⁡πθ(yw∣x)πref(yw∣x)−βlog⁡πθ(yl∣x)πref(yl∣x))]\mathcal{L}_{\text{DPO}}(\theta) = -\,\mathbb{E}_{(x,y_w,y_l)\sim\mathcal{D}}\left[\log\sigma\!\left(\beta\log\frac{\pi_\theta(y_w\mid x)}{\pi_{\text{ref}}(y_w\mid x)} - \beta\log\frac{\pi_\theta(y_l\mid x)}{\pi_{\text{ref}}(y_l\mid x)}\right)\right]

맨 앞 코드가 이 식입니다. policy_logps_chosen - ref_logps_chosen이 첫 로그비이고, beta가 앞에 붙고, 두 개를 빼서 logsigmoid에 넣습니다.

DPO 손실의 기울기

기울기 유도

시그모이드 안에 든 것을 한 글자로 줄여 씁니다.

Δ=βlog⁡πθ(yw∣x)πref(yw∣x)−βlog⁡πθ(yl∣x)πref(yl∣x)\Delta = \beta\log\frac{\pi_\theta(y_w\mid x)}{\pi_{\text{ref}}(y_w\mid x)} - \beta\log\frac{\pi_\theta(y_l\mid x)}{\pi_{\text{ref}}(y_l\mid x)}

Δ\Delta 는 두 답의 암묵적 보상 차이이고, 한 쌍의 손실은 L=−log⁡σ(Δ)L = -\log\sigma(\Delta) 입니다. 연쇄 법칙으로 두 단계에 나눠 미분합니다.

첫 단계는 Δ\Delta 에 대한 미분입니다. 시그모이드의 도함수는 σ′(z)=σ(z) σ(−z)\sigma'(z) = \sigma(z)\,\sigma(-z) 이므로

dLdΔ=−σ′(Δ)σ(Δ)=−σ(Δ) σ(−Δ)σ(Δ)=−σ(−Δ)\frac{dL}{d\Delta} = -\frac{\sigma'(\Delta)}{\sigma(\Delta)} = -\frac{\sigma(\Delta)\,\sigma(-\Delta)}{\sigma(\Delta)} = -\sigma(-\Delta)

둘째 단계는 θ\theta 에 대한 Δ\Delta 의 기울기입니다. 참조 모델은 학습 중에 고정되어 있어 log⁡πref\log\pi_{\text{ref}} 는 θ\theta 로 미분하면 0이고, 남는 것은 정책의 로그확률 둘입니다.

∇θΔ=β(∇θlog⁡πθ(yw∣x)−∇θlog⁡πθ(yl∣x))\nabla_\theta \Delta = \beta\big(\nabla_\theta\log\pi_\theta(y_w\mid x) - \nabla_\theta\log\pi_\theta(y_l\mid x)\big)

둘을 곱하면

∇θL=−β σ(−Δ) (∇θlog⁡πθ(yw∣x)−∇θlog⁡πθ(yl∣x))\nabla_\theta L = -\beta\,\sigma(-\Delta)\,\big(\nabla_\theta\log\pi_\theta(y_w\mid x) - \nabla_\theta\log\pi_\theta(y_l\mid x)\big)

부호를 읽어야 합니다. 경사 하강은 θ←θ−η ∇θL\theta \leftarrow \theta - \eta\,\nabla_\theta L 이라 앞의 마이너스가 뒤집혀, 실제 한 걸음은 +ηβσ(−Δ)+\eta\beta\sigma(-\Delta) 곱하기 (∇log⁡πθ(yw)−∇log⁡πθ(yl))\big(\nabla\log\pi_\theta(y_w) - \nabla\log\pi_\theta(y_l)\big) 방향입니다. 선택된 답의 로그확률을 올리는 방향으로 가고 거절된 답의 로그확률을 올리는 방향과는 반대로 갑니다. β\beta 와 σ(−Δ)\sigma(-\Delta) 는 둘 다 양수라 방향을 바꾸지 못하고 보폭만 정합니다.

손으로 한 미분이 맞는지는 수치 미분으로 맞춰 봤습니다. 답이 넷이고 로짓 θ\theta 를 그대로 소프트맥스에 넣는 정책에서 β=0.1\beta = 0.1 로 두고, 한 성분씩 10−610^{-6} 만큼 흔들어 잰 기울기와 위 공식이 (−0.05075, 0.05075, 0, 0)(-0.05075,\,0.05075,\,0,\,0) 으로 일치합니다. 이 정책에서는 ∇log⁡π(yk)=ek−π\nabla\log\pi(y_k) = e_k - \pi 라 뺄셈에서 π\pi 가 지워지고 선택·거절된 두 답의 성분만 남습니다. 첫 성분이 음수이므로 경사 하강이 선택된 답의 로짓을 올립니다.

틀린 정도의 가중치

기울기 앞에 붙은 σ(−Δ)\sigma(-\Delta) 는 지금 이 쌍을 얼마나 틀리게 알고 있는가입니다. σ(Δ)\sigma(\Delta) 가 정책이 매기는 「ywy_w 가 낫다」의 확률이므로, σ(−Δ)=1−σ(Δ)\sigma(-\Delta) = 1 - \sigma(\Delta) 는 정답에 모자란 확률입니다.

Δ=−2\Delta = -2 이면 순서를 거꾸로 알고 있는 쌍이고 가중치가 0.881로 거의 온 힘으로 밉니다. Δ=0\Delta = 0 이면 두 답을 구별하지 못하는 상태라 0.5입니다. Δ=2\Delta = 2 이면 0.119, Δ=5\Delta = 5 이면 0.0067로, 이미 잘 갈라 놓은 쌍은 Δ=0\Delta = 0 인 쌍의 75분의 1만 밉니다. 학습이 쉬운 쌍을 알아서 놓고 어려운 쌍에 힘을 모으는 것은 따로 넣은 장치가 아니라 로그 시그모이드를 미분하면 나오는 성질입니다. 보상 모델 손실의 기울기에서 본 것과 같은 가중치입니다.

기울기의 가중치 σ(−Δ)는 지금 틀린 정도다

여기에 β\beta 가 얹힙니다. Δ\Delta 는 β\beta 곱하기 로그비 차이라, β\beta 를 크게 두면 로그비가 조금만 벌어져도 Δ\Delta 가 커져 가중치가 빨리 잦아들고 정책은 참조 모델에서 조금만 움직입니다. 작게 두면 같은 로그비 차이에도 Δ\Delta 가 작아 계속 밀어붙이고 정책이 멀리 나갑니다. 최적 정책에서 지수의 분모였던 β\beta 와 같은 것을 손실 쪽에서 본 것이라 방향도 같습니다 — 여기서도 KL 1 단위의 가격입니다.

함께 내려가는 두 로그확률

손실이 보는 것은 Δ\Delta 하나, 즉 두 로그비의 차이뿐입니다. 그러니 차이만 벌어진다면 두 로그확률이 어디로 가든 손실은 줄어듭니다. 선택된 답의 확률이 올라가야 한다는 조건은 식 어디에도 없습니다.

실제로 둘이 함께 내려가는 일이 흔히 보고되고, 장난감으로 그 구조를 재현할 수 있습니다. 답이 셋이고 정책의 로짓이 파라미터 두 개의 선형 결합인 모델입니다. 답마다 특징 벡터가 있어 선택된 답은 (1, 1)(1,\,1), 거절된 답은 (1, 0)(1,\,0), 데이터에 한 번도 나오지 않는 셋째 답은 (0, 2)(0,\,2) 입니다. 참조 모델은 (0.6, 0.3, 0.1)(0.6,\,0.3,\,0.1) 로 두고 β=0.5\beta = 0.5, 보폭 1로 이 한 쌍만 100걸음 학습했습니다.

import numpy as np
sig = lambda z: 1 / (1 + np.exp(-z))
Phi = np.array([[1., 1.], [1., 0.], [0., 2.]])   # 선택 · 거절 · 데이터 밖
b0 = np.log([0.6, 0.3, 0.1])
lp = lambda th: (b0 + Phi @ th) - np.log(np.exp(b0 + Phi @ th).sum())

th, beta = np.zeros(2), 0.5
ref = lp(th)
for step in range(100):
    l = lp(th)
    D = beta * ((l[0] - ref[0]) - (l[1] - ref[1]))
    th += beta * sig(-D) * (Phi[0] - Phi[1])    # 경사 하강 한 걸음

l = lp(th)
D = beta * ((l[0] - ref[0]) - (l[1] - ref[1]))
print(np.round(l - ref, 3), round(D, 3), np.round(np.exp(l), 3))
# [ -3.994 -10.28    2.291] 3.143 [0.011 0.    0.989]

선택된 답의 로그비는 0에서 −3.99-3.99 로, 거절된 답은 −10.28-10.28 로 둘 다 내려갔습니다. 그 사이는 6.29로 벌어져 Δ=3.14\Delta = 3.14 가 되었고 손실은 0.693에서 0.042로 줄었습니다. 선택된 답의 확률은 0.6에서 0.011로 떨어졌고, 빠져나간 확률은 데이터에 없는 셋째 답이 0.989까지 가져갔습니다.

두 로그비가 함께 내려가도 Δ는 벌어진다

원인은 기울기 식에 이미 있습니다. 이 모델에서 ∇log⁡π(y)=ϕ(y)−Eπ[ϕ]\nabla\log\pi(y) = \phi(y) - \mathbb{E}_\pi[\phi] 이고 뺄셈에서 기댓값이 지워지므로, 한 걸음의 방향은 ϕ(yw)−ϕ(yl)=(0, 1)\phi(y_w) - \phi(y_l) = (0,\,1) 입니다. 둘째 특징을 키우라는 뜻인데 그 특징을 가장 많이 가진 것은 선택된 답이 아니라 셋째 답입니다. 실제 신경망에서도 답들은 토큰과 표현을 나눠 쓰므로, 한 쌍을 벌리려는 걸음이 데이터 밖의 답을 함께 밀어 올리는 일이 생깁니다. 그리고 손실은 그 답을 보지 않으므로 이것을 막을 항이 없습니다.

가정과 맞바꿈

두 가정

이 유도가 성립하기 위해 조용히 쓴 가정 둘을 적어 둡니다. 첫째, 선호 데이터가 Bradley-Terry 모델을 따른다고 가정했습니다. 사람의 선호가 실수 점수 하나로 순서 지어지지 않는 경우 — 평가자마다 기준이 다르거나 A가 B보다, B가 C보다, C가 A보다 나은 순환이 있는 경우 — 이 다리는 그만큼 흔들립니다.

둘째, πθ\pi_\theta 가 π⋆\pi^\star 자리에 그대로 들어갈 수 있다고 두었습니다. 최적 정책은 모든 분포 중에서 고른 것인데 신경망이 표현할 수 있는 분포는 그중 일부이므로, 정확히 그 볼츠만 분포에 닿는다는 보장은 없습니다. 앞 절의 장난감이 그 예입니다. 파라미터가 둘뿐이라 선택된 답만 올리는 분포를 표현할 수 없었고, 손실을 줄이는 가장 쉬운 길이 데이터 밖의 답을 키우는 것이었습니다.

참조 모델의 분포 어긋남

셋째 걱정은 데이터가 어디서 왔는가입니다. 최적 정책 π⋆∝πref er/β\pi^\star \propto \pi_{\text{ref}}\,e^{r/\beta} 는 참조 모델을 기울인 것이라, 참조 모델이 거의 안 내는 답에는 보상이 높아도 확률이 잘 안 옵니다. 참조 모델이 10−610^{-6} 을 주는 드문 답이 흔한 답보다 보상이 3 높다고 해 봅시다. β=0.5\beta = 0.5 면 가중치가 e6≈403e^{6} \approx 403 배라, 최적 정책에서 그 답의 확률은 약 0.0004입니다. 이 정도로 드문 답이 선호 데이터의 선택된 답이라면, 식이 가리키는 목표와 데이터가 보여 주는 목표가 서로 다른 곳에 있습니다.

실제로 선호 데이터의 답은 참조 모델이 쓴 것이 아닐 때가 많습니다. 다른 모델이 썼거나 사람이 고쳐 쓴 답입니다. 그러면 로그비가 참조 모델이 드물게 보는 영역에서 계산되고, 그 자리의 log⁡πref\log\pi_{\text{ref}} 는 크고 불안정한 음수라 암묵적 보상이 제 뜻을 잃습니다.

흔한 대처는 DPO 전에 선택된 답들로 한 번 지도 파인튜닝을 하는 것입니다. 지도 파인튜닝(SFT)은 주어진 답을 정답으로 두고 그 답의 로그확률을 최대화하도록 모델을 학습하는 것이고, 이렇게 데이터 쪽으로 옮긴 모델을 참조 모델이자 학습의 출발점으로 씁니다. 참조 모델이 데이터의 답들을 충분한 확률로 내게 되면 로그비가 제자리에서 계산되고, 위의 10−610^{-6} 같은 답이 사라집니다.

온라인 샘플링과의 맞바꿈

보상 모델을 없앤 대신 무엇을 얻고 무엇을 잃는지도 유도에 적혀 있습니다. 얻은 것은 모델 두 개와 샘플링 루프입니다. 보상 모델과 가치 모델을 메모리에 올릴 필요가 없고, 학습 중에 답을 뽑는 비싼 과정도 없습니다. 선호 쌍과 두 모델의 로그확률만 있으면 지도 학습처럼 배치를 돌리면 됩니다.

잃은 것은 학습 중에 새 답을 뽑아 채점받을 기회입니다. DPO는 데이터셋에 있는 답만 봅니다. 앞의 장난감에서 확률의 99%가 데이터 밖의 셋째 답으로 옮겨 갔는데, 그 답은 데이터에 없으므로 손실은 끝까지 그 사실을 몰랐습니다. 정책에서 답을 뽑아 보상 모델로 채점하는 방식이었다면 셋째 답이 곧 표본에 나타났을 것이고, 점수가 낮다면 그 자리에서 깎였을 것입니다.

이 대비가 실무에서 두 방법이 나란히 쓰이는 까닭입니다. 가볍고 안정적인 DPO로 먼저 크게 옮기고, 새 답을 뽑아 채점하는 방식으로 다듬는 식입니다. 현재 정책으로 새 답을 뽑아 선호 쌍을 다시 만들고 DPO를 되풀이하는 변형도 그 중간에 있습니다. 다만 답을 뽑는 쪽으로 돌아오면 비용 문제가 다시 생깁니다 — 한 번 뽑은 배치를 여러 걸음에 재사용하고 싶어지고, 그러면 지금 정책이 아니라 예전 정책이 뽑은 표본으로 학습하게 됩니다. 그 대가를 다음 글에서 셉니다.

정리

유도의 흐름

지난 다섯 편이 하나로 이어집니다. 「좋은 답을 내게 한다」를 Eπ[r]\mathbb{E}_\pi[r] 로 적었고, 그 그래디언트를 표본에서 얻는 항등식을 세웠고, 분산을 줄이는 베이스라인을 붙였고, 선호 쌍에서 rr 을 얻는 손실을 유도했고, KL 예산을 승수로 목적식에 넣었습니다. 그리고 이 글에서 그 목적식을 풀었더니 최적 정책이 닫힌 형태로 나왔고, 그것을 뒤집어 앞 글의 손실에 넣자 보상 모델이 사라졌습니다.

사라진 것은 보상 모델만이 아닙니다. 계산할 수 없던 분배함수는 같은 프롬프트의 두 답을 빼는 자리에서 지워졌고, 그 대가로 데이터가 프롬프트마다 쌍으로 묶여 있어야 한다는 조건이 붙었습니다. 샘플링 루프도 사라졌고, 그 대가로 데이터 밖의 답을 볼 눈을 잃었습니다. 얻은 것과 치른 것이 식의 같은 자리에서 나옵니다.

요점

  • max⁡πEπ[r]−βKL(π∥πref)\max_\pi \mathbb{E}_\pi[r] - \beta\mathrm{KL}(\pi\|\pi_{\text{ref}}) 의 해는 볼츠만 분포 π⋆∝πref er/β\pi^\star \propto \pi_{\text{ref}}\,e^{r/\beta} 다. 분포를 숫자 NN 개의 벡터로 보고 합이 1이라는 제약에 라그랑주 승수를 쓰면 편미분으로 나온다. 지수라서 양수 조건은 저절로 지켜진다.
  • 목적식을 −βKL(π∥π⋆)+βlog⁡Z-\beta\mathrm{KL}(\pi\|\pi^\star) + \beta\log Z 로 다시 묶으면 그것이 최대라는 것과 최적값이 βlog⁡Z\beta\log Z 라는 것이 함께 나온다.
  • β→∞\beta \to \infty 이면 참조 모델, β→0\beta \to 0 이면 최고점 답 하나다. 그 사이에서 평균 보상과 KL이 그리는 곡선의 기울기가 정확히 β\beta 라 KL 1 단위의 가격으로 읽는다. 예산을 정하고 싶으면 적응형 KL 계수로 가격을 맞춘다.
  • 식을 뒤집으면 r=βlog⁡π⋆πref+βlog⁡Zr = \beta\log\frac{\pi^\star}{\pi_{\text{ref}}} + \beta\log Z 다. 앞 항이 암묵적 보상이고, 토큰마다의 합이라 길이 편향이 들어온다.
  • log⁡Z(x)\log Z(x) 는 프롬프트에만 의존하므로 같은 프롬프트의 두 답을 비교하면 상쇄된다. 프롬프트가 다른 답끼리는 비교할 수 없어 데이터가 (x,yw,yl)(x, y_w, y_l) 쌍이어야 한다.
  • DPO 손실의 기울기는 −βσ(−Δ)(∇log⁡πθ(yw)−∇log⁡πθ(yl))-\beta\sigma(-\Delta)(\nabla\log\pi_\theta(y_w) - \nabla\log\pi_\theta(y_l)) 다. 가중치 σ(−Δ)\sigma(-\Delta) 는 지금 틀린 정도이고, 손실이 차이만 보므로 두 로그확률이 함께 내려가도 손실은 준다.
  • 유도가 기대는 것은 Bradley-Terry 가정과 표현력이고, 데이터가 참조 모델에서 멀면 지도 파인튜닝으로 먼저 옮긴다. 샘플링 루프를 지운 대신 데이터 밖의 답을 채점받을 기회를 잃었다.

읽어주셔서 감사합니다. 😊

LATEST

수학의 최신 글

수학2026.09.07

양자화 오차: 격자 사상, 오차 분산, 이상치 채널

실수를 2^b개 격자에 사상할 때 오차의 분산이 왜 Δ²/12인지 유도하고, 그것이 비트당 6.02dB라는 SNR로 번역되는 과정을 실측과 대조했습니다. 이상치 하나가 나머지 값의 유효 비트를 어떻게 먹는지, 그리고 int4에서 성능이 무너지는 지점을 오차 예산으로 미리 계산하는 법까지.

중급18 MIN
수학2026.09.07

수치적으로 안정한 계산 패턴 모음

최댓값 빼기, 로그 공간, log1p·expm1, 분산의 두 공식, 정규화의 ε, fp32 누산, 역행렬 대신 solve — 프레임워크가 몰래 해 주는 일곱 가지를 하나씩 꺼내 각각 어떤 고장을 막는지 직접 재 봤습니다. 수식을 그대로 옮긴 코드가 왜 라이브러리보다 나쁜지에 대한 목록입니다.

중급22 MIN
수학2026.09.07

부동소수점은 어디서 새는가: 반올림, 상쇄, 더하는 순서

0.1 + 0.2가 0.3이 아닌 이유부터 시작해 머신 엡실론을 유도하고, 같은 16비트인데 fp16과 bf16이 서로 다른 지점에서 터지는 이유, 비슷한 수를 뺄 때 유효자리가 사라지는 파괴적 상쇄, 그리고 1,000만 개를 순서만 바꿔 더했을 때 오차가 백만 배 갈리는 실험까지 직접 재 봤습니다.

중급23 MIN