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

정책 그래디언트: 점수는 가중치일 뿐이다

점수 RR은 미분할 수 없고, 응답을 뽑는 순간 연속성도 끊긴다. 그런데 SFT에서 매일 미분하던 양이 하나 있다. 확률의 로그다. 점수는 그대로 두고 로그확률만 미분해서 평균 점수를 올릴 수 있을까?

역사: 기울기를 계산하지 않고 기울기를 따라가기

1980년대 신경망 연구에서 역전파는 기울기가 단순하면서도 놀랄 만큼 잘 듣는 학습 규칙의 바탕이 된다는 것을 보여 주고 있었다. 그런데 보상만 주어지는 학습에는 역전파할 정답이 없다. 노스이스턴 대학의 로널드 윌리엄스(Ronald J. Williams)는 출력을 무작위로 내는 신경망 단위를 두고 이 물음을 붙들었다. 1986년부터 기술 보고서와 학회 발표로 조금씩 내놓은 결과를 1992년 학술지 Machine Learning의 논문 「Simple Statistical Gradient-Following Algorithms for Connectionist Reinforcement Learning」으로 묶었다. 그가 보인 것은, 받은 보상을 행동 확률의 로그 미분에 곱해 파라미터를 고치면 평균적으로 기대 보상이 오르는 방향으로 간다는 것이다. 기울기를 따로 계산하지도, 계산할 정보를 모아 두지도 않는다. 제목의 「간단한(simple)」이 그 뜻이라고 그는 적고, 더 정확한 말은 「모형을 쓰지 않는(non-model-based)」일 것이라고 덧붙였다. 조건은 출력을 내는 확률분포가 파라미터로 미분 가능하다는 것 하나였다. 이 규칙들에 그가 붙인 이름이 REINFORCE다.

1999년 학회 NIPS에서 발표된 리처드 서튼(Richard S. Sutton)과 동료들의 논문은 다른 쪽의 막힘에서 출발했다. 그때까지 주류는 행동마다 가치(앞으로 받을 점수의 어림)를 매기고 가치가 가장 높은 행동을 고르는 방식이었다. 이 방식에서는 어림값이 아주 조금만 바뀌어도 고르는 행동이 통째로 바뀐다. 그 불연속 때문에 간단한 문제와 간단한 함수 근사에서도 어떤 정책으로도 수렴하지 못하는 예가 여럿 알려져 있었다. 서튼과 동료들은 정책을 따로 파라미터로 두고 기대 보상의 기울기를 따라 고치면 파라미터를 조금 바꿀 때 정책도 조금만 바뀐다고 짚고, 그 기울기를 경험에서 추정할 수 있는 꼴로 적는 정책 그래디언트 정리(policy gradient theorem)를 보였다. 같은 논문은 윌리엄스의 REINFORCE가 평균으로는 정확한 기울기를 주지만 학습이 훨씬 느려 그동안 별로 주목받지 못했다고도 적었다.

답이 둘일 때: 확률을 옮기면 평균 점수가 움직인다

식을 세우기 전에 숫자로 보자. 답이 「맞음」과 「틀림」 둘뿐이고, 채점기는 맞음에 1점, 틀림에 0점을 준다. 가장 작은 경우를 고른 것은, 답이 둘이면 맞음의 확률 pp 하나만 정하면 틀림의 확률 1−p1-p 도 정해져서 손으로 끝까지 셀 수 있기 때문이다. 지금 모델은 맞음을 p=0.3p = 0.3 으로 고른다. 평균 점수는 0.3×1+0.7×0=0.30.3 \times 1 + 0.7 \times 0 = 0.3 이다.

맞음의 확률을 0.31로 조금 올리면 평균 점수는 0.31이 된다. 점수는 하나도 바뀌지 않았는데 평균 점수가 움직였다. 움직인 것은 확률뿐이다. pp 를 0.01 올렸더니 평균 점수가 0.01 올랐으니, 평균 점수를 pp 로 미분한 값은 1이다.

실제 언어모델에서는 답의 가짓수가 너무 많아 확률표 전체를 훑으며 채점할 수 없다. 그래서 확률표를 들여다볼 수 없고 답을 뽑아 채점만 할 수 있다고 하자. 그래도 이 1을 얻을 수 있을까? 뽑힌 답의 점수에, 그 답의 로그확률을 pp 로 미분한 값을 곱해 보자. 맞음이 뽑히면 1×ddplog⁡p=1/0.3≈3.331 \times \frac{d}{dp}\log p = 1/0.3 \approx 3.33, 틀림이 뽑히면 점수가 0이라 0이다. 맞음은 열 번에 세 번꼴로 뽑히므로 평균은 0.3×3.33+0.7×0=10.3 \times 3.33 + 0.7 \times 0 = 1. 정확한 기울기와 같다. 한 번 한 번의 값(3.33이나 0)은 1과 거리가 멀지만, 평균은 정확하다.

0.30 → 0.31 0.70 → 0.69 맞음 틀림 점수 1 (그대로) 점수 0 (그대로) 평균 점수 0.30 → 0.31 뽑아서 어림하면 맞음 (30%): 1 × 1/0.3 = 3.33 틀림 (70%): 0 평균: 0.3 × 3.33 = 1 점수는 그대로 두고, 움직이는 것은 확률뿐
로그 미분 트릭 — 확률만 미분한다

이 계산을 일반 식으로 적자. JJ를 합의 형태로 쓰고 미분하면, 응답 하나하나의 점수 RR은 θ\theta와 무관한 숫자이므로(θ\theta가 바꾸는 것은 그 응답이 뽑힐 확률뿐이다) 그냥 곱해진 채로 남는다.

∇θJ=∑yR(y) ∇θπθ(y)\nabla_\theta \textcolor{#d62728}{J} = \sum_{\textcolor{#1c9c60}{y}} \textcolor{#d9670b}{R}(\textcolor{#1c9c60}{y})\, \nabla_\theta \textcolor{#1565c0}{\pi_\theta}(\textcolor{#1c9c60}{y})

(∇θ\nabla_\theta 의 ∇(나블라)는 파라미터 θ\theta 의 성분마다 미분한 값을 모은 그래디언트다.) 여기에 미분 공식 하나를 쓴다. ∇log⁡π=∇ππ\nabla \log \textcolor{#1565c0}{\pi} = \dfrac{\nabla \textcolor{#1565c0}{\pi}}{\textcolor{#1565c0}{\pi}} 이므로 ∇π=π⋅∇log⁡π\nabla \textcolor{#1565c0}{\pi} = \textcolor{#1565c0}{\pi} \cdot \nabla \log \textcolor{#1565c0}{\pi}. 이것이 로그 미분 트릭(log-derivative trick)이다. 답이 둘일 때 맞음이 뽑히면 곱한 1/0.31/0.3 이 바로 이 ∇log⁡π\nabla \log \textcolor{#1565c0}{\pi} 였다. 대입하면:

∇θJ=∑yπθ(y) R(y) ∇θlog⁡πθ(y)=Ey∼πθ[ R(y) ∇θlog⁡πθ(y) ]\nabla_\theta \textcolor{#d62728}{J} = \sum_{\textcolor{#1c9c60}{y}} \textcolor{#1565c0}{\pi_\theta}(\textcolor{#1c9c60}{y})\, \textcolor{#d9670b}{R}(\textcolor{#1c9c60}{y})\, \nabla_\theta \log \textcolor{#1565c0}{\pi_\theta}(\textcolor{#1c9c60}{y}) = \mathbb{E}_{\textcolor{#1c9c60}{y} \sim \textcolor{#1565c0}{\pi_\theta}}\big[\, \textcolor{#d9670b}{R}(\textcolor{#1c9c60}{y})\, \nabla_\theta \log \textcolor{#1565c0}{\pi_\theta}(\textcolor{#1c9c60}{y}) \,\big]
∇θJ평균 점수를 올리는 방향 (목적함수의 그래디언트)R(y)응답 y의 점수 — 미분하지 않는 가중치(곱하는 수 — 신경망 파라미터가 아니다)πθ(y)학습 중인 정책이 응답 y를 뽑을 확률∇θlog⁡πθ(y)로그확률의 그래디언트 (SFT가 매일 계산하는 양) \small\begin{array}{ll} \nabla_\theta \textcolor{#d62728}{J} & \text{평균 점수를 올리는 방향 (목적함수의 그래디언트)} \\ \textcolor{#d9670b}{R}(\textcolor{#1c9c60}{y}) & \text{응답 }\textcolor{#1c9c60}{y}\text{의 점수 — 미분하지 않는 가중치(곱하는 수 — 신경망 파라미터가 아니다)} \\ \textcolor{#1565c0}{\pi_\theta}(\textcolor{#1c9c60}{y}) & \text{학습 중인 정책이 응답 }\textcolor{#1c9c60}{y}\text{를 뽑을 확률} \\ \nabla_\theta \log \textcolor{#1565c0}{\pi_\theta}(\textcolor{#1c9c60}{y}) & \text{로그확률의 그래디언트 (SFT가 매일 계산하는 양)} \end{array}

이 식이 정책 그래디언트(policy gradient)다. 아무것도 덧붙이지 않은 이 형태를 흔히 VPG(Vanilla Policy Gradient)라 부른다. 식을 말로 읽으면:

“내가 뽑은 응답의 로그확률을 올려라. 단, 그 응답의 점수에 비례한 크기로.”

세 가지를 짚어두자.

  1. 보상은 미분되지 않는다. R(y)\textcolor{#d9670b}{R}(\textcolor{#1c9c60}{y})는 그래디언트 앞에 곱해지는 스칼라 가중치다. 채점기가 블랙박스여도 상관없다.
  2. 미분하는 것은 log⁡πθ(y)\log \textcolor{#1565c0}{\pi_\theta}(\textcolor{#1c9c60}{y}) 뿐이다. 이것은 SFT 손실과 똑같은 양이다. 이미 매일 역전파하고 있는 그것.
  3. 기대값은 πθ\textcolor{#1565c0}{\pi_\theta} 아래에서 취한다. 즉 응답은 지금의 모델이 뽑아야 한다. 남이 만든 응답으로 이 식을 쓰면 틀린 그래디언트가 나온다. 비싸게 모아 둔 남의 응답을 그대로 쓸 수 없다는 뜻이라, 실무에서 자주 발목을 잡는 조건이다.
응답을 토큰으로 쪼개면

응답의 확률은 토큰 확률의 곱(확률의 곱셈 규칙 — 미분의 연쇄 법칙과는 다르다)이므로, 로그를 씌우면 응답의 로그확률은 토큰 로그확률의 합이다.

log⁡πθ(y∣x)=∑t=1Tlog⁡πθ(yt∣x,y<t)\log \textcolor{#1565c0}{\pi_\theta}(\textcolor{#1c9c60}{y} \mid \textcolor{#0093b8}{x}) = \sum_{t=1}^{\textcolor{#915a08}{T}} \log \textcolor{#1565c0}{\pi_\theta}(\textcolor{#1c9c60}{y_t} \mid \textcolor{#0093b8}{x}, \textcolor{#1c9c60}{y_{<t}})
πθ(y∣x)응답 전체의 확률πθ(yt∣x,y<t)앞 토큰들이 주어졌을 때 t번째 토큰의 확률T응답 길이 (토큰 수) \small\begin{array}{ll} \textcolor{#1565c0}{\pi_\theta}(\textcolor{#1c9c60}{y} \mid \textcolor{#0093b8}{x}) & \text{응답 전체의 확률} \\ \textcolor{#1565c0}{\pi_\theta}(\textcolor{#1c9c60}{y_t} \mid \textcolor{#0093b8}{x}, \textcolor{#1c9c60}{y_{<t}}) & \text{앞 토큰들이 주어졌을 때 }t\text{번째 토큰의 확률} \\ \textcolor{#915a08}{T} & \text{응답 길이 (토큰 수)} \end{array}

따라서 그래디언트도 토큰별 항의 합이 된다.

∇θJ=E[∑t=1TΦt⏟빈칸⋅∇θlog⁡πθ(yt∣x,y<t)],VPG: Φt=R(x,y)\nabla_\theta \textcolor{#d62728}{J} = \mathbb{E}\Big[\sum_{t=1}^{\textcolor{#915a08}{T}} \underbrace{\textcolor{#8e44ad}{\Phi_t}}_{\text{빈칸}} \cdot \nabla_\theta \log \textcolor{#1565c0}{\pi_\theta}(\textcolor{#1c9c60}{y_t} \mid \textcolor{#0093b8}{x}, \textcolor{#1c9c60}{y_{<t}})\Big], \qquad \text{VPG: } \textcolor{#8e44ad}{\Phi_t} = \textcolor{#d9670b}{R}(\textcolor{#0093b8}{x}, \textcolor{#1c9c60}{y})
Φt토큰 t의 로그확률에 곱하는 가중치 (이 책의 빈칸. Φ는 대문자 파이)R(x,y)응답 전체의 점수 — VPG는 모든 토큰에 똑같이 곱한다J목적함수πθ학습 중인 정책 \small\begin{array}{ll} \textcolor{#8e44ad}{\Phi_t} & \text{토큰 }t\text{의 로그확률에 곱하는 가중치 (이 책의 빈칸. }\Phi\text{는 대문자 파이)} \\ \textcolor{#d9670b}{R}(\textcolor{#0093b8}{x}, \textcolor{#1c9c60}{y}) & \text{응답 전체의 점수 — VPG는 모든 토큰에 똑같이 곱한다} \\ \textcolor{#d62728}{J} & \text{목적함수} \\ \textcolor{#1565c0}{\pi_\theta} & \text{학습 중인 정책} \end{array}

일부러 보상 자리를 Φt\textcolor{#8e44ad}{\Phi_t}라는 빈칸으로 적었다. 위의 유도에서는 Φt=R\textcolor{#8e44ad}{\Phi_t} = \textcolor{#d9670b}{R} — 응답 전체의 점수가 모든 토큰에 똑같이 곱해진다. 그런데 이 빈칸에 다른 것을 넣어도 기대값이 여전히 올바른 그래디언트가 되는 경우들이 있다(2016년 슐먼(John Schulman)과 동료들의 논문이 이런 경우 여섯 가지를 한 식에 모아 정리했다).

SFT는 특수한 정책 그래디언트다

정책 그래디언트 식에서 두 가지를 바꾸면 SFT가 된다: 응답을 모델이 뽑는 대신 데이터셋의 정답을 쓰고, 점수를 항상 1로 둔다.

∇θLSFT∝−∇θlog⁡πθ(y∗)⟷∇θJ≈R(y) ∇θlog⁡πθ(y),    y∼πθ\nabla_\theta \textcolor{#d62728}{\mathcal{L}_\text{SFT}} \propto -\nabla_\theta \log \textcolor{#1565c0}{\pi_\theta}(\textcolor{#1c9c60}{y^*}) \quad\longleftrightarrow\quad \nabla_\theta \textcolor{#d62728}{J} \approx \textcolor{#d9670b}{R}(\textcolor{#1c9c60}{y})\,\nabla_\theta \log \textcolor{#1565c0}{\pi_\theta}(\textcolor{#1c9c60}{y}), \;\; \textcolor{#1c9c60}{y} \sim \textcolor{#1565c0}{\pi_\theta}
LSFTSFT 손실 (정답의 음의 로그확률)y∗데이터셋의 정답J정책 그래디언트의 목적함수R(y)모델이 뽑은 응답 y의 점수y∼πθ지금의 모델이 뽑은 응답 \small\begin{array}{ll} \textcolor{#d62728}{\mathcal{L}_\text{SFT}} & \text{SFT 손실 (정답의 음의 로그확률)} \\ \textcolor{#1c9c60}{y^*} & \text{데이터셋의 정답} \\ \textcolor{#d62728}{J} & \text{정책 그래디언트의 목적함수} \\ \textcolor{#d9670b}{R}(\textcolor{#1c9c60}{y}) & \text{모델이 뽑은 응답 }\textcolor{#1c9c60}{y}\text{의 점수} \\ \textcolor{#1c9c60}{y} \sim \textcolor{#1565c0}{\pi_\theta} & \text{지금의 모델이 뽑은 응답} \end{array}

거꾸로 말하면 정책 그래디언트는 **“내가 만든 답으로, 점수에 비례해 하는 SFT”**다. 점수가 음수면? 로그확률을 내린다. 흔히 "나쁜 예제를 추가하면 SFT가 RL이 된다"고 하는데, 그 말의 알맹이가 바로 이 부호다. 그리고 "남이 만든 답"과 "내가 만든 답"의 차이는 KL 발산의 두 방향, 순방향 KL(forward)과 역방향 KL(reverse)의 차이이기도 하다 — 기대값을 데이터 위에서 취하면 모델이 스스로 만드는 실수는 손실에 나타나지 않고, 내 샘플 위에서 취하면 바로 그 실수가 가중치를 받는다.

ML에서: 빈칸을 채우는 알고리즘들

언어모델을 점수로 학습시키는 실제 알고리즘에서 움직이는 것은 언제나 언어모델의 파라미터 θ\theta 그 자체다. 알고리즘마다 달라지는 것은 로그확률 앞에 곱하는 빈칸 Φt\textcolor{#8e44ad}{\Phi_t} 뿐이다. 정책 그래디언트 계열 알고리즘의 발전은 사실상 이 빈칸을 채우는 방법의 역사다.

알고리즘 빈칸 Φt\textcolor{#8e44ad}{\Phi_t} 무엇을 개선했나
VPG R\textcolor{#d9670b}{R} 출발점 — 정확하지만 기대값을 계산할 수 없음
REINFORCE R−b\textcolor{#d9670b}{R} - \textcolor{#00897b}{b} (샘플로 추정) 기대값을 샘플 평균으로 대체, 베이스라인(점수에서 빼 주는 기준값)으로 분산 감소
Actor-Critic A^t\textcolor{#8e44ad}{\hat A_t} (어드밴티지: 그 토큰이 평소보다 얼마나 나았나. 비평가 — 앞으로 받을 점수를 예측하는 보조 모델 — 가 추정) 토큰마다 다른 가중치, 분산 더 감소
PPO 클리핑된 (확률 비율 × A^t\textcolor{#8e44ad}{\hat A_t}). 확률 비율 = 지금 정책의 확률 ÷ 샘플을 뽑은 옛 정책의 확률, 클리핑 = 정해진 폭을 넘지 않게 자르기 같은 샘플을 여러 번 재사용하되 멀리 가지 않게
GRPO (Ri−μG)/σG(\textcolor{#d9670b}{R_i} - \textcolor{#00897b}{\mu_G})/\textcolor{#008deb}{\sigma_G} 비평가 대신 그룹(같은 프롬프트에 뽑은 응답 여러 개)의 점수 평균 μG\textcolor{#00897b}{\mu_G}(μ는 뮤) 와 표준편차 σG\textcolor{#008deb}{\sigma_G}
변종들 표준편차로 나누기를 뺌, 비대칭 클리핑(위아래로 자르는 폭을 다르게), 시퀀스 단위 비율(토큰마다가 아니라 응답 전체로 잰 확률 비율)… GRPO의 길이 편향과 불안정 수정
문제 3 — 틀린 답에 −1점을 주면

「답이 둘일 때」의 예에서 채점 규칙만 바꾼다. 맞음은 그대로 1점, 틀림은 이제 −1점이다. 모델은 여전히 맞음을 확률 p=0.3p = 0.3, 틀림을 1−p=0.71 - p = 0.7 로 고른다. (가) 틀림이 뽑혔을 때, 점수 × (틀림의 로그확률을 pp 로 미분한 값)은 얼마인가? 이 샘플은 pp 를 올리는가, 내리는가? (나) 맞음이 뽑힐 때와 틀림이 뽑힐 때의 값을 뽑힐 확률로 평균 내면 얼마인가? 평균 점수 J\textcolor{#d62728}{J} 를 pp 로 직접 미분한 값과 견주어라.

선생님 (평상)
선생님
민준 학생, 틀림이 뽑혔어요. 이 샘플은 p를 어느 쪽으로 밀까요?
김민준 (자신만만)
김민준
점수가 −1이니까 뽑힌 답을 벌주는 거죠. 음수를 곱하니까 p가 내려가요.
이서연 (평상)
이서연
잠깐, 여기서 미분하는 건 틀림의 로그확률이야. 틀림의 확률은 1 − p 라서 log(1 − p)를 p로 미분하면 −1/(1 − p), 곧 −1/0.7이야.
김민준 (놀람)
김민준
아, 음수에 음수를 곱하는 거구나. (−1) × (−1/0.7) ≈ +1.43. 틀림이 뽑혔는데 p가 올라가네요?
선생님 (질문)
선생님
틀림의 확률을 내리는 것과 맞음의 확률 p를 올리는 것은 다른 일일까요?
이서연 (평상)
이서연
답이 둘뿐이라 같은 일이에요. 틀림이 준 만큼 맞음이 늘어요. 틀림이 0점이던 때는 틀림이 뽑히면 아무 일도 없었는데, −1점이 되니까 틀림이 뽑혀도 신호가 생기네요.
선생님 (평상)
선생님
그럼 (나). 두 경우를 평균 내 볼까요?
김민준 (의심)
김민준
맞음은 그대로 1/0.3 ≈ 3.33이니까 0.3 × 3.33 + 0.7 × 1.43 = 1 + 1 = 2. 어, 틀림이 0점일 때는 1이었는데 두 배가 됐어요. 제가 뭘 잘못 셌나요?
이서연 (평상)
이서연
직접 미분해 보자. 평균 점수가 p × 1 + (1 − p) × (−1) = 2p − 1 이니까 미분하면 2야. 맞게 센 거야.
김민준 (깨달음)
김민준
p를 올리면 맞음에서 1점을 얻고, 틀림에서 1점을 덜 잃으니까 두 배구나. 조교님이 오답에 감점까지 하면 대충 찍기보다 제대로 푸는 게 곱절로 남는 거랑 같네요.

정리 (가) (−1)×ddplog⁡(1−p)=10.7≈1.43(-1) \times \frac{d}{dp}\log(1-p) = \frac{1}{0.7} \approx 1.43. 틀림이 뽑힌 샘플도 pp 를 올린다. 답이 둘뿐이라 틀림의 확률을 내리는 것이 곧 맞음의 확률을 올리는 것이다. (나) 0.3×3.33+0.7×1.43=20.3 \times 3.33 + 0.7 \times 1.43 = 2 이고, J=2p−1\textcolor{#d62728}{J} = 2p - 1 을 직접 미분한 값과 같다.