Filter concepts by levelShowing all levels.

AI Full-Stack · Section 2

Mathematics for AI — Practical Level

Level
beginner
Read
50 min
Concepts
6

The practical math a full-stack AI engineer actually reaches for: linear algebra for reading tensor shapes, probability for reasoning about model confidence and Bayes-style inversions, statistics for reporting a metric honestly instead of as a single misleading number, calculus for understanding what a gradient is and why the chain rule makes backpropagation possible, and optimization — gradient descent, SGD, momentum, Adam — as the mechanism every training loop runs on. None of it is here to be derived by hand; all of it is here to diagnose a misbehaving model.

What is true here

  1. Matrix multiplication is a batch of dot products — the mechanical core of every neural network layer
  2. Bayes' theorem inverts P(evidence|hypothesis) into P(hypothesis|evidence), the thing you actually want
  3. Percentiles and confidence intervals report an estimate honestly; the mean alone hides tails and uncertainty
  4. The gradient points toward steepest increase; training steps opposite it, chained through layers by the chain rule
  5. Learning rate, momentum and Adam are the knobs that make gradient descent fast and stable rather than slow or divergent

What you will be able to do

  • Read a tensor shape and know which linear-algebra operation produced it
  • Apply Bayes' theorem to invert a conditional probability
  • Report a metric with the right summary statistic — mean, median, percentile, or a confidence interval
  • Explain what a gradient is and why the chain rule makes backpropagation work
  • Diagnose a stalled or diverging training run from its loss curve using learning rate, momentum and optimizer choice

Practical math for ML

Five topics used as diagnostics throughout the rest of this course, not as a standalone math curriculum.

Linear algebra for ML

standardbeginner

A vector is a list of numbers — one training example, or one set of model weights. A matrix is a grid of numbers — a batch of examples, or a layer of weights. A tensor is the general term for either, at any number of dimensions. Every neural network layer is, mechanically, matrix multiplication plus a nonlinearity.

Think of it as

You do not need to compute any of this by hand — libraries do that — but you do need to read the shapes. A vector of length n is a point in n-dimensional space. A dot product of two vectors measures how much they point the same direction, and is the core operation behind similarity search and a single neuron's weighted sum. Matrix multiplication is many dot products at once — a batch of input vectors times a weight matrix produces a batch of outputs in one operation, which is why it is written as one line of code instead of a loop. A norm measures a vector's length (L2 norm is ordinary Euclidean distance from the origin) and shows up constantly — regularization penalizes large weight norms, embeddings are often compared after normalizing to unit length. Transpose flips a matrix's rows and columns; a matrix has an inverse only if it is square and none of its rows are redundant, and 'inverse' is how you undo a linear transformation.

text
dot(a, b)        = sum(a_i * b_i)              # scalar; measures alignment
A @ B             = matrix multiplication        # (m,k) @ (k,n) -> (m,n)
||v||_2           = sqrt(sum(v_i^2))             # L2 norm, vector length
A.T               = transpose (flip rows/cols)
A_inv @ A == I    = inverse (undoes A), only if A is square, non-redundant

The shapes, at a glance

The shapes, at a glance
ObjectShapeML example
Vector(n,)A 300-dimensional word embedding
Matrix(rows, cols)A batch of 32 examples, each with 10 features: (32, 10)
Tensor(dim1, dim2, ...)A batch of 32 images, each 28×28 pixels, 3 channels: (32, 3, 28, 28)

Remember: A vector is a list of numbers, a matrix is a grid, a tensor is either at any dimension. Matrix multiplication is a batch of dot products in one operation — the mechanical core of every neural network layer.

See also: tensors devices and autograd

Probability foundations for ML

standardbeginner

A random variable is a quantity whose value comes from chance, described by a distribution of possible outcomes and their probabilities. Conditional probability asks how likely something is, given that something else is already known. Bayes' theorem is the rule for flipping one conditional probability into the other.

Think of it as

Most of ML's probabilistic vocabulary exists to answer one question: given what we know, how confident should the model be? A distribution describes the full spread of possible outcomes for a random variable, not just one number — a classifier's softmax output is a probability distribution over classes. Expectation (the mean of a distribution) and variance (how spread out it is) summarize a distribution with two numbers. Conditional probability, written P(A | B), is the probability of A once B is already known — a spam filter estimates P(spam | these words). Bayes' theorem lets you invert that: turn P(evidence | hypothesis), which is often easy to estimate from data, into P(hypothesis | evidence), which is what you actually want to know. Covariance measures whether two variables move together; independence means knowing one tells you nothing about the other, the assumption naive Bayes leans on.

text
P(A | B)  = P(A and B) / P(B)                    # conditional probability
Bayes:      P(A | B) = P(B | A) * P(A) / P(B)

E[X]      = sum(x * P(x))                          # expectation (mean)
Var(X)    = E[(X - E[X])^2]                         # variance
Cov(X, Y) = E[(X - E[X]) * (Y - E[Y])]              # covariance

A, B independent  <=>  P(A | B) == P(A)

Remember: A distribution describes a random variable's full spread of outcomes; expectation and variance summarize it with two numbers. Bayes' theorem inverts P(evidence|hypothesis) into P(hypothesis|evidence) — the thing you actually want.

See also: svm and naive bayes · calibration and probability quality

Statistics foundations for ML

standardbeginner

Mean and median both describe a "typical" value, but the mean is pulled by outliers and the median is not. Standard deviation says how spread out values are, in the same units as the data. A confidence interval gives a range for an uncertain estimate instead of one number. Hypothesis testing asks whether a difference you measured is likely real or likely noise.

Think of it as

Statistics is how you go from a sample of data to a trustworthy claim about the whole population it came from. Mean, median and percentiles all summarize a distribution's center or shape, but they answer different questions — mean is exact but sensitive to outliers, median is robust to them, a percentile (like p95) says where most of the mass sits and matters more than the mean when tails are what you care about (latency, cost per request). Standard deviation is the square root of variance, back in the data's original units, so it is more interpretable. A confidence interval is what you report instead of a single number when the estimate itself is uncertain — 'accuracy is 87%, ±2%' is more honest than '87%' alone. Hypothesis testing formalizes 'is this difference real': it estimates the probability the difference would appear by chance alone (the p-value) even if there were truly no effect. Correlation measures how two variables move together, from -1 to 1, but correlation is not causation — two variables can move together because a third variable drives both.

text
mean   = sum(x) / n
median = middle value when x is sorted
std    = sqrt(variance)
p95    = value below which 95% of observations fall

correlation(X, Y) in [-1, 1]
  1  -> move perfectly together
  0  -> no linear relationship
 -1  -> move perfectly opposite

Remember: Mean is exact but outlier-sensitive; median and percentiles are robust and matter more for tails (latency, cost). A confidence interval reports a range, not a point. Correlation is not causation.

See also: statistical significance and ab testing

Calculus and gradients

standardbeginner

A derivative is the rate a function's output changes as its input changes — its slope at a point. A gradient is the same idea for a function of many inputs at once: a vector pointing in the direction the output increases fastest. Training a model is, mechanically, repeatedly nudging its parameters a small step opposite the gradient of its loss.

Think of it as

You do not compute derivatives by hand in practice — autograd libraries do it — but the concept is what makes training make sense. A derivative tells you: if I nudge this one input slightly, does the output go up or down, and how fast? A partial derivative is that same question for one input of a multi-input function, holding the others fixed. The gradient collects every partial derivative into one vector, and that vector points toward the steepest increase — so moving opposite it is the fastest way to decrease a loss, which is exactly what training does. The chain rule is what makes gradients computable through a deep, multi-layer function: it lets you compute the derivative of a composition (layer 10's output depends on layer 9's, which depends on layer 8's...) by multiplying the derivatives of each step. That chained multiplication is literally what backpropagation is.

text
df/dx           # derivative: how f changes as x changes
∂f/∂x, ∂f/∂y    # partial derivatives, one per input
∇f = [∂f/∂x, ∂f/∂y, ...]   # gradient: vector of all partials

chain rule: d/dx f(g(x)) = f'(g(x)) * g'(x)

update rule:  param = param - learning_rate * gradient

Remember: A gradient is the vector of all partial derivatives, pointing toward steepest increase. Training moves parameters opposite it to decrease the loss. The chain rule — multiplying each layer's derivative — is what makes that computable through a deep network; that is backpropagation.

See also: optimization and gradient descent · forward pass and backpropagation

Optimization and gradient descent

corebeginner

A loss function measures how wrong a model's predictions are — training tries to make it as small as possible. Gradient descent minimizes it by repeatedly nudging the model's parameters a small step opposite the gradient. The learning rate controls how big that step is.

Think of it as

Picture the loss as a landscape with hills and valleys, one axis per parameter, and training as walking downhill on it. The gradient at your current position points uphill, so gradient descent steps opposite it — a small move toward lower loss. Doing this using every training example on every step (batch gradient descent) is accurate but slow; stochastic gradient descent (SGD) uses one example, or a small mini-batch, per step instead — noisier, but far faster, and the noise itself sometimes helps escape shallow dips that are not the true minimum. The learning rate is the step size: too small and training crawls, wasting time; too large and it overshoots the valley and can bounce around or diverge entirely instead of settling. Momentum smooths the walk by carrying some of the previous step's direction forward, like a ball rolling downhill instead of a series of disconnected hops — it powers through small bumps and speeds up consistent downhill runs. Adam combines momentum with a per-parameter adaptive learning rate, so parameters that need bigger steps get them and ones that need smaller, more careful steps get those instead — it is the default optimizer for most deep learning today because it needs less manual learning-rate tuning than plain SGD.

text
gradient descent:  param = param - learning_rate * gradient(loss, param)

SGD:      gradient computed on one example / mini-batch, not the full dataset
momentum: velocity = beta * velocity + gradient
          param    = param - learning_rate * velocity
Adam:     momentum + a per-parameter adaptive learning rate

What we're doing: Trace three steps of gradient descent minimizing a simple loss by hand.

gradient_descent_trace.pypython
# Minimize loss(w) = (w - 4) ** 2, whose minimum is at w = 4.
# gradient = d/dw (w - 4)^2 = 2 * (w - 4)

w = 0.0
learning_rate = 0.1

for step in range(3):
    gradient = 2 * (w - 4)
    w = w - learning_rate * gradient
    loss = (w - 4) ** 2
    print(f"step {step}: w={w:.3f}  loss={loss:.3f}")
5
The gradient at w=0 is 2*(0-4) = -8: it points steeply downhill toward larger w, which is exactly the direction the minimum is in.
6
The update moves opposite the gradient: w becomes 0 - 0.1*(-8) = 0.8 on the first step, already closer to the true minimum at w=4.
7
Loss shrinks every step (16.0 -> 10.24 -> 6.55...) because each step is guaranteed to be downhill, as long as the learning rate is not too large for this loss surface.

Why this works: A minimal, one-parameter example makes visible what "step opposite the gradient" means mechanically — the same update rule, applied to millions of parameters at once via the chain rule, is the entire training loop of a neural network.

Picking a learning rate without watching the loss curve

Wrong

python
optimizer = SGD(model.parameters(), lr=1.0)  # copied from
                                               # an unrelated project
train(model, optimizer, epochs=10)
# Loss is NaN after epoch 2. Nobody looked until the end.

Better

python
optimizer = SGD(model.parameters(), lr=0.01)
for epoch in range(10):
    loss = train_one_epoch(model, optimizer)
    print(f"epoch {epoch}: loss={loss:.4f}")  # watched every epoch
    if math.isnan(loss):
        raise RuntimeError("diverged — lower the learning rate")

What you see: Loss oscillates, grows, or becomes NaN a few steps or epochs into training, and the run is left going for hours before anyone notices — because the loss curve was never actually watched during training, only checked at the end.

Why: A learning rate that is too large for a given loss surface causes steps to overshoot the minimum and diverge instead of converge — this is visible almost immediately in the loss curve, but invisible if nobody looks until training finishes.

Gradient descent, actually run: 30 real steps to the minimum

f(x,y) = x^2 + 4y^2, learning rate 0.12 — every point is a real computed step, not illustrative

Generated from real data — src/content/_image-generators/teach-gradient-descent-path.py

  • A contour plot of the real loss surface f(x,y) = x squared plus 4 y squared, with a real gradient-descent path drawn on top.
  • The path starts at (4, 2) with loss 32.0 and takes 30 real computed steps, curving into the elliptical bowl toward the minimum at (0,0).
  • By the final step, loss has dropped to essentially 0.000 — the real convergence of gradient descent on this surface.

Reading a learning-rate problem

Reading a learning-rate problem
SymptomLikely causeFix
Loss barely decreases over many stepsLearning rate too smallIncrease it, or use an adaptive optimizer (Adam)
Loss oscillates wildly or grows (NaN)Learning rate too largeDecrease it; check for exploding gradients
Loss decreases smoothly then plateaus earlyStuck in a shallow local minimum / needs momentumAdd momentum, or switch to Adam
Loss decreases then starts rising againOverfitting, not an optimization problemSee generalization, §1 — this is a different failure mode

Remember: Gradient descent steps opposite the gradient to reduce loss. SGD uses mini-batches for speed. Learning rate is the step size — too small wastes time, too large diverges. Momentum smooths the path; Adam adds a per-parameter adaptive rate on top and is the common default.

See also: calculus and gradients · forward pass and backpropagation · batch size lr epochs and steps

Reasoning about model behavior, not reciting formulas

referencebeginner

The goal of this section is not to derive or recite formulas from memory. It is to use them as a diagnostic — when a model trains badly, the math above tells you where to look: the loss landscape, the gradient, the learning rate, the data's distribution.

Remember: The four prior concepts exist to diagnose training, not to be recited. A vanishing gradient, a diverging loss, an unreliable metric — each points back to one piece of the math here.

See also: optimization and gradient descent

Advertisement