
Karpathy 씨의 200줄 GPT 「microGPT」를 한 줄씩 파헤치기
요약
Andrej Karpathy가 공개한 200줄 규모의 microGPT 코드를 한 줄씩 분석하며 Transformer 구조를 파헤칩니다. 외부 라이브러리 없이 Python만으로 구현된 이 코드를 통해 GPT의 데이터 준비, 토크나이저, Autograd 엔진의 원리를 학습할 수 있습니다.
핵심 포인트
- 외부 라이브러리 없이 구현된 200줄의 microGPT 코드 분석
- 데이터 준비부터 토크나이저, Autograd 엔진까지의 핵심 구조 이해
- GPT-4와 microGPT의 파라미터 규모 및 학습 데이터 차이 비교
- Python만으로 구동 가능한 Transformer 아키텍처의 동작 원리 습득
2026년 2월에 Andrej Karpathy(안드레 카파시) 씨가 공개한 「microGPT」. 화제가 되었을 때 만져보며 중간까지 기사를 쓰다 말았는데, 완전히 잊고 있었기에 이제야 공개해 봅니다.
200줄의 외부 라이브러리를 사용하지 않는(PyTorch도 NumPy도 없습니다) 방식으로 GPT를 훈련(Training) 및 추론(Inference)하는 의욕적인 코드입니다.
이 기사에서는 microGPT를 구동하는 방법을 소개함과 동시에, 코드의 내용을 조금 깊이 파고들며 GPT(Transformer)의 구조를 이해해 보고자 합니다.
참고로, 시각화 코드나 코드 이해에는 AI를 활용한 후 나름대로 체크를 거쳤습니다. 틀린 부분이 있다면 지적해 주시면 감사하겠습니다.
또한, 비슷한 컨셉으로 실제로 한 줄씩 실행하며 시각화하면서 이해를 높이는 기사로는 아래 기사가 이해하기 쉽고 퀄리티가 높으므로, 이 기사를 읽지 않고 아래 기사를 읽으셔도 좋습니다.
논리보다 RUN
우선 일단 실행해 봅시다. 라이브러리 의존성이 없기 때문에 Python만 설치되어 있다면 Mac에서도 Linux에서도 Windows(WSL2)에서도 간단히 구동할 수 있을 것입니다.
터미널에서 다음 명령어를 실행해 주세요.
curl -O https://gist.githubusercontent.com/karpathy/8627fe009c40f57531cb18360106ce95/raw/microgpt.py
python3 microgpt.py
다음과 같이 학습이 시작되고, 마지막에 학습된 모델을 통한 추론 결과가 나옵니다.
num docs: 32033
vocab size: 27
num params: 4192
...
아무래도 사람의 이름을 학습하여 사람다운 이름을 생성하는 프로그램인 것 같습니다.
"도저히 내 PC에서는 돌아가지 않는다!" 하는 분은, 아래 사이트에서 「RUN」 하면 일단 브라우저에서 돌아갈 것입니다.
microGPT의 전체상
microGPT의 전체 구성입니다. 200줄의 코드는 크게 6개의 섹션으로 나뉩니다.
| 섹션 | 행수 | 내용 |
|---|---|---|
| 1. 데이터 준비 | ~20행 | 이름 데이터셋 로드 |
| ... |
ChatGPT와 아키텍처(Architecture)는 같지만, 규모는 전혀 다릅니다.
microGPT: 4,192 파라미터(Parameter), 이름 데이터로 학습, 이름 생성 -
GPT-4: 수천억 파라미터(추정), 인터넷 규모의 텍스트로 학습, 대화 가능
여기서부터가 코드 해설입니다.
섹션 1: 데이터 준비와 토크나이저(Tokenizer)
데이터셋
# input.txt가 없으면 다운로드 (약 32,000개의 영어 인명)
if not os.path.exists('input.txt'):
import urllib.request
...
학습 데이터는 약 32,000개의 영어 인명입니다. 이 파일에서 원본 데이터를 확인할 수 있습니다.
이를 docs에 로드하여 무작위로 섞고 있습니다.
토크나이저(Tokenizer)
# 모든 문서에서 고유한 문자를 추출 → ['a', 'b', ..., 'z'] (26자)
uchars = sorted(set(''.join(docs)))
BOS = len(uchars) # BOS (Beginning Of Sequence) = 26
...
ChatGPT의 토크나이저(BPE, 어휘 수 10만 이상)와 비교하면 매우 단순하지만, 하고 있는 일의 본질은 같습니다. 문자열을 수치 ID로 변환하여 모델이 처리할 수 있도록 합니다.
섹션 2: Autograd (자동 미분 엔진)
PyTorch의 torch.Tensor의 초간이 버전을 구현하고 있습니다.
Value 클래스가 다음 값들을 가집니다.
data: 스칼라 값 (포워드 패스(Forward Pass)에서 계산) -
grad: 기울기 (백워드 패스(Backward Pass)에서 계산) -
_children: 계산 그래프(Computation Graph) 상의 자식 노드 -
_local_grads: 국소 기울기
class Value:
__slots__ = ('data', 'grad', '_children', '_local_grads')
def __init__(self, data, children=(), local_grads=()):
...
각 연산에서 계산 그래프 구축
def __add__(self, other):
# 덧셈: f(a, b) = a + b → df/da = 1, df/db = 1
other = other if isinstance(other, Value) else Value(other)
...
연산할 때마다 「국소 기울기 (local gradient)」를 함께 기록하고 있습니다.
미분 (calculus)입니다. 덧셈이라면 기울기는 (1, 1),
곱셈이라면 (b, a)
입니다. 그 외에는 다음과 같은 느낌입니다.
out = a ** n → ∂out/∂a = n·a^(n-1)
out = log(a) → ∂out/∂a = 1/a
out = exp(a) → ∂out/∂a = exp(a)
...
relu는 딥러닝 (deep learning)에서 자주 사용되는 활성화 함수 (activation function)입니다. 그림으로 그리면 간단하여, 아래 그림의 왼쪽처럼 0보다 크면 비례하고, 0 이하면 0인 상태를 유지하는 함수입니다.

미분하면 0보다 크면 1, 0 이하면 0이 되기 때문에, 계산을 단순화할 수 있다는 것이 장점입니다.
이것들이 나중에 역전파 (backpropagation)에 사용됩니다.
역전파 (Backpropagation)
def backward(self):
# 스텝 1: 위상 정렬 (topological sort, 의존 순서대로 나열)
topo = []
...
재귀 (recursion)를 많이 사용하기 때문에 조금 이해하기 어려울 수 있지만, 요점은 "자식 (입력 측)이 먼저, 부모 (출력 측)가 나중"인 순서로 리스트화하고 있습니다. 리스트에 추가하면 visited에 넣어, 동일한 노드를 두 번 나열하지 않도록 하고 있습니다.
reversed(topo)를 통해 순서를 자식→부모에서 부모→자식으로 역전시켜, 각 노드 v에 대해 다음과 같은 계산을 수행하며, 연쇄 법칙 (chain rule)에 의해 오차를 역전파합니다.
child.grad += local_grad * v.grad
섹션 3: 파라미터 초기화
n_embd = 16 # 임베딩 차원 (embedding dimension)
n_head = 4 # 어텐션 헤드 (attention head) 수
n_layer = 1 # 레이어 (layer) 수 (GPT-2는 12~48층)
...
가중치 행렬 (weight matrix)을 가우스 분포 (Gaussian distribution)로 초기화하고 있습니다.
matrix = lambda nout, nin, std=0.08: [
[Value(random.gauss(0, std)) for _ in range(nin)]
for _ in range(nout)
...
전체 파라미터 수는 4,192개입니다. GPT-2의 1.24억 개와 비교하면 매우 작지만, 구조는 동일합니다.
섹션 4: Transformer 아키텍처
GPT-2를 준수하고 있지만, 세 가지 간략화가 있습니다.
- LayerNorm → RMSNorm (학습 가능한 파라미터 없음)
- GeLU → ReLU (활성화 함수 간략화)
- 바이어스 없음 (모든 선형층 (linear layer))
선형 변환 (Linear Transformation)과 소프트맥스 (Softmax)
다음과 같습니다. 해설은 생략합니다.
def linear(x, w):
"""행렬 × 벡터: y = Wx"""
return [sum(wi * xi for wi, xi in zip(wo, x)) for wo in w]
...
GPT 본체
def gpt(token_id, pos_id, keys, values):
# 임베딩 (embedding): 토큰 정보 + 위치 정보
tok_emb = state_dict['wte'][token_id]
...
tok_emb와 pos_emb는 토큰 임베딩 벡터 (token embedding vector)와 위치 임베딩 벡터 (position embedding vector)입니다. 이것들을 합성한 것을 학습합니다.
참고로, 위치 정보 임베딩에 관해서는 「Attention is All You Need」 논문에서는 sin/cos를 사용한 포지셔널 인코딩 (Positional Encoding)이라는 기법이 사용되었고, 최근에는 RoPE (Rotary Position Embedding)라는 토큰의 상대적 위치를 회전 행렬 (sin이나 cos 성분)로 표현하는 기법이 사용되기도 합니다.
여기서부터가 Transformer의 핵심 부분입니다.
멀티 헤드 셀프 어텐션 (Multi-Head Self-Attention)
어텐션 부분입니다. 다음의 세 가지 벡터 Q (Query), K (Key), V (Value)를 정의합니다.
- Query: 각 입력 토큰이 「무엇을 찾고 있는가」를 나타내는 벡터
- Key: 각 요소의 「라벨」과 같은 벡터
- Value: 실제로 추출하고자 하는 「정보」를 나타내는 벡터
# Q, K, V를 투영 (Projection)
q = linear(x, state_dict[f'layer{li}.attn_wq'])
k = linear(x, state_dict[f'layer{li}.attn_wk'])
...
4개의 헤드(head)가 병렬로 동작하며, 각각 서로 다른 「관점」에서 주목합니다.
for h in range(n_head): # 4개의 헤드
# 어텐션 스코어 (Attention Score): Q·K^T / √d_k
attn_logits = [
...
수식으로는 다음과 같습니다.
/ head_dim**0.5
`) 의 스케일링 (Scaling)은, 내적 (dot product) 값이 너무 커져서 소프트맥스 (Softmax)가 극단적인 분포가 되는 것을 방지합니다.
MLP (Feed-Forward Network)
# 16차원 → 64차원 → ReLU → 16차원
x = linear(x, state_dict[f'layer{li}.mlp_fc1']) # 확장
x = [xi.relu() for xi in x] # 비선형성 (Non-linearity)
...
어텐션 (Attention)에서 모은 정보를 MLP로 처리합니다. 차원을 4배로 확장하여 ReLU 함수를 거친 뒤 다시 원래대로 되돌리는 것은 GPT-2와 동일한 구조입니다.
잔차 연결 (Residual Connection)
x = [a + b for a, b in zip(x, x_residual)]
어텐션 전후, MLP 전후에서 입력을 다시 더해줍니다. 이것이 기울기 소실 (Gradient Vanishing)을 방지하고, 깊은 네트워크의 학습을 안정화하는 것으로 보입니다.
섹션 5: 훈련 루프 (Adam)
for step in range(1000):
# 1개의 문서를 토큰화 (Tokenization)
doc = docs[step % len(docs)]
...
기울기 (Gradient)를 바탕으로 파라미터를 Adam이라는 대표적인 기법을 사용하여 최적화 (Optimization)합니다.
range(1000)
에서 알 수 있듯이 1000 스텝 (step) 동안 학습을 진행합니다.
섹션 6: 추론 (Inference)
temperature = 0.5 # 낮을수록 → 보수적, 높을수록 → 창의적
for sample_idx in range(20):
token_id = BOS # BOS 토큰부터 생성 시작
...
추론을 통해 학습된 모델로부터 새로운 이름을 생성합니다.
다음은 출력 예시입니다.
sample 1: kamon
sample 2: ann
sample 3: karai
...
이름 같기도 하고, 이름 같지 않기도 한 문자열이 출력되었습니다.
시각화: 모델의 학습을 「보다」
코드를 읽는 것뿐만 아니라, 훈련 중의 내부 상태를 시각화해 보았습니다.
Loss 곡선

훈련이 진행됨에 따라 Loss가 내려가는 모습. 3.3에서 2.0 전후로 수렴.
초기 Loss는 ln(27) ≈ 3.3
(랜덤 예측의 교차 엔트로피 (Cross-Entropy)). 즉, 처음에는 27개의 문자 중에서 완전히 랜덤하게 예측하다가, 학습이 진행됨에 따라 패턴을 파악해 나갑니다.
생성 텍스트의 변화

학습 초기에는 의미를 알 수 없는 문자열이지만, 학습이 진행됨에 따라 이름처럼 변해감.
Step 1에서는 의미를 알 수 없는 문자의 나열이지만, Step 1000에서는 실제로 존재해도 이상하지 않을 이름이 생성됩니다.
파라미터 분포의 변화

초기에는 좁은 가우스 분포 (Gaussian Distribution)였던 가중치 (Weight)가 학습이 진행됨에 따라 넓어짐
Attention 가중치 히트맵 (Heatmap)

4개의 헤드가 서로 다른 패턴으로 과거의 토큰에 주목하고 있는 모습.
학습 초기에는 어텐션 (Attention)이 거의 균일하지만, 학습이 진행되면 각 헤드가 서로 다른 주목 패턴을 학습해 나갑니다. 어떤 헤드는 직전의 토큰에 집중하고, 다른 헤드는 더 넓은 범위를 보고 있습니다.
시각화 코드
시각화에 사용한 코드는 다음과 같습니다. 참고용입니다.
시각화 코드
"""
microGPT 시각화 스크립트 — 훈련 중의 내부 상태 시각화
원본: https://gist.github.com/karpathy/8627fe009c40f57531cb18360106ce95
...
요약
요약
microGPT를 정성껏 읽고 분석해 보았습니다. Transformer는 상당히 복잡하기 때문에 제대로 이해하는 것이 꽤 어렵다고 생각하지만, 이렇게 200줄로 압축된 코드를 따라가다 보면 이해를 심화하는 데 매우 좋네요.
모르는 부분은 말 그대로 LLM에게 물어보거나 시각화(Visualization)를 요청함으로써 이해하는 데 도움을 받았습니다.
참고 링크
- microGPT (Gist) — 본체 코드
- microGPT (Blog) — Karpathy 씨에 의한 해설
- micrograd — Autograd의 모태
- makemore — 이름 데이터셋의 출처
관련 기사
Discussion

AI 자동 생성 콘텐츠
본 콘텐츠는 Zenn AI의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기