教師あり学習3 ハイパーパラメーターとチューニング(2)

[Aidemy](https://aidemy.net/)　2020/9/30
#はじめに
　こんにちは、んがょぺです！バリバリの文系ですが、AIの可能性に興味を持ったのがきっかけで、AI特化型スクール「Aidemy」に通い、勉強しています。ここで得られた知識を皆さんと共有したいと思い、Qiitaでまとめています。以前のまとめ記事も多くの方に読んでいただけてとても嬉しいです。ありがとうございます！
　今回は、教師あり学習の3つめの投稿になります。どうぞよろしくお願いします。

＊本記事は「Aidemy」での学習内容を「自分の言葉で」まとめたものになります。表現の間違いや勘違いを含む可能性があります。ご了承ください。

今回学ぶこと
・決定木、ランダムフォレスト、k-NNのハイパーパラメータ
・チューニング（パラメータ調整）の自動化

#決定木のハイパーパラメータ
##パラメータ　max_depth
・__max_depth__は__モデルが学習する木の深さ__を示す。この値を設定しなかったり値が大きすぎたりすると、教師データに寄り添いすぎてしまい汎化されないので、max_depthを制限することで汎化されるようにする。このことを「決定木の枝刈り」という。

##パラメータ　random_state
・決定木は階層構造のため、最初の方に取り出したデータの方が全体に占める割合は大きくなる。よって、データの取り出す順番を指定する__random_stateの結果に与える影響は、他のモデルに比べて大きい。__

#ランダムフォレストのハイパーパラメータ
##パラメータ　n_estimators
・ランダムフォレストは「ランダムなデータの決定木を複数作成し、それぞれ分類した結果のうち最も多数だったクラスを結果として出力する」モデルであるが、__n_estimators__はこのときの__「作成される決定木の個数」__を示す。

##パラメータ　max_depth
・ランダムフォレストにおいても決定木の深さを設定することができるが、決定木は複数あるので、一つ一つを深くしない方が良い。よって、__通常の決定木より小さな値にする。__

##パラメータ　random_state
・ランダムフォレストでは「ランダムなデータの決定木を複数作成し」とあるように、データの抽出にあたっても乱数を用いるので、__random_stateの値が変わると分析結果も大きく異なる。__

#k-NNのハイパーパラメータ
##パラメータ　n_neighbors
・k-NNは、「予測データと類似している教師データをk個抽出し、最も多かったクラスを予測結果として出力する」モデルであるが、__n_neighbors__はこの__kの値__を示す。すなわち「一つのデータに対して予測のために比較する教師データの個数」である。

#チューニング（パラメータ調整）の自動化
・全てのパラメータを値を変えながらチューニングしていくのは非常に手間。なので、__パラメーターの範囲を指定して一番結果の良かったパラメーターセットを計算機に見つけてもらう__ということを行うことで手間が省ける。
・自動化には__「グリッドサーチ」「ランダムサーチ」__の2種類がある。

##グリッドサーチ
・グリッドサーチは、__パラメータの値の候補を予め複数指定しておき、それぞれのセットごとに評価し、最も結果の良かったセットを採用する__方法である。
・値が「文字列」「整数」「True/False」などのときによく使われる（明示的な指定が簡単なため）。
・実際に非線形SVMでグリッドサーチを行う。方法は、__モデル作成時に各パラメータを辞書のキーとして、値をリストとして渡せば良い。__

```python
from sklearn.model_selection import GridSearchCV
#パラメータの候補（kernelはprecomputed以外、Cは-5~5までを指数として10のi乗を行う）
set_grid = {SVC():{"kernel":["linear","poly","rbf","sigmoid"],"C":[10**i for i in range(-5,5)],"decision_function_shape":["ovr","ovo"],"random_state":[0]}}
#後で格納する最も結果の良かった「正解率」と「使用したパラメータ」を定義
max_score = 0
best_param = None
#set_gridの「SVC（）」を「model」、「パラメータ（の候補）」を「param」としてグリッドサーチを行う。
for model,param in set_grid.items():
    search_model = GridSearchCV(model, param)
    search_model.fit(train_X, train_y)
#正解率を算出し、最も結果の良かったパラメータセットと正解率を格納する。
    pred_y = search_model.predict(test_X)
    score = f1_score(test_y,pred_y,average="micro")
    if max_score<score:
        max_score = score
        best_model = model.class.name
        best_param = searchmodel.best_param
```

##ランダムサーチ
・ランダムサーチは、__パラメータの値を範囲指定して、その範囲内からランダムに値を設定してモデルの評価を行い、最も結果の良かったパラメータセットを採用する__方法である。
・範囲指定は__確率関数__というものを使う。これはscipy.statsモジュールからインポートする。
・実際に非線形SVMでグリッドサーチを行う。方法はグリッドサーチと同じ流れでOK。

```python
import scipy.stats as sc
from sklearn.model_selection import RandomizedSearchCV
#パラメータの候補（Cは平均0.00001,標準偏差1000の一様分布、random_stateは0〜100の整数値をランダムでとる）
set_rondom = {SVC():{"kernel":["linear","poly","rbf","sigmoid"],"C":sc.uniform(0.00001,1000),"decision_function_shape":["ovr","ovo"],"random_state":sc.randint(0,100)}}
#後で格納する最も結果の良かった「正解率」と「使用したパラメータ」を定義(以下はグリッドとほぼ同じ)
max_score = 0
best_param = None
#set_gridの「SVC（）」を「model」、「パラメータ（の候補）」を「param」としてランダムサーチを行う。
for model,param in set_random.items():
    search_model = RandomizedSearchCV(model, param)
    search_model.fit(train_X, train_y)
#正解率を算出し、最も結果の良かったパラメータセットと正解率を格納する。
    pred_y = search_model.predict(test_X)
    score = f1_score(test_y,pred_y,average="micro")
    if max_score<score:
        max_score = score
        best_model = model.__class__.__name__
        best_param = search_model.best_params_
```

##ハイパーパラメータ調整の難点
・パラメータの自動調整は基本的に勾配法と言う、損失関数を減少させるように値を動かす方法をとる。この勾配法では、擬似的な解__鞍点__が存在し、これにハマると本来の解にたどり着けなくなる。
・しかし、この損失関数は場合によって変わるため、__調整は試しながら決める必要がある。__

#まとめ
・決定木のハイパーパラメータには木の深さを制限する__「max_depth」__の他「random_state」がある。
・ランダムフォレストのハイパーパラメータには、決定木の個数を指定する__「n_estimators」__の他「max_depth」「random_state」がある。
・k-NNのハイパーパラメータには、予測のために比較する教師データの個数を指定する__「n_neighbors」__がある。
・チューニングは__「グリッドサーチ」「ランダムサーチ」__で自動化できる。

今回は以上です。ここまで読んでくださりありがとうございました。