k近傍法

K近傍法(K Nearest Neighbors)は、さっくり言うと、「回帰や分類を行う際に、似たようなデータをk個集めてそれらの多数決から目的とする値を求める」という手法です。

  • 回帰問題

  検証データと似たk個のデータのそれぞれの値の平均値や中央値、もしくは重み付けした集計値を予測結果に用います。

  • クラス分類

  検証データと似たk個のデータそれぞれの「クラス」でもっとも数が多いクラスにデータを分類します。(多数決の原理です)

KNN実装方法

① 学習データ(ラベル付け)とテストデータを用意する

② Kの値を決める

③ 全てのテストデータに対して、下記の処理を行う。

 ③-1 学習データとの距離を算出する。距離の算出には、一般的にユークリッド距離が使われる。

 ③-2 算出した距離をリストに保存して、ソートする。

 ③-3 距離は一番近いK個学習データを選べる。

 ③-4 数が多いクラスにデータを分類する。

                      K Nearest Neighbours

K値の特徴

K の値によって精度が変わるため、最適な K の値を設定する必要があります。

K 特徴
小さい値 ノイズに弱い
大きい値 精度が下がる

クラス分類

クラス分類には、KNeighborsClassifier クラスを使用します。

実際に分類を行ってみます。

アヤメという花の分類を行います。
特徴量として、萼(がく)片の長さ・幅、花びらの長さ・幅の4種類があり、
分類クラスとして、Setosa、Versicolor、Virgínia の3種類があります。

%matplotlib inline
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

from sklearn.model_selection import train_test_split

# アヤメデータセット読み込み
from sklearn.datasets import load_iris
iris = load_iris()

# 特徴量
X = iris.data
# 目的変数
Y = iris.target
# データ表示(特徴量)
print("データ数 = %d  特徴量 = %d" % (X.shape[0], X.shape[1]))
pd.DataFrame(X, columns=iris.feature_names).head()
s1.png
# データ表示(目的変数)
print("データ数 = %d" % (Y.shape[0]))
print(Y)

s2.png

# トレーニング・テストデータ分割
X_train, X_test, Y_train, Y_test = train_test_split(X, Y, random_state=0)

#
# K-近傍法
#
from sklearn.neighbors import KNeighborsClassifier

list_nn = []
list_score = []
for k in range(1, 31): # K = 1~30
  # KNeighborsClassifier
  knc = KNeighborsClassifier(n_neighbors=k)
  knc.fit(X_train, Y_train)

  # 予測 
  Y_pred = knc.predict(X_test)

  # 評価 R^2
  score = knc.score(X_test, Y_test)
  print("[%d] score: {:.2f}".format(score) % k)

  list_nn.append(k)
  list_score.append(score)

# プロット
plt.ylim(0.9, 1.0)
plt.xlabel("n_neighbors")
plt.ylabel("score")
plt.plot(list_nn, list_score)

K = 1~30 まで実行しました。
K = 1~23 までの精度は 97% ですが、K = 24 以降は精度が下がっています。
K の値は、少ない数値で問題なさそうです。

s3.png

回帰分析

回帰分析には、KNeighborsRegressor クラスを使用します。

ボストン住宅価格データの回帰分類を行います。
特徴量として 犯罪発生率や住宅区画の密集度など 13種類があり、
目的変数として、住宅価格があります。
今回は、特徴量として部屋数を使い住宅価格を予測します。

# ボストン住宅価格データセット
from sklearn import datasets
boston = datasets.load_boston()

# 説明変数
X = boston.data
# 目的変数
Y = boston.target
# データ表示(特徴量)
print("データ数 = %d  特徴量 = %d" % (X.shape[0], X.shape[1]))
pd.DataFrame(X, columns=boston.feature_names).head()

s7.png

# データ表示(目的変数)
print("データ数 = %d" % (Y.shape[0]))
print(Y[:10]) # 先頭 10件表示
s8.png
# 説明変数に部屋数のみ使用
X = boston.data[:, [5]] # 部屋数

# トレーニング・テストデータ分割
X_train, X_test, Y_train, Y_test = train_test_split(X, Y, random_state=0)

# プロット
plt.xlabel("RM")
plt.ylabel("Target")
plt.plot(X_train, Y_train, "o")
from sklearn.neighbors import KNeighborsRegressor
from sklearn.metrics import mean_squared_error
from sklearn.metrics import mean_absolute_error

list_k = []
list_score = []
for k in range(1, 21):
  # KNeighborsClassifier
  knr = KNeighborsRegressor(n_neighbors=k)
  knr.fit(X_train, Y_train)

  # 予測 
  Y_pred = knr.predict(X_test)

  #
  # 評価
  #
  # 平均絶対誤差(MAE)
  mae = mean_absolute_error(Y_test, Y_pred)
  # 平方根平均二乗誤差(RMSE)
  rmse = np.sqrt(mean_squared_error(Y_test, Y_pred))  
  # スコア R^2
  score = knr.score(X_test, Y_test)

  print("[%d] MAE = %.2f,  RMSE = %.2f,  score = %.2f" % (k, mae, rmse, score))

  list_k.append(k)
  list_score.append(score)

# プロット
plt.ylim(0, 0.7)
plt.xlabel("k")
plt.ylabel("score")
plt.plot(list_k, list_score)

K = 6 以降、ほとんど変化がありません。
K 値は 6 で良いようです。

s10.png

参考リンク:https://qiita.com/fujin/items/128ed7188f7e7df74f2c