機械学習において、作成したモデルの精度を高めるためのテクニックのひとつがハイパーパラメータのチューニング(調整)です。
ハイパーパラメータのチューニングの方法としては、手動、グリッドサーチ、ランダムサーチ、ベイズ最適化などの方法があります。
今回は、ベイズ最適化による方法でよく使われるライブラリであるHyperoptとOptunaの使い方を比較するため、同じ問題(Boston house prices dataset)に対して、同じモデル(リッジ回帰; sklearn.linear_model.Ridge
)を用いて、簡単なコードを作成してみました。(あくまでHyperoptとOptunaの使用例を示すためのもので、この問題の解法を示しているわけではございませんので、ご了承ください。)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
from sklearn import datasets from sklearn.linear_model import Ridge from sklearn.model_selection import train_test_split from sklearn.metrics import mean_absolute_error from hyperopt import fmin, tpe, hp, STATUS_OK, Trials, space_eval def score(params): reg = Ridge(**params) reg.fit(X_train, y_train) y_pred = reg.predict(X_test) score = mean_absolute_error(y_test, y_pred) print(f'params: {params}, score: {score:.4f}') return {'loss': score, 'status': STATUS_OK} space = { 'alpha': hp.loguniform('alpha', 0.1, 5.0), 'fit_intercept': hp.choice('fit_intercept', [True, False]), 'normalize': hp.choice('normalize', [True, False]), } if __name__ == '__main__': # データセットをロード (X, y) = datasets.load_boston(return_X_y=True) X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42) # hyperopt によるハイパーパラメータ最適化 trials = Trials() best = fmin(score, space, algo=tpe.suggest, trials=trials, max_evals=100) # 結果を表示 sorted_lst = sorted(trials.trials, key=lambda x: x['result']['loss']) min_loss = sorted_lst[0]['result']['loss'] print(f'best score: {min_loss:.4f}, best params: {space_eval(space, best)}') |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
from sklearn import datasets from sklearn.linear_model import Ridge from sklearn.model_selection import train_test_split from sklearn.metrics import mean_absolute_error import optuna def objective(trial): params = { 'alpha': trial.suggest_loguniform("alpha", 0.1, 5), 'fit_intercept': trial.suggest_categorical('fit_intercept', [True, False]), 'normalize': trial.suggest_categorical('normalize', [True, False]), } reg = Ridge(**params) reg.fit(X_train, y_train) y_pred = reg.predict(X_test) mae = mean_absolute_error(y_test, y_pred) return mae if __name__ == '__main__': # データセットをロード (X, y) = datasets.load_boston(return_X_y=True) X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42) # optuna によるハイパーパラメータ最適化 study = optuna.create_study() study.optimize(objective, n_trials=100) # 結果を表示 print(f'best score: {study.best_value:.4f}, best params: {study.best_params}') |
上がHyperoptのコード例、下がOptunaのコード例です。コードを比較するとわかるように、Hyperoptの方は最適化するモデル(関数)とは別にハイパーパラメータの範囲を定義してfmin
関数に関数とハイパーパラメータの範囲を定義したものを渡していますが、Optunaの方は最適化するモデル(関数)の中でハイパーパラメータの範囲を定義しています。
今回の例では、以下の通り、実行結果はほぼ同じになりました。
1 |
best score: 3.1175, best params: {'alpha': 4.321534442189128, 'fit_intercept': True, 'normalize': False} |
1 |
best score: 3.1174, best params: {'alpha': 4.373698954104559, 'fit_intercept': True, 'normalize': False} |
HyperoptとOptunaは、TPE(Tree-structured Parzen Estimator)という同一の最適化エンジンを使っていますが、枝刈りの機能の貢献により、Optuna の方が効率的に最適化の処理をするようです。
HyperoptとOptunaについての詳しい情報は、以下のリンクをご参照ください。