Machine Learning cheat sheet

A scannable Machine Learning reference: 28 short snippets across 13 topics, each linking back to the lesson it came from.

At a glance

TopicWhat it covers
Machine learning in one pageMost business problems are supervised. Decide early whether you are predicting a category (classification) or a numberlesson
Evaluation and overfittingA high mean with a large standard deviation means the result depends on which fold you got — the model is not stablelesson
Pipelines and saving modelsIf you scale or impute before splitting, statistics from the test set sneak into training and your score is optimisticlesson
Data collection, cleaning and feature engineeringA model is a summary of the rows you fed it. If those rows are a biased sample — only surviving customers, onlylesson
Splitting data correctlyWith little data, skip the fixed validation set and use cross-validation on the development split. What you must not dolesson
Supervised algorithms in practiceA linear model assumes the outcome is a weighted sum of the features — or, for logistic regression, that the log-oddslesson
Unsupervised learningThere is no ground-truth label to score against, so validate a clustering by whether the segments differ on somethinglesson
Classification metrics in depthReport the matrix and the two error counts, not just a scalar. Stakeholders can argue about how many false positiveslesson
Regression metrics and residual analysisQuote the error in the units of the problem, next to the mean target value. 'RMSE 412' means nothing until the readerlesson
Imbalanced dataThe class balance is a property of the problem, not a defect in the data. Removing negative rows to 'fix' it throwslesson
Hyperparameter tuning and experiment trackingTune two or three parameters, not everything. Each extra dimension multiplies the fits, increases the chance oflesson
Interpretability, bias and fairnessPermutation importance, partial dependence, SHAP, subgroup metrics, where bias enters, and documenting what a modellesson
Deploying and monitoring a modelStore the model version with every prediction. Without it you cannot explain an old decision, reproduce a complaint, orlesson

Quick snippets

Machine learning in one page

Features beat algorithms

import pandas as pd
from sklearn.model_selection import train_test_split

df = pd.read_csv("churn.csv")
X = df.drop(columns=["churned", "customer_id"])   # id is not a feature
y = df["churned"]

X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.2, random_state=42, stratify=y)   # stratify keeps class balance

Start with a baseline you must beat

from sklearn.dummy import DummyClassifier
from sklearn.metrics import accuracy_score

base = DummyClassifier(strategy="most_frequent").fit(X_train, y_train)
accuracy_score(y_test, base.predict(X_test))     # the number to beat

Full lesson: Machine learning in one page →

Evaluation and overfitting

Overfitting, underfitting, just right

from sklearn.model_selection import cross_val_score
from sklearn.ensemble import GradientBoostingClassifier

scores = cross_val_score(
    GradientBoostingClassifier(), X_train, y_train,
    cv=5, scoring="roc_auc")
print(scores.mean(), scores.std())     # report the spread, not just the mean

Metrics that match the cost

from sklearn.metrics import precision_recall_curve

prec, rec, thresholds = precision_recall_curve(y_test, proba)
# choose the operating point from the business cost, then hard-code it
best = thresholds[(prec > 0.9)].min()

Full lesson: Evaluation and overfitting →

Pipelines and saving models

Tuning without fooling yourself

from sklearn.model_selection import GridSearchCV

grid = {"clf__C": [0.01, 0.1, 1, 10],
        "clf__class_weight": [None, "balanced"]}

search = GridSearchCV(model, grid, cv=5, scoring="roc_auc", n_jobs=-1)
search.fit(X_train, y_train)

print(search.best_params_, search.best_score_)

Saving the whole pipeline

import joblib

joblib.dump(search.best_estimator_, "churn_v3.joblib")

# later, in the service
model = joblib.load("churn_v3.joblib")
model.predict(new_rows)      # preprocessing travels with the model

Full lesson: Pipelines and saving models →

Data collection, cleaning and feature engineering

Collect before you model

import pandas as pd

df = pd.read_csv("events.csv", parse_dates=["created_at"])
df = df.drop_duplicates(subset=["event_id"])
df = df[df["created_at"] < CUTOFF]          # never train on rows newer than the label

print(df.shape)
print(df.dtypes.to_string())
print(df.isna().mean().sort_values(ascending=False).head(10))
print(df["label"].value_counts(normalize=True))

From columns to features

df["hour"] = df["created_at"].dt.hour
df["dow"] = df["created_at"].dt.dayofweek
df["days_since_signup"] = (df["created_at"] - df["signup_at"]).dt.days
df["amount_log"] = np.log1p(df["amount"])
df["orders_per_day"] = df["orders"] / df["tenure_days"].clip(lower=1)

# frequency encoding is safe: it uses no label
freq = df["city"].value_counts(normalize=True)
df["city_freq"] = df["city"].map(freq).fillna(0.0)

Full lesson: Data collection, cleaning and feature engineering →

Splitting data correctly

Three sets, three jobs

from sklearn.model_selection import train_test_split

# hold out the test set first and lock it away
X_dev, X_test, y_dev, y_test = train_test_split(
    X, y, test_size=0.2, random_state=42, stratify=y)

# then split development data into train and validation inside every experiment
X_train, X_val, y_train, y_val = train_test_split(
    X_dev, y_dev, test_size=0.25, random_state=42, stratify=y_dev)

print(X_train.shape, X_val.shape, X_test.shape)

Split by structure, not by row

from sklearn.model_selection import GroupKFold, StratifiedKFold, TimeSeriesSplit

# several rows per customer: no customer may appear on both sides
for tr, va in GroupKFold(n_splits=5).split(X, y, groups=df["customer_id"]):
    ...

# future-only training, for anything with a time signal
for tr, va in TimeSeriesSplit(n_splits=5, gap=1).split(X):
    ...

# keep the class ratio stable in every fold
StratifiedKFold(n_splits=5, shuffle=True, random_state=42)

Nested cross-validation

from sklearn.model_selection import GridSearchCV, cross_val_score

inner = GridSearchCV(model, {"clf__C": [0.1, 1, 10]}, cv=3, scoring="roc_auc")
outer_scores = cross_val_score(inner, X_dev, y_dev, cv=5, scoring="roc_auc")

print(outer_scores.mean(), outer_scores.std())   # the number you may quote

Full lesson: Splitting data correctly →

Supervised algorithms in practice

Start linear

from sklearn.linear_model import LinearRegression, LogisticRegression, Ridge

lin = LinearRegression().fit(X_train_scaled, y_train)
log = LogisticRegression(max_iter=1000).fit(X_train_scaled, y_train_cls)
ridge = Ridge(alpha=1.0).fit(X_train_scaled, y_train)

log.predict_proba(X_test_scaled)[:, 1]     # probabilities
log.coef_                                  # effect per feature, given the scaling
log.classes_                               # column order for predict_proba

What each family assumes

from sklearn.neighbors import KNeighborsClassifier
from sklearn.svm import SVC

knn = KNeighborsClassifier(n_neighbors=15, weights="distance")
svc = SVC(C=1.0, gamma="scale", probability=False)

# both are distance-based: scale first, and tune k or C on the validation folds
knn.fit(X_train_scaled, y_train).score(X_test_scaled, y_test)
svc.fit(X_train_scaled, y_train).score(X_test_scaled, y_test)

Full lesson: Supervised algorithms in practice →

Unsupervised learning

Reducing dimensions

from sklearn.decomposition import PCA, TruncatedSVD
from sklearn.manifold import TSNE

pca = PCA(n_components=0.95, random_state=42).fit(X_scaled)   # keep 95% of variance
print(pca.n_components_, pca.explained_variance_ratio_.sum())
X_pca = pca.transform(X_scaled)

# SVD works on sparse text matrices where PCA would densify them
svd = TruncatedSVD(n_components=100, random_state=42)

# t-SNE is for looking at data, not for feeding a model
emb = TSNE(n_components=2, perplexity=30, random_state=42).fit_transform(X_scaled)

Anomaly detection

from sklearn.ensemble import IsolationForest
from sklearn.neighbors import LocalOutlierFactor

iso = IsolationForest(contamination=0.02, random_state=42).fit(X_scaled)
print(iso.predict(X_scaled)[:10])            # -1 = flagged as anomalous

lof = LocalOutlierFactor(n_neighbors=20, contamination=0.02)
lof.fit_predict(X_scaled)                    # local density relative to neighbours

Full lesson: Unsupervised learning →

Classification metrics in depth

Everything starts at the confusion matrix

from sklearn.metrics import classification_report, confusion_matrix

cm = confusion_matrix(y_test, y_pred)
tn, fp, fn, tp = cm.ravel()

precision = tp / (tp + fp)      # of the alarms raised, how many were real
recall = tp / (tp + fn)         # of the real cases, how many were caught
fpr = fp / (fp + tn)            # of the clean cases, how many were wrongly flagged
print(cm, precision, recall, fpr)
print(classification_report(y_test, y_pred, digits=3))

ROC-AUC versus precision-recall

from sklearn.metrics import (average_precision_score,
                             precision_recall_curve, roc_auc_score, roc_curve)

prob = clf.predict_proba(X_test)[:, 1]
print("roc auc", roc_auc_score(y_test, prob))
print("average precision", average_precision_score(y_test, prob))

fpr, tpr, thr = roc_curve(y_test, prob)
prec, rec, thr_pr = precision_recall_curve(y_test, prob)

Thresholds and calibration

import numpy as np
from sklearn.calibration import CalibratedClassifierCV

# pick the threshold from the cost of each error type
cost_fp, cost_fn = 5, 50
expected = cost_fp * (1 - prob) + cost_fn * prob
threshold = np.quantile(prob, 0.10)          # flag the riskiest 10%

# are the probabilities themselves trustworthy?
cal = CalibratedClassifierCV(clf, method="isotonic", cv=5).fit(X_train, y_train)
prob_cal = cal.predict_proba(X_test)[:, 1]
print(prob_cal.mean(), y_test.mean())        # predicted rate vs observed rate

Full lesson: Classification metrics in depth →

Regression metrics and residual analysis

Pick the error you actually pay

import numpy as np
from sklearn.metrics import (mean_absolute_error, mean_squared_error,
                             r2_score, root_mean_squared_error)

mae = mean_absolute_error(y_test, pred)
rmse = root_mean_squared_error(y_test, pred)
r2 = r2_score(y_test, pred)

print(mae, rmse, r2)
print("typical error", f"{mae / np.mean(y_test):.1%}")   # relative to the mean target

Residuals tell you what the score cannot

resid = y_test - pred

# structure in the residuals means the model is missing something
df = pd.DataFrame({"pred": pred, "resid": resid, "segment": segments})
print(df.groupby("segment")["resid"].agg(["mean", "std", "count"]))

# a funnel shape means the error grows with the prediction
print(df.assign(bucket=pd.qcut(df["pred"], 5)).groupby("bucket")["resid"].mean())

# the biggest misses are worth reading one by one
print(df.reindex(resid.abs().sort_values(ascending=False).index).head(10))

When the cost is asymmetric

from sklearn.ensemble import GradientBoostingRegressor

# under-stocking is worse than over-stocking: model a high quantile
high = GradientBoostingRegressor(loss="quantile", alpha=0.9, random_state=42)
high.fit(X_train, y_train)

lower = GradientBoostingRegressor(loss="quantile", alpha=0.1, random_state=42)
lower.fit(X_train, y_train)

band = high.predict(X_test) - lower.predict(X_test)   # prediction interval width

Full lesson: Regression metrics and residual analysis →

Imbalanced data

Why accuracy lies

import numpy as np
from sklearn.dummy import DummyClassifier
from sklearn.metrics import accuracy_score, average_precision_score

print(np.bincount(y))                       # e.g. [9900, 100]

base = DummyClassifier(strategy="most_frequent").fit(X_train, y_train)
print("accuracy", accuracy_score(y_test, base.predict(X_test)))    # 0.99
print("average precision", average_precision_score(y_test, np.zeros_like(y_test)))

Choose the operating point

from sklearn.metrics import precision_recall_curve

prob = pipe.predict_proba(X_test)[:, 1]
prec, rec, thr = precision_recall_curve(y_test, prob)

# if the team can review 200 cases a week, take the top 200 by probability
k = 200
order = np.argsort(-prob)[:k]
print("precision at k", y_test.values[order].mean())
print("recall at k", y_test.values[order].sum() / y_test.sum())

Full lesson: Imbalanced data →

Hyperparameter tuning and experiment tracking

Spend the budget wisely

from sklearn.ensemble import HistGradientBoostingClassifier

boost = HistGradientBoostingClassifier(
    learning_rate=0.05,
    max_iter=1000,
    early_stopping=True,          # stop when validation loss stops improving
    validation_fraction=0.15,
    n_iter_no_change=25,
    random_state=42).fit(X_train, y_train)

print(boost.n_iter_)              # rounds actually used

Full lesson: Hyperparameter tuning and experiment tracking →

Interpretability, bias and fairness

Global and local explanations

from sklearn.inspection import permutation_importance

perm = permutation_importance(
    boost, X_val, y_val, scoring="average_precision",
    n_repeats=10, random_state=42, n_jobs=-1)

order = np.argsort(-perm.importances_mean)
for i in order[:8]:
    print(X_val.columns[i], round(perm.importances_mean[i], 4),
          round(perm.importances_std[i], 4))

Full lesson: Interpretability, bias and fairness →

Deploying and monitoring a model

Batch or service

import joblib
import pandas as pd

model = joblib.load("churn_v3.joblib")

def score_batch(path_in="scores_in.parquet", path_out="scores_out.parquet"):
    rows = pd.read_parquet(path_in)
    rows["churn_prob"] = model.predict_proba(rows)[:, 1]
    rows["scored_at"] = pd.Timestamp.utcnow()
    rows["model_version"] = "churn_v3"
    rows.to_parquet(path_out, index=False)
    return len(rows)

Training and serving must agree

# a single feature path shared by training, backfill and the service
def build_features(raw: pd.DataFrame) -> pd.DataFrame:
    out = raw.copy()
    out["amount_log"] = np.log1p(out["amount"])
    out["days_since_signup"] = (out["event_at"] - out["signup_at"]).dt.days
    return out[FEATURE_ORDER]        # column order and names fixed in one place

assert list(build_features(sample).columns) == FEATURE_ORDER

Drift, retraining and rollback

import numpy as np

def psi(expected, actual, bins=10):
    """Population stability index between a training column and today's data."""
    edges = np.quantile(expected, np.linspace(0, 1, bins + 1))
    e = np.histogram(expected, bins=edges)[0] / len(expected)
    a = np.histogram(actual, bins=edges)[0] / len(actual)
    e, a = np.clip(e, 1e-6, None), np.clip(a, 1e-6, None)
    return float(np.sum((a - e) * np.log(a / e)))

# psi under 0.1: stable, 0.1-0.25: watch, above 0.25: investigate
print({c: round(psi(train[c], today[c]), 3) for c in FEATURE_ORDER})

Full lesson: Deploying and monitoring a model →

FAQ

Is this Machine Learning cheat sheet free to use?
Yes. No sign-up and no tracking: the page is static, every example is on the page itself, and you can print it or save it as a one-page reference.
Where do the examples come from?
Every snippet is taken from the 13 lessons of the Machine Learning course on this site, and each section links back to the lesson it was pulled from.
How do I go deeper than a cheat sheet?
Open the full Machine Learning course — it carries the worked explanations, the edge cases and the exercises behind every line here.

AI Basics AI Agents Math for AI scikit-learn TensorFlow PyTorch

Last refreshed 2026-09-27.