본문 바로가기

Euron/정리

[Week11] 07. 군집화(2)

03. 평균 이동(Mean Shift)

K-평균과 유사하게 중심을 군비의 중심으로 지속적으로 움직이면서 군집화를 수행한다.

K-평균이 중심에 소속된 데이터의 평균 거리 중심으로 이동하는 데 반해, 평균 이동은 중심을 데이터가 모여 있는 밀도가 가장 높은 곳으로 이동시킨다.

 

평균 이동 군집화는 확률 밀도 함수를 이용해 데이터의 분포도를 찾고, 이를 이용해 군집 중심점을 찾는다.

알반적으로 주어진 모델의 확률 밀도 함수를 찾기 위해 KDE를 이용한다.

 

KDE는 커널 함수를 통해 어떤 변수의 확률 밀도 함수를 측정하는 대표적인 방법으로, 주변 데이터와의 거리 값을 KDE 함수 값으로 입력한 뒤 그 반환 값을 현재 위치에서 업데이터하면서 이동하는 방식을 취한다.

대표적인 커널 함수로서 가우시안 분포 함수가 사용된다.

커널 함수식

K는 커널 함수, x는 확률 변숫값, xi는 관측값, h는 대역폭(bandwidth)이다.

h값이 작을수록 뾰족한 KDE를 가지게 되며, 이는 변동성이 큰 방식으로 확률 밀도 함수를 추정하므로 과적합하기 쉽다.

또한 대역폭이 작을수록 많은 수의 군집 중심점을 가진다. 군집의 개수를 지정하지 않고 대역폭의 크기에 따라 군집화를 수행하므로 가장 중요한 초기화 파라미터는 bandwidth이다.

사이킷러은 최적의 대역폭 계산을 위해 estimate_bandwidth() 함수를 제공한다.

import numpy as np
from sklearn.datasets import make_blobs
from sklearn.cluster import MeanShift

X, y = make_blobs(n_samples=200, n_features=2, centers=3, 
                  cluster_std=0.7, random_state=0)
                  
from sklearn.cluster import estimate_bandwidth

bandwidth = estimate_bandwidth(X)

import pandas as pd

clusterDF = pd.DataFrame(data=X, columns=['ftr1', 'ftr2'])
clusterDF['target'] = y

# estimate_bandwidth()로 최적의 bandwidth 계산
best_bandwidth = estimate_bandwidth(X)

meanshift= MeanShift(bandwidth=best_bandwidth)
cluster_labels = meanshift.fit_predict(X)
print('cluster labels 유형:',np.unique(cluster_labels))

 

최적으로 측정된 bandwidth를 평균 이동 입력값으로 적용해 make_blobs 데이터 세트에 군집화를 수행한 결과 3개의 군집으로 구성됨을 알 수 있다.

 

구성된 3개의 군집을 cluster_centers_속성을 이용해 시각화한다.

import matplotlib.pyplot as plt
%matplotlib inline

clusterDF['meanshift_label']  = cluster_labels
centers = meanshift.cluster_centers_
unique_labels = np.unique(cluster_labels)
markers=['o', 's', '^', 'x', '*']

for label in unique_labels:
    label_cluster = clusterDF[clusterDF['meanshift_label']==label]
    center_x_y = centers[label]
    # 군집별로 다른 마커로 산점도 적용
    plt.scatter(x=label_cluster['ftr1'], y=label_cluster['ftr2'], edgecolor='k', marker=markers[label] )
    
    # 군집별 중심 표현
    plt.scatter(x=center_x_y[0], y=center_x_y[1], s=200, color='gray', alpha=0.9, marker=markers[label])
    plt.scatter(x=center_x_y[0], y=center_x_y[1], s=70, color='k', edgecolor='k', marker='$%d$' % label)
    
plt.show()

평균 이동의 장점은 데이터 세트의 형태를 특정 형태로 가정한다든가, 특정 분포도 기반의 모델로 가정하지 않기 때문에 좀 더 유연한 군집화가 가능하다. 

또한 이상치의 영향력도 크지 않으며, 미리 군집의 개수를 정할 필요도 없다.

다만 알고리즘의 수행 시간이 오래걸리고, bandwidth의 영향력이 너무 크다.

이러한 특징으로 평균 이동 군집화 기법은 컴퓨터 비전 영역에서 더 많이 사용된다.

 


04. GMM(Gaussian Mixture Model)

GMM(Gaussian Mixture Model) 소개

GMM 군집화는 군집화를 적용하고자 하는 데이터가 여러 개의 가우시안 분포를 가진 데이터 집합이 섞여서 생성된 것이라는 가정하에 군집화를 수행하는 방식이다.

정규 분포로도 알려진 가우시안 분포는 좌우 대칭형의 종 형태를 가진 연속 확률 함수이다.

평균이 0이고, 표준 편차가 1인 정규 분포를 표준 정규 분포라 한다.

 

GMM은 데이터를 여러 개의 가우시안 분포가 섞인 것으로 간주한다.

따라서 데이터 세트가 있을 때, 이를 구성하는 여러 개의 정규 분포 곡선을 추출하고, 개별 데이터가 이 중 어떤 정규 분포에 속하는지 결정하는 방식이다.

이와 같은 방식은 GMM에서는 모수 추정이라 한다.

 

모수 추정에서 추정하는 것

  • 개별 정규 분포의 평균과 분산
  • 각 데이터가 어떤 정규 분포에 해당되는지의 확률

모수 추정을 위해 GMM은 EM방법을 작용하며, 사이킷런을 이를 지원하기 위해 GaussianMixture 클래스를 지원한다.

 

GMM을 이용한 붓꽃 데이터 세트 군집화

GaussianMixture 객체의 가장 중요한 초기화 파라미터는 모델의 총 개수를 나타내는 n_components이다.

from sklearn.mixture import GaussianMixture

gmm = GaussianMixture(n_components=3, random_state=0).fit(iris.data)
gmm_cluster_labels = gmm.predict(iris.data)

# 클러스터링 결과를 irisDF 의 'gmm_cluster' 컬럼명으로 저장
irisDF['gmm_cluster'] = gmm_cluster_labels
irisDF['target'] = iris.target

# target 값에 따라서 gmm_cluster 값이 어떻게 매핑되었는지 확인. 
iris_result = irisDF.groupby(['target'])['gmm_cluster'].value_counts()
print(iris_result)

Target 0은 cluster 0으로, Target 2는 cluster2 2로 모두 잘 매핑됐다. Target 1만 분산되어 매핑되어 앞서 한 K-means보다 효과적인 결과가 도출되었다.

 

붓꽃 데이터 세트는 GMM 군집화에 더 효과적이다.

개별 군집 내의 데이터 세트가 원형으로 흩어져 있는 경우, K-means와 같이 평균 거리 중심으로 이동하는 알고리즘이 더 효과적이다.

 

GMM과 K-평균의 비교

KMeans는 원형의 범위에서 군집화를 수행한다.

make_blobs()로 긴 데이터 세트를 만든 후 KMeans와 GMM을 비교한다.

from sklearn.datasets import make_blobs

# make_blobs() 로 300개의 데이터 셋, 3개의 cluster 셋, cluster_std=0.5 을 만듬. 
X, y = make_blobs(n_samples=300, n_features=2, centers=3, cluster_std=0.5, random_state=0)

# 길게 늘어난 타원형의 데이터 셋을 생성하기 위해 변환함. 
transformation = [[0.60834549, -0.63667341], [-0.40887718, 0.85253229]]
X_aniso = np.dot(X, transformation)
# feature 데이터 셋과 make_blobs( ) 의 y 결과 값을 DataFrame으로 저장
clusterDF = pd.DataFrame(data=X_aniso, columns=['ftr1', 'ftr2'])
clusterDF['target'] = y

# 3개의 Cluster 기반 Kmeans 를 X_aniso 데이터 셋에 적용 
kmeans = KMeans(3, random_state=0)
kmeans_label = kmeans.fit_predict(X_aniso)
clusterDF['kmeans_label'] = kmeans_label

visualize_cluster_plot(kmeans, clusterDF, 'kmeans_label',iscenter=True)

K-means 결과

# 3개의 n_components기반 GMM을 X_aniso 데이터 셋에 적용 
gmm = GaussianMixture(n_components=3, random_state=0)
gmm_label = gmm.fit(X_aniso).predict(X_aniso)
clusterDF['gmm_label'] = gmm_label

# GaussianMixture는 cluster_centers_ 속성이 없으므로 iscenter를 False로 설정. 
visualize_cluster_plot(gmm, clusterDF, 'gmm_label',iscenter=False)

GaussianMixture는 군집의 중심 좌표를 구할 수 없지만, 시각화 결과 긴 데이터 세트에서는 K-Means보다는 GMM에서 군집화가 더 잘 됨을 알 수 있다.

print('### KMeans Clustering ###')
print(clusterDF.groupby('target')['kmeans_label'].value_counts())
print('\n### Gaussian Mixture Clustering ###')
print(clusterDF.groupby('target')['gmm_label'].value_counts())

이와 같이 GMM의 경우 KMeans보다는 유연하게 다양한 데이터 세트에 잘 적용될 수 있다는 장점이 있다.

하지만, 군집화를 위한 수행 시간이 오래걸린다는 단점이 있다.

 


05. DBSCAN

DBSCAN의 개요

다음으로 밀도 기반 군집화의 대표적인 알고리즘인 DBSCAN에 대해 알아본다.

DBSCAN은 간단하고 직관적인 알고리즘으로 돼있음에도 데이터의 분포가 기하학적으로 복잡한 데이터 세트에도 효과적인 군집화가 가능하다.

위와 같은 형태의 데이터 세트는 K-Means, Mean-Shift, GMM으로는 효과적인 군집화를 수행하기 어렵다.

 

DBSCAN이 이러한 복잡한 데이터 세트에서도 잘 수행하는데, 가장 중요한 두 파라미터는 다음과 같다.

  • 입실론 주변 영역(epsilon) : 개별 데이터를 중심으로 입실론 반경을 가지는 원형의 영역
  • 최소 데이터 개수(min points) : 개별 데이터의 입실론 주변 영역에 포함되는 타 데이터의 개수

epsilon 내에 포함되는 최소 데이터 개수를 충족시키는가 아닌가에 따라 데이터 포인트를 정의한다.

  • Core Point : 주변 영역(epsilon) 내에 최소 데이터 개수 이상의 타 데이터를 가지고 있을 경우 해당 데이터를 핵심 포인트라고 한다.
  • Neighbor Point : 주변 영역 내에 위치한 타 데이터를 이웃 포인트라고 한다.
  • Border Point : 주변 역역 내에 최소 데이터 개수 이상의 이웃 포인트를 가지고 있지 않지만 핵심 포인트를 이웃 포인트를 가지고 있는 데이터를 경계 포인트라 한다.
  • Noise Point : 최소 데이터 개수 이상의 이웃 포인터를 가지고 있지 않으며, 핵심 포인트도 이웃 포인트로 가지고 있지 않은 데이터를 잡음 포인트라 한다.

DBSCAN은 epsilon 주변 영역의 촤소 데이터 개수를 포함하는 밀도 기준을 충족시키는 데이터인 핵심 포인트를 연결하면서 군집화를 구성하는 방식이다.

 

DBSCAN 클래스의 주요 파라미터

  • eps : epsilon 주변 영역의 반경을 의미한다.
  • min_samples : 핵심 포인트가 되기 위해 입실론 주변 영역 내에 포함대야 할 데이터의 최소 개수를 의미한다.(자신의 데이터를 포함한다.)

 

DBSCAN 적용하기 - 붓꽃 데이터 세트

from sklearn.cluster import DBSCAN

dbscan = DBSCAN(eps=0.6, min_samples=8, metric='euclidean')
dbscan_labels = dbscan.fit_predict(iris.data)

irisDF['dbscan_cluster'] = dbscan_labels
irisDF['target'] = iris.target

iris_result = irisDF.groupby(['target'])['dbscan_cluster'].value_counts()
print(iris_result)

-1은 노이즈에 속하는 군비을 의미한다.

DBSCAN은 군집의 개수를 알고리즘에 따라 자동으로 저장하여 두 개의 군집으로 군집화됐다.

 

2차원 평면에서 군집화 데이터 세트를 표현하기 위해 PCA를 이용한 후 시각화한다.

from sklearn.decomposition import PCA
# 2차원으로 시각화하기 위해 PCA n_componets=2로 피처 데이터 세트 변환
pca = PCA(n_components=2, random_state=0)
pca_transformed = pca.fit_transform(iris.data)
# visualize_cluster_2d( ) 함수는 ftr1, ftr2 컬럼을 좌표에 표현하므로 PCA 변환값을 해당 컬럼으로 생성
irisDF['ftr1'] = pca_transformed[:,0]
irisDF['ftr2'] = pca_transformed[:,1]

visualize_cluster_plot(dbscan, irisDF, 'dbscan_cluster', iscenter=False)

DBSCAN을 적용할 때는 특정 군집 개수로 군집을 강제하지 않는 것이 좋다.

DBSCAN 알고리즘에 적절한 eps와 min_samples 파라미터를 통해 최적의 군집을 찾는게 더 중요하다.

일반적으로 eps의 값을 크게 하면 반경이 커져 포함하는 데이터가 많아지므로 노이즈 데이터 개수가 작아딘다.

반대로 min_samples를 크게 하면 주어진 반경 내에서 더 많은 데이터를 포함시켜야 하므로 노이즈 데이터 개수가 커지게 된다.

 

DBSCAN 적용하기 - make_circles() 데이터 세트

make_circles() 함수를 이용해 위에서 본 것과 같은 2차원 데이터 세트를 만든다.

make_circles() 함수는 2개의 피처만을 생성해 피처 개수 지정 파라미터가 따로 없다.

파라미터 noise는 노이즈 데이터 세트의 비율이며, factor는 외부 원과 내부 원의 scale 비율이다.

from sklearn.datasets import make_circles

X, y = make_circles(n_samples=1000, shuffle=True, noise=0.05, random_state=0, factor=0.5)
clusterDF = pd.DataFrame(data=X, columns=['ftr1', 'ftr2'])
clusterDF['target'] = y

visualize_cluster_plot(None, clusterDF, 'target', iscenter=False)

 

먼저 K-  Means로 위 데이터 세트를 군집화한다.

# KMeans로 make_circles( ) 데이터 셋을 클러스터링 수행. 
from sklearn.cluster import KMeans

kmeans = KMeans(n_clusters=2, max_iter=1000, random_state=0)
kmeans_labels = kmeans.fit_predict(X)
clusterDF['kmeans_cluster'] = kmeans_labels

visualize_cluster_plot(kmeans, clusterDF, 'kmeans_cluster', iscenter=True)

이와 같이 거리 기반 군집화로는 데이터가 특정한 형태로 지속해서 이어지는 부분을 찾아내기 어렵다.

GMM도 마찬가지로 제대로 군집화가 되지 않는다.

 

다음은 DBSCAN으로 군집화를 적용한다.

# DBSCAN으로 make_circles( ) 데이터 셋을 클러스터링 수행. 
from sklearn.cluster import DBSCAN

dbscan = DBSCAN(eps=0.2, min_samples=10, metric='euclidean')
dbscan_labels = dbscan.fit_predict(X)
clusterDF['dbscan_cluster'] = dbscan_labels

visualize_cluster_plot(dbscan, clusterDF, 'dbscan_cluster', iscenter=False)

DBSCAN으로 군집화를 적용해 원하는 방향으로 정확히 군집화가 됐다.

 


# 회고

군집화 끝! 실습은 다음 시간에 한다.

공부하다 찾은 또다른 머신러닝 분류 그림이다.

배운게 다 있지도 않고, 안 배운 알고리즘도 굉장히 많다.

여기서 더 추가될라나..