主成分分析(PCA)

コンピューターサイエンスで非常に重要な技術の一つです。次元圧縮の定番で「まずはPCAでもしてみるか」的な感じで利用されます。PCAとはPricipal Component Analysisと呼ばれ日本語では主成分分析と呼ばれます。

コンピュータサイエンスを専攻する学生にとっては、必ず学んでおいた方がいい技術の1つです。ただ殆どのサイトは数式を並べていて、結局何なのかがわかりづらいな、と思います。機械学習、統計学にしろ概念が非常に重要です。このブログでは概念を主に説明します。最後に簡単な実装例をおつけしておきます。

PCAとは(概念)

PCAは次元圧縮や特徴抽出というふうに言われます。なぜでしょうか。

それは、たくさんのデータから、そのデータを構成する基礎となるようなデータ(軸)が求まるからです。

少し詳細に説明します。例えば、ものすごい次元のデータが膨大にあったとします。
全部保持するのは容量的に無理です。ここで基礎となるような数個のデータと係数で、その莫大な集合をほぼ表現できるとしたら便利だし、これは次元圧縮と言えるのではないでしょうか。そして基礎となるような数個のデータが膨大なデータの基礎となるなら、これはデータの特徴とも呼べそうです。PCAはそれをするため次元圧縮や特徴抽出と言われています。

基礎となるようなデータは主成分ベクトルとよばれます。主成分ベクトルが面白いのは基底ベクトルであることです。基底ベクトルは、互いに独立で直交しているという特性をもっているベクトルで、その座標系での軸となるベクトルです。

実はPCAにより算出される主成分ベクトルはすべて基底ベクトルです。PCAにより算出された基底ベクトルが織り成す特徴量空間は元データがよく見える空間となります。(分散してみえるようになる)。こうした基となる軸のベクトル(データ)がPCAにより求まるのです。

ちなみに、基底ベクトルであれば掛け合わせるだけで簡単にその基底ベクトルを軸とした空間に変換が出来ます。線形代数の話になりますが、直交する基底ベクトルのみで構成されている行列を直交行列といい、その直交行列をデータにかけると、直交行列を系とした座標系に変換ができます。

PCAにより得られる主成分ベクトル同士は直交しており、それぞれの軸からでも分散をが大きくみえますが、その量は軸別で分散値が異なります。このどれくらい分散しているか(よく見えるか)というのは、寄与率で判断ができます。寄与率が高いほど主成分ベクトルがどれくらい重要かが判定できます。

蛇足で、別に直交していなくてもよく見える軸で見ればいいではないのか、というようなアイディアがあり、これはICA(独立成分分析)とよばれ音声処理などで活躍しています。

PCAの具体的な処理手順の

主成分分析とは、以下の手順をするだけで求まります。

・共分散行列を求めて固有値問題を解く

これがPCAです。従って、問題は以下の2つになります。

  1. 共分散行列を求める。
  2. 固有値問題を解く。

この二つのプロセスで解けるということで、わかってしまえば非常に簡単です。

共分散行列とは

共分散行列は、分散共分散行列とも呼ばれています。共分散行列の求め方については以下の具体例で示します。今、N次元のV1, V2, V3, V4のベクトルがあるとするとします。

そしてV1,V2,V3,V4の平均ベクトルV_avrを求めます。

V_{avr} =\frac{ V_1 + V_2 + V_3 + V_4 }{ 4}

続いて、V1, V2, V3, V4それぞれの共分散行列を作ります。例えば、V1の分散行列は次のようにして作成されます。

(V_1 - V_{avr}) \times (V_1 - V_{avr})^T ( ^Tは転置行列を表します。結果的にNxNのマトリックスが作成されます。)

例えば、V1が[ 2, 3, 4]という行列で平均ベクトルが[1, 1, 1 ] だったとすれば、V_1 - V_{avr}を行い[1,2,3]というベクトルを得ます。後は式にならって、


{MAT}_1 = \begin{bmatrix}1 & 2 & 3 \\ 2& 4&6\\3&6&9\end{bmatrix}

となり、V1の共分散行列MAT1が求まりました。V2, V3, V4についても同様のことを行います。

最終的な共分散行列をもとめるには、それぞれの共分散行列を全て足し、ベクトルの数(4つ)で割ればそれで終わりです。

共分散行列=(MAT1 + MAT2 + MAT3 + MAT4)/4 

これで目的の共分散行列を作ることができました。意外と簡単です^^;

固有値問題とは

固有値問題は以下のようなベクトルと値を求める操作です。

Ax = λx

ここで、λは固有値とxは固有ベクトルと呼ばれます。ここでAは行列を表します。 ここでいうAが先程のNxNの行列(共分散行列)となります。

固有値問題を解く方法はこの範囲を超えますので、様々なサイトをご覧ください。

PythonのLinear Algebraのライブラリを使って解く擬似コードを紹介します。

import numpy
from numpy import array
from numpy import linalg
def PCA(X):
  # 共分散行列の計算
    x_mean = array( map( sum, X.T) ) / len( X )
    X_ = X - x_mean  
    X_t = numpy.dot( X_.T, X_ ) / len(X)
    #固有値、固有分解
    lam, vec = linalg.eig( X_t ) # <== 固有値、固有分解が求まる!!
    ans = zip( lam, vec.T )
    ans.sort( reverse = True ) # <= 固有値が高い順にソート

linalg.eigにより固有値分解、固有値ベクトルを算出します。固有値分解の方法については興味があれば自分で調べてみてください。このように、固有値分解のライブラリさえ手に入っている状態であれば簡単にPCAを行うことができます。

ランク落ち

固有値分解を行う上で一つ注意がRANK落ちに注意してください。ランクがFULLでないと(つまり4x4の行列であれば、4つの独立したベクトルにより、その行列が構成されていないと)不正な値が出ます。(使うライブラリや実装方法によりますが)

そうした場合、正則化させてランク落ちをなくすことや、行列のランクがFULLでないのであれば特異値分解により求める必要があります。

参考文献

https://ja.wikipedia.org/wiki/%E4%B8%BB%E6%88%90%E5%88%86%E5%88%86%E6%9E%90

おすすめ記事

投稿者: 等々力 康弘

画像処理エンジニア。組み込みソフト出身。 株式会社モルフォにてR&D部門、主に機械学習業務に携わり、顔認識&顔検出のアルゴリズム開発に従事。国内特許数件、国際特許1件。 モルフォ社退社後、株式会社Dynaptico創業(CEO)。アメリカ人、スウェーデン人と3名とフードデリバリーサイトmaishoku.comを立ち上げる。社長業の他、開発業務においてバックグラウンド関連全般(Djangoを用いいたバックエンドサーバ&APIサーバーの作成、 リバースプロキシなどの負荷分散サーバ関連、OCRプログラムの作成、CISCOルータの管理, 、seleniumを用いたテストサーバーの構築、Androidアプリの開発等々)に携わる。 2019年DynapticoのCEOを辞職。 2020年2月にComputer Scienceに特化した株式会社OctOpt創業。 OSはUbuntu。Appleが苦手。Swiftのバージョンアップ対応とか死ぬほど嫌い。 Python/C++/C Twitter: @rocky_house シフト自動調整スケジュールサイトをVue.js+graphene djangoで構築. https://www.allshifter.com

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です