04. IVF: Inverted File Index
Chapter 4 of 18 · 20 min
IVF (Inverted File Index) is the workhorse of vector search. Its core idea: partition your vectors into clusters using k-means, then at query time, find which cluster your query belongs to and search only that cluster (plus neighbors).
EXERCISE
Build an IVF index with 100 clusters on 100k vectors. Vary nprobe from 1 to 20 and measure recall (compare against brute force ground truth). Plot recall vs nprobe. Observe how recall saturates—there's a point of diminishing returns.
# Continuing from above...
def evaluate_recall(index, vectors, queries, k=10, nprobe=1):
"""Calculate recall by comparing to brute force."""
# Ground truth via brute force
db = index.vectors # access vectors
diff = db[:, np.newaxis] - queries[np.newaxis, :, :]
all_dists = np.sqrt(np.sum(diff ** 2, axis=2))
gt_indices = np.argsort(all_dists, axis=0)[:k]
# IVF results
recall_sum = 0
for i, query in enumerate(queries):
_, indices = index.search(query, nprobe)
relevant = set(gt_indices[:, i])
retrieved = set(indices[:k])
recall_sum += len(relevant & retrieved) / k
return recall_sum / len(queries)
# Run evaluation
for nprobe in [1, 5, 10, 20, 50]:
recall = evaluate_recall(index, vectors, queries[:100], k=10, nprobe=nprobe)
print(f"nprobe={nprobe:2d}: recall={recall:.4f}")