K-Nearest Neighbours
Distance metrics, curse of dimensionality, ANN indexes
A handwritten digit arrives as a 28×28 pixel image. You need to classify it. A decision tree would learn a set of pixel-threshold rules at training time. Logistic regression would learn a weight for every pixel. kNN does neither: it stores all 60,000 training images and at prediction time finds the 3 most similar training images by Euclidean distance across all 784 pixels, then takes a majority vote. Zero training time. A new "7" finds three sevens in the training set, votes 3-0, classification done.
The price arrives at query time. Each prediction requires computing the distance from the test image to all 60,000 training images across 784 dimensions: 60,000 × 784 = 47 million multiplications per query. At 1,000 queries per second, that is 47 billion multiplications per second — feasible on fast hardware for MNIST, but already impractical for 1 million images. kNN does not generalize through learned parameters; it memorizes. The entire training set is the model.
Feature scaling is not optional. Age ranges from 0 to 100. Income ranges from 0 to 500,000. Without standardization, a 1-dollar difference in income contributes 5,000× more to Euclidean distance than a 1-year age difference. The nearest neighbors are found entirely in the income dimension. A 50-year-old earning 50K a year looks identical to a 1-year-old earning 50K. StandardScaler before kNN is non-negotiable.
The deeper failure mode is dimensionality. kNN works because nearby points in feature space share labels — local homogeneity. In high dimensions, that assumption breaks. As the number of dimensions grows, the ratio of the distance to the nearest neighbor versus the farthest neighbor converges toward 1. Every point becomes approximately equidistant from every other. The neighborhood concept collapses: there is no meaningful local structure, only a global average. With d = 100, k = 10 nearest neighbors are barely more similar to the query than randomly drawn points.
The production resurrection of kNN is approximate nearest neighbor search. FAISS, HNSW, and ScaNN build indexes that find approximate nearest neighbors in O(log n) instead of O(n). HNSW at 95% recall@10 queries 10 million vectors in under 1 millisecond. Every embedding-based recommendation system, every vector database (Pinecone, Weaviate, Chroma), and every dense retrieval system in a RAG pipeline is kNN with an approximate index. The algorithm is from the 1960s; the implementation is state of the art.
NOT this. kNN is a toy algorithm that does not scale. Nearest-neighbor search is the production architecture for modern retrieval. When a language model generates a query embedding and retrieves relevant documents, it is running kNN against an index of millions of passage embeddings. When a recommendation system finds the top-50 similar users to target for a new item, it is running kNN against a user embedding matrix. The algorithm is ancient. The feature spaces it operates on — dense embeddings from transformers — are not.
The formal statement: exact kNN is O(nd) per query where n is the number of indexed vectors and d is the dimensionality. ANN indexes reduce this to O(d log n) or better, with recall controlled by a search parameter. For the digit classifier: n = 60,000, d = 784, brute force takes ~47M ops. For a production recommendation system: n = 10M, d = 256, brute force takes ~2.56B ops per query — ANN takes ~600K ops at 95% recall.
Key points
- Always use ANN (FAISS, HNSW) when n > 100K — exact kNN is O(n) per query and completely infeasible at scale. HNSW gives sub-millisecond search over 100M vectors at 95%+ recall. For the digit classifier at 60K training images, brute-force kNN runs in ~1ms per query on modern hardware — acceptable. Scale to 10M items and exact kNN takes ~160ms per query, which kills any real-time system. HNSW reduces this to under 1ms at 95% recall@10. The transition point: once n exceeds ~100K, reach for FAISS or HNSW before any other optimization. The recall-speed tradeoff is controllable via the ef (search width) parameter — set it higher for better recall, lower for lower latency.
- Trap: forgetting to scale features. If feature ranges differ by 1000×, kNN sees only the largest-range feature. StandardScaler or L2-normalize embeddings before indexing — this mistake silently destroys retrieval quality with no obvious error. For the digit classifier: pixel values range from 0 to 255, so scaling is uniform and kNN works correctly. For a user-feature matrix with age (0–100) and annual income (0–500,000), raw Euclidean distance finds "nearest neighbors" by income alone. A 20-year-old earning 80K a year is identified as nearest to a 65-year-old earning 80,001, ignoring the 45-year age gap. StandardScaler brings both features to unit variance. For embedding vectors from transformers: L2-normalize before indexing so that cosine similarity equals the dot product — the default in FAISS's IndexFlatIP.
- Diagnostic: if kNN performance is unexpectedly poor, check the intra-cluster distance distribution — if all distances are similar (high-dimensional degenerate case), reduce dimensionality with PCA or switch from Euclidean to cosine similarity. For the digit classifier: compute the distribution of distances from each test point to its 10 nearest neighbors. If the min and max distances are nearly identical (e.g., min 18.2, max 19.1 across 60,000 candidates), the curse of dimensionality is active — the 784-dimensional space has too many uninformative pixel dimensions. Fix: apply PCA to retain the top 50 components explaining ~85% of variance, then run kNN in 50 dimensions. Alternatively, switch from raw pixels to learned embeddings from a CNN — the 128-dimensional embedding space concentrates all discriminative information, and kNN in that space is highly effective.
kNN makes one bet — nearby points share labels — so the distance metric and the feature space are the model, k is just a smoothing parameter, and in high dimensions that bet fails because all distances converge; the production answer is ANN indexing over learned embeddings where the space is built to make proximity meaningful.
Recap
- kNN's one bet: nearby points share labels. Zero training time — it just stores the data.
- The distance metric and feature space ARE the model; k is just a smoothing parameter.
- High dimensions break it — all distances converge (curse of dimensionality).
- Always scale features — a 1000× range difference makes kNN see only the largest feature.
- At scale (n > 100K) use ANN (FAISS, HNSW) — exact kNN is O(n) per query; HNSW gives sub-ms search over 100M vectors at 95%+ recall.
- Production answer: ANN over learned embeddings where the space is built to make proximity meaningful.
Check your understanding
Q1. Why does KNN fail in 1000 dimensions even with millions of training points?
- `A) Training data grows sparse — millions of points across 1000 dimensions leave most volume empty, so no neighbours exist within any radius; more data alone fixes it.`
- `B) KNN's O(nd) inference time becomes prohibitive at d=1000; the failure is purely computational, and approximate-neighbour indexes restore full accuracy instantly.`
- `C) Curse of dimensionality: nearest and farthest distances converge to nearly the same value, so local averaging breaks down here.`
- `D) At d=1000, Euclidean distance violates the triangle inequality entirely, so switching to cosine similarity alone restores a working metric without any reduction.`
Q2. A production recommendation system uses KNN with n=50M items and d=256-dimensional embeddings. Brute-force KNN is too slow. Select the two correct parts of a sound architecture here.
- `A) Build an ANN index offline (HNSW or FAISS IVF) over the 50M embeddings, then query it online for a shortlist of top-k approximate candidates per user.`
- `B) Re-rank that shortlist with a more expensive scoring function or learned ranker — the classic retrieve-and-rerank pattern used across production search.`
- `C) Reduce d=256 to d=16 with PCA and keep brute-force search, since compressing that aggressively at recommendation scale carries essentially no recall cost.`
- `D) Shard the 50M items across 100 machines and run brute-force KNN in parallel per shard, which reaches sub-100ms latency with zero accuracy loss.`
Q3. When would you choose KNN over a trained classifier like logistic regression or a decision tree?
- `A) Choose it when n is very large (n > 1M), since KNN needs zero training time while logistic regression and trees scale with training-set size instead.`
- `B) Choose it when features are all categorical, since Hamming distance beats log-odds coefficients and consistently outperforms trees on categorical tabular data.`
- `C) Choose it when the boundary is irregular and non-linear, training data is small and low-dimensional, you need online learning, or instance-level explanations matter.`
- `D) Choose it whenever the positive class prior is below 10%, since local density estimation near rare classes is naturally unaffected by global imbalance.`
Try it interactively
ML Systems Lab is a free interview-prep platform for ML engineers — work through the full interactive module, quizzes, and drills.
Open ML Systems Lab →