LearnThatStack Ace your next interview

Classical ML Algorithms.
Cheat sheet.

Quick reference for Classical ML Algorithms - sectioned for fast scanning. Skim the part you're shaky on, walk in confident.

Machine Learning & Data Science 12-section reference ~18 min read

30-second picker

Situation Reach for
Tabular, want an honest baseline with no tuning Random forest
Tabular, last few points of accuracy, someone owns tuning Gradient boosting (XGBoost/LightGBM)
Need coefficients you will report or defend Linear / logistic regression on standardized features
Wide data (p near or above n), want a smaller model Lasso or Elastic Net
Text or sparse high-dim counts Linear SVM or logistic regression (not KNN, not RBF SVM)
Under ~100k rows, curved boundary, clean labels RBF SVM
Clusters, k defensible, round blobs K-Means
Clusters of arbitrary shape, outliers must stay out DBSCAN
Small n, want the nesting / no k up front Agglomerative + dendrogram
Squash 50 columns into a few features PCA
Just looking at the data in 2D t-SNE / UMAP (never as features)

Check whether this model needs feature scaling

Model Scale?
KNN, K-Means, DBSCAN, SVM (any kernel), PCA Yes, mandatory
Ridge / Lasso / Elastic Net Yes - the penalty is scale-dependent
Plain OLS / unpenalized logistic Not for the fit; yes to compare coefficients or speed the solver
Decision tree, random forest, boosted trees No

Fit the scaler on the training fold only. Fitting on train+test leaks distribution info.

Linear and logistic regression

Read a linear coefficient correctly

Expected change in the target per +1 unit of that feature, others held fixed. Intercept = prediction when every feature is 0, often far outside the data and meaningless.

  • Raw coefficients are not comparable across features (millimetres earn tiny numbers). Standardize first to rank influence.
  • "Others held fixed" is fiction when features move together, so it is not a causal effect. Signs can flip when you add or drop a correlated feature.
  • Always report a coefficient with the exact feature set it was fitted on.

Diagnose broken regression assumptions from one plot

Residuals vs fitted values:

Pattern What broke Damage
Funnel / fan Constant variance p-values and CIs lie; the line still looks fine
Curve or bend Linearity Coefficients are wrong
Time order or repeated subjects Independence Model looks far more confident than it is

Linearity and independence corrupt the estimates. Constant variance and normality only corrupt the uncertainty around them.

Turn a logistic coefficient into an odds ratio

import math
math.exp(0.7)    # 2.01 -> odds roughly double per +1 unit
  • log-odds = ln(p / (1-p)); logistic regression is linear in that space.
  • A coefficient is not a probability change. Odds 0.01 -> 0.02 barely moves p; odds 1 -> 2 moves p from 0.50 to 0.67.
  • Report odds ratios, and quote a probability only at a stated baseline.

Remember what the sigmoid does to gradients

1 / (1 + math.exp(-2.2))   # 0.900   (z=0 -> 0.5, z=-2.2 -> 0.100)

Saturates: once |z| is large the curve is flat and the gradient nearly vanishes, so a confidently wrong prediction learns very slowly. Unscaled features push scores into that flat region and training stalls.

Decide between the normal equation and gradient descent

  • Closed form: one step, roughly O(p^3), needs X'X invertible. Perfectly correlated columns make it singular, near-collinear ones make it unstable. Good libraries use QR or SVD, never a literal inverse.
  • Gradient descent when p is large, data does not fit in memory, updates stream, or the loss has no closed form (logistic, lasso, anything boosted).

Detect and handle multicollinearity

Step Do this
Detect VIF > 5 suspect, > 10 serious; pairwise correlation; large condition number
Fix Drop one of the pair, combine into one index, or use Ridge (spreads weight instead of picking arbitrarily)
Ignore If you only need predictions - accuracy barely suffers; trees just split importance between the twins

Symptom: coefficients swing wildly across resamples and signs flip against intuition, long before accuracy degrades.

Regularization

Pick L1 vs L2 vs Elastic Net

Want Use Catch
Sparsity, fewer features to serve and explain L1 (Lasso) Selected set is unstable across resamples - treat it as a hypothesis, not truth
Stability when every feature carries some signal L2 (Ridge) Shrinks toward zero, never reaches it
Correlated feature groups, or p > n Elastic Net Two knobs (strength + mix); coefficients shrunk twice so effects read small

Lasso keeps roughly one member of a correlated group at random, and can never select more features than you have rows. The L2 part of Elastic Net fixes both.

Choose the regularization strength

  • Log-spaced grid (1e-3 to 1e2, times 10 or times 3 per step), cross-validated. Linear grids waste most of their points.
  • Pick by the metric you care about, never training loss - that always prefers the weakest penalty.
  • One-standard-error rule: take the strongest lambda scoring within 1 SE of the best. Simpler model, almost no accuracy cost.
  • The CV score at the winning lambda is optimistic. Keep a separate test split for the number you report.

Trees and ensembles

Recall how a split is chosen and what "pure" means

Greedy scan of every feature x every candidate threshold. Score = parent impurity minus the size-weighted average child impurity. Impurity is Gini or entropy (classification), variance (regression). Pure = one class only, impurity 0. Optimal trees are NP-hard, so greedy is the accepted compromise.

Stopping only at purity is memorization - the tree can always isolate single rows. Cap max_depth, set min_samples_leaf, or prune.

Gini favours high-cardinality features (more thresholds means more chances to look good). Watch identifier-like columns.

Know what each random forest randomization buys

  • Bagging: each tree gets its own bootstrap sample (n draws with replacement), so structures diverge.
  • Feature randomness: each split sees only a random subset, sqrt(p) for classification, p/3 for regression. Without it one dominant feature roots every tree, errors correlate, and averaging barely helps.
  • Trade-off: a smaller subset decorrelates better but weakens each tree. Worth tuning.

Use out-of-bag error instead of a holdout

Each row is scored only by the trees whose bootstrap sample excluded it. Behaves like k-fold CV for the price of one training run.

(1 - 1/10_000) ** 10_000   # 0.3679 -> 1/e, so ~37% of rows are OOB per tree
  • Reads slightly pessimistic on small forests (each row judged by ~1/3 of the trees).
  • Assumes independent rows, so it silently leaks on time series or repeated entities. Use a grouped split there.

Recall the gradient boosting loop

Start at a constant. Each round: compute the loss gradient per row, fit a shallow tree to those pseudo-residuals, add it to the running prediction scaled by the learning rate.

  • Squared error: the gradient is exactly the residual, so "fit the residuals" is literal.
  • Log loss: it is a pseudo-residual, and leaf values are chosen to reduce the actual loss.
  • Nothing in the procedure knows a residual is measurement error, so it chases label noise forever. Early stopping on a held-out set is not optional.

Tune a booster in the right order

  1. Fix learning_rate around 0.05, let early stopping choose n_estimators. Never tune the two independently - halving the rate roughly doubles the trees needed for a small gain.
  2. max_depth 3-8 (this caps interaction order) or num_leaves.
  3. Row and column subsampling 0.5-1.0 - cuts variance and speeds every round.
  4. min_child_weight / min samples per leaf, plus L2 on leaf weights, to stop tiny leaves memorizing rare rows.

Do not tune on the same split you early-stop on. Use a third holdout, or nested folds when data is small.

Choose forest vs boosted trees

Random forest Gradient boosting
Trees Deep, parallel, averaged Shallow, sequential
Hyperparameters Hard to break Easy to break, rewards tuning
Free validation OOB estimate None
Noisy labels Robust Fits the noise

Boosting usually wins by a few points on tabular data, not by a category. If a forest ties a tuned booster, ship the forest.

Recall what XGBoost changed

  • Second-order objective (gradient + Hessian); each leaf value is a Newton step. Leaf L2 penalty plus a fixed cost per split, so pruning falls out of the objective.
  • Speed is data layout: features pre-binned into histograms, compressed column blocks, one linear scan per level, threaded, cache friendly, spills to disk.
  • Missing values get a learned default direction - on wide sparse matrices that alone is most of the win.
  • Rounds are still sequential. More cores never shorten a 1000-round chain.

KNN

Recall how KNN predicts and what it cannot do

Store the rows, predict from the k nearest. Classification = majority vote; regression = mean of the neighbours' targets, optionally weighted by 1/distance. No training step; roughly O(n*d) distance computations per query.

  • Prediction surface is a staircase, not a curve.
  • Cannot extrapolate: past the training range it returns the same edge average, flat forever. Bad for trending data like prices over time.

Choose k

  • Sweep k on a validation set, plot error, pick the flat region. sqrt(n) is a starting point, nothing more.
  • Small k tracks noise (one mislabelled neighbour flips the answer). Large k smooths across the boundary and drags toward the majority class.
  • Odd k only breaks ties in binary problems - with 3 classes a 2-2-1 vote still ties at k=5.
  • Distance weighting matters more than parity: it makes ties rare and stops large k washing out local structure.
  • On imbalanced data check per-class recall; raising k quietly buries the minority.

Choose a distance metric

Feature type Metric
Continuous, comparable scales Euclidean (Minkowski p=2)
High-dim, counts, grid-like Manhattan (p=1)
Text / sparse, length varies Cosine (ignores magnitude)
Binary or categorical Hamming
Mixed numeric + categorical Gower

The metric is a real hyperparameter - change it and the neighbour set, and so the prediction, changes.

Scale features before KNN

Distance sums squared differences, so the widest-range feature owns every decision. A 20-year age gap versus a 5000 salary gap is 400 against 25,000,000 - the model is a salary lookup wearing a KNN costume.

Do not standardize by reflex: it gives pure noise columns equal say. Min-max for bounded features, robust scaler for heavy tails.

Know why KNN dies at scale

The cost sits at serve time, and the model is the data, so memory grows with the dataset you keep collecting. KD-trees and ball trees get you near O(log n) in low dimensions but degrade to a full scan past roughly 20 features. Escapes: subsample, cluster the training set into representatives, or move to a model that compresses training into weights.

Know why high dimensions break neighbours

Distances concentrate - the ratio of farthest to nearest drifts toward 1, so the top neighbour is barely closer than the hundredth and the vote is near random. Irrelevant features add distance and no signal, so adding features often makes KNN worse. Fix order: select features, weight dimensions by relevance, PCA, or change model. Cosine helps on sparse text.

SVM

Recall the margin and support vectors

Hyperplane: w . x + b = 0. Margin is the gap to the closest point on each side. Maximizing it is convex, so the boundary is unique and reproducible run to run (perceptron-style rules return whatever they stumble into). Support vectors are the points on the margin edge - they alone determine the boundary; delete every other row, refit, get the same model.

Margin is measured in feature units, so it means nothing without scaling. A handful of borderline points control everything - one mislabelled edge row visibly shifts the boundary.

Set C (soft margin and slack)

Hard margin demands zero training errors and only exists on perfectly separable data; one bad label makes it infeasible. Soft margin allows slack per point: 0 = outside the margin and correct, between 0 and 1 = inside the margin but correct, above 1 = misclassified.

C Behaviour
Large Violations expensive -> narrow margin, near hard-margin, a few outliers bend the boundary
Small Tolerant -> wide margin, eventually underfits

Always tune C. Do not assume hard margin generalizes even on separable data.

Use the kernel trick, and know when not to

A kernel returns the dot product two points would have in a higher-dimensional space without building that space. RBF corresponds to infinite dimensions in one exponential, which buys curved boundaries from a linear algorithm.

  • gamma too high memorizes individual points.
  • Training scales O(n^2) to O(n^3) and prediction touches every support vector, so it stalls past roughly 100k rows. On large tabular data a linear model or boosted trees win on both score and time.

Naive Bayes

Know what the independence assumption costs

Features are assumed independent given the class, so per-feature probabilities multiply instead of estimating a joint distribution. Almost always false ("new" and "york"), but the class ranking survives a lot of wrongness, so classification stays usable while the probabilities do not. Correlated features act as repeated votes, saturating scores near 0 or 1. Treat the output as a rank, never as calibrated confidence you can threshold on.

Set Laplace smoothing (alpha)

Any feature-class pair never seen together gets probability 0, and one zero factor drags the whole posterior to 0 - a single unknown token vetoes an obvious spam.

P = (count + alpha) / (total + alpha * V), V = vocabulary size.

alpha=1 is convention, not law. On a 100k-token vocabulary, adding 1 to every cell floods rare classes with fake evidence and flattens the model. Sweep it; small values often beat 1 on large vocabularies.

Loss functions and class imbalance

Recall what cross-entropy penalizes

Negative log of the probability assigned to the true class.

-math.log(0.9)   # 0.105  barely punished
-math.log(0.1)   # 2.303  over 20x worse

Averaged over the dataset it equals the negative log-likelihood, so minimizing it is maximum likelihood estimation. It cares about calibrated confidence, not just which class wins.

Know why not MSE for classification

Paired with a sigmoid, MSE gives a non-convex surface with flat regions: confidently wrong means the sigmoid derivative is near 0, the gradient vanishes, and learning stalls. Cross-entropy cancels that derivative - the gradient reduces to (prediction - label), so confidently wrong produces a large gradient. MSE is still fine as an evaluation score for probabilities (the Brier score); it is just the wrong training loss.

Diagnose what imbalance actually did

The loss sums over rows, so the majority class supplies most of the gradient. Boundaries drift into the minority region, and predicted probabilities pull toward the base rate, so a 0.5 threshold may never be crossed.

Imbalance only bites where classes overlap. With clean separation, a 1-in-1000 split trains fine. Check first: is ranking quality (PR-AUC) bad, or is only the threshold wrong?

Choose an imbalance fix

Method When
Threshold moving Cheapest and often enough. Train on the natural distribution, pick the cutoff from a validation curve. Fit unchanged, probabilities stay calibrated
Class weights Rescales the loss per class. No rows added, no leakage risk, flat memory
Random undersampling Much faster training, throws away real signal, needs probability recalibration afterwards
SMOTE Only when the minority is one dense blob. Several rare modes -> interpolation manufactures rows that never occur. In high dims the neighbours are near arbitrary

Start with class weights + threshold moving. Reach for SMOTE only after those plateau, and only inside the training fold.

Build a classifier when positives are under 1%

  • Count positives in absolute terms first. 1% of 10M = 100k positives, which is plenty. 1% of 3k = 30, and no algorithm rescues that - aggregate features, use rules or anomaly detection, and buy labels.
  • Default with enough positives: boosted trees with class weights, keeping every majority row that fits in memory.
  • If you must undersample, recalibrate the predicted probabilities or cost-based decisions become meaningless.
  • Rank by score and cut where a false positive costs what a catch is worth.
  • Monitor the positive rate and score distribution. Rare classes drift, and a stale threshold quietly sends the wrong volume to review.

Cross-validate imbalanced data without leaking

  • Resample inside each training fold, never before the split. Wrap sampler + estimator in a pipeline so it refits per fold.
  • Oversampling first puts duplicate rows on both sides; a SMOTE point built from a held-out neighbour carries that neighbour's position into training.
  • Score on the untouched, still-imbalanced fold - only that resembles production.
  • Stratify the splits or a fold lands with zero positives and an undefined metric. Few positives -> repeated stratified splits, since one 5-fold run has huge variance.
  • Repeated customers or events need a grouped split too.

Choose One-vs-Rest or One-vs-One

One-vs-Rest One-vs-One
Models for K classes K K(K-1)/2
Best for Anything roughly linear in n (default) Kernel SVM and other superlinear learners - many tiny fits beat a few huge ones
Balance Manufactures imbalance (20 classes -> each task is 5% positive) Each fit sees 2 classes, balanced and simpler
Combining Scores from separate models are not comparable Votes; ties possible, slower prediction

Clustering

Recall the K-Means loop

Assign every row to the nearest centroid (squared Euclidean), then move each centroid to the mean of its assigned rows. Repeat until nothing moves. Each pass is O(nkd), so it stays fast on millions of rows. The mean is exactly the point minimizing within-cluster squared distance, so the objective only falls - to a local optimum.

Fix bad initialization

Two seeds landing inside one true cluster split it and merge two others. The loss goes flat, so it looks converged and stable. k-means++ picks seeds one at a time with probability proportional to squared distance from the nearest chosen centroid, at one extra pass per centroid. It reduces bad seeds, it does not remove them: run 10 restarts and keep the lowest inertia. If those restarts disagree badly, the problem is k or cluster shape, not seeding.

Choose the number of clusters

  • The elbow is weak. Within-cluster sum of squares always falls, so you are hunting a bend, not a minimum, and on real data three people read three elbows.
  • Silhouette (own cluster vs nearest other cluster) has a real maximum. The gap statistic compares your curve against uniform random data. Both still assume your algorithm's cluster shape.
  • Stability check: rerun on subsamples. A k whose groupings change is not real.
  • Cap k by the downstream use - segments a team can actually run campaigns for.

Recognize when K-Means is the wrong tool

It assumes round blobs of similar size, and every boundary is a straight line halfway between two centroids. Rings, crescents and elongated bands get sliced across their true shape while the loss still looks fine. The mean is not robust either: one far point drags its centroid off the dense region, and that shifted centroid then steals nearby points.

Problem Swap to
Outliers K-Medoids (centre is a real data point)
Stretched / unequal covariance Gaussian mixture
Arbitrary shapes DBSCAN

If the geometry really is spherical and clean, K-Means remains the fastest sensible choice - do not abandon it by reflex.

Choose K-Means or hierarchical

K-Means: needs k up front, O(nki*d), scales to millions, seed-dependent. Agglomerative: no k up front, roughly O(n^2 log n) time and O(n^2) memory, deterministic, and produces a dendrogram you can cut at any height - which matters when the domain is genuinely nested (taxonomies, document topics). Past tens of thousands of rows it stops being practical.

Middle path: cluster a sample hierarchically to read off k, then run K-Means on everything.

Pick a linkage rule

Linkage Produces Failure mode
Single (closest pair) Elongated, snaking clusters Chains through noise - one bridge point merges two real clusters
Complete (farthest pair) Compact clusters Splits genuinely large clusters, reacts badly to outliers
Average (mean of all pairs) Middle ground, good default for non-Euclidean distances -
Ward (least variance increase) Even, spherical clusters; the usual default Requires Euclidean; imposes round equal-sized groups on data that has neither

Linkage changes the result more than the distance metric does. Compare single against Ward when you suspect elongated structure.

Set DBSCAN's eps and minPts

A point is a core point when at least minPts neighbours sit within radius eps. Core points that reach each other chain into a cluster of any shape; points that never reach the threshold are labelled noise, which makes it a usable anomaly detector.

Knob Too small Too large
eps Almost everything becomes noise Separate clusters melt into one blob
minPts Loose, sprawling clusters Fewer, tighter clusters, more noise
  • Starting rule: minPts about 2x the feature count; tune eps from the knee of a k-distance plot.
  • Both knobs live in your data's units, so scale first.
  • eps is a single global radius, so clusters at different densities cannot both work.
  • Also loses in high dimensions (distances flatten) and when every point must get a label.

Dimensionality reduction

Recall what PCA actually computes

New orthogonal axes ordered by variance; keep the first few and project. Eigenvectors of the covariance matrix are the directions, eigenvalues are the variance along them, and eigenvalue / sum(eigenvalues) is the explained variance ratio you report.

  • Components are weighted blends of every original feature, so you can no longer say "the model reacts to income".
  • Linear only - curved or clustered structure survives poorly.
  • Variance is not usefulness. The direction that separates two classes can be low-variance, and PCA will happily discard it.

Standardize before PCA (with one exception)

Variance carries units, so income in currency has variance millions of times larger than age in years, and PC1 points almost exactly along income. Standardizing means running PCA on the correlation matrix instead of the raw covariance matrix. Fit the scaler on training rows only.

Exception: when every feature already shares one unit (pixels, spectra, same-scale sensors), relative variance is meaningful. Forcing unit variance there lifts quiet noisy channels up to the informative ones.

Decide how many components to keep

Why you reduced Rule
Feeding a downstream model Treat n_components as a hyperparameter in a CV pipeline; pick by downstream score
Convention 95% cumulative variance, or where the scree plot flattens - both eyeball, both vague on smooth curves
Plotting 2 or 3 regardless
Compression Smallest count meeting your storage or latency budget

Kaiser's rule (eigenvalue > 1 on standardized data) usually keeps too many.

Relate SVD to PCA

X = U S V', singular values sorted, so truncating after k terms is the best rank-k approximation under squared error. On a column-centered X: rows of V' are the principal directions, s_i^2 / (n-1) is the variance of component i, and the scores are U S.

Libraries compute PCA this way because forming the covariance matrix squares the condition number and needs a d-by-d allocation. Randomized truncated SVD returns the top k in roughly O(ndk) without ever building it.

Centering is the whole difference. Truncated SVD on raw sparse counts (centering would destroy sparsity) is latent semantic analysis, not PCA.

Use t-SNE and UMAP for looking only

A nonlinear embedding fitted to one dataset: it matches pairwise neighbour probabilities, preserving local neighbourhoods and sacrificing global structure.

  • Distances between well-separated blobs are meaningless, and so are cluster sizes.
  • Perplexity and the random seed change the layout. Two runs, two different maps.
  • It is not a transform - classic t-SNE cannot embed a new point, so you cannot fit on train and apply to test. Feeding the coordinates into a model leaks and will not generalize.

Use PCA for features; keep t-SNE/UMAP for looking at data.

Recall the curse of dimensionality

Space grows exponentially with features, so a fixed dataset gets sparse fast - holding density steady would need exponentially more rows per feature added. Consequences: distance-based methods lose contrast, and with enough columns one will look predictive by chance, so trees and linear models start fitting noise. More features are not free; feature selection, regularization or PCA usually beats handing the model every column.

Cost quick-reference

Model Train Predict
OLS, normal equation O(n*p^2 + p^3) O(p)
Decision tree O(nplog n) O(depth)
Random forest T trees, parallel O(T * depth)
Boosting T trees, strictly sequential O(T * depth)
KNN (brute force) free O(n*d) per query
Kernel SVM O(n^2) to O(n^3) O(#support vectors * d)
K-Means O(nkd) per iteration O(k*d)
Agglomerative ~O(n^2 log n), O(n^2) memory -
PCA (randomized SVD) O(ndk) O(d*k)

Leakage and reporting pitfalls

  • Any preprocessing fit outside the CV fold leaks: scalers, PCA, resamplers, target encoding.
  • Tuning and early stopping on the same split. Use a third holdout or nested folds.
  • Reporting the CV score at the chosen hyperparameter as the final number - it is optimistic by construction.
  • Random splits on time-ordered or repeated-entity data. This breaks OOB estimates too; use grouped or time-based splits.
  • Reading a logistic coefficient as a probability change, or comparing raw coefficients across differently-scaled features.
  • Trusting Lasso's selected feature set, Naive Bayes probabilities, the elbow plot, or a single K-Means run.
Found this useful? Pass it on.
Pro · $10/mo

The sheet is free. Pro goes deeper.

Pro opens the full question library behind every sheet, every refresher and a monthly AI allowance. One subscription, all formats.

Full question library All refreshers Cancel anytime