2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

グリッドサーチとOptunaを実際に使って比較してみた!

Last updated at Posted at 2025-04-25

はじめに

こんにちは。船井総合研究所の平尾です。

とある案件においてXGBoostのパラメータチューニングをグリッドサーチによって行っていました。グリッドサーチによる計算実行時間は約1時間と長かったです。

しかし、先輩社員に「Optuna」というツールの存在を教えてもらい、Optunaを用いたところ実行が約30秒で終わって非常に感動しました。

そこで今回は、scikit-learnのdiabetes(糖尿病データ)に対するモデル構築において、グリッドサーチとOptunaを実際に使って実行時間、精度を比較してみました。

各ツールの概要

グリッドサーチ

グリッドサーチは、指定したハイパーパラメータの値の全ての組み合わせにおいてモデル構築を行う方法です。

以下は例です。

param_grid = {
    'learning_rate':[0.01, 0.05, 0.10, 0.15, 0.20, 0.25, 0.30], 
    'max_depth': [2, 4, 6, 8, 10], 
    'min_child_weight':[0, 1, 2, 3, 4], 
    'subsample':[0.2, 0.4, 0.6, 0.8, 1],
    'colsample_bytree':[0.2, 0.4, 0.6, 0.8, 1], 
    'reg_lambda':[2, 4, 6, 8, 10],
}

組み合わせの総数は21875通りとなります。
実行時間が長くなるのも納得です。

変数として扱うパラメータの数が多いほど計算量が多くなり、実行時間も長くなります。

Optuna

指定したハイパーパラメータの範囲の中で最適と思われる数値の組み合わせを作成し、
モデル学習を行うという流れを指定回数繰り返します。

以下は例です。

param = {
        'learning_rate':trial.suggest_float('learning_rate', 0.01, 0.30),
        'max_depth': trial.suggest_int('max_depth', 2, 10),
        'min_child_weight': trial.suggest_int('min_child_weight', 0, 4),
        'subsample': trial.suggest_float('subsample', 0.2, 1),
        'colsample_bytree': trial.suggest_float('colsample_bytree', 0.2, 1),
        'reg_lambda': trial.suggest_int('reg_lambda', 2, 10),}

このように調整したいパラメータの範囲を指定します。

データの準備

まずはモデル学習できるようにデータを準備します。

ライブラリをインポートします。

import pandas as pd
import numpy as np
import seaborn as sns
from sklearn.datasets import load_diabetes

データフレームを作成します。
(参考:【Python】scikit-learnのdiabetes datasetを使って回帰分析をしてみる)

# データの読み込み
dia = load_diabetes()

# 目的変数
exp_data = pd.DataFrame(dia.data, columns=dia.feature_names)

# 説明変数
tar_data = pd.DataFrame(dia.target, columns=['target'])

# データを結合
df = pd.concat([exp_data, tar_data], axis=1)

df.rename({"s1":"tc", "s2":"ldl", "s3":"hdl", "s4":"tch", "s5":"ltg", "s6":"glu"}, axis=1, inplace=True)
df

dfは次のようになります。
スクリーンショット 2025-04-23 155205.png

10個の説明変数と1つの目的変数からできています。
これを用いて、XGBoostのパラメータチューニングを行います。

グリッドサーチの場合

コードは以下の通りです。(参考:機械学習のパラメータチューニングフレームワークを比較してみた!)

#必要なライブラリのインポート
import xgboost as xgb
from sklearn.model_selection import train_test_split
from sklearn.model_selection import KFold
from sklearn.metrics import root_mean_squared_error
from sklearn.model_selection import GridSearchCV
from sklearn.metrics import r2_score

#目的変数と説明変数に分類し、トレーニングデータとテストデータに分割
X = df.iloc[:,:10]
y = df.iloc[:,10]
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=42)

# 変数として扱うパラメータの値の設定
param_grid = {
    'learning_rate':[0.01, 0.05, 0.10, 0.15, 0.20, 0.25, 0.30], 
    'max_depth': [2, 4, 6, 8, 10], 
    'min_child_weight':[0, 1, 2, 3, 4], 
    'subsample':[0.2, 0.4, 0.6, 0.8, 1],
    'colsample_bytree':[0.2, 0.4, 0.6, 0.8, 1], 
    'reg_lambda':[2, 4, 6, 8, 10],
}

# XGBoostの定数部分を指定。
base_model = xgb.XGBRegressor(
    objective='reg:squarederror',
    booster="gbtree",                             
    n_estimators=100,                         
)

#各パラメータの組み合わせにおいて交差検証
cv = KFold(n_splits=6, shuffle=True, random_state=42) 
grid_search = GridSearchCV(base_model, param_grid, cv=cv, scoring='neg_root_mean_squared_error')                     
grid_search.fit(X_train, y_train)

# 交差検定で最もスコアが良かったときのパラメータを用いてモデルを再学習
pred = grid_search.predict(X_test)
r2 = r2_score(y_test, pred)

#各結果の出力
print("最適パラメータ : " , grid_search.best_params_)
print("最適パラメータにおける交差検定でのRMSEの平均: " , -grid_search.best_score_)
print("最適パラメータにおける最終評価でのRMSE:", root_mean_squared_error(y_test, pred))
print(f"決定係数 (R^2): {r2}")

GridSearchCVを用いてグリッドサーチを行います。
各パラメータの組み合わせにおいて交差検定を行っています。
評価指標としてRMSEを用いています。

各パラメータの組み合わせにおける交差検定において
最も精度が良かった組み合わせを採用し、
テストデータを用いて最終的な精度を出力します。

Optunaの場合

コードは以下の通りです。
(参考:機械学習のパラメータチューニングフレームワークを比較してみた!)

#必要なライブラリのインポート
import optuna
import xgboost as xgb
from sklearn.base import clone
from sklearn.model_selection import train_test_split
from sklearn.model_selection import cross_val_score, KFold
from sklearn.metrics import root_mean_squared_error
from sklearn.metrics import r2_score

#目的変数と説明変数に分類し、トレーニングデータとテストデータに分割
X = df.iloc[:,:10]
y = df.iloc[:,10]
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=42)

#XGBoostの定数部分を指定。
base_model = xgb.XGBRegressor(
    objective='reg:squarederror',
    booster="gbtree",                                
    n_estimators=100                       
)

#チューニングにおける関数
def objective(trial):
#各変数の範囲を指定
    param = {
        'learning_rate':trial.suggest_float('learning_rate', 0.01, 0.30),
        'max_depth': trial.suggest_int('max_depth', 2, 10),
        'min_child_weight': trial.suggest_int('min_child_weight', 0, 4),
        'subsample': trial.suggest_float('subsample', 0.2, 1),
        'colsample_bytree': trial.suggest_float('colsample_bytree', 0.2, 1),
        'reg_lambda': trial.suggest_int('reg_lambda', 2, 10),}
    
    #base_modelに変数を組み込む
    model = clone(base_model).set_params(**param) 

    
    #交差検証
    cv = KFold(n_splits=6, shuffle=True, random_state=42) 
    score = cross_val_score(model, X_train, y_train, cv=cv, scoring='neg_root_mean_squared_error')
    score = -score #scoreを正にする
    mean_score = np.mean(score) #各回におけるscoreを平均
    return mean_score #scoreの平均を出力

# objective関数を用いてパラメータチューニング
study = optuna.create_study(direction='minimize',sampler=optuna.samplers.TPESampler(seed=0)) #結果の再現性を担保
study.optimize(objective, n_trials=100)
best_parameters = study.best_params
best_score = study.best_value

# 交差検定で最もスコアが良かったときのパラメータを用いてモデルを再学習
model = clone(base_model).set_params(**best_parameters) 
model.fit(X_train, y_train)
y_pred = model.predict(X_test)
test_score = root_mean_squared_error(y_test, y_pred)
r2 = r2_score(y_test, y_pred)

#各結果の出力
print('最適パラメータにおける交差検定でのRMSEの平均:',best_score)
print('最適パラメータ :',best_parameters)
print('最適パラメータにおける最終評価でのRMSE:',test_score)
print(f"決定係数 (R^2): {r2}")

指定したハイパーパラメータの範囲の中で最適と思われる数値の組み合わせを作成し、モデル学習を行うという流れを100回繰り返します。
Optunaの場合においても、各パラメータの組み合わせにおいて交差検定を行い、
最も精度が良かった組み合わせを採用し、テストデータを用いて最終的な精度を出力します。
また、以下のコード

study = optuna.create_study(direction='minimize',sampler=optuna.samplers.TPESampler(seed=0)) #結果の再現性を担保

により、各実行において出力結果にばらつきがでないようにしています。
(参考:Google ColaboratoryでOptunaを実行する方法(Studyの再現性を確保する))

精度・実行時間比較

グリッドサーチ Optuna
RMSE(交差検証) 56.5 57.4
RMSE(テスト) 54.1 52.6
決定係数 0.47 0.50
実行時間 50分40秒 9秒

RMSE(交差検証):各回の交差検証において最もRMSEが低くなったときのその値。
RMSE(テスト):各回の交差検証において最もRMSEが低くなったときのパラメータの組み合わせでモデルを再度作成し、そのモデルで測ったRMSEの値。
決定係数:再度作成したモデルが出力した予測データと、正解データを用いて計算した決定係数。
実行時間:上記コードを実行するのにかかった時間。

決定係数を見ればわかる通り、どちらの手法においても
良い精度は得られませんでした。
そもそもXGBoostとデータが合っていなかったのか、説明変数に
なにかしらの手を加えたほうが良かったのか、等々、
いろいろと考察した方が良さそうです。

実行時間に関しては、Optunaはグリッドサーチに比べて圧倒的に計算が早かったです。
Optunaでは100回試行、グリッドサーチでは約2万回試行なので、
それはそうであろうという結果でした。

パラメータ比較

グリッドサーチ Optuna
learning_rate 0.1 0.068
max_depth 2 2
min_child_weight 2 4
subsample 0.6 0.22
colsample_bytree 0.2 0.85
reg_lambda 4 7

learning_rateとmax_depthにおいては、
グリッドサーチとOptunaとで近しいor等しい数値となりました。
それ以外はバラバラの数値となりました。

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?