Random Forests

A hospital wants to predict which patients will be readmitted within thirty days. A single Decision Trees model is easy to read. But it is wildly sensitive to which patients happened to end up in the training set. Retrain it on a slightly different sample and the splits change completely. Averaging many such trees would help. Except that trees grown on bootstrap resamples of the same data tend to make similar mistakes. This happens because they keep rediscovering the same dominant predictors near the root. Random forests fix this by injecting a second, independent source of randomness. Each split is restricted to a random subset of features. This makes the trees in the ensemble genuinely disagree with each other. Their errors cancel out on average rather than reinforcing each other. This idea, introduced by Leo Breiman in 2001, turned an unstable but flexible base learner into something more reliable. It became one of the most reliable off-the-shelf predictors in applied machine learning. Random forests require almost no tuning. They handle mixed numerical and categorical data. They tolerate irrelevant features gracefully. They also come with a built-in, honest estimate of test error — the out-of-bag (OOB) estimate. No separate validation split is required. They remain a first-choice baseline in tabular prediction tasks across biology, finance, and industry. They are also the direct ancestor of gradient boosted trees, the other dominant tree-ensemble family. The central mechanical trick is deceptively simple. Combine Bagging and the Bootstrap with random feature selection at every split. Bagging alone reduces variance by averaging trees fit on bootstrap resamples. But the trees remain correlated, because the strongest predictor usually wins the top split. This happens in almost every resampled tree. Random forests break this correlation by hiding most of the features from each split. This forces trees to explore different structure. It also produces more independent errors. This article works out precisely why that decorrelation reduces variance. It walks through the growing algorithm and the OOB mechanism step by step. It also shows how to read variable importance scores without being misled by them. By the end you will be able to state the algorithm as pseudocode. You will be able to derive the variance-reduction argument from first principles. You will be able to trace OOB error computation on a concrete dataset. You will also recognize the common failure modes. These include the danger of trusting default importance scores with correlated features.

Key takeaways
  • Random forests average many decision trees, each grown on a bootstrap sample with splits restricted to a random subset of `m` features.
  • The random feature subsampling decorrelates trees beyond what bagging alone achieves, which is what actually reduces ensemble variance.
  • Bias per tree stays roughly the same as an unpruned single tree; averaging mainly attacks variance, so trees are grown deep and largely unpruned.
  • Out-of-bag (OOB) error reuses the ~37% of data each tree never saw, giving a free, nearly unbiased test-error estimate with no held-out set.
  • Variable importance comes from mean decrease in impurity (fast, biased toward high-cardinality features) or permutation importance (slower, more trustworthy).
  • Random forests are a stepping stone toward Stacking and Model Blending, where heterogeneous models — not just resampled trees — are combined.
The core insight

Random forests reduce prediction variance by averaging many deep decision trees that are deliberately decorrelated through random feature subsetting at every split, not merely through bootstrap resampling.

The idea

A random forest is an ensemble of decision trees, each trained on an independent bootstrap sample of the training data, in which every candidate split considers only a random subset of the available features rather than all of them. Formally, given training data of size nn, the forest builds BB trees T1,…,TBT_1, \dots, T_B. Tree TbT_b is grown on a bootstrap sample Db\mathcal{D}_b drawn with replacement from the original nn observations, and at each internal node the algorithm picks the best split using only m<pm < p features drawn uniformly at random from the full set of pp predictors (a fresh random subset at every single node, not once per tree). Predictions are aggregated by averaging (regression) or majority vote (classification) across all BB trees. The two random ingredients serve different purposes. Bootstrap resampling, inherited from Bagging and the Bootstrap, makes each tree see a slightly different training set, which alone reduces variance somewhat but leaves trees correlated — because in most resamples the single strongest predictor still dominates the top splits. Restricting the split search to mm random features is the added mechanism that breaks this correlation: with a smaller mm, strong predictors are sometimes simply unavailable at a given node, forcing the tree to use other, otherwise-shadowed features. This produces trees whose errors are less alike, which is precisely the condition under which averaging helps most. Random forests deliberately grow trees deep — often to purity or a minimum leaf size of 1 (regression) or a few observations (classification) — rather than pruning them, unlike the pruning strategy used for single trees under Tree Pruning and Complexity Control. This is not an oversight: a single unpruned tree has low bias but very high variance, and averaging is the mechanism used to control variance instead of pruning. Pruning each individual tree would push bias up for no compensating benefit, since the ensemble's decorrelation already tames the variance.

How it works

Growing a random forest of BB trees proceeds by repeating a bootstrap-and-grow procedure independently for each tree, then aggregating. ``` function RANDOM_FOREST_TRAIN(data D of size n, num_trees B, mtry m, max_leaf_size k): forest = [] for b = 1 to B: D_b = SAMPLE_WITH_REPLACEMENT(D, size = n) # bootstrap sample OOB_b = { i in D : i not in D_b } # out-of-bag indices for tree b T_b = GROW_TREE(D_b, m, k) forest.append( (T_b, OOB_b) ) return forest function GROW_TREE(data, m, k): node = new node if STOPPING_CONDITION(data, k): # e.g. |data| <= k, or pure node node.prediction = AGGREGATE(data.labels) # mean (regression) / majority (classification) return node F = RANDOM_SUBSET(all_features, size = m) # fresh draw at THIS node best_split = None for feature in F: for threshold in CANDIDATE_THRESHOLDS(data, feature): gain = IMPURITY_DECREASE(data, feature, threshold) if best_split is None or gain > best_split.gain: best_split = (feature, threshold, gain) if best_split is None or best_split.gain <= 0: node.prediction = AGGREGATE(data.labels) return node left_data, right_data = SPLIT(data, best_split.feature, best_split.threshold) node.split = best_split node.left = GROW_TREE(left_data, m, k) node.right = GROW_TREE(right_data, m, k) return node function PREDICT(forest, x): predictions = [ TREE_PREDICT(T_b, x) for (T_b, _) in forest ] return AGGREGATE(predictions) # mean or majority vote function OOB_ERROR(forest, D): errors = 0 for i in D: preds_i = [ TREE_PREDICT(T_b, x_i) for (T_b, OOB_b) in forest if i in OOB_b ] if preds_i is empty: continue # rare, skip y_hat_i = AGGREGATE(preds_i) errors += LOSS(y_hat_i, y_i) return errors / n ``` Two parameters dominate behavior: `mtry`, the number of features considered per split (mm), and `num_trees` (BB). Standard defaults are m=⌊p⌋m = \lfloor \sqrt{p} \rfloor for classification and m=⌊p/3⌋m = \lfloor p/3 \rfloor for regression, though both can be tuned via OOB error without touching a held-out set. Increasing BB never hurts expected performance in the large-BB limit (it only costs computation), because averaging more independent, identically-distributed error terms only reduces variance further; BB is typically chosen large enough that OOB error has stabilized (hundreds to a few thousand trees). The out-of-bag mechanism deserves its own emphasis. Each bootstrap sample of size nn drawn with replacement excludes, on average, a fraction

(1−1n)n→e−1≈0.368\left(1 - \frac{1}{n}\right)^n \to e^{-1} \approx 0.368

of the original observations as n→∞n \to \infty. Those excluded points are "out-of-bag" for that tree and were never used to fit it, so predicting them with that tree gives an honest holdout prediction. Aggregating each observation's OOB predictions across only the trees that never saw it produces an estimate of test error that behaves like cross-validation, without any explicit validation split or refitting.

Figure
Random forest architecture: bootstrap sampling, per-node feature subsetting, and aggregation
Random Forest: from data to prediction Training data D (n rows, p features) Bootstrap D₁ (OOB₁) Bootstrap D₂ (OOB₂) Bootstrap D_B (OOB_B) Tree T₁ (feature subset per split) Tree T₂ (feature subset per split) Tree T_B (feature subset per split) ⋮ Aggregate mean / vote ŷ(x) Out-of-bag (OOB) error estimation For point i, predict using only trees whose bootstrap sample excluded i (≈36.8% of trees, on average, since P(excluded) = (1 − 1/n)ⁿ → e⁻¹) Average those predictions per point, compare to true label → OOB error ≈ unbiased test error, computed with no separate validation split

Complexity & correctness

Time complexity. Growing one unpruned tree on nn observations with pp total features, but only mm considered per split, costs O(m⋅nlog⁡n)O(m \cdot n \log n) under the standard assumption of a roughly balanced tree of depth O(log⁡n)O(\log n): at each of O(log⁡n)O(\log n) levels, sorting or scanning to find the best threshold across nn points and mm candidate features costs O(mn)O(m n) total per level (with pre-sorted features maintained across splits, a common implementation trick). Across all levels this gives O(mnlog⁡n)O(m n \log n) per tree, and O(B⋅m⋅nlog⁡n)O(B \cdot m \cdot n \log n) for the whole forest. In the worst case of a highly unbalanced tree (e.g., pathological data), a single tree can degrade toward O(mn2)O(m n^2), but with reasonable stopping rules (minimum leaf size) this is rarely observed in practice. Prediction for one query point is O(Blog⁡n)O(B \log n), since each tree is traversed root-to-leaf in O(depth)O(\text{depth}).

Space complexity. Storing BB trees costs O(B⋅n)O(B \cdot n) in the worst case (one leaf per training point) though typically far less with a minimum leaf size; training additionally needs O(n)O(n) working memory per tree if sorted feature copies are cached, or O(mn)O(mn) if all mm candidate feature columns are held simultaneously during a split search.

Why decorrelation reduces variance — the core argument. Suppose each tree's prediction at a fixed test point xx is a random variable (random over the bootstrap sample and feature subsets) with the same variance σ2\sigma^2 and pairwise correlation ρ\rho between any two trees. The variance of the average of BB such identically distributed, equally correlated predictors is

Var(1B∑b=1BTb(x))=ρσ2+1−ρBσ2.\mathrm{Var}\left(\frac{1}{B}\sum_{b=1}^B T_b(x)\right) = \rho \sigma^2 + \frac{1-\rho}{B}\sigma^2 .

As B→∞B \to \infty, the second term vanishes, leaving ρσ2\rho \sigma^2 as an irreducible floor. Plain bagging (no feature subsetting) reduces the second term but does nothing about ρ\rho; because dominant predictors tend to win the top split in nearly every bootstrap tree, ρ\rho stays uncomfortably high. Restricting each split to a random subset of m<pm < p features actively lowers ρ\rho: with smaller mm, the top predictor is simply unavailable at many nodes across many trees, forcing structurally different splits and less-correlated errors. This is the entire justification for random forests over plain bagged trees — a strictly smaller ρ\rho at the (small) cost of a possibly slightly larger per-tree σ2\sigma^2, since restricting the split search can occasionally miss the globally best split. The bias–variance decomposition of the ensemble prediction is otherwise the same as for any averaged estimator: bias is approximately the bias of a single deep tree (small, since deep trees are low-bias), while variance is governed by the formula above.

Why OOB error is (nearly) unbiased. For observation ii, only trees whose bootstrap sample excluded ii are used to predict it — by construction these trees never touched yiy_i during fitting, so the prediction is a genuine out-of-sample prediction, structurally identical to a leave-some-out cross-validation fold. Since roughly 36.8% of trees exclude any given point, and this holds independently, symmetrically, across all nn points and all BB trees, averaging these excluded-tree predictions and comparing to the true label yields an estimate whose expectation converges to the true generalization error as B→∞B \to \infty. It is not perfectly unbiased for any finite training set (it slightly underestimates error compared to a fully independent test set drawn from the population, since OOB points still come from the same finite dataset), but empirically it tracks held-out test error very closely and is the standard practical proxy.

Honest tradeoffs. Random forests reduce variance but cannot reduce the bias of a deep tree below what a single fully grown tree already achieves; on problems where the true signal requires extrapolation beyond the training range, or is dominated by a strong additive/linear structure, forests can underperform simpler models. They are also comparatively memory-hungry (storing hundreds of full trees) and slower at prediction time than a single tree or a linear model, and their internal randomness makes exact reproducibility require careful seeding. Compared to boosting, forests parallelize trivially (each tree is independent) but typically need more trees to reach comparable accuracy on structured, low-noise problems.

Worked example

Consider a small illustrative regression dataset predicting a patient's length of hospital stay (days) from three features: age, prior admissions count, and a lab marker. Suppose n=8n = 8 patients and we grow B=5B = 5 trees with m=2m = 2 of the 3 features considered per split. The table shows which patients land in each tree's bootstrap sample (✓ = in-bag, blank = out-of-bag), abbreviated for illustration.

PatientT1T_1T2T_2T3T_3T4T_4T5T_5True yy
P1✓✓✓4
P2✓✓✓✓6
P3✓✓✓3
P4✓✓✓5
P5✓✓✓2
P6✓✓✓✓7
P7✓✓✓3
P8✓✓✓6

For patient P1, out-of-bag trees are T3T_3 and T5T_5 (the two trees whose bootstrap draw skipped P1). Suppose T3(P1)=3.6T_3(\text{P1}) = 3.6 and T5(P1)=4.4T_5(\text{P1}) = 4.4. The OOB prediction is the average over exactly the trees that never trained on P1:

y^OOB(P1)=3.6+4.42=4.0,\hat{y}_{\text{OOB}}(\text{P1}) = \frac{3.6 + 4.4}{2} = 4.0,

compared to true y=4y = 4: squared error =0.0= 0.0. For P5, out-of-bag trees are T1T_1 and T2T_2; suppose their predictions are 2.92.9 and 3.43.4, giving y^OOB(P5)=3.15\hat{y}_{\text{OOB}}(\text{P5}) = 3.15 against true y=2y=2, squared error =1.32= 1.32. Repeating this for all 8 patients (using only each patient's own out-of-bag trees) and averaging the squared errors yields the OOB mean-squared-error — a stand-in for test MSE, computed with zero extra data held aside. Now trace the effect of `mtry`. If m=3m = 3 (i.e. every feature considered at every split, equivalent to plain bagging), suppose the lab marker is overwhelmingly the strongest predictor: nearly every tree's root split uses it, so T1,…,T5T_1, \dots, T_5 make strongly correlated errors — ρ\rho is high, and the variance-reduction formula above shows the averaged variance stays close to ρσ2\rho \sigma^2, not much smaller than a single tree. Dropping to m=2m = 2 or m=1m = 1 forces roughly a third to two-thirds of trees to split first on age or prior admissions instead, since the lab marker is randomly excluded from the candidate set at many root nodes; the resulting trees disagree more, ρ\rho falls, and the OOB MSE measured across a full run typically decreases — this is the direct, checkable signature of decorrelation actually working, and is the standard way practitioners tune `mtry`: sweep a few candidate values and pick the one with lowest OOB error. Finally, consider importance. Suppose across all trees, splitting on "prior admissions" reduces impurity by a cumulative amount much larger than splitting on "age," even though a permutation test — shuffling the age column and remeasuring OOB error — shows only a small increase in error, while shuffling prior admissions causes a large jump. This divergence between the two importance measures is expected and explained below.

Common pitfalls & traps

PitfallWhy it happensConsequenceFix
Treating OOB error as identical to a held-out test set from a different populationOOB points are still drawn from the same finite training distributionSlight optimistic bias versus fresh external dataStill validate on a truly external test set when stakes are high
Using mean-decrease-in-impurity importance to rank features with different cardinalitiesHigh-cardinality or continuous features get more candidate split points, inflating apparent gainCategorical/low-cardinality features look artificially unimportantUse permutation importance, or conditional importance measures
Setting `mtry` = pp (all features)Removes the decorrelation mechanism entirely, reducing to plain baggingHigher ρ\rho, less variance reduction than expectedTune `mtry` via OOB error, don't default to "all features"
Interpreting importance scores under strong feature correlationCorrelated features "split" the importance credit between them via random subsettingBoth correlated features appear artificially less important individuallyGroup correlated features before computing importance, or use clustering-based importance
Assuming more trees always improves accuracy meaningfullyVariance reduction saturates once BB is large; only computation keeps growingWasted training time with negligible accuracy gainMonitor OOB error curve and stop once it plateaus
Ignoring extrapolation limitsTrees predict flat, bounded values outside the training feature rangePoor performance on inputs beyond observed rangePrefer models with explicit trend structure for extrapolation-heavy tasks

Pioneers & history

Origins · 1 minLeo Breiman and how this was discovered
LB
Leo Breiman
1928–2005 · Statistics and Machine Learning, UC Berkeley

Leo Breiman spent an early career in mathematical probability before turning to applied statistics and, later, machine learning, where he became one of the most influential bridge-builders between statistical theory and algorithmic prediction. His 1996 paper on bagging demonstrated that averaging bootstrap-resampled unstable predictors reduces variance; his 2001 paper "Random Forests" extended this by adding random feature subsetting, proving foundational variance bounds and popularizing out-of-bag error estimation as a built-in validation mechanism. Breiman's broader "two cultures" perspective — contrasting data-modeling statistics with algorithmic, prediction-focused modeling — anticipated much of the philosophy underlying modern machine learning.

Origins · 1 minhow this was discovered
History
1984
— Breiman, Friedman, Olshen, and Stone publish CART, establishing the modern decision-tree framework.
1996
— Breiman introduces bagging, showing bootstrap-averaged trees reduce variance.
2001
— Breiman formalizes random forests, adding random feature subsetting and out-of-bag error estimation.
2006
— Extremely randomized trees (Extra-Trees) push randomization further by also randomizing split thresholds.
2010
s — Random forests become a standard baseline across bioinformatics, finance, and competition machine learning, alongside the rise of gradient boosted trees.

Where this matters

Random forests are a default baseline wherever tabular, structured data with mixed feature types needs a robust predictor without heavy tuning. In genomics and bioinformatics, they classify tumor subtypes from gene-expression matrices with far more features than samples, since random feature subsetting tolerates high dimensionality gracefully. In finance, they underlie credit-risk and fraud-detection models where interpretability of variable importance, alongside reasonable accuracy, is valued over squeezing out the last fraction of a percent of performance. Ecology and remote sensing use random forests for land-cover classification from satellite imagery, exploiting their robustness to noisy, correlated spectral bands. Search and recommendation systems have historically used forests (and their close relatives) as one component among several models for ranking. Statistical software ecosystems — R's `randomForest` and `ranger`, Python's `scikit-learn` `RandomForestClassifier`/`Regressor` — make the algorithm a near-universal first attempt on new tabular datasets before reaching for more elaborate ensembles. The conceptual lineage continues directly into Stacking and Model Blending, where random forests are frequently one of several diverse base learners whose predictions are themselves combined by a meta-model, and into gradient boosting, which replaces independent-tree averaging with sequential, error-correcting tree fitting.

Ask the tutor: How exactly does lowering `mtry` trade off per-tree bias against inter-tree correlation, and where is the optimal point for a dataset with only a few truly informative features among many noise features?

Why not just use bagging without random feature subsetting?

Bagging alone resamples the training data but still lets every tree search all pp features at every split. When one or two predictors are much stronger than the rest, nearly every bootstrap tree picks the same dominant feature near the root, producing highly correlated trees. The variance formula ρσ2+(1−ρ)σ2/B\rho\sigma^2 + (1-\rho)\sigma^2/B shows that averaging highly correlated predictors barely reduces variance below ρσ2\rho\sigma^2, however large BB becomes. Random feature subsetting deliberately hides the dominant predictor from many split searches, forcing genuine structural diversity across trees and lowering ρ\rho. This is precisely the addition that distinguishes random forests from plain bagged trees and is responsible for most of the practical accuracy gain.

How should I choose `mtry` and the number of trees `B`?

`mtry` is best tuned by sweeping a small grid of candidate values (common starting points: p\sqrt{p} for classification, p/3p/3 for regression) and comparing OOB error, since no separate validation split is needed. Smaller `mtry` decorrelates trees more but can raise each tree's individual variance slightly if the search misses good splits; the sweet spot depends on how many features are truly informative. For `B`, more trees never increase expected error (only computation cost), so the standard practice is to plot OOB error against `B` and stop once the curve visibly plateaus, typically in the hundreds to low thousands of trees for moderate-sized tabular datasets.

Is OOB error the same as k-fold cross-validation error?

They are conceptually similar — both estimate test error using data withheld from training — but not identical. Cross-validation partitions data into disjoint folds and refits the entire model kk times, each time excluding one fold completely. OOB error instead reuses the natural byproduct of bootstrap sampling: each tree already has its own held-out set (about 36.8% of the data), so no refitting is needed at all. In practice OOB error tracks cross-validation and external test error closely for random forests, and is generally preferred for tuning because it comes essentially free during a single training run.

Why do impurity-based and permutation-based importance sometimes disagree strongly?

Mean-decrease-in-impurity importance accumulates the impurity reduction credited to a feature every time it is used for a split, summed and averaged over all trees. This measure is biased toward features with many possible split points — continuous or high-cardinality categorical variables — simply because they offer more candidate thresholds to search over, inflating their apparent gain. Permutation importance instead measures how much OOB error increases when a feature's values are randomly shuffled, breaking its relationship with the outcome while preserving its marginal distribution; this does not have the same cardinality bias. When the two measures disagree sharply, permutation importance is usually the more trustworthy signal for understanding true predictive contribution.

How do random forests handle correlated or redundant features?

Correlated features tend to split importance credit between themselves, since random feature subsetting means each correlated variable is sometimes present and sometimes absent from a given node's candidate set, and either one can be used interchangeably when both carry similar signal. This makes individual importance scores for correlated features appear lower than their true joint contribution, which can mislead someone reading importance rankings in isolation. Prediction accuracy itself is not badly harmed by correlated features, since the forest can substitute one for another as needed; the caution applies specifically to interpreting importance scores, not to raw predictive performance.

When would a random forest underperform compared to a simpler model?

Random forests are piecewise-constant predictors built from axis-aligned splits, so they struggle with smooth, especially linear or additive, relationships that a linear or additive model captures exactly with far fewer parameters. They also cannot extrapolate meaningfully beyond the range of training data, since leaf predictions are fixed constants bounded by observed training values. On small datasets with strong prior structure, or on time-series problems with a genuine trend that continues beyond the observed range, simpler parametric models or specialized time-series methods often outperform random forests despite the forest's generally strong benchmark performance elsewhere.

How does a random forest differ from gradient boosted trees?

Both are tree ensembles, but they build and combine trees in fundamentally different ways. Random forests grow trees independently and in parallel on bootstrap samples with random feature subsets, then average — the goal is variance reduction via decorrelation. Gradient boosting grows trees sequentially, where each new tree is fit to the residual errors of the ensemble built so far — the goal is bias reduction via iterative error correction. Boosted ensembles are typically more sensitive to hyperparameters and prone to overfitting if not carefully regularized (learning rate, tree depth, early stopping), while random forests are comparatively robust and easier to deploy with default settings.

Before this

Where this leads