Command Palette

Search for a command to run...

Hectal
PHASE 14Intermediate ~34 min· topic 5 of 10

Topic 14.5

Vectors and Matrices: The Maths AI Runs On

In one line

A vector is a list of numbers that can describe anything from a point on a map to the meaning of a sentence. The dot product, norm, cosine similarity and matrix multiplication (@) are the four operations behind embeddings, search and every neural-network layer.

Think of it like this

Describing fruit with numbers. Give each fruit three scores: sweetness, sourness, crunch. Apple is [7, 3, 8], a lemon is [1, 9, 2], an orange [6, 5, 3]. Each fruit is now a vector, a point in a space with three directions. Fruit that taste alike sit close together. This is exactly how AI represents words, images and documents: as vectors called embeddings, with hundreds or thousands of numbers instead of three.

Words you'll meet

New words in this topic, in plain English. Come back here whenever one feels fuzzy.

Vector
A list of numbers, which you can think of as a point or an arrow in space.
Activation function
A simple function applied to each output of a layer. ReLU turns negatives into 0.
Gradient
The numbers that say which way, and how much, to nudge each weight to improve a model.
Embedding
A vector that represents the meaning of something, such as a word, sentence or image.
Dot product
Multiply matching positions of two vectors and add everything up: one number.
Norm
The length of a vector.
Unit vector
A vector of length 1.
Cosine similarity
How closely two vectors point in the same direction: from -1 to 1.
Matrix multiplication
Combine every row of one matrix with every column of another using dot products. Written A @ B.
Transpose
Swap rows and columns. Written A.T.
Weights and bias
The numbers a layer learns: W is multiplied with the inputs and b is added.
Top-k
The k items with the highest scores.

Step by step

01Dot product and length

v @ w for v = [3, 4] and w = [1, 2] is 3*1 + 4*2 = 11. np.linalg.norm(v) is sqrt(3*3 + 4*4) = 5. Divide by it to get [0.6, 0.8], a unit vector with length 1 that points the same way.

main.pywhole filepython
v = np.array([3.0, 4.0])
w = np.array([1.0, 2.0])
print(v @ w)                       # 11.0
print(np.linalg.norm(v))           # 5.0
print(v / np.linalg.norm(v))       # [0.6 0.8]

02Matrix multiplication and its shape rule

A @ B for two (2, 2) matrices is a (2, 2) result where each entry is a dot product of a row of A and a column of B. In bill terms: each row of A is one shopper's basket, B holds prices, and every entry of the result is one finished bill. A * B is element-wise and gives a different answer. Shapes must satisfy (n, k) @ (k, m); (2, 3) @ (2, 3) fails because 3 does not equal 2, and NumPy says so in a matmul error.

Matrix multiplication and its shape rulediagram
Rendering diagram…

03Embeddings are vectors

The example below uses tiny 4-number embeddings. They are invented to show the mechanics: real embeddings come from a model and have hundreds of numbers. "cat" and "kitten" point in almost the same direction (cosine 0.995), "car" does not (0.116).

04Top-k search in three lines

Normalise every row with emb / np.linalg.norm(emb, axis=1, keepdims=True). Then unit @ query is a vector of cosine scores, one per item (a (5, 4) @ (4,) product gives (5,)). np.argsort(-scores) lists indices from best to worst. The item that is the query itself comes first with score 1.0.

main.pywhole filepython
unit = emb / np.linalg.norm(emb, axis=1, keepdims=True)
scores = unit @ unit[0]
order = np.argsort(-scores)[:3]

05A neural-network layer

The layer below takes 4 examples with 3 features and produces 2 outputs per example. W has shape (3, 2): 3 inputs by 2 outputs. b has shape (2,) and broadcasts. After ReLU, any negative output becomes 0. The weights here are random (seeded), but in a real network they are learned.

A neural-network layerdiagram
Rendering diagram…

Try it yourself

  1. 1

    Find the closest to dog

    In the search example, set the query to the dog row (index 2) and print the top three. Does puppy come second, as you would expect?

  2. 2

    Dot product of unit vectors

    Check that unit[0] @ unit[1] equals the cosine from the first line. Why do they match?

  3. 3

    Break the shape rule

    In the layer example change W to shape (4, 2) and read the error. What must W's first dimension be equal to?

Code & diagrams

Dot product, norm, matrix multiplication New tab

Swap A and B in A @ B: the answer changes, because matrix multiplication is not commutative.

Sign in to run this example in your browser.

Expected output

dot: 11.0 11.0
norm: 5.0
unit: [0.6 0.8] length: 1.0
A @ B: [[2.0, 1.0], [4.0, 3.0]]
A * B: [[0.0, 2.0], [3.0, 0.0]]
A.T: [[1.0, 3.0], [2.0, 4.0]]
A @ I equals A: True
ValueError: matmul: Input operand 1 has a mismatch in its core dimension 0
Cosine similarity and top-k search over embeddings New tab

These 4-number embeddings are made up to show the mechanics. Change the query to emb[3] (car) and see the order change.

Sign in to run this example in your browser.

Expected output

cat vs kitten: 0.995
cat vs car   : 0.116
cat     1.000
kitten  0.995
puppy   0.656
A neural-network layer is X @ W + b New tab

Change the shape of W to (3, 5) and watch the output shape and bias both need to become 5 long.

Sign in to run this example in your browser.

Expected output

X (4, 3) W (3, 2) b (2,) -> Z (4, 2)
after ReLU, negatives become 0: True
first row Z: [ 0.024 -0.633]
first row A: [0.024 0.   ]
A shopping bill is a dot product New tab

The dot product is what a bill does: multiply each price by its quantity and add. Then the matrix form gives three shoppers' bills in one @, the same shape idea as a neural-network layer: (3 baskets, 3 items) @ (3 prices) gives 3 bills.

Sign in to run this example in your browser.

Expected output

line totals: [120.  84.  84.]
bill (dot product): 288.0
same by hand: 288.0
three bills: [288. 196. 300.]
shapes: (3, 3) @ (3,) -> (3,)

Break it on purpose

Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.

Break #1

Element-wise multiply instead of matrix multiply

Use * where @ is meant.

main.pywhole filepython
import numpy as np

X = np.ones((4, 3))
W = np.ones((3, 2))
print((X * W).shape)
terminal
$ python main.py
── what you'll see ──
Traceback (most recent call last):
File "main.py", line 5, in <module>
print((X * W).shape)
~~^~~
ValueError: operands could not be broadcast together with shapes (4,3) (3,2)

Myth vs fact

Myth

Embeddings are only for text.

Fact

Any item can be turned into a vector: images, audio, products, users, code. Similar items end up with nearby vectors.

Myth

Cosine similarity and dot product are the same thing.

Fact

They are equal only for unit vectors. Otherwise the dot product also depends on length.

Myth

A neural network is magic.

Fact

A layer is a matrix multiplication plus a bias plus a simple function. The skill is in how the numbers are learned.

Pro corner

Extra depth for experienced readers. New to this? Skip it for now and come back later.

  • ▸

    Real vector databases do not score every row. They use approximate nearest-neighbour indexes (graph or clustering methods) that trade a small loss in recall for large speed-ups. The scoring maths is still dot products or cosine.

  • ▸

    Matrix multiplication costs about n * k * m multiplications. NumPy hands @ on floats to optimised BLAS libraries (cache-blocked, multi-threaded on desktops), which is why @ beats hand-written loops by orders of magnitude. GPUs do it even faster for large sizes.

  • ▸

    Precision matters: models use float32, float16 or bfloat16. Normalise embeddings and compare floats with a tolerance. Do not use ==. After normalising a float32 vector its length is only close to 1 (for example 0.9999999), so a cosine can come out as 1.0000001. Passing that to np.arccos gives nan, so np.clip(cos, -1.0, 1.0) first.

  • ▸

    Edge cases of cosine: a zero vector has length 0, so dividing by its norm gives nan (with a warning). Guard against it or add a tiny epsilon to the norm. If your embedding model already returns unit vectors (many do, check its documentation), the plain dot product is the cosine and you can skip the normalising step. For unit vectors, squared Euclidean distance equals 2 - 2 * cosine, so ranking by smallest distance and by largest cosine gives the same order.

  • ▸

    @, np.dot and * are three different things. @ (np.matmul) is matrix multiplication and, for arrays with more than 2 dimensions, treats the leading dimensions as a batch ((32, n, k) @ (k, m) gives (32, n, m)). np.dot agrees for 1-D and 2-D inputs but behaves differently for more dimensions, and accepts scalars. * is element-wise. Prefer @ for readability. Integer arrays do not get the fast BLAS path that float arrays do, so @ on integers is much slower than on floats.

  • ▸

    np.linalg.norm(v) is the Euclidean (L2) length for a vector, and for a 2-D array the default is the Frobenius norm (the length of all the entries as one long vector), not a per-row length. For per-row lengths pass axis=1. Other norms exist: ord=1 adds absolute values (the "taxi-cab" distance), ord=np.inf takes the largest absolute value.

Remember this

  1. 1

    Recall from Topic 14.1: a vector is a 1-D array (one row of numbers) and a matrix is a 2-D array (rows and columns). You can also picture a vector as an arrow from the starting corner of a map to a point. The numbers say how far to go in each direction.

  2. 2

    The dot product of two vectors multiplies matching positions and adds the results: [3, 4] . [1, 2] = 3*1 + 4*2 = 11. The everyday version is a shopping bill. Put the prices in one vector and the quantities in another. Multiply each price by its quantity, add up the line totals, and you have the bill. In NumPy write a @ b (or np.dot(a, b)). Notice a * b is something else: element-wise multiplication that gives another vector (the line totals).

  3. 3

    The norm (length) of a vector is np.linalg.norm(v). For [3.0, 4.0] it is 5.0: walk 3 steps east and 4 steps north and the straight-line distance from the start is 5 (the longest side of a 3-4-5 right triangle). Dividing a vector by its norm gives a unit vector (length 1) that keeps the direction. Normalising embeddings to unit length makes comparisons simpler.

  4. 4

    Cosine similarity measures how closely two vectors point in the same direction, ignoring their length: a @ b / (norm(a) * norm(b)). It is 1 for the same direction, 0 for unrelated (at right angles) and -1 for opposite. For unit vectors it is just the dot product. Search engines for text, images and chatbot memory use it to find the items whose embeddings are nearest to a question's embedding.

  5. 5

    A top-k search is then three lines: normalise the rows of the embedding matrix, compute unit @ query to get one score per item, and take np.argsort(-scores)[:k] for the best k. This is what a vector database does, with clever indexes to avoid scoring every row. In Topic 16.8 you will use embeddings from a real model with exactly this code.

  6. 6

    Matrix multiplication A @ B combines rows of A with columns of B using dot products. The shapes must fit: (n, k) @ (k, m) gives (n, m). The inner sizes k must match, and they vanish. It is not element-wise, and A @ B differs from B @ A. The transpose A.T swaps rows and columns, and np.eye(n) is the identity matrix, which leaves a matrix unchanged under @.

  7. 7

    A neural-network layer is Z = X @ W + b. X has shape (batch, inputs), one row per example. W has shape (inputs, outputs), the learned weights. b has shape (outputs,), the learned bias (a number added to each output), broadcast across the batch (Topic 14.3). Z has shape (batch, outputs). An activation function such as ReLU (np.maximum(Z, 0), which turns every negative number into 0) is then applied element-wise. A deep network is many of these layers stacked. Training means adjusting W and b so the outputs get closer to the right answers.

  8. 8

    NumPy runs these on the CPU, in compiled code that uses optimised linear-algebra libraries. Frameworks such as PyTorch use the same ideas on GPUs (graphics chips that do huge amounts of arithmetic at once), with automatic gradients (the numbers that say which way to nudge each weight) for training. If you can read shapes, you can read their code.

Explain it without notes

01

What is an embedding and why is cosine similarity a useful way to compare two of them?

02

State the shape rule for A @ B and the shape of the result. How is it different from A * B?

03

Explain Z = X @ W + b with shapes for a layer that maps 128 inputs to 64 outputs on a batch of 32.

04

How do you find the top 3 most similar items to a query with NumPy?

05

When are cosine similarity, dot product and Euclidean distance equivalent for ranking, and when do they disagree?

06

What can go wrong when you normalise vectors, and how do you guard against it?

Practice

01

Write normalise(v) that returns v divided by its length. Test it on [3.0, 4.0].

02

Given A of shape (2, 3) and B of shape (3, 4), say what A @ B shape is and confirm it with np.ones.

03

Compute the cosine similarity between [1.0, 0.0] and [1.0, 1.0].

04

Given three embeddings as the rows of emb = np.array([[1.0, 0.0], [1.0, 1.0], [0.0, 1.0]]), compute the whole 3-by-3 table of cosine similarities between every pair with one matrix product, rounded to 3 places. Hint: unit @ unit.T.

Trade-offs

  • ↔

    Exact versus approximate search. Scoring every row is exact but slow for millions of items; indexes are fast but approximate.

  • ↔

    Cosine versus dot product versus distance. Cosine ignores length; the dot product rewards length; Euclidean distance suits some embeddings. Use what the model's authors recommend.

  • ↔

    Precision versus memory. float32 embeddings halve storage compared with float64, with negligible effect on ranking.

Done when you can

  • Done when you can compute dot products and norms and explain them.

  • Done when you can normalise vectors and compute cosine similarity by hand and with NumPy.

  • Done when you can write a top-k search with @ and argsort.

  • Done when you can state the shape rule for @ and tell @ from *.

  • Done when you can describe X @ W + b and give the shape of every piece.