Thomas Piacentini
← Workshop

Cheat sheet

Embeddings and Vector Search

Original text · English

Encode text, pick a metric, size the vectors, choose an index and fix bad recall. Python, NumPy and FAISS.

Retrieve, then rerank

chunksembedaddsearchtop 10Documentssplit into chunksBi-encodertext to vectorL2 normalizeunit lengthVector indexHNSW or flatQuerysame model, query prefixTop 10to the readerCross-encoderscores pairsTop 100ANN search
Nodes and connections
NodeRoleKind
Documentssplit into chunks
Bi-encodertext to vector
L2 normalizeunit length
Vector indexHNSW or flat
Querysame model, query prefix
Top 10to the reader
Cross-encoderscores pairs
Top 100ANN search
FromToConnection
DocumentsBi-encoderchunks
QueryBi-encoderembed
Bi-encoderL2 normalize
L2 normalizeVector indexadd
Vector indexTop 100search
Top 100Cross-encoder
Cross-encoderTop 10top 10
Sources

Encode, search, truncate

encoder = SentenceTransformer("BAAI/bge-small-en-v1.5")384 dim
embeddings = encoder.encode(corpus, normalize_embeddings=True)unit vectors
q_emb = encoder.encode([query], normalize_embeddings=True)
sims = (embeddings @ q_emb.T).flatten()dot equals cosine
order = np.argsort(-sims)[:top_k]best first
def truncate(vectors, dim):Matryoshka cut
out = vectors[:, :dim]
return out / np.linalg.norm(out, axis=1, keepdims=True)renormalize
emb_256 = truncate(emb, 256)
index = faiss.IndexFlatL2(dimension)exact search
index.add(embeddings)
distances, indices = index.search(query_vectors, k=5)
Sources

Tasks

TaskCode
Truncate one vectortruncate_matryoshka(vec, 64)
Binarize a vectorbinarize(vec)
Search by Hamming distanceengine.index.search(query_vec, top_k=5, metric="hamming")
Fuse dense and sparse ranksrrf_fuse([dense_ranked[:5], sparse_scores[:5]])
Load BGE-M3BGEM3FlagModel("BAAI/bge-m3", use_fp16=True)
Run an analogyanalogy(vocab, W, "man", "king", "woman")
Vector for an unseen wordfasttext_vector(word, ngram_table)
Evaluate on MTEB tasksevaluation.run(encoder, output_folder="./mteb-results")
Sources

Similarity math

Cosine
\cos(a, b) = \frac{a \cdot b}{\lVert a \rVert \, \lVert b \rVert}. Range -1 to 1. Ignores length.
Cosine distance
1 - \cos(a, b). Range 0 to 2.
Dot product
\sum_i a_i b_i. Equals cosine on unit vectors.
L2
\sqrt{\sum_i (a_i - b_i)^2}. Smaller is closer.
Unit vectors
\lVert a - b \rVert^2 = 2 - 2 \cos(a, b).
Hamming
count of different bits after binarize.
MaxSim
best doc token per query token, then sum.
RRF
\sum \frac{1}{k + \text{rank} + 1} over rankings, rank from 0.
Sources

Choose an index

CommandWhat it does
IndexFlatIPExact inner product. Cosine on unit vectors. About 100k vectors
IndexFlatL2Exact L2, no training. Up to about 1M vectors
IndexIVFFlatK cells, searches the closest few. Needs training. About 10M
IndexIVFPQIVF plus product quantization. 100M and more
IndexHNSWGraph index. Fastest for many queries. Large index
LSHRandom hash projections. Near-duplicate detection
KD-trees, ball treesExact. Low and medium dimension only
Sources

Size and compression

float32, 1536 dim
6,144 bytes per vector. 61 GB for 10 million.
3,072 dim
12 KB per vector at full precision.
Binary
192 bytes per vector, 32x smaller. 5 to 10 percent recall loss.
Rescore
binary first pass, then full precision on the top 1000.
Matryoshka 256 of 1536
6x less storage, about 3 to 5 percent loss.
Safe cut
1,536 to 256 is usually safe. 1,536 to 64 is not.
Sources

Defaults in the code

SettingDefaultNote
chunk_size200Words per chunk, chunk_text
overlap50Words shared by two chunks
top_k5Results per search
metric"cosine"Also dot, euclidean, hamming
RRF k60rrf_fuse
hash_embed dim256Hash trick demo embedder
n_min3FastText n-grams, n_max 6
x_max100GloVe weighting, alpha 0.75
Sources

Troubleshooting

SymptomCauseFix
bge recall 3 to 5 points lowNo query prefixPrepend the prefix from the model card
Truncated vectors rank badlyNo renormalize after the cutDivide by the norm again
Recall falls after truncationCut too far, or no Matryoshka trainingValidate on your eval set
Nearest neighbors changeCosine and L2 conventions mixedMatch the training metric
Query and doc do not matchAsymmetric modelCheck the model card
cosine: dim mismatchVectors of different sizeOne model and one dim for both
Long docs match nothingOne vector averages all topicsChunk 256 to 512 tokens, 50 overlap
Unseen word has no vectorWord2Vec and GloVe have no fallbackUse FastText n-grams
Sources

Abbreviations

ANN
Approximate Nearest Neighbor. Small accuracy loss, large speed gain.
HNSW
Hierarchical Navigable Small World. Multi-layer graph index.
IVF
Inverted file index. Searches a few clusters only.
PQ
Product quantization. Search in compressed space.
LSH
Locality-sensitive hashing. Random hash projections.
MTEB
Massive Text Embedding Benchmark. 56 tasks at launch, 100+ in v2.
BEIR
18 zero-shot retrieval tasks.
OOV
Out of vocabulary. A word not seen in training.
Sources

Questions

Cosine or dot product?
On L2-normalized vectors the choice does not matter.
Why ANN?
Brute force at 1M by 1536 is 1.5 billion multiply-adds per query.
Why rerank?
A cross-encoder reads query and doc together. It is slower but more accurate.
Why RRF, not a score sum?
BM25 is unbounded. Cosine is in -1 to 1. Ranks compare.
Which number for a RAG retriever?
Recall@k. No right passage, no answer.
Sources

Tips

  • Always set normalize_embeddings=True.
  • Benchmark on your own queries. Leaderboard rank is not enough.
  • Swap a model only if it wins on your domain by more than 3 points.
  • Report recall@1, @5 and @10 side by side.
  • Measure p99 latency. MTEB scores hide it.
  • For a failing query, diff the sparse and dense rankings.
Sources
Original source
---
id: embeddings-vector-search
title: Embeddings and Vector Search
code: EV
subtitle: Encode text, pick a metric, size the vectors, choose an index and fix bad recall. Python, NumPy and FAISS.
hue: 170
sources: phases/11-llm-engineering/04-embeddings, phases/05-nlp-foundations-to-advanced/22-embedding-models-deep-dive, phases/05-nlp-foundations-to-advanced/03-word-embeddings-word2vec, phases/05-nlp-foundations-to-advanced/04-glove-fasttext-subword, phases/05-nlp-foundations-to-advanced/14-information-retrieval-search, phases/01-math-foundations/14-norms-and-distances, phases/02-ml-fundamentals/06-knn-and-distances, phases/04-computer-vision/20-image-retrieval-metric, phases/19-capstone-projects/65-hybrid-retrieval-bm25-dense
source_hash: 16665e2c8ff93f4e
---

## Retrieve, then rerank
type: architecture
from: phases/11-llm-engineering/04-embeddings/docs/en.md, phases/05-nlp-foundations-to-advanced/22-embedding-models-deep-dive/docs/en.md, phases/05-nlp-foundations-to-advanced/14-information-retrieval-search/docs/en.md

```arch
node | docs | 1,1 | Documents | split into chunks
node | enc | 2,1 | Bi-encoder | text to vector
node | norm | 3,1 | L2 normalize | unit length
node | idx | 4,1 | Vector index | HNSW or flat
node | q | 1,2 | Query | same model, query prefix
node | out | 2,2 | Top 10 | to the reader
node | rerank | 3,2 | Cross-encoder | scores pairs
node | topk | 4,2 | Top 100 | ANN search
edge | docs | enc | chunks
edge | q | enc | embed
edge | enc | norm |
edge | norm | idx | add
edge | idx | topk | search
edge | topk | rerank |
edge | rerank | out | top 10
```

## Encode, search, truncate
type: annotated-code
lang: python
from: phases/05-nlp-foundations-to-advanced/14-information-retrieval-search/docs/en.md, phases/05-nlp-foundations-to-advanced/22-embedding-models-deep-dive/docs/en.md, phases/02-ml-fundamentals/06-knn-and-distances/docs/en.md

```python
encoder = SentenceTransformer("BAAI/bge-small-en-v1.5")  # 384 dim
embeddings = encoder.encode(corpus, normalize_embeddings=True)  # unit vectors
q_emb = encoder.encode([query], normalize_embeddings=True)
sims = (embeddings @ q_emb.T).flatten()  # dot equals cosine
order = np.argsort(-sims)[:top_k]        # best first
def truncate(vectors, dim):              # Matryoshka cut
    out = vectors[:, :dim]
    return out / np.linalg.norm(out, axis=1, keepdims=True)  # renormalize
emb_256 = truncate(emb, 256)
index = faiss.IndexFlatL2(dimension)     # exact search
index.add(embeddings)
distances, indices = index.search(query_vectors, k=5)
```

## Tasks
type: task-table
from: phases/05-nlp-foundations-to-advanced/22-embedding-models-deep-dive/docs/en.md, phases/05-nlp-foundations-to-advanced/22-embedding-models-deep-dive/code/main.py, phases/11-llm-engineering/04-embeddings/code/embeddings.py, phases/05-nlp-foundations-to-advanced/03-word-embeddings-word2vec/docs/en.md, phases/05-nlp-foundations-to-advanced/04-glove-fasttext-subword/docs/en.md

| Task | Code |
|---|---|
| Truncate one vector | `truncate_matryoshka(vec, 64)` |
| Binarize a vector | `binarize(vec)` |
| Search by Hamming distance | `engine.index.search(query_vec, top_k=5, metric="hamming")` |
| Fuse dense and sparse ranks | `rrf_fuse([dense_ranked[:5], sparse_scores[:5]])` |
| Load BGE-M3 | `BGEM3FlagModel("BAAI/bge-m3", use_fp16=True)` |
| Run an analogy | `analogy(vocab, W, "man", "king", "woman")` |
| Vector for an unseen word | `fasttext_vector(word, ngram_table)` |
| Evaluate on MTEB tasks | `evaluation.run(encoder, output_folder="./mteb-results")` |

## Similarity math
type: concepts
from: phases/11-llm-engineering/04-embeddings/docs/en.md, phases/11-llm-engineering/04-embeddings/code/embeddings.py, phases/01-math-foundations/14-norms-and-distances/docs/en.md, phases/04-computer-vision/20-image-retrieval-metric/docs/en.md, phases/05-nlp-foundations-to-advanced/22-embedding-models-deep-dive/docs/en.md, phases/05-nlp-foundations-to-advanced/22-embedding-models-deep-dive/code/main.py

- **Cosine**: $\cos(a, b) = \frac{a \cdot b}{\lVert a \rVert \, \lVert b \rVert}$. Range -1 to 1. Ignores length.
- **Cosine distance**: $1 - \cos(a, b)$. Range 0 to 2.
- **Dot product**: $\sum_i a_i b_i$. Equals cosine on unit vectors.
- **L2**: $\sqrt{\sum_i (a_i - b_i)^2}$. Smaller is closer.
- **Unit vectors**: $\lVert a - b \rVert^2 = 2 - 2 \cos(a, b)$.
- **Hamming**: count of different bits after binarize.
- **MaxSim**: best doc token per query token, then sum.
- **RRF**: $\sum \frac{1}{k + \text{rank} + 1}$ over rankings, rank from 0.

## HNSW search
type: flow
from: phases/11-llm-engineering/04-embeddings/docs/en.md, phases/01-math-foundations/14-norms-and-distances/docs/en.md

1. **Build**: put each vector in a multi-layer graph, linked to approximate neighbors.
2. **Top layers**: sparse, with long links between distant clusters.
3. **Bottom layer**: dense, holds all vectors with short links.
4. **Enter**: the query starts at the top layer.
5. **Descend**: greedy moves to closer nodes, then one layer down.
6. **Return**: approximate top-k in $O(\log n)$, not $O(n)$.
7. **Cost**: typically 95 to 99 percent recall. Milliseconds at 10 million vectors.

## Choose an index
type: command-table
from: phases/04-computer-vision/20-image-retrieval-metric/docs/en.md, phases/01-math-foundations/14-norms-and-distances/docs/en.md, phases/02-ml-fundamentals/06-knn-and-distances/docs/en.md

| Command | What it does |
|---|---|
| `IndexFlatIP` | Exact inner product. Cosine on unit vectors. About 100k vectors |
| `IndexFlatL2` | Exact L2, no training. Up to about 1M vectors |
| `IndexIVFFlat` | K cells, searches the closest few. Needs training. About 10M |
| `IndexIVFPQ` | IVF plus product quantization. 100M and more |
| `IndexHNSW` | Graph index. Fastest for many queries. Large index |
| LSH | Random hash projections. Near-duplicate detection |
| KD-trees, ball trees | Exact. Low and medium dimension only |

## Size and compression
type: concepts
from: phases/11-llm-engineering/04-embeddings/docs/en.md, phases/05-nlp-foundations-to-advanced/22-embedding-models-deep-dive/docs/en.md

- **float32, 1536 dim**: 6,144 bytes per vector. 61 GB for 10 million.
- **3,072 dim**: 12 KB per vector at full precision.
- **Binary**: 192 bytes per vector, 32x smaller. 5 to 10 percent recall loss.
- **Rescore**: binary first pass, then full precision on the top 1000.
- **Matryoshka 256 of 1536**: 6x less storage, about 3 to 5 percent loss.
- **Safe cut**: 1,536 to 256 is usually safe. 1,536 to 64 is not.

## Defaults in the code
type: defaults
from: phases/11-llm-engineering/04-embeddings/code/embeddings.py, phases/05-nlp-foundations-to-advanced/22-embedding-models-deep-dive/code/main.py, phases/05-nlp-foundations-to-advanced/04-glove-fasttext-subword/docs/en.md

| Setting | Default | Note |
|---|---|---|
| `chunk_size` | 200 | Words per chunk, `chunk_text` |
| `overlap` | 50 | Words shared by two chunks |
| `top_k` | 5 | Results per search |
| `metric` | `"cosine"` | Also dot, euclidean, hamming |
| RRF `k` | 60 | `rrf_fuse` |
| `hash_embed` dim | 256 | Hash trick demo embedder |
| `n_min` | 3 | FastText n-grams, `n_max` 6 |
| `x_max` | 100 | GloVe weighting, `alpha` 0.75 |

## Troubleshooting
type: troubleshooting
from: phases/05-nlp-foundations-to-advanced/22-embedding-models-deep-dive/docs/en.md, phases/05-nlp-foundations-to-advanced/22-embedding-models-deep-dive/code/main.py, phases/04-computer-vision/20-image-retrieval-metric/docs/en.md, phases/11-llm-engineering/04-embeddings/docs/en.md, phases/05-nlp-foundations-to-advanced/03-word-embeddings-word2vec/docs/en.md, phases/05-nlp-foundations-to-advanced/04-glove-fasttext-subword/docs/en.md

| Symptom | Cause | Fix |
|---|---|---|
| bge recall 3 to 5 points low | No query prefix | Prepend the prefix from the model card |
| Truncated vectors rank badly | No renormalize after the cut | Divide by the norm again |
| Recall falls after truncation | Cut too far, or no Matryoshka training | Validate on your eval set |
| Nearest neighbors change | Cosine and L2 conventions mixed | Match the training metric |
| Query and doc do not match | Asymmetric model | Check the model card |
| `cosine: dim mismatch` | Vectors of different size | One model and one dim for both |
| Long docs match nothing | One vector averages all topics | Chunk 256 to 512 tokens, 50 overlap |
| Unseen word has no vector | Word2Vec and GloVe have no fallback | Use FastText n-grams |

## Abbreviations
type: glossary
from: phases/01-math-foundations/14-norms-and-distances/docs/en.md, phases/11-llm-engineering/04-embeddings/docs/en.md, phases/05-nlp-foundations-to-advanced/22-embedding-models-deep-dive/docs/en.md, phases/05-nlp-foundations-to-advanced/03-word-embeddings-word2vec/docs/en.md

- **ANN**: Approximate Nearest Neighbor. Small accuracy loss, large speed gain.
- **HNSW**: Hierarchical Navigable Small World. Multi-layer graph index.
- **IVF**: Inverted file index. Searches a few clusters only.
- **PQ**: Product quantization. Search in compressed space.
- **LSH**: Locality-sensitive hashing. Random hash projections.
- **MTEB**: Massive Text Embedding Benchmark. 56 tasks at launch, 100+ in v2.
- **BEIR**: 18 zero-shot retrieval tasks.
- **OOV**: Out of vocabulary. A word not seen in training.

## Questions
type: qa
from: phases/11-llm-engineering/04-embeddings/docs/en.md, phases/01-math-foundations/14-norms-and-distances/docs/en.md, phases/19-capstone-projects/65-hybrid-retrieval-bm25-dense/docs/en.md, phases/05-nlp-foundations-to-advanced/14-information-retrieval-search/docs/en.md, phases/04-computer-vision/20-image-retrieval-metric/docs/en.md

- **Cosine or dot product?** On L2-normalized vectors the choice does not matter.
- **Why ANN?** Brute force at 1M by 1536 is 1.5 billion multiply-adds per query.
- **Why rerank?** A cross-encoder reads query and doc together. It is slower but more accurate.
- **Why RRF, not a score sum?** BM25 is unbounded. Cosine is in -1 to 1. Ranks compare.
- **Which number for a RAG retriever?** Recall@k. No right passage, no answer.

## Tips
type: tips
from: phases/05-nlp-foundations-to-advanced/22-embedding-models-deep-dive/docs/en.md, phases/04-computer-vision/20-image-retrieval-metric/docs/en.md, phases/05-nlp-foundations-to-advanced/14-information-retrieval-search/docs/en.md, phases/11-llm-engineering/04-embeddings/docs/en.md

- Always set `normalize_embeddings=True`.
- Benchmark on your own queries. Leaderboard rank is not enough.
- Swap a model only if it wins on your domain by more than 3 points.
- Report recall@1, @5 and @10 side by side.
- Measure p99 latency. MTEB scores hide it.
- For a failing query, diff the sparse and dense rankings.
Code and artifacts 1
What you take away

Saved in your notebook on this device.

Sources and limits

Reading checkpoints are self-reported. Test results are recorded separately and apply to the executed cases.

AI Engineering from Scratch · 3f9715c · MIT

Download this file ↓