The Industrial Leap from KNN to ANN for Real-Time Recommendations
In the world of machine learning, K-Nearest Neighbors (KNN) is often one of the first algorithms taught. Its simplicity is elegant: to classify a new data point, find the training examples "nearest" to it and take a majority vote.1 In recommendation systems, this translates to a powerful and intuitive concept: "people like you also liked..." (user-based collaborative filtering) or "people who viewed this item also viewed..." (item-based collaborative filtering).2 However, the journey from this simple concept to a production-grade, real-time recommendation engine serving millions of users is a story of astronomical scale and computational bottlenecks. Naive KNN, while intuitive, is fundamentally unworkable for any large-scale online system. This article is a practitioner's guide to that journey. We will explore why the "agony" of naive KNN's complexity forced a new approach, how Approximate Nearest Neighbors (ANN) saved the day, and how modern libraries like Spotify's Annoy and Meta's Faiss are used in practice. Most importantly, we'll provide a detailed "cookbook" for tuning these libraries, demystifying the critical parameters like nlist and nprobe that every engineer must balance.
The Allure and Agony of K-Nearest Neighbors
The Simple Idea: "People Like You Also Liked..."
In a modern recommendation system, we represent both users and items as high-dimensional vectors, or "embeddings." These embeddings are learned by deep learning models, and they capture semantic "taste." A user vector might encode their preferences, while an item vector encodes its content and style. In this shared embedding space, similar users and similar items are "close" to each other. The KNN approach seems to be a natural fit.2 To generate recommendations for a user:
- Take the user's vector (the query).
- Compare it to every other user's vector in the database.
- Find the most similar users (the "nearest neighbors").
- Aggregate the items those users liked and recommend them.3
A key, and often misunderstood, property of KNN is that it's a "non-parametric" or "lazy learning" algorithm.1 Unlike a deep neural network, it doesn't "learn" a compact set of parameters during a training phase.4 Instead, the "model" is the entire dataset. This simplicity is deceptive, as it inverts the typical performance profile of an ML system: it has zero "training" cost but an astronomical "inference" cost.5
The Production Wall: Why Naive KNN Fails Online
In an industrial-scale online system, such as a user refreshing their homepage or watching a video, the application has a strict "latency budget." The recommendation module is often expected to return results in well under 100 milliseconds.6 Naive KNN cannot possibly meet this budget. The core problem is its computational complexity. To find the nearest neighbors for a single query vector, the algorithm must compute the distance (e.g., Euclidean or cosine similarity) between that query vector and every single vector in the -item database.7 This gives KNN a query-time complexity of , where:
- is the number of vectors in the index (e.g., 100 million products, 1 billion videos).
- is the dimensionality of the vectors (e.g., 128, 256, or 768).
Let's do the math for a "modest" system:
- = 100,000,000 items
- = 128 dimensions
A single recommendation query would require floating-point operations. A high-end CPU can perform billions of operations per second, but this calculation, repeated for every user request, is still orders of magnitude too slow. It would take seconds or even minutes, not milliseconds.5 This lazy-learning model, where all computation is deferred until query time 1, is the exact opposite of what production systems require. We need models that are "eager"—models that have a high offline training cost (where we have hours or days and massive compute) but a tiny online inference cost (a single, fast forward pass). Naive KNN's inference cost is a non-starter. It doesn't just "perform poorly"; it is fundamentally and mathematically impossible to use for a large-scale, real-time system.
The Billion-Scale Solution: Approximate Nearest Neighbors (ANN)
Since finding the exact nearest neighbors is computationally infeasible, the industry pivoted to a more practical solution: finding almost the right neighbors.
The Great Trade-Off: Sacrificing Perfect for "Good Enough"
The breakthrough was to abandon the guarantee of perfect accuracy. This is the core of Approximate Nearest Neighbors (ANN). ANN algorithms use intelligent shortcuts—data structures like trees, graphs, or partitions—to efficiently navigate the search space.6 Instead of checking all items, they intelligently check only a tiny fraction of them. The goal is no longer to find the perfect neighbors but to find a set of neighbors that are "close enough".8 This introduces a new, critical trade-off: Speed vs. Recall.
- Speed (QPS): How many queries can we serve per second?
- Recall: What fraction of the true nearest neighbors did our approximate search find?.9
In a recommendation context, this trade-off is almost always a worthwhile one. The embedding space itself is an imperfect, "fuzzy" representation of human taste. A user is highly unlikely to notice or care about the difference between the "perfect" 10th most similar item and the 12th or 15th. The product goal is to show highly relevant content, and an ANN search with 99% recall still provides a list of exceptionally relevant candidates.10 We happily sacrifice that "tiny drop in accuracy" for a 1,000x or 10,000x speedup.10
ANN in the Modern Recommender: The Industrial Architecture
ANN isn't the entire recommendation system. It's the high-speed engine for one specific, crucial stage: Retrieval (or Candidate Generation). Modern industrial recommenders (like those at Pinterest, Netflix, and Alibaba) are multi-stage funnels 11:
- Stage 1: Retrieval (Candidate Generation): This stage's job is to use ANN to "filter" the entire billion-item corpus down to a small set of hundreds (e.g., top 500) of relevant candidates. This stage must be extremely fast (low latency) and have high recall (it must not miss any good candidates).12
- Stage 2: Ranking: This stage takes the 500 candidates from retrieval and uses a "heavy," complex, and computationally expensive model (e.g., a Deep Neural Network or Transformer) to "re-rank" them. This model can use many more features (e.g., user's age, time of day, device) to produce a precise, personalized score for each of the 500 items.11
- Stage 3: Re-ranking/Blending: A final stage may apply business logic, diversification, and other rules.
This multi-stage architecture is the key. The ANN retrieval stage "sets the upper bound for the quality of recommendations".12 If a great item isn't in the 500 candidates, the "smarter" ranking model will never even see it. This architecture is often implemented using a "Two-Tower Model".11
- One tower (the "user tower") is a neural network that takes real-time user features (e.g., recent clicks, search history) and computes a single "query vector".13
- The other tower (the "item tower") is a neural network that has been run offline on the entire item catalog to generate an embedding for every single item.14
The "online KNN" problem is now beautifully decoupled. The computationally crippling part—running the item tower on all items—is moved offline into a massive batch job.14 The item embeddings are then "baked" into a static ANN index. At query time, the only online computations are:
- Run the "user tower" once (fast) to get a 128-dim query vector.
- Use that query vector to perform an ANN search against the pre-built index (very fast).
This is how "online KNN" is actually practiced: Offline Build, Online Query. The ANN index is the serving layer for the pre-computed item tower, and it's what makes sub-second retrieval from billions of items possible.13 This offline-build approach does, however, create a "freshness" problem. When a new item is added, it won't appear in recommendations until the next daily or hourly batch job rebuilds the index.15 This leads many systems to adopt a hybrid approach: a massive, static "corpus index" and a small, dynamic "freshness index" (e.g., using HNSW, which can add items online) for new content.16
ANN in Practice: Deep Dive on Foundational Libraries
Two libraries dominate the industrial landscape for high-performance ANN: Annoy and Faiss.
Spotify's Annoy: Speed through Trees and mmap
Annoy (Approximate Nearest Neighbors Oh Yeah) is an open-source library from Spotify, built specifically to power music recommendations.17 Core Concept: Random Projection Trees Annoy's algorithm is elegantly simple. It builds a "forest" of binary trees 18:
- To build a single tree, it recursively splits the entire vector space using "random hyperplanes."
- A hyperplane is chosen by sampling two random points from the data and defining the plane as the one equidistant between them.19
- This repeats until the leaf nodes contain some small, fixed number of items.
- It builds a forest of these trees (controlled by n_trees).18 Building multiple trees ensures that vectors that fall near a "bad" split boundary in one tree will likely be grouped correctly in another.
- A query searches through all trees, inspecting a priority queue of candidate nodes, to find the nearest neighbors.19
The Industrial Advantage: mmap Annoy's signature feature is its "secret weapon" for production: it creates "large read-only file-based data structures that are mmapped into memory".20 mmap (memory-mapping) is a system call that maps a file on disk directly to a process's virtual memory. The OS then handles paging the data into and out of physical RAM as needed.21 This has a massive infrastructure implication. Imagine a 32-core production server running your recommendation API, with 32 independent worker processes. Your ANN index is 100 GB.
- Without mmap: Each of the 32 processes would need to load the entire 100 GB index into its own private RAM. The machine would need of RAM, which is impossibly expensive.
- With mmap: All 32 processes can share the exact same 100 GB of physical RAM.27 The operating system sees it as a single, shared resource. This "force multiplier" allows for "big data work on tiny workers" 21, dramatically reducing infrastructure costs.
The main trade-off is that Annoy's index is "read-only." You cannot add new items without a full, offline rebuild.21 Tuning Annoy: Annoy is famously simple to tune, with only two key parameters 20:
- n_trees (Build-time): The number of trees in the forest. More trees give higher accuracy but create a larger index and take longer to build.
- search_k (Query-time): The number of nodes to inspect during the search. A larger value gives higher accuracy but makes the query slower.
Meta's Faiss: A "Lego Toolkit" for Vector Search
Faiss (Facebook AI Similarity Search) is an open-source library from Meta (FAIR).22 It is not a single algorithm but a "toolkit" or "Lego box" of composable indexing methods.23 This is Faiss's core philosophy: it gives the engineer fine-grained control over the Speed vs. Memory vs. Accuracy trade-off.24 You can combine different "building blocks" to create the perfect index for your specific use case.25 Let's look at the four most important "Lego bricks":
- IndexFlatL2: The brute-force, exact KNN index. It computes full distances to every vector.8 It is 100% accurate but slow. We use it as the "ground truth" to benchmark the recall of our approximate indexes.26
- IndexIVF: The Inverted File index. This is a partitioning component that divides the search space to improve speed.27
- IndexPQ: Product Quantization. This is a compression component that shrinks vectors to save memory.28
- IndexHNSW: Hierarchical Navigable Small World. This is a graph-based component that provides state-of-the-art speed and recall, but at a high memory cost.29
The industrial decision of which index to use comes down to balancing these trade-offs, as summarized in the table below.
| Index Type (Factory String) | Core Concept | Search Speed | Recall (Accuracy) | Memory Usage | Primary Use Case |
|---|---|---|---|---|---|
| "Flat" (e.g., IndexFlatL2) | Brute-Force (Exact KNN) 27 | Very Slow () | 100% (Perfect) | Very High ( bytes) | Ground truth, small datasets (<100k) |
| "IVF...,Flat" (e.g., IndexIVFFlat) | Clustered / Partitioned 27 | Fast | Good-High | Very High (Same as Flat) | High-accuracy search where RAM is plentiful. |
| "IVF...,PQ..." (e.g., IndexIVFPQ) | Clustered + Compressed 30 | Very Fast | Good | Very Low ( bytes) | Billion-scale search with memory constraints. |
| "HNSW..." (e.g., IndexHNSWFlat) | Layered Proximity Graph 31 | Ultra Fast () | Very High | High (High overhead for links) | Low-latency, high-recall search where RAM is plentiful. |
Deconstructing Faiss: The Algorithms That Power Industrial ANN
To tune Faiss effectively, we must first understand how its core components work.
Partitioning the Search Space: IndexIVF (Inverted File Index)
The IndexIVF is Faiss's primary "divide and conquer" strategy.27 It borrows the "inverted index" concept from classical text search engines.23
How it Works (Coarse Quantization):
- Step 1 (Train): We first need a "coarse quantizer." We run a k-means algorithm on a sample of our data to find nlist cluster centroids (e.g., nlist = 65536).27 These centroids partition the entire vector space into nlist "Voronoi cells".27
- Step 2 (Add): For every vector in our -item dataset, we find its single closest centroid. We then add the vector (or just its ID) to an "inverted list" (or "posting list") associated with that centroid.32 At the end, we have nlist lists, each containing roughly vectors.
- Step 3 (Query): This is the magic. When a new query vector comes in:
This "search scope reduction" is the source of its speed.30 Let's re-run our math for = 100 million items:
- Naive IndexFlat: 100,000,000 distance calculations.
- IndexIVFFlat (with nlist=10000, nprobe=8):
- Compare query to centroids: 10,000 calculations.
- Search the 8 lists (each 10,000 items): calculations.
- Total: calculations.
We've achieved a >1,000x speedup by reducing the search cost from to roughly . The primary flaw? IndexIVFFlat is fast, but it still requires all full-sized vectors to be stored in RAM.27
Compressing the Vectors: IndexPQ (Product Quantization)
Product Quantization (PQ) is a "lossy compression" technique designed to solve the memory problem.34
How it Works (Fine Quantization):
- Step 1 (Train):
- We split each -dimensional vector (e.g., ) into equal, independent sub-vectors. For example, if , we get 16 sub-vectors of 8 dimensions each.28
- For each of the sub-spaces, we run a separate k-means algorithm to find centroids (e.g., , which corresponds to nbits=8 since ).28
- This training process results in "codebooks," each containing 256 tiny 8-dimensional vectors.
- Step 2 (Add / Compress):
- When a full 128-dim vector comes in, we split it into its 16 8-dim sub-vectors.
- For each sub-vector, we find its closest centroid in its corresponding codebook (e.g., sub-vector 1 is closest to centroid #42 in codebook 1; sub-vector 2 is closest to centroid #119 in codebook 2, etc.).28
- We then replace that 8-dim float sub-vector with the ID of that centroid (a single 8-bit integer, 0-255).28
- The final, compressed "PQ code" for the 128-dim vector is just an array of 16 integers.
This results in a massive memory reduction. Let's do the math again:
- Original Vector: 128 dimensions 4 bytes/float = 512 bytes.
- PQ-Compressed Vector: 16 sub-vectors 1 byte/ID (for nbits=8) = 16 bytes.
This is a 32x reduction in memory. With 1 billion vectors, the RAM required drops from 512 GB to a much more manageable 16 GB. This is what allows Faiss to handle datasets with "billions or trillions" of vectors.35 The trade-off is that distance calculations are now approximate (we're comparing compressed codes, not original vectors), which reduces recall.36
The Power Couple: IndexIVFPQ
This is the most common and powerful combination for massive, memory-constrained industrial applications.28 It combines IVF's speed with PQ's memory savings.
How it Works:
- Train: We first train the nlist IVF "coarse quantizer" centroids.
- Train: We then train the PQ "fine quantizer" codebooks.
- Add: For each of the vectors, we find its correct IVF cell, compress the vector using PQ (down to 16 bytes), and store that tiny compressed code in the inverted list for that cell.30
A query proceeds just like in IndexIVF (find nprobe cells), but the final brute-force search is done on the compressed PQ codes, making it both memory-efficient and extremely fast. This index hits the "sweet spot" that makes billion-scale, in-memory search economically and computationally viable.28
Graphing the Neighbors: IndexHNSW (Hierarchical Navigable Small World)
HNSW is a newer, graph-based algorithm that is the state-of-the-art in many benchmarks for high-recall, low-latency search, especially when memory is less of a concern.29
How it Works (Proximity Graphs):
- Core Idea: HNSW builds a "proximity graph," where nodes are our vectors and edges connect vectors that are "close" to each other.29
- "Small World" Property: The graph is constructed so that you can get from any node to any other node in a very small number of hops.31
- "Hierarchical" Structure: This is the key innovation. HNSW builds multiple layers of graphs, much like a "probability skip list".29
- The top layer (Layer ) is very sparse, containing only a few nodes and "long-range" links.
- Each layer below (Layer , ,...) gets progressively denser, with more nodes and "shorter-range" links.
- The bottom layer (Layer 0) contains all the vectors.29
- Search Process:
- A query vector enters the top, sparsest layer at a fixed "entry point".29
- It "greedily" traverses the graph in that layer, always moving to the neighbor closest to the query.37
- When it finds a "local minimum" (a node whose neighbors are all farther from the query), it drops down to the next, denser layer and continues its greedy search.29
- This process repeats until it finds the local minimum at the bottom, densest layer (Layer 0), which contains the nearest neighbors.29
This hierarchical "zoom-in" approach is incredibly efficient, achieving a query complexity of .37 HNSW is often the fastest and most accurate index, but it comes at a high memory cost. The graph itself, with all its "links" (edges), must be stored in RAM in addition to the vectors, and this overhead can be substantial.26
The Practitioner's Guide: Tuning Faiss for Production
This brings us to the most practical, and critical, part of the process: tuning. There is no such thing as the "best" parameters. All tuning is an exercise in balancing the Speed (QPS) vs. Accuracy (Recall) vs. Memory trade-offs.9
The Eternal Trade-off: Charting Recall vs. Latency (QPS)
The industrial tuning process is empirical. You must benchmark your index with your data.
- Get Ground Truth: Take a representative sample of your query vectors (e.g., 10,000 user vectors) and your database vectors (e.g., 1,000,000 item vectors). Use faiss.IndexFlatL2 to find the true 100% accurate nearest neighbors for all 10k queries. Save these as your "ground truth."
- Build Approximate Index: Build your candidate index (e.g., IndexIVF4096,PQ16).
- Benchmark Loop: Now, loop through a range of your query-time parameter (e.g., nprobe = ).22
- Measure: For each nprobe value, run all 10k queries and measure two things:
- Average Recall@10: What percentage of the true top-10 neighbors (from your ground truth) did your approximate search find?.38
- Average Queries per Second (QPS) or Average Latency (ms).
- Plot and Decide: Plot Recall (Y-axis) vs. QPS (X-axis). You will get a curve. Find the "knee" of the curve, or more practically, find the highest recall point that still meets your online latency budget (e.g., "we must be < 20ms," which might correspond to 5,000 QPS).39
This entire process is what open-source projects like ann-benchmarks automate, allowing you to compare different libraries and index types on public datasets.40
Tuning IndexIVF*: The nlist and nprobe Cookbook
This directly addresses the most common tuning question for Faiss. Choosing nlist (Number of Centroids/Clusters)
- What it is: The number of k-means clusters (Voronoi cells) to partition the data into. This is a build-time parameter.27 It cannot be changed without rebuilding the entire index.
- Industrial Rule of Thumb: The Faiss wiki provides a clear guideline: nlist should be between and , where is the size of your dataset.26
- Example: For vectors, . A good nlist is between 4,000 and 16,000.26
- Example: For vectors, . A good nlist is between 40,000 and 160,000 (e.g., 65536).
- Trade-off:
- High nlist (e.g., 65536): More, smaller cells. Faster search (fewer items per list to scan) but lower recall (the query vector might be near a cell boundary, and the true neighbor is in the next cell over, which isn't in your nprobe). Requires more training data.26
- Low nlist (e.g., 1024): Fewer, larger cells. Slower search (more items to scan per list) but higher recall (true neighbors are more likely to be in the same big cell).41
Choosing nprobe (Number of Probes)
- What it is: The number of nearby cells (clusters) to search at query-time.27
- The "A/B Testing" Parameter: This is the most important parameter for online tuning because it can be changed dynamically at runtime without retraining or rebuilding the index.22
- This makes nprobe the perfect knob for production A/B testing. We can serve a small fraction of users with nprobe=8 and another fraction with nprobe=16. We can then measure the business impact (e.g., clicks, engagement, revenue) of the higher-recall, higher-latency nprobe=16 group against the lower-recall, lower-latency nprobe=8 group.39
- Trade-off (The core Speed vs. Accuracy knob):
- nprobe = 1: Fastest speed. You only search the single closest cell. Lowest recall.42
- nprobe = 16: You search the 16 closest cells. Roughly 16x slower (more data to scan), but much higher recall.22
- nprobe = nlist: Slowest speed. You search all cells. This is equivalent to brute-force (IndexFlat) and gives 100% recall, but defeats the whole purpose.43
Tuning IndexPQ: The m and nbits Cookbook
Choosing m (Number of Sub-vectors)
- What it is: How many "chunks" to split the vector into. This is a build-time parameter.28
- Constraint: Your vector dimension must be divisible by .44 (e.g., , can be 8, 16, 32, 64).
- Trade-off (Memory vs. Accuracy): The total compressed size is bytes.
- High m (e.g., m=32): Smaller, 4-dim sub-vectors. More accurate reconstruction, but less compression (32 bytes per vector, assuming nbits=8).
- Low m (e.g., m=16): Larger, 8-dim sub-vectors. Less accurate reconstruction (more "lossy"), but more compression (16 bytes per vector).36
Choosing nbits (Bits per Sub-vector)
- What it is: Controls the size of the codebook for each sub-vector ().28
- Industrial Rule of Thumb: Almost always use nbits = 8.44
- This is not just for accuracy; it's a critical engineering optimization. means the centroid ID for each sub-vector fits in exactly one byte. This makes the final PQ code perfectly byte-aligned, which is extremely efficient for modern CPUs and SIMD (Single Instruction, Multiple Data) instructions to process. Using nbits=10 () would be slightly more accurate but would require complex, slow bit-packing, as each ID would take 10 bits. The industry has standardized on nbits=8 as the best practical trade-off.36
Tuning IndexHNSW: The M, efConstruction, efSearch Cookbook
Choosing M (Max Connections)
- What it is: The maximum number of edges (neighbors) per node in the graph layers. This is a build-time parameter.45
- Trade-off (Memory vs. Recall): This is the main knob for HNSW's memory footprint.46
Choosing efConstruction (Build-Time Search Depth)
- What it is: Controls how "hard" the algorithm looks for the best neighbors when inserting a new node during the build phase.45
- Trade-off (Build-Time vs. Index Quality):
- High efConstruction (e.g., 500): Much slower build, but a higher-quality, more accurate graph, which leads to better recall at query time.47
- Low efConstruction (e.g., 100): Faster build, but a lower-quality graph (higher risk of bad local minima).
- Industrial Practice: Set efConstruction as high as your offline build-time budget will allow. This is an offline cost, so it's usually worth paying for a better index.46
Choosing efSearch (Query-Time Search Depth)
- What it is: The nprobe equivalent for HNSW. It's the size of the "candidate list" (beam search) used during the query. This is a query-time parameter.45
- Trade-off (Speed vs. Accuracy): This is the main online tuning knob for HNSW.47
- Industrial Practice: Same as nprobe. Benchmark your QPS vs. Recall curve (as described in 5.1) for different efSearch values and pick the highest one that meets your production latency budget.39
ANN Parameter Tuning Cheat Sheet
This table synthesizes the tuning process into an actionable "cheat sheet" for practitioners.
| Index | Parameter | What it is | When to Tune | Primary Trade-Off | Industrial Rule of Thumb |
|---|---|---|---|---|---|
| IVF | nlist | # of clusters (centroids) | Build-time | Speed vs. Recall (indirect) | to 26 |
| IVF | nprobe | # of clusters to search | Query-time | Speed vs. Recall | A/B Test this. Start at 1, increase (e.g., 8, 16, 32) until latency budget is hit.22 |
| PQ | m | # of sub-vectors | Build-time | Memory vs. Recall | must be divisible by . Higher = more memory, better recall.44 |
| PQ | nbits | Bits per sub-vector | Build-time | Memory vs. Recall | Almost always 8 (for 1-byte alignment).44 |
| HNSW | M | Max edges/node | Build-time | Memory vs. Recall | Start at 16 or 32. Higher = much more RAM.29 46 |
| HNSW | efConstruction | Build-time search depth | Build-time | Build-Time vs. Index Quality | Set as high as you can afford (e.g., 400+).47 |
| HNSW | efSearch | Query-time search depth | Query-time | Speed vs. Recall | A/B Test this. Start low (e.g., 64), increase until latency budget is hit.39 47 |
Conclusion: Assembling Your First Production-Grade Index
We can tie all these "Lego bricks" together using the Faiss "index factory string," a powerful shorthand for defining complex, composable indices.48
Example 1: The Baseline (Brute-Force)
- Code: index = faiss.index_factory(D, "Flat")
- Deconstruction: "Use a brute-force (Flat) index. is your vector dimension (e.g., 128)".48 This is your ground truth.
Example 2: The Memory-Saver (Billion-Scale Champion)
- Code: index = faiss.index_factory(D, "IVF65536,PQ16")
- Deconstruction: "Use an Inverted File (IVF) index with 65,536 (nlist) centroids. Inside each inverted list, store vectors compressed with Product Quantization (PQ) using ".28
- Tuning: Build this index offline. At query time, tune it by setting index.nprobe = 16 (or whatever value your latency budget allows).
Example 3: The Speed-Demon (Million-Scale, High RAM)
- Code: index = faiss.index_factory(D, "HNSW32")
- Deconstruction: "Use an HNSW graph where each node has max connections. This index implies Flat storage (full vectors)".26
- Tuning: Build this index offline (with a high efConstruction). At query time, tune it by setting index.hnsw.efSearch = 128.
The journey from classical KNN to industrial-scale ANN is not just an optimization. It is a fundamental architectural shift. It's the realization that we can move the crippling computational cost offline into an "index build" phase, leaving only a fast, "good enough," and tunable (for HNSW) or -like (for IVF) query for the real-time path. This trade-off—sacrificing perfect recall for massive gains in speed and memory efficiency—is the central design principle that enables modern recommendation engines at companies like Spotify 49, Meta 50, Netflix 51, and Pinterest 52 to search billions of items and return relevant, personalized results in the blink of an eye.
Works cited
Footnotes
-
k-nearest neighbors algorithm - Wikipedia, accessed November 2, 2025, https://en.wikipedia.org/wiki/K-nearest_neighbors_algorithm ↩ ↩2 ↩3
-
Recommender Systems using KNN - GeeksforGeeks, accessed November 2, 2025, https://www.geeksforgeeks.org/machine-learning/recommender-systems-using-knn/ ↩ ↩2
-
Collaborative Filtering and KNN | by Ali Ozan Memetoglu - Medium, accessed November 2, 2025, https://medium.com/@aliozan_memetoglu/4-collaborative-filtering-and-knn-f997f8993256 ↩
-
k nearest neighbors computational complexity | by Jakub Adamczyk | TDS Archive | Medium, accessed November 2, 2025, https://medium.com/data-science/k-nearest-neighbors-computational-complexity-502d2c440d5 ↩
-
Is k nearest neighbours regression inherently slow? - Stack Overflow, accessed November 2, 2025, https://stackoverflow.com/questions/59178717/is-k-nearest-neighbours-regression-inherently-slow ↩ ↩2
-
A Comprehensive Guide to Approximate Nearest Neighbors Algorithms | Shaped Blog, accessed November 2, 2025, https://www.shaped.ai/blog/approximate-nearest-neighbors-algorithms ↩ ↩2
-
Deep Dive on KNN: Understanding and Implementing the K-Nearest Neighbors Algorithm, accessed November 2, 2025, https://arize.com/blog-course/knn-algorithm-k-nearest-neighbor/ ↩
-
Approximate Nearest Neighbor (ANN) Search - GeeksforGeeks, accessed November 2, 2025, https://www.geeksforgeeks.org/machine-learning/approximate-nearest-neighbor-ann-search/ ↩ ↩2
-
Why are high recall values important when benchmarking approximate nearest neighbor searches, and how do vector databases typically trade off recall for speed? - Zilliz, accessed November 2, 2025, https://zilliz.com/ai-faq/why-are-high-recall-values-important-when-benchmarking-approximate-nearest-neighbor-searches-and-how-do-vector-databases-typically-trade-off-recall-for-speed ↩ ↩2
-
Understanding the approximate nearest neighbor (ANN) algorithm | Elastic Blog, accessed November 2, 2025, https://www.elastic.co/blog/understanding-ann ↩ ↩2
-
Candidate Generation Using a Two Tower Approach With Expedia Group Traveler Data | by Eric Rincon - Medium, accessed November 2, 2025, https://medium.com/expedia-group-tech/candidate-generation-using-a-two-tower-approach-with-expedia-group-traveler-data-ca6a0dcab83e ↩ ↩2 ↩3
-
Synergizing Implicit and Explicit User Interests: A Multi-Embedding Retrieval Framework at Pinterest - arXiv, June 2025, https://arxiv.org/html/2506.23060v1 ↩ ↩2
-
Two-Stage Recommender Systems — Merlin Models documentation, accessed November 2, 2025, https://nvidia-merlin.github.io/models/v0.5.0/examples/05-Retrieval-Model.html ↩ ↩2
-
Two-tower model for recommendation system : r/learnmachinelearning - Reddit, accessed November 2, 2025, https://www.reddit.com/r/learnmachinelearning/comments/1kmnj2y/twotower_model_for_recommendation_system/ ↩ ↩2
-
Unlocking Efficient Ad Retrieval: Offline Approximate Nearest Neighbors in Pinterest Ads, accessed November 2, 2025, https://medium.com/pinterest-engineering/unlocking-efficient-ad-retrieval-offline-approximate-nearest-neighbors-in-pinterest-ads-6fccc131ac14 ↩
-
Approximate Nearest Neighbour Search on Dynamic Datasets: An Investigation - arXiv, April 2024, https://arxiv.org/html/2404.19284v2 ↩
-
Approximate Nearest Neighbors Oh Yeah (ANNOY) - AAU Social Data Science Deep Learning - 2019 Portfolio, accessed November 2, 2025, https://sds-aau.github.io/M3Port19/portfolio/ann/ ↩
-
What is Annoy (Approximate Nearest Neighbors Oh Yeah)? - Zilliz Learn, accessed November 2, 2025, https://zilliz.com/learn/what-is-annoy ↩ ↩2
-
Performance of Annoy method Vs. KD-Tree - Stack Overflow, accessed November 2, 2025, https://stackoverflow.com/questions/37105782/performance-of-annoy-method-vs-kd-tree ↩ ↩2
-
spotify/annoy: Approximate Nearest Neighbors in C++/Python optimized for memory usage and loading/saving to disk - GitHub, accessed November 2, 2025, https://github.com/spotify/annoy ↩ ↩2
-
Approximate Nearest Neighbor Oh Yeah (Annoy) - Hacker News, accessed November 2, 2025, https://news.ycombinator.com/item?id=38072277 ↩ ↩2 ↩3
-
Introduction to Facebook AI Similarity Search (Faiss) - Pinecone, accessed November 2, 2025, https://www.pinecone.io/learn/series/faiss/faiss-tutorial/ ↩ ↩2 ↩3 ↩4 ↩5
-
Welcome to Faiss Documentation — Faiss documentation, accessed November 2, 2025, https://faiss.ai/ ↩ ↩2
-
Faiss - Meta AI, accessed November 2, 2025, https://ai.meta.com/tools/faiss/ ↩
-
What Is FAISS (Facebook AI Similarity Search)? How Does It Work? - Designveloper, accessed November 2, 2025, https://www.designveloper.com/blog/what-is-faiss/ ↩
-
Guidelines to choose an index · facebookresearch/faiss Wiki - GitHub, accessed November 2, 2025, https://github.com/facebookresearch/faiss/wiki/Guidelines-to-choose-an-index ↩ ↩2 ↩3 ↩4 ↩5 ↩6 ↩7
-
Similarity Search, Part 1: kNN & Inverted File Index | Towards Data Science, accessed November 2, 2025, https://towardsdatascience.com/similarity-search-knn-inverted-file-index-7cab80cc0e79/ ↩ ↩2 ↩3 ↩4 ↩5 ↩6 ↩7 ↩8 ↩9 ↩10
-
Product Quantization: Compressing high-dimensional vectors by 97% - Pinecone, accessed November 2, 2025, https://www.pinecone.io/learn/series/faiss/product-quantization/ ↩ ↩2 ↩3 ↩4 ↩5 ↩6 ↩7 ↩8 ↩9 ↩10
-
Hierarchical Navigable Small Worlds (HNSW) - Pinecone, accessed November 2, 2025, https://www.pinecone.io/learn/series/faiss/hnsw/ ↩ ↩2 ↩3 ↩4 ↩5 ↩6 ↩7 ↩8 ↩9 ↩10
-
Inverted File Indexes (IVF) - PRIMO.ai, accessed November 2, 2025, https://primo.ai/index.php/Inverted_File_Indexes_(IVF) ↩ ↩2 ↩3
-
What is a Hierarchical Navigable Small World - MongoDB, accessed November 2, 2025, https://www.mongodb.com/resources/basics/hierarchical-navigable-small-world ↩ ↩2
-
Inverted File Indexing (IVF) in FAISS: A Comprehensive Guide | by Ahmad Jawabreh, accessed November 2, 2025, https://medium.com/@Jawabreh0/inverted-file-indexing-ivf-in-faiss-a-comprehensive-guide-c183fe979d20 ↩
-
Deep Dive into Faiss IndexIVFPQ for vector search - siddharth's space - WordPress.com, accessed November 2, 2025, https://sidshome.wordpress.com/2023/12/30/deep-dive-into-faiss-indexivfpq-for-vector-search/ ↩ ↩2
-
Home · facebookresearch/faiss Wiki - GitHub, accessed November 2, 2025, https://github.com/facebookresearch/faiss/wiki ↩
-
The Faiss Library - arXiv, January 2024, https://arxiv.org/html/2401.08281v2 ↩
-
Similarity Search, Part 2: Product Quantization - Towards Data Science, accessed November 2, 2025, https://towardsdatascience.com/similarity-search-product-quantization-b2a1a6397701/ ↩ ↩2 ↩3
-
A Visual Guide to the Hierarchical Navigable Small World Algorithm - Christopher Fu, accessed November 2, 2025, https://cfu288.com/blog/2024-05_visual-guide-to-hnsw/ ↩ ↩2
-
Vector Search: Navigating Recall and Performance - OpenSource Connections, accessed November 2, 2025, https://opensourceconnections.com/blog/2025/02/27/vector-search-navigating-recall-and-performance/ ↩
-
How does increasing the number of probes or search depth (like nprobe or efSearch) impact query latency, and how can one find an optimal setting that balances speed and recall? - Milvus, accessed November 2, 2025, https://milvus.io/ai-quick-reference/how-does-increasing-the-number-of-probes-or-search-depth-like-nprobe-or-efsearch-impact-query-latency-and-how-can-one-find-an-optimal-setting-that-balances-speed-and-recall ↩ ↩2 ↩3 ↩4 ↩5 ↩6
-
erikbern/ann-benchmarks: Benchmarks of approximate nearest neighbor libraries in Python - GitHub, accessed November 2, 2025, https://github.com/erikbern/ann-benchmarks ↩
-
Nearest Neighbor Indexes for Similarity Search - Pinecone, accessed November 2, 2025, https://www.pinecone.io/learn/series/faiss/vector-indexes/ ↩
-
FAISS: A quick tutorial to efficient similarity search | by Shayan Fazeli - Medium, accessed November 2, 2025, https://shayan-fazeli.medium.com/faiss-a-quick-tutorial-to-efficient-similarity-search-595850e08473 ↩
-
Faster search · facebookresearch/faiss Wiki - GitHub, accessed November 2, 2025, https://github.com/facebookresearch/faiss/wiki/Faster-search ↩
-
Faiss product quantization - OpenSearch Documentation, accessed November 2, 2025, https://docs.opensearch.org/latest/vector-search/optimizing-storage/faiss-product-quantization/ ↩ ↩2 ↩3 ↩4
-
How hierarchical navigable small world (HNSW) algorithms can improve search - Redis, accessed November 2, 2025, https://redis.io/blog/how-hnsw-algorithms-can-improve-search/ ↩ ↩2 ↩3
-
Optimize HNSW Parameters in FAISS for Better Searches - BakingAI Blog, accessed November 2, 2025, https://blog.bakingai.com/optimize-hnsw-parameters-faiss/ ↩ ↩2 ↩3 ↩4
-
What are the key configuration parameters for an HNSW index (such as M and efConstruction/efSearch), and how does each influence the trade-off between index size, build time, query speed, and recall? - Milvus, accessed November 2, 2025, https://milvus.io/ai-quick-reference/what-are-the-key-configuration-parameters-for-an-hnsw-index-such-as-m-and-efconstructionefsearch-and-how-does-each-influence-the-tradeoff-between-index-size-build-time-query-speed-and-recall ↩ ↩2 ↩3 ↩4
-
Understanding FAISS Indexing - Tanya Soni - Medium, accessed November 2, 2025, https://arithmancylabs.medium.com/understanding-faiss-indexing-86ec98048bd9 ↩ ↩2
-
Introducing Voyager: Spotify's New Nearest-Neighbor Search Library, accessed November 2, 2025, https://engineering.atspotify.com/introducing-voyager-spotifys-new-nearest-neighbor-search-library ↩
-
Faiss: A library for efficient similarity search - Engineering at Meta - Facebook, accessed November 2, 2025, https://engineering.fb.com/2017/03/29/data-infrastructure/faiss-a-library-for-efficient-similarity-search/ ↩
-
Vector Search in Action: A Netflix-Inspired FAISS Walkthrough | by Raj Sengo - Medium, accessed November 2, 2025, https://medium.com/data-science-collective/vector-search-in-action-a-netflix-inspired-faiss-walkthrough-80ae2c5c6e75 ↩
-
Establishing a Large Scale Learned Retrieval System at Pinterest - Medium, accessed November 2, 2025, https://medium.com/pinterest-engineering/establishing-a-large-scale-learned-retrieval-system-at-pinterest-eb0eaf7b92c5 ↩