はじめに
こちらの記事ではハイパーパラメータの最適化手法について、次の内容をまとめています。
- グリッドサーチの概念と実行コード
- ランダムサーチの概念と実行コード
- グリッドサーチとランダムサーチの使い分け
ハイパーパラメータ
ハイパーパラメータとは、モデルが学習を開始する前に、ユーザが値を指定する必要のある設定値のこと。
ハイパーパラメータの例
| モデル | パラメータ名 | 内容 | 値の種類 |
|---|---|---|---|
| DecisionTreeClassifier | max_depth | ツリーの最大深さ | 整数 |
| RandomForestClassifier | n_estimators | 構成する木の数 | 整数 |
| LogisticRegression | C | 正則化の強さ(小さいほど強い) | 連続値(スケール依存) |
| LogisticRegression | penalty | 正則化手法 | カテゴリ |
| SVC | gamma | "rbf" カーネルの影響度 | 連続値(スケール依存) |
| SVC | kernel | カーネルの種類 | カテゴリ |
| KMeans | n_clusters | クラスタ数 | 整数 |
| KNN | n_neighbors | 近傍点の数 K | 整数 |
| KNN | weights | 重み付け方法 | カテゴリ |
| 任意のGDモデル | learning_rate | 学習率 | 連続値 |
最適なハイパーパラメータを選択する手法には、グリッドサーチとランダムサーチがある。
グリッドサーチ
グリッドサーチとは、ハイパーパラメータの値の候補を指定し、その候補の組み合わせ全てを試行することで最適な値を見つける手法のこと。
全ての値で試すので、網羅的な探索ができる一方で、範囲が広いと計算コストが増大する。
グリッドサーチを実行するには、sklearn.model_selectionモジュールのGridSearchCVクラスが持つ、fit()メソッドを利用する。
GridSearchCVのコンストラクタ引数
-
第1引数: モデルオブジェクト -
param_grid: 辞書オブジェクト{"パラメータ名" : [値の候補リスト]} -
cv: 分割数(int) or 分割オブジェクト(StratifiedKFold,KFold) -
n_jobs: 計算に使用するCPU数。-1で全使用 -
scoring: 評価指標(str)
fit(X_train, y_train)
fit()メソッドの引数に学習用の説明データと目的データを渡して実行すると、
- 最適なハイパーパラメータの探索
- そのハイパーパラメータを使用したモデルの学習
を行い、さらに
- その最適パラメータとその値(辞書型)を
best_params_属性に - その学習済みモデルを自身の
best_estimator_属性に
それぞれ格納する。
サンプルコード
以下はirisのデータセットを分類する決定木モデルの'木の深さ'を設定するmax_depthパラメータの値を、グリッドサーチで決定するコードである。
from sklearn.model_selection import GridSearchCV,train_test_split,StratifiedKFold
from sklearn.datasets import load_iris
from sklearn.tree import DecisionTreeClassifier
# データの取得と分割
iris = load_iris()
X, y = iris.data, iris.target
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=123,shuffle=True, stratify=y)
# モデルの作成
model = DecisionTreeClassifier(random_state=123)
# グリッドサーチオブジェクトの作成
param_grid = {"max_depth" : [3, 4, 5]}
fold = StratifiedKFold(n_splits=10, shuffle=True, random_state=123)
grid_search = GridSearchCV(model, param_grid=param_grid, cv=fold)
# グリッドサーチの実行
grid_search.fit(X_train, y_train)
# 結果の確認
print(grid_search.best_params_)
print(grid_search.best_estimator_)
{'max_depth': 4}
DecisionTreeClassifier(max_depth=4, random_state=123)
GridSearchCVのcvには整数値を渡すこともできるが、その場合は分割数のみの指定となる。
一方で、KFoldやStrarifiedKFoldオブジェクトを渡せば、これらのオブジェクト生成時に設定したshuffle,random_state引数の値で分割を行うことができる。
StratifiedKFoldはクラスの層化がされた分割となる。
ランダムサーチ
ハイパーパラメータの値の範囲と試行回数を指定し、その中からランダムに選んだ値で試行することで最適な値を見つける手法がランダムサーチ。
グリッドサーチのように全ての値を試すわけではないので、効率的に探索することができる。
RandomizedSearchCVのコンストラクタ引数
-
第1引数: モデルオブジェクト -
param_distribution: 辞書オブジェクト{"パラメータ名" : 確率分布オブジェクト} -
cv: 分割数(int) or 分割オブジェクト(StratifiedKFold,KFold) -
n_iter: 試行回数(int) -
n_jobs: 計算に使用するCPU数-1の場合は全てのCPU -
scoring: 評価指標(str)
確率分布オブジェクト
param_distribution引数に渡す辞書の値には確率分布オブジェクトを指定する。確率分布オブジェクトは、「サンプリングされる値がどのようなパターンで出現するか」をRandomizedSearchCVに指示する役割を持つ。
例えば値の範囲が 0.00001〜1000 で、最適な値が 1 以下にあるような場合、1以下の数値がサンプリングされる可能性はかなり低いので、適切な結果を得にくい。
このように桁が大きく異なる範囲の場合、対数変換をして小さい桁のスケールと大きい桁のスケールを相対的に揃えてから均等にサンプリングする方が良い。
この、値をサンプリングする時のパターンを指定するのがparam_distribution引数である。
主な確率分布オブジェクト
-
scipy.stats.loguniform(a, b): a以上b未満のの対数一様分布 -
scipy.stats.randint(a, b): a以上b未満の整数の一様分布
サンプルコード
以下はirisのデータセットを分類するSVCモデルのgammaパラメータの値を、ランダムサーチで決定するコードである。
from sklearn.model_selection import RandomizedSearchCV,train_test_split,StratifiedKFold
from sklearn.datasets import load_iris
from sklearn.svm import SVC
from scipy.stats import loguniform
# データの取得と分割
iris = load_iris()
X, y = iris.data, iris.target
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=123,shuffle=True, stratify=y)
# モデルの作成
model = SVC(kernel="rbf",random_state=123)
# ランダムサーチオブジェクトの作成
param_distributions = {
"gamma" : loguniform(1e-4, 1e0),
"C" : loguniform(1e0,1e2)
}
random_search = RandomizedSearchCV(model,
param_distributions=param_distributions,
n_iter = 50,
cv=5,
random_state=123,
scoring="accuracy",
n_jobs=-1)
# ランダムサーチの実行
random_search.fit(X_train, y_train)
# 結果の確認
print(random_search.best_params_)
print(random_search.best_estimator_)
{'C': 27.4750150868112, 'gamma': 0.00492522233779106}
SVC(C=27.4750150868112, gamma=0.00492522233779106, random_state=123)
グリッドサーチとランダムサーチの違い
| グリッドサーチ | ランダムサーチ | |
|---|---|---|
| 探索方法 | 候補値の全組み合わせを網羅 | 分布からランダムにサンプリング |
| 計算コスト | 高い | 低い ( n_iterで回数を指定) |
| パラメータ数 | 少ないとき | 多いとき |
| 探索範囲 | 狭いとき | 広いとき |
| 値の性質 | 離散的(カテゴリ,整数) | スケールに依存する |
ランダムサーチで広い範囲から絞り込みを行い、グリッドサーチでその範囲内を細かく調べていく、という流れが一般的である。