Kiru Lab / The Mathematics of Learning / The Linear Algebra You Actually Need
Decomposition: Finding Structure in a Matrix
Eigenvectors and the SVD tell you what a transformation actually does, independent of coordinates.
Concept · about 30 minutes
An eigenvector of a matrix is a direction the matrix does not rotate — it only stretches it, by a factor called the eigenvalue. Finding those directions tells you the transformation's intrinsic behavior, stripped of whatever arbitrary coordinate system you wrote it in.
The singular value decomposition generalizes this to any matrix, including non-square ones. It factors `W = U S V^T`, where `V^T` rotates the input, `S` stretches each axis by a singular value, and `U` rotates into the output space. Every matrix, without exception, is a rotation, then a stretch, then another rotation.
import numpy as np
U, S, Vt = np.linalg.svd(W, full_matrices=False)
print(S[:10]) # how much each direction is amplified
print((S > 1e-8).sum(), "effective rank of", min(W.shape))
print(S[0] / S[-1], "condition number")The condition number
The ratio of largest to smallest singular value tells you how badly the matrix amplifies error. A large condition number means a tiny change in input can produce a large change in output — numerically fragile, slow to optimize, and in a control system, dangerous. This one number connects ill-conditioned optimization landscapes, unstable training, and robot singularities into a single phenomenon.
The spectrum of a weight matrix is a structural fact about a layer that you can read without running a single input through it. Low effective rank means the layer is compressing; a heavy tail means it is passing a few dominant directions and ignoring the rest.
Hold on to
- Every matrix is a rotation, a stretch, and another rotation
- Singular values reveal effective rank and numerical conditioning
- Bad conditioning links unstable training and robot singularities
Work through
Try each one before opening the solution. Getting it wrong first is most of where the learning happens.
-
Compute the SVD of a trained linear layer and plot the singular value spectrum on a log scale. Estimate its effective rank.
Hint
Plot `S` from `np.linalg.svd` on a log y-axis.
Solution
Trained layers usually show a heavy-tailed spectrum: a few large singular values and a long decaying tail. Effective rank — the count above a tolerance, or the number needed to capture 99% of the energy — is typically far below the nominal dimension, which is why low-rank adaptation methods work at all.
import numpy as np, matplotlib.pyplot as plt U, S, Vt = np.linalg.svd(W, full_matrices=False) plt.semilogy(S); plt.xlabel("index"); plt.ylabel("singular value"); plt.show() energy = np.cumsum(S ** 2) / np.sum(S ** 2) print("rank for 99% of energy:", int(np.searchsorted(energy, 0.99)) + 1) print("nominal rank:", min(W.shape)) -
Build a matrix with condition number 10^6 and show how a 1e-9 perturbation of the input changes the output.
Hint
Build it from a diagonal matrix with singular values 1 and 1e-6.
Solution
Solving with the ill-conditioned matrix amplifies the perturbation by roughly the condition number — a 1e-9 input change produces an output change near 1e-3, six orders of magnitude larger. This is the same amplification that makes a robot arm near a singularity demand enormous joint velocities for a tiny hand motion.
import numpy as np A = np.diag([1.0, 1e-6]) # condition number 1e6 b = np.array([1.0, 1.0]) x1 = np.linalg.solve(A, b) x2 = np.linalg.solve(A, b + 1e-9) print("condition number:", np.linalg.cond(A)) # 1e+06 print("output change:", np.abs(x1 - x2).max()) # ~1e-03Check your work
Paste this after your own code. If it runs without raising, you have it.
import numpy as np assert np.linalg.cond(np.diag([1.0, 1e-6])) > 1e5 print("ok")
Sign in to track your progress through the lab.