最近、異常検知を勉強しています。
今回は、簡単な異常検知のプログラム作ってみたので紹介したいと思います。
ソースコードはgithubにもあげておきました。
※筆者は異常検知入門者なので、ミスがあると思いますのでお気をつけください
今回すること
今回は、簡易的なデータセットを作成して、異常検知をやってみます。
正常データだけを学習させて、正常空間をモデリングし、異常データを検知します。
具体的なやることとしては、
- 正常のデータだけの学習用データ、通常・異常データが混じった評価用データの作成
- 異常検知を行うモデルを正常データで学習
- モデルを正常・異常を含むデータで評価
になります。
今回は簡易データセットを使うので、特に特徴量抽出などを行いません。
実装、コーディング
では、実装していきます。
ライブラリのimport
まずは、必要なライブラリ等をimport しておきます。
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.cm as cm
from sklearn import datasets
from sklearn.metrics import classification_report, roc_auc_score, roc_curve, confusion_matrix
from sklearn.svm import OneClassSVM
データセットの用意
今回使うデータセットを作ります。
作ると言ってもsklearnが用意してくれている
簡易データセット作成用のメソッド(make_blobs)を使うだけです。
X, Y = datasets.make_blobs(n_samples=200, centers=2, n_features=2,random_state=0)
これだけで、2クラスの2次元のデータを作成できます。
print(X.shape)
print(Y.shape)
(200, 2)
(200,)
ちなみに、2クラスなので、Yは0か1が格納されています。
今回は正常値を0,異常値を1として進めていきます。
normal = 1
abnormal = 0
x_normal = X[np.where(Y==normal,True,False)]
y_normal = Y[np.where(Y==normal,True,False)]
x_abnormal = X[np.where(Y==abnormal,True,False)]
y_abnormal = Y[np.where(Y==abnormal,True,False)]
試しに作ったデータセットを見てます。
plt.scatter(x_normal[:, 0], x_normal[:, 1], c="grey")
plt.scatter(x_abnormal[:, 0], x_abnormal[:, 1], c="gold")
黄色が異常値、灰色が正常値になります。評価する際にはこのデータセットを使います。
今回は、1とラベルが付けされているデータを正常データとしてそちらを学習用とします。
つまり、以下のデータだけを学習させます。
plt.scatter(x_normal[:, 0], x_normal[:, 1], c='grey')
plt.xlim(-2,5)
plt.ylim(-3,7)
異常検知用のモデルの作成と学習
では、異常検知用のモデルに学習させます。
ここではsklearnのOneClassSVMを使っていきます。
svm = OneClassSVM(kernel='rbf', nu=0.2, gamma=1e-04)
svm.fit(x_normal)
学習fitで行うだけです。学習用(正常)データだけで学習させます
今回はOne Class SVMについての説明は省略します。
評価
では、評価していきます。
先程のデータ群に対して、どのように正常か異常値かをモデルが判断したかをplotしてみます。
x1_min, x1_max = X[:, 0].min() - 2, X[:, 0].max() + 2
x2_min, x2_max = X[:, 1].min() - 2, X[:, 1].max() + 2
x1_ = np.linspace(x1_min, x1_max, 500)
x2_ = np.linspace(x2_min, x2_max, 500)
xx1, xx2 = np.meshgrid(x1_, x2_)
z = svm.decision_function(np.c_[xx1.ravel(), xx2.ravel()])
z = z.reshape(xx1.shape)
plt.contourf(xx1, xx2, z, cmap=plt.cm.PuBu)
a = plt.contour(xx1, xx2, z, levels=[0], linewidths=2, colors='darkred')
b = plt.scatter(X[Y == normal, 0], X[Y == normal, 1], c='white', edgecolors='k')
c = plt.scatter(X[Y == abnormal, 0], X[Y == abnormal, 1], c='gold', edgecolors='k')
plt.legend([a.collections[0], b, c], ['learned frontier', 'normal', 'abnormal'], bbox_to_anchor=(1.05, 1))
plt.xlabel('x1')
plt.ylabel('x2')
plt.axis('tight')
plt.show()
灰色が正常データ、黄色が異常データになります。
赤い楕円の内側を正常と判断しています。
まぁまぁ綺麗に判別できていると思います。
では、ROCとAUCも見ておきます。
y_score = svm.decision_function(X)
auc = roc_auc_score(Y,y_score)
print('auc:',auc)
fpr, tpr, thresholds = roc_curve(Y, y_score)
plt.plot(fpr, tpr, label='baseline(AUC = %.3f)'%auc)
plt.plot([0,1],[0,1],'k--')
plt.legend()
plt.title('ROC curve')
plt.xlabel('False Positive Rate')
plt.ylabel('True Positive Rate')
plt.grid(True)
AUCも0.99なので、非常にうまく判別できているようです。
終わりに
今回は、非常にシンプルな問題を用いて簡単な異常検知を試してみました。
実際の問題はこんな簡単ではないので、特徴量を作成する過程で様々な試行錯誤が必要だと思います。
次回はもう少し複雑な問題設定で異常検知をやってみようと思います。