Mukul-svg/graddy는 순수 파이썬으로 짠 아주 작은 autograd(자동 미분) 엔진 + 신경망 라이브러리입니다. Andrej Karpathy의 micrograd 계열 교육용 프로젝트로, 숫자 하나하나에 직접 사칙연산을 하면 계산 그래프가 쌓이고, .backward() 한 번에 미분(기울기)이 거꾸로 흘러내리는 과정을 99줄짜리 engine.py에 다 담았습니다. 여기에 micrograd엔 없는 '학습된 신경망을 matplotlib으로 그리는' 시각화가 더해진 게 차별점입니다. (저장소: Mukul-svg/graddy · 언어 Python 100% · ★ 신생(거의 0) · 라이선스 MIT · 2026-06-23 생성)
"PyTorch의 loss.backward()가 안에서 무슨 일을 하는지, 99줄로 직접 열어 보는 프로젝트."
딥러닝을 배우면 누구나 loss.backward()를 칩니다. 하지만 그 한 줄 안에서 무슨 일이 벌어지는지는 PyTorch라는 거대한 블랙박스 뒤에 숨어 있습니다. graddy는 그 블랙박스를 99줄로 줄여 열어 줍니다 — 숫자를 Value라는 객체로 감싸고, 그 객체들끼리 +·*를 하면 계산 그래프(DAG)가 저절로 그려지고, 마지막 결과에서 .backward()를 부르면 연쇄법칙(chain rule)으로 기울기가 거꾸로 흘러 각 숫자의 .grad에 쌓입니다.
핵심 사용법은 micrograd와 똑같이 단순합니다 — Value(-4.0) 같은 숫자를 만들고, 평범하게 수식을 쓰고, y.backward()를 부른 뒤 x.grad를 읽으면 됩니다. 이 문서는 그 안의 설계 — "연산자 오버로딩으로 그래프를 어떻게 짓나", "backward()의 위상정렬은 왜 필요한가", "기울기를 왜 =가 아니라 +=로 쌓나", 그리고 "학습된 신경망을 어떻게 그림으로 보여 주나"를 파고듭니다.
"backprop을 '소스로' 배우는 micrograd 계보. graddy의 한 끗은 '학습 결과를 눈으로 보는' 시각화다."
graddy는 2026년 6월 23일에 만들어진 신생 저장소로, 분석 시점 기준 커밋 1개·별 0개입니다. '트렌딩'에 잡힌 건 인지도가 아니라 신선함 + micrograd라는 사랑받는 장르 + 좋은 GitHub 토픽 덕입니다. 즉, 검증된 라이브러리가 아니라 잘 만든 '학습용 개인 프로젝트'로 보는 게 맞습니다. (그래서 이 문서도 '쓰는 도구'가 아니라 '읽고 배우는 교본'으로 다룹니다.)
backprop을 코드로 배우는 길에서 micrograd는 이미 고전입니다. graddy는 그 계보 위에 두 가지를 더합니다 — 엔진에 exp()·tanh() 활성함수를 추가했고(micrograd 엔진은 relu만), 무엇보다 '학습이 끝난 신경망'을 그림으로 그리는 시각화를 넣었습니다.
| 관점 | graddy | micrograd (Karpathy) |
|---|---|---|
| 입자 단위 | 스칼라(Value=숫자 하나) | 스칼라(동일) |
| 활성함수 | relu·exp·tanh | 엔진은 relu 중심 |
| 시각화 | 학습된 '망 구조'를 matplotlib으로 (가중치=색·굵기, 편향=주석) | Graphviz로 '계산 그래프' |
| 검증 | PyTorch와 기울기 대조 테스트 | 데모·sklearn moons |
micrograd의 유명한 draw_dot은 모든 연산 노드를 그린 계산 그래프입니다. graddy의 draw_neural_network()는 다릅니다 — 학습이 끝난 신경망의 뉴런과 가중치 자체를 그립니다. 가중치 부호는 색(양수 초록·음수 빨강), 크기는 선의 굵기·투명도로, 각 뉴런의 편향(bias)은 아래에 숫자로. 그래서 "훈련이 가중치를 어떻게 바꿨나"를 한눈에 볼 수 있습니다. backprop 입문자에게 특히 직관적입니다.
다만 솔직히 짚으면, README는 '텐서 지원'·'신경망 라이브러리'라며 다소 부풀려 말하지만 현재는 철저히 스칼라입니다(가중치 곱 하나하나가 파이썬 float 연산이라 느림). 학습 도구로는 그게 오히려 장점입니다 — 모든 게 느리지만 모든 게 들여다보입니다.
"코어는 의존성 0 — 파이썬 표준 math/random뿐. 외부 라이브러리는 데모·테스트·그림용으로만 등장한다."
graddy의 무게중심은 딱 두 파일 — engine.py(autograd 스칼라)와 nn.py(그 위에 올린 신경망)입니다. 이 둘은 외부 패키지 없이 파이썬 표준 라이브러리만 씁니다. 나머지(numpy·matplotlib·sklearn·torch)는 데모와 테스트, 그림에만 쓰입니다.
| 기술 | 역할 | 한 줄 설명 |
|---|---|---|
| Python 3.6+ | 전체 언어 | f-string 정도만 쓰는 평범한 파이썬. 별도 빌드 도구 없음. |
표준 math | engine.py | exp 등 — 활성함수 계산에만. autograd 자체엔 외부 의존성 0. |
표준 random | nn.py | 가중치 초기화(uniform(-1,1)). |
a + b를 쓰면 내부적으로 a.__add__(b)가 불립니다. 연산자 오버로딩은 그 __add__ 같은 함수를 내 클래스에 맞게 새로 정의하는 것입니다. graddy는 Value 클래스의 __add__·__mul__·__pow__를 재정의해, "값끼리 더하면 새 그래프 노드가 생기고 미분 규칙이 따라붙도록" 만듭니다. 즉, 평범한 수식을 쓰는 행위가 곧 그래프를 짓는 행위가 됩니다.| 기술 | 역할 |
|---|---|
| torch (PyTorch) | test/test.py에서만 — graddy의 forward·grad가 PyTorch와 같은지 대조(골든 레퍼런스 테스트). |
| numpy | 좌표 계산(linspace·meshgrid) — 시각화·결정경계 그림용. |
| matplotlib | 시각화 백엔드. graphviz 불필요(micrograd와 달리 그림이 순수 matplotlib). |
| scikit-learn | make_moons 장난감 데이터셋(이진 분류 데모). |
주의할 점 — 저장소에 setup.py·pyproject.toml·requirements.txt가 전혀 없습니다. 즉 pip install로 깔리는 패키지가 아니라, 클론해서 저장소 안에서 실행하거나 sys.path에 경로를 넣어 쓰는 구조입니다. 패키지 __init__.py도 비어 있어 from graddy.engine import Value처럼 모듈을 콕 집어 임포트합니다.
"핵심은 두 가지 — 연산할 때 그래프가 저절로 그려지고, backward는 그 그래프를 거꾸로 한 번 훑는다."
모든 숫자는 Value(data, _children, _op) 객체입니다. 각 노드는 값(data)·기울기(grad)·부모로 가는 화살표(_prev)·연산 이름(_op), 그리고 결정적으로 자기 기울기를 자식에게 밀어 넣는 함수 _backward를 들고 있습니다.
backward()는 깊이우선탐색으로 그래프를 위상정렬(자식이 부모보다 먼저 오도록 줄세우기)한 뒤, 출력의 기울기를 1.0으로 두고 역순으로 각 노드의 _backward()를 부릅니다. 모든 국소 규칙이 +=로 더하기 때문에, 한 값이 여러 곳에 쓰여도 기울기가 올바르게 합산됩니다.
# engine.py — backward의 전부
def backward(self):
topo = []; visited = set()
def build_topo(v):
if v not in visited:
visited.add(v)
for child in v._prev:
build_topo(child)
topo.append(v) # 후위순회 → 자식이 부모보다 앞에
build_topo(self)
self.grad = 1.0 # 출력의 기울기를 1로 씨앗
for node in reversed(topo): # 부모 → 자식 순으로
node._backward() # 연쇄법칙을 += 로 흘린다
Neuron은 가중치들(Value)과 편향 하나를 들고 Σ wᵢ·xᵢ + b를 계산한 뒤(이 계산 자체가 그래프를 늘림) relu를 씌웁니다. Layer는 뉴런들의 묶음, MLP는 층들의 연결입니다. 영리하게도 마지막 층만 활성함수를 빼(nonlin = i != 마지막) 출력이 relu에 눌리지 않게 합니다.
# nn.py — 순전파가 곧 그래프를 늘리는 행위
def __call__(self, x):
act = sum((wi*xi for wi,xi in zip(self.w, x)), self.b) # Σ wᵢxᵢ + b
return act.relu() if self.nonlin else act # 마지막 층은 선형
Optimizer 클래스가 없습니다. 각 데모가 직접 기울기 반대 방향으로 가중치를 조금 옮기는 코드를 씁니다: p.data -= lr * p.grad. 이게 경사하강법(gradient descent)의 알맹이입니다 — "오차를 줄이는 방향(=기울기의 반대)으로 한 걸음씩". 일부 데모는 여기에 학습률 감소·L2 정규화·hinge 손실을 더합니다. 옵티마이저가 없다는 건 결함이 아니라, 가장 중요한 한 줄을 숨기지 않고 보여 주는 교육적 선택입니다.model.zero_grad() # 지난 기울기 0으로
loss.backward() # 역전파로 모든 grad 채움
for p in model.parameters():
p.data -= learning_rate * p.grad # 한 걸음 갱신(SGD)
"전부 합쳐 ~517줄. autograd는 engine.py, 신경망은 nn.py, 나머지는 데모·그림·테스트."
| 파일 | 역할 |
|---|---|
| engine.py | 핵심 중의 핵심. Value 클래스 하나에 그래프 구축·backward가 다 들어 있다(99줄). |
| nn.py | Value를 레고처럼 쌓아 만든 Neuron/Layer/MLP. parameters()·zero_grad() 제공. |
| plot_nn.py | 이 레포의 차별점. 학습된 망을 색·굵기·편향 주석으로 그린다. |
| test/test.py | "내 autograd가 PyTorch와 같은 답을 내는가"를 확인하는 골든 테스트. |
"backprop을 '소스로' 이해하기에 거의 완벽한 재료. 연산별 미분 공식이 코드에 그대로 있다."
| 연산 | _backward (국소미분 × 상류 grad) | 수학 |
|---|---|---|
| + (덧셈) | self.grad += out.grad | ∂/∂a = 1 |
| * (곱셈) | self.grad += other.data * out.grad | ∂/∂a = b |
| ** (거듭제곱) | self.grad += (n*a**(n-1)) * out.grad | n·aⁿ⁻¹ |
| relu | self.grad += (out.data>0) * out.grad | a>0이면 1, 아니면 0 |
| exp | self.grad += out.data * out.grad | d(eˣ)=eˣ(=out.data) |
| tanh | self.grad += (1 - t**2) * out.grad | 1 − tanh²(x) |
backward()와 각 연산의 _backward 클로저만 읽으면, 역전파의 전부를 이해할 수 있습니다. 특히 왜 위상정렬이 필요한지(한 노드의 기울기는 그걸 쓰는 모든 곳에서 다 모인 뒤에야 자식으로 흘러야 함)를 코드로 체감하는 게 핵심입니다.
한 값이 여러 갈래로 쓰이면(fan-out), 각 갈래에서 온 기울기를 더해야 맞습니다. 그래서 모든 _backward가 =가 아니라 +=를 씁니다. graddy의 테스트는 일부러 z를 두 번 재사용해 이 누적이 맞는지 확인합니다. 미분의 '덧셈 법칙'이 코드 한 글자에 녹아 있는 셈입니다.
plot_nn.py의 그리기 함수는 matplotlib 기본 도형만으로 뉴런(원)과 가중치(선)를 그립니다. 부호는 색, 크기는 굵기·투명도로 인코딩해 "이 망이 무엇을 학습했나"를 눈으로 보게 합니다. 데이터 시각화 입문으로도 훌륭합니다.
# plot_nn.py — 가중치를 색/굵기로 인코딩 (차별점)
color = '#2E7D32' if w > 0 else '#C62828' # 양수 초록 / 음수 빨강
alpha = max(0.1, min(0.85, abs(w) / max_w)) # 투명도 = |가중치|
linewidth = 0.5 + 2.5 * (abs(w) / max_w) # 굵기 = |가중치|
같은 수식을 graddy와 PyTorch로 각각 돌려 forward 값과 grad가 일치하는지 assert로 확인합니다. 직접 만든 수치 코드를 신뢰할 수 있는 기준과 대조하는 '골든 레퍼런스 테스트' 패턴 — 모든 수치 구현에 적용할 좋은 습관입니다.
"코어는 파이썬만 있으면 된다. GPU도, 무거운 의존성도 없다."
| 항목 | 요구사항 |
|---|---|
| 언어 | Python 3.6+ (f-string 정도만 사용). 패키징 파일 없음 → pip 설치가 아니라 클론 후 실행. |
| 코어(engine·nn) | 외부 의존성 0(표준 math·random만). |
| 테스트 | pip install torch (기울기 대조용). |
| 시각화·데모 | pip install numpy matplotlib scikit-learn (graphviz 불필요). |
| 하드웨어 | CPU만으로 충분. 스칼라 연산이라 느리지만 데이터가 수백 점 규모라 문제없다. |
"손으로 grad 계산 검산 → 작은 MLP 학습 → 새 연산 추가 → 시각화 확장 순으로."
README 예제(y = h + q + q*x …)를 실행해 y.data·x.grad를 출력하고, 같은 식을 종이에 미분해 손 계산과 일치하는지 확인합니다. 일치하는 순간 backprop이 '마법'이 아니라 연쇄법칙임이 손에 잡힙니다.
demo.py를 돌려 100스텝 동안 손실이 줄어드는 걸 관찰하고, 학습률을 0.01→0.1→0.5로 바꿔 가며 발산·진동을 직접 만들어 봅니다. 경사하강의 감각을 몸으로 익히는 실습입니다.
engine.py에 sigmoid를 추가하세요 — forward는 1/(1+e⁻ˣ), _backward는 s*(1-s). 그런 뒤 test/test.py처럼 PyTorch와 grad를 대조해 내 미분 규칙이 맞는지 검증합니다. autograd 엔진을 '확장'해 보는 핵심 실습입니다.
plot_nn.py의 그리기에 '활성값 히트맵'이나 '에지 위 가중치 숫자'를 더해 봅니다. 더 나아가, 스칼라 Value를 numpy 배열로 바꾸는 '미니 텐서화'를 시도하면 micrograd→tinygrad로 가는 길이 보입니다.
"미분 기초 → autograd 원리 → 신경망 → 텐서화/프레임워크 순으로 쌓으면 단단하다."
| 주차 | 주제 | 학습 자료 / 키워드 |
|---|---|---|
| 1주차 | 미분·연쇄법칙 복습 | 편미분, chain rule, 경사(gradient)의 의미 |
| 2주차 | 역전파 원리 | graddy engine.py, Karpathy "micrograd" 강의(YouTube) |
| 3주차 | 계산 그래프·define-by-run | 위상정렬, 동적 그래프, PyTorch autograd 개념 |
| 4주차 | 신경망 기초 | 퍼셉트론→MLP, 활성함수(relu/tanh/sigmoid), 손실함수(MSE/hinge) |
| 5주차 | 학습 동역학 | SGD, 학습률, L2 정규화, 과적합/결정경계 |
| 6주차 | 텐서화·프레임워크 | tinygrad 읽기, numpy 벡터화, PyTorch로 같은 망 재현 |
"문서에 나온 용어를 한자리에."
| 용어 | 의미 |
|---|---|
| autograd | 미분(기울기)을 자동 계산하는 기능. 연산마다 국소 미분 규칙만 정의해 두면 기계가 연쇄. |
| 역전파(backprop) | 출력에서 입력으로 거꾸로 연쇄법칙을 적용해 모든 기울기를 한 번에 구하는 알고리즘. |
| 계산 그래프 | 값(노드)이 어떤 연산으로 만들어졌는지의 지도(DAG). graddy는 forward 때 즉석에서 그림. |
| 연산자 오버로딩 | +·* 같은 연산을 내 클래스에 맞게 재정의. 수식 쓰기 = 그래프 짓기. |
| 위상정렬 | 의존 순서대로 줄세우기. backward에서 자식이 부모보다 먼저 오게 한다. |
| 경사하강(SGD) | 기울기 반대 방향으로 가중치를 조금씩 옮겨 손실을 줄이는 학습법. |
| 활성함수 | 뉴런 출력에 비선형성을 더하는 함수(relu·tanh·sigmoid 등). 없으면 층을 쌓아도 선형. |
| MLP | Multi-Layer Perceptron. 여러 층의 뉴런으로 이뤄진 가장 기본적인 신경망. |