Part II: 정책 그래디언트의 진화 — VPG에서 PPO까지

— DPO의 한 줄 수식이 어디서 왔는지 알려면, 30여 년 전의 질문으로 돌아가야 한다: “점수를 미분할 수 없는데, 어떻게 점수를 올리는가?”


Chapter 2: VPG — 미분할 수 없는 점수를 올리는 법

의문

DPO는 좋은 답과 나쁜 답의 마진을 레퍼런스(학습 전 모델을 고정해 둔 비교 기준)보다 더 벌리는 한 줄짜리 손실이다. 그런데 그 식은 어디서 왔을까? DPO 논문의 부제는 “Your Language Model is Secretly a Reward Model” 이다. "비밀리에"라는 말은 원래 보상 모델이 따로 있었다는 뜻이다. 그 보상 모델로 언어모델을 학습시키던 방법 — RLHF(사람의 선호로 학습한 보상 모델의 점수를 강화학습으로 올리는 절차)와, 그 안에서 점수를 올리는 데 쓰인 강화학습 알고리즘 PPO — 가 먼저 있었고, DPO는 그 무거운 절차를 수식 변환으로 건너뛴 우회로다.

우회로를 이해하려면 원래 길부터 걸어봐야 한다. 원래 길의 출발점에는 이런 질문이 있다:

SFT(정답을 보여 주며 그대로 따라 쓰게 하는 지도 미세조정)는 프롬프트 xx 에 대한 정답 y∗y^*가 주어지니,
모델 πθ\pi_\theta 가 그 정답을 낼 확률의 음의 로그 −log⁡πθ(y∗∣x)-\log \pi_\theta(y^* \mid x)를 미분하면 됐다.
그런데 정답 대신 점수만 있다면? "이 답은 0.8점, 저 답은 0.1점"이라는 채점만 가능하고,
채점 기준(사람, 검증기, 보상 모델)은 미분할 수 없다면?

이 장은 그 물음에 대한 가장 순수한 답을 본다. 아무것도 덧붙이지 않은 그 답이, 뒤에 나오는 복잡한 알고리즘들이 모두 딛고 선 바닥이다.

목적함수: 내가 뽑는 응답의 평균 점수

채점만 가능한 문제를 풀려면, 먼저 언어모델이 하는 일을 "선택을 하고 점수를 받는 게임"으로 다시 적어야 한다. 모델은 언제 한 번의 선택을 하고, 점수는 언제 들어오는가? 그리고 무엇을 올려야 하는가?

언어모델을 RL의 언어로 다시 쓰기

강화학습(RL)의 기본 단어는 상태·행동·정책·보상이다. 언어모델에 그대로 대응시킬 수 있다.

RL 용어 뜻 언어모델에서
상태 sts_t 지금 보고 있는 상황 프롬프트 xx + 지금까지 쓴 토큰 y<ty_{<t}
행동 ata_t 이번에 고르는 것 다음 토큰 yty_t
정책 πθ(a∣s)\pi_\theta(a \mid s) 상황별 행동 확률 언어모델의 다음 토큰 분포 (소프트맥스 출력)
보상 RR 결과에 대한 점수 응답 y=(y1,…,yT)y = (y_1, \dots, y_T) 가 끝난 뒤 한 번 매기는 점수 R(x,y)R(x, y)

마지막 줄이 중요하다. 로봇이나 게임과 달리, 언어모델은 보통 응답이 끝나야 점수를 받는다. 중간 토큰마다 받는 보상은 0이고 마지막에 한 번 몰아서 받는다. 토큰을 하나 고를 때마다 상태는 한 토큰씩 길어지고, 점수는 맨 끝에서야 들어온다.

상태 (보고 있는 것) 행동 보상 s₁ = x y₁ 고름 r₁ = 0 s₂ = x + y₁ y₂ 고름 r₂ = 0 s₃ = x + y₁ + y₂ y₃ 고름 r₃ = R(x, y) 응답이 끝나면 채점 상태는 한 토큰씩 길어지고, 점수는 맨 끝에 한 번

그렇다면 응답 중간의 토큰 하나가 끝의 점수에 얼마나 기여했는지는, 그 점수로 알아낼 수 있을까?

목표: 평균 점수를 올려라

정책이 할 일은 한 문장으로 적힌다. 내가 뽑는 응답들의 평균 점수를 올려라.

J(θ)=Ey∼πθ(⋅∣x)[R(x,y)]=∑yπθ(y∣x) R(x,y)\textcolor{#d62728}{J}(\theta) = \mathbb{E}_{\textcolor{#1c9c60}{y} \sim \textcolor{#1565c0}{\pi_\theta}(\cdot \mid \textcolor{#0093b8}{x})}\big[\textcolor{#d9670b}{R}(\textcolor{#0093b8}{x}, \textcolor{#1c9c60}{y})\big] = \sum_{\textcolor{#1c9c60}{y}} \textcolor{#1565c0}{\pi_\theta}(\textcolor{#1c9c60}{y} \mid \textcolor{#0093b8}{x})\, \textcolor{#d9670b}{R}(\textcolor{#0093b8}{x}, \textcolor{#1c9c60}{y})
J(θ)목적함수: 지금 정책이 받는 평균 점수πθ학습 중인 정책(언어모델), 파라미터 θR(x,y)프롬프트 x에 대한 응답 y의 점수x, y프롬프트와 응답 \small\begin{array}{ll} \textcolor{#d62728}{J}(\theta) & \text{목적함수: 지금 정책이 받는 평균 점수} \\ \textcolor{#1565c0}{\pi_\theta} & \text{학습 중인 정책(언어모델), 파라미터 } \theta \\ \textcolor{#d9670b}{R}(\textcolor{#0093b8}{x}, \textcolor{#1c9c60}{y}) & \text{프롬프트 } \textcolor{#0093b8}{x} \text{에 대한 응답 } \textcolor{#1c9c60}{y} \text{의 점수} \\ \textcolor{#0093b8}{x},\ \textcolor{#1c9c60}{y} & \text{프롬프트와 응답} \end{array}

SFT와 나란히 놓으면 차이가 보인다.

SFT 정책 그래디언트
주어진 것 정답 y∗\textcolor{#1c9c60}{y^*} 채점 함수 R(x,y)\textcolor{#d9670b}{R}(\textcolor{#0093b8}{x}, \textcolor{#1c9c60}{y})
목표 log⁡πθ(y∗)\log \textcolor{#1565c0}{\pi_\theta}(\textcolor{#1c9c60}{y^*}) 최대화 Ey∼πθ[R]\mathbb{E}_{\textcolor{#1c9c60}{y} \sim \textcolor{#1565c0}{\pi_\theta}}[\textcolor{#d9670b}{R}] 최대화
응답은 누가 만드나 데이터셋 (남이 만든 것) 지금의 모델 자신
미분 가능한가 그대로 가능 R\textcolor{#d9670b}{R}도, 샘플링도 미분 불가
왜 그냥 미분할 수 없는가

J(θ)\textcolor{#d62728}{J}(\theta) 는 두 가지 모양으로 적을 수 있고, 모양마다 막히는 곳이 다르다.

합으로 보면 J=∑yπθ(y∣x) R(x,y)\textcolor{#d62728}{J} = \sum_{\textcolor{#1c9c60}{y}} \textcolor{#1565c0}{\pi_\theta}(\textcolor{#1c9c60}{y} \mid \textcolor{#0093b8}{x})\,\textcolor{#d9670b}{R}(\textcolor{#0093b8}{x}, \textcolor{#1c9c60}{y}) 다. 응답 하나하나의 점수 R\textcolor{#d9670b}{R} 은 θ\theta 와 상관없이 정해진 숫자이고, θ\theta 가 바꾸는 것은 각 응답이 뽑힐 확률 πθ\textcolor{#1565c0}{\pi_\theta} 뿐이다. 이 모양이면 미분 자체는 된다. 막히는 곳은 합의 크기다. 가능한 모든 응답을 하나씩 채점해 더해야 하는데, 응답의 가짓수는 셀 엄두가 나지 않을 만큼 많다.

뽑아서 보면 응답 몇 개를 뽑아 채점한 평균이 J\textcolor{#d62728}{J} 의 어림값이다. 합은 필요 없어졌지만, 이 값을 θ\theta 로 미분하려면 「θ\theta → 응답 뽑기 → 채점」 길을 거꾸로 따라가야 하고, 그 길이 두 곳에서 끊긴다. 토큰 하나를 뽑는 순간 선택은 이것 아니면 저것이라, θ\theta 를 살짝 바꾼다고 뽑힌 토큰이 살짝 바뀌지 않는다. 채점기도 미분할 수 없다. 수학 문제 채점기가 "정답이면 1"을 돌려줄 때, 답을 살짝 바꾸면 점수가 얼마나 바뀌는지 물을 방법이 없다. 사람 평가자는 말할 것도 없다.

그래서 SFT처럼 "손실을 만들고 역전파"하는 길이 막혀 있다. 필요한 것은 점수는 건드리지 않고, 확률만 미분하는 방법이다.

문제 1 — 총점만 알려 주는 쪽지 시험

다섯 문항짜리 쪽지 시험에서 조교는 문항별로 채점해 주지 않고 총점만 알려 준다. 민준이 처음 낸 답안은 3점이었다. 5번 문항의 답만 바꿔 다시 냈더니 4점이었다. (가) 5번 문항에 대해 무엇을 알 수 있는가? (나) 처음 답안에서 1번 문항을 맞혔는지 알 수 있는가?

선생님 (평상)
선생님
민준 학생, 3점짜리 처음 답안에서 1번은 맞았을까요?
김민준 (자신만만)
김민준
다섯 문항에 3점이면 과반을 맞힌 거니까, 1번도 맞았겠죠.
이서연 (평상)
이서연
짐작 말고 확실히 아는 것부터 보자. 5번만 바꿨는데 1점 올랐으니까, 5번은 처음 답이 틀렸고 바꾼 답이 맞은 거야.
김민준 (난처함)
김민준
그럼 처음 답안의 3점은 전부 1~4번에서 나왔네. 넷 가운데 셋을 맞혔고… 어느 게 틀렸는지는 모르겠어요.
선생님 (평상)
선생님
그래요. 총점 하나만으로는 문항 하나하나의 몫을 가를 수 없어요. 한 군데만 다른 두 답안을 견주면, 그 한 군데의 몫만 드러나죠.

정리 (가) 처음 낸 5번 답은 틀렸고, 바꾼 답은 맞았다. (나) 알 수 없다. 1~4번 가운데 셋을 맞혔다는 것까지만 안다. 총점만으로는 문항별 몫이 드러나지 않고, 한 군데만 다른 두 답안을 견주면 그 한 군데의 몫만 드러난다.

문제 2 — 상태, 행동, 보상

프롬프트 "수도는?"에 모델이 “서울 입니다 .” 세 토큰을 생성해 점수 R=1\textcolor{#d9670b}{R} = 1을 받았다. (가) 각 단계의 상태와 행동을 적고, 토큰마다의 보상 r1,r2,r3\textcolor{#d9670b}{r_1}, \textcolor{#d9670b}{r_2}, \textcolor{#d9670b}{r_3} 을 쓰시오. (나) 같은 프롬프트에 모델이 "서울 이요 ."를 생성해 R=0.6\textcolor{#d9670b}{R} = 0.6을 받은 적도 있다. 두 응답에서 첫 행동 "서울"을 고른 상태는 같은가? 두 점수로 "서울"을 고른 것이 얼마나 잘한 선택이었는지 말할 수 있는가?

선생님 (평상)
선생님
민준 학생, “서울 입니다 .” 를 만드는 동안 모델은 몇 번 선택을 했나요?
김민준 (평상)
김민준
세 번이요. “서울”, “입니다”, “.” 를 하나씩 골랐으니까요.
선생님 (평상)
선생님
그 세 번 각각, 모델이 보고 있던 것은요?
김민준 (평상)
김민준
첫 번째는 “수도는?” 만, 두 번째는 “수도는? 서울”, 세 번째는 “수도는? 서울 입니다”. 그게 상태고, 고른 토큰이 행동이죠.
이서연 (평상)
이서연
보상은 행동마다 하나씩 붙는 거야?
김민준 (평상)
김민준
아니, 채점기는 문장이 끝나야 돌아가잖아. 앞의 두 번은 0이고 마지막에 1.
선생님 (미소)
선생님
맞아요. 언어모델 RL의 보상은 대부분 이렇게 끝에 몰려 있어요. 그럼 (나)로 가 볼까요. '서울’이라는 첫 토큰은 얼마나 잘한 걸까요?
김민준 (자신만만)
김민준
첫 응답이 1점이니까 '서울’도 1점짜리 선택이죠. 정답의 첫 단어잖아요.
이서연 (평상)
이서연
그런데 둘째 응답도 '서울’로 시작했는데 0.6점이야. 같은 상태에서 같은 토큰을 골랐는데, 그럼 '서울’은 1점짜리야, 0.6점짜리야?
김민준 (난처함)
김민준
어… 둘 다 첫 상태는 "수도는?"이고 고른 것도 '서울’이네. 달라진 건 그다음부터고.
선생님 (질문)
선생님
그럼 두 점수의 차이 0.4는 어디서 왔다고 봐야 할까요?
이서연 (평상)
이서연
둘째 행동부터요. '입니다’와 '이요’가 갈린 뒤로 달라졌으니까요. ‘서울’ 하나의 몫은 이 두 점수로는 떼어 낼 수 없어요.
김민준 (깨달음)
김민준
아까 쪽지 시험이랑 같네요. 한 군데만 다른 두 답안을 견주면 달라진 곳의 몫만 보이고, 똑같은 앞부분의 몫은 안 보여요.

정리 (가) 상태 = 프롬프트 + 지금까지의 토큰, 행동 = 다음 토큰. 보상은 마지막 행동 뒤에 한 번 (r1=r2=0\textcolor{#d9670b}{r_1} = \textcolor{#d9670b}{r_2} = 0, r3=1\textcolor{#d9670b}{r_3} = 1). (나) 첫 상태(“수도는?”)와 첫 행동(“서울”)은 두 응답에서 같다. 점수 차 0.4는 둘째 행동부터 갈린 데서 왔고, “서울” 하나의 몫은 끝에 받은 점수로 떼어 낼 수 없다.