Similarity Metrics
Cosine similarity measures only the angle between two embeddings; the choice of metric decides whether a search ranking is measuring meaning or accidentally measuring vector length.
Why Does This Exist?
Setting up any retrieval system built on embeddings requires picking a way to actually compare two vectors, and most vector databases present this as a single dropdown option, chosen once at index creation and rarely revisited. That casualness is misleading: the different metrics available don't just differ by scale or convention — they can genuinely disagree about which of two candidates is more similar to a query, ranking the exact same pair of results in opposite orders depending on which metric was chosen.
Getting this choice wrong doesn't produce an error. It produces a search system that returns plausible-looking, subtly worse results, ranked partly by an accident of vector length rather than by actual relevance — one of the most common and hardest-to-notice bugs in a retrieval pipeline.
Think of It Like This
Comparing opinions by direction versus by raw score
Picture two movie critics rating the same ten films. One is generous, scoring everything 8 to 10; the other is harsh, scoring the identical films 2 to 4. Ask whether their taste agrees, and the answer depends entirely on which question you're actually asking. By raw score, they disagree on almost every film — nothing is close. By the pattern of what they rank highest versus lowest, they agree almost perfectly; one is simply a shifted, compressed version of the other.
Cosine similarity asks the pattern question — it cares only about relative direction, ignoring overall scale. Euclidean distance asks the raw-score question — it cares about the actual gap, scale included. Embeddings from a neural model are much closer to the "harsh versus generous critic" situation than people expect, which is exactly why the choice of metric matters as much as it does.
How It Actually Works
Three measures, one real disagreement
Cosine similarity measures only the angle between two vectors, ranging from (opposite) to (identical direction), completely blind to either vector's length:
Euclidean distance measures the actual straight-line gap between two points, and it's sensitive to both direction and magnitude:
Dot product is the raw, unnormalized — fast to compute, but it mixes direction with both vectors' magnitudes at once, meaning a long vector pointing somewhat off-target can outscore a short vector pointing exactly on target.
The diagram above shows the disagreement directly: two vectors pointing in the identical direction but with very different lengths score as perfectly identical by cosine (angle is zero) and as far apart by Euclidean distance (the gap between their endpoints is large). Neither metric is "wrong" — they're measuring genuinely different properties of the same pair of vectors.
Why embeddings default to cosine
For embeddings produced by a neural model, a vector's direction is what the training process shaped to carry meaning; a vector's magnitude is frequently just an artifact of training dynamics, with no reliable semantic content attached to it. Ranking search results by raw dot product on unnormalized embeddings lets that meaningless magnitude leak into the ranking — a document whose embedding happens to be longer, for reasons that have nothing to do with actual relevance, can systematically outrank a shorter, more relevant one. Cosine similarity sidesteps this entirely by discarding magnitude before comparing, which is why it's the default choice for embedding-based retrieval specifically, even though Euclidean distance remains the right default for many other kinds of vector comparison.
The shortcut that makes cosine cheap
If every vector is normalized to unit length before it's stored — dividing each vector by its own norm — then the plain dot product of two unit vectors is exactly their cosine similarity, since the denominator in the cosine formula becomes 1 for both. This is why production vector databases commonly normalize at write time: it buys the correctness of cosine similarity while keeping the raw speed of a plain dot product, which is measurably faster to compute at the scale a real index operates at.
Show Me the Code
Computing all three metrics on the same pair of vectors, and then showing normalization collapse the dot-product-versus-cosine gap.
import numpy as np
def cosine(u: np.ndarray, v: np.ndarray) -> float: return float(u @ v / (np.linalg.norm(u) * np.linalg.norm(v)))
a = np.array([1.0, 1.0]) # short vectorb = np.array([10.0, 10.0]) # same direction, ten times longer
print(round(cosine(a, b), 4)) # -> 1.0 -- identical directionprint(round(float(np.linalg.norm(a - b)), 4)) # -> 12.73 -- far apart by Euclidean distanceprint(round(float(a @ b), 2)) # -> 20.0 -- raw dot product, inflated by b's length
a_unit, b_unit = a / np.linalg.norm(a), b / np.linalg.norm(b)print(round(float(a_unit @ b_unit), 4)) # -> 1.0 -- matches cosine exactly, once normalizedThe raw dot product of 20.0 looks like a meaningful number until you notice it's driven almost entirely by b's length — normalizing first collapses that inflation and recovers the same 1.0 that cosine similarity reported directly.
Watch Out For
Choosing inner product for speed without normalizing first
Some vector databases default to inner product (raw dot product) purely because it's the cheapest operation to compute at index-query time, without checking whether the stored vectors are actually normalized. On unnormalized embeddings, this silently ranks results partly by vector length — longer documents or certain content types can systematically score higher, regardless of actual relevance, and nothing in the system flags this as an error because the query still returns a plausible-looking ranked list.
Mismatching the metric against the model's training objective
An embedding model trained with a contrastive objective specifically tuned for cosine similarity produces a space where angle carries the meaning and magnitude doesn't. Searching that same index with Euclidean distance instead, or vice versa for a model trained differently, measures something the model was never optimized to make meaningful along that axis. Match the similarity metric used at query time to whatever metric the embedding model was actually trained and evaluated against — check the model's documentation rather than assuming.
The Quick Version
- Cosine similarity measures direction only; Euclidean distance measures the actual gap, including magnitude; dot product mixes both.
- The three metrics can disagree on which candidate is more similar — this isn't a rounding difference, it's a genuine disagreement.
- Embedding magnitude is usually a training artifact without reliable meaning, which is why cosine similarity is the default for embedding search.
- Normalizing vectors to unit length at write time makes the cheap dot product equivalent to cosine similarity, buying both speed and correctness.
- The right metric depends on what the embedding model was actually trained and evaluated against, not just on which option is fastest to compute.
What to Read Next
- Embeddings is the object this page's metrics are used to compare.
- Embedding Models determines which metric a given model's vector space was actually shaped for.
- Vector Databases is where the metric choice this page covers gets configured in a real system.
- Approximate Nearest Neighbor Search relies on the same metric to define what "nearest" even means.