집에서도 가능한 프론티어급 완전 비동기 RL: Moongazer RL을 RTX 5090으로 구동하기
요약
본 글은 LLM 개발에서 발생하는 '대기 시간' 문제를 해결하는 완전 비동기 RL(Fully Asynchronous RL)의 개념을 소개합니다. 이는 응답 생성 과정과 모델 업데이트 과정을 분리하여, 생성이 진행되는 동안에도 학습이 지속되게 함으로써 효율성을 극대화합니다. 이를 직접 구현할 수 있는 Moongazer-RL 도구와 RTX 5090 구동 예시를 제시합니다.
핵심 포인트
- 완전 비동기 RL은 생성과 모델 업데이트 과정을 분리하여 대기 시간을 줄입니다.
- 에이전트 학습의 대기 시간은 '롤아웃 길이 차이'와 '생성-학습 순차 처리'에서 발생합니다.
- Moongazer-RL을 통해 완전 비동기 RL 메커니즘을 직접 구현하고 테스트할 수 있습니다.
- RTX 5090 환경에서의 구동 예시를 제공하여 실질적인 적용 방법을 안내합니다.
1. 완전 비동기 RL이란
안녕하세요! 여러분은 대규모 온라인 RL을 진행하고 계신가요?
대규모 RL을 수반하는 LLM 개발 연구실에서는, LLM의 온라인 RL 과정에서 '모델이 생각하는 것을 어떻게 기다릴 것인가'가 큰 과제가 되고 있습니다.
코드를 수정하여 테스트하거나, 검색 결과를 읽고 다음 검색을 수행하는 등의 에이전트 학습에서는 하나의 시도가 길어지며, 그룹별로 완료되는 시간도 일정하지 않습니다.
이러한 대기 시간을 줄이는 방법이 바로 **완전 비동기 RL(Fully Asynchronous RL)**입니다. 응답을 생성하는 처리와 모델을 업데이트하는 처리를 분리하여, 생성을 계속하면서 학습을 진행합니다.
정말 효율적이죠.
최근 모델들은 어디를 비동기로 처리하고 있는가
물론, 최근의 모든 모델이 동일한 구성으로 학습하고 있는 것은 아닙니다. Kimi K2, Kimi K2.5, MiniMax M2.5 등의 공개 자료를 읽어보면, 무엇을 병렬로 구동할지, 어디서 대기할지에 차이가 있습니다.
| 모델/기반 | 공개 자료 | 대기 시간 처리 방식 |
|---|---|---|
| Kimi K2 | 기술 보고 §3.3 | 생성과 학습을 번갈아 수행하는 동기형. 다수의 시도를 병렬로 구동하고, 긴 시도는 중간 저장하여 다음 반복에서 계속합니다 |
| ... | ||
| Moonshot AI의 Kimi K2는 동일한 GPU 위에서 생성 엔진과 학습 엔진을 전환하는 동기형입니다. 그럼에도 불구하고, 도구 응답을 기다리는 동안 다른 시도를 진행하거나, 길어지는 시도를 분할함으로써 효율성을 높이고 있습니다. 생성 내부를 병렬화하는 것만으로도 줄일 수 있는 대기 시간이 존재합니다. |
나아가 그 후속 모델인 Kimi K2.5에서는 이 에이전트 태스크의 비동기 실행을 대규모로 관리하고 있습니다. 다만, 태스크를 비동기로 구동하는 것과, 생성/모델 업데이트를 완전 비동기로 하는 것은 별개입니다. 또한, K2.5의 PARL은 에이전트에게 작업 분할 및 병렬 실행을 학습시키는 메커니즘이므로, 학습 기반의 동기 방식과는 구분하여 읽어야 합니다. (Kimi K2.5 기술 보고 §3・Appendix D)
본 기사에서 다루는 완전 비동기 RL은 생성과 모델 업데이트 사이의 스텝별 대기 시간을 없애는 방식입니다. 생성 측은 다음 데이터를 계속 만들어내고, 학습 측은 사용할 수 있는 데이터가 모였을 때 업데이트합니다.
이 메커니즘을 직접 시도해 볼 수 있도록 Moongazer-RL을 만들었습니다. Prime Intellect의 verifiers를 이용할 수 있으며, 학습 조건을 YAML로 지정할 수 있습니다. 후반부에서는 RTX 5090 한 장을 사용하는 예시를 설정과 스크립트 내용을 보면서 구동합니다.
2. 왜 완전 비동기 RL이 유리한가
에이전트 학습에는 두 종류의 대기 시간이 있다
모델이 환경과 상호작용하여 얻는 일련의 기록을 '롤아웃(Rollout)'이라고 부릅니다. 수학의 객관식 문제라면 문제에 대한 답변과 채점 결과가 있고, 코드를 작성하는 에이전트라면 중간 도구 호출이나 테스트 결과까지 포함됩니다.
첫 번째 대기 시간은 롤아웃들 간의 길이 차이에서 발생합니다. 몇 번의 검색으로 끝나는 문제도 있고, 여러 번 코드를 수정하고 테스트해야 하는 문제도 있습니다. 정해진 배치 전체 시도가 완료될 때까지 기다리는 구성에서는, 마지막 긴 시도가 학습 시작을 지연시킵니다.

그림 1에서 A가 끝났어도 B와 C의 처리가 남아있습니다. 여러 시도를 병렬로 구동할 수 있다면, 도구 대기 시간 동안 다른 생성을 진행할 수 있습니다. 하지만 배치 전체를 회수한 후에 학습을 진행한다면, 마지막 시도의 대기 시간은 여전히 남게 됩니다.
두 번째는 생성과 학습을 순차적으로 진행하기 위한 대기 시간입니다. 생성 전용과 학습 전용 GPU를 준비하더라도, 생성이 끝날 때까지 학습 측을 멈추고, 업데이트가 끝날 때까지 생성 측을 멈춘다면, 양쪽 모두 충분히 사용하지 못합니다.
생성과 학습을 중첩하여, 완료된 데이터부터 사용하기
완전 비동기 RL에서는 완료된 롤아웃을 큐(Queue)에 전달하고, 학습 측이 거기서 데이터를 가져옵니다. 충분한 데이터가 모이면, 다른 롤아웃이 진행되는 와중에도 학습할 수 있습니다. 생성 측 역시 다음 업데이트를 일괄적으로 기다리지 않고 작업을 계속합니다.

그림 2는 생성용과 학습용 GPU를 분리하여 설명한 예시입니다. 각 배치 생성/채점에 4시간, 모델 업데이트에 2시간이 걸린다고 가정하면, 동기형에서는 3번의 업데이트에 총 18시간이 걸립니다. 처리를 중첩하면 14시간으로 종료됩니다. 이는 스케줄 비교이며, 동일한 정확도에 도달하는 시간을 측정한 결과는 아닙니다.
충분히 많은 횟수를 반복할 경우, 동기형의 1배치당 시간은 대략 '생성 시간 + 학습 시간'입니다. 독립적인 계산 자원에서 이상적으로 중첩된다면, 비동기형은 '생성 시간과 학습 시간 중 더 긴 쪽'에 가까워집니다. 통신이나 큐 대기를 제외한 근사치이지만, 왜 대기 시간을 줄이는 것이 유리한지를 파악할 수 있습니다.
나아가, 완료된 처리의 묶음(batch)에 다음 작업을 넣으면, 롤아웃 길이(rollout length)가 들쭉날쭉해도 생성 측을 계속 구동할 수 있습니다. 다만, GRPO처럼 동일 문제에 대한 여러 답변을 비교하는 학습에서는, 그 비교에 필요한 그룹을 모은 후에 전달합니다.
가중치를 반영하기 위한 대기 시간도 무시할 수 없습니다. INTELLECT-3의 기술 보고서에는, 생성 도중에 가중치 업데이트를 사용하지 않을 경우, 스텝(step) 시간이 2배 이상 증가했다고 보고되어 있습니다. 이는 H200을 480개, 시퀀스 길이 65,536으로 사용한 조건에서의 비교입니다. 이것은 '완전 비동기화하면 무조건 2배 빨라진다'는 수치가 아니라, 긴 생성을 끝낼 때까지 가중치 업데이트를 기다리는 비용을 보여주는 예시입니다. INTELLECT-3 기술 보고서 §3.3
생성 측과 학습 측을 개별적으로 조정할 수 있다
LLM의 생성(generation)과 학습(training)에서는, 잘하는 처리 방식이나 메모리 사용법이 다릅니다. 생성 측은 다수의 요청과 KV 캐시를 관리하고, 학습 측은 역전파(backpropagation)를 위한 활성화 값(activations)이나 옵티마이저 상태(optimizer state)를 유지합니다. 이 둘을 분리하면, 생성 측에는 추론 엔진(inference engine)을, 학습 측에는 학습용 구현을 사용하여 각각 조정할 수 있습니다.
prime-rl의 2026년 6월 해설에서도, 생성과 학습을 분리하여 최적화하는 방침을 보여주고 있습니다. 장시간 에이전트 태스크에서는, 생성이 끝날 때까지 기다리지 않고 가중치를 반영하는 것이 이 구성을 가능하게 하는 요소가 됩니다. RL at 1T Scale
어떤 것을 조정해야 할지는 대기 시간과 큐 상태를 보면 좁혀질 수 있습니다.
| 관측할 상태 | 추정 가능한 원인 | 확인할 장소 |
|---|---|---|
| 큐가 비어 있고, 학습 측이 자주 기다림 | 데이터 공급이 학습을 따라가지 못함 | 생성 속도, 도구/채점 시간, 제외한 그룹 수 |
| ... | ||
| 비동기화한다고 해서 느린 처리 자체가 사라지는 것은 아닙니다. 느린 처리를 기다리는 동안 다른 작업을 진행할 수 있게 되고, 다음에 어디를 개선해야 할지도 찾기 쉬워집니다. |
데이터의 오래됨과 완성 순서에 따른 편향 관리하기
처리를 독립적으로 진행하면, 생성 측과 학습 측에서 모델 세대가 어긋납니다. 예를 들어 버전 10의 모델로 답변을 만들고 있는 동안, 학습 측이 버전 11로 넘어갈 수 있습니다. 이 차이를 staleness라고 부릅니다.

오래된 모델이 만든 데이터로 현재 모델을 업데이트하기 때문에, 생성 시와 학습 시의 방책이 다른 오프폴리시(off-policy) 학습이 됩니다. Moongazer RL에서는, 학습에 사용하기 직전에 세대 차이를 확인하고 지정한 상한을 초과하는 데이터를 제외합니다. 큐 용량도 제한하여, 생성 측이 너무 앞서 나가는 것을 막습니다.
다만, 세대가 같다고 해서 생성 측과 학습 측이 완전히 일치한다고 할 수는 없습니다. 예를 들어 이번 예시에서는, 생성 측이 bf16이고, 학습 측이 4bit입니다. 정밀도나 계산 구현이 다르면, 같은 토큰에 부여하는 확률에도 차이가 생깁니다.
따라서, 생성된 토큰 ID와 생성 시의 확률의 로그값인 logprob를 기록하고, 학습 시의 확률과의 차이를 보정합니다. Kimi K2.5의 기반에서도, 토큰을 그대로 전달하고 추론 시의 logprob를 기록하여 train–inference mismatch의 보정에 사용하는 설계가 설명되어 있습니다. Kimi K2.5 기술 보고서 Appendix D
이번 예시에서는, 토큰별 확률 비율에 상한을 두는 truncated importance sampling을 사용합니다. 개념적인 보정 가중치는 다음 형태입니다.
또 하나 주의할 점은, 빨리 끝나는 데이터로의 편향입니다. 완성된 순서대로 계속 사용하면, 짧거나 간단한 태스크가 먼저 학습되고, 긴 태스크가 뒤로 밀릴 수 있습니다. MiniMax M2.5의 Forge에서는, 투입 순서로 나눈 범위 중에서, 완성된 데이터를 먼저 사용하는 Windowed FIFO를 채택하여, 엄격한 순서 대기와 자유로운 비동기 실행 사이의 중간 지점을 선택했습니다. Forge 공식 해설 §3.1
비동기 RL에서는, GPU 가동률과 어떤 데이터로 학습할지를 함께 고려해야 합니다. 생성 횟수뿐만 아니라, 학습에 사용된 태스크, 세대 차이, 제외된 이유까지 기록하는 것이 그 목적입니다.
이 구성을 하나의 GPU에서 시도하기
지금까지의 속도 설명은, 생성 측과 학습 측이 다른 계산 자원을 사용할 수 있는 경우를 중심으로 하고 있습니다. 기본적으로 완전 비동기 RL은 대규모 RL을 하는 경우가 아니라면 잘 사용되지 않으며, Miles나 Slime, veRL 등은 여러 노드를 사용하는 것 같은 대규모 환경에 최적화되어 있습니다.
그럼에도 불구하고, 생성(Generation), 채점(Scoring), 학습(Training), 가중치 배포(Weight Distribution)를 분리하여 작동시키는 메커니즘을 시도해보고 싶습니다. 우선 작은 모델과 LoRA로 일련의 처리를 구동시켜 어디에서 대기하는지 확인합니다. 거기서부터 환경이나 보상, 학습 방법을 변경하여 실험할 수 있도록 Moongazer RL을 만들었습니다.
3. Moongazer RL 소개 및 사용법
이번에 만든 Moongazer RL은 계산 자원이 풍부하지 않은(潤沢ではない) RL 환경에서 프론티어 수준의 RL을 수행하기 위해 제작되었으며, 수많은 RL 환경을 사용할 수 있고 각종 조정이 쉽고, 1~3개의 GPU 정도의 환경에서 잘 작동하도록 설계되었습니다.
verifiers 환경을 사용해 실험하기
Moongazer RL은 생성 측에 vLLM을, 학습 측에 Unsloth와 TRL을 결합하여 만들었습니다. 베이스 모델에 추가하는 작은 파라미터인 LoRA 어댑터를 학습하고, 그 업데이트를 vLLM에 반영합니다.
환경으로는 Prime Intellect의 verifiers v1 API를 이용합니다. verifiers는 LLM의 학습/평가 환경을 만들기 위한 라이브러리입니다. 태스크 출제, 상호작용 진행, 채점을 환경 측에 맡기고, Moongazer RL은 모델 호출과 학습용 데이터 변환을 담당합니다.
따라서 대응하는 verifiers v1 환경을 도입하면, 환경의 처리를 Moongazer RL 전용으로 다시 작성하지 않고도 이용할 수 있습니다. 문제나 보상을 바꾼 실험에서도 생성-학습-가중치 배포의 메커니즘을 공통적으로 사용할 수 있습니다.
학습 조건은 YAML에 정리합니다. 모델, 환경, 생성 수, 학습률, 비동기 처리 설정을 같은 파일에서 관리할 수 있으며, 실행 시 --set으로 개별적으로 덮어쓸 수 있습니다. 예를 들어 학습률만 바꿔서 비교할 때도 구동 스크립트를 수정할 필요가 없습니다.
학습 알고리즘 설정도 분리되어 있습니다. 보상으로부터 각 답변의 상대적인 좋음을 계산하는 방법, 정책의 목적 함수(objective function), off-policy 보정 등을 지정할 수 있습니다. 예를 들어 advantage 계산에는 group_zscore나 rloo, 목적 함수에는 grpo_clip이나 cispo를 선택할 수 있습니다. 조합의 제약은 설정 검증(setting validation)에서 확인할 수 있습니다. 환경과 학습 조건을 조금씩 바꾸는 실험에 사용하기 좋은 구성으로 만들었습니다.
RTX 5090을 위해 준비한 예제
여기서는 examples/rtx5090-math-env를 사용합니다. 모델은 google/gemma-4-E2B-it이고, 환경은 verifiers v1 버전의 math-env입니다. 수학 문제에 답변하고, math-verify로 최종 답변을 대조하여 보상을 부여합니다.
vLLM과 학습 측은 1장의 RTX 5090을 공유합니다. vLLM은 bf16으로 생성하고, 학습 측은 베이스 모델을 4bit으로 로드하여 LoRA를 업데이트합니다. 학습 측에서는 기본 텍스트 전용 설정에 의해 일부 모듈을 CPU로 옮기는 처리도 사용합니다. GPU 메모리와 호스트(host) 측 메모리 양쪽을 사용하는 구성입니다.

그림 4의 메모리 사용량은 후술할 12 스텝 실행 시 측정된 값입니다. vLLM용 영역을 확보한 후에, 4bit 학습 모델을 로드합니다.
실행에는 Linux, RTX 5090 및 대응하는 NVIDIA 드라이버, Python 3.12, uv, Git이 필요합니다. 모델 다운로드량은 약 10.3GB이며, 별도로 Python 환경이나 데이터셋, 로그를 저장할 용량도 필요합니다. 이 기사의 코드와 설정은 2026년 10월 3일자 커밋 d6e61e1을 기반으로 합니다.
1. 리포지토리를 가져와 환경을 준비하기
먼저 리포지토리를 가져옵니다. 아래의 작업은 리포지토리의 루트 디렉터리에서 수행합니다.
git clone https://github.com/foxn2000/moongazer-rl.git
cd moongazer-rl
git checkout d6e61e128ce9d31542fdae50cb7ce3f6e21382e3
여기서는 기사에서 설명하는 버전에 고정합니다.
환경을 준비하는 스크립트는 setup.sh입니다.
examples/rtx5090-math-env/setup.sh
이 스크립트는 먼저 uv sync --extra unsloth를 실행하여 리포지토리 직하단의 .venv에 학습용 의존 패키지를 넣습니다. 그 후, v1 버전의 math-env를 example 내의 envs/에 도입하고, PYTHONPATH
을 통해 로드할 수 있도록 합니다.
여기서 사용하는 math-env는 Prime Intellect의 research-environments 리포지토리에 있는 v1 버전을 사용합니다. 이 example이 참조하는 Environments Hub 버전은 v0 API이기 때문에, 동일한 이름의 환경으로 대체할 수는 없습니다. setup.sh에서는 가져오는 커밋도 고정하고 있습니다.
다음으로, vLLM 전용 Python 환경을 만듭니다. setup.sh의 해당 부분은 다음과 같습니다. 이는 처리를 읽기 위한 발췌이며, $VLLM_VENV는 스크립트 내에서 지정하는 vllm-env/ 경로입니다.
uv venv --python 3.12 "$VLLM_VENV"
VIRTUAL_ENV="$VLLM_VENV" uv pip install "vllm==0.28.0"
환경을 분리하는 이유는, 이 구성의 vLLM과 Unsloth가 요구하는 torch나 transformers 버전이 다르기 때문입니다. 두 프로세스는 HTTP를 통해 통신합니다.
스크립트 마지막에서는 mg-rl validate로 설정을 검증합니다. 이는 설정과 도입된 패키지의 일관성을 확인하는 처리입니다. 의존 패키지를 로드할 수 없는 항목은 경고가 되므로, 종료 코드 외에 경고 내용도 확인해야 합니다. GPU 메모리에 들어갈 수 있는지는 실행 시점에 확인합니다.
2. 베이스 모델 다운로드하기
다음으로, 모델을 가져옵니다.
examples/rtx5090-math-env/download-model.sh
download-model.sh에서는 Hugging Face의 CLI를 사용하여 google/gemma-4-E2B-it를 다운로드합니다. 저장 경로는 examples/rtx5090-math-env/models/gemma-4-E2B-it/입니다. 이 경로를 vLLM과 학습 측 양쪽 모두가 참조합니다.
3. YAML로 모델과 환경 확인하기
설정 파일은 math-env-async-1gpu.yaml입니다. 아래에서는 포함된 파일에서 주요 항목을 발췌하여 설명합니다. 실행에는 포함된 전체 파일을 사용합니다.
model:
name: models/gemma-4-E2B-it
load_in_4bit: true
...
load_in_4bit: true는 학습 측의 베이스 모델을 4bit로 로드하는 설정입니다. lora.r: 16은 LoRA의 랭크(rank)로, 추가하여 학습할 파라미터의 규모와 관련됩니다. max_seq_length는 입력과 출력을 포함한 시퀀스 길이의 상한입니다.
renderer에서는 Gemma 4에 맞춘 프롬프트 구성과 출력 해석을 지정합니다. 이 example은 enable_thinking: false입니다.
환경 측은 다음 설정입니다.
environment:
taskset:
id: math-env
...
taskset으로 수학 문제와 채점 방법을 지정하고, harness로 모델과의 상호작용을 진행합니다. 이 설정에서는 도구를 사용하지 않는 1턴의 답변을 생성합니다. `
비동기 처리 설정은 async 섹션에 있습니다.
async:
max_staleness: 1
queue_capacity: 4
...
async 섹션이 있으면, mg-rl train은 비동기 학습 루프를 사용합니다. 역할(role) 생성(rollout)을 담당하는 두 개의 producer가 그룹을 만들고, 최대 4개의 그룹 큐로 전달합니다. 학습 측은 큐에서 2개의 그룹을 가져와 업데이트합니다.
max_staleness: 1은 학습에 사용되는 데이터의 세대 차이를 최대 1로 제한하는 설정입니다. 예를 들어 학습 측이 버전 11이라면, 버전 10으로 만든 데이터는 사용할 수 있지만, 버전 9의 데이터는 제외됩니다. 각 그룹 생성 중에는 동일한 LoRA 어댑터를 사용하여 하나의 답변 도중에 모델의 세대가 바뀌지 않도록 합니다.
drop_zero_std_groups: true는 모든 답변의 보상이 같은 그룹을 학습 대상에서 제외하는 설정입니다. 이번처럼 그룹 내 보상 차이를 사용하는 학습에서는, 만점이나 오답만으로 이루어진 그룹에서는 차이를 얻을 수 없기 때문에 다른 그룹을 생성합니다. 이로 인해 실제 생성 본수는 "step 수 × 16본"보다 많아집니다.
학습 측에서는 주로 다음 설정을 사용합니다.
trainer:
backend: unsloth-native
args:
...
scale_rewards: group은 그룹 내에서 보상을 표준화하는 설정입니다. loss_type: dapo는 이 구현에서 클립된 정책 목적 함수와 학습 대상 토큰 수에 따른 손실 정규화에 대응합니다. beta: 0.0이므로, 이 예시에서는 참조 모델과의 KL 페널티를 추가하지 않습니다.
생성 측에는 로컬의 vLLM을 지정합니다.
policy:
backend: vllm-server
args:
...
processed_logprobs는 샘플링 처리를 반영한 확률을 받기 위한 설정입니다. 생성 시의 토큰 ID와 logprob를 그대로 학습에 전달하여, 앞 절의 off-policy 보정에 사용합니다. bf16으로 생성하고 4bit로 학습하는 경우, 같은 세대라도 확률이 일치한다고 할 수 없으므로 이 차이도 확인 대상이 됩니다.
전체 설정은 example의 YAML을 참조하고, 각 키에 대한 설명은 설정 및 CLI를 참고해 주십시오.
5. 실행 스크립트를 읽고 학습하기
실행에는 run-async-1gpu.sh를 사용합니다. 처음에는 12 step으로 나누어 실행합니다.
examples/rtx5090-math-env/run-async-1gpu.sh \
--set rollout.max_steps=12
--set은 해당 실행에 한해서 YAML의 값을 덮어씁니다. 지정하지 않으면, 포함된 설정의 200 step이 됩니다.
run-async-1gpu.sh는 먼저 serve-vllm.sh를 시작합니다. vLLM이 GPU 메모리를 확보한 후에 학습 측을 로드하기 때문에 순서가 이렇습니다. /v1/models에 접근하여 실행 확인을 하고, 응답이 돌아온 후에 mg-rl train을 실행합니다.
serve-vllm.sh에서는 vLLM에 다음 옵션을 전달하고 있습니다. 아래는 주요 부분의 발췌입니다.
--dtype bfloat16 \
--max-model-len "$MAX_MODEL_LEN" \
--gpu-memory-utilization "$GPU_UTIL" \
...
GPU_UTIL의 기본값은 0.5입니다. vLLM 측의 메모리 예산을 낮추고, 같은 GPU에 학습 측을 올리기 위한 설정입니다. 실제 프로세스 전체 사용량은 이 값만으로 결정되지 않으므로, 실행 중 사용량도 확인합니다.
LoRA 관련 옵션은 여러 세대의 어댑터를 다루기 위해 지정했습니다. 또한, 스크립트는 VLLM_ALLOW_RUNTIME_LORA_UPDATING=True를 설정합니다. 이를 통해 학습한 어댑터를 /v1/load_lora_adapter로 로드할 수 있습니다. 새로운 어댑터가 사용 가능해지면, 이후의 역할 생성(rollout)을 새 세대로 전환합니다.
학습 측을 시작하는 부분은 다음과 같습니다. $HERE는 example 디렉토리이고, $CONFIG는 설정 파일이며, $GPU는 기본적으로 0을 가리킵니다.
vLLM과 학습 측에 동일한 CUDA_VISIBLE_DEVICES를 지정하여 하나의 GPU를 공유합니다. PYTHONPATH는 앞서 도입한 math-env를 로드하기 위한 것입니다. 마지막의 "$@"을 통해 실행 시 지정된 --set 등의 인수를 학습 명령어에 전달합니다.
스크립트는 example 디렉토리로 이동한 후에 학습을 시작합니다. 따라서 YAML 내부의 models/gemma-4-E2B-it나 runs/math-env-async-1gpu도 example을 기준으로 해결됩니다. 학습 명령어가 종료되면, 스크립트가 구동한 vLLM에도 종료 시그널이 전송됩니다.
6. 로그에서 학습과 비동기 처리를 확인하기
실행 결과는 examples/rtx5090-math-env/runs/math-env-async-1gpu/에 저장됩니다. 리포지토리의 루트 디렉토리에서 다음 명령어로 확인할 수 있습니다.
uv run --no-sync mg-rl inspect \
examples/rtx5090-math-env/runs/math-env-async-1gpu
uv run --no-sync mg-rl async-report \
...```
`inspect`에서는 실행 상태나 기록된 결과를 확인할 수 있습니다. `async-report`에서는 생성과 학습의 시간적 중첩, staleness 분포, 가중치 전송 소요 시간, 처리량(throughput) 등을 확인할 수 있습니다.
개별 답변과 보상은 `rollouts.jsonl`에, 각 step의 학습 결과는 `steps.jsonl`에, 생성 시작이나 가중치 전송 같은 이벤트는 `events.jsonl`에 남습니다. 보상이 바뀔 때 실제 답변까지 추적할 수 있으므로 설정을 변경하면서 원인을 조사할 수 있습니다. 저장 형식은 Run 디렉토리 설명에 정리되어 있습니다.
#### 7. RTX 5090으로 12 step 구동 결과
RTX 5090을 1개 사용하여 이 설정으로 12 step을 구동했습니다. 473초 만에 완료되었으며, GPU 메모리 피크는 28.7GB였습니다.
| 항목 | 결과 |
|---|---|
| 실행 시간 | 473초 |
| ... | |
98.3%는 학습 처리 구간 중 모델 호출 구간과 겹친 비율입니다. 이는 GPU 사용률이나 GPU 커널의 동시 실행률을 나타내는 값은 아닙니다. 모델 호출에는 응답 대기 시간도 포함되므로, 이 값으로 확인할 수 있는 것은 두 구간이 시간적으로 겹치고 있다는 점뿐입니다.
반면, 학습 측의 대기 시간은 61.8%로, 이번 실행에서는 롤아웃 공급이 병목(bottleneck)이 되고 있습니다. 비동기 메커니즘이 작동하더라도 생성과 학습 처리량의 균형에 따라 대기 시간은 남습니다.
이번에 확인한 것은 1개의 GPU로 비동기 학습 루프를 완주할 수 있다는 것입니다. 동기 방식과의 속도 비교나, 학습을 통한 정확도 향상 평가는 아직 진행하지 않았습니다. 그 부분을 확실히 하려면, 학습을 길게 돌리는 것뿐만 아니라, 동일 조건의 동기 실험이나 다른 평가 데이터와의 비교가 필요합니다.
## 4. 요약
완전 비동기 RL에서는 롤아웃 생성과 모델 업데이트를 독립적으로 진행하여, 생성/학습/가중치 전송 대기 시간을 줄입니다. 한편으로는, 생성 시와 학습 시의 모델에 차이가 발생하므로 데이터의 구식화(staleness) 관리와 확률 보정이 필요합니다.
Moongazer RL에서는 이 처리를 verifiers v1 환경, vLLM, Unsloth・TRL로 조합했습니다. 환경과 학습 조건을 YAML로 지정하고 로그를 보면서 실험을 진행할 수 있습니다.
RTX 5090용 example에서는 수학 답변 생성과 LoRA 학습을 1개의 GPU에 올렸습니다. 우선 12 step을 구동하여, 답변, 보상, 생성과 학습의 중첩을 확인하는 것부터 시작할 수 있습니다. 그 설정을 기반으로 문제나 보상, 학습 조건을 변경하며 자신의 환경에서 완전 비동기 RL을 시도해 보세요.
코드와 example은 foxn2000/moongazer-rl에서 공개하고 있습니다.
### Discussion

AI 자동 생성 콘텐츠
본 콘텐츠는 Zenn AI의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기