이 레포가 무엇을 하는 물건인가.
완성된 모델을 빌려 쓰는 게 아니라, 모델의 모든 부품(어텐션·MLP·블록·임베딩)을 직접 코드로 만들고, 데이터까지 직접 내려받아 학습 루프를 돌립니다. "LLM이 안에서 실제로 어떻게 돌아가는가"를 코드로 이해하는 게 목적입니다.
조금 더 풀어볼게요. LLM (Large Language Model, 대규모 언어 모델)은 ChatGPT처럼 다음에 올 단어를 계속 예측하며 문장을 만들어내는 인공지능입니다. 이 레포는 그 LLM의 핵심 뼈대인 Transformer를 from scratch(밑바닥부터), 즉 남이 만든 모델 클래스를 가져다 쓰지 않고 PyTorch의 기본 텐서 연산만으로 처음부터 조립합니다.
저자(Fareed Khan)는 먼저 셰익스피어 텍스트로 230만 파라미터 모델을 만들어 봤지만 출력이 횡설수설했고, "구조를 더 단순하게 + 데이터는 더 다양하게" 바꿔보니 1,300만 파라미터부터 비로소 맞춤법과 문장 부호가 맞는 글이 나오기 시작했다고 합니다. 그 경험을 그대로 코드와 글로 정리한 게 이 저장소예요.
transformers.from_pretrained() 같은 편의 함수 없이 어텐션·MLP를 직접 코딩한다는 의미.트렌딩 이유 · 비슷한 자료 대비 강점.
"나만의 LLM 만들기" 글은 인터넷에 차고 넘칩니다. 그런데 대부분은 허깅페이스 라이브러리의 model = AutoModel.from_pretrained("gpt2") 한 줄로 이미 다 만들어진 모델을 불러올 뿐, 그 안에서 무슨 일이 일어나는지는 가려져 있죠. 이 레포가 뜨는 이유는 정확히 그 반대를 하기 때문입니다 — 어텐션 한 줄, MLP 한 줄까지 전부 직접 보여주고, README가 사실상 한 편의 친절한 강의 노트입니다.
| 항목 | 보통의 "LLM 만들기" 튜토리얼 | 이 레포의 접근 |
|---|---|---|
| 모델 정의 | 라이브러리에서 불러오기 한 줄 | 어텐션·MLP·블록을 PyTorch로 직접 구현 |
| 데이터 | 장난감 수준의 작은 텍스트 | 실전 대규모 말뭉치 The Pile(825GB) 일부 |
| 규모 | 설명용 초소형 | 1,300만 ~ 20억 파라미터까지 확장 |
| 하드웨어 | 언급 모호 | GPU별 학습 가능 표 + 단일 GPU 전제 |
| 문서 | 코드 위주 | 코드 한 줄씩 한국어로도 풀 만한 상세 해설 |
또 하나의 매력은 현실적인 교훈을 솔직하게 담은 점입니다. 저자는 "무조건 크게 만들면 좋아진다"는 환상을 깹니다. 깊이가 얕은 채로 20억 파라미터로 키웠더니 오히려 데이터를 통째로 외워버리는 overfitting(과적합)이 나고, 짧고 단순한 맥락에선 1,300만 모델이 더 말이 되는 글을 쓰더라는 거죠. "작지만 한 가지 일을 잘하는 모델"의 가능성을 보여줍니다.
완성된 모델을 불러오면 빠르게 결과는 나오지만, 어텐션이 왜 그렇게 계산되는지·왜 잔차 연결이 필요한지를 모른 채 넘어갑니다. 무언가 고장 나면 손쓸 방법이 없죠.
200줄 남짓한 모델 코드를 한 줄씩 따라가며 "이 줄이 무슨 일을 하는가"를 이해하게 합니다. 한 번 직접 짜 보면 다른 LLM 코드도 읽을 눈이 생깁니다.
학습 파이프라인을 떠받치는 도구들 한눈에.
이 프로젝트는 화면(프론트엔드)이나 서버가 없는 순수 머신러닝 학습 코드입니다. 그래서 스택도 "데이터를 어떻게 가져와 → 어떻게 숫자로 바꿔 → 어떻게 학습하나"의 한 줄기로 구성돼요.
| 레이어 | 기술 / 도구 | 역할 |
|---|---|---|
| 딥러닝 프레임워크 | PyTorch (torch.nn, torch.nn.functional) | 텐서 연산·자동 미분·레이어. 모델의 모든 부품을 짓는 토대. |
| 토크나이저 | tiktoken (r50k_base) | 글자를 모델이 먹는 숫자(토큰 ID)로 변환. GPT-3/GPT-2가 쓰던 그 사전. |
| 학습 데이터 | The Pile (monology/pile-uncopyrighted) | 책·논문·코드·웹 등 22종을 모은 825GB 말뭉치. 여기선 5~10%만 사용. |
| 원본 포맷 | JSON Lines + Zstandard (.jsonl.zst) | 한 줄에 JSON 하나씩 + 압축. 대용량 텍스트의 표준 배포 형식. |
| 토큰 저장소 | HDF5 (h5py) | 토큰화된 수십억 개 숫자를 디스크에 담고 학습 때 빠르게 꺼내 쓰는 큰 통. |
| 수치 연산·유틸 | NumPy, tqdm, requests | 배열 처리·진행바·파일 다운로드. |
| 실행 환경 | CUDA GPU (cu118 빌드) | 행렬 곱을 수천 배 빠르게. 학습엔 사실상 필수. |
[31373] 같은 숫자로 바꿔줘야 계산할 수 있죠. 토크나이저는 그 변환 사전입니다. tiktoken은 OpenAI가 만든 빠른 토크나이저이고, r50k_base는 약 5만 개 토큰을 가진 GPT-3 시절 사전이에요. 비유하면, 외국어 책을 읽기 전에 모든 단어에 번호를 매긴 색인표를 만드는 일.데이터 한 조각이 들어가서 "다음 단어 예측"이 나오기까지.
먼저 숲(전체 그림)부터 봅니다. 이 프로젝트는 크게 두 단계예요. ① 텍스트를 숫자로 바꿔 통(HDF5)에 담는 데이터 파이프라인, ② 그 숫자를 먹여 모델을 학습시키는 Transformer. 아래 그림은 그 전체 여정입니다.
이제 나무(흐름 한 줄기)를 손으로 따라가 봅시다. 토큰 한 배치가 들어가 loss(오답 점수) 하나가 나오기까지의 해피 패스입니다. 핵심 줄만 짚을게요.
① 입력 준비 — get_batch_iterator가 HDF5에서 토큰을 뭉텅이로 꺼내, 입력 xb와 "한 칸 뒤로 민" 정답 yb를 만듭니다. 모델의 과제는 늘 "지금까지를 보고 바로 다음 토큰 맞히기"예요.
xb = random_samples[:, :context_length] # 입력: 0 ~ T-1번째 토큰
yb = random_samples[:, 1:context_length+1] # 정답: 1 ~ T번째 (한 칸 밀림)
② 임베딩 — 토큰 ID를 의미 벡터로 바꾸는 token_embed와, "몇 번째 자리인가"를 알려주는 position_embed를 더합니다. 어텐션은 순서 감각이 없어서 위치 정보를 따로 주입해야 하거든요.
x = self.token_embed(idx) + self.position_embed(pos_idxs[:T])
③ Block을 N번 통과 — 핵심입니다. 각 블록은 똑같은 두 동작을 반복해요. 먼저 Multi-Head Attention으로 "이 단어가 다른 어떤 단어를 봐야 하나"를 계산하고, 그 다음 MLP로 정보를 더 가공합니다. 둘 다 잔차 연결(원본을 더해 돌려줌)로 감싸죠.
x = x + self.attn(self.ln1(x)) # 어텐션 + 잔차(원본 더하기)
x = x + self.mlp(self.ln2(x)) # MLP + 잔차
④ 예측과 채점 — 마지막 lm_head가 각 자리마다 "다음에 올 토큰별 점수(logits)"를 5만여 개 뱉고, cross_entropy가 정답 yb와 비교해 loss 하나로 요약합니다. 이 loss를 줄이는 방향으로 가중치를 조금씩 미는 게 학습이에요.
곁가지(평가 루프, 학습률 감쇠, 부분 저장 등)는 여기선 접어둡니다 — 일의 95%는 위 네 단계가 합니다.
tril(아래 삼각 행렬)로 만들고, 미래 자리에 -inf를 넣어 확률을 0으로 만듭니다. 시험에서 뒤 문제 답안지를 가리는 종이라고 보면 됩니다.x + f(x)처럼 변형 결과에 원본을 더해 돌려주는 것 — 깊은 층을 쌓아도 정보가 사라지지 않게 하는 "지름길". 레이어 정규화는 각 층의 숫자 크기를 일정하게 다듬어 학습이 출렁이지 않게 잡아주는 평형추입니다. 깊은 모델이 무너지지 않게 받치는 두 기둥.참고로 멀티헤드 어텐션은 단순히 어텐션을 여러 개 병렬로 돌리는 구조입니다. 각 "헤드"가 서로 다른 관계(문법·의미·거리 등)를 따로 보고, 결과를 옆으로 이어 붙여요.
어떤 폴더가 무슨 일을 하나.
구조가 아주 단정합니다. 모델 부품은 src/models/, 실행 스크립트는 scripts/, 설정값은 config/ 한 곳에 모여 있어 길을 잃을 일이 없어요. ※ 아래 트리는 2026-06-02 작성 시점(프리트레인 범위)을 기준으로 하며, 현재 레포에는 SFT·DPO·PPO·GRPO 등 포스트트레이닝 파이프라인 스크립트(train_sft.py·train_dpo.py·train_ppo.py·train_grpo.py 등)와 configs/ 디렉토리가 추가로 포함되어 있습니다.
| 파일/폴더 | 역할 |
|---|---|
src/models/attention.py | 이 레포의 심장. 싱글헤드 Head와 이를 묶은 MultiHeadAttention이 들어 있다. |
src/models/transformer.py | 임베딩 → 블록들 → 최종 출력까지 조립한 Transformer 클래스. forward(학습)와 generate(추론) 둘 다 여기. |
config/config.py | 모델을 13M로 줄일지 2B로 키울지 등 거의 모든 손잡이가 모인 곳. 보통 여기만 만진다. |
scripts/data_preprocess.py | 가장 오래 걸리는 단계. .jsonl.zst를 풀어 한 줄씩 토큰화해 .h5로 쌓는다. |
scripts/train_transformer.py | 모델 초기화 → 배치 반복 → loss 역전파 → 주기적 평가/저장. 학습의 본체. |
data_loader/data_loader.py | get_batch_iterator — 무한 반복하며 무작위 배치를 꺼내 입력/정답 쌍으로 내어준다. |
이 레포에서 진짜로 배워 갈 것 + 어디를 보면 되는지.
교과서 수식 softmax(QKᵀ/√d)·V가 실제 PyTorch 코드에서 어떻게 생겼는지 한눈에 들어옵니다. attention.py의 Head.forward를 읽으면 "쿼리·키를 곱하고 → 미래를 가리고 → softmax → 밸류에 곱하기"가 그대로 보여요.
k = self.key(x); q = self.query(x) # 키·쿼리 만들기
attn = q @ k.transpose(-2,-1) / math.sqrt(C) # 점수 = 쿼리·키 내적
attn = attn.masked_fill(tril==0, float('-inf'))# 미래 가리기(코절 마스크)
attn = F.softmax(attn, dim=-1) # 점수 → 확률
out = attn @ self.value(x) # 확률로 밸류 가중합
딥러닝의 절반은 데이터 준비입니다. data_preprocess.py는 825GB급 압축 파일을 스트리밍으로 한 줄씩 읽어(zstd.open) 메모리 폭발 없이 토큰화하고, HDF5 데이터셋을 필요할 때마다 resize하며 이어 붙입니다. "큰 데이터를 어떻게 조금씩 다루나"의 실전 패턴이에요.
text = data['text'] + "<|endoftext|>" # 글 끝 표시 토큰 추가
encoded = enc.encode(text, allowed_special={'<|endoftext|>'})
dataset.resize(dataset.shape[0] + len(encoded), axis=0) # 통 늘리고
dataset[start:end] = encoded # 이어 담기
"forward → loss → zero_grad → backward → step"이라는 모든 PyTorch 학습의 4박자가 군더더기 없이 들어 있습니다. 여기에 AdamW 옵티마이저, 일정 스텝 뒤 학습률 감쇠(5e-4 → 5e-5), 주기적 검증 loss 측정까지 실전 요소가 골고루 있어요.
_, loss = model(xb, yb) # 1) 예측 + loss
optimizer.zero_grad(set_to_none=True) # 2) 이전 기울기 비우기
loss.backward() # 3) 역전파(기울기 계산)
optimizer.step() # 4) 가중치 한 걸음 갱신
config.py의 N_EMBED·N_HEAD·N_BLOCKS·CONTEXT_LENGTH 네 값만 바꾸면 모델이 1,300만에서 20억까지 커집니다. 학습 시작 시 sum(p.numel() for p in model.parameters())로 실제 파라미터 수를 찍어보며 "무엇이 크기를 결정하나"를 몸으로 익힐 수 있어요.
generate는 "지금까지의 토큰 → 다음 토큰 확률 → 하나 뽑기 → 뒤에 붙이기"를 반복합니다. torch.multinomial로 확률적으로 뽑기 때문에 같은 입력이라도 매번 다른 글이 나오죠. 자기회귀(autoregressive) 생성의 핵심 루프입니다.
logits = self(idx[:, -context_length:])[0][:, -1, :] # 마지막 자리 예측
probs = F.softmax(logits, dim=-1)
idx_next = torch.multinomial(probs, num_samples=1) # 확률대로 한 개 뽑기
idx = torch.cat((idx, idx_next), dim=1) # 뒤에 붙이고 반복
돌리려면 무엇이 필요한가 — 특히 GPU.
이 프로젝트의 가장 현실적인 제약은 GPU 메모리입니다. 1,300만 파라미터 모델은 거의 모든 게이밍/무료 GPU에서 돌지만, 20억 파라미터는 24GB 이상급에서만 가능합니다. README가 친절하게 GPU별 표를 제공해요.
| 항목 | 요구사항 |
|---|---|
| 최소(13M 학습) | VRAM 6~8GB GPU면 충분. 무료 Colab/Kaggle T4(16GB)로 하루 안에 결과. |
| 대형(2B 학습) | VRAM 24GB+ 필요. RTX 3090/4090, A100(40GB), Quadro RTX 8000(48GB) 등. |
| 디스크 | The Pile 1파일 ≈ 11GB. 토큰화 후 HDF5까지 고려해 수십 GB 여유. |
| 런타임 | Python 3.8+, PyTorch(CUDA cu118 빌드), h5py·tiktoken·zstandard 등. |
| OS | Linux 권장(클라우드 GPU 환경), Windows/맥도 GPU 있으면 가능. |
| 선행 지식 | 파이썬 OOP·신경망·PyTorch 기초(README에 영상 링크 제공). |
저자도 강하게 권합니다: 먼저 13M 모델부터. 무료 T4로 하루면 결과가 나오고 학습 흐름을 다 익힐 수 있어요. 깊이가 얕은 채 무작정 키운 10억 모델은 과적합만 나고 며칠씩 걸립니다.
손에 익히는 단계 — 쉬운 것부터 차례로.
무료 Colab/Kaggle GPU에서 클론 → requirements.txt 설치 → 데이터 1파일만 받기 → 전처리 → 학습 → 생성까지 전 과정을 따라 합니다. config.py를 N_EMBED=128, N_HEAD=8, N_BLOCKS=1로 줄이는 게 포인트.
python scripts/data_download.py --train_max 1 # 데이터 1개만
python scripts/data_preprocess.py --max_data 1000
python scripts/train_transformer.py
python scripts/generate_text.py --model_path models/your_model.pth --input_text hi
N_EMBED이나 N_BLOCKS를 2배로 늘려 보고, 학습 시작 시 출력되는 "Total number of parameters"가 어떻게 변하는지 기록합니다. "어떤 값이 모델 크기를 좌우하나"의 감을 잡는 가장 빠른 실험.
전처리의 r50k_base를 GPT-4 계열 cl100k_base로 바꿔(--tokenizer_name) 어휘 크기가 커지면 토큰 수·학습 양상이 어떻게 달라지는지 비교합니다. VOCAB_SIZE도 맞춰 줘야 한다는 함정까지 직접 부딪혀 보세요.
현재 generate는 순수 확률 추출만 합니다. logits를 temperature로 나눠 무작위성을 조절하거나, 상위 k개만 남기는 top-k 샘플링을 직접 붙여 출력 품질 변화를 관찰해 보세요. LLM 디코딩 전략의 첫걸음.
13M 베이스 모델을 이메일·기사 같은 좁은 도메인 텍스트로 추가 학습(파인튜닝)해, "작지만 한 가지 일을 잘하는 모델"을 만들어 봅니다. 저자가 제시한 다음 단계로, 작은 모델의 실용 가치를 직접 확인하는 과제.
한 주씩 따라가는 4주 계획.
| 주차 | 주제 | 학습 자료 |
|---|---|---|
| 1주차 | PyTorch 기초 + 13M 모델 끝까지 돌리기 | README 선행 영상 · 실습 1·2 |
| 2주차 | 어텐션·Transformer 원리 정독 | attention.py·transformer.py · 논문 「Attention is All You Need」 |
| 3주차 | 데이터 파이프라인 + 토크나이저 이해 | data_preprocess.py · tiktoken 문서 · 실습 3 |
| 4주차 | 디코딩 전략 + 파인튜닝으로 확장 | 실습 4·5 · Karpathy "nanoGPT" / "makemore" 비교 학습 |
본문에 나온 용어 빠른 참조.
| 용어 | 의미 |
|---|---|
| Transformer | 어텐션을 중심으로 한 신경망 구조. 오늘날 거의 모든 LLM의 뼈대. |
| 토큰 (token) | 모델이 다루는 텍스트의 최소 조각(단어·부분 단어). 숫자 ID로 표현된다. |
| 임베딩 (embedding) | 토큰 ID를 의미가 담긴 실수 벡터로 바꾼 것. 단어의 좌표. |
| 어텐션 (attention) | 각 토큰이 다른 토큰을 얼마나 참고할지 정하는 가중치 계산. |
| 멀티헤드 (multi-head) | 어텐션을 여러 갈래 병렬로 돌려 다양한 관계를 동시에 포착. |
| MLP / 피드포워드 | 어텐션 뒤에서 정보를 4배로 펼쳤다 압축하며 가공하는 작은 신경망. |
| 잔차 연결 (residual) | 변형 결과에 원본을 더해 돌려주는 지름길. 깊은 층의 정보 손실 방지. |
| LayerNorm | 층마다 숫자 분포를 다듬어 학습을 안정화하는 정규화. |
| 코절 마스크 (causal mask) | 미래 토큰을 못 보게 가리는 삼각형 가리개. |
| logits | softmax 직전의 날 점수. 다음 토큰 후보들의 상대적 선호도. |
| cross_entropy | 예측 분포와 정답의 차이를 한 숫자(loss)로 만드는 손실 함수. |
| AdamW | 널리 쓰이는 옵티마이저. loss가 줄도록 가중치를 똑똑하게 갱신. |
| 과적합 (overfitting) | 훈련 데이터를 통째로 외워, 새 데이터엔 약해지는 현상. |
| The Pile | 책·논문·코드·웹 등 22종을 모은 825GB 공개 학습 말뭉치. |