LoginSignup
0
0

k近傍法(kNN)でモデルの適用範囲(AD)を評価する!

Posted at

はじめに

k近傍法を用いたモデルの適用範囲評価について解説していきます。

k近傍法とは?

  • k近傍法(k-NN:k-Nearest Neighbors algotithm)はデータ密度を計算する手法
  • あるサンプルに対してデータセットの中から最も距離が近いサンプルk個を選択してその平均距離を算出
  • 距離の平均値が小さいほどデータの密度が高いと考える
  • この手法を選択することでデータの密度で評価できるので、データ分布が複数に分かれている場合でも適切にAD(Applicability Domain)を設定できる

k近傍方を用いたモデルの適用範囲の決定方法

  • トレーニングデータで平均距離が小さい順にサンプルを並べる
  • しきい値を上位●%と定め、その平均距離より大きいサンプルはデータの密度が小さくAD外と見なす
  • 例えばトレーニングデータが100個あって上位80%のサンプルの平均距離を10とした際には、平均距離が10以下のサンプルはAD内と見なす

k近傍法の実装

#ライブラリのインポート
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.neighbors import NearestNeighbors  # k-NN
# ハイパーパラメータの設定
rate_of_training_samples_inside_ad = 0.8  # AD 内となるトレーニングデータの割合。AD のしきい値を決めるときに使用
k_in_knn = 5  # k-NN における k
number_of_test_samples = 150  # テストデータのサンプル数←ここはのちほど "len(x_test)"と定義しても良さそう

データセットを読み込みます。ここでは以下のようなボストンのデータセットを読み込みます。

image.png

# データセットの読み込み
dataset = pd.read_csv('boston.csv', index_col=0)
dataset

image.png

# データセットの大きさの確認
dataset.shape
出力
(506, 14)
# データ分割
y = dataset.iloc[:, 0]  # 目的変数
x = dataset.iloc[:, 1:]  # 説明変数
# ランダムにトレーニングデータとテストデータとに分割
# random_state に数字を与えることで、別のときに同じ数字を使えば、ランダムとはいえ同じ結果にすることができます
x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=number_of_test_samples, shuffle=True, random_state=99)
# オートスケーリング
autoscaled_x_train = (x_train - x_train.mean()) / x_train.std()
autoscaled_x_test = (x_test - x_train.mean()) / x_train.std()
# k-NN による AD
ad_model = NearestNeighbors(n_neighbors=k_in_knn, metric='euclidean')  # AD モデルの宣言
ad_model.fit(autoscaled_x_train)  # k-NN による AD では、トレーニングデータの x を model_ad に格納することに対応

トレーニングデータに対して、kNNで平均距離を算出していく

# サンプルごとの k 最近傍サンプルとの距離に加えて、k 最近傍サンプルのインデックス番号も一緒に出力されるため、出力用の変数を 2 つに
# トレーニングデータでは k 最近傍サンプルの中に自分も含まれ、自分との距離の 0 を除いた距離を考える必要があるため、k_in_knn + 1 個と設定
knn_distance_train, knn_index_train = ad_model.kneighbors(autoscaled_x_train, n_neighbors=k_in_knn + 1)
# トレーニングデータでの計算結果
knn_distance_train = pd.DataFrame(knn_distance_train, index=x_train.index)  # DataFrame型に変換
mean_of_knn_distance_train = pd.DataFrame(knn_distance_train.iloc[:, 1:].mean(axis=1),
                                          columns=['mean_of_knn_distance'])  # 自分以外の k_in_knn 個の距離の平均
mean_of_knn_distance_train

image.png

# トレーニングデータのサンプルの rate_of_training_samples_inside_ad * 100 % が含まれるようにしきい値を設定
sorted_mean_of_knn_distance_train = mean_of_knn_distance_train.iloc[:, 0].sort_values(ascending=True)  # 距離の平均の小さい順に並び替え
# しきい値の計算
ad_threshold = sorted_mean_of_knn_distance_train.iloc[
    round(autoscaled_x_train.shape[0] * rate_of_training_samples_inside_ad) - 1]
ad_threshold
出力
1.4662693333774972

各サンプルで平均距離を計算して、この値より平均距離が小さいとAD内、大きいとAD外と判別していく。

# テストデータに対する k-NN 距離の計算
knn_distance_test, knn_index_test = ad_model.kneighbors(autoscaled_x_test)
knn_distance_test = pd.DataFrame(knn_distance_test, index=x_test.index)  # DataFrame型に変換
mean_of_knn_distance_test = pd.DataFrame(knn_distance_test.mean(axis=1),
                                         columns=['mean_of_knn_distance'])  # k_in_knn 個の距離の平均
# テストデータの平均距離一覧
mean_of_knn_distance_test

image.png

# テストデータに対して、AD の中か外かを判定
inside_ad_flag_test = pd.DataFrame(mean_of_knn_distance_test <= ad_threshold, index=x_test.index)  # AD 内のサンプルのみ TRUE
inside_ad_flag_test.columns=['inside_ad_flag']
# 平均距離を併記しておく(オリジナル)
inside_ad_flag_test["mean_of_knn_distance"] = mean_of_knn_distance_test["mean_of_knn_distance"]
inside_ad_flag_test

image.png

 # csv ファイルに保存。同じ名前のファイルがあるときは上書きされるため注意
inside_ad_flag_test.to_csv('inside_ad_flag_test_withL.csv') 

参考文献

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0