Machine Learning Fundamentals
Machine learning is the craft of getting a computer to learn a useful rule from examples instead of having a human write the rule by hand. This page walks from the very first intuition to production concerns: how data is prepared, how every classical algorithm works, how models are judged with the right metric, and how they are kept healthy after deployment, which is the foundation every deep learning and LLM interview quietly assumes.
- ML learns a function from data; supervised learning uses labels, unsupervised learning finds structure, reinforcement learning learns from rewards.
- Most real-world wins come from the data: clean splits, no leakage, sensible features, and a metric that matches the business cost of each mistake.
- Every model sits somewhere on the bias–variance trade-off; regularization, cross-validation and ensembles are the tools for landing in the sweet spot.
- For tabular data, gradient-boosted trees are the usual strongest baseline; linear models are the most interpretable; kNN, SVM and linear models need scaled features.
- Accuracy lies on imbalanced data: use precision, recall, F1, PR-AUC, calibrated probabilities and a threshold chosen from the cost of false positives versus false negatives.
- A deployed model decays: monitor inputs, predictions and outcomes for drift, and have a retraining and rollback plan.
What machine learning is (and is not)
Traditional programming takes rules + data and produces answers. Machine learning flips this: it takes data + answers (examples) and produces the rules (a model). Once learned, the model is applied to new data to produce new answers. A classic formal definition says a program learns from experience E with respect to a task T and a performance measure P if its performance on T, measured by P, improves with E. For a spam filter: T is labelling emails, P is the fraction labelled correctly (or better, recall on spam at a fixed false-alarm rate), and E is a pile of emails that humans already marked as spam or not.
A traditional program is a recipe card: exact steps written by a chef. Machine learning is an apprentice who tastes hundreds of finished dishes, each labelled “good” or “too salty”, and gradually works out the rule for seasoning on their own. The tasted dishes are the training data, the labels are the targets, the apprentice's internal sense of “how much salt” is the model's parameters, and cooking a brand-new dish is inference on unseen data.
AI vs ML vs deep learning vs generative AI
+--------------------------------------------------------------+ | Artificial Intelligence: any technique that makes machines | | act intelligently (search, logic rules, planning, ML ...) | | +------------------------------------------------------+ | | | Machine Learning: systems that learn rules from data | | | | +----------------------------------------------+ | | | | | Deep Learning: ML with many-layer neural | | | | | | networks that learn their own features | | | | | | +--------------------------------------+ | | | | | | | Generative AI: models that create | | | | | | | | new text, images, audio, code (LLMs, | | | | | | | | diffusion models) | | | | | | | +--------------------------------------+ | | | | | +----------------------------------------------+ | | | +------------------------------------------------------+ | +--------------------------------------------------------------+
| Layer | What it adds | Typical example |
|---|---|---|
| AI | The goal: intelligent behaviour. Can be hand-coded (chess search, expert systems). | Route planner using A* search |
| Machine learning | Rules are learned from data, but features are often hand-engineered. | Gradient-boosted trees predicting loan default from 40 tabular columns |
| Deep learning | Learns the features too, layer by layer, from raw pixels, audio or text. Needs more data and compute. | CNN recognising tumours in scans |
| Generative AI | Models the data distribution well enough to sample new content. | LLM drafting an email; diffusion model generating an image |
When ML is the right tool
Use ML when
- The rule is too complex to write by hand (faces, speech, fraud patterns).
- The rule changes over time and must be relearned (demand, spam tactics).
- You have (or can collect) representative examples with outcomes.
- An occasional error is tolerable and measurable.
Avoid ML when
- A simple deterministic rule already works (tax calculation, sorting).
- There is no data, or labels are impossible to obtain.
- Every decision must be exactly explainable and zero-error is required.
- The cost of building and maintaining the model outweighs the gain.
The vocabulary you must be fluent in
Features (X)
The input columns the model sees: age, pixel values, word counts. Also called predictors, attributes or independent variables.
Target / label (y)
What we want to predict: price, spam or not, cultivar. Also called the response or dependent variable.
Model
A parameterised function f(X; θ) that maps features to predictions.
Parameters
Values learned from data during training: weights, biases, split thresholds, centroids.
Hyperparameters
Settings chosen before training that control learning: learning rate, tree depth, k in kNN, regularization strength C or λ.
Training / fitting
Adjusting the parameters to minimise a loss on the training data. “Fitting the model” simply means training it.
Inference
Using the trained, frozen model to predict on new data. No parameters change.
Generalization
How well the model performs on data it never saw. This, not training performance, is the real goal.
Types of learning
Learning paradigms differ in what feedback the learner gets. That single question decides the whole setup: the loss, the data you must collect and how you evaluate.
Supervised: a guide walks with you and names every street (labelled examples). Unsupervised: you wander alone and notice that some neighbourhoods feel similar (clusters) without anyone naming them. Self-supervised: you cover a word on a street sign and guess it from the rest of the sign, making your own quiz from raw data. Reinforcement: you try routes and a friend only says “faster” or “slower” at the end of each trip (reward). The kind of feedback you get, from street names to a single “faster”, is what separates the four paradigms.
| Paradigm | Feedback | Typical tasks | Example algorithms |
|---|---|---|---|
| Supervised | Correct answer for every training example | Classification (discrete label), regression (number) | Linear/logistic regression, trees, random forest, gradient boosting, SVM, kNN, neural nets |
| Unsupervised | None; only inputs | Clustering, dimensionality reduction, density estimation, anomaly detection, association rules | k-means, DBSCAN, hierarchical clustering, GMM, PCA, t-SNE, UMAP, Isolation Forest |
| Semi-supervised | A few labels plus many unlabelled examples | Classification when labelling is expensive | Self-training / pseudo-labelling, label propagation, consistency regularization |
| Self-supervised | Labels manufactured from the data itself | Pretraining representations | Next-token prediction (GPT), masked language modelling (BERT), Word2Vec skip-gram/CBOW, contrastive learning (SimCLR, CLIP) |
| Reinforcement | Delayed scalar reward from an environment | Sequential decisions: games, robotics, recommendation, RLHF for LLMs | Q-learning, DQN, policy gradients, PPO |
Supervised learning: classification vs regression
Classification
- Output is a category: spam/ham, malignant/benign, one of three wine cultivars.
- Binary, multiclass (one of many) or multilabel (several at once).
- Loss: cross-entropy (log loss), hinge.
- Metrics: precision, recall, F1, ROC-AUC, PR-AUC.
Regression
- Output is a continuous number: house price, temperature, delivery time.
- Can be single or multi-output.
- Loss: MSE, MAE, Huber, quantile.
- Metrics: MAE, RMSE, R², MAPE.
Other useful distinctions
Parametric vs non-parametric
Parametric models have a fixed number of parameters regardless of data size (linear regression). Non-parametric models grow with the data (kNN stores everything; a fully grown tree can have one leaf per sample).
Discriminative vs generative
Discriminative models learn P(y | x) or the boundary directly (logistic regression, SVM). Generative models learn P(x | y) and P(y), or P(x), and can synthesise data (Naive Bayes, GMM, LLMs).
Batch vs online
Batch learning trains on the full dataset offline. Online learning updates incrementally as new data arrives (SGD on streams, partial_fit in scikit-learn).
Instance-based vs model-based
Instance-based methods memorise examples and compare (kNN). Model-based methods compress data into parameters (regression coefficients).
The ML project lifecycle
Algorithms are maybe 10–20% of a real ML project. The rest is framing the problem, getting data right, evaluating honestly and operating the model. Interviewers love candidates who think in this full loop.
You first decide what cuisine the neighbourhood wants (problem framing), source ingredients (data collection), wash and chop them (cleaning and features), test recipes in the kitchen (training and validation), hold a tasting night with outside guests (test set), open to the public (deployment), and keep reading reviews and checking suppliers (monitoring and drift). A great recipe with rotten ingredients or the wrong cuisine still fails, just as a strong algorithm on bad data or a badly framed problem does.
- Frame the problem What decision will the prediction drive? What is the cost of each kind of error? Is ML even needed? Define a baseline (current rule, or “predict the majority class”).
- Choose the metric Translate the business goal into an offline metric (for example recall at precision ≥ 0.60) and an online metric (fraud losses, click-through).
- Collect and label data Ensure it represents production conditions. Check label quality and how labels are generated.
- Explore (EDA) Distributions, missing values, class balance, scale differences, correlations, outliers, leakage suspects.
- Split Train / validation / test before any fitting, stratified or by time or group as appropriate.
- Preprocess and engineer features Impute, encode, scale, create features, all fitted on the training split only (use a
Pipeline). - Train baselines, then stronger models Start simple (logistic regression, a shallow tree), then try ensembles.
- Tune and validate Cross-validation, hyperparameter search, threshold selection on validation data.
- Evaluate once on the test set Report the final number, error analysis by segment, calibration, fairness checks.
- Deploy Batch or real-time serving, shadow or canary rollout, A/B test against the current system.
- Monitor and retrain Watch data drift, prediction drift, performance, latency; retrain on a schedule or on triggers.
Business goal --> Metric --> Data --> EDA --> Split --> Features
^ |
| v
Monitor <-- Deploy <-- Test once <-- Tune/CV <-- Train baselines
|
+--> drift or decay detected --> retrain / re-frame
DummyClassifier and DummyRegressor. A model that cannot beat them is not learning anything useful.Data preparation: splits, leakage, cleaning and features
“Garbage in, garbage out” is the most reliable law in ML. This section covers the practical decisions that decide whether a model's reported score is honest and whether it will hold up in production.
The training set is the textbook and practice problems, the validation set is the mock exam you use to decide how to study, and the test set is the final exam kept sealed until the end. Data leakage is a student who has seen the final exam answers; they score brilliantly and then fail on the job. Keeping the test set sealed, and never letting it influence any choice, is what makes the final score a trustworthy estimate of real-world performance.
Train / validation / test splits
| Split | Used for | Typical share |
|---|---|---|
| Training | Fitting parameters (weights, splits) | 60–80% |
| Validation | Choosing hyperparameters, thresholds, features, early stopping | 10–20% (or replaced by cross-validation) |
| Test | A single, final, unbiased estimate of generalization | 10–20% |
- Stratified split keeps class proportions identical across splits. Use it for classification, especially with imbalance:
train_test_split(..., stratify=y). - Time-based split for temporal data: train on the past, validate and test on the future. Random shuffling leaks the future into training.
- Group split when several rows belong to the same entity (patient, user, device): all rows of one group must fall in one split (
GroupKFold), or the model memorises the entity. - With huge datasets, a 98/1/1 split can be fine: one percent of ten million rows is still a very precise estimate.
Data leakage
Leakage means information that would not be available at prediction time sneaks into training, making offline scores unrealistically good. Interviewers expect you to name concrete patterns, not just the word.
Target leakage
- A feature that is a consequence or proxy of the label: “treatment_given” when predicting disease; “account_closed_date” when predicting churn.
- Features computed with future information, like a 30-day average that includes days after the prediction time.
- IDs or join keys that encode the label (a hospital ID that is almost a proxy for the diagnosis protocol).
- In-sample target encoding: replacing a category with the mean label computed on all rows, including the row you are predicting.
Train–test contamination
- Fitting a scaler, imputer, encoder, PCA or feature selector on the full dataset before splitting.
- Duplicate or near-duplicate rows present in both train and test (same user, same image crop, same leaked ID).
- Oversampling (SMOTE) before splitting, so synthetic copies of test points appear in training.
- Tuning hyperparameters or thresholds on the test set; peeking at test metrics while iterating.
- Random splits on grouped or temporal data: the same patient or a future week lands in both sides.
StandardScaler().fit_transform(X) on the whole dataset and then splitting. The scaler has learned the mean and standard deviation of the test rows, so a little test information has leaked into training. Always fit preprocessing on the training split only, then transform validation and test. Wrapping everything in a Pipeline makes this automatic, even inside cross-validation.Missing values
| Strategy | When it fits | Caveat |
|---|---|---|
| Drop rows | Very few rows missing, missing completely at random | Loses data; biases results if missingness is informative |
| Drop column | Column mostly empty and not important | May discard signal |
| Mean / median imputation | Numeric; median is robust to outliers | Shrinks variance, weakens correlations |
| Most-frequent or “Missing” category | Categorical features | “Missing” as its own category often carries signal |
| kNN / iterative (model-based) imputation | Features correlated with each other | Slower; must still be fitted on train only |
| Missing-indicator flag + impute | When “was missing” is itself predictive | Adds columns |
| Native handling | XGBoost, LightGBM, CatBoost and scikit-learn's HistGradientBoosting learn a default direction for missing values | Still understand why data is missing |
Encoding categorical features
| Encoding | How | Good for | Watch out |
|---|---|---|---|
| One-hot | One binary column per category | Nominal features with few levels; linear models, kNN, SVM | Explodes with high cardinality; drop one column for unregularized linear models (dummy-variable trap) |
| Ordinal / label | Map categories to integers | Truly ordered categories (small < medium < large); tree models | Implies a false order for nominal features in linear models |
| Target (mean) encoding | Replace category with mean target in that category | High-cardinality features (zip codes, product IDs) | Leaks the target; compute out-of-fold with smoothing |
| Frequency / count | Replace with how often the category appears | High cardinality, trees | Different categories can collide |
| Hashing | Hash category into a fixed number of buckets | Huge, open-ended vocabularies, streaming | Collisions; not invertible |
| Embeddings | Learned dense vector per category | Neural nets, recommender IDs | Needs enough data per category |
Feature scaling
Worked example: a tumour dataset has a “mean area” feature around 654 and a “mean fractal dimension” around 0.06. In a Euclidean distance, a difference of 100 in area swamps any difference in fractal dimension, so kNN would effectively ignore the second feature. If the training mean area is 654 with standard deviation 350, a tumour with area 1004 becomes z = (1004 − 654)/350 = 1.0, now on the same footing as every other standardized feature.
| Needs scaling | Does not need scaling |
|---|---|
| kNN, k-means, SVM (distance or margin based), PCA (variance based), linear/logistic regression with regularization or gradient descent, neural networks | Decision trees, random forests, gradient-boosted trees (they split on thresholds, so any monotonic rescaling gives the same splits); Naive Bayes (mostly) |
Feature engineering
Domain ratios and differences
debt / income, price per square foot, time since last purchase. Often worth more than any algorithm change.
Transformations
log(1 + x) for skewed counts or money; Box-Cox / Yeo-Johnson; binning ages into bands.
Interactions and polynomials
x1·x2, x² let linear models capture curvature (PolynomialFeatures).
Date and time
Hour, weekday, month, holiday flag; cyclical encoding with sin/cos so 23:00 is close to 00:00.
Aggregations
Per-user counts and means over past windows (transactions in last 24 h). Compute strictly from the past to avoid leakage.
Text
Bag-of-words, TF-IDF, n-grams, or embeddings. See NLP fundamentals.
Feature selection
| Family | Idea | Examples |
|---|---|---|
| Filter | Score each feature independently of any model | Variance threshold, correlation, chi-square, mutual information, ANOVA F-test |
| Wrapper | Search subsets by training models | Recursive Feature Elimination (RFE), forward/backward selection |
| Embedded | Selection happens during training | L1 (Lasso) zeroing coefficients, tree feature importances |
Why select features at all: fewer features means less overfitting, faster training and inference, cheaper data pipelines and easier explanations. It also fights the curse of dimensionality: as dimensions grow, data becomes sparse and distances between points become nearly equal, which hurts distance-based methods.
Putting preprocessing in a pipeline
import pandas as pd
from sklearn.compose import ColumnTransformer
from sklearn.pipeline import Pipeline
from sklearn.impute import SimpleImputer
from sklearn.preprocessing import StandardScaler, OneHotEncoder
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split
num_cols = ["age", "income", "balance"]
cat_cols = ["city", "plan"]
preprocess = ColumnTransformer([
("num", Pipeline([("impute", SimpleImputer(strategy="median")),
("scale", StandardScaler())]), num_cols),
("cat", Pipeline([("impute", SimpleImputer(strategy="most_frequent")),
("onehot", OneHotEncoder(handle_unknown="ignore"))]), cat_cols),
])
model = Pipeline([("prep", preprocess),
("clf", LogisticRegression(max_iter=1000, class_weight="balanced"))])
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, stratify=y, random_state=42)
model.fit(X_train, y_train) # imputer/scaler/encoder fitted on train only
print(model.score(X_test, y_test))
How models learn: loss functions and gradient descent
Almost every supervised model is trained the same way: define a loss that measures how wrong a prediction is, then adjust the parameters to make the average loss (the cost or objective) as small as possible. Gradient descent is the workhorse for that adjustment.
You stand on a foggy hillside and want to reach the valley floor. You cannot see far, but you can feel the slope under your feet, so you take a step in the steepest downhill direction, then feel again. The height is the loss, your position is the parameter values, the slope you feel is the gradient, and your stride length is the learning rate. Too short a stride and you take forever; too long and you overshoot the valley and bounce around. You might also settle in a small dip that is not the lowest valley, which is a local minimum.
Loss vs cost
Strictly, the loss is the error on one example and the cost is the average (or sum) over the dataset, often plus a regularization penalty. In practice, libraries and papers use “loss” for both; the formula with 1/n and a summation is the cost.
Common loss functions
| Loss | Task | Behaviour |
|---|---|---|
| MSE (L2) | Regression | Squares errors, so large errors dominate: missing by 50,000 costs 2.5 billion, missing by 10 costs 100. Sensitive to outliers. Predicts the conditional mean. |
| MAE (L1) | Regression | Every unit of error counts the same; robust to outliers. Predicts the conditional median. Not differentiable at 0. |
| Huber | Regression | Quadratic for small errors, linear for large: MSE's smoothness with MAE's robustness. |
| Quantile (pinball) | Regression | Asymmetric: penalises under-forecasting more than over-forecasting (or vice versa). Used for prediction intervals and inventory. |
| Cross-entropy (log loss) | Classification | Rewards confident correct probabilities, punishes confident wrong ones very hard. |
| Hinge | Classification (SVM) | Zero loss once a point is beyond the margin on the correct side; only borderline points matter. |
Worked example, cross-entropy. The true label is spam (y = 1). If the model says p = 0.9, the loss is −ln 0.9 ≈ 0.105. If it says p = 0.05 (confidently wrong), the loss is −ln 0.05 ≈ 3.0, almost thirty times larger. The negative sign exists because logs of probabilities are negative; negating turns “maximise the log-probability of the right answer” into a positive quantity to minimise. Minimising cross-entropy is exactly maximum-likelihood estimation.
Gradient descent
Worked example. Fit y = w·x to the points (1, 2), (2, 4), (3, 6) with MSE, starting from w = 0 and η = 0.1. The gradient is dJ/dw = −(2/n) Σ xi(yi − w xi).
- Step 1 Residuals are 2, 4, 6; Σ x·residual = 2 + 8 + 18 = 28; gradient = −(2/3)(28) ≈ −18.67; w = 0 + 0.1 × 18.67 = 1.867.
- Step 2 Residuals are 0.133, 0.267, 0.400; Σ x·residual ≈ 1.867; gradient ≈ −1.244; w ≈ 1.991.
- Step 3 onwards The gradient keeps shrinking and w converges to the true value 2. The loss surface here is convex, so there is a single global minimum.
| Variant | Data per update | Trade-off |
|---|---|---|
| Batch GD | Entire dataset | Stable, exact gradient; slow and memory-hungry on big data |
| Stochastic GD (SGD) | One example | Fast, noisy updates; noise can help escape shallow minima |
| Mini-batch GD | 32–1024 examples | The default: vectorises well on GPUs, reasonably smooth |
| Momentum | — | Accumulates a velocity so steps speed up along consistent directions |
| RMSProp / Adagrad | — | Per-parameter adaptive learning rates |
| Adam | — | Momentum + RMSProp; the common default for neural nets |
- Learning rate too small: painfully slow convergence. Too large: overshoots and diverges (loss oscillates or explodes). It does not adapt automatically in plain GD; the gradient gives direction, the learning rate gives step size.
- Epoch: one full pass over the training data. Batch size: examples per update; one epoch = n / batch_size updates.
- Convex vs non-convex: linear and logistic regression have convex costs (one global minimum). Neural networks are non-convex with many local minima and saddle points, but many of those minima generalise similarly well.
- When to stop: when validation loss stops improving (early stopping), not when training loss hits zero. Real data has noise, so driving training error to zero means memorising that noise.
Linear regression and regularization
Linear regression predicts a number as a weighted sum of the features plus an intercept. It is the simplest useful model, the best-understood statistically, and the building block of logistic regression and neural networks.
A pizzeria charges a base price plus a fixed amount per topping and per inch of diameter. If you only see a list of past pizzas and their prices, you can work backwards to find the base price and each per-unit charge. The base price is the intercept b, the per-topping and per-inch charges are the weights w, and working them out from receipts is fitting the regression. If the real menu has a discount for very large pizzas, a straight-line rule will systematically misprice them, which is exactly what underfitting a curved relationship looks like.
Interpreting coefficients
Each weight wj is the expected change in ŷ for a one-unit increase in xj, holding all other features fixed. Coefficients are only comparable across features if the features are on the same scale, and they become unstable when features are highly correlated (multicollinearity).
Assumptions (for valid inference, not just prediction)
- Linearity The expected target is a linear function of the features (after any transformations).
- Independence Errors are independent (violated in time series with autocorrelation).
- Homoscedasticity Errors have constant variance across predictions (no funnel shape in residual plots).
- Normality of errors Needed for exact confidence intervals and p-values, not for the point predictions.
- No perfect multicollinearity No feature is an exact linear combination of others; high correlation inflates coefficient variance (check the variance inflation factor, VIF).
Polynomial regression
Adding x², x³, … as extra features keeps the model linear in its parameters while fitting curves. Degree 1 on curved data underfits; degree 9 on 80 noisy points fits the noise and overfits. Regularization makes high-degree fits usable.
Regularization: L2 (Ridge), L1 (Lasso), Elastic Net
alpha in scikit-learn's Ridge/Lasso) sets penalty strength; in Elastic Net the mixing weight is l1_ratio. The intercept is not penalised. In LogisticRegression and SVC the knob is C = 1/λ, so smaller C means stronger regularization.Imagine packing for a flight with a fee on every kilogram. An L2 fee grows with the square of each item's weight, so you trim a little from everything but rarely leave anything behind. An L1 fee is a flat rate per kilogram, so it is often cheapest to leave some items at home entirely. The items are the features, their weights are the coefficients, and the fee is the penalty λ: L2 shrinks all coefficients smoothly, while L1 sets some to exactly zero and so performs feature selection.
| L2 / Ridge | L1 / Lasso | Elastic Net | |
|---|---|---|---|
| Effect on weights | Shrinks all toward zero, never exactly zero | Drives many exactly to zero (sparse) | Sparse but stable |
| Geometry | Circular constraint region | Diamond with corners on the axes, where solutions land | Rounded diamond |
| Correlated features | Spreads weight across them | Picks one arbitrarily | Keeps groups together |
| Differentiable | Yes; closed-form solution exists | Not at 0; solved by coordinate descent | Coordinate descent |
| Bayesian view | Gaussian prior on weights | Laplace prior on weights | Mixture |
| Use when | Many small effects, multicollinearity | You expect few relevant features, want selection | Many correlated features with sparsity |
Why it works: a penalty on weight size limits how wildly the function can bend to chase individual points, trading a little bias for a large reduction in variance. Large λ underfits (all weights near zero); λ = 0 recovers plain OLS. Tune λ by cross-validation (RidgeCV, LassoCV, GridSearchCV).
import numpy as np
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import PolynomialFeatures, StandardScaler
from sklearn.linear_model import LinearRegression, Ridge, Lasso
from sklearn.model_selection import train_test_split, cross_val_score
rng = np.random.default_rng(42)
X = np.sort(rng.random((80, 1)), axis=0)
y = np.sin(2 * np.pi * X).ravel() + rng.normal(0, 0.15, 80)
X_tr, X_te, y_tr, y_te = train_test_split(X, y, test_size=0.2, random_state=42)
for name, reg in [("OLS", LinearRegression()), ("Ridge", Ridge(alpha=1.0)), ("Lasso", Lasso(alpha=0.01))]:
pipe = Pipeline([("poly", PolynomialFeatures(degree=9)),
("scale", StandardScaler()),
("model", reg)])
pipe.fit(X_tr, y_tr)
cv = cross_val_score(pipe, X, y, cv=5, scoring="r2")
print(f"{name}: train R2={pipe.score(X_tr, y_tr):.3f} "
f"test R2={pipe.score(X_te, y_te):.3f} CV={cv.mean():.3f}+/-{cv.std():.3f}")
# A large train-test gap for OLS that shrinks with Ridge/Lasso is regularization at work.
| Pros | Cons |
|---|---|
| Fast, simple, interpretable coefficients; closed-form solution; works with few samples; good baseline; well-developed statistical inference | Only linear relationships unless you engineer features; sensitive to outliers (MSE) and multicollinearity; extrapolates poorly |
Logistic regression
Despite the name, logistic regression is a classifier. It computes a linear score, squashes it through the sigmoid into a probability between 0 and 1, and thresholds that probability to pick a class.
For movie-review sentiment, picture a balance scale. Words like “masterpiece” and “brilliant” add weight to the positive pan; “boring” and “waste” add weight to the negative pan. The model learns how heavy each word is, sums the weights of the words present, and the tilt of the scale is turned into a probability. Word weights are the coefficients, the net tilt is the logit z, and the sigmoid converts that tilt into the probability of “positive”.
Worked example. A model has b = −1.0 and a single weight w = 0.8 on “number of suspicious links”. An email with 2.75 links gives z = −1.0 + 0.8 × 2.75 = 1.2, so p = 1/(1 + e−1.2) = 1/(1 + 0.301) ≈ 0.77. With the default threshold 0.5 it is flagged as spam. Each extra link multiplies the odds of spam by e0.8 ≈ 2.23.
- Decision boundary is where z = 0 (p = 0.5): a straight line or hyperplane. Non-linear boundaries need engineered features or kernels.
- Multiclass: softmax (multinomial) regression generalises the sigmoid,
softmax(z)k = ezk / Σj ezj. Scores [2, 1, 0] become probabilities of about [0.67, 0.24, 0.09]. One-vs-rest is the alternative. - Regularization: scikit-learn applies L2 by default with C = 1.0; smaller C means stronger regularization. On a 50,000-word sentiment vocabulary, a very small C (0.01) worked best because it stopped the model trusting rare words.
- Perfect separation: without regularization, weights grow without bound when the classes are perfectly separable; regularization keeps them finite.
- Probabilities: logistic regression is usually reasonably well calibrated out of the box, which is one reason it is popular in credit scoring and medicine.
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split, GridSearchCV
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import classification_report, confusion_matrix
data = load_breast_cancer()
X, y = data.data, 1 - data.target # raw data: 0 = malignant; flip so malignant = 1 (positive class)
X_tr, X_te, y_tr, y_te = train_test_split(X, y, test_size=0.3, stratify=y, random_state=42)
pipe = make_pipeline(StandardScaler(),
LogisticRegression(class_weight="balanced", max_iter=10000, random_state=42))
grid = GridSearchCV(pipe, {"logisticregression__C": [0.01, 0.1, 1, 10, 100]},
scoring="recall", cv=5, n_jobs=-1)
grid.fit(X_tr, y_tr)
y_pred = grid.predict(X_te)
print(grid.best_params_)
print(confusion_matrix(y_te, y_pred))
print(classification_report(y_te, y_pred, target_names=["benign", "malignant"]))
recall_score defaults to pos_label=1. Without remapping (y = 1 - target) or setting pos_label=0, you would be optimising recall for benign tumours, the opposite of the clinical goal.| Pros | Cons |
|---|---|
| Fast; outputs probabilities; interpretable log-odds coefficients; strong baseline for text and tabular data; convex (no local minima); works well with sparse high-dimensional features | Linear boundary only; needs scaling for regularization and fast convergence; sensitive to correlated features and outliers; can underfit complex patterns |
k-nearest neighbours (kNN)
kNN has no training phase worth the name: it stores the training set, and to classify a new point it finds the k closest stored points and takes a majority vote (or averages their values for regression). It is the purest “similar inputs have similar outputs” model.
You move to a new street and want to know whether it is safe to leave your bike unlocked. You ask the five nearest neighbours and go with the majority. The neighbours are the stored training points, “nearest” is the distance metric, five is k, and the majority answer is the prediction. Ask only one neighbour and a single paranoid person decides for you (high variance); ask the whole city and you just get the city average (high bias).
Worked example. Training points (feature1, feature2, label): A (1, 1, red), B (2, 1, red), C (4, 4, blue), D (5, 4, blue), E (2, 3, blue). Query Q = (2, 2). Distances: A √2 ≈ 1.41, B 1.0, E 1.0, C √8 ≈ 2.83, D √13 ≈ 3.61. With k = 3 the neighbours are B (red), E (blue), A (red), so the vote is 2–1 for red. With weights="distance", votes are weighted by 1/d: red = 1/1 + 1/1.41 = 1.71, blue = 1/1 = 1.0, still red.
- Choosing k: small k gives a jagged, noisy boundary (overfits); large k gives a smooth boundary (underfits). Use an odd k for binary problems to avoid ties, and tune it with cross-validation.
- Scaling is mandatory: otherwise the feature with the largest range dominates the distance.
- Distance weighting helps when the minority class is sparse locally: a very close minority neighbour outvotes several distant majority ones.
- Cost: there is no parameter fitting; training just stores the data (O(n) space). Naive prediction is O(n·d) per query. KD-trees and ball trees speed up low dimensions; approximate nearest-neighbour indexes (HNSW, IVF, product quantization) are what vector databases use at scale.
- Curse of dimensionality: in hundreds of dimensions, all points are roughly equidistant and “nearest” loses meaning. Reduce dimensions first.
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.neighbors import KNeighborsClassifier
from sklearn.model_selection import GridSearchCV
knn = make_pipeline(StandardScaler(), KNeighborsClassifier())
grid = GridSearchCV(knn, {"kneighborsclassifier__n_neighbors": [3, 5, 7, 9, 11, 15],
"kneighborsclassifier__weights": ["uniform", "distance"]},
scoring="f1_macro", cv=5)
grid.fit(X_train, y_train)
print(grid.best_params_, grid.best_score_)
| Pros | Cons |
|---|---|
| Simple, no training, naturally multiclass, non-linear boundaries, easy to add new data | Slow and memory-heavy at prediction time; needs scaling; suffers badly in high dimensions; sensitive to irrelevant features and imbalance; no model to interpret |
Naive Bayes
Naive Bayes is a probabilistic classifier built on Bayes' theorem with one bold simplification: given the class, every feature is assumed independent of every other feature. The assumption is almost always false, yet the classifier is fast, needs little data, and works surprisingly well for text.
A detective adds or subtracts points for each clue separately: footprints found, plus five; red coat, plus three; solid alibi, minus eight. She never asks whether footprints and a red coat together mean something different. Each clue is a feature, the points are the log-likelihoods P(feature | class), and adding them up independently is the “naive” independence assumption. It misses clue interactions, but it is quick and often reaches the right suspect.
Worked example. Priors: P(spam) = 0.4, P(ham) = 0.6. Likelihoods: P(“free” | spam) = 0.5, P(“free” | ham) = 0.05, P(“meeting” | spam) = 0.1, P(“meeting” | ham) = 0.3. An email contains both words. Spam score = 0.4 × 0.5 × 0.1 = 0.020. Ham score = 0.6 × 0.05 × 0.3 = 0.009. Normalising: P(spam | email) = 0.020 / 0.029 ≈ 0.69, so it is classified as spam.
| Variant | Feature type | Typical use |
|---|---|---|
| GaussianNB | Continuous, assumed normal per class | Numeric tabular data |
| MultinomialNB | Counts (how many times a word appears) | Document classification with counts or TF-IDF |
| BernoulliNB | Binary present/absent; also uses absence as evidence | Short texts, one-hot bag-of-words |
| ComplementNB | Counts; estimates from the complement of each class | Imbalanced text classification |
| CategoricalNB | Categorical features | Survey or categorical tabular data |
- Zero-frequency problem: a word never seen with a class would give probability 0 and wipe out the whole product. Smoothing (α > 0) fixes it.
- Probabilities are poorly calibrated: because correlated features are double-counted, Naive Bayes outputs are pushed toward 0 and 1. The ranking is often fine; the numbers are overconfident.
- In a binary bag-of-words sentiment benchmark, BernoulliNB reached F1 ≈ 0.81 but recall of only about 0.76, lower than logistic regression, largely because negation (“not bad”) breaks the independence assumption.
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.naive_bayes import MultinomialNB
from sklearn.pipeline import make_pipeline
nb = make_pipeline(CountVectorizer(ngram_range=(1, 2), min_df=2), MultinomialNB(alpha=1.0))
nb.fit(train_texts, train_labels)
print(nb.predict(["free prize, claim now", "agenda for tomorrow's meeting"]))
| Pros | Cons |
|---|---|
| Extremely fast to train and predict; works with tiny data; handles very high dimensions; naturally multiclass; strong text baseline | Independence assumption ignores interactions; poorly calibrated probabilities; Gaussian variant assumes normality; cannot learn “not good”-style combinations |
Decision trees
A decision tree asks a sequence of yes/no questions about the features (“is income > 50k?”), each answer leading down a branch until a leaf gives the prediction. Trees are the foundation of the most successful tabular models: random forests and gradient boosting.
In the game of twenty questions, a good player asks the question that splits the remaining possibilities most evenly (“is it alive?”) rather than a narrow one (“is it a giraffe?”). A decision tree does the same: at each node it picks the feature and threshold that best separates the classes. Each question is a split, “best separates” is measured by Gini impurity or information gain, and the final guess is the leaf. A player allowed unlimited questions can memorise any single game, which is exactly how an unpruned tree overfits.
Impurity measures
Worked example. A parent node has 10 samples: 6 positive, 4 negative.
- Parent Gini = 1 − (0.6² + 0.4²) = 1 − 0.52 = 0.48. Entropy = −(0.6 log20.6 + 0.4 log20.4) ≈ 0.971 bits.
- Candidate split Left child: 5 positive, 1 negative (6 samples). Right child: 1 positive, 3 negative (4 samples).
- Gini of children Left = 1 − (25 + 1)/36 ≈ 0.278. Right = 1 − (1 + 9)/16 = 0.375. Weighted = 0.6 × 0.278 + 0.4 × 0.375 ≈ 0.317. Gini decrease = 0.48 − 0.317 ≈ 0.163.
- Entropy of children Left ≈ 0.650, right ≈ 0.811. Weighted = 0.6 × 0.650 + 0.4 × 0.811 ≈ 0.715. Information gain = 0.971 − 0.715 ≈ 0.256 bits.
- Choose The tree evaluates every feature and threshold this way and keeps the split with the largest impurity decrease, then recurses on each child.
Algorithms and stopping
- CART (scikit-learn): binary splits, Gini or entropy for classification, MSE for regression. ID3 / C4.5: entropy and gain ratio, multiway splits. The split search is greedy: locally best at each node, not globally optimal.
- Pre-pruning (early stopping):
max_depth,min_samples_split,min_samples_leaf,max_leaf_nodes,min_impurity_decrease. - Post-pruning: grow the full tree, then remove branches that do not pay for their complexity. Cost-complexity pruning minimises R(T) + α·|leaves| (
ccp_alpha). - Feature importance (mean decrease in impurity) sums the impurity reduction each feature produced, weighted by samples reaching the node. It is biased toward high-cardinality continuous features; permutation importance is more reliable.
An overfitting study on a small wine dataset shows the pattern: a depth-1 stump has low train and test accuracy (underfits), an unlimited-depth tree has 100% train accuracy with a clear drop on test (overfits), and a tuned depth sits between with the smallest gap.
from sklearn.tree import DecisionTreeClassifier, export_text
from sklearn.model_selection import GridSearchCV
params = {"max_depth": [3, 5, 7, 10, None],
"min_samples_split": [2, 5, 10],
"min_samples_leaf": [1, 2, 4]}
grid = GridSearchCV(DecisionTreeClassifier(class_weight="balanced", random_state=42),
params, scoring="recall", cv=5, n_jobs=-1)
grid.fit(X_train, y_train) # no scaling needed for trees
tree = grid.best_estimator_
print(export_text(tree, feature_names=list(feature_names)))
for depth in [1, None, tree.max_depth]:
t = DecisionTreeClassifier(max_depth=depth, random_state=42).fit(X_train, y_train)
print(depth, t.score(X_train, y_train), t.score(X_test, y_test)) # watch the gap
| Pros | Cons |
|---|---|
| Human-readable rules; no scaling needed; handles mixed feature types and non-linearity; captures interactions automatically; fast inference | High variance: small data changes produce a different tree; overfits without pruning; axis-aligned, step-shaped boundaries; greedy splits; cannot extrapolate beyond the training range in regression |
Ensembles: bagging, random forests, boosting and stacking
An ensemble combines many models so that their individual errors cancel out. Bagging trains strong, high-variance models independently and averages them to reduce variance. Boosting trains weak, high-bias models in sequence, each fixing the previous ones' mistakes, to reduce bias. Stacking learns how to combine different model types.
Bagging is a panel of two hundred judges, each reading a different random excerpt of a case and voting independently; individual judges are erratic, but the majority is reliable because their mistakes are not the same. Boosting is a relay of tutors: the first teaches the basics, the second focuses only on the questions the student still gets wrong, the third on what remains, and so on. The judges are bagged trees whose averaging cuts variance, the tutors are boosted weak learners whose sequence cuts bias, and stacking is a head judge who learns how much to trust each panel.
Why averaging works
Bagging (bootstrap aggregating)
- Bootstrap Draw B samples of size n from the training data with replacement.
- Train Fit one high-variance model (usually a deep tree) on each sample, in parallel.
- Aggregate Majority vote (classification) or average (regression).
Each bootstrap sample leaves out about (1 − 1/n)n ≈ 1/e ≈ 36.8% of rows. Those out-of-bag (OOB) rows act as a free validation set for that tree (oob_score=True).
Random forest
A random forest is bagged decision trees plus one extra trick: at every split, only a random subset of features is considered (max_features, typically √d for classification, d/3 or all for regression). This decorrelates the trees so that one dominant feature does not appear at the top of every tree.
| Hyperparameter | Effect |
|---|---|
n_estimators | More trees never overfit in the classic sense; the gain flattens, cost rises. 200–1000 is typical. |
max_features | Lower means more decorrelation and more randomness; the most important knob. |
max_depth, min_samples_leaf | Limit individual tree complexity; helps with noisy data and model size. |
class_weight="balanced_subsample" | Reweights classes per bootstrap for imbalance. |
Extra Trees (extremely randomized trees) go further and pick split thresholds at random, trading a little bias for more variance reduction and faster training.
Boosting
AdaBoost (adaptive boosting) trains weak learners, usually depth-1 stumps, sequentially and reweights the data so misclassified points get more attention.
Gradient boosting generalises the idea to any differentiable loss. Each new tree is fitted to the negative gradient of the loss with respect to the current predictions (for MSE, simply the residuals), and added with a small learning rate. It is gradient descent in function space.
Worked example. House prices 200, 300, 400 (thousands). F0 = 300 for everyone; residuals are −100, 0, +100. The first tree learns to predict those residuals (say −90, 0, +90). With η = 0.1, predictions become 291, 300, 309. The residuals shrink to −91, 0, 91, the next tree fits those, and so on; after hundreds of small steps the predictions approach the targets, with early stopping deciding when to quit.
XGBoost, LightGBM and CatBoost
| Library | Distinguishing ideas | Reach for it when |
|---|---|---|
| XGBoost | Second-order (gradient and Hessian) split gain; explicit L1/L2 penalties on leaf weights; level-wise growth; sparsity-aware missing-value handling; histogram mode; mature GPU support | A robust, well-documented default for tabular competitions and production |
| LightGBM | Histogram binning; leaf-wise growth (split the leaf with the largest gain); GOSS (keep large-gradient rows, sample small ones); exclusive feature bundling; very fast and memory-light | Large datasets, many features, need for speed; control overfitting with num_leaves and min_child_samples |
| CatBoost | Ordered target statistics for categorical features without leakage; ordered boosting to reduce prediction shift; symmetric (oblivious) trees for fast inference; strong defaults | Many high-cardinality categorical features; little time to tune |
| scikit-learn HistGradientBoosting | LightGBM-style histogram boosting, native missing values and categorical support | You want boosting without an extra dependency |
Key boosting hyperparameters: n_estimators with early stopping, learning_rate, tree size (max_depth or num_leaves), subsample and colsample_bytree (row and column sampling, which add bagging-style randomness), min_child_weight, and regularization (reg_lambda, reg_alpha).
Stacking and blending
Stacking trains several diverse base models (say logistic regression, random forest, gradient boosting, kNN), then trains a meta-model on their out-of-fold predictions. Using out-of-fold predictions is essential: if the meta-model saw base-model predictions on data those models were trained on, it would learn to trust overfit outputs. Blending is the simpler version that uses a single hold-out set. Voting just averages (soft voting) or majority-votes (hard voting) without learning weights.
from sklearn.ensemble import (RandomForestClassifier, GradientBoostingClassifier,
HistGradientBoostingClassifier, AdaBoostClassifier,
StackingClassifier)
from sklearn.linear_model import LogisticRegression
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.model_selection import cross_val_score
rf = RandomForestClassifier(n_estimators=500, max_features="sqrt", oob_score=True,
n_jobs=-1, random_state=42).fit(X_train, y_train)
print("OOB accuracy:", rf.oob_score_)
hgb = HistGradientBoostingClassifier(learning_rate=0.05, max_iter=1000,
early_stopping=True, validation_fraction=0.1,
random_state=42)
ada = AdaBoostClassifier(n_estimators=300, learning_rate=0.5, algorithm="SAMME",
random_state=42)
stack = StackingClassifier(
estimators=[("lr", make_pipeline(StandardScaler(), LogisticRegression(max_iter=5000))),
("rf", rf), ("hgb", hgb)],
final_estimator=LogisticRegression(), cv=5) # meta-model trained on out-of-fold predictions
for name, m in [("rf", rf), ("hgb", hgb), ("ada", ada), ("stack", stack)]:
print(name, cross_val_score(m, X_train, y_train, cv=5, scoring="roc_auc").mean())
# XGBoost equivalent (separate package). In XGBoost 2+, early_stopping_rounds
# belongs on fit(), not the constructor:
# from xgboost import XGBClassifier
# xgb = XGBClassifier(n_estimators=2000, learning_rate=0.05, max_depth=6,
# subsample=0.8, colsample_bytree=0.8, eval_metric="aucpr")
# xgb.fit(X_tr, y_tr, eval_set=[(X_va, y_va)], early_stopping_rounds=50, verbose=False)
Bagging / random forest
- Reduces variance; base learners are deep trees.
- Trees trained independently and in parallel.
- Hard to overfit by adding trees; few knobs; robust default.
- OOB estimate for free.
- Less accurate than tuned boosting on most tabular tasks.
Boosting
- Reduces bias (and some variance); base learners are shallow trees.
- Trees trained sequentially; each depends on the previous.
- Can overfit with too many rounds or a high learning rate; needs early stopping and tuning.
- Sensitive to label noise and outliers (AdaBoost especially).
- Usually state of the art for tabular data.
Support vector machines (SVM)
An SVM finds the separating hyperplane with the widest margin, the largest gap between the boundary and the nearest points of each class. Only those nearest points, the support vectors, determine the boundary. With kernels, SVMs draw non-linear boundaries without ever computing high-dimensional features explicitly.
Two villages sit on a plain, and you must build a straight road between them that stays as far from both as possible. The houses closest to the road on either side decide where it goes; houses deep inside the villages are irrelevant. The road's centre line is the decision boundary, its width is the margin, the closest houses are the support vectors, and allowing a few houses to sit on the road in exchange for a wider road overall is the soft margin controlled by C.
- C is the price of a margin violation. Large C: few violations, narrow margin, risk of overfitting. Small C: wider margin, more violations tolerated, more regularization.
- Worked example: if the learned w = (3, 4), then ‖w‖ = 5 and the margin width is 2/5 = 0.4 units. A solution with w = (0.6, 0.8) would have margin 2.0, five times wider.
The kernel trick
Data that is not linearly separable in its original space often becomes separable after mapping to a higher-dimensional space φ(x). The SVM's dual formulation only ever uses dot products between points, so we can replace xi·xj with a kernel K(xi, xj) = φ(xi)·φ(xj) and never compute φ at all.
Example: points on a line at x = −2, −1, 1, 2 labelled (+, −, −, +) cannot be split by one threshold. Map each point to (x, x²): the + points go to height 4, the − points to height 1, and the horizontal line x² = 2.5 separates them. A polynomial kernel does this implicitly.
- RBF γ: large γ means each point influences only a tiny neighbourhood, so the boundary wraps around individual points and overfits; small γ gives a smooth, nearly linear boundary. Tune C and γ together on a log grid.
- Scaling is essential because both the margin and the RBF distance depend on feature magnitudes.
- Probabilities: SVMs output distances, not probabilities.
SVC(probability=True)adds Platt scaling via internal cross-validation (slower); otherwise usedecision_functionscores for ranking and threshold tuning. - Scale limits: kernel SVM training is roughly O(n²) to O(n³), fine for tens of thousands of rows, painful beyond.
LinearSVCorSGDClassifier(loss="hinge")scale to millions and excel on sparse text: a linear SVM tied for best (F1 ≈ 0.875) on bag-of-words movie-review sentiment. - SVR (support vector regression) fits a tube of width ε around the data and only penalises points outside it.
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.svm import SVC
from sklearn.model_selection import GridSearchCV
svm = make_pipeline(StandardScaler(),
SVC(class_weight="balanced", probability=True, random_state=42))
grid = GridSearchCV(svm, {"svc__C": [0.1, 1, 10, 100],
"svc__kernel": ["linear", "rbf"],
"svc__gamma": ["scale", 0.01, 0.1, 1]},
scoring="recall", cv=5, n_jobs=-1)
grid.fit(X_train, y_train)
print(grid.best_params_)
print("support vectors per class:", grid.best_estimator_[-1].n_support_)
| Pros | Cons |
|---|---|
| Strong on high-dimensional and small-to-medium data; maximum-margin gives good generalization; kernels model non-linearity; only support vectors matter (robust to far-away points) | Slow on large n with kernels; needs scaling and careful C/γ tuning; no native probabilities; harder to interpret with non-linear kernels; sensitive to overlapping noisy classes |
Clustering: k-means, hierarchical, DBSCAN and GMM
Clustering groups similar items together without any labels. It is used for customer segmentation, grouping documents or support tickets, compressing colours in images, finding structure before labelling, and as a feature for downstream models. There is no single “right” clustering; each algorithm encodes a different idea of what a cluster is.
A class is told to arrange itself for a photo by height, but nobody is given anyone's height. People glance around, shuffle, and gradually sort themselves into short, medium and tall groups with no instructions. Each person is a data point, height is the feature, the self-organised groups are clusters, and the fact that nobody was told where to stand is what makes it unsupervised.
k-means
- Initialise Pick k centroids (k-means++ spreads initial centroids apart, which is the scikit-learn default).
- Assign Put each point in the cluster of its nearest centroid.
- Update Move each centroid to the mean of its assigned points.
- Repeat until assignments stop changing. Run several random starts (
n_init) and keep the best.
Worked example. One-dimensional points {1, 2, 3, 10, 11, 12}, k = 2, initial centroids 1 and 2.
- Assign 1 goes to centroid 1; 2, 3, 10, 11, 12 go to centroid 2.
- Update Centroids become 1 and (2 + 3 + 10 + 11 + 12)/5 = 7.6.
- Assign 1, 2, 3 are closer to 1; 10, 11, 12 are closer to 7.6.
- Update Centroids become 2 and 11. Assignments no longer change: converged, with inertia 1 + 0 + 1 + 1 + 0 + 1 = 4.
Choosing k and judging clusters
- Elbow method: plot inertia against k; pick the point where adding clusters stops helping much.
- Silhouette score: s = (b − a) / max(a, b), where a is the mean distance to points in your own cluster and b the mean distance to the nearest other cluster. Ranges from −1 to 1; higher is better.
- Davies–Bouldin (lower is better) and Calinski–Harabasz (higher is better) are other internal indices. If some true labels exist, use Adjusted Rand Index or normalised mutual information.
- Ultimately, business usefulness decides: can the marketing team act on these segments?
k-means limitations: assumes roughly spherical, similar-sized clusters; needs k in advance; sensitive to scale, outliers and initialisation; only works with means (Euclidean). k-medoids uses actual points as centres and any distance; mini-batch k-means scales to huge data.
Hierarchical (agglomerative) clustering
Start with every point as its own cluster and repeatedly merge the two closest clusters, producing a tree called a dendrogram. Cutting the dendrogram at a chosen height gives any number of clusters without rerunning. The linkage defines “closest”: single (nearest pair; finds chains, sensitive to noise), complete (farthest pair; compact clusters), average, and Ward (merge that least increases variance; the usual default). Cost is at least O(n²) memory, so it suits thousands of points, not millions.
DBSCAN
Density-based clustering: a point with at least min_samples neighbours within radius eps is a core point; clusters grow by connecting core points that are within eps of each other; points reachable from a core point but not core themselves are border points; everything else is noise. DBSCAN finds arbitrarily shaped clusters, does not need k, and labels outliers explicitly (label −1). It struggles when clusters have very different densities (HDBSCAN addresses this) and in high dimensions. Choose eps from the “knee” of a sorted k-distance plot.
Gaussian mixture models (GMM)
A GMM assumes the data comes from a mixture of k Gaussian distributions, each with its own mean, covariance and weight, and fits them with the Expectation–Maximization (EM) algorithm: the E-step computes each point's probability of belonging to each component (soft assignment), the M-step re-estimates means, covariances and weights from those probabilities. GMM gives soft memberships and elliptical clusters; k-means is the special case with hard assignments and equal spherical covariances. Choose k with BIC or AIC.
| Algorithm | Cluster shape | Needs k? | Outliers | Scale |
|---|---|---|---|---|
| k-means | Spherical, similar size | Yes | Pulled toward them | Very large (mini-batch) |
| Hierarchical | Depends on linkage | No (cut the tree) | Single linkage sensitive | Small to medium |
| DBSCAN / HDBSCAN | Arbitrary | No (eps, min_samples) | Explicit noise label | Medium to large |
| GMM | Elliptical, soft | Yes (BIC helps) | Low-likelihood points | Medium |
from sklearn.preprocessing import StandardScaler
from sklearn.cluster import KMeans, DBSCAN, AgglomerativeClustering
from sklearn.mixture import GaussianMixture
from sklearn.metrics import silhouette_score
Xs = StandardScaler().fit_transform(X)
for k in range(2, 9):
km = KMeans(n_clusters=k, n_init=10, random_state=42).fit(Xs)
print(k, round(km.inertia_, 1), round(silhouette_score(Xs, km.labels_), 3))
db_labels = DBSCAN(eps=0.5, min_samples=5).fit_predict(Xs) # -1 = noise
agg_labels = AgglomerativeClustering(n_clusters=4, linkage="ward").fit_predict(Xs)
gmm = GaussianMixture(n_components=4, covariance_type="full", random_state=42).fit(Xs)
soft = gmm.predict_proba(Xs) # probability of each point belonging to each component
print("BIC:", gmm.bic(Xs))
Dimensionality reduction: PCA, t-SNE and UMAP
Real data often has hundreds or thousands of features (a 224×224 colour image has about 150,000 pixel values), but most of the meaningful variation lives in far fewer directions. Dimensionality reduction compresses data to fight the curse of dimensionality, speed up models, remove noise and redundancy, and make data visible in 2D.
To capture a three-dimensional teapot in a single two-dimensional photo, you rotate it until the view shows the most detail: the spout and handle side by side, not the top of the lid. PCA does the same for data: it finds the viewing angles that show the most spread. The camera angles are the principal components, the amount of detail each view shows is the explained variance, and the flat photo is the projected low-dimensional data; some depth information is lost, but most of the shape survives.
Principal component analysis (PCA)
- Standardize each feature (PCA chases variance, so unscaled large-range features dominate).
- Covariance Compute the d×d covariance matrix Σ = (1/(n−1)) XTX of the centred data.
- Eigen-decompose Eigenvectors of Σ are the principal directions; eigenvalues are the variance along each. In practice libraries use the SVD of X, which is more stable.
- Keep top k Sort by eigenvalue and keep the first k components.
- Project Z = X Wk, where Wk holds the k eigenvectors as columns.
Worked example. Four standardized features give eigenvalues 4.2, 1.1, 0.5 and 0.2 (total 6.0). PC1 explains 4.2/6.0 = 70%; PC1 + PC2 explain 5.3/6.0 ≈ 88%. Keeping two components retains 88% of the variance with half the dimensions. A common rule is to keep enough components for 90–95% of the variance, or pick k by downstream cross-validated performance.
- PCA is linear and unsupervised: the directions of greatest variance are not necessarily the most predictive of the label. LDA (linear discriminant analysis) is the supervised alternative that maximises class separation.
- Components are combinations of all original features, so they are harder to interpret.
- Variants: Incremental PCA (streaming), Kernel PCA (non-linear), Truncated SVD (works directly on sparse TF-IDF matrices; called LSA in text).
t-SNE and UMAP
Both are non-linear methods built mainly for visualization. t-SNE converts high-dimensional distances into neighbour probabilities (Gaussian) and finds a 2D layout whose neighbour probabilities (heavy-tailed Student-t) match, minimising KL divergence. It preserves local neighbourhoods beautifully, but cluster sizes and between-cluster distances in the plot are not meaningful, results depend on perplexity and the random seed, and it is slow. UMAP rests on manifold and graph ideas, is much faster, scales to millions of points, preserves more global structure, and can transform new points.
| PCA | t-SNE | UMAP | |
|---|---|---|---|
| Type | Linear | Non-linear | Non-linear |
| Preserves | Global variance | Local neighbourhoods | Local plus more global structure |
| Deterministic | Yes | No (seed-dependent) | Mostly (seeded) |
| Transform new data | Yes | No (standard version) | Yes |
| Speed | Fast | Slow | Fast |
| Use as model features | Yes | No | Sometimes, with care |
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.decomposition import PCA
from sklearn.manifold import TSNE
from sklearn.linear_model import LogisticRegression
pca = make_pipeline(StandardScaler(), PCA(n_components=0.95)) # keep 95% of variance
Z = pca.fit_transform(X_train)
print(Z.shape, pca[-1].explained_variance_ratio_.cumsum())
clf = make_pipeline(StandardScaler(), PCA(n_components=10), LogisticRegression(max_iter=1000))
clf.fit(X_train, y_train) # PCA fitted inside the pipeline
emb_2d = TSNE(n_components=2, perplexity=30, random_state=42).fit_transform(X_sample)
Anomaly detection
Anomaly (outlier, novelty) detection finds rare observations that differ from the bulk of the data: fraudulent transactions, failing machines, network intrusions, sensor glitches, defective products. The defining difficulty is that anomalies are rare, varied, and often unlabelled, so the problem is usually framed as learning what “normal” looks like.
A building guard never studies a catalogue of burglars. Instead she learns the regular rhythm: who arrives when, which doors they use. Anyone who breaks the pattern, like someone entering a side door at 3 a.m., stands out. The regulars are the normal training data, her sense of rhythm is the learned density or boundary, and the 3 a.m. visitor is an anomaly with a high anomaly score. Like the guard, the model flags unusual, not necessarily bad, which is why flagged cases still need review.
| Approach | Idea | Examples |
|---|---|---|
| Statistical | Points far from the mean or outside a fitted distribution | z-score > 3, IQR rule (below Q1 − 1.5·IQR or above Q3 + 1.5·IQR), Mahalanobis distance, Gaussian density |
| Isolation-based | Anomalies are easy to isolate with random splits | Isolation Forest: short average path length means anomalous |
| Density / distance | Anomalies live in sparse regions | Local Outlier Factor (LOF), kNN distance, DBSCAN noise points |
| Boundary | Learn a boundary around normal data | One-Class SVM, Elliptic Envelope |
| Reconstruction | Normal data is reconstructed well; anomalies are not | PCA reconstruction error, autoencoders |
| Supervised | Enough labelled anomalies exist | Gradient boosting with class weights, treated as imbalanced classification |
| Time series | Deviation from forecast or seasonal pattern | Residual from seasonal decomposition, forecast error bands |
Worked example (z-score). Daily transaction amounts for a customer have mean 50 and standard deviation 12. A 110 transaction has z = (110 − 50)/12 = 5.0, far beyond the usual cutoff of 3, so it is flagged. Note that z-scores assume a roughly normal distribution; for skewed money data, a log transform or the IQR rule is safer.
from sklearn.ensemble import IsolationForest
from sklearn.neighbors import LocalOutlierFactor
iso = IsolationForest(n_estimators=300, contamination=0.01, random_state=42).fit(X_train)
scores = -iso.score_samples(X_new) # higher = more anomalous
flags = iso.predict(X_new) # -1 = anomaly, 1 = normal
lof = LocalOutlierFactor(n_neighbors=20, novelty=True).fit(X_train)
lof_flags = lof.predict(X_new)
contamination parameter just sets the threshold; choose it from review capacity.Recommender systems basics
Recommenders predict which items (movies, products, songs, posts) a user will value. They mix ideas from supervised learning, matrix factorization, nearest-neighbour search and ranking, and they are a favourite ML system-design interview topic.
If a friend has loved the same ten books as you, you will probably enjoy the eleventh book they loved. A good bookseller, alternatively, notices you like slow-burn mysteries and suggests similar titles. The friend with similar taste is collaborative filtering (users who agreed in the past will agree again); the bookseller matching book attributes to your profile is content-based filtering; and a store that uses both is a hybrid recommender.
Content-based
Recommend items similar to what the user liked, using item features (genre, text embeddings). Works for new items; can trap users in a filter bubble.
User-based collaborative filtering
Find users with similar rating histories (cosine or Pearson similarity) and recommend what they liked.
Item-based collaborative filtering
“People who bought this also bought”: similarity between items computed from co-interactions. More stable than user-based.
Matrix factorization
Factor the sparse user×item rating matrix into user and item latent vectors whose dot product predicts the rating.
Two-tower / deep models
Neural encoders embed users and items into one space; nearest-neighbour search retrieves candidates at scale.
Hybrid
Combine signals; almost all production systems are hybrids.
Worked example. With two latent factors (action, romance), a user vector p = (0.9, 0.1) and a movie vector q = (4.5, 0.5) give a predicted score of 0.9 × 4.5 + 0.1 × 0.5 = 4.1; the same user with a romance film q = (0.5, 4.8) scores 0.45 + 0.48 = 0.93. The action film is recommended.
Production architecture and pitfalls
- Candidate generation Retrieve hundreds of items from millions quickly (item-item similarity, two-tower embeddings with approximate nearest-neighbour search, popularity).
- Ranking Score candidates with a richer model (gradient boosting or a deep network using user, item and context features) predicting click, watch time or purchase.
- Re-ranking Apply business rules, diversity, freshness, fairness, and deduplication.
- Cold start: new users or items have no history. Use content features, popularity, onboarding questions, or exploration.
- Implicit vs explicit feedback: clicks and watch time are plentiful but noisy; star ratings are clean but rare. A missing interaction is not a negative.
- Metrics: offline precision@k, recall@k, NDCG, MAP, hit rate, coverage and diversity; online click-through rate, conversion, retention through A/B tests.
- Feedback loops and popularity bias: the model only learns from what it showed; add exploration (bandits) and debiasing.
Bias–variance trade-off, overfitting and underfitting
Every model's expected error on new data can be split into three parts: bias (error from wrong or too-simple assumptions), variance (error from being too sensitive to the particular training sample) and irreducible noise (randomness no model can remove). Making a model more flexible lowers bias but raises variance, so the goal is the complexity that minimises their sum.
Picture four darts players. One always hits the same spot, but it is off to the left of the bullseye: low variance, high bias. Another sprays darts all over the board, centred on the bullseye on average: low bias, high variance. The worst player is both off-centre and scattered; the champion is tightly grouped on the bullseye. Each throw is a model trained on a different sample of data, the bullseye is the true function, the distance of the group's centre from the bullseye is bias, and the spread of the group is variance.
Model complexity: very simple ---------- moderate ---------- very complex
Bias^2: HIGH falling low lower lowest
Variance: lowest low low-mid rising HIGH
Total error: high falling MINIMUM rising high
Diagnosis: underfit sweet spot overfit
Examples: linear fit on curves, tuned depth / 1-NN, unpruned tree,
k-NN with huge k tuned C or k degree-15 polynomial
| Underfitting (high bias) | Good fit | Overfitting (high variance) | |
|---|---|---|---|
| Training error | High | Low | Very low |
| Validation error | High, close to training | Low, close to training | Much higher than training |
| Example numbers | Train loss 0.45, val 0.47 | Train 0.10, val 0.12 | Train 0.05, val 0.30 |
| Typical cause | Model too simple, weak features, too much regularization, too few training iterations | — | Model too complex for the data size, noisy labels, too many features, training too long, leakage in training only |
| Fixes | More flexible model, better features, polynomial terms, less regularization, train longer, boosting | — | More data, regularization (L1/L2, dropout), simpler model, pruning, early stopping, feature selection, bagging, data augmentation, cross-validated tuning |
There are no universal numeric thresholds for “overfit”: judge by the gap between training and validation error and by comparison with a baseline and with the achievable error (human performance, or the noise level of the labels).
Learning curves and validation curves
Learning curve (error vs training-set size)
- High bias: both curves plateau high and close together; more data will not help, a better model will.
- High variance: a large gap that narrows as data grows; more data will help.
Validation curve (error vs one hyperparameter)
- Sweep tree depth, C, k or λ.
- Training error keeps falling with complexity; validation error falls then rises. Pick the minimum of validation error.
import numpy as np
from sklearn.model_selection import learning_curve, validation_curve
from sklearn.tree import DecisionTreeClassifier
sizes, train_sc, val_sc = learning_curve(DecisionTreeClassifier(max_depth=5, random_state=42),
X, y, cv=5, train_sizes=np.linspace(0.1, 1.0, 8),
scoring="accuracy")
print(train_sc.mean(axis=1), val_sc.mean(axis=1))
depths = [1, 2, 3, 5, 8, 12, None]
tr, va = validation_curve(DecisionTreeClassifier(random_state=42), X, y,
param_name="max_depth", param_range=depths, cv=5)
for d, a, b in zip(depths, tr.mean(1), va.mean(1)):
print(d, round(a, 3), round(b, 3))
Cross-validation and hyperparameter tuning
A single train/validation split gives a noisy estimate that depends on which rows happened to land where. Cross-validation (CV) reuses the data so that every row is validated once, giving both a more reliable mean score and a measure of its spread. Hyperparameter tuning then searches for the settings with the best cross-validated score.
A chef testing a new recipe on one table of guests might get a table that loves spicy food. Instead, she cooks five times, each time letting a different fifth of the guests judge while the rest help refine the recipe, then averages the verdicts. Each fifth of the guests is a fold, averaging the verdicts is the CV mean, and the disagreement between tables is the CV standard deviation, which says how much to trust the average.
k-fold cross-validation
Fold 1: [VAL ][train][train][train][train] -> score 1 Fold 2: [train][VAL ][train][train][train] -> score 2 Fold 3: [train][train][VAL ][train][train] -> score 3 Fold 4: [train][train][train][VAL ][train] -> score 4 Fold 5: [train][train][train][train][VAL ] -> score 5 Report: mean +/- std of the 5 scores (test set stays sealed outside all of this)
| Scheme | Use when | scikit-learn |
|---|---|---|
| k-fold (k = 5 or 10) | Default for i.i.d. regression data | KFold |
| Stratified k-fold | Classification, especially imbalanced; keeps class ratios per fold | StratifiedKFold (default for classifiers) |
| Group k-fold | Multiple rows per patient, user or device | GroupKFold, StratifiedGroupKFold |
| Time-series split | Temporal data: always train on the past, validate on the future (expanding or sliding window, optional gap) | TimeSeriesSplit |
| Leave-one-out | Very small datasets; high variance, expensive | LeaveOneOut |
| Repeated k-fold | Small data where one CV run is noisy | RepeatedStratifiedKFold |
| Nested CV | Unbiased estimate of a whole tune-and-train procedure: inner loop tunes, outer loop evaluates | GridSearchCV inside cross_val_score |
A high mean with a large standard deviation (recall 0.95 ± 0.08) is less trustworthy than a slightly lower but stable score (0.93 ± 0.02). Stability across folds is part of model selection.
Bootstrap versus cross-validation
Cross-validation retrains the model on each fold and estimates how well the training procedure generalises. The bootstrap resamples a fixed set of predictions (or the raw data) with replacement to put an error bar on a statistic. Use CV to choose models and hyperparameters; use a paired bootstrap (or McNemar) on a sealed test set to say whether model A is really better than model B. The two are not interchangeable: a bootstrap of training accuracy is optimistic because every resample still overlaps the original training rows. Out-of-bag scores from bagged trees are the useful exception, because each tree is scored only on rows it never saw.
Hyperparameter search strategies
| Method | How it works | Best for |
|---|---|---|
| Manual / validation curve | Sweep one knob at a time | Understanding sensitivity |
| Grid search | Try every combination in a grid | Two or three hyperparameters with few values; cost grows exponentially |
| Random search | Sample combinations from distributions | Many hyperparameters where only a few matter; usually beats grid search for the same budget |
| Bayesian optimisation | A surrogate model (Gaussian process, TPE) proposes promising settings from past results | Expensive models (Optuna, Hyperopt, scikit-optimize) |
| Successive halving / Hyperband | Start many configs cheaply, keep the best, give them more budget | Large searches, deep learning (HalvingGridSearchCV) |
| Early stopping | Stop boosting rounds or epochs when validation stops improving | Boosting and neural networks |
Search learning rates and regularization strengths on a log scale (0.001, 0.01, 0.1, 1, 10), because their effect is multiplicative.
from scipy.stats import loguniform, randint
from sklearn.model_selection import (StratifiedKFold, GridSearchCV, RandomizedSearchCV,
cross_val_score)
from sklearn.ensemble import RandomForestClassifier
cv = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)
search = RandomizedSearchCV(
RandomForestClassifier(random_state=42, n_jobs=-1),
{"n_estimators": randint(200, 1000), "max_depth": [None, 5, 10, 20],
"max_features": ["sqrt", "log2", 0.3], "min_samples_leaf": randint(1, 10)},
n_iter=40, scoring="average_precision", cv=cv, random_state=42, n_jobs=-1)
search.fit(X_train, y_train)
print(search.best_params_, search.best_score_)
# Nested CV: honest estimate of "tune + train" as a whole
inner = GridSearchCV(RandomForestClassifier(random_state=42),
{"max_depth": [5, 10, None]}, cv=3, scoring="roc_auc")
outer_scores = cross_val_score(inner, X_train, y_train, cv=5, scoring="roc_auc")
print(outer_scores.mean(), outer_scores.std())
final_score = search.best_estimator_.score(X_test, y_test) # touch the test set once, at the end
best_score_ from a grid search as the model's expected performance. After trying hundreds of settings, the best CV score is optimistically biased because you selected the luckiest one. Use nested CV or a truly untouched test set for the final number. Likewise, never choose the decision threshold on the test set.Classification metrics with worked examples
The metric is the definition of success, so choosing it is a product decision disguised as a technical one. Start from the confusion matrix, then pick the metric that reflects the real cost of each kind of mistake.
A smoke alarm that never rings is right almost every day, because fires are rare, yet it is useless. One that rings every time you make toast catches every fire but gets unplugged. Recall is “of all real fires, how many did the alarm catch?”, precision is “of all the times it rang, how many were real fires?”, and the sensitivity dial on the alarm is the decision threshold that trades one against the other. The “always right on quiet days” alarm is a high-accuracy, zero-recall model on imbalanced data.
The confusion matrix
PREDICTED
Positive Negative
ACTUAL Positive TP FN <- missed cases (Type II error)
Negative FP TN
^
false alarms (Type I error)
scikit-learn's confusion_matrix puts actual classes in rows and predicted classes in columns, ordered by label value. With labels {0, 1}, it prints [[TN, FP], [FN, TP]], so the false negatives sit at position [1][0].
Worked example: a cancer screening model
On 1,000 test patients the model gives TP = 80, FP = 20, FN = 10, TN = 890.
| Metric | Calculation | Value | Meaning |
|---|---|---|---|
| Accuracy | (80 + 890) / 1000 | 0.970 | 97% of all predictions correct |
| Precision | 80 / (80 + 20) | 0.800 | When it flags cancer, it is right 80% of the time |
| Recall | 80 / (80 + 10) | 0.889 | It catches 89% of real cancers; 10 are missed |
| Specificity | 890 / (890 + 20) | 0.978 | 98% of healthy patients correctly cleared |
| FPR | 20 / 910 | 0.022 | 2.2% of healthy patients get a false alarm |
| F1 | 2 × 0.8 × 0.889 / (0.8 + 0.889) | 0.842 | Balanced summary of precision and recall |
The accuracy paradox
A hospital dataset has 9,900 healthy and 100 diseased patients. A model that always predicts “healthy” scores 9,900 / 10,000 = 99% accuracy while catching zero diseased patients: recall = 0, and precision is undefined (no positive predictions). Accuracy is dominated by the huge true-negative count. For a life-threatening disease, a missed case (false negative) is far costlier than a false alarm (an extra test), so the team should prioritise recall, subject to a precision floor so clinics are not flooded, and not the balanced F1, which assumes both errors cost the same.
Which metric when
| Situation | Prioritise | Why |
|---|---|---|
| Disease screening, fraud detection, safety defects | Recall (with a precision floor), F2 | Missing a positive is dangerous or expensive |
| Spam filtering, content takedown, legal or punitive actions | Precision (with a recall floor), F0.5 | False alarms hurt users (a real email lost to the spam folder) |
| Both errors matter similarly, imbalanced data | F1, PR-AUC | Summarises both, ignores the huge TN count |
| Balanced classes, equal error costs | Accuracy is acceptable | Simple and interpretable |
| Ranking quality across thresholds | ROC-AUC (balanced), PR-AUC (imbalanced) | Threshold-independent |
| Probabilities drive decisions (pricing, risk) | Log loss, Brier score, calibration | You need trustworthy probabilities, not just labels |
| Multiclass where every class matters equally | Macro-F1 | Each class counts the same regardless of size |
Thresholds and the precision–recall trade-off
Most classifiers output a score or probability; the label comes from comparing it with a threshold (0.5 by default). Raising the threshold makes the model more cautious: precision rises, recall falls. Lowering it catches more positives at the cost of more false alarms. The threshold is a business decision and should be tuned on validation data.
ROC curve and ROC-AUC
The ROC curve plots true positive rate (recall) against false positive rate as the threshold sweeps from 1 down to 0. AUC (area under the curve) summarises it: 1.0 is perfect, 0.5 is random guessing, below 0.5 means the scores are inverted. AUC has a clean interpretation: the probability that a randomly chosen positive gets a higher score than a randomly chosen negative.
Worked example. Positives score 0.9, 0.8, 0.4; negatives score 0.7, 0.3. Of the 3 × 2 = 6 positive–negative pairs, the positive wins in 5 (only 0.4 vs 0.7 loses), so AUC = 5/6 ≈ 0.83.
Precision–recall curve and PR-AUC
The PR curve plots precision against recall across thresholds; its area is summarised by average precision (AP). A random classifier's PR-AUC equals the positive rate (for example 0.01 for 1% fraud), not 0.5. With heavy imbalance, ROC-AUC can look excellent because the FPR denominator (all negatives) is enormous, while PR-AUC exposes how many false alarms accompany each catch.
ROC-AUC
- Uses TPR and FPR; insensitive to class balance.
- Good for balanced problems and comparing rankers.
- Can be over-optimistic when positives are rare.
PR-AUC (average precision)
- Uses precision and recall; ignores true negatives.
- Preferred for rare-positive problems (fraud, disease, retrieval).
- Baseline equals prevalence, so compare against it.
Log loss and the Brier score
Example: three predictions p = 0.9 (y = 1), 0.2 (y = 0), 0.6 (y = 0). Log loss = −(ln 0.9 + ln 0.8 + ln 0.4)/3 = (0.105 + 0.223 + 0.916)/3 ≈ 0.415. Brier = (0.01 + 0.04 + 0.36)/3 ≈ 0.137. The confident-ish wrong prediction (0.6 for a negative) contributes most of both.
Calibration
A model is calibrated if, among all cases it scores 0.8, about 80% are actually positive. Calibration is about trustworthy confidence; accuracy is about correctness; AUC is about ranking. A model can rank perfectly (AUC 1.0) and still be badly calibrated.
- Typical behaviour: logistic regression is usually well calibrated; Naive Bayes is overconfident; SVM scores are not probabilities; random forests are under-confident near 0 and 1; boosted trees and deep networks are often overconfident; class reweighting and resampling distort probabilities.
- Diagnose with a reliability diagram (
calibration_curve) and expected calibration error (ECE). - Fix with Platt scaling (a sigmoid fitted on validation scores), isotonic regression (non-parametric, needs more data), or temperature scaling for neural nets. Calibration rarely changes ranking or accuracy, but it makes probabilities usable for thresholds, expected-value decisions and pricing.
Multiclass averaging
| Average | How | Use |
|---|---|---|
| Macro | Compute the metric per class, take the plain mean | All classes equally important (rare classes count fully) |
| Weighted | Per-class metric weighted by class support | Reflect class frequency, but hides poor rare-class performance |
| Micro | Pool all TP, FP, FN across classes, then compute | Overall per-instance performance; for single-label multiclass, micro-F1 equals accuracy |
Example: three classes with per-class F1 of 0.95 (500 samples), 0.90 (400) and 0.40 (100). Macro-F1 = (0.95 + 0.90 + 0.40)/3 = 0.75; weighted-F1 = (0.95 × 500 + 0.90 × 400 + 0.40 × 100)/1000 = 0.875. The weighted number hides the failing rare class. Other multiclass metrics: Cohen's kappa (agreement beyond chance), Matthews correlation coefficient (MCC, robust on imbalance), top-k accuracy.
import numpy as np
from sklearn.metrics import (confusion_matrix, classification_report, precision_score,
recall_score, f1_score, roc_auc_score, average_precision_score,
log_loss, brier_score_loss, precision_recall_curve,
matthews_corrcoef)
from sklearn.calibration import calibration_curve, CalibratedClassifierCV
proba = model.predict_proba(X_val)[:, 1]
y_hat = (proba >= 0.5).astype(int)
tn, fp, fn, tp = confusion_matrix(y_val, y_hat).ravel()
print(classification_report(y_val, y_hat, digits=3))
print("ROC-AUC", roc_auc_score(y_val, proba), "PR-AUC", average_precision_score(y_val, proba))
print("log loss", log_loss(y_val, proba), "Brier", brier_score_loss(y_val, proba))
print("MCC", matthews_corrcoef(y_val, y_hat))
# pick the highest threshold that still gives recall >= 0.95 (on VALIDATION data)
prec, rec, thr = precision_recall_curve(y_val, proba)
ok = np.where(rec[:-1] >= 0.95)[0]
t_star = thr[ok[-1]]
print("threshold", t_star, "precision", prec[ok[-1]], "recall", rec[ok[-1]])
frac_pos, mean_pred = calibration_curve(y_val, proba, n_bins=10) # reliability diagram data
calibrated = CalibratedClassifierCV(base_model, method="isotonic", cv=5).fit(X_train, y_train)
Regression metrics with a worked example
Regression metrics measure how far predictions are from actual numbers. The choice again depends on the cost of errors: are big misses disproportionately bad? Are relative errors what matters? Do you need a scale-free number to compare across problems?
An archery coach can score by the average distance of arrows from the centre, or by the average squared distance, which makes one wild arrow cost far more than several near misses. Average distance is MAE, average squared distance is MSE (and its square root RMSE brings it back to centimetres), and comparing the archer with someone who always aims at the average spot of all arrows is R².
Worked example. Actual values 3, 5, 2, 8; predictions 2.5, 5, 4, 7.
| i | y | ŷ | Error | |Error| | Error² | (y − ȳ)², ȳ = 4.5 |
|---|---|---|---|---|---|---|
| 1 | 3 | 2.5 | 0.5 | 0.5 | 0.25 | 2.25 |
| 2 | 5 | 5 | 0 | 0 | 0 | 0.25 |
| 3 | 2 | 4 | −2 | 2 | 4 | 6.25 |
| 4 | 8 | 7 | 1 | 1 | 1 | 12.25 |
| Sum | 3.5 | 5.25 | 21.0 |
MAE = 3.5/4 = 0.875. MSE = 5.25/4 = 1.3125. RMSE = √1.3125 ≈ 1.146. R² = 1 − 5.25/21 = 0.75, so the model explains 75% of the variance around the mean. Notice RMSE > MAE because the single error of 2 is amplified by squaring; RMSE is always ≥ MAE, and a big gap between them signals a few large errors.
| Metric | Units | Strength | Weakness |
|---|---|---|---|
| MAE | Target units | Robust to outliers, easy to explain | Treats all errors equally |
| MSE / RMSE | Squared / target units | Penalises large errors; smooth for optimisation | Outliers dominate |
| R² | Unitless | Compares with the mean baseline; scale-free | Always rises with more features in-sample (use adjusted R²); not an error size |
| MAPE | Percent | Business-friendly relative error | Explodes near zero; asymmetric (sMAPE, WAPE are alternatives) |
| RMSLE | Log units | Relative errors on skewed positive targets | For the same absolute miss, under-prediction is penalised more than over-prediction (log(100)−log(50) > log(150)−log(100)) |
| Quantile / pinball loss | Target units | Evaluates prediction intervals and asymmetric costs | Needs a chosen quantile |
from sklearn.metrics import (mean_absolute_error, mean_squared_error, r2_score,
mean_absolute_percentage_error)
import numpy as np
y_true = np.array([3, 5, 2, 8]); y_pred = np.array([2.5, 5, 4, 7])
print(mean_absolute_error(y_true, y_pred)) # 0.875
print(mean_squared_error(y_true, y_pred)) # 1.3125
print(np.sqrt(mean_squared_error(y_true, y_pred))) # 1.146
print(r2_score(y_true, y_pred)) # 0.75
print(mean_absolute_percentage_error(y_true, y_pred))
Handling class imbalance
Imbalance is the norm for the most valuable problems: fraud (0.1%), rare disease (1%), churn (5%), click-through (2%), defects. The model sees so few positives that simply predicting “negative” minimises most losses. Imbalance is handled at four levels: the metric, the data, the algorithm, and the decision threshold.
If a sniffer dog practises on 999 clean bags for every bag with contraband, it learns that “nothing here” is almost always the right call. Trainers therefore show it more contraband bags (oversampling), fewer clean ones (undersampling), give a big treat for finding contraband (class weights), and decide how sure the dog must be before sitting down (the threshold). Each trick maps to an imbalance technique; none of them changes how rare contraband is at a real airport, which is why the evaluation must still use realistic data.
| Technique | How | Pros | Cons |
|---|---|---|---|
| Right metric | Recall, precision, F1/Fβ, PR-AUC, MCC instead of accuracy | Essential first step, free | Does not change the model by itself |
| Stratified splitting | Keep class ratio in every split and fold | Stable, honest estimates | — |
| Class weights | Multiply the loss of minority examples (class_weight="balanced", scale_pos_weight in XGBoost) | No data duplication; works in most libraries | Distorts probabilities (recalibrate); can increase variance |
| Random oversampling | Duplicate minority examples | Simple | Exact duplicates encourage overfitting |
| SMOTE and variants | Synthesise minority points by interpolating between neighbours | Smoother decision regions than duplication | Can create unrealistic points in overlapping regions; poor with categorical and high-dimensional data |
| Random undersampling | Drop majority examples | Faster training; works with huge data | Throws away information |
| Cleaning methods | Tomek links, Edited Nearest Neighbours remove borderline majority points | Sharper boundary | Can remove useful data |
| Ensemble resampling | Balanced random forest, EasyEnsemble, RUSBoost | Uses all majority data across members | More complexity |
| Threshold moving | Pick the threshold on validation data to hit a recall or cost target | Often the single most effective step; no retraining | Needs good probability ranking |
| Anomaly-detection framing | Model the normal class only | Works with almost no positives | Flags “unusual”, not necessarily “positive” |
| More positive data | Active learning, targeted labelling, data augmentation | Fixes the root cause | Costs time and money |
Worked example. With 9,900 negatives and 100 positives, balanced weights are 10,000 / (2 × 9,900) ≈ 0.505 for negatives and 10,000 / (2 × 100) = 50 for positives, so each positive counts about 99 times as much as each negative in the loss. For SMOTE, a minority point (2, 3) with neighbour (4, 7) and λ = 0.25 creates (2 + 0.5, 3 + 1) = (2.5, 4.0).
from imblearn.pipeline import Pipeline as ImbPipeline # imbalanced-learn package
from imblearn.over_sampling import SMOTE
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import StratifiedKFold, cross_val_score
pipe = ImbPipeline([("scale", StandardScaler()),
("smote", SMOTE(k_neighbors=5, random_state=42)), # applied to training folds only
("clf", LogisticRegression(max_iter=5000))])
cv = StratifiedKFold(5, shuffle=True, random_state=42)
print(cross_val_score(pipe, X_train, y_train, cv=cv, scoring="average_precision").mean())
# Often simpler and as good: class weights + threshold tuning
weighted = LogisticRegression(class_weight="balanced", max_iter=5000)
Interpretability and explainability
Stakeholders, regulators and debugging all need to know why a model predicts what it does. Global explanations describe the model overall (which features matter most); local explanations justify a single prediction (why was this loan rejected). Models are either intrinsically interpretable (linear models, small trees) or explained post hoc with model-agnostic tools.
Friends share a meal where some dishes were shared and some were individual. To split the bill fairly, you ask how much the total would change if each person had not come, averaged over every possible order in which people could have arrived. The friends are the features, the bill is the prediction minus the average prediction, and each person's fair share is their SHAP (Shapley) value. The shares always add up exactly to the bill, which is SHAP's additivity property.
| Method | Scope | How it works | Caveats |
|---|---|---|---|
| Coefficients | Global | Weights of a linear or logistic model on standardized features | Correlated features split or flip weights; not causal |
| Tree rules | Global and local | Read the path from root to leaf | Only for small trees |
| Impurity importance (MDI) | Global | Total impurity decrease per feature in trees | Biased toward high-cardinality features; computed on training data |
| Permutation importance | Global | Shuffle one feature on held-out data; measure the score drop | Underestimates correlated features (the other copy compensates); needs repeats |
| Partial dependence (PDP) and ICE | Global / per-instance | Average (PDP) or per-row (ICE) prediction as one feature varies | Misleading with strongly correlated features; ALE plots fix this |
| SHAP | Local, aggregates to global | Shapley values from cooperative game theory: each feature's average marginal contribution across feature coalitions | Exact computation is exponential; TreeSHAP is fast for trees, KernelSHAP is slow; attribution is not causation |
| LIME | Local | Perturb the instance, fit a simple weighted linear model around it | Unstable across runs; depends on the perturbation scheme and kernel width |
| Counterfactuals | Local | Smallest change that flips the decision (“income +5k would approve”) | May suggest infeasible changes |
| Surrogate model | Global | Train a small tree to mimic the black box | Only as faithful as its fidelity score |
from sklearn.inspection import permutation_importance, PartialDependenceDisplay
r = permutation_importance(model, X_val, y_val, n_repeats=10, scoring="roc_auc", random_state=42)
for i in r.importances_mean.argsort()[::-1][:10]:
print(feature_names[i], round(r.importances_mean[i], 4), "+/-", round(r.importances_std[i], 4))
PartialDependenceDisplay.from_estimator(model, X_val, features=["age", "income"])
import shap # separate package
explainer = shap.TreeExplainer(tree_model)
shap_values = explainer.shap_values(X_val)
shap.summary_plot(shap_values, X_val) # global view built from local explanations
MLOps basics: deployment, monitoring and drift
A model in a notebook creates no value. MLOps is the set of practices that makes models reproducible, deployable, observable and maintainable: essentially DevOps plus data and model versioning, plus the fact that ML systems silently decay as the world changes.
A forecaster who learned the weather in one coastal city is excellent there. Move her inland and her rules of thumb slowly stop working, even though she has not changed at all. The forecaster is the deployed model, the move inland is drift (the input distribution or its relationship to the outcome changed), comparing her forecasts with actual weather each day is monitoring, and sending her on a refresher course with local data is retraining.
Deployment patterns
| Pattern | How | Use when |
|---|---|---|
| Batch scoring | Scheduled job scores a table (nightly churn scores) | Predictions not needed instantly; cheapest |
| Online / real-time API | Model behind a REST or gRPC endpoint, latency budget in milliseconds | Fraud checks at payment time, recommendations |
| Streaming | Score events from a queue (Kafka) as they arrive | Sensor or clickstream anomaly detection |
| Edge / on-device | Model shipped to phones or devices, often quantized | Privacy, offline use, low latency |
Safe rollout
- Shadow mode: the new model scores live traffic silently alongside the old one; compare without user impact.
- Canary release: send a small percentage of traffic to the new model, watch metrics, then ramp up.
- A/B test: randomised split with a pre-registered online metric and enough sample size for statistical significance.
- Rollback: keep the previous model version deployable in one step.
Kinds of drift
| Drift | What changed | Example | Detect with |
|---|---|---|---|
| Data (covariate) drift | P(X), the input distribution | A new marketing campaign brings younger customers | PSI, KS test, KL/JS divergence, chi-square on categories, feature summaries |
| Concept drift | P(y | X), the relationship itself | Fraudsters change tactics; the same features now mean something else | Performance on fresh labels, error rates over time |
| Label (prior) drift | P(y), the base rate | Disease prevalence rises in flu season | Predicted and actual positive rates |
| Prediction drift | Distribution of model outputs | Scores creep upward | Score histograms, mean prediction |
| Training–serving skew | Features computed differently offline and online | A timezone bug in the online feature pipeline | Log online features and compare with offline recomputation |
Worked example. A feature has training bin fractions (0.5, 0.3, 0.2) and live fractions (0.3, 0.3, 0.4). PSI = (0.3 − 0.5) ln(0.3/0.5) + 0 + (0.4 − 0.2) ln(0.4/0.2) = (−0.2)(−0.511) + (0.2)(0.693) ≈ 0.102 + 0.139 = 0.241, a moderate shift close to the alarm level.
What to monitor
- System health Latency, throughput, error rates, memory, cost.
- Data quality Schema changes, missing-value rates, out-of-range values, new categories.
- Drift Feature and prediction distributions against the training reference.
- Model performance Metrics on delayed ground-truth labels, broken down by segment.
- Business KPIs The outcome the model is meant to move, plus fairness metrics across groups.
The supporting toolkit
Versioning
Code (git), data (snapshots, DVC), models and parameters (a model registry such as MLflow) so any prediction can be reproduced.
Experiment tracking
Log hyperparameters, metrics, data versions and artifacts for every run.
Feature store
A shared catalogue of feature definitions, with an offline store for training (historical, point-in-time joins so you never use tomorrow's aggregates to predict today) and an online store for low-latency serving of the latest values. The interview point is training–serving consistency: one computation path, versioned features, and no ad-hoc SQL that drifts from the notebook. Common at companies with many models sharing user or item features; skip it for a single batch job.
CI/CD/CT
Automated tests for data and code, automated deployment, and continuous training pipelines triggered by schedule or drift.
Serialization
Save the whole pipeline (preprocessing plus model) with joblib, ONNX or a framework format, so serving applies identical transforms.
Governance
Model cards, audit logs, approval steps, privacy controls and bias reviews, especially in regulated domains.
import joblib
joblib.dump(pipeline, "churn_model_v3.joblib") # preprocessing + model together
model = joblib.load("churn_model_v3.joblib")
proba = model.predict_proba(new_rows)[:, 1]
import numpy as np
def psi(expected, actual, bins=10):
edges = np.quantile(expected, np.linspace(0, 1, bins + 1))
edges[0], edges[-1] = -np.inf, np.inf
e = np.histogram(expected, edges)[0] / len(expected) + 1e-6
a = np.histogram(actual, edges)[0] / len(actual) + 1e-6
return float(np.sum((a - e) * np.log(a / e)))
Reinforcement learning essentials
Reinforcement learning (RL) is learning by trial and error: an agent takes actions in an environment, receives rewards, and learns a policy that maximises total future reward. There are no labelled correct actions, only delayed feedback. RL powers game-playing agents, robotics, some recommendation and ad-bidding systems, and the RLHF stage that aligns large language models.
You never show a dog a labelled dataset of correct behaviour. It tries things; when it sits on command it gets a treat, and over time it learns which actions in which situations lead to treats. The dog is the agent, the living room and your commands are the environment and state, sitting or rolling over are actions, treats are rewards, and the dog's learned habit of what to do when is its policy. A dog that only ever repeats the first trick that earned a treat never discovers better ones, which is the exploration–exploitation dilemma.
action a_t
+---------+ -----------> +-------------+
| Agent | | Environment |
| (policy)| <----------- | |
+---------+ state s_t+1, +-------------+
reward r_t+1
State (s)
What the agent observes about the environment now.
Action (a)
A choice the agent can make; discrete (left/right) or continuous (steering angle).
Reward (r)
A scalar signal after each step; the only definition of the goal.
Policy π(a | s)
The agent's strategy: which action to take (or the probability of each) in each state.
Return G
Sum of future rewards, discounted by γ so near rewards count more.
Value V(s), Q(s, a)
Expected return from a state, or from taking action a in state s and then following the policy.
Worked example. Q(s, right) = 0, the agent moves right, receives r = 1, and the best Q-value in the next state is 2. With α = 0.5 and γ = 0.9: Q(s, right) ← 0 + 0.5 × (1 + 0.9 × 2 − 0) = 0.5 × 2.8 = 1.4. Repeating such updates over many episodes propagates reward information backwards through the states.
| Concept | Meaning |
|---|---|
| Exploration vs exploitation | Try new actions to learn, or use the best known action to earn. ε-greedy: act randomly with probability ε, otherwise greedily; decay ε over time. |
| Model-free vs model-based | Learn values or policies directly from experience (Q-learning, PPO) vs learn a model of the environment and plan with it (AlphaZero-style search). |
| Value-based | Learn Q, act greedily: Q-learning, SARSA (on-policy variant), DQN (deep Q-network with experience replay and a target network). |
| Policy-based | Optimise the policy directly by gradient ascent on expected return: REINFORCE, policy gradients. |
| Actor–critic | An actor (policy) plus a critic (value function) that reduces gradient variance: A2C, PPO, SAC. |
| On-policy vs off-policy | Learn from data generated by the current policy (SARSA, PPO) vs from any data, including old or other policies (Q-learning, DQN). |
| Multi-armed bandit | RL with a single state: choose among options to maximise reward (A/B testing that adapts; UCB, Thompson sampling). |
| Reward hacking | The agent exploits flaws in the reward definition instead of achieving the intended goal. |
Why RL matters for LLMs: RLHF
- Supervised fine-tuning A pretrained LLM is fine-tuned on human-written demonstrations.
- Reward model Humans rank several model answers to the same prompt; a model is trained to predict those preferences, giving a scalar reward.
- RL optimisation The LLM (the policy; its actions are tokens; the state is the prompt plus text so far) is optimised with PPO to maximise the reward model's score, with a KL penalty that keeps it close to the original model so it does not drift into reward hacking.
- Alternatives Direct Preference Optimization (DPO) skips the explicit reward model and RL loop by optimising directly on preference pairs.
import numpy as np
n_states, n_actions = 16, 4
Q = np.zeros((n_states, n_actions))
alpha, gamma, eps = 0.1, 0.99, 1.0
for episode in range(5000):
s = env.reset()
done = False
while not done:
a = np.random.randint(n_actions) if np.random.rand() < eps else int(Q[s].argmax())
s_next, r, done = env.step(a)
target = r + (0 if done else gamma * Q[s_next].max())
Q[s, a] += alpha * (target - Q[s, a]) # temporal-difference update
s = s_next
eps = max(0.05, eps * 0.999) # explore less over time
Choosing an algorithm: a practical cheat sheet
No algorithm wins everywhere (the “no free lunch” theorem). In practice the choice is driven by data type and size, the need for interpretability, latency and training budget, and whether you need probabilities.
You would not use a lorry for a trip to the corner shop or a bicycle to move house. A bicycle (linear model) is cheap, transparent and often enough; a van (random forest) carries most loads reliably with little fuss; a racing car (tuned gradient boosting or a deep network) is fastest on the right track but needs expert tuning and maintenance. The trip is your problem and data, and the right vehicle is the simplest one that gets you there within budget.
| Algorithm | Best for | Scaling needed | Interpretability | Main weakness |
|---|---|---|---|---|
| Linear / logistic regression | Baselines, sparse text, regulated domains, small data | Yes | High | Linear boundaries only |
| Naive Bayes | Text, tiny data, speed | No | Medium | Independence assumption; poor probabilities |
| kNN | Small, low-dimensional data; similarity search | Yes | Medium (show neighbours) | Slow inference; high dimensions |
| Decision tree | Explainable rules, mixed data | No | High (if shallow) | High variance |
| Random forest | Robust tabular default with little tuning | No | Medium | Large models; weaker than tuned boosting |
| Gradient boosting (XGBoost / LightGBM / CatBoost) | Best accuracy on tabular data | No | Medium (with SHAP) | Tuning; sensitive to noise; sequential training |
| SVM | High-dimensional, small-to-medium data; text (linear) | Yes | Low with kernels | Scales poorly with n (kernel) |
| Neural networks | Images, audio, text, very large data | Yes | Low | Data- and compute-hungry; tuning |
| k-means / DBSCAN / GMM | Segmentation, structure discovery | Yes | Medium | Evaluation is subjective |
| PCA | Compression, denoising, visualization | Yes | Low-medium | Linear; ignores the label |
- Start with a dummy baseline and a simple model Logistic or linear regression gives a reference score and reveals data problems quickly.
- Try a strong default Random forest or gradient boosting for tabular; fine-tuned pretrained networks for images and text.
- Iterate on data and features before algorithms Better labels and features usually beat exotic models.
- Tune with cross-validation, then compare on stability as well as mean score
- Prefer the simpler model when scores are close Easier to explain, cheaper to serve, more robust to drift.
Quick revision
- ML learns rules from data plus answers; traditional programming applies hand-written rules to data.
- AI contains ML, which contains deep learning, which contains generative AI.
- Supervised learning uses labels (classification or regression); unsupervised finds structure; self-supervised makes labels from raw data; RL learns from rewards.
- Parameters are learned during training; hyperparameters are set before training and tuned on validation data.
- Train fits parameters, validation chooses hyperparameters and thresholds, test is used once for the final estimate.
- Use stratified splits for classification, time-based splits for temporal data and group splits for repeated entities.
- Leakage is information unavailable at prediction time reaching training; fit every preprocessing step on training data only, inside a Pipeline.
- Do not fill missing values with 0 unless zero is meaningful; use median, most-frequent, model-based imputation or a missing flag.
- One-hot for nominal low-cardinality features, ordinal for ordered ones, out-of-fold target encoding for high cardinality.
- kNN, k-means, SVM, PCA, regularized linear models and neural nets need scaling; tree models do not.
- Loss is per example, cost is the average (plus penalties); MSE for regression, cross-entropy for classification.
- Cross-entropy punishes confident wrong predictions hardest: −ln 0.05 ≈ 3.0 versus −ln 0.9 ≈ 0.1.
- Gradient descent updates θ ← θ − η∇J; too small a learning rate is slow, too large diverges.
- Linear and logistic regression have convex costs with one global minimum; neural networks do not.
- L2 (Ridge) shrinks all weights; L1 (Lasso) zeroes some (feature selection); Elastic Net mixes both.
- In scikit-learn, C = 1/λ: smaller C means stronger regularization.
- Logistic regression models log-odds linearly; ew is the odds ratio per unit of a feature.
- kNN: small k overfits, large k underfits; suffers from the curse of dimensionality.
- Naive Bayes assumes conditional independence; use Laplace smoothing to avoid zero probabilities.
- Gini = 1 − Σp²; entropy = −Σp log2p; trees choose the split with the largest impurity decrease.
- Unpruned trees overfit; control with max_depth, min_samples_leaf or cost-complexity pruning.
- Bagging reduces variance with parallel deep trees; boosting reduces bias with sequential shallow trees.
- Random forests decorrelate trees by sampling features at each split; about 36.8% of rows are out-of-bag per tree.
- Gradient boosting fits each new tree to the negative gradient (residuals for MSE) with a small learning rate.
- LightGBM grows leaf-wise with histograms; XGBoost uses second-order gains and regularized leaves; CatBoost handles categoricals with ordered statistics.
- Stacking trains a meta-model on out-of-fold predictions of diverse base models.
- SVM maximises the margin 2/‖w‖; C trades margin width for violations; the kernel trick replaces dot products.
- RBF γ large means wiggly, overfit boundaries; small means smooth ones.
- k-means minimises within-cluster squared distance, needs k, assumes spherical clusters and finds only a local optimum.
- DBSCAN finds arbitrary shapes and labels noise; GMM gives soft, elliptical clusters via EM.
- PCA projects onto orthogonal directions of maximum variance; standardize first; keep 90–95% variance or tune k.
- t-SNE and UMAP are for visualization; t-SNE distances between clusters are not meaningful.
- Expected error = bias² + variance + irreducible noise.
- Underfitting: both errors high. Overfitting: low train error, much higher validation error.
- k-fold CV gives a mean and a spread; use nested CV for an unbiased estimate of tuning plus training.
- CV estimates a training procedure's performance; the bootstrap puts error bars on a statistic of a frozen model. Do not substitute a training-set bootstrap for held-out CV.
- Leakage patterns: target proxies, future-looking windows, in-sample target encoding, IDs, fit-on-all preprocessing, duplicates, SMOTE-before-split, random splits of grouped or temporal data.
- Random search usually beats grid search for the same budget; search rates and penalties on a log scale.
- Precision = TP/(TP+FP); recall = TP/(TP+FN); F1 is their harmonic mean.
- With TP 80, FP 20, FN 10, TN 890: precision 0.80, recall 0.89, F1 0.84, accuracy 0.97.
- A 99%-accurate model on a 99:1 dataset can have zero recall; prefer recall, precision, F1 or PR-AUC.
- ROC-AUC is the probability a random positive outranks a random negative; PR-AUC is better when positives are rare.
- Cost-optimal threshold for calibrated probabilities is CFP/(CFP + CFN); tune thresholds on validation data only.
- Calibration means predicted probabilities match observed frequencies; fix with Platt scaling or isotonic regression.
- Macro averaging treats classes equally; weighted averaging can hide a failing rare class.
- RMSE ≥ MAE; a large gap means a few big errors. R² can be negative on test data.
- Imbalance toolkit: right metric, stratification, class weights, resampling inside CV (SMOTE), threshold moving.
- SHAP values add up to prediction minus base value; importance is not causation.
- Drift types: data (P(X)), concept (P(y|X)), label (P(y)); PSI above 0.25 signals significant shift.
- A feature store is one definition of each feature, served offline (point-in-time training joins) and online (low-latency inference) to prevent training–serving skew.
- Ship the full preprocessing-plus-model pipeline as one artifact to avoid training–serving skew.
- Q-learning: Q ← Q + α[r + γ max Q′ − Q]; RLHF uses a reward model and PPO with a KL penalty.
Glossary
- Accuracy
- Fraction of all predictions that are correct; misleading on imbalanced data.
- AdaBoost
- Boosting method that reweights misclassified samples and combines weak learners with weights based on their error.
- Anomaly detection
- Finding rare observations that deviate from the normal pattern, often without labels.
- AUC (ROC-AUC)
- Area under the ROC curve; the probability a random positive is scored above a random negative.
- Average precision (PR-AUC)
- Summary of the precision–recall curve; its random baseline equals the positive rate.
- Bagging
- Training models on bootstrap samples and averaging them to reduce variance.
- Bias (statistical)
- Error from overly simple or wrong assumptions; systematic deviation from the truth.
- Bias–variance trade-off
- Increasing flexibility lowers bias but raises variance; the best model balances both.
- Bootstrap
- Resampling with replacement to estimate the sampling distribution of a statistic; not a substitute for cross-validation of a training procedure.
- Brier score
- Mean squared error of predicted probabilities versus 0/1 labels; a proper scoring rule, lower is better.
- Calibration
- Agreement between predicted probabilities and observed frequencies.
- CatBoost
- Gradient-boosting library with ordered target statistics for categorical features and symmetric trees.
- Collaborative filtering
- Recommending based on patterns of similar users or items in interaction data.
- Concept drift
- Change in the relationship between inputs and the target over time.
- Confusion matrix
- Table of actual versus predicted classes showing TP, FP, FN and TN counts.
- Cost function
- The aggregated loss over the dataset, often plus a regularization penalty, that training minimises.
- Cross-entropy (log loss)
- Loss for probabilistic classifiers that heavily penalises confident wrong predictions.
- Cross-validation
- Repeatedly splitting data into training and validation folds to estimate performance reliably.
- Curse of dimensionality
- As dimensions grow, data becomes sparse and distances lose meaning.
- Data drift
- Change in the input feature distribution between training and production.
- Data leakage
- Information unavailable at prediction time that contaminates training and inflates scores.
- DBSCAN
- Density-based clustering that finds arbitrary shapes and labels sparse points as noise.
- Decision tree
- Model that predicts by a sequence of threshold questions on features.
- Early stopping
- Halting training when validation performance stops improving.
- Elastic Net
- Regularization combining L1 and L2 penalties.
- Entropy
- Measure of impurity or uncertainty: −Σ p log2 p.
- Expectation–Maximization (EM)
- Iterative algorithm alternating soft assignment and parameter re-estimation, used to fit GMMs.
- F1 score
- Harmonic mean of precision and recall.
- Feature engineering
- Creating or transforming input variables to expose signal to the model.
- Feature store
- Shared, versioned feature definitions with offline (training) and online (serving) paths, using point-in-time joins to avoid leakage and skew.
- Gaussian mixture model (GMM)
- Probabilistic model of data as a weighted mix of Gaussian components, giving soft cluster memberships.
- Gini impurity
- 1 − Σ p²; the chance of mislabelling a random sample by labelling randomly from the node's class mix.
- Gradient boosting
- Sequentially adding trees fitted to the negative gradient of the loss.
- Gradient descent
- Optimisation that repeatedly steps parameters opposite the gradient of the cost.
- Grid search
- Exhaustive evaluation of every combination of listed hyperparameter values.
- Hinge loss
- max(0, 1 − y·f(x)); the SVM loss that ignores points beyond the margin.
- Hyperparameter
- Setting chosen before training, such as learning rate, depth, k or C.
- Information gain
- Reduction in entropy achieved by a split.
- Isolation Forest
- Anomaly detector that flags points isolated by few random splits.
- k-means
- Clustering that alternates assigning points to the nearest centroid and recomputing centroids.
- Kernel trick
- Computing dot products in a high-dimensional feature space via a kernel function without explicit mapping.
- kNN
- k-nearest neighbours: predicts from the labels of the k closest training points.
- L1 regularization (Lasso)
- Penalty on the sum of absolute weights; produces sparse models.
- L2 regularization (Ridge)
- Penalty on the sum of squared weights; shrinks all weights smoothly.
- Learning rate
- Step size of each gradient update, or shrinkage of each boosting tree.
- LightGBM
- Fast gradient-boosting library using histograms and leaf-wise tree growth.
- LIME
- Local explanation method fitting a simple surrogate model around one prediction.
- Logistic regression
- Linear classifier that maps a weighted sum through a sigmoid to a probability.
- MAE
- Mean absolute error; average absolute difference between prediction and truth.
- Matrix factorization
- Decomposing a user×item matrix into latent user and item vectors.
- MSE / RMSE
- Mean squared error and its square root; penalise large errors heavily.
- Naive Bayes
- Probabilistic classifier assuming features are independent given the class.
- Nested cross-validation
- Inner CV tunes hyperparameters, outer CV estimates performance of the whole procedure.
- One-hot encoding
- Representing a category as a binary vector with a single 1.
- Out-of-bag (OOB)
- Rows not drawn in a tree's bootstrap sample, usable as free validation data.
- Overfitting
- Learning noise in the training data, leading to poor performance on new data.
- PCA
- Principal component analysis: linear projection onto orthogonal directions of maximum variance.
- Permutation importance
- Drop in score when a feature's values are shuffled on held-out data.
- Pipeline
- Chained preprocessing and model steps fitted together to prevent leakage and skew.
- Policy
- In RL, the mapping from states to actions or action probabilities.
- Precision
- TP / (TP + FP): how often a positive prediction is correct.
- PSI
- Population Stability Index; measures distribution shift between a reference and live data.
- Q-learning
- Off-policy RL algorithm that learns action values via temporal-difference updates.
- R-squared (R²)
- Coefficient of determination: fraction of target variance explained relative to predicting the mean.
- Random forest
- Bagged decision trees with random feature subsets at each split.
- Recall
- TP / (TP + FN): fraction of actual positives that are caught; also sensitivity or TPR.
- Reinforcement learning
- Learning a policy by trial and error to maximise cumulative reward.
- RLHF
- Reinforcement learning from human feedback: optimising a model against a reward model trained on human preferences.
- ROC curve
- Plot of true positive rate against false positive rate across thresholds.
- SHAP
- Shapley-value explanations assigning each feature an additive contribution to a prediction.
- Silhouette score
- Cluster quality measure from −1 to 1 comparing own-cluster and nearest-other-cluster distances.
- SMOTE
- Synthetic Minority Over-sampling Technique; creates minority points by interpolating between neighbours.
- Specificity
- TN / (TN + FP): fraction of actual negatives correctly identified.
- Stacking
- Ensemble in which a meta-model learns to combine base models' out-of-fold predictions.
- Support vectors
- Training points on or inside the SVM margin that define the boundary.
- SVM
- Support vector machine: maximum-margin classifier, extendable with kernels.
- t-SNE
- Non-linear visualization method preserving local neighbourhoods in 2D or 3D.
- Training–serving skew
- Mismatch between features computed in training and in production.
- UMAP
- Fast non-linear dimensionality reduction preserving local and some global structure.
- Underfitting
- Model too simple to capture the pattern; high error on training and validation data.
- Variance (model)
- Sensitivity of the learned model to the particular training sample.
- XGBoost
- Regularized gradient-boosting library using second-order gradients and sparsity-aware splits.
Interview questions
Fundamentals
What is machine learning, and how does it differ from traditional programming?
Machine learning builds a model that learns a mapping from inputs to outputs using examples, instead of a programmer writing the rules. Traditional programming takes rules plus data and returns answers; ML takes data plus answers and returns the rules (the model), which then produce answers for new data. Formally, a program learns if its performance P on task T improves with experience E. Example: a spam filter learns which word patterns indicate spam from emails already labelled by users.
How do AI, machine learning, deep learning and generative AI relate to each other?
They are nested. AI is the broad goal of intelligent behaviour, including hand-coded search and rule systems. ML is the subset that learns from data. Deep learning is the subset of ML using many-layer neural networks that learn their own features from raw inputs. Generative AI is the part of deep learning (mostly) that models data distributions well enough to create new text, images or audio, such as LLMs and diffusion models.
What are the main types of machine learning?
- Supervised: labelled input–output pairs; classification and regression.
- Unsupervised: no labels; clustering, dimensionality reduction, anomaly detection.
- Semi-supervised: a few labels plus many unlabelled examples.
- Self-supervised: labels generated from the data itself (next-word prediction, masked tokens); the basis of LLM pretraining.
- Reinforcement learning: an agent learns from rewards through interaction.
What is the difference between classification and regression?
Classification predicts a discrete category (spam or not, one of three cultivars); regression predicts a continuous number (price, temperature). They differ in output layer, loss (cross-entropy vs MSE/MAE) and metrics (precision, recall, AUC vs MAE, RMSE, R²). Some problems can be framed either way, such as predicting a rating as a number or as one of five classes.
What is the difference between parameters and hyperparameters? Give examples.
Parameters are learned from data during training: weights and biases in linear models and neural networks, split thresholds in trees, centroids in k-means. Hyperparameters are chosen before training and control how learning happens: learning rate, number of epochs, batch size, tree depth, number of trees, k in kNN, regularization strength C or λ, dropout rate. You do not choose parameters; you tune hyperparameters, using validation data or cross-validation. Hyperparameters are not derived from features.
Why do we split data into training, validation and test sets?
The training set fits parameters. The validation set is used to make choices: hyperparameters, features, thresholds, early stopping. The test set is held back and used once to estimate how the final model performs on unseen data. If you make choices using the test set, it stops being unseen and the reported score becomes optimistic. Typical splits are 70/15/15 or 80/10/10, or train/test plus cross-validation on the training portion.
What is a stratified split and when should you use it?
A stratified split keeps the class proportions the same in every subset. With 37% malignant tumours overall, both train and test will contain about 37% malignant cases. Use it for classification, especially with imbalanced or small datasets, so that a split does not accidentally contain very few positives. In scikit-learn: train_test_split(X, y, stratify=y) and StratifiedKFold.
What are overfitting and underfitting, and how do you tell which one you have?
Underfitting: the model is too simple, so both training and validation error are high and close together (train loss 0.45, val 0.47). Overfitting: the model memorises noise, so training error is very low but validation error is much higher (train 0.05, val 0.30). A good fit has both low and close (train 0.10, val 0.12). There is no universal percentage threshold; you judge by the gap and trend between training and validation error, and against a baseline.
What is a loss function, and how is it different from a cost function?
A loss function measures how wrong the prediction is for a single example; it gives the signal used to update weights. The cost function aggregates the loss over the dataset (usually the mean) and may include regularization terms; it is what training minimises. In practice, libraries and slides often call the averaged version “the loss” too, so a formula with 1/n and a sum is technically the cost.
Which loss function would you use for regression and which for classification?
Regression: MSE by default (penalises large errors), MAE when outliers should not dominate, Huber as a compromise, quantile loss for asymmetric costs or prediction intervals. Binary classification: binary cross-entropy on sigmoid outputs. Multiclass: categorical cross-entropy on softmax outputs, with the true class one-hot encoded (or as an integer index). SVMs use hinge loss. The final choice also depends on which mistakes cost the business more.
Why does MSE penalise large errors so heavily?
Because errors are squared. An error of 10 contributes 100; an error of 50,000 contributes 2.5 billion. A single big miss can therefore dominate the average, which is desirable when large errors are disproportionately costly, and undesirable when the data has outliers (then use MAE or Huber).
What is cross-entropy, and why does it have a negative sign?
Cross-entropy measures how well predicted probabilities match the true classes: loss = −log(probability assigned to the true class). Correct and confident gives low loss (−ln 0.9 ≈ 0.1); wrong and confident gives very high loss (−ln 0.05 ≈ 3.0). Log probabilities are negative because probabilities are between 0 and 1, so the minus sign makes the loss positive, turning “maximise the likelihood of the correct class” into a minimisation problem for gradient descent.
What is gradient descent?
An iterative optimisation algorithm that minimises the cost by repeatedly moving parameters a small step opposite to the gradient: θ ← θ − η∇J(θ). The gradient gives the direction of steepest increase; the learning rate η sets the step size. Variants differ in how much data they use per step (batch, stochastic, mini-batch) and how they adapt the step (momentum, RMSProp, Adam). Libraries such as scikit-learn and PyTorch apply it automatically during training.
What are an epoch, a batch and an iteration?
An epoch is one full pass over the training data. A batch (mini-batch) is the subset of examples processed before one weight update. An iteration is one such update. With 10,000 examples and batch size 100, one epoch is 100 iterations. Epochs, batch size and learning rate are all hyperparameters.
Should you keep training until the training MSE reaches zero?
No. Real data contains noise, so zero training error means the model has memorised that noise and will likely generalise worse. Stop when validation loss stops improving (early stopping), for example: val loss 0.12 at epoch 5, 0.10 at epoch 10 (best), 0.15 at epoch 15, so keep the epoch-10 model.
What is feature scaling, and which algorithms need it?
Scaling puts features on comparable ranges: standardization (z = (x − μ)/σ), min-max to [0, 1], or robust scaling with median and IQR. Distance- and margin-based methods (kNN, k-means, SVM), variance-based PCA, regularized linear and logistic regression, and neural networks need it; otherwise a feature measured in hundreds (tumour area) dominates one measured in hundredths (fractal dimension). Tree-based models are scale-invariant because they split on thresholds.
How do you handle missing values?
First understand why they are missing. Options: drop rows or columns if few or useless; impute numeric features with the median (robust) or mean; impute categoricals with the most frequent value or a “Missing” category; use kNN or iterative imputation when features are correlated; add a missing-indicator flag when missingness itself carries signal; or let XGBoost, LightGBM or HistGradientBoosting handle NaN natively. Fit imputers on training data only. Do not blindly replace with 0, because the model will treat 0 as a real value unless zero is genuinely meaningful.
What is one-hot encoding, and when would you use something else?
One-hot encoding turns a categorical feature into binary columns, one per category, so that no false order is implied. It suits nominal features with few categories and linear, kNN or SVM models. For truly ordered categories use ordinal encoding; for high-cardinality features (zip codes, product IDs) use out-of-fold target encoding, frequency encoding, hashing or learned embeddings; tree models can often use ordinal codes directly, and CatBoost handles categories natively.
What is a confusion matrix?
A table comparing actual classes (rows) with predicted classes (columns). For binary problems it holds TP (positive predicted positive), FN (positive predicted negative, a missed case), FP (negative predicted positive, a false alarm) and TN. It shows not only how many predictions were right but what kind of mistakes were made. Example: of 100 emails with 40 spam, catching 35 spam (TP), missing 5 (FN) and flagging 10 normal emails (FP) leaves 50 TN.
Define accuracy, precision, recall and F1.
- Accuracy = (TP + TN)/total: overall correctness; fine for balanced classes.
- Precision = TP/(TP + FP): when the model says positive, how often it is right; matters when false alarms are costly (spam, fraud blocks).
- Recall = TP/(TP + FN): of actual positives, how many were caught; matters when misses are dangerous (disease screening).
- F1 = 2PR/(P + R): harmonic mean; a single number when both errors matter, especially on imbalanced data. Precision 0.9 and recall 0.1 give F1 of only 0.18.
A cancer model has TP = 80, FP = 20, FN = 10, TN = 890. What is its precision, and what does it mean?
Precision = TP/(TP + FP) = 80/100 = 0.80: of the 100 patients flagged as having cancer, 80 truly do and 20 were false alarms. Do not confuse it with recall, TP/(TP + FN) = 80/90 ≈ 0.89, which says 89% of actual cancers were caught, or with accuracy, (80 + 890)/1000 = 0.97. F1 is about 0.84.
What is linear regression?
A model that predicts a number as a weighted sum of features plus an intercept, ŷ = wTx + b, fitted by minimising squared error (ordinary least squares), either in closed form via the normal equation or by gradient descent. Each coefficient is the expected change in the target per unit change in that feature, holding others fixed. It is fast and interpretable but only captures linear relationships unless you engineer features.
What is logistic regression, and why is it called regression if it classifies?
Logistic regression computes a linear score z = wTx + b and passes it through the sigmoid 1/(1 + e−z) to get a probability, then applies a threshold (0.5 by default). It is called regression because it linearly regresses the log-odds, log(p/(1 − p)) = wTx + b. It is trained with cross-entropy and outputs reasonably calibrated probabilities.
What does the sigmoid function do, and what does softmax do?
The sigmoid squashes any real number into (0, 1), turning a binary logit into a probability. Softmax generalises this to many classes: it exponentiates each score and divides by the sum, so outputs are positive and sum to 1. Scores [2, 1, 0] become about [0.67, 0.24, 0.09]. Softmax is calculated from the model's outputs, not predefined.
How does k-nearest neighbours work?
kNN stores the training set. To predict, it computes the distance from the query to every stored point, takes the k closest, and returns the majority class (classification) or the mean value (regression), optionally weighting closer neighbours more. It needs scaled features, a sensible distance metric and a tuned k: small k is noisy (overfits), large k is overly smooth (underfits).
How does a decision tree decide where to split?
At each node it tries every feature and candidate threshold and picks the split that most reduces impurity: Gini impurity (1 − Σp²) or entropy (information gain) for classification, variance (MSE) for regression. It then recurses on each child until a stopping rule (max depth, min samples per leaf, pure nodes) is met. The search is greedy: best at each step, not globally optimal.
What is a random forest?
An ensemble of decision trees, each trained on a bootstrap sample of the rows and considering only a random subset of features at every split. Predictions are averaged (regression) or voted (classification). The randomness decorrelates the trees, so averaging cancels their individual errors and sharply reduces variance compared with a single tree. It works well with little tuning, gives an out-of-bag score for free, and provides feature importances.
What is a support vector machine?
A classifier that finds the hyperplane separating the classes with the widest possible margin. Only the points nearest the boundary (support vectors) determine it. A soft margin, controlled by C, allows some violations for noisy data, and the kernel trick lets SVMs draw non-linear boundaries by implicitly mapping data to higher-dimensional spaces.
What is clustering? Name a few algorithms.
Clustering groups unlabelled data points so that points in the same group are more similar to each other than to points in other groups. Algorithms: k-means (centroid-based, needs k), hierarchical/agglomerative (builds a dendrogram), DBSCAN (density-based, finds arbitrary shapes and noise), Gaussian mixture models (soft probabilistic clusters). Uses include customer segmentation, grouping documents and image compression.
What is PCA used for?
Principal component analysis reduces dimensionality by projecting data onto a few orthogonal directions that capture the most variance. It is used to compress features, remove redundancy and noise, speed up downstream models, fight the curse of dimensionality and visualise data in 2D. It is unsupervised and linear, and features should be standardized first.
What is regularization, and why is it needed?
Regularization adds a penalty on model complexity to the training objective so the model prefers simpler solutions and generalises better. For linear models, L2 (Ridge) penalises squared weights and shrinks them; L1 (Lasso) penalises absolute weights and zeroes some out; Elastic Net mixes both. In neural networks, dropout, weight decay, early stopping and data augmentation serve the same purpose. Its strength (λ or C) is a hyperparameter applied on top of the loss when the model tends to overfit.
What is cross-validation?
A technique that splits the training data into k folds, trains on k − 1 folds and validates on the remaining one, rotating until each fold has been the validation set once. The mean score is a more reliable estimate than one split, and the standard deviation shows stability. It is used to compare models and tune hyperparameters without touching the test set. It is not the same as the bootstrap: CV retrains and estimates the procedure's expected score; a bootstrap of a frozen model's test predictions estimates the uncertainty of that score.
Which models can output predicted probabilities?
Logistic regression (sigmoid or softmax), Naive Bayes, neural networks with sigmoid or softmax outputs, decision trees and random forests (class proportions in leaves, averaged across trees), gradient boosting (via the logistic link), kNN (fraction of neighbours) and GMMs. SVMs output margins; probability=True adds Platt scaling. Many of these probabilities need calibration before being trusted as real likelihoods.
What does “fitting a model” mean?
Training it: running the learning algorithm on training data so that its parameters are adjusted to minimise the loss. In scikit-learn this is the .fit(X_train, y_train) call; afterwards .predict or .predict_proba use the frozen parameters on new data.
What is the curse of dimensionality?
As the number of features grows, the volume of the space grows exponentially, so data becomes sparse, the amount of data needed to cover the space explodes, and distances between points become nearly equal. Distance-based methods like kNN and k-means suffer most, and overfitting becomes easier. Remedies: feature selection, dimensionality reduction (PCA), regularization, and models that handle sparse high-dimensional data (linear models).
What is data leakage?
Leakage is when information that would not be available at prediction time influences training, making offline scores unrealistically high. Common patterns: a feature derived from the label (a “treatment given” column when predicting disease); future-looking windows; in-sample target encoding; IDs that act as label proxies; fitting a scaler, selector or SMOTE on the whole dataset before splitting; duplicates or the same user across train and test; random splits of temporal data; and tuning on the test set. It leads to models that fail in production.
Going deeper
When do you use the bootstrap versus cross-validation?
Cross-validation retrains the model on each fold and estimates how well the whole training procedure (preprocessing + algorithm + hyperparameters) will generalise. The bootstrap resamples a fixed collection of predictions or observations with replacement to estimate the sampling distribution of a statistic: a confidence interval for F1, AUC, or the paired difference between two finished models. Use CV on the training data to choose models; use a paired bootstrap or McNemar on a sealed test set to decide whether the winner is really better. A bootstrap of training accuracy is optimistic because every resample still overlaps the original training rows. Out-of-bag scores from bagged trees are the useful exception.
Explain the bias–variance trade-off.
Expected test error decomposes into bias² + variance + irreducible noise. Bias is error from overly simple assumptions (a straight line through curved data); variance is error from sensitivity to the specific training sample (a deep tree that changes completely with new data). Increasing model flexibility lowers bias but raises variance. The goal is the complexity that minimises the total, found by validation curves or cross-validation, and shifted by regularization, more data (lowers variance) or richer features (lowers bias).
Why does L1 regularization produce sparse models while L2 does not?
Geometrically, the L1 constraint region is a diamond whose corners lie on the axes, and the loss contours usually first touch it at a corner, where some weights are exactly zero. The L2 region is a circle with no corners, so the touching point generally has all weights non-zero. Analytically, the L1 penalty's gradient has constant magnitude λ however small the weight, so it keeps pushing weights all the way to zero; the L2 gradient 2λw shrinks as w shrinks, so weights approach zero but never reach it.
What happens to Ridge or Lasso coefficients as λ goes to zero and to infinity?
As λ → 0 the penalty vanishes and the solution becomes ordinary least squares (possibly overfitting or unstable with correlated features). As λ → ∞ every coefficient is driven to zero and the model predicts only the intercept (the mean), badly underfitting. Lasso reaches exact zeros progressively along the way, which traces out the “regularization path”. In scikit-learn classifiers, C = 1/λ, so the directions are reversed.
What are the assumptions of linear regression, and what happens when they are violated?
- Linearity: otherwise systematic errors; add transformations or polynomial terms.
- Independent errors: violated by autocorrelation in time series; standard errors become wrong.
- Homoscedasticity: constant error variance; a funnel-shaped residual plot means intervals are unreliable; transform the target or use weighted least squares.
- Normal errors: only needed for exact inference, not for predictions.
- No perfect multicollinearity: correlated features make coefficients unstable and uninterpretable; use Ridge, drop or combine features, check VIF.
Predictions can still be fine when inference assumptions fail; interpretation of p-values and coefficients suffers most.
Why is cross-entropy preferred over MSE for training logistic regression?
With a sigmoid output, MSE produces a non-convex objective with flat regions: when the model is confidently wrong, the sigmoid saturates and the gradient becomes tiny, so learning stalls. Cross-entropy combined with the sigmoid gives a convex objective whose gradient is simply (p − y)x, large when the prediction is badly wrong. Cross-entropy is also the negative log-likelihood of a Bernoulli model, so minimising it is maximum-likelihood estimation.
How do you interpret a logistic regression coefficient?
A coefficient wj is the change in log-odds of the positive class per one-unit increase in xj, holding other features fixed. Exponentiating gives the odds ratio: w = 0.7 means the odds multiply by e0.7 ≈ 2.0 per unit. On standardized features, “one unit” is one standard deviation, which makes coefficients comparable. Correlated features can make individual coefficients misleading.
Why can a small C (strong regularization) work best on bag-of-words text classification?
A vocabulary of tens of thousands of words gives a very high-dimensional, sparse feature space with many rare words that appear in only a few reviews. Without strong regularization, the model assigns large weights to those rare words and memorises the training set. A small C (for example 0.01) forces weights to stay small and spread across many informative words, which generalises better. In one movie-review experiment, C = 0.01 was the best setting for both logistic regression and a linear SVM.
Gini impurity or entropy: which should you use?
They almost always select the same or very similar splits. Gini (1 − Σp²) is slightly faster because it avoids logarithms and is scikit-learn's default; entropy (information gain) is marginally more sensitive to changes in small class probabilities and is what ID3 and C4.5 use. The choice rarely matters compared with depth and leaf-size constraints, so tune those instead.
Compute the Gini impurity of a node with 6 positives and 4 negatives, and the Gini decrease of a split into (5+, 1−) and (1+, 3−).
Parent: 1 − (0.6² + 0.4²) = 0.48. Left child (6 samples): 1 − (25/36 + 1/36) ≈ 0.278. Right child (4 samples): 1 − (1/16 + 9/16) = 0.375. Weighted child impurity = 0.6 × 0.278 + 0.4 × 0.375 ≈ 0.317. Gini decrease ≈ 0.48 − 0.317 = 0.163. The equivalent information gain with entropy is about 0.971 − 0.715 = 0.256 bits.
How do you prevent a decision tree from overfitting?
Pre-pruning: limit max_depth, require min_samples_split and min_samples_leaf, cap max_leaf_nodes, or require a min_impurity_decrease. Post-pruning: grow fully and prune with cost-complexity pruning (ccp_alpha) chosen by cross-validation. Or move to an ensemble: a random forest averages many trees to cut variance. A study with a depth-1 tree (underfits), an unlimited tree (100% train accuracy, lower test accuracy) and a tuned tree shows the tuned one with the smallest train–test gap.
Why don't tree-based models need feature scaling?
A tree splits on whether a single feature exceeds a threshold. Any monotonic transformation of that feature (standardization, min-max, log) preserves the ordering of values, so the same partition of samples is available and the chosen splits are identical. Scaling can still matter if trees are combined with scale-sensitive models or regularized leaf values, but not for the split structure.
Compare bagging and boosting.
| Bagging | Boosting | |
|---|---|---|
| Goal | Reduce variance | Reduce bias (and variance) |
| Training | Independent, parallel, bootstrap samples | Sequential; each model corrects previous errors |
| Base learner | Deep, low-bias trees | Shallow, weak trees or stumps |
| Combination | Equal-weight vote or average | Weighted sum |
| Overfitting risk | Low from adding models | Rises with too many rounds or high learning rate |
| Noise sensitivity | Robust | More sensitive to label noise and outliers |
Why does a random forest sample features at each split, and not just rows?
If one feature is very strong, every bagged tree would split on it first and the trees would be highly correlated. Averaging correlated models barely reduces variance: the variance of the average is ρσ² + (1 − ρ)σ²/B, and the ρσ² term does not shrink with more trees. Restricting each split to a random subset of features (max_features, often √d) forces trees to use different features, lowers ρ, and makes averaging far more effective.
What is the out-of-bag score?
Each bootstrap sample omits roughly (1 − 1/n)n ≈ 36.8% of rows. For each row, the trees that did not see it can predict it, and aggregating those predictions gives an out-of-bag estimate of generalization performance without a separate validation set. It is close to a cross-validated estimate and is available via oob_score=True.
How does gradient boosting work?
It builds an additive model in stages. Start with a constant prediction (the mean). At each round, compute the negative gradient of the loss with respect to the current predictions (for MSE, simply the residuals y − F(x)), fit a small regression tree to those pseudo-residuals, and add it to the model scaled by a learning rate: Fm = Fm−1 + ηhm. This is gradient descent in function space. Small learning rates plus many trees with early stopping, row and column subsampling and depth limits give the best generalization.
How does AdaBoost differ from gradient boosting?
AdaBoost reweights the training samples: misclassified points get larger weights so the next weak learner focuses on them, and each learner gets a vote α = ½ ln((1 − ε)/ε) based on its weighted error (20% error gives α ≈ 0.69). It corresponds to minimising exponential loss, which makes it sensitive to outliers. Gradient boosting instead fits each learner to the negative gradient of any differentiable loss (squared, absolute, log loss, Huber), which is more general and more robust with the right loss.
What distinguishes XGBoost, LightGBM and CatBoost?
- XGBoost: second-order (gradient plus Hessian) split gains, L1/L2 regularization on leaf weights, sparsity-aware missing-value handling, level-wise tree growth, histogram and GPU modes.
- LightGBM: histogram-based splits, leaf-wise growth (deeper, more accurate trees but easier to overfit; control
num_leaves), GOSS gradient-based row sampling and exclusive feature bundling; very fast on large data. - CatBoost: ordered target statistics for categorical features without leakage, ordered boosting to reduce prediction shift, symmetric trees for fast inference, strong defaults.
Explain the kernel trick.
Some data only becomes linearly separable after mapping to a higher-dimensional feature space φ(x). SVM training and prediction depend on the data only through dot products xi·xj, so we can substitute a kernel K(xi, xj) = φ(xi)·φ(xj) that computes the high-dimensional dot product directly, without ever building φ(x). The RBF kernel exp(−γ‖x − z‖²) corresponds to an infinite-dimensional space. Example: points at −2, −1, 1, 2 labelled +, −, −, + are not separable on the line, but mapping x to (x, x²) separates them with the line x² = 2.5.
What do the C and gamma hyperparameters of an RBF SVM control?
C is the penalty for margin violations: high C tries to classify every training point correctly with a narrow margin (low bias, high variance); low C allows more violations for a wider, smoother margin (more regularization). Gamma sets the reach of each training point: high gamma means very local influence and a wiggly boundary that can overfit; low gamma means broad influence and a smoother, almost linear boundary. Tune both together on a logarithmic grid with cross-validation, after scaling features.
Compare SVM and logistic regression.
Both are linear classifiers in their basic form. Logistic regression minimises log loss, uses every point, and outputs calibrated-ish probabilities. SVM minimises hinge loss, which ignores correctly classified points beyond the margin, so only support vectors matter; it gives margins, not probabilities, and pairs naturally with kernels. SVMs can be more robust when classes are well separated; logistic regression is preferred when probabilities or coefficient interpretation are needed. On sparse text both perform similarly.
Why does weights="distance" sometimes help kNN, especially for a sparse minority class?
With uniform weights, a query surrounded by a few very close minority points and several farther majority points is outvoted by the majority. Distance weighting gives each neighbour a vote proportional to 1/d, so the very close minority neighbours dominate and the local evidence wins. It also makes the choice of k less critical, but can make predictions noisier if the closest neighbour is itself noisy.
Why is BernoulliNB, not MultinomialNB, the natural choice for binary one-hot text features?
BernoulliNB models each word as present or absent and explicitly uses the absence of a word as evidence, which suits 0/1 features. MultinomialNB models word counts; on binary data it loses the count information it is designed for and ignores absent words. For count or TF-IDF features, MultinomialNB (or ComplementNB for imbalance) is usually better.
Why does Naive Bayes work well despite its unrealistic independence assumption?
Classification only needs the class with the highest posterior to be correct, not the probabilities themselves. Even when dependencies distort the probabilities, they often distort all classes similarly, so the ranking survives. It also has very low variance (few parameters), which helps on small or high-dimensional data. The cost is overconfident, poorly calibrated probabilities and an inability to learn feature interactions such as negation.
How do you choose k in k-means?
Combine several signals: the elbow in the inertia-vs-k curve, the average silhouette score (higher is better), Davies–Bouldin (lower is better), the gap statistic, BIC if you use a GMM, stability of clusters across resamples, and above all whether the clusters are interpretable and actionable for the business. Always scale features first and use several initialisations.
When would you choose DBSCAN over k-means?
When clusters have irregular, non-spherical shapes; when you do not know the number of clusters; and when you want outliers labelled as noise instead of forced into a cluster (for example, geographic points or anomaly screening). Avoid it when clusters have very different densities (use HDBSCAN) or data is high-dimensional, where distance-based density becomes unreliable.
How does a Gaussian mixture model relate to k-means?
Both need k and alternate between assigning points and updating cluster parameters. k-means makes hard assignments and implicitly assumes spherical clusters of equal size. A GMM, fitted with EM, gives soft probabilistic memberships and learns a mean, a full covariance (elliptical shape) and a weight for each component. k-means is the limit of a GMM with equal spherical covariances shrinking to zero. GMMs also provide a likelihood, useful for density estimation, anomaly scores and BIC-based model selection.
Walk through the steps of PCA.
- Standardize features (mean 0, variance 1).
- Compute the covariance matrix (or directly take the SVD of the centred data).
- Find eigenvectors (directions) and eigenvalues (variance along each).
- Sort by eigenvalue and keep the top k components, for example enough to explain 95% of variance.
- Project the data onto those components.
With eigenvalues 4.2, 1.1, 0.5 and 0.2, two components explain (4.2 + 1.1)/6.0 ≈ 88% of the variance.
What is the difference between PCA and t-SNE?
PCA is a linear, deterministic projection that preserves global variance, can transform new data and can be used as features for downstream models. t-SNE is a non-linear, stochastic method that preserves local neighbourhoods for 2D/3D visualization; distances between clusters and cluster sizes in its output are not meaningful, it depends on perplexity and seed, and it cannot natively embed new points. UMAP is a faster alternative that preserves more global structure.
What is k-fold cross-validation, and how do you pick k?
Split the data into k folds; train on k − 1 and validate on the remaining one, k times. Report the mean and standard deviation. k = 5 or 10 is the usual compromise: larger k uses more training data per fit (less bias in the estimate) but costs more and can increase variance of the estimate; leave-one-out is the extreme. For small data, repeated stratified k-fold gives more stable estimates.
Grid search or random search?
Grid search is exhaustive and suits two or three hyperparameters with few values, but cost grows exponentially. Random search samples from distributions and, for the same budget, explores many more distinct values of each important hyperparameter, so it usually finds better settings when only a few hyperparameters really matter. For expensive models, Bayesian optimisation (Optuna, TPE) or successive halving/Hyperband are more efficient. Search learning rates and penalties on a log scale.
How does GridSearchCV work, and what should you do after it finishes?
You give it an estimator (ideally a whole pipeline), a parameter grid, a scoring metric and a CV scheme. It trains and cross-validates a model for every combination, picks the one with the best mean validation score, and by default refits it on all the training data (best_estimator_). Afterwards, inspect cv_results_ for stability, remember that best_score_ is optimistically biased, and evaluate the refitted model once on the held-out test set.
Explain ROC-AUC and what an AUC of 0.5, 0.72 and 0.95 mean.
The ROC curve plots the true positive rate against the false positive rate across all thresholds; the AUC is its area, equal to the probability that a random positive is scored higher than a random negative. 0.5 is random guessing, 0.72 is a useful but weak ranker that confuses many pairs, 0.95 is excellent ranking. Values below 0.5 mean the scores are inverted. AUC is threshold-independent and says nothing about calibration.
When should you prefer PR-AUC over ROC-AUC?
When positives are rare and you care about the positive class, such as fraud, rare disease or retrieval. ROC's false positive rate divides by the huge number of negatives, so even thousands of false alarms barely move it and ROC-AUC looks excellent. Precision divides by predicted positives, so PR curves expose the false-alarm burden. Remember the PR-AUC baseline equals the positive rate, not 0.5.
What is model calibration, and how is it different from accuracy?
A calibrated model's probabilities match observed frequencies: among cases scored 0.7, about 70% are positive. Accuracy measures how many predictions are correct, ignoring confidence; AUC measures ranking. A model can be accurate but badly calibrated. Check with reliability diagrams, Brier score or expected calibration error; fix with Platt scaling or isotonic regression on validation data. Calibration rarely changes ranking or accuracy much, but makes probabilities trustworthy for decisions.
What is the difference between macro, micro and weighted averaging?
Macro computes the metric per class and takes the unweighted mean, so each class counts equally. Weighted averages per-class metrics by class size. Micro pools all TP, FP and FN across classes before computing, and for single-label multiclass micro-F1 equals accuracy. With per-class F1 of 0.95, 0.90 and 0.40 on 500, 400 and 100 samples, macro-F1 is 0.75 but weighted-F1 is 0.875, hiding the failing class. Macro-F1 is the natural choice when every class matters equally, such as three wine cultivars.
Compare MAE, RMSE and R².
MAE is the average absolute error in target units, robust to outliers. RMSE is the square root of the mean squared error, also in target units, but penalises large errors more, so RMSE ≥ MAE and a big gap indicates a few large errors. R² is unitless: 1 − SSres/SStot, the fraction of variance explained relative to predicting the mean; it can be negative on test data. For actuals 3, 5, 2, 8 and predictions 2.5, 5, 4, 7: MAE 0.875, RMSE 1.146, R² 0.75.
What is SMOTE, and what are its risks?
SMOTE creates synthetic minority examples by picking a minority point, choosing one of its k nearest minority neighbours, and interpolating: xnew = xi + λ(xnn − xi). This fills in minority regions rather than duplicating points. Risks: it can create unrealistic samples where classes overlap, amplify noise, work poorly with categorical or very high-dimensional features, distort probabilities, and cause leakage if applied before splitting or outside the CV loop. Class weights plus threshold tuning are often as effective.
Explain SHAP values and how they differ from LIME.
SHAP assigns each feature its Shapley value: its average marginal contribution to the prediction across all possible coalitions of features. The contributions add up exactly to the prediction minus the average prediction, and the method satisfies consistency guarantees; TreeSHAP computes it quickly for tree ensembles. LIME perturbs the instance, gets black-box predictions, and fits a weighted linear model locally; it is model-agnostic and fast but can be unstable across runs and sensitive to its perturbation settings. Neither is causal.
What are data drift and concept drift?
Data (covariate) drift is a change in the input distribution P(X), such as a new customer demographic. Concept drift is a change in the relationship P(y | X), such as fraudsters changing tactics so the same features now mean something different. Label drift is a change in P(y), the base rate. Data drift is detectable without labels (PSI, KS tests); concept drift usually needs fresh ground truth and shows up as falling performance.
How does reinforcement learning differ from supervised learning?
In supervised learning each example comes with the correct answer and examples are independent. In RL there are no correct actions, only a scalar reward that may be delayed many steps; the agent's actions change the data it sees next; and it must balance exploring new actions against exploiting known good ones. The objective is to maximise cumulative discounted reward, not to match labels. Skip-gram and CBOW, for example, are self-supervised, not RL, because their targets come directly from the text.
Advanced
Derive the bias–variance decomposition for squared error.
Let y = f(x) + ε with E[ε] = 0 and Var(ε) = σ², and let f̂ be trained on a random dataset. Write f̄ = E[f̂(x)]. Then E[(y − f̂)²] = E[(f + ε − f̂)²] = σ² + E[(f − f̂)²] because ε is independent of f̂ and has zero mean. Adding and subtracting f̄: E[(f − f̄ + f̄ − f̂)²] = (f − f̄)² + E[(f̄ − f̂)²], since the cross term has expectation zero. So error = bias² + variance + σ².
Show why minimising cross-entropy is equivalent to maximum-likelihood estimation.
For binary labels modelled as Bernoulli with pi = σ(wTxi), the likelihood is ∏ piyi(1 − pi)1−yi. Taking logs gives Σ [yi log pi + (1 − yi) log(1 − pi)]. Maximising this is the same as minimising its negative average, which is exactly binary cross-entropy. The same argument with a categorical distribution gives categorical cross-entropy, and a Gaussian noise model gives MSE.
What is the Bayesian interpretation of L1 and L2 regularization?
Regularized estimation is maximum a posteriori (MAP) estimation: minimise −log likelihood − log prior. A Gaussian prior on weights, p(w) ∝ exp(−w²/2τ²), contributes a squared penalty (L2/Ridge) with λ inversely related to the prior variance. A Laplace prior, p(w) ∝ exp(−|w|/b), contributes an absolute penalty (L1/Lasso); its sharp peak at zero is why MAP estimates are sparse.
Why does Lasso behave poorly with highly correlated features, and what fixes it?
When features are strongly correlated, Lasso tends to pick one of them almost arbitrarily and zero out the rest, and the choice can flip between resamples, making selection unstable. Elastic Net adds an L2 term that encourages correlated features to share weight (the grouping effect) while still producing sparsity. Alternatives include grouping features first, stability selection, or PCA.
Explain the SVM dual formulation and why it enables kernels.
Using Lagrange multipliers αi for the margin constraints, the primal problem becomes the dual: maximise Σαi − ½Σi,j αiαjyiyj(xi·xj) subject to 0 ≤ αi ≤ C and Σαiyi = 0. The data appear only through dot products, and the decision function is f(x) = Σαiyi(xi·x) + b. Replacing dot products with a kernel K gives non-linear SVMs. Points with αi > 0 are the support vectors; the rest have no influence.
What makes a valid kernel?
By Mercer's theorem, K must be symmetric and positive semi-definite: for any finite set of points, the Gram matrix Kij = K(xi, xj) has no negative eigenvalues. Then K corresponds to a dot product in some feature space. Sums, positive scalings and products of valid kernels are valid. The sigmoid kernel is not PSD for all parameters, which is one reason it is rarely used.
How does XGBoost compute the gain of a split?
It uses a second-order Taylor expansion of the loss. For each leaf, with G the sum of gradients and H the sum of Hessians of the samples in it, the optimal leaf weight is w* = −G/(H + λ) and the leaf's contribution to the objective is −½G²/(H + λ). The gain of splitting a node into left and right is ½[GL²/(HL + λ) + GR²/(HR + λ) − (GL + GR)²/(HL + HR + λ)] − γ, where λ is L2 regularization on leaf weights and γ the minimum gain required to split (a pruning threshold).
Why does LightGBM's leaf-wise growth overfit more easily than level-wise growth, and how do you control it?
Leaf-wise growth always splits the single leaf with the largest loss reduction, so it can produce deep, unbalanced trees that chase small groups of samples; level-wise growth splits all leaves at a depth, acting as implicit regularization. Leaf-wise reaches lower loss with fewer leaves but overfits small data. Control it with num_leaves (well below 2max_depth), max_depth, min_child_samples, min_split_gain, row and column subsampling, L1/L2 regularization and early stopping.
What problem does CatBoost's ordered target statistics solve?
Naive target encoding replaces each category with the mean target over all rows, including the row itself, so the encoded feature leaks that row's label and the model overfits (target leakage and prediction shift). CatBoost orders rows by a random permutation and encodes each row using only target values of rows that come before it, with a prior for smoothing. Ordered boosting applies the same idea to residuals. The result is leakage-free categorical encoding without manual out-of-fold schemes.
Why must stacking use out-of-fold predictions?
If base models predict on the same rows they were trained on, those predictions are overfit and look more accurate than they will on new data, so the meta-model learns to over-trust the most overfit base model. Generating each base model's predictions for a row from a model trained on other folds (cross_val_predict) gives realistic inputs to the meta-model. The test set is then scored by base models refitted on all training data. Blending approximates this with one hold-out split.
Why can impurity-based feature importance be biased, and what are the alternatives?
Mean decrease in impurity is computed on training data and favours features with many possible split points (continuous or high-cardinality features such as IDs), because they offer more chances to reduce impurity by chance. It also splits credit arbitrarily among correlated features. Alternatives: permutation importance on held-out data (with repeats; beware correlated features), drop-column importance (retrain without the feature; expensive), and SHAP values, which give consistent local and global attributions.
What is nested cross-validation, and when is it necessary?
An inner CV loop selects hyperparameters; an outer loop evaluates the entire “tune then train” procedure on folds the inner loop never saw. It is necessary when you need an unbiased estimate of performance and do not have a separate large test set, for example on small medical datasets or when comparing algorithm families fairly, because the best inner CV score is optimistically biased by the selection itself. In scikit-learn: pass a GridSearchCV object to cross_val_score.
How do you cross-validate time-series models correctly?
Never shuffle. Use forward-chaining (expanding window) or sliding-window splits where each validation fold lies strictly after its training data (TimeSeriesSplit), optionally with a gap to reflect prediction latency and to avoid leakage from overlapping windows of lag features. Compute rolling features using only past data, re-fit preprocessing inside each split, and evaluate on multiple future horizons. Random k-fold leaks future information and overstates performance.
Derive the cost-optimal classification threshold.
For a case with calibrated probability p of being positive, predicting positive has expected cost (1 − p)CFP, and predicting negative has expected cost pCFN (assuming correct decisions cost nothing). Predict positive when (1 − p)CFP < pCFN, which gives p > CFP/(CFP + CFN). If a missed fraud costs 10 times a false alarm, the threshold is 1/11 ≈ 0.09. This only holds if probabilities are calibrated and the deployment base rate matches training.
How does resampling or class weighting affect predicted probabilities, and how do you correct it?
Training on a rebalanced dataset makes the model believe positives are more common than they are, inflating predicted probabilities. If the training positive rate was changed from π to π′, you can correct scores with the prior-shift formula: oddstrue = oddsmodel × [π/(1 − π)] / [π′/(1 − π′)]. More generally, recalibrate on a validation set with the real class ratio using Platt scaling or isotonic regression. Rankings (AUC) are largely unaffected.
What is the Matthews correlation coefficient and why is it recommended for imbalanced data?
MCC = (TP·TN − FP·FN) / √((TP + FP)(TP + FN)(TN + FP)(TN + FN)). It is the correlation between predicted and actual labels, ranging from −1 to 1 with 0 for random. It uses all four cells of the confusion matrix, so it is only high when the model does well on both classes, unlike accuracy (fooled by the majority) or F1 (ignores true negatives and depends on which class is labelled positive).
How does precision change when the deployment prevalence differs from the test set?
Recall and specificity are properties of the classifier on each class and do not depend on prevalence, but precision does: precision = (recall · π) / (recall · π + FPR · (1 − π)). With recall 0.9 and FPR 0.05, a 50% prevalence gives precision 0.95, while a 1% prevalence gives 0.009/(0.009 + 0.0495) ≈ 0.15. A model validated on a balanced test set will look far worse in a rare-event production setting.
Explain the EM algorithm for Gaussian mixtures.
Initialise means, covariances and mixing weights. E-step: compute responsibilities rik = πkN(xi | μk, Σk) / ΣjπjN(xi | μj, Σj), the probability that component k generated point i. M-step: update πk as the average responsibility, μk as the responsibility-weighted mean, and Σk as the responsibility-weighted covariance. Each iteration never decreases the log-likelihood, so EM converges, but only to a local optimum; use several initialisations and choose k with BIC.
Why is k-means sensitive to initialisation, and how does k-means++ help?
k-means performs coordinate descent on a non-convex objective, so it converges to a local minimum that depends on where centroids start; two starting centroids in the same true cluster can permanently split it. k-means++ chooses the first centroid at random and each next one with probability proportional to its squared distance from the nearest chosen centroid, spreading initial centroids out. This gives an expected O(log k) approximation guarantee and faster convergence. Combine it with multiple restarts (n_init).
How is PCA related to the singular value decomposition?
For centred data X = UΣVT, the columns of V are the principal directions (eigenvectors of XTX), the squared singular values divided by (n − 1) are the eigenvalues (variances), and UΣ gives the projected coordinates. Computing PCA through SVD avoids explicitly forming the covariance matrix, which is more numerically stable; randomized SVD scales it to large data, and truncated SVD works on sparse matrices without centring.
When can PCA hurt a supervised model?
PCA keeps high-variance directions without looking at the target. If the discriminative signal lies in a low-variance direction (for example, a small but consistent shift between classes), dropping those components removes exactly what the classifier needs. It also destroys feature interpretability and can blur sparse, meaningful features. Choose the number of components by downstream cross-validated performance, or use supervised reduction (LDA, feature selection).
What are Shapley values, formally, and why is exact SHAP expensive?
For feature j, φj = ΣS ⊆ F\{j} [|S|!(|F| − |S| − 1)!/|F|!] · [v(S ∪ {j}) − v(S)], the weighted average of j's marginal contribution over all subsets S of the other features, where v(S) is the expected prediction when only features in S are known. It is the unique attribution satisfying efficiency (sums to the prediction minus base), symmetry, dummy and additivity. Exact computation needs 2|F| subsets; TreeSHAP exploits tree structure to compute it in polynomial time, and KernelSHAP approximates it by sampling.
How do you detect drift statistically, and what are the pitfalls?
Compare a reference window (training or a stable period) with a live window per feature: PSI or Jensen–Shannon divergence on binned values, Kolmogorov–Smirnov for continuous features, chi-square for categorical ones, plus multivariate checks such as a “domain classifier” trained to distinguish reference from live data (AUC well above 0.5 signals drift). Pitfalls: with huge samples, tests flag tiny irrelevant shifts; many features cause multiple-testing false alarms; drift in an unimportant feature may not matter. Weight alerts by feature importance and confirm with performance on fresh labels.
Explain the Bellman equation and the difference between Q-learning and SARSA.
The Bellman optimality equation says the value of an action equals the immediate reward plus the discounted value of acting optimally afterwards: Q*(s, a) = E[r + γ maxa′ Q*(s′, a′)]. Q-learning updates toward r + γ maxa′Q(s′, a′), the greedy next action, regardless of what the agent actually does next, so it is off-policy. SARSA updates toward r + γQ(s′, a′) using the action actually taken by the current (for example ε-greedy) policy, so it is on-policy and learns safer behaviour when exploration is risky.
How is reinforcement learning used in RLHF for language models, and why is a KL penalty needed?
After supervised fine-tuning, humans rank multiple responses; a reward model is trained on those preferences (typically a Bradley–Terry pairwise loss). The LLM is then treated as a policy whose actions are tokens and optimised with PPO to maximise the reward model's score. Without constraint the policy would exploit weaknesses of the imperfect reward model (reward hacking), producing odd or degenerate text; a KL-divergence penalty against the reference model keeps outputs close to the fluent original distribution. DPO achieves a similar objective directly from preference pairs without an explicit RL loop.
What is the no-free-lunch theorem, and what does it mean in practice?
Averaged over all possible problems, every learning algorithm performs equally well; an algorithm only does better on some problems by doing worse on others. In practice real-world problems are not uniformly random, so inductive biases matter: trees suit tabular interactions, CNNs suit images, linear models suit sparse text. The lesson is to match the model's assumptions to the data and verify empirically with honest validation, rather than assuming one algorithm is universally best.
Why do classical linear models sometimes beat neural networks on text classification?
On bag-of-words features with moderate data, linear models are hard to beat: sentiment is largely carried by individual words, the problem is nearly linearly separable in high dimensions, and strong regularization controls variance. Neural sequence models need more data, careful tuning (gradient clipping, sequence length, learning rate) and pretrained embeddings. In one experiment, a linear SVM and logistic regression (F1 ≈ 0.875) beat vanilla RNN (0.57) and LSTM (0.53) models that suffered vanishing gradients, and a bidirectional LSTM with pretrained embeddings (0.81) that overfit; an attention model came closest (0.86). Low training loss did not guarantee better test F1.
What is target encoding, and how do you implement it without leakage?
Target encoding replaces each category with a statistic of the target for that category, usually the mean. To avoid leakage, compute it out-of-fold (each row's encoding uses only other folds), smooth toward the global mean with a weight that depends on category count, m·global + n·category_mean over (m + n), and add noise if needed. At inference, use encodings computed on the full training data, with the global mean for unseen categories. scikit-learn's TargetEncoder does cross-fitting automatically.
How do you estimate uncertainty or prediction intervals for a regression model?
Options: quantile regression (train models with pinball loss at, say, the 5th and 95th percentiles; LightGBM and GradientBoosting support it), conformal prediction (use residuals on a calibration set to produce intervals with guaranteed coverage under exchangeability), bootstrap ensembles (spread of predictions across resampled models), Bayesian models or Gaussian processes, and for random forests, the spread across trees or quantile regression forests. Always check empirical coverage on held-out data.
What is the difference between a generative and a discriminative classifier, with examples?
A discriminative classifier models P(y | x) or the decision boundary directly: logistic regression, SVM, trees, most neural classifiers. A generative classifier models how data is generated, P(x | y) and P(y), and uses Bayes' rule for P(y | x): Naive Bayes, linear discriminant analysis, GMM-based classifiers. Generative models can sample data and handle missing features, and often do better with very little data; discriminative models usually win with more data because they spend capacity only on the boundary.
Scenario & debugging
A disease screener is 99% accurate on data with 9,900 healthy and 100 sick patients, but a colleague says it is useless. Who is right, and what metric should the team use?
The colleague is probably right: a model that always predicts “healthy” also scores 99% while catching zero sick patients. Accuracy is dominated by true negatives under imbalance. For a life-threatening disease, a false negative (a sick patient sent home) is far worse than a false alarm (an extra test), so prioritise recall on the sick class, subject to a minimum precision so clinics are not flooded; F2 or recall at a fixed precision are good single numbers. Precision alone ignores misses, and F1 wrongly treats both errors as equally costly. Also report the confusion matrix and PR-AUC.
Your model scored 0.99 AUC offline but performs poorly in production. What do you investigate?
- Leakage: features that encode the label or use future information; look at the top feature importances for something suspiciously strong.
- Split problems: duplicates or the same users in train and test; random split on temporal data.
- Training–serving skew: features computed differently online (units, time zones, default values, missing-value handling).
- Distribution shift: production population differs from training data.
- Label definition mismatch between the offline dataset and the production outcome.
Fix by rebuilding a time-based, group-aware evaluation and logging live features for comparison.
Production accuracy has dropped from 92% to 84% over three months. Walk through your response.
- Rule out bugs: pipeline failures, schema changes, a new upstream data source, missing-value spikes, a changed feature definition.
- Check data drift per feature (PSI, KS) weighted by importance, and prediction drift.
- Check label and concept drift with recent ground truth: has the base rate or the feature–target relationship changed? Segment performance by region, device and customer type.
- Act: fix bugs; if only the base rate changed, recalibrate or move the threshold; if concept drift, retrain on recent data (possibly with time-weighting) and validate on the latest period.
- Prevent recurrence: drift dashboards, alerts, scheduled or triggered retraining, shadow deployment of the retrained model, one-click rollback.
Your training accuracy is 99% and validation accuracy is 70%. What do you do?
This is a large generalization gap, typically overfitting. First check it is not caused by a train/validation mismatch or leakage within training (for example duplicated rows in training). Then, in rough order of cost: increase regularization (lower C, higher λ, dropout), reduce model complexity (shallower trees, fewer features, larger k), use early stopping, switch to an ensemble like a random forest, add data or augmentation, and tune with cross-validation. Plot a learning curve: if the gap narrows with more data, collecting data will help.
Both training and validation error are high. What now?
The model is underfitting (high bias). Try a more flexible model (trees or boosting instead of linear), add or engineer better features (interactions, polynomial terms, domain ratios), reduce regularization, train longer or with a better learning rate, and check data quality: noisy or inconsistent labels can make the task look hard. Compare with a baseline and human-level performance to see how much headroom exists. More data alone will not fix high bias.
A teammate scaled the full dataset with StandardScaler before splitting and got great results. What is wrong, and how do you fix it?
The scaler learned the mean and standard deviation of the test rows, so information from the test set leaked into the training transformation, making results slightly optimistic; the effect is larger for small datasets and much larger for steps like feature selection, target encoding or SMOTE done the same way. Fix: split first, fit the scaler on training data only, then transform validation and test, ideally by putting the scaler inside a Pipeline so cross-validation refits it on each training fold.
You must build a fraud model where only 0.2% of transactions are fraud. Describe your approach.
- Frame costs: value of a caught fraud vs cost of a blocked legitimate transaction and analyst review capacity.
- Time-based split; stratified CV within the training period.
- Features: transaction amount relative to the user's history, velocity counts over past windows, merchant and device risk, geography mismatch, all computed strictly from the past.
- Model: gradient boosting with
scale_pos_weightor class weights; compare with logistic regression baseline and an Isolation Forest score as a feature. - Metric: PR-AUC and recall at the precision (or alert volume) the operations team can handle; choose the threshold on validation data from the cost ratio.
- Calibrate if scores feed decisions; monitor drift closely because fraudsters adapt; feed analyst verdicts back as labels.
A spam filter is sending important customer emails to the spam folder. Which metric is failing, and what do you change?
These are false positives, so precision on the spam class is too low for the product. Raise the decision threshold, weight false positives more heavily in training, add whitelist and sender-reputation features, and evaluate with precision at a required recall (or F0.5). Also look at the misclassified emails for patterns (newsletters, invoices) and add labelled examples of those.
A medical model must reach recall of at least 0.95 on malignant cases while keeping precision above 0.60. How do you achieve and verify this?
Make malignant the positive class (in the breast-cancer dataset that means remapping 0 to 1). Tune models with recall-oriented scoring and class weights using stratified CV. Then create a validation split from the training data, get predicted probabilities, and plot the precision–recall curve; choose the highest threshold that achieves recall ≥ 0.95 and check precision at that point is ≥ 0.60. Only then evaluate once on the untouched test set and report the number of false negatives. Never choose the threshold on the test set, or the final metrics become optimistic.
Your logistic regression gives very different coefficients each time you retrain on slightly different data. Why, and what do you do?
Likely multicollinearity: correlated features can trade weight between them with little change in predictions, so individual coefficients are unstable, and signs can even flip. Check correlations and variance inflation factors. Fixes: add L2 regularization (Ridge-style) to stabilise, drop or combine redundant features, use PCA, or use Elastic Net. If the goal is prediction rather than interpretation, the instability may not matter; if it is interpretation, it matters a lot.
kNN performs terribly on a dataset with 300 features. Why, and how do you fix it?
In high dimensions distances concentrate (all points look roughly equally far away), irrelevant features add noise to every distance, and unscaled features dominate. Fix by scaling, removing irrelevant features (filter or embedded selection), reducing dimensions with PCA or learned embeddings, choosing a more suitable metric (cosine for text), and tuning k. Or use a model that handles high dimensions better, such as regularized linear models or gradient boosting.
Your k-means clusters look meaningless to the business team. What might be wrong?
Common causes: features not scaled, so one large-range feature (income) defines the clusters; categorical one-hot features used with Euclidean distance; outliers dragging centroids; the data has non-spherical or no real cluster structure; k chosen poorly; or features that do not reflect the business question. Try scaling, feature selection guided by the business goal, log-transforming skewed variables, DBSCAN or GMM for other shapes, k-prototypes or Gower distance for mixed data, and profile each cluster with interpretable summaries.
A random forest shows “customer_id” as the most important feature. What does this tell you?
The model is memorising individual customers: IDs have many unique values, so impurity-based importance favours them, and if the same customers appear in train and test, the model looks good while learning nothing generalizable. Drop identifiers, split by customer (group split), and verify with permutation importance on held-out data. If an ID-like feature still carries signal, it may encode something real (for example, account age embedded in sequential IDs), which should be extracted explicitly.
You tuned many hyperparameters with GridSearchCV and reported best_score_ as the expected performance. Your manager is surprised when the real performance is lower. Why?
Selecting the best of many configurations on the same CV folds overfits the validation data: part of the winner's score is luck. best_score_ is therefore optimistically biased, more so with small data and large grids. Report a score from an untouched test set, or use nested cross-validation to estimate the performance of the whole tuning procedure.
The MLP beats logistic regression by 0.5 macro-F1 points on a small dataset. Which would you ship?
Probably logistic regression, unless the gain is clearly beyond the cross-validation noise and matters to the business. Check the CV standard deviations: on a small dataset, a 0.5-point difference is usually within noise. Logistic regression is easier to explain (coefficients tell a winemaker or doctor why), cheaper and faster to serve, easier to debug and more stable under drift. Choose the MLP only if the improvement is consistent, significant and worth the extra complexity.
In an MLP, what happens if the L2 penalty (alpha) is set too high or too low?
Too high: weights are forced toward zero, the network behaves almost linearly or constant, and both training and validation scores fall (underfitting). Too low: the network can fit noise in a small dataset, training score is near perfect but validation lags (overfitting). Tune alpha on a log scale (0.0001, 0.001, 0.01, 0.1) with cross-validation, alongside early stopping and architecture size.
Your model's training loss is very low but test F1 is worse than a simpler model. What happened?
Low training loss is necessary but not sufficient: the model has overfit, or its predictions are skewed toward one class. A bidirectional LSTM example had training loss 0.048 but test F1 0.81, below a linear model at 0.875, with low precision because it over-predicted the positive class. Check the validation loss curve, the confusion matrix and per-class precision/recall; add regularization, early stopping or dropout; and tune the threshold on validation data.
A stakeholder wants the model's predicted probabilities to set insurance premiums. What must you check first?
Calibration. Premiums depend on the actual probability of a claim, so a 0.2 score must correspond to about a 20% claim rate. Plot a reliability diagram and compute the Brier score on held-out data with the real class ratio; if the model was trained with class weights or resampling, its probabilities are inflated. Recalibrate with Platt scaling or isotonic regression, check calibration by segment, and consider fairness and regulatory constraints on which features may be used.
You need to explain to a customer why their loan application was rejected by a gradient-boosting model. How?
Compute a local explanation, such as SHAP values from TreeSHAP, for that application, and translate the largest negative contributors into plain-language reason codes (“high debt-to-income ratio”, “short credit history”). Offer a counterfactual where appropriate (“reducing outstanding debt by X would likely change the decision”). Ensure the explanation is faithful, uses only permitted features, and is reviewed for regulatory requirements. If explanations are central, consider a monotonic-constrained or inherently interpretable model.
After deploying, the fraction of positive predictions doubled overnight, but nothing in the model changed. What do you check?
Check the inputs: an upstream schema or unit change (cents vs dollars), a feature suddenly null and imputed with an extreme value, a new category mapped to “unknown”, a broken join, or a time-zone bug. Compare live feature distributions with the training reference and with yesterday's. Then consider genuine shifts: a marketing campaign, a holiday, a real attack. Put in place data-quality validation (schema, ranges, null rates) that blocks bad inputs before scoring.
You have a large unlabelled dataset and a budget to label only 2,000 examples. How do you proceed?
Use the unlabelled data: cluster or embed it to understand its structure and sample a diverse, representative initial labelled set (stratified by cluster). Train a baseline, then use active learning to label the most informative examples (uncertain predictions, disagreement across models, or diverse samples). Apply semi-supervised methods such as pseudo-labelling of high-confidence predictions, or use pretrained or self-supervised embeddings as features. Keep a random, untouched labelled subset for honest evaluation.
Two models have ROC-AUC 0.90 and 0.88, but the second has higher precision at the operating point you care about. Which do you choose?
The one that performs better where the product actually operates. ROC-AUC averages over all thresholds, including many that will never be used. If the business constraint is, for example, “at most 500 alerts per day” or “precision ≥ 0.8”, compare recall or precision at that operating point (or partial AUC in that region), check the difference is stable across CV folds or bootstrap samples, and weigh cost, latency and interpretability.
Your time-series forecasting model looked excellent in random k-fold CV but fails on next month's data. Why?
Random k-fold on temporal data trains on the future and validates on the past, and lag or rolling features computed over the full series leak future values. The model learns patterns that rely on information it will not have. Re-evaluate with forward-chaining time splits (TimeSeriesSplit with a gap), compute features only from past data, and test across several future periods, including seasonal changes.
A multiclass model has 90% accuracy, but one small class is almost never predicted correctly. How do you detect and fix this?
Accuracy and weighted averages hide it; look at the per-class classification report, the confusion matrix and macro-F1. Fixes: class weights, targeted oversampling or data collection for that class, per-class thresholds or cost-sensitive decision rules, features that distinguish it from the classes it is confused with, and checking whether its labels are noisy or ambiguous (perhaps it should be merged with another class).
Adding a new feature improved validation AUC from 0.78 to 0.97. Should you celebrate?
Not yet. A jump that large from one feature is a classic leakage signal. Ask when the feature's value becomes known relative to the prediction time, whether it is derived from the label or from post-outcome processes (for example, “number of collection calls” when predicting default), and whether it will be available in production with the same definition. Validate with a strict time-based split and check that the feature's values at prediction time match what was used in training.
An e-commerce recommender only ever suggests the same popular items. What is going on, and what would you change?
This is popularity bias reinforced by a feedback loop: the model learns from clicks on items it already showed, which were mostly popular ones, so it keeps recommending them. Add exploration (bandit-style slots), debias training data (inverse propensity weighting), include diversity, novelty and coverage in re-ranking and offline metrics, use content features to surface long-tail and new items (cold start), and measure the effect with an online A/B test on engagement and long-term retention.
You are asked to deploy a model with a 20 ms latency budget, but your best model is a 2,000-tree ensemble that takes 80 ms. What are your options?
Reduce trees with a higher learning rate or stronger early stopping, limit depth or leaves, use a faster inference runtime (compiled trees, ONNX, Treelite), batch requests, precompute features and cache predictions for frequent entities, or distil the ensemble into a smaller model trained on its predictions. Measure the accuracy loss of each option against the latency gain; sometimes a simpler model is within noise of the best and meets the budget easily. Also check whether feature retrieval, not the model, dominates latency.
A stakeholder asks whether you should train until the loss stops decreasing, or pick a fixed number of iterations. What do you advise?
Neither exactly: monitor the validation metric and stop when it has not improved for a patience window, keeping the best checkpoint (early stopping). Training loss usually keeps falling long after the model has started to overfit. For gradient boosting, set a large maximum number of trees and pass early stopping on a validation set to fit (XGBoost 2+: early_stopping_rounds is an argument of fit, not the constructor); for neural networks, use a callback that restores the best weights. The number of iterations is then an outcome, not a guess.