Cosine Similarity, Dot Product and Euclidean Distance Explained

A
Author
30 Oct, 2026 3 min read 0 views

To find the vectors closest to a query you need a definition of "close". The three common choices are cosine similarity, dot product and Euclidean distance. For normalized embeddings they all rank results in the same order, so the practical rule is simple: use the metric your embedding model was trained for, and cosine similarity if the model does not say.

Cosine similarity

Cosine similarity measures the angle between two vectors, ignoring their length. It returns a value from -1 (opposite) through 0 (unrelated) to 1 (same direction). For text embeddings, values near 1 mean very similar meaning. It is the most common metric for semantic search because the direction of an embedding carries the meaning.

The formula divides the dot product by the product of the two vector lengths. Many databases expose cosine distance, which is 1 minus cosine similarity, so smaller is better. Check which one your tool returns.

Dot product

The dot product multiplies matching numbers and adds them up. It grows with both the angle match and the vector lengths. If all your vectors have length 1, the dot product equals cosine similarity and is cheaper to compute because it skips the division. Many embedding models output normalized vectors for exactly this reason, and some were trained with dot product in mind.

Euclidean distance

Euclidean (L2) distance is the straight-line distance between two points. Smaller means closer. Unlike cosine, it is affected by vector length. For normalized vectors, squared Euclidean distance equals 2 minus 2 times the cosine similarity, so the ranking is identical.

Compute all three

This script compares the metrics on small vectors you can check by hand. It needs only NumPy.

import numpy as np

a = np.array([1.0, 2.0, 3.0])
b = np.array([2.0, 4.0, 6.0])   # same direction as a, twice as long
c = np.array([3.0, 0.0, -1.0])

def cosine(x, y):
    return float(np.dot(x, y) / (np.linalg.norm(x) * np.linalg.norm(y)))

def euclid(x, y):
    return float(np.linalg.norm(x - y))

for name, v in [("b", b), ("c", c)]:
    print(name,
          "cosine=%.3f" % cosine(a, v),
          "dot=%.1f" % np.dot(a, v),
          "l2=%.3f" % euclid(a, v))

Vector b points in exactly the same direction as a, so cosine is 1.000, even though the dot product (28.0) and the Euclidean distance (3.742) are affected by its larger length. Vector c points elsewhere, and its cosine is close to 0. This shows why length matters for dot product and L2 but not for cosine.

Which one should you use?

  • Follow the model card. Most text embedding models say which metric to use. Use it.
  • If vectors are normalized, cosine, dot product and L2 give the same ranking. Dot product is the fastest, so many systems normalize once at ingestion and then use inner product.
  • If vectors are not normalized, cosine is the safe default for text, because it ignores length differences that carry little meaning.
  • Use L2 when the magnitude carries meaning, which is uncommon for text.

The index must match the query

A vector index is built for one metric. In pgvector, for example, the index operator class (cosine, inner product or L2) must match the operator you use in your query, or the index will not be used. Pick the metric before you create the index, and write it down in your project notes. Changing it later means rebuilding the index.

Common mistakes

  • Treating a distance as a similarity. With cosine distance, 0 is a perfect match, not the worst.
  • Mixing normalized and unnormalized vectors in one index, which makes dot product scores inconsistent.
  • Setting a fixed similarity threshold (for example 0.8) copied from another model. Scores depend on the model, so tune thresholds on your own data.
  • Comparing raw scores across different queries. Scores are most useful for ranking within one query.

Once you can score two vectors, you can build a complete search over a small collection. That is the next post.

Was this article helpful?

Comments

No comments yet. Have a question or a tip? Be the first to share it.

Leave a comment

Comments are reviewed before they appear.