RL · 2026-09-25
비동기 RL과 오래된 데이터
생성과 학습을 겹치는 비동기 RL에서 배포 버전이 어떻게 바뀌고, 생성 중이거나 버퍼에 쌓인 경험을 언제 제외하는지 살펴봅니다.
앞 글에서는 경험을 만든 정책과 학습하는 정책의 확률이 달라질 때 이를 어떻게 보정하는지 보았습니다. 이번에는 그 차이가 벌어지는 실행 과정을 살펴보겠습니다. 학습기가 준비된 경험으로 모델을 업데이트하는 동안 추론기가 다음 경험을 계속 만들면, 두 작업이 서로 기다리는 시간을 줄일 수 있습니다. 대신 방금 완성된 경험도 이미 이전 가중치로 만든 데이터일 수 있습니다.
비동기 RL은 생성과 학습을 겹쳐 진행하고, 그 과정에서 오래된 경험을 관리하는 문제입니다. 먼저 두 작업을 어떻게 겹치는지 보고, 학습 스텝과 추론기에 반영된 버전을 구별하겠습니다. 이어 한 경험이 생성 중과 버퍼 대기 중에 어떻게 오래되는지, Miles의 기본 버퍼가 어떤 순서로 꺼내고 어느 시점에 제외하는지 살펴보겠습니다.
생성과 학습을 겹치기
추론용 GPU와 학습용 GPU를 분리해 놓았다고 두 작업이 자동으로 동시에 진행되는 것은 아닙니다. 생성이 모두 끝나야 학습을 시작하고, 학습과 가중치 동기화가 끝나야 다음 생성을 시작한다면 한쪽이 일하는 동안 다른 쪽은 기다립니다.
그림 1은 분리된 GPU에서 동기 실행과 비동기 실행을 비교합니다. G1과 G2는 각각 한 번의 학습에 필요한 완료 그룹 묶음입니다. 막대 길이는 실행 순서를 설명하기 위한 예이며, 측정한 성능 수치가 아닙니다.

동기 실행에서는 G1을 생성한 뒤 학습하고, 새 가중치를 추론기에 반영한 다음 G2를 생성합니다. 비동기 실행에서는 G1을 학습하는 동안 G2를 생성합니다. 준비된 경험을 버퍼에서 가져가는 학습 루프와, 다음 경험을 만들어 넣는 생성 루프가 독립적으로 진행하는 것입니다.
Miles의 fully async 구성에서도 가중치를 반영하는 weight sync 동안에는 생성을 잠시 멈춥니다. 또 G1 학습을 마쳤는데 G2가 아직 준비되지 않았다면 학습기는 기다립니다. 비동기는 각각의 계산을 더 빠르게 만드는 기법이 아니라, 동시에 진행할 수 있는 작업을 겹쳐 대기를 줄이는 실행 방식입니다. Miles의 fully async 실행 흐름
여기서 학습에 필요한 준비 조건은 그대로 유지됩니다. GRPO처럼 같은 질문의 여러 응답을 비교하는 구성이라면, 필요한 샘플과 평가 결과가 갖춰진 그룹을 사용합니다. 다른 그룹의 생성이 계속되고 있다는 이유로 미완료 그룹의 응답 하나를 임의로 꺼내 학습하는 것은 아닙니다.
학습 스텝과 배포 버전
모델의 진행 상태를 말할 때는 두 기록을 구별해야 합니다. 학습 스텝은 학습기가 가중치를 업데이트하며 진행한 기록이고, 이 글에서 말하는 배포 버전은 새 가중치를 추론기에 반영한 기록입니다. 여기서 배포는 외부 사용자에게 서비스를 공개한다는 뜻이 아니라, RL 시스템 내부의 생성 모델을 갱신한다는 뜻입니다.
그림 2에서는 학습 스텝마다 한 번 업데이트하고, 세 스텝마다 sync한다고 가정합니다. step 9의 가중치를 추론기에 반영한 상태를 v3이라고 부르겠습니다.

학습기는 step 10과 step 11을 수행해 가중치를 바꿉니다. 그러나 아직 sync하지 않았으므로 추론기는 계속 step 9의 가중치인 v3으로 생성합니다. step 12에서 sync하면 그때의 가중치를 한 번에 반영하고 배포 버전이 v4가 됩니다. 학습은 세 번 진행했지만 배포 버전은 한 번 증가한 것입니다.
그러면 step 11에서 v3으로 만든 경험을 학습하려 할 때, 학습 모델을 무엇이라고 불러야 할까요? 이 예에서는 “step 11의 학습 가중치이며, 마지막으로 추론기에 반영한 배포 버전은 v3”이라고 구별하면 됩니다. 학습 가중치까지 단순히 v3이라고 부르면 두 번의 업데이트가 가려집니다.
Miles의 fully async 버퍼가 사용하는 staleness는 다음 배포 버전 간격입니다.
현재 추론기에 반영된 배포 버전 − 그룹 안에서 가장 오래된 생성 버전따라서 step 11에서 v3으로만 만든 그룹의 간격은 3 − 3 = 0입니다. 학습 가중치는 이미 두 번 바뀌었어도 이 지표는 0일 수 있습니다. Miles 문서도 이 값을 배포된 rollout 버전 단위로 설명하며, 가중치를 매 스텝 반영하지 않으면 학습 스텝 수와 같지 않다고 구별합니다. Miles의 staleness 지표 정의
이 기준은 Miles의 해당 버퍼를 설명하는 기준입니다. 다른 시스템의 staleness가 반드시 같은 카운터를 사용한다고 가정하면 안 됩니다. 또한 버전 간격은 확률 분포의 거리가 아닙니다. 간격이 0이어도 최신 학습 가중치와 다를 수 있고, 앞 글에서 본 엔진 간 계산 차이도 남을 수 있습니다.
생성 중에도 경험은 오래된다
경험이 오래되는 시점은 생성이 끝난 뒤만이 아닙니다. 긴 응답이나 도구를 사용하는 여러 턴의 실행이 계속되는 동안, 다른 경험을 학습한 가중치가 추론기에 반영될 수 있습니다. 완료된 뒤 버퍼에서 기다리는 동안에도 다음 배포가 일어날 수 있습니다.
그림 3은 v3으로 시작한 경험 하나를 따라갑니다. 중간에 생성을 잠시 멈춰 v4를 반영하고, 앞부분을 유지한 채 이어 생성하는 경우입니다.

앞부분 토큰은 v3으로, 뒷부분 토큰은 v4로 만들었습니다. 후속 생성을 v4로 했다고 앞부분이 v4의 경험으로 바뀌지는 않습니다. 이 경험의 가장 오래된 생성 버전은 여전히 v3이며, 완료 시점의 간격은 4 − 3 = 1입니다.
이처럼 하나의 응답 안에 여러 생성 버전이 들어가는 실행도 가능합니다. Miles의 기본 retract 모드는 sync를 위해 진행 중인 생성을 대기열로 되돌리고, 새 가중치로 KV 캐시를 다시 계산한 뒤 생성을 이어갑니다. 앞부분의 생성 기록과 이후에 생성하는 토큰은 구별됩니다. 모든 경험이 반드시 여러 버전을 거친다는 뜻은 아닙니다. Miles의 생성 일시정지와 재개
이제 경험이 완료되어 버퍼에 들어갑니다. 기다리는 동안 v5가 배포되어도 저장된 생성 기록은 그대로입니다. 꺼낼 시점의 간격은 5 − 3 = 2가 됩니다. 생성 중에 한 번, 버퍼에서 기다리는 동안 한 번 더 버전 간격이 벌어진 것입니다.
실제 Miles의 기본 버퍼는 개별 경험이 아니라 그룹 전체를 검사합니다. 그림의 경험을 포함한 그룹에서 다른 샘플의 생성 버전도 v3보다 오래되지 않았다고 하면, 그룹의 가장 오래된 버전 역시 v3입니다. 다른 샘플에 v2가 하나라도 있으면 v2를 기준으로 계산합니다. 그룹을 가장 오래된 생성 기록보다 더 신선하게 취급하지 않는 보수적인 기준입니다. Miles v0.1 §2.2.2
버퍼의 입구와 출구에서 검사하기
Miles의 기본 버퍼는 먼저 들어온 그룹부터 꺼내는 FIFO 방식입니다. 이는 생성 요청을 시작한 순서와는 다릅니다. A를 먼저 시작했더라도 B, C, A 순서로 준비되어 버퍼에 들어왔다면 B부터 꺼냅니다. 버퍼의 순서는 대기 중인 그룹 중 무엇을 먼저 검사할지를 정합니다.
그림 4는 Miles v0.1 논문이 설명하는 세 가지 제외 사유를 검사 시점에 따라 나눈 것입니다. 앞의 두 조건은 그룹이 도착할 때, staleness는 학습용으로 꺼낼 때 확인합니다.

첫째는 생성을 끝내지 못한 그룹입니다. 예를 들어 제한 시간 안에 에이전트 실행을 마치지 못해 중단 상태로 반환된 그룹입니다. 아직 정상적으로 생성 중인 그룹을 버퍼에서 미리 꺼내 버린다는 뜻이 아닙니다. 생성 작업이 포기한 결과를 입구에서 확인하고 학습 대상에서 제외합니다.
둘째는 사용자 필터가 거부한 그룹입니다. 예를 들어 그룹의 모든 응답이 같은 보상을 받아 상대적인 어드밴티지가 모두 0이라면, 그런 그룹을 제외하도록 필터를 설정할 수 있습니다. 어떤 결과를 제외할지는 학습 설정이 정합니다.
셋째는 허용한 staleness를 넘은 그룹입니다. 이 조건은 입고 이후에도 달라지므로 꺼낼 때 확인합니다. 그림에서는 현재 배포 버전이 v5이고 허용 간격이 1입니다. 맨 앞 B의 가장 오래된 생성 버전이 v3이면 5 − 3 = 2로 제외합니다. 다음 C가 v4이면 5 − 4 = 1로 통과해 학습 배치에 들어갑니다. 필요한 수만큼 사용할 그룹이 모이지 않으면 학습기는 더 기다립니다. Miles v0.1의 제외 조건, Table 1, Miles 기본 버퍼 구현
즉, FIFO는 입고 순서대로 검사를 진행한다는 뜻이지, 입고된 데이터가 모두 학습된다는 뜻은 아닙니다. 버퍼에 들어올 때 신선했던 그룹도 소비 시점에는 제외될 수 있습니다. 위 세 가지는 논문의 설명 범위이며, 최신 구현의 모든 유효성 검사나 학습 배치를 구성한 뒤의 추가 필터를 열거한 목록은 아닙니다.
대기와 폐기 비용을 함께 조절하기
운영에서는 버퍼 용량, 허용 staleness, weight sync 주기를 함께 봐야 합니다. 각각 보관할 양, 소비할 수 있는 조건, 생성 모델을 갱신하는 간격을 정합니다.
버퍼가 크면 생성과 학습의 일시적인 속도 차이를 더 많이 흡수할 수 있습니다. 대신 경험이 오래 기다릴 여지도 커집니다. Miles는 버퍼가 차면 새 그룹을 넣는 작업이 공간을 기다리게 합니다. 이렇게 소비 속도에 맞춰 생산을 제한하는 것을 backpressure라고 합니다.
허용 staleness를 줄이면 오래된 경험을 더 적극적으로 제외합니다. 그러나 그만큼 이미 생성에 쓴 비용이 학습으로 이어지지 않거나, 학습기가 새 데이터를 기다릴 수 있습니다. Miles에서는 --max-weight-staleness로 이 제한을 설정하며, 설정하지 않으면 해당 필터는 꺼져 있습니다.
sync를 자주 하면 이후 생성이 최신 학습 가중치를 더 빨리 따라갑니다. 대신 가중치 전달과 생성 일시정지 비용이 늘 수 있습니다. 드물게 하면 이전 가중치로 생성하는 구간이 길어집니다. 이때 동일한 배포 버전 간격 1도 그 사이에 들어 있는 학습 업데이트 수가 달라질 수 있으므로, sync 주기를 바꾸고 staleness 숫자만 그대로 비교해서는 안 됩니다. Miles의 --update-weights-interval은 학습 루프의 반영 주기를 정하며, 루프 한 번에 실제 optimizer 업데이트를 몇 번 하는지도 함께 확인해야 합니다.
제외된 과제를 다시 생성할지도 선택할 수 있습니다. Miles의 retry는 중단되었거나 너무 오래된 그룹의 원래 프롬프트를 데이터 소스로 돌려 새 경험을 생성하게 합니다. 같은 오래된 기록을 그대로 버퍼에 다시 넣는 방식이 아닙니다. 사용자 필터가 거부한 그룹은 이 재시도 대상에서 제외됩니다. Miles의 버퍼 제어 옵션
버퍼가 자주 비는지, 얼마나 많은 그룹이 stale로 제외되는지, 실제 소비한 그룹의 버전 간격은 어떤지를 함께 보면 기다림을 줄인 대가를 확인할 수 있습니다. 다음 글에서는 생성 자체의 비용을 줄이는 추론 최적화로 들어가겠습니다.