Samuel Agbede's Learning Notes

Archives
Log in
Subscribe
13 September 2026

How Does the Dot Product Measure Similarity?

Originally published on samuelagbede.com.

I was studying Build a Large Language Model (From Scratch) the other day when I came across the idea of using dot products to calculate similarity between two normalised vectors.

I found that intriguing.

The dot product is simply the sum of the pairwise products of two vectors' components. How would that indicate similarity?

This article is my research written up. To be honest, proving why this works isn't necessary to use it. If you're like me, however, you value understanding why things are the way they are. Knowledge for knowledge's sake ;)

Before we go on this adventure, let's start with some basics.

What do we mean by similarity?

A vector can be represented as a collection of numbers. Geometrically, we can think of it as an arrow with a magnitude (length) and a direction.

Take a vector called A, defined as (4, 5). This vector has two dimensions, although vectors can have many more. On a graph, vector A looks like this:

Vector A from the origin to (4, 5).

Starting at the origin, it extends 4 steps along the x-axis and 5 steps along the y-axis.

Now imagine another vector, B, defined as (2, 1).

Vectors A = (4, 5) and B = (2, 1), with the angle between them.

If I wanted to calculate how similar A is to B, a good first question would be: similar based on what metric?

I could, for instance, measure the distance between their endpoints. A smaller distance would indicate greater similarity. Notice what's happening here, though: the lengths of the vectors matter, as well as their directions. If A were shorter, that distance would change.

If I were interested only in direction, I could measure the angle θ (theta) between the vectors. That's the approach we'll use here.

On our two-dimensional graph, we could measure the angle with a protractor:

  • At 0°, the vectors point in the same direction.
  • At 90°, they're orthogonal (perpendicular).
  • At 180°, they point in opposite directions.

Cosine turns these angles into a similarity score:

Unknown environment 'aligned'

Plotting vectors and measuring angles isn't a practical approach for computers, especially when vectors have hundreds of dimensions. We need a way to compute this score directly from their components.

Thankfully, maths can help us here.

giggles in trigonometry

Start with the law of cosines

There's a mathematical rule that lets us calculate the cosine of an angle in a triangle, provided we know the lengths of all three sides. It's called the law of cosines.

Unknown environment 'aligned'

Here, a, b, and c are the side lengths. The angle θ lies between the sides of lengths a and b, opposite the side of length c.

The law of cosines generalises Pythagoras' theorem:

Pythagoras' theorem applies to right-angled triangles. When θ = 90°, the cosine term is zero, leaving us with the equation above.

How does this relate to our vectors A and B? That's what we're about to find out.

Find the lengths of the vectors

We know the components of A and B, but we haven't calculated their lengths yet.

For A, the horizontal component is 4 and the vertical component is 5. Its length is the hypotenuse of the right triangle below.

The right triangle formed by the horizontal and vertical components of vector A.

Pythagoras' theorem gives us a way to calculate that length:

Unknown environment 'aligned'

Plugging in the components of A:

Unknown environment 'aligned'

We write the length of A as |A|, so:

More generally, we can write:

Here, Ax and Ay are the x and y components of A. Keep this relationship in mind; we'll use it again shortly.

The same calculation for B = (2, 1) gives |B| = √5 ≈ 2.236.

Find the third side

Okay, now that we have |A| and |B|, how do we calculate the distance between their endpoints? Remember, we need all three side lengths to use the law of cosines.

It turns out there's a neat way to do this. Subtract B from A to get a new vector, C = A - B:

This vector connects the endpoint of B to the endpoint of A. That makes sense: to get from B to A, move 2 steps along the x-axis and 4 steps along the y-axis.

We can now use Pythagoras' theorem to calculate its length, |C| = |A - B|.

Vector C = A − B = (2, 4), connecting the endpoint of B to the endpoint of A.

Let's write the calculation in terms of the components, using A = (Ax, Ay) and B = (Bx, By):

Unknown environment 'aligned'

A brief clarification on the last step: remember the relationship I asked you to keep in mind?

That's how we replace the sums of squared components with |A|² and |B|².

Now we need to connect this result to the angle between the vectors.

Connect the two formulas

Remember the law of cosines:

For our triangle, the side lengths are |A|, |B|, and |C|, so:

Since C = A - B, we can rewrite this as:

We now have two expressions for |A - B|². One uses the components of the vectors; the other uses their lengths and the angle between them. Because they describe the same quantity, we can set them equal:

Unknown environment 'aligned'

Subtract |A|² + |B|² from both sides, then divide by -2:

Unknown environment 'aligned'

Look at the left-hand side of the last equation. That's exactly the dot product!

For A = (Ax, Ay) and B = (Bx, By), the dot product is A · B = AxBx + AyBy. Therefore:

The dot product equals the length of A multiplied by the length of B, multiplied by the cosine of the angle between them.

Check it with our example

Let's test the formula using our original vectors:

Unknown environment 'aligned'

Now let's check this with the law of cosines, keeping the squared lengths exact to avoid rounding too early:

Unknown environment 'aligned'

We get the same answer.

Why normalisation makes the difference

If we're dealing with normalised vectors—that is, vectors with a length of 1—the formula becomes:

Unknown environment 'aligned'

This is really beautiful, actually.

The dot product of two normalised vectors equals their cosine similarity. Without normalisation, the lengths of the vectors also affect the dot product.

We don't need to calculate the angle first, or find the vector connecting the endpoints of A and B.

Let's normalise our original vectors. To normalise a non-zero vector, divide each component by the vector's length:

Unknown environment 'aligned'

Their dot product gives us:

Unknown environment 'aligned'

The same angle again! We've kept the exact values in the calculation; using the rounded components would give a slightly different approximation.

That, my friends, is the beauty of mathematics.

Let me stop now because my head hurts, lol!

Don't miss what's next. Subscribe to Samuel Agbede's Learning Notes:
← Newer Using Jev to verify cached LLM answers Older → How LLMs Turn Text into Embeddings
LinkedIn
Twitter
Powered by Buttondown, the easiest way to start and grow your newsletter.