Learning 04 · what “embedding” actually names
The word “embedding” gets attached to at least three different things. The confusion that matters: “when I use an embedding model for search/RAG, am I just reading rows out of the learned table inside an LLM?” Short answer — no. This note pins down why.
token_embd.weight (ds4.c:3033), gathered by metal/get_rows.metal
🧭 Lineage · word2vec/GloVe → sentence-transformers / BGE / E5 / text-embedding-3
One word, three referents
Drawn apart, the three are easy to keep straight — the split that matters is lookup vs activation vs output, and context-free vs context-aware:
Shape [vocab, d_model], one vector per token. A static
lookup: token 15339 → row 15339, the same vector
every time, regardless of neighbors — context-free. It is the
network's input layer. The only sense our engine uses.
The vectors between layers, after attention has mixed in surrounding context. Context-dependent: “bank” gets a different vector in “river bank” vs “bank account.” They are activations computed by a forward pass — not stored anywhere.
One fixed-size vector for an entire sentence or document — for semantic search, RAG, clustering, dedup. It is the output of running text through a whole network and then pooling.
When you call an “embedding model” (text-embedding-3, BGE, E5,
sentence-transformers, …) you get #3 — not the table from #1.
Not a lookup — a forward pass
For each input text it runs a full forward pass. The learned token table is in there — it's the front door — but the vector you receive is the product of the entire network, squashed into one:
Two more things distinguish a real embedding model from a raw LLM's table:
A generative LLM trains on next-token prediction. An embedding model is trained with a contrastive objective (InfoNCE / cosine loss): shown (query, relevant doc, irrelevant doc), it pushes relevant vectors together and irrelevant ones apart. That is what makes its output geometry good for cosine comparison — a raw LLM's token rows are not, off the shelf.
Many embedding models are smaller, encoder-only / bidirectional (BERT-style), specialized for this job — not decoder LLMs at all.
Where the “pull the table out” intuition is right
“Train a net, throw it away, keep the table” perfectly describes word2vec and GloVe (~2013). Their entire output was a static word→vector table: train a shallow net on a context-prediction task, discard the net, keep the table. A lookup was a pure table read — context-free, exactly sense #1. Modern sentence-embedding models replaced that because the static table has hard limits:
| word2vec / GloVe (old) | embedding model (modern) | |
|---|---|---|
| What you use | the table itself (a lookup) | the output of a forward pass |
| Context-aware? | No — one vector per word, forever | Yes — same word, different vector by context |
| Word order | ignored | captured (attention) |
| Granularity | per word | per sentence / document |
| Trained for | predicting nearby words | similarity (contrastive) |
The one to keep
A lookup in the model's input table; context-free; one per token; it's the input. The only sense our inference engine touches.
A forward pass + pooling over a separate, similarity-trained network; context-aware; one per text; it's the output.
ds4 · token_embd.weight
gathered by metal/get_rows.metal — the lookup, sense #1, in a real engine.docs/learnings/04-embedding-models.md.