【初心者】コピペでできる決定木とランダムフォレスト

スポンサーリンク
Python




非エンジニアサラリーマンがお届けする、コピペでできる機械学習シリーズの決定木ランダムフォレスト編です。問題解決にコードは最低限でいいと考えています。コードはコピーして、データ収集や結果の解釈に力を注いで下さい。

だれでも、すぐに実践できるようにscikit-learnのデータセットを使います。また、自身のデータでもすぐにコピペできるように、scikit-learnのデータセットを一旦データフレームにしてから機械学習を実施していきます。

環境構築は、【2020年最新】Anacondaのインストール方法

それでは、やっていきましょう!

必要なライブラリをインポート

import pandas as pd
import numpy as np
from sklearn.datasets import load_breast_cancer
import warnings
warnings.filterwarnings('ignore')

今回のデータセットはscikit-learnのデータセットにあるcancerを使用します。

データセットを整える

目的変数:ガンが良性(benign:0)悪性(malignant:1)
説明変数:さまざまな検査結果(ここでは詳細は言及しません、ゴメンナサイ)

cancer = load_breast_cancer() 
df = pd.DataFrame(data=cancer.data, columns=cancer.feature_names)
series1 = pd.Series(data=[cancer.target_names[i] for i in cancer.target], name="cancer")
df = pd.concat([df,series1], axis=1)
mapping = {'benign': 0, 'malignant': 1}
df['cancer'] = df['cancer'].map(mapping)
df

上のコードはデータフレームにするためのコードです。
cancerのデータセットで実践する場合は上のコードを実行して下さい。
自身のデータを使用する場合は、以下のコードから実行して下さい。

機械学習がしやすいデータの形に整える

X = df.iloc[:,0:30].values # 説明変数
y = df.iloc[:,-1].values   # 目的変数

Xは説明変数で、測定が30項目ある

yは目的変数で、一番後ろにある

from sklearn.model_selection import train_test_split, GridSearchCV
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size = 0.25,
    random_state = 1,
    stratify = y)

訓練用のデータとテスト用のデータに分割する

決定木

from sklearn.tree import DecisionTreeClassifier
tree = DecisionTreeClassifier(random_state=1)

from sklearn.model_selection import GridSearchCV

param_grid = {'criterion': ['gini', 'entropy'],
             'max_depth': [2, 3, 4, 5]
             }
gs_tree = GridSearchCV(tree,
                       param_grid = param_grid,
                       cv = 10
                      )
gs_tree.fit(X_train, y_train)

# 裁量スコアとなるパラメータ値を出力
print('Train accuracy_random_forest: %.3f' % gs_tree.best_score_)
print(gs_tree.best_params_)

y_pred=gs_tree.predict(X_test)

コードを実行すると
Train accuracy_random_forest: 0.937
{‘criterion’: ‘gini’, ‘max_depth’: 5}
が出力されると思います(バージョンによって多少は違うかも)

モデル作成に使用した訓練データでは正解率が93.7%です。

モデルの評価(決定木)

from sklearn.metrics import precision_score, recall_score, f1_score, roc_auc_score
print('Test accuracy: %.3f' % gs_tree.best_estimator_.score(X_test, y_test))
print('Precision: %.3f' % precision_score(y_test, y_pred))
print('Recall: %.3f' % recall_score(y_test, y_pred))
print('F1: %.3f' % f1_score(y_test, y_pred))
print('roc_auc: %.3f' % roc_auc_score(y_test, y_pred))

モデルの評価にはさまざまな方法がありますが、以下は一般的なものだと思います。上のコードを実行すると、以下が出力されます。
Test accuracy: 0.909
Precision: 0.955
Recall: 0.792
F1: 0.866
roc_auc: 0.885
ここでは、一番分かりやす正解率のみをみていきます。正解率は90.9%です。これを良いとするか悪いとするかは、問題の内容によって異なります。
もう少し分かりやすく、混同行列をみてみましょう

import matplotlib.pyplot as plt
from sklearn.metrics import confusion_matrix
# テストと予測のデータから混合行列を生成
confmat = confusion_matrix(y_true=y_test, y_pred=y_pred)

fig, ax = plt.subplots(figsize=(3, 3),dpi=200)
# matshow関数で行列からヒートマップを描画
ax.matshow(confmat, cmap=plt.cm.Blues, alpha=0.3)
for i in range(confmat.shape[0]):       # クラス0の繰り返し処理
    for j in range(confmat.shape[1]):   # クラス1の繰り返し処理
        ax.text(x=j, y=i, s=confmat[i, j], va='center', ha='center')  # 件数を表示

plt.xlabel('Predicted label')
plt.ylabel('True label')
plt.tight_layout()
plt.show()

出力は

予測は良性だが、実際は悪性(上図の左下)11と多いように思います。
決定木を可視化してみましょう。

from pydotplus import graph_from_dot_data
from sklearn.tree import export_graphviz
from IPython.display import Image

dot_data = export_graphviz(gs_tree.best_estimator_,
                           filled = True,
                           rounded = True,
                           class_names = ['malignant', 'benign'],
                           feature_names = cancer.feature_names
                          )

# 決定木のプロットを出力
graph = graph_from_dot_data(dot_data)
graph.write_png('tree.jpg')  
Image(graph.create_png())


もうちょっとイイ感じに

from dtreeviz.trees import dtreeviz


viz = dtreeviz(gs_tree.best_estimator_,
               X_train,
               y_train,
               target_name='cancer',
               feature_names=cancer.feature_names,
               class_names=cancer. target_names.tolist(),
               orientation='LR'
               )
display(viz)

出力

こんな感じでカットオフで分割することができ、理解しやすいですね。

ランダムフォレスト

ランダムフォレストは決定木を使ったアンサンブル学習です。アンサンブル学習とは、決定木をさくさん作って、それを総合して、モデルを作るものです。決定木ひとつだけでは、かたよりがあり少し頼りないから、たくさん作ってしまおう、というものですね。

from sklearn.ensemble import RandomForestClassifier

random_forest = RandomForestClassifier(n_estimators=50, random_state=1)
param_grid = {'max_depth': [3, 4, 5, 6, 7, 8],
              'criterion': ['gini', 'entropy'],
              }
random_forest_CV = GridSearchCV(estimator=random_forest,
                                param_grid=param_grid,
                                scoring='accuracy',
                                cv=10,
                                n_jobs = -1
                                )
random_forest_CV.fit(X_train, y_train)

# 裁量スコアとなるパラメータ値を出力
print('Train accuracy_random_forest: %.3f' % random_forest_CV.best_score_)
print(random_forest_CV.best_params_)

y_pred=random_forest_CV.predict(X_test)  # ランダムフォレストを使って予測を作る

出力
Train accuracy_random_forest: 0.960
{‘criterion’: ‘gini’, ‘max_depth’: 7}
正解率が96%、ランダムフォレストのハイパーパラメータがジニ不純で、決定木の分割が7ということです。正解率のみの理解で十分です。なんせ、わたしは理解不要のコピーコーダーなので。しかし、この正解率は訓練用のデータなので、以下にモデル学習に使用していない、テストデータで正解率をみてみましょう。

モデルの評価(ランダムフォレスト)

from sklearn.metrics import precision_score, recall_score, f1_score, roc_auc_score
print('Test accuracy: %.3f' % random_forest_CV.score(X_test, y_test))
print('Precision: %.3f' % precision_score(y_test, y_pred))
print('Recall: %.3f' % recall_score(y_test, y_pred))
print('F1: %.3f' % f1_score(y_test, y_pred))
print('roc_auc: %.3f' % roc_auc_score(y_test, y_pred))

出力
Test accuracy: 0.944
Precision: 0.979
Recall: 0.868
F1: 0.920
roc_auc: 0.928
正解率は94.4%で、決定木の90.9%より良くなりました。たった3.5%ですが、正解率は良いにこしたことはありません。場合によっては、数%が数千万円、数億円の違いになることもあります。
決定木と同じように混同行列をみてみましょう

import matplotlib.pyplot as plt
from sklearn.metrics import confusion_matrix
# テストと予測のデータから混合行列を生成
confmat = confusion_matrix(y_true=y_test, y_pred=y_pred)

fig, ax = plt.subplots(figsize=(3, 3),dpi=200)
# matshow関数で行列からヒートマップを描画
ax.matshow(confmat, cmap=plt.cm.Blues, alpha=0.3)
for i in range(confmat.shape[0]):       # クラス0の繰り返し処理
    for j in range(confmat.shape[1]):   # クラス1の繰り返し処理
        ax.text(x=j, y=i, s=confmat[i, j], va='center', ha='center')  # 件数を表示

plt.xlabel('Predicted label')
plt.ylabel('True label')
plt.tight_layout()
plt.show()


ちなみに、ランダムフォレストはいくつもの決定木を統合した結果なので、基本的にはひとつの決定木で表すことはできません。
決定木とランダムフォレストのコードは以上です。
決定木やランダムフォレストはデータの整理がいらないので、簡単です。このコードのコピペである程度の精度がでると思います。
これを使って、じゃんじゃん問題解決していきましょう。まだまだ、機械学習を通常の業務で使えている一般企業は少ないです(IT企業などはしりませんが)




コメント

タイトルとURLをコピーしました