NCCL 없이 버킷과 프록시로 잇는 비동기 GRPO와 LoRA

Hugging Face Jobs 환경에서 NCCL 대신 버킷과 프록시로 롤아웃과 학습을 분리하고 LoRA 어댑터만 주고받는 비동기 GRPO 구조를 따라가며 왜 이런 설계가 나왔는지 읽는다.

원문 논문 보기
'NCCL 없이 버킷과 프록시로 잇는 비동기 GRPO와 LoRA' 기사 커버 이미지

Async GRPO with LoRA across HF Jobs: a bucket, a proxy, and no NCCL은 Hugging Face가 공개한 기술 글로 Jobs 환경에서 강화학습을 어떻게 돌릴지에 대한 설계를 다룬다. 원문 링크는 제목 아래에 걸어두었다.

이 글이 내세우는 주장은 하나의 문장으로 정리된다. 안정적인 집단 간 연결을 기대하기 어려운 Jobs 위에서는 생성과 학습을 분리하고 LoRA 어댑터만 버킷과 프록시로 주고받으면서 오래된 정책에서 온 갱신은 클리핑과 중요도 가중으로 다스리는 편이 더 실용적이라는 것이다. 이 글에서는 그 문제의식에서 출발해 GRPO의 선택과 비동기의 이유, 버킷과 프록시의 역할, 그리고 오래된 경험을 다루는 법까지 차례로 따라간다.

왜 생성 시간이 학습을 멈추게 할까

제가 이 글에서 가장 흥미롭게 본 부분은 출발점의 불편함이었습니다. 강화학습으로 언어 모델을 다듬을 때 학습 자체보다 생성이 병목을 만든다는 대목인데요. 같은 프롬프트에 여러 답을 뽑아야 하니 길이가 들쭉날쭉해지고, 어느 하나가 길어지면 나머지 GPU가 그 하나를 기다리면서 멈춰 섭니다. 조용한 연구실 그림으로 보면 사소해 보이지만, 돈을 내고 빌린 GPU 입장에서는 그 기다림이 그대로 요금으로 찍히기 때문입니다.

생각해보면 사람도 비슷하게 기다립니다. 분식집에서 김밥 네 줄을 함께 주문하면 주방은 가장 오래 걸리는 메뉴에 맞춰 손님이 기다리게 되는데요. 여럿이 함께 일할수록 가장 느린 작업이 전체 속도를 정합니다. 언어 모델의 샘플링도 이와 닮았는데, 짧은 답은 금방 끝나도 긴 답 하나가 끝나지 않으면 다음 단계로 넘어가지 못합니다. 그렇다면 이 기다림을 구조로 풀 수는 없을까요.

저는 여기서 멈칫했습니다. 흔히 분산 학습이라고 하면 더 많은 GPU를 한데 묶어 같은 박자로 움직이게 만드는 장면을 떠올리기 때문입니다. 박자가 맞으면 효율이 오르는 게 맞죠. 그런데 생성 길이가 제각각인 강화학습에서는 박자를 맞추는 행위 자체가 기다림을 키웁니다. 저희가 새로운 AI 아키텍처를 볼 때도 benchmark 숫자만 보지는 않습니다. 실제로 무엇이 바뀌었고, 그 변화가 시스템에서 어떤 비용 구조를 만드는지를 함께 봅니다. 그런 눈으로 보면 이 글의 출발점은 숫자가 아니라 대기 시간의 구조에 있는데, 이 지점을 먼저 짚어야 뒤에 나오는 비동기와 버킷 이야기가 자연스럽게 이어집니다.

같은 질문에 여러 답을 놓고 우열을 가리는 법

GRPO라는 이름을 처음 들으면 또 하나의 약자처럼 느껴질 수 있습니다. 그런데 이 글에서 설명하는 선택은 비교적 또렷한데, 별도의 가치망을 두지 않고 같은 프롬프트에서 나온 여러 완성본을 서로 비교해 우열을 매긴다는 것입니다. 하나의 답을 절대 점수로 재는 대신, 한 묶음 안에서 상대적으로 나은 답과 부족한 답을 가려 그 차이로 정책을 움직입니다.

이 방식이 와닿았던 이유는 평가의 기준을 바깥에서 끌어오지 않기 때문입니다. 선생이 따로 있어서 점수를 매기는 게 아니라, 같은 반 아이들의 답안지를 펼쳐놓고 서로 견주어 방향을 정하는 셈인데요. 물론 보상 신호 자체가 사라지는 건 아닙니다. 답이 좋은지 나쁜지를 알려주는 신호는 여전히 필요한데, 그 신호를 개별 답마다 절대값으로 해석하지 않고 묶음 안의 상대 위치로 바꾸어 씁니다. 그래서 가치망이라는 별도 장치가 필요 없어지고, 그만큼 메모리와 연산의 짐이 줄어듭니다.

여기서는 조금 조심해서 읽을 필요가 있습니다. 가치망이 없다고 해서 학습이 가벼워졌다고만 받아들이면 놓치는 게 있기 때문입니다. 비교를 묶음 단위로 한다는 것은 곧 묶음을 만드는 생성 비용이 학습의 일부가 된다는 뜻입니다. 생성 없이 비교도 없고, 비교 없이 갱신도 없습니다. 그렇다면 다음 질문은 자연스럽게 생깁니다. 묶음을 만드는 일을 어떻게 하면 기다림 없이 돌릴 수 있을까. 이 질문이 비동기 구조로 이어지는데요. 그 전에 분산 학습의 상식부터 한 번 흔들어볼 필요가 있습니다.

동기식 학습에서 긴 샘플을 기다리며 노는 GPU와 비동기 롤아웃으로 계속 돌아가는 학습을 비교한 그림
동기식 학습에서 긴 샘플을 기다리며 노는 GPU와 비동기 롤아웃으로 계속 돌아가는 학습을 비교한 그림

한 박자로 묶는 분산 학습이 여기서는 어긋나는 이유

분산 학습 이야기를 하면 보통 DDP나 FSDP 같은 이름이 먼저 떠오릅니다. 여러 GPU가 같은 모델을 나누어 들고 기울기를 자주 맞추는 방식인데, 이 전제에는 빠르고 안정적인 집단 통신이 깔려 있습니다. 흔히 NCCL로 묶인 연결이 그 역할을 맡습니다. 같은 방 안에 모여 합창하듯 박자를 맞추는 구조라고 할까요.

그런데 Hugging Face Jobs는 그 합창 무대와는 거리가 있습니다. 작업마다 뜨는 시점이 다르고, 언제 끊길지 모르며, 서로 직접 이어주는 빠른 길이 보장되지 않기 때문입니다. 이 글은 이 환경을 솔직하게 인정하는 데서 출발하는데요. 안정적인 연결을 가정하고 설계를 쌓는 대신, 없다고 보고 설계를 바꿉니다. 박자를 맞추는 동기식을 고집하면 연결이 흔들릴 때마다 전체가 멈추기 때문에, 아예 박자를 맞추지 않아도 돌아가는 길을 찾습니다.

개인적으로 인상 깊었던 점은 이 선택이 성능 자랑이 아니라 환경 인정에서 나왔다는 것입니다. 더 빠른 통신을 요구하는 대신 통신을 덜 필요로 하는 학습을 고른 셈인데, 이런 판단은 현장에서 일해본 사람의 냄새가 납니다. 물론 이렇게 말하면 storage 기반 조율이 만능처럼 들릴 수 있는데, 그렇지 않습니다. 직접 연결을 버리면 지연이 생기고 버전이 어긋나며 오래된 정보가 섞이죠. 문제는 그다음입니다. 그 대가를 치르고도 남을 만큼 주고받는 짐을 줄일 수 있을까. 여기서 LoRA가 등장합니다.

통째로 옮기지 않고 어댑터만 주고받는 법

LoRA를 이미 아시는 분도 많을 텐데요. 전체 가중치를 다 고치는 대신 작은 어댑터를 덧붙여 그 부분만 학습하는 방식입니다. 이 글에서는 이 선택을 메모리 절약 정도로만 다루지 않습니다. 주고받는 짐을 메가바이트 규모로 줄여주는 통행권처럼 다룹니다. 전체 모델을 매번 밀어 올리고 내려받는 것과 어댑터만 올리고 받는 것은 통신의 체급이 완전히 다르기 때문입니다.

이 차이는 생각보다 중요합니다. 전체 가중치를 주고받아야 한다면 버킷 같은 느린 경로로는 감당이 안 되는데요. 파일이 크면 올리고 내리는 데 시간이 걸리고, 그 사이 버전은 또 바뀌며, 기다림은 다시 늘어납니다. 반면 어댑터만 오간다면 이야기가 달라집니다. 올리기도 가볍고 내리기도 가벼우니, 느린 storage를 사이에 두어도 학습이 앞으로 나가죠. 버킷을 파라미터 서버처럼 쓸 수 있다는 말은 바로 이 전제가 깔렸을 때 성립합니다.

그렇다면 LoRA는 만능 열쇠일까요. 저는 그렇게 보지 않습니다. 어댑터만으로 표현할 수 있는 변화에는 한계가 있고, 어떤 과제에서는 전체를 다듬을 때와 차이가 날 수 있기 때문입니다. 다만 이 글의 맥락에서는 그 한계를 감수할 이유가 분명한데요. 직접 연결이 없는 Jobs 위에서 학습을 돌리려면 어댑터라는 작은 단위가 필요하고, 그 작은 단위가 있어야 비동기와 버킷이라는 다음 수가 성립하기 때문입니다. 순서가 중요합니다. LoRA가 먼저가 아니라, 환경이 먼저이고 LoRA는 그 환경에 맞는 답으로 들어옵니다.

전체 가중치 동기화와 가벼운 LoRA 어댑터 동기화를 나란히 비교한 개념도
전체 가중치 동기화와 가벼운 LoRA 어댑터 동기화를 나란히 비교한 개념도

버킷을 서버처럼 쓰면 무엇이 달라질까

여기서 버킷이라는 말이 나오면 잠깐 갸우뚱할 수 있습니다. 흔히 버킷은 파일을 넣어두는 창고로 생각하기 때문입니다. 그런데 이 글에서 버킷은 창고를 넘어 만남의 장소가 됩니다. 롤아웃 작업자가 뽑아낸 궤적을 올려두는 곳이자, 학습기가 새로 만든 LoRA 체크포인트를 올려두는 곳입니다. 직접 만나지 못하는 작업들이 시간차를 두고 같은 장소를 통해 일하는 셈입니다.

월요일 아침 물류 창고를 떠올려보시면 감이 옵니다. 배송 트럭이 각자 다른 시간에 와도 물건을 정해진 선반에 올려두면 다음 트럭이 그걸 가져갑니다. 트럭끼리 직접 만나지 않아도 일이 돌아갑니다. 이 글의 버킷도 그런 선반인데요. 생성 쪽은 궤적이라는 상자를 올리고, 학습 쪽은 어댑터라는 상자를 올립니다. 각 작업자는 자기가 온 시점에 있는 최신 상자를 집어 들고 자기 일을 하면 됩니다. 그래서 분산된 Jobs라는 흩어진 구조에서도 학습이 이어지죠.

여기서 재미있는 질문이 생깁니다. 버킷이 궤적을 쌓아두는 replay buffer 역할까지 겸하면 학습은 더 유연해질까요. 답은 그렇다고 보는 편이 맞는데요. 생성과 학습의 속도가 달라도 버킷이 완충 지대가 되어주기 때문입니다. 생성이 빠르면 궤적이 쌓이고, 학습이 바쁘면 쌓인 것부터 꺼내 쓰면 됩니다. 다만 공짜 점심은 없습니다. 버킷에 오래 머문 궤적은 지금 정책이 만든 것과는 조금 다른 분포에서 왔을 수 있기 때문입니다. 이 오래된 경험의 문제는 뒤에서 다시 다루겠습니다. 그 전에 이 창고를 누가 정리하는지를 봐야 하는데요. 그 역할이 프록시입니다.

롤아웃 작업자와 학습기가 버킷을 통해 궤적과 어댑터를 주고받는 구조도
롤아웃 작업자와 학습기가 버킷을 통해 궤적과 어댑터를 주고받는 구조도

누가 최신판을 알려주고 일을 나눠줄까

여러 작업이 각자 돌다 보면 금방 헷갈리는 질문이 생깁니다. 지금 최신 어댑터는 몇 번 판인지, 내가 들고 있는 궤적은 어느 판에서 나온 것인지, 누구는 끝나고 누구는 늦어지는지를 누가 알려줄까요. 이 글에서 그 교통정리를 맡는 존재가 가벼운 프록시 서비스입니다. 무거운 연산을 대신하는 게 아니라 버전 발견과 작업 조율을 맡죠.

프록시의 일은 말로 풀면 단순합니다. 학습기가 새 어댑터를 올리면 판 번호를 매겨 알리고, 롤아웃 작업자가 일을 시작할 때 지금 받아 가야 할 판이 무엇인지 알려주며, 늦어지는 작업자나 중간에 끊긴 작업을 전제로 전체 흐름이 멈추지 않게 합니다. 학교 앞 횡단보도에서 신호봉을 든 분을 떠올리시면 되는데요. 차를 대신 몰아주지는 않지만, 누가 언제 나가고 멈출지를 정해주니 길이 막히지 않습니다. 분산된 Jobs에서는 이런 가벼운 중심이 오히려 잘 맞습니다. 모든 걸 한곳에서 계산하면 그곳이 병목이 되지만, 판 관리와 안내만 맡으면 가볍게 오래 버틸 수 있기 때문입니다.

실제 시스템 관점에서 보면 다음 질문이 바로 생깁니다. 프록시가 하나면 그 하나가 넘어졌을 때는 어떻게 될까요. 이 글은 프록시를 가볍게 두어 부담을 낮추는 쪽을 택하는데요. 저는 이 판단이 솔직하다고 봅니다. 완전한 무중단을 약속하는 대신, 끊김과 지각을 당연한 일로 두고 설계했기 때문입니다. Jobs는 원래 선점되고 늦어지는 환경이라, 이를 예외가 아니라 평소 일로 다루는 편이 더 단단합니다. 그렇다면 다음으로 볼 것은 이렇게 모아진 경험의 신선도입니다.

버전 추적과 작업 조율을 맡은 가벼운 프록시 서비스의 동작 개념도
버전 추적과 작업 조율을 맡은 가벼운 프록시 서비스의 동작 개념도

조금 늦은 경험을 어떻게 믿을까

비동기 구조에서는 오래된 정책에서 나온 궤적이 섞일 수밖에 없습니다. 롤아웃 작업자가 어댑터 몇 판 전 버전을 들고 생성을 시작했는데, 그 사이 학습기가 앞으로 나갔다면 그 궤적은 지금 정책과는 조금 어긋난 상태입니다. 이를 stale이라고 부르는데요. 그냥 쓰면 학습이 흔들릴 수 있습니다. 과거의 입맛으로 지금의 메뉴를 평가하는 셈이기 때문입니다.

이 글이 고른 처방은 PPO 방식의 클리핑과 중요도 가중입니다. 말은 어려운데 뜻은 정직합니다. 지금 정책과 그때 정책이 얼마나 달라졌는지를 따져보고, 너무 벌어진 부분은 갱신에서 세게 반영하지 않도록 눌러둡니다. 마치 오래된 지도로 길을 찾을 때, 새로 생긴 도로는 일단 조심해서 들어가는 것과 비슷합니다. 지도가 아예 쓸모없는 건 아니지만, 바뀐 구간에서는 속도를 낮추는 것이죠. 이렇게 하면 버킷에 쌓인 궤적을 버리지 않고도 학습을 안정시킬 수 있습니다.

여기서는 조금 조심해서 읽을 필요가 있습니다. 클리핑이 만능 보정은 아니기 때문입니다. 너무 오래된 궤적까지 살려 쓸 수는 없고, 가중을 너무 세게 누르면 학습 신호 자체가 옅어지죠. 저는 이 숫자보다 그 뒤의 구조가 더 중요하다고 봅니다. 오래된 경험을 어떻게 수학적으로 눌렀는지보다, 버릴 수 없는 환경이라 눌러서라도 쓰겠다는 선택이 먼저이기 때문입니다. Jobs 위에서는 신선한 궤적만 골라 쓰겠다고 하면 기다림이 다시 생기는데요. 기다림을 없애기로 했으니 오래된 궤적을 다루는 법까지 패키지로 가져갑니다. 이 패키지가 비동기 GRPO의 실제 모습입니다.

오래된 정책에서 온 업데이트를 클리핑과 중요도 가중으로 안정시키는 모습을 담은 그림
오래된 정책에서 온 업데이트를 클리핑과 중요도 가중으로 안정시키는 모습을 담은 그림

끊기고 늦는 작업자를 평소 일로 두는 설계

분산 시스템을 오래 보다 보면 전제가 둘로 갈립니다. 모두 제시간에 온다고 믿고 짜는 설계와, 누군가는 늦거나 빠진다고 보고 짜는 설계인데요. 이 글은 분명히 후자 쪽에 서 있습니다. Jobs의 선점과 straggler를 고장 취급하지 않고 흐름의 일부로 둡니다. 어떤 작업이 중간에 사라져도, 어떤 작업이 한참 뒤에 결과를 들고 와도 전체가 멈추지 않죠.

이게 가능한 이유는 앞에서 본 장치들이 맞물리기 때문입니다. 버킷이 완충 지대가 되어주고, 프록시가 판을 알려주며, 어댑터가 가벼워서 오가기 쉽고, 오래된 궤적은 가중으로 눌러 쓰니까요. 하나만 빠져도 어딘가 막히는데, 넷이 함께 있으니 늦는 작업자가 있어도 학습이 앞으로 갑니다. 합창단 비유를 다시 가져오면, 지휘자 없이 각자 악보 사본을 들고 연습실에 녹음본을 남기는 방식이라고 할까요. 누군가 늦게 와도 최신 녹음본을 듣고 자기 파트를 얹으면 됩니다.

좋은데, 이걸 어디에 올리면 가장 잘 맞을까요. 저는 작은 팀에서 밤사이 돌리는 실험이나, 자원이 들쭉날쭉한 조직에서 여러 Jobs를 모아 학습을 이어가는 장면이 먼저 떠오릅니다. 비싼 전용 클러스터를 통째로 빌리기 어렵지만, 남는 시간과 흩어진 자원은 있는 경우인데요. 이런 조건에서는 기다리지 않는 구조가 돈이 됩니다. 물론 모든 강화학습이 여기 맞아야 하는 건 아닙니다. 정밀한 동기화가 필요한 큰 학습이라면 전용 연결이 있는 클러스터가 여전히 낫습니다. 이 글도 그걸 부정하지 않습니다. Jobs라는 조건에서 무엇을 고를지를 말할 뿐인데, 그 조건 구분이 분명해서 읽는 맛이 있습니다.

빠른 길을 버리면 치러야 할 값

여기까지 읽으면 storage 동기화가 좋아 보이지만, 대가를 짚지 않고 넘어가면 반쪽 이해가 됩니다. 직접 연결에 비해 버킷을 거치면 지연이 생기고, 버전이 어긋나며, 궤적의 신선도가 떨어집니다. 어댑터가 가볍다고 해도 올리고 내리고 알리는 과정에는 시간이 들고, 프록시의 안내가 있어도 작업자마다 들고 있는 판이 달라집니다. 빠름을 기대하고 보면 답답할 수 있습니다.

그런데 관점을 바꾸면 이 대가는 선택의 값으로 보입니다. NCCL이 있는 방에서는 동기식이 빠르고 정확하죠. 하지만 그 방을 못 쓸 때는 선택지가 달라지는데요. 아예 학습을 못 돌리거나, 기다림을 감수하고 돌리거나, 느린 길을 쓰되 기다림을 없애는 쪽으로 돌리는 것입니다. 이 글은 세 번째를 고른 셈인데, 그 판단에는 LoRA라는 전제가 깔려 있습니다. 주고받는 짐이 작으니 느린 길도 쓸 만해진 것입니다. 짐이 컸다면 이 길은 통하지 않았을 겁니다.

저는 이 대목에서 과장 없는 태도가 마음에 들었습니다. 버킷이 NCCL을 대체한다는 말이 더 빠르다는 뜻이 아니라, Jobs라는 조건에서 돌아가게 만든다는 뜻으로 읽히기 때문입니다. 대체라는 말은 흔히 우위를 뜻하는데, 여기서는 생존에 가깝죠. 빠른 길을 쓸 수 없으니 느린 길로 가되, 그 길에 맞게 짐을 줄이고 순서를 바꾼 것입니다. 그렇다면 이런 생존형 설계가 말해주는 더 큰 이야기는 무엇일까요. 마지막 두 구간에서는 시야를 넓혀보겠습니다.

직접 연결 없이 버킷만으로 이어진 여러 분산 작업의 전체 배치도
직접 연결 없이 버킷만으로 이어진 여러 분산 작업의 전체 배치도

분산 학습의 상식을 어디까지 넓힐 수 있을까

오랫동안 분산 학습의 상식은 빠른 연결을 먼저 깔고 그 위에 학습을 올리는 것이었습니다. 기울기를 자주 맞추고, 박자를 맞추고, 느린 작업자를 기다리거나 잘라내는 방식이었습니다. 그런데 이 글의 구조는 그 순서를 뒤집습니다. 연결을 가정하지 않고, 기다리지 않으며, 늦는 작업자를 자르지 않고 눌러서 씁니다. 학습 알고리즘도 그에 맞춰 GRPO처럼 가벼운 비교 방식을 고르고, 동기화 단위도 어댑터로 줄이죠.

질문도 바뀌었습니다. 어떻게 더 빨리 맞출까에서 어떻게 맞추지 않고도 앞으로 나갈까로 말이죠. 이 전환은 Jobs 같은 공용 작업 실행 환경이 늘어나면서 더 중요해질 것 같은데요. 누구나 전용 클러스터를 갖지 않아도 여러 개의 작은 작업을 묶어 학습을 이어갈 수 있다면, 강화학습의 문턱은 그만큼 낮아집니다. 물론 정밀한 대규모 학습까지 이 길로 옮기자는 이야기는 아닙니다. 다만 중간 규모의 시도와 반복이 필요한 다듬기 단계에서는 이런 가벼운 길이 꽤 쓸모 있을 수 있죠.

저희가 새로운 AI 아키텍처를 볼 때도 benchmark 숫자만 보지는 않습니다. 실제로 무엇이 바뀌었고, 그 변화가 시스템에서 어떤 비용 구조를 만드는지를 함께 봅니다. 그런 눈으로 보면 이 글의 수확은 점수가 아니라 선택지입니다. 빠른 연결이 없을 때 쓸 수 있는 또 하나의 학습 모양을 보여준 것입니다. 그렇다면 이 모양을 더 단단하게 만들려면 무엇이 남았을까요. 그 이야기는 마지막에 다시 다루겠습니다.

다음에 제가 지켜보고 싶은 장면

앞에서 미뤄뒀던 질문으로 돌아올 차례입니다. 이 모양을 더 단단하게 만들려면 무엇을 봐야 할까요. 저는 먼저 버전 어긋남의 한계가 궁금합니다. 몇 판까지 뒤처진 궤적을 눌러서 쓸 수 있는지, 그 한계가 과제마다 어떻게 달라지는지를 더 보고 싶죠. 보상 신호가 성긴 과제와 빽빽한 과제에서는 오래된 경험의 값이 다를 수 있기 때문입니다. 두 번째는 버킷의 운영 감각입니다. 궤적과 체크포인트가 쌓이면 보관과 정리, 읽기 쓰기 경쟁이 생기는데요. 학습이 길어질수록 이 창고 정리가 전체 속도를 정할 수 있습니다.

세 번째는 프록시의 단순함이 어디까지 버티는지입니다. 지금은 판 관리와 안내만으로 충분해 보이지만, 작업 수가 크게 늘거나 작업 길이가 더 들쭉날쭉해지면 안내 방식도 손봐야 할 수 있죠. 네 번째는 LoRA 범위의 선택입니다. 어느 크기와 어느 위치의 어댑터가 이런 비동기 흐름과 잘 맞는지는 여전히 손으로 만져볼 문제라고 봅니다. 작게 하면 오가기 쉽지만 담을 수 있는 변화가 줄고, 크게 하면 반대가 되니까요.

그렇다면 사람의 역할은 무엇일까요. 저는 이 글을 읽고 그런 생각을 했습니다. 빠른 연결이 없을 때 포기하는 대신, 알고리즘과 동기화 단위와 조율 방식을 함께 바꾸는 선택을 하는 일이 바로 사람의 몫이 아닐까 하는데요. 버킷과 프록시는 거창한 발명이 아니라 평범한 부품을 엮은 것인데, 그 엮음이 Jobs라는 조건 위에서는 학습을 가능하게 만듭니다. 다음에 비슷한 조건에서 학습을 돌려야 할 때, 저는 이 글의 답보다 이 글의 질문 순서를 먼저 떠올릴 것 같습니다. 무엇을 가정하지 않을지, 무엇을 가볍게 만들지, 늦는 것을 어떻게 품을지를 말이죠.

참고 자료

  1. Async GRPO with LoRA across HF Jobs: a bucket, a proxy, and no NCCL · huggingface.co

    리뷰 원문