Machine Learning Interview Questions: 40-Question Checkpoint

Most ML screens recycle the same forty or so theory fundamentals, just reworded. That is good news: the list is learnable. This checkpoint collects the machine learning interview questions that actually come up, grouped the way real screens group them, each with a spoken-style answer and runnable code that proves the claims people get wrong. Tally your score at the end and turn every miss into a short revisit list.

“A model is only as trustworthy as the way you tested it.”

Last Updated: July 2026 | Tested on: Python 3.14.6, scikit-learn 1.9.0, NumPy 2.4.6 | Difficulty: Intermediate | Reading Time: 28 minutes

Classic ML interviews run on three currents: theory you can state, tradeoffs you can defend, and debugging scenarios you can reason through live. Bias versus variance is theory. Choosing recall over precision for fraud detection is a tradeoff. “Training accuracy is 99% but production is 60%, what do you check first” is the debugging scenario, and it is where most candidates stall. The forty questions below train all three muscles, and every claim comes with runnable scikit-learn or NumPy code, because “I watched it happen in a real run” beats “I read it somewhere” in any technical conversation.

These forty stay on ML theory; for the language round of the same loop, the Python interview questions checkpoint drills the core-Python side.

25 or moreunder 25ML theory checkpoint~40 rapid-fire questionsBias, varianceand fitRegularizationand regressionMetricswhen accuracy liesValidationand leakageModels andunsupervisedScore yourselfout of 40Coding-readygo to the drillsRevisit listeach miss names its postRe-read linked posts,rescore in a weekCoding roundk-means + logisticgradient step in NumPyDesign warmupchurn predictor sketchOn-ramp toML system designThe ML Theory Screen: Five Groups to a Score, Then the Coding Round

That flowchart is your study route: five theory groups produce a score out of 40, and under 25 means a revisit list with a linked post for every miss. Land 25 or better and you earn the coding round, two from-scratch drills plus a churn-predictor sketch that sets up the ML system-design chapter later. Each section below follows the route in order.

How This Checkpoint Works

Each of the five groups below has eight questions, forty in total, chosen because these machine learning interview questions show up again and again in real screens across companies of every size. For each group you get a table with a one-line answer you could say out loud and a link to the post that explains it properly, then one runnable code block that proves the part people usually get wrong. Read the answer, cover it, and say it back in your own words. If you cannot, that question goes on your revisit list.

Give yourself one point for every question you can answer cleanly without peeking, so the group tables add up to 40. Keep a running tally on paper. A spoken answer counts only if you could explain it to a teammate in three sentences, not just recognize the words. We total it up in the scoring section, and the honest truth is that this checkpoint measures whether you understand ML theory, while the drills at the end measure whether you can still write the math in NumPy under mild pressure.

Bias, Variance, and Fit

Think of a student cramming for an exam. One student learns three shallow rules and applies them to everything, so they miss the nuance and score badly on any question, that is high bias, or underfitting. Another student memorizes the exact answer to every practice question word for word, then panics when the real exam rephrases them, that is high variance, or overfitting. The whole group is about spotting which student your model is behaving like, and steering it to the calm middle that actually learned the pattern.

QuestionOne-line answerDeep dive
Bias vs variance?High bias underfits (too simple); high variance overfits (too sensitive to noise).ML Math Intuition
What is overfitting?The model memorizes training noise and fails on new data.Train/Test Split
What is underfitting?The model is too simple to capture the real pattern.Linear Regression
The bias-variance tradeoff?Lowering one usually raises the other; aim for the sweet spot between them.ML Math Intuition
How do you spot overfitting?A big gap between low training error and high validation error.Train/Test Split
What is generalization?Performance on unseen data, the only score that actually counts.ML Introduction
More data or simpler model?Both cut variance; more data lets a richer model behave, less data needs simpler.Random Forest
Signal vs noise?Signal is the real pattern; noise is random. Fit the signal, never the noise.Feature Selection

📄 fit.py: watch the train-test gap open as model complexity climbs

import numpy as np
from sklearn.linear_model import LinearRegression
from sklearn.preprocessing import PolynomialFeatures
from sklearn.pipeline import make_pipeline
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_squared_error

rng = np.random.default_rng(0)
X = np.linspace(0, 1, 60).reshape(-1, 1)
y = np.sin(2 * np.pi * X).ravel() + rng.normal(0, 0.15, 60)
Xtr, Xte, ytr, yte = train_test_split(X, y, test_size=0.4, random_state=0)

for degree in (1, 4, 15):
    model = make_pipeline(PolynomialFeatures(degree), LinearRegression())
    model.fit(Xtr, ytr)
    tr = mean_squared_error(ytr, model.predict(Xtr))
    te = mean_squared_error(yte, model.predict(Xte))
    verdict = "underfit" if degree == 1 else ("good fit" if degree == 4 else "overfit")
    print(f"degree {degree:2d}: train MSE {tr:.3f} | test MSE {te:.3f}  -> {verdict}")

▶ Output

degree  1: train MSE 0.211 | test MSE 0.149  -> underfit
degree  4: train MSE 0.022 | test MSE 0.037  -> good fit
degree 15: train MSE 0.011 | test MSE 0.050  -> overfit

What happened here: The straight line (degree 1) has high error on both training and test data, that is bias, the model is too simple to bend with a sine wave. The degree-4 model fits nicely and its train and test errors stay close together, the healthy sign. The degree-15 model earns the lowest training error of all, 0.011, by wiggling through the noise, but its test error jumps back up to 0.050, and that gap between a great training score and a worse test score is the fingerprint of overfitting. This one table is the whole bias-variance conversation, and interviewers love that you can show it rather than recite it.

Regularization and Regression

Picture packing a bag for a weekend trip. Without a limit you throw in ten pairs of shoes just in case, and the bag becomes heavy and useless. Regularization is a weight limit on the bag: it charges the model a penalty for every large coefficient, so it only keeps the features that truly earn their place. Ridge quietly shrinks everything a little, while Lasso is stricter and drops the weak items to zero entirely, which is why Lasso doubles as automatic feature selection.

QuestionOne-line answerDeep dive
What is regularization?A penalty on large weights that curbs overfitting and simplifies the model.Ridge & Lasso
L1 (Lasso) vs L2 (Ridge)?L1 pushes weak weights to exactly zero; L2 shrinks all weights smoothly.Ridge & Lasso
What does alpha (lambda) control?Penalty strength: bigger alpha means a simpler, more shrunken model.Ridge & Lasso
Why does Lasso select features?Its L1 penalty drives unhelpful coefficients all the way to zero.Feature Selection
Linear regression assumptions?Linearity, independent errors, constant variance, roughly normal residuals.Linear Regression
What is multicollinearity?Correlated features make coefficients unstable; Ridge steadies them.Ridge & Lasso
What does R-squared mean?The share of the target’s variance the model explains; 1.0 is perfect.Linear Regression
When use polynomial features?When the relationship curves; but a high degree overfits very fast.Ridge & Lasso

📄 regularize.py: five noise features, and only Lasso zeros them out

import numpy as np
from sklearn.linear_model import LinearRegression, Ridge, Lasso

rng = np.random.default_rng(1)
n = 80
# Only the first 3 features really matter; the rest are pure noise
X = rng.normal(size=(n, 8))
true_w = np.array([5.0, -4.0, 3.0, 0, 0, 0, 0, 0])
y = X @ true_w + rng.normal(0, 0.5, n)

plain = LinearRegression().fit(X, y)
ridge = Ridge(alpha=5.0).fit(X, y)
lasso = Lasso(alpha=0.3).fit(X, y)

np.set_printoptions(precision=2, suppress=True)
print("plain OLS coefs:", plain.coef_)
print("ridge   coefs:  ", ridge.coef_)
print("lasso   coefs:  ", lasso.coef_)
print("lasso zeroed features:", int(np.sum(np.abs(lasso.coef_) < 1e-8)), "of 8")

▶ Output

plain OLS coefs: [ 4.94 -3.97  3.    0.09 -0.02 -0.13  0.12 -0.04]
ridge   coefs:   [ 4.5  -3.71  2.68  0.04  0.03 -0.04  0.18 -0.08]
lasso   coefs:   [ 4.43 -3.67  2.55  0.    0.   -0.    0.   -0.  ]
lasso zeroed features: 5 of 8

What happened here: All three models recover the real coefficients near 5, -4, and 3 for the first three features. The difference is what they do with the five noise features that should be zero. Plain least squares leaves them as small nonzero values like 0.09 and -0.13, harmless here but noise the model still listens to. Ridge shrinks everything toward zero a little without removing anything. Lasso is the interesting one: it drives all five noise features to exactly 0.0, so the printout confirms it zeroed 5 of 8, which is the concrete reason people say Lasso performs feature selection while Ridge does not.

Metrics: When Accuracy Lies

Imagine a smoke alarm that never goes off. In a house that rarely catches fire, it is right more than 99% of the time, and that number is worthless because the one moment it matters, it fails. That is the accuracy trap on imbalanced data. This group is about the metrics that survive imbalance: precision asks how many of your alarms were real fires, recall asks how many real fires you caught, and F1 blends the two so one comfortable number cannot hide a disaster.

QuestionOne-line answerDeep dive
When does accuracy mislead?On imbalanced classes; always guessing the majority can score 96%.Model Evaluation
Precision vs recall?Precision: of predicted positives, how many were right. Recall: of real positives, how many caught.Model Evaluation
What is the F1 score?The harmonic mean of precision and recall, one number balancing both.Model Evaluation
What is a confusion matrix?A table of true and false positives and negatives (TP, FP, FN, TN).Confusion Matrix
What is ROC-AUC?The chance the model ranks a random positive above a random negative.ROC Curves
PR curve vs ROC, when each?Precision-recall for heavy imbalance; ROC when classes are fairly balanced.ROC Curves
The precision-recall tradeoff?Moving the decision threshold trades one against the other.Model Evaluation
How do you pick a metric?From the real cost of a false positive versus a false negative.Model Evaluation

📄 metrics.py: a 96% accurate model that catches zero real positives

import numpy as np
from sklearn.dummy import DummyClassifier
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score, confusion_matrix
from sklearn.datasets import make_classification

X, y = make_classification(n_samples=2000, weights=[0.97, 0.03],
                           n_informative=5, random_state=7)
Xtr, Xte, ytr, yte = train_test_split(X, y, test_size=0.3, random_state=7)

# A lazy model that always predicts the majority class
dummy = DummyClassifier(strategy="most_frequent").fit(Xtr, ytr)
pred_d = dummy.predict(Xte)
print(f"always-majority: accuracy {accuracy_score(yte, pred_d):.3f}  recall {recall_score(yte, pred_d):.3f}")

# A real model that pays attention to the rare class
clf = LogisticRegression(max_iter=1000, class_weight="balanced").fit(Xtr, ytr)
pred_c = clf.predict(Xte)
print(f"real classifier: accuracy {accuracy_score(yte, pred_c):.3f}  "
      f"precision {precision_score(yte, pred_c):.3f}  recall {recall_score(yte, pred_c):.3f}  f1 {f1_score(yte, pred_c):.3f}")
tn, fp, fn, tp = confusion_matrix(yte, pred_c).ravel()
print(f"confusion matrix -> TN {tn}  FP {fp}  FN {fn}  TP {tp}")

▶ Output

always-majority: accuracy 0.963  recall 0.000
real classifier: accuracy 0.772  precision 0.129  recall 0.909  f1 0.226
confusion matrix -> TN 443  FP 135  FN 2  TP 20

What happened here: The lazy model that always predicts the majority class scores a shiny 96.3% accuracy and yet its recall is 0.000, it never catches a single real positive, which is the accuracy trap in one line. The real classifier looks worse on accuracy, 77.2%, but its recall is 0.909, meaning it catches 20 of the 22 actual positives, as the confusion matrix confirms with only 2 false negatives. Its precision is low at 0.129 because it raises many false alarms, and that is the honest tradeoff you would tune with the threshold. The lesson interviewers want stated plainly: on imbalanced data you report precision, recall, and F1, never accuracy alone.

Validation and Leakage

Here is the classroom version. If a teacher hands out the exam answers during revision, everyone scores full marks and the exam measures nothing. Data leakage is exactly that: information from the test set sneaks into training, and your reported score becomes a fantasy. The cure is discipline about what your model is allowed to see and when, and the safest way to enforce it is a scikit-learn Pipeline that refits its preprocessing on each training fold only, so the validation fold stays a true stranger.

QuestionOne-line answerDeep dive
Why a train/test split?To estimate how the model does on data it has never seen.Train/Test Split
What is cross-validation?Rotate the validation fold k times and average, for a steadier estimate.Cross-Validation
What is k-fold?Split data into k parts; each takes a turn as the validation set.Cross-Validation
What is stratified k-fold?K-fold that keeps each class ratio steady per fold; vital for imbalance.Cross-Validation
What is data leakage?Test information reaches training, so scores look far better than reality.ML Pipeline
Scale before or after the split?Fit the scaler on training data only, ideally inside a pipeline.ML Pipeline
Why use a Pipeline?It bundles preprocessing with the model so CV refits it per fold, no leakage.ML Pipeline
Validation vs test set?Validation tunes choices; the test set is the final untouched judge.Hyperparameter Tuning

📄 leakage.py: random labels that score 76% when you leak, 51% when you do not

import numpy as np
from sklearn.preprocessing import StandardScaler
from sklearn.feature_selection import SelectKBest, f_classif
from sklearn.linear_model import LogisticRegression
from sklearn.pipeline import make_pipeline
from sklearn.model_selection import cross_val_score

rng = np.random.default_rng(3)
# 200 samples, 1000 pure-noise features, labels are random: TRUE score must be ~0.5
X = rng.normal(size=(200, 1000))
y = rng.integers(0, 2, size=200)

# LEAKY: pick the "best" features using ALL the data, then cross-validate
Xsel = SelectKBest(f_classif, k=20).fit_transform(X, y)
leaky = cross_val_score(LogisticRegression(max_iter=1000), Xsel, y, cv=5).mean()

# HONEST: selection happens inside the pipeline, refit on each training fold only
pipe = make_pipeline(StandardScaler(), SelectKBest(f_classif, k=20),
                     LogisticRegression(max_iter=1000))
honest = cross_val_score(pipe, X, y, cv=5).mean()

print(f"leaky  CV accuracy (selection outside CV): {leaky:.3f}")
print(f"honest CV accuracy (selection in pipeline): {honest:.3f}")
print("truth: labels are random, so real accuracy should be ~0.50")

▶ Output

leaky  CV accuracy (selection outside CV): 0.765
honest CV accuracy (selection in pipeline): 0.510
truth: labels are random, so real accuracy should be ~0.50

What happened here: The labels are coin flips, so no honest model can beat about 50%. Yet the leaky version scores 76.5%, a completely fake result, because it chose the 20 “best” features by peeking at all 200 samples including the ones each fold would later use for validation. Those features looked predictive only because they were cherry-picked on the same data being scored. The honest version puts feature selection inside the pipeline, so every fold reselects features from its training rows alone, and the score falls back to a truthful 51.0%. This is the single most common way a beginner reports an amazing model that collapses in production, and naming it calmly is a strong signal in a screen.

Models and Unsupervised Learning

Two mental models cover this group. For ensembles, imagine a panel of judges. Bagging (random forest) asks many independent judges and averages their votes, which cancels out individual mistakes and lowers variance. Boosting builds judges one after another, where each new judge focuses on the cases the panel got wrong so far, which lowers bias. For unsupervised learning, K-means sorts points into groups by nearest center, and Principal Component Analysis (PCA) rotates your data to find the few directions that carry most of the variation, so you can compress or visualize.

QuestionOne-line answerDeep dive
Bagging vs boosting?Bagging trains trees in parallel to cut variance; boosting builds them in sequence to cut bias.Random Forest
How does a tree split?Greedily on the feature that most reduces impurity (Gini or entropy).Decision Trees
Why does a forest use many trees?Averaging many decorrelated trees lowers variance without adding bias.Random Forest
The gradient boosting idea?Each new tree fits the errors the current ensemble still makes.Gradient Boosting
How does K-means work?Assign each point to its nearest centroid, move centroids to the mean, repeat.K-Means
How do you choose k?The elbow of the inertia curve, or the highest silhouette score.K-Means
What is PCA?A rotation to new axes ordered by variance, keeping only the top few.PCA
When would you use PCA?To compress correlated features or to visualize; not for interpretability.PCA

📄 models.py: bagging vs boosting side by side, then PCA variance

import numpy as np
from sklearn.ensemble import RandomForestClassifier, GradientBoostingClassifier
from sklearn.decomposition import PCA
from sklearn.datasets import load_wine
from sklearn.model_selection import cross_val_score
from sklearn.preprocessing import StandardScaler

X, y = load_wine(return_X_y=True)

rf = cross_val_score(RandomForestClassifier(random_state=0), X, y, cv=5).mean()
gb = cross_val_score(GradientBoostingClassifier(random_state=0), X, y, cv=5).mean()
print(f"random forest (bagging, parallel trees):   CV accuracy {rf:.3f}")
print(f"gradient boosting (sequential, fixes errors): CV accuracy {gb:.3f}")

Xs = StandardScaler().fit_transform(X)
pca = PCA().fit(Xs)
cum = np.cumsum(pca.explained_variance_ratio_)
k = int(np.argmax(cum >= 0.90) + 1)
print(f"PCA: first 2 components keep {cum[1]:.1%} of variance")
print(f"PCA: {k} of {X.shape[1]} components reach 90% variance")

▶ Output

random forest (bagging, parallel trees):   CV accuracy 0.983
gradient boosting (sequential, fixes errors): CV accuracy 0.939
PCA: first 2 components keep 55.4% of variance
PCA: 8 of 13 components reach 90% variance

What happened here: On this small wine dataset the random forest reaches 98.3% cross-validated accuracy and gradient boosting 93.9%, and the point is not that one always wins, it is that you can explain the difference: the forest averages many independent trees to fight variance, while boosting stacks trees in sequence, each correcting the last, to fight bias.

The PCA lines show the compression story: the first two components already hold 55.4% of the total variance, and you need 8 of the 13 original features to reach 90%, which tells you the wine features carry real, spread-out information rather than being mostly redundant. That single number, how many components reach 90%, is how you justify a dimensionality choice instead of guessing.

Score Yourself: The Revisit List

Add up your points across the five tables, one per question you could answer out loud without peeking, for a total out of 40. The band you land in tells you what to do next. With machine learning interview questions the honest goal is not a perfect score today, it is a shrinking revisit list over the next two weeks.

ScoreWhere you standDo next
33 to 40Screen-ready on ML theoryGo straight to the two coding drills below.
25 to 32Solid, a few soft spotsRe-read the deep-dive posts for your misses, then rescore.
Under 25Gaps to close firstBuild the revisit list below and work it before the drills.

Here is the trick that makes this efficient: every question in the five tables already names its deep-dive post in the last column, so your revisit list writes itself. For each question you missed, jot the topic and open the linked lesson, for example a shaky answer on leakage sends you to the ML Pipeline post, and a blank on Lasso sends you to Ridge and Lasso. Work only those posts, not the whole chapter, then come back and rescore in a week. Targeted review beats re-reading everything, and the delay is what moves a fact from “I recognize it” to “I can say it cold.”

Two From-Scratch Drills

The standard ML coding round asks you to write an algorithm in plain NumPy, no scikit-learn, to prove you understand the math and not just the import. Two come up constantly: K-means clustering and a logistic-regression gradient step. Write each yourself, then check it against the library to confirm you got it right. Both run on Python 3.14.6 exactly as shown.

First, K-means. The loop is two lines of idea: assign every point to its nearest centroid, then move each centroid to the average of its members, and repeat until nothing moves. We run it a few times from different random starts and keep the best, exactly what the library means by n_init, then compare inertia (the total squared distance to centroids) against scikit-learn.

📄 kmeans_from_scratch.py: assign, move, repeat, and match the library

import numpy as np
from sklearn.cluster import KMeans as SkKMeans
from sklearn.datasets import make_blobs

def one_run(X, k, rng, iters=100):
    centers = X[rng.choice(len(X), k, replace=False)].copy()
    for _ in range(iters):
        # assign each point to its nearest centroid
        dists = np.linalg.norm(X[:, None, :] - centers[None, :, :], axis=2)
        labels = dists.argmin(axis=1)
        new = centers.copy()
        for j in range(k):                 # move centroid to its members' mean
            if np.any(labels == j):
                new[j] = X[labels == j].mean(axis=0)
        if np.allclose(new, centers):
            break
        centers = new
    inertia = ((X - centers[labels]) ** 2).sum()
    return labels, centers, inertia

def kmeans(X, k, n_init=10, seed=0):
    rng = np.random.default_rng(seed)
    best = None
    for _ in range(n_init):                # keep the best of several restarts
        res = one_run(X, k, rng)
        if best is None or res[2] < best[2]:
            best = res
    return best

X, _ = make_blobs(n_samples=300, centers=4, cluster_std=0.6, random_state=42)
labels, centers, inertia = kmeans(X, k=4, seed=1)
print(f"from-scratch inertia: {inertia:.1f}")

sk = SkKMeans(n_clusters=4, n_init=10, random_state=42).fit(X)
print(f"sklearn inertia:      {sk.inertia_:.1f}")
print(f"within 2% of sklearn: {abs(inertia - sk.inertia_) / sk.inertia_ < 0.02}")

▶ Output

from-scratch inertia: 203.9
sklearn inertia:      203.9
within 2% of sklearn: True

What happened here: Our hand-written K-means landed on an inertia of 203.9, identical to scikit-learn’s 203.9, which is the confirmation you want: the algorithm is the same, the library just adds smarter initialization and a C-speed inner loop. The two details that separate a passing answer from a buggy one are both visible here. We guard against an empty cluster with the if np.any(labels == j) check so a centroid never divides by zero, and we run several random restarts and keep the lowest inertia, because a single run can settle into a bad local grouping. Talk through those two while you type them and you have shown real understanding.

Second, the logistic-regression gradient step. This is the heart of how most models actually learn: predict, measure the error, and nudge the weights in the direction that reduces it. The gradient for logistic regression is beautifully simple, it is the input features times the prediction error, averaged over the samples. We loop that step and then check our trained weights against scikit-learn on the same data.

📄 logreg_gradient.py: predict, error, nudge the weights, repeat

import numpy as np
from sklearn.linear_model import LogisticRegression

def sigmoid(z):
    return 1.0 / (1.0 + np.exp(-z))

def train(X, y, lr=0.3, epochs=8000):
    w = np.zeros(X.shape[1])
    b = 0.0
    n = len(y)
    for _ in range(epochs):
        p = sigmoid(X @ w + b)          # predicted probability
        error = p - y                    # the gradient's core term
        w -= lr * (X.T @ error) / n      # one gradient step on the weights
        b -= lr * error.mean()           # and on the bias
    return w, b

rng = np.random.default_rng(0)
X = rng.normal(size=(600, 3))
true_w = np.array([2.0, -1.0, 0.5])
# sample labels from the real probability, so the classes overlap (not separable)
y = (rng.random(600) < sigmoid(X @ true_w + 0.3)).astype(float)

w, b = train(X, y)

# turn sklearn's L2 almost off so both fit the same objective
sk = LogisticRegression(C=1e4, max_iter=8000).fit(X, y)

def cosine(a, b):
    return float(a @ b / (np.linalg.norm(a) * np.linalg.norm(b)))

np.set_printoptions(precision=2, suppress=True)
print("from-scratch weights:", w, "bias:", round(b, 2))
print("sklearn weights:     ", sk.coef_[0], "bias:", round(sk.intercept_[0], 2))
print(f"weight-direction agreement (cosine): {cosine(w, sk.coef_[0]):.4f}")

▶ Output

from-scratch weights: [ 1.94 -1.19  0.55] bias: 0.39
sklearn weights:      [ 1.94 -1.19  0.55] bias: 0.39
weight-direction agreement (cosine): 1.0000

What happened here: Our from-scratch weights came out as 1.94, -1.19, 0.55 with a bias of 0.39, and scikit-learn, solving the same problem with a far fancier optimizer, landed on exactly the same numbers, so the cosine agreement between the two weight vectors is 1.0000. That match is the proof the gradient is correct. The line to remember for the interview is error = p - y then w -= lr * (X.T @ error) / n: the update is just the features weighted by how wrong each prediction was, scaled by the learning rate.

Note we sampled labels from the true probability so the classes overlap; on perfectly separable data the weights would run off to infinity without any regularization, which is itself a good thing to mention.

Mini System-Design Warmup: A Churn Predictor

The last thing a screen sometimes throws in is an open question like “how would you build a model to predict which customers will cancel?” You are not expected to code a full system, you are expected to sketch the shape and name the traps. Say a subscription service wants to flag customers likely to churn next month so the retention team can call them. Here is the four-step sketch worth saying out loud, and it doubles as the on-ramp to the ML system-design chapter later in the series.

  • Frame it. Binary classification: will this customer cancel in the next 30 days, yes or no. The label comes from historical cancellations, and churn is usually imbalanced, so recall and ROC-AUC matter more than accuracy.
  • Features. Tenure, monthly charge, plan type, support tickets, recent usage trend. Mix numeric and categorical, and only use data known before the prediction date to avoid time-travel leakage.
  • Pipeline. Scale numbers, one-hot the categories, fit a model, all inside one Pipeline so cross-validation stays leak-free, exactly the discipline from the validation group.
  • Evaluate and act. Report ROC-AUC and recall on a time-based holdout, then set a threshold that matches how many calls the retention team can actually make.

That sketch is enough to say in an interview, but it is far more convincing if you can stand up the skeleton in a few lines. Here is that whole plan as a runnable pipeline on synthetic data, mixing numeric and categorical columns and scoring with cross-validated Receiver Operating Characteristic (ROC)-Area Under the Curve (AUC).

📄 churn_sketch.py: a leak-free mixed-type pipeline, scored by ROC-AUC

import numpy as np
import pandas as pd
from sklearn.compose import ColumnTransformer
from sklearn.preprocessing import StandardScaler, OneHotEncoder
from sklearn.pipeline import Pipeline
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import cross_val_score

rng = np.random.default_rng(5)
n = 1000
df = pd.DataFrame({
    "tenure_months": rng.integers(1, 72, n),
    "monthly_charge": rng.normal(70, 25, n).round(2),
    "plan": rng.choice(["basic", "plus", "pro"], n),
})
# higher churn for short tenure + costly plan (plus a little noise)
risk = (df["tenure_months"] < 12).astype(int) + (df["monthly_charge"] > 90).astype(int)
df["churned"] = ((risk + rng.normal(0, 0.5, n)) > 0.9).astype(int)

X = df.drop(columns="churned")
y = df["churned"]

num = ["tenure_months", "monthly_charge"]
cat = ["plan"]
pre = ColumnTransformer([
    ("num", StandardScaler(), num),
    ("cat", OneHotEncoder(), cat),
])
model = Pipeline([("prep", pre), ("clf", LogisticRegression(max_iter=1000))])

scores = cross_val_score(model, X, y, cv=5, scoring="roc_auc")
print(f"churn base rate: {y.mean():.1%}")
print(f"5-fold ROC-AUC:  {scores.mean():.3f} +/- {scores.std():.3f}")

▶ Output

churn base rate: 23.7%
5-fold ROC-AUC:  0.801 +/- 0.031

What happened here: The base rate shows 23.7% of customers churned, so accuracy alone would reward a lazy model that predicts “stays” for everyone, which is why we scored ROC-AUC instead. The ColumnTransformer scales the two numeric columns and one-hot encodes the plan, and wrapping it with the model in a single Pipeline means each of the five cross-validation folds fits its preprocessing on training rows only, so there is no leakage even though we standardized.

The result, an ROC-AUC of 0.801, says the model ranks a random churner above a random stayer about 80% of the time. That is a genuine end-to-end answer built from the exact pieces this checkpoint drilled, and it is the natural lead-in to the full ML system-design lesson.

Common Mistakes

❌ Mistake: Scaling the whole dataset before the train/test split

# Bad: the scaler sees test rows, so their statistics leak into training
scaler = StandardScaler().fit(X_all)      # fit on everything, then split
X_all = scaler.transform(X_all)
X_train, X_test = train_test_split(X_all)

# Good: fit preprocessing inside a pipeline so CV refits it per fold
pipe = make_pipeline(StandardScaler(), LogisticRegression())
scores = cross_val_score(pipe, X, y, cv=5)   # no test statistics leak in

Why: When you fit the scaler on all the data, its mean and standard deviation are computed partly from rows you later score on, so a whisper of the test set has already entered training. The scores look a little better than the truth, and worse, the habit hides a real bug in bigger pipelines. Putting every fit-based step inside a Pipeline means cross-validation refits it on each training fold alone, which is the leak-free default you should reach for every time.

❌ Mistake: Reporting accuracy on imbalanced data

# Wrong: "my fraud model is 98% accurate!"
#   If 98% of transactions are legitimate, predicting "legit" always scores 98%
#   and catches zero fraud.

# Right: report metrics that survive imbalance
#   precision, recall, F1, and ROC-AUC or PR-AUC, chosen by the cost of each error

Why: Accuracy counts every correct call equally, so on a rare-positive problem it is dominated by the easy majority and tells you almost nothing about the cases you care about. Name the base rate first, then reach for precision, recall, and F1, and decide which one to optimize from the real-world cost of a false positive versus a false negative. That framing, cost before metric, is what separates a memorized answer from an experienced one.

Best Practices

  • Answer in three sentences, then stop. Say what it is, why it matters, and one concrete example. Rambling past a correct answer usually walks you into a follow-up you did not need.
  • Name the trap before the tool. For any modeling question, mention leakage, imbalance, or overfitting first, then the fix. It reads as someone who has shipped models, not just trained them.
  • Show the math in NumPy when asked. Being able to write the K-means loop or the logistic gradient step proves you understand what the library does under the hood.
  • Turn every miss into a named revisit. Do not re-read the whole chapter; open only the deep-dive post your wrong answer points to, then rescore that question in a week.
  • Quote versions honestly. The scikit-learn API here was verified on version 1.9.0 at the time of writing; the theory is evergreen, but say “at the time of writing” when a method name might drift.

Wrapping Up

Machine learning interview questions stop being scary once you see the screen as a fixed checklist of forty theory fundamentals, grouped into bias and variance, regularization, metrics, validation and leakage, and models with clustering. You ran real code to settle the ones people get wrong: you watched the train-test gap open as a model overfit, watched Lasso zero out five noise features, watched a 96% accurate model catch zero real positives, watched leakage inflate a random-label score to 76%, and matched your own NumPy K-means and logistic gradient step to scikit-learn exactly.

These ideas are evergreen, they were true decades ago and they behave the same today, with the one honest caveat that specific library method names should be checked against your installed version, verified here on scikit-learn 1.9.0 at the time of writing. Score yourself, build the revisit list from your misses, work the two drills until the loops flow, and the ML screen becomes a formality instead of a fear.

This post is the checkpoint that closes the machine learning half of the journey and hands you off to the deep learning and AI chapters ahead. For the full roadmap, from beginner basics through the AI/ML deep dives, visit the Python + AI/ML tutorial series home.

Frequently Asked Questions

What machine learning interview questions are asked most often?

The most common machine learning interview questions cluster into five theory groups: bias and variance (over and underfitting), regularization (Ridge vs Lasso), metrics (precision, recall, F1, ROC-AUC and when accuracy lies), validation and leakage (cross-validation and pipelines), and models with unsupervised learning (bagging vs boosting, K-means, PCA). A screen usually pulls a handful from each group, which is why this checkpoint organizes forty of them the same way, each with a runnable proof and a link to the lesson that teaches it fully.

What is the difference between bias and variance?

High bias means the model is too simple and underfits, missing the real pattern and scoring poorly on both training and test data. High variance means the model is too flexible and overfits, memorizing training noise so it does great on training data but poorly on new data. The bias-variance tradeoff is that reducing one usually raises the other, and the goal is the sweet spot where training and validation error are both low and close together.

What is data leakage in machine learning?

Data leakage is when information from the test set influences training, so your reported score is far better than the model will do in production. Classic causes are scaling or selecting features on the whole dataset before splitting, or using a feature that would not be known at prediction time. The fix is to put every fit-based preprocessing step inside a scikit-learn Pipeline so cross-validation refits it on each training fold only.

Why is accuracy a bad metric for imbalanced data?

On imbalanced data, one class dominates, so a model that always predicts the majority class scores high accuracy while catching none of the rare cases you actually care about. A fraud model can be 98% accurate and detect zero fraud. Instead report precision, recall, and F1, plus ROC-AUC or PR-AUC, and choose which to optimize based on the real cost of a false positive versus a false negative.

How should I use this checkpoint to prepare?

Cover each answer, say it out loud in three sentences, and give yourself a point only if you could explain it to a teammate. Total your score out of 40, then for every miss open the deep-dive post named in that question’s row and re-read just that lesson. Rescore in a week so the ideas move from recognition to recall, and work the two from-scratch drills so your NumPy stays sharp for the coding round.

Interview Questions About the Interview

The same machine learning interview questions as they show up in a live conversation, framed as scenarios you can practice out loud.

Q: Your model gets 99% accuracy on a fraud dataset. Are you happy? Walk me through it.

My first reaction is suspicion, not celebration, because fraud is rare, so if 99% of transactions are legitimate, a model that predicts “legit” for everything already scores 99% and catches no fraud at all. I would look at the confusion matrix and report recall and precision on the fraud class, plus ROC-AUC or PR-AUC. If recall is near zero, the accuracy is meaningless, and I would rebalance with class weights or resampling and tune the threshold to the cost of a missed fraud versus a false alarm.

Q: A teammate scaled the features before splitting into train and test. What do you tell them?

That it is a data leak, gently but clearly. Fitting the scaler on the full dataset means its mean and standard deviation were computed partly from the test rows, so test information has quietly entered training and the reported score is optimistic. The fix is to fit the scaler on the training data only, and the clean way to guarantee that is to wrap scaling and the model in a single Pipeline, so cross-validation refits the scaler on each training fold by itself.

Q: Explain the difference between bagging and boosting to someone non-technical.

Bagging is asking many independent judges the same question and averaging their votes, so their individual mistakes cancel out and the group is steadier, that is a random forest and it mainly reduces variance. Boosting is a relay where each new judge studies the cases the previous ones got wrong and focuses there, so the team gradually fixes its blind spots, that is gradient boosting and it mainly reduces bias. Same idea of combining many small trees, opposite strategy for how they are built.

Q: You are asked to code K-means without any library. Where do you start, and what breaks?

I start with the two-step loop: assign each point to its nearest centroid, then move each centroid to the mean of the points assigned to it, and repeat until the centroids stop moving. Two things break if you are not careful. A cluster can end up empty, so dividing by its count is a crash, and you guard it by leaving that centroid in place or reseeding it. And a single random start can settle into a poor grouping, so you run it several times from different starts and keep the one with the lowest inertia, which is exactly what the library’s n_init parameter does.

Further reading: for the full reference, see the official Python documentation.

Previous: ML: End-to-End Project, House Price Prediction

Next: Google Colab Graphics Processing Unit (GPU) Setup: Where to Run Deep Learning for Free

Series Home: Python + AI/ML Tutorial Series

RahulAuthor posts

Avatar for Rahul

Rahul is a passionate IT professional who loves to sharing his knowledge with others and inspiring them to expand their technical knowledge. Rahul's current objective is to write informative and easy-to-understand articles to help people avoid day-to-day technical issues altogether. Follow Rahul's blog to stay informed on the latest trends in IT and gain insights into how to tackle complex technical issues. Whether you're a beginner or an expert in the field, Rahul's articles are sure to leave you feeling inspired and informed.

No comment

Leave a Reply

Your email address will not be published. Required fields are marked *