とある京大生の作業ログと日々の雑記

コンピュータサイエンスについて学んだことを可視化したり日々の雑記をまとめてます。

機械学習における特異値解析と主成分分析のお話

こんばんは!コミさん(@komi3_ )です。


今日はバイトが休みだったのでテキトーに論文でも読んでなんか強化学習モデルとか実装しようかなーとか軽く考えながらぼーっとしてました。

そしたら脳科学の授業をとってる友達から、「ちょっとプログラミング関係のことでわからないことが...」と相談されましてね...

まあぼく自身理学部に所属してて、周りでプログラミング強い人があんまりいないのでこの手の相談はよく受けるんですよ。


まあそんなこんなで
ぼく「どれどれ、見せてみたまえ」
友達「これなんやけど...」


で、問題見てみたら

{ \displaystyle
上記の画像データに対してPCAを利用してSVDを行い、分散共分散行列の固有値の特異値を計算し...
}


ぼく「......(わかんねえ)」


こんな背景で、今日の午後を丸ごとSVDとPCAの理解に時間を割きました。はい。

まあたくさん時間割いただけあって、かなり理解しましたよ!

ということで解説始めます〜!


職場の上司からの教えなのですが、プログラミング関係のことでわからん概念とかを取り扱うときは
「インプットとアウトプットがそれぞれなんなのか、目的はなんなのか」
ということを意識すれば理解は比較的早まるし、つまづくということはなくなるだろう、ということを教わりました。


今回の解説では、インプットとアウトプットに重点を置いて解説をしようと思います!


では改めて、解説スタートです!


まずは用語の説明ですね。単語がわからんと何もわかりませんもんね。外国語と同じです。

SVD (singular value decomposition) : 特異値分解
PCA (principal component analysis) : 主成分分析


まあ意味はわかりませんね。まあじきにわかるので、とりあえずここではスルーで。
とりあえず今回の理解のターゲットはこいつらですね。


ではまず目的について述べましょう。
こいつらをやることでどんなメリットがあるのか?


端的に答えますと、
「PCA, SVDを行うことで次元削減を行える。その結果、いらない特徴量を捨てることができ、学習時間の短縮化や過学習の防止に繋がる」


ということです。


理解しやすさのために具体例をあげましょう。


今、1000枚の顔画像を使って画像認識を行いたいと思っているとしましょう。
顔画像の画像認識を行うとき、必要な情報は例えばどんなものでしょう?

まあおそらく、顔の輪郭や目の形、鼻の相対的な位置とかですよね。

でもこれら画像情報の中にはいらないデータとかもありますよね。

例えば、背景とか、写ってる人の服の柄とかです。

こういった「必要な特徴量」もあれば「いらない特徴量」も画像にはあるわけです。


こうした「いらない特徴量」をカットするのに、SVDやPCAは有効なのです。


ざっとまとめるとこんな感じです。

そして機械学習とか技術的な話はここまでです。

ここから先は数学のお話です。

ちなみにぼくは数学がとても嫌いですし苦手です。

「まあ人には乗り越えなければならない山もあるのだ」と己に言い聞かせましょう。


しかしまあ、ぼくは数学屋さんじゃないので、情報屋さんに近い形でがんばって説明します。


ピクセル{250 \times 250}の画像データが1000枚あるとします。
このとき、これらデータは({1000 \times 250 \times 250})のデータ構造を持ちますよね。


まずは画像一枚あたりのデータ構造を2次元から1次元にreshapeして、データ構造を({1000 \times 62500})の形にします。
{62500}ってのは{62500 = 250 \times 250}です。


ここではこの{1000 \times 62500}の行列を{X}とでもしておきましょう。

PCA (主成分分析)


PCAには分散共分散行列が必要です。このデータから分散共分散行列を求めたいので、このデータ行列{X}の各成分に対して平均0、標準偏差が1となるように標準化します。
この標準化された行列は{N}とでもしておきましょう。


ここで、このデータ行列から得られる分散共分散行列を{C}とすると、{N}{C}の関係式は
{ \displaystyle
C = \frac{N^T N}{1000 - 1}
}
です。

(標準化された上で)転置との内積で共分散行列が求まるらしい。


PCAという操作を行うと、分散共分散行列は以下のように分解でき、それぞれの文字は以下の通りに表されます。

{ \displaystyle
{\begin{align}
C &= V_{PCA} L V_{PCA}^{T}
\\
V_{PCA} &= \left( \begin{array}{cccc}
\boldsymbol{v_1} & \boldsymbol{v_2} & \cdots & \boldsymbol{v_p}
\end{array} \right)
\\
L &= \left( \begin{array}{cccc}
\lambda_1 & 0 & 0 & \cdots & 0
\\
0 & \lambda_2 & 0 & \cdots & 0
\\
\vdots & & \ddots & & \vdots
\\
\\
0 & 0 & & \cdots & \lambda_p
\end{array} \right)
\end{align}
}
}


ここで、{\lambda _{n}}固有値(Eigenvalue)で、大きいものから順に並んでるものとします。

また、{v_p}{p}次元の列ベクトルで、この{v_p}が第{p}主成分と言われています。


これらの行列のサイズは全て[p \times p]です。


準備はこんな感じで、ここまでくればデータの次元数を落とすことができるようになります。


どのようにやるかというと、データを主成分空間に変換します。

具体的には、データ行列{X}を入力とすれば、{X \cdot V_{PCA}}とすることで、主成分空間に変換することができます。


今回の場合、次元数を{p}次元から{q}次元に落とすとして、{V_{PCA}}の最初の{q}列だけとってきた{V_{PCA}^{(q)}}を用いれば、{N \cdot V_{PCA}^{(q)}}{p \times q}の主成分空間に変換された出力を得ることができます。

SVD (特異値分解)

さっきと同様に標準化されたデータ行列を{N}(ただし形は{(n \times p)})としたとき、SVDの操作は以下のような分解になります。

{\displaystyle
{\begin{align}
N &= U S V_{SVD}^T
\\
S &= \left( \begin{array}
 \ s_1 & 0 & 0 & \cdots & 0
\\
0 & s_2 & 0 & \cdots & 0
\\
\vdots & & \ddots & & \vdots
\\
\\
0 & 0 & & \cdots & s_p
\\
0 & 0 & & \cdots & 0
\\
0 & 0 & & \cdots & 0
\\
\vdots & \vdots & & & \vdots
\end{array} \right)
\end{align}
}
}


ここでUV_{SVD}の形はそれぞれn \times np \times pで、かつ直交行列です。

Sの形はn \times pで、s_iは第i特異値と言います。


あとはN \cdot V_{SVD}とすることでPCAと同様にデータの次元数を落とせます。


2つの関係性について

ざっと見た感じ、
PCA ⇨ 共分散行列を求めてから分解、データの次元を変更できる
SVD ⇨ データ行列を直接分解してデータの次元変更ができる

みたいな感じで、両方とも入力は標準化されたデータ行列、出力は次元を落としたデータ行列です。
やってることはほぼ変わりませんよね。

つまりなんかしら関係式が成立しそうなので、これからはそれにスポットを当ててみようと思います。

先程と同様に、標準化されたデータ行列{N}を用います。形はn \times pです。

\displaystyle
C = \frac{N^T \cdot N}{n-1}
= \frac{(U S V_{SVD}^T)^T \cdot U S V_{SVD}^T}{n-1}
= \frac{V_{SVD} S^T U^T \cdot U S V_{SVD}^T}{n-1}
= \frac{V_{SVD} S^T S V_{SVD}^T}{n-1}

ここで、Uが直交行列という性質を利用してます。

また、C = V_{PCA} L V_{PCA}^{T}でもあるので、

\displaystyle
{V_{pca} L V_{pca}^T = \frac{V_{svd} S^T S V_{svd}^T}{n - 1}
}

が成立します。

ここで、S^T Sを計算すると
\displaystyle
{S^T S = \left( \begin{array}{cccc}
s_1^2 & 0 & 0 & \cdots & 0
\\
0 & s_2^2 & 0 & \cdots & 0
\\
\vdots & & \ddots & & \vdots
\\
\\
0 & 0 & & \cdots & s_p^2
\end{array} \right)
}

で、対角行列より、

\displaystyle
{\lambda_i = \frac{s_i^2}{n - 1}
}

という関係式が表されます。

なんかとても綺麗になりましたね。



以上で、大まかな式の説明は終わりです。

実装について

以下にSVDとPCAを簡単に求めるための実装を示しておきます。


About SVD and PCA


まとめ

まあこんな感じか、といったノリです。
なんか頭が良くなった気がします。

お疲れ様でした。