Clustering - K-means, K-medoid

K-means Clusterig

  • 각 군집에 할당된 포인트들의 평균 좌표를 이용해 중심점을 반복적으로 업데이트하면서 군집을 분류해 나가는 방법
    • 가장 단순하고 빠른 군집화 방법
  • 초기에 제일 처음 랜덤하게 포인트를 하나 잡아서 그 포인트에 가까운 데이터들을 같은 군집으로 할당해준다. 그 다음 아래와 같은 방법으로 반복한다.
    • 다음과 같은 목저함수 값이 최소화될 때까지 군집의 중심위치와 각 데이터가 소속될 군집을 반복해서 찾는다. 이 값을 관성(inertia)이라 한다.
  • 이 식에서 $ K $ 는 군집의 갯수이고 $ C_{k} $ 는 $ k $ 번쨰 군집에 속하는 데이터의 집합, $ \mu_{k} $ 는 $ k $ 번째 군집의 중심위치(centroid), $ d $ 는 $ x_{i}, \mu_{k} $ 두 데이터 사이의 거리 혹은 비유사도(dissimilarity)로 정의한다. 만약 유클리드 거리를 사용한다면 다음과 같다.
  • 위 식은 다음처럼 표현할 수 도 있다.
  • 세부 알고리즘은 다음과 같다.
    • 1) 임의의 중심위치 $ \mu_{k} (k=1, \ldots , K) $ 를 고른다. 보통 데이터 표본 중에서 $ K $ 개를 선택한다.
    • 2) 모든 데이터 $ x_{i} (i = 1, \ldots , N) $ 에서 각각의 중심위치 $ \mu_{k} $ 까지의 거리를 계산한다.
    • 3) 각 데이터에서 가장 가까운 중심위치를 선택하여 각 데이터가 속하는 군집을 정한다.
    • 4) 각 군집에 대해 중심위치 $ \mu_{k} $ 를 다시 계산한다.
    • 5) 2~4를 반복한다.
  • K-means 군집화는 항상 수렴하지만 최종 군집화 결과가 전역 최적점이라는 보장은 없다. 군집화 결과는 초기 중심위치에 따라 달라질 수 있다.
  • K-means clusetering은 유클리드 거리를 사용하므로 너무 차원이 높을 때는 군집화 성능이 떨어질 수 있다.

K-means clustering의 작동 원리

  • Scikit-Learn의 cluster 서브패키지는 K-means 군집화를 위한 KMeans 클래스를 제공한다. 다음과 같은 인수를 받을 수 있다.

    • n_clusters : 군집의 개수
    • init : 초기화 방법 random이면 무작위, k-means++ 이면 K-means++ 방법. 또는 각 데이터의 군집 label
    • n_init : 초기 중심위치 시도 횟수. default 10이고 10개의 무작위 중심위치 목록 중 가장 좋은 값을 선택한다.
    • max_iter : 최대 반복 횟수
    • random_state : 시드값
  • 다음은 make_blobs 커맨드를 통해 만든 데이터를 2개로 K-means 군집화하는 과정을 나타낸 것이다. 각각의 그림은 군집을 정하는 단계 3에서 멈춘 것이다. 마커(marker)의 모양은 소속된 군집을 나타내고 크기가 큰 마커가 해당 군집의 중심위치이다. 각 단계에서 중심위치는 전단계의 군집의 평균으로 다시 계산되는 것을 확인할 수 있다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
from sklearn.datasets import make_blobs
from sklearn.cluster import KMeans

X, _ = make_blobs(n_samples=20, random_state=4)

def plot_KMeans(n):
model = KMeans(n_clusters=2, init="random", n_init=1, max_iter=n, random_state=6).fit(X)
c0, c1 = model.cluster_centers_
plt.scatter(X[model.labels_ == 0, 0], X[model.labels_ == 0, 1], marker='v', facecolor='r', edgecolors='k')
plt.scatter(X[model.labels_ == 1, 0], X[model.labels_ == 1, 1], marker='^', facecolor='y', edgecolors='k')
plt.scatter(c0[0], c0[1], marker='v', c="r", s=200)
plt.scatter(c1[0], c1[1], marker='^', c="y", s=200)
plt.grid(False)
plt.title("반복횟수={}, 관성={:5.2f}".format(n, -model.score(X)))

plt.figure(figsize=(12, 10))
plt.subplot(321)
plot_KMeans(1)
plt.subplot(322)
plot_KMeans(2)
plt.subplot(323)
plot_KMeans(3)
plt.subplot(324)
plot_KMeans(4)
plt.tight_layout()
plt.show()

k-means 반복횟수 증가에 따른 중심 위치의 변화

K-means++ 알고리즘

  • K-means++ 알고리즘은 초기 중심위치를 설정하기 위한 알고리즘이다. 다음과 같은 방법을 통해 되도록 멀리 떨어진 중심위치 집합을 찾아낸다.

      1. 중심위치를 저장할 집합 $ M $ 준비
      1. 우선 하나의 중심위치 $ \mu_{0} $ 를 랜덤하게 선택하여 $ M $ 에 넣는다.
      1. $ M $ 에 속하지 않는 모든 표본 $ x_{i} $ 에 대해 거리 $ d(M, x_{i}) $ 를 계산. $ d(M, x_{i}) $ 는 $ M $ 안의 모든 샘플 $ \mu_{k} $ 에 대해 $ d(\mu_{k}, x_{i}) $ 를 계산하여 가장 작은 값 선택
      1. $ d(M, x_{i}) $ 에 비례한 확률로 다음 중심위치 $ \mu $를 선택
      1. K 개의 중심위치를 선택할 때 까지 반복
      1. K-means 방법 사용
  • 중심을 구할 경우에는 각 데이터(포인트)들간의 거리의 합이 최소화하는 되는 지점이 중심이 될 것이므로 포인트간의 거리를 계산해야 한다. 여기서는 가장 간단하고 많이들 알고있는 유클리디안 거리와 맨하탄 거리를 소개할 것이다. 이외에도 마할라노비스의 거리등 거리를 구하는 방법은 여러가지가 있다. 이를 결정하는 것은 해당 데이터에 성질(예를 들어 위도 경도 데이터라고 해서 무조건적으로 Haversine distance를 사용하는 것은아니지만 위도와 경도의 데이터를 통해 항공기들간의 시간대 별 위치의 중심점을 찾아야 하는 경우 Haversine distance를 사용하듯)에 따라 분석자가 결정해야 할 몫이다.

참조 Distance type

K-means clustering의 작동 원리

최적의 K를 찾는 방법

  • K값을 설정하는 방법 중 가장 좋은 것은 군집의 개수를 미리 알고있는 경우이다. 예를 들어 뉴스기사를 각 카테고리별로 묶는다고 가정하면 크롤링해온 뉴스 기사의 카테고리 수를 미리 알고 있기 때문에 정확히 군집의 수를 결정할 수 있다. 허나, 대부분의 경우 우리는 데이터의 군집을 정확히 알고 있지 않은 상황에서 분석을 진행한다. 그러므로 다음과 같이 대체로 Elbow method 또는 Silhouette method

최적의 k를 찾는 방법 - 01

  • Elbow method는 $ \frac{군집간 분산}{전체 분산} $인 비율을 보고 k를 결정한다. WSS(군집 내 분산)은 작을 수록 군집의 중심에 많이 모여있는 것이므로 WSS(군집 내 분산)이 작을 수록 좋다는 말은 달리 말해 BSS(군집 간 분산)이 클수록 좋다는 의미이기도 하다. 전체 데이터의 중심과 군집의 중심들간의 거리가 떨어져 있을 수록 군집이 잘 분류 되었다고 판단할 수 있기 때문이다. 군집의 개수가 늘어날 수록 BSS는 높은 값을 갖아 데이터의 개수만큼 K를 설정하면 비율의 값은 100%가 될 것이다.

최적의 k를 찾는 방법 - 02

  • 위의 방법에서는 $ \frac{군집간 분산}{전체 분산} $의 비율 증가분을 보고 k를 결정하였다면 TSS(전체 분산)=BSS(군집 간 분산)+WSS(군집 내 분산)이므로 WSS가 가장 낮아지는 cluster의 개수를 찾는 방법도 동일한 결과를 얻을 수 있다는 사실을 알 수 있다.

최적의 k를 찾는 방법 - 03

  • 다른 한가지 방법인 Silhouette method도 거의 유사한 원리로 k를 결정하는 데 군집 내의 임의의 데이터와 다른 데이터들 간의 거리로 유사성을 파악하고, 다른 군집 중 제일 가까운 데이터와의 거리로 군집간의 유사성을 대표하는 지표로 삼아 제일 가까운 군집의 데이터의 거리와 객체내의 데이터들과의 거리의 차이가 크면 클수록 잘 군집이 형성되어졌다고 판단한다.

최적의 k를 찾는 방법 - 04

  • 최적의 k로 군집이 형성되었을 경우 가까운 군집과의 거리가 군집내 다른 데이터들의 거리보다 크기 때문에 분모에 해당하는 값은 b(i)로 되며, 분자의 가까운 군집과의 거리는 멀어질 것이며 군집내 데이터들과의 거리 차이는 0에 가까울 수록 최적의 k임을 의미하는 것이기 때문에 전체적인 값을 최대 1을 갖게되고 반대로 생각해보면 -1의 값을 최소로 갖게된다. s(i)값이 클수록 적절한 k가 될 확률이 높다.

최적의 k를 찾는 방법 - 05

  • 실루엣 메서드의 간단한 예시를 보면 아래와 같으며, 실루엣 값의 평균값인 실루엣 계수가 높은 값을 갖는 k를 최적의 k로 결정한다.

최적의 k를 찾는 방법 - 06

Elbow method를 사용하는 것이 일반적인데, 사실 k는 분석자가 미리 알고있을 경우가 가장 좋을 것이다. 허나, 모를 경우 최적의 k를 찾아도 고차원의 데이터에 대해서는 정확히 맞아 떨어지기 힘들다. 고차원 상에서도 데이터간의 거리를 계산할 순 있지만, 차원이 높아질수록 거리의 개념이 실질적으로 가까운지에 대한 감각이 무뎌지기 때문이다. 그런 이유로 거리를 기반으로 하는 군집 분석 같은 경우에는 차원이 높아질수록 최적의 k로 결정한 군집이 실제로 맞지 않을 가능성이 커진다.

  • 이러한 단점을 보완하여 나온 방법이 K-medoid clustering이다.

K-medoid Clustering

  • Elbow method와 Silhouette method도 k를 찾을 수 있는 하나의 방법인 것이지 찾은 k가 절대적으로 최적이라는 것은 아니다. 그래서 군집의 갯수를 찾는 것이 상당히 어려운 문제이다. k-means clustering은 아래와 같은 단점이 존재한다.

k-means clustering의 단점

  • 위에서 언급한 k-means의 단점을 보완하기 위한 방법으로 가장 간단히 평균대신 중간점을 사용하는 방벙이다.

k-medoid clustering이란?

  • k-medoid clustering도 다른 clustering과 동일하게 찾은 k가 절대적으로 최적인 k를 의미하진 않는다. 예를 들어 아래 그림에서와 같이 초승달 모양의 데이터 군집 분포는 거리에 기반한 모델인 k-means와 k-medoid clustering은 명확히 2군집으로 분류하기 어렵다.

k-means와 k-medoid 비교


1
2
3
4
from sklearn import datasets
import matplotlib.pyplot as plt
import pandas as pd
from sklearn.cluster import KMeans

Iris 데이터를 활용하여 Kmeans clustering

  • iris data에서 2가지 feature를 가지고만 군집 분석을 실행할 것이다.

1
2
3
iris = datasets.load_iris()
X = iris.data[:, :2]
y = iris.target


1
2
3
plt.scatter(X[:,0], X[:,1], c=y, cmap='gist_rainbow')
plt.xlabel('Spea1 Length', fontsize=18)
plt.ylabel('Sepal Width', fontsize=18)

iris 데이터 scatter plot

  • k=3으로 설정하고 k-means 군집분석을 진행해 보았다. 물론 이미 class label이 3개라는 사실을 알고 있으므로 여기선 3으로 설정하였으므로, 추후에 역으로 3으로 설정한 것이 타당한지에 대한 검증을 해 볼 것이다.

1
2
km = KMeans(n_clusters = 3, n_jobs = 4, random_state=21)
km.fit(X)
결과
1
2
3
KMeans(algorithm='auto', copy_x=True, init='k-means++', max_iter=300,
n_clusters=3, n_init=10, n_jobs=4, precompute_distances='auto',
random_state=21, tol=0.0001, verbose=0)

  • k=3으로 설정한 k-means 군집 분석의 결과의 군집 중심은 다음과 같다.

1
2
centers = km.cluster_centers_
print(centers)
결과
1
2
3
[[5.77358491 2.69245283]
[5.006 3.428 ]
[6.81276596 3.07446809]]

  • 해당 k-means 군집 분석의 결과로 예측한 label을 토대로 동일한 scatter plot을 그려 실제 데이터와 비교해 볼 것이다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
new_labels = km.labels_
fig, axes = plt.subplots(1, 2, figsize=(16,8))
axes[0].scatter(X[:, 0], X[:, 1], c=y, cmap='gist_rainbow',
edgecolor='k', s=150)
axes[1].scatter(X[:, 0], X[:, 1], c=new_labels, cmap='jet',
edgecolor='k', s=150)
axes[0].set_xlabel('Sepal length', fontsize=18)
axes[0].set_ylabel('Sepal width', fontsize=18)
axes[1].set_xlabel('Sepal length', fontsize=18)
axes[1].set_ylabel('Sepal width', fontsize=18)
axes[0].tick_params(direction='in', length=10, width=5, colors='k', labelsize=20)
axes[1].tick_params(direction='in', length=10, width=5, colors='k', labelsize=20)
axes[0].set_title('Actual', fontsize=18)
axes[1].set_title('Predicted', fontsize=18)
plt.show()

clustering 결과와 실제 데이터 비교

2차원의 가상 데이터에 Kmeans clustering

  • 새롭게 군집분석을 위한 데이터를 가상으로 만들어 진행해 볼 것이다. feature의 개수는 2개이고 데이터의 수는 150개로 하고 군집의 표준편차는 0.5로 하여 3개의 군집을 띄게 데이터를 생성해 주었다. 이는 위의 iris 데이터와 거의 동일한 분포를 띄는 데이터를 얻을 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from sklearn.datasets import make_blobs
# create dataset
X, y = make_blobs(
n_samples=150, n_features=2,
centers=3, cluster_std=0.5,
shuffle=True, random_state=0
)

# plot
plt.scatter(
X[:, 0], X[:, 1],
c='white', marker='o',
edgecolor='black', s=50
)
plt.show()

새롭게 생성한 2차원 데이터

  • 새롭게 만든 데이터도 k=3으로 하여 군집분석을 진행한다.

1
2
3
4
5
6
km = KMeans(
n_clusters=3, init='random',
n_init=10, max_iter=300,
tol=1e-04, random_state=0
)
y_km = km.fit_predict(X)

  • 새롭게 만든 데이터를 군집 분석 결과로 예측된 label의 값에 따라 marker와 색을 다르게 주어 2차원 plot을 그려 보았다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
plt.figure(figsize=(10,6))
plt.scatter(
X[y_km == 0, 0], X[y_km == 0, 1],
s=50, c='lightgreen',
marker='s', edgecolor='black',
label='cluster 1'
)

plt.scatter(
X[y_km == 1, 0], X[y_km == 1, 1],
s=50, c='orange',
marker='o', edgecolor='black',
label='cluster 2'
)

plt.scatter(
X[y_km == 2, 0], X[y_km == 2, 1],
s=50, c='lightblue',
marker='v', edgecolor='black',
label='cluster 3'
)

# plot the centroids
plt.scatter(
km.cluster_centers_[:, 0], km.cluster_centers_[:, 1],
s=250, marker='*',
c='red', edgecolor='black',
label='centroids'
)
plt.legend(scatterpoints=1)
plt.grid()
plt.show()

가상의 생성 데이터에 대한 군집 분석 결과

k 를 4로 할경우


1
2
3
4
5
6
km = KMeans(
n_clusters=4, init='random',
n_init=10, max_iter=300,
tol=1e-04, random_state=0
)
y_km = km.fit_predict(X)


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
plt.figure(figsize=(10,6))
plt.scatter(
X[y_km == 0, 0], X[y_km == 0, 1],
s=50, c='lightgreen',
marker='s', edgecolor='black',
label='cluster 1'
)

plt.scatter(
X[y_km == 1, 0], X[y_km == 1, 1],
s=50, c='orange',
marker='o', edgecolor='black',
label='cluster 2'
)

plt.scatter(
X[y_km == 2, 0], X[y_km == 2, 1],
s=50, c='lightblue',
marker='v', edgecolor='black',
label='cluster 3'
)


plt.scatter(
X[y_km == 3, 0], X[y_km == 3, 1],
s=50, c='lightblue',
marker='d', edgecolor='black',
label='cluster 4'
)


# plot the centroids
plt.scatter(
km.cluster_centers_[:, 0], km.cluster_centers_[:, 1],
s=250, marker='*',
c='red', edgecolor='black',
label='centroids'
)
plt.legend(scatterpoints=1)
plt.grid()
plt.show()

k=4로 한 경우의 scatter plot

  • Elbow method를 통해 최적의 k를 찾기 위해 각 cluster의 개수 별로 WSS(군집 내 분산)을 계산하여 그래프로 시각화한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
distortions = []
for i in range(1, 11):
km = KMeans(
n_clusters=i, init='random',
n_init=10, max_iter=300,
tol=1e-04, random_state=0
)
km.fit(X)
#inertia가 군집 내의 분산을 의미
distortions.append(km.inertia_)

# plot
plt.figure(figsize=(10,6))
plt.plot(range(1, 11), distortions, marker='o')
plt.xlabel('Number of clusters')
plt.ylabel('Distortion')
plt.show()

Elbow method를 사용한 최적의 k 추정

비정형 데이터에 대한 K-means 군집 분석

  • 정형 데이터에 관한 군집 분석 말고도 비정형 데이터인 문자열 데이터에 관한 군집 분석도 실행해 볼 것이다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.cluster import KMeans
from sklearn.metrics import adjusted_rand_score

documents = ["This little kitty came to play when I was eating at a restaurant.","hello kitty is my favorite character",
"Merley has the best squooshy kitten belly.","Is Google translator so good?","google google"
"google Translate app is incredible.","My dog s name is Kong","dog dog dog","cat cat"
"If you open 100 tab in google you get a smiley face.","Kong is a very cute and lovely dog",
"Best cat photo I've ever taken.","This is a cat house"
"Climbing ninja cat kitty.","What's your dog's name?","Cat s paws look like jelly",
"Impressed with google map feedback.","I want to join google","You have to wear a collar when you walk the dog",
"Key promoter extension for google Chrome.","Google is the best company","Google researcher"]

vectorizer = TfidfVectorizer(stop_words='english')
X = vectorizer.fit_transform(documents)


1
2
3
true_k = 3
model = KMeans(n_clusters=true_k, init='k-means++', max_iter=100, n_init=1)
model.fit(X)
결과
1
2
3
KMeans(algorithm='auto', copy_x=True, init='k-means++', max_iter=100,
n_clusters=3, n_init=1, n_jobs=None, precompute_distances='auto',
random_state=None, tol=0.0001, verbose=0)

  • 아래의 label은 위의 군집분석을 실행 할 때마다 다르기에 군집의 결과를 살펴보면서 어떤 데이터들이 같은 군집으로 묶였는지 확인해 본다.그러므로 위의 코드를 실행시 필자와 다른 결과를 볼 수 있을 것이다. 허나 군집으로 묶여있는 데이터들은 동일 할 것이다.

예측 결과 군집 0인 데이터들의 문서

  • 주로 고양이를 의미하는 kitty와 cat을 갖는 문서를 하나의 군집으로 묶어 주었다.

1
[x for x, y in zip(documents, model.labels_) if  y == 0]
결과
1
2
3
4
5
6
7
['This little kitty came to play when I was eating at a restaurant.',
'hello kitty is my favorite character',
'Merley has the best squooshy kitten belly.',
'cat catIf you open 100 tab in google you get a smiley face.',
"Best cat photo I've ever taken.",
'This is a cat houseClimbing ninja cat kitty.',
'Cat s paws look like jelly']

예측 결과 군집 1인 데이터들의 문서

  • 주로 dog를 갖는 문서를 하나의 군집으로 묶어주었다.

1
[x for x, y in zip(documents, model.labels_) if  y == 1]
결과
1
2
3
4
5
['My dog s name is Kong',
'dog dog dog',
'Kong is a very cute and lovely dog',
"What's your dog's name?",
'You have to wear a collar when you walk the dog']

예측 결과 군집 2인 데이터들의 문서


1
[x for x, y in zip(documents, model.labels_) if  y == 2]
결과
1
2
3
4
5
6
7
['Is Google translator so good?',
'google googlegoogle Translate app is incredible.',
'Impressed with google map feedback.',
'I want to join google',
'Key promoter extension for google Chrome.',
'Google is the best company',
'Google researcher']

  • 아래와 같은 새로운 문자열의 예측한 경우 위에서 살펴본 키워드를 갖으면 해당 군집으로 예측하는 것을 볼 수 있다.

1
2
3
4
5
6
7
8
9
10
11
Y = vectorizer.transform(["chrome browser to open."])
prediction = model.predict(Y)
print(prediction)

Y = vectorizer.transform(["I want to have a dog"])
prediction = model.predict(Y)
print(prediction)

Y = vectorizer.transform(["My cat is hungry."])
prediction = model.predict(Y)
print(prediction)
결과
1
2
3
[0]
[1]
[0]

  • 다음은 K-means++ 방법을 사용하여 MNIST Digit 이미지 데이터를 군집화한 결과이다. 각 군집에서 10개씩의 데이터만 표시하였다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
from sklearn.datasets import load_digits

digits = load_digits()

model = KMeans(init="k-means++", n_clusters=10, random_state=0)
model.fit(digits.data)
y_pred = model.labels_

def show_digits(images, labels):
f = plt.figure(figsize=(8, 2))
i = 0
while (i < 10 and i < images.shape[0]):
ax = f.add_subplot(1, 10, i + 1)
ax.imshow(images[i], cmap=plt.cm.bone)
ax.grid(False)
ax.set_title(labels[i])
ax.xaxis.set_ticks([])
ax.yaxis.set_ticks([])
plt.tight_layout()
i += 1

def show_cluster(images, y_pred, cluster_number):
images = images[y_pred == cluster_number]
y_pred = y_pred[y_pred == cluster_number]
show_digits(images, y_pred)


for i in range(10):
show_cluster(digits.images, y_pred, i)

MNIST 데이터 K-means 군집화한 결과

  • 이미지의 제목에 있는 숫자는 군집 번호에 지나지 않으므로 원래 숫자의 번호와 일치하지 않는다. 하지만 이를 예측문제라고 가정하고 분류 결과 행렬을 만들면 아래와 같다.

1
2
3
from sklearn.metrics import confusion_matrix

confusion_matrix(digits.target, y_pred)
결과
1
2
3
4
5
6
7
8
9
10
array([[  1,   0,   0,   0,   0, 177,   0,   0,   0,   0],
[ 0, 1, 55, 24, 0, 0, 0, 2, 1, 99],
[ 0, 13, 2, 148, 2, 1, 3, 0, 0, 8],
[ 0, 155, 0, 1, 11, 0, 7, 0, 2, 7],
[163, 0, 7, 0, 0, 0, 7, 0, 0, 4],
[ 2, 1, 0, 0, 42, 0, 0, 1, 136, 0],
[ 0, 0, 1, 0, 0, 1, 0, 177, 0, 2],
[ 0, 0, 0, 0, 0, 0, 177, 0, 0, 2],
[ 0, 4, 6, 3, 48, 0, 5, 2, 4, 102],
[ 0, 6, 20, 0, 139, 0, 7, 0, 6, 2]])

  • 이 군집화 결과의 ARI, AMI, 실루엣 계수 값은 다음과 같다.

1
2
3
4
5
from sklearn.metrics.cluster import adjusted_mutual_info_score, adjusted_rand_score, silhouette_score

print("ARI:", adjusted_rand_score(digits.target, y_pred))
print("AMI:", adjusted_mutual_info_score(digits.target, y_pred))
print("Silhouette Score:", silhouette_score(digits.data, y_pred))
결과
1
2
3
ARI: 0.6703800183468681
AMI: 0.7417664506416767
Silhouette Score: 0.18249069204151275

  • 군집화 결과를 주성분 분석을 통해 2차원에 투영하면 다음과 같다. 겹쳐져 있는 부분은 고차원상에서는 떨어져 있을 수 있다.

1
2
3
4
5
6
7
from sklearn.decomposition import PCA

pca = PCA(n_components=2)
X = pca.fit_transform(digits.data)

plt.scatter(X[:, 0], X[:, 1], c=y_pred, cmap=plt.cm.Set1)
plt.show()

MNIST 데이터를 PCA를 통해 2개의 주성분 벡터를 갖도록 차원을 축소한 그래프

  • K-means clusetering은 유클리드 거리를 사용하므로 너무 차원이 높을 때는 군집화 성능이 떨어질 수 있다. 이 때는 차원 축소를 한 후 군집화를 하는 것이 도움이 될 수도 있다.
  • MNIST Digit 데이터를 10차원으로 차원 축소하여 K-means 군집화하고 ARI, AMI, 실루엣 계수를 각각 계산하여 차원축소를 하지 않았을 때와 비교하라.

- 차원을 축소하여도 실루엣계수를 포함한 모든 지표들이 살펴보았을 때 오히려 낮아졌다. 이를 통해 복잡한 데이터에 대해서는 적합하지 않은 실루엣계수의 단점을 확인 할 수 있으며, 무조건 적으로 PCA를 하더라도 높아지지 않는다는 점을 확인할 수 있다.

1
2
3
4
5
6
7
8
9
from sklearn.metrics.cluster import adjusted_mutual_info_score, adjusted_rand_score, silhouette_score

model = KMeans(init="k-means++", n_clusters=10, random_state=0)
model.fit(digits.data)
y_pred = model.labels_

print("ARI:", adjusted_rand_score(digits.target, y_pred))
print("AMI:", adjusted_mutual_info_score(digits.target, y_pred))
print("Silhouette Score:", silhouette_score(digits.data, y_pred))
결과
1
2
3
ARI: 0.6686991223627669
AMI: 0.7397973157276612
Silhouette Score: 0.18251916424600556


1
2
3
4
5
6
7
8
9
10
11
12
pca = PCA(n_components=10)
X = pca.fit_transform(digits.data)

model = KMeans(init="k-means++", n_clusters=10, random_state=0)
model.fit(X)
y_pred = model.labels_

from sklearn.metrics.cluster import adjusted_mutual_info_score, adjusted_rand_score, silhouette_score

print("ARI:", adjusted_rand_score(digits.target, y_pred))
print("AMI:", adjusted_mutual_info_score(digits.target, y_pred))
print("Silhouette Score:", silhouette_score(digits.data, y_pred))
결과
1
2
3
ARI: 0.6492572540256956
AMI: 0.7183058275931469
Silhouette Score: 0.18116247153029966

미니배치 K-means 군집화

  • K-means 방법에서는 중심위치와 모든 데이터 사이의 거리를 계산해야 하기 때문에 데이터의 갯수가 많아지면 계산량도 늘어난다. 데이터의 수가 너무 많을 때는 미니 배치 K-means 군집화 방법을 사용하면 계산량을 줄일 수 있다. 미니배치 K-means 군집화는 데이터를 미니배치 크기만큼 무작위로 분리하여 K-means 군집화를 한다. 모든 데이터를 한꺼번에 썼을 때와 결과가 다를 수는 있지만 큰 차이가 없다.
  • Scikit-Learn의 cluster 서브패키지는 미니배치 K-means 군집화를 위한 MiniBatchKMeans 클래스를 제공한다. 미니배치 크기 batch_size인수를 추가로 받는다.
  • 150,000개의 데이터를 사용하여 실행 시간을 비교할 것이다.

1
2
3
4
from sklearn.cluster import MiniBatchKMeans
from sklearn.datasets import make_blobs

X, y = make_blobs(n_samples=150000, cluster_std=[1.0, 2.5, 0.5], random_state=170)

  • 미니배치 군집화의 속도가 훨씬 빠른 것을 알 수 있다.

1
2
3
%%time

model1 = KMeans(n_clusters=3).fit(X)
결과
1
2
CPU times: user 1.01 s, sys: 66.8 ms, total: 1.08 s
Wall time: 604 ms


1
2
3
%%time

model2 = MiniBatchKMeans(n_clusters=3, batch_size=1000, compute_labels=True).fit(X)
결과
1
2
CPU times: user 191 ms, sys: 7.71 ms, total: 199 ms
Wall time: 158 ms

  • 군집화 결과는 그다지 차이가 없다.

1
2
3
4
5
6
7
8
9
idx = np.random.randint(150000, size=300)
plt.subplot(121)
plt.scatter(X[idx, 0], X[idx, 1], c=model1.labels_[idx])
plt.title("K-평균 군집화")
plt.subplot(122)
plt.scatter(X[idx, 0], X[idx, 1], c=model2.labels_[idx])
plt.title("미니배치 K-평균 군집화")
plt.tight_layout()
plt.show()

미니배치와 일반적인 K-means 군집화 결과