Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Ensemble Methods (XGBoost, LightGBM, CatBoost)

🎯 Goal

After reading this chapter:

  • You will understand the difference between Ensemble methods (Bagging, Boosting, Stacking)
  • You will know when to use Random Forest, XGBoost, LightGBM, CatBoost
  • You will know how to get good results in Kaggle competitions on tabular data
  • You will be able to deploy Gradient Boosting algorithms to production

What to learn

  • Bagging: Random Forest, Extra Trees
  • Boosting: AdaBoost, Gradient Boosting, XGBoost, LightGBM, CatBoost
  • Stacking: meta-learner, blending
  • Voting: hard vs soft voting
  • Feature importance — Gini, permutation, SHAP
  • Hyperparameter tuning — specifically for XGBoost/LightGBM
  • Early stopping — preventing overfitting
  • Categorical handling — CatBoost’s advantage

Libraries

pip install scikit-learn xgboost lightgbm catboost shap

Important topics

Bagging vs Boosting

Bagging (Bootstrap Aggregating):
- Parallel: har model bir-biriga bog'liq emas
- Random Forest = Bagging + Decision Trees + random features
- Maqsad: variance kamaytirish (overfitting'ni)

Boosting:
- Sequential: har model oldingisining xatolarini tuzatishga harakat qiladi
- Gradient Boosting, XGBoost, LightGBM, CatBoost
- Maqsad: bias kamaytirish (underfitting'ni)

Gradient Boosting algorithm

1. F₀(x) = mean(y)  ← boshlang'ich bashorat
2. Repeat (M ta marta):
   a. r_i = y_i - F_{m-1}(x_i)   ← residual (xato)
   b. Yangi tree h_m(x) — r ni bashorat qilish uchun
   c. F_m(x) = F_{m-1}(x) + learning_rate * h_m(x)
3. Final: F_M(x)

Which gradient boosting?

XGBoostLightGBMCatBoost
SpeedMediumVery fast FastestMedium
Accuracy⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐
CategoricalOneHot neededOneHot neededAutomatic
MemoryMediumLowMedium
HyperparamManyManyFew (good defaults)
DocumentationExcellentGoodGood
Industry adoptionLargestLargeGrowing

**Recommendation:**Start with LightGBM(fast), then XGBoost(stable), finally CatBoost(if many categoricals).

Most important hyperparameters (LightGBM/XGBoost)

ParameterDescriptionDefaultRange
n_estimatorsNumber of trees100100-10000
learning_rateStep size0.10.01-0.3
max_depthTree depth-1 (unlimited)3-15
num_leaves (LGBM)Number of leaves3115-256
min_child_samplesMin samples in leaf201-100
subsampleRow sampling1.00.5-1.0
colsample_bytreeFeature sampling1.00.5-1.0
reg_alpha (L1)L1 regularization00-10
reg_lambda (L2)L2 regularization10-10

Early Stopping

# If validation loss doesn't improve, training stops
model.fit(
    X_train, y_train,
    eval_set=[(X_val, y_val)],
    early_stopping_rounds=50,
)
# saves best_iteration

Code examples

Random Forest

from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import cross_val_score

rf = RandomForestClassifier(
    n_estimators=500,
    max_depth=None,
    min_samples_split=2,
    min_samples_leaf=1,
    max_features="sqrt",
    n_jobs=-1,
    random_state=42,
    class_weight="balanced",
)

scores = cross_val_score(rf, X, y, cv=5, scoring="f1")
print(f"F1:{scores.mean():.3f}±{scores.std():.3f}")

# Feature importance
rf.fit(X, y)
importance_df = pd.DataFrame({
    "feature": X.columns,
    "importance": rf.feature_importances_,
}).sort_values("importance", ascending=False).head(10)
print(importance_df)

XGBoost

import xgboost as xgb
from sklearn.model_selection import train_test_split

X_train, X_val, y_train, y_val = train_test_split(X, y, test_size=0.2, random_state=42)

model = xgb.XGBClassifier(
    n_estimators=1000,
    learning_rate=0.05,
    max_depth=6,
    subsample=0.8,
    colsample_bytree=0.8,
    reg_alpha=0.1,
    reg_lambda=1.0,
    random_state=42,
    eval_metric="logloss",
    early_stopping_rounds=50,
)

model.fit(
    X_train, y_train,
    eval_set=[(X_val, y_val)],
    verbose=False,
)

print(f"Best iteration:{model.best_iteration}")
print(f"Best score:{model.best_score:.4f}")

LightGBM

import lightgbm as lgb

model = lgb.LGBMClassifier(
    n_estimators=2000,
    learning_rate=0.05,
    num_leaves=63,
    max_depth=-1,
    min_child_samples=20,
    subsample=0.8,
    colsample_bytree=0.8,
    reg_alpha=0.1,
    reg_lambda=1.0,
    random_state=42,
    n_jobs=-1,
    class_weight="balanced",
)

model.fit(
    X_train, y_train,
    eval_set=[(X_val, y_val)],
    eval_metric="auc",
    callbacks=[lgb.early_stopping(50), lgb.log_evaluation(0)],
)

print(f"Best iter:{model.best_iteration_}")

CatBoost — Categorical Magic

from catboost import CatBoostClassifier

cat_features = ["city", "department", "education"]  # column names

model = CatBoostClassifier(
    iterations=1000,
    learning_rate=0.05,
    depth=6,
    l2_leaf_reg=3,
    cat_features=cat_features,  # automatic handling!
    early_stopping_rounds=50,
    random_seed=42,
    verbose=100,
)

model.fit(X_train, y_train, eval_set=(X_val, y_val))

Optuna tuning (for LightGBM)

import optuna
import lightgbm as lgb

def objective(trial):
    params = {
        "objective": "binary",
        "metric": "auc",
        "learning_rate": trial.suggest_float("learning_rate", 0.01, 0.3, log=True),
        "num_leaves": trial.suggest_int("num_leaves", 15, 256),
        "max_depth": trial.suggest_int("max_depth", 3, 15),
        "min_child_samples": trial.suggest_int("min_child_samples", 5, 100),
        "subsample": trial.suggest_float("subsample", 0.5, 1.0),
        "colsample_bytree": trial.suggest_float("colsample_bytree", 0.5, 1.0),
        "reg_alpha": trial.suggest_float("reg_alpha", 1e-3, 10, log=True),
        "reg_lambda": trial.suggest_float("reg_lambda", 1e-3, 10, log=True),
        "n_estimators": 2000,
        "random_state": 42,
        "n_jobs": -1,
        "verbose": -1,
    }
    
    model = lgb.LGBMClassifier(**params)
    model.fit(
        X_train, y_train,
        eval_set=[(X_val, y_val)],
        callbacks=[lgb.early_stopping(50, verbose=False)],
    )
    
    return roc_auc_score(y_val, model.predict_proba(X_val)[:, 1])

study = optuna.create_study(direction="maximize")
study.optimize(objective, n_trials=100, show_progress_bar=True)

print(f"Best params:{study.best_params}")
print(f"Best AUC:{study.best_value:.4f}")

SHAP — Model interpretation

import shap

# TreeExplainer for tree-based models (fast)
explainer = shap.TreeExplainer(model)
shap_values = explainer.shap_values(X_test)

# Global importance
shap.summary_plot(shap_values, X_test, plot_type="bar")
shap.summary_plot(shap_values, X_test)  # beeswarm plot

# Local explanation (for a single prediction)
shap.force_plot(explainer.expected_value, shap_values[0], X_test.iloc[0])

Stacking — Combining Multiple models

from sklearn.ensemble import StackingClassifier
from sklearn.linear_model import LogisticRegression

estimators = [
    ("rf", RandomForestClassifier(n_estimators=200, random_state=42)),
    ("xgb", xgb.XGBClassifier(n_estimators=200, random_state=42)),
    ("lgb", lgb.LGBMClassifier(n_estimators=200, random_state=42, verbose=-1)),
]

stack = StackingClassifier(
    estimators=estimators,
    final_estimator=LogisticRegression(),
    cv=5,
    n_jobs=-1,
)

stack.fit(X_train, y_train)

Backend integration

XGBoost FastAPI serving

from fastapi import FastAPI
from pydantic import BaseModel
import xgboost as xgb
import numpy as np

app = FastAPI()
model = xgb.XGBClassifier()
model.load_model("models/xgb_v1.json")  # XGBoost native format (faster)

class Features(BaseModel):
    feature_vector: list[float]

@app.post("/predict")
def predict(input_data: Features):
    X = np.array([input_data.feature_vector])
    proba = float(model.predict_proba(X)[0, 1])
    return {
        "prediction": int(proba > 0.5),
        "probability": proba,
    }

ONNX export — Cross-platform

# LightGBM/XGBoost → ONNX → works anywhere (C++, Java, Go, .NET)
from onnxmltools import convert_lightgbm
from skl2onnx.common.data_types import FloatTensorType

initial_types = [("input", FloatTensorType([None, X_train.shape[1]]))]
onnx_model = convert_lightgbm(model, initial_types=initial_types)

with open("model.onnx", "wb") as f:
    f.write(onnx_model.SerializeToString())

# Serving (ONNX Runtime)
import onnxruntime as ort
session = ort.InferenceSession("model.onnx")
predictions = session.run(None, {"input": X_test.astype("float32")})[0]

Batch prediction with Celery

from celery import Celery

celery_app = Celery("ml_tasks", broker="redis://localhost:6379")

@celery_app.task
def batch_predict(file_path: str):
    df = pd.read_csv(file_path)
    model = joblib.load("model.joblib")
    
    predictions = model.predict_proba(df)[:, 1]
    df["churn_probability"] = predictions
    df["risk_segment"] = pd.cut(predictions, bins=[0, 0.3, 0.7, 1.0],
                                 labels=["low", "medium", "high"])
    
    output_path = file_path.replace(".csv", "_predictions.csv")
    df.to_csv(output_path, index=False)
    
    return {"file": output_path, "n_predictions": len(df)}

Resources

🏋️ Exercises

🟢 Easy

  1. Compare RandomForest and LogisticRegression (Titanic).
  2. Compare n_estimators in XGBoost at 100, 500, 1000.
  3. Get feature importance from 3 different models.

🟡 Medium

  1. 3-way comparison: On the same dataset, compare XGBoost, LightGBM, CatBoost — accuracy + training time.
  2. Optuna: For LightGBM tune with 100 trials, compare default vs tuned.
  3. SHAP: For your trained model, SHAP summary plot, top 10 features.

🔴 Hard

  1. Stacking ensemble: 5+ base models + meta-learner. Submit to Kaggle.
  2. ONNX serving: Export XGBoost to ONNX, serving in Go or Node.js (deeper backend integration).
  3. Custom objective: Write a business-aware objective function (e.g., asymmetric loss: FN $50, FP $10).

Capstone — Kaggle Competition

notebooks/month-02/06_kaggle_competition.ipynb:

  • Kaggle — Titanicor Spaceship Titanic
  • Complete pipeline: EDA → FE → 3+ models → ensemble → submission
  • Goal: top 30% (~0.80 accuracy on Titanic, ~0.81 on Spaceship Titanic)
  • Upload the notebook to Kaggle too (public notebook)
  • Commit to GitHub, Kaggle Profile link in README

✅ Checklist

  • I know the difference between Bagging and Boosting
  • I know when to use Random Forest, XGBoost, LightGBM
  • I can work with early stopping
  • I do hyperparameter tuning with Optuna
  • I know the categorical handling advantage of CatBoost
  • I can interpret a model with SHAP
  • I’m familiar with ONNX export and loading
  • I submitted to a Kaggle competition (top 30%)

Month 2 complete! Review the Exercises and proceed to Month 3 — Deep Learning.