[ML] 군집화 - K-Means Clustering

강주형·2022년 7월 19일
0

군집화 개요

Clustering

  1. 데이터 포인트들을 별개의 군집으로 그룹화 하는 것을 의미
  2. 유사성이 높은 데이터들을 동일한 그룹으로 분류하고 서로 다른 군집들이 상이하게 그룹화

군집화 활용 분야

  • 고객, 마켓, 브랜드, 사회 경제 활동 세분화
  • 이미지 검출, 세분화, 트랙킹
  • 이상 검출

군집화 알고리즘 종류

  • K-Means
  • Mean Shift
  • Gaussian Mixture Model
  • DBSCAN

K-Means Clustering 개요

군집 중심점 (Centroid) 기반 클러스터링


(a) 2개의 군집 중심점을 설정
(b) 각 데이터를 가장 가까운 중심점에 소속
(c) 중심점에 할당된 데이터들의 평균 중심으로 중심점 이동
(d) 각 데이터들은 이동된 중심점 기준으로 가장 가까운 중심점에 소속
(e) 다시 중심점에 할당된 데이터들의 평균 중심으로 중심점 이동
(f), (g), (h), (i) 반복
-> 중심점을 이동하였지만 데이터들의 중심점 소속 변경이 없으면 군집화 완료!

K-Means 장점

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

K-Means 단점

  • 거리 기반 알고리즘으로 속성의 개수가 매우 많을 경우 군집화 정확도가 떨어짐
    -> 이를 위해 PCA로 차원 축소를 할 수 있음
  • 반복 횟수가 많을수록 수행 시간 증가
  • 이상치에 취약함

K-Means Clustering 실습

scikit-learn에서 sklearn.cluster.KMeans 클래스 제공

주요 파라미터

  • n_cluster: 군집화할 개수 (군집 중심점의 개수)
  • max_iter: 최대 반복 횟수
    -> 이 횟수 전에 모든 데이터의 중심점 이동이 없으면 종료

주요 속성

  • labels_: 각 데이터 포인트가 속한 군집 중심점 레이블
  • cluster_centers_: 각 군집 중심점 좌표 (군집 개수, Feature 수)

iris 데이터를 이용해서 실습 진행
우선 데이터프레임으로 만들자

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

iris = load_iris()
print('target name:', iris.target_names)
irisDF = pd.DataFrame(data=iris.data, columns=['sepal_length','sepal_width','petal_length','petal_width'])
irisDF.head(3)

K-Means Clustering 진행
군집 개수는 3개로 설정
init='k-means++은 centroid 초기화를 k-means++ 기법을 사용하겠다는 뜻 (자세한 설명은 생략)

kmeans = KMeans(n_clusters=3, init='k-means++', max_iter=300,random_state=0)
kmeans.fit(irisDF)

군집화 결과를 확인해보자

kmeans.fit_predict(irisDF)
array([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
       1, 1, 1, 1, 1, 1, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 2, 2, 2, 2, 0, 2, 2, 2,
       2, 2, 2, 0, 0, 2, 2, 2, 2, 0, 2, 0, 2, 0, 2, 2, 0, 0, 2, 2, 2, 2,
       2, 0, 2, 2, 2, 2, 0, 2, 2, 2, 0, 2, 2, 2, 0, 2, 2, 0])

fit_transform()을 하면 변형이라기 보다는
각 데이터의 좌표값을 반환함

kmeans.fit_transform(irisDF)
array([[3.41925061, 0.14135063, 5.0595416 ],
      [3.39857426, 0.44763825, 5.11494335],
      [3.56935666, 0.4171091 , 5.27935534],
      [3.42240962, 0.52533799, 5.15358977],
      [3.46726403, 0.18862662, 5.10433388],
      [3.14673162, 0.67703767, 4.68148797],
...
      [1.94554509, 5.09187392, 0.50939919],
      [1.44957743, 4.60916261, 0.61173881],
      [0.89747884, 4.21767471, 1.10072376],
      [1.17993324, 4.41184542, 0.65334214],
      [1.50889317, 4.59925864, 0.83572418],
      [0.83452741, 4.0782815 , 1.1805499 ]])

군집화 결과를 cluster칼럼으로 추가해서 원래의 traget과 비교해보자

irisDF['target'] = iris.target
irisDF['cluster']=kmeans.labels_
irisDF.head(10)

아마 target=0cluster=1에 대응되는 것 같다.
groupby로 좀 더 구체적으로 확인하자

irisDF['target'] = iris.target
irisDF['cluster']=kmeans.labels_

irisDF.groupby(['target','cluster']).count()

첫번째 군집은 완벽하고, 두 번째도 거의 완벽하다, 세 번째는 조금 헷갈린 듯

왜 저런 결과가 나왔는지, 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]

PCA 두 축으로 시각화 진행

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

아무래도 파란색과 초록색 군집이 서로 붙어있어서 조금의 오차가 있던 것으로 추정됨

이 시각화는 한 줄로도 표현 가능

plt.scatter(x=irisDF.loc[:, 'pca_x'], y=irisDF.loc[:, 'pca_y'], c=irisDF['cluster']) # c: color

iris 데이터는 그만하고, 랜덤 데이터를 생성해서 진행해보자
scikit-learn의 make_blobs를 이용하면 군집화를 위한 랜덤 데이터 샘플링이 된다.
n_samples: 샘플의 개수
n_features: 데이터의 feature의 개수 (시각화하려면 2로 설정 (x,y)(x, y))
centers: 군집의 개수 (ndarray로 넣으면 개별 군집 중심점의 좌표)
cluser_std: 생성될 군집 내 데이터의 표준편차 (리스트로 군집별 설정 가능)

import numpy as np
import matplotlib.pyplot as plt
from sklearn.cluster import KMeans
from sklearn.datasets import make_blobs

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]

데이터프레임으로 만들고 진행하자

import pandas as pd

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

Clustering 수행 전에 어떻게 데이터가 생성됐는지 각 군집별로 구분해서 시각화해보자 (랜덤 데이터지만 어느정도 군집 특성을 고려해서 생성된 것임!)

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

K-Means Clustering을 수행하고 각 데이터와 centroid까지 시각화

# 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]
    plt.scatter(x=label_cluster['ftr1'], y=label_cluster['ftr2'], edgecolor='k', 
                marker=markers[label] )
    
    center_x_y = centers[label]
    
    # 군집별 중심 위치 좌표 시각화 
    
    plt.scatter(x=center_x_y[0], y=center_x_y[1], s=220, color='white', # centroid 배경 
                alpha=0.9, edgecolor='k', marker=markers[label])
    plt.scatter(x=center_x_y[0], y=center_x_y[1], s=80, color='k', edgecolor='k', # centroid 숫자
                marker='$%d$' % label)

plt.show()

위에 그래프와 각각 비교해보자
거의 동일하게 잘 군집화 됨
groupby로 어느정도 맞았나 확인하고 마무리하자

print(clusterDF.groupby('target')['kmeans_label'].value_counts())
target  kmeans_label
0       0               66
        1                1
1       2               67
2       1               65
        2                1
Name: kmeans_label, dtype: int64

2개만 빼면 랜덤 샘플링이 의도한 군집과 동일하다.

이건 make_blobs가 군집화를 가정하고 초기 군집을 저장해놓은 거라 비교가 가능한 것
-> 군집화는 원래 비지도학습이라 Label(Target)이 없는 게 정상임!

profile
Statistics & Data Science

0개의 댓글