[Python] 머신러닝 완벽가이드 - 07. 군집화[KMeans]

Updated:

파이썬 머신러닝 완벽가이드 교재를 토대로 공부한 내용입니다.

실습과정에서 필요에 따라 내용의 누락 및 추가, 수정사항이 있습니다.


기본 세팅

import numpy as np
import pandas as pd

import matplotlib as mpl
import matplotlib.pyplot as plt
import seaborn as sns

import warnings
%matplotlib inline
%config InlineBackend.figure_format = 'retina'

mpl.rc('font', family='NanumGothic') # 폰트 설정
mpl.rc('axes', unicode_minus=False) # 유니코드에서 음수 부호 설정

# 차트 스타일 설정
sns.set(font="NanumGothic", rc={"axes.unicode_minus":False}, style='darkgrid')
plt.rc("figure", figsize=(10,8))

warnings.filterwarnings("ignore")

1. K-Means

수행과정

  1. K-Means는 군집 중심점(centroid)라는 특정한 임의의 지점을 선택해 해당 중심에 가장 가까운 포인트들을 선택한다.

  2. 군집 중심점은 선택된 포인트의 평균 지점으로 이동하고 이동된 중심점에서 다시 가까운 포인트를 선택한다.

  3. 이 과정을 반복하다가 더이상 중심점의 이동이 없으면 반복을 멈추고 해당 중심점에 속하는 포인트들을 군집화 한다.

장점

  • 알고리즘이 쉽고 간결하다.

  • 비지도학습으로 사전 라벨 데이터가 필요없다.

단점

  • 거리 기반의 알고리즘이므로 속성(피처)의 개수가 많으면 군집화 정확도가 떨어진다.

  • 반복 횟수가 많아지면 수행 시간이 매우 느려진다.

  • 몇 개의 군집 즉, K 설정에 대한 가이드가 어렵다.

1.1 IRIS 데이터

from sklearn.datasets import load_iris

# target 변수를 제외한 데이터 프레임
iris = load_iris()
iris_df = pd.DataFrame(data=iris.data, columns=['sepal_length','sepal_width','petal_length','petal_width'])

iris_df.head(3)
sepal_length sepal_width petal_length petal_width
0 5.1 3.5 1.4 0.2
1 4.9 3.0 1.4 0.2
2 4.7 3.2 1.3 0.2
from sklearn.cluster import KMeans

kmeans = KMeans(n_clusters=3, init="k-means++", max_iter=300, random_state=0)
kmeans.fit(iris_df)
KMeans(n_clusters=3, random_state=0)
  • sklearn.clusterKMeans()로 K-Means를 수행할 수 있다.

  • n_clusters는 군집화할 갯수로서 군집 중심점의 개수를 의미한다.

  • init은 초기에 군집 중심점 좌표 설정 방식으로 보통은 k-means++로 설정한다.

  • max_iter는 최대 반복 횟수로 그 전에 모든 데이터 중심점 이동이 없으면 종료한다.

kmeans.labels_
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, 2, 2, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
       2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
       2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 2, 0, 0, 0, 0, 2, 0, 0, 0,
       0, 0, 0, 2, 2, 0, 0, 0, 0, 2, 0, 2, 0, 2, 0, 0, 2, 2, 0, 0, 0, 0,
       0, 2, 0, 0, 0, 0, 2, 0, 0, 0, 2, 0, 0, 0, 2, 0, 0, 2])
  • labels_ 속성은 각 데이터 포인트가 속한 군집 중심점 레이블이다.

  • 이 외에도 cluster_centers_ 속성은 각 군집 중심점 좌표를 가지고 있다.

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

iris_result = iris_df.groupby(['target','cluster']).size()
iris_result
target  cluster
0       1          50
1       0           2
        2          48
2       0          36
        2          14
dtype: int64
  • target이 0인 데이터는 모두 1번 군집으로 잘 분류되었다.

  • target이 1인 데이터는 2개만 0번 군집으로, 나머지는 2번 군집으로 분류되었다.

  • target이 2인 데이터는 36개가 0번 군집으로, 14개가 2번 군집으로 분류되었다.

from sklearn.decomposition import PCA

# PCA 차원축소
pca = PCA(n_components=2)
pca_transformed = pca.fit_transform(iris.data)

iris_df["pca_x"] = pca_transformed[:,0]
iris_df["pca_y"] = pca_transformed[:,1]

iris_df.head(3)
sepal_length sepal_width petal_length petal_width target cluster pca_x pca_y
0 5.1 3.5 1.4 0.2 0 1 -2.684126 0.319397
1 4.9 3.0 1.4 0.2 0 1 -2.714142 -0.177001
2 4.7 3.2 1.3 0.2 0 1 -2.888991 -0.144949
  • PCA를 이용해 2개의 컴포넌트를 생성 후 데이터에 추가하였다.

  • 이를 이용해서 군집화를 시각화 해보자.

sns.scatterplot(x="pca_x", y="pca_y", data=iris_df, 
                hue= "cluster", style = "cluster")

plt.title("3 Clusters Visualization by 2 PCA Components")
plt.show()

png

  • Cluster 1은 명확하게 잘 분리되어 있다.

  • Cluster 0,2는 대부분 잘 분리되어있지만 일부가 완전히 분리되진 않았다.

1.2 군집화 가상데이터

사이킷런 패키지에는 군집화용 데이터 생성이 가능한 다양한 API가 있다.

  • make_blobs(): 대표적인 군집화 데이터 생성기로 개별 군집의 중심점과 표준 편차 제어기능이 있다.

  • make_classfication(): 역시 대표적인 군집화 데이터 생성기로 노이즈를 추가할 수 있다.

  • 이외에도 make_circle(), make_moons()은 중심 기반 군집화로 해결하기 어려운 데이터를 생성한다.

from sklearn.datasets import make_blobs

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

# target 값 분포
unique, counts = np.unique(y, return_counts=True)
print(f"군집종류: {unique}, 각 군집별 갯수: {counts}")
(200, 2) (200,)
군집종류: [0 1 2], 각 군집별 갯수: [67 67 66]
  • make_blobs()를 이용해 가상데이터를 생성하였다.

  • centers는 군집의 갯수로서 만약 ndarray 형태로 입력하면 개별 군집 중심점의 좌표로 인식한다.

  • cluster_std는 생성될 군집 데이터의 표준편차를 의미하며 각 군집별로 설정 가능하다.

# 데이터 프레임 생성
cluster_df = pd.DataFrame(X, columns=["ftr1","ftr2"])
cluster_df["target"] = y

# target 값 종류
target_list = np.unique(y)

# 가상 데이터 시각화
markers=['o', 's', '^']

for target in target_list:
    target_cluster = cluster_df[cluster_df['target'] == target]
    plt.scatter(x=target_cluster['ftr1'], y=target_cluster['ftr2'], 
                edgecolor='k', marker=markers[target] )
    
plt.show()

png

  • 가상 데이터를 데이터 프레임으로 만들고 시각화 해보았다.

  • 이 데이터를 이용해서 K-Means를 수행해보자.

# K-Means 객체 생성
kmeans = KMeans(n_clusters=3, init="k-means++", max_iter=200, random_state=0)

# cluster label (fit 후 labels_, fit_predict의 결과가 같았다.)
cluster_labels = kmeans.fit_predict(cluster_df.iloc[:,:-1])
cluster_df["kmeans_label"] = cluster_labels

# 개별 클러스터의 중심 위치 좌표
centers = kmeans.cluster_centers_
  • K-Means를 수행하고 데이터 프레임에 cluster label을 추가하였다.

  • cluster_centers_ 속성의 각 군집 중심점 좌표를 시각화를 위해 추출하였다.

# cluster 값 종류
unique_labels = np.unique(cluster_labels)

markers=['o', 's', '^', 'P','D','H','x']

for cluster in unique_labels:
    
    # 각 군집 시각화
    cluster_v = cluster_df[cluster_df['kmeans_label'] == cluster]    
    plt.scatter(x=cluster_v['ftr1'], y=cluster_v['ftr2'], edgecolor='k', marker=markers[cluster] )
    
    # 군집별 중심 위치 시각화
    center_xy = centers[cluster]
    
    plt.scatter(x = center_xy[0], y = center_xy[1], s=300, color='white',
                alpha=0.9, edgecolor='k', marker = markers[cluster])
    
    plt.scatter(x = center_xy[0], y = center_xy[1], s=70, color='k', 
                edgecolor='k', marker = f"${cluster}$")
    
    
plt.show()

png

  • 각 군집과 군집별 중심 위치를 시각화 하였다.

  • 앞서 가상데이터를 시각화했을 때와 비교하면 제법 잘 매핑된 것으로 보인다.

cluster_df.groupby(['target','kmeans_label']).size()
target  kmeans_label
0       0               66
        2                1
1       1               67
2       1                1
        2               65
dtype: int64
  • 실제로 target 0,2에서 각각 1건씩만 다르게 분류되고 나머지는 잘 매핑되었다.

Leave a comment