16. RLVR과 GRPO — 비평가를 내려놓다

GRPO 목적함수: 그룹 어드밴티지를 PPO의 클리핑에 끼운다

어드밴티지는 구했다. 이제 이 값으로 정책을 움직여야 한다. 그런데 생성은 비싸다. DeepSeekMath는 질문 하나에 답을 64개씩, 답마다 최대 1,024토큰까지 뽑았다. 한 번 뽑은 답으로 여러 번 업데이트하고 싶어진다. 그러다 정책이 뽑을 때의 정책에서 멀리 벗어나면 어떻게 막을까? 레퍼런스(학습 전 모델을 얼려 둔 기준)에서 너무 멀어지는 것은 또 어떻게 막을까?

DeepSeekMath 논문의 목적함수는 두 물음 모두에 RLHF-PPO의 답을 빌린다. 멀리 벗어나는 것은 PPO의 클리핑으로(확률 비율이 1 ± ε 밖으로 나가면 그래디언트를 끊는다), 레퍼런스에서 멀어지는 것은 레퍼런스와의 KL 항으로 막는다. 이 절에서는 이 논문의 표기를 따라 프롬프트를 qq, ii번째 응답을 oio_i로 적는다(앞 절에서 xx, yiy_i로 적던 것과 같은 것이다. qq는 질문 question, oo는 출력 output의 머리글자다). 확률 비율은 토큰마다 잰다.

ri,t(θ)=πθ(oi,t∣q,oi,<t)πold(oi,t∣q,oi,<t)\textcolor{#c2185b}{r_{i,t}}(\theta) = \frac{\textcolor{#1565c0}{\pi_\theta}(\textcolor{#1c9c60}{o_{i,t}} \mid \textcolor{#0093b8}{q}, \textcolor{#1c9c60}{o_{i,<t}})}{\textcolor{#6f6f78}{\pi_\text{old}}(\textcolor{#1c9c60}{o_{i,t}} \mid \textcolor{#0093b8}{q}, \textcolor{#1c9c60}{o_{i,<t}})}
ri,t확률 비율 (응답 i, 토큰 t)πθ학습 중인 정책πold답을 뽑을 때의 정책 (업데이트하는 동안 고정)q프롬프트 (질문)oi,t, oi,<ti번째 응답의 t번째 토큰과 그 앞 토큰들 \small\begin{array}{ll} \textcolor{#c2185b}{r_{i,t}} & \text{확률 비율 (응답 i, 토큰 t)} \\ \textcolor{#1565c0}{\pi_\theta} & \text{학습 중인 정책} \\ \textcolor{#6f6f78}{\pi_\text{old}} & \text{답을 뽑을 때의 정책 (업데이트하는 동안 고정)} \\ \textcolor{#0093b8}{q} & \text{프롬프트 (질문)} \\ \textcolor{#1c9c60}{o_{i,t}},\ \textcolor{#1c9c60}{o_{i,<t}} & \text{i번째 응답의 t번째 토큰과 그 앞 토큰들} \end{array}
JGRPO(θ)=E[1G∑i=1G1∣oi∣∑t=1∣oi∣{min⁡(ri,tA^i,t,  clip(ri,t,1−ε,1+ε) A^i,t)−β DKL[πθ ∥ πref]}]\textcolor{#d62728}{J_\text{GRPO}}(\theta) = \mathbb{E}\left[\frac{1}{\textcolor{#5f970c}{G}}\sum_{i=1}^{\textcolor{#5f970c}{G}}\frac{1}{\textcolor{#915a08}{|o_i|}}\sum_{t=1}^{\textcolor{#915a08}{|o_i|}}\Big\{\min\big(\textcolor{#c2185b}{r_{i,t}}\textcolor{#8e44ad}{\hat A_{i,t}},\; \text{clip}(\textcolor{#c2185b}{r_{i,t}}, 1-\textcolor{#1f6d7a}{\varepsilon}, 1+\textcolor{#1f6d7a}{\varepsilon})\,\textcolor{#8e44ad}{\hat A_{i,t}}\big) - \textcolor{#827717}{\beta}\, \textcolor{#8c564b}{\mathbb{D}_\text{KL}}\big[\textcolor{#1565c0}{\pi_\theta} \,\|\, \textcolor{#6f6f78}{\pi_\text{ref}}\big]\Big\}\right]
JGRPOGRPO 목적함수 (최대화)ri,t확률 비율 πθ/πold (응답 i, 토큰 t)A^i,t어드밴티지 (응답 i의 모든 토큰에 같은 값)ε클리핑 폭βKL 강도 (DeepSeekMath: 0.04)DKLKL 발산 (손실 안에 직접, k3 추정)πθ학습 중인 정책πref레퍼런스 정책G, oi, ∣oi∣그룹 크기, i번째 응답, 그 토큰 수 \small\begin{array}{ll} \textcolor{#d62728}{J_\text{GRPO}} & \text{GRPO 목적함수 (최대화)} \\ \textcolor{#c2185b}{r_{i,t}} & \text{확률 비율 } \pi_\theta / \pi_\text{old} \text{ (응답 i, 토큰 t)} \\ \textcolor{#8e44ad}{\hat A_{i,t}} & \text{어드밴티지 (응답 i의 모든 토큰에 같은 값)} \\ \textcolor{#1f6d7a}{\varepsilon} & \text{클리핑 폭} \\ \textcolor{#827717}{\beta} & \text{KL 강도 (DeepSeekMath: 0.04)} \\ \textcolor{#8c564b}{\mathbb{D}_\text{KL}} & \text{KL 발산 (손실 안에 직접, k3 추정)} \\ \textcolor{#1565c0}{\pi_\theta} & \text{학습 중인 정책} \\ \textcolor{#6f6f78}{\pi_\text{ref}} & \text{레퍼런스 정책} \\ \textcolor{#5f970c}{G},\ \textcolor{#1c9c60}{o_i},\ \textcolor{#915a08}{|o_i|} & \text{그룹 크기, i번째 응답, 그 토큰 수} \end{array}

KL 항 앞의 β\textcolor{#827717}{\beta}는 레퍼런스 쪽으로 당기는 목줄의 강도다. RLHF-PPO와 달라진 곳 세 군데:

부품 RLHF-PPO GRPO
어드밴티지 A^i,t\textcolor{#8e44ad}{\hat A_{i,t}} 비평가 + GAE(가까운 미래는 실제 결과, 먼 미래는 비평가 예측으로 섞어 어드밴티지를 구하는 방법), 토큰마다 다름 (Ri−μG)/σG(\textcolor{#d9670b}{R_i} - \textcolor{#00897b}{\mu_G})/\textcolor{#008deb}{\sigma_G}, 응답 안에서 동일
KL 항의 위치 보상에 섞음 (R−β⋅KL\textcolor{#d9670b}{R} - \textcolor{#827717}{\beta} \cdot \textcolor{#8c564b}{\text{KL}}) 손실에 직접 더함
KL 추정 방식 로그비율 πrefπθ−log⁡πrefπθ−1\frac{\textcolor{#6f6f78}{\pi_\text{ref}}}{\textcolor{#1565c0}{\pi_\theta}} - \log\frac{\textcolor{#6f6f78}{\pi_\text{ref}}}{\textcolor{#1565c0}{\pi_\theta}} - 1 (흔히 "k3"라 부르는 항상 0 이상인 추정량)

KL을 보상에서 빼 손실로 옮긴 까닭을 논문은 이렇게 적었다: 보상에 섞으면 어드밴티지 계산이 복잡해진다. 그룹 평균을 빼고 표준편차로 나누는 깔끔한 계산을 지키려고 KL을 따로 뗀 것이다.

k3: 늘 0 이상인 KL 어림

KL 발산은 두 분포 전체에 대한 기대값이라 정확히 셀 수 없다. 실제로는 뽑힌 토큰에서 잰 값 하나로 어림한다. 가장 쉬운 어림은 로그비율 log⁡(πθ/πref)\log(\textcolor{#1565c0}{\pi_\theta}/\textcolor{#6f6f78}{\pi_\text{ref}})이다. 평균을 내면 KL과 같지만, 토큰 하나에서는 음수가 될 수 있다. KL은 0 이상인데 어림이 음수면, 손실의 KL 항이 거꾸로 레퍼런스에서 멀어지라고 미는 토큰이 생긴다.

토큰 둘로 보자. πθ=0.5\textcolor{#1565c0}{\pi_\theta} = 0.5, πref=0.4\textcolor{#6f6f78}{\pi_\text{ref}} = 0.4인 토큰의 로그비율은 log⁡(0.5/0.4)=0.223\log(0.5/0.4) = 0.223이다. πθ=0.2\textcolor{#1565c0}{\pi_\theta} = 0.2, πref=0.3\textcolor{#6f6f78}{\pi_\text{ref}} = 0.3인 토큰은 log⁡(0.2/0.3)=−0.405\log(0.2/0.3) = -0.405로 음수다. 비율 πref/πθ\textcolor{#6f6f78}{\pi_\text{ref}}/\textcolor{#1565c0}{\pi_\theta}에서 1을 뺀 값은 학습 정책이 뽑은 토큰들에 대해 평균이 정확히 0이다(비율의 평균이 ∑πθ⋅πref/πθ=∑πref=1\sum \textcolor{#1565c0}{\pi_\theta} \cdot \textcolor{#6f6f78}{\pi_\text{ref}}/\textcolor{#1565c0}{\pi_\theta} = \sum \textcolor{#6f6f78}{\pi_\text{ref}} = 1이므로). 그러니 이 값을 로그비율에 더해도 평균은 그대로 KL이다. 더하고 나면 토큰마다 늘 0 이상이 된다.

D^KL=πrefπθ−log⁡πrefπθ−1(뽑힌 토큰 하나에서)\textcolor{#8c564b}{\hat{\mathbb{D}}_\text{KL}} = \frac{\textcolor{#6f6f78}{\pi_\text{ref}}}{\textcolor{#1565c0}{\pi_\theta}} - \log\frac{\textcolor{#6f6f78}{\pi_\text{ref}}}{\textcolor{#1565c0}{\pi_\theta}} - 1 \qquad (\text{뽑힌 토큰 하나에서})
D^KL토큰 하나에서 잰 KL 어림 (k3)πref레퍼런스가 그 토큰에 준 확률πθ학습 중인 정책이 그 토큰에 준 확률 \small\begin{array}{ll} \textcolor{#8c564b}{\hat{\mathbb{D}}_\text{KL}} & \text{토큰 하나에서 잰 KL 어림 (k3)} \\ \textcolor{#6f6f78}{\pi_\text{ref}} & \text{레퍼런스가 그 토큰에 준 확률} \\ \textcolor{#1565c0}{\pi_\theta} & \text{학습 중인 정책이 그 토큰에 준 확률} \end{array}

앞의 두 토큰은 각각 0.8−1+0.223=0.0230.8 - 1 + 0.223 = 0.023, 1.5−1−0.405=0.0951.5 - 1 - 0.405 = 0.095로 둘 다 0 이상이다. "k3"라는 이름은 존 슐먼(John Schulman)이 2020년 블로그 글에서 KL 어림 셋을 k1, k2, k3로 이름 붙인 데서 왔다. DeepSeekMath는 이 어림을 인용해 KL 항에 썼다.

1 0 −1 −0.405 0.095 k3 (늘 0 이상) 로그비율 (음수가 된다) 비율 πref / πθ (뽑힌 토큰 하나에서) 0.5 1 1.5 2 2.5 3

위젯의 단추는 앞 절 문제 2의 그룹(여덟 가운데 하나만 정답)과 아래 문제 4의 그룹을 불러온다. 문제 4는 보상이 0, 0.5, 1이 아닌 0.9라 원을 눌러서는 만들 수 없다. 풀이를 마친 뒤 두 그룹의 막대와 수치판을 견주어 보라.

ML에서: 논문의 설정과 두 개의 나누기

논문의 설정은 β=0.04\textcolor{#827717}{\beta} = 0.04, 프롬프트당 G=64\textcolor{#5f970c}{G} = 64개 샘플, 학습 배치 1,024였다. 그리고 논문은 뽑은 답마다 업데이트를 한 번만 했다고 적었다. 업데이트가 한 번이면 그 순간 πθ=πold\textcolor{#1565c0}{\pi_\theta} = \textcolor{#6f6f78}{\pi_\text{old}}라 비율이 1이고, 클리핑은 일을 하지 않는다. 클리핑은 같은 답으로 여러 번 업데이트하는 구현을 위한 안전장치로 식에 남아 있다.

식에는 두 개의 “나누기” — 응답 길이 1/∣oi∣1/\textcolor{#915a08}{|o_i|}와 표준편차 1/σG1/\textcolor{#008deb}{\sigma_G} — 도 보인다. 무해해 보이는 이 두 나누기는 GRPO를 대규모로 돌린 연구들이 가장 먼저 의심한 부품이다. 아래 문제에서 표준편차 나누기가 무엇을 하는지 따져 본다.

문제 3 — 다들 만점인 과제의 1점

조교가 과제 점수를 표준점수(평균을 빼고 표준편차로 나눈 값)로 바꿔 성적에 넣는다. 수강생은 여덟 명이다. 과제 A는 어려워서 한 명만 10점이고 일곱 명은 0점이다. 과제 B는 쉬워서 일곱 명이 10점이고 한 명만 9점이다. (가) 과제 A의 0점과 과제 B의 9점은 각각 표준점수로 얼마인가? (나) 어느 쪽이 성적에서 더 크게 깎이는가?

이서연 (평상)
이서연
당연히 과제 A의 0점이 더 깎이지. 10점 만점에 0점이면 평균에서 한참 떨어졌잖아.
김민준 (평상)
김민준
계산해 볼게. 과제 A는 평균 1.25, 표준편차 3.31이라 0점은 (0 − 1.25)/3.31 = −0.38. 과제 B는 평균 9.875, 표준편차 0.331이라 9점은 (9 − 9.875)/0.331 = −2.65.
이서연 (난처함)
이서연
9점이 0점보다 일곱 배 가까이 더 깎여? 한 문제 실수한 사람이 과제를 아예 못 한 사람보다 더 혼나네.
선생님 (짚어주기)
선생님
9점과 10점의 차이 1점은 과제 B에서 표준편차 몇 개만큼이에요?
이서연 (평상)
이서연
표준편차가 0.33이니까 1점이 표준편차 세 개쯤이에요. 나누는 수가 작으면 작은 차이가 크게 불어나요.
김민준 (평상)
김민준
다 같이 잘한 과제인데, 0.33으로 나누는 순간 1점이 성적을 가르는 거네요.

정리 (가) 과제 A의 0점은 −0.38, 과제 B의 9점은 −2.65. (나) 과제 B의 9점이 일곱 배 가까이 더 깎인다. 표준편차가 작은 과제에서는 1점 차이가 표준편차 세 개쯤으로 부풀려진다.

문제 4 — (킬러) 0.1점 차이의 무게

보상 모델로 부분 점수를 주는 GRPO에서, 한 그룹의 보상이 [1,1,1,1,1,1,1,0.9][1, 1, 1, 1, 1, 1, 1, 0.9]로 나왔다. (가) 각 답의 A^i\textcolor{#8e44ad}{\hat A_i}를 구하시오. (나) 문제 2의 그룹(여덟 가운데 하나만 정답이라 보상 1, 나머지 일곱은 0)과 비교하시오. (다) 모두 정답인 그룹에서 보상 모델이 ±0.01\pm 0.01 정도의 잡음을 낸다면 어떤 일이 벌어지는가?

김민준 (평상)
김민준
μ = 7.9/8 = 0.9875. 편차가 일곱 개는 +0.0125, 하나는 −0.0875. σ = 0.0331. 그러면 1점짜리들은 +0.38, 0.9점짜리는 −2.65예요.
선생님 (평상)
선생님
문제 2의 그룹이랑 비교해 볼까요?
이서연 (놀람)
이서연
부호만 바뀌고 숫자가 똑같아. 문제 2는 정답 하나가 +2.65, 여기는 0.9점 하나가 −2.65.
김민준 (자신만만)
김민준
아까 과제 B랑 같은 계산이네요. 그래도 과제는 사람 성적이라 억울한 거고, 모델한테는 0.1점이라도 더 좋은 쪽으로 미는 거니까 손해는 아니지 않아요?
선생님 (질문)
선생님
민준 학생, 문제 2에서 +2.65를 받은 답은 여덟 중 혼자 문제를 푼 답이었어요. 여기서 −2.65를 받은 답은요?
김민준 (생각)
김민준
여기선… 다 맞혔는데 조금 덜 예쁘게 쓴 답이요. 그게 "혼자 못 푼 답"이랑 같은 크기로 눌리는 거네요. 혼자 풀어낸 일만큼 큰 신호를 0.1점 차이에 쓰는 셈이니 과하네요.
이서연 (평상)
이서연
이유는 알겠어. 보상에 1차 변환 ar + b를 해도 (r − μ)/σ는 안 변하잖아. 문제 2의 보상을 0.1배 하고 0.9 더한 게 이 그룹이니까 어드밴티지가 같을 수밖에 없어. 그러니까 σ가 0에 가까워지는 게 문제고, 분모에 아주 작은 수만 더하면 해결돼.
선생님 (질문)
선생님
서연 학생, 10⁻⁶을 더하면 여기서 σ는 얼마가 되죠?
이서연 (평상)
이서연
0.0331 + 0.000001… 거의 그대로네요. 아, 그 작은 수는 σ가 정확히 0일 때 0으로 나누는 것만 막아주는 거지, 작은 σ를 크게 만드는 건 아니구나.
이서연 (깨달음)
이서연
문제는 수치 안정성이 아니라 스케일 불변성이에요. σ로 나누면 차이가 얼마나 작든 항상 "표준 크기"로 부풀려져요.
선생님 (평상)
선생님
그럼 (다)는요?
김민준 (평상)
김민준
모두 맞힌 그룹인데 보상 모델이 0.99, 1.01 이런 잡음을 내면… 그 잡음이 전부 ±1 크기 어드밴티지가 돼요. 원래 배울 게 없어야 할 그룹에서 잡음을 열심히 배우는 거네요.
선생님 (평상)
선생님
그래요. 이 문제를 정면으로 지적한 게 류(Zichen Liu)와 동료들이 2025년에 낸 Dr. GRPO예요. 'GRPO를 제대로 고쳤다(Done Right)'는 뜻으로 붙인 이름인데, 표준편차로 나누기를 빼 버리자고 하죠.
이서연 (평상)
이서연
통계 수업에서 표준화할 때 분산이 거의 0인 변수는 빼라고 했던 게 이거였구나. 표준화하면 잡음만 남은 변수가 제일 크게 보이니까.

정리 (가) 1점 답 +0.38+0.38, 0.9점 답 −2.65-2.65. (나) 문제 2 그룹의 보상을 1차 변환한 것이라 어드밴티지 크기가 똑같다 — (r−μ)/σ(\textcolor{#d9670b}{r}-\textcolor{#00897b}{\mu})/\textcolor{#008deb}{\sigma}는 스케일에 불변이다. 혼자 문제를 푼 답과 같은 크기의 신호가 0.1점 차이에 쓰인다. (다) 모두 정답인 그룹의 작은 잡음이 ±1\pm 1 크기의 어드밴티지로 부풀려진다. 분모에 더하는 아주 작은 수는 0으로 나누기만 막을 뿐이다. 이 문제를 지적하며 표준편차 나누기를 뺀 것이 Dr. GRPO다.