静かなる名辞

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



【python】sklearnのSparsePCAを使ってみる

はじめに

 SparsePCAというものがあることを知ったので、使ってみようと思います。

SparsePCAとは?

 その名の通り、スパースな主成分分析です。スパースな主成分ベクトルを推定します。

Sparse PCA - Wikipedia

 原理などは理解しないで、カジュアルに使えるかどうか試してみるだけという趣旨です。なので「どうやって動いているの?」という質問には答えられません。許してください。

sklearnの実装

 きっちり存在しています(存在しなかったらこんな記事は書きませんが)。

sklearn.decomposition.SparsePCA — scikit-learn 0.20.1 documentation

 主要なパラメータとしては、以下のものがあります。

  • n_components

 PCAのと同じです。

  • alpha

 スパースPCAのキモで、L1正則化の強さを調整できます。

  • ridge_alpha

 こちらはtransformの際に使われるリッジ回帰(L2正則化)の正則化パラメータです。なんでリッジを使うのかは、実のところよくわかりません。

  • max_iter

 このパラメータがあるということは、最適化とか勾配法的なもので推定するのだな、というくらいに思っておきます。

  • normalize_components

 主成分ベクトルのノルムを1にするかどうか。Trueにしておくと良いと思います。

 結果に大きな影響を及ぼすのは上くらいだと思います。他のパラメータについてはドキュメントを参照してください。

実験

 今回はwineデータセットでやってみました。素のPCAでやった場合、alphaを0.5と5にした場合の結果をバイプロットで示します。

import matplotlib.pyplot as plt
from sklearn.datasets import load_wine
from sklearn.preprocessing import StandardScaler
from sklearn.decomposition import PCA, SparsePCA

def biplot(X_2d, components, target, ax):
    r1 = 5
    r2 = 1.01
    for i, coef in enumerate(components.T):
        ax.arrow(0, 0, coef[0]*r1, coef[1]*r1, color='r')    
        ax.text(coef[0]*r1*r2, coef[1]*r1*r2, i, color='b', fontsize=8)

    ax.scatter(X_2d[:,0], X_2d[:,1], c=target, cmap="rainbow")

def main():
    wine = load_wine()
    ss = StandardScaler()
    X = ss.fit_transform(wine.data)

    pca = PCA(n_components=2)
    spca = SparsePCA(n_components=2,
                     max_iter=3000,
                     n_jobs=-1,
                     normalize_components=True)
    
    fig, axes = plt.subplots(figsize=(12, 6), nrows=1, ncols=3)

    X_2d = pca.fit_transform(X)
    biplot(X_2d, pca.components_, wine.target, axes[0])
    axes[0].set_title("PCA")

    for i,alpha in zip([1, 2], [0.5, 5]):
        spca.set_params(alpha=alpha)
        X_2d = spca.fit_transform(X)
        biplot(X_2d, spca.components_, wine.target, axes[i])
        axes[i].set_title("SPCA alpha={:.2f}".format(alpha))
    plt.savefig("result.png")

    # 図と突き合わせて確認するために特徴量の名前を出力しておく
    for i, name in enumerate(wine.feature_names):
        print(i, name)

if __name__ == "__main__":
    main()

 max_iterをきもち高めにしましたが、結果は数秒程度で出ました。

result.png
result.png

0 alcohol
1 malic_acid
2 ash
3 alcalinity_of_ash
4 magnesium
5 total_phenols
6 flavanoids
7 nonflavanoid_phenols
8 proanthocyanins
9 color_intensity
10 hue
11 od280/od315_of_diluted_wines
12 proline

 とりあえず、PCAの結果とSparsePCAの結果で左右が反転しているのに注意。

 あとは見ての通りで、alpha=0.5で一部の係数が主成分にべたっと張り付くようになり、alpha=5では大半の係数が主成分に張り付いています。これがSparsePCAの効果で、結果の解釈が容易になるということらしいです(この次元数だとあまり威力はありませんが、高次元では活躍しそうです)。

 ワインにはあまり詳しくないので、今回は結果を細かく解釈することはしませんが……。

まとめ

 使えることがわかりました。