scikit-learnでk最近傍法(kNN法)を使う方法

scikit-learnでk最近傍法(kNN法)を使う方法
えびかずき
えびかずき

こんにちは、えびかずきです。
今回はscikit-learnでk最近傍法(kNN法)を使う方法について説明していきます。

こんな人におすすめ:
scikit-learnの使い方を知りたい
・k近傍法とは何か学びたい

結論として、sklearn.neighborsモジュールのKNeighborsClassifierクラスを使うことで、k最近傍法を実装できます。

開発環境

Python 3.7.3

scikit-learn 0.24.2
NumPy 1.20.2

IDE:jupyter Notebook

k最近傍法とは何か

まずk最近傍法とは何かについて、簡単に説明しておきます。

k最近傍(k-Nearest Neighbor,kNN)法とは、その名前のとおり、分類したいデータの周辺に着目して、クラス分類をおこなう機械学習手法です。

kNN法の概念はかなり単純です。

分類したいデータに距離が最も近いk個の教師データを探して、多数決でクラス分けをします。

kNNの本質はこの一文で説明できます。とても理解しやすくて簡単ですね。

では図を使って具体的に説明していきましょう。

k=1の場合

まずk=1の場合ですが、

この時は分類したいデータ(図中のtest pred)に一番近い教師データ(図中のtraining class)を1つ探して、そのクラスを分類結果として与えます。

多数決に1つの教師データしか使わないので、一番近い教師データのクラスがそのまま分類結果になるというわけです。

分類の様子を表したものが下図です。

k=1でのkNN分類結果
https://github.com/amueller/introduction_to_ml_with_python

k=3の場合

続いてk=3の場合をみていきましょう。

この時は分類したいデータに一番近い教師データを3つ探し出して、その教師データの多数決で分類結果を決めます。

下図では3つのテストデータをk=3で分類した様子を示しています。

k=3でのkNN分類結果
https://github.com/amueller/introduction_to_ml_with_python

kを大きくすると、教師データとの整合性が低くなりますが、過学習を回避できるというメリットがあります。

要するに、分類器としての汎化性能が上がるということですね。

では最適なkの値はどうやって見つければ良いのでしょうか?

その方法は、一般的には実際に具体的なデータを使って交差確認法などで誤り率が最も低くなるところを探すという経験的なものです。

要するに、横軸にkの値、縦軸に誤り率を取って図示すればよいという訳ですね。

ボロノイ図について

ちなみに、k=1において各教師データそれぞれにおける最近傍の領域を区分けすると、下図のようになります。

これをロシアの数学者ボロノイちなんでボロノイ図という名前がついています。

これに関係して、各点の境界面(2次元データの時は線)をボロノイ境界と呼ぶことがあります。

ボロノイ図/Wikipedia

kNN法の特徴

kNN法は概念が単純で理解しやすく、クラス分類として優れた手法です。

しかしながら、分類したいデータと各教師データとの距離を全て計算して距離が最小になる時の教師データを見つける必要があります。

したがって、計算量がかなり多くなってしまうという欠点があります。

分類精度を上げるために教師データを増やすと、計算量が莫大に増えるというジレンマを抱えているわけです。

その緩和策として、近似最近傍探索と呼ばれる計算量をなるべく減らす手法もあります(詳しくは参考書籍を参照ください)。

scikit-learnによる実装例

ではscikit-learnで実装していきましょう。

なお、それぞれのPythonコードは、前提として以下のライブラリがインポートされているものとします。

# ライブラリのインポート
%matplotlib inline
import matplotlib.pyplot as plt
import numpy as np
import mglearn

「mglearn」というライブラリはscikit-learnの結果を図示したり、サンプルデータを作るために「Pythonではじめる機械学習/オライリー」の著者が教育用に作ったライブラリです。

以下のようにpipでインストールできます。

$ pip install mglearn

デモ用の教師データ作成

まずデモ用の教師データを作成します。

以下のようにsklearn.datasetのmake_blobsモジュールを使ってデータを作ります。

ちなみにblobというのはインクの染みという意味です。
つまり紙にインクの染みをポツポツと垂らしたようなデータを生成するクラスですね。

from sklearn.datasets import make_blobs 

X_train, Y_train = make_blobs(centers=2, random_state=4, n_samples=30, n_features=2)

これで、2次元の教師データができました。

kNN分類器の実装

ではkNN分類器を実装していきましょう。

kNNの実装には、sklearn.neighborsモジュールのKNeighborsClassifierクラスを使います。

k=1の場合

k=1の場合は、KNeighborsClassifierクラスの引数を「n_neighbors=1」という風に設定します。

fitメソッドで学習を実行できます。

#k=1でのkNN分類

from sklearn.neighbors import KNeighborsClassifier
clf = KNeighborsClassifier(n_neighbors=1)
clf.fit(X_train, Y_train)

では、mglearnライブラリで結果を図示してみましょう。

# 結果を図示するコード

fig, ax = plt.subplots(1, 1, figsize=(4, 3))
mglearn.plots.plot_2d_separator(clf, X_train, fill=True, eps=0.5,ax=ax, alpha=.2)
mglearn.discrete_scatter(X_train[:, 0], X_train[:, 1], Y_train, ax=ax)
ax.set_title(clf.__class__.__name__)
ax.set_xlabel("Feature 0")
ax.set_ylabel("Feature 1")
ax.legend()

アウトプットがこちら。

色分けは各クラスの境界、つまりボロノイ境界を示しています。

k=1なので教師データとの整合性がかなり良いですね。

k=1の時の結果

k=3の場合

k=3の場合は、KNeighborsClassifierクラスの引数を「n_neighbors=3」とするだけですね。

#k=3でのkNN分類

from sklearn.neighbors import KNeighborsClassifier
clf = KNeighborsClassifier(n_neighbors=3)
clf.fit(X_train, Y_train)

k=1の時と同じように結果を図示したものがこちら。

k=3の時の結果

k=3とすると、境界が少し滑らかになって過学習を回避できていますね。一方で教師データとの整合性は少し下がっていますね。

まとめ

今回はscikit-learnでk最近傍法(kNN法)を使う方法について説明しました。

scikit-learnを使うと最も簡単に実装できてしまいますね。

しかしある程度は理論的な理解をしていないと、いつどんな場面で使うことが効果的か判断がつきません。

内容をきちんと理解して使いましょう。

参考

記事作成にあたって、以下の情報が参考になりました。

参考URL

scikit-learn/公式ドキュメント

参考書籍

・Pythonではじめる機械学習/オライリー

機械学習の入門書。入門者が身につけるべき機械学習手法が広く浅く学べる良書。

サンプルコードは以下のgithubリポジトリに公開されています。

https://github.com/amueller/introduction_to_ml_with_python

・はじめてのパターン認識/平井有三

筑波大での講義内容を元に編集された、有名なパターン認識の入門書。

理論をしっかり学びたい人向け。
Pythonでの実装例はなく、Rで実装されていることに注意。

機械学習カテゴリの最新記事