Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Linear algebra

Linear algebra is tested as a proxy for whether you understand what your model actually computes. The interviewer wants shapes, costs, and one geometric picture per object. The common failure is reciting definitions without the picture: saying “SVD factorises a matrix” without rotate-scale-rotate, or “PCA finds directions of variance” without saying those directions are eigenvectors of the covariance matrix. The second common failure is not knowing why the normal equation is numerically worse than SVD, which is a two-line condition-number argument.

The equations

Matrix multiplication, shapes and cost.

The inner dimensions must match at and they vanish from the result; each of the output entries costs multiplies and adds, so the cost is multiply-accumulates, or floating-point operations.

Dot product and its geometric form.

is the angle between the two vectors, so the dot product mixes magnitude and direction; dividing out both norms leaves cosine similarity, which is direction only.

Vector and matrix norms.

are the singular values of ; the Frobenius norm treats the matrix as one long vector, and the induced spectral norm is the largest factor by which can stretch any vector.

Rank and rank-nullity.

Rank is the number of genuinely independent directions in the output; the null space holds the input directions crushes to zero, and the two dimensions must add up to the number of columns.

Eigenvalue equation.

is an eigenvector and its eigenvalue; these are the directions only stretches and never rotates, and they exist for square only.

Spectral decomposition of a symmetric matrix.

For symmetric real the eigenvalues are real and the eigenvectors can be chosen orthonormal, so is a sum of rank-one pieces along perpendicular axes.

Singular value decomposition.

Every matrix, square or not, has an SVD; the right singular vectors are eigenvectors of the Gram matrix , the left ones are eigenvectors of , and the singular values are the square roots of those shared eigenvalues.

Pseudo-inverse and least squares.

The normal equation comes from setting the gradient of to zero; the pseudo-inverse gives the same answer when has full column rank and the minimum-norm answer when it does not.

Covariance matrix and the PCA objective.

is the data matrix with the column means subtracted; the constrained maximum of that quadratic form is the top eigenvector of , with value the top eigenvalue, so PCA is exactly an eigendecomposition of the covariance.

Positive semi-definiteness, three equivalent statements.

must be symmetric for this to be meaningful; the three forms are the quadratic-form test, the spectrum test, and the factorisation test, and each is the useful one in a different proof.

Determinant as a volume factor.

is any region of ; the determinant is the signed factor by which scales volume, and a negative sign means the map flips orientation.

Condition number.

bounds how much a relative error in the input is amplified in the output; forming the Gram matrix squares it, which is the whole numerical case against the normal equation.

Trace and the cyclic property.

Trace is the sum of the diagonal and also the sum of the eigenvalues; cyclic permutation is legal whenever the shapes still conform, and it lets you move a small factor to the outside and avoid forming a large product.

Jacobian and the gradients used in backprop.

The Jacobian of is ; the quadratic-form gradient simplifies to when is symmetric, and the third result is the one backprop uses for every linear layer under squared error.

Code from memory

Power iteration for the dominant eigenpair, checked against numpy.linalg.eig.

import numpy as np

def power_iteration(A, steps=500, tol=1e-12):
    n = A.shape[0]
    v = np.ones(n) / np.sqrt(n)
    lam = 0.0
    for _ in range(steps):
        # one matrix-vector product, written as explicit loops
        w = np.zeros(n)
        for i in range(n):
            s = 0.0
            for j in range(n):
                s += A[i, j] * v[j]
            w[i] = s
        norm = np.sqrt(sum(w[i] ** 2 for i in range(n)))
        w = w / norm
        # Rayleigh quotient gives the eigenvalue for the current vector
        lam_new = sum(w[i] * sum(A[i, j] * w[j] for j in range(n)) for i in range(n))
        if abs(lam_new - lam) < tol:
            v, lam = w, lam_new
            break
        v, lam = w, lam_new
    return lam, v

rng = np.random.default_rng(0)
B = rng.normal(size=(5, 5))
A = B @ B.T                                   ## symmetric, so eigenvalues are real

lam, v = power_iteration(A)
ev, EV = np.linalg.eig(A)
k = np.argmax(np.abs(ev))
print("power iteration lambda =", round(lam, 8))
print("numpy.linalg.eig  lambda =", round(float(ev[k].real), 8))
print("eigenvector alignment |v.u| =", round(abs(float(v @ EV[:, k].real)), 10))

Output: power iteration gives 8.07932277 and numpy.linalg.eig gives 8.07932277, an exact match to eight decimals, and the eigenvector alignment is 1.0. Convergence rate is per step, so a near-tie between the top two eigenvalues makes it slow.

PCA from scratch, checked against sklearn.decomposition.PCA on the explained-variance ratio.

import numpy as np
from sklearn.decomposition import PCA

def pca_from_scratch(X, k):
    n, d = X.shape
    mu = X.mean(axis=0)
    Xc = X - mu                                   ## centre: PCA is undefined without this
    C = (Xc.T @ Xc) / (n - 1)                     ## covariance, d x d, symmetric PSD
    vals, vecs = np.linalg.eigh(C)                ## eigh: ascending order, orthonormal vecs
    order = np.argsort(vals)[::-1]
    vals, vecs = vals[order], vecs[:, order]
    Z = Xc @ vecs[:, :k]                          ## project onto top-k eigenvectors
    ratio = vals[:k] / vals.sum()
    return Z, vals, ratio

rng = np.random.default_rng(1)
L = rng.normal(size=(6, 3))
X = rng.normal(size=(400, 3)) @ L.T + 5.0         ## 6-D data with 3-D structure, off-centre

Z, vals, ratio = pca_from_scratch(X, 3)
sk = PCA(n_components=3).fit(X)
print("mine   ", np.round(ratio, 6))
print("sklearn", np.round(sk.explained_variance_ratio_, 6))
print("max abs diff", float(np.max(np.abs(ratio - sk.explained_variance_ratio_))))

Output: both print [0.653702 0.281613 0.064685], with maximum absolute difference 1.08e-14. Use eigh and not eig on a covariance matrix, because eigh exploits symmetry and returns real ordered eigenvalues.

Least squares three ways on well-conditioned and then ill-conditioned data.

import numpy as np

def ls_normal(A, b):
    return np.linalg.solve(A.T @ A, A.T @ b)      ## normal equation

def ls_svd(A, b, rcond=1e-12):
    U, s, Vt = np.linalg.svd(A, full_matrices=False)
    s_inv = np.array([1.0 / si if si > rcond * s[0] else 0.0 for si in s])
    return Vt.T @ (s_inv * (U.T @ b))             ## pseudo-inverse A+ = V S+ U^T

def compare(A, b, tag):
    w1 = ls_normal(A, b)
    w2 = np.linalg.lstsq(A, b, rcond=None)[0]
    w3 = ls_svd(A, b)
    print(tag, "cond(A) = %.3e  cond(A^T A) = %.3e" % (np.linalg.cond(A), np.linalg.cond(A.T @ A)))
    print("  normal ", np.round(w1, 6))
    print("  lstsq  ", np.round(w2, 6))
    print("  svd    ", np.round(w3, 6))
    print("  max|normal-svd| = %.3e   max|lstsq-svd| = %.3e"
          % (np.max(np.abs(w1 - w3)), np.max(np.abs(w2 - w3))))

rng = np.random.default_rng(2)
A = rng.normal(size=(50, 3)); w_true = np.array([1.0, -2.0, 0.5]); b = A @ w_true
compare(A, b, "well-conditioned:")

## ill-conditioned: third column is nearly a copy of the first
A2 = A.copy(); A2[:, 2] = A2[:, 0] + 1e-7 * rng.normal(size=50)
b2 = A2 @ w_true
compare(A2, b2, "ill-conditioned: ")

Output. Well-conditioned, and : all three return [1. -2. 0.5] and the largest pairwise difference is 6.66e-16, which is machine precision. Ill-conditioned, and : the normal equation returns [0.911663 -2. 0.588337], wrong in the first and third coefficients by 8.83e-02, while lstsq and the SVD pseudo-inverse both still return [1. -2. 0.5] and agree with each other to 1.46e-09. The squared condition number has eaten roughly half the available digits.

Gram-Schmidt with explicit loops, verified by .

import numpy as np

def gram_schmidt(A):
    n, d = A.shape
    Q = np.zeros((n, d))
    for j in range(d):
        v = A[:, j].copy()
        # subtract the projection onto every earlier q, one at a time (modified GS)
        for i in range(j):
            r = 0.0
            for k in range(n):
                r += Q[k, i] * v[k]
            for k in range(n):
                v[k] -= r * Q[k, i]
        norm = np.sqrt(sum(v[k] ** 2 for k in range(n)))
        if norm < 1e-12:
            raise ValueError("column %d is linearly dependent" % j)
        Q[:, j] = v / norm
    return Q

rng = np.random.default_rng(4)
A = rng.normal(size=(8, 5))
Q = gram_schmidt(A)
G = Q.T @ Q
dev = float(np.max(np.abs(G - np.eye(5))))
print("max |Q^T Q - I| =", "%.3e" % dev)
print("orthonormal to 1e-12:", dev < 1e-12)
print("span preserved (residual of A on Q):",
      "%.3e" % float(np.max(np.abs(Q @ (Q.T @ A) - A))))

Output: max |Q^T Q - I| = 2.220e-16, so the columns are orthonormal to machine precision, and the residual of against its own projection is 6.661e-16, so the span is preserved. Subtracting each projection as it is computed, rather than all at once from the original column, is modified Gram-Schmidt and it is markedly more stable.

Questions

Q1. What does rank mean in plain words, and why is a low-rank matrix compressible?

Rank is the number of independent directions the matrix actually uses. If is with rank , then every column is a linear combination of the same basis columns, so the other columns carry no new information. That is why rank- means compressible: you can write with and , so storage drops from numbers to numbers. At and that is 16.8 million numbers down to 131 thousand, a factor of 128. LoRA uses exactly this. It freezes the pretrained weight and learns an update with inner dimension , on the empirical claim that the useful fine-tuning update has low intrinsic rank. You train parameters instead of , and at inference you can fold into one matrix, so there is no added latency.

Say it. Rank is how many independent directions a matrix really uses. If the rank is , every column is a combination of the same basis columns, so you can factor the matrix as an by times an by and store numbers instead of . For a 4096-square matrix at rank 16 that is a 128-fold saving. LoRA is this idea applied to fine-tuning: freeze the pretrained weight and learn a low-rank update , betting that the useful update has low intrinsic rank. At inference you fold it back in, so no extra latency.

Q2. What does the SVD give you geometrically?

Rotate, scale, rotate. Write . Applying to a vector does three things in order. First rotates, or rotates and reflects, the input into a new orthonormal frame; it changes the axes but not any length. Then is diagonal, so it stretches each new axis by the singular value ; this is the only step that changes size, and a zero collapses that axis entirely. Then rotates the result into the output space. So every linear map, of any shape, is a rotation, then an axis-aligned scaling, then another rotation. The image of the unit sphere under is an ellipsoid whose semi-axis lengths are the singular values and whose axis directions are the columns of . The number of nonzero is the rank, is the spectral norm, and the columns of with span the null space.

Say it. Rotate, scale, rotate. turns the input into a new orthonormal frame without changing any length, stretches each of those axes by its singular value, and rotates into the output space. So every linear map, square or not, is a rotation, an axis-aligned scaling, and another rotation. The unit sphere goes to an ellipsoid with semi-axes equal to the singular values along the columns of . The count of nonzero singular values is the rank, the largest is the spectral norm, and the right singular vectors with zero singular value span the null space.

Q3. Why is SVD preferred over the normal equation for least squares?

Because forming squares the condition number. The relative error in a solved linear system scales with the condition number of the matrix you solve, and because the eigenvalues of the Gram matrix are the squared singular values of . In double precision you have about 16 decimal digits. If , the SVD route loses about 8 digits and keeps 8, while the normal equation solves a system with and keeps essentially none. My third code block shows this: at and , the normal equation is wrong by in the coefficients while the SVD route is still correct to . The SVD also degrades gracefully when is rank-deficient: you truncate the tiny singular values and get the minimum-norm solution, whereas is then singular and the solve simply fails.

Say it. Because squares the condition number: the eigenvalues of the Gram matrix are the squared singular values, so becomes . Double precision gives you sixteen digits, so at of ten to the eight the SVD keeps eight digits and the normal equation keeps none. I measured it: at of two times ten to the seven the normal equation was off by nine parts in a hundred while the SVD was correct to a billionth. SVD also handles rank deficiency by truncating small singular values and returning the minimum-norm solution, where the Gram matrix is just singular.

Q4. What are the eigenvectors of a covariance matrix, and why is PCA exactly that eigendecomposition?

The covariance matrix is symmetric and positive semi-definite, so it has real non-negative eigenvalues and orthonormal eigenvectors. The variance of the data projected onto a unit direction is exactly . PCA asks for the unit direction of maximum projected variance, so it maximises subject to . Form the Lagrangian and set the gradient to zero: , which is . So the stationary points are the eigenvectors and the objective value at each is . The maximum is therefore the top eigenvector, and its eigenvalue is the variance it captures. The next component repeats the problem restricted to the orthogonal complement, which gives the second eigenvector. Explained-variance ratio is .

Say it. The covariance is symmetric and positive semi-definite, so it has real eigenvalues and orthonormal eigenvectors. The projected variance along a unit direction is the quadratic form , so PCA maximises that under a unit-norm constraint. Take the Lagrangian, differentiate, and you get directly. So the stationary directions are the eigenvectors and the objective at each equals its eigenvalue. The top eigenvector is the first component and its eigenvalue is the variance it captures; each later component is the same problem in the orthogonal complement.

Q5. How does PCA relate to the SVD of the centred data matrix?

They are the same computation. Take the SVD of the centred data, . Then . That is already an eigendecomposition of : the right singular vectors are the principal directions, and the eigenvalues are . The projected scores are , so you never need at all. In practice you always take the SVD route, for three reasons. It avoids forming , which squares the condition number and loses half your digits. It costs rather than to form the covariance plus to decompose it, which matters when is large. And when , as with text features, is and may not even fit in memory while does. That is why sklearn.decomposition.PCA calls an SVD internally.

Say it. They are the same thing. If the centred data has SVD , then the covariance is over , which is already its eigendecomposition. So the right singular vectors are the principal directions, the eigenvalues are the squared singular values over , and the scores are . You always take the SVD route, because forming the covariance squares the condition number, costs more when is large, and needs a by matrix that may not fit when features outnumber samples. That is what sklearn does internally.

Q6. What does positive semi-definite mean, and where does it show up in ML?

A symmetric matrix is positive semi-definite when for every . Two equivalent statements: all eigenvalues are non-negative, and can be written as . Geometrically the quadratic form is a bowl that never dips below zero, though it may be flat in some directions. Three places it appears. A covariance matrix is , which is by construction, so it is PSD; this is why variance along any direction is never negative. A kernel matrix must be PSD by Mercer’s condition, because is a Gram matrix; if your similarity matrix is not PSD there is no feature space behind it and the SVM dual is not convex. The Hessian is PSD at any local minimum, which is the second-order condition, and a function whose Hessian is PSD everywhere is convex. Positive definite, with strictly positive eigenvalues, additionally means invertible and a strict minimum.

Say it. Positive semi-definite means the quadratic form is never negative, equivalently all eigenvalues are at least zero, equivalently the matrix factors as . Covariance matrices are PSD because they are literally a Gram matrix, which is why no direction has negative variance. Kernel matrices must be PSD, because that is what guarantees a feature space exists and keeps the SVM dual convex. And the Hessian is PSD at any local minimum; if it is PSD everywhere the function is convex. Strictly positive eigenvalues give you invertibility and a strict minimum.

Q7. What does the condition number tell you about optimisation difficulty?

For a quadratic objective the Hessian is constant and the level sets are ellipsoids with axis lengths set by . The condition number is how elongated that bowl is. Gradient descent is stable only when the step size satisfies , because the steepest direction diverges otherwise. However, progress along the flattest direction goes as per step. So the largest eigenvalue caps the learning rate while the smallest eigenvalue sets how fast you actually move, and the error contracts by roughly per step. The number of iterations to a fixed accuracy therefore scales linearly with . At you need on the order of ten thousand steps where a well-conditioned problem needs ten. The practical fixes all reduce : feature normalisation, batch or layer normalisation, momentum which gives instead of , and per-coordinate scaling as in Adam.

Say it. The condition number is the ratio of the largest to the smallest Hessian eigenvalue, and it is how elongated the loss bowl is. The largest eigenvalue caps your learning rate at two over for stability, and the smallest eigenvalue sets how fast you move along the flat direction, so the contraction per step is about over and iterations scale linearly with . At a condition number of ten thousand you need thousands of steps for what a round bowl does in ten. That is why we normalise features, use normalisation layers, momentum, and per-coordinate scaling in Adam.

Q8. Why does a matrix multiply cost , and how does that give the transformer FLOP count?

For times there are output entries, and each is a dot product of length , so multiply-accumulates or about floating-point operations. Apply that to a transformer. A linear layer mapping to over tokens is a by product, so FLOPs, which is times the parameter count of that layer. Summing over all weight matrices gives the standard rule: forward pass costs about FLOPs per token for parameters. The backward pass costs about twice the forward, because it computes a gradient with respect to both the input and the weights, so training costs about per token. Attention adds a term the parameter count does not cover: the and score-times- products cost about per layer, which is quadratic in sequence length and dominates at long context.

Say it. An by times by product has outputs, each a length- dot product, so multiply-accumulates and about FLOPs. A transformer linear layer over tokens is times that layer’s parameter count, so the forward pass is roughly FLOPs per token for parameters, the backward is twice that, and training is about per token. Attention is the exception, because the score matrix is by : those two products cost about per layer, which is quadratic in sequence length and takes over at long context.

Q9. What does the trace trick buy you?

Two things. First, cyclic permutation, , lets you reorder a product so the largest intermediate matrix is never formed. If , then : the left side builds a outer product for and the right side is . Similarly with tall is cheaper as or the other way round, depending on which dimension is smaller. Second, it turns scalars into traces so matrix calculus applies. Any scalar equals its own trace, so , and then the standard identity gives derivatives with no index bookkeeping. It also gives identities you use constantly: , , and the Gaussian log-likelihood’s quadratic term written as a trace against the sample covariance.

Say it. Two things. Cyclic permutation lets you reorder a product to avoid forming the big intermediate: the trace of an outer product is just , which is order instead of squared. And because any scalar equals its own trace, you can rewrite a quadratic form as a trace and then use matrix-calculus identities like the derivative of trace being transposed, so you differentiate without index gymnastics. It also gives the identities you use daily: Frobenius norm squared is trace of , and trace equals the sum of eigenvalues.

Q10. What is the difference between an orthogonal and an orthonormal basis, and why does orthogonality help numerically?

An orthogonal set has vectors that are mutually perpendicular, for , but with arbitrary lengths. An orthonormal set adds unit length, , so the Gram matrix is exactly the identity. A square matrix with orthonormal columns satisfies , so and the inverse is free. Orthonormality is what makes things numerically safe. Coefficients come from a single dot product, , with no linear solve. All singular values of are 1, so and multiplying by neither amplifies error nor loses it. Lengths and angles are preserved, , so is a rotation or reflection. That is why stable algorithms are built from orthogonal transforms: QR by Householder reflections, and the SVD itself. In deep learning it is why orthogonal weight initialisation helps in deep or recurrent stacks, since repeated multiplication neither explodes nor vanishes.

Say it. Orthogonal means mutually perpendicular; orthonormal adds unit length, so the Gram matrix is exactly the identity and equals . That gives you three numerical wins. Coefficients are a single dot product with no solve. Every singular value is one, so the condition number is one and multiplying by neither amplifies nor loses error. And lengths and angles are preserved, so is a pure rotation or reflection. That is why QR and the SVD are built from orthogonal transforms, and why orthogonal initialisation helps very deep or recurrent networks.

Q11. Why do we normalise embeddings before cosine similarity, and what happens if we do not?

Cosine similarity is defined as , so the normalisation is part of the definition. If you divide each vector by its own L2 norm once, in advance, then the raw dot product is already the cosine, which is the point: dot products are a single fast matrix multiply and every vector database, FAISS included, is built around inner-product search. If you skip normalisation and use the raw dot product, you are ranking by , so long vectors win regardless of direction. Embedding norms correlate with things you did not want to rank on, such as token count, word frequency, and how typical the text is, so retrieval starts returning long or generic passages instead of relevant ones. The failure is quiet, because the results still look plausible. Two notes: normalisation must use the same convention at index time and query time, and after normalisation cosine and squared Euclidean distance are monotonically related, , so they rank identically.

Say it. Because the cosine is the dot product divided by both norms, so if you normalise once up front, the plain dot product is already the cosine, and that is what inner-product search in a vector database computes. Skip it and you rank by norm times norm times cosine, so long vectors win on length alone. Embedding norms track passage length, word frequency and typicality, so you quietly start retrieving long generic chunks instead of relevant ones. Normalise at index time and query time the same way. After normalising, squared Euclidean distance is two minus twice the cosine, so the two rankings agree.

Q12. What is a projection matrix, and what is the idempotence property?

A projection matrix maps any vector onto a subspace and leaves vectors already in that subspace untouched. That second requirement is idempotence: , because projecting something that is already projected changes nothing. An orthogonal projection additionally satisfies , which means the residual is perpendicular to the subspace. For a subspace spanned by the columns of with full column rank, , and if the columns are orthonormal, as in , this collapses to . The eigenvalues of a projection are only 0 and 1, so the trace equals the rank, which equals the dimension of the subspace. This is exactly least squares: is the point in the column space of closest to , and the normal equation is just the statement that the residual is orthogonal to every column, . Attention heads, PCA reconstruction, and gradient projection all use the same object.

Say it. A projection maps a vector onto a subspace and leaves anything already in that subspace alone, which is exactly idempotence: . If it is also symmetric it is an orthogonal projection, so the residual is perpendicular to the subspace. For the column space of it is , and for orthonormal columns it collapses to . Eigenvalues are only zero and one, so the trace equals the rank. Least squares is precisely this: the fitted values are the projection of onto the column space, and the normal equation says the residual is orthogonal to every column.

Q13. What does a determinant of zero mean?

It means the matrix collapses volume to zero, so it is singular. Equivalently, and these are all the same fact: the columns are linearly dependent, the rank is less than , the null space contains a nonzero vector, at least one eigenvalue is zero, at least one singular value is zero, and no inverse exists. Geometrically, is the factor by which scales the volume of any region, so a determinant of zero means the unit cube is flattened into a lower-dimensional slab with no volume. The map is not injective: distinct inputs land on the same output, so it cannot be undone. In practice you almost never test , for two reasons. The determinant is a product of numbers, so it underflows or overflows badly at large , and it is scale dependent since . Use the condition number or the smallest singular value instead; those tell you how close to singular you are, which is the question that actually matters.

Say it. Zero determinant means the matrix squashes volume to nothing, so it is singular. Equivalently the columns are dependent, the rank is deficient, the null space is nontrivial, an eigenvalue is zero, a singular value is zero, and there is no inverse. The unit cube gets flattened into something with no volume, so the map cannot be undone. In practice never test the determinant against zero: it is a product of numbers so it overflows or underflows, and it scales as . Look at the smallest singular value or the condition number, which tell you how near-singular you are.

Q14. What is the relation between the Frobenius norm and the singular values?

. The middle step is just the definition of the trace of the Gram matrix, and the last step holds because the trace equals the sum of eigenvalues and the eigenvalues of are the squared singular values. Two consequences. First, the Frobenius norm is invariant under orthogonal transforms, since ; rotations do not change total energy. Second, this is what makes truncated SVD optimal. The Eckart-Young theorem says the best rank- approximation in Frobenius norm is , and the error is exactly , the tail energy you discarded. So the fraction of energy retained is , which is the same quantity PCA calls the explained-variance ratio. Compare the spectral norm, , which sees only the largest singular value.

Say it. The Frobenius norm squared is the sum of all squared entries, which equals the trace of , which equals the sum of the squared singular values. So it is invariant under rotations, because orthogonal transforms do not change total energy. It also explains truncated SVD: by Eckart-Young the best rank- approximation is the top singular triplets, and the squared Frobenius error is exactly the sum of the discarded squared singular values. That energy ratio is the same number PCA reports as explained variance. The spectral norm, by contrast, only sees the largest singular value.

Q15. Derive step by step.

Write the residual and expand the squared norm as an inner product:

The two cross terms combined because is a scalar, so it equals its own transpose . Now differentiate term by term. The first term is a quadratic form with , whose gradient is ; here is symmetric, so that is . The second term is linear, with , whose gradient is , giving . The third term is constant. Add them:

Check the shapes: is and is , so the gradient is -dimensional like . Setting it to zero gives the normal equation .

Say it. Expand the squared norm as minus plus a constant; the cross terms merge because a scalar equals its own transpose. The quadratic form differentiates to plus times , and is symmetric, so that is . The linear term gives minus . Add them and factor: , which is features transposed against the residual. Shapes check, since is by and the residual is . Set it to zero and you have the normal equation.

Done when

  • You can write the SVD, say rotate-scale-rotate, and state in one sentence why the right singular vectors are eigenvectors of with .
  • You can code power iteration, PCA from scratch, and Gram-Schmidt in NumPy from memory, and each runs first try and matches the library.
  • You can give the condition-number argument against the normal equation in under a minute, including the squaring and the digit count in double precision.
  • You can derive on a whiteboard, and say why the Hessian eigenvalue ratio caps the learning rate.