본문 바로가기
Daily Review

데이터사이언티스트에게 필요한 3가지 이상 탐지 모델(Anomaly Detection)

by data-analyst-luke 2022. 9. 1.
반응형

아래 2가지 상황을 경험해본 적 있을 것이다.

  1. 모델이 원하는 대로 작동하지 않습니다.
  2. 일부 포인트가 나머지 포인트와 크게 다르다는 사실에 주목하지 않을 수 없습니다.

이런 상황이라면, 아마도 데이터에 이상치가 있다는 거다.

 

이상치란 무엇입니까?

 


통계에서 이상치는 다른 관측치와 크게 다른 데이터입니다. 위의 그림에서 우리는 대부분의 점이 선형 초평면 안에 있고 그 주위에 있지만 단일 점이 나머지 부분에서 발산하는 것을 볼 수 있음을 분명히 알 수 있습니다. 이 점은 이상치 입니다.

예를 들어 아래 목록을 살펴보십시오.

[ 1,35,20,32,40,46,45 , 4500 ]

여기에서 1과 4500이 데이터 세트의 이상값임을 분명히 쉽게 알 수 있습니다.

 

내 데이터에 이상치가 있는 이유는 무엇입니까?

일반적으로 이상치는 다음 시나리오 중 하나에서 발생할 수 있습니다.

  1. 때로는 측정 오류로 인해 우연히 발생할 수 있습니다.
  2. 데이터가 이상값 없이 100% 깨끗한 경우는 거의 없기 때문에 때때로 데이터에서 발생할 수 있습니다.

 

이상치가 왜 문제인가?

1. 선형 모델

일부 데이터가 있고 선형 회귀를 사용하여 이 데이터에서 주택 가격을 예측하려고 한다고 가정해 보겠습니다. 가능한 가설은 다음과 같습니다.


이 경우 실제로 데이터를 너무 잘 피팅( 과적합 )하고 있습니다. 그러나 모든 포인트가 대략적으로 같은 범위에 위치하는 방법에 유의하십시오.

이제 이상치를 추가할 때 어떤 일이 발생하는지 봅시다.

 


분명히 우리는 우리의 가설이 어떻게 이동했는지 알 수 있습니다. 따라서 추론은 이상치가 없을 때보다 훨씬 더 나빠질 것입니다. 선형 모델에는 다음이 포함됩니다.

  • 퍼셉트론
  • 선형 + 로지스틱 회귀
  • 신경망
  • KNN

 

2. 데이터 입력


일반적인 시나리오는 데이터가 누락되는 것이며 두 가지 접근 방식 중 하나를 사용할 수 있습니다.

  1. 행이 누락된 인스턴스 제거
  2. 통계적 방법을 사용하여 데이터 대치

 

두 번째 옵션을 사용한다면 이상치가 통계적 방법의 값을 크게 변경할 수 있으므로 문제가 있는 전가를 가질 수 있습니다. 예를 들어 이상값이 없는 가상 데이터로 돌아가면 다음과 같습니다.

# 이상값이 없는 데이터 
np.array([35,20,32,40,46,45]).mean() = 36.333333333333336 # 2개의 이상치가 있는 데이터 
np.array([1,35,20,32,40,46,45,4500]).mean() = 589.875

 

분명히 이 예시는 매우 극단적이지만 아이디어는 동일하게 유지됩니다. 데이터의 이상치는 일반적으로 문제입니다. 이상치는 통계 분석 및 모델링에서 심각한 문제를 일으킬 수 있기 때문입니다. 그러나 이 기사에서는 이를 감지하고 대처하는 방법에 대해 몇 가지 방법을 살펴볼 것입니다.

 

솔루션 1: DBSCAN

 


잡음이 있는 애플리케이션의 밀도 기반 공간 클러스터링(또는 더 간단하게는 DBSCAN)은 실제로 KMeans와 마찬가지로 감독되지 않은 클러스터링 알고리즘입니다. 그러나 그 용도 중 하나는 데이터에서 이상값을 감지할 수도 있다는 것입니다.

 

DBSCAN은 KMeans 및 Gaussian Mixtures로 수행할 수 없는 비선형 분리 가능한 클러스터를 찾을 수 있기 때문에 인기가 있습니다. 클러스터와 밀도가 충분할 때 잘 작동하며 밀도가 낮은 영역으로 분리되어 있습니다.

 

DBSCAN 작동 방식에 대한 개요

알고리즘은 클러스터를 고밀도의 연속 영역으로 정의합니다. 알고리즘은 매우 간단합니다.

  1. 각 인스턴스에 대해 몇 개의 인스턴스가 그로부터 작은 거리 ε(엡실론) 내에 있는지 계산합니다. 이 영역을 인스턴스의 ε- 이웃이라고 합니다.
  2. 인스턴스의 ε-neighbourhood에 min_samples 개 이상의 인스턴스가 있는 경우 코어 인스턴스 로 간주됩니다 . 이는 인스턴스가 고밀도 영역(내부에 인스턴스가 많은 영역)에 위치한다는 것을 의미합니다.
  3. 코어 인스턴스의 ε-neighbourhood 내부의 모든 인스턴스는 동일한 클러스터에 할당됩니다. 여기에는 다른 코어 인스턴스가 포함될 수 있으므로 인접한 코어 인스턴스의 긴 단일 시퀀스가 ​​단일 클러스터를 형성합니다.
  4. 코어 인스턴스가 아니거나 코어 인스턴스의 ε-이웃에 없는 모든 인스턴스는 이상값입니다.

 

DBSCAN 알고리즘 구현

DBSCAN 알고리즘은 Scikit-Learn의 직관적인 API 덕분에 매우 사용하기 쉽습니다. 작동 중인 알고리즘의 예를 살펴보겠습니다.

from sklearn.cluster import DBSCAN 
from sklearn.datasets import make_moons
X, y = make_moons(n_samples=1000, noise=0.05)
dbscan = DBSCAN(eps=0.2, min_samples=5)
dbscan.fit(X)

 

여기서 우리는 0.05의 ε-이웃 길이로 DBSCAN을 인스턴스화하고 5는 인스턴스가 코어 인스턴스로 간주되는 데 필요한 최소 샘플 수입니다.

 

비지도 알고리즘 이므로 레이블을 전달하지 않는다는 것을 기억하십시오 . 다음 명령을 사용하여 알고리즘이 생성한 레이블을 볼 수 있습니다.

dbscan.labels_
OUT:
array([ 0,  2, -1, -1,  1,  0,  0,  0, ...,  3,  2,  3,  3,  4,  2,  6,  3])

 

일부 레이블의 값이 -1인 방법에 유의하십시오. 이는 이상값입니다.

 

DBSCAN에는 예측 방법이 없고 fit_predict 방법만 있으므로 새 인스턴스를 클러스터링할 수 없습니다. 대신 다른 분류기를 사용하여 학습하고 예측할 수 있습니다. 이 예에서는 KNN을 사용하겠습니다.

from sklearn.neighbors import KNeighborsClassifier
knn = KNeighborsClassifier(n_neighbors=50)
knn.fit(dbscan.components_, dbscan.labels_[dbscan.core_sample_indices_])
X_new = np.array([[-0.5, 0], [0, 0.5], [1, -0.1], [2, 1]])
knn.predict(X_new)
OUT:
array([1, 0, 1, 0])

 

여기에서 핵심 샘플과 해당 이웃에 KNN 분류기를 맞춥니다.

그러나 우리는 한 가지 문제에 봉착합니다. 이상치 없이 KNN 데이터를 제공했습니다. 이는 새 인스턴스가 실제로 이상값이더라도 KNN이 새 인스턴스에 대한 클러스터를 선택하도록 강제하기 때문에 문제가 됩니다.

 

이를 방지하기 위해 우리는 KNN 분류기의 kneighbors 방법을 활용합니다. 이 방법은 인스턴스 세트가 주어지면 훈련 세트의 k개의 최근접 이웃의 거리와 인덱스를 반환합니다. 그런 다음 최대 거리를 설정할 수 있으며 인스턴스가 해당 거리를 초과하면 이상값으로 규정합니다.

y_dist, y_pred_idx = knn.kneighbors(X_new, n_neighbors=1)
y_pred = dbscan.labels_[dbscan.core_sample_indices_][y_pred_idx]
y_pred[y_dist > 0.2] = -1
y_pred.ravel()
OUT:
array([-1, 0, 1, -1])

 

여기에서 이상 탐지를 위한 DBSCAN에 대해 논의하고 구현했습니다. DBSCAN은 빠르고, 하이퍼파라미터가 2개뿐이며, 이상치에 대해 강력하기 때문에 훌륭합니다.

 

솔루션 2: IsolationForest

 


IsolationForest는 앙상블 학습 이상 감지 알고리즘으로, 고차원 데이터 세트에서 이상값을 감지하는 데 특히 유용합니다.알고리즘은 기본적으로 다음을 수행합니다.

  1. 결정 트리가 무작위로 성장하는 랜덤 포레스트를 생성합니다. 각 노드에서 기능이 무작위로 선택되고 데이터 세트를 두 개로 분할하기 위해 임의의 임계값을 선택합니다.
  2. 모든 인스턴스가 서로 격리될 때까지 데이터 세트를 계속 잘라냅니다.
  3. 이상 현상은 일반적으로 다른 인스턴스와 멀리 떨어져 있으므로 평균적으로(모든 의사 결정 트리에서) 일반 인스턴스보다 더 적은 단계로 격리됩니다.

 

IsolationForest 알고리즘 구현

다시 말하지만, Scikit-Learn의 직관적인 API 덕분에 IsolationForest 클래스를 쉽게 구현할 수 있습니다. 작동 중인 알고리즘의 예를 살펴보겠습니다.

from sklearn.ensemble import IsolationForest
from sklearn.metrics import mean_absolute_error
import pandas as pd

 

우리는 또한 우리의 오류를 측정하기 위해 mean_absolute_error를 가져올 것입니다. 데이터의 경우 Jason Brownlee의 GitHub 에서 얻을 수 있는 데이터 세트를 사용합니다 .

url='https://raw.githubusercontent.com/jbrownlee/Datasets/master/housing.csv'
df = pd.read_csv(url, header=None)
data = df.values
# split into input and output elements
X, y = data[:, :-1], data[:, -1]

 

Isolation Forest를 적용하기 전에 데이터에 기본 선형 회귀 모델을 적용하고 MAE를 구해 보겠습니다.

from sklearn.linear_model import LinearRegression
lr = LinearRegression()
lr.fit(X,y)
mean_absolute_error(lr.predict(X),y)
OUT:
3.2708628109003177

 

비교적 좋은 점수. 이제 Isolation Forest가 변칙성을 제거하여 점수를 향상시킬 수 있는지 봅시다!

먼저 IsolationForest를 인스턴스화합니다.

iso = IsolationForest(contamination='auto',random_state=42)

 

알고리즘에서 가장 중요한 하이퍼파라미터는 아마도 데이터세트의 이상치 수를 추정하는 데 사용되는 오염 파라미터일 것입니다. 0.0에서 0.5 사이의 값이며 기본적으로 0.1로 설정되어 있습니다.

그러나 이것은 본질적으로 무작위화된 랜덤 포레스트이므로 랜덤 포레스트의 모든 하이퍼파라미터도 알고리즘에서 사용할 수 있습니다.

 

다음으로 데이터를 알고리즘에 학습시킵니다.

y_pred = iso.fit_predict(X,y)
mask = y_pred != -1

 

DBSCAN에서와 같이 예측 값 = -1도 필터링하는 방법에 유의하십시오. 이는 이상값으로 간주됩니다.

이제 이상치 필터링된 데이터로 X와 Y를 재할당합니다.

X,y = X[mask,:],y[mask]

 

이제 선형 회귀 모델을 데이터에 맞추고 MAE를 측정해 보겠습니다.

lr.fit(X,y)
mean_absolute_error(lr.predict(X),y)
OUT:
2.643367450077622

 

와우~ 비용절감이 좋네요. 이것은 Isolation Forest의 위력을 분명히 보여줍니다.

 

 

솔루션 3: 상자 그림 + 터키 방법

Boxplots는 이상값을 식별하는 매우 일반적인 방법이지만 실제로 후자는 이상값을 식별하는 가장 과소평가된 방법일 수 있습니다. 그러나 터키 방법을 시작하기 전에 Boxplots에 대해 이야기합시다.

 

Boxplot

 


Boxplots는 기본적으로 분위수를 통해 숫자 데이터를 표시하는 그래픽 방식을 제공합니다. 이상값을 시각화하는 매우 간단하면서도 효과적인 방법입니다.

위쪽 및 아래쪽 수염은 분포의 경계를 나타내며 위 또는 아래는 이상값으로 간주됩니다. 위의 그림에서 ~80 이상과 ~62 미만은 모두 이상값으로 간주됩니다.

 

Boxplot 작동 방식

기본적으로 상자 그림은 데이터 세트를 5개 부분으로 분할하여 작동합니다.

 


  • Min : 이상치를 제외한 분포에서 가장 낮은 데이터 포인트.
  • Max : 이상값을 제외한 분포에서 가장 높은 데이터 포인트.
  • 중앙값( ​​Q 2 / 50번째 백분위수) : 데이터 세트의 중간 값입니다.
  • 1사분위수( Q 1 / 25번째 백분위수) : 데이터 세트의 하위 절반의 중앙값입니다.
  • 3사분위수( Q3 / 75번째 백분위수) : 데이터 세트의 상위 절반의 중앙값입니다.

 

사분위수 범위(IQR)는 이상치를 정의하기 때문에 중요합니다. 기본적으로 다음과 같습니다.

IQR = Q3 - Q1
Q3: third quartile
Q1: first quartile

 

상자 그림에서 1.5 * IQR의 거리가 측정되고 데이터 세트의 더 높은 관찰 지점을 포함합니다. 유사하게, 1.5 * IQR의 거리는 데이터 세트의 더 낮은 관찰 지점에서 측정됩니다. 이 거리를 벗어나는 것은 모두 이상값입니다. 더 구체적으로:

  • 관찰된 점이 (Q1 − 1.5 * IQR) 미만이거나 boxplot 더 낮은 수염인 경우 이상값으로 간주됩니다.
  • 마찬가지로 관찰된 점이 (Q3 + 1.5 * IQR) 또는 boxplot 상단 수염 위에 있는 경우에도 이상값으로 간주됩니다.
사진   위키피디아 제공

상자 그림 구현

Python에서 Boxplots를 사용하여 이상값을 감지하는 방법을 살펴보겠습니다!

import matplotlib.pyplot as plt
import seaborn as sns
import numpy as np
X = np.array([45,56,78,34,1,2,67,68,87,203,-200,-150])
y = np.array([1,1,0,0,1,0,1,1,0,0,1,1])

 

데이터의 상자 그림을 그려 보겠습니다.

sns.boxplot(X)
plt.show()

따라서 상자 그림에 따르면 데이터에 중앙값 50과 3개의 이상값이 있음을 알 수 있습니다. 이러한 점을 제거합시다.

X = X[(X < 150) & (X > -50)]
sns.boxplot(X)
plt.show()

여기서는 기본적으로 임계값을 설정하여 -50보다 작거나 150보다 큰 모든 포인트가 제외됩니다. 그리고 결과; 균등 분배!

 

터키 방법 이상값 탐지

터키 방법 이상값 감지는 실제로 상자 그림의 비시각적 방법입니다. 방법은 시각화가 없다는 점을 제외하고는 동일합니다.

내가 때때로 boxplot과 반대로 이 방법을 선호하는 이유는 때때로 시각화를 살펴보고 임계값을 설정해야 하는 대략적인 추정을 하는 것이 실제로 효과적이지 않기 때문입니다.

대신에 이상치로 정의한 인스턴스를 실제로 반환할 수 있는 알고리즘을 코딩할 수 있습니다.

 

구현을 위한 코드는 다음과 같습니다.

import numpy as np
from collections import Counter
def detect_outliers(df, n, features):
    # list to store outlier indices
    outlier_indices = []
    # iterate over features(columns)
    for col in features:
        # Get the 1st quartile (25%)
        Q1 = np.percentile(df[col], 25)
        # Get the 3rd quartile (75%)
        Q3 = np.percentile(df[col], 75)
        # Get the Interquartile range (IQR)
        IQR = Q3 - Q1
        # Define our outlier step
        outlier_step = 1.5 * IQR
       # Determine a list of indices of outliers
       outlier_list_col = df[(df[col] < Q1 - outlier_step) |     (df[col] > Q3 + outlier_step)].index
   # append outlier indices for column to the list of outlier indices 
        outlier_indices.extend(outlier_list_col)
   # select observations containing more than 2 outliers
    outlier_indices = Counter(outlier_indices)        
    multiple_outliers = list(k for k, v in outlier_indices.items() if v > n)
return multiple_outliers
# detect outliers from list of features
list_of_features = ['x1', 'x2']
# params dataset, number of outliers for rejection, list of features Outliers_to_drop = detect_outliers(dataset, 2, list_of_features)

 

기본적으로 이 코드는 다음을 수행합니다.

  1. 모든 기능에 대해 다음을 얻습니다.
  • 1사분위수
  • 3사분위수
  • IQR

2. 다음 으로 boxplots에서와 같이 1.5 * IQR인 이상치 단계를 정의합니다.

3. 다음과 같은 방법으로 이상값을 감지합니다.

  • 관찰된 점이 < Q1인지 확인 - 이상치 단계
  • 관찰된 점이 Q3 + 이상치 단계인지 확인

4. 그런 다음 k 이상값(이 경우 k = 2)이 있는 관측값을 선택합니다.

 

 

결론

 

요약하자면, 많은 이상치 탐지 알고리즘이 존재하지만 가장 일반적인 3가지 알고리즘인 DBSCAN, IsolationForest 및 Boxplots를 살펴보았습니다. 다음을 수행하도록 권장합니다.

 

  1. Titanic Dataset에서 다음 방법을 시도하십시오. 어느 것이 이상치를 가장 잘 감지합니까?
  2. 다른 이상값 탐지 방법을 찾아 처음에 시도한 방법보다 성능이 더 좋은지 나쁜지 확인합니다.

 

나는 나의 추종자들에게 정말로 감사하고, 나는 끊임없이 글을 쓰고 모든 사람들에게 생각할 거리를 제공하기를 희망합니다. 그러나 지금은 작별인사를 해야 합니다 ;}

반응형

댓글