IVF — carve the space into cells
IVF runs k-means once to split the space into cells around centroids, then at query time scans only the few cells nearest the query — turning an all-N scan into a small fraction of it, the first explicit recall-for-speed trade.
Flat touched all N vectors; IVF carves the space into cells and touches only the nprobe cells near the query, buying a 10-100× speedup for a sliver of recall. But that sliver has a specific, repeatable shape — and it's worth its own scene, because it's the gotcha that bites every junior who ships IVF.
Scene 04
IVF — carve the space into cells
- Watch
- Try it
- Predict
- Capture
Flat's whole problem was that it compared the query against every single song. IVF — short for inverted file index — fixes that with one upfront step: it runs a clustering pass called k-means that drops a handful of marker points (centroids) into the space, and every song joins whichever centroid it's nearest to. The colored region a centroid owns is its cell. Watch the centroids drop in and the boundaries snap shut — the map is now carved into a few neighborhoods. The query 'Now Playing' lands squarely in the middle cell, alongside Midnight Drive, Synth Dawn, and Study Beats. (A common rule of thumb sizes the number of cells around 4·√N to 16·√N — small enough that each holds a real neighborhood.)
Highlighted lines are the ones running in the diagram right now.
def train(vectors, nlist):# Lloyd's algorithm: nlist marker pointscentroids = kmeans(vectors, k = nlist)# inverted lists: cell_id -> vectors in that celllists = {c: [] for c in range(nlist)}for v in vectors:c = argmin(dist(v, centroids)) # nearest celllists[c].append(v)return centroids, lists
def probe_set(query, centroids, nprobe):# rank cells by centroid distance to the queryordered = sort(range(nlist),key = lambda c: dist(query, centroids[c]),)# open only the nprobe nearest; skip the restreturn ordered[:nprobe]
def search(query, centroids, lists, nprobe, k):cells = probe_set(query, centroids, nprobe)candidates = []for c in cells: # not all nlist cellsfor v in lists[c]: # only the opened listscandidates.append((dist(query, v), v))return smallest_k(candidates, k)
Where this sits in Build a vector database (Pinecone / Weaviate / pgvector style)
Scene 04 of 15, in the First index act — Flat baseline, IVF cells, and the boundary miss that bites every junior.. k-means splits the space into Voronoi cells; at query time scan only the nprobe cells nearest the query — turning an all-N scan into a fraction of it. The first explicit recall-for-speed trade.
Up next. We saw 'Lonely Synth' vanish at nprobe=1. That wasn't bad luck — it's the signature failure of cell-based search, and it happens to the obvious neighbor sitting just on the wrong side of a boundary. Let's freeze that one picture and understand exactly why a clearly-close point gets missed.
All 15 scenes in Build a vector database (Pinecone / Weaviate / pgvector style) · Every curriculum