logo

이상치 탐지와 처리

이상치 탐지와 처리

  • 이상치(outlier): 기존에 관찰된 분포 바깥에 속하는 값
  • 정상치(inlier): 기존에 관찰된 분포 안에 속하는 값
  • 이상치가 생기는 주요 원인
    • 입력하는 사람의 실수 또는 센서 오류
    • 제대로 교정되지 않은 측정 장비 또는 환경 변화로 인한 측정 오류
    • 데이터 단위 변환이나 스케일링 과정에서의 잘못된 처리
    • 매우 드문 사건이나 특이한 현상
    • 데이터가 다양한 집단에서 수집된 경우
    • 결측치를 부정확한 값으로 대치한 경우
      • 예: 관행적으로 결측치를 99로 입력하는 경우
      • 예: 다봉분포에서 평균값으로 대치하는 경우

이상치 다루기

  • 수정해야 할 경우
    • 데이터 포인트의 원본과 대조했을 때 입력이 잘못된 경우
  • 삭제해야 할 경우
    • 데이터 포인트가 연구의 모집단에 속하지 않는 경우
    • 실험 절차나 측정이 잘못된 경우
  • 진단 지표를 기계적으로 적용해 데이터를 삭제하면 안 됨
  • 데이터 삭제에는 객관적 이유가 필요
  • 데이터를 삭제한 경우 결과 보고에 포함해야 함
  • 삭제 여부를 결정하기 어려운 경우
    • 삭제한 데이터셋과 삭제하지 않은 데이터셋을 따로 분석하고 결과 비교
  • 모형을 수정해야 할 경우
    • 중요한 변수의 누락
    • 상호작용
    • 비선형성

데이터 불러오기

# 데이터 로딩
import numpy as np
import pandas as pd

df_outlier = pd.read_csv("outlier_detection.csv")
df_outlier.head()
dataset_names = df_outlier.dataset.unique().tolist()

데이터셋 하나 그리기

# 시각화
import matplotlib.pyplot as plt
import seaborn as sns

dataset = df_outlier.query('dataset == "single_cluster"')
sns.scatterplot(x="sensor_a", y="sensor_b", hue="type", data=dataset)

single_cluster 데이터셋의 이상치 산점도

한 번에 그리기

g = sns.FacetGrid(df_outlier, col="dataset", hue="type")
g.map(sns.scatterplot, "sensor_a", "sensor_b")

여러 데이터셋을 한 번에 비교한 산점도

시각화 함수

def apply_outlier_detection(algorithm):
    g = sns.FacetGrid(
        df_outlier,
        col="dataset",
        col_order=dataset_names,
        hue="type",
    )
    g.map(sns.scatterplot, "sensor_a", "sensor_b")

    xx, yy = np.meshgrid(
        np.linspace(-7, 7, 150),
        np.linspace(-7, 7, 150),
    )

    for i, ax in enumerate(g.axes.flat):
        dataset_name = dataset_names[i]
        X = df_outlier.query(
            "dataset == @dataset_name"
        )[["sensor_a", "sensor_b"]].to_numpy()

        algorithm.fit(X)
        Z = algorithm.predict(np.c_[xx.ravel(), yy.ravel()])
        Z = Z.reshape(xx.shape)
        ax.contour(xx, yy, Z, levels=[0], linewidths=2, colors="black")

    return g.add_legend()
  • 각 데이터셋에 알고리즘을 학습한 뒤, 격자상의 모든 점을 예측해 정상/이상 경계를 그림

EllipticEnvelope

  • 데이터의 분포를 알고 있다고 가정할 수 있으면, 분포를 추정해 이상점 여부를 진단할 수 있음
from sklearn.covariance import EllipticEnvelope

outliers_fraction = 0.15
algorithm = EllipticEnvelope(
    contamination=outliers_fraction,
    random_state=42,
)
apply_outlier_detection(algorithm)

EllipticEnvelope로 추정한 이상치 경계

OneClassSVM

  • 이상치와 마진을 최대화하는 방식
  • gamma를 이용해 커널의 특성을 조절할 수 있음
    • "scale"은 기본값
  • RBF 커널의 경우 gamma를 줄이면 좁고 뾰족한 형태의 경계가 만들어짐
from sklearn.svm import OneClassSVM

algorithm = OneClassSVM(
    nu=outliers_fraction,
    kernel="rbf",
    gamma="scale",
)
apply_outlier_detection(algorithm)

OneClassSVM으로 추정한 이상치 경계

Isolation Forest

  • 데이터를 분할하는 트리(tree)를 무작위로 만듦
  • 이상치는 뿌리에서 짧은 거리 안에서 고립되는 경향이 있음
  • 트리를 많이 만들어 forest를 구성하고, 쉽게 고립되는 점을 찾음

Isolation Forest의 정상 지점 분할 예시

Isolation Forest의 비정상 지점 분리 예시

IsolationForest

from sklearn.ensemble import IsolationForest

algorithm = IsolationForest(
    contamination=outliers_fraction,
    random_state=42,
)
apply_outlier_detection(algorithm)

IsolationForest로 추정한 이상치 경계

LocalOutlierFactor

  • k-최근접 이웃에 대해 밀도를 비교함
  • 주변 이웃과 비교해 멀리 떨어져 있으면 이상치로 판정
from sklearn.neighbors import LocalOutlierFactor

algorithm = LocalOutlierFactor(
    n_neighbors=35,
    contamination=outliers_fraction,
    novelty=True,
)
apply_outlier_detection(algorithm)

LocalOutlierFactor로 추정한 이상치 경계

알고리즘별 예측 실습

퀴즈를 풀기 전에 다음 코드를 한 번 실행합니다. uneven_cluster 데이터로 모델을 학습하고, (0, -3)(1.5, -2)를 차례로 판정합니다.

import numpy as np
import pandas as pd

df_outlier = pd.read_csv("outlier_detection.csv")

X_practice = (
    df_outlier
    .query('dataset == "uneven_cluster"')
    [["sensor_a", "sensor_b"]]
    .to_numpy()
)

test_points = np.array([
    [0.0, -3.0],
    [1.5, -2.0],
])

네 알고리즘의 predict()는 정상으로 판정한 점에 1, 이상치로 판정한 점에 -1을 반환합니다. 예측 배열의 첫 번째 값은 (0, -3), 두 번째 값은 (1.5, -2)의 결과입니다.