Command Palette

Search for a command to run...

Hectal
PHASE 2Beginner ~20 min· topic 2 of 10

Topic 2.2

Dot Product, Cosine and Distance

In one line

Three ways to compare two vectors: multiply and add (dot product), compare the direction only (cosine), or measure the gap between the points (distance). They rank differently when vectors have different lengths and identically when every vector has length 1.

Think of it like this

Two people pointing at the sky. Cosine asks 'are you pointing the same way?', and ignores how long your arm is. Distance asks 'how far apart are your fingertips?', and your arm length matters. The dot product mixes both: it grows when you point the same way *and* when your arms are long. Which question you ask changes who counts as 'closest'.

Words you'll meet

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

Dot product
Multiply two vectors slot by slot and add up. Big when they point the same way, and big when they are long.
Cosine similarity
The dot product divided by both lengths. It only looks at direction: 1 same, 0 unrelated, -1 opposite.
Euclidean distance
The straight-line gap between two points. Smaller means more similar.
Length (norm)
How long a vector is: the square root of the sum of its squares.
Unit vector
A vector of length 1.
Similarity versus distance
Similarity grows as things get closer, distance shrinks. Rank accordingly: highest similarity first, lowest distance first.

Step by step

01Step 1: the dot product by hand

For query [3, 4] and candidate A [6, 8]: 3*6 + 4*8 = 18 + 32 = 50. For B [4, 3]: 12 + 12 = 24. For C [-3, -4]: -9 - 16 = -25. A negative dot product means the vectors point in opposite directions.

measures.pywhole filepython
def dot(a, b):
    return sum(x * y for x, y in zip(a, b))

def length(a):
    return math.sqrt(dot(a, a))

def cosine(a, b):
    return dot(a, b) / (length(a) * length(b))

02Step 2: cosine removes the length

A is exactly twice as long as the query but points the same way, so its cosine is 1.000. B points slightly differently (cosine 0.960). C points the opposite way (-1.000). Cosine answers only 'same direction?'.

03Step 3: distance asks a different question

The gap from the query to A is 5.000, to B only 1.414, to C 10.000. Distance puts B first. It prefers a vector that is simply *near* in space, even if the direction is slightly different.

Step 3: distance asks a different questiondiagram
Rendering diagram…

04Step 4: make the vectors the same length

Scale every vector to length 1 (Topic 2.3). Now only direction is left. In the second example all three measures return D2, D1, D4, D3.

05Step 5: the identity that ties them together

For unit vectors, distance^2 = 2 - 2 * cosine. The example prints both sides for D1: 0.4000 and 0.4000. So 'highest cosine' and 'smallest distance' are the same ordering, and you can use whichever is fastest in your store.

06Step 6: check what your model expects

Model documentation says which measure it is trained for, and whether its output is already normalised. When it says 'cosine', a store set to dot product is fine only if you normalise the vectors first.

Try it yourself

  1. 1

    Predict the order

    Change the candidates to A: [2, 0], B: [1, 1], C: [0, 3] with the query [1, 0]. Before running, write down the order by dot, by cosine and by distance. Which one disagrees?

  2. 2

    Make a long vector win

    In the first example, change A to [60, 80]. Does cosine change? Does the dot product? Does distance? What does that tell you about comparing vectors of different lengths?

  3. 3

    Check the identity yourself

    In the second example, print the identity for D3 instead of D1. Is 2 - 2*cosine still equal to the distance squared? What if one vector is not normalised?

Code & diagrams

Three measures, three rankings New tab

Two-number vectors so you can check each result by hand. Real vectors have hundreds of numbers, and the maths is the same.

Sign in to run this example in your browser.

Expected output

query: [3, 4]  A: [6, 8] (same direction, longer)  B: [4, 3] (close by)  C: [-3, -4] (opposite)
A: dot=50.0 cosine=1.000 distance=5.000
B: dot=24.0 cosine=0.960 distance=1.414
C: dot=-25.0 cosine=-1.000 distance=10.000
ranked by dot:      A, B, C
ranked by cosine:   A, B, C
ranked by distance: B, A, C
On unit vectors the three agree New tab
Sign in to run this example in your browser.

Expected output

unit vectors, ranked by dot:      D2, D1, D4, D3
unit vectors, ranked by cosine:   D2, D1, D4, D3
unit vectors, ranked by distance: D2, D1, D4, D3
D1: cosine=0.8000  distance squared=0.4000  2 - 2*cosine=0.4000

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

Call it cosine, but return the dot product

In the Python tab, change the body of cosine to return dot(a, b) (forget to divide by the lengths). Run.

terminal
$ Run
── what you'll see ──
query: [3, 4] A: [6, 8] (same direction, longer) B: [4, 3] (close by) C: [-3, -4] (opposite)
A: dot=50.0 cosine=50.000 distance=5.000
B: dot=24.0 cosine=24.000 distance=1.414
C: dot=-25.0 cosine=-25.000 distance=10.000
ranked by dot: A, B, C
ranked by cosine: A, B, C
ranked by distance: B, A, C

Myth vs fact

Myth

Cosine, dot product and distance are interchangeable.

Fact

They rank the same only when all vectors have length 1. With different lengths they can disagree, as the first example shows.

Myth

A cosine of 0 means the texts are opposites.

Fact

0 means unrelated (at right angles). -1 would be opposite, and real text embeddings rarely go far below 0.

Myth

Smaller is always better for a score.

Fact

Only for distance. For dot product and cosine, larger means more similar. Mixing the two up reverses a ranking.

Pro corner

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

  • ▸

    Stores often implement 'cosine' as 'normalise, then dot product', because the dot product is the cheapest operation and hardware does it fast. If the vectors are already unit length, the normalising is wasted work you can skip, but only if you are sure (Topic 2.3).

  • ▸

    Some stores report cosine distance, which is 1 - cosine: smaller is better, and 0 means identical. Check which one a score is before you set a threshold on it.

  • ▸

    Distance and dot product are sensitive to vector length. Some models are trained with a dot-product objective, so their lengths may be part of what they learned. Do not normalise a model's output unless its documentation says that is safe.

Remember this

  1. 1

    Why this exists. Topic 2.1 ended with 'similar texts get similar vectors'. Search needs a number for 'how similar', so it can sort. There is more than one reasonable number, and the choice matters because the vectors a model produces are not always the same length. A wrong choice quietly gives a wrong ranking.

  2. 2

    Dot product. Multiply the two lists slot by slot and add the results: [3, 4] and [6, 8] give 3*6 + 4*8 = 50. It is large when the vectors point the same way and also large when the vectors are long. It is the fastest of the three, which is why stores like it.

  3. 3

    Cosine similarity. Divide the dot product by both lengths: dot(a, b) / (length(a) * length(b)). The lengths cancel out, so only the angle between the vectors is left. The result runs from 1 (same direction) through 0 (unrelated, at right angles) to -1 (opposite). It is the usual choice for text.

  4. 4

    Euclidean distance. The straight-line gap between the two points: subtract slot by slot, square, add, take the square root. Here *smaller* means closer, the reverse of the other two. The example below shows the trap: for the query [3, 4], the long vector [6, 8] (same direction) wins by dot and cosine, but the short vector [4, 3] (close by) wins by distance.

  5. 5

    When the three agree. If every vector has length 1, the lengths drop out and the three rank the same way. The dot product *is* the cosine, and distance squared is 2 - 2 * cosine, so a larger cosine always means a smaller distance (the second example proves it for D1: cosine 0.8000, distance squared 0.4000, and 2 - 2*0.8 is also 0.4). This is why most embedding models return length-1 vectors and why stores say 'use dot product on normalised vectors' (Topic 2.3).

  6. 6

    Which one to use. Use the one the model was trained with. Most text embedding models are trained for cosine similarity, and many output unit vectors, so dot and cosine are the same. Read the model's documentation, and use the same measure for the index as for the query. Never mix measures across one index.

Explain it without notes

01

What is the difference between the dot product and cosine similarity, and when are they equal?

02

In the example, why does distance rank B first while cosine ranks A first?

03

Why do embedding stores usually let you pick a distance measure when you create an index?

04

A colleague set a similarity threshold of 0.8 and switched the store from cosine to distance, keeping 0.8. What goes wrong?

Practice

01

Write cosine(a, b) and distance(a, b) and print both for a = [1, 0], b = [0.6, 0.8] to three decimals. Which is larger, and what does distance^2 equal for these unit vectors?

02

Write rank(query, docs, measure) where docs is a dict of name to vector and measure is "cosine" or "distance". Return names best first. Print the order for the query [1, 0] and docs {"x": [2, 0], "y": [0.5, 0.5]} under both measures.

Trade-offs

  • ↔

    Cosine: robust to vector length, the natural measure for text; needs two lengths (or normalised input).

  • ↔

    Dot product: the cheapest, and equal to cosine on unit vectors; misleading when lengths differ.

  • ↔

    Distance: intuitive for points in space; the direction of 'better' is reversed, and it is length-sensitive.

Done when you can

  • Done when you can compute dot, cosine and distance for two small vectors by hand.

  • Done when you can say which of the three ranks differently for vectors of different lengths, and why.

  • Done when you can state the identity between distance and cosine for unit vectors.

  • Done when you know where to look up which measure a model expects.