【機械学習入門】「ランダムフォレストでクラス分類」というのをやってみる♬

機械学習でクラス分類の定番の「ランダムフォレストでクラス分類」をやってみた。
何より、この手法だと音声のクラス分類というかある程度の認識ができるかもしれないと思える精度が出ている。
これもほぼ参考の通りすすめたが、やはりいくつかトラップがあったのでまとめておく。
【参考】
・[機械学習手法「ランダムフォレスト」でクラス分類にチャレンジしよう](https://www.sejuku.net/blog/64455)
上記は、最近更新されているということで、ちょっと手を入れて動いたが、ウワンの環境だとまんまだと動きそうには無かったので、以下の改良を実施している。
###やったこと
・print(f"acc: {clf.score(test_images, test_labels)}")をprint("acc:{:.2f} %".format(acc))に変更
・グラフを表示して見やすく改善し、保存するようにした
・GridSearchCVをパラメータ変更して実施
###・print(f"acc: {clf.score(test_images, test_labels)}")をprint("acc:{:.2f} %".format(acc))に変更
これもウワンの環境だと動かない。
とりあえず、accは数値をグラフにも出力したいので以下のように変更した。

```py
acc=clf.score(test_images, test_labels)*100
print("acc:{:.2f} %".format(acc))
```
###・グラフを表示して見やすく改善し、保存するようにした
入力のdataset出力に以下のように最後の三行を追加して、保存して１秒表示してクローズするようにした。

```py
plt.figure(figsize=(15,15))
for i in range(25):
    plt.subplot(5,5,i+1)
    plt.xticks([])
    plt.yticks([])
    plt.imshow(train_images[i].reshape((8,8)), cmap=plt.cm.binary)
    plt.xlabel(train_labels[i])
plt.savefig('RFC/dataset_input.jpg')
plt.pause(1)
plt.close()
```
さらに、リザルトも同様に以下を追加して、
①上で計算した精度accを表示するようにした
②描画した画像を保存した
③１秒描画して、closeするようにした

```py
    if i==0:
        plt.title("acc:{:.2f} %".format(acc), fontsize=36)  
 
plt.savefig('RFC/RFC_results.jpg')
plt.pause(1)
plt.close()
```
ということで、結果描画のコードは以下のとおりに変更した。

```py
plt.figure(figsize=(15,15))
# 先頭から25枚テストデータを可視化
for i in range(25):
    # 画像を作成
    plt.subplot(5,5,i+1)
    plt.xticks([])
    plt.yticks([])
    plt.imshow(test_images[i].reshape((8,8)), cmap=plt.cm.binary)
    # 今プロットを作っている画像データの予測ラベルと正解ラベルをセット
    predicted_label = predicted_labels[i]
    true_label      = test_labels[i]

    # 予測ラベルが正解なら緑、不正解なら~~赤色~~青色を使う

    if predicted_label == true_label:
        color = 'green' # True label color
    else:
        color = 'blue'   # False label color red
    plt.xlabel("{} True({})".format(predicted_label,
                                  true_label), color=color, fontsize=18)
    if i==0:
        plt.title("acc:{:.2f} %".format(acc), fontsize=36)  
 
plt.savefig('RFC/RFC_results.jpg')
plt.pause(1)
plt.close()
```
###・GridSearchCVをパラメータ変更して実施
ここが一番苦労した。まず、上記参考のとおりだと当初エラーを吐いていて終了しないので、以下のように変更した。
※ここは、単に環境のせいかもしれないがエラーの原因は不明
エラーは以下のとおり

```
ImportError: [joblib] Attempting to do parallel computing without protecting your import on a system that does not support forking. To use parallel-computing in a script, you must protect your main loop using "if __name__ == '__main__'". Please see the joblib documentation on Parallel for more information
```
ということで、並列処理の部分を以下のとおり変更しました。
`n_jobs=1)       # 並列処理 最大値-1`

ハイパーパラメータなどの意味は、参考②を見てください。
【参考】
・②[3.2.4.3.1. sklearn.ensemble.RandomForestClassifier](https://scikit-learn.org/stable/modules/generated/sklearn.ensemble.RandomForestClassifier.html)

```py
search_params = {
     'n_estimators'      : [5, 10, 20, 30, 50, 100, 300],   
      'max_features'      : [3, 5, 10, 15, 20],  
      'random_state'      : [2525],
      'n_jobs'            : [1],
      'min_samples_split' : [3, 5, 10, 15, 20, 25, 30, 40, 50, 100],  
      'max_depth'         : [3, 5, 10, 15, 20, 25, 30, 40, 50, 100]  
}

gs = GridSearchCV(RFC(),           # 対象の機械学習モデル
                  search_params,   # 探索パラメタ辞書
                  cv=3,            # クロスバリデーションの分割数 3
                  verbose=True,    # ログ表示verbose=True
                  n_jobs=1)       # 並列処理 最大値-1
gs.fit(train_images, train_labels)
 
print(gs.best_estimator_)

acc=gs.score(test_images, test_labels)*100
print("acc:{:.2f} %".format(acc))

predicted_labels = gs.predict(test_images)
```
以下で、上記のフィッティング結果gsが予測した結果を表示する。

```py
plt.figure(figsize=(15,15))
# 先頭から25枚テストデータを可視化
for i in range(25):
 
    # 画像を作成
    plt.subplot(5,5,i+1)
    plt.xticks([])
    plt.yticks([])
    plt.imshow(test_images[i].reshape((8,8)), cmap=plt.cm.binary)
 
    # 今プロットを作っている画像データの予測ラベルと正解ラベルをセット
    predicted_label = predicted_labels[i]
    true_label      = test_labels[i]
 
    # 予測ラベルが正解なら緑、不正解なら赤色を使う
    if predicted_label == true_label:
        color = 'green' # True label color
    else:
        color = 'blue'   # False label color red
    plt.xlabel("{} True({})".format(predicted_label,
                                  true_label), color=color, fontsize=18)
    if i==0:
        plt.title("acc:{:.2f} %".format(acc), fontsize=36)  
 
plt.savefig('RFC/RFC_best_results.jpg')
plt.pause(1)
plt.close()
```

因みに、パラメーターを変化させたときの結果は以下のようになった。
最初は、ハイパーパラメータ固定でやってみます。

```py
search_params = {
     'n_estimators'      : [5],   
      'max_features'      : [5],  
      'random_state'      : [2525],
      'n_jobs'            : [1],
      'min_samples_split' : [10],  
      'max_depth'         : [10]  
}
```
計算時間は、[Parallel(n_jobs=1)]: Done   3 out of   3 | elapsed:    0.0s finishedであり、あっという間に終了します。

```
>python RFC.py
[Parallel(n_jobs=-1)]: Done   6 out of  10 | elapsed:    0.0s remaining:    0.0s
[Parallel(n_jobs=-1)]: Done  10 out of  10 | elapsed:    0.0s finished
[Parallel(n_jobs=8)]: Done   6 out of  10 | elapsed:    0.0s remaining:    0.0s
[Parallel(n_jobs=8)]: Done  10 out of  10 | elapsed:    0.0s finished
acc:92.78 %
[Parallel(n_jobs=8)]: Done   6 out of  10 | elapsed:    0.0s remaining:    0.0s
[Parallel(n_jobs=8)]: Done  10 out of  10 | elapsed:    0.0s finished
Fitting 3 folds for each of 1 candidates, totalling 3 fits
[Parallel(n_jobs=1)]: Done   3 out of   3 | elapsed:    0.0s finished
RandomForestClassifier(bootstrap=True, class_weight=None, criterion='gini',
            max_depth=10, max_features=5, max_leaf_nodes=None,
            min_impurity_decrease=0.0, min_impurity_split=None,
            min_samples_leaf=1, min_samples_split=10,
            min_weight_fraction_leaf=0.0, n_estimators=10, n_jobs=1,
            oob_score=False, random_state=2525, verbose=0,
            warm_start=False)
acc:91.94 %
```
ハイパーパラメータは`'min_samples_split' : [5, 25, 50, 100],`と`'max_depth'         : [5, 25, 50, 100]` の二つを動かしています。組み合わせは１６通りなので、 Fitting 3 folds for each of 16 candidates, totalling 48 fitsとなっています。

```py
search_params = {
     'n_estimators'      : [5],   
      'max_features'      : [5],  
      'random_state'      : [2525],
      'n_jobs'            : [1],
      'min_samples_split' : [5, 25, 50, 100],  
      'max_depth'         : [5, 25, 50, 100]  
}
```
計算時間は、[Parallel(n_jobs=1)]: Done  48 out of  48 | elapsed:    0.3s finishedであり、あっという間に終了します。そして、精度も決め打ちのacc:93.89 %より悪くacc:90.00 %と上がっていません。

```
[Parallel(n_jobs=-1)]: Done   6 out of  10 | elapsed:    0.0s remaining:    0.0s
[Parallel(n_jobs=-1)]: Done  10 out of  10 | elapsed:    0.0s finished
[Parallel(n_jobs=8)]: Done   6 out of  10 | elapsed:    0.0s remaining:    0.0s
[Parallel(n_jobs=8)]: Done  10 out of  10 | elapsed:    0.0s finished
acc:93.89 %
[Parallel(n_jobs=8)]: Done   6 out of  10 | elapsed:    0.0s remaining:    0.0s
[Parallel(n_jobs=8)]: Done  10 out of  10 | elapsed:    0.0s finished
Fitting 3 folds for each of 16 candidates, totalling 48 fits
[Parallel(n_jobs=1)]: Done  48 out of  48 | elapsed:    0.4s finished
RandomForestClassifier(bootstrap=True, class_weight=None, criterion='gini',
            max_depth=25, max_features=5, max_leaf_nodes=None,
            min_impurity_decrease=0.0, min_impurity_split=None,
            min_samples_leaf=1, min_samples_split=5,
            min_weight_fraction_leaf=0.0, n_estimators=5, n_jobs=1,
            oob_score=False, random_state=2525, verbose=0,
            warm_start=False)
acc:90.00 %
```
フィッティングはmin_samples_splitを１０個、max_depthを１０個の掛け算なので、
Fitting 3 folds for each of 100 candidates, totalling 300 fitsについて実施します。

```py
search_params = {
     'n_estimators'      : [5],   
      'max_features'      : [5],  
      'random_state'      : [2525],
      'n_jobs'            : [1],
      'min_samples_split' : [3, 5, 10, 15, 20, 25, 30, 40, 50, 100],  
      'max_depth'         : [3, 5, 10, 15, 20, 25, 30, 40, 50, 100]  
}
```
計算時間は、[Parallel(n_jobs=1)]: Done 300 out of 300 | elapsed:    5.2s finishedであっという間に終了します。

```
>python RFC.py
[Parallel(n_jobs=-1)]: Done   6 out of  10 | elapsed:    0.0s remaining:    0.0s
[Parallel(n_jobs=-1)]: Done  10 out of  10 | elapsed:    0.0s finished
[Parallel(n_jobs=8)]: Done   6 out of  10 | elapsed:    0.0s remaining:    0.0s
[Parallel(n_jobs=8)]: Done  10 out of  10 | elapsed:    0.0s finished
acc:95.56 %
[Parallel(n_jobs=8)]: Done   6 out of  10 | elapsed:    0.0s remaining:    0.0s
[Parallel(n_jobs=8)]: Done  10 out of  10 | elapsed:    0.0s finished
Fitting 3 folds for each of 100 candidates, totalling 300 fits
[Parallel(n_jobs=1)]: Done 300 out of 300 | elapsed:    5.2s finished
RandomForestClassifier(bootstrap=True, class_weight=None, criterion='gini',
            max_depth=10, max_features=5, max_leaf_nodes=None,
            min_impurity_decrease=0.0, min_impurity_split=None,
            min_samples_leaf=1, min_samples_split=10,
            min_weight_fraction_leaf=0.0, n_estimators=10, n_jobs=1,
            oob_score=False, random_state=2525, verbose=0,
            warm_start=False)
acc:93.61 %
```
以下は、n_estimators以外は参考と同じハイパーパラメータを試しています。

```py
search_params = {
     'n_estimators'      : [5],   
      'max_features'      : [3, 5, 10, 15, 20],  
      'random_state'      : [2525],
      'n_jobs'            : [1],
      'min_samples_split' : [3, 5, 10, 15, 20, 25, 30, 40, 50, 100],  
      'max_depth'         : [3, 5, 10, 15, 20, 25, 30, 40, 50, 100]  
}
```
計算時間は、[Parallel(n_jobs=1)]: Done 1500 out of 1500 | elapsed:   31.5s finishedですぐフィッティングは終わります。
今回は精度が上がっています。

```
>python RFC.py
[Parallel(n_jobs=-1)]: Done   6 out of  10 | elapsed:    0.0s remaining:    0.0s
[Parallel(n_jobs=-1)]: Done  10 out of  10 | elapsed:    0.0s finished
[Parallel(n_jobs=8)]: Done   6 out of  10 | elapsed:    0.0s remaining:    0.0s
[Parallel(n_jobs=8)]: Done  10 out of  10 | elapsed:    0.0s finished
acc:96.67 %
[Parallel(n_jobs=8)]: Done   6 out of  10 | elapsed:    0.0s remaining:    0.0s
[Parallel(n_jobs=8)]: Done  10 out of  10 | elapsed:    0.0s finished
Fitting 3 folds for each of 500 candidates, totalling 1500 fits
[Parallel(n_jobs=1)]: Done 1500 out of 1500 | elapsed:   31.5s finished
RandomForestClassifier(bootstrap=True, class_weight=None, criterion='gini',
            max_depth=15, max_features=15, max_leaf_nodes=None,
            min_impurity_decrease=0.0, min_impurity_split=None,
            min_samples_leaf=1, min_samples_split=3,
            min_weight_fraction_leaf=0.0, n_estimators=10, n_jobs=1,
            oob_score=False, random_state=2525, verbose=0,
            warm_start=False)
acc:97.22 %
```
以下が参考のパラメータで実施した結果です。

```py
search_params = {
     'n_estimators'      : [5, 10, 20, 30, 50, 100, 300],   
      'max_features'      : [3, 5, 10, 15, 20],  
      'random_state'      : [2525],
      'n_jobs'            : [1],
      'min_samples_split' : [3, 5, 10, 15, 20, 25, 30, 40, 50, 100],  
      'max_depth'         : [3, 5, 10, 15, 20, 25, 30, 40, 50, 100]  
}
```
計算時間は[Parallel(n_jobs=1)]: Done 10500 out of 10500 | elapsed: 24.7min finishedかかっています。

```
>python RFC.py
[Parallel(n_jobs=-1)]: Done   6 out of  10 | elapsed:    0.0s remaining:    0.0s
[Parallel(n_jobs=-1)]: Done  10 out of  10 | elapsed:    0.0s finished
[Parallel(n_jobs=8)]: Done   6 out of  10 | elapsed:    0.0s remaining:    0.0s
[Parallel(n_jobs=8)]: Done  10 out of  10 | elapsed:    0.0s finished
acc:95.56 %
[Parallel(n_jobs=8)]: Done   6 out of  10 | elapsed:    0.0s remaining:    0.0s
[Parallel(n_jobs=8)]: Done  10 out of  10 | elapsed:    0.0s finished
Fitting 3 folds for each of 3500 candidates, totalling 10500 fits
[Parallel(n_jobs=1)]: Done 10500 out of 10500 | elapsed: 24.7min finished
RandomForestClassifier(bootstrap=True, class_weight=None, criterion='gini',
            max_depth=15, max_features=10, max_leaf_nodes=None,
            min_impurity_decrease=0.0, min_impurity_split=None,
            min_samples_leaf=1, min_samples_split=3,
            min_weight_fraction_leaf=0.0, n_estimators=100, n_jobs=1,
            oob_score=False, random_state=2525, verbose=0,
            warm_start=False)
acc:97.22 %
```
結果は以下のとおりでした。
![RFC_best_results.jpg](https://qiita-image-store.s3.amazonaws.com/0/233744/b459a1ad-465b-3918-fa24-3f38db424f04.jpeg)


###まとめ
・ランダムフォレストでクラス分類をやってみた
・簡単な計算量で９４％程度の精度が出ている
・GridSearchCVにより、ハイパーパラメーターを変更することにより、acc:97.22 %の精度となった

・音声にも適用できそうな感じだが、データの食わせ方などを整理する必要がある
・並列処理が失敗していること、GPUが使えていないのでまだまだ工夫が必要である
###おまけ
コード全体と表示、およびグラフは以下のとおり

```py
from sklearn import datasets
import numpy as np
import matplotlib.pyplot as plt
from sklearn.ensemble import RandomForestClassifier as RFC
from sklearn.model_selection import train_test_split, GridSearchCV

mnist = datasets.load_digits()
 
train_images, test_images, train_labels, test_labels = \
train_test_split(mnist.data, mnist.target, test_size=0.2)
plt.figure(figsize=(15,15))
for i in range(25):
    plt.subplot(5,5,i+1)
    plt.xticks([])
    plt.yticks([])
    plt.imshow(train_images[i].reshape((8,8)), cmap=plt.cm.binary)
    plt.xlabel(train_labels[i])
plt.savefig('RFC/dataset_input.jpg')
plt.pause(1)
plt.close()
```
![dataset_input.jpg](https://qiita-image-store.s3.amazonaws.com/0/233744/fde48002-9511-e35c-749b-6bc4d920e854.jpeg)


```py
clf = RFC(verbose=True,       # 学習中にログを表示します。この指定はなくてもOK
          n_jobs=-1,          # 複数のCPUコアを使って並列に学習します。-1は最大値。
          random_state=2525)  # 乱数のシードです。
clf.fit(train_images, train_labels)
```
以下のような出力がでている。

```
[Parallel(n_jobs=-1)]: Done   6 out of  10 | elapsed:    0.0s remaining:    0.0s
[Parallel(n_jobs=-1)]: Done  10 out of  10 | elapsed:    0.0s finished
[Parallel(n_jobs=8)]: Done   6 out of  10 | elapsed:    0.0s remaining:    0.0s
[Parallel(n_jobs=8)]: Done  10 out of  10 | elapsed:    0.0s finished
```
プリント文は以下のとおり変更

```py
#print(f"acc: {clf.score(test_images, test_labels)}")
acc=clf.score(test_images, test_labels)*100
print("acc:{:.2f} %".format(acc))
```
accの値は、毎回変わるがだいたい９２－９７％位

```
acc:96.67 %
```

```py
predicted_labels = clf.predict(test_images)
plt.figure(figsize=(15,15))
# 先頭から25枚テストデータを可視化
for i in range(25):
     # 画像を作成
    plt.subplot(5,5,i+1)
    plt.xticks([])
    plt.yticks([])
    plt.imshow(test_images[i].reshape((8,8)), cmap=plt.cm.binary)
     # 今プロットを作っている画像データの予測ラベルと正解ラベルをセット
    predicted_label = predicted_labels[i]
    true_label      = test_labels[i]
     # 予測ラベルが正解なら緑、不正解なら赤色を使う
    if predicted_label == true_label:
        color = 'green' # True label color
    else:
        color = 'blue'   # False label color red
    plt.xlabel("{} True({})".format(predicted_label,
                                  true_label), color=color, fontsize=18)
    if i==0:
        plt.title("acc:{:.2f} %".format(acc), fontsize=36)  
 
plt.savefig('RFC/RFC_results.jpg')
plt.pause(1)
plt.close()
```
![RFC_results.jpg](https://qiita-image-store.s3.amazonaws.com/0/233744/9514e307-69f4-badc-faa2-c8ef1679143e.jpeg)

以下は、上記のGridSearchCVの部分です。解説は本文見てください。
※因みに、ハイパーパラメータについては参考②のとおりです。

```py
search_params = {
     'n_estimators'      : [5, 10, 20, 30, 50, 100, 300],   
      'max_features'      : [3, 5, 10, 15, 20],  
      'random_state'      : [2525],
      'n_jobs'            : [1],
      'min_samples_split' : [3, 5, 10, 15, 20, 25, 30, 40, 50, 100],  
      'max_depth'         : [3, 5, 10, 15, 20, 25, 30, 40, 50, 100]  
}

gs = GridSearchCV(RFC(),           # 対象の機械学習モデル
                  search_params,   # 探索パラメタ辞書
                  cv=3,            # クロスバリデーションの分割数 3
                  verbose=True,    # ログ表示verbose=True
                  n_jobs=1)       # 並列処理 最大値-1
gs.fit(train_images, train_labels)
 
print(gs.best_estimator_)

acc=gs.score(test_images, test_labels)*100
print("acc:{:.2f} %".format(acc))
```
ここで以下の出力

```
[Parallel(n_jobs=8)]: Done   6 out of  10 | elapsed:    0.0s remaining:    0.0s
[Parallel(n_jobs=8)]: Done  10 out of  10 | elapsed:    0.0s finished
Fitting 3 folds for each of 3500 candidates, totalling 10500 fits
[Parallel(n_jobs=1)]: Done 10500 out of 10500 | elapsed: 24.7min finished
RandomForestClassifier(bootstrap=True, class_weight=None, criterion='gini',
            max_depth=15, max_features=10, max_leaf_nodes=None,
            min_impurity_decrease=0.0, min_impurity_split=None,
            min_samples_leaf=1, min_samples_split=3,
            min_weight_fraction_leaf=0.0, n_estimators=100, n_jobs=1,
            oob_score=False, random_state=2525, verbose=0,
            warm_start=False)
acc:97.22 %
```

```py
predicted_labels = gs.predict(test_images)

plt.figure(figsize=(15,15))
# 先頭から25枚テストデータを可視化
for i in range(25):
 
    # 画像を作成
    plt.subplot(5,5,i+1)
    plt.xticks([])
    plt.yticks([])
    plt.imshow(test_images[i].reshape((8,8)), cmap=plt.cm.binary)
 
    # 今プロットを作っている画像データの予測ラベルと正解ラベルをセット
    predicted_label = predicted_labels[i]
    true_label      = test_labels[i]
 
    # 予測ラベルが正解なら緑、不正解なら赤色を使う
    if predicted_label == true_label:
        color = 'green' # True label color
    else:
        color = 'blue'   # False label color red
    plt.xlabel("{} True({})".format(predicted_label,
                                  true_label), color=color, fontsize=18)
    if i==0:
        plt.title("acc:{:.2f} %".format(acc), fontsize=36)  
 
plt.savefig('RFC/RFC_best_results.jpg')
plt.pause(1)
plt.close()
```
最後に、print(clf.feature_importances_)（参考②を参照）というのを出力してみた。
以下の結果となったが、今一つ理解できていない。

```
[ 0.          0.00094352  0.01670567  0.01123247  0.00882619  0.01384746
  0.00939751  0.00126243  0.          0.00562329  0.02417195  0.00492873
  0.01397598  0.03589657  0.00410676  0.00021007  0.          0.00639884
  0.01714048  0.02075534  0.03832153  0.05506783  0.00534367  0.00058733
  0.00015253  0.0099321   0.04474083  0.02425292  0.02111824  0.02204334
  0.03807613  0.          0.          0.02444648  0.0302593   0.01526245
  0.05058831  0.02990348  0.02624532  0.          0.          0.00663288
  0.04416113  0.03860901  0.01708779  0.01542507  0.02436917  0.          0.
  0.00329738  0.0139552   0.03081125  0.02021156  0.01524503  0.03056448
  0.00266671  0.          0.00057159  0.02594106  0.00653645  0.03660243
  0.01690982  0.01596019  0.00267674]
```