Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

GRPOTrainer로 훈련하기

DeepSeek의 인상적인 새 추론 모델은 Group Relative Policy Optimization(GRPO, 그룹 상대 정책 최적화)이라는 핵심 혁신을 기반으로 합니다. PPO를 영리하게 변형한 이 기법은 모델의 훈련 수렴성을 개선할 뿐만 아니라 기존 방식보다 메모리도 적게 사용합니다. 이것은 DeepSeek의 새로운 R1 모델이 놀라울 만큼 낮은 학습 비용으로 최첨단 성능을 달성하는 데 도움을 준 Core 아이디어 중 하나였습니다. 이 글에서는 GRPOTrainer의 기본 사용법과 그것이 작동하는 이유, 그리고 새로운 모델 훈련에 어떻게 활용할 수 있는지를 살펴봅니다.

GRPO는 훈련 중에 모델이 생성한 데이터를 사용하여 반복적으로 개선되는 온라인 학습 알고리즘입니다. 핵심 혁신은 그룹 점수로부터 기준선(baseline)을 추정함으로써 비평가(critic) 모델이 필요 없게 만들어, PPO에 비해 훈련 자원을 크게 줄인다는 점입니다.

이 알고리즘은 네 가지 주요 단계로 작동합니다.

  1. 완성 생성: 배치의 각 프롬프트에 대해 이전 정책을 사용하여 G개의 서로 다른 완성을 생성합니다
  2. 보상 계산: 규칙 기반 보상 함수를 사용하여 각 완성에 점수를 매깁니다
  3. 어드밴티지 정규화: 그룹의 평균과 표준편차로 보상을 정규화하여 상대적 어드밴티지를 계산합니다
  4. 정책 최적화: 새 정책이 이전 정책에서 크게 벗어나지 않도록 하는 클리핑된 PPO 방식의 목적 함수를 사용하여 모델을 업데이트합니다

이 접근법은 별도의 비평가 네트워크가 필요 없고, 그룹 통계를 사용하여 기준선을 설정하며, 클리핑을 통해 정책의 안정성을 유지하고, 기존 PPO에 비해 메모리 사용량을 줄이기 때문에 특히 효율적입니다.

“아하 모먼트”

DeepSeek-R1을 훈련하는 동안 연구자들은 이른바 “아하 모먼트“를 관찰했는데, 이는 GRPO 훈련을 통해 정교한 다단계 추론 행동이 창발하는 현상을 가리키는 용어입니다. 모델은 다음과 같은 행동을 보이는 일종의 창발적 사고의 연쇄(chain-of-thought) 추론을 보여 주었습니다.

  1. 초기 해결 접근법을 생성합니다
  2. 자신의 추론이 올바른지 자동으로 검증합니다
  3. 논리상의 잠재적 결함이나 빈틈을 찾아냅니다
  4. 필요할 때 대안적 접근법을 생성합니다
  5. 최종 답을 정하기 전에 여러 풀이 경로를 비교합니다

이러한 행동은 특히 수학적·논리적 추론 과제에서 두드러지게 나타났습니다. 복잡한 문장제 문제를 풀 때가 그 예입니다. 특히 흥미로운 점은 이러한 검증 행동이 명시적인 사고의 연쇄 프롬프팅이나 인간 피드백 없이 순전히 GRPO 보상 최적화 과정만으로 창발했다는 것입니다. 검증 단계를 포함한 풀이가 비교 그룹 내에서 일관되게 더 높은 보상을 받았기 때문에, 그룹 상대적 어드밴티지 계산은 자연스럽게 모델이 더 철저한 추론 전략을 발전시키도록 유도했습니다.

논문에서는 관찰된 네 가지 주요 창발적 행동을 다음과 같이 기술합니다.

  • 자기 검증: 모델은 자신의 작업을 자동으로 점검하고 답을 검증하는 법을 학습했습니다.
  • 확장된 사고의 연쇄: 명시적인 단계별 추론을 통해 풀이가 더 상세해졌습니다.
  • 탐색적 추론: 모델은 최선의 풀이를 정하기 전에 여러 접근법을 시도했습니다.
  • 성찰: 모델은 자신의 추론 과정에 의문을 제기하고 이를 수정하는 능력을 발전시켰습니다.

이러한 창발적 행동은 그룹 상대 정책 최적화의 이점에 관한 이론적 예측과 일치합니다. 같은 컨텍스트 그룹 안에서 여러 풀이 시도를 비교함으로써, 학습 과정은 자연스럽게 더 견고하고 스스로 오류를 바로잡는 추론 패턴을 선택하게 됩니다. KL 발산 제약은 이러한 패턴이 급격한 정책 변화가 아니라 점진적이고 안정적으로 발전하도록 보장합니다.

더 쉽게 말하면, 이는 모델이 스스로 “자신의 풀이를 점검하는” 법을 익힌 것과 같습니다. 꼼꼼한 학생이 수학 문제를 푼 뒤 다시 돌아가 답이 타당한지 확인하고, 무언가 이상해 보이면 다른 방법을 시도하는 것과 비슷합니다. 이 훈련 방법은 본질적으로 여러 풀이 시도를 나란히 비교함으로써 이러한 신중하고 철저한 접근 방식에 보상을 주었습니다. 이는 마치 교사가 같은 숙제의 여러 버전을 채점하면서 풀이 과정을 보여 주고 답을 검증한 학생에게 더 좋은 점수를 주는 것과 같습니다. 모델은 이렇게 하도록 명시적으로 배운 것이 아닙니다. 이러한 좋은 학습 습관이 꾸준히 더 나은 결과로 이어졌기 때문에 자연스럽게 그런 습관을 기르게 된 것입니다!

이론

GRPO는 훈련 중에 모델이 생성한 데이터를 사용하여 반복적으로 개선되는 온라인 학습 알고리즘입니다. GRPO의 핵심 혁신은 그룹 기반 정규화를 통해 어드밴티지를 추정하는 방식과 KL 발산 제약을 효율적으로 처리하는 방식에 있습니다.

GRPO의 핵심(Core) 동작 방식은 먼저 각 입력 프롬프트마다 여러 개의 완성을 생성하는 것입니다. 배치의 각 프롬프트에 대해 모델은 현재 정책 를 사용하여 서로 다른 완성문 개를 생성합니다. 이 완성문들은 상대 비교에 사용될 “그룹“을 형성하며, 이는 GRPO가 어드밴티지를 추정하는 방식의 근간입니다.

GRPO라는 이름은 바로 이 어드밴티지 추정 과정에서 비롯되었습니다. 각 완성문에 대해 보상 모델을 사용하여 보상 를 계산합니다. GRPO는 어드밴티지를 추정하기 위해 크리틱 네트워크를 사용하는 대신, 각 그룹 내에서 보상을 정규화합니다:

이 그룹 상대 정규화 방식은 같은 그룹 내의 다른 완성문과 비교하여 보상을 자연스럽게 상대 비교하므로, 어드밴티지 추정을 위한 내장 기준선을 제공한다는 점에서 특히 효과적입니다.

안정적인 훈련을 보장하기 위해 GRPO는 Schulman의 근사기를 사용한 KL 발산 추정을 활용합니다. 이 항은 훈련 중에 정책이 참조 정책에서 너무 멀리 벗어나지 않도록 하는 데 도움이 됩니다. 완전한 GRPO 손실 함수는 어드밴티지 최대화와 이 KL 발산 페널티를 결합합니다.

별도의 크리틱 모델이 필요 없으므로 메모리 효율이 더 높습니다. 그룹 상대 어드밴티지 계산은 훈련의 분산을 줄이는 데 도움이 되며, KL 발산 항은 정책 붕괴를 방지합니다. 그룹 내 정규화는 이 방법을 보상 스케일링에 강건하게 만들며, 생성당 한 번의 업데이트(PPO의 여러 번 업데이트와 비교하여)는 구현을 단순화합니다. 이러한 설계 덕분에 GRPO는 기존 언어 모델 아키텍처와 통합하기에 특히 적합합니다.

더 쉽게 말하자면, GRPO를 교실에서 에세이를 채점하는 교사라고 상상해 보십시오. 교사는 별도의 조교(크리틱 모델)에게 채점을 돕게 하는 대신, 에세이를 작은 그룹으로 묶어 함께 살펴봅니다. 각 그룹에서 교사는 에세이들의 순위를 서로 상대적으로 매기며, 어떤 에세이가 해당 그룹의 평균보다 더 나은지 또는 못한지를 판단합니다. 이것이 그룹 내에서 보상을 정규화하는 것과 같습니다.

또한 교사는 과제마다 채점 기준을 완전히 바꾸지 않도록 주의하며(KL 발산 제약), 이전에 채점했던 방식과 어느 정도 일관성을 유지하고자 합니다. 이는 학생들이 매번 크게 다른 기준을 마주하는 대신, 무엇이 기대되는지 이해하고 점진적으로 향상되도록 돕습니다.

이 방식은 추가 조교가 필요 없고, 작은 그룹 내에서 에세이를 비교하는 것이 모든 에세이를 다른 모든 에세이와 비교하려는 것보다 더 다루기 쉽기 때문에 기존 방법보다 더 효율적입니다. 이는 작은 토론 분반마다 상대평가를 하는 것과 대학 전체에 걸쳐 상대평가를 하려는 것의 차이와 같습니다!

사용법

TRL 프레임워크를 통해 GRPO를 사용하는 방법은 다음과 같습니다:

from datasets import load_dataset
from trl import GRPOConfig, GRPOTrainer

# Load your dataset
dataset = load_dataset("trl-lib/tldr", split="train")

# Configure training arguments
training_args = GRPOConfig(
    output_dir="output",
    num_train_epochs=3,
    per_device_train_batch_size=1,
    gradient_accumulation_steps=8,
    learning_rate=1e-6,
    logging_steps=10,
    num_generations=8,
    max_prompt_length=512,
    max_completion_length=256,
    temperature=0.9,
    beta=0.04,  # KL penalty coefficient
)

# Initialize trainer
trainer = GRPOTrainer(
    model="your/base/model",
    args=training_args,
    train_dataset=dataset,
    reward_funcs=reward_func,
)

# Start training
trainer.train()

주요 훈련 매개변수는 다음과 같습니다:

  • num_generations: 프롬프트당 생성할 완성문의 수 (기본값: 8)
  • beta: 모델이 참조 정책에서 얼마나 벗어날 수 있는지를 제어하는 KL 페널티 계수입니다(기본값: 0.04)
  • temperature: 생성 시 사용하는 샘플링 온도입니다(기본값: 0.9)
  • max_prompt_length: 입력 프롬프트의 최대 길이입니다(기본값: 512)
  • max_completion_length: 생성되는 완성문의 최대 길이입니다(기본값: 256)

GRPO는 학습 중에 다음과 같은 주요 지표를 기록합니다.

  • completion_length: 완성문의 평균 길이입니다
  • reward/{reward_func_name}: 각 보상 함수가 계산한 보상입니다
  • reward: 평균 보상입니다
  • reward_std: 보상 그룹 내 표준편차의 평균입니다
  • kl: 모델과 참조 모델 사이의 평균 KL 발산입니다

온라인 방법에서 주된 병목이 되는 경우가 많은 생성 속도를 높이려면 vLLM을 사용할 수 있습니다.

from trl import GRPOConfig

training_args = GRPOConfig(
    # ... other args ...
    use_vllm=True,
    vllm_gpu_memory_utilization=0.9,
)

학습 방향을 이끌기 위해 사용자 정의 보상 함수를 정의할 수도 있습니다. 이 함수는 다음 조건을 충족해야 합니다.

  1. prompts와 completions를 키워드 인수로 받아야 합니다
  2. 각 완성문에 대한 보상을 나타내는 float 리스트를 반환해야 합니다

더 긴 완성문에 보상을 주는 보상 함수 예시입니다.

def reward_func(completions, **kwargs):
    """Reward function that evaluates natural text quality based on multiple factors:
    - Length (with diminishing returns after target length)
    - Readability metrics
    - Sentence structure variety
    - Proper punctuation
    """
    rewards = []
    target_length = 200  # Ideal completion length

    for completion in completions:
        # Base score
        score = 0.0

        # Length reward with diminishing returns
        length = len(completion)
        if length < target_length:
            length_score = length / target_length
        else:
            length_score = 1.0 + 0.1 * math.log(length / target_length)
        score += length_score

        # Reward sentence variety (look for mix of short and long sentences)
        sentences = [s.strip() for s in completion.split('.') if s.strip()]
        if sentences:
            lengths = [len(s) for s in sentences]
            variance = statistics.variance(lengths) if len(lengths) > 1 else 0
            score += min(0.5, variance / 500)  # Cap variance reward

        # Reward proper punctuation
        if any(p in completion for p in ['.', '!', '?']):
            score += 0.3

        # Penalize very short sentences or fragments
        if any(len(s) < 10 for s in sentences):
            score -= 0.2

        rewards.append(float(score))

    return rewards

GRPO는 여러 보상 함수를 지원합니다. 리스트로 전달하기만 하면 됩니다.

trainer = GRPOTrainer(
    reward_funcs=[reward_func1, reward_func2],
    # rest of arguments
)

최종 보상은 각 함수가 계산한 보상의 합으로 계산됩니다.


원문 © Stephen Diehl, stephendiehl.com · 글 CC BY-SA 4.0, 코드 MIT · Stephen Diehl은 런던의 소프트웨어 엔지니어이자 작가입니다(저자가 정한 소개 문구). 이 글은 저자가 개인 자격으로 쓴 것이며 어떤 조직과의 연관도 밝히거나 암시해서는 안 됩니다 (라이선스와 고지).
이 페이지는 원문을 한국어로 옮기며 변경한 비공식 번역이며 같은 CC BY-SA 4.0으로 제공합니다 · yeokja 한국어 번역 · Anthropic 사의 claude-opus-5-5 모델을 활용하여 번역되었으며 학습을 모두 비허용한 상태로 작업하였습니다.