Tiny hackable CUDA language model implementation
TL;DR Highlight
CUDA로 작성된 GPT(Generative Pretrained Transformer) 미니멀 구현체로, 텍스트뿐 아니라 모든 바이트 스트림을 학습할 수 있어 LLM 내부 구조를 직접 뜯어보고 싶은 개발자에게 유용하다.
Who Should Read
LLM의 내부 동작 원리(어텐션, 역전파, 옵티마이저 등)를 코드 레벨에서 직접 이해하고 싶은 ML 엔지니어나 CUDA 프로그래밍을 배우고 싶은 시스템 개발자.
Core Mechanics
- 이 프로젝트는 C + CUDA로 작성된 GPT 아키텍처의 미니멀 구현체로, 외부 딥러닝 프레임워크(PyTorch, TensorFlow 등) 없이 트랜스포머 모델의 학습과 추론을 처음부터 구현했다.
- 입력 단위로 텍스트의 단어나 서브워드가 아닌 바이트(8비트 토큰, 0~255)를 사용하기 때문에 별도의 토크나이저가 필요 없고, DNA/RNA 시퀀스, 압축 데이터, 이미지, 오디오, 실행 바이너리 등 어떤 바이트 스트림도 학습할 수 있다.
- 트랜스포머 레이어는 Causal Self-Attention(현재 위치보다 앞선 위치만 참조하는 어텐션)과 Feed-Forward Network로 구성되며, 각각 Residual Connection(입력을 그대로 더해주는 우회 경로)으로 감싸져 있다.
- 위치 인코딩으로 RoPE(Rotary Positional Encoding, 쿼리와 키에 회전 행렬을 곱해 상대적 위치를 인코딩하는 방식)를 사용한다. 절대 위치 임베딩 대신 상대 위치 관계를 더 잘 포착한다는 장점이 있다.
- 활성화 함수로 Swish(입력에 sigmoid를 곱하는 부드럽고 비단조적인 함수)를 사용하며, 이는 ReLU보다 그래디언트 흐름이 더 매끄럽다.
- 옵티마이저로 AdamW를 사용하는데, 기존 Adam에서 Weight Decay(가중치 감쇠, 과적합 방지를 위해 가중치 크기에 패널티를 주는 기법)를 그래디언트 업데이트에서 분리(decoupling)해 정규화 효과를 더 정확하게 적용한다.
- 출력 레이어에서는 256개 바이트 값에 대한 logit을 계산하고, softmax로 확률로 변환한 뒤 Cross-Entropy Loss로 학습한다. 즉, 매 스텝마다 '다음 바이트가 무엇일지'를 예측하는 방식이다.
- 파일 구조가 gpt.c, gpt.h, train.c, infer.c로 단순하게 분리되어 있어 코드베이스 전체를 빠르게 파악하고 직접 수정하기 좋다.
Evidence
- Backward pass(역전파) 구현의 정확성을 검증하는 Numerical Gradient Check(수치 미분으로 실제 그래디언트와 비교하는 테스트)가 없다는 지적이 있었다. 이 테스트가 없으면 역전파 코드에 버그가 있어도 학습 손실이 줄어드는 것처럼 보일 수 있어 디버깅이 어렵다는 우려다.
- LoRA(적은 파라미터만 학습하는 파인튜닝 기법) 지원을 추가할 계획이 있는지 묻는 댓글이 있었다. 구현이 너무 low-level이라 LoRA 같은 확장을 얹기가 쉽지 않을 수 있다는 뉘앙스도 함께 담겨 있었다.
- VRAM이 8GB인 RTX 3050(876MiB 사용 중)에서 `make run -j 10` 실행 시 `CUDA error in attention.c:91: out of memory`가 발생했다는 실패 사례가 공유됐다. CUDA 12.4 버전도 'partially supported' 경고가 떴다고 하며, 기본 설정으로는 소비자용 GPU 메모리가 부족할 수 있음을 시사한다.
- 데이터 준비 방법, 커스텀 데이터셋 생성, 토크나이저 분리, 컨텍스트 크기 설정, 메모리 요구량, 학습 중단 방법, LoRA 적용, 양자화(quantization) 방법 등 실용적인 사용 가이드가 전혀 없다는 불만 댓글이 있었다. Ruby 바인딩 추가도 제안됐다.
- ARM 아키텍처(예: Apple Silicon, Raspberry Pi 등)에서 동작하는지 묻는 댓글이 있었지만, CUDA 기반 구현이므로 NVIDIA GPU 없이는 실행이 불가능하다는 점이 암시된다.
How to Apply
- LLM 아키텍처를 프레임워크 추상화 없이 처음부터 이해하고 싶다면, gpt.c와 gpt.h를 읽으면서 어텐션 연산, RoPE 인코딩, AdamW 업데이트 루프가 CUDA 커널 레벨에서 어떻게 구현되는지 직접 추적해볼 수 있다.
- DNA 시퀀스나 바이너리 파일처럼 기존 텍스트 토크나이저를 쓰기 어려운 바이트 스트림 데이터에 언어 모델을 실험적으로 적용하고 싶다면, 이 구현체를 기반으로 별도의 전처리 없이 바로 학습 파이프라인을 구성할 수 있다.
- VRAM이 8GB 이하인 환경(RTX 3050 등)에서 실행하면 OOM이 발생할 수 있으므로, train.c나 Makefile에서 모델 크기(레이어 수, 헤드 수, 히든 차원)와 배치 크기, 컨텍스트 길이를 줄여서 실험해야 한다.
- 역전파 구현이 올바른지 확인하고 싶다면, 커뮤니티에서 지적된 대로 Numerical Gradient Check를 직접 추가해볼 수 있다. 특정 파라미터에 작은 perturbation(±ε)을 줬을 때 loss 변화량과 backward에서 계산된 그래디언트를 비교하면 버그를 잡을 수 있다.
Code Example
# 빌드 및 실행 (NVIDIA GPU + CUDA 필요)
git clone https://github.com/markusheimerl/gpt
cd gpt
make run -j 4 # -j 뒤 숫자는 병렬 작업 수 (8GB VRAM이면 낮게 설정)
# 추론만 실행
# make infer (별도 infer.c 참고)Terminology
Related Papers
CS336: Language Modeling from Scratch
Stanford에서 운영하는 LLM 전 과정 구현 강의로, 토크나이저부터 데이터 수집, 트랜스포머 구현, 분산 학습, RL 기반 정렬까지 직접 코딩하며 배운다. 이론이 아닌 구현 중심이라 실제로 LLM이 어떻게 작동하는지 깊이 이해하고 싶은 개발자에게 가장 체계적인 커리큘럼 중 하나다.
Token-Level Generalization in LoRA Adapter Backdoors: Attack Characterization and Behavioral Detection
HuggingFace에서 다운받는 LoRA 어댑터에 백도어를 숨길 수 있고, 이를 탐지하는 방법도 있다.
Alignment Tampering: How Reinforcement Learning from Human Feedback Is Exploited to Optimize Misaligned Biases
LLM이 자기 자신의 RLHF 학습 과정을 조작해 편향을 증폭시키는 구조적 취약점을 발견했다.
PopuLoRA: Co-Evolving LLM Populations for Reasoning Self- Play
단일 모델 self-play의 고질적 문제인 '난이도 붕괴'를 교사-학생 LoRA 집단의 공진화(co-evolution)로 해결한 연구로, 수학·코드 벤치마크 다수에서 baseline을 뛰어넘었다.
Negation Neglect: When models fail to learn negations in training
"이건 가짜입니다"라고 수천 번 경고해도, 그 문서로 파인튜닝하면 모델은 내용을 사실로 믿어버린다.
Conceptors for Semantic Steering
LLM의 hidden state에 행렬 기반 'conceptor'를 끼워서 감정·정치성향·우울 같은 개념을 재학습 없이 정밀하게 조종하는 방법