본문 바로가기
머신러닝/아웃라이어, 결측치

마할라노비스 거리Mahalanobis distance

by 혜 림 2021. 3. 30.

# 다변량 변수에서 outlier 탐색하는 기법

# 차원이 늘어난 z 스코어 기반 outlier 탐색 기법이라고 생각하면 될 듯 (엄밀히는 조금 다르지만)

 

그러나 z 스코어 기반 outlier 탐색 기법은 유클리디안 거리를 사용함 

 

# 그렇기에 다른 점은 

- 점들 사이의 거리가 아니라, 센터로부터의 거리를 이용함 

- 공분산 행렬covariance matrix를 고려함 (=> 변수의 variation을 잘 캡쳐함)

- 카이 제곱 분포의 quantile에 기반해 cutoff 값을 정함

- 데이터 자체에 대한 가정 X

 

 



# 거리를 구하는 방법

이때 점들 사이의 거리가 아니라 센터로부터의 거리라고 했으므로 위에서 X2는 centerpoint라고 생각하면 된다.

centerpoint는 각 col의 평균으로 이루어진 벡터다. 

여기서 C는 공분산 행렬을 의미한다. 

 

 

# 임의의 데이터 생성 

# 데이터 셋의 크기를 12,000 * 15 으로 설정
# mu vector의 크기는 15
# cov vector의 크기는 15*15
# 다변량 정규 분포를 만들기 위해서 평균벡터와 공분산행렬을 만듦

# 그 중 11,880(90%)의 정상 데이터 생성
# 평균은 모두 2, 분산은 3, 공분산은 모두 1

import numpy as np
import pandas as pd
import scipy.stats as sps
import random


row=12000
col=15

mu=np.full(col,2)
cov=np.full((col,col),1)
np.fill_diagonal(cov,3)
# print(mu)
# print("="*50)
# print(cov)

rv=sps.multivariate_normal(mu,cov)
data=rv.rvs(12000)
# print(data)

# outlier 여부를 알려주는 feature새로 생성
df=pd.DataFrame(data)
df['outlier']=0
#display(df)
# 그 중 10%인 120 *15의 이상치 데이터 셋
# 평균은 모두 2, 분산 3, 공분산 2

row=120
col=15

mu=np.full(col,11)
cov=np.zeros((col,col))
np.fill_diagonal(cov,1)

# print(mu)
# print("="*50)
# print(cov)

rv=sps.multivariate_normal(mu,cov)
out=rv.rvs(row)
# print(out)
# 랜덤하게 정상 데이터와 이상치 데이터를 섞음

out=np.c_[ out, np.ones(row) ]
out_index=random.sample(range(0,12000),row)
for i,val in enumerate(out_index):
    df.loc[df.index[val],:]=out[i] 
# display(df)

이상치를 포함하고 있는 데이터 셋에서, 마할라노비스 거리를 이용하여 이상치 탐색

 

df_y=df.outlier
df_x=df.drop(df[['outlier']],axis=1)
df_x=np.array(df_x)
realIndex=np.where(df_y==1)

from scipy.stats import chi2 # 카이제곱 분포 라이브러리

# 공분산의 역행렬 구하기
covariance=np.cov(df_x,rowvar=False)
covariance_reverse=np.linalg.matrix_power(covariance,-1)

# 중심점 찾기 (평균 이용)
centerpoint=np.mean(df_x,axis=0)

# 거리 벡터 만들기
distances=[]
for i, val in enumerate(df_x):
    x1=val
    x2=centerpoint
    distance=(x1-x2).T.dot(covariance_reverse).dot(x1-x2)
    distances.append(distance)
distances=np.array(distances)

# F 점수 기준으로 이상치 색출


cutoff=chi2.ppf(0.99,df_x.shape[1]) # df.shape[1] 자유도 하에서 카이제곱분포의 상위 1%의 값을 threshold로 지정
outlierIndex=np.where(distances>cutoff)

print("True Positive = ",len(set(outlierIndex[0]).intersection(set(realIndex[0])))/len(outlierIndex[0]))
# True Positive =  0.5607476635514018
len(set(realIndex[0])-(set(outlierIndex[0])))
# 0

- 아웃라이어 데이터가 너무 소심하게 떨어져 있어서인지 알파(0.95, 0.99 같은 값들)을 높게 잡아주어야 했다.

 알파값을 0.999로 설정한다면, true positive는 약 95%로 상당히 높게 나왔다.

하지만 실제 상황에서도 그렇게 알파값을 극한으로 잡을지는 의문이다.

 

- 또한 실제로 있는 아웃라이어의 비율에 비해서 마할라노비스 거리를 이용하면 많은 아웃라이어들이 측정됐음을 알 수 있다. 또한 false positive는 많지만 false negative은 존재하지 않는다는 점에 주목할 필요가 있다. 

 

- 한편 중위수를 이용한 경우와 평균을 이용한 경우 큰 차이는 없었다.

scaler를 적용한 후에도 데이터를 돌려봤지만, 크게 달라지는 것은 없었다. 

 

 

임의의 데이터 셋으로 실행하였으니, 실제의 데이터 셋은 다를 수 있다. 

 

 

# 주의할 점

- 만약 데이터가 원형으로 분포한다면, 유클리디안 거리를 쓰는 것이 합리적

- 또한 거리 기반이기에 scaling에 유의할 것  

- 또한 중요한 문제점은 centerpoint를 평균으로 계산한다면, outlier에 의해 평균이 왜곡될 가능성이 있음 

 

 

참고자료

datascienceschool.net/02%20mathematics/08.06%20%EB%8B%A4%EB%B3%80%EC%88%98%EC%A0%95%EA%B7%9C%EB%B6%84%ED%8F%AC.html

 

8.6 다변수정규분포 — 데이터 사이언스 스쿨

\(D\)차원 **다변수정규분포(MVN: multivariate Gaussian normal distribution)**의 확률밀도함수는 평균벡터 \(\mu\) 와 공분산행렬 \(\Sigma\) 라는 두 개의 모수를 가지며 다음과 같은 수식으로 정의한다. \[ \begin{a

datascienceschool.net

https://towardsdatascience.com/multivariate-outlier-detection-in-python-e946cfc843b3

 

Multivariate Outlier Detection in Python

Multivariate Outliers and Mahalanobis Distance in Python

towardsdatascience.com

 

'머신러닝 > 아웃라이어, 결측치' 카테고리의 다른 글

실루엣 분석Silhouette Analysis  (0) 2021.03.30
자기조직화지도 SOM  (0) 2021.03.30
알고리즘 간단 요약 및 비교  (0) 2021.03.30
paper 요약  (0) 2021.03.23
알고리즘, 데이터 적용  (0) 2021.03.23

댓글