アンサンブル学習の代表的な機械学習モデルであるRandom Forestsのパラメータチューニングを試みる。 


# はじめに

## 目的

本ページの目的は不均衡なデータに対して Random Forests のチューニングを試みる。 精度が劇的に向上しましたという、ストーリーではなく、パラメータチューニングに焦点をあてる。

# 本ページを読み終えて理解すること

- Random Forests の パラメータチューニングが可能となる
- Random Forests のパラメータチューニングにより不均衡データに対応した機械学習モデルを構築する

# 分析対象データ : 不均衡なクレジット不正利用データ
- データ (Kaggle)
    - ダウンロード先：[Credit Card Fraud Detection](https://www.kaggle.com/mlg-ulb/creditcardfraud)
    - データの説明
        - 2013年9月にヨーロッパのカード保有者がクレジットカードで行った取引のデータ
        - 284,807 件の取引の内、492件の不正利用が発覚
            - いわゆる不均衡データ
            - 但し書き
                - 本データは一般的なPCでは膨大な計算量がかかり、パラメータチューニングが困難になる。そこで、一部データを抽出してチューニングに焦点を当てる。
        - クレジットカードの利用情報はプライバシーに関わるため、主成分分析により処理された形で特徴量V1からV28がデータに含まれている
            - ただし、時間と合計金額は主成分分析により変換されていない。
        - 分類対象の変数: Class
            - 不正の場合は 1
            - それ以外の場合は 0 
    - 分析の目的
        - クレジットカードが不正利用されたかどうかを分類可能か？

# 分析手法
本ページで扱う機械学習モデルは、Random Forestの中でもWeigth Random Forests と呼ばれている。


# 実験
ページが長くなるため、実行結果は一部省略する
## パッケージ読み込み



```python
import numpy as np
import pandas as pd
#from sklearn.model_selection import train_test_split
#from sklearn.tree import DecisionTreeClassifier
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import StratifiedKFold, cross_validate
# CatBoostを利用した分類
#from catboost import CatBoostClassifier
#import lightgbm as lgb
#import xgboost as xgb
from sklearn.model_selection import GridSearchCV
# 指標を計算するため
from sklearn.metrics import accuracy_score, cohen_kappa_score, balanced_accuracy_score, make_scorer, f1_score, recall_score
# 見た目を綺麗にするもの
import matplotlib.pyplot as plt
import seaborn as sns
import pprint, pydotplus
from pylab import rcParams
# 保存
from sklearn.externals.joblib import dump
from sklearn.externals.joblib import load
```

## データの読み込み
- 重要変数
    - tata_setは機械学習の用語である特徴量（もしくは特徴変数) を表す
    - target_setは機械学習の用語であるクラス (分類対象)を表す




```python
data = pd.read_csv("creditcard.csv")
pd.set_option('display.max_rows', 10)
#print(data.columns) # データの列名確認
# 今回のデータでは、計算量が膨大すぎるのでデータを削減する。現在　284,807 rows × 31 columns
np.random.seed(seed=1)
data = data.sort_values('Class')
# 正常な利用件数を2000件抽出
tmp1 = data.loc[data.loc[:,'Class'] == 0,:].sample(2000) # 
# 不正利用された300権を抽出
tmp2 = data.loc[data.loc[:,'Class'] == 1,:].sample(300)
class_weight_data = {0:1, 1:2300/300}
print(class_weight_data)
data = pd.concat([tmp1,tmp2]) 
#display(data)
target_set = data['Class'] 
data_set = data.loc[:,'V1':'Amount']
#print(target_set,type(target_set)) # クラスラベルの確認
print(data_set,len(data_set.columns)) # 特徴量の確認
```
## Random Forests; デフォルトセッティング


```python
%%time
model = RandomForestClassifier(random_state = 43,
                               n_jobs = -1,
                               oob_score=True)
print(model)
scoring = {'accuracy': make_scorer(accuracy_score),
           'kappa': make_scorer(cohen_kappa_score),
           'blanced_accuracy': make_scorer(balanced_accuracy_score) }
skf = StratifiedKFold(n_splits=10,
                      shuffle=True,
                      random_state=0)
scores_skf = cross_validate(model, 
                        data_set,   # 
                        target_set,
                        cv=skf, 
                        return_train_score=True,
                        n_jobs = -1,
                        scoring=scoring)
```

    RandomForestClassifier(bootstrap=True, class_weight=None, criterion='gini',
                max_depth=None, max_features='auto', max_leaf_nodes=None,
                min_impurity_decrease=0.0, min_impurity_split=None,
                min_samples_leaf=1, min_samples_split=2,
                min_weight_fraction_leaf=0.0, n_estimators='warn', n_jobs=-1,
                oob_score=True, random_state=43, verbose=0, warm_start=False)
    Wall time: 2.41 s



```python
#pprint.pprint(scores_skf)
print("StratifiedKFoldのテスト用正解率" + str(scores_skf['test_accuracy'].mean()))
print("StratifiedKFoldの訓練用正解率" + str(scores_skf['train_accuracy'].mean()))
print("StratifiedKFoldのテスト用blanced_正解率" + str(scores_skf['test_blanced_accuracy'].mean()))
print("StratifiedKFoldのテスト用kappa" + str(scores_skf['test_kappa'].mean()))
rcParams['figure.figsize'] = 15, 7
plt.plot(scores_skf['train_accuracy'],label='accuracy_train', linestyle="dashed")
plt.plot(scores_skf['test_accuracy'],label='accuracy_test')
plt.plot(scores_skf['train_blanced_accuracy'],label='blanced_accuracy_train', linestyle="dashed")
plt.plot(scores_skf['test_blanced_accuracy'],label='blanced_accuracy_test')
plt.plot(scores_skf['train_kappa'],label='kappa_train', linestyle="dashed")
plt.plot(scores_skf['test_kappa'],label='kappa_test')
plt.ylabel("Index")
plt.xlabel("#CV")
plt.legend(loc="best")
plt.show()
```

    StratifiedKFoldのテスト用正解率0.9800000000000001
    StratifiedKFoldの訓練用正解率0.9950241545893718
    StratifiedKFoldのテスト用blanced_正解率0.929
    StratifiedKFoldのテスト用kappa0.9060060809845943


### 結果1:Random Forests; デフォルトセッティング

- 過学習の有無
    - 訓練用のAccuracyが　0.995 である。
- 精度のまとめ
    - Accuracy: 0.98
    - Blanced accuracy: 0.929
    - Kappa: 0.9060060809845943

## Random Forests の不均衡データに対するチューニング
以下はデータから直接指定する。分類対象のラベル(クラスラベル)が同一であれば設定不要。

- class_weight: クラスラベルの比率に偏りがある場合は balanced または “balanced_subsample” を指定する。
    - default=None
    - balanced : n_samples / (n_classes * np.bincount(y)) としてクラスラベルに重みをつける。
    - balanced_subsample : balancedとほぼ同じ。各木のブーストラップサンプルを利用する
    - dict で指定する方法:
        - クラス 0 は 300,クラス 1 は 200件あり約7倍も偏りがある。
            - この場合には {0:1, 1:2300/300}と指定 (少し1にウェイトが重くなるようにする
            - クラス 1 (つまり不正利用)への重みが強くなる

より詳細なパラメータを参照したい場合は[sklearn.ensemble.RandomForestClassifier](https://scikit-learn.org/stable/modules/generated/sklearn.ensemble.RandomForestClassifier.html)のページを参照してください。


```python
%%time
model_balanced = RandomForestClassifier(random_state = 43,
                               n_jobs = -1,
                               oob_score=True,
                              class_weight = 'balanced')
model_balanced_subsample = RandomForestClassifier(random_state = 43,
                               n_jobs = -1,
                               oob_score=True,
                              class_weight = 'balanced_subsample')
model_d = RandomForestClassifier(random_state = 43,
                               n_jobs = -1,
                               oob_score=True,
                              class_weight = class_weight_data)
print(model_balanced)
print(model_balanced_subsample)
print(model_d)
scoring = {'accuracy': make_scorer(accuracy_score),
           'kappa': make_scorer(cohen_kappa_score),
           'blanced_accuracy': make_scorer(balanced_accuracy_score) }
skf = StratifiedKFold(n_splits=10,
                      shuffle=True,
                      random_state=0)
scores_balanced = cross_validate(model_balanced, 
                        data_set,   # 
                        target_set,
                        cv=skf, 
                        return_train_score=True,
                        n_jobs = -1,
                        scoring=scoring)
scores_balanced_subsample = cross_validate(model_balanced_subsample, 
                        data_set,   # 
                        target_set,
                        cv=skf, 
                        return_train_score=True,
                        n_jobs = -1,
                        scoring=scoring)
scores_d = cross_validate(model_d, 
                        data_set,   # 
                        target_set,
                        cv=skf, 
                        return_train_score=True,
                        n_jobs = -1,
                        scoring=scoring)
```

    RandomForestClassifier(bootstrap=True, class_weight='balanced',
                criterion='gini', max_depth=None, max_features='auto',
                max_leaf_nodes=None, min_impurity_decrease=0.0,
                min_impurity_split=None, min_samples_leaf=1,
                min_samples_split=2, min_weight_fraction_leaf=0.0,
                n_estimators='warn', n_jobs=-1, oob_score=True,
                random_state=43, verbose=0, warm_start=False)
    RandomForestClassifier(bootstrap=True, class_weight='balanced_subsample',
                criterion='gini', max_depth=None, max_features='auto',
                max_leaf_nodes=None, min_impurity_decrease=0.0,
                min_impurity_split=None, min_samples_leaf=1,
                min_samples_split=2, min_weight_fraction_leaf=0.0,
                n_estimators='warn', n_jobs=-1, oob_score=True,
                random_state=43, verbose=0, warm_start=False)
    RandomForestClassifier(bootstrap=True,
                class_weight={0: 1, 1: 7.666666666666667}, criterion='gini',
                max_depth=None, max_features='auto', max_leaf_nodes=None,
                min_impurity_decrease=0.0, min_impurity_split=None,
                min_samples_leaf=1, min_samples_split=2,
                min_weight_fraction_leaf=0.0, n_estimators='warn', n_jobs=-1,
                oob_score=True, random_state=43, verbose=0, warm_start=False)
    CPU times: user 129 ms, sys: 12.7 ms, total: 142 ms
    Wall time: 6.92 s



```python
#pprint.pprint(scores_balanced)
print("scores_balancedのテスト用正解率" + str(scores_balanced['test_accuracy'].mean()))
print("scores_balancedの訓練用正解率" + str(scores_balanced['train_accuracy'].mean()))
print("scores_balancedのテスト用blanced_正解率" + str(scores_balanced['test_blanced_accuracy'].mean()))
print("scores_balancedのテスト用kappa" + str(scores_balanced['test_kappa'].mean()))
rcParams['figure.figsize'] = 15, 7
plt.plot(scores_balanced['train_accuracy'],label='accuracy_train', linestyle="dashed")
plt.plot(scores_balanced['test_accuracy'],label='accuracy_test')
plt.plot(scores_balanced['train_blanced_accuracy'],label='blanced_accuracy_train', linestyle="dashed")
plt.plot(scores_balanced['test_blanced_accuracy'],label='blanced_accuracy_test')
plt.plot(scores_balanced['train_kappa'],label='kappa_train', linestyle="dashed")
plt.plot(scores_balanced['test_kappa'],label='kappa_test')
plt.ylabel("Index")
plt.xlabel("#CV")
plt.legend(loc="best")
plt.show()
#pprint.pprint(scores_balanced_subsample)
print("scores_balanced_subsampleのテスト用正解率" + str(scores_balanced_subsample['test_accuracy'].mean()))
print("scores_balanced_subsampleの訓練用正解率" + str(scores_balanced_subsample['train_accuracy'].mean()))
print("scores_balanced_subsampleのテスト用blanced_正解率" + str(scores_balanced_subsample['test_blanced_accuracy'].mean()))
print("scores_balanced_subsampleのテスト用kappa" + str(scores_balanced_subsample['test_kappa'].mean()))
rcParams['figure.figsize'] = 15, 7
plt.plot(scores_balanced_subsample['train_accuracy'],label='accuracy_train', linestyle="dashed")
plt.plot(scores_balanced_subsample['test_accuracy'],label='accuracy_test')
plt.plot(scores_balanced_subsample['train_blanced_accuracy'],label='blanced_accuracy_train', linestyle="dashed")
plt.plot(scores_balanced_subsample['test_blanced_accuracy'],label='blanced_accuracy_test')
plt.plot(scores_balanced_subsample['train_kappa'],label='kappa_train', linestyle="dashed")
plt.plot(scores_balanced_subsample['test_kappa'],label='kappa_test')
plt.ylabel("Index")
plt.xlabel("#CV")
plt.legend(loc="best")
plt.show()
print("scores_dのテスト用正解率" + str(scores_d['test_accuracy'].mean()))
print("scores_dの訓練用正解率" + str(scores_d['train_accuracy'].mean()))
print("scores_dのテスト用blanced_正解率" + str(scores_d['test_blanced_accuracy'].mean()))
print("scores_dのテスト用kappa" + str(scores_d['test_kappa'].mean()))
rcParams['figure.figsize'] = 15, 7
plt.plot(scores_d['train_accuracy'],label='accuracy_train', linestyle="dashed")
plt.plot(scores_d['test_accuracy'],label='accuracy_test')
plt.plot(scores_d['train_blanced_accuracy'],label='blanced_accuracy_train', linestyle="dashed")
plt.plot(scores_d['test_blanced_accuracy'],label='blanced_accuracy_test')
plt.plot(scores_d['train_kappa'],label='kappa_train', linestyle="dashed")
plt.plot(scores_d['test_kappa'],label='kappa_test')
plt.ylabel("Index")
plt.xlabel("#CV")
plt.legend(loc="best")
plt.show()
```

    scores_balancedのテスト用正解率0.9800000000000001
    scores_balancedの訓練用正解率0.994927536231884
    scores_balancedのテスト用blanced_正解率0.9275833333333334
    scores_balancedのテスト用kappa0.9057970812088367



![output_13_1.png](https://qiita-image-store.s3.amazonaws.com/0/154819/7896ac96-f52e-5d8e-5c56-246cc4e7b781.png)

    scores_balanced_subsampleのテスト用正解率0.9782608695652175
    scores_balanced_subsampleの訓練用正解率0.9951690821256038
    scores_balanced_subsampleのテスト用blanced_正解率0.9223333333333334
    scores_balanced_subsampleのテスト用kappa0.8969364320825853



![output_13_3.png](https://qiita-image-store.s3.amazonaws.com/0/154819/f1a8c6f2-d264-86f4-3e8a-4b65d8ce64d1.png)



    scores_dのテスト用正解率0.9800000000000001
    scores_dの訓練用正解率0.9947826086956522
    scores_dのテスト用blanced_正解率0.9261666666666667
    scores_dのテスト用kappa0.9054953893965584

![output_13_5.png](https://qiita-image-store.s3.amazonaws.com/0/154819/e90f2854-9f2b-f0e0-711d-2e6e30e1b2c5.png)

### 結果2: Random Forests の不均衡データに対するチューニング
Balanced Accuracyから判断する精度は以下の順位となる。

- デフォルト設定：0.929
- scores_balancedのテスト用blanced_正解率0.9275833333333334
- scores_dのテスト用blanced_正解率0.9261666666666667
- scores_balanced_subsampleのテスト用blanced_正解率0.9223333333333334


Kappaから判断する精度は以下の順位となる。

- デフォルト設定：0.9060060809845943
- scores_balancedのテスト用kappa0.9057970812088367
- scores_dのテスト用kappa0.9054953893965584
- scores_balanced_subsampleのテスト用kappa0.8969364320825853

#### これまでの結果のまとめ
いずれの指標においても デフォルト設定の精度 > balanced > {0:1, 1:2300/300} > balanced_subsample となった。

## Random Forests グリッドサーチによるチューニング
Random Forestsの最も重要なパラメータは次の通りである。大抵チューニングを開始する際には、この二つのパラメータを利用する。

- n_estimators: 木の数 (著者の経験的には 500 から 1000 程度
- max_features: 決定木を成長させる際に利用する特徴変数の数 (著者の経験的には sqrt(扱う特徴変数の数)前後



```python
%%time
model_for_gscv = RandomForestClassifier(random_state = 43,
                               n_jobs = -1,
                               oob_score=True)
# パラメーターを設定する
param_grid = {"n_estimators": [100,300,500 ,1000], # 2000まではいらない
              "max_features": [1, 3, 5, 7, None],
              "class_weight": ['balanced','balanced_subsample',class_weight_data,None]
             } 
skf = StratifiedKFold(n_splits=10,
                      shuffle=True,
                      random_state=0)
# グリッドサーチでパラメータチューニング
grid_result = GridSearchCV(estimator = model_for_gscv,
                           param_grid = param_grid,
                           scoring = make_scorer(cohen_kappa_score), #accuracy, balanced_accuracy
                           cv = skf,
                           return_train_score = True,
                           n_jobs = -1)

grid_result.fit(data_set, target_set)
```

    Wall time: 4min 14s

### 結果

先に結果をまとめる。
- ベストなパラメータ
    - {'class_weight': 'balanced', 'max_features': 3, 'n_estimators': 500}
    - しかし、同率で次のような組み合わせもある。
        - {'class_weight': {0: 1, 1: 7.666666666666667}, 'max_features': 3, 'n_estimators': 500
        - {'class_weight': 'balanced_subsample', 'max_features': 3, 'n_estimators': 300}
    - 精度は 0.9121293
デフォルト設定は0.906であり、若干向上した。
- 各パラメータの傾向
    - 何本の木を生成するか？ : n_estimators
        - 値が大きい方が精度が高くなる傾向が見られる。
    - 木を成長させる際に利用する特徴量の数 : max_features 
        - 3 または 5程度が良い。多すぎても少なすぎても、よくない
    - 不均衡に対する処理：class_weight
        - None 以外なら問題なし

```python
%%time
# ベストな分類器を抽出
pprint.pprint(grid_result.best_estimator_)
# ベストなパラメータを抽出
pprint.pprint(grid_result.best_params_)
# ベストな正解率を抽出
pprint.pprint(grid_result.best_score_)
# 各種途中結果を抽出
pprint.pprint(grid_result.cv_results_)

dump(grid_result,"model_grid_small_data.sav")
reload_best_model = load("model_grid_small_data.sav")
```




 RandomForestClassifier(bootstrap=True, class_weight='balanced',
                criterion='gini', max_depth=None, max_features=3,
                max_leaf_nodes=None, min_impurity_decrease=0.0,
                min_impurity_split=None, min_samples_leaf=1,
                min_samples_split=2, min_weight_fraction_leaf=0.0,
                n_estimators=500, n_jobs=-1, oob_score=True, random_state=43,
                verbose=0, warm_start=False)
    {'class_weight': 'balanced', 'max_features': 3, 'n_estimators': 500}
    0.9121293798200826

## より細かなチューニング
ランダムフォレスト には次のようなパラメータも用意されている。以下のパラメータは決定木にも利用されるパラメータである。

- min_samples_leaf:
    - リーフ(末端のノード)を構成するのに必要なサンプル数。
- min_samples_split: 図解ありの説明な場合は[ここ](https://qiita.com/kibinag0/items/6e06561aceeb27ea86d8)を参照
    - ノードに含まれるサンプルの数が、min_samples_splitに指定された値以上の場合は、決定木の分岐をさらに進める。
- max_depth
    - 木の大きさを設定する。
    
```python
%%time
model = RandomForestClassifier(random_state = 43,
                               n_jobs = -1,
                               oob_score=True)
# パラメーターを設定する
param_grid = {"n_estimators": [100,300,500,1000], # 2000まではいらない
              "max_features": [1, 3, 5, 7, None],
              "class_weight": ['balanced','balanced_subsample',class_weight_data,None],
              "min_samples_leaf":  [1, 2, 4],
              "min_samples_split": [2, 5, 10],
              "max_depth": [3,5,7,10,None] 
             } 
skf = StratifiedKFold(n_splits=10,
                      shuffle=True,
                      random_state=0)
# パラメータチューニングをグリッドサーチ
grid_result = GridSearchCV(estimator = model,
                           param_grid = param_grid,
                           scoring = make_scorer(cohen_kappa_score), #accuracy, balanced_accuracy
                           cv = skf,
                           return_train_score = True,
                           n_jobs = -1)

grid_result.fit(data_set, target_set)
dump(grid_result,"model_grid_small_data_detil.sav")
reload_best_model = load("model_grid_small_data_detil.sav")
```

### 結果 : より細かなチューニング
先に結果をまとめる。

- ベストなパラメータと精度
    - パラメータ
        - {'class_weight': {0: 1, 1: 7.666666666666667},
        - 'max_depth': None,
        - 'max_features': 5,
        - 'min_samples_leaf': 2,
        - 'min_samples_split': 10,
        - 'n_estimators': 100}
    - 精度
        - 0.9169874536344943
            - 簡易的なチューニングの精度 0.9121293 より向上
        
- 各パラメータの傾向
    - 何本の木を生成するか？ : n_estimators
        - 値が大きい方が精度が低くなる（おそらく過学習するため）。
    - 木を成長させる際に利用する特徴量の数 : max_features 
        - 5, 7, None が良い。少なすぎは良くなさそう
    - 不均衡に対する処理：class_weight
        - None は精度が低い
	- class_weight_data を設定する方が良いが、平均的には変化が少ない
    - min_samples_leafとmin_samples_split
        - 2 から 4が良さそう。
    - max_depth
        - Noneが良さそう。

```python
reload_best_model = load("model_grid_small_data_detil.sav")
# ベストな分類器を抽出
pprint.pprint(reload_best_model.best_estimator_)
# ベストなパラメータを抽出
pprint.pprint(reload_best_model.best_params_)
# ベストな正解率を抽出
pprint.pprint(reload_best_model.best_score_)
# 各種途中結果を抽出
pprint.pprint(reload_best_model.cv_results_)
```

#### どのパラメータが有効か？
n_estimators, max_features, class_weight, min_samples_leaf, min_samples_splitの順に精度の箱ひげ図を示す。


```python
plot_p_data = pd.DataFrame( {"n_estimators:100":reload_best_model.cv_results_["mean_test_score"][reload_best_model.cv_results_["param_n_estimators"] == 100],
                             "n_estimators:300": reload_best_model.cv_results_["mean_test_score"][reload_best_model.cv_results_["param_n_estimators"] == 300],
                             "n_estimators:500": reload_best_model.cv_results_["mean_test_score"][reload_best_model.cv_results_["param_n_estimators"] == 500],
                             "n_estimators:1000": reload_best_model.cv_results_["mean_test_score"][reload_best_model.cv_results_["param_n_estimators"] == 1000]}
                          )
rcParams['figure.figsize'] = 10, 5
plot_p_data.boxplot()
plt.show()
plot_p_data = pd.DataFrame( {"max_features:1":reload_best_model.cv_results_["mean_test_score"][reload_best_model.cv_results_["param_max_features"] == 1],
                             "max_features:3": reload_best_model.cv_results_["mean_test_score"][reload_best_model.cv_results_["param_max_features"] == 3],
                             "max_features:5": reload_best_model.cv_results_["mean_test_score"][reload_best_model.cv_results_["param_max_features"] == 5],
                             "max_features:7": reload_best_model.cv_results_["mean_test_score"][reload_best_model.cv_results_["param_max_features"] == 7],
                             "max_features:None": reload_best_model.cv_results_["mean_test_score"][reload_best_model.cv_results_["param_max_features"] == None]}
                          )
plot_p_data.boxplot()
plt.show()
plot_p_data = pd.DataFrame( {"balanced":reload_best_model.cv_results_["mean_test_score"][reload_best_model.cv_results_["param_class_weight"] == "balanced"],
                             "balanced_subsample": reload_best_model.cv_results_["mean_test_score"][reload_best_model.cv_results_["param_class_weight"] == "balanced_subsample"],
                             "class_weight_data": reload_best_model.cv_results_["mean_test_score"][reload_best_model.cv_results_["param_class_weight"] == class_weight_data],
                             "None": reload_best_model.cv_results_["mean_test_score"][reload_best_model.cv_results_["param_class_weight"] == None]}
                          )
plot_p_data.boxplot()
plt.show()
plot_p_data = pd.DataFrame( {"min_samples_leaf:1":reload_best_model.cv_results_["mean_test_score"][reload_best_model.cv_results_["param_min_samples_leaf"] == 1],
                             "min_samples_leaf:2": reload_best_model.cv_results_["mean_test_score"][reload_best_model.cv_results_["param_min_samples_leaf"] == 2],
                             "min_samples_leaf:4": reload_best_model.cv_results_["mean_test_score"][reload_best_model.cv_results_["param_min_samples_leaf"] == 4]}
                          )
plot_p_data.boxplot()
plt.show()
plot_p_data = pd.DataFrame( {"min_samples_split:1":reload_best_model.cv_results_["mean_test_score"][reload_best_model.cv_results_["param_min_samples_split"] == 2],
                             "min_samples_split:2": reload_best_model.cv_results_["mean_test_score"][reload_best_model.cv_results_["param_min_samples_split"] == 5],
                             "min_samples_split:4": reload_best_model.cv_results_["mean_test_score"][reload_best_model.cv_results_["param_min_samples_split"] == 10]}
                          )
plot_p_data.boxplot()
plt.show()
plot_p_data = pd.DataFrame( {"param_max_depth:3":reload_best_model.cv_results_["mean_test_score"][reload_best_model.cv_results_["param_max_depth"] == 3],
                             "param_max_depth:5": reload_best_model.cv_results_["mean_test_score"][reload_best_model.cv_results_["param_max_depth"] == 5],
                             "param_max_depth:7": reload_best_model.cv_results_["mean_test_score"][reload_best_model.cv_results_["param_max_depth"] == 7],
                            "param_max_depth:10": reload_best_model.cv_results_["mean_test_score"][reload_best_model.cv_results_["param_max_depth"] == 10],
                            "param_max_depth:None": reload_best_model.cv_results_["mean_test_score"][reload_best_model.cv_results_["param_max_depth"] == None]}
                          )
plot_p_data.boxplot()
plt.show()
```


![output_31_0.png](https://qiita-image-store.s3.amazonaws.com/0/154819/318c9b10-dba5-4cc1-92a9-46742318db17.png)
![output_31_1.png](https://qiita-image-store.s3.amazonaws.com/0/154819/25e5fbb1-26b0-fea7-d906-ba495d2f4f2b.png)
![output_31_2.png](https://qiita-image-store.s3.amazonaws.com/0/154819/f8f387f8-7ce0-63d9-53ff-5d9750df0d3b.png)
![output_31_3.png](https://qiita-image-store.s3.amazonaws.com/0/154819/1433a834-5d35-e7fd-6ad8-efae08711e4e.png)
![output_31_4.png](https://qiita-image-store.s3.amazonaws.com/0/154819/7e0c6d50-90da-6e96-98aa-e540de3170b0.png)
![output_31_5.png](https://qiita-image-store.s3.amazonaws.com/0/154819/8a248b53-a875-9d63-29da-0432984f18af.png)


# まとめ

- そもそも不均衡なデータへの対策は必要か？
    - 必要: クラスラベルの比率から罰則を行はない場合は、精度が低下した
- max_depth, min_samples_leaf, min_samples_split までチューニングは必要か？
    - 必要そう：精度の向上が期待できる。max_features も重要だが、木の大きさを変更させることは過学習への対策などにも必要であり、いずれのパラメータもいくつか選んだ方が良いと思う。
- デフォルト設定から精度向上までの道のりは？
    - "n_estimators" と "max_features" の二つでも精度向上が見込めるが、max_depth、min_samples_leaf、min_samples_split も含めたほうがより精度向上が見込める。
    - Kappa 係数で 最終的に 0.01 ほど向上した。推定精度が90%台からスタートしたため、それなりに精度が向上したと思われる。
        - 参考：今回のデータはデータ量が少ない。
        - 参考：画像認識の世界では1%あがるのに一年ほどかかる研究もある。
- 注意 (著者感覚
    - データセットの種類に大きく依存するため、これが最もベストな組み合わせはないと思われる。
    - また、推奨されている設定でも、精度が向上しないことがあるため、鵜呑みは厳禁