Skip to content

Commit ca9c25a

Browse files
committed
...
1 parent e0a79a7 commit ca9c25a

1 file changed

Lines changed: 191 additions & 25 deletions

File tree

tiny_autograd_tutorial.ipynb

Lines changed: 191 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -39,15 +39,15 @@
3939
},
4040
{
4141
"cell_type": "code",
42-
"execution_count": null,
42+
"execution_count": 33,
4343
"metadata": {},
4444
"outputs": [],
4545
"source": [
4646
"from __future__ import annotations\n",
4747
"\n",
4848
"import math\n",
4949
"from dataclasses import dataclass, field\n",
50-
"from typing import Callable, Set\n",
50+
"from typing import Callable\n",
5151
"\n",
5252
"\n",
5353
"@dataclass\n",
@@ -61,7 +61,7 @@
6161
" _backward: 역전파 시 실행할 함수\n",
6262
" \"\"\"\n",
6363
" data: float\n",
64-
" _prev: Set[Value] = field(default_factory=set, repr=False)\n",
64+
" _prev: set[Value] = field(default_factory=set, repr=False)\n",
6565
" _backward: Callable[[], None] = field(default=lambda: None, repr=False)\n",
6666
" grad: float = 0.0\n",
6767
"\n",
@@ -70,8 +70,48 @@
7070
" raise TypeError(\"Value.data must be a number\")\n",
7171
" self.data = float(self.data)\n",
7272
"\n",
73-
" def __repr__(self) -> str:\n",
74-
" return f\"Value(data={self.data:.6f}, grad={self.grad:.6f})\""
73+
" def __repr__(self) -> str:\n",
74+
" return f\"Value(data={self.data:.6f}, grad={self.grad:.6f})\"\n",
75+
" \n",
76+
" def __hash__(self) -> int:\n",
77+
" return id(self)\n",
78+
"\n",
79+
" def __eq__(self, other) -> bool:\n",
80+
" return self is other"
81+
]
82+
},
83+
{
84+
"cell_type": "code",
85+
"execution_count": 30,
86+
"metadata": {},
87+
"outputs": [
88+
{
89+
"name": "stdout",
90+
"output_type": "stream",
91+
"text": [
92+
"[1]\n",
93+
"[1, 2]\n",
94+
"[1]\n",
95+
"[2]\n"
96+
]
97+
}
98+
],
99+
"source": [
100+
"def append_item(x, items=[]): # 가변 기본값\n",
101+
" items.append(x)\n",
102+
" return items\n",
103+
"\n",
104+
"def append_item2(x, items=None):\n",
105+
" if items is None:\n",
106+
" items = []\n",
107+
" items.append(x)\n",
108+
" return items\n",
109+
"\n",
110+
"print(append_item(1)) # [1]\n",
111+
"print(append_item(2)) # [1, 2] ← 이전 호출과 공유됨\n",
112+
"\n",
113+
"print(append_item2(1)) # [1]\n",
114+
"print(append_item2(2)) # [2]"
75115
]
76116
},
77117
{
@@ -88,7 +128,7 @@
88128
},
89129
{
90130
"cell_type": "code",
91-
"execution_count": null,
131+
"execution_count": 34,
92132
"metadata": {},
93133
"outputs": [],
94134
"source": [
@@ -120,6 +160,33 @@
120160
"Value.__radd__ = lambda self, other: self.__add__(other)"
121161
]
122162
},
163+
{
164+
"cell_type": "code",
165+
"execution_count": 36,
166+
"metadata": {},
167+
"outputs": [
168+
{
169+
"name": "stdout",
170+
"output_type": "stream",
171+
"text": [
172+
"Value(data=5.0, grad=0.0)\n",
173+
"Value(data=5.0, grad=0.0)\n"
174+
]
175+
}
176+
],
177+
"source": [
178+
"hasattr(Value, \"__add__\") # True가 나와야 정상\n",
179+
"\n",
180+
"a = Value(3.0)\n",
181+
"\n",
182+
"# 왼쪽이 숫자라서, int.__add__가 Value를 모르면 a.__radd__(2)가 호출됨\n",
183+
"c1 = 2 + a # __radd__ 경유 → __add__ 재사용\n",
184+
"c2 = a + 2 # __add__\n",
185+
"\n",
186+
"print(c1)\n",
187+
"print(c2)"
188+
]
189+
},
123190
{
124191
"cell_type": "markdown",
125192
"metadata": {},
@@ -129,9 +196,20 @@
129196
},
130197
{
131198
"cell_type": "code",
132-
"execution_count": null,
199+
"execution_count": 37,
133200
"metadata": {},
134-
"outputs": [],
201+
"outputs": [
202+
{
203+
"name": "stdout",
204+
"output_type": "stream",
205+
"text": [
206+
"a = Value(data=2.0, grad=0.0)\n",
207+
"b = Value(data=3.0, grad=0.0)\n",
208+
"c = a + b = Value(data=5.0, grad=0.0)\n",
209+
"c의 부모 노드들: {Value(data=2.0, grad=0.0), Value(data=3.0, grad=0.0)}\n"
210+
]
211+
}
212+
],
135213
"source": [
136214
"# 간단한 덧셈 예제\n",
137215
"a = Value(2.0)\n",
@@ -158,7 +236,7 @@
158236
},
159237
{
160238
"cell_type": "code",
161-
"execution_count": null,
239+
"execution_count": 38,
162240
"metadata": {},
163241
"outputs": [],
164242
"source": [
@@ -175,6 +253,7 @@
175253
"\n",
176254
" def _backward() -> None:\n",
177255
" # Product rule 적용\n",
256+
" print(\"backward is called, self.grad = \", self.grad, \"other.grad = \", other.grad, \"out.grad = \", out.grad)\n",
178257
" self.grad += other.data * out.grad # d(a*b)/da = b\n",
179258
" other.grad += self.data * out.grad # d(a*b)/db = a\n",
180259
"\n",
@@ -194,9 +273,20 @@
194273
},
195274
{
196275
"cell_type": "code",
197-
"execution_count": null,
276+
"execution_count": 45,
198277
"metadata": {},
199-
"outputs": [],
278+
"outputs": [
279+
{
280+
"name": "stdout",
281+
"output_type": "stream",
282+
"text": [
283+
"x = Value(data=3.0, grad=0.0)\n",
284+
"y = Value(data=4.0, grad=0.0)\n",
285+
"z = x * y = Value(data=12.0, grad=0.0)\n",
286+
"w = z * 2 = Value(data=24.0, grad=0.0)\n"
287+
]
288+
}
289+
],
200290
"source": [
201291
"# 곱셈 예제\n",
202292
"x = Value(3.0)\n",
@@ -225,7 +315,7 @@
225315
},
226316
{
227317
"cell_type": "code",
228-
"execution_count": null,
318+
"execution_count": 46,
229319
"metadata": {},
230320
"outputs": [],
231321
"source": [
@@ -260,7 +350,7 @@
260350
},
261351
{
262352
"cell_type": "code",
263-
"execution_count": null,
353+
"execution_count": 47,
264354
"metadata": {},
265355
"outputs": [],
266356
"source": [
@@ -295,7 +385,7 @@
295385
},
296386
{
297387
"cell_type": "code",
298-
"execution_count": null,
388+
"execution_count": 48,
299389
"metadata": {},
300390
"outputs": [],
301391
"source": [
@@ -344,7 +434,7 @@
344434
},
345435
{
346436
"cell_type": "code",
347-
"execution_count": null,
437+
"execution_count": 49,
348438
"metadata": {},
349439
"outputs": [],
350440
"source": [
@@ -430,6 +520,12 @@
430520
" self.grad = 1.0\n",
431521
" for v in reversed(topo):\n",
432522
" v._backward()\n",
523+
" \n",
524+
" def __hash__(self) -> int:\n",
525+
" return id(self)\n",
526+
"\n",
527+
" def __eq__(self, other) -> bool:\n",
528+
" return self is other\n",
433529
"\n",
434530
" def __repr__(self) -> str:\n",
435531
" return f\"Value(data={self.data:.6f}, grad={self.grad:.6f})\""
@@ -448,9 +544,28 @@
448544
},
449545
{
450546
"cell_type": "code",
451-
"execution_count": null,
547+
"execution_count": 51,
452548
"metadata": {},
453-
"outputs": [],
549+
"outputs": [
550+
{
551+
"name": "stdout",
552+
"output_type": "stream",
553+
"text": [
554+
"=== Forward Pass ===\n",
555+
"a = 1.300000\n",
556+
"b = -0.700000\n",
557+
"c = a * b = -0.910000\n",
558+
"d = c + a = 0.390000\n",
559+
"e = tanh(b) = -0.604368\n",
560+
"out = d * e = -0.235703\n",
561+
"\n",
562+
"=== Backward Pass ===\n",
563+
"∂out/∂a = -0.181310\n",
564+
"∂out/∂b = -0.538130\n",
565+
"(Value(data=-0.235703, grad=1.000000), -0.18131033313514905, -0.5381296701591537)\n"
566+
]
567+
}
568+
],
454569
"source": [
455570
"# 복잡한 함수 예제\n",
456571
"def test_complex_function():\n",
@@ -481,7 +596,8 @@
481596
" \n",
482597
" return out, a.grad, b.grad\n",
483598
"\n",
484-
"result = test_complex_function()"
599+
"result = test_complex_function()\n",
600+
"print(result)"
485601
]
486602
},
487603
{
@@ -495,9 +611,34 @@
495611
},
496612
{
497613
"cell_type": "code",
498-
"execution_count": null,
614+
"execution_count": 25,
499615
"metadata": {},
500-
"outputs": [],
616+
"outputs": [
617+
{
618+
"name": "stdout",
619+
"output_type": "stream",
620+
"text": [
621+
"\n",
622+
"테스트 케이스: a=1.3, b=-0.7\n",
623+
" 자동미분: ∂f/∂a = -0.18131033, ∂f/∂b = -0.53812967\n",
624+
" 수치미분: ∂f/∂a = -0.18131033, ∂f/∂b = -0.53812967\n",
625+
" 상대오차: a = 9.15e-13, b = 1.26e-11\n",
626+
" 통과: True\n",
627+
"\n",
628+
"테스트 케이스: a=0.5, b=0.5\n",
629+
" 자동미분: ∂f/∂a = 0.69317574, ∂f/∂b = 0.82089438\n",
630+
" 수치미분: ∂f/∂a = 0.69317574, ∂f/∂b = 0.82089438\n",
631+
" 상대오차: a = 4.10e-12, b = 2.06e-11\n",
632+
" 통과: True\n",
633+
"\n",
634+
"테스트 케이스: a=-1.2, b=2.0\n",
635+
" 자동미분: ∂f/∂a = 2.89208274, ∂f/∂b = -1.41117607\n",
636+
" 수치미분: ∂f/∂a = 2.89208274, ∂f/∂b = -1.41117607\n",
637+
" 상대오차: a = 9.31e-11, b = 3.11e-10\n",
638+
" 통과: True\n"
639+
]
640+
}
641+
],
501642
"source": [
502643
"import numpy as np\n",
503644
"\n",
@@ -564,9 +705,34 @@
564705
},
565706
{
566707
"cell_type": "code",
567-
"execution_count": null,
708+
"execution_count": 26,
568709
"metadata": {},
569-
"outputs": [],
710+
"outputs": [
711+
{
712+
"name": "stdout",
713+
"output_type": "stream",
714+
"text": [
715+
"\n",
716+
"=== 연산 그래프 구조 ===\n",
717+
"노드 개수: 5\n",
718+
"엣지 개수: 5\n",
719+
"\n",
720+
"노드 정보:\n",
721+
" Node 0: data=1.0000, grad=1.0000\n",
722+
" Node 1: data=3.0000, grad=0.0000\n",
723+
" Node 2: data=2.0000, grad=0.0000\n",
724+
" Node 3: data=6.0000, grad=0.0000\n",
725+
" Node 4: data=8.0000, grad=0.0000\n",
726+
"\n",
727+
"연결 정보:\n",
728+
" 2.0000 -> 6.0000\n",
729+
" 6.0000 -> 8.0000\n",
730+
" 2.0000 -> 8.0000\n",
731+
" 3.0000 -> 6.0000\n",
732+
" 8.0000 -> 1.0000\n"
733+
]
734+
}
735+
],
570736
"source": [
571737
"def trace_graph(root):\n",
572738
" \"\"\"연산 그래프 추적\"\"\"\n",
@@ -728,7 +894,7 @@
728894
],
729895
"metadata": {
730896
"kernelspec": {
731-
"display_name": "Python 3",
897+
"display_name": "ai",
732898
"language": "python",
733899
"name": "python3"
734900
},
@@ -742,9 +908,9 @@
742908
"name": "python",
743909
"nbconvert_exporter": "python",
744910
"pygments_lexer": "ipython3",
745-
"version": "3.11.0"
911+
"version": "3.11.7"
746912
}
747913
},
748914
"nbformat": 4,
749915
"nbformat_minor": 4
750-
}
916+
}

0 commit comments

Comments
 (0)