Random Forest (Classification): Principles, Examples, and Python Implementation

Random Forest (Classification) : Guide Complet — Principes, Exemples et Implémentation Python

Random Forest (Classification): Complete Guide — Principles, Examples, and Python Implementation

Summary

Random Forest is a supervised ensemble learning algorithm designed to solve classification problems. Introduced by Leo Breiman in 2001, it combines the bagging (Bootstrap Aggregating) principle with random feature subset selection at each decision node. The result is a forest of decision trees whose majority vote determines the predicted class. Robust, versatile, and resistant to overfitting, Random Forest remains one of the most widely used algorithms in practice, both in data science competitions and in industrial production.

Mathematical Principle

Forest Construction

The classification Random Forest builds a forest of B decision trees. Each tree b is constructed independently:

  1. Bootstrap sample: n samples are drawn with replacement from the n observations in the training set. Each sample has a probability of 1/n of being selected at each draw. On average, approximately 63.2% of the original observations appear in a given tree’s bootstrap sample.
  2. Node splitting with random feature selection: at each node of each tree, instead of examining all p features to find the best split, m features are randomly drawn from the p available (with m ≪ p, typically m = √p for classification), and then the best split is chosen from among these m features according to the Gini criterion or entropy.

Prediction Formula

For an observation $x$, each tree $b$ produces a class prediction $T_b(x) \in {1, 2, \ldots, K}$. The final Random Forest prediction is determined by majority vote:

$$
\hat{y}(x) = \arg\max_k \sum_{b=1}^{B} \mathbb{1}(T_b(x) = k)
$$

where I(·) is the indicator function and K is the total number of classes.

Out-of-Bag (OOB) Error

Observations not selected in a tree’s bootstrap sample are said to be out-of-bag (OOB) for that tree. On average, each observation is OOB for about 36.8% of trees. We can therefore estimate the generalization error without a separate validation set:

$$
\mathrm{OOB}{\mathrm{error}} = \frac{1}{n} \sum\right)
$$}^{n} \mathbb{1}\left(y_i \neq \hat{y}_i^{(\mathrm{OOB})

where ŷ_i^(OOB) is the prediction obtained by majority vote of only the trees for which observation i is out-of-bag. The OOB error is an unbiased estimate of the generalization error, comparable to cross-validation.

Intuition: Why a Forest Beats a Single Tree

A single decision tree is fragile: a slight modification of the training data can produce a radically different tree. This is called high variance. The tree overfits the training data too specifically, capturing noise as well as signal.

But when many trees are combined, a remarkable phenomenon occurs: the forest becomes robust. Each tree sees a slightly different version of the data (thanks to bootstrapping) and examines a different subset of features at each split. Each tree makes an independent, imperfect, yet diversified decision.

The crowd is smarter than a single expert. This is the fundamental idea of ensemble learning. Tree diversity ensures that their errors cancel each other out, while their correct predictions reinforce each other. Mathematically, the total variance of the ensemble is reduced by a factor proportional to 1/B, where B is the number of trees.

Random feature subsampling (feature subsampling) is crucial: without it, all trees would tend to make the same splits and would be highly correlated. By forcing each tree to examine different features, we increase decorrelation between trees, which further enhances the effectiveness of majority voting.

Python Implementation with scikit-learn

Basic Example: RandomForestClassifier

from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import classification_report, confusion_matrix
import numpy as np

# 1. Generate a synthetic dataset
X, y = make_classification(
    n_samples=2000,
    n_features=20,
    n_informative=10,
    n_redundant=5,
    n_clusters_per_class=2,
    random_state=42
)

# 2. Split into training and test sets
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.2, random_state=42, stratify=y
)

# 3. Create and train the Random Forest
rf = RandomForestClassifier(
    n_estimators=100,
    criterion='gini',
    max_depth=None,
    min_samples_split=2,
    min_samples_leaf=1,
    max_features='sqrt',
    bootstrap=True,
    oob_score=True,
    random_state=42,
    n_jobs=-1
)

rf.fit(X_train, y_train)

# 4. Evaluate
y_pred = rf.predict(X_test)
print("Classification Report:")
print(classification_report(y_test, y_pred))
print(f"OOB Score: {rf.oob_score_:.4f}")
print("Confusion Matrix:")
print(confusion_matrix(y_test, y_pred))

Feature Importance

Random Forest provides an intrinsic measure of each feature’s importance, calculated as the average reduction of the splitting criterion (Gini) weighted by the number of samples reaching each node:

# Feature importance
importances = rf.feature_importances_
indices = np.argsort(importances)[::-1]

print("Feature Importance (top 10):")
for i in range(min(10, X.shape[1])):
    print(f"  Feature {indices[i]:2d}: {importances[indices[i]]:.4f}")

# Visualization (if matplotlib is available)
import matplotlib.pyplot as plt
plt.figure(figsize=(10, 6))
plt.bar(range(min(10, X.shape[1])), importances[indices[:10]], align='center')
plt.xticks(range(min(10, X.shape[1])), [f"X{i}" for i in indices[:10]])
plt.xlabel('Feature')
plt.ylabel('Importance')
plt.title('Feature Importance — Random Forest')
plt.tight_layout()
plt.show()

Comparison with a Single Decision Tree

from sklearn.tree import DecisionTreeClassifier
from sklearn.metrics import accuracy_score

# A single decision tree
dt = DecisionTreeClassifier(max_depth=None, random_state=42)
dt.fit(X_train, y_train)

y_pred_rf = rf.predict(X_test)
y_pred_dt = dt.predict(X_test)

print(f"Random Forest (100 trees): {accuracy_score(y_test, y_pred_rf):.4f}")
print(f"Single Decision Tree:      {accuracy_score(y_test, y_pred_dt):.4f}")
print(f"OOB Score (RF):            {rf.oob_score_:.4f}")

Typically, the Random Forest significantly outperforms the single decision tree on the test set because it generalizes better. The single tree suffers from overfitting: it can achieve 100% accuracy on the training set but see its performance drop on test data.

Impact of n_estimators and max_depth

import matplotlib.pyplot as plt

# Study of the impact of the number of trees (n_estimators)
results = {'n_estimators': [], 'train_acc': [], 'test_acc': [], 'oob_score': []}

for n in [1, 5, 10, 25, 50, 100, 200, 500]:
    rf_n = RandomForestClassifier(
        n_estimators=n, max_depth=None, oob_score=True,
        random_state=42, n_jobs=-1
    )
    rf_n.fit(X_train, y_train)
    results['n_estimators'].append(n)
    results['train_acc'].append(accuracy_score(y_train, rf_n.predict(X_train)))
    results['test_acc'].append(accuracy_score(y_test, rf_n.predict(X_test)))
    results['oob_score'].append(rf_n.oob_score_)

plt.figure(figsize=(12, 5))

plt.subplot(1, 2, 1)
plt.plot(results['n_estimators'], results['train_acc'], 'b-', label='Train')
plt.plot(results['n_estimators'], results['test_acc'], 'g-', label='Test')
plt.plot(results['n_estimators'], results['oob_score'], 'r--', label='OOB')
plt.xlabel("Number of Trees (n_estimators)")
plt.ylabel('Accuracy')
plt.legend()
plt.title('Impact of n_estimators')
plt.grid(True, alpha=0.3)

# Study of the impact of max_depth
results_depth = {'max_depth': [], 'train_acc': [], 'test_acc': []}

for depth in [1, 2, 3, 5, 10, 20, 50, None]:
    rf_d = RandomForestClassifier(
        n_estimators=100, max_depth=depth, random_state=42, n_jobs=-1
    )
    rf_d.fit(X_train, y_train)
    label = str(depth) if depth else 'None'
    results_depth['max_depth'].append(label)
    results_depth['train_acc'].append(accuracy_score(y_train, rf_d.predict(X_train)))
    results_depth['test_acc'].append(accuracy_score(y_test, rf_d.predict(X_test)))

plt.subplot(1, 2, 2)
plt.plot(range(len(results_depth['max_depth'])), results_depth['train_acc'], 'b-', label='Train')
plt.plot(range(len(results_depth['max_depth'])), results_depth['test_acc'], 'g-', label='Test')
plt.xticks(range(len(results_depth['max_depth'])), results_depth['max_depth'])
plt.xlabel('max_depth')
plt.legend()
plt.title('Impact of max_depth')
plt.grid(True, alpha=0.3)

plt.tight_layout()
plt.show()

The analysis reveals two key trends:

  • n_estimators: performance increases rapidly and then stabilizes. Beyond 100-200 trees, the marginal gain becomes negligible. More trees never cause overfitting (unlike depth), so this parameter can be increased without risk, at the cost of computation time.
  • max_depth: too little depth causes underfitting (high bias), while excessive depth increases the variance of individual trees. Random Forest is more tolerant than a single tree to large depths thanks to bagging, but limiting max_depth to 10-20 can improve generalization on noisy data.

Random Forest Classification Hyperparameters

Hyperparameter Default Value Role Practical Tip
n_estimators 100 Number of trees in the forest Increase until scores stabilize (100-500). No overfitting.
criterion ‘gini’ Splitting criterion: ‘gini’ or ‘entropy’ ‘gini’ is faster, ‘entropy’ (information gain) can be slightly better on some datasets.
max_depth None Maximum depth of each tree None = full development. Limit to 10-20 if overfitting.
min_samples_split 2 Minimum samples required to split a node Increase (5-20) to reduce overfitting on noisy data.
min_samples_leaf 1 Minimum samples in a leaf 5-10 smooths predictions and reduces variance.
max_features ‘sqrt’ Number of features randomly drawn at each split ‘sqrt’ = √p (classification), ‘log2’ for high dimensions, float for fine-grained control.
bootstrap True Sampling with replacement (true bagging) Leave as True. If False, all trees see the same data.
oob_score False Compute the out-of-bag score Enable for internal validation without a separate set.

Advantages and Limitations of Random Forest

Advantages

  1. Resistant to overfitting: bagging and random feature subsampling considerably reduce variance compared to a single tree. Increasing the number of trees never causes overfitting.
  2. Little preprocessing required: no need for feature normalization or scaling. Naturally handles both numerical and categorical features.
  3. Feature importance: provides an intrinsic measure of each variable’s importance, useful for exploratory analysis and variable selection.
  4. OOB estimation: allows model validation without a separate validation set, saving precious data.
  5. Robustness to outliers and noise: majority voting makes the model insensitive to individual outlier values.
  6. Parallelizable: each tree is built independently, allowing significant speedup on multiple cores.

Limitations

  1. Less interpretable than a single tree: a forest of 200 trees cannot be visualized. The transparency of the tree-based decision is lost.
  2. Memory cost: storing hundreds of trees requires more resources than a linear model or a single tree.
  3. Slower inference: prediction requires propagating the observation through each tree, which is slow on large datasets.
  4. Cannot extrapolate: like all decision trees, Random Forest cannot extrapolate beyond the range of values seen during training (less critical in classification than in regression).
  5. Bias on imbalanced classes: with imbalanced datasets, trees tend to favor the majority class. Use class_weight='balanced' or resampling.

4 Concrete Use Cases

1. Medical Diagnosis

Classification of pathologies from clinical data (blood tests, medical imaging,medical history). Random Forest is particularly well-suited because it handles heterogeneous data, is robust to noise in medical measurements, and provides feature importance that helps practitioners understand the dominant risk factors.

2. Banking Fraud Detection

Binary classification of fraudulent vs. legitimate transactions from hundreds of variables (amount, location, time, customer history, terminal type). Random Forest is ideal for large-scale, imbalanced fraud data, especially with class_weight='balanced' to penalize false negatives.

3. Text Classification

Automatic categorization of documents (news, sports, economy, technology) from TF-IDF representations or word embeddings. Random Forest works well in high-dimensional feature spaces and captures non-linear interactions between terms that linear models miss.

4. Credit Risk Assessment

Classification of borrower creditworthiness (good/bad payer) based on financial profile, repayment history, employment, and socio-demographic data. Financial institutions widely use Random Forests for their tradeoff between accuracy and stability, combined with the ability to explain predictions via analysis of important features.

See Also