Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Chapter 1 ended on a specific failure. Our retrieval system counted shared words between a question and a document, and it worked, right up until someone asked “do unused holidays roll over to next year?” instead of using the word “vacation.” Same question, different words, zero words in common with the document that actually answers it. The retriever picked the wrong document, or a tie, and the generate step never had a chance to save it.

“Holiday” and “vacation” mean the same thing to a person. To a computer comparing spelling, they’re as unrelated as “holiday” and “giraffe.” This chapter fixes that, by changing what we compare: not words, but meaning.

Chapter 2 of 10 · Foundations

Where we left off: Chapter 1’s word-matching retriever tied or lost on a question that used “holidays” instead of “vacation,” because it only compares spelling.

What we fix now: Turn text into vectors so meaning, not spelling, determines what counts as similar, built from scratch with nothing but word co-occurrence counts and a bit of linear algebra.

By the end, you can:

  • explain what an embedding is and what cosine similarity measures

  • build a tiny embedding model from scratch and use it to fix Chapter 1’s exact failure

  • name two concrete limits of this toy model, and why production models handle them differently

The core idea

An embedding turns a piece of text into a list of numbers, a vector, chosen so that texts with similar meaning end up as vectors that are close together, and texts with different meaning end up far apart. “Vacation” and “holiday” should land near each other in this space. “Vacation” and “printer” should not.

That’s the entire idea. Once text is a vector, “how similar are these two pieces of text” becomes a question with actual math behind it: measure the distance, or more commonly the angle, between two vectors. Close together (small angle) means similar meaning. Far apart (large angle) means unrelated.

The hard part is earning that property in the first place: how do you turn a sentence into numbers such that “meaning” falls out of the geometry? That’s what the rest of this chapter builds, from scratch, so you can see exactly where those numbers come from instead of treating them as a black box.

Where real embedding numbers come from

Production systems (OpenAI’s, Cohere’s, the open-source sentence-transformers models) train large neural networks on billions of words to produce these vectors. We can’t run that in a browser tab. But the core mechanism those models build on is much older and simple enough to build live, right here, with nothing but counting and a bit of linear algebra:

  1. Take a body of text (a corpus).

  2. For every pair of words, count how often they show up near each other across that corpus. This gives a big table: one row per word, and each row is a fingerprint of “which words tend to appear around me.”

  3. Two words that keep similar company end up with similar fingerprints, even if the two words themselves are never adjacent. “Holiday” and “vacation” both tend to show up near words like “days,” “unused,” and “employees,” so their fingerprints look alike, well before anything is told what either word “means.”

  4. Compress those long, mostly-redundant fingerprints down into a short list of numbers per word (a handful of dimensions instead of hundreds), keeping whatever pattern explains the most variation. That compressed fingerprint is the embedding.

This co-occurrence-then-compress approach is the same lineage as classic techniques like LSA, and conceptually close to how GloVe (a well-known pretrained embedding model) was built, just at a vastly smaller scale here: dozens of sentences instead of billions of words. To be clear about what you’re about to run: this is not a download of some official pretrained file. It’s a tiny embedding model, trained live, in your browser, on the twenty sentences below, in a fraction of a second.

Measuring “close together”

With every word turned into a vector, we need a way to measure how close two vectors are. The standard tool is cosine similarity: the cosine of the angle between two vectors.

cosine_similarity(a,b)=a⋅b∥a∥ ∥b∥\text{cosine\_similarity}(a, b) = \frac{a \cdot b}{\lVert a \rVert \, \lVert b \rVert}

where a⋅ba \cdot b is the dot product (∑iaibi\sum_i a_i b_i) and ∥a∥\lVert a \rVert is the vector’s length (∑iai2\sqrt{\sum_i a_i^2}). The result always falls between -1 and 1: values closer to 1 indicate stronger directional similarity, values near 0 indicate weak directional similarity, and negative values indicate vectors oriented in opposing directions. Geometrically that’s precise; whether “opposite direction” also means “opposite meaning” is a separate question, and the answer depends on the specific embedding model, not something to assume automatically. Dividing by the lengths is what makes this about direction (meaning) rather than magnitude (how long the vector happens to be), which matters because nothing here guarantees every word’s vector comes out the same length.

You don’t need to memorize this formula to use embeddings, most libraries compute it for you in one line, but it’s worth seeing once so “similarity score” stops being magic.

“holiday” and “vacation” score meaningfully higher than “holiday” paired with “expense” or “stipend”, topic words that never showed up in the same sentences. Nobody told this model that holidays and vacations are the same idea; it fell out of which words tend to surround them in the corpus. Try editing the pairs list above with your own word combinations from the corpus and re-run the cell.

In Chapter 1, this exact question tied or lost against the wrong document, because it shares zero words with the vacation policy. Here, the vacation document wins clearly, not because it shares more words (it still doesn’t), but because “holidays roll over to next year” and “vacation days into the next calendar year” point in nearly the same direction in this vector space. Edit question above and try your own phrasing, including ones that share no words at all with any document, and re-run the cell.

Before · word matching (Chapter 1)

Same question: “do unused holidays roll over to next year?”

Shared words with the vacation document: 4, but all four are generic connectors (“unused,” “over,” “next,” “to”). Zero overlap on the words that actually carry the topic, “holiday” or “vacation.”

After · embeddings (this chapter)

Same question: “do unused holidays roll over to next year?”

Cosine similarity with the vacation document: 0.943, next closest document only 0.515. This win comes from genuine semantic closeness between “holiday” and “vacation,” not lucky word overlap.

Seeing it, not just measuring it

Numbers on their own can be hard to trust. Here’s the same idea as a picture: compress each word down to just 2 dimensions (instead of 20) and plot them. Words with related meaning should end up near each other on the page.

Look for the two loose clusters: vacation-and-holiday-flavored words on one side, remote-and-office-flavored words on another, with expense-and-purchase words forming their own neighborhood. Nothing forced that grouping; it’s a side effect of which words share sentence neighborhoods in the corpus above.

Bonus (Colab only): a real pretrained embedding model

This cell installs sentence-transformers and runs all-MiniLM-L6-v2, a real pretrained transformer model trained on far more than twenty sentences. It’s here so you can compare its output to the toy model above, not because you need to understand how it works internally yet, that’s a later chapter.

Quick check

Before moving on, answer for yourself (no peeking ahead):

  1. In your own words, what does it mean for two words to be “close together” as vectors, and what does the model actually measure to decide that, since nobody tells it what any word means?

Check your reasoning ▸

A strong answer describes vectors whose angle (measured via cosine similarity) is small, and explains that closeness comes purely from co-occurrence statistics: words that tend to appear near the same neighboring words end up with similar fingerprints, with no step where anyone tells the model what a word means.

  1. The toy model in this chapter never saw the word “furlough.” What happens if you try to embed a question containing it, and why?

Check your reasoning ▸

embedding(“furlough”) returns nothing, because the model only has vectors for words it saw in its twenty-sentence training corpus. A good answer connects this to a real limitation: this approach can’t represent any word it wasn’t trained on, unlike production models trained on billions of words, some of which can still build a reasonable vector for an unfamiliar word by breaking it into familiar sub-word pieces.

  1. Why does a single fixed vector per word struggle with a word that has more than one meaning, such as “remote”? What would need to change to fix that?

Check your reasoning ▸

Because this model builds exactly one vector per word from co-occurrence statistics baked in ahead of time, it can’t distinguish “remote work” from a TV “remote,” both senses blend into the same point. Fixing it requires a model that looks at the surrounding sentence before producing a vector, so the same word gets a different vector depending on context, which is what real neural embedding models do.

  1. Comparing a question against 3 documents with cosine similarity is instant. What do you think starts to break down once there are 3 million documents instead of 3, and why might “compare against everything, one at a time” stop being a workable strategy?

Check your reasoning ▸

A good answer recognizes this as a scaling problem: one cosine similarity is cheap, but three million of them for every single question adds up, and that cost is paid again for every new question. It doesn’t need the specific fix (that’s Chapter 6), just the recognition that linear, compare-everything search stops being fast at real-world scale.

  1. Chapter 1’s retriever technically got the vacation-vs-holiday question right too, just for the wrong reason. Why does “got the right document, for the wrong reason” matter, and can you think of a slightly different question where that same lucky word overlap would have picked the wrong document instead?

Check your reasoning ▸

A strong answer notices Chapter 1’s score came entirely from generic connector words like “unused” and “next,” not from recognizing “holiday” as related to “vacation,” so it was luck, not understanding. A good transfer example reuses those same connector words about a different topic, for instance “do unused sick days roll over to next year” would also share “unused,” “over,” “next,” “to” with the vacation document, and could out-score a real sick-leave document by the same accidental mechanism.


You can now: turn text into vectors and use cosine similarity to compare meaning instead of spelling, and you’ve watched it genuinely fix Chapter 1’s exact failure.

But one problem remains: every document so far has been one sentence. Real documents are pages long, and averaging an entire multi-topic document into one vector blends everything together.

Next → Chapter 3: Chunking Long Documents