Skip to content

Practical Machine Learning Interview Questions

46 questions with answers. Hands-on questions where you write code, a query, a formula or concrete steps.

How to approach these questions

Talk while you work: say your approach first, write the simplest correct version, then improve it and mention edge cases.

Solved — practicalStart practicing
3 QUESTIONS FREESign up to unlock the full question bank and track your progress.Unlock every question and answer, then continue practising company-wise and package-wise.
3 questions free Unlock the rest in seconds
01

How do you set up a basic train-test split with scikit-learn?

Save to ▾
Show answer
Use train_test_split with a fixed random_state for reproducibility and stratify for classification so class ratios match. from sklearn.model_selection import train_test_split X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.2, random_state=42, stratify=y ) For time series, avoid random splits and use the most recent period as the test set.
02

Write a minimal scikit-learn workflow to train and evaluate a model.

Save to ▾
Show answer
The fit, predict, score pattern is the same for almost every estimator. from sklearn.linear_model import LogisticRegression from sklearn.metrics import accuracy_score model = LogisticRegression(max_iter=1000) model.fit(X_train, y_train) preds = model.predict(X_test) print(accuracy_score(y_test, preds)) Use predict_proba when you need probabilities for thresholds or ranking.
03

How would you build a quick baseline before trying complex models?

Save to ▾
Show answer
Use a DummyClassifier or DummyRegressor to see the score of a trivial guess, then a simple model. from sklearn.dummy import DummyClassifier base = DummyClassifier(strategy='most_frequent') base.fit(X_train, y_train) print(base.score(X_test, y_test)) Any real model must clearly beat this number; otherwise the features carry little signal.
04

How do you implement simple gradient descent for linear regression in NumPy?

Save to ▾
Show answer
Compute predictions, the gradient of MSE, and update weights in a loop. import numpy as np w = np.zeros(X.shape[1]); b = 0.0; lr = 0.01 for _ in range(1000): err = X @ w + b - y w -= lr * (2 / len(y)) * (X.T @ err) b -= lr * (2 / len(y)) * err.sum() Scale features first or the loop converges slowly.
05

How do you make an ML experiment reproducible?

Save to ▾
Show answer
Fix random seeds in NumPy, Python and the model, pin library versions in requirements.txt, version the data or record its hash, and save the exact code commit and hyperparameters. Tools like MLflow log parameters, metrics and artifacts per run. Use pipelines so preprocessing is identical each time, and keep the train-test split stored rather than recreated randomly.
06

How do you build a scikit-learn pipeline for mixed numeric and categorical data?

Save to ▾
Show answer
Use ColumnTransformer inside a Pipeline so preprocessing is fit only on training folds. from sklearn.compose import ColumnTransformer from sklearn.pipeline import Pipeline from sklearn.impute import SimpleImputer from sklearn.preprocessing import StandardScaler, OneHotEncoder num = Pipeline([('imp', SimpleImputer(strategy='median')), ('sc', StandardScaler())]) pre = ColumnTransformer([ ('num', num, num_cols), ('cat', OneHotEncoder(handle_unknown='ignore'), cat_cols) ]) model = Pipeline([('pre', pre), ('clf', LogisticRegression(max_iter=1000))])
07

How do you extract useful features from a datetime column in pandas?

Save to ▾
Show answer
Convert to datetime and pull out calendar parts. import pandas as pd df['ts'] = pd.to_datetime(df['ts']) df['hour'] = df['ts'].dt.hour df['dow'] = df['ts'].dt.dayofweek df['month'] = df['ts'].dt.month df['is_weekend'] = df['dow'].isin([5, 6]).astype(int) For cyclical features like hour, sine and cosine encoding keeps 23 and 0 close together.
08

How do you find and remove duplicate rows and check missing values in pandas?

Save to ▾
Show answer
Quick data-quality checks before modelling: print(df.isna().sum().sort_values(ascending=False)) print(df.duplicated().sum()) df = df.drop_duplicates() df['age'] = df['age'].fillna(df['age'].median()) For modelling, do the median imputation inside a pipeline instead so the value is learned only from training data.
09

How do you calculate VIF for features in Python?

Save to ▾
Show answer
Use statsmodels on the numeric feature matrix with a constant added. import pandas as pd from statsmodels.stats.outliers_influence import variance_inflation_factor from statsmodels.tools import add_constant Xc = add_constant(X_num) vif = pd.Series([variance_inflation_factor(Xc.values, i) for i in range(Xc.shape[1])], index=Xc.columns) print(vif.drop('const')) Values above 5 to 10 indicate strong multicollinearity.
10

How do you select the top k features using mutual information in scikit-learn?

Save to ▾
Show answer
SelectKBest with mutual_info_classif captures non-linear relationships, unlike plain correlation. from sklearn.feature_selection import SelectKBest, mutual_info_classif sel = SelectKBest(score_func=mutual_info_classif, k=20) X_new = sel.fit_transform(X_train, y_train) print(X_train.columns[sel.get_support()]) Put the selector inside a pipeline so selection is redone in each CV fold.
11

Fit a linear regression in scikit-learn and print RMSE and R-squared.

Save to ▾
Show answer
from sklearn.linear_model import LinearRegression from sklearn.metrics import mean_squared_error, r2_score import numpy as np lr = LinearRegression().fit(X_train, y_train) pred = lr.predict(X_test) print('RMSE', np.sqrt(mean_squared_error(y_test, pred))) print('R2', r2_score(y_test, pred)) print(dict(zip(X_train.columns, lr.coef_))) Newer scikit-learn versions also provide root_mean_squared_error directly.
12

How do you tune the alpha of Ridge regression using cross-validation?

Save to ▾
Show answer
RidgeCV tests many alphas efficiently; scale features first. from sklearn.linear_model import RidgeCV from sklearn.pipeline import make_pipeline from sklearn.preprocessing import StandardScaler import numpy as np m = make_pipeline(StandardScaler(), RidgeCV(alphas=np.logspace(-3, 3, 30))) m.fit(X_train, y_train) print(m[-1].alpha_) LassoCV and ElasticNetCV work the same way.
13

How do you create polynomial features with regularisation in a pipeline?

Save to ▾
Show answer
from sklearn.preprocessing import PolynomialFeatures, StandardScaler from sklearn.linear_model import Ridge from sklearn.pipeline import make_pipeline model = make_pipeline( PolynomialFeatures(degree=2, include_bias=False), StandardScaler(), Ridge(alpha=1.0) ) model.fit(X_train, y_train) Tune degree and alpha together with GridSearchCV to avoid overfitting.
14

How do you create lag and rolling features for a sales forecasting model in pandas?

Save to ▾
Show answer
Sort by item and date, then shift so features use only past values. df = df.sort_values(['item', 'date']) g = df.groupby('item')['sales'] df['lag_7'] = g.shift(7) df['roll_mean_28'] = g.transform(lambda s: s.shift(1).rolling(28).mean()) The shift before rolling prevents the current day's sales from leaking into its own feature.
15

How do you check regression residuals visually in Python?

Save to ▾
Show answer
import matplotlib.pyplot as plt res = y_test - pred fig, ax = plt.subplots(1, 2, figsize=(10, 4)) ax[0].scatter(pred, res, s=8); ax[0].axhline(0, color='red') ax[0].set_xlabel('Predicted'); ax[0].set_ylabel('Residual') ax[1].hist(res, bins=40) plt.show() A funnel shape suggests heteroscedasticity; a curve suggests a missing non-linear term.
16

Use statsmodels to get p-values for regression coefficients.

Save to ▾
Show answer
import statsmodels.api as sm Xc = sm.add_constant(X_train) ols = sm.OLS(y_train, Xc).fit() print(ols.summary()) The summary shows coefficients, standard errors, p-values, confidence intervals, R-squared and diagnostics. scikit-learn focuses on prediction and does not report p-values, so statsmodels is preferred for inference.
17

Print a confusion matrix and classification report in scikit-learn.

Save to ▾
Show answer
from sklearn.metrics import confusion_matrix, classification_report pred = clf.predict(X_test) print(confusion_matrix(y_test, pred)) print(classification_report(y_test, pred, digits=3)) The report shows precision, recall, F1 and support per class plus macro and weighted averages. ConfusionMatrixDisplay.from_estimator draws it as a plot.
18

How do you choose a classification threshold that maximises F1?

Save to ▾
Show answer
from sklearn.metrics import precision_recall_curve import numpy as np proba = clf.predict_proba(X_val)[:, 1] p, r, t = precision_recall_curve(y_val, proba) f1 = 2 * p * r / (p + r + 1e-9) best = t[np.argmax(f1[:-1])] print(best) Choose it on validation data, not the test set. scikit-learn 1.5+ also offers TunedThresholdClassifierCV.
19

How do you apply SMOTE correctly without leakage?

Save to ▾
Show answer
Use an imbalanced-learn pipeline so oversampling happens only on training folds. from imblearn.pipeline import Pipeline from imblearn.over_sampling import SMOTE from sklearn.model_selection import cross_val_score pipe = Pipeline([('pre', pre), ('smote', SMOTE(random_state=42)), ('clf', LogisticRegression(max_iter=1000))]) print(cross_val_score(pipe, X, y, cv=5, scoring='average_precision').mean()) Applying SMOTE before splitting puts synthetic copies of test points into training.
20

How do you build a text classifier with TF-IDF and logistic regression?

Save to ▾
Show answer
from sklearn.feature_extraction.text import TfidfVectorizer from sklearn.linear_model import LogisticRegression from sklearn.pipeline import make_pipeline clf = make_pipeline( TfidfVectorizer(ngram_range=(1, 2), min_df=2, sublinear_tf=True), LogisticRegression(max_iter=1000, class_weight='balanced') ) clf.fit(train_texts, train_labels) print(clf.score(test_texts, test_labels))
21

How do you plot an ROC curve and get AUC in scikit-learn?

Save to ▾
Show answer
from sklearn.metrics import roc_auc_score, RocCurveDisplay import matplotlib.pyplot as plt proba = clf.predict_proba(X_test)[:, 1] print('AUC', roc_auc_score(y_test, proba)) RocCurveDisplay.from_predictions(y_test, proba) plt.show() Pass probabilities or decision scores, not hard labels, otherwise the curve has only one point.
22

Train a random forest and show feature importances in scikit-learn.

Save to ▾
Show answer
from sklearn.ensemble import RandomForestClassifier import pandas as pd rf = RandomForestClassifier(n_estimators=300, min_samples_leaf=2, n_jobs=-1, random_state=42, oob_score=True) rf.fit(X_train, y_train) print('OOB', rf.oob_score_) imp = pd.Series(rf.feature_importances_, index=X_train.columns) print(imp.sort_values(ascending=False).head(10))
23

Train LightGBM with early stopping.

Save to ▾
Show answer
import lightgbm as lgb m = lgb.LGBMClassifier(n_estimators=5000, learning_rate=0.05, num_leaves=31, subsample=0.8, subsample_freq=1, colsample_bytree=0.8) m.fit(X_train, y_train, eval_set=[(X_val, y_val)], eval_metric='auc', callbacks=[lgb.early_stopping(100)]) print(m.best_iteration_) The large n_estimators is only an upper limit; early stopping picks the real number.
24

Compute permutation importance on validation data.

Save to ▾
Show answer
from sklearn.inspection import permutation_importance import pandas as pd r = permutation_importance(model, X_val, y_val, n_repeats=10, scoring='roc_auc', random_state=42, n_jobs=-1) imp = pd.Series(r.importances_mean, index=X_val.columns) print(imp.sort_values(ascending=False)) Near-zero or negative values mean the feature adds little on unseen data.
25

Explain a single prediction with SHAP for a tree model.

Save to ▾
Show answer
import shap explainer = shap.TreeExplainer(model) sv = explainer(X_val) shap.plots.waterfall(sv[0]) shap.plots.beeswarm(sv) The waterfall shows how each feature pushes one prediction up or down from the base value; the beeswarm shows global importance and direction. TreeExplainer is fast and exact for tree ensembles.
26

Visualise a small decision tree and its rules.

Save to ▾
Show answer
from sklearn.tree import DecisionTreeClassifier, export_text, plot_tree import matplotlib.pyplot as plt dt = DecisionTreeClassifier(max_depth=3, random_state=0).fit(X_train, y_train) print(export_text(dt, feature_names=list(X_train.columns))) plot_tree(dt, feature_names=X_train.columns, filled=True) plt.show() Shallow trees are a good way to explain rules to business teams.
27

Run k-means and plot the elbow and silhouette scores.

Save to ▾
Show answer
from sklearn.cluster import KMeans from sklearn.metrics import silhouette_score Xs = StandardScaler().fit_transform(X) for k in range(2, 10): km = KMeans(n_clusters=k, n_init=10, random_state=42).fit(Xs) print(k, round(km.inertia_), round(silhouette_score(Xs, km.labels_), 3)) Look for the bend in inertia and a high silhouette value.
28

Apply PCA and check explained variance.

Save to ▾
Show answer
from sklearn.decomposition import PCA import numpy as np pca = PCA().fit(Xs) cum = np.cumsum(pca.explained_variance_ratio_) n = np.argmax(cum >= 0.95) + 1 print(n, 'components explain 95 percent') X_red = PCA(n_components=0.95).fit_transform(Xs) Passing a float to n_components keeps enough components to reach that variance.
29

Detect anomalies with Isolation Forest.

Save to ▾
Show answer
from sklearn.ensemble import IsolationForest iso = IsolationForest(n_estimators=300, contamination=0.01, random_state=42) iso.fit(X_train) scores = -iso.score_samples(X_new) flags = iso.predict(X_new) == -1 Higher scores mean more anomalous. Tune contamination to match how many alerts reviewers can handle.
30

Build RFM features for customer segmentation in pandas.

Save to ▾
Show answer
snap = df['order_date'].max() + pd.Timedelta(days=1) rfm = df.groupby('customer_id').agg( recency=('order_date', lambda d: (snap - d.max()).days), frequency=('order_id', 'nunique'), monetary=('amount', 'sum')) rfm[['frequency', 'monetary']] = np.log1p(rfm[['frequency', 'monetary']]) Scale these columns before clustering.
31

Run DBSCAN and count clusters and noise points.

Save to ▾
Show answer
from sklearn.cluster import DBSCAN import numpy as np db = DBSCAN(eps=0.5, min_samples=10).fit(Xs) labels = db.labels_ n_clusters = len(set(labels)) - (1 if -1 in labels else 0) print(n_clusters, 'clusters,', np.sum(labels == -1), 'noise') Pick eps from the knee of a sorted k-nearest-neighbour distance plot.
32

How would you encode cyclical features like hour of day or month?

Save to ▾
Show answer
Map the value onto a circle with sine and cosine so the end connects to the start. import numpy as np df['hour_sin'] = np.sin(2 * np.pi * df['hour'] / 24) df['hour_cos'] = np.cos(2 * np.pi * df['hour'] / 24) Now 23:00 and 00:00 are close, which linear and distance-based models cannot see with raw integers. Tree models often manage with the raw value.
33

Run stratified 5-fold cross-validation with multiple metrics.

Save to ▾
Show answer
from sklearn.model_selection import StratifiedKFold, cross_validate cv = StratifiedKFold(n_splits=5, shuffle=True, random_state=42) res = cross_validate(pipe, X, y, cv=cv, scoring=['roc_auc', 'average_precision', 'f1'], n_jobs=-1) for k in ['test_roc_auc', 'test_average_precision', 'test_f1']: print(k, res[k].mean().round(3), res[k].std().round(3))
34

Tune a pipeline with GridSearchCV.

Save to ▾
Show answer
from sklearn.model_selection import GridSearchCV params = {'clf__C': [0.01, 0.1, 1, 10], 'clf__penalty': ['l2']} gs = GridSearchCV(pipe, params, cv=5, scoring='roc_auc', n_jobs=-1) gs.fit(X_train, y_train) print(gs.best_params_, gs.best_score_) best = gs.best_estimator_ The step__param naming targets parameters inside a pipeline step.
35

Tune LightGBM with Optuna.

Save to ▾
Show answer
import optuna from sklearn.model_selection import cross_val_score def objective(trial): p = {'num_leaves': trial.suggest_int('num_leaves', 15, 255), 'learning_rate': trial.suggest_float('learning_rate', 0.01, 0.2, log=True), 'min_child_samples': trial.suggest_int('min_child_samples', 10, 200)} m = lgb.LGBMClassifier(n_estimators=500, **p) return cross_val_score(m, X, y, cv=5, scoring='roc_auc').mean() study = optuna.create_study(direction='maximize') study.optimize(objective, n_trials=50)
36

Plot a learning curve in scikit-learn.

Save to ▾
Show answer
from sklearn.model_selection import LearningCurveDisplay import numpy as np LearningCurveDisplay.from_estimator(model, X, y, cv=5, train_sizes=np.linspace(0.1, 1.0, 6), scoring='roc_auc', n_jobs=-1) plt.show() A persistent gap between curves suggests more data or regularisation; two low curves suggest a richer model or better features.
37

Get a bootstrap confidence interval for a test metric.

Save to ▾
Show answer
import numpy as np from sklearn.metrics import roc_auc_score rng = np.random.default_rng(0); scores = [] yt, pp = np.array(y_test), np.array(proba) for _ in range(1000): i = rng.integers(0, len(yt), len(yt)) if len(np.unique(yt[i])) == 2: scores.append(roc_auc_score(yt[i], pp[i])) print(np.percentile(scores, [2.5, 97.5]))
38

Check calibration with a reliability curve and Brier score.

Save to ▾
Show answer
from sklearn.calibration import CalibrationDisplay from sklearn.metrics import brier_score_loss proba = clf.predict_proba(X_test)[:, 1] print('Brier', brier_score_loss(y_test, proba)) CalibrationDisplay.from_predictions(y_test, proba, n_bins=10) plt.show() Points on the diagonal mean predicted probabilities match observed frequencies.
39

How do you save and load a scikit-learn model?

Save to ▾
Show answer
Save the whole fitted pipeline so preprocessing travels with the model. import joblib joblib.dump(pipe, 'model_v1.joblib') pipe = joblib.load('model_v1.joblib') preds = pipe.predict(new_df) Load with the same scikit-learn version used for training, and never load pickle or joblib files from untrusted sources because they can execute code. ONNX or skops are safer portable options.
40

Serve a scikit-learn model with FastAPI.

Save to ▾
Show answer
from fastapi import FastAPI from pydantic import BaseModel import joblib, pandas as pd app = FastAPI() model = joblib.load('model_v1.joblib') class Item(BaseModel): age: int income: float city: str @app.post('/predict') def predict(item: Item): df = pd.DataFrame([item.model_dump()]) return {'score': float(model.predict_proba(df)[0, 1])} Run it with: uvicorn app:app --host 0.0.0.0 --port 8000
41

Write a simple Dockerfile for an ML API.

Save to ▾
Show answer
FROM python:3.12-slim WORKDIR /app COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt COPY app.py model_v1.joblib ./ EXPOSE 8000 CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "8000"] The exec-form CMD avoids a shell wrapper. Pin versions in requirements.txt so training and serving match.
42

Log a training run to MLflow.

Save to ▾
Show answer
import mlflow, mlflow.sklearn mlflow.set_experiment('churn') with mlflow.start_run(): pipe.fit(X_train, y_train) auc = roc_auc_score(y_val, pipe.predict_proba(X_val)[:, 1]) mlflow.log_params({'C': 1.0, 'model': 'logreg'}) mlflow.log_metric('val_auc', auc) mlflow.sklearn.log_model(pipe, 'model', registered_model_name='churn-model')
43

Calculate the Population Stability Index for a feature.

Save to ▾
Show answer
import numpy as np def psi(expected, actual, bins=10): cuts = np.quantile(expected, np.linspace(0, 1, bins + 1)) cuts[0], cuts[-1] = -np.inf, np.inf e = np.histogram(expected, cuts)[0] / len(expected) + 1e-6 a = np.histogram(actual, cuts)[0] / len(actual) + 1e-6 return float(np.sum((a - e) * np.log(a / e))) Rule of thumb: below 0.1 stable, 0.1 to 0.25 moderate, above 0.25 significant drift.
44

Write a unit test for a model prediction function.

Save to ▾
Show answer
import joblib, pandas as pd def test_predict_shape_and_range(): model = joblib.load('model_v1.joblib') df = pd.DataFrame([{'age': 30, 'income': 50000.0, 'city': 'Pune'}, {'age': 45, 'income': 0.0, 'city': 'UnknownCity'}]) p = model.predict_proba(df)[:, 1] assert p.shape == (2,) assert ((p >= 0) & (p <= 1)).all() Run with pytest in CI so broken artifacts never reach production.
45

Use HistGradientBoosting with native categorical support in scikit-learn.

Save to ▾
Show answer
from sklearn.ensemble import HistGradientBoostingClassifier clf = HistGradientBoostingClassifier( categorical_features='from_dtype', max_iter=500, learning_rate=0.05, early_stopping=True, random_state=42) X_train[cat_cols] = X_train[cat_cols].astype('category') clf.fit(X_train, y_train) It handles missing values natively and is much faster than GradientBoostingClassifier on large data.
46

Find the most similar items using cosine similarity.

Save to ▾
Show answer
from sklearn.feature_extraction.text import TfidfVectorizer from sklearn.neighbors import NearestNeighbors X = TfidfVectorizer(min_df=2).fit_transform(df['title']) nn = NearestNeighbors(n_neighbors=6, metric='cosine').fit(X) dist, idx = nn.kneighbors(X[0]) print(df['title'].iloc[idx[0][1:]].tolist()) The first neighbour is the item itself, so skip it.

Frequently asked questions

Why are practical questions important?

Most technical rounds include at least one live task. Practising the exact typing and reasoning makes you calm and fast in the real round.

How should I answer them?

Talk while you work: say your approach first, write the simplest correct version, then improve it and mention edge cases.