Pythonで”k近傍法”を解く。-Pythonで機械学習を解くシリーズ-

Python

人工知能第3ブーム到来の昨今、機械学習を使ってみたい、あるいは使わなくてはならないという人が少なからずいるのではないでしょうか。しかし、機械学習技術を本当の意味で学ぶとなると数理統計学を始めとした高度な数学的知識からは逃れられないのが現実です。そんな中今の時代は大変恵まれていて、AIに関するライブラリが充実し、そういった数学的背景を知らなくても機械学習を実装することはできてしまいます(それが良いことなのかはさておき)。

私はそのライブラリの恩恵を最大に利用し、何事もまず体験してみることが重要だと考えています。

ここではそんな機械学習ライブラリの1つ「scikit-learn」を用いて機械学習を実装していきます。今回は「k-近傍法」という機械学習アルゴリズムを用いてアイリスという花のクラス分類を体験していきましょう。

k-近傍法

k-近傍法とは

k-近傍法は英語表記の「k-nearest neighbor algorithm」からk-NNとも呼ばれ、機械学習の中で最も単純な学習アルゴリズムと言われています。軽く定義すると以下のようになります。

  • 多次元の特徴空間において最も近くなるデータポイント(近傍点)に基づく分類手法

nearest neighbor algorithmの名に恥じぬ圧倒的わかりやすさです。

ここで1つの疑問が出てくるかもしれません。’k’とは一体何のことなんでしょう。実はこの’k’は近傍点の数を表しているのです。’k’ということで近傍点の数は任意に決められるということも同時に表されています。

それでは、近傍点が1つの場合を見ていきましょう(図1)。星印がテストデータです。最も近い1つのデータポイントのクラスに分類されているのがわかります。

図1 k-近傍法を用いた二値分類(近傍点:1)

続いて近傍点が3つの場合を見ていきましょう(図2)。近傍点が2つ以上になるとまず最も近いk個の近傍点を求めます。そしてその中から最も多く現れたクラスに分類される、という流れになります。

図2 k-近傍法を用いた二値分類(近傍点:3)

特徴

k-近傍法の最大のメリットは前述しました通り、その圧倒的理解しやすさにあります。教師あり学習の中でも群を抜いたわかりやすいアルゴリズムとなっています。

他にもメリットはございます。

それはモデル構築が非常に高速という点です。これはk-近傍法が実際に計算するのは予測時に行われるという「怠惰学習(lazy learning)」の一種であるためです。学習、つまりモデル構築の段階ではデータを覚えるだけなんですね。予測の段階で初めて近傍点を探し始めます。

対してデメリットもございます。

訓練データセットが大きくなると予測が遅くなります。これは上記のメリットと理由は同じで怠惰学習であることが要因です。数百以上の多数の特徴量を扱えないというのもデメリットの1つです。

k-近傍法によるアイリスの分類

irisデータセット

今回用いるデータセットのirisデータセットはscikit-learnのdatasetsモジュールに含まれています。load_iris関数で読み込みます。

from sklearn.datasets import load_iris
iris = load_iris()
print(iris.keys())
dict_keys(['data', 'target', 'target_names', 'DESCR', 'feature_names', 'filename'])

irisオブジェクトはディクショナリのように振る舞うBunchクラスのオブジェクトです。上のコードでは読み込んだ後、keyを表示させています。

‘data’と’target’にはそれぞれデータとそれに対応するラベルが、’target_names’はラベルの名前、今回だと花の種類が、’DESCR’には簡単なデータセットの説明、’feature_names’は特徴量の名前、’filename’にはデータのファイルパスがそれぞれ格納されています。

実際に自分で中身を見てみるのが一番いいでしょう。それでは、’data’キーの中身を少し調査してみましょう。

iris_data = iris['data'] #iris.dataでも可
print('shape: {}'.format(iris_data.shape))
print('the first three data:\n{}'.format(iris_data[:3]))
shape: (150, 4)
the first three data:
[[5.1 3.5 1.4 0.2]
 [4.9 3.  1.4 0.2]
 [4.7 3.2 1.3 0.2]]

データセットのキーはiris.dataのように属性変数のようにも振る舞います。dataのshapeを見てもらうとわかる通り、特徴量4つの150個のデータセットだということがわかります。上から3つのデータを表示させています。

モデルの構築

それでは先ほど読み込んだアイリスのデータセットを使ってk-近傍法で分類していきます。今回は近傍点の数を1に設定します。

from sklearn.model_selection import train_test_split
from sklearn.neighbors import KNeighborsClassifier
X_train, X_test, y_train, y_test = train_test_split(
    iris['data'], iris['target'], random_state=0)
knn = KNeighborsClassifier(n_neighbors=1)
knn.fit(X_train, y_train)

まず1,2行目で必要なクラスをインポートしています。4,5行目ではデータセットを訓練データとテストデータに分けています。

機械学習はそのモデルがどのくらいの精度なのかを評価する必要があります。その際、学習に使ったデータを使うことができず全く新しいデータを使う必要があります(既に学習したデータを予測することができるのは当たり前だから)。そのため学習に使うデータ、評価に使うデータと分けているのです。割合は3:1です。X_train、X_testがそれぞれ訓練データ、テストデータでy_train、y_testがそれぞれ訓練データの正解ラベル、テストデータの正解ラベルです。

7行目はknnというKNeighborsClassifierオブジェクトを作成しています。引数に近傍点の数を指定しています。これには学習のアルゴリズムと予測のアルゴリズムがカプセル化されています。

8行目でknnのfitメソッドを使いモデルの構築を行います。引数に訓練データとその正解ラベルを渡しています。

これだけでモデルの構築が完了してしまいました。fitメソッドで完結してしまう点は罪悪感を覚えるほど簡単です。

モデルの評価

それでは先ほど作成したモデルの評価をしていきます。復習となりますが、モデルの評価は未だ見たことがない新たなデータに対しても予測ができているかを測定しています。ちなみにこのようなどのデータに対してもうまく機能している状態を「汎化」といいます。

モデルの評価も構築の時同様、何も難しいことはなくscoreメソッドを用いるだけです。

knn.score(X_test, y_test)
0.9736842105263158

引数にテストデータとその正解ラベルを渡しています。これによって、knnというモデルは約97%の精度が得られることがわかります。

モデルの予測

それではこのモデルを使って実際にアイリスの分類をしていきましょう。

ここに一輪のアイリスがあるとします。そのアイリスは6cmのガク、1.7cmのガクを持ち、花弁の長さが5.4cm、幅が1.8cmだとします。このアイリスの品種はなんでしょうか。モデルの予測にはpredictメソッドを用います。

import numpy as np
new_iris = np.array([[6, 1.7, 5.4, 1.8]])
prediction = knn.predict(new_iris)
print('predicted number: {}'.format(prediction))
print('predicted target: {}'.format(iris['target_names'][prediction]))
predicted number: [2]
predicted target: ['virginica']

新たなアイリスのデータが2次元のNumpy配列になっていることに注意してください。irisのデータの形式と合わせるために2次元のNumpy配列にする必要があります。

それを引数にpredictメソッドを実行します。出力は[2]、knnが予測した品種は’virginica‘でした。

先ほど精度が97%と非常に高かったので信憑性が高い結果です。

コメント