How do you choose between k-means and DBSCAN for clustering document embeddings?
answer
- is "no cluster" a valid answer?
- k fixed up front versus discovered
- density, plus a noise label
- spherical partition versus arbitrary shape
- one global radius in high dimensions
basics
~20 sk-means forces every document into one of k clusters you fix in advance, so it fits a corpus you want fully partitioned. DBSCAN groups by density, discovers how many clusters exist, and leaves sparse points unlabelled as noise.
solid answer
~50 sThe practical decision rule is whether "this document belongs to no cluster" is a valid answer for your problem. k-means always produces a full partition into k groups you choose up front, and it assumes clusters are roughly spherical and comparable in spread — good when every document must be routed somewhere. DBSCAN instead grows clusters from dense neighbourhoods, so the number of clusters emerges from the data, shapes can be irregular, and low-density points get a noise label. Clustering app-store review embeddings is the classic case for density: one tight complaint cluster about a login bug is real, and the long tail of one-off gripes is better labelled noise than smeared across centroids. The catch is that a single global density radius is fragile on raw high-dimensional vectors, so in practice people normalize, often reduce to a few dozen dimensions first, and reach for HDBSCAN over plain DBSCAN.
code
python · 11 linesimport numpy as np
from sklearn.cluster import DBSCAN, KMeans
from sklearn.preprocessing import normalize
emb = normalize(np.random.rand(500, 64))
km = KMeans(n_clusters=8, n_init=10, random_state=0).fit(emb)
print("k-means clusters:", sorted(set(km.labels_))) # every point is assigned
db = DBSCAN(eps=0.35, min_samples=5, metric="cosine").fit(emb)
print("noise points:", int((db.labels_ == -1).sum())) # -1 means 'no cluster'go deeper
Know the one-line contrast: k-means needs the number of clusters up front and assigns everything, DBSCAN infers the count from density and can label a point as noise. Say plainly which one you would run first and why.
Be ready to explain the mechanics — centroids and the assign/update loop versus core points, a radius and a minimum neighbour count — and to state that k-means implicitly assumes roughly spherical, comparably sized clusters.
Show that you have run this on real corpora: normalizing first, the fragility of one global radius on high-dimensional vectors, reaching for HDBSCAN or a reduction step, and the diagnostic symptoms of a bad run such as a catch-all cluster or a 70% noise rate.
Own the framing that the algorithm choice follows from the product decision: whether outliers must be actioned or discarded, whether the taxonomy must stay stable across weekly re-runs, and whether clustering is even the right tool versus retrieval or a supervised classifier once labels exist.
## What each algorithm actually optimizes **k-means** partitions n points into exactly k groups so as to minimize the total squared distance from each point to its cluster's centroid. The usual algorithm alternates two steps: assign every point to the nearest centroid, then recompute each centroid as the mean of its members. It needs k as an input, it assigns *every* point, and its implicit model is that clusters are convex blobs of similar size and spread. Initialization matters (k-means++ seeding is the standard remedy), and different random seeds can give different partitions on data with no strong structure. **DBSCAN** takes no k. It takes a neighbourhood radius (`eps`) and a minimum neighbour count (`min_samples`). A point with at least `min_samples` neighbours inside `eps` is a *core point*; core points within `eps` of each other are chained into the same cluster; points reachable from a core point but not themselves core become border members; everything else is **noise**, reported in scikit-learn as label `-1`. The number of clusters is an output, cluster shapes may be arbitrary rather than spherical, and outliers are a first-class result instead of being absorbed. ## The decision rule that matters Ask whether "belongs to no cluster" is an acceptable answer. If downstream you must route every document somewhere — every ticket to a queue, every item to a shelf — a partition is what you want, and k-means (or a hierarchical cut) gives it directly. If your goal is to *find the dense pockets and ignore the rest*, density clustering is the honest tool. App-store reviews make the difference concrete. Suppose you embed a month of reviews. A few hundred are the same complaint — the login flow loops after a password reset — and they sit in a tight, dense neighbourhood. The remaining thousands are scattered one-off remarks about pricing, a typo, a feature wish. DBSCAN returns the login cluster and marks most of the tail as noise, which is exactly the summary a product manager wants. k-means with k=8 has no concept of an outlier: every scattered review is pulled into some cluster, centroids drift toward the mean of unrelated text, and the resulting groups are much harder to name. ## Why density clustering is harder on raw embedding vectors DBSCAN's `eps` is a single global distance threshold. In a space with hundreds or thousands of dimensions, the spread between nearest and farthest neighbour distances narrows, so the window in which one `eps` separates "dense" from "sparse" is narrow and hard to find. Real corpora also have clusters of genuinely different densities — a boilerplate-heavy topic is much tighter than a discursive one — and plain DBSCAN cannot use two thresholds at once. The standard mitigations, in the order people try them: - **L2-normalize the vectors** and cluster with cosine distance, so document length and verbosity do not dominate. - **Reduce to roughly 5–50 dimensions first** (UMAP is the common choice) purely as a preprocessing step for the clustering, then run density clustering there. This is the pipeline popular topic-modelling tooling uses. - **Use HDBSCAN**, the hierarchical variant, which builds a hierarchy over density levels and extracts clusters that persist across it. You give it a minimum cluster size instead of a radius, it tolerates varying density, and it still returns a noise label plus per-point membership strengths. k-means has its own scaling notes: it is cheap and linear in n per iteration, minibatch variants handle millions of vectors, and on L2-normalized vectors minimizing squared Euclidean distance ranks assignments the same way maximizing cosine similarity does — which is why "spherical k-means" on normalized embeddings is a reasonable default. ## How you know the choice went wrong With k-means: one giant catch-all cluster holding a third of the corpus, centroids that all sit near the global mean, or assignments that reshuffle substantially when you change the seed. With DBSCAN: 70% of documents labelled noise (`eps` too small or `min_samples` too high), or a single mega-cluster swallowing everything (`eps` too large). Both symptoms are diagnostic, and both are cheap to check before you show anyone a chart. ## Neighbouring options Agglomerative hierarchical clustering needs no k either: it merges bottom-up and gives you a dendrogram you can cut at whatever granularity a human finds useful, at the cost of quadratic time and memory, so it suits tens of thousands of documents rather than millions. And the most under-used answer is not to cluster at all: if the real question is "what else looks like this document?", nearest-neighbour retrieval answers it directly without committing to any global partition.
- Your DBSCAN run labels 70% of the corpus as noise — what do you check first?That is almost always a parameter or geometry problem, not a data one. Raise `eps` or lower `min_samples` and watch how the noise fraction moves; plot the sorted distance to each point's k-th nearest neighbour and look for where the curve bends, which is the usual way to pick `eps`. If no single radius works, the density genuinely varies across topics — switch to HDBSCAN, or reduce the vectors to a few dozen dimensions and cluster there.
- Does it matter whether you L2-normalize embeddings before running k-means?Yes. Unnormalized vectors let magnitude influence the squared-distance objective, so document length or verbosity artefacts can drive assignments instead of topic. After L2 normalization every vector sits on the unit sphere and the k-means objective orders assignments the same way cosine similarity does — the "spherical k-means" setup most embedding pipelines assume.
- When would you reach for hierarchical clustering instead of either of these?When you want to choose granularity after seeing the structure rather than before. Agglomerative clustering merges bottom-up and produces a dendrogram, so a human can cut it at the level that yields a usable taxonomy, and you can inspect coarse and fine views of the same corpus. The cost is roughly quadratic time and memory, so it suits tens of thousands of documents, not millions.
saying these in an interview costs you the question
- Claims DBSCAN needs no tuning because it finds k itself
- Says k-means will surface the outliers in the corpus
- Picks eps by eyeballing a 2D projection of the data
- Assumes every document must belong to some cluster
- Treats a single global distance threshold as easy to set in high dimensions