静かなる名辞

pythonとプログラミングのこと

【python】sklearnのMeanShiftクラスタリングを試してみる

はじめに

 MeanShiftはクラスタリングアルゴリズムクラスタ数を自動で決定してくれるという長所がある。

 理論的には最急降下法で各クラスタの極大点を探していく感じらしいです。わかりやすい解説があったので、リンクを張っておきます(ただし私自身はすべては読み込めていない)。

Mean Shift Clustering

 このMeanShiftはsklearnに実装されているので、簡単に試してみることができます。

 sklearn.cluster.MeanShift — scikit-learn 0.19.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の結果から。

iris.png
iris.png

 MeanShiftは2クラスタと解釈しているようです。本来のデータとは異なりますが、敢えて人の目で見ると妥当な結果な気もします。この場合、スケーリングによる変化は微々たるものです。

 次に、wineの方。

wine.png
wine.png

 一見するとoriginalが悪すぎるように見えますが、PCAでむりやり二次元に写像しているためです。クラスタリング自体は写像前のオリジナルの空間で行っているため、影響はありません。

 この場合、合格と言って良いのは入力をスケーリングしたKMeansだけで、あとはダメダメです。特徴量の次元数が大きいから、うまく動いていないのでしょうか。ちょっと謎。

結論

 良いか悪いかの判断はつきかねますが、できることはわかりました。

 たぶんbandwidthを変えるとコロコロ結果が変わるのでしょう。どんな感じで変わるのかは、今後気が向いたときに検証しようと思います。

 →やりました。
hayataka2049.hatenablog.jp