Training & Optimization

Layer 3 · Code

Training & Optimization

Implementing SGD and Adam from scratch, then writing a full PyTorch training loop.

12 min read110 XP

python
import numpy as np

class SGDMomentum:
    def __init__(self, params, lr=0.01, momentum=0.9):
        self.lr = lr
        self.momentum = momentum
        self.velocity = [np.zeros_like(p) for p in params]

    def step(self, params, grads):
        for i, (p, g) in enumerate(zip(params, grads)):
            self.velocity[i] = self.momentum * self.velocity[i] - self.lr * g
            p += self.velocity[i]        # in-place update
        return params

# toy quadratic loss: L(w) = (w - 3)^2, gradient = 2(w - 3)
w = np.array([0.0])
opt = SGDMomentum([w], lr=0.1, momentum=0.9)
for step in range(20):
    grad = 2 * (w - 3)
    opt.step([w], [grad])
print(w)  # converges toward 3.0
SGD with momentum, implemented manually on raw NumPy arrays.
python
import numpy as np

class Adam:
    def __init__(self, params, lr=0.001, beta1=0.9, beta2=0.999, eps=1e-8):
        self.lr, self.b1, self.b2, self.eps = lr, beta1, beta2, eps
        self.m = [np.zeros_like(p) for p in params]
        self.v = [np.zeros_like(p) for p in params]
        self.t = 0

    def step(self, params, grads):
        self.t += 1
        for i, (p, g) in enumerate(zip(params, grads)):
            self.m[i] = self.b1 * self.m[i] + (1 - self.b1) * g
            self.v[i] = self.b2 * self.v[i] + (1 - self.b2) * (g ** 2)
            m_hat = self.m[i] / (1 - self.b1 ** self.t)   # bias correction
            v_hat = self.v[i] / (1 - self.b2 ** self.t)
            p -= self.lr * m_hat / (np.sqrt(v_hat) + self.eps)
        return params
Adam, implemented from its update rule.