군집화

2024. 1. 5. 19:30책/머신러닝 완벽가이드

k-평균 알고리즘

  • 군집 중심점이라는 특정한 임의의 지점을 선택해 해당 중심에 가장 가까운 포인트들을 선택하는 군집화 기법

k-평균 동작 process

  1. 군집화의 기준이 되는 중심을 구성하는 군집화 개수만큼 임의의 위치에 가져다 놓음
  2. 각 데이터가 가장 가까운 곳에 위치한 중심점에 소속됨
  3. 소속이 결정되면 군집 중심정르 소속된 데이터의 평균 중심으로 이동
  4. 각 데이터는 기존에 속한 중심점보다 더 가까운 중심점이 있다면 해당 중심점으로 다시 소속을 변경
  5. 다시 중심을 소속된 데이터의 평균 중심으로 이동
  6. 중심점을 이동했는데 데이터의 중심점 소속 변경이 없으면 군집화를 종료. 그렇지 않다면 다시 4번 과정을 거쳐서 소속을 변경하고 이 과정을 반복

장점

  • 일반적인 군집화에서 가장 많이 활용되는 알고리즘
  • 쉽고 간결

단점

  • 거리 기반 알고리즘으로 속성의 개수가 매우 많을 경우 군집화 정확도가 떨어짐(이를 위해 pca로 차원 감소를 적용해야 할 수도 있음)
  • 반복 횟수가 많을 경우 수행 시간이 매우 느려짐
  • 맷 개의 군집을 선택해야 할지 가이드하기 어려움

하이퍼 파라미터 / 설명

n_clusters 군집화할 개수(군집 중심점의 개수)
init 초기에 군집 중심점의 좌표를 설정할 방식. 보통은 임의로 설정하지 않고 K-Means++ 방식으로 설정
max-iter 최대 반복 횟수(이 횟수 이전에 모든 데이터의 중심점 이동이 없으면 종료)

 

from sklearn.preprocessing import scale
from sklearn.datasets import load_iris
from sklearn.cluster import KMeans
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
%matplotlib inline

iris = load_iris()
# 보다 편리한 데이터 Handling을 위해 DataFrame으로 변환
irisDF = pd.DataFrame(data=iris.data, columns=['sepal_length','sepal_width','petal_length','petal_width'])
irisDF.head(3)

# 개정판 소스 코드 수정(2019.12.24)
kmeans = KMeans(n_clusters=3, init='k-means++', max_iter=300,random_state=0)
kmeans.fit(irisDF)

# irisDF['cluster']=kmeans.labels_ 개정 소스코드 변경(2019.12.24)
irisDF['target'] = iris.target
irisDF['cluster']=kmeans.labels_
iris_result = irisDF.groupby(['target','cluster'])['sepal_length'].count()
print(iris_result)

# iris 4개의 속성을 2차원 평면에 그리기 위해 PCA로 2개로 차원 축소
from sklearn.decomposition import PCA
pca = PCA(n_components=2)
pca_transformed = pca.fit_transform(iris.data)

irisDF['pca_x'] = pca_transformed[:,0]
irisDF['pca_y'] = pca_transformed[:,1]

# cluster 값이 0, 1, 2 인 경우마다 별도의 Index로 추출
marker0_ind = irisDF[irisDF['cluster']==0].index
marker1_ind = irisDF[irisDF['cluster']==1].index
marker2_ind = irisDF[irisDF['cluster']==2].index

# cluster값 0, 1, 2에 해당하는 Index로 각 cluster 레벨의 pca_x, pca_y 값 추출. o, s, ^ 로 marker 표시
plt.scatter(x=irisDF.loc[marker0_ind,'pca_x'], y=irisDF.loc[marker0_ind,'pca_y'], marker='o')
plt.scatter(x=irisDF.loc[marker1_ind,'pca_x'], y=irisDF.loc[marker1_ind,'pca_y'], marker='s')
plt.scatter(x=irisDF.loc[marker2_ind,'pca_x'], y=irisDF.loc[marker2_ind,'pca_y'], marker='^')

plt.xlabel('PCA 1')
plt.ylabel('PCA 2')
plt.title('3 Clusters Visualization by 2 PCA Components')
plt.show()

 

군집화 알고리즘 테스트를 위한 데이터 생성

  • 사이킷런의 데이터 생성기 : 여러 개의 클래스에 해당하는 데이터 세트를 만드는데, 하나의 클래스에 여러 개의 군집이 분포될 수 있게 데이터를 생성

make_blobs()

  • 개별 군집의 중심점과 표준 편차 제어 기능이 추가됨
  • 피처 데이터 세트, 타깃 데이터 세트가 투플로 전환

파라미터 설명

n_samples 생성할 총 데이터의 개수 (디폴트 = 100)
n_features 데이터의 피처 개수
centers init값
cluster_std 생성될 군집 데이터의 표준편차
import numpy as np
import matplotlib.pyplot as plt
from sklearn.cluster import KMeans
from sklearn.datasets import make_blobs
%matplotlib inline

# 테스트 데이터 생성
X, y = make_blobs(n_samples=200, n_features=2, centers=3, cluster_std=0.8, random_state=0)
print(X.shape, y.shape)

# y target 값의 분포를 확인
unique, counts = np.unique(y, return_counts=True)
print(unique,counts)

> (200, 2) (200,)
> [0 1 2] [67 67 66]

# DataFrame에 적용
import pandas as pd
clusterDF = pd.DataFrame(data=X, columns=['ftr1', 'ftr2'])
clusterDF['target'] = y

target_list = np.unique(y)
# 각 target별 scatter plot 의 marker 값들.
markers=['o', 's', '^', 'P','D','H','x']
# 3개의 cluster 영역으로 구분한 데이터 셋을 생성했으므로 target_list는 [0,1,2]
# target==0, target==1, target==2 로 scatter plot을 marker별로 생성.
for target in target_list:
    target_cluster = clusterDF[clusterDF['target']==target]
    plt.scatter(x=target_cluster['ftr1'], y=target_cluster['ftr2'], edgecolor='k', marker=markers[target] )

plt.show()

 

KMeans 객체를 이용하여 X 데이터를 K-Means 클러스터링 수행 후, 시각화

# KMeans 객체를 이용하여 X 데이터를 K-Means 클러스터링 수행
kmeans = KMeans(n_clusters=3, init='k-means++', max_iter=200, random_state=0)
cluster_labels = kmeans.fit_predict(X)
clusterDF['kmeans_label']  = cluster_labels

#cluster_centers_ 는 개별 클러스터의 중심 위치 좌표 시각화를 위해 추출
centers = kmeans.cluster_centers_
unique_labels = np.unique(cluster_labels)
markers=['o', 's', '^', 'P','D','H','x']

# 군집된 label 유형별로 iteration 하면서 marker 별로 scatter plot 수행.
for label in unique_labels:
    label_cluster = clusterDF[clusterDF['kmeans_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='white',
                alpha=0.9, edgecolor='k', 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()

print(clusterDF.groupby('target')['kmeans_label'].value_counts())

> target  kmeans_label
> 0       0               66
>         1                1
> 1       2               67
> 2       1               65
>         2                1

 

 

군집 평가

실루엣 분석 : 각 군집 간의 거리가 얼마나 효율적으로 분리되어 있는지 나타냄

  • 다른 군집과의 거리는 떨어져 있고 동일 군집끼리의 데이터는 서로 가깝게 잘 뭉쳐 있는 경우 효율적으로 분리 되었다 말할 수 있음
  • 실루엣 계수(silhouette coefficient) : 개별 데이터가 가지는 군집화 지표
  • 실루엣 계수는 -1~1 사이의 값을 가짐
    • 1에 가까울 수록, 근처의 군집과 더 멀리 떨어져 있다는 것
    • 0에 가까울 수록, 근처의 군집과 가까워진다는 것
      • 값은 아예 다른 군집에 데이터 포인트가 할당되었다는 것
from sklearn.preprocessing import scale
from sklearn.datasets import load_iris
from sklearn.cluster import KMeans
# 실루엣 분석 metric 값을 구하기 위한 API 추가
from sklearn.metrics import silhouette_samples, silhouette_score
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd

%matplotlib inline

iris = load_iris()
feature_names = ['sepal_length','sepal_width','petal_length','petal_width']
irisDF = pd.DataFrame(data=iris.data, columns=feature_names)
kmeans = KMeans(n_clusters=3, init='k-means++', max_iter=300,random_state=0).fit(irisDF)

irisDF['cluster'] = kmeans.labels_

# iris 의 모든 개별 데이터에 실루엣 계수값을 구함.
score_samples = silhouette_samples(iris.data, irisDF['cluster'])
print('silhouette_samples( ) return 값의 shape' , score_samples.shape)
print(np.mean(silhouette_samples(iris.data, irisDF['cluster'])))
print(silhouette_score(iris.data, irisDF['cluster']))

> silhouette_samples( ) return 값의 shape (150,)
> 0.5528190123564095
> 0.5528190123564095

# irisDF에 실루엣 계수 컬럼 추가
irisDF['silhouette_coeff'] = score_samples

# 모든 데이터의 평균 실루엣 계수값을 구함.
average_score = silhouette_score(iris.data, irisDF['cluster'])
print('붓꽃 데이터셋 Silhouette Analysis Score:{0:.3f}'.format(average_score))

> 붓꽃 데이터셋 Silhouette Analysis Score:0.553

# 군집별 평균 실루엣 계수
print(irisDF.groupby('cluster')['silhouette_coeff'].mean())

> cluster
> 0    0.417320
> 1    0.798140
> 2    0.451105
> Name: silhouette_coeff, dtype: float64

군집별 평균 실루엣 계수의 시각화를 통한 군집 개수 최적화 방법

  • 전체 데이터의 평균 실루엣 계수 값이 높다고 해서, 반드시 최적의 군집 개수로 군집화가 잘 됐다고 볼 수 없음
    • 특정 군집만 실루엣 계수가 엄청 높고 나머지 군집들은 낮아도, 평균 실루엣 계수 자체는 높게 나올 수 있기 때문
  • 좋은 군집의 조건
    1. 전체 실루엣 계수의 평균값(silhouette_score())은 0~1 사이의 값을 가지며, 1에 가까움
    2. 하지만 전체 실루엣 계수의 평균값과 더불어, 개별 군집의 평균값의 편차가 크지 않아야 함
    3. 즉, 개별 군집의 실루엣 계수 평균값이 전체 실루엣 계수 평균값에서 크게 벗어나지 않는 것이 중요
### 여러개의 클러스터링 갯수를 List로 입력 받아 각각의 실루엣 계수를 면적으로 시각화한 함수 작성
def visualize_silhouette(cluster_lists, X_features):

    from sklearn.datasets import make_blobs
    from sklearn.cluster import KMeans
    from sklearn.metrics import silhouette_samples, silhouette_score

    import matplotlib.pyplot as plt
    import matplotlib.cm as cm
    import math

    # 입력값으로 클러스터링 갯수들을 리스트로 받아서, 각 갯수별로 클러스터링을 적용하고 실루엣 개수를 구함
    n_cols = len(cluster_lists)

    # plt.subplots()으로 리스트에 기재된 클러스터링 수만큼의 sub figures를 가지는 axs 생성
    fig, axs = plt.subplots(figsize=(4*n_cols, 10), nrows=2, ncols=n_cols)

    # 리스트에 기재된 클러스터링 갯수들을 차례로 iteration 수행하면서 실루엣 개수 시각화
    for ind, n_cluster in enumerate(cluster_lists):

        # KMeans 클러스터링 수행하고, 실루엣 스코어와 개별 데이터의 실루엣 값 계산.
        clusterer = KMeans(n_clusters = n_cluster, max_iter=500, random_state=0)
        cluster_labels = clusterer.fit_predict(X_features)
        centers = clusterer.cluster_centers_

        sil_avg = silhouette_score(X_features, cluster_labels)
        sil_values = silhouette_samples(X_features, cluster_labels)

        y_lower = 10
        axs[0,ind].set_title('Number of Cluster : '+ str(n_cluster)+'\\n' \\
                          'Silhouette Score :' + str(round(sil_avg,3)) )
        axs[0,ind].set_xlabel("The silhouette coefficient values")
        axs[0,ind].set_ylabel("Cluster label")
        axs[0,ind].set_xlim([-0.1, 1])
        axs[0,ind].set_ylim([0, len(X_features) + (n_cluster + 1) * 10])
        axs[0,ind].set_yticks([])  # Clear the yaxis labels / ticks
        axs[0,ind].set_xticks([0, 0.2, 0.4, 0.6, 0.8, 1])

        # 클러스터링 갯수별로 fill_betweenx( )형태의 막대 그래프 표현.
        for i in range(n_cluster):
            ith_cluster_sil_values = sil_values[cluster_labels==i]
            ith_cluster_sil_values.sort()

            size_cluster_i = ith_cluster_sil_values.shape[0]
            y_upper = y_lower + size_cluster_i

            color = cm.nipy_spectral(float(i) / n_cluster)
            axs[0,ind].fill_betweenx(np.arange(y_lower, y_upper), 0, ith_cluster_sil_values, \\
                                facecolor=color, edgecolor=color, alpha=0.7)
            axs[0,ind].text(-0.05, y_lower + 0.5 * size_cluster_i, str(i))
            y_lower = y_upper + 10

            # 클러스터링된 데이터 시각화
            axs[1,ind].scatter(X_features[:, 0], X_features[:, 1], marker='.', s=30, lw=0, alpha=0.7, \\
                c=cluster_labels)
            axs[1,ind].set_title("Clustered data")
            axs[1,ind].set_xlabel("Feature space for the 1st feature")
            axs[1,ind].set_ylabel("Feature space for the 2nd feature")

        # 군집별 중심 위치 좌표 시각화
        unique_labels = np.unique(cluster_labels)
        for label in unique_labels:
            center_x_y = centers[label]
            axs[1,ind].scatter(x=center_x_y[0], y=center_x_y[1], s=70, color='k', edgecolor='k',
                        marker='$%d$' % label)

        axs[0,ind].axvline(x=sil_avg, color="red", linestyle="--")

# make_blobs 을 통해 clustering 을 위한 4개의 클러스터 중심의 500개 2차원 데이터 셋 생성
from sklearn.datasets import make_blobs
X, y = make_blobs(n_samples=500, n_features=2, centers=4, cluster_std=1, \\
                  center_box=(-10.0, 10.0), shuffle=True, random_state=1)

# cluster 개수를 2개, 3개, 4개, 5개 일때의 클러스터별 실루엣 계수 평균값을 시각화
visualize_silhouette([ 2, 3, 4, 5], X)

 

#iris 데이터로 실루엣 시각화 분석
from sklearn.datasets import load_iris

iris=load_iris()
visualize_silhouette([ 2, 3, 4,5 ], iris.data)

 

 

단점

  • 직관적으로 이해하기 쉽지만 각 데이터별로 다른 데이터와의 거리를 반복적으로 계산해야 하므로, 데이터 양이 늘어나면 수행시간이 크게 늘어남
  • 메모리 부족 등의 에러가 발생하기 쉬움 (이 경우 군집별로 임의의 데이터를 샘플링 해 실루엣 계수를 평가하는 방안을 고민할 것)

평균 이동

K-평균과 평균 이동의 공통점

  • 군집의 중심을 지속적으로 움직이면서 군집화를 수행

K-평균과 평균 이동의 차이점

  • K-평균은 중심에 소속된 데이터의 평균 거리 중심으로 이동
  • 평균 이동은 데이터가 모여있는 밀도가 가장 높은 곳으로 이동

평균 이동 특징

  • 데이터의 분포도를 이용해 군집 중심점을 찾음
    • 군집 중심점은 데이터 포인트가 모여있는 곳이라는 생각에서 착안
    • 이를 위해 확률 밀도 함수를 이용
    • 확률 밀도 함수가 피크인 점(가장 집중적으로 데이터가 모여 있을)을 군집 중심점으로 선정
    • 주어진 모델의 확률 밀도 함수를 찾기 위해서 KDE(Kernel Density Estimation)를 이용
    • 주변 데이터와의 거리 값을 KDE 함수 값으로 입력한 뒤, 그 반환 값을 현재 위치에서 업데이트하면서 이동하는 방식

KDE(Kernel Density Estimation)

  • 커널 함수를 통해 어떤 변수의 확률 밀도 함수를 추정하는 대표적인 방법
  • 개별 데이터 각각에, 커널 함수를 적용한 값을 모두 더한 뒤 데이터 건수로 나눠 확률 밀도 함수를 추정
  • 확률 밀도 함수 PDF(Probability Density Function)
    • 확률 변수의 분포를 나타내는 함수 (정규 분포, 감마 분포, t-분포 등)
    • 확률 밀도 함수를 알면 특정 변수가 어떤 값을 갖게 될지에 대한 확률을 알게 되므로, 이를 통해 변수의 특성, 확률 분포 등 변수의 많은 요소를 알 수 있음
  • 커널 함수의 예시 : 가우시안 커널 함수 적용

 

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)

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

> cluster labels 유형: [0 1 2 3 4 5]

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

> cluster labels 유형: [0 1 2]

 

대역폭 h : KDE 형태를 부드럽거나 뾰족한 형태로 평활화(smoothing)하는데 적용

  • 작은h 값: 좁고 뾰족한 KDE를 가지며 과적합 되기 쉬움 - 많은 수의 군집 중심점을 가짐
  • 큰h 값: 과도하게 평활화된 KDE를 가지며 과소적합 되기 쉬움 - 적은 수의 군집 중심점을 가짐

 

  • bandwidth (=KDE의 h) 값을 작게 할수록 군집 개수가 많아짐
  • estimate_bandwidth(X) : 최적의 대역폭 h 찾아줌. 파라미터로 피처 데이터 세트(X) 입력
from sklearn.cluster import estimate_bandwidth

# estimate_bandwidth()로 최적의 bandwidth 계산
bandwidth = estimate_bandwidth(X)
print('bandwidth 값:', round(bandwidth,3))

> bandwidth 값: 1.816

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))

> cluster labels 유형: [0 1 2]

 

  • 커널 함수의 예시 : 가우시안 커널 함수 적용
    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)
    
    meanshift= MeanShift(bandwidth=0.8)
    cluster_labels = meanshift.fit_predict(X)
    print('cluster labels 유형:', np.unique(cluster_labels))
    
    > cluster labels 유형: [0 1 2 3 4 5]
    
    meanshift= MeanShift(bandwidth=1) # bandwidth 변경
    cluster_labels = meanshift.fit_predict(X)
    print('cluster labels 유형:', np.unique(cluster_labels))
    
    > cluster labels 유형: [0 1 2]
    
    대역폭 h : KDE 형태를 부드럽거나 뾰족한 형태로 평활화(smoothing)하는데 적용
    • 작은h 값: 좁고 뾰족한 KDE를 가지며 과적합 되기 쉬움 - 많은 수의 군집 중심점을 가짐
    • 큰h 값: 과도하게 평활화된 KDE를 가지며 과소적합 되기 쉬움 - 적은 수의 군집 중심점을 가짐

  • bandwidth (=KDE의 h) 값을 작게 할수록 군집 개수가 많아짐
  • estimate_bandwidth(X) : 최적의 대역폭 h 찾아줌. 파라미터로 피처 데이터 세트(X) 입력
from sklearn.cluster import estimate_bandwidth

# estimate_bandwidth()로 최적의 bandwidth 계산
bandwidth = estimate_bandwidth(X)
print('bandwidth 값:', round(bandwidth,3))

> bandwidth 값: 1.816

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))

> cluster labels 유형: [0 1 2]
  • 평균 이동의 장점
    • 데이터 세트의 형태를 특정 형태로 가정하거나, 특정 분포 기반의 모델로 가정하지 않기 때문에 유연한 군집화 가능
    • 이상치의 영향력도 크지 않으며, 미리 군집의 개수를 정하지 않아도 됨
  • 평균 이동의 단점
    • 수행 시간이 오래 걸리고, bandwidth의 크기에 따른 군집화 영향도가 큼
  • 활용
    • 컴퓨터 비전 영역에서 많이 사용
    • 이미지나 영상 데이터에서, 특정 개체를 구분하거나 움직임을 추적하는데 뛰어난 역할

GMM(Gaussian Mixture Model)

  • 군집화를 적용하고자 하는 데이터가, 여러 개의 가우시안 분포를 가진 데이터 집합들이 섞여서 생성된 것이라는 가정하에, 군집화를 수행하는 방식
  • 데이터를 여러 개의 가우시안 분포가 섞인 것으로 간주
  • 이러한 서로 다른 정규 분포에 기반해 군집화를 수행하는 것이 GMM 군집화 방식
  • ex) 1000개의 데이터 세트가 있다면 이를 구성하는 여러 개의 정규 분포 곡선을 추출하고, 개별 데이터가 이 중 어떤 정규 분포에 속하는지 결정하는 방식

이와 같은 방식을 GMM에서는 모수 추정이라고 하는데, 모수 추정은 대표적으로 2가지를 추정하는 것

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

→ 이러한 모수 추정을 위해 EM(expectiation and maximization)방법 적용

GMM(Gaussian Mixture Model)

  • 군집화를 적용하고자 하는 데이터가, 여러 개의 가우시안 분포를 가진 데이터 집합들이 섞여서 생성된 것이라는 가정하에, 군집화를 수행하는 방식
  • 데이터를 여러 개의 가우시안 분포가 섞인 것으로 간주
  • 이러한 서로 다른 정규 분포에 기반해 군집화를 수행하는 것이 GMM 군집화 방식
  • ex) 1000개의 데이터 세트가 있다면 이를 구성하는 여러 개의 정규 분포 곡선을 추출하고, 개별 데이터가 이 중 어떤 정규 분포에 속하는지 결정하는 방식

이와 같은 방식을 GMM에서는 모수 추정이라고 하는데, 모수 추정은 대표적으로 2가지를 추정하는 것

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

→ 이러한 모수 추정을 위해 EM(expectiation and maximization)방법 적용

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  gmm_cluster
> 0       0              50
> 1       2              45
>         1               5
> 2       1              50
> Name: gmm_cluster, dtype: int64
  • n_components: 모델의 총 개수. 군집의 개수를 정하는데 중요한 역할 수행
  • fit(피처 데이터 세트), predict(피처 데이터 세트)를 수행해 군집을 결정
# 붓꽃 데이터 세트의 K-평균 군집화를 수행한 결과

kmeans = KMeans(n_clusters=3, init='k-means++', max_iter=300,random_state=0).fit(iris.data)
kmeans_cluster_labels = kmeans.predict(iris.data)
irisDF['kmeans_cluster'] = kmeans_cluster_labels
iris_result = irisDF.groupby(['target'])['kmeans_cluster'].value_counts()
print(iris_result)

> target  kmeans_cluster
> 0       1              50
> 1       0              48
>					2								2
>	2       2              36
>         0              14
> Name: kmeans_cluster, dtype: int64
  • kmeans 군집화는 개별 군집의 중심에서 원형의 범위로 데이터를 군집화 하지만, 데이터가 길쭉한 타원형으로 늘어선 경우에 군집화를 잘 수행하지 못함
#**GMM군집화와 K-Means군집화를 비교하기 위해 타원형으로 늘어선 임의의 데이터 세트를 생성**
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
# 생성된 데이터 셋을 target 별로 다른 marker 로 표시하여 시각화 함. 
visualize_cluster_plot(None, clusterDF, 'target', iscenter=False)

 

#K-Means 군집화 수행
# 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)

 

 

#GMM 군집화 수행
# 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)

 

 

#GMM과 K-Means 군집화 결과 비교
print('### KMeans Clustering ###')
print(clusterDF.groupby('target')['kmeans_label'].value_counts())
print('\\n### Gaussian Mixture Clustering ###')
print(clusterDF.groupby('target')['gmm_label'].value_counts())

[output[
### KMeans Clustering ###
target  kmeans_label
0       2                73
        0                27
1       1               100
2       0                86
        2                14
Name: kmeans_label, dtype: int64

### Gaussian Mixture Clustering ###
target  gmm_label
0       2            100
1       1            100
2       0            100
Name: gmm_label, dtype: int64

 

DBSCAN(밀도 기반 군집화)

  • 입실론 주변 영역의 최소 데이터 개수를 포함하는 밀도 기준을 충족시키는 데이터인, 핵심 포인트를 연결하면서 군집화를 구성하는 방식
  • 데이터의 분포가 기하학적으로 복잡한 데이터 세트에도 효과적으로 군집화 가능

중요한 파라미터

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

입실론 주변 영역 내에 포함되는 최소 데이터 개수를 충족 시키는가 아닌가에 따라 데이터 포인트를 정의

  • 핵심 포인트 : 주변 영역 내에 최소 데이터 개수 이상의 타 데이터를 가지고 있을 경우
  • 이웃 포인트 : 주변 영역 내에 위치한 타 데이터
  • 경계 포인트 : 주변 영역 내에 최소 데이터 개수 이상의 이웃 포인트를 가지고 있지 않지만 핵심 포인트를 이웃 포인트로 가지고 있는 데이터
  • 잡음 포인트 : 최소 데이터 개수 이상의 이웃 포인트를 가지고 있지 않으며, 핵심 포인트도 이웃 포인트로 가지고 있지 않는 데이터

DBSCAN의 주요한 초기화 파라미터

eps : 입실론 주변 영역의 반경을 의미

min_samples : 핵심 포인트가 되기 위해 입실론 주변 영역 내에 포함돼야 할 데이터의 최소 개수 의미

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)

> target  dbscan_cluster
> 0        0                49
>         -1                 1
> 1        1                46
>         -1                 4
> 2        1                42
>         -1                 8
> Name: dbscan_cluster, dtype: int64

# 군집 레이블이 -1인 것은 노이즈에 속하는 군집을 의미

 

  • 군집 레이블이 -1인 것은 노이즈에 속하는 군집을 의미
  • Target 유형이 3가지 인데, 군집이 2개가 됐다고 군집화 효율이 떨어진다는 의미는 아님
#PCA 2개 컴포넌트로 기존 feature들을 차원 축소 후 시각화

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)

 

★ 표시는 모두 노이즈 값

 

from sklearn.cluster import DBSCAN

dbscan = DBSCAN(eps=0.8, 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)

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

[output]
target  dbscan_cluster
0        0                50
1        1                50
2        1                47
        -1                 3
Name: dbscan_cluster, dtype: int64

 

#min_samples의 크기를 증가 후 노이즈 확인

dbscan = DBSCAN(eps=0.6, min_samples=16, 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)
visualize_cluster_plot(dbscan, irisDF, 'dbscan_cluster', iscenter=False)

[output]
target  dbscan_cluster
0        0                48
        -1                 2
1        1                44
        -1                 6
2        1                36
        -1                14
Name: dbscan_cluster, dtype: int64

 

  • eps 값을 크게 하면, 반경이 커져 포함하는 데이터가 많아지므로 노이즈 데이터 개수가 작아짐
  • min_samples를 크게 하면, 주어진 반경 내에서 더 많은 데이터를 포함시켜야 하므로 노이즈 데이터 개수가 커짐

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

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)

 

# 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으로 make_circles( ) 데이터 셋을 클러스터링 수행. 
from sklearn.mixture import GaussianMixture

gmm = GaussianMixture(n_components=2, random_state=0)
gmm_label = gmm.fit(X).predict(X)
clusterDF['gmm_cluster'] = gmm_label

visualize_cluster_plot(gmm, clusterDF, 'gmm_cluster', iscenter=False)

 

GMM은 앞 절의 일렬로 늘어선 데이터 세트에서는 효과적으로 군집화 적용이 가능했으나 내부와 외부의 원형이 구성된 더 복잡한 형태의 데이터 세트에서는 군집화가 원하는 방향으로 되지 않았음

# 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은 원하는 방향으로 정확히 군집화가 됨

  • 결론적으로, K 평균, 평균 이동, GMM으로는 기하학적으로 복잡한 데이터 세트를 효과적으로 군집화하기는 어렵다는 것을 알 수 있음
  • 이에 반해, DBSCAN는 데이터의 분포가 기하학적으로 복잡한 데이터 세트에도 효과적으로 군집화가 가능함

' > 머신러닝 완벽가이드' 카테고리의 다른 글

분류  (0) 2024.01.05
회귀  (1) 2024.01.05
차원축소  (1) 2023.12.30
224~349p  (0) 2023.12.16
XGBoost 개념  (0) 2023.12.05