Pythonで主成分分析(PCA)による次元削減の理論と実装を詳しく解説する

スポンサーリンク

主成分分析、略してPCAは、次元削減の手法として有名です。

1901年から使われており、現在でも機械学習や統計学の分野で最もよく使われている次元削減法です。

PCAは教師なし統計手法の1つです。

この記事では、PCAについてある程度理解した上で、PythonとNumPyを使ってゼロから自分で実装してみます。

スポンサーリンク

そもそもなぜPCAを使うのか?

PCAを使用する理由を説明するために、ある例を見てみましょう。

2つの変数と10個のデータポイントを持つデータセットがあるとします。

このデータ点を可視化するように言われれば、非常に簡単に可視化することができます

その結果も非常に解釈しやすい。

| — | — | — | — | — | — | — | — | — | — | — |
| X1 | 2 | 8 | 1 | 4 | 22 | 15 | 25 | 29 | 4 | 2 |
| X2 | 3 | 6 | 2 | 6 | 18 | 16 | 20 | 23 | 6 | 4 |

データポイントの例

#Importing required libraries
import numpy as np

さて、変数の数を増やそうとすると、3次元以上の次元を想像することはほとんど不可能になる。

高次元のデータセットを分析するときに直面するこの問題は、一般に「次元の呪い」と呼ばれるものです。

この言葉は、Richard E. Bellmanによって初めて作られた。

主成分分析は、高次元のデータを低次元に縮小する一方で、データセットのばらつきを最大限にとらえることができる。

データの可視化は、PCAの最も一般的なアプリケーションです。

また、PCAはデータの次元数を減らすことで、アルゴリズムの学習を高速化するためにも使用される。

pythonによるPCAの実装

以下の内容から最大限の直感を得るためには、線形代数と行列について少し知っていることが前提です。

もしそうでなければ、YouTubeの3Blue1BrownのGrant Sandersonによる線形代数シリーズを見て、概念を再確認することを強くお勧めします。

主成分分析とは、n次元の楕円体をデータに当てはめ、その楕円体の各軸が主成分を表すようなものと考えることができる。

主成分の軸が大きければ大きいほど、データのばらつきが大きいことを表している。

#Generate a dummy dataset.
X = np.random.randint(10,50,100).reshape(20,5)
# mean Centering the data 
X_meaned = X - np.mean(X , axis = 0)

PythonでPCAを実装するためのステップ

# calculating the covariance matrix of the mean-centered data.
cov_mat = np.cov(X_meaned , rowvar = False)

1. 各変数の平均を引く

データセットが原点にセンタリングされるように、データセットから各変数の平均を引く。

これは共分散行列を計算するときに非常に便利です。

#Calculating Eigenvalues and Eigenvectors of the covariance matrix
eigen_values , eigen_vectors = np.linalg.eigh(cov_mat)

上記のコードで生成されたデータは次元(20,5)、すなわち20の例と各例に5つの変数を持っている。

2. 共分散行列の計算

平均中心化されたデータの共分散行列を計算します。

共分散行列については、Wikipediaのこちらの記事で詳しく説明されています。

共分散行列は、要素の互いの共分散を表す正方行列です。

ある要素のそれ自身との共分散は、単にその分散に過ぎません。

だから共分散行列の対角要素は、要素の分散に過ぎないのです。

#sort the eigenvalues in descending order
sorted_index = np.argsort(eigen_values)[::-1]
 
sorted_eigenvalue = eigen_values[sorted_index]
#similarly sort the eigenvectors
sorted_eigenvectors = eigen_vectors[:,sorted_index]

共分散行列は、 numpy.cov( ) メソッドを用いて簡単に求めることができます。

デフォルトの rowvarTrue に設定されていますが、必要な次元の共分散行列を得るためには False に設定することを忘れないようにしてください。

3. 固有値と固有ベクトルの計算

求めた共分散行列の固有値と固有ベクトルを計算します。

得られた共分散行列の固有ベクトルは互いに直交しており、各ベクトルは主軸を表しています。

固有値が高いほど、変動が大きいことを意味します。

したがって、より高い固有値を持つ主軸は、データのより高い変動性を捕らえる軸となります。

直交とは、ベクトルが互いに直交していることを意味します。

固有値とベクトルは、その背後にある考え方や概念を理解するまでは、とても怖いもののように思えます。

# select the first n eigenvectors, n is desired dimension
# of our final reduced data.
 
n_components = 2 #you can select any number of components.
eigenvector_subset = sorted_eigenvectors[:,0:n_components]

NumPyの linalg.eigh( ) メソッドは、複素エルミート行列または実対称行列の固有値と固有ベクトルを返します。

4. 固有値を降順に並べ替える

固有値とそれに対応する固有ベクトルの降順に並べ替える。

固有ベクトル行列の各列は主成分に対応しているので、固有値の降順に並べると、主成分の変動率の降順に自動的に並べられることを思い出してください。

したがって、並べ替えた固有ベクトル行列の最初の列は、最も高い変動性を持つ主成分となります。

#Transform the data
X_reduced = np.dot(eigenvector_subset.transpose(),X_meaned.transpose()).transpose()

np.argsort は、同じ形のインデックスの配列を返します。

5. 並べ替えられた固有値行列から部分集合を選ぶ


我々の必要性、すなわち、number_comp = 2として、再配列された固有値行列からサブセットを選択します。

これは、最初の2つの主成分を選択したことを意味します。

import numpy as np
 
def PCA(X , num_components):
     
    #Step-1
    X_meaned = X - np.mean(X , axis = 0)
     
    #Step-2
    cov_mat = np.cov(X_meaned , rowvar = False)
     
    #Step-3
    eigen_values , eigen_vectors = np.linalg.eigh(cov_mat)
     
    #Step-4
    sorted_index = np.argsort(eigen_values)[::-1]
    sorted_eigenvalue = eigen_values[sorted_index]
    sorted_eigenvectors = eigen_vectors[:,sorted_index]
     
    #Step-5
    eigenvector_subset = sorted_eigenvectors[:,0:num_components]
     
    #Step-6
    X_reduced = np.dot(eigenvector_subset.transpose() , X_meaned.transpose() ).transpose()
     
    return X_reduced

number_components = 2 は、最終的にデータを2つの変数に減らすことを意味します。

6. データの変換

最後に、固有ベクトル部分集合の転置と平均値中心データの転置を内積して、データを変換します。

内積の結果を転置することで、高次元から低次元に縮小されたデータが得られます。

import pandas as pd
 
#Get the IRIS dataset
url = "https://archive.ics.uci.edu/ml/machine-learning-databases/iris/iris.data"
data = pd.read_csv(url, names=['sepal length','sepal width','petal length','petal width','target'])
 
#prepare the data
x = data.iloc[:,0:4]
 
#prepare the target
target = data.iloc[:,4]
 
#Applying it to PCA function
mat_reduced = PCA(x , 2)
 
#Creating a Pandas DataFrame of reduced Dataset
principal_df = pd.DataFrame(mat_reduced , columns = ['PC1','PC2'])
 
#Concat it with target variable to create a complete Dataset
principal_df = pd.concat([principal_df , pd.DataFrame(target)] , axis = 1)

最終的なXếthes_reducedの次元は( 20, 2 )となり、元々は( 20, 5 )という高次元のデータであったことがわかります。

これで、今あるツールでデータを可視化できるようになった。

やったね!ミッション完了だ。

Pythonによる主成分分析の完全なコード

では、上記のすべてを組み合わせて関数を作り、ゼロから主成分分析を試してみましょう。

import seaborn as sb
import matplotlib.pyplot as plt
 
plt.figure(figsize = (6,6))
sb.scatterplot(data = principal_df , x = 'PC1',y = 'PC2' , hue = 'target' , s = 60 , palette= 'icefire')

データ行列と成分数を入力引数として、PCAという関数を定義しました。

IRISデータセットを使って、PCA関数を適用してみましょう。

Plotting Data On Two Dimensions
Plotting Data On Two Dimensions

重要なヒント: MLアルゴリズムを適用する前に、必要であればデータを標準化する必要があります。

上記のコードでは、データの標準化を行いませんでしたが、PCAを実装する際に標準化を行っています。

seabornとmatplotlibライブラリを使って結果をプロットしてみましょう。

Fitting An Ellipse To Data
Example: Fitting An Ellipse To Data
Reduced Dimension Plot
Reduced Dimension Plot

これで完了です。

完璧に動作しました。

まとめ

この記事では、PCAについて、PCAの仕組み、NumPyを使ったPCAの実装について学びました。

タイトルとURLをコピーしました