K-Means法の入力に距離行列を使用する


はじめに

scikit-learnライブラリのK-Means法をしばしば使っていますが
意外と理解できていない部分が多い。

忘れないためのメモ代わりに残しておきます。
Python3.6をJupyter notebook 上で動かしていて、コードは以下の通り。

test1 = np.random.multivariate_normal([10,4],[[1,1.2],[1.2,5]],100)
test2 = np.random.multivariate_normal([5,-3],[[0.5,-0.5],[-0.5,2]],100)
test3 = np.random.multivariate_normal([3,6],[[3,0],[0,1.2]],100)
test = np.vstack([test1,test2,test3])
fig = plt.figure(figsize=(20,10))
ax = fig.add_subplot(1,2,1)
ax.scatter(test1[:,0],test1[:,1])
ax.scatter(test2[:,0],test2[:,1])
ax.scatter(test3[:,0],test3[:,1])

distance_mat = cdist(test,test,metric="euclidean")
distance_mat.shape

km = KMeans(precompute_distances=True,n_clusters=3)
result = km.fit(distance_mat)
y_pred , centroid = result.labels_ , result.cluster_centers_
ax = fig.add_subplot(1,2,2)
ax.scatter(test[:,0],test[:,1],c=y_pred)
ax.scatter(centroid[:,0],centroid[:,1],c="red",marker="x",s=20)
plt.show()

サンプル点は二次元ガウス分布でサンプリングし、クラスターが3つにわかれるように平均値を調整している。それぞれのガウス分布ごとに100サンプル出力しています。
左図が初期サンプリング、右図がKmeans法による予測。そして赤い点が各クラスタのセントロイドになります。
これが大きくずれている。それもそのはず、実はこのセントロイドはそれぞれが300次元のベクトルで、プロットしているのは最初の2次元のみ。
プロットした時には気付きませんでしたが
centroid.shape
で確認するとしっかりと(3,300)となってました。
この300はサンプル数に対応すると同時に入力の距離行列の次元も300*300
ひょっとして距離行列をKmeans法でクラスタリングしてる?

Kmeans法が距離行列から計算できることを確認した時点で、
仮説として、セントロイドはサンプル点から選ばれるのではないだろうかと考えていましたが
結果として違っていました。
検証できるかは不明。

ちなみに上記の仮説はK-medoids法と呼ばれる手法を事前に仕入れていたので、
そうなのでは?と考えた次第。
K-medoids法についての説明はこちらの記事で書いて下さっている方がいます。

ちなみに初投稿ですのでいろいろと不備がありそうですがご容赦を。