그동안 데이터전처리 관련 포스팅을 아주 오랫동안 쉬었다가 오랜만에 작성을 해본다.
지난번에 포스팅 한 이상치 처리 시리즈는 모두 일변량 이상치 감지에 관한 것이었고, 오늘부터는 이변량과 다변량 이상치 감지에 관한 포스팅으로 작성하려고 한다.
그리하여 다변량 분석에 속하는 첫번째 이상치 감지 알고리즘은 DBSCAN(Density-Based Spatial Clustering of Applications with Noise)에 관한 것이다.
이 알고리즘은 클러스터에 속하지 않는 점을 이상값으로 식별하는 클러스터링 알고리즘인 K-Means의 대안으로 사용하기도 한다. 클러스터 수를 미리 지정할 필요가 없다는 점을 제외하면 K-Means와 같다. 클러스터링은 유사한 데이터 포인트들이 그룹화되는 방식으로 데이터 포인트 세트를 그룹화하는 방법이다. 따라서 클러스터링 알고리즘은 데이터 포인트 간의 유사성 또는 비 유사성을 찾는다. 클러스터링은 비지도 학습에 속하는 방법이므로 데이터 포인트와 관련된 레이블이 없다. 그래서 DBSCAN은 데이터의 기본 구조를 찾기 위해서 열심히 작동하게 된다 :))
클러스터링 작업을 수행하는 다양한 접근 방식과 알고리즘이 있는데 다음과 같이 큰 3가지 분류로 구분할 수 있다.
- 파티션 기반(Partition-based) 클러스터링 : 예 : k-means, k-median
- 계층적(Hierarchical) 클러스터링 : 예 : Agglomerative, Divisive
- 밀도 기반(Density-based) 클러스터링 : 예 : DBSCAN
DBSCAN은 위에서 언급했다시피 밀도 기반의 클러스터링 알고리즘이다. 앞으로 시간은 아주 많으므로 K-means와 Hierarchical 기반 클러스터링 관련한 글은 다음번에 작성해 볼 것이다.
Density-based 클러스터링 개념
위의 3가지 분류 중 파티션 기반의 클러스터링이나 계층적 클러스터링 기술은 일반적인 모양의 클러스터에서 매우 효율적이다. 예를 들어, 아래 그림에 활용된 데이터셋은 k-means 알고리즘을 사용하여 3개의 클러스터로 쉽게 나누어 졌다.
그러나 형태가 중구난방으로 된 임의의 형태의 클러스터나 이상값을 감지하는 경우에는 오히려 밀도 기반의 클러스터링 기술이 더 효율적이라고 한다.
위의 그림에서 데이터 포인트는 임의의 모양으로 그룹화되거나 이상값을 포함하고 있다. 밀도 기반 클러스터링 알고리즘은 고밀도의 영역과 이상값을 찾는데 매우 효과적이다. 여러 분야에서 일련의 작업 과정 중에서 이상값을 탐지하는 것은 매우 중요한 작업이다.
DBSCAN 알고리즘 개념
DBSCAN은 노이즈가 있는 밀도 기반 공간 클러스터링을 대표하는 알고리즘이다. 임의의 모양의 클러스터나 이상값이 있는 클러스터를 찾을 수 있다. 이 알고리즘은 주된 목표는 특정 포인트가 클러스터의 많은 포인트에 가까이 위치하면 클러스터에 속한다고 판단하는 것이다. 이것을 판단하는 주요한 2가지 파라미터가 있다.
- eps : 이웃을 지정하는 거리. 두 점 사이의 거리가 eps보다 작거나 같으면 두 점을 이웃이라고 간주한다.
- minPts : 클러스터를 정의하기 위한 최소 데이터 포인트 수를 의미한다.
이 두 파라미터 변수를 기반으로 포인트를 핵심 포인트와 경계 포인트로 분류한다.
핵심 포인트(Core point) : 반경 eps가 있는 주변 영역에 최소 minPts 수의 포인트 (포인트 자체 포함)가 있는 경우의 포인트를 의미한다.
경계 지점(Border point) : 핵심 지점에서 도달 할 수 있고 주변 영역 내에 minPts 개수 미만의 지점이 있는 경우의 포인트를 의미한다.
이상치(Outlier) : 핵심 포인트가 아니거나 핵심 포인트에서 도달 할 수 없는 포인트를 이상치로 정의한다.
방금 정의한 3가지 포인트를 위키피디아의 시각화 이미지로 확인해보자.
이 경우 minPts는 4이다. 빨간색 포인트는 반경 eps가 있는 주변 영역 내에 4개 이상의 포인트가 있기 때문에 핵심 포인트이다. 이 영역은 그림에서 원으로 표시된다. 노란색 포인트는 핵심 포인트에서 도달 할 수 있고 이웃 내에 4개 미만의 포인트가 있기 때문에 경계 포인트이다. 도달 할 수 있음이란 핵심 포인트의 주변 영역에 있음을 의미한다. 포인트 B와 C는 인접 지역(즉, 반경 eps의 주변 영역) 내에 두 포인트(포인트 자체 포함해서)을 갖는다. 마지막으로 N은 핵심 포인트가 아니고 핵심 포인트에도 도달 할 수 없기 때문에 이상치로 정의할 수 있다.
DBSCAN 알고리즘 작동 방식
위에서 파라미터(=매개변수)와 포인트의 유형에 대해서 학습하였다. 방금 학습한 지식을 베이스로 알고리즘이 어떻게 동작하는지를 학습해보자.
- minPts 및 eps를 정의한다.
- 시작 포인트는 반경 eps를 사용하여 결정된 인접 영역에서 무작위로 선택된다. 인접 포인트에 최소 minPts에서 정의한 수의 포인트가 있으면 해당 포인트가 핵심 포인트로 표시되고 클러스터 형성을 시작한다. 그렇지 않은 경우의 포인트는 노이즈로 표시된다. 클러스터 형성이 시작되면(클러스터 A라고 가정) 초기 포인트 근처에 있는 모든 포인트가 클러스터 A의 일부가 된다. 이러한 새 포인트가 핵심 포인트인 경우 근처에있는 포인트도 클러스터 A에 추가된다.(Note. 노이즈로 표시된 포인트도 다시 클러스터의 일부가 될 수 있다.)
- 다음 단계는 이전 단계에서 정의하지 않은 포인트 중에서 무작위로 다른 포인트를 선택하는 것이다. 그런 다음 동일한 절차가 적용된다.(Note. 이러한 단계를 적용함으로써 DBSCAN 알고리즘은 고밀도 영역을 찾아 저밀도 영역과 분리 할 수 있게 된다.)
- 모든 포인트가 다 정의되고 나면 과정이 완료된다.
점 사이의 거리는 k-means 알고리즘에서와 같이 거리 측정 방법을 사용하여 결정된다. 가장 일반적으로 사용되는 방법은 유클리드 거리이다.
클러스터에는 이웃(서로 연결 가능한)인 핵심 포인트와 이러한 핵심 포인트의 모든 경계 포인트가 포함된다. 클러스터를 형성하는데 필요한 조건은 적어도 하나의 핵심 포인트가 있어야 한다는 것이다. 가능성은 거의 없지만 하나의 핵심 포인트와 경계 포인트만 있는 클러스터가 있을 수도 있다.
파이썬 실습 : DBSCAN 알고리즘 이상치 탐지
위에서 이론은 충분히 학습한 것 같으니 파이썬을 활용해서 데이터셋에서 진짜 이상치들을 탐지해보는 실습을 해보기로 한다 :))
개인적으로 제일 신나는 시간이다!! ㅎㅎ
필요한 라이브러리와 세팅은 다음과 같이 해주었다.
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import warnings
warnings.filterwarnings("ignore")
from sklearn.cluster import DBSCAN
sns.set(style="whitegrid", palette="pastel", color_codes=True)
sns.set_context('talk')
활용한 데이터셋은 캐글 오픈 데이터셋인 보험 데이터셋!
(링크 : www.kaggle.com/rpsuraj/outlier-detection-techniques-simplified/?select=insurance.csv#data)
df = pd.read_csv("insurance.csv")
df.head()
out :
이 보험 데이터셋은 나이, 성별, BMI, 자녀 수, 흡연 여부, 지역, 보험료 이렇게 7가지로 변수가 구성되어 있다.
7가지 변수 중에서 나는 나이['age']와 BMI['bmi'] 변수만을 선택하여 DBSCAN 알고리즘 이상치 탐지 실습을 진행해보겠다.
df[['age'], ['bmi']]로 2가지 변수로만 구성된 데이터프레임을 만들어 준 다음 싸이킷런 알고리즘 실행에 적합한 데이터 형태인 array 형태로 추출하기 위해서 values를 사용하여 추출한 배열 형식을 X로 만들어주었다.
X = df[['age','bmi']].values
X
out :
>> X를 확인하면 2개의 변수 배열이 잘 추출된 것을 확인할 수 있다.
그 다음 오늘의 주인공인 싸이킷런의 DBSCAN을 불러와 알고리즘 탐지 모델을 만들어 방금 만들어놓은 X에 피팅해준다.
일단 초기 모델이니 eps는 2.5, min_sample은 13으로 시작해볼까? =_=
dbscan = DBSCAN(eps=2.5, min_samples=13).fit(X)
피팅까지 끝냈으니 이제 결과를 확인할 시간이다.
dbscan으로 모든 데이터 중 이상치로 분류한 데이터를 구분하기 위해서 라벨링을 해주고, 각 라벨의 수치가 어느 정도 나왔는지 value_counts로 확인한다.
labels = dbscan.labels_
pd.Series(labels).value_counts()
out :
>> 이런.. 이상치가 무려 86개나 나왔다 ㅠㅠ
위의 결과를 시각화로 구현하여 눈으로 확인해보자.
plt.figure(figsize=(12,12))
unique_labels = set(labels)
colors = ['#586fab', '#f55354']
for color,label in zip(colors, unique_labels):
sample_mask = [True if l == label else False for l in labels]
plt.plot(X[:,0][sample_mask], X[:, 1][sample_mask], 'o', color=color);
plt.xlabel('Age');
plt.ylabel('BMI');
>> 빨간 포인트들이 이상치로 분류된 점들이다. DBSCAN 모델을 너무 보수적으로 만들었나.(그건 알 수 없다)
이번엔 모델링을 다시 조정해서 실험을 해보았다.
dbscan = DBSCAN(eps=3.0, min_samples=10).fit(X)
위와 같은 과정은 코드를 생략한다!!
결과의 시각화는 이렇게 나왔다.
사실 이 모델링으로 판단한 이상치들이 맞는건지 이전 모델링이 더 맞는건지 이것만 기준으로해서는 확실히 알 수 없다. 지금 이것은!! 데이터 분석의 목적을 정하지도 않은 상태에서 그저 DBSCAN 이상치 탐지 실험을 해보는 것이 전부인 포스팅이기 때문이다 -_-;;
그래서 DBSCAN 이상치 탐지의 개념과 작동원리, 그리고 그것을 파이썬으로 실습해보는 것으로 만족해하며 포스팅을 마치려고 한다.
추가로 이 DBSCAN 모델링 이상치 탐지 방법의 단점은 모델에서 정의한 치수가 높을수록 정확도가 떨어질 수 있다고 한다. 또한 eps 올바른 값을 추정하는 것은 매우 어려울 수 있기 때문에 많은 가정을 놓고 많이 실험해봐야 한다는 것;;(이것은 다른 데이터 분석 알고리즘도 마찬가지인 듯 ㅠㅠ)
Clary K
[참고 사이트]
towardsdatascience.com/dbscan-clustering-explained-97556a2ad556
www.kaggle.com/rpsuraj/outlier-detection-techniques-simplified#Types-of-outliers: