Unsloth와 엔비디아, 소비자용 GPU에서 LLM 학습 25% 속도 향상 달성
Unsloth와 NVIDIA는 소비자용 GPU에서 LLM 파인튜닝 시 발생하는 숨겨진 병목 현상을 해결하여 학습 속도를 약 25% 향상시켰습니다. 반복적인 메타데이터 구축을 캐싱하고, 그래디언트 체크포인팅 시 버퍼를 2개 사용해 연산을 겹치게 하며, MoE 라우팅을 최적화하는 세 가지 핵심 기술을 도입했습니다. 이는 개발자들이 기존 하드웨어의 한계를 뛰어넘어 최대치의 성능을 끌어낼 수 있게 해준다는 점에서 실무적으로 매우 중요합니다.
모델 블로그 Unsloth Studio✨ 문서 블로그 Unsloth와 NVIDIA로 LLM 학습을 더 빠르게 만드는 방법 2026년 5월 6일
저자: Daniel, Michael, Mathew, Datta (NVIDIA의 도움을 받아 작성됨)
파인튜닝(Fine-tuning)은 오늘날 가장 연산 집약적인 워크로드 중 하나이며, 계속해서 하드웨어의 한계를 시험하고 있습니다. NVIDIA GPU는 이러한 워크로드에 맞게 특별히 제작되었으며, 복잡한 문제를 조각내어 병렬로 처리합니다. Unsloth는 로컬 RTX 노트북부터 DGX Spark 개인 AI 슈퍼컴퓨터에 이르기까지 광범위한 NVIDIA GPU에서 원활하게 작동합니다.
개발자들이 GPU의 성능을 최대한 끌어낼 수 있도록 돕기 위해 Unsloth는 NVIDIA와 팀업하여 학습 속도를 저하시키는 숨겨진 병목 현상을 제거했습니다. 새롭게 구현된 이러한 최적화들을 결합했을 때 GPU 학습 속도를 약 25% 가속할 수 있었습니다. 우리가 정확히 어떻게 했는지 여기에 설명해 드립니다.
모델 학습을 최적화할 때 개발자들은 보통 영향력이 큰 일반적인 커널(kernel)부터 시작합니다. 행렬 곱셈(matmul), 어텐션(attention), 퓨즈드 옵스(fused ops), 그룹화된 GEMM(grouped GEMM) 등이죠. 이러한 커널들이 대부분의 연산을 담당하지만, 주요 구성 요소가 최적화되고 나면 전혀 다른 종류의 병목 현상이 나타납니다. GPU는 메타데이터에 종속된 작업에서 멈춤(stall) 현상을 겪게 됩니다. 런타임은 매 반복(iteration)마다 동일한 데이터 구조를 다시 구축하며, 복사(copy) 및 연산(compute) 스트림은 겹칠(overlap) 수 있음에도 불구하고 순차적으로 실행됩니다.
이러한 병목 현상을 해결하기 위해 Unsloth와 NVIDIA는 세 가지 개선 사항을 협력하여 개발했습니다:
- 레이어 간에 팩된 시퀀스(packed-sequence) 메타데이터를 재구성하는 것을 피하기 위해 캐싱(Caching) 적용
- 그래디언트 체크포인팅(gradient checkpointing) 중 두 개의 버퍼를 사용하여 활성화 리로드(activation reload)가 역방향 연산(backward compute)과 겹칠 수 있도록 함
- argsort 및 bincount로 토큰을 한 번 그룹화하여 GPT-OSS MoE 라우팅의 비용을 절감
이러한 최적화의 공통된 패턴은 간단합니다. 반복되는 불필요한 정리 작업(bookkeeping)을 줄이고, 복사 작업이 유용한 연산과 병렬적으로 이루어지도록 만드는 것입니다.
- 팩된 시퀀스(Packed-Sequence) 메타데이터 캐싱
몇 가지 짧은 예제가 있다고 가정해 보겠습니다. 모든 예제를 동일한 길이로 패딩(padding)하고 패딩 토큰에 연산 리소스를 낭비하는 대신, 이를 하나의 긴 팩된 시퀀스로 연결(concatenate)합니다.
그러나 모델은 여전히 각 원본 시퀀스가 어디에서 시작하고 끝나는지 알아야 합니다. 따라서 팩된 토큰과 함께 다음과 같은 시퀀스 메타데이터를 전달합니다:
- 시퀀스 길이(sequence lengths)
- 누적 시퀀스 오프셋(cumulative sequence offsets, cu_seqlens)
- 최대 시퀀스 길이(maximum sequence length)
- 위 세 가지 항목에서 파생된 어텐션 구조(attention structure)
여기서 핵심은 고정된 팩된 배치에 대해 해당 메타데이터가 모든 레이어에서 동일하다는 것입니다. 팩된 배치의 경계 정보를 다음과 같이 작성해 보겠습니다:
B = { lengths, cu_seqlens, max_seqlen, mask structure }
그러면 순방향 패스(forward pass)에서 모든 트랜스포머 레이어가 동일한 B를 사용하게 됩니다. 모델에 L개의 레이어가 있는 경우, 레이어당 한 번씩 B를 재구축하거나 재동기화하는 것은 새로운 작업이 아닙니다. 이는 동일한 정보를 반복해서 재구축하는 것과 같습니다.
다시 말해, 유용한 작업은 다음과 같습니다: B를 한 번 구축하고, 이를 L번 사용합니다.
반면 비효율적인 방식은 다음과 같습니다: B 구축 + B 구축 + ⋯ + B 구축 (L번 반복)
여기서 발생하는 오버헤드는 주로 추가적인 FLOPs(초당 부동소수점 연산)에 있지 않습니다. 이러한 경로 중 일부는 디바이스-투-호스트 동기화(device-to-host synchronization)를 강제하여 결과적으로 GPU-CPU 동기화 지점을 만들 수 있습니다. 레이어별 경로 내에서 이러한 일이 발생하면 모든 레이어에서 오버헤드가 반복됩니다. 바로 이것이 팩된 시퀀스 캐싱 변경을 통해 줄이고자 하는 부분입니다.
반복적으로 팩된 시퀀스 정보, SDPA 팩된 마스크, xFormers 블록 마스크를 재구성하는 대신, 현재 팩된 배치에 대해 기기당 재사용 가능한 메타데이터와 이에서 파생된 어텐션 측면의 구조를 캐싱합니다. 그런 다음 이러한 캐시된 구조가 레이어 전반에 걸쳐 재사용됩니다.
왜 도움이 되는가 팩된 학습은 이미 패딩 낭비를 없애 하드웨어 활용도를 높여줍니다. 하지만 메타데이터 경로가 계속해서 동기화를 강제하면, 모델의 실제 학습과는 무관한 오버헤드로 인해 얻었던 이점의 일부를 잃게 됩니다. 캐싱은 핵심 경로에서 반복되는 조정 작업을 제거하므로 도움이 됩니다. 순방향 패스는 여러 레이어에 걸쳐 동일한 팩된 메타데이터가 반복적으로 소비되는 곳이므로 가장 큰 이점을 얻습니다.
벤치마크 Qwen3-14B QLoRA SFT 기준:
- 순방향 패스(forward): +43.3%
- 역방향 패스(backward): +5.8%
- 배치당 속도(per batch): +14.3%
순방향 패스에서 가장 큰 이점을 볼 수 있는 이유는 다음과 같습니다.