logo

이상치 탐지와 처리

이상치의 의미

  • 이상치(outlier): 다른 관측치와 비교해 드물거나 기존 패턴에서 크게 벗어난 관측치
  • 정상치(inlier): 분석 기준에서 일반적인 패턴에 속하는 관측치
  • 이상치 여부는 데이터의 구조와 탐지 방법에 따라 달라지며, 탐지 결과만으로 오류라고 단정할 수 없음
  • 이상치가 생기는 주요 원인
    • 입력하는 사람의 실수 또는 센서 오류
    • 제대로 교정되지 않은 측정 장비 또는 환경 변화로 인한 측정 오류
    • 데이터 단위 변환이나 스케일링 과정에서의 잘못된 처리
    • 매우 드문 사건이나 특이한 현상
    • 데이터가 다양한 집단에서 수집된 경우
    • 결측치를 부정확한 값으로 대치한 경우
      • 예: 관행적으로 결측치를 99로 입력하는 경우
      • 예: 다봉분포에서 평균값으로 대치하는 경우

이상치 다루기

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

실습 준비

  • 서로 다른 분포를 가진 5개 데이터셋, 각 300개 관측치
  • dataset: 데이터셋 구분, sensor_a·sensor_b: 탐지에 사용할 두 센서값
  • type: 수업용 데이터의 신호(signal)·잡음(noise) 구분
    • 그림의 색상으로만 사용하며 모델 학습에서는 제외
  • 두 센서값은 비슷한 범위로 구성. 실제 데이터의 단위·범위가 다르면 거리 기반 방법을 적용하기 전에 표준화 검토

실습 프롬프트

🤖 AI 프롬프트
outlier_detection.csv의 dataset별로 sensor_a와 sensor_b를 산점도로 그려줘.
EllipticEnvelope, OneClassSVM, IsolationForest, LocalOutlierFactor로
각 데이터셋을 따로 학습하고 정상과 이상의 경계를 비교해줘.
type은 그림의 색상으로만 사용하고 학습 입력에서는 제외해.
EllipticEnvelope, IsolationForest, LocalOutlierFactor의 contamination은 0.15,
OneClassSVM은 nu=0.15, kernel="rbf", gamma="scale"로 설정해.
LocalOutlierFactor는 n_neighbors=35, novelty=True로 새 좌표를 예측해.
난수를 사용하는 모델은 random_state=42로 고정해.
각 단계에 초보자를 위한 한글 주석을 달아줘.

데이터 확인

import numpy as np
import pandas as pd

df_outlier = pd.read_csv("outlier_detection.csv")
df_outlier.head()
실행 결과
   sensor_a  sensor_b         dataset    type
0 -0.563413 -0.365339  single_cluster  signal
1 -0.182347  0.078352  single_cluster  signal
2 -0.667129 -0.673359  single_cluster  signal
3  0.099650 -0.025302  single_cluster  signal
4  1.081618  0.668264  single_cluster  signal
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")

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

탐지 경계를 비교하는 함수

  • 학습 입력: sensor_a, sensor_b 두 열
  • fit(X): 해당 데이터셋에 모델 학습
  • predict(...): 새로운 좌표를 정상 1 또는 이상 -1로 판정
  • 검은 선: 격자에서 예측값이 -1에서 1로 바뀌는 경계
  • 다음 함수는 네 알고리즘의 결과를 같은 방식으로 그리기 위한 공통 코드
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),  # 센서값 범위를 덮는 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

  • 정상 데이터가 하나의 다변량 정규분포에 가깝다고 가정
  • 이상치의 영향을 줄여 중심과 공분산을 추정하고, 타원형 정상 영역에서 멀리 떨어진 점을 탐지
  • 여러 군집이나 초승달 모양을 하나의 타원으로 설명하기는 어려움
  • contamination=0.15: 학습 데이터의 예상 이상치 비율을 15%로 지정해 판정 문턱값 설정
    • 데이터 생성 시의 잡음 비율을 참고한 실습 설정이며, 실제 데이터에서는 근거에 따라 조정
from sklearn.covariance import EllipticEnvelope

outliers_fraction = 0.15  # 실습에서 가정한 이상치 비율
algorithm = EllipticEnvelope(
    contamination=outliers_fraction,
    random_state=42,
)
apply_outlier_detection(algorithm)

EllipticEnvelope로 추정한 이상치 경계

OneClassSVM

  • 정상 데이터가 주로 차지하는 영역을 커널 기반 경계로 학습
  • RBF 커널의 gamma: 각 관측치가 경계에 영향을 미치는 범위 조절
    • 작은 값: 넓은 영향 범위
    • 큰 값: 국소적인 영향 범위
    • "scale": 변수 개수와 입력 분산을 이용해 계산하는 기본 설정
  • nu: 정상 영역을 학습할 때, 학습 데이터가 영역 밖에 놓이는 것을 얼마나 허용할지 조절하는 값
    • nu=0.15: 학습 데이터의 최대 15%가 정상 영역 밖에 놓이는 것을 허용하는 기준
    • 값을 높이면 더 많은 학습 데이터를 영역 밖에 둘 수 있어, 정상 영역을 더 엄격하게 정하는 경향
from sklearn.svm import OneClassSVM

algorithm = OneClassSVM(
    nu=0.15,  # contamination과 구분되는 SVM의 경계 설정
    kernel="rbf",
    gamma="scale",
)
apply_outlier_detection(algorithm)

OneClassSVM으로 추정한 이상치 경계

Isolation Forest

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

Isolation Forest의 정상 지점 분할 예시

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

from sklearn.ensemble import IsolationForest

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

IsolationForest로 추정한 이상치 경계

LocalOutlierFactor

  • 국소 이상치 계수(Local Outlier Factor, LOF): 관측치의 국소 밀도를 주변 이웃의 밀도와 비교
  • n_neighbors=35: 밀도 추정과 비교에 사용하는 가까운 이웃 수
    • 해당 관측치: 가까운 이웃 35개까지의 거리를 이용해 밀도 추정
    • 각 이웃: 자기 주변의 가까운 이웃 35개를 이용해 밀도 추정
    • 해당 관측치의 밀도와 이웃들의 밀도를 비교해, 주변보다 밀도가 낮은 점을 이상치 후보로 판단
  • novelty=True: 학습에 없던 새 좌표에 predict()를 사용하기 위한 설정
    • 이 실습에서는 경계를 그리는 격자와 시험 좌표에 적용
    • 학습 데이터 자체의 이상치를 찾을 때는 기본 설정인 novelty=False와 fit_predict(X) 사용
from sklearn.neighbors import LocalOutlierFactor

algorithm = LocalOutlierFactor(
    n_neighbors=35,  # 밀도 추정과 비교에 사용할 이웃 수
    contamination=outliers_fraction,
    novelty=True,  # 격자상의 새로운 좌표를 예측
)
apply_outlier_detection(algorithm)

LocalOutlierFactor로 추정한 이상치 경계

알고리즘별 예측 실습

  • uneven_cluster로 네 모델을 학습하고, 학습에 사용하지 않은 두 좌표를 판정
  • 같은 좌표에 대한 판정이 탐지 기준에 따라 어떻게 달라지는지 비교
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],
])
models = {
    "EllipticEnvelope": EllipticEnvelope(
        contamination=outliers_fraction, random_state=42,
    ),
    "OneClassSVM": OneClassSVM(nu=0.15, kernel="rbf", gamma="scale"),
    "IsolationForest": IsolationForest(
        contamination=outliers_fraction, random_state=42,
    ),
    "LocalOutlierFactor": LocalOutlierFactor(
        n_neighbors=35, contamination=outliers_fraction, novelty=True,
    ),
}

predictions = pd.DataFrame(index=["(0, -3)", "(1.5, -2)"])
for name, model in models.items():
    model.fit(X_practice)
    predictions[name] = model.predict(test_points)

predictions
실행 결과
           EllipticEnvelope  OneClassSVM  IsolationForest  LocalOutlierFactor
(0, -3)                   1            1               -1                  -1
(1.5, -2)                -1            1                1                  -1
  • 1: 정상 판정, -1: 이상 판정
  • (0, -3): EllipticEnvelope·OneClassSVM은 정상, IsolationForest·LOF는 이상으로 판정
  • (1.5, -2): OneClassSVM·IsolationForest는 정상, EllipticEnvelope·LOF는 이상으로 판정
  • 판정 불일치는 각 방법이 사용하는 분포·경계·고립·밀도 기준의 차이
  • 실무에서는 점수나 판정만으로 삭제하지 않고 원자료·수집 조건·업무 맥락을 함께 확인

퀴즈

문제 1 / 5맞음: 0힌트: 0틀림: 0채점중: 0남음: 5

이상치란 어떤 관측치인가요?

  • ○다른 관측치와 비교해 드물거나 패턴에서 크게 벗어난 값
  • ○평균보다 큰 모든 값
  • ○입력 오류로 생긴 값만 해당

퀴즈를 풀려면 대화형 기능을 불러와야 합니다.

Previous
주성분 분석