今回は、決定木(けっていぎ、decision tree)の機械学習アルゴリズムによる分類方法をご紹介します。決定木は、元々は(リスクマネジメントなどの)決定理論の分野において、 決定を行うため、計画を立案して目標に到達するために用いられるグラフで、意志決定を助けることを目的として作られたものです。 決定木は木構造の特別な形をしています。

機械学習の分野においては決定木は予測モデルであり、ある事項に対する観察結果から、その事項の目標値に関する結論(分類結果)を導きます。内部節点は変数に対応し、子節点への枝はその変数の取り得る値を示します。 葉(端点)は、根(root)からの経路によって表される変数値に対して、目的変数の予測値を表します。 データから決定木を作る機械学習の手法のことを決定木学習、あるいはくだけた言い方では単に決定木と呼びます。それでは、具体的に決定木を使用した機械学習を行って行きましょう。

 

 

今回、使用するデータは、オープンソースであるUCI機械学習リポジトリから、「ウィスコンシン肺がんデータ」を使用します。569のデータからなり、第1列目はサンプルのID番号、2列目に’M’(悪性)または'B'(良性)かの分類クラスが、3列目から32列目までが変量になります。

 

https://archive.ics.uci.edu/ml/machine-learning-databases/breast-cancer-wisconsin/wdbc.data

 

30の変量がありますが、2次元の図にプロットする都合上、主成分分析により、2つの主成分を抽出し、Pythonを使用して肺がんが悪性か良性かを予測する機械学習モデルを作成します。主成分分析とPythonを使用した機械学習の流れは、概ね以下になります。決定木においては、入力データの標準化(Standardization)は不要です。なお、Pythonのバージョンは3.5以上を想定しています。

 

  1. プロット出力用の関数を定義する。
  2. データを入力する。
  3. 入力データを、トレーニングデータとテストデータに分ける。
  4. 適切なモデル(Classifier)を選択する。
  5. 主成分分析により、入力データから上位2つの主成分を抽出する。
  6. 主成分分析により抽出されたトレーニングデータを使用して、モデルに機械学習させる。
  7. テストデータを使用して、ラベルの分類を行い、モデルを評価する。
  8. 学習結果を図にプロットする。
  9. 決定木の図を作成する。

では、各ステップを詳しく見ていきましょう。

 

①プロット出力用の関数を定義する。

まず、対話形式ではなく、Pythonのスクリプトをファイルで用意します。Pythonスクリプトの先頭に以下の2行を添付しておくことをお勧めします。

#!/usr/bin/env python
# -*- coding:utf-8 -*-

 

ここでは、以下のように、「plot_decision_regions」という名前のプロット出力用の関数を定義します。第6回から第10回で使用したものとほぼ同じ関数ですが、plot_decision_regions関数の引数がresolution=0.5となっている点だけが、これまでと異なります。

 

import numpy as np

import matplotlib.pyplot as plt

from matplotlib.colors import ListedColormap

 

def plot_decision_regions(X, y, classifier, resolution=0.5):

    # setup marker generator and color map
    markers = ('s', 'x', 'o', '^', 'v')
    colors = ('red', 'blue', 'lightgreen', 'gray', 'cyan')
    cmap = ListedColormap(colors[:len(np.unique(y))])

    # plot the decision surface
    x1_min, x1_max = X[:, 0].min() - 1, X[:, 0].max() + 1
    x2_min, x2_max = X[:, 1].min() - 1, X[:, 1].max() + 1
    xx1, xx2 = np.meshgrid(np.arange(x1_min, x1_max, resolution),
                         np.arange(x2_min, x2_max, resolution))
    Z = classifier.predict(np.array([xx1.ravel(), xx2.ravel()]).T)
    Z = Z.reshape(xx1.shape)
    plt.contourf(xx1, xx2, Z, alpha=0.4, cmap=cmap)
    plt.xlim(xx1.min(), xx1.max())
    plt.ylim(xx2.min(), xx2.max())

    # plot class samples
    for idx, cl in enumerate(np.unique(y)):
        plt.scatter(x=X[y == cl, 0], y=X[y == cl, 1],
                    alpha=0.8, c=cmap(idx),
                    marker=markers[idx], label=cl)
            

 

②データを入力する。

今回は、冒頭でご紹介した「ウィスコンシン肺がんデータ」を、オープンソースとして提供しているサイトのURLから、pandasライブラリーを使用して以下のようにデータを抽出します。変量Xの配列(569 x 30)に、ラベル(悪性か良性か)を y(569x 1)という配列に569サンプル分のデータを格納します。sklearn.preprocessingライブラリーのLabelEncoder関数を使用して、ラベルの'M'(悪性)を数字の'1'に、'B'(良性)を数字の'0'に、それぞれ変換しています。

 

import pandas as pd

df = pd.read_csv('https://archive.ics.uci.edu/ml/machine-learning-databases/breast-cancer-wisconsin/wdbc.data', header=None)

 

from sklearn.preprocessing import LabelEncoder

X = df.loc[:, 2:].values

y = df.loc[:, 1].values

le = LabelEncoder()

y = le.fit_transform(y)

 

③入力データを、トレーニングデータとテストデータに分ける。

scikit-learning.model_selectionライブラリーのtrain_test_split関数を使用して、

変量配列Xとラベル配列yについて、トレーニングデータとテストデータに分けます。変量配列Xを、それぞれ、X_train配列, X_test配列に分割し、ラベル配列yは、y_tarin配列, y_test配列へそれぞれ分割します。test_sizeのパラメータにより、テストデータの割合を指定できます。ここでは、0.2を指定することで、テストデータの割合を全体の20%と指定しています。全569サンプルの20%(= 114サンプル)がテストデータで、残りの455サンプルがトレーニングデータとなります。random_state=1を指定することにより、ランダムにトレーニングデータとテストデータを分割することができます。

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


④適切なモデル(Classifier)を選択する。

様々なClassifierがscikit-learnライブラーの中でサポートされています。線形データとして分類できる場合は、Perceptron, Adaptive Linear Neuron(Adaline), Logistic regulation, Support Vector Machines(SVM), Decision tree, Random forests, K-nearest neighbors(KNN)などがあります。今回は、Decision treeを選択することにいたします。sklearn.treeライブラリーのDecisionTreeClassifier関数を使用して以下のように記述します。

from sklearn.tree import DecisionTreeClassifier

dt = DecisionTreeClassifier(max_depth=5,

                              criterion='entropy',

                              random_state=0)

 

まず、max_depthで決定木の最大の階層を指定します(デフォルトはNone)。もし Noneが指定された場合は、全ての葉が最下層になるか、全ての葉が、「min_samples_split」パラメータで指定された数より少ないサンプルを含むまで、節点が展開されます。 criterionには、分岐の質を測定するための関数です。criteriaに“ gini ”を指定すると「Gini impurity」に、 “entropy”を指定すると、「information gain」となります。デフォルトは、“ gini ”です。

 

⑤主成分分析により、入力データから上位2つの主成分を抽出する。

sklearn.decompositionライブラリーの中でサポートされているPCA関数を用いて、トレーニングデータについて主成分分析を行い、トレーニングデータ及びテストデータについて、2つの主成分を抽出します。

 

from sklearn.decomposition import PCA

pca = PCA(n_components=2)

X_train_pca = pca.fit_transform(X_train,y_train)

X_test_pca = pca.transform(X_test)

 

⑥主成分分析により抽出されたトレーニングデータを使用して、モデルに機械学習させる。

トレーニングデータにfitメソッドを適用して、学習させます。

dt.fit(X_train_pca, y_train)

 

⑦テストデータを使用して、ラベルの分類を行い、モデルを評価する。

テストデータを使用して、ラベルの分類を行い、sklearn.metricsライブラリーのaccuracy_score関数を用いて、モデルの精度を評価します。

 

from sklearn.metrics import accuracy_score

y_pred = dt.predict(X_test_pca)

print('Accuracy: %.3f' % accuracy_score(y_test,y_pred))

         

出力結果は、以下のように93.9%の精度と表示されます。

Accuracy: 0.939

 

決定木(Decision tree)にご興味のある方は、以下の本の第3章を是非、読んでみてください。

「Python Machine Learning: Unlock Deeper Insights into Machine Learning With This Vital Guide to Cutting-edge Predictive Analytics」

Sebastian Raschka (著)

出版社: Packt Publishing (2015/9/23)

言語: 英語

ISBN-10: 1783555130

ISBN-13: 978-1783555130

 

⑧学習結果を図にプロットする。

①で定義したplot_decision_regions関数を用いて、トレーニングデータとテストデータについて、それぞれ、抽出した第1主成分(PC1)を横軸に、第2主成分(PC2)を縦軸にした2次元領域にプロットします。

 

まず、トレーニングデータについてプロットします。

plot_decision_regions(X_train_pca, y_train, classifier=dt)

plt.xlabel('PC 1')

plt.ylabel('PC 2')

plt.legend(loc='lower left')

plt.tight_layout()

plt.show()

 

×印と青い領域が「悪性: 1」、■印と赤い領域が「良性:0」を表しています。

多数のサンプルに例外があり、赤と青が混在している領域があります。トレーニングデータについて、あまり良く、分類されていないことがわかります。

 

次に、テストデータについてプロットします。

plot_decision_regions(X_test_pca, y_test, classifier=dt)

plt.xlabel('PC 1')

plt.ylabel('PC 2')

plt.legend(loc='lower left')

plt.tight_layout()

plt.show()

 

テストデータについては、5つのサンプルに例外がありますが、概ね、領域で、分類されていることがわかります。

 

⑨決定木の図を作成する。

sklearn.treeライブラリーのexport_graphviz関数を使用することにより、学習結果を決定木の図で表示することができます。ここでは、出力ファイル名を「tree1.dot」として、以下のように記述します。

 

from sklearn.tree import export_graphviz

export_graphviz(dt,out_file='tree1.dot',feature_names=['PC1','PC2'])

 

上記コードを実行すると、カレントディレクトリに「tree1.dot」というファイルが作成されます。以下のサイトから、GraphVizプログラムをダウンロードして、導入します。

 

http://www.graphviz.org

 

導入後はGraphVizの実行ファイルが保管されているディレクトリを環境変数 PATHに追加してください。例えばWindowsの場合は、「システムの詳細設定」から「環境変数」を選択し、環境変数Pathに「C:\Program Files (x86)\Graphviz2.38\bin」の値を追加します。

 

コマンドプロンプトで、以下のコマンドを実行することにより、tree1.dotをPNG形式のイメージファイルに変換することができます。

 

> dot -Tpng tree1.dot -o tree1.png

 

変換したtree1.pngファイルをイメージビューワーで開くと、以下のような決定木が表示されます。

 

全体を通してのコードは以下のようになります。なお、本コードの稼働環境は、Python3.5以上を想定しています。

#!/usr/bin/env python
# -*- coding:utf-8 -*-

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.colors import ListedColormap

def plot_decision_regions(X, y, classifier, resolution=0.5):

    # setup marker generator and color map
    markers = ('s', 'x', 'o', '^', 'v')
    colors = ('red', 'blue', 'lightgreen', 'gray', 'cyan')
    cmap = ListedColormap(colors[:len(np.unique(y))])

    # plot the decision surface
    x1_min, x1_max = X[:, 0].min() - 1, X[:, 0].max() + 1
    x2_min, x2_max = X[:, 1].min() - 1, X[:, 1].max() + 1
    xx1, xx2 = np.meshgrid(np.arange(x1_min, x1_max, resolution),
                         np.arange(x2_min, x2_max, resolution))
    Z = classifier.predict(np.array([xx1.ravel(), xx2.ravel()]).T)
    Z = Z.reshape(xx1.shape)
    plt.contourf(xx1, xx2, Z, alpha=0.4, cmap=cmap)
    plt.xlim(xx1.min(), xx1.max())
    plt.ylim(xx2.min(), xx2.max())

    # plot class samples
    for idx, cl in enumerate(np.unique(y)):
        plt.scatter(x=X[y == cl, 0], y=X[y == cl, 1],
                    alpha=0.8, c=cmap(idx),
                    marker=markers[idx], label=cl)


import pandas as pd

df = pd.read_csv('https://archive.ics.uci.edu/ml/machine-learning-databases/breast-cancer-wisconsin/wdbc.data', header=None)

from sklearn.preprocessing import LabelEncoder
X = df.loc[:, 2:].values
y = df.loc[:, 1].values
le = LabelEncoder()
y = le.fit_transform(y)
#le.transform(['M', 'B'])

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

from sklearn.tree import DecisionTreeClassifier
dt = DecisionTreeClassifier(max_depth=5,
                              criterion='entropy',
                              random_state=0)

from sklearn.decomposition import PCA
pca = PCA(n_components=2)
X_train_pca = pca.fit_transform(X_train,y_train)
X_test_pca = pca.transform(X_test)
dt.fit(X_train_pca, y_train)

from sklearn.metrics import accuracy_score
y_pred = dt.predict(X_test_pca)
print('Accuracy: %.3f' % accuracy_score(y_test,y_pred))

plot_decision_regions(X_train_pca, y_train, classifier=dt)
plt.xlabel('PC 1')
plt.ylabel('PC 2')
plt.legend(loc='lower left')
plt.tight_layout()
plt.show()

plot_decision_regions(X_test_pca, y_test, classifier=dt)
plt.xlabel('PC 1')
plt.ylabel('PC 2')
plt.legend(loc='lower left')
plt.tight_layout()
plt.show()

from sklearn.tree import export_graphviz
export_graphviz(dt,out_file='tree1.dot',feature_names=['PC1','PC2'])