Measuring Similarity - From "Near" to Search by Meaning
In Phase 1 you learned that meaning becomes coordinates, and that close means similar. But we waved a hand at the most important word: "close." A computer can't squint at a map - it needs a number, a single value that says "these two vectors are 0.91 similar" or "these are basically unrelated."
This phase makes "close" precise, and then shows the payoff: once you can measure closeness, search by meaning falls out almost for free. You embed the question, you find the nearest stored vectors, you return what they point to. That's it. That's semantic search.
The intuition: same direction means same meaning
What it actually is. The most common way to measure similarity between two embeddings is cosine similarity. Forget the dots on the map for a second and picture an arrow drawn from the origin to each point. Cosine similarity asks one question - do these two arrows point the same way?
^ arrow A: "a small fluffy cat"
| A B arrow B: "a tiny furry kitten"
| ╱ ╱ → point almost the SAME direction
| ╱ ╱ → cosine similarity ≈ 1 (very similar)
|╱╱
+───────────────>
|╲
| ╲ C arrow C: "quarterly tax filing"
| ╲ → points a totally different way
v C → cosine similarity ≈ 0 (unrelated)
What just happened: Arrows A and B point in nearly the same direction, so their cosine similarity is near 1 - the system reads them as meaning almost the same thing. Arrow C heads off elsewhere, so its similarity to A is near 0. Cosine similarity ignores how long each arrow is and cares only about its direction, which is exactly what you want for comparing meaning.
📝 Terminology - cosine similarity. A single number, between -1 and 1, that measures how aligned two vectors are. 1 = pointing the same way (most similar). 0 = at right angles (unrelated). -1 = pointing opposite ways. With text embeddings you'll mostly see values between 0 and 1, "higher = more similar."
Why people get this wrong. The frequent confusion is between similarity and distance - two sides of the same coin, easy to mix up. Higher cosine similarity means more alike (1 is best). Smaller distance means more alike (0 is best). Some tools report one, some the other; a few use plain straight-line (Euclidean) distance instead of cosine. The headache isn't the math - it's forgetting which direction means "better" in the tool in front of you.
⚠️ Gotcha - know whether your tool returns "bigger is better" or "smaller is better." Sort results the wrong way and you'll proudly return the least relevant matches. Before you trust any ranking, confirm: is this a similarity score (sort descending) or a distance (sort ascending)? Check the docs once; it saves a baffling afternoon.
Semantic search: embed the query, find the nearest neighbors
This is the whole point. Everything so far was setup for this one move.
What it actually is. Semantic search means searching by meaning instead of by matching words. Three steps:
- Ahead of time: embed every document you want to be searchable, and store the vectors.
- At search time: embed the user's query with the same model.
- Find the stored vectors nearest to the query vector, and return whatever they point to.
That third step - "find the nearest stored vectors to this one" - is called nearest-neighbor search.
📝 Terminology - nearest-neighbor search. Given one query point, find the stored points closest to it. "Find the 5 nearest" is a k-nearest-neighbors search, often written k=5.
What it does in real life. Here's the move with a small library of stored documents:
STORED (embedded once, ahead of time)
┌────────────────────────────────────────────────┐
│ doc1 "How to reset your password" │
│ doc2 "Recovering a lost account login" │
│ doc3 "Our refund and returns policy" │
│ doc4 "Office holiday hours for December" │
└────────────────────────────────────────────────┘
QUERY: "I can't get into my account"
│
├─ embed with the SAME model → query vector
│
▼
compare query vector to every stored vector (cosine similarity):
doc2 "Recovering a lost account login" 0.89 ◄── nearest
doc1 "How to reset your password" 0.81
doc3 "Our refund and returns policy" 0.12
doc4 "Office holiday hours for December" 0.05
What just happened: The query "I can't get into my account" shares no words with doc2 ("Recovering a lost account login") - not one. A keyword search for "account" would miss the intent entirely. But in meaning space, "can't get into my account" and "recovering a lost account login" point almost the same direction, so doc2 scores highest. The system understood the problem, not the vocabulary.
💡 Key point. This is the superpower in one line: semantic search matches meaning, so it handles synonyms and paraphrase automatically. "Car" finds "automobile." "How do I cancel" finds "ending your subscription." "It's broken" finds "troubleshooting a malfunction." You didn't write any synonym lists - the embedding model already knows these things mean the same.
Where it beats keyword search - and where it doesn't
Semantic search is not strictly better than keyword search; it's different, and the best systems often use both.
| Situation | Keyword search | Semantic search |
|---|---|---|
| Query uses different words than the doc ("can't log in" vs "account recovery") | Misses it | Finds it |
| Synonyms / paraphrase / typos in meaning | Misses unless you maintain synonym lists | Handles naturally |
Exact match needed: an error code, a SKU, a function name like parseDate |
Nails it | May drift to "similar-looking" but wrong results |
| Rare proper noun the model never learned well | Finds the literal string | Can fumble it |
| Explaining why a result matched | Easy - show the matched word | Hard - "the vectors were close" isn't satisfying |
What just happened: Each method wins where the other is weak. Keyword search is literal and precise; semantic search is flexible and meaning-aware. Production search frequently runs both and blends the rankings - a pattern called hybrid search. You don't choose a side; you choose per query, or run both and merge.
⚠️ Gotcha - exact identifiers are semantic search's blind spot. Search for the error code ERR_2048 or part number XJ-9920, and embeddings may happily return things that look or feel similar instead of the exact match. Anything where the literal string is the point - codes, IDs, names - is where you want keyword matching in the mix.
Why this saves you later. Almost every "AI that finds the right thing" feature - support-article search, recommendation, deduplication, the retrieval step inside chatbots - is this exact pattern: embed, find nearest, return. When you build RAG, the "retrieval" half is the semantic search you just learned.
Recap
- Cosine similarity measures whether two vectors point the same direction: 1 = most similar, 0 = unrelated.
- Similarity (bigger is better) and distance (smaller is better) are two ways to say the same thing - always know which your tool returns.
- Semantic search = embed every document ahead of time, embed the query with the same model, return the nearest neighbors.
- Because it matches meaning, it handles synonyms and paraphrase for free - its biggest advantage over keyword search.
- It's weak on exact identifiers; serious systems often blend semantic and keyword search.
You can now reason about search by meaning end to end on a small library. The last question is the practical one: what happens when "compare the query to every stored vector" means comparing against ten million of them? That's the next phase - plus the three gotchas that quietly wreck real systems.
← Phase 1: Meaning as Coordinates · Guide overview · Phase 3: Vector Databases & the Gotchas →
Before the quiz: without looking back, say (or jot down) the core idea of this phase in your own words.
Check your understanding 2 questions
1. Cosine similarity measures...
2. Semantic search works by...