Math for Machine Learning
Gradients, probability, information, optimizers, decompositions and float safety. NumPy, SciPy and PyTorch.
- Concepts and terms
- Tasks and commands
- Annotated code
- Diagrams
- Defaults and numbers
- Troubleshooting
- Questions and tips
Computational graph, forward and backward#
From Lesson 01.05
Key formulas#
- Chain rule
- \frac{dy}{dx} = f'(g(x)) \cdot g'(x)
- Bayes
- P(A \mid B) = \frac{P(B \mid A) \cdot P(A)}{P(B)}
- Entropy
- H(P) = -\sum_{x} p(x) \log(p(x))
- Cross-entropy
- H(P, Q) = -\sum_{x} p(x) \log(q(x))
- KL divergence
- D_{KL}(P \,\|\, Q) = H(P, Q) - H(P). Not symmetric.
- Log-sum-exp
- \log \sum_i e^{x_i} = \max(x) + \log \sum_i e^{x_i - \max(x)}
- Cosine similarity
- \frac{a \cdot b}{\lVert a \rVert_2 \, \lVert b \rVert_2}, from -1 to +1
- SVD
- A = U \Sigma V^T. Rank k: A_k = U_k \Sigma_k V_k^T
From Lesson 01.05, Lesson 01.07, Lesson 01.09, Lesson 01.13, Lesson 01.14, Lesson 01.11
Update rules#
- Gradient descent
- w = w - \text{lr} \cdot \text{gradient}
- Momentum
- v = \beta v + \text{gradient}, then w = w - \text{lr} \cdot v
- Adam moments
- m = \beta_1 m + (1 - \beta_1) g, v = \beta_2 v + (1 - \beta_2) g^2
- Bias correction
- \hat{m} = m / (1 - \beta_1^t), \hat{v} = v / (1 - \beta_2^t)
- Adam step
- w = w - \text{lr} \cdot \hat{m} / (\sqrt{\hat{v}} + \epsilon)
- Cosine schedule
- \text{lr}_{\min} + 0.5 (\text{lr}_{\max} - \text{lr}_{\min})(1 + \cos(\pi t / T))
- Newton step
- w_{\text{new}} = w_{\text{old}} - H^{-1} \cdot \text{gradient}
- Temperature
- p_i = e^{z_i / T} / \sum_j e^{z_j / T}
From Lesson 01.08, Lesson 01.04, Lesson 01.16
Tasks in NumPy#
| Task | Code |
|---|---|
| Dense layer | np.maximum(0, |
| Solve a square system | np.linalg.solve(A, |
| Least squares | np.linalg.lstsq(X, |
| Thin SVD | U, |
| Rank k approximation | U[:, |
| Pseudoinverse solve | np.linalg.pinv(A) @ b |
| Row norms | np.linalg.norm(X, |
| Batched matmul | np.einsum("bij, |
From Lesson 01.02, Lesson 01.11, Lesson 01.17, Lesson 01.14, Lesson 01.12, Lesson 01.01
Tasks in PyTorch and SciPy#
| Task | Code |
|---|---|
| AdamW for transformers | torch.optim.AdamW(model.parameters(), |
| Cosine schedule | torch.optim.lr_scheduler.CosineAnnealingLR(adam, |
| Clip by norm | torch.nn.utils.clip_grad_norm_() |
| Check a backward pass | torch.autograd.gradcheck() |
| Stable log of softmax | torch.nn.functional.log_softmax() |
| Inverse CDF of N(0, 1) | stats.norm(loc= |
| PCA to 2D | SklearnPCA(n_components= |
| t-SNE to 2D | TSNE(n_components= |
Autograd in a few lines#
x = torch.tensor(2.0, requires_grad=True)record operations on xy = x ** 2 + 3 * x + 1builds the graphy.backward()reverse mode passprint(x.grad)7.0 = 2*x + 3def __mul__(self, other): out = Value(self.data * other.data, (self, other), '*') def _backward(): self.grad += other.data * out.gradlocal grad times upstream other.grad += self.data * out.grad+= sums over all uses out._backward = _backward return outself.grad = 1.0seed dy/dy = 1for v in reversed(topo):output to inputs v._backward()chain rule at each nodeFrom Lesson 01.05
Stable softmax and cross-entropy#
def softmax_stable(logits): max_logit = max(logits)subtract the max first exps = [math.exp(z - max_logit) for z in logits]largest term is 1 total = sum(exps) return [e / total for e in exps]def cross_entropy_stable(true_class, logits): max_logit = max(logits) shifted = [z - max_logit for z in logits] log_sum_exp = math.log(sum(math.exp(s) for s in shifted)) log_prob = shifted[true_class] - log_sum_expstay in log space return -log_probFrom Lesson 01.13
PCA with SVD#
From Lesson 01.10, Lesson 01.11
Defaults and numbers#
| Setting | Default | Note |
|---|---|---|
| Adam lr | 0.001 | start here for most problems |
| Adam beta2 | 0.999 | beta1 is 0.9 |
| Adam epsilon | 1e-8 | avoids division by zero |
| SGD momentum lr | 0.01 | with momentum 0.9 |
| Gradient check h | 1e-5 | 1e-5 to 1e-7 is typical |
| Transformer max_norm | 1.0 | 0.5 for RL, 5.0 for simpler nets |
| Dynamic loss scale start | 65536 | halve on inf, double after N clean steps |
| Top-p | 0.9 | with temperature 0.7 for general use |
From Lesson 01.08, Lesson 01.13, Lesson 01.16
Troubleshooting#
| Symptom | Cause | Fix |
|---|---|---|
| Loss is NaN after a few epochs | softmax overflow or lr too high | stable softmax, lower lr, clip gradients |
| Loss stuck at log(num_classes) | near uniform outputs | check labels, loss function, dead ReLUs |
| Accuracy 1 to 3% low with float16 | gradient underflow | dynamic loss scaling or bfloat16 |
| Gradient norm 0.0 in some layers | dead ReLU or float16 underflow | LeakyReLU or GELU, gradient scaling |
| Results differ across GPUs | float addition is not associative | torch.use_deterministic_algorithms(True) |
0.1 + 0.2 == 0.3 is False | 0.1 is not exact in binary | math.isclose() or a tolerance |
| Solution changes a lot for small input changes | large condition number | add \lambda I or drop collinear features |
| One unseen word zeroes Naive Bayes | MLE gives probability zero | Laplace smoothing, add 1 to counts |
Questions#
- Why reverse mode?
- Many inputs and one loss. One backward pass gives all gradients.
- Why cross-entropy?
- Minimizing it maximizes the likelihood of the data.
- Why bfloat16 for training?
- It has the float32 range, so values above 65,504 do not overflow.
- Why SVD and not the eigenvectors of A.T @ A?
- That product squares the condition number.
- Why the reparameterization trick?
- z = \mu + \sigma \epsilon lets gradients flow through \mu and \sigma.
From Lesson 01.05, Lesson 01.09, Lesson 01.13, Lesson 01.11, Lesson 01.16
Tips#
- Check shapes:
(m x n) @ (n x p) = (m x p). - Sum log probabilities. Do not multiply many small probabilities.
- Never compare floats with
==. - Check gradients when you write a new layer or loss.
- Perplexity is the exponential of cross-entropy in nats.
- In a gradient check, a relative error below 1e-5 is acceptable.
From Lesson 01.02, Lesson 01.13, Lesson 01.07, Lesson 01.09, Lesson 01.08