MODELCATALOG

Many models combined. Where tabular accuracy lives.

sklearn.ensemble

ensemble

Many models combined. Where tabular accuracy lives.

1 Get data Something to practise on
2 Split & tune An honest test set
3 Prepare Into numbers a model accepts
4 Compose One object, no leakage
5 Model The algorithm itself
6 Unsupervised No labels available
7 Measure Is it any good, and why
8 Utilities Glue and extension

What it is for

Combining many weak learners into a strong one. On tabular data with a few hundred features these still beat neural networks, train in minutes, and come with mature explainability tooling.

Boosting

HistGradientBoosting is sklearn's answer to LightGBM and is usually the best in-library tabular model. It needs no scaling, no one-hot encoding and no imputation.

HistGradientBoostingClassifier / Regressor common Fast, handles missing values and categoricals natively
GradientBoostingClassifier / Regressor The older, slower implementation. Prefer Hist
AdaBoostClassifier Mostly historical interest now

Bagging

RandomForestClassifier / Regressor common Strong default, little tuning needed
ExtraTreesClassifier / Regressor More randomised, faster to fit, sometimes better
BaggingClassifier Wrap any estimator in bootstrap aggregation

Combining models

VotingClassifier / Regressor Combine by vote or averaged probability
StackingClassifier / Regressor Train a meta-model on the base models' outputs

Anomaly detection

IsolationForest bank Unsupervised outlier scoring. Useful in AML where labels are scarce

Worked examples 4

Runnable, with the arguments that change the outcome called out. Output shapes are shown as comments where they help.

sklearn.ensemble

from sklearn.ensemble import HistGradientBoostingClassifier

# No scaling, no one-hot, no imputation needed.
clf = HistGradientBoostingClassifier(
    max_iter=500,
    learning_rate=0.05,
    max_leaf_nodes=31,
    early_stopping=True,
    validation_fraction=0.15,
    categorical_features="from_dtype",   # pandas 'category' columns (1.4+)
    class_weight="balanced",             # 1.5+
    random_state=42,
)
clf.fit(X_train, y_train)

The strongest tabular model in the library

No scaling, no one-hot encoding, no imputation. Usually within a hair of LightGBM without the extra dependency.

from sklearn.ensemble import HistGradientBoostingClassifier

clf = HistGradientBoostingClassifier(
    max_iter=1000,                # boosting rounds; early stopping cuts it
    learning_rate=0.05,           # lower needs more rounds, generalises better
    max_leaf_nodes=31,            # complexity per tree
    min_samples_leaf=50,          # raise on noisy data
    l2_regularization=1.0,
    early_stopping=True,
    validation_fraction=0.15,
    n_iter_no_change=30,
    categorical_features="from_dtype",   # pandas 'category' dtype (1.4+)
    class_weight="balanced",             # 1.5+
    random_state=42,
)

clf.fit(X_train, y_train)
print("stopped at iteration", clf.n_iter_)

# Feed it a DataFrame with proper dtypes and it does the rest:
#   df["product"] = df["product"].astype("category")
#   df["income"]  stays float, NaNs and all

Random forest, and why its importances mislead

feature_importances_ is biased toward high-cardinality features. Model validation teams know this and will ask.

from sklearn.ensemble import RandomForestClassifier
from sklearn.inspection import permutation_importance
import numpy as np

rf = RandomForestClassifier(
    n_estimators=500,
    max_depth=None,
    min_samples_leaf=5,
    class_weight="balanced_subsample",
    n_jobs=-1, random_state=42,
)
rf.fit(X_train, y_train)

# Built in - fast, but biased toward features with many distinct values
built_in = rf.feature_importances_

# Permutation - slower, model-agnostic, computed on HELD-OUT data
perm = permutation_importance(rf, X_test, y_test, n_repeats=10,
                              scoring="average_precision",
                              n_jobs=-1, random_state=42)

print(f"{'feature':26} {'built-in':>10} {'permutation':>12}")
for i in np.argsort(perm.importances_mean)[::-1][:10]:
    print(f"{feature_names[i]:26} {built_in[i]:10.4f} "
          f"{perm.importances_mean[i]:12.4f}")

# Where the two disagree sharply, the built-in number is usually the
# one that is wrong - typically on an ID-like column.

Unsupervised anomaly detection when you have no labels

Useful in AML and fraud where confirmed positives are scarce. Scores how easily each row is isolated from the rest.

from sklearn.ensemble import IsolationForest

iso = IsolationForest(
    n_estimators=200,
    contamination=0.01,     # expected proportion of anomalies - this is
                            # a business decision, not a fitted parameter
    max_samples="auto",
    random_state=42, n_jobs=-1,
)
iso.fit(X_train)            # no y at all

pred  = iso.predict(X_test)             # -1 anomalous, 1 normal
score = iso.score_samples(X_test)       # lower = more anomalous

import numpy as np
order = np.argsort(score)
print("most anomalous rows:", order[:10])
print("their scores:", score[order[:10]].round(3))

# Rank by score rather than trusting the -1/1 label. contamination
# only sets where the cut falls, and you usually want to hand
# investigators a ranked queue rather than a binary flag.