Scikit-Learn アイリスのクラス分類で基本を学ぶ

概要

本投稿では、Pythonを用いた機械学習の導入として、非常に有名なアイリス(アヤメ科の花)の種類の分類を行います。前提として、アイリスについて、花弁の長さと幅、ガクの長さと幅の測定結果と、setosa, versicolor, virginicaの3種類の分類結果を持っているものとします。

この機械学習モデルの目標は、新しく発見したアイリスの種類を、花弁の長さと幅、ガクの長さと幅の値から予測することです。このような問題は、クラス分類(Classification)問題の一例となります。

データの読み込み

ここで用いるアイリスのデータセットは、scikit-learnのdatasetに含まれており、下記の手順でロードします。

from sklearn.datasets import load_iris
iris_dataset = load_iris()

データの確認

keys

読み込んだデータのKeyを確認することが可能です。

print(iris_dataset.keys())
# dict_keys(['data', 'target', 'frame', 'target_names', 'DESCR', 'feature_names', 'filename'])

DESCR

KeyのDESCRはデータセットの説明(description)です。データセットの概要を確認できます。この場合は、データ数や特徴量、分類したいクラス名、特徴量の最大値や最小値などを把握できます。

print(iris_dataset['DESCR'][:1000])
'''
.. _iris_dataset:

Iris plants dataset
--------------------

**Data Set Characteristics:**

    :Number of Instances: 150 (50 in each of three classes)
    :Number of Attributes: 4 numeric, predictive attributes and the class
    :Attribute Information:
        - sepal length in cm
        - sepal width in cm
        - petal length in cm
        - petal width in cm
        - class:
                - Iris-Setosa
                - Iris-Versicolour
                - Iris-Virginica
                
    :Summary Statistics:

    ============== ==== ==== ======= ===== ====================
                    Min  Max   Mean    SD   Class Correlation
    ============== ==== ==== ======= ===== ====================
    sepal length:   4.3  7.9   5.84   0.83    0.7826
    sepal width:    2.0  4.4   3.05   0.43   -0.4194
    petal length:   1.0  6.9   3.76   1.76    0.9490  (high!)
    petal width:    0.1  2.5   1.20   0.76    0.9565  (high!)
    ============== ==== ==== ======= ===== ====================
'''

target_names

target_namesには、予測したいアイリスの種類が格納されています。

iris_dataset['target_names']
# array(['setosa', 'versicolor', 'virginica'], dtype='<U10')

fearute_names

feature_namesには、ガクの長さや幅など、特徴量の説明が格納されています。

iris_dataset['feature_names']
# ['sepal length (cm)',  # ガクの長さ
#  'sepal width (cm)',   # ガクの幅
#  'petal length (cm)',  # 花弁の長さ
#  'petal width (cm)']   # 花弁の幅

data

データはdataに格納されており、4つの特徴量の値がnumpy.ndarrayとして格納されています。配列のshapeを確認してみると、150個のアイリスの測定結果が格納されていることが分かります。。下のコードでは、dataの先頭5行の中身を確認しています。

type(iris_dataset['data'])
# numpy.ndarray

iris_dataset['data'].shape
# (150, 4)

iris_dataset['data'][:5]
# array([[5.1, 3.5, 1.4, 0.2],
#        [4.9, 3. , 1.4, 0.2],
#        [4.7, 3.2, 1.3, 0.2],
#        [4.6, 3.1, 1.5, 0.2],
#        [5. , 3.6, 1.4, 0.2]])

target

targetには、測定した150個のアイリスの種類の正解ラベルが格納されています。

iris_dataset['target']
'''
#array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
       2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
       2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2])
'''

target_names

targetの0, 1, 2の値は、それぞれ花の種類を表しています。

iris_dataset['target_names']
# array(['setosa', 'versicolor', 'virginica'], dtype='<U10')

機械学習モデルの作成

訓練データとテストデータ

機械学習では、既に持っているデータを全てモデル構築に使用して、その精度を評価することはできません。これは、作成した機械学習のモデルが手持ちのデータの正解を覚えただけで、まだ見たことのない新しいデータに対しては、汎化出来ているか分からないからです。

そこで、モデルの性能を評価するためには、モデルにとって新しいデータを使用する必要があります。そのためには、手持ちのデータセットを訓練データとテストデータの2つに分けることが一般的です。

from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(iris_dataset['data'], iris_dataset['target'], random_state = 0)

train_test_split関数では、引数としたデータを一度シャッフルし、train:75% test:25%にデータ数を分割します。shape属性をそれぞれ参照することで、分割出来ていることを確認できます。また、random_stateパラメータを0に固定することで、train_test_split関数を何度呼び出しても出力が固定され、同じ結果を得ることができます。

print(X_train.shape)
print(y_train.shape)
# (112, 4)
# (112,)
print(X_test.shape)
print(y_test.shape)
# (38, 4)
# (38,)

ペアプロットの作成

データを観察するために、特徴量の組み合わせをプロットします。まずは訓練データからpandas.DataFrameを作成し、’species’列を追加した後、ラベル名を花の種類に変更して、Pythonのグラフ描画ライブラリであるseabornを使用して、ペアプロットを描画しています。

import seaborn as sns
df = pd.DataFrame(X_train, columns = iris_dataset.feature_names)
df['species'] = y_train
df.loc[df['species'] == 0, 'species'] = 'setosa'
df.loc[df['species'] == 1, 'species'] = 'versicolor'
df.loc[df['species'] == 2, 'species'] = 'virginica'
df.head()
import seaborn as sns
pg = sns.pairplot(df, hue = 'species', palette = 'dark', plot_kws = {'alpha':0.8})

グラフから、4つの特徴量によって、花の種類は比較的分離していることが確認できます。

k-最近傍法

今回は、とても理解しやすいモデルであるk-最近傍法(k-Nearest Neighbors)によるクラス分類を使用します。k-最近傍法では、予測したいデータの点に対して、最も近い訓練データの点を探すことで、ラベルを判定します。

まずは、以下の手順でkNeighborsClassifierオブジェクトを生成します。引数n_neighborsに、近傍点の数を指定します。

from sklearn.neighbors import KNeighborsClassifier
knn = KNeighborsClassifier(n_neighbors = 3)

訓練データからモデルを構築するには、作成したknnオブジェクトのfitメソッドを呼び出すだけで良いです。引数には、訓練データのX_trainと、そのラベルであるy_trainを指定します。

knn.fit(X_train, y_train)
# KNeighborsClassifier(n_neighbors=3)

テストデータ(X_test)の花の種類を予測するには、knnオブジェクトのpredictメソッドを使用します。この結果を表示させると、0, 1, 2の花の種類が得られていることを確認できます。

y_pred = knn.predict(X_test)
print(y_pred)
#[2 1 0 2 0 2 0 1 1 1 2 1 1 1 1 0 1 1 0 0 2 1 0 0 2 0 0 1 1 0 2 1 0 2 2 1 0 2]

作成したモデルの精度を評価するためには、scoreメソッドを用います。今回作成したモデルでは、テストデータの正解ラベル(y_test)に対して、約97%の精度で予測が出来ていることを確認できました。

knn.score(X_test, y_test)
# 0.9736842105263158