Kaggle / Titanic のチュートリアルの RandomForestClassifier のパラメータを調べる

# はじめに
Kaggle / Titanic のチュートリアルでは `RandomForestClassifier()` で学習している. このパラメータを調整して, 学習精度が向上するか確認する.

# データの準備
```python
import numpy as np
import pandas as pd
import seaborn
import matplotlib.pyplot as plt

train_data = pd.read_csv("../train.csv")

from sklearn.model_selection import train_test_split

train_data_orig = train_data
train_data, cv_data = train_test_split(train_data_orig, test_size=0.3, random_state=1)
```
`train_test_split` を使って, train : cv = 7 : 3 にデータを分割した (cv ; cross validation).

```python
train_data.info()

<class 'pandas.core.frame.DataFrame'>
Int64Index: 623 entries, 114 to 37
Data columns (total 12 columns):
 #   Column       Non-Null Count  Dtype  
---  ------       --------------  -----  
 0   PassengerId  623 non-null    int64  
 1   Survived     623 non-null    int64  
 2   Pclass       623 non-null    int64  
 3   Name         623 non-null    object 
 4   Sex          623 non-null    object 
 5   Age          496 non-null    float64
 6   SibSp        623 non-null    int64  
 7   Parch        623 non-null    int64  
 8   Ticket       623 non-null    object 
 9   Fare         623 non-null    float64
 10  Cabin        135 non-null    object 
 11  Embarked     622 non-null    object 
dtypes: float64(2), int64(5), object(5)
memory usage: 63.3+ KB
```

```python
cv_data.info()

<class 'pandas.core.frame.DataFrame'>
Int64Index: 268 entries, 862 to 92
Data columns (total 12 columns):
 #   Column       Non-Null Count  Dtype  
---  ------       --------------  -----  
 0   PassengerId  268 non-null    int64  
 1   Survived     268 non-null    int64  
 2   Pclass       268 non-null    int64  
 3   Name         268 non-null    object 
 4   Sex          268 non-null    object 
 5   Age          218 non-null    float64
 6   SibSp        268 non-null    int64  
 7   Parch        268 non-null    int64  
 8   Ticket       268 non-null    object 
 9   Fare         268 non-null    float64
 10  Cabin        69 non-null     object 
 11  Embarked     267 non-null    object 
dtypes: float64(2), int64(5), object(5)
memory usage: 27.2+ KB
```
train が 623 個, cv が 268 個, 合計が 891 個になっている.

# チュートリアル通りに学習する

```python
from sklearn.ensemble import RandomForestClassifier

features = ["Pclass", "Sex", "SibSp", "Parch"]

y = train_data["Survived"]
y_cv = cv_data["Survived"]
X = pd.get_dummies(train_data[features])
X_cv = pd.get_dummies(cv_data[features])

model = RandomForestClassifier(n_estimators=100, max_depth=5, random_state=1, max_features="auto")
model.fit(X, y)
predictions = model.predict(X_cv)

print('Train score: {}'.format(model.score(X, y)))
print('CV score: {}'.format(model.score(X_cv, y_cv)))
```

```
Train score: 0.8394863563402889
CV score: 0.753731343283582
```

train は 84% くらいの正解になっているが, CV では 75% の正解に留まる. 過学習? ってことでしょうか?

# RandomForestClassifier のパラメータを手動で変更する
## n_estimator
`n_estimator` の値を変えてみる.

```python
rfc_results = pd.DataFrame(columns=["train", "cv"])

for iter in [1, 10, 100]:
    model = RandomForestClassifier(n_estimators=iter, max_depth=5, random_state=1, max_features="auto")
    model.fit(X, y)
    predictions = model.predict(X_cv)
    rfc_results.loc[iter] = model.score(X, y), model.score(X_cv, y_cv)

rfc_results
```

|	|train	|cv|
|---:|---:|---:|
|1	|0.826645	|0.753731 |
|10	|0.833066	|0.753731 |
|100	|0.839486	|0.753731 |

決定木の数が増えるにつれて, train のスコアは微増するが, cv のスコアは変わらない. ムムッ.

## max_depth

`max_depth` の値を変えてみる.

### max_depth = 2
```python
for iter in [1, 10, 100]:
    model = RandomForestClassifier(n_estimators=iter, max_depth=2, random_state=1, max_features="auto")
    model.fit(X, y)
    predictions = model.predict(X_cv)
    rfc_results.loc[iter] = model.score(X, y), model.score(X_cv, y_cv)

rfc_results
```

|	|train	|cv |
|---:|---:|---:|
|1	|0.813804	|0.731343 |
|10	|0.81862	|0.753731 |
|100	|0.817014	|0.761194 |

cv のスコア 76% が出てきた.

### max_depth = 3
```python
for iter in [1, 10, 100]:
    model = RandomForestClassifier(n_estimators=iter, max_depth=3, random_state=1, max_features="auto")
    model.fit(X, y)
    predictions = model.predict(X_cv)
    rfc_results.loc[iter] = model.score(X, y), model.score(X_cv, y_cv)

rfc_results
```

|	|train	|cv |
|---:|---:|---:|
|1	|0.81862	|0.753731 |
|10	|0.82504	|0.776119 |
|100	|0.82504	|0.768657 |

cv のスコア 77.6% が出てきた.

### max_depth = 4
```python
for iter in [1, 10, 100]:
    model = RandomForestClassifier(n_estimators=iter, max_depth=4, random_state=1, max_features="auto")
    model.fit(X, y)
    predictions = model.predict(X_cv)
    rfc_results.loc[iter] = model.score(X, y), model.score(X_cv, y_cv)

rfc_results
```

|	|train	|cv |
|---:|---:|---:|
|1	|0.823435	|0.764925 |
|10	|0.82825	|0.761194 |
|100	|0.826645	|0.764925 |

cv のスコアは 76.5% 程度.

以上から, `max_depth=3` として, `n_estimators=10` が一番スコアが高かった.

# RandomForestClassifier のパラメータを自動で変更する

グリッドサーチ (`GridSearchCV`) という方法で, 一番良いパラメータを探す. これは, 列挙したパラメータの組み合わせをしらみつぶしで試してみて, 一番良いものを探すという方法.

```python
from sklearn.model_selection import GridSearchCV

param_grid = {"max_depth": [2, 3, 4, 5, None],
              "n_estimators":[1, 3, 10, 30, 100],
              "max_features":["auto", None]}

model_grid = GridSearchCV(estimator=RandomForestClassifier(random_state=1),
                 param_grid = param_grid,   
                 scoring="accuracy",  # metrics
                 cv = 3,              # cross-validation
                 n_jobs = 1)          # number of core

model_grid.fit(X, y) #fit

model_grid_best = model_grid.best_estimator_ # best estimator
print("Best Model Parameter: ", model_grid.best_params_)
```

`from` 行に注意. ネットで調べると `from sklearn.grid_search import GridSearchCV` という書き方もあったが, 私の場合ではこれでは NG でした.

```python
Best Model Parameter:  {'max_depth': 3, 'max_features': 'auto', 'n_estimators': 10}
```

手動で試した通り, `max_depth = 3`, `n_estimators = 10` が一番良かった. `max_features` も 2 種類試したが, `"auto"` が良かった.

```python
print('Train score: {}'.format(model.score(X, y)))
print('CV score: {}'.format(model.score(X_cv, y_cv)))

Train score: 0.826645264847512
CV score: 0.7649253731343284
```

# Kaggle / Titanic に submit
今回のパラメタを用いて結果を予測し, Kaggle に submit した. しかし精度は 0.77751 で, ***チュートリアルそのままのパラメタと変わらなかった***. びえん.

# おわりに
特徴量で改善を図る前に, 学習のハイパーパラメータを調整することで, ちょっとだけ向上させることができた. 次はいよいよ, 特徴量を検討していきたい.

# 参考
- [scikit-learnでデータを訓練用とテスト用に分割するtrain_test_split](https://note.nkmk.me/python-sklearn-train-test-split/)
- [pandas.DataFrameに列や行を追加（assign, appendなど）](https://note.nkmk.me/python-pandas-assign-append/)
- [scikit-learnでモデルのハイパーパラメータチューニングをしよう！](https://qiita.com/ragAgar/items/2f6bebdba5f9d7381310)
- [scikit-learnのGridSearchCVでハイパーパラメータ探索](https://qiita.com/saiaron/items/bb96c0f898cd0cbcd788)
- [Pythonでsklearnのgrid searchが使えないときの対処方法](https://qiita.com/shinaK/items/434935d331c0511edef3)