Skip to content

程式碼逐行解讀 — 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 包含 selfother
  • 反向傳播:加法將上游梯度直接傳給兩個輸入
  • 使用 += 而非 =:一個節點可能被多個節點使用

乘法 __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/dcc = 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 out
  • x < 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()
  1. 拓撲排序:DFS 後序遍歷,確保子節點在父節點之前被加入 list
  2. 初始化根梯度dy/dy = 1
  3. 逆序反向傳播:從輸出端往輸入端依序呼叫 _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