テストエンジニアが機械学習してみた備忘録

広島とジト目が好きなテストエンジニアが機械学習に手を出した備忘録。

sklearn.neighborsの使い方調査

機械学習による異常検知」の7章にある時系列データの異常検知をPythonでやってみようと思ったものの、 1番始めに掲載されていた最近傍法でいきなり詰まったので備忘録。

詰まった理由はどのパッケージを使えばいいのか判断に困ったこと。

API Reference — scikit-learn 0.19.1 documentation

APIリファレンス見るとsklearn.neighborsモジュールにはいくつかのパッケージがあるようですが、 User Guideを見ると主に以下のいずれかを利用すれば良さそうです。

  • NearestNeighbors
  • KNeighborsClassifier
  • KNeighborsRegressor

KNeighborsClassifierとKNeighborsRegressorはそれぞれ「分類」と「回帰」で使い分ければ良さそうですが、 これらとNearestNeighborsとの使い分けがピンと来ませんでした。 一応、ドキュメント的にはNearestNeighborsがUnsupervised(教師なし)で、 KNeighborsClassifierとKNeighborsRegressorはSupervised(教師あり)という違いは読み取れたのですが、 そもそも最近傍法って「教師あり」ちゃうんかい…と。

User GuideのコードからNearestNeighborsを解釈する

「1.6.1.1. Finding the Nearest Neighbors」では近傍の点を見つけるシンプルなタスクとして、 NearestNeighborsを用いた教師なしアルゴリズムを紹介しています。

from sklearn.neighbors import NearestNeighbors
import numpy as np

X = np.array([[-1, -1], [-2, -1], [-3, -2], [1, 1], [2, 1], [3, 2]])
nbrs = NearestNeighbors(n_neighbors=2, algorithm='ball_tree').fit(X)
distances, indices = nbrs.kneighbors(X)

sklearn.neighbors.NearestNeighbors — scikit-learn 0.19.1 documentation

NearestNeighborsの使い方は、fit(X)メソッドでモデルを作成し、 kneighbors(X)メソッドでXに対する近傍の点と距離を得る、というもののようです。 kneighbors(X)で特定する近傍の点の数は、メソッド実行時に指定しなければ、 NearestNeighborsのコンストラクタに指定した数がデフォルトで使われるみたいですね。

となると、上記のコードはXの各点に対して、近傍の点を2つ見つけるということをやっています。 つまり、各点について自分自身と、自身と最も近い点の2つを返すことになり、距離については当然前者は0となります。

実際にXをプロットして、indices(近傍の点)とdistance(距離)の出力結果を見てみるとわかりやすいです。

f:id:gratk:20171210184932p:plain

>>> indices                                           
array([[0, 1],
       [1, 0],
       [2, 1],
       [3, 4],
       [4, 3],
       [5, 4]]...)
>>> distances
array([[ 0.        ,  1.        ],
       [ 0.        ,  1.        ],
       [ 0.        ,  1.41421356],
       [ 0.        ,  1.        ],
       [ 0.        ,  1.        ],
       [ 0.        ,  1.41421356]])

ということは

distances, indices = nbrs.kneighbors(X, 3)

とすると近傍の点を3つまで出力してくれますし、

nbrs.kneighbors(np.array([[0, 0]]))

というようにXに含まれない点を入れると、Xの中から近いものを探してくれます。

教師データがないので、入力値に対して予測値や分類ラベルを返すわけではなく、 あくまで近傍の点を探してくるということのみを行なっているということですね。 「教師なしの最近傍法」のイメージが少し湧きました。

KNeighborsClassifierとKNeighborsRegression

KNeighborsClassifierとKNeighborsRegressionはUser Guideにコードがなかったので、リファレンスを見ていきます。

sklearn.neighbors.KNeighborsClassifier — scikit-learn 0.19.1 documentation

sklearn.neighbors.KNeighborsRegressor — scikit-learn 0.19.1 documentation

NearestNeighborsと異なる点に絞ったポイントとしては

  • fit()に教師データも与える
  • fit()でモデル生成後にpredict()を呼ぶことで予測結果を得られる
    • Classifierは属するクラス
    • Regressionは予測値
  • Classifierはpredict_probaで各クラスに属する確率を得られる

といったあたりでしょうか。

まとめ

NearestNeighborsにはpredict()が存在しないので、 近傍の点を見つけたいだけならNearestNeighborsで十分ですが、 予測結果も得たいならKNeighborClassifierかKNeighborRegressionを用いる必要がありそうです。

一方、KNeighborClassifierやKNeighborRegressionにも、 近傍の点を見つけるkneighbors()は存在するので、 「予測結果も得たいけど近傍の点も得たい」という場合に、 わざわざNearestNeighborsと併用する必要はなさそうです。

元々の目的である「異常検知で用いる」場合についてですが、 「機械学習による異常検知」では最近傍点との距離を異常度と定義して用いているため、 NearestNeighborsのkneighbors()をk=1で用いて、返ってくる距離を利用するという方針でいけそうです。