Similarity Metrics

RoadmapsAI Engineering

Scenario

Your embedding model turns 'dog' and 'puppy' into two arrays of 1,536 numbers. Before you read on, predict: which metric would a vector database use to decide which is closer to a query — the angle between the arrays, the straight-line distance, or both?

Think about what 'similar meaning' means geometrically, then read on.

Mental model

Similarity is geometry — measuring how closely two arrows point in the same direction, or how far apart two points are in space.

Once text becomes a vector, comparing meaning means measuring distance or angle. Cosine similarity checks if two vectors point in the same direction, ignoring how 'long' they are. Euclidean distance measures the physical gap between the two points. You do not need to memorize the formulas, but you must know which metric your model and database are using — because mixing them causes silent errors.

Deep dive

Once text is embedded, it is a point in high-dimensional space. To find the best answers for a user's question, you need a math formula that turns two arrays of numbers into a single score saying how 'close' they are. The metric you choose defines what 'close' means.

You need a formula to turn two vectors into one closeness score.

The standard choice is cosine similarity. It measures the angle between two vectors. If they point in exactly the same direction, the angle is 0 and the cosine is 1 — identical meaning. If they point in perpendicular directions, the cosine is 0 — unrelated. **Cosine cares entirely about direction and ignores vector length.**

Cosine = direction only. Two vectors pointing the same way = same meaning, regardless of length.

Euclidean distance (L2) measures the straight-line gap between two points in space. It cares about both direction AND length. If two vectors point in the same direction but one is twice as long, Euclidean says they are far apart — while cosine says they are perfectly identical. For embeddings where length varies, this difference matters.

Euclidean = straight-line gap, sensitive to vector length.

The dot product multiplies matching numbers in two vectors and sums the results. Here is the key insight: if your vectors are normalized (forced to have exactly length 1), the dot product gives the same ranking as cosine similarity. But dot product is faster for hardware to compute. This is why databases default to dot product when they know vectors are normalized.

Dot product is faster. For normalized vectors, dot product = cosine similarity for ranking purposes.

**Most modern models output normalized vectors by default**, including OpenAI's `text-embedding-3` family. Because all vectors have length 1, cosine, Euclidean, and dot product all rank your results in the same order. In practice: configure your database to use dot product (inner product) for speed, and the ranking will be identical to cosine.

Pipeline: Text → Embed → Score (similarity metric) → Retrieve. For normalized embeddings, dot product is the fastest choice.

Code examples

The three metrics in plain Python

import numpy as np
from numpy.linalg import norm

def cosine_similarity(a, b):
    return np.dot(a, b) / (norm(a) * norm(b))

def euclidean_distance(a, b):
    return norm(np.array(a) - np.array(b))

def dot_product(a, b):
    return np.dot(a, b)

If both vectors are normalized (length = 1), the division in cosine similarity does nothing. `cosine_similarity` and `dot_product` return the same value. `np.dot()` runs faster on hardware because it skips the division.

Choosing a metric in pgvector

-- Cosine distance (<=>): ORDER BY ascending = most similar first
SELECT text FROM docs ORDER BY embedding <=> :query_vector LIMIT 5;

-- Euclidean distance (<->)
SELECT text FROM docs ORDER BY embedding <-> :query_vector LIMIT 5;

-- Inner product / dot product (<#>)
SELECT text FROM docs ORDER BY embedding <#> :query_vector LIMIT 5;

pgvector exposes three operators. For normalized OpenAI embeddings, the inner product (`<#>`) is the fastest and gives the same top-k results as cosine. Note: all three return distance, so ORDER BY ascending = most similar first.

Common mistakes

Glossary

cosine similarity
A number between 0 and 1 that measures how similar two pieces of text are by comparing the direction of their vectors. 1 means identical meaning, 0 means unrelated.
euclidean distance
A number that measures the straight-line gap between two points in a vector space. Smaller distance means more similar.
dot product
A fast math operation that multiplies matching numbers in two vectors and sums the results. For normalized vectors, it gives the same ranking as cosine similarity but runs faster.

Recall questions

Questions & answers

You migrate from an open-source embedding model (variable vector length) to OpenAI's embeddings (normalized to length 1). Your vector database sorts by cosine distance. A coworker suggests switching to dot product to save CPU cycles. Is this safe?

Yes. OpenAI's embeddings are normalized, so all vectors have length 1. For normalized vectors, dot product ranks results in exactly the same order as cosine similarity, but runs faster. You only need to make sure the ORDER BY direction is correct — ascending for both distance metrics.

Approach: Identify the relationship between normalization, cosine, and dot product. State clearly that normalization makes them equivalent for ranking, so the switch is safe.

Your retrieval system returns the worst possible matches for every query. You are using `ORDER BY embedding <=> query_vector DESC` in pgvector. What is the bug?

The `<=>` operator in pgvector returns cosine distance — smaller values are more similar. Sorting DESC means you are ordering from largest distance to smallest, which gives you the least similar vectors first. Change `DESC` to `ASC` to get the most similar results.

Approach: Spot the distance vs similarity inversion. pgvector operators return distance (lower is better), so you must sort ascending for similarity search.

Continue learning

Previous: Embeddings

Next: ANN Indexes (HNSW & IVF)

Next: pgvector, Pinecone, & Chroma

Return to AI Engineering Roadmap