logo
|
Blog
    채용공고
    ML EngineeringML Research

    유저와 함께 만드는 LLM 2편 — 제타에 Online Learning 도입하기

    Jun 10, 2026
    유저와 함께 만드는 LLM 2편 — 제타에 Online Learning 도입하기
    Contents
    들어가며학습 인프라Rollout과 학습 사이의 확률 불일치점수는 잘 오르는데, 왜 생성은 이상해질까?결과마치며참고 문헌
    김만수 | #ML Engineer

    들어가며

    지난 1편에서는 제타의 답변 재생성 시그널을 활용해 DPO(Direct Preference Optimization)로 모델을 개선한 과정을 소개해 드리며 글 후반에 최근에는 GRPO(Group Relative Policy Optimization)와 같은 online-learning 기법을 활용하고 있음을 간단히 설명드렸습니다.
    Deepseek[7]에서 처음 소개한 GRPO 방법론은 페어 데이터를 활용하는 대신 데이터를 평가하는 리워드 모델을 학습하여 활용합니다. 이때, 주어진 데이터셋 내에서 선호도를 학습하는 DPO 등과 달리, 생성 모델이 학습과정을 거치며 생성하는 답변을 리워드 모델이 실시간으로 평가하여 학습에 반영하며 online-learning으로 분류됩니다. GRPO는 DPO에 비하여 시그널이 페어쌍으로 존재하지 않아도 되기 때문에 리워드 모델만 잘 만든다면 제타에 존재하는 다양한 유저의 시그널을 활용할 수 있다는 장점이 있습니다.
    하지만 이렇듯 개념적으로 간단해 보이는 GRPO 방법론을 실제 프로덕션에 적용하려고 하니, 예상보다 훨씬 많은 문제들을 마주했습니다. 모델 크기가 점차 커지면서 간단한 학습 구조로는 안정적인 학습이 불가능하였고, 안정적인 학습을 위해 학습 노드와 추론 노드를 분리하자 두 엔진 사이의 커널 차이로 인한 미묘한 확률 차이가 발생했습니다. 이를 개선하여 학습을 안정화한 뒤에도, 리워드 모델 점수는 올라가지만 실제 생성 품질은 떨어지는 현상이 나타났습니다. 이번 포스트에서는 GRPO 학습 과정에서 저희가 어떤 문제를 만났고, 이를 해결하기 위해 학습 인프라와 알고리즘을 어떻게 바꿔 나갔는지 소개해보려 합니다.

    학습 인프라

    Online-learning을 적용하면서 가장 먼저 고민하게 된 것은 학습 인프라였습니다. 처음에는 GRPO 알고리즘을 잘 구현하고, 리워드 모델만 안정적으로 붙이면 학습이 어느 정도 돌아갈 것이라고 생각했습니다. 하지만 실제로는 알고리즘 이전에, 생성·채점·학습을 어디에서 어떻게 처리할 것인지와 같은 학습 인프라 구성부터 쉽지 않았습니다.
    Online-learning에서는 현재 모델이 여러 응답을 생성하고, 리워드 모델이 각 응답에 점수를 매긴 뒤, 그 결과를 다시 학습에 사용합니다. 즉 한 번의 학습 스텝 안에 생성, 채점, 학습이 모두 이루어지게 되며, 이를 위해 처음에는 모든 과정을 한 노드에서 순차적으로 처리하는 동기 구조로 시작했습니다. 같은 GPU에서 vLLM으로 응답을 생성하고, 리워드 모델로 점수를 매긴 다음, 바로 학습을 수행하는 방식이었습니다.
    하지만 이 구조는 아래와 같은 한계점을 가졌습니다.
    • GPU 유휴 시간이 생깁니다. 생성 → 채점 → 학습을 한 GPU에서 순차적으로 처리하면, 한 단계가 수행되는 동안 다른 단계는 멈춰있을 수밖에 없습니다. 게다가 단계가 바뀔 때마다 전환 비용도 발생합니다. 예를 들어 학습 차례가 되면 vLLM이 점유하던 GPU 메모리를 CPU로 offload하고 KV 캐시를 비웠다가(sleep), 다시 생성 차례가 오면 GPU로 reload합니다(wake). offload와 reload의 이러한 교차만으로도 스텝마다 적지 않은 시간이 소요되고, 결국 엔진을 재웠다 깨우는 사이사이에 GPU가 사용되지 않으면서 스텝 시간의 상당 부분이 유휴 시간으로 소모됩니다.
    • GPU 메모리를 안정적으로 관리하기 어렵습니다. 학습 모델과 optimizer state, 그리고 vLLM이 하나의 GPU 메모리를 나눠 써야 하니 양쪽 모두 배치를 충분히 키우기 어렵습니다. 더 까다로운 문제는 그 다음입니다. PyTorch의 메모리 관리자(caching allocator)는 '비어 있는 GPU 메모리는 언제든 끌어다 쓸 수 있다'는 전제로 동작하는데, 같은 GPU에 vLLM이 함께 올라가 있으면 이 전제가 성립하지 못합니다. vLLM과 메모리를 나눠 쓰는 순간 memory fragmentation 및 peak memory를 안정적으로 제어하지 못해, 결국 메모리 부족(OOM) 오류가 발생하곤 했습니다.
    • 리워드 모델을 올릴 여유 메모리 공간 또한 부족합니다. 활용하는 유저 시그널을 늘릴수록, 각 시그널을 채점할 리워드 모델도 하나둘 GPU에 함께 올려야 합니다. 학습 모델과 vLLM만으로도 이미 여유가 없는 GPU에 시그널 수만큼 추가적인 리워드 모델을 사용할 필요성이 생기면서, 메모리 공간이 더더욱 부족해졌습니다.
    이중 특히 세 번째 문제는 구조를 바꾸지 않고는 해결할 수 없었습니다. 유저 시그널을 늘려 그만큼 리워드 모델을 자유롭게 붙이는 것이 이후 실험 계획의 핵심이었는데, 같은 GPU를 나눠 쓰는 구조에는 애초에 그럴 여유가 없었기 때문입니다.
    그래서 저희는 학습과 추론을 노드 수준에서 완전히 분리한 비동기(async) GRPO 인프라로 학습 인프라를 교체하기로 결정하였습니다. 이에 변경된 구조는 다음과 같습니다.
    그림 1. Synchronous RL vs Decoupled Asynchronous RL
    그림 1. Synchronous RL vs Decoupled Asynchronous RL
    이렇게 생성과 학습을 분리하는 방식은 최근 대규모 RL 시스템들이 공통적으로 택하는 방향이기도 합니다. Asynchronous RLHF [1], AReaL [2], verl(HybridFlow) [3], OpenRLHF [4], Meta의 LlamaRL [5] 같은 프레임워크가 모두 같은 구조를 채택하고 있습니다.
    저희도 이러한 구조를 채택하여 인프라 구조를 설계하였으며, 그 외에도 아래 세 가지 부분을 특별히 고려하였습니다.
    • 리워드 모델과 생성 엔진은 추론 노드를 공유합니다. 리워드 모델과 생성 엔진은 forward 연산만 수행하기 때문에 학습 노드에 비해 필요한 메모리가 적고, 학습 노드의 forward → backward 대비 generate → classify 과정이 더 짧은 시간 내에 계산 가능하여 동일 노드에 배치하기로 결정하였습니다.
    • NCCL로 가중치를 빠르게 동기화합니다. 매 스텝 학습마다 갱신된 가중치는 추론 노드의 vLLM에 전달되어야 합니다. 이때 HTTP 전송을 사용할 경우 GPU → CPU 복사, 직렬화, TCP 전송 등 단계가 많아 수십, 수백 B 크기의 모델 규모에서 큰 동기화 비용이 요구됩니다. 이를 해결하기 위해 학습 프로세스와 vLLM 프로세스를 하나의 NCCL 그룹으로 묶고, FSDP 파라미터를 스트리밍으로 모아 1GB 버퍼 두 개에 번갈아 패킹해 NVLink와 RDMA로 broadcast했습니다. 이렇게 하는 경우, 전송 중 추가되는 파라미터는 항상 하나뿐이라 메모리 부담이 적고, HTTP로는 수백 초 걸리던 가중치 동기화가 NCCL 기반으로 변경하니 수 초 안에 처리가 가능했습니다.
    • 너무 오래된 rollout은 버립니다. 비동기 구조에서는 큐의 rollout이 몇 스텝 전 정책에서 생성됐을 수 있습니다. 그래서 생성 시점의 정책 버전을 샘플에 함께 기록하고, 현재 정책과의 차이가 허용치를 넘으면 해당 샘플은 폐기합니다. 허용치를 낮출수록 더 on-policy에 가까워지지만 처리량이 떨어지고, 높이면 그 반대입니다. staleness를 명시적으로 통제하는 것이 학습 안정성에 중요하다는 점은 AReaL [2]에서도 언급되며, Asynchronous RLHF [1]에서는 어느 정도의 staleness는 학습에 큰 영향을 주지 않음을 언급하고 있습니다.
    이러한 설계를 통해 매번 모델 크기에 적합한 형태로 구조를 가져갈 수 있었고, 메모리 관리도 한층 수월해져 이후 다양한 사이즈의 모델에 대해서도 노드 수 외에는 별도의 인프라 변경 없이 학습을 진행할 수 있었습니다.

    Rollout과 학습 사이의 확률 불일치

    인프라를 구축하고 학습이 되기 시작하자, 이번에는 또다른 문제가 나타났습니다. 바로 vLLM이 생성한 Rollout의 확률과 학습 모델이 계산한 확률이 불일치하는 문제였습니다. PPO(Proximal Policy Optimization) 계열 알고리즘은 "이 응답을 만든 정책"과 "지금 학습 중인 정책"의 확률 비율로 학습 신호를 보정합니다. PPO[6]는 이를 probability ratio rt(θ)r_t(\theta)rt​(θ) 로 정의하고, 이 값이 너무 커지지 않도록 클리핑한 목적함수를 사용합니다.
    rt(θ)=πθ(at∣st)πθold(at∣st),LCLIP=Et[min⁡(rt A^t, clip(rt, 1−ϵ, 1+ϵ) A^t)]r_t(\theta) = \frac{\pi_\theta(a_t \mid s_t)}{\pi_{\theta_\text{old}}(a_t \mid s_t)}, \quad L^{\text{CLIP}} = \mathbb{E}_t\left[\min\big(r_t\,\hat{A}_t,\ \text{clip}(r_t,\,1-\epsilon,\,1+\epsilon)\,\hat{A}_t\big)\right]rt​(θ)=πθold​​(at​∣st​)πθ​(at​∣st​)​,LCLIP=Et​[min(rt​A^t​, clip(rt​,1−ϵ,1+ϵ)A^t​)]
    GRPO [7]에서도 ratio 및 clipping 부분은 동일하며, 저희 구조에서 분자는 학습 노드의 forward를 통해, 분모는 응답을 직접 생성한 추론 노드의 vLLM 인퍼런스를 통해 계산되게 됩니다.
    그런데 두 값을 비교해 보니, 같은 응답인데도 확률이 서로 어긋나 있었습니다. 이유는 두 가지였습니다.
    첫째, 같은 가중치라도 추론 엔진이 다르면 확률이 달라집니다. vLLM과 학습 프레임워크는 커널 구현과 연산 정밀도가 달라, 같은 가중치로 계산해도 logprob이 미세하게 달라집니다. 토큰당 평균 0.01~0.02 수준의 작은 차이지만, 수백 토큰짜리 응답에서는 학습 신호가 눈에 띄게 틀어질 만한 크기가 됩니다. rollout을 위해 vLLM과 같은 추론 엔진을 사용하게 되면, 누구나 의도치 않게 off-policy 학습을 하게 된다는 관찰이 커뮤니티에서도 독립적으로 보고된 바 있는데 [8], 같은 가중치를 두고 한 엔진은 어떤 토큰의 확률을 1로, 다른 엔진은 0으로 계산하는 극단적인 경우가 관측되기도 하였습니다.
    그림 2. Token Probability Difference
    그림 2. Token Probability Difference
    둘째, 큐에서 꺼낸 rollout이 이미 옛 정책이 생성한 응답일 수 있습니다. 동기 구조와 달리, 비동기 구조에서는 큐에서 꺼낸 rollout이 몇 스텝 전의 정책에서 만들어진 것일 수 있습니다. 즉 아무리 허용치 이내라 해도, 응답을 만든 정책과 지금 학습 중인 정책이 엄밀하게는 동일한 모델이 아니게 됩니다.
    위에서 언급한 두 가지 불일치(엔진 차이, 정책 버전 차이)를 해결하기 위해, 저희는 아래 두 가지 방법을 적용하여 학습을 안정화하였습니다.
    첫째, 엔진 차이와 버전 차이를 importance ratio를 통해 보정합니다. rollout 시점에 vLLM이 계산한 토큰별 logprob을 샘플에 함께 저장해 두고, 학습할 때는 각 토큰 loss에 아래 가중치를 곱함으로써 vLLM이 생성한 rollout의 분포가 학습 분포와 일치하도록 보정했습니다.
    wt=min⁡(πtrain(yt∣x,y<t)πvllm(yt∣x,y<t),  C),Lt←wt⋅Ltw_t = \min\left(\frac{\pi_{\text{train}}(y_t \mid x, y_{<t})}{\pi_{\text{vllm}}(y_t \mid x, y_{<t})},\; C\right), \qquad \mathcal{L}_t \leftarrow w_t \cdot \mathcal{L}_twt​=min(πvllm​(yt​∣x,y<t​)πtrain​(yt​∣x,y<t​)​,C),Lt​←wt​⋅Lt​
    여기서 분모의 πvllm\pi_{\text{vllm}}πvllm​ 은 "그 응답을 생성할 당시에 해당 엔진이 계산한 확률" 입니다. πtrain/πvllm\pi_{\text{train}}/\pi_{\text{vllm}}πtrain​/πvllm​ term 안에는 (1) 엔진 구현 차이와 (2) 정책 버전 차이에 대한 정보가 함께 들어가고, 위와 같은 importance ratio를 도입함으로써 위의 두 문제를 동시에 고려할 수 있습니다.
    그렇게 생각하니 자연스럽게, "PPO clip의 분모를 πvllm\pi_{\text{vllm}}πvllm​ 으로 바꾸면 한 번에 해결되지 않을까?"의 질문에 도달하게 됩니다. loss의 importance ratio와 같은 별도의 보정항을 곱할 필요 없이, clip 비율의 분모를 πold\pi_{\text{old}}πold​ 대신 πvllm\pi_{\text{vllm}}πvllm​ 으로 갈아끼워 clip 하나로 끝내자는 방식(PPO-IS)이죠.
    LPPO-IS=Et[min⁡(πtrainπvllmA^t, clip(πtrainπvllm, 1−ϵ, 1+ϵ)A^t)]L^{\text{PPO-IS}} = \mathbb{E}_t\left[\min\left(\frac{\pi_{\text{train}}}{\pi_{\text{vllm}}}\hat{A}_t,\ \text{clip}\left(\frac{\pi_{\text{train}}}{\pi_{\text{vllm}}},\,1-\epsilon,\,1+\epsilon\right)\hat{A}_t\right)\right]LPPO-IS=Et​[min(πvllm​πtrain​​A^t​, clip(πvllm​πtrain​​,1−ϵ,1+ϵ)A^t​)]
    그러나 이러한 방식을 사용하는 경우, precision이 낮은 8bit 학습에서 정확도가 거의 0에 수렴하는 문제가 발생하게 됩니다 [8]. 이러한 PPO-IS 방식의 정확도가 무너지는 이유는 PPO clip 이 본질적으로 맡았던 역할을 생각해보면 알 수 있습니다. PPO clip의 창 [1−ϵ, 1+ϵ][1-\epsilon,\ 1+\epsilon][1−ϵ, 1+ϵ] 은 원래 "정책이 한 스텝에 너무 많이 변하지 않게" 막는 trust-region 제어용이라 보통 매우 작은 크키로 설정합니다 보통 ϵ≈0.2\epsilon \approx 0.2ϵ≈0.2). 이 좁은 범위에 분포 불일치 보정까지 떠맡기면, πvllm\pi_{\text{vllm}}πvllm​ 이 크게 어긋난 토큰일수록 ratio가 clip의 범위를 넘어서게 되어 gradient가 0이 됩니다. 정작 보정이 가장 필요한 토큰의 신호가 죽는 것이죠.
    그래서 [8]에서는 이 둘의 역할을 분리하기 위한 방법으로 TIS(Truncated Importance Sampling) 방법을 제시합니다. 스텝 크기 제어는 좁은 ϵ\epsilonϵ 의 clip 비율 πtrain/πold\pi_{\text{train}}/\pi_{\text{old}}πtrain​/πold​ 이 맡고, 분포 불일치 보정은 느슨한 상한 CCC 의 별도 가중치 wt=min⁡(πtrain/πvllm, C)w_t = \min(\pi_{\text{train}}/\pi_{\text{vllm}},\ C)wt​=min(πtrain​/πvllm​, C) 가 맡도록 하는것이죠.
    Lt=−min⁡ ⁣(rtA^t, clip(rt, 1−ϵ, 1+ϵ)A^t)⏟PPO clip (step-size control)  ⋅  wt⏟TIS (mismatch correction),rt=πtrainπold,wt=min⁡ ⁣(πtrainπvllm, C)\mathcal{L}_t = -\underbrace{\min\!\left(r_t\hat{A}_t,\ \text{clip}(r_t,\,1-\epsilon,\,1+\epsilon)\hat{A}_t\right)}_{\text{PPO clip (step-size control)}} \;\cdot\; \underbrace{w_t}_{\text{TIS (mismatch correction)}}, \qquad r_t=\frac{\pi_{\text{train}}}{\pi_{\text{old}}},\quad w_t=\min\!\left(\frac{\pi_{\text{train}}}{\pi_{\text{vllm}}},\,C\right)Lt​=−PPO clip (step-size control)min(rt​A^t​, clip(rt​,1−ϵ,1+ϵ)A^t​)​​⋅TIS (mismatch correction)wt​​​,rt​=πold​πtrain​​,wt​=min(πvllm​πtrain​​,C)
    저희도 이런 truncated importance sampling을 적용했습니다. 이 방식은 IMPALA [9] 이후로, 오픈소스 LLM RL 프레임워크들(verl, TRL 등)에서도 널리 쓰이고 있습니다.
    둘째, 정책 버전 차이는 애초에 작게 유지합니다. Importance ratio를 이용한 보정에만 의존하는 경우, 버전 차이가 커질수록 wtw_twt​ 가 상한에 걸리는 토큰이 늘어나 유효 신호가 급감하게 될 수 있으므로, 앞에서 소개한 staleness 허용치로 버전 차이를 보정이 감당 가능한 범위로 제한해 둡니다. 또한 학습 노드와 추론 노드가 이미 분리되어 있는 만큼, 추론 노드 대수를 충분히 늘리면 rollout이 오래 기다리며 staleness가 과도해지는 문제를 구조적으로 완화할 수 있습니다.
    비동기 학습 인프라로 전환하고, 확률 불일치로 인한 학습 불안정과 성능 저하를 보정하고 나니 비로소 점수가 안정적으로 오르기 시작했는데요. 그런데 실제 응답을 확인해보니, 모델의 답변 품질은 오히려 떨어지고 있었습니다.

    점수는 잘 오르는데, 왜 생성은 이상해질까?

    사실 online-learning에서 생성 품질이 무너지는 현상(degeneration) 자체는 널리 알려진 문제입니다. 다만 저희가 겪은 현상은 흔히 이야기되는 degeneration과는 조금 결이 달랐습니다. 일반적인 강화학습에서 품질 붕괴는 대개 정책의 entropy가 급격히 무너지는 현상으로 인해, 탐색이 원활히 이루어지지 않아 발생하는 문제로 설명되곤 합니다 [10]. 그래서 이 계열의 연구들은 clip 범위를 비대칭으로 넓히거나 [11], covariance에 기반해 entropy를 직접 제어하는 [10] 방식으로 탐색을 살리는 데 초점을 맞추는 등 entropy를 제어하는 것에 집중하고 있습니다 [12].
    이에 저희도 가장 우선적으로 학습하면서 변화하는 entropy의 추이를 살펴보았지만, 문제의 핵심은 거기에 있지 않았습니다. entropy의 불안정으로 인해 degeneration이 발생하는 것이 아니라, 생성 품질과는 상관없이 손쉽게 점수를 올릴 수 있는 방향으로 학습되는 reward hacking이 발생하고 있었습니다.
    Reward hacking은 크게 두 가지의 형태로 나타났습니다. 첫째는 보상 신호 쪽으로, 사용자 시그널에 응답 품질과 직접 관계없는 특성(길이, 문체 등)에 편향이 생긴 경우였습니다. 다른 하나는 학습 알고리즘 쪽으로, importance sampling이나 loss 정규화 같은 알고리즘 특성에 의해 특정 토큰 혹은 응답이 최종적으로 상대적으로 높은 점수를 받게되는 경우였습니다. 이제 저희가 실제로 마주친 두 가지 케이스를 통해, 어떠한 문제가 발생하였고 어떻게 해결하였는지 차례로 풀어보겠습니다.

    Case 1: 띄어쓰기가 사라진 모델

    GSPO[13]와 같이 sequence 단위 importance sampling으로 학습한 모델에서 이상한 현상이 관찰되기 시작했습니다. 학습이 진행될수록 모델이 띄어쓰기를 생략하기 시작한 것입니다. 예를 들어 "오늘 어디 가?"를 "오늘어디가?"처럼 단어 사이의 공백을 지워 붙이는 식으로, 의미는 같지만 띄어쓰기를 하지 않은 문장을 출력하는 경향이 많아지기 시작했습니다.
    저희가 세운 가설 중 하나는 학습 단위에 원인이 있을 수 있다는 점이었습니다. 한국어에서 띄어쓰기는 ‘공백이 붙은 토큰( 어디)’과 ‘붙지 않은 토큰(어디)’ 중 무엇을 고르느냐의 문제, 다시 말해 토큰 단위 선택의 문제로 볼 수 있습니다. 토큰 단위로 학습하는 GRPO에서는 ‘공백을 뗀다’는 선택 하나하나가 개별 신호로 평가되고 교정될 여지가 있는 반면, GSPO는 문장을 이루는 토큰 묶음에 대해 평가가 이루어지게 됩니다. 구체적으로, 위에서 설명된 ratio가 GRPO에서는 토큰마다 계산되지만, GSPO에서는 sequence 단위로 계산되며, 이는 토큰별 비율을 길이로 평균낸 값이 됩니다.
    ri,t(θ)=πθ(yi,t∣x,yi,<t)πθold(yi,t∣x,yi,<t)r_{i,t}(\theta) = \frac{\pi_\theta(y_{i,t} \mid x, y_{i,<t})}{\pi_{\theta_\text{old}}(y_{i,t} \mid x, y_{i,<t})}ri,t​(θ)=πθold​​(yi,t​∣x,yi,<t​)πθ​(yi,t​∣x,yi,<t​)​
    si(θ)=(πθ(yi∣x)πθold(yi∣x))1/∣yi∣=exp⁡(1∣yi∣∑t=1∣yi∣log⁡ri,t(θ))s_i(\theta) = \left(\frac{\pi_\theta(y_i \mid x)}{\pi_{\theta_\text{old}}(y_i \mid x)}\right)^{1/|y_i|} = \exp\left(\frac{1}{|y_i|}\sum_{t=1}^{|y_i|}\log r_{i,t}(\theta)\right)si​(θ)=(πθold​​(yi​∣x)πθ​(yi​∣x)​)1/∣yi​∣=exp​∣yi​∣1​t=1∑∣yi​∣​logri,t​(θ)​
    이러한 구조에서는 토큰 수준의 미세한 신호가 응답 전체의 평균에 묻히면서, 개별 토큰에서 발생한 변화가 충분히 제동되지 못한 채 누적될 가능성이 있습니다. 예를 들어 띄어쓰기를 조금씩 빼는 편향이 매 스텝에서 완만하게 쌓일 수 있습니다.
    이러한 문제를 해결하기 위해 importance sampling 단위를 문장 단위에서 토큰 단위로 전환하였더니, 실제로 띄어쓰기를 생략하는 현상이 크게 줄어드는 것을 확인할 수 있었습니다.

    Case 2: 길이 디플레이션

    두번째 문제는 RL에서 가장 흔하게 나타나는 편향인 길이 편향이었습니다. 다만 방향은 정반대였는데, 흔히 길이가 길어지는 방향으로 편항이 생기는 것과 달리 저희 리워드 모델은 짧은 응답일수록 더 좋은 점수를 주는 편향을 가지고 있었고, 정책은 이를 빠르게 학습해 응답을 점점 더 짧게 만드는 쪽으로 움직였습니다. 1편의 DPO에서는 데이터 분포를 조정해 이 문제를 완화할 수 있었지만, online-learning에서는 정책이 분포 밖으로 직접 탐색해 나가기 때문에 데이터 조정만으로는 이러한 편향을 막기 어려웠습니다.
    이를 위해 저희는 길이 자체를 리워드에 포함하기로 하였습니다. 그런데 두 신호를 단순히 더해 GRPO에 넣는 방식에는 함정이 하나 있습니다. GRPO는 그룹 내 표준편차로 advantage를 정규화하는데, 분산이 큰 채널이 분산이 작은 채널의 신호를 압도해 버릴 수 있기 때문입니다. 그 결과 길이 신호가 리워드 모델 신호를 희석하거나, 반대로 길이 신호가 아예 무시되는 일이 생길 수 있습니다.
    이를 해결하기 위해 저희는 추가로 GDPO[14]에서 소개된 채널별 분리 정규화를 도입했는데, 이는 리워드 모델 점수와 길이 보상을 각각 독립적으로 그룹 정규화한 뒤 가중합하는 방식입니다. 이렇게 하면 각 채널이 자기 스케일로 정규화되기 때문에 한 채널이 다른 채널을 잠식하지 못하고, 채널별 기여도를 모니터링할 수도 있게 됩니다. 다만 길이 보상은 논문과는 다르게 정의하였습니다. 기존 GDPO 논문에서는 길이가 목표 길이 이내인지 여부만 보는 binary 보상을 사용합니다.
    Rlength={1if L≤l0otherwise\mathcal{R}_\text{length} = \begin{cases} 1 & \text{if } L \le l \\ 0 & \text{otherwise} \end{cases}Rlength​={10​if L≤lotherwise​
    하지만 제타처럼 자연스러운 대화가 핵심인 서비스에서는 '특정 길이 안에만 들어오면 만점'이라는 기준이 오히려 부자연스러운 응답을 유도할 수 있습니다. 저희는 서비스에 맞는 목표 길이를 정해 두고, 그 목표에서 벗어날수록 보상이 부드럽게 감소하도록 하는 길이 보상을 설계했습니다.
    이렇게 품질 신호와 길이 신호를 분리해 정규화하고, 길이 보상도 연속적으로 주자 정책이 길이 혹은 품질에만 집중하던 문제가 사라지고, 품질을 높이는 동시에 적절한 길이를 가진 문장을 출력하기 시작하였습니다.

    결과

    위에서 소개한 바와 같이 저희는 online-learning을 위해 (1) 필요한 인프라를 설계하고, (2) rollout과 학습 사이의 확률 불일치를 보정하고, (3) reward hacking을 해결하는 과정을 거쳤습니다. 이렇게 학습된 모델을 실제 유저 환경에서 1편에서 소개한 DPO 모델과 비교한 결과는 아래와 같습니다.
    notion image
    A/B 테스트 결과, GRPO 모델은 DPO 모델 대비 평균 이용시간과 리텐션 모두에서 유의미한 개선을 보였습니다. 정성적으로 분석하였을 때도 GRPO 모델은 DPO 모델에 비해 대화 맥락을 더 잘 따라가고, 단순히 무난한 답변을 생성하는 대신 현재의 분위기나 캐릭터성을 살려 대화를 이어갔으며, 이러한 차이는 제타처럼 재미와 몰입감이 중요한 서비스에서 큰 몰입감의 차이를 가져옵니다.
    또한 초반 실험에서 관찰했던 reward hacking 현상도 최종 모델에서는 크게 완화되었습니다. Sequence 단위 importance sampling을 사용할 때 나타났던 띄어쓰기 생략 현상은 token 단위 보정으로 줄어들었고, 짧은 응답만 선호하던 길이 디플레이션은 채널별 분리 정규화와 연속적인 길이 보상을 적용한 뒤 안정화되었습니다. 최종 A/B 테스트에서도 평균 응답 길이는 목표 범위 안에서 유지되었고, 반복 표현이나 띄어쓰기 이상과 같은 가드레일 지표 또한 안정화되는 것을 확인할 수 있었습니다.
    결과적으로 GRPO는 DPO 이후의 추가 개선 방법으로 충분히 효과적이었습니다. 다만 이번 실험에서 얻은 개선은 GRPO 알고리즘을 단순히 적용한, 그 알고리즘 하나만의 결과라기보다는, 비동기 학습 인프라, 확률 불일치 보정, reward hacking 해결 등 종합적인 해결책이 잘 어우러진 결과에 더 가깝습니다.

    마치며

    이번 글에서는 유저 시그널로 학습한 리워드 모델을 바탕으로 online-learning를 적용하면서, 실제 서비스 모델 학습에서 마주한 문제와 해결 과정을 소개했습니다.
    현재에 저희는 응답 한 턴의 품질을 넘어, 세션 전체에서 이어지는 재미와 몰입감을 보상으로 활용하는 방향을 더 깊게 연구하고 있습니다. 제타에서 좋은 대화는 결국 한 번의 답변만으로 만들어지기보다, 여러 턴에 걸쳐 자연스럽게 이어지는 흐름 속에서 만들어지기 때문입니다.
    앞으로도 저희는 더 큰 베이스 모델로의 스케일업과 함께, 세션 수준의 유저 시그널을 online-learning에 안정적으로 반영하는 방법도 계속 실험하고 공유드리겠습니다.
    스캐터랩 ML 팀은 모델 학습뿐 아니라, 이를 뒷받침하는 학습 인프라까지 직접 만들고 있습니다. 이런 문제를 함께 풀어보고 싶으시다면 아래 링크로 편하게 연락 주세요. 😃
    👉🏻 1:1 티타임 요청

    참고 문헌

    [1] Noukhovitch, Michael, et al. "Asynchronous RLHF: Faster and More Efficient Off-Policy RL for Language Models." ICLR. 2025.
    [2] Fu, Wei, et al. "AReaL: A Large-Scale Asynchronous Reinforcement Learning System for Language Reasoning." NeurIPS. 2025.
    [3] Sheng, Guangming, et al. "HybridFlow: A Flexible and Efficient RLHF Framework." EuroSys. 2025.
    [4] Hu, Jian, et al. "OpenRLHF: An Easy-to-use, Scalable and High-performance RLHF Framework." arXiv:2405.11143.
    [5] "LlamaRL: A Distributed Asynchronous Reinforcement Learning Framework for Efficient Large-scale LLM Training." arXiv:2505.24034.
    [6] Schulman, John, et al. "Proximal Policy Optimization Algorithms." arXiv:1707.06347.
    [7] Shao, Zhihong, et al. "DeepSeekMath: Pushing the Limits of Mathematical Reasoning in Open Language Models." arXiv:2402.03300.
    [8] Yao, Feng, et al. "Your Efficient RL Framework Secretly Brings You Off-Policy RL Training." Blog post. 2025. (후속 논문: Yao, Feng, et al. "On the Rollout-Training Mismatch in Modern RL Systems." OPT Workshop @ NeurIPS. 2025.)
    [9] Espeholt, Lasse, et al. "IMPALA: Scalable Distributed Deep-RL with Importance Weighted Actor-Learner Architectures." ICML. 2018.
    [10] Cui, Ganqu, et al. "The Entropy Mechanism of Reinforcement Learning for Reasoning Language Models." arXiv:2505.22617.
    [11] Yu, Qiying, et al. "DAPO: An Open-Source LLM Reinforcement Learning System at Scale." arXiv:2503.14476.
    [12] Lei, Ming, and Christophe Baehr. "A Comparative Theoretical Analysis of Entropy Control Methods in Reinforcement Learning for Reasoning Language Models." arXiv:2604.09676.
    [13] Zheng, Chujie, et al. "Group Sequence Policy Optimization." arXiv:2507.18071.
    [14] "GDPO: Decoupled Reward Normalization for Multi-Objective Group Relative Policy Optimization." arXiv:2601.05242.
     
    Share article
    Contents
    들어가며학습 인프라Rollout과 학습 사이의 확률 불일치점수는 잘 오르는데, 왜 생성은 이상해질까?결과마치며참고 문헌

    Scatter Lab

    RSS·Powered by Inblog