はじめに
MeanShiftはクラスタリングアルゴリズム。クラスタ数を自動で決定してくれるという長所がある。
理論的には最急降下法で各クラスタの極大点を探していく感じらしいです。わかりやすい解説があったので、リンクを張っておきます(ただし私自身はすべては読み込めていない)。
このMeanShiftはsklearnに実装されているので、簡単に試してみることができます。
sklearn.cluster.MeanShift — scikit-learn 0.20.1 documentation
sklearnのトイデータで遊んでみましょう。
目次
使い方
いつものsklearnのモデルです。fitしてpredictするだけ。
いつだったかFuzzy C-Meansをやったときは苦労しましたが、とりあえずそんな心配は要りません。
となると気になるのはパラメータですが、指定しなくても
- bandwidth
勝手に推定される
- seeds
乱数のシードなので勝手に決まる。指定するときは[n_samples, n_features]が必要。
- bin_seeding
よくわからないけど、初期値の選び方みたいな。上のと関係がありそう。Trueにすると速くなるらしい。デフォルトのFalseの方がアルゴリズムとしては厳密なはず(よくわからんけど)。
- min_bin_freq
これも上のと関係がありそう。わかるようなわからないような感じ。
- cluster_all
すべての点をクラスタリングするかどうか。default=Trueなので敢えてFalseにする理由は・・・(高速化なんだろうな)。
- n_jobs : int
いつもの並列化数
本質的な挙動に関わるのはbandwidthで、あとは高速化のために計算を端折るための引数がいっぱいあるだけっぽいです。
そしてbandwidthも勝手に推定してくれるので、敢えて指定する必要性を感じません(推定の良し悪しがどうかという議論はありますが)。
今回はn_jobs以外すべてデフォルトでやってみます。
実験
iris, wineで見てみる。せっかくなのでK-Meansと比較してみます。ついでに、入力をスケーリングすると結果が変わるかも見ます。
プログラム
いろいろ手抜きをしています。が、とにかく結果は出ます。
import matplotlib.pyplot as plt from sklearn.datasets import load_iris, load_wine from sklearn.cluster import MeanShift, KMeans from sklearn.decomposition import PCA from sklearn.preprocessing import StandardScaler from sklearn.pipeline import Pipeline def main(): iris = load_iris() wine = load_wine() kmeans = KMeans(n_clusters=3, n_jobs=-1) mean_shift = MeanShift(n_jobs=-1) s_kmeans = Pipeline([("scaler", StandardScaler()), ("kmeans", KMeans(n_clusters=3, n_jobs=-1))]) s_mean_shift = Pipeline([("scaler", StandardScaler()), ("meanshift", MeanShift(n_jobs=-1))]) # iris and wine pca = PCA(n_components=2) for dataset, dataset_name in zip([iris, wine], ["iris", "wine"]): fig, axes = plt.subplots(nrows=2, ncols=3, figsize=(20,8)) axes = axes.ravel() PCA_X = pca.fit_transform(dataset.data) origin_y = dataset.target km_y = kmeans.fit_predict(dataset.data) ms_y = mean_shift.fit_predict(dataset.data) s_km_y = s_kmeans.fit_predict(dataset.data) s_ms_y = s_mean_shift.fit_predict(dataset.data) n_clusters = [3, 3, mean_shift.cluster_centers_.shape[0], 3, s_mean_shift.named_steps.meanshift.cluster_centers_.shape[0]] for i, (y, name, n_cluster) in enumerate( zip([origin_y, km_y, ms_y, s_km_y, s_ms_y], ["original", "k-means", "mean-shift", "scaling+k-means", "scaling+mean-shift"], n_clusters)): for target in range(n_cluster): axes[i].scatter(PCA_X[y==target, 0], PCA_X[y==target, 1], c="rgb"[target]) axes[i].set_title("{0} n_cluster:{1}".format(name, n_cluster)) plt.savefig("{0}.png".format(dataset_name)) if __name__ == "__main__": main()
結果
まずirisの結果から。
MeanShiftは2クラスタと解釈しているようです。本来のデータとは異なりますが、敢えて人の目で見ると妥当な結果な気もします。この場合、スケーリングによる変化は微々たるものです。
次に、wineの方。
一見するとoriginalが悪すぎるように見えますが、PCAでむりやり二次元に写像しているためです。クラスタリング自体は写像前のオリジナルの空間で行っているため、影響はありません。
この場合、合格と言って良いのは入力をスケーリングしたKMeansだけで、あとはダメダメです。特徴量の次元数が大きいから、うまく動いていないのでしょうか。ちょっと謎。
結論
良いか悪いかの判断はつきかねますが、できることはわかりました。
たぶんbandwidthを変えるとコロコロ結果が変わるのでしょう。どんな感じで変わるのかは、今後気が向いたときに検証しようと思います。
→やりました。
www.haya-programming.com