import numpy as onp
def angle(v, w):
return onp.arccos(v.dot(w) / (onp.linalg.norm(v) * onp.linalg.norm(w)))
angle(onp.array([0, 1, 2]), onp.array([2, 3, 4]))Dive into Deep Learning · §24.1
The geometry under the algebra
angles, projections, hyperplanes, and how matrices move space.
Motivation
A vector is a list of numbers with a useful geometric interpretation.
These pictures are the foundation for the eigendecomposition and the SVD in the sections that follow.
01
Vectors and their geometry
points, directions, dot products, projection
Vectors
The same array denotes a point (its coordinates) or a direction (an arrow that may start anywhere). Deep learning often uses the direction view.
Reading a vector as a direction makes addition visual: follow one arrow, then the next, tip to tail.
Vectors
Why is the dot product tied to the angle? The two vectors span a plane, so measure \|\mathbf{v}-\mathbf{w}\|^2 two ways and equate.
Algebra gives \|\mathbf{v}\|^2 - 2\,\mathbf{v}\cdot\mathbf{w} + \|\mathbf{w}\|^2; the law of cosines gives \|\mathbf{v}\|^2 + \|\mathbf{w}\|^2 - 2\,\|\mathbf{v}\|\,\|\mathbf{w}\|\cos\theta.
Cancel the common terms: \;\mathbf{v}\cdot\mathbf{w} = \|\mathbf{v}\|\,\|\mathbf{w}\|\cos\theta, so \theta=\arccos\!\big(\mathbf{v}\cdot\mathbf{w}/\|\mathbf{v}\|\,\|\mathbf{w}\|\big).
np.float64(0.4189900840328574)
Vectors
\arccos only accepts inputs in [-1, 1], so we need a guarantee.
Cauchy–Schwarz. \;|\mathbf{v}\cdot\mathbf{w}| \le \|\mathbf{v}\|\,\|\mathbf{w}\|, with equality iff \mathbf{v} and \mathbf{w} are collinear.
The proof needs only one fact: a squared length is never negative, so q(t) = \|\mathbf{v} - t\mathbf{w}\|^2 \ge 0 is a parabola that cannot dip below zero, forcing its discriminant \le 0. Dividing through shows the cosine never leaves [-1, 1], so \theta is always a genuine angle.
Vectors
How much of \mathbf{v} points along \mathbf{w}? Drop \mathbf{v} onto the line through \mathbf{w}:
\operatorname{proj}_{\mathbf{w}}\mathbf{v} = \frac{\mathbf{v}\cdot\mathbf{w}}{\mathbf{w}\cdot\mathbf{w}}\,\mathbf{w}.
The residual \mathbf{r} is at a right angle to the projection direction, so Cauchy–Schwarz states that a leg is no longer than the hypotenuse. Two vectors are orthogonal when \mathbf{v}\cdot\mathbf{w} = 0.
This is least squares in one dimension (the best fit leaves an orthogonal residual); the SVD scales the same idea to any matrix.
Vectors
Stack an orthonormal basis of a subspace S as the columns of \mathbf{Q} (so \mathbf{Q}^\top\mathbf{Q} = \mathbf{I}). Then \mathbf{P} = \mathbf{Q}\mathbf{Q}^\top projects any vector onto S.
\mathbf{P}\mathbf{x} is the unique closest point of S; the residual \mathbf{x} - \mathbf{P}\mathbf{x} is orthogonal to all of S; and \mathbf{P}^2 = \mathbf{P}: projecting twice changes nothing.
qr manufactures the orthonormal basis. Both claims check to roundoff on a random 3-dimensional subspace of \mathbb{R}^5, and residual-orthogonality is least squares: the optimal fit’s error is orthogonal to every feature.
import numpy as onp
# A random 5x3 matrix whose columns span a 3-dim subspace of R^5
onp.random.seed(0)
A = onp.random.randn(5, 3)
Q, _ = onp.linalg.qr(A) # orthonormal basis of the column space
P = Q.dot(Q.T) # projection matrix onto that subspace
x = onp.random.randn(5)
r = x - P.dot(x) # residual
(onp.linalg.norm(P.dot(P) - P), # P^2 = P (idempotent)
onp.linalg.norm(Q.T.dot(r))) # residual is orthogonal to the subspace(np.float64(4.153077993666847e-16), np.float64(4.211628519682908e-16))
Vectors
Two subspaces ride on every matrix: the column space is what it can produce, the null space is what it sends to zero.
02
Similarity in high dimensions
cosine similarity and near-orthogonality
High dimensions
Comparing direction, not magnitude, is often what we want: an image and a dimmed copy point the same way, so they should score as identical.
\cos\theta = \frac{\mathbf{v}\cdot\mathbf{w}}{\|\mathbf{v}\|\,\|\mathbf{w}\|} \;\in\; [-1, 1].
This is the signal behind embedding retrieval, the scaled dot products inside attention, and the alignment objective of contrastive learning.
High dimensions
Drop two unrelated unit vectors into \mathbb{R}^d: what cosine do we expect?
For a random unit vector, \;\mathbb{E}[\cos\theta] = 0 and \operatorname{Var}(\cos\theta) = \tfrac{1}{d}.
Why: rotate so \mathbf{u}=\mathbf{e}_1, so \cos\theta=v_1. Symmetry \mathbf{v}\mapsto-\mathbf{v} kills the mean; \sum_i v_i^2=1 with all coordinates alike gives \mathbb{E}[v_1^2]=\tfrac{1}{d}.
Thus, the cosine concentrates at 0 with width 1/\sqrt{d}. Whether an observed cosine is meaningful depends on the dimension, candidate set, and learned representation.
High dimensions
Attention compares one query against thousands of keys by dot product, and near-orthogonality is its operating environment: the many unrelated keys score near zero, so the few that share structure with the query stand out against a quiet background.
The \sqrt{d} factor follows from the variance result. With entries of typical size 1, \|\mathbf{q}\| \approx \|\mathbf{k}\| \approx \sqrt{d} while \operatorname{sd}(\cos\theta) = 1/\sqrt{d}, so
\operatorname{sd}(\mathbf{q}\cdot\mathbf{k}) \approx \sqrt{d}\cdot\sqrt{d}\cdot\tfrac{1}{\sqrt{d}} = \sqrt{d}.
\mathbf{Q}\mathbf{K}^\top/\sqrt{d} is standardization: dividing the raw scores by their standard deviation keeps them O(1), so the softmax stays in its responsive range instead of saturating.
03
Hyperplanes and decision boundaries
the primitive every classifier shares
Hyperplanes
The set \{\mathbf{x} : \mathbf{w}\cdot\mathbf{x} = b\} is a plane with normal \mathbf{w}, sitting at distance b/\|\mathbf{w}\| from the origin. Sliding b translates it without rotating.
For any point, the signed distance (\mathbf{w}\cdot\mathbf{x} - b)/\|\mathbf{w}\| is positive on one side and negative on the other. That number is exactly a linear classifier’s margin.
Hyperplanes
Take Fashion-MNIST t-shirts and trousers. Average each class: the means are blurry but recognizable. The line between them, \mathbf{w} = \overline{\mathbf{x}}_1 - \overline{\mathbf{x}}_0, is our normal.
Hyperplanes
Classify each 784-dimensional image by the side it falls on, with the threshold at the midpoint of the two means’ projections:
import numpy as onp
def as_numpy(x):
if hasattr(x, 'asnumpy'): # MXNet
return x.asnumpy()
if hasattr(x, 'detach'): # PyTorch
return x.detach().cpu().numpy()
if hasattr(x, 'numpy'): # TensorFlow
return x.numpy()
return onp.asarray(x) # JAX / NumPy
X_test_np, y_test_np = as_numpy(X_test), as_numpy(y_test)
ave_0_np, ave_1_np = as_numpy(ave_0), as_numpy(ave_1)
# Normal = difference of class means; threshold = midpoint of their projections
w = (ave_1_np - ave_0_np).ravel()
b = onp.dot(w, (ave_0_np + ave_1_np).ravel()) / 2
predictions = X_test_np.reshape(len(X_test_np), -1).dot(w) > b
# Accuracy
onp.mean(predictions.astype(y_test_np.dtype) == y_test_np, dtype=onp.float64)np.float64(0.9155)
In this run over 2{,}000 test images, this rule is correct about 92% of the time without iterative parameter training.
Hyperplanes
Reduce every image to one number, \mathbf{w}\cdot\mathbf{x}, its position along the normal. The two classes form two modes; the dashed threshold lies between them.
A learned classifier adjusts this boundary to reduce overlap; a deep network learns features that make the classes more readily separable.
04
Matrices as linear maps
skew, rotate, scale, and the determinant
Linear maps
A matrix is fixed by where it sends the basis vectors, namely its columns. Every other vector follows as a weighted sum, so the entire grid is carried along: lines stay lines, the origin stays put, cells stay evenly spaced.
Matrices preserve linear combinations, though they may shear, rotate, reflect, project, or scale directions. Multiplying two matrices composes their maps.
Linear maps
\mathbf{B} = \bigl[\begin{smallmatrix}2&-1\\4&-2\end{smallmatrix}\bigr] has dependent columns, \mathbf{b}_1 + 2\,\mathbf{b}_2 = \mathbf{0}. The whole plane lands on one line (the column space), and the direction (1,2)^\top is mapped to the origin (the null space). Once inputs collide, no inverse can tell them apart.
Rank–nullity. \operatorname{rank}\mathbf{A} + \dim\ker\mathbf{A} = n: of the n input dimensions, \dim\ker\mathbf{A} map to zero and \operatorname{rank}\mathbf{A} remain in the image. For \mathbf{B}: 1 + 1 = 2.
Linear maps
A square matrix is orthogonal when \mathbf{Q}^\top\mathbf{Q} = \mathbf{I}. Such maps preserve every dot product, hence all lengths and angles:
(\mathbf{Q}\mathbf{x})\cdot(\mathbf{Q}\mathbf{y}) = \mathbf{x}^\top\mathbf{Q}^\top\mathbf{Q}\,\mathbf{y} = \mathbf{x}\cdot\mathbf{y}.
They are rotations and reflections, and they form the length- and angle-preserving factors in both the spectral theorem and the SVD.
Linear maps
The unit square maps to a parallelogram; its signed area is \det\mathbf{A}. The sign records orientation, and a zero determinant means space was mapped to a lower dimension.
For our grid matrix, \det = 1\cdot3 - 2\cdot(-1) = 5:
np.float64(4.999999999999999)
Composed maps multiply their area scalings, \det(\mathbf{A}\mathbf{B})=\det\mathbf{A}\,\det\mathbf{B}. Two payoffs: \det\mathbf{Q}=\pm1 for orthogonal \mathbf{Q}, and (next section) \det\mathbf{A}=\prod_i\lambda_i, the per-axis stretches multiplied.
Linear maps
Three properties pin the determinant down in every dimension, as a function of the columns: multilinear (linear in each column), alternating (zero when two columns are equal), normalized (\det\mathbf{I} = 1). Exactly one such function exists.
Consequences: swapping two columns flips the sign, and a triangular determinant is the product of the diagonal:
import numpy as onp
A = onp.array([[2, 1, 0], [1, 3, 1], [0, 1, 2]])
A_swap = onp.array([[1, 2, 0], [3, 1, 1], [1, 0, 2]]) # first two columns swapped
T = onp.array([[2, 5, 1], [0, 3, 4], [0, 0, 1]]) # upper triangular
(onp.linalg.det(A), # 8, matching the cofactor expansion
onp.linalg.det(A_swap), # -8: a column swap flips the sign
onp.linalg.det(T)) # 6 = 2 * 3 * 1, the diagonal product(np.float64(8.000000000000002),
np.float64(-8.000000000000002),
np.float64(6.0))
\det(\mathbf{A}\mathbf{B}) = \det\mathbf{A}\,\det\mathbf{B} = \det(\mathbf{B}\mathbf{A}): determinants commute even when matrices do not.
Linear maps
For a square matrix: \det\mathbf{A} = 0 \iff the columns are linearly dependent \iff \mathbf{A} is not invertible.
For the rank-one matrix \mathbf{B}, 2\cdot(-2) - (-1)\cdot 4 = 0, as expected.
When invertible, \mathbf{A}^{-1} undoes the map; the 2\times2 formula checks out, multiplying back to the identity:
array([[1., 0.],
[0., 1.]])
Prefer linalg.solve(A, b) over inv(A) @ b: stabler, and it never forms the inverse.
05
Einstein summation
one rule for every product
Einstein notation
Dot products, matrix–vector, matrix–matrix, and traces are one pattern, (\mathbf{A}\mathbf{B})_{ik} = a_{ij}\,b_{jk}: multiply entries, sum the repeated index. einsum makes that index string the whole definition (the matrix–vector call recovers our worked \mathbf{A}\mathbf{v} = [0, -5]^\top):
Wrap-up
These geometric ideas extend to the eigendecomposition, the SVD, attention, and embeddings.