旅行好きなソフトエンジニアの備忘録

プログラミングや技術関連のメモを始めました

【異常検知】 GMM(Gaussian Mixture Model)による外れ値検知

GMMによる外れ値検出手法を試してみます。LOFやiForestのようにずばりそのものを見つけることが出来なかったので、scikit-learnにあるGaussianMixtureクラスを流用して作成します。 まずは、GMMを用いて外れ値検出を行うクラスをGMMAnomalyDetectorクラスとして、gmmanomalydetector.pyに作ります。

import numpy as np
from sklearn.mixture import GaussianMixture

class GMMAnomalyDetector:
    def __init__(self, max_n_component, covariance_type='full'):
        self._max_n_component = max_n_component
        self._covariance_type = covariance_type
        self._best_gmm = None
        self.best_n_component = -1

    def fit(self, X):
        # BIC基準でベストなクラスタ数を2~max_n_componentの範囲で探す
        lowest_bic = np.inf
        for n_component in range(2, self._max_n_component + 1):
            gmm = GaussianMixture(n_components=n_component, covariance_type=self._covariance_type)
            gmm.fit(X)
            bic = gmm.bic(X)
            if bic < lowest_bic:
                lowest_bic = bic
                self._best_gmm = gmm
                self.best_n_component = n_component

    def predict(self, X, contamination=0.1):
        # スコア下位N%を異常と見なす
        scores = np.exp(self._best_gmm.score_samples(X))
        ordered_scores = np.argsort(scores)
        anomaly_indices = ordered_scores[:int(len(scores)*contamination + 0.5)]
        # scikit-learnに倣って正常を1、異常を-1として返す
        prediction = np.ones((len(scores)), dtype=np.int)
        prediction[anomaly_indices] = -1
        return prediction


次にGMMAnomalyDetectorクラスを利用して外れ値検出を行う例をmain.pyとして以下に書きます。

import numpy as np
import matplotlib.pyplot as plt
from gmmanomalydetector import GMMAnomalyDetector

np.random.seed(42)

# Generate train data
X = 0.3 * np.random.randn(100, 2)
# Generate some abnormal novel observations
X_outliers = np.random.uniform(low=-4, high=4, size=(20, 2))
X = np.r_[X + 2, X - 2, X_outliers]

# fit the model
clf = GMMAnomalyDetector(max_n_component=10)
clf.fit(X)
# 全体の9%が異常データ
contamination = 0.09
y_pred = clf.predict(X, contamination)
# 正常を1、異常を-1と出力します
ANOMALY_DATA = -1
predicted_outlier_index = np.where(y_pred == ANOMALY_DATA)
predicted_outlier = X[predicted_outlier_index]

plt.title("Gaussian Mixture Model (k=" + str(clf.best_n_component) + ')')

a = plt.scatter(X[:200, 0], X[:200, 1], c='yellow',
                edgecolor='k', s=30, marker='o')
b = plt.scatter(X[200:, 0], X[200:, 1], c='red',
                edgecolor='k', s=30, marker='o')
c = plt.scatter(predicted_outlier[:, 0], predicted_outlier[:, 1], c='blue',
                edgecolor='k', s=10, marker='x')
plt.axis('tight')
plt.xlim((-5, 5))
plt.ylim((-5, 5))
plt.legend([a, b, c],
           ["normal observations",
            "abnormal observations",
            "observations predicted as abnormal"],
           loc="upper left", prop={'size': 12})
plt.show()

best_n_component=5となっているため、5つのクラスタに分けるのが最も当てはまりが良いと判定されたようです。 f:id:ni4muraano:20171117224444p:plain