Original source
---
id: linear-algebra
title: Linear Algebra for ML
code: LA
subtitle: Vectors, transforms, eigenvalues, SVD and Ax = b solvers. From-scratch Python and NumPy from the lessons.
hue: 230
sources: phases/01-math-foundations/01-linear-algebra-intuition, phases/01-math-foundations/02-vectors-matrices-operations, phases/01-math-foundations/03-matrix-transformations, phases/01-math-foundations/11-singular-value-decomposition, phases/01-math-foundations/17-linear-systems
source_hash: d0fc32de779910f0
---
## SVD: rotate, scale, rotate
type: architecture
from: phases/01-math-foundations/11-singular-value-decomposition/docs/en.md
```arch
node | x | 1,1 | Input space | n-dim
node | vt | 2,1 | V^T | rotate
node | sig | 3,1 | Sigma | scale by sigma_i
node | u | 4,1 | U | rotate
node | ata | 2,2 | A^T A | eigenvectors are V
node | aat | 3,2 | A A^T | eigenvectors are U
node | y | 4,2 | Output space | m-dim
edge | x | vt |
edge | vt | sig |
edge | sig | u |
edge | u | y |
edge | ata | vt | V
edge | aat | u | U
```
## Gaussian elimination with partial pivoting
type: flow
from: phases/01-math-foundations/17-linear-systems/docs/en.md, phases/01-math-foundations/17-linear-systems/code/linear_systems.py
1. **Pivot**: in column k, find the largest entry at or below row k.
2. **Swap**: swap that row with row k. A small pivot amplifies rounding errors.
3. **Check**: a pivot below `1e-12` raises `ValueError`.
4. **Eliminate**: for each row i below k, compute m = A[i][k] / A[k][k]. Subtract m times row k.
5. **Back substitute**: solve the last equation first, then go upward.
6. **Reuse**: L stores the multipliers. U is the eliminated matrix. PA = LU.
## Tasks in NumPy
type: task-table
from: phases/01-math-foundations/01-linear-algebra-intuition/docs/en.md, phases/01-math-foundations/02-vectors-matrices-operations/docs/en.md, phases/01-math-foundations/03-matrix-transformations/docs/en.md, phases/01-math-foundations/11-singular-value-decomposition/docs/en.md, phases/01-math-foundations/17-linear-systems/docs/en.md
| Task | Code |
|---|---|
| Dot product and length | `np.dot(a, b)`, `np.linalg.norm(a)` |
| Matrix multiply | `A @ B` |
| Element-wise multiply | `A * B` |
| Rank | `np.linalg.matrix_rank(A)` |
| Eigenvalues and eigenvectors | `np.linalg.eig(A)` |
| Thin SVD | `np.linalg.svd(A, full_matrices=False)` |
| Least squares | `np.linalg.lstsq(A, b, rcond=None)[0]` |
| QR, check Q | `np.allclose(Q @ Q.T, np.eye(3))` |
## Truncate, compress and invert with SVD
type: annotated-code
lang: python
from: phases/01-math-foundations/11-singular-value-decomposition/docs/en.md, phases/01-math-foundations/11-singular-value-decomposition/code/svd.py
```python
U, S, Vt = np.linalg.svd(A, full_matrices=False) # S sorted in decreasing order
A_k = U[:, :k] @ np.diag(S[:k]) @ Vt[:k, :] # best rank-k approximation
compressed_size = k * (rows + cols + 1) # storage for rank k
A_pinv = Vt.T @ np.diag(1.0 / S) @ U.T # pseudoinverse, nonzero S only
x_svd = A_pinv @ b # least-squares solution
x_pinv = np.linalg.pinv(A) @ b # same result from NumPy
U, S, V = svd_from_scratch(A) # power iteration and deflation
A_pinv = pseudoinverse_via_svd(A, tol=1e-10) # 1/s only for s above tol
```
## Solve Ax = b from scratch
type: annotated-code
lang: python
from: phases/01-math-foundations/17-linear-systems/docs/en.md, phases/01-math-foundations/17-linear-systems/code/linear_systems.py
```python
x = gaussian_elimination(A, b) # partial pivoting, O(n^3)
P, L, U = lu_decompose(A) # PA = LU, pay O(n^3) one time
x = lu_solve(P, L, U, b) # O(n^2) for each new b
L = cholesky(A) # A = L L^T, symmetric positive definite only
x = cholesky_solve(L, b) # forward, then back substitution
w_ols = least_squares_normal(X, y) # solves A^T A x = A^T b
w_ridge = ridge_regression(X, y, lam=1.0) # adds lam * np.eye(n)
x, iters = conjugate_gradient(A, b) # SPD, at most n iterations
kappa = condition_number(A) # S[0] / S[-1], inf if singular
```
## Transformation matrices
type: command-table
from: phases/01-math-foundations/03-matrix-transformations/docs/en.md, phases/01-math-foundations/03-matrix-transformations/code/transformations.py
| Command | What it does |
|---|---|
| `rotation_2d(theta)` | [[c, -s], [s, c]]. Keeps distances. det 1 |
| `scaling_2d(sx, sy)` | [[sx, 0], [0, sy]]. det is sx * sy |
| `shearing_2d(kx, ky)` | [[1, kx], [ky, 1]]. Rectangles become parallelograms |
| `reflection_y()` | [[-1, 0], [0, 1]]. Flips orientation. det -1 |
| `reflection_x()` | [[1, 0], [0, -1]]. det -1 |
| `mat_mul(S, R)` | Rotate first, then scale. Order matters |
| `eigenvalues_2x2(matrix)` | Roots of the characteristic equation. Complex if discriminant < 0 |
| `eigenvector_2x2(matrix, eigenvalue)` | Unit eigenvector for one eigenvalue |
## Key formulas
type: concepts
from: phases/01-math-foundations/01-linear-algebra-intuition/docs/en.md, phases/01-math-foundations/02-vectors-matrices-operations/docs/en.md, phases/01-math-foundations/03-matrix-transformations/docs/en.md, phases/01-math-foundations/11-singular-value-decomposition/docs/en.md, phases/01-math-foundations/17-linear-systems/docs/en.md
- **Shapes**: (m x n) @ (n x p) = (m x p). The inner dimensions must match.
- **Cosine**: $\cos\theta = \frac{a \cdot b}{\lvert a\rvert \lvert b\rvert}$. 1 is the same direction, 0 is orthogonal.
- **Projection**: $\text{proj}_b a = \frac{a \cdot b}{b \cdot b} b$. The residual is perpendicular to $b$.
- **Eigen**: $Av = \lambda v$. Eigenvalues are roots of $\det(A - \lambda I) = 0$.
- **2x2 eigenvalues**: $\lambda^2 - (a+d)\lambda + (ad - bc) = 0$.
- **SVD**: $A = U \Sigma V^T$ and $A v_i = \sigma_i u_i$. Rank is the count of nonzero $\sigma_i$.
- **Normal equations**: $A^T A x = A^T b$. Ridge adds $\lambda I$ to $A^T A$.
- **Condition number**: $\kappa = \sigma_{\max} / \sigma_{\min}$.
## Pick a solver
type: command-table
from: phases/01-math-foundations/17-linear-systems/docs/en.md
| Command | What it does |
|---|---|
| Gaussian elimination | Square, nonsingular A. O(n^3). One solve |
| LU decomposition | Many solves with the same A. O(n^2) for each solve |
| QR decomposition | Least squares with m >= n. Numerically stable |
| Cholesky | Symmetric positive definite A. Twice as fast as LU |
| Normal equations | Overdetermined, small n. Linear regression |
| SVD / pseudoinverse | Rank-deficient A. Minimum-norm solution |
| Conjugate gradient | Large sparse SPD A. Iterative |
## Numbers and defaults
type: defaults
from: phases/01-math-foundations/01-linear-algebra-intuition/docs/en.md, phases/01-math-foundations/01-linear-algebra-intuition/code/vectors.py, phases/01-math-foundations/11-singular-value-decomposition/docs/en.md, phases/01-math-foundations/11-singular-value-decomposition/code/svd.py, phases/01-math-foundations/17-linear-systems/docs/en.md, phases/01-math-foundations/17-linear-systems/code/linear_systems.py
| Setting | Default | Note |
|---|---|---|
| Zero test in `rank()` | 1e-10 | Smaller entries count as zero |
| Pivot test in elimination | 1e-12 | Smaller pivot raises ValueError |
| `power_iteration` `num_iters` | 200 | Stops early at `tol=1e-10` |
| `conjugate_gradient` `tol` | 1e-10 | `max_iter` defaults to n |
| Safe condition number | < 100 | The solution is accurate |
| Meaningless for float64 | 10^{16} | The matrix is effectively singular |
| Image at rank k=50 | 14.6% | Of the 800 x 600 values |
| LoRA rank | 16 | 131K parameters, not 16M |
## Troubleshooting
type: troubleshooting
from: phases/01-math-foundations/01-linear-algebra-intuition/docs/en.md, phases/01-math-foundations/02-vectors-matrices-operations/docs/en.md, phases/01-math-foundations/03-matrix-transformations/docs/en.md, phases/01-math-foundations/11-singular-value-decomposition/docs/en.md, phases/01-math-foundations/17-linear-systems/docs/en.md
| Symptom | Cause | Fix |
|---|---|---|
| Shape mismatch error | Inner dimensions differ | Write each shape before you multiply |
| `inverse_2x2` raises ValueError | Determinant is 0 | Use the pseudoinverse |
| "Matrix is not positive definite" | A is not SPD | Add lambda I, or use LU |
| Weights swing on small input changes | Collinear features, rank deficient | Drop features or add regularization |
| Answer loses digits | kappa near 10^k loses k digits | Regularize, or use QR or SVD |
| `np.linalg.eig(A.T @ A)` is imprecise | It squares the condition number | Use `np.linalg.svd(A)` |
| Composition gives the wrong point | B @ A applies A first | Multiply in reverse order of application |
| Gram-Schmidt dots are not exactly 0 | Floating point | Compare with a tolerance such as 1e-9 |
## Questions
type: qa
from: phases/01-math-foundations/03-matrix-transformations/docs/en.md, phases/01-math-foundations/11-singular-value-decomposition/docs/en.md, phases/01-math-foundations/17-linear-systems/docs/en.md
- **Why factor with LU?** The O(n^3) cost is paid one time. Each new b costs O(n^2).
- **When does Cholesky work?** A is symmetric positive definite. $X^T X + \lambda I$ is SPD for $\lambda > 0$.
- **What does the determinant tell you?** The area scale. 0 crushes a dimension. -1 flips orientation.
- **Why is truncated SVD the best rank k?** Eckart-Young. The spectral error is $\sigma_{k+1}$.
- **What do eigenvalues say about stability?** Magnitude above 1 explodes. Below 1 vanishes.
## Tips
type: tips
from: phases/01-math-foundations/01-linear-algebra-intuition/docs/en.md, phases/01-math-foundations/11-singular-value-decomposition/docs/en.md, phases/01-math-foundations/17-linear-systems/docs/en.md
- PCA is SVD on centered data. The components are the right singular vectors V.
- Explained variance for each component is sigma_i^2 / (n-1).
- Look for a clear gap in the singular values. Keep the values above it to denoise.
- Ridge improves the condition number to $(\sigma_{\max} + \lambda) / (\sigma_{\min} + \lambda)$.
- The pseudoinverse gives the solution with the smallest norm when many solutions exist.
- Orthogonal initialization uses QR to make orthonormal weight columns.