Python のハイパーパラメータ自動最適化ライブラリー Optuna その3
– scikit-learnのモデルをOptunaでCV(クロスバリデーション)を実施する方法 –

Python のハイパーパラメータ自動最適化ライブラリー Optuna その3– scikit-learnのモデルをOptunaでCV(クロスバリデーション)を実施する方法 –

scikit-learnのモデルをOptunaCV(クロスバリデーション)を実施する方法は2種類あります。

  • 良し悪しを判断するメトリクスにscikit-learnのCVを指定する方法
  • OptunaのCV関数(OptunaSearchCV)を使う方法

前回乳がんの例では、実は「良し悪しを判断するメトリクスにscikit-learnのCVを指定する方法」で実施していました。

最初に前回のコードを振り返り、その後にOptunaCV関数(OptunaSearchCV)を使う方法を紹介します。

先ずは手順の復習です。

Optuna の手順の復習

Optunaは、目的関数(objective function)を設定し最適化を目指すことでより良いハイパーパラメータの組み合わせを探索します。

以下、ざっくり手順です。

  • ステップ1:目的関数を設定する
    • 各モデルのハイパーパラメータの集合を定義する
    • 良し悪しを判断するメトリクスを定義する
  • ステップ2:目的関数の最適化を実行する
  • ステップ3:最適解を利用する

ステップ4で試行回数(n_trials)を設定し実行します。試行回数が多いほど時間が掛かりますが、より良い解になる可能性が高くなります

 

分類問題(乳がんデータ)

前回説明した分類問題(乳がんデータ)の例です。

良し悪しを判断するメトリクスを、どのように設定したのかを見てみます。Step 4のところです(色を変えています)。scikit-learnCV(クロスバリデーション)cross_val_score関数を使っています。

以下、前回のコードです。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
# 目的関数の設定(ステップ1)
def objective(trial):
#ハイパーパラメータの集合を定義する
##推定器の集合
classifier_name = trial.suggest_categorical("classifier",
["LogReg",
"SVC",
"RandomForest"
]
)
##推定器ごとのハイパーパラメータの集合
###ロジ回(LogReg)
if classifier_name == 'LogReg':
logreg_c = trial.suggest_float("logreg_c",
1e-10, 1e10,
log=True
)
classifier_obj = linear_model.LogisticRegression(C=logreg_c)
###サポートベクターマシン(SVC)
elif classifier_name == "SVC":
svc_c = trial.suggest_float("svc_c",
1e-10, 1e10,
log=True
)
classifier_obj = sklearn.svm.SVC(C=svc_c,
gamma="auto"
)
###ランダムフォレスト(RandomForest)
else:
rf_n_estimators = trial.suggest_int("rf_n_estimators",
10, 1000
)
rf_max_depth = trial.suggest_int("rf_max_depth",
2, 50,
log=True
)
classifier_obj = ensemble.RandomForestClassifier(
max_depth=rf_max_depth,
n_estimators=rf_n_estimators
)
#良し悪しを判断するメトリクスを定義する
##CVの実施
score = model_selection.cross_val_score(classifier_obj,
X,
y,
n_jobs=-1,
cv=10
)
##CVの結果の平均値
accuracy = score.mean()
return accuracy
# 目的関数の最適化を実行する(ステップ2)
study = optuna.create_study(direction="maximize")
study.optimize(objective, n_trials=100)
# 最適解の出力
print(f"The best value is : \n {study.best_value}")
print(f"The best parameters are : \n {study.best_params}")
# 目的関数の設定(ステップ1) def objective(trial): #ハイパーパラメータの集合を定義する ##推定器の集合 classifier_name = trial.suggest_categorical("classifier", ["LogReg", "SVC", "RandomForest" ] ) ##推定器ごとのハイパーパラメータの集合 ###ロジ回(LogReg) if classifier_name == 'LogReg': logreg_c = trial.suggest_float("logreg_c", 1e-10, 1e10, log=True ) classifier_obj = linear_model.LogisticRegression(C=logreg_c) ###サポートベクターマシン(SVC) elif classifier_name == "SVC": svc_c = trial.suggest_float("svc_c", 1e-10, 1e10, log=True ) classifier_obj = sklearn.svm.SVC(C=svc_c, gamma="auto" ) ###ランダムフォレスト(RandomForest) else: rf_n_estimators = trial.suggest_int("rf_n_estimators", 10, 1000 ) rf_max_depth = trial.suggest_int("rf_max_depth", 2, 50, log=True ) classifier_obj = ensemble.RandomForestClassifier( max_depth=rf_max_depth, n_estimators=rf_n_estimators ) #良し悪しを判断するメトリクスを定義する ##CVの実施 score = model_selection.cross_val_score(classifier_obj, X, y, n_jobs=-1, cv=10 ) ##CVの結果の平均値 accuracy = score.mean() return accuracy # 目的関数の最適化を実行する(ステップ2) study = optuna.create_study(direction="maximize") study.optimize(objective, n_trials=100) # 最適解の出力 print(f"The best value is : \n {study.best_value}") print(f"The best parameters are : \n {study.best_params}")
# 目的関数の設定(ステップ1)
def objective(trial):

    #ハイパーパラメータの集合を定義する

    ##推定器の集合
    classifier_name = trial.suggest_categorical("classifier", 
                                                ["LogReg",
                                                 "SVC",
                                                 "RandomForest"
                                                ]
                                               )

    ##推定器ごとのハイパーパラメータの集合
    ###ロジ回(LogReg)
    if classifier_name == 'LogReg':
        logreg_c = trial.suggest_float("logreg_c",
                                       1e-10, 1e10, 
                                       log=True
                                      )
        classifier_obj = linear_model.LogisticRegression(C=logreg_c)
    ###サポートベクターマシン(SVC)
    elif classifier_name == "SVC":
        svc_c = trial.suggest_float("svc_c",
                                    1e-10, 1e10,
                                    log=True
                                   )
        classifier_obj = sklearn.svm.SVC(C=svc_c,
                                         gamma="auto"
                                        )
    ###ランダムフォレスト(RandomForest)
    else:
        rf_n_estimators = trial.suggest_int("rf_n_estimators",
                                            10, 1000
                                           )
        rf_max_depth = trial.suggest_int("rf_max_depth", 
                                         2, 50,
                                         log=True
                                        )
        classifier_obj = ensemble.RandomForestClassifier(
            max_depth=rf_max_depth,
            n_estimators=rf_n_estimators
        )

    #良し悪しを判断するメトリクスを定義する
    ##CVの実施
    score = model_selection.cross_val_score(classifier_obj,
                                            X,
                                            y,
                                            n_jobs=-1,
                                            cv=10
                                           )
    ##CVの結果の平均値
    accuracy = score.mean()
    return accuracy

# 目的関数の最適化を実行する(ステップ2)
study = optuna.create_study(direction="maximize")
study.optimize(objective, n_trials=100)

# 最適解の出力
print(f"The best value is : \n {study.best_value}")
print(f"The best parameters are : \n {study.best_params}")

 

以下、実行結果です。

 

最良なモデルランダムフォレストです。

 

次に、このランダムフォレストに対し、OptunaCV関数(OptunaSearchCV)を使い、CV(クロスバリデーション)を実施してみたいと思いますが……

……ただ、先程の例ではロジスティック回帰SVMランダムフォレスト3つのアルゴリズム(数理モデル)を対象にしているため、OptunaのCV関数(OptunaSearchCV)を使った例と見比べるとき、やや面倒です。

 

そのため、ランダムフォレストのみに対し、scikit-learnのCVの関数(model_selection.cross_val_score)を指定するコード例を示します。

以下、コードです。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
# ライブラリーの読み込み
import optuna
import pandas as pd
import sklearn.svm
from sklearn import linear_model
from sklearn import ensemble
from sklearn import datasets
from sklearn import model_selection
# サンプルデータの読み込み(乳がんデータ)
X,y = datasets.load_breast_cancer(return_X_y=True, as_frame=True)
# 目的関数の設定(ステップ1)
def objective(trial):
#ハイパーパラメータの集合を定義する
model = ensemble.RandomForestClassifier()
params = {"n_estimators":trial.suggest_int("n_estimators",
10,1000
),
"max_depth":trial.suggest_int("max_depth",
2, 50,
log=True
)
}
classifier_obj = model.set_params(**params)
#良し悪しを判断するメトリクスを定義する
score = model_selection.cross_val_score(classifier_obj,
X,
y,
n_jobs=-1,
cv=10
)
accuracy = score.mean()
return accuracy
# 目的関数の最適化を実行する(ステップ2)
study = optuna.create_study(direction="maximize")
study.optimize(objective, n_trials=100)
# 最適解の出力
print(f"The best value is : \n {study.best_value}")
print(f"The best parameters are : \n {study.best_params}")
# ライブラリーの読み込み import optuna import pandas as pd import sklearn.svm from sklearn import linear_model from sklearn import ensemble from sklearn import datasets from sklearn import model_selection # サンプルデータの読み込み(乳がんデータ) X,y = datasets.load_breast_cancer(return_X_y=True, as_frame=True) # 目的関数の設定(ステップ1) def objective(trial): #ハイパーパラメータの集合を定義する model = ensemble.RandomForestClassifier() params = {"n_estimators":trial.suggest_int("n_estimators", 10,1000 ), "max_depth":trial.suggest_int("max_depth", 2, 50, log=True ) } classifier_obj = model.set_params(**params) #良し悪しを判断するメトリクスを定義する score = model_selection.cross_val_score(classifier_obj, X, y, n_jobs=-1, cv=10 ) accuracy = score.mean() return accuracy # 目的関数の最適化を実行する(ステップ2) study = optuna.create_study(direction="maximize") study.optimize(objective, n_trials=100) # 最適解の出力 print(f"The best value is : \n {study.best_value}") print(f"The best parameters are : \n {study.best_params}")
# ライブラリーの読み込み
import optuna
import pandas as pd
import sklearn.svm
from sklearn import linear_model
from sklearn import ensemble
from sklearn import datasets
from sklearn import model_selection

# サンプルデータの読み込み(乳がんデータ)
X,y = datasets.load_breast_cancer(return_X_y=True, as_frame=True)

# 目的関数の設定(ステップ1)
def objective(trial):
    
    #ハイパーパラメータの集合を定義する
    model = ensemble.RandomForestClassifier()

    params = {"n_estimators":trial.suggest_int("n_estimators",
                                               10,1000
                                              ),
              "max_depth":trial.suggest_int("max_depth",
                                            2, 50,
                                            log=True
                                           )
             }
    classifier_obj = model.set_params(**params)

    #良し悪しを判断するメトリクスを定義する
    score = model_selection.cross_val_score(classifier_obj,
                                            X,
                                            y,
                                            n_jobs=-1,
                                            cv=10
                                           )
    accuracy = score.mean()
    return accuracy

# 目的関数の最適化を実行する(ステップ2)
study = optuna.create_study(direction="maximize")
study.optimize(objective, n_trials=100)

# 最適解の出力
print(f"The best value is : \n {study.best_value}")
print(f"The best parameters are : \n {study.best_params}")

 

以下、実行結果です。

 

OptunaのCV関数(OptunaSearchCV)を使う方法

Optunaで、scikit-learnのモデルをCV(クロスバリデーション)を実施するとき、OptunaSearchCVを使っても実施できます。

今回は、ランダムフォレストで実施してみます。

簡単な流れは以下です。

  1. モデル設定
  2. ハイパーパラメータ探索の設定
  3. CV(クロスバリデーション)の設定
  4. ハイパーパラメータの探索の実施
  5. 最適解の出力

 

先ず、モデルの設定です。ランダムフォレストです。

以下、コードです。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
# ライブラリーの読み込み
import optuna
import pandas as pd
from sklearn import ensemble
from sklearn import datasets
# サンプルデータの読み込み(乳がんデータ)
X,y = datasets.load_breast_cancer(return_X_y=True, as_frame=True)
# モデル設定
model = ensemble.RandomForestClassifier()
# ライブラリーの読み込み import optuna import pandas as pd from sklearn import ensemble from sklearn import datasets # サンプルデータの読み込み(乳がんデータ) X,y = datasets.load_breast_cancer(return_X_y=True, as_frame=True) # モデル設定 model = ensemble.RandomForestClassifier()
# ライブラリーの読み込み
import optuna
import pandas as pd

from sklearn import ensemble
from sklearn import datasets

# サンプルデータの読み込み(乳がんデータ)
X,y = datasets.load_breast_cancer(return_X_y=True, as_frame=True)

# モデル設定
model = ensemble.RandomForestClassifier()

 

次に、ハイパーパラメータ探索の設定です。

OptunaSearchCVでは、param_distributions引数を使用してハイパーパラメータの探索空間を定義します。

通常の設定時(前回)とは異なる関数を使って行いますが、やっていることはほぼ同じす。

  • optuna.distributions.FloatDistribution実数値の範囲を指定するための分布。一様分布や対数一様分布などを選択可能。
  • optuna.distributions.IntDistribution整数値の範囲を指定するための分布。
  • optuna.distributions.CategoricalDistribution:カテゴリ変数のサンプリング。

 

以下、コードです。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
# ハイパーパラメータの探索の設定
params = {
"n_estimators":optuna.distributions.IntDistribution(10,1000),
"max_depth":optuna.distributions.IntDistribution(2,50,log=True)
}
# ハイパーパラメータの探索の設定 params = { "n_estimators":optuna.distributions.IntDistribution(10,1000), "max_depth":optuna.distributions.IntDistribution(2,50,log=True) }
# ハイパーパラメータの探索の設定
params = {
    "n_estimators":optuna.distributions.IntDistribution(10,1000),
    "max_depth":optuna.distributions.IntDistribution(2,50,log=True)
}

 

CV(クロスバリデーション)の設定をします。

以下、コードです。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
# CV(クロスバリデーション)の設定
optuna_search = optuna.integration.OptunaSearchCV(model,
params,
cv=10,
n_jobs=-1,
n_trials=100,
scoring='accuracy'
)
# CV(クロスバリデーション)の設定 optuna_search = optuna.integration.OptunaSearchCV(model, params, cv=10, n_jobs=-1, n_trials=100, scoring='accuracy' )
# CV(クロスバリデーション)の設定
optuna_search = optuna.integration.OptunaSearchCV(model,
                                                  params,
                                                  cv=10,
                                                  n_jobs=-1,
                                                  n_trials=100,
                                                  scoring='accuracy'
                                                 )

 

では、CVで評価しながらハイパーパラメータの探索を実施します。

以下、コードです。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
# ハイパーパラメータの探索の実施
optuna_search.fit(X, y)
# ハイパーパラメータの探索の実施 optuna_search.fit(X, y)
# ハイパーパラメータの探索の実施
optuna_search.fit(X, y)

 

結果を見てみます。

以下、コードです。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
# 最適解の出力
print(f"The best value is : \n {optuna_search.best_score_}")
print(f"The best parameters are : \n {optuna_search.best_params_}")
# 最適解の出力 print(f"The best value is : \n {optuna_search.best_score_}") print(f"The best parameters are : \n {optuna_search.best_params_}")
# 最適解の出力
print(f"The best value is : \n {optuna_search.best_score_}")
print(f"The best parameters are : \n {optuna_search.best_params_}")

 

以下、実行結果です。

 

まとめ

scikit-learnのモデルをOptunaCV(クロスバリデーション)を実施する方法は2種類あります。

  • 良し悪しを判断するメトリクスにscikit-learnのCVを指定する方法
  • OptunaのCV関数(OptunaSearchCV)を使う方法

今回は、前回の乳がんの例の分類問題に対し、OptunaCV関数(OptunaSearchCV)を使う方法を紹介しました。

非常に手軽にできますので、興味ある方は一度チャレンジしてみてください。

Python のハイパーパラメータ自動最適化ライブラリー Optuna その4 – 時系列Facebook ProphetモデルをOptunaで自動最適化 –