MatN23/AdaptiveTrainingSystem. 요즘 대부분은 HuggingFace에서 '다 구워진 모델'을 내려받아 미세조정만 합니다. 이 프로젝트는 정반대로, 트랜스포머 LLM을 밑바닥부터(토크나이저 → 모델 구조 → 학습 루프 → GPU 커널 → C++ 추론 엔진) 직접 만드는 PyTorch 프레임워크입니다. 특히 Mixture-of-Experts(MoE)·Mixture-of-Depths(MoD) 같은 희소(sparse) 구조를 지원하고, 남들은 PyTorch 기본 연산에 맡기는 부분까지 손으로 짠 CUDA·Apple Metal 커널로 가속합니다. 게다가 학습 중 손실 곡선을 감시하며 스스로 개입하는 자율 오케스트레이터까지 들어 있습니다. 별 21개의 1인 개발 프로젝트라 '대규모 검증된 도구'는 아니지만, 현대 LLM의 내부와 GPU 성능 엔지니어링을 한 저장소에서 통째로 공부하기에는 보기 드물게 알찬 교본입니다. (저장소: MatN23/AdaptiveTrainingSystem · 언어 Python(+CUDA/Metal/C++) · PyTorch + DeepSpeed/FSDP/ColossalAI · Apache-2.0 · ★21 · TrendShift 라이브 멘션 #62135)
"완성차를 사 오는 대신, 엔진 피스톤까지 직접 깎아 차를 만드는 1인 공장."
대부분의 사람은 HuggingFace에서 이미 학습된 모델을 내려받아 씁니다(= 완성차 구매). 이 프로젝트는 정반대로, 모델의 첫 단추인 토크나이저부터, GPU가 실제로 돌리는 연산 커널까지 직접 만듭니다(= 엔진 피스톤을 손으로 깎는 공장). 그래서 "LLM이 안에서 진짜 어떻게 도는가"를 가려진 부분 없이 들여다볼 수 있습니다.
핵심 특징 셋: ① MoE·MoD 희소 구조(필요한 부분만 켜서 계산량 절약), ② 손으로 짠 CUDA·Metal 커널(PyTorch 기본 연산보다 빠른 길을 직접 깔되, 안 되면 PyTorch로 자동 우회), ③ 자율 오케스트레이터(학습이 발산하는지 감시하고 하이퍼파라미터를 스스로 조정).
좀 더 구체적으로, 저장소의 실제 코드는 모두 Src/Main_Scripts/ 아래에 있습니다. 작성자(덴마크 개발자 Matias Nielsen, 1인) 본인 코드는 파이썬 약 3.2만 줄 + GPU 커널 약 5천 줄 규모이고, 모델 전체 구조가 들어 있는 core/model.py 한 파일만 2,710줄입니다. 학습 루프(trainer.py)는 4,321줄, 자율 오케스트레이터(orchestrator.py)는 2,503줄로, "README만 화려한 빈 껍데기"가 아니라 실제로 동작하는 구현이 핵심입니다(78개의 pytest 테스트도 포함).
이 문서가 파고드는 건 "어떻게 설치하나"가 아니라 그 안의 설계입니다 — "MoE 라우팅을 CUDA로 가속하면서 어떻게 역전파(미분)를 안 깨뜨렸나", "왜 RMSNorm은 fp32로 계산하나", "백엔드 3종(DeepSpeed·FSDP·ColossalAI)을 어떻게 하나의 인터페이스로 갈아끼우나", "학습을 스스로 감시하는 오케스트레이터는 무엇을 보나". 현대 LLM 내부와 GPU 프로그래밍을 동시에 공부하려는 사람에게 작지만 밀도 높은 실전 교본입니다.
"래퍼(wrapper)가 아니라 진짜 구현"이라는 점, 그리고 1인 프로젝트라 과장도 섞여 있다는 점.
비슷한 'LLM 학습 코드'는 많지만, 이 저장소가 라이브 멘션을 타고 화제가 된 이유는 대부분이 흉내만 내는 어려운 부분을 실제로 짜 놨기 때문입니다. 다섯 가지로 정리하면:
README가 스스로 못 박습니다 — "이건 모델 동물원(model zoo)이나 API 래퍼가 아니라, 커스텀 CUDA 커널을 갖춘 완전한 학습 시스템이다." 토크나이저, 모델, MoE 라우팅, 손실 함수, 분산 학습, 추론까지 한 저장소 안에서 손으로 이어집니다.
대부분의 취미·학습용 프레임워크는 GPU 연산을 PyTorch에 떠넘깁니다. 이 프로젝트는 .cu(CUDA)·.metal(애플) 소스를 실제로 포함하고, 실행 시점에 nvcc로 컴파일해서 RMSNorm·RoPE·SwiGLU·MoE 라우팅을 가속합니다(안 되면 PyTorch로 자동 우회). moe_cuda_ops.cu 1,207줄, transformer_ops.cu 981줄 — "커널을 직접 짠다"는 말이 빈말이 아닙니다.
MoE(전문가 혼합)는 비교적 흔하지만, 여기에 2024년 DeepMind의 MoD(깊이 혼합)까지 결합해 한 모델에서 학습 가능하게 한 오픈 구현은 드뭅니다. "어떤 전문가에게 보낼까"와 "이 층을 아예 건너뛸까"를 동시에 학습합니다.
대부분의 학습 프레임워크가 NVIDIA 전용인데, 이 프로젝트는 M1~M4 맥에서도 MoE 학습·추론이 되도록 Metal 셰이더와 MPS 경로를 따로 갖췄습니다.
BENCHMARKS.md가 측정 환경을 NVIDIA T4 1장으로 명시하고, A100/H100 같은 대형 GPU 수치는 "검증되지 않음 … 구조적 추정치일 뿐"이라고 스스로 라벨을 붙입니다. 과대광고가 흔한 분야에서 이런 자기 고지는 드문 신뢰 신호입니다.
| 비교 대상 | 성격 | AdaptiveTrainingSystem과의 차이 |
|---|---|---|
| nanoGPT / litGPT | 교육용 미니 GPT | 이쪽이 범위가 훨씬 넓다(MoE·MoD·다중 백엔드·오케스트레이터). 대신 더 무겁고 'minimal'하진 않다. |
| Megatron-LM / DeepSpeed | 대규모 분산 학습 엔진 | 1인 프로젝트라 규모는 작고, 대규모 병렬화는 직접 만들지 않고 DeepSpeed·FSDP·ColossalAI 백엔드에 위임한다. |
| HuggingFace Transformers | 사전학습 모델 허브 | 철학이 정반대. 사전학습 가중치 없음, 밑바닥부터 학습 전용, 저수준 제어 중심. |
GitHub가 보여 주는 거대한 코드량의 대부분은 Src/Main_Scripts/ColossalAI/에 통째로 복사된 외부 프레임워크(약 24만 줄)입니다. 이건 작성자의 코드가 아닙니다. 작성자 본인 기여분은 파이썬 약 3.2만 줄 + 커널 약 5천 줄로 봐야 정확합니다.
최상위 package.json·일부 Dockerfile은 "LuminaAI Desktop"이라는 별개의 Electron 앱 흔적이고, 거기서 가리키는 main.js·train.py 같은 파일은 저장소에 없습니다. 이전 프로젝트의 잔재이니 Electron을 학습 시스템의 일부로 오해하지 마세요. 진짜 프로젝트는 Src/ 아래 파이썬입니다.
"최대 300B 파라미터"·"2~7배 가속" 같은 수치는 작성자 본인도 대형 GPU에서는 검증하지 못했다고 밝힙니다. '검증된 프로덕션 도구'가 아니라 '잘 만든 학습 교본'으로 접근하는 게 맞습니다.
한 저장소 안에 파이썬·CUDA·Metal·C++가 공존한다. 각 층이 무엇을 담당하는지 펼쳐 보자.
이 프로젝트는 웹 서비스가 아니라 '모델 학습 엔진'이라, 보통의 백엔드/프론트엔드 구분 대신 ① 모델·학습 코어(파이썬) ② GPU 가속(CUDA/Metal) ③ 분산·인프라로 나누어 보는 게 자연스럽습니다.
Python(약 3.2만 줄, 본체) — 모델 구조·학습 루프·오케스트레이터·데이터 처리. / CUDA C++(.cu) — NVIDIA GPU용 손코딩 커널. / Apple Metal(.metal) + Objective-C++(.mm) — 애플 실리콘용 커널. / C++(.cpp/.hpp) — 파이썬과 분리된 독립 추론 런타임. "학습은 파이썬에서 지휘하되, 무거운 계산은 손으로 짠 저수준 커널이 처리"하는 구조입니다.
| 패키지 | 역할 |
|---|---|
| torch ≥ 2.1 | 딥러닝 기반 프레임워크 — 텐서·자동미분·기본 GPU 연산 |
| transformers / datasets | 토크나이저 유틸·데이터셋 로딩 (사전학습 가중치는 안 씀) |
| einops · safetensors | 텐서 형태 변환 · 안전한 가중치 직렬화 |
| sentencepiece | 서브워드 토크나이저 학습/적용 |
| numpy · scipy | 수치 연산 보조 |
| 패키지 | 역할 |
|---|---|
| deepspeed ≥ 0.11 | ZeRO 1~3 · CPU/NVMe 오프로딩 — 큰 모델을 여러 GPU·CPU 메모리에 쪼개 담는 핵심 백엔드 |
| triton ≥ 2.1 | 파이썬으로 GPU 커널을 쓰는 DSL — FP8 경로에 사용 |
| ray · mpi4py | 분산 작업 오케스트레이션 · MPI 통신 |
| ninja · cmake · pybind11 | 네이티브 커널 빌드 · C++↔파이썬 바인딩 |
| 분류 | 패키지 |
|---|---|
| 양자화(4/8bit) | bitsandbytes · auto-gptq · optimum · optimum-quanto |
| 데이터(고속) | pyarrow · polars (제로카피·메모리맵 데이터셋), datasets |
| 추적·UX | wandb(실험 추적) · loguru · rich · click · tqdm · PyYAML |
| 애플 | pyobjc-framework-Metal (파이썬에서 Metal 커널 디스패치) |
| 서빙·보안 | PyJWT(인증) · 자체 rate limiter · input validator |
네이티브 커널은 실행 시점에 컴파일됩니다. cuda_kernels.py가 현재 GPU의 SM 아키텍처를 감지해 nvcc를 호출하고, 만들어진 .so를 ctypes로 불러옵니다(이게 핵심 트릭, §4·§6에서 자세히). 그 밖에 Dockerfile.backend(python:3.10-slim, Flask 헬스체크 포트 5001), docker-compose.dev.yml(Jupyter 8888 포함), makefile, GitHub Actions CI(test.yml)가 있습니다.
Dockerfile.backend가 train.py·lumina_desktop.py·ChatAI.py 같은 최상위 파일을 COPY하는데, 이 파일들은 Src/에 없습니다(위 §2의 LuminaAI 잔재와 같은 맥락). 도커 빌드를 그대로 따라 하면 실패할 수 있으니, 실제 학습은 Src/Main_Scripts/Main.py를 직접 실행하는 쪽으로 접근하세요.
설정(config) 객체 하나가 모델 → 학습 루프 → GPU 커널 → 분산 백엔드를 타고 흐른다.
전체 구조의 핵심은 "설정이 모든 걸 결정하고, 무거운 계산은 손코딩 커널로 내려가되, 실패하면 항상 PyTorch로 우회한다"입니다. 실제 코드(Src/Main_Scripts/)의 흐름을 그림으로 보면:
가장 자주 등장하는 패턴입니다. 모든 가속 연산(RMSNorm·RoPE·SwiGLU·MoE 라우팅)은 통합 백엔드 → 직접 CUDA/Metal → PyTorch 순으로 시도하고, 각 단계를 try/except로 감쌉니다. "빠른 길이 막히면 느려도 확실한 길로 자동 우회"라, 어떤 하드웨어에서도 일단 돌아가게 만드는 실전적 안전장치입니다.
def forward(self, x):
input_dtype = x.dtype
if USE_UNIFIED_BACKEND and self._impl is not None:
try: return self._impl(x) # 1순위: 통합 백엔드
except Exception: pass
if self._cuda_impl is not None and x.is_cuda:
try: return self._cuda_impl(x) # 2순위: 직접 CUDA
except Exception: pass
x_normed = self._norm_pytorch(x) # 3순위: PyTorch 우회(내부 fp32)
if self.elementwise_affine:
x_normed = x_normed * self.weight.float()
return x_normed.to(input_dtype)
core/model.py RMSNorm.forward (약 L311) — 같은 모양의 폴백이 모델 전반에 반복됩니다.
backend/backend_deepspeed.py·backend_fsdp.py·backend_colossalai.py가 동일한 표면(인터페이스)을 제공하고, 오케스트레이터가 use_deepspeed() 같은 플래그로 하나를 고릅니다. "큰 모델을 어떻게 여러 GPU에 쪼갤까"라는 어려운 문제를 직접 재구현하지 않고 검증된 라이브러리에 위임하되, 갈아끼우기 쉽게 추상화한 영리한 선택입니다.
training/orchestrator.py(2,503줄)는 학습을 지켜보는 '관제탑'입니다. 백그라운드 스레드 monitoring_loop()가 돌면서:
| 구성요소 | 역할 |
|---|---|
| MetaLearningEngine | 과거 학습 기록에서 패턴을 학습 |
| AdaptiveHyperparameterOptimizer | 학습률·배치 크기를 실시간 조정 |
| ArchitectureEvolution | MoE 전문가를 학습 중에 추가/제거(가지치기) |
| RealTimeAnalytics | 손실 곡선 분석 · 이상(발산) 감지 |
| ProductionMonitoring | 운영 지표 모니터링 |
"학습이 발산하면 멈추거나 학습률을 낮추고, 전문가가 놀고 있으면 가지치기한다" — 이런 피드백 컨트롤러를 직접 만드는 법을 통째로 공부할 수 있는 부분입니다.
커널을 미리 빌드해 두는 게 아니라, 처음 쓰는 순간 현재 GPU에 맞춰 nvcc로 컴파일합니다. torch.cuda.get_device_properties로 SM 아키텍처(예: sm_75)를 알아내 컴파일 옵션을 맞추죠(§6에 코드).
저정밀(fp16/bf16) 학습에서 터지기 쉬운 부분을 의도적으로 고정밀로 계산합니다. RMSNorm은 fp32로 계산한 뒤 원래 타입으로 되돌리고, RoPE의 cos/sin 테이블은 fp64로 만들어 캐시합니다. "어디서 정밀도를 지켜야 학습이 안 터지는가"의 좋은 예시입니다.
성능 최적화는 종종 특정 하드웨어에서만 동작합니다. 이 프로젝트처럼 가속 경로를 try로 감싸고 항상 순정 PyTorch 폴백을 두면, 최적화의 이득은 챙기면서도 "그 GPU에선 안 돌아가요" 문제를 피할 수 있습니다. 분산 백엔드를 직접 만들지 않고 위임한 것도 같은 철학 — "어려운 건 검증된 것에 맡기고, 내가 통제할 부분만 손으로 짠다."
진짜 코드는 전부 Src/Main_Scripts/ 아래. 그 안을 역할별로 펼쳐 본다.
읽는 순서를 추천하자면: ① config/config_manager.py(무엇을 만드는지) → ② core/model.py(모델이 어떻게 생겼는지) → ③ training/trainer.py(어떻게 학습하는지) → ④ core/moe_cuda_ops.cu(GPU에서 실제로 어떻게 도는지) 순입니다. ColossalAI/ 폴더는 외부 코드이니 처음엔 건너뛰세요.
이 저장소의 진짜 가치는 "현대 LLM 내부 + GPU 성능 엔지니어링"을 한곳에서 코드로 볼 수 있다는 것.
core/model.py요즘 LLM의 표준 부품들이 한 파일에 다 구현돼 있습니다. 읽을 위치까지 적어 두면: RMSNorm(L260), RotaryEmbedding/RoPE(L366), DenseGroupedQueryAttention/GQA(L613), SwiGLUExpert(L1006), TransformerBlock(L1578). LayerNorm 대신 RMSNorm, 절대 위치 임베딩 대신 RoPE, 모든 헤드가 KV를 따로 갖는 대신 GQA로 공유 — "왜 요즘 모델은 이렇게 바뀌었나"를 코드로 체득하기 좋습니다.
MoEFFNLayer(L1068)는 top-k 게이팅, 온도(temperature) 스케일링, capacity factor, 그리고 전문가에게 일이 골고루 가도록 하는 보조 부하분산 손실(auxiliary load-balancing loss)을 모두 구현합니다. 가장 영리한 부분은 "CUDA로 top-k를 빠르게 고르되, 미분은 PyTorch로 흐르게" 한 점입니다:
if self.use_cuda_ops and MoECUDAOps is not None and x.is_cuda:
try:
top_k_indices, cuda_probs = MoECUDAOps.topk_gating(
gate_logits, self.top_k,
temperature=self.routing_temperature, use_cuda=True)
# 인덱스는 CUDA가 고르고, 확률은 PyTorch로 다시 계산 →
top_k_logits = torch.gather(scaled_logits, -1, top_k_indices)
top_k_probs = F.softmax(top_k_logits, dim=-1) # 게이트로 가는 미분 경로 유지
except Exception as e:
top_k_indices, top_k_probs = self._pytorch_routing(gate_logits)
else:
top_k_indices, top_k_probs = self._pytorch_routing(gate_logits)
커널은 "누구를 뽑을지"(인덱스)만 빠르게 정하고, 실제 확률은 F.softmax로 다시 계산해 역전파가 라우터 가중치까지 흐르게 합니다. "선택은 가속하되 학습은 안 깨뜨린다" — MoE를 직접 짤 때 가장 헷갈리는 지점의 모범답안입니다.
MoDRouter(L907)와 DenseSwiGLUWithMoD(L1362)는 토큰별로 "이 층을 거칠지/건너뛸지"를 학습합니다. capacity_factor를 낮출수록 더 많이 건너뛰어 FLOPs가 줄어듭니다. MoE(가로로 전문가 나눔)와 MoD(세로로 층 건너뜀)를 같이 쓰는 흔치 않은 조합을 실제 코드로 볼 수 있습니다.
core/moe_cuda_ops.cu여기가 이 저장소의 백미입니다. GPU 한 워프(32 스레드) 안에서 레지스터만으로 최댓값을 구하는 워프 셔플 리덕션, 정렬 없이 top-k를 뽑는 바이토닉 정렬, 한 번에 4개 float를 읽는 float4 벡터화 같은 실전 기법이 들어 있습니다:
// 워프 셔플로 32개 값의 최댓값을 동기화 없이 구하기
__device__ __forceinline__ float warp_reduce_max(float val) {
for (int offset = WARP_SIZE/2; offset > 0; offset >>= 1)
val = fmaxf(val, __shfl_xor_sync(FULL_MASK, val, offset));
return val;
}
// 각 레인이 자기 몫의 float4를 로드 → 메모리 대역폭 최대화
const float4 *logits_vec = reinterpret_cast<const float4 *>(token_logits);
// __launch_bounds__(256, 4): T4(SM당 1024스레드)에 맞춘 점유율 튜닝
그리고 SwiGLU 활성화는 이렇게 한 줄짜리 커널로 융합합니다(빠른 __expf intrinsic 사용):
__global__ void __launch_bounds__(256)
swiglu_kernel_simple_fp32(const float* __restrict__ gate,
const float* __restrict__ up,
float* __restrict__ output, const int n) {
const int idx = blockIdx.x * blockDim.x + threadIdx.x;
if (idx >= n) return;
float silu_u = up[idx] / (1.0f + __expf(-up[idx])); // SiLU
output[idx] = gate[idx] * silu_u; // gate로 곱셈
}
CUDA를 처음 보더라도, "스레드 인덱스 계산 → 범위 체크 → 자기 원소 처리"라는 기본 골격과 __restrict__·__launch_bounds__ 같은 최적화 힌트를 실제 맥락에서 익힐 수 있습니다.
training/cuda_kernels.py커널을 미리 빌드하지 않고, 현재 GPU 아키텍처를 감지해 nvcc를 그 자리에서 호출합니다. "C 소스를 파이썬에서 컴파일해 불러오는" 패턴을 통째로 보여 줍니다:
prop = torch.cuda.get_device_properties(0)
sm = f"sm_{prop.major}{prop.minor}" # 예: sm_75 (T4)
arch = f"compute_{prop.major}{prop.minor}"
cmd = ["nvcc", "-O3", "-shared", "-fPIC", "--use_fast_math",
f"--generate-code=arch={arch},code={sm}",
"-o", _SO_FILE, _CU_FILE]
subprocess.run(cmd, capture_output=True, text=True, timeout=180)
# 만들어진 .so는 ctypes로 로드
cuda_kernels.py에는 _lib_loss를 모듈 전역으로 두고 "지역 변수로 두면 함수 반환 후 GC돼 버린다"는 주석이 있습니다. 로드한 .so 핸들이 가비지 컬렉션되면 커널 호출이 깨지는 실제 ctypes 함정입니다.
CUDA 인덱스 검증 플래그(validate_cuda_indices)가 기본 꺼짐입니다. 켜면 매 스텝 .item() 호출로 호스트-디바이스 동기화가 생겨 느려지기 때문 — "안전 검사 vs 속도"를 켜고 끄는 손잡이로 만든 좋은 예입니다.
backend/같은 인터페이스 뒤에서 DeepSpeed(ZeRO 1~3·오프로딩), FSDP, ColossalAI를 갈아끼울 수 있습니다. "큰 모델을 여러 GPU에 어떻게 쪼개나(ZeRO가 옵티마이저 상태·그래디언트·파라미터를 단계별로 분산)"를 세 방식으로 비교 학습하기에 좋습니다.
training/chinchilla_scaler.pyChinchilla 법칙(모델 크기 대비 최적 토큰 수)을 코드로 구현합니다. ConvergenceDetector(정체·발산 감지), ComputeEfficiencyTracker(토큰당 FLOPs)로 "지금 더 학습할 가치가 있나"를 판단합니다.
moe_inference_runtime/학습된 MoE 모델을 파이썬 없이 C++로 서빙하는 별도 엔진입니다(runtime.cpp + CPU/CUDA/MPS 백엔드). "학습 코드와 추론 코드는 분리된다"는 실무 패턴을 보여 줍니다.
Src/tests/78개 pytest가 있습니다 — test_chinchilla.py(9개), test_checkpointing.py(7개), test_adaptive_logic.py(7개), test_e2e.py. "확률적이라 테스트가 어렵다는" ML 코드를 어떻게 검증하는지 참고할 만합니다.
"300B 파라미터"라는 말에 겁먹지 말 것 — 작은 설정은 평범한 GPU에서도 돈다.
| 경로 | 요구사항 / 메모 |
|---|---|
| NVIDIA (CUDA) | Volta~Hopper(sm_70~sm_90). 커널을 nvcc로 즉석 컴파일하므로 CUDA Toolkit + nvcc 설치 필수. 벤치마크는 T4(16GB) 기준. |
| Apple Silicon | M1~M4, MPS + pyobjc-framework-Metal로 Metal 커널 디스패치. |
| CPU 전용 | 동작은 하지만 Flash Attention·DeepSpeed·커스텀 커널이 꺼져 느림(학습 구조 확인용). |
| 작은 모델(입문) | 디버그 설정은 약 1.6GB VRAM에서 돌아 입문용 GPU로 충분. |
| 정밀도 | FP32 / FP16 / BF16 / 혼합. FP8(e4m3)은 H100+에서만(Triton 경로, 실험적). |
| 대규모(수십억+) | DeepSpeed ZeRO-3 + CPU/NVMe 오프로딩 + 다중 GPU 필요. 단, 이 영역 수치는 README가 "미검증"이라 명시. |
| OS | CUDA/Docker는 리눅스(python:3.10-slim 기반), Metal은 macOS. |
공부가 목적이라면 "300B"는 잊고 가장 작은 디버그 설정으로 시작하세요. 무료 Colab(T4)이나 입문용 GPU에서 작은 모델을 몇 스텝 돌려 보며 손실이 줄어드는지, MoE 라우팅 통계가 찍히는지를 관찰하는 것만으로도 이 코드의 80%를 이해할 수 있습니다.
읽기만 하면 금방 잊는다. 난이도별로 손을 움직여 보자.
디버그/소형 설정으로 Src/Main_Scripts/Main.py를 돌려, 손실이 실제로 줄어드는지 관찰하세요. 목표는 결과물이 아니라 "학습 루프가 도는 걸 내 눈으로 보는 것"입니다.
config/config_manager.py의 DeepSeekConfig에서 층 수·은닉 차원·전문가 수를 바꿔 보고, 내장된 파라미터 수 계산이 어떻게 달라지는지 보세요. "무엇이 모델 크기를 키우나"를 수식이 아니라 손으로 체감하게 됩니다.
MoEFFNLayer의 top_k·routing_temperature를 바꿔 보고, 보조 부하분산 손실과 전문가별 사용 빈도(라우팅 통계)가 어떻게 변하는지 보세요. top_k=1과 top_k=2의 차이, 온도를 높였을 때 라우팅이 고르게 퍼지는지를 확인합니다.
NVIDIA GPU에서 training/cuda_kernels.py로 커널을 nvcc 빌드한 뒤, core/benchmark.py / benchmark_cuda_kernels.py로 커스텀 커널 vs 순정 PyTorch 속도를 직접 재 보세요. README의 "2~7배" 주장을 내 GPU에서 검증하는 셈입니다.
MoDRouter의 capacity_factor를 낮춰 가며 "몇 %의 토큰이 층을 건너뛰는지", 그때 토큰당 FLOPs(ComputeEfficiencyTracker)와 품질이 어떻게 절충되는지 측정하세요. 동시에 orchestrator.py의 monitoring_loop가 발산을 감지하면 무엇을 하는지 로그로 추적합니다.
이 저장소를 '교과서'로 삼아 6주간 현대 LLM 학습을 정복하는 코스.
| 주차 | 주제 | 이 저장소에서 볼 곳 + 곁들일 자료 |
|---|---|---|
| 1주 | 트랜스포머 기초 부품 | core/model.py의 RMSNorm·RoPE·GQA·SwiGLU 읽기. 곁들임: nanoGPT, "Attention is All You Need". |
| 2주 | MoE / MoD 희소 구조 | MoEFFNLayer·MoDRouter + 라우터 학습. 논문: Switch Transformer(MoE), Mixture-of-Depths(2024). |
| 3주 | 분산 학습 · 메모리 | backend/ 3종 비교. 개념: DeepSpeed ZeRO 1~3, FSDP, CPU/NVMe 오프로딩, 그래디언트 체크포인팅. |
| 4주 | GPU 커널 프로그래밍 | moe_cuda_ops.cu·transformer_ops.cu. 개념: 워프·셰어드 메모리·셔플·점유율(occupancy). 곁들임: NVIDIA CUDA C++ Programming Guide. |
| 5주 | 추론 · 양자화 | moe_inference_runtime/(C++ 서빙) + 4/8bit 양자화(bitsandbytes·GPTQ). 학습 후 모델을 가볍게 배포하기. |
| 6주 | 스케일링 · 자동화 | chinchilla_scaler.py·orchestrator.py. 개념: Chinchilla 컴퓨트 최적, 수렴 감지, 자율 하이퍼파라미터 튜닝. |
한 번에 다 이해하려 하지 마세요. 1~2주(모델 구조)만 제대로 소화해도 "요즘 LLM이 어떻게 생겼나"를 코드로 아는 사람이 됩니다. CUDA(4주)는 어렵지만, §6의 두 커널 스니펫만 줄 단위로 따라가도 GPU 프로그래밍의 감을 잡을 수 있습니다.
이 문서·저장소에 나온 용어를 한 줄씩 정리. 모르면 여기로 돌아오기.
| 용어 | 한 줄 뜻 |
|---|---|
| MoE | 전문가 혼합 — 토큰마다 일부 전문가에게만 일을 맡겨 '덩치 큰데 가벼운' 모델을 만드는 구조. |
| MoD | 깊이 혼합 — 쉬운 토큰이 일부 층을 건너뛰게 학습해 FLOPs를 줄이는 기법(2024, DeepMind). |
| top-k 게이팅 | 라우터가 점수 상위 k명의 전문가만 골라 토큰을 보내는 방식. |
| 부하분산 손실 | 특정 전문가에게만 일이 몰리지(expert collapse) 않도록 균형을 강제하는 보조 손실. |
| capacity factor | 전문가/층이 받아들이는 토큰 용량 배수 — 낮추면 계산↓ 품질 절충. |
| RMSNorm | LayerNorm의 경량 버전(평균 빼기 생략). 요즘 LLM 표준. 안정성 위해 fp32로 계산. |
| RoPE | 회전 위치 임베딩 — 위치 정보를 회전 변환으로 주입. cos/sin 테이블을 fp64로 캐시. |
| GQA | 그룹 쿼리 어텐션 — 여러 쿼리 헤드가 KV 헤드를 공유해 메모리·속도 절약. |
| SwiGLU | SiLU 게이팅을 쓴 FFN 활성화 — 요즘 LLM이 즐겨 쓰는 구조. |
| Chinchilla 법칙 | 주어진 컴퓨트에서 '모델 크기 vs 학습 토큰 수'의 최적 비율. |
| DeepSpeed ZeRO | 옵티마이저 상태·그래디언트·파라미터를 GPU들에 단계(1~3)별로 쪼개 담는 분산 기법. |
| FSDP | PyTorch 기본 제공 완전 샤딩 데이터 병렬 — ZeRO와 유사한 메모리 절약. |
| 오프로딩 | GPU에 안 들어가는 텐서를 CPU RAM·NVMe로 잠시 내려 두는 기법. |
| 양자화 | 가중치를 4/8bit로 줄여 메모리·속도를 버는 기법(bitsandbytes·GPTQ). |
| CUDA 워프(warp) | GPU에서 동시에 한 명령을 실행하는 32개 스레드 묶음. |
| __shfl_xor_sync | 워프 안 스레드끼리 메모리 없이 레지스터 값을 교환하는 셔플 명령(리덕션에 사용). |
| 바이토닉 정렬 | 병렬 친화적 정렬 알고리즘 — 워프 안 top-k 선택에 활용. |
| float4 벡터화 | 4개의 float를 한 번에 읽어 메모리 대역폭을 끌어올리는 기법. |
| nvcc | NVIDIA CUDA 컴파일러 — 이 프로젝트는 실행 시점에 호출해 커널을 빌드. |
| Triton | 파이썬으로 GPU 커널을 작성하는 DSL — FP8 경로에 사용. |
| Metal / MPS | 애플 GPU용 셰이딩 언어와 PyTorch 백엔드 — 맥에서 학습/추론 가능. |
| Apache Arrow | 제로카피·메모리맵 가능한 열 지향 데이터 포맷(polars·pyarrow). |
| 혼합 정밀도(AMP) | fp16/bf16로 계산하되 민감한 부분만 fp32로 — 속도·메모리 절약. |
원본 저장소와, 각 개념을 더 깊이 팔 때의 출발점.