바닥부터 한 줄씩 PyTorch로 손수 만들며 배우는 LLM 교과서 저장소llms-from-scratch v1.0.18 · 노트북 66개 · 파이썬 149개 · 라이선스 Apache-2.0 · 최신 커밋 2026-08-09 · Manning 2024, ISBN 978-1633437166 · TrendShift Daily #6)
LLMs-from-scratch는 "ChatGPT 같은 LLM을 PyTorch로 처음부터, 한 단계씩(step by step) 구현한다"를 목표로 하는 교육용 코드 저장소다. 하는 일을 한 문장으로 줄이면 텍스트를 숫자로 바꾸는 토크나이저부터, 문맥을 읽는 어텐션, 문장을 이어 쓰는 GPT 본체, 그리고 사람 지시를 따르도록 다듬는 파인튜닝까지 — 현대 LLM의 전 과정을 남의 라이브러리 없이 직접 코딩하는 것이다.
대부분의 LLM 튜토리얼은 이미 완성된 소스(transformers 라이브러리)를 사서 데워 내는 레시피다. model = AutoModel.from_pretrained(...) 한 줄이면 GPT가 뚝딱 나오지만, 그 안에서 무슨 일이 일어나는지는 여전히 깜깜하다.
이 저장소는 반대다. 밀을 빻아 밀가루를 만들고, 소스를 처음부터 졸이는 방식이다. 어텐션 점수를 손으로 계산하고, LayerNorm의 평균·분산을 직접 구하고, 학습 루프의 손실을 한 줄씩 짠다. 느리지만, 끝나고 나면 "GPT는 마법이 아니라 행렬 곱과 softmax의 반복"이라는 것을 몸으로 알게 된다.
저자 Sebastian Raschka는 위스콘신대 통계학 교수 출신의 유명 ML 교육자로, 이 책과 저장소는 출간 이후 개발자·학생·연구자 사이에서 "LLM을 진짜로 이해하려면 이걸 따라 짜 봐라"의 표준 교재로 자리 잡았다. 저장소는 책이 나온 2024년 이후에도 거의 매일 업데이트되며(최신 커밋 2026-08-09), 최신 오픈 모델을 from-scratch로 해부한 보너스가 계속 추가된다.
이 저장소는 신생 프로젝트가 아니다. 그런데도 TrendShift Daily 상위권에 다시 오르는 이유는, 책의 정적인 예제에 머물지 않고 2026년에 쏟아진 최신 오픈 모델들을 발표 직후 "from scratch"로 재현해 올리는 살아있는 아카이브로 진화했기 때문이다. 아래 세 가지가 핵심이다.
모두가 LLM API를 쓰지만, 정작 그 안을 아는 사람은 드물다. 면접·연구·실무 어디서든 "어텐션이 실제로 어떻게 계산되나?", "KV 캐시가 왜 필요한가?"를 코드로 설명할 수 있는 사람은 귀하다. 이 저장소는 그 간극을 정확히 메운다. 각 개념이 노트북 1개 = 개념 1개로 격리돼 있어, 궁금한 부품만 뜯어볼 수 있다.
2025~2026년 사이 저장소에는 최신 아키텍처의 from-scratch 구현이 줄줄이 추가됐다. Qwen3(dense+MoE), Gemma 3/4, Olmo 3, Qwen3.5, 그리고 DeepSeek-V3.2의 Sparse Attention(DSA)까지. 새 모델이 나올 때마다 "이번엔 뭐가 다른가"를 200줄 남짓의 읽을 수 있는 코드로 확인할 수 있으니, 최신 흐름을 따라가려는 개발자들이 반복해서 찾는다.
Hugging Face transformers는 "완성차 딜러" — 수천 개 모델을 즉시 굴릴 수 있지만 엔진 내부는 최적화·추상화로 덮여 있다. nanoGPT(Karpathy)는 "경주용 미니 엔진" — 극도로 간결하지만 학습 과정 설명은 최소한이다.
LLMs-from-scratch는 그 사이의 "정비 교육 과정"이다. 부품 하나하나를 왜·어떻게 조립하는지 설명(책)과 실행 코드(저장소)를 짝지어, "이해"에 최적화돼 있다.
저자는 여기서 멈추지 않고 속편 Build a Reasoning Model (From Scratch)를 냈고, 그 코드(reasoning-from-scratch)를 git 서브모듈로 연결해 뒀다. 사전학습된 모델에서 시작해 추론 능력(reasoning)을 강화하는 법 — inference-time scaling, 강화학습(GRPO/RLVR), 증류(distillation) — 을 역시 밑바닥부터 구현한다. "GPT 만들기 → 추론 모델로 키우기"의 연속된 커리큘럼이 완성된 셈이다.
이 저장소의 기술 선택 철학은 한마디로 "의존성 최소화"다. 딥러닝 프레임워크 하나(PyTorch)와 토크나이저 하나(tiktoken)만 실질적으로 쓰고, 어텐션·정규화·학습 루프처럼 보통 라이브러리에 맡기는 부분을 전부 직접 구현한다. 그래야 "안이 보이기" 때문이다.
| 계층 | 도구 | 역할 / 왜 이걸 쓰나 |
|---|---|---|
| 딥러닝 코어 | PyTorch ≥2.2 | 텐서·자동미분·nn.Module. 모델의 모든 부품(어텐션·MLP·LayerNorm)을 이 위에 손으로 쌓는다. 유일한 필수 프레임워크. |
| 토크나이저 | tiktoken | OpenAI GPT-2와 동일한 BPE 토크나이저. ch02는 이걸 쓰되, BPE 자체도 "from scratch"로 다시 만드는 보너스가 따로 있다. |
| 가중치 로딩 | TensorFlow | 의외의 등장. OpenAI가 공개한 원본 GPT-2 가중치가 TF 체크포인트라서, 그걸 읽어 PyTorch 모델에 이식할 때만 쓴다. |
| 수치/데이터 | NumPy · pandas | 데이터 전처리, ch06 스팸 분류 데이터셋(CSV) 다루기. |
| 시각화 | matplotlib | 손실 곡선, 어텐션 가중치, 학습률 스케줄 그리기. |
| 진행/실습 | tqdm · JupyterLab | 학습 진행 바, 그리고 모든 장이 대화형 노트북(.ipynb)으로 제공된다. |
import transformers 뒤에 숨는다. 이 저장소는 그 import를 금지함으로써, 학습자가 GPTModel 클래스를 직접 타이핑하게 만든다. 불편함이 곧 교육 설계다.| 영역 | 도구 | 내용 |
|---|---|---|
| 패키지 관리 | uv · pixi · pip | pyproject.toml 하나로 세 방식 모두 지원. uv(Rust 기반 초고속 설치)를 1순위로 권장. |
| 재사용 패키지 | llms-from-scratch (PyPI) | 각 장의 클래스를 라이브러리처럼 import. from llms_from_scratch.ch04 import GPTModel 식으로 노트북 밖에서도 재사용. |
| 테스트 | pytest + GitHub Actions | Linux·Windows·macOS 3개 OS에서 매 장의 코드가 실제로 도는지 CI로 검증. test_ch02~07, test_llama3, test_qwen3 등. |
| 환경 격리 | Docker · AWS SageMaker | setup/에 Docker 이미지와 SageMaker 노트북 설정까지 문서화. |
| 린트 | Ruff | line-length 140, 코드 스타일 자동 검사. |
즉 "교육용 예제"라고 얕볼 코드가 아니다. 3개 OS CI, PyPI 배포, 테스트 스위트를 갖춘 실무 수준의 저장소 위생을 유지하면서, 내용물은 초심자가 읽을 수 있게 유지한다는 점이 이 프로젝트의 완성도다.
GPT의 추론은 "토큰 ID 뭉치 → 다음 토큰 확률"로 요약된다. 저장소의 GPTModel이 만드는 흐름을 그리면 이렇다.
여기서 반복되는 핵심 부품은 Transformer 블록 하나다. 이 블록이 "문맥을 읽는 부분(어텐션)"과 "각 토큰이 혼자 생각하는 부분(피드포워드)"으로 나뉘고, 각각을 shortcut(잔차 연결)과 Pre-LayerNorm으로 감싼다. 저장소의 실제 코드는 이렇게 짧다.
class TransformerBlock(nn.Module):
def forward(self, x):
# ① 어텐션 서브블록 (Pre-LN + 잔차)
shortcut = x
x = self.norm1(x) # 먼저 정규화
x = self.att(x) # 문맥 읽기 (멀티헤드 어텐션)
x = self.drop_shortcut(x)
x = x + shortcut # 입력을 다시 더함 → 기울기 소실 방지
# ② 피드포워드 서브블록 (Pre-LN + 잔차)
shortcut = x
x = self.norm2(x)
x = self.ff(x) # 토큰별 비선형 변환 (4배 확장 → GELU → 축소)
x = self.drop_shortcut(x)
x = x + shortcut
return x
층을 깊게 쌓으면 기울기가 뒤로 전달되며 점점 작아져 사라진다(기울기 소실). 잔차 연결은 각 블록에 "원본 입력으로 가는 지름길"을 하나 더 놓는 것이다. 덕분에 12층, 48층을 쌓아도 학습 신호가 맨 앞까지 도달한다. 고속도로에 비상 갓길을 하나 더 깔아 두는 것과 같다.
멀티헤드 어텐션은 이 저장소에서 가장 공들여 설명하는 부분이다. 핵심은 "각 토큰이 앞선 토큰들 중 누구를 얼마나 참고할지" 점수를 매기고(causal mask로 미래는 못 보게 막고), 그 점수로 값(value)을 가중합하는 것이다.
class MultiHeadAttention(nn.Module):
def forward(self, x):
keys = self.W_key(x) # 각 토큰의 "이름표"
queries = self.W_query(x) # 각 토큰의 "질문"
values = self.W_value(x) # 각 토큰의 "내용물"
# d_out을 num_heads개로 쪼갬 → 여러 관점에서 동시에 본다
# (b, tokens, heads, head_dim) 로 view 후 transpose
attn_scores = queries @ keys.transpose(2, 3) # 질문·이름표 유사도
attn_scores.masked_fill_(mask_bool, -torch.inf) # 미래 토큰 차단(causal)
attn_weights = torch.softmax(
attn_scores / keys.shape[-1]**0.5, dim=-1) # 스케일 후 확률화
context = (attn_weights @ values) # 내용물 가중합
return self.out_proj(context) # 헤드 합치고 투영
masked_fill_(mask_bool, -inf) 한 줄이 GPT를 "생성 모델"로 만든다. 각 토큰이 자기보다 뒤(미래)의 토큰을 못 보게 어텐션 점수를 음의 무한대로 눌러, softmax 후 0이 되게 한다. 시험 볼 때 뒷장을 가리는 것과 같다 — 그래야 "다음 단어 맞히기"를 정직하게 학습한다.놀라운 점은, GPT-2의 네 가지 크기가 완전히 같은 코드에 숫자만 바꿔 나온다는 것이다. 설정 딕셔너리의 세 값(임베딩 차원·층 수·헤드 수)만 키우면 124M이 1.5B이 된다.
| 모델 | emb_dim | n_layers | n_heads | 파라미터 |
|---|---|---|---|---|
| gpt2-small | 768 | 12 | 12 | 124M |
| gpt2-medium | 1024 | 24 | 16 | 355M |
| gpt2-large | 1280 | 36 | 20 | 774M |
| gpt2-xl | 1600 | 48 | 25 | 1558M |
공통 설정은 어휘 50,257개, 문맥 길이 1,024토큰. 본편은 이 중 가장 작은 124M을 노트북에서 직접 학습시키고, 나머지 큰 모델은 OpenAI 공개 가중치를 로딩해 파인튜닝에 쓴다.
ch04·ch05의 보너스 폴더가 이 저장소를 "책 부록"에서 "최신 아키텍처 도감"으로 끌어올린다. 표준 멀티헤드 어텐션을 기준점 삼아, 그 뒤에 나온 효율화 기법들을 하나씩 갈아 끼워 본다.
| 기법 | 핵심 아이디어 | 어디서 왔나 |
|---|---|---|
| GQA | Key/Value 헤드를 여러 Query가 공유 → KV 캐시 메모리 절감 | Llama 2/3 |
| MLA | Key/Value를 저차원으로 압축 저장(latent) 후 복원 | DeepSeek-V2/V3 |
| SWA | 각 토큰이 "가까운 과거 고정 창"만 봄 → O(L²)→O(L·w) | Mistral |
| DSA | 고정 창 대신 학습된 선택기가 중요한 과거 토큰만 고름 | DeepSeek-V3.2 |
| MoE | FFN을 여러 전문가로 쪼개고, 라우터가 토큰당 소수만 활성화 | DeepSeek-V3, Qwen3 |
DeepSeek-V3는 전문가(expert)가 256개, 총 파라미터가 6,710억개다. 하지만 토큰 하나를 처리할 때 라우터가 켜는 전문가는 단 9개(공유 1 + 선택 8)뿐 — 실제로 계산에 쓰이는 건 370억개다.
큰 도서관(671B의 지식)을 지어 두되, 질문 하나엔 관련 서가(37B)만 걸어가 뒤지는 셈이다. "용량은 크게, 계산은 작게"가 MoE의 핵심 트릭이고, 저장소는 이 절감 효과를 실제 코드로 측정해 보여준다.
구조를 이해하는 열쇠는 하나다. 책의 각 장이 폴더 하나에 대응하고, 각 폴더 안에서 01_main-chapter-code/가 본문 코드, 나머지 번호 폴더가 심화 보너스다. 궁금한 주제를 목차에서 찾듯 폴더로 바로 들어가면 된다.
| 경로 | 무엇이 들었나 |
|---|---|
| chNN/01_main-chapter-code/ | 그 장의 "정답 코드". 책 본문과 1:1로 대응. 여기만 따라가도 GPT가 완성된다. |
| chNN/02_bonus_.../ | 본문에서 다 못 담은 심화. 효율적 어텐션, 하이퍼파라미터 튜닝, UI 만들기 등 선택 학습. |
| pkg/llms_from_scratch/ | 각 장의 클래스를 라이브러리로 묶은 것. ch02.py~ch07.py, qwen3.py, llama3.py, KV 캐시 변형 + pytest 테스트. |
| appendix-A/ | PyTorch 입문 + DDP-script.py(다중 GPU 분산 학습 예제). |
| setup/ | Python 설치 팁 → 라이브러리 설치 → Docker → AWS SageMaker. 초심자 진입 장벽 제거용. |
ch04/01_main-chapter-code/gpt.py는 ch02~ch04의 모든 코드(데이터로더 + 어텐션 + GPT 모델)를 독립 실행 가능한 단일 스크립트로 모아 둔 것이다. 전체 그림을 빨리 보고 싶으면 이 파일 하나부터 읽고, 세부는 각 장 노트북으로 파고들면 된다.
| 장 | 배우는 개념 | 핵심 산출물 |
|---|---|---|
| Ch2 | 토큰화, BPE, 임베딩, 슬라이딩 윈도우 데이터로더 | 텍스트 → 학습용 (입력, 정답) 배치 |
| Ch3 | self-attention → causal → multi-head, 스케일링, 마스킹 | MultiHeadAttention 클래스 |
| Ch4 | LayerNorm·GELU·FFN·잔차연결·Transformer 블록 | 완전한 GPTModel |
| Ch5 | 교차엔트로피·perplexity, 학습 루프, 생성(temperature·top-k), 가중치 로딩 | 사전학습된 124M + 텍스트 생성 |
| Ch6 | 분류 헤드 교체, 스팸 분류 파인튜닝 | 스팸 판별 GPT |
| Ch7 | 지시 데이터셋(Alpaca식), 지시 파인튜닝, Ollama로 응답 평가, DPO 정렬 | 지시 따르는 챗봇형 GPT |
exp()를 씌운 값으로, "모델이 평균 몇 개의 선택지 사이에서 갈팡질팡하는가"로 해석된다. 낮을수록 좋다. ch05에서 학습이 진행되며 이 값이 떨어지는 걸 눈으로 확인한다.단순 문법이 아니라 "왜 이 순서로, 왜 이 부품을 쓰는가"라는 아키텍처적 직관 — Pre-LN이 왜 학습을 안정시키는지, GQA가 왜 메모리를 아끼는지, 파인튜닝이 왜 사전학습보다 값싼지 — 를 코드를 짜 보며 체득하는 것이 이 저장소의 진짜 소득이다. 이건 API만 써서는 절대 생기지 않는 감각이다.
각 부품이 격리돼 있어 "한 조각만 바꿔 실험"이 쉽다. 예를 들어: 표준 어텐션을 GQA로 갈아 끼우고 KV 캐시 메모리를 재 보기 · emb_dim만 키워 손실 변화 관찰 · GELU를 SwiGLU로 교체 · temperature/top-k 값을 바꿔 생성 문장의 다양성 비교 · 자신의 텍스트로 작은 코퍼스를 만들어 사전학습 돌려 보기.
저자는 본편 코드를 의도적으로 일반 노트북에서 합리적 시간 안에 돌아가게 설계했다. 특수 하드웨어가 없어도 누구나 따라올 수 있게 하려는 것이다. GPU가 있으면 코드가 자동으로 감지해 쓰고, 없으면 CPU로도 진행된다.
| 항목 | 요구/권장 |
|---|---|
| Python | 3.10 ~ 3.14 |
| 본편 학습(124M) | 일반 노트북 CPU로도 가능(느림). Apple Silicon(MPS)·GPU 자동 활용. |
| GPU 메모리 | 필수 아님. 있으면 학습 속도 크게 향상. 큰 모델 실험 시 권장. |
| 가중치 로딩 | OpenAI GPT-2 원본 로딩엔 TensorFlow 설치 필요(디스크 여유). |
| 대규모 실험 | Gutenberg 사전학습·다중 GPU(DDP)·SageMaker는 별도 클라우드/GPU 환경 권장. |
여기서 학습하는 124M 모델은 교육용 축소판이다. 문장을 그럴듯하게 잇지만, 상용 챗봇 수준의 성능은 아니다. 이 저장소의 목표는 성능이 아니라 이해다. "작동 원리가 대형 모델과 동일한 최소 모델"을 손에 넣는 것이 핵심이다.
ch04/01_main-chapter-code/gpt.py를 그대로 실행해 초기화된 124M 모델로 텍스트를 생성해 본다. 그 다음 ch05로 넘어가 OpenAI GPT-2 가중치를 로딩하고, temperature와 top_k 값을 0.1 / 1.0 / 2.0으로 바꿔 가며 같은 프롬프트의 출력이 어떻게 달라지는지 관찰한다. "결정적 vs 창의적" 생성의 차이를 체감하는 것이 목표.
MultiHeadAttention.forward에서 attn_weights를 밖으로 꺼내 matplotlib 히트맵으로 그린다. 특정 문장을 넣었을 때 어떤 토큰이 어떤 토큰을 "쳐다보는지"를 눈으로 확인한다. causal mask 때문에 오른쪽 위 삼각형이 항상 비어 있는 것도 확인해 볼 것.
ch07의 지시 파인튜닝 파이프라인에 자신이 만든 지시-응답 쌍(수십~수백 개)을 넣어 작은 도메인 챗봇을 만든다. 학습 전후 응답을 ollama_evaluate.py로 채점해 개선 폭을 정량화한다. 데이터 품질이 성능을 어떻게 좌우하는지 체감하는 과제.
ch04 보너스의 GQA·MLA·SWA를 표준 MHA 대신 끼워 넣고, 동일 조건에서 KV 캐시 메모리 사용량 · 추론 속도 · perplexity를 표로 비교한다. "효율화 기법이 정확도를 얼마나 희생하는가"의 트레이드오프를 직접 측정한다.
ch05/16_qwen3.5 또는 17_gemma4 노트북을 정독하며 최신 아키텍처를 손으로 재현한 뒤, 서브모듈 reasoning-from-scratch로 넘어가 GRPO 강화학습으로 추론 능력을 붙여 본다. "사전학습된 모델 → 추론 모델"의 전체 여정을 완주하는 최종 과제.
| 주차 | 주제 | 저장소 경로 / 병행 학습 |
|---|---|---|
| 1주 | PyTorch 기초 다지기 | appendix-A (텐서 → 자동미분 → 학습 루프 → DDP) |
| 2주 | 텍스트 → 숫자, 어텐션 직관 | ch02(토큰화·임베딩) → ch03(self→multi-head) |
| 3주 | GPT 본체 조립 | ch04 (LayerNorm·GELU·FFN·Transformer 블록) |
| 4주 | 사전학습 + 텍스트 생성 | ch05 (손실·학습 루프·temperature/top-k·가중치 로딩) |
| 5주 | 학습 안정화 트릭 | appendix-D (LR 워밍업·코사인 감쇠·그래디언트 클리핑) |
| 6주 | 파인튜닝 2종 | ch06(분류) → ch07(지시) → appendix-E(LoRA) |
| 7주 | 효율화 아키텍처 | ch04 GQA/MLA/SWA/MoE · ch05 KV 캐시·Muon |
| 8주 | 추론 모델로 확장 | reasoning-from-scratch (inference scaling·GRPO·distillation) |
저자가 만든 17시간 15분 영상 강의(Manning)가 각 장을 코드로 함께 짜 준다. 책·저장소·영상 3종을 병행하면 "글로 읽고 → 코드로 짜고 → 손동작으로 확인"의 삼중 학습이 된다. 또 저자의 블로그·뉴스레터(Ahead of AI)가 최신 모델 해설의 원천이라, 보너스 폴더와 짝지어 읽으면 이해가 깊어진다.
norm→att). GPT-2·현대 LLM의 표준으로, 깊은 층에서도 학습이 안정적이다.