【異常検知】 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つのクラスタに分けるのが最も当てはまりが良いと判定されたようです。