元iOSエンジニアの自分が、我妻先生の「みんなのKaggle講座」の「ハイパーパラメータの調整」についてまとめてみた

## 導入
Udemyの我妻先生の[「みんなのKaggle講座」](https://www.udemy.com/course/learning-kaggle/) を受講しました。
その中で、ハイパーパラメータのoptunaの使い方が勉強になったのでここにまとめさせていただきます
この記事を見て、気になった方はぜひ購入を検討してみてください。

## この記事を読んで得られること
- kaggleのタイタニックはやったけど、ハイパーパラメータって何？カタカナ長すぎない？という方が、ハイパーパラメータについて理解できる
- ハイパーパラメータを自動で調整してくれるoptunaというライブラリについて、使い方も含めて理解できる

## アジェンダ
* ハイパーパラメータとは？
* ハイパーパラメータ前の事前準備
* 手動でハイパーパラメータを調整した場合
* optunaとは？
* optunaを利用してハイパーパラメータを調整した場合
* まとめ

## ハイパーパラメータとは？
ハイパーパラメータとは、機械学習アルゴリズムの挙動を設定する値(パラメータ)のことです。
この値から、どのように機械学習アルゴリズムが学習を行い、モデルを作るかが決まります。基本的には人が調整する値になります。
例えば、決定木のmax_depthなどがハイパーパラメータになります。max_depthでは、決定木の深さをどこまで深くするかを決めるものです。
下記はアイスを購入するかどうかを決定木で決めている簡易的な例になります。


max_depthが**1**の決定木
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/201104/563cd80b-f5d9-9cb3-e33c-cad7aaa251a6.png)


max_depthが**2**の決定木
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/201104/ecd3ed43-d31b-57be-b042-52e45926c55e.png)

上記のようにmax_depthが違うことで、作成されるモデルが変わっていきます。
何も設定しないと、基本的にはdefaultで設定された値になります。ちなみにmax_depthのdefaultはNodeで、できる限りまで深く分割していきます。

## 事前準備
このハイパーパラメータを調整していくために、まずは機械学習のモデルを作成する準備をしていきます。
データは、タイタニック号のデータを扱います。

### 必要なライブラリの導入
```Python
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns

# 交差検証を行うため
from sklearn.model_selection import train_test_split
from sklearn.metrics import log_loss

# LightGBM構築のため
import lightgbm as lgb
```

### データの読み込みと特徴量エンジニアリング
タイタニックのデータの読み込みと特徴量エンジニアリングを行います。
こちらについて、詳しく知りたい方は[「Udemy「みんなのKaggle講座」の特徴量エンジニアリングをまとめてみた」の特徴量エンジニアリング](https://qiita.com/yasui83/items/225ea21790c94c1da222#%E7%89%B9%E5%BE%B4%E9%87%8F%E3%82%A8%E3%83%B3%E3%82%B8%E3%83%8B%E3%82%A2%E3%83%AA%E3%83%B3%E3%82%B0)を参照してください。
```Python
train_data = pd.read_csv(filepath_or_buffer = "train.csv")
test_data = pd.read_csv(filepath_or_buffer = "test.csv")

test_id = test_data["PassengerId"]

data = pd.concat(objs = [train_data, test_data], sort = False)

data["Sex"].replace(to_replace = ["male", "female"], value = [0,1], inplace = True)
data["Embarked"].fillna(value = ("S"), inplace = True)
data["Embarked"] = data["Embarked"].map(arg = {"S": 0, "C": 1, "Q": 2})

data["Fare"].fillna(value = data["Fare"].mean(), inplace = True)
data["Age"].fillna(value = data["Age"].mean(), inplace = True)

data["Family"] = data["Parch"] + data["SibSp"]

data.drop(labels = ["Name", "PassengerId", "SibSp", "Parch", "Ticket", "Cabin"],
          axis = 1, inplace = True)

train_data = data[:len(train_data)]
test_data = data[len(train_data):]
t = train_data["Survived"]
x_train = train_data.drop(labels = "Survived", axis = 1)
x_test = test_data.drop(labels = "Survived", axis = 1)

x_train.head()
```

### 交差検証(クロスバリデーション)
過学習を防ぐために、交差検証(クロスバリデーション)を行います。
train_test_splitを利用して、訓練用のデータを訓練用と検証用に分割します。

```Python
x_train, x_valid, t_train, t_valid = train_test_split(x_train, t, test_size = 0.3, stratify = t)
```

## 手動でハイパーパラメータを調整
機械学習のモデルは、LightGBMを扱います。
LightGBMの中で今回 調整するハイパーパラメータは、下記の4種類になります。
* **objective**: LightGBMで、どのようなモデルを作成するかを決める。今回は生存しているか、死亡しているかの二値分類なので、binary(二値分類)を指定。
* **max_bin**: 1つの分岐に入るデータ数の最大値の指定。大きな数を指定することで、一般性を高めることができ、過学習抑制に繋げられる。
* **learning_rate**: 学習率。defaultは0.1。
* **num_leaves**: 葉の数。大きくすると精度は上がるが過学習が進む。

下記のcodeから、LightGBMでモデルを作成して、テストデータにある乗客たちの生死を予測します。
```Python
categorical_features = ["Embarked", "Pclass", "Sex"]

# こちらがハイパーパラメータになります。今回は手動で記載をしています。
params = {
    'objective': 'binary',
    'max_bin': 260,
    'learning_rate': 0.05, #学習率
    'num_leave': 35 # 葉の数
}

# LightGBM用のデータセットの作成
lgb_train = lgb.Dataset(x_train, t_train, categorical_feature = categorical_features)
lgb_val = lgb.Dataset(x_valid, t_valid, reference = lgb_train, categorical_feature = categorical_features)

# 手動で設定したハイパーパラメータで学習
model = lgb.train(params = params, train_set = lgb_train,
                  valid_sets = [lgb_train, lgb_val],
                  verbose_eval = 20,
                  num_boost_round = 500,
                  early_stopping_rounds = 10)

y_test = model.predict(data = x_test, num_iteration = model.best_iteration)
```

こちらを下記のcodeで提出用のファイルに変更して、kaggleに提出します。
```Python
y_test = (y_test > 0.5).astype(int) # 0.5より大きい場合はTrue。小さい場合はFalse。それをint型にすることで、1を生存、0を死亡を表せる。

survived_test = pd.Series(data = y_test, name = "Survived")
subm_data = pd.concat(objs = [test_id, survived_test], axis = 1) # axis = 1で、行方向で結合

subm_data.to_csv(path_or_buf = "submission_titanic_hp.csv", index = False)

subm_data
```

上記の結果のscoreは 0.75119 でした。


## optunaとは？
optunaとは、Preferred Networks社という日本のAIベンチャーが開発している作成しているものになります。
最もモデルの精度が高くなるハイパーパラメータの組み合わせを自動で探索してくれるフレームワークです。


## optunaを利用して、ハイパーパラメータを自動で調整
事前準備までのcodeは一緒ですが、それ以降のcodeが変わります。

optunaをまずinstallします。
```Python
!pip install optuna
import optuna
```


LightGBMのモデルを作成する関数を下記で定義します。
```Python
categorical_features = ["Embarked", "Pclass", "Sex"]

def objective(trial):
    # ハイパーパラメータの設定
    params = {
        "objective": "binary",
        "max_bin": trial.suggest_int("max_bin",200,500), 
        "learning_rate": 0.05,
        "num_leaves": trial.suggest_int("num_leaves", 16, 128)
    }

    # LightGBM用のデータセットの作成
    lgb_train = lgb.Dataset(x_train, t_train, categorical_feature = categorical_features)
    lgb_val = lgb.Dataset(x_valid, t_valid, reference = lgb_train, categorical_feature = categorical_features)

    # 設定されたハイパーパラメータ paramsでLightGBMのモデルを作成
    model = lgb.train(params = params, train_set = lgb_train, valid_sets = [lgb_train, lgb_val],
                      verbose_eval = 20,
                      num_boost_round = 500,
                      early_stopping_rounds = 10)
    
    y_valid = model.predict(data = x_valid, num_iteretion = model.best_iteration)
    score = log_loss(y_true = t_valid, y_pred = y_valid)
    return score
```

optunaを利用して、最もモデルの精度が高くなるハイパーパラメータを探索します。
```Python
study = optuna.create_study(sampler = optuna.samplers.RandomSampler())
study.optimize(func = objective, n_trials = 30)
```

最もscoreが良かったハイパーパラメータを下記で表示させます。
```Python
print(study.best_params)
```

結果は下記になりました。
```Python
{'max_bin': 239, 'num_leaves': 16}
```

optunaが発見した、上記のベストなハイパーパラメータで予測をします。
```Python
params = {
    "objective": "binary",
    "max_bin": study.best_params["max_bin"], # optunaの結果の239が代入される
    "learning_rate": 0.05,
    "num_leaves": study.best_params["num_leaves"]  # optunaの結果の16が代入される
}

lgb_train = lgb.Dataset(x_train, t_train, categorical_feature = categorical_features)
lgb_val = lgb.Dataset(x_valid, t_valid, reference = lgb_train, categorical_feature = categorical_features)

model = lgb.train(params = params, train_set = lgb_train, valid_sets = [lgb_train, lgb_val],
                  verbose_eval = 20,
                  num_boost_round = 500,
                  early_stopping_rounds = 10)

y_test = model.predict(data = x_test, num_iteration = model.best_iteration)
```

提出用のデータを作成します。
```Python
y_test = (y_test > 0.5).astype(int) # 0.5より大きい場合はTrue。小さい場合はFalse。それをint型にすることで、1を生存、0を死亡を表せる。

survived_test = pd.Series(data = y_test, name = "Survived")
subm_data = pd.concat(objs = [test_id, survived_test], axis = 1) # axis = 1で、行方向で結合

subm_data.to_csv(path_or_buf = "submission_titanic_hp.csv", index = False)

subm_data
```

kaggleに提出したところ、optunaを利用したときのscoreは0.76794となりました。
optunaを利用していないときが、0.75119なので少しモデルの精度の改善につながりました。


## まとめ
今回は、ハイパーパラメータとは？とハイパーパラメータの自動調整をするoputunaについて解説しました。
- ハイパーパラメータとは？
    - 機械学習アルゴリズムの挙動を設定する値(パラメータ)のこと
- optunaとは？
    - 最もモデルの精度が高くなるハイパーパラメータの組み合わせを自動で探索してくれるフレームワーク

いかがだったでしょうか。
今回の記事はUdemyの我妻先生の「みんなのKaggle講座」 のハイパーパラメータの調整についてまとめさせていただきました。
ハイパーパラメータという名前を聞いたとき、本当によくわからなかったのですが、
そんな昔の自分みたいな方が少しでもハイパーパラメータについての理解が深まれば幸いです。
お読みいただいてありがとうございました。