Similarity Is a Retrieval Policy
The formula is the easy part. The engineering decision is whether magnitude carries signal, noise, confidence, popularity, or a bug.
- Authorship
- InterviewsVector
- Published / updated
- 2026-08-11 / 2026-08-11
- Review status
- Artifact tests passing · primary sources recorded
Original InterviewsVector material. Code examples are covered by repository tests and primary references are recorded. No named human reviewer is claimed.
The decision in one pass
Dot product ranks vectors by alignment and magnitude. Cosine similarity ranks only by angle because it divides out both magnitudes. Neither metric is universally correct: choosing one declares what your system is allowed to reward. If vector norm is not a validated feature, normalize before indexing and query with the same contract.
Why this matters
A retrieval team can change no model code, switch from cosine to inner product, and silently change which documents win. The incident will look like an embedding regression even though the real failure is an undocumented scoring policy.
You will be able to
- Derive dot product and cosine similarity from vector geometry.
- Predict how normalization changes rankings before running code.
- Detect zero vectors, inconsistent preprocessing, and norm-driven popularity bias.
- Write a metric contract that survives embedding-model and index migrations.
Prerequisite contract
- — Basic algebra
- — Vectors as ordered coordinates
- RAG mental model →
Your Vector Loop for this lab
- 01
Model
Treat a metric as a ranking policy over candidate vectors.
- 02
Derive
Separate angular agreement from magnitude.
- 03
Build
Implement and test both scores without a numerical library.
- 04
Stress
Create rankings where the metrics disagree and inspect zero-vector behavior.
- 05
Operate
Version normalization with the embedding and index contract.
- 06
Defend
Explain when norm is signal and when it is an accidental bias.
Start with the ranking, not the formula
Suppose a query vector points toward a relevant document. One candidate points in almost exactly the same direction but has a small norm. Another is less aligned but has a very large norm. Inner product may choose the second candidate; cosine will choose the first. That disagreement is not numerical noise. It is the product policy encoded by the metric.
| Policy | Rewards | Ignores | Typical risk |
|---|---|---|---|
| Dot product | alignment multiplied by both norms | nothing | large-norm items dominate |
| Cosine | angular alignment | both norms | useful norm information is discarded |
| Euclidean distance | absolute proximity | nothing | scale and dimension dominate |
Derive the two policies
a · b = Σᵢ aᵢbᵢ = ‖a‖₂ ‖b‖₂ cos(θ)
The dot product combines direction and magnitude. A perfectly aligned vector can still score poorly if its norm is small.
cosine(a,b) = (a · b) / (‖a‖₂ ‖b‖₂)
Dividing by both norms leaves only the cosine of the angle. For nonzero vectors the score lies between −1 and 1.
If every stored vector and every query is L2-normalized, inner product and cosine produce the same ordering because all norms equal one. This is why some vector systems implement cosine by normalizing once and using a maximum-inner-product index. The equivalence disappears the moment one side uses a different preprocessing path.
Move the vectors and predict the winner
Similarity policy lab
Change a candidate vector. Watch angle, norm, dot product, and cosine update together, then predict which ranking policy wins.
- Norm
- 5.00
- Angle
- 36.9°
- Dot
- 4.00
- Cosine
- 0.800
Predict both winners before checking.
Build the audit before the index
1from math import sqrt2 3def dot(a, b):4 if len(a) != len(b):5 raise ValueError("dimension mismatch")6 return sum(x * y for x, y in zip(a, b))7 8def l2(v):9 return sqrt(dot(v, v))10 11def cosine(a, b):12 denom = l2(a) * l2(b)13 if denom == 0:14 raise ValueError("cosine is undefined for a zero vector")15 return dot(a, b) / denom16 17def rank(query, candidates, score):18 return sorted(candidates, key=lambda item: score(query, item[1]), reverse=True)19 20query = [1.0, 0.0]21candidates = [("aligned", [0.9, 0.1]), ("large", [4.0, 3.0])]22print([name for name, _ in rank(query, candidates, dot)])23print([name for name, _ in rank(query, candidates, cosine)])Expected output
['large', 'aligned']
['aligned', 'large']Verify: Run python -m unittest discover courses/ai-engineering/reference-impl/similarity.
The audit intentionally fails on a zero vector instead of returning zero. Returning zero hides whether the vector came from empty input, a failed model call, aggressive pooling, or corrupted storage. Undefined similarity is an upstream data-quality event.
Break the metric contract
- 01Normalize only the documentsThe query norm becomes a request-wide constant, so ranking is unchanged for one query, but raw scores stop being comparable across queries.
- 02Mix embedding model versionsEqual dimensions do not imply a shared coordinate system. Similarity becomes syntactically valid and semantically meaningless.
- 03Index empty textA zero or near-zero vector makes cosine unstable and often reveals an ingestion bug.
- 04Let norm encode frequencyInner product can create a popularity loop where already common items keep winning independently of intent.
Productionize the metric as a versioned contract
| Contract field | Why it must be explicit |
|---|---|
| embedding model + revision | defines the coordinate system |
| input preprocessing | changes what the encoder sees |
| pooling strategy | changes vector meaning and norm |
| normalization | changes the ranking policy |
| distance metric | must match index construction and queries |
| dimension | catches incompatible writes early |
| index version | supports shadow comparison and rollback |
For D dimensions, exact scoring is O(D) per candidate; a full scan is O(ND). Approximate nearest-neighbor indexes reduce candidates but do not repair a bad metric contract. Evaluate recall at k against an exact baseline, and slice results by norm, language, document type, tenant, and index age.
Operate at three altitudes
Production lens
- — Record vector norm distributions by model version and corpus slice.
- — Reject incompatible model revisions even when dimensions match.
- — Keep an exact-search evaluation set to detect ANN recall loss.
- — Treat deletes, ACL filters, and freshness as part of retrieval correctness.
Staff lens
- — Choose the metric from product utility, then make the index serve that metric.
- — Demand a migration plan that isolates representation, index, and ranking changes.
- — Make score calibration and cross-query comparability explicit if downstream logic uses thresholds.
Interview defense
When would you choose dot product over cosine similarity for embeddings?
I would use dot product only when vector norm is intentionally trained or validated as useful signal, or when vectors are already normalized so it is an efficient equivalent of cosine. I would verify norm distributions and ranking slices, version preprocessing with the index, and shadow the choice against task-level recall and downstream utility.
Expect the interviewer to press on
- — Why can equal dimensionality still be incompatible?
- — How would you migrate a billion-vector index?
- — What does an ANN recall regression look like?
Misconceptions to remove
“Cosine is always the right metric for semantic search.”
It is right only when direction represents the useful signal and magnitude should not affect ranking.
“A vector database decides the similarity semantics.”
The product and embedding contract decide the semantics; the database implements and approximates them.
“Adding epsilon solves zero vectors.”
It avoids division by zero while concealing the ingestion or representation failure that created the vector.
Check your model
1. If every query and document vector has unit norm, can dot product and cosine return different rankings?
No. Their scores are equal because both norm terms are one, aside from implementation-level floating-point differences.
2. Why should raw cosine scores not automatically become a global relevance threshold?
Score distributions vary by model, task, language, corpus, and query ambiguity; a threshold requires calibration on the deployment distribution.
Prove the mechanism
Create a dataset where dot product, cosine, and Euclidean distance each produce a different top result. Explain which feature each policy rewards.
Add a production constraint
Add an exact top-k baseline and measure recall@k for an approximate index while stratifying misses by vector norm.
Artifact: Tested similarity audit
courses/ai-engineering/reference-impl/similarity/similarity_audit.py
Download reference implementationPrimary references and next links
References
- 1. Efficient and robust approximate nearest neighbor search using HNSW
Malkov and Yashunin. Primary paper for the HNSW graph index.
- 2. Billion-scale similarity search with GPUs
Johnson, Douze, and Jégou. Primary Faiss paper covering large-scale vector search.
- 3. Cosine similarity API reference
scikit-learn. Official library definition and behavior.
Continue through the graph
- ANN index trade-offs →
Continue from metric semantics to approximate index design.
- Embedding index migration →
Practice the production cutover.
- Production RAG architecture →
See similarity inside the full retrieval system.
Glossary: vector · norm · dot product · cosine similarity · embedding · recall@k