Tune ML Models Efficiently with Optuna: Focus on XGBoost
Hyperparameter tuning is a crucial but often time-consuming part of building high-performing machine learning models. The choice of hyperparameter values like learning rate, regularization strength, number of trees, etc. can dramatically impact a model‘s speed and performance on a task. However, manually testing all possible hyperparameter combinations is usually infeasible.
Thankfully, there are automated approaches to intelligently search for optimal hyperparameter configurations. In this post, we‘ll dive into Optuna, a popular open-source hyperparameter optimization framework. We‘ll see how Optuna can help you tune machine learning models like XGBoost efficiently and achieve better performance faster.
Hyperparameter Tuning Approaches
Before we jump into Optuna, let‘s review some common hyperparameter tuning techniques:
Grid Search: Exhaustively searches through a manually specified set of hyperparameter values. Simple but computationally expensive, especially with many hyperparameters.
Random Search: Samples hyperparameter values randomly from a defined distribution. Can be more efficient than grid search.
Bayesian Optimization: Builds a probabilistic model of the objective function (e.g. validation accuracy) and uses it to select the most promising hyperparameter values to evaluate next. Aims to minimize the number of iterations required to find the optimal values.
Bayesian optimization techniques like Gaussian Processes and Tree-structured Parzen Estimators (TPE) have been shown to outperform random search for hyperparameter optimization in many cases. Optuna employs TPE to make the tuning process more efficient.
Introducing Optuna
Developed by researchers at Preferred Networks, Optuna is an open-source automatic hyperparameter optimization framework designed for machine learning. Key features include:
- Lightweight, versatile framework that can be used with any ML library
- Supports various sampling algorithms to accommodate different hyperparameter search spaces
- Intelligent pruning of unpromising trials to save time and resources
- Easy parallelization of multiple optimization trials
- Flexible definition of hyperparameter search spaces using familiar Python syntax
- Interactive visualization of optimization results
Optuna‘s selling point is its efficient and automated optimization of hyperparameters using Bayesian optimization with minimal human input required. By intelligently selecting the set of hyperparameter values to evaluate at each step based on previous results, it aims to find optimal hyperparameter values in fewer iterations compared to random or grid search.
Tuning an XGBoost Model with Optuna
Now let‘s see how we can use Optuna to tune a gradient boosted trees model with XGBoost. We‘ll optimize an XGBoost model on the UCI Housing dataset to walk through the steps. The full code is available here.
First we load the data and split it into train and test sets:
import sklearn.datasets
import xgboost as xgb
from sklearn.model_selection import train_test_split
data, target = sklearn.datasets.fetch_california_housing(return_X_y=True)
X_train, X_test, y_train, y_test = train_test_split(data, target, test_size=0.25)
dtrain = xgb.DMatrix(X_train, label=y_train)
dtest = xgb.DMatrix(X_test, label=y_test)
Next, we define an objective function that takes a set of hyperparameters (as a trial object) and returns a score to minimize, in this case the mean squared error on a validation set:
import optuna
def objective(trial):
param = {
"verbosity": 0,
"objective": "reg:squarederror",
"n_estimators": trial.suggest_int("n_estimators", 50, 300, step=50),
"max_depth": trial.suggest_int("max_depth", 2, 10),
"learning_rate": trial.suggest_float("learning_rate", 1e-4, 1e-1, log=True),
"subsample": trial.suggest_float("subsample", 0.2, 1.0),
"colsample_bytree": trial.suggest_float("colsample_bytree", 0.2, 1.0),
}
bst = xgb.train(param, dtrain)
preds = bst.predict(dtest)
mse = mean_squared_error(y_test, preds)
return mse
A few things to note here:
- We use the
trial.suggest_*methods to define the hyperparameter search space. These allow us to specify the range of values for both continuous (e.g.learning_rate) and discrete (e.g.max_depth) hyperparameters. - For
learning_rate,log=Trueis used to sample values from a log-uniform distribution, which is useful for searching over several orders of magnitude. - The
objectivereturns a validation MSE score for Optuna to minimize.
With the objective function defined, we can now set up and run the Optuna optimization:
study = optuna.create_study(direction="minimize")
study.optimize(objective, n_trials=100)
print("Best score:", study.best_value)
print("Best params:", study.best_params)
Here we create an Optuna study object and tell it we want to minimize the objective. We kick off the optimization process by calling study.optimize, specifying the objective function and the number of trials (n_trials) to run.
Optuna will intelligently sample hyperparameter values and prune unpromising trials along the way. At the end, we can access the best score and hyperparameter values found via study.best_value and study.best_params.
Visualizing Hyperparameter Importance
One useful feature of Optuna is the ability to visualize the hyperparameter optimization results. This can give insights into which hyperparameters were most important and help inform future searches.
We can create an importance plot using optuna.visualization.plot_param_importances():
optuna.visualization.plot_param_importances(study)

This plot shows the hyperparameters ranked by importance according to a random forest model trained on the optimization results. We can see that learning_rate and n_estimators had the biggest impact on XGBoost performance for this dataset.
We can also visualize the optimization history to see how the best score improved over time:
optuna.visualization.plot_optimization_history(study)

Comparing Tuned vs Default XGBoost
Finally, let‘s compare the performance of our tuned XGBoost model to the default model to see how much difference tuning made:
default_params = {
"verbosity": 0,
"objective": "reg:squarederror",
}
bst = xgb.train(default_params, dtrain)
preds = bst.predict(dtest)
default_mse = mean_squared_error(y_test, preds)
print(f"Default MSE: {default_mse:.2f}")
print(f"Tuned MSE: {study.best_value:.2f}")
Default MSE: 0.56
Tuned MSE: 0.45
We can see that hyperparameter tuning with Optuna gave a nice boost over the default settings, reducing the test MSE from 0.56 to 0.45.
Of course, the impact of tuning will depend on the model and dataset. But Optuna makes it simple to optimize any ML model with an efficient, automated approach. Beyond just XGBoost, it can be readily applied to tune models like LightGBM, CatBoost, sklearn estimators, Keras models, and more with minimal code changes.
Tips for Efficient Optuna Usage
To make the most of Optuna for hyperparameter tuning, keep these tips in mind:
- Be thoughtful about defining your hyperparameter search spaces. Use appropriate ranges and distributions (log vs uniform vs int). Visualizing the search results can help inform this.
- Set
n_trialshigh enough to adequately explore the search space, but not so high that it takes prohibitively long. You can always increasen_trialsand rerun if needed. - Utilize Optuna‘s pruning functionality by setting an early stopping threshold via
trial.set_user_attr. This allows you to stop unpromising trials early. - Take advantage of parallelization if you have the resources. Optuna makes it easy to distribute optimization trials across multiple cores or machines.
- Use the visualization functions to analyze and learn from the optimization process. The importance plot can inform which hyperparameters to focus on, while the optimization history shows the progression of the best score over time.
Conclusion
Hyperparameter tuning is a key part of building accurate, efficient machine learning models, but it doesn‘t have to be a manual slog. Optuna offers an automated, scalable approach to hyperparameter optimization, leveraging Bayesian techniques to intelligently search the space of possible configurations.
As we saw in the XGBoost example, Optuna makes it straightforward to optimize an ML model. With just a few lines of code to define the hyperparameter ranges and objective function, Optuna can find high-performing hyperparameter values and provide informative visualizations of the tuning process.
While we focused on XGBoost here, Optuna is a versatile framework that isn‘t limited to any particular model. Give it a shot the next time you need to tune hyperparameters and see how much more efficient the process can be! The time saved can be much better spent on other aspects of the ML pipeline, like feature engineering or model analysis.
I hope this guide has been a helpful introduction to Optuna and how it can accelerate your machine learning projects. Let me know if you have any other questions!