Original source
---
id: calculus-optimization
title: Calculus, Gradients and Convex Optimization
code: CG
subtitle: Derivatives, autodiff, GD, momentum, Adam, Newton and convexity tests. From-scratch Python and PyTorch.
hue: 30
sources: phases/01-math-foundations/04-calculus-for-ml, phases/01-math-foundations/05-chain-rule-and-autodiff, phases/01-math-foundations/08-optimization, phases/01-math-foundations/18-convex-optimization
source_hash: e8999348285411d1
---
## Forward and backward through one neuron
type: architecture
from: phases/01-math-foundations/04-calculus-for-ml/docs/en.md
```arch
node | x | 1,1 | x | input
node | z1 | 2,1 | z1 = w*x | multiply
node | z2 | 3,1 | z2 = w*x + b | add bias
node | a | 4,1 | a = sigmoid(z2) | activation
node | dw | 1,2 | dL/dw | dL/dz2 * x
node | db | 2,2 | dL/db | dL/dz2 * 1
node | dz2 | 3,2 | dL/dz2 | dL/da * a(1-a)
node | loss | 4,2 | L | cross-entropy loss
edge | x | z1 | *w
edge | z1 | z2 | +b
edge | z2 | a |
edge | a | loss |
edge | loss | dz2 | backward
edge | dz2 | dw | x
edge | dz2 | db | 1
```
## Reverse-mode autodiff in Value.backward
type: flow
from: phases/01-math-foundations/05-chain-rule-and-autodiff/docs/en.md, phases/01-math-foundations/05-chain-rule-and-autodiff/code/autodiff.py
1. **Record**: each operation makes a new `Value` with its children and a `_backward` closure.
2. **Sort**: `build_topo` puts every node after the nodes it depends on.
3. **Seed**: set `self.grad = 1.0` on the output. This is dy/dy = 1.
4. **Walk back**: call `v._backward()` for each node in reversed topological order.
5. **Accumulate**: each closure adds to `.grad` with `+=`. A value used twice gets the sum.
6. **Reset**: set `p.grad = 0.0` for every parameter before the next `loss.backward()`.
7. **Update**: `p.data -= lr * p.grad` for each parameter.
## Tasks in code
type: task-table
from: phases/01-math-foundations/04-calculus-for-ml/code/derivatives.py, phases/01-math-foundations/05-chain-rule-and-autodiff/code/autodiff.py, phases/01-math-foundations/05-chain-rule-and-autodiff/docs/en.md, phases/01-math-foundations/08-optimization/code/optimizers.py, phases/01-math-foundations/18-convex-optimization/code/convex.py
| Task | Code |
|---|---|
| Central difference | `numerical_derivative(f, x, h=1e-7)` |
| Gradient of many inputs | `numerical_gradient(f, point, h=1e-7)` |
| Hessian of f(x, y) | `hessian_2d(f, x, y, h=1e-5)` |
| Check an autodiff gradient | `gradient_check(build_expr, x_val, h=1e-7)` |
| XOR network, 4 hidden | `model = MLP([2, 4, 1])` |
| Run an optimizer | `optimize(optimizer, func, grad_func, start)` |
| Test convexity by sampling | `check_convexity(f, dim)` |
| Newton steps in 2D | `newtons_method(grad_f, hessian_f, x0)` |
## Optimizers from scratch
type: annotated-code
lang: python
from: phases/01-math-foundations/08-optimization/docs/en.md, phases/01-math-foundations/08-optimization/code/optimizers.py
```python
GradientDescent(lr=0.001) # w = w - lr * gradient
SGDMomentum(lr=0.001, momentum=0.9) # v = beta * v + gradient
Adam(lr=0.001, beta1=0.9, beta2=0.999, epsilon=1e-8) # two moments per weight
m_hat = [m / (1 - self.beta1 ** self.t) for m in self.m] # bias correction
p - self.lr * mh / (vh ** 0.5 + self.epsilon) # per-weight step size
adam_history = optimize(Adam(lr=0.01), rosenbrock, rosenbrock_gradient, start)
```
## Autograd and optimizers in PyTorch
type: annotated-code
lang: python
from: phases/01-math-foundations/05-chain-rule-and-autodiff/docs/en.md, phases/01-math-foundations/08-optimization/docs/en.md
```python
x = torch.tensor(2.0, requires_grad=True)
y = x ** 2 + 3 * x + 1
y.backward() # reverse mode through the recorded graph
print(x.grad) # 7.0 = 2*x + 3
sgd = 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)
```
## Key formulas
type: concepts
from: phases/01-math-foundations/04-calculus-for-ml/docs/en.md, phases/01-math-foundations/05-chain-rule-and-autodiff/docs/en.md, phases/01-math-foundations/08-optimization/docs/en.md, phases/01-math-foundations/18-convex-optimization/docs/en.md
- **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.
## Derivatives to know
type: command-table
from: phases/01-math-foundations/04-calculus-for-ml/docs/en.md, phases/01-math-foundations/05-chain-rule-and-autodiff/docs/en.md
| 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 |
## Pick a method
type: command-table
from: phases/01-math-foundations/04-calculus-for-ml/docs/en.md, phases/01-math-foundations/08-optimization/docs/en.md, phases/01-math-foundations/18-convex-optimization/docs/en.md
| 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 |
## Numbers and defaults
type: defaults
from: phases/01-math-foundations/04-calculus-for-ml/code/derivatives.py, phases/01-math-foundations/05-chain-rule-and-autodiff/docs/en.md, phases/01-math-foundations/08-optimization/docs/en.md, phases/01-math-foundations/08-optimization/code/optimizers.py
| 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 |
## Troubleshooting
type: troubleshooting
from: phases/01-math-foundations/04-calculus-for-ml/docs/en.md, phases/01-math-foundations/05-chain-rule-and-autodiff/docs/en.md, phases/01-math-foundations/08-optimization/docs/en.md, phases/01-math-foundations/18-convex-optimization/docs/en.md
| 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
type: qa
from: phases/01-math-foundations/05-chain-rule-and-autodiff/docs/en.md, phases/01-math-foundations/18-convex-optimization/docs/en.md
- **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$.
## Tips
type: tips
from: phases/01-math-foundations/04-calculus-for-ml/docs/en.md, phases/01-math-foundations/05-chain-rule-and-autodiff/docs/en.md, phases/01-math-foundations/08-optimization/docs/en.md, phases/01-math-foundations/18-convex-optimization/docs/en.md
- 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.