Performance Benchmarking
This tutorial compares Perpetual with a heavily tuned LightGBM model across different datasets (California Housing and Cover Type). Perpetual achieves state-of-the-art results without any hyperparameter tuning.
[ ]:
import sys
from importlib.metadata import version
import numpy as np
import optuna
from lightgbm import LGBMClassifier, LGBMRegressor
from perpetual import PerpetualBooster
from sklearn.datasets import fetch_california_housing, fetch_covtype
from sklearn.metrics import log_loss, mean_squared_error
from sklearn.model_selection import cross_validate, train_test_split
[ ]:
print(sys.version)
[ ]:
print(f"numpy: {version('numpy')}")
print(f"optuna: {version('optuna')}")
print(f"lightgbm: {version('lightgbm')}")
print(f"scikit-learn: {version('scikit-learn')}")
print(f"perpetual: {version('perpetual')}")
[ ]:
task_is_cal_housing = False # change to False for Cover Types task.
[ ]:
seed = 0 # average results are reported for 5 seeds -> [0, 1, 2, 3, 4]
n_estimators = 1 # results are reported for 100, 300, 1000 n_estimators.
n_trials = 1
[ ]:
if task_is_cal_housing:
data, target = fetch_california_housing(return_X_y=True, as_frame=True)
scoring = "neg_mean_squared_error"
metric_function = mean_squared_error
metric_name = "mse"
LGBMBooster = LGBMRegressor
objective_type = "SquaredLoss"
else:
data, target = fetch_covtype(return_X_y=True, as_frame=True)
scoring = "neg_log_loss"
metric_function = log_loss
metric_name = "log_loss"
LGBMBooster = LGBMClassifier
objective_type = "LogLoss"
[ ]:
X_train, X_test, y_train, y_test = train_test_split(
data, target, test_size=0.2248, random_state=seed
)
print(f"len(X_train): {len(X_train)}")
print(f"len(X_test): {len(X_test)}")
[ ]:
best_cv_results = None
cv_results = None
def save_best_cv_results(study, trial):
global best_cv_results
if study.best_trial.number == trial.number:
best_cv_results = cv_results
[ ]:
def objective_function(trial):
global cv_results
params = {
"seed": seed,
"verbosity": -1,
"n_estimators": n_estimators,
"learning_rate": trial.suggest_float("learning_rate", 0.001, 0.5, log=True),
"min_split_gain": trial.suggest_float("min_split_gain", 1e-6, 1.0, log=True),
"reg_alpha": trial.suggest_float("reg_alpha", 1e-6, 1.0, log=True),
"reg_lambda": trial.suggest_float("reg_lambda", 1e-6, 1.0, log=True),
"colsample_bytree": trial.suggest_float("colsample_bytree", 0.2, 1.0),
"subsample": trial.suggest_float("subsample", 0.2, 1.0),
"subsample_freq": trial.suggest_int("subsample_freq", 1, 10),
"max_depth": trial.suggest_int("max_depth", 3, 33),
"num_leaves": trial.suggest_int("num_leaves", 2, 1024),
"min_child_samples": trial.suggest_int("min_child_samples", 1, 100),
}
model = LGBMBooster(**params)
cv_results = cross_validate(
model,
X_train,
y_train,
cv=5,
scoring=scoring,
return_train_score=True,
return_estimator=True,
)
return -1 * np.mean(cv_results["test_score"])
[ ]:
sampler = optuna.samplers.TPESampler(seed=seed)
study = optuna.create_study(direction="minimize", sampler=sampler)
[ ]:
study.optimize(objective_function, n_trials=n_trials, callbacks=[save_best_cv_results])
[ ]:
print(f"Number of finished trials: {len(study.trials)}")
print("Best trial:")
print(f" Number: {study.best_trial.number}")
print(f" Value: {study.best_trial.value}")
print(" Params: ")
for key, value in study.best_trial.params.items():
print(f" {key}: {value}")
[ ]:
print(f"CV train scores: {-1 * best_cv_results['train_score']}")
print(
f"CV train scores average : {round(np.mean(-1 * best_cv_results['train_score']), 6)}"
)
print(f"CV valid scores: {-1 * best_cv_results['test_score']}")
print(
f"CV valid scores average : {round(np.mean(-1 * best_cv_results['test_score']), 6)}"
)
[ ]:
models = best_cv_results["estimator"]
[ ]:
for i, model in enumerate(models):
y_pred = (
model.predict_proba(X_train)
if metric_name == "log_loss"
else model.predict(X_train)
)
print(
f"Model {i}, train {metric_name}: {round(metric_function(y_train, y_pred), 6)}"
)
[ ]:
for i, model in enumerate(models):
y_pred = (
model.predict_proba(X_test)
if metric_name == "log_loss"
else model.predict(X_test)
)
print(f"Model {i}, test {metric_name}: {round(metric_function(y_test, y_pred), 6)}")
[ ]:
if metric_name == "log_loss":
y_pred = np.mean([model.predict_proba(X_train) for model in models], axis=0)
else:
y_pred = np.mean([model.predict(X_train) for model in models], axis=0)
print(f"Train {metric_name}: {round(metric_function(y_train, y_pred), 6)}")
[ ]:
if metric_name == "log_loss":
y_pred = np.mean([model.predict_proba(X_test) for model in models], axis=0)
else:
y_pred = np.mean([model.predict(X_test) for model in models], axis=0)
print(f"Test {metric_name}: {round(metric_function(y_test, y_pred), 6)}")
LightGBM n_estimators |
Seed |
LightGBM mse |
LightGBM cpu time |
|---|---|---|---|
100 |
0 |
0.186588 |
729 |
100 |
1 |
0.194348 |
1294 |
100 |
2 |
0.197862 |
990 |
100 |
3 |
0.188629 |
1143 |
100 |
4 |
0.194338 |
860 |
100 |
avg |
0.192196 |
978 |
300 |
0 |
0.185100 |
2282 |
300 |
1 |
0.192767 |
3650 |
300 |
2 |
0.190481 |
2746 |
300 |
3 |
0.182359 |
2782 |
300 |
4 |
0.191614 |
3871 |
300 |
avg |
0.188464 |
3066 |
1000 |
0 |
0.179158 |
9615 |
1000 |
1 |
0.190866 |
7258 |
1000 |
2 |
0.188030 |
10997 |
1000 |
3 |
0.179903 |
7636 |
1000 |
4 |
0.190033 |
8095 |
1000 |
avg |
0.185598 |
8720 |
[ ]:
model = PerpetualBooster(objective=objective_type)
[ ]:
model.budget = 1.0
model.fit(X_train, y_train)
[ ]:
if metric_name == "log_loss":
y_pred = model.predict_proba(X_test)
else:
y_pred = model.predict(X_test)
print(f"Test {metric_name}: {round(metric_function(y_test, y_pred), 6)}")
[ ]:
model.number_of_trees