IVFPQ — skip most cells, compress the rest
IVFPQ stacks the two earlier ideas — IVF cells skip most of the data and PQ compresses what survives (quantizing each vector's residual from its centroid) — giving the FAISS billion-scale workhorse: small memory AND a fraction-of-N scan, paid for with compounded approximation that re-ranking partly buys back.
PQ shrank the vectors but still touched all of them; IVF skipped most vectors but stored them whole. IVFPQ does both — open only nearby cells, then store each survivor as a tiny compressed residual — and becomes the only index that fits a billion vectors in RAM at speed. We now have four very different dots on the chart, and it's time to see what they collectively prove.
Scene 09
IVFPQ — skip most cells, compress the rest
- Watch
- Try it
- Predict
- Capture
We're not inventing anything new here — we're stacking two tools you already have. IVF carves the 14 songs into a few cells and, for the query 'Now Playing', opens only the nearby ones (everything else stays dimmed and unscanned). Then, for the songs that survive in those opened cells, PQ shrinks each one. The twist: PQ doesn't compress the raw vector — it compresses the RESIDUAL, which just means 'how far this point sits from the centre of its cell.' Because every point in a cell is already huddled near that centre, the residual is a small wobble, and small wobbles round to a prototype far more accurately than a whole vector would. Watch the byte counter collapse from 6,144 bytes (1536 dimensions × 4 bytes each) down to a handful, while only the nearby cells light up.
Highlighted lines are the ones running in the diagram right now.
def add(vec):c = nearest_centroid(vec) # IVF: which cellresidual = vec - centroids[c] # the small leftovercode = pq.encode(residual, m) # m one-byte idscells[c].append((id_of(vec), code))
def search(q, k, nprobe, rerank):near = nprobe_nearest_cells(q, nprobe)cands = []for c in near: # the rest stay closedcands += pq_score(q, c)short = top(cands, 4 * k) # cheap shortlistif rerank:short = exact_rescore(q, short)return top(short, k)
def pq_score(q, c):qr = q - centroids[c] # query residual for this cell# one table per subvector: qr-chunk -> each prototypetable = adc_tables(qr, m)out = []for (id, code) in cells[c]:d = sum(table[j][code[j]] for j in range(m))out.append((id, d))return out
Where this sits in Build a vector database (Pinecone / Weaviate / pgvector style)
Scene 09 of 15, in the Compress & pick act — PQ shrinks 6 KB to 96 bytes, IVFPQ combines both, and the trilemma forces a choice.. Stack IVF (skip most cells) and PQ (compress the residual): the FAISS billion-scale workhorse — tiny memory and a fraction-of-N scan, paid for with compounded approximation.
Up next. Look at the four dots we've placed: Flat owns recall, HNSW owns speed-at-recall but eats RAM, IVFPQ owns memory but eats recall. No single dot is best at everything. That's not an accident of our toy dataset — it's the organizing law of the whole field. Let's make it explicit.
All 15 scenes in Build a vector database (Pinecone / Weaviate / pgvector style) · Every curriculum