機械学習のハイパーパラメータの値によってモデルの精度が大きく変わることがある。
SVMのハイパーパラメータに対して、グリッドサーチ(Grid Search)とベイズ最適化(Bayesian Optimization)の精度を比較した。
ベイズ最適化には、ライブリscikit-optimize(skopt)を用いた。
作成したコードはGoogle colab上で実装し、GitHubにあります。
https://github.com/JunJun100/BayesianOptimization
SVMのハイパーパラメータ
SVMにはハイパーパラメータである、gammaとCの2つある。
ハイパーパラメータの値によって精度が低い、あるいは過学習が起きる。
以下の図では、(gamma=10^-1, C=10^0)が最も精度がよい。
グリッドサーチ
グリッドサーチは、探索範囲を格子状(グリッド)にする。
予めパラメータの探索候補値を与え、全ての組み合わせに対して計算する。
シンプルなアルゴリズムだが、計算時間がかかる。
ベイズ最適化
グリッドサーチはシンプルなアルゴリズムだが、探索に時間がかかる。
より効率的に探索するのがベイズ最適化である。
ベイズ最適化では、正答率が高そうなエリアを中心に探索する。
しかし、局所解に陥らないよう定期的に他のエリアも探索する。
人間の感覚のように、それっぽいエリアの探索をベイズ最適化は実現できる。
実装
設定
# 乱数のシードを設定
import random
import numpy as np
np.random.seed(1234)
random.seed(1234)
# Breast cancer dataset for binary classification
import pandas as pd
from sklearn.datasets import load_breast_cancer
import time
from sklearn.model_selection import GridSearchCV
from sklearn.metrics import f1_score
from sklearn.svm import SVC
# Bayesian Optimization
!pip install scikit-optimize
from skopt import BayesSearchCV
from skopt.space import Real, Categorical, Integer
データの読み込み
# set x and y
dataset = load_breast_cancer()
X = pd.DataFrame(dataset.data, columns = dataset.feature_names)
y = pd.Series(dataset.target, name = 'y')
X.join(y).head(3)
Grid Search
# パラメータグリッドの設定
param_grid_svc = {
'C' : [0.1, 1, 10, 50, 100, 150, 200, 250, 300, 350, 400, 450, 500],
'gamma' : [1e-8, 1e-7, 1e-6, 1e-5, 1e-4, 1e-3, 1e-2, 1e-1, 1.0, 2.0]
}
print('探索空間: ', param_grid_svc)
探索回数としては、13*10 = 130となる。
# Grid Search
t1 = time.time() # 時間計測開始
gs_svc = GridSearchCV(estimator = SVC(random_state = 1),
param_grid = param_grid_svc,
scoring = 'f1',
cv = 5,
return_train_score = False
)
gs_svc.fit(X, y)
t2 = time.time() # 時間計測終了
print('{:.2f}秒かかった'.format(t2 - t1))
# 実行結果:10.53秒かかった
# テストスコアの確認
df_gs_tmp = pd.DataFrame(gs_svc.cv_results_)
df_gs = df_gs_tmp.sort_values('rank_test_score', ascending = True)
df_gs.head()
上位3つのmean_test_scoreは0.966832と同順位である。
gammaは1e-05,Cは[400, 500]の間に最適解がある。
# Best Hyperparameter
gs_svc.best_params_
# 実行結果:{'C': 400, 'gamma': 1e-05}
Bayesian Optimization
# 探索範囲の設定
param_bayes_svc = {
'C' : Real(0.1, 500.0, prior = 'log-uniform'),
'gamma' : Real(1e-8, 2.0, prior = 'log-uniform')
}
print('探索空間: ', param_bayes_svc)
今回の探索は30回(n_iter=30)に設定した。
# Bayes Search
t1 = time.time() # 時間計測開始
bo_svc = BayesSearchCV(estimator = SVC(random_state = 1),
search_spaces = param_bayes_svc,
scoring = 'f1',
cv = 5,
n_iter = 30,
return_train_score = False
)
bo_svc.fit(X, y)
t2 = time.time() # 時間計測終了
print('{:.2f}秒かかった'.format(t2 - t1))
# 実行結果:24.83秒かかった
↑Grid Searchの2倍以上の時間がかかった。
(n_iterを増やすと精度は上がるが、計算時間が大きく増える)
# テストスコアの確認
df_bo_tmp = pd.DataFrame(bo_svc.cv_results_)
df_bo = df_bo_tmp.sort_values('rank_test_score', ascending = True)
df_bo.head()
mean_test_scoreのベストスコアを見ると、0.966836
Grid Searchののベストスコアは、0.966832
# Best Hyperparameter
bo_svc.best_params_
# 以下、実行結果:
# OrderedDict([('C', 499.99999999999994), ('gamma', 9.368957868762131e-06)])
結果
手法 | 計算時間[sec] | 探索回数 | (C, gamma) | f1スコア |
---|---|---|---|---|
グリッドサーチ | 10.53 | 130 | (400~500, 1e-05) | 0.966832 |
ベイズ最適化 | 24.83 | 30 | (500, 9.4e-06) | 0.966836 |
まとめ
ベイズ最適化の方が少ない探索回数で最適解を得られたが、計算時間はグリッドサーチの2倍以上かかった。
ベイズ最適化のライブラリscikit-optimizeは、グリッドサーチと同様に実装することができるので、
実装が容易である。
他のベイズ最適化のライブラリ(GPyOptやBayesianOptimization等)を使えば、
計算時間が改善されるかもしれない。