k-means法を実行する際に妥当なkを決めたいという欲求があります。
クラスタ集合の凝集度を定量化することでkと凝集度の関係を得られます。
複数のkについてkと凝集度の関係を取得し、
そこから妥当なkを発見する方法がエルボー法です。
この記事においては以下を書いてみます。
- クラスタ集合の凝集度を定量化する方法
- kと凝集度の関係を表すグラフの書き方
クラスタ集合の凝集度を定量化する方法
これ以上無いくらいシンプルなやり方です。
データポイントとcentroidの距離の2乗和(SSE)を凝集度として使います。
それを全クラスタに関して計算し足し合わせます。
足し合わせた値を歪み(distortion)と言っているところが多いです。
クラスタ数kが増えるにつれてこの値は小さくなるだろうと予測できます。
横軸にk、縦軸にSSEの和をとるグラフを書いてみると、
kの増加に伴ってSSEの和が小さくなること、減少幅は次第に小さくなりそうです。
(すべてのケースでそうなるのかは不明)
減少幅が緩やかになる地点のkが費用対効果が高いkですし、
ほぼ減少が止まる(サチる)地点のkを採用することは、
それ以上kを増加させても意味がないという事実を根拠として使えそうです。
擬似データへの適用
前回から使っている多次元正規分布に従う擬似クラスタデータに対してk-meansを適用します。
k=1から9まで適用し、k-distortionグラフを書いてみます。(だいぶPythonに慣れてきた..。)
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
mu = [[0,0], [20,20], [50,50], [40,30], [40,10], [20,40]]
sigma = [ [[30,20],[20,50]], [[20,30],[10,20]], [[60,40],[20,20]], [[60,20],[20,60]] ,[[30,10],[10,30]],[[50,20],[20,50]] ]
points = 100
clusters = []
for index in range(len(mu)):
cluster = np.random.multivariate_normal(mu[index], sigma[index], points)
dig = np.full((points,1),index+1, dtype=int)
cluster = np.hstack((cluster,dig))
clusters = np.r_[clusters,cluster] if len(clusters) > 0 else cluster
plt.scatter(x=clusters[:,0], y=clusters[:,1],c=clusters[:,2])
from sklearn.cluster import KMeans
kmeans_model = KMeans(n_clusters=6, init='random',max_iter=10).fit(clusters[:,:2])
labels = kmeans_model.labels_
distortions = []
numClusters = 10
for i in range(1,numClusters):
kmeans_model = KMeans(n_clusters=i, init='random',max_iter=10)
kmeans_model.fit(clusters[:,:2])
distortions.append(kmeans_model.inertia_)
plt.plot(range(1,numClusters),distortions,marker='o')
plt.xlabel('Number of clusters')
plt.ylabel('Distortion')
plt.show()
複数の擬似点を中心に2次元正規分布に従う散らばりをもったデータ達です。
k-distortionグラフです。もの凄い期待通りなグラフができました。
k=5か6あたりでdistortionがサチっています。
6-meansを適用し、centroidを重ねてみました。
散布図を見ての想像になりますが、
確かに4-meansと5-meansでは、5-meansの方が凝集していそうです。
5-meansと6-meansだと、4と5程凝集度合いに変化がなさそうです。