- Compute norms, dot products and cosine similarity, and explain why cosine similarity drives embedding search and attention.
- Predict the output shape and FLOP cost of matrix products in batched neural-network layers.
- Apply transpose, identity and inverse correctly, and choose a linear solver over an explicit inverse.
- Derive eigenvalues and eigenvectors of a small matrix and relate them to curvature, PCA and recurrent dynamics.
- Explain the SVD and low-rank approximation, and calculate the parameter savings of a LoRA adapter.
- Implement vectorized NumPy code that uses broadcasting safely.
Every modern AI model is, at the level of the hardware, a long sequence of multiplications and additions on arrays of numbers. Linear algebra is the language for those arrays. When a search engine finds documents "similar" to your query, when a transformer mixes information between tokens, when LoRA fine-tunes a 70-billion-parameter model on one GPU, and when PyTorch throws RuntimeError: mat1 and mat2 shapes cannot be multiplied, you are looking at linear algebra. This lesson builds the parts an ML engineer uses daily, with the shapes, the numbers, and the code.
The picture to keep in your head
Think of a vector as an arrow, or equivalently as a list of measurements of one thing: a house described by (square metres, bedrooms, age), or a word described by 768 learned numbers. Think of a matrix as a machine that takes in arrows and puts out new arrows: it can stretch, rotate, squash, project, or mix the coordinates. A neural network layer is exactly such a machine (plus a bias and a nonlinearity). Training a network means adjusting the numbers inside the machines until the arrows that come out are useful.
Two questions recur in every section below: how big is it? (norms) and which way does it point? (directions, angles, eigenvectors). Keep both in mind.
Vectors
A vector is an ordered list of real numbers . In ML, is the number of features (for tabular data) or the embedding dimension (for learned representations). We write vectors in bold lowercase and, by convention, treat them as columns.
Two operations define the vector space:
- Addition: . Residual connections in transformers are literally .
- Scalar multiplication: . A gradient-descent step is : scale a vector, then subtract.
Norms: how big is a vector?
A norm measures length. The three you will meet constantly:
For : , , .
Where they appear: the L2 norm of the weights is the L2 regularization (weight decay) penalty; the L1 norm encourages sparse weights (Lasso); gradient clipping rescales the gradient whenever its L2 norm exceeds a threshold, a standard safeguard in LLM training; and the L∞ norm bounds the perturbation size in adversarial-robustness work. For matrices, the Frobenius norm is the L2 norm of all entries flattened.
The dot product and cosine similarity
The dot product (inner product) of two vectors of the same length is
where is the angle between them. The second equality is the geometric meaning: the dot product is large when the vectors are long and point the same way, zero when they are perpendicular (orthogonal), and negative when they point in opposite directions.
Dividing out the lengths leaves pure direction, the cosine similarity:
Worked example. Let and .
- Dot product: .
- Lengths: , .
- Cosine similarity: , an angle of about .
Where it appears. Embedding models map text, images or audio to vectors so that semantically similar inputs land close together in direction. Retrieval-augmented generation (RAG) ranks documents by the cosine similarity between the query embedding and each document embedding. If the embeddings are L2-normalized (scaled to length 1), cosine similarity equals the plain dot product, which is why vector databases often store normalized vectors and use fast inner-product search. Attention in transformers scores how much token should attend to token using a dot product between a query and a key vector, divided by so the scores' variance does not grow with the key dimension .
Matrices as transformations
A matrix has rows and columns. Multiplying it by a vector gives with
Two equivalent readings, both useful:
- Row view: each output is the dot product of row of with . A linear layer with output neurons computes dot products, one per neuron, each measuring how well the input matches that neuron's weight pattern.
- Column view: is a weighted sum of the columns of , with weights . The set of all such outputs is the column space of ; its dimension is the rank.
A matrix is a linear map: . Rotations, scalings, reflections and projections are all linear. Stacking linear maps gives another linear map (), which is precisely why neural networks need nonlinear activation functions between layers: without them, a 100-layer network would collapse to a single matrix.
Matrix multiplication and the shape rule
The product of and is with
The inner dimensions must match ( and ), and the result takes the outer dimensions (). Matrix multiplication is associative, , but generally not commutative: , and often only one of them is even defined. Computing costs about floating-point operations (one multiply and one add per term), which is where the "2 × parameters FLOPs per token" rule of thumb for transformers comes from.
Worked example.
Shapes: .
Batch × features × weights
In practice, data arrives as a design matrix : one row per example in the batch, one column per feature. A linear (fully connected) layer with weight matrix and bias computes
Read it as "each of the rows gets transformed independently by the same ." For a transformer MLP block with sequences of tokens and model width , the activations are , the up-projection weight is , and the output is : the leading dimensions just ride along. Note that PyTorch's nn.Linear stores its weight as and computes x @ W.T + b; the maths is identical, only the storage convention differs.
The single most useful debugging habit in deep learning is writing the shape of every tensor next to every line of code.
Transpose
The transpose swaps rows and columns: , so an matrix becomes . Key identity: (order reverses). Transposes are everywhere in backpropagation: if , the gradient of the loss with respect to is and with respect to is . You will derive this in the calculus lesson; here, notice that the transposes are exactly what make the shapes line up. A matrix with is symmetric; covariance matrices and Hessians are symmetric.
Identity and inverse
The identity has ones on the diagonal and zeros elsewhere; . A square matrix is invertible if there is a matrix with . This exists exactly when the determinant , equivalently when has full rank, equivalently when no non-zero vector is squashed to zero.
For a matrix,
Worked example. has , so . Check: .
Where it appears: the closed-form least-squares solution for linear regression is (the normal equations). In code you almost never form an inverse explicitly; you call a solver (np.linalg.solve, np.linalg.lstsq), which is faster and far more numerically stable. When is nearly singular (highly correlated features), adding before solving is ridge regression, a direct link between linear algebra and regularization.
Eigenvalues and eigenvectors
Most vectors change direction when a matrix acts on them. An eigenvector of a square matrix is a non-zero vector that only gets scaled:
where the scalar is the eigenvalue. Eigenvectors are the matrix's "natural axes"; eigenvalues say how much it stretches along each.
Worked example. . Eigenvalues solve :
For : gives , so . Check: . For : . So stretches the diagonal direction by 3 and leaves the anti-diagonal alone.
Symmetric matrices are especially well behaved (the spectral theorem): their eigenvalues are real and their eigenvectors can be chosen orthonormal, so with orthogonal and diagonal.
Where eigenvalues appear in AI:
- PCA: the eigenvectors of the data's covariance matrix are the directions of greatest variance; the eigenvalues are those variances.
- Optimization: the eigenvalues of the Hessian (the matrix of second derivatives) describe the curvature of the loss. A large ratio between the largest and smallest eigenvalue (a high condition number) means a long narrow valley where plain gradient descent zig-zags; this motivates momentum and Adam (lesson 5).
- Recurrent networks: repeatedly multiplying by the same matrix makes components along eigenvectors with explode and those with vanish, the classic exploding/vanishing gradient problem.
Singular value decomposition (SVD)
Eigendecomposition only works for (some) square matrices. The SVD works for every matrix :
where and are orthogonal (their columns are orthonormal) and is with non-negative singular values on its diagonal. Geometrically: every linear map is a rotation (possibly with a reflection) by , then an axis-aligned stretch by , then another rotation (or reflection) by . The number of non-zero singular values is the rank.
Worked check: for , has eigenvalues and , so the singular values are and . (In general the singular values of are the square roots of the eigenvalues of .)
Low-rank approximation
Keep only the top singular values and vectors:
The Eckart–Young theorem says is the best rank- approximation of in Frobenius (and spectral) norm. Storing costs numbers instead of .
PCA through the SVD
Principal component analysis centers the data matrix (subtract each column's mean to get ), takes the SVD , and projects onto the first right singular vectors: . The variance explained by component is . This is mathematically the same as the eigendecomposition of the covariance matrix , but numerically better. PCA is used for visualizing embeddings, compressing features, and whitening inputs.
LoRA: low rank in fine-tuning
LoRA (Hu et al., 2021) freezes a pretrained weight matrix and learns an update in factored form:
The product has rank at most . For a projection, a full update has parameters; with , LoRA trains , a factor of 256 fewer for that matrix. The bet, which the paper supports empirically, is that the change needed to adapt a model to a task lives in a low-dimensional subspace. The authors report that for GPT-3 175B, LoRA cut trainable parameters by 10,000 times and GPU memory by 3 times compared with full fine-tuning with Adam. Since is initialized to zero, at the start, so training begins exactly at the pretrained model.
Tensors and broadcasting
A tensor, in deep-learning usage, is simply an -dimensional array (this is looser than the physics/maths definition). Scalars are rank-0, vectors rank-1, matrices rank-2. Typical shapes: an image batch in PyTorch is ; token activations are ; attention scores are for heads.
Broadcasting lets you combine arrays of different shapes without copying data. NumPy and PyTorch use the same rule: align shapes from the rightmost dimension; two dimensions are compatible if they are equal or one of them is 1 (a missing dimension counts as 1); size-1 dimensions are virtually stretched.
| Shape A | Shape B | Result | Typical use |
|---|---|---|---|
| (32, 10) | (10,) | (32, 10) | add bias to every row |
| (4, 1) | (1, 3) | (4, 3) | outer-product-style grid |
| (B, T, d) | (d,) | (B, T, d) | LayerNorm scale/shift |
| (B, h, T, T) | (T, T) | (B, h, T, T) | add causal mask |
| (32, 10) | (32,) | error | trailing dims 10 vs 32 |
The last row is the classic bug: to subtract a per-row value you need shape , e.g. x - x.max(axis=1, keepdims=True).
Code: the whole lesson in NumPy
import numpy as np
# Cosine similarity between "embeddings"
a, b = np.array([1., 2., 2.]), np.array([2., 1., 2.])
cos = a @ b / (np.linalg.norm(a) * np.linalg.norm(b))
print(round(cos, 3)) # 0.889
# Batch x features @ weights
rng = np.random.default_rng(0)
X = rng.normal(size=(32, 768)) # 32 examples, 768 features
W = rng.normal(size=(768, 3072)) * 0.02
bias = np.zeros(3072)
Y = X @ W + bias # bias broadcasts over rows
print(Y.shape) # (32, 3072)
# Solve instead of invert
A = np.array([[2., 1.], [1., 1.]])
print(np.linalg.solve(A, np.array([3., 2.]))) # [1. 1.]
# Eigen-decomposition of a symmetric matrix
vals, vecs = np.linalg.eigh(np.array([[2., 1.], [1., 2.]]))
print(vals) # [1. 3.]
# PCA via SVD on correlated 2-D data
Z = rng.normal(size=(500, 2)) @ np.array([[3., 0.], [2.5, 0.5]])
Zc = Z - Z.mean(axis=0)
U, S, Vt = np.linalg.svd(Zc, full_matrices=False)
explained = S**2 / (S**2).sum()
print(explained.round(3)) # first component dominates
proj = Zc @ Vt[:1].T # (500, 1) projection
# Low-rank (LoRA-style) parameter count
d = k = 4096; r = 8
print(d * k, r * (d + k)) # 16777216 65536
np.linalg.eigh is the routine for symmetric matrices (it returns eigenvalues in ascending order); np.linalg.svd with full_matrices=False returns the compact SVD. The same operations exist in PyTorch as torch.linalg.eigh, torch.linalg.svd and the @ operator, and broadcasting rules are identical.
Common pitfalls
- Shape mismatches hidden by broadcasting. Subtracting a vector from a column produces a matrix with no error. Assert shapes in critical code.
- Confusing row and column conventions. Textbooks often write with column vectors; code uses row-major batches or PyTorch's
x @ W.T. Translate deliberately. - Computing explicit inverses.
inv(A) @ bis slower and less accurate thansolve(A, b); for least squares uselstsq. - Cosine on unnormalized vectors with dot-product indexes. If your vector index uses inner product but your vectors are not unit length, longer vectors win regardless of direction.
- Forgetting to center before PCA. Without centering, the first "component" mostly points at the mean.
- Elementwise vs matrix product.
*is elementwise in NumPy and PyTorch;@is matrix multiplication.
In practice
- Write shapes as comments; use
einsum(np.einsum('btd,df->btf', X, W)) when an operation involves more than two axes, because the subscripts document themselves. - Normalize embeddings once at indexing time and use inner-product search for cosine similarity.
- Use
float32or lower-precision types for large matrix products, but accumulate reductions in higher precision; mixed precision is standard on GPUs. - Reach for SVD when you need to understand or compress a matrix: checking effective rank of a weight update, PCA plots of embeddings, or choosing a LoRA rank.
- When a model diverges, look at norms: the L2 norm of gradients and of activations per layer is the first diagnostic professionals check, and gradient clipping by global norm is a default safety net.
Key takeaways
- A neural-network layer is a matrix multiplication plus a bias and a nonlinearity; without the nonlinearity, stacked layers collapse into one matrix.
- Cosine similarity compares direction only; on L2-normalized vectors it equals the dot product, which is why embedding indexes normalize.
- In a product of an (m×k) and a (k×n) matrix the inner dimensions must match, the result is (m×n), and the cost is about 2mkn FLOPs.
- Eigenvectors are a matrix's natural axes; eigenvalues of covariance matrices give PCA variances and eigenvalues of the Hessian give loss curvature.
- The SVD exists for every matrix and its truncation is the best low-rank approximation, the idea behind PCA and LoRA.
- Broadcasting aligns shapes from the right and stretches size-1 axes; it is powerful and also a common source of silent bugs.
Terms in this lesson
- Vector
- An ordered list of n real numbers, treated as a point or arrow in n-dimensional space.
- Norm
- A function that assigns a non-negative length to a vector, such as the L1, L2 or L-infinity norm.
- Dot product
- The sum of elementwise products of two equal-length vectors, equal to the product of their lengths and the cosine of the angle between them.
- Cosine similarity
- The dot product of two vectors divided by the product of their L2 norms, measuring alignment of direction on a scale from -1 to 1.
- Rank
- The number of linearly independent columns (equivalently rows) of a matrix, i.e. the dimension of its column space.
- Eigenvector
- A non-zero vector whose direction is unchanged by a square matrix, which only scales it by the corresponding eigenvalue.
- Singular value decomposition
- The factorization of any matrix as U Σ Vᵀ with orthogonal U and V and non-negative singular values on the diagonal of Σ.
- Broadcasting
- The array-library rule that aligns shapes from the right and virtually stretches size-1 dimensions so arrays of different shapes can be combined elementwise.
- LoRA
- Low-Rank Adaptation, a fine-tuning method that freezes pretrained weights and trains a low-rank update BA added to them.
Check your understanding
- 1.
A batch of activations has shape (16, 128, 512) and a linear layer's weight (in x @ W convention) has shape (512, 2048). What is the output shape?
- 2.
Vectors u = (1, 0, 1) and v = (0, 1, 1). What is their cosine similarity?
- 3.
Your vector database ranks by inner product, but your embeddings are not normalized. What is the likely effect?
- 4.
A LoRA adapter of rank r = 16 is attached to a 4096 × 1024 weight matrix. How many trainable parameters does it add?
- 5.
Which statement about the symmetric matrix [[2, 1], [1, 2]] is correct?
Exercises
Work each problem before opening the solution.
- Exercise 1Core
Compute the L1, L2 and L∞ norms of , then the cosine similarity between and .
Show solution
Norms: ; ; .
Dot product: . .
Cosine similarity .
- Exercise 2Core
A transformer layer receives activations of shape . Its MLP has an up-projection and a down-projection , written in the
x @ Wconvention. (a) Give the shape after each projection. (b) Estimate the FLOPs of the two matrix multiplications for the whole batch, ignoring the activation function and biases.Show solution
(a) After up-projection: . After down-projection: .
(b) The batch contains token rows. Each product costs FLOPs. Up: . Down: the same, . Total FLOPs. Equivalently, (parameters ) 2048 tokens .
- Exercise 3Core
Find the eigenvalues and unit eigenvectors of . Is invertible? What is its rank?
Show solution
, so , .
For : gives , so . Check: .
For : , so . Check: .
A zero eigenvalue means , so is not invertible. Its rank is 1 (the second row is half the first).
- Exercise 4Challenge
Write a NumPy function
low_rank(W, r)that returns the best rank- approximation of a matrix and the relative Frobenius error . Test it on a matrix that is a rank-4 matrix plus small noise, for . Explain how this relates to choosing a LoRA rank.Show solution
import numpy as np def low_rank(W, r): U, S, Vt = np.linalg.svd(W, full_matrices=False) Wr = (U[:, :r] * S[:r]) @ Vt[:r] err = np.linalg.norm(W - Wr) / np.linalg.norm(W) return Wr, err rng = np.random.default_rng(0) W = rng.normal(size=(256, 4)) @ rng.normal(size=(4, 256)) W += 0.01 * rng.normal(size=W.shape) for r in (1, 4, 16): _, e = low_rank(W, r) print(r, round(e, 4))U[:, :r] * S[:r]scales each kept left singular vector by its singular value (broadcasting over rows). By Eckart–Young, the truncated SVD is optimal. Expected behaviour: a large error at (about 84% here, because three strong directions are missing), a tiny error at (only the noise remains, about 0.5%), and only marginal further improvement at , because the extra components fit noise. The relative error also equals .Link to LoRA: if the weight update a task needs has a rapidly decaying singular-value spectrum, a small rank captures it and larger ranks add parameters without much benefit. Practitioners therefore sweep a few ranks (e.g. 4, 8, 16, 64) and pick the smallest that matches validation quality.
- Exercise 5Challenge
Show that for a symmetric matrix the eigenvectors belonging to two different eigenvalues are orthogonal. Then explain why this makes PCA components uncorrelated.
Show solution
Let , , with . Then . Hence , and since , .
PCA: the covariance is symmetric, so with orthonormal . The projected data has covariance , which is diagonal: the components have zero covariance with each other, and their variances are the eigenvalues.
Further reading
- bookMarc Peter Deisenroth, A. Aldo Faisal, Cheng Soon Ong, Mathematics for Machine Learning (2020), chapters 2-4
- bookIan Goodfellow, Yoshua Bengio, Aaron Courville, Deep Learning (2016), chapter 2: Linear Algebra
- bookAston Zhang, Zachary C. Lipton, Mu Li, Alexander J. Smola, Dive into Deep Learning, Appendix 22.1: Geometry and Linear Algebraic Operations
- bookDive into Deep Learning, Appendix 22.2: Eigendecompositions
- paperEdward J. Hu et al., LoRA: Low-Rank Adaptation of Large Language Models (2021)
Go further
Sources
Fact-checked and last reviewed 2026-11-05