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

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

「入門 機械学習による異常検知」の時系列データの異常検知をPythonでやってみる 〜近傍法編〜

これを勉強がてらRではなくPythonで実装してみます。

blog.brains-tech.co.jp

これもまた先駆者がいるのですが、まぁ自分で手を動かすことが大事なので… 上記の記事はさらっと読みましたが、コードは読まず1から組んでみます。

はじめに(1章で異常検知の概要を掴む)

  • 異常検知の基本
    • 正常となるモデルをデータから作り、そのモデルから外れるものを異常とすること
  • 機械学習で異常検知
    • 「モデルから外れる」という判定基準を、過去のデータから機械学習の手法で導くこと
  • 異常検知モデルの構築
    1. 分布推定(確率分布の作成)
    2. 異常度定義
    3. 閾値設定

実行環境

  • Mac OS High Sierra (10.13.1)
  • Python 3.6.2
    • numpy 1.13.1
    • matplotlib 2.0.2
    • pandas 0.20.3
    • sklearn 0.19.1
  • jupyter notebook
    • 掲載コードはjupyter notebook上で動作させています

データの準備

書籍で用いられている心拍数のデータを利用します。以下からDLできます。

http://www.cs.ucr.edu/~eamonn/discords/qtdbsel102.txt

データにヘッダがないので各列の意味するところがよくわかりませんが、 書籍では3列目のデータを用い、最初から3000件を学習データ、そこからさらに3000件をテストデータとしています。 試しにこれらをプロットしてみます。

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

df = pd.read_csv("./qtdbsel102.csv", sep="\t", header=None)
plt.plot(df.loc[:2999, 2])
plt.plot(df.loc[3000:5999, 2])

f:id:gratk:20171207004643p:plain
学習データ

f:id:gratk:20171207004651p:plain
テストデータ

確かにこれらを学習データとテストデータとして用いることができそうです。

近傍法

scikit-learnで近傍法を用いるにはsklearn.neibhborsモジュールを使用します。 今回は最近傍点までの距離を異常度として用いるため、NearestNeighborパッケージを使用します。 この辺りの考察は以下の記事で整理しました。

gratk.hatenablog.jp

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from sklearn.neighbors import NearestNeighbors


'''
dataをsize毎のスライス窓に分割
'''
def embed(data, size):
    window = np.empty((0, size))
    for index in range(0, len(data)-size+1):
        new_window = [data[i] for i in range(index, index+size)]
        window = np.append(window, np.array([new_window]), axis=0)
    return window


df = pd.read_csv("./qtdbsel102.csv", sep="\t", header=None)

# 前から3000件を学習データ、その次の3000件をテストデータとする
train = df.loc[:2999, 2]
test = df.loc[3000:5999, 2].reset_index(drop=True)

# サイズ100のスライス窓に分割
train_window = embed(train.as_matrix(), 100)
test_window = embed(test.as_matrix(), 100)

# 学習データでモデルを生成し、学習データについて異常度(最近傍までの距離を)算出
nbrs = NearestNeighbors(n_neighbors=1, n_jobs=-1).fit(train_window)
distances, indices = nbrs.kneighbors(test_window)

# テストデータと異常度のプロット
plt.plot(test)
plt.plot(distances)
plt.legend(["test data", "abnormality"])

f:id:gratk:20171211002928p:plain

異常箇所で異常度が高くなっているので、うまくできた雰囲気です。 ただ、近傍法を用いた方法はノイズに弱いという欠点があるようなので、 実戦投入するにはもう少し別の方法を学ぶ必要がありそうです。

次に登場する手法は「特異スペクトル変換法」で、実戦投入できそうな手法のようですが、 主成分分析を扱うようなので、次はまず主成分分析の勉強をしてみたいと思います。