A symmetric matrix has real eigenvalues and perpendicular eigenvectors, so it is a pure stretch along perpendicular axes. The covariance matrix of a dataset is symmetric, and its eigenvectors are the directions in which the data varies most, the principal components.
About 50 minutes
By the end you can
Recognize a symmetric matrix and name where symmetric matrices appear in machine learning.
State the spectral theorem, A=QΛQ⊤, and explain what an orthogonal matrix does to lengths and angles.
Prove that eigenvectors of a symmetric matrix for different eigenvalues are perpendicular.
Compute the variance of data along a direction as u⊤Cu, and find principal components as eigenvectors of the covariance matrix.
Here are five points in the plane: (3,3), (−3,−3), (1,−1), (−1,1) and (0,0). Plot them and one question answers itself: along which direction do they spread out most? Along the diagonal line through (1,1). Two of the points sit on that line, three times as far from the middle as the two on the other diagonal.
Now ask the same question of 10,000 sentence embeddings, each a list of 768 numbers. No picture helps. What you want is a calculation that takes the data and returns the direction of greatest spread, then the next one, and so on. This lesson builds it, and the answer is an eigenvector. Not of any matrix, though: of a symmetric one, and symmetric matrices have unusually clean eigenvectors.
We start with what symmetric means and what it does to eigenvectors, prove it, and then use it on data. The same facts carry the next lesson, on singular values.
Most matrices from the last two lessons had eigenvectors at odd angles to each other: (1,1) and (1,−2) for [4213]. One family of matrices never does that, and it is the family machine learning meets most.
A matrix is symmetric if it equals its own transpose, A⊤=A: the entry in row i, column j equals the entry in row j, column i, so the matrix is a mirror image across its diagonal. [2112] is symmetric; [4213] is not, because its top-right 1 differs from its bottom-left 2. Symmetric matrices are everywhere in machine learning: covariance matrices (this lesson), the Hessian of a loss (the matrix of second derivatives, in gradient descent), and A⊤A for any matrix A (the next lesson) are all symmetric.
See what symmetry does before we prove anything. The instrument below is the matrix instrument again, starting at the symmetric matrix [2112], with the lime eigen-lines switched on and the eigenvalues in the readouts. The coral arrow is the second column, (1,2). Dragging its tip sideways changes the top-right entry and leaves the bottom-left one alone, which breaks the symmetry.
In every symmetric case the eigen-lines were perpendicular, and there were always two of them. That is a theorem.
The picture suggests a rule. Here it is in full, followed by the two proofs we need for 2x2 matrices.
This is the diagonalization A=PDP−1 of the previous lesson with the best possible P, one whose inverse is just its transpose. A matrix like Q, whose columns have length 1 and are perpendicular to each other, is called orthogonal. Three facts about it:
Its inverse is its transpose. Entry (i,j) of Q⊤Q is row i of Q⊤ dotted with column j of Q, which is column i dotted with column j. That is 1 when i=j (length 1) and 0 otherwise (perpendicular), so Q⊤Q=I.
It keeps every length. Write the squared length as a dot product and use the first fact:
∣Qx∣2=(Qx)⊤(Qx)=x⊤Q⊤Qx=x⊤x=∣x∣2.
It keeps every angle. The same steps show (Qx)⋅(Qy)=x⊤Q⊤Qy=x⊤y=x⋅y, and the angle between two vectors is fixed by their dot product and their lengths. So in the plane an orthogonal matrix is a rotation or a reflection (in more dimensions, a combination of them).
Read A=QΛQ⊤ right to left, as before. Q⊤ rotates (or reflects) the eigen-directions onto the axes, Λ stretches each axis by its eigenvalue (a negative one also flips it), and Q rotates back. No shear anywhere: a symmetric matrix is a pure stretch along perpendicular axes.
We will prove the two halves of the theorem for the cases we need. First, why the eigenvalues are real in the 2x2 case.
Go slower: A symmetric 2x2 matrix never has complex eigenvalues
Write A=[abbd]. Its trace is a+d and its determinant is ad−b2. The eigenvalues are complex only when the discriminant (trA)2−4detA is negative, so compute it one move at a time:
(a+d)2−4(ad−b2)=a2+2ad+d2−4ad+4b2=a2−2ad+d2+4b2=(a−d)2+4b2expand both partscombine 2ad−4ada2−2ad+d2=(a−d)2
A sum of squares is never negative, so the eigenvalues are real. The discriminant is zero only if a=d and b=0, that is, when A is a multiple of I, and then every direction is an eigen-direction.
Second, why the eigenvectors are perpendicular. The proof is three lines. It uses only the transpose rule (AB)⊤=B⊤A⊤ from shapes, transposes and batches and the dot product written as a matrix product, a⋅b=a⊤b.
Go slower: Eigenvectors for different eigenvalues are perpendicular
Let Av1=λ1v1 and Av2=λ2v2 with λ1=λ2 and A⊤=A. Compute the number (Av1)⋅v2 in two ways.
Way 1, using Av1=λ1v1:
(Av1)⋅v2=λ1(v1⋅v2).
Way 2, moving A to the other side, one move per step:
(Av1)⋅v2=(Av1)⊤v2=v1⊤A⊤v2=v1⊤Av2=v1⊤(λ2v2)=λ2(v1⋅v2).dot product as a matrix producttranspose rulesymmetry, A⊤=AAv2=λ2v2
Compare. Both equal the same number, so λ1(v1⋅v2)=λ2(v1⋅v2), which rearranges to
(λ1−λ2)(v1⋅v2)=0.
The first factor is not zero because the eigenvalues differ. So v1⋅v2=0: the eigenvectors are perpendicular. The step that needed symmetry was replacing A⊤ by A. Without it, the argument breaks, and so does the conclusion.
Worked example.A=[2112] has trace 4 and determinant 4−1=3, so λ2−4λ+3=(λ−3)(λ−1)=0 and the eigenvalues are 3 and 1. For λ=3, the row (−1,1) of A−3I gives the eigenvector (1,1). For λ=1, the row (1,1) of A−I gives (1,−1). Their dot product is 1⋅1+1⋅(−1)=0, as promised. Both have length 2, so dividing by 2 gives unit vectors:
Q=21[111−1],Λ=[3001].
Multiply back to check. QΛ scales the columns of Q by 3 and 1, and this Q happens to equal its own transpose, so
Compare the non-symmetric [4213] from the first lesson: its eigenvectors (1,1) and (1,−2) have dot product 1−2=−1, so they are not perpendicular.
To recap: symmetric means A⊤=A; such a matrix has real eigenvalues and perpendicular eigenvectors, so it factors as QΛQ⊤ with an orthogonal Q that keeps lengths and angles. Take one apart yourself, including the check.
On paperA symmetric matrix, taken apart
Let A=[5222].
Find its eigenvalues and an eigenvector for each.
Check that the eigenvectors are perpendicular.
Write A=QΛQ⊤ with unit eigenvectors, and multiply it out to confirm you get A back.
Enter the two eigenvalues below as a column, larger first.
Work it on real paper: writing each step is the point. Then check your final answer here and compare your working with the walk-through.
The two eigenvalues as a column, larger first
One entry per box, top to bottom. 0.25, -2, 3/4 and sqrt(2) all work. Enter moves to the next empty box and checks once all are filled.
Now the payoff promised at the start: the direction in which data varies most. It takes three steps. Measure how the features vary together (the covariance matrix), turn "the spread along a direction" into a formula, and maximize that formula.
The data and its covariance. You have n examples, each with d features, stored as the rows of a data matrix X of shape n×d (the row convention used in code). First center the data: subtract each column's mean, so every feature averages to zero. Call the result X~. The covariance matrix is
C=n−11X~⊤X~,Cjk=n−11i=1∑nx~ijx~ik.
It is d×d. Its diagonal entry Cjj is the variance of feature j, the average squared distance from the mean. The off-diagonal entry Cjk is the covariance of features j and k, positive when they tend to rise together. It is symmetric because Cjk and Ckj are the same sum. (Dividing by n−1 is the usual sample convention, and it is what np.cov does. Module 0 divided by n, as np.var does; the two choices differ by the same factor for every variance and change no direction.)
For the five points from the opening, the mean is (0,0), so they are already centered. Add up the three kinds of product over the points:
Each feature has variance 5, and the covariance 4 is positive: when x is large, y tends to be large too.
The spread along a direction. Take a unit vector u and project every centered example onto it: zi=u⋅x~i, where x~i is row i of X~ written as a column. Each zi is how far example i lies along u. The zi average to zero, and their variance turns out to be a single number built from u, C and u again (an expression of this kind is called a quadratic form):
n−11i=1∑nzi2=u⊤Cu.
Check it on the five points with u=(1,1)/2. The projections are (3+3)/2=32, then −32, then (1−1)/2=0, 0 and 0. Their squares add to 18+18=36, and 36/4=9. The formula gives the same: Cu=(5+4,4+5)/2=(9,9)/2, and u⊤Cu=(9+9)/2=9. Along the x-axis, u=(1,0), it gives C11=5, and along (1,−1)/2 it gives (5−4−4+5)/2=1. The diagonal really is the long direction.
Go slower: From projected variance to u transpose C u
Each zi=u⊤x~i is a number, so it equals its own transpose, x~i⊤u. Then
zi2=zi⋅zi=(u⊤x~i)(x~i⊤u)=u⊤(x~ix~i⊤)u.
Sum over i and pull the fixed u outside the sum:
i∑zi2=u⊤(i∑x~ix~i⊤)u.
The sum of outer products ∑ix~ix~i⊤ is exactly X~⊤X~: that is the outer-product view of matrix multiplication, with column i of X~⊤ times row i of X~. Divide by n−1 and you have u⊤Cu. As a bonus, a variance is never negative, so u⊤Cu≥0 for every u. With u a unit eigenvector, u⊤Cu=λu⊤u=λ, so every eigenvalue of C is at least 0.
The best direction is the top eigenvector.C is symmetric, so by the spectral theorem it has perpendicular unit eigenvectors q1,…,qd. Number them so the eigenvalues come largest first, λ1≥λ2≥⋯≥λd≥0. Any unit u can be written as u=c1q1+⋯+cdqd, and then c12+⋯+cd2=1. The reason: ∣u∣2=u⋅u, and multiplying out that dot product gives a term cicj(qi⋅qj) for every pair; the pairs with i=j are 0 (perpendicular) and each qi⋅qi is 1, so ∣u∣2=c12+⋯+cd2, which is 1 for a unit vector. The box below shows that then
with equality when u=q1. The variance along u is an average of the eigenvalues, weighted by ci2, and an average is never above its largest value.
Go slower: Why u transpose C u is a weighted average of the eigenvalues
Apply C to u, using linearity and then Cqi=λiqi:
Cu=c1Cq1+⋯+cdCqd=c1λ1q1+⋯+cdλdqd.
Now dot with u=c1q1+⋯+cdqd. Multiplying out gives one term cjciλi(qj⋅qi) for every pair i,j. Each qj⋅qi with i=j is 0, because the eigenvectors are perpendicular, and each qi⋅qi is 1. Only the terms with i=j survive:
u⊤Cu=c12λ1+c22λ2+⋯+cd2λd.
Every λi is at most λ1, so replacing each by λ1 can only make the sum larger, and c12+⋯+cd2=1 turns it into λ1. Equality needs all the weight on λ1: c1=±1 and every other ci=0, which is u=±q1.
Check it on the five points. Their covariance has the unit eigenvectors q1=(1,1)/2 and q2=(1,−1)/2, with eigenvalues 9 and 1 (the worked example below confirms this; they are the variances along the two diagonals found above). The x-axis u=(1,0) has c1=u⋅q1=1/2 and c2=u⋅q2=1/2 (dotting u=c1q1+c2q2 with q1 leaves only c1, because q1⋅q1=1 and q1⋅q2=0), so c12=c22=21, and the formula gives 9⋅21+1⋅21=5: the variance along the x-axis, C11, halfway between the two eigenvalues.
So the direction of greatest variance is the top eigenvector, and the variance along it is the top eigenvalue. The same argument, restricted to directions perpendicular to q1, picks out q2, and so on down the list.
Worked example. For the five points, C=[5445] has the same pattern as [2112] (equal diagonal entries, equal off-diagonal entries), so its eigenvectors are again (1,1) and (1,−1). The eigenvalues are 5+4=9 and 5−4=1: check that C(1,1)=(9,9) and C(1,−1)=(1,−1), and that 9+1=10 is the trace and 9×1=25−16 the determinant. The first principal component is (1,1)/2, with variance 9, the number we computed from the projections above. It carries 9/(9+1)=90% of the total variance.
Every other direction has less spread than the first component. Measure one yourself.
Work it outSpread along another direction
The five points from the lesson have covariance C=[5445]. What is the variance of the data along the unit direction u=(0.6,0.8)? Enter a decimal to two decimal places.
variance
Type a number: 0.25, -2, 3/4 and sqrt(2) all work. Enter checks.
Now a larger cloud: 200 random points stretched by 2 along one axis and 0.5 along the other, then tilted by 30° and moved away from the origin, so you can watch PCA recover the tilt.
⌘+Enter runs · edit freelyPython sleeps until you run code
The code prints an angle of 27.5°, close to the 30° tilt but not equal to it, because 200 random points only approximate the shape they were drawn from. For the same reason the two variances come out as 3.352 and 0.199 rather than 22=4 and 0.52=0.25, the spreads the cloud was drawn with. The first component still holds 94.4% of the variance. Use np.linalg.eigh, not eig, for symmetric matrices: it is faster, guarantees real results and perpendicular eigenvectors, and sorts the eigenvalues (in increasing order, which is why the code reverses them).
To recap: center the data, form the symmetric covariance matrix C, and its eigenvectors, largest eigenvalue first, are the principal components; each eigenvalue is the variance along its direction. Now implement it, and test it on the five points and on a 3D cloud with a known shape.
Code itPCA from the covariance matrix
Implement principal component analysis with the covariance matrix and np.linalg.eigh.
covariance(X): center the columns of the (n,d) data, then compute X~⊤X~/(n−1).
pca(X, k): the top k unit eigenvectors as the rows of a (k,d) array, largest variance first, and their variances.
project(X, components): the coordinates of each centered example along each component, shape (n,k).
explained_variance_ratio(X, k): the fraction of the total variance in the top k components.
The tests include the five points from the lesson, moved away from the origin so that forgetting to center shows up, and a 3D cloud with a known shape. Do not call np.cov in your code; the tests use it to check you.
⌘+Enter runsPython sleeps until you run code
Write your code where the starter says raise NotImplementedError, then press Run tests. Each check says what it expects.