Linear Algebra for ML
A layer in a neural network, a linear regression model, and a batch of predictions being computed all at once are the same operation: multiply a matrix by a vector (or another matrix). Nearly every piece of notation in this knowledge base is linear algebra, so this page fixes the vocabulary once.
A layer, a linear model, and a batch of predictions are all the same operation — .

Vectors and vector spaces
A vector is an ordered list of numbers. Geometrically, it's a point (or an arrow from the origin) in -dimensional space. A dataset of examples, each with features, is stored as a matrix — one row per example.
Matrices as linear maps
A matrix is a function: it takes a vector in and produces a vector in via (or when is a batch of row-vectors and a single weight vector). This is "linear" because — no bending, no thresholds, just scaling and combining.
Matrix multiplication as composition
Multiplying matrices means: apply 's transformation, then apply 's. Order matters — in general — because composing "rotate then scale" is not the same as "scale then rotate."
Shapes and broadcasting
The single biggest source of bugs in this entire field is a shape mismatch. For : if is and is , the output is — one prediction per row. Broadcasting lets NumPy/PyTorch apply an operation between arrays of different shapes by implicitly repeating the smaller one along missing dimensions, but it fails silently when shapes are compatible but wrong — e.g. adding a vector to a column produces an matrix by accident, not an error.
Transpose, inverse, pseudo-inverse
- Transpose flips rows and columns: .
- Inverse satisfies — only exists for square, full-rank matrices.
- Pseudo-inverse generalises "inverse" to non-square matrices, and is exactly the closed-form solution to least squares (see Linear Regression).
Norms
| Norm | Formula | Use |
|---|---|---|
| sparsity-inducing penalties (Lasso) | ||
| Euclidean distance, weight decay | ||
| Frobenius | matrix version of |
Dot product as similarity
. Two vectors pointing the same direction have a large positive dot product; orthogonal vectors have zero; opposite vectors are negative. This is why cosine similarity (dot product normalised by magnitude) is the default similarity measure for embeddings throughout Sequences & NLP.
Eigenvectors and eigenvalues
For a square matrix , an eigenvector satisfies — applying to only scales it, never rotates it. is the corresponding eigenvalue. Geometrically, eigenvectors are the axes a transformation stretches along without turning.
| Symbol | Meaning |
|---|---|
| a square matrix (a linear transformation) | |
| an eigenvector of | |
| the eigenvalue: how much scales along |
SVD, stated
Every matrix (not just square ones) factors as , where are orthogonal and is diagonal with non-negative entries (singular values). This is the machinery behind PCA and SVD — proved out there in full.
Why GPUs make this fast
Matrix multiplication decomposes into millions of independent multiply-accumulate operations that can run in parallel — exactly what GPUs are built for. A neural network forward pass is a sequence of matrix multiplications, which is why the entire deep learning revolution rode on hardware originally built for rendering graphics.
Code: a linear layer by hand, and a broadcasting bug
import numpy as np
n, d, k = 5, 3, 2 # 5 examples, 3 input features, 2 output units
X = np.random.randn(n, d) # shape (5, 3)
W = np.random.randn(d, k) # shape (3, 2)
b = np.random.randn(k) # shape (2,)
Y = X @ W + b # (5,3) @ (3,2) -> (5,2), then broadcast +(2,)
assert Y.shape == (n, k)
print("linear layer output shape:", Y.shape)
# --- The classic broadcasting bug ---
predictions = np.random.randn(n) # shape (5,) -- one prediction per row
targets = np.random.randn(n, 1) # shape (5,1) -- accidentally a column vector
wrong = predictions - targets # broadcasts to (5,5) instead of (5,) !
print("buggy shape (should be (5,), is):", wrong.shape)
correct = predictions - targets.squeeze()
print("fixed shape:", correct.shape)
# --- Eigenvalues ---
A = np.array([[2.0, 0.0], [0.0, 3.0]])
eigenvalues, eigenvectors = np.linalg.eig(A)
print("eigenvalues:", eigenvalues)
The buggy block is not a crash — it silently produces a matrix of pairwise differences instead of the vector of per-example residuals you wanted, and every downstream computation is now wrong without raising an error.
See also
- Calculus and Gradients — the derivative machinery built on top of this notation.
- Curse of Dimensionality — what happens to these geometric intuitions as grows large.