Appearance
程式碼逐行解讀 — micrograd
engine.py — 自動微分引擎
Value 類別建構子
python
class Value:
def __init__(self, data, _children=(), _op=''):
self.data = data # 儲存的值
self.grad = 0 # 梯度,初始為 0
self._backward = lambda: None # 反向傳播函式
self._prev = set(_children) # 計算圖中的子節點
self._op = _op # 產生此節點的運算(用於視覺化)_children是 tuple,存入set中去除重複_backward預設為空操作(no-op),葉節點不需要反向傳播_op僅供除錯與視覺化用
加法 __add__
python
def __add__(self, other):
other = other if isinstance(other, Value) else Value(other)
out = Value(self.data + other.data, (self, other), '+')
def _backward():
self.grad += out.grad
other.grad += out.grad
out._backward = _backward
return out- 先將
other轉為 Value(支援Value + int) out的_prev包含self和other- 反向傳播:加法將上游梯度直接傳給兩個輸入
- 使用
+=而非=:一個節點可能被多個節點使用
乘法 __mul__
python
def __mul__(self, other):
other = other if isinstance(other, Value) else Value(other)
out = Value(self.data * other.data, (self, other), '*')
def _backward():
self.grad += other.data * out.grad # dL/da = b * dL/dout
other.grad += self.data * out.grad # dL/db = a * dL/dout
out._backward = _backward
return out- 鏈鎖律:
dL/da = b * dL/dc(c = a * b)
冪運算 __pow__
python
def __pow__(self, other):
assert isinstance(other, (int, float)), "only supporting int/float powers"
out = Value(self.data**other, (self,), f'**{other}')
def _backward():
self.grad += (other * self.data**(other-1)) * out.grad
out._backward = _backward
return out- 僅支援常數指數
dL/da = n * a^(n-1) * dL/dc
ReLU
python
def relu(self):
out = Value(0 if self.data < 0 else self.data, (self,), 'ReLU')
def _backward():
self.grad += (out.data > 0) * out.grad
out._backward = _backward
return outx < 0時輸出 0,梯度為 0(神經元死亡)x > 0時輸出 x,梯度等於上游梯度
backward — 拓撲排序與反向傳播
python
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
for v in reversed(topo):
v._backward()- 拓撲排序:DFS 後序遍歷,確保子節點在父節點之前被加入 list
- 初始化根梯度:
dy/dy = 1 - 逆序反向傳播:從輸出端往輸入端依序呼叫
_backward
輔助運算
python
def __neg__(self): return self * -1
def __radd__(self, other): return self + other
def __sub__(self, other): return self + (-other)
def __rsub__(self, other): return other + (-self)
def __rmul__(self, other): return self * other
def __truediv__(self, other): return self * other**-1
def __rtruediv__(self, other): return other * self**-1- 所有運算都基於已實作的
__add__,__mul__,__pow__ - 除法透過
x / y = x * y^(-1)實現
nn.py — 神經網路庫
Module 基類
python
class Module:
def zero_grad(self):
for p in self.parameters():
p.grad = 0
def parameters(self):
return []zero_grad():清除所有參數的梯度(每次 backward 前呼叫)parameters():回傳可訓練參數列表(子類覆寫)
Neuron
python
class Neuron(Module):
def __init__(self, nin, nonlin=True):
self.w = [Value(random.uniform(-1,1)) for _ in range(nin)]
self.b = Value(0)
self.nonlin = nonlin
def __call__(self, x):
act = sum((wi*xi for wi,xi in zip(self.w, x)), self.b)
return act.relu() if self.nonlin else act- 權重隨機初始化
[-1, 1],偏置初始化為0 - 前向傳播:
w·x + b,可選 ReLU
Layer
python
class Layer(Module):
def __init__(self, nin, nout, **kwargs):
self.neurons = [Neuron(nin, **kwargs) for _ in range(nout)]
def __call__(self, x):
out = [n(x) for n in self.neurons]
return out[0] if len(out) == 1 else out- 如果只有一個 neuron,直接回傳 scalar(方便最後一層)
MLP
python
class MLP(Module):
def __init__(self, nin, nouts):
sz = [nin] + nouts
self.layers = [Layer(sz[i], sz[i+1], nonlin=i!=len(nouts)-1) for i in range(len(nouts))]- 最後一層不使用 ReLU(
nonlin=False) - 範例:
MLP(2, [16, 16, 1])兩層隱藏層,輸出層單一 neuron