코딩

clustering - kmeans

포숑 2022. 3. 21. 23:12
728x90

 

clustering 은 target 값이 없는 unsupervised 비지도 학습이고 고객 분류, 데이터 분석, 차원 축소 기법, 이상치 탐지에 사용된다. 

고객 분류는 클러스터별로 고객을 나누어서 추천 시스템 구축하는 거, 데이터 분석은 분석 정확도를 높이기 위해서 클러스터별로 나눠서 보는 것이다.

준지도 학습으로 사용하는 방법도 있다. labeled 된 샘플이 적을 경우 동일한 클러스터에 있는 모든 샘플에 label 을 전파시킨다. imbalance dataset 에 사용하기 좋은 방법인 것 같지만 조금 위험해 보이기도 하다.

 

clustering 알고리즘 중에서 가장 유명한 것은 kmeans 와 dbscan 이다. 오늘은 그 중에서 kmeans 에 대해 알아보려고 한다.

kmeans 는 centroid 라 부르는 특정 포인트를 중심으로 샘플을 모아서 군집시키는 방법이다.

 

kmeans 알고리즘은 다음과 같은 순서로 작동한다. 

1) 센트로이드를 랜덤으로 배정한다. 이 때, 몇 개의 센트로이드를 생성할 것인지는 사용자가 처음에 지정해야 한다. (k) 

2) 샘플들에 레이블을 할당한다. (클러스터를 만든다, 할당하는 방식은 분산이 최소화되는 방향으로)

3) 클러스터별로 다시 센트로이드를 계산한다. (클러스터 내에서 무게중심 = 다음 센트로이드)

4) 새로 계산한 센트로이드를 중심으로 다시 샘플들에 레이블을 할당한다. 

5) 센트로이드의 변화가 없을 때까지 반복한다.

 

만만한 iris dataset 으로 clustering 실습을 해보자.

import numpy as np
import matplotlib.pyplot as plt
from sklearn.cluster import KMeans
from sklearn.datasets import load_iris
import pandas as pd 
import seaborn as sns

iris = load_iris()
df = pd.DataFrame(data = iris.data, columns = iris.feature_names)
df['target'] = iris.target
X = df[['sepal length (cm)', 'sepal width (cm)', 'petal length (cm)', 'petal width (cm)']]
y = df['target']

# 모델링

kmeans = KMeans()
kmeans.fit(X)
labels = kmeans.labels_
# 이렇게 해도 되고 

kmeans = KMeans()
labels = kmeans.fit_predict(X)
# 이렇게 해도 된다. 

# 신규 데이터가 있다면 predict 로 예측해준다. 
# predict_labels = kmeans.predict(data_unseen)

# 평가 지표 : silhouette metrics 
# dunn index 나 다른 평가지표도 있는데 대표적으로 실루엣 계수를 많이 본다.
from sklearn.metrics import silhouette_samples, silhouette_score
sample_silhouette_values = silhouette_samples(X, labels)

df['cluster'] = labels
df['sample_score'] = sample_silhouette_values

# cluster 별 실루엣 계수 확인
df.groupby('cluster').mean()['sample_score']

# 전체 실루엣 계수 확인
silhouette_score(X, labels)
df['sample_score'].mean()
# 둘은 같은 값이다.

 

적절한 k 값을 찾기 위한 방법으로는 elbow method 가 있다. 

이를 시각화해서 나타내는 아주 좋은 함수다.

아래는 클러스터별로 실루엣 스코어를 나타낸 히트맵 그래프다.

def visualize_elbowmethod(data, param_init='random', param_n_init=10, param_max_iter=300):
    distortions = []
    for i in range(1, 10):
        km = KMeans(n_clusters=i, init=param_init, n_init=param_n_init, max_iter=param_max_iter, random_state=0)
        km.fit(data)
        distortions.append(km.inertia_)

    plt.plot(range(1, 10), distortions, marker='o')
    plt.xlabel('Number of Cluster')
    plt.ylabel('Distortion')
    plt.show()
    
def visualize_silhouette_layer(data, param_init='random', param_n_init=10, param_max_iter=300):
    clusters_range = range(2,15)
    results = []

    for i in clusters_range:
        clusterer = KMeans(n_clusters=i, init=param_init, n_init=param_n_init, max_iter=param_max_iter, random_state=0)
        cluster_labels = clusterer.fit_predict(data)
        silhouette_avg = silhouette_score(data, cluster_labels)
        results.append([i, silhouette_avg])

    result = pd.DataFrame(results, columns=["n_clusters", "silhouette_score"])
    pivot_km = pd.pivot_table(result, index="n_clusters", values="silhouette_score")

    plt.figure()
    sns.heatmap(pivot_km, annot=True, linewidths=.5, fmt='.3f', cmap=sns.cm._rocket_lut)
    plt.tight_layout()
    plt.show()

엘보로 보면 k는 3이 적당하고, 실루엣 계수로 보면 2가 적당하다. 

728x90