ML Mathematical Theory
Deep learning fundamentals — regularization, optimization, normalization, attention math, and activation functions
Core Architecture & Signal Flow
Activation Functions · Transformer Attention Math · Weight Initialization
Activation Functions
Activation functions introduce nonlinearity into the network. Without them, stacking linear layers would collapse to a single linear transformation — no matter how deep the network, it could only learn linear relationships.
| Activation | Formula | Output Range | Zero-Centered? | Compute Cost | Vanishing Gradient | Sparsity | Primary Use |
|---|---|---|---|---|---|---|---|
| Sigmoid | 1 / (1 + e⁻ˣ) | (0, 1) | No | High (exp) | High risk | No | Binary classification output |
| Tanh | (eˣ − e⁻ˣ) / (eˣ + e⁻ˣ) | (−1, 1) | Yes | High (exp) | Moderate | No | RNN hidden layers |
| ReLU | max(0, x) | [0, ∞) | No | Low (threshold) | Low (x>0) | Yes (zeros negatives) | Default hidden layers |
Weight Initialization Strategies
Why not initialize all weights to 0?
Sample weights from a distribution with:
nin = fan-in, nout = fan-out. Keeps var(output) = var(input) across layers. Works because sigmoid/tanh are nearly linear near 0.
Sample weights from a distribution with:
nin = fan-in. The factor of 2 compensates for ReLU zeroing ~50% of activations, which would otherwise halve signal variance every layer.
Transformer Attention Math
Learning Dynamics & Convergence
Loss Functions · Optimization + Momentum · Vanishing + Exploding Gradients
Loss Functions
Vanishing & Exploding Gradients
Why do deep networks suffer — and how do residual connections fix it?
- Gradient becomes f′(x) + 1 — the +1 provides a constant highway for gradients.
- Faster convergence: direct path for both gradients and information.
- Smoother landscape: enables training of very deep networks.
Optimization & Momentum
Optimization algorithms update model weights to minimize the loss. Momentum accumulates a velocity vector — incorporating a weighted history of past gradients to move faster through flat regions and resist oscillation in narrow valleys.
- 1st moment m: Exponential moving average of gradients — direction and velocity.
- 2nd moment v: Moving average of squared gradients — intensity. Large gradient → shrink LR. Small gradient → accelerate LR.
- Adaptive per-parameter LR. Faster convergence, but can generalize worse than SGD.
| Optimizer | Speed | LR Tuning | Memory | Generalization |
|---|---|---|---|---|
| SGD | Slow | Hard | None | Excellent |
| SGD + Momentum | Faster | Medium | Velocity | Good |
| Adam | Fastest | Easy | Velocity + Intensity | Can overfit |
Regularization & Generalization
Bias-Variance Trade-off · L1 Lasso vs L2 Ridge · Dropout
Regularization (L1 & L2)
What is regularization?
Adds a penalty term to the loss to discourage overly large weights:
λ controls regularization strength; Ω(w) is the weight penalty.
Why does it prevent overfitting?
- Sensitivity: Smaller weights → less sensitive slopes → noise causes smaller output changes.
- Geometrically: Restricts search space — forces the model to capture only the strongest signals.
- Hessian: Adds a bowl to the loss landscape, shifting minima toward smoother, more generalizable regions.
L1 vs L2
Ω(w) = Σ|wᵢ|
Constant gradient (±1) near zero — pushes weights all the way to exactly 0. Produces sparse models.
Ω(w) = Σwᵢ²
Gradient scales with weight (2w) — damps near zero but rarely reaches exactly 0. Produces small, smooth weights.

L1's diamond corners lie on the axes — w* lands where a weight = 0. L2's circle intersects the contour off-axis, shrinking but not zeroing.
Bias-Variance Tradeoff
Bias is error from overly simplistic assumptions. Variance is error from excessive sensitivity to training data. Increasing model complexity tends to decrease bias but increase variance. The goal is the sweet spot that minimizes total error on unseen data.

Low bias = predictions near center. Low variance = predictions tightly clustered. Ideal: top-left (low bias, low variance).
Dropout
Prevents co-adaptation of neurons and reduces overfitting. Srivastava et al., 2014
Dropout is a regularization technique that randomly zeros out individual neurons during each training step using a Bernoulli mask. This prevents neurons from co-adapting — relying too heavily on specific neighbors — and forces the network to learn more distributed, redundant representations that generalize better to unseen data. At inference, dropout is turned off and activations are rescaled to match expected training values.
class MyDropout(nn.Module):
def __init__(self, p=0.5):
super().__init__()
self.p = p
def forward(self, x):
if not self.training or self.p == 0:
return x
# Bernoulli mask: 1 = keep, 0 = drop
mask = (torch.rand_like(x) > self.p).float()
# Inverted scaling keeps expected value = x
return x * mask / (1 - self.p)- DropPath: Drops entire residual branches rather than individual neurons — acts as a stronger structural regularizer for deep networks.
- Attention Dropout: Applied directly to the softmax attention weight matrix, preventing the model from always attending to the same set of tokens.
- Variational Dropout: Reuses the same mask across all time steps in a sequence, preserving positional structure while still regularizing.
Normalization & Stability
Batch Normalization — mean/variance shift, γ and β affine transforms
Batch Normalization
Normalizes activations per mini-batch; stabilizes and accelerates training. Ioffe & Szegedy, 2015
Batch Normalization standardizes each layer's activations to have zero mean and unit variance across the mini-batch, then applies learned affine parameters γ (scale) and β (shift) to restore expressiveness. This decouples each layer from the scale of its inputs, allowing higher learning rates, faster convergence, and reduced sensitivity to weight initialization.
μB and σ²B are the batch mean and variance. ε is a small constant for numerical stability. γ and β are learned parameters that let the model undo the normalization if needed.
Smaller momentum = slower updates (more stable); larger = faster but noisier.
Batch Normalization — Code
def batch_norm_forward(x, gamma, beta, eps=1e-5, momentum=0.9,
running_mean=None, running_var=None, training=True):
N, D = x.shape
if running_mean is None: running_mean = np.zeros(D)
if running_var is None: running_var = np.ones(D)
if training:
batch_mean = np.mean(x, axis=0)
batch_var = np.var(x, axis=0)
x_hat = (x - batch_mean) / np.sqrt(batch_var + eps)
out = gamma * x_hat + beta
# Exponential moving average
running_mean = momentum * running_mean + (1 - momentum) * batch_mean
running_var = momentum * running_var + (1 - momentum) * batch_var
cache = (x, x_hat, batch_mean, batch_var, gamma, eps)
return out, cache, running_mean, running_var
else:
x_hat = (x - running_mean) / np.sqrt(running_var + eps)
return gamma * x_hat + beta