Calculus, Gradients and Convex Optimization
Derivatives, autodiff, GD, momentum, Adam, Newton and convexity tests. From-scratch Python and PyTorch.
- Concepts and terms
- Tasks and commands
- Annotated code
- Diagrams
- Defaults and numbers
- Troubleshooting
- Questions and tips
Forward and backward through one neuron#
From Lesson 01.04
Reverse-mode autodiff in Value.backward#
Tasks in code#
| Task | Code |
|---|---|
| Central difference | numerical_derivative(f, |
| Gradient of many inputs | numerical_gradient(f, |
| Hessian of f(x, y) | hessian_2d(f, |
| Check an autodiff gradient | gradient_check(build_expr, |
| XOR network, 4 hidden | model = |
| Run an optimizer | optimize(optimizer, |
| Test convexity by sampling | check_convexity(f, |
| Newton steps in 2D | newtons_method(grad_f, |
From 01.04 code/derivatives.py, 01.05 code/autodiff.py, Lesson 01.05, 01.08 code/optimizers.py, 01.18 code/convex.py
Optimizers from scratch#
GradientDescent(lr=0.001)w = w - lr * gradientSGDMomentum(lr=0.001, momentum=0.9)v = beta * v + gradientAdam(lr=0.001, beta1=0.9, beta2=0.999, epsilon=1e-8)two moments per weightm_hat = [m / (1 - self.beta1 ** self.t) for m in self.m]bias correctionp - self.lr * mh / (vh ** 0.5 + self.epsilon)per-weight step sizeadam_history = optimize(Adam(lr=0.01), rosenbrock, rosenbrock_gradient, start)Autograd and optimizers in PyTorch#
x = torch.tensor(2.0, requires_grad=True)y = x ** 2 + 3 * x + 1y.backward()reverse mode through the recorded graphprint(x.grad)7.0 = 2*x + 3sgd = torch.optim.SGD(model.parameters(), lr=0.01, momentum=0.9)adam = torch.optim.Adam(model.parameters(), lr=0.001)adamw = torch.optim.AdamW(model.parameters(), lr=0.001, weight_decay=0.01)scheduler = torch.optim.lr_scheduler.CosineAnnealingLR(adam, T_max=100)From Lesson 01.05, Lesson 01.08
Derivatives to know#
| Command | What it does |
|---|---|
| x^2 | 2x. MSE loss |
| wx + b | x for w, 1 for b, w for x. Linear layer |
| e^x | e^x. Softmax, attention |
| ln(x) | 1/x. Cross-entropy loss |
| sigmoid | f(x)(1-f(x)). Sigmoid activation |
| tanh | 1 - tanh^2. Classic activation |
| x^n | n x^(n-1). Used by __pow__ |
| sin(x^2) | cos(x^2) * 2x. Chain rule example |
From Lesson 01.04, Lesson 01.05
Key formulas#
- Central difference
- f'(x) \approx \frac{f(x + h) - f(x - h)}{2h}.
- Chain rule
- \frac{dy}{dx} = f'(g(x)) \cdot g'(x). Sum the paths where the graph branches.
- Gradient descent
- w = w - \text{lr} \cdot \text{gradient}.
- Momentum
- v = \beta \cdot v + \text{gradient}, then w = w - \text{lr} \cdot v.
- Adam
- w = w - \text{lr} \cdot \hat{m} / (\sqrt{\hat{v}} + \epsilon) with bias-corrected moments.
- Newton
- x_{\text{new}} = x - H^{-1} \cdot \text{gradient}.
- Convex
- f(tx + (1-t)y) \le t f(x) + (1-t) f(y) for t \in [0, 1].
- Lagrangian
- L(x, \lambda) = f(x) + \lambda g(x). Set its gradient to zero.
Pick a method#
| Command | What it does |
|---|---|
| Gradient descent | First derivatives. O(n) memory. Baseline |
| SGD with momentum | Best final accuracy with more tuning |
| Adam | Diagonal curvature from gradient moments. Deep learning default |
| AdamW | Adam with decoupled weight decay. Transformers |
| Newton's method | Full Hessian. O(n^3) per step. Small convex problems |
| L-BFGS | Last m gradient differences. Medium convex problems |
| K-FAC | Kronecker-factored Fisher. Large-batch training |
From Lesson 01.04, Lesson 01.08, Lesson 01.18
Numbers and defaults#
| Setting | Default | Note |
|---|---|---|
Adam lr | 0.001 | Start here for most problems |
Adam beta2 | 0.999 | beta1 is 0.9. Moment decay rates |
Adam epsilon | 1e-8 | Stops division by zero |
SGD momentum lr | 0.01 | With momentum 0.9 |
AdamW weight_decay | 0.01 | Decoupled from the gradient step |
| Mini-batch size | 32-256 | Good gradient estimate, moderate noise |
Finite difference h | 1e-7 | In numerical_derivative |
| Gradient check difference | < 1e-5 | Autodiff against numerical |
From 01.04 code/derivatives.py, Lesson 01.05, Lesson 01.08, 01.08 code/optimizers.py
Troubleshooting#
| Symptom | Cause | Fix |
|---|---|---|
| Loss grows or becomes nan | Learning rate too large | Reduce the learning rate |
| Training is too slow | Learning rate too small | Increase it. Add a schedule |
| Zigzag in a narrow valley | High condition number of the Hessian | Add momentum, or use Newton |
| Gradient near 0, no progress | Saddle point | Momentum and mini-batch noise |
| Gradients grow each step | .grad adds with += | Set p.grad = 0.0 before backward |
| Autodiff disagrees with numeric | Bug in a _backward rule | Run gradient_check |
| Noisy second derivative | Step h too small | Use h=1e-4 for both calls |
| Newton too costly for a big model | O(n^2) memory, O(n^3) inverse | Use L-BFGS or Adam |
Questions#
- Why reverse mode for networks?
- Many inputs and one loss. One backward pass gives all gradients.
- How do I test convexity?
- f'' >= 0 in 1D. Hessian positive semidefinite in many dimensions.
- Why does SGD work on non-convex losses?
- Most critical points are saddle points. Most minima are good.
- Why does L1 give sparse weights?
- The diamond has corners on the axes.
- What is complementary slackness?
- \lambda_i \, g_i(x) = 0. Support vectors have \lambda > 0.
From Lesson 01.05, Lesson 01.18
Tips#
- Use a learning rate schedule for runs longer than a few epochs.
- Cosine annealing and warmup suit transformers and large models.
- Newton's method solves a quadratic in one step.
- Gradient checks cost 2 forward passes per parameter. Do not run them in production training.
- A Hessian with mixed eigenvalue signs at a critical point marks a saddle.
- Flat minima generalize better than sharp minima.