Random forests

ml basics

Random forests are covered in section 8.2.2 of An Introduction to Statistical Learning. The method is Leo Breiman’s, from his 2001 paper “Random Forests”. It’s probably the most widely used “just works” model for tabular data in banking: credit scoring, fraud, operations risk.

The bagging post ended on a limit. Averaging many trees cuts variance only down to a floor of ρ σ2\key{\rho}\,\risk{\sigma^2}, where ρ\key{\rho} is how similar the trees’ errors are. Random forests attack ρ\key{\rho}.

The problem with bagging: every tree looks the same

On the made-up settlement data, the strongest single signal of a failed trade is an SSI mismatch (the two banks’ settlement instructions don’t match). I bagged 300 trees and looked at the first question each tree asks:

ModelTrees whose first question is “SSI mismatch?”
Bagging300 of 300

Each tree sees a different bootstrap sample, but the mismatch signal is so strong that every single tree starts with it, and probably makes similar choices after it. The trees are different, but not very different. Their mistakes are correlated, so averaging them can’t cancel much.

The fix: limit the choice at each split

A random forest adds one rule to bagging:

At every split, pick a random subset of m\key{m} features, and only consider those.

Sometimes “SSI mismatch” isn’t in the subset. Then the tree has to find the next-best question using the other features, and it learns something the other trees didn’t.

ModelFeatures allowed per splitTrees starting with “SSI mismatch?”
Baggingall 5300 of 300
Random forest2 of 5122 of 300
Random forest1 of 570 of 300

Now the trees genuinely differ: some start with how late the trade was booked, some with the new counterparty, some with cross-border. Each is a bit weaker on its own, but they are wrong in more different ways, so their average is better.

How big should m\key{m} be? With pp features, the usual starting point is

m, the features each split may use; ρ, how alike the trees arevariance

m≈p\key{m} \approx \sqrt{p}

which is what ISLP recommends and what scikit-learn’s RandomForestClassifier uses by default. For regression, The Elements of Statistical Learning suggests m≈p/3\key{m} \approx p/3, but note that scikit-learn’s RandomForestRegressor defaults to all features, which is plain bagging, so set it yourself. These are starting points, not laws: tune m\key{m} on held-out data. Setting m=p\key{m} = p turns a random forest back into plain bagging.

The results

Same 15,000 training trades and 5,000 test trades as in the earlier posts. Every tree is grown until its leaves hold 20 trades, so the only difference between rows is the randomness. Scores are AUC on the test trades (0.5 is a coin flip, 1.0 is perfect):

ModelTest AUC
One tree0.747
Bagging, 300 trees0.782
Random forest, 300 trees, 2 of 5 features per split0.795
Random forest, 300 trees, 1 of 5 features per split0.799
Extra trees, 300 trees, 2 of 5 features, random cut-offs0.812

Each step adds more randomness, and each step helps. The last row is a variation called extremely randomised trees (scikit-learn’s ExtraTreesClassifier). It also picks the cut-off values at random instead of searching for the best one, which makes the trees even more different and very fast to train.

With only five features, the gains are modest. Random forests shine most with many features, where a few strong ones would otherwise dominate every tree.

Which features matter?

Random forests can report how much each feature contributes. There are two common ways, and they can disagree badly.

Impurity importance (scikit-learn’s feature_importances_): add up how much each feature reduced Gini impurity across all the splits in all the trees. It’s free, since it comes from training.

Permutation importance: take the test set, randomly shuffle one feature’s column so it no longer carries any information, and measure how much the test score drops. The bigger the drop, the more the model relied on that feature.

FeatureImpurity importancePermutation importance (drop in test AUC)
SSI mismatch0.5200.158
Hours booked late0.2040.065
Trade size0.1790.000
New counterparty0.0500.026
Cross-border0.0470.036

Bar chart of permutation importance: SSI mismatch far ahead, then hours booked late, cross-border, new counterparty; trade size zero.

Look at trade size. Impurity importance ranks it third. Permutation importance says shuffling it costs nothing. In my made-up data, trade size has only a tiny real effect on fails, so the second answer is the honest one.

Why does impurity importance get it wrong? Trade size is a continuous number with thousands of different values, so it offers thousands of possible cut-offs. With that many tries, some cut-off always reduces impurity a little on the training data, by fitting noise. Features like that get inflated scores. scikit-learn’s own documentation warns about exactly this bias towards features with many unique values. For anything you’ll act on, use permutation importance on test data.

The code

from sklearn.ensemble import RandomForestClassifier
from sklearn.inspection import permutation_importance

forest = RandomForestClassifier(
    n_estimators=300,          # number of trees
    max_features=2,            # features considered at each split (m)
    min_samples_leaf=20,       # stop splitting below 20 trades
    n_jobs=-1,                 # use all CPU cores
    random_state=0,
)
forest.fit(X_train, y_train)
forest.predict_proba(X_test)[:, 1]                     # probability of a fail

result = permutation_importance(forest, X_test, y_test, scoring="roc_auc", n_repeats=10)
result.importances_mean                                # drop in AUC per shuffled feature

Why banks like random forests

  • Few settings to get wrong. The defaults are usually decent.
  • Hard to overfit by adding trees. More trees never makes it worse; it just stops improving.
  • Handles mixed data (yes/no flags, amounts, counts) without scaling.
  • Built-in honesty check: out-of-bag scores, as in bagging.

And the trade-offs: a forest of 300 trees is much harder to explain to a regulator or a business user than one tree, and it’s slower to score. Model risk teams usually ask for importance analysis and example explanations alongside it.

Summary

IdeaRuleRemember it as
Random forestbagging + random subset of m\key{m} features at each splitforce the trees to differ
Why it workslower correlation ρ\key{\rho} between treeslower floor ρσ2\key{\rho}\risk{\sigma^2} on variance
Default m\key{m}p\sqrt{p} (set it yourself for regression in scikit-learn)then tune it
Extra treesalso random cut-offseven more different, very fast
Importanceprefer permutation importance on test dataimpurity importance favours many-valued features

Next

Boosting: instead of training trees independently and averaging, train them one after another, each one focusing on the mistakes of the ones before.

References