case-kの備忘録

日々の備忘録です。最近はGCPやデータ分析系のことを呟きます

ランダムフォレストで癌の良性・悪性を分類予測してみた

ランダムフォレストで癌の良性・悪性を分類分類予測してみました。以前にChainerで同様の癌の分類問題を行ったので、比較してみたいと思います。

ランダムフォレストとは

ランダムフォレストは分類問題でよく用いられる手法です。ランダムフォレストは少しずつ異なる決定木という手法を集約させたものです。決定木は分類するために重要な説明変数や分類の過程がわかりやすいので、よく分類問題で用いられる手法ですが問題点があります。決定木には過剰適合しやすく汎化性能が低くなってしまう問題があります。過剰適合とは訓練データに対しては高い精度を出しますが、未知のテストデータに対しては高い精度を出すことができません。ランダムフォレストはこの決定木が過剰適合してしまうことに対する解決策の1つで、それぞれ異なった方向に過剰適合した決定木をたくさん作ってその平均を取れば過剰適合の度合いを減らし、決定木の予測性能を維持したまま汎化性能を高めるための方法です。つまり、ランダムフォレストとは過剰適合したモデルの平均をとって汎化性を高めた手法です。

ランダムフォレストのメリット・デメリット

分類アルゴリズムが複数ある中で、ランダムフォレストのメリットとデメリットをまとめました。

メリット

・汎化性が高い
・デフォルトのパラメータで十分よく機能する
・決定木同様にランダムフォレストでも特徴量の重要度をみることができる。
基本的に決定木の過剰適合してしまう欠点を補うためのアルゴリズムです。あえて決定木を使う場合があるとすれば、簡易な分類プロセスを可視化させたい場合だと言えます。

デメリット

・テキストデータなど非常に高次元のデータに対してうまく機能しない
・乱数のシード(random_state)を変更すると構築するモデルが大きく変わる
・メモリ消費量
・実行時間
テキストデータの分類や、メモリ消費量・実行時間が重要なアプリケーションには適さないように思います。

実装編

概要はこのくらいにして実際にランダムフォレストで顔の分類予測問題を解いてみたいと思います。
今回はランダムフォレストの便利関数で分類において重要な役割を果たしている説明変数を可視化し、モデルを再構築してみたいと思います。

・データの取得・加工
・説明変数・目的変数に分割
・訓練データ・テストデータ分割
・モデルの構築・予測
・重要な説明変数を確認
・重要なパラメータを活用してモデルの再構築

# library
from sklearn.datasets import load_breast_cancer
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import train_test_split
import pandas as pd
import numpy as np
from matplotlib import pyplot as plt
%matplotlib inline
import seaborn as sns
sns.set

データの取得・加工

cancer = load_breast_cancer()
print(cancer.keys())

# Out
dict_keys(['target_names', 'target', 'DESCR', 'feature_names', 'data'])
# function 
def get_target_names(x):
    if x == 0:
        return "malignant"
    if x == 1:
        return "benign"
cancer_data = pd.DataFrame(columns=cancer['feature_names'],data = cancer['data'])
cancer_data['target'] = cancer['target']
cancer_data["target_names"] = cancer_data['target'].apply(lambda x : get_target_names(x))
cancer_data.head(5)

f:id:casekblog:20180830201032p:plain:w500

説明変数・目的変数に分割

X = cancer_data.iloc[:,0:30]
y = cancer_data['target']
print("X shape:{}".format(X.shape))
print("y shape:{}".format(y.shape))

# Out
X shape:(569, 30)
y shape:(569,)

訓練データ・テストデータ分割

X_train, X_test, y_train, y_test = train_test_split(X,y,random_state=42)

モデルの構築・予測

forest = RandomForestClassifier(n_estimators=5, random_state=42)
# n_estimators
forest.fit(X_train,y_train)
print("Accuracy on training set:{:.3f}".format(forest.score(X_train,y_train)))
print("Accuracy on  test set:{:.3f}".format(forest.score(X_test,y_test)))

# Out
Accuracy on training set:0.991
Accuracy on  test set:0.958

パラメータのチューニングも行っていないのにかなり精度の高いモデルを構築できたのではないでしょうか?
Chainerと比較して見るとわかりやすいですが、パラメータチューニングなしだとChainerは訓練データに対して62%、テストデータに対して65%でした。
case-k.hatenablog.com
このようにランダムフォレストはデフォルトで十分な評価精度を算出することが可能です。


重要な説明変数を確認

def plot_feature_importances(model, X):
    n_features = X.shape[1]
    plt.barh(range(n_features), model.feature_importances_, align='center')
    plt.yticks(np.arange(n_features), X)
    plt.xlabel("Feature importance")
    plt.ylabel("Feature")
plot_feature_importances(forest, X)

f:id:casekblog:20180830201110p:plain:w500

分類問題で重要な説明変数を可視化できました。
説明変数「worst concave points」「 worst radius」「area error」
が分類予測において重要な役割を果たしているようです。
説明変数3つでモデルを再構築してみます。

重要なパラメータを活用してモデルの再構築

# function
def get_feature_importances(model, X):
    feature_importances = pd.DataFrame()
    feature =   X.keys()
    importances = model.feature_importances_
    feature_importances['feature'] = feature
    feature_importances['importance'] = importances
    return feature_importances

関数を定義できましたので重要な説明変数を降順でみてみたいと思います。

df_feature_importances = get_feature_importances(forest, X)
df_feature_importances_sort = df_feature_importances.sort_values(by='importance',ascending=False).head()
df_feature_importances_sort.head()

f:id:casekblog:20180830201124p:plain:w500

重要とされる上位4つの説明変数を使ってモデルを再構築してみます。

columns = ['worst concave points','worst radius', 'area error','worst concavity']
X = cancer_data[columns]
y = cancer_data['target']
X_train, X_test, y_train, y_test = train_test_split(X,y,random_state=42)
forest = RandomForestClassifier(n_estimators=5, random_state=42)
# n_estimators
forest.fit(X_train,y_train)
print("Accuracy on training set:{:.3f}".format(forest.score(X_train,y_train)))
print("Accuracy on  test set:{:.3f}".format(forest.score(X_test,y_test)))

# Out
Accuracy on training set:0.993
Accuracy on  test set:0.958

説明変数4つでテストデータに対して、説明変数30個分と同等の評価精度を確認できました。
このようにランダムフォレストは重要な説明変数を探索するのに適しています。
画像分類やテキストはディープラーニングのChainerが適しているように思いましたが、癌の分類予測問題はランダムフォレストの方が適しているように思いました。