Linear Algebra for ML¶
The language of machine learning: every dataset is a matrix, every model output a vector, and PCA, least squares, embeddings, and transformers are all linear algebra in disguise.
Table of Contents¶
- Installation / Setup
- Core Syntax
- Common Operations
- Practical Examples
- Common Patterns
- Best Practices
- Common Mistakes
- Troubleshooting
- Related Cheat Sheets
- Official Documentation
Installation / Setup¶
No math install needed — use NumPy:
Core Syntax¶
Vectors¶
A vector $x \in \mathbb{R}^n$ is an ordered list of $n$ numbers — a single data point or a set of weights.
| Concept | Definition | Notes |
|---|---|---|
| Addition / scaling | $(x+y)_i = x_i + y_i$, $(cx)_i = c x_i$ | Elementwise |
| Dot product | $x \cdot y = \sum_i x_i y_i = x^\top y$ | Measures alignment |
| Euclidean norm | $|x|_2 = \sqrt{x^\top x}$ | Length; default norm |
| L1 norm | $|x|_1 = \sum_i \lvert x_i \rvert$ | Sparsity (lasso) |
| Unit vector | $x / |x|_2$ | Direction only |
| Cosine similarity | $\cos\theta = \frac{x \cdot y}{|x||y|}$ | Embedding similarity |
| Orthogonal | $x \cdot y = 0$ | No shared direction |
np.dot(a, b) # or a @ b
np.linalg.norm(a) # L2
np.linalg.norm(a, ord=1) # L1
a / np.linalg.norm(a) # unit vector
cos = a @ b / (np.linalg.norm(a) * np.linalg.norm(b))
Matrices¶
$A \in \mathbb{R}^{m \times n}$ maps $n$-dim input to $m$-dim output. A dataset with $N$ rows and $d$ features is $X \in \mathbb{R}^{N \times d}$.
| Operation | Definition | Use |
|---|---|---|
| Matrix-vector product | $(Ax)i = \sum_j Ax_j$ | Linear model: $y = Xw$ |
| Matrix product | $(AB){ik} = \sum_j A$}B_{jk | Composing transforms |
| Transpose | $(A^\top){ij} = A$ | Swap rows/cols |
| Identity | $I$ | $AI = A$ |
| Trace | $\mathrm{tr}(A) = \sum_i A_{ii}$ | Sum of eigenvalues |
| Determinant | $\det(A)$ | Volume scale; zero ⇔ singular |
| Inverse | $A^{-1}A = I$ | "Divide" by a matrix |
| Rank | # independent rows/cols | True dimensionality |
Key facts: $(AB)^\top = B^\top A^\top$; $A$ invertible ⇔ $\det(A) \neq 0$ ⇔ full rank ⇔ no zero eigenvalues.
Eigenvalues & eigenvectors¶
$$A v = \lambda v$$
For a square matrix $A$, an eigenvector $v$ only gets stretched (by $\lambda$), not rotated. $A^k v = \lambda^k v$ — powers live in the eigenvalues.
- $\det(A) = \prod_i \lambda_i$, $\mathrm{tr}(A) = \sum_i \lambda_i$
- Symmetric $A$ ($A = A^\top$): real eigenvalues, orthonormal eigenvectors — covariance matrices qualify.
- Positive semi-definite ($x^\top A x \ge 0$ for all $x$): all $\lambda_i \ge 0$ — true for covariance and Gram matrices; guarantees convex quadratic forms.
- Sparse/ill-conditioned? Watch the condition number $\kappa = \lambda_{\max}/\lambda_{\min}$ (large $\kappa$ = numerically unstable).
w, V = np.linalg.eig(A) # eigenvalues w, eigenvectors as columns of V
# symmetric: np.linalg.eigh(A) is faster and numerically safer
Singular value decomposition (SVD)¶
$$A = U \Sigma V^\top$$
Every matrix (even non-square) factorizes into: $U$ (left singular vectors), $\Sigma$ (singular values, non-negative, sorted), $V^\top$ (right singular vectors).
- $\mathrm{rank}(A)$ = number of nonzero singular values.
- Truncated SVD keeps the top $k$ singular values — best rank-$k$ approximation of $A$ (Eckart–Young).
- This is exactly what PCA / latent semantic analysis / word embeddings compute.
- Pseudoinverse $A^+ = V \Sigma^{-1} U^\top$: the least-squares solution machine.
U, s, Vt = np.linalg.svd(A, full_matrices=False)
k = 2
A_k = U[:, :k] @ np.diag(s[:k]) @ Vt[:k, :] # best rank-2 approx
Other decompositions¶
| Decomposition | Form | Use |
|---|---|---|
| LU | $A = LU$ | Solving linear systems |
| QR | $A = QR$ | Least squares, stability |
| Cholesky | $A = LL^\top$ | SPD matrices; sampling MVN; Kalman/GP |
| Eigendecomposition | $A = V\Lambda V^{-1}$ | Square, diagonalizable |
Common Operations¶
X @ w # matrix-vector (linear model forward)
X.T @ X # Gram matrix (covariance unscaled)
np.linalg.solve(A, b) # SOLVE, never A_inv @ b for this
np.linalg.inv(A) # explicit inverse (rarely needed)
np.linalg.det(A), np.trace(A)
np.linalg.matrix_rank(X)
np.linalg.norm(A) # Frobenius norm = sqrt(sum of sq)
np.linalg.lstsq(X, y, rcond=None) # least-squares solution
np.linalg.pinv(X) # pseudoinverse
Practical Examples¶
1. Cosine similarity between embeddings¶
def cos_sim(a, b):
return a @ b / (np.linalg.norm(a) * np.linalg.norm(b))
# RAG retrieval: score = cosine similarity of query vs document embeddings
scores = np.array([cos_sim(query_emb, doc_emb) for doc_emb in doc_embeddings])
top_k = np.argsort(scores)[-5:][::-1]
2. PCA in 4 lines (via SVD)¶
Xc = X - X.mean(axis=0) # center
U, s, Vt = np.linalg.svd(Xc, full_matrices=False)
Z = Xc @ Vt.T # scores (projections)
W_pc = Vt[:2].T # top-2 principal directions
X_reduced = Xc @ W_pc # 2-D embedding for plotting
explained = (s**2) / (s**2).sum() # variance explained per component
3. Least-squares regression in closed form¶
Xb = np.c_[np.ones(len(X)), X] # design matrix with bias
w = np.linalg.pinv(Xb) @ y # = lstsq solution
# geometrically: y_hat = Xb w is the projection of y onto col(Xb)
Common Patterns¶
# Standardize -> covariance -> eigen -> PCA
cov = np.cov(Xc, rowvar=False) # d x d covariance
w, V = np.linalg.eigh(cov) # eigh for symmetric
proj = Xc @ V[:, ::-1][:, :2] # keep top-2 by eigenvalue
# Linear layer as affine transform (DL)
z = X @ W.T + b
# Attention score = scaled dot product (transformers)
scores = Q @ K.T / np.sqrt(d_k)
Best Practices¶
- Solve systems with
np.linalg.solve/lstsq, never by computing $A^{-1}$ — faster and numerically stable. - Use
eighfor symmetric matrices (covariance, Gram) — assumes and exploits symmetry. - Center data before PCA/SVD; scale features when units differ.
- Think in ranks, not sizes — $\mathrm{rank}(X)$ tells you the true degrees of freedom and whether $X^\top X$ is invertible.
- Check the condition number before inverting near-singular matrices; regularize (ridge adds $\lambda I$).
- Dimension errors are the #1 bug: verify with
X.shapeand remember $(AB)^\top = B^\top A^\top$ when deriving gradients.
Common Mistakes¶
| Mistake | Consequence | Fix |
|---|---|---|
np.linalg.inv(X.T @ X) @ X.T @ y |
Slow + unstable for correlated features | np.linalg.lstsq(X, y) or add ridge |
| Assuming $X^\top X$ invertible | Failure with $N < d$ or collinear columns | Regularize, reduce dims, use pinv |
| Forgetting to center in PCA | First PC captures the mean | Subtract column means first |
eig on non-symmetric matrices in ML |
Complex eigenvalues appear | Check symmetry → eigh |
| Mixing row vs column vectors | Shape errors in @ |
Keep a convention; .reshape(-1, 1) for columns |
Norm without axis on a matrix |
Whole-matrix norm | np.linalg.norm(X, axis=1) |
Troubleshooting¶
| Symptom | Cause | Fix |
|---|---|---|
LinAlgError: Singular matrix |
Zero determinant / collinear columns | Regularize or drop redundant features |
ValueError: matmul: shape mismatch |
Incompatible dims | Confirm inner dims match; check transposes |
| Complex eigenvalues unexpectedly | Asymmetric matrix or numerical noise | Use eigh on symmetric; symmetrize $A + A^\top$ |
| PCA components look wrong | Uncentered/unscaled data | X - mean; standardize features |
| Tiny/large norms in softmax/attention | Poor scaling | Divide by $\sqrt{d}$ (scaled dot-product) |
Related Cheat Sheets¶
- calculus.md — gradients (optimization uses both)
- probability.md — covariance/correlation from inner products
- optimization.md — why conditioning and eigenvalues matter for convergence
- NumPy — every operation implemented
- SciPy —
scipy.linalgextras (expm, Cholesky variants) - Mathematics & Statistics category index · Home · Index
Official Documentation¶
Last Updated¶
2026-09-06 — Initial version.