PROMETHEUS OS
NATIVE AI OPERATING SYSTEM
[INIT] Prometheus OS loading...
[CORE] Neural interface initializing...
[AI] LLM router connecting...
[MEM] Memory graph loading...
[AGT] Agent chains on standby...
[SEC] Security protocols active...
[NET] Provider mesh established...
[UI] Workspace rendering...
[DONE] All systems nominal.
Welcome to PROMETHEUS OS.
READY · OFFLINE · SECURE · PRIVATE
🎨 THEME ENGINE
PROMETHEUS OS · 23 THEMES
Trial version: Dark + Light themes only.
Full theme access with PROMETHEUS OS license.
Dark theme active
Experience all 23 themes in PROMETHEUS OS

Linear Algebra for AI

Intermediate 20 min· 5 exercises

Work fluently with the vectors, matrices and tensors inside every model: compute similarities between embeddings, predict the shapes of layer outputs, and use eigen- and singular-value decompositions to understand PCA and low-rank fine-tuning (LoRA).

By the end of this lesson you will be able to
  • 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 x∈Rn\mathbf{x} \in \mathbb{R}^n is an ordered list of nn real numbers x=(x1,x2,…,xn)\mathbf{x} = (x_1, x_2, \dots, x_n). In ML, nn 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: (x+y)i=xi+yi(\mathbf{x} + \mathbf{y})_i = x_i + y_i. Residual connections in transformers are literally x+f(x)\mathbf{x} + f(\mathbf{x}).
  • Scalar multiplication: (c x)i=c xi(c\,\mathbf{x})_i = c\,x_i. A gradient-descent step is w−η∇L\mathbf{w} - \eta \nabla L: scale a vector, then subtract.

Norms: how big is a vector?

A norm measures length. The three you will meet constantly:

∥x∥1=∑i=1n∣xi∣,∥x∥2=∑i=1nxi2,∥x∥∞=max⁡i∣xi∣.\|\mathbf{x}\|_1 = \sum_{i=1}^n |x_i|, \qquad \|\mathbf{x}\|_2 = \sqrt{\sum_{i=1}^n x_i^2}, \qquad \|\mathbf{x}\|_\infty = \max_i |x_i|.

For x=(3,−4)\mathbf{x} = (3, -4): ∥x∥1=7\|\mathbf{x}\|_1 = 7, ∥x∥2=9+16=5\|\mathbf{x}\|_2 = \sqrt{9 + 16} = 5, ∥x∥∞=4\|\mathbf{x}\|_\infty = 4.

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 ∥A∥F=∑i,jAij2\|A\|_F = \sqrt{\sum_{i,j} A_{ij}^2} 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

x⋅y=x⊤y=∑i=1nxiyi=∥x∥2 ∥y∥2cos⁡θ,\mathbf{x} \cdot \mathbf{y} = \mathbf{x}^\top \mathbf{y} = \sum_{i=1}^n x_i y_i = \|\mathbf{x}\|_2 \, \|\mathbf{y}\|_2 \cos\theta,

where θ\theta 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:

cos⁡(x,y)=x⊤y∥x∥2 ∥y∥2∈[−1,1].\cos(\mathbf{x}, \mathbf{y}) = \frac{\mathbf{x}^\top \mathbf{y}}{\|\mathbf{x}\|_2 \, \|\mathbf{y}\|_2} \in [-1, 1].

Worked example. Let a=(1,2,2)\mathbf{a} = (1, 2, 2) and b=(2,1,2)\mathbf{b} = (2, 1, 2).

  1. Dot product: 1⋅2+2⋅1+2⋅2=2+2+4=81\cdot 2 + 2 \cdot 1 + 2 \cdot 2 = 2 + 2 + 4 = 8.
  2. Lengths: ∥a∥2=1+4+4=3\|\mathbf{a}\|_2 = \sqrt{1 + 4 + 4} = 3, ∥b∥2=4+1+4=3\|\mathbf{b}\|_2 = \sqrt{4 + 1 + 4} = 3.
  3. Cosine similarity: 8/(3⋅3)=8/9≈0.8898 / (3 \cdot 3) = 8/9 \approx 0.889, an angle of about 27°27°.

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 ii should attend to token jj using a dot product qi⊤kj\mathbf{q}_i^\top \mathbf{k}_j between a query and a key vector, divided by dk\sqrt{d_k} so the scores' variance does not grow with the key dimension dkd_k.

Matrices as transformations

A matrix A∈Rm×nA \in \mathbb{R}^{m \times n} has mm rows and nn columns. Multiplying it by a vector x∈Rn\mathbf{x} \in \mathbb{R}^n gives y=Ax∈Rm\mathbf{y} = A\mathbf{x} \in \mathbb{R}^m with

yi=∑j=1nAijxj.y_i = \sum_{j=1}^n A_{ij} x_j .

Two equivalent readings, both useful:

  • Row view: each output yiy_i is the dot product of row ii of AA with x\mathbf{x}. A linear layer with mm output neurons computes mm dot products, one per neuron, each measuring how well the input matches that neuron's weight pattern.
  • Column view: y\mathbf{y} is a weighted sum of the columns of AA, with weights x1,…,xnx_1, \dots, x_n. The set of all such outputs is the column space of AA; its dimension is the rank.

A matrix is a linear map: A(cx+y)=cAx+AyA(c\mathbf{x} + \mathbf{y}) = cA\mathbf{x} + A\mathbf{y}. Rotations, scalings, reflections and projections are all linear. Stacking linear maps gives another linear map (B(Ax)=(BA)xB(A\mathbf{x}) = (BA)\mathbf{x}), 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 C=ABC = AB of A∈Rm×kA \in \mathbb{R}^{m \times k} and B∈Rk×nB \in \mathbb{R}^{k \times n} is C∈Rm×nC \in \mathbb{R}^{m \times n} with

Cij=∑p=1kAipBpj.C_{ij} = \sum_{p=1}^{k} A_{ip} B_{pj}.

The inner dimensions must match (kk and kk), and the result takes the outer dimensions (m×nm \times n). Matrix multiplication is associative, (AB)C=A(BC)(AB)C = A(BC), but generally not commutative: AB≠BAAB \ne BA, and often only one of them is even defined. Computing ABAB costs about 2mkn2mkn 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.

X=(1234),W=(10−1210),XW=(1⋅1+2⋅21⋅0+2⋅11⋅(−1)+2⋅03⋅1+4⋅23⋅0+4⋅13⋅(−1)+4⋅0)=(52−1114−3).X = \begin{pmatrix} 1 & 2 \\ 3 & 4 \end{pmatrix}, \quad W = \begin{pmatrix} 1 & 0 & -1 \\ 2 & 1 & 0 \end{pmatrix}, \quad XW = \begin{pmatrix} 1\cdot1 + 2\cdot2 & 1\cdot0 + 2\cdot1 & 1\cdot(-1) + 2\cdot0 \\ 3\cdot1 + 4\cdot2 & 3\cdot0 + 4\cdot1 & 3\cdot(-1)+4\cdot0 \end{pmatrix} = \begin{pmatrix} 5 & 2 & -1 \\ 11 & 4 & -3 \end{pmatrix}.

Shapes: (2×2)(2×3)=(2×3)(2 \times 2)(2 \times 3) = (2 \times 3).

Batch × features × weights

In practice, data arrives as a design matrix X∈RB×dinX \in \mathbb{R}^{B \times d_{\text{in}}}: one row per example in the batch, one column per feature. A linear (fully connected) layer with weight matrix W∈Rdin×doutW \in \mathbb{R}^{d_{\text{in}} \times d_{\text{out}}} and bias b∈Rdout\mathbf{b} \in \mathbb{R}^{d_{\text{out}}} computes

Y=XW+b,Y∈RB×dout.Y = XW + \mathbf{b}, \qquad Y \in \mathbb{R}^{B \times d_{\text{out}}}.

Read it as "each of the BB rows gets transformed independently by the same WW." For a transformer MLP block with B=32B = 32 sequences of T=512T = 512 tokens and model width d=768d = 768, the activations are (32,512,768)(32, 512, 768), the up-projection weight is (768,3072)(768, 3072), and the output is (32,512,3072)(32, 512, 3072): the leading dimensions just ride along. Note that PyTorch's nn.Linear stores its weight as (dout,din)(d_{\text{out}}, d_{\text{in}}) 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 A⊤A^\top swaps rows and columns: (A⊤)ij=Aji(A^\top)_{ij} = A_{ji}, so an m×nm \times n matrix becomes n×mn \times m. Key identity: (AB)⊤=B⊤A⊤(AB)^\top = B^\top A^\top (order reverses). Transposes are everywhere in backpropagation: if Y=XWY = XW, the gradient of the loss with respect to XX is ∂L∂YW⊤\frac{\partial L}{\partial Y} W^\top and with respect to WW is X⊤∂L∂YX^\top \frac{\partial L}{\partial Y}. You will derive this in the calculus lesson; here, notice that the transposes are exactly what make the shapes line up. A matrix with A=A⊤A = A^\top is symmetric; covariance matrices and Hessians are symmetric.

Identity and inverse

The identity InI_n has ones on the diagonal and zeros elsewhere; Ix=xI\mathbf{x} = \mathbf{x}. A square matrix AA is invertible if there is a matrix A−1A^{-1} with A−1A=AA−1=IA^{-1}A = AA^{-1} = I. This exists exactly when the determinant det⁡A≠0\det A \ne 0, equivalently when AA has full rank, equivalently when no non-zero vector is squashed to zero.

For a 2×22 \times 2 matrix,

(abcd)−1=1ad−bc(d−b−ca).\begin{pmatrix} a & b \\ c & d \end{pmatrix}^{-1} = \frac{1}{ad - bc} \begin{pmatrix} d & -b \\ -c & a \end{pmatrix}.

Worked example. A=(2111)A = \begin{pmatrix} 2 & 1 \\ 1 & 1 \end{pmatrix} has det⁡A=2⋅1−1⋅1=1\det A = 2\cdot1 - 1\cdot1 = 1, so A−1=(1−1−12)A^{-1} = \begin{pmatrix} 1 & -1 \\ -1 & 2 \end{pmatrix}. Check: (2111)(1−1−12)=(2−1−2+21−1−1+2)=I\begin{pmatrix} 2 & 1 \\ 1 & 1 \end{pmatrix}\begin{pmatrix} 1 & -1 \\ -1 & 2 \end{pmatrix} = \begin{pmatrix} 2-1 & -2+2 \\ 1-1 & -1+2 \end{pmatrix} = I.

Where it appears: the closed-form least-squares solution for linear regression is w=(X⊤X)−1X⊤y\mathbf{w} = (X^\top X)^{-1} X^\top \mathbf{y} (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 X⊤XX^\top X is nearly singular (highly correlated features), adding λI\lambda I 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 AA is a non-zero vector that only gets scaled:

Av=λv,A\mathbf{v} = \lambda \mathbf{v},

where the scalar λ\lambda is the eigenvalue. Eigenvectors are the matrix's "natural axes"; eigenvalues say how much it stretches along each.

Worked example. S=(2112)S = \begin{pmatrix} 2 & 1 \\ 1 & 2 \end{pmatrix}. Eigenvalues solve det⁡(S−λI)=0\det(S - \lambda I) = 0:

(2−λ)2−1=0  ⇒  2−λ=±1  ⇒  λ1=3,  λ2=1.(2-\lambda)^2 - 1 = 0 \;\Rightarrow\; 2 - \lambda = \pm 1 \;\Rightarrow\; \lambda_1 = 3,\; \lambda_2 = 1.

For λ1=3\lambda_1 = 3: (S−3I)v=0(S - 3I)\mathbf{v} = \mathbf{0} gives −v1+v2=0-v_1 + v_2 = 0, so v1=12(1,1)\mathbf{v}_1 = \tfrac{1}{\sqrt2}(1, 1). Check: S(1,1)⊤=(3,3)⊤=3 (1,1)⊤S(1,1)^\top = (3, 3)^\top = 3\,(1,1)^\top. For λ2=1\lambda_2 = 1: v2=12(1,−1)\mathbf{v}_2 = \tfrac{1}{\sqrt2}(1, -1). So SS 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 S=QΛQ⊤S = Q \Lambda Q^\top with QQ orthogonal and Λ\Lambda 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 ∣λ∣>1|\lambda| > 1 explode and those with ∣λ∣<1|\lambda| < 1 vanish, the classic exploding/vanishing gradient problem.

Singular value decomposition (SVD)

Eigendecomposition only works for (some) square matrices. The SVD works for every matrix A∈Rm×nA \in \mathbb{R}^{m \times n}:

A=UΣV⊤,A = U \Sigma V^\top,

where U∈Rm×mU \in \mathbb{R}^{m \times m} and V∈Rn×nV \in \mathbb{R}^{n \times n} are orthogonal (their columns are orthonormal) and Σ\Sigma is m×nm \times n with non-negative singular values σ1≥σ2≥⋯≥0\sigma_1 \ge \sigma_2 \ge \dots \ge 0 on its diagonal. Geometrically: every linear map is a rotation (possibly with a reflection) by V⊤V^\top, then an axis-aligned stretch by Σ\Sigma, then another rotation (or reflection) by UU. The number of non-zero singular values is the rank.

Worked check: for M=(3045)M = \begin{pmatrix} 3 & 0 \\ 4 & 5 \end{pmatrix}, M⊤M=(25202025)M^\top M = \begin{pmatrix} 25 & 20 \\ 20 & 25 \end{pmatrix} has eigenvalues 4545 and 55, so the singular values are 45≈6.708\sqrt{45} \approx 6.708 and 5≈2.236\sqrt{5} \approx 2.236. (In general the singular values of AA are the square roots of the eigenvalues of A⊤AA^\top A.)

Low-rank approximation

Keep only the top rr singular values and vectors:

Ar=∑i=1rσiuivi⊤.A_r = \sum_{i=1}^{r} \sigma_i \mathbf{u}_i \mathbf{v}_i^\top .

The Eckart–Young theorem says ArA_r is the best rank-rr approximation of AA in Frobenius (and spectral) norm. Storing ArA_r costs r(m+n)r(m + n) numbers instead of mnmn.

PCA through the SVD

Principal component analysis centers the data matrix X∈RN×dX \in \mathbb{R}^{N \times d} (subtract each column's mean to get XcX_c), takes the SVD Xc=UΣV⊤X_c = U\Sigma V^\top, and projects onto the first kk right singular vectors: Z=XcV:,1:kZ = X_c V_{:, 1:k}. The variance explained by component ii is σi2/(N−1)\sigma_i^2 / (N - 1). This is mathematically the same as the eigendecomposition of the covariance matrix 1N−1Xc⊤Xc\frac{1}{N-1} X_c^\top X_c, 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 W0∈Rd×kW_0 \in \mathbb{R}^{d \times k} and learns an update in factored form:

W=W0+ΔW=W0+BA,B∈Rd×r,  A∈Rr×k,  r≪min⁡(d,k).W = W_0 + \Delta W = W_0 + BA, \qquad B \in \mathbb{R}^{d \times r},\; A \in \mathbb{R}^{r \times k},\; r \ll \min(d, k).

The product BABA has rank at most rr. For a 4096×40964096 \times 4096 projection, a full update has 16,777,21616{,}777{,}216 parameters; with r=8r = 8, LoRA trains 8×(4096+4096)=65,5368 \times (4096 + 4096) = 65{,}536, 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 BB is initialized to zero, ΔW=0\Delta W = 0 at the start, so training begins exactly at the pretrained model.

Tensors and broadcasting

A tensor, in deep-learning usage, is simply an nn-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 (B,C,H,W)(B, C, H, W); token activations are (B,T,d)(B, T, d); attention scores are (B,h,T,T)(B, h, T, T) for hh 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 AShape BResultTypical 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,)errortrailing dims 10 vs 32

The last row is the classic bug: to subtract a per-row value you need shape (32,1)(32, 1), 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 (32,)(32,) vector from a (32,1)(32, 1) column produces a (32,32)(32, 32) matrix with no error. Assert shapes in critical code.
  • Confusing row and column conventions. Textbooks often write WxW\mathbf{x} with column vectors; code uses row-major batches XWXW or PyTorch's x @ W.T. Translate deliberately.
  • Computing explicit inverses. inv(A) @ b is slower and less accurate than solve(A, b); for least squares use lstsq.
  • 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 float32 or 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. 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. 2.

    Vectors u = (1, 0, 1) and v = (0, 1, 1). What is their cosine similarity?

  3. 3.

    Your vector database ranks by inner product, but your embeddings are not normalized. What is the likely effect?

  4. 4.

    A LoRA adapter of rank r = 16 is attached to a 4096 × 1024 weight matrix. How many trainable parameters does it add?

  5. 5.

    Which statement about the symmetric matrix [[2, 1], [1, 2]] is correct?

Exercises

Work each problem before opening the solution.

  1. Exercise 1Core

    Compute the L1, L2 and L∞ norms of x=(2,−1,−2,4)\mathbf{x} = (2, -1, -2, 4), then the cosine similarity between x\mathbf{x} and y=(1,1,0,2)\mathbf{y} = (1, 1, 0, 2).

    Show solution

    Norms: ∥x∥1=2+1+2+4=9\|\mathbf{x}\|_1 = 2 + 1 + 2 + 4 = 9; ∥x∥2=4+1+4+16=5\|\mathbf{x}\|_2 = \sqrt{4 + 1 + 4 + 16} = 5; ∥x∥∞=4\|\mathbf{x}\|_\infty = 4.

    Dot product: 2⋅1+(−1)⋅1+(−2)⋅0+4⋅2=2−1+0+8=92\cdot1 + (-1)\cdot1 + (-2)\cdot0 + 4\cdot2 = 2 - 1 + 0 + 8 = 9. ∥y∥2=1+1+0+4=6≈2.449\|\mathbf{y}\|_2 = \sqrt{1 + 1 + 0 + 4} = \sqrt6 \approx 2.449.

    Cosine similarity =9/(5⋅2.449)≈0.735= 9 / (5 \cdot 2.449) \approx 0.735.

  2. Exercise 2Core

    A transformer layer receives activations of shape (B,T,d)=(8,256,1024)(B, T, d) = (8, 256, 1024). Its MLP has an up-projection (1024,4096)(1024, 4096) and a down-projection (4096,1024)(4096, 1024), written in the x @ W convention. (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: (8,256,4096)(8, 256, 4096). After down-projection: (8,256,1024)(8, 256, 1024).

    (b) The batch contains 8×256=20488 \times 256 = 2048 token rows. Each product costs 2⋅m⋅k⋅n2 \cdot m \cdot k \cdot n FLOPs. Up: 2⋅2048⋅1024⋅4096≈1.72×10102 \cdot 2048 \cdot 1024 \cdot 4096 \approx 1.72 \times 10^{10}. Down: the same, 1.72×10101.72 \times 10^{10}. Total ≈3.44×1010\approx 3.44 \times 10^{10} FLOPs. Equivalently, 2×2 \times (parameters =2⋅1024⋅4096=8,388,608= 2 \cdot 1024 \cdot 4096 = 8{,}388{,}608) ×\times 2048 tokens ≈3.44×1010\approx 3.44 \times 10^{10}.

  3. Exercise 3Core

    Find the eigenvalues and unit eigenvectors of A=(4221)A = \begin{pmatrix} 4 & 2 \\ 2 & 1 \end{pmatrix}. Is AA invertible? What is its rank?

    Show solution

    det⁡(A−λI)=(4−λ)(1−λ)−4=λ2−5λ=λ(λ−5)\det(A - \lambda I) = (4-\lambda)(1-\lambda) - 4 = \lambda^2 - 5\lambda = \lambda(\lambda - 5), so λ1=5\lambda_1 = 5, λ2=0\lambda_2 = 0.

    For λ=5\lambda = 5: (A−5I)v=0(A - 5I)\mathbf{v} = 0 gives −v1+2v2=0-v_1 + 2v_2 = 0, so v1=(2,1)/5\mathbf{v}_1 = (2, 1)/\sqrt5. Check: A(2,1)=(10,5)=5(2,1)A(2,1) = (10, 5) = 5(2,1).

    For λ=0\lambda = 0: 4v1+2v2=04v_1 + 2v_2 = 0, so v2=(1,−2)/5\mathbf{v}_2 = (1, -2)/\sqrt5. Check: A(1,−2)=(0,0)A(1,-2) = (0, 0).

    A zero eigenvalue means det⁡A=0\det A = 0, so AA is not invertible. Its rank is 1 (the second row is half the first).

  4. Exercise 4Challenge

    Write a NumPy function low_rank(W, r) that returns the best rank-rr approximation of a matrix and the relative Frobenius error ∥W−Wr∥F/∥W∥F\|W - W_r\|_F / \|W\|_F. Test it on a 256×256256 \times 256 matrix that is a rank-4 matrix plus small noise, for r=1,4,16r = 1, 4, 16. 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 r=1r = 1 (about 84% here, because three strong directions are missing), a tiny error at r=4r = 4 (only the noise remains, about 0.5%), and only marginal further improvement at r=16r = 16, because the extra components fit noise. The relative error also equals ∑i>rσi2/∑iσi2\sqrt{\sum_{i>r} \sigma_i^2 / \sum_i \sigma_i^2}.

    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.

  5. 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 S=S⊤S = S^\top, Su=λuS\mathbf{u} = \lambda\mathbf{u}, Sv=μvS\mathbf{v} = \mu\mathbf{v} with λ≠μ\lambda \ne \mu. Then λ u⊤v=(Su)⊤v=u⊤S⊤v=u⊤Sv=μ u⊤v\lambda\,\mathbf{u}^\top\mathbf{v} = (S\mathbf{u})^\top\mathbf{v} = \mathbf{u}^\top S^\top \mathbf{v} = \mathbf{u}^\top S\mathbf{v} = \mu\,\mathbf{u}^\top\mathbf{v}. Hence (λ−μ) u⊤v=0(\lambda - \mu)\,\mathbf{u}^\top\mathbf{v} = 0, and since λ≠μ\lambda \ne \mu, u⊤v=0\mathbf{u}^\top\mathbf{v} = 0.

    PCA: the covariance C=1N−1Xc⊤XcC = \frac{1}{N-1}X_c^\top X_c is symmetric, so C=QΛQ⊤C = Q\Lambda Q^\top with orthonormal QQ. The projected data Z=XcQZ = X_cQ has covariance 1N−1Z⊤Z=Q⊤CQ=Λ\frac{1}{N-1}Z^\top Z = Q^\top C Q = \Lambda, which is diagonal: the components have zero covariance with each other, and their variances are the eigenvalues.

Further reading

Go further

Sources

Fact-checked and last reviewed 2026-11-05

Stay in the loop
Prometheus OS™ · Native AI OS · Built for the future © 2025 - 2026