2 분 소요

이상치 탐지(Anomaly detection)

이상 탐지(anomaly detection)란 일반적인 데이터 패턴과 다른 패턴을 보이는 자료를 찾는 것을 말한다. 이런 이상이 있는 데이터를 이상치(anomaly)라 하며 이상 탐지는 사기 탐지, 침입 탐지, 안전 관리를 포함한 다양한 분야에 널리 활용된다.

머신러닝과 딥러닝은 이런 이상치 데이터 때문에 성능 크게 좌우된다.

 

사이킷런에는 이상치를 탐지하기 위한 모델이 몇개 이미 준비되어 있는데 오늘은 아래 3가지 모델들을 살펴본다.

  1. EllipticEnvelope

  2. LocalOutlierFactor

  3. IsolationForest

 

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.covariance import EllipticEnvelope
from sklearn.neighbors import LocalOutlierFactor
from sklearn.ensemble import IsolationForest

 

데이터 생성

rng = np.random.RandomState(42)

X_train = 0.2 * rng.randn(1000, 2)
X_train = np.r_[X_train + 3, X_train]
X_train = pd.DataFrame(X_train, columns = ['x1', 'x2'])
# Generating new, 'normal' observation
X_test = 0.2 * rng.randn(200, 2)
X_test = np.r_[X_test + 3, X_test]
X_test = pd.DataFrame(X_test, columns = ['x1', 'x2'])
# Generating outliers
outliers = rng.uniform(low=-1, high=5, size=(50, 2))
outliers = pd.DataFrame(outliers, columns = ['x1', 'x2'])
plt.scatter(X_train.x1, X_train.x2, c='white', s=20*4, edgecolor='k', label='training observations')
plt.scatter(outliers.x1, outliers.x2, c='red', s=20*4, edgecolor='k', label='new abnormal obs.')

plt.legend(loc='upper right')
plt.show()

image.png

 

모델

이상치가 얼마나 있는지 모른다는 가정안에서 사실 모순된 얘기지만 사이킷런에서 위 3가지 모델을 사용하기 위해선 contamination, 즉 데이터 내에서 이상치의 비율을 알아야 한다.

모델 출력 (1: 정상, -1: 이상)

EllipticEnvelope

가우스 분산 데이터 세트에서 이상치를 탐지하기위한 객체로

정규 분포를 이용하여 데이터 분포에 타원을 그립니다. 타원에서 벗어날수록 outlier입니다.

X_outliers = outliers.copy()

clf = EllipticEnvelope(contamination = 0.1, random_state=42)
clf.fit(X_train)
y_pred_train = clf.predict(X_train)
y_pred_test = clf.predict(X_test)
y_pred_outliers = clf.predict(X_outliers)
X_outliers = X_outliers.assign(y = y_pred_outliers)
plt.scatter(X_train.x1, X_train.x2, c='white',
                 s=20*4, edgecolor='k', label="training observations")
plt.scatter(X_outliers.loc[X_outliers.y == -1, ['x1']], 
                 X_outliers.loc[X_outliers.y == -1, ['x2']], 
                 c='red', s=20*4, edgecolor='k', label="detected outliers")
plt.scatter(X_outliers.loc[X_outliers.y == 1, ['x1']], 
                 X_outliers.loc[X_outliers.y == 1, ['x2']], 
                 c='green', s=20*4, edgecolor='k', label="detected regular obs")
plt.legend(loc='upper right')
plt.show()

image.png

print("테스트 데이터셋에서 정확도:", list(y_pred_test).count(1)/y_pred_test.shape[0])
print("이상치 데이터셋에서 정확도:", list(y_pred_outliers).count(-1)/y_pred_outliers.shape[0])
테스트 데이터셋에서 정확도: 0.9075
이상치 데이터셋에서 정확도: 0.82

 

Local Outlier Factor (LOF)

해당 관측치의 주변 데이터(neighbor)를 이용하여 국소적(local) 관점으로 이상치 정도를 파악하는 모델

image.png

집단 C1 과 집단 C2의 density가 다르며, 데이터 o1은 눈에 띄게 다른 데이터와의 거리가 멀기에 걸러내기 쉽지만 데이터 o2는 걸러내기가 어렵다.

이때 local의 상대적인 dense를 비교하여 outlier를 정하자는 LOF가 등장하게 됨

X_outliers = outliers.copy()

clf = LocalOutlierFactor(n_neighbors=20, novelty=True, contamination=0.1)
clf.fit(X_train)
y_pred_train = clf.predict(X_train)
y_pred_test = clf.predict(X_test)
y_pred_outliers = clf.predict(X_outliers)
X_outliers = X_outliers.assign(y = y_pred_outliers)
plt.scatter(X_train.x1, X_train.x2, c='white',
                 s=20*4, edgecolor='k', label="training observations")
plt.scatter(X_outliers.loc[X_outliers.y == -1, ['x1']], 
                 X_outliers.loc[X_outliers.y == -1, ['x2']], 
                 c='red', s=20*4, edgecolor='k', label="detected outliers")
plt.scatter(X_outliers.loc[X_outliers.y == 1, ['x1']], 
                 X_outliers.loc[X_outliers.y == 1, ['x2']], 
                 c='green', s=20*4, edgecolor='k', label="detected regular obs")
plt.legend(loc='upper right')
plt.show()

image.png

print("테스트 데이터셋에서 정확도:", list(y_pred_test).count(1)/y_pred_test.shape[0])
print("이상치 데이터셋에서 정확도:", list(y_pred_outliers).count(-1)/y_pred_outliers.shape[0])
테스트 데이터셋에서 정확도: 0.935
이상치 데이터셋에서 정확도: 0.96

 

IsolationForest

밀도기반으로 이상 탐지를 하는 의사결정 트리기반 이상탐지 기법

다차원 데이터셋에서 효율적으로 작동하는 아웃라이어 제거 방법이다.

X_outliers = outliers.copy()

clf = IsolationForest(contamination = 0.1, random_state=42)
clf.fit(X_train)
y_pred_train = clf.predict(X_train)
y_pred_test = clf.predict(X_test)
y_pred_outliers = clf.predict(X_outliers)
X_outliers = X_outliers.assign(y = y_pred_outliers)
plt.scatter(X_train.x1, X_train.x2, c='white',
                 s=20*4, edgecolor='k', label="training observations")
plt.scatter(X_outliers.loc[X_outliers.y == -1, ['x1']], 
                 X_outliers.loc[X_outliers.y == -1, ['x2']], 
                 c='red', s=20*4, edgecolor='k', label="detected outliers")
plt.scatter(X_outliers.loc[X_outliers.y == 1, ['x1']], 
                 X_outliers.loc[X_outliers.y == 1, ['x2']], 
                 c='green', s=20*4, edgecolor='k', label="detected regular obs")
plt.legend(loc='upper right')
plt.show()

image.png

print("테스트 데이터셋에서 정확도:", list(y_pred_test).count(1)/y_pred_test.shape[0])
print("이상치 데이터셋에서 정확도:", list(y_pred_outliers).count(-1)/y_pred_outliers.shape[0])
테스트 데이터셋에서 정확도: 0.9175
이상치 데이터셋에서 정확도: 0.98

 

카테고리:

업데이트:

댓글남기기