クラスタリングアルゴリズムの比較と評価
Overview
クラスタリングアルゴリズムを利用する際に問題になることの1つに、アルゴリズムがどの程度うまく機能したかを判断し、各アルゴリズムの結果を比較することが難しいことが挙げられます。ここでは、k-means、凝集型クラスタリング、DBSCANを比較してみます。
Coding(正解データを用いたクラスタリングの評価)
クラスタリングアルゴリズムの出力を、正解データクラスタリングと比較して評価するために用いられる指標がいくつかあります。最も重要なものは、調整ランド(adjusted rand index:ARI)と正規化相互情報量(normalized mutual information:NMI)です。これらはいずれも定量的な指標で最良の場合に1を、関係ないクラスタリングの場合に0を取ります(ただしARIは負の値になりうります)。
two_moonsでARIを用いてクラスタリングを比較して評価する
code: Python
from sklearn.metrics.cluster import adjusted_rand_score
from sklearn.datasets import make_moons
from sklearn.preprocessing import StandardScaler
from sklearn.cluster import KMeans
from sklearn.cluster import AgglomerativeClustering
from sklearn.cluster import DBSCAN
X, y = make_moons(n_samples=200, noise=0.05, random_state=0)
# データを平均0、分散1にスケール変換する
scaler = StandardScaler()
scaler.fit(X)
X_scaled = scaler.transform(X)
fig, axes = plt.subplots(1, 4, figsize=(15, 3), subplot_kw={'xticks': (), 'yticks': ()})
# 利用するアルゴリズムのリストを作る
# 参照のためにランダムなクラスタ割り当てを作る
random_state = np.random.RandomState(seed=0)
random_clusters = random_state.randint(low=0, high=2, size=len(X))
# ランダムな割り当てをプロット
axes0.scatter(X_scaled:, 0, X_scaled:, 1, c=random_clusters, cmap=mglearn.cm3, s=60) axes0.set_title("Random assignment - ARI: {:.2f}".format(adjusted_rand_score(y, random_clusters))) for ax, algorithm in zip(axes1:, algorithms): # クラスタ割り当てとクラスタセンタをプロット
clusters = algorithm.fit_predict(X_scaled)
ax.scatter(X_scaled:, 0, X_scaled:, 1, c=clusters, cmap=mglearn.cm3, s=60) ax.set_title("{} - ARI: {:.2f}".format(algorithm.__class__.__name__, adjusted_rand_score(y, clusters)))
https://gyazo.com/583ddbf1391ce8345782cf2eb2928374
ARIのスコアをみてみると、完全にランダムなものは0、良く再現できているDBSCANは1となっています。
Coding(正解データを用いないクラスタリングの評価)
実際にはARIのような指標を用いるには大きな問題があります。それは、実世界では正解データがない場合が多いことです。また、そもそも正解データがあるならそれを使ってクラス分類器のような教師ありモデルを作ればよいです。したがって、ARIやNMIのような指標はアルゴリズムの開発過程でしか利用できず、アプリケーションがうまくいってかどうかの指標になりません。
正解データを必要としないクラスタリングの指標もあります。シルエット係数(silhouette coefficient)などがそうです。しかし、これらの指標も実際にはうまくいきません。
code: Python
from sklearn.metrics.cluster import silhouette_score
X, y = make_moons(n_samples=200, noise=0.05, random_state=0)
# データを平均0、分散を1にスケール変換する
scaler = StandardScaler()
scaler.fit(X)
X_scaled = scaler.transform(X)
fig, axes = plt.subplots(1, 4, figsize=(15, 3), subplot_kw={'xticks': (), 'yticks': ()})
random_state = np.random.RandomState(seed=0)
random_clusters = random_state.randint(low=0, high=2, size=len(X))
axes0.scatter(X_scaled:, 0, X_scaled:, 1, c=random_clusters, cmap=mglearn.cm3, s=60) axes0.set_title("Random assignment: {:.2f}".format(silhouette_score(X_scaled, random_clusters))) for ax, algorithm in zip(axes1:, algorithms): clusters = algorithm.fit_predict(X_scaled)
ax.scatter(X_scaled:, 0, X_scaled:, 1, c=clusters, cmap=mglearn.cm3, s=60) ax.set_title("{} : {:.2f}".format(algorithm.__class__.__name__, silhouette_score(X_scaled, clusters)))
https://gyazo.com/aac7230fe9932050f31a697e11e5c7cc
DBSCANの結果のほうが良さそうにみえますが、k-meansのほうがスコアが高いです。もう少し良い評価方法として、頑健性を用いたクラスタリング評価指標があります。しかし、これはscikit-learnでは実装されていません。
// TODO P190