Classical Machine Learning Algorithms Questions
Foundational non-deep-learning models and when to reach for each. Covers linear and logistic regression, decision trees and ensemble methods (random forests, gradient boosting), support vector machines, k-nearest neighbors, and clustering, including their assumptions, strengths, and failure modes. Focuses on algorithm selection and the numerical and implementation considerations behind these workhorse models.
Given a new, unlabeled dataset, how would you decide on the number of clusters to use? Cover the practical approaches you would use, plus how you'd handle this at very large scale.
Sample Answer
Direct answer
I would combine several practical signals rather than trust one number: the elbow in within-cluster sum of squares as a first pass, the silhouette score for a more calibrated view, the gap statistic when I can afford it, and a stability check across resampled runs, then validate the final choice against domain constraints. At very large scale, I would compute all of these on a subsample or with streaming approximations rather than on the full dataset directly.
Structured elaboration
Elbow method (WCSS), the standard starting point.
WCSS(k)=∑j=1k∑xi∈Cj∥xi−μj∥2
Plot WCSS against k and look for the point where adding another cluster stops meaningfully reducing it. Simple and visual, and the one most interviewers expect you to reach for first, but the "elbow" can be ambiguous on real data.
Silhouette score. Averages, per point, how much closer it is to its own cluster than to the nearest other cluster (formula and worked calculation are covered under cluster-quality evaluation). Favors convex, well-separated clusters, choose the k that maximizes the mean silhouette.
Gap statistic. Compares the observed WCSS to its expectation under a null reference distribution (points drawn uniformly over the data's range). Choose k where the gap is maximized, or the smallest k within one standard error of the maximum. More principled than the elbow but requires generating and clustering multiple reference datasets, so it's noticeably more expensive.
BIC/AIC via Gaussian Mixture Models (a further, more advanced option). Fit GMMs for a range of k and pick the one minimizing BIC (or AIC); this captures cluster covariance structure and penalizes unnecessary complexity, but assumes the Gaussian mixture model family is a reasonable fit to begin with, and is more setup than most interviews expect as a first answer.
Stability. Re-run clustering across different initializations and bootstrap subsamples, and measure how much the resulting cluster assignments agree with each other (e.g., the Adjusted Rand Index, a score from -1 to 1 that measures how similar two cluster assignments are, correcting for the agreement you'd expect from random chance; 1 means identical assignments). A k that produces consistent assignments across resamples is more trustworthy than one that only looks good on a single run.
At very large scale: use mini-batch k-means instead of full k-means, compute WCSS/silhouette/gap on a representative subsample rather than the full dataset, and use distributed implementations (Spark MLlib or similar) when even the subsample-based computation needs to be parallelized.
Worked example
Six points in one dimension: 1, 2, 3, 10, 11, 12, an intentionally clear two-cluster structure, to show what the elbow actually looks like numerically.
k=1: {1,2,3,10,11,12}, μ=6.5, WCSS=125.5
k=2: {1,2,3} (μ=2), {10,11,12} (μ=11), WCSS=4
k=3: {1,2,3}, {10,11}, {12}, WCSS=2.5
Going from k=1 to k=2, WCSS drops by 121.5 (from 125.5 to 4), a huge improvement because the true structure genuinely has two groups. Going from k=2 to k=3, WCSS only drops by a further 1.5 (from 4 to 2.5), because the third cluster is just splitting an already-tight group. That sharp drop-then-flatten pattern between k=1 to 2 and k=2 to 3 is exactly what "the elbow" means in practice, k=2 is the right call here.
Trade-offs & pitfalls
- No single metric is authoritative. The elbow is visual and can be ambiguous on noisier or less separated data than this example; silhouette assumes convex clusters; gap statistic is expensive; BIC assumes a Gaussian mixture. Use agreement across methods as your real signal.
- Stability matters as much as any single-run metric. A clustering that changes drastically with a different random seed or a 10% subsample is not a reliable choice of k, regardless of what WCSS or silhouette says on one run.
- At scale, computing exact silhouette or gap statistics on the full dataset is often infeasible, subsampling introduces its own variance, so validate on more than one subsample before committing.
- Pitfall: treating "choose k" as a purely statistical decision. If the business use case needs exactly 5 actionable segments, a statistically "better" k=7 may be the wrong operational answer, domain constraints deserve a seat at the table alongside the quantitative methods.
How does a random forest work? Explain bagging and feature subsampling, why that reduces variance versus a single tree, and what out-of-bag error gives you for free.
Sample Answer
Direct answer
A random forest trains many decision trees, each on a bootstrap sample of the training data and each considering only a random subset of features at every split, then averages their predictions (or takes a majority vote for classification). Bagging reduces variance because averaging many models trained on different resamples of the data cancels out each individual tree's idiosyncrasies; feature subsampling adds a second, independent source of randomness that decorrelates the trees from each other, which matters because averaging correlated predictors reduces variance far less than averaging independent ones. Out-of-bag error gives you a validation estimate essentially for free, since each tree naturally leaves out about a third of the data it never trains on.
Bagging: bootstrap aggregation
For each of N trees, draw a bootstrap sample (sampling with replacement, same size as the original training set) and train a full, largely unconstrained tree on it. Individual trees trained this way are high-variance, low-bias, small changes in the resample can produce quite different trees, but averaging many of them cancels that variance out while the bias of the average stays close to the bias of a single tree.
Feature subsampling: why it's not redundant with bagging
At every split, instead of considering all features, only a random subset (often p features for classification, out of p total) is considered. Without this, if one feature is a strong predictor, nearly every bootstrap tree would split on it near the root, making the trees highly correlated with each other, and averaging correlated predictors doesn't reduce variance nearly as much as averaging independent ones. Forcing different trees to sometimes split on different (weaker) features decorrelates them, which is what actually drives the ensemble's variance reduction.
This decorrelation effect is captured directly in the variance formula for an average of N predictors with pairwise correlation ρ and individual variance σ2:
Var(avg)=ρσ2+N(1−ρ)σ2As N→∞, the second term vanishes and the variance floor is ρσ2, so lower correlation (from feature subsampling) directly lowers the best achievable variance, not just the rate at which you get there.
Out-of-bag (OOB) error
Because each tree's bootstrap sample is drawn with replacement from n points, any given training point has probability (1−n1)n≈e1≈0.368 of being left out of a given tree's sample entirely. Averaging each point's predictions only across the trees that didn't see it during training gives an estimate of generalization error using data the forest genuinely never trained on for that prediction, without setting aside a separate validation set.
Worked example: real numbers for both mechanisms
Variance reduction from bagging + decorrelation. Take a single tree's prediction variance σ2=4.0 and, thanks to feature subsampling, an average pairwise correlation between trees of ρ=0.2, with N=100 trees:
Var(avg)=0.2(4.0)+100(1−0.2)(4.0)=0.8+0.032=0.832That's a reduction from 4.0 to 0.832, about a 79% drop in prediction variance versus a single tree, and notice that raising N further barely helps once you're near the ρσ2=0.8 floor: going from 100 to 1,000 trees would only shave the second term from 0.032 to 0.0032, the correlation term dominates once N is reasonably large.
OOB fraction. For n=100 training points, the exact left-out fraction per tree is:
(1−1/100)100=0.3660which matches the 1/e≈0.3679 limiting approximation closely even at a moderate n=100, confirming the "roughly a third of the data is held out per tree" rule of thumb used in practice.
Trade-offs vs. a single tree
- Much lower variance and generally better generalization, at the direct cost of interpretability: you can trace exactly why a single tree made a decision, but not easily for an average of 100 of them (though permutation or SHAP-based feature importance partially recovers this).
- Higher memory and inference cost, since prediction now means running the input through every tree and averaging, versus a single traversal.
- Individual trees can still overfit somewhat if left completely unconstrained, so
max_depth/min_samples_leafare still worth setting even inside a forest, just less aggressively than you would for a single standalone tree. - OOB error is a convenient built-in validation signal, but it's not a full substitute for a proper held-out test set when you're also using the OOB score to select hyperparameters, since repeatedly optimizing against the same OOB estimate can itself start to overfit to it.
Walk through the k-means algorithm step by step: initialization, assignment, and centroid update. Why does initialization matter, how does k-means++ help, and what are its failure modes (non-convex clusters, varying density or scale)?
Sample Answer
Direct answer
Lloyd's k-means algorithm alternates two steps until the assignments stop changing: assign every point to its nearest centroid, then recompute each centroid as the mean of the points assigned to it. It minimizes within-cluster sum of squared distances, but that objective is non-convex, so where you start genuinely changes where you end up; k-means++ fixes this by choosing smarter starting centroids instead of random ones. Its failure modes (non-convex clusters, clusters of different density or scale) all trace back to the same root cause: k-means implicitly assumes clusters are round, similarly-sized, and separated mainly by Euclidean distance to a single center.
The algorithm, step by step
- Initialization: choose k starting centroids.
- Assignment: assign every point to the nearest centroid (by Euclidean distance).
- Update: recompute each centroid as the mean of the points now assigned to it.
- Repeat steps 2-3 until assignments stop changing (convergence to a local minimum of within-cluster sum of squares).
Why initialization matters
Because the objective is non-convex, different starting centroids converge to different local minima, some clearly worse than others. Bad luck in initialization can leave a centroid stranded with very few (or zero) points, or produce a lopsided partition that doesn't match the data's real structure. This is why running k-means once and trusting the result is risky; multiple random restarts, keeping the run with the lowest final within-cluster sum of squares, is a standard mitigation.
How k-means++ helps
Instead of picking all k starting centroids uniformly at random, k-means++ picks the first centroid uniformly at random, then picks each subsequent centroid with probability proportional to its squared distance from the nearest already-chosen centroid. This actively spreads the initial centroids out across the data rather than risking two starting centroids landing close together, and it comes with a theoretical guarantee (expected O(logk)-competitive with the optimal clustering), a meaningfully better starting point than uniform random in both theory and practice.
Worked example: one full iteration, with real numbers
Seven 2D points: (1,1),(1.5,2),(3,4),(5,7),(3.5,5),(4.5,5),(3.5,4.5). Pin the initial centroids deliberately far apart: c1=(1,1), c2=(5,7).
Assignment (iteration 1): computing Euclidean distance from every point to both centroids, points (1,1) and (1.5,2) are closer to c1; points (5,7), (3.5,5), (4.5,5), and (3.5,4.5) are closer to c2. Point (3,4) is an exact tie, 13≈3.6056 to each centroid, not a near-tie with a margin either way. Distance alone can't break an exact tie; a real implementation needs an explicit convention (e.g. assign to the lower-indexed centroid). Applying that convention here, (3,4) joins c1's group for this iteration.
Update: the new centroids are the means of each group:
c1′=mean((1,1),(1.5,2),(3,4))=(1.833, 2.333) c2′=mean((5,7),(3.5,5),(4.5,5),(3.5,4.5))=(4.125, 5.375)Assignment (iteration 2): re-checking distances with the updated centroids, (3,4)'s distance to c1′ is ≈2.03 and its distance to c2′ is ≈1.78, so it is now closer to c2′ and switches clusters. This is the algorithm doing real work between iterations, not just converging trivially, a point's assignment can and does flip as the centroids move, and the algorithm continues until no point's assignment changes between consecutive iterations.
Failure modes
- Non-convex cluster shapes: k-means assigns purely by distance to a single center, so it cannot separate, for example, two concentric rings or a crescent-and-blob shape, even though those might be visually obvious clusters to a human. A density-based method (DBSCAN) or spectral clustering handles this; k-means structurally cannot.
- Varying density: k-means tends to split a single, naturally large/sparse cluster into pieces while merging a genuinely separate, small/dense cluster into a neighboring one, because it optimizes total squared distance, not density.
- Varying scale (unequal cluster size or spread): a large, diffuse cluster next to a small, tight one often has some of the large cluster's points pulled toward the small cluster's centroid, since k-means has no notion of "this cluster is allowed to be bigger."
Trade-offs and pitfalls
- Always standardize features before k-means; like KNN and SVM, it's a distance-based method and is sensitive to whichever feature has the largest raw numeric range.
- k itself has to be chosen, k-means doesn't determine it; the elbow method (plotting within-cluster sum of squares against k) and the silhouette score are the two common approaches, each with real limitations (the elbow is often not sharp in practice, and silhouette can be expensive to compute at scale).
- Mini-batch k-means trades some accuracy for a large speedup on very large datasets by updating centroids from small random batches rather than the full dataset each iteration, worth reaching for once naive k-means's per-iteration cost (O(nkd)) becomes the bottleneck.
- Because of the non-convexity, always run several initializations (or trust k-means++'s single smarter initialization plus a modest number of restarts) and keep the lowest-cost result, a single run's output should never be reported as "the" clustering without that check.
What is overfitting, and what is underfitting? Walk through a practical, prioritized checklist you'd use to reduce overfitting in a production model.
Sample Answer
Direct answer
Underfitting is when a model is too simple to capture the real pattern in the data, so it does poorly on both training and validation data. Overfitting is when a model captures the training data's noise along with (or instead of) the real pattern, so it does well on training data but poorly on new data. In production, the prioritized checklist runs data quality and evaluation setup first, since those catch overfitting you'd otherwise chase with the wrong fix, then model capacity, then explicit regularization.
A prioritized checklist for reducing overfitting
- Fix the validation strategy first. If your validation split is leaking information from training (duplicate rows across the split, a time-ordered problem evaluated with a random split, or validation-set peeking across many iterations), every other fix you try is being graded on a broken exam. This costs nothing to check and is the single most common root cause of "overfitting" that isn't actually about the model.
- Get more or better data, or clean what you have. More representative examples reduce variance directly; removing mislabeled points or clear outliers stops the model from learning noise you introduced yourself.
- Reduce model capacity relative to the data you have. For a tree, that's shallower depth or a higher
min_samples_leaf; for a linear model, that's fewer features or stronger regularization. Capacity should scale with how much signal your data can actually support, not with what your library defaults to. - Add explicit regularization or ensembling. L1/L2 penalties on a linear model, or bagging (random forest) to average out the variance of individual trees, directly trade a little bias for a lot less variance.
- Cross-validate the choice, don't eyeball one train/test split. A single split can look fine by luck; k-fold (or a time-aware variant) gives you a more honest read before you commit to a configuration.
- Monitor in production, not just at training time. Data drift can turn a well-fit model into an effectively overfit one months later, since the patterns it learned stop matching the current distribution.
Worked example: watching overfitting happen, with real numbers
Fit polynomial regression of increasing degree to a noisy sine wave, with everything pinned so the numbers are exact: 20 points on x∈[0,1], y=sin(2πx)+ε with ε∼N(0,0.22), seeded with numpy.random.default_rng(42), first 14 points as the training set and the last 6 as a held-out test set (a fixed split, not random, so the result reproduces exactly).
import numpy as np
rng = np.random.default_rng(42)
x = np.linspace(0, 1, 20)
y = np.sin(2*np.pi*x) + rng.normal(0, 0.2, size=x.shape)
x_train, y_train = x[:14], y[:14]
x_test, y_test = x[14:], y[14:]
def fit_and_eval(degree):
X_train = np.vander(x_train, degree+1, increasing=True)
X_test = np.vander(x_test, degree+1, increasing=True)
beta, *_ = np.linalg.lstsq(X_train, y_train, rcond=None)
train_mse = np.mean((X_train @ beta - y_train)**2)
test_mse = np.mean((X_test @ beta - y_test)**2)
return train_mse, test_mse
Running this for degrees 1, 3, and 9 gives:
| Degree | Train MSE | Test MSE |
|---|---|---|
| 1 (underfit) | 0.1787 | 0.2973 |
| 3 (good fit) | 0.0250 | 0.2873 |
| 9 (overfit) | 0.0064 | 476,628.15 |
Degree 1 is underfitting: both errors are high and close together, since a straight line can't represent a sine wave. Degree 3 is the sweet spot: training error drops and test error is close to (slightly below) degree 1's. Degree 9 is a textbook overfit: training error looks best of all three (0.0064) while test error explodes to nearly half a million, because with 14 training points and a degree-9 polynomial (10 coefficients) the model has just enough freedom to fit the noise almost exactly and swing wildly between points.
Trade-offs and pitfalls
- Train error alone never tells you if you're overfitting; you need the train/validation gap, and the gap only means something if the validation split is trustworthy (see item 1 above).
- Simplifying a model too aggressively swaps overfitting for underfitting; the goal is matching capacity to the data, not always making the model smaller.
- Regularization strength and model capacity interact: a large model with strong regularization can behave like a much smaller model, so "add more regularization" and "use a simpler model" are often two ways of reaching the same place, not independent fixes to stack.
- Production drift monitoring is easy to skip because it doesn't show up in an offline train/test split at all; a model that generalized well at launch can quietly become overfit to a distribution that no longer exists.
A product manager asks you to explain what the coefficients from your logistic regression mean. How do you explain odds ratios and feature impact to someone without a stats background?
Sample Answer
Direct answer
Explain it in two layers: first, a plain-language sentence about direction and rough size ("holding everything else equal, one more support ticket in the last 30 days roughly multiplies a customer's odds of churning by 1.5"), then, if they want more, the odds-ratio mechanics behind that sentence. The key translation step a PM needs is going from a coefficient (which lives on the log-odds scale, not intuitive to anyone) to an odds ratio (a multiplier, more intuitive) to an approximate change in probability at their specific baseline rate (the only thing that actually maps to "how big a deal is this").
Structured elaboration
Why coefficients aren't directly readable. Logistic regression models log(1−pp)=β0+β1x1+⋯+βnxn, the log-odds, as a linear function of the features. A coefficient βj is the change in log-odds per one-unit increase in xj, holding other features fixed. Nobody, including most data scientists on first encounter, has good intuition for what "log-odds increased by 0.4" feels like, so the raw coefficient is the wrong unit to hand a PM.
Step 1: convert to an odds ratio. Exponentiating gives ORj=eβj, the odds ratio: exactly how much the odds (not the probability) get multiplied by a one-unit increase in xj. This is already more intuitive ("odds go up by 50%") but "odds" is still not the same thing as "probability", and conflating them is the single most common mistake in explaining this to a non-technical audience.
Step 2: convert odds ratio to an approximate probability change at a specific baseline. Odds and probability relate by odds=1−pp, so the same odds ratio produces a very different probability change depending on the baseline rate: a 50% odds increase moves an 8% baseline probability to roughly 11.5%, but the same 50% odds increase would move a 50% baseline probability all the way to 60%. This baseline-dependence is exactly why you should never say "50% higher odds" and let a stakeholder silently substitute "50% higher probability", the two are only close to each other when the baseline probability is very small.
A concrete script for the PM conversation: "Right now, about 8 out of 100 customers churn. For every extra support ticket in the last 30 days, a customer's chance of churning goes up to roughly 11 or 12 out of 100, holding everything else about them the same. It's not a huge jump for one ticket, but it compounds: several tickets in a row push that number up faster than you'd guess from a straight line, because we're modeling odds, not probability, directly."
Worked example
Suppose βj=0.4 for "number of support tickets in the last 30 days", and the baseline churn probability at the average customer's other feature values is p0=0.08.
- Odds ratio: e0.4≈1.492.
- Baseline odds: 1−0.080.08=0.08696.
- New odds after a one-unit increase: 0.08696×1.492≈0.12972.
- New probability: 1+0.129720.12972≈0.1148, i.e. about 11.5%.
So one extra ticket moves this customer from an 8.0% to an 11.5% chance of churning, a 3.5 percentage-point increase (verified by computing it two independent ways: through the odds transformation above, and directly by shifting the logit by 0.4 and re-applying the sigmoid, both give 0.1148). Note this is a local approximation around this specific baseline; the same coefficient produces a different percentage-point shift for a customer starting at, say, a 40% baseline churn probability, which is exactly why "the effect" has to be quoted relative to a stated starting point rather than as one universal number.
Trade-offs & pitfalls
- Never say "the odds went up 50%, so the probability went up 50%"; that's only approximately true when the baseline probability is very small (well under 10%), and it's flatly wrong otherwise.
- Always anchor the explanation to a real baseline rate the PM already has intuition for ("our typical customer", "our highest-risk segment") rather than quoting the odds ratio in the abstract.
- "Holding everything else equal" is doing real work in this sentence and is worth saying out loud: if support tickets are correlated with tenure or plan type, a PM might otherwise misread the coefficient as the total observed effect of tickets in the raw data, not the effect isolated from those other features.
- Coefficients only describe correlational structure inside a fitted model, not causation; if the eventual decision is "let's proactively call customers with many tickets to prevent churn", that's a causal claim the model alone doesn't license, worth flagging explicitly before a business decision is made on it.
Unlock Full Question Bank
Get access to all 31 Classical Machine Learning Algorithms interview questions and detailed answers.
Sign in to ContinueJoin thousands of developers preparing for their dream job.