機械学習
MachineLearning

主成分分析とは

主成分分析(Principle Component Analysis)とは,どういったものなのかを説明したいと思います.主成分分析は多次元のデータを圧縮する方法です.

主成分分析とは直接は関係ありませんが,次元圧縮の一例として例えばプログラマのスキルとしてpython, C++, Javaの熟練度を測るためにGithub上でのstar⭐️の数でスキルを評価できると仮定しましょう.
それはつまり3次元のデータを1次元に要約(圧縮)したことになります.
他にも体重と身長の2次元データを一次元にするモデルとしてBMIなどもその一例です.

本題ですが主成分分析とは、座標で考えると,例えば3次元のデータ(x,y,z座標)を二次元のデータ(l,m座標)に要約(圧縮)するようなものです.
この時,第n主成分を分散の大きい順に,l(エル)を第1主成分,mを第2主成分と呼びます.
イメージとしては,三次元空間にある赤い点を主成分軸(この場合第1・第2主成分)にして2次元で表すということです.

3dimention_to_2dimention (2).png

本記事では,3次元を2次元に圧縮する例を紹介します.

誤解してほしくないこと

今回は3次元から2次元への射影を考えますが,100次元から10次元への圧縮でも主成分分析を使えばそれは主成分分析です.Twitterに誤解して覚えてる人がいましたが”空間を面に、面を直線に落とし込んでいる”わけではありません.
主成分分析は次元圧縮のアルゴリズムというだけで、分かりやすいように3次元から2次元への射影を表現しているに過ぎません.高次元では高次元多様体から低次元多様体への次元圧縮を計算していると考えても良いかもしれません(※多様体学習を参照)が,射影できる次元が特別決められているわけではありません.詳しくはPRMLの下巻を参照してください.

主成分軸をどのように見つけるのか.

主成分を見つけるためには,分散が最大になるような軸を探します.
分散はどのようなものかといえば以下の式のようにデータ$x_i$とデータ平均$\mu$の二乗和を平均nで割ったものです.(端的に言ってしまえば,データの散らばり具合を定義していることになります.)

分散の定義

Var[X] = E[(X - \mu)^2] = \frac{1}{n} \sum_{i=1}^n(x_i - \mu)^2

この時Xを確率変数といいます.右辺の$x_i$と同じです.
最大となる分散を探すというのことは軸から最も離れた確率変数を探すということになります.
下の図のような具合にデータが散りばめられている時分散は大きくなります.

variance (1).png 

それに対して,ほぼありえないことですが,直線にぴったし確率変数が当てはまった場合は分散は0になります.(下の図)
variance2.png

固有値と固有ベクトル

固有値と固有ベクトルの定義

n次正方行列において,
$A \vec{v} = \lambda \vec{v}$ を満たす零ベクトルではないn次元実数ベクトル,
$\vec{v} \neq \vec{0}_n$ とスカラー$\lambda$ が存在するとき,

$\lambda$ をAの固有値. $\vec{v}$をAの($\lambda$に対する)固有ベクトルという.

これだけでは何を言っているか分からないので実際に計算をしてみる.
実際には行列に対して固有多項式を定義してやりそれを解くことによって求まる.

固有多項式の定義

$n$次正方行列Aに関して

|A - \lambda E| = 0

をAの固有方程式という.


以下の2次正方行列の固有値と固有ベクトルを求めよ.

A = 
\begin{pmatrix}
a & b \\ 
c & d \end{pmatrix}

固有方程式は代入すればいいだけなので,

|A-\lambda E| = |\begin{pmatrix} 1 & 2 \\ 3 & 2 \end{pmatrix} - \begin{pmatrix} \lambda & 0 \\ 0 & \lambda \end{pmatrix}| \\
\\
= |\begin{pmatrix} 1-\lambda & 2 \\ 3 & 2-\lambda \end{pmatrix}|

あとはサラスの公式で計算すればいいだけなので,
$= (1-\lambda)(2-\lambda)- (2 ・ 3)$
$=\lambda ^2 -3\lambda -4$
$=(\lambda + 1)(\lambda -4) = 0$
よって$\lambda = -1,4$

固有値$\lambda_1=-1$に対する固有ベクトル

\vec{p} = \begin{pmatrix}p_1 \\ p_2 \end{pmatrix} を求める.
\begin{pmatrix}1 & 2 \\ 3 & 2\end{pmatrix} \begin{pmatrix}p_1 \\ p_2 \end{pmatrix} = (-1)\begin{pmatrix}p_1 \\ p_2 \end{pmatrix} \\
\Rightarrow p_1 + 2p_2 = -p_1, 3p_1 + 2p_2 = -p_2 \Rightarrow p_1 = -p_2

連立方程式を解くと
$3p_1 = -3p_2 = p_1 = -p_2$となる.
よってスカラー$\kappa \neq 0$を用いて$\lambda_1$に対応する固有ベクトル$\vec{p}$は次式で与えられる.

\vec{p} = \kappa \begin{pmatrix} 1 \\ -1 \end{pmatrix}  ただし \kappaは0以外の整数(k \neq 0) 

また固有値$\lambda_2 = 4$に対する固有ベクトルは

\vec{q} = \begin{pmatrix} q_1 \\q_2 \end{pmatrix} を求める.

上と同じ手順に解くと$\lambda_2=4$に対する固有ベクトル$\vec{q}$が求まる.

\vec{q} =  \kappa \begin{pmatrix} 1 \\ \frac{3}{2} \end{pmatrix} (k \neq 0)

寄与率と累積寄与率

次に寄与率についての説明です.第1主成分と第2主成分の定義は以下になります.
第1主成分の寄与率は第1主成分がデータ全体のデータの散らばり具合をどれくらいカバーしているかを表しています.第2主成分以降も同様です.

第1主成分の寄与率 = $\frac{(第1主成分の固有値)}{(第1主成分の固有値)+(第2主成分の固有値)}$
第2主成分の寄与率 = $\frac{(第2主成分の固有値)}{(第1主成分の固有値)+(第2主成分の固有値)}$

一般に第i主成分までの定義は以下になります.
第i主成分の寄与率 = $\frac{l_i}{総分散}$ = $\frac{l_i}{総固有値}$
総分散 = $\Sigma^n_{i=1}l_i$ (ただし $l_i$は第i固有値)

累積寄与率

累積寄与率とは,第1主成分から第m主成分までの寄与率の和です.
第1主成分から第m主成分での圧縮がデータの散らばり具合をどの程度カバーしているかの説明する割合です.(1に近ければデータの散らばり具合を説明できている割合が高いことになります.)

第1主成分から第m主成分までの累積寄与率 = $\frac{l_1+l_2 + \cdots + lm}{総分散}$

(分散共分散行列で求めた固有値は,主成分方向の分散に対応します.)


実際に応用して見る.

PythonとJavaのソースコードのGithub上でのスターの総数と給料という3次元のデータを2次元に次元圧縮する例を考えてみました.あたいはある程度相関があるように適当に調整しました.(現実とはおそらく異なるでしょう....)
pythonのscikit-learnのPCAのライブラリを使用しました.

点の色 Pythonのstar⭐️の総数 Javaのstar⭐️の総数 給料(万円)
70 30 700
32 60 480
32 20 300
黄色 20 120 600
茶色 40 120 630
グレー 40 30 520
ディープピンク 300 1100 1200
2000 400 1500
オレンジ 40 180 800
pca_github_star.py
# coding: utf-8

import matplotlib.pyplot as plt
import numpy as np
# 主成分分析をするライブラリ
from sklearn.decomposition import PCA

# [Pythonのコードのスター総数, Javaのコードのスターの総数, 年収]
X = np.array([[70, 30, 700],[32, 60, 480],[32, 20, 300],[20, 120, 600],[40, 120, 630], [40, 30, 520], [300, 1100, 1200], [2000, 400, 1500],[40, 180, 800]])
pca = PCA(n_components=2)
pca.fit(X)

# 各主成分によってどの程度カバー出来ているかの割合
print(pca.explained_variance_ratio_)
# [ 0.80636224  0.17927921]

# 次元削減をXに適用する.
pca_point = pca.transform(X)

color = ['blue', 'green', 'red', 'yellow', 'brown', 'gray', 'deeppink', 'black','orange']
plt.scatter(*pca_point.T,  color=color)
plt.show()

主成分得点をプロットすると以下のグラフになる.

pca_kekka2.png

x軸に相当するのが給料と見ることができるでしょう.y軸に相当する方向の解釈が難しいですが,よーくデータ分布を見るとJavaができて給料がそこそこの人が高くなる傾向にあるように見ることができそうです.黒の人は給料は高いがpythonで勝負している人と解釈できそうだ.ピンクさんは給料が高くpythonよりもjavaが得意な人と見ることができそうです.
他の人たちもだいたい同じ規則でしたがっていそうです.

まとめ

最後のはデータをQiitaのアドベントカレンダーように急いで作ったのであくまで参考程度に解釈してほしい.自然のリアルなデータだと主成分分析は気づかない特徴を調査することができるので使えるだろう.またカーネルPCAなどもあるが私の現在のレベルでは難しいのでもう少しレベルアップしたらどこかで書きたい.
また,主成分分析では主成分軸を見つけて関係している因子を自分で解釈するが,逆の発想で因子を自分で仮定して推定する手法に因子分析というものがある.それはおいおい書けたら書きたいと思う.

参考文献

統計科学研究所R主成分分析.ppt から数式など参照させてもらいました.

固有値の計算手順や主成分分析の考え方などの文言など.
線形代数学に基づくデータ分析法: 原田 史子,島川 博光