Linear algebra · foundations Section 1.1 · item 3 of 29

The dot
product

Multiply matching coordinates, add them up, get one number. That number tells you how much two vectors point the same way — and it is, by a wide margin, the operation your hardware spends most of its time performing.

A bare vector space has no notion of length or angle. The dot product is the extra structure that supplies both. Everything geometric you know about vectors is downstream of it.

Two definitions, one number

a · b = ∑ aibi
a · b = ‖a‖ ‖b‖ cos θ

One is arithmetic and one is geometry. They are provably equal, and moving between them is most of what makes the operation useful.

Drag one past the other

The dashed line drops a perpendicular from b onto a. The amber segment is b's shadow — and the dot product is simply the length of a times the length of that shadow. Swing b past the perpendicular and watch the shadow flip sides and the sign go negative.

a b projection of b onto a
a · b
‖a‖ · ‖b‖
cos θ
angle θ

Try "same angle, longer b". The angle is unchanged but the dot product doubles — because it measures magnitude and alignment together. Section 05 is about when that's what you want and when it isn't.

01Why they agree

Arithmetic and geometry, reconciled

That a sum of coordinate products should equal a product of lengths and a cosine is not obvious. The proof is three lines, and it's worth seeing once because it explains where the cosine comes from.

The derivation

Expand the squared distance between the two tips algebraically:

ab‖² = ‖a‖² − 2(a·b) + ‖b‖²

And write the same quantity by the law of cosines:

ab‖² = ‖a‖² + ‖b‖² − 2‖a‖‖b‖cos θ

Set them equal, cancel, and the geometric form drops out. The cosine was hiding in the algebra the whole time.

The properties that follow

symmetric
a·b = b·a. Order never matters.
bilinear
(ca + d)·b = c(a·b) + d·b. Linear in each argument separately, which is what makes it play well with everything else.
positive definite
a·a ≥ 0, and zero only for the zero vector. This is what lets you define a length from it.
Cauchy–Schwarz
|a·b| ≤ ‖a‖‖b‖, with equality exactly when the vectors are parallel. Equivalently: cosine can never leave [−1, 1].
Cauchy–Schwarz is doing quiet work all over machine learning. It's why cosine similarity is bounded, why correlation coefficients live in [−1, 1], and why the tightest possible bound on how much one vector can explain another is achieved exactly when they're parallel. It's one inequality with an enormous number of downstream consequences.
02Reading it

What the number is telling you

Positive

Angle under 90°. The vectors broadly agree — moving along one takes you partly along the other. The larger the value, the stronger the agreement, though magnitude is mixed in.

Zero

Orthogonal. Exactly perpendicular. Neither vector has any component along the other — knowing one tells you nothing about the other's contribution.

Negative

Angle over 90°. They oppose. Moving along one takes you backwards along the other, which is exactly why the gradient gets a minus sign.

Zero dot product does not mean "unrelated" in the everyday sense. It means no linear relationship along that direction. Two variables can be perfectly deterministically related — y = x² on a symmetric range — and still have zero correlation, which is the statistical version of this exact fact. Orthogonality is a strong statement about linear structure and a weak one about dependence.

The size of the number is not comparable across pairs

A dot product of 40 tells you nothing on its own. It could be two nearly-perpendicular long vectors or two short well-aligned ones. To compare alignments across different pairs you must divide out the magnitudes — which is section 05.

03Projection

The shadow interpretation

The most useful mental image: shine a light perpendicular to a and measure the shadow that b casts along it. The dot product is ‖a‖ times that shadow length. This is what the hero draws.

Scalar projection

compab = a·ba

How far along a the shadow reaches. A signed number — negative if b leans backwards.

Vector projection

projab = a·ba·a a

The shadow as an actual vector, pointing along a. Note the denominator is a·a, not ‖a‖ — a common slip.

Least squares is a projection, literally. Fitting y with a linear model means finding the point in the column space of X closest to y — which is the projection of y onto that subspace. The residual is what's left over, and it is orthogonal to every column of X precisely because that's the defining property of a projection. The normal equations Xᵀ(y − Xβ) = 0 are just that orthogonality written down: every dot product between a feature and the residual is zero.
04Length

Where norms come from

Take the dot product of a vector with itself and every cosine becomes 1. What's left is the squared length.

v · v = ‖v‖²  ⟹  ‖v‖ = √(v·v)

This is Pythagoras

In coordinates, v·v = v₁² + v₂² + … — the sum of squared components. The Euclidean norm is not an arbitrary choice; it's what the dot product forces on you.

The L1 norm, by contrast, does not come from any inner product. That's a genuine structural difference, and part of why L1 regularization behaves so differently from L2.

Normalizing

v̂ = v ⁄ ‖v

Divide by the length and you get a unit vector — pure direction, magnitude discarded. Then â·b̂ is the cosine directly, with no division needed at query time.

This is why embedding libraries normalize by default. If every vector has unit length, dot product and cosine similarity are the same operation — and dot products are what the hardware is fast at. The normalization is a one-time cost that buys you a cheaper metric forever.
05Cosine

Dividing magnitude out

cos θ = a·ba‖‖b  ∈ [−1, 1]

Cosine similarity is the dot product with both magnitudes normalized away. It answers "how aligned?" while the raw dot product answers "how aligned, and how big?". Those give different rankings, and the difference matters in retrieval.

Same query, two metrics

ranked by raw dot product
ranked by cosine
Drag the amber query vector · the two rankings disagree
Raw dot product has a length bias, and it is not always a bug. A long document vector wins on dot product simply for being long, even if its direction is a worse match. If length encodes something real — confidence, popularity, term count — you may want that. If it's an artifact of how the vectors were produced, you don't. Decide deliberately rather than by default.
Vector databases make you choose, and the choice has consequences. Cosine is a proper similarity with well-understood approximate-nearest-neighbour algorithms. Maximum inner product search is genuinely harder, because inner product isn't a metric — it violates the triangle inequality, and a vector isn't even its own nearest neighbour under it. Normalizing your embeddings and using cosine sidesteps the whole problem.
06Orthogonality

The bases worth having

Vectors are orthogonal when their dot product is zero. A basis where every pair is orthogonal and every vector has unit length is orthonormal, and it makes almost every computation easier.

Coordinates become dot products

If {e1en} is orthonormal:
v = ∑ (v·ei) ei

To find the coefficient on any basis vector, just take a dot product. No system to solve, no matrix to invert. In a non-orthogonal basis you'd have to solve n equations simultaneously.

What else it buys

  • Lengths are preserved. An orthogonal matrix Q satisfies QᵀQ = I, so ‖Qx‖ = ‖x‖. Rotations and reflections, nothing else.
  • Inverses are free. Q⁻¹ = Qᵀ. Transposing is nearly costless; inverting is not.
  • Numerically stable. Errors don't amplify, which is why QR decomposition is preferred over normal equations for least squares.
  • Pythagoras generalizes. For orthogonal components, squared lengths simply add — which is what makes variance decompositions work.
Gram–Schmidt turns any basis into an orthonormal one. Take each vector, subtract off its projection onto everything already processed, then normalize what's left. The subtraction is precisely the projection formula from section 03, applied repeatedly. It's also, essentially, what QR decomposition computes — and modern implementations use Householder reflections instead purely because classical Gram–Schmidt loses accuracy in floating point.
07Generalizing

The dot product is one inner product among many

An inner product is any operation taking two vectors to a scalar that is symmetric, linear in each argument, and positive definite. The familiar dot product is the simplest example. Others are more useful in specific settings — and each one defines its own geometry.

Inner productDefinitionWhat "orthogonal" then means
Standard dot∑ aᵢbᵢGeometrically perpendicular
WeightedaᵀWb, W positive definitePerpendicular after rescaling the axes
MahalanobisaᵀΣ⁻¹bPerpendicular after accounting for feature correlations
Function space∫ f(x)g(x) dxFourier basis functions. This is why the transform works
Random variablesE[XY]Uncorrelated, once centred
Matrices (Frobenius)tr(AᵀB)Used throughout optimization on matrix spaces
KernelK(x, y) = ⟨φ(x), φ(y)⟩Perpendicular in a feature space you never construct
Correlation is cosine similarity. Centre two variables by subtracting their means, treat them as vectors, and take the cosine of the angle between them — that number is the Pearson correlation coefficient. Its range of [−1, 1] is Cauchy–Schwarz. Orthogonal centred variables are uncorrelated variables. It's the same operation with different vocabulary attached.
The kernel trick is the last row. An SVM only ever needs inner products between data points, never the points themselves. So you can replace every x·y with a kernel function that equals the inner product in some vastly higher-dimensional space — and compute in that space without ever building a vector in it. An RBF kernel corresponds to an infinite-dimensional feature space, evaluated in constant time.
08Matrices

Matrix multiplication is a grid of dot products

Every entry of a matrix product is one dot product: row i of the left matrix against column j of the right. Click any output cell to see which pair produces it.

A · 3×4
×
B · 4×2
=
C · 3×2
Click a cell in C.

Why the inner dimensions must match

A dot product needs two vectors of the same length. Row i of A has as many entries as A has columns; column j of B has as many as B has rows. Those must agree or the operation is undefined — which is the entire shape rule for (n,k) @ (k,m) → (n,m), and why the shared dimension disappears.

Gram matrices

XXᵀ holds every pairwise dot product between rows; XᵀX holds every pairwise dot product between columns. The second one is the matrix at the heart of the normal equations, and when the features are centred and scaled it is the correlation matrix. One multiplication produces every similarity at once.

This is why GPUs look the way they do. A matrix multiplication is a vast number of independent dot products, each a sequence of multiply-and-accumulate operations with no dependencies between them. That is the single most parallelizable useful computation anyone has found, and tensor cores are silicon built to do nothing else. The dominance of transformers over recurrent models is partly a story about which architecture reduces to more of these.
09Everywhere

Where it actually appears

WhereThe dot productReading
A neuronw · x + bHow strongly the input matches the pattern the weights encode
Linear layerWxOne dot product per output unit — a whole bank of pattern detectors
Logistic regressionσ(w · x)The score before squashing is a single alignment measure
Attentionq · k / √dHow much this query wants that key. Literally a similarity score
Retrievalquery · documentSemantic search is a dot product against millions of vectors
Cosine similaritynormalized dotDeduplication, clustering, recommendation, RAG
Convolutionkernel · patchSlide a filter and take a dot product at every position
PCAx · componentEach principal-component score is a projection
Least squaresXᵀ(y − Xβ) = 0Residual orthogonal to every feature
Correlationcosine of centred variablesSame operation, statistical vocabulary
Kernel methodsK(x, y)An inner product in a space you never build
Directional derivative∇f · dHow fast the loss changes if you step in direction d
Gradient descentstep against ∇fThe steepest direction is the one whose dot product with the gradient is most negative
The last two rows explain the minus sign. The change in loss for a small step d is ∇f · d. To make that as negative as possible with a fixed step length, you pick d pointing exactly opposite the gradient — because a dot product is minimized when the vectors are antiparallel. Gradient descent's defining move is a Cauchy–Schwarz argument.
10In practice

Computation, and the high-dimensional surprise

Almost everything is nearly orthogonal

Pick two random unit vectors in d dimensions. The expected cosine between them is zero, and the spread shrinks like 1/√d. In 768 dimensions the typical cosine between two random directions is about 0.036.

So in a high-dimensional embedding space, "unrelated" is the overwhelming default and a cosine of 0.3 is already a strong signal. Calibrate your intuition to the dimension — a similarity threshold that makes sense at d = 3 is meaningless at d = 768.

That fact is load-bearing, not a curiosity. Because you can pack exponentially many nearly-orthogonal directions into d dimensions — far more than d exactly-orthogonal ones — a model can store many more distinguishable concepts than it has dimensions, accepting slight interference between them. That's the basis of both the Johnson–Lindenstrauss lemma and the superposition hypothesis in interpretability.
Why attention divides by √d. A dot product of two d-dimensional vectors with unit-variance components has variance proportional to d, so raw scores grow with model width and push softmax into saturation where gradients vanish. Dividing by √d holds the scale constant. It's a one-symbol fix for a dot product doing exactly what a sum of d random terms does.
# prefer library calls — they hit BLAS, which is enormously faster than a loop
import numpy as np

np.dot(a, b)          # or a @ b — both dispatch to optimized BLAS
a @ B                 # vector against a whole matrix at once
A @ B                 # and matrices against matrices
np.einsum('ij,jk->ik', A, B)   # when the contraction isn't obvious, say it explicitly

# normalize once, then dot products ARE cosine similarities
E = E / np.linalg.norm(E, axis=1, keepdims=True)   # keepdims! see the tensors explainer
sims = E @ q                                       # all similarities in one call
top  = np.argsort(-sims)[:10]

# the high-dimensional check, empirically
d = 768
X = np.random.randn(2000, d)
X /= np.linalg.norm(X, axis=1, keepdims=True)
print(np.abs(X[:1000] @ X[1000:].T).mean())   # ≈ 0.03, as predicted
Two numerical notes. Summing millions of products in float32 accumulates rounding error, which is why BLAS accumulates in higher precision internally — and why a hand-written loop can disagree with np.dot in the last few digits. And subtracting nearly-equal dot products is a classic catastrophic-cancellation trap: computing variance as E[x²] − E[x]² is the textbook example of getting it wrong.
11Practice

Drills

#Do thisYou'll know it when
1Compute dot products by hand and confirm both formulas agreeThe cosine stops feeling like a separate fact
2Derive the geometric form from the law of cosinesYou could reconstruct it from memory
3Project one vector onto another by hand and verify the residual is orthogonalProjection and least squares are the same idea
4Rank documents by dot product and by cosine on the same query; find a case where they disagreeYou choose a retrieval metric deliberately
5Implement Gram–Schmidt on three vectors in ℝ³Orthonormalization is repeated projection-and-subtract
6Compute Pearson correlation as the cosine of centred vectorsStatistics and geometry collapse into one thing
7Sample random unit vectors in d = 3, 50 and 768; plot the cosine distributionsHigh-dimensional near-orthogonality is something you've measured
8Write matrix multiplication as an explicit triple loop, then time it against @You never write the loop again
9Implement scaled dot-product attention from the equations aloneThe √d makes sense rather than being copied

The one-paragraph summary

The dot product multiplies matching coordinates and sums them, and equals the product of the two lengths with the cosine of the angle between them — the two definitions agree by the law of cosines. Its sign says whether the vectors agree, oppose, or are perpendicular, and its magnitude confounds alignment with length, which is why cosine similarity divides both norms out when you need to compare across pairs. Taking a vector's dot product with itself gives the squared Euclidean norm, so the notion of length itself comes from this operation; projection follows immediately, and least squares is exactly a projection with the residual orthogonal to every feature. Generalizing the definition to any symmetric, bilinear, positive-definite form gives inner products on function spaces, on random variables — where correlation turns out to be cosine similarity of centred variables — and on implicit feature spaces via kernels. Matrix multiplication is a grid of dot products, which is why the inner dimensions must match and why the operation parallelizes so well that hardware is designed around it. And in high dimensions random vectors are almost always nearly orthogonal, which both calibrates what a similarity score means and explains how a model can represent far more concepts than it has dimensions.