Vocabulary for this course

The terms the chapters assume, in plain words. You can open this from any chapter.

approximate nearest neighbour (ANN)
A search that checks only part of the index, so it is fast but may miss some of the true closest matches.
collection
A named group of vectors that share one configuration and are searched together.
cosine similarity
The dot product of two unit-length arrows, from 1 when they point the same way to -1 when they oppose.
dot product
Multiply matching numbers in two lists and add the results; it grows with both length and agreement.
embedding
A list of numbers that places a piece of text in a space where similar meanings sit close together.
embedding model
The trained program that turns text into vectors, with one fixed output width.
hybrid search
Running a word-matching search and a meaning-based search together, then combining their ranked lists into one.
index
A structure built ahead of time so the database can find nearby items without comparing against every one.
magnitude
The length of a vector, found by squaring its numbers, adding them and taking the square root.
metadata filter
A rule that keeps only the items whose stored facts, such as date or owner, match before or during the search.
model card
A short document describing a trained system: its data, its scores and its known limits.
normalised vector
A vector divided by its own length, so it has length one and only direction remains.
quantisation
Storing each number with less precision, which shrinks memory and usually costs a little accuracy.
recall
The share of the truly closest items that a search actually returned.
reranking
Scoring a short list of results again with a slower, more careful method and putting the best first.
similarity function
A rule that scores two vectors and returns a number saying how alike they are.
top-k
The shortlist a search returns: its best few results, cut off at a number chosen in advance.
training loss
The average error an embedding model makes on the text pairs it is trained to match.
unit length
A vector whose magnitude is exactly 1, which makes a dot product read as pure agreement.
vector
An ordered list of numbers, where each position is one measured property.

Vector databases from the ground up

Similarity metrics

Cosine, dot product and Euclidean distance are three ways to say “close”. They disagree about magnitude, and the embedding model you chose was trained with one of them in mind.

Part 2 of 10

Intermediate14 min readBack to Courses

Three ways to measure closeness

Cosine compares direction and ignores length: it is the angle between two vectors. Dot product multiplies and sums, so it rewards both alignment and magnitude (how long the vector is). Euclidean distance measures the straight line between the points, so it cares about absolute position as well as direction. Three sensible definitions, three different rankings. Only one of them matches the way your embedding model was trained.

On unit-length vectors (vectors of length one), cosine and dot product rank the same. Euclidean distance ranks in reverse order of dot product. That match is why the choice often seems not to matter, and why it quietly does once the vectors are not normalised. The test is simple: pick two documents with the same direction and very different lengths. Then see whether your ranking puts them in the order you expect.

The practical result is that “similar” is not one idea. Two documents can be similar because they point the same way, because one is longer and louder, or because they sit near each other in absolute space. The metric is the sentence that says which of those you meant. The embedding model is the sentence that says which of them you can even measure.

What normalisation changes

Dividing a vector by its length removes magnitude. After that the dot product is the cosine, and a long document no longer outranks a short one because its embedding is larger. Some models already return unit vectors; others do not, and the difference is invisible until you measure it. A model card that says “use cosine” is telling you the training loss, not promising that the output is normalised.

Normalise once, at write time, and store the result. Normalising on every query is a small cost that hides a larger question: are the stored vectors normalised too? A collection that is half normalised is worse than one that is not normalised at all. The scores are similar enough to look meaningful and different enough to mislead. A migration that changes normalisation is a re-embedding, not a config change.

Cosine, in one line of numpy

import numpy as np

def cosine(a, b):
    a = a / np.linalg.norm(a)
    b = b / np.linalg.norm(b)
    return float(a @ b)

# embeddings must already be unit-length for this to be a cosine
scores = embeddings @ query
best = np.argsort(-scores)[:5]

Three metrics, three rankings

Run the three rankings, then make document b ten times longer and run them again. Explain which metric changed its mind and why.

Output
Click "Run" to execute your code

Choose the metric the model was trained with

Embedding models are trained with a similarity function in their loss. A model trained with cosine is not guaranteed to behave under raw dot product, and a model trained for dot product may use magnitude as a signal. The model card usually says which one. When it does not, the safe default is cosine on normalised vectors. “Safe” here means the failure is a lower score, not an inverted ranking.

Mixing metrics between write and query is the classic failure. If you index with cosine and query with dot, the top result is often right and the fifth is often wrong. That is the worst kind of bug: it passes the demo and fails the audit. The same thing happens when a library changes its default, or when one service normalises and another does not. Write the metric down where the data lives.

A metric mismatch does not raise an error. Scores stay in a plausible range, the first result usually looks fine, and recall degrades quietly. Write the metric into the collection name or the config, not into a comment.

Distance, similarity and thresholds

Cosine and dot are similarities: bigger is better. Euclidean is a distance: smaller is better. A threshold means different things in each, and a cutoff copied from one to the other will either admit everything or nothing. Pick the direction once, encode it in the function name, and make the caller unable to pass the wrong one by accident.

Thresholds are also model-specific. A cosine of 0.8 is a strong match for one model and a normal one for another. So calibrate the cutoff on your own labelled pairs rather than borrowing a number from a blog post. Plot the score distribution for known matches and known non-matches; the gap between them is the only threshold that means anything.

A good test set makes this concrete: pairs a human marked as duplicates, pairs that share a topic but are not duplicates, and pairs that are unrelated. Score all three groups and look at the overlap. If duplicates and topic-neighbours share a range, no threshold will separate them. Then the metric or the model needs to change rather than the cutoff.

Normalise once, at write time

Compare raw dot product with cosine, then normalise the stored vectors and confirm dot and cosine now agree on every pair.

Output
Click "Run" to execute your code

Check what you learned

One question at a time. A wrong answer explains itself — take another go.

Part 1 of 5

On unit-length vectors, which two metrics rank identically?