主成分分析(PCA)を実装する

はじめに

この記事ではPythonで主成分分析(PCA)の実装を行います。

まずはじめにscikit-learnを用いて実装した後に、

NumPyのみを利用して、自前での実装を試みます。

実装

scikit-learn

まずはscikit-learnを使ってみます。

データにはフィッシャーのアヤメを用います

1
2
3
4
5
6
7
8
9
10
11
12
from sklearn.datasets import load_iris
import numpy as np
iris = load_iris()
X = iris.data
from sklearn.decomposition import PCA
pca = PCA()
X_ = (X - X.mean(axis=0))/X.std(axis=0) # 標準化
X_new = pca.fit_transform(X_)
print (pca.explained_variance_)
print (pca.explained_variance_ratio_) # 寄与率
print (pca.components_)
print (X_new[0]) # 変換後データの先頭レコード

pca.explained_variance_ratio_は、変換後の各主成分の寄与率を表しています。
pca.explained_variance_pca.components_が何者なのかは今後わかります。

固有値分解

次に固有値分解を用いた実装を行います。

主成分分析は、データの分散共分散行列の固有値分解に帰着できます。確認してみましょう。

1
2
3
4
5
6
7
8
9
10
11
class MyPCA:
def __init__(self):
pass
def fit_transform_eig(self, X):
X = (X - X.mean(axis=0))/X.std(axis=0)
cov = np.cov(X.T)
l, v = np.linalg.eig(cov)
self.explained_variance_ = l # 固有値
self.explained_variance_ratio_ = self.explained_variance_ / self.explained_variance_.sum()
self.components_ = v # 固有ベクトル
return X.dot(v)
1
2
3
4
5
6
pca = MyPCA()
X_new = pca.fit_transform_eig(X)
print (pca.explained_variance_) # 固有値に相当
print (pca.explained_variance_ratio_) # 寄与率
print (pca.components_) # 固有ベクトルに相当
print (X_new[0])

先ほどと全く同じ結果が得られたはずです。

さて、ここでpca.explained_variance_が固有値に相当することが分かります。そして、その和が1になるように規格化したのがpca.explained_variance_ratio_となります。

また、pca.components_が固有ベクトルに相当することが分かります。

特異値分解

次に特異値分解を用いて実装することもできます。実際、scikit-learnの主成分分析は特異値分解で実装されています。

これは対称行列の特異値と固有値が等しく、かつ分散共分散行列は対称行列であるためです。

1
2
3
4
5
6
7
8
9
10
11
class MyPCA:
def __init__(self):
pass
def fit_transform_svd(self, X):
X = (X - X.mean(axis=0))/X.std(axis=0)
cov = np.cov(X.T)
U, s, V = np.linalg.svd(cov)
self.explained_variance_ = s # 特異値
self.explained_variance_ratio_ = self.explained_variance_ / self.explained_variance_.sum()
self.components_ = U # 左特異ベクトル(この場合V.Tも同じ)
return X.dot(V.T)
1
2
3
4
5
6
pca = MyPCA()
X_new = pca.fit_transform_svd(X)
print (pca.explained_variance_) # 特異値に相当
print (pca.explained_variance_ratio_) # 寄与率
print (pca.components_) # 左特異ベクトルに相当
print (X_new[0])

分散共分散行列が対称行列であるため、固有値と特異値、固有ベクトルと特異ベクトルが等しくなっていることも確認できます。

まとめ

NumPyの固有値分解や特異値分解を使って、scikit-learnPCAを再現することができました。

記事情報

  • 投稿日:2020年4月7日
  • 最終更新日:2020年4月7日