Part IV: 비평가 없이 — RLVR과 GRPO

— DPO는 PPO의 무게를 “온라인 학습을 포기하는” 쪽으로 덜어냈다. 이 파트는 반대쪽 길이다: 온라인 학습은 지키고, 비평가와 보상 모델을 내려놓는다.


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

의문

RLHF-PPO(사람 선호로 학습한 보상 모델의 점수를 PPO로 끌어올리는 방식)는 모델 네 개를 동시에 굴렸다: 학습하는 정책(답을 내는 언어모델 자체), 레퍼런스(학습 전 모델을 얼려 둔 비교 기준), 보상 모델, 그리고 비평가(가치 모델 — 상황마다 앞으로 받을 보상을 예측하는 신경망). 정책이 70억 파라미터면 보상 모델과 비평가도 보통 그와 맞먹는 크기라, 정책 하나를 다듬으려고 큰 모델 넷을 메모리에 올린다. DPO는 이 중 보상 모델과 비평가를 수식 변환으로 지웠지만, 대가로 온라인 학습을 잃었다 — 고정된 데이터셋으로만 배우고, 탐색하지 못하고, 데이터가 낡는다. JoyCaption(이미지를 보고 설명글을 쓰는 공개 모델) 같은 실전 사례는 그 대가를 사람 손으로 메우는 과정이었다.

그렇다면 반대로 물을 수 있다:

온라인·온폴리시(지금 학습 중인 정책이 방금 뽑은 답으로 배우는) 학습은 그대로 두고, 무거운 두 모델만 뺄 수는 없을까?
보상 모델 대신 — 정답이 있는 문제라면 채점 규칙 하나로.
비평가 대신 — 같은 문제에 여러 번 답해 보고, 그 평균으로.

두 생각은 따로 나왔다. 비평가를 평균으로 바꾸는 방법은 2024년 2월 DeepSeek의 수학 모델 DeepSeekMath 논문에서 GRPO(Group Relative Policy Optimization, 그룹 상대 정책 최적화)라는 이름으로 나왔다. 채점을 규칙에 맡기는 방식은 같은 해 11월 앨런 AI 연구소의 툴루 3(Tülu 3) 논문에서 RLVR(Reinforcement Learning with Verifiable Rewards, 검증 가능한 보상으로 하는 강화학습)이라는 이름을 얻었다. 2025년 1월 DeepSeek-R1이 둘을 합쳐 추론 모델의 판도를 바꿨다.

그룹 베이스라인: 여러 답의 평균이 비평가를 대신한다

무거운 두 모델만 뺄 수 있을까? 답하려면 먼저 네 모델이 각각 무슨 일을 하고 있었는지, DPO는 그중 무엇을 어떻게 지웠는지 나란히 적어 두어야 한다. 아래 표의 아래첨자 ψ(프사이)와 φ(파이, phi)는 보상 모델과 비평가의 파라미터를 가리키는 그리스 문자다.

모델 역할 (RLHF-PPO) DPO
정책 πθ\pi_\theta 학습 대상, 응답 생성 유지
레퍼런스 πref\pi_\text{ref} KL로 학습 전 모델 쪽에 당기는 목줄 유지
보상 모델 rψr_\psi 응답 채점 — 사람 선호로 따로 학습 수식으로 소거
비평가 VϕV_\phi 베이스라인·어드밴티지 추정 수식으로 소거
온라인 생성 매 스텝 새 응답 포기 (고정 데이터)

이 장은 먼저 비평가 쪽을, 그다음 보상 쪽을 본다.

비평가가 하던 일은 하나였다. 정책 그래디언트는 답마다 받은 점수로 그 답의 확률을 밀어 올리거나 내린다. 점수를 그대로 쓰면 0점이 아닌 답은 모두 밀려 올라가고 흔들림도 크다. 그래서 점수에서 「이 상황이면 보통 이 정도는 받는다」는 기준값, 곧 베이스라인 bb를 빼고 쓴다. 기대값은 그대로 두고 분산만 줄이는 셈이다. 가장 자연스러운 베이스라인은 “이 상황에서 기대되는 보상”, 즉 가치 함수 VV이고, Actor-Critic의 비평가는 그것을 신경망으로 학습해서 추정했다.

언어모델에서는 이 추정을 프롬프트마다, 그것도 토큰마다 해야 한다. 그런데 보상은 응답이 끝난 뒤 마지막 토큰에 한 번 들어오고, 비평가는 중간 토큰마다 「여기서부터 받을 점수」를 맞혀야 한다. 정책만 한 신경망을 하나 더 학습시키면서도 그 값이 잘 맞는다는 보장이 없다. 신경망 없이 같은 값을 얻을 방법은 없을까?

역사: 비평가를 학습하지 않고 기준을 잡으려던 시도들

먼저 이 고민에 부딪힌 것은 사진 설명문(캡션)을 쓰는 모델이었다. 2016년 말 IBM 연구소의 레니(Steven Rennie)와 동료들은 캡션 모델을 CIDEr 점수(사람이 쓴 설명들과 낱말 묶음이 얼마나 겹치는지 재는 점수)로 직접 끌어올리려고 REINFORCE를 썼다. 베이스라인이 필요했지만, Actor-Critic처럼 비평가 신경망을 따로 학습시키는 수고는 피하고 싶었다. 그들이 고른 기준은 같은 사진에 대해 모델이 가장 그럴듯하다고 고른 설명 하나의 점수였다. 뽑은 설명이 그보다 나으면 밀어 올리고 못하면 내린다. 모델이 자기 답으로 자기를 평가한다고 해서 이름이 「스스로 비평하는 학습」(self-critical sequence training, SCST)이다. 이 방법으로 MSCOCO 캡션 과제의 최고 기록을 CIDEr 104.9에서 114.7로 올렸다(CVPR 2017). 같은 프롬프트에서 답을 여럿 뽑아 자기를 뺀 나머지의 평균을 기준으로 쓰는 RLOO도 같은 흐름에 있다.

2024년 2월 DeepSeek 연구진의 샤오(Zhihong Shao)와 동료들은 수학 모델 DeepSeekMath를 강화학습으로 다듬으며 같은 벽을 언어모델에서 만났다. 논문은 비평가를 뺄 까닭을 둘 적었다. 비평가는 보통 정책과 맞먹는 크기라 메모리와 계산이 크게 들고, 보상이 마지막 토큰에만 붙는 언어모델에서는 토큰마다 정확한 가치를 학습시키기가 어렵다. 그래서 같은 질문에 답을 여러 개 뽑아, 그 점수들의 평균을 베이스라인으로 썼다. 지시 따르기까지 학습한 70억 파라미터 모델에 이 방법을 돌리자 초등 수학 서술형 문제 모음 GSM8K 정답률이 82.9%에서 88.2%로, 경시대회 수학 모음 MATH가 46.8%에서 51.7%로 올랐다.

같은 문제에 여러 번 답하기

비평가가 맞히려던 값은 「이 프롬프트에서 지금 정책이 받을 평균 점수」다. 평균이라면 직접 재 볼 수 있다. 같은 프롬프트에 여러 번 답하게 하고 점수를 평균 내면 된다. 예로 고른 문제는 「1부터 100까지의 합은?」이다. 답이 하나로 정해져 규칙으로 채점할 수 있고, 하나를 빠뜨린 4950 같은 흔한 오답도 있다. 현재 정책이 여덟 번 답했다.

답 내용 채점 점수 평균을 뺀 값 표준편차로 나눈 값
1 “5050입니다. 가우스 공식으로…” 정답 1 +0.25 +0.577
2 “5050. 1+100=101, 101×50=5050” 정답 1 +0.25 +0.577
3 “답은 5050이에요” 정답 1 +0.25 +0.577
4 “4950입니다. 1+2+…+99=4950” 오답 0 −0.75 −1.732
5 “5050. 등차수열의 합 S=n(n+1)/2S = n(n+1)/2…” 정답 1 +0.25 +0.577
6 “100×101÷2 = 5050” 정답 1 +0.25 +0.577
7 “5500입니다” 오답 0 −0.75 −1.732
8 “계산하면 5050이 됩니다…” 정답 1 +0.25 +0.577

여덟 답 가운데 여섯이 맞아 평균은 6/8 = 0.75다. 이 0.75가 비평가가 신경망으로 맞히려던 값의 어림이다. 각 답의 점수에서 평균을 빼면 정답은 +0.25, 오답은 −0.75가 된다. 평균보다 나은 답은 밀어 올리고, 못한 답은 밀어 내린다는 뜻이다.

점수 1 점수 0 평균 0.75 +0.25 +0.25 +0.25 −0.75 +0.25 +0.25 −0.75 +0.25 답 1 답 2 답 3 답 4 답 5 답 6 답 7 답 8

GRPO는 여기에 한 단계를 더해, 빼고 난 값을 여덟 점수의 표준편차 0.75×0.25=0.433\sqrt{0.75 \times 0.25} = 0.433으로 나눈다. 정답은 0.25/0.433=+0.5770.25/0.433 = +0.577, 오답은 −0.75/0.433=−1.732-0.75/0.433 = -1.732를 받는다. (여기서는 8로 나누는 표준편차를 썼다. 구현에 따라 7로 나누기도 하는데 — PyTorch의 torch.std 기본값이 이쪽이다 — 그러면 0.463이 된다.)

같은 프롬프트에서 뽑은 답들의 묶음을 그룹이라 부른다. 그룹의 크기를 GG로 적으면, 방금 한 계산은 이렇다.

V^(x)=μG=1G∑i=1GR(x,yi),A^i=R(x,yi)−μGσG\textcolor{#00897b}{\hat V(x)} = \textcolor{#00897b}{\mu_G} = \frac{1}{\textcolor{#5f970c}{G}}\sum_{i=1}^{\textcolor{#5f970c}{G}} \textcolor{#d9670b}{R}(\textcolor{#0093b8}{x}, \textcolor{#1c9c60}{y_i}), \qquad \textcolor{#8e44ad}{\hat A_i} = \frac{\textcolor{#d9670b}{R}(\textcolor{#0093b8}{x}, \textcolor{#1c9c60}{y_i}) - \textcolor{#00897b}{\mu_G}}{\textcolor{#008deb}{\sigma_G}}
V^(x)프롬프트 x의 가치 추정 (비평가 대신)μG그룹 평균 보상R검증기가 준 점수x, yi프롬프트와 i번째 응답A^i응답 i의 어드밴티지σG그룹 보상의 표준편차G같은 프롬프트에서 뽑은 응답 수 \small\begin{array}{ll} \textcolor{#00897b}{\hat V(x)} & \text{프롬프트 x의 가치 추정 (비평가 대신)} \\ \textcolor{#00897b}{\mu_G} & \text{그룹 평균 보상} \\ \textcolor{#d9670b}{R} & \text{검증기가 준 점수} \\ \textcolor{#0093b8}{x},\ \textcolor{#1c9c60}{y_i} & \text{프롬프트와 i번째 응답} \\ \textcolor{#8e44ad}{\hat A_i} & \text{응답 i의 어드밴티지} \\ \textcolor{#008deb}{\sigma_G} & \text{그룹 보상의 표준편차} \\ \textcolor{#5f970c}{G} & \text{같은 프롬프트에서 뽑은 응답 수} \end{array}

표준편차를 흔히 σ로 적어 σG\textcolor{#008deb}{\sigma_G}다. 위 표에서 G=8\textcolor{#5f970c}{G} = 8, μG=0.75\textcolor{#00897b}{\mu_G} = 0.75, σG=0.433\textcolor{#008deb}{\sigma_G} = 0.433이었다. 신경망 하나를 통째로 학습하는 대신 생성을 몇 번 더 하는 것으로 바꾼 셈이다. 정책 그래디언트 E[∑tΦt∇log⁡πθ]\mathbb{E}[\sum_t \textcolor{#8e44ad}{\Phi_t} \nabla\log\textcolor{#1565c0}{\pi_\theta}] 에서 각 토큰의 로그확률 그래디언트에 곱하는 빈칸 Φt\textcolor{#8e44ad}{\Phi_t}(대문자 파이)로 쓰면:

Φt=A^i=Ri−μGσG(응답 i의 모든 토큰 t에 같은 값)\textcolor{#8e44ad}{\Phi_t} = \textcolor{#8e44ad}{\hat A_i} = \frac{\textcolor{#d9670b}{R_i} - \textcolor{#00897b}{\mu_G}}{\textcolor{#008deb}{\sigma_G}} \qquad (\text{응답 } i \text{의 모든 토큰 } t \text{에 같은 값})
Φt정책 그래디언트의 빈칸A^i응답 i의 어드밴티지Ri응답 i의 점수μG그룹 평균 (베이스라인)σG그룹 표준편차 \small\begin{array}{ll} \textcolor{#8e44ad}{\Phi_t} & \text{정책 그래디언트의 빈칸} \\ \textcolor{#8e44ad}{\hat A_i} & \text{응답 i의 어드밴티지} \\ \textcolor{#d9670b}{R_i} & \text{응답 i의 점수} \\ \textcolor{#00897b}{\mu_G} & \text{그룹 평균 (베이스라인)} \\ \textcolor{#008deb}{\sigma_G} & \text{그룹 표준편차} \end{array}

표에서 하나가 눈에 띈다. 오답이 더 큰 크기의 어드밴티지를 받는다. 대부분 맞히는 문제에서의 오답은 드문 사건이라 강한 정보를 담는다. 그렇다면 몫을 다 모으면 어느 쪽이 클까? 여섯 정답이 받은 +0.577들과 두 오답이 받은 −1.732들을 견주는 일은 아래 문제 1·2에 남겨 둔다.

그렇다면 모두 맞히거나 모두 틀리면 어떻게 될까? 위젯에서 "모두 정답"을 눌러 보라. 보상이 전부 1이면 μG=1\textcolor{#00897b}{\mu_G} = 1, 모든 A^i=0\textcolor{#8e44ad}{\hat A_i} = 0 — 이 프롬프트에서는 아무것도 배우지 않는다. 모두 틀려도 마찬가지다. GRPO는 자동으로 "지금 모델에게 적당히 어려운 문제"에서만 배운다. 쉬운 문제는 이미 졸업했고, 어려운 문제는 아직 손이 닿지 않는다. 이 현상은 공짜 커리큘럼이기도 하고, 배치의 상당 부분이 낭비된다는 뜻이기도 하다. 위젯의 「문제 2」 단추는 아래 문제 2의 그룹을 불러온다. 풀이를 마친 뒤 수치판과 견주어 보라.

ML에서: 응답 안의 모든 토큰이 같은 몫을 받는다

공짜는 아니다. Actor-Critic의 비평가는 토큰마다 다른 V(st)\textcolor{#00897b}{V}(\textcolor{#0093b8}{s_t})를 줘서 “어느 토큰이 잘했나”(크레딧 할당, credit assignment)를 가렸다. GRPO의 베이스라인은 프롬프트 하나에 숫자 하나다. 응답 안의 모든 토큰이 같은 어드밴티지를 받는다 — 정답 풀이의 쓸데없는 문장도, 오답 풀이의 올바른 중간 단계도.

위 표로 세어 보자. 「입니다」로 끝나는 답은 1, 4, 7번이다. 하나는 정답, 둘은 오답이라 이 말투의 토큰들은 이 그룹에서 0.577−2×1.732=−2.8870.577 - 2 \times 1.732 = -2.887만큼 눌린다. 말투는 정답과 아무 상관이 없는데도 그렇다. 곱셈으로 공식을 쓴 2, 5, 6번은 셋 다 정답이라 3×0.577=+1.7323 \times 0.577 = +1.732만큼 올라간다. 그래도 GRPO가 작동하는 것은 그룹이 수없이 쌓이기 때문이다. 정답과 상관없는 말투는 다른 그룹에서는 정답 쪽에 섞여 올라가기도 해서, 그룹이 쌓일수록 눌린 몫과 올라간 몫이 서로 지워진다. 정답 풀이에 꾸준히 더 자주 나오는 표현만 계속 올라간다. 왜 지워지는지는 아래 문제 1·2를 풀고 나면 보인다. 그래도 한 그룹 안에서 엉뚱한 토큰이 몫을 받는 비용은 남아서, 긴 추론을 학습할 때 비평가를 다시 들여오는 방법(바이트댄스의 VAPO, 2025 — 가치 모델을 쓰는 PPO 틀)도 나왔다.

문제 1 — 회식비 정산

여덟 명이 회식비 96,000원을 똑같이 나눠 내기로 했다. (가) 한 사람이 카드로 전부 냈다. 낸 사람은 얼마를 돌려받고, 나머지 일곱은 각각 얼마를 보내야 하는가? 돌려받는 돈과 보내는 돈의 합을 견주시오. (나) 두 사람이 48,000원씩 나눠 냈다면?

김민준 (평상)
김민준
한 사람 몫이 96,000 ÷ 8 = 12,000원이니까, 낸 사람은 84,000원을 돌려받고 일곱 명은 12,000원씩 보내요. 7 × 12,000 = 84,000이라 보내는 돈의 합도 84,000원으로 같아요.
김민준 (평상)
김민준
(나)는 돌려받을 84,000원을 둘이 나누니까 42,000원씩이요.
선생님 (짚어주기)
선생님
민준 학생, 48,000원을 낸 두 사람도 자기 몫 12,000원은 내야 하죠?
김민준 (당황)
김민준
아, 그럼 48,000 − 12,000 = 36,000원씩이네요. 돌려받는 합은 72,000원이고, 여섯 명이 12,000원씩 보내니 보내는 합도 72,000원. 돌려받을 돈이 84,000원으로 정해진 게 아니었어요.
이서연 (아하)
이서연
양쪽 합은 늘 같은데, 한 사람이 움직이는 돈은 낸 사람이 하나일 때 84,000원, 둘일 때 36,000원이야. 드문 쪽에 선 사람일수록 한 명이 크게 움직여.
김민준 (평상)
김민준
단톡방 정산 앱이 하는 계산이 이거였네요.

정리 (가) 낸 사람은 84,000원을 돌려받고, 일곱 명이 12,000원씩 모두 84,000원을 보낸다. (나) 두 사람이 36,000원씩 모두 72,000원을 돌려받고, 여섯 명이 12,000원씩 모두 72,000원을 보낸다. 평균을 뺀 값은 합이 0이라 받는 쪽과 내는 쪽의 합이 늘 같고, 드문 쪽이 한 명당 큰 몫을 갖는다.

문제 2 — 한 명만 맞힌 그룹

8개 답 중 1개만 정답(보상 1)이고 일곱은 오답(보상 0)이다. (가) 각 답의 어드밴티지 A^i\textcolor{#8e44ad}{\hat A_i}를 구하시오. 7개 오답이 받는 억제의 합과 1개 정답이 받는 강화 중 어느 쪽이 큰가? (나) 그룹 크기를 DeepSeekMath처럼 G=64\textcolor{#5f970c}{G} = 64로 늘렸는데 이번에도 하나만 정답이다. 그 하나의 A^i\textcolor{#8e44ad}{\hat A_i}는 얼마인가? "표준화한 값은 대개 ±2 안에 든다"는 말과 견주시오.

김민준 (평상)
김민준
μ = 1/8 = 0.125, σ = √(0.125 × 0.875) = 0.331. 정답은 (1 − 0.125)/0.331 = +2.65, 오답은 −0.125/0.331 = −0.38 이요.
선생님 (평상)
선생님
좋아요. 그럼 억제와 강화, 어느 쪽이 커요?
김민준 (자신만만)
김민준
아까 회식비 정산이랑 같아요. 7 × 0.38 = 2.65. 평균을 뺐으니 양쪽 합이 같고, σ로 나눠도 모두 같은 수로 나누니까 그대로예요.
선생님 (평상)
선생님
그럼 (나), 64개 가운데 하나만 맞히면요?
이서연 (평상)
이서연
표준화한 값은 보통 ±2 안에 들잖아. 64개면 그룹이 커서 더 안정될 테니 2 언저리일 거야.
선생님 (짚어주기)
선생님
서연 학생, "±2 안"은 어떤 분포를 두고 한 말이었죠?
이서연 (생각)
이서연
정규분포요… 여기는 0 아니면 1이고, 1이 64개 중 하나뿐이네요. 계산해 볼게요. μ = 1/64, σ = √(1/64 × 63/64) = √63/64. 정답은 (63/64) ÷ (√63/64) = √63 ≈ 7.94.
이서연 (깨달음)
이서연
드물수록 끝없이 커지네요. G개 중 하나만 1이면 그 답은 √(G − 1)을 받아요.
김민준 (평상)
김민준
나머지 63개는 −1/√63 ≈ −0.126씩이니까, 운 좋게 한 번 맞힌 답 하나가 나머지 63개를 다 합친 만큼의 몫을 혼자 받는 거네요.
선생님 (평상)
선생님
그래요. 그룹이 클수록 드물게 나온 정답 하나가 그 프롬프트의 학습을 거의 혼자 끌고 가요.
이서연 (평상)
이서연
확률 수업에서 체비셰프 부등식을 배울 때, 표준화한 값이 k를 넘을 확률은 1/k² 이하라는 것뿐이라 드문 값이 크게 튀는 건 막아 주지 않는다고 했던 게 이거였구나. 여기선 7.94 이상인 값이 나올 확률이 1/64라, 부등식의 한계 1/63에 거의 붙어 있어요.

정리 (가) A^정답=+2.65\textcolor{#8e44ad}{\hat A_\text{정답}} = +2.65, A^오답=−0.38\textcolor{#8e44ad}{\hat A_\text{오답}} = -0.38. ∑i(ri−μ)=0\sum_i (\textcolor{#d9670b}{r_i} - \textcolor{#00897b}{\mu}) = 0이고 모두 같은 σ\textcolor{#008deb}{\sigma}로 나누므로 강화의 합과 억제의 합은 항상 같다. (나) 63≈7.94\sqrt{63} \approx 7.94, 나머지는 −1/63≈−0.126-1/\sqrt{63} \approx -0.126. 0/1 보상에서 G\textcolor{#5f970c}{G}개 중 하나만 맞히면 그 답은 G−1\sqrt{\textcolor{#5f970c}{G}-1}을 받는다. 표준화했다고 ±2 안에 드는 것이 아니다.