Modules/Gen AI/ Similarity Metric Lab

Dot Product vs Cosine Similarity

Two ways to score "how similar", and they disagree the moment length stops being constant. Which one your embedding model was trained for is not optional trivia.

Stretch Doc B

1.0×

same direction as before, just longer — like a verbose, repetitive document embedding

Rank By

Query: "lightweight training method"

—

The Space, 2D For Clarity

Both Scores

 

Overview

Two ways to score "how similar", and they disagree the moment length stops being constant.

In this guide
  1. What this is
  2. The formulas
  3. The same formula, one difference
  4. When size is noise, and when it is signal
  5. The identity that makes this a non-issue in practice
  6. The one difference, and when it bites
  7. Guided tour
  8. Which one is "right"?
  9. What to recall
  10. Interpreting the numbers
  11. The zero-vector trap
  12. Other measures, briefly
  13. Questions people ask
  14. Recap in one screen

Dot Product vs Cosine: A Practical Guide

The same two vectors, two different questions asked of them.

What this is

Cosine likeness asks "what angle apart are these two vectors" and ignores length fully. Dot product asks "how much do these two vectors agree, weighted by how long they both are" — length is part of the answer, not discarded. Most embedding search defaults to cosine, but a growing number of models (some recommendation embeddings, some matryoshka and MIPS-optimised models) are trained. That way, dot product is the correct likeness, and using cosine on them silently under-uses the model. In embedding spaces, cosine likeness is the standard retrieval metric because documents of different lengths give vectors of different sizes. And you want a long document about cats to match a short query about cats. The dot product would rank the long document higher simply because its vector is longer, not because it is more apt. But in attention methods inside transformers, the dot product is used on purpose. The size of a key vector encodes how strongly that token wants to be attended to, and normalising it away would lose that signal. Scaled dot-product attention divides by the square root of the dimension to keep the values in a range where softmax gives useful gradients. But it does not normalise the vectors themselves. The two measures agree on direction and disagree on scale. If both vectors are unit-length, cosine likeness and the dot product return exactly the same number. This page computes both measures on pairs of vectors, shows how size affects each one, and covers when to use which in practice.

The formulas

dot(a,b) = Σ aᵢbᵢ     cos(a,b) = dot(a,b) / (‖a‖ ‖b‖)

Cosine is the dot product after dividing out both vectors' lengths —. This is exactly why stretching one vector along its own direction changes its dot product with anything but never changes its cosine with anything.

The same formula, one difference

dot product = a · b = Σ aᵢbᵢ

cosine likeness = (a · b) / (‖a‖ ‖b‖)

Cosine likeness is the dot product divided by both lengths. That division is the whole difference, and it removes size from the check, leaving only direction.

Worked through. Take a = [3, 4] and two candidates:

  • b = [6, 8] — the same direction, twice as long.
  • c = [4, 3] — a different direction, the same length.
 Dot productCosine
a · b18 + 32 = 5050 / (5 × 10) = 1.00
a · c12 + 12 = 2424 / (5 × 5) = 0.96

By dot product, b scores twice as high as c. By cosine, b is a perfect match (the same direction) and c is very close. The two measures rank them the same way here, and for different reasons. And they will disagree when lengths vary much.

When size is noise, and when it is signal

Use cosine when length carries no meaning. Text embeddings are the standard case. A 4,000-word article and a 200-word summary of it should be close. And their vector lengths differ mainly because of length and count, not content.

Use the dot product when length carries meaning. Some recommendation systems on purpose encode fame or confidence as vector size. So that a popular item scores higher for an equally-good direction match. Discarding the length would discard that data.

SituationMeasure
Text and document embeddingsCosine
Semantic search, RAGCosine
Recommendations with popularity in the magnitudeDot product
Trained with a dot-product objectiveDot product — match the training
Comparing images by feature vectorsCosine, usually

That fourth row is the one people miss. If the embedding model was trained with an inner-product objective, its geometry is arranged for the dot product, and using cosine may not be what it was optimised for. Model documentation often states which to use, and following it matters more than the general argument.

The identity that makes this a non-issue in practice

On vectors normalised to length 1, the two measures are the same. The denominator of cosine likeness becomes 1 × 1.

That is why almost each embedding pipeline normalises on the way in. Then uses plain dot products. You get cosine semantics with the cost of a dot product. And a dot product over a matrix is a single fast matrix product.

There is a second identity worth knowing. On normalised vectors:

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

So squared Euclidean distance is a dropping function of the dot product. This means ranking by cosine likeness and ranking by Euclidean distance give exactly the same order on normalised vectors. The choice of metric in a vector database is then a question of implementation rather than of results.

import numpy as np

vecs = model.encode(chunks, normalize_embeddings=True)   # normalise once
q = model.encode([question], normalize_embeddings=True)[0]

scores = vecs @ q          # dot product == cosine, because both are unit length
top = np.argsort(-scores)[:5]

The one difference, and when it bites

Cosine likeness is the dot product with the lengths divided out. So the two agree exactly when vectors are normalised and disagree in a specific, predictable way when they are not. This shows which retrieval bugs come from that gap.

example_01.pyNumPy
Output

That is not a coincidence — it falls straight out of the algebra relating the two. This the next block states right.

example_02.pyNumPy
Output

That algebra has a practical result: once each vector is normalised to unit length, dot product and cosine become the same number.

example_03.pyNumPy
Output

Given that equivalence once normalised, the choice comes down to what a vector's length is meant to encode.

example_04.pyNumPy
Output

Getting that choice wrong gives a real bug: the next block shows an embedding model whose vector length grows with text length.

example_05.pyNumPy
Output

Finally, it is worth separating cosine likeness from cosine distance, and checking how both relate to euclidean distance on unit vectors.

example_06.pyNumPy
Output

Guided tour

  1. Start at 1x with cosine ranking. Doc A, the concise on-topic passage, ranks first.
  2. Stretch Doc B to 5x. Under cosine, nothing about the ranking moves — Doc B's direction. And so its angle to the query, never changed.
  3. Switch to dot product and stretch again. Somewhere around 3-4x, Doc B overtakes Doc A — purely. This is because it got longer, with its actual topical relevance unchanged.
  4. Read both scores side by side at 5x. Cosine still ranks Doc A first; dot product now ranks Doc B first. Same embeddings, same query, opposite answer.

Which one is "right"?

Neither, in the abstract — it depends on what the embedding model was trained to make meaningful. If length in your model's embeddings correlates with noise (document verbosity, padding, repetition), cosine is safer. If length was trained to carry real signal (fame, confidence, specificity), dot product is the metric the model really optimised for, and cosine throws that signal away. Check your model's documentation; do not assume.

What to recall

Cosine likeness divides out vector length and only ever measures direction; dot product does not. So it rewards longer vectors whether or not that length means anything. The two metrics agree only when each vector has the same length — true if you normalise your embeddings to unit length, false if not. Know which one your embedding model was trained against, because using the wrong one is a silent, hard-to-debug retrieval quality bug.

Interpreting the numbers

Cosine likeness has a fixed range, which makes it thresholdable:

ValueMeaning
1.0Identical direction
0.8–0.95Strongly related
0.5–0.8Loosely related
0.0Orthogonal — nothing in common
NegativeOpposed directions

The dot product has no fixed range at all, which is a practical downside. A score of 42 means nothing without knowing the typical sizes in that space.

Two cautions about cosine thresholds:

Calibrate against your own data. Many sentence-embedding models score unrelated text at 0.2–0.4. So a threshold of 0.5 admits large noise. Score a few hundred known-related and known-unrelated pairs and put the cut where the distributions separate.

Negative values are rare in practice. Most modern embedding models place nearly all of it in a narrow cone of the space. So scores cluster in a band and true opposition is uncommon. Relative ranking remains reliable; absolute values are model-specific.

The zero-vector trap

Cosine likeness divides by the vector lengths, so a zero-length vector gives a division by zero.

That is not hypothetical. An empty document, a chunk that held only whitespace or markup, a user with no links. All give zero vectors, and the result is a nan that propagates silently through averages and rankings.

def cosine(a, b):
    denom = np.linalg.norm(a) * np.linalg.norm(b)
    return float(a @ b / denom) if denom else 0.0     # guard it

The dot product has no such problem, which is a minor point in its favour. The real fix is to filter empty content before indexing it.

Other measures, briefly

MeasureSensitive to length?Use
CosineNoText, embeddings, semantic similarity
Dot productYesWhen magnitude encodes something real
EuclideanYesCoordinates in real space
ManhattanYesSparse or high-dimensional data
MahalanobisYes, and accounts for correlationOutlier detection

Euclidean distance is the one people reach for by habit from geometry. And it is often the wrong choice for unnormalised embeddings. A long vector is far from all of it. So document length rules the ranking. Normalise, and it becomes the same to cosine.

Questions people ask

Which should I use for RAG? Cosine — or the same way, normalise your vectors and use dot products.

Are they ever truly different in results? Only on unnormalised vectors. Normalised, they rank identically.

Why do some databases default to inner product? Because it is the cheapest step. And it is correct if you normalise on the way in. Check whether yours normalises for you.

Can cosine likeness be negative? In math yes, down to −1. With most text embedding models it is uncommon.

Does normalising lose data? It discards size. For text embeddings that is often noise; for models that encode confidence in the size, it is a loss.

What threshold should I use? Do not guess — measure it on labelled pairs from your own corpus. Model-to-model change is large.

Recap in one screen

  • Cosine likeness is the dot product divided by both lengths. This removes size and keeps direction.
  • Use cosine when length is noise (almost always, for text). Use the dot product when size encodes something real.
  • On unit-length vectors the two are the same, and Euclidean distance ranks the same way.
  • Normalise once at indexing time, then a fast matrix times gives you cosine scores.
  • Guard against zero vectors, and calibrate thresholds against your own labelled pairs.

Recall check

0 of 3

Say the answer out loud before you reveal it — recalling it is what makes it stick, and rereading it is not.

  1. What does this module say about “What this is”?

  2. What does this module say about “Stretch Doc B”?

  3. What does this module say about “When size is noise, and when it is signal”?

Cheat sheet

Dot Product vs Cosine Similarity for Retrieval

Stretch a document vector and watch dot-product ranking promote it for being longer, while cosine ranking stays put.

GEN AI · vizlearn.in/gen_ai/dot_product_vs_cosine_similarity.html

About the author

Ashish Jangra builds and maintains VizLearn. Every module here is written and the visualisation behind it hand-built, so the numbers in a readout come from the same code that draws the picture. Corrections are genuinely welcome and get priority over everything else — if a page states something wrong, or an animation misrepresents what the algorithm does, get in touch.