Boosted Trees › Implementation in Scikit-learn

Michael BrenndoerferJuly 3, 20259 min read
Machine Learning from ScratchAll chapters
Part 5 of 9Tree-Based Models
Chapter 43Boosted Trees › Implementation in Scikit-learn
Reading section 5 of 8 · Implementation in Scikit-learn · 9 minSection 5 of 8 · 9 min

Book contents

Section 5 of 8 · 9 min read

Boosted Trees › Implementation in Scikit-learnOne section · Sections in this chapter5 / 8 ↓
Reading tools Definitions & inline links

Choose your expertise level to adjust how many terms are explained. Beginners see more tooltips, experts see fewer to maintain reading flow. Hover over underlined terms for instant definitions.

Article links

Make inline references clickable

Boosted trees are implemented in scikit-learn through the GradientBoostingClassifier and GradientBoostingRegressor classes. Let's implement a complete example with proper preprocessing and evaluation:

In[7]:
Code
import numpy as np
from sklearn.ensemble import GradientBoostingClassifier
from sklearn.model_selection import cross_val_score, train_test_split

np.random.seed(42)
n_samples = 1000

# Generate features for classification
age = np.random.normal(40, 15, n_samples)
age = np.clip(age, 18, 80)

income = np.random.normal(60, 20, n_samples)
income = np.clip(income, 20, 150)

experience = np.random.normal(10, 8, n_samples)
experience = np.clip(experience, 0, 30)

# Create target based on realistic relationship
log_odds = -5 + 0.02 * age + 0.01 * income + 0.05 * experience
prob_purchase = 1 / (1 + np.exp(-log_odds))
y_class = np.random.binomial(1, prob_purchase, n_samples)

X_class = np.column_stack([age, income, experience])  # Combine features

X_train_class, X_test_class, y_train_class, y_test_class = train_test_split(
    X_class, y_class, test_size=0.2, random_state=42, stratify=y_class
)

gb_classifier = GradientBoostingClassifier(
    n_estimators=100,
    learning_rate=0.1,
    max_depth=3,
    min_samples_split=5,
    min_samples_leaf=2,
    random_state=42,
)

gb_classifier.fit(X_train_class, y_train_class)

y_pred_class = gb_classifier.predict(X_test_class)
y_pred_proba_class = gb_classifier.predict_proba(X_test_class)[:, 1]

# Use a more robust cross-validation approach
import warnings

from sklearn.model_selection import StratifiedKFold

# Suppress the specific warning about failed fits
warnings.filterwarnings("ignore", category=UserWarning, module="sklearn")

# Check class distribution to ensure we have both classes
unique_classes, class_counts = np.unique(y_train_class, return_counts=True)
print(
    f"Class distribution in training set: {dict(zip(unique_classes, class_counts))}"
)

# Use StratifiedKFold with error handling
skf = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)
cv_scores = cross_val_score(
    gb_classifier, X_train_class, y_train_class, cv=skf, scoring="roc_auc"
)
Out[8]:
Console
Classification Performance:
Accuracy: 0.945
ROC AUC: 0.668

Feature Importance:
Age: 0.415
Income: 0.329
Experience: 0.256

Sample Predictions:
Age: 21.8, Income: 57.0, Exp: 2.7 -> Prob: 0.007, Actual: 0
Age: 39.8, Income: 90.6, Exp: 7.6 -> Prob: 0.061, Actual: 0
Age: 39.6, Income: 64.3, Exp: 8.7 -> Prob: 0.017, Actual: 1
Age: 35.4, Income: 28.3, Exp: 0.0 -> Prob: 0.003, Actual: 0
Age: 52.4, Income: 87.8, Exp: 11.8 -> Prob: 0.027, Actual: 0
Classification Report:

              precision    recall  f1-score   support

           0       0.95      0.99      0.97       190
           1       0.00      0.00      0.00        10

    accuracy                           0.94       200
   macro avg       0.47      0.50      0.49       200
weighted avg       0.90      0.94      0.92       200


Confusion Matrix:
[[189   1]
 [ 10   0]]

Cross-validation ROC AUC scores:
 [0.51439145 0.58100329 0.55180921 0.52672697 0.48026316]

Mean CV ROC AUC: 0.531 (+/- 0.068)
Out[9]:
Console
Feature Importance in Boosted Tree Classifier:
==================================================
Age         : 0.415
Income      : 0.329
Experience  : 0.256
==================================================
Higher values indicate more influential features.
Out[10]:
Visualization
Boosted-tree receiver operating characteristic curve from false positive rate zero to one and true positive rate zero to one, with the measured AUC in the legend and a dashed random-classifier diagonal.
Receiver operating characteristic curve for the boosted-tree classifier. The curve traces the trade-off between true positive rate and false positive rate across classification thresholds; its legend reports the measured area under the curve. The dashed diagonal retains the random-classifier baseline at AUC = 0.5.

Now let's implement a regression example:

In[11]:
Code
np.random.seed(42)

# Generate features for regression
house_size = np.random.normal(2000, 500, n_samples)
house_size = np.clip(house_size, 800, 4000)

bedrooms = np.random.poisson(3, n_samples)
bedrooms = np.clip(bedrooms, 1, 6)

bathrooms = np.random.poisson(2, n_samples)
bathrooms = np.clip(bathrooms, 1, 4)

# Create target based on realistic relationship
house_price = (
    50000
    + 100 * house_size
    + 20000 * bedrooms
    + 15000 * bathrooms
    + np.random.normal(0, 10000, n_samples)
)
house_price = np.clip(house_price, 100000, 800000)

X_reg = np.column_stack([house_size, bedrooms, bathrooms])

X_train_reg, X_test_reg, y_train_reg, y_test_reg = train_test_split(
    X_reg, house_price, test_size=0.2, random_state=42
)

gb_regressor = GradientBoostingRegressor(
    n_estimators=100,
    learning_rate=0.1,
    max_depth=3,
    min_samples_split=5,
    min_samples_leaf=2,
    random_state=42,
)

gb_regressor.fit(X_train_reg, y_train_reg)
y_pred_reg = gb_regressor.predict(X_test_reg)
Out[12]:
Console
Regression Performance:
R² Score: 0.958
RMSE: 12387.57

Feature Importance:
House Size: 0.688
Bedrooms: 0.249
Bathrooms: 0.063

Sample Predictions:
Size: 2272, Beds: 3, Baths: 1 -> Pred: $356879, Actual: $339322
Size: 2491, Beds: 3, Baths: 3 -> Pred: $405659, Actual: $393120
Size: 1080, Beds: 4, Baths: 2 -> Pred: $274314, Actual: $260181
Size: 1713, Beds: 1, Baths: 4 -> Pred: $297229, Actual: $314224
Size: 1438, Beds: 2, Baths: 1 -> Pred: $248029, Actual: $247561
Out[13]:
Console
Feature Importance in Boosted Tree Regressor:
==================================================
House Size  : 0.688
Bedrooms    : 0.249
Bathrooms   : 0.063
==================================================
Higher values indicate more influential features.
Out[14]:
Visualization
Scatter plot of held-out actual and predicted house prices with dollar-formatted axes, a dashed perfect-prediction diagonal, and the test-set R-squared value in the title.
Predicted versus actual house prices for the boosted-tree regressor. Each point is a held-out house, and the dashed diagonal marks perfect predictions. The displayed R² summarizes overall fit, while vertical distance from the diagonal shows the prediction error for each observation.

Hyperparameter Tuning

Boosted trees have several important hyperparameters that can significantly affect performance:

In[15]:
Code
param_grid = {
    "n_estimators": [50, 100, 200],
    "learning_rate": [0.01, 0.1, 0.2],
    "max_depth": [3, 5, 7],
    "min_samples_split": [2, 5, 10],
    "min_samples_leaf": [1, 2, 4],
}

# Use a smaller subset for faster tuning
X_train_small = X_train_class[:500]
y_train_small = y_train_class[:500]

grid_search = GridSearchCV(
    GradientBoostingClassifier(random_state=42),
    param_grid,
    cv=3,  # 3-fold CV for faster tuning
    scoring="roc_auc",
    n_jobs=-1,
)

grid_search.fit(X_train_small, y_train_small)
Out[16]:
Console
Best parameters:
{'learning_rate': 0.1, 'max_depth': 5, 'min_samples_leaf': 2, 'min_samples_split': 10, 'n_estimators': 50}

Best cross-validation score: 0.654
Test set ROC AUC: 0.621
Out[17]:
Visualization
Training and validation ROC AUC learning curves over ten training-set sizes, with different line styles and markers and shaded one-standard-deviation cross-validation bands around each mean.
Learning curve for the regularized boosted-tree classifier across ten training-set sizes. Solid circles show the mean training ROC AUC, dashed squares show mean validation ROC AUC, and the shaded bands preserve one standard deviation across five stratified folds. The gap between the curves reveals how model fit and generalization change as more observations become available.

Key Parameters

Below are some of the main parameters that affect how Gradient Boosting works and performs.

  • n_estimators: Number of boosting stages (trees) to perform (default: 100). More trees generally improve performance but increase computation time and risk of overfitting. Start with 100 and increase if validation scores improve.
  • learning_rate: Shrinkage parameter that controls the contribution of each tree (default: 0.1). Lower values (0.01-0.1) require more trees but often lead to better generalization. Values between 0.01 and 0.2 work well for most datasets.
  • max_depth: Maximum depth of each tree (default: 3). Controls the complexity of individual trees. Shallow trees (3-5) work well for boosting as they focus on learning simple patterns iteratively.
  • min_samples_split: Minimum samples required to split an internal node (default: 2). Higher values prevent overfitting by requiring more data for splits. Values of 5-10 are commonly used.
  • min_samples_leaf: Minimum samples required in a leaf node (default: 1). Higher values smooth the model and prevent overfitting. Values of 2-4 work well for most problems.
  • subsample: Fraction of samples to use for fitting each tree (default: 1.0). Values less than 1.0 introduce stochastic gradient boosting, which can help prevent overfitting. Try values between 0.5 and 0.8.
  • loss: Loss function to optimize (default: 'log_loss' for classification, 'squared_error' for regression). Different loss functions are appropriate for different problem types.
  • random_state: Seed for reproducibility (default: None). Set to an integer to ensure consistent results across runs.

Key Methods

The following are the most commonly used methods for interacting with Gradient Boosting models.

  • fit(X, y): Trains the gradient boosting model on the training data X and target values y. Each tree is trained sequentially to correct the errors of previous trees.
  • predict(X): Returns predicted class labels (classification) or values (regression) for input data X by summing predictions from all trees in the ensemble.
  • predict_proba(X): Returns probability estimates for each class (classification only). Useful for setting custom decision thresholds or understanding prediction confidence.
  • score(X, y): Returns the mean accuracy (classification) or R² score (regression) on the given test data.
  • staged_predict(X): Returns predictions at each stage of boosting. Useful for determining the optimal number of trees and implementing early stopping.